Merge changes from topic 'update-index-ref-decorator'

* changes:
  RefCursor: Remove unnecessary getUpdateIndex method
  RefDatabase/Ref: Add versioning to reference database
This commit is contained in:
Jonathan Nieder 2018-12-26 13:12:14 -05:00 committed by Gerrit Code Review @ Eclipse.org
commit f5bdb9745f
17 changed files with 342 additions and 60 deletions

View File

@ -61,6 +61,7 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
@ -134,6 +135,33 @@ public void testCreate() throws IOException {
assertEquals("ref: refs/heads/master\n", read(new File(d, HEAD)));
}
@Test(expected = UnsupportedOperationException.class)
public void testVersioningNotImplemented_exactRef() throws IOException {
assertFalse(refdir.hasVersioning());
Ref ref = refdir.exactRef(HEAD);
assertNotNull(ref);
ref.getUpdateIndex(); // Not implemented on FS
}
@Test
public void testVersioningNotImplemented_getRefs() throws Exception {
assertFalse(refdir.hasVersioning());
RevCommit C = repo.commit().parent(B).create();
repo.update("master", C);
List<Ref> refs = refdir.getRefs();
for (Ref ref : refs) {
try {
ref.getUpdateIndex();
fail("FS doesn't implement ref versioning");
} catch (UnsupportedOperationException e) {
// ok
}
}
}
@Test
public void testGetRefs_EmptyDatabase() throws IOException {
Map<String, Ref> all;

View File

@ -44,6 +44,7 @@
package org.eclipse.jgit.internal.storage.reftable;
import static org.eclipse.jgit.lib.Constants.HEAD;
import static org.eclipse.jgit.lib.Constants.MASTER;
import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
import static org.eclipse.jgit.lib.Constants.R_HEADS;
import static org.eclipse.jgit.lib.Ref.Storage.NEW;
@ -68,6 +69,7 @@
import org.eclipse.jgit.lib.ObjectIdRef;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefComparator;
import org.eclipse.jgit.lib.SymbolicRef;
import org.junit.Test;
public class MergedReftableTest {
@ -128,6 +130,7 @@ public void oneTableScan() throws IOException {
Ref act = rc.getRef();
assertEquals(exp.getName(), act.getName());
assertEquals(exp.getObjectId(), act.getObjectId());
assertEquals(1, act.getUpdateIndex());
}
assertFalse(rc.next());
}
@ -145,6 +148,7 @@ public void deleteIsHidden() throws IOException {
assertTrue(rc.next());
assertEquals("refs/heads/master", rc.getRef().getName());
assertEquals(id(2), rc.getRef().getObjectId());
assertEquals(1, rc.getRef().getUpdateIndex());
assertFalse(rc.next());
}
}
@ -162,6 +166,7 @@ public void twoTableSeek() throws IOException {
assertEquals("refs/heads/master", rc.getRef().getName());
assertEquals(id(2), rc.getRef().getObjectId());
assertFalse(rc.next());
assertEquals(1, rc.getRef().getUpdateIndex());
}
}
@ -177,6 +182,7 @@ public void twoTableById() throws IOException {
assertTrue(rc.next());
assertEquals("refs/heads/master", rc.getRef().getName());
assertEquals(id(2), rc.getRef().getObjectId());
assertEquals(1, rc.getRef().getUpdateIndex());
assertFalse(rc.next());
}
}
@ -212,6 +218,7 @@ public void fourTableScan() throws IOException {
Ref act = rc.getRef();
assertEquals(exp.getName(), act.getName());
assertEquals(exp.getObjectId(), act.getObjectId());
assertEquals(1, rc.getRef().getUpdateIndex());
}
assertFalse(rc.next());
}
@ -231,9 +238,11 @@ public void scanDuplicates() throws IOException {
assertTrue(rc.next());
assertEquals("refs/heads/apple", rc.getRef().getName());
assertEquals(id(3), rc.getRef().getObjectId());
assertEquals(2000, rc.getRef().getUpdateIndex());
assertTrue(rc.next());
assertEquals("refs/heads/banana", rc.getRef().getName());
assertEquals(id(2), rc.getRef().getObjectId());
assertEquals(1000, rc.getRef().getUpdateIndex());
assertFalse(rc.next());
}
}
@ -251,12 +260,14 @@ public void scanIncludeDeletes() throws IOException {
Ref r = rc.getRef();
assertEquals("refs/heads/master", r.getName());
assertEquals(id(8), r.getObjectId());
assertEquals(1, rc.getRef().getUpdateIndex());
assertTrue(rc.next());
r = rc.getRef();
assertEquals("refs/heads/next", r.getName());
assertEquals(NEW, r.getStorage());
assertNull(r.getObjectId());
assertEquals(1, rc.getRef().getUpdateIndex());
assertFalse(rc.next());
}
@ -277,6 +288,7 @@ public void oneTableSeek() throws IOException {
Ref act = rc.getRef();
assertEquals(exp.getName(), act.getName());
assertEquals(exp.getObjectId(), act.getObjectId());
assertEquals(1, act.getUpdateIndex());
assertFalse(rc.next());
}
}
@ -303,17 +315,17 @@ public void missedUpdate() throws IOException {
assertTrue(rc.next());
assertEquals("refs/heads/a", rc.getRef().getName());
assertEquals(id(1), rc.getRef().getObjectId());
assertEquals(1, rc.getUpdateIndex());
assertEquals(1, rc.getRef().getUpdateIndex());
assertTrue(rc.next());
assertEquals("refs/heads/b", rc.getRef().getName());
assertEquals(id(2), rc.getRef().getObjectId());
assertEquals(2, rc.getUpdateIndex());
assertEquals(2, rc.getRef().getUpdateIndex());
assertTrue(rc.next());
assertEquals("refs/heads/c", rc.getRef().getName());
assertEquals(id(3), rc.getRef().getObjectId());
assertEquals(3, rc.getUpdateIndex());
assertEquals(3, rc.getRef().getUpdateIndex());
}
}
@ -344,6 +356,63 @@ public void compaction() throws IOException {
}
}
@Test
public void versioningSymbolicReftargetMoves() throws IOException {
Ref master = ref(MASTER, 100);
List<Ref> delta1 = Arrays.asList(master, sym(HEAD, MASTER));
List<Ref> delta2 = Arrays.asList(ref(MASTER, 200));
MergedReftable mr = merge(write(delta1, 1), write(delta2, 2));
Ref head = mr.exactRef(HEAD);
assertEquals(head.getUpdateIndex(), 1);
Ref masterRef = mr.exactRef(MASTER);
assertEquals(masterRef.getUpdateIndex(), 2);
}
@Test
public void versioningSymbolicRefMoves() throws IOException {
Ref branchX = ref("refs/heads/branchX", 200);
List<Ref> delta1 = Arrays.asList(ref(MASTER, 100), branchX,
sym(HEAD, MASTER));
List<Ref> delta2 = Arrays.asList(sym(HEAD, "refs/heads/branchX"));
List<Ref> delta3 = Arrays.asList(sym(HEAD, MASTER));
MergedReftable mr = merge(write(delta1, 1), write(delta2, 2),
write(delta3, 3));
Ref head = mr.exactRef(HEAD);
assertEquals(head.getUpdateIndex(), 3);
Ref masterRef = mr.exactRef(MASTER);
assertEquals(masterRef.getUpdateIndex(), 1);
Ref branchRef = mr.exactRef(MASTER);
assertEquals(branchRef.getUpdateIndex(), 1);
}
@Test
public void versioningResolveRef() throws IOException {
List<Ref> delta1 = Arrays.asList(sym(HEAD, "refs/heads/tmp"),
sym("refs/heads/tmp", MASTER), ref(MASTER, 100));
List<Ref> delta2 = Arrays.asList(ref(MASTER, 200));
List<Ref> delta3 = Arrays.asList(ref(MASTER, 300));
MergedReftable mr = merge(write(delta1, 1), write(delta2, 2),
write(delta3, 3));
Ref head = mr.exactRef(HEAD);
Ref resolvedHead = mr.resolve(head);
assertEquals(resolvedHead.getObjectId(), id(300));
assertEquals("HEAD has not moved", resolvedHead.getUpdateIndex(), 1);
Ref master = mr.exactRef(MASTER);
Ref resolvedMaster = mr.resolve(master);
assertEquals(resolvedMaster.getObjectId(), id(300));
assertEquals("master also has update index",
resolvedMaster.getUpdateIndex(), 3);
}
private static MergedReftable merge(byte[]... table) {
List<Reftable> stack = new ArrayList<>(table.length);
for (byte[] b : table) {
@ -360,6 +429,14 @@ private static Ref ref(String name, int id) {
return new ObjectIdRef.PeeledNonTag(PACKED, name, id(id));
}
private static Ref sym(String name, String target) {
return new SymbolicRef(name, newRef(target));
}
private static Ref newRef(String name) {
return new ObjectIdRef.Unpeeled(NEW, name, null);
}
private static Ref delete(String name) {
return new ObjectIdRef.Unpeeled(NEW, name, null);
}

View File

@ -108,7 +108,7 @@ public void oneTable() throws IOException {
assertTrue(rc.next());
assertEquals(MASTER, rc.getRef().getName());
assertEquals(id(1), rc.getRef().getObjectId());
assertEquals(0, rc.getUpdateIndex());
assertEquals(0, rc.getRef().getUpdateIndex());
}
}
@ -155,7 +155,7 @@ public void twoTablesOneRef() throws IOException {
assertTrue(rc.next());
assertEquals(MASTER, rc.getRef().getName());
assertEquals(id(2), rc.getRef().getObjectId());
assertEquals(1, rc.getUpdateIndex());
assertEquals(1, rc.getRef().getUpdateIndex());
}
}
@ -203,12 +203,12 @@ public void twoTablesTwoRefs() throws IOException {
assertTrue(rc.next());
assertEquals(MASTER, rc.getRef().getName());
assertEquals(id(3), rc.getRef().getObjectId());
assertEquals(1, rc.getUpdateIndex());
assertEquals(1, rc.getRef().getUpdateIndex());
assertTrue(rc.next());
assertEquals(NEXT, rc.getRef().getName());
assertEquals(id(2), rc.getRef().getObjectId());
assertEquals(0, rc.getUpdateIndex());
assertEquals(0, rc.getRef().getUpdateIndex());
}
}

