ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

18 — reset / restore / switch(重置三兄弟:轻 / 中 / 重)

18 — reset / restore / switch(重置三兄弟:轻 / 中 / 重) 18 — reset / restore / switch重置三兄弟轻 / 中 / 重# 18 — reset / restore / switch重置三兄弟轻 / 中 / 重摘要本文系统讲解 Git 中三个易混淆的撤销命令——reset、restore 和 switch。通过“书桌/作业篮/档案柜”三棵树模型清晰区分各命令的作用范围reset 用于回退提交历史分 soft、mixed、hard 三档restore 用于恢复文件内容switch 用于切换分支。文章包含详细对比表格、决策流程图、安全等级评估及多个实战场景帮助读者建立安全的 Git 操作习惯。reset 三种模式对比三区书桌 / 作业篮 / 档案柜详解写在前面这一章要解决什么学完后你应该能一眼分清 reset、restore、switch 各自干什么——再也不会混看到别人写git reset --hard时知道这有多危险想撤销某步操作时知道该用哪个命令、加什么参数说出 “三棵树”书桌 / 作业篮 / 档案柜各自的含义以及每条命令动了哪棵读者设定大一同学刚接触命令行零项目经验。你只需要会add和commit剩下的这章带你走。1. 定位1.1 一句话先记住reset 往回拨指针轻 / 中 / 重看参数restore 把拿出来的东西放回去switch 换频道切到别的分支1.2 搞不清这三兄弟会怎样把reset --hard当成 “撤销提交” 的一般操作 → 工作区所有未提交的改动一瞬间消失找都找不回来把restore和reset混用 → 该放回文件的地方却动了提交历史把switch和reset混用 → 该换分支的时候却把当前分支指针拨飞了网上搜 “git 撤销” 出来一堆checkout的老教程越看越晕1.3 和你已经会的事对比你已经会git add把书桌上的文件放进作业篮和git commit把作业篮里的文件归档到档案柜。现在要学的三个命令本质上就是在问“我想往回拨档案柜的指针” →reset“我想把文件放回原来的样子” →restore“我想换一个分支频道” →switch以前这三个活儿都让checkout一个命令干所以新手经常搞混。Git 2.23 之后把它们拆成了三个命令各管各的清楚多了。2. 本质三棵树模型复习Git 里有三个 “地方” 存着你的文件状态我们用生活场景来记术语白话类比工作区(worktree)你正在编辑的文件夹书桌——你眼前摊开的作业暂存区(index / staging area)下一次提交要收录的内容作业篮——准备交但还没交的作业HEAD / 分支最近一次提交指向的快照档案柜——已经归档的作业它们之间的关系书桌 ──(git add)──→ 作业篮 ──(git commit)──→ 档案柜 ←─(git restore)── ←─(git reset)──────git add书桌 → 作业篮git commit作业篮 → 档案柜git restore从作业篮或档案柜把东西放到书桌上恢复文件内容git reset把档案柜的指针往回拨移动分支指向哪个提交三兄弟的比喻想象你在学校犯了点小错老师有三种处理方式命令比喻动了什么危险程度git reset --soft轻轻提醒——“下次注意”不罚你只动 HEAD档案柜的标签安全git reset --mixed警告——记录扣掉你要重做动 HEAD 暂存区档案柜标签 作业篮中等git reset --hard重罚——一切清零全部重来动 HEAD 暂存区 工作区三棵树全动核武器级别git restore把拿出来的东西放回去恢复工作区或暂存区的文件内容安全不动提交git switch换频道——换到别的节目只动 HEAD换到另一个分支安全3. 建议学习顺序先理解三棵树书桌/作业篮/档案柜→ 知道每个命令动了哪棵先学switch最安全只换频道再学restore恢复文件不动历史最后学reset三档从轻到重最危险放最后看对照表把全章串起来做小实验亲手验证4. 动手准备mkdirgit-reset-labcdgit-reset-labgitinitgitconfig user.name “Ada Example”gitconfig user.email “adaexample.com”白话翻译创建一个实验仓库设好身份。这个仓库随时可以删放心折腾。git--versiongit version 2.43.0白话翻译确认 Git 版本。本书所有示例基于 2.43.0你的版本只要 2.23有 switch/restore就行。先造点提交记录用来做实验echo第一版file.txtgitaddfile.txtgitcommit-m“v1: 第一版”echo第二版file.txtgitaddfile.txtgitcommit-m“v2: 加了第二版”echo第三版file.txtgitaddfile.txtgitcommit-m“v3: 加了第三版”白话翻译造了三个提交就像把三份作业依次放进档案柜。现在file.txt里有三行文字。5. 跟着做5.1 switch换频道gitswitch-cfeatureSwitched to a new branch feature白话翻译创建并切到feature分支——就像换了一个频道但书桌上的东西没变。gitbranch* feature main白话翻译星号在feature旁边说明你当前在这个分支。gitswitch mainSwitched to branch main白话翻译换回main频道。书桌上的文件会跟着main分支的状态变。关键点switch只动 HEAD换频道不会动书桌上还未提交的改动。如果有未提交的改动和目标分支冲突Git 会提示你先处理。5.2 restore把拿出来的东西放回去先搞点改动echo“随手写的”file.txt白话翻译在书桌上随便加了点东西还没 add。gitstatusOn branch main Changes not staged for commit: (use git add file... to update what will commit) (use git restore file... to discard changes in working directory) modified: file.txt白话翻译Git 说 file.txt 在书桌上被改了还没放进作业篮。注意 Git 已经在提示你用restore了恢复工作区书桌上的改动不要了gitrestore file.txt白话翻译把 file.txt 恢复成档案柜里最新提交的样子。书桌上那行 “随手写的” 没了。catfile.txt第一版 第二版 第三版白话翻译确认 “随手写的” 这一行确实消失了文件回到了最近一次提交的状态。现在试另一种情况已经 add 了想从作业篮里拿出来echo“放进去又后悔的”file.txtgitaddfile.txtgitstatusOn branch main Changes to be committed: modified: file.txt白话翻译改了文件并且 add 了改动在作业篮里。你后悔了想把作业篮里的改动退回书桌。gitrestore--stagedfile.txt白话翻译--staged的意思是 “我操作的对象是作业篮”。把 file.txt 从作业篮里拿出来放回书桌状态但书桌上的文件内容不变。gitstatusOn branch main Changes not staged for commit: (use git add file... to update what will commit) (use git restore file... to discard changes in working directory) modified: file.txt白话翻译改动从作业篮回到了书桌。如果连书桌上的改动也不想要了再执行一次git restore file.txt就行。restore 小结git restore 文件→ 书桌上的改动不要了恢复工作区git restore --staged 文件→ 作业篮里的改动拿出来取消暂存书桌上的内容不变它永远不会动 HEAD / 分支指针只管文件内容。5.3 reset --soft轻轻提醒先看看当前的提交历史gitlog--onelinea3c1d2e v3: 加了第三版 b4e5f6a v2: 加了第二版 c7d8e9b v1: 第一版白话翻译三个提交最新的叫 v3。gitreset--softHEAD~1白话翻译把档案柜的指针往回拨一个提交HEAD~1表示 “上一个提交”。--soft 轻轻提醒只动档案柜的标签。gitlog--onelineb4e5f6a v2: 加了第二版 c7d8e9b v1: 第一版白话翻译v3 不见了档案柜的标签指到了 v2。gitstatusOn branch main Changes to be committed: modified: file.txt白话翻译但改动并没有丢v3 的内容还在作业篮里等着你重新提交。相当于老师说了 “这次不算你重新交”但你的作业还在篮子里不用重写。如果你后悔了想恢复 v3gitcommit-mv3: 加了第三版重新提交白话翻译把作业篮里的东西重新提交就行了什么都没丢。5.4 reset --mixed默认警告先把历史搞回三个提交echo第三版file.txtgitaddfile.txtgitcommit-mv3: 加了第三版现在来试--mixed这是 reset 的默认行为不写也行gitreset HEAD~1白话翻译等同于git reset --mixed HEAD~1。把档案柜标签往回拨一个同时清空作业篮。gitlog--onelineb4e5f6a v2: 加了第二版 c7d8e9b v1: 第一版白话翻译v3 又从历史里消失了。gitstatusOn branch main Changes not staged for commit: (use git add file... to update what will commit) modified: file.txt白话翻译注意和--soft的区别改动不在作业篮里了而是回到了书桌上。你需要重新add才能提交。相当于老师把你的作业从篮子里拿出来扔回书桌“重新抄一遍再交”。5.5 reset --hard重罚核武器先把历史搞回三个提交gitaddfile.txtgitcommit-m“v3: 加了第三版”echo还没提交的新东西file.txt白话翻译提交了 v3又在书桌上写了新内容但没 add。gitreset--hardHEAD~1白话翻译核武器发射档案柜标签往回拨 作业篮清空 书桌上所有未提交的改动全部消灭。gitlog--onelineb4e5f6a v2: 加了第二版 c7d8e9b v1: 第一版gitstatusOn branch main nothing to commit, working tree clean白话翻译干干净净就像 v3 从来不存在书桌上那行 “还没提交的新东西” 也消失了。找不回来了除非你用 reflog后面会说。相当于老师把你作业撕了、篮子倒了、桌上也擦干净了。一切归零。永远不要对已经 push 到远程的提交使用reset --hard你的队友已经基于那个提交工作了你把历史改了他们就乱套了。6. 命令分组换频道组只动 HEAD命令白话动了什么git switch 分支名换到指定分支HEAD 指向新分支git switch -c 新分支名创建新分支并切过去先创建分支再动 HEAD恢复内容组动文件不动 HEAD命令白话动了什么git restore 文件书桌上的改动不要了工作区文件恢复成暂存区的样子git restore --staged 文件作业篮里的改动拿出来暂存区恢复成 HEAD 的样子工作区不变git restore --staged --worktree 文件两个都恢复暂存区和工作区都恢复成 HEAD 的样子拨指针组动 HEAD可能连带更多命令白话动了什么git reset --soft 目标轻轻提醒只动 HEADgit reset --mixed 目标警告默认动 HEAD 暂存区git reset --hard 目标重罚核武器动 HEAD 暂存区 工作区7. 对照表这是全章最重要的部分哪条命令动了哪棵树命令工作区书桌暂存区作业篮HEAD/分支档案柜标签git switch 分支跟着变*—移动git restore 文件恢复——git restore --staged 文件—恢复—git restore --staged --worktree 文件恢复恢复—git reset --soft 目标——移动git reset --mixed 目标—恢复移动git reset --hard 目标恢复恢复移动* switch 换分支时工作区的文件会变成目标分支的样子。如果有未提交的改动和目标分支冲突Git 会拒绝切换。决策流程“我想撤销什么”你想撤销什么 │ ├─ 想撤销书桌上的修改还没 add │ └→ git restore 文件 │ ├─ 想撤销 add 操作文件还在书桌上但不想放进作业篮了 │ └→ git restore --staged 文件 │ ├─ 想撤销最近一次提交内容还要只是提交本身想撤 │ └→ git reset --soft HEAD~1 │ ├─ 想撤销最近一次提交 取消暂存内容回书桌重新整理再交 │ └→ git reset HEAD~1 │ ├─ 想把所有改动全部清空回到某个历史状态确定不要了 │ └→ git reset --hard 目标 │ └─ 想换到另一个分支 └→ git switch 分支名reset 三档直观对比reset --soft: HEAD 移动 ──→ 暂存区不动 ──→ 工作区不动 档案柜标签拨了篮子和书桌原封不动 reset --mixed: HEAD 移动 ──→ 暂存区重置 ──→ 工作区不动 档案柜标签拨了篮子清空书桌上的东西还在但需要重新 add reset --hard: HEAD 移动 ──→ 暂存区重置 ──→ 工作区重置 三个全动标签拨了、篮子倒了、桌子擦了安全等级一览安全程度命令能否轻松恢复最安全switch天然安全不会丢东西安全restore天然安全只管文件内容不动历史安全reset --soft随时重新 commit 就回来中等reset --mixed改动回到书桌重新 add 就行危险reset --hard需要reflog才可能抢救8. 安全习惯按回车之前先git status—— 看看当前状态想清楚你要动的是书桌、作业篮还是档案柜永远不要对已 push 的提交使用reset --hard—— 你改了别人的历史队友的仓库会乱套拿不准就用--soft—— 最安全什么都没丢随时可以重来书桌上永远只放当前任务相关的东西—— 乱糟糟的书桌容易误操作小步提交—— 每次只提交一个逻辑改动这样reset回退时粒度更细不会一刀切太多记住reflog这根救命稻草——万一reset --hard后悔了git reflog能看到最近的 HEAD 移动记录有可能救回来在可丢弃的仓库里练习——本章的实验仓库随时可以删掉重来9. 真实场景场景一提交信息写错了gitcommit-mfix bux# 拼写错误解法还没 push用--soft撤回重新写提交信息。gitreset--softHEAD~1gitcommit-mfix bug白话翻译轻轻拨回一个提交改动还在作业篮里重新提交就行了什么都没丢。场景二add 了一个不该提交的文件gitadd.# 哎呀debug.log 也被 add 了解法从作业篮里拿出来。gitrestore--stageddebug.log白话翻译只把 debug.log 从作业篮里拿出来回到书桌上。其他已经在篮子里的文件不受影响。场景三提交了两个功能想拆成两次提交gitlog--onelinea1b2c3d 加了功能A和功能B解法gitreset HEAD~1# 现在所有改动回到书桌上gitadd功能A的文件gitcommit-m“加功能A”gitadd功能B的文件gitcommit-m“加功能B”白话翻译用默认的--mixed把提交撤回改动回到书桌然后分两次 add commit。场景四实验搞砸了全部重来gitreset--hardHEAD~3白话翻译核武器最近三个提交 所有未提交的改动全部消灭。只有在确定不要这些内容时才用。如果后悔了gitrefloga1b2c3d HEAD{0}: reset: moving to HEAD~3 d4e5f6a HEAD{1}: commit: v3: 加了第三版 ...白话翻译reflog 记录了 HEAD 的每一次移动。找到你想回去的那个编号然后gitreset--hardd4e5f6a白话翻译用 reflog 里的哈希值恢复回去。但这是最后一根稻草别指望每次都能救回来。场景五切分支做热修gitswitch-chotfix# 修 bug ...gitadd.gitcommit-m“修复紧急 bug”gitswitch maingitmerge hotfix白话翻译开一个热修分支修完再合回主分支。switch只管换频道安全操作。10. 进阶补充为什么 restore/switch 替代了 checkout在 Git 2.23 之前git checkout一个命令干了太多事git checkout 分支→ 切分支现在是git switchgit checkout -- 文件→ 恢复工作区现在是git restoregit checkout HEAD -- 文件→ 恢复暂存区 工作区现在是git restore --staged --worktree一个命令干三件事参数还容易搞混。拆开之后每个命令只干一件事不容易出错。老命令现在还能用Git 不会删老功能但新命令更清晰推荐优先使用。HEAD~1是什么意思HEAD 当前提交HEAD~1 当前提交的父提交往前一个HEAD~2 往前两个HEAD~3 往前三个你也可以用提交的哈希值代替比如git reset --soft a1b2c3d。checkout和新命令的对应关系旧命令新命令说明git checkout 分支git switch 分支切分支git checkout -b 新分支git switch -c 新分支创建并切到新分支git checkout -- 文件git restore 文件恢复工作区git checkout HEAD -- 文件git restore --staged --worktree 文件恢复暂存区和工作区reflog后悔药git reflog记录了 HEAD 的每一次移动默认保留 90 天。即使你reset --hard了只要 90 天内大概率能找回来gitreflog# 找到你想回去的那个条目gitreset--hard那个哈希但 reflog 不是万能的只记录 HEAD 的移动不记录单个文件的修改超过 90 天会被自动清理git gc垃圾回收后可能就真没了所以预防永远比补救重要。11. 小实验在前面创建的git-reset-lab仓库里完成以下实验。每一步先猜结果再运行命令验证。实验 1验证 restore 不动 HEAD记录当前 HEADgit log --oneline -1echo “测试” file.txt git add file.txtgit restore --staged file.txt再看 HEADgit log --oneline -1验证HEAD 哈希没变——restore 确实不动提交历史实验 2对比 soft 和 mixed造一个新提交echo 新内容 file.txt git add file.txt git commit -m 测试提交git reset --soft HEAD~1git status→ 改动应该在暂存区git reset HEAD~1即 --mixedgit status→ 改动应该回到工作区不在暂存区对比两次git status的区别实验 3体验 hard 的 “核爆”echo 会消失的东西 file.txt不 addecho 也会消失的 another.txt git add another.txtgit reset --hard HEAD检查file.txt和another.txt→ 都没了尝试git reflog看看有没有后悔药实验 4switch 的安全保护echo 未提交 file.txtgit switch -c another-branch如果成功了没冲突说明 Git 允许带着未提交的改动换分支回去改出冲突在另一个分支上提交对 file.txt 的不同修改再在有未提交改动时git switch main→ 会报错体会switch 在有冲突时会拒绝切换保护你的改动12. 常见问题 FAQ问 1reset 和 restore 到底什么区别核心区别reset 动的是指针分支指向哪个提交restore 动的是文件内容。reset是把 “档案柜的标签” 往回拨连带可能影响作业篮和书桌restore是把 “文件的内容” 恢复成某个版本的样子不动标签问 2reset --hard 之后真的找不回来了吗大概率能救——用git reflog找到之前的 HEAD 哈希再用git reset --hard 那个哈希恢复。但如果已经过了 90 天或者执行过git gc那就真没了。所以别把它当常规操作。问 3我应该用checkout还是新命令用新命令。switch和restore拆开之后语义更清楚不容易搞混。老命令还能用但新项目建议从头就用新命令。问 4git reset HEAD不加--soft也不加--hard会怎样默认是--mixed也就是动 HEAD 暂存区不动工作区。等同于git reset --mixed HEAD。问 5restore 可以恢复到任意历史版本吗可以。git restore --source某个哈希 文件能把文件恢复到任意提交时的样子。不写--source默认从暂存区恢复写了--staged则从 HEAD 恢复。问 6switch 和 reset 都动 HEAD区别在哪switch是把 HEAD 指向另一个分支不改变当前分支指向的提交reset是把当前分支本身的指向往回拨改变了历史打个比方switch 是换频道别的频道还在reset 是把当前频道的进度条往回拉。问 7已经 push 到远程的提交被 reset 了怎么办如果你 reset 之后又 push需要git push --force。这会覆盖远程的历史所有基于旧历史工作的队友都会出问题。正确做法对已 push 的提交用git revert下一章会讲来新建一个 “反向提交”而不是改写历史。问 8实验里的哈希值和书上不一样正常吗正常。哈希值是根据你的文件内容和提交时间算出来的每台机器都不一样。关键看命令结构和git status的输出模式是否一致。13. 总结三个命令三个职责命令一句话核心记忆switch换频道只动 HEAD切分支restore把东西放回去只动文件内容不动历史reset往回拨指针动 HEAD根据参数可能连带动暂存区和工作区reset 三档参数比喻动了什么口诀--soft轻轻提醒HEAD提交撤了篮子还在--mixed警告HEAD 暂存区提交撤了篮子清了桌子还在--hard重罚核武器HEAD 暂存区 工作区三个全动想清楚再按回车最重要的安全规则不确定就用--soft已 push 的提交不要 reset按回车之前先git status核武器--hard只在可丢弃的仓库里用参考资料与延伸阅读以Git 2.43.0验证。演示身份Ada Example adaexample.com。Pro Git 中文git-switch 文档git-restore 文档git-reset 文档图示署名:assets/diagrams/ATTRIBUTION.md
返回列表