git - 子模块和 'git pull --rebase'

标签 git macos git-submodules git-pull

我们最近切换到 git 并尝试使用子模块来包含我们的公共(public)库。

无论我们做什么,我们都无法让“git pull --rebase”在 super 模块或子模块中工作。

我们得到:

james:libraries james$ git pull --rebase
Cannot pull with rebase: You have unstaged changes.
Please commit or stash them.

即使我们根本没有任何本地更改并且有一个干净的目录,也会发生这种情况。对我们可能做错了什么有什么想法吗?

谢谢! 詹姆斯

最佳答案

We can not get 'git pull --rebase' to work in the Super or Submodule

要确保涉及子模块,请使用 Git 2.14(2017 年第 3 季度,OP 问题后 6 年)或更多带有“git pull --rebase --recurse-submodules”,它学会了将子模块中的分支 rebase 到更新的基础。

参见 commit e8906a9 (2017 年 6 月 27 日),和 commit a6d7eb2 , commit 8c69832 , commit 886dc15 (2017 年 6 月 23 日)作者:Stefan Beller (stefanbeller) .
(由 Junio C Hamano -- gitster -- merge 于 commit c9c63ee ,2017 年 7 月 13 日)

pull: optionally rebase submodules (remote submodule changes only)

Signed-off-by: Brandon Williams
Signed-off-by: Stefan Beller

Teach pull to optionally update submodules when '--recurse-submodules' is provided.

This will teach pull to run 'submodule update --rebase' when the '--recurse-submodules' and '--rebase' flags are given under specific circumstances.

On a rebase workflow:

  1. Both sides change the submodule

Let's assume the following history in a submodule:

H---I---J---K---L local branch
     \
      M---N---O---P remote branch

and the following in the superproject (recorded submodule in parens):

A(H)---B(I)---F(K)---G(L)  local branch
        \
         C(N)---D(N)---E(P) remote branch

In an ideal world this would rebase the submodule and rewrite the submodule pointers that the superproject points at such that the superproject looks like

A(H)---B(I)              F(K')---G(L')  rebased branch
        \               /
         C(N)---D(N)---E(P) remote branch

and the submodule as:

      J---K---L (old dangeling tip)
     /
H---I               J'---K'---L' rebased branch
     \             /
      M---N---O---P remote branch

And if a conflict arises in the submodule the superproject rebase would stop at that commit at which the submodule conflict occurs.

Currently a "pull --rebase" in the superproject produces a merge conflict as the submodule pointer changes are conflicting and cannot be resolved.


  1. Local submodule changes only

Assuming histories as above, except that the remote branch would not contain submodule changes, then a result as

A(H)---B(I)              F(K)---G(L)  rebased branch
        \               /
         C(I)---D(I)---E(I) remote branch

is desire-able. This is what currently happens in rebase.

If the recursive flag is given, the ideal git would produce a superproject as:

A(H)---B(I)               F(K')---G(L')  rebased branch (incl. sub rebase!)
         \               /
          C(I)---D(I)---E(I) remote branch

and the submodule as:

      J---K---L (old dangeling tip)
     /
H---I               J'---K'---L' Locally rebased branch
     \             /
      M---N---O---P advaced branch

This patch doesn't address this issue, however a test is added that this fails up front.


  1. Remote submodule changes only

Assuming histories as in (1) except that the local superproject branch would not have touched the submodule the rebase already works out in the superproject with no conflicts:

A(H)---B(I)              F(P)---G(P)  rebased branch (no sub changes)
        \               /
         C(N)---D(N)---E(P) remote branch

The recurse flag as presented in this patch would additionally update the submodule as:

H---I               J'---K'---L' rebased branch
     \             /
      M---N---O---P remote branch

As neither J, K, L nor J', K', L' are referred to from the superproject, no rewriting of the superproject commits is required.


Conclusion for 'pull --rebase --recursive'

If there are no local superproject changes it is sufficient to call "submodule update --rebase" as this produces the desired results.
In case of conflicts, the behavior is the same as in 'submodule update --recursive' which is assumed to be sane.

This patch implements (3) only.


On a merge workflow:

We'll start off with the same underlying DAG as in (1) in the rebase workflow.
So in an ideal world a 'pull --merge --recursive' would produce this:

H---I---J---K---L---X
     \             /
      M---N---O---P

with X as the new merge-commit in the submodule and the superproject as:

A(H)---B(I)---F(K)---G(L)---Y(X)
        \                  /
         C(N)---D(N)---E(P)

However modifying the submodules on the fly is not supported in git merge such that Y(X) is not easy to produce in a single patch. In fact git merge doesn't know about submodules at all.

However when at least one side does not contain commits touching the submodule at all, then we do not need to perform the merge for the submodule but a fast-forward can be done via checking out either L or P in the submodule.

This strategy is implemented in 68d03e4a6e ("Implement automatic fast-forward merge for submodules", 2010-07-07, Git v1.7.3-rc0 -- merge) already, so to align with the rebase behavior we need to also update the worktree of the submodule.


