能否删除(或撤销)Git 提交但保留更改?
能否删除(或撤销)Git 提交但保留更改?
技术背景
在使用 Git 进行版本控制时,我们可能会因为各种原因需要撤销某次提交,但又不想丢失这次提交所做的更改。例如,临时提交了一些未完成的代码,后续想要重新组织提交,或者提交了错误的内容但希望保留修改。
实现步骤
1. 使用 git reset 命令
如果要撤销的提交是最后一次提交,且之后没有进行额外的工作,可以使用 git reset 命令。
1 | |
需要注意的是,某些 shell 会将 ^ 视为特殊字符,例如一些 Windows 系统的 shell 或启用了通配符的 ZSH。在这些情况下,可能需要将 HEAD^ 用引号括起来,或者使用 HEAD~1。
git reset 命令如果不指定 --hard 或 --soft,会将 HEAD 指向指定的提交,而不会更改任何文件。HEAD^ 表示当前提交的(第一个)父提交。
还可以设置一个别名来简化操作:
1 | |
之后就可以使用 git uncommit 来撤销最后一次提交。
2. 合并提交(Squashing)
合并提交意味着将两个或多个提交合并为一个。可以使用 git rebase -i <ref> 命令进入交互式变基模式。
- 运行
git log找到要撤销的提交,复制其 SHA1 值,替换<ref>。 - Git 会列出当前状态与
<ref>之间的所有提交。 - 将要撤销的提交前的
pick改为fixup或squash。使用fixup会丢弃该提交的消息并将更改合并到列表中的前一个提交;squash则允许编辑合并后新提交的消息。
3. 使用 git stash 暂存更改
在未来,为避免此类问题,可以考虑使用 git stash 临时存储未提交的工作。
1 | |
可以使用 git stash list 查看暂存列表,使用 git stash apply stash@{#} 恢复暂存的更改,其中 # 是暂存列表中的位置。如果要恢复最近的暂存,可以直接使用 git stash apply。
4. 针对不同 reset 模式的操作
git reset 命令有三种模式:
git reset HEAD^ --soft:撤销git commit,更改仍然存在于工作树(项目文件夹)和索引(--cached)中。git reset HEAD^ --mixed:撤销git commit和git add,更改仍然存在于工作树中。git reset HEAD^ --hard:就像从未对代码库进行过这些更改一样,更改从工作树中消失。
5. 已推送提交的处理
如果提交已经推送到远程仓库,可以使用 git revert -n <sha> 命令撤销特定提交,同时将更改保留在本地文件中。
6. 针对特定文件的撤销
如果只想撤销第一次提交中特定文件的更改,并将它们添加到第二次提交中,可以使用以下命令:
1 | |
7. 根据 reflog 进行重置
可以使用 git reflog 查看所有提交记录,找到要撤销的提交及其对应的 HEAD 编号,然后使用 git reset HEAD@{#NumberOfCommitYouWantToUndo} 进行重置。
核心代码
1 | |
最佳实践
- 在进行任何可能修改提交历史的操作(如变基)之前,先备份重要的分支或创建副本。
- 使用有意义的提交消息,以便在需要撤销或合并提交时更容易识别。
- 定期使用
git stash暂存未完成的工作,避免不必要的临时提交。
常见问题
- 提交已推送到远程仓库:如果提交已经推送给他人,修改提交历史可能会导致问题。可以使用
git revert命令创建一个新的提交来撤销之前的提交。 git reset提示输入:某些情况下,git reset HEAD^可能会提示输入。可以尝试使用git reset HEAD~#numberOfCommits来选择要重置的本地提交数量。- ZSH 特殊字符问题:在 ZSH 中,
^是特殊字符,需要进行转义,如git reset --soft HEAD\^,或者使用HEAD~避免转义。