java 使用 jGit 循环提交文件

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4777453/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 07:58:56  来源:igfitidea点击:

Looping over commits for a file with jGit

javajgit

提问by Andy Jarrett

I've managed to get to grips with the basics of jGit file in terms of connecting to a repos and adding, commiting, and even looping of the commit messages for the files.

我已经设法掌握了 jGit 文件的基础知识,包括连接到存储库以及添加、提交甚至循环文件的提交消息。

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 be able to get all the commit message for a single file and then be able revert the single file back to a specific reference/point in time.

我接下来要做的是能够获取单个文件的所有提交消息,然后能够将单个文件恢复到特定的参考/时间点。

采纳答案by treffer

Here is how to find the changes of a commit 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 walker and will return all elements available in a root commit.

请注意, TreeFilter.ANY_DIFF 适用于单个树遍历器,并将返回根提交中可用的所有元素。

You would then have to iterate over the tree to see if your file is in the given delta (this is quite easy).

然后您必须遍历树以查看您的文件是否在给定的增量中(这很容易)。

    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 the pure in repo path, split by '/')

(cleanPath 是 repo 路径中的纯路径,由 '/' 分割)

Now wrap that code into a RevWalk.next loop and you'll get the commits and files altered by the commit.

现在将该代码包装到 RevWalk.next 循环中,您将获得由提交更改的提交和文件。

You may want to use a different filter than ANY_DIFF, because ANY_DIFF is true if one tree differs. This is a bit counter-intuitive in case of a merge where the blob didn't change compared to all parent trees. So here is the core of an ALL_DIFF, that will only show elements that differ from all parent trees:

您可能希望使用与 ANY_DIFF 不同的过滤器,因为如果一棵树不同,则 ANY_DIFF 为真。在合并的情况下,与所有父树相比,blob 没有改变,这有点违反直觉。所以这里是 ALL_DIFF 的核心,它只会显示与所有父树不同的元素:

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)

(Scala 代码,源自 AnyDiffFilter)

回答by Skip

So I tried to get charlieboy's solution to work, and it mostly did, but it failed for me in the following case (maybe something changed in jgit since that post?)

所以我试图让 charlieboy 的解决方案发挥作用,并且大部分都成功了,但在以下情况下它对我来说失败了(也许自那篇文章以来 jgit 中的某些内容发生了变化?)

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

添加文件A,提交为“提交1” 添加文件B,提交为“提交2”

getFileVersionDateList("fileA")

Both commit 1and commit 2were found, where I expected only commit 1.

双方commit 1commit 2发现,在这里我只希望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 the LogCommand is even simpler, and looks like this:

使用 LogCommand 甚至更简单,如下所示:

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'd also check the commit dates, etc, as needed.

显然,您还会根据需要检查提交日期等。

回答by araqnid

The general approach with git to find the history of a specific file is to walk through the revision graph (which you are doing) and for each one, test the object referred to by the path in question (can be either a blob or a tree to find the history for an entire subtree). So essentially act as a filter on the revision set output by the revision walker.

使用 git 查找特定文件历史的一般方法是遍历修订图(您正在做的),并针对每个修订图,测试相关路径所引用的对象(可以是 blob 或树查找整个子树的历史记录)。因此,本质上充当修订版walker 输出的修订集集的过滤器。

The jgit documentation seems... sparse. But you should be able to get a RevTree corresponding to each RevCommit, and if necessarily walk through that with each path segment in turn down to a terminal object ID.

jgit 文档似乎......稀疏。但是您应该能够获得与每个 RevCommit 相对应的 RevTree,并且如果必须通过每个路径段依次向下遍历到终端对象 ID。

回答by charles

araqnid is right, this is how I got a list of Dates, each date relating to commit that included the file in question...

araqnid 是对的,这就是我获得日期列表的方式,每个日期与包含相关文件的提交有关......

Then you can retrieve the file from a specific commit as you have the name of the file and the date of the commit, see the two methods below....

然后您可以从特定提交中检索文件,因为您拥有文件名和提交日期,请参阅下面的两种方法....

note: this code is in a .groovy class, so you will no doubt have to amend a little for java.

注意:此代码位于 .groovy 类中,因此您无疑需要为 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
}