Skip to content

Instantly share code, notes, and snippets.

@eeichinger
Last active March 29, 2024 06:18
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save eeichinger/1044107a1126901249b1164dac2fce15 to your computer and use it in GitHub Desktop.
Save eeichinger/1044107a1126901249b1164dac2fce15 to your computer and use it in GitHub Desktop.
Recursively execute git commands, find unpushed commits, ...
#
# Description:
# Various additional git utility commands
#
# git recursive <command>:
# from the current working folder search recursively for local git repos and run 'git <command>' on each of them.
#
# git recursive-exec <command>:
# from the current working folder search recursively for local git repos and run '<command>' on each of them.
#
# git pull-recursive:
# from the current working folder search recursively for local git repos and run 'git pull-all' on each of them.
#
# git pull-all:
# in a git repo folder loop over all tracking branches and call 'git pull' on them
#
# git unpushed-recursive:
# from the current working folder search recursively for local git repos and run 'git unpushed-all' on each of them.
#
# git unpushed-all:
# in a git repo folder loop over all tracking branches and call 'git --no-pager log @{push}..' on them to list unpushed commits.
#
#
# Install:
# 1. checkout the gist repo into a folder ~/bin/git:
# mkdir -p ~/bin >/dev/null && pushd ~/bin && git clone git@gist.github.com:1044107a1126901249b1164dac2fce15.git git
# 2. add ~/bin/git to your path (e.g. via ~/.profile)
#!/bin/sh
#
# Description:
# Update all local branches, checking out each branch in succession.
# Eventually returns to the original branch. Use "-n" for dry-run.
# (inspired from http://stackoverflow.com/a/17180894/51264)
#
# Performs the following:
# 1. stash away all pending changes
# 2. prune obsolete tracking branches
# 3. git pull on all tracking branches
# 4. pop back all changes
# 5. open sourcetree
#
# Usage:
# git pull-all [-n|--dryrun] [-s|--sourcetree]
git_pull_all() {
local run currentBranch old_stash new_stash
[[ "$1" = "-n" || "$1" = "--dryrun" ]] && shift && run=echo
[[ "$1" = "-s" || "$1" = "--sourcetree" ]] && shift && opensourcetree="open -a SourceTree ."
$run git remote update --prune;
currentBranch=$(git name-rev --name-only HEAD 2>/dev/null)
old_stash=$(git rev-parse -q --verify refs/stash);
$run git stash save -q -u 'before pull';
new_stash=$(git rev-parse -q --verify refs/stash);
for x in $( git branch | cut -c3- ) ; do
echo ">>> Updating `pwd` git:($x)"
($run git checkout $x && $run git pull --ff-only || exit 2)
done
[ ${#currentBranch} -gt 0 ] && $run git checkout "$currentBranch" >/dev/null
if [ "$old_stash" != "$new_stash" ]; then
$run git stash pop;
fi
$run $opensourcetree
}
git_pull_all "$@"
#!/bin/sh
#
# Description:
# from the current working folder search recursively for local git repos and run 'git pull-all' on them. All arguments to 'git pull-recursive' are passed to 'git pull-all'
# Basically a convenience shortcut for 'git recursive pull-all'
#
# Usage:
# git pull-recursive [args...]
git_pull_recursive() {
local dir
for f in */.git/; do
if [[ -d "$f" && ! -L "$f" ]]; then
dir=$(dirname $f)
echo ">>> Updating repository folder './$dir'"
(pushd $dir; git pull-all "$@" || exit 2)
fi
done
}
git_pull_recursive "$@"
#!/bin/sh
#
# Description:
# execute git commands recursively in all repository folders
#
# Usage:
# git recursive [-n|--dryrun] <git command>
#
# Example
# git recursive remote -v # prints remote info for each git repo
#
git_recursive() {
local dir run
[[ "$1" = "-n" || "$1" = "--dryrun" ]] && shift && run=echo
for f in */.git/; do
if [[ -d "$f" && ! -L "$f" ]]; then
dir=$(dirname $f)
echo ">>> Executing Repository folder './$dir'"
(export SLUG="$dir" && args=`echo "$*" | envsubst` && pushd $dir 1>/dev/null; eval $run git --no-pager "$args" || exit 2)
res=$?
if [ "$res" -gt 0 ] ; then
echo "Error: $res"
#exit $res;
fi
fi
done
}
git_recursive "$@"
#!/bin/sh
#
# Description:
# execute commands recursively in all git repository folders
#
# Usage:
# git recursive-exec [-n|--dryrun] <command>
#
# Example
# git recursive-exec open -a SourceTree . # open SourceTree for each repo folder
#
git_recursive() {
local dir run
run="sh -c"
[[ "$1" = "-n" || "$1" = "--dryrun" ]] && shift && run=echo
for f in */.git/; do
if [[ -d "$f" && ! -L "$f" ]]; then
dir=$(dirname $f)
echo ">>> Executing Repository folder './$dir'"
(pushd "$dir" 1>/dev/null && $run "$@")
res=$?
if [ "$res" -gt 0 ] ; then
exit $res;
fi
fi
done
}
git_recursive "$@"
#!/bin/sh
#
# Description:
# Fetch origin and for each tracking branch list any unpushed local commits
#
# Usage:
# git unpushed-all [-n|--dryrun]
git_unpushed_all() {
local run currentBranch log res
[[ "$1" = "-n" || "$1" = "--dryrun" ]] && shift && run=echo
currentBranch=$(git name-rev --name-only HEAD 2>/dev/null)
$run git fetch --all
for x in $( git branch | cut -c3- ) ; do
$run git checkout $x &>/dev/null
log=$(git --no-pager log origin/$x.. 2>/dev/null)
res=$?
if [[ "$res" -eq 0 && "${#log}" -gt 0 ]]; then
echo ">>> Branch '$x' has unpublished updates:"
echo "$log"
fi
done
[ ${#currentBranch} -gt 0 ] && $run git checkout "$currentBranch" >/dev/null 2>&1
}
git_unpushed_all "$@"
#!/bin/sh
#
# Description:
# from the current working folder search recursively for local git repos and run 'git unpushed-all' on each of them.
#
# Usage:
# git unpushed-recursive [args...]
git_unpushed_recursive() {
local dir
for f in */.git/; do
if [[ -d "$f" && ! -L "$f" ]]; then
dir=$(dirname $f)
echo ">>> Checking repository folder './$dir'"
(pushd $dir >/dev/null; git unpushed-all "$@";)
fi
done
}
git_unpushed_recursive "$@"
@elonderin
Copy link

elonderin commented Jul 11, 2022

thx!. At least git-recursive needs a bash, ie #!/usr/bin/env bash

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment