Skip to content

Instantly share code, notes, and snippets.

@pyropeter
Created January 10, 2014 22:15
Show Gist options
  • Save pyropeter/8363759 to your computer and use it in GitHub Desktop.
Save pyropeter/8363759 to your computer and use it in GitHub Desktop.
Ugly shell script to do a bidirectional sync of two git repos (Like monotones sync command)
#!/bin/bash
set -e
set -u
# this script assumes the following:
# * you want to sync the master[0-9]+ branches
# * you are in a non-bare repo
# * this script runs interactive (maybe not needed?)
# * the remote to sync with is 'origin' and a bare repo
# What this does:
# 1. fetch
# 2. find all branches (local and remote)
# 3. get independent heads
# 4. do stuff with local branches to have one branch for each independent head
# 5. for each local branch, search for a remote branch with common ancestor.
# * if there is more than one branch with common ancestor, I have no f**** idea
# what I'm doing.
# * If there are not enough remote branches, create new ones by searching for
# an unused name
# 6. push local branches to matching remote branches
remote=origin
branchbase=master
# get up-to-date copy of remote repository
git fetch "$remote"
# get a list of all interesting remote branches
remotes="`git show-ref | grep "refs/remotes/$remote/$branchbase[0-9][0-9]*" | cut -d/ -f2-`"
# get a list of all interesting local branches
locals="`git show-ref | grep "refs/heads/$branchbase[0-9][0-9]*" | cut -d/ -f2-`"
# at the end, there should be a local and a remote branch for each of
# the commits listed by:
heads="`git merge-base --independent $locals $remotes`"
# do stuff with local branches
echo Merging heads into local branches.
echo
declare -A branchesToFill
for branch in $locals; do
branchesToFill[$branch]="$branch"
done
for head in $heads; do
echo Trying to merge head "$head".
if [ ${#branchesToFill[*]} -gt 0 ]; then
commit="`git merge-base "$head" "${branchesToFill[@]}"`"
echo Using commit "$commit" as merge base.
branch="`git name-rev --no-undefined --name-only --refs="heads/$branchbase*" "$commit" \
2>/dev/null || true`"
if [ -n "$branch" ]; then
echo Commit is on branch "$branch". Fast-forwarding.
else
echo Commit is on no branch, creating a new one.
fi
else
echo "There is no branch left to fill, creating a new one."
branch=
fi
if [ -z "$branch" ]; then
# create new branch
for ((i=0;; i++)); do
if git update-ref -m 'sync: create new branchbase' "refs/heads/$branchbase$i" "$head" "" \
2>/dev/null; then
echo Created new branch "$branchbase$i".
break
fi
done
else
# fast-forward branch $symbolic to commit $head
git update-ref -m 'sync: fast-forward' "refs/heads/$branch" "$head" "$commit"
# remove branch from list
unset branchesToFill[heads/$branch]
fi
echo Successfully merged head "$head" into local branches.
echo
done
# delete all branches still in list
if [ ${#branchesToFill[*]} -gt 0 ]; then
for branch in "${branchesToFill[@]}"; do
echo Deleting unused branch "$branch"
git branch -d "$branch"
done
fi
# update the list of all interesting local branches
locals="`git show-ref | grep "refs/heads/$branchbase[0-9][0-9]*" | cut -d/ -f2-`"
echo Done.
echo
echo Searching for matching branches on remote
declare -A matches
declare -A unusedRemotes
for branch in $remotes; do
unusedRemotes[$branch]="$branch"
done
for branch in $locals; do
for other in "${unusedRemotes[@]}"; do
if git merge-base --is-ancestor "$other" "$branch"; then
matches[$branch]="${other##*/}"
unset unusedRemotes[$other]
break
fi
done
if [ "${matches[$branch]:-unset}" = unset ]; then
for ((i=0;; i++)); do
if git show-ref --quiet --verify "refs/remotes/$remote/$branchbase$i"; then
continue
fi
matches[$branch]="$branchbase$i"
break
done
fi
done
echo Done:
for branch in $locals; do
echo -e \\t "$branch" \\t '->' \\t "${matches[$branch]}"
done
echo
echo Pushing results to the remote.
declare -A refspec
for branch in $locals; do
refspec[$branch]="$branch:${matches[$branch]}"
done
echo git push "$remote" "${refspec[@]}"
git push "$remote" "${refspec[@]}"
echo Done.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment