diff options
author | Matthias Sohn <matthias.sohn@sap.com> | 2023-11-27 23:34:02 +0100 |
---|---|---|
committer | Matthias Sohn <matthias.sohn@sap.com> | 2023-11-27 23:34:02 +0100 |
commit | 5fdd58ae3c76fed65a8868bf5efca08e2c5b4449 (patch) | |
tree | ba8436cd05db0993ce274533649cbdea9bb17aae | |
parent | 9a05ca42baae22485a2b4c8fd17008525715b3e1 (diff) | |
parent | 42666cca3476456d6f84c28c3f1629c21cdf7c8a (diff) | |
download | jgit-5fdd58ae3c76fed65a8868bf5efca08e2c5b4449.tar.gz jgit-5fdd58ae3c76fed65a8868bf5efca08e2c5b4449.zip |
Merge branch 'master' into stable-6.8
* master:
Adapt to type parameter added in commons-compress 1.25.0
Improve footer parsing to allow multiline footers.
Make the tests buildable by bazel test
BitmapIndex: Add interface to track bitmaps found (or not)
BitmapWalker: Remove BitmapWalkListener
Change-Id: Id2aeb0acd37bdd68a2f2c9f09f4d442fa40c9dd1
13 files changed, 272 insertions, 143 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<? extends ArchiveEntry> applyFormatOptions( + ArchiveOutputStream<? extends ArchiveEntry> s, Map<String, Object> o) throws IOException { for (Map.Entry<String, Object> 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<ArchiveOutputStream> { + ArchiveCommand.Format<ArchiveOutputStream<TarArchiveEntry>> { private static final List<String> 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<TarArchiveEntry> 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<ArchiveOutputStream> { +public final class Tbz2Format extends BaseFormat + implements ArchiveCommand.Format<ArchiveOutputStream<TarArchiveEntry>> { private static final List<String> SUFFIXES = Collections .unmodifiableList(Arrays.asList(".tar.bz2", ".tbz", ".tbz2")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - private final ArchiveCommand.Format<ArchiveOutputStream> tarFormat = new TarFormat(); + private final ArchiveCommand.Format<ArchiveOutputStream<TarArchiveEntry>> 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<TarArchiveEntry> 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<ArchiveOutputStream> { +public final class TgzFormat extends BaseFormat + implements ArchiveCommand.Format<ArchiveOutputStream<TarArchiveEntry>> { private static final List<String> SUFFIXES = Collections .unmodifiableList(Arrays.asList(".tar.gz", ".tgz")); //$NON-NLS-1$ //$NON-NLS-2$ - private final ArchiveCommand.Format<ArchiveOutputStream> tarFormat = new TarFormat(); + private final ArchiveCommand.Format<ArchiveOutputStream<TarArchiveEntry>> 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<TarArchiveEntry> 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<ArchiveOutputStream> { +public final class TxzFormat extends BaseFormat + implements ArchiveCommand.Format<ArchiveOutputStream<TarArchiveEntry>> { private static final List<String> SUFFIXES = Collections .unmodifiableList(Arrays.asList(".tar.xz", ".txz")); //$NON-NLS-1$ //$NON-NLS-2$ - private final ArchiveCommand.Format<ArchiveOutputStream> tarFormat = new TarFormat(); + private final ArchiveCommand.Format<ArchiveOutputStream<TarArchiveEntry>> 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<TarArchiveEntry> out, ObjectId tree, String path, FileMode mode, ObjectLoader loader) throws IOException { tarFormat.putEntry(out, tree, path, mode, loader); 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<FileRepository> 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.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: <some.mailing.list@example.com>\n" + + "not really a footer line but we'll skip it anyway\n" + + "Acked-by: Some Reviewer <sr@example.com>\n"); + List<FooterLine> 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("<some.mailing.list@example.com>", f.getValue()); + + f = footers.get(2); + assertEquals("Acked-by", f.getKey()); + assertEquals("Some Reviewer <sr@example.com>", 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<FooterLine> 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/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 @@ -40,6 +40,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. */ 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 @@ -48,76 +48,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. * * @param walker @@ -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; 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<FooterLine> 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<FooterLine> 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. * <p> * 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: * <ul> * <li>{@code Signed-off-by} (agrees to Developer Certificate of Origin) * <li>{@code Acked-by} (thinks change looks sane in context) 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", |