1. git commit --amend -m 命令
git commend --amend -m
适用于修改当前分支最新一条commit的message。
例如,当前我的main分支有一条最新的commit,我现在想修改这条commit,
当我执行git commit --amend -m 'modified'
后,再执行git log,可以看到这条commit messgae已经完成修改。
可以看到这两条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文件,并提交了修改:
在vscode中点击git log的commit id可以看到文件修改历史:
提交代码后发现,reset.js文件中有一个case没有考虑进去,我们当然可以直接修改然后再提交一个commit,但是我觉得在本地没要新建一个commit,而且这实际上还是在完成上个需求,所以放到一起比较好。
因为这条commit正好是我这个分支的第一个,这个时候我会执行 git reset --soft HEAD~1
:
可以看到此时'this is reset'就没了,再观察一下左侧暂存区的状态:
此时reset.js就退回到了暂存区。
当我修改完之后,左侧的暂存区和工作区都出现了reset.js,这个也不难理解,暂存区是上一个commit时reset.js的状态,而下面更改的工作区是当前修改后reset.js的状态。修改完毕后,只要再执行add
,commit
就可以了。
可以看到'this is reset'又出现了,但是和git commit --amend -m
不同的是,commit Date已经完全改变。
合并commit
我们接到了一个开发combine功能的需求,经过需求分析和任务拆解,用三个阶段完成了任务,为了方便代码管理,也同样分成了三个提交。这在本地开发时是没有问题的。问题在于,这样的commit细粒度过于小,提交到源码库之后有些乱,因为这三个commit实际上是为了完成同一个任务,那么这时候就需要将这三个commit合并。
这时就需要找到我们想合并的commit中最早的一条commit之前的commit id,比如我这个场景中,就是找f1c3a12d这个commit id:
git reset --soft f1c3a12d597e97ed3d919e4358fa6d6ad813c3c7
相当于就是撤销f1c3a12d之前的提交,并将f1c3a12d作为HEAD。
现在暂存区也出现了combine.js文件。
此时只要再执行add , commit 就可以达到目的。
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:
输入命令git rebase -i HEAD~1
:
会出现这样一个交互界面,上文说了,这实际上是一个vim,所以可以直接进行修改。
主要里面Command
这一段,写了几个rebase命令。
对于咱们现在修改commit message的需求来说,只需要理解reword
这个命令的含义。正如后面注释所说,reword
表示使用这条commit,但要修改commit message。
所以,将pick改写成r:
随后保存、退出。
注意:r 后面的 '8602d9e' 这条commit id不能删除,否则git不知道要选择哪条commit。
接下来又会跳出一个vim界面,就到了真正修改commit message的时候:
编辑之后保存,可以看到git提示成功:
打开git log,修改成功。
合并commit
使用rebase合并commit的思路,是先确定commit的范围,然后选择后面提交的commit合并到最早的一条中去。
选择多条commit有两种方法:
第一种是以HEAD为起点: git rebase -i HEAD~<N>
,N表示你想合并几条。
第二种是指定commit:git rebase -i <commit-id>
, commit-id为最早的那条commit再往前推一个。
下面分别举例:
当前分支有三个提交,现在我想把这三个合并。
使用HEAD
使用 git rebase -i HEAD~3
:
熟悉的界面又来了,不同的是这次多了几条,咱们刚刚指定的是
HEAD~3
,现在出现的也是3条,正好符合咱们的需求。
之前修改commit message使用的是r
命令,这次就要学习另外两个命令:pick p
和 squash s
。
在多条commit合并的操作中,squash和pick是配合使用,比如:
p commit1
s commit2
表示将commit2合并到p中。
看看squash后面的注释:使用这条commit,但是合并到之前的commit。
接下来将squash
和pick
用到咱们的需求中:
根据之前的分析,s是把该条commit合并到之前的commit,在咱们的环境中rebase1是最早的commit,所以pick rebase1,其余两个都是squash。
保存后又会出现一个vim界面,和之前修改一个commit类似,这个vim就是来编辑合并后新的commit的commit message:
对commit message进行编辑,注释掉不需要的内容:
接下来就看到成功的提示:
再打开git log看看:
三条commit被合并成了一条。
撤销rebase
上面合并了三条commit,现在我想撤销合并,怎么操作?
答案是:git reflog
。
打开reflog,可以看到所有的git操作都在这里,有点像mysql的redo log,现在我想要git恢复到rebase之前的状态,观察内容可以得知,rebase之前的状态是HEAD@{4},是我对commit进行了一个amend操作:
再执行:git reset --hard HEAD@{4}
:
可以看到之前的三条commit又恢复了!
使用commit id:
现在操作一下使用第二种commit id的方式合并提交。
观察 git log, 我想合并rebase1 - rebase3,那么就是要对这仨进行操作,所以在指定commit id是应当再往前推进一个,既'a17cdd':
执行git rebase -i a17cdd0fbc61cd4c9ca696cca71a24c76a4eb831
:
又出现了熟悉界面,接下来的操作就和之前一样,不再赘述。
3. 总结
本文介绍了:
git commit --amend
git reset --soft
git rebase -i
三种修改/合并commit的方法,其中还提到了利用git reflog
查看git操作日志并进行撤销操作的方法。