From c0d15c2abfef49dfaa0ae1071e099695a548f9c2 Mon Sep 17 00:00:00 2001 From: Ivan Frade Date: Mon, 20 Nov 2023 10:25:03 -0800 Subject: BitmapWalker: Remove BitmapWalkListener We can track bitmaps queries that found a bitmap directly in the BitmapIndex. Remove the listener. Change-Id: I5ad518a58b681bf327fee3ae5c5f6e4449d3da1f --- .../src/org/eclipse/jgit/revwalk/BitmapWalker.java | 84 +--------------------- 1 file changed, 2 insertions(+), 82 deletions(-) diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmapWalker.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmapWalker.java index c0e41d644a..7e65ffb8f6 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmapWalker.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/BitmapWalker.java @@ -47,76 +47,6 @@ public final class BitmapWalker { private Bitmap prevBitmap; - /** - * Report commits found during the walk - * - * @since 6.8 - */ - public interface BitmapWalkListener { - /** - * The commit was already visited or is reachable from a visited commit - * - * @param oid - * objectId of the commit already visited directly or - * indirectly - */ - void onCommitSeen(ObjectId oid); - - /** - * The commit has a bitmap in the bitmap index - * - * @param oid - * objectId of the commit with a bitmap in the bitmap index - */ - void onCommitWithBitmap(ObjectId oid); - - /** - * The commit doesn't have bitmap - * - * @param oid - * objectId of the commit without a bitmap in the bitmap - * index - */ - void onCommitWithoutBitmap(ObjectId oid); - - } - - /** - * Empty listener - * - * @since 6.8 - */ - public static final BitmapWalkListener NOOP_LISTENER = new BitmapWalkListener() { - @Override - public void onCommitSeen(ObjectId oid) { - // Nothing to do - } - - @Override - public void onCommitWithBitmap(ObjectId oid) { - // Nothing to do - } - - @Override - public void onCommitWithoutBitmap(ObjectId oid) { - // Nothing to do - } - }; - - private final BitmapWalkListener listener; - - /** - * Create a BitmapWalker. - * - * @param walker walker to use when traversing the object graph. - * @param bitmapIndex index to obtain bitmaps from. - * @param pm progress monitor to report progress on. - */ - public BitmapWalker( - ObjectWalk walker, BitmapIndex bitmapIndex, ProgressMonitor pm) { - this(walker, bitmapIndex, pm, NOOP_LISTENER); - } - /** * Create a BitmapWalker. * @@ -126,18 +56,12 @@ public final class BitmapWalker { * index to obtain bitmaps from. * @param pm * progress monitor to report progress on. - * @param listener - * listener of event happening during the walk. Use - * {@link BitmapWalker#NOOP_LISTENER} for a no-op listener. - * - * @since 6.8 */ public BitmapWalker(ObjectWalk walker, BitmapIndex bitmapIndex, - ProgressMonitor pm, BitmapWalkListener listener) { + ProgressMonitor pm) { this.walker = walker; this.bitmapIndex = bitmapIndex; this.pm = (pm == null) ? NullProgressMonitor.INSTANCE : pm; - this.listener = listener; } /** @@ -219,7 +143,6 @@ public final class BitmapWalker { Bitmap bitmap = bitmapIndex.getBitmap(obj); if (bitmap != null) { result.or(bitmap); - listener.onCommitWithBitmap(obj); } } @@ -260,7 +183,6 @@ public final class BitmapWalker { Bitmap bitmap = bitmapIndex.getBitmap(obj); if (bitmap != null) { bitmapResult.or(bitmap); - listener.onCommitWithBitmap(obj); } } @@ -290,8 +212,7 @@ public final class BitmapWalker { } walker.setObjectFilter(new BitmapObjectFilter(bitmapResult)); - ObjectId oid; - while ((oid = walker.next()) != null) { + while (walker.next() != null) { // Iterate through all of the commits. The BitmapRevFilter does // the work. // @@ -303,7 +224,6 @@ public final class BitmapWalker { // of bitmaps. pm.update(1); countOfBitmapIndexMisses++; - listener.onCommitWithoutBitmap(oid); } RevObject ro; -- cgit v1.2.3 From aab75dba7e63c88ddce92a75b2afa24cc97aeb04 Mon Sep 17 00:00:00 2001 From: Ivan Frade Date: Mon, 20 Nov 2023 12:01:12 -0800 Subject: BitmapIndex: Add interface to track bitmaps found (or not) We want to know what objects had bitmaps in the walk of the request. We can check their position in the history and evaluate our bitmap selection algorithm. Introduce a listener interface to the BitmapIndex to report which getBitmap() calls returned a bitmap (or not) and a method to the bitmap index to set the listener. Change-Id: Iac8fcc1539ddd2dd450e8a1cf5a5b1089679c378 --- .../tst/org/eclipse/jgit/lib/BitmapIndexTest.java | 85 ++++++++++++++++++++++ .../internal/storage/file/BitmapIndexImpl.java | 16 +++- .../src/org/eclipse/jgit/lib/BitmapIndex.java | 52 +++++++++++++ 3 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/BitmapIndexTest.java diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/BitmapIndexTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/BitmapIndexTest.java new file mode 100644 index 0000000000..ee4fa8bcc7 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/lib/BitmapIndexTest.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2024, Google Inc. 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.lib; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import org.eclipse.jgit.internal.storage.file.FileRepository; +import org.eclipse.jgit.internal.storage.file.GC; +import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase; +import org.eclipse.jgit.junit.TestRepository; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevWalk; +import org.junit.Before; +import org.junit.Test; + +public class BitmapIndexTest extends LocalDiskRepositoryTestCase { + + private static final String MAIN = "refs/heads/main"; + + TestRepository repo; + + RevCommit tipWithBitmap; + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + FileRepository db = createWorkRepository(); + repo = new TestRepository<>(db); + + RevCommit base = repo.commit().create(); + RevCommit one = repo.commit().parent(base).create(); + tipWithBitmap = repo.commit().parent(one).create(); + repo.update(MAIN, tipWithBitmap); + + GC gc = new GC(repo.getRepository()); + gc.setAuto(false); + gc.gc().get(); + + assertNotNull(repo.getRevWalk().getObjectReader().getBitmapIndex()); + } + + + @Test + public void listener_getBitmap_counted() throws Exception { + try (RevWalk rw = repo.getRevWalk(); + ObjectReader or = rw.getObjectReader()) { + BitmapLookupCounter counter = new BitmapLookupCounter(); + BitmapIndex bitmapIndex = or.getBitmapIndex(); + bitmapIndex.addBitmapLookupListener(counter); + + bitmapIndex.getBitmap(tipWithBitmap); + bitmapIndex.getBitmap(tipWithBitmap); + bitmapIndex.getBitmap(ObjectId.zeroId()); + + assertEquals(2, counter.bitmapFound); + assertEquals(1, counter.bitmapNotFound); + } + } + + private static class BitmapLookupCounter + implements BitmapIndex.BitmapLookupListener { + int bitmapFound = 0; + + int bitmapNotFound = 0; + + @Override + public void onBitmapFound(AnyObjectId oid) { + bitmapFound += 1; + } + + @Override + public void onBitmapNotFound(AnyObjectId oid) { + bitmapNotFound += 1; + } + } +} \ No newline at end of file diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BitmapIndexImpl.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BitmapIndexImpl.java index 8d8c6a0455..5602158d2d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BitmapIndexImpl.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/BitmapIndexImpl.java @@ -38,6 +38,8 @@ public class BitmapIndexImpl implements BitmapIndex { final int indexObjectCount; + private BitmapLookupListener listener = BitmapLookupListener.NOOP; + /** * Creates a BitmapIndex that is back by Compressed bitmaps. * @@ -57,8 +59,11 @@ public class BitmapIndexImpl implements BitmapIndex { @Override public CompressedBitmap getBitmap(AnyObjectId objectId) { EWAHCompressedBitmap compressed = packIndex.getBitmap(objectId); - if (compressed == null) + if (compressed == null) { + listener.onBitmapNotFound(objectId); return null; + } + listener.onBitmapFound(objectId); return new CompressedBitmap(compressed, this); } @@ -67,6 +72,15 @@ public class BitmapIndexImpl implements BitmapIndex { return new CompressedBitmapBuilder(this); } + @Override + public void addBitmapLookupListener(BitmapLookupListener listener) { + if (listener == null) { + throw new IllegalArgumentException( + "Use NOOP instance for no listener"); // @NON-NLS-1@ + } + this.listener = listener; + } + int findPosition(AnyObjectId objectId) { int position = packIndex.findPosition(objectId); if (position < 0) { diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BitmapIndex.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BitmapIndex.java index 97b5847f91..acaa6335d4 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/BitmapIndex.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/BitmapIndex.java @@ -39,6 +39,58 @@ public interface BitmapIndex { */ BitmapBuilder newBitmapBuilder(); + /** + * Report the results of {@link #getBitmap(AnyObjectId)} + * + * @since 6.8 + */ + interface BitmapLookupListener { + + /** + * This object has a bitmap in the index + * + * @param oid + * object id + */ + void onBitmapFound(AnyObjectId oid); + + /** + * This object does not have a bitmap in the index + * + * @param oid + * object id + */ + void onBitmapNotFound(AnyObjectId oid); + + /** + * No-op instance + */ + BitmapLookupListener NOOP = new BitmapLookupListener() { + @Override + public void onBitmapFound(AnyObjectId oid) { + // Nothing to do + } + + @Override + public void onBitmapNotFound(AnyObjectId oid) { + // Nothing to do + } + }; + } + + /** + * Report to this listener whether {@link #getBitmap(AnyObjectId)} finds a + * commit. + * + * @param listener + * instance listening to lookup events in the index. Never null. + * Set to {@link BitmapLookupListener#NOOP} to disable. + * @since 6.8 + */ + default void addBitmapLookupListener(BitmapLookupListener listener) { + // Empty implementation for API compatibility + } + /** * A bitmap representation of ObjectIds that can be iterated to return the * underlying {@code ObjectId}s or operated on with other {@code Bitmap}s. -- cgit v1.2.3 From 2a739ad2c1e948b34f6644eca39aa75fe020309d Mon Sep 17 00:00:00 2001 From: Kamil Musin Date: Thu, 23 Nov 2023 11:47:53 +0100 Subject: Make the tests buildable by bazel test All the errorprone checkers fail in many existing places of the codebase, making it impossible to run bazel test. Downgrade all to ":WARN". MergeToolTest is asserting a wrong error line, but the line seems different between bazel and mvn test runs. Therefore we don't fix it and simply run ``` bazel test -- //... -//org.eclipse.jgit.pgm.test:pgm ``` Change-Id: I44913ebc70dcc934cb69481cc3ff9ae0d8059707 --- tools/BUILD | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/tools/BUILD b/tools/BUILD index 1d29d76604..c2b2366c49 100644 --- a/tools/BUILD +++ b/tools/BUILD @@ -53,7 +53,7 @@ java_package_configuration( "-Xep:ArraysAsListPrimitiveArray:ERROR", "-Xep:ArrayToString:ERROR", "-Xep:AssertEqualsArgumentOrderChecker:ERROR", - "-Xep:AssertionFailureIgnored:ERROR", + "-Xep:AssertionFailureIgnored:WARN", "-Xep:AsyncCallableReturnsNull:ERROR", "-Xep:AsyncFunctionReturnsNull:ERROR", "-Xep:AutoValueConstructorOrderChecker:ERROR", @@ -62,7 +62,7 @@ java_package_configuration( "-Xep:AutoValueSubclassLeaked:WARN", "-Xep:BadAnnotationImplementation:ERROR", "-Xep:BadComparable:ERROR", - "-Xep:BadImport:ERROR", + "-Xep:BadImport:WARN", "-Xep:BadInstanceof:ERROR", "-Xep:BadShiftAmount:ERROR", "-Xep:BanSerializableRead:ERROR", @@ -75,8 +75,8 @@ java_package_configuration( "-Xep:CacheLoaderNull:ERROR", "-Xep:CannotMockFinalClass:ERROR", "-Xep:CanonicalDuration:ERROR", - "-Xep:CatchAndPrintStackTrace:ERROR", - "-Xep:CatchFail:ERROR", + "-Xep:CatchAndPrintStackTrace:WARN", + "-Xep:CatchFail:WARN", "-Xep:ChainedAssertionLosesContext:ERROR", "-Xep:ChainingConstructorIgnoresParameter:ERROR", "-Xep:CharacterGetNumericValue:ERROR", @@ -123,7 +123,7 @@ java_package_configuration( "-Xep:DurationGetTemporalUnit:ERROR", "-Xep:DurationTemporalUnit:ERROR", "-Xep:DurationToLongTimeUnit:ERROR", - "-Xep:EmptyBlockTag:ERROR", + "-Xep:EmptyBlockTag:WARN", "-Xep:EmptyCatch:ERROR", "-Xep:EmptySetMultibindingContributions:ERROR", "-Xep:EqualsGetClass:ERROR", @@ -158,7 +158,7 @@ java_package_configuration( "-Xep:FromTemporalAccessor:ERROR", "-Xep:FunctionalInterfaceClash:ERROR", "-Xep:FunctionalInterfaceMethodChanged:ERROR", - "-Xep:FutureReturnValueIgnored:ERROR", + "-Xep:FutureReturnValueIgnored:WARN", "-Xep:FuturesGetCheckedIllegalExceptionType:ERROR", "-Xep:GetClassOnAnnotation:ERROR", "-Xep:GetClassOnClass:ERROR", @@ -167,7 +167,7 @@ java_package_configuration( "-Xep:GuiceAssistedInjectScoping:ERROR", "-Xep:GuiceAssistedParameters:ERROR", "-Xep:HashtableContains:ERROR", - "-Xep:HidingField:ERROR", + "-Xep:HidingField:WARN", "-Xep:IdentityBinaryExpression:ERROR", "-Xep:IdentityHashMapBoxing:ERROR", "-Xep:IdentityHashMapUsage:ERROR", @@ -251,7 +251,7 @@ java_package_configuration( "-Xep:LockOnBoxedPrimitive:ERROR", "-Xep:LogicalAssignment:ERROR", "-Xep:LongFloatConversion:ERROR", - "-Xep:LongLiteralLowerCaseSuffix:ERROR", + "-Xep:LongLiteralLowerCaseSuffix:WARN", "-Xep:LoopConditionChecker:ERROR", "-Xep:LoopOverCharArray:ERROR", "-Xep:LossyPrimitiveCompare:ERROR", @@ -270,7 +270,7 @@ java_package_configuration( "-Xep:MixedDescriptors:ERROR", "-Xep:MixedMutabilityReturnType:WARN", "-Xep:MockitoUsage:ERROR", - "-Xep:ModifiedButNotUsed:ERROR", + "-Xep:ModifiedButNotUsed:WARN", "-Xep:ModifyCollectionInEnhancedForLoop:ERROR", "-Xep:ModifyingCollectionWithItself:ERROR", "-Xep:ModifySourceCollectionInStream:ERROR", @@ -323,7 +323,7 @@ java_package_configuration( "-Xep:PreferredInterfaceType:OFF", "-Xep:PrimitiveAtomicReference:ERROR", "-Xep:PrivateSecurityContractProtoAccess:ERROR", - "-Xep:ProtectedMembersInFinalClass:ERROR", + "-Xep:ProtectedMembersInFinalClass:WARN", "-Xep:ProtoBuilderReturnValueIgnored:ERROR", "-Xep:ProtocolBufferOrdinal:ERROR", "-Xep:ProtoDurationGetSecondsGetNano:ERROR", @@ -372,7 +372,7 @@ java_package_configuration( "-Xep:ThreadJoinLoop:ERROR", "-Xep:ThreadLocalUsage:ERROR", "-Xep:ThreadPriorityCheck:ERROR", - "-Xep:ThreeLetterTimeZoneID:ERROR", + "-Xep:ThreeLetterTimeZoneID:WARN", "-Xep:ThrowIfUncheckedKnownChecked:ERROR", "-Xep:ThrowNull:ERROR", "-Xep:TimeUnitConversionChecker:ERROR", @@ -389,14 +389,14 @@ java_package_configuration( "-Xep:TypeParameterQualifier:ERROR", "-Xep:TypeParameterShadowing:ERROR", "-Xep:TypeParameterUnusedInFormals:ERROR", - "-Xep:UndefinedEquals:ERROR", - "-Xep:UnescapedEntity:ERROR", + "-Xep:UndefinedEquals:WARN", + "-Xep:UnescapedEntity:WARN", "-Xep:UnnecessaryAssignment:ERROR", "-Xep:UnnecessaryCheckNotNull:ERROR", "-Xep:UnnecessaryLambda:ERROR", "-Xep:UnnecessaryMethodInvocationMatcher:ERROR", "-Xep:UnnecessaryMethodReference:ERROR", - "-Xep:UnnecessaryParentheses:ERROR", + "-Xep:UnnecessaryParentheses:WARN", "-Xep:UnnecessaryTypeArgument:ERROR", "-Xep:UnrecognisedJavadocTag:ERROR", "-Xep:UnsafeFinalization:ERROR", @@ -405,12 +405,12 @@ java_package_configuration( "-Xep:UnusedAnonymousClass:ERROR", "-Xep:UnusedCollectionModifiedInPlace:ERROR", "-Xep:UnusedException:ERROR", - "-Xep:UnusedMethod:ERROR", + "-Xep:UnusedMethod:WARN", "-Xep:UnusedNestedClass:ERROR", - "-Xep:UnusedVariable:ERROR", + "-Xep:UnusedVariable:WARN", "-Xep:URLEqualsHashCode:ERROR", "-Xep:UseBinds:ERROR", - "-Xep:UseCorrectAssertInTests:ERROR", + "-Xep:UseCorrectAssertInTests:WARN", "-Xep:VariableNameSameAsType:ERROR", "-Xep:VarTypeName:ERROR", "-Xep:WaitNotInLoop:ERROR", -- cgit v1.2.3 From 340cc787a0d14a0698d757a919ccd77e0a129fb0 Mon Sep 17 00:00:00 2001 From: Kamil Musin Date: Fri, 24 Nov 2023 15:17:26 +0100 Subject: Improve footer parsing to allow multiline footers. According to the https://git-scm.com/docs/git-interpret-trailers the CGit supports multiline trailers. Subsequent lines of such multiline trailers have to start with a whitespace. We also rewrite the original parsing code to make it easier to work with. The old code had pointers moving both backwards and forwards at the same time. In the rewritten code we first find the start of the last paragraph and then do all the parsing. Since all the getters of the FooterLine return String, I've considered rewriting the parsing code to operate on strings. However the original code seems to be written with the idea, that the data is only lazily copied in getters and no extra allocations should be performed during original parsing (ex. during RevWalk). The changed code keeps to this idea. Bug: Google b/312440626 Change-Id: Ie1e3b17a4a5ab767b771c95f00c283ea6c300220 --- .../org/eclipse/jgit/revwalk/FooterLineTest.java | 51 +++++++++++++++++++++ .../src/org/eclipse/jgit/revwalk/FooterLine.java | 53 +++++++++++----------- .../src/org/eclipse/jgit/revwalk/RevCommit.java | 5 +- 3 files changed, 81 insertions(+), 28 deletions(-) diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FooterLineTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FooterLineTest.java index 01f6a3a0a0..303aedcd00 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FooterLineTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/revwalk/FooterLineTest.java @@ -318,6 +318,57 @@ public class FooterLineTest extends RepositoryTestCase { assertFalse("not CC", line.matches(FooterKey.CC)); } + @Test + public void testMultilineFooters() { + String msg = buildMessage("subject\n\nbody of commit\n" + + "Not-A-Footer-Line: this line must not be read as a footer\n" + + "\n" // paragraph break, now footers appear in final block + + "Notes: The change must not be merged until dependency ABC is\n" + + " updated.\n" + + "CC: \n" + + "not really a footer line but we'll skip it anyway\n" + + "Acked-by: Some Reviewer \n"); + List footers = FooterLine.fromMessage(msg); + FooterLine f; + + assertNotNull(footers); + assertEquals(3, footers.size()); + + f = footers.get(0); + assertEquals("Notes", f.getKey()); + assertEquals( + "The change must not be merged until dependency ABC is updated.", + f.getValue()); + + f = footers.get(1); + assertEquals("CC", f.getKey()); + assertEquals("", f.getValue()); + + f = footers.get(2); + assertEquals("Acked-by", f.getKey()); + assertEquals("Some Reviewer ", f.getValue()); + } + + @Test + public void testMultilineFooters_multipleWhitespaceAreAllowed() { + String msg = buildMessage("subject\n\nbody of commit\n" + + "Not-A-Footer-Line: this line must not be read as a footer\n" + + "\n" // paragraph break, now footers appear in final block + + "Notes: The change must not be merged until dependency ABC is\n" + + " updated.\n"); + List footers = FooterLine.fromMessage(msg); + FooterLine f; + + assertNotNull(footers); + assertEquals(1, footers.size()); + + f = footers.get(0); + assertEquals("Notes", f.getKey()); + assertEquals( + "The change must not be merged until dependency ABC is updated.", + f.getValue()); + } + private String buildMessage(String msg) { StringBuilder buf = new StringBuilder(); buf.append("tree " + ObjectId.zeroId().name() + "\n"); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/FooterLine.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/FooterLine.java index 2d396d57c5..cadeaec6e0 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/FooterLine.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/FooterLine.java @@ -74,41 +74,42 @@ public final class FooterLine { */ public static List fromMessage( byte[] raw) { - int ptr = raw.length - 1; - while (raw[ptr] == '\n') // trim any trailing LFs, not interesting - ptr--; + // Find the end of the last paragraph. + int parEnd = raw.length; + for (; parEnd > 0 && (raw[parEnd - 1] == '\n' || raw[parEnd - 1] == ' '); --parEnd); int msgB = RawParseUtils.commitMessage(raw, 0); ArrayList r = new ArrayList<>(4); Charset enc = RawParseUtils.guessEncoding(raw); - for (;;) { - ptr = RawParseUtils.prevLF(raw, ptr); - if (ptr <= msgB) - break; // Don't parse commit headers as footer lines. - int keyStart = ptr + 2; - if (raw[keyStart] == '\n') - break; // Stop at first paragraph break, no footers above it. + // Search for the beginning of last paragraph + int parStart = parEnd; + for (; parStart > msgB && (raw[parStart - 1] != '\n' || raw[parStart - 2] != '\n'); --parStart); - int keyEnd = RawParseUtils.endOfFooterLineKey(raw, keyStart); - if (keyEnd < 0) - continue; // Not a well formed footer line, skip it. + for (int ptr = parStart; ptr < parEnd;) { + int keyStart = ptr; + int keyEnd = RawParseUtils.endOfFooterLineKey(raw, ptr); + if (keyEnd < 0) { + // Not a well-formed footer line, skip it. + ptr = RawParseUtils.nextLF(raw, ptr); + continue; + } // Skip over the ': *' at the end of the key before the value. - // - int valStart = keyEnd + 1; - while (valStart < raw.length && raw[valStart] == ' ') - valStart++; - - // Value ends at the LF, and does not include it. - // - int valEnd = RawParseUtils.nextLF(raw, valStart); - if (raw[valEnd - 1] == '\n') - valEnd--; - + int valStart, valEnd; + for (valStart = keyEnd + 1; valStart < raw.length && raw[valStart] == ' '; ++valStart); + + for(ptr = valStart;;) { + ptr = RawParseUtils.nextLF(raw, ptr); + // Next line starts with whitespace for a multiline footer. + if (ptr == raw.length || raw[ptr] != ' ') { + valEnd = raw[ptr - 1] == '\n' ? ptr - 1 : ptr; + break; + } + } r.add(new FooterLine(raw, enc, keyStart, keyEnd, valStart, valEnd)); } - Collections.reverse(r); + return r; } @@ -199,7 +200,7 @@ public final class FooterLine { * character encoding. */ public String getValue() { - return RawParseUtils.decode(enc, buffer, valStart, valEnd); + return RawParseUtils.decode(enc, buffer, valStart, valEnd).replaceAll("\n +", " "); } /** diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java index 0392ea428c..1f0f13152e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/revwalk/RevCommit.java @@ -570,8 +570,9 @@ public class RevCommit extends RevObject { * the order of the line's appearance in the commit message itself. *

* A footer line's key must match the pattern {@code ^[A-Za-z0-9-]+:}, while - * the value is free-form, but must not contain an LF. Very common keys seen - * in the wild are: + * the value is free-form. The Value may be split over multiple lines with + * each subsequent line starting with at least one whitespace. Very common + * keys seen in the wild are: *

    *
  • {@code Signed-off-by} (agrees to Developer Certificate of Origin) *
  • {@code Acked-by} (thinks change looks sane in context) -- cgit v1.2.3 From ef901e9aea21c51de1ec26ff8265faf657326857 Mon Sep 17 00:00:00 2001 From: Matthias Sohn Date: Sat, 18 Nov 2023 00:06:01 +0100 Subject: Adapt to type parameter added in commons-compress 1.25.0 In 1.25.0 commons-compress added a generic type parameter to ArchiveOutputStream to avoid unchecked/unconfirmed type casts in subclasses. Change-Id: Ib4c208fc1fb65f73ea57c5bf723fde71b0d6d9f7 --- .../src/org/eclipse/jgit/archive/BaseFormat.java | 4 +++- .../src/org/eclipse/jgit/archive/TarFormat.java | 4 ++-- .../src/org/eclipse/jgit/archive/Tbz2Format.java | 9 +++++---- .../src/org/eclipse/jgit/archive/TgzFormat.java | 9 +++++---- .../src/org/eclipse/jgit/archive/TxzFormat.java | 9 +++++---- 5 files changed, 20 insertions(+), 15 deletions(-) diff --git a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/BaseFormat.java b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/BaseFormat.java index 25cd36817a..4757998a29 100644 --- a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/BaseFormat.java +++ b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/BaseFormat.java @@ -15,6 +15,7 @@ import java.io.IOException; import java.text.MessageFormat; import java.util.Map; +import org.apache.commons.compress.archivers.ArchiveEntry; import org.apache.commons.compress.archivers.ArchiveOutputStream; import org.eclipse.jgit.archive.internal.ArchiveText; import org.eclipse.jgit.util.StringUtils; @@ -42,7 +43,8 @@ public class BaseFormat { * @throws IOException * if an IO error occurred */ - protected ArchiveOutputStream applyFormatOptions(ArchiveOutputStream s, + protected ArchiveOutputStream applyFormatOptions( + ArchiveOutputStream s, Map o) throws IOException { for (Map.Entry p : o.entrySet()) { try { diff --git a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TarFormat.java b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TarFormat.java index dfa60321b0..3db1124752 100644 --- a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TarFormat.java +++ b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TarFormat.java @@ -35,7 +35,7 @@ import org.eclipse.jgit.revwalk.RevCommit; * Unix TAR format (ustar + some PAX extensions). */ public final class TarFormat extends BaseFormat implements - ArchiveCommand.Format { + ArchiveCommand.Format> { private static final List SUFFIXES = Collections .unmodifiableList(Arrays.asList(".tar")); //$NON-NLS-1$ @@ -57,7 +57,7 @@ public final class TarFormat extends BaseFormat implements } @Override - public void putEntry(ArchiveOutputStream out, + public void putEntry(ArchiveOutputStream out, ObjectId tree, String path, FileMode mode, ObjectLoader loader) throws IOException { if (mode == FileMode.SYMLINK) { diff --git a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/Tbz2Format.java b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/Tbz2Format.java index 26da431147..03a2305e2b 100644 --- a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/Tbz2Format.java +++ b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/Tbz2Format.java @@ -17,6 +17,7 @@ import java.util.List; import java.util.Map; import org.apache.commons.compress.archivers.ArchiveOutputStream; +import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.compressors.bzip2.BZip2CompressorOutputStream; import org.eclipse.jgit.api.ArchiveCommand; import org.eclipse.jgit.lib.FileMode; @@ -26,12 +27,12 @@ import org.eclipse.jgit.lib.ObjectLoader; /** * bzip2-compressed tarball (tar.bz2) format. */ -public final class Tbz2Format extends BaseFormat implements - ArchiveCommand.Format { +public final class Tbz2Format extends BaseFormat + implements ArchiveCommand.Format> { private static final List SUFFIXES = Collections .unmodifiableList(Arrays.asList(".tar.bz2", ".tbz", ".tbz2")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - private final ArchiveCommand.Format tarFormat = new TarFormat(); + private final ArchiveCommand.Format> tarFormat = new TarFormat(); @Override public ArchiveOutputStream createArchiveOutputStream(OutputStream s) @@ -54,7 +55,7 @@ public final class Tbz2Format extends BaseFormat implements } @Override - public void putEntry(ArchiveOutputStream out, + public void putEntry(ArchiveOutputStream out, ObjectId tree, String path, FileMode mode, ObjectLoader loader) throws IOException { tarFormat.putEntry(out, tree, path, mode, loader); diff --git a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TgzFormat.java b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TgzFormat.java index d1ed035d0f..6ce9c1e748 100644 --- a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TgzFormat.java +++ b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TgzFormat.java @@ -17,6 +17,7 @@ import java.util.List; import java.util.Map; import org.apache.commons.compress.archivers.ArchiveOutputStream; +import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream; import org.apache.commons.compress.compressors.gzip.GzipParameters; import org.eclipse.jgit.api.ArchiveCommand; @@ -27,12 +28,12 @@ import org.eclipse.jgit.lib.ObjectLoader; /** * gzip-compressed tarball (tar.gz) format. */ -public final class TgzFormat extends BaseFormat implements - ArchiveCommand.Format { +public final class TgzFormat extends BaseFormat + implements ArchiveCommand.Format> { private static final List SUFFIXES = Collections .unmodifiableList(Arrays.asList(".tar.gz", ".tgz")); //$NON-NLS-1$ //$NON-NLS-2$ - private final ArchiveCommand.Format tarFormat = new TarFormat(); + private final ArchiveCommand.Format> tarFormat = new TarFormat(); @Override public ArchiveOutputStream createArchiveOutputStream(OutputStream s) @@ -57,7 +58,7 @@ public final class TgzFormat extends BaseFormat implements } @Override - public void putEntry(ArchiveOutputStream out, + public void putEntry(ArchiveOutputStream out, ObjectId tree, String path, FileMode mode, ObjectLoader loader) throws IOException { tarFormat.putEntry(out, tree, path, mode, loader); diff --git a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TxzFormat.java b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TxzFormat.java index 42549de302..f51f904bc7 100644 --- a/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TxzFormat.java +++ b/org.eclipse.jgit.archive/src/org/eclipse/jgit/archive/TxzFormat.java @@ -17,6 +17,7 @@ import java.util.List; import java.util.Map; import org.apache.commons.compress.archivers.ArchiveOutputStream; +import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.compressors.xz.XZCompressorOutputStream; import org.eclipse.jgit.api.ArchiveCommand; import org.eclipse.jgit.lib.FileMode; @@ -26,12 +27,12 @@ import org.eclipse.jgit.lib.ObjectLoader; /** * Xz-compressed tar (tar.xz) format. */ -public final class TxzFormat extends BaseFormat implements - ArchiveCommand.Format { +public final class TxzFormat extends BaseFormat + implements ArchiveCommand.Format> { private static final List SUFFIXES = Collections .unmodifiableList(Arrays.asList(".tar.xz", ".txz")); //$NON-NLS-1$ //$NON-NLS-2$ - private final ArchiveCommand.Format tarFormat = new TarFormat(); + private final ArchiveCommand.Format> tarFormat = new TarFormat(); @Override public ArchiveOutputStream createArchiveOutputStream(OutputStream s) @@ -54,7 +55,7 @@ public final class TxzFormat extends BaseFormat implements } @Override - public void putEntry(ArchiveOutputStream out, + public void putEntry(ArchiveOutputStream out, ObjectId tree, String path, FileMode mode, ObjectLoader loader) throws IOException { tarFormat.putEntry(out, tree, path, mode, loader); -- cgit v1.2.3