Merge "Do not log packfiles moved away or pruned"
diff --git a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MultiPackIndex.java b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MultiPackIndex.java
index a7df4fa..d91f713 100644
--- a/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MultiPackIndex.java
+++ b/org.eclipse.jgit.pgm/src/org/eclipse/jgit/pgm/MultiPackIndex.java
@@ -16,14 +16,13 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
-import java.util.LinkedHashMap;
import org.eclipse.jgit.internal.storage.file.ObjectDirectory;
import org.eclipse.jgit.internal.storage.file.Pack;
import org.eclipse.jgit.internal.storage.file.PackFile;
-import org.eclipse.jgit.internal.storage.file.PackIndex;
import org.eclipse.jgit.internal.storage.midx.MultiPackIndexPrettyPrinter;
import org.eclipse.jgit.internal.storage.midx.MultiPackIndexWriter;
+import org.eclipse.jgit.internal.storage.midx.PackIndexMerger;
import org.eclipse.jgit.internal.storage.pack.PackExt;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.kohsuke.args4j.Argument;
@@ -86,11 +85,11 @@ private void writeMultiPackIndex() throws IOException {
ObjectDirectory odb = (ObjectDirectory) db.getObjectDatabase();
- LinkedHashMap<String, PackIndex> indexes = new LinkedHashMap<>();
+ PackIndexMerger.Builder builder = PackIndexMerger.builder();
for (Pack pack : odb.getPacks()) {
PackFile packFile = pack.getPackFile().create(PackExt.INDEX);
try {
- indexes.put(packFile.getName(), pack.getIndex());
+ builder.addPack(packFile.getName(), pack.getIndex());
} catch (IOException e) {
throw die("Cannot open index in pack", e);
}
@@ -98,7 +97,7 @@ private void writeMultiPackIndex() throws IOException {
MultiPackIndexWriter writer = new MultiPackIndexWriter();
try (FileOutputStream out = new FileOutputStream(midxPath)) {
- writer.write(NullProgressMonitor.INSTANCE, out, indexes);
+ writer.write(NullProgressMonitor.INSTANCE, out, builder.build());
} catch (IOException e) {
throw die("Cannot write midx " + midxPath, e);
}
diff --git a/org.eclipse.jgit.test/exttst/org/eclipse/jgit/internal/storage/midx/CgitMidxCompatibilityTest.java b/org.eclipse.jgit.test/exttst/org/eclipse/jgit/internal/storage/midx/CgitMidxCompatibilityTest.java
index 334e52b..3aa6d69 100644
--- a/org.eclipse.jgit.test/exttst/org/eclipse/jgit/internal/storage/midx/CgitMidxCompatibilityTest.java
+++ b/org.eclipse.jgit.test/exttst/org/eclipse/jgit/internal/storage/midx/CgitMidxCompatibilityTest.java
@@ -25,13 +25,10 @@
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
import org.eclipse.jgit.internal.storage.file.Pack;
import org.eclipse.jgit.internal.storage.file.PackFile;
-import org.eclipse.jgit.internal.storage.file.PackIndex;
import org.eclipse.jgit.internal.storage.pack.PackExt;
import org.eclipse.jgit.lib.NullProgressMonitor;
import org.eclipse.jgit.test.resources.SampleDataRepositoryTestCase;
@@ -93,15 +90,15 @@ public void jgit_loadsCgitMidx()
}
private byte[] generateJGitMidx() throws IOException {
- Map<String, PackIndex> indexes = new HashMap<>();
+ PackIndexMerger.Builder builder = PackIndexMerger.builder();
for (Pack pack : db.getObjectDatabase().getPacks()) {
PackFile packFile = pack.getPackFile().create(PackExt.INDEX);
- indexes.put(packFile.getName(), pack.getIndex());
+ builder.addPack(packFile.getName(), pack.getIndex());
}
MultiPackIndexWriter writer = new MultiPackIndexWriter();
ByteArrayOutputStream out = new ByteArrayOutputStream();
- writer.write(NullProgressMonitor.INSTANCE, out, indexes);
+ writer.write(NullProgressMonitor.INSTANCE, out, builder.build());
return out.toByteArray();
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsMidxWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsMidxWriterBitmapsTest.java
similarity index 98%
rename from org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsMidxWriterTest.java
rename to org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsMidxWriterBitmapsTest.java
index ac89baa..0872dd4 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsMidxWriterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/dfs/DfsMidxWriterBitmapsTest.java
@@ -37,7 +37,7 @@
import com.googlecode.javaewah.EWAHCompressedBitmap;
@RunWith(Parameterized.class)
-public class DfsMidxWriterTest {
+public class DfsMidxWriterBitmapsTest {
@Parameterized.Parameters(name = "{0}")
public static Iterable<TestInput> data() throws Exception {
@@ -55,7 +55,7 @@ public String toString() {
private TestInput ti;
- public DfsMidxWriterTest(TestInput ti) {
+ public DfsMidxWriterBitmapsTest(TestInput ti) {
this.ti = ti;
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/MidxIteratorsTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/MidxIteratorsTest.java
new file mode 100644
index 0000000..3105423
--- /dev/null
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/MidxIteratorsTest.java
@@ -0,0 +1,434 @@
+/*
+ * Copyright (C) 2026, Google LLC.
+ *
+ * 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.midx;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.IntStream;
+
+import org.eclipse.jgit.internal.storage.file.PackIndex;
+import org.eclipse.jgit.internal.storage.midx.MultiPackIndex.MidxIterator;
+import org.eclipse.jgit.junit.FakeIndexFactory;
+import org.eclipse.jgit.lib.ObjectId;
+import org.junit.Test;
+
+public class MidxIteratorsTest {
+ private final static String OID_PREFIX = "0000000000000000000000000000000000";
+
+ @Test
+ public void fromPackIndexIterator_basicIteration() {
+ PackIndex index1 = indexOf(object("000001", 500),
+ object("000003", 3000), object("000005", 1500));
+
+ MidxIterator it = MidxIterators.fromPackIndexIterator("index1", index1);
+ assertNextEntry(it, "000001", 0, 500);
+ assertNextEntry(it, "000003", 0, 3000);
+ assertNextEntry(it, "000005", 0, 1500);
+ assertFalse(it.hasNext());
+ }
+
+ @Test
+ public void fromPackIndexIterator_peek() {
+ PackIndex index1 = indexOf(object("000001", 500),
+ object("000003", 3000), object("000005", 1500));
+
+ MidxIterator it = MidxIterators.fromPackIndexIterator("index1", index1);
+ assertPeekEntry(it, "000001", 0, 500);
+ assertPeekEntry(it, "000001", 0, 500);
+ assertNextEntry(it, "000001", 0, 500);
+
+ assertPeekEntry(it, "000003", 0, 3000);
+ assertPeekEntry(it, "000003", 0, 3000);
+ assertNextEntry(it, "000003", 0, 3000);
+
+ assertPeekEntry(it, "000005", 0, 1500);
+ assertPeekEntry(it, "000005", 0, 1500);
+ assertNextEntry(it, "000005", 0, 1500);
+ assertFalse(it.hasNext());
+ }
+
+ @Test
+ public void fromPackIndexIterator_reset() {
+ PackIndex index1 = indexOf(object("000001", 500),
+ object("000003", 3000), object("000005", 1500));
+
+ MidxIterator it = MidxIterators.fromPackIndexIterator("index1", index1);
+ while (it.hasNext()) {
+ it.next();
+ }
+ it.reset();
+ assertNextEntry(it, "000001", 0, 500);
+ assertNextEntry(it, "000003", 0, 3000);
+
+ it.reset();
+ assertPeekEntry(it, "000001", 0, 500);
+ }
+
+ @Test
+ public void fromPackIndexIterator_getPackNames() {
+ PackIndex index1 = indexOf(object("000001", 500),
+ object("000003", 1500), object("000005", 3000));
+ MidxIterator it = MidxIterators.fromPackIndexIterator("index1", index1);
+ assertEquals(List.of("index1"), it.getPackNames());
+ }
+
+ @Test
+ public void fromPackIndexIterator_empty() {
+ MidxIterator it = MidxIterators.fromPackIndexIterator("index1",
+ indexOf());
+ assertFalse(it.hasNext());
+ }
+
+ @Test
+ public void join_basicIteration() {
+ FakeMidxIterator itOne = FakeMidxIterator.from("itOne", 2,
+ List.of(new IndexEntry("000001", 0, 500),
+ new IndexEntry("000003", 1, 1500)));
+
+ FakeMidxIterator itTwo = FakeMidxIterator.from("itTwo", 2,
+ List.of(new IndexEntry("000002", 0, 500),
+ new IndexEntry("000004", 1, 1500)));
+
+ MidxIterator it = MidxIterators.join(List.of(itOne, itTwo));
+ assertNextEntry(it, "000001", 0, 500);
+ assertNextEntry(it, "000002", 2, 500);
+ assertNextEntry(it, "000003", 1, 1500);
+ assertNextEntry(it, "000004", 3, 1500);
+ assertFalse(it.hasNext());
+ }
+
+ @Test
+ public void join_basicIteration_packIndexIterators() {
+ PackIndex idxOne = indexOf(object("000001", 500),
+ object("000003", 1500), object("000005", 3000));
+
+ PackIndex idxTwo = indexOf(object("000002", 500),
+ object("000003", 1500), object("000004", 3000));
+
+ List<MidxIterator> packIts = List.of(
+ MidxIterators.fromPackIndexIterator("index1", idxOne),
+ MidxIterators.fromPackIndexIterator("index2", idxTwo));
+ MidxIterator it = MidxIterators.join(packIts);
+ assertNextEntry(it, "000001", 0, 500);
+ assertNextEntry(it, "000002", 1, 500);
+ assertNextEntry(it, "000003", 0, 1500);
+ assertNextEntry(it, "000003", 1, 1500);
+ assertNextEntry(it, "000004", 1, 3000);
+ assertNextEntry(it, "000005", 0, 3000);
+ assertFalse(it.hasNext());
+ }
+
+ @Test
+ public void join_duplicates_inPackIdOrder() {
+ FakeMidxIterator itOne = FakeMidxIterator.from("itOne", 2,
+ List.of(new IndexEntry("000001", 0, 501),
+ new IndexEntry("000003", 1, 1501)));
+
+ FakeMidxIterator itTwo = FakeMidxIterator.from("itTwo", 2,
+ List.of(new IndexEntry("000001", 0, 500),
+ new IndexEntry("000003", 1, 1500)));
+
+ MidxIterator it = MidxIterators.join(List.of(itOne, itTwo));
+ assertNextEntry(it, "000001", 0, 501);
+ assertNextEntry(it, "000001", 2, 500);
+ assertNextEntry(it, "000003", 1, 1501);
+ assertNextEntry(it, "000003", 3, 1500);
+ assertFalse(it.hasNext());
+ }
+
+ @Test
+ public void join_duplicates_inPackIdOrder_shift() {
+ FakeMidxIterator itOne = FakeMidxIterator.from("itOne", 5,
+ List.of(new IndexEntry("000001", 4, 501),
+ new IndexEntry("000003", 2, 1501)));
+
+ FakeMidxIterator itTwo = FakeMidxIterator.from("itTwo", 2,
+ List.of(new IndexEntry("000001", 0, 500),
+ new IndexEntry("000003", 1, 1500)));
+
+ MidxIterator it = MidxIterators.join(List.of(itOne, itTwo));
+ assertNextEntry(it, "000001", 4, 501);
+ assertNextEntry(it, "000001", 5, 500);
+ assertNextEntry(it, "000003", 2, 1501);
+ assertNextEntry(it, "000003", 6, 1500);
+ assertFalse(it.hasNext());
+ }
+
+ @Test
+ public void join_peek() {
+ FakeMidxIterator itOne = FakeMidxIterator.from("itOne", 2,
+ List.of(new IndexEntry("000001", 0, 500),
+ new IndexEntry("000003", 1, 1500)));
+
+ FakeMidxIterator itTwo = FakeMidxIterator.from("itTwo", 2,
+ List.of(new IndexEntry("000002", 0, 500),
+ new IndexEntry("000004", 1, 1500)));
+
+ MidxIterator it = MidxIterators.join(List.of(itOne, itTwo));
+ assertPeekEntry(it, "000001", 0, 500);
+ assertPeekEntry(it, "000001", 0, 500);
+ assertNextEntry(it, "000001", 0, 500);
+
+ assertPeekEntry(it, "000002", 2, 500);
+ assertPeekEntry(it, "000002", 2, 500);
+ assertNextEntry(it, "000002", 2, 500);
+
+ assertPeekEntry(it, "000003", 1, 1500);
+ assertPeekEntry(it, "000003", 1, 1500);
+ assertNextEntry(it, "000003", 1, 1500);
+
+ assertPeekEntry(it, "000004", 3, 1500);
+ assertPeekEntry(it, "000004", 3, 1500);
+ assertNextEntry(it, "000004", 3, 1500);
+ assertFalse(it.hasNext());
+ }
+
+ @Test
+ public void join_reset() {
+ FakeMidxIterator itOne = FakeMidxIterator.from("itOne", 2,
+ List.of(new IndexEntry("000001", 0, 500),
+ new IndexEntry("000003", 1, 1500)));
+
+ FakeMidxIterator itTwo = FakeMidxIterator.from("itTwo", 2,
+ List.of(new IndexEntry("000002", 0, 500),
+ new IndexEntry("000004", 1, 1500)));
+
+ MidxIterator it = MidxIterators.join(List.of(itOne, itTwo));
+ while (it.hasNext()) {
+ it.next();
+ }
+
+ it.reset();
+ assertNextEntry(it, "000001", 0, 500);
+ assertNextEntry(it, "000002", 2, 500);
+
+ it.reset();
+ assertPeekEntry(it, "000001", 0, 500);
+ }
+
+ @Test
+ public void join_getPackNames() {
+ FakeMidxIterator itOne = FakeMidxIterator.from("itOne", 2,
+ List.of(new IndexEntry("000001", 0, 500),
+ new IndexEntry("000003", 1, 1500)));
+
+ FakeMidxIterator itTwo = FakeMidxIterator.from("itTwo", 2,
+ List.of(new IndexEntry("000002", 0, 500),
+ new IndexEntry("000004", 1, 1500)));
+
+ MidxIterator it = MidxIterators.join(List.of(itOne, itTwo));
+ assertEquals(List.of("itOne0", "itOne1", "itTwo0", "itTwo1"),
+ it.getPackNames());
+ }
+
+ @Test
+ public void join_empty_totallyEmpty() {
+ FakeMidxIterator itOne = FakeMidxIterator.from("itOne", 2, List.of());
+ FakeMidxIterator itTwo = FakeMidxIterator.from("itTwo", 2, List.of());
+
+ MidxIterator it = MidxIterators.join(List.of(itOne, itTwo));
+ assertFalse(it.hasNext());
+ }
+
+ @Test
+ public void join_empty_oneSideEmpty() {
+ FakeMidxIterator itOne = FakeMidxIterator.from("itOne", 2, List.of());
+ FakeMidxIterator itTwo = FakeMidxIterator.from("itTwo", 2,
+ List.of(new IndexEntry("000002", 0, 500),
+ new IndexEntry("000004", 1, 1500)));
+
+ MidxIterator it = MidxIterators.join(List.of(itOne, itTwo));
+ // Even when empty, the first iterator occupies the packIds
+ assertNextEntry(it, "000002", 2, 500);
+ assertNextEntry(it, "000004", 3, 1500);
+ assertFalse(it.hasNext());
+ }
+
+ @Test
+ public void dedup_basicIteration() {
+ FakeMidxIterator itOne = FakeMidxIterator.from("itOne", 2,
+ List.of(new IndexEntry("000001", 0, 500),
+ new IndexEntry("000001", 1, 600),
+ new IndexEntry("000003", 0, 1500),
+ new IndexEntry("000003", 1, 1501)));
+ MidxIterator dedup = MidxIterators.dedup(itOne);
+ assertNextEntry(dedup, "000001", 0, 500);
+ assertNextEntry(dedup, "000003", 0, 1500);
+ assertFalse(dedup.hasNext());
+ }
+
+ @Test
+ public void dedup_peek() {
+ FakeMidxIterator itOne = FakeMidxIterator.from("itOne", 2,
+ List.of(new IndexEntry("000001", 0, 500),
+ new IndexEntry("000001", 1, 600),
+ new IndexEntry("000003", 0, 1500),
+ new IndexEntry("000003", 1, 1501)));
+
+ MidxIterator it = MidxIterators.dedup(itOne);
+ assertPeekEntry(it, "000001", 0, 500);
+ assertPeekEntry(it, "000001", 0, 500);
+ assertNextEntry(it, "000001", 0, 500);
+
+ assertPeekEntry(it, "000003", 0, 1500);
+ assertPeekEntry(it, "000003", 0, 1500);
+ assertNextEntry(it, "000003", 0, 1500);
+
+ assertFalse(it.hasNext());
+ }
+
+ @Test
+ public void dedup_reset() {
+ FakeMidxIterator itOne = FakeMidxIterator.from("itOne", 2,
+ List.of(new IndexEntry("000001", 0, 500),
+ new IndexEntry("000001", 1, 600),
+ new IndexEntry("000003", 0, 1500),
+ new IndexEntry("000003", 1, 1501),
+ new IndexEntry("000005", 0, 200),
+ new IndexEntry("000005", 1, 201)));
+
+ MidxIterator it = MidxIterators.dedup(itOne);
+ while (it.hasNext()) {
+ it.next();
+ }
+ it.reset();
+ assertNextEntry(it, "000001", 0, 500);
+ assertNextEntry(it, "000003", 0, 1500);
+
+ it.reset();
+ assertPeekEntry(it, "000001", 0, 500);
+ }
+
+ @Test
+ public void dedup_reset_sameElement() {
+ FakeMidxIterator itOne = FakeMidxIterator.from("itOne", 2,
+ List.of(new IndexEntry("000001", 0, 500),
+ new IndexEntry("000001", 1, 600),
+ new IndexEntry("000001", 2, 1500),
+ new IndexEntry("000001", 3, 1501),
+ new IndexEntry("000001", 4, 200),
+ new IndexEntry("000001", 5, 201)));
+
+ MidxIterator it = MidxIterators.dedup(itOne);
+ while (it.hasNext()) {
+ it.next();
+ }
+ it.reset();
+ assertNextEntry(it, "000001", 0, 500);
+
+ it.reset();
+ assertPeekEntry(it, "000001", 0, 500);
+ }
+
+ @Test
+ public void dedup_getPackNames() {
+ FakeMidxIterator itOne = FakeMidxIterator.from("itOne", 4,
+ List.of(new IndexEntry("000001", 0, 500),
+ new IndexEntry("000001", 1, 600),
+ new IndexEntry("000003", 2, 1500),
+ new IndexEntry("000003", 3, 1501)));
+ MidxIterator dedup = MidxIterators.dedup(itOne);
+ assertEquals(List.of("itOne0", "itOne1", "itOne2", "itOne3"),
+ dedup.getPackNames());
+ }
+
+ @Test
+ public void dedup_getEmpty() {
+ FakeMidxIterator itOne = FakeMidxIterator.from("itOne", 4, List.of());
+ MidxIterator dedup = MidxIterators.dedup(itOne);
+ assertFalse(dedup.hasNext());
+ }
+
+ private static void assertNextEntry(MidxIterator it, String shortOid,
+ int packId, int offset) {
+ assertTrue("expected to have more items", it.hasNext());
+ MultiPackIndex.MutableEntry e = it.next();
+ assertEquals(OID_PREFIX + shortOid, e.getObjectId().name());
+ assertEquals(packId, e.getPackId());
+ assertEquals(offset, e.getOffset());
+ }
+
+ private static void assertPeekEntry(MidxIterator it, String shortOid,
+ int packId, int offset) {
+ assertTrue(it.hasNext());
+ MultiPackIndex.MutableEntry e = it.peek();
+ assertEquals(OID_PREFIX + shortOid, e.getObjectId().name());
+ assertEquals(packId, e.getPackId());
+ assertEquals(offset, e.getOffset());
+ }
+
+ private static PackIndex indexOf(FakeIndexFactory.IndexObject... objs) {
+ return FakeIndexFactory.indexOf(Arrays.asList(objs));
+ }
+
+ private static FakeIndexFactory.IndexObject object(String name,
+ long offset) {
+ return new FakeIndexFactory.IndexObject(OID_PREFIX + name, offset);
+ }
+
+ private static class FakeMidxIterator implements MidxIterator {
+ private final List<String> packNames;
+
+ private final List<IndexEntry> entries;
+
+ private int position;
+
+ static FakeMidxIterator from(String packNameBase, int packCount,
+ List<IndexEntry> entries) {
+ List<String> packNames = IntStream.range(0, packCount)
+ .mapToObj(i -> packNameBase + i).toList();
+ return new FakeMidxIterator(packNames, entries);
+ }
+
+ private FakeMidxIterator(List<String> packNames,
+ List<IndexEntry> entries) {
+ this.entries = entries;
+ this.packNames = packNames;
+ }
+
+ @Override
+ public MultiPackIndex.MutableEntry peek() {
+ return entries.get(position).asMutableEntry();
+ }
+
+ @Override
+ public List<String> getPackNames() {
+ return packNames;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return position < entries.size();
+ }
+
+ @Override
+ public MultiPackIndex.MutableEntry next() {
+ return entries.get(position++).asMutableEntry();
+ }
+
+ @Override
+ public void reset() {
+ position = 0;
+ }
+ }
+
+ record IndexEntry(String shortOid, int packId, int offset) {
+ MultiPackIndex.MutableEntry asMutableEntry() {
+ MultiPackIndex.MutableEntry entry = new MultiPackIndex.MutableEntry();
+ entry.oid.fromObjectId(ObjectId.fromString(OID_PREFIX + shortOid));
+ entry.packOffset.setValues(packId, offset);
+ return entry;
+ }
+ }
+}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/MultiPackIndexLoaderTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/MultiPackIndexLoaderTest.java
index 3c7e27d..a59e874 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/MultiPackIndexLoaderTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/MultiPackIndexLoaderTest.java
@@ -17,7 +17,6 @@
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
-import java.util.LinkedHashMap;
import java.util.List;
import org.eclipse.jgit.internal.storage.file.PackIndex;
@@ -64,13 +63,11 @@ public void load_validFile_basic_jgit() throws Exception {
new FakeIndexFactory.IndexObject(
"0000000000000000000000000000000000000012", 1502)));
- LinkedHashMap<String, PackIndex> packs = new LinkedHashMap<>(3);
- packs.put("p1", idxOne);
- packs.put("p2", idxTwo);
- packs.put("p3", idxThree);
+ PackIndexMerger data = PackIndexMerger.builder().addPack("p1", idxOne)
+ .addPack("p2", idxTwo).addPack("p3", idxThree).build();
MultiPackIndexWriter writer = new MultiPackIndexWriter();
ByteArrayOutputStream out = new ByteArrayOutputStream();
- writer.write(NullProgressMonitor.INSTANCE, out, packs);
+ writer.write(NullProgressMonitor.INSTANCE, out, data);
MultiPackIndex midx = MultiPackIndexLoader
.read(new ByteArrayInputStream(out.toByteArray()));
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/MultiPackIndexTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/MultiPackIndexTest.java
index a2c86f3..75a5a95 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/MultiPackIndexTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/MultiPackIndexTest.java
@@ -22,7 +22,6 @@
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
-import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;
@@ -114,7 +113,7 @@ public void jgit_largeOffsetChunk() throws IOException {
"0000000000000000000000000000000000000002", (1L << 35)),
new FakeIndexFactory.IndexObject(
"0000000000000000000000000000000000000003", 13)));
- LinkedHashMap<String, PackIndex> packs = orderedMapOf("p1", idxOne,
+ PackIndexMerger packs = midxDataFor("p1", idxOne,
"p2", idxTwo);
MultiPackIndexWriter writer = new MultiPackIndexWriter();
ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -145,7 +144,7 @@ public void jgit_largeOffset_noChunk() throws IOException {
"0000000000000000000000000000000000000002", 501),
new FakeIndexFactory.IndexObject(
"0000000000000000000000000000000000000003", 13)));
- LinkedHashMap<String, PackIndex> packs = orderedMapOf("p1", idxOne,
+ PackIndexMerger packs = midxDataFor("p1", idxOne,
"p2", idxTwo);
MultiPackIndexWriter writer = new MultiPackIndexWriter();
ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -182,7 +181,7 @@ public void jgit_resolve() throws IOException {
// Match
"32fe829a1c000000000000000000000000000010");
- LinkedHashMap<String, PackIndex> packs = orderedMapOf("p1", idxOne,
+ PackIndexMerger packs = midxDataFor("p1", idxOne,
"p2", idxTwo);
MultiPackIndexWriter writer = new MultiPackIndexWriter();
ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -226,7 +225,7 @@ public void jgit_resolve_matchLimit() throws IOException {
// Match
"32fe829a1c000000000000000000000000000010");
- LinkedHashMap<String, PackIndex> packs = orderedMapOf("r1", idxOne,
+ PackIndexMerger packs = midxDataFor("r1", idxOne,
"r2", idxTwo);
MultiPackIndexWriter writer = new MultiPackIndexWriter();
ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -261,7 +260,7 @@ public void jgit_resolve_noMatches() throws IOException {
"bbbbbb0000000000000000000000000000000003",
"32fe829a1c000000000000000000000000000010");
- LinkedHashMap<String, PackIndex> packs = orderedMapOf("p1", idxOne,
+ PackIndexMerger packs = midxDataFor("p1", idxOne,
"p2", idxTwo);
MultiPackIndexWriter writer = new MultiPackIndexWriter();
ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -292,7 +291,7 @@ public void jgit_resolve_noMatches_last() throws IOException {
"bbbbbb0000000000000000000000000000000003",
"32fe829a1c000000000000000000000000000010");
- LinkedHashMap<String, PackIndex> packs = orderedMapOf("p1", idxOne,
+ PackIndexMerger packs = midxDataFor("p1", idxOne,
"p2", idxTwo);
MultiPackIndexWriter writer = new MultiPackIndexWriter();
ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -314,11 +313,11 @@ public void jgit_resolve_empty() throws IOException {
PackIndex idxOne = FakeIndexFactory.indexOf(List.of());
PackIndex idxTwo = FakeIndexFactory.indexOf(List.of());
- LinkedHashMap<String, PackIndex> packs = orderedMapOf("p1", idxOne,
+ PackIndexMerger data = midxDataFor("p1", idxOne,
"p2", idxTwo);
MultiPackIndexWriter writer = new MultiPackIndexWriter();
ByteArrayOutputStream out = new ByteArrayOutputStream();
- writer.write(NullProgressMonitor.INSTANCE, out, packs);
+ writer.write(NullProgressMonitor.INSTANCE, out, data);
MultiPackIndex midx = MultiPackIndexLoader
.read(new ByteArrayInputStream(out.toByteArray()));
@@ -377,11 +376,11 @@ private static MultiPackIndex createMultiPackIndex() throws IOException {
new FakeIndexFactory.IndexObject(
"0000000000000000000000000000000000000012", 1502)));
- LinkedHashMap<String, PackIndex> packs = orderedMapOf("p1", idxOne,
+ PackIndexMerger data = midxDataFor("p1", idxOne,
"p2", idxTwo, "p3", idxThree);
MultiPackIndexWriter writer = new MultiPackIndexWriter();
ByteArrayOutputStream out = new ByteArrayOutputStream();
- writer.write(NullProgressMonitor.INSTANCE, out, packs);
+ writer.write(NullProgressMonitor.INSTANCE, out, data);
return MultiPackIndexLoader
.read(new ByteArrayInputStream(out.toByteArray()));
@@ -398,11 +397,11 @@ public void jgit_getObjectCount_emtpy() throws IOException {
PackIndex idxOne = FakeIndexFactory.indexOf(List.of());
PackIndex idxTwo = FakeIndexFactory.indexOf(List.of());
- LinkedHashMap<String, PackIndex> packs = orderedMapOf("p1", idxOne,
+ PackIndexMerger data = midxDataFor("p1", idxOne,
"p2", idxTwo);
MultiPackIndexWriter writer = new MultiPackIndexWriter();
ByteArrayOutputStream out = new ByteArrayOutputStream();
- writer.write(NullProgressMonitor.INSTANCE, out, packs);
+ writer.write(NullProgressMonitor.INSTANCE, out, data);
MultiPackIndex midx = MultiPackIndexLoader
.read(new ByteArrayInputStream(out.toByteArray()));
@@ -433,11 +432,11 @@ public void jgit_findBitmapPosition() throws IOException {
new FakeIndexFactory.IndexObject(
"0000000000000000000000000000000000000012", 1502)));
- LinkedHashMap<String, PackIndex> packs = orderedMapOf("p1", idxOne,
+ PackIndexMerger data = midxDataFor("p1", idxOne,
"p2", idxTwo, "p3", idxThree);
MultiPackIndexWriter writer = new MultiPackIndexWriter();
ByteArrayOutputStream out = new ByteArrayOutputStream();
- writer.write(NullProgressMonitor.INSTANCE, out, packs);
+ writer.write(NullProgressMonitor.INSTANCE, out, data);
MultiPackIndex midx = MultiPackIndexLoader
.read(new ByteArrayInputStream(out.toByteArray()));
@@ -495,11 +494,11 @@ public void jgit_getObjectAtBitmapPosition() throws IOException {
new FakeIndexFactory.IndexObject(
"0000000000000000000000000000000000000012", 1502)));
- LinkedHashMap<String, PackIndex> packs = orderedMapOf("p1", idxOne,
+ PackIndexMerger data = midxDataFor("p1", idxOne,
"p2", idxTwo, "p3", idxThree);
MultiPackIndexWriter writer = new MultiPackIndexWriter();
ByteArrayOutputStream out = new ByteArrayOutputStream();
- writer.write(NullProgressMonitor.INSTANCE, out, packs);
+ writer.write(NullProgressMonitor.INSTANCE, out, data);
MultiPackIndex midx = MultiPackIndexLoader
.read(new ByteArrayInputStream(out.toByteArray()));
@@ -563,11 +562,11 @@ public void jgit_iterator_emtpy() throws IOException {
PackIndex idxOne = FakeIndexFactory.indexOf(List.of());
PackIndex idxTwo = FakeIndexFactory.indexOf(List.of());
- LinkedHashMap<String, PackIndex> packs = orderedMapOf("p1", idxOne,
+ PackIndexMerger data = midxDataFor("p1", idxOne,
"p2", idxTwo);
MultiPackIndexWriter writer = new MultiPackIndexWriter();
ByteArrayOutputStream out = new ByteArrayOutputStream();
- writer.write(NullProgressMonitor.INSTANCE, out, packs);
+ writer.write(NullProgressMonitor.INSTANCE, out, data);
MultiPackIndex midx = MultiPackIndexLoader
.read(new ByteArrayInputStream(out.toByteArray()));
@@ -591,11 +590,11 @@ public void jgit_iterator_peek() throws IOException {
new FakeIndexFactory.IndexObject(
"0000000000000000000000000000000000000015", 1501)));
- LinkedHashMap<String, PackIndex> packs = orderedMapOf("p1", idxOne,
+ PackIndexMerger data = midxDataFor("p1", idxOne,
"p2", idxTwo);
MultiPackIndexWriter writer = new MultiPackIndexWriter();
ByteArrayOutputStream out = new ByteArrayOutputStream();
- writer.write(NullProgressMonitor.INSTANCE, out, packs);
+ writer.write(NullProgressMonitor.INSTANCE, out, data);
MultiPackIndex midx = MultiPackIndexLoader
.read(new ByteArrayInputStream(out.toByteArray()));
@@ -662,21 +661,16 @@ private static void assertEntry(MultiPackIndex.MutableEntry e, String oid,
assertEquals(expectedOffset, e.packOffset.getOffset());
}
- private static LinkedHashMap<String, PackIndex> orderedMapOf(String s1,
- PackIndex pi1, String s2, PackIndex pi2) {
- LinkedHashMap<String, PackIndex> map = new LinkedHashMap<>(2);
- map.put(s1, pi1);
- map.put(s2, pi2);
- return map;
+ private static PackIndexMerger midxDataFor(String s1, PackIndex pi1,
+ String s2, PackIndex pi2) {
+ return PackIndexMerger.builder().addPack(s1, pi1).addPack(s2, pi2)
+ .build();
}
- private static LinkedHashMap<String, PackIndex> orderedMapOf(String s1,
+ private static PackIndexMerger midxDataFor(String s1,
PackIndex pi1, String s2, PackIndex pi2, String s3, PackIndex pi3) {
- LinkedHashMap<String, PackIndex> map = new LinkedHashMap<>(3);
- map.put(s1, pi1);
- map.put(s2, pi2);
- map.put(s3, pi3);
- return map;
+ return PackIndexMerger.builder().addPack(s1, pi1).addPack(s2, pi2)
+ .addPack(s3, pi3).build();
}
private static ObjectId oid(String last3chars) {
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/MultiPackIndexWriterTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/MultiPackIndexWriterTest.java
index 5971dceb..1ca8aaf 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/MultiPackIndexWriterTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/MultiPackIndexWriterTest.java
@@ -23,7 +23,6 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.LinkedHashMap;
import java.util.List;
import org.eclipse.jgit.internal.storage.file.PackIndex;
@@ -47,9 +46,9 @@ public void write_allSmallOffsets() throws IOException {
object("0000000000000000000000000000000000000004", 1500),
object("0000000000000000000000000000000000000006", 3000));
- LinkedHashMap<String, PackIndex> data = new LinkedHashMap<>();
- data.put("packname1", index1);
- data.put("packname2", index2);
+ PackIndexMerger data = PackIndexMerger.builder()
+ .addPack("packname1", index1).addPack("packname2", index2)
+ .build();
MultiPackIndexWriter writer = new MultiPackIndexWriter();
ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -85,9 +84,9 @@ public void write_smallOffset_limit() throws IOException {
object("0000000000000000000000000000000000000002", 500),
object("0000000000000000000000000000000000000004", 1500),
object("0000000000000000000000000000000000000006", 3000));
- LinkedHashMap<String, PackIndex> data = new LinkedHashMap<>(2);
- data.put("packname1", index1);
- data.put("packname2", index2);
+ PackIndexMerger data = PackIndexMerger.builder()
+ .addPack("packname1", index1).addPack("packname2", index2)
+ .build();
MultiPackIndexWriter writer = new MultiPackIndexWriter();
ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -122,9 +121,9 @@ public void write_largeOffset() throws IOException {
object("0000000000000000000000000000000000000002", 500),
object("0000000000000000000000000000000000000004", 1500),
object("0000000000000000000000000000000000000006", 3000));
- LinkedHashMap<String, PackIndex> data = new LinkedHashMap<>(2);
- data.put("bbbbbbbbb", index1);
- data.put("aaaaaaaaa", index2);
+ PackIndexMerger data = PackIndexMerger.builder()
+ .addPack("bbbbbbbbb", index1).addPack("aaaaaaaaa", index2)
+ .build();
MultiPackIndexWriter writer = new MultiPackIndexWriter();
ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -159,9 +158,8 @@ public void write_largeOffset() throws IOException {
public void jgit_emptyMidx() throws IOException {
PackIndex idxOne = FakeIndexFactory.indexOf(List.of());
PackIndex idxTwo = FakeIndexFactory.indexOf(List.of());
- LinkedHashMap<String, PackIndex> packs = new LinkedHashMap<>(2);
- packs.put("p1", idxOne);
- packs.put("p2", idxTwo);
+ PackIndexMerger packs = PackIndexMerger.builder().addPack("p1", idxOne)
+ .addPack("p2", idxTwo).build();
MultiPackIndexWriter writer = new MultiPackIndexWriter();
ByteArrayOutputStream out = new ByteArrayOutputStream();
writer.write(NullProgressMonitor.INSTANCE, out, packs);
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/PackIndexMergerTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/PackIndexMergerTest.java
index a43992d..971087e 100644
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/PackIndexMergerTest.java
+++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/PackIndexMergerTest.java
@@ -14,19 +14,23 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
-import java.util.LinkedHashMap;
import org.eclipse.jgit.internal.storage.file.PackIndex;
+import org.eclipse.jgit.internal.storage.midx.MultiPackIndex.MutableEntry;
import org.eclipse.jgit.junit.FakeIndexFactory;
import org.eclipse.jgit.junit.FakeIndexFactory.IndexObject;
+import org.eclipse.jgit.lib.NullProgressMonitor;
import org.junit.Test;
public class PackIndexMergerTest {
@Test
- public void rawIterator_noDuplicates() {
+ public void bySha1Iterator_noDuplicates() {
PackIndex idxOne = indexOf(
oidOffset("0000000000000000000000000000000000000001", 500),
oidOffset("0000000000000000000000000000000000000005", 12),
@@ -39,12 +43,12 @@ public void rawIterator_noDuplicates() {
oidOffset("0000000000000000000000000000000000000004", 502),
oidOffset("0000000000000000000000000000000000000007", 14),
oidOffset("0000000000000000000000000000000000000012", 1502));
- PackIndexMerger merger = new PackIndexMerger(
- orderedMapOf("p1", idxOne, "p2", idxTwo, "p3", idxThree));
+ PackIndexMerger merger = createMergerFor("p1", idxOne, "p2", idxTwo,
+ "p3", idxThree);
assertEquals(9, merger.getUniqueObjectCount());
assertEquals(3, merger.getPackCount());
assertFalse(merger.needsLargeOffsetsChunk());
- Iterator<PackIndexMerger.MidxMutableEntry> it = merger.rawIterator();
+ Iterator<MutableEntry> it = merger.bySha1Iterator();
assertNextEntry(it, "0000000000000000000000000000000000000001", 0, 500);
assertNextEntry(it, "0000000000000000000000000000000000000002", 1, 501);
assertNextEntry(it, "0000000000000000000000000000000000000003", 1, 13);
@@ -61,92 +65,31 @@ public void rawIterator_noDuplicates() {
}
@Test
- public void rawIterator_noDuplicates_honorPackOrder() {
+ public void bySha1Iterator_withDuplicates() {
PackIndex idxOne = indexOf(
oidOffset("0000000000000000000000000000000000000001", 500),
- oidOffset("0000000000000000000000000000000000000005", 12),
oidOffset("0000000000000000000000000000000000000010", 1500));
PackIndex idxTwo = indexOf(
oidOffset("0000000000000000000000000000000000000002", 501),
oidOffset("0000000000000000000000000000000000000003", 13),
+ oidOffset("0000000000000000000000000000000000000005", 800),
oidOffset("0000000000000000000000000000000000000015", 1501));
PackIndex idxThree = indexOf(
oidOffset("0000000000000000000000000000000000000004", 502),
+ oidOffset("0000000000000000000000000000000000000005", 12),
oidOffset("0000000000000000000000000000000000000007", 14),
oidOffset("0000000000000000000000000000000000000012", 1502));
- PackIndexMerger merger = new PackIndexMerger(
- orderedMapOf("p3", idxThree, "p2", idxTwo, "p1", idxOne));
+ PackIndexMerger merger = createMergerFor("p1", idxOne, "p2", idxTwo,
+ "p3", idxThree);
assertEquals(9, merger.getUniqueObjectCount());
assertEquals(3, merger.getPackCount());
assertFalse(merger.needsLargeOffsetsChunk());
- Iterator<PackIndexMerger.MidxMutableEntry> it = merger.rawIterator();
- assertNextEntry(it, "0000000000000000000000000000000000000001", 2, 500);
- assertNextEntry(it, "0000000000000000000000000000000000000002", 1, 501);
- assertNextEntry(it, "0000000000000000000000000000000000000003", 1, 13);
- assertNextEntry(it, "0000000000000000000000000000000000000004", 0, 502);
- assertNextEntry(it, "0000000000000000000000000000000000000005", 2, 12);
- assertNextEntry(it, "0000000000000000000000000000000000000007", 0, 14);
- assertNextEntry(it, "0000000000000000000000000000000000000010", 2,
- 1500);
- assertNextEntry(it, "0000000000000000000000000000000000000012", 0,
- 1502);
- assertNextEntry(it, "0000000000000000000000000000000000000015", 1,
- 1501);
- assertFalse(it.hasNext());
- }
-
- @Test
- public void rawIterator_allDuplicates() {
- PackIndex idxOne = indexOf(
- oidOffset("0000000000000000000000000000000000000001", 500),
- oidOffset("0000000000000000000000000000000000000005", 12),
- oidOffset("0000000000000000000000000000000000000010", 1500));
- PackIndexMerger merger = new PackIndexMerger(
- orderedMapOf("p1", idxOne, "p2", idxOne, "p3", idxOne));
- assertEquals(3, merger.getUniqueObjectCount());
- assertEquals(3, merger.getPackCount());
- assertFalse(merger.needsLargeOffsetsChunk());
- Iterator<PackIndexMerger.MidxMutableEntry> it = merger.rawIterator();
- assertNextEntry(it, "0000000000000000000000000000000000000001", 0, 500);
- assertNextEntry(it, "0000000000000000000000000000000000000001", 1, 500);
- assertNextEntry(it, "0000000000000000000000000000000000000001", 2, 500);
- assertNextEntry(it, "0000000000000000000000000000000000000005", 0, 12);
- assertNextEntry(it, "0000000000000000000000000000000000000005", 1, 12);
- assertNextEntry(it, "0000000000000000000000000000000000000005", 2, 12);
- assertNextEntry(it, "0000000000000000000000000000000000000010", 0,
- 1500);
- assertNextEntry(it, "0000000000000000000000000000000000000010", 1,
- 1500);
- assertNextEntry(it, "0000000000000000000000000000000000000010", 2,
- 1500);
- assertFalse(it.hasNext());
- }
-
- @Test
- public void bySha1Iterator_noDuplicates() {
- PackIndex idxOne = indexOf(
- oidOffset("0000000000000000000000000000000000000001", 500),
- oidOffset("0000000000000000000000000000000000000005", 12),
- oidOffset("0000000000000000000000000000000000000010", 1500));
- PackIndex idxTwo = indexOf(
- oidOffset("0000000000000000000000000000000000000002", 501),
- oidOffset("0000000000000000000000000000000000000003", 13),
- oidOffset("0000000000000000000000000000000000000015", 1501));
- PackIndex idxThree = indexOf(
- oidOffset("0000000000000000000000000000000000000004", 502),
- oidOffset("0000000000000000000000000000000000000007", 14),
- oidOffset("0000000000000000000000000000000000000012", 1502));
- PackIndexMerger merger = new PackIndexMerger(
- orderedMapOf("p1", idxOne, "p2", idxTwo, "p3", idxThree));
- assertEquals(9, merger.getUniqueObjectCount());
- assertEquals(3, merger.getPackCount());
- assertFalse(merger.needsLargeOffsetsChunk());
- Iterator<PackIndexMerger.MidxMutableEntry> it = merger.bySha1Iterator();
+ Iterator<MutableEntry> it = merger.bySha1Iterator();
assertNextEntry(it, "0000000000000000000000000000000000000001", 0, 500);
assertNextEntry(it, "0000000000000000000000000000000000000002", 1, 501);
assertNextEntry(it, "0000000000000000000000000000000000000003", 1, 13);
assertNextEntry(it, "0000000000000000000000000000000000000004", 2, 502);
- assertNextEntry(it, "0000000000000000000000000000000000000005", 0, 12);
+ assertNextEntry(it, "0000000000000000000000000000000000000005", 1, 800);
assertNextEntry(it, "0000000000000000000000000000000000000007", 2, 14);
assertNextEntry(it, "0000000000000000000000000000000000000010", 0,
1500);
@@ -163,12 +106,12 @@ public void bySha1Iterator_allDuplicates() {
oidOffset("0000000000000000000000000000000000000001", 500),
oidOffset("0000000000000000000000000000000000000005", 12),
oidOffset("0000000000000000000000000000000000000010", 1500));
- PackIndexMerger merger = new PackIndexMerger(
- orderedMapOf("p1", idxOne, "p2", idxOne, "p3", idxOne));
+ PackIndexMerger merger = createMergerFor("p1", idxOne, "p2", idxOne,
+ "p3", idxOne);
assertEquals(3, merger.getUniqueObjectCount());
assertEquals(3, merger.getPackCount());
assertFalse(merger.needsLargeOffsetsChunk());
- Iterator<PackIndexMerger.MidxMutableEntry> it = merger.bySha1Iterator();
+ Iterator<MutableEntry> it = merger.bySha1Iterator();
assertNextEntry(it, "0000000000000000000000000000000000000001", 0, 500);
assertNextEntry(it, "0000000000000000000000000000000000000005", 0, 12);
assertNextEntry(it, "0000000000000000000000000000000000000010", 0,
@@ -187,12 +130,12 @@ public void bySha1Iterator_differentIndexSizes() {
oidOffset("0000000000000000000000000000000000000004", 500),
oidOffset("0000000000000000000000000000000000000007", 12),
oidOffset("0000000000000000000000000000000000000012", 1500));
- PackIndexMerger merger = new PackIndexMerger(
- orderedMapOf("p1", idxOne, "p2", idxTwo, "p3", idxThree));
+ PackIndexMerger merger = createMergerFor("p1", idxOne, "p2", idxTwo,
+ "p3", idxThree);
assertEquals(6, merger.getUniqueObjectCount());
assertEquals(3, merger.getPackCount());
assertFalse(merger.needsLargeOffsetsChunk());
- Iterator<PackIndexMerger.MidxMutableEntry> it = merger.bySha1Iterator();
+ Iterator<MutableEntry> it = merger.bySha1Iterator();
assertNextEntry(it, "0000000000000000000000000000000000000002", 1, 500);
assertNextEntry(it, "0000000000000000000000000000000000000003", 1, 12);
assertNextEntry(it, "0000000000000000000000000000000000000004", 2, 500);
@@ -205,8 +148,46 @@ public void bySha1Iterator_differentIndexSizes() {
}
@Test
+ public void bySha1Iterator_withAnotherMidx() throws IOException {
+ PackIndex idxOne = indexOf(
+ oidOffset("0000000000000000000000000000000000000010", 1500));
+ PackIndex idxTwo = indexOf(
+ oidOffset("0000000000000000000000000000000000000002", 500),
+ oidOffset("0000000000000000000000000000000000000003", 12));
+ PackIndex idxThree = indexOf(
+ oidOffset("0000000000000000000000000000000000000004", 500),
+ oidOffset("0000000000000000000000000000000000000007", 12),
+ oidOffset("0000000000000000000000000000000000000012", 1500));
+ MultiPackIndex midx = midxOf("one", idxOne, "two", idxTwo, "three",
+ idxThree);
+
+ PackIndex idxFour = indexOf(
+ oidOffset("0000000000000000000000000000000000000001", 12),
+ oidOffset("0000000000000000000000000000000000000007", 600),
+ oidOffset("0000000000000000000000000000000000000015", 300));
+
+ PackIndexMerger merger = PackIndexMerger.builder()
+ .addMidx(midx.iterator()).addPack("four", idxFour).build();
+ assertEquals(8, merger.getUniqueObjectCount());
+ assertEquals(4, merger.getPackCount());
+ assertFalse(merger.needsLargeOffsetsChunk());
+ Iterator<MutableEntry> it = merger.bySha1Iterator();
+ assertNextEntry(it, "0000000000000000000000000000000000000001", 3, 12);
+ assertNextEntry(it, "0000000000000000000000000000000000000002", 1, 500);
+ assertNextEntry(it, "0000000000000000000000000000000000000003", 1, 12);
+ assertNextEntry(it, "0000000000000000000000000000000000000004", 2, 500);
+ assertNextEntry(it, "0000000000000000000000000000000000000007", 2, 12);
+ assertNextEntry(it, "0000000000000000000000000000000000000010", 0,
+ 1500);
+ assertNextEntry(it, "0000000000000000000000000000000000000012", 2,
+ 1500);
+ assertNextEntry(it, "0000000000000000000000000000000000000015", 3, 300);
+ assertFalse(it.hasNext());
+ }
+
+ @Test
public void merger_noIndexes() {
- PackIndexMerger merger = new PackIndexMerger(new LinkedHashMap<>());
+ PackIndexMerger merger = PackIndexMerger.builder().build();
assertEquals(0, merger.getUniqueObjectCount());
assertFalse(merger.needsLargeOffsetsChunk());
assertTrue(merger.getPackNames().isEmpty());
@@ -216,8 +197,8 @@ public void merger_noIndexes() {
@Test
public void merger_emptyIndexes() {
- PackIndexMerger merger = new PackIndexMerger(
- orderedMapOf("p1", indexOf(), "p2", indexOf()));
+ PackIndexMerger merger = createMergerFor("p1", indexOf(), "p2",
+ indexOf());
assertEquals(0, merger.getUniqueObjectCount());
assertFalse(merger.needsLargeOffsetsChunk());
assertEquals(2, merger.getPackNames().size());
@@ -232,8 +213,7 @@ public void bySha1Iterator_largeOffsets_needsChunk() {
oidOffset("0000000000000000000000000000000000000004", 12));
PackIndex idx2 = indexOf(oidOffset(
"0000000000000000000000000000000000000003", (1L << 31) + 10));
- PackIndexMerger merger = new PackIndexMerger(
- orderedMapOf("p1", idx1, "p2", idx2));
+ PackIndexMerger merger = createMergerFor("p1", idx1, "p2", idx2);
assertTrue(merger.needsLargeOffsetsChunk());
assertEquals(2, merger.getOffsetsOver31BitsCount());
assertEquals(3, merger.getUniqueObjectCount());
@@ -248,8 +228,7 @@ public void bySha1Iterator_largeOffsets_noChunk() {
oidOffset("0000000000000000000000000000000000000004", 12));
PackIndex idx2 = indexOf(oidOffset(
"0000000000000000000000000000000000000003", (1L << 31) + 10));
- PackIndexMerger merger = new PackIndexMerger(
- orderedMapOf("p1", idx1, "p2", idx2));
+ PackIndexMerger merger = createMergerFor("p1", idx1, "p2", idx2);
assertFalse(merger.needsLargeOffsetsChunk());
assertEquals(2, merger.getOffsetsOver31BitsCount());
assertEquals(3, merger.getUniqueObjectCount());
@@ -269,8 +248,8 @@ public void getObjectsPerPack_noDuplicates() {
oidOffset("0000000000000000000000000000000000000004", 502),
oidOffset("0000000000000000000000000000000000000007", 14),
oidOffset("0000000000000000000000000000000000000012", 1502));
- PackIndexMerger merger = new PackIndexMerger(
- orderedMapOf("p1", idxOne, "p2", idxTwo, "p3", idxThree));
+ PackIndexMerger merger = createMergerFor("p1", idxOne, "p2", idxTwo,
+ "p3", idxThree);
assertArrayEquals(new int[] { 3, 3, 3 }, merger.getObjectsPerPack());
}
@@ -285,8 +264,8 @@ public void getObjectsPerPack_differentIndexSizes() {
oidOffset("0000000000000000000000000000000000000004", 500),
oidOffset("0000000000000000000000000000000000000007", 12),
oidOffset("0000000000000000000000000000000000000012", 1500));
- PackIndexMerger merger = new PackIndexMerger(
- orderedMapOf("p1", idxOne, "p2", idxTwo, "p3", idxThree));
+ PackIndexMerger merger = createMergerFor("p1", idxOne, "p2", idxTwo,
+ "p3", idxThree);
assertArrayEquals(new int[] { 1, 2, 3 }, merger.getObjectsPerPack());
}
@@ -296,29 +275,28 @@ public void getObjectsPerPack_allDuplicates() {
oidOffset("0000000000000000000000000000000000000001", 500),
oidOffset("0000000000000000000000000000000000000005", 12),
oidOffset("0000000000000000000000000000000000000010", 1500));
- PackIndexMerger merger = new PackIndexMerger(
- orderedMapOf("p1", idxOne, "p2", idxOne, "p3", idxOne));
+ PackIndexMerger merger = createMergerFor("p1", idxOne, "p2", idxOne,
+ "p3", idxOne);
assertArrayEquals(new int[] { 3, 0, 0 }, merger.getObjectsPerPack());
}
@Test
public void getObjectsPerPack_noIndexes() {
- PackIndexMerger merger = new PackIndexMerger(new LinkedHashMap<>());
+ PackIndexMerger merger = PackIndexMerger.builder().build();
assertArrayEquals(new int[] {}, merger.getObjectsPerPack());
}
@Test
public void getObjectsPerPack_emptyIndexes() {
- PackIndexMerger merger = new PackIndexMerger(
- orderedMapOf("p1", indexOf(), "p2", indexOf()));
+ PackIndexMerger merger = createMergerFor("p1", indexOf(), "p2",
+ indexOf());
assertArrayEquals(new int[] { 0, 0 }, merger.getObjectsPerPack());
}
- private static void assertNextEntry(
- Iterator<PackIndexMerger.MidxMutableEntry> it, String oid,
+ private static void assertNextEntry(Iterator<MutableEntry> it, String oid,
int packId, long offset) {
assertTrue(it.hasNext());
- PackIndexMerger.MidxMutableEntry e = it.next();
+ MutableEntry e = it.next();
assertEquals(oid, e.getObjectId().name());
assertEquals(packId, e.getPackId());
assertEquals(offset, e.getOffset());
@@ -332,21 +310,28 @@ private static PackIndex indexOf(IndexObject... objs) {
return FakeIndexFactory.indexOf(Arrays.asList(objs));
}
- private static LinkedHashMap<String, PackIndex> orderedMapOf(String s1,
- PackIndex pi1, String s2, PackIndex pi2) {
- LinkedHashMap map = new LinkedHashMap(3);
- map.put(s1, pi1);
- map.put(s2, pi2);
- return map;
- }
+ private static MultiPackIndex midxOf(String s1, PackIndex idx1, String s2,
+ PackIndex idx2, String s3, PackIndex idx3) throws IOException {
+ PackIndexMerger merger = createMergerFor(s1, idx1, s2, idx2, s3, idx3);
+ MultiPackIndexWriter w = new MultiPackIndexWriter();
- private static LinkedHashMap<String, PackIndex> orderedMapOf(String s1,
- PackIndex pi1, String s2, PackIndex pi2, String s3, PackIndex pi3) {
- LinkedHashMap map = new LinkedHashMap(3);
- map.put(s1, pi1);
- map.put(s2, pi2);
- map.put(s3, pi3);
- return map;
- }
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ w.write(NullProgressMonitor.INSTANCE, out, merger);
+
+ ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+ return MultiPackIndexLoader.read(in);
+ }
+
+ private static PackIndexMerger createMergerFor(String s1, PackIndex pi1,
+ String s2, PackIndex pi2) {
+ return PackIndexMerger.builder().addPack(s1, pi1).addPack(s2, pi2)
+ .build();
+ }
+
+ private static PackIndexMerger createMergerFor(String s1, PackIndex pi1,
+ String s2, PackIndex pi2, String s3, PackIndex pi3) {
+ return PackIndexMerger.builder().addPack(s1, pi1).addPack(s2, pi2)
+ .addPack(s3, pi3).build();
+ }
}
diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/PackIndexPeekIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/PackIndexPeekIteratorTest.java
deleted file mode 100644
index 0b3ccac..0000000
--- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/midx/PackIndexPeekIteratorTest.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * Copyright (C) 2025, Google LLC
- *
- * 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.midx;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-
-import java.util.Arrays;
-
-import org.eclipse.jgit.internal.storage.file.PackIndex;
-import org.eclipse.jgit.junit.FakeIndexFactory;
-import org.junit.Test;
-
-public class PackIndexPeekIteratorTest {
- @Test
- public void next() {
- PackIndex index1 = indexOf(
- object("0000000000000000000000000000000000000001", 500),
- object("0000000000000000000000000000000000000003", 1500),
- object("0000000000000000000000000000000000000005", 3000));
- PackIndexMerger.PackIndexPeekIterator it = new PackIndexMerger.PackIndexPeekIterator(0, index1);
- assertEquals("0000000000000000000000000000000000000001", it.next().name());
- assertEquals("0000000000000000000000000000000000000003", it.next().name());
- assertEquals("0000000000000000000000000000000000000005", it.next().name());
- assertNull(it.next());
- }
-
- @Test
- public void peek_doesNotAdvance() {
- PackIndex index1 = indexOf(
- object("0000000000000000000000000000000000000001", 500),
- object("0000000000000000000000000000000000000003", 1500),
- object("0000000000000000000000000000000000000005", 3000));
- PackIndexMerger.PackIndexPeekIterator it = new PackIndexMerger.PackIndexPeekIterator(0, index1);
- it.next();
- assertEquals("0000000000000000000000000000000000000001", it.peek().name());
- assertEquals("0000000000000000000000000000000000000001", it.peek().name());
- it.next();
- assertEquals("0000000000000000000000000000000000000003", it.peek().name());
- assertEquals("0000000000000000000000000000000000000003", it.peek().name());
- it.next();
- assertEquals("0000000000000000000000000000000000000005", it.peek().name());
- assertEquals("0000000000000000000000000000000000000005", it.peek().name());
- it.next();
- assertNull(it.peek());
- assertNull(it.peek());
- }
-
- @Test
- public void empty() {
- PackIndex index1 = indexOf();
- PackIndexMerger.PackIndexPeekIterator it = new PackIndexMerger.PackIndexPeekIterator(0, index1);
- assertNull(it.next());
- assertNull(it.peek());
- }
-
- private static PackIndex indexOf(FakeIndexFactory.IndexObject... objs) {
- return FakeIndexFactory.indexOf(Arrays.asList(objs));
- }
-
- private static FakeIndexFactory.IndexObject object(String name, long offset) {
- return new FakeIndexFactory.IndexObject(name, offset);
- }
-}
\ No newline at end of file
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsMidxWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsMidxWriter.java
index 654bc30..0cdb304 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsMidxWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsMidxWriter.java
@@ -17,7 +17,6 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
-import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -26,8 +25,8 @@
import org.eclipse.jgit.annotations.Nullable;
import org.eclipse.jgit.internal.storage.file.PackBitmapIndexBuilder;
-import org.eclipse.jgit.internal.storage.file.PackIndex;
import org.eclipse.jgit.internal.storage.midx.MultiPackIndexWriter;
+import org.eclipse.jgit.internal.storage.midx.PackIndexMerger;
import org.eclipse.jgit.internal.storage.pack.PackBitmapCalculator;
import org.eclipse.jgit.internal.storage.pack.PackBitmapIndexWriter;
import org.eclipse.jgit.lib.Constants;
@@ -92,11 +91,10 @@ public static DfsPackDescription writeMidx(ProgressMonitor pm,
DfsObjDatabase objdb, List<DfsPackFile> packs,
@Nullable DfsPackDescription base, PackConfig packConfig)
throws IOException {
- LinkedHashMap<String, PackIndex> inputs = new LinkedHashMap<>(
- packs.size());
+ PackIndexMerger.Builder dataBuilder = PackIndexMerger.builder();
try (DfsReader ctx = objdb.newReader()) {
for (DfsPackFile pack : packs) {
- inputs.put(pack.getPackDescription().getPackName(),
+ dataBuilder.addPack(pack.getPackDescription().getPackName(),
pack.getPackIndex(ctx));
}
}
@@ -105,7 +103,8 @@ public static DfsPackDescription writeMidx(ProgressMonitor pm,
try (DfsOutputStream out = objdb.writeFile(midxPackDesc,
MULTI_PACK_INDEX)) {
MultiPackIndexWriter w = new MultiPackIndexWriter();
- MultiPackIndexWriter.Result result = w.write(pm, out, inputs);
+ MultiPackIndexWriter.Result result = w.write(pm, out,
+ dataBuilder.build());
midxPackDesc.addFileExt(MULTI_PACK_INDEX);
midxPackDesc.setFileSize(MULTI_PACK_INDEX, result.bytesWritten());
midxPackDesc.setObjectCount(result.objectCount());
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFileMidx.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFileMidx.java
index 0582737..3c63db2 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFileMidx.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFileMidx.java
@@ -20,6 +20,7 @@
import org.eclipse.jgit.errors.StoredObjectRepresentationNotAvailableException;
import org.eclipse.jgit.internal.storage.file.PackIndex;
import org.eclipse.jgit.internal.storage.file.PackReverseIndex;
+import org.eclipse.jgit.internal.storage.midx.MultiPackIndex;
import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
import org.eclipse.jgit.internal.storage.pack.PackOutputStream;
import org.eclipse.jgit.lib.AbbreviatedObjectId;
@@ -152,6 +153,18 @@ protected int getObjectCount(DfsReader ctx) throws IOException {
*/
protected abstract byte[] getChecksum(DfsReader ctx) throws IOException;
+ /**
+ * Get a midx iterator over the contents of *this* midx, without the base.
+ *
+ * @param ctx
+ * a ready
+ * @return an iterator over the objects in this midx in sha1 order
+ * @throws IOException
+ * an error loading the underlying data
+ */
+ protected abstract MultiPackIndex.MidxIterator localIterator(DfsReader ctx)
+ throws IOException;
+
@Override
public final PackIndex getPackIndex(DfsReader ctx) {
return new MidxPackIndex(this, ctx);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFileMidxNPacks.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFileMidxNPacks.java
index bed7a53..f042f2e 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFileMidxNPacks.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFileMidxNPacks.java
@@ -52,8 +52,6 @@ public final class DfsPackFileMidxNPacks extends DfsPackFileMidx {
private static final int REF_POSITION = 0;
- private final List<DfsPackFile> packs;
-
// The required packs, in the order specified in the multipack index
// Initialized lazily, when the midx is loaded
private final DfsPackFile[] packsInIdOrder;
@@ -64,14 +62,27 @@ public final class DfsPackFileMidxNPacks extends DfsPackFileMidx {
private final VOffsetCalculatorNPacks offsetCalculator;
+ /**
+ * Create the DfsPackFileMidx instance for this midx with n packs
+ *
+ * @param cache
+ * dfs block cache
+ * @param desc
+ * description of the midx
+ * @param knownPacks
+ * known packs, to translate the pack names in coveredPacks into
+ * DfsPackFile instances. It must contain at least all packs
+ * covered by this midx.
+ * @param base
+ * base used by this midx.
+ */
DfsPackFileMidxNPacks(DfsBlockCache cache, DfsPackDescription desc,
- List<DfsPackFile> requiredPacks, @Nullable DfsPackFileMidx base) {
+ List<DfsPackFile> knownPacks, @Nullable DfsPackFileMidx base) {
super(cache, desc);
this.base = base;
- this.packs = requiredPacks;
String[] coveredPackNames = desc.getCoveredPacks().stream()
.map(DfsPackDescription::getPackName).toArray(String[]::new);
- packsInIdOrder = getPacksInMidxIdOrder(coveredPackNames);
+ packsInIdOrder = getPacksInMidxIdOrder(knownPacks, coveredPackNames);
offsetCalculator = VOffsetCalculatorNPacks.fromPacks(packsInIdOrder,
base != null ? base.getOffsetCalculator() : null);
this.length = offsetCalculator.getMaxOffset();
@@ -114,8 +125,9 @@ private static RefWithSize loadMultiPackIndex(DfsReader ctx,
private record RefWithSize(MultiPackIndex idx, long size) {
}
- private DfsPackFile[] getPacksInMidxIdOrder(String[] packNames) {
- Map<String, DfsPackFile> byName = packs.stream()
+ private DfsPackFile[] getPacksInMidxIdOrder(List<DfsPackFile> knownPacks,
+ String[] packNames) {
+ Map<String, DfsPackFile> byName = knownPacks.stream()
.collect(Collectors.toUnmodifiableMap(
p -> p.getPackDescription().getPackName(),
Function.identity()));
@@ -170,7 +182,7 @@ && getPackDescription().hasFileExt(BITMAP_INDEX)) {
List<DfsPackFile> fullyIncludedIn(DfsReader ctx,
BitmapIndex.BitmapBuilder need) throws IOException {
List<DfsPackFile> fullyIncluded = new ArrayList<>();
- for (DfsPackFile pack : packs) {
+ for (DfsPackFile pack : packsInIdOrder) {
List<DfsPackFile> includedPacks = pack.fullyIncludedIn(ctx, need);
if (!includedPacks.isEmpty()) {
fullyIncluded.addAll(includedPacks);
@@ -186,7 +198,7 @@ List<DfsPackFile> fullyIncludedIn(DfsReader ctx,
@Override
public CommitGraph getCommitGraph(DfsReader ctx) throws IOException {
- for (DfsPackFile pack : packs) {
+ for (DfsPackFile pack : packsInIdOrder) {
CommitGraph cg = pack.getCommitGraph(ctx);
if (cg != null) {
return cg;
@@ -242,6 +254,12 @@ protected int getObjectCount(DfsReader ctx) throws IOException {
return midx(ctx).getChecksum();
}
+ @Override
+ protected MultiPackIndex.MidxIterator localIterator(DfsReader ctx)
+ throws IOException {
+ return midx(ctx).iterator();
+ }
+
/**
* Packs indexed by this multipack index (base NOT included)
*
@@ -249,7 +267,7 @@ protected int getObjectCount(DfsReader ctx) throws IOException {
*/
@Override
public List<DfsPackFile> getCoveredPacks() {
- return packs;
+ return List.of(packsInIdOrder);
}
/**
@@ -344,7 +362,7 @@ void resolve(DfsReader ctx, Set<ObjectId> matches, AbbreviatedObjectId id,
@Override
void copyPackAsIs(PackOutputStream out, DfsReader ctx) throws IOException {
// Assumming the order of the packs does not really matter
- for (DfsPackFile pack : packs) {
+ for (DfsPackFile pack : packsInIdOrder) {
pack.copyPackAsIs(out, ctx);
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFileMidxSingle.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFileMidxSingle.java
index 71ff884..89dbf9d 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFileMidxSingle.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/dfs/DfsPackFileMidxSingle.java
@@ -25,6 +25,8 @@
import org.eclipse.jgit.internal.storage.file.PackBitmapIndex;
import org.eclipse.jgit.internal.storage.file.PackIndex;
import org.eclipse.jgit.internal.storage.file.PackReverseIndex;
+import org.eclipse.jgit.internal.storage.midx.MidxIterators;
+import org.eclipse.jgit.internal.storage.midx.MultiPackIndex;
import org.eclipse.jgit.internal.storage.midx.MultiPackIndex.PackOffset;
import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
import org.eclipse.jgit.internal.storage.pack.PackExt;
@@ -149,6 +151,14 @@ protected int getObjectCount(DfsReader ctx) throws IOException {
return checksum;
}
+ @Override
+ protected MultiPackIndex.MidxIterator localIterator(DfsReader ctx)
+ throws IOException {
+ String packName = pack.getPackDescription().getPackName();
+ PackIndex packIndex = pack.getPackIndex(ctx);
+ return MidxIterators.fromPackIndexIterator(packName, packIndex);
+ }
+
/**
* Packs indexed by this multipack index (base NOT included)
*
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MidxIterators.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MidxIterators.java
new file mode 100644
index 0000000..c30b9b8
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MidxIterators.java
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2026, Google LLC.
+ *
+ * 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.midx;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import org.eclipse.jgit.internal.storage.file.PackIndex;
+import org.eclipse.jgit.internal.storage.midx.MultiPackIndex.MidxIterator;
+import org.eclipse.jgit.internal.storage.midx.MultiPackIndex.MutableEntry;
+import org.eclipse.jgit.lib.MutableObjectId;
+
+/**
+ * Helpers for midx iterators
+ */
+public final class MidxIterators {
+
+ /**
+ * Wrap a PackIndex iterator so it looks like a midx iterator with a fixed
+ * packId
+ *
+ * @param packName
+ * pack name this iterator is going over
+ * @param idx
+ * a PackIndex
+ * @return a midx iterator that returns the objects of the pack index in the
+ * original iterator order
+ */
+ public static MidxIterator fromPackIndexIterator(String packName,
+ PackIndex idx) {
+ return new MidxIteratorOverPackIndex(packName, idx);
+ }
+
+ /**
+ * Merge results from multiple midx iterators.
+ * <p>
+ * This iterator can return duplicates (in ascending packId order). Wrap
+ * with {@link #dedup(MidxIterator)} to remove duplicates.
+ * <p>
+ * This iterator shifts the pack ids in iterator order. If the first
+ * iterator covers 3 packs, packIds of the second iterator are increased by
+ * 3.
+ *
+ * @param iterators
+ * midx iterators to combine
+ * @return a unique iterator that returns the union of the iterators in sha1
+ * order. The packIds of the entries are shi
+ */
+ public static MidxIterator join(List<MidxIterator> iterators) {
+ return new JoinMidxIterator(iterators);
+ }
+
+ /**
+ * Dedup consecutive duplicates from a midx iterator
+ *
+ * @param source
+ * a midx iterator emitting entries in sha1 order. In case of
+ * duplicates, the first one is returned and others skipped.
+ * @return iterator without duplicates.
+ */
+ public static MidxIterator dedup(MidxIterator source) {
+ return new DedupMidxIterator(source);
+ }
+
+ private MidxIterators() {
+ }
+
+ /**
+ * Convert a PackIndex iterator into a MidxIterator
+ */
+ private static class MidxIteratorOverPackIndex implements MidxIterator {
+
+ private final List<String> packNames;
+
+ private final PackIndex idx;
+
+ private Iterator<PackIndex.MutableEntry> idxIt;
+
+ private boolean peeked;
+
+ private final MutableEntry entry = new MutableEntry();
+
+ MidxIteratorOverPackIndex(String packName,
+ PackIndex idx) {
+ this.packNames = List.of(packName);
+ this.idx = idx;
+ this.idxIt = idx.iterator();
+ }
+
+ @Override
+ public MutableEntry peek() {
+ if (peeked) {
+ return entry;
+ }
+
+ peeked = true;
+ readNext();
+ return entry;
+ }
+
+ @Override
+ public List<String> getPackNames() {
+ return packNames;
+ }
+
+ @Override
+ public boolean hasNext() {
+ if (peeked) {
+ return true;
+ }
+ return idxIt.hasNext();
+ }
+
+ @Override
+ public MutableEntry next() {
+ if (peeked) {
+ peeked = false;
+ return entry;
+ }
+ readNext();
+ return entry;
+ }
+
+ private void readNext() {
+ PackIndex.MutableEntry idx = idxIt.next();
+ idx.copyOidTo(entry.oid);
+ entry.packOffset.setValues(0, idx.getOffset());
+ }
+
+ @Override
+ public void reset() {
+ this.idxIt = idx.iterator();
+ this.entry.clear();
+ peeked = false;
+ }
+ }
+
+ private static class JoinMidxIterator implements MidxIterator {
+
+ private final List<String> packNames;
+
+ private final List<MidxIterator> indexIterators;
+
+ private final int[] packCountAgg;
+
+ private final MutableEntry local = new MutableEntry();
+
+ public JoinMidxIterator(List<MidxIterator> indexIterators) {
+ this.indexIterators = indexIterators;
+ packCountAgg = new int[indexIterators.size()];
+ for (int i = 1; i < indexIterators.size(); i++) {
+ packCountAgg[i] = indexIterators.get(i - 1).getPackNames()
+ .size() + packCountAgg[i - 1];
+ }
+ packNames = indexIterators.stream().map(MidxIterator::getPackNames)
+ .flatMap(List::stream).toList();
+ }
+
+ @Override
+ public MutableEntry peek() {
+ int p = best();
+ MidxIterator it = indexIterators.get(p);
+ return shiftPackId(it.peek(), packCountAgg[p]);
+ }
+
+ @Override
+ public List<String> getPackNames() {
+ return packNames;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return indexIterators.stream().anyMatch(Iterator::hasNext);
+ }
+
+ @Override
+ public MutableEntry next() {
+ int p = best();
+ MidxIterator it = indexIterators.get(p);
+ return shiftPackId(it.next(), packCountAgg[p]);
+ }
+
+ private MutableEntry shiftPackId(MutableEntry entry, int shift) {
+ local.fill(entry, shift);
+ return local;
+ }
+
+ private int best() {
+ int winnerPos = -1;
+ int winnerPackShift = 0;
+ MidxIterator winner = null;
+ for (int index = 0; index < indexIterators.size(); index++) {
+ MidxIterator current = indexIterators.get(index);
+ if (!current.hasNext()) {
+ continue;
+ }
+ if (winner == null
+ || compareEntries(current.peek(), packCountAgg[index],
+ winner.peek(), winnerPackShift) < 0) {
+ winner = current;
+ winnerPos = index;
+ winnerPackShift = packCountAgg[winnerPos];
+ }
+ }
+
+ if (winner == null) {
+ throw new NoSuchElementException();
+ }
+
+ return winnerPos;
+ }
+
+ private static int compareEntries(MutableEntry a, int aPackShift,
+ MutableEntry b, int bPackShift) {
+ int cmp = a.oid.compareTo(b.oid);
+ if (cmp != 0) {
+ return cmp;
+ }
+
+ return Integer.compare(a.getPackId() + aPackShift,
+ b.getPackId() + bPackShift);
+ }
+
+ @Override
+ public void reset() {
+ indexIterators.stream().forEach(MidxIterator::reset);
+ local.clear();
+ }
+ }
+
+ private static class DedupMidxIterator implements MidxIterator {
+ private final MidxIterator src;
+
+ private final MutableObjectId lastOid = new MutableObjectId();
+
+ private MutableEntry next;
+
+ private final MutableEntry copy = new MutableEntry();
+
+ /**
+ * Iterator over sorted by sha1 entries that removes duplicates choosing
+ * the first one found.
+ *
+ * @param src
+ * iterator in sha1 order
+ */
+ DedupMidxIterator(MidxIterator src) {
+ this.src = src;
+ readNext();
+ }
+
+ @Override
+ public MutableEntry peek() {
+ return next;
+ }
+
+ @Override
+ public List<String> getPackNames() {
+ return src.getPackNames();
+ }
+
+ @Override
+ public boolean hasNext() {
+ return next != null;
+ }
+
+ @Override
+ public MutableEntry next() {
+ copy.fill(next, 0);
+ readNext();
+ return copy;
+ }
+
+ private void readNext() {
+ while (true) {
+ if (!src.hasNext()) {
+ next = null;
+ return;
+ }
+
+ next = src.next();
+ if (!lastOid.equals(next.oid)) {
+ lastOid.fromObjectId(next.oid);
+ return;
+ }
+ }
+ }
+
+ @Override
+ public void reset() {
+ lastOid.clear();
+ src.reset();
+ readNext();
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndex.java
index 1721cb6..b209aa9 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndex.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndex.java
@@ -162,6 +162,11 @@ interface MidxIterator extends Iterator<MutableEntry> {
* @return pack names
*/
List<String> getPackNames();
+
+ /**
+ * Restart the iteration from the beginning
+ */
+ void reset();
}
/**
@@ -235,21 +240,11 @@ public String toString() {
* <p>
* Mutable so the iterator can reuse the instance for performance.
*/
- class MutableEntry implements Comparable<MutableEntry> {
+ class MutableEntry {
protected final MutableObjectId oid = new MutableObjectId();
protected final PackOffset packOffset = new PackOffset();
- @Override
- public int compareTo(MutableEntry mutableEntry) {
- int cmp = oid.compareTo(mutableEntry.oid);
- if (cmp != 0) {
- return cmp;
- }
-
- return packOffset.getPackId() - mutableEntry.packOffset.getPackId();
- }
-
/**
* Copy data from other into this instance, adding the shift to the
* packId
@@ -278,6 +273,11 @@ public long getOffset() {
return packOffset.getOffset();
}
+ public void clear() {
+ oid.clear();
+ packOffset.setValues(0, 0);
+ }
+
@Override
public String toString() {
return String.format("%s,%s", oid.name(), packOffset);
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndexV1.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndexV1.java
index ac6f00c..cfa5c33 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndexV1.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndexV1.java
@@ -392,6 +392,11 @@ public MutableEntry peek() {
public List<String> getPackNames() {
return Arrays.asList(midx.getPackNames());
}
+
+ @Override
+ public void reset() {
+ position = 0;
+ }
}
private static class ReverseIndex {
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndexWriter.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndexWriter.java
index 4d2e580..7c69b78 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndexWriter.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/MultiPackIndexWriter.java
@@ -35,9 +35,8 @@
import java.util.Map;
import org.eclipse.jgit.internal.JGitText;
-import org.eclipse.jgit.internal.storage.file.PackIndex;
import org.eclipse.jgit.internal.storage.io.CancellableDigestOutputStream;
-import org.eclipse.jgit.internal.storage.midx.PackIndexMerger.MidxMutableEntry;
+import org.eclipse.jgit.internal.storage.midx.MultiPackIndex.MutableEntry;
import org.eclipse.jgit.lib.ProgressMonitor;
import org.eclipse.jgit.util.NB;
@@ -78,17 +77,15 @@ public record Result(long bytesWritten, int objectCount,
* progress monitor
* @param outputStream
* stream to write the multipack index file
- * @param inputs
- * pairs of name and index for each pack to include in the
- * multipack index.
+ * @param data
+ * a pack index merger with the data sources (in order) for this
+ * midx
* @return data about the write (e.g. bytes written)
* @throws IOException
* Error writing to the stream
*/
public Result write(ProgressMonitor monitor, OutputStream outputStream,
- Map<String, PackIndex> inputs) throws IOException {
- PackIndexMerger data = new PackIndexMerger(inputs);
-
+ PackIndexMerger data) throws IOException {
// List of chunks in the order they need to be written
List<ChunkHeader> chunkHeaders = createChunkHeaders(data);
long expectedSize = calculateExpectedSize(chunkHeaders);
@@ -223,9 +220,9 @@ private void writeChunkLookup(CancellableDigestOutputStream out,
private void writeFanoutTable(WriteContext ctx) throws IOException {
byte[] tmp = new byte[4];
int[] fanout = new int[256];
- Iterator<MidxMutableEntry> iterator = ctx.data.bySha1Iterator();
+ Iterator<MutableEntry> iterator = ctx.data.bySha1Iterator();
while (iterator.hasNext()) {
- MidxMutableEntry e = iterator.next();
+ MutableEntry e = iterator.next();
fanout[e.getObjectId().getFirstByte() & 0xff]++;
}
for (int i = 1; i < fanout.length; i++) {
@@ -250,9 +247,9 @@ private void writeFanoutTable(WriteContext ctx) throws IOException {
private void writeOidLookUp(WriteContext ctx) throws IOException {
byte[] tmp = new byte[OBJECT_ID_LENGTH];
- Iterator<MidxMutableEntry> iterator = ctx.data.bySha1Iterator();
+ Iterator<MutableEntry> iterator = ctx.data.bySha1Iterator();
while (iterator.hasNext()) {
- MidxMutableEntry e = iterator.next();
+ MutableEntry e = iterator.next();
e.getObjectId().copyRawTo(tmp, 0);
ctx.out.write(tmp, 0, OBJECT_ID_LENGTH);
}
@@ -272,9 +269,9 @@ private void writeOidLookUp(WriteContext ctx) throws IOException {
*/
private void writeObjectOffsets(WriteContext ctx) throws IOException {
byte[] entry = new byte[8];
- Iterator<MidxMutableEntry> iterator = ctx.data.bySha1Iterator();
+ Iterator<MutableEntry> iterator = ctx.data.bySha1Iterator();
while (iterator.hasNext()) {
- MidxMutableEntry e = iterator.next();
+ MutableEntry e = iterator.next();
NB.encodeInt32(entry, 0, e.getPackId());
if (!ctx.data.needsLargeOffsetsChunk()
|| fitsIn31bits(e.getOffset())) {
@@ -305,10 +302,10 @@ private void writeRidx(WriteContext ctx) throws IOException {
// memory. We could also iterate reverse indexes looking up
// their position in the midx (and discarding if the pack doesn't
// match).
- Iterator<MidxMutableEntry> iterator = ctx.data.bySha1Iterator();
+ Iterator<MutableEntry> iterator = ctx.data.bySha1Iterator();
int midxPosition = 0;
while (iterator.hasNext()) {
- MidxMutableEntry e = iterator.next();
+ MutableEntry e = iterator.next();
OffsetPosition op = new OffsetPosition(e.getOffset(), midxPosition);
midxPosition++;
packOffsets.computeIfAbsent(e.getPackId(), k -> new ArrayList<>())
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/PackIndexMerger.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/PackIndexMerger.java
index 4a296ee..3436446 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/PackIndexMerger.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/midx/PackIndexMerger.java
@@ -10,13 +10,11 @@
package org.eclipse.jgit.internal.storage.midx;
import java.util.ArrayList;
-import java.util.Iterator;
import java.util.List;
-import java.util.Map;
-import java.util.NoSuchElementException;
import org.eclipse.jgit.internal.storage.file.PackIndex;
-import org.eclipse.jgit.lib.AnyObjectId;
+import org.eclipse.jgit.internal.storage.midx.MultiPackIndex.MidxIterator;
+import org.eclipse.jgit.internal.storage.midx.MultiPackIndex.MutableEntry;
import org.eclipse.jgit.lib.MutableObjectId;
/**
@@ -32,59 +30,13 @@
* entries. The stats of the combined index are calculated in an iteration at
* construction time.
*/
-class PackIndexMerger {
+public class PackIndexMerger {
private static final int LIMIT_31_BITS = (1 << 31) - 1;
private static final long LIMIT_32_BITS = (1L << 32) - 1;
- /**
- * Object returned by the iterator.
- * <p>
- * The iterator returns (on each next()) the same instance with different
- * values, to avoid allocating many short-lived objects. Callers should not
- * keep a reference to that returned value.
- */
- static class MidxMutableEntry {
- // The object id
- private final MutableObjectId oid = new MutableObjectId();
-
- // Position of the pack in the ordered list of pack in this merger
- private int packId;
-
- // Offset in its pack
- private long offset;
-
- public AnyObjectId getObjectId() {
- return oid;
- }
-
- public int getPackId() {
- return packId;
- }
-
- public long getOffset() {
- return offset;
- }
-
- /**
- * Copy values from another mutable entry
- *
- * @param packId
- * packId
- * @param other
- * another mutable entry
- */
- private void fill(int packId, PackIndex.MutableEntry other) {
- other.copyOidTo(oid);
- this.packId = packId;
- this.offset = other.getOffset();
- }
- }
-
- private final List<String> packNames;
-
- private final List<PackIndex> indexes;
+ private final MidxIterator midxIterator;
private final boolean needsLargeOffsetsChunk;
@@ -94,43 +46,99 @@ private void fill(int packId, PackIndex.MutableEntry other) {
private final int[] objectsPerPack;
- /**
- * Build a common view of these pack indexes
- * <p>
- * Order matters: in case of duplicates, the first pack with the object wins
- *
- * @param packs
- * map of pack names to indexes, ordered.
- */
- PackIndexMerger(Map<String, PackIndex> packs) {
- this.packNames = packs.keySet().stream().toList();
- this.indexes = packs.values().stream().toList();
+ private final List<String> packnames;
- objectsPerPack = new int[packNames.size()];
- // Iterate for duplicates
+ /**
+ * Builder collecting the inputs for the merger.
+ * <p>
+ * Order matters. Packs will appear in the midx in the order they are added.
+ */
+ public static class Builder {
+
+ private final List<MidxIterator> packIndexes = new ArrayList<>();
+
+ /**
+ * Add a regular pack to the midx
+ *
+ * @param name
+ * name of the pack
+ * @param idx
+ * primary index of the pack
+ * @return this builder
+ */
+ public Builder addPack(String name, PackIndex idx) {
+ packIndexes.add(MidxIterators.fromPackIndexIterator(name, idx));
+ return this;
+ }
+
+ /**
+ * Add data from this midx iterator to the merge
+ * <p>
+ * Packs are kept in the order of the iterator.
+ *
+ * @param midx
+ * a midx iterator
+ * @return this builder
+ */
+ public Builder addMidx(MidxIterator midx) {
+ packIndexes.add(midx);
+ return this;
+ }
+
+ /**
+ * Build the merger instance
+ *
+ * @return a merger instance
+ */
+ public PackIndexMerger build() {
+ return new PackIndexMerger(
+ MidxIterators.dedup(MidxIterators.join(packIndexes)));
+ }
+ }
+
+ /**
+ * Create a builder
+ *
+ * @return an empty builder
+ */
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ /**
+ * A common view of the input pack indexes
+ *
+ * @param midxIterator
+ * MidxIterator built by deduping union of all pack indexes
+ */
+ private PackIndexMerger(MidxIterator midxIterator) {
+ this.midxIterator = midxIterator;
+ this.packnames = midxIterator.getPackNames();
+
+ objectsPerPack = new int[packnames.size()];
+ // Iterate for duplicates and counts that we need to build the chunk
+ // headers.
int objectCount = 0;
boolean hasLargeOffsets = false;
int over31bits = 0;
MutableObjectId lastSeen = new MutableObjectId();
- MultiIndexIterator it = new MultiIndexIterator(indexes);
- while (it.hasNext()) {
- MidxMutableEntry entry = it.next();
- if (lastSeen.equals(entry.oid)) {
- continue;
- }
+ while (midxIterator.hasNext()) {
+ MutableEntry entry = midxIterator.next();
// If there is at least one offset value larger than 2^32-1, then
// the large offset chunk must exist, and offsets larger than
// 2^31-1 must be stored in it instead
- if (entry.offset > LIMIT_32_BITS) {
+ if (entry.getOffset() > LIMIT_32_BITS) {
hasLargeOffsets = true;
}
- if (entry.offset > LIMIT_31_BITS) {
+ if (entry.getOffset() > LIMIT_31_BITS) {
over31bits++;
}
lastSeen.fromObjectId(entry.oid);
objectCount++;
- objectsPerPack[entry.packId]++;
+ // TODO(ifrade): we can calculate the fanout table already here.
+ // It saves an iteration over all objects for only 1Kb of memory
+ objectsPerPack[entry.getPackId()]++;
}
uniqueObjectCount = objectCount;
offsetsOver31BitsCount = over31bits;
@@ -168,7 +176,7 @@ int getOffsetsOver31BitsCount() {
}
/**
- * Number of objects selected for the midx per packid
+ * Number of objects selected for the midx per pack id
*
* @return array where position n contains the amount of objects selected
* for pack id n
@@ -187,7 +195,7 @@ int getOffsetsOver31BitsCount() {
* @return List of pack names, in the order used by the merge.
*/
List<String> getPackNames() {
- return packNames;
+ return packnames;
}
/**
@@ -196,160 +204,22 @@ List<String> getPackNames() {
* @return count of packs merged
*/
int getPackCount() {
- return packNames.size();
+ return packnames.size();
}
/**
* Iterator over the merged indexes in sha1 order without duplicates
* <p>
+ * This always returns the same iterator resetted. We don't support two
+ * iterators over this merged data.
+ * <p>
* The returned entry in the iterator is mutable, callers should NOT keep a
* reference to it.
*
* @return an iterator in sha1 order without duplicates.
*/
- Iterator<MidxMutableEntry> bySha1Iterator() {
- return new DedupMultiIndexIterator(new MultiIndexIterator(indexes),
- getUniqueObjectCount());
- }
-
- /**
- * For testing. Iterate all entries, not skipping duplicates (stable order)
- *
- * @return an iterator of all objects in sha1 order, including duplicates.
- */
- Iterator<MidxMutableEntry> rawIterator() {
- return new MultiIndexIterator(indexes);
- }
-
- /**
- * Iterator over n-indexes in ObjectId order.
- * <p>
- * It returns duplicates if the same object id is in different indexes. Wrap
- * it with {@link DedupMultiIndexIterator (Iterator, int)} to avoid
- * duplicates.
- */
- private static final class MultiIndexIterator
- implements Iterator<MidxMutableEntry> {
-
- private final List<PackIndexPeekIterator> indexIterators;
-
- private final MidxMutableEntry mutableEntry = new MidxMutableEntry();
-
- MultiIndexIterator(List<PackIndex> indexes) {
- this.indexIterators = new ArrayList<>(indexes.size());
- for (int i = 0; i < indexes.size(); i++) {
- PackIndexPeekIterator it = new PackIndexPeekIterator(i,
- indexes.get(i));
- // Position in the first element
- if (it.next() != null) {
- indexIterators.add(it);
- }
- }
- }
-
- @Override
- public boolean hasNext() {
- return !indexIterators.isEmpty();
- }
-
- @Override
- public MidxMutableEntry next() {
- PackIndexPeekIterator winner = null;
- for (int index = 0; index < indexIterators.size(); index++) {
- PackIndexPeekIterator current = indexIterators.get(index);
- if (winner == null
- || current.peek().compareBySha1To(winner.peek()) < 0) {
- winner = current;
- }
- }
-
- if (winner == null) {
- throw new NoSuchElementException();
- }
-
- mutableEntry.fill(winner.getPackId(), winner.peek());
- if (winner.next() == null) {
- indexIterators.remove(winner);
- }
- return mutableEntry;
- }
- }
-
- private static class DedupMultiIndexIterator
- implements Iterator<MidxMutableEntry> {
- private final MultiIndexIterator src;
-
- private int remaining;
-
- private final MutableObjectId lastOid = new MutableObjectId();
-
- DedupMultiIndexIterator(MultiIndexIterator src, int totalCount) {
- this.src = src;
- this.remaining = totalCount;
- }
-
- @Override
- public boolean hasNext() {
- return remaining > 0;
- }
-
- @Override
- public MidxMutableEntry next() {
- MidxMutableEntry next = src.next();
- while (next != null && lastOid.equals(next.oid)) {
- next = src.next();
- }
-
- if (next == null) {
- throw new NoSuchElementException();
- }
-
- lastOid.fromObjectId(next.oid);
- remaining--;
- return next;
- }
- }
-
- /**
- * Convenience around the PackIndex iterator to read the current value
- * multiple times without consuming it.
- * <p>
- * This is used to merge indexes in the multipack index, where we need to
- * compare the current value between indexes multiple times to find the
- * next.
- * <p>
- * We could also implement this keeping the position (int) and
- * MutableEntry#getObjectId, but that would create an ObjectId per entry.
- * This implementation reuses the MutableEntry and avoid instantiations.
- */
- // Visible for testing
- static class PackIndexPeekIterator {
- private final Iterator<PackIndex.MutableEntry> it;
-
- private final int packId;
-
- PackIndex.MutableEntry current;
-
- PackIndexPeekIterator(int packId, PackIndex index) {
- it = index.iterator();
- this.packId = packId;
- }
-
- PackIndex.MutableEntry next() {
- if (it.hasNext()) {
- current = it.next();
- } else {
- current = null;
- }
- return current;
- }
-
- PackIndex.MutableEntry peek() {
- return current;
- }
-
- int getPackId() {
- return packId;
- }
+ MidxIterator bySha1Iterator() {
+ midxIterator.reset();
+ return midxIterator;
}
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
index ba7573a..8e604ba 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java
@@ -623,7 +623,7 @@ private Set<RevCommit> getCommitsMergedInto(RevCommit needle, Collection<RevComm
markStart(c);
boolean commitFound = false;
RevCommit next;
- while ((next = next()) != null) {
+ while ((next = next()) != null && !monitor.isCancelled()) {
if (next.getGeneration() < cutoff) {
markUninteresting(next);
uninteresting.add(next);