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.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference; 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))); 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 @Test
public void testGetRefs_EmptyDatabase() throws IOException { public void testGetRefs_EmptyDatabase() throws IOException {
Map<String, Ref> all; Map<String, Ref> all;

View File

@ -44,6 +44,7 @@
package org.eclipse.jgit.internal.storage.reftable; package org.eclipse.jgit.internal.storage.reftable;
import static org.eclipse.jgit.lib.Constants.HEAD; 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.OBJECT_ID_LENGTH;
import static org.eclipse.jgit.lib.Constants.R_HEADS; import static org.eclipse.jgit.lib.Constants.R_HEADS;
import static org.eclipse.jgit.lib.Ref.Storage.NEW; import static org.eclipse.jgit.lib.Ref.Storage.NEW;
@ -68,6 +69,7 @@
import org.eclipse.jgit.lib.ObjectIdRef; import org.eclipse.jgit.lib.ObjectIdRef;
import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefComparator; import org.eclipse.jgit.lib.RefComparator;
import org.eclipse.jgit.lib.SymbolicRef;
import org.junit.Test; import org.junit.Test;
public class MergedReftableTest { public class MergedReftableTest {
@ -128,6 +130,7 @@ public void oneTableScan() throws IOException {
Ref act = rc.getRef(); Ref act = rc.getRef();
assertEquals(exp.getName(), act.getName()); assertEquals(exp.getName(), act.getName());
assertEquals(exp.getObjectId(), act.getObjectId()); assertEquals(exp.getObjectId(), act.getObjectId());
assertEquals(1, act.getUpdateIndex());
} }
assertFalse(rc.next()); assertFalse(rc.next());
} }
@ -145,6 +148,7 @@ public void deleteIsHidden() throws IOException {
assertTrue(rc.next()); assertTrue(rc.next());
assertEquals("refs/heads/master", rc.getRef().getName()); assertEquals("refs/heads/master", rc.getRef().getName());
assertEquals(id(2), rc.getRef().getObjectId()); assertEquals(id(2), rc.getRef().getObjectId());
assertEquals(1, rc.getRef().getUpdateIndex());
assertFalse(rc.next()); assertFalse(rc.next());
} }
} }
@ -162,6 +166,7 @@ public void twoTableSeek() throws IOException {
assertEquals("refs/heads/master", rc.getRef().getName()); assertEquals("refs/heads/master", rc.getRef().getName());
assertEquals(id(2), rc.getRef().getObjectId()); assertEquals(id(2), rc.getRef().getObjectId());
assertFalse(rc.next()); assertFalse(rc.next());
assertEquals(1, rc.getRef().getUpdateIndex());
} }
} }
@ -177,6 +182,7 @@ public void twoTableById() throws IOException {
assertTrue(rc.next()); assertTrue(rc.next());
assertEquals("refs/heads/master", rc.getRef().getName()); assertEquals("refs/heads/master", rc.getRef().getName());
assertEquals(id(2), rc.getRef().getObjectId()); assertEquals(id(2), rc.getRef().getObjectId());
assertEquals(1, rc.getRef().getUpdateIndex());
assertFalse(rc.next()); assertFalse(rc.next());
} }
} }
@ -212,6 +218,7 @@ public void fourTableScan() throws IOException {
Ref act = rc.getRef(); Ref act = rc.getRef();
assertEquals(exp.getName(), act.getName()); assertEquals(exp.getName(), act.getName());
assertEquals(exp.getObjectId(), act.getObjectId()); assertEquals(exp.getObjectId(), act.getObjectId());
assertEquals(1, rc.getRef().getUpdateIndex());
} }
assertFalse(rc.next()); assertFalse(rc.next());
} }
@ -231,9 +238,11 @@ public void scanDuplicates() throws IOException {
assertTrue(rc.next()); assertTrue(rc.next());
assertEquals("refs/heads/apple", rc.getRef().getName()); assertEquals("refs/heads/apple", rc.getRef().getName());
assertEquals(id(3), rc.getRef().getObjectId()); assertEquals(id(3), rc.getRef().getObjectId());
assertEquals(2000, rc.getRef().getUpdateIndex());
assertTrue(rc.next()); assertTrue(rc.next());
assertEquals("refs/heads/banana", rc.getRef().getName()); assertEquals("refs/heads/banana", rc.getRef().getName());
assertEquals(id(2), rc.getRef().getObjectId()); assertEquals(id(2), rc.getRef().getObjectId());
assertEquals(1000, rc.getRef().getUpdateIndex());
assertFalse(rc.next()); assertFalse(rc.next());
} }
} }
@ -251,12 +260,14 @@ public void scanIncludeDeletes() throws IOException {
Ref r = rc.getRef(); Ref r = rc.getRef();
assertEquals("refs/heads/master", r.getName()); assertEquals("refs/heads/master", r.getName());
assertEquals(id(8), r.getObjectId()); assertEquals(id(8), r.getObjectId());
assertEquals(1, rc.getRef().getUpdateIndex());
assertTrue(rc.next()); assertTrue(rc.next());
r = rc.getRef(); r = rc.getRef();
assertEquals("refs/heads/next", r.getName()); assertEquals("refs/heads/next", r.getName());
assertEquals(NEW, r.getStorage()); assertEquals(NEW, r.getStorage());
assertNull(r.getObjectId()); assertNull(r.getObjectId());
assertEquals(1, rc.getRef().getUpdateIndex());
assertFalse(rc.next()); assertFalse(rc.next());
} }
@ -277,6 +288,7 @@ public void oneTableSeek() throws IOException {
Ref act = rc.getRef(); Ref act = rc.getRef();
assertEquals(exp.getName(), act.getName()); assertEquals(exp.getName(), act.getName());
assertEquals(exp.getObjectId(), act.getObjectId()); assertEquals(exp.getObjectId(), act.getObjectId());
assertEquals(1, act.getUpdateIndex());
assertFalse(rc.next()); assertFalse(rc.next());
} }
} }
@ -303,17 +315,17 @@ public void missedUpdate() throws IOException {
assertTrue(rc.next()); assertTrue(rc.next());
assertEquals("refs/heads/a", rc.getRef().getName()); assertEquals("refs/heads/a", rc.getRef().getName());
assertEquals(id(1), rc.getRef().getObjectId()); assertEquals(id(1), rc.getRef().getObjectId());
assertEquals(1, rc.getUpdateIndex()); assertEquals(1, rc.getRef().getUpdateIndex());
assertTrue(rc.next()); assertTrue(rc.next());
assertEquals("refs/heads/b", rc.getRef().getName()); assertEquals("refs/heads/b", rc.getRef().getName());
assertEquals(id(2), rc.getRef().getObjectId()); assertEquals(id(2), rc.getRef().getObjectId());
assertEquals(2, rc.getUpdateIndex()); assertEquals(2, rc.getRef().getUpdateIndex());
assertTrue(rc.next()); assertTrue(rc.next());
assertEquals("refs/heads/c", rc.getRef().getName()); assertEquals("refs/heads/c", rc.getRef().getName());
assertEquals(id(3), rc.getRef().getObjectId()); 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) { private static MergedReftable merge(byte[]... table) {
List<Reftable> stack = new ArrayList<>(table.length); List<Reftable> stack = new ArrayList<>(table.length);
for (byte[] b : table) { for (byte[] b : table) {
@ -360,6 +429,14 @@ private static Ref ref(String name, int id) {
return new ObjectIdRef.PeeledNonTag(PACKED, name, id(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) { private static Ref delete(String name) {
return new ObjectIdRef.Unpeeled(NEW, name, null); return new ObjectIdRef.Unpeeled(NEW, name, null);
} }

View File

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

View File

@ -48,6 +48,10 @@
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame; import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.Arrays;
import java.util.List;
import org.junit.Test; import org.junit.Test;
@ -114,11 +118,44 @@ public void testConstructor_Peeled() {
assertSame(ID_B, r.getPeeledObjectId()); 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 @Test
public void testToString() { public void testToString() {
ObjectIdRef r; ObjectIdRef r;
r = new ObjectIdRef.Unpeeled(Ref.Storage.LOOSE, name, ID_A); 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; SymbolicRef r;
t = new ObjectIdRef.Unpeeled(Ref.Storage.NEW, targetName, null); 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(Ref.Storage.LOOSE, r.getStorage());
assertSame(name, r.getName()); assertSame(name, r.getName());
assertNull("no id on new ref", r.getObjectId()); assertNull("no id on new ref", r.getObjectId());
@ -77,9 +77,10 @@ public void testConstructor() {
assertSame("leaf is t", t, r.getLeaf()); assertSame("leaf is t", t, r.getLeaf());
assertSame("target is t", t, r.getTarget()); assertSame("target is t", t, r.getTarget());
assertTrue("is symbolic", r.isSymbolic()); assertTrue("is symbolic", r.isSymbolic());
assertTrue("holds update index", r.getUpdateIndex() == 1);
t = new ObjectIdRef.Unpeeled(Ref.Storage.PACKED, targetName, ID_A); 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(Ref.Storage.LOOSE, r.getStorage());
assertSame(name, r.getName()); assertSame(name, r.getName());
assertSame(ID_A, r.getObjectId()); assertSame(ID_A, r.getObjectId());
@ -88,6 +89,7 @@ public void testConstructor() {
assertSame("leaf is t", t, r.getLeaf()); assertSame("leaf is t", t, r.getLeaf());
assertSame("target is t", t, r.getTarget()); assertSame("target is t", t, r.getTarget());
assertTrue("is symbolic", r.isSymbolic()); assertTrue("is symbolic", r.isSymbolic());
assertTrue("holds update index", r.getUpdateIndex() == 2);
} }
@Test @Test
@ -133,6 +135,6 @@ public void testToString() {
d = new SymbolicRef("D", c); d = new SymbolicRef("D", c);
assertEquals("SymbolicRef[D -> C -> B -> " + targetName + "=" 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); super(repo);
} }
/** {@inheritDoc} */
@Override
public boolean hasVersioning() {
return true;
}
/** {@inheritDoc} */ /** {@inheritDoc} */
@Override @Override
public boolean performsAtomicTransactions() { public boolean performsAtomicTransactions() {

View File

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

View File

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

View File

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

View File

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

View File

@ -67,7 +67,25 @@ public static class Unpeeled extends ObjectIdRef {
*/ */
public Unpeeled(@NonNull Storage st, @NonNull String name, public Unpeeled(@NonNull Storage st, @NonNull String name,
@Nullable ObjectId id) { @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 @Override
@ -100,7 +118,28 @@ public static class PeeledTag extends ObjectIdRef {
*/ */
public PeeledTag(@NonNull Storage st, @NonNull String name, public PeeledTag(@NonNull Storage st, @NonNull String name,
@Nullable ObjectId id, @NonNull ObjectId p) { @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; peeledObjectId = p;
} }
@ -131,7 +170,25 @@ public static class PeeledNonTag extends ObjectIdRef {
*/ */
public PeeledNonTag(@NonNull Storage st, @NonNull String name, public PeeledNonTag(@NonNull Storage st, @NonNull String name,
@Nullable ObjectId id) { @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 @Override
@ -152,6 +209,8 @@ public boolean isPeeled() {
private final ObjectId objectId; private final ObjectId objectId;
private final long updateIndex;
/** /**
* Create a new ref pairing. * Create a new ref pairing.
* *
@ -162,12 +221,16 @@ public boolean isPeeled() {
* @param id * @param id
* current value of the ref. May be {@code null} to indicate a * current value of the ref. May be {@code null} to indicate a
* ref that does not exist yet. * 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, protected ObjectIdRef(@NonNull Storage st, @NonNull String name,
@Nullable ObjectId id) { @Nullable ObjectId id, long updateIndex) {
this.name = name; this.name = name;
this.storage = st; this.storage = st;
this.objectId = id; this.objectId = id;
this.updateIndex = updateIndex;
} }
/** {@inheritDoc} */ /** {@inheritDoc} */
@ -211,6 +274,15 @@ public Storage getStorage() {
return storage; return storage;
} }
/** {@inheritDoc} */
@Override
public long getUpdateIndex() {
if (updateIndex == -1) {
throw new UnsupportedOperationException();
}
return updateIndex;
}
/** {@inheritDoc} */ /** {@inheritDoc} */
@NonNull @NonNull
@Override @Override
@ -220,7 +292,9 @@ public String toString() {
r.append(getName()); r.append(getName());
r.append('='); r.append('=');
r.append(ObjectId.toString(getObjectId())); 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(); return r.toString();
} }
} }

View File

@ -217,4 +217,27 @@ public boolean isPacked() {
*/ */
@NonNull @NonNull
Storage getStorage(); 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(); 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. * Determine if a proposed reference name overlaps with an existing one.
* <p> * <p>

View File

@ -58,6 +58,8 @@ public class SymbolicRef implements Ref {
private final Ref target; private final Ref target;
private final long updateIndex;
/** /**
* Create a new ref pairing. * Create a new ref pairing.
* *
@ -69,6 +71,24 @@ public class SymbolicRef implements Ref {
public SymbolicRef(@NonNull String refName, @NonNull Ref target) { public SymbolicRef(@NonNull String refName, @NonNull Ref target) {
this.name = refName; this.name = refName;
this.target = target; 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} */ /** {@inheritDoc} */
@ -128,6 +148,15 @@ public boolean isPeeled() {
return getLeaf().isPeeled(); return getLeaf().isPeeled();
} }
/** {@inheritDoc} */
@Override
public long getUpdateIndex() {
if (updateIndex == -1) {
throw new UnsupportedOperationException();
}
return updateIndex;
}
/** {@inheritDoc} */ /** {@inheritDoc} */
@SuppressWarnings("nls") @SuppressWarnings("nls")
@Override @Override
@ -143,7 +172,9 @@ public String toString() {
r.append(cur.getName()); r.append(cur.getName());
r.append('='); r.append('=');
r.append(ObjectId.toString(cur.getObjectId())); r.append(ObjectId.toString(cur.getObjectId()));
r.append("]"); r.append("(");
r.append(updateIndex); // Print value, even if -1
r.append(")]");
return r.toString(); return r.toString();
} }
} }