Git:分支&&HEAD的重置

分支的重置

 
question:有新提交时,master分支对应的引用文件的内容是否改变?master分支对应的引用文件中的内容可否人为改变?

 
touch new-commit.txt
git add new-commit.txt
git commit -m "judge"
cat .git/refs/heads/master  #发现内容确实改变了
git log --graph --oneline   #查看提交日志,看到了刚刚的提交
 
git reset可以将master引用指向任意一个存在的提交id.(--hard参数会破坏工作区未提交的改动):
git reset --hard HEAD^
cat .git/refs/heads/master   #查看master内容发现内容已改变,ls发现刚建的文件却仍在(1.9.4.msysgit.2)
note: 重置命令很危险,因为重置让提交历史也改变了,通过浏览提交历史不能恢复。But->

 
Git 提供了一个挽救机制:.git/logs目录下日志文件记录了分支的变更,more非裸版本库都提供分支日志功能:
因为git config core.logallrefupdates命令的输出为true.
tail -5 .git/logs/refs/heads/master #查看master分支的目录文件发现,它记录了master分支指向的变迁,最新的改变
                                    #添加到文件的末尾。

 
Git提供了git reflog命令对.git/logs/refs/heads/master进行操作
显示此文件的内容:
        git reflog show master | head -5   #将最新的改变放在最前面显示
        #输出中表达式<refname>@{<n>}的含义:引用<refname>之前第<n>次改变时的SHA1哈希值。eg:a0c641e master{3}:...
重置master为两次改变之前的值:
        git reset --hard master@{2}

 
深入git reset命令:
    git reset有三种用法:
        git reset [-q] [<tree-ish>] [--] <paths>…
        git reset (--patch | -p) [<tree-ish>] [--] [<paths>…]
        git reset [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>]
1)包含<paths>的用法不会重置引用,更不会更改工作区,它用指定提交状态<commit>下的文件<paths>替换掉暂存区中的文件。
2)不包含<paths>的用法则会重置引用,根据不同的选项可以对暂存区或工作区进行重置。
对于2):
    * 使用参数--hard: 引用指向新的提交ID; 替换暂存区、工作区,工作区和暂存区HEAD所指向的目录树内容相同。
    * 使用参数--soft: git reset --soft <commit> 只更改引用的指向(index),不改变暂存区和工作区
    * --mixed(默认)或不适用:git reset <commit> 会更改引用指向、重置暂存区,但不改变工作区

重置命令的一个用途就是修改引用(如master)的游标指向,实际上在执行重置命令时没有使用任何参数对所要重置的分支名(如master)
进行设置,因为重置命令针对的是头指针HEAD.之所以重置命令没有改变头指针HEAD的内容,因为HEAD指向了一个引用refs/heads/master,
所以重置命令体现为分支“游标的变更”,HEAD本身一直指向的是refs/heads/master分支,并没有在重置时改变。

################################################################################################################################


 
 
git checkout

HEAD的重置即检出:

git reset 使HEAD的内容不能改变而一直指向master分支。而git checkout的实质是修改HEAD本身的指向,该指针不会
影响分支"游标"(如master)

查看当前HEAD的指向:
        cat .git/HEAD   #输出ref: refs/heads/master
执行git branch -v会看到当前处于master分支624c083.
检出该ID的父提交:
        git checkout 624c083^
输出告诉我们:现在处于“分离头指针”状态,即HEAD头指针指向了一个具体的提交ID(可用cat .git/HEAD查看),而不是一个引用(分支).
也可查看最新提交的reflog,发现HEAD头指针被更改了,由指向master分支变成指向一个提交id:
        $ git reflog -1  #数字1
现在查看一下HEAD和master对应的提交id,发现指向不一样:
        $ git rev-parse HEAD master   #分支master的指向没有改变,还指向原有的提交id

现在版本库中的HEAD是指向e695606提交的,再做一次修改:
        touch detached-commit.txt
        git add detached-commit.txt
查看一下状态,输出说当前不处于任何分支,因为HEAD处于“分离头指针”模式:
        git status
提交:  #会出现警示
    git commit -m "commit in detached HEAD mode."
查看头指针,指向了新的提交:
        cat .git/HEAD
再查看日志会发现新的提交是建立在之前的提交基础上:
        git log --graph --pretty=oneline #记下新的提交id acc2f69
以master分支名作为参数切换到master分支上:
        $ git checkout master
HEAD头指针重新指向了分支,而不是分离头指针模式:
        $ cat .git/HEAD  #输出: ref: refs/heads/master
切换之后,之前本地建立的新文件detached-commit.txt不见了(ls),刚才提交的日志也不见了。
看看刚才记下的提交id,发现刚才的提交还存在于版本库的对象库中:
        git show acc2f69
#由于这个提交没有被任何分支跟踪到,当reflog中含有该提交的日志过期后,这个提交随时会从版本库中删除。

#######################################################################

①在"分离头指针"模式下进行的测试提交除了使用提交id访问外,不能通过master分支或其他引用访问到。
②如果这个提交是master分支所需要的,若用git reset,可以重置,但会丢掉master分支原先的提交。
★使用合并操作git merge可以实现两者兼顾:
将测试提交合并到master分支中:
1)、确认当前处于master分支: git branch -v
2)、执行合并操作,将测试提交合并到当前分支: git merge acc2f69
3)、ls 发现工作区中多了一个detached-commit.txt文件。
4)、查看日志,看到不一样的分支图,出现了开发分支,后又合并。
5)、查看最新提交,发现这个有两个parent: git cat-file -p HEAD

#######################################################################

深入了解git checkout命令:
git checkout会重写工作区。
checkout的用法,用法可划分为两种,有无<paths>:
  git checkout [-q] [-f] [-m] [<branch>]
  git checkout [-q] [-f] [-m] --detach [<branch>]
  git checkout [-q] [-f] [-m] [--detach] <commit>
  git checkout [-q] [-f] [-m] [[-b|-B|--orphan] <new_branch>] [<start_point>] ###
  git checkout [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>…
  git checkout [-p|--patch] [<tree-ish>] [--] [<paths>…]
省略<commit>,重置的默认值是HEAD,而检出的默认值是暂存区。因此:重置一般用于重置暂存区(除非使用--hard),
检出命令主要是覆盖工作区(若<若commit>不省略,也会替换缓存区响应文件)。
*包含路径<paths>的用法:不会改变HEAD头指针,主要是用于指定版本的文件覆盖工作区中对应的文件。如果省略了<commit>,
会用暂存区的文件覆盖工作区文件;否则用指定提交中的文件覆盖暂存区和工作区的文件。
*不使用路径<paths>的用法:会改变HEAD头指针,(后面加<branch>使HEAD切换到一个分支才可以对提交进行跟踪,否则仍然进入
分离头指针状态,在分离头指针状态下的提交不能被引用关联到,可能丢失)。最主要作用是切换到分支,若省略branch相当于对工作区
进行状态检查。
*###创建和切换到新的分支,新的分支从start_point指定的提交开始创建。新的分支也是在refs/heads命名空间下的引用。
eg:
git checkout branch  #检出branch分支,更新HEAD以指向branch分支,用branch指向的树更新暂存区和工作区。
git checkout    #汇总显示工作区、暂存区和HEAD的差异,或git checkout HEAD
git checkout -- filename    #用暂存区中的filename覆盖工作区中的filename文件
git checkout branch -- filename    #用branch所指向的提交中的filename覆盖暂存区和工作区中响应的文件。
git checkout -- .    git checkout .   #取消所有本地的修改(相对于暂存区),相当于用暂存区的所有文件直接覆盖本地。







 
 
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值