#!/bin/bash -euE # when $gitmode is true, $ibranch's commits are used as IDs gitmode=true tag='git-rewrite-id' ibranch='' obranch='' wbranch='' usage() { echo 'malformed call to internal function' } verbose() { : "$*" #echo "$*" } ################################################################################ id2commit() { [[ $# = 2 ]] || { usage; return 1; } local branch=$1 local id=$2 if [[ $branch == $ibranch ]] && $gitmode; then printf '%s\n' "$id" else git log "$branch" --pretty=format:'%H' --grep "${tag}: ${id}" fi } commit2id() { [[ $# = 2 ]] || { usage; return 1; } local branch=$1 local commit=$2 if [[ $branch == $ibranch ]] && $gitmode; then printf '%s\n' "$commit" else git log "$branch" -n1 --pretty=formtat:'%B' "$commit" | sed -n "s|^\s*${tag}: ||p" fi } # commit2commit c2c() { [[ $# = 3 ]] || { usage; return 1; } local from=$1 local to=$2 local commit=$3 if [[ "$from" == "$to" ]]; then # optimization # also, properly normalizes $ibranch when $gitmode=true git log "$commit" -n1 --pretty=format:'%H' else id2commit "$to" "$(commit2id "$from" "$commit")" fi } ################################################################################ main() { # Parse command line arguments ######################################### if [[ $1 = '--svn' ]]; then gitmode=false tag='git-svn-id' shift fi if [[ $# < 3 ]]; then usage exit 1 fi ibranch=$1 obranch=$2 shift 2 local filters=(); if $gitmode; then filters=(--msg-filter "git-rewrite-branch--appendid '${tag}'") fi filters+=("$@") # Main ################################################################# wbranch="$obranch.tmp" local revlist local rebase if git checkout "$obranch" -- 2>/dev/null; then # obranch exists, update it echo 'Updating existing rewritten branch...' local icommit="$(c2c "$ibranch" "$ibranch" "$ibranch")" local ocommit="$(c2c "$obranch" "$ibranch" "$obranch")" if [[ "$icommit" == "$ocommit" ]]; then echo "Nothing to do" return 0 fi local common="$(c2c "$obranch" "$ibranch" "$obranch")" if c2c "$ibranch" "$ibranch" "${common}^" &>/dev/null; then revlist="$(c2c "$obranch" "$ibranch" "$obranch")^..${wbranch}" else # There is only one commit from $ibranch in $obranch revlist="$wbranch" fi rebase=true else # obranch does not exist, create it echo 'Creating new rewritten branch...' revlist=$wbranch rebase=false fi git checkout "$ibranch" git branch -D "$wbranch" 2>/dev/null || true git checkout -b "$wbranch" git filter-branch -f "${filters[@]}" "$revlist" if $rebase; then # rebase the changes in wbranch onto obranch echo 'Rebasing rewrites onto existing branch...' local wcommit="$(c2c "$wbranch" "$ibranch" "$wbranch")" if [[ "$wcommit" == "$ocommit" ]]; then echo "Nothing to do" return 0 fi local commonish="$(c2c "$obranch" "$wbranch" "$obranch")" cmd=(git rebase --onto "$obranch" "$commonish" "$wbranch") verbose verbose " o---o---o $obranch" verbose ' :' verbose " o---o---o---o---o $ibranch" verbose ' \ :' verbose ' `-C---o---o '"$wbranch" verbose verbose " C = $commonish" verbose verbose " ${cmd[*]}" verbose "${cmd[@]}" git checkout "$obranch" git branch -d "$wbranch" else git branch -m "$wbranch" "$obranch" fi } main "$@"