diff --git a/org.eclipse.jgit.benchmarks/BUILD b/org.eclipse.jgit.benchmarks/BUILD index 7e331b101..ecd268c4b 100644 --- a/org.eclipse.jgit.benchmarks/BUILD +++ b/org.eclipse.jgit.benchmarks/BUILD @@ -8,6 +8,8 @@ jmh_java_benchmarks( name = "benchmarks", srcs = SRCS, deps = [ + "//lib:javaewah", + "//lib:slf4j-api", "//org.eclipse.jgit:jgit", ], ) diff --git a/org.eclipse.jgit.packaging/pom.xml b/org.eclipse.jgit.packaging/pom.xml index b65066e39..385c54ac2 100644 --- a/org.eclipse.jgit.packaging/pom.xml +++ b/org.eclipse.jgit.packaging/pom.xml @@ -294,7 +294,7 @@ org.eclipse.cbi.maven.plugins eclipse-jarsigner-plugin - 1.3.1 + 1.3.2 org.codehaus.mojo diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileReftableTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileReftableTest.java index 15c9109ca..32342e356 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileReftableTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/file/FileReftableTest.java @@ -25,7 +25,9 @@ import static org.junit.Assert.fail; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.OutputStream; import java.security.SecureRandom; import java.util.ArrayList; import java.util.Collection; @@ -133,20 +135,21 @@ public void testConvert() throws Exception { assertTrue(db.getRefDatabase().hasFastTipsWithSha1()); } + @Test - public void testConvertToRefdir() throws Exception { + public void testConvertBrokenObjectId() throws Exception { db.convertToPackedRefs(false, false); - assertTrue(db.getRefDatabase() instanceof RefDirectory); - Ref h = db.exactRef("HEAD"); - assertTrue(h.isSymbolic()); - assertEquals("refs/heads/master", h.getTarget().getName()); + new File(db.getDirectory(), "refs/heads").mkdirs(); - Ref b = db.exactRef("refs/heads/b"); - assertFalse(b.isSymbolic()); - assertTrue(b.isPeeled()); - assertEquals(bCommit, b.getObjectId().name()); + String invalidId = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"; + File headFile = new File(db.getDirectory(), "refs/heads/broken"); + try (OutputStream os = new FileOutputStream(headFile)) { + os.write(Constants.encodeASCII(invalidId + "\n")); + } - assertFalse(db.getRefDatabase().hasFastTipsWithSha1()); + Ref r = db.exactRef("refs/heads/broken"); + assertNotNull(r); + db.convertToReftable(true, false); } @Test diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/ReftableTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/ReftableTest.java index 0d739b927..ea0d92acf 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/ReftableTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/internal/storage/reftable/ReftableTest.java @@ -941,6 +941,12 @@ public void logScan() throws IOException { } assertFalse(lc.next()); } + + for (Ref exp : refs) { + try (LogCursor lc = t.seekLog(exp.getName())) { + assertTrue("has " + exp.getName(), lc.next()); + } + } } @Test diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkMergedIntoTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkMergedIntoTest.java index 2f16aa49e..2754bd36a 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkMergedIntoTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkMergedIntoTest.java @@ -99,7 +99,7 @@ public void testIsMergedIntoAny() throws Exception { createBranch(commit(commit(a)), b); createBranch(commit(commit(i)), c); - assertTrue( rw.isMergedIntoAny(a, getRefs())); + assertTrue(rw.isMergedIntoAny(a, getRefs())); } @Test @@ -125,4 +125,23 @@ public void testIsMergedIntoAll() throws Exception { assertTrue(rw.isMergedIntoAll(a, getRefs())); } + + @Test + public void testMergeIntoAnnotatedTag() throws Exception { + /* + * a + * | + * b + * / \ + * c v1 (annotated tag) + */ + String c = "refs/heads/c"; + String v1 = "refs/tags/v1"; + final RevCommit a = commit(); + final RevCommit b = commit(a); + createBranch(commit(b), c); + createBranch(tag("v1", b), v1); + + assertTrue(rw.isMergedIntoAll(a, getRefs())); + } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkUtilsReachableTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkUtilsReachableTest.java index 200cb6a4f..0a045c917 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkUtilsReachableTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/RevWalkUtilsReachableTest.java @@ -82,20 +82,33 @@ public void findBranchesReachableManyTimes() throws Exception { * a b * | | * c d + * | \ + * f e + * | / + * g */ RevCommit a = commit(); RevCommit b = commit(); RevCommit c = commit(a); RevCommit d = commit(b); + RevCommit f = commit(d); + RevCommit e = commit(d); + RevCommit g = commit(f, e); Ref branchA = branch("a", a); Ref branchB = branch("b", b); Ref branchC = branch("c", c); Ref branchD = branch("d", d); + Ref branchE = branch("e", e); + Ref branchF = branch("f", f); + Ref branchG = branch("g", g); assertContains(a, asList(branchA, branchC)); - assertContains(b, asList(branchB, branchD)); + assertContains(b, asList(branchB, branchD, branchE, branchF, branchG)); assertContains(c, asList(branchC)); - assertContains(d, asList(branchD)); + assertContains(d, asList(branchD, branchE, branchF, branchG)); + assertContains(e, asList(branchE, branchG)); + assertContains(f, asList(branchF, branchG)); + assertContains(g, asList(branchG)); } private Ref branch(String name, RevCommit dst) throws Exception { diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackLsRefsFileRepositoryTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackLsRefsFileRepositoryTest.java new file mode 100644 index 000000000..7d5fc6101 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/transport/UploadPackLsRefsFileRepositoryTest.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2021, Saša Živkov 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.transport; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Objects; +import java.util.function.Consumer; + +import org.eclipse.jgit.internal.storage.file.FileRepository; +import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase; +import org.eclipse.jgit.junit.TestRepository; +import org.eclipse.jgit.lib.Sets; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevTag; +import org.junit.Before; +import org.junit.Test; + +// TODO: refactor UploadPackTest to run against both DfsRepository and FileRepository +public class UploadPackLsRefsFileRepositoryTest + extends LocalDiskRepositoryTestCase { + + private FileRepository server; + + private TestRepository remote; + + @Before + @Override + public void setUp() throws Exception { + super.setUp(); + server = createWorkRepository(); + remote = new TestRepository<>(server); + } + + @Test + public void testV2LsRefsPeel() throws Exception { + RevCommit tip = remote.commit().message("message").create(); + remote.update("master", tip); + server.updateRef("HEAD").link("refs/heads/master"); + RevTag tag = remote.tag("tag", tip); + remote.update("refs/tags/tag", tag); + + ByteArrayInputStream recvStream = uploadPackV2("command=ls-refs\n", + PacketLineIn.delimiter(), "peel", PacketLineIn.end()); + PacketLineIn pckIn = new PacketLineIn(recvStream); + + assertThat(pckIn.readString(), + is(tip.toObjectId().getName() + " HEAD")); + assertThat(pckIn.readString(), + is(tip.toObjectId().getName() + " refs/heads/master")); + assertThat(pckIn.readString(), is(tag.toObjectId().getName() + + " refs/tags/tag peeled:" + tip.toObjectId().getName())); + assertTrue(PacketLineIn.isEnd(pckIn.readString())); + } + + private ByteArrayInputStream uploadPackV2(String... inputLines) + throws Exception { + return uploadPackV2(null, inputLines); + } + + private ByteArrayInputStream uploadPackV2( + Consumer postConstructionSetup, String... inputLines) + throws Exception { + ByteArrayInputStream recvStream = uploadPackV2Setup( + postConstructionSetup, inputLines); + PacketLineIn pckIn = new PacketLineIn(recvStream); + + // drain capabilities + while (!PacketLineIn.isEnd(pckIn.readString())) { + // do nothing + } + return recvStream; + } + + private ByteArrayInputStream uploadPackV2Setup( + Consumer postConstructionSetup, String... inputLines) + throws Exception { + + ByteArrayInputStream send = linesAsInputStream(inputLines); + + server.getConfig().setString("protocol", null, "version", "2"); + UploadPack up = new UploadPack(server); + if (postConstructionSetup != null) { + postConstructionSetup.accept(up); + } + up.setExtraParameters(Sets.of("version=2")); + + ByteArrayOutputStream recv = new ByteArrayOutputStream(); + up.upload(send, recv, null); + + return new ByteArrayInputStream(recv.toByteArray()); + } + + private static ByteArrayInputStream linesAsInputStream(String... inputLines) + throws IOException { + try (ByteArrayOutputStream send = new ByteArrayOutputStream()) { + PacketLineOut pckOut = new PacketLineOut(send); + for (String line : inputLines) { + Objects.requireNonNull(line); + if (PacketLineIn.isEnd(line)) { + pckOut.end(); + } else if (PacketLineIn.isDelimiter(line)) { + pckOut.writeDelim(); + } else { + pckOut.writeString(line); + } + } + return new ByteArrayInputStream(send.toByteArray()); + } + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableDatabase.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableDatabase.java index a80fa837b..e9e17c0ec 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableDatabase.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/FileReftableDatabase.java @@ -28,6 +28,7 @@ import java.util.stream.Collectors; import org.eclipse.jgit.annotations.NonNull; +import org.eclipse.jgit.errors.MissingObjectException; import org.eclipse.jgit.events.RefsChangedEvent; import org.eclipse.jgit.internal.storage.reftable.MergedReftable; import org.eclipse.jgit.internal.storage.reftable.ReftableBatchRefUpdate; @@ -592,15 +593,20 @@ private static Ref refForWrite(RevWalk rw, Ref r) throws IOException { r.getTarget().getName(), null)); } ObjectId newId = r.getObjectId(); - RevObject obj = rw.parseAny(newId); RevObject peel = null; - if (obj instanceof RevTag) { - peel = rw.peel(obj); + try { + RevObject obj = rw.parseAny(newId); + if (obj instanceof RevTag) { + peel = rw.peel(obj); + } + } catch (MissingObjectException e) { + /* ignore this error and copy the dangling object ID into reftable too. */ } if (peel != null) { - return new ObjectIdRef.PeeledTag(PACKED, r.getName(), newId, - peel.copy()); - } + return new ObjectIdRef.PeeledTag(PACKED, r.getName(), newId, + peel.copy()); + } + return new ObjectIdRef.PeeledNonTag(PACKED, r.getName(), newId); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockReader.java index 009631289..d07713db8 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockReader.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/BlockReader.java @@ -59,7 +59,6 @@ class BlockReader { private byte blockType; private long endPosition; - private boolean truncated; private byte[] buf; private int bufLen; @@ -79,10 +78,6 @@ byte type() { return blockType; } - boolean truncated() { - return truncated; - } - long endPosition() { return endPosition; } @@ -298,16 +293,8 @@ private void parseBlockStart(BlockSource src, long pos, int fileBlockSize) // Log blocks must be inflated after the header. long deflatedSize = inflateBuf(src, pos, blockLen, fileBlockSize); endPosition = pos + 4 + deflatedSize; - } - if (bufLen < blockLen) { - if (blockType != INDEX_BLOCK_TYPE) { - throw invalidBlock(); - } - // Its OK during sequential scan for an index block to have been - // partially read and be truncated in-memory. This happens when - // the index block is larger than the file's blockSize. Caller - // will break out of its scan loop once it sees the blockType. - truncated = true; + } else if (bufLen < blockLen) { + readBlockIntoBuf(src, pos, blockLen); } else if (bufLen > blockLen) { bufLen = blockLen; } @@ -372,7 +359,7 @@ private void setupEmptyFileBlock() { } void verifyIndex() throws IOException { - if (blockType != INDEX_BLOCK_TYPE || truncated) { + if (blockType != INDEX_BLOCK_TYPE) { throw invalidBlock(); } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableReader.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableReader.java index 9e2ae9160..cabb2e184 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableReader.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/reftable/ReftableReader.java @@ -435,7 +435,7 @@ private BlockReader readBlock(long pos, long end) throws IOException { BlockReader b = new BlockReader(); b.readBlock(src, pos, sz); - if (b.type() == INDEX_BLOCK_TYPE && !b.truncated()) { + if (b.type() == INDEX_BLOCK_TYPE) { if (indexCache == null) { indexCache = new LongMap<>(); } 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 d5b3643af..6e29438d0 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevWalk.java @@ -528,6 +528,7 @@ private List getMergedInto(RevCommit needle, Collection haystacks, Enum returnStrategy, ProgressMonitor monitor) throws IOException { List result = new ArrayList<>(); List uninteresting = new ArrayList<>(); + List marked = new ArrayList<>(); RevFilter oldRF = filter; TreeFilter oldTF = treeFilter; try { @@ -540,22 +541,25 @@ private List getMergedInto(RevCommit needle, Collection haystacks, return result; } monitor.update(1); - RevObject o = parseAny(r.getObjectId()); + RevObject o = peel(parseAny(r.getObjectId())); if (!(o instanceof RevCommit)) { continue; } RevCommit c = (RevCommit) o; - resetRetain(RevFlag.UNINTERESTING); + reset(UNINTERESTING | TEMP_MARK); markStart(c); boolean commitFound = false; RevCommit next; while ((next = next()) != null) { - if (References.isSameObject(next, needle)) { + if (References.isSameObject(next, needle) + || (next.flags & TEMP_MARK) != 0) { result.add(r); if (returnStrategy == GetMergedIntoStrategy.RETURN_ON_FIRST_FOUND) { return result; } commitFound = true; + c.flags |= TEMP_MARK; + marked.add(c); break; } } @@ -571,6 +575,9 @@ private List getMergedInto(RevCommit needle, Collection haystacks, roots.addAll(uninteresting); filter = oldRF; treeFilter = oldTF; + for (RevCommit c : marked) { + c.flags &= ~TEMP_MARK; + } } return result; } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java index 07b348d0e..7daf2a4be 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/transport/UploadPack.java @@ -1092,6 +1092,7 @@ private void lsRefsV2(PacketLineOut pckOut) throws IOException { rawOut.stopBuffering(); PacketLineOutRefAdvertiser adv = new PacketLineOutRefAdvertiser(pckOut); + adv.init(db); adv.setUseProtocolV2(true); if (req.getPeel()) { adv.setDerefTags(true); diff --git a/pom.xml b/pom.xml index ef528c699..9522c5fee 100644 --- a/pom.xml +++ b/pom.xml @@ -332,7 +332,7 @@ org.eclipse.cbi.maven.plugins eclipse-jarsigner-plugin - 1.3.1 + 1.3.2 org.eclipse.tycho.extras