我有一个带有Git子模块的项目。它来自ssh://…URL,在提交a上,提交B已经被推到那个URL,我想让子模块检索提交,并更改它。

现在,我的理解是git子模块更新应该这样做,但它没有。它不做任何事情(没有输出,成功退出代码)。这里有一个例子:

$ 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日志和git显示否认新提交的存在。到目前为止,我只是在rm-ing模块并重新添加它,但这在原则上是错误的,在实践中也很乏味。


当前回答

Git 1.8.2提供了一个新选项——remote,它将启用这种行为。运行

git submodule update --remote --merge

将从每个子模块的上游获取最新的更改,将它们合并到子模块中,并检出子模块的最新修订。如文档所述:

——远程 此选项仅对update命令有效。与其使用超项目记录的SHA-1来更新子模块,不如使用子模块的远程跟踪分支的状态。

这相当于在每个子模块中运行git pull <remote> <default_branch>(通常是git pull origin master或git pull origin main),这通常正是你想要的。

其他回答

@Jason在某种程度上是正确的,但不完全正确。

更新 更新已注册的子模块, 即克隆缺失子模块和 方法中指定的提交 包含存储库的索引。 这将使子模块HEAD为 分离,除非——rebase或——merge是 指定或键 子模块。美元的名字。Update设置为 重组或合并。

因此,git子模块update确实签出,但它是提交到包含存储库的索引中。它根本不知道上游的新提交。所以去你的子模块,获得你想要的提交,并在主存储库中提交更新后的子模块状态,然后更新git子模块。

简单地获取子模块:

git submodule update --init --recursive

现在继续将它们更新到最新的主分支(例如):

git submodule foreach git pull origin master

git子模块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

在这个讨论中,似乎有两种不同的场景被混合在一起:

场景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(快进)每当我只是做拉。顺便说一句,我已经重新调基了。

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确保脚本继续错误。要做到这一点,迭代中的所有内容都必须用双引号括起来,Git命令用圆括号括起来(操作符优先级)。

我最喜欢的部分:

for i in $(git submodule foreach --quiet 'echo $path')
do
  echo "Adding $i to root repo"
  git add "$i"
done

用——quiet迭代所有子模块-,这将删除' entered MODULE_PATH'输出。使用'echo $path'(必须是单引号),子模块的路径被写入输出。

这个相对子模块路径列表被捕获在一个数组中($(…))-最后迭代这个,并执行git add $i来更新父库。

最后,提交一些消息,说明父库已更新。如果什么都没有做,默认情况下这个提交将被忽略。把这个推到原点,你就完成了。

我在Jenkins作业中运行了一个脚本,该脚本随后链接到计划的自动部署,它的工作就像一个魅力。

我希望这将对某人有所帮助。

对我来说,所有的git子模块都不工作。但这个方法奏效了:

cd <path/to/submodule>
git pull

它下载并因此更新第三方回购。 然后

cd <path/to/repo>
git commit -m "update latest version" <relative_path/to/submodule>
git push

它更新了您的远程回购(与最后一次提交的链接repo@xxxxxx)。