文章目录
Git-工具重置解密(git reset、git checkout)
本章总结对应Pro Git一书7.7 Git 工具 - 重置揭密章节。
主要是了解git reset和git checkout命令的内部运作原理以及使用方式。
三棵树
其实reset和checkout的本质就是操纵三棵树,这三棵树为:
树 | 用途 |
---|---|
HEAD | 当前分支(最新一次提交的快照,下一次提交快照的父节点(parent)) |
Index | 索引区(暂存区) |
Working Directory | 工作目录 |
HEAD:是指向当前分支引用的指针,它总是指向最后一次提交快照(分支指向最后一次提交),并且作为下一次提交的父节点信息。
# 查看HEAD指向分支引用指向的最后一次commit对象包含信息
$ git cat-file -p HEAD
tree 80adf9e9282dd6167e39f7aa89324ee785709d32
author zhangxy <928839929@qq.com> 1536044917 +0800
committer zhangxy <928839929@qq.com> 1536044917 +0800
新建 fileA.txt
Index:索引区,即暂存区。预期的下一次提交。
# 查看索引区内容
Administrator@USER-20171018VX MINGW64 /d/rc (master)
$ git ls-files -s
100644 78981922613b2afb6025042ff6bd878ac1994e85 0 fileA.txt
Working Directory:另外两棵树以一种高效但并不直观的方式,将它们的内容存储在 .git 文件夹中。 工作目录会将它们解包为实际的文件以便编辑。 你可以把工作目录当做沙盒。在你将修改提交到暂存区并记录到历史之前,可以随意更改。
工作流程
我们通过在项目中修改一个fileA.txt,并将这个文件提交到本地仓库流程来看三棵树的变化。我们在之前已经创建了fileA.txt文件并提交到了本地仓库。现在三个树的状态如下图。
此时执行git status命令工作目录是干净的。
Administrator@USER-20171018VX MINGW64 /d/rc (master)
$ git status
On branch master
nothing to commit, working tree clean
1,修改fileA.txt文件后三棵树状态
2,添加到索引区后三棵树状态
git add fileA.txt
3,提交到本地仓库后三棵树状态
git commit -m "新建 fileA.txt"
通过以上工作流程我们可以看出git add、git commit其实是操纵三棵树,其实git reset和git checkout也是操作这三棵树,只不过过程与add和commit相反。
git reset
我们先来看看git reset如果操作三棵树。在演示之前我们对fileA.txt文件进行修改并提交到了本地仓库,当前三棵树的状态如图:
执行git status工作目录是干净的,因为三棵树的内容一致。
移动HEAD(git reset --soft)
使用命令$ git reset --soft HEAD~
将当前HEAD指向上一次commit对象。三棵树状态如图。
执行git status提示我们fileA.txt待提交到仓库,这是因为我们移动了HEAD树,导致HEAD与工作目录和索引区内容不同导致。
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: fileA.txt
更新索引区(git reset --mixed)
使用命令git reset --mixed HEAD
将当前HEAD指向的最新一次提交内容更新到索引区。三棵树的状态如图。
执行git status提示我们file.txt文件待加入存储区,工作目录内容与索引区和HEAD不同导致。
$ git status
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
modified: fileA.txt
no changes added to commit (use "git add" and/or "git commit -a")
这里需要注意:执行git reset --mixed会默认执行git reset --soft,也就是说 --mixed选项功能包含–soft选项功能。如git reset --mixed 6ae24c9相当于:
git reset --soft 6ae24c9
git reset --mixed 6ae24c9
git reset --soft --mixed 6ae24c9
git reset 6ae24c9
更新工作目录(git reset --hard)
使用命令git reset --hard HEAD
将工作目录内容更新为索引区内容。三棵树的状态如图:
执行git status命令会提示我们工作目录是干净的,因为工作目录、索引区和HEAD内容都是相同的。
$ git status
On branch master
nothing to commit, working tree clean
这里需要注意:执行git reset --hard会默认执行git rest --soft和git reset --mixed,也就是说–hard选项功能包含–soft和 --mixed选项功能。
通过路径来更新索引区和工作目录
我们也可以不用将某个提交快照更新到索引区或工作目录,我们可以将指定的文件更新到索引区和工作目录,我们需要为reset命令指定一个路径,形式如下:
将最新一次提交快照中的file.txt文件更新到索引区(file.txt在项目根目录)
git reset -mixed HEAD fileA.txt
注意:HEAD只能指向一个提交对象,不能让HEAD指向一个提交对象中的文件,同时又指向另一个提交对象中的文件。所有没有 git rest --soft HEAD file.txt
命令,即不能移动HEAD指向到某个文件。
git checkout
git checkout和git reset --hard非常相似。checkout也会移动HEAD、更新索引区和工作目录,但是两者有2点不同:
- 第一个重要区别git checkout HEAD(切换到当前分支)是对工作目录是安全的(切换分支前如果三棵树的状态不一致会提示你在干净的工作目录下切换到其它分支),git reset --hard HEAD命令则会直接覆盖工作目录中内容已修改的文件(因已经修改的文件在工作目录中还未提交到本地仓库,一旦失误覆盖工作目录内容后修改的内容将无法恢复)。
- 第二个重要的区别是如何更新 HEAD。 reset 会移动 HEAD 分支的指向,而 checkout 只会移动 HEAD自身来指向另一个分支。
git checkout命令也可以指定路径(git checkout master fileA.txt
,用master分支最新一次提交快照中的fileA.txt更新索引区和工作目录,注意checkout指定路径后不会移动HEAD自身)。
git reset总结(1)
从以上内容我们可以看出:
- reset命令指定HEAD或HEAD其实就是指向一个commit对象对应的SHA-1的引用,只不过HEAD指向了分支的最新一次提交,HEAD指向分支最新一次提交的上一个提交。我们也可以通过显示的指定一个SHA-1(这个SHA-1必须指向一个提交对象)来让HEAD移动到指定SHA-1,索引区更新到指定的SAH-1,工作目录更新到指定的SHA-1。
- 现在我们也不难理解这些命令
git reset --soft HEAD~
回退到上一次提交,即丢弃最新一次提交
解析:其实就是移动了HEAD,将HEAD指向上一个commit对象,注意此命令没有加上路径的形式()。git reset --soft HEAD fileA.txt
git reset HEAD
将已暂存的文件移除暂存区,不加选项默认为–mixed
解析:其实就是将HEAD指向最新一次更新,然后将仓库最新一次提交快照更新到索引区,加上路径会将提交快照中指定的文件移除暂存区。
git reset --hard HEAD
将工作目录所有文件回退到仓库最新一次提交
解析:其实就是将HEAD指向最新一次更新,然后将仓库最新一次提交快照更新到索引区,将索引区内容更新到工作目录),注意此命令没有加上路径的形式()。git reset --hard HEAD fileA.txt
git checkout HEAD fileA.txt
将工作目录指定文件回退到仓库最新一次提交快照中的文件,即将指定的某个提交中的文件还原到工作目录
git checkout beanch-name
切换分支
git reset总结(2)
希望你现在熟悉并理解了 reset 命令,不过关于它和 checkout 之间的区别,你可能还是会有点困惑,毕竟不太可能记住不同调用的所有规则。
下面的速查表列出了命令对树的影响。 “HEAD” 一列中的 “REF” 表示该命令移动了 HEAD 指向的分支引用,而`‘HEAD’’ 则表示只移动了 HEAD 自身。 特别注意 WD Safe? 一列 - 如果它标记为 NO,那么运行该命令之前请考虑一下。ps:这个表才是精华,鉴定完毕。