git - Git:跟踪子模块中的分支,但在其他子模块中提交(可能是嵌套的)

标签 git git-branch git-submodules

我正在寻找一种情况,其中我有一个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)

如您所见,在master签出最新的git submodule update --remote子mod1之后,即使我从未为它配置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

这是我想避免的。
编辑:另一个请求是:我不想对子模块或子模块做任何修改(这些是联合项目)。

最佳答案

将所有子模块更新到关联的分支(默认情况下为master),也就是说,它对submod1和submod2有效,但对其他子模块却做了错误的工作。
实际上,是的,它会做“其他的错误的事情”。
我将用下面的一个例子来说明这个bug(一个名为parent的repo带有一个子模块'sub',本身带有一个子模块'subsub',使用git版本2.9.0.windows.1)。
我将提出一个简单的解决方案,允许sub遵循master,同时确保subsub不会在它自己的master签出。
安装程序
让我们用两次提交创建一个reposubsub

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

让该回购成为另一个回购的子模块:
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'在其自己的sub头(subsub is an alias for master 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

让我们确保gl已在git log签出(它不是sub):
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

让我们添加并提交子模块“subsub”(在其父repo“c1”中签出):
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“master HEAD C2”中添加两个提交:
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

subsub的最新提交在右提交时引用了其子模块“master~1”(子模块c1一个,而不是sub一个)
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

最后,让我们制作一个主父回购'sub',并将'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.

让我们确保subsub没有在其c1头签出(就像我们之前对c2所做的那样)
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

现在,我们将parent(在其sub提交时签出,而不是在其sub头部签出)添加到master回购协议:
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

让我们将subsub作为repo中的子模块设置为sub
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

缺陷:
如果我克隆回购协议,然后要求其任何子模块在其远程分支之后签出,c1c2 master将签出其parent分支(而只有sub应该签出masterparent应该保持在parent)。
首先克隆:
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'

到目前为止,还不错:subsubsub是在master签出的,而不是sub(即:不是他们的master
但是:
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'

现在,从克隆subsub中,子模块和子模块都在其c1处。
即使如此(按预期在其sub处签出),在subsub处仍有c1
vonc@VONCAVN7 D:\git\tests\subm\p1\sub
> git ls-tree @
100644 blob 25a0feba7e1c1795be3b8e7869aaa5dba29d33e8    .gitmodules
160000 commit f3087a9bc9b743625e0799f57c017c82c50e35d6  subsub

解决方法:
在不修改c2master HEAD c2中的任何内容的情况下,下面是如何确保p1保持其预期的master HEAD c2提交,而不是遵循sub(就像master应该这样)
从本身有嵌套子模块的子模块调用subsub(因此这里没有c2
vonc@VONCAVN7 D:\git\tests\subm\p1\sub
> git submodule update --recursive
Submodule path 'subsub': checked out 'f3087a9bc9b743625e0799f57c017c82c50e35d6'

我们现在有:
sub保持在subsub(因为父指令及其初始指令)
subsub被设置回其记录的sha1(c1,而不是master
结论
它看起来确实是个糟糕的设计:subgit submodule update --recursive应用于所有嵌套的子模块,当没有找到--remote时默认为master。
您可以编写脚本来解决此问题,以便:
sub你想要什么
将顶部父repomaster文件中未指定分支的任何子模块重置为其正确的sha1。
只需在.gitmodules脚本中的任何地方创建branch脚本(bash脚本,即使在常规的windowsgit submodule update --recursive --remote会话中也可以工作,因为它将由git bash解释)
subsub
#!/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可以用c1调用任何名为master c2的脚本)
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

--recursive保持设置为--remote(commitsubmodule.<path>.<branch>,未更改),而update --remote重置为.gitmodules(而不是其%PATH%)。
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' 

避免调用git-subupd并确保我的子模块没有处于分离的头状态(conformyour answer)。

关于git - Git:跟踪子模块中的分支,但在其他子模块中提交(可能是嵌套的),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37879715/

相关文章:

git - 自动构建 .mailmap 文件的工具

git - 为什么一个新的本地分支会立即开始跟踪创建它的本地分支,我该如何关闭它?

git - git merge 期间的奇怪输出

git - 如何在不创建长期分支的情况下维护旧版本?

git - 为什么一个分支的更改会影响 git 中的另一个分支?

git - git bundle 应该如何与子模块一起工作?

git - 在 Git 中管理一组相关的存储库

git - 将本地文件推送到 github 存储库中的目录

git - JGit HTTPS 克隆不起作用

git - GIT 上的 SSIS 解决方案?