首页 > 解决方案 > 拉入子模块,现在在子模块中有一个无关的提交

问题描述

ProjectB 是 projectA 的子模块。projectB 发生了一些开发,我更新了 projectA 的子模块以指向最新的 projectB 提交。为此,我将 projectB 的上游更改拉入了我的 projectA 子模块。我将更改提交给 projectA 中的子模块。我愉快地编码,但已经意识到我的 projectA 子模块的 git 状态是:

git status
# On branch master
# Your branch is ahead of 'origin/master' by 1 commit.
#   (use "git push" to publish your local commits)
#
nothing to commit, working directory clean

尽管从未对 projectA 子模块进行本地更改。我的子模块的 git 日志是:

git log --oneline --decorate
2703249 (HEAD, master) Bugfix L148: correctly filling raw_hitmap_neig_s
9f1db21 (origin/master, origin/HEAD) created QL2P.xml for MCgen
1a3dfe5 Changed column name of files for DB output.
...

2703249是最新的 projectB 提交,这些我都没有在本地完成。我想知道在这种情况下如何解释 git log,以及如何摆脱我的 repo 中的无关提交(不对 projectB 的远程进行任何类型的更改)。

根据这篇文章,在从子模块的远程获取更新部分中,我认为问题的根源与我拉入子模块的事实有关,但我不明白为什么会导致额外的提交,或如何解决。

标签: gitgit-submodules

解决方案


如评论中所述(并已针对此答案进行了修复),您只需要:

cd projectB && git fetch

获取子模块的origin/master更新。

原因是您的特定 Git 版本非常旧(Git 现在是 2.26)。低于 1.8.4 的 Git 版本(当然包括您的 1.8.3.1)有一个坏习惯,即origin/*在各种情况下不更新名称,特别是包括使用git pull.

每个 Git 都有某个存储库的独立副本。这包括子模块,它们只是 Git 存储库,将根据需要被一些更高级别的超级项目Git 存储库拉出。幸运的是,我们可以在这里忽略大部分子模块方面,因为它对我们正在讨论的一个特定问题没有影响。

虽然每个 Git 都是独立的——有自己的分支名称,独立于任何其他 Git 的分支名称——我们总是可以将一个 Git 连接到另一个 Git。我们通常通过将一些 Git 存储库连接到我们克隆的存储库来做到这一点。也就是说,我们通过运行. 如果我们再次连接到 URL,他们的Git 可能会有我们可以抓取的新内容。或者,如果我们有可以给他们的新东西,我们也可以这样做。不过,显然,我们将再次需要该 URL。git clone url

遥控器会为您记住 URL

Git 存储库会为您记住一个 URL。回想一下那个 URL,我们使用了一个remote,它只是一个简短的名称,例如origin——事实上,第一个远程的标准名称,我们从中做了我们的git clone origin。因此,大多数 Git 存储库都有一个origin短名称,用于表示我们当时实际输入的任何 URL——或者,对于子模块,超级项目用来运行git clone以创建子模块 Git 的 URL。

对于更复杂的情况,您可以添加更多遥控器:每个遥控器记住一个 URL。但我们只考虑一个远程案例。

连接 Git 的命令是git fetchgit push

要将您的 Git 连接到另一个,您将运行git fetchgit push. 怎么样git pull?好吧,git pull它只是做件事的包装器:它首先git fetch为您运行:

  • git fetch命令将您的 Git 连接到另一个 Git 并从中获取内容。您可以给它一个 URL、https://example.com/some/path/to/repo.git或简称origin— 更容易键入、易于记忆,并且有多种好处。或者你可以让 Git 找出它origin是唯一的遥控器,然后运行git fetch​​.

  • git push命令将您的 Git 连接到另一个 Git并向它们提供东西。与 一样git fetch,您可以给它一个 URL,或者漂亮的短origin远程名称。

换句话说,我们选择另一个 Git,以及转移的方向——没有双向的“从另一个 Git 获取东西并把东西交给另一个 Git”选项,尽管这样的东西可能很有用。如果我们想这样做,我们必须运行两个单独的命令。git fetch这些操作也不是很对称,但无论如何我们只会看这里。

当您使用 时git fetch,您的 Git 会连接到另一个 Git,并让它列出其所有分支和标签以及其他此类名称,以及有关它们的 Git 内部详细信息。1 如果您git fetch自己运行,您的 Git 会查看此内容并默认获取它们的所有分支名称,然后重命名它们:例如,它们master变为 your origin/master

然后,您的 Git 会获取他们拥有的任何新提交,而您没有,然后更新您的所有origin/*名称。这些origin/*名称是您的远程跟踪名称2 它们是你的 Git 对其他 Git 分支名称的记忆,这是你的 Git 上次调用他们的 Git 并带来新东西的时候。

要使此重命名生效,您必须使用短远程名称。 如果您使用 URL,Git 不知道在其分支名称前面添加哪个远程名称!无论如何,短名称通常更容易输入,因此大多数人只使用短名称,并origin/*更新他们的名称。

有时你可能并不想要所有的东西:origin/master例如,如果你只想要 . 你可以让你的 Git 只查看它们的master. 有时这可以节省一点时间:也许他们master根本没有更新,或者有一个小更新,而他们developexperimental分支有很多你不需要的新东西。

1.8.4 之前的 Git 版本在这里有所不同。 无论出于何种原因——Git 人在 1.8.4 之后改变了主意,我真的不明白他们在那之前的想法——他们设置了一些东西,以便在那些版本的 Git 中,git fetch origin master 没有更新origin/master,但git fetch origin 确实更新了origin/*.


1这在一些最新版本的 Git 中实际上是不同的,因为一些存储库有大量的分支和标签名称,如果你只是想这样做,这部分过程可能会浪费大量时间和网络带宽得到一件事。

2 Git 调用这些远程跟踪分支名称。不过,它们并不完全是分支名称,而且分支这个词在 Git 中已经被过度使用,所以我喜欢删除那个多余的词,并称它们为远程跟踪名称远程跟踪这两个词在 Git 中也被过度使用了,所以即使这样仍然不是很好,但我们必须给它们起个名字!


git pull

运行的git fetch那个git pull使用“只更新一个分支”选项。也就是说,它会计算出你的 Git 想要从他们的哪个分支中提取并且只询问那个分支。然后它执行第二步,即运行第二个 Git 命令——通常是——将其git merge实际合并到您自己的分支中。3

这是为了方便。毕竟,只做git fetch一件事:他们那里获取新的东西,更新(可能是在 1.8.4 之前的版本)origin/*过程中的部分或全部名称,以便您的 Git 现在记住他们拥有的东西。这无助于您完成自己的工作,因为您自己的工作通常发生在您的分支上。所以之后git fetch,你需要第二步:将他们的工作混合到我的分支中。第二步可能使用git merge,或者可能使用git rebase,或者你甚至可能做一些完全不同的事情——也许创建一个你自己的新分支——但几乎总是第二步。

因此,git pull,git fetch然后运行第二个 Git 命令。你可以——并且必须——提前选择第二个 Git 命令是git merge还是git rebase,而不知道会出现什么。我不是特别喜欢git pull,但是对于一些定义明确的工作流程,它确实更方便,如果它适合你,那很好。

然而,碰巧的是,在1.8.4之前git pull的 Git 版本中,运行git fetch的方式是你的origin/*名字肯定不会更新。因此,您每次都会遇到这种特殊行为。

如果您升级到 1.8.4 或更高版本,git pull 更新一个远程跟踪名称 - 例如,<code>origin/master - 对应于它获取的分支名称。或者,您可以避免git pull,就像我在 Git 1.6、4 时代学到的那样,只需手动执行这两个步骤。


3在使用子模块的时候,这个合并操作往往是完全不需要的。现代 Git 中的子模块支持比这些非常旧版本的 Git 中的子模块支持要好得多,您现在可以使用git submodule命令来获取更新,而不是手动一次进入每个子模块。即便如此,即使在现代 Git 中,它仍然有点粗糙,人们称这些sob模块是有原因的。:-) 细节可能会变得非常混乱。

4那时,git pull有一些坏的错误。我失去了他们几周的工作。据我所知,那些虫子再也没有回来,但由于我大多避免git pull,我不会看到它们。


推荐阅读