View File

@ -186,6 +186,7 @@ public void oneIdRef() throws IOException {
assertFalse(act.isSymbolic());
assertEquals(exp.getName(), act.getName());
assertEquals(exp.getObjectId(), act.getObjectId());
assertEquals(0, act.getUpdateIndex());
assertNull(act.getPeeledObjectId());
assertFalse(rc.wasDeleted());
assertFalse(rc.next());
@ -195,6 +196,7 @@ public void oneIdRef() throws IOException {
Ref act = rc.getRef();
assertNotNull(act);
assertEquals(exp.getName(), act.getName());
assertEquals(0, act.getUpdateIndex());
assertFalse(rc.next());
}
}
@ -216,6 +218,7 @@ public void oneTagRef() throws IOException {
assertEquals(exp.getName(), act.getName());
assertEquals(exp.getObjectId(), act.getObjectId());
assertEquals(exp.getPeeledObjectId(), act.getPeeledObjectId());
assertEquals(0, act.getUpdateIndex());
}
}
@ -237,6 +240,7 @@ public void oneSymbolicRef() throws IOException {
assertNotNull(act.getLeaf());
assertEquals(MASTER, act.getTarget().getName());
assertNull(act.getObjectId());
assertEquals(0, act.getUpdateIndex());
}
}
@ -250,14 +254,17 @@ public void resolveSymbolicRef() throws IOException {
Ref head = t.exactRef(HEAD);
assertNull(head.getObjectId());
assertEquals("refs/heads/tmp", head.getTarget().getName());
assertEquals(0, head.getUpdateIndex());
head = t.resolve(head);
assertNotNull(head);
assertEquals(id(1), head.getObjectId());
assertEquals(0, head.getUpdateIndex());
Ref master = t.exactRef(MASTER);
assertNotNull(master);
assertSame(master, t.resolve(master));
assertEquals(0, master.getUpdateIndex());
}
@Test
@ -335,14 +342,17 @@ public void namespaceHeads() throws IOException {
try (RefCursor rc = t.seekRefsWithPrefix("refs/tags/")) {
assertTrue(rc.next());
assertEquals(V1_0, rc.getRef().getName());
assertEquals(0, rc.getRef().getUpdateIndex());
assertFalse(rc.next());
}
try (RefCursor rc = t.seekRefsWithPrefix("refs/heads/")) {
assertTrue(rc.next());
assertEquals(MASTER, rc.getRef().getName());
assertEquals(0, rc.getRef().getUpdateIndex());
assertTrue(rc.next());
assertEquals(NEXT, rc.getRef().getName());
assertEquals(0, rc.getRef().getUpdateIndex());
assertFalse(rc.next());
}
@ -432,11 +442,12 @@ public void withReflog() throws IOException {
assertTrue(rc.next());
assertEquals(MASTER, rc.getRef().getName());
assertEquals(id(1), rc.getRef().getObjectId());
assertEquals(1, rc.getUpdateIndex());
assertEquals(1, rc.getRef().getUpdateIndex());
assertTrue(rc.next());
assertEquals(NEXT, rc.getRef().getName());
assertEquals(id(2), rc.getRef().getObjectId());
assertEquals(1, rc.getRef().getUpdateIndex());
assertFalse(rc.next());
}
try (LogCursor lc = t.allLogs()) {
@ -569,6 +580,7 @@ public void byObjectIdOneRefNoIndex() throws IOException {
assertTrue("has 42", rc.next());
assertEquals("refs/heads/42", rc.getRef().getName());
assertEquals(id(42), rc.getRef().getObjectId());
assertEquals(0, rc.getRef().getUpdateIndex());
assertFalse(rc.next());
}
try (RefCursor rc = t.byObjectId(id(100))) {
@ -579,6 +591,7 @@ public void byObjectIdOneRefNoIndex() throws IOException {
assertTrue("has master", rc.next());
assertEquals("refs/heads/master", rc.getRef().getName());
assertEquals(id(100), rc.getRef().getObjectId());
assertEquals(0, rc.getRef().getUpdateIndex());
assertFalse(rc.next());
}
@ -600,6 +613,7 @@ public void byObjectIdOneRefWithIndex() throws IOException {
assertTrue("has 42", rc.next());
assertEquals("refs/heads/42", rc.getRef().getName());
assertEquals(id(42), rc.getRef().getObjectId());
assertEquals(0, rc.getRef().getUpdateIndex());
assertFalse(rc.next());
}
try (RefCursor rc = t.byObjectId(id(100))) {
@ -610,6 +624,7 @@ public void byObjectIdOneRefWithIndex() throws IOException {
assertTrue("has master", rc.next());
assertEquals("refs/heads/master", rc.getRef().getName());
assertEquals(id(100), rc.getRef().getObjectId());
assertEquals(0, rc.getRef().getUpdateIndex());
assertFalse(rc.next());
}
@ -654,7 +669,6 @@ public void badCrc32() throws IOException {
}
}
private static void assertScan(List<Ref> refs, Reftable t)
throws IOException {
try (RefCursor rc = t.allRefs()) {
@ -663,6 +677,7 @@ private static void assertScan(List<Ref> refs, Reftable t)
Ref act = rc.getRef();
assertEquals(exp.getName(), act.getName());
assertEquals(exp.getObjectId(), act.getObjectId());
assertEquals(0, rc.getRef().getUpdateIndex());
}
assertFalse(rc.next());
}
@ -676,6 +691,7 @@ private static void assertSeek(List<Ref> refs, Reftable t)
Ref act = rc.getRef();
assertEquals(exp.getName(), act.getName());
assertEquals(exp.getObjectId(), act.getObjectId());
assertEquals(0, rc.getRef().getUpdateIndex());
assertFalse(rc.next());
}
}

View File

@ -48,6 +48,10 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.Arrays;
import java.util.List;
import org.junit.Test;
@ -114,11 +118,44 @@ public void testConstructor_Peeled() {
assertSame(ID_B, r.getPeeledObjectId());
}
@Test
public void testUpdateIndex() {
ObjectIdRef r;
r = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, name, ID_A, 3);
assertTrue(r.getUpdateIndex() == 3);
r = new ObjectIdRef.PeeledTag(Ref.Storage.LOOSE, name, ID_A, ID_B, 4);
assertTrue(r.getUpdateIndex() == 4);
r = new ObjectIdRef.PeeledNonTag(Ref.Storage.LOOSE, name, ID_A, 5);
assertTrue(r.getUpdateIndex() == 5);
}
@Test
public void testUpdateIndexNotSet() {
List<ObjectIdRef> r = Arrays.asList(
new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, name, ID_A),
new ObjectIdRef.PeeledTag(Ref.Storage.LOOSE, name, ID_A, ID_B),
new ObjectIdRef.PeeledNonTag(Ref.Storage.LOOSE, name, ID_A));
for (ObjectIdRef ref : r) {
try {
ref.getUpdateIndex();
fail("Update index wasn't set. It must throw");
} catch (UnsupportedOperationException u) {
// Ok
}
}
}
@Test
public void testToString() {
ObjectIdRef r;
r = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, name, ID_A);
assertEquals("Ref[" + name + "=" + ID_A.name() + "]", r.toString());
assertEquals("Ref[" + name + "=" + ID_A.name() + "(-1)]",
r.toString());
}
}

