转自大神周沫凡:https://mofanpy.com/tutorials/others/git/
Git book:https://git-scm.com/book/zh/v2(p40)
目录
1.回到从前(reset)
1.1 修改已commit的版本(--amend)
1.2 取消暂存的文件(reset回到add之前:git reset HEAD )
1.3 取消已经commit的文件(reset回到commit之前:git reset --hard )
2.回到从前(撤销对文件的修改:checkout针对单个文件)
有时候我们总会忘了什么, 比如已经提交了 commit 却发现在这个 commit 中忘了附上另一个文件. 接下来我们模拟这种情况. 上节内容中, 我们最后一个 commit 是 change 2, 我们将要添加另外一个文件, 将这个修改也 commit 进 change 2. 所以我们复制 1.py 这个文件, 改名为 2.py. 并把 2.py 变成 staged, 然后使用 --amend 将这次改变合并到之前的 change 2 中.
(git log --oneline 显示所有已经提交的版本,且每个 commit 内容显示在单独一行)接上节内容,首先查看一下所有log
Administrator@PC201710130840 MINGW64 ~/Desktop/daily-learning/gitTUT (master) $ git log --oneline # 看看所有的log 显示已经提交的版本 2f7e561 (HEAD -> master) change 2 2d350cb change 1 d19b6a5 1.txt我们有时候提交完才发现漏掉了几个文件没有添加,或者提交信息写错了。此时,可以运行带有--amend选项的提交命令尝试重新提交,最终第二次提交覆盖第一次提交,且在git log --oneline命令下只显示第二次提交。
在1.txt同级文件目录下复制1.txt为2.txt【2.txt为上次提交忘记的文件】
(git commit --amend 会将缓存区(已经add未commit的)的文件提交)
(git commit --amend --no-edit # "--no-edit": 不编辑, 直接合并到上一个 commit)
Administrator@PC201710130840 MINGW64 ~/Desktop/daily-learning/gitTUT (master) $ git add 2.txt Administrator@PC201710130840 MINGW64 ~/Desktop/daily-learning/gitTUT (master) $ git commit --amend --no-edit # "--no-edit": 不编辑, 直接合并到上一个 commit [master a5df2d0] change 2 Date: Thu May 23 14:43:34 2019 +0800 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 2.txt Administrator@PC201710130840 MINGW64 ~/Desktop/daily-learning/gitTUT (master) $ git log --oneline # "--oneline": 每个 commit 内容显示在一行 a5df2d0 (HEAD -> master) change 2 # 合并过的 change 2,此时ID改变 2d350cb change 1 d19b6a5 1.txt由上面git log --oneline结果可知,最终第二次提交代替(覆盖)第一次提交的结果,即第二次的change 2代替第一次的change 2,且change 2 的 id改变
新实验:在1.py中添加b的信息,提交时错误的将提交信息写错了(写成了add a)。此时就可以应用amend重新进行编辑。
Administrator@PC201710130840 MINGW64 /f/Git (master) $ git add 1.py #add 1.py Administrator@PC201710130840 MINGW64 /f/Git (master) $ git commit -m "add a" #commit 1.py,提交信息写错 [master d065dbf] add a 1 file changed, 3 insertions(+), 1 deletion(-) Administrator@PC201710130840 MINGW64 /f/Git (master) #查看当下log $ git log --oneline d065dbf (HEAD -> master) add a 7cbef99 do a little change 434a45c create 1.py Administrator@PC201710130840 MINGW64 /f/Git (master) $ git commit --amend #使用amend进行修正,此时会跳入编辑框 [master 0b05ad5] add b Date: Wed Aug 26 14:36:08 2020 +0800 1 file changed, 3 insertions(+), 1 deletion(-) Administrator@PC201710130840 MINGW64 /f/Git (master) $ git log --oneline #查看当下log:最新提交信息已纠正,且覆盖,id改变 0b05ad5 (HEAD -> master) add b 7cbef99 do a little change 434a45c create 1.py(git reset HEAD <file>将已经add过(staged状态)的文件,退回到modified状态)
【多用于已经修改了两个文件,且想它们作为两次独立的修改提交,但却输入git add *暂存了两个,如何取消暂存其中的一个呢?】
例:对1.txt修改后add,后又想撤回到modified状态(add 1.txt后想取消add)
$ git add 1.txt Administrator@PC201710130840 MINGW64 ~/Desktop/daily-learning/gitTUT (master) $ git status -s # "-s": status 的缩写模式 M 1.txt # staged Administrator@PC201710130840 MINGW64 ~/Desktop/daily-learning/gitTUT (master) $ git reset 1.txt #调用reset Unstaged changes after reset: M 1.txt Administrator@PC201710130840 MINGW64 ~/Desktop/daily-learning/gitTUT (master) $ git status -s M 1.txt # unstaged(git reset --hard HEAD #方式一:回到HEAD处,即最新版本)
(git reset --hard id #方式二:回到指针为2d350cb所指的版本)
(git reset --hard HEAD@{4} #方式三:选择想要挽救的版本(或者使用这种指定HEAD名称的方式))
在穿梭到过去的 commit 之前, 我们必须了解 git 是如何一步一步累加更改的. 我们截取网上的一些图片
http://bramus.github.io/ws2-sws-course-materials/xx.git.html
(即不同的版本号是HEAD指针所在位置)
每个 commit 都有自己的 id 数字号, HEAD 是一个指针, 指引当前的状态是在哪个 commit. 最近的一次 commit 在最右边, 我们如果要回到过去, 就是让 HEAD 回到过去并 reset 此时的 HEAD 到过去的位置.
Administrator@PC201710130840 MINGW64 ~/Desktop/daily-learning/gitTUT (master) $ git log --oneline #查看所有已经提交的版本 a5df2d0 (HEAD -> master) change 2 #最新版 2d350cb change 1 d19b6a5 1.txt Administrator@PC201710130840 MINGW64 ~/Desktop/daily-learning/gitTUT (master) $ git add 1.txt Administrator@PC201710130840 MINGW64 ~/Desktop/daily-learning/gitTUT (master) $ git reset --hard HEAD #方式一:回到HEAD处,即最新版本 HEAD is now at a5df2d0 change 2 Administrator@PC201710130840 MINGW64 ~/Desktop/daily-learning/gitTUT (master) $ git status -s #没有输出,表示没有更新 Administrator@PC201710130840 MINGW64 ~/Desktop/daily-learning/gitTUT (master) $ git reset --hard 2d350cb #方式二:回到指针为2d350cb所指的版本 HEAD is now at 2d350cb change 1 #回到某个版本的方式有3种,1.指定版本ID, 2.git reset --hard HEAD^^^(回到前3个版本) 3.git reset --hard HEAD~3(回到前3个版本) Administrator@PC201710130840 MINGW64 ~/Desktop/daily-learning/gitTUT (master) $ git log --oneline #此时没有change 2对应的版本了 2d350cb (HEAD -> master) change 1 d19b6a5 1.txt(git reflog查看最近做的所有HEAD的改动)怎么 change 2 消失了!!! 还有办法挽救消失的 change 2 吗? 我们可以查看 $ git reflog 里面最近做的所有 HEAD 的改动, 并选择想要挽救的 commit id 或指针名称:
Administrator@PC201710130840 MINGW64 ~/Desktop/daily-learning/gitTUT (master) $ git log --oneline #当前保留的版本号 2d350cb (HEAD -> master) change 1 d19b6a5 1.txt Administrator@PC201710130840 MINGW64 ~/Desktop/daily-learning/gitTUT (master) $ git reflog #查看最近做的所有 HEAD 的改动 2d350cb (HEAD -> master) HEAD@{0}: reset: moving to 2d350cb a5df2d0 HEAD@{1}: reset: moving to HEAD a5df2d0 HEAD@{2}: commit (amend): change 2 2f7e561 HEAD@{3}: commit: change 2 2d350cb (HEAD -> master) HEAD@{4}: commit: change 1 d19b6a5 HEAD@{5}: commit (initial): 1.txt Administrator@PC201710130840 MINGW64 ~/Desktop/daily-learning/gitTUT (master) $ git reset --hard 2f7e561 #选择想要挽救的版本:通过id HEAD is now at 2f7e561 change 2 Administrator@PC201710130840 MINGW64 ~/Desktop/daily-learning/gitTUT (master) $ git log --oneline 2f7e561 (HEAD -> master) change 2 2d350cb change 1 d19b6a5 1.txt Administrator@PC201710130840 MINGW64 ~/Desktop/daily-learning/gitTUT (master) $ git reflog 2f7e561 (HEAD -> master) HEAD@{0}: reset: moving to 2f7e561 2d350cb HEAD@{1}: reset: moving to 2d350cb a5df2d0 HEAD@{2}: reset: moving to HEAD a5df2d0 HEAD@{3}: commit (amend): change 2 2f7e561 (HEAD -> master) HEAD@{4}: commit: change 2 2d350cb HEAD@{5}: commit: change 1 d19b6a5 HEAD@{6}: commit (initial): 1.txt Administrator@PC201710130840 MINGW64 ~/Desktop/daily-learning/gitTUT (master) $ git reset --hard HEAD@{4} #选择想要挽救的版本:或者使用这种指定HEAD名称的方式 HEAD is now at 2f7e561 change 2 Administrator@PC201710130840 MINGW64 ~/Desktop/daily-learning/gitTUT (master) $ git log --oneline 2f7e561 (HEAD -> master) change 2 2d350cb change 1 d19b6a5 1.txt这样我们就再次奇迹般的回到了 change 2.
NOTE:调用时加上--hard选项会使git reset成为一个危险的命令(可能导致工作目录中所有当前进度丢失)。不加选项的的git reset并不危险,它只会修改暂存区域。
新实验:
Administrator@PC201710130840 MINGW64 /f/Git (master) $ git reflog #查看最近HEAD的改动 0b05ad5 (HEAD -> master) HEAD@{0}: commit (amend): add b d065dbf HEAD@{1}: commit: add a 7cbef99 HEAD@{2}: commit: do a little change 434a45c HEAD@{3}: commit (initial): create 1.py Administrator@PC201710130840 MINGW64 /f/Git (master) $ git reset --hard d065dbf #如果想退回add a这个信息的版本 HEAD is now at d065dbf add a Administrator@PC201710130840 MINGW64 /f/Git (master) $ git reflog d065dbf (HEAD -> master) HEAD@{0}: reset: moving to d065dbf 0b05ad5 HEAD@{1}: commit (amend): add b d065dbf (HEAD -> master) HEAD@{2}: commit: add a 7cbef99 HEAD@{3}: commit: do a little change 434a45c HEAD@{4}: commit (initial): create 1.py(其作用相当于add+commit的逆操作,使得文件从本地仓库回退到本地)
( git checkout 2d350cb -- 1.txt #表示1.txt文件回到id号为2d350cb的版本)
之前我们使用 reset 的时候是针对整个版本库(commit与commit之间来回穿梭), 回到版本库的某个过去. 不过如果我们只想回到某个文件的过去, 又该怎么办呢?
改写文件checkout其实 checkout 最主要的用途并不是让单个文件回到过去, 我们之后会继续讲 checkout 在分支 branch 中的应用, 这一节主要讲 checkout 让文件回到过去
同前面2.txt一样,创建3.txt文件
Administrator@PC201710130840 MINGW64 ~/Desktop/daily-learning/gitTUT (master) $ git add 3.txt Administrator@PC201710130840 MINGW64 ~/Desktop/daily-learning/gitTUT (master) $ git commit --amend --no-edit [master 11f06ae] change 2 Date: Thu May 23 14:43:34 2019 +0800 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 3.txt Administrator@PC201710130840 MINGW64 ~/Desktop/daily-learning/gitTUT (master) $ git log --oneline 11f06ae (HEAD -> master) change 2 2d350cb change 1 d19b6a5 1.txt我们现在的版本库有两个文件:
1.txt 和 3.txt
其内容均为
查看当前版本
Administrator@PC201710130840 MINGW64 ~/Desktop/daily-learning/gitTUT (master) $ git log --oneline 11f06ae (HEAD -> master) change 2 2d350cb change 1 d19b6a5 1.txt调用checkout回到过去版本
Administrator@PC201710130840 MINGW64 ~/Desktop/daily-learning/gitTUT (master) $ git checkout 2d350cb -- 1.txt #表示1.txt文件回到id号为2d350cb的版本此时,1.txt文件内容变为:
而3.txt内容没有发生改变。
对1.txt重新进行编辑为如下:
然后add并commit 1.txt:
Administrator@PC201710130840 MINGW64 ~/Desktop/daily-learning/gitTUT (master) $ git add 1.txt Administrator@PC201710130840 MINGW64 ~/Desktop/daily-learning/gitTUT (master) $ git commit -m "back to change 1 and add commit for 1.txt" [master edb3ff8] back to change 1 and add commit for 1.txt 1 file changed, 2 insertions(+), 3 deletions(-) Administrator@PC201710130840 MINGW64 ~/Desktop/daily-learning/gitTUT (master) $ git log --oneline edb3ff8 (HEAD -> master) back to change 1 and add commit for 1.txt 11f06ae change 2 2d350cb change 1 d19b6a5 1.txt可以看出,不像reset时那样,此时change 2版本仍然保留,且不影响新添加的 back to change 1 and add commit for 1.txt 版本。即1.txt既回到了过去,又改写了未来。
NOTE:git checkout --[file] 是一个危险的命令。你对那个文件做的所有修改都会消失——你只是拷贝了另一个文件来覆盖它。除非你确实不想要那个文件了,否则不要使用这个命令。
如果你仍然想保留对那个文件作出的修改,但是现在仍然需要撤销,这时使用git 分支中的保存进度与分支(通常是更好的办法)。
git 中任何已提交的东西几乎总是可以恢复的,甚至那些被删除的分支中的提交或使用 -- amend选项覆盖的提交也可以恢复。然而,任何你未提交的东西丢失后很可能再也找不到了。
总结:
--amend 修改已commit的版本git reset HEAD <file> 取消暂存的文件git reset --hard 取消已经commit的文件git checkout 2d350cb -- 1.txt 撤销对文件的修改