diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java index 5a2bd9c33..56346e1d8 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java @@ -61,6 +61,7 @@ import java.io.File; 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 class RefDirectoryTest extends LocalDiskRepositoryTestCase { 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 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 all; diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/MergedReftableTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/MergedReftableTest.java index 1d11573b9..9f7e7331e 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/MergedReftableTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/MergedReftableTest.java @@ -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.ObjectId; 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 class MergedReftableTest { 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 class MergedReftableTest { 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 class MergedReftableTest { 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 class MergedReftableTest { 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 class MergedReftableTest { 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 class MergedReftableTest { 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 class MergedReftableTest { 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 class MergedReftableTest { Ref act = rc.getRef(); assertEquals(exp.getName(), act.getName()); assertEquals(exp.getObjectId(), act.getObjectId()); + assertEquals(1, act.getUpdateIndex()); assertFalse(rc.next()); } } @@ -303,16 +315,19 @@ public class MergedReftableTest { assertTrue(rc.next()); assertEquals("refs/heads/a", rc.getRef().getName()); assertEquals(id(1), rc.getRef().getObjectId()); + assertEquals(1, rc.getRef().getUpdateIndex()); assertEquals(1, rc.getUpdateIndex()); assertTrue(rc.next()); assertEquals("refs/heads/b", rc.getRef().getName()); assertEquals(id(2), rc.getRef().getObjectId()); + assertEquals(2, rc.getRef().getUpdateIndex()); assertEquals(2, rc.getUpdateIndex()); assertTrue(rc.next()); assertEquals("refs/heads/c", rc.getRef().getName()); assertEquals(id(3), rc.getRef().getObjectId()); + assertEquals(3, rc.getRef().getUpdateIndex()); assertEquals(3, rc.getUpdateIndex()); } } @@ -344,6 +359,63 @@ public class MergedReftableTest { } } + @Test + public void versioningSymbolicReftargetMoves() throws IOException { + Ref master = ref(MASTER, 100); + + List delta1 = Arrays.asList(master, sym(HEAD, MASTER)); + List 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 delta1 = Arrays.asList(ref(MASTER, 100), branchX, + sym(HEAD, MASTER)); + List delta2 = Arrays.asList(sym(HEAD, "refs/heads/branchX")); + List 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 delta1 = Arrays.asList(sym(HEAD, "refs/heads/tmp"), + sym("refs/heads/tmp", MASTER), ref(MASTER, 100)); + List delta2 = Arrays.asList(ref(MASTER, 200)); + List 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 stack = new ArrayList<>(table.length); for (byte[] b : table) { @@ -360,6 +432,14 @@ public class MergedReftableTest { 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); } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/ReftableCompactorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/ReftableCompactorTest.java index 46a37ff60..20228283b 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/ReftableCompactorTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/ReftableCompactorTest.java @@ -108,6 +108,7 @@ public class ReftableCompactorTest { assertTrue(rc.next()); assertEquals(MASTER, rc.getRef().getName()); assertEquals(id(1), rc.getRef().getObjectId()); + assertEquals(0, rc.getRef().getUpdateIndex()); assertEquals(0, rc.getUpdateIndex()); } } @@ -155,6 +156,7 @@ public class ReftableCompactorTest { assertTrue(rc.next()); assertEquals(MASTER, rc.getRef().getName()); assertEquals(id(2), rc.getRef().getObjectId()); + assertEquals(1, rc.getRef().getUpdateIndex()); assertEquals(1, rc.getUpdateIndex()); } } @@ -203,11 +205,13 @@ public class ReftableCompactorTest { assertTrue(rc.next()); assertEquals(MASTER, rc.getRef().getName()); assertEquals(id(3), rc.getRef().getObjectId()); + assertEquals(1, rc.getRef().getUpdateIndex()); assertEquals(1, rc.getUpdateIndex()); assertTrue(rc.next()); assertEquals(NEXT, rc.getRef().getName()); assertEquals(id(2), rc.getRef().getObjectId()); + assertEquals(0, rc.getRef().getUpdateIndex()); assertEquals(0, rc.getUpdateIndex()); } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/ReftableTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/ReftableTest.java index 0ee785c60..d72d6d424 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/ReftableTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/ReftableTest.java @@ -186,6 +186,7 @@ public class ReftableTest { 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 class ReftableTest { Ref act = rc.getRef(); assertNotNull(act); assertEquals(exp.getName(), act.getName()); + assertEquals(0, act.getUpdateIndex()); assertFalse(rc.next()); } } @@ -216,6 +218,7 @@ public class ReftableTest { assertEquals(exp.getName(), act.getName()); assertEquals(exp.getObjectId(), act.getObjectId()); assertEquals(exp.getPeeledObjectId(), act.getPeeledObjectId()); + assertEquals(0, act.getUpdateIndex()); } } @@ -237,6 +240,7 @@ public class ReftableTest { assertNotNull(act.getLeaf()); assertEquals(MASTER, act.getTarget().getName()); assertNull(act.getObjectId()); + assertEquals(0, act.getUpdateIndex()); } } @@ -250,14 +254,17 @@ public class ReftableTest { 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 class ReftableTest { 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,13 @@ public class ReftableTest { assertTrue(rc.next()); assertEquals(MASTER, rc.getRef().getName()); assertEquals(id(1), rc.getRef().getObjectId()); + assertEquals(1, rc.getRef().getUpdateIndex()); assertEquals(1, rc.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 +581,7 @@ public class ReftableTest { 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 +592,7 @@ public class ReftableTest { 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 +614,7 @@ public class ReftableTest { 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 +625,7 @@ public class ReftableTest { 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 +670,6 @@ public class ReftableTest { } } - private static void assertScan(List refs, Reftable t) throws IOException { try (RefCursor rc = t.allRefs()) { @@ -663,6 +678,7 @@ public class ReftableTest { Ref act = rc.getRef(); assertEquals(exp.getName(), act.getName()); assertEquals(exp.getObjectId(), act.getObjectId()); + assertEquals(0, rc.getRef().getUpdateIndex()); } assertFalse(rc.next()); } @@ -676,6 +692,7 @@ public class ReftableTest { Ref act = rc.getRef(); assertEquals(exp.getName(), act.getName()); assertEquals(exp.getObjectId(), act.getObjectId()); + assertEquals(0, rc.getRef().getUpdateIndex()); assertFalse(rc.next()); } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdRefTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdRefTest.java index fb16c6bac..6553bfa83 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdRefTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdRefTest.java @@ -48,6 +48,10 @@ import static org.junit.Assert.assertFalse; 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 class ObjectIdRefTest { 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 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()); } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/SymbolicRefTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/SymbolicRefTest.java index 1342253c0..99b2211e6 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/SymbolicRefTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/SymbolicRefTest.java @@ -68,7 +68,7 @@ public class SymbolicRefTest { 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 class SymbolicRefTest { 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 class SymbolicRefTest { 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 class SymbolicRefTest { d = new SymbolicRef("D", c); assertEquals("SymbolicRef[D -> C -> B -> " + targetName + "=" - + ID_A.name() + "]", d.toString()); + + ID_A.name() + "(-1)]", d.toString()); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftableDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftableDatabase.java index 70816307f..0e0a6ef5e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftableDatabase.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftableDatabase.java @@ -97,6 +97,12 @@ public class DfsReftableDatabase extends DfsRefDatabase { super(repo); } + /** {@inheritDoc} */ + @Override + public boolean hasVersioning() { + return true; + } + /** {@inheritDoc} */ @Override public boolean performsAtomicTransactions() { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockReader.java index ce2ba4a2e..44529bfff 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockReader.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockReader.java @@ -170,24 +170,27 @@ class BlockReader { 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 @@ class BlockReader { *
    *
  • {@link #name()} *
  • {@link #match(byte[], boolean)} - *
  • {@link #readRef()} + *
  • {@link #readRef(long)} *
  • {@link #readLogUpdateIndex()} *
  • {@link #readLogEntry()} *
  • {@link #readBlockPositionList()} @@ -575,8 +578,8 @@ class BlockReader { 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() { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/Reftable.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/Reftable.java index a1087e202..cb02628e8 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/Reftable.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/Reftable.java @@ -280,7 +280,7 @@ public abstract class Reftable implements AutoCloseable { 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} */ diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableReader.java index 81b30e4cb..8fde7e3b2 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableReader.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableReader.java @@ -479,7 +479,6 @@ public class ReftableReader extends Reftable { private final boolean prefix; private Ref ref; - private long updateIndex; BlockReader block; RefCursorImpl(long scanEnd, byte[] match, boolean prefix) { @@ -508,8 +507,7 @@ public class ReftableReader extends Reftable { return false; } - updateIndex = minUpdateIndex + block.readUpdateIndexDelta(); - ref = block.readRef(); + ref = block.readRef(minUpdateIndex); if (!includeDeletes && wasDeleted()) { continue; } @@ -524,7 +522,7 @@ public class ReftableReader extends Reftable { @Override public long getUpdateIndex() { - return updateIndex; + return ref.getUpdateIndex(); } @Override @@ -605,7 +603,6 @@ public class ReftableReader extends Reftable { private final ObjectId match; private Ref ref; - private long updateIndex; private int listIdx; private LongList blockPos; @@ -679,8 +676,7 @@ public class ReftableReader extends Reftable { } 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())) { @@ -696,7 +692,7 @@ public class ReftableReader extends Reftable { @Override public long getUpdateIndex() { - return updateIndex; + return ref.getUpdateIndex(); } @Override diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdRef.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdRef.java index 22aaa3ad7..747318170 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdRef.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdRef.java @@ -67,7 +67,25 @@ public abstract class ObjectIdRef implements Ref { */ 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 abstract class ObjectIdRef implements Ref { */ 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 abstract class ObjectIdRef implements Ref { */ 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 abstract class ObjectIdRef implements Ref { private final ObjectId objectId; + private final long updateIndex; + /** * Create a new ref pairing. * @@ -162,12 +221,16 @@ public abstract class ObjectIdRef implements Ref { * @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 abstract class ObjectIdRef implements Ref { return storage; } + /** {@inheritDoc} */ + @Override + public long getUpdateIndex() { + if (updateIndex == -1) { + throw new UnsupportedOperationException(); + } + return updateIndex; + } + /** {@inheritDoc} */ @NonNull @Override @@ -220,7 +292,9 @@ public abstract class ObjectIdRef implements Ref { 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(); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Ref.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Ref.java index faabbf892..32c8b06c9 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Ref.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Ref.java @@ -217,4 +217,27 @@ public interface Ref { */ @NonNull Storage getStorage(); + + /** + * Indicator of the relative order between updates of a specific reference + * name. A number that increases when a reference is updated. + *

    + * 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. + *

    + * 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(); + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java index 68929b422..5010a89ed 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java @@ -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. *

    diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/SymbolicRef.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/SymbolicRef.java index d4b83b012..0e9a23fe1 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/SymbolicRef.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/SymbolicRef.java @@ -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 class SymbolicRef implements Ref { 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 class SymbolicRef implements Ref { 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(); } }