master分支在版本库的引用目录(.git/refs)中体现为一个引用文件.git/refs/heads/master,其内容就是分支中最新提交的提交ID。

$ cat .git/refs/heads/master
e695606fc5e31b2ff9038a48a3d363f4c21a3d86

通过对提交本身数据结构的分析,看到提交可以通过到父提交的关联实现对提交历史的追溯。注意:下面的git log命令中使用了--oneline参数,类似于--pretty=oneline,但是可以显示更短小的提交ID。

$ git log --graph --oneline
* e695606 which version checked in?
* a0c641e who does commit?
* 9e8a761 initialized.

分支游标master探秘

先来看看当有新的提交发生的时候,文件.git/refs/heads/master的内容如何改变。首先在工作区创建一个新文件,姑且叫做new-commit.txt,然后提交到版本库中。

$ touch new-commit.txt
$ git add new-commit.txt
$ git commit -m "does master follow this new commit?"
[master 4902dc3] does master follow this new commit?
0 files changed, 0 insertions(+), 0 deletions(-)
create mode 100644 new-commit.txt

此时工作目录下会有两个文件,其中文件new-commit.txt是新增的。

$ ls
new-commit.txt welcome.txt

来看看master分支指向的提交ID是否改变了。

  • 先看看在版本库引用空间(.git/refs/目录)下的master文件内容的确更改了,指向了新的提交。
$ cat .git/refs/heads/master
4902dc375672fbf52a226e0354100b75d4fe31e3
  • 再用git log查看一下提交日志,可以看到刚刚完成的提交。
$ git log --graph --oneline
* 4902dc3 does master follow this new commit?
* e695606 which version checked in?
* a0c641e who does commit?
* 9e8a761 initialized.

引用refs/heads/master就好像是一个游标,在有新的提交发生的时候指向了新的提交。可是如果只可上、不可下,就不能称为“游标”。Git提供了git reset命令,可以将“游标”指向任意一个存在的提交ID。下面的示例就尝试人为的更改游标。(注意下面的命令中使用了--hard参数,会破坏工作区未提交的改动,慎用。)

$ git reset --hard HEAD^
HEAD is now at e695606 which version checked in?

HEAD^代表了HEAD的父提交,所以这条命令就相当于将master重置到上一个老的提交上。来看一下master文件的内容是否更改了。

$ cat .git/refs/heads/master
e695606fc5e31b2ff9038a48a3d363f4c21a3d86

果然master分支的引用文件的指向更改为前一次提交的ID了。而且通过下面的命令可以看出新添加的文件new-commit.txt也丢失了。

$ ls
welcome.txt

重置命令不仅仅可以重置到前一次提交,重置命令可以直接使用提交ID重置到任何一次提交。

  • 通过git log查询到最早的提交ID。
$ git log --graph --oneline
* e695606 which version checked in?
* a0c641e who does commit?
* 9e8a761 initialized.
  • 然后重置到最早的一次提交。
$ git reset --hard 9e8a761
HEAD is now at 9e8a761 initialized.
  • 重置后会发现welcome.txt也回退到原始版本库,曾经的修改都丢失了。
$ cat welcome.txt
Hello.

使用重置命令很危险,会彻底的丢弃历史。那么还能够通过浏览提交历史的办法找到丢弃的提交ID,再使用重置命令恢复历史么?不可能!因为重置让提交历史也改变了。

$ git log
commit 9e8a761ff9dd343a1380032884f488a2422c495a
Author: Jiang Xin <jiangxin@ossxp.com>
Date: Sun Nov 28 12:48:26 2010 +0800 initialized.

用reflog挽救错误的重置

如果没有记下重置前master分支指向的提交ID,想要重置回原来的提交真的是一件麻烦的事情(去对象库中一个一个地找)。幸好Git提供了一个挽救机制,通过.git/logs目录下日志文件记录了分支的变更。默认非裸版本库(带有工作区)都提供分支日志功能,这是因为带有工作区的版本库都有如下设置:

$ git config core.logallrefupdates
true

查看一下master分支的日志文件.git/logs/refs/heads/master中的内容。下面命令显示了该文件的最后几行。(将输出中的40位的SHA1提交ID缩短。)

$ tail -5 .git/logs/refs/heads/master
dca47ab a0c641e Jiang Xin <jiangxin@ossxp.com> 1290999606 +0800 commit (amend): who does commit?
a0c641e e695606 Jiang Xin <jiangxin@ossxp.com> 1291022581 +0800 commit: which version checked in?
e695606 4902dc3 Jiang Xin <jiangxin@ossxp.com> 1291435985 +0800 commit: does master follow this new commit?
4902dc3 e695606 Jiang Xin <jiangxin@ossxp.com> 1291436302 +0800 HEAD^: updating HEAD
e695606 9e8a761 Jiang Xin <jiangxin@ossxp.com> 1291436382 +0800 9e8a761: updating HEAD

可以看出这个文件记录了master分支指向的变迁,最新的改变追加到文件的末尾因此最后出现。最后一行可以看出因为执行了git reset –hard命令,指向的提交ID由e695606改变为9e8a761。

