123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878 |
- /*
- * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com> and others
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Distribution License v. 1.0 which is available at
- * https://www.eclipse.org/org/documents/edl-v10.php.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
-
- package org.eclipse.jgit.internal.storage.file;
-
- import static org.eclipse.jgit.internal.storage.pack.PackWriter.NONE;
- import static org.eclipse.jgit.lib.Constants.INFO_ALTERNATES;
- import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
- import static org.junit.Assert.assertEquals;
- import static org.junit.Assert.assertFalse;
- import static org.junit.Assert.assertNotNull;
- import static org.junit.Assert.assertTrue;
- import static org.junit.Assert.fail;
-
- import java.io.ByteArrayInputStream;
- import java.io.ByteArrayOutputStream;
- import java.io.File;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.text.ParseException;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.Collections;
- import java.util.HashSet;
- import java.util.List;
- import java.util.Set;
-
- import org.eclipse.jgit.errors.MissingObjectException;
- import org.eclipse.jgit.internal.storage.file.PackIndex.MutableEntry;
- import org.eclipse.jgit.internal.storage.pack.PackExt;
- import org.eclipse.jgit.internal.storage.pack.PackWriter;
- import org.eclipse.jgit.junit.JGitTestUtil;
- import org.eclipse.jgit.junit.TestRepository;
- import org.eclipse.jgit.junit.TestRepository.BranchBuilder;
- import org.eclipse.jgit.lib.NullProgressMonitor;
- import org.eclipse.jgit.lib.ObjectId;
- import org.eclipse.jgit.lib.ObjectIdSet;
- import org.eclipse.jgit.lib.ObjectInserter;
- import org.eclipse.jgit.lib.Repository;
- import org.eclipse.jgit.lib.Sets;
- import org.eclipse.jgit.revwalk.DepthWalk;
- import org.eclipse.jgit.revwalk.ObjectWalk;
- import org.eclipse.jgit.revwalk.RevBlob;
- import org.eclipse.jgit.revwalk.RevCommit;
- import org.eclipse.jgit.revwalk.RevObject;
- import org.eclipse.jgit.revwalk.RevWalk;
- import org.eclipse.jgit.storage.pack.PackConfig;
- import org.eclipse.jgit.storage.pack.PackStatistics;
- import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
- import org.eclipse.jgit.transport.PackParser;
- import org.junit.After;
- import org.junit.Before;
- import org.junit.Test;
-
- public class PackWriterTest extends SampleDataRepositoryTestCase {
-
- private static final List<RevObject> EMPTY_LIST_REVS = Collections
- .<RevObject> emptyList();
-
- private static final Set<ObjectIdSet> EMPTY_ID_SET = Collections
- .<ObjectIdSet> emptySet();
-
- private PackConfig config;
-
- private PackWriter writer;
-
- private ByteArrayOutputStream os;
-
- private Pack pack;
-
- private ObjectInserter inserter;
-
- private FileRepository dst;
-
- private RevBlob contentA;
-
- private RevBlob contentB;
-
- private RevBlob contentC;
-
- private RevBlob contentD;
-
- private RevBlob contentE;
-
- private RevCommit c1;
-
- private RevCommit c2;
-
- private RevCommit c3;
-
- private RevCommit c4;
-
- private RevCommit c5;
-
- @Override
- @Before
- public void setUp() throws Exception {
- super.setUp();
- os = new ByteArrayOutputStream();
- config = new PackConfig(db);
-
- dst = createBareRepository();
- File alt = new File(dst.getObjectDatabase().getDirectory(), INFO_ALTERNATES);
- alt.getParentFile().mkdirs();
- write(alt, db.getObjectDatabase().getDirectory().getAbsolutePath() + "\n");
- }
-
- @Override
- @After
- public void tearDown() throws Exception {
- if (writer != null) {
- writer.close();
- writer = null;
- }
- if (inserter != null) {
- inserter.close();
- inserter = null;
- }
- super.tearDown();
- }
-
- /**
- * Test constructor for exceptions, default settings, initialization.
- *
- * @throws IOException
- */
- @Test
- public void testContructor() throws IOException {
- writer = new PackWriter(config, db.newObjectReader());
- assertFalse(writer.isDeltaBaseAsOffset());
- assertTrue(config.isReuseDeltas());
- assertTrue(config.isReuseObjects());
- assertEquals(0, writer.getObjectCount());
- }
-
- /**
- * Change default settings and verify them.
- */
- @Test
- public void testModifySettings() {
- config.setReuseDeltas(false);
- config.setReuseObjects(false);
- config.setDeltaBaseAsOffset(false);
- assertFalse(config.isReuseDeltas());
- assertFalse(config.isReuseObjects());
- assertFalse(config.isDeltaBaseAsOffset());
-
- writer = new PackWriter(config, db.newObjectReader());
- writer.setDeltaBaseAsOffset(true);
- assertTrue(writer.isDeltaBaseAsOffset());
- assertFalse(config.isDeltaBaseAsOffset());
- }
-
- /**
- * Write empty pack by providing empty sets of interesting/uninteresting
- * objects and check for correct format.
- *
- * @throws IOException
- */
- @Test
- public void testWriteEmptyPack1() throws IOException {
- createVerifyOpenPack(NONE, NONE, false, false);
-
- assertEquals(0, writer.getObjectCount());
- assertEquals(0, pack.getObjectCount());
- assertEquals("da39a3ee5e6b4b0d3255bfef95601890afd80709", writer
- .computeName().name());
- }
-
- /**
- * Write empty pack by providing empty iterator of objects to write and
- * check for correct format.
- *
- * @throws IOException
- */
- @Test
- public void testWriteEmptyPack2() throws IOException {
- createVerifyOpenPack(EMPTY_LIST_REVS);
-
- assertEquals(0, writer.getObjectCount());
- assertEquals(0, pack.getObjectCount());
- }
-
- /**
- * Try to pass non-existing object as uninteresting, with non-ignoring
- * setting.
- *
- * @throws IOException
- */
- @Test
- public void testNotIgnoreNonExistingObjects() throws IOException {
- final ObjectId nonExisting = ObjectId
- .fromString("0000000000000000000000000000000000000001");
- try {
- createVerifyOpenPack(NONE, haves(nonExisting), false, false);
- fail("Should have thrown MissingObjectException");
- } catch (MissingObjectException x) {
- // expected
- }
- }
-
- /**
- * Try to pass non-existing object as uninteresting, with ignoring setting.
- *
- * @throws IOException
- */
- @Test
- public void testIgnoreNonExistingObjects() throws IOException {
- final ObjectId nonExisting = ObjectId
- .fromString("0000000000000000000000000000000000000001");
- createVerifyOpenPack(NONE, haves(nonExisting), false, true);
- // shouldn't throw anything
- }
-
- /**
- * Try to pass non-existing object as uninteresting, with ignoring setting.
- * Use a repo with bitmap indexes because then PackWriter will use
- * PackWriterBitmapWalker which had problems with this situation.
- *
- * @throws IOException
- * @throws ParseException
- */
- @Test
- public void testIgnoreNonExistingObjectsWithBitmaps() throws IOException,
- ParseException {
- final ObjectId nonExisting = ObjectId
- .fromString("0000000000000000000000000000000000000001");
- new GC(db).gc();
- createVerifyOpenPack(NONE, haves(nonExisting), false, true, true);
- // shouldn't throw anything
- }
-
- /**
- * Create pack basing on only interesting objects, then precisely verify
- * content. No delta reuse here.
- *
- * @throws IOException
- */
- @Test
- public void testWritePack1() throws IOException {
- config.setReuseDeltas(false);
- writeVerifyPack1();
- }
-
- /**
- * Test writing pack without object reuse. Pack content/preparation as in
- * {@link #testWritePack1()}.
- *
- * @throws IOException
- */
- @Test
- public void testWritePack1NoObjectReuse() throws IOException {
- config.setReuseDeltas(false);
- config.setReuseObjects(false);
- writeVerifyPack1();
- }
-
- /**
- * Create pack basing on both interesting and uninteresting objects, then
- * precisely verify content. No delta reuse here.
- *
- * @throws IOException
- */
- @Test
- public void testWritePack2() throws IOException {
- writeVerifyPack2(false);
- }
-
- /**
- * Test pack writing with deltas reuse, delta-base first rule. Pack
- * content/preparation as in {@link #testWritePack2()}.
- *
- * @throws IOException
- */
- @Test
- public void testWritePack2DeltasReuseRefs() throws IOException {
- writeVerifyPack2(true);
- }
-
- /**
- * Test pack writing with delta reuse. Delta bases referred as offsets. Pack
- * configuration as in {@link #testWritePack2DeltasReuseRefs()}.
- *
- * @throws IOException
- */
- @Test
- public void testWritePack2DeltasReuseOffsets() throws IOException {
- config.setDeltaBaseAsOffset(true);
- writeVerifyPack2(true);
- }
-
- /**
- * Test pack writing with delta reuse. Raw-data copy (reuse) is made on a
- * pack with CRC32 index. Pack configuration as in
- * {@link #testWritePack2DeltasReuseRefs()}.
- *
- * @throws IOException
- */
- @Test
- public void testWritePack2DeltasCRC32Copy() throws IOException {
- final File packDir = db.getObjectDatabase().getPackDirectory();
- final PackFile crc32Pack = new PackFile(packDir,
- "pack-34be9032ac282b11fa9babdc2b2a93ca996c9c2f.pack");
- final PackFile crc32Idx = new PackFile(packDir,
- "pack-34be9032ac282b11fa9babdc2b2a93ca996c9c2f.idx");
- copyFile(JGitTestUtil.getTestResourceFile(
- "pack-34be9032ac282b11fa9babdc2b2a93ca996c9c2f.idxV2"),
- crc32Idx);
- db.openPack(crc32Pack);
-
- writeVerifyPack2(true);
- }
-
- /**
- * Create pack basing on fixed objects list, then precisely verify content.
- * No delta reuse here.
- *
- * @throws IOException
- * @throws MissingObjectException
- *
- */
- @Test
- public void testWritePack3() throws MissingObjectException, IOException {
- config.setReuseDeltas(false);
- final ObjectId forcedOrder[] = new ObjectId[] {
- ObjectId.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"),
- ObjectId.fromString("c59759f143fb1fe21c197981df75a7ee00290799"),
- ObjectId.fromString("aabf2ffaec9b497f0950352b3e582d73035c2035"),
- ObjectId.fromString("902d5476fa249b7abc9d84c611577a81381f0327"),
- ObjectId.fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3") ,
- ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259") };
- try (RevWalk parser = new RevWalk(db)) {
- final RevObject forcedOrderRevs[] = new RevObject[forcedOrder.length];
- for (int i = 0; i < forcedOrder.length; i++)
- forcedOrderRevs[i] = parser.parseAny(forcedOrder[i]);
-
- createVerifyOpenPack(Arrays.asList(forcedOrderRevs));
- }
-
- assertEquals(forcedOrder.length, writer.getObjectCount());
- verifyObjectsOrder(forcedOrder);
- assertEquals("ed3f96b8327c7c66b0f8f70056129f0769323d86", writer
- .computeName().name());
- }
-
- /**
- * Another pack creation: basing on both interesting and uninteresting
- * objects. No delta reuse possible here, as this is a specific case when we
- * write only 1 commit, associated with 1 tree, 1 blob.
- *
- * @throws IOException
- */
- @Test
- public void testWritePack4() throws IOException {
- writeVerifyPack4(false);
- }
-
- /**
- * Test thin pack writing: 1 blob delta base is on objects edge. Pack
- * configuration as in {@link #testWritePack4()}.
- *
- * @throws IOException
- */
- @Test
- public void testWritePack4ThinPack() throws IOException {
- writeVerifyPack4(true);
- }
-
- /**
- * Compare sizes of packs created using {@link #testWritePack2()} and
- * {@link #testWritePack2DeltasReuseRefs()}. The pack using deltas should
- * be smaller.
- *
- * @throws Exception
- */
- @Test
- public void testWritePack2SizeDeltasVsNoDeltas() throws Exception {
- config.setReuseDeltas(false);
- config.setDeltaCompress(false);
- testWritePack2();
- final long sizePack2NoDeltas = os.size();
- tearDown();
- setUp();
- testWritePack2DeltasReuseRefs();
- final long sizePack2DeltasRefs = os.size();
-
- assertTrue(sizePack2NoDeltas > sizePack2DeltasRefs);
- }
-
- /**
- * Compare sizes of packs created using
- * {@link #testWritePack2DeltasReuseRefs()} and
- * {@link #testWritePack2DeltasReuseOffsets()}. The pack with delta bases
- * written as offsets should be smaller.
- *
- * @throws Exception
- */
- @Test
- public void testWritePack2SizeOffsetsVsRefs() throws Exception {
- testWritePack2DeltasReuseRefs();
- final long sizePack2DeltasRefs = os.size();
- tearDown();
- setUp();
- testWritePack2DeltasReuseOffsets();
- final long sizePack2DeltasOffsets = os.size();
-
- assertTrue(sizePack2DeltasRefs > sizePack2DeltasOffsets);
- }
-
- /**
- * Compare sizes of packs created using {@link #testWritePack4()} and
- * {@link #testWritePack4ThinPack()}. Obviously, the thin pack should be
- * smaller.
- *
- * @throws Exception
- */
- @Test
- public void testWritePack4SizeThinVsNoThin() throws Exception {
- testWritePack4();
- final long sizePack4 = os.size();
- tearDown();
- setUp();
- testWritePack4ThinPack();
- final long sizePack4Thin = os.size();
-
- assertTrue(sizePack4 > sizePack4Thin);
- }
-
- @Test
- public void testDeltaStatistics() throws Exception {
- config.setDeltaCompress(true);
- // TestRepository will close repo
- FileRepository repo = createBareRepository();
- ArrayList<RevObject> blobs = new ArrayList<>();
- try (TestRepository<FileRepository> testRepo = new TestRepository<>(
- repo)) {
- blobs.add(testRepo.blob(genDeltableData(1000)));
- blobs.add(testRepo.blob(genDeltableData(1005)));
- try (PackWriter pw = new PackWriter(repo)) {
- NullProgressMonitor m = NullProgressMonitor.INSTANCE;
- pw.preparePack(blobs.iterator());
- pw.writePack(m, m, os);
- PackStatistics stats = pw.getStatistics();
- assertEquals(1, stats.getTotalDeltas());
- assertTrue("Delta bytes not set.",
- stats.byObjectType(OBJ_BLOB).getDeltaBytes() > 0);
- }
- }
- }
-
- // Generate consistent junk data for building files that delta well
- private String genDeltableData(int length) {
- assertTrue("Generated data must have a length > 0", length > 0);
- char[] data = {'a', 'b', 'c', '\n'};
- StringBuilder builder = new StringBuilder(length);
- for (int i = 0; i < length; i++) {
- builder.append(data[i % 4]);
- }
- return builder.toString();
- }
-
-
- @Test
- public void testWriteIndex() throws Exception {
- config.setIndexVersion(2);
- writeVerifyPack4(false);
-
- PackFile packFile = pack.getPackFile();
- PackFile indexFile = packFile.create(PackExt.INDEX);
-
- // Validate that IndexPack came up with the right CRC32 value.
- final PackIndex idx1 = PackIndex.open(indexFile);
- assertTrue(idx1 instanceof PackIndexV2);
- assertEquals(0x4743F1E4L, idx1.findCRC32(ObjectId
- .fromString("82c6b885ff600be425b4ea96dee75dca255b69e7")));
-
- // Validate that an index written by PackWriter is the same.
- final File idx2File = new File(indexFile.getAbsolutePath() + ".2");
- try (FileOutputStream is = new FileOutputStream(idx2File)) {
- writer.writeIndex(is);
- }
- final PackIndex idx2 = PackIndex.open(idx2File);
- assertTrue(idx2 instanceof PackIndexV2);
- assertEquals(idx1.getObjectCount(), idx2.getObjectCount());
- assertEquals(idx1.getOffset64Count(), idx2.getOffset64Count());
-
- for (int i = 0; i < idx1.getObjectCount(); i++) {
- final ObjectId id = idx1.getObjectId(i);
- assertEquals(id, idx2.getObjectId(i));
- assertEquals(idx1.findOffset(id), idx2.findOffset(id));
- assertEquals(idx1.findCRC32(id), idx2.findCRC32(id));
- }
- }
-
- @Test
- public void testExclude() throws Exception {
- // TestRepository closes repo
- FileRepository repo = createBareRepository();
-
- try (TestRepository<FileRepository> testRepo = new TestRepository<>(
- repo)) {
- BranchBuilder bb = testRepo.branch("refs/heads/master");
- contentA = testRepo.blob("A");
- c1 = bb.commit().add("f", contentA).create();
- testRepo.getRevWalk().parseHeaders(c1);
- PackIndex pf1 = writePack(repo, wants(c1), EMPTY_ID_SET);
- assertContent(pf1, Arrays.asList(c1.getId(), c1.getTree().getId(),
- contentA.getId()));
- contentB = testRepo.blob("B");
- c2 = bb.commit().add("f", contentB).create();
- testRepo.getRevWalk().parseHeaders(c2);
- PackIndex pf2 = writePack(repo, wants(c2),
- Sets.of((ObjectIdSet) pf1));
- assertContent(pf2, Arrays.asList(c2.getId(), c2.getTree().getId(),
- contentB.getId()));
- }
- }
-
- private static void assertContent(PackIndex pi, List<ObjectId> expected) {
- assertEquals("Pack index has wrong size.", expected.size(),
- pi.getObjectCount());
- for (int i = 0; i < pi.getObjectCount(); i++)
- assertTrue(
- "Pack index didn't contain the expected id "
- + pi.getObjectId(i),
- expected.contains(pi.getObjectId(i)));
- }
-
- @Test
- public void testShallowIsMinimalDepth1() throws Exception {
- try (FileRepository repo = setupRepoForShallowFetch()) {
- PackIndex idx = writeShallowPack(repo, 1, wants(c2), NONE, NONE);
- assertContent(idx, Arrays.asList(c2.getId(), c2.getTree().getId(),
- contentA.getId(), contentB.getId()));
-
- // Client already has blobs A and B, verify those are not packed.
- idx = writeShallowPack(repo, 1, wants(c5), haves(c2), shallows(c2));
- assertContent(idx, Arrays.asList(c5.getId(), c5.getTree().getId(),
- contentC.getId(), contentD.getId(), contentE.getId()));
- }
- }
-
- @Test
- public void testShallowIsMinimalDepth2() throws Exception {
- try (FileRepository repo = setupRepoForShallowFetch()) {
- PackIndex idx = writeShallowPack(repo, 2, wants(c2), NONE, NONE);
- assertContent(idx,
- Arrays.asList(c1.getId(), c2.getId(), c1.getTree().getId(),
- c2.getTree().getId(), contentA.getId(),
- contentB.getId()));
-
- // Client already has blobs A and B, verify those are not packed.
- idx = writeShallowPack(repo, 2, wants(c5), haves(c1, c2),
- shallows(c1));
- assertContent(idx,
- Arrays.asList(c4.getId(), c5.getId(), c4.getTree().getId(),
- c5.getTree().getId(), contentC.getId(),
- contentD.getId(), contentE.getId()));
- }
- }
-
- @Test
- public void testShallowFetchShallowParentDepth1() throws Exception {
- try (FileRepository repo = setupRepoForShallowFetch()) {
- PackIndex idx = writeShallowPack(repo, 1, wants(c5), NONE, NONE);
- assertContent(idx, Arrays.asList(c5.getId(), c5.getTree().getId(),
- contentA.getId(), contentB.getId(), contentC.getId(),
- contentD.getId(), contentE.getId()));
-
- idx = writeShallowPack(repo, 1, wants(c4), haves(c5), shallows(c5));
- assertContent(idx, Arrays.asList(c4.getId(), c4.getTree().getId()));
- }
- }
-
- @Test
- public void testShallowFetchShallowParentDepth2() throws Exception {
- try (FileRepository repo = setupRepoForShallowFetch()) {
- PackIndex idx = writeShallowPack(repo, 2, wants(c5), NONE, NONE);
- assertContent(idx,
- Arrays.asList(c4.getId(), c5.getId(), c4.getTree().getId(),
- c5.getTree().getId(), contentA.getId(),
- contentB.getId(), contentC.getId(),
- contentD.getId(), contentE.getId()));
-
- idx = writeShallowPack(repo, 2, wants(c3), haves(c4, c5),
- shallows(c4));
- assertContent(idx, Arrays.asList(c2.getId(), c3.getId(),
- c2.getTree().getId(), c3.getTree().getId()));
- }
- }
-
- @Test
- public void testShallowFetchShallowAncestorDepth1() throws Exception {
- try (FileRepository repo = setupRepoForShallowFetch()) {
- PackIndex idx = writeShallowPack(repo, 1, wants(c5), NONE, NONE);
- assertContent(idx, Arrays.asList(c5.getId(), c5.getTree().getId(),
- contentA.getId(), contentB.getId(), contentC.getId(),
- contentD.getId(), contentE.getId()));
-
- idx = writeShallowPack(repo, 1, wants(c3), haves(c5), shallows(c5));
- assertContent(idx, Arrays.asList(c3.getId(), c3.getTree().getId()));
- }
- }
-
- @Test
- public void testShallowFetchShallowAncestorDepth2() throws Exception {
- try (FileRepository repo = setupRepoForShallowFetch()) {
- PackIndex idx = writeShallowPack(repo, 2, wants(c5), NONE, NONE);
- assertContent(idx,
- Arrays.asList(c4.getId(), c5.getId(), c4.getTree().getId(),
- c5.getTree().getId(), contentA.getId(),
- contentB.getId(), contentC.getId(),
- contentD.getId(), contentE.getId()));
-
- idx = writeShallowPack(repo, 2, wants(c2), haves(c4, c5),
- shallows(c4));
- assertContent(idx, Arrays.asList(c1.getId(), c2.getId(),
- c1.getTree().getId(), c2.getTree().getId()));
- }
- }
-
- private FileRepository setupRepoForShallowFetch() throws Exception {
- FileRepository repo = createBareRepository();
- // TestRepository will close the repo, but we need to return an open
- // one!
- repo.incrementOpen();
- try (TestRepository<Repository> r = new TestRepository<>(repo)) {
- BranchBuilder bb = r.branch("refs/heads/master");
- contentA = r.blob("A");
- contentB = r.blob("B");
- contentC = r.blob("C");
- contentD = r.blob("D");
- contentE = r.blob("E");
- c1 = bb.commit().add("a", contentA).create();
- c2 = bb.commit().add("b", contentB).create();
- c3 = bb.commit().add("c", contentC).create();
- c4 = bb.commit().add("d", contentD).create();
- c5 = bb.commit().add("e", contentE).create();
- r.getRevWalk().parseHeaders(c5); // fully initialize the tip RevCommit
- return repo;
- }
- }
-
- private static PackIndex writePack(FileRepository repo,
- Set<? extends ObjectId> want, Set<ObjectIdSet> excludeObjects)
- throws IOException {
- try (RevWalk walk = new RevWalk(repo)) {
- return writePack(repo, walk, 0, want, NONE, excludeObjects);
- }
- }
-
- private static PackIndex writeShallowPack(FileRepository repo, int depth,
- Set<? extends ObjectId> want, Set<? extends ObjectId> have,
- Set<? extends ObjectId> shallow) throws IOException {
- // During negotiation, UploadPack would have set up a DepthWalk and
- // marked the client's "shallow" commits. Emulate that here.
- try (DepthWalk.RevWalk walk = new DepthWalk.RevWalk(repo, depth - 1)) {
- walk.assumeShallow(shallow);
- return writePack(repo, walk, depth, want, have, EMPTY_ID_SET);
- }
- }
-
- private static PackIndex writePack(FileRepository repo, RevWalk walk,
- int depth, Set<? extends ObjectId> want,
- Set<? extends ObjectId> have, Set<ObjectIdSet> excludeObjects)
- throws IOException {
- try (PackWriter pw = new PackWriter(repo)) {
- pw.setDeltaBaseAsOffset(true);
- pw.setReuseDeltaCommits(false);
- for (ObjectIdSet idx : excludeObjects) {
- pw.excludeObjects(idx);
- }
- if (depth > 0) {
- pw.setShallowPack(depth, null);
- }
- // ow doesn't need to be closed; caller closes walk.
- ObjectWalk ow = walk.toObjectWalkWithSameObjects();
-
- pw.preparePack(NullProgressMonitor.INSTANCE, ow, want, have, NONE);
- File packdir = repo.getObjectDatabase().getPackDirectory();
- PackFile packFile = new PackFile(packdir, pw.computeName(),
- PackExt.PACK);
- try (FileOutputStream packOS = new FileOutputStream(packFile)) {
- pw.writePack(NullProgressMonitor.INSTANCE,
- NullProgressMonitor.INSTANCE, packOS);
- }
- PackFile idxFile = packFile.create(PackExt.INDEX);
- try (FileOutputStream idxOS = new FileOutputStream(idxFile)) {
- pw.writeIndex(idxOS);
- }
- return PackIndex.open(idxFile);
- }
- }
-
- // TODO: testWritePackDeltasCycle()
- // TODO: testWritePackDeltasDepth()
-
- private void writeVerifyPack1() throws IOException {
- final HashSet<ObjectId> interestings = new HashSet<>();
- interestings.add(ObjectId
- .fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"));
- createVerifyOpenPack(interestings, NONE, false, false);
-
- final ObjectId expectedOrder[] = new ObjectId[] {
- ObjectId.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"),
- ObjectId.fromString("c59759f143fb1fe21c197981df75a7ee00290799"),
- ObjectId.fromString("540a36d136cf413e4b064c2b0e0a4db60f77feab"),
- ObjectId.fromString("aabf2ffaec9b497f0950352b3e582d73035c2035"),
- ObjectId.fromString("902d5476fa249b7abc9d84c611577a81381f0327"),
- ObjectId.fromString("4b825dc642cb6eb9a060e54bf8d69288fbee4904"),
- ObjectId.fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3"),
- ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259") };
-
- assertEquals(expectedOrder.length, writer.getObjectCount());
- verifyObjectsOrder(expectedOrder);
- assertEquals("34be9032ac282b11fa9babdc2b2a93ca996c9c2f", writer
- .computeName().name());
- }
-
- private void writeVerifyPack2(boolean deltaReuse) throws IOException {
- config.setReuseDeltas(deltaReuse);
- final HashSet<ObjectId> interestings = new HashSet<>();
- interestings.add(ObjectId
- .fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"));
- final HashSet<ObjectId> uninterestings = new HashSet<>();
- uninterestings.add(ObjectId
- .fromString("540a36d136cf413e4b064c2b0e0a4db60f77feab"));
- createVerifyOpenPack(interestings, uninterestings, false, false);
-
- final ObjectId expectedOrder[] = new ObjectId[] {
- ObjectId.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"),
- ObjectId.fromString("c59759f143fb1fe21c197981df75a7ee00290799"),
- ObjectId.fromString("aabf2ffaec9b497f0950352b3e582d73035c2035"),
- ObjectId.fromString("902d5476fa249b7abc9d84c611577a81381f0327"),
- ObjectId.fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3") ,
- ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259") };
- if (!config.isReuseDeltas() && !config.isDeltaCompress()) {
- // If no deltas are in the file the final two entries swap places.
- swap(expectedOrder, 4, 5);
- }
- assertEquals(expectedOrder.length, writer.getObjectCount());
- verifyObjectsOrder(expectedOrder);
- assertEquals("ed3f96b8327c7c66b0f8f70056129f0769323d86", writer
- .computeName().name());
- }
-
- private static void swap(ObjectId[] arr, int a, int b) {
- ObjectId tmp = arr[a];
- arr[a] = arr[b];
- arr[b] = tmp;
- }
-
- private void writeVerifyPack4(final boolean thin) throws IOException {
- final HashSet<ObjectId> interestings = new HashSet<>();
- interestings.add(ObjectId
- .fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"));
- final HashSet<ObjectId> uninterestings = new HashSet<>();
- uninterestings.add(ObjectId
- .fromString("c59759f143fb1fe21c197981df75a7ee00290799"));
- createVerifyOpenPack(interestings, uninterestings, thin, false);
-
- final ObjectId writtenObjects[] = new ObjectId[] {
- ObjectId.fromString("82c6b885ff600be425b4ea96dee75dca255b69e7"),
- ObjectId.fromString("aabf2ffaec9b497f0950352b3e582d73035c2035"),
- ObjectId.fromString("5b6e7c66c276e7610d4a73c70ec1a1f7c1003259") };
- assertEquals(writtenObjects.length, writer.getObjectCount());
- ObjectId expectedObjects[];
- if (thin) {
- expectedObjects = new ObjectId[4];
- System.arraycopy(writtenObjects, 0, expectedObjects, 0,
- writtenObjects.length);
- expectedObjects[3] = ObjectId
- .fromString("6ff87c4664981e4397625791c8ea3bbb5f2279a3");
-
- } else {
- expectedObjects = writtenObjects;
- }
- verifyObjectsOrder(expectedObjects);
- assertEquals("cded4b74176b4456afa456768b2b5aafb41c44fc", writer
- .computeName().name());
- }
-
- private void createVerifyOpenPack(final Set<ObjectId> interestings,
- final Set<ObjectId> uninterestings, final boolean thin,
- final boolean ignoreMissingUninteresting)
- throws MissingObjectException, IOException {
- createVerifyOpenPack(interestings, uninterestings, thin,
- ignoreMissingUninteresting, false);
- }
-
- private void createVerifyOpenPack(final Set<ObjectId> interestings,
- final Set<ObjectId> uninterestings, final boolean thin,
- final boolean ignoreMissingUninteresting, boolean useBitmaps)
- throws MissingObjectException, IOException {
- NullProgressMonitor m = NullProgressMonitor.INSTANCE;
- writer = new PackWriter(config, db.newObjectReader());
- writer.setUseBitmaps(useBitmaps);
- writer.setThin(thin);
- writer.setIgnoreMissingUninteresting(ignoreMissingUninteresting);
- writer.preparePack(m, interestings, uninterestings);
- writer.writePack(m, m, os);
- writer.close();
- verifyOpenPack(thin);
- }
-
- private void createVerifyOpenPack(List<RevObject> objectSource)
- throws MissingObjectException, IOException {
- NullProgressMonitor m = NullProgressMonitor.INSTANCE;
- writer = new PackWriter(config, db.newObjectReader());
- writer.preparePack(objectSource.iterator());
- assertEquals(objectSource.size(), writer.getObjectCount());
- writer.writePack(m, m, os);
- writer.close();
- verifyOpenPack(false);
- }
-
- private void verifyOpenPack(boolean thin) throws IOException {
- final byte[] packData = os.toByteArray();
-
- if (thin) {
- PackParser p = index(packData);
- try {
- p.parse(NullProgressMonitor.INSTANCE);
- fail("indexer should grumble about missing object");
- } catch (IOException x) {
- // expected
- }
- }
-
- ObjectDirectoryPackParser p = (ObjectDirectoryPackParser) index(packData);
- p.setKeepEmpty(true);
- p.setAllowThin(thin);
- p.setIndexVersion(2);
- p.parse(NullProgressMonitor.INSTANCE);
- pack = p.getPack();
- assertNotNull("have PackFile after parsing", pack);
- }
-
- private PackParser index(byte[] packData) throws IOException {
- if (inserter == null)
- inserter = dst.newObjectInserter();
- return inserter.newPackParser(new ByteArrayInputStream(packData));
- }
-
- private void verifyObjectsOrder(ObjectId objectsOrder[]) {
- final List<PackIndex.MutableEntry> entries = new ArrayList<>();
-
- for (MutableEntry me : pack) {
- entries.add(me.cloneEntry());
- }
- Collections.sort(entries, (MutableEntry o1, MutableEntry o2) -> Long
- .signum(o1.getOffset() - o2.getOffset()));
-
- int i = 0;
- for (MutableEntry me : entries) {
- assertEquals(objectsOrder[i++].toObjectId(), me.toObjectId());
- }
- }
-
- private static Set<ObjectId> haves(ObjectId... objects) {
- return Sets.of(objects);
- }
-
- private static Set<ObjectId> wants(ObjectId... objects) {
- return Sets.of(objects);
- }
-
- private static Set<ObjectId> shallows(ObjectId... objects) {
- return Sets.of(objects);
- }
- }
|