View File

@ -68,7 +68,7 @@ public void testConstructor() {
SymbolicRef r;
t = new ObjectIdRef.Unpeeled(Ref.Storage.NEW, targetName, null);
r = new SymbolicRef(name, t);
r = new SymbolicRef(name, t, 1);
assertSame(Ref.Storage.LOOSE, r.getStorage());
assertSame(name, r.getName());
assertNull("no id on new ref", r.getObjectId());
@ -77,9 +77,10 @@ public void testConstructor() {
assertSame("leaf is t", t, r.getLeaf());
assertSame("target is t", t, r.getTarget());
assertTrue("is symbolic", r.isSymbolic());
assertTrue("holds update index", r.getUpdateIndex() == 1);
t = new ObjectIdRef.Unpeeled(Ref.Storage.PACKED, targetName, ID_A);
r = new SymbolicRef(name, t);
r = new SymbolicRef(name, t, 2);
assertSame(Ref.Storage.LOOSE, r.getStorage());
assertSame(name, r.getName());
assertSame(ID_A, r.getObjectId());
@ -88,6 +89,7 @@ public void testConstructor() {
assertSame("leaf is t", t, r.getLeaf());
assertSame("target is t", t, r.getTarget());
assertTrue("is symbolic", r.isSymbolic());
assertTrue("holds update index", r.getUpdateIndex() == 2);
}
@Test
@ -133,6 +135,6 @@ public void testToString() {
d = new SymbolicRef("D", c);
assertEquals("SymbolicRef[D -> C -> B -> " + targetName + "="
+ ID_A.name() + "]", d.toString());
+ ID_A.name() + "(-1)]", d.toString());
}
}

