2013-09-26 47 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

相關問題