## 打包
雖然我們已經了解了網絡傳輸 Git 數據的常用方法(如 HTTP,SSH 等),但還有另外一種不太常見卻又十分有用的方式。
Git 可以將它的數據 “打包” 到一個文件中。 這在許多場景中都很有用。 有可能你的網絡中斷了,但你又希望將你的提交傳給你的合作者們。 可能你不在辦公網中并且出于安全考慮沒有給你接入內網的權限。 可能你的無線、有線網卡壞掉了。 可能你現在沒有共享服務器的權限,你又希望通過郵件將更新發送給別人,卻不希望通過?`format-patch`?的方式傳輸 40 個提交。
這些情況下?`git bundle`?就會很有用。?`bundle`?命令會將?`git push`?命令所傳輸的所有內容打包成一個二進制文件,你可以將這個文件通過郵件或者閃存傳給其他人,然后解包到其他的倉庫中。
來看看一個簡單的例子。 假設你有一個包含兩個提交的倉庫:
~~~
$ 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
Initialized empty Git repository in /private/tmp/bundle/repo/.git/
$ cd repo
$ git log --oneline
9a466c5 second commit
b1ec324 first commit
~~~
如果你在打包時沒有包含 HEAD 引用,你還需要在命令后指定一個?`-b master`?或者其他被引入的分支,否則 Git 不知道應該檢出哪一個分支。
現在假設你提交了 3 個修訂,并且要用郵件或者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
~~~
首先我們需要確認我們希望被打包的提交區間。 和網絡協議不太一樣,網絡協議會自動計算出所需傳輸的最小數據集,而我們需要手動計算。 當然你可以像上面那樣將整個倉庫打包,但最好僅僅打包變更的部分 —— 就是我們剛剛在本地做的 3 個提交。
為了實現這個目標,你需要計算出差別。 就像我們在?[提交區間](http://git-scm.com/book/zh/v2/ch00/_commit_ranges)?介紹的,你有很多種方式去指明一個提交區間。 我們可以使用?`origin/master..master`?或者?`master ^origin/master`?之類的方法來獲取那 3 個在我們的 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`?子命令同樣可以告訴你有哪些頂端。 該功能的目的是查看哪些是可以被拉入的,所以你可以使用?`fetch`?或者?`pull`?命令從包中導入提交。 這里我們要從包中取出?`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`?很適合用于共享或者網絡類型的操作。
- 前言
- Scott Chacon 序
- Ben Straub 序
- 獻辭
- 貢獻者
- 引言
- 1. 起步
- 1.1 關于版本控制
- 1.2 Git 簡史
- 1.3 Git 基礎
- 1.4 命令行
- 1.5 安裝 Git
- 1.6 初次運行 Git 前的配置
- 1.7 獲取幫助
- 1.8 總結
- 2. Git 基礎
- 2.1 獲取 Git 倉庫
- 2.2 記錄每次更新到倉庫
- 2.3 查看提交歷史
- 2.4 撤消操作
- 2.5 遠程倉庫的使用
- 2.6 打標簽
- 2.7 Git 別名
- 2.8 總結
- 3. Git 分支
- 3.1 分支簡介
- 3.2 分支的新建與合并
- 3.3 分支管理
- 3.4 分支開發工作流
- 3.5 遠程分支
- 3.6 變基
- 3.7 總結
- 4. 服務器上的 Git
- 4.1 協議
- 4.2 在服務器上搭建 Git
- 4.3 生成 SSH 公鑰
- 4.4 配置服務器
- 4.5 Git 守護進程
- 4.6 Smart HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 第三方托管的選擇
- 4.10 總結
- 5. 分布式 Git
- 5.1 分布式工作流程
- 5.2 向一個項目貢獻
- 5.3 維護項目
- 5.4 總結
- 6. GitHub
- 6.1 賬戶的創建和配置
- 6.2 對項目做出貢獻
- 6.3 維護項目
- 6.4 管理組織
- 6.5 腳本 GitHub
- 6.6 總結
- 7. Git 工具
- 7.1 選擇修訂版本
- 7.2 交互式暫存
- 7.3 儲藏與清理
- 7.4 簽署工作
- 7.5 搜索
- 7.6 重寫歷史
- 7.7 重置揭密
- 7.8 高級合并
- 7.9 Rerere
- 7.10 使用 Git 調試
- 7.11 子模塊
- 7.12 打包
- 7.13 替換
- 7.14 憑證存儲
- 7.15 總結
- 8. 自定義 Git
- 8.1 配置 Git
- 8.2 Git 屬性
- 8.3 Git 鉤子
- 8.4 使用強制策略的一個例子
- 8.5 總結
- 9. Git 與其他系統
- 9.1 作為客戶端的 Git
- 9.2 遷移到 Git
- 9.3 總結
- 10. Git 內部原理
- 10.1 底層命令和高層命令
- 10.2 Git 對象
- 10.3 Git 引用
- 10.4 包文件
- 10.5 引用規格
- 10.6 傳輸協議
- 10.7 維護與數據恢復
- 10.8 環境變量
- 10.9 總結
- A. 其它環境中的 Git
- A1.1 圖形界面
- A1.2 Visual Studio 中的 Git
- A1.3 Eclipse 中的 Git
- A1.4 Bash 中的 Git
- A1.5 Zsh 中的 Git
- A1.6 Powershell 中的 Git
- A1.7 總結
- B. 將 Git 嵌入你的應用
- A2.1 命令行 Git 方式
- A2.2 Libgit2
- A2.3 JGit
- C. Git 命令
- A3.1 設置與配置
- A3.2 獲取與創建項目
- A3.3 快照基礎
- A3.4 分支與合并
- A3.5 項目分享與更新
- A3.6 檢查與比較
- A3.7 調試
- A3.8 補丁
- A3.9 郵件
- A3.10 外部系統
- A3.11 管理
- A3.12 底層命令