{"thread":{"id":"15780","subject":"[PATCH v3] Add git-svn branch to allow branch creation in SVN repositories","startedAt":"2008-10-04T14:24:44Z","lastAt":"2008-10-05T02:35:17Z","messageCount":2,"participants":["Deskin Miller","Eric Wong"],"isPatch":true,"patchVersion":3,"patchTotal":null},"messages":[{"id":"92294","messageId":"20081004142444.GC3052@riemann.deskinm.fdns.net","threadId":"15780","inReplyTo":null,"subject":"[PATCH v3] Add git-svn branch to allow branch creation in SVN repositories","fromName":"Deskin Miller","fromEmail":"deskinm@umich.edu","sentAt":"2008-10-04T14:24:44Z","receivedAt":"2008-10-04T14:24:44Z","isPatch":true,"sender":{"key":"deskinm@umich.edu","avatar":"https://gravatar.com/avatar/d340a0e612cdf0a79535c71863c0b4c535e9aba63b42032226ae903e638b64f9?d=mp&s=160"},"body":">From 7a8a811bbb229825da8c90e71c57a60634a9280b Mon Sep 17 00:00:00 2001\nFrom: Florian Ragwitz <rafl@debian.org>\nDate: Tue, 2 Sep 2008 14:20:39 +0000\n\nSigned-off-by: Florian Ragwitz <rafl@debian.org>\nSigned-off-by: Deskin Miller <deskinm@umich.edu>\n---\nEric Wong <normalperson <at> yhbt.net> writes:\n> Florian Ragwitz <rafl <at> debian.org> wrote:\n> > Signed-off-by: Florian Ragwitz <rafl <at> debian.org>\n> \n> The patch looks good, but can you add a test for this functionality?\n\nHere's a new patch with tests for this feature.  There are also some minor\nchanges to Florian's patch: branch now parses -t/--tag, and some of the\nmessages in cmd_branch change depending on $_tag also.\n\nFlorian, I apologise if I'm stepping on your toes here; this is a feature I\nwould've written if I was better at Perl, so I want to see it included, and I'm\nglad for your work on it.\n\n Documentation/git-svn.txt     |   24 ++++++++++++++++-\n git-svn.perl                  |   46 +++++++++++++++++++++++++++++++-\n t/t9128-git-svn-cmd-branch.sh |   59 +++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 127 insertions(+), 2 deletions(-)\n create mode 100755 t/t9128-git-svn-cmd-branch.sh\n\ndiff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt\nindex 1e644ca..0fe4955 100644\n--- a/Documentation/git-svn.txt\n+++ b/Documentation/git-svn.txt\n@@ -149,6 +149,22 @@ and have no uncommitted changes.\n \tis very strongly discouraged.\n --\n \n+'branch'::\n+\tCreate a branch in the SVN repository.\n+\n+-m;;\n+--message;;\n+\tAllows to specify the commit message.\n+\n+-t;;\n+--tag;;\n+\tCreate a tag by using the tags_subdir instead of the branches_subdir\n+\tspecified during git svn init.\n+\n+'tag'::\n+\tCreate a tag in the SVN repository. This is a shorthand for\n+\t'branch -t'.\n+\n 'log'::\n \tThis should make it easy to look up svn log messages when svn\n \tusers refer to -r/--revision numbers.\n@@ -372,7 +388,8 @@ Passed directly to 'git-rebase' when using 'dcommit' if a\n -n::\n --dry-run::\n \n-This can be used with the 'dcommit' and 'rebase' commands.\n+This can be used with the 'dcommit', 'rebase', 'branch' and 'tag'\n+commands.\n \n For 'dcommit', print out the series of git arguments that would show\n which diffs would be committed to SVN.\n@@ -381,6 +398,9 @@ For 'rebase', display the local branch associated with the upstream svn\n repository associated with the current branch and the URL of svn\n repository that will be fetched from.\n \n+For 'branch' and 'tag', display the urls that will be used for copying when\n+creating the branch or tag.\n+\n --\n \n ADVANCED OPTIONS\n@@ -498,6 +518,8 @@ Tracking and contributing to an entire Subversion-managed project\n \tgit svn clone http://svn.foo.org/project -T trunk -b branches -t tags\n # View all branches and tags you have cloned:\n \tgit branch -r\n+# Create a new branch in SVN\n+    git svn branch waldo\n # Reset your master to trunk (or any other branch, replacing 'trunk'\n # with the appropriate name):\n \tgit reset --hard remotes/trunk\ndiff --git a/git-svn.perl b/git-svn.perl\nindex 80a5728..e7b9254 100755\n--- a/git-svn.perl\n+++ b/git-svn.perl\n@@ -66,7 +66,7 @@ my ($_stdin, $_help, $_edit,\n \t$_version, $_fetch_all, $_no_rebase,\n \t$_merge, $_strategy, $_dry_run, $_local,\n \t$_prefix, $_no_checkout, $_url, $_verbose,\n-\t$_git_format, $_commit_url);\n+\t$_git_format, $_commit_url, $_tag);\n $Git::SVN::_follow_parent = 1;\n my %remote_opts = ( 'username=s' => \\$Git::SVN::Prompt::_username,\n                     'config-dir=s' => \\$Git::SVN::Ra::config_dir,\n@@ -131,6 +131,15 @@ my %cmd = (\n \t\t\t  'revision|r=i' => \\$_revision,\n \t\t\t  'no-rebase' => \\$_no_rebase,\n \t\t\t%cmt_opts, %fc_opts } ],\n+\tbranch => [ \\&cmd_branch,\n+\t            'Create a branch in the SVN repository',\n+\t            { 'message|m=s' => \\$_message,\n+\t              'dry-run|n' => \\$_dry_run,\n+\t\t      'tag|t' => \\$_tag } ],\n+\ttag => [ sub { $_tag = 1; cmd_branch(@_) },\n+\t         'Create a tag in the SVN repository',\n+\t         { 'message|m=s' => \\$_message,\n+\t           'dry-run|n' => \\$_dry_run } ],\n \t'set-tree' => [ \\&cmd_set_tree,\n \t                \"Set an SVN repository to a git tree-ish\",\n \t\t\t{ 'stdin|' => \\$_stdin, %cmt_opts, %fc_opts, } ],\n@@ -537,6 +546,41 @@ sub cmd_dcommit {\n \tunlink $gs->{index};\n }\n \n+sub cmd_branch {\n+\tmy ($branch_name, $head) = @_;\n+\n+\tdie ($_tag ? \"tag\" : \"branch\") . \" name required\\n\"\n+\t\tunless $branch_name;\n+\t$head ||= 'HEAD';\n+\n+\tmy ($src, $rev, undef, $gs) = working_head_info($head);\n+\n+\tmy $remote = Git::SVN::read_all_remotes()->{svn};\n+\tmy $glob = $remote->{ $_tag ? 'tags' : 'branches' };\n+\tmy ($lft, $rgt) = @{ $glob->{path} }{qw/left right/};\n+\tmy $dst = join '/', $remote->{url}, $lft, $branch_name, ($rgt || ());\n+\n+\tmy $ctx = SVN::Client->new(\n+\t\tauth    => Git::SVN::Ra::_auth_providers(),\n+\t\tlog_msg => sub {\n+\t\t\t${ $_[0] } = defined $_message\n+\t\t\t\t? $_message\n+\t\t\t\t: 'Create ' . ($_tag ? 'tag ' : 'branch ' )\n+\t\t\t\t. $branch_name;\n+\t\t},\n+\t);\n+\n+\teval {\n+\t\t$ctx->ls($dst, 'HEAD', 0);\n+\t} and die \"branch ${branch_name} already exists\\n\";\n+\n+\tprint \"Copying ${src} at r${rev} to ${dst}...\\n\";\n+\t$ctx->copy($src, $rev, $dst)\n+\t\tunless $_dry_run;\n+\n+\t$gs->fetch_all;\n+}\n+\n sub cmd_find_rev {\n \tmy $revision_or_hash = shift or die \"SVN or git revision required \",\n \t                                    \"as a command-line argument\\n\";\ndiff --git a/t/t9128-git-svn-cmd-branch.sh b/t/t9128-git-svn-cmd-branch.sh\nnew file mode 100755\nindex 0000000..47c4d4d\n--- /dev/null\n+++ b/t/t9128-git-svn-cmd-branch.sh\n@@ -0,0 +1,59 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2008 Deskin Miller\n+#\n+\n+test_description='git svn partial-rebuild tests'\n+. ./lib-git-svn.sh\n+\n+test_expect_success 'initialize svnrepo' '\n+\tmkdir import &&\n+\t(\n+\t\tcd import &&\n+\t\tmkdir trunk branches tags &&\n+\t\tcd trunk &&\n+\t\techo foo > foo &&\n+\t\tcd .. &&\n+\t\tsvn import -m \"import for git-svn\" . \"$svnrepo\" >/dev/null &&\n+\t\tcd .. &&\n+\t\trm -rf import &&\n+\t\tsvn co \"$svnrepo\"/trunk trunk &&\n+\t\tcd trunk &&\n+\t\techo bar >> foo &&\n+\t\tsvn ci -m \"updated trunk\" &&\n+\t\tcd .. &&\n+\t\trm -rf trunk\n+\t)\n+'\n+\n+test_expect_success 'import into git' '\n+\tgit svn init --stdlayout \"$svnrepo\" &&\n+\tgit svn fetch &&\n+\tgit checkout remotes/trunk\n+'\n+\n+test_expect_success 'git svn branch tests' '\n+\tgit svn branch a &&\n+\tbase=$(git rev-parse HEAD:) &&\n+\ttest $base = $(git rev-parse remotes/a:) &&\n+\tgit svn branch -m \"created branch b blah\" b &&\n+\ttest $base = $(git rev-parse remotes/b:) &&\n+\ttest_must_fail git branch -m \"no branchname\" &&\n+\tgit svn branch -n c &&\n+\ttest_must_fail git rev-parse remotes/c &&\n+\ttest_must_fail git svn branch a &&\n+\tgit svn branch -t tag1 &&\n+\ttest $base = $(git rev-parse remotes/tags/tag1:) &&\n+\tgit svn branch --tag tag2 &&\n+\ttest $base = $(git rev-parse remotes/tags/tag2:) &&\n+\tgit svn tag tag3 &&\n+\ttest $base = $(git rev-parse remotes/tags/tag3:) &&\n+\tgit svn tag -m \"created tag4 foo\" tag4 &&\n+\ttest $base = $(git rev-parse remotes/tags/tag4:) &&\n+\ttest_must_fail git svn tag -m \"no tagname\" &&\n+\tgit svn tag -n tag5 &&\n+\ttest_must_fail git rev-parse remotes/tags/tag5 &&\n+\ttest_must_fail git svn tag tag1\n+'\n+\n+test_done\n-- \n1.6.0.2.307.gc427\n"},{"id":"92319","messageId":"20081005023517.GA14078@untitled","threadId":"15780","inReplyTo":"20081004142444.GC3052@riemann.deskinm.fdns.net","subject":"Re: [PATCH v3] Add git-svn branch to allow branch creation in SVN repositories","fromName":"Eric Wong","fromEmail":"normalperson@yhbt.net","sentAt":"2008-10-05T02:35:17Z","receivedAt":"2008-10-05T02:35:17Z","isPatch":true,"sender":{"key":"e@80x24.org","avatar":null},"body":"Thanks Deskin, one small fix to quiet the warning that was\nmaking t9108 fail.\n\n>From c7e48f128865823a4c5024eb4f67b69231d1d5dc Mon Sep 17 00:00:00 2001\nFrom: Florian Ragwitz <rafl@debian.org>\nDate: Tue, 2 Sep 2008 14:20:39 +0000\nSubject: [PATCH] Add git-svn branch to allow branch creation in SVN repositories\n\n[ew: fixed a warning to stderr causing t9108 to fail]\n\nSigned-off-by: Florian Ragwitz <rafl@debian.org>\nSigned-off-by: Deskin Miller <deskinm@umich.edu>\nAcked-by: Eric Wong <normalperson@yhbt.net>\n---\n Documentation/git-svn.txt     |   24 ++++++++++++++++-\n git-svn.perl                  |   47 ++++++++++++++++++++++++++++++++-\n t/t9128-git-svn-cmd-branch.sh |   59 +++++++++++++++++++++++++++++++++++++++++\n 3 files changed, 128 insertions(+), 2 deletions(-)\n create mode 100755 t/t9128-git-svn-cmd-branch.sh\n\ndiff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt\nindex 82d03b4..84c8f3c 100644\n--- a/Documentation/git-svn.txt\n+++ b/Documentation/git-svn.txt\n@@ -149,6 +149,22 @@ and have no uncommitted changes.\n \tis very strongly discouraged.\n --\n \n+'branch'::\n+\tCreate a branch in the SVN repository.\n+\n+-m;;\n+--message;;\n+\tAllows to specify the commit message.\n+\n+-t;;\n+--tag;;\n+\tCreate a tag by using the tags_subdir instead of the branches_subdir\n+\tspecified during git svn init.\n+\n+'tag'::\n+\tCreate a tag in the SVN repository. This is a shorthand for\n+\t'branch -t'.\n+\n 'log'::\n \tThis should make it easy to look up svn log messages when svn\n \tusers refer to -r/--revision numbers.\n@@ -372,7 +388,8 @@ Passed directly to 'git-rebase' when using 'dcommit' if a\n -n::\n --dry-run::\n \n-This can be used with the 'dcommit' and 'rebase' commands.\n+This can be used with the 'dcommit', 'rebase', 'branch' and 'tag'\n+commands.\n \n For 'dcommit', print out the series of git arguments that would show\n which diffs would be committed to SVN.\n@@ -381,6 +398,9 @@ For 'rebase', display the local branch associated with the upstream svn\n repository associated with the current branch and the URL of svn\n repository that will be fetched from.\n \n+For 'branch' and 'tag', display the urls that will be used for copying when\n+creating the branch or tag.\n+\n --\n \n ADVANCED OPTIONS\n@@ -498,6 +518,8 @@ Tracking and contributing to an entire Subversion-managed project\n \tgit svn clone http://svn.example.com/project -T trunk -b branches -t tags\n # View all branches and tags you have cloned:\n \tgit branch -r\n+# Create a new branch in SVN\n+    git svn branch waldo\n # Reset your master to trunk (or any other branch, replacing 'trunk'\n # with the appropriate name):\n \tgit reset --hard remotes/trunk\ndiff --git a/git-svn.perl b/git-svn.perl\nindex 7609a83..ef6d773 100755\n--- a/git-svn.perl\n+++ b/git-svn.perl\n@@ -66,7 +66,7 @@ my ($_stdin, $_help, $_edit,\n \t$_version, $_fetch_all, $_no_rebase,\n \t$_merge, $_strategy, $_dry_run, $_local,\n \t$_prefix, $_no_checkout, $_url, $_verbose,\n-\t$_git_format, $_commit_url);\n+\t$_git_format, $_commit_url, $_tag);\n $Git::SVN::_follow_parent = 1;\n my %remote_opts = ( 'username=s' => \\$Git::SVN::Prompt::_username,\n                     'config-dir=s' => \\$Git::SVN::Ra::config_dir,\n@@ -131,6 +131,15 @@ my %cmd = (\n \t\t\t  'revision|r=i' => \\$_revision,\n \t\t\t  'no-rebase' => \\$_no_rebase,\n \t\t\t%cmt_opts, %fc_opts } ],\n+\tbranch => [ \\&cmd_branch,\n+\t            'Create a branch in the SVN repository',\n+\t            { 'message|m=s' => \\$_message,\n+\t              'dry-run|n' => \\$_dry_run,\n+\t\t      'tag|t' => \\$_tag } ],\n+\ttag => [ sub { $_tag = 1; cmd_branch(@_) },\n+\t         'Create a tag in the SVN repository',\n+\t         { 'message|m=s' => \\$_message,\n+\t           'dry-run|n' => \\$_dry_run } ],\n \t'set-tree' => [ \\&cmd_set_tree,\n \t                \"Set an SVN repository to a git tree-ish\",\n \t\t\t{ 'stdin|' => \\$_stdin, %cmt_opts, %fc_opts, } ],\n@@ -537,6 +546,42 @@ sub cmd_dcommit {\n \tunlink $gs->{index};\n }\n \n+sub cmd_branch {\n+\tmy ($branch_name, $head) = @_;\n+\n+\tunless (defined $branch_name && length $branch_name) {\n+\t\tdie(($_tag ? \"tag\" : \"branch\") . \" name required\\n\");\n+\t}\n+\t$head ||= 'HEAD';\n+\n+\tmy ($src, $rev, undef, $gs) = working_head_info($head);\n+\n+\tmy $remote = Git::SVN::read_all_remotes()->{svn};\n+\tmy $glob = $remote->{ $_tag ? 'tags' : 'branches' };\n+\tmy ($lft, $rgt) = @{ $glob->{path} }{qw/left right/};\n+\tmy $dst = join '/', $remote->{url}, $lft, $branch_name, ($rgt || ());\n+\n+\tmy $ctx = SVN::Client->new(\n+\t\tauth    => Git::SVN::Ra::_auth_providers(),\n+\t\tlog_msg => sub {\n+\t\t\t${ $_[0] } = defined $_message\n+\t\t\t\t? $_message\n+\t\t\t\t: 'Create ' . ($_tag ? 'tag ' : 'branch ' )\n+\t\t\t\t. $branch_name;\n+\t\t},\n+\t);\n+\n+\teval {\n+\t\t$ctx->ls($dst, 'HEAD', 0);\n+\t} and die \"branch ${branch_name} already exists\\n\";\n+\n+\tprint \"Copying ${src} at r${rev} to ${dst}...\\n\";\n+\t$ctx->copy($src, $rev, $dst)\n+\t\tunless $_dry_run;\n+\n+\t$gs->fetch_all;\n+}\n+\n sub cmd_find_rev {\n \tmy $revision_or_hash = shift or die \"SVN or git revision required \",\n \t                                    \"as a command-line argument\\n\";\ndiff --git a/t/t9128-git-svn-cmd-branch.sh b/t/t9128-git-svn-cmd-branch.sh\nnew file mode 100755\nindex 0000000..47c4d4d\n--- /dev/null\n+++ b/t/t9128-git-svn-cmd-branch.sh\n@@ -0,0 +1,59 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2008 Deskin Miller\n+#\n+\n+test_description='git svn partial-rebuild tests'\n+. ./lib-git-svn.sh\n+\n+test_expect_success 'initialize svnrepo' '\n+\tmkdir import &&\n+\t(\n+\t\tcd import &&\n+\t\tmkdir trunk branches tags &&\n+\t\tcd trunk &&\n+\t\techo foo > foo &&\n+\t\tcd .. &&\n+\t\tsvn import -m \"import for git-svn\" . \"$svnrepo\" >/dev/null &&\n+\t\tcd .. &&\n+\t\trm -rf import &&\n+\t\tsvn co \"$svnrepo\"/trunk trunk &&\n+\t\tcd trunk &&\n+\t\techo bar >> foo &&\n+\t\tsvn ci -m \"updated trunk\" &&\n+\t\tcd .. &&\n+\t\trm -rf trunk\n+\t)\n+'\n+\n+test_expect_success 'import into git' '\n+\tgit svn init --stdlayout \"$svnrepo\" &&\n+\tgit svn fetch &&\n+\tgit checkout remotes/trunk\n+'\n+\n+test_expect_success 'git svn branch tests' '\n+\tgit svn branch a &&\n+\tbase=$(git rev-parse HEAD:) &&\n+\ttest $base = $(git rev-parse remotes/a:) &&\n+\tgit svn branch -m \"created branch b blah\" b &&\n+\ttest $base = $(git rev-parse remotes/b:) &&\n+\ttest_must_fail git branch -m \"no branchname\" &&\n+\tgit svn branch -n c &&\n+\ttest_must_fail git rev-parse remotes/c &&\n+\ttest_must_fail git svn branch a &&\n+\tgit svn branch -t tag1 &&\n+\ttest $base = $(git rev-parse remotes/tags/tag1:) &&\n+\tgit svn branch --tag tag2 &&\n+\ttest $base = $(git rev-parse remotes/tags/tag2:) &&\n+\tgit svn tag tag3 &&\n+\ttest $base = $(git rev-parse remotes/tags/tag3:) &&\n+\tgit svn tag -m \"created tag4 foo\" tag4 &&\n+\ttest $base = $(git rev-parse remotes/tags/tag4:) &&\n+\ttest_must_fail git svn tag -m \"no tagname\" &&\n+\tgit svn tag -n tag5 &&\n+\ttest_must_fail git rev-parse remotes/tags/tag5 &&\n+\ttest_must_fail git svn tag tag1\n+'\n+\n+test_done\n-- \nEric Wong\n"}]}