Git push与pull的默认行为

一直以来对git pushgit pull命令的默认行为感受混乱,今天抽空总结下。git

git push

一般对于一个本地的新建分支,例如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

  1. develop分支中本地新增的commit被push到远程仓库
  2. push失败,并收到git以下的警告
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提示。

upstream & downstream

说到这里,须要解释一下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 pull

弄清楚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/

by Abruzzi's blog

相关文章
相关标签/搜索