exportcommit: replace backticks with safe_pipe_capture() or system() - initial pass
[git.git] / git-format-patch.sh
index 9b40880..5fb8ce1 100755 (executable)
@@ -3,31 +3,28 @@
 # Copyright (c) 2005 Junio C Hamano
 #
 
 # Copyright (c) 2005 Junio C Hamano
 #
 
-. git-sh-setup
-
-usage () {
-    echo >&2 "usage: $0"' [-n] [-o dir | --stdout] [--keep-subject] [--mbox]
-    [--check] [--signoff] [-<diff options>...]
-    [--help]
-    ( from..to ... | upstream [ our-head ] )
-
-Prepare each commit with its patch since our-head forked from upstream,
+USAGE='[-n | -k] [-o <dir> | --stdout] [--signoff] [--check] [--mbox] [--diff-options] <upstream> [<our-head>]'
+LONG_USAGE='Prepare each commit with its patch since our-head forked from upstream,
 one file per patch, for e-mail submission.  Each output file is
 numbered sequentially from 1, and uses the first line of the commit
 message (massaged for pathname safety) as the filename.
 
 one file per patch, for e-mail submission.  Each output file is
 numbered sequentially from 1, and uses the first line of the commit
 message (massaged for pathname safety) as the filename.
 
-When -o is specified, output files are created in that directory; otherwise in
-the current working directory.
+There are three output modes.  By default, output files are created in
+the current working directory; when -o is specified, they are created
+in that directory instead; when --stdout is specified, they are spit
+on standard output, and can be piped to git-am.
 
 When -n is specified, instead of "[PATCH] Subject", the first line is formatted
 as "[PATCH N/M] Subject", unless you have only one patch.
 
 When --mbox is specified, the output is formatted to resemble
 UNIX mailbox format, and can be concatenated together for processing
 
 When -n is specified, instead of "[PATCH] Subject", the first line is formatted
 as "[PATCH N/M] Subject", unless you have only one patch.
 
 When --mbox is specified, the output is formatted to resemble
 UNIX mailbox format, and can be concatenated together for processing
-with applymbox.
-'
-    exit 1
-}
+with applymbox.'
+. git-sh-setup
+
+# Force diff to run in C locale.
+LANG=C LC_ALL=C
+export LANG LC_ALL
 
 diff_opts=
 LF='
 
 diff_opts=
 LF='
@@ -37,14 +34,12 @@ outdir=./
 while case "$#" in 0) break;; esac
 do
     case "$1" in
 while case "$#" in 0) break;; esac
 do
     case "$1" in
-    -a|--a|--au|--aut|--auth|--autho|--author)
-    author=t ;;
     -c|--c|--ch|--che|--chec|--check)
     check=t ;;
     -c|--c|--ch|--che|--chec|--check)
     check=t ;;
-    -d|--d|--da|--dat|--date)
-    date=t ;;
-    -m|--m|--mb|--mbo|--mbox)
-    date=t author=t mbox=t ;;
+    -a|--a|--au|--aut|--auth|--autho|--author|\
+    -d|--d|--da|--dat|--date|\
+    -m|--m|--mb|--mbo|--mbox) # now noop
+    ;;
     -k|--k|--ke|--kee|--keep|--keep-|--keep-s|--keep-su|--keep-sub|\
     --keep-subj|--keep-subje|--keep-subjec|--keep-subject)
     keep_subject=t ;;
     -k|--k|--ke|--kee|--keep|--keep-|--keep-s|--keep-su|--keep-sub|\
     --keep-subj|--keep-subje|--keep-subjec|--keep-subject)
     keep_subject=t ;;
@@ -176,81 +171,101 @@ titleScript='
        q
 '
 
        q
 '
 
