代码之家  ›  专栏  ›  技术社区  ›  Tim unnamed eng

git pull的合并步骤是否发生在获取的与本地当前分支不对应的远程分支上?

git
  •  1
  • Tim unnamed eng  · 技术社区  · 6 年前

    说的是合并步骤 git pull :

    但是Git怎么知道合并那些特定的分支呢?答案 来自配置文件:

    [branch "master"]
            remote = origin
            merge = refs/heads/master
    

    换言之,这为Git提供了两条关键信息: 当主人 是电流吗 ,签出分支,使用原点作为默认远程 在获取(或拉取)期间从中获取更新。 从远程作为默认分支合并到此中,主

    有可能 git拉 fetch = +refs/heads/*:refs/remotes/origin/* ).

    但是合并步骤只说明合并发生在 获取的与本地当前分支相对应的远程分支 . 合并是否也发生在 ? 如果是,什么时候发生?

    2 回复  |  直到 6 年前
        1
  •  2
  •   torek    6 年前

    git pull的fetch步骤可能会获取多个远程分支(因为 fetch = +refs/heads/*:refs/remotes/origin/* ).

    平原 git fetch 真的很有价值 遥控器的分支( refs/heads/* ). 但是 git pull 使用 平原 git获取 git获取 远程跟踪名称(假设Git版本>=1.8.4).

    出于其他原因,这实际上对你的问题的要点并不那么重要,但要说清楚的是,我已经补充了一个(非常)长的描述 git获取 操作和方式 用这个。同时,让我们来解决这个问题 第二 命令 跑。记得, git拉 本质上是:

    • 运行 (有多种选择),然后
    • 运行第二个Git命令,通常是 git merge (有多种选择)。

    第一 命令主要收集提交的对象,以及从其他Git存储库中完成这些对象所需的任何内容。在获取之后,您的Git有(一些/足够的)Git的提交,但它们不是的一部分 属于 你的

    如果第二个命令是 合并分支 ,您的Git运行:

    git merge -m message hash1 hash2 ... ]

    merge branch ' B ' of URL 对某些人来说 B 和一个或多个,但我们希望只有一个提交哈希ID。

    任何 合并分支 现在的 分支机构。任何合并: 2

    • 正常完成,或
    • --no-commit ).

    对于正常和快进的情况,结果是当前分支命名为 HEAD

    所以答案是:

    合并是否也发生在与本地当前分支不对应的其他获取的远程分支上?

    ,但问题本身是混乱的,或者至少是混乱的 惯性导航与制导 在它的措辞:合并发生 仅限当前分支!因为你只能 退房 仅本地分支机构 地方的 分支永远不会合并到一起。从这个意义上说,合并只能“发生在”本地分支机构。我得猜猜你说的“合并发生在……上”是什么意思。。。“远程分支”(请参阅下面很长的“long”部分,以了解这样的猜测。)

    请注意 合并分支 作为论据 一个或多个提交哈希ID。这些其他提交哈希ID 可以 可以通过远程跟踪名称指定,或者作为原始哈希ID指定,或者以Git接受哈希ID的任何其他方式指定,例如相对名称 develop~7^2~3 (尽管我从不推荐这最后一个表达)。这个 git拉 命令始终使用原始哈希ID,但通过 合并分支 -m


    1 git拉 这是一个设计拙劣的命令:把它想象成一把多刃瑞士军刀,其中几刃永远不能合上,所以每次使用它时,除非你握着它,否则你总是把手掌割断 非常 精致地。多哈希合并通常会导致Git称之为 章鱼合并 ,但我不想在这里谈这些血淋淋的细节。这个答案已经太长了。

    2 这个 git merge --squash 操作与 git merge --no-commit ,作为 --squash 打开 --不承诺 . 这迫使你做出最后的承诺。有什么特别的 git合并--挤压 当你自己做出承诺时,Git会做出新的承诺, 毕竟不是合并提交 git合并--不提交 .

    这里的一些诡计是由于 git拉 git获取 . 要理解这一点,我们必须从 真的,深深的 git获取

    git获取

    这个 git获取 命令是复杂的,很多这种复杂的情况都是由于非常老的Git的工作方式造成的。远古的,原始的吉特人没有 远程跟踪名称:没有 origin/master . 这导致了很多丑陋的东西,所以最终Git的人发明了远程跟踪名称,但是由于向后兼容的原因,他们不能仅仅 远程跟踪名称。这就是很多奇怪的地方。

    暂时不用担心,记住这一点 git获取 git ls-remote . 我鼓励学习Git的人跑步 其他 你的 Git在任何 git获取 .

    还要记住调用 git获取 是(简化):

    git fetch [<remote> [<refspec1> [<refspec2> ...]]]
    

    这个 remote 通常默认为 origin 但你当然可以直截了当地写出来 起源 . 任何 其他 论据 起源 参考规范 提供任何refspec,这样我们就不必定义它们是如何工作的;我们假设您的Git和他们的Git使用的是所谓的智能协议(愚蠢的协议可能会浪费大量时间传输无用的数据,所以我们不要去那里)。最后,我们假设一个标准 fetch = 行,读取 .

    这是真实的 git ls远程 输出,微调一位(ok,a ),对于Git的Git存储库:

    ecbdaf0899161c067986e9d9d564586d4b045d62        HEAD
    0d0ac3826a3bbb9247e39e12623bbcfdd722f24c        refs/heads/maint
    ecbdaf0899161c067986e9d9d564586d4b045d62        refs/heads/master
    2a65cea5f9ce26d22baec1ec541c86d41e2e700a        refs/heads/next
    855f98be272f19d16564ed44d8e858d8d459d7e5        refs/heads/pu
    0596e1ad5143dab167e62bf39387d2b4e06cadb6        refs/heads/todo
    d5aef6e4d58cfe1549adef5b436f3ace984e8c86        refs/tags/gitgui-0.10.0
    3d654be48f65545c4d3e35f5d3bbed5489820930        refs/tags/gitgui-0.10.0^{}
    [massive snippage]
    dcba104ffdcf2f27bc5058d8321e7a6c2fe8f27e        refs/tags/v2.9.5
    4d4165b80d6b91a255e2847583bd4df98b5d54e1        refs/tags/v2.9.5^{}
    

    maint , master , next pu ,和 todo . (他们还给了我们一个推荐的分支机构,让我们通过 ,以及大约20个bajillion标记的列表以及这些标记标识的提交。)

    git fetch remote 您的Git在某个URL调用其他Git(可能很长,很难键入)URL存储为一个配置条目,使用更短、更简单、更有意义的URL 你到底做了什么 git ls-remote remote 打印出来。这会给你的Git一个完整的 他们的 Git的分支和标记名。

    然后你的Git通过 取回= 线, 您尚未提供任何参考规范。这将为Git提供远程跟踪名称的列表,例如 refs/remotes/origin/master ,以创建或更新。它还提供了Git将要提交的提交散列id的列表 需要 参考/遥控/原点/主 在名称中哈希ID数据库必须提供 有效的 原点/主 ,它需要在本地具有哈希ID为的提交(在本例中) ecbdaf0899161c067986e9d9d564586d4b045d62

    此时,您的Git将把这些必需的提交哈希id添加到它需要确保存在的提交列表中。它现在进入对话的“拥有/想要/不要”阶段。

    我有哈希ID为H的提交 欲望 ,你的Git会说: 是的,请寄给我那个承诺书,并告诉我那个承诺书的父母。 如果你的Git已经 但是,你的Git会说: 不,我根本不需要那个承诺。 已经有了,所以你的Git说 对那些人;一旦你的Git拒绝了所有的父母,或者他们的Git已经用完了父母,这两个Git就开始实际的数据传输。

    为了完成数据传输,他们的Git现在打包了所有选定的提交(如果有的话,也许您已经有了所有的分支提示提交)和所有必需的子对象,并将它们发送出去,称为 薄包装 . 这是计算密集型的 你的 结束时,您可能会看到更多关于检查连接性之类的消息,但不管怎样,现在您的Git拥有了所有提交,以及完成这些提交所需的所有其他对象。

    会话 fetch linegit将继续更新远程跟踪分支。现在您的Git已经有了必要的提交,Git将替换您的Git的旧值 参考/遥控/原点/主 以及他们的新价值观。每个更新操作:

    • 可以是快进,和/或
    • + 登录获取参考规范)。

    强制更新是Git将执行的操作,即使更新操作本身不是一个快进操作。快进更新是指 新的 哈希ID保留现有哈希ID (有关可达性的完整讨论,请参见 Think Like (a) Git ). 所以最后一个阶段尝试更新每个映射的名称。通常,如果需要的话,这是强制的,这样所有这些更新都会完成,而不考虑快进性。你的Git指纹:

       <oldhash>..<newhash>    master -> master
    

    如果你的Git更新了你的 参考/遥控/原点/主 基于他们的 refs/heads/master <oldhash> 是您的 参考/遥控/原点/主 以及 <newhash> 是新哈希ID的缩写。

     + <oldhash>...<newhash>   master -> master (forced update)
    

    (注意加号和三个点,以及附加说明)如果更新是 快进,但还是发生了。

    (如果更新不是快进的并且不是强制的,那么Git不会打印任何内容,也不会更新远程跟踪名称。这在实践中从未发生过,因为 取回= 这条线有加号。)

    git获取

    首先,当然,我们需要定义 参考规范 . 两者 git获取 git push 使用它们,尽管它们在细节上有点不同。

    refspec非常简单:它们主要是两个名称之间带有冒号的成对名称。前面有一个可选的加号,意思是

    +refs/heads/master:refs/remotes/origin/master
    

    这和我们在电影里看到的很像 取来 线路。

    来源 ,右边的是 目的地 . 所以这个refspec的意思是 使用 参考/主管/主管 参考/遥控/原点/主 . 我们会抓到你的 他们的 主人 并且成功了 我们的 原点/主 ,如果有必要,我们会强制更新。

    refs/heads/ (或对于标签 refs/tags/ 猜测 通过检查您的和/或它们的名称的内容,您的意思是分支名称还是标记名称。如果你写的话,这有点草率 master:master 例如,如果 分支 命名 主人 ,但同时也是 标签 主人 ? 我不知道答案我必须测试才能确定但是我 参考/标题/ 我的意思是,不要做一个名为 . :-)

    最后,这意味着您将看到refspec 很多。这对于 git获取 :我们不想覆盖 我们的 主人 原点/主 相反。

    你也可以用一种 一半 -参考规范:

    master
    

    或:

    :delete
    

    这意味着更复杂的是 对于 以及 . 与 git推送 , :delete 是对另一个Git的请求 删除 git获取 ,省略源代码完全没有意义(测试表明Git将丢失的源代码解释为 ,但我建议不要假设它没有任何文档记录。)

    省略 目的地 另一方面,告诉我们 git获取 是吗 . 也就是说,如果您运行:

    git fetch origin master
    

    把他们的 主人 ,不要碰我的任何分行名称。 你的 ,而不是他们的!当然,你可以:

    git fetch origin master:newbranch
    

    新的 存储库中名为 newbranch 起源 主人 原点/主 git fetch origin master 更新 原点/主 .

    git推送 使用与源相同的名称 ,所以 git push origin master 方法 git push origin master:master 吉特。不,你用的是 直接!)

    自动更新 你的 主人 (假设标准 取回= 行,再次)。在Git版本之前, git获取源主机 真的把他们的 主人 然后。。。不更新 有什么事吗 .

    好吧,那是个谎言, 因为 git获取 总是 自古以来 .git/FETCH_HEAD git拉 回到画面,但首先,让我们完成这个场景。

    如果没有参考规范,就服从你的要求 取回= 设置

    如果你跑了 git fetch origin git获取 没有任何参数和Git选择 作为遥控器,Git将使用 设置以决定要获取的内容。通常的设置是“将所有分支名称带过来,并将它们重命名为远程跟踪名称”,这是由标准refspec强制执行的,带有两个星号和前导加号。

    另一个有点不正常,但不是疯狂怪异, 取来 git clone --single-branch . 而不是 +refs/heads/*:refs/remotes/origin/* ,上面写着 +refs/heads/somebranch:refs/remotes/origin/somebranch . 这就是你克隆的原因 单个分支: 每一个 取来 运行时,不带任何参数,只从Git中的一个分支更新一个远程跟踪名称。还有更奇怪的 取来 线条创建者 git clone --mirror

    在这两种正常情况下,一个常规克隆,或者一个分支克隆 git获取 将带来所有或一个分支,并更新所有或一个远程跟踪名称。然后为了保持向后兼容性,Git将更新 .git/取头 ,塞进里面 全部的 .git/取头 文件内容根本没有实际用途。

    使用refspecs时,请遵守您的refspecs

    git获取源主机 . 这给了我们一个机会 参考规范 ,你的Git会服从的。你的Git会拿来的 他们的 主人 分支机构。然后你的Git会更新你的 原点/主 自动地, 如果 您的Git至少是1.8.4,因为这样做是明智的,而且 取回= 线路正常。

    每一个 即使是一个非常古老的吉特,你的吉特也会 .git/取头 只有 没有 更新 原点/主 原点/主 再一次 也许是这个 git获取源主机 是由 git拉

    git拉

    我们现在知道,如果你跑:

    git pull
    

    (假设这样做有用),Git将运行 git获取 然后运行第二个命令。如果你跑了 git拉 争论,争论 git获取 那个 git拉 运行来自配置:

    $ git config --get-regexp '^branch\.master\.'
    branch.master.remote origin
    branch.master.merge refs/heads/master
    

    git获取 那个 git拉

    git fetch origin refs/heads/master
    

    这个 部分来自 branch.master.remote refspec实际上就是 branch.master.merge

    git获取 ,我们现在知道了,会在 分支的提示提交,由于 部分。这反过来会根据需要带来其他主分支提交。我们的Git可能会也可能不会更新我们自己的Git ,这取决于我们的Git版本,但在所有情况下,我们的Git都会 .git/取头 主人 -分支提示提交。

    git拉 那么,威尔 把这个杂烩从鱼里弄出来 文件。 这个 是散列ID的来源 git拉 为了我们!有什么更新吗 原点/主 是偶然的 git拉

    我们还可以运行:

    git pull xyz
    

    在这种情况下,我们的Git将使用 xyz 作为远程名称,但仍将运行:

    git pull origin refs/heads/master
    

    因为 设置为 参考/主管/主管 . 所以这次 git获取 将调用下面列出的任何URL ,获取 他们的 ,或者更新我们的 refs/remotes/xyz/master 取决于我们的吉特葡萄酒。但我们的Git会再次写信给 .git/取头 实际的 哈希ID 为了他们的利益 主人 ,以及我们的 git拉 将此哈希ID传递给 合并分支

    我们可以跑:

    git pull anyremote foobar
    

    git拉

    git fetch anyremote foobar
    

    这将通过我们上面描述的所有花哨的匹配来决定是否要把他们的 refs/heads/foobar refs/tags/foobar . 但不管发生什么,如果 一点用都没有,它为 anyremote foobar ,然后我们的 git拉 合并分支 .

    git拉 一次又一次地刺伤用户

    鉴于用户已经学会运行:

    git pull origin master
    

    嘿,如果我能拉大师,为什么我不能拉大师和发展两者? 它们运行:

    git pull origin master develop
    

    git拉 不会的。 相反, 跑:

    git fetch origin master develop
    

    这个 git获取 命令尽职地召唤 ,带来其所需的任何提交 主人 ,带来其所需的任何提交 develop ,并可能更新远程跟踪名称,这取决于我们的Git年份。然后我们的 取来 .git/取头 ,写在里面 两个哈希ID .

    现在我们的 git拉 只有一个

    git merge -m $message $hash1 $hash2
    

    这会产生一个 章鱼合并 我们现在的分部。要获得两个独立的合并,用户 , git拉 应该 已经完成:

    git checkout master && git merge -m $message1 $hash1 &&
    git checkout develop && git merge -m $message2 $hash2
    

    两个明显的消息和两个散列。

    避免 git拉 . 它做了一些非常令人惊讶的事情。很多程序员都犯了这个错误,早在十多年前Git的早期,我就犯过,还有很多同事也犯过。今天的文档要好得多,所以可能会有更少的用户犯这个错误,但这仍然需要大量的解释才有意义。

        2
  •  1
  •   dtanabe    6 年前

    其他 地方的 分支不受 git pull . (但分支的相应指针指向 origin 已更新)。

    如果切换到另一个也已更新的分支,则可以使用 git merge git rebase git拉 只不过 git fetch && git merge 可以吗( 如果你的 pull.rebase 配置值设置为 true