From 4b3c5194acd8a9b18bf269888cab2ea29c376508 Mon Sep 17 00:00:00 2001 From: Kaushik Lingarkar <quic_kaushikl@quicinc.com> Date: Tue, 19 Nov 2024 09:37:33 -0800 Subject: [PATCH] Support built-in diff drivers for hunk header function names The regexes defined for each built-in driver will be used to determine the function name for a hunk header. Each driver can specify a list of regexes to negate and match. They can also define pattern compilation flags if needed. These drivers only apply to text files with unified patch type. Following built-in drivers have been added: - cpp - dts - java - python - rust Support for more languages can be added as needed to match the cgit implementation. Change-Id: Ice5430bfed7e4aaf9f00e52e44402479984953c5 --- .../eclipse/jgit/test/resources/greeting.c | Bin 0 -> 1203 bytes .../jgit/test/resources/greeting.javasource | Bin 0 -> 1081 bytes .../eclipse/jgit/test/resources/greeting.py | Bin 0 -> 961 bytes .../eclipse/jgit/test/resources/greeting.rs | Bin 0 -> 845 bytes .../eclipse/jgit/test/resources/sample.dtsi | Bin 0 -> 384 bytes .../diff/DiffFormatterBuiltInDriverTest.java | 173 ++++++++++++++++++ org.eclipse.jgit/.settings/.api_filters | 28 +++ .../src/org/eclipse/jgit/diff/DiffDriver.java | 116 ++++++++++++ .../org/eclipse/jgit/diff/DiffFormatter.java | 140 ++++++++++++-- .../jgit/diff/PatchIdDiffFormatter.java | 4 +- 10 files changed, 448 insertions(+), 13 deletions(-) create mode 100644 org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.c create mode 100644 org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.javasource create mode 100644 org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.py create mode 100644 org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.rs create mode 100644 org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/sample.dtsi create mode 100644 org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterBuiltInDriverTest.java create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffDriver.java diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.c b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.c new file mode 100644 index 0000000000000000000000000000000000000000..366116092188f04fca1e85b50f101b093abf92d8 GIT binary patch literal 1203 zcmY$+%uCKGO-WU-DK1IL%-74X<3biH%FIhg6-zFuEJy_jah2s~rYNMRmbe$Crj}&p zrE4T-Bo--X6{QxJ=9K6tB<JT9mncBQ@)C1XH5ID46ciK`iVKP|^Geb*VCs}SQgd?h zbre*K6_s=pz$&e|YPsOXyCoK-mZ#?AkZZhqett?)Wh%%5Jq3@%vQ&jch4iA-#1e&+ z#7ZnymgM{7m!}p%tt&1mf;l`bzeqtNGp|G;Q^8ijz*?cWq$oNw)><J`TN|bX>_4!u zf~`VHeh%0mh&oMcu<vV;y%vyKRGgoen3GwRnnGkKqK0cpW^Sr~noD9O%mLuwhKqtd z5^ZV_3$eSnq$s(dQUg_5M*+@<IuRBCaB;9PP|3_R1r3lnxdj;JDCOoC<z?ojD`_g& z+A0{poCFRN^f(7aiUL$UC}t2b4^m#MkeXARs({a~#I%ysqP+b4Jfdxb>mbX%)Us5P z{03Ez(=JF}0C^T8KY+s~C9x8VUQprzrNG?8%sdTP0E6QklAEFp4PcRtoG~GiItof@ z`T0uFh=j(7l4@~GOrDaC0z?f|GCj2fp6wtKItofjiABU}#+tB^460JnQ9vXYNb1Dv QprX{0(xN;C15n8T0LHa<hX4Qo literal 0 HcmV?d00001 diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.javasource b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.javasource new file mode 100644 index 0000000000000000000000000000000000000000..9659685c634d49ae837a96f7eefb789ca34133e5 GIT binary patch literal 1081 zcmXRYP0GnkR!GiCEG|}XFG@`<$;?YvsOC~oP*8x%1(y_o#L`nsVDcJJp}fT0R86R2 zkOru5ZgIMTt%8zAYEDkRj)IbcwgO0@g0_N^qLMY(WRT*b)RNMoJcZoibck3j7g!3z zQn$pS)biAv9O5i>&(BXus!T<-R!_kru`E>~Q6ar3HL*k?C9#qmhXkY+73b$A=44i- zreF&R9R;ZWOEPm){nK0$E3t<S#Jiv%C@#+|NzPEvfNRhz$@j@GPc3pzEKb$XMAn|1 zSe&Y$l$&3amzkHYWCac?kP#pZ4R4Uw6`(2*{zupbQkqnhnwX6cf|#0^R+3tjm!F@9 z$6UA?l1xr5OC{i5s0t!YO-W5lEX^sw?fsO*N+OM_g$E)2gjigXSdy8nP?n#WqL7=I znFmdV(Xk4NMd`)JQ3Oj9pi~CQMz#uhsparorJ;!k_~6RolGI$i{L&J=f}+g4lAJt^ ubZF+&L(XVQY5Dm|n#5~_XBMTT#3E8OVolsiNr_cTI*4Qkb4V>$Ef)YH!!-^7 literal 0 HcmV?d00001 diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.py b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.py new file mode 100644 index 0000000000000000000000000000000000000000..9eda6cd8fe526d57f50ff6f4b3c9b379afdc5025 GIT binary patch literal 961 zcmYe!Nh~f_a4$+tEy>JFx8hPzP*6xoO;bouEs0NuifR<6=A`K;<R#{&YC=?iRKaB9 zb5o0p6Vp=_Y!%X!JW_LV@^uudL27FimB7Y<6c?qIlosVFpzGlRD}dRTmROWpo|=<` z-9DHsihb_+`6)@2sW2P$6g(2kQWX+G7A2M_q$E~ix&hrBR5ui)78U2`CFW#SrKVsD z03C&r%-q!Y{IvL##7d;lKoV83MOLGilV6@%q@fA+DJUp1(~zYVY;6^ka`TJwGV{`v z5P=I)h~gCFKn8hQ0jdY)b3{<5=3ucQF|8!EC@()hk0d+bx`?zTwJeone?awMv!OVZ zlmJOdti&_~n(nX#B^Os_nnHX$D5b{7gQFonJ~uHlFFqa`MzF-HU<=P5&}b|u%FHX# ufXVBj<Q1i~{Cp)%NOWUS2TOe#N=b=Dc+{b|7-kblQ<ahqBEe{CasdEr0WkRh literal 0 HcmV?d00001 diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.rs b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/greeting.rs new file mode 100644 index 0000000000000000000000000000000000000000..a3aa5cbe7c617c65bc15ba1f8710e0f6ce634c43 GIT binary patch literal 845 zcmXReDJo4aQE)FxO)bgHOSk6Y%FHduf%6rrxfB!>6w>k((o;*~)1j&~)QVGc(sUH^ z5_40n6x51KiZm5;?G%DbiZb)kp<2M&@{4j4OB6MfJW_LV@^uudYZa9sx-`MY)^dR< zsOf2mMXBYfIXNVo?w+5Yl2n-rGG9-@Be5)1AyFZ{C^fM}AtkXApN$2nMaB7fi8+~7 zsVR6or=w7knVTA)pBA5zSc%WSxrrso8OVzDO7i1#^2<|;k`s$lH8k}Si{pz+iZnEl z!Uz;jO1b$(d6{|XN(#1i3Td!F19?jUDg*KxEa-H=fd<kC!b*v0C8<Su`T2QRbi?KF z=ua(6#bGy82DjdLO!uZFR$^CQ3wCuaS1lJ;TAo5~VrCvZ7;;ieK=B1mfC{$ov}X<0 zQBahbSCW&bsG(F{tE2;0r-zarmD2L_l{7U8>wu>*rKH3nqI6(SBT7k$RZ2Pv@Q4H1 HUCRXkC`<mL literal 0 HcmV?d00001 diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/sample.dtsi b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/sample.dtsi new file mode 100644 index 0000000000000000000000000000000000000000..6aa4ecdd4cb442e52be17a1e93d556acd68af7f8 GIT binary patch literal 384 zcmdN-DJj-1Gt{@{;?h^B=HkrFPf5*DuvJiUtw_u*$VpXj%1<mxQL^UZOwP|ONG!=r z%1H%Dr$VH4lHk$>rNtlvIFk!X9Sp!M&Xm-$%;ePglF9<Gp5%g3kU^YiMkN;I>LlkE zm84eaCR#!i6s4vs*eci<*jaONa@JaNfiM?mZfb6RQ6<=DG?O7RV7)M{6&3~%pkPp8 z1Yv=VtF`9h;w()pDlxE908tKx28L;d1_lr(qk1y0*wD<>)F2VnV7LZ@3Xndq8#D7t uQj3a83rdPX?luLBCFkTPXX~aFr52W^<|S8xB#cZTE&<8cT65J}a{&MduxNS! literal 0 HcmV?d00001 diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterBuiltInDriverTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterBuiltInDriverTest.java new file mode 100644 index 000000000..135287198 --- /dev/null +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/diff/DiffFormatterBuiltInDriverTest.java @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. + * and other copyright owners as documented in the project's IP log. + * + * 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.diff; + +import static org.junit.Assert.assertEquals; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.stream.Collectors; +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.junit.JGitTestUtil; +import org.eclipse.jgit.junit.RepositoryTestCase; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.treewalk.CanonicalTreeParser; +import org.junit.Test; + +public class DiffFormatterBuiltInDriverTest extends RepositoryTestCase { + @Test + public void testCppDriver() throws Exception { + String fileName = "greeting.c"; + String body = Files.readString( + Path.of(JGitTestUtil.getTestResourceFile(fileName) + .getAbsolutePath())); + RevCommit c1; + RevCommit c2; + try (Git git = new Git(db)) { + createCommit(git, ".gitattributes", "*.c diff=cpp"); + c1 = createCommit(git, fileName, body); + c2 = createCommit(git, fileName, + body.replace("Good day", "Greetings") + .replace("baz", "qux")); + } + try (ByteArrayOutputStream os = new ByteArrayOutputStream(); + DiffFormatter diffFormatter = new DiffFormatter(os)) { + String actual = getHunkHeaders(c1, c2, os, diffFormatter); + String expected = + "@@ -27,7 +27,7 @@ void getPersonalizedGreeting(char *result, const char *name, const char *timeOfD\n" + + "@@ -37,7 +37,7 @@ int main() {"; + assertEquals(expected, actual); + } + } + + @Test + public void testDtsDriver() throws Exception { + String fileName = "sample.dtsi"; + String body = Files.readString( + Path.of(JGitTestUtil.getTestResourceFile(fileName) + .getAbsolutePath())); + RevCommit c1; + RevCommit c2; + try (Git git = new Git(db)) { + createCommit(git, ".gitattributes", "*.dtsi diff=dts"); + c1 = createCommit(git, fileName, body); + c2 = createCommit(git, fileName, + body.replace("clock-frequency = <24000000>", + "clock-frequency = <48000000>")); + } + try (ByteArrayOutputStream os = new ByteArrayOutputStream(); + DiffFormatter diffFormatter = new DiffFormatter(os)) { + String actual = getHunkHeaders(c1, c2, os, diffFormatter); + String expected = "@@ -20,6 +20,6 @@ uart0: uart@101f1000 {"; + assertEquals(expected, actual); + } + } + + @Test + public void testJavaDriver() throws Exception { + String resourceName = "greeting.javasource"; + String body = Files.readString( + Path.of(JGitTestUtil.getTestResourceFile(resourceName) + .getAbsolutePath())); + RevCommit c1; + RevCommit c2; + try (Git git = new Git(db)) { + createCommit(git, ".gitattributes", "*.java diff=java"); + String fileName = "Greeting.java"; + c1 = createCommit(git, fileName, body); + c2 = createCommit(git, fileName, + body.replace("Good day", "Greetings") + .replace("baz", "qux")); + } + try (ByteArrayOutputStream os = new ByteArrayOutputStream(); + DiffFormatter diffFormatter = new DiffFormatter(os)) { + String actual = getHunkHeaders(c1, c2, os, diffFormatter); + String expected = + "@@ -22,7 +22,7 @@ public String getPersonalizedGreeting(String name, String timeOfDay) {\n" + + "@@ -32,6 +32,6 @@ public static void main(String[] args) {"; + assertEquals(expected, actual); + } + } + + @Test + public void testPythonDriver() throws Exception { + String fileName = "greeting.py"; + String body = Files.readString( + Path.of(JGitTestUtil.getTestResourceFile(fileName) + .getAbsolutePath())); + RevCommit c1; + RevCommit c2; + try (Git git = new Git(db)) { + createCommit(git, ".gitattributes", "*.py diff=python"); + c1 = createCommit(git, fileName, body); + c2 = createCommit(git, fileName, + body.replace("Good day", "Greetings")); + } + try (ByteArrayOutputStream os = new ByteArrayOutputStream(); + DiffFormatter diffFormatter = new DiffFormatter(os)) { + String actual = getHunkHeaders(c1, c2, os, diffFormatter); + String expected = "@@ -16,7 +16,7 @@ def get_personalized_greeting(self, name, time_of_day):"; + assertEquals(expected, actual); + } + } + + @Test + public void testRustDriver() throws Exception { + String fileName = "greeting.rs"; + String body = Files.readString( + Path.of(JGitTestUtil.getTestResourceFile(fileName) + .getAbsolutePath())); + RevCommit c1; + RevCommit c2; + try (Git git = new Git(db)) { + createCommit(git, ".gitattributes", "*.rs diff=rust"); + c1 = createCommit(git, fileName, body); + c2 = createCommit(git, fileName, + body.replace("Good day", "Greetings") + .replace("baz", "qux")); + } + try (ByteArrayOutputStream os = new ByteArrayOutputStream(); + DiffFormatter diffFormatter = new DiffFormatter(os)) { + String actual = getHunkHeaders(c1, c2, os, diffFormatter); + String expected = + "@@ -14,7 +14,7 @@ fn get_personalized_greeting(&self, name: &str, time_of_day: &str) -> String {\n" + + "@@ -23,5 +23,5 @@ fn main() {"; + assertEquals(expected, actual); + } + } + + private String getHunkHeaders(RevCommit c1, RevCommit c2, + ByteArrayOutputStream os, DiffFormatter diffFormatter) + throws IOException { + diffFormatter.setRepository(db); + diffFormatter.format(new CanonicalTreeParser(null, db.newObjectReader(), + c1.getTree()), + new CanonicalTreeParser(null, db.newObjectReader(), + c2.getTree())); + diffFormatter.flush(); + return Arrays.stream(os.toString(StandardCharsets.UTF_8).split("\n")) + .filter(line -> line.startsWith("@@")) + .collect(Collectors.joining("\n")); + } + + private RevCommit createCommit(Git git, String fileName, String body) + throws IOException, GitAPIException { + writeTrashFile(fileName, body); + git.add().addFilepattern(".").call(); + return git.commit().setMessage("message").call(); + } +} diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters index 2c22f0231..d3ae4cf37 100644 --- a/org.eclipse.jgit/.settings/.api_filters +++ b/org.eclipse.jgit/.settings/.api_filters @@ -1,5 +1,33 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> <component id="org.eclipse.jgit" version="2"> + <resource path="src/org/eclipse/jgit/diff/DiffDriver.java" type="org.eclipse.jgit.diff.DiffDriver"> + <filter id="1109393411"> + <message_arguments> + <message_argument value="6.10.1"/> + <message_argument value="org.eclipse.jgit.diff.DiffDriver"/> + </message_arguments> + </filter> + </resource> + <resource path="src/org/eclipse/jgit/diff/DiffFormatter.java" type="org.eclipse.jgit.diff.DiffFormatter"> + <filter id="1142947843"> + <message_arguments> + <message_argument value="6.10.1"/> + <message_argument value="format(EditList, RawText, RawText, DiffDriver)"/> + </message_arguments> + </filter> + <filter id="1142947843"> + <message_arguments> + <message_argument value="6.10.1"/> + <message_argument value="format(FileHeader, RawText, RawText, DiffDriver)"/> + </message_arguments> + </filter> + <filter id="1142947843"> + <message_arguments> + <message_argument value="6.10.1"/> + <message_argument value="writeHunkHeader(int, int, int, int, String)"/> + </message_arguments> + </filter> + </resource> <resource path="src/org/eclipse/jgit/gitrepo/RepoProject.java" type="org.eclipse.jgit.gitrepo.RepoProject"> <filter id="1141899266"> <message_arguments> diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffDriver.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffDriver.java new file mode 100644 index 000000000..9ae1aaa29 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffDriver.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. + * and other copyright owners as documented in the project's IP log. + * + * 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.diff; + +import java.util.List; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + * Built-in drivers for various languages, sorted by name. These drivers will be + * used to determine function names for a hunk. + * <p> + * When writing or updating patterns, assume the contents are syntactically + * correct. Patterns can be simple and need not cover all syntactical corner + * cases, as long as they are sufficiently permissive. + * + * @since 6.10.1 + */ +@SuppressWarnings({"ImmutableEnumChecker", "nls"}) +public enum DiffDriver { + /** + * Built-in diff driver for <a + * href="https://learn.microsoft.com/en-us/cpp/cpp/cpp-language-reference">c++</a> + */ + cpp(List.of( + /* Jump targets or access declarations */ + "^[ \\t]*[A-Za-z_][A-Za-z_0-9]*:\\s*($|/[/*])"), List.of( + /* functions/methods, variables, and compounds at top level */ + "^((::\\s*)?[A-Za-z_].*)$")), + /** + * Built-in diff driver for <a + * href="https://devicetree-specification.readthedocs.io/en/stable/source-language.html">device + * tree files</a> + */ + dts(List.of(";", "="), List.of( + /* lines beginning with a word optionally preceded by '&' or the root */ + "^[ \\t]*((/[ \\t]*\\{|&?[a-zA-Z_]).*)")), + /** + * Built-in diff driver for <a + * href="https://docs.oracle.com/javase/specs/jls/se21/html/index.html">java</a> + */ + java(List.of( + "^[ \\t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)"), + List.of( + /* Class, enum, interface, and record declarations */ + "^[ \\t]*(([a-z-]+[ \\t]+)*(class|enum|interface|record)[ \\t]+.*)$", + /* Method definitions; note that constructor signatures are not */ + /* matched because they are indistinguishable from method calls. */ + "^[ \\t]*(([A-Za-z_<>&\\]\\[][?&<>.,A-Za-z_0-9]*[ \\t]+)+[A-Za-z_]" + + "[A-Za-z_0-9]*[ \\t]*\\([^;]*)$")), + /** + * Built-in diff driver for <a + * href="https://docs.python.org/3/reference/index.html">python</a> + */ + python(List.of("^[ \\t]*((class|(async[ \\t]+)?def)[ \\t].*)$")), + /** + * Built-in diff driver for <a * + * href="https://doc.rust-lang.org/reference/introduction.html">java</a> + */ + rust(List.of("^[\\t ]*((pub(\\([^\\)]+\\))?[\\t ]+)?" + + "((async|const|unsafe|extern([\\t ]+\"[^\"]+\"))[\\t ]+)?" + + "(struct|enum|union|mod|trait|fn|impl|macro_rules!)[< \\t]+[^;]*)$")); + + private final List<Pattern> negatePatterns; + + private final List<Pattern> matchPatterns; + + DiffDriver(List<String> negate, List<String> match, int flags) { + if (negate != null) { + this.negatePatterns = negate.stream() + .map(r -> Pattern.compile(r, flags)) + .collect(Collectors.toList()); + } else { + this.negatePatterns = null; + } + this.matchPatterns = match.stream().map(r -> Pattern.compile(r, flags)) + .collect(Collectors.toList()); + } + + DiffDriver(List<String> match) { + this(null, match, 0); + } + + DiffDriver(List<String> negate, List<String> match) { + this(negate, match, 0); + } + + /** + * Returns the list of patterns used to exclude certain lines from being + * considered as function names. + * + * @return the list of negate patterns + */ + public List<Pattern> getNegatePatterns() { + return negatePatterns; + } + + /** + * Returns the list of patterns used to match lines for potential function + * names. + * + * @return the list of match patterns + */ + public List<Pattern> getMatchPatterns() { + return matchPatterns; + } +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java index 2f472b5c0..fa446e18c 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/DiffFormatter.java @@ -30,7 +30,9 @@ import java.util.Collections; import java.util.List; +import java.util.regex.Pattern; import org.eclipse.jgit.api.errors.CanceledException; +import org.eclipse.jgit.attributes.Attribute; import org.eclipse.jgit.diff.DiffAlgorithm.SupportedAlgorithm; import org.eclipse.jgit.diff.DiffEntry.ChangeType; import org.eclipse.jgit.dircache.DirCacheIterator; @@ -703,7 +705,7 @@ public void format(List<? extends DiffEntry> entries) throws IOException { */ public void format(DiffEntry ent) throws IOException { FormatResult res = createFormatResult(ent); - format(res.header, res.a, res.b); + format(res.header, res.a, res.b, getDiffDriver(ent)); } private static byte[] writeGitLinkText(AbbreviatedObjectId id) { @@ -749,11 +751,14 @@ private String quotePath(String path) { * text source for the post-image version of the content. This * must match the content of * {@link org.eclipse.jgit.patch.FileHeader#getNewId()}. + * @param diffDriver + * the diff driver used to obtain function names in hunk headers * @throws java.io.IOException - * writing to the supplied stream failed. + * writing to the supplied stream failed. + * @since 6.10.1 */ - public void format(FileHeader head, RawText a, RawText b) - throws IOException { + public void format(FileHeader head, RawText a, RawText b, + DiffDriver diffDriver) throws IOException { // Reuse the existing FileHeader as-is by blindly copying its // header lines, but avoiding its hunks. Instead we recreate // the hunks from the text instances we have been supplied. @@ -763,8 +768,49 @@ public void format(FileHeader head, RawText a, RawText b) if (!head.getHunks().isEmpty()) end = head.getHunks().get(0).getStartOffset(); out.write(head.getBuffer(), start, end - start); - if (head.getPatchType() == PatchType.UNIFIED) - format(head.toEditList(), a, b); + if (head.getPatchType() == PatchType.UNIFIED) { + format(head.toEditList(), a, b, diffDriver); + } + } + + /** + * Format a patch script, reusing a previously parsed FileHeader. + * <p> + * This formatter is primarily useful for editing an existing patch script + * to increase or reduce the number of lines of context within the script. + * All header lines are reused as-is from the supplied FileHeader. + * + * @param head + * existing file header containing the header lines to copy. + * @param a + * text source for the pre-image version of the content. This must match + * the content of {@link org.eclipse.jgit.patch.FileHeader#getOldId()}. + * @param b + * text source for the post-image version of the content. This must match + * the content of {@link org.eclipse.jgit.patch.FileHeader#getNewId()}. + * @throws java.io.IOException + * writing to the supplied stream failed. + */ + public void format(FileHeader head, RawText a, RawText b) + throws IOException { + format(head, a, b, null); + } + + /** + * Formats a list of edits in unified diff format + * + * @param edits + * some differences which have been calculated between A and B + * @param a + * the text A which was compared + * @param b + * the text B which was compared + * @throws java.io.IOException + * if an IO error occurred + */ + public void format(EditList edits, RawText a, RawText b) + throws IOException { + format(edits, a, b, null); } /** @@ -776,11 +822,14 @@ public void format(FileHeader head, RawText a, RawText b) * the text A which was compared * @param b * the text B which was compared + * @param diffDriver + * the diff driver used to obtain function names in hunk headers * @throws java.io.IOException * if an IO error occurred + * @since 6.10.1 */ - public void format(EditList edits, RawText a, RawText b) - throws IOException { + public void format(EditList edits, RawText a, RawText b, + DiffDriver diffDriver) throws IOException { for (int curIdx = 0; curIdx < edits.size();) { Edit curEdit = edits.get(curIdx); final int endIdx = findCombinedEnd(edits, curIdx); @@ -791,7 +840,8 @@ public void format(EditList edits, RawText a, RawText b) final int aEnd = (int) Math.min(a.size(), (long) endEdit.getEndA() + context); final int bEnd = (int) Math.min(b.size(), (long) endEdit.getEndB() + context); - writeHunkHeader(aCur, aEnd, bCur, bEnd); + writeHunkHeader(aCur, aEnd, bCur, bEnd, + getFuncName(a, aCur - 1, diffDriver)); while (aCur < aEnd || bCur < bEnd) { if (aCur < curEdit.getBeginA() || endIdx + 1 < curIdx) { @@ -881,8 +931,30 @@ protected void writeRemovedLine(RawText text, int line) * @throws java.io.IOException * if an IO error occurred */ - protected void writeHunkHeader(int aStartLine, int aEndLine, - int bStartLine, int bEndLine) throws IOException { + protected void writeHunkHeader(int aStartLine, int aEndLine, int bStartLine, + int bEndLine) throws IOException { + writeHunkHeader(aStartLine, aEndLine, bStartLine, bEndLine, null); + } + + /** + * Output a hunk header + * + * @param aStartLine + * within first source + * @param aEndLine + * within first source + * @param bStartLine + * within second source + * @param bEndLine + * within second source + * @param funcName + * function name of this hunk + * @throws java.io.IOException + * if an IO error occurred + * @since 6.10.1 + */ + protected void writeHunkHeader(int aStartLine, int aEndLine, int bStartLine, + int bEndLine, String funcName) throws IOException { out.write('@'); out.write('@'); writeRange('-', aStartLine + 1, aEndLine - aStartLine); @@ -890,6 +962,10 @@ protected void writeHunkHeader(int aStartLine, int aEndLine, out.write(' '); out.write('@'); out.write('@'); + if (funcName != null) { + out.write(' '); + out.write(funcName.getBytes()); + } out.write('\n'); } @@ -1247,4 +1323,46 @@ private boolean combineB(List<Edit> e, int i) { private static boolean end(Edit edit, int a, int b) { return edit.getEndA() <= a && edit.getEndB() <= b; } + + private String getFuncName(RawText text, int startAt, + DiffDriver diffDriver) { + if (diffDriver != null) { + while (startAt > 0) { + String line = text.getString(startAt); + startAt--; + if (matchesAny(diffDriver.getNegatePatterns(), line)) { + continue; + } + if (matchesAny(diffDriver.getMatchPatterns(), line)) { + String funcName = line.replaceAll("^[ \\t]+", ""); + return funcName.substring(0, + Math.min(funcName.length(), 80)).trim(); + } + } + } + return null; + } + + private boolean matchesAny(List<Pattern> patterns, String text) { + if (patterns != null) { + for (Pattern p : patterns) { + if (p.matcher(text).find()) { + return true; + } + } + } + return false; + } + + private DiffDriver getDiffDriver(DiffEntry entry) { + Attribute diffAttr = entry.getDiffAttribute(); + if (diffAttr != null) { + try { + return DiffDriver.valueOf(diffAttr.getValue()); + } catch (IllegalArgumentException e) { + return null; + } + } + return null; + } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/diff/PatchIdDiffFormatter.java b/org.eclipse.jgit/src/org/eclipse/jgit/diff/PatchIdDiffFormatter.java index 4343642f9..b401bbe73 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/diff/PatchIdDiffFormatter.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/diff/PatchIdDiffFormatter.java @@ -44,8 +44,8 @@ public ObjectId getCalulatedPatchId() { } @Override - protected void writeHunkHeader(int aStartLine, int aEndLine, - int bStartLine, int bEndLine) throws IOException { + protected void writeHunkHeader(int aStartLine, int aEndLine, int bStartLine, + int bEndLine, String funcName) throws IOException { // The hunk header is not taken into account for patch id calculation } -- GitLab