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 -i
是 rebase --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
改成 edit
。 edit
的意思是应用这个 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 开始之前指定一些额外操作。通过交互式变基还可以实现其它历史重写操作,如“重新排序提交”、“压缩提交”、“拆分提交”等,这些历史重写操作不常用,我个人从来没用过,所以就不讲了,你可以在实际有需要的时候自己再去研究一下。
Git 实用操作:重写 Commit 历史的更多相关文章
- Git 实用操作:撤销 Commit 提交
有的时候,改完代码提交 commit 后发现写得实在太烂了,连自己的都看不下去,与其修改它还不如丢弃重写.怎么操作呢? 使用 reset 撤销 如果是最近提交的 commit 要丢弃重写可以用 res ...
- git实用操作21条
1.建空目录 mkdir e:\gg 2.把该目录变成仓库 git init //发现当前目录下多了一个.git 3.新建文件readme.txt 4.添加文件到仓库 git add readm ...
- git 实用操作
查看某文件的某些行的变化历史: $ git log --pretty=short -u -L 2003,2005:Executor.cpp http://stackoverflow.com/quest ...
- GIT实用操作指令(更新中)
提取多次提交的文件 git archive --format=zip HEAD `git diff --name-only 较早的提交ID 较晚的提交ID` > diff.zip
- git rebase -i命令修改commit历史
[TOC] 修改commit历史的前提 修改历史的提交是可能有风险的,是否有风险取决于commit是否已经推送远程分支,未推送,无风险,如果已推送,就千万不要修改commit了. 修改commit历史 ...
- Git误操作 git reset强制回滚 恢复commit方法
参考: 找回Git中丢失的Commit Git误操作 git reset强制回滚 恢复commit方法 使用Git时,常有误操作,在Commit之后又执行了git reset --hard HEAD强 ...
- 美化git commit历史
为什么要美化commit历史? 答:假如一个分支的多次意义相近的 commit,会把整个提交历史搞得很混乱, 此时可以将几个commit 合并为一个 commit,以美化整个 commit 历史. 怎 ...
- Git常用操作汇总(转)
如果一个文件被删除了,可以使用切换版本号进行恢复.恢复方法: 先确定需要恢复的文件要恢复成哪一个历史版本(commit),假设那个版本号是: commit_id,那么 git checkout com ...
- Git 实用基础(配置,建库,提交,推送 GitHub)
Git 实用基础(配置,建库,提交,推送 GitHub) SVN ? Git ? 目前市面上主流的版本控制系统就是 SVN 和 Git . 两者的区别简单通俗地说就是,版本数据是否有在本地. 如果觉得 ...
随机推荐
- 通过java程序(JSch)运行远程linux主机上的shell脚本
如果您看完文章之后,觉得对您有帮助,请帮我点个赞,您的支持是我不竭的创作动力! 如果您看完文章之后,觉得对您有帮助,请帮我点个赞,您的支持是我不竭的创作动力! 如果您看完文章之后,觉得对您有帮助,请帮 ...
- Java并发--基础知识
一.为什么要用到并发 充分利用多核CPU的计算能力 方便进行业务拆分,提升应用性能 二.并发编程有哪些缺点 频繁的上下文切换 时间片是CPU分配给各个线程的时间,因为时间非常短,所以CPU不断通过切换 ...
- 基于注解的DI
目录 一.使用注解的步骤 二.@Component 三.@Value 四.@Autowired 五.@Qualifier 六.@Resource 七.XML和注解对比 通过spring的注解完成对ja ...
- C#LeetCode刷题之#290-单词模式(Word Pattern)
问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3778 访问. 给定一种 pattern(模式) 和一个字符串 s ...
- Webpack 定义process.env的时机
定义 process.env的时机 如果已经提取了公共配置文件 webpack.common.js 分别定义了开发配置webpack.dev.js和生产配置webpack.prod.js 在webpa ...
- Vue 图片压缩上传: element-ui + lrz
步骤 安装依赖包 npm install --save lrz 在main.js里引入 import lrz from 'lrz' 封装 compress函数 封装上传组件 upload-image ...
- Linux学习日志第一天——基础命令①
文章目录 前言 命令的作用及基本构成 关于路径 命令 ls (list) 命令 pwd (print working directory) 命令cd (change directory) 命令 mkd ...
- android studio实现圆角的button
操作过程 1. 在drawable中新建一个button_circle_shape.xml 但是建立这个xml是有操作的,因为从drawable右键是创建不了xml的, 具体操作如下: 右键res–& ...
- Oracle从回收站找回误删的数据
Step1 先根据删除时间查看删除了那些表 select * from recyclebin where type = 'TABLE' and createtime like '${删除时间}%' o ...
- Chevereto免费图床搭建教程 | vps搭建免费图床教程
Chevereto免费图床搭建教程 | vps搭建免费图床教程 本文首发于:https://chens.life/How-to-build-a-free-image-bed.html 前言 自从有了个 ...