summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xgit-rewrite-branch149
-rwxr-xr-xgit-rewrite-branch--appendid4
2 files changed, 153 insertions, 0 deletions
diff --git a/git-rewrite-branch b/git-rewrite-branch
new file mode 100755
index 0000000..a2b3c81
--- /dev/null
+++ b/git-rewrite-branch
@@ -0,0 +1,149 @@
+#!/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 #################################################################
+
+ 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
+
+ # Just to be safe
+ git branch -D "$obranch.tmp" 2>/dev/null || true
+
+ wbranch="$obranch.tmp"
+ 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
+ else
+ # obranch does not exist, create it
+ echo 'Creating new rewritten branch...'
+ wbranch=$obranch
+ revlist=$wbranch
+ fi
+
+ git checkout "$ibranch"
+ git checkout -b "$wbranch"
+ git filter-branch -f "${filters[@]}" "$revlist"
+
+ if [[ "$obranch" != "$wbranch" ]]; 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"
+ fi
+}
+
+main "$@"
diff --git a/git-rewrite-branch--appendid b/git-rewrite-branch--appendid
new file mode 100755
index 0000000..e44180a
--- /dev/null
+++ b/git-rewrite-branch--appendid
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+tag=$1
+sed '$a'"${tag}: ${GIT_COMMIT}"