Skip to content

Instantly share code, notes, and snippets.

@borgand
Last active January 7, 2019 06:44
Show Gist options
  • Save borgand/5441827 to your computer and use it in GitHub Desktop.
Save borgand/5441827 to your computer and use it in GitHub Desktop.
A helper script to set *svn:mergeinfo* property when using `git svn dcommit` on merged git branches.This makes it possible to merge two SVN branches using **git-svn**.NB! the merged-from branch **MUST** be pushed to SVN.USAGE: git merge-svn <branch name>EDIT: added exit condition when mergeinfo calculation fails to avoid pushing incomplete merge…
#!/bin/bash
function usage {
echo "USAGE: git merge-svn <from> [<to>]"
echo ""
echo " from The branch name to be merged FROM"
echo " to Optional branch name to be merged onto. Default: HEAD"
echo ""
}
# CHANGE THIS: change this to match the SVN remote name
SVN_REMOTE_NAME=svn
# get svn base URL and remove username from it (if it's there)
SVN_BASE=$(git config svn-remote.$SVN_REMOTE_NAME.url | sed -e "s#//.*@#//#")
# Set FROM and TO from arguments
FROM=$1
TO=$2
# If FROM is not given, we can't really do anything
if [[ -z $FROM ]]; then
echo "ERROR: From branch not given"
usage
exit 1
fi
# If TO is not given, calculate if from HEAD
if [[ -z $TO ]]; then
TO=$(git rev-parse --abbrev-ref HEAD)
fi
# ensure we are on TO
git checkout $TO > /dev/null 2>&1
# Get current commit on FROM
LAST=$(git rev-parse $FROM)
# Get common ancestor
ANCESTOR=$(git merge-base $FROM $TO)
# if ancestor is last commit on FROM these two branches are already merged
# get previous ancestor to calculate MERGEINFO
if [[ $ANCESTOR == $LAST ]]; then
GIT_MERGED="true"
ANCESTOR=$(git merge-base $FROM "$TO~1")
fi
# Get some info to show
ANCESTOR_SVN_REV=$(git svn find-rev $ANCESTOR)
ANCESTOR_SHORT_REV=$(git rev-parse --short $ANCESTOR)
TO_SHORT_REV=$(git rev-parse --short $TO)
TO_SVN_REV=$(git svn find-rev $TO)
FROM_SHORT_REV=$(git rev-parse --short $FROM)
FROM_SVN_REV=$(git svn find-rev $FROM)
# Verify that FROM is dcommitted to SVN
if [[ -z $FROM_SVN_REV ]]; then
echo "Branch $FROM is not pushed (dcommitted) to SVN!"
echo "Aborting!"
exit
fi
# Nothing to do if git-merged and svn dcommitted
if [[ $GIT_MERGED == "true" && ! -z $TO_SVN_REV ]]; then
echo "Already up to date"
exit
fi
###############
# The real work
###############
# Parse merged commit messages for SVN revision number
NEWMERGEINFO=$(git log --reverse $ANCESTOR..$FROM | grep "git-svn-id" | tr -s ' ' | cut -f3 -d' ' | sed -e "s#$SVN_BASE##" | sed -e "s/@/:/") || exit 1
# Get previous mergeinfo
OLDMERGEINFO=$(git svn propget svn:mergeinfo)
# Build new mergeinfo
if [[ $? == 0 ]]; then
MERGEINFO="$OLDMERGEINFO
$NEWMERGEINFO"
else
MERGEINFO="$NEWMERGEINFO"
fi
echo "About to do an SVN merge: $FROM -> $TO"
# Tweak graph depending on merge status
if [[ $GIT_MERGED == "true" ]]; then
echo "
* $TO [$TO_SHORT_REV]
|\\
| * $FROM [$FROM_SHORT_REV] (r$FROM_SVN_REV)"
else
echo "
* NEW MERGE COMMIT
|\\
| * $FROM [$FROM_SHORT_REV] (r$FROM_SVN_REV)
* | $TO [$TO_SHORT_REV] (r$TO_SVN_REV)"
fi
echo " \\|
* [$ANCESTOR_SHORT_REV] (r$ANCESTOR_SVN_REV)
"
echo -n "STEP 1: GIT merge "
if [[ -z $GIT_MERGED ]]; then
echo ""
echo "Executing:
git merge --no-ff $FROM"
echo
echo -n "Continue? (y/n) [n]: "
read ANSWER
if [[ $ANSWER == 'y' ]]; then
git merge --no-ff $FROM
echo ""
else
echo "Aborting!"
exit
fi
else
echo "(already up to date)"
fi
echo "STEP 2: SVN dcommit"
echo ""
echo "executing:
git svn dcommit --mergeinfo"
echo $MERGEINFO
echo ""
echo -n "Continue? (y/n) [n]: "
read ANSWER
if [[ $ANSWER == 'y' ]]; then
git svn dcommit --mergeinfo "$MERGEINFO"
else
echo "Aborting!"
exit
fi
@ryanfitzer
Copy link

This looks like a great solution, but does not handle the case of merge conflicts. Is there any way to restart the process after merge conflicts are resolved?

@ryanfitzer
Copy link

Another issue I found (which might be specific to just me): when the process is successful (no conflicts were produced), after the dcommit, I'm not longer on any branch. Running git status returns "Not currently on any branch."

I verified that the commit and dcommit were applied to the proper local and remote branches. Checking out the branch also shows the correct file updates. Any ideas why this would happen?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment