Цикл по коммитам для файла с jGit - PullRequest
9 голосов
/ 24 января 2011

Мне удалось разобраться с основами файла 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 ]

10 голосов
/ 02 мая 2011

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

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

(скалярный код)

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

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

Возможно, вы захотите использовать фильтр, отличный от ANY_DIFF, поскольку ANY_DIFF имеет значение true, если одно дерево отличается. Это немного нелогично в случае слияния, когда 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)

7 голосов
/ 25 августа 2012

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

добавить файл A, зафиксировать как "commit 1" добавить файл B, зафиксировать как «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));
}

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

2 голосов
/ 24 января 2011

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

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

1 голос
/ 14 апреля 2011

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
}
...