The --sleep option provides a means for specifying that there should be a certain number of seconds of delay after sending a certain number of emails; see Documentation/git-send-email.txt Signed-off-by: Michael Witten <mfwitten@xxxxxxxxx> --- Documentation/git-send-email.txt | 30 +++++++++++++++ git-send-email.perl | 74 +++++++++++++++++++++++++++++++++++--- 2 files changed, 99 insertions(+), 5 deletions(-) diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt index 12845d6..f0c2e7b 100644 --- a/Documentation/git-send-email.txt +++ b/Documentation/git-send-email.txt @@ -168,6 +168,36 @@ Automating entire patch series. Default is the value of the 'sendemail.chainreplyto' configuration value; if that is unspecified, default to --chain-reply-to. +--sleep=<seconds>[,<burst>]:: + This option specfies that send-email should sleep for <seconds> + after sending <burst> messages as quickly as possible; <seconds> + should be an integer >= 0 and <burst> should be an integer >= 1. + This mode of operation attacks 2 problems: email throttling and + arrival disorder. Default is the value of the 'sendemail.sleep' + configuration variable, or '0' if that does not exist. ++ +By default, send-email tries to send one patch per email as quickly as +possible. Unfortunately, some email services restrict a user by refusing +to send more than some maximum number of email messages, M, in a given +period of seconds, S. This can be troublesome if the patch series has +more than M patches, because the server will ultimately refuse to send +some of them. In this case, simply pass '--sleep=S,M' or '--sleep S,M' +or set sendemail.sleep to 'S,M'. ++ +Moreover, the emails often arrive at the final destination out of order; +though send-email manipulates the date fields and usually chains subsequent +emails via the In-Reply-To headers, some mail viewers nevertheless insist +on presenting them by order of arrival. This may be mitigated by using +something like '--sleep 60' (the equivalent of '--sleep 60,1'), so that +there is a 60 second delay between sending each patch. ++ +*Note*: Because of varying routes and batching schemes, there is no delay +that can guarantee the correct arrival order. Obviously, one solution is to +choose an obscenely large number, so be prepared to run send-email in the +background. Of course, spreading emails across time makes it more likely +that unrelated email messages arrive between patches. Therefore, send-email +warns you if both --sleep and --no-chain-reply-to are used. + --identity=<id>:: A configuration identity. When given, causes values in the 'sendemail.<id>' subsection to take precedence over diff --git a/git-send-email.perl b/git-send-email.perl index 4f0462f..c26a1b5 100755 --- a/git-send-email.perl +++ b/git-send-email.perl @@ -77,6 +77,7 @@ git send-email [options] <file | directory | rev-list options > --smtp-ssl * Deprecated. Use '--smtp-encryption ssl'. Automating: + --sleep <secs>[,<burst>] * Delay <secs> every <burst> email(s). --identity <id> * Use the sendemail.<id> options. --cc-cmd <command> * Email Cc: via `<command> \$patch_path` --suppress-cc <category> * author, self, sob, cc, cccmd, body, @@ -194,7 +195,7 @@ sub do_edit { } # Variables with corresponding config settings -my ($thread, $chain_reply_to, $suppress_from, $signed_off_by_cc, $cc_cmd); +my ($sleep, $thread, $chain_reply_to, $suppress_from, $signed_off_by_cc, $cc_cmd); my ($smtp_server, $smtp_server_port, $smtp_authuser, $smtp_encryption); my ($identity, $aliasfiletype, @alias_files, @smtp_host_parts); my ($validate, $confirm); @@ -210,6 +211,7 @@ my %config_bool_settings = ( ); my %config_settings = ( + "sleep" => \$sleep, "smtpserver" => \$smtp_server, "smtpserverport" => \$smtp_server_port, "smtpuser" => \$smtp_authuser, @@ -262,6 +264,7 @@ my $rc = GetOptions( "cc=s" => \@initial_cc, "bcc=s" => \@bcclist, "chain-reply-to!" => \$chain_reply_to, + "sleep=s" => \$sleep, "smtp-server=s" => \$smtp_server, "smtp-server-port=i" => \$smtp_server_port, "smtp-user=s" => \$smtp_authuser, @@ -334,6 +337,43 @@ foreach my $setting (values %config_bool_settings) { ${$setting->[0]} = $setting->[1] unless (defined (${$setting->[0]})); } +#### Parse input + +my ($sleep_seconds, $sleep_burst); + +if (defined $sleep) {{ + + unless ($chain_reply_to) { + + print "Both --sleep and --no-chain-reply-to are in effect.\n"; + print "Therefore, it is much more likely that unrelated\n"; + print "email messages will appear between any 2 of your\n"; + print "patches.\n\n"; + + $_ = ask( + "How to proceed? ([q]uit | --[s]leep | --no-[c]hain | [n]either | [b]oth): ", + valid_re => qr/^(?:b|s|c|n|q)/i, + default => 'b' + ); + + /^b/ or + /^s/ and $chain_reply_to = 1 or + /^c/ and $sleep = undef, last or + /^n/ and $chain_reply_to = 1, $sleep = undef, last or + /^q/ and exit; + } + + $sleep =~ /^(\d+)(?:,(\d+))?$/ + or print "Should be '--sleep=<seconds>[,<burst>]', but got '--sleep=\"$sleep\"'\n" + and exit; + + # Explicitly convert to integers to avoid repeated conversion: + # (<burst> = 0 is not defined, but let's be nice and absorb it) + + $sleep_seconds = 0+$1; + $sleep_burst = $2 ? 0+$2 : 1; +}} + # 'default' encryption is none -- this only prevents a warning $smtp_encryption = '' unless (defined $smtp_encryption); @@ -1041,8 +1081,12 @@ $references = $initial_reply_to || ''; $subject = $initial_subject; $message_num = 0; -foreach my $t (@files) { - open(F,"<",$t) or die "can't open file $t"; +my $burst_count = $sleep_burst; +my $time_of_last_message; + +for (my $index = 0; $index < @files; $index++) { + my $file = $files[$index]; + open(F,"<",$file) or die "can't open file $file"; my $author = undef; my $author_encoding; @@ -1151,7 +1195,7 @@ foreach my $t (@files) { close F; if (defined $cc_cmd && !$suppress_cc{'cccmd'}) { - open(F, "$cc_cmd $t |") + open(F, "$cc_cmd $file |") or die "(cc-cmd) Could not execute '$cc_cmd'"; while(<F>) { my $c = $_; @@ -1198,7 +1242,27 @@ foreach my $t (@files) { my $message_was_sent = send_message(); - # set up for the next message + # Throttle the outgoing rate: + + if ($sleep_seconds && $message_was_sent) { + + $time_of_last_message = time; + + unless (--$burst_count) { # unless we can send more + + $burst_count = $sleep_burst; + + my $already_elapsed = time - $time_of_last_message; + + if ($already_elapsed < $sleep_seconds && $index < $#files) { + my $this_long = $sleep_seconds - $already_elapsed; + while (($this_long -= sleep $this_long) > 0) {} + } + } + } + + # set up for the next message: + if ($message_was_sent and $chain_reply_to || not defined $reply_to || length($reply_to) == 0) { $reply_to = $message_id; if (length $references > 0) { -- 1.6.2.2.479.g2aec -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html