包(bundle)与归档
把一个仓库塞进单个文件,有两种办法。它们看上去可以互换,其实不能,而挑错了那一个,正是这一页存在的全部理由。
| 一个包(bundle) | 一个归档 | |
|---|---|---|
| 装的是 | 历史:提交、分支、标签 | 某一个提交上的那些文件 |
| 用什么打开 | git clone / git fetch——它就是一个远程 |
任何解压工具 |
| 之后 | 你还能再从它获取、合并、继续干活 | 什么也没有。它是一张快照 |
| 用在 | 把工作搬到一台没有网络的机器上 | 「把 v2.1 的源码发我」 |
⌘K → 打包仓库或导出归档。

包
包(bundle)是 git 对一道任何网络都跨不过去的沟壑给出的答案:一台物理隔离的机器、一支 U 盘、一个邮件附件、飞机上的一台笔记本。接收的那一端跑一句 git clone work.bundle myrepo,就得到一个真正的仓库,带着你的历史和你的分支,并且能从那个文件里获取,仿佛它是一台服务器。
三种范围:
| 范围 | 什么会被带走 | 大小 |
|---|---|---|
| 全部 | 每一个分支和标签,完整历史 | 整个仓库 |
| 某一个分支或标签 | 那个 ref 以及它能到达的一切 | 通常是其中的大部分 |
| 一段提交区间 | 只有两端之间的东西 | 很小 |
区间包是一块历史的补丁,不是一个仓库。 它把远的那一端记为前置条件:git 拒绝在一个还没有那个提交的仓库里打开它,因为那样新的提交就无处可挂。这是正确的行为,也是头一回遇上时的意外。当对方已经拿到了你到某个点为止的工作时——他们上次收到的那个标签、你们共同分出来的那个提交——就该用区间。
收到一个包
导入包… 会读取这个文件,列出它装了什么,并且一上来就告诉你这个仓库能不能用它——如果缺少前置条件,它会告诉你缺几个,而不是等到后面用 git 自己的措辞失败。
导入进来的 ref 落在 bundle/… 下面,也就是远程跟踪的命名空间里。本地什么都不动:没有分支被快进,没有工作被覆盖。然后你按自己的节奏去合并、变基或检出 bundle/main,跟对待任何别的远程上的分支完全一样。
如果你想从一个包新建一个仓库,那就在终端里从这个文件克隆:git clone work.bundle myrepo。Gitcito 是导入到一个已打开的仓库里;它不从文件克隆。
归档
git archive 会把某一个提交上的那棵树写成一个 zip 或者一个 tarball。没有 .git,没有历史,也没法从它获取——而当接收方该拿到的是源代码、不是仓库时,这恰恰就是重点。
| 选项 | 它做什么 |
|---|---|
| 引用 | 要导出的分支、标签或提交。通常答案是一个标签 |
| 格式 | zip、tar.gz 或 tar |
| 包进一个目录 | 加一层顶层文件夹,这样解包时就不会把文件撒得到处都是 |
| 只要这个路径 | 只导出一个子目录,而不是整棵树 |
export-ignore 才是用它的理由
一个仓库可以在 .gitattributes 里标记路径:
.github/ export-ignore
test/ export-ignore
*.psd export-ignore
这些路径会被排除在每一个归档之外,同时仍然留在仓库里。一个项目就是这样发出一个不含 CI 配置、不含测试夹具、不含那 200 MB 设计文件的发布 tarball 的,而且这条规则住在仓库里,不住在某个人的发布脚本里。
值得知道的限制
- 包是一份完整副本,除非你用区间。给一个 2 GB 的仓库打包会写出一个 2 GB 的文件,耗时和一次克隆差不多。
- 空的包会被拒绝,拒绝它的是 git 而不是 Gitcito:两端之间什么都没有的区间会产生一个错误,而不是一个没用的文件。
- 导入不做合并。 ref 到达
bundle/…之后就待在那儿,直到你拿它们做点什么。 - 归档没有历史,所以它变不回一个仓库。如果接收方之后还需要提交,那就发一个包。
export-ignore只影响归档。 它不会对克隆、对包、对历史藏起任何东西——要做那件事,见从历史中移除一个文件。
另请参阅:同步 · 安全分享 · 从历史中移除一个文件