我正在尝试使用旧版本的libgit2(没有checkout.h)实现类似checkout的功能。
首先,我在分支A上,看起来像:
Branch:
A A0 --- A1
/
Master M
每次提交都会创建一个具有相同名称的文件,例如,标记为A1的提交会创建一个文件A1。如果我在这一点上看gitk,一切看起来都是我想要的方式。
现在我创建了一个新的分支B
,我想添加一个提交:
Branch:
A A0 --- A1
/
Master M
\
B B0
但是,当我使用我的代码“结帐”B时,它会使A0和A1未跟踪而不是删除它们,正如我所期望的那样:
(B)$ git status
# On branch B
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
#
# A0
# A1
nothing added to commit but untracked files present (use "git add" to track)
所以,我认为我的结帐代码缺少一些东西,这就是:
void Checkout(const char *branch_name, git_commit *branch_tip) {
// Update index & tree
git_tree *tree;
git_commit_tree(&tree, branch_tip);
git_index_read_tree(index_, tree);
git_index_write(index_);
git_tree_free(tree);
// Reset head
string branch_ref = string("refs/heads/") + branch_name;
git_reference *head;
git_reference_lookup(&head, repo_, kGitHeadFile);
git_reference_set_target(head, branch_ref.c_str());
git_reference_free(head);
}
(请注意,我实际上正在检查实际代码中每一行的返回代码,并且所有内容都返回0,只是不想在这里混乱。)
据我所知,此代码与git文档描述git checkout <branch>
:
To prepare for working on <branch>, switch to it by updating
the index and the files in the working tree, and by pointing
HEAD at the branch.
是否有一些......“更新工作树”命令我需要运行吗?
答案 0 :(得分:3)
如果您必须自己编写,可以查看libgit2中现有实现使用的基本策略。让我们考虑实施强制结账(即忽略工作目录中的任何修改过的文件),因为这是一个更简单的案例。
你还没有提到你的libgit2有多大。我将编写以下内容,假设您可以访问diff功能,我甚至会使用一些稍微更新的访问器函数来实现差异数据。如果您的版本中没有这些访问器功能,则可能必须重新设置此功能才能使用回调功能。如果核心差异功能不可用,那么你的libgit2太老了,我相信。
您需要考虑您来自的旧HEAD以及您要移动的新HEAD,以便了解哪些文件将被删除(与工作目录中未跟踪的文件相比)。在libgit2中最简单的事情就是:
git_diff_list *diff;
git_diff_delta *delta;
git_blob *blob;
size_t i;
FILE *fp;
git_diff_tree_to_tree(&diff, repo, from_tree, to_tree, NULL);
for (i = 0; i < git_diff_num_deltas(diff); ++i) {
git_diff_get_patch(NULL, &delta, diff, i);
switch (delta->status) {
case GIT_DELTA_ADDED:
case GIT_DELTA_MODIFIED:
/* file was added or modified between the two commits */
git_blob_lookup(&blob, repo, &delta->new_file.oid);
fp = fopen(delta->new_file.path, "w");
fwrite(git_blob_rawdata(blob), git_blob_rawsize(blob), 1, fp);
fclose(fp);
git_blob_free(blob);
break;
case GIT_DELTA_DELETED:
/* file was removed between the two commits */
unlink(delta->old_file.path);
break;
default:
/* no change required */
}
}
git_diff_list_free(diff);
/* now update the index with the tree we just wrote out */
git_index_read_tree(index, to_tree);
git_index_write(index);
/* and do the other stuff you have to update the HEAD */
上面的实际代码存在很多问题,您必须解决这些问题:
delta->new_file.path
和delta->old_file.path
中的路径相对于存储库的工作目录,而不是进程的当前工作目录,因此打开和取消链接文件的调用需要调整相应的路径根据您的使用情况,也许可以忽略类型更改(即变为blob的目录等),并且可能可以接受仿真--force
。如果没有,那么这真的开始变成很多代码。
答案 1 :(得分:2)
您没有说明为什么您不能升级到最近的支持结帐的libgit2,以便您可以致电:
git_checkout_head(repo, NULL);
所以我要说:将你的libgit2升级到更新的东西。你会非常不满意继续使用旧版本。特别是,结账不是您想要自己实现的功能。 (看看libgit2&#39; s checkout.c
。)
但回答你的问题,基本方法是:
将工作目录与HEAD
指向的树进行比较。收集任何不同项目的列表,理想情况下使用缓存,这样就不需要计算索引和workdir之间明显不变的文件的哈希值。请确保从配置中加载过滤器,并根据需要应用您自己的版本,因为过滤器未在任何缺少结帐的libgit2版本中公开。
对于该列表中的每个项目,将数据写入磁盘。请务必根据需要应用任何过滤器。同样,您必须推出自己的过滤器。
更新索引以反映您使用git_index_add_bypath
写入文件系统的内容。
libgit2 checkout documentation中提供了一组非常详细的信息。