-
1. 起步
-
2. Git 基礎
-
3. Git 分支
-
4. 伺服器上的 Git
- 4.1 協議
- 4.2 在伺服器上部署 Git
- 4.3 生成 SSH 公鑰
- 4.4 架設伺服器
- 4.5 Git Daemon
- 4.6 Smart HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 第三方託管服務
- 4.10 小結
-
5. 分散式 Git
-
A1. 附錄 A: Git 在其他環境
- A1.1 圖形介面
- A1.2 Visual Studio 中的 Git
- A1.3 Visual Studio Code 中的 Git
- A1.4 IntelliJ / PyCharm / WebStorm / PhpStorm / RubyMine 中的 Git
- A1.5 Sublime Text 中的 Git
- A1.6 Bash 中的 Git
- A1.7 Zsh 中的 Git
- A1.8 PowerShell 中的 Git
- A1.9 小結
-
A2. 附錄 B: 在應用程式中嵌入 Git
-
A3. 附錄 C: Git 命令
A2.2 附錄 B:在您的應用程式中嵌入 Git - Libgit2
Libgit2
您還可以選擇使用 Libgit2。Libgit2 是 Git 的一個無依賴實現,側重於為其他程式提供一個優秀的 API。您可以在 https://libgit2.org 找到它。
首先,讓我們看一下 C API 的樣子。這是一個快速概覽。
// Open a repository
git_repository *repo;
int error = git_repository_open(&repo, "/path/to/repository");
// Dereference HEAD to a commit
git_object *head_commit;
error = git_revparse_single(&head_commit, repo, "HEAD^{commit}");
git_commit *commit = (git_commit*)head_commit;
// Print some of the commit's properties
printf("%s", git_commit_message(commit));
const git_signature *author = git_commit_author(commit);
printf("%s <%s>\n", author->name, author->email);
const git_oid *tree_id = git_commit_tree_id(commit);
// Cleanup
git_commit_free(commit);
git_repository_free(repo);
前幾行程式碼開啟一個 Git 倉庫。git_repository 型別表示一個記憶體快取中的倉庫控制代碼。這是最簡單的方法,適用於您知道倉庫工作目錄或 .git 資料夾的確切路徑的情況。還有 git_repository_open_ext,它包含搜尋選項;git_clone 及其相關函式用於克隆遠端倉庫到本地;以及 git_repository_init 用於建立全新的倉庫。
第二段程式碼使用 rev-parse 語法(有關更多資訊,請參閱 分支引用)來獲取 HEAD 指向的提交。返回的型別是 git_object 指標,它表示倉庫 Git 物件資料庫中存在的內容。git_object 實際上是幾種不同型別物件的“父”型別;每種“子”型別的記憶體佈局都與 git_object 相同,因此您可以安全地轉換為正確的型別。在這種情況下,git_object_type(commit) 會返回 GIT_OBJ_COMMIT,因此可以安全地將其轉換為 git_commit 指標。
下一段程式碼展示瞭如何訪問提交的屬性。這裡的最後一行使用 git_oid 型別;這是 Libgit2 對 SHA-1 雜湊的表示。
從這個示例中,可以開始出現一些模式:
-
如果您宣告一個指標並將其引用傳遞給 Libgit2 呼叫,該呼叫很可能會返回一個整數錯誤程式碼。
0表示成功;任何小於 0 的值都表示錯誤。 -
如果 Libgit2 為您填充了一個指標,您負責釋放它。
-
如果 Libgit2 從呼叫中返回一個
const指標,您不必釋放它,但當它所屬的物件被釋放時,該指標將失效。 -
編寫 C 有點痛苦。
最後一點意味著在使用 Libgit2 時,您不太可能用 C 編寫程式碼。幸運的是,有許多特定語言的繫結可供使用,它們可以輕鬆地從您特定的語言和環境中處理 Git 倉庫。讓我們來看一下使用 Libgit2 的 Ruby 繫結的上述示例,這些繫結名為 Rugged,可以在 https://github.com/libgit2/rugged 找到。
repo = Rugged::Repository.new('path/to/repository')
commit = repo.head.target
puts commit.message
puts "#{commit.author[:name]} <#{commit.author[:email]}>"
tree = commit.tree
如您所見,程式碼更加簡潔。首先,Rugged 使用異常;它可以引發 ConfigError 或 ObjectError 等來指示錯誤條件。其次,沒有顯式的資源釋放,因為 Ruby 是垃圾回收的。讓我們來看一個稍微複雜一點的例子:從頭開始建立一個提交。
blob_id = repo.write("Blob contents", :blob) # (1)
index = repo.index
index.read_tree(repo.head.target.tree)
index.add(:path => 'newfile.txt', :oid => blob_id) # (2)
sig = {
:email => "bob@example.com",
:name => "Bob User",
:time => Time.now,
}
commit_id = Rugged::Commit.create(repo,
:tree => index.write_tree(repo), # (3)
:author => sig,
:committer => sig, # (4)
:message => "Add newfile.txt", # (5)
:parents => repo.empty? ? [] : [ repo.head.target ].compact, # (6)
:update_ref => 'HEAD', # (7)
)
commit = repo.lookup(commit_id) # (8)
-
建立一個新的 blob,其中包含新檔案的內容。
-
使用 head commit 的 tree 填充索引,並在
newfile.txt路徑下新增新檔案。 -
這會在 ODB 中建立一個新的 tree,並將其用於新提交。
-
我們對作者和提交者欄位使用相同的簽名。
-
提交訊息。
-
建立提交時,您必須指定新提交的父提交。這使用了 HEAD 的尖端作為單個父提交。
-
Rugged(和 Libgit2)可以在提交時選擇性地更新引用。
-
返回值是新提交物件(commit object)的 SHA-1 雜湊,然後您可以使用它來獲取一個
Commit物件。
Ruby 程式碼乾淨整潔,但由於 Libgit2 負責繁重的工作,所以程式碼執行速度也會很快。如果您不是 Ruby 開發者,我們將在 其他繫結 中介紹一些其他的繫結。
高階功能
Libgit2 有一些核心 Git 範圍之外的功能。一個例子是可插拔性:Libgit2 允許您為多種操作提供自定義“後端”,因此您可以以不同於標準 Git 的方式儲存內容。Libgit2 允許使用配置、引用儲存和物件資料庫等自定義後端。
讓我們看看這是如何工作的。下面的程式碼摘自 Libgit2 團隊提供的後端示例集(可以在 https://github.com/libgit2/libgit2-backends 找到)。以下是如何設定自定義物件資料庫(ODB)後端。
git_odb *odb;
int error = git_odb_new(&odb); // (1)
git_odb_backend *my_backend;
error = git_odb_backend_mine(&my_backend, /*…*/); // (2)
error = git_odb_add_backend(odb, my_backend, 1); // (3)
git_repository *repo;
error = git_repository_open(&repo, "some-path");
error = git_repository_set_odb(repo, odb); // (4)
請注意,錯誤被捕獲但未處理。我們希望您的程式碼比我們的好。
-
初始化一個空的“前端”物件資料庫(ODB),它將作為執行實際工作的“後端”的容器。
-
初始化一個自定義 ODB 後端。
-
將後端新增到前端。
-
開啟一個倉庫,並設定它使用我們的 ODB 來查詢物件。
但這個 git_odb_backend_mine 是什麼?嗯,那是您自己的 ODB 實現的建構函式,您可以在其中做任何您想做的事情,只要您正確地填充 git_odb_backend 結構。它可能看起來像這樣:
typedef struct {
git_odb_backend parent;
// Some other stuff
void *custom_context;
} my_backend_struct;
int git_odb_backend_mine(git_odb_backend **backend_out, /*…*/)
{
my_backend_struct *backend;
backend = calloc(1, sizeof (my_backend_struct));
backend->custom_context = …;
backend->parent.read = &my_backend__read;
backend->parent.read_prefix = &my_backend__read_prefix;
backend->parent.read_header = &my_backend__read_header;
// …
*backend_out = (git_odb_backend *) backend;
return GIT_SUCCESS;
}
這裡最微妙的限制是 my_backend_struct 的第一個成員必須是一個 git_odb_backend 結構;這確保了記憶體佈局是 Libgit2 程式碼所期望的。其餘部分是任意的;該結構可以根據您的需要設定大小。
初始化函式為結構分配記憶體,設定自定義上下文,然後填充它支援的 parent 結構的成員。請檢視 Libgit2 原始碼中的 include/git2/sys/odb_backend.h 檔案以獲取完整的呼叫簽名集;您特定的用例將有助於確定您想要支援哪些。
其他繫結
Libgit2 提供了許多語言的繫結。這裡我們展示了一個小型示例,使用了截至撰寫本文時一些更完整的繫結包;存在用於 C++、Go、Node.js、Erlang 和 JVM 等多種語言的庫,它們都處於不同的成熟階段。官方的繫結集合可以在 https://github.com/libgit2 的倉庫中找到。我們將編寫的程式碼將返回 HEAD 所指向的提交的訊息(類似於 git log -1)。
LibGit2Sharp
如果您正在編寫 .NET 或 Mono 應用程式,LibGit2Sharp(https://github.com/libgit2/libgit2sharp)是您正在尋找的。這些繫結是用 C# 編寫的,並且花費了大量精力來用原生感覺的 CLR API 包裝原始的 Libgit2 呼叫。這是我們的示例程式的樣子:
new Repository(@"C:\path\to\repo").Head.Tip.Message;
對於桌面 Windows 應用程式,甚至還有一個 NuGet 包可以幫助您快速入門。
objective-git
如果您的應用程式執行在 Apple 平臺上,您很可能使用 Objective-C 作為實現語言。Objective-Git(https://github.com/libgit2/objective-git)是 Libgit2 在該環境中繫結的名稱。示例程式如下:
GTRepository *repo =
[[GTRepository alloc] initWithURL:[NSURL fileURLWithPath: @"/path/to/repo"] error:NULL];
NSString *msg = [[[repo headReferenceWithError:NULL] resolvedTarget] message];
Objective-git 與 Swift 完全相容,所以如果您已經放棄了 Objective-C,也不要害怕。
pygit2
Libgit2 在 Python 中的繫結稱為 Pygit2,可以在 https://www.pygit2.org 找到。我們的示例程式:
pygit2.Repository("/path/to/repo") # open repository
.head # get the current branch
.peel(pygit2.Commit) # walk down to the commit
.message # read the message
延伸閱讀
當然,對 Libgit2 功能的全面介紹超出了本書的範圍。如果您想了解更多關於 Libgit2 本身的資訊,可以在 https://libgit2.github.com/libgit2 找到 API 文件,在 https://libgit2.github.com/docs 找到一系列指南。對於其他繫結,請檢視捆綁的 README 和測試;通常會有小型教程和進一步閱讀的提示。