简介:PDF教程围绕IntelliJ IDEA中Git分支回退到指定历史版本的操作展开,面向需要掌握Git版本回退技巧的开发者,尤其适合在团队协作中遇到误提交问题的场景。资源以单一PDF文档呈现,仅1个文件,压缩后大小约729KB,内容精炼但覆盖完整。目前已有2.2万余人学习浏览,可见其参考价值。教程系统讲解了Revert与Reset Head指针两种回退方式,对比了二者在保留提交历史、处理工作区改动及远程同步上的差异;详细演示了从提交列表定位目标版本、右键执行Revert并解决冲突、提交后Push同步,以及使用Reset Current Branch to Here配合Hard模式、必要时通过git push -f强制同步远程仓库的完整流程,并给出了Hard、Mixed等模式的选择建议;还结合git log、git reflog等命令说明如何查找目标版本,帮助读者在误操作后安全恢复代码。对于使用IDEA做Git管理的开发者而言,这是一份简洁实用的参考资料。
1. IDEA 里做分支回退,先分清“回退”和“反悔”
如果你经历过一次版本事故:功能分支刚合并进 master,测了半天发现不对劲,赶紧打开 IntelliJ IDEA,在 Git Log 里右键一个看起来“那时候一切正常”的提交,点了 Reset,推不上远程,又点了 Force Push,最后代码缺了一段,同事的项目也乱了。这个标题讲的是 IDEA 里怎么把分支回退到指定的历史版本,但它真正的内核是一道选择题:你要的是“把分支指针挪回某个历史提交”,还是“保留今天之前的所有提交历史,只让代码内容回到旧版本”?IDEA 把这两个动作都藏在 Git Log 的右键菜单里,界面长得差不多,后果是完全不一样的两件事。这篇文章是我按实际血泪经验梳理出来的操作路径,适合正在用 IDEA 做多分支开发、急着在 merge 或合并后恢复某个稳定版本、又不想把团队 Git 历史搞成一锅粥的人读。
2. 回退前必须摸清的三件事:分支类型、提交位置和仓库状态
2.1 本地分支与远程分支:先判断这次的回退会不会影响别人
Git 里的分支不是文件夹,而是一个指向提交对象的“指针”。你在 IDEA 的分支列表里看到的feature/payment是本地指针,而origin/feature/payment是远程仓库指针在本地缓存的一份快照。回退一个分支前,第一件事就是看它有没有对应的远程版本。如果分支只存在于本地,从来没有 push 出去过,那这个指针怎么移动都不会影响别人,用 reset 随便折腾都没大问题。如果远程分支已经存在,甚至已经拉过 MR/PR,别人很可能基于它开出了新分支,那你本地一 reset,历史就和远程分叉了。
判断方式很简单。IDEA 里打开Alt+9的 Git 工具窗口,左侧是 Branches 面板。Local Branches 下面是你本地仓库的分支,Remote Branches 下面是远程跟踪分支。如果同一个名字两边都有,说明这是一个公共分支。你 reset 之后,IDEA 的 Log 里会出现一种奇怪的局面:本地分支指向回退后的老提交,origin/xxx仍然指向之前的旧 HEAD,看起来像两条提交线并行。这种状态一旦 push,Git 会拒绝,因为不是 fast-forward,IDEA 通常会弹一个 “Push rejected” 的红字提示。
我再补充一个更直观的命令行确认方式:
$ git branch -vv # 输出里能看到每个本地分支和远程跟踪分支的关系 # 如果 show 出一行 like: feature/payment 3f2a1c5 [origin/feature/payment] 最新提交信息 # 说明这个分支已经有远端对应,回退时必须考虑同步问题这条命令在 IDEA 的终端面板里跑一次比切对话框更快。我一般这样决策:分支过去一周只有我一个人 checkout,没有合并到 master/main,也没推过几次,那它就是接近私有分支,可以直接 Reset。只要成功推送过一次,并且在远程创建过 MR,那它就不再是“自己的分支”,所有回退操作优先用 Revert,而不是把指针硬拽回去。
2.2 工作区、暂存区与未提交变更:Hard 模式下容易弄丢的东西
回退到指定历史版本,本质上是把当前状态“覆盖”成目标提交的状态。Git 在这里有三个层次:HEAD 分支指针、暂存区 index、工作目录 working directory。日常开发时,你改过的文件可能同时存在于这三个层次里,而不同的回退模式清理的层次不同。git reset --hard会同时清空暂存区和工作区,所有未提交修改和未被跟踪的新文件都会消失;git reset --mixed只清暂存区,工作区内容保留为未暂存状态;git reset --soft移动指针但暂存区内容原封不动。
IDEA 的文件树会按颜色提示文件状态:新建绿色、修改蓝色、删除了灰色。很多人回退前根本不看这些颜色就点了 Dialog 里的 Hard,结果把自己刚写的接口实现全丢了。如果你确实带着未提交改动,又必须做 Hard Reset,先做一次备份,这是我做回退操作雷打不动的第一步:
$ git stash push -u -m "backup-before-rollback-20250210" # -u 会把未被跟踪的新文件也一起暂存 # 回退验证完成后,用 git stash pop 恢复,或者 git stash apply 指定某条更新IDEA 里也支持Git > Stash Changes...,提交说明一定要写清楚,比如backup-before-rollback-payment-v2,因为 stash 默认只能看到一句 message,不写清楚过两天就想不起来是哪个备份了。
另外你还需要知道 IDEA 自带一个不依赖 Git 的本地历史 Local History。即使你忘了 stash 直接 Hard Reset,也可以回到项目树,右键某个目录或文件,选Local History > Show History,找回 reset 之前的本地文件快照。这个功能不属于 Git,但它常常是最后一次补救手段。我的建议是,回退前不只看 stash,还要看一眼 Local History 里最近的快照时间,确认有东西可以用来兜底。
2.3 在 IDEA 的 Log 视图里定位到那个“指定的历史版本”
找提交是回退前的最后一步准备。IDEA 的 Git Log 默认显示当前分支的提交历史,如果你要找的历史版本不在当前分支的祖先链里,它压根不会出现。所以第一步是确认 Log 顶部的筛选器。IDEA 通常有一个下拉列表,可以切到All Branches,或者从左侧 Branches 面板选中某个远程分支再打开 Log。这样你至少能看到这个提交在别的分支上存在于什么位置。
历史比较长时,用筛选框输入提交信息关键词,比如版本号v1.2.3、某用户的名字、某个 commit 号片段。IDEA 会把匹配到的提交实时高亮,合并后的节点也会显示出来。选中提交之后,下方窗口会展示该提交涉及的文件列表,双击某个文件能打开 diff,这能帮你确认它是不是你要回退到的那个时间点。如果要更保险,切到终端看一眼:
$ git log --oneline --graph --all --decorate=v1.2 # --all 不局限于当前分支,能看到所有分支可达的提交 # --decorate 显示分支名和 tag 名,便于按版本号找 $ git show --stat 3f2a1c5d # 查看目标提交改动了哪些文件,判断是否符合你的预期如果 IDEA 的 Log 因为仓库太大渲染得很慢,终端是这个操作的最快补充。找到目标提交后,把它的完整哈希复制到一个临时笔记里,避免滚动窗口时丢失记忆。到这里,回退前的准备状态已经齐了:你知道分支有没有远程,你知道带着多少未提交变更,你也知道目标提交在哪里。下面就可以进入真正的回退操作。
3. 在 IDEA 中把分支回退到指定历史版本:Reset 和 Revert 两条路径
3.1 最直接的版本:在 Log 里右键目标提交,Reset Current Branch
如果你已经确认目标历史提交就在当前分支的祖先链中,“把分支指针挪回它”这个动作就是 Reset。在 IDEA 的 Log 界面中,单击目标提交后右键,菜单里选择Reset Current Branch...。弹出的对话框会列出 Soft、Mixed、Hard 三种模式,不同版本的中文 IDEA 叫“软重置”“混合重置”“硬重置”。选定模式后点 Reset,当前分支的 HEAD 就会指向目标提交。命令行的等价位是:
$ git reset --hard 3f2a1c5d # --hard 把分支指针、暂存区、工作区全部重置为目标提交状态 # 这是你“指定历史版本”最直白的落地方式 # --mixed 只重置暂存区,工作区改动保留 # --soft 只移动指针,暂存区内容不变要提醒的是,Reset 会改变当前分支的提交历史。假设你现在的历史是 A > B > C > D,而你 reset 到 A,那么 B、C、D 这三个提交会从当前分支的可达引用里消失。它们不会立刻被 Git 删除,但对普通开发者来说,在 IDEA 的界面上是找不到的。所以 Reset 只适合两种场景:一个是分支完全本地私有,没有人 clone 过;另一个是你有明确权限,并且团队约定允许改写远端历史。
如果你要回退的分支已经推送过,并且被别人拉了分支下来,Reset 会让你进入一个必须强制推送才能解决的境地。我在这篇文章里反复强调这一点,是因为大部分“回退翻车”现场,本质不是操作不会,而是选错了分支类型。IDEA 里的 Reset 对话框没有危险确认提示,点下去之后文件立刻变化,但不会生成任何“后悔提交”。所以执行前至少要按第 2 章的流程备份,或者先看一眼第 6 章的备份分支方案。
3.2 保留历史的 Revert:指定要取消的提交,生成反向提交
如果你想要的状态是“代码回到历史版本”,但不想破坏提交历史,那你应该用 Revert。IDEA 里的操作是:在 Git Log 中右键那些“不想要的提交”,选择Revert Commit。记住这里不是右键目标历史版本,而是右键目标版本之后产生的那几个提交。IDEA 会检查这些提交引入的修改,在当前 HEAD 之上生成一个反向提交。例如分支历史是 A > B > C > D,你想回到 A 的状态,需要 Revert D、C、B,得到 A > B > C > D > D' > C' > B',最终代码内容和 A 一致,但历史里 D、C、B 仍然存在。
命令行批量操作:
$ git revert --no-edit HEAD~3..HEAD # 回退最近 3 个提交,每个提交生成一个新的反向提交 # 如果中间有冲突,需要手动解决后再 git revert --continue反直觉的地方在于:你明明是想“回到 A”,但如果只对 A 提交执行 Revert,撤销的是 A 引入的改动,后面 B、C、D 的改动仍然在,最终代码不是 A 的状态。所以操作前先在终端里把目标区间的提交列出来:
$ git log --oneline 3f2a1c5d..HEAD # 这段区间里列出的提交就是需要被 revert 的对象 # 如果区间是空的,说明 HEAD 已经在目标提交上,不需要回退Revert 的好处是,推送远程时是 fast-forward,不会产生非快进分叉。代价是如果中间的提交很多,冲突解决会比较痛苦。在 IDEA 里尤其明显:每 Revert 一个提交,都可能弹一次 Merge 对话框,左侧是冲突文件列表,右侧是三个版本。逐条Mark as Resolved,最后提交一次。这个过程最好不要试图用一个 Revert 操作打完所有提交,除非你非常确定它们彼此独立。
3.3 Reset 的 Hard / Mixed / Soft 参数:选错了代码就没了
很多人在 IDEA 的 Reset 对话框里看到三选一,下意识选了最下面的 Hard。这样做没错,但有必要把参数含义拆开讲,否则你不知道自己到底在冒什么险。
| 模式 | 分支指针 | 暂存区 | 工作目录 | 回退后的典型状态 |
|---|---|---|---|---|
| Soft | 移动到目标提交 | 保留原样 | 保留原样 | 原差异全部变成已暂存状态,可以直接 commit |
| Mixed | 移动到目标提交 | 重置为目标提交的暂存状态 | 保留原样 | 原差异变成未暂存状态,可以重新 add |
| Hard | 移动到目标提交 | 重置为目标提交的暂存状态 | 重置为目标提交 | 工作区与目标提交完全一致,原差异被丢弃 |
当你明确说“把分支回退到指定的历史版本”,大多数场景要的是 Hard,因为只有 Hard 会把你的工作目录同步过去,直接看到旧版本的文件内容。而 Soft 或 Mixed 只是移动了分支指针,文件内容仍然是回退前的样子,之后你还得手动处理那一堆 diff。它们真正的用途是提交整理:比如你现在积累了几十个乱七八糟的本地提交,想先回退到一个干净的基线上,再重新提交,那用 Mixed。
我在 IDEA 里的习惯是:只有在分支仅供自己使用且已经 stash 备份之后,才选 Hard。如果分支有远程副本,且不允许 force push,那就干脆不要选 Reset,直接用 4.3 节的方案。命令行的区分再强调一次,git reset --soft经常会让人误以为代码回退了,实际上工作区没动,跑起程序还是旧行为。
4. 回退到历史版本后,怎么让远程分支跟着回退并完成同步
4.1 先看远程分支当前的状态:Push 之前先 Fetch 一次
本地 reset 或 revert 做完,不会自动影响远程仓库。这时候最容易犯的错是不做 fetch 就打开 Push 窗口,看到 IDEA 提示“本地落后”或“Push rejected”才意识到远端状态和自己手里的快照不一样。正确做法是先 fetch,再看差异。fetch 只更新远程跟踪分支,不会动你的本地分支,是安全的动作。
$ git fetch origin $ git log --oneline origin/feature/payment -5 # 查看远程分支当前还保留哪些提交 $ git log --oneline feature/payment -5 # 对比本地分支回退后的提交序列如果你用的是 Reset,本地分支会比远程分支短一截,Log 里会看到origin/feature/payment悬在本地分支后面。如果你用的是 Revert,本地分支会比远程分支多出几个反向提交,Log 里是一个正常的快进关系。这一步是决定你能不能直接 push 的关键:正常快进可以直接 push,非快进需要额外地思考要不要强推。
我见过有人在这一步直接点了 IDEA 的 Pull,想先拉下来再推,结果是本地又变回旧的完整历史,白做了一次 Reset。Pull 不是回退同步的好工具,尤其当本地分支已经被 reset 到和历史无关的位置时,Pull 只会制造更多的 merge commit,更混乱。
4.2 IDEA 里的 Force Push 入口和确认要点
当你做了 Reset 后再推送到已经存在的远程分支,IDE 会拒绝这次 push,提示信息一般是 "Push rejected: Non-fast-forward"。IDEA 的 Push 窗口右上角有一个方向向下的箭头,点开里面有Force Push选项,有些版本会在推送失败后弹出一个红色按钮明确建议你强制推送。这个入口虽然就在 UI 上,但点下去之前你要知道你正在覆盖远程的历史。
命令行等价写法:
$ git push --force-with-lease origin feature/payment # 强烈建议用 --force-with-lease 而不是 -f # 它会检查远程分支在你 fetch 之后有没有被其他人更新过 # 如果别人刚推过新提交,force-with-lease 会拒绝覆盖,等你看完再说注意别用git push -f,它是无条件的,很可能会把你同事刚推上来的提交直接从远端抹掉。IDEA 的 Force Push 在不同版本里不一定都走了 lease 逻辑,所以更稳妥的是切到终端手动执行强制推送。执行完 force push 之后,远程分支的历史和你本地回退后的历史会变成一条线,但其他同事本地的旧历史不会被自动修正,他们需要在各自仓库里重新 fetch 并决定如何处理那些“丢失”的提交。这是公共分支动历史时要付出的成本。
4.3 远程分支有保护规则,不能 force push 时怎么办
很多团队在 GitLab / GitHub / Gitea 上开启了分支保护规则,master/main 不允许任何 force push。这时候你想把 master 回退到历史版本,能走的路就只剩一条:在历史版本之上,生成一个新的普通提交,让远程分支自然快进到这个新提交。常见做法是借助临时分支来做文件级覆盖:
$ git checkout -b restore-v1 3f2a1c5d # 从目标历史版本创建临时分支,分支内容就是旧版本内容 $ git checkout feature/master # 切回原来的分支,准备把旧版本内容覆盖过来 $ git checkout restore-v1 -- . # 把 restore-v1 里的全部文件复制到当前工作区 $ git add -A && git commit -m "rollback content to v1.0.0" # 生成一个全新的回退提交 $ git push origin feature/master # 不需要 force,因为这是一个普通新提交IDEA 里对应的操作是:在 Log 右键目标提交,选择New Branch...创建临时分支;然后切回原分支,在项目树中全选文件,右键Git > Add,或者直接在终端用git checkout restore-v1 -- .覆盖整个工作区。这个方案保留了原来的所有历史,也保留了回退记录,远程分支始终是 fast-forward。缺点是一次提交里会包含非常多的文件修改,review 的人会看得很辛苦,但这是受保护分支上回退的代价。
如果回退的内容范围很小,还可以直接用 IDEA 的右键Git > Revert,只撤销几个具体的提交,避免一次性覆盖全部代码。可如果明确目标是“回到某个历史版本的完整快照”,临时分支覆盖法最直接。
4.4 回退的是 merge 提交或功能分支合并时怎么做
“idea中如何回退merge操作”是一个高频问题。假设功能分支feature/a合入 master,生成了 merge 提交M,现在要撤销整个功能。你直接在 IDEA 里右键M选 Revert Commit,如果 Git 版本较老或 IDEA 处理不够聪明,可能报错:merge commit 需要指定-m参数。命令行的正确处理是:
$ git show --summary 3a2b1c9d # 输出里会看到 Merge: 7a2d3e4 9f8c7b6 # 第一个 parent 是主分支侧,第二个 parent 是并入的功能分支侧 $ git revert -m 1 3a2b1c9d # -m 1 表示保留主分支这一侧,撤销被合并进来的功能分支侧IDEA 的 Revert 对话框在某些版本里会尝试自动填充 -m,但不一定填对。如果你的目标只是让 master 回到 merge 之前的状态,应该选用 -m 1。如果选成 -m 2,实际上会保留功能分支的内容而撤销主分支原有的提交,结果基本不可预测。
Revert merge 之后还要特别注意:merge 提交本身没有被删除,功能分支上的原始提交也都还在。如果以后再次把feature/a合并进来,之前被 revert 掉的代码可能重新出现。处理这一类问题的规范做法是:revert merge 之后,不要继续在原功能分支上开发,而是从回退后的 master 重新拉一个新分支来进行后续修复。这也是很多团队踩过坑后总结出来的分支规范。
5. 回退到历史版本过程中常见的五个坑:现象、原因、解决
5.1 Hard Reset 后,未提交的代码彻底消失
现象:做了git reset --hard,原先改了一半的接口文件、新建的配置文件全不见了。IDEA 的 Log 里也没有任何提交能找回它们。
原因:Hard Reset 不生成新提交,而是直接把暂存区和工作区重置为目标提交的状态。未提交、未跟踪的文件在 Git 对象模型里没有引用,reset 之后就像被物理删除。
解决:reset 前用git stash push -u备份,已经发生的话,先在 IDEA 里用 Local History:右键受影响目录,选Local History > Show History,IDEA 会把文件编辑的历史快照列出来,找到 reset 之前的版本并恢复。Local History 的缺点是它只记录 IDE 打开过的文件,新建后没保存过的文件可能没有记录,所以备份才是正解。
5.2 把公共分支 Reset 后 Force Push,同事本地历史全乱
现象:你在 master 上 reset 到旧版本,然后 force push。同事第二天拉取时出现很多冲突,本地分支和 origin/master 形成分叉,甚至有人在合并时丢失了功能代码。
原因:公共分支的历史被重写,原来的提交从远程引用链上消失了。同事本地分支的父提交还指向旧的 HEAD,fetch 后无法完成 fast-forward 合并,只能 rebase 或手动调整。
解决:公共分支不要 reset。如果已经发生,先告诉所有受影响的人不要继续操作,自己在本地使用git reflog找回被覆盖的提交,创建临时备份分支推送出去,让同事从备份分支 cherry-pick 他们需要的改动。以后回退公共分支一律使用 Revert,这应该作为团队约定写进分支规范里。
5.3 Revert 之后再次合并同一个功能分支,被撤销的代码又出现
现象:功能分支feature/a合并到 master,你 revert 了这次 merge。过了两周,团队继续在feature/a上开发,再次合并到 master 时,之前被 revert 掉的老代码全都回来了。
原因:Revert 只生成一个反向提交,它没有删除功能分支上的原始提交。第二次合并时,feature/a上依然包含最早那批提交,它们的改动又会被重新引入,和之前 revert 的语义互相矛盾。
解决:这是一种常见的 Git 语义冲突。正确做法是 revert merge 之后,不要在原功能分支上继续累积提交。如果需求还想继续实现,从当前 master 重新拉feature/a-v2分支,在新分支上只包含新改动。如果实在需要恢复原分支,可以考虑 revert 之前那次 revert,但也要确保没有其他订正叠加。
5.4 在 IDEA 的 Log 里找不到目标历史版本
现象:Log 界面里怎么搜都搜不到某个发布提交,右键也没有 Reset / Revert 选项,好像它从仓库里消失了一样。
原因:IDEA 默认只显示“当前分支可达”的提交。如果目标历史版本在另一个分支上,或者曾经被git reset --hard移除,那它当然不会出现在当前分支的 Log 里。另外,Log 顶部的筛选器可能被限定成了具体分支名,导致提交被过滤掉。
解决:把 Log 筛选器切到All Branches或Show All。还找不到就在终端执行git reflog --all,reflog 会记录本机所有分支的移动历史,即使提交已不可达也能看到哈希。找到哈希后执行git branch recover 3f2a1c5d,把它挂到一个新分支上,再在 IDEA 中切到该分支继续找回内容。
5.5 回退后 IDEA 仍然显示旧代码,运行起来不对劲
现象:reset 成功,文件内容看着也变了,但运行测试时行为还是回退前的,甚至编译失败,提示找不到某些类。
原因:IDEA 的增量编译、模块缓存和运行配置没有及时刷新。分支指针移动后文件系统确实变了,但 IDE 的 build 缓存还在使用旧的 class 文件。
解决:先执行Build > Rebuild Project。Maven 项目在终端跑mvn clean compile,Gradle 项目跑gradle clean build,把旧的编译产物清掉。如果还不行,用File > Invalidate Caches / Restart...,勾选清理文件系统缓存并重启 IDEA,让它重新索引整个项目。这一个步骤看起来和 Git 无关,却是回退后判断“到底回没回退成功”的关键环节。
6. 回退前的双保险:临时分支 + Reflog 兜底与快速验证
6.1 动手前先给当前分支造一个备份分支
回退之前,我每次都会做两件小事:把当前 HEAD 的提交哈希写下来,再创建一个备份分支。备份分支的内容就是回退前的完整状态,创建后不要切换过去,也不急着推远程,但最好在确认安全后推一份,防止本地磁盘出问题。
$ git branch backup/payment-20250210 # 这个命令只创建分支,不切换分支,方便立刻继续操作 $ git push origin backup/payment-20250210 # 推送后,备份分支留在远程,即使后面 reset 失误,也能随时找回IDEA 分支菜单里的New Branch...有一个缺点:默认会切换到新分支,打断操作思路。所以我更习惯在终端跑git branch。这个备份分支的成本很低,一条命令而已,但对于一个运行了两周的开发分支来说,它是回退事故里唯一的后悔药。
6.2 用 Reflog 找回被 reset 抹掉的旧版 HEAD
如果备份分支忘了建,也不要立刻放弃。Git 在本地维护着一个 reflog,记录 HEAD 发生移动的历史,包括 reset 前的位置。在 IDEA 的终端里输入:
$ git reflog --date=iso | head -20 # 你会看到类似 3b53941 HEAD@{0}: reset: moving to 3f2a1c5d # 上面这一条里的 3b53941 就是 reset 之前的分支头,立刻固定它 $ git branch rescue/old-head 3b53941 # 把旧 HEAD 挂到 rescue 分支上,防止它被后续 Git 操作回收Reflog 只存在于本地,远程仓库看不到。所以只要你在 reset 后没有执行git gc --prune=now,旧提交通常还能找回来。这也是我为什么在文件丢失时先让大家看 reflog,而不是直接跑git fsck。如果 reflog 里也找不到,再考虑git fsck --unreachable,但那个命令输出很杂,不是特别有必要。
6.3 用两个 diff 检查回退是否正确
回退完成的标志不是 IDEA 不报错,而是文件内容确实和指定历史版本一致。我最常用的验证命令就两段:
$ git diff --stat 3f2a1c5d HEAD # 回退成功后,这条命令输出的差异应该非常小或为空 # 如果输出为空,说明当前 HEAD 和目标提交的内容一致 $ git status --porcelain # 输出为空,表示工作区干净,没有残留未提交的旧改动如果 diff 还有内容,先确认是不是编译产物、IDE 配置文件或.gitignore忽略的文件造成的。排除干扰后再用git diff 3f2a1c5d HEAD -- src/只看源码目录。只有当这里也干净了,我才放心把结果 push 到远程。
说实话,我以前第一反应也是 Hard Reset。每次都要被同事的冲突提醒一次才长记性。后来我在分支规范里加了三条硬规则:分支有没有被别人推过?目标历史版本和当前 HEAD 之间有没有别人的合并提交?这次操作是不是必须重写远程历史?三条都回答完,才知道该选 reset 还是 revert,要不要 force push。这个习惯救过我不止一次,也希望能帮你在 IDEA 里少踩一次回退的坑。
本文还有配套的精品资源,点击获取