"git-pull develop" 会从开发中获取所有可访问的提交吗?

Will a "git-pull develop" fetch all the commits reacheable from develop?

我有一个问题,关于 git 将如何从远程提取更改,以及有多少历史记录。

我正在考虑为我的项目遵循 gitFlow 工作流程。我们有 80 名开发人员,我们将把我们的更改从功能分支集成到开发分支 - 通过拉取请求首先执行代码审查。

我们将需要(在本地)将我们的功能分支重新设置为开发(之上)的基础,以便我们集成所有最新的开发更改。因此,我们将经常开发。在这里,我不想获取其他队友的特性分支——也不想获取他们的提交历史。

现在,如果我拉开发,这个操作是否会带来在其他功能分支下发生的提交历史,如果它们可以从开发访问(通过合并提交)?

提前致谢:-)

编辑:我可能不够清楚:

  1. 我们在本地使用 rebase,因此开发分支上的拉取请求是可合并的。我们不使用合并,因为在执行代码审查时它可能 "pollute" 具有分支。如果 pull 请求被接受,我们将与非快进提交合并。

  2. 我知道我可以"git fetch origin develop"。这是我的问题:git pull origin develop 只是 "fetch" 蓝色提交还是绿色提交?见图git-pull-

我从一个完整的答案开始,但是方式太长了。

仅回答几个具体问题,您的担忧是真实的,但有点误导(不是您的错,因为 Git 文档很糟糕)。关键问题不在于 git fetch 获取什么,1 它是你合并的提交的 提交图 中的内容 git merge;当您选择 运行 git rebase 时,哪些提交会被复制,这再次取决于提交 graph 以及您提供给 [=23] 的参数=].

关键概念是可达性。像 origin/master 这样的名称(git fetch 更新)使提交可达,但提交(git fetch 引入)也使其他提交可达。可到达的提交使提交的整个提交链 "before" 可到达。合并提交,其中列出了多个父提交 ID,使 两个 (或更多)提交链可访问。


1当然,git fetch获取的内容,不可能达到(在您的副本中回购协议),因为它不存在(在您的回购协议副本中)。我怀疑这就是你的目标,但一般来说很难实现,而且也没有必要。


请记住 (1) 每个提交都由其 SHA-1 哈希 ID 标识,(2) 每个提交都包含其父提交的哈希 ID,以及 (3) 分支名称只是一个提交 ID 的名称。分支 name 经常得到一个新的 ID 塞入其中,以增加分支(添加常规或合并提交),或指向由 rebase 复制的提交。

然后,请记住 git rebase 通过 复制 提交来工作。这些副本具有新的不同 ID:

          A--B--C       [original mybranch, before rebase]
         /
...--o--o
         \
          o--o           <-- origin/theirbranch
              \
               A'-B'-C'   <-- mybranch [after rebase]

这保证没问题只要没有其他人有名称(分支或标签名称)或指向任何原始提交的提交ABC。如果他们确实有这样的名字,那些现有的名字可能会——也可能不会——继续指向原来的名字,而不是新的副本。 即使那样也可以 只要您现在不使用它们。如果并且当名称更新为指向新提交时,只要没有仍然可访问的提交指向旧提交,旧的就变得无关紧要。但是,如果现有 commits 指向 "outdated" 提交,这些提交将永远指向它们,因为提交是永久性的。2


2没有 Git 对象可以改变。这是Git做出的根本保证。但是,所有 Git 对象(包括提交)完全 无法访问的最终将被删除。 Git 有一个 "garbage collector"、git gc 可以做到这一点。这有点复杂,因为有许多宽限期技巧来保留对象:默认情况下,所有内容都有 14 天,并且引用(包括分支、标签和远程跟踪分支名称)可能有 reflog 条目,这使得原本无法访问的提交可以访问再次。默认情况下,reflog 条目本身会保留 30 天或 90 天,这取决于 另一个 可达性计算,将引用中的当前哈希值与 reflog 条目中的哈希值进行比较。只要 Git 认为这可能是个好主意,通常会自动调用垃圾收集器。


fetch

例如,假设您的 git fetchorigin/BobsBranch 引入您的存储库,并且它指向一些提交:

          B1-B2-B3    <-- origin/BobsBranch
         /
...--o--o             <-- origin/develop
         \
          C1-C2-C3    <-- my_independent_work

您可以随时更改您的工作。同时,Bob 可以对 BobsBranch 进行变基(尽管他可能需要将结果强制推送到服务器)。假设他完全放弃了这三个提交,转而支持一个新的 B4 提交。你 运行 git fetch 并选择一个新的,不同的 origin/BobsBranch;您的存储库现在有:

          B4          <-- origin/BobsBranch
         /
        | B1-B2-B3    [a reflog entry for origin/BobsBranch]
        |/
...--o--o             <-- origin/develop
         \
          C1-C2-C3    <-- my_independent_work

reflog-only 提交不会出现在 git log --allgitk --all 视图中,只要您从不使用任何这些 B* 提交,它们就不会造成伤害你以任何方式(嗯,他们 在你的存储库中占用了一点 space)。

为了避免将它们带过来即使它们是无害的,您可以运行git fetch按照说明避免将它们带过来。当你 运行 git pull 方便命令时, git pull 运行s git fetch 带指令只带过来 一个 origin/<em>whatever</em> 分支的可达提交,因此通常可以避免将它们带过来——当然,除非它们可以从您的 Git 是否需要,根据一个分支提示。

merge

当您合并到 "reaches" 稍后由 rebase 复制的提交时,会发生 "bad" 情况。例如,假设你有这个:

...--o--o--A--B   <-- origin/feature_X
         \
          C--D    <-- feature_Y

现在您决定是时候将 origin/feature_X 的提交(AB)合并到您的 feature_Y 中,因此您进行合并提交:

...--o--o--A--B   <-- origin/feature_X
         \     \
          C--D--o   <-- feature_Y

如果其他人(上游)决定变基并强制推送他们的 feature_X,以便您的 origin/feature_X 指向新副本,您最终会得到:

          o--A'-B'  <-- origin/feature_X
         /
...--o--o--A--B
         \     \
          C--D--o   <-- feature_Y

即使没有 name 附加到 rebase 复制的提交,如果你通过 its 选择了其他东西,这也会发生姓名。例如,如果其他人推送 feature_F 并承诺完成:

       A----B
      /      \
...--o--o--E--F   <-- origin/feature_F
         \
          C--D    <-- feature_Y

然后你合并它,你得到这个:

       A----B
      /      \
...--o--o--E--F   <-- origin/feature_F
         \     \
          C--D--o   <-- feature_Y

现在假设他们,或者第三个人,然后 rebase 一个分支 他们 有那个指向 B,没有意识到/记住那个提交 F本身指向B。也就是说,他们从这个开始(注意他们没有你的feature_Y):

       A----B     <-- myhacks
      /      \
...--o--o--E--F   <-- feature_F, origin/feature_F

然后决定将 myhacks 变基到提交 E 会更好,所以他们 运行:

$ git checkout myhacks
$ git rebase 123e4567    # <id-of-E>

产生:

       A----B
      /      \
...--o--o--E--F      <-- feature_F, origin/feature_F
            \
             A'-B'   <-- myhacks

最终,当您获取(可能通过 git pull)并获得他们最终版本的 myhacks 时——不管它当时是否有名字,只要它有提交 A'B'——你将拥有(并保留)原始的 A--B 提交,通过提交 F,并添加 A'-B' 链,即使你可能永远不会看过他们的分店-名字 myhacks.

结论

