【问题标题】:Git: track branch in submodule but commit in other submodule (possibly nested)Git:跟踪子模块中的分支,但在其他子模块中提交(可能嵌套)
【发布时间】:2016-10-19 04:22:34
【问题描述】:

我正在寻找一种情况,其中我有一个带有(可能嵌套的子模块)的 git 结构。对于这些子模块中的每一个,我想分别指定它们是否应该跟踪一个分支 (参见例如Git submodules: Specify a branch/tag

例如,我的项目可能如下所示:

main.tex
|- submod1 @ master
|    |-subsubmod1 @qsdf123
|- submod2 @ master
|    |-subsubmod2 @shasha12
|- submod3 @ qsdf321

现在,我想要一种方法来更新我的子模块。

git submodule update --recursive

会将所有子模块更新为其最后记录的 sha(即,它将适用于 subsubmod1、subsubmod2 和 submod3,但对其余部分执行错误的操作。 另一方面

git submodule update --recursive --remote

会将所有子模块更新到关联的分支(默认情况下,master),即它适用于 submod1 和 submod2,但对其余部分执行错误的操作。

有没有办法很好地完成这项工作?

针对第一个答案,我将澄清“做错事”的意思。

这是一个小例子

bartb@EB-Latitude-E5450 ~/Desktop/test $ git init
Initialized empty Git repository in /home/bartb/Desktop/test/.git/
bartb@EB-Latitude-E5450 ~/Desktop/test $ git submodule add ../remote/ submod1
Cloning into 'submod1'...
done.
bartb@EB-Latitude-E5450 ~/Desktop/test $ git submodule add ../remote/ submod2
Cloning into 'submod2'...
done.
bartb@EB-Latitude-E5450 ~/Desktop/test $ cd submod1
bartb@EB-Latitude-E5450 ~/Desktop/test/submod1 $ git log
commit 42d476962fc4e25c64ff2a807d2bf9b3e2ea31f8
Author: Bart Bogaerts <bart.bogaerts@cs.kuleuven.be>
Date:   Tue Jun 21 08:56:05 2016 +0300

    init commit

commit db1ba3bc4b02df4677f1197dc137ff36ddfeeb5f
Author: Bart Bogaerts <bart.bogaerts@cs.kuleuven.be>
Date:   Tue Jun 21 08:55:52 2016 +0300

    init commit
bartb@EB-Latitude-E5450 ~/Desktop/test/submod1 $ git checkout db1ba3bc4b02df4677f1197dc137ff36ddfeeb5f
Note: checking out 'db1ba3bc4b02df4677f1197dc137ff36ddfeeb5f'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b <new-branch-name>

HEAD is now at db1ba3b... init commit
bartb@EB-Latitude-E5450 ~/Desktop/test/submod1 $ cd ..
bartb@EB-Latitude-E5450 ~/Desktop/test $ git config -f .gitmodules submodule.submod2.branch master
bartb@EB-Latitude-E5450 ~/Desktop/test $ git commit -a -m "modules"
[master (root-commit) ea9e55f] modules
 3 files changed, 9 insertions(+)
 create mode 100644 .gitmodules
 create mode 160000 submod1
 create mode 160000 submod2
bartb@EB-Latitude-E5450 ~/Desktop/test $ git status
On branch master
nothing to commit, working directory clean
bartb@EB-Latitude-E5450 ~/Desktop/test $  git submodule update --recursive --remote
Submodule path 'submod1': checked out '42d476962fc4e25c64ff2a807d2bf9b3e2ea31f8'
bartb@EB-Latitude-E5450 ~/Desktop/test $ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

    modified:   submod1 (new commits)

如你所见,在最新的git submodule update --remote submod1 在 master 上检出后,即使我从未为它配置过 master 分支。这就是我所说的“做错事”的意思

子子模块也会发生同样的事情:它们都是在 master 而不是在它们的特定提交时签出的。

这个“问题”其实是git submodule update --remote的预期。来自 git 文档:

This option is only valid for the update command. Instead of using the superproject’s recorded SHA-1 to update the submodule, use the status of the submodule’s remote-tracking branch. The remote used is branch’s remote (branch.<name>.remote), defaulting to origin. The remote branch used defaults to master, but the branch name may be overridden by setting the submodule.<name>.branch option in either .gitmodules or .git/config (with .git/config taking precedence).

https://git-scm.com/docs/git-submodule

尤其是部分:

The remote branch used defaults to master

这是我想要避免的。

编辑:附加要求是:我不想对子模块或子模块进行任何修改(这些是联合项目)。

【问题讨论】:

  • 你用的是什么git版本?
  • git 版本 2.6.0.rc3
  • 2.9.0 版本是否仍然存在此问题?
  • 对不起,我刚刚意识到我在另一台电脑上做了上述测试(应该检查那里安装的 git 版本)。
  • 我目前无法访问我拥有适当权限的 PC。明天我会检查它是否仍然存在。

标签: git git-branch git-submodules


【解决方案1】:

2020 年更新:

OP BartBog 报告in the comments

由于$top/.gitmodules 不包含子子模块(和子子子模块)的分支信息,当前(2016 年)的答案对子子模块效果不佳(不再?)

新解决方案:

export top=$(pwd) 
git submodule foreach 'b=$(git config -f ${top}/.gitmodules submodule.${path}.branch); \
  case "${b}" in \
    "") git checkout ${sha1}; git su ;; 
    *) git checkout ${b}; git pull origin ${b}; git su;; 
  esac')

git-su 是我的脚本的名称


2016 年原始答案:

会将所有子模块更新到关联的分支(默认情况下,master),即它适用于 submod1 和 submod2,但对其余部分执行错误的操作。

实际上,是的,它会“做错事”。

我将通过下面的示例来说明该错误(一个名为 parent 的 repo 带有一个子模块'sub',它本身带有一个子模块'subsub'),使用 git 版本 2.9.0.windows.1。

我将提出一个简单的解决方法,允许sub 关注master,同时确保subsub 不会在自己的master 上签出。


设置

让我们用两个提交创建一个 repo subsub

vonc@VONCAVN7 D:\git\tests\subm
> git init subsub1
Initialized empty Git repository in D:/git/tests/subm/subsub1/.git/
> cd subsub1
> git commit --allow-empty -m "subsub c1"
[master (root-commit) f3087a9] subsub c1
> git commit --allow-empty -m "subsub c2"
[master 03d08cc] subsub c2

让那个 repo subsub 成为另一个 repo 'sub' 的子模块:

vonc@VONCAVN7 D:\git\tests\subm
> git init sub
Initialized empty Git repository in D:/git/tests/subm/sub/.git/

> cd sub
> git submodule add -- ../subsub
Cloning into 'D:/git/tests/subm/sub/subsub'...
done.

默认情况下,子模块“subsub”在其自己的master HEAD (gl is an alias for git log with pretty format) 处签出:

vonc@VONCAVN7 D:\git\tests\subm\sub\subsub
> gl
* 03d08cc  - (HEAD -> master, origin/master, origin/HEAD) subsub c2 (4 minutes ago) VonC
* f3087a9  - subsub c1 (4 minutes ago) VonC

让我们确保sub 已在c1 签出subsub(这不是 master HEAD C2):

vonc@VONCAVN7 D:\git\tests\subm\sub\subsub
> git checkout @~
Note: checking out '@~'.

You are in 'detached HEAD' state.     
HEAD is now at f3087a9... subsub c1
> git br -avv
* (HEAD detached at f3087a9) f3087a9 subsub c1
  master                03d08cc [origin/master] subsub c2
  remotes/origin/HEAD   -> origin/master
  remotes/origin/master 03d08cc subsub c2

让我们在其父 repo 'sub' 中添加并提交子模块 'subsub'(在 master~1c1 签出):

vonc@VONCAVN7 D:\git\tests\subm\sub\subsub
> cd ..
vonc@VONCAVN7 D:\git\tests\subm\sub
> git add .
> git commit -m "subsub at HEAD-1"
[master (root-commit) 1b8144b] subsub at HEAD-1
 2 files changed, 4 insertions(+)
 create mode 100644 .gitmodules
 create mode 160000 subsub

让我们在那个 repo 'sub' 中添加几个提交:

vonc@VONCAVN7 D:\git\tests\subm\sub
> git commit --allow-empty -m "sub c1"
[master b7d1c40] sub c1
> git commit --allow-empty -m "sub c2"
[master c77f4b2] sub c2

vonc@VONCAVN7 D:\git\tests\subm\sub
> gl
* c77f4b2  - (HEAD -> master) sub c2 (2 seconds ago) VonC
* b7d1c40  - sub c1 (3 seconds ago) VonC
* 1b8144b  - subsub at HEAD-1 (77 seconds ago) VonC

sub 的最新提交确实在正确的提交处引用了它的子模块“subsub”(子子 c1 一个,而不是 c2 一个)

vonc@VONCAVN7 D:\git\tests\subm\sub
> git ls-tree @
100644 blob 25a0feba7e1c1795be3b8e7869aaa5dba29d33e8    .gitmodules
160000 commit f3087a9bc9b743625e0799f57c017c82c50e35d6  subsub
              ^^^
              That is subsub master~1 commit c1

最后,让我们创建一个主父 repo 'parent' 并添加 'sub' 作为子模块:

vonc@VONCAVN7 D:\git\tests\subm
> git init parent
Initialized empty Git repository in D:/git/tests/subm/parent/.git/
> cd parent

vonc@VONCAVN7 D:\git\tests\subm\parent
> git submodule add -- ../sub
Cloning into 'D:/git/tests/subm/parent/sub'...
done.

让我们确保sub没有在其master HEAD 中签出(就像我们之前对subsub 所做的那样)

vonc@VONCAVN7 D:\git\tests\subm\parent
> cd sub

vonc@VONCAVN7 D:\git\tests\subm\parent\sub
> gl
* c77f4b2  - (HEAD -> master, origin/master, origin/HEAD) sub c2 (2 minutes ago) VonC
* b7d1c40  - sub c1 (2 minutes ago) VonC
* 1b8144b  - subsub at HEAD-1 (3 minutes ago) VonC

vonc@VONCAVN7 D:\git\tests\subm\parent\sub
> git checkout @~1
Note: checking out '@~1'.

You are in 'detached HEAD' state.
HEAD is now at b7d1c40... sub c1

现在,我们将sub(在其c1 提交处签出,而不是在其c2 master HEAD 处签出)添加到parent 存储库:

vonc@VONCAVN7 D:\git\tests\subm\parent
> git add .
> git st
On branch master

Initial commit

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)

        new file:   .gitmodules
        new file:   sub


vonc@VONCAVN7 D:\git\tests\subm\parent
> git commit -m "sub at c1"
[master (root-commit) 27374ec] sub at c1
 2 files changed, 4 insertions(+)
 create mode 100644 .gitmodules
 create mode 160000 sub

sub 跟随master 作为repo parent 中的子模块:

vonc@VONCAVN7 D:\git\tests\subm\parent
> git config -f .gitmodules submodule.sub.branch master
> git diff
diff --git a/.gitmodules b/.gitmodules
index 8688a8c..97974c1 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,4 @@
 [submodule "sub"]
        path = sub
        url = ../sub
+       branch = master

vonc@VONCAVN7 D:\git\tests\subm\parent
> git add .
> git commit -m "sub follows master"
[master 2310a02] sub follows master
 1 file changed, 1 insertion(+)

vonc@VONCAVN7 D:\git\tests\subm\parent
> gl
* 2310a02  - (HEAD -> master) sub follows master (1 second ago) VonC
* 27374ec  - sub at c1 (2 minutes ago) VonC

错误:

如果我克隆 repo parent,然后要求它的任何子模块在他们的远程分支之后签出,sub subsub 将签出他们的master 分支(而只有sub 应该结帐mastersubsub 应该留在c1)

首先是克隆:

vonc@VONCAVN7 D:\git\tests\subm
> git clone --recursive parent p1
Cloning into 'p1'...
done.
Submodule 'sub' (D:/git/tests/subm/sub) registered for path 'sub'
Cloning into 'D:/git/tests/subm/p1/sub'...
done.
Submodule path 'sub': checked out 'b7d1c403edaddf6a4c00bbbaa8e2dfa6ffbd112f'
Submodule 'subsub' (D:/git/tests/subm/subsub) registered for path 'sub/subsub'
Cloning into 'D:/git/tests/subm/p1/sub/subsub'...
done.
Submodule path 'sub/subsub': checked out 'f3087a9bc9b743625e0799f57c017c82c50e35d6'

