跳转到内容

Git/Git常见用法

来自ACM Class Wiki
< Git

此文档列举了一些版本管理的需求及对应的 Git 用法。其中会涉及一些 Git 核心概念(比如远程仓库,commit,引用等),请先了解这些概念后阅读此文档。Git 文档中列举了一些核心概念的意义,具体见 Git 文档的 Git 中的常见概念章节。

本文档没有涵盖所有功能,如果需要了解 Git 子命令的全部用法,请使用以下命令:(请将下面的 subcommand 替换成子命令,如 remote)

man 1 git-<subcommand>

创建仓库

创建仓库主要有两种方法:

创建仓库后,所有的操作都可以在仓库的根目录及其子目录下操作(不需要都在项目根目录下进行)。

直接创建本地仓库

在项目的根目录执行

git init

此操作会初始化一个 Git 仓库环境。(注意此时不会有任何 commit 产生)

如需要与远程仓库关联,请参阅远程仓库元数据操作章节。

从远程仓库复制

执行

git clone <repository> [<directory>]

此操作会将对应的仓库复制到指定的目录(如没有指定,则会以某个方便的名称,通常是仓库的名字),并建立远程跟踪分支。

Git clone 省去了手动设定远程分支的操作,因此在实践中较为推荐。

此外,对于有编辑权限的仓库,推荐使用 ssh 协议的 URL。

显示当前状态

在项目中执行

git status

即可显示当前的状态,其包含仓库的绝大多数状态。

此命令非常重要,当遇到任何困难时,都可以执行此命令来获取提示。

远程仓库相关

远程仓库元数据操作

命令 用途 例子
git remote [-v | --verbose] show 列举所有远程分支(如加上了 -v--verbose,则会显示具体 URL)
git remote show <remote_branch> 显示远程分支的详细信息 git remote show origin
git remote add <name> <URL> 添加远程仓库 git remote add github git@github.com:abc/abc.git
git remote set-url <name> <URL> 修改远程仓库,与 git remote add 类似 git remote set-url github git@github.com:abc/abc.git
git remote remove <name> 删除远程仓库 git remote remove github
git remote rename <old> <new> 修改远程仓库名称 git remote rename github gh

拉取远程仓库数据

在项目中执行

git fetch [<name>]

即可拉取 <name>(如果没有指定,则为 origin)对应仓库的最新版本。

将远程分支同步到本地分支

在项目中执行

git pull [<name>]

即可将 <name>(如果没有指定,则为 origin)对应仓库的最新版本的对应分支同步到本地。

此步骤中,相当与完成了两个操作

  1. 拉取远程仓库数据,相当于 git fetch <name>
  2. 尝试将本地分支合并到远程分支。

由于不同版本的 Git 有不同的默认合并行为,因此除非可以保证本地分支完全是基于远程分支的,否则不建议使用 git push,推荐 git fetch 后采用合适的合并手段(参见合并修改章节)。

此外,如果在执行 git pull 前有未提交的变更,git pull 可能会报错(当远程分支也修改了这些文件时)。推荐做法是暂时储藏修改

将本地分支推送到远程分支

在项目中执行

git push [<name>]

即可将当前分支推送到 <name>(如果没有指定,则为 origin)对应仓库的对应分支。(如果之前没有推送过,你需要设置对应的远程分支,Git 会提示你应该如何操作)

注意:推送到远程仓库时,如果不使用 --force-f 参数,那么只有在远程仓库可以 fast-forward 你的所有提交链时才可顺利提交。

删除远程分支

在项目中执行

git push <repo> --delete <branch>

即可将远程仓库 <repo> 中的 <branch> 删除。

多分支(引用)相关

新增本地分支

在项目中执行

git branch <new-branch>

以在当前 HEAD 位置新增一个名为 <new-branch> 的分支。

提示:通常情况下,我们可以通过合理的命名来明确分支的作用,比如 feat/psql 表示实现 postgreSQL 的分支,chore/bump-version 表示用于测试修改版本后的兼容性的分支。

删除本地分支

在项目中执行

git branch --delete <branch>

以删除对应的分支(也可将 --delete 改成 -d)。如果分支和上游不一致,那么此操作可能会失败。

