Git 重置

本贴最后更新于 2300 天前,其中的信息可能已经东海扬尘

Git 重置


1 版本库回退

[root@izwz9f5nsv33jsmjcx0qw1z demo]# git status -s
 M demo.txt
[root@izwz9f5nsv33jsmjcx0qw1z demo]# cat .git/refs/heads/master 
ee76d8a1bd776d7042c7054e5abf180b371550cf
[root@izwz9f5nsv33jsmjcx0qw1z demo]# git commit -am "new commit"
[master adf3af7] new commit
 1 file changed, 1 insertion(+)
[root@izwz9f5nsv33jsmjcx0qw1z demo]# cat .git/refs/heads/master 
adf3af7077fe75b98167fbbc26bb8cfcd05eaf5d
[root@izwz9f5nsv33jsmjcx0qw1z demo]# 

  由上面的命令和结果可以看出,当有新的提交发生时,master 分支对应的引用文件中的内容就会变成最新的提交 ID。且我们可以通过 git reset 命令,人为的改变引用的提交 ID。
  引用 refs/heads/master 就好像是一个游标,一般这个游标都指向最新提交。
git reset --hard HEAD^ 注意 --hard 参数会破坏工作区未提交的改动,慎用! HEAD^ 也可以是某次提交 ID

reset 命令很危险,因为执行后会彻底丢弃历史,且无法恢复。这里的无法恢复是指通过 git log 查看不到之前的提交记录,其实通过下面的 reflog 还是可以恢复的。

2 reflog 挽救错误的重置

.git/log/regs/heads/ 目录下的日志记录文件记录了分支的变更,例如:

[root@izwz9f5nsv33jsmjcx0qw1z demo]# tail .git/logs/refs/heads/master 为了排版就不贴结果了
使用 Git 为我们提供的命令更直观更方便。例如:

[root@izwz9f5nsv33jsmjcx0qw1z demo]# git reflog show master
adf3af7 master@{0}: commit: new commit
ee76d8a master@{1}: reset: moving to HEAD^
69d6d43 master@{2}: commit: 提交中文测试
ee76d8a master@{3}: reset: moving to HEAD^^
d748b3a master@{4}: commit: 提交中文测试
9938bca master@{5}: commit: <D0><C2><C4><DA><C8><DD><C8><DD><CC>ύ
ee76d8a master@{6}: commit: inset qwe
f325ad0 master@{7}: commit (amend): Initial demo666
9bd630b master@{8}: commit (initial): Initial demo

上面两条命令的结果都可以看到 master 分支指向的变迁。通过这些日志我们就可以恢复我们的误操作。

[root@izwz9f5nsv33jsmjcx0qw1z demo]# cat demo.txt 
123
qwe
123
[root@izwz9f5nsv33jsmjcx0qw1z demo]# git reset --hard master@{2}
HEAD is now at 69d6d43 提交中文测试
[root@izwz9f5nsv33jsmjcx0qw1z demo]# cat demo.txt 
123
qwe
中文测试
[root@izwz9f5nsv33jsmjcx0qw1z demo]# git log --oneline -10
69d6d43 提交中文测试
ee76d8a inset qwe
f325ad0 Initial demo666
[root@izwz9f5nsv33jsmjcx0qw1z demo]# 

上面 git reset --hard master@{2} 让我们恢复了重置操作。而且看到提交内容和日志也恢复了。这个时候再来看日志记录

[root@izwz9f5nsv33jsmjcx0qw1z demo]# git reflog show master
69d6d43 master@{0}: reset: moving to master@{2}
adf3af7 master@{1}: commit: new commit
ee76d8a master@{2}: reset: moving to HEAD^
69d6d43 master@{3}: commit: 提交中文测试
ee76d8a master@{4}: reset: moving to HEAD^^
d748b3a master@{5}: commit: 提交中文测试
9938bca master@{6}: commit: <D0><C2><C4><DA><C8><DD><C8><DD><CC>ύ
ee76d8a master@{7}: commit: inset qwe
f325ad0 master@{8}: commit (amend): Initial demo666
9bd630b master@{9}: commit (initial): Initial demo

可以发现,我们恢复操作也被记录在日志文件中。


3 Git reset 操作

git reset [-q] [<tree-ish>] [--] <paths>...
git reset (--patch | -p) [<tree-sh>] [--] [<paths>...]
git reset [--soft | --mixed | --hard | --merge | --keep] [-q] [<commit>]

reset 命令常用的三种操作方式,但是《Git 权威指南》书上只解释了第一种和第三种。
  第一种用法(包含了路径 <paths> 的用法)不会重置引用,不会改变工作区,而是用指定提交 ID 下的文件替换掉暂存区中的文件,例如 git reset HEAD <paths> 相当于取消之前执行 git add <paths> 命令时改变的暂存区。
  第三种用法(不使用路径 <paths> 的用法)则会重置引用,根据参数可以对暂存区或工作区进行重置。例如:
  QQ 图片 20170911195256.png-329.1kB

  • 使用参数 --hard ,如:git reset --hard <commit>。会执行图上所有动作。

    • 1.替换引用指向,引用指向新的提交 ID。
    • 2.替换暂存区,使用上一步所指向的提交 ID 对应的目录树替换暂存区的目录树
    • 3.替换工作区,使用刚被替换的暂存区目录树替换工作区
  • 使用参数 --soft ,如:git reset --soft <commit>。只会指向 1 操作,即只更改引用指向。不替换目录树。

  • 使用参数 --mixed 或者不加参数(此参数为默认参数) ,如:git reset <commit>。会执行 1、2 动作。即更改引用的指向及重置暂存区,但不改变工作区。

  • 命令 git resetgit reset HEAD 效果一样,因为不指定 commitID,默认的就是 HEAD 对应的提交 ID。此命令将 HEAD 对应的目录树替换暂存区的目录树。

  • 命令 git reset --soft HEAD^ 俗称软回滚 ,工作区和暂存区都不改变,但是引用向前回退一次。当对提交的说明或者提交的更改不满意时,用此撤销提交,以便重新提交。

    • 前面有个修补提交的命令 git commit --amend 此修补命令相当于执行了下面两条命令:
    • git reset --soft HEAD^
    • git commit -e -F .git/COMMIT_EDITMSG.git/COMMIT_EDITMSG 保存了上次的提交日志。
  • Simon
    20 引用 • 10 回帖
  • Git

    Git 是 Linux Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。

    209 引用 • 358 回帖

相关帖子

回帖

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...