+on_src () { $src_rune "set -e; cd $src_path; $*"; }
+on_dst () { $dst_rune "set -e; cd $dst_path; $*"; }
+
+
+#----- fetch the current refs from both sides -----
+
+branch_pats=''
+for branch_pat in "$@"; do
+ branch_pats+=" '[r]efs/heads/$branch_pat'"
+done
+
+get_branches_rune='
+ git for-each-ref --format="%(refname)=%(objectname)" '"$branch_pats"'
+'
+
+src_branches=( $(
+ on_src '
+ printf H
+ git symbolic-ref -q HEAD || test $? = 1
+ echo " "
+ '"$get_branches_rune"'
+ '
+))
+
+src_head="${src_branches[0]}"
+unset src_branches[0]
+: "${src_branches[@]}"
+
+case "$src_head" in
+H) ;; # already detached
+*)
+ src_head="${src_head#H}"
+ for check in "${src_branches[@]}"; do
+ case "$check" in
+ "$src_head"=*)
+ fail "would delete checked-out branch $src_head"
+ ;;
+ esac
+ done
+ ;;
+esac
+
+dst_branches=( $(on_dst "$get_branches_rune") )
+: "${dst_branches[@]}"
+
+
+#----- check for nonequal overlaps -----
+
+ok=true
+for dst_check in "${dst_branches[@]}"; do
+ dst_ref="${dst_check%=*}"
+ for src_check in "${src_branches[@]}"; do
+ case "$src_check" in
+ "$dst_check") ;;
+ "$dst_ref"=*)
+ ok=false
+ echo >&2 "src: $src_check dst: $dst_check"
+ ;;
+ esac
+ done
+done
+
+$ok || fail "would overwrite some destination branch(es)"
+
+
+#----- do the transfer -----
+
+refspecs=()
+for src_xfer in "${src_branches[@]}"; do
+ src_ref="${src_xfer%=*}"
+ refspecs+=("$src_ref:$src_ref")
+done
+
+case "$op" in
+put) git push "$remote" "${refspecs[@]}" ;;
+get) git fetch "$remote" "${refspecs[@]}" ;;
+*) fail "unknown $op ???" ;;
+esac
+
+
+#----- delete the refs on the source -----
+
+(
+ printf "%s\n" "$message"
+ for src_rm in "${src_branches[@]}"; do printf "%s\n" "$src_rm"; done
+) | on_src '
+ read message
+ while read src_rm; do
+ src_ref="${src_rm%=*}"
+ src_obj="${src_rm##*=}"
+ git update-ref -m "$message" -d "$src_ref" "$src_obj"
+ echo "move complete: $src_ref"
+ done
+'
+
+echo 'moved ${#src_branches[@]} branches.'