Git重置
git reset
命令改变分支引用文件的内容,即实现分支的重置
git reset --hard HEAD^
将master重置到上一个老的提交上。使用了hard参数,会破坏工作区未提交的改动
使用重置命令很危险,会彻底的丢弃历史。那么,能不能通过浏览提交历史的方法找到丢弃的提交ID,就不能通过重置命令恢复历史。
使用git reflog
挽救错误的重置
如果没有记下重置之前master分支指向的提交ID,想要重置会原来的提交似乎是件麻烦的事情(去对象库中一个一个找)。幸好Git提供了一种挽救的机制,通过.git/logs目录下的日志文件记录了分支的变更。默认的非裸版本库(带有工作区)都提供分支日志功能,这是因为带有工作区的版本库都有如下设置:
git config core.logAllRefUpdates
------------------------------>true
tail -5 .git/logs/refs/heads/master
查看日志文件的前5行
9d9e84cbe161a004045c23848b12753823053717 eef995dc79c6e5717942a19494a2ad3e02fbf8a5 17305 <guoxi.zhang@hand-china.com> 1539960515 +0800 commit (amend): 配置文件中没有name的提交
eef995dc79c6e5717942a19494a2ad3e02fbf8a5 2443ae29d86ec5edd6fbb70e2a01f1902dd02a37 17305 <guoxi.zhang@hand-china.com> 1540029427 +0800 commit: which version checked in
2443ae29d86ec5edd6fbb70e2a01f1902dd02a37 b2a5a950d4633923b63d5873154e3215157bc26b 17305 <guoxi.zhang@hand-china.com> 1540094230 +0800 commit: does master follow this new commit
b2a5a950d4633923b63d5873154e3215157bc26b 2443ae29d86ec5edd6fbb70e2a01f1902dd02a37 17305 <guoxi.zhang@hand-china.com> 1540108418 +0800 reset: moving to HEAD^
2443ae29d86ec5edd6fbb70e2a01f1902dd02a37 7a368a3ab1cbe2aec6fec3c5e15e7c6283ddeb18 17305 <guoxi.zhang@hand-china.com> 1540108475 +0800 reset: moving to 7a368a3
Git提供了一个git reflog命令对这个文件进行操作。使用show子命令可以显示此文件内容
git reflog show master|head -5
结果
7a368a3 master@{0}: reset: moving to 7a368a3
2443ae2 master@{1}: reset: moving to HEAD^
b2a5a95 master@{2}: commit: does master follow this new commit
2443ae2 master@{3}: commit: which version checked in
eef995d master@{4}: commit (amend): 配置文件中没有name的提交
重置master为两次变更之前的值
git reset --hard master@{2}
重置之后,工作区的文件以及提交历史都回来了,而且恢复master的操作也记录在日志中了
深入理解git reset命令
git reset命令的用法
用法一:git reset [-q] [<commit>] [--] <paths>
用法二:git reset [--soft | --mixed | --hard | --merge | --keep] [-q] [<commit>]
上面列出两个用法,其中[<commit>]都是可选项,可以使用引用或者提交ID,如果省略了<commit>,则相当于使用了HEAD的指向作为提交ID。两种方法的区别在于第一种用法在命令中包含路径<paths>。为了避免路径和引用(或者提交ID)同名而发生冲突,可以在<paths>前用两个连续的短线作为分隔。
第一种用法(包含了路径<paths>的用法)不会重置引用,更不会改变工作区,而是用指定提交状态(<commit>)下的文件(<paths>)替换掉暂存区的文件。例如命令git reset HEAD <paths>
相当于取消之前执行git add <paths>命令时改变的暂存区
第二种用法(不使用路径<paths>的用法)则会重置引用。根据不同的选项,可以对暂存区或者工作区进行重置。参照下面的版本库模型来看下参数对第二种用法的影响
- 使用参数--hard,如
git reset --hard <commit>
会执行上图的1,2,3,即
1:替换引用的指向。引用指向新的提交ID
2:替换暂存区,替换后,暂存区的内容和引用指向的目录树一样。
3:替换工作区。替换后,工作区的内容和暂存区的一样,也和HEAD指向的目录树的内容一样。 - 使用参数--soft,如
git reset --soft <commit>
,会执行上图的1,即只更改引用的指向,不会更改暂存区和工作区 - 使用参数--mixed或者不使用参数(默认就是--mixed),如
git reset <commit>
,会执行上图的1,2,即更改引用的指向和暂存区,但不改变工作区
下面通过一些示例,看一下重置命令的不同用法。
- 命令:
git reset
仅用HEAD指向的目录树重置暂存区,工作区不会受到影响。相当于将之前用git add
命令更新到暂存区的内容撤出暂存区。引用也为改变,因为引用重置到HEAD相当于没有重置 - 命令
git reset HEAD
效果同上 - 命令
git reset filename
仅将文件filename的改动撤出暂存区,暂存区的其他文件不变,相当于命令git add filename
的反向操作 - 命令
git reset HEAD filename
效果同上 - 命令
git reset --soft HEAD^
工作区和暂存区的内容不变,但是引用向前回退一次,当对最新提交的提交说明或者提交更改不满意时,撤销最新的提交以便重新提交。之前提到过一个git commit --amend
命令用于对最新的提交进行重新提交以修补错误的提交说明或者错误的提交文件。提交修补命令实际上相当于执行下面两条命令
git reset --soft HEAD^
git commit -e -F .git/COMMIT_EDITMSG
注:文件.git/COMMIT_EDITMSG保存了上次的提交日志 - 命令
git reset HEAD^
工作区不变,暂存区回退到上一次提交,引用也会回退一次 - 命令
git reset --mixed HEAD^
效果同上
命令git reset --hard HEAD^
彻底撤销最近的提交。引用回退到前一次,而且工作区和暂存区都会回退到上一次的提交状态。自上一次以来的提交全部丢失