Git 如何带你回到过去

本文详细介绍了如何使用Git恢复过去的代码。通过`git checkout`和`git reset`命令,你可以回到几分钟、几小时甚至几天前的状态。理解Git的三层结构(工作目录、暂存区、仓库)对于成功回退至关重要。`git reflog`可用于找回旧的提交,即使它们不再日志中。总结了使用这些命令的流程,并提供了相关资源链接。
摘要由CSDN通过智能技术生成

这篇日志是关于如何使用Git恢复过去的代码,可以猛击 这里 补充一点背景知识。

    在本文中出场的主角有:

 

    本文的内容包括:

  • 回到x分钟前
  • 回到x小时前
  • 回到x天前
  • 在过去和现在自由往返(总结)

 

    git 在管理版本时使用一个三层结构,如下图所示:

                                                

在工作目录里做出的更改可以分次保存到暂存区中,提交时再将暂存区的内容作为一个版本存入仓库中(下文中一个“提交”就代表版本仓库中的一个版本)。回到过去,实际上就是从暂存区或版本仓库中将文件取出,然后覆盖工作目录里的相同文件。

回到 x 分钟前

在 x 分钟这样比较短的时间里,通常只更改了工作目录里的内容,所以用暂存区中的文件就可以带我们回到 x 分钟以前。使用下面的命令

git checkout -- <file-name> 

例子:输入下面命令,建立一个仓库,其中仅包含一个文件 readme.txt,文件内容为"x minutes passed"。

$ mkdir TimeMachine							#新建目录 
$ cd TimeMachine 
[TimeMachine]$ git init							#初始化版本仓库 
Initialized empty Git repository in /home/cm/cmgit/TimeMachine/.git/
[TimeMachine]$ echo "x minutes passed" > readme.txt			#添加文件 
[TimeMachine]$ git add readme.txt					#开始追踪新文件 
[TimeMachine]$ git status						#查看状态 
# On branch master
#
# Initial commit
#
# Changes to be committed:
#   (use "git rm --cached <file>..." to unstage)
#
#    new file:   readme.txt
#

从 git 给出的提示可以知道,readme.txt 已经被保存到暂存区中了。


接下来验证下 git checkout -- <filename> 的作用

[TimeMachine]$ echo "df;aei;akjfaiej" > readme.tx	#用一些乱码替换原来的内容 
[TimeMachine]$ cat readme.txt				#查看文件内容 
df;aei;akjfaiej
[TimeMachine]$ git checkout -- readme.txt		#从暂存区取出 readme.txt 覆盖工作目录中的文件 
[TimeMachine]$ cat readme.txt				#再次查看文件内容 
x minutes passed

注:如果是意外删除了 readme.txt 也可以用这种方式恢复


回到 x 小时前 

 经过了 x 小时,大家至少已经进行过一次提交(commit),x 小时前的版本已经被保存到版本仓库中了,这时候如何恢复到 x 小时前呢?分两种情况来看:

    1. 指定恢复某个文件:git checkout <commit-name> <file-name>

这条命令所做的工作其实就是从提交中取出文件,然后覆盖到工作目录和暂存区里,commit-name 可以是任意的你希望的提交名称,一个小技巧是,可以用HEAD表示当前分支中的最后一次提交,HEAD^表示倒数第二次提交,HEAD~n表示倒数第n次提交。

    2. 恢复提交中的所有文件:git reset --hard <commit-name>

想要让所有文件都回到过去?用上面这条命令就行了,它会将提交中的所有内容覆盖到暂存区和工作区。

例子:沿着之前的例子继续往下,首先要提交上次的修改

[TimeMachine]$ git commit -m "version:001"	#提交暂存区中的内容,标记为“version:001” 
[master 256c6f3] version:001
 1 files changed, 1 insertions(+), 0 deletions(-)
 create mode 100644 readme.txt

然后开始新的工作,向 readme.txt 中添加一行 “x hours passed”,然后提交

[TimeMachine]$ echo "x hours passed" >> readme.txt 	#向 readme.txt 中添加内容 
[TimeMachine]$ git commit -a -m "version:002"		#暂存并提交,相当于先执行 git add 再执行 git commit 
[master af78cb8] version:002
 1 files changed, 1 insertions(+), 0 deletions(-)
[TimeMachine]$ git log					#查看日志,日志中记录了提交的顺序和内容 
commit af78cb8154a6cdabdb3a67eb8b7f151219a13105
Author: cndmt <**@126.com>
Date:   Wed Jul 27 00:15:50 2011 +0800
    version:002						#这是本次的提交 
commit 256c6f3d183ba1dbe769d542999938a6dca752b6
Author: cndmt <**@126.com>
Date:   Mon Jul 25 23:24:30 2011 +0800
    version:001						#这是上次的提交


最后看看上面两条命令能做什么,这里或许有细心的童鞋会问,例子里只有一个文件,上面的两条命令不是一样了么?这个是对的,在恢复文件方面的确是相同的,但是~还有一点不同,马上就可以看到。

先看 git checkout

[TimeMachine]$ git checkout HEAD^ readme.txt 	# 回到过去~~ 
[TimeMachine]$ cat readme.txt			# x hours passed 木有了 
x minutes passed
[TimeMachine]$ git log				# 再看下日志,木有变化 
commit af78cb8154a6cdabdb3a67eb8b7f151219a13105
Author: cndmt <**@126.com>
Date:   Wed Jul 27 00:15:50 2011 +0800
    version:002
