Browse Source

Add flag for keeping ref tombstones in GC reftable

A tombstone will prevent a delayed reference update from resurrecting the
deleted reference.

Change-Id: Id9f4df43d435a299ff16cef614821439edef9b11
Signed-off-by: Minh Thai <mthai@google.com>
stable-4.10
Minh Thai 7 years ago
parent
commit
15a189e4e0
  1. 1
      org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java
  2. 102
      org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollectorTest.java
  3. 14
      org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java

1
org.eclipse.jgit.junit/src/org/eclipse/jgit/junit/TestRepository.java

@ -587,6 +587,7 @@ public class TestRepository<R extends Repository> {
public void delete(String ref) throws Exception { public void delete(String ref) throws Exception {
ref = normalizeRef(ref); ref = normalizeRef(ref);
RefUpdate u = db.updateRef(ref); RefUpdate u = db.updateRef(ref);
u.setForceUpdate(true);
switch (u.delete()) { switch (u.delete()) {
case FAST_FORWARD: case FAST_FORWARD:
case FORCED: case FORCED:

102
org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollectorTest.java

@ -9,6 +9,7 @@ import static org.eclipse.jgit.internal.storage.pack.PackExt.REFTABLE;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
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 static org.junit.Assert.fail;
@ -18,6 +19,7 @@ import java.util.Collections;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource; import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource;
import org.eclipse.jgit.internal.storage.dfs.DfsRefDatabase;
import org.eclipse.jgit.internal.storage.reftable.RefCursor; import org.eclipse.jgit.internal.storage.reftable.RefCursor;
import org.eclipse.jgit.internal.storage.reftable.ReftableConfig; import org.eclipse.jgit.internal.storage.reftable.ReftableConfig;
import org.eclipse.jgit.internal.storage.reftable.ReftableReader; import org.eclipse.jgit.internal.storage.reftable.ReftableReader;
@ -842,6 +844,106 @@ public class DfsGarbageCollectorTest {
} }
} }
@Test
public void reftableWithoutTombstoneResurrected() throws Exception {
RevCommit commit0 = commit().message("0").create();
String NEXT = "refs/heads/next";
DfsRefDatabase refdb = (DfsRefDatabase)repo.getRefDatabase();
git.update(NEXT, commit0);
Ref next = refdb.exactRef(NEXT);
assertNotNull(next);
assertEquals(commit0, next.getObjectId());
git.delete(NEXT);
refdb.clearCache();
assertNull(refdb.exactRef(NEXT));
DfsGarbageCollector gc = new DfsGarbageCollector(repo);
gc.setReftableConfig(new ReftableConfig());
gc.setIncludeDeletes(false);
gc.setConvertToReftable(false);
run(gc);
assertEquals(1, odb.getReftables().length);
try (DfsReader ctx = odb.newReader();
ReftableReader rr = odb.getReftables()[0].open(ctx)) {
rr.setIncludeDeletes(true);
assertEquals(1, rr.minUpdateIndex());
assertEquals(2, rr.maxUpdateIndex());
assertNull(rr.exactRef(NEXT));
}
RevCommit commit1 = commit().message("1").create();
DfsPackDescription t1 = odb.newPack(INSERT);
Ref newNext = new ObjectIdRef.PeeledNonTag(Ref.Storage.LOOSE, NEXT,
commit1);
try (DfsOutputStream out = odb.writeFile(t1, REFTABLE)) {
ReftableWriter w = new ReftableWriter();
w.setMinUpdateIndex(1);
w.setMaxUpdateIndex(1);
w.begin(out);
w.writeRef(newNext, 1);
w.finish();
t1.addFileExt(REFTABLE);
t1.setReftableStats(w.getStats());
}
odb.commitPack(Collections.singleton(t1), null);
assertEquals(2, odb.getReftables().length);
refdb.clearCache();
newNext = refdb.exactRef(NEXT);
assertNotNull(newNext);
assertEquals(commit1, newNext.getObjectId());
}
@Test
public void reftableWithTombstoneNotResurrected() throws Exception {
RevCommit commit0 = commit().message("0").create();
String NEXT = "refs/heads/next";
DfsRefDatabase refdb = (DfsRefDatabase)repo.getRefDatabase();
git.update(NEXT, commit0);
Ref next = refdb.exactRef(NEXT);
assertNotNull(next);
assertEquals(commit0, next.getObjectId());
git.delete(NEXT);
refdb.clearCache();
assertNull(refdb.exactRef(NEXT));
DfsGarbageCollector gc = new DfsGarbageCollector(repo);
gc.setReftableConfig(new ReftableConfig());
gc.setIncludeDeletes(true);
gc.setConvertToReftable(false);
run(gc);
assertEquals(1, odb.getReftables().length);
try (DfsReader ctx = odb.newReader();
ReftableReader rr = odb.getReftables()[0].open(ctx)) {
rr.setIncludeDeletes(true);
assertEquals(1, rr.minUpdateIndex());
assertEquals(2, rr.maxUpdateIndex());
next = rr.exactRef(NEXT);
assertNotNull(next);
assertNull(next.getObjectId());
}
RevCommit commit1 = commit().message("1").create();
DfsPackDescription t1 = odb.newPack(INSERT);
Ref newNext = new ObjectIdRef.PeeledNonTag(Ref.Storage.LOOSE, NEXT,
commit1);
try (DfsOutputStream out = odb.writeFile(t1, REFTABLE)) {
ReftableWriter w = new ReftableWriter();
w.setMinUpdateIndex(1);
w.setMaxUpdateIndex(1);
w.begin(out);
w.writeRef(newNext, 1);
w.finish();
t1.addFileExt(REFTABLE);
t1.setReftableStats(w.getStats());
}
odb.commitPack(Collections.singleton(t1), null);
assertEquals(2, odb.getReftables().length);
refdb.clearCache();
assertNull(refdb.exactRef(NEXT));
}
private TestRepository<InMemoryRepository>.CommitBuilder commit() { private TestRepository<InMemoryRepository>.CommitBuilder commit() {
return git.commit(); return git.commit();
} }

14
org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsGarbageCollector.java

@ -108,6 +108,7 @@ public class DfsGarbageCollector {
private PackConfig packConfig; private PackConfig packConfig;
private ReftableConfig reftableConfig; private ReftableConfig reftableConfig;
private boolean convertToReftable = true; private boolean convertToReftable = true;
private boolean includeDeletes;
private long reftableInitialMinUpdateIndex = 1; private long reftableInitialMinUpdateIndex = 1;
private long reftableInitialMaxUpdateIndex = 1; private long reftableInitialMaxUpdateIndex = 1;
@ -185,6 +186,17 @@ public class DfsGarbageCollector {
return this; return this;
} }
/**
* @param include
* if true, the garbage collector will include tombstones for
* deleted references in the reftable. Default is {@code false}.
* @return {@code this}
*/
public DfsGarbageCollector setIncludeDeletes(boolean include) {
includeDeletes = include;
return this;
}
/** /**
* Set minUpdateIndex for the initial reftable created during conversion. * Set minUpdateIndex for the initial reftable created during conversion.
* *
@ -699,7 +711,7 @@ public class DfsGarbageCollector {
try (ReftableStack stack = ReftableStack.open(ctx, reftablesBefore)) { try (ReftableStack stack = ReftableStack.open(ctx, reftablesBefore)) {
ReftableCompactor compact = new ReftableCompactor(); ReftableCompactor compact = new ReftableCompactor();
compact.addAll(stack.readers()); compact.addAll(stack.readers());
compact.setIncludeDeletes(false); compact.setIncludeDeletes(includeDeletes);
compactReftable(pack, compact); compactReftable(pack, compact);
} }
} }

Loading…
Cancel
Save