Git提供了一个git reflog命令,对这个文件进行操作。使用show子命令可以显示此文件的内容。

$ git reflog show master | head -5
9e8a761 master@{0}: 9e8a761: updating HEAD
e695606 master@{1}: HEAD^: updating HEAD
4902dc3 master@{2}: commit: does master follow this new commit?
e695606 master@{3}: commit: which version checked in?
a0c641e master@{4}: commit (amend): who does commit?

使用git reflog的输出和直接查看日志文件最大的不同在于显示顺序的不同,即最新改变放在了最前面显示,而且只显示每次改变的最终的SHA1哈希值。还有个重要的区别在于使用git reflog的输出中还提供一个方便易记的表达式:<refname>@{<n>}。这个表达式的含义是引用<refname>之前第<n>次改变时的SHA1哈希值。

那么将引用master切换到两次变更之前的值,可以使用下面的命令。

  • 重置master为两次改变之前的值。
$ git reset --hard master@{2}
HEAD is now at 4902dc3 does master follow this new commit?
  • 重置后工作区中文件new-commit.txt又回来了。
$ ls
new-commit.txt welcome.txt
  • 提交历史也回来了。
$ git log --oneline
4902dc3 does master follow this new commit?
e695606 which version checked in?
a0c641e who does commit?
9e8a761 initialized.

此时如果再用git reflog查看,会看到恢复master的操作也记录在日志中了。

$ git reflog show master | head -5
4902dc3 master@{0}: master@{2}: updating HEAD
9e8a761 master@{1}: 9e8a761: updating HEAD
e695606 master@{2}: HEAD^: updating HEAD
4902dc3 master@{3}: commit: does master follow this new commit?
e695606 master@{4}: commit: which version checked in?

深入了解git reset命令

重置命令(git reset)是Git最常用的命令之一,也是最危险,最容易误用的命令。来看看git reset命令的用法。

用法一: git reset [-q] [<commit>] [--] <paths>...
用法二: git reset [--soft | --mixed | --hard | --merge | --keep] [-q] [<commit>]

上面列出了两个用法,其中 都是可选项,可以使用引用或者提交ID,如果省略 则相当于使用了HEAD的指向作为提交ID。

上面列出的两种用法的区别在于,第一种用法在命令中包含路径。为了避免路径和引用(或者提交ID)同名而冲突,可以在前用两个连续的短线(减号)作为分隔。

第一种用法(包含了路径的用法)不会重置引用,更不会改变工作区,而是用指定提交状态()下的文件()替换掉暂存区中的文件。例如命令git reset HEAD 相当于取消之前执行的git add 命令时改变的暂存区。

第二种用法(不使用路径的用法)则会重置引用。根据不同的选项,可以对暂存区或者工作区进行重置。参照下面的版本库模型图,来看一看不同的参数对第二种重置语法的影响。

命令格式: git reset [–soft | –mixed | –hard ] []

  • 使用参数--hard,如:git reset –hard 。

会执行上图中的1、2、3全部的三个动作。即:

  1. 替换引用的指向。引用指向新的提交ID。

  2. 替换暂存区。替换后,暂存区的内容和引用指向的目录树一致。

  3. 替换工作区。替换后,工作区的内容变得和暂存区一致,也和HEAD所指向的目录树内容相同。

  • 使用参数--soft,如:git reset –soft 。

会执行上图中的操作1。即只更改引用的指向,不改变暂存区和工作区。

  • 使用参数--mixed或者不使用参数(缺省即为--mixed),如:git reset 。

会执行上图中的操作1和操作2。即更改引用的指向以及重置暂存区,但是不改变工作区。

下面通过一些示例,看一下重置命令的不同用法。

  • 命令:git reset

仅用HEAD指向的目录树重置暂存区,工作区不会受到影响,相当于将之前用git add命令更新到暂存区的内容撤出暂存区。引用也未改变,因为引用重置到HEAD相当于没有重置。

  • 命令:git reset HEAD

命令:git reset HEAD

  • 命令:git reset – filename

仅将文件filename撤出暂存区,暂存区中其他文件不改变。相当于对命令git add filename的反向操作。

  • 命令:git reset HEAD filename

同上。

  • 命令:git reset –soft HEAD^

工作区和暂存区不改变,但是引用向前回退一次。当对最新提交的提交说明或者提交的更改不满意时,撤销最新的提交以便重新提交。

在之前曾经介绍过一个修补提交命令git commit –amend,用于对最新的提交进行重新提交以修补错误的提交说明或者错误的提交文件。修补提交命令实际上相当于执行了下面两条命令。(注:文件.git/COMMIT_EDITMSG保存了上次的提交日志)

$ git reset --soft HEAD^
$ git commit -e -F .git/COMMIT_EDITMSG
  • 命令:git reset HEAD^

工作区不改变,但是暂存区会回退到上一次提交之前,引用也会回退一次。

  • 命令:git reset –mixed HEAD^

同上。

  • 命令:git reset –hard HEAD^

