如果修改过的文件,不仅添加到暂存去,还提交到了本地仓库,已经无法撤销修改,但是可以回退到修改前的版本。

前置知识

HEAD指针

git仓库初始化之后,默认会创建一个master分支,即主分支。分支是git对版本进行管理的时间线,记录着每次提交,自动把它们串成一条时间线。
在master这条分支时间线上有很多版本的时间节点【commit-id】,而HEAD指针指向的是当前分支最新一次的版本时间节点。

  1. * 63cd517 (HEAD -> master) v3
  2. * 678c5a5 v2
  3. * c34a6ba v1

git后悔药-reset - 图1

HEAD可以表示相对位置:

  • 单独HEAD表示当前工作分支的最新提交版本
  • 使用^表示前一个版本,例如HEAD^ ,如果要表示前2个版本,HEAD^^,依此类推。
  • 如果版本跨度大,使用 ^就不科学,可使用~加数字表示当前版本之前的第几个版本。使用HEAD~10表示为最新版本往前第10个版本。
  • 也可以使用HEAD@{num}的方式表示。HEAD@{0}就相当于HEADHEAD@{1}相当于HEAD^

git后悔药-reset - 图2

reflog命令介绍

git reflog可以查看到被退回的commit-id历史,而git log只能查看当前HEAD之前的提交版本。
所以git reflog是引用日志,可以查看所有历史版本信息,该命令大多是为了进行版本回退或恢复操作使用,可以从中找到所需的commit 索引。
git后悔药-reset - 图3

reset命令

reset命令可以实现代码回退,有soft、mixed、hard三种类型回退方式。

—soft软回退

git reset --soft commit-id,回退到指定的版本。soft软回退,仅仅修改分支中HEAD指针的位置,不改变工作区和暂存区代码。实际只是移动了本地仓库HEAD指针的指向。
在本地创建测试项目,并添加readme.md文件,添加内容并提交,目的是回退到该版本。

  1. # 使用git log查看历史版本记录
  2. shuais-MacBook-Pro:reset shuai$ git log --oneline
  3. * 63cd517 (HEAD -> master) v3
  4. * 678c5a5 v2
  5. * c34a6ba v1
  6. # 使用reflog查看历史版本记录
  7. shuais-MacBook-Pro:reset shuai$ git reflog
  8. 63cd517 (HEAD -> master) HEAD@{0}: commit: v3
  9. 678c5a5 HEAD@{1}: commit: v2
  10. c34a6ba HEAD@{2}: commit (initial): v1
  11. # 查看readme.md文件内容
  12. shuais-MacBook-Pro:reset shuai$ cat readme.md
  13. v3

向readme文件添加一行数据,并提交到本地仓库

  1. shuais-MacBook-Pro:reset shuai$ echo "new line" >> readme.md
  2. shuais-MacBook-Pro:reset shuai$ cat readme.md
  3. v3new line
  4. shuais-MacBook-Pro:reset shuai$ git commit -a -m "commit new line v4"
  5. [master a5e5191] commit new line v4
  6. 1 file changed, 1 insertion(+), 1 deletion(-)
  7. shuais-MacBook-Pro:reset shuai$ git log --oneline
  8. a5e5191 (HEAD -> master) commit new line v4
  9. 63cd517 v3
  10. 678c5a5 v2
  11. c34a6ba v1

现在可以对比工作区、暂存区、本地仓库中代码的差异。此时他们三个并无差异

  1. # 对比工作区和暂存区中,差异
  2. shuais-MacBook-Pro:reset shuai$ git diff readme.md
  3. # 对比 暂存区 和本地仓库中内容差异
  4. shuais-MacBook-Pro:reset shuai$ git diff --cached readme.md

执行reset回退

  1. # 回退到上一个提交记录
  2. shuais-MacBook-Pro:reset shuai$ git reset --soft HEAD^

回退后对比文件内容

  1. # 对比工作区和暂存区文件差异,此时并无差异
  2. shuais-MacBook-Pro:reset shuai$ git diff readme.md
  3. # 对比暂存区和仓库中文件差异
  4. shuais-MacBook-Pro:reset shuai$ git diff --cached readme.md
  5. diff --git a/readme.md b/readme.md
  6. index 04d0d54..6f67266 100644
  7. --- a/readme.md
  8. +++ b/readme.md
  9. @@ -1 +1 @@
  10. -v3
  11. \ No newline at end of file
  12. +v3new line
  13. # 对比工作区和仓库中文件的差异
  14. shuais-MacBook-Pro:reset shuai$ git diff HEAD readme.md
  15. diff --git a/readme.md b/readme.md
  16. index 04d0d54..6f67266 100644
  17. --- a/readme.md
  18. +++ b/readme.md
  19. @@ -1 +1 @@
  20. -v3
  21. \ No newline at end of file
  22. +v3new line

对比后发现,三者之前文件的差异

  • 工作区和暂存区无差异
  • 暂存区 和 仓库中有差异
  • 工作区和仓库中 有差异

    说明: 使用soft参数工作区和暂存区中内容没有回退,只有仓库中内容回退

查看提交日志

  1. # 使用log查看历史版本记录
  2. shuais-MacBook-Pro:reset shuai$ git log --oneline
  3. 63cd517 (HEAD -> master) v3
  4. 678c5a5 v2
  5. c34a6ba v1
  6. # 使用reflog查看历史记录
  7. shuais-MacBook-Pro:reset shuai$ git reflog
  8. 63cd517 (HEAD -> master) HEAD@{0}: reset: moving to HEAD^
  9. a5e5191 HEAD@{1}: commit: commit new line v4
  10. 63cd517 (HEAD -> master) HEAD@{2}: commit: v3
  11. 678c5a5 HEAD@{3}: commit: v2
  12. c34a6ba HEAD@{4}: commit (initial): v1

可以看到log和reflog命令的区别:

  • 使用git log命令查看历史记录,已经看不到a5e5191这次提交记录
  • 使用git reflog命令查看历史记录,可以看到a5e5191记录还在

    恢复到回退前版本

    ```git

    查看工作目录中文件状态

    shuais-MacBook-Pro:reset shuai$ git status On branch master Changes to be committed: (use “git restore —staged …” to unstage)
    1. modified: readme.md

    此时将文件退回了暂存区中,通过commit提交即可。

提交记录

shuais-MacBook-Pro:reset shuai$ git commit -m “append newline v5” [master d352dd8] append newline v5 1 file changed, 1 insertion(+), 1 deletion(-)

查看目录中文件状态

shuais-MacBook-Pro:reset shuai$ git status On branch master nothing to commit, working tree clean

使用log查看历史记录

shuais-MacBook-Pro:reset shuai$ git log —oneline d352dd8 (HEAD -> master) append newline v5 63cd517 v3 678c5a5 v2 c34a6ba v1

已经看不到第4次提交

使用reflog查看历史记录,可以查看到所有历史记录

shuais-MacBook-Pro:reset shuai$ git reflog d352dd8 (HEAD -> master) HEAD@{0}: commit: append newline v5 63cd517 HEAD@{1}: reset: moving to HEAD^ a5e5191 HEAD@{2}: commit: commit new line v4 63cd517 HEAD@{3}: commit: v3 678c5a5 HEAD@{4}: commit: v2 c34a6ba HEAD@{5}: commit (initial): v1

通过回退 a5e5191,还有回退到第4次提交

shuais-MacBook-Pro:reset shuai$ git reset —soft a5e5191

shuais-MacBook-Pro:reset shuai$ git log —oneline a5e5191 (HEAD -> master) commit new line v4 63cd517 v3 678c5a5 v2 c34a6ba v1

会生成一条新的commit提交日志,可以看出a5e5191和第4次提交的commit一样,说明已经回退到第4次

shuais-MacBook-Pro:reset shuai$ git reflog a5e5191 (HEAD -> master) HEAD@{0}: reset: moving to a5e5191 d352dd8 HEAD@{1}: commit: append newline v5 63cd517 HEAD@{2}: reset: moving to HEAD^ a5e5191 (HEAD -> master) HEAD@{3}: commit: commit new line v4 63cd517 HEAD@{4}: commit: v3 678c5a5 HEAD@{5}: commit: v2 c34a6ba HEAD@{6}: commit (initial): v1

  1. <a name="IO3zf"></a>
  2. ### --mixed命令回退
  3. > mixed是混合的,中等的回退。该命令不仅修改仓库的HEAD指针,还将暂存区的数据进行了回退。但是工作区的代码状态不变
  4. 创建本地测试文件,新建readme.md文件,并添加内容。
  5. ```git
  6. # 使用log查看提交历史记录
  7. shuais-MacBook-Pro:rest shuai$ git log --oneline
  8. c45fa60 (HEAD -> master) v3
  9. 86d8436 v2
  10. d5030ef v1
  11. # 使用reflog查看历史记录
  12. shuais-MacBook-Pro:rest shuai$ git reflog
  13. c45fa60 (HEAD -> master) HEAD@{0}: commit: v3
  14. 86d8436 HEAD@{1}: commit: v2
  15. d5030ef HEAD@{2}: commit (initial): v1

