Py学习  »  Git

Git中的push和pull的默认行为

林晨熙 • 4 年前 • 125 次点击  
阅读 44

Git中的push和pull的默认行为

在讨论 push 和 pull 的默认行为前我们需要先了解 upstream、downstream 和 FETCH_HEAD 的概念。

upstream && downstream

通俗来说,当本地仓库 R 的某分支 x 的代码 push 到远程仓库 R'的 x'分支时,把 x'分支称为 x 分支的 upstream,相对地把 x 分支称为 x'分支的 downstream。R 和 R'以及 x 和 x'可以不同名,但通常我们都设置为同名(默认同名)。通过 upstream 和 downstream 就建立起了本地分支和远程分支间的映射关系。以下是关于 track 关系的常用命令:

  • 查询 upstream 和 downstream 的映射关系

    git branch -vvcat .git/config

    $ 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
    复制代码
  • 建立当前分支的 upstream

    git branch -u [repository-name/branch-name]

    $ git branch -u origin/dev
    =>  Branch 'dev' set up to track remote branch 'dev' from 'origin'.
    复制代码
  • 取消其他分支的 upstream

    git branch --unset-upstream [branch-name]

    省略分支名则表示取消当前分支的 upstream。

    //取消dev分支的upstream
    $ git branch --unset-upstream dev
    复制代码

FETCH_HEAD

FETCH_HEAD 是是一个短期引用,表示刚刚从远程获取的分支的最新 commit。每执行一次 fetch 操作都会更新一次 FETCH_HEAD 列表,这个列表存在于.git/FETCH_HEAD 文件中,它的每一行对应一个分支的最新 commit,第一行表示当前分支的最新 commit。 在 master 分支下执行 git fetch,然后查看.git/FETCH_HEAD 文件,输出如下:

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。

push

推送到远程分支命令:

$ 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 不能被推送。

pull

拉取远程分支命令:

$ 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 相当于分两步执行

  • 将 pull 替换为 fetch 执行命令 git fetch pb dev
  • git merge FETCH_HEAD

第二步 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 分支。

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/33034
 
125 次点击