-whosepatchScript='
-/^author /{
-       s/author \(.*>\) \(.*\)$/au='\''\1'\'' ad='\''\2'\''/p
-       q
-}'
-
 process_one () {
 process_one () {
-       mailScript='
-       /./d
-       /^$/n'
-       case "$keep_subject" in
-       t)  ;;
-       *)
-           mailScript="$mailScript"'
-           s|^\[PATCH[^]]*\] *||
-           s|^|[PATCH'"$num"'] |'
-           ;;
-       esac
-       mailScript="$mailScript"'
-       s|^|Subject: |'
-       case "$mbox" in
-       t)
-           echo 'From nobody Mon Sep 17 00:00:00 2001' ;# UNIX "From" line
-           ;;
-       esac
+       perl -w -e '
+my ($keep_subject, $num, $signoff, $commsg) = @ARGV;
+my ($signoff_pattern, $done_header, $done_subject, $signoff_seen,
+    $last_was_signoff);
 
 
-       eval "$(LANG=C LC_ALL=C sed -ne "$whosepatchScript" $commsg)"
-       test "$author,$au" = ",$me" || {
-               mailScript="$mailScript"'
-       a\
-From: '"$au"
-       }
-       test "$date,$au" = ",$me" || {
-               mailScript="$mailScript"'
-       a\
-Date: '"$ad"
-       }
+if ($signoff) {
+       $signoff = "Signed-off-by: " . `git-var GIT_COMMITTER_IDENT`;
+       $signoff =~ s/>.*/>/;
+       $signoff_pattern = quotemeta($signoff);
+}
 
 
-       mailScript="$mailScript"'
-       : body
-       p
-       n
-       b body'
+my @weekday_names = qw(Sun Mon Tue Wed Thu Fri Sat);
+my @month_names = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
 
 
-       (cat $commsg ; echo; echo) |
-       sed -ne "$mailScript" |
-       git-stripspace
+sub show_date {
+    my ($time, $tz) = @_;
+    my $minutes = abs($tz);
+    $minutes = ($minutes / 100) * 60 + ($minutes % 100);
+    if ($tz < 0) {
+        $minutes = -$minutes;
+    }
+    my $t = $time + $minutes * 60;
+    my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = gmtime($t);
+    return sprintf("%s %s %d %02d:%02d:%02d %d %+05d",
+                  $weekday_names[$wday],
+                  $month_names[$mon],
+                  $mday, $hour, $min, $sec,
+                  $year+1900, $tz);
+}
 
 
-       test "$signoff" = "t" && {
-               offsigner=`git-var GIT_COMMITTER_IDENT | sed -e 's/>.*/>/'`
-               line="Signed-off-by: $offsigner"
-               grep -q "^$line\$" $commsg || {
-                       echo
-                       echo "$line"
-                       echo
-               }
+print "From nobody Mon Sep 17 00:00:00 2001\n";
+open FH, "git stripspace <$commsg |" or die "open $commsg pipe";
+while (<FH>) {
+    unless ($done_header) {
+       if (/^$/) {
+           $done_header = 1;
        }
        }
-       echo
-       echo '---'
-       echo
+       elsif (/^author (.*>) (.*)$/) {
+           my ($author_ident, $author_date) = ($1, $2);
+           my ($utc, $off) = ($author_date =~ /^(\d+) ([-+]?\d+)$/);
+           $author_date = show_date($utc, $off);
+
+           print "From: $author_ident\n";
+           print "Date: $author_date\n";
+       }
+       next;
+    }
+    unless ($done_subject) {
+       unless ($keep_subject) {
+           s/^\[PATCH[^]]*\]\s*//;
+           s/^/[PATCH$num] /;
+       }
+        print "Subject: $_";
+       $done_subject = 1;
+       next;
+    }
+
+    $last_was_signoff = 0;
+    if (/Signed-off-by:/i) {
+        if ($signoff ne "" && /Signed-off-by:\s*$signoff_pattern$/i) {
+           $signoff_seen = 1;
+       }
+    }
+    print $_;
+}
+if (!$signoff_seen && $signoff ne "") {
+    if (!$last_was_signoff) {
+        print "\n";
+    }
+    print "$signoff\n";
+}
+print "\n---\n\n";
+close FH or die "close $commsg pipe";
+' "$keep_subject" "$num" "$signoff" $commsg
+
        git-diff-tree -p $diff_opts "$commit" | git-apply --stat --summary
        echo
        git-diff-tree -p $diff_opts "$commit" | git-apply --stat --summary
        echo
-       git-cat-file commit "$commit^" | sed -e 's/^tree /applies-to: /' -e q
        git-diff-tree -p $diff_opts "$commit"
        git-diff-tree -p $diff_opts "$commit"
-       echo "---"
+       echo "-- "
        echo "@@GIT_VERSION@@"
 
        echo "@@GIT_VERSION@@"
 
-       case "$mbox" in
-       t)
-               echo
-               ;;
-       esac
+       echo
 }
 
 total=`wc -l <$series | tr -dc "[0-9]"`
 }
 
 total=`wc -l <$series | tr -dc "[0-9]"`
+case "$total,$numbered" in
+1,*)
+       numfmt='' ;;
+*,t)
+       numfmt=`echo "$total" | wc -c`
+       numfmt=$(($numfmt-1))
+       numfmt=" %0${numfmt}d/$total"
+esac
+
 i=1
 while read commit
 do
 i=1
 while read commit
 do
@@ -259,10 +274,7 @@ do
     case "$numbered" in
     '') num= ;;
     *)
     case "$numbered" in
     '') num= ;;
     *)
-       case $total in
-       1) num= ;;
-       *) num=' '`printf "%d/%d" $i $total` ;;
-       esac
+        num=`printf "$numfmt" $i` ;;
     esac
 
     file=`printf '%04d-%stxt' $i "$title"`
     esac
 
     file=`printf '%04d-%stxt' $i "$title"`