使用jGit循环提交文件

时间:2011-01-24 00:06:27

标签: java jgit

我已经成功地掌握了jGit文件的基础知识,包括连接到repos以及添加,提交甚至循环文件的提交消息。

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() );

}

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

4 个答案:

答案 0 :(得分:10)

以下是如何根据所有父提交找到提交的更改

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

(scala代码)

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

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

    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是纯粹的repo路径,由'/'分隔)

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

您可能希望使用与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代码,派生自AnyDiffFilter)

答案 1 :(得分:7)

所以我试图让charlieboy的解决方案起作用,而且大多数都是这样,但在以下情况下我失败了(自那篇文章以来jgit中可能有些变化?)

添加fileA,提交为“commit 1” 添加fileB,提交为“commit 2”

getFileVersionDateList("fileA")

找到了commit 1commit 2,我只期望commit 1

我的解决方案如下:

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));
}

使用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));
}

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

答案 2 :(得分:2)

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

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

答案 3 :(得分:1)

araqnid是对的,这就是我得到日期列表的方式,每个日期都与提交相关,包括有问题的文件......

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

注意:此代码位于.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
}