在讨论 push 和 pull 的默认行为前咱们须要先了解 upstream、downstream 和 FETCH_HEAD 的概念。git
通俗来讲,当本地仓库 R 的某分支 x 的代码 push 到远程仓库 R'的 x'分支时,把 x'分支称为 x 分支的 upstream,相对地把 x 分支称为 x'分支的 downstream。R 和 R'以及 x 和 x'能够不一样名,但一般咱们都设置为同名(默认同名)。经过 upstream 和 downstream 就创建起了本地分支和远程分支间的映射关系。如下是关于 track 关系的经常使用命令:github
查询 upstream 和 downstream 的映射关系bash
git branch -vv
或 cat .git/config
fetch
$ git branch -vv
=> dev 32cf90b [origin/dev] e23rw
master 9b04659 [origin/master] dadfa
$ cat .git/config
=> [branch "master"]
remote = origin
merge = refs/heads/master
[branch "dev"]
remote = origin
merge = refs/heads/dev
复制代码
创建当前分支的 upstreamspa
git branch -u [repository-name/branch-name]
code
$ git branch -u origin/dev
=> Branch 'dev' set up to track remote branch 'dev' from 'origin'.
复制代码
取消其余分支的 upstreamrem
git branch --unset-upstream [branch-name]
it
省略分支名则表示取消当前分支的 upstream。io
//取消dev分支的upstream
$ git branch --unset-upstream dev
复制代码
FETCH_HEAD 是一个短时间引用,表示刚刚从远程获取的分支的最新 commit。每执行一次 fetch 操做都会更新一次 FETCH_HEAD 列表,这个列表存在于.git/FETCH_HEAD
文件中,它的每一行对应一个分支的最新 commit,第一行表示当前分支的最新 commit。 在 master 分支下执行 git fetch,而后查看.git/FETCH_HEAD
文件,输出以下:ast
9b04659a6105b139fad28018ee0f8c777379134a branch 'master' of https://github.com/fengyueran/test
91edbb325972ffb8e924e664d61aa79054967e12 not-for-merge branch 'dev' of https://github.com/fengyueran/test
32cf90b6fdad208260acde62fa24e72653895758 not-for-merge branch 'dev1' of https://github.com/fengyueran/test
32cf90b6fdad208260acde62fa24e72653895758 not-for-merge branch 'dev2' of https://github.com/fengyueran/test
复制代码
能够看到,直接 git fetch 会获取全部分支的最新 commit,第一行为 master 分支,即当前分支。此时执行 git fetch origin master,而后查看.git/FETCH_HEAD
文件,输出以下:
9b04659a6105b139fad28018ee0f8c777379134a branch 'master' of https://github.com/fengyueran/test
复制代码
能够看到只获取了指定分支 master 的最新 commit。
推送到远程分支命令:
$ git push [remote-repository-name] [branch-name]
例: 将代码推送到远程仓库pb的dev分支
$ git push pb dev
复制代码
当咱们不显示指定仓库名和分支名而直接用 git push 会是什么效果呢? 对于 origin 仓库的 master 分支,是能够直接推送的,以下:
$ git push
=> Counting objects: 3, done.
Writing objects: 100% (3/3), 201 bytes | 201.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0)
To https://github.com/fengyueran/test.git
* [new branch] master -> master
复制代码
之因此可以直接推送是由于在 clone 的时候会自动建立 master 分支来跟踪 origin/master。
对于非 origin 仓库非 master 分支 git push 会是怎样呢? 以下,切换到 dev 分支而后 git push
$ git checkout -b dev
$ git push
=> fatal: The current branch dev has no upstream branch.
To push the current branch and set the remote as upstream, use
git push --set-upstream origin dev
复制代码
提示没有 upstream 的分支,也就说本地的 dev 分支 push 时不知道推送到远程的哪一个分支,也就无法推送。但这取决于具体的 git 配置,在 Git2.0 以前,直接 git push,若是没有 upstream 就以当前分支名做为 upstream。咱们能够经过配置 push.default 来改变这种行为,命令以下:
$ git config push.default [option]
复制代码
push.default 选项
push.default 有几个可选值: nothing, current, upstream, simple, matching
nothing
什么都不推送除非显示地指定远程分支。
current
推送当前分支到远程同名分支,若是远程不存在,则建立同名分支。
$ git config push.default current
$ git push
=> Total 0 (delta 0), reused 0 (delta 0)
remote:
remote: Create a pull request for 'dev' on GitHub by visiting:
remote: https://github.com/fengyueran/test/pull/new/dev
remote:
To https://github.com/fengyueran/test.git
* [new branch] dev -> dev
复制代码
upstream
推送当前分支到 upstream 分支上,所以必须有 upstream 分支,这种模式一般用于从一个仓库获取代码的情景。
simple
和 upstream 相似,不一样点在于,必须保证本地分支与 upstream 分支同名,不然不能 push。
matching
推送全部本地和远程两端都存在的同名分支。
在 Git2.0 以前 push.default 默认值为 matching,2.0 以后默认值为 simple,没有 upstream 不能被推送。
拉取远程分支命令:
$ git pull [remote-repository-name] [branch-name]
例: 拉取远程仓库pb的dev分支
$ git pull pb dev
复制代码
当咱们不显示指定仓库名和分支名而直接用 git pull 会是什么效果呢?
事实上 git pull = git fetch + git merge FETCH_HEAD,直接 git pull 时会首先执行 git fetch origin 获取 origin 仓库全部分支,而后执行 git merge FETCH_HEAD 将 FETCH_HEAD(远程某个分支的最新 commit) 合并到当前分支。 若是是 git pull --rebase 第二步则执行 rebase 操做,即 git rebase FETCH_HEAD。
以下: git pull pb dev 至关于分两步执行
第二步 merge 时会访问 git 的默认配置。
// cat .git/config
[branch "master"]
remote = origin
merge = refs/heads/master
复制代码
能够看到 master 分支的 upstream 为远程的 master 分支,所以在 pull 合并时会合并远程的 master 分支,这里的 refs/heads/master 指的是远程仓库的 master 分支。固然咱们能够修改这个配置,从新创建 upstream 或直接修改配置。 这里直接经过如下命令修改配置
git config branch.master.merge refs/heads/dev
复制代码
此时,再查看配置
[branch "master"]
remote = origin
merge = refs/heads/dev
复制代码
merge 的值变成了 refs/heads/dev,也就是说在 master 分支运行 pull 再 merge 的时候会 merge 远程仓库的 dev 分支而不是 master 分支。