View File

@ -97,6 +97,12 @@ protected DfsReftableDatabase(DfsRepository repo) {
super(repo);
}
/** {@inheritDoc} */
@Override
public boolean hasVersioning() {
return true;
}
/** {@inheritDoc} */
@Override
public boolean performsAtomicTransactions() {

View File

@ -170,24 +170,27 @@ long readUpdateIndexDelta() {
return readVarint64();
}
Ref readRef() throws IOException {
Ref readRef(long minUpdateIndex) throws IOException {
long updateIndex = minUpdateIndex + readUpdateIndexDelta();
String name = RawParseUtils.decode(UTF_8, nameBuf, 0, nameLen);
switch (valueType & VALUE_TYPE_MASK) {
case VALUE_NONE: // delete
return newRef(name);
return newRef(name, updateIndex);
case VALUE_1ID:
return new ObjectIdRef.PeeledNonTag(PACKED, name, readValueId());
return new ObjectIdRef.PeeledNonTag(PACKED, name, readValueId(),
updateIndex);
case VALUE_2ID: { // annotated tag
ObjectId id1 = readValueId();
ObjectId id2 = readValueId();
return new ObjectIdRef.PeeledTag(PACKED, name, id1, id2);
return new ObjectIdRef.PeeledTag(PACKED, name, id1, id2,
updateIndex);
}
case VALUE_SYMREF: {
String val = readValueString();
return new SymbolicRef(name, newRef(val));
return new SymbolicRef(name, newRef(val, updateIndex), updateIndex);
}
default:
@ -410,7 +413,7 @@ void verifyIndex() throws IOException {
* <ul>
* <li>{@link #name()}
* <li>{@link #match(byte[], boolean)}
* <li>{@link #readRef()}
* <li>{@link #readRef(long)}
* <li>{@link #readLogUpdateIndex()}
* <li>{@link #readLogEntry()}
* <li>{@link #readBlockPositionList()}
@ -575,8 +578,8 @@ private long readVarint64() {
return val;
}
private static Ref newRef(String name) {
return new ObjectIdRef.Unpeeled(NEW, name, null);
private static Ref newRef(String name, long updateIndex) {
return new ObjectIdRef.Unpeeled(NEW, name, null, updateIndex);
}
private static IOException invalidBlock() {

View File

@ -168,7 +168,6 @@ private class MergedRefCursor extends RefCursor {
private final PriorityQueue<RefQueueEntry> queue;
private RefQueueEntry head;
private Ref ref;
private long updateIndex;
MergedRefCursor() {
queue = new PriorityQueue<>(queueSize(), RefQueueEntry::compare);
@ -206,7 +205,6 @@ public boolean next() throws IOException {
}
ref = t.rc.getRef();
updateIndex = t.rc.getUpdateIndex();
boolean include = includeDeletes || !t.rc.wasDeleted();
add(t);
skipShadowedRefs(ref.getName());
@ -241,11 +239,6 @@ public Ref getRef() {
return ref;
}
@Override
public long getUpdateIndex() {
return updateIndex;
}
@Override
public void close() {
if (head != null) {
@ -285,7 +278,7 @@ String name() {
}
long updateIndex() {
return rc.getUpdateIndex();
return rc.getRef().getUpdateIndex();
}
}

View File

@ -68,13 +68,6 @@ public abstract class RefCursor implements AutoCloseable {
*/
public abstract Ref getRef();
/**
* Get updateIndex that last modified the current reference.
*
* @return updateIndex that last modified the current reference.
*/
public abstract long getUpdateIndex();
/**
* Whether the current reference was deleted.
*

View File

@ -280,7 +280,7 @@ private Ref resolve(Ref ref, int depth) throws IOException {
if (dst == null) {
return null; // claim it doesn't exist
}
return new SymbolicRef(ref.getName(), dst);
return new SymbolicRef(ref.getName(), dst, ref.getUpdateIndex());
}
/** {@inheritDoc} */

View File

@ -256,7 +256,7 @@ public Stats getStats() {
private void mergeRefs(MergedReftable mr) throws IOException {
try (RefCursor rc = mr.allRefs()) {
while (rc.next()) {
writer.writeRef(rc.getRef(), rc.getUpdateIndex());
writer.writeRef(rc.getRef(), rc.getRef().getUpdateIndex());
}
}
}

View File

@ -479,7 +479,6 @@ private class RefCursorImpl extends RefCursor {
private final boolean prefix;
private Ref ref;
private long updateIndex;
BlockReader block;
RefCursorImpl(long scanEnd, byte[] match, boolean prefix) {
@ -508,8 +507,7 @@ public boolean next() throws IOException {
return false;
}
updateIndex = minUpdateIndex + block.readUpdateIndexDelta();
ref = block.readRef();
ref = block.readRef(minUpdateIndex);
if (!includeDeletes && wasDeleted()) {
continue;
}
@ -522,11 +520,6 @@ public Ref getRef() {
return ref;
}
@Override
public long getUpdateIndex() {
return updateIndex;
}
@Override
public void close() {
// Do nothing.
@ -605,7 +598,6 @@ private class ObjCursorImpl extends RefCursor {
private final ObjectId match;
private Ref ref;
private long updateIndex;
private int listIdx;
private LongList blockPos;
@ -679,8 +671,7 @@ public boolean next() throws IOException {
}
block.parseKey();
updateIndex = minUpdateIndex + block.readUpdateIndexDelta();
ref = block.readRef();
ref = block.readRef(minUpdateIndex);
ObjectId id = ref.getObjectId();
if (id != null && match.equals(id)
&& (includeDeletes || !wasDeleted())) {
@ -694,11 +685,6 @@ public Ref getRef() {
return ref;
}
@Override
public long getUpdateIndex() {
return updateIndex;
}
@Override
public void close() {
// Do nothing.

View File

@ -67,7 +67,25 @@ public static class Unpeeled extends ObjectIdRef {
*/
public Unpeeled(@NonNull Storage st, @NonNull String name,
@Nullable ObjectId id) {
super(st, name, id);
super(st, name, id, -1);
}
/**
* Create a new ref pairing with update index.
*
* @param st
* method used to store this ref.
* @param name
* name of this ref.
* @param id
* current value of the ref. May be {@code null} to indicate
* a ref that does not exist yet.
* @param updateIndex
* number increasing with each update to the reference.
*/
public Unpeeled(@NonNull Storage st, @NonNull String name,
@Nullable ObjectId id, long updateIndex) {
super(st, name, id, updateIndex);
}
@Override
@ -100,7 +118,28 @@ public static class PeeledTag extends ObjectIdRef {
*/
public PeeledTag(@NonNull Storage st, @NonNull String name,
@Nullable ObjectId id, @NonNull ObjectId p) {
super(st, name, id);
super(st, name, id, -1);
peeledObjectId = p;
}
/**
* Create a new ref pairing with update index.
*
* @param st
* method used to store this ref.
* @param name
* name of this ref.
* @param id
* current value of the ref. May be {@code null} to indicate
* a ref that does not exist yet.
* @param p
* the first non-tag object that tag {@code id} points to.
* @param updateIndex
* number increasing with each update to the reference.
*/
public PeeledTag(@NonNull Storage st, @NonNull String name,
@Nullable ObjectId id, @NonNull ObjectId p, long updateIndex) {
super(st, name, id, updateIndex);
peeledObjectId = p;
}
@ -131,7 +170,25 @@ public static class PeeledNonTag extends ObjectIdRef {
*/
public PeeledNonTag(@NonNull Storage st, @NonNull String name,
@Nullable ObjectId id) {
super(st, name, id);
super(st, name, id, -1);
}
/**
* Create a new ref pairing with update index.
*
* @param st
* method used to store this ref.
* @param name
* name of this ref.
* @param id
* current value of the ref. May be {@code null} to indicate
* a ref that does not exist yet.
* @param updateIndex
* number increasing with each update to the reference.
*/
public PeeledNonTag(@NonNull Storage st, @NonNull String name,
@Nullable ObjectId id, long updateIndex) {
super(st, name, id, updateIndex);
}
@Override
@ -152,6 +209,8 @@ public boolean isPeeled() {
private final ObjectId objectId;
private final long updateIndex;
/**
* Create a new ref pairing.
*
@ -162,12 +221,16 @@ public boolean isPeeled() {
* @param id
* current value of the ref. May be {@code null} to indicate a
* ref that does not exist yet.
* @param updateIndex
* number that increases with each ref update. Set to -1 if the
* storage doesn't support versioning.
*/
protected ObjectIdRef(@NonNull Storage st, @NonNull String name,
@Nullable ObjectId id) {
@Nullable ObjectId id, long updateIndex) {
this.name = name;
this.storage = st;
this.objectId = id;
this.updateIndex = updateIndex;
}
/** {@inheritDoc} */
@ -211,6 +274,15 @@ public Storage getStorage() {
return storage;
}
/** {@inheritDoc} */
@Override
public long getUpdateIndex() {
if (updateIndex == -1) {
throw new UnsupportedOperationException();
}
return updateIndex;
}
/** {@inheritDoc} */
@NonNull
@Override
@ -220,7 +292,9 @@ public String toString() {
r.append(getName());
r.append('=');
r.append(ObjectId.toString(getObjectId()));
r.append(']');
r.append('(');
r.append(updateIndex); // Print value, even if -1
r.append(")]"); //$NON-NLS-1$
return r.toString();
}
}

View File

@ -217,4 +217,27 @@ public boolean isPacked() {
*/
@NonNull
Storage getStorage();
/**
* Indicator of the relative order between updates of a specific reference
* name. A number that increases when a reference is updated.
* <p>
* With symbolic references, the update index refers to updates of the
* symbolic reference itself. For example, if HEAD points to
* refs/heads/master, then the update index for exactRef("HEAD") will only
* increase when HEAD changes to point to another ref, regardless of how
* many times refs/heads/master is updated.
* <p>
* Should not be used unless the {@code RefDatabase} that instantiated the
* ref supports versioning (see {@link RefDatabase#hasVersioning()})
*
* @return the update index (i.e. version) of this reference.
* @throws UnsupportedOperationException
* if the creator of the instance (e.g. {@link RefDatabase})
* doesn't support versioning and doesn't override this method
* @since 5.3
*/
default long getUpdateIndex() {
throw new UnsupportedOperationException();
}
}

View File

@ -110,6 +110,19 @@ public abstract class RefDatabase {
*/
public abstract void close();
/**
* With versioning, each reference has a version number that increases on
* update. See {@link Ref#getUpdateIndex()}.
*
* @implSpec This method returns false by default. Implementations
* supporting versioning must override it to return true.
* @return true if the implementation assigns update indices to references.
* @since 5.3
*/
public boolean hasVersioning() {
return false;
}
/**
* Determine if a proposed reference name overlaps with an existing one.
* <p>

View File

@ -58,6 +58,8 @@ public class SymbolicRef implements Ref {
private final Ref target;
private final long updateIndex;
/**
* Create a new ref pairing.
*
@ -69,6 +71,24 @@ public class SymbolicRef implements Ref {
public SymbolicRef(@NonNull String refName, @NonNull Ref target) {
this.name = refName;
this.target = target;
this.updateIndex = -1;
}
/**
* Create a new ref pairing.
*
* @param refName
* name of this ref.
* @param target
* the ref we reference and derive our value from.
* @param updateIndex
* index that increases with each update of the reference
*/
public SymbolicRef(@NonNull String refName, @NonNull Ref target,
long updateIndex) {
this.name = refName;
this.target = target;
this.updateIndex = updateIndex;
}
/** {@inheritDoc} */
@ -128,6 +148,15 @@ public boolean isPeeled() {
return getLeaf().isPeeled();
}
/** {@inheritDoc} */
@Override
public long getUpdateIndex() {
if (updateIndex == -1) {
throw new UnsupportedOperationException();
}
return updateIndex;
}
/** {@inheritDoc} */
@SuppressWarnings("nls")
@Override
@ -143,7 +172,9 @@ public String toString() {
r.append(cur.getName());
r.append('=');
r.append(ObjectId.toString(cur.getObjectId()));
r.append("]");
r.append("(");
r.append(updateIndex); // Print value, even if -1
r.append(")]");
return r.toString();
}
}