3 # Moves a branch to or from the current git tree to or from
6 # usage: git-branchmove get|put REMOTE PATTERN
11 fail () { echo >&2 "git-branchmove: $*"; exit 16; }
12 badusage () { fail "bad usage: $*"; }
14 if [ $# -lt 3 ]; then badusage "too few arguments"; fi
17 case "$op" in get|put) ;; *) badusage "unknown operation \`$op'"; esac
22 # determine execute-sh runes for src and dst trees
23 # list affected branches on source
24 # check that source branches are not checked out
25 # list affected branches on destination and moan if any nonequal overlap
26 # transfer src->dst refs/heads/BRANCH:refs/heads/BRANCH
27 # transfer and merge reflog(s) xxx todo
31 *:*) remoteurl="$remote" ;;
32 [/.]*) remoteurl="$remote" ;;
34 git config remote."$remote".pushurl ||
35 git config remote."$remote".url ||
36 fail "no pushurl or url defined for remote $remote"
40 remote_spec="$(perl -e '
42 if (m#^ssh://([^:/]+)(?:\:(\w+))?#) {
46 } elsif (m#^([-+_.0-9a-zA-Z\@]+):(?!//|:)#) {
47 print "$'\''|ssh $1\n";
49 print "$&|sh -c $1\n";
51 die "git-branchmove: unsupported remote url \`$_'\''\n";
55 remote_path="${remote_spec%%|*}"
56 remote_rune="${remote_spec#*|}"
60 src_rune="$remote_rune"
61 src_path="$remote_path"
64 msg="git-branchmove: moved to $remote ($remoteurl)" ;;
68 dst_rune="$remote_rune"
69 dst_path="$remote_path"
72 msg="git-branchmove; moved to `hostname -f` by `whoami`" ;;
77 on_src () { $src_rune "set -e; cd $src_path; $*"; }
78 on_dst () { $dst_rune "set -e; cd $dst_path; $*"; }
81 #----- fetch the current refs from both sides -----
84 for branch_pat in "$@"; do
85 branch_pats+=" '[r]efs/heads/$branch_pat'"
89 git for-each-ref --format="%(refname)=%(objectname)" '"$branch_pats"'
95 git symbolic-ref -q HEAD || test $? = 1
97 '"$get_branches_rune"'
101 src_head="${src_branches[0]}"
102 unset src_branches[0]
103 : "${src_branches[@]}"
106 H) ;; # already detached
108 src_head="${src_head#H}"
109 for check in "${src_branches[@]}"; do
112 fail "would delete checked-out branch $src_head"
119 dst_branches=( $(on_dst "$get_branches_rune") )
120 : "${dst_branches[@]}"
123 #----- check for nonequal overlaps -----
126 for dst_check in "${dst_branches[@]}"; do
127 dst_ref="${dst_check%=*}"
128 for src_check in "${src_branches[@]}"; do
133 echo >&2 "src: $src_check dst: $dst_check"
139 $ok || fail "would overwrite some destination branch(es)"
142 #----- do the transfer -----
145 for src_xfer in "${src_branches[@]}"; do
146 src_ref="${src_xfer%=*}"
147 refspecs+=("$src_ref:$src_ref")
151 put) git push "$remote" "${refspecs[@]}" ;;
152 get) git fetch "$remote" "${refspecs[@]}" ;;
153 *) fail "unknown $op ???" ;;
157 #----- delete the refs on the source -----
160 printf "%s\n" "$message"
161 for src_rm in "${src_branches[@]}"; do printf "%s\n" "$src_rm"; done
164 while read src_rm; do
165 src_ref="${src_rm%=*}"
166 src_obj="${src_rm##*=}"
167 git update-ref -m "$message" -d "$src_ref" "$src_obj"
168 echo "move complete: $src_ref"
172 echo 'moved ${#src_branches[@]} branches.'