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>
This commit is contained in:
parent
f3897ac6c3
commit
15a189e4e0
|
@ -587,6 +587,7 @@ public <T extends AnyObjectId> T update(String ref, T obj) throws Exception {
|
|||
public void delete(String ref) throws Exception {
|
||||
ref = normalizeRef(ref);
|
||||
RefUpdate u = db.updateRef(ref);
|
||||
u.setForceUpdate(true);
|
||||
switch (u.delete()) {
|
||||
case FAST_FORWARD:
|
||||
case FORCED:
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
@ -18,6 +19,7 @@
|
|||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
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.ReftableConfig;
|
||||
import org.eclipse.jgit.internal.storage.reftable.ReftableReader;
|
||||
|
@ -842,6 +844,106 @@ public void compactsReftables() throws Exception {
|
|||
}
|
||||
}
|
||||
|
||||
@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() {
|
||||
return git.commit();
|
||||
}
|
||||
|
|
|
@ -108,6 +108,7 @@ public class DfsGarbageCollector {
|
|||
private PackConfig packConfig;
|
||||
private ReftableConfig reftableConfig;
|
||||
private boolean convertToReftable = true;
|
||||
private boolean includeDeletes;
|
||||
private long reftableInitialMinUpdateIndex = 1;
|
||||
private long reftableInitialMaxUpdateIndex = 1;
|
||||
|
||||
|
@ -185,6 +186,17 @@ public DfsGarbageCollector setConvertToReftable(boolean convert) {
|
|||
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.
|
||||
*
|
||||
|
@ -699,7 +711,7 @@ private void writeReftable(DfsPackDescription pack) throws IOException {
|
|||
try (ReftableStack stack = ReftableStack.open(ctx, reftablesBefore)) {
|
||||
ReftableCompactor compact = new ReftableCompactor();
|
||||
compact.addAll(stack.readers());
|
||||
compact.setIncludeDeletes(false);
|
||||
compact.setIncludeDeletes(includeDeletes);
|
||||
compactReftable(pack, compact);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue