@ -56,9 +56,11 @@ import static org.eclipse.jgit.internal.storage.pack.PackExt.PACK;
import java.io.IOException ;
import java.io.IOException ;
import java.util.ArrayList ;
import java.util.ArrayList ;
import java.util.Calendar ;
import java.util.Collection ;
import java.util.Collection ;
import java.util.Collections ;
import java.util.Collections ;
import java.util.EnumSet ;
import java.util.EnumSet ;
import java.util.GregorianCalendar ;
import java.util.HashSet ;
import java.util.HashSet ;
import java.util.List ;
import java.util.List ;
import java.util.Set ;
import java.util.Set ;
@ -82,6 +84,7 @@ import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.revwalk.RevWalk ;
import org.eclipse.jgit.revwalk.RevWalk ;
import org.eclipse.jgit.storage.pack.PackConfig ;
import org.eclipse.jgit.storage.pack.PackConfig ;
import org.eclipse.jgit.storage.pack.PackStatistics ;
import org.eclipse.jgit.storage.pack.PackStatistics ;
import org.eclipse.jgit.util.SystemReader ;
import org.eclipse.jgit.util.io.CountingOutputStream ;
import org.eclipse.jgit.util.io.CountingOutputStream ;
/** Repack and garbage collect a repository. */
/** Repack and garbage collect a repository. */
@ -100,7 +103,8 @@ public class DfsGarbageCollector {
private PackConfig packConfig ;
private PackConfig packConfig ;
// See pack(), below, for how these two variables interact.
// See packIsCoalesceableGarbage(), below, for how these two variables
// interact.
private long coalesceGarbageLimit = 50 < < 20 ;
private long coalesceGarbageLimit = 50 < < 20 ;
private long garbageTtlMillis = TimeUnit . DAYS . toMillis ( 1 ) ;
private long garbageTtlMillis = TimeUnit . DAYS . toMillis ( 1 ) ;
@ -228,14 +232,8 @@ public class DfsGarbageCollector {
if ( packConfig . getIndexVersion ( ) ! = 2 )
if ( packConfig . getIndexVersion ( ) ! = 2 )
throw new IllegalStateException (
throw new IllegalStateException (
JGitText . get ( ) . supportOnlyPackIndexVersion2 ) ;
JGitText . get ( ) . supportOnlyPackIndexVersion2 ) ;
if ( garbageTtlMillis > 0 ) {
// We disable coalescing because the coalescing step will keep
// refreshing the UNREACHABLE_GARBAGE pack and we wouldn't
// actually prune anything.
coalesceGarbageLimit = 0 ;
}
startTimeMillis = System . currentTimeMillis ( ) ;
startTimeMillis = SystemReader . getInstance ( ) . getCurrentTime ( ) ;
ctx = ( DfsReader ) objdb . newReader ( ) ;
ctx = ( DfsReader ) objdb . newReader ( ) ;
try {
try {
refdb . refresh ( ) ;
refdb . refresh ( ) ;
@ -310,14 +308,14 @@ public class DfsGarbageCollector {
expiredGarbagePacks = new ArrayList < DfsPackFile > ( packs . length ) ;
expiredGarbagePacks = new ArrayList < DfsPackFile > ( packs . length ) ;
long mostRecentGC = mostRecentGC ( packs ) ;
long mostRecentGC = mostRecentGC ( packs ) ;
long now = System . currentTimeMillis ( ) ;
long now = SystemReader . getInstan ce ( ) . getC urrentTime( ) ;
for ( DfsPackFile p : packs ) {
for ( DfsPackFile p : packs ) {
DfsPackDescription d = p . getPackDescription ( ) ;
DfsPackDescription d = p . getPackDescription ( ) ;
if ( d . getPackSource ( ) ! = UNREACHABLE_GARBAGE ) {
if ( d . getPackSource ( ) ! = UNREACHABLE_GARBAGE ) {
packsBefore . add ( p ) ;
packsBefore . add ( p ) ;
} else if ( packIsExpiredGarbage ( d , mostRecentGC , now ) ) {
} else if ( packIsExpiredGarbage ( d , mostRecentGC , now ) ) {
expiredGarbagePacks . add ( p ) ;
expiredGarbagePacks . add ( p ) ;
} else if ( d . getFileSize ( PackExt . PACK ) < coalesceGarbageLimit ) {
} else if ( packIsCoalesceableGarbage ( d , now ) ) {
packsBefore . add ( p ) ;
packsBefore . add ( p ) ;
}
}
}
}
@ -360,6 +358,68 @@ public class DfsGarbageCollector {
& & now - d . getLastModified ( ) > = garbageTtlMillis ;
& & now - d . getLastModified ( ) > = garbageTtlMillis ;
}
}
private boolean packIsCoalesceableGarbage ( DfsPackDescription d , long now ) {
// An UNREACHABLE_GARBAGE pack can be coalesced if its size is less than
// the coalesceGarbageLimit and either garbageTtl is zero or if the pack
// is created in a close time interval (on a single calendar day when
// the garbageTtl is more than one day or one third of the garbageTtl).
//
// When the garbageTtl is more than 24 hours, garbage packs that are
// created within a single calendar day are coalesced together. This
// would make the effective ttl of the garbage pack as garbageTtl+23:59
// and limit the number of garbage to a maximum number of
// garbageTtl_in_days + 1 (assuming all of them are less than the size
// of coalesceGarbageLimit).
//
// When the garbageTtl is less than or equal to 24 hours, garbage packs
// that are created within a one third of garbageTtl are coalesced
// together. This would make the effective ttl of the garbage packs as
// garbageTtl + (garbageTtl / 3) and would limit the number of garbage
// packs to a maximum number of 4 (assuming all of them are less than
// the size of coalesceGarbageLimit).
if ( d . getPackSource ( ) ! = UNREACHABLE_GARBAGE
| | d . getFileSize ( PackExt . PACK ) > = coalesceGarbageLimit ) {
return false ;
}
if ( garbageTtlMillis = = 0 ) {
return true ;
}
long lastModified = d . getLastModified ( ) ;
long dayStartLastModified = dayStartInMillis ( lastModified ) ;
long dayStartToday = dayStartInMillis ( now ) ;
if ( dayStartLastModified ! = dayStartToday ) {
return false ; // this pack is not created today.
}
if ( garbageTtlMillis > TimeUnit . DAYS . toMillis ( 1 ) ) {
return true ; // ttl is more than one day and pack is created today.
}
long timeInterval = garbageTtlMillis / 3 ;
if ( timeInterval = = 0 ) {
return false ; // ttl is too small, don't try to coalesce.
}
long modifiedTimeSlot = ( lastModified - dayStartLastModified ) / timeInterval ;
long presentTimeSlot = ( now - dayStartToday ) / timeInterval ;
return modifiedTimeSlot = = presentTimeSlot ;
}
private static long dayStartInMillis ( long timeInMillis ) {
Calendar cal = new GregorianCalendar (
SystemReader . getInstance ( ) . getTimeZone ( ) ) ;
cal . setTimeInMillis ( timeInMillis ) ;
cal . set ( Calendar . HOUR_OF_DAY , 0 ) ;
cal . set ( Calendar . MINUTE , 0 ) ;
cal . set ( Calendar . SECOND , 0 ) ;
cal . set ( Calendar . MILLISECOND , 0 ) ;
return cal . getTimeInMillis ( ) ;
}
/** @return all of the source packs that fed into this compaction. */
/** @return all of the source packs that fed into this compaction. */
public List < DfsPackDescription > getSourcePacks ( ) {
public List < DfsPackDescription > getSourcePacks ( ) {
return toPrune ( ) ;
return toPrune ( ) ;