Зацикливание коммитов для файла с jGit

мне удалось разобраться с основами 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() );

}

то, что я хочу сделать дальше, - это получить все сообщение фиксации для одного файла, а затем вернуть один файл обратно к определенной ссылке/точке во времени.

4 ответов


вот как найти изменения фиксации на основе всех родительских коммитов

        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 работает для одного дерева и возвращает все элементы, доступные в корневой фиксации.

затем вам придется перебирать дерево, чтобы увидеть, находится ли ваш файл в данной дельте (это довольно легко).

    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-это чистый путь в репо, разделенный на'/')

Теперь оберните этот код в Ревуок.следующий цикл, и вы получите коммиты и файлы, измененные фиксацией.

вы можете использовать другой фильтр, чем 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)


поэтому я попытался заставить решение charlieboy работать, и это в основном так, но это не удалось для меня в следующем случае (может быть, что-то изменилось в jgit с этого поста?)

добавить fileA, commit как " commit 1" добавить fileB, commit как "commit 2"

getFileVersionDateList("fileA")

и commit 1 и commit 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));
}

очевидно, вы также проверяете даты фиксации и т. д. По мере необходимости.


общий подход с git, чтобы найти историю конкретного файла, состоит в том, чтобы пройти через график ревизий (который вы делаете) и для каждого из них проверить объект, на который ссылается путь (может быть либо blob, либо дерево, чтобы найти историю для всего поддерева). Поэтому по существу действуйте как фильтр на выходе набора ревизий с помощью ходока ревизий.

документация jgit кажется... редкий. Но вы должны иметь возможность получить RevTree, соответствующий каждому RevCommit, и если обязательно пройти через это с каждым сегментом пути в свою очередь до терминального идентификатора объекта.


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
}