Git Bundle

今天我们来认识一下 git bundle 这个命令。

作用

先来简单了解一下这个命令是干嘛的,在官方文档中对这个命令名称的描述翻译过来为:

git-bundle:通过归档移动对象和引用

听起来有点抽象,那我们就不用纠结官方对这个命令的描述了,通俗点讲就是:

用于将本地数据或分支打包到一个文件中,然后共享给别人。

使用场景

那么有哪些使用场景呢,比如:

  • 在网络不通畅时(网络断了或无法直连公司内网),你希望将你的提交通过U盘或邮件等方式传给你项目的协作开发者们。
  • 又或者你现在没有共享服务器的权限,你又希望通过邮件将更新发送给别人,却不希望通过 git format-patch 的方式传输 30 个提交。

像最近由于疫情原因,时不时就需要在家办公,而又不方便直连公司内网拉取代码,直接将整个项目打包成zip又显得笨重还不灵活,一个人开发还好,多个人协作时就不能方便的记录互相之间的修改了。有了 git bundle,我们就可以变得灵活方便了,下面就来看看怎么玩~

命令概要

git bundle create <file> <git-rev-list-args>
git bundle verify <file>
git bundle list-heads <file> [<refname>…]
git bundle unbundle <file> [<refname>…]

这是命令支持的基本操作,下面我们一个个来看。

创建 - Create

这个操作可以将整个分支或指定区间内的提交打包成文件。

file:指生成的文件名;

git-rev-list-args:用于指定打包的引用或提交的区间;

整个分支

$ git bundle create repo_test.bundle master

该命令会生成 repo_test.bundle 文件,该文件中包含了生成 master 分支所需要的所有数据。

指定区间

$ git bundle create repo_test.bundle HEAD dev_v1.0.0 ^master

该命令只打包在 dev_v1.0.0 分支而不在 master 分支的 commits

也可以像这样:

$ git bundle create repo_test.bundle HEAD HEAD~2..HEAD

该命令会将 HEAD~2 (不含) 与 HEAD(含) 之间的提交节点生成文件。

取用 - pull/clone/fetch

通过 git bundle create 命令创建的 bundle 文件我们可以通过 pull/clone 等方式去拉取。

想一下,在正常修改完代码 提交 后,我们是通过 git push 推送到远端仓库,而现在我们因为网络原因而无法直连仓库,所以其实我们可以把这个 bundle 文件当做一个「离线可移动的中转仓库」, git bundle 在这种情况下就相当于 git push 操作,而推送的目标就是生成的这个 bundle 文件,然后将文件发给你的项目协作者,对方通过 git pull repo_test.bundle 来获取你的更新。

clone

通过 clone 命令,可以从 bundle 文件生成一个 git 库。

来看看示例,命令如下:

$ git clone repo_test.bundle test
Cloning into 'bundle'...
Receiving objects: 100% (6/6), done.
warning: remote HEAD refers to nonexistent ref, unable to checkout.

上述命令会从 repo_test.bundle 文件中生成一个 git 仓库,同时解压出的文件会存储于名为 test 的文件夹下。

但上述操作有个 warning,表示 解压失败,因为我们在 git bundle create 打包时没有指定 HEAD 引用,解压出来后不知道该检出(checkout)到哪个分支。可以通过以下两种方式解决:

第一种:在打包时添加 HEAD 选项:
$ git bundle create repo_test.bundle HEAD master

再次执行 clone 操作就可以成功解压,这样解压后,会在本地仓库自动创建一个 master 分支。

第二种:在 clone 时添加 -b 选项:
$ git clone -b master repo_test.bundle dev

注意:此处 -b 新建的分支名,必须与生成 bundle 文件时的分支名一致。

pull

不只是 clone,我们也可以通过 pull/fetch 来操作。

通过 pull 或 fetch 可以从文件中拉取相应的数据,这时候 bundle 文件就相当于远程仓库。

假如你的队友已经有现成的仓库了,只需要拉取你的更新,可以执行:

$ git pull repo_test.bundle

fetch

来看看 fetch 的操作示例:

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

此操作会从 repo_test.bundle 文件中的 master 分支导出到本地的 other-branch 分支中。

执行 git branch,你会发现多了一个 other-branch 分支:

$ git branch
* master
other-branch

基本的使用其实了解以上操作就可以应对我们开头提到的那些场景了,为了知识的完整性,继续来了解一下其他几个操作。

校验 - verify

用来校验生成的包是否合法,也就是说能否正常解压出来。

操作示例:

$ git bundle verify repo_test.bundle
The bundle contains this ref:
1faebfab3ef4e001b032c0c5c96f400c19565076 HEAD
The bundle requires this ref:
506f9bae79d1b82ba245dfa2b24dd8e76d3dc3c6
repo_test.bundle is okay

如上提示 okay 表示是 合法的 bundle 文件

如果不合法会有以下类似的提示:

$ git bundle verify file.bundle
error: 'file.bundle' does not look like a v2 or v3 bundle file

在上述操作中,随便弄了个文件将后缀名改成 .bundle ,所以 不是一个合法的 bundle 文件

列出引用 - list-heads

列出 bundle 包中定义的引用。

比如:

$ git bundle list-heads repo_test.bundle
506f9bae79d1b82ba245dfa2b24dd8e76d3dc3c6 HEAD
506f9bae79d1b82ba245dfa2b24dd8e76d3dc3c6 refs/heads/master

存储引用 - unbundle

将包中的对象传递给 git index-pack 以存储在存储库中,然后打印所有已定义引用的名称。如果给出了引用列表,则仅打印与列表中的引用匹配的引用。

看描述有点抽象,来看看操作示例:

$ git bundle unbundle repo_test.bundle
506f9bae79d1b82ba245dfa2b24dd8e76d3dc3c6 HEAD
506f9bae79d1b82ba245dfa2b24dd8e76d3dc3c6 refs/heads/master

看起来好像和 list-heads 操作一样,但其实 unbundle 给当前仓库 进行了存储索引的操作

一般用来配合其他命令操作,比如我们执行 unbundle 操作后再执行如下命令:

$ git checkout -b dev_v1.0.0 506f9bae79d1b82ba245dfa2b24dd8e76d3dc3c6

此时会根据我们指定的 SHA-1 值创建一个 dev_v1.0.0 分支,而如果你是执行 list-heads 操作后便去执行上述命令则会提示:

fatal: reference is not a tree:506f9bae79d1b82ba245dfa2b24dd8e76d3dc3c6

结语

简单来说,git bundle 的使用场景其实可以理解为:

在网络受限无法直连仓库的情况下,将本地数据推送(打包)到一个 bundle 文件,然后通过邮件或IM软件等方式传送给你的协作者,对方在更新的时候,拉取更新的来源是你的 bundle 文件而不是远端仓库,因为此时 bundle 文件就相当于一个离线的中转仓库。