Merge branch 'ab/perl-fixes' into next
authorJunio C Hamano <gitster@pobox.com>
Fri, 9 Mar 2018 21:13:38 +0000 (13:13 -0800)
committerJunio C Hamano <gitster@pobox.com>
Fri, 9 Mar 2018 21:13:38 +0000 (13:13 -0800)
Clean-up to various pieces of Perl code we have.

* ab/perl-fixes:
perl Git::LoadCPAN: emit better errors under NO_PERL_CPAN_FALLBACKS
Makefile: add NO_PERL_CPAN_FALLBACKS knob
perl: move the perl/Git/FromCPAN tree to perl/FromCPAN
perl: generalize the Git::LoadCPAN facility
perl: move CPAN loader wrappers to another namespace
perl: update our copy of Mail::Address
perl: update our ancient copy of Error.pm
git-send-email: unconditionally use Net::{SMTP,Domain}
Git.pm: hard-depend on the File::{Temp,Spec} modules
gitweb: hard-depend on the Digest::MD5 5.8 module
Git.pm: add the "use warnings" pragma
Git.pm: remove redundant "use strict" from sub-package
perl: *.pm files should not have the executable bit

1  2 
Makefile
git-send-email.perl
perl/Git.pm
diff --combined Makefile
index de4b8f0c02e2119f7252ff9e6b6c6d479e664a36,4e0cdb3ca4a3c722c12b7219c72c978dddac45bd..a1d8775adb4b38a0340cd7d04184915f0ee65d28
+++ b/Makefile
@@@ -296,6 -296,12 +296,12 @@@ all:
  #
  # Define NO_PERL if you do not want Perl scripts or libraries at all.
  #
+ # Define NO_PERL_CPAN_FALLBACKS if you do not want to install bundled
+ # copies of CPAN modules that serve as a fallback in case the modules
+ # are not available on the system. This option is intended for
+ # distributions that want to use their packaged versions of Perl
+ # modules, instead of the fallbacks shipped with Git.
+ #
  # Define PYTHON_PATH to the path of your Python binary (often /usr/bin/python
  # but /usr/bin/python2.7 on some platforms).
  #
@@@ -2162,8 -2168,6 +2168,8 @@@ gettext.sp gettext.s gettext.o: EXTRA_C
  http-push.sp http.sp http-walker.sp remote-curl.sp imap-send.sp: SPARSE_FLAGS += \
        -DCURL_DISABLE_TYPECHECK
  
 +pack-revindex.sp: SPARSE_FLAGS += -Wno-memcpy-max-count
 +
  ifdef NO_EXPAT
  http-walker.sp http-walker.s http-walker.o: EXTRA_CPPFLAGS = -DNO_EXPAT
  endif
