Skip to content

Lisiur's Blog

Git 中的各种恢复

分类计算机技术
标签#git
最后编辑时间2023/05/25 14:03
状态已发布
创建时间2023/05/19 18:09

前置知识

git 有三个区域,分别为工作区(working-tree),暂存区(stage)和历史区(history-tree)。

git 图例

常见的操作为:

  • 在工作区做修改

  • 通过 git add 命令添加到暂存区

  • 通过 git commit 命令提交到历史区

常见场景

撤销工作区的修改

  • git restore

撤销 git add

git add 的本质是更新了暂存区的索引,默认情况下,暂存区的索引和历史区一致,git add 更新暂存区后,git status 会显示当前暂存区和历史区的差异,这部分差异即为 git add 的结果。

git add 后的图例

因此撤销 git add 的本质就是从历史区恢复暂存区的索引。

git restore --staged 可以做到这一点。

git restore --stated 会使用历史区的索引覆盖暂存区

git restore 🖼️ 图片做对比

撤销 git commit

git commit 的本质是把暂存区的索引提交到历史区。

git commit 后的图例

撤销 commit 的本质就是让 HEAD 指向要撤销的 commit 的前一个 commit 即可。

git reset --[soft|mixed|hard] HEAD^ 都能回退到 HEAD^ (上一个commit),区别如下:

  • soft: 不改变当前工作区的内容,不改变暂存区的内容。

    git reset --soft HEAD^ 撤销历史区的最近一次修改

  • mixed: 不改变当前工作区的内容,改变暂存区的内容。

    git reset --mixed HEAD^ 撤销历史区的最近一次修改,并将历史区的索引同步到暂存区

  • hard: 改变当前工作区的内容,改变暂存区的内容。

    git reset --hard HEAD^ 撤销历史区最近一次修改,并将历史区的索引同步到暂存区和工作区

组合拳

  • 场景一: commit 前发现有个文件不该放到该次 commit
    git restore --staged <pathspec> # 将文件踢出暂存区
    git commit -m 'bla bla'         # commit
    
  • 场景二: commit 后发现内容写错了
    git reset --soft HEAD^  # 撤销 commit
    git commit -m 'bla bla' # 重新 commit
    
    # 或者
    git commit --amend
    
  • 场景三: commit 后发现有些文件不该放到该次 commit
    • 拳法一: 不该放的文件比较少
      git reset --soft HEAD^          # 撤销 commit
      git restore --staged <pathspec> # 将不合群的文件踢出暂存区
      git commit -m 'bla bla'         # 重新 commit
      
    • 拳法二: 不该放的文件比较多
      git reset HEAD^         # 撤销 commit
      git add <pathspec>      # 将合群的文件加入暂存区
      git commit -m 'bla bla' # 重新 commit
      
  • 场景四: 撤销工作区的修改
    git restore <pathspec>
    

一点建议

  1. git reset 也可以用来撤销 git add ,但 git reset 的本意是为了操控历史区,其对暂存区和工作区的操作算是副作用。我们使用 git reset 来撤销 git add,即从HEAD 恢复暂存区的内容,本质上是使用 git reset HEAD,回退 commit 到 HEAD,相当于没有回退,只是使用其同时能撤销暂存区的修改的副作用。
  2. 此外,git checkout 所承载的功能非常之多,最常用的就是用于切换分支,但同时却也能撤销工作区的修改,这很令人困惑,并且该操作很危险,比如当执行 git checkout dev 时,你不知道其是切换到 dev 分支,还是撤销 dev 文件的修改。因此,git restore 被引入,该命令的本意就如同其名称所隐含的那样:恢复。因此,我个人建议使用 git restore 来恢复工作区或者暂存区。(另外使用新的命令 git switch 来切换分支)
  3. 一定要慎用 git reset —hardgit restore ,这两个命令会丢弃工作区的内容,请注意是永久性的丢弃工作区的内容!