Skip to content

Commit

Permalink
Issue checkstyle#77: git: add line changes detection
Browse files Browse the repository at this point in the history
  • Loading branch information
Luolc authored and rnveach committed Aug 17, 2017
1 parent 83a9ecb commit f92362c
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 4 deletions.
1 change: 1 addition & 0 deletions config/import-control.xml
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,6 @@

<subpackage name="git">
<allow pkg="org.eclipse.jgit"/>
<allow pkg="org.apache.commons.lang"/>
</subpackage>
</import-control>
16 changes: 16 additions & 0 deletions src/main/java/com/github/checkstyle/regression/data/GitChange.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

package com.github.checkstyle.regression.data;

import java.util.List;

import org.immutables.value.Value;

/**
Expand All @@ -32,4 +34,18 @@ public interface GitChange {
* @return the path of the changed file
*/
String path();

/**
* The line numbers of the added changes.
* The first line of a file is marked as line zero.
* @return the line numbers of the added changes
*/
List<Integer> addedLines();

/**
* The line numbers of the deleted changes.
* The first line of a file is marked as line zero.
* @return the line numbers of the deleted changes
*/
List<Integer> deletedLines();
}
35 changes: 31 additions & 4 deletions src/main/java/com/github/checkstyle/regression/git/DiffParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,16 @@

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;

import org.apache.commons.lang.math.IntRange;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.diff.DiffFormatter;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
Expand All @@ -36,6 +40,7 @@
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
import org.eclipse.jgit.util.io.DisabledOutputStream;

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

