Browse Source

RefDatabase/Ref: Add versioning to reference database

In DFS implementations the reference table can fall out of sync, but
it is not possible to check this situation in the current API.

Add a property to the Refs indicating the order of its updates.  This
version is set only by RefDatabase implementations that support
versioning (e.g reftable based).

Caller is responsible to check if the reference db creates versioned
refs before accessing getUpdateIndex(). E.g:

   Ref ref = refdb.exactRef(...);
   if (refdb.hasVersioning()) {
       ref.getUpdateIndex();
   }

Change-Id: I0d5ec8e8df47c730301b2e12851a6bf3dac9d120
Signed-off-by: Ivan Frade <ifrade@google.com>
stable-5.3
Ivan Frade 6 years ago
parent
commit
6ea888a036
  1. 28
      org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/RefDirectoryTest.java
  2. 80
      org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/MergedReftableTest.java
  3. 4
      org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/ReftableCompactorTest.java
  4. 19
      org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/ReftableTest.java
  5. 39
      org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/ObjectIdRefTest.java
  6. 8
      org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/SymbolicRefTest.java
  7. 6
      org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsReftableDatabase.java
  8. 19
      org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockReader.java
  9. 2
      org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/Reftable.java
  10. 12
      org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableReader.java
  11. 84
      org.eclipse.jgit/src/org/eclipse/jgit/lib/ObjectIdRef.java
  12. 23
      org.eclipse.jgit/src/org/eclipse/jgit/lib/Ref.java
  13. 13
      org.eclipse.jgit/src/org/eclipse/jgit/lib/RefDatabase.java
  14. 33
      org.eclipse.jgit/src/org/eclipse/jgit/lib/SymbolicRef.java

28
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<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;

80
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<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 +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);
}

4
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());
}
}

19
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<Ref> 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());
}
}

39
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<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());
}
}

8
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());
}
}

6
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() {

19
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 {
* <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 @@ 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() {

2
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} */

12
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

84
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();
}
}

23
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.
* <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();
}
}

13
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.
* <p>

33
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();
}
}

Loading…
Cancel
Save