-
-
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)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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