{"thread":{"id":"28370","subject":"[PATCH v2] git svn dcommit: new option --interactive.","startedAt":"2011-09-12T14:57:28Z","lastAt":"2011-09-12T14:57:28Z","messageCount":1,"participants":["Frédéric Heitzmann"],"isPatch":true,"patchVersion":2,"patchTotal":null},"messages":[{"id":"175335","messageId":"1315839448-4046-1-git-send-email-frederic.heitzmann@gmail.com","threadId":"28370","inReplyTo":null,"subject":"[PATCH v2] git svn dcommit: new option --interactive.","fromName":"Frédéric Heitzmann","fromEmail":"frederic.heitzmann@gmail.com","sentAt":"2011-09-12T14:57:28Z","receivedAt":"2011-09-12T14:57:28Z","isPatch":true,"sender":{"key":"frederic.heitzmann@gmail.com","avatar":null},"body":"Allow the user to check the patch set before it is commited to SNV. It is\nthen possible to accept/discard one patch, accept all, or quit.\n\nThis interactive mode is similar with 'git send email' behaviour. However,\n'git svn dcommit' returns as soon as one patch is discarded.\nPart of the code was taken from git-send-email.perl (see 'ask' function)\n\nTests several combinations of potential answers to\n'git svn dcommit --interactive'. For each of them, test whether patches\nwere commited to SVN or not.\n\nThanks-to Eric Wong <normalperson@yhbt.net> for the initial idea.\n\nSigned-off-by: Frédéric Heitzmann <frederic.heitzmann@gmail.com>\nSigned-off-by: Eric Wong <normalperson@yhbt.net>\n---\n Documentation/git-svn.txt              |    8 +++\n git-svn.perl                           |   76 +++++++++++++++++++++++++++++++-\n t/t9160-git-svn-dcommit-interactive.sh |   64 +++++++++++++++++++++++++++\n 3 files changed, 147 insertions(+), 1 deletions(-)\n create mode 100644 t/t9160-git-svn-dcommit-interactive.sh\n\ndiff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt\nindex e75fc19..022989f 100644\n--- a/Documentation/git-svn.txt\n+++ b/Documentation/git-svn.txt\n@@ -226,6 +226,14 @@ discouraged.\n \tbranches, use a single space character between the branches\n \t(`--mergeinfo=\"/branches/foo:1-10 /branches/bar:3,5-6,8\"`)\n \n+--interactive;;\n+\tAsk the user to confirm that a patch set should actually be sent to SVN.\n+\tFor each patch, one may answer \"yes\" (accept this patch), \"no\" (discard this\n+\tpatch), \"all\" (accept all patches), or \"quit\".\n+\t+\n+\t'git svn dcommit' returns immediately if answer if \"no\" or \"quit\", without\n+\tcommiting anything to SVN.\n+\n 'branch'::\n \tCreate a branch in the SVN repository.\n \ndiff --git a/git-svn.perl b/git-svn.perl\nindex d067837..56b6867 100755\n--- a/git-svn.perl\n+++ b/git-svn.perl\n@@ -87,7 +87,7 @@ my ($_stdin, $_help, $_edit,\n \t$_version, $_fetch_all, $_no_rebase, $_fetch_parent,\n \t$_merge, $_strategy, $_dry_run, $_local,\n \t$_prefix, $_no_checkout, $_url, $_verbose,\n-\t$_git_format, $_commit_url, $_tag, $_merge_info);\n+\t$_git_format, $_commit_url, $_tag, $_merge_info, $_interactive);\n $Git::SVN::_follow_parent = 1;\n $SVN::Git::Fetcher::_placeholder_filename = \".gitignore\";\n $_q ||= 0;\n@@ -163,6 +163,7 @@ my %cmd = (\n \t\t\t  'revision|r=i' => \\$_revision,\n \t\t\t  'no-rebase' => \\$_no_rebase,\n \t\t\t  'mergeinfo=s' => \\$_merge_info,\n+\t\t\t  'interactive|i' => \\$_interactive,\n \t\t\t%cmt_opts, %fc_opts } ],\n \tbranch => [ \\&cmd_branch,\n \t            'Create a branch in the SVN repository',\n@@ -256,6 +257,27 @@ my %cmd = (\n \t\t{} ],\n );\n \n+use Term::ReadLine;\n+package FakeTerm;\n+sub new {\n+\tmy ($class, $reason) = @_;\n+\treturn bless \\$reason, shift;\n+}\n+sub readline {\n+\tmy $self = shift;\n+\tdie \"Cannot use readline on FakeTerm: $$self\";\n+}\n+package main;\n+\n+my $term = eval {\n+\t$ENV{\"GIT_SVN_NOTTY\"}\n+\t\t? new Term::ReadLine 'git-svn', \\*STDIN, \\*STDOUT\n+\t\t: new Term::ReadLine 'git-svn';\n+};\n+if ($@) {\n+\t$term = new FakeTerm \"$@: going non-interactive\";\n+}\n+\n my $cmd;\n for (my $i = 0; $i < @ARGV; $i++) {\n \tif (defined $cmd{$ARGV[$i]}) {\n@@ -366,6 +388,36 @@ sub version {\n \texit 0;\n }\n \n+sub ask {\n+\tmy ($prompt, %arg) = @_;\n+\tmy $valid_re = $arg{valid_re};\n+\tmy $default = $arg{default};\n+\tmy $resp;\n+\tmy $i = 0;\n+\n+\tif ( !( defined($term->IN)\n+            && defined( fileno($term->IN) )\n+            && defined( $term->OUT )\n+            && defined( fileno($term->OUT) ) ) ){\n+\t\treturn defined($default) ? $default : undef;\n+\t}\n+\n+\twhile ($i++ < 10) {\n+\t\t$resp = $term->readline($prompt);\n+\t\tif (!defined $resp) { # EOF\n+\t\t\tprint \"\\n\";\n+\t\t\treturn defined $default ? $default : undef;\n+\t\t}\n+\t\tif ($resp eq '' and defined $default) {\n+\t\t\treturn $default;\n+\t\t}\n+\t\tif (!defined $valid_re or $resp =~ /$valid_re/) {\n+\t\t\treturn $resp;\n+\t\t}\n+\t}\n+\treturn undef;\n+}\n+\n sub do_git_init_db {\n \tunless (-d $ENV{GIT_DIR}) {\n \t\tmy @init_db = ('init');\n@@ -557,6 +609,28 @@ sub cmd_dcommit {\n \t\t     \"If these changes depend on each other, re-running \",\n \t\t     \"without --no-rebase may be required.\"\n \t}\n+\n+\tif (defined $_interactive){\n+\t\tmy $ask_default = \"y\";\n+\t\tforeach my $d (@$linear_refs){\n+\t\t\tprint \"debug : d = $d\\n\";\n+\t\t\tmy ($fh, $ctx) = command_output_pipe(qw(show --summary), \"$d\");\n+\t\t\twhile (<$fh>){\n+\t\t\t\tprint $_;\n+\t\t\t}\n+\t\t\tcommand_close_pipe($fh, $ctx);\n+\t\t\t$_ = ask(\"Commit this patch to SVN? ([y]es (default)|[n]o|[q]uit|[a]ll): \",\n+\t\t\t         valid_re => qr/^(?:yes|y|no|n|quit|q|all|a)/i,\n+\t\t\t         default => $ask_default);\n+\t\t\tdie \"Commit this patch reply required\" unless defined $_;\n+\t\t\tif (/^[nq]/i) {\n+\t\t\t\texit(0);\n+\t\t\t} elsif (/^a/i) {\n+\t\t\t\tlast;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n \tmy $expect_url = $url;\n \tGit::SVN::remove_username($expect_url);\n \tif (defined($_merge_info)) {\ndiff --git a/t/t9160-git-svn-dcommit-interactive.sh b/t/t9160-git-svn-dcommit-interactive.sh\nnew file mode 100644\nindex 0000000..e38d9fa\n--- /dev/null\n+++ b/t/t9160-git-svn-dcommit-interactive.sh\n@@ -0,0 +1,64 @@\n+#!/bin/sh\n+#\n+# Copyright (c) 2011 Frédéric Heitzmann\n+\n+test_description='git svn dcommit --interactive series'\n+. ./lib-git-svn.sh\n+\n+test_expect_success 'initialize repo' '\n+\tsvn_cmd mkdir -m\"mkdir test-interactive\" \"$svnrepo/test-interactive\" &&\n+\tgit svn clone \"$svnrepo/test-interactive\" test-interactive &&\n+\tcd test-interactive &&\n+\ttouch foo && git add foo && git commit -m\"foo: first commit\" &&\n+\tgit svn dcommit\n+\t'\n+\n+test_expect_success 'answers: y [\\n] yes' '\n+\t(\n+\t\techo \"change #1\" >> foo && git commit -a -m\"change #1\" &&\n+\t\techo \"change #2\" >> foo && git commit -a -m\"change #2\" &&\n+\t\techo \"change #3\" >> foo && git commit -a -m\"change #3\" &&\n+\t\t( echo \"y\n+\n+y\" | GIT_SVN_NOTTY=1 git svn dcommit --interactive ) &&\n+\t\ttest $(git rev-parse HEAD) = $(git rev-parse remotes/git-svn)\n+\t)\n+\t'\n+\n+test_expect_success 'answers: yes yes no' '\n+\t(\n+\t\techo \"change #1\" >> foo && git commit -a -m\"change #1\" &&\n+\t\techo \"change #2\" >> foo && git commit -a -m\"change #2\" &&\n+\t\techo \"change #3\" >> foo && git commit -a -m\"change #3\" &&\n+\t\t( echo \"yes\n+yes\n+no\" | GIT_SVN_NOTTY=1 git svn dcommit --interactive ) &&\n+\t\ttest $(git rev-parse HEAD^^^) = $(git rev-parse remotes/git-svn) &&\n+\t\tgit reset --hard remotes/git-svn\n+\t)\n+\t'\n+\n+test_expect_success 'answers: yes quit' '\n+\t(\n+\t\techo \"change #1\" >> foo && git commit -a -m\"change #1\" &&\n+\t\techo \"change #2\" >> foo && git commit -a -m\"change #2\" &&\n+\t\techo \"change #3\" >> foo && git commit -a -m\"change #3\" &&\n+\t\t( echo \"yes\n+quit\" | GIT_SVN_NOTTY=1 git svn dcommit --interactive ) &&\n+\t\ttest $(git rev-parse HEAD^^^) = $(git rev-parse remotes/git-svn) &&\n+\t\tgit reset --hard remotes/git-svn\n+\t)\n+\t'\n+\n+test_expect_success 'answers: all' '\n+\t(\n+\t\techo \"change #1\" >> foo && git commit -a -m\"change #1\" &&\n+\t\techo \"change #2\" >> foo && git commit -a -m\"change #2\" &&\n+\t\techo \"change #3\" >> foo && git commit -a -m\"change #3\" &&\n+\t\t( echo \"all\" | GIT_SVN_NOTTY=1 git svn dcommit --interactive ) &&\n+\t\ttest $(git rev-parse HEAD) = $(git rev-parse remotes/git-svn) &&\n+\t\tgit reset --hard remotes/git-svn\n+\t)\n+\t'\n+\n+test_done\n-- \n1.7.7.rc0.73.g0a352.dirty\n"}]}