在討論 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 分支。