目录
1、分离HEAD
首先看一下 “HEAD”。
HEAD 是一个对当前所在分支的符号引用 —— 也就是指向你正在其基础上进行工作的提交记录。
HEAD 总是指向当前分支上最近一次提交记录。大多数修改提交树的 Git 命令都是从改变 HEAD 的指向开始的。
HEAD 通常情况下是指向分支名的(如 bugFix)。在你提交时,改变了 bugFix 的状态,这一变化通过 HEAD 变得可见。
用如下流程理解,观察提交前后 HEAD 的位置:
$ git checkout C1
$ git checkout main
$ git commit
$ git checkout C2
看到了吗? HEAD 指向了 main
,随着提交向前移动。
注:实际这些命令并不是真的在查看 HEAD 指向。如果想看 HEAD 指向,可以通过
cat .git/HEAD
查看, 如果 HEAD 指向的是一个引用,还可以用
git symbolic-ref HEAD
查看它的指向。
分离的 HEAD 就是让其指向了某个具体的提交记录而不是分支名。
在命令执行之前的状态如下所示:
HEAD -> main -> C1 (HEAD 指向 main, main 指向 C1)
执行命令:
$ git checkout C1
现在变成了: HEAD -> C1
2、相对引用(^)
通过指定提交记录哈希值的方式在 Git 中移动不太方便。在实际应用时,并没有像本教程中的图这么漂亮的可视化树图供你参考,所以你就可以用 git log
来查查看提交记录的哈希值。
并且哈希值在真实的 Git 世界中也会更长(基于 SHA - 1,共 40 位)。例如上面介绍中的提交记录的哈希值可能是 fed2da64c0efc5293610bdd892f82a58e8cbc5d8
。
令人欣慰的是,Git 对哈希的处理很智能。你只需要提供能够唯一标识提交记录的前几个字符即可。因此我可以仅输入fed2
而不是上面的一长串字符。
而通过哈希值指定提交记录很不方便,所以 Git 引入了相对引用。
使用相对引用的话,你就可以从一个易于记忆的地方(比如 bugFix
分支或 HEAD
)开始计算。
介绍两个简单的用法:
- 使用 ^ 向上移动 1 个提交记录
- 使用
~<num>
向上移动多个提交记录,如~3
1):“^”操作符
把这个符号加在引用名称的后面,表示让 Git 寻找指定提交记录的 parent 提交。
所以 main^
相当于“main
的 parent 节点”。main^^
是 main
的第二个 parent 节点。
执行如下指令变化如图:
$ git checkout main^
→
也可以将 HEAD
作为相对引用的参照。下面咱们就用 HEAD
在提交树中向上移动一次。
$ git checkout C3
$ git checkout HEAD^
2):“~”操作符
如果你想在提交树中向上移动很多步的话,敲那么多 ^
也挺烦人的,Git 当然也考虑到了这一点,于是又引入了操作符 ~
。
该操作符后面可以跟一个数字(可选,不跟数字时与 ^
相同,向上移动一次),指定向上移动多少次。
用 ~<num>
一次后退四步。
$ git checkout HEAD~4
→
强制修改分支位置
我使用相对引用最多的就是移动分支。
可以直接使用 -f 让分支指向另一个提交。例如:git branch -f main HEAD~3
此命令会将 main 分支强制指向HEAD 的第 3 级parent 提交。示意如下:
$ git branch -f main HEAD~3
或者
$ git branch -f main C1
→
相对引用为我们提供了一种简洁的引用提交记录 C1
的方式, 而 -f
则容许我们将分支强制移动到那个位置。
3、撤销变更
在 Git 里撤销变更的方法很多。
和提交一样,撤销变更由底层部分(暂存区的独立文件或者片段)和上层部分(变更到底是通过哪种方式被撤销的)组成。我们这里主要关注的是后者。
主要有两种方法用来撤销变更
-
git reset
-
git revert
1):git reset
git reset
通过把分支记录回退几个提交记录来实现撤销改动。你可以将这想象成“改写历史”。git reset
向上移动分支,原来指向的提交记录就跟从来没有提交过一样。
$ git reset HEAD~1
→
Git 把 main 分支移回到 C1
;现在我们的本地代码库根本就不知道有 C2
这个提交了。
注:在reset后,
C2
所做的变更还在,但是处于未加入暂存区状态。
2):git revert
虽然在你的本地分支中使用 git reset
很方便,但是这种“改写历史”的方法对大家一起使用的远程分支是无效的!
为了撤销更改并将此撤销分享给别人,我们需要使用 git revert
。
$ git revert HEAD
→
在我们要撤销的提交记录后面多了一个新提交!这是因为新提交记录 C2'
引入了更改 —— 这些更改刚好是用来撤销 C2
这个提交的。也就是说 C2'
的状态与 C1
是相同的。
revert 之后就可以把你的更改推送到远程仓库与别人分享啦。