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

我有一个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 ,它似乎做了一个抓取(但不可能,因为它不是提示input密码!),但是git loggit show否认存在新的提交。 到目前为止,我只是在模块中重新添加模块,但这在原理上和实践上都是单调乏味的。

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 

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

– 远程

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

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

在你的项目父目录下运行:

 git submodule update --init 

或者如果您有recursion子模块运行:

 git submodule update --init --recursive 

有时这仍然不起作用,这是因为在子模块正在更新的时候,你在本地子模块目录中进行了本地更改。

大多数情况下,本地更改可能不是您想要提交的更改。 这可能是由于子模块中的文件删除等原因造成的。如果是,请在您的本地子模块目录中执行重置,并在您的项目父目录中再次运行:

 git submodule update --init --recursive 

你的主项目指向子模块应该在的一个特定的提交。 git submodule update所做的是尝试在每个已经初始化的子模块中签出提交。 子模块实际上是一个独立的存储库 – 只是在子模块中创build一个新的提交并推送不够,还需要在主项目中明确添加子模块的新版本。

所以,就你而言,你应该在子模块中find正确的提交 – 让我们假设这是主要的提示:

 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 

从这个angular度来看,如果其他人更新他们的主项目,那么他们的git submodule update将更新子模块,假设它已经被初始化。

似乎在这个讨论中混合了两种不同的场景:

情况1

使用我的父repo指向子模块的指针,我想查看每个子模块中的提交,即父代码库指向的位置,可能是在第一次遍历所有子模块并从远程更新/拉取这些子模块之后。

正如指出的那样,这是完成的

 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个path到所有n个子模块,比如在一个脚本中更新父级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 

首先,我假设所有回购商都存在名称为$ BRANCH(第二个参数)的分支。 随意使这更复杂。

首先几个部分是一些检查,争论在那里。 然后,我拉动父回购的最新的东西(我只是喜欢用–ff(快进),只要我只是在拉,我有rebase,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-commands被包装在parantheses(运算符优先级)中。

我最喜欢的部分:

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

迭代所有子模块 – 使用--quiet ,这将删除“inputMODULE_PATH”输出。 使用'echo $path' (必须使用单引号),子模块的path被写入输出。

这个相对子模块path的列表被捕获在一个数组( $(...) )中 – 最后迭代这个,做git add $i来更新父回购。

最后,用一些消息来解释父回购已经更新。 如果没有完成,这个提交将被忽略。 推这个来源,你就完成了。

我有一个脚本运行在jenkins工作,链接到一个计划后自动部署,它像一个魅力。

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

@Jason是正确的,但不完全是。

更新

更新已注册的子模块,即克隆缺less的子模块,并检出包含存储库的索引中指定的提交。 除非指定了–rebase或–merge,或者将子模块$ name.update设置为重新绑定或合并,否则将会使子模块HEAD分离。

所以,git子模块更新检查,但事情是,它是在包含存储库的索引提交。 它还不知道上游的新提交。 所以去你的子模块,得到你想要的提交,并提交更新的子模块状态在主要的回购,然后做的git submodule update

简单而简单,可以获取子模块:

 git submodule update --init --recursive 

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

 git submodule foreach git pull origin master 

这里有一个很棒的单行版本,可以将所有内容更新到最新版本:

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

感谢Mark Jaquith