在 Git 2.27(2020 年第 2 季度)之前,“git pull --rebase”尝试运行一个 rebase,即使在注意到 pull 导致快进并且不需要 rebase 也不明智的情况下,在过去的几年里,由于一个没有人注意到的错误。

参见 commit fbae70d (2020 年 3 月 27 日)Elijah Newren (newren) .
(由 Junio C Hamano -- gitster -- merge 于 commit dfdce31 ,2020 年 4 月 22 日)

pull: avoid running both merge and rebase

Signed-off-by: Elijah Newren

When opt_rebase is true, we still first check if we can fast-forward.
If the branch is fast-forwardable, then we can avoid the rebase and just use merge to do the fast-forward logic.

However, when commit a6d7eb2c7a ("pull: optionally rebase submodules (remote submodule changes only)", 2017-06-23, Git v2.14.0-rc0 -- merge) added the ability to rebase submodules, it accidentally caused us to run BOTH a merge and a rebase.

Add a flag to avoid doing both.

This was found when a user had both pull.rebase and rebase.autosquash set to true.

In such a case, the running of both merge and rebase would cause ORIG_HEAD to be updated twice (and match HEAD at the end instead of the commit before the rebase started), against expectation.


git pull --rebase --recurse-submodules”检查本地更改 Git 2.30(2020 年第 1 季度)修复了错误的范围并未能在应有的情况下正确运行

pull: check for local submodule modifications with the right range

Ever since 'git pull' learned '--recurse-submodules' in a6d7eb2 (pull: optionally rebase submodules (remote submodule changes only), 2017-06-23, Git v2.14.0-rc0), we check if there are local submodule modifications by checking the revision range 'curr_head --not rebase_fork_point'.

The goal of this check is to abort the pull if there are submodule modifications in the local commits being rebased, since this scenario is not supported.

However, the actual range of commits being rebased is not 'rebase_fork_point..curr_head', as the logic in 'get_rebase_newbase_and_upstream' reveals, it is 'upstream..curr_head'.

If the 'git merge-base --fork-point' invocation in 'get_rebase_fork_point' fails to find a fork point between the current branch and the remote-tracking branch we are pulling from, 'rebase_fork_point' is null and since 4d36f88 (submodule: do not pass null OID to setup_revisions, 2018-05-24, Git v2.18.0-rc1), 'submodule_touches_in_range' checks 'curr_head' and all its ancestors for submodule modifications.

Since it is highly likely that there are submodule modifications in this range (which is in effect the whole history of the current branch), this prevents 'git pull --rebase --recurse-submodules' from succeeding if no fork point exists between the current branch and the remote-tracking branch being pulled.
This can happen, for example, when the current branch was forked from a commit which was never recorded in the reflog of the remote-tracking branch we are pulling, as the last two paragraphs of the "Discussion on fork-point mode" section in git-merge-base explain.

Fix this bug by passing 'upstream' instead of 'rebase_fork_point' as the 'excl_oid' argument to 'submodule_touches_in_range'.


在 Git 2.32(2021 年第 2 季度)中,sHA-256 转换对子模块有影响,当使用 null oid 时:

参见 commit 3dd7146 , commit b8505ec , commit 71b7672 , commit 72871b1 , commit dd15f4f , commit 1422844 , commit 5a6dce7 , commit 0e5e228 , commit 5951bf4 , commit ab795f0 , commit c3b4e4e , commit 92e2cab , commit cf09832 (2021 年 4 月 26 日)brian m. carlson (bk2204) .
(由 Junio C Hamano -- gitster -- merge 于 commit aaa3c80 ,2021 年 5 月 10 日)

hash: provide per-algorithm null OIDs

Signed-off-by: brian m. carlson

Up until recently, object IDs did not have an algorithm member, only a hash.
Consequently, it was possible to share one null (all-zeros) object ID among all hash algorithms.
Now that we're going to be handling objects from multiple hash algorithms, it's important to make sure that all object IDs have a correct algorithm field.

Introduce a per-algorithm null OID, and add it to struct hash_algo.
Introduce a wrapper function as well, and use it everywhere we used to use the null_oid constant.

关于git - 子模块和 'git pull --rebase',我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6791705/

相关文章:

Java Eclipse 文件夹与 git

android - Glide Process 'command ' git'' 以非零退出值 128 完成

macos - 如何在用户点击时创建文本字段

git 子模块 : customization

git - 从文件夹创建子模块存储库并保留其 git 提交历史

git - 将现有的 Git 存储库推送到 Github 只会发送大约一半的提交?

github文件夹上面有个白色箭头打不开

python - 为什么 'pip show' 或 'pip list' 对我不起作用?

python - OSX 上的 tensorflow : Failed to load the native TensorFlow runtime (No module named pywrap_tensorflow_internal)

git - check out 带有子模块的 git 存储库时出错