目录
1、合并远程仓库
2、远程追踪
3、git push的参数
4、git fetch的参数
5、没有source的source
6、git pull的参数
为什么不用 merge 呢?
为了 push 新变更到远程仓库,你要做的就是包含远程仓库中最新变更。意思就是只要你的本地分支包含了远程分支(如
o/master
)中的最新变更就可以了,至于具体是用 rebase 还是 merge,并没有限制。那么既然没有规定限制,为何前面几节都在着重于 rebase 呢?为什么在操作远程分支时不喜欢用
merge
呢?
在开发社区里,有许多关于 merge 与 rebase 的讨论。以下是关于 rebase 的优缺点:
优点:
- Rebase 使你的提交树变得很干净, 所有的提交都在一条线上
缺点:
- Rebase 修改了提交树的历史
比如, 提交 C1 可以被 rebase 到 C3 之后。这看起来 C1 中的工作是在 C3 之后进行的,但实际上是在 C3 之前。
一些开发人员喜欢保留提交历史,因此更偏爱 merge。而其他人(比如我自己)可能更喜欢干净的提交树,于是偏爱 rebase。仁者见仁,智者见智。 :D
目标
初始
solution
git checkout master
git pull
git merge side1
git merge side2
git merge side3
git push
远程跟踪分支
在前几节课程中有件事儿挺神奇的,Git 好像知道
master
与o/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
本节我们在不检出 master
分支的情况下将工作推送到的远程仓库中的 master
分支上。
目标
初始
git checkout -b side o/master
git commit
git pull --rebase
git push
既然你知道了远程跟踪分支,我们可以开始揭开 git push、fetch 和 pull 的神秘面纱了。我们会逐个介绍这几个命令,它们在理念上是非常相似的。
首先来看
git push
。在远程跟踪课程中,你已经学到了 Git 是通过当前检出分支的属性来确定远程仓库以及要 push 的目的地的。(如何理解?)这是未指定参数时的行为,我们可以为 push 指定参数,语法是:
git push
参数是什么意思呢?我们稍后会深入其中的细节, 先看看例子, 这个命令是:
git push origin master
把这个命令翻译过来就是:
切到本地仓库中的“master”分支,获取所有的提交,再到远程仓库“origin”中找到“master”分支,将远程仓库中没有的提交记录都添加上去,搞定之后告诉我。
我们通过“place”参数来告诉 Git 提交记录来自于 master, 要推送到远程仓库中的 master。它实际就是要同步的两个仓库的位置。
需要注意的是,因为我们通过指定参数告诉了 Git 所有它需要的信息, 所以它就忽略了我们所检出的分支的属性!
本关我们要更新远程仓库中的 foo
和 master
, 但是 git checkout
被禁用了!
注意:远程分支使用 o/
开头是因为 origin/
对于 UI 来说太长了。不用太在意这个,直接用 origin
作为远程仓库的名称就可以了。
目标
初始
solution —— 这里不明白
git push origin master
git push origin foo
当为 git push 指定 place 参数为
master
时,我们同时指定了提交记录的来源和去向。你可能想问 —— 如果来源和去向分支的名称不同呢?比如你想把本地的
foo
分支推送到远程仓库中的bar
分支。
要同时为源和目的地指定
的话,只需要用冒号
:
将二者连起来就可以了:
git push origin
这个参数实际的值是个 refspec,“refspec” 是一个自造的词,意思是 Git 能识别的位置(比如分支
foo
或者HEAD~1
)
目标
初始
solution
git push origin master^:foo
git push origin foo:master
git push origin foo:master 把本地foo目前为止所有的代码,提交到远程的matser的下面。
我们刚学习了 git push 的参数,很酷的
参数,还有用冒号分隔的 refspecs(
)。 这些参数可以用于
git fetch
吗?你猜中了!
git fetch
的参数和git push
极其相似。他们的概念是相同的,只是方向相反罢了(因为现在你是下载,而非上传)
参数
如果你像如下命令这样为 git fetch 设置 的话:
git fetch origin foo
Git 会到远程仓库的
foo
分支上,然后获取所有本地不存在的提交,放到本地的o/foo
上。
你可能会好奇 —— 为何 Git 会将新提交放到
o/foo
而不是放到我本地的 foo 分支呢?之前不是说这样的 参数就是同时应用于本地和远程的位置吗?好吧, 本例中 Git 做了一些特殊处理,因为你可能在 foo 分支上的工作还未完成,你也不想弄乱它。还记得在
git fetch
课程里我们讲到的吗 —— 它不会更新你的本地的非远程分支, 只是下载提交记录(这样, 你就可以对远程分支进行检查或者合并了)。
“如果我们指定
会发生什么呢?”
如果你觉得直接更新本地分支很爽,那你就用冒号分隔的 refspec 吧。不过,你不能在当前检出的分支上干这个事,但是其它分支是可以的。
这里有一点是需要注意的 ——
source
现在指的是远程仓库中的位置,而才是要放置提交的本地仓库的位置。它与 git push 刚好相反,这是可以讲的通的,因为我们在往相反的方向传送数据。
理论上虽然行的通,但开发人员很少这么做。我在这里介绍它主要是为了从概念上说明
fetch
和push
的相似性,只是方向相反罢了。
git fetch
课程里我们讲到的吗 —— 它不会更新你的本地的非远程分支, 只是下载提交记录(这样, 你就可以对远程分支进行检查或者合并了)。
以下基本操作基本没人会这样干,因为这样会修改本地的记录
注意这里远程的o/foo也移动了。
古怪的
Git 有两种关于
的用法是比较诡异的,即你可以在 git push 或 git fetch 时不指定任何
source
,方法就是仅保留冒号和 destination 部分,source 部分留空。
git push origin :side
git fetch origin :bugFix
我们分别来看一下这两条命令的作用……
既然你已经掌握关于
git fetch
和git push
参数的方方面面了,关于 git pull 几乎没有什么可以讲的了 :)因为 git pull 到头来就是 fetch 后跟 merge 的缩写。你可以理解为用同样的参数执行 git fetch,然后再 merge 你所抓取到的提交记录。
以下命令在 Git 中是等效的:
git pull origin foo
相当于:
git fetch origin foo; git merge o/foo
还有...
git pull origin bar~1:bugFix
相当于:
git fetch origin bar~1:bugFix; git merge bugFix
看到了? git pull 实际上就是 fetch + merge 的缩写, git pull 唯一关注的是提交最终合并到哪里(也就是为 git fetch 所提供的 destination 参数)
一起来看个例子吧:
merge是参数合并到*上
目标
初始
solution
你需要下载一些提交,然后创建一些新分支,再合并这些分支到其它分支
git pull origin bar:foo
git pull origin master:side