学习资料
- Learn Git Branching:像是闯关游戏一样,每一个关卡都有一个实用场景
本地
git commit
一个新提交。
git branch
创建一个新分支。
Git 的分支也非常轻量。
git branch newImage
创建新分支。接下来继续切换分支
git checkout newImage
注意:在 Git 2.23 版本中,引入了一个名为 git switch
的新命令,最终会取代 git checkout
,因为 checkout
作为单个命令有点超载(它承载了很多独立的功能)。 由于现在很多人还无法使用 switch
,本次课程仍然使用 checkout
而不是 switch
, 但是如果你想尝试一下新命令,我们的应用也是支持的!并且你可以从这里学到更多关于新命令的内容。
如果你想创建一个新的分支同时切换到新创建的分支的话 git checkout -b <your-branch-name>
来实现
git checkout -b newImage
等于
git branch newImage
git checkout newImage
git merge
合并两个分支: Git 中合并两个分支时会产生一个特殊的提交记录,它有两个 parent 节点。翻译成自然语言相当于:“我要把这两个 parent 节点本身及它们所有的祖先都包含进来。”
这里可以看到。带星号的分支。去合并merge后面的分支
这里值得注意。main继承自其他的分支,其他分支再去合并main的时候,并不会产生新的节点,而是指向合并的那个提交记录。
题目

