如何静默地“git 拉取”次级子模块,以便不需要更多提交?

How to `git pull` secondary submodules silently so that it does not require any more commits?

我对子模块和 git 比较陌生,但我一直在使用它们将我所有的 tmux 和 vim 插件等包含在 [=41] 上我自己的点文件存储库中=]枢纽。

这种情况并不经常发生,但有时当我拉取我的点文件存储库时,我的许多子模块文件都发生了变化。例如,在我最近的 git fetch 中,我得到了这样的东西(删除了几个插件更新以使其更短):

remote: Enumerating objects: 32, done.
remote: Counting objects: 100% (32/32), done.
remote: Compressing objects: 100% (8/8), done.
remote: Total 21 (delta 11), reused 20 (delta 10), pack-reused 0
Unpacking objects: 100% (21/21), 2.65 KiB | 14.00 KiB/s, done.
From https://github.com/someone/dotfiles
   fb997fe..2bffa27  master     -> origin/master
Fetching submodule tmux/plugins/tmux-yank
From https://github.com/tmux-plugins/tmux-yank
   d776f4e..1b1a436  master     -> origin/master
Fetching submodule vim/bundle/ultisnips
From https://github.com/SirVer/ultisnips
   7941f98..d3b36cd  master     -> origin/master
Fetching submodule vim/bundle/vimtex
From https://github.com/lervag/vimtex
   9b53bb31..49eab5d5  master     -> origin/master
 * [new tag]           v1.4       -> v1.4

合并期间:

herophant:~/.dotfiles$ git merge
Updating fb997fe..2bffa27
Fast-forward
 .gitmodules                   |  9 +++++++++
 bashrc                        |  6 ++++++
 tmux/plugins/tmux-yank        |  2 +-
 vim/UltiSnips/tex.snippets    |  8 ++++++++
 vim/bundle/rainbow            |  1 +
 vim/bundle/syntastic          |  1 +
 vim/bundle/ultisnips          |  2 +-
 vim/bundle/vim-airline        |  2 +-
 vim/bundle/vim-airline-themes |  2 +-
 vim/bundle/vim-fugitive       |  2 +-
 vim/bundle/vim-racket         |  1 +
 vim/bundle/vimtex             |  2 +-
 vim/ftplugin/tex.vim          |  1 +
 vimrc                         | 20 ++++++++++++++++++--
 14 files changed, 51 insertions(+), 8 deletions(-)
 create mode 160000 vim/bundle/rainbow
 create mode 160000 vim/bundle/syntastic
 create mode 160000 vim/bundle/vim-racket
 create mode 100644 vim/ftplugin/tex.vim

现在我的 git 状态(短格式)显示:

## master...origin/master
 M .gitmodules
 M tmux/plugins/tmux-yank
 M vim/bundle/ultisnips
 M vim/bundle/vim-airline
 M vim/bundle/vim-airline-themes
 M vim/bundle/vim-fugitive
 M vim/bundle/vimtex

我真的不在乎子模块发生了什么,但如果它们得到更新,那可能是一件好事。我只是希望他们默默地做这件事,这样我就不必再做一次承诺来解决这个问题。有办法实现吗?

编辑:即使我将此更改提交到原始哈希,在一台机器上提交一个原始提交哈希会导致其他机器在 git pull 之后将这些提交哈希“修改”到它们以前的版本。

例如,vim-airline-themes 是昨天修改的。我不确定发生了什么,但是在 git commit、vim-airline-themes 之后立即显示已修改。好的,我再做一次提交。这就是改变的地方

me@main-machine:~/.dotfiles$ git diff 4577802~ 4577802
diff --git a/vim/bundle/vim-airline-themes b/vim/bundle/vim-airline-themes
index e1b0d9f..7f53ebc 160000
--- a/vim/bundle/vim-airline-themes
+++ b/vim/bundle/vim-airline-themes
@@ -1 +1 @@
-Subproject commit e1b0d9f86cf89e84b15c459683fd72730e51a054
+Subproject commit 7f53ebc8f7af2fd7e6a0a31106b99491e01cd18f

我承诺,再做一些 git pulls 只是为了确定,并且“没有新的变化”。我转到我的另一台机器,从我的 dotfiles 存储库中执行 git pull,然后看到 /vim/bundle/vim-airline-themes 子模块已被修改。改变了什么?

me@other-machine:~/.dotfiles$ git diff vim/bundle/vim-airline-themes
diff --git a/vim/bundle/vim-airline-themes b/vim/bundle/vim-airline-themes
index 7f53ebc..e1b0d9f 160000
--- a/vim/bundle/vim-airline-themes
+++ b/vim/bundle/vim-airline-themes
@@ -1 +1 @@
-Subproject commit 7f53ebc8f7af2fd7e6a0a31106b99491e01cd18f
+Subproject commit e1b0d9f86cf89e84b15c459683fd72730e51a054

Git 似乎出于某种原因想要撤消它自己的更改?这一切看起来都是多余的,而且我相信这可以通过某种方式避免。解决方法是什么?

简短的回答是否定的。更具体地说:

I just want them to do this silently so that I don't have to make another commit to account for this.

您确实需要重新提交!子模块是另一个 Git 存储库,因此,它分两部分实现:

  • 首先(在许多方面不那么重要),您将在超级项目的 work-tree 的顶层有一个名为 .gitmodules 的文件。这个文件进入每个提交,它存储一个新的 superproject-clone 需要的信息,以便 运行 它自己的每个子模块的 git clone

  • 其次 - 也是您需要进行新提交的原因 - 每个 commit 存储一个 data-pair,包括:

    • 子模块的路径,
    • 要使用的原始提交哈希 ID 子模块中。

超级项目 Git 使用这些哈希 ID 来了解每个子模块中 git checkout 的内容。子模块存储库由 其超级项目控制,由超级项目 Git 执行:

(cd $path && git checkout $hash)

在指定提交的子模块中获取 detached-HEAD。 $path$hash 来自超级项目提交。

各个子模块中的每一个都得到更新的事实并不是这里的触发器。您(大概)想要 在 子模块 中使用最新提交 的事实是触发器:您需要 record 这个事实在你的超级项目中。为此需要在超级项目 Git.

中进行新的提交

编辑: 如下面的评论所述,要从哪个子模块 repository/ies 中准确决定要提交的内容可能会变得非常棘手。我个人不喜欢 git pull 但它确实有一个在这里很有用的功能:你可以让它递归地进入每个子模块和 运行 另一个 git pull 在每个子模块中。不过那是一把 multi-edged 剑,因为你可能不想要一把 本身。

您可以在超级项目中 运行 的 git submodule 命令也有多种操作模式:git submodule update --remote 有您的超级项目 运行 a [=每个子模块中的 20=] 后跟每个子模块中的 git checkout。将此与不带 --remotegit submodule update 进行比较,后者将 运行 每个子模块中的 git fetch 后跟每个子模块中的 git checkout。这提出了一个明显(但很好)的问题:鉴于我们只是说两者做同样的事情,确切地说,区别是什么?答案在于 git checkout 使用的哈希 ID。没有 --remote 的那个使用 superproject 要求的哈希 ID。带有 --remote 的那个使用哈希 ID,该哈希 ID 与相关子模块中的某些 remote-tracking 名称 一起使用,由 git fetch 运行 在该子模块中。

涉及到如此多的存储库——一个用于超级项目,一个用于超级项目的远程,每个子模块一个,每个子模块一个远程,所有这些都在这里使用——它变得混乱。再加上每个子模块都可以是一个或多个它自己的子模块的超级项目,并且这些操作中的每一个都可以递归,也可以不递归,这简直是一场噩梦。

这种事情是子模块有时被称为“sob-modules”的挥之不去的原因之一。子模块的许多最痛苦的方面是......好吧,我现在不会说 fixed,但比十年前不那么痛苦了。但是 Git 由于是分布式的,本来就很棘手,而子模块只会使这种情况呈指数级恶化。

所有这些都没有单一的治疗方法:这确实是一个难题,您只需要投入大量 skull-sweat 即可。如果您的各个子模块的作者协调他们的工作,那会有所帮助。