* [PATCH] A utility to perform merges between Subversion branches using git
@ 2007-04-27 0:55 Steven Grimm
2007-04-27 4:30 ` Junio C Hamano
0 siblings, 1 reply; 3+ messages in thread
From: Steven Grimm @ 2007-04-27 0:55 UTC (permalink / raw)
To: git
Signed-off-by: Steven Grimm <koreth@midwinter.com>
---
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
diff --git a/contrib/svn/README b/contrib/svn/README
new file mode 100644
index 0000000..f3627ab
--- /dev/null
+++ b/contrib/svn/README
@@ -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.
+
diff --git a/contrib/svn/git-svnmerge b/contrib/svn/git-svnmerge
new file mode 100755
index 0000000..e7ed45d
--- /dev/null
+++ b/contrib/svn/git-svnmerge
@@ -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.
+#
+# 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 <koreth@midwinter.com>
+#
+
+die() {
+ echo "$@" 1>&2
+ exit 2
+}
+
+store_status() {
+ echo $Status $OldHead $RevToMerge $CommitMessage > "$StatusFile"
+}
+
+while test $# -gt 1; do
+ case "$1" in
+ -m)
+ shift
+ CommitMessage="$1"
+ shift
+ ;;
+ *)
+ die "Unknown argument $1"
+ esac
+done
+
+if test $# -lt 1; then
+ die "Usage: $0 [-m commit-message] other-branch-name"
+fi
+
+GitDir="`git rev-parse --git-dir`"
+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
+
+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`"
+ RevToMerge="`git rev-list --max-count=1 $1`"
+ if test -z "$RevToMerge"; then
+ die "Can't merge nonexistent branch."
+ fi
+
+ # 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
+
+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:'`"
+ 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
+# 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
+
+ rm "$StatusFile"
+ echo "Successfully merged $SvnRevision."
+
+ Status=""
+fi
+
+if test -n "$Status"; then
+ die "Unknown status $Status in status file!"
+fi
+
+exit 0
--
1.5.2.rc0.35.gf41c8
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH] A utility to perform merges between Subversion branches using git
2007-04-27 0:55 [PATCH] A utility to perform merges between Subversion branches using git Steven Grimm
@ 2007-04-27 4:30 ` Junio C Hamano
2007-04-27 22:38 ` Junio C Hamano
0 siblings, 1 reply; 3+ messages in thread
From: Junio C Hamano @ 2007-04-27 4:30 UTC (permalink / raw)
To: Steven Grimm; +Cc: git
Steven Grimm <koreth@midwinter.com> writes:
> diff --git a/contrib/svn/git-svnmerge b/contrib/svn/git-svnmerge
> new file mode 100755
> index 0000000..e7ed45d
> --- /dev/null
> +++ b/contrib/svn/git-svnmerge
> @@ -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.
> +#
> +# 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 <koreth@midwinter.com>
> +#
This is contrib stuff, so I'll limit the comments mostly to style.
> +
> +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.
> + exit 2
> +}
Does exit value 2 have any significance, or just any non-zero
value would do?
> +
> +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.
> +
> +while test $# -gt 1; do
while <<command>>
do
without semicolon.
> + case "$1" in
> + -m)
Align case arms with "case/esac", just like you would do
"switch" and "case" in C.
> + *)
> + 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.
> +done
> +
> +if test $# -lt 1; then
if <<command>>
then
without semicolon.
> + die "Usage: $0 [-m commit-message] other-branch-name"
> +fi
> +
> +GitDir="`git rev-parse --git-dir`"
GIT_DIR?
> +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
> +
> +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`
> + 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."
> +
> + # 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
> +
> +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"
> + 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
> +# 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.
> +
> + rm "$StatusFile"
> + echo "Successfully merged $SvnRevision."
> +
> + Status=""
> +fi
> +
> +if test -n "$Status"; then
> + die "Unknown status $Status in status file!"
> +fi
> +
> +exit 0
> --
> 1.5.2.rc0.35.gf41c8
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] A utility to perform merges between Subversion branches using git
2007-04-27 4:30 ` Junio C Hamano
@ 2007-04-27 22:38 ` Junio C Hamano
0 siblings, 0 replies; 3+ messages in thread
From: Junio C Hamano @ 2007-04-27 22:38 UTC (permalink / raw)
To: Steven Grimm; +Cc: git
Junio C Hamano <junkio@cox.net> writes:
>> +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.
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?
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2007-04-27 22:39 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-04-27 0:55 [PATCH] A utility to perform merges between Subversion branches using git Steven Grimm
2007-04-27 4:30 ` Junio C Hamano
2007-04-27 22:38 ` Junio C Hamano
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.