看懂 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 修正错误,这是不会记入步数的 😄