关注:CodingTechWork
引言
Git 是一个强大的版本控制工具,广泛应用于现代软件开发中。它为开发人员提供了多种功能来管理代码、协作开发和版本控制。在 Git 中,有时我们需要撤销或回退某些提交,而git revert
是一个非常有用的命令。与git reset
等命令不同,git revert
能够通过创建新的提交来撤销历史提交,保证项目历史的完整性。
在本篇文章中,我们将详细介绍 Git 中 git revert
的使用方式、工作原理、实战示例以及一些注意事项。
git revert介绍
git revert
用于撤销一个已经提交到 Git 仓库的更改。不同于git reset
,git revert
会通过创建一个新的提交
来“反转”
指定提交的内容,而不会改变历史提交的记录
。因此,git revert
是一种更安全的回退操作,适用于团队合作中,因为它不会影响历史提交的结构。
git revert 的工作原理
git revert
原理:
- 生成新的提交:
git revert
会创建一个新的提交,内容是要撤销的提交的“反操作”。也就是说,git revert
会通过相反的改动来撤销指定提交的更改。例如,如果原来提交中增加了某些代码行,git revert
会通过删除这些行来撤销该提交。 - 保持项目历史的完整性:与
git reset
可能修改项目历史不同,git revert
不会删除任何提交,而是通过新的提交来记录撤销操作
,从而保证了版本历史的完整性。这使得它特别适用于公共分支(如master
或main
分支)。
执行步骤:
- Git 会根据指定的提交,计算出需要反向操作的更改。
- 创建一个新的提交,将这些反向更改应用到当前工作区。
- 你可以选择是否修改提交信息(默认信息为“Revert ”)。
- 提交反向更改,新的提交将插入到当前分支历史中。
git revert 的常见用法
撤销单个提交
假设我们在分支上做了多次提交,其中有一个提交包含了不需要的更改。使用 git log
查看提交历史:
git log --oneline
输出结果可能是这样的:
a1b2c3d (HEAD -> master) Added new feature
a1342cf Fixed bug in feature X
d4e5f6g Added README file
如果你想撤销某个特定提交,可以直接指定该提交的哈希值(commit hash)来执行git revert
。
git revert <commit-hash>
例如,如果你想撤销提交 a1b2c3d,可以运行以下命令:
git revert a1b2c3d
Git 会自动生成一个新的提交,反向应用 a1b2c3d 提交中的修改,并提示你输入提交消息。
撤销多个提交
如果你需要撤销一系列提交,可以使用 git revert
的-n
或--no-commit
选项,它会将所有反向更改暂时保存在暂存区中,但不会立即提交。你可以手动编辑这些更改,并最终一起提交。
git revert -n <commit-hash1>^..<commit-hash2>
<commit-hash1>^..<commit-hash2>
表示从<commit-hash1>
到<commit-hash2>
的提交范围。^
表示选择<commit-hash1>
的父提交,因此是一个范围。
举个例子,撤销从提交 a1b2c3d 到提交 d4e5f6g 的所有修改:
git revert -n a1b2c3d^..d4e5f6g
执行后,所有变更会暂时存放在暂存区,等待提交。
举个例子:
假设你有以下的提交历史:
* abc1234 (HEAD -> master) Merge feature-branch into master
* d7e6b2d (feature-branch) Add new feature
* 3a6c0f2 (master) Initial commit
其中,abc1234 是一个合并提交,feature-branch 被合并进了 master。现在,你想撤销这个合并提交,并希望保留 master 分支的变更,因此使用命令:
git revert -m 1 abc1234
这个命令会创建一个新的提交,撤销合并操作中的更改,实际上它将会 恢复到 abc1234 合并之前的状态,但历史记录仍会保留合并提交。新的提交将会与 abc1234 之后的提交一起继续保留在历史中。
为什么需要 -m 参数?
合并提交有多个父提交,Git 需要知道你希望保留哪个父提交的内容。-m 1
表示选择第一个父提交作为保留的版本,通常是主分支的版本。如果你使用 -m
,那么 Git 将选择第二个父提交,通常是被合并进来的分支的版本。
使用交互式 git revert
当你想撤销多个提交时,有时会遇到冲突,Git 会提示你进行冲突解决。git revert
会一个一个地撤销指定的提交,并且如果有冲突,Git 会停止并让你解决冲突。解决冲突后,你可以继续操作。
解决冲突后运行:
git revert --continue
如果你决定取消当前的撤销操作,可以使用:
git revert --abort
撤销合并提交
有时,你可能会遇到需要撤销合并提交的场景。合并提交具有特殊的结构,因此在执行 git revert 时需要额外小心。假设你有一个合并提交 abc1234,可以使用以下命令来撤销它:
git revert -m 1 abc1234
-m 1
: 指定了合并提交的“主父提交”(通常是主分支)。-m
后面的数字表示选择哪个父提交作为保留版本。
通过 git revert -m
,Git 会根据指定的父提交创建一个新的提交来撤销合并操作。
强制跳过某次提交的提交信息编辑
在某些场景下,你可能希望跳过git revert
操作中的提交信息编辑过程。可以通过--no-edit
选项来跳过:
git revert --no-edit f7e8b2c
这个命令会直接执行`git revert操作,但不会弹出编辑器让你修改提交信息,而是使用默认的信息。
实战示例:撤销错误提交
假设你正在一个开发分支feature/login
上工作,发现最近的一个提交引入了一个错误。你需要撤销该错误提交,但又不想修改历史提交(即不使用 git rese
t)。这时,git revert
就非常适合。
查看提交历史
首先,我们查看最近的提交历史,找到要撤销的提交。
git log --oneline
输出可能是这样的:
a1b2c3d Add login feature
d4e5f6g Fix UI bug
h7i8j9k Update documentation
假设提交 a1b2c3d 引入了一个错误,我们需要撤销它。
执行 git revert
接下来,使用 git revert 撤销该提交:
git revert a1b2c3d
Git 会自动生成一个新的提交,撤销 a1b2c3d 中的所有更改。
解决冲突
如果 Git 在应用反向更改时发生了冲突,Git 会提示你进行冲突解决。你需要打开冲突文件,手动解决冲突,然后执行:
git add <conflicted-file>
git revert --continue
完成提交
最终,提交就会成功创建,撤销操作也完成了。可以通过 git log 查看新的提交记录,确认撤销操作已经生效。
git log --oneline
输出可能是:
f7g8h9i Revert "Add login feature"
a1b2c3d Add login feature
d4e5f6g Fix UI bug
h7i8j9k Update documentation
着重讲一下git revert -m
git提交历史模拟
假设我们有以下的 Git 历史:
* abc1234 (HEAD -> master) Merge feat02-branch into master
* def5678 (feat02-branch) Add feature 2
* ghi2345 (feat01-branch) Add feature 1
* jkl3456 (master) Initial commit
在这个例子中:
- abc1234 是合并提交,它将 feat02-branch 合并到 master。
- def5678 是 feat02-branch 上的提交,它增加了 feat02-branch 的功能。
- ghi2345 是 feat01-branch 上的提交,它增加了 feat01-branch 的功能。
- jkl3456 是 master 上的初始提交。
合并提交 abc1234 是一个合并提交,它有两个父提交:
- jkl3456(master 分支的提交)
- def5678(feat02-branch 分支的提交)
git revert -m 1 abc1234
git revert -m 1 abc1234
表示撤销合并提交 abc1234,并保留第一个父提交(master 分支的提交)。即,撤销 feat02-branch 的更改并保留 master 分支的更改。
执行这个命令后,Git 会生成一个新的提交,撤销合并 feat02-branch 到 master
的操作,而不影响 feat01-branch 和 master 之间的关系。
命令:
git revert -m 1 abc1234
git revert -m 2 abc1234
git revert -m 2 abc1234
表示撤销合并提交 abc1234,并保留第二个父提交(feat02-branch 分支的提交)。即,撤销 master 上的更改并保留 feat02-branch 的更改。
执行这个命令后,Git 会生成一个新的提交,撤销 master
分支的更改,同时保留 feat02-branch
的更改。
命令:
git revert -m 2 abc1234
git revert -m 3 abc1234
git revert -m 3
是一个特殊情况,适用于三方合并,通常会有多个父提交。例如,如果合并提交涉及 feat01-branch、feat02-branch 和 master 三个分支,那么可以使用 -m 3 来指定第三个父提交。然而,在我们这个例子中,由于合并提交 abc1234 只涉及 master 和 feat02-branch 两个父提交,因此 -m 3 不适用。假设有三方合并的历史如下:
* abcd1234 (HEAD -> master) Merge branch 'feat01-branch' and 'feat02-branch'
|\
| * 12345678 (feat01-branch) Commit in feat01-branch
* | 87654321 (feat02-branch) Commit in feat02-branch
|/
* 11223344 Initial commit in master
在这个示例中:
- abcd1234 是合并提交,具有 3 个父提交:
-
- 父提交 1:master(在 abcd1234 之前的提交)。
-
- 父提交 2:feat01-branch(合并进来的一个分支)。
-
- 父提交 3:feat02-branch(合并进来的另一个分支)。
git revert -m 3 abcd1234
使用-m 3
时,Git 会选择第三个父提交作为基准(在这个例子中是 feat02-branch),并撤销合并提交中的更改。也就是说,撤销的合并提交会回滚 master 和 feat01-branch 的更改,但保留 feat02-branch 上的内容。
git revert -m 3 abcd1234
效果:Git 会撤销 abcd1234 的合并,回滚 master 和 feat01-branch 上的修改。结果是保留了 feat02-branch 上的修改,并回滚了 master 和 feat01-branch 的修改。
-m 总结
git revert -m 1 <commit>
:撤销合并提交,保留第一个父提交的变更。git revert -m 2 <commit>
:撤销合并提交,保留第二个父提交的变更。git revert -m 3 <commit>
:撤销合并提交,保留第三个父提交的变更(适用于三方合并)。
git revert 与 git reset 的比较
git revert vs git reset
git revert
:
- 用于撤销已经提交的内容。
- 保持历史提交的完整性,不会改变提交记录。
- 适用于公共分支,避免影响其他开发人员的工作。
git reset
:
- 用于重置当前分支的指针,可以回退到某个历史提交。
- 会改变提交历史,可能导致丢失更改。
- 适用于个人分支或本地仓库的操作。
哪个更适合团队合作?
在团队协作中,通常建议使用git revert
,因为它不会重写历史,而是创建一个新的提交,撤销先前的更改。这样可以保证团队成员之间的协作历史不受干扰。
注意事项
- 小心使用
git revert
:git revert
会创建新的提交,因此如果你频繁地进行撤销操作,可能会导致提交历史变得冗长。在撤销多个提交时,可以考虑将多个撤销操作合并到一次提交中,以保持清晰的历史记录。 - 处理冲突:当你撤销提交时,可能会遇到文件冲突。务必认真解决冲突,以确保撤销操作正确执行。
- 避免在已经发布的分支中使用 git reset:
git reset
会重写历史,这会给协作带来麻烦,而git revert
保持了历史的连贯性,因此在公共分支中,推荐使用git revert
。
总结
git revert
是 Git 中撤销历史提交的强大工具,能够通过创建新的提交来撤销指定的更改,保持项目历史的完整性,避免重写历史,适用于团队合作中的常见场景。通过理解和掌握git revert
,开发人员可以更加灵活和安全地管理代码历史,并在出现错误时轻松回退。