git checkout -b bugFix
git commit
git checkout main
git commit
git merge
git rebase
合并分支的方法是 git
。rebase
实际上就是取出一系列的提交记录,“复制”它们,然后在另外一个地方逐个的放下去。
这个方法可以理解成重新设置根据地。这里就是把bugFix
写到main
的下一个节点。
再切换到main
执行git rebase bugFix
,效果只是把main
分支引用向前移动
题目:
git checkout -b bugFix
git commit
git checkout main
git commit
git checkout bugFix
git rebase main
HEAD
HEAD
是是当前所在分支的符号引用。
- 如果想看 HEAD 指向,可以通过
cat .git/HEAD
查看 - 如果 HEAD 指向的是一个引用,还可以用
git symbolic-ref HEAD
查看它的指向。
分离的HEAD
:正常的HEAD
指向的是所在的分支名。分离的HEAD
是让它指向某一个具体的提交记录。
正常情况下是这样的
HEAD -> main -> C1
HEAD 指向 main, main 指向 C1
git checkout c1 这里checkout操作的是HEAD
git checkout c1
分离之后的是这样的
HEAD -> C1
题目:
git checkout c4
git checkout
移动提交记录的两种方法:
- git checkout :移动的是
HEAD
- git branch -f :移动的是分支
上面例子中的c2,c3,c4
其实都是提交记录的哈希值,查看的命令是git log
在实际应用时,并没有像本程序中这么漂亮的可视化提交树供你参考,所以你就不得不用
git log
来查查看提交记录的哈希值。并且哈希值在真实的 Git 世界中也会更长(译者注:基于 SHA-1,共 40 位)。例如前一关的介绍中的提交记录的哈希值可能是
fed2da64c0efc5293610bdd892f82a58e8cbc5d8
。舌头都快打结了吧…比较令人欣慰的是,Git 对哈希的处理很智能。你只需要提供能够唯一标识提交记录的前几个字符即可。因此我可以仅输入fed2
而不是上面的一长串字符。
通过哈希值指定这个提交记录不是很方便。所以有相对引用。相对引用的对象可以是HEAD
,引用名称。
- 使用
^
向上移动 1 个提交记录 - 使用
~<num>
向上移动多个提交记录,如~3
题目:
git checkout bugFix~
值得注意。 checkout
操作的作用是切换当前的操作分支。也就是head
。
git branch -f
使用相对引用最多的就是移动分支。可以直接使用 -f
选项让分支指向另一个提交。例如:
git branch -f main HEAD~3
上面的命令会将 main 分支强制指向 HEAD 的第 3 级 parent 提交。
题目:
git branch -f main c6
git checkout HEAD~
git branch -f bugFix HEAD~
这个题目很好的反应了。如果想移动HEAD
的节点用``checkout就可以完成。
checkout的操作的对象就是
HEAD。如果想移动其他的分支那就用
git branch -f`。
撤销变更
主要有两种方法用来撤销变更 —— 一是 git reset
,还有就是 git revert
。接下来咱们逐个进行讲解。
git reset
:撤销本地的分支, 这个命令就可以看成移动所在分支到某个位置
功能与移动分支有点像————git branch -f
git checkout HEAD~
这个移动HEAD到某个位置git revert
:增加一个新提交,这个新提交跟上上一个提交内容一样,可以给远程的使用
题目:
git reset HEAD~ 这个命令就可以看成移动所在分支到某个位置
功能与移动分支有点像,git branch -f
git checkout HEAD~这个移动HEAD到某个位置
git checkout pushed
git revert HEAD
Git Cherry-pick
git cherry-pick
, 命令形式为:
git cherry-pick <提交号>...
作用:简而言之在HEAD
下面复制一些之前的提交。
如果你想将一些提交复制到当前所在的位置(HEAD
)下面的话, Cherry-pick 是最直接的方式了。
题目
git cherry-pick bugFix side~ another
或者
git cherry-pick c3 c4 c7
交互式的 rebase
rebase
就是重新选择base
加上-i
代表交互式,以哪个提交为base
呢,就看最后一个参数HEAD~n
当你知道你所需要的提交记录(并且还知道这些提交记录的哈希值)时 cherry-pick
再好不过了
但是如果你不清楚你想要的提交记录的哈希值呢? 幸好 Git 帮你想到了这一点, 我们可以利用交互式的 rebase
交互式 rebase
指的是使用带参数 --interactive
的 rebase
命令, 简写为 -i
题目:
git rebase -i HEAD~4
本地栈式提交
来看一个在开发中经常会遇到的情况:我正在解决某个特别棘手的 Bug,为了便于调试而在代码中添加了一些调试命令并向控制台打印了一些信息。
这些调试和打印语句都在它们各自的提交记录里。最后我终于找到了造成这个 Bug 的根本原因,解决掉以后觉得沾沾自喜!
最后就差把 bugFix
分支里的工作合并回 main
分支了。你可以选择通过 fast-forward 快速合并到 main
分支上,但这样的话 main
分支就会包含我这些调试语句了。你肯定不想这样,应该还有更好的方式……
方式1
git checkout main
git cherry-pick bugFix
这里也可以看着使用cherry-pick,需要把HEAD移动到要操作的节点
方式2
git rebase -i HEAD~3
git branch -f main bugFix 这里注意:*应该还在bugFix上面
提交的技巧 #1
你之前在 newImage
分支上进行了一次提交,然后又基于它创建了 caption
分支,然后又提交了一次。
此时你想对某个以前的提交记录进行一些小小的调整。比如设计师想修改一下 newImage
中图片的分辨率,尽管那个提交记录并不是最新的了。
简而言之,想修改之前的提交
我们可以通过下面的方法来克服困难:
- 先用
git rebase -i
将提交重新排序,然后把我们想要修改的提交记录挪到最前 - 然后用
git commit --amend
来进行一些小修改 - 接着再用
git rebase -i
来将他们调回原来的顺序 - 最后我们把 main 移到修改的最前端(用你自己喜欢的方法),就大功告成啦!

git rebase -i HEAD~2
git commit --amend 这里注意HEAD在C2'上面,执行这个--amend,在这个位置再出现一个平行提交C2''
git rebase -i HEAD~2
git branch -f main caption
Git Tags
打标签:版本号;可以永远指向某个提交记录的标识呢,比如软件发布新的大版本
git tag v1 c1 我们将这个标签命名为 v1,并且明确地让它指向提交记录 C1,
git tag v1 如果你不指定提交记录,Git 会用 HEAD 所指向的位置。
git tag v0 c1
git tag v1 c2
git checkout HEAD^1
Git Describe
作用:返回提交记录的标签和描述信息
git describe
的语法是:
git describe <ref> < ref>可以是任何能被 Git 识别成提交记录的引用
git describe Git 会使用你目前所在的位置(`HEAD`)
它输出的结果是这样的:
<tag>_<numCommits>_g<hash>
tag
表示的是离 ref
最近的标签, numCommits
是表示这个 ref
与 tag
相差有多少个提交记录, hash
表示的是你所给定的 ref
所表示的提交记录哈希值的前几位。
当 ref
提交记录上有某个标签时,则只输出标签名称
多次rebase
这一节是综合练习题:
git cherry-pick c3 c4 c5 c6 c7
本关不允许使用
这里还比较好理解:把bugfix
重新设置base
为main
这里一下子来了好几个,把side
的base
设置成bugfix:可以理解成要复制成一个提交节点,需要把不同分支下的每一步修改都加上去。
git rebase main bugFix
git rebase bugFix side
git rebase side another
git rebase another main 这里是使用rebase改变main的位置
选择 parent 提交记录
- 就是指间接引用时有两个parent的时候通过
^
选择不同的支路 - 还有快捷方式,把^和~组合到一起
git branch bugWork HEAD~^2~
纠缠不清的分支
现在我们的 main
分支是比 one
、two
和 three
要多几个提交。出于某种原因,我们需要把 main
分支上最近的几次提交做不同的调整后,分别添加到各个的分支上。
one
需要重新排序并删除 C5
,two
仅需要重排排序,而 three
只需要提交一次。
git checkout one
git cherry-pick c4 c3 c2
git checkout two
git cherry-pick c5 c4 c3 c2
git branch -f three c2
远程
引言:远程仓库并不复杂, 在如今的云计算盛行的世界很容易把远程仓库想象成一个富有魔力的东西, 但实际上它们只是你的仓库在另个一台计算机上的拷贝。你可以通过因特网与这台计算机通信 —— 也就是增加或是获取提交记录
话虽如此, 远程仓库却有一系列强大的特性
- 首先也是最重要的的点, 远程仓库是一个强大的备份。本地仓库也有恢复文件到指定版本的能力, 但所有的信息都是保存在本地的。有了远程仓库以后,即使丢失了本地所有数据, 你仍可以通过远程仓库拿回你丢失的数据。
- 还有就是, 远程让代码社交化了! 既然你的项目被托管到别的地方了, 你的朋友可以更容易地为你的项目做贡献(或者拉取最新的变更)
现在用网站来对远程仓库进行可视化操作变得越发流行了(像 GitHub), 但远程仓库永远是这些工具的顶梁柱, 因此理解其概念非常的重要!
git clone
在本地创建一个远程仓库的拷贝(比如从 github.com)
在我们的本地仓库多了一个名为 o/main
的分支, 这种类型的分支就叫远程分支。由于远程分支的特性导致其拥有一些特殊属性。
远程分支有一个特别的属性,在你切换到远程分支时,自动进入分离 HEAD 状态。Git 这么做是出于不能直接在这些分支上进行操作的原因, 你必须在别的地方完成你的工作, (更新了远程分支之后)再用远程分享你的工作成果。
远程分支有一个命名规范 —— 它们的格式是:
<remote name>/<branch name>
------------远程仓库名/分支名
:远程仓库名
默认是origin
:因为当git clone
某个仓库时,Git把远程仓库的名称设置为origin
了因此,如果你看到一个名为
o/main
的分支,那么这个分支就叫main
,远程仓库的名称就是o
。大多数的开发人员会将它们主要的远程仓库命名为
origin
,并不是o
。这是因为当你用git clone
某个仓库时,Git 已经帮你把远程仓库的名称设置为origin
了不过
origin
对于我们的 UI 来说太长了,因此不得不使用简写o
😃 但是要记住, 当你使用真正的 Git 时, 你的远程仓库默认为origin
!
本地切换远程分支:o/main
不会的改变,HEAD
改变,分离了。
题目:

git commit
git checkout o/main 这里进入HEAD分离状态
git commit
git fetch
Git 远程仓库相当的操作实际可以归纳为两点:向远程仓库传输数据以及从远程仓库获取数据。
从远程仓库获取数据 —— git fetch
。
git fetch
完成了仅有的但是很重要的两步:
- 从远程仓库下载本地仓库中缺失的提交记录
- 更新远程分支指针(如
o/main
)
git fetch
实际上将本地仓库中的远程分支更新成了远程仓库相应分支最新的状态。
题目:

git fetch
git Pull
git fetch
获取远程的数据, 现在需要把远程的数据和本地数据进行合并。
其实有很多方法的
git cherry-pick o/main
git rebase o/main
git merge o/main
- 等等
实际上,由于先抓取更新再合并到本地分支这个流程很常用,因此 Git 提供了一个专门的命令来完成这两个操作。它就是我们要讲的 git pull
。
首先用 git fetch
命令和git merge
命令:这个merge主要是以两个原来的为父节点生成一个新的节点。
git pull
就是 git fetch 和 git merge 的缩写!
题目:

git pull
git push
上负责将你的变更上传到指定的远程仓库,并在远程仓库上合并你的新提交记录。
困难来自于远程库提交历史的偏离。在讨论这个问题的细节前,我们先来看一个例子……
本地修改了。远程仓库中该分支也更新了。也就是说本地这个修改之后的节点他的父节点在远程仓库中已经又更新了一个新节点。这个时候git
就不知道把本地这个节点放在哪个位置了。
那该如何解决这个问题呢?
首先 rebase
类似的 git pull --rebase
就是 fetch
和 rebase
的简写!
首先把远程仓库的节点给更新下来。再把我们的新更新的节点放在远程节点的下面。最后再给推送到远程仓库。
或者merge
这个就跟pull
方法是一样的。不先把远程仓库给下载下来。然后再跟本地更新好的新节点。进行合并形成一个最新节点,然后push到远程的仓库。
当然 —— 前面已经介绍过 git pull
就是 fetch
和 merge
的简写
题目:

git clone
git fakeTeamwork
git commit
git pull --rebase
git push
远程服务器拒绝!(Remote Rejected)
如果你是在一个大的合作团队中工作, 很可能是main被锁定了, 需要一些Pull Request流程来合并修改。如果你直接提交(commit)到本地main, 然后试图推送(push)修改, 你将会收到这样类似的信息:
! [远程服务器拒绝] main -> main (TF402455: 不允许推送(push)这个分支; 你必须使用pull request来更新这个分支.)
为什么会被拒绝?
远程服务器拒绝直接推送(push)提交到main, 因为策略配置要求 pull requests 来提交更新.
这里直接把本地仓库得
main
提交到远程仓库的main
。显然这是不符合流程的。正确流程是先建立一个分支。然后去push这个分支,并申请Pull Request
解决办法:
- 新建一个分支
feature
, 推送到远程服务器. - 然后
reset
你的main
分支和远程服务器保持一致, 否则下次你pull
并且他人的提交和你冲突的时候就会有问题.
题目:

git reset o/main
或者
将main分支强制转到o/main
git branch -f main o/main
git checkout -b feature c2
git push origin feature