# 代码回滚

# 场景一: 仅在工作区做了修改

此时做的修改,仅仅是在工作区进行了修改,还没有执行添加到暂存区操作(git add),也就更不可能执行了提交操作(git commit)。

## 注意,文件名前有 双短横线
git checkout -- <文件>

# 比如,撤回对 main.js 文件的修改
git checkout -- main.js
1
2
3
4
5

需要注意的是,这些改动因为从来没有提交到 Git 仓库中过,Git 无法追踪其历史版本,一旦回滚就相当于直接丢弃这些修改了。

场景:刚 clone 或 pull 了代码到本地,对一个文件做了一些修改,但发现这些修改不想要了,想恢复到 clone 时的状态,此时就可以对这个文件执行 git checkout

# 场景二: 已添加(add)到暂存区中

当修改已经添加(git add)到暂存区中,但还没有执行提交 (git commit) 时,可以通过 reset 来进行回滚:

git reset HEAD -- <文件>
1

执行后,只是将文件修改从暂存区移回到工作区,修改还依然存在,如果要确认放弃修改的内容,可执行场景一的操作。

# 场景三: 已提交(commit), 但还没有 push

已提交修改到本地的 Git 仓库,但是还没有推送到远程仓库时:

# 将当前工作区回滚到某一次 commit 之后,并将回滚的所有文件修改保留在工作区
git reset <commit hash>
# --hard 表示不保留修改内容到工作区,直接回滚到某一次 commit 之后的状态
git reset --hard <commit-hash>
1
2
3
4

# 场景四:已提交(commit), 但还没有 push, 想修改最后一次 commit

当 commit 之后,还没有 push 到远程的时候,发现刚才的修改还需要优化,如果直接修改后再次 add 并 commit 的话,就会多生成一次 commit 记录。但我们只想要一次提交记录。

方式一:就像场景三中一样,将刚才不完美的 commit 先通过 reset 撤回,等修改完善之后再一次性进行提交。

方式二:commit 时合并到最后一次提交中,修改内容会合并,并且提交说明会覆盖最后一次的提交说明。

# --amend 表示完善提交记录,将本次的修改合并到最后一次 commit 中,并且以本次的 commit message 为准
git commit --amend -m <commit-message>
1
2

# 已 push 的修改

对于任何一次已经 push 到远程的 commit 提交记录,都不能用 git reset <commit> 来执行,因为 reset 会丢弃指定的 commit 之后的所有 commit,这样修改之后再 push 的话会和远程仓库造成冲突。

比如,假设当前本地和远程均有 a, b, c, d 四次提交记录,在本地执行 reset b 之后,做出一些修改,然后 add 并 commit 生成提交记录 e , c 和 d 就会被丢弃,此时本地的提交记录就只有 a, b, e, 而远程仓库的提交记录是 a, b, c, d, 这样本地和远程仓库的提交记录不一致,就会造成冲突,无法 push 到远程。

所以,针对已经 push 到远程的提交记录,不能使用 git reset 来执行回滚操作,而是应该使用 git revert 来完成。

git revert 会将某一次指定的 commit 作为新的提交,还是以上面的例子为例,本地和远程均有 a, b, c, d 提交记录,revert b 之后,会将 b 的修改内容添加到暂存区,提交后会生成新的提交记录 e,此时本地的提交记录为 a, b, c, d, e, 远程为 a, b, c, d, 没有冲突,可以直接 push 到远程。

# 版本恢复时,不要直接进行提交,可以先进行编辑再进行提交
git revert -n <commit-hash>
# 版本恢复时,直接先将指定的提交记录作为一次新的提交
git revert <commit-hash>
1
2
3
4