文章目录

  • 1. 前言
  • 2. 基础篇
    • 2.1 Git Commit
    • 2.2 Git Branch
    • 2.3 Git Merge
    • 2.4 Git Rebase
  • 3. 高级篇
    • 3.1 分离HEAD
    • 3.2 相对引用(一)
    • 3.3 相对引用(二)
    • 3.4 撤销变更
  • 4. 移动提交记录
    • 4.1 Git Cherry-pick
    • 4.2 交互式的 rebase
  • 5. 杂项
    • 5.1 只取一个提交记录
    • 5.2 提交的技巧#1
    • 5.3 提交的技巧#2
    • 5.4 Git Tags
    • 5.5 Git Describe
  • 6. 高级话题
    • 6.1 多次rebase
    • 6.2 两个父结点
    • 6.3 纠缠不清的分支
  • 7. Push & Pull —— Git 远程仓库
    • 7.1 远程仓库
    • 7.2 远程分支
    • 7.3 Git Fetch
    • 7.4 Git Pull
    • 7.5 模拟团队合作
    • 7.6 Git Push
    • 7.7 偏离的任务
  • 8. 关于 origin 和它的周边 —— Git 远程仓库高级操作
    • 8.1 推送分支
    • 8.2 合并远程仓库
    • 8.3 远程追踪
    • 8.4 Git Push的参数(一)
    • 8.5Git Push的参数(二)
    • 8.6 Git Fetch的参数
    • 8.7 没有source的source
    • 8.8 Git Pull参数
  • 9. 后记

1. 前言

最近接触了 Git 后就被它强大的功能吸引了,正好在 GitHub 上发现了一个很不错的在线学习网站:LearningGitBranching,将其通关后,写下了通关的所有答案和笔记心得。

2. 基础篇

2.1 Git Commit

Git 仓库中的提交记录保存的是你的目录下所有文件的快照,就像是把整个目录复制,然后再粘贴一样,但比复制粘贴优雅许多!

Git 希望提交记录尽可能地轻量,因此在你每次进行提交时,它并不会盲目地复制整个目录。条件允许的情况下,它会将当前版本与仓库中的上一个版本进行对比,并把所有的差异打包到一起作为一个提交记录。

Git 还保存了提交的历史记录。这也是为什么大多数提交记录的上面都有父结点的原因 —— 我们会在图示中用箭头来表示这种关系。对于项目组的成员来说,维护提交历史对大家都有好处。

关于提交记录太深入的东西咱们就不再继续探讨了,现在你可以把提交记录看作是项目的快照。提交记录非常轻量,可以快速地在这些提交记录之间切换!


原状态:

当前有两次提交记录C0C1(这里C0C1是该次提交的hash值,用于唯一地标识该次提交),并且当前处于主要分支master*号标识当前分支),头指针指向C1

本关任务:

  • 在当前分支中提交两次修改

这里只需要连续使用两次git commit即可完成提交修改
输入:

  • git commit提交一次修改,新增结点C2
  • git commit提交第二次修改,新增结点C3

2.2 Git Branch

Git 的分支也非常轻量。它们只是简单地指向某个提交纪录 —— 仅此而已。所以许多 Git 爱好者传颂:

早建分支!多用分支!

这是因为即使创建再多分的支也不会造成储存或内存上的开销,并且按逻辑分解工作到不同的分支要比维护那些特别臃肿的分支简单多了。

在将分支和提交记录结合起来后,我们会看到两者如何协作。现在只要记住使用分支其实就相当于在说:“我想基于这个提交以及它所有的父提交进行新的工作。”


原状态:

当前有两次提交记录C0C1,并且当前处于主要分支master,头指针指向C1,然后任务是:

  • 创建一个新的名为bugFix的分支
  • 将当前分支切换到bugFix分支

这里我们分两步完成:

  • 创建分支git branch bugFix
  • 切换分支git checkout bugFix

当然,这里还有一个更加简洁的方法:
git checkout -b bugFix
可以新建分支并直接切换到该bugFix分支

2.3 Git Merge

太好了! 我们已经知道如何提交以及如何使用分支了。接下来咱们看看如何将两个分支合并到一起。就是说我们新建一个分支,在其上开发某个新功能,开发完成后再合并回主线。

咱们先来看一下第一种方法 —— git merge。在 Git 中合并两个分支时会产生一个特殊的提交记录,它有两个父结点。翻译成自然语言相当于:“我要把这两个父结点本身及它们所有的祖先都包含进来。”


原状态:

当前有两次提交记录C0C1,并且当前处于主要分支master,头指针指向C1,然后任务是:

  • 创建新分支 bugFix
  • git checkout bugFix 命令切换到该分支
  • 提交一次
  • git checkout master 切换回 master
  • 再提交一次
  • git mergebugFix 合并到 master

这关也同样十分简单:

  • 创建新分支 git branch bugFix
  • git checkout bugFix 命令切换到该分支
  • git commit提交一次
  • git checkout master 切换回 master
  • git commit再提交一次
  • git merge bugFixbugFix 合并到 master

注意:这里git merge bugFix 是把该分支bugFix合并到当前分支(master)
对于前两步也同样可以使用git checkout -b bugFix一步完成。

2.4 Git Rebase

第二种合并分支的方法是 git rebaseRebase 实际上就是取出一系列的提交记录,“复制”它们,然后在另外一个地方逐个的放下去。

Rebase 的优势就是可以创造更线性的提交历史,这听上去有些难以理解。如果只允许使用 Rebase 的话,代码库的提交历史将会变得异常清晰。


原状态:

当前有两次提交记录C0C1,并且当前处于主要分支master,头指针指向C1,然后任务是:

  • 新建并切换到 bugFix 分支
  • 提交一次
  • 切换回 master 分支再提交一次
  • 再次切换到 bugFix 分支,rebasemaster

这关不难,但是理解git rebase的用法稍微复杂点:

  • git checkout -b bugFix新建并切换到 bugFix 分支
  • git commit提交一次

  • git checkout master切换回 master 分支,然后git commit再提交一次

  • git checkout bugFix再次切换到 bugFix 分支,然后使用git rebase master 使其连接到master分支后。移动以后会使得两个分支的功能看起来像是按顺序开发,但实际上它们是并行开发的。

注意:提交记录 C2 依然存在(树上那个半透明的结点),而 C3’ 是我们 Rebase 到 master 分支上的 C2 的副本。
同时 master 还未更新,这时候应该使用 git checkout master然后git rebase bugFix:

由于 bugFix 继承自 master,所以 Git 只是简单的把 master 分支的引用向前移动了一下而已,即同一分支的rebase 只是将原分支更新到该分支位置。


此外,git base bugFix的意思是,将该bugFix分支rebase到当前分支(master),也就是该bugFix分支作为master的基(父结点),类似于git merge bugFix 把该分支bugFix合并到当前分支(master)

3. 高级篇

3.1 分离HEAD

