blog
blog copied to clipboard
Git 修改commit历史的常用方法
开发时经常会出现以下场景
请把你当前分支与master 最新版本做一次rebase,我们在合并你的PR 你把你几个commits squash成一个commit,避免git历史过于复杂和难看 你去把你commit 的message修改一下,使得commit 的信息让人更清晰易懂
上述的这些问题常见于PR下面的讨论,下面总结一下解决问题的方法
重写最近的commit 的信息
在提交后, 如果你发现在其描述中发现typo,或者找到一个更好的方式来描述commit。要进行校正:
git commit --amend
这时git将进入编辑模式,在这里你可以修改之前的commit 的相关信息
除了简单的编辑commit记录之外,你也可以单单修改coomit的author
git commit --amend --author="waltcow <[email protected]>"
修改其它的commit的内容
使用 Interactive Rebase 模式
git rebase
重新把每次的提交一个一个按顺序apply在当前的branch上。git rebase
接受几个使用的option,其中有个非常实用的就是 --interactive
(- i
是对应的shortcut),进入Interactive Rebase
模式后,git将打开编辑器,list出包含的改动信息,这些list的每行开始都接受command
,用户可以决定相关操作的参数后再进行rebase
详细可以见下面的相关例子
现在我要重写当前分支上最后4个commit的信息,我只需要
git rebase -i HEAD~4
下面我们将会看到
pick 07c5abd Introduce OpenPGP and teach basic usage
pick de9b1eb Fix PostChecker::Post#urls
pick 3e7ee36 Hey kids, stop all the highlighting
pick fa20af3 git interactive rebase, squash, amend
# Rebase 8db7e8b..fa20af3 onto 8db7e8b
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
上面我们可以看到最近的commit记录,时间排序从older=>newer, 下面有相关的command的使用介绍,如下将逐一介绍
-
pick
这是默认的选择,就是把commit再重新apply一次而已,commit的内容和信息都不会有改动 -
reword
commit的内容不变,仅仅把commit的message做修改
比如我把上面的commit改成
pick 07c5abd Introduce OpenPGP and teach basic usage
pick de9b1eb Fix PostChecker::Post#urls
reword 3e7ee36 Hey kids, stop all the highlighting
pick fa20af3 git interactive rebase, squash, amend
+
When I save and quit the editor, git will follow the described commands, landing
当我:wq
后git 将会输出
robots.thoughtbot.com tc-git-rebase % git rebase -i HEAD~4
[detached HEAD dd62a66] Stop all the highlighting
Author: Caleb Thompson
Date: Fri Oct 31 10:52:26 2014 -0500
2 files changed, 39 insertions(+), 42 deletions(-)
Successfully rebased and updated refs/heads/tc-git-rebase.
将多个commits Squash在一起
-
squash
将提交融合到前一个commit(上面的那一行) -
fixup
类似于squash
,但是commit的message将会被丢弃
下面是使用案例 现在我们我4个commit,前三个commit是我同事改的,我自己的是最后一个
pick 07c5abd Introduce OpenPGP and teach basic usage
pick de9b1eb Fix PostChecker::Post#urls
pick 3e7ee36 Hey kids, stop all the highlighting
pick fa20af3 git interactive rebase, squash, amend
假如我现在想把我同事的修改合并在一起,以为前三个都是做一个的主题的,这样合并后的话,我们以后如果想revert
这部分变更将变简单。
前三个commit中我想只保留第一个commit的message,后面两个的更改提交并压缩到第一个提交消息中
pick 07c5abd Introduce OpenPGP and teach basic usage
squash de9b1eb Fix PostChecker::Post#urls
squash 3e7ee36 Hey kids, stop all the highlighting
pick fa20af3 git interactive rebase, squash, amend
squash
的作用就是把当前commit向前合并,一直合并到pick
为止
fixup
和squash
非常类似,把pick
修改成fixup
,同样会向前合并到pick
,唯一的区别就是,fixup会忽略当前commit的信息,只会应用修改。
与远程master分支做 Rebase
当我们fork一个远程仓库,在一个功能分支上开始工作,并且upstream/maste将不断的前进。我们的历史看起来像:
A---B---C feature
/
D---E---F---G upstream/master
当仓库管理者在合并你的PR时都会教你 rebase on top of master
,这样做可以避免merge
时出现冲突,并保证了两个分支是基于一个parent 的,在rebase后你所看到的历史是这样的
A'--B'--C' feature
/
D---E---F---G upstream/master
一般使用的命令
# Point our `upstream` remote to the original fork
git remote add upstream https://github.com/thoughtbot/factory_girl.git
# Fetch latest commits from `upstream` (the original fork)
git fetch upstream
# Checkout our feature branch
git checkout feature
# Reapply it onto upstream's master
git rebase upstream/master
# Fix conflicts, then `git rebase --continue`, repeat until done
# Push to our fork
git push --force origin feature