到目前为止,一切顺利:subsubsubc1 签出,不是 c2(即:不是他们的master HEAD c2

但是:

vonc@VONCAVN7 D:\git\tests\subm\p1
> git submodule update --recursive --remote
Submodule path 'sub': checked out 'c77f4b2590794e030ec68a8cea23ae566701d2de'
Submodule path 'sub/subsub': checked out '03d08cc81e3b9c0734b8f53fad03ea7b1f0373df'

现在,从克隆p1 中,子模块和子子模块都位于它们的master HEAD c2

即使如此,sub(按预期在其master 签出)仍然在c2 拥有subsub

vonc@VONCAVN7 D:\git\tests\subm\p1\sub
> git ls-tree @
100644 blob 25a0feba7e1c1795be3b8e7869aaa5dba29d33e8    .gitmodules
160000 commit f3087a9bc9b743625e0799f57c017c82c50e35d6  subsub

解决方法:

在不修改subsubsub 中的任何内容的情况下,以下是如何确保subsub 保持在预期的c1 提交而不是遵循master(就像sub 应该这样做)

从具有自身嵌套子模块的子模块调用git submodule update --recursive(所以这里没有--remote

vonc@VONCAVN7 D:\git\tests\subm\p1\sub
> git submodule update --recursive
Submodule path 'subsub': checked out 'f3087a9bc9b743625e0799f57c017c82c50e35d6'

我们现在有:

  • sub 保留在 master (因为父 .gitmodules branch 指令及其初始 git submodule update --recursive --remote
  • subsub 设置回其记录的 sha1(c1,而不是 master c2

结论

  1. 看起来确实是个糟糕的设计:--recursive--remote 应用于所有 个嵌套子模块,当没有找到submodule.&lt;path&gt;.&lt;branch&gt; 时默认为master。
  2. 您可以编写脚本来摆脱这种情况,以便:
  • update --remote你想要什么
  • 将没有在顶级父 repo .gitmodules 文件中指定分支的任何子模块重置为其正确的 SHA1。

只需在 %PATH% 中的任何位置创建 git-subupd 脚本(一个 bash 脚本,即使在常规 Windows CMD 会话中也可以工作,因为它将由 git bash 解释)

git-subupd:

#!/bin/bash
git submodule update --recursive --remote
export top=$(pwd)
git submodule foreach --recursive 'b=$(git config -f ${top}/.gitmodules submodule.${path}.branch); case "${b}" in "") git checkout ${sha1};; esac'

“git 命令的组合”简化为一个 git 调用:

cd /path/to/parent/repo
git subupd

就是这样。
(任何名为git-xxx的脚本都可以被git调用git xxx

vonc@VONCAVN7 D:\git\tests\subm\p1
> git subupd
Submodule path 'sub/subsub': checked out '03d08cc81e3b9c0734b8f53fad03ea7b1f0373df'
Entering 'sub'
Entering 'sub/subsub'
Previous HEAD position was 03d08cc... subsub c2
HEAD is now at f3087a9... subsub c1

sub 保持设置为master(提交c2,不变),而subsub 重置为c1(而不是其master c2)。

OP BartBog 声明 in the comments 使用该脚本的轻微变化:

export top=$(pwd)
git submodule foreach --recursive \
  'b=$(git config -f ${top}/.gitmodules submodule.${path}.branch); \
   case "${b}" in \
     "") git checkout ${sha1};; \
      *) git checkout ${b}; git pull origin ${b};; \
   esac' 

避免调用submodule update --remote 并确保我的子模块不处于分离头状态(符合your answer)。

【讨论】:

  • 我在我的问题中澄清了我所说的“做错事”的意思,并提供了一个出错的简单示例。
  • @BartBog 好的。我已经修改了我的答案,添加了一个完整的测试和一个可能的解决方法(不涉及对子模块的任何修改)
  • 我不会称其为错误:文档中提到 --remote 应该是这样工作的。我猜这只是一个糟糕的设计选择。
  • 这个解决方案的另一个问题是我基本上必须为所有子模块执行两个额外的命令(由 --remote 命令更新)
  • @BartBog 我正在编写一个脚本以通过 one 全局命令自动执行该过程。
猜你喜欢
  • 2013-12-05
  • 2017-07-23
  • 1970-01-01
  • 2013-11-27
  • 2010-12-04
  • 2015-03-22
  • 2012-02-29
  • 1970-01-01
  • 2016-09-24
相关资源
最近更新 更多