一直以來對git push
與git pull
命令的默認行爲感受混亂,今天抽空總結下。git
一般對於一個本地的新建分支,例如git checkout -b develop
, 在develop分支commit了代碼以後,若是直接執行git push
命令,develop分支將不會被push到遠程倉庫(但此時git push
操做有可能會推送一些代碼到遠程倉庫,這取決於咱們本地git config配置中的push.default
默認行爲,下文將會逐一詳解)。fetch
所以咱們至少須要顯式指定將要推送的分支名,例如git push origin develop
,才能將本地新分支推送到遠程倉庫。this
當咱們經過顯式指定分支名進行初次push操做後,本地有了新的commit,此時執行git push
命令會有什麼效果呢?code
若是你不曾改動過git config中的push.default
屬性,根據咱們使用的git不一樣版本(Git 2.0以前或以後),git push
一般會有兩種大相徑庭的行爲:orm
fatal: The current branch new has no upstream branch. To push the current branch and set the remote as upstream, use git push --set-upstream origin develop
爲何git版本不一樣會有兩種不一樣的push行爲?blog
由於在git的全局配置中,有一個push.default屬性,其決定了git push
操做的默認行爲。在Git 2.0以前,這個屬性的默認被設爲'matching',2.0以後則被更改成了'simple'。ci
咱們能夠經過git version
肯定當前的git版本(若是小於2.0,更新是個更好的選擇),經過git config --global push.default 'option'
改變push.default的默認行爲(或者也可直接編輯~/.gitconfig文件)。rem
push.default 有如下幾個可選值:
nothing, current, upstream, simple, matchingget
其用途分別爲:workflow
nothing - push操做無效,除非顯式指定遠程分支,例如git push origin develop
(我以爲。。。能夠給那些不肯學git的同事配上此項)。
current - push當前分支到遠程同名分支,若是遠程同名分支不存在則自動建立同名分支。
upstream - push當前分支到它的upstream分支上(這一項其實用於常常從本地分支push/pull到同一遠程倉庫的情景,這種模式叫作central workflow)。
simple - simple和upstream是類似的,只有一點不一樣,simple必須保證本地分支和它的遠程
upstream分支同名,不然會拒絕push操做。
matching - push全部本地和遠程兩端都存在的同名分支。
所以若是咱們使用了git2.0以前的版本,push.default = matching,git push後則會推送當前分支代碼到遠程分支,而2.0以後,push.default = simple,若是沒有指定當前分支的upstream分支,就會收到上文的fatal提示。
說到這裏,須要解釋一下git中的upstream究竟是什麼:
git中存在upstream和downstream,簡言之,當咱們把倉庫A中某分支x的代碼push到倉庫B分支y,此時倉庫B的這個分支y就叫作A中x分支的upstream,而x則被稱做y的downstream,這是一個相對關係,每個本地分支都相對地能夠有一個遠程的upstream分支(注意這個upstream分支能夠不一樣名,但一般咱們都會使用同名分支做爲upstream)。
初次提交本地分支,例如git push origin develop
操做,並不會定義當前本地分支的upstream分支,咱們能夠經過git push --set-upstream origin develop
,關聯本地develop分支的upstream分支,另外一個更爲簡潔的方式是初次push時,加入-u參數,例如git push -u origin develop
,這個操做在push的同時會指定當前分支的upstream。
注意push.default = current能夠在遠程同名分支不存在的狀況下自動建立同名分支,有些時候這也是個極其方便的模式,好比初次push你能夠直接輸入 git push 而沒必要顯示指定遠程分支。
弄清楚git push
的默認行爲後,再來看看git pull
。
當咱們未指定當前分支的upstream時,一般git pull
操做會獲得以下的提示:
There is no tracking information for the current branch. Please specify which branch you want to merge with. See git-pull(1) for details git pull <remote> <branch> If you wish to set tracking information for this branch you can do so with: git branch --set-upstream-to=origin/<branch> new1
git pull
的默認行爲和git push
徹底不一樣。當咱們執行git pull
的時候,其實是作了git fetch + git merge
操做,fetch操做將會更新本地倉庫的remote tracking,也就是refs/remotes中的代碼,並不會對refs/heads中本地當前的代碼形成影響。
當咱們進行pull的第二個行爲merge時,對git來講,若是咱們沒有設定當前分支的upstream,它並不知道咱們要合併哪一個分支到當前分支,因此咱們須要經過下面的代碼指定當前分支的upstream:
git branch --set-upstream-to=origin/<branch> develop // 或者git push --set-upstream origin develop
實際上,若是咱們沒有指定upstream,git在merge時會訪問git config中當前分支(develop)merge的默認配置,咱們能夠經過配置下面的內容指定某個分支的默認merge操做
[branch "develop"] remote = origin merge = refs/heads/develop // [1]爲何不是refs/remotes/develop?
或者經過command-line直接設置:
git config branch.develop.merge refs/heads/develop
這樣當咱們在develop分支git pull時,若是沒有指定upstream分支,git將根據咱們的config文件去merge origin/develop
;若是指定了upstream分支,則會忽略config中的merge默認配置。
以上就是git push和git pull操做的所有默認行爲,若有錯誤,歡迎斧正
[1] 爲何merge = refs/heads/develop 而不是refs/remotes/develop?
由於這裏merge指代的是咱們想要merge的遠程分支,是remote上的refs/heads/develop,文中便是origin上的refs/heads/develop,這和咱們在本地直接執行git merge
是不一樣的(本地執行git merge origin/develop
則是直接merge refs/remotes/develop)。
refs:
http://git-scm.com/book/en/v2/Git-Internals-The-Refspec
http://stackoverflow.com/questions/658885/how-do-you-get-git-to-always...
http://stackoverflow.com/questions/17096311/why-do-i-need-to-explicitl...
http://www.gitguys.com/topics/the-configuration-file-branch-section/