我有一个带有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
-ing模块并重新添加它,但是这是在原则上都是错误的和繁琐的做法。
#1楼
在项目父目录中,运行:
git submodule update --init
或者如果你有递归子模块运行:
git submodule update --init --recursive
有时这仍然不起作用,因为在更新子模块时,您在本地子模块目录中以某种方式进行了本地更改。
大多数情况下,本地更改可能不是您要提交的更改。 它可能由于子模块中的文件删除等原因而发生。如果是这样,请在本地子模块目录和项目父目录中重置,再次运行:
git submodule update --init --recursive
#2楼
在这个讨论中似乎将两种不同的场景混合在一起:
场景1
使用我的父存储库指向子模块的指针,我想检查父存储库指向的每个子模块中的提交,可能是在首次迭代所有子模块并从远程更新/拉取这些子模块之后。
正如所指出的那样,这是完成的
git submodule foreach git pull origin BRANCH
git submodule update
场景2,我认为是OP的目标
在一个或多个子模块中发生了新的事情,我想1)拉出这些变化,2)更新父存储库以指向这个/这些子模块的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个路径,例如脚本来更新父存储库的提交指针。
通过每个子模块进行自动迭代,更新父存储库指针(使用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
精
首先,我假设所有存储库中都存在名为$ BRANCH(第二个参数)的分支。 随意使这更复杂。
前几节是检查参数是否存在的一些部分。 然后我拉出父存储库的最新东西(我更喜欢使用--ff(快速转发),每当我做拉动时。我已经关闭了,BTW)。
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命令 - 意味着前一个命令必须执行而不会出错。
在可能的成功拉动之后(如果在远程上找到了新的东西),我会进行推送以确保在客户端上不会留下可能的合并提交。 同样, 如果在拉新的东西,实际上把它才会发生。
最后,最后|| true
|| true
是确保脚本继续上的错误。 为了实现这一点,迭代中的所有内容都必须用双引号括起来,Git命令用括号括起来(运算符优先级)。
我最喜欢的部分:
for i in $(git submodule foreach --quiet 'echo $path')
do
echo "Adding $i to root repo"
git add "$i"
done
迭代所有子模块 - with --quiet
,删除'Entering MODULE_PATH'输出。 使用'echo $path'
(必须使用单引号),子模块的路径将写入输出。
这个相对子模块路径列表在一个数组中捕获( $(...)
) - 最后迭代这个并执行git add $i
来更新父存储库。
最后,提交一些消息,说明父存储库已更新。 如果没有做任何事情,默认情况下将忽略此提交。 把它推到原点,你就完成了。
我有一个在Jenkins工作中运行它的脚本,之后链接到计划的自动部署,它就像一个魅力。
我希望这会对某人有所帮助。
#3楼
Git 1.8.2提供了一个新选项--remote
,可以实现这种行为。 运行
git submodule update --remote --merge
将从每个子模块的上游获取最新的更改,将它们合并,并检查子模块的最新版本。 正如文档所说:
- 远程
此选项仅对update命令有效。 不使用超级项目记录的SHA-1来更新子模块,而是使用子模块的远程跟踪分支的状态。
这相当于在每个子模块中运行git pull
,这通常就是你想要的。
#4楼
这是一个很棒的单行程序,可以将所有内容更新为最新的主服务器:
git submodule foreach 'git fetch origin --tags; git checkout master; git pull' && git pull && git submodule update --init --recursive
#5楼
简单明了,要获取子模块:
git submodule update --init --recursive
现在继续将它们更新到最新的主分支(例如):
git submodule foreach git pull origin master
#6楼
在我的情况下,我希望git
更新到最新版本,同时重新填充任何丢失的文件。
以下恢复了丢失的文件(感谢--force
,这里似乎没有提到),但它没有提取任何新的提交:
git submodule update --init --recursive --force
这样做:
git submodule update --recursive --remote --merge --force
#7楼
git pull --recurse-submodules
这将提取所有最新提交。
#8楼
如果您不知道主机分支,请执行以下操作:
git submodule foreach git pull origin $(git rev-parse --abbrev-ref HEAD)
它将获得主Git存储库的一个分支,然后为每个子模块将拉动相同的分支。
#9楼
注意,虽然更新子模块提交的现代形式是:
git submodule update --recursive --remote --merge --force
较旧的形式是:
git submodule foreach --quiet git pull --quiet origin
除了......这第二种形式并不是真的“安静”。
见NguyễnTháiNgọcDuy( pclouds
) 提交a282f5a (2019年4月12日) 。
(由Junio C gitster
合并- gitster
-在提交f1c9f6c ,2019年4月25日)
submodule foreach
:修复“<command> --quiet
”没有得到尊重罗宾报道说
git submodule foreach --quiet git pull --quiet origin
不再那么安静了。
它应该在fc1b924之前安静 (submodule
:portsubmodule
子命令'foreach
'从shell到C,2018-05-10,Git v2.19.0-rc0)因为parseopt
不能随意吃掉选项。“
git pull
”表现得好像没有给出--quiet
。这是因为
submodule--helper
parseopt
submodule--helper
将尝试解析两个--quiet
选项,就像它们是foreach的选项一样,而不是git-pull
的选项。
已解析的选项将从命令行中删除。 所以当我们稍后拉,我们执行这个git pull origin
调用子模块助手时,在“
git pull
”前添加“--
”将停止parseopt
以解析实际上不属于submodule--helper foreach
选项submodule--helper foreach
。删除
PARSE_OPT_KEEP_UNKNOWN
作为安全措施。parseopt
永远不会看到未知选项或出现问题。 我正在查看它们时还有一些用法字符串更新。在它的同时,我还将“
--
”添加到将“$@
”传递给submodule--helper
其他子命令submodule--helper
。 在这些情况下,“$@
”是路径而且不太可能是--something-like-this
。
但问题仍然存在,git-submodule
已经解析并分类了什么是选项,什么是路径。submodule--helper
永远不应该将git-submodule
传递的路径视为选项,即使它们看起来像是一个。
并且Git 2.23(Q3 2019)解决了另一个问题:当使用“ --recursive
”选项时,“ git submodule foreach
”不保护传递给命令的命令行选项在每个子模块中正确运行。
参见由momoson
Sonnet( momoson
) 提交的30db18b (2019年6月24日) 。
(由Junio C gitster
合并- gitster
-在提交968eecb ,2017年7月9日)
submodule foreach
:修复选项的递归呼叫:
git submodule foreach --recursive <subcommand> --<option>
导致出现错误,指出
submodule--helper
未知选项--<option>
。
当然,这只是当<option>
不是git submodule foreach
的有效选项时。原因是,上面的调用内部转换为对子模块的调用 - helper:
git submodule--helper foreach --recursive \\ -- <subcommand> --<option>
这个调用与它的第一级子模块中选择执行子开始,并通过调用的下一次迭代不断
submodule foreach
调用git --super-prefix <submodulepath> submodule--helper \\ foreach --recursive <subcommand> --<option>
在第一级子模块内。 请注意,缺少子命令前面的双短划线。
这个问题最近才开始出现,因为在提交a282f5a中删除了参数解析
git submodule foreach
的PARSE_OPT_KEEP_UNKNOWN
标志。
因此,现在抱怨未知选项,因为参数解析未通过双破折号正确结束。此提交通过在递归期间在子命令前添加双短划线来解决问题。
#10楼
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
#11楼
@Jason在某种程度上是正确的,但并非完全正确。
更新
更新已注册的子模块,即克隆缺少的子模块,并检查包含的存储库索引中指定的提交。 这将使子模块HEAD被分离,除非指定了--rebase或--merge或密钥子模块。$ name.update设置为rebase或merge。
因此, git submodule update
执行checkout,但它是在包含存储库的索引中的提交。 它根本不知道上游的新提交。 因此,转到您的子模块,获取所需的提交并在主存储库中提交更新的子模块状态,然后执行git submodule update
。
#12楼
您的主项目指向子模块应该处于的特定提交。 git submodule update
尝试检查已初始化的每个子模块中的提交。 子模块实际上是一个独立的存储库 - 只是在子模块中创建一个新的提交并推送它是不够的。 您还需要在主项目中显式添加新版本的子模块。
所以,你的情况,你应该找到犯辅助模块中的正确的-假设这是尖master
:
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
从这一点开始,如果其他人更新了他们的主项目,那么git submodule update
将更新子模块,假设它已被初始化。