新增一行数据,并提交到仓库中

  1. # 新增一行数据
  2. shuais-MacBook-Pro:rest shuai$ echo "new line " >> readme.md
  3. # 提交数据
  4. shuais-MacBook-Pro:rest shuai$ git commit -a -m "第4次提交,add new line"
  5. [master 7bce542] 4次提交,add new line
  6. 1 file changed, 1 insertion(+)
  7. # 查看此时仓库的日志
  8. shuais-MacBook-Pro:rest shuai$ git log --oneline
  9. 7bce542 (HEAD -> master) 4次提交,add new line
  10. c45fa60 v3
  11. 86d8436 v2
  12. d5030ef v1

现在对比工作区、暂存区、本地仓库中文件的差异。此时3者并无差异

  1. # 对比工作区和暂存区文件差异
  2. shuais-MacBook-Pro:rest shuai$ git diff readme.md
  3. # 对比暂存区和仓库文件的差异
  4. shuais-MacBook-Pro:rest shuai$ git diff --cached readme.md
  5. # 对比工作区和仓库文件的差异
  6. shuais-MacBook-Pro:rest shuai$ git diff HEAD readme.md

执行回退操作,退回v3版本

  1. shuais-MacBook-Pro:rest shuai$ git reset --mixed HEAD^
  2. Unstaged changes after reset:
  3. M readme.md
  • Unstaged changes after reset: 说明回退后,有未被追踪的文件
  • M readme.md:表示readme.md文件修改后,未被追踪。即修改后,文件未添加到暂存区的状态
  • 此时文件被退回到了工作区的状态

回退后,对比文件差异

  1. # 对比工作区和暂存区中文件差异
  2. shuais-MacBook-Pro:rest shuai$ git diff readme.md
  3. diff --git a/readme.md b/readme.md
  4. index 46b68dc..36125a4 100644
  5. --- a/readme.md
  6. +++ b/readme.md
  7. @@ -1 +1,2 @@
  8. hello world v3
  9. +new line
  10. # 对比暂存区和仓库中文件差异
  11. shuais-MacBook-Pro:rest shuai$ git diff --cached readme.md
  12. #对比工作区和仓库中文件差异
  13. shuais-MacBook-Pro:rest shuai$ git diff HEAD readme.md
  14. diff --git a/readme.md b/readme.md
  15. index 46b68dc..36125a4 100644
  16. --- a/readme.md
  17. +++ b/readme.md
  18. @@ -1 +1,2 @@
  19. hello world v3
  20. +new line

通过对比可以发现:

  • 工作区和暂存区的内容出现了差异
  • 暂存区和仓库中没有差异
  • 工作区和仓库中 出现了差异

    查看提交日志

    ```git

    使用log查看历史提交记录

    shuais-MacBook-Pro:rest shuai$ git log —oneline c45fa60 (HEAD -> master) v3 86d8436 v2 d5030ef v1

使用reflog查看提交记录

shuais-MacBook-Pro:rest shuai$ git reflog c45fa60 (HEAD -> master) HEAD@{0}: reset: moving to HEAD^ 7bce542 HEAD@{1}: commit: 第4次提交,add new line c45fa60 (HEAD -> master) HEAD@{2}: commit: v3 86d8436 HEAD@{3}: commit: v2 d5030ef HEAD@{4}: commit (initial): v1

  1. - log查看时,第4次提交记录已经不存在
  2. - reflog查看提交记录,第4次提交记录仍然存在
  3. <a name="AKGoW"></a>
  4. #### 恢复到回退之前版本
  5. `git reset --mixed commit-id`命令回退,做了2个操作
  6. - 移动HEAD指针,即回退仓库中的代码
  7. - 回退暂存区中的内容,将暂存区内容退到HEAD指针指向的版本
  8. ```git
  9. shuais-MacBook-Pro:rest shuai$ git status
  10. On branch master
  11. Changes not staged for commit:
  12. (use "git add <file>..." to update what will be committed)
  13. (use "git restore <file>..." to discard changes in working directory)
  14. modified: readme.md
  15. no changes added to commit (use "git add" and/or "git commit -a")

readme.md文件是修改未暂存状态。接下来要先添加到暂存区【add】,然后在提交【commit】

  1. # 添加到暂存区
  2. shuais-MacBook-Pro:rest shuai$ git add .
  3. #提交到仓库
  4. shuais-MacBook-Pro:rest shuai$ git commit -m "append new line v5"
  5. # 使用log查看提交历史
  6. shuais-MacBook-Pro:rest shuai$ git log --oneline
  7. ead44f4 (HEAD -> master) append new line v5
  8. c45fa60 v3
  9. 86d8436 v2
  10. d5030ef v1
  11. #使用reflog查看提交历史
  12. shuais-MacBook-Pro:rest shuai$ git reflog
  13. ead44f4 (HEAD -> master) HEAD@{0}: commit: append new line v5
  14. c45fa60 HEAD@{1}: reset: moving to HEAD^
  15. 7bce542 HEAD@{2}: commit: 4次提交,add new line
  16. c45fa60 HEAD@{3}: commit: v3
  17. 86d8436 HEAD@{4}: commit: v2
  18. d5030ef HEAD@{5}: commit (initial): v1
  1. # 执行回退到第四次提交7bce542
  2. shuais-MacBook-Pro:rest shuai$ git reset --mixed 7bce542
  3. # 查看文件状态
  4. shuais-MacBook-Pro:rest shuai$ git status
  5. On branch master
  6. nothing to commit, working tree clean
  7. # log查看版本提交历史
  8. shuais-MacBook-Pro:rest shuai$ git log --oneline
  9. 7bce542 (HEAD -> master) 4次提交,add new line
  10. c45fa60 v3
  11. 86d8436 v2
  12. d5030ef v1
  13. # 使用reflog查看提交的历史
  14. shuais-MacBook-Pro:rest shuai$ git reflog
  15. 7bce542 (HEAD -> master) HEAD@{0}: reset: moving to 7bce542
  16. ead44f4 HEAD@{1}: commit: append new line v5
  17. c45fa60 HEAD@{2}: reset: moving to HEAD^
  18. 7bce542 (HEAD -> master) HEAD@{3}: commit: 4次提交,add new line
  19. c45fa60 HEAD@{4}: commit: v3
  20. 86d8436 HEAD@{5}: commit: v2
  21. d5030ef HEAD@{6}: commit (initial): v1

—hard命令回退

git reset --hard commit-id回退到指定版本,hard强硬的,严格的回退。该参数的回退,会把工作区和暂存区中的数据都回退到指定版本。【该命令谨慎使用】
本地创建测试代码库,创建出readme.md文件,git init初始化项目

  1. # 使用log查看历史记录
  2. shuais-MacBook-Pro:reset shuai$ git log --oneline
  3. 229cc1f (HEAD -> master) v3
  4. be516b5 v2
  5. 04b38f8 v1
  6. #使用reflog查看历史记录
  7. shuais-MacBook-Pro:reset shuai$ git reflog
  8. 229cc1f (HEAD -> master) HEAD@{0}: commit: v3
  9. be516b5 HEAD@{1}: commit: v2
  10. 04b38f8 HEAD@{2}: commit (initial): v1
  11. # 向readme.md文件新增数据new line
  12. $ echo "new line" >> readme.md
  13. # 提交到本地仓库
  14. shuais-MacBook-Pro:reset shuai$ git commit -a -m "第4次提交,新增内容new line "
  15. [master 6804892] 4次提交,新增内容new line
  16. 1 file changed, 1 insertion(+), 1 deletion(-)
  17. # 查看历史日志
  18. shuais-MacBook-Pro:reset shuai$ git log --oneline
  19. 6804892 (HEAD -> master) 4次提交,新增内容new line
  20. 229cc1f v3
  21. be516b5 v2
  22. 04b38f8 v1

现在可以对比工作区、暂存区、本地仓库中代码的差异。此时他们三个并无差异。

执行回退操作,退回到v3版本

  1. # 回退到前一个提交记录的版本
  2. shuais-MacBook-Pro:reset shuai$ git reset --hard HEAD^
  3. HEAD is now at 229cc1f v3

HEAD is now at 229cc1f v3 意思是HEAD现在位于 229cc1f 提交记录,可以看到就是v3的版本记录。

回退后对比文件差异

  1. # 对比工作区和暂存区
  2. shuais-MacBook-Pro:reset shuai$ git diff readme.md
  3. # 对比暂存区和仓库
  4. shuais-MacBook-Pro:reset shuai$ git diff --cached readme.md
  5. # 对比工作区和仓库
  6. shuais-MacBook-Pro:reset shuai$ git diff HEAD readme.md

此时,工作区、暂存区、仓库都退回到了执行版本。

查看提交日志记录

  1. # 使用log查看提交历史记录
  2. shuais-MacBook-Pro:reset shuai$ git log --oneline
  3. 229cc1f (HEAD -> master) v3
  4. be516b5 v2
  5. 04b38f8 v1
  6. # 使用reflog查看提交历史记录
  7. shuais-MacBook-Pro:reset shuai$ git reflog
  8. 229cc1f (HEAD -> master) HEAD@{0}: reset: moving to HEAD^
  9. 6804892 HEAD@{1}: commit: 4次提交,新增内容new line
  10. 229cc1f (HEAD -> master) HEAD@{2}: commit: v3
  11. be516b5 HEAD@{3}: commit: v2
  12. 04b38f8 HEAD@{4}: commit (initial): v1
  • log命令查看时,第4次提交已经不存在
  • reflog命令查看时,第4次提交仍然存在

    恢复到回退前版本

    git reset --hard 命令回退,做了3个操作

  • 移动HEAD执行,回退仓库中的记录

  • 暂存区内容的回退到HEAD指针指向的版本
  • 工作区内容,回退到HEAD指针指向的版本
    1. # 查看文件状态,工作目录是非常干净
    2. shuais-MacBook-Pro:reset shuai$ git status
    3. On branch master
    4. nothing to commit, working tree clean
    如果要恢复到回退之前的版本,也只能使用git reset —hard命令操作。 ```git

    1查看下可以回退的历史版本

    shuais-MacBook-Pro:reset shuai$ git reflog 229cc1f (HEAD -> master) HEAD@{0}: reset: moving to HEAD^ 6804892 HEAD@{1}: commit: 第4次提交,新增内容new line 229cc1f (HEAD -> master) HEAD@{2}: commit: v3 be516b5 HEAD@{3}: commit: v2 04b38f8 HEAD@{4}: commit (initial): v1

2,执行回退,回退到第4次提交,即6804892

shuais-MacBook-Pro:reset shuai$ git reset —hard 6804892 HEAD is now at 6804892 第4次提交,新增内容new line

3 查看文件状态

shuais-MacBook-Pro:reset shuai$ git status On branch master nothing to commit, working tree clean

4使用log命令查看版本历史

shuais-MacBook-Pro:reset shuai$ git log —oneline 6804892 (HEAD -> master) 第4次提交,新增内容new line 229cc1f v3 be516b5 v2 04b38f8 v1

5使用reflog命令查看可回退的版本历史

shuais-MacBook-Pro:reset shuai$ git reflog 6804892 (HEAD -> master) HEAD@{0}: reset: moving to 6804892 229cc1f HEAD@{1}: reset: moving to HEAD^ 6804892 (HEAD -> master) HEAD@{2}: commit: 第4次提交,新增内容new line 229cc1f HEAD@{3}: commit: v3 be516b5 HEAD@{4}: commit: v2 04b38f8 HEAD@{5}: commit (initial): v1 ``` 可以看到,readme.md文件内容完全恢复。

reset使用总结

关于reset版本回退。当在v4版本中,回退到v3版本
git后悔药-reset - 图4
在工作区、暂存区,仓库中文件的状态
git后悔药-reset - 图5
分别执行3中回退方式

  • git reset —soft HEAD^ :软性回退
  • git reset —mixed HEAD^ :混合回退
  • git reset —hard HEAD^ :硬性回退

    soft回退示意图

    从v4版本回退到v3
    git后悔药-reset - 图6
    目录中对应的文件状态
    git后悔药-reset - 图7
    只有HEAD指针指向了v3。工作区和暂存区都还是v4版本
    修改后再次提交:
    修改readme文件再次提交,会在v3版本之上创建一个新的commit记录。并移动HEAD指针指向新commit提交
    git后悔药-reset - 图8
    如果使用git log命令查看提交历史,就看不到v4的提交信息,只有v1、v2、v3、v5。

    但是v4并不会在git记录中删除,会存储在git 的本地仓库中,可以使用git reflog查看到v4提交信息。

mixed回退示意图

使用git reset —mixed HEAD^命令回退到v3版本
git后悔药-reset - 图9
文件状态的变化
git后悔药-reset - 图10
mixed命令,完成了2步操作:

  • 把HEAD指针指向了v3版本
  • 把暂存区中的文件回退到HEAD的指针,即v3版本

    —mixed参数是git reset默认选项,不写任何参数时也是该参数。

hard回退示意图

执行命令git reset --hard HEAD^命令回退到v3版本
git后悔药-reset - 图11
git后悔药-reset - 图12
hard命令,完成了3步操作:

  • 把HEAD指针指向v3版本
  • 把暂存区的指针指向HEAD,也退回的到了v3版本
  • 把工作区的指针指向HEAD,也退回的到了v3版本

注意⚠️:—hard参数是reset命令唯一危险的用法,是能够是git真正的销毁数据的操作。 其他任何形式的reset操作都可以轻松撤销,但是—hard选项不能,因为它强制覆盖了工作区的文件 在这种情况下,只能从仓库中找回该文件的v4提交版本,可以通过reflog来找回它。但是如果该文件还未提交,那么reflog并不会有记录,git仍会覆盖它而导致无法恢复数据。