在接触 Git 更高级功能之前,我们有必要先学习在你项目的提交树上前后移动的几种方法。

一旦熟悉了如何在 Git 提交树上移动,你驾驭其它命令的能力也将水涨船高!

我们首先看一下 “HEAD”。 HEAD 是一个对当前检出记录的符号引用 —— 也就是指向你正在其基础上进行工作的提交记录。

HEAD 总是指向当前分支上最近一次提交记录。大多数修改提交树的 Git 命令都是从改变 HEAD 的指向开始的。

HEAD 通常情况下是指向分支名的(如 master),HEAD 的指向随着master提交向前移动。

分离的 HEAD 就是让其指向了某个具体的提交记录而不是分支名。
比如:
HEAD -> master -> C1

HEAD 指向 master, master 指向 C1

使用git checkout C1分离后变成了:

HEAD -> C1

(实际这些命令并不是真的在查看 HEAD 指向,看下一屏就了解了。如果想看 HEAD 指向,可以通过 cat .git/HEAD 查看, 如果 HEAD 指向的是一个引用,还可以用 git symbolic-ref HEAD 查看它的指向。但是该程序不支持这两个命令)


原状态:

如图,当前任务是:

  • bugFix 分支中分离出 HEAD 并让其指向一个提交记录。

这关只需要一步就能搞定了,关键是理解如何分离HEAD,以及分离后的情况:

  • 使用git checkout C4即可将HEAD指针指向C4(注意原本是指向master
  • 或者使用另一个方法:git checkout C3 进入 bugFix 分支之前的位置,然后git rebase bugFix将当前master分支 rebasebugFix下(该方法稍许麻烦,但可以更加深入理解rebase

3.2 相对引用(一)

通过指定提交记录哈希值的方式在 Git 中移动不太方便。在实际应用时,并没有像本程序中这么漂亮的可视化提交树供你参考,所以你就不得不用 git log 来查查看提交记录的哈希值。

并且哈希值在真实的 Git 世界中也会更长(基于 SHA-1,共 40 位)。例如前一关的介绍中的提交记录的哈希值可能是 fed2da64c0efc5293610bdd892f82a58e8cbc5d8。舌头都快打结了吧…

比较令人欣慰的是,Git 对哈希的处理很智能。你只需要提供能够唯一标识提交记录的前几个字符即可。因此我可以仅输入fed2 而不是上面的一长串字符。

正如前面所说,通过哈希值指定提交记录很不方便,所以 Git 引入了相对引用。这个就很厉害了!

使用相对引用的话,你就可以从一个易于记忆的地方(比如 bugFix 分支或 HEAD)开始计算。

相对引用非常给力,这里我介绍两个简单的用法:

  • 使用 ^ 向上移动 1 个提交记录
  • 使用 ~<num> 向上移动多个提交记录,如 ~3

本关任务是:

  • 切换到 bugFix 的父结点。这会进入分离 HEAD 状态。

解决方案:

  • 使用git checkout bugFix切换到 bugFix分支所在处C4,(同时HEAD也会切换到 C4 处),然后使用git checkout HEAD^回退到C3

你甚至可以直接使用git checkout C3,但是这不是本关卡想要看到的方法。

3.3 相对引用(二)

~操作符

如果你想在提交树中向上移动很多步的话,敲那么多 ^ 貌似也挺烦人的,Git 当然也考虑到了这一点,于是又引入了操作符 ~

该操作符后面可以跟一个数字(可选,不跟数字时与 ^ 相同,向上移动一次),指定向上移动多少次。

另一个重点就是:强制修改分支位置

使用相对引用最多的就是移动分支。可以直接使用 -f 选项让分支指向另一个提交。例如:

git branch -f master HEAD~3

上面的命令会将 master 分支强制指向 HEAD 的第 3 级父提交。

相对引用为我们提供了一种简洁的引用提交记录的方式, 而 -f 则容许我们将分支强制移动到那个位置。


原状态:

这里可以看到,masterbugFix两个分支处于不同位置,同时HEAD亦处于master的父结点所在处,并且还有一个回退过的结点C6。本关任务是:

  • 移动 HEAD,masterbugFix 到目标所示的位置。

解决方案如下:

  • git branch -f master C6使得master移动到C6位置

  • git branch -f bugFix HEAD~2使得bugFix移动到HEAD的二级父结点C0

  • git checkout C1使得HEAD结点移动到C1

3.4 撤销变更

在 Git 里撤销变更的方法很多。和提交一样,撤销变更由底层部分(暂存区的独立文件或者片段)和上层部分(变更到底是通过哪种方式被撤销的)组成。我们这个应用主要关注的是后者。

主要有两种方法用来撤销变更 —— 一是 git reset,还有就是 git revert。接下来咱们逐个进行讲解。

  • git reset 通过把当前分支记录回退几个提交记录来实现撤销改动。你可以将这想象成“改写历史”。git reset 向上移动分支,原来指向的提交记录就跟从来没有提交过一样。

虽然在你的本地分支中使用 git reset 很方便,但是这种“改写历史”的方法对大家一起使用的远程分支是无效的哦!

  • git revert 通过把当前分支在我们要撤销的提交记录后面增加一个新提交,使得新提交记录引入了更改,这些更改刚好是用来撤销原先的提交的,revert 之后就可以把你的更改推送到远程仓库与别人分享啦。

原状态:

当前分支位local。其中,pushed是远程分支,local是本地分支
当前任务:

  • 分别撤销 local 分支和 pushed 分支上的最近一次提交。共需要撤销两个提交(每个分支一个)。

解决方案:

  • 首先使用git reset HEAD^local回退到C1(因为当前分支是local,HEAD也处于该位置)

  • git checkout pushed进入pushed分支,然后git revert HEAD增加一个新的提交

注意:reset是回退,回退的位置是相对于HEAD的位置,而revert则是在当前分支所处位置点后新增一个新的提交,也就是将需要回退到的位置复制后加入到当前分支后。虽然二者都是回退到某个位置,但使用的方法不一样。

4. 移动提交记录

4.1 Git Cherry-pick

到现在我们已经学习了 Git 的基础知识 —— 提交、分支以及在提交树上移动。 这些概念涵盖了 Git 90% 的功能,同样也足够满足开发者的日常需求

然而, 剩余的 10% 在处理复杂的工作流时(或者当你陷入困惑时)可能就显得尤为重要了。接下来要讨论的这个话题是“整理提交记录” —— 开发人员有时会说“我想要把这个提交放到这里, 那个提交放到刚才那个提交的后面”, 而接下来就讲的就是它的实现方式,非常清晰、灵活,还很生动。

看起来挺复杂, 其实是个很简单的概念。

本系列的第一个命令是 git cherry-pick, 命令形式为:

git cherry-pick <提交号>...

如果你想将一些提交复制到当前所在的位置(HEAD)下面的话, Cherry-pick 是最直接的方式了。我个人非常喜欢 cherry-pick,因为它特别简单。


原状态:

如图,本关有多个分支,当前分支是master
当前任务:

  • 将三个分支中的提交记录复制到 master

解决方案:

  • git cherry-pick C3 C4 C7将三个点按顺序接到master分支所处位置后面,移动后master自动会更新到最新的结点处

4.2 交互式的 rebase

当你知道你所需要的提交记录(并且还知道这些提交记录的哈希值)时, 用 cherry-pick 再好不过了 —— 没有比这更简单的方式了。

但是如果你不清楚你想要的提交记录的哈希值呢? 幸好 Git 帮你想到了这一点, 我们可以利用交互式的 rebase —— 如果你想从一系列的提交记录中找到想要的记录, 这就是最好的方法了

交互式 rebase 指的是使用带参数 --interactiverebase 命令, 简写为 -i

如果你在命令后增加了这个选项, Git 会打开一个 UI 界面并列出将要被复制到目标分支的备选提交记录,它还会显示每个提交记录的哈希值和提交说明,提交说明有助于你理解这个提交进行了哪些更改。

在实际使用时,所谓的 UI 窗口一般会在文本编辑器 —— 如 Vim —— 中打开一个文件。

当 rebase UI界面打开时, 你能做3件事:

  • 调整提交记录的顺序(通过鼠标拖放来完成)
  • 删除你不想要的提交(通过切换 pick 的状态来完成,关闭就意味着你不想要这个提交记录)
  • 合并提交。 遗憾的是由于某种逻辑的原因,该课程不支持此功能,因此我不会详细介绍这个操作。简而言之,它允许你把多个提交记录合并成一个。

原状态:

当前分支处于master,任务如下:

  • 做一次交互式的 rebase,整理成目标窗口中的提交顺序

解决方案:

  • 首先使用git rebase -i HEAD~4打开交互式窗口,注意这里HEAD~4指的是从HEAD往前数(包括HEAD)共计4个结点

  • 将我们不需要的结点点击Omit删除,并且拖动鼠标移动位置,最后确认

5. 杂项

5.1 只取一个提交记录

来看一个在开发中经常会遇到的情况:我正在解决某个特别棘手的 Bug,为了便于调试而在代码中添加了一些调试命令并向控制台打印了一些信息。

这些调试和打印语句都在它们各自的提交记录里。最后我终于找到了造成这个 Bug 的根本原因,解决掉以后觉得沾沾自喜!

最后就差把 bugFix 分支里的工作合并回 master 分支了。你可以选择通过 fast-forward 快速合并到 master 分支上,但这样的话 master 分支就会包含我这些调试语句了。你肯定不想这样,应该还有更好的方式……

实际我们只要让 Git 复制解决问题的那一个提交记录就可以了。跟之前我们在“整理提交记录”中学到的一样,我们可以使用

    git rebase -igit cherry-pick

来达到目的。


原状态:

该图意思是:我们在master分支后新建了几个分支用于调试代码,最后我们想要将结果接到master分支后面,不需要中间的调试内容。

本关任务:

  • 得到如下所示的图。本关与上一关类似,但是需要确保 master 分支能得到 bugFix 分支上的相关提交。

解决方案:

  • 首先使得当前分支为master所在位置:git checkout master
  • 然后使用git cherry-pick C4,使得复制一个新的C4'结点接到master

5.2 提交的技巧#1

接下来这种情况也是很常见的:你之前在 newImage 分支上进行了一次提交,然后又基于它创建了 caption 分支,然后又提交了一次。

此时你想对的某个以前的提交记录进行一些小小的调整。比如设计师想修改一下 newImage 中图片的分辨率,尽管那个提交记录并不是最新的了。

我们可以通过下面的方法来克服困难:

  • 先用 git rebase -i 将提交重新排序,然后把我们想要修改的提交记录挪到最前
  • 然后用 git commit --amend 来进行一些小修改
  • 接着再用 git rebase -i 来将他们调回原来的顺序
  • 最后我们把 master 移到修改的最前端(用你自己喜欢的方法),就大功告成啦!

当然完成这个任务的方法不止上面提到的一种(我知道你在看 cherry-pick 啦),之后我们会多点关注这些技巧啦,但现在暂时只专注上面这种方法。 最后有必要说明一下目标状态中的那几个' —— 我们把这个提交移动了两次,每移动一次会产生一个 ';而 C2 上多出来的那个是我们在使用了 amend 参数提交时产生的,所以最终结果就是这样了。

也就是说,在对比结果的时候只会对比提交树的结构,对于 ' 的数量上的不同,并不纳入对比范围内。只要你的 master 分支结构与目标结构相同,就算你通过。


原状态:

本关任务:

解决方案如下:

  • 首先git rebase -i HEAD~2交换C2C3的位置,顺序调整如下:

  • 然后git commit --amend修改当前分支所在处(C2'),这句命令主要是将当前结点C2'的父结点再分出一个修改后的子结点C2''

  • 然后使用git rebase -i HEAD~2调回顺序

  • 最后更新master: 需要先使用git checkout master将当前分支切回master,然后git rebase caption更新master,完成任务

5.3 提交的技巧#2

正如你在上一关所见到的,我们可以使用 rebase -i 对提交记录进行重新排序。只要把我们想要的提交记录挪到最前端,我们就可以很轻松的用 --amend 修改它,然后把它们重新排成我们想要的顺序。

但这样做就唯一的问题就是要进行两次排序,而这有可能造成由 rebase 而导致的冲突。下面还是看看 git cherry-pick 是怎么做的吧。要在心里牢记 cherry-pick 可以将提交树上任何地方的提交记录取过来追加到 HEAD 上(只要不是 HEAD 上游的提交就没问题)。


原状态:

这一关的目标和上一关一样,通过 --amend改变提交记录 C2,但你不能用 rebase -i
目标:

解决方案:

  • 首先进入master分支所在处:git checkout master
  • 然后使用git cherry-pick newImageC2接到C1后面

  • 使用git commit --amend创造新结点C2''

  • 最后使用git cherry-pick captionC3加至C2''后面,大功告成

5.4 Git Tags

相信通过前面课程的学习你已经发现了:分支很容易被人为移动,并且当有新的提交时,它也会移动。分支很容易被改变,大部分分支还只是临时的,并且还一直在变。

你可能会问了:有没有什么可以永远指向某个提交记录的标识呢,比如软件发布新的大版本,或者是修正一些重要的 Bug 或是增加了某些新特性,有没有比分支更好的可以永远指向这些提交的方法呢?

当然有了!Git 的 tag 就是干这个用的啊,它们可以(在某种程度上 —— 因为标签可以被删除后重新在另外一个位置创建同名的标签)永久地将某个特定的提交命名为里程碑,然后就可以像分支一样引用了。

更难得的是,它们并不会随着新的提交而移动。你也不能检出到某个标签上面进行修改提交,它就像是提交树上的一个锚点,标识了某个特定的位置。

我们将这个标签命名为 v1,并且明确地让它指向提交记录 C1,如果你不指定提交记录,Git 会用 HEAD 所指向的位置。


原状态:

目标:

解决方案:

  • 使用如下语句添加tag
git tag v0 C1
git tag v1 C2

  • 最后分离HEAD到C2上:git checkout C2

5.5 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 是表示这个 reftag 相差有多少个提交记录, hash 表示的是你所给定的 ref 所表示的提交记录哈希值的前几位。

ref 提交记录上有某个标签时,则只输出标签名称。

例子如下:

git describe master 会输出:

    v1_2_gC2

git describe side会输出:

    v2_1_gC4

6. 高级话题

6.1 多次rebase

这一关有很多分支!咱们把这些分支 rebasemaster 上吧。

但是你的领导给你提了点要求 —— 他们希望得到有序的提交历史,也就是我们最终的结果应该是 C6'C7' 上面, C5'C6' 上面,依此类推。

即使搞砸了也没关系,用 reset 命令就可以重新开始了。


原状态:

目标:

十分复杂!!
解决方案:

  • 首先将bugFix分支接到master分支后,使用如下命令
    git checkout bugFixgit rebase master

  • 然后将side分支接到bugFix分支后面
    git checkout sidegit rebase bugFix

  • 最后,将another分支接到side分支后面,并且更新master分支
    git checkout anothergit rebase sidegit checkout mastergit rebase another

总结:通过这关,更好的理解了rebase的作用,中文名译为变基,我的通俗理解git rebase [branch]就是将当前分支接到 branch之后,而具体是从哪一个结点开始断开,主要是根据当前分支与branch之间开始出现分支的结点算起。

6.2 两个父结点

操作符 ^~符一样,后面也可以跟一个数字。

但是该操作符后面的数字与 ~ 后面的不同,并不是用来指定向上返回几代,而是指定合并提交记录的某个父提交。还记得前面提到过的一个合并提交有两个父提交吧,所以遇到这样的节点时该选择哪条路径就不是很清晰了。

Git 默认选择合并提交的“第一个”父提交,在操作符 ^ 后跟一个数字可以改变这一默认行为。


本关任务:在指定的目标位置创建一个新的分支。
原状态:

目标:

解决方案:

  • 直接使用链式相对引用git checkout master^^2~依次回到master分支的父节点C6C6第二父节点C5(正上方是第一父节点)、C5父节点C2

  • 然后使用git branch bugWork在HEAD处新建分支

  • 最后git checkout master回归master分支

6.3 纠缠不清的分支

哇塞大神!这关我们要来点不同的!

现在我们的 master 分支是比 onetwothree 要多几个提交。出于某种原因,我们需要把 master 分支上最近的几次提交做不同的调整后,分别添加到各个的分支上。

本关任务:one 需要重新排序并删除 C5two 仅需要重排排序,而 three 只需要提交一次。
原状态:

目标:

解决方案:

  • 先做最简单的分支three
    git checkout threegit rebase C2

  • 然后做one分支
    git checkout onegit cherry-pick C4 C3 C2

  • 最后解决two分支
    git checkout twogit cherry-pick C5 C4 C3 C2

7. Push & Pull —— Git 远程仓库

7.1 远程仓库

远程仓库并不复杂, 在如今的云计算盛行的世界很容易把远程仓库想象成一个富有魔力的东西, 但实际上它们只是你的仓库在另个一台计算机上的拷贝。你可以通过因特网与这台计算机通信 —— 也就是增加或是获取提交记录

话虽如此, 远程仓库却有一系列强大的特性

首先也是最重要的的点, 远程仓库是一个强大的备份。本地仓库也有恢复文件到指定版本的能力, 但所有的信息都是保存在本地的。有了远程仓库以后,即使丢失了本地所有数据, 你仍可以通过远程仓库拿回你丢失的数据。

还有就是, 远程让代码社交化了! 既然你的项目被托管到别的地方了, 你的朋友可以更容易地为你的项目做贡献(或者拉取最新的变更)

现在用网站来对远程仓库进行可视化操作变得越发流行了(像 Github 或 Phabricator), 但远程仓库永远是这些工具的顶梁柱, 因此理解其概念非常的重要!

直到现在, 教程都聚焦于本地仓库的操作(branchmergerebase 等等)。但我们现在需要学习远程仓库的操作 —— 我们需要一个配置这种环境的命令, 它就是 git clone。 从技术上来讲,git clone 命令在真实的环境下的作用是在本地创建一个远程仓库的拷贝(比如从 github.com)。 但在我们的教程中使用这个命令会有一些不同 —— 它会在远程创建一个你本地仓库的副本。显然这和真实命令的意思刚好相反,但是它帮咱们把本地仓库和远程仓库关联到了一起,在教程中就凑合着用吧。


本关只需要使用git clone命令将本地仓库复制到远程仓库即可

7.2 远程分支

既然你已经看过 git clone 命令了,咱们深入地看一下发生了什么。

你可能注意到的第一个事就是在我们的本地仓库多了一个名为 o/master 的分支, 这种类型的分支就叫远程分支。由于远程分支的特性导致其拥有一些特殊属性。

远程分支反映了远程仓库(在你上次和它通信时)的状态。这会有助于你理解本地的工作与公共工作的差别 —— 这是你与别人分享工作成果前至关重要的一步.

远程分支有一个特别的属性,在你检出时自动进入分离 HEAD 状态。Git 这么做是出于不能直接在这些分支上进行操作的原因, 你必须在别的地方完成你的工作, (更新了远程分支之后)再用远程分享你的工作成果。

你可能想问这些远程分支的前面的 o/ 是什么意思呢?好吧, 远程分支有一个命名规范 —— 它们的格式是:

<remote name>/<branch name>

因此,如果你看到一个名为 o/master 的分支,那么这个分支就叫 master,远程仓库的名称就是 o

大多数的开发人员会将它们主要的远程仓库命名为 origin,并不是 o。这是因为当你用 git clone 某个仓库时,Git 已经帮你把远程仓库的名称设置为 origin

不过 origin 对于我们的 UI 来说太长了,因此不得不使用简写 o ? 但是要记住, 当你使用真正的 Git 时, 你的远程仓库默认为 origin!

当你使用

    git checkout o/mastergit commit


Git 变成了分离 HEAD 状态,当添加新的提交时 o/master 也不会更新。这是因为 o/master 只有在远程仓库中相应的分支更新了以后才会更新。

本关任务:

  • master 分支上做一次提交;然后检出 o/master,再做一次提交。

原状态:

解决方案:

    git commitgit checkout o/mastergit commit


可以看到,当切换回o/master的时候在git commit,只会分离HEAD,o/master并不会更新

7.3 Git Fetch

Git 远程仓库相当的操作实际可以归纳为两点:向远程仓库传输数据以及从远程仓库获取数据。既然我们能与远程仓库同步,那么就可以分享任何能被 Git 管理的更新(因此可以分享代码、文件、想法、情书等等)。

本节课我们将学习如何从远程仓库获取数据 —— 命令如其名,它就是 git fetch

你会看到当我们从远程仓库获取数据时, 远程分支也会更新以反映最新的远程仓库。

git fetch 完成了仅有的但是很重要的两步:

  • 从远程仓库下载本地仓库中缺失的提交记录
  • 更新远程分支指针(如 o/master)

git fetch 实际上将本地仓库中的远程分支更新成了远程仓库相应分支最新的状态。

如果你还记得上一节课程中我们说过的,远程分支反映了远程仓库在你最后一次与它通信时的状态,git fetch 就是你与远程仓库通信的方式了!希望我说的够明白了,你已经了解 git fetch 与远程分支之间的关系了吧。

git fetch 通常通过互联网(使用 http://git:// 协议) 与远程仓库通信。

但是:

git fetch并不会改变你本地仓库的状态。它不会更新你的 master 分支,也不会修改你磁盘上的文件。

理解这一点很重要,因为许多开发人员误以为执行了 git fetch以后,他们本地仓库就与远程仓库同步了。它可能已经将进行这一操作所需的所有数据都下载了下来,但是并没有修改你本地的文件。我们在后面的课程中将会讲解能完成该操作的命令 ?

所以, 你可以将 git fetch的理解为单纯的下载操作。


本关任务:使用git fetch更新本地的远程分支
原状态:

使用git fetch后:

可以看到,本地的远程分支(带o/开头的)都更新了,但是本地分支并不会更新。

7.4 Git Pull

既然我们已经知道了如何用 git fetch 获取远程的数据, 现在我们学习如何将这些变化更新到我们的工作当中。

其实有很多方法的 —— 当远程分支中有新的提交时,你可以像合并本地分支那样来合并远程分支。也就是说就是你可以执行以下命令:

    git cherry-pick o/mastergit rebase o/mastergit merge o/master

等等…

实际上,由于先抓取更新再合并到本地分支这个流程很常用,因此 Git 提供了一个专门的命令来完成这两个操作。它就是我们要讲的 git pull

先看看fetchmerge依次执行的结果:
原状态:

使用如下命令后:

    git fetchgit merge o/master

得到如图:

我们用 fetch 下载了 C3, 然后通过 git merge o/master 合并了这一提交记录。现在我们的 master 分支包含了远程仓库中的更新(在本例中远程仓库名为 origin

而使用git pull则一步就能得到同样的结果!

这清楚地说明了 git pull 就是 git fetchgit merge <just-fetched-branch> 的缩写!

7.5 模拟团队合作

这里有一件棘手的事 —— 为了接下来的课程, 我们需要先教你如何制造远程仓库的变更。

这意味着,我们需要“假装”你的同事、朋友、合作伙伴更新了远程仓库,有可能是某个特定的分支,或是几个提交记录。

为了做到这点,我们引入一个自造命令 git fakeTeamwork!它的名称已经说明了一切,先看演示…

接下来的关卡会相当的困难,所以在本关会让你做许多事情,先来热热身。

原状态:

本关任务是:

  • 克隆一个远程仓库
  • 在刚创建的远程仓库中模拟一些修改
  • 在你自己的本地分支上做一些提交
  • 拉取远程仓库的变更
    解决方案:
  • 克隆一个远程仓库:git clone
  • 在远程仓库主要分支master新增三个修改C2 C3git fakeTeamwork 2

  • 使用git commit在本地分支上做一些提交

  • 拉取远程仓库的变更git pull

7.6 Git Push

OK,我们已经学过了如何从远程仓库获取更新并合并到本地的分支当中。这非常棒……但是我如何与大家分享我的成果呢?

嗯,上传自己分享内容与下载他人的分享刚好相反,那与 git pull 相反的命令是什么呢?git push

git push 负责将你的变更上传到指定的远程仓库,并在远程仓库上合并你的新提交记录。一旦 git push 完成, 你的朋友们就可以从这个远程仓库下载你分享的成果了!

你可以将 git push 想象成发布你成果的命令。它有许多应用技巧,稍后我们会了解到,但是咱们还是先从基础的开始吧……

注意 —— git push 不带任何参数时的行为与 Git 的一个名为 push.default 的配置有关。它的默认值取决于你正使用的 Git 的版本,但是在教程中我们使用的是 upstream。 这没什么太大的影响,但是在你的项目中进行推送之前,最好检查一下这个配置。


原状态:

本关任务:

  • 向远程仓库分享两个提交记录

解决方案:

  • 两次git commit提交两次修改C2 C3
  • 向远程仓库分享提交记录

7.7 偏离的任务

现在我们已经知道了如何从其它地方 pull 提交记录,以及如何 push 我们自己的变更。看起来似乎没什么难度,但是为何还会让人们如此困惑呢?困难来自于远程库提交历史的偏离。

假设你周一克隆了一个仓库,然后开始研发某个新功能。到周五时,你新功能开发测试完毕,可以发布了。但是 —— 天啊!你的同事这周写了一堆代码,还改了许多你的功能中使用的 API,这些变动会导致你新开发的功能变得不可用。但是他们已经将那些提交推送到远程仓库了,因此你的工作就变成了基于项目旧版的代码,与远程仓库最新的代码不匹配了。

这种情况下, git push 就不知道该如何操作了。如果你执行 git push,Git 应该让远程仓库回到星期一那天的状态吗?还是直接在新代码的基础上添加你的代码,异或由于你的提交已经过时而直接忽略你的提交?

因为这情况(历史偏离)有许多的不确定性,Git 是不会允许你 push 变更的。实际上它会强制你先合并远程最新的代码,然后才能分享你的工作。
例如下图:

使用git push后,什么都没有变,因为命令失败了!git push 失败是因为你最新提交的 C3 基于远程分支中的 C1。而远程仓库中该分支已经更新到 C2了,所以 Git 拒绝了你的推送请求。

那该如何解决这个问题呢?很简单,你需要做的就是使你的工作基于最新的远程分支。

有许多方法做到这一点呢,不过最直接的方法就是通过 rebase 调整你的工作。咱们继续,看看怎么 rebase

应该用:

    git fetchgit rebase o/mastergit push

我们用 git fetch 更新了本地仓库中的远程分支,然后用 rebase 将工们的工作移动到最新的提交记录下,最后再用 git push 推送到远程仓库。
结果如下:

还有其它的方法可以在远程仓库变更了以后更新我的工作吗? 当然有,我们还可以使用 merge

尽管 git merge 不会移动你的工作(它会创建新的合并提交),但是它会告诉 Git 你已经合并了远程仓库的所有变更。这是因为远程分支现在是你本地分支的祖先,也就是说你的提交已经包含了远程分支的所有变化。

咱们们用 merge 替换 rebase 来试一下:

    git fetchgit merge o/mastergit push

我们用 git fetch 更新了本地仓库中的远程分支,然后合并了新变更到我们的本地分支(为了包含远程仓库的变更),最后我们用 git push 把工作推送到远程仓库。
结果如下:

很好!但是要敲那么多命令,有没有更简单一点的?

当然 —— 前面已经介绍过 git pull 就是 fetchmerge 的简写,类似的 git pull --rebase 就是 fetchrebase 的简写!

这次用 --rebase

    git pull --rebasegit push

与之前第一次使用rebase的结果一样,但是命令更短了!

换用常规的pull:

    git pullgit push

与第二次使用merge的结果一样!

fetchrebase/mergepush 组成的工作流很普遍。后续课程我们会讲解更复杂的工作流,不过现在我们先解决这个关卡吧。


原状态:

本关任务:

  • 克隆你的仓库
  • 模拟一次远程提交(fakeTeamwork
  • 完成一次本地提交
  • rebase 发布你的工作

解决方案:

  • 克隆仓库git clone
  • 模拟一次远程提交git fakeTeamwork
  • 完成一次本地提交git commit

  • 先获取远程仓库的更新并更新本地分支mastergit pull --rebase
    然后发布工作:git push

8. 关于 origin 和它的周边 —— Git 远程仓库高级操作

8.1 推送分支

既然你应该很熟悉 fetchpullpush 了,现在我们要通过一个新的工作流来测试你的这些技能。

在大型项目中开发人员通常会在(从 master 上分出来的)特性分支上工作,工作完成后只做一次集成。这跟前面课程的描述很相像(把 side 分支推送到远程仓库),不过本节我们会深入一些.

但是有些开发人员只在 master 上做 pushpull —— 这样的话 master 总是最新的,始终与远程分支 (o/master) 保持一致。

对于接下来这个工作流,我们集成了两个步骤:

  • 将特性分支集成到 master
  • 推送并更新远程分支

让我们看看如何快速的更新 master 分支并推送到远程。

    git pull --rebasegit push


我们执行了两个命令:

  • 将我们的工作 rebase 到远程分支的最新提交记录
  • 向远程仓库推送我们的工作

这个关卡的 Boss 很厉害 —— 以下是通关提示:

  • 这里共有三个特性分支 —— side1 side2side3
  • 我需要将这三分支按顺序推送到远程仓库
  • 因为远程仓库已经被更新过了,所以我们还要把那些工作合并过来

? 紧张了?祝你好运!完成了本关, 你就向目标又迈近了一大步啦!


原状态:

目标:

本关任务:

  • 将三分支按顺序推送到远程仓库
  • 合并远程仓库的工作

解决方案:

  • 更新当前的o/master分支为远程仓库的更新
    git checkout mastergit pull --rebase

  • side1更新到master分支下
    git checkout side1git rebase o/master

  • side2更新到side1分支下
    git checkout side2git rebase side1

  • side3更新到side2分支下
    git checkout side3git rebase side2

  • 最后先将master更新到最新,然后push到远端
    git rebase side3git push

8.2 合并远程仓库

为了 push 新变更到远程仓库,你要做的就是包含远程仓库中最新变更。意思就是只要你的本地分支包含了远程分支(如 o/master)中的最新变更就可以了,至于具体是用 rebase 还是 merge,并没有限制。

那么既然没有规定限制,为何前面几节都在着重于 rebase 呢?为什么在操作远程分支时不喜欢用 merge 呢?

在开发社区里,有许多关于 mergerebase 的讨论。以下是关于 rebase 的优缺点:

优点:

  • Rebase 使你的提交树变得很干净, 所有的提交都在一条线上

缺点:

  • Rebase 修改了提交树的历史

比如, 提交 C1 可以被 rebaseC3 之后。这看起来 C1 中的工作是在 C3 之后进行的,但实际上是在 C3 之前。

一些开发人员喜欢保留提交历史,因此更偏爱 merge。而其他人(比如我自己)可能更喜欢干净的提交树,于是偏爱 rebase。仁者见仁,智者见智。 ?

本关,我们还是解决上一关卡中的问题,但是要用 merge 替换 rebase。这显然有点画蛇添足,但这只是为了更好的说明上面的观点。


原状态:

目标:

这也太复杂了吧!
解决方案:

  • 首先从远程更新master分支:
    git checkout mastergit pull --rebase

  • 然后合并side1git merge side1

  • 合并side2git merge side2

  • 合并side3git merge side3

  • 最后将更新推送到远端:git push

8.3 远程追踪

在前几节课程中有件事儿挺神奇的,Git 好像知道 mastero/master 是相关的。当然这些分支的名字是相似的,可能会让你觉得是依此将远程分支 master 和本地的 master 分支进行了关联。这种关联在以下两种情况下可以清楚地得到展示:

  • pull 操作时, 提交记录会被先下载到 o/master 上,之后再合并到本地的 master 分支。隐含的合并目标由这个关联确定的。
  • push 操作时, 我们把工作从 master 推到远程仓库中的 master 分支(同时会更新远程分支 o/master) 。这个推送的目的地也是由这种关联确定的!

直接了当地讲,master 和 o/master 的关联关系就是由分支的“remote tracking”属性决定的。master 被设定为跟踪 o/master —— 这意味着为 master 分支指定了推送的目的地以及拉取后合并的目标。

你可能想知道 master 分支上这个属性是怎么被设定的,你并没有用任何命令指定过这个属性呀!好吧, 当你克隆仓库的时候, Git 就自动帮你把这个属性设置好了。

当你克隆时, Git 会为远程仓库中的每个分支在本地仓库中创建一个远程分支(比如 o/master)。然后再创建一个跟踪远程仓库中活动分支的本地分支,默认情况下这个本地分支会被命名为 master。

克隆完成后,你会得到一个本地分支(如果没有这个本地分支的话,你的目录就是“空白”的),但是可以查看远程仓库中所有的分支(如果你好奇心很强的话)。这样做对于本地仓库和远程仓库来说,都是最佳选择。

这也解释了为什么会在克隆的时候会看到下面的输出:

    local branch "master" set to track remote branch "o/master"

我能自己指定这个属性吗?

当然可以啦!你可以让任意分支跟踪 o/master, 然后该分支会像 master 分支一样得到隐含的 push 目的地以及 merge 的目标。 这意味着你可以在分支 totallyNotMaster 上执行 git push,将工作推送到远程仓库的 master 分支上。

有两种方法设置这个属性,

第一种就是通过远程分支检出一个新的分支,执行:

git checkout -b totallyNotMaster o/master

就可以创建一个名为 totallyNotMaster 的分支,它跟踪远程分支 o/master

第二种方法:

另一种设置远程追踪分支的方法就是使用:git branch -u 命令,执行:

    git branch -u o/master foo

这样 foo 就会跟踪 o/master 了。如果当前就在 foo 分支上, 还可以省略 foo

    git branch -u o/master

OK! 本节我们在不检出 master 分支的情况下将工作推送到的远程仓库中的 master 分支上。因为这是高级课程, 就不做过多的提示了! ?


原状态:

目标:

本关任务是:

  • 在不检出 master 分支的情况下将工作推送到的远程仓库中的 master 分支上

解决方案:

  • 新建一个分支side,它跟踪远程分支 o/master,并提交一次
    git checkout -b side o/mastergit commit

  • 获取更新并接到 master 分支后
    git pull --rebase

  • 最后使用git push更新到远端

8.4 Git Push的参数(一)

很好! 既然你知道了远程跟踪分支,我们可以开始揭开 git pushfetchpull 的神秘面纱了。我们会逐个介绍这几个命令,它们在理念上是非常相似的。

首先来看 git push。在远程跟踪课程中,你已经学到了 Git 是通过当前检出分支的属性来确定远程仓库以及要 push 的目的地的。这是未指定参数时的行为,我们可以为 push 指定参数,语法是:

    git push <remote> <place>

<place> 参数是什么意思呢?我们稍后会深入其中的细节, 先看看例子, 这个命令是:

    git push origin master

把这个命令翻译过来就是:

切到本地仓库中的“master”分支,获取所有的提交,再到远程仓库“origin”中找到“master”分支,将远程仓库中没有的提交记录都添加上去,搞定之后告诉我。

我们通过“place”参数来告诉 Git 提交记录来自于 master, 要推送到远程仓库中的 master。它实际就是要同步的两个仓库的位置。

需要注意的是,因为我们通过指定参数告诉了 Git 所有它需要的信息, 所以它就忽略了我们所检出的分支的属性!

本关我们要更新远程仓库中的 foomaster, 但是 git checkout 被禁用了!

注意:远程分支使用 o/ 开头是因为 origin/ 对于 UI 来说太长了。不用太在意这个,直接用 origin 作为远程仓库的名称就可以了。


原状态:

目标:


本关任务就是在HEAD不处于某分支上的时候更新该分支
解决方案:

  • git push origin master更新远程origin仓库的master分支

  • git push origin foo更新远程origin仓库的foo分支

8.5Git Push的参数(二)

<place>参数详解

还记得之前课程说的吧,当为 git push 指定 place 参数为 master 时,我们同时指定了提交记录的来源和去向。

你可能想问 —— 如果来源和去向分支的名称不同呢?比如你想把本地的 foo 分支推送到远程仓库中的 bar 分支。

哎,很遗憾 Git 做不到…… 开个玩笑,别当真!当然是可以的啦 ? Git 拥有超强的灵活性(有点过于灵活了)

要同时为源和目的地指定 <place> 的话,只需要用冒号 : 将二者连起来就可以了:

    git push origin <source>:<destination>

这个参数实际的值是个 refspec,“refspec” 是一个自造的词,意思是 Git 能识别的位置(比如分支 foo 或者 HEAD~1

一旦你指定了独立的来源和目的地,就可组织出言简意赅的远程操作命令了,记住,source 可以是任何 Git 能识别的位置:

执行如下命令:将本地分支foo的父亲结点推送到远程仓库originmaster分支上:

    git push origin foo^:master

这是个另人困惑的命令,但是它确实是可以运行的 —— Git 将 foo^ 解析为一个位置,上传所有未被包含到远程仓库里 master 分支中的提交记录。

如果你要推送到的目的分支不存在会怎么样呢?没问题!Git 会在远程仓库中根据你提供的名称帮你创建这个分支!


原状态

目标:

本关任务:

  • 使用带参数的git push推送分支到远程仓库

解决方案:

  • 首先:git push origin foo:master使用本地的foo更新远程仓库的master分支

  • 然后:git push origin C5:foo使用本地的C5结点更新远程仓库的foo分支

8.6 Git Fetch的参数

我们刚学习了 git push 的参数,很酷的 <place> 参数,还有用冒号分隔的 refspecs(<source>:<destination>)。 这些参数可以用于 git fetch 吗?

你猜中了!git fetch 的参数和 git push 极其相似。他们的概念是相同的,只是方向相反罢了(因为现在你是下载,而非上传)

让我们逐个讨论下这些概念……

<place> 参数

如果你像如下命令这样为 git fetch 设置 <place> 的话:

git fetch origin foo

Git 会到远程仓库的 foo 分支上,然后获取所有本地不存在的提交,放到本地的 o/foo 上。

看个例子:

使用git fetch origin foo

我们只下载了远程仓库中 foo 分支中的最新提交记录,并更新了 o/foo

你可能会好奇 —— 为何 Git 会将新提交放到 o/foo 而不是放到我本地的 foo 分支呢?之前不是说这样的 <place> 参数就是同时应用于本地和远程的位置吗?

好吧, 本例中 Git 做了一些特殊处理,因为你可能在 foo 分支上的工作还未完成,你也不想弄乱它。还记得在 git fetch 课程里我们讲到的吗 —— 它不会更新你的本地的非远程分支, 只是下载提交记录(这样, 你就可以对远程分支进行检查或者合并了)。

“如果我们指定 <source>:<destination> 会发生什么呢?”

如果你觉得直接更新本地分支很爽,那你就用冒号分隔的 refspec 吧。不过,你不能在当前检出的分支上干这个事,但是其它分支是可以的。

这里有一点是需要注意的 —— source 现在指的是远程仓库中的位置,而 <destination> 才是要放置提交的本地仓库的位置。它与 git push 刚好相反,这是可以讲的通的,因为我们在往相反的方向传送数据。

理论上虽然行的通,但开发人员很少这么做。我在这里介绍它主要是为了从概念上说明 fetchpush 的相似性,只是方向相反罢了。

看个疯狂的例子:

使用:git fetch origin foo~1:bar

哇! 看见了吧, Git 将 foo~1 解析成一个 origin 仓库的位置,然后将那些提交记录下载到了本地的 bar 分支(一个本地分支)上。注意由于我们指定了目标分支,fooo/foo 都没有被更新。

如果执行命令前目标分支不存在会怎样呢?跟 git push 一样,Git 会在 fetch 前自己创建立本地分支, 就像是 Git 在 push 时,如果远程仓库中不存在目标分支,会自己在建立一样。

如果 git fetch 没有参数,它会下载所有的提交记录到各个远程分支……

相当简单,但是仅需更新一次,值得你去做!


原状态:

目标:

本关任务:

  • 抓取目标窗口中指定的提交记录,使用这些魔幻的命令吧!

注意:使用 fetch 时, 你必须指定 sourcedestination。 注意一下目标窗口, 因为提交对象的 ID 可能会变哦

解决方案:

  • 使用远程仓库的foo分支更新本地分支mastergit fetch origin foo:master

  • 使用远程仓库的master分支的父结点更新本地分支foogit fetch origin master^:foo

  • 最后合并本地的foomaster分支
    git checkout foogit merge master

8.7 没有source的source

Git 有两种关于 <source> 的用法是比较诡异的,即你可以在 git pushgit fetch 时不指定任何 source,方法就是仅保留冒号和 destination 部分,source 部分留空。

    git push origin :sidegit fetch origin :bugFix
  • 如果 push<source> 到远程仓库会如何呢?它会删除远程仓库中的分支!
  • 如果 fetch<source> 到本地,会在本地创建一个新分支。

原状态:

本关任务:

  • 删除一个远程的分支,
  • git fetch 在本地创建一个新分支

解决方案:

  • 删除一个远程的分支:git push origin :foo

  • 在本地创建一个新分支:git fetch origin :bar

8.8 Git Pull参数

既然你已经掌握关于 git fetchgit push 参数的方方面面了,关于 git pull 几乎没有什么可以讲的了 ?

因为 git pull 到头来就是 fetch 后跟 merge 的缩写。你可以理解为用同样的参数执行 git fetch,然后再 merge 你所抓取到的提交记录。

以下命令在 Git 中是等效的:

git pull origin foo 相当于:

    git fetch origin foogit merge o/foo

还有…

git pull origin bar~1:bugFix 相当于:

    git fetch origin bar~1:bugFixgit merge bugFix

看到了? git pull实际上就是 fetch + merge 的缩写, git pull 唯一关注的是提交最终合并到哪里(也就是为 git fetch 所提供的 destination 参数)

如果我们指定要抓取的 place,所有的事情都会跟之前一样发生,只是增加了 merge 操作

    git pull origin master


看到了吧! 通过指定 master 我们更新了 o/master。然后将 o/master merge 到我们的检出位置,无论我们当前检出的位置是哪。

pull 也可以用 source:destination 吗? 当然喽, 看看吧:

    git pull origin master:foo


哇, 这个命令做的事情真多。它先在本地创建了一个叫 foo 的分支,从远程仓库中的 master 分支中下载提交记录,并合并到 foo,然后再 merge 到我们的当前检出的分支 bar 上。操作够多的吧?!


原状态:

目标:

本关任务:

  • 按照目标窗口中的状态进行操作。你需要下载一些提交,然后创建一些新分支,再合并这些分支到其它分支, 但这用不了几个命令 ?

解决方案:

  • 根据远程仓库的bar分支在本地仓库新建一个foo分支,并将其merge到当前本地仓库检出的分支master
    git pull origin bar:foo

  • 根据远程仓库的bar分支在本地仓库新建一个side分支,并将其merge到当前本地仓库检出的分支master
    git pull origin master:side

9. 后记

终于通关了 ?

花了我不少时间(主要还是写博客麻烦)

还有理解 Git 的具体过程也费了不少心力

学习 Git 才刚开始,还有很长一段路要走,共勉 ?

Git Learning 学习笔记相关推荐

  1. 笔记 | 吴恩达Coursera Deep Learning学习笔记

    向AI转型的程序员都关注了这个号☝☝☝ 作者:Lisa Song 微软总部云智能高级数据科学家,现居西雅图.具有多年机器学习和深度学习的应用经验,熟悉各种业务场景下机器学习和人工智能产品的需求分析.架 ...

  2. 《Got Git》学习笔记(一)

    <Got Git>学习笔记(一) 最近想对自己的代码和文档进行归档整理,需要一个版本控制系统来进行 处理.自然而然的想到了目前流行的GitHub. GitHub,是一个面向开源及私有软件项 ...

  3. 【Machine Learning 学习笔记】Stochastic Dual Coordinate Ascent for SVM 代码实现

    [Machine Learning 学习笔记]Stochastic Dual Coordinate Ascent for SVM 代码实现 通过本篇博客记录一下Stochastic Dual Coor ...

  4. git的学习笔记(二):git远程操作

    git的学习笔记(一):git本地操作 1.创建ssh key ssh-keygen -t rsa -C "your_email@example.com" 执行命令后会在用户的家目 ...

  5. 【Machine Learning 学习笔记】feature engineering中noisy feature的影响

    [Machine Learning 学习笔记]feature engineering中noisy feature的影响 通过本篇博客记录一下添加噪声对Lasso和SVM的影响,采用的数据集为sklea ...

  6. Git 个人学习笔记及心得

    作为程序员如果你还不知道 Git 和 GitHub,说不过去吧,赶紧来学习一波. 一.认识GitHub Git 是个版本控制系统,说明白点就是进行代码的各种管理,比如你写错代码进行回滚啊.追寻 Bug ...

  7. Learn Git Branching 学习笔记(移动提交记录篇)

    目录 一.移动提交记录篇 1.Git Cherry-pick 2.交互式rebase Git用法高级篇在上一篇文章中Learn Git Branching 学习笔记(高级篇)_流年--by gone的 ...

  8. Learn Git Branching 学习笔记(高级话题篇)

    目录 一.高级话题篇 1.多分支rebase 2.选择父提交记录 3.纠缠不清的分支 Git的一些技术.技巧与贴士集合在上一篇文章中 Learn Git Branching 学习笔记(Git 技术.技 ...

  9. git/github学习笔记

    原文地址为: git/github学习笔记 请移步到:http://www.testclass.net/git/ ----- 我重新对git/github教程进行了编排和整理. 1. git 版本控制 ...

最新文章

  1. “CRISPR婴儿”计划疯狂重启 顶级科学家们表示无力阻止
  2. Silverlight实例教程 - Out of Browser的自定义应用
  3. 双方互GAN,不如来试试群体博弈?更快更强更自由 | ICLR 2021
  4. GO语言变量和常量、语言控制语句流程
  5. HUD 1043 Eight 八数码问题 A*算法 1667 The Rotation Game IDA*算法
  6. 基本的JVM内存分配过程案例
  7. multiprocessing python_Python教程:进程和线程amp;多进程
  8. 抽象工厂和工厂方法示例_抽象工厂设计模式示例
  9. 小程序和vue语法对比_React语法开发微信小程序 Demo
  10. Redis-慢查询分析
  11. mysql常用sql命令
  12. [PED01]Deep Subspace Clustering Networks
  13. 消防管道标志色号_消防标志的起步问题
  14. 计算机在职双证博士的学校,双证在职博士招生院校有哪些?
  15. php实现tptp客户端
  16. Should we ban guns 英语禁枪议论文
  17. TD-SCDMA介绍
  18. 国内外计算机联锁系统的发展,车站计算机联锁系统的现状与发展
  19. 安卓进阶开发资料之分享
  20. 让人拍案叫绝的创意如何产生

热门文章

  1. 中国大学MOOC翁恺C语言PTA入门练习(更新中)
  2. 《追风筝的人》的寓意读后感3200字
  3. uni-app 小项目开发 仿小米商城
  4. 关于爬虫selenium的初使用
  5. 计算机 睡眠 无法打印,[投诉]打印机手动开启休眠后不能用
  6. pdf照片显示正常打印时被翻转_考研准考证下载打印常见问题,走过路过不要错过进来看看...
  7. 人像跟踪相机_什么是拍摄人像的最佳相机镜头?
  8. 强化一波 hooks,这次咱们换个发力点
  9. 用于OpenGl的Camera类(QT实现的3D摄像机类)
  10. 手把手教你写网络爬虫(1):网易云音乐歌单