blog icon indicating copy to clipboard operation
blog copied to clipboard

Git 修改commit历史的常用方法

Open waltcow opened this issue 8 years ago • 0 comments

开发时经常会出现以下场景

请把你当前分支与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为止 fixupsquash非常类似,把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

waltcow avatar Feb 14 '17 05:02 waltcow