实战应用的常见技巧【二】
同系列的往期文章如下:
【Git 系列】实战应用的常见技巧【一】
整个利用 Git 的开发过程中, commit 的管理是核心。下面将对 commit 的定义和各种管理操作,伴随着使用场景进行介绍。
本节将介绍 commit 的定义和一系列的管理操作。
定义
在整个 Git 的开发中,最基本的控制单位即 commit 。branch 无非也是对 commit 的一种管理(整合)方式。每当完成一个小的目的,或者有其它任务需要处理,我们都将这部分更改添加到 stage 中,随后 commit。这算作一次完整的 commit。但有些场景下,可能需要修改,删除,合并某些特定的 commit。随后的章节中将详细介绍这些对 commit 的管理操作。
管理操作
本节中将详细地针对 commit 的修改,删除,合并操作进行介绍。
场景一:刚 commit 完成,发现一个小 bug
对于该场景,当然可以选择修复 bug 后,再 commit 一次。但是这会使得 commit 有些混乱。最理想的方法就是修改刚才的提交的 commit,即:修改当前的 commit。在修复完 bug 后,通过以下命令可以解决:
git commit --amend
该命令的含义为:可以将 stage 中的内容和当前 commit 的内容合并,这不会产生新的 commit。
场景二:倒数第二个 commit 有误
既然有命令可以修改当前的 commit 并且不会产生新 commit。那如果可以将当前 commit 向前移动一个,此时修改当前 commit,则场景二就解决。所以下面介绍 Git 中的两个偏移符号,便于 commit 的移动。
-
^
在一个 commit 后添加一个或多个^
,就可以将 commit 向前移动等数量个。eg.git rebase -i HEAD^^
-
~
在一个 commit 后添加数字 x,则可以将 commit 向前移动 x 个。eg.git rebase -i HEAD~2
上述两个偏移符号^ ~
结合 HEAD
可以快速定位到离当前最近的几个 commit 。如果离的太远,使用偏移符号就显得臃肿,此时还有别的方法,后面也会讲解。
对于仅修改倒数第二个 commit 的场景,可以参考如下步骤:
#1、偏移到需要修改的 commit
git rebase -i HEAD^^
# 上面命令输入后,将进入一个编辑界面,例如编辑界面展示下面两条语句。将第一条 pick 改为e 。
pick 49f2d85 add a cc.cpp
pick 8036e04 update the tmp.cpp
#修改后为
e 49f2d85 add a cc.cpp
pick 8036e04 update the tmp.cpp
# 修改后,进行保存退出
#2. 进行本次的修改
...
#3. 提交修改到当前 commit
git commit --amend
#4. 继续 rebase,直到结束。后续将讲解 rebase。
git rebase continue
该操作核心利用了git rebase
命令,这将在场景三中进行详细介绍。
场景三:想要修改特定的某几个 commit
可以看到,场景三覆盖了场景一和场景二。但是功能越强大,操作复杂度也肯定相应提高。所以场景一和场景二的相对简单的操作也是有必要保留。
场景二中,提到了 rebase
命令,其实对 commit 的复杂的操作都可以通过该命令来完成,后面章节都将一一进行讲解。在本节中,仅仅讲解如何处理场景三。
rebase
命令:rebase 命令的功能非常强大,但是改命令直接修改 commit,而我们创建 commit 就是为了记录开发的流程,事实就是如此,rebase 命令却打破了事实。所以 rebase 命令有利有弊。谨慎使用。
下面举例说明在场景三下,如何使用rebase 解决。
- 当前commit情况如下所示,现在想要修改 commit 1 和 commit 3。
2. 输入 git rebase -i HEAD~3
,终端将展示如下编辑界面,自上而下是三条 commit (按照提交时间排序)。编辑界面的下面注释中提供很多控制命令和其功能。此处编辑器可以设置,这里设置的为 vim。
3. 根据需求,想要修改commit 1 和 commit 3,则只需将对应的commit 前的 pick 修改为 e(或者edit),如下图所示。修改完毕后,保存退出。git 将会自上而下依次执行命令。
4. git 首先将会执行第一条:e 187a147 1
,即编辑 commit 1。终端界面,如下图所示。可以看到,终端 git 也给出了提示,它知道需要在该commit 停止,进行修改,在修改完成后,执行 git commit --amend
。此时,若想继续修 commit 3,需要执行 git rebase --continue
。
5. 想要继续进行修改,需要执行git rebase --continue
,由于 commit 2 的命令设置为 pick
,git 将会自动跳过(其实是保留了该commit),所以rebase 将停止在 commit 3。如下图所示。
6. 此时,对commit 3 进行相应的修改即可。如下图所示。最后同样需要执行git rebase --continue
让 git 自动检测是否结束此次 rebase。
注意,观察下图,分别为rebase 前后commit ID,可以发现是不同的。
从上述的描述中,不难看出,git rebase -i HEAD^^^
相当于重新遍历最后三个commit,如果需要修改,就重新修改,并提交。注意,是重新提交,并没有在原本的基础上进行修改(commit id改变了)。修改前后,commit id变化,这就是 rebase 最大的特点。
场景四:合并最近的几次commit
回想场景三下,使用了 git rebase -i
,也仅仅使用了 edit
命令,剩余还有很多命令可以用来控制 commit。本节,介绍s (squash)
来合并提交。
还是举例说明,如何使用。
-
如下图,当前处于master分支。想要将commit 1和commit 2进行合并。
-
需要使用 rebase 来重新提交这三次commit。如下图所示,将commit 2前的 pick 修改为 s。s 命令的含义为使用该 commit,但是和前一个commit合并。修改后,保存退出。
-
git 同样还是先执行第一个 commit 的命令, 由于是pick,所以直接保留,并没有停止。继续这将停止在 commit 2中,由于commit 2 的命令是 s ,所以展示了如下commit message的修改界面(pick 命令没有界面显示,所以本界面是在上个保存退出后直接展示):
-
合并过程中,需要对提交的消息重新修改,所以出现本界面。我们对其修改即可,修改后,如下所示。修改后,保存退出。
-
保存退出后,终端展示如下所示。由于 commit 3 的命令是 pick,所以直接跳过,并结束此次 rebase。
-
此时,看一下 commit 记录。可以看到 commit 1 和 commit 2 合并,并且commit message也是之前所设置的。
通过上述过程,可以发现,通过rebase 命令可以很方便的对 commit 进行修改和调整。在rebase 中的命令,s、p、e可以同时使用,来实现更加复杂的操作。
场景五:删除 commit
同样使用 rebase 来操作,使用命令 d
即可。
杂
场景一:想从某个 commit 开始一些尝试的操作(偏向不可能保留)
有时候突然,想尝试某个操作,但是这个尝试大概率不会被保留,可以尝试如下命令。如果想从 commit_a 开始尝试。输入下面命令:
git checkout commit_a
该命令的含义为:将 HEAD 移动到 commit_a (当然该 commit_a 可以通过 HEAD^^
等方式来确定),移动后,当前的 commit 不属于任何的 branch(HEAD 处于 detached 状态) ,相当于创建了一个匿名的 branch 开进行本次尝试。尝试完成后,两种结果。
- 不想保留此次尝试,直接切换 branch 到现存的即可,本次匿名 branch 将丢失。eg.
git switch master
- 想要保留此次尝试。为该匿名 branch 创建新的名字即可。eg.
git switch -c new_branch
参考文章
git rebase 用法详解与工作原理
【Git】rebase 用法小结