commit 256c6f3d183ba1dbe769d542999938a6dca752b6
Author: cndmt <**@126.com>
Date:   Mon Jul 25 23:24:30 2011 +0800
    version:001

再看 git reset --hard

[TimeMachine]$ git checkout HEAD readme.txt	# 先复原刚才的更改 
[TimeMachine]$ cat readme.txt			# 验证一下 
x minutes passed
x hours passed
[TimeMachine]$ git reset --hard HEAD^		# 又回到过去~ 为什么要说又?~~ 
\HEAD is now at 256c6f3 version:001		# 这里有提示,git checkout 可没有阿? 
[cm@cm TimeMachine]$ cat readme.txt		# 再看文件内容 
x minutes passed
[cm@cm TimeMachine]$ git log			# 差别就在这了,日志显示 少了一个提交 
commit 256c6f3d183ba1dbe769d542999938a6dca752b6
Author: cm <ender35@126.com>
Date:   Mon Jul 25 23:24:30 2011 +0800
    version:001


git reset 为什么会 改变日志的内容呢? 在最上面介绍主角的时候说 git reset 会移动 HEAD ,那为什么移动了HEAD 日志就变了?请看下图


                                                  HEAD (指向 version:002)
                                                           |
                                                           v
                         version:001 <-- version:002 <-- master (指向 version:002)


git 把每次的提交都保存成一个 commit 文件(每次查看日志的时候显示的 commit 256c6f... 神马的 就是commit 文件的名字)。文件中包含一个指针指向上一次的提交,比如 version:002的commit 文件中包含一个指向 version:001 的指针。可以看出来,git实际上是用链表的方法管理 commit 文件的。同时为了支持分支功能,git 为每一个分支设置一个分支头(保存在 .git/refs/heads/ 文件夹中),分支头都指向分支中的最后一个提交,上图中master分支的分支头指向的就是version:002。而 HEAD 默认指向的是当前分支的最后一次提交,与当前分支的分支头一样。

既然是链表,git log 的做了什么事就很好猜了,从 HEAD 开始 向前查找,直到遇到某个提交的指针为空,说明它是第一次的提交,然后把找到的内容显示出来就ok了。

啰嗦了这么多,终于讲到 git reset --hard 了,结合之前的那条提示(\HEAD is now at 256c6f3 version:001 ),可以知道这条命令实际上同时移动了 分支头 和 HEAD ,移动以后变成了这样


                    HEAD (指向 version:001)
                                 |
                                 v
                    version:001 <-- version:002 
                                 ^
                                  |
                    master (指向 version:001)


于是 git log 从 HEAD 一路向前,只找到了 version:001。

但是~ 按照链表理论,这时候 version:002 应该已经永远的离开了我们,还有可能找回来么? 答案是肯定滴~方法就是下面要讲的,回到 x 天前。


回到 x 天前

x 天过去了,执行了n次 git commit、git reset 后,会出现不少类似 version:002 的不在日志中列出的提交,git 仍然会将它们保存一段时间(默认为30天),使用 git reflog 可以具体查看之前使用过的所有提交。

继续上面的例子:使用 git reflog 看看会显示些什么

[TimeMachine]$ git reflog 
......                                                 	# 写日志的时候产生了一些多余内容,省略之
af78cb8 HEAD@{9}: commit: version:002                  	# 这样就找到了 version:002 
256c6f3 HEAD@{10}: commit (initial): version:001

再次使用 git reset --hard 就可以将分支头和HEAD重新指向 version:002

[TimeMachine]$ git reset --hard af78c                   # af78c 就是上面查出来的 commit 文件名 
HEAD is now at af78cb8 version:002
[TimeMachine]$ git log --pretty=format:"%h %s"       	# 再看日志文件 
af78cb8 version:002
256c6f3 version:001

补充的一点是 每次使用 git reset 时 git 会自动把移动前的 HEAD 复制到 ORIG_HEAD 里,可以简单的使用 git reset --hard ORIG_HEAD 回到上一次使用 git reset 之前(回到 x 分钟前 例子中的情况)。

在过去和现在自由往返(总结)

命令使用的流程:


                                       - working directory <-
                                    /                                  \                                                                                             
                              git add                          git checkout -- <filename>   
                                   \                                   /
                                       ----> staging area ----
                                   /                                   \        
git checkout <commit> <file> # 单个文件    git commit
       git reset <commit> # 全部文件                 |
                                   \                                   /
                                    ------- repository <-----


注:使用 git reset 时 若不加入 --hard 选项,则只覆盖暂存区的内容,不改变工作目录中的文件

git reset ORIG_HEAD 可以立即撤销上一次 git reset 所做出的更改

git reflog 显示近期使用过的 commit 文件,然后用 git reset <commit-name> 将提交重新加入到日志里

 

 一些有用的链接:

为什么git更加优秀呢:http://zh-tw.whygitisbetterthanx.com/

官方中文教程:http://gitbook.liuhui998.com/

git 魔法:http://www-cs-students.stanford.edu/~blynn/gitmagic/intl/zh_cn/book.html

 

日志到这就结束了,水平有限,不知道说清楚了没,有任何问题,欢迎来人来函以及来而无往非礼也之交谈~

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值