常用的修改/合并commit的方式

53 阅读6分钟

1. git commit --amend -m 命令

git commend --amend -m 适用于修改当前分支最新一条commit的message。

例如,当前我的main分支有一条最新的commit,我现在想修改这条commit,

image.png

当我执行git commit --amend -m 'modified'后,再执行git log,可以看到这条commit messgae已经完成修改。

image.png

可以看到这两条commit的commit id已经不一样了,表明修改commit message实际上会生成一个新的commit。

2. git reset --soft 命令

git reset --soft <commit-id> 这条命令相当于撤销到commit-id之前的所有提交,将修改的文件退到暂存区。

修改commit

这个修改commit可以理解为修改commit message 或者 修改commit中包含的文件。

修改位于HEAD的commit

如果需要修改第一条commit,那么执行git reset --soft HEAD~1,等文件退还到暂存区后,完成需要的更改,再执行git commit进行提交即可。

例如,我先添加了一个reset.js文件,并提交了修改:

image.png

在vscode中点击git log的commit id可以看到文件修改历史:

image.png

提交代码后发现,reset.js文件中有一个case没有考虑进去,我们当然可以直接修改然后再提交一个commit,但是我觉得在本地没要新建一个commit,而且这实际上还是在完成上个需求,所以放到一起比较好。

因为这条commit正好是我这个分支的第一个,这个时候我会执行 git reset --soft HEAD~1

image.png

可以看到此时'this is reset'就没了,再观察一下左侧暂存区的状态:

image.png

此时reset.js就退回到了暂存区。

image.png

当我修改完之后,左侧的暂存区和工作区都出现了reset.js,这个也不难理解,暂存区是上一个commit时reset.js的状态,而下面更改的工作区是当前修改后reset.js的状态。修改完毕后,只要再执行add,commit就可以了。

image.png

可以看到'this is reset'又出现了,但是和git commit --amend -m不同的是,commit Date已经完全改变。

合并commit

我们接到了一个开发combine功能的需求,经过需求分析和任务拆解,用三个阶段完成了任务,为了方便代码管理,也同样分成了三个提交。这在本地开发时是没有问题的。问题在于,这样的commit细粒度过于小,提交到源码库之后有些乱,因为这三个commit实际上是为了完成同一个任务,那么这时候就需要将这三个commit合并。

image.png

这时就需要找到我们想合并的commit中最早的一条commit之前的commit id,比如我这个场景中,就是找f1c3a12d这个commit id: git reset --soft f1c3a12d597e97ed3d919e4358fa6d6ad813c3c7

image.png

相当于就是撤销f1c3a12d之前的提交,并将f1c3a12d作为HEAD。

image.png

现在暂存区也出现了combine.js文件。

此时只要再执行add , commit 就可以达到目的。

image.png

2. git reabse 命令

git rebase 是git的变基操作,可以对commit历史进行编辑等操作,使得commit历史保持线性和整洁。

我理解的rebase,实际上就是对目前分支的HEAD进行操作,改变分支HEAD的指向。因此,可以利用rebase的这个特性对当前分支的HEAD commit进行修改,以及对前若干项commit进行删除、合并等操作。

rebase 的两种模式

standard模式(标准模式)

标准模式就是直接输入git rebase <base>,使用场景一般是合并分支。例如将开发分支合到master上。

interactive模式(交互模式)

交互模式就是git rebase -i <new base> <old base>,当使用 -i 选项时,会调用一个vim编辑器,通过在vim编辑器中选取、编辑commit。

修改位于HEAD的commit

看看目前的git log,我想修改最近一条commit的commit message:

image.png

输入命令git rebase -i HEAD~1

image.png

会出现这样一个交互界面,上文说了,这实际上是一个vim,所以可以直接进行修改。

主要里面Command这一段,写了几个rebase命令。

对于咱们现在修改commit message的需求来说,只需要理解reword这个命令的含义。正如后面注释所说,reword表示使用这条commit,但要修改commit message

所以,将pick改写成r:

image.png

随后保存、退出。

注意:r 后面的 '8602d9e' 这条commit id不能删除,否则git不知道要选择哪条commit。

接下来又会跳出一个vim界面,就到了真正修改commit message的时候:

image.png

编辑之后保存,可以看到git提示成功:

image.png

打开git log,修改成功。

image.png

合并commit

使用rebase合并commit的思路,是先确定commit的范围,然后选择后面提交的commit合并到最早的一条中去。

选择多条commit有两种方法:

第一种是以HEAD为起点: git rebase -i HEAD~<N>,N表示你想合并几条。

第二种是指定commit:git rebase -i <commit-id>, commit-id为最早的那条commit再往前推一个。

下面分别举例:

当前分支有三个提交,现在我想把这三个合并。

image.png

使用HEAD

使用 git rebase -i HEAD~3:

image.png 熟悉的界面又来了,不同的是这次多了几条,咱们刚刚指定的是HEAD~3,现在出现的也是3条,正好符合咱们的需求。

之前修改commit message使用的是r命令,这次就要学习另外两个命令:pick psquash s

在多条commit合并的操作中,squash和pick是配合使用,比如:

p commit1
s commit2

表示将commit2合并到p中。

看看squash后面的注释:使用这条commit,但是合并到之前的commit。

接下来将squashpick用到咱们的需求中:

image.png

根据之前的分析,s是把该条commit合并到之前的commit,在咱们的环境中rebase1是最早的commit,所以pick rebase1,其余两个都是squash。

保存后又会出现一个vim界面,和之前修改一个commit类似,这个vim就是来编辑合并后新的commit的commit message:

image.png

对commit message进行编辑,注释掉不需要的内容:

image.png

接下来就看到成功的提示:

image.png

再打开git log看看:

image.png

三条commit被合并成了一条。

撤销rebase

上面合并了三条commit,现在我想撤销合并,怎么操作?

答案是:git reflog

打开reflog,可以看到所有的git操作都在这里,有点像mysql的redo log,现在我想要git恢复到rebase之前的状态,观察内容可以得知,rebase之前的状态是HEAD@{4},是我对commit进行了一个amend操作:

image.png

再执行:git reset --hard HEAD@{4}:

image.png

可以看到之前的三条commit又恢复了!

使用commit id:

现在操作一下使用第二种commit id的方式合并提交。

观察 git log, 我想合并rebase1 - rebase3,那么就是要对这仨进行操作,所以在指定commit id是应当再往前推进一个,既'a17cdd':

image.png

执行git rebase -i a17cdd0fbc61cd4c9ca696cca71a24c76a4eb831

image.png

又出现了熟悉界面,接下来的操作就和之前一样,不再赘述。

3. 总结

本文介绍了:

  • git commit --amend
  • git reset --soft
  • git rebase -i 三种修改/合并commit的方法,其中还提到了利用git reflog查看git操作日志并进行撤销操作的方法。