@ -5,6 +5,7 @@ import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.GC
import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.INSERT ;
import static org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource.UNREACHABLE_GARBAGE ;
import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK ;
import static org.eclipse.jgit.internal.storage.pack.PackExt.REFTABLE ;
import static org.junit.Assert.assertEquals ;
import static org.junit.Assert.assertFalse ;
import static org.junit.Assert.assertNotNull ;
@ -13,19 +14,29 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail ;
import java.io.IOException ;
import java.nio.charset.StandardCharsets ;
import java.util.Collections ;
import java.util.concurrent.TimeUnit ;
import org.eclipse.jgit.internal.storage.dfs.DfsObjDatabase.PackSource ;
import org.eclipse.jgit.internal.storage.reftable.RefCursor ;
import org.eclipse.jgit.internal.storage.reftable.ReftableConfig ;
import org.eclipse.jgit.internal.storage.reftable.ReftableReader ;
import org.eclipse.jgit.internal.storage.reftable.ReftableWriter ;
import org.eclipse.jgit.junit.MockSystemReader ;
import org.eclipse.jgit.junit.TestRepository ;
import org.eclipse.jgit.lib.AnyObjectId ;
import org.eclipse.jgit.lib.BatchRefUpdate ;
import org.eclipse.jgit.lib.NullProgressMonitor ;
import org.eclipse.jgit.lib.ObjectId ;
import org.eclipse.jgit.lib.ObjectIdRef ;
import org.eclipse.jgit.lib.Ref ;
import org.eclipse.jgit.lib.Repository ;
import org.eclipse.jgit.revwalk.RevBlob ;
import org.eclipse.jgit.revwalk.RevCommit ;
import org.eclipse.jgit.revwalk.RevWalk ;
import org.eclipse.jgit.storage.pack.PackConfig ;
import org.eclipse.jgit.transport.ReceiveCommand ;
import org.eclipse.jgit.util.SystemReader ;
import org.junit.After ;
import org.junit.Before ;
@ -653,6 +664,185 @@ public class DfsGarbageCollectorTest {
assertEquals ( 2 , odb . getPacks ( ) . length ) ;
}
@SuppressWarnings ( "boxing" )
@Test
public void producesNewReftable ( ) throws Exception {
String master = "refs/heads/master" ;
RevCommit commit0 = commit ( ) . message ( "0" ) . create ( ) ;
RevCommit commit1 = commit ( ) . message ( "1" ) . parent ( commit0 ) . create ( ) ;
BatchRefUpdate bru = git . getRepository ( ) . getRefDatabase ( )
. newBatchUpdate ( ) ;
bru . addCommand ( new ReceiveCommand ( ObjectId . zeroId ( ) , commit1 , master ) ) ;
for ( int i = 1 ; i < = 5100 ; i + + ) {
bru . addCommand ( new ReceiveCommand ( ObjectId . zeroId ( ) , commit0 ,
String . format ( "refs/pulls/%04d" , i ) ) ) ;
}
try ( RevWalk rw = new RevWalk ( git . getRepository ( ) ) ) {
bru . execute ( rw , NullProgressMonitor . INSTANCE ) ;
}
DfsGarbageCollector gc = new DfsGarbageCollector ( repo ) ;
gc . setReftableConfig ( new ReftableConfig ( ) ) ;
run ( gc ) ;
// Single GC pack present with all objects.
assertEquals ( 1 , odb . getPacks ( ) . length ) ;
DfsPackFile pack = odb . getPacks ( ) [ 0 ] ;
DfsPackDescription desc = pack . getPackDescription ( ) ;
assertEquals ( GC , desc . getPackSource ( ) ) ;
assertTrue ( "commit0 in pack" , isObjectInPack ( commit0 , pack ) ) ;
assertTrue ( "commit1 in pack" , isObjectInPack ( commit1 , pack ) ) ;
// Sibling REFTABLE is also present.
assertTrue ( desc . hasFileExt ( REFTABLE ) ) ;
ReftableWriter . Stats stats = desc . getReftableStats ( ) ;
assertNotNull ( stats ) ;
assertTrue ( stats . totalBytes ( ) > 0 ) ;
assertEquals ( 5101 , stats . refCount ( ) ) ;
assertEquals ( 1 , stats . minUpdateIndex ( ) ) ;
assertEquals ( 1 , stats . maxUpdateIndex ( ) ) ;
DfsReftable table = new DfsReftable ( DfsBlockCache . getInstance ( ) , desc ) ;
try ( DfsReader ctx = odb . newReader ( ) ;
ReftableReader rr = table . open ( ctx ) ;
RefCursor rc = rr . seekRef ( "refs/pulls/5100" ) ) {
assertTrue ( rc . next ( ) ) ;
assertEquals ( commit0 , rc . getRef ( ) . getObjectId ( ) ) ;
assertFalse ( rc . next ( ) ) ;
}
}
@Test
public void leavesNonGcReftablesIfNotConfigured ( ) throws Exception {
String master = "refs/heads/master" ;
RevCommit commit0 = commit ( ) . message ( "0" ) . create ( ) ;
RevCommit commit1 = commit ( ) . message ( "1" ) . parent ( commit0 ) . create ( ) ;
git . update ( master , commit1 ) ;
DfsPackDescription t1 = odb . newPack ( INSERT ) ;
try ( DfsOutputStream out = odb . writeFile ( t1 , REFTABLE ) ) {
out . write ( "ignored" . getBytes ( StandardCharsets . UTF_8 ) ) ;
t1 . addFileExt ( REFTABLE ) ;
}
odb . commitPack ( Collections . singleton ( t1 ) , null ) ;
DfsGarbageCollector gc = new DfsGarbageCollector ( repo ) ;
gc . setReftableConfig ( null ) ;
run ( gc ) ;
// Single GC pack present with all objects.
assertEquals ( 1 , odb . getPacks ( ) . length ) ;
DfsPackFile pack = odb . getPacks ( ) [ 0 ] ;
DfsPackDescription desc = pack . getPackDescription ( ) ;
assertEquals ( GC , desc . getPackSource ( ) ) ;
assertTrue ( "commit0 in pack" , isObjectInPack ( commit0 , pack ) ) ;
assertTrue ( "commit1 in pack" , isObjectInPack ( commit1 , pack ) ) ;
// Only INSERT REFTABLE above is present.
DfsReftable [ ] tables = odb . getReftables ( ) ;
assertEquals ( 1 , tables . length ) ;
assertEquals ( t1 , tables [ 0 ] . getPackDescription ( ) ) ;
}
@Test
public void prunesNonGcReftables ( ) throws Exception {
String master = "refs/heads/master" ;
RevCommit commit0 = commit ( ) . message ( "0" ) . create ( ) ;
RevCommit commit1 = commit ( ) . message ( "1" ) . parent ( commit0 ) . create ( ) ;
git . update ( master , commit1 ) ;
DfsPackDescription t1 = odb . newPack ( INSERT ) ;
try ( DfsOutputStream out = odb . writeFile ( t1 , REFTABLE ) ) {
out . write ( "ignored" . getBytes ( StandardCharsets . UTF_8 ) ) ;
t1 . addFileExt ( REFTABLE ) ;
}
odb . commitPack ( Collections . singleton ( t1 ) , null ) ;
odb . clearCache ( ) ;
DfsGarbageCollector gc = new DfsGarbageCollector ( repo ) ;
gc . setReftableConfig ( new ReftableConfig ( ) ) ;
run ( gc ) ;
// Single GC pack present with all objects.
assertEquals ( 1 , odb . getPacks ( ) . length ) ;
DfsPackFile pack = odb . getPacks ( ) [ 0 ] ;
DfsPackDescription desc = pack . getPackDescription ( ) ;
assertEquals ( GC , desc . getPackSource ( ) ) ;
assertTrue ( "commit0 in pack" , isObjectInPack ( commit0 , pack ) ) ;
assertTrue ( "commit1 in pack" , isObjectInPack ( commit1 , pack ) ) ;
// Only sibling GC REFTABLE is present.
DfsReftable [ ] tables = odb . getReftables ( ) ;
assertEquals ( 1 , tables . length ) ;
assertEquals ( desc , tables [ 0 ] . getPackDescription ( ) ) ;
assertTrue ( desc . hasFileExt ( REFTABLE ) ) ;
}
@Test
public void compactsReftables ( ) throws Exception {
String master = "refs/heads/master" ;
RevCommit commit0 = commit ( ) . message ( "0" ) . create ( ) ;
RevCommit commit1 = commit ( ) . message ( "1" ) . parent ( commit0 ) . create ( ) ;
git . update ( master , commit1 ) ;
DfsGarbageCollector gc = new DfsGarbageCollector ( repo ) ;
gc . setReftableConfig ( new ReftableConfig ( ) ) ;
run ( gc ) ;
DfsPackDescription t1 = odb . newPack ( INSERT ) ;
Ref next = new ObjectIdRef . PeeledNonTag ( Ref . Storage . LOOSE ,
"refs/heads/next" , commit0 . copy ( ) ) ;
try ( DfsOutputStream out = odb . writeFile ( t1 , REFTABLE ) ) {
ReftableWriter w = new ReftableWriter ( ) ;
w . setMinUpdateIndex ( 42 ) ;
w . setMaxUpdateIndex ( 42 ) ;
w . begin ( out ) ;
w . sortAndWriteRefs ( Collections . singleton ( next ) ) ;
w . finish ( ) ;
t1 . addFileExt ( REFTABLE ) ;
t1 . setReftableStats ( w . getStats ( ) ) ;
}
odb . commitPack ( Collections . singleton ( t1 ) , null ) ;
gc = new DfsGarbageCollector ( repo ) ;
gc . setReftableConfig ( new ReftableConfig ( ) ) ;
run ( gc ) ;
// Single GC pack present with all objects.
assertEquals ( 1 , odb . getPacks ( ) . length ) ;
DfsPackFile pack = odb . getPacks ( ) [ 0 ] ;
DfsPackDescription desc = pack . getPackDescription ( ) ;
assertEquals ( GC , desc . getPackSource ( ) ) ;
assertTrue ( "commit0 in pack" , isObjectInPack ( commit0 , pack ) ) ;
assertTrue ( "commit1 in pack" , isObjectInPack ( commit1 , pack ) ) ;
// Only sibling GC REFTABLE is present.
DfsReftable [ ] tables = odb . getReftables ( ) ;
assertEquals ( 1 , tables . length ) ;
assertEquals ( desc , tables [ 0 ] . getPackDescription ( ) ) ;
assertTrue ( desc . hasFileExt ( REFTABLE ) ) ;
// GC reftable contains the compaction.
DfsReftable table = new DfsReftable ( DfsBlockCache . getInstance ( ) , desc ) ;
try ( DfsReader ctx = odb . newReader ( ) ;
ReftableReader rr = table . open ( ctx ) ;
RefCursor rc = rr . allRefs ( ) ) {
assertEquals ( 1 , rr . minUpdateIndex ( ) ) ;
assertEquals ( 42 , rr . maxUpdateIndex ( ) ) ;
assertTrue ( rc . next ( ) ) ;
assertEquals ( master , rc . getRef ( ) . getName ( ) ) ;
assertEquals ( commit1 , rc . getRef ( ) . getObjectId ( ) ) ;
assertTrue ( rc . next ( ) ) ;
assertEquals ( next . getName ( ) , rc . getRef ( ) . getName ( ) ) ;
assertEquals ( commit0 , rc . getRef ( ) . getObjectId ( ) ) ;
assertFalse ( rc . next ( ) ) ;
}
}
private TestRepository < InMemoryRepository > . CommitBuilder commit ( ) {
return git . commit ( ) ;
}