章節 ▾ 第二版

7.12 Git 工具 - 打包

打包

儘管我們已經介紹了透過網路傳輸 Git 資料(HTTP、SSH 等)的常用方法,但還有一種不常用但實際上可能非常有用。

Git 能夠將資料“打包”到一個單獨的檔案中。這在各種場景下都很有用。也許你的網路中斷了,想把更改傳送給同事。也許你在異地工作,出於安全原因無法訪問本地網路。也許你的無線/乙太網卡壞了。也許你暫時沒有訪問共享伺服器的許可權,想透過電子郵件傳送更新,又不想透過 format-patch 傳輸 40 個提交。

這時 git bundle 命令就能派上用場。bundle 命令會將透過 git push 命令通常會在網路上傳輸的所有內容打包成一個二進位制檔案,你可以將其透過電子郵件傳送給他人,或將其放在 U 盤上,然後在另一個倉庫中解包。

讓我們來看一個簡單的例子。假設你有一個包含兩個提交的倉庫。

$ git log
commit 9a466c572fe88b195efd356c3f2bbeccdb504102
Author: Scott Chacon <schacon@gmail.com>
Date:   Wed Mar 10 07:34:10 2010 -0800

    Second commit

commit b1ec3248f39900d2a406049d762aa68e9641be25
Author: Scott Chacon <schacon@gmail.com>
Date:   Wed Mar 10 07:34:01 2010 -0800

    First commit

如果你想將該倉庫傳送給某人,並且你無法訪問用於推送的倉庫,或者根本不想設定一個,你可以使用 git bundle create 命令進行打包。

$ git bundle create repo.bundle HEAD master
Counting objects: 6, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (6/6), 441 bytes, done.
Total 6 (delta 0), reused 0 (delta 0)

現在你得到了一個名為 repo.bundle 的檔案,它包含了重新建立倉庫 master 分支所需的所有資料。使用 bundle 命令,你需要列出所有要包含的引用或特定提交範圍。如果你打算在其他地方克隆它,你應該像我們在這裡一樣,將 HEAD 新增為引用。

你可以將這個 repo.bundle 檔案透過電子郵件傳送給其他人,或者將其放在 U 盤上,然後親自送過去。

在另一端,假設你收到了這個 repo.bundle 檔案,並想在此專案上進行工作。你可以像從 URL 克隆一樣,從這個二進位制檔案中克隆到另一個目錄。

$ git clone repo.bundle repo
Cloning into 'repo'...
...
$ cd repo
$ git log --oneline
9a466c5 Second commit
b1ec324 First commit

如果你在引用中沒有包含 HEAD,你還需要指定 -b master 或其他包含的分支,否則它將不知道要檢出哪個分支。

現在,假設你在該倉庫上進行了三個提交,並想透過 U 盤或電子郵件打包將新提交發回去。

$ git log --oneline
71b84da Last commit - second repo
c99cf5b Fourth commit - second repo
7011d3d Third commit - second repo
9a466c5 Second commit
b1ec324 First commit

首先,我們需要確定要包含在包中的提交範圍。與網路協議為我們確定要透過網路傳輸的最少量資料不同,我們必須手動確定這一點。你也可以像之前一樣打包整個倉庫,這也能奏效,但最好只打包差異——只打包我們剛剛在本地做的三個提交。

要做到這一點,你需要計算差異。正如我們在 提交範圍 中描述的那樣,你可以用多種方式指定提交範圍。要獲取我們在 master 分支中有但不在我們最初克隆的分支中的三個提交,我們可以使用類似 origin/master..mastermaster ^origin/master 的命令。你可以使用 log 命令進行測試。

$ git log --oneline master ^origin/master
71b84da Last commit - second repo
c99cf5b Fourth commit - second repo
7011d3d Third commit - second repo

既然我們有了要包含在包中的提交列表,我們就來打包它們。我們使用 git bundle create 命令,給它一個我們想要的包檔名以及要包含的提交範圍。

$ git bundle create commits.bundle master ^9a466c5
Counting objects: 11, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (9/9), 775 bytes, done.
Total 9 (delta 0), reused 0 (delta 0)

現在我們的目錄中有一個 commits.bundle 檔案。如果我們將其傳送給合作伙伴,她就可以將其匯入到原始倉庫中,即使在那期間有更多工作已經完成。

當她收到包時,她可以在匯入到她的倉庫之前檢查它以檢視其中包含的內容。第一個命令是 bundle verify 命令,它將確保檔案實際上是一個有效的 Git 包,並且你擁有所有必要的祖先以正確地重建它。

$ git bundle verify ../commits.bundle
The bundle contains 1 ref
71b84daaf49abed142a373b6e5c59a22dc6560dc refs/heads/master
The bundle requires these 1 ref
9a466c572fe88b195efd356c3f2bbeccdb504102 second commit
../commits.bundle is okay

如果打包器只建立了最近兩次提交的包,而不是全部三次,那麼原始倉庫將無法匯入它,因為它缺少必需的歷史記錄。verify 命令的顯示會是這樣:

$ git bundle verify ../commits-bad.bundle
error: Repository lacks these prerequisite commits:
error: 7011d3d8fc200abe0ad561c011c3852a4b7bbe95 Third commit - second repo

但是,我們的第一個包是有效的,所以我們可以從中獲取提交。如果你想檢視包中可以匯入的分支,還有一個命令可以只列出頭節點。

$ git bundle list-heads ../commits.bundle
71b84daaf49abed142a373b6e5c59a22dc6560dc refs/heads/master

verify 子命令也會告訴你頭節點。重點是檢視可以拉取的內容,所以你可以使用 fetchpull 命令從這個包匯入提交。這裡我們將包中的 master 分支提取到我們倉庫中一個名為 other-master 的分支。

$ git fetch ../commits.bundle master:other-master
From ../commits.bundle
 * [new branch]      master     -> other-master

現在我們可以看到,在 other-master 分支上有了匯入的提交,以及我們在自己的 master 分支上同時進行的任何提交。

$ git log --oneline --decorate --graph --all
* 8255d41 (HEAD, master) Third commit - first repo
| * 71b84da (other-master) Last commit - second repo
| * c99cf5b Fourth commit - second repo
| * 7011d3d Third commit - second repo
|/
* 9a466c5 Second commit
* b1ec324 First commit

所以,當沒有適當的網路或共享倉庫時,git bundle 對於共享或進行網路類操作來說真的非常有用。