题
我设法在连接仓库,添加,提交甚至循环的文件消息的情况下,掌握了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() );
}
我想做的下一步是能够获取单个文件的所有提交消息,然后能够将单个文件恢复到特定的参考/时间点。
解决方案
这是如何根据所有父母提交的方式找到提交的更改
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适用于单个树步行者,并将返回root commit中可用的所有元素。
然后,您必须在树上迭代,以查看您的文件是否在给定的三角洲(这很容易)。
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是正确的。在合并的情况下,与所有亲本树相比,斑点没有改变,这有点违反直觉。因此,这是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代码,源自Anydiffffilter)
其他提示
因此,我试图获得查理博伊(Charlieboy)的工作解决方案,但大多数情况下确实如此,但是在下面的情况下,这对我来说是失败的(自从该帖子以来,JGIT中有些事情发生了变化吗?)
添加filea,提交为“ commit 1”添加fileb,提交为“ 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或树找到整个子树的历史)。因此,从本质上讲,修订版Walker在修订集输出上充当过滤器。
JGIT文档似乎...稀疏。但是,您应该能够获得与每个RevCommit相对应的REVTREE,并且如果必要在每个路径段中遍历该revtree,则转到终端对象ID。
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
}