try {
final TreeParserPair pair = getTreeParserPair(repository, branchName);
final Git git = new Git(repository);
final DiffFormatter formatter = new DiffFormatter(DisabledOutputStream.INSTANCE);
formatter.setRepository(repository);

try {
returnValue = git.diff()
final List<DiffEntry> diffs = git.diff()
.setOldTree(pair.commonAncestorTreeParser)
.setNewTree(pair.prTreeParser)
.call()
.stream()
.filter(entry -> entry.getChangeType() != DiffEntry.ChangeType.DELETE)
.map(DiffParser::convertDiffEntryToGitChange)
.collect(Collectors.toList());
for (DiffEntry diff : diffs) {
returnValue.add(convertDiffEntryToGitChange(diff, formatter));
}
}
finally {
git.close();
Expand Down Expand Up @@ -158,11 +168,28 @@ private static AbstractTreeIterator prepareTreeParser(RevWalk walk, RevCommit co
/**
* Converts a {@link DiffEntry} to {@link GitChange} for the further use.
* @param diffEntry the {@link DiffEntry} instance to be converted
* @param formatter the diff formatter to provide the line changes information
* @return the {@link GitChange} instance converted from the given {@link DiffEntry}
* @throws IOException JGit library exception
*/
private static GitChange convertDiffEntryToGitChange(DiffEntry diffEntry) {
private static GitChange convertDiffEntryToGitChange(
DiffEntry diffEntry, DiffFormatter formatter) throws IOException {
final List<Integer> addedLines = formatter.toFileHeader(diffEntry).toEditList().stream()
.filter(edit -> edit.getBeginB() < edit.getEndB())
.flatMapToInt(edit -> Arrays.stream(
new IntRange(edit.getBeginB(), edit.getEndB() - 1).toArray()))
.boxed()
.collect(Collectors.toList());
final List<Integer> deletedLines = formatter.toFileHeader(diffEntry).toEditList().stream()
.filter(edit -> edit.getBeginA() < edit.getEndA())
.flatMapToInt(edit -> Arrays.stream(
new IntRange(edit.getBeginA(), edit.getEndA() - 1).toArray()))
.boxed()
.collect(Collectors.toList());
return ImmutableGitChange.builder()
.path(diffEntry.getNewPath())
.addAllAddedLines(addedLines)
.addAllDeletedLines(deletedLines)
.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ public void testParseModifyChange() throws Exception {
assertEquals("There should be 1 change detected", 1, changes.size());
final GitChange expected = ImmutableGitChange.builder()
.path("HelloWorld")
.addAddedLines(0)
.build();
assertEquals("The change is not as expected", expected, changes.get(0));
}
Expand Down Expand Up @@ -203,4 +204,71 @@ public void testParseFilePermissionChange() throws Exception {
}
}
}

@Test
public void testParseLineChangesAddLine() throws Exception {
try (Repository repository = GitUtils.createNewRepository()) {
final File helloWorld = GitUtils.addAnEmptyFileAndCommit(repository, "HelloWorld");
Files.write(helloWorld.toPath(), "line 0\n"
.getBytes(Charset.forName("UTF-8")), StandardOpenOption.APPEND);
GitUtils.addAllAndCommit(repository, "add original line");
GitUtils.createNewBranchAndCheckout(repository, "foo");
Files.write(helloWorld.toPath(), "line 1 added\nline 2 added\n"
.getBytes(Charset.forName("UTF-8")), StandardOpenOption.APPEND);
GitUtils.addAllAndCommit(repository, "add line 1, 2");
final List<GitChange> changes = DiffParser.parse(
repository.getDirectory().getParent(), "foo");
assertEquals("There should be 1 change detected", 1, changes.size());
final GitChange expected = ImmutableGitChange.builder()
.path("HelloWorld")
.addAddedLines(1, 2)
.build();
assertEquals("The change is not as expected", expected, changes.get(0));
}
}

@Test
public void testParseLineChangesRemoveLine() throws Exception {
try (Repository repository = GitUtils.createNewRepository()) {
final File helloWorld = GitUtils.addAnEmptyFileAndCommit(repository, "HelloWorld");
Files.write(helloWorld.toPath(), "line 0\nline 1 to be removed\nline 2 to be removed\n"
.getBytes(Charset.forName("UTF-8")), StandardOpenOption.APPEND);
GitUtils.addAllAndCommit(repository, "add original three lines");
GitUtils.createNewBranchAndCheckout(repository, "foo");
Files.write(helloWorld.toPath(), "line 0\n"
.getBytes(Charset.forName("UTF-8")), StandardOpenOption.TRUNCATE_EXISTING);
GitUtils.addAllAndCommit(repository, "remove two lines");
final List<GitChange> changes = DiffParser.parse(
repository.getDirectory().getParent(), "foo");
assertEquals("There should be 1 change detected", 1, changes.size());
final GitChange expected = ImmutableGitChange.builder()
.path("HelloWorld")
.addDeletedLines(1, 2)
.build();
assertEquals("The change is not as expected", expected, changes.get(0));
}
}

@Test
public void testParseLineChangesModifyLine() throws Exception {
try (Repository repository = GitUtils.createNewRepository()) {
final File helloWorld = GitUtils.addAnEmptyFileAndCommit(repository, "HelloWorld");
Files.write(helloWorld.toPath(), "line 0\nline 1\nline 2\n"
.getBytes(Charset.forName("UTF-8")), StandardOpenOption.APPEND);
GitUtils.addAllAndCommit(repository, "add original three lines");
GitUtils.createNewBranchAndCheckout(repository, "foo");
Files.write(helloWorld.toPath(), "line 0\nline 1 changed\nline 2 changed\n"
.getBytes(Charset.forName("UTF-8")), StandardOpenOption.TRUNCATE_EXISTING);
GitUtils.addAllAndCommit(repository, "modify two lines");
final List<GitChange> changes = DiffParser.parse(
repository.getDirectory().getParent(), "foo");
assertEquals("There should be 1 change detected", 1, changes.size());
final GitChange expected = ImmutableGitChange.builder()
.path("HelloWorld")
.addAddedLines(1, 2)
.addDeletedLines(1, 2)
.build();
assertEquals("The change is not as expected", expected, changes.get(0));
}
}
}

0 comments on commit f92362c

Please sign in to comment.