Git:跟踪子模块中的分支但在其他子模块中提交(可能嵌套)
Git: track branch in submodule but commit in other submodule (possibly nested)
我正在寻找一种情况,其中我有一个 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,但对其余部分做错误的事情。
有什么办法可以很好地完成这项工作吗?
作为对第一个答案的回应,我将阐明 "do wrong stuff" 的意思。
这是一个小例子
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 分支。这就是我所说的 "do wrong stuff"
子子模块也会发生同样的事情:它们都是在主模块而不是在它们的特定提交时检出的。
这个"issue"实际上是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
这是我想避免的。
编辑:一个额外的请求是:我不想对 submods 或 subsubmods 做任何修改(这些是联合项目)。
2020 年更新:
OP BartBog reports :
The current (2016) answer does not work that well (anymore?) with subsubmodules since $top/.gitmodules
does not contain the branch info of the subsub (and subsubsub modules)
New solution:
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')
where git-su
is the name of my script
2016 年原始答案:
will update all submodules to the associated branch (by default, master), i.e., it will work for submod1 and submod2, but do wrong stuff for the rest.
实际上,是的,它会“为其余部分做错误的事情”。
我将用下面的例子来说明这个错误(一个名为 parent
的 repo 有一个子模块 'sub
',它本身有一个子模块 'subsub
'),使用 git 版本 2.9.0.windows.1.
并且我将提出一个简单的解决方法,允许 sub
跟随 master
,同时确保 subsub
不会自行检出 master
。
设置
让我们创建一个包含两次提交的回购 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
已 subsub
在 c1
签出(这是 而不是 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
让我们在其父仓库“sub
”中添加并提交子模块“subsub
”(在 master~1
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 '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
”(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
最后,让我们创建一个主父仓库“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
作为 repo parent
:
中的子模块跟随 master
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
应该签出 master
,subsub
应该保留在 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'
到目前为止,一切顺利:sub
和 subsub
在 c1
签出, 不是 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
解决方法:
在不修改 sub
和 subsub
内的任何内容的情况下,这里是如何确保 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
)
结论
- 看起来确实是个糟糕的设计:
--recursive
将 --remote
应用于 所有 嵌套子模块,当没有 submodule.<path>.<branch>
时默认为 master找到了。
- 您可以通过编写脚本来解决此问题,以便:
update --remote
随心所欲
- 将顶级父仓库
.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 declares 使用该脚本的细微变化:
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'
to avoid the call to submodule update --remote
AND to make sure that my submodules are not in detached head state (conform your answer).
我正在寻找一种情况,其中我有一个 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,但对其余部分做错误的事情。
有什么办法可以很好地完成这项工作吗?
作为对第一个答案的回应,我将阐明 "do wrong stuff" 的意思。
这是一个小例子
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 分支。这就是我所说的 "do wrong stuff"
子子模块也会发生同样的事情:它们都是在主模块而不是在它们的特定提交时检出的。
这个"issue"实际上是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
这是我想避免的。
编辑:一个额外的请求是:我不想对 submods 或 subsubmods 做任何修改(这些是联合项目)。
2020 年更新:
OP BartBog reports
The current (2016) answer does not work that well (anymore?) with subsubmodules since
$top/.gitmodules
does not contain the branch info of the subsub (and subsubsub modules)
New solution:
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')
where
git-su
is the name of my script
2016 年原始答案:
will update all submodules to the associated branch (by default, master), i.e., it will work for submod1 and submod2, but do wrong stuff for the rest.
实际上,是的,它会“为其余部分做错误的事情”。
我将用下面的例子来说明这个错误(一个名为 parent
的 repo 有一个子模块 'sub
',它本身有一个子模块 'subsub
'),使用 git 版本 2.9.0.windows.1.
并且我将提出一个简单的解决方法,允许 sub
跟随 master
,同时确保 subsub
不会自行检出 master
。
设置
让我们创建一个包含两次提交的回购 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
已 subsub
在 c1
签出(这是 而不是 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
让我们在其父仓库“sub
”中添加并提交子模块“subsub
”(在 master~1
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 '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
”(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
最后,让我们创建一个主父仓库“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
作为 repo parent
:
master
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
应该签出 master
,subsub
应该保留在 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'
到目前为止,一切顺利:sub
和 subsub
在 c1
签出, 不是 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
解决方法:
在不修改 sub
和 subsub
内的任何内容的情况下,这里是如何确保 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
)
结论
- 看起来确实是个糟糕的设计:
--recursive
将--remote
应用于 所有 嵌套子模块,当没有submodule.<path>.<branch>
时默认为 master找到了。 - 您可以通过编写脚本来解决此问题,以便:
update --remote
随心所欲- 将顶级父仓库
.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 declares
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'
to avoid the call to
submodule update --remote
AND to make sure that my submodules are not in detached head state (conform your answer).