git - 在没有 refspec 参数的情况下运行 git push、pull 和 fetch

标签 git

来自 Loeliger 2ed 的 Git 版本控制,

What if you don’t specify a refspec at all on a git push command? How does Git know what to do or where to send data?

First, without an explicit remote given to the command, Git assumes you want to use origin.

Without a refspec, git push will send your commits to the remote for all branches that are common between your repository and the upstream repository. Any local branch that is not already present in the upstream repository will not be sent upstream; branches must already exist and match names.


  • 都是
    您的存储库和上游存储库之间共有的分支”与本地存储库中的所有远程跟踪分支和远程存储库中相应的跟踪分支相同吗?
  • 报价适用于 git push .
    它(尤其是没有refspec 的情况下的部分)是否适用于git fetch并到 git pull ?
  • 最佳答案

    (恐怕这个答案又一次相当长了,因为这些问题做出的假设 - 至少在今天 - 是不正确的。跳到粗体部分以获得实际答案,但在此之前的所有内容也很重要。)

    您引用的书籍文本已过时,并且(现在)完全错误(这是不断发展的软件的危险,书籍已过时):

    First, without an explicit remote given to the command, Git assumes you want to use origin.



    在相对快速地浏览 git 自己的 git 历史记录时,我还没有准确地找到行为发生变化的时间,但是 git 1.8.2.1 和 now says 中的文档发生了变化。 :

    When the command line does not specify where to push with the <repository> argument, branch.*.remote configuration for the current branch is consulted to determine where to push. If the configuration is missing, it defaults to origin.



    (这个行为也匹配 git fetch——不出所料,因为他们使用相同的源代码来得到那个结果。)这没有提到可选的 branch.$branch.pushremote设置,覆盖 branch.$branch.remote .在所有情况下,这里 $branch代表您当前的分支,如 git symbolic-ref --short HEAD 的输出所示. (如果您处于“分离的 HEAD”模式,因此 git symbolic-ref 失败,git push 通常会给您一个“致命的:您当前不在分支上”错误,我认为自 2.0 版以来默认值已更改。)

    这还不是全部。 the git-config documentation增加了一个怪癖:

    remote.pushDefault
    The remote to push to by default. Overrides branch.<name>.remote for all branches, and is overridden by branch.<name>.pushRemote for specific branches.



    (幸运的是,截至今天,我认为这涵盖了所有内容。也就是说,git 首先为当前分支查找 pushremote 设置。如果没有这样的设置,它接下来会查找 remote.pushdefault 。如果这也是未设置,它回退到当前分支的 remote 设置。旧版本的 git 中的行为可能会有所不同。还要注意配置项不区分大小写:pushremotepushRemote 是相同的配置名称 [这让我想知道分支名称部分是否也不区分大小写;我将不得不在某个时候对其进行测试]。)

    关于 refspecs 的下一部分更错误,因为 git 随着时间的推移获得了一些更改,其中一些甚至在 git 的内置文档中也没有很好地记录。您的报价如下:

    Without a refspec, git push will send your commits to the remote for all branches that are common between your repository and the upstream repository.



    new documentation说:

    When the command line does not specify what to push with <refspec>... arguments or --all, --mirror, --tags options, the command finds the default <refspec> by consulting remote.*.push configuration, and if it is not found, honors push.default configuration to decide what to push (See git-config for the meaning of push.default).


    push.default设置是在 git 1.8 版左右添加的,但其默认值在 git 2.0 中更改。如果您还没有配置 push.default你会收到这个警告:
    warning: push.default is unset; ...
    

    自从配置项可用以来。咨询the git-config documentation ,您现在会发现:

    push.default
    Defines the action git push should take if no refspec is explicitly given. Different values are well-suited for specific workflows ...



    有五个选项,我将在此处简单列出(有关详细信息,请参阅链接文档):nothing , current , simple , upstream , 和 matching .您引用的书中文字描述matching ,这是 git 2.0 版之前的默认行为。新的默认值是 simple .

    git push , 如果您配置“匹配”行为

    Are "all branches that are common between your repository and the upstream repository" the same as all the remote-tracking branches in the local repository and the corresponding tracked branches in the remote repository?



    不。

    我建议您尝试运行 git ls-remote origin (或将 origin 替换为任何其他 Remote 的名称):
    $ git ls-remote origin
    28274d02c489f4c7e68153056e9061a46f62d7a0        HEAD
    1ff88560c8d22bcdb528a6629239d638f927cb96        refs/heads/maint
    28274d02c489f4c7e68153056e9061a46f62d7a0        refs/heads/master
    0ac5344e619fec2068de9ab2afdb99d1af8854be        refs/heads/next
    5467631257c047b16d95929559dd1887d27b0ddc        refs/heads/pu
    68a0f56b615b61afdbd86be01a3ca63dca70edc0        refs/heads/todo
    d5aef6e4d58cfe1549adef5b436f3ace984e8c86        refs/tags/gitgui-0.10.0
    3d654be48f65545c4d3e35f5d3bbed5489820930        refs/tags/gitgui-0.10.0^{}
    [snip -- lots more omitted]
    

    然后,尝试 git for-each-ref refs/heads :
    $ git for-each-ref refs/heads
    

    您将看到类似的输出,但具有不同的 SHA-1 和单词 commit夹在中间。

    这里的要点是refs/heads中列出的每个“头” — 在两个输出中,来自 ls-remote来自 for-each-ref — 代表 git push 的(本地)分支在进行“匹配”时考虑。匹配的头部被添加到推送列表中,给出一组 refspecs(以内部形式,但很容易表示为 name:name 或者,如果您使用 --force 标志, +name:name )。

    即使没有为这些本地分支配置上游,也会发生这种情况。因此,如果他们(远程)有 refs/heads/bob这是供 Bob Roberts 使用的,您有一个 refs/heads/bob您正在使用有关您的管道浮标或钓鱼浮标或其他任何信息的信息,即使您的 bob git 也会尝试匹配这些信息。并不意味着要逆流而上。

    push对比 fetch

    The quote is for git push. Does it (especially the part for the case without a refspec) apply to git fetch and to git pull?



    不。
    git fetch的行为更容易解释,因为它不会对你做所有奇怪的事情 git pull做。 git pull命令应该很方便,但是 git pull正试图将一个基本上存储库范围的“获取”操作与一个基本上受分支约束的“merge ”操作混合在一起。这两者不能很好地结合在一起。1(我认为 git pull 充其量可以在某个时候进行修改以执行更简单的、存储库范围的“获取所有内容,然后检查并 merge ,成对地,一些基于分支的on tracking configs and arguments”序列。我认为这会使其表现得像大多数人似乎期望的那样。它也主要是向后不兼容的,并且表现得像现在一样仅适用于最简单但默认的情况: 获取所有,然后只 merge 当前分支。但这无论如何都是猜测。)

    如果您省略所有引用规范,git fetch阅读您的 remote.$remote.fetch配置行。 (请注意,此“省略所有引用规范”子句意味着您还必须避免旧的“$GIT_DIR/branches 中的命名文件|”方法,例如。)

    名为 origin 的远程的默认配置行(单数)通常看起来像这样(查看您的本地存储库配置文件以查看它):
    [remote "origin"]
        +refs/heads/*:refs/remotes/origin/*
    

    这是“远程跟踪分支”产生的实际机制。
    fetch过程以相同的 git ls-remote origin 开始step,它从远程获取每个引用的列表——分支、标签或你有什么。 fetch代码继续将这些与 fetch = 中的每一个匹配起来。线下 [remote "origin"] . refspec 左侧的星号具有明显的含义;右侧的星号将替换为左侧星号匹配的任何内容。 (在当前版本的 git 中,* 必须匹配“整个部分”,松散地定义为“斜线之间的东西”,但在即将到来的 git 中,这个约束将放宽——尽管我不确定有什么用处。)

    您可能有很多 fetch =随心所欲。每个原始引用都遍历每一行以获得新的(替换)引用:如果左侧匹配,则右侧提供替换。 (如果右侧缺失或为空,由于历史原因,行为会变得有点奇怪,部分是为了保持 git pull 工作,部分是因为所需的行为在 git 1.8 左右发生了变化。)

    所有这些替换的结果形成了重命名的引用,并决定了哪些引用被复制了;但是对于每个原始引用,最多只能有一个结果:例如,您不能将“他们的”refs/heads/foo 映射到“您的”refs/remotes/origin/bar 和您的 refs/remotes/origin/zoink。

    要使存储库充当镜像,您可以使用 +refs/*:refs/*在不重命名的情况下带来所有引用(但这会破坏每个 fetch 上的本地分支,因此这主要仅对 --bare 有意义)。

    (“修剪”——根据另一方在启动协商期间为获取或推送发送的内容删除“死”引用——是单独完成的,并且仅当您设置了 --prune 标志时。)

    1它不是这样开始的。其实原pull脚本完全早于 Remote 和远程跟踪分支。提交 7ef76925d9c19ef74874e1735e2436e56d0c4897 拆分 pull进入 pullfetch然后两者随着时间的推移朝着不同的方向发展;一旦“远程”和“远程跟踪分支”出现,它们已经发展到试图混合它们的地步,我认为,只是一个坏主意,现在更糟。 :-)

    关于git - 在没有 refspec 参数的情况下运行 git push、pull 和 fetch,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34523494/

    相关文章:

    windows - git fetch 结果出错

    php - Gitignore Laravel 供应商文件夹

    c# - 在分支之间转换时生成错误 : Your project is not referencing the ".NETFramework,Version=v4.7.2" framework

    ruby-on-rails - 使用 Git 和 Heroku 进行适当的持续集成和持续部署

    Git 更改 pull 请求的源分支

    git - 从 "git diff"中排除文件

    git - 永久添加 SSH key

    xcode - 自动 Xcode Git 提交提示

    git - 将现有项目迁移到 git

    git - 保持 fork 最新的最有效方法