I have a project with a Git submodule. It is from an ssh://... URL, and is on commit A. Commit B has been pushed to that URL, and I want the submodule to retrieve the commit
If you are looking to checkout master
branch for each submodule -- you can use the following command for that purpose:
git submodule foreach git checkout master
Here's an awesome one-liner to update everything to the latest on master:
git submodule foreach 'git fetch origin --tags; git checkout master; git pull' && git pull && git submodule update --init --recursive
Thanks to Mark Jaquith
The git submodule update command actually tells Git that you want your submodules to each check out the commit already specified in the index of the superproject. If you want to update your submodules to the latest commit available from their remote, you will need to do this directly in the submodules.
So in summary:
# 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"
Or, if you're a busy person:
git submodule foreach git pull origin master
It seems like two different scenarios are being mixed together in this discussion:
Scenario 1
Using my parent repository's pointers to submodules, I want to check out the commit in each submodule that the parent repository is pointing to, possibly after first iterating through all submodules and updating/pulling these from remote.
This is, as pointed out, done with
git submodule foreach git pull origin BRANCH
git submodule update
Scenario 2, which I think is what OP is aiming at
New stuff has happened in one or more submodules, and I want to 1) pull these changes and 2) update the parent repository to point to the HEAD (latest) commit of this/these submodules.
This would be done by
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
Not very practical, since you would have to hardcode n paths to all n submodules in e.g. a script to update the parent repository's commit pointers.
It would be cool to have an automated iteration through each submodule, updating the parent repository pointer (using git add
) to point to the head of the submodule(s).
For this, I made this small Bash script:
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
To run it, execute
git-update-submodules.sh /path/to/base/repo BRANCH_NAME
Elaboration
First of all, I assume that the branch with name $BRANCH (second argument) exists in all repositories. Feel free to make this even more complex.
The first couple of sections is some checking that the arguments are there. Then I pull the parent repository's latest stuff (I prefer to use --ff (fast-forwarding) whenever I'm just doing pulls. I have rebase off, BTW).
git checkout $BRANCH && git pull --ff origin $BRANCH
Then some submodule initializing, might be necessary, if new submodules have been added or are not initialized yet:
git submodule sync
git submodule init
git submodule update
Then I update/pull all submodules:
git submodule foreach "(git checkout $BRANCH && git pull --ff origin $BRANCH && git push origin $BRANCH) || true"
Notice a few things: First of all, I'm chaining some Git commands using &&
- meaning previous command must execute without error.
After a possible successful pull (if new stuff was found on the remote), I do a push to ensure that a possible merge-commit is not left behind on the client. Again, it only happens if a pull actually brought in new stuff.
Finally, the final || true
is ensuring that script continues on errors. To make this work, everything in the iteration must be wrapped in the double-quotes and the Git commands are wrapped in parentheses (operator precedence).
My favourite part:
for i in $(git submodule foreach --quiet 'echo $path')
do
echo "Adding $i to root repo"
git add "$i"
done
Iterate all submodules - with --quiet
, which removes the 'Entering MODULE_PATH' output. Using 'echo $path'
(must be in single-quotes), the path to the submodule gets written to output.
This list of relative submodule paths is captured in an array ($(...)
) - finally iterate this and do git add $i
to update the parent repository.
Finally, a commit with some message explaining that the parent repository was updated. This commit will be ignored by default, if nothing was done. Push this to origin, and you're done.
I have a script running this in a Jenkins job that chains to a scheduled automated deployment afterwards, and it works like a charm.
I hope this will be of help to someone.
the simplest way to handle git projects containing submodules is to always add
--recurse-submodules
at the end of each git command example:
git fetch --recurse-submodules
another
git pull --update --recurse-submodules
etc...
Git 1.8.2 features a new option, --remote
, that will enable exactly this behavior. Running
git submodule update --remote --merge
will fetch the latest changes from upstream in each submodule, merge them in, and check out the latest revision of the submodule. As the documentation puts it:
--remote
This option is only valid for the update command. Instead of using the superproject’s recorded SHA-1 to update the submodule, use the status of the submodule’s remote-tracking branch.
This is equivalent to running git pull
in each submodule, which is generally exactly what you want.