Looping over commits for file with jGit

I was able to figure out the basics of a jGit file in terms of connecting to the repository and adding, commenting and even cyclizing commit messages for files.

File gitDir = new File("/Users/myname/Sites/helloworld/.git"); RepositoryBuilder builder = new RepositoryBuilder(); Repository repository; repository = builder.setGitDir(gitDir).readEnvironment() .findGitDir().build(); Git git = new Git(repository); RevWalk walk = new RevWalk(repository); RevCommit commit = null; // Add all files // AddCommand add = git.add(); // add.addFilepattern(".").call(); // Commit them // CommitCommand commit = git.commit(); // commit.setMessage("Commiting from java").call(); Iterable<RevCommit> logs = git.log().call(); Iterator<RevCommit> i = logs.iterator(); while (i.hasNext()) { commit = walk.parseCommit( i.next() ); System.out.println( commit.getFullMessage() ); } 

What I want to do next is the ability to get the entire commit message for one file, and then return one file back to a specific link / point in time.

+8
java jgit
source share
4 answers

Here's how to find commit changes based on all parent commits

  var tree = new TreeWalk(repository) tree.addTree(commit.getTree) commit.getParents foreach { parent => tree.addTree(parent.getTree) } tree.setFilter(TreeFilter.ANY_DIFF) 

(scala code)

Note that TreeFilter.ANY_DIFF works for a single tree runner and returns all the elements available in the root commit.

Then you have to iterate over the tree to see if your file is in the given delta (this is pretty simple).

  while (tree.next) if (tree.getDepth == cleanPath.size) { // we are at the right level, do what you want } else { if (tree.isSubtree && name == cleanPath(tree.getDepth)) { tree.enterSubtree } } } 

(cleanPath is clean in the repo path, divided by '/')

Now wrap this code in a RevWalk.next loop and you will get commits and files modified by commit.

You can use a different filter than ANY_DIFF because ANY_DIFF is true if one tree is different. This is a little contrary to intuition in the case of merging, where the blob has not changed compared to all the parent trees. So, here is the ALL_DIFF core, which will display only those elements that are different from all the parent trees:

 override def include(walker: TreeWalk): Boolean = { val n = walker.getTreeCount(); if (n == 1) { return true; } val m = walker.getRawMode(0) var i = 1 while (i < n) { if (walker.getRawMode(i) == m && walker.idEqual(i, 0)) { return false } i += 1 } true } 

(scala code derived from AnyDiffFilter)

+9
source share

So, I tried to get the charlieboy solution to work, and it basically did, but it did not help me in the following case (maybe something has changed in jgit from this post?)

add fileA, commit as "commit 1" add fileB, commit as "commit 2"

 getFileVersionDateList("fileA") 

And commit 1 and commit 2 were found, where I was expecting only commit 1 .

My solution was as follows:

 List<Commit> commits = new ArrayList<Commit>(); RevWalk revWalk = new RevWalk(repository); revWalk.setTreeFilter( AndTreeFilter.create( PathFilterGroup.createFromStrings(<relative path in question>), TreeFilter.ANY_DIFF) ); RevCommit rootCommit = revWalk.parseCommit(repository.resolve(Constants.HEAD)); revWalk.sort(RevSort.COMMIT_TIME_DESC); revWalk.markStart(rootCommit); for (RevCommit revCommit : revWalk) { commits.add(new GitCommit(getRepository(), revCommit)); } 

Using LogCommand is even simpler and looks like this:

 List<Commit> commitsList = new ArrayList<Commit>(); Git git = new Git(repository); LogCommand logCommand = git.log() .add(git.getRepository().resolve(Constants.HEAD)) .addPath(<relative path in question>); for (RevCommit revCommit : logCommand.call()) { commitsList.add(new GitCommit(this, revCommit)); } 

Obviously, you should also check commit dates, etc., if necessary.

+6
source share

A general approach with git to search for the history of a particular file is to go through the revision schedule (which you do) and for each of them, check the object that the path in question refers to (maybe either drops or a tree to find the history for everything subtree). Therefore, essentially, act as a filter at the output of the revision, which is displayed by the revision cursor.

The jgit documentation seems ... rare. But you should be able to get the RevTree corresponding to each RevCommit, and, if necessary, go through this with each segment of the path in the queue to the identifier of the target.

+2
source share

araqnid is right, this is how I got a list of dates, every date associated with the commit that included this file ...

Then you can get the file from a specific commit, since you have the file name and commit date, see the two methods below.

note: this code is in the .groovy class, so you will undoubtedly have to change a bit for java.

 byte[] getAnyPreviousVersionFileBytes(String relativeFilePath, Date date) { byte[] bytes = null try { RevWalk revWalk = new RevWalk(repository) ObjectId headId = repository.resolve(Constants.HEAD); RevCommit root = revWalk.parseCommit(headId); revWalk.sort(RevSort.COMMIT_TIME_DESC); revWalk.markStart(root); for (RevCommit revCommit: revWalk) { // if date matches then walk the tree in this commit if (new Date(revCommit.commitTime * 1000L) == date) { TreeWalk treeWalk = TreeWalk.forPath(repository, relativeFilePath, revCommit.getTree()) if (treeWalk != null) { treeWalk.setRecursive(true) CanonicalTreeParser canonicalTreeParser = treeWalk.getTree(0, CanonicalTreeParser) while (!canonicalTreeParser.eof()) { // if the filename matches, we have a match, so set teh byte array to return if (canonicalTreeParser.getEntryPathString() == relativeFilePath) { ObjectLoader objectLoader = repository.open(canonicalTreeParser.getEntryObjectId()) bytes = objectLoader.bytes } canonicalTreeParser.next(1) } } } } } catch (Exception e) { throw new JgitException(e) } return bytes } List<Date> getFileVersionDateList(String relativeFilePath) { List<Date> versions = new LinkedList<Date>() try { RevWalk revWalk = new RevWalk(repository) ObjectId headId = repository.resolve(Constants.HEAD); RevCommit root = revWalk.parseCommit(headId); revWalk.sort(RevSort.COMMIT_TIME_DESC); revWalk.markStart(root); for (RevCommit revCommit: revWalk) { TreeWalk treeWalk = TreeWalk.forPath(repository, relativeFilePath, revCommit.getTree()) if (treeWalk != null) { treeWalk.setRecursive(true) CanonicalTreeParser canonicalTreeParser = treeWalk.getTree(0, CanonicalTreeParser) while (!canonicalTreeParser.eof()) { // if the filename matches, we have a match, so add the date of this commit to the list if (canonicalTreeParser.getEntryPathString() == relativeFilePath) { versions.add(new Date(revCommit.commitTime * 1000L)) } canonicalTreeParser.next(1) } } } } catch (Exception e) { throw new JgitException(e) } return versions } 
+1
source share

Source: https://habr.com/ru/post/651421/


All Articles