Skip to content

Commit f92362c

Browse files
Luolcrnveach
authored andcommitted
Issue #77: git: add line changes detection
1 parent 83a9ecb commit f92362c

File tree

4 files changed

+116
-4
lines changed

4 files changed

+116
-4
lines changed

config/import-control.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,6 @@
4949

5050
<subpackage name="git">
5151
<allow pkg="org.eclipse.jgit"/>
52+
<allow pkg="org.apache.commons.lang"/>
5253
</subpackage>
5354
</import-control>

src/main/java/com/github/checkstyle/regression/data/GitChange.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919

2020
package com.github.checkstyle.regression.data;
2121

22+
import java.util.List;
23+
2224
import org.immutables.value.Value;
2325

2426
/**
@@ -32,4 +34,18 @@ public interface GitChange {
3234
* @return the path of the changed file
3335
*/
3436
String path();
37+
38+
/**
39+
* The line numbers of the added changes.
40+
* The first line of a file is marked as line zero.
41+
* @return the line numbers of the added changes
42+
*/
43+
List<Integer> addedLines();
44+
45+
/**
46+
* The line numbers of the deleted changes.
47+
* The first line of a file is marked as line zero.
48+
* @return the line numbers of the deleted changes
49+
*/
50+
List<Integer> deletedLines();
3551
}

src/main/java/com/github/checkstyle/regression/git/DiffParser.java

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,16 @@
2121

2222
import java.io.File;
2323
import java.io.IOException;
24+
import java.util.Arrays;
25+
import java.util.LinkedList;
2426
import java.util.List;
2527
import java.util.stream.Collectors;
2628

29+
import org.apache.commons.lang.math.IntRange;
2730
import org.eclipse.jgit.api.Git;
2831
import org.eclipse.jgit.api.errors.GitAPIException;
2932
import org.eclipse.jgit.diff.DiffEntry;
33+
import org.eclipse.jgit.diff.DiffFormatter;
3034
import org.eclipse.jgit.lib.Constants;
3135
import org.eclipse.jgit.lib.Repository;
3236
import org.eclipse.jgit.revwalk.RevCommit;
@@ -36,6 +40,7 @@
3640
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
3741
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
3842
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
43+
import org.eclipse.jgit.util.io.DisabledOutputStream;
3944

