我正在使用R包git2r与libgit2进行交互。我想获取每次提交中更新的文件列表,类似于git log --stat
或git log --name-only
的输出。但是,我无法获取初始提交中包含的文件。下面我提供代码来设置示例Git存储库以及基于我的研究尝试的解决方案。
下面的代码在/tmp
中创建一个临时目录,创建空文本文件,然后单独提交每个文件。
# Create example Git repo
path <- tempfile("so-git2r-ex-")
dir.create(path)
setwd(path)
# Set the number of fake files
n_files <- 3
file.create(paste0("file", 1:n_files, ".txt"))
library("git2r")
repo <- init(".")
for (i in 1:n_files) {
add(repo, sprintf("file%d.txt", i))
commit(repo, sprintf("Added file %d", i))
}
这个SO post建议您执行diff比较所需提交的树对象及其父提交。这种方法很有效,除了初始提交,因为没有父提交将它与它进行比较。
get_files_from_diff <- function(c1, c2) {
# Obtain files updated in commit c1.
# c2 is the commit that preceded c1.
git_diff <- diff(tree(c1), tree(c2))
files <- sapply(git_diff@files, function(x) x@new_file)
return(files)
}
log <- commits(repo)
n <- length(log)
for (i in 1:n) {
print(i)
if (i == n) {
print("Unclear how to obtain list of files from initial commit.")
} else {
files <- get_files_from_diff(log[[i]], log[[i + 1]])
print(files)
}
}
此SO post建议通过解析提交摘要来获取提交信息,例如更改的文件。这与git log --stat
非常相似,但异常是初始提交。它没有列出任何文件。查看source code,提交摘要中的文件是通过上面相同的方法获得的,这解释了为什么没有为初始提交显示文件(它没有父提交)。
for (i in 1:n) {
summary(log[[i]])
}
这应该是可能的。 Git命令diff-tree
有一个标志--root
,用于将根提交与NULL树(source)进行比较。从手册页:
--root When --root is specified the initial commit will be shown as a big creation event. This is equivalent to a diff against the NULL tree.
此外,libgit2库具有函数git_diff_tree_to_tree,它接受NULL树。不幸的是,我不清楚是否可以通过git2r git2r_diff将NULL树传递给git2r C函数diff method for git-tree objects。有没有办法用git2r创建一个NULL树对象?
> tree()
Error in (function (classes, fdef, mtable) :
unable to find an inherited method for function ‘tree’ for signature ‘"missing"’
> tree(NULL)
Error in (function (classes, fdef, mtable) :
unable to find an inherited method for function ‘tree’ for signature ‘"NULL"’
答案 0 :(得分:0)
我根据同事的见解提出了一个解决方案,您可以通过检查git_tree
对象来获取当前正在跟踪的文件。这显示了到目前为止已跟踪的所有文件,但由于根提交是第一次提交,这意味着必须在该提交中添加这些文件。
摘要方法打印文件,可以使用as
方法捕获此数据框。
summary(tree(log[[n]]))
# mode type sha name
# 1 100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 file1.txt
as(tree(log[[n]]), "data.frame")
# mode type sha name
# 1 100644 blob e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 file1.txt
以下函数从根提交中获取文件。虽然在这个小例子中并不明显,但主要的复杂因素是子目录被表示为树,因此您需要递归搜索树以获取所有文件名。
obtain_files_in_commit_root <- function(repo, commit) {
# Obtain the files in the root commit of a Git repository
stopifnot(class(repo) == "git_repository",
class(commit) == "git_commit",
length(parents(commit)) == 0)
entries <- as(tree(commit), "data.frame")
files <- character()
while (nrow(entries) > 0) {
if (entries$type[1] == "blob") {
# If the entry is a blob, i.e. file:
# - record the name of the file
# - remove the entry
files <- c(files, entries$name[1])
entries <- entries[-1, ]
} else if (entries$type[1] == "tree") {
# If the entry is a tree, i.e. subdirectory:
# - lookup the entries for this tree
# - add the subdirectory to the name so that path is correct
# - remove the entry from beginning and add new entries to end of
# data.frame
new_tree_df <- as(lookup(repo, entries$sha[1]), "data.frame")
new_tree_df$name <- file.path(entries$name[1], new_tree_df$name)
entries <- rbind(entries[-1, ], new_tree_df)
} else {
stop(sprintf("Unknown type %s found in commit %s",
entries$type[1], commit))
}
}
return(files)
}
obtain_files_in_commit_root(repo, log[[n]])
# [1] "file1.txt"