Set<ObjectId> all = new HashSet<ObjectId>(); | Set<ObjectId> all = new HashSet<ObjectId>(); | ||||
for (Ref r : db.getAllRefs().values()) | for (Ref r : db.getAllRefs().values()) | ||||
all.add(r.getObjectId()); | all.add(r.getObjectId()); | ||||
pw.preparePack(m, all, Collections.<ObjectId> emptySet()); | |||||
pw.preparePack(m, all, PackWriter.NONE); | |||||
final ObjectId name = pw.computeName(); | final ObjectId name = pw.computeName(); | ||||
import static org.junit.Assert.assertNotNull; | import static org.junit.Assert.assertNotNull; | ||||
import static org.junit.Assert.assertTrue; | import static org.junit.Assert.assertTrue; | ||||
import static org.junit.Assert.fail; | import static org.junit.Assert.fail; | ||||
import static org.eclipse.jgit.internal.storage.pack.PackWriter.NONE; | |||||
import java.io.ByteArrayInputStream; | import java.io.ByteArrayInputStream; | ||||
import java.io.ByteArrayOutputStream; | import java.io.ByteArrayOutputStream; | ||||
public class PackWriterTest extends SampleDataRepositoryTestCase { | public class PackWriterTest extends SampleDataRepositoryTestCase { | ||||
private static final Set<ObjectId> EMPTY_SET_OBJECT = Collections | |||||
.<ObjectId> emptySet(); | |||||
private static final List<RevObject> EMPTY_LIST_REVS = Collections | private static final List<RevObject> EMPTY_LIST_REVS = Collections | ||||
.<RevObject> emptyList(); | .<RevObject> emptyList(); | ||||
*/ | */ | ||||
@Test | @Test | ||||
public void testWriteEmptyPack1() throws IOException { | public void testWriteEmptyPack1() throws IOException { | ||||
createVerifyOpenPack(EMPTY_SET_OBJECT, EMPTY_SET_OBJECT, false, false); | |||||
createVerifyOpenPack(NONE, NONE, false, false); | |||||
assertEquals(0, writer.getObjectCount()); | assertEquals(0, writer.getObjectCount()); | ||||
assertEquals(0, pack.getObjectCount()); | assertEquals(0, pack.getObjectCount()); | ||||
final ObjectId nonExisting = ObjectId | final ObjectId nonExisting = ObjectId | ||||
.fromString("0000000000000000000000000000000000000001"); | .fromString("0000000000000000000000000000000000000001"); | ||||
try { | try { | ||||
createVerifyOpenPack(EMPTY_SET_OBJECT, Collections.singleton( | |||||
nonExisting), false, false); | |||||
createVerifyOpenPack(NONE, Collections.singleton(nonExisting), | |||||
false, false); | |||||
fail("Should have thrown MissingObjectException"); | fail("Should have thrown MissingObjectException"); | ||||
} catch (MissingObjectException x) { | } catch (MissingObjectException x) { | ||||
// expected | // expected | ||||
public void testIgnoreNonExistingObjects() throws IOException { | public void testIgnoreNonExistingObjects() throws IOException { | ||||
final ObjectId nonExisting = ObjectId | final ObjectId nonExisting = ObjectId | ||||
.fromString("0000000000000000000000000000000000000001"); | .fromString("0000000000000000000000000000000000000001"); | ||||
createVerifyOpenPack(EMPTY_SET_OBJECT, Collections.singleton( | |||||
nonExisting), false, true); | |||||
createVerifyOpenPack(NONE, Collections.singleton(nonExisting), | |||||
false, true); | |||||
// shouldn't throw anything | // shouldn't throw anything | ||||
} | } | ||||
final ObjectId nonExisting = ObjectId | final ObjectId nonExisting = ObjectId | ||||
.fromString("0000000000000000000000000000000000000001"); | .fromString("0000000000000000000000000000000000000001"); | ||||
new GC(db).gc(); | new GC(db).gc(); | ||||
createVerifyOpenPack(EMPTY_SET_OBJECT, | |||||
Collections.singleton(nonExisting), false, true, true); | |||||
createVerifyOpenPack(NONE, Collections.singleton(nonExisting), false, | |||||
true, true); | |||||
// shouldn't throw anything | // shouldn't throw anything | ||||
} | } | ||||
pw.setReuseDeltaCommits(false); | pw.setReuseDeltaCommits(false); | ||||
for (ObjectIdSet idx : excludeObjects) | for (ObjectIdSet idx : excludeObjects) | ||||
pw.excludeObjects(idx); | pw.excludeObjects(idx); | ||||
pw.preparePack(NullProgressMonitor.INSTANCE, want, | |||||
Collections.<ObjectId> emptySet()); | |||||
pw.preparePack(NullProgressMonitor.INSTANCE, want, NONE); | |||||
String id = pw.computeName().getName(); | String id = pw.computeName().getName(); | ||||
File packdir = new File(repo.getObjectsDirectory(), "pack"); | File packdir = new File(repo.getObjectsDirectory(), "pack"); | ||||
File packFile = new File(packdir, "pack-" + id + ".pack"); | File packFile = new File(packdir, "pack-" + id + ".pack"); | ||||
final HashSet<ObjectId> interestings = new HashSet<ObjectId>(); | final HashSet<ObjectId> interestings = new HashSet<ObjectId>(); | ||||
interestings.add(ObjectId | interestings.add(ObjectId | ||||
.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7")); | .fromString("82c6b885ff600be425b4ea96dee75dca255b69e7")); | ||||
createVerifyOpenPack(interestings, EMPTY_SET_OBJECT, false, false); | |||||
createVerifyOpenPack(interestings, NONE, false, false); | |||||
final ObjectId expectedOrder[] = new ObjectId[] { | final ObjectId expectedOrder[] = new ObjectId[] { | ||||
ObjectId.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"), | ObjectId.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"), |
import java.io.IOException; | import java.io.IOException; | ||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.Collection; | import java.util.Collection; | ||||
import java.util.Collections; | |||||
import java.util.HashSet; | import java.util.HashSet; | ||||
import java.util.List; | import java.util.List; | ||||
import java.util.Set; | import java.util.Set; | ||||
try (PackWriter pw = newPackWriter()) { | try (PackWriter pw = newPackWriter()) { | ||||
pw.setTagTargets(tagTargets); | pw.setTagTargets(tagTargets); | ||||
pw.preparePack(pm, allHeads, none()); | |||||
pw.preparePack(pm, allHeads, PackWriter.NONE); | |||||
if (0 < pw.getObjectCount()) | if (0 < pw.getObjectCount()) | ||||
writePack(GC, pw, pm); | writePack(GC, pw, pm); | ||||
} | } | ||||
try (PackWriter pw = newPackWriter()) { | try (PackWriter pw = newPackWriter()) { | ||||
for (ObjectIdSet packedObjs : newPackObj) | for (ObjectIdSet packedObjs : newPackObj) | ||||
pw.excludeObjects(packedObjs); | pw.excludeObjects(packedObjs); | ||||
pw.preparePack(pm, txnHeads, none()); | |||||
pw.preparePack(pm, txnHeads, PackWriter.NONE); | |||||
if (0 < pw.getObjectCount()) | if (0 < pw.getObjectCount()) | ||||
writePack(GC_TXN, pw, pm); | writePack(GC_TXN, pw, pm); | ||||
} | } | ||||
} | } | ||||
private static Set<ObjectId> none() { | |||||
return Collections.<ObjectId> emptySet(); | |||||
} | |||||
private void packGarbage(ProgressMonitor pm) throws IOException { | private void packGarbage(ProgressMonitor pm) throws IOException { | ||||
// TODO(sop) This is ugly. The garbage pack needs to be deleted. | // TODO(sop) This is ugly. The garbage pack needs to be deleted. | ||||
PackConfig cfg = new PackConfig(packConfig); | PackConfig cfg = new PackConfig(packConfig); |
import java.util.Set; | import java.util.Set; | ||||
import java.util.TreeMap; | import java.util.TreeMap; | ||||
import org.eclipse.jgit.annotations.NonNull; | |||||
import org.eclipse.jgit.dircache.DirCacheIterator; | import org.eclipse.jgit.dircache.DirCacheIterator; | ||||
import org.eclipse.jgit.errors.CorruptObjectException; | import org.eclipse.jgit.errors.CorruptObjectException; | ||||
import org.eclipse.jgit.errors.IncorrectObjectTypeException; | import org.eclipse.jgit.errors.IncorrectObjectTypeException; | ||||
ret.add(rest); | ret.add(rest); | ||||
} | } | ||||
if (!txnHeads.isEmpty()) { | if (!txnHeads.isEmpty()) { | ||||
PackFile txn = writePack(txnHeads, null, null, excluded); | |||||
PackFile txn = writePack(txnHeads, PackWriter.NONE, null, excluded); | |||||
if (txn != null) | if (txn != null) | ||||
ret.add(txn); | ret.add(txn); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
private PackFile writePack(Set<? extends ObjectId> want, | |||||
Set<? extends ObjectId> have, Set<ObjectId> tagTargets, | |||||
private PackFile writePack(@NonNull Set<? extends ObjectId> want, | |||||
@NonNull Set<? extends ObjectId> have, Set<ObjectId> tagTargets, | |||||
List<ObjectIdSet> excludeObjects) throws IOException { | List<ObjectIdSet> excludeObjects) throws IOException { | ||||
File tmpPack = null; | File tmpPack = null; | ||||
Map<PackExt, File> tmpExts = new TreeMap<PackExt, File>( | Map<PackExt, File> tmpExts = new TreeMap<PackExt, File>( |
import java.util.zip.Deflater; | import java.util.zip.Deflater; | ||||
import java.util.zip.DeflaterOutputStream; | import java.util.zip.DeflaterOutputStream; | ||||
import org.eclipse.jgit.annotations.NonNull; | |||||
import org.eclipse.jgit.errors.CorruptObjectException; | import org.eclipse.jgit.errors.CorruptObjectException; | ||||
import org.eclipse.jgit.errors.IncorrectObjectTypeException; | import org.eclipse.jgit.errors.IncorrectObjectTypeException; | ||||
import org.eclipse.jgit.errors.LargeObjectException; | import org.eclipse.jgit.errors.LargeObjectException; | ||||
public class PackWriter implements AutoCloseable { | public class PackWriter implements AutoCloseable { | ||||
private static final int PACK_VERSION_GENERATED = 2; | private static final int PACK_VERSION_GENERATED = 2; | ||||
/** Empty set of objects for {@code preparePack()}. */ | |||||
public static Set<ObjectId> NONE = Collections.emptySet(); | |||||
private static final Map<WeakReference<PackWriter>, Boolean> instances = | private static final Map<WeakReference<PackWriter>, Boolean> instances = | ||||
new ConcurrentHashMap<WeakReference<PackWriter>, Boolean>(); | new ConcurrentHashMap<WeakReference<PackWriter>, Boolean>(); | ||||
* @throws IOException | * @throws IOException | ||||
* when some I/O problem occur during reading objects. | * when some I/O problem occur during reading objects. | ||||
*/ | */ | ||||
public void preparePack(final Iterator<RevObject> objectsSource) | |||||
public void preparePack(@NonNull Iterator<RevObject> objectsSource) | |||||
throws IOException { | throws IOException { | ||||
while (objectsSource.hasNext()) { | while (objectsSource.hasNext()) { | ||||
addObject(objectsSource.next()); | addObject(objectsSource.next()); | ||||
* progress during object enumeration. | * progress during object enumeration. | ||||
* @param want | * @param want | ||||
* collection of objects to be marked as interesting (start | * collection of objects to be marked as interesting (start | ||||
* points of graph traversal). | |||||
* points of graph traversal). Must not be {@code null}. | |||||
* @param have | * @param have | ||||
* collection of objects to be marked as uninteresting (end | * collection of objects to be marked as uninteresting (end | ||||
* points of graph traversal). | |||||
* points of graph traversal). Pass {@link #NONE} if all objects | |||||
* reachable from {@code want} are desired, such as when serving | |||||
* a clone. | |||||
* @throws IOException | * @throws IOException | ||||
* when some I/O problem occur during reading objects. | * when some I/O problem occur during reading objects. | ||||
*/ | */ | ||||
public void preparePack(ProgressMonitor countingMonitor, | public void preparePack(ProgressMonitor countingMonitor, | ||||
Set<? extends ObjectId> want, | |||||
Set<? extends ObjectId> have) throws IOException { | |||||
@NonNull Set<? extends ObjectId> want, | |||||
@NonNull Set<? extends ObjectId> have) throws IOException { | |||||
ObjectWalk ow; | ObjectWalk ow; | ||||
if (shallowPack) | if (shallowPack) | ||||
ow = new DepthWalk.ObjectWalk(reader, depth); | ow = new DepthWalk.ObjectWalk(reader, depth); | ||||
* ObjectWalk to perform enumeration. | * ObjectWalk to perform enumeration. | ||||
* @param interestingObjects | * @param interestingObjects | ||||
* collection of objects to be marked as interesting (start | * collection of objects to be marked as interesting (start | ||||
* points of graph traversal). | |||||
* points of graph traversal). Must not be {@code null}. | |||||
* @param uninterestingObjects | * @param uninterestingObjects | ||||
* collection of objects to be marked as uninteresting (end | * collection of objects to be marked as uninteresting (end | ||||
* points of graph traversal). | |||||
* points of graph traversal). Pass {@link #NONE} if all objects | |||||
* reachable from {@code want} are desired, such as when serving | |||||
* a clone. | |||||
* @throws IOException | * @throws IOException | ||||
* when some I/O problem occur during reading objects. | * when some I/O problem occur during reading objects. | ||||
*/ | */ | ||||
public void preparePack(ProgressMonitor countingMonitor, | public void preparePack(ProgressMonitor countingMonitor, | ||||
ObjectWalk walk, | |||||
final Set<? extends ObjectId> interestingObjects, | |||||
final Set<? extends ObjectId> uninterestingObjects) | |||||
@NonNull ObjectWalk walk, | |||||
@NonNull Set<? extends ObjectId> interestingObjects, | |||||
@NonNull Set<? extends ObjectId> uninterestingObjects) | |||||
throws IOException { | throws IOException { | ||||
if (countingMonitor == null) | if (countingMonitor == null) | ||||
countingMonitor = NullProgressMonitor.INSTANCE; | countingMonitor = NullProgressMonitor.INSTANCE; | ||||
out.write(packcsum); | out.write(packcsum); | ||||
} | } | ||||
private void findObjectsToPack(final ProgressMonitor countingMonitor, | |||||
final ObjectWalk walker, final Set<? extends ObjectId> want, | |||||
Set<? extends ObjectId> have) | |||||
throws MissingObjectException, IOException, | |||||
IncorrectObjectTypeException { | |||||
private void findObjectsToPack(@NonNull ProgressMonitor countingMonitor, | |||||
@NonNull ObjectWalk walker, @NonNull Set<? extends ObjectId> want, | |||||
@NonNull Set<? extends ObjectId> have) throws IOException { | |||||
final long countingStart = System.currentTimeMillis(); | final long countingStart = System.currentTimeMillis(); | ||||
beginPhase(PackingPhase.COUNTING, countingMonitor, ProgressMonitor.UNKNOWN); | beginPhase(PackingPhase.COUNTING, countingMonitor, ProgressMonitor.UNKNOWN); | ||||
if (have == null) | |||||
have = Collections.emptySet(); | |||||
stats.interestingObjects = Collections.unmodifiableSet(new HashSet<ObjectId>(want)); | stats.interestingObjects = Collections.unmodifiableSet(new HashSet<ObjectId>(want)); | ||||
stats.uninterestingObjects = Collections.unmodifiableSet(new HashSet<ObjectId>(have)); | stats.uninterestingObjects = Collections.unmodifiableSet(new HashSet<ObjectId>(have)); | ||||