13

I have been working on a Java based product for which the Git features are going to be integrated. Using one of the Git features, I have done adding 10+ files into the Git repository by staging followed by committing them in a single commit.

Is the reverse of the above process possible? I.e Finding the list of files committed as part of a commit.

I got the commit with the help of the git.log() command but I am not sure how to get the file list for the commit.

Example Code:

Git git = (...);
Iterable<RevCommit> logs = git.log().call();
for(RevCommit commit : logs) {
    String commitID = commit.getName();
    if(commitID != null && !commitID.isEmpty()) {
    TableItem item = new TableItem(table, SWT.None);
    item.setText(commitID);
    // Here I want to get the file list for the commit object
}
}
Rüdiger Herrmann
  • 20,512
  • 11
  • 62
  • 79
Thamilarasan M
  • 171
  • 1
  • 1
  • 8

3 Answers3

17

Each commit points to a tree that denotes all files that make up the commit.

Note, that this not only includes the files that were added, modified, or removed with this particular commit but all files contained in this revision.

If the commit is represented as a RevCommit, the ID of the tree can be obtained like this:

ObjectId treeId = commit.getTree().getId();

If the commit ID originates from another source, it needs to be resolved first to get the associated tree ID. See here, for example: How to obtain the RevCommit or ObjectId from a SHA1 ID string with JGit?

In order to iterate over a tree, use a TreeWalk:

try (TreeWalk treeWalk = new TreeWalk(repository)) {
  treeWalk.reset(treeId);
  while (treeWalk.next()) {
    String path = treeWalk.getPathString();
    // ...
  }
}

If you are only interested in the changes that were recorded with a certain commit, see here: Creating Diffs with JGit or here: File diff against the last commit with JGit

Ravinder Payal
  • 2,884
  • 31
  • 40
Rüdiger Herrmann
  • 20,512
  • 11
  • 62
  • 79
  • 2
    I'm getting: org.eclipse.jgit.errors.IncorrectObjectTypeException: Object e5acd3dd99bfc09bf93ba4dd2db4a9ddba4c7359 is not a tree. – Gringo Golf Jul 05 '17 at 19:48
  • why else would a file be in staging area if not being added/modified or removed ? – NimChimpsky Nov 20 '17 at 04:59
  • @NimChimpsky thank you for the hint. My answer wasn't very clear in this respect. Please see my edited answer which hopefully makes more sense. – Rüdiger Herrmann Nov 20 '17 at 20:00
  • Is it possible to get the commits from a file path in the repository? – Jorge Campos Aug 01 '18 at 22:57
  • Are you looking for something like this: https://stackoverflow.com/questions/11471836/how-to-git-log-follow-path-in-jgit-to-retrieve-the-full-history-includi? – Rüdiger Herrmann Aug 02 '18 at 06:18
  • I'm getting the same problem as GringoGolf above: org.eclipse.jgit.errors.IncorrectObjectTypeException * the given object id does not denote a tree, but instead names * some other non-tree type of object. Note that commits are not * trees, even if they are sometimes called a "tree-ish". – paiego Mar 03 '19 at 20:14
  • @paiego I guess the `IncorrectObjectTypeException` exception was due to a missing step. Please see the updated answer. – Rüdiger Herrmann Mar 04 '19 at 07:22
0

I edited a little from the code given in this link. You can try using the below code.

public void commitHistory(Git git) throws NoHeadException, GitAPIException, IncorrectObjectTypeException, CorruptObjectException, IOException, UnirestException 
{
    Iterable<RevCommit> logs = git.log().call();
    int k = 0;
    for (RevCommit commit : logs) {
        String commitID = commit.getName();
        if (commitID != null && !commitID.isEmpty())
        {
            LogCommand logs2 = git.log().all();
            Repository repository = logs2.getRepository();
            tw = new TreeWalk(repository);
            tw.setRecursive(true);
            RevCommit commitToCheck = commit;
            tw.addTree(commitToCheck.getTree());
            for (RevCommit parent : commitToCheck.getParents())
            {
                tw.addTree(parent.getTree());
            }
            while (tw.next())
            {
                int similarParents = 0;
                for (int i = 1; i < tw.getTreeCount(); i++)
                    if (tw.getFileMode(i) == tw.getFileMode(0) && tw.getObjectId(0).equals(tw.getObjectId(i)))
                        similarParents++;
                if (similarParents == 0) 
                        System.out.println("File names: " + fileName);
            }
        }
    }
}
Arutsudar Arut
  • 195
  • 1
  • 13
0

I came up with this solution for finding the list of files changed in a commit. Because a commit only has the files(tree) at the time of the commit, it doesn't have any changes. You can't get differences until you compare the commit with another commit(a parent). This solution was researched by looking at how Jgit performed a diff.

Note: For simplicity, I didn't handle the special cases of a commit having multiple parents or no parents.

Finds a set of files (unique and sorted) that were changed by a set of commits.

public Set<String> getChangedFiles(Git git, Set<RevCommit> commits)
{
    TreeWalk walk = new TreeWalk(git.getRepository());
    walk.setRecursive(true);
    walk.setFilter(TreeFilter.ANY_DIFF);

    Set<String> files = new TreeSet<>(); // Uses natural ordering
    commits.forEach(commit->
    {
        System.err.println( commit.getFullMessage() );
        ObjectId a = commit.getTree().getId();
        RevCommit parent = commit.getParent(0);
        ObjectId b = parent.getTree().getId();

        try
        {
            walk.reset(b,a);
            List<DiffEntry>changes = DiffEntry.scan(walk);
            changes.forEach(de->
            {
                files.add(de.getNewPath()); // use old path if change type is deleted
            });
        }
        catch (IOException e)
        {
            e.printStackTrace();
            System.exit(1);
        }
    });
    return files;
}
Clint
  • 1
  • 1