将Git子模块更新为最新的原始提交

时间:2011-04-29 05:41:47

标签: git git-submodules

我有一个带有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 loggit show拒绝新提交的存在。到目前为止,我刚刚rm - 模块并重新添加它,但这在原则上是错误的,在实践中是乏味的。

15 个答案:

答案 0 :(得分:1245)

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

答案 1 :(得分:379)

Git 1.8.2提供了一个新选项--remote,可以实现此行为。运行

git submodule update --remote --merge

将从每个子模块的上游获取最新的更改,将它们合并,并检查子模块的最新版本。正如the docs所说:

  

- 远程

     

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

这相当于在每个子模块中运行git pull,这通常正是您想要的。

答案 2 :(得分:107)

在项目父目录中运行:

git submodule update --init 

或者如果你有递归子模块运行:

git submodule update --init --recursive

有时这仍然不起作用,因为在更新子模块时,本地子模块目录中有局部更改。

大多数情况下,本地更改可能不是您要提交的更改。它可能由于子模块中的文件删除等原因而发生。如果是这样,请在本地子模块目录和项目父目录中重新运行:

git submodule update --init --recursive 

答案 3 :(得分:70)

您的主项目指向子模块应该处于的特定提交。 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

从现在开始,如果其他人更新了他们的主项目,那么git submodule update为他们将更新子模块,假设它已被初始化。

答案 4 :(得分:19)

在本次讨论中似乎将两种不同的场景混合在一起:

情景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命令 - 这意味着上一个命令必须执行w / o错误。

在可能的成功拉动之后(如果在遥控器上发现新的东西),我做了一个推动以确保在客户端上不会留下可能的合并提交。再次,只有如果拉实际上带来了新的东西。

最后,最终|| true确保脚本继续出错。为了使这项工作,迭代中的所有内容必须用双引号括起来,并且git-commands包含在parantheses中(运算符优先级)。

我最喜欢的部分:

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

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

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

最后,提交一些消息,说明父代表已更新。如果没有做任何事情,默认情况下将忽略此提交。把它推到原点,你就完成了。

我有一个在jenkins-job中运行它的脚本,之后链接到计划的自动部署,它就像一个魅力。

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

答案 5 :(得分:17)

简单明了,要获取子模块:

git submodule update --init --recursive

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

git submodule foreach git pull origin master

答案 6 :(得分:4)

git pull --recurse-submodules

这将提取所有最新提交。

答案 7 :(得分:3)

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

  

更新

     

更新已注册的子模块,   即克隆缺失的子模块和   签出中指定的提交   包含存储库的索引。   这将使子模块HEAD成为   除非--rebase或--merge是分离的   指定或密钥   子模块。$ name.update设置为   改变或合并。

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

答案 8 :(得分:2)

就我而言,我希望f = open("guru99.txt", "a") data = "some data" f.write(data) 更新到最新版本,同时重新填充所有丢失的文件。

以下内容恢复了丢失的文件(感谢git,在这里似乎没有提到),但是它没有提取任何新的提交:

--force

这样做:

git submodule update --init --recursive --force

答案 9 :(得分:1)

这是一个非常棒的单行程序,可以将所有内容更新为最新的主人:

git submodule foreach 'git fetch origin --tags; git checkout master; git pull' && git pull && git submodule update --init --recursive

Thanks to Mark Jaquith

答案 10 :(得分:1)

如果您不知道主机分支,请执行以下操作:

git submodule foreach git pull origin $(git rev-parse --abbrev-ref HEAD)

它将获得主git的分支,然后对于每个子模块,将pull设置为相同的分支

答案 11 :(得分:1)

如果您希望为每个子模块检出master分支,则可以为此使用以下命令:

git submodule foreach git checkout master

答案 12 :(得分:1)

这对我来说可以更新到最新的提交

git submodule update --recursive --remote --init

答案 13 :(得分:0)

请注意,更新子模块提交的现代形式是:

git submodule update --recursive --remote --merge --force

较旧的形式是:

git submodule foreach --quiet git pull --quiet origin

除了...第二种形式不是真的“安静”。

请参见commit a282f5aNguyễn Thái Ngọc Duy (pclouds)(2019年4月12日)。
(由Junio C Hamano -- gitster --commit f1c9f6c中合并,2019年4月25日)

  

submodule foreach:修复“ <command> --quiet”不被尊重

     

罗宾报道

git submodule foreach --quiet git pull --quiet origin
     

不再安静了。
  在fc1b924submodule:端口submodule子命令'foreach'从外壳到C,2018-05-10,Git v2.19.0-rc0)之前应该保持安静parseopt不能再误食。

     

git pull”的行为就像没有给出--quiet

     

发生这种情况是因为parseopt中的submodule--helper会尝试解析   两个--quiet选项都好像是foreach的选项,而不是git-pull的选项。
  解析的选项从命令行中删除。所以当我们这样做   稍后再拉,我们就这样执行

git pull origin
     

调用子模块助手时,在“ --”前面添加“ git pull”   停止parseopt来解析不真正属于的选项   submodule--helper foreach

     

PARSE_OPT_KEEP_UNKNOWN被删除是一项安全措施。 parseopt应该   永远不会看到未知的选项或出了什么问题。也有   当我查看它们时,会更新几个用法字符串。

     

与此同时,我还将“ --”添加到其他将“ $@”传递给   submodule--helper。在这些情况下,“ $@”是路径,不太可能是   --something-like-this
  但重点仍然是,git-submodule已对选项和路径进行了解析和分类。
  submodule--helper永远不要将git-submodule传递的路径视为选项,即使它们看起来像一个。

答案 14 :(得分:0)

处理包含子模块的git项目的最简单方法是始终添加

--recurse-submodules 
在每个git命令末尾的

例如:

git fetch --recurse-submodules

另一个

git pull --update --recurse-submodules

等...