彻底撤销最近的提交。引用回退到前一次,而且工作区和暂存区都会回退到上一次提交的状态。自上一次以来的提交全部丢失。

GIt-重置的更多相关文章

  1. 完整学习git五git重置

    git重置命令的一个用途就是修改引用的游标指向 1查看git操作日志 git reflog 2查看当前head游标的指向 cat .git/refs/heads/master 3重置和跳转 git r ...

  2. 第三节《Git重置》

    先来看看.git/refs/heads/master文件的内容 [root@git demo]# cat .git/refs/heads/master e97f443b2d1cee7eeca7dc2e ...

  3. git重置账号密码

    1.打开控制面板(快捷打开win+R,输入control) 2.点击打开用户账户 3.点击凭据管理器 4.点击windows凭据删除你的git凭据即可

  4. git 重置密码后,本地电脑需要修改git密码

    查看用户名git config user.name 查看密码git config user.password 查看邮箱git config user.email 修改密码git config --gl ...

  5. git reset soft,hard,mixed之区别深解

    GIT reset命令,似乎让人很迷惑,以至于误解,误用.但是事实上不应该如此难以理解,只要你理解到这个命令究竟在干什么. 首先我们来看几个术语 HEAD 这是当前分支版本顶端的别名,也就是在当前分支 ...

  6. Git权威指南 读笔(2)

    第七章 Git重置: Git提供了一个挽救机制,通过.git/logs目录下日志文件记录了分支的变更. master分支的日志文件.git/logs/refs/heads/master,显示最后5行: ...

  7. git 入门宝典

    本篇教程是按照我自己的组织方式,然后从多篇教程中拼凑出来的,嘎嘎~,真佩服自己的技术! 原本想叫 git 宝典的,结果一查git的命令大全,还有那么多的git命令与功能没有接触到,所以...还是谦虚一 ...

  8. Git让你从入门到精通,看这一篇就够了!

    简介 Git 是什么? Git 是一个开源的分布式版本控制系统. 什么是版本控制? 版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统. 什么是分布式版本控制系统? 介绍分布 ...

  9. git 本地代码冲突解决,强制更新

    git reset soft,hard,mixed之区别深解 git reset --hard  强制更新覆盖本地   GIT reset命令,似乎让人很迷惑,以至于误解,误用.但是事实上不应该如此难 ...

  10. Git让你从入门到精通,看这一篇就够了

    简介 Git 是什么? Git 是一个开源的分布式版本控制系统. 什么是版本控制? 版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统. 什么是分布式版本控制系统? 介绍分布 ...

随机推荐

  1. Java语言程序设计(第三版)第二章课后习题答案(仅供参考)

    2.1   注意不同类型转换 import java.util.Scanner; public class Ch02 { public static void main(String[] args) ...

  2. jquery jquery选择器总结 转自(永远的麦子)

    jQuery选择器总结 阅读目录 1, 基本选择器? 2, 层次选择器? 3, 过滤选择器? 4, 表单选择器? jQuery选择器共有四大类,分别为基本选择器,层次选择器,过滤选择器和表单选择器.下 ...

  3. java中方法体的作用

    java中抽象类中可以存在的抽象方法或接口中的方法不允许有方法体,但不属于方法体是空的.java.awt.event包中的适配器类中方法体是空的. 从语法中说,没有方法体必须是空的这一要求,只要是非抽 ...

  4. 用weex create 创建app项目 报 ERROR in index.web.js from UglifyJs 错误!

    用weex create创建一个APP项目,安装依赖后运行报 这个是package.json index.web.js 在dist目录下是build时生成的. 上面的答案没有给大家细节,不好意思致歉下 ...

  5. spring的struts简单介绍

    之前一段时间学习了springmvc+mybatis+spring框架,突然对之前的struts东西有点陌生, 所以这里简单记录下温故而知新的东西吧. 1.  首先建立一个Dynamic Web Pr ...

  6. Maven建立spring-web项目

    参考博客网址: https://blog.csdn.net/caoxuekun/article/details/77336444 1.eclipse集成maven 2.maven创建web项目 3.搭 ...

  7. 部署webservice到远程服务器

    在本地编写好webservice后并在本机验证正确后,在本地发布后,直接将发布时设置的文件夹复制到远程服务器上,在远程服务器的IIS上默认网站->新建虚拟目录->设置别名->物理路径 ...

  8. pta 编程题15 列出连通集

    其它pta数据结构编程题请参见:pta 题目 题目要求分别以深度优先搜索和广度优先搜索输出图的连通集. 广度优先搜索要用到队列,先回顾一下循环队列: struct QNode { int* Data; ...

  9. WCF使用地址去调用服务端的方法

    前面的章节已经讲过了WCF的代码和SVC页面的分离,这里是分离后,客户端调用代码如下: try { var myBinding = new BasicHttpBinding(); var myEndp ...

  10. lsscsi 与 cat /proc/scsi/scsi

    [root@localhost ~]# lsscsi[0:0:0:0]    disk    SEAGATE  ST300MM0048      N001  /dev/sda [0:0:2:0]    ...