git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v2] git svn dcommit: new option --interactive.

From
FHFrédéric Heitzmann <frederic.heitzmann@gmail.com>
Date
Sep 12, 2011, 14:57 UTC
Message-ID
<1315839448-4046-1-git-send-email-frederic.heitzmann@gmail.com>

Allow the user to check the patch set before it is commited to SNV. It is then possible to accept/discard one patch, accept all, or quit.

This interactive mode is similar with 'git send email' behaviour. However, 'git svn dcommit' returns as soon as one patch is discarded. Part of the code was taken from git-send-email.perl (see 'ask' function)

Tests several combinations of potential answers to 'git svn dcommit --interactive'. For each of them, test whether patches were commited to SVN or not.

Thanks-to Eric Wong <normalperson@yhbt.net> for the initial idea.
Signed-off-by: Frédéric Heitzmann <frederic.heitzmann@gmail.com>
Signed-off-by: Eric Wong <normalperson@yhbt.net>
---
 Documentation/git-svn.txt              |    8 +++
 git-svn.perl                           |   76 +++++++++++++++++++++++++++++++-
 t/t9160-git-svn-dcommit-interactive.sh |   64 +++++++++++++++++++++++++++
 3 files changed, 147 insertions(+), 1 deletions(-)
 create mode 100644 t/t9160-git-svn-dcommit-interactive.sh
diff --git a/Documentation/git-svn.txt b/Documentation/git-svn.txt
index e75fc19..022989f 100644
--- a/Documentation/git-svn.txt
+++ b/Documentation/git-svn.txt
@@ -226,6 +226,14 @@ discouraged.
 	branches, use a single space character between the branches
 	(`--mergeinfo="/branches/foo:1-10 /branches/bar:3,5-6,8"`)
 
+--interactive;;
+	Ask the user to confirm that a patch set should actually be sent to SVN.
+	For each patch, one may answer "yes" (accept this patch), "no" (discard this
+	patch), "all" (accept all patches), or "quit".
+	+
+	'git svn dcommit' returns immediately if answer if "no" or "quit", without
+	commiting anything to SVN.
+
 'branch'::
 	Create a branch in the SVN repository.
 
diff --git a/git-svn.perl b/git-svn.perl
index d067837..56b6867 100755
--- a/git-svn.perl
+++ b/git-svn.perl
@@ -87,7 +87,7 @@ my ($_stdin, $_help, $_edit,
 	$_version, $_fetch_all, $_no_rebase, $_fetch_parent,
 	$_merge, $_strategy, $_dry_run, $_local,
 	$_prefix, $_no_checkout, $_url, $_verbose,
-	$_git_format, $_commit_url, $_tag, $_merge_info);
+	$_git_format, $_commit_url, $_tag, $_merge_info, $_interactive);
 $Git::SVN::_follow_parent = 1;
 $SVN::Git::Fetcher::_placeholder_filename = ".gitignore";
 $_q ||= 0;
@@ -163,6 +163,7 @@ my %cmd = (
 			  'revision|r=i' => \$_revision,
 			  'no-rebase' => \$_no_rebase,
 			  'mergeinfo=s' => \$_merge_info,
+			  'interactive|i' => \$_interactive,
 			%cmt_opts, %fc_opts } ],
 	branch => [ \&cmd_branch,
 	            'Create a branch in the SVN repository',
@@ -256,6 +257,27 @@ my %cmd = (
 		{} ],
 );
 
+use Term::ReadLine;
+package FakeTerm;
+sub new {
+	my ($class, $reason) = @_;
+	return bless \$reason, shift;
+}
+sub readline {
+	my $self = shift;
+	die "Cannot use readline on FakeTerm: $$self";
+}
+package main;
+
+my $term = eval {
+	$ENV{"GIT_SVN_NOTTY"}
+		? new Term::ReadLine 'git-svn', \*STDIN, \*STDOUT
+		: new Term::ReadLine 'git-svn';
+};
+if ($@) {
+	$term = new FakeTerm "$@: going non-interactive";
+}
+
 my $cmd;
 for (my $i = 0; $i < @ARGV; $i++) {
 	if (defined $cmd{$ARGV[$i]}) {
@@ -366,6 +388,36 @@ sub version {
 	exit 0;
 }
 
+sub ask {
+	my ($prompt, %arg) = @_;
+	my $valid_re = $arg{valid_re};
+	my $default = $arg{default};
+	my $resp;
+	my $i = 0;
+
+	if ( !( defined($term->IN)
+            && defined( fileno($term->IN) )
+            && defined( $term->OUT )
+            && defined( fileno($term->OUT) ) ) ){
+		return defined($default) ? $default : undef;
+	}
+
+	while ($i++ < 10) {
+		$resp = $term->readline($prompt);
+		if (!defined $resp) { # EOF
+			print "\n";
+			return defined $default ? $default : undef;
+		}
+		if ($resp eq '' and defined $default) {
+			return $default;
+		}
+		if (!defined $valid_re or $resp =~ /$valid_re/) {
+			return $resp;
+		}
+	}
+	return undef;
+}
+
 sub do_git_init_db {
 	unless (-d $ENV{GIT_DIR}) {
 		my @init_db = ('init');
@@ -557,6 +609,28 @@ sub cmd_dcommit {
 		     "If these changes depend on each other, re-running ",
 		     "without --no-rebase may be required."
 	}
+
+	if (defined $_interactive){
+		my $ask_default = "y";
+		foreach my $d (@$linear_refs){
+			print "debug : d = $d\n";
+			my ($fh, $ctx) = command_output_pipe(qw(show --summary), "$d");
+			while (<$fh>){
+				print $_;
+			}
+			command_close_pipe($fh, $ctx);
+			$_ = ask("Commit this patch to SVN? ([y]es (default)|[n]o|[q]uit|[a]ll): ",
+			         valid_re => qr/^(?:yes|y|no|n|quit|q|all|a)/i,
+			         default => $ask_default);
+			die "Commit this patch reply required" unless defined $_;
+			if (/^[nq]/i) {
+				exit(0);
+			} elsif (/^a/i) {
+				last;
+			}
+		}
+	}
+
 	my $expect_url = $url;
 	Git::SVN::remove_username($expect_url);
 	if (defined($_merge_info)) {
diff --git a/t/t9160-git-svn-dcommit-interactive.sh b/t/t9160-git-svn-dcommit-interactive.sh
new file mode 100644
index 0000000..e38d9fa
--- /dev/null
+++ b/t/t9160-git-svn-dcommit-interactive.sh
@@ -0,0 +1,64 @@
+#!/bin/sh
+#
+# Copyright (c) 2011 Frédéric Heitzmann
+
+test_description='git svn dcommit --interactive series'
+. ./lib-git-svn.sh
+
+test_expect_success 'initialize repo' '
+	svn_cmd mkdir -m"mkdir test-interactive" "$svnrepo/test-interactive" &&
+	git svn clone "$svnrepo/test-interactive" test-interactive &&
+	cd test-interactive &&
+	touch foo && git add foo && git commit -m"foo: first commit" &&
+	git svn dcommit
+	'
+
+test_expect_success 'answers: y [\n] yes' '
+	(
+		echo "change #1" >> foo && git commit -a -m"change #1" &&
+		echo "change #2" >> foo && git commit -a -m"change #2" &&
+		echo "change #3" >> foo && git commit -a -m"change #3" &&
+		( echo "y
+
+y" | GIT_SVN_NOTTY=1 git svn dcommit --interactive ) &&
+		test $(git rev-parse HEAD) = $(git rev-parse remotes/git-svn)
+	)
+	'
+
+test_expect_success 'answers: yes yes no' '
+	(
+		echo "change #1" >> foo && git commit -a -m"change #1" &&
+		echo "change #2" >> foo && git commit -a -m"change #2" &&
+		echo "change #3" >> foo && git commit -a -m"change #3" &&
+		( echo "yes
+yes
+no" | GIT_SVN_NOTTY=1 git svn dcommit --interactive ) &&
+		test $(git rev-parse HEAD^^^) = $(git rev-parse remotes/git-svn) &&
+		git reset --hard remotes/git-svn
+	)
+	'
+
+test_expect_success 'answers: yes quit' '
+	(
+		echo "change #1" >> foo && git commit -a -m"change #1" &&
+		echo "change #2" >> foo && git commit -a -m"change #2" &&
+		echo "change #3" >> foo && git commit -a -m"change #3" &&
+		( echo "yes
+quit" | GIT_SVN_NOTTY=1 git svn dcommit --interactive ) &&
+		test $(git rev-parse HEAD^^^) = $(git rev-parse remotes/git-svn) &&
+		git reset --hard remotes/git-svn
+	)
+	'
+
+test_expect_success 'answers: all' '
+	(
+		echo "change #1" >> foo && git commit -a -m"change #1" &&
+		echo "change #2" >> foo && git commit -a -m"change #2" &&
+		echo "change #3" >> foo && git commit -a -m"change #3" &&
+		( echo "all" | GIT_SVN_NOTTY=1 git svn dcommit --interactive ) &&
+		test $(git rev-parse HEAD) = $(git rev-parse remotes/git-svn) &&
+		git reset --hard remotes/git-svn
+	)
+	'
+
+test_done
-- 
1.7.7.rc0.73.g0a352.dirty
Message 1 of 1 in “git svn dcommit: new option --interactive.”
  1. git svn dcommit: new option --interactive.Frédéric Heitzmann, Sep 12, 2011

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.