Pushing all submodules recursively
The git-powercommit script I wrote recently does push submodules recursively as a part of its workflow. Under the hood, there is a map-like iterator which uses git-status --porcelain=v2
to iterate over repository objects. Here is its definition:
mapchanges() {(
set -e -x
local filter="$1"
local commiter="$2"
git status --no-renames --porcelain=v2 | \
while read N XY sub mH mI mW hH hI path ; do
$filter $N $XY $sub $mH $mI $mW $hH $hI "$path"
done | \
sort -u --reverse | \
while read path; do
$commiter "$path"
done
)}
In order to iterate over submodules, you need to provide it with the filter and the action callback functions. In your case, the filter function could be:
filter_subm() {
# Inputs are according to `git status --porcelain=v2` spec. The function
# filters submodules which has changed commits.
local sub="$3"; shift 8; local path="$1"
case "$sub" in
SC??) echo "$path" ;;
*) ;;
esac
}
As for the action function, the origina script does commit the whole submodule, but in your case you could insert the push commands like the following
push_subm() {
local path="$1"
(cd -P "$path" && git push origin; )
}
Now, we bring everything together with the line like
mapchanges filter_subm push_subm
Please, consider reviewing the original script to find out the details on how to organize the recursion.
The command git push --recurse-submodules=on-demand
does not work if you have submodules which contain submodules. (git
version: 2.20.1)
Add the following alias in your ~/.gitconfig
file:
[alias]
push-all = "! find . -depth -name .git -exec dirname {} \\; 2> /dev/null | sort -n -r | xargs -I{} bash -c \"cd {}; git status | grep ahead > /dev/null && { echo '* Pushing: {}'; git push; }\""
Then issue git push-all
in your parent git folder.
Explanation
!
: We are issuing a non-git command.find . -depth -name .git -exec dirname {} \\; 2> /dev/null
: Find all submodules (and git repositories, which wouldn't harm)| sort -n -r
: Sort by path depth, deepest will be first.| xargs -I{} bash -c \"
: Pass directories to the following commands:cd {};
:cd
to the target directory.git status | grep ahead > /dev/null
: Test if it is necessary to push this repository.&& { echo '* Pushing: {}'; git push; }\""
: Inform and push.
git1.7.11 ([ANNOUNCE] Git 1.7.11.rc1) mentions:
"git push --recurse-submodules" learned to optionally look into the histories of submodules bound to the superproject and push them out.
So you can use:
git push --recurse-submodules=on-demand
You can use git submodule foreach
to run any desired command on each submodule, e.g.
git submodule foreach git push origin master
See: man git-submodule
.