4045
import com.github.checkstyle.regression.data.GitChange;
4146
import com.github.checkstyle.regression.data.ImmutableGitChange;
@@ -44,6 +49,7 @@
4449
* Parses git diff between PR branch and master for the further use.
4550
* @author LuoLiangchen
4651
*/
52+
// -@cs[ClassDataAbstractionCoupling] We have to import many classes from JGit
4753
public final class DiffParser {
4854
/** Prevents instantiation. */
4955
private DiffParser() {
@@ -59,24 +65,28 @@ private DiffParser() {
5965
*/
6066
public static List<GitChange> parse(String repositoryPath, String branchName)
6167
throws IOException, GitAPIException {
62-
final List<GitChange> returnValue;
68+
final List<GitChange> returnValue = new LinkedList<>();
6369
final File gitDir = new File(repositoryPath, ".git");
6470
final Repository repository = new FileRepositoryBuilder().setGitDir(gitDir)
6571
.readEnvironment().findGitDir().build();
6672

6773
try {
6874
final TreeParserPair pair = getTreeParserPair(repository, branchName);
6975
final Git git = new Git(repository);
76+
final DiffFormatter formatter = new DiffFormatter(DisabledOutputStream.INSTANCE);
77+
formatter.setRepository(repository);
7078

7179
try {
72-
returnValue = git.diff()
80+
final List<DiffEntry> diffs = git.diff()
7381
.setOldTree(pair.commonAncestorTreeParser)
7482
.setNewTree(pair.prTreeParser)
7583
.call()
7684
.stream()
7785
.filter(entry -> entry.getChangeType() != DiffEntry.ChangeType.DELETE)
78-
.map(DiffParser::convertDiffEntryToGitChange)
7986
.collect(Collectors.toList());
87+
for (DiffEntry diff : diffs) {
88+
returnValue.add(convertDiffEntryToGitChange(diff, formatter));
89+
}
8090
}
8191
finally {
8292
git.close();
@@ -158,11 +168,28 @@ private static AbstractTreeIterator prepareTreeParser(RevWalk walk, RevCommit co
158168
/**
159169
* Converts a {@link DiffEntry} to {@link GitChange} for the further use.
160170
* @param diffEntry the {@link DiffEntry} instance to be converted
171+
* @param formatter the diff formatter to provide the line changes information
161172
* @return the {@link GitChange} instance converted from the given {@link DiffEntry}
173+
* @throws IOException JGit library exception
162174
*/
163-
private static GitChange convertDiffEntryToGitChange(DiffEntry diffEntry) {
175+
private static GitChange convertDiffEntryToGitChange(
176+
DiffEntry diffEntry, DiffFormatter formatter) throws IOException {
177+
final List<Integer> addedLines = formatter.toFileHeader(diffEntry).toEditList().stream()
178+
.filter(edit -> edit.getBeginB() < edit.getEndB())
179+
.flatMapToInt(edit -> Arrays.stream(
180+
new IntRange(edit.getBeginB(), edit.getEndB() - 1).toArray()))
181+
.boxed()
182+
.collect(Collectors.toList());
183+
final List<Integer> deletedLines = formatter.toFileHeader(diffEntry).toEditList().stream()
184+
.filter(edit -> edit.getBeginA() < edit.getEndA())
185+
.flatMapToInt(edit -> Arrays.stream(
186+
new IntRange(edit.getBeginA(), edit.getEndA() - 1).toArray()))
187+
.boxed()
188+
.collect(Collectors.toList());
164189
return ImmutableGitChange.builder()
165190
.path(diffEntry.getNewPath())
191+
.addAllAddedLines(addedLines)
192+
.addAllDeletedLines(deletedLines)
166193
.build();
167194
}
168195

src/test/java/com/github/checkstyle/regression/git/DiffParserTest.java

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ public void testParseModifyChange() throws Exception {
7979
assertEquals("There should be 1 change detected", 1, changes.size());
8080
final GitChange expected = ImmutableGitChange.builder()
8181
.path("HelloWorld")
82+
.addAddedLines(0)
8283
.build();
8384
assertEquals("The change is not as expected", expected, changes.get(0));
8485
}
@@ -203,4 +204,71 @@ public void testParseFilePermissionChange() throws Exception {
203204
}
204205
}
205206
}
207+
208+
@Test
209+
public void testParseLineChangesAddLine() throws Exception {
210+
try (Repository repository = GitUtils.createNewRepository()) {
211+
final File helloWorld = GitUtils.addAnEmptyFileAndCommit(repository, "HelloWorld");
212+
Files.write(helloWorld.toPath(), "line 0\n"
213+
.getBytes(Charset.forName("UTF-8")), StandardOpenOption.APPEND);
214+
GitUtils.addAllAndCommit(repository, "add original line");
215+
GitUtils.createNewBranchAndCheckout(repository, "foo");
216+
Files.write(helloWorld.toPath(), "line 1 added\nline 2 added\n"
217+
.getBytes(Charset.forName("UTF-8")), StandardOpenOption.APPEND);
218+
GitUtils.addAllAndCommit(repository, "add line 1, 2");
219+
final List<GitChange> changes = DiffParser.parse(
220+
repository.getDirectory().getParent(), "foo");
221+
assertEquals("There should be 1 change detected", 1, changes.size());
222+
final GitChange expected = ImmutableGitChange.builder()
223+
.path("HelloWorld")
224+
.addAddedLines(1, 2)
225+
.build();
226+
assertEquals("The change is not as expected", expected, changes.get(0));
227+
}
228+
}
229+
230+
@Test
231+
public void testParseLineChangesRemoveLine() throws Exception {
232+
try (Repository repository = GitUtils.createNewRepository()) {
233+
final File helloWorld = GitUtils.addAnEmptyFileAndCommit(repository, "HelloWorld");
234+
Files.write(helloWorld.toPath(), "line 0\nline 1 to be removed\nline 2 to be removed\n"
235+
.getBytes(Charset.forName("UTF-8")), StandardOpenOption.APPEND);
236+
GitUtils.addAllAndCommit(repository, "add original three lines");
237+
GitUtils.createNewBranchAndCheckout(repository, "foo");
238+
Files.write(helloWorld.toPath(), "line 0\n"
239+
.getBytes(Charset.forName("UTF-8")), StandardOpenOption.TRUNCATE_EXISTING);
240+
GitUtils.addAllAndCommit(repository, "remove two lines");
241+
final List<GitChange> changes = DiffParser.parse(
242+
repository.getDirectory().getParent(), "foo");
243+
assertEquals("There should be 1 change detected", 1, changes.size());
244+
final GitChange expected = ImmutableGitChange.builder()
245+
.path("HelloWorld")
246+
.addDeletedLines(1, 2)
247+
.build();
248+
assertEquals("The change is not as expected", expected, changes.get(0));
249+
}
250+
}
251+
252+
@Test
253+
public void testParseLineChangesModifyLine() throws Exception {
254+
try (Repository repository = GitUtils.createNewRepository()) {
255+
final File helloWorld = GitUtils.addAnEmptyFileAndCommit(repository, "HelloWorld");
256+
Files.write(helloWorld.toPath(), "line 0\nline 1\nline 2\n"
257+
.getBytes(Charset.forName("UTF-8")), StandardOpenOption.APPEND);
258+
GitUtils.addAllAndCommit(repository, "add original three lines");
259+
GitUtils.createNewBranchAndCheckout(repository, "foo");
260+
Files.write(helloWorld.toPath(), "line 0\nline 1 changed\nline 2 changed\n"
261+
.getBytes(Charset.forName("UTF-8")), StandardOpenOption.TRUNCATE_EXISTING);
262+
GitUtils.addAllAndCommit(repository, "modify two lines");
263+
final List<GitChange> changes = DiffParser.parse(
264+
repository.getDirectory().getParent(), "foo");
265+
assertEquals("There should be 1 change detected", 1, changes.size());
266+
final GitChange expected = ImmutableGitChange.builder()
267+
.path("HelloWorld")
268+
.addAddedLines(1, 2)
269+
.addDeletedLines(1, 2)
270+
.build();
271+
assertEquals("The change is not as expected", expected, changes.get(0));
272+
}
273+
}
206274
}

0 commit comments

Comments
 (0)