learn git branching学习整理
介绍
learn git branching是一个非常好的git学习网站,它与传统的文字讲解相比较起来有一个非常大的亮点----图形化的git提交树可以实时的反馈并告诉你当前所做的git操作在对于代码管理来说实际意味着什么,这是一个非常好的反馈机制,相比较于直接用命令行来学习git,你会更加了解当前正在做什么。
learn git branching还内置了一套自己的git算法,其大部分命令和真实的git命令没有区别,并且它会以闯关的形式进行git教学,如果能够顺利的通过所有关卡,那么初学者对于git的理解也就差不多了。
链接: https://learngitbranching.js.org/?locale=zh_CN.
我在刚开始接触并通过learn git branching学习git的时候正是被上面的那些优点所吸引,产生了想要快速通关的想法,但是在中途的时候我发现,每一个独立的每一个小关卡虽然简单,但是其实之后的一个关卡的通过其实都要依赖一些之前所用到过的git命令,如果对于之前的命令不加记忆,你很快就会玩不下去,于是我就开始记录通关命令,再后来转念一想由于工作的原因需要用到git,但是公司的电脑只能访问为数不多的知识查询网站(其中就不包括learn git branching)所以干脆还是把每一关的通关记录做成博客,方便以后需要用到的时候随时查看。
另外,我发现learn git branching虽然强大,但是有些git相关的知识他也是不包含的,比如git在真实的环境中,本地电脑下是有三个工作区的(workspace、缓冲区和本地仓库),这一点在教程中都没有体现出来(以后统称learn git branching为教程),另外,由于是git教学的一个网站,它的很多命令也是网站作者自己实现的,所以并不是所有的git命令都可以在该网站上使用验证,所以在这篇博客完成后我会按照教程关卡的顺序在真实环境下详细验证一些我觉得理解起来比较模糊的命令。如果有必要的话,应该会把验证过程也上传到博客。
第一关:Git commit
- git的提交非常轻量化,大部分情况下它只会把当前版本与上一个版本进行对比,并且将差异打包提交。
- Git保存了提交的历史记录,这就是为什么大多数提交记录的上面都有父节点的原因。
通关条件
初始条件:
通关目标:
通关命令
git commit
git commit
第二关:Git Branch
Git的分支也非常轻量化,即使创建再多的分支也不会造成存储或内存上的开销,使用分支其实就相当于在说:“我想基于这个提交以及它所有的父提交进行新的工作”
基础命令:
git branch newImage
,使用了这个命令之后会生成一个新的分支,并且指向现在的主分支。
通关条件
初始条件:
通关目标:
通关命令
- git branch bugFix
git checkout bugFix - git checkout -b bugfix
备注:第一种方法,先创建分支再切换,第二种方法,创建分支的同时再切换到该分支
第三关:git merge(分支与合并)
我们可以新建一个分支,在其上开发某个新功能,开发完成后再合并回主线。合并两个分支时会产生一个特殊的提交记录,它有两个父节点,相当于:“我要把这两个父节点本身及它们所有的祖先都包含进来”
基础命令:
git merge bugfix
,如果有两个分支main和bugFix,且当前分支为main,那么使用git merge bugFix就会使main分支commit一次,并且commit后的版本会合并bugFix中的新功能。
命令演示
git merge bugfix
:
再把main分支合并到bugFix,git checkout bugFix;git merge main
:
通关条件:
初始条件
通关目标:
通关命令:
git checkout -b bugfix //创建bugFix分支并切换到该分支
git commit //bugFix分支提交一次
git checkout main //切换到main分支
git commit //main分支提交一次
git merge bugfix //把bugFix分支的修改合入main分支
备注:哪个分支上有*号,表示当前处于哪个分支。
第四关:Git Rebase
第二种合并分支的方法是git rebase。rebase实际上就是取出一系列的提交记录,“复制”它们,然后在另外一个地方逐个的放下去。
Rebase的优势就是可以创造更线性的提交历史,这听上去有些难以理解。如果只允许使用Rebase的话,代码库的提交历史将会变得异常清晰。
基础命令:
git rebase main
同样还是准备两个分支,并且当前所在的分支是bugFix。我们想要把bugFix分支里的工作直接移到main分支上。移动以后会使得两个分支的功能看起来像是按顺序开发,但实际上它们是并行开发的。
命令演示
git rebase main
备注:
看起来就像是bugFix分支放弃了自己之前的父节点,并且在把两个分支的内容进行合并的基础上,父节点改为main的当前节点。这一点和git merge不同,git merge并不会改变分支的父节点。
git checkout main;git rebase bugFix
通关条件
初始条件:
通关目标:
通关命令:
git checkout -b bugFix //创建bugFix分支并切换到该分支
git commit //bugFix分支提交一次
git checkout main //切换到main分支
git commit //main分支提交一次
git checkout bugfix //切换到bugFix分支
git rebase main //将main分支的当前提交作为父节点,并合并修改点到 bugFix分支
第五关:在提交树上移动
在接触Git更高级功能之前,我们有必要先学习在你项目的提交树上前后移动的几种方法。
一旦熟悉了如何在Git提交树上移动,你驾驭其他命令的能力也将水涨船高。
HEAD
HEAD个人感觉有点像C语言的指针,HEAD总是指向当前分支上最近一次提交记录,大多数Git命令都是从改变HEAD的指向开始的。HEAD通常情况下是指向分支名的(如bugFix),在你提交时,产生了一个新的提交记录,HEAD也将指向这个最新的记录。
命令演示
git checkout C1;git checkout main;git commit;git checkout C2
备注
可以通过git checkout <提交记录的哈希值>和git checkout <分支名>来切换HEAD到某个提交记录上或切换到某个分支所在的提交记录上,哈希值即为实际使用Git的时候代表提交记录的唯一一串数值,在本教程中简化了哈希值的表示,用C0 C1 C2来代替。另外,如果想要查看HEAD指向,可以通过cat .git/HEAD查看,如果HEAD指向的是一个引用,还可以用git symbolic-ref HEAD查看它的指向。
通关条件
初始条件:
通关目标:
通关命令:
git checkout C4 //将HEAD切换到提交记录C4上
备注:
可以通过git checkout <提交记录的哈希值>和git checkout <分支名>来切换HEAD到某个提交记录上或切换到某个分支所在的提交记录上
第六关:相对引用
正如我之前所说,在真实的世界中哈希值是很长的(由40位的随机字符组成),所以在真实世界中如果要切换到某个提交记录可就不是git checkout c1这么简单了(最少输入前四位哈希值),所以基于这种不方便的“绝对路径”式的移动,git提出了相对引用的解决办法。
使用相对应用的话,你就可以从一个易于记忆的地方开始计算(比如bugFix分支或HEAD),下面是相对引用的两个简单用法:
① 使用^向上移动1个提交记录
② 使用~向上移动多个提交记录,如~3
基础命令
main^
相当于“main的父节点”。main^^
是main的第二个父节点
命令演示
git checkout main^
HEAD也可以作为相对引用的参照:git checkout C3;git checkout HEAD^;git checkout HEAD^;git checkout HEAD^
通关条件
初始条件:
通关目标:
通关命令
git checkout bugFix^
第七关:相对引用(~)
这个没什么好说的,和之前差不多,只不过用数字的方式来表示相对往上跳几次。
通关条件
命令演示
git checkout HEAD~4
强制修改分支位置
强制修改分支的-f参数可以使分支指向另一个提交。例如git branch -f main HEAD~3可以让main分支指向HEAD之前的第三个提交记录git branch -f main HEAD~3
通关条件
初始条件:
通关条件:
通关命令
git branch -f main c6
git checkout HEAD^
git branch -f bugFix HEAD^
备注:
git branch -f main c6 :把main分支切换到提交记录c6上,是git branch -f bugFix HEAD^的变种用法
第八关:撤销变更
在git里撤销变更的方法很多。和提交一样,撤销变更由底层部分(暂存区的独立文件或者片段)和上层部分(变更到底是通过哪种方式被撤销的)组成。我们这个应用主要关注的是后者。
主要有两种方法用来撤销变更—一是git reset,还有就是git revert。
基础命令:
git reset通过把分支记录回退几个提交记录来实现撤销改动。你可以将这想象成“改写历史”。git reset 向上移动分支,原来指向的提交记录就跟从来没有提交过一样。
命令演示
git reset HEAD~1
备注:
git 把main分支移回到c1;现在本地代码库根本就不知道有c2这个提交了
git Revert
虽然在本地分支中使用git reset很方便,但是这种“改写历史”的方法对大家一起使用的远程分支是无效的,为了撤销更改并分享给别人,我们需要使用git revert。git revert HEAD
备注:
新的提交记录c2’引入了更改,这些更改刚好是用来撤销c2这个提交的。也就是说C2’的状态与C1是相同的。
通关条件
初始条件:
通关目标:
通关命令
git reset HEAD~1
git checkout pushed
git revert HEAD
第九关:git cheery-pick(整理提交记录)
到现在我们已经学习了Git的基础知识—提交、分支以及在提交树上移动。这些概念涵盖了Git90%的功能,同样也足够满足开发者的日常需求。
然而,剩余的10%在处理复杂的工作流时(或者当你陷入困惑时)可能就显得尤为重要了。接下来要讨论的这个话题是“整理提交记录”—开发人员有时会说“我想要把这个提交放到这里,那个提交放到刚才那个提交的后面”,而接下来就讲的就是它的实现方式,非常清晰、灵活,还很生动。
看起来挺复杂,其实是一个很简单的概念。
基础命令
本系列的第一个命令是git cherry-pick,命令形式为:
git cherry-pick <提交号>…
如果你想将一些提交复制到当前所在的位置(HEAD)下面的话,Cherry-pick是最直接的方式了。我个人非常喜欢cherry-pick,因为它特别简单。
接下来看一个具体的例子:
这里有一个仓库,我们想将side分支上的工作复制到main分支,你立刻想到了之前学过的rebase了吧?但是咱们还是看看cherry-pick有什么本领吧。
命令演示
git cherry-pick c2 c4
备注:我们只需要提交记录C2和C4,所以Git就将它们抓过来放到当前分支下了。就是这么简单。
通关条件
初始条件:
通关目标:
通关命令
git cherry-pick c3 c4 c7
备注:
看图好像挺复杂的,其实就是把c3 c4 c7 cherry-pick到main分支上。
第十关:交互式rebase
当你知道你所需要的提交记录(并且还知道这些提交记录的哈希值)时,用cherry-pick再好不过了—没有比这更简单的方式了。但是如果你不清楚你想要的提交记录的哈希值呢?幸好git帮你想到了这一点,我们可以利用交互式的rebase—如果你想从一系列的提交记录中找到想要的记录,这就是最好的方法了。
具体来看一下:
交互式rebase指的是使用带参数—interactive的rebase命令,简写为-i
如果你在命令后增加了这个选项,git会打开一个UI界面并列出将要被复制到目标分支的备选提交记录,他还会显示每个提交记录的哈希值和提交说明,提交说明有助于你理解这个提交进行了哪些更改。
在实际使用时,所谓的UI窗口一般会在文本编辑器—如vim中打开一个文件。考虑到课程的初衷,我弄了一个对话框来模拟这些操作。
基础命令
当rebase UI界面打开时,你能做3件事:
① 调整提交记录的顺序(通过鼠标拖放来完成)
② 删除你不想要的提交(通过切换pick的状态来完成,关闭就意味着你不想要这个提交记录)
③ 合并提交。遗憾的是由于某种逻辑的原因,我们的课程不支持此功能,因此我不会详细介绍这个操作。简而言之,它允许你把多个提交记录合并成一个。
命令演示
接下来看一个实例:git rebase -I HEAD~4
备注:拖拽界面我没有截屏,其实也挺简单的,就四个标签让你拖,随便改变他们的位置,每个标签上面还有一个按钮,你可以选择删除它或者保留。HEAD~4的意思是,从当前提交记录开始选择一共四个提交记录进行拖拽选择。
通关条件
初始条件:
通关目标:
通关命令
git rebase -I HEAD~4
备注:
拖拽,就完事了。如果你还是不清楚的话,可以到learn git branching去亲自体验一下这一关。
第十一关:本地栈式提交(只取一个提交记录)
来看一个在开发中经常会遇到的情况:我正在解决某个特别棘手的Bug,为了便于调试而在代码中添加了一些调试命令并向控制台打印了一些信息。这些调试和打印语句都在他们各自的提交记录里。最后我终于找到了造成这个Bug的根本原因,解决掉以后觉得沾沾自喜!最后就差把bugFix分支里的工作合并回main分支了。你可以选择通过fast-forward快速合并到main分支上,但这样的话main分支就会包含我这些调试语句了。你肯定不想这样,应该还有更好的方式……
实际上我们只要让Git复制解决问题的那一个提交记录就可以了。跟之前我们在“整理提交记录”中学到的一样,我们可以使用
① git rebase -i
② git cherry-pick
来达到目的。
通关条件
初始条件:
通关目标:
通关命令
方法1:
git checkout main //切换当前分支到main
git cherry-pick c4 //把提交记录c4更新到当前分支下(main)
方法2:
git rebase -I HEAD~4 //当前分支所在提交记录往上数4个进行拖拽】
git branch -f main c4’ //把main分支切换到提交记录c4’上
git checkout main //当前分支切换为main
备注
① main分支相当于出现bug的版本,debug提交记录c2则代表开始尝试修复bug的版本,提交记录c3则表示这个bug好像很棘手,我必须加一点打印来追踪问题所在,提交记录c4则表示,通过c3添加的打印信息,bug已经被修复了,而且为了调试所添加的打印信息也已经被删除了,所以现在我只需要把c4合入到main分支,c2 和c3则是不需要的。
② 还是cherry-pick简单,不过方法1中的命令操作完了之后bugFix还是停留在记录c4上(当时已经过关了,我就没有继续操作,把bugFix切换到c4’上)。
第十二关:提交的技巧 #1
接下来这种情况也是很常见的:你之前在newImage分支上进行了一次提交,然后又基于它创建了caption分支,然后又提交了一次。
此时你想对某个以前的提交记录进行了一些小小的调整。比如设计师想修改一下newImage中图片的分辨率,尽管那个提交记录并不是最新的了。
我们可以通过下面的方法来克服困难:
① 先用git rebase -i 将提交重新排序,然后把我们想要修改的提交记录挪到最前
② 然后用git commit –amend来进行一些小修改
③ 接着再用git rebase -i来将他们调回原来的顺序
④ 最后我们把main 移到修改的最前端(用你自己喜欢的方法),就大功告成了。
当然完成这个任务的方法不止上面提到的一种(我知道你在看cherry-pick),之后我们会多点关注这些技巧,但现在暂时只专注上面这种方法。最后有必要说明一下目标状态中那几个’ —我们把这个提交记录移动了两次,每移动一次会产生一个’;而c2上多出来的那个是我们在使用了amend参数提交时产生的,所以最终结果就是这样了。
也就是说,我在对比结果的时候只会对比提交树的结构,对于’的数量上的不同,并不纳入对比范围内。只要你的main分支结构与目标结构相同,我就算你通过。
通关条件
初始条件:
通关目标:
通关命令
git rebase -I HEAD~2 //把提交记录C2挪到最前面来,方便修改
git commit -amend //这个命令的意思应该是在不产生新的提交记录的情况下修改当前//提交记录,也就是我们看到的中间的两个c2,不过教程里面没讲,//以后有机会我会尝试一下的。
git rebase -I HEAD~2 //再次把c3挪到最前方。
git checkout main //切换当前分支为main
第十三关:提交的技巧 #2
正如你在上一关所见到的,我们可以使用rebase -i对提交记录进行重新排序。只要把我们想要的提交记录挪到最前端,我们就可以很轻松的用—amend修改它,然后把它们重新排成我们想要的顺序。
但这样做唯一的问题就是要进行两次排序,而这有可能造成由rebase而导致的冲突。下面还是看看git cherry-pick是怎么做的吧。
基础命令:
要在心里牢记cherry-pick可以将提交树上任何地方的提交记录取过来追加到HEAD上(只要不是HEAD上游的提交就没问题)。
命令演示
来看看这个例子:git cherry-pick C2
通关条件
初始条件:
通关目标:
通关命令
git checkout main
git cherry-pick c2
git commit –amend
git cherry-pick c3
第十四关:Git Tags
相信通过前面课程的学习你已经发现了:分支很容易被人为移动,并且当有新的提交时,它也会移动。分支很容易被改变,大部分分支还只是临时的,并且还一直在变。
你可能会问了:有没有什么可以永远指向某个提交记录的标识呢,比如软件发布新的大版本,或者是修正一些重要的Bug或是增加了某些新特性,有没有比分支更好的可以永远指向这些提交的方法呢?
当然有了!Git的tag就是干这个用的,它们可以(在某种程度上—因为标签可以被删除后重新在另外一个位置创建同名的标签)永久地将某个特定地提交命名为里程碑,然后就可以像分支一样引用了。
更难得的是,它们不会随着新的提交而移动。你也不能切换到某个标签上面进行修改,它就像是提交树上的一个锚点,标识了某个特定的位置。
咱们来看看标签到底是什么样。
基础命令
咱们先建立一个标签,指向提交记录C1,表示这是我们1.0版本。
命令演示
git tag v1 c1
备注:
我们将这个标签命名为v1,并且明确地让它指向提交记录c1,如果你不指定提交记录,Git会用HEAD所指向的位置。
通关条件
初始条件:
通关目标:
通关命令
git tag v1 c2
git tag v0 c1
git checkout v1 //切换到v1上去(相当于通过标签来切换到c2上)
第十五关:Git Describe
由于标签在代码库中起着“锚点”的作用,Git还为此专门设计了一个命令用来描述离你最近的锚点(也就是标签),他就是git describe!
Git Describe 能帮你在提交历史中移动了多次以后找到方向;当你用git bisect(一个查找产生Bug的提交记录的指令)找到某个提交记录时,或者是当你坐在你那刚刚度假回来的同事的电脑前时,可能会用到这个命令。
基础命令:
git describe的语法是:git describe <ref>
<ref>
可以是任何能被Git识别成提交记录的引用,如果你没有指定的话,Git会以你目前所检出的位置(HEAD)。
它输出的结果是这样的:<tag>_<numCommits>_g<hash>
tag表示的是离ref最近的标签,numCommits是表示这个ref与tag相差有多少个提交记录,hash表示的是你所给定的ref所表示的提交记录哈希值的前几位。
当ref提交记录上有某个标签时,则只输出标签名称。
命令演示
接下来看一个例子,对于下面的提交树:git tag v2 c3
备注
git describe main会输出:
v1_2_gC2
git describe side会输出:
v2_1_gC4
通关条件
随便敲几个git describe命令感受一下就可以了,想要通关直接git commit即可,这一关主要是记住<tag>_<numCommits>_g<hash>
表示的含义,其他的倒没啥。
第十六关:多分支rebase
哥们儿,我们准备了很多分支!咱们把这些分支rebase到main上吧。
但是你的领导给你提了点要求—他们希望得到有序的提交历史,也就是我们最终的结果应该是c6’在c7’上面,c5’在c6’上面,依此类推。
通关条件
初始条件:
通关目标:
通关命令
我的答案(6步):
git rebase main bugFix //把bugFix分支rebase到main上
git rebase c3’ c4 //把提交记录c4 rebase到c3’上
git rebase c4’ c5 //把提交记录c5 rebase到c4’上
git rebase c5’ side //把side分支rebase到c5’上
git rebase side another //把another分支rebase到side分支上
git branch -f main c7‘ //把main分支切换到提交记录c7’上文章来源:https://www.toymoban.com/news/detail-467632.html
标准答案(4步):
git rebase main bugFix //第一步是一样的
git rebase bugFix side //原来直接把siderebase到bugFix上可以同时线性提交c4 c5 c6
git rebase side another //这一步也是一样的
git rebase another main //新的main分支切换方法(高端)文章来源地址https://www.toymoban.com/news/detail-467632.html
到了这里,关于learn git branching学习整理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!