2013-09-26 37 views
1

我试图用旧版本libgit2(无checkout.h)实施结账一样的功能。如何在使用libgit2签出分支时更新工作树?

首先,我对分支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. 

有一些...“更新工作树”命令我需要运行?

回答

3

如果您必须亲自编写此代码,则可以查看libgit2中现有实现使用的基本策略。让我们考虑实现一个强制检出(即忽略工作目录中的任何修改过的文件),因为这是一个更简单的情况。

你还没有提到你的libgit2有多大。假设你可以访问diff功能,我会写下面的内容,我甚至会使用一些比较新近的访问器函数来处理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 */ 

有许多与上述实际代码问题,你必须解决:

  1. 的路径在delta->new_file.pathdelta->old_file.path相对于存储库的工作目录,而不是进程的当前工作目录,因此打开和取消链接文件的调用将需要相应地调整路径。
  2. 代码根本不处理目录。打开文件之前,您必须制作包含该文件的目录。删除文件后,如果它是目录中的最后一个文件,则必须删除包含该文件的目录。如果你有一个目录变成普通文件的分支,反之亦然,你必须在添加之前处理删除。
  3. 该代码不会执行任何错误检查,这是一个坏主意
  4. 此代码忽略索引和工作目录中的修改的未决更改。但我们正在谈论强制结账,所以你会得到你所得到的。
  5. 我只是写在上面的代码把我的头顶部,因此可能会有错别字等

根据你的使用情况,也许一切都会好的忽略类型的变化(即目录,成为斑点等),也许模拟--force将是可以接受的。如果没有,那么这真的开始变成很多代码。

+0

我忘了提及该代码也不会将CRLF过滤应用于blob数据,但如果您不在最近的libgit2上,那么您可能不太会用正确的方式处理它。希望你在一个不需要它的平台上。 – arrbee

2

你没有说明具体原因,你不能只升级到最新libgit2是支持结账,这样你可以只要致电:

git_checkout_head(repo, NULL); 

所以我要说:升级libgit2更近一些。你会非常不满意继续使用旧版本。结账,特别是,不是你想要实现自己的功能。 (看看libgit2的checkout.c

但是,为了回答你的问题,基本方法是:

  1. 比较工作目录树通过HEAD指向。理想情况下,通过使用缓存,这样,您不必计算上有明显的指数和WORKDIR之间不变的文件哈希收集的是不同的任何项目,列表。一定要装入过滤掉的配置和应用自己的人的版本,如不合适的,因为过滤器不libgit2的任何版本,缺乏结账公开曝光。

  2. 该列表中的每一个项目,将数据写入磁盘。一定要适当地使用任何过滤器。再次,你将不得不推出你自己的过滤器。

  3. 更新的指数,以反映您在使用git_index_add_bypath写信给文件系统。

libgit2 checkout documentation中提供了一组非常详细的信息。

+0

正如我所提到的,我不能使用git_checkout_ *函数。我正试图实现我自己的。 – kristina

+0

哈。我会更新我的答案。 –

+0

男人,我希望能有一个简单的命令,我可以运行。尽管如此,感谢您的详细信息。 (并且相信我,如果可以的话我会升级。) – kristina

相关问题