如果因为某些原因,上述的操作失败了,那么可以用下面的命令来强制删除。(也可将 --delete --force 改成 -D

git branch --delete --force <branch>

修改本地分支名称

在项目中输入

git branch -m <old-branch> <new-branch>

即可将 old-branch 改名为 <new-branch>(-m 代表 move,也可以用 --move 代替)。

合并修改

在所有的合并修改中,请在自己的分支合并其他分支。

Git 提供了两种主要合并方法:

这两个合并方法各有优势,且工作流有明显区别(具体请看常用合并工作流部分),请按照个人喜好或上游要求选择合适的合并方法。

在合并前,如果有未提交的变更,可能会导致无法合并,推荐做法是暂时储藏修改

当合并时出现了双方修改,就会出现冲突。Git 有一套内建的合并策略,可以将不同来源的修改应用到一起,比如有两个来源的修改,分别修改了同一文件,两处修改距离较远,那么 Git 就可以将这两个变更合并起来。但是在一些情况下,仍然无法自动解决冲突,这时需要手动解决冲突

Merge 方式

在项目中输入

git merge [<commit-or-ref>...]

即可将指定的提交合并到当前分支(如果没有指定,则为当前分支对应的远程分支)。

merge 的行为分大致为下面两种情况:

  • 可以 fast forward 的(可以用 --ff-only 参数强制使用 fast forward),即被合并的分支是在当前 HEAD 的子提交。此时会直接将 HEAD 和当前所在的分支移动到 C。比如下图是合并 feat 的示意图:
      master,HEAD    feat
...---A-----------B---C
  • 不可以 fast forward。此时会产生一个新的提交,其会指向当前位置及所有指定的提交。比如下图是合并 feat 的示意图:
      A---B---C feat                A---B---C feat 
     /                   --->      /         \
D---E---F---G master,HEAD     D---E---F---G---H master,HEAD

如果只想要合并变更,但是不想要出现一个指向多个提交的新提交,可以通过 --squash 来只操作变更,不提交变更。

如果操作时出现了冲突,请参见解决冲突章节。

优点:

  • 不会影响原有的提交(包括提交者、作者和签名)。

缺点:

  • 提交的继承关系比较复杂。
  • 当被合并的提交与当前差异较大时,出现的冲突相比 rebase 方式更难以处理。因为 rebase 是逐个提交处理的,每个提交的改动量和改动主题较小。

Rebase 方式

在项目中输入

git rebase <commit-or-ref>

即可将指定的提交以 rebase 方式合并到当前分支。

Rebase 本质上是在将从两个提交的公共祖先节点到当前 HEAD 的所有节点一个个应用到指定的提交上,这个过程相当于将原来的基础(公共祖先节点)改成了指定的提交上。比如下图中合并 feat 时:

      A---B---C feat              A---B---C feat        
     /                 --->      /         \
D---E---F---G master,HEAD   D---E           F'---G' master,HEAD

推荐操作者为 rebase 时的所在分支的唯一提交者。

如果操作时出现了冲突,请参见解决冲突章节。

如果需要使用更多功能,可以使用 -i--interactive 参数,

git rebase [-i | --interactive] <commit-or-ref>

利用此功能,我们可以:

  • 应用提交 (pick)
  • 应用并修改提交文字信息 (reword)
  • 应用提交但不直接修补 (edit)
  • 应用提交但融合至上一个提交 (squash)
  • 执行命令
  • 在某处停止
  • 移除提交
  • 为当前 HEAD 打上标签
  • 将当前 HEAD 移到某个标签
  • 合并提交
  • 为引用设立占位符,以将这个引用更新为此处的新提交

优点:

  • 产生的提交为一条链。
  • 当被合并的提交与当前差异较大时,出现的冲突更好处理。

缺点:

  • 如果从两个分支的公共祖先到操作前的 HEAD 有提交不是操作者,则新产生的提交无法保留原来的签名。
  • 当分支有两个及以上修改者时,很容易出现执行时所在分支并不是最新的,更新的提交只能用 cherry-pick 的方式应用到此操作产生的新的提交上。(如果你是 rebase 时所在分支的唯一提交者,那么你不会遇到这种情况。)
  • 可能相比 merge 方式会有更多冲突,不过出现这种情况的频率不高。

解决冲突

只要有合并的行为,就意味着我们可能要解决冲突。当我们在合并修改时,Git 可能会告诉我们自动合并失败,需要我们手动合并。

通过查看当前的仓库状态 (git status),我们可以查看发生问题的文件。

在文件中,你可能会一些看到类似这样的内容

<<<<<<< HEAD
...
||||||| b6279c7
...
=======
...
>>>>>>> feat

第一个 ... 代表的是 HEAD 的内容,中间的代表两者的公共祖先的内容,最后一个代表的是被合并的内容。

如果你使用了一些更美观直观的显示工具,那么你可能会看到一些更加直观的显示,这对于解决冲突很有用。

此外,rebase 时有更多的优势,因为 rebase 是每个提交单独合并的,所以每次冲突的修改量较小,且冲突的主题比较明确。

解决完冲突后,根据方法的不同,采取不同的措施:

一些其他的「小技巧」:

  • 「走为上计」:git merge --abortgit rebase --skipgit rebase --abort(具体取决于使用的方法)可以放弃操作。
  • 「言听计从」:git merge -s theirs ...
  • 「固执己见」:git merge -s ours ...

常用合并工作流

假设仓库的主线为 master,新特性位于 feat,现在需要将 feat 分支的修改合并到 master,但是 master 不可以用 fast forward 的方式合并 feat。

Merge 方式下:

在 master 分支合并 feat (git merge feat)。

合并完成后,建议切换到 feat 分支,以 fast forward 方式合并 master,这样 master 和 feat 就位于同一提交处了。此操作也可不执行,但这样长时间不将主线的新提交合并到特性分支很容易出现各种问题(如容易冲突、特性合到主线后出问题、特性分支缺少主线的问题修复)。

Rebase 方式下:

在 feat 分支 rebase master 分支 (git rebase master)。

然后切换到 master 分支,以 fast forward 方式合并 master。

切换分支

在项目中执行

git switch <another-branch>

即可将切换到 <another-branch> 分支。

如果需要切换到一个 tag 指向的位置或直接切换到某个 commit,需要在项目中执行

git switch <another-branch> --detach

以切换到需要的位置。这是因为在这种情况下,我们在切换之后就并不属于任何的分支了。如果在此基础上有新的提交,那么离开这个新的提交意味着丢下了一些没有和任何分支关联的提交(在离开这个提交时,你会看到类似的警告)。不过只要你不主动清除掉这些提交,通过引用日志,我们仍然可以找回这些提交。

修改分支指向的提交

如果不在需要修改的分支,请先切换到对应分支

当我们需要将目前的分支改到特定的提交或引用 (ref) 时,我们可以在项目中执行

git reset [--soft | --mixed | --hard | --merge | --keep] <commit-or-ref>

来将当前的分支移动到 commit-or-ref 上。commit-or-ref 可以是引用(如 master),也可以是提交的哈希值(如 e46f1a0)。

在执行时,你可以根据需求使用不同的方式:

  • --soft:不修改目前工作环境及被暂存的文件。
  • --mixed(默认):修改暂存的文件,但是不修改目前工作环境。
  • --hard:修改目前工作环境及被暂存的文件。没有被追踪的文件都会被丢弃,相当于直接切换到了对应提交。
  • --merge:修改目前工作环境中的部分文件,这些文件是在切换的两个提交里不同的,并保留其余的文件,暂存的内容会被完全忽略。如果在此过程中修改的文件同时也有没有被记录的变更,则 Git 会放弃此操作。
  • --keep:修改目前工作环境及暂存区中的部分文件,这些文件是在切换的两个提交里不同的,并将其余的文件(包括暂存的和未暂存的)当作未暂存的。如果在此过程中修改的文件同时也有没有被记录的变更,则 Git 会放弃此操作。

相对引用

在使用 Git 管理仓库时,我们有时会遇到比如需要对当前 HEAD 的前一个提交或前面两个提交操作。当然,我们可以先查看日志,然后获得对应的提交哈希值。不过这未必是最方便的,我们只需要指定从 HEAD 往前几个提交即可明确指定需要的提交。

为了方便上面的需求,Git 提供了一种方便的相对引用方式来确定前面的某个提交,如 HEAD^ 表示上一个提交,HEAD~2 表示上两级的那个提交。本章节会具体解释 ^~ 的含义。

  • ~:表示在提交路径上回溯。HEAD~<n> 表示在当前所在 HEAD 上的前 n 个提交。比如:
    • HEAD~0:等同于 HEAD
    • HEAD~1:表示上一个提交,等同于 HEAD~
    • HEAD~2:表示前面的第二个提交,等同于 HEAD~~
    • HEAD~N:表示前面的第 n 个提交,等同于后面有 n 个 ~
  • ^:表示切换父级提交。注意:这表示其自带一个回溯一次的作用,然后在父级的一个或多个提交间切换(不是循环轮换,超过父级的提交数会报错)。HEAD~<n> 表示在当前所在位置的 HEAD 上的第 n 个父提交。推荐使用的时候查看提交详情
    • 注:当使用 merge 来合并提交时,会出现多个父提交。

获得两个分支或提交的公共祖先提交

在项目中输入

git merge-base --all <commit-or-ref1> <commit-or-ref2>

可以查看两个提交的公共祖先提交。

提交相关

添加到暂存区

在项目中输入

git add <path>...

即可将目录下被修改过的文件添加到暂存区,以便之后提交

为了方便操作,我们可以用以下命令将当前目录及其子目录中被修改过的文件。(注意:请务必在执行之前或执行之后查看当前状态,避免提交不必要的文件!

git add .

新增提交

注意:请务必在执行之前查看当前状态,避免提交不必要的文件!

在项目中输入

git commit

并在 Git 打开文本编辑器的提示下输入提交信息(如果留空则放弃提交),以产生一个新的提交。

如果提交信息很短,则可以使用 -m <msg> 参数来直接提交。比如,

git commit -m 'initial commit'

特别注意:提交之后,几乎所有的文件都可以找回。但是如果没有提交,那么文件很可能会丢失。

修正提交

有时,我们会在新增提交之后发现新增的提交有一些问题,这是我们可以考虑使用修正提交的方式。修正提交,就是放弃原有的提交,用一个新的提交来代替原有提交。比如下图是修正 C 提交的示意图:

A---B---C HEAD     A---B---C
              --->      \
                         C' HEAD

注意:修正提交会破坏原有的提交链,会产生分叉,尤其是当原提交已经被别人接收时,别人必须退回到上一个提交,然后在以 fast forward 方式接受新提交。

要修正提交,我们需要将修正的内容加到暂存区,然后在新增提交的基础上加上 --amend 参数即可。只要加上了 --amend 参数,Git 就会放弃当前 HEAD 处的提交,退回到上一个提交,然后新增一个提交。比如

git commit --amend

显示日志

在项目中输入

git log

即可查看日志。

如果需要显示提交的图关系,可以加上 --graph 参数。

如果需要每个提交只占用一行,可以加上 --oneline 参数。

显示差异

显示差异大致有下面几种常见用法:

另外,可以考虑使用更直观且美观的显示工具,具体见更美观直观的显示工具

显示当前仓库与特定提交的差异

在项目中输入

git diff <commit-or-ref>

即可查看当前仓库与特定提交之间的差异。

特别的,如果要查看当前仓库与当前 HEAD 提交的差异,可以不用指定提交,即

git diff

这对于遇到冲突时非常有用,因为 Git 会显示冲突的差异,而不是在文件中的双方修改格式。

如果要查看特定路径下的差异,可以在后面指定路径。(下面的 -- 用于告诉 Git 后面的参数只能是路径,此参数可选)

git diff <commit-or-ref> [--] <path>...

显示当前暂存区与特定提交的差异

暂存区与前面类似,只要加上 --cached 参数即可。

完整的命令要求为:

git diff --cached [<commit-or-ref>] [--] <path>...

显示特定提交之间的差异

在项目中输入

git diff <commit-or-ref1> <commit-or-ref2>

即可查看二者之间的差异。

如果要查看特定路径下的差异,可以在后面指定路径。(下面的 -- 用于告诉 Git 后面的参数只能是路径,此参数可选)

git diff <commit-or-ref1> <commit-or-ref2> [--] <path>...

从差异生成补丁以便在别处应用

将前面的几种情况的命令的输出文件重定向到特定文件(原来是终端)即可生成补丁。

比如,要从当前仓库与 HEAD 的差异生成到补丁 (repo.patch),我们可以

git diff > repo.patch

生成补丁后,可以将补丁文件应用到其他位置的仓库

显示提交详情

在项目中输入

git show <commit-or-ref>...

以展示所有提交的详情。

还原当前暂存区

有时,我们可能会不小心把仓库暂存区中的一些部分破坏了,或者做了很多修改但是发现其中有很多问题(且添加到了暂存区),因而想要将暂存区还原。

注意:此操作有危险,可能会将没有提交的数据舍弃。要达到类似的目的,也可以考虑暂时储藏修改,这不会导致数据被舍弃。

在项目中输入

git reset [--mixed | --hard | --merge | --keep]

以还原变更。不同的参数代表了:

  • --mixed(默认):还原暂存的文件,但是不修改目前工作环境。
  • --hard:还原目前工作环境及被暂存的文件。
  • --merge:还原暂存的文件。
  • --keep:将暂存区的文件取消暂存。

还原当前工作环境

有时,我们会不小心删除了一些文件,或者把工作区的文件做了很多修改但是发现其中有很多问题,且尚未添加到暂存区,因此想要将工作环境还原。

注意:此操作有危险,可能会将没有提交的数据舍弃。要达到类似的目的,也可以考虑暂时储藏修改,这不会导致数据被舍弃。

在项目中输入

git restore <file>...

以将对应文件将会变成当前 HEAD 中的文件内容。

如果要改成其他提交的内容,请加上 (--source=<tree>) 参数,如

git restore --source=HEAD~2 Makefile

可以将 Makefile 变成当前 HEAD 前两个的提交对应的内容。

取消暂存文件

在项目中输入

git reset HEAD <file>...

就可以取消暂存 <file> 这些文件。

另外,为方便使用,可以添加一个 Git 子命令别名 unstage,作为 reset HEAD -- 的别名。你可以用下面的方式来设置全局别名:

git config --global alias.unstage 'reset HEAD --'

清理未加入 Git 的文件

在项目中输入

git clean [path...]

即可清理工作区中未加入 Git 的文件。

还原 (revert) 提交

在使用 Git 的过程中,我们可能会提交一些错误的变更,比如修复了旧问题但是引入了更严重的新问题、误删除了一些文件等等。我们可以通过还原这个错误提交的方式来舍弃这些变更。

在项目中输入

git revert <commit-or-ref>...

即可将每一个提交的变更还原到当前 HEAD。注意,每应用一个提交,就会有一个新的提交产生,如果不想产生新的提交,请加上 -n--no-commit 选项。

遴选 (cherry-pick) 提交

有时,我们需要将其他分支中提交的变更应用到当前分支中。比如,项目中维护了一个开发分支(为下一版本做准备)和一个当前版本的主分支。开发分支已经修复了某个关键问题,而我们需要将这个修复应用到主分支上。这时,我们需要将修复问题的几个提交找出来,在主分支上做出相应的更改。Git 提供了一个方便的自动应用方法——cherry-pick。

在项目中输入

git cherry-pick <commit-or-ref>...

即可将每一个提交的变更逐个应用到当前 HEAD。注意,每应用一个提交,就会有一个新的提交产生,如果不想产生新的提交,请加上 -n--no-commit 选项。

比如 git cherry-pick e46f1a0 a0c13db e910141 会将三个提交逐个依次引用到当前 HEAD。

暂时储藏 (stash) 修改

在切换到其他分支(或是将远程分支同步到本地分支)时,如果你当前的工作目录有一些修改过的文件(且未提交),Git 可能会拒绝切换,这是因为切换时发生变化的文件里有你修改过的文件。此时,我们可以将这些文件暂时储藏。

Git 提供了 stash 子命令来处理暂时储藏修改。具体来说,stash 机制维护了一个修改栈,栈顶即为最近加入到修改栈的修改。下表列举一些常见的命令。

命令 用途
git stash 暂时储藏修改
git stash pop 还原上一个暂时储藏的修改
git stash clear 清空修改栈
git stash list 列举所有修改

显示文件每个部分的最近修改信息

在项目中输入(下面的 -- 用于告诉 Git 后面的参数不能是选项)

git blame [<commit-or-ref>] [--] <file>

即可查看对应提交(如果没有指定,则为当前 HEAD)的某个文件每一个部分的最近修改人、时间和提交。

查找字符串

在项目中输入

git grep ...

即可查找字符串(... 后面的部分参数与 grep 语法类似)。

注:此功能有更好的替代品,参见更好用的字符串查找工具

应用补丁

在项目中输入

git apply <path-to-patch>

即可将对应补丁应用到当前项目。

标签

新增标签

在项目中输入

git tag <tag>

即可新增标签。

如果你需要加上一些备注,你可以在项目中输入

git tag -a <tag> -m <message>

来加上你指定的信息。

推送标签

默认情况下,git push 是不会推送标签的。要推送标签,你需要在项目中输入

git push --tags

即可推送标签。

列举标签

在项目中输入

git tag -l

即可列举出所有的标签。

删除本地标签

在项目中输入

git tag -d <tag>

即可删除本地标签。

删除远程标签

在项目中输入

push --delete <remote-name> <tag>

即可删除远程标签。

显示引用日志 (Reference Log)

在项目中输入

git reflog

即可查看所有与引用相关的日志。

子仓库相关

Git 提供了多种子仓库管理方法。遗憾的是,所有的方法都要么要求使用者掌握额外的知识来使用,要么大大增加了维护的麻烦(因此有些开源项目采用了一个大仓库的管理方法)。综合来看,submodule 是最好的管理方法,尽管其要求使用者也知道如何正确使用带有 submodule 的仓库。本文将只对 submodule 的工作流进行介绍。

对于使用者,有以下几种使用需求:

对于维护者,有以下几种使用需求:

复制或更新子仓库

在项目中输入

git submodule update --init --recursive

可以将所有的子仓库(及其子仓库)递归地复制到本地。

更方便地,如果想要在 pull 时一道更新子仓库,可以加上 --recurse-submodules 参数。

如果想要更简单地操作,也可以为 pull --recurse-submodules 设置别名,避免每次主动输入参数。如下面的命令将为 pull --recurse-submodules 设置别名 full-pull,这样使用 git full-pull 即可一同更新子仓库。

git config --global alias.full-pull 'pull --recurse-submodules'

复制带子仓库的仓库

在项目中输入

git clone <repository> [<directory>] --recurse-submodules

可以将原仓库及所有的子仓库(及其子仓库)递归地复制到本地。

列举子仓库

在母仓库中输入

git submodule

即可列举所有的子仓库。

添加子仓库

在项目中输入

git submodule add <repository> [<directory>]

以将 repository 所指的仓库复制到 directory 指定的位置(默认为当前位置)。

为了方便他人使用,建议用 https 协议的仓库,这样即便使用者没有注册账户亦可使用。如使用 https 协议仓库,但你仍然想要用 ssh 连接,你可以设置 URL 替换。比如对于 GitHub,你可以执行一次以下的命令来让 Git 在每次遇到 https 的 GitHub 仓库链接时替换为 ssh 链接。(如果要只对当前的子仓库生效,可以在子仓库目录中执行去掉 --global 参数后的命令)

git config --global url.git@github.com:.insteadOf https://github.com/

更新子仓库

进入子仓库,用一般 Git 仓库的操作方法更新子仓库,然后离开子仓库,通过 git add 将子仓库的变动加到暂存区,然后新增提交

子仓库的操作(包括推送到远程仓库、将远程仓库拉取到本地)与一般的操作无异,只是当前位置可能处于脱离分支的状态。

如果只是要更新到子仓库的远程仓库的 master,可以在母仓库中输入

git submodule update --remote

删除子仓库

首先,可以先列举出子仓库

然后,在母仓库中输入

git submodule deinit <path-to-submodule>

以删除 Git 的子模块跟踪信息。

接着,将子仓库删除

git rm <path-to-submodule>

并将已经暂存的数据清除

git rm <path-to-submodule> --cached

最后,新增一个提交以提交变更。

修改子仓库 URL

首先,修改仓库根目录的 .gitmodules 文件中子仓库的 URL。

然后,在母仓库使用

git submodule sync

将新的 URL 更新。

最后,新增一个提交以提交变更。

杂项

更美观直观的显示工具

Git 的 diff/blame/grep/show 命令的显示方法可能不够直观。可以考虑使用其他显示工具,如 delta 项目等。

注:不必担心这样的输出会影响 diff 产生的 patch,Git 只会在确定当前为终端时使用这类工具。

更好用的字符串查找工具

有很多工具可以提供更好的字符串查找功能,如 rgag 等。

检查两个提交的前后关系

有时,我们可能需要检查一个提交比另一个提交新了多少个提交,又少了多少个提交,从而大概估计两个分支之间的关系。下面的脚本可以完成这样的需求:

#!/bin/bash

if [ $# -ne 2 ]; then
    cat << EOF >&2
Usage: git-count-aheads-and-behinds <commit1> <commit2>
EOF
   exit 1
fi

get_count() {
    git rev-list --count "$1" --not "$(git merge-base --all "$1" "$2")"
}

echo "$1 is $(get_count "$1" "$2") commit(s) ahead of, and $(get_count "$2" "$1") commit(s) behind $2."