将 git 将提交历史回退到指定提交点,主要有两个方法git reset --hard
和git rebase -i
。
建议在执行前先使用以下命令查看提交历史,确认要回退到的具体位置:
# 查看历史(Q退出)
git log
# 精简输出
git log --oneline
在 git log 界面按 q
键就可以退出。这是因为 git log 使用的是类似 less
的分页器。除了 q
退出外,你还可以用:
空格键
- 向下翻页b
- 向上翻页j
- 向下滚动一行k
- 向上滚动一行
下面将分别介绍这两个命令:
1. 使用 git reset --hard commit_id
:
# 回退到指定的提交点,比如: 2f979bf9658cd0da0d814143f6cdef31e002ebe8
# 在该提交点之后提交的内容都将会被删除掉(当前这一步还不会删除,如果有重要的内容,请提前备份)
git reset --hard 2f979bf9658cd0da0d814143f6cdef31e002ebe8
# 或者
git reset --hard 2f979bf
# 强制推送到当前分支(强制推送后,就会删除此提交点之后的内容)
git push -f origin HEAD
# 或指定具体分支名,比如 master
git push -f origin master
上面的方法,简单粗暴,会无脑打回指定提交点。
git reset
除了--hard
参数以外,还有另外两个参数--soft
和--mixed
(默认)
下面简单介绍一下 假设你:
- 修改了文件 A
- git add 添加到暂存区
- git commit 提交了变更
然后:
# 使用 --soft
git reset --soft HEAD~1
# 结果:
# - 提交被撤销
# - 文件 A 的修改还在,并且仍然在暂存区中
# - 你可以直接再次提交或修改
# 使用 --hard
git reset --hard HEAD~1
# 结果:
# - 提交被撤销
# - 文件 A 的所有修改都消失
# - 文件 A 恢复到上一次提交的状态
还有一个中间选项 --mixed
(默认选项):
git reset HEAD~1 # 或 git reset --mixed HEAD~1
# - 提交被撤销
# - 保留工作区的修改
# - 清空暂存区(需要重新 git add)
使用建议:
- 想保留文件修改并重新提交,用
--soft
- 想完全丢弃提交和修改,用
--hard
- 不确定用哪个时,先用
--soft
,更安全
对于git reset
这个命令来说,有时实际情况比较复杂,当我们想要回退时,发现在此次提交后的内容里面存在其他团队成员提交的内容且仍想保留一些提交时,这种方案就不太可行了,下面介绍一个交互式的回退方式,可以灵活配置每次提交的内容。
2. 使用 git rebase -i commit_id^
:
# 交互式 rebase 到指定 commit 的父提交
# 注意末尾的 `^` 符号表示选择这个 commit 的父提交
git rebase -i 2f979bf9658cd0da0d814143f6cdef31e002ebe8^
接下来会打开一个编辑器窗口,显示类似这样的内容:
pick abc1234 commit message 1
pick def5678 commit message 2
pick ghi9012 commit message 3
# 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
# d, drop = remove commit
最上面三行列举了该提交点之后的所有提交,默认的pick
命令就是不做任何修改。
下面详细解释这些交互式 rebase 命令的含义:
-
p
或pick
(使用提交)- 保持该提交不变
- 这是最基本的选项,直接使用这个提交,不做任何修改
-
r
或reword
(修改提交信息)- 保留提交的修改内容
- 但会暂停让你重新编辑提交信息
- 适用于修正拼写错误或完善提交描述
-
e
或edit
(编辑提交)- 暂停在这个提交
- 允许你修改这个提交的内容
- 可以增加或删除文件更改
- 完成后需要用
git commit --amend
修改 - 然后使用
git rebase --continue
继续
-
s
或squash
(合并到上一个提交)- 将这个提交合并到它上面的提交中
- 会提示你编辑合并后的提交信息
- 常用于将多个相关的小提交合并成一个有意义的大提交
-
d
或drop
(删除提交)- 完全删除这个提交
- 这个提交的所有更改都会丢失
假设你想删除最后两个提交,只保留第一个,效果如下:
pick abc1234 commit message 1
d def5678 commit message 2
d ghi9012 commit message 3
操作步骤:
- vim 编辑器:按
i
进入编辑模式,编辑完按Esc
,然后输入:wq
保存退出(nano 编辑器:直接编辑,完成后按Ctrl + X
,然后按Y
确认保存,最后按Enter
) - 如果你想删除某个提交,将那一行的
pick
改为d
或直接删除那一行 - 保留想要的提交,保持
pick
- 保存并关闭编辑器(在 vim 中是
:wq
)
实际使用示例:
# 原始提交列表
pick abc1234 添加登录功能
pick def5678 修复登录按钮样式
pick ghi9012 修正登录文案错别字
# 如果想要合并相关提交,可以改成:
pick abc1234 添加登录功能
squash def5678 修复登录按钮样式
squash ghi9012 修正登录文案错别字
# 或者如果要修改提交信息:
pick abc1234 添加登录功能
reword def5678 修复登录按钮样式
pick ghi9012 修正登录文案错别字
记住:
- 这些命令会按从上到下的顺序执行
squash
必须有一个前面的提交(不能对第一个提交使用)- 修改完保存后,可能还需要编辑额外的提交信息
- 如果操作错误,随时可以用
git rebase --abort
取消操作
如果执行后反悔了,只要你还记得原来的分支位置,可以通过以下方式恢复:
# 查看操作历史(按Q退出)
git reflog
# 恢复到操作前的状态
git reset --hard HEAD@{1} # 数字表示 reflog 中的记录序号