我有一个带有Git子模块的项目.它来自ssh:// ... URL,并且在提交A.提交B已被推送到该URL,我希望子模块检索提交,并更改为它.
现在,我的理解是git submodule update
应该这样做,但事实并非如此.它没有做任何事情(没有输出,成功退出代码).这是一个例子:
$ mkdir foo $ cd foo $ git init . Initialized empty Git repository in /.../foo/.git/ $ git submodule add ssh://user@host/git/mod mod Cloning into mod... user@host's password: hunter2 remote: Counting objects: 131, done. remote: Compressing objects: 100% (115/115), done. remote: Total 131 (delta 54), reused 0 (delta 0) Receiving objects: 100% (131/131), 16.16 KiB, done. Resolving deltas: 100% (54/54), done. $ git commit -m "Hello world." [master (root-commit) 565b235] Hello world. 2 files changed, 4 insertions(+), 0 deletions(-) create mode 100644 .gitmodules create mode 160000 mod # At this point, ssh://user@host/git/mod changes; submodule needs to change too. $ git submodule init Submodule 'mod' (ssh://user@host/git/mod) registered for path 'mod' $ git submodule update $ git submodule sync Synchronizing submodule url for 'mod' $ git submodule update $ man git-submodule $ git submodule update --rebase $ git submodule update $ echo $? 0 $ git status # On branch master nothing to commit (working directory clean) $ git submodule update mod $ ...
我也试过git fetch mod
,这似乎做取(但不能可能,因为它不要求输入密码!),但git log
并git show
否认新提交的存在.到目前为止,我刚刚rm
对模块进行了重新添加,但这在原则上是错误的,在实践中也是乏味的.
该git submodule update
命令实际告诉Git您希望每个子模块检出已在超级项目的索引中指定的提交.如果要将子模块更新为其远程可用的最新提交,则需要在子模块中直接执行此操作.
总结如下:
# Get the submodule initially git submodule add ssh://bla submodule_dir git submodule init # Time passes, submodule upstream is updated # and you now want to update # Change to the submodule directory cd submodule_dir # Checkout desired branch git checkout master # Update git pull # Get back to your project root cd .. # Now the submodules are in the state you want, so git commit -am "Pulled down update to submodule_dir"
或者,如果你是一个忙碌的人:
git submodule foreach git pull origin master
Git 1.8.2提供了一个新选项--remote
,可以实现这种行为.运行
git submodule update --remote --merge
将从每个子模块的上游获取最新的更改,将它们合并,并检查子模块的最新版本.正如文档所说:
- 远程
此选项仅对update命令有效.不使用超级项目记录的SHA-1来更新子模块,而是使用子模块的远程跟踪分支的状态.
这相当于git pull
在每个子模块中运行,这通常正是您想要的.
在您的项目父目录中运行:
git submodule update --init
或者如果你有递归子模块运行:
git submodule update --init --recursive
有时这仍然无法正常工作,因为在更新子模块时,您在本地子模块目录中进行了本地更改.
大多数情况下,本地更改可能不是您要提交的更改.这可能是由于子模块中的文件删除等原因造成的.如果是这样,请在本地子模块目录和项目父目录中重新运行:
git submodule update --init --recursive
您的主项目指向子模块应该处于的特定提交.git submodule update
尝试在每个已初始化的子模块中检出该提交是什么.子模块实际上是一个独立的存储库 - 只需在子模块中创建一个新的提交并推送它是不够的,您还需要在主项目中显式添加新版本的子模块.
因此,在您的情况下,您应该在子模块中找到正确的提交 - 让我们假设这是主要的提示:
cd mod git checkout master git pull origin master
现在回到主项目,暂存子模块并提交:
cd .. git add mod git commit -m "Updating the submodule 'mod' to the latest version"
现在推送您的新版主项目:
git push origin master
从这一点开始,如果其他人更新了他们的主项目,那么master
他们将更新子模块,假设它已被初始化.
在本次讨论中,似乎有两种不同的场景混合在一起:
场景1
使用我的父repo指向子模块的指针,我想检查父repo指向的每个子模块中的提交,可能是在首次迭代所有子模块并从远程更新/拉取这些子模块之后.
正如所指出的那样,这是完成的
git submodule foreach git pull origin BRANCH git submodule update
场景2,我认为是OP的目标
在1个或多个子模块中发生了新的事情,我想1)拉出这些变化,2)更新父repo以指向这个/这些子模块的HEAD(最新)提交.
这将通过以下方式完成
git submodule foreach git pull origin BRANCH git add module_1_name git add module_2_name ...... git add module_n_name git push origin BRANCH
不太实用,因为你必须硬编码所有n个子模块的n个路径,例如脚本来更新父repo的提交指针.
通过每个子模块进行自动迭代,更新父repo指针(使用git add)指向子模块的头部会有什么好处.
为此,我制作了这个小的bash脚本:
git-update-submodules.sh
#!/bin/bash
APP_PATH=$1
shift
if [ -z $APP_PATH ]; then
echo "Missing 1st argument: should be path to folder of a git repo";
exit 1;
fi
BRANCH=$1
shift
if [ -z $BRANCH ]; then
echo "Missing 2nd argument (branch name)";
exit 1;
fi
echo "Working in: $APP_PATH"
cd $APP_PATH
git checkout $BRANCH && git pull --ff origin $BRANCH
git submodule sync
git submodule init
git submodule update
git submodule foreach "(git checkout $BRANCH && git pull --ff origin $BRANCH && git push origin $BRANCH) || true"
for i in $(git submodule foreach --quiet 'echo $path')
do
echo "Adding $i to root repo"
git add "$i"
done
git commit -m "Updated $BRANCH branch of deployment repo to point to latest head of submodules"
git push origin $BRANCH
要运行它,请执行
git-update-submodules.sh /path/to/base/repo BRANCH_NAME
精
首先,我假设所有repo上都存在名为$ BRANCH(第二个参数)的分支.随意使这更复杂.
前几节是检查参数是否存在的一些部分.然后我拉出父回购的最新东西(我更喜欢使用--ff(快进)每当我做拉动时.我已经关闭了,顺便说一句).
git checkout $BRANCH && git pull --ff origin $BRANCH
如果新的子模块已添加或尚未初始化,则可能需要进行一些子模块初始化:
git submodule sync
git submodule init
git submodule update
然后我更新/拉取所有子模块:
git submodule foreach "(git checkout $BRANCH && git pull --ff origin $BRANCH && git push origin $BRANCH) || true"
注意一些事情:首先,我正在使用链接一些git命令git add
- 意味着前一个命令必须执行w/o错误.
在可能成功拉动之后(如果在遥控器上发现了新的东西),我做了一个推动,以确保在客户端上不会留下可能的合并提交.同样,只有发生如果拉新的东西实际上带来的.
最后,最后一步&&
是确保脚本继续出错.为了使这项工作,迭代中的所有内容必须用双引号括起来,并且git-commands包含在parantheses(operator precedence)中.
我最喜欢的部分:
for i in $(git submodule foreach --quiet 'echo $path')
do
echo "Adding $i to root repo"
git add "$i"
done
迭代所有子模块 - 使用|| true
,删除'Entering MODULE_PATH'输出.使用--quiet
(必须使用单引号),子模块的路径将写入输出.
这个相对子模块路径列表在一个数组('echo $path'
)中捕获- 最后迭代这个并做$(...)
更新父存储库.
最后,提交一些消息,解释父代表已更新.如果没有做任何事情,默认情况下将忽略此提交.把它推到原点,你就完成了.
我有一个在jenkins工作中运行它的脚本,之后链接到计划的自动部署,它就像一个魅力.
我希望这会对某人有所帮助.
简单明了,要获取子模块:
git submodule update --init --recursive
现在继续将它们更新到最新的主分支(例如):
git submodule foreach git pull origin master
注意,更新子模块提交的现代形式是:
git submodule update --recursive --remote --merge --force
较旧的形式是:
git submodule foreach --quiet git pull --quiet origin
除了...第二种形式不是真的“安静”。
See commit a282f5a (12 Apr 2019) by Nguy?n Thái Ng?c Duy (pclouds
).
(Merged by Junio C Hamano -- gitster
-- in commit f1c9f6c, 25 Apr 2019)
submodule foreach
: fix "" not being respected
--quiet Robin reported that
git submodule foreach --quiet git pull --quiet originis not really quiet anymore.
It should be quiet before fc1b924 (submodule
: portsubmodule
subcommand 'foreach
' from shell to C, 2018-05-10, Git v2.19.0-rc0) becauseparseopt
can't accidentally eat options then."
git pull
" behaves as if--quiet
is not given.This happens because
parseopt
insubmodule--helper
will try to parse both--quiet
options as if they are foreach's options, notgit-pull
's.
The parsed options are removed from the command line. So when we do pull later, we execute just thisgit pull originWhen calling submodule helper, adding "
--
" in front of "git pull
" will stopparseopt
for parsing options that do not really belong tosubmodule--helper foreach
.
PARSE_OPT_KEEP_UNKNOWN
is removed as a safety measure.parseopt
should never see unknown options or something has gone wrong. There are also a couple usage string update while I'm looking at them.While at it, I also add "
--
" to other subcommands that pass "$@
" tosubmodule--helper
. "$@
" in these cases are paths and less likely to be--something-like-this
.
But the point still stands,git-submodule
has parsed and classified what are options, what are paths.
submodule--helper
should never consider paths passed bygit-submodule
to be options even if they look like one.
And Git 2.23 (Q3 2019) fixes another issue: "git submodule foreach
" did not protect command line options passed to the command to be run in each submodule correctly, when the "--recursive
" option was in use.
See commit 30db18b (24 Jun 2019) by Morian Sonnet (momoson
).
(Merged by Junio C Hamano -- gitster
-- in commit 968eecb, 09 Jul 2019)
submodule foreach
: fix recursion of optionsCalling:
git submodule foreach --recursive-- leads to an error stating that the option
--
is unknown tosubmodule--helper
.
That is of course only, whenis not a valid option for
git submodule foreach
.The reason for this is, that above call is internally translated into a call to submodule--helper:
git submodule--helper foreach --recursive \ ---- This call starts by executing the subcommand with its option inside the first level submodule and continues by calling the next iteration of the
submodule foreach
callgit --super-prefixsubmodule--helper \ foreach --recursive -- inside the first level submodule. Note that the double dash in front of the subcommand is missing.
This problem starts to arise only recently, as the
PARSE_OPT_KEEP_UNKNOWN
flag for the argument parsing ofgit submodule foreach
was removed in commit a282f5a.
Hence, the unknown option is complained about now, as the argument parsing is not properly ended by the double dash.This commit fixes the problem by adding the double dash in front of the subcommand during the recursion.