我们在上面看到的 "bad" 案例发生在 git fetch 通过名称引入提交 F 时(在您从中获取的存储库中,大概是一个存储在中央服务器)feature_F。 (你和你的 Git 重命名为 origin/feature_F。)问题不是 feature_F(或 origin/feature_F)本身,而是 myhacks:一个名字既不你,也不是中央服务器,曾经见过!确实有那个名字的人——或者甚至可能是事后编造的——用它来 copy 提交 AB,而不考虑谁有原件。然后他推送副本,也许在 另一个 名称下。

名称在 fetchpush 时间很重要,因为 git fetchgit push 传输由 refspecs 提交(主要只是对参考名称,加上一些辅助的东西)。不过,在那之前和之后,名称主要是分散注意力:重要的是提交集,由它们的 ID 和它们的可达性状态命名。

will git pull origin develop just "fetch" the blue commits or also the green ones?

Git 2.19(2018 年第 3 季度)在获取提交时添加了两项改进,一项在客户端,一项在服务器端(提醒,fetch 由 git pull 调用)。
这会影响“reachability”的完成方式,但不会解决 torek 提到的问题。

第一个:

"git fetch" 学习了一个新选项 "--negotiation-tip" 来限制它告诉另一端为 "have" 的提交集,以减少浪费的带宽和周期,这会很有帮助当接收存储库有很多与它正在从中获取的远程历史记录无关的引用时。

参见 commit 3390e42 (02 Jul 2018) by Jonathan Tan (jhowtan)(由 Junio C Hamano -- gitster -- in commit 30bf8d9 合并,2018 年 8 月 2 日)

fetch-pack: support negotiation tip whitelist

During negotiation, fetch-pack eventually reports as "have" lines all commits reachable from all refs. Allow the user to restrict the commits sent in this way by providing a whitelist of tips; only the tips themselves and their ancestors will be sent.

Both globs and single objects are supported.

This feature is only supported for protocols that support connect or stateless-connect (such as HTTP with protocol v2).

This will speed up negotiation when the repository has multiple relatively independent branches (for example, when a repository interacts with multiple repositories, such as with linux-next and torvalds/linux), and the user knows which local branch is likely to have commits in common with the upstream branch they are fetching.


其次,Git 将一次获取更多提交:

Git 添加了一个服务器端旋钮以跳过 exponential/fibbonacci 中的提交,试图以较少的迭代次数覆盖更广泛的历史, 可能接受更大的 packfile 传输,而不是在“git fetch”事务.

期间的共同祖先发现期间一次返回一次提交

参见 commit 42cc748 (16 Jul 2018) by Jonathan Tan (jhowtan)(由 Junio C Hamano -- gitster -- in commit 7c85ee6 合并,2018 年 8 月 2 日)

negotiator/skipping: skip commits during fetch

Introduce a new negotiation algorithm used during fetch that skips commits in an effort to find common ancestors faster.
The skips grow similarly to the Fibonacci sequence as the commit walk proceeds further away from the tips. The skips may cause unnecessary commits to be included in the packfile, but the negotiation step typically ends more quickly.

Usage of this algorithm is guarded behind the configuration flag fetch.negotiationAlgorithm.


注意:如 Git 2.24 中所述,fetch.negotiationAlgorithm 等设置仍处于实验阶段。

参见 commit aaf633c, commit c6cc4c5, commit ad0fb65, commit 31b1de6, commit b068d9a, commit 7211b9e (13 Aug 2019) by Derrick Stolee (derrickstolee)
(由 Junio C Hamano -- gitster -- in commit f4f8dfe 合并,2019 年 9 月 9 日)

repo-settings: create feature.experimental setting

The 'feature.experimental' setting includes config options that are not committed to become defaults, but could use additional testing.

Update the following config settings to take new defaults, and to use the repo_settings struct if not already using them:

  • 'pack.useSparse=true'
  • 'fetch.negotiationAlgorithm=skipping'

In the case of fetch.negotiationAlgorithm, the existing logic would load the config option only when about to use the setting, so had a die() statement on an unknown string value.
This is removed as now the config is parsed under prepare_repo_settings().


