{"thread":{"id":"7862","subject":"[PATCH] A utility to perform merges between Subversion branches using git","startedAt":"2007-04-27T00:55:28Z","lastAt":"2007-04-27T22:38:40Z","messageCount":3,"participants":["Steven Grimm","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"40545","messageId":"20070427005528.GA12688@midwinter.com","threadId":"7862","inReplyTo":null,"subject":"[PATCH] A utility to perform merges between Subversion branches using git","fromName":"Steven Grimm","fromEmail":"koreth@midwinter.com","sentAt":"2007-04-27T00:55:28Z","receivedAt":"2007-04-27T00:55:28Z","isPatch":true,"sender":{"key":"koreth@midwinter.com","avatar":"https://gravatar.com/avatar/71b4d2e8b62f168bdc9e9205341159e3567003b4f9e2127c617c5fa0a1f5bad2?d=mp&s=160"},"body":"Signed-off-by: Steven Grimm <koreth@midwinter.com>\n---\n contrib/svn/README       |    8 +++\n contrib/svn/git-svnmerge |  139 ++++++++++++++++++++++++++++++++++++++++++++++\n 2 files changed, 147 insertions(+), 0 deletions(-)\n create mode 100644 contrib/svn/README\n create mode 100755 contrib/svn/git-svnmerge\n\ndiff --git a/contrib/svn/README b/contrib/svn/README\nnew file mode 100644\nindex 0000000..f3627ab\n--- /dev/null\n+++ b/contrib/svn/README\n@@ -0,0 +1,8 @@\n+This directory contains tools useful for a mixed git/Subversion environment.\n+They will likely all depend on git-svnimport or git-svn, which are the main\n+Subversion bridges in git.\n+\n+git-svnmerge\n+\tUse git to make up for svn's lack of merge tracking. This tool lets\n+\tyou merge between Subversion branches in your git-svn repository.\n+\ndiff --git a/contrib/svn/git-svnmerge b/contrib/svn/git-svnmerge\nnew file mode 100755\nindex 0000000..e7ed45d\n--- /dev/null\n+++ b/contrib/svn/git-svnmerge\n@@ -0,0 +1,139 @@\n+#!/bin/sh\n+#\n+# Handles svn merges using git-svn.\n+#\n+# Usage: git-svnmerge [-m commit-message] other-branch-name\n+#\n+# This script pulls the changes from another svn branch into the current branch\n+# and checks the result into svn. Then it updates the grafts file so that the\n+# merge is known to git. That means that subsequent runs of the script will\n+# automatically know which changes to apply.\n+#\n+# The user is assumed to have already updated the repository using git-svn.\n+#\n+# Exit codes:\n+#   0 Merge successfully committed to svn.\n+#   1 Merge has conflicts that need to be resolved.\n+#   2 Encountered an error.\n+#\n+# Author: Steven Grimm <koreth@midwinter.com>\n+#\n+\n+die() {\n+\techo \"$@\" 1>&2\n+\texit 2\n+}\n+\n+store_status() {\n+\techo $Status $OldHead $RevToMerge $CommitMessage > \"$StatusFile\"\n+}\n+\n+while test $# -gt 1; do\n+\tcase \"$1\" in\n+\t\t-m)\n+\t\t\tshift\n+\t\t\tCommitMessage=\"$1\"\n+\t\t\tshift\n+\t\t\t;;\n+\t\t*)\n+\t\t\tdie \"Unknown argument $1\"\n+\tesac\n+done\n+\n+if test $# -lt 1; then\n+\tdie \"Usage: $0 [-m commit-message] other-branch-name\"\n+fi\n+\n+GitDir=\"`git rev-parse --git-dir`\"\n+if test -z \"$GitDir\"; then\n+\tdie \"Not a git repository.\"\n+fi\n+StatusFile=\"$GitDir/svnmerge-status\"\n+\n+# We might be resuming a previous run, so get the old state if any.\n+Status=\"start\"\n+if test -f \"$StatusFile\"; then\n+\tread Status OldHead RevToMerge CommitMessage < \"$StatusFile\"\n+fi\n+\n+if test \"$Status\" = \"start\"; then\n+\t# Make sure there aren't uncommitted (to svn) changes here.\n+\tif test -n \"`git svn dcommit -n`\"; then\n+\t\tdie \"Can't merge to dirty branch\"\n+\tfi\n+\n+\t# Record the revisions we're merging. We'll use them in the\n+\t# grafts file later.\n+\tOldHead=\"`git rev-list --max-count=1 HEAD`\"\n+\tRevToMerge=\"`git rev-list --max-count=1 $1`\"\n+\tif test -z \"$RevToMerge\"; then\n+\t\tdie \"Can't merge nonexistent branch.\"\n+\tfi\n+\n+\t# Do the actual merge.\n+\tgit merge --squash \"$RevToMerge\" || \\\n+\t\tMergeFailed=1\n+\n+\t# Did we actually merge anything?\n+\tif git status > /dev/null; then\n+\t\t:\n+\telse\n+\t\techo \"No changes to merge. Have you fetched from svn?\"\n+\t\texit 0\n+\tfi\n+\n+\tStatus=merged\n+\tstore_status\n+\n+\t# If there are conflicts, bail out.\n+\tif test -n \"$MergeFailed`git ls-files -u`\"; then\n+\t\techo \"Please resolve conflicts and run again to continue.\"\n+\t\texit 1\n+\tfi\n+fi\n+\n+if test \"$Status\" = \"merged\"; then\n+\tif test -n \"`git ls-files -u`\"; then\n+\t\techo \"There are still conflicts; can't continue.\"\n+\t\texit 1\n+\tfi\n+\n+\tSvnRevision=\"`git log --pretty=format:%b -n 1 $RevToMerge | \\\n+\t\t\tegrep '^git-svn-id:' | \\\n+\t\t\tsed 's:.*/\\([^/]*\\)@\\([0-9]*\\) [0-9a-f].*:\\1 revision \\2:'`\"\n+\techo \"$SvnRevision merged. Committing.\"\n+\n+\tif test -z \"$CommitMessage\"; then\n+\t\tCommitMessage=\"Merge from $SvnRevision\"\n+\tfi\n+\n+\tgit commit -m \"$CommitMessage\" || \\\n+\t\tdie \"Commit failed\"\n+\n+\tStatus=committed\n+\tstore_status\n+fi\n+\n+# Since git-svn dcommit can fail for reasons having nothing to do with the\n+# local repository (e.g. the svn server is down), we allow the user to retry\n+# the dcommit by running this command again.\n+if test \"$Status\" = \"committed\"; then\n+\tgit svn dcommit || \\\n+\t\tdie \"Can't commit to Subversion\"\n+\n+\t# dcommit will update our current HEAD to point to the newly committed\n+\t# svn revision. Update grafts file to tell git that it's a merge.\n+\tNewRevision=\"`git rev-list --max-count=1 HEAD`\"\n+\techo \"$NewRevision $OldHead $RevToMerge\" >> $GitDir/info/grafts\n+\n+\trm \"$StatusFile\"\n+\techo \"Successfully merged $SvnRevision.\"\n+\n+\tStatus=\"\"\n+fi\n+\n+if test -n \"$Status\"; then\n+\tdie \"Unknown status $Status in status file!\"\n+fi\n+\n+exit 0\n-- \n1.5.2.rc0.35.gf41c8\n"},{"id":"40560","messageId":"7vy7kephg8.fsf@assigned-by-dhcp.cox.net","threadId":"7862","inReplyTo":"20070427005528.GA12688@midwinter.com","subject":"Re: [PATCH] A utility to perform merges between Subversion branches using git","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-04-27T04:30:31Z","receivedAt":"2007-04-27T04:30:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Steven Grimm <koreth@midwinter.com> writes:\n\n> diff --git a/contrib/svn/git-svnmerge b/contrib/svn/git-svnmerge\n> new file mode 100755\n> index 0000000..e7ed45d\n> --- /dev/null\n> +++ b/contrib/svn/git-svnmerge\n> @@ -0,0 +1,139 @@\n> +#!/bin/sh\n> +#\n> +# Handles svn merges using git-svn.\n> +#\n> +# Usage: git-svnmerge [-m commit-message] other-branch-name\n> +#\n> +# This script pulls the changes from another svn branch into the current branch\n> +# and checks the result into svn. Then it updates the grafts file so that the\n> +# merge is known to git. That means that subsequent runs of the script will\n> +# automatically know which changes to apply.\n> +#\n> +# The user is assumed to have already updated the repository using git-svn.\n> +#\n> +# Exit codes:\n> +#   0 Merge successfully committed to svn.\n> +#   1 Merge has conflicts that need to be resolved.\n> +#   2 Encountered an error.\n> +#\n> +# Author: Steven Grimm <koreth@midwinter.com>\n> +#\n\nThis is contrib stuff, so I'll limit the comments mostly to style.\n\n> +\n> +die() {\n> +\techo \"$@\" 1>&2\n\nMany of our scripts do redirection at the beginning, like:\n\n\techo >&2 ...\n\nAlso omitting 1 when redirecting to stderr is an idiom.\n\n> +\texit 2\n> +}\n\nDoes exit value 2 have any significance, or just any non-zero\nvalue would do?\n\n> +\n> +store_status() {\n> +\techo $Status $OldHead $RevToMerge $CommitMessage > \"$StatusFile\"\n> +}\n\nWe do not seem to do CamelCase on shell variable names.  This is\nminor, as it is mostly taste.\n\n> +\n> +while test $# -gt 1; do\n\n        while <<command>>\n        do\n\nwithout semicolon.\n\n> +\tcase \"$1\" in\n> +\t\t-m)\n\nAlign case arms with \"case/esac\", just like you would do\n\"switch\" and \"case\" in C.\n\n> +\t\t*)\n> +\t\t\tdie \"Unknown argument $1\"\n> +\tesac\n\n... and do not omit ';;' just like you do not omit \"break\" in\nthe last case arm in switch in C.\n\n> +done\n> +\n> +if test $# -lt 1; then\n\n\tif <<command>>\n        then\n\nwithout semicolon.\n\n> +\tdie \"Usage: $0 [-m commit-message] other-branch-name\"\n> +fi\n> +\n> +GitDir=\"`git rev-parse --git-dir`\"\n\nGIT_DIR?\n\n> +if test -z \"$GitDir\"; then\n> +\tdie \"Not a git repository.\"\n> +fi\n> +StatusFile=\"$GitDir/svnmerge-status\"\n> +\n> +# We might be resuming a previous run, so get the old state if any.\n> +Status=\"start\"\n> +if test -f \"$StatusFile\"; then\n> +\tread Status OldHead RevToMerge CommitMessage < \"$StatusFile\"\n> +fi\n> +\n> +if test \"$Status\" = \"start\"; then\n> +\t# Make sure there aren't uncommitted (to svn) changes here.\n> +\tif test -n \"`git svn dcommit -n`\"; then\n> +\t\tdie \"Can't merge to dirty branch\"\n> +\tfi\n> +\n> +\t# Record the revisions we're merging. We'll use them in the\n> +\t# grafts file later.\n> +\tOldHead=\"`git rev-list --max-count=1 HEAD`\"\n\nOldHead=`git rev-parse --verify HEAD`\n\n> +\tRevToMerge=\"`git rev-list --max-count=1 $1`\"\n> +\tif test -z \"$RevToMerge\"; then\n> +\t\tdie \"Can't merge nonexistent branch.\"\n> +\tfi\n\nWouldn't it possible that $1 might have $IFS in it here, would\nit?\n\n\tRevToMerge=`git rev-parse --verify \"$1\"`\n\nIf you are expecting \"$1\" to be only an existing branch, not\njust an arbitrary rev, you might want to be more strict by\nsaying:\n\n\tRevToMerge=`git show-ref -s refs/heads/\"$1\"` ||\n        die \"Can't merge nonexistent branch.\"\n\n> +\n> +\t# Do the actual merge.\n> +\tgit merge --squash \"$RevToMerge\" || \\\n> +\t\tMergeFailed=1\n> +\n> +\t# Did we actually merge anything?\n> +\tif git status > /dev/null; then\n> +\t\t:\n> +\telse\n> +\t\techo \"No changes to merge. Have you fetched from svn?\"\n> +\t\texit 0\n> +\tfi\n> +\n> +\tStatus=merged\n> +\tstore_status\n> +\n> +\t# If there are conflicts, bail out.\n> +\tif test -n \"$MergeFailed`git ls-files -u`\"; then\n> +\t\techo \"Please resolve conflicts and run again to continue.\"\n> +\t\texit 1\n> +\tfi\n> +fi\n> +\n> +if test \"$Status\" = \"merged\"; then\n> +\tif test -n \"`git ls-files -u`\"; then\n> +\t\techo \"There are still conflicts; can't continue.\"\n> +\t\texit 1\n> +\tfi\n> +\n> +\tSvnRevision=\"`git log --pretty=format:%b -n 1 $RevToMerge | \\\n> +\t\t\tegrep '^git-svn-id:' | \\\n> +\t\t\tsed 's:.*/\\([^/]*\\)@\\([0-9]*\\) [0-9a-f].*:\\1 revision \\2:'`\"\n\nPiping grep to sed often makes your script look amateurish.\nHow about...\n\n        script='s|^git-svn-id:.*/\\([^/]*\\)@\\([0-9]*\\) [0-9a-f].*|\\1 revision \\2|p'\n        git show -s --pretty=format:%b $RevToMerge |\n        sed -n -e \"$script\"\n\n> +\techo \"$SvnRevision merged. Committing.\"\n> +\n> +\tif test -z \"$CommitMessage\"; then\n> +\t\tCommitMessage=\"Merge from $SvnRevision\"\n> +\tfi\n> +\n> +\tgit commit -m \"$CommitMessage\" || \\\n> +\t\tdie \"Commit failed\"\n> +\n> +\tStatus=committed\n> +\tstore_status\n> +fi\n> +\n> +# Since git-svn dcommit can fail for reasons having nothing to do with the\n> +# local repository (e.g. the svn server is down), we allow the user to retry\n> +# the dcommit by running this command again.\n> +if test \"$Status\" = \"committed\"; then\n> +\tgit svn dcommit || \\\n> +\t\tdie \"Can't commit to Subversion\"\n> +\n> +\t# dcommit will update our current HEAD to point to the newly committed\n> +\t# svn revision. Update grafts file to tell git that it's a merge.\n> +\tNewRevision=\"`git rev-list --max-count=1 HEAD`\"\n> +\techo \"$NewRevision $OldHead $RevToMerge\" >> $GitDir/info/grafts\n\nThis graft look very yucky, and would not scale.\n\nPresumably, the tree in HEAD commit before dcommit match the one\nin NewRevision, right?  I wonder if there is a way to convince\n\"git-svn\" after dcommit that the commit you made yourself above\nis where it should stay.\n\n> +\n> +\trm \"$StatusFile\"\n> +\techo \"Successfully merged $SvnRevision.\"\n> +\n> +\tStatus=\"\"\n> +fi\n> +\n> +if test -n \"$Status\"; then\n> +\tdie \"Unknown status $Status in status file!\"\n> +fi\n> +\n> +exit 0\n> -- \n> 1.5.2.rc0.35.gf41c8\n"},{"id":"40635","messageId":"7v3b2lmoi7.fsf@assigned-by-dhcp.cox.net","threadId":"7862","inReplyTo":"7vy7kephg8.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] A utility to perform merges between Subversion branches using git","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2007-04-27T22:38:40Z","receivedAt":"2007-04-27T22:38:40Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <junkio@cox.net> writes:\n\n>> +if test \"$Status\" = \"committed\"; then\n>> +\tgit svn dcommit || \\\n>> +\t\tdie \"Can't commit to Subversion\"\n>> +\n>> +\t# dcommit will update our current HEAD to point to the newly committed\n>> +\t# svn revision. Update grafts file to tell git that it's a merge.\n>> +\tNewRevision=\"`git rev-list --max-count=1 HEAD`\"\n>> +\techo \"$NewRevision $OldHead $RevToMerge\" >> $GitDir/info/grafts\n>\n> This graft look very yucky, and would not scale.\n\nStyle issues aside...\n\nI do not know what dcommit is supposed to do, but I wonder if\nthat graft part can be folded into it.  Even if git-svn cannot\ndeal with merges that happen on the svn side, assuming if your\ngrafting solution works, it indicates that git-svn itself does\nnot have trouble seeing a merge commit on git side, as its\ninvocation of \"git-rev-list --parents\" would show that the\ncommit you grafted an extra parent to has two parents and\ngit-svn needs to be able to deal with it.\n\nThe command sequence in your contrib script is:\n\n - make a commit on top of git-svn managed head by hand,\n   recording what should have happened if git-svn knew how to\n   merge two svn branches;\n\n - make svn dcommit to record it to svn side, and let it make an\n   extra commit to mark the synchronization between git-svn\n   managed head and svn repository;\n\n - pretend that the synchronization commit svn dcommit made has\n   an extra commit, which is what you created by hand;\n\nSo perhaps you can add an option \"git-svn dcommit --merge\n$OldHead $RevToMerge\" that does whatever it usually does and\nrecord the resulting commit as a merge between these two\ncommits?\n"}]}