Skip to content

Instantly share code, notes, and snippets.

@danthe1st
Last active August 10, 2022 20:59
Show Gist options
  • Save danthe1st/afc0990804dc1199b67baffc853ff504 to your computer and use it in GitHub Desktop.
Save danthe1st/afc0990804dc1199b67baffc853ff504 to your computer and use it in GitHub Desktop.
safe git rebase (pre-rebase hook)

Description

This gist contains a script of mine that prevents unsafe rebases.

I consider a rebase as unsafe if there is work based on the branch to rebase.

This works should work cross platform as long as bash is installed correctly on /bin/bash (git for windows comes with bash).

This hook can be useful if git is configured to do a rebase instead of a merge when pulling.

How to use

The script (the pre-rebase file) needs to be in the hooks directory of the local git repository (the .git directory) you want to work it in.

If you want the script to be added to the hooks directory automatically, you can create a template directory for git repositories.

To do that, you just need to create a directory with a subdirectory hooks. After that, set the init.templatedir config to that directory (git config --global init.templatedir <your template directory>) and copy the script (the pre-rebase file) to the directory.

#!/bin/bash
function printDelim(){
echo "====================================================================================================="
}
function debug(){
if [[ -n "$DEBUG" ]]; then
echo -e "[DEBUG] \e[90m$*\e[0m"
fi
}
function info(){
echo -e "\e[94m$*\e[0m"
}
function err(){
echo -e "\e[91m$*\e[0m"
}
function success(){
echo -e "\e[92m$*\e[0m"
}
if [[ -n "$SKIP_REBASE_CHECK" ]] && [[ "$SKIP_REBASE_CHECK" != "0" ]]; then
info "rebase check skipped because SKIP_REBASE_CHECK is set (and not 0)"
exit 0
fi
from="$1"
to="$2"
if [[ -z "$to" ]]; then
to="HEAD"
fi
printDelim
info "trying to rebase $to from $from"
info "testing if there is work that is based of newer commits"
printDelim
base="$(git merge-base "$from" "$to")"
debug "base: $base"
for branch in $(git for-each-ref --format='%(refname)'); do
if [[ "$branch" == \** ]]; then
debug "skip $branch because of asterisk"
elif [[ "$(git rev-parse "$branch")" != "$(git rev-parse "$to")" ]]; then
debug "testing branch: $branch"
common="$(git merge-base "$branch" "$to")"
if [[ -z "$common" ]]; then
debug "skip $branch because no common ancestor has been found"
elif [[ -z "$(git log "$base".."$common")" ]]; then
debug "branch $branch ok"
else
debug "Problems with branch $branch" > /dev/stderr
printDelim
err "\"$branch\" contains work that is based on work that would be rebased."
err "It is possibly unsafe to continue this rebase."
err "Try to fix those problems or run this command again using with the variable \"SKIP_REBASE_CHECK\" set to non-zero if you still want to rebase."
printDelim
exit 255
fi
else
debug "skipping $branch $branch as it is the branch to rebase"
fi
done
printDelim
success "This rebase seems safe."
printDelim
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment