A utility to perform merges between Subversion branches using git
PATCH 1/1 A utility to perform merges between Subversion branches using git
--- contrib/svn/README | 8 +++ contrib/svn/git-svnmerge | 139 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 147 insertions(+), 0 deletions(-) create mode 100644 contrib/svn/README create mode 100755 contrib/svn/git-svnmerge
contrib/svn/README new file +8 -0
@@ -0,0 +1,8 @@ +This directory contains tools useful for a mixed git/Subversion environment. +They will likely all depend on git-svnimport or git-svn, which are the main +Subversion bridges in git. + +git-svnmerge + Use git to make up for svn's lack of merge tracking. This tool lets + you merge between Subversion branches in your git-svn repository. +
contrib/svn/git-svnmerge new file +139 -0
139 lines not on this page · raw message
Signature · 1 line
1.5.2.rc0.35.gf41c8
Quoted 16 lines · Steven Grimm
contrib/svn/git-svnmerge new file +10
-0@@ -0,0 +1,139 @@ +#!/bin/sh +# +# Handles svn merges using git-svn. +# +# Usage: git-svnmerge [-m commit-message] other-branch-name +# +# This script pulls the changes from another svn branch into the current branch +# and checks the result into svn. Then it updates the grafts file so that the +# merge is known to git. That means that subsequent runs of the script will +# automatically know which changes to apply.
contrib/svn/git-svnmerge line 20
Quoted 10 lines
contrib/svn/git-svnmerge new file +10
-0+# +# The user is assumed to have already updated the repository using git-svn. +# +# Exit codes: +# 0 Merge successfully committed to svn. +# 1 Merge has conflicts that need to be resolved. +# 2 Encountered an error. +# +# Author: Steven Grimm <[email protected]> +#
This is contrib stuff, so I'll limit the comments mostly to style.
contrib/svn/git-svnmerge line 23
Quoted 3 lines
contrib/svn/git-svnmerge new file +3
-0+ +die() { + echo "$@" 1>&2
Many of our scripts do redirection at the beginning, like:
echo >&2 ...
Also omitting 1 when redirecting to stderr is an idiom.
contrib/svn/git-svnmerge line 25
Quoted 2 lines
contrib/svn/git-svnmerge new file +2
-0+ exit 2 +}
Does exit value 2 have any significance, or just any non-zero value would do?
contrib/svn/git-svnmerge line 29
Quoted 4 lines
contrib/svn/git-svnmerge new file +4
-0+ +store_status() { + echo $Status $OldHead $RevToMerge $CommitMessage > "$StatusFile" +}
We do not seem to do CamelCase on shell variable names. This is minor, as it is mostly taste.
contrib/svn/git-svnmerge line 31
Quoted 2 lines
contrib/svn/git-svnmerge new file +2
-0+ +while test $# -gt 1; do
while <<command>>
do
without semicolon.
contrib/svn/git-svnmerge line 33
Quoted 2 lines
contrib/svn/git-svnmerge new file +2
-0+ case "$1" in + -m)
Align case arms with "case/esac", just like you would do "switch" and "case" in C.
contrib/svn/git-svnmerge line 40
Quoted 3 lines
contrib/svn/git-svnmerge new file +3
-0+ *) + die "Unknown argument $1" + esac
... and do not omit ';;' just like you do not omit "break" in the last case arm in switch in C.
contrib/svn/git-svnmerge line 43
Quoted 3 lines
contrib/svn/git-svnmerge new file +3
-0+done + +if test $# -lt 1; then
if <<command>>
thenwithout semicolon.
contrib/svn/git-svnmerge line 47
Quoted 4 lines
contrib/svn/git-svnmerge new file +4
-0+ die "Usage: $0 [-m commit-message] other-branch-name" +fi + +GitDir="`git rev-parse --git-dir`"
GIT_DIR?
Quoted 10 lines
contrib/svn/git-svnmerge new file +10
-0+if test -z "$GitDir"; then + die "Not a git repository." +fi +StatusFile="$GitDir/svnmerge-status" + +# We might be resuming a previous run, so get the old state if any. +Status="start" +if test -f "$StatusFile"; then + read Status OldHead RevToMerge CommitMessage < "$StatusFile" +fi
contrib/svn/git-svnmerge line 67
Quoted 10 lines
contrib/svn/git-svnmerge new file +10
-0+ +if test "$Status" = "start"; then + # Make sure there aren't uncommitted (to svn) changes here. + if test -n "`git svn dcommit -n`"; then + die "Can't merge to dirty branch" + fi + + # Record the revisions we're merging. We'll use them in the + # grafts file later. + OldHead="`git rev-list --max-count=1 HEAD`"
OldHead=git rev-parse --verify HEAD
contrib/svn/git-svnmerge line 71
Quoted 4 lines
contrib/svn/git-svnmerge new file +4
-0+ RevToMerge="`git rev-list --max-count=1 $1`" + if test -z "$RevToMerge"; then + die "Can't merge nonexistent branch." + fi
Wouldn't it possible that $1 might have $IFS in it here, would it?
RevToMerge=`git rev-parse --verify "$1"`
If you are expecting "$1" to be only an existing branch, not just an arbitrary rev, you might want to be more strict by saying:
RevToMerge=`git show-ref -s refs/heads/"$1"` ||
die "Can't merge nonexistent branch."Quoted 22 lines
contrib/svn/git-svnmerge new file +22
-0+ + # Do the actual merge. + git merge --squash "$RevToMerge" || \ + MergeFailed=1 + + # Did we actually merge anything? + if git status > /dev/null; then + : + else + echo "No changes to merge. Have you fetched from svn?" + exit 0 + fi + + Status=merged + store_status + + # If there are conflicts, bail out. + if test -n "$MergeFailed`git ls-files -u`"; then + echo "Please resolve conflicts and run again to continue." + exit 1 + fi +fi
contrib/svn/git-svnmerge line 103
Quoted 10 lines
contrib/svn/git-svnmerge new file +10
-0+ +if test "$Status" = "merged"; then + if test -n "`git ls-files -u`"; then + echo "There are still conflicts; can't continue." + exit 1 + fi + + SvnRevision="`git log --pretty=format:%b -n 1 $RevToMerge | \ + egrep '^git-svn-id:' | \ + sed 's:.*/\([^/]*\)@\([0-9]*\) [0-9a-f].*:\1 revision \2:'`"
Piping grep to sed often makes your script look amateurish. How about...
script='s|^git-svn-id:.*/\([^/]*\)@\([0-9]*\) [0-9a-f].*|\1 revision \2|p'
git show -s --pretty=format:%b $RevToMerge |
sed -n -e "$script"Quoted 14 lines
contrib/svn/git-svnmerge new file +14
-0+ echo "$SvnRevision merged. Committing." + + if test -z "$CommitMessage"; then + CommitMessage="Merge from $SvnRevision" + fi + + git commit -m "$CommitMessage" || \ + die "Commit failed" + + Status=committed + store_status +fi + +# Since git-svn dcommit can fail for reasons having nothing to do with the
contrib/svn/git-svnmerge line 127
Quoted 10 lines
contrib/svn/git-svnmerge new file +10
-0+# local repository (e.g. the svn server is down), we allow the user to retry +# the dcommit by running this command again. +if test "$Status" = "committed"; then + git svn dcommit || \ + die "Can't commit to Subversion" + + # dcommit will update our current HEAD to point to the newly committed + # svn revision. Update grafts file to tell git that it's a merge. + NewRevision="`git rev-list --max-count=1 HEAD`" + echo "$NewRevision $OldHead $RevToMerge" >> $GitDir/info/grafts
This graft look very yucky, and would not scale.
Presumably, the tree in HEAD commit before dcommit match the one in NewRevision, right? I wonder if there is a way to convince "git-svn" after dcommit that the commit you made yourself above is where it should stay.
Quoted 14 lines
contrib/svn/git-svnmerge new file +12
-0+ + rm "$StatusFile" + echo "Successfully merged $SvnRevision." + + Status="" +fi + +if test -n "$Status"; then + die "Unknown status $Status in status file!" +fi + +exit 0Signature · 1 line
1.5.2.rc0.35.gf41c8
Quoted 10 lines · Junio C Hamano
contrib/svn/git-svnmerge line 127
Quoted 8 lines
contrib/svn/git-svnmerge new file +8
-0+if test "$Status" = "committed"; then + git svn dcommit || \ + die "Can't commit to Subversion" + + # dcommit will update our current HEAD to point to the newly committed + # svn revision. Update grafts file to tell git that it's a merge. + NewRevision="`git rev-list --max-count=1 HEAD`" + echo "$NewRevision $OldHead $RevToMerge" >> $GitDir/info/graftsThis graft look very yucky, and would not scale.
Style issues aside...
I do not know what dcommit is supposed to do, but I wonder if that graft part can be folded into it. Even if git-svn cannot deal with merges that happen on the svn side, assuming if your grafting solution works, it indicates that git-svn itself does not have trouble seeing a merge commit on git side, as its invocation of "git-rev-list --parents" would show that the commit you grafted an extra parent to has two parents and git-svn needs to be able to deal with it.
The command sequence in your contrib script is:
- make a commit on top of git-svn managed head by hand, recording what should have happened if git-svn knew how to merge two svn branches;
- make svn dcommit to record it to svn side, and let it make an extra commit to mark the synchronization between git-svn managed head and svn repository;
- pretend that the synchronization commit svn dcommit made has an extra commit, which is what you created by hand;
So perhaps you can add an option "git-svn dcommit --merge $OldHead $RevToMerge" that does whatever it usually does and record the resulting commit as a merge between these two commits?