Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using the JGIT, how can I retrieve the line numbers of added/deleted lines

Assuming the following piece of code is committed to a Git repository:

int test(){
   int a = 3;
   int b = 4;
   int c = a + b;
   return c;
}

and is later updated to

int test(){
  return 7;
}

I currently have a method which uses the JGit API in order to access the Git repository where the above are committed and outputs a string which is similar to the following:

int test(){
-int a = 3;
-int b = 4;
-int c = a + b;
-return c;
+return 7;
}

Now, my requirements have changed and would like to know the line numbers of the changed lines only. So I would want something like the following:

2 -int a = 3;
3 -int b = 4;
4 -int c = a + b;
5 -return c;
2 +return 7;

Basically, the same information that the GitHub application gives when an update is made.

Any help would be greatly appreciated :)

snippet of how the -/+ lines are computed:

            String oldHash = "ee3e216ab5047748a22e9ec5ad3e92834704f0cc";
        Git git = null;
        try {
            //the path where the repo is.
            git = Git.open(new File("C:\\Users\\Administrator\\Documents\\GitHub\\Trial"));
        } catch (IOException e1) {
            e1.printStackTrace();
        }
        Repository repository = git.getRepository();
        ObjectId old = null;
        ObjectId head = null;
        //a new reader to read objects from getObjectDatabase()
        ObjectReader reader = repository.newObjectReader();
        //Create a new parser.
        CanonicalTreeParser oldTreeIter = new CanonicalTreeParser();
        CanonicalTreeParser newTreeIter = new CanonicalTreeParser();
        List<DiffEntry> diffs = null;

        try {
            //parse a git repository string and return an ObjectId
            old = repository.resolve(oldHash + "^{tree}");
            head = repository.resolve("HEAD^{tree}");
            //Reset this parser to walk through the given tree
            oldTreeIter.reset(reader, old);
            newTreeIter.reset(reader, head);
            diffs = git.diff()//Returns a command object to execute a diff command
                    .setNewTree(newTreeIter)
                    .setOldTree(oldTreeIter)
                    .call();//returns a DiffEntry for each path which is different

        } catch (RevisionSyntaxException | IOException | GitAPIException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        //DiffLineCountFilter d = new DiffLineCountFilter();
        //out is the stream the formatter will write to
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        //Create a new formatter with a default level of context.
        DiffFormatter df = new DiffFormatter(out);
        //Set the repository the formatter can load object contents from.
        df.setRepository(git.getRepository());
        ArrayList<String> diffText = new ArrayList<String>();
        //A DiffEntry is 'A value class representing a change to a file' therefore for each file you have a diff entry
        for(DiffEntry diff : diffs)
        {
           try {
                 //Format a patch script for one file entry.
                df.format(diff);
                RawText r = new RawText(out.toByteArray());
                r.getLineDelimiter();


                diffText.add(out.toString());
                out.reset();
            } catch (IOException e) {
                e.printStackTrace();
            }

         }
like image 990
gracey Avatar asked Oct 19 '13 14:10

gracey


2 Answers

You need to do the difference between the A line indexes and B line indexes from the diff result:

int linesAdded = 0;
int linesDeleted = 0;
int filesChanged = 0;
try {
    repo = new FileRepository(new File("repo/.git"));
    RevWalk rw = new RevWalk(repo);
    RevCommit commit = rw.parseCommit(repo.resolve("486817d67b")); // Any ref will work here (HEAD, a sha1, tag, branch)
    RevCommit parent = rw.parseCommit(commit.getParent(0).getId());
    DiffFormatter df = new DiffFormatter(DisabledOutputStream.INSTANCE);
    df.setRepository(repo);
    df.setDiffComparator(RawTextComparator.DEFAULT);
    df.setDetectRenames(true);
    List<DiffEntry> diffs;
    diffs = df.scan(parent.getTree(), commit.getTree());
    filesChanged = diffs.size();
    for (DiffEntry diff : diffs) {
        for (Edit edit : df.toFileHeader(diff).toEditList()) {
            linesDeleted += edit.getEndA() - edit.getBeginA();
            linesAdded += edit.getEndB() - edit.getBeginB();
        }
    }
} catch (IOException e1) {
    throw new RuntimeException(e1);
}
like image 71
qwertzguy Avatar answered Nov 19 '22 03:11

qwertzguy


Just a tip for anyone who might have this problem. I did not manage to get the line numbers of the added and deleted lines but I did manage to get a string which contains only the added and deleted lines without the other lines which were not changed.

This was simply done by adding the line:

   df.setContext(0);

in the snippet I provided above right before the line

   df.format(diff);
like image 43
gracey Avatar answered Nov 19 '22 04:11

gracey