上一篇大家学会了如何下载远端储存库 (git clone
, git pull
) 与上传远端储存库 (git push
),本篇文章来教大家认识远端储存库的其他细节。
git clone
将远端储存库複製到本地,并建立工作目录与本地储存库 (就是 .git
资料夹)
git pull
将远端储存库的最新版下载回来,下载的内容包含完整的物件储存库(object storage)。并且将远端分支合併到本地分支。 (将 origin/master
远端分支合併到 master
本地分支)
所以一个 git pull
动作,完全相等于以下两段指令:
git fetch
git merge origin/master
git push
将本地储存库中目前分支的所有相关物件推送到远端储存库中。
git fetch
将远端储存库的最新版下载回来,下载的内容包含完整的物件储存库(object storage)。 这个命令不包含「合併」分支的动作。
git ls-remote
显示特定远端储存库的参照名称。包含远端分支与远端标籤。
我们先前学习过关于「分支」的概念,不过仅限于「本地分支」,今天我们多出了个「远端分支」,事情就相对複杂一些。
基本上,当我们的版本控管流程加上了远端储存库之后,原本的分支就可以被拆成四种不同的概念:
远端追踪分支
这个分支位于远端,目的是用来追踪分支的变化情形。通常远端分支你是存取不到的。
本地追踪分支
当你执行 git clone
複製一个远端容器回来之后,所有远端追踪分支会被下载回来,并且相对应的建立起一个同名的 本地追踪分支。
我们以複製 jQuery 在 GitHub 上的专案为例,透过 git clone https://github.com/jquery/jquery.git
下载回来后,执行 git branch -a
指令,显示出所有「本地分支」与「本地追踪分支」。「本地追踪分支」就是如下图红字的部分:
本地分支
在透过 git branch
指令执行时所显示的分支,就是所谓的「本地分支」,这些分支存在于本地端,而这些分支又常被称为 主题分支 (Topic Branch) 或 开发分支 (Development Branch),就是因为这些分支预设不会被推送到远端储存库,主要用来做开发用途。
远端分支
顾名思义,远端分支就是在远端储存库中的分支,如此而已。如果你用 GitHub 的话,你是无法存取远端分支的。
虽然说「概念上」可以分为这四类,但其实这些分支就只是参照名称而已,而这裡的「追踪分支」主要就是用来跟远端的分支做对应,你不应该直接在这些分支上建立版本 (虽然你还是可以这麽做,但强烈不建议乱搞),而是把这些「本地追踪分支」视为是一种「唯读」的分支。
我们在上一篇有提过可以透过 git remote
指令手动加入一个「远端储存库」,例如:
git remote add origin https://github.com/doggy8088/sandbox-empty2.git
这个 origin
名称是在 Git 版本控管中惯用的预设远端分支的参照名称,主要目的是用来代表一个远端储存库的 URL 位址。
不过,事实上你可以在你的工作目录中,建立多个远端储存库的参照位址。例如我们以 sandbox-empty2
这个专案为例,我们先複製回来,然后透过 git remote -v
可列出目前注册在工作目录裡的远端储存库资讯。如果我们额外将 jQuery 的远端储存库也一併下载回来,则可以用以下指令先行注册一个名称起来。
git remote add jquery https://github.com/jquery/jquery.git
最后再用 git fetch
指令把完整的 jQuery 远端储存库一併下载回来,完整的执行过程如下图示:
你可以看到,我们事实上可以在一个 Git 工作目录中,加入许多相关或不相关的远端储存库,这些複製回来的完整储存库,都包含了这些储存库中的所有物件与变更历史,这些 Git 物件随时都可以灵活运用。不过,通常我们注册多个远端储存库的机会并不多,除非你想抓特其他团队成员的版本库回来查看内容。
这些注册进工作目录的远端储存库设定资讯,都储存在 .git\config
设定档中,其内容如下范例:
[remote "origin"]
url = https://github.com/doggy8088/sandbox-empty2.git
fetch = +refs/heads/*:refs/remotes/origin/*
[remote "jquery"]
url = https://github.com/jquery/jquery.git
fetch = +refs/heads/*:refs/remotes/jquery/*
这个 [remote "origin"]
区段的设定,包含了远端储存库的代表名称 origin
,还有两个重要的参数,分别是 url
与 fetch
这两个,所代表的意思是:「远端储存库 URL 位址在 https://github.com/doggy8088/sandbox-empty2.git
,然后 fetch
所指定的则是一个参照名称对应规格(refspec)。」
我们先来看一下 refspec 的格式:
+refs/heads/*:refs/remotes/origin/*
这个格式概略区分成 4 块:
+
设定 +
加号,代表传输资料时,不会特别使用安全性确认机制。
refs/heads/*
「来源参照规格」,代表一个位于远端储存库的远端分支,而 *
星号代表 refs/heads/
这个路径下「所有的远端参照」。
:
这用来区隔「来源分支」与「目的分支」
refs/remotes/origin/*
「目的参照规格」,代表一个位于本地储存库的本地追踪分支,而 *
星号代表工作目录的 refs/remotes/origin/
这个路径下「所有的本地参照」。
当我们定义好这份 refspec 对应规格后,主要会影响到的是 git fetch
与 git push
这两个远端储存库的操作。
git fetch
就是把远端储存库的相关物件取回,但要取得那些远端分支的物件呢?就是透过这份 refspec 的定义,他才知道的。以上述为例,当你执行 git fetch
或 git fetch origin
的时候,他会先透过 URL 连到远端储存库,然后找出「来源参照规格」的那些远端分支 (refs/heads/*
),取回之后放入「目的参照规格」的那些本地追踪分支(refs/remotes/origin/*
)。
我们要怎样查询远端储存库到底有哪些分支呢?你可以执行 git ls-remote
或 git ls-remote origin
即可列出所有远端分支:
如果你把 fetch 的 refspec 修改成以下这样,那麽除了 master
以外的远端分支,就不会被下载了!:
fetch = +refs/heads/master:refs/remotes/origin/master
如果你想明确下载特定几个分支就好,你可以重複定义好几个不同的 fetch
参照规格 (refspec),例如:
[remote "origin"]
url = https://github.com/doggy8088/sandbox-empty2.git
fetch = +refs/heads/master:refs/remotes/origin/master
fetch = +refs/heads/TestBranch:refs/remotes/origin/TestBranch
另外,在我们透过 git remote add [URL]
建立远端储存库设定时,并没有 push
参照规格,其预设值如下:
push = +refs/heads/*:refs/heads/*
所代表的意思则是,当执行 git push
时,Git 指令会参考这份 push
的参照规格,让你将本地储存库在 refs/heads/*
底下的所有分支与标籤,全部都推送到相对应远端储存库的 refs/heads/*
参照名称下。
最后,无论你执行 git push
或 git fetch
,在不特别加参数的情况下,Git 预设就是用 origin
当成远端储存库,并使用 origin
的参照规格。
我们已经知道,一个工作目录下的本地储存库,可能会定义有多个远端储存库。所以当你想将 非 master
分支 透过 git push
推送到远端时,Git 可能不知道你到底想推送到哪裡,所以我们要另外定义本地分支与远端储存库之间的关系。
我们以 https://github.com/doggy8088/frontend-tools.git
这个远端储存库为例,我複製下来后,预设就会有一个 master
本地分支,我尝试建立一个 FixForCRLF
本地分支,直接透过 git push
无法推送成功,你必须输入完整的 git push origin FixForCRLF
指令才能将本地分支推送上去,原因就出在你并没有设定「本地分支」与「远端储存库」之间的预设对应。
要将本地分支建立起跟远端储存库的对应关系,只要在 git push
的时候加上 --set-upstream
参数,即可将本地分支注册进 .git\config
设定档,之后再用 git push
就可以顺利的自动推送上去。
执行 git push --set-upstream origin FixForCRLF
的同时,会在 .git\config
设定档增加以下内容:
[branch "FixForCRLF"]
remote = origin
merge = refs/heads/FixForCRLF
你可以从这个设定档的格式中发现,在这个 [branch "FixForCRLF"]
设定裡面,有两个属性分别是 remote
与 merge
,所代表的意思是:「当你想要将本地的 FixForCRLF
分支推送到远端储存库时,预设的远端储存库为 origin
这个,然后推送的时候要将本次的变更合併到 refs/heads/FixForCRLF
这个远端分支裡。」
当然,我们在一开始执行 git clone https://github.com/doggy8088/frontend-tools.git
的时候,Git 就会预设帮我们建立好 master
分支的对应关系,所以针对 master
分支进行操作时,不需要额外加上 --set-upstream
就能使用。其分支的定义内容如下:
[branch "master"]
remote = origin
merge = refs/heads/master
本篇文章详细的介绍,在面对远端储存库时的一些重要观念,尤其是参照规格 (refspec) 这一段,学会之后才有机会设定更加符合自己或团队需要的设定。不过,还是建议大家不要修改预设值,以免把大家搞糊涂了。
我重新整理一下本日学到的 Git 指令与参数: