看懂 Git
- 合并操作
- 分离 HEAD
- 分离 HEAD 测试
- 相对引用(^ || ~)
- 操作符 ^
- 相对引用 ^ 测试
- 操作符 ~
- 相对引用 ~ 测试
- 撤销变更
- Git Reset
- Git Revert
- 撤销变更 测试
- 整理提交记录
- Git Cherry-pick
- 测试
- 交互式 rebase
- 交互式 rebase 测试
合并操作
关键字:
commit
、branch
、merge
、rebase
…
基础指令在此不介绍
分离 HEAD
HEAD 是一个对当前所在分支的符号引用 —— 也就是指向你正在其基础上进行工作的提交记录
HEAD 总是指向当前分支上最近一次提交记录。大多数修改提交树的 Git 命令都是从改变 HEAD 的指向开始的
HEAD 通常情况下是指向分支名的 在你提交时,改变了分支名的状态,这一变化通过 HEAD 变得可见
如果想看
HEAD
指向,可以通过cat .git/HEAD
查看, 如果HEAD
指向的是一个引用,还可以用git symbolic-ref HEAD
查看它的指向
分离的 HEAD 就是让其指向了某个具体的提交记录而不是分支名。在命令执行之前的状态如下所示:
HEAD -> main -> C1
HEAD 指向 main, main 指向 C1
git checkout C1;
现在我们切换到 C1
,变成了
分离 HEAD 测试
想完成此关,从
bugFix
分支中分离出HEAD
并让其指向一个提交记录
通过哈希值指定提交记录。每个提交记录的哈希值显示在代表提交记录的圆圈中
git checkout C4;
相对引用(^ || ~)
通过指定提交记录哈希值的方式在 Git 中移动不太方便
在实际应用时,并没有像本程序中这么漂亮的可视化提交树供你参考
所以你就不得不用 git log 来查查看提交记录的哈希值
并且哈希值在真实的 Git 世界中也会更长(译者注:基于 SHA-1,共 40 位)
例如前一关的介绍中的提交记录的哈希值可能是 fed2da64c0efc5293610bdd892f82a58e8cbc5d8...
比较令人欣慰的是,Git 对哈希的处理很智能
你只需要提供能够唯一标识提交记录的前几个字符即可
因此我可以仅输入fed2 而不是上面的一长串字符
正如我前面所说,通过哈希值指定提交记录很不方便,所以
Git
引入了相对引用。这个就很厉害了!
使用相对引用的话,你就可以从一个易于记忆的地方(比如bugFix
分支或HEAD
)开始计算
相对引用非常给力,这里我介绍两个简单的用法:
使用^
向上移动1
个提交记录
使用~\<num>
向上移动多个提交记录,如~3
操作符 ^
首先看看操作符 (^) 把这个符号加在引用名称的后面,表示让 Git 寻找指定提交记录的 parent 提交
所以 main^ 相当于 “main 的 parent 节点”
main^^ 是 main 的第二个 parent 节点
现在咱们切换到 main 的 parent 节点
git checkout main^;
这种方式是不是比输入哈希值方便多了?
你也可以将 HEAD
作为相对引用的参照 下面咱们就用 HEAD
在提交树中向上移动几次
git checkout C3;
git checkout HEAD^;
git checkout HEAD^;
git checkout HEAD^
很简单吧 我们可以一直使用 HEAD^
向上移动
相对引用 ^ 测试
要完成此关,切换到
bugFix
的parent
节点。这会进入分离HEAD
状态
如果你愿意的话,使用哈希值也可以过关,但请尽量使用相对引用!
git checkout bugFix^;
操作符 ~
如果你想在提交树中向上移动很多步的话,敲那么多 ^ 貌似也挺烦人的
Git 当然也考虑到了这一点,于是又引入了操作符 ~
该操作符后面可以跟一个数字(可选,不跟数字时与 ^ 相同,向上移动一次)指定向上移动多少次
咱们用 ~<num>
一次后退四步
git checkout HEAD~4;
多么的简洁 —— 相对引用就是方便啊
强制修改分支位置
你现在是相对引用的专家了,现在用它来做点实际事情。
我使用相对引用最多的就是移动分支。可以直接使用 -f 选项让分支指向另一个提交。例如:
git branch -f main HEAD~3
上面的命令会将 main 分支强制指向 HEAD 的第 3 级 parent 提交
相对引用为我们提供了一种简洁的引用提交记录
C1
的方式, 而-f
则容许我们将分支强制移动到那个位置
相对引用 ~ 测试
既然你已经看过相对引用与强制移动分支的演示了,那么赶快使用这些技巧来挑战这一关吧!
要完成此关,移动 HEAD main 和 bugFix 到目标所示的位置
git checkout HEAD~1;
git branch -f main HEAD~1;
git branch -f main C6;
撤销变更
在 Git 里撤销变更的方法很多
和提交一样,撤销变更由底层部分(暂存区的独立文件或者片段)和上层部分(变更到底是通过哪种方式被撤销的)组成
我们这个应用主要关注的是后者
主要有两种方法用来撤销变更 —— 一是 git reset 还有就是 git revert
Git Reset
git reset
通过把分支记录回退几个提交记录来实现撤销改动,你可以将这想象成“改写历史”,git reset
向上移动分支,原来指向的提交记录就跟从来没有提交过一样
git reset HEAD^;
||
git reset HEAD~1;
漂亮! Git
把 main
分支移回到 C1
;现在我们的本地代码库根本就不知道有 C2
这个提交了
(译者注:在 reset
后, C2
所做的变更还在,但是处于未加入暂存区状态)
Git Revert
虽然在你的本地分支中使用 git reset 很方便,但是这种“改写历史”的方法对大家一起使用的远程分支是无效的哦!
为了撤销更改并分享给别人,我们需要使用 git revert 来看演示
git revert HEAD;
奇怪!在我们要撤销的提交记录后面居然多了一个新提交!这是因为新提交记录
C2'
引入了更改 —— 这些更改刚好是用来撤销C2
这个提交的。也就是说C2'
的状态与C1
是相同的
revert
之后就可以把你的更改推送到远程仓库与别人分享啦
撤销变更 测试
要完成此关,分别撤销 local 分支和 pushed 分支上的最近一次提交。共需要撤销两个提交(每个分支一个)
记住 pushed 是远程分支,local 是本地分支 —— 这么说你应该知道用分别哪种方法了吧?
git reset HEAD^;
git checkout pushed;
git revert HEAD;
整理提交记录
到现在我们已经学习了 Git 的基础知识 —— 提交、分支以及在提交树上移动。
这些概念涵盖了 Git 90% 的功能,同样也足够满足开发者的日常需求
然而, 剩余的 10% 在处理复杂的工作流时(或者当你陷入困惑时)可能就显得尤为重要了
接下来要讨论的这个话题是“整理提交记录” —— 开发人员有时会说“我想要把这个提交放到这里, 那个提交放到刚才那个提交的后面”
而接下来就讲的就是它的实现方式,非常清晰、灵活,还很生动
Git Cherry-pick
系列的第一个命令是 git cherry-pick, 命令形式为
git cherry-pick <提交号>...
如果你想将一些提交复制到当前所在的位置(HEAD)下面的话, Cherry-pick 是最直接的方式了
这里有一个仓库, 我们想将 side
分支上的工作复制到 main
分支,你立刻想到了之前学过的 rebase
了吧?但是咱们还是看看 cherry-pick 有什么本领吧
git cherry-pick C2 C4;
我们只需要提交记录 C2
和 C4
,所以 Git
就将被它们抓过来放到当前分支下了
测试
要通过此关,只需要简单的将三个分支中的提交记录复制到 main 上就可以了
git cherry-pick C3 C4 C7;
交互式 rebase
当你知道你所需要的提交记录(并且还知道这些提交记录的哈希值)时
用 cherry-pick 再好不过了 —— 没有比这更简单的方式了
但是如果你不清楚你想要的提交记录的哈希值呢?
幸好 Git 帮你想到了这一点, 我们可以利用交互式的 rebase —— 如果你想从一系列的提交记录中找到想要的记录, 这就是最好的方法了
交互式
rebase
指的是使用带参数--interactive
的rebase
命令, 简写为-i
如果你在命令后增加了这个选项,Git
会打开一个UI
界面并列出将要被复制到目标分支的备选提交记录,它还会显示每个提交记录的哈希值和提交说明,提交说明有助于你理解这个提交进行了哪些更改
在实际使用时,所谓的UI
窗口一般会在文本编辑器 —— 如Vim
—— 中打开一个文件
当 rebase UI界面打开时, 你能做3件事:
调整提交记录的顺序(通过鼠标拖放来完成)
删除你不想要的提交(通过切换 pick 的状态来完成,关闭就意味着你不想要这个提交记录)
合并提交. 简而言之,它允许你把多个提交记录合并成一个
当你点击下面的按钮时,会出现一个交互对话框,对提交记录做个排序(当然你也可以删除某些提交),点击确定看结果
git rebase -i HEAD~4;
排序关键字 pick
Git
严格按照你在对话框中指定的方式进行了复制
交互式 rebase 测试
要通过本关, 做一次交互式的
rebase
,整理成目标窗口中的提交顺序,记住,你随时都可以用 undo、reset 修正错误,这是不会记入步数的 😄