@@@ -2218,15 -2222,13 +2224,15 @@@ $(VCSSVN_LIB): $(VCSSVN_OBJS
  
  export DEFAULT_EDITOR DEFAULT_PAGER
  
 -.PHONY: doc man html info pdf
 -doc:
 +.PHONY: doc man man-perl html info pdf
 +doc: man-perl
        $(MAKE) -C Documentation all
  
 -man:
 +man: man-perl
        $(MAKE) -C Documentation man
  
 +man-perl: perl/build/man/man3/Git.3pm
 +
  html:
        $(MAKE) -C Documentation html
  
@@@ -2304,14 -2306,22 +2310,22 @@@ po/build/locale/%/LC_MESSAGES/git.mo: p
  
  LIB_PERL := $(wildcard perl/Git.pm perl/Git/*.pm perl/Git/*/*.pm perl/Git/*/*/*.pm)
  LIB_PERL_GEN := $(patsubst perl/%.pm,perl/build/lib/%.pm,$(LIB_PERL))
+ LIB_CPAN := $(wildcard perl/FromCPAN/*.pm perl/FromCPAN/*/*.pm)
+ LIB_CPAN_GEN := $(patsubst perl/%.pm,perl/build/lib/%.pm,$(LIB_CPAN))
  
  ifndef NO_PERL
  all:: $(LIB_PERL_GEN)
+ ifndef NO_PERL_CPAN_FALLBACKS
+ all:: $(LIB_CPAN_GEN)
+ endif
+ NO_PERL_CPAN_FALLBACKS_SQ = $(subst ','\'',$(NO_PERL_CPAN_FALLBACKS))
  endif
  
  perl/build/lib/%.pm: perl/%.pm
        $(QUIET_GEN)mkdir -p $(dir $@) && \
-       sed -e 's|@@LOCALEDIR@@|$(localedir_SQ)|g' < $< > $@
+       sed -e 's|@@LOCALEDIR@@|$(localedir_SQ)|g' \
+           -e 's|@@NO_PERL_CPAN_FALLBACKS@@|$(NO_PERL_CPAN_FALLBACKS_SQ)|g' \
+       < $< > $@
  
  perl/build/man/man3/Git.3pm: perl/Git.pm
        $(QUIET_GEN)mkdir -p $(dir $@) && \
@@@ -2624,7 -2634,7 +2638,7 @@@ endi
        done && \
        ./check_bindir "z$$bindir" "z$$execdir" "$$bindir/git-add$X"
  
 -.PHONY: install-gitweb install-doc install-man install-html install-info install-pdf
 +.PHONY: install-gitweb install-doc install-man install-man-perl install-html install-info install-pdf
  .PHONY: quick-install-doc quick-install-man quick-install-html
  install-gitweb:
        $(MAKE) -C gitweb install
@@@ -2635,7 -2645,7 +2649,7 @@@ install-doc: install-man-per
  install-man: install-man-perl
        $(MAKE) -C Documentation install-man
  
 -install-man-perl: perl/build/man/man3/Git.3pm
 +install-man-perl: man-perl
        $(INSTALL) -d -m 755 '$(DESTDIR_SQ)$(mandir_SQ)/man3'
        (cd perl/build/man/man3 && $(TAR) cf - .) | \
        (cd '$(DESTDIR_SQ)$(mandir_SQ)/man3' && umask 022 && $(TAR) xof -)
@@@ -2738,7 -2748,7 +2752,7 @@@ clean: profile-clean coverage-clea
        $(RM) $(TEST_PROGRAMS) $(NO_INSTALL)
        $(RM) -r bin-wrappers $(dep_dirs)
        $(RM) -r po/build/
 -      $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo common-cmds.h $(ETAGS_TARGET) tags cscope*
 +      $(RM) *.pyc *.pyo */*.pyc */*.pyo common-cmds.h $(ETAGS_TARGET) tags cscope*
        $(RM) -r $(GIT_TARNAME) .doc-tmp-dir
        $(RM) $(GIT_TARNAME).tar.gz git-core_$(GIT_VERSION)-*.tar.gz
        $(RM) $(htmldocs).tar.gz $(manpages).tar.gz
diff --combined git-send-email.perl
index 3a68aae060226e6497d6a0e7d936efca95844f93,1ec22c5ef3418bdba25a7654f782748813c60541..2fa7818ca9a8ac7363d17aef17b864f7361b3eb8
@@@ -26,11 -26,13 +26,13 @@@ use Text::ParseWords
  use Term::ANSIColor;
  use File::Temp qw/ tempdir tempfile /;
  use File::Spec::Functions qw(catdir catfile);
- use Git::Error qw(:try);
+ use Git::LoadCPAN::Error qw(:try);
  use Cwd qw(abs_path cwd);
  use Git;
  use Git::I18N;
- use Git::Mail::Address;
+ use Net::Domain ();
+ use Net::SMTP ();
+ use Git::LoadCPAN::Mail::Address;
  
  Getopt::Long::Configure qw/ pass_through /;
  
@@@ -57,7 -59,6 +59,7 @@@ git send-email --dump-aliase
      --[no-]cc               <str>  * Email Cc:
      --[no-]bcc              <str>  * Email Bcc:
      --subject               <str>  * Email "Subject:"
 +    --reply-to              <str>  * Email "Reply-To:"
      --in-reply-to           <str>  * Email "In-Reply-To:"
      --[no-]xmailer                 * Add "X-Mailer:" header (default).
      --[no-]annotate                * Review each patch that will be sent in an editor.
@@@ -168,13 -169,13 +170,13 @@@ my $re_encoded_word = qr/=\?($re_token)
  
  # Variables we fill in automatically, or via prompting:
  my (@to,$no_to,@initial_to,@cc,$no_cc,@initial_cc,@bcclist,$no_bcc,@xh,
 -      $initial_reply_to,$initial_subject,@files,
 +      $initial_in_reply_to,$reply_to,$initial_subject,@files,
        $author,$sender,$smtp_authpass,$annotate,$use_xmailer,$compose,$time);
  
  my $envelope_sender;
  
  # Example reply to:
 -#$initial_reply_to = ''; #<20050203173208.GA23964@foobar.com>';
 +#$initial_in_reply_to = ''; #<20050203173208.GA23964@foobar.com>';
  
  my $repo = eval { Git->repository() };
  my @repo = $repo ? ($repo) : ();
@@@ -316,8 -317,7 +318,8 @@@ die __("--dump-aliases incompatible wit
      if !$help and $dump_aliases and @ARGV;
  $rc = GetOptions(
                    "sender|from=s" => \$sender,
 -                    "in-reply-to=s" => \$initial_reply_to,
 +                    "in-reply-to=s" => \$initial_in_reply_to,
 +                  "reply-to=s" => \$reply_to,
                    "subject=s" => \$initial_subject,
                    "to=s" => \@initial_to,
                    "to-cmd=s" => \$to_cmd,
@@@ -381,10 -381,6 +383,10 @@@ unless ($rc) 
  die __("Cannot run git format-patch from outside a repository\n")
        if $format_patch and not $repo;
  
 +die __("`batch-size` and `relogin` must be specified together " .
 +      "(via command-line or configuration option)\n")
 +      if defined $relogin_delay and not defined $batch_size;
 +
  # Now, let's fill any that aren't set in with defaults:
  
  sub read_config {
@@@ -683,8 -679,7 +685,8 @@@ if ($compose) 
  
        my $tpl_sender = $sender || $repoauthor || $repocommitter || '';
        my $tpl_subject = $initial_subject || '';
 -      my $tpl_reply_to = $initial_reply_to || '';
 +      my $tpl_in_reply_to = $initial_in_reply_to || '';
 +      my $tpl_reply_to = $reply_to || '';
  
        print $c <<EOT1, Git::prefix_lines("GIT: ", __ <<EOT2), <<EOT3;
  From $tpl_sender # This line is ignored.
@@@ -696,9 -691,8 +698,9 @@@ for the patch you are writing
  Clear the body content if you don't wish to send a summary.
  EOT2
  From: $tpl_sender
 +Reply-To: $tpl_reply_to
  Subject: $tpl_subject
 -In-Reply-To: $tpl_reply_to
 +In-Reply-To: $tpl_in_reply_to
  
  EOT3
        for my $f (@files) {
                do_edit($compose_filename);
        }
  
 -      open my $c2, ">", $compose_filename . ".final"
 -              or die sprintf(__("Failed to open %s.final: %s"), $compose_filename, $!);
 -
        open $c, "<", $compose_filename
                or die sprintf(__("Failed to open %s: %s"), $compose_filename, $!);
  
 -      my $need_8bit_cte = file_has_nonascii($compose_filename);
 -      my $in_body = 0;
 -      my $summary_empty = 1;
        if (!defined $compose_encoding) {
                $compose_encoding = "UTF-8";
        }
 -      while(<$c>) {
 -              next if m/^GIT:/;
 -              if ($in_body) {
 -                      $summary_empty = 0 unless (/^\n$/);
 -              } elsif (/^\n$/) {
 -                      $in_body = 1;
 -                      if ($need_8bit_cte) {
 -                              print $c2 "MIME-Version: 1.0\n",
 -                                       "Content-Type: text/plain; ",
 -                                         "charset=$compose_encoding\n",
 -                                       "Content-Transfer-Encoding: 8bit\n";
 -                      }
 -              } elsif (/^MIME-Version:/i) {
 -                      $need_8bit_cte = 0;
 -              } elsif (/^Subject:\s*(.+)\s*$/i) {
 -                      $initial_subject = $1;
 -                      my $subject = $initial_subject;
 -                      $_ = "Subject: " .
 -                              quote_subject($subject, $compose_encoding) .
 -                              "\n";
 -              } elsif (/^In-Reply-To:\s*(.+)\s*$/i) {
 -                      $initial_reply_to = $1;
 -                      next;
 -              } elsif (/^From:\s*(.+)\s*$/i) {
 -                      $sender = $1;
 -                      next;
 -              } elsif (/^(?:To|Cc|Bcc):/i) {
 -                      print __("To/Cc/Bcc fields are not interpreted yet, they have been ignored\n");
 -                      next;
 +
 +      my %parsed_email;
 +      while (my $line = <$c>) {
 +              next if $line =~ m/^GIT:/;
 +              parse_header_line($line, \%parsed_email);
 +              if ($line =~ /^$/) {
 +                      $parsed_email{'body'} = filter_body($c);
                }
 -              print $c2 $_;
        }
        close $c;
 -      close $c2;
  
 -      if ($summary_empty) {
 +      open my $c2, ">", $compose_filename . ".final"
 +      or die sprintf(__("Failed to open %s.final: %s"), $compose_filename, $!);
 +
 +
 +      if ($parsed_email{'From'}) {
 +              $sender = delete($parsed_email{'From'});
 +      }
 +      if ($parsed_email{'In-Reply-To'}) {
 +              $initial_in_reply_to = delete($parsed_email{'In-Reply-To'});
 +      }
 +      if ($parsed_email{'Reply-To'}) {
 +              $reply_to = delete($parsed_email{'Reply-To'});
 +      }
 +      if ($parsed_email{'Subject'}) {
 +              $initial_subject = delete($parsed_email{'Subject'});
 +              print $c2 "Subject: " .
 +                      quote_subject($initial_subject, $compose_encoding) .
 +                      "\n";
 +      }
 +
 +      if ($parsed_email{'MIME-Version'}) {
 +              print $c2 "MIME-Version: $parsed_email{'MIME-Version'}\n",
 +                              "Content-Type: $parsed_email{'Content-Type'};\n",
 +                              "Content-Transfer-Encoding: $parsed_email{'Content-Transfer-Encoding'}\n";
 +              delete($parsed_email{'MIME-Version'});
 +              delete($parsed_email{'Content-Type'});
 +              delete($parsed_email{'Content-Transfer-Encoding'});
 +      } elsif (file_has_nonascii($compose_filename)) {
 +              my $content_type = (delete($parsed_email{'Content-Type'}) or
 +                      "text/plain; charset=$compose_encoding");
 +              print $c2 "MIME-Version: 1.0\n",
 +                      "Content-Type: $content_type\n",
 +                      "Content-Transfer-Encoding: 8bit\n";
 +      }
 +      # Preserve unknown headers
 +      foreach my $key (keys %parsed_email) {
 +              next if $key eq 'body';
 +              print $c2 "$key: $parsed_email{$key}";
 +      }
 +
 +      if ($parsed_email{'body'}) {
 +              print $c2 "\n$parsed_email{'body'}\n";
 +              delete($parsed_email{'body'});
 +      } else {
                print __("Summary email is empty, skipping it\n");
                $compose = -1;
        }
 +
 +      close $c2;
 +
  } elsif ($annotate) {
        do_edit(@files);
  }
@@@ -817,32 -795,6 +819,32 @@@ sub ask 
        return;
  }
  
 +sub parse_header_line {
 +      my $lines = shift;
 +      my $parsed_line = shift;
 +      my $addr_pat = join "|", qw(To Cc Bcc);
 +
 +      foreach (split(/\n/, $lines)) {
 +              if (/^($addr_pat):\s*(.+)$/i) {
 +                      $parsed_line->{$1} = [ parse_address_line($2) ];
 +              } elsif (/^([^:]*):\s*(.+)\s*$/i) {
 +                      $parsed_line->{$1} = $2;
 +              }
 +      }
 +}
 +
 +sub filter_body {
 +      my $c = shift;
 +      my $body = "";
 +      while (my $body_line = <$c>) {
 +              if ($body_line !~ m/^GIT:/) {
 +                      $body .= $body_line;
 +              }
 +      }
 +      return $body;
 +}
 +
 +
  my %broken_encoding;
  
  sub file_declares_8bit_cte {
@@@ -923,22 -875,16 +925,22 @@@ sub expand_one_alias 
  @initial_cc = process_address_list(@initial_cc);
  @bcclist = process_address_list(@bcclist);
  
 -if ($thread && !defined $initial_reply_to && $prompting) {
 -      $initial_reply_to = ask(
 +if ($thread && !defined $initial_in_reply_to && $prompting) {
 +      $initial_in_reply_to = ask(
                __("Message-ID to be used as In-Reply-To for the first email (if any)? "),
                default => "",
                valid_re => qr/\@.*\./, confirm_only => 1);
  }
 -if (defined $initial_reply_to) {
 -      $initial_reply_to =~ s/^\s*<?//;
 -      $initial_reply_to =~ s/>?\s*$//;
 -      $initial_reply_to = "<$initial_reply_to>" if $initial_reply_to ne '';
 +if (defined $initial_in_reply_to) {
 +      $initial_in_reply_to =~ s/^\s*<?//;
 +      $initial_in_reply_to =~ s/>?\s*$//;
 +      $initial_in_reply_to = "<$initial_in_reply_to>" if $initial_in_reply_to ne '';
 +}
 +
 +if (defined $reply_to) {
 +      $reply_to =~ s/^\s+|\s+$//g;
 +      ($reply_to) = expand_aliases($reply_to);
 +      $reply_to = sanitize_address($reply_to);
  }
  
  if (!defined $smtp_server) {
@@@ -958,7 -904,7 +960,7 @@@ if ($compose && $compose > 0) 
  }
  
  # Variables we set as part of the loop over files
 -our ($message_id, %mail, $subject, $reply_to, $references, $message,
 +our ($message_id, %mail, $subject, $in_reply_to, $references, $message,
        $needs_confirm, $message_num, $ask_default);
  
  sub extract_valid_address {
@@@ -1199,10 -1145,8 +1201,8 @@@ sub valid_fqdn 
  sub maildomain_net {
        my $maildomain;
  
-       if (eval { require Net::Domain; 1 }) {
-               my $domain = Net::Domain::domainname();
-               $maildomain = $domain if valid_fqdn($domain);
-       }
+       my $domain = Net::Domain::domainname();
+       $maildomain = $domain if valid_fqdn($domain);
  
        return $maildomain;
  }
  sub maildomain_mta {
        my $maildomain;
  
-       if (eval { require Net::SMTP; 1 }) {
-               for my $host (qw(mailhost localhost)) {
-                       my $smtp = Net::SMTP->new($host);
-                       if (defined $smtp) {
-                               my $domain = $smtp->domain;
-                               $smtp->quit;
+       for my $host (qw(mailhost localhost)) {
+               my $smtp = Net::SMTP->new($host);
+               if (defined $smtp) {
+                       my $domain = $smtp->domain;
+                       $smtp->quit;
  
-                               $maildomain = $domain if valid_fqdn($domain);
+                       $maildomain = $domain if valid_fqdn($domain);
  
-                               last if $maildomain;
-                       }
+                       last if $maildomain;
                }
        }
  
@@@ -1367,14 -1309,11 +1365,14 @@@ Message-Id: $message_i
        if ($use_xmailer) {
                $header .= "X-Mailer: git-send-email $gitversion\n";
        }
 -      if ($reply_to) {
 +      if ($in_reply_to) {
  
 -              $header .= "In-Reply-To: $reply_to\n";
 +              $header .= "In-Reply-To: $in_reply_to\n";
                $header .= "References: $references\n";
        }
 +      if ($reply_to) {
 +              $header .= "Reply-To: $reply_to\n";
 +      }
        if (@xh) {
                $header .= join("\n", @xh) . "\n";
        }
@@@ -1549,8 -1488,8 +1547,8 @@@ EO
        return 1;
  }
  
 -$reply_to = $initial_reply_to;
 -$references = $initial_reply_to || '';
 +$in_reply_to = $initial_in_reply_to;
 +$references = $initial_in_reply_to || '';
  $subject = $initial_subject;
  $message_num = 0;
  
@@@ -1760,9 -1699,9 +1758,9 @@@ foreach my $t (@files) 
  
        # set up for the next message
        if ($thread && $message_was_sent &&
 -              ($chain_reply_to || !defined $reply_to || length($reply_to) == 0 ||
 +              ($chain_reply_to || !defined $in_reply_to || length($in_reply_to) == 0 ||
                $message_num == 1)) {
 -              $reply_to = $message_id;
 +              $in_reply_to = $message_id;
                if (length $references > 0) {
                        $references .= "\n $message_id";
                } else {
diff --combined perl/Git.pm
index a7440a1f096aa193a21688414669300425146382,9f246c7988f5ff43ecf8ea3465387a526a66e6f4..16ebcc612ce4acb4fba6511d5b388184934cb22a
@@@ -9,7 -9,10 +9,10 @@@ package Git
  
  use 5.008;
  use strict;
+ use warnings;
  
+ use File::Temp ();
+ use File::Spec ();
  
  BEGIN {
  
@@@ -101,7 -104,7 +104,7 @@@ increase notwithstanding)
  
  
  use Carp qw(carp croak); # but croak is bad - throw instead
- use Git::Error qw(:try);
+ use Git::LoadCPAN::Error qw(:try);
  use Cwd qw(abs_path cwd);
  use IPC::Open2 qw(open2);
  use Fcntl qw(SEEK_SET SEEK_CUR);
@@@ -189,7 -192,6 +192,6 @@@ sub repository 
                };
  
                if ($dir) {
-                       _verify_require();
                        File::Spec->file_name_is_absolute($dir) or $dir = $opts{Directory} . '/' . $dir;
                        $opts{Repository} = abs_path($dir);
  
@@@ -534,9 -536,7 +536,9 @@@ If TIME is not supplied, the current lo
  sub get_tz_offset {
        # some systems don't handle or mishandle %z, so be creative.
        my $t = shift || time;
 -      my $gm = timegm(localtime($t));
 +      my @t = localtime($t);
 +      $t[5] += 1900;
 +      my $gm = timegm(@t);
        my $sign = qw( + + - )[ $gm <=> $t ];
        return sprintf("%s%02d%02d", $sign, (gmtime(abs($t - $gm)))[2,1]);
  }
@@@ -1290,8 -1290,6 +1292,6 @@@ sub temp_release 
  sub _temp_cache {
        my ($self, $name) = _maybe_self(@_);
  
-       _verify_require();
        my $temp_fd = \$TEMP_FILEMAP{$name};
        if (defined $$temp_fd and $$temp_fd->opened) {
                if ($TEMP_FILES{$$temp_fd}{locked}) {
        $$temp_fd;
  }
  
- sub _verify_require {
-       eval { require File::Temp; require File::Spec; };
-       $@ and throw Error::Simple($@);
- }
  =item temp_reset ( FILEHANDLE )
  
  Truncates and resets the position of the C<FILEHANDLE>.
@@@ -1694,7 -1687,6 +1689,6 @@@ sub DESTROY 
  # Pipe implementation for ActiveState Perl.
  
  package Git::activestate_pipe;
- use strict;
  
  sub TIEHANDLE {
        my ($class, @params) = @_;