-
Notifications
You must be signed in to change notification settings - Fork 12
/
git-rebase-all
executable file
·90 lines (79 loc) · 2.63 KB
/
git-rebase-all
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#!/bin/bash
USAGE='<new-upstream> <root>'
OPTIONS_KEEPDASHDASH=
OPTIONS_SPEC="\
git rebase-all <new-upstream> <root>
git rebase-all --continue | --abort | --skip
--
Actions:
continue! continue
abort! abort and check out the original branch
skip! skip current patch and continue
"
. "$(git --exec-path)/git-sh-setup"
require_work_tree_exists
cd_to_toplevel
total_argc=$#
while test $# != 0
do
case "$1" in
--continue|--skip|--abort)
test $total_argc -eq 2 || usage
action=${1##--}
;;
--onto)
test 2 -le "$#" || usage
onto="$2"
shift
;;
--)
shift
break
;;
esac
shift
done
test $# -eq 2 || [ x$action != x ] || usage
if [ $# -eq 2 ]; then
[ x$action != x ] && usage
# Do setup
new_upstream=$1
root=$2
# Fetch all the branches that contain the root of the subtree
branches=$(git for-each-ref --contains $root --no-merged $new_upstream --format='%(refname:strip=2)' refs/heads/)
echo rebasing $branches onto $new_upstream
# Use the tree from the common ancestor of all the branches. It probably
# doesn't matter what tree we use, but the merge-base is a) conveniently
# accessible and b) probably the most sensible thing. Conceptually, this
# equates to committing an octomerge that undoes everything the topic branches
# did, which is a well-defined operation.
merge_base=$(git merge-base $branches)
tree=$(git cat-file commit $merge_base | head -n1 | cut -c6-)
# Create the octomerge: a temporary commit that has all the topic branches as
# parents.
octomerge=$(echo 'merging' $branches |
git commit-tree $tree -p `echo $branches | sed -e's/ / -p /g'`)
git update-ref refs/hidden/octomerge $octomerge || die "couldn't branch"
# The money shot: rebase the octomerge onto the new upstream.
git rebase --preserve-merges $root refs/hidden/octomerge --onto $new_upstream ||
exit $? # if the rebase drops to shell, stop here.
else
[ x$action = x ] && usage
# Pass the action through to git-rebase.
git rebase --$action ||
exit $? # if it drops to shell again, stop again.
branches=`git log --format=%s refs/hidden/octomerge -1`
branches=${branches#merging }
fi
# Move all the old branches to point to the rebased commits.
# The parents of the octomerge remain in the same order through the rebase.
branches_list=($branches)
new_commit_list=(`git rev-parse HEAD^@`)
for index in ${!branches_list[@]}; do
branch=${branches_list[$index]}
echo "moving $branch (was $(git rev-parse --short $branch))"
git branch -f $branch ${new_commit_list[$index]}
done
# and clean up.
git checkout -q ${branches_list[0]}
git update-ref -d refs/hidden/octomerge