做了这么多年开发,我见过太多人在SourceTree上点错一个按钮,然后慌慌张张跑来问“提交没了怎么办”。SourceTree这个Git图形客户端确实好用,日常提交、拉取、推送都直观,尤其对不习惯敲命令的同事特别友好。但真碰上要撤销提交、恢复提交这种操作时,界面上那几个选项很容易让人犯迷糊——重置到提交、回退提交、反向提交……名字长得挺像,作用却完全不同。
这篇文章就聚焦其中两种最常用的“重置”操作,解决一个核心问题:提交出了状况,怎么用SourceTree的软重置和硬重置把提交恢复回正确的状态。同时会把它背后到底发生了什么、重置前要考虑什么、重置后翻车了怎么补救,一起讲清楚。内容偏实战,跟着操作就能落地,适合正在用SourceTree、又被“重置”功能绕晕的人。
1. 认识SourceTree的重置功能:先搞清楚能做什么
1.1 Git重置的三种模式:软重置、混合重置、硬重置
重置的本质是移动HEAD指针。HEAD在Git里就是“当前分支指向的提交”,你日常提交、切换分支,其实都在移动HEAD。而reset(重置)就是强制把HEAD移动到你指定的某个提交上,同时根据你选择的模式,决定中间被跳过的这些提交里的改动到底何去何从。
Git的reset分三种模式:
- Soft(软重置):只移动HEAD,中间提交的改动全部留下,而且是以“已暂存”的状态待在暂存区,相当于这些文件已经被git add过了。你重新提交的时候,改动内容原封不动地等着你。
- Mixed(混合重置):移动HEAD,清空暂存区,但工作区里的文件内容不动。改动都还在,只是从“已暂存”变成“未暂存”,需要你重新git add。这是Git reset的默认模式。
- Hard(硬重置):移动HEAD,清空暂存区,还把工作区里的文件内容直接覆盖回目标提交的状态。中间提交的改动全部彻底丢弃,包括你还没提交的本地修改——这是最危险、也最需要谨慎的模式。
用一个不恰当的类比来说明:就像你在一份文档里写了几段话,Soft是“保留草稿、方便重新排版”,Mixed是“把文字打回原形但内容还在”,Hard则是“不保存直接关文档,让你从头来过”。
在SourceTree的图形界面里,这三种模式对应的就是重置弹窗里的三个选项。理解它们之间的差别,比记命令参数重要多了。
1.2 SourceTree里的重置入口与回退提交的区分
在SourceTree中,选中提交列表里的任意一个提交,右键菜单里能看到“重置到提交...”选项。点击后会弹出窗口,要求你选择重置模式,默认通常是混合重置(Mixed)。选择模式后确认,SourceTree就帮你执行对应的git reset操作。
要注意的是,SourceTree界面上还有一个“回退提交”(Revert Commit)选项,很多人会把重置和回退搞混。回退不是移动HEAD,而是基于当前状态创建一个新提交,这个新提交的内容是把目标提交的改动反向应用——比如目标提交里加了某行代码,回退提交就是删掉这行代码。回退的好处是历史记录不会被改写,特别适合已经推送到远程的公共分支。
以我的经验,新手第一步先分清“重置”和“回退”这两种操作,后面才不容易出事。重置是改写历史,回退是新增一个反向提交来抵消历史,两者方向完全不同。
另外提一句,不同版本的SourceTree界面会有细微差别,比如Mac版和Windows版的菜单位置不完全一样,但“重置到提交”这个核心入口基本都在右键菜单里,老版本可能叫法稍有差异。如果你找不到,可以按快捷键或者去顶部菜单栏的“仓库”下面翻一翻。
2. 两种重置恢复提交的完整实操:软重置与硬重置都要会
2.1 软重置实操:提交写错、漏文件、合并提交一步搞定
软重置最典型的场景有三个:提交信息写错了、提交之后发现漏了文件、想把最近几个提交合并成一个。
我拿“提交信息写错”来拆解操作步骤。假设你本地连续提交了两次,第一次提交信息是“fix: 修复登录错误”,结果后来发现漏了一个关键修改,第二次提交就随手写了“wip”。现在你想把这两次提交合并成一次,提交信息统一改成“fix: 修复登录错误”。
操作步骤是这样的:
- 在提交列表里选中第二次提交的上一个提交(也就是第一次提交“fix: 修复登录错误”)
- 右键 -> “重置到提交...”,在弹窗里选择Soft模式
- 确认后,SourceTree会把HEAD回退到第一次提交的位置,同时把第二次提交的所有改动放回暂存区
- 此时看SourceTree左侧的文件状态面板,会看到这些改动处于“已暂存”状态
- 点击“提交”按钮,重新编写提交信息,比如还是写“fix: 修复登录错误”
- 确认提交即可
这样两次提交就合并成一次了,历史记录干干净净。整个过程中,改动内容没有丢失,只是被你从“已提交的某一次”挪回到了“暂存区”,再重新提交而已。
还有一个更轻量级的场景:如果只是单纯想改最近一次提交的提交信息,不用软重置这么麻烦。直接在SourceTree提交按钮右侧找“更改上次提交”(Amend)功能,改完信息后确认即可。Amend本质就是“软重置到上一个提交再重新提交”的封装,一条命令两秒钟搞定的事,没必要手动走重置流程。
2.2 硬重置实操:彻底丢弃无用提交回到干净状态
硬重置就是“不给自己留退路”的重置方式。操作后,目标提交之后的所有提交会从当前分支指针上消失,工作区和暂存区也会被强制同步到目标提交的状态。如果没有推送到远程,这些提交就只能在reflog里找了。
硬重置适合这些场景:写了一堆没用的提交想直接丢弃、分支合并失败想回到合并前、本地实验代码改坏了想全部清空重新来。
操作步骤:
- 在提交列表里找到你想回去的那个提交点
- 右键 -> “重置到提交...”,这次在弹窗里选择Hard模式
- SourceTree会弹出比较醒目的警告,确认后执行重置
执行完之后,你再看看提交历史,目标提交之后的提交全部消失了,工作区也变成一个干净的状态。这个效果很利落,但代价是:所有未提交的更改也会被一并清除。如果你手头还有没保存的工作,哭都来不及。
所以硬重置之前,我强烈建议做两个动作:
- 如果有未提交的改动,先在SourceTree左侧点击“贮藏”(Stash)按钮,输入一个说明把这批改动暂存起来,重置完成后再通过“应用贮藏分支”恢复
- 再确认一下你选中的目标提交,确实是你想回去的那个点。万一选错,重置完整个历史就变了
我在实际项目里用硬重置最多的场景是分支合并崩了:冲突一堆,手动处理得乱七八糟,最后干脆重置回合并前的状态,重新来一次干净合并。
2.3 混合重置:SourceTree里默认的那个选项有什么用
虽然标题说两种重置,但SourceTree重置弹窗里默认的其实是混合重置(Mixed),很多朋友可能已经用过但没注意。
混合重置介于软和硬之间:HEAD回到目标提交,暂存区被清空,但工作区文件内容保持不变。效果就是改动都还在,只是所有文件都变成“未暂存”状态,需要你重新选择、重新git add再提交。
这个模式比较适合“只想撤销暂存”的场景。比如你一口气把几十个文件全部git add了,但其实只想提交其中某几个,用混合重置可以把这些暂存全部清掉,文件内容不会丢,然后再有选择地重新暂存需要提交的文件。
不过在SourceTree图形界面里,选择要暂存哪些文件本来就是拖拽和勾选的事,比敲命令方便得多,所以混合重置在SourceTree里的存在感确实相对低一些。真正高频的,还是软重置和硬重置这两个。但理解混合重置能帮你串起整条知识线,明白重置模式下HEAD、暂存区、工作区三者分别会发生什么变化。
3. 实战场景:误操作后怎么把提交找回来
3.1 改提交信息:用Amend还是软重置
场景描述:你本地刚提交了一个修复,commit信息写成了“修搞”,或者写着写着把分支名也写进去了,变成“feat/xxx-修复”这种不规范的格式。提交还没推送到远程,现在想改成一个规范的写法。
最快的路径是Amend(更改上次提交)。在SourceTree提交界面的“提交”按钮附近找到“更改上次提交”选项,点击后在信息框里把提交信息改成规范写法,确认即可。如果这个提交已经推送到远程,那就需要强制推送才能覆盖远端历史,团队协作时得先跟同事打招呼。
那我为什么还要在前面讲软重置呢?因为Amend只适用于“最近一次提交”。如果你需要修改的不是最近一次,而是前面某一次提交的信息,Amend就无能为力了。这时候必须用软重置回到目标提交的上一个提交,把后面的提交全部“打散”回暂存区,然后重新提交,一步到位。
这两个功能定位不同:Amend是轻量化改最近一次提交,软重置是处理历史里更靠前的提交。新手容易在SourceTree里到处找“修改历史提交信息”的按钮,实际上SourceTree没有直接提供这个入口,用软重置是标准解法。
3.2 补漏文件:软重置合并进上次提交
场景描述:提交完代码突然想起来,有个配置文件或者资源文件忘了加进去。这个文件很重要,不能单独开一个新提交,必须合并进刚才那次提交。
用软重置是最顺手的:
- 右键目标提交(就是刚才那个漏了文件的提交),选“重置到提交...”,模式选Soft
- 确认后,所有改动回到暂存区
- 把漏掉的文件重新暂存
- 点击提交按钮,提交信息会自动保留原来的内容,直接确认即可
这样漏掉的文件就并进同一个提交了,提交历史里看不出任何破绽。同事拉取代码时也不会看到一坨“补充漏提交的文件”之类的多余提交。
这里有个操作细节:软重置之后,提交信息留在输入框里的,通常是目标提交的信息。如果你不改,重新提交时它会用原来的信息;如果你想微调,直接在输入框里改就行。我自己的习惯是每次这种操作前看一眼提交信息,确认它确实是我想保留的内容。
3.3 合并冲突处理崩了:硬重置回到合并前
场景描述:你在SourceTree里把分支A合并到分支B,冲突一大堆,手动改得乱七八糟,越改越乱,最后干脆想放弃合并,回到合并之前的状态。
这种情况硬重置是最快的解法。找到合并提交的父提交——也就是合并之前B分支指向的那个提交,右键“重置到提交...”,模式选Hard。确认后,工作区和提交历史全部回到合并前,那堆冲突修改全部消失。
操作细节:合并前如果有未提交的改动,被冲突处理折腾一通后可能已经“混”进工作区了。建议先点“贮藏”把改动暂存起来,然后再硬重置,重置完之后再“应用贮藏分支”,把没提交的工作成果恢复出来。不然硬重置一执行,这些改动和冲突现场一起没了。
我在实际项目里用过很多次这个操作。合并崩了不要慌,只要还没推送到远程,硬重置回退永远比手动撤销一堆冲突修改要高效。
3.4 大招:硬重置之后反悔,用reflog找回丢失提交
这是最有价值的一节,请务必记住——硬重置之后反悔,提交不会真的消失,Git的reflog会记录下来。
reflog是Git里的“操作日志”,记录HEAD每一次移动的历史,包括你重置前的提交位置。即便你把提交历史重置得面目全非,reflog里仍然能找到那些“被删除”的提交哈希。
具体操作:
- 在SourceTree顶部菜单找到“仓库” -> “打开终端”(不同版本可能叫“终端”或者“命令提示符”)
- 执行命令查看reflog:
git reflog- 会看到类似这样的输出:
a1b2c3d HEAD@{0}: reset: moving to b2c3d4e f5e6d7c HEAD@{1}: commit: 修复登录错误 9a8b7c6 HEAD@{2}: commit: 增加导出功能- 找到你想恢复的那条提交记录(比如f5e6d7c),记下它的哈希
- 回到SourceTree,右键当前分支,选择“签出...”,或者在终端直接执行:
git cherry-pick f5e6d7c这条命令会把那个“丢失”的提交重新应用到当前分支上,提交内容完整恢复。
需要特别说明的是,豪横的硬重置并没有物理删除提交对象,Git的机制决定了它在reflog过期内不会立即清理。所以只要你在提交后短时间内反悔,几乎100%能找回来。我帮同事排查过好几次意外重置的现场,靠这一招把重要提交从“历史垃圾箱”里捞了回来。
如果你在SourceTree界面上仍然能看到丢失的提交(有些情况下重置后提交列表里还会短暂显示),直接右键那个提交选“签出”也能恢复。但硬重置后通常列表里就看不到了,所以reflog必须学会用。
4. 常见问题与避坑指南:重置前先搞清楚的几件事
4.1 重置和回退提交的区别,一表看懂
这是新手问得最多的问题,也是绕开误区最重要的一步。我把两者的区别整理成了一张表,建议收藏:
| 对比项 | 重置到提交(Reset) | 回退提交(Revert) |
|---|---|---|
| 原理 | 移动HEAD指针到目标提交 | 创建新提交反向应用目标提交的改动 |
| 历史记录 | 会被改写,目标提交之后的提交从分支上消失 | 保留原提交,额外多一个新提交 |
| 是否影响远程 | 本地重置后需要强制推送,不然与远程分叉 | 普通推送即可,安全 |
| 适用场景 | 本地开发阶段、提交还没推送到远程 | 提交已经推到远程、团队协作分支 |
| 风险 | 硬重置可能丢失未提交的更改 | 基本无风险 |
一句话总结:还没推送的提交,用重置很舒服;推送到公共分支的提交,优先用回退。
4.2 已推送的分支能重置吗:本地和远程的分叉问题
能重置,但有代价。
如果你已经把提交推送到远程仓库,回到本地把HEAD重置到一个更早的位置,这时候本地和远程就出现了分叉。再想推送,SourceTree会提示你“推送被拒绝”,因为远程有本地没有的提交。
这时候你需要勾选“强制推送”选项,把远程历史强制覆盖成本地的样子。强制推送在单人开发或自己维护的分支上问题不大,在团队协作分支上非常危险——同事基于旧历史拉取的代码可能还在他们本地,你强制推上去,同事下次推送就会出现严重冲突,甚至弄丢别人的提交。
所以我的原则很明确:本地分支随便重置;已推送到公共分支的提交,哪怕写错了,也优先用回退提交产生新提交来修复,而不是重置加强制推送。如果你负责的分支只有你一个人在用,强制推送倒是可以接受,但要养成提前通知团队的习惯。
4.3 硬重置会清掉未提交更改:贮藏功能怎么配合
这是最常见的翻车原因。很多朋友以为硬重置只是把提交历史去掉,没想到工作区里辛辛苦苦改了一半的代码也一起被覆盖了。
执行硬重置之前,我建议做两步检查:
- 看SourceTree左侧文件状态面板,有没有未提交的更改。有的话,先点“贮藏”(Stash)按钮把这批改动保存起来
- 再确认一下提交列表里选中的目标提交,确实是你想去的那个点
贮藏的操作很简单:在SourceTree左侧找到“贮藏”按钮,点击后输入一个说明文字,比如“登录模块改动WIP”,确认即可。硬重置完成后,点击“应用贮藏分支”就能把之前没提交的更改恢复回来。整套流程我每次操作硬重置前都会走一遍,给人感觉就像买了个意外险,踏实。
跟贮藏相关的还有一个容易困惑的点:新加的文件能不能贮藏?答案是可以的,SourceTree的贮藏功能会把未跟踪的新文件也一起保存进去(前提是你勾选了对应的包含未跟踪文件的选项)。如果你有一个新文件还没add,重置前想保留它,用它就对了。
4.4 我个人的安全重置流程:动手前先问自己三个问题
最后分享一个我这几年的操作习惯,算是踩坑踩出来的总结。每次在SourceTree里准备点“重置到提交”之前,先花30秒问自己三个问题:
- 这个提交推送到远程了吗?推送到公共分支了吗?——如果推送了,就别用重置,改用回退提交
- 重置之后,我不想丢的改动还在不在?——不确定就先去贮藏
- 如果反悔了,我能不能找回它?——至少要知道reflog怎么用
这三问过一遍,重置操作基本不会出大乱子。哪怕是硬重置,只要reflog还在,提交就还有救。剩下的,就是大胆操作了。
按我个人经验,SourceTree里真正让人畏惧的从来不是功能复杂,而是对底层逻辑不清楚。重置无非就是移动HEAD指针加清理暂存区和工作区,想清楚这一点,软重置、硬重置用起来就非常顺手,还能举一反三解决很多提交历史问题。后面我还会继续更新SourceTree的其他实用功能,比如贮藏的进阶用法、分支对比、历史记录审查这些,都是平时工作里高频又容易踩坑的点。先把重置练熟,Git这条路就算走通一半了。