• Git 实用操作:重写 Commit 历史


    当我们修改完代码,提交了一个 commit,然后发现改错了,怎么修正?下面分两种情况来讨论:修正最近一次提交,和修正历史多个提交。

    修正最近一次提交

    如果发现刚刚提交的内容有错误,当场再修改一下再提交一个新 commit 不就可以么?可以是可以,不过还有一个更加优雅和简单的解决方法:

    git commit --amend
    

    "amend" 是“修正”的意思。在提交时,如果加上 --amend 参数,Git 不会在当前 commit 上增加 commit,而是会把当前 commit 的内容和暂存区(stageing area)里的最近一次 commit 的内容合并起来后创建一个新的 commit,用这个新的 commit 把之前最新的 commit 替换掉。所以 commit --amend 做的事就是它的字面意思:对最新一条 commit 进行修正。

    具体地,比如你发现刚刚的提交中 foo.txt 文件有错别字,你就可以把文件中的错别字修改好之后,输入以下命令:

    git add foo.txt
    git commit --amend
    

    此时 Git 会把你带到提交信息编辑界面。提交信息默认是最近那次提交时填的信息。你可以修改或者保留它,然后保存退出。然后,你的最新 commit 就被更新了。

    需要注意的有一点:commit --amend 并不是直接修改原 commit 的内容,而是如上面动图所示生成一条新的 commit。

    修正历史多个提交

    commit --amend 可以修正最新 commit 的错误,但如果是倒数第二个、第三个 commit 写错了,怎么办?

    如果不是最新的 commit 写错,就不能用 commit --amend 来修复了,而是要用 rebase。不过需要给 rebase 也加一个参数:-i

    rebase -irebase --interactive 的缩写形式,意为“交互式变基”。

    所谓交互式 rebase,就是在 rebase 的操作执行之前,你可以指定要 rebase 的 commit 链中的哪一个 commit 是否需要进一步修改。

    那么你就可以利用这个特点,进行一次原地 rebase。

    例如你是在写错了 commit 之后,又提交了一次才发现之前写错了。现在再用 commit --amend 已经晚了,此时就要用 rebase -i 命令了:

    git rebase -i HEAD^^
    

    在 Git 中,有两个「偏移符号」: ^~

    ^ 的用法:在 commit 的后面加一个或多个 ^ 号,可以把 commit 往回偏移,偏移的数量是 ^ 的数量。例如:master^ 表示 master 指向的 commit 之前的那个 commit; HEAD^^ 表示 HEAD 所指向的 commit 往前数两个 commit。

    ~ 的用法:在 commit 的后面加上 ~ 号和一个数,可以把 commit 往回偏移,偏移的数量是 ~ 号后面的数。例如:HEAD~5 表示 HEAD 指向的 commit 往前数 5 个 commit。

    上面这行代码表示,把当前 commit ( HEAD 所指向的 commit) rebase 到 HEAD 向前两个的 commit 上:

    如果没有 -i 参数的话,这种原地 rebase 相当于空操作,会直接结束。而在加了 -i 后,就会跳到一个新的界面:

    pick 310154e 第 N-2 次提交
    pick a5f4a0d 第 N-1 次提交
    
    # Rebase 710f0f8..a5f4a0d onto 710f0f8
    #
    # Commands:
    # p, pick <commit> = use commit
    # r, reword <commit> = use commit, but edit the commit message
    # e, edit <commit> = use commit, but stop for amending
    # s, squash <commit> = use commit, but meld into previous commit
    # f, fixup <commit> = like "squash", but discard this commit's log message
    # x, exec <command> = run command (the rest of the line) using shell
    # b, break = stop here (continue rebase later with 'git rebase --continue')
    # d, drop <commit> = remove commit
    ...
    

    这个编辑界面的最顶部,列出了将要被 rebase 的所有 commits,也就是倒数第二个 commit “第 N-2 次提交”和最近的 commit “第 N-1 次提交”。需要注意,这个排列是正序的,旧的 commit 会排在上面,新的排在下面。

    这两行指示了两个信息:需要处理哪些 commit 和如何处理它们。

    你需要修改这两行的内容来指定你需要的操作。每个 commit 默认的操作都是 pick,表示直接应用这个 commit。所以如果你现在直接退出编辑界面,那么结果仍然是空操作。

    但你的目标是修改倒数第二个 commit,也就是上面的那个“第 N-2 次提交”,所以你需要把它的操作指令从 pick 改成 editedit 的意思是应用这个 commit,然后停下来等待继续修正。其他的操作指令,在这个界面里都已经列举了出来(下面的 "Commands…" 部分文字),你可以自己研究一下。

    edit 310154e 第 N-2 次提交
    pick a5f4a0d 第 N-1 次提交
    
    ...
    

    pick 修改成 edit 后,就可以退出编辑界面了,并输出以下信息:

    $ git rebase -i HEAD^^
    Stopped at 310154e... 第 N-2 次提交
    You can amend the commit now, with
    
           git commit --amend
    
    Once you're satisfied with your changes, run
    
           git rebase --continue
    

    上图的提示信息说明,rebase 过程已经停在“第 N-2 次提交”的 commit 的位置,那么现在你就可以去修改你想修改的内容了。

    修改完成后,和前文修改最近提交的方法一样,用 commit --amend 把修改原地应用到一个新的 commit:

    git add foo.txt
    git commit --amend
    

    这样你的倒数第二个错误的 commit 就被修正了。在此过程中,把原来倒数第二个 commit 的内容和当前修改的内容合并在一起创建了一个新的 commit,并用此 commit 原地替换了原来的原来倒数第二个 commit:

    然后,你可以用 rebase --continue 来继续上述 rebase 过程。

    git rebase --continue
    

    所有需要重写的 commit 都修改完成后,这次交互式 rebase 的过程就完美结束了。

    总结

    只修正最近的错误提交,使用简单的 commit --amend 即可。若修改历史多个提交用交互式变基 rebase -i,它可以在 rebase 开始之前指定一些额外操作。通过交互式变基还可以实现其它历史重写操作,如“重新排序提交”、“压缩提交”、“拆分提交”等,这些历史重写操作不常用,我个人从来没用过,所以就不讲了,你可以在实际有需要的时候自己再去研究一下。

  • 相关阅读:
    静态代码块、非静态代码块、构造函数之间的执行顺序
    Linux跨主机传输文件
    🗒 Linux 系统监控
    Mysql Mode
    Mysql 表锁行锁
    Centos 下修改时区
    Redis 解决内存过大
    Mysql 表达式
    Centos 二进制包安装Mysql5.7
    Vim 快捷键
  • 原文地址:https://www.cnblogs.com/willick/p/13637185.html
Copyright © 2020-2023  润新知