拉入子模块,现在在子模块中有一个无关的提交

Pulled into submodule and now have an extraneous commit in submodule

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 提交,其中 none 我在本地完成。我想知道在这种情况下如何解释 git 日志,以及如何摆脱我的回购中的无关提交(不对 projectB 的远程进行任何更改)。

基于此 article,在部分中,从子模块的远程获取更新 我认为问题的根源与我拉入我的子模块,但我不明白为什么会导致额外提交,或如何修复。

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

cd projectB && git fetch

更新子模块的 origin/master

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

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

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

遥控器为您记住URL

一个 Git 存储库将为您记住一个URL。回想一下 URL,我们使用 remote,它只是一个短名称,如 origin——事实上,第一个遥控器的标准名称,来自我们所做的 git clone origin。因此,大多数 Git 存储库都有一个 origin 短名称,用于我们当时实际输入的任何内容 URL — 或者,对于子模块, URL 超级项目用于 运行 git clone 创建子模块 Git.

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

连接Git的命令是git fetchgit push

要将您的 Git 连接到另一个,您需要 运行 git fetchgit pushgit pull 呢?嗯,git pull 只是一个包装器,可以做 两件事 :它从 运行ning 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 变成你的 origin/master,例如。

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

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

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

Git 1.8.4 之前的版本在这里有所不同。 无论出于何种原因,Git 人从 1.8.4 开始改变了主意,并且我真的不明白他们在那之前在想什么——他们设置的是 Git、git fetch origin master didn't update origin/master,但 git fetch origin 已更新 origin/*


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

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


git pull

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

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

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

不过,在 Git 版本中 before 1.8.4,git pull 运行s git fetch in这样一来,您的 origin/* 名称绝对 不会 得到更新。所以你每次都会遇到这种特殊行为。

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


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

4那时候,git pull有一些严重的错误。我失去了几个星期的工作给他们。据我所知,那些 bug 再也没有出现过,但由于我主要避免 git pull,所以我不会看到它们。