Skip to content

Instantly share code, notes, and snippets.

@SergeyAlekseevN
Created November 11, 2019 20:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SergeyAlekseevN/55bcb80ee37cd44e16310f0bbe068493 to your computer and use it in GitHub Desktop.
Save SergeyAlekseevN/55bcb80ee37cd44e16310f0bbe068493 to your computer and use it in GitHub Desktop.
Mass operations for git local repos
#!/bin/sh
COMMAND="$1"
IGNORED_MODULES=$(cat .ignored_modules 2> /dev/null)
RED_COLOR='\033[0;31m'
GREEN_COLOR='\033[0;32m'
GRAY_COLOR='\033[0;2m'
NO_COLOR='\033[0m'
_EXPOSE_PARAMS="shift 2"
is_allowed() {
for M in $IGNORED_MODULES
do
if [ "./$M/" = "$1" ]
then
return 1
fi
done
}
has_branch() {
BRANCH="$1"
[ "$BRANCH" ] || die "No branch was specified"
git rev-parse --quiet --verify "$BRANCH" > /dev/null
return $?
}
die() {
echo "$1"
exit 1
}
# Executed before switching directory to a module, as many times as there are modules.
#
# Parameters:
# 1: module name
_before_module() {
MODULE="$1"
if [ -d "$MODULE/.git" ]
then
cd "$MODULE" || return 1
return
else
echo "$GRAY_COLOR$MODULE$NO_COLOR> not a Git repo"
return 2
fi
}
# Executed after switching directory to a module, as many times as there are
# modules.
#
# Parameters:
# none
_after_module() {
cd .. || return 1
}
cmd_switch_module_branch() {
MODULE="$1"
shift 2 # eat module name and command name
BRANCH="$1"
[ "$BRANCH" ] || die "No branch was specified"
if has_branch "$BRANCH"
then
git checkout --quiet "$BRANCH"
echo "$GREEN_COLOR$MODULE$NO_COLOR switched to $BRANCH"
else
echo "$GRAY_COLOR$MODULE$NO_COLOR> No branch $BRANCH"
fi
}
cmd_pull_rebase() {
MODULE="$1"
shift 2 # eat module name and command name
RESULT=$(git pull --rebase)
echo "$GREEN_COLOR$MODULE$NO_COLOR> $RESULT"
}
cmd_full_merge() {
MODULE="$1";
shift 2 # eat module name and command name
OPTIND=""
GIT_PUSH="true"
COMMIT_MESSAGE_TEXT=""
COMMIT_MESSAGE_PARAM="--log --no-edit -m"
while getopts "pm:" OPT "$@"
do
case "$OPT" in
p)
GIT_PUSH="git push"
;;
m)
COMMIT_MESSAGE_TEXT="$OPTARG"
;;
\?)
echo "Invalid option -$OPT" >&2
exit 1
;;
:)
echo "Option $OPT requires an argument" >&2
exit 1
;;
esac
done
eval SOURCE_BRANCH='$'$OPTIND
eval TARGET_BRANCH='$'$((OPTIND + 1))
[ "$SOURCE_BRANCH" ] || die "No source branch was specified"
[ "$TARGET_BRANCH" ] || die "No target branch was specified"
echo "----------------------------------------------"
echo "Merge $SOURCE_BRANCH -> $TARGET_BRANCH> $MODULE"
if [ -z "$COMMIT_MESSAGE_TEXT" ]
then
COMMIT_MESSAGE_TEXT="Merge $SOURCE_BRANCH into $TARGET_BRANCH"
fi
# shellcheck disable=SC2086
git checkout "$TARGET_BRANCH" && \
git branch --set-upstream-to=origin/"$TARGET_BRANCH" && \
git pull --rebase && \
git merge --no-ff $COMMIT_MESSAGE_PARAM "$COMMIT_MESSAGE_TEXT" "$SOURCE_BRANCH" && \
"$GIT_PUSH" && \
git checkout "$SOURCE_BRANCH"
}
cmd_last_commit_date() {
MODULE="$1"
shift 2 # eat module name and command name
BRANCH="$1"
[ "$BRANCH" ] || die "No branch was specified"
LAST_COMMIT_DATE=$(git log --all --format="%ar;%d" | grep "$BRANCH" | cut -d";" -f1)
printf "%-64s %s\n" "$MODULE" "$LAST_COMMIT_DATE"
}
cmd_list_module() {
MODULE="$1"
pwd
}
cmd_reset_hard() {
MODULE="$1"
shift 2 # eat module name and command name
BRANCH="$1"
[ "$BRANCH" ] || die "No branch was specified"
echo "----------------------------------------------"
echo "Resetting (hard) to branch $BRANCH > $MODULE"
git reset --hard "$BRANCH"
}
cmd_execute_command() {
MODULE="$1"
shift 2 # eat module name and command name
echo "----------------------------------------------"
echo "$MODULE> git" "$@"
git "$@"
}
cmd_fwdiff() {
MODULE="$1"
shift 2 # eat module name and command name
SOURCE_BRANCH="$1"
TARGET_BRANCH="$2"
VERBOSE="$3"
[ "$SOURCE_BRANCH" ] || die "No source branch was specified"
[ "$TARGET_BRANCH" ] || die "No target branch was specified"
if ! has_branch "$SOURCE_BRANCH" || ! has_branch "$TARGET_BRANCH"
then
if [ "$VERBOSE" ]
then
echo "$MODULE> No branch $SOURCE_BRANCH" 2> /dev/null
fi
return
fi
if git merge-base --is-ancestor "$SOURCE_BRANCH" "$TARGET_BRANCH"
then
if [ "$VERBOSE" ]
then
echo "$MODULE> No need to merge $SOURCE_BRANCH to $TARGET_BRANCH"
fi
else
echo "$GREEN_COLOR$MODULE$NO_COLOR> $SOURCE_BRANCH is behind $TARGET_BRANCH"
fi
}
cmd_diff_name_only() {
MODULE="$1"
shift 2 # eat module name and command name
SOURCE_BRANCH="$1"
TARGET_BRANCH="$2"
VERBOSE="$3"
[ "$SOURCE_BRANCH" ] || die "No source branch was specified"
[ "$TARGET_BRANCH" ] || die "No target branch was specified"
if ! has_branch "$SOURCE_BRANCH" || ! has_branch "$TARGET_BRANCH"
then
if [ "$VERBOSE" ]
then
echo "$MODULE> No branch $SOURCE_BRANCH" 2> /dev/null
fi
return
fi
DIFF_RESULT=$(git diff --name-only "$SOURCE_BRANCH" "$TARGET_BRANCH")
if [ "$DIFF_RESULT" ]
then
DIFF_RESULT=$(echo "$DIFF_RESULT" | xargs printf "\t%s\n")
echo "$GREEN_COLOR$MODULE$NO_COLOR>"
echo "$DIFF_RESULT"
else
[ "$VERBOSE" ] && echo "$GRAY_COLOR$MODULE$NO_COLOR> No diff"
fi
}
# Remove a branch that has no changes or diffs with
# the source branch.
cmd_cleanup_unchanged_branches() {
MODULE="$1"
shift 2
SOURCE_BRANCH="$1"
TARGET_BRANCH="$2"
VERBOSE="$3"
[ "$SOURCE_BRANCH" ] || die "No source branch was specified"
[ "$TARGET_BRANCH" ] || die "No target branch was specified"
if ! has_branch "$SOURCE_BRANCH" || ! has_branch "$TARGET_BRANCH"
then
if [ "$VERBOSE" ]
then
echo "$MODULE> No branch $SOURCE_BRANCH" 2> /dev/null
fi
return
fi
DIFF_RESULT=$(git diff --name-only "$SOURCE_BRANCH" "$TARGET_BRANCH")
if [ -z "$DIFF_RESULT" ]
then
# read does not support color codes. We print colored message without \n
# shellcheck disable=SC2059
printf "$GREEN_COLOR$MODULE$NO_COLOR> $TARGET_BRANCH has no differences with $SOURCE_BRANCH. Type 'y' to delete it>"
read -r INPUT
if [ "y" = "$INPUT" ]
then
git checkout "$SOURCE_BRANCH"
git branch -d "$TARGET_BRANCH"
else
echo "$MODULE> $TARGET_BRANCH left intact"
fi
fi
}
cmd_grep_string() {
MODULE="$1"
shift 2 # eat module name and command name
REGEXP="$1"
GIT_LOG=$(git log --all --regexp-ignore-case --pretty=oneline --grep="$REGEXP")
if [ "$GIT_LOG" ]
then
# shellcheck disable=SC2059
printf "$GREEN_COLOR$MODULE$NO_COLOR>\n"
GIT_LOG=$(echo "$GIT_LOG" | xargs -I{} printf '\t%s\n' {})
echo "$GIT_LOG"
fi
}
cmd_update_all_remote_branches() {
MODULE="$1"
shift 2 # eat module name and command name
git fetch --all > /dev/null || die "Unable to fetch all remote branches"
# maintain a list of already tracked branches
ALREADY_TRACKED_BRANCHES=""
# lists all remotes, except entries like 'origin/HEAD -> origin/master'
for REMOTE_BRANCH in $(git branch --remote | grep -Fv ' -> ')
do
LOCAL_BRANCH="${REMOTE_BRANCH#*/}"
if has_branch "$LOCAL_BRANCH"
then
ALREADY_TRACKED_BRANCHES="$ALREADY_TRACKED_BRANCHES $LOCAL_BRANCH"
else
git branch "$LOCAL_BRANCH" --track "$REMOTE_BRANCH"
fi
done
if [ "$ALREADY_TRACKED_BRANCHES" ]
then
echo "$GREEN_COLOR$MODULE$NO_COLOR> Already tracked: $ALREADY_TRACKED_BRANCHES"
fi
}
[ "$COMMAND" ] || die "No command was specified"
for MODULE in ./*/
do
[ -d "$MODULE" ] || die "No module subdirectories. Nothing to do"
if is_allowed "$MODULE"
then
if _before_module "$MODULE"
then
case "$COMMAND" in
list)
cmd_list_module "$MODULE" "$@"
;;
checkout)
cmd_switch_module_branch "$MODULE" "$@"
;;
pull-rebase)
cmd_pull_rebase "$MODULE" "$@"
;;
full-merge)
cmd_full_merge "$MODULE" "$@"
;;
last-commit-date)
cmd_last_commit_date "$MODULE" "$@"
;;
reset-hard)
cmd_reset_hard "$MODULE" "$@"
;;
fwdiff)
cmd_fwdiff "$MODULE" "$@"
;;
dirdiff)
cmd_diff_name_only "$MODULE" "$@"
;;
namediff)
cmd_diff_name_only "$MODULE" "$@"
;;
cleanup-unchanged-branches)
cmd_cleanup_unchanged_branches "$MODULE" "$@"
;;
cub)
cmd_cleanup_unchanged_branches "$MODULE" "$@"
;;
grep)
cmd_grep_string "$MODULE" "$@"
;;
uarb)
cmd_update_all_remote_branches "$MODULE" "$@"
;;
update-all-remote-branches)
cmd_update_all_remote_branches "$MODULE" "$@"
;;
exec)
cmd_execute_command "$MODULE" "$@"
;;
*)
echo "Unknown command: $COMMAND"
exit 1
;;
esac
_after_module
fi
fi
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment