{"thread":{"id":"32844","subject":"[PATCH 0/4] Make git-send-email git-credential","startedAt":"2013-02-06T20:47:02Z","lastAt":"2013-02-07T07:22:49Z","messageCount":11,"participants":["Michal Nazarewicz","Jeff King","Junio C Hamano","Matthieu Moy"],"isPatch":true,"patchVersion":1,"patchTotal":4},"messages":[{"id":"208857","messageId":"cover.1360183427.git.mina86@mina86.com","threadId":"32844","inReplyTo":null,"subject":"[PATCH 0/4] Make git-send-email git-credential","fromName":"Michal Nazarewicz","fromEmail":"mpn@google.com","sentAt":"2013-02-06T20:47:02Z","receivedAt":"2013-02-06T20:47:02Z","isPatch":true,"sender":{"key":"mpn@google.com","avatar":null},"body":"From: Michal Nazarewicz <mina86@mina86.com>\n\nAs discussed on the list, adding git-credential interface to Git.pm\n(sort of copied from git-remote-mediawiki) and making git-send-email\nuse it.\n\nI see git-remote-mediawiki does not have “use Git” so I did not touch\nit.  On top of that I'd have no way to tests the changes anyway.\n\nMichal Nazarewicz (4):\n  Git.pm: Allow command_close_bidi_pipe() to be called as method\n  Git.pm: Allow pipes to be closed prior to calling\n    command_close_bidi_pipe\n  Git.pm: Add interface for git credential command.\n  git-send-email: Use git credential to obtain password.\n\n Documentation/git-send-email.txt |   4 +-\n git-send-email.perl              |  59 +++++++++++---------\n perl/Git.pm                      | 116 ++++++++++++++++++++++++++++++++++++++-\n 3 files changed, 149 insertions(+), 30 deletions(-)\n\n-- \n1.8.1.2.549.g4fa355e\n"},{"id":"208862","messageId":"01fceaac7a7d961cc7e0355b9c3175e0ba68cd0f.1360183427.git.mina86@mina86.com","threadId":"32844","inReplyTo":"cover.1360183427.git.mina86@mina86.com","subject":"[PATCH 1/4] Git.pm: Allow command_close_bidi_pipe() to be called as method","fromName":"Michal Nazarewicz","fromEmail":"mpn@google.com","sentAt":"2013-02-06T20:47:03Z","receivedAt":"2013-02-06T20:47:03Z","isPatch":true,"sender":{"key":"mpn@google.com","avatar":null},"body":"From: Michal Nazarewicz <mina86@mina86.com>\n\nThe documentation of command_close_bidi_pipe() claims that it can\nbe called as a method, but it does not check whether the first\nargument is $self or not assuming the latter.  Using _maybe_self()\nfixes this.\n\nSigned-off-by: Michal Nazarewicz <mina86@mina86.com>\n---\n perl/Git.pm | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/perl/Git.pm b/perl/Git.pm\nindex 931047c..bbb753a 100644\n--- a/perl/Git.pm\n+++ b/perl/Git.pm\n@@ -430,7 +430,7 @@ have more complicated structure.\n \n sub command_close_bidi_pipe {\n \tlocal $?;\n-\tmy ($pid, $in, $out, $ctx) = @_;\n+\tmy ($self, $pid, $in, $out, $ctx) = _maybe_self(@_);\n \tforeach my $fh ($in, $out) {\n \t\tunless (close $fh) {\n \t\t\tif ($!) {\n-- \n1.8.1.2.549.g4fa355e\n"},{"id":"208859","messageId":"c0966644278b0addbef6a03289ef9c553addf573.1360183427.git.mina86@mina86.com","threadId":"32844","inReplyTo":"cover.1360183427.git.mina86@mina86.com","subject":"[PATCH 2/4] Git.pm: Allow pipes to be closed prior to calling command_close_bidi_pipe","fromName":"Michal Nazarewicz","fromEmail":"mpn@google.com","sentAt":"2013-02-06T20:47:04Z","receivedAt":"2013-02-06T20:47:04Z","isPatch":true,"sender":{"key":"mpn@google.com","avatar":null},"body":"From: Michal Nazarewicz <mina86@mina86.com>\n\nThe command_close_bidi_pipe() function will insist on closing both\ninput and output pipes returned by command_bidi_pipe().  With this\nchange it is possible to close one of the pipes in advance and\npass undef as an argument.\n\nThis allows for something like:\n\n  my ($pid, $in, $out, $ctx) = command_bidi_pipe(...);\n  print $out \"write data\";\n  close $out;\n  # ... do stuff with $in\n  command_close_bidi_pipe($pid, $in, undef, $ctx);\n\nSigned-off-by: Michal Nazarewicz <mina86@mina86.com>\n---\n perl/Git.pm | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/perl/Git.pm b/perl/Git.pm\nindex bbb753a..6a2d52d 100644\n--- a/perl/Git.pm\n+++ b/perl/Git.pm\n@@ -432,7 +432,7 @@ sub command_close_bidi_pipe {\n \tlocal $?;\n \tmy ($self, $pid, $in, $out, $ctx) = _maybe_self(@_);\n \tforeach my $fh ($in, $out) {\n-\t\tunless (close $fh) {\n+\t\tif (defined $fh && !close $fh) {\n \t\t\tif ($!) {\n \t\t\t\tcarp \"error closing pipe: $!\";\n \t\t\t} elsif ($? >> 8) {\n-- \n1.8.1.2.549.g4fa355e\n"},{"id":"208861","messageId":"f4b5413b0a55474346daa7b0866c7a4fed55778d.1360183427.git.mina86@mina86.com","threadId":"32844","inReplyTo":"cover.1360183427.git.mina86@mina86.com","subject":"[PATCH 3/4] Git.pm: Add interface for git credential command.","fromName":"Michal Nazarewicz","fromEmail":"mpn@google.com","sentAt":"2013-02-06T20:47:05Z","receivedAt":"2013-02-06T20:47:05Z","isPatch":true,"sender":{"key":"mpn@google.com","avatar":null},"body":"From: Michal Nazarewicz <mina86@mina86.com>\n\nAdd a credential() function which is an interface to the\ngit credential command.\n\nSigned-off-by: Michal Nazarewicz <mina86@mina86.com>\n---\n perl/Git.pm | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-\n 1 file changed, 111 insertions(+), 1 deletion(-)\n\ndiff --git a/perl/Git.pm b/perl/Git.pm\nindex 6a2d52d..5a18921 100644\n--- a/perl/Git.pm\n+++ b/perl/Git.pm\n@@ -59,7 +59,8 @@ require Exporter;\n                 command_bidi_pipe command_close_bidi_pipe\n                 version exec_path html_path hash_object git_cmd_try\n                 remote_refs prompt\n-                temp_acquire temp_release temp_reset temp_path);\n+                temp_acquire temp_release temp_reset temp_path\n+                credential);\n \n \n =head1 DESCRIPTION\n@@ -1000,6 +1001,115 @@ sub _close_cat_blob {\n }\n \n \n+sub _credential_read {\n+\tmy %credential;\n+\tmy ($reader, $op) = (@_);\n+\twhile (<$reader>) {\n+\t\tchomp;\n+\t\tmy ($key, $value) = /([^=]*)=(.*)/;\n+\t\tif (not defined $key) {\n+\t\t\tthrow Error::Simple(\"unable to parse git credential $op response:\\n$_\\n\");\n+\t\t}\n+\t\t$credential{$key} = $value;\n+\t}\n+\treturn %credential;\n+}\n+\n+sub _credential_write {\n+\tmy ($credential, $writer) = @_;\n+\n+\tfor my $key (sort {\n+\t\t# url overwrites other fields, so it must come first\n+\t\treturn -1 if $a eq 'url';\n+\t\treturn  1 if $b eq 'url';\n+\t\treturn $a cmp $b;\n+\t} keys %$credential) {\n+\t\tif (defined $credential->{$key} && length $credential->{$key}) {\n+\t\t\tprint $writer $key, '=', $credential->{$key}, \"\\n\";\n+\t\t}\n+\t}\n+\tprint $writer \"\\n\";\n+}\n+\n+sub _credential_run {\n+\tmy ($self, $credential, $op) = _maybe_self(@_);\n+\n+\tmy ($pid, $reader, $writer, $ctx) = command_bidi_pipe('credential', $op);\n+\n+\t_credential_write $credential, $writer;\n+\tclose $writer;\n+\n+\tif ($op eq \"fill\") {\n+\t\t%$credential = _credential_read $reader, $op;\n+\t} elsif (<$reader>) {\n+\t\tthrow Error::Simple(\"unexpected output from git credential $op response:\\n$_\\n\");\n+\t}\n+\n+\tcommand_close_bidi_pipe($pid, $reader, undef, $ctx);\n+}\n+\n+=item credential( CREDENTIAL_HASH [, OPERATION ] )\n+\n+=item credential( CREDENTIAL_HASH, CODE )\n+\n+Executes C<git credential> for a given set of credentials and\n+specified operation.  In both form C<CREDENTIAL_HASH> needs to be\n+a reference to a hash which stores credentials.  Under certain\n+conditions the hash can change.\n+\n+In the first form, C<OPERATION> can be C<'fill'> (or omitted),\n+C<'approve'> or C<'reject'>, and function will execute corresponding\n+C<git credential> sub-command.  In case of C<'fill'> the values stored\n+in C<CREDENTIAL_HASH> will be changed to the ones returned by the\n+C<git credential> command.  The usual usage would look something like:\n+\n+\tmy %cred = (\n+\t\t'protocol' => 'https',\n+\t\t'host' => 'example.com',\n+\t\t'username' => 'bob'\n+\t);\n+\tGit::credential \\%cred;\n+\tif (try_to_authenticate($cred{'username'}, $cred{'password'})) {\n+\t\tGit::credential \\%cred, 'approve';\n+\t\t... do more stuff ...\n+\t} else {\n+\t\tGit::credential \\%cred, 'reject';\n+\t}\n+\n+In the second form, C<CODE> needs to be a reference to a subroutine.\n+The function will execute C<git credential fill> to fill provided\n+credential hash, than call C<CODE> with C<CREDENTIAL> as the sole\n+argument, and finally depending on C<CODE>'s return value execute\n+C<git credential approve> (if return value yields true) or C<git\n+credential reject> (otherwise).  The return value is the same as what\n+C<CODE> returned.  With this form, the usage might look as follows:\n+\n+\tif (Git::credential {\n+\t\t'protocol' => 'https',\n+\t\t'host' => 'example.com',\n+\t\t'username' => 'bob'\n+\t}, sub {\n+\t\tmy $cred = shift;\n+\t\treturn try_to_authenticate($cred->{'username'}, $cred->{'password'});\n+\t}) {\n+\t\t... do more stuff ...\n+\t}\n+\n+=cut\n+\n+sub credential {\n+\tmy ($self, $credential, $op_or_code) = (_maybe_self(@_), 'fill');\n+\n+\tif ('CODE' eq ref $op_or_code) {\n+\t\t_credential_run $credential, 'fill';\n+\t\tmy $ret = $op_or_code->($credential);\n+\t\t_credential_run $credential, $ret ? 'approve' : 'reject';\n+\t\treturn $ret;\n+\t} else {\n+\t\t_credential_run $credential, $op_or_code;\n+\t}\n+}\n+\n { # %TEMP_* Lexical Context\n \n my (%TEMP_FILEMAP, %TEMP_FILES);\n-- \n1.8.1.2.549.g4fa355e\n"},{"id":"208860","messageId":"1d13f9fb587b8ea740cdd774173b8f63f45f86af.1360183427.git.mina86@mina86.com","threadId":"32844","inReplyTo":"cover.1360183427.git.mina86@mina86.com","subject":"[PATCH 4/4] git-send-email: Use git credential to obtain password.","fromName":"Michal Nazarewicz","fromEmail":"mpn@google.com","sentAt":"2013-02-06T20:47:06Z","receivedAt":"2013-02-06T20:47:06Z","isPatch":true,"sender":{"key":"mpn@google.com","avatar":null},"body":"From: Michal Nazarewicz <mina86@mina86.com>\n\nIf smtp_user is provided but smtp_pass is not, instead of prompting\nfor password, make git-send-email use git credential command\ninstead.\n\nSigned-off-by: Michal Nazarewicz <mina86@mina86.com>\n---\n Documentation/git-send-email.txt |  4 +--\n git-send-email.perl              | 59 +++++++++++++++++++++++-----------------\n 2 files changed, 36 insertions(+), 27 deletions(-)\n\ndiff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt\nindex 44a1f7c..0cffef8 100644\n--- a/Documentation/git-send-email.txt\n+++ b/Documentation/git-send-email.txt\n@@ -164,8 +164,8 @@ Sending\n Furthermore, passwords need not be specified in configuration files\n or on the command line. If a username has been specified (with\n '--smtp-user' or a 'sendemail.smtpuser'), but no password has been\n-specified (with '--smtp-pass' or 'sendemail.smtppass'), then the\n-user is prompted for a password while the input is masked for privacy.\n+specified (with '--smtp-pass' or 'sendemail.smtppass'), then\n+a password is obtained using 'git-credential'.\n \n --smtp-server=<host>::\n \tIf set, specifies the outgoing SMTP server to use (e.g.\ndiff --git a/git-send-email.perl b/git-send-email.perl\nindex be809e5..76bbfc3 100755\n--- a/git-send-email.perl\n+++ b/git-send-email.perl\n@@ -1045,6 +1045,39 @@ sub maildomain {\n \treturn maildomain_net() || maildomain_mta() || 'localhost.localdomain';\n }\n \n+# Returns 1 if authentication succeeded or was not necessary\n+# (smtp_user was not specified), and 0 otherwise.\n+\n+sub smtp_auth_maybe {\n+\tif (!defined $smtp_authuser || $auth) {\n+\t\treturn 1;\n+\t}\n+\n+\t# Workaround AUTH PLAIN/LOGIN interaction defect\n+\t# with Authen::SASL::Cyrus\n+\teval {\n+\t\trequire Authen::SASL;\n+\t\tAuthen::SASL->import(qw(Perl));\n+\t};\n+\n+\t# TODO: Authentication may fail not because credentials were\n+\t# invalid but due to other reasons, in which we should not\n+\t# reject credentials.\n+\t$auth = Git::credential({\n+\t\t'protocol' => 'smtp',\n+\t\t'host' => join(':', $smtp_server, $smtp_server_port),\n+\t\t'username' => $smtp_authuser,\n+\t\t# if there's no password, \"git credential fill\" will\n+\t\t# give us one, otherwise it'll just pass this one.\n+\t\t'password' => $smtp_authpass\n+\t}, sub {\n+\t\tmy $cred = shift;\n+\t\treturn !!$smtp->auth($cred->{'username'}, $cred->{'password'});\n+\t});\n+\n+\treturn $auth;\n+}\n+\n # Returns 1 if the message was sent, and 0 otherwise.\n # In actuality, the whole program dies when there\n # is an error sending a message.\n@@ -1185,31 +1218,7 @@ X-Mailer: git-send-email $gitversion\n \t\t\t    defined $smtp_server_port ? \" port=$smtp_server_port\" : \"\";\n \t\t}\n \n-\t\tif (defined $smtp_authuser) {\n-\t\t\t# Workaround AUTH PLAIN/LOGIN interaction defect\n-\t\t\t# with Authen::SASL::Cyrus\n-\t\t\teval {\n-\t\t\t\trequire Authen::SASL;\n-\t\t\t\tAuthen::SASL->import(qw(Perl));\n-\t\t\t};\n-\n-\t\t\tif (!defined $smtp_authpass) {\n-\n-\t\t\t\tsystem \"stty -echo\";\n-\n-\t\t\t\tdo {\n-\t\t\t\t\tprint \"Password: \";\n-\t\t\t\t\t$_ = <STDIN>;\n-\t\t\t\t\tprint \"\\n\";\n-\t\t\t\t} while (!defined $_);\n-\n-\t\t\t\tchomp($smtp_authpass = $_);\n-\n-\t\t\t\tsystem \"stty echo\";\n-\t\t\t}\n-\n-\t\t\t$auth ||= $smtp->auth( $smtp_authuser, $smtp_authpass ) or die $smtp->message;\n-\t\t}\n+\t\tsmtp_auth_maybe or die $smtp->message;\n \n \t\t$smtp->mail( $raw_from ) or die $smtp->message;\n \t\t$smtp->to( @recipients ) or die $smtp->message;\n-- \n1.8.1.2.549.g4fa355e\n"},{"id":"208882","messageId":"20130206230406.GK27507@sigill.intra.peff.net","threadId":"32844","inReplyTo":"c0966644278b0addbef6a03289ef9c553addf573.1360183427.git.mina86@mina86.com","subject":"Re: [PATCH 2/4] Git.pm: Allow pipes to be closed prior to calling command_close_bidi_pipe","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-02-06T23:04:06Z","receivedAt":"2013-02-06T23:04:06Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Feb 06, 2013 at 09:47:04PM +0100, Michal Nazarewicz wrote:\n\n> From: Michal Nazarewicz <mina86@mina86.com>\n> \n> The command_close_bidi_pipe() function will insist on closing both\n> input and output pipes returned by command_bidi_pipe().  With this\n> change it is possible to close one of the pipes in advance and\n> pass undef as an argument.\n> \n> This allows for something like:\n> \n>   my ($pid, $in, $out, $ctx) = command_bidi_pipe(...);\n>   print $out \"write data\";\n>   close $out;\n>   # ... do stuff with $in\n>   command_close_bidi_pipe($pid, $in, undef, $ctx);\n\nShould this part go into the documentation for command_close_bidi_pipe\nin Git.pm?\n\n-Peff\n"},{"id":"208884","messageId":"20130206231625.GL27507@sigill.intra.peff.net","threadId":"32844","inReplyTo":"f4b5413b0a55474346daa7b0866c7a4fed55778d.1360183427.git.mina86@mina86.com","subject":"Re: [PATCH 3/4] Git.pm: Add interface for git credential command.","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-02-06T23:16:25Z","receivedAt":"2013-02-06T23:16:25Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Feb 06, 2013 at 09:47:05PM +0100, Michal Nazarewicz wrote:\n\n> +sub _credential_read {\n> +\tmy %credential;\n> +\tmy ($reader, $op) = (@_);\n> +\twhile (<$reader>) {\n> +\t\tchomp;\n> +\t\tmy ($key, $value) = /([^=]*)=(.*)/;\n\nEmpty keys are not valid. Can we make this:\n\n  /^([^=]+)=(.*)/\n\nto fail the regex? Otherwise, I think this check:\n\n> +\t\tif (not defined $key) {\n> +\t\t\tthrow Error::Simple(\"unable to parse git credential $op response:\\n$_\\n\");\n> +\t\t}\n\nwould not pass because $key would be the empty string.\n\n> +sub _credential_write {\n> +\tmy ($credential, $writer) = @_;\n> +\n> +\tfor my $key (sort {\n> +\t\t# url overwrites other fields, so it must come first\n> +\t\treturn -1 if $a eq 'url';\n> +\t\treturn  1 if $b eq 'url';\n> +\t\treturn $a cmp $b;\n> +\t} keys %$credential) {\n> +\t\tif (defined $credential->{$key} && length $credential->{$key}) {\n> +\t\t\tprint $writer $key, '=', $credential->{$key}, \"\\n\";\n> +\t\t}\n> +\t}\n\nThere are a few disallowed characters, like \"\\n\" in key or value, and\n\"=\" in a key. They should never happen unless the caller is buggy, but\nshould we check and catch them here?\n\n> +In the second form, C<CODE> needs to be a reference to a subroutine.\n> +The function will execute C<git credential fill> to fill provided\n> +credential hash, than call C<CODE> with C<CREDENTIAL> as the sole\n> +argument, and finally depending on C<CODE>'s return value execute\n> +C<git credential approve> (if return value yields true) or C<git\n> +credential reject> (otherwise).  The return value is the same as what\n> +C<CODE> returned.  With this form, the usage might look as follows:\n\nThis is a nice touch. It makes the normal calling code a lot simpler.\n\n-Peff\n"},{"id":"208885","messageId":"20130206231943.GM27507@sigill.intra.peff.net","threadId":"32844","inReplyTo":"1d13f9fb587b8ea740cdd774173b8f63f45f86af.1360183427.git.mina86@mina86.com","subject":"Re: [PATCH 4/4] git-send-email: Use git credential to obtain password.","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2013-02-06T23:19:43Z","receivedAt":"2013-02-06T23:19:43Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Feb 06, 2013 at 09:47:06PM +0100, Michal Nazarewicz wrote:\n\n> From: Michal Nazarewicz <mina86@mina86.com>\n> \n> If smtp_user is provided but smtp_pass is not, instead of prompting\n> for password, make git-send-email use git credential command\n> instead.\n> \n> Signed-off-by: Michal Nazarewicz <mina86@mina86.com>\n> ---\n>  Documentation/git-send-email.txt |  4 +--\n>  git-send-email.perl              | 59 +++++++++++++++++++++++-----------------\n>  2 files changed, 36 insertions(+), 27 deletions(-)\n\nNice. I don't see anything obviously wrong with the code, but I didn't\ntry it myself. I wonder how hard it would be to have some tests in\nt9001. It looks like we don't test the smtp code paths at all, since we\nwould have to implement a fake smtp server. Which probably means the\nanswer is is \"pretty hard\", unless there is an easy-to-use CPAN smtp\nserver module we can plug in.\n\n-Peff\n"},{"id":"208890","messageId":"7vip659e08.fsf@alter.siamese.dyndns.org","threadId":"32844","inReplyTo":"20130206230406.GK27507@sigill.intra.peff.net","subject":"Re: [PATCH 2/4] Git.pm: Allow pipes to be closed prior to calling command_close_bidi_pipe","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2013-02-07T00:14:15Z","receivedAt":"2013-02-07T00:14:15Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n> On Wed, Feb 06, 2013 at 09:47:04PM +0100, Michal Nazarewicz wrote:\n>\n>> From: Michal Nazarewicz <mina86@mina86.com>\n>> \n>> The command_close_bidi_pipe() function will insist on closing both\n>> input and output pipes returned by command_bidi_pipe().  With this\n>> change it is possible to close one of the pipes in advance and\n>> pass undef as an argument.\n>> \n>> This allows for something like:\n>> \n>>   my ($pid, $in, $out, $ctx) = command_bidi_pipe(...);\n>>   print $out \"write data\";\n>>   close $out;\n>>   # ... do stuff with $in\n>>   command_close_bidi_pipe($pid, $in, undef, $ctx);\n>\n> Should this part go into the documentation for command_close_bidi_pipe\n> in Git.pm?\n\nYeah, it probably should.\n"},{"id":"208897","messageId":"vpqvca4vbe9.fsf@grenoble-inp.fr","threadId":"32844","inReplyTo":"f4b5413b0a55474346daa7b0866c7a4fed55778d.1360183427.git.mina86@mina86.com","subject":"Re: [PATCH 3/4] Git.pm: Add interface for git credential command.","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2013-02-07T07:19:42Z","receivedAt":"2013-02-07T07:19:42Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Michal Nazarewicz <mpn@google.com> writes:\n\n> From: Michal Nazarewicz <mina86@mina86.com>\n>\n> Add a credential() function which is an interface to the\n> git credential command.\n\nNice.\n\nI think you should credit git-remote-mediawiki for the code in the\ncommit message. Perhaps have a first \"copy/paste\" commit, and then an\n\"adaptation\" commit to add sort, ^ anchor in regexp, doc and your\ncallback mechanism, but I won't insist on that.\n\nOther than that, it all looks good, thanks. I'll take care of deleting\nthe old code in git-remote-mediawiki and use Git.pm instead.\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"},{"id":"208898","messageId":"vpqpq0cvb92.fsf@grenoble-inp.fr","threadId":"32844","inReplyTo":"f4b5413b0a55474346daa7b0866c7a4fed55778d.1360183427.git.mina86@mina86.com","subject":"Re: [PATCH 3/4] Git.pm: Add interface for git credential command.","fromName":"Matthieu Moy","fromEmail":"matthieu.moy@grenoble-inp.fr","sentAt":"2013-02-07T07:22:49Z","receivedAt":"2013-02-07T07:22:49Z","isPatch":true,"sender":{"key":"matthieu.moy@grenoble-inp.fr","avatar":"https://gravatar.com/avatar/72c8a2705971a25dfaff23cece15130d405685845d911aedd5667ace277f3fc5?d=mp&s=160"},"body":"Michal Nazarewicz <mpn@google.com> writes:\n\n> Subject: [PATCH 3/4] Git.pm: Add interface for git credential command.\n\nAh, just a nitpick: usually we write the message without capital after\n\":\" and without the final \".\".\n\n-- \nMatthieu Moy\nhttp://www-verimag.imag.fr/~moy/\n"}]}