在 Git 2.24(2019 年第 4 季度)中,引入了一种影响(相关)配置变量组默认设置的机制。

参见 commit aaf633c, commit c6cc4c5, commit ad0fb65, commit 31b1de6, commit b068d9a, commit 7211b9e (13 Aug 2019) by Derrick Stolee (derrickstolee)
(由 Junio C Hamano -- gitster -- in commit f4f8dfe 合并,2019 年 9 月 9 日)

repo-settings: create feature.experimental setting

Signed-off-by: Derrick Stolee

The 'feature.experimental' setting includes config options that are not committed to become defaults, but could use additional testing.

Update the following config settings to take new defaults, and to use the repo_settings struct if not already using them:

  • 'pack.useSparse=true'

  • 'fetch.negotiationAlgorithm=skipping'

In the case of fetch.negotiationAlgorithm, the existing logic would load the config option only when about to use the setting, so had a die() statement on an unknown string value.
This is removed as now the config is parsed under prepare_repo_settings().
In general, this die() is probably misplaced and not valuable. A test was removed that checked this die() statement executed.


Git 2.29(2020 年第 4 季度)更新了延迟克隆存储库中的按需获取代码。

参见 commit db3c293 (02 Sep 2020), and commit 9dfa8db, commit 7ca3c0a, commit 5c3b801, commit abcb7ee, commit e5b9421, commit 2b713c2, commit cbe566a (17 Aug 2020) by Jonathan Tan (jhowtan)
(由 Junio C Hamano -- gitster -- in commit b4100f3 合并,2020 年 9 月 3 日)

negotiator/noop: add noop fetch negotiator

Signed-off-by: Jonathan Tan

Add a noop fetch negotiator.

This is introduced to allow partial clones to skip the unneeded negotiation step when fetching missing objects using a "git fetch"(man) subprocess.
(The implementation of spawning a "git fetch"(man) subprocess will be done in a subsequent patch.)
But this can also be useful for end users, e.g. as a blunt fix for object corruption.

git config 现在包含在其 man page 中:

fetch.negotiationAlgorithm:

Control how information about the commits in the local repository is sent when negotiating the contents of the packfile to be sent by the server.

Set to "skipping" to use an algorithm that skips commits in an effort to converge faster, but may result in a larger-than-necessary packfile;
or set to "noop" to not send any information at all, which will almost certainly result in a larger-than-necessary packfile, but will skip the negotiation step.


随着 Git 2.32(2021 年第 2 季度),“git push"(man) 通过协议 v2 学习发现与接收端的共同祖先。

参见 commit 6db01a7 (08 Apr 2021) by Junio C Hamano (gitster)
参见 commit 477673d, commit 9c1e657 (04 May 2021), and commit 6871d0c, commit 57c3451, commit 8102570 (08 Apr 2021) by Jonathan Tan (jhowtan)
(由 Junio C Hamano -- gitster -- in commit 644f4a2 合并,2021 年 5 月 16 日)

fetch: teach independent negotiation (no packfile)

Signed-off-by: Jonathan Tan

Currently, the packfile negotiation step within a Git fetch cannot be done independent of sending the packfile, even though there is at least one application wherein this is useful.
Therefore, make it possible for this negotiation step to be done independently.
A subsequent commit will use this for one such application - push negotiation.

This feature is for protocol v2 only.
(An implementation for protocol v0 would require a separate implementation in the fetch, transport, and transport helper code.)

In the protocol, the main hindrance towards independent negotiation is that the server can unilaterally decide to send the packfile.
This is solved by a "wait-for-done" argument: the server will then wait for the client to say "done".
In practice, the client will never say it; instead it will cease requests once it is satisfied.

In the client, the main change lies in the transport and transport helper code.
fetch_refs_via_pack() performs everything needed - protocol version and capability checks, and the negotiation itself.

There are 2 code paths that do not go through fetch_refs_via_pack() that needed to be individually excluded: the bundle transport (excluded through requiring smart_options, which the bundle transport doesn't support) and transport helpers that do not support takeover.
If or when we support independent negotiation for protocol v0, we will need to modify these 2 code paths to support it.
But for now, report failure if independent negotiation is requested in these cases.

technical/protocol-v2 现在包含在其 man page 中:

If the 'wait-for-done' feature is advertised, the following argument can be included in the client's request.

wait-for-done

Indicates to the server that it should never send "ready", but should wait for the client to say "done" before sending the packfile.


在 Git 2.33(2021 年第 3 季度)之前,最近添加的代码在“git push"(man) 期间没有足够仔细地检查其参数。

参见 commit eff4045 (08 Jul 2021), and commit 60fadf8, commit 1e5b5ea (30 Jun 2021) by Ævar Arnfjörð Bjarmason (avar)
(由 Junio C Hamano -- gitster -- in commit b2fc822 合并,2021 年 7 月 16 日)

fetch: fix segfault in --negotiate-only without --negotiation-tip=*

Signed-off-by: Ævar Arnfjörð Bjarmason

The recent --negotiate-only option would segfault in the call to oid_array_for_each() in negotiate_using_fetch() unless one or more --negotiation-tip=* options were provided.

All of the other tests for the feature combine both, but nothing was checking this assumption, let's do that and add a test for it.
Fixes a bug in 9c1e657 ("fetch: teach independent negotiation (no packfile)", 2021-05-04, Git v2.32.0-rc0 -- merge).

并且:

fetch: document the --negotiate-only option

Signed-off-by: Ævar Arnfjörð Bjarmason

There was no documentation for the --negotiate-only option added in 9c1e657 ("fetch: teach independent negotiation (no packfile)", 2021-05-04, Git v2.32.0-rc0 -- merge), only documentation for the related push.negotiation option added in the following commit in 477673d ("send-pack: support push negotiation", 2021-05-04, Git v2.32.0-rc0 -- merge).

Let's document it, and update the cross-linking I'd added between --negotiation-tip=* and 'fetch.negotiationAlgorithm' in 5266082 ("fetch doc: cross-link two new negotiation options", 2018-08-01, Git v2.19.0-rc0 -- merge listed in batch #7).

I think it would be better to say "in common with the remote" here than "...the server", but the documentation for --negotiation-tip=* above this talks about "the server", so let's continue doing that in this related option.
See 3390e42 ("fetch-pack: support negotiation tip whitelist", 2018-07-02, Git v2.19.0-rc0 -- merge) for that documentation.

git config 现在包含在其 man page 中:

See also the --negotiate-only and --negotiation-tip options to git fetch.

fetch-options 现在包含在其 man page 中:

See also the fetch.negotiationAlgorithm and push.negotiate configuration variables documented in git config, and the --negotiate-only option below.

--negotiate-only

Do not fetch anything from the server, and instead print the ancestors of the provided --negotiation-tip=* arguments, which we have in common with the server.

Internally this is used to implement the push.negotiate option, see git config.


在 Git 2.34(2021 年第 4 季度)中,最近在“git push"(man) 代码路径中引入的共同祖先协商已得到修复。

参见 commit 8282311, commit 54a03bc, commit 74fab8f (15 Jul 2021) by Jonathan Tan (jhowtan)
(由 Junio C Hamano -- gitster -- in commit 066f6cd 合并,2021 年 8 月 24 日)

fetch: die on invalid --negotiation-tip hash

Signed-off-by: Jonathan Tan

If a full hexadecimal hash is given as a --negotiation-tip togit fetch"(man), and that hash does not correspond to an object, "git fetch" will segfault if --negotiate-only is given and will silently ignore that hash otherwise.
Make these cases fatal errors, just like the case when an invalid ref name or abbreviated hash is given.