git-format-patch.shon commit checkout: automerge local changes while switching branches. (19205ac)
   1#!/bin/sh
   2#
   3# Copyright (c) 2005 Junio C Hamano
   4#
   5
   6USAGE='[-n | -k] [-o <dir> | --stdout] [--signoff] [--check] [--mbox] [--diff-options] <upstream> [<our-head>]'
   7LONG_USAGE='Prepare each commit with its patch since our-head forked from upstream,
   8one file per patch, for e-mail submission.  Each output file is
   9numbered sequentially from 1, and uses the first line of the commit
  10message (massaged for pathname safety) as the filename.
  11
  12There are three output modes.  By default, output files are created in
  13the current working directory; when -o is specified, they are created
  14in that directory instead; when --stdout is specified, they are spit
  15on standard output, and can be piped to git-am.
  16
  17When -n is specified, instead of "[PATCH] Subject", the first line is formatted
  18as "[PATCH N/M] Subject", unless you have only one patch.
  19
  20When --mbox is specified, the output is formatted to resemble
  21UNIX mailbox format, and can be concatenated together for processing
  22with applymbox.'
  23. git-sh-setup
  24
  25# Force diff to run in C locale.
  26LANG=C LC_ALL=C
  27export LANG LC_ALL
  28
  29diff_opts=
  30LF='
  31'
  32
  33outdir=./
  34while case "$#" in 0) break;; esac
  35do
  36    case "$1" in
  37    -a|--a|--au|--aut|--auth|--autho|--author)
  38    author=t ;;
  39    -c|--c|--ch|--che|--chec|--check)
  40    check=t ;;
  41    -d|--d|--da|--dat|--date)
  42    date=t ;;
  43    -m|--m|--mb|--mbo|--mbox)
  44    date=t author=t mbox=t ;;
  45    -k|--k|--ke|--kee|--keep|--keep-|--keep-s|--keep-su|--keep-sub|\
  46    --keep-subj|--keep-subje|--keep-subjec|--keep-subject)
  47    keep_subject=t ;;
  48    -n|--n|--nu|--num|--numb|--numbe|--number|--numbere|--numbered)
  49    numbered=t ;;
  50    -s|--s|--si|--sig|--sign|--signo|--signof|--signoff)
  51    signoff=t ;;
  52    --st|--std|--stdo|--stdou|--stdout)
  53    stdout=t mbox=t date=t author=t ;;
  54    -o=*|--o=*|--ou=*|--out=*|--outp=*|--outpu=*|--output=*|--output-=*|\
  55    --output-d=*|--output-di=*|--output-dir=*|--output-dire=*|\
  56    --output-direc=*|--output-direct=*|--output-directo=*|\
  57    --output-director=*|--output-directory=*)
  58    outdir=`expr "$1" : '-[^=]*=\(.*\)'` ;;
  59    -o|--o|--ou|--out|--outp|--outpu|--output|--output-|--output-d|\
  60    --output-di|--output-dir|--output-dire|--output-direc|--output-direct|\
  61    --output-directo|--output-director|--output-directory)
  62    case "$#" in 1) usage ;; esac; shift
  63    outdir="$1" ;;
  64    -h|--h|--he|--hel|--help)
  65        usage
  66        ;;
  67    -*' '* | -*"$LF"* | -*'     '*)
  68        # Ignore diff option that has whitespace for now.
  69        ;;
  70    -*) diff_opts="$diff_opts$1 " ;;
  71    *) break ;;
  72    esac
  73    shift
  74done
  75
  76case "$keep_subject$numbered" in
  77tt)
  78        die '--keep-subject and --numbered are incompatible.' ;;
  79esac
  80
  81tmp=.tmp-series$$
  82trap 'rm -f $tmp-*' 0 1 2 3 15
  83
  84series=$tmp-series
  85commsg=$tmp-commsg
  86filelist=$tmp-files
  87
  88# Backward compatible argument parsing hack.
  89#
  90# Historically, we supported:
  91# 1. "rev1"             is equivalent to "rev1..HEAD"
  92# 2. "rev1..rev2"
  93# 3. "rev1" "rev2       is equivalent to "rev1..rev2"
  94#
  95# We want to take a sequence of "rev1..rev2" in general.
  96# Also, "rev1.." should mean "rev1..HEAD"; git-diff users are
  97# familiar with that syntax.
  98
  99case "$#,$1$2" in
 1001,?*..?*)
 101        # single "rev1..rev2"
 102        ;;
 1031,?*..)
 104        # single "rev1.." should mean "rev1..HEAD"
 105        set x "$1"HEAD
 106        shift
 107        ;;
 1081,*)
 109        # single rev1
 110        set x "$1..HEAD"
 111        shift
 112        ;;
 1132,?*..?*)
 114        # not traditional "rev1" "rev2"
 115        ;;
 1162,*)
 117        set x "$1..$2"
 118        shift
 119        ;;
 120esac
 121
 122# Now we have what we want in $@
 123for revpair
 124do
 125        case "$revpair" in
 126        ?*..?*)
 127                rev1=`expr "$revpair" : '\(.*\)\.\.'`
 128                rev2=`expr "$revpair" : '.*\.\.\(.*\)'`
 129                ;;
 130        *)
 131                rev1="$revpair^"
 132                rev2="$revpair"
 133                ;;
 134        esac
 135        git-rev-parse --verify "$rev1^0" >/dev/null 2>&1 ||
 136                die "Not a valid rev $rev1 ($revpair)"
 137        git-rev-parse --verify "$rev2^0" >/dev/null 2>&1 ||
 138                die "Not a valid rev $rev2 ($revpair)"
 139        git-cherry -v "$rev1" "$rev2" |
 140        while read sign rev comment
 141        do
 142                case "$sign" in
 143                '-')
 144                        echo >&2 "Merged already: $comment"
 145                        ;;
 146                *)
 147                        echo $rev
 148                        ;;
 149                esac
 150        done
 151done >$series
 152
 153me=`git-var GIT_AUTHOR_IDENT | sed -e 's/>.*/>/'`
 154
 155case "$outdir" in
 156*/) ;;
 157*) outdir="$outdir/" ;;
 158esac
 159test -d "$outdir" || mkdir -p "$outdir" || exit
 160
 161titleScript='
 162        /./d
 163        /^$/n
 164        s/^\[PATCH[^]]*\] *//
 165        s/[^-a-z.A-Z_0-9]/-/g
 166        s/\.\.\.*/\./g
 167        s/\.*$//
 168        s/--*/-/g
 169        s/^-//
 170        s/-$//
 171        s/$/./
 172        p
 173        q
 174'
 175
 176whosepatchScript='
 177/^author /{
 178        s/'\''/'\''\\'\'\''/g
 179        s/author \(.*>\) \(.*\)$/au='\''\1'\'' ad='\''\2'\''/p
 180        q
 181}'
 182
 183process_one () {
 184        mailScript='
 185        /./d
 186        /^$/n'
 187        case "$keep_subject" in
 188        t)  ;;
 189        *)
 190            mailScript="$mailScript"'
 191            s|^\[PATCH[^]]*\] *||
 192            s|^|[PATCH'"$num"'] |'
 193            ;;
 194        esac
 195        mailScript="$mailScript"'
 196        s|^|Subject: |'
 197        case "$mbox" in
 198        t)
 199            echo 'From nobody Mon Sep 17 00:00:00 2001' ;# UNIX "From" line
 200            ;;
 201        esac
 202
 203        eval "$(sed -ne "$whosepatchScript" $commsg)"
 204        test "$author,$au" = ",$me" || {
 205                mailScript="$mailScript"'
 206        a\
 207From: '"$au"
 208        }
 209        test "$date,$au" = ",$me" || {
 210                mailScript="$mailScript"'
 211        a\
 212Date: '"$ad"
 213        }
 214
 215        mailScript="$mailScript"'
 216        a\
 217
 218        : body
 219        p
 220        n
 221        b body'
 222
 223        (cat $commsg ; echo; echo) |
 224        sed -ne "$mailScript" |
 225        git-stripspace
 226
 227        test "$signoff" = "t" && {
 228                offsigner=`git-var GIT_COMMITTER_IDENT | sed -e 's/>.*/>/'`
 229                line="Signed-off-by: $offsigner"
 230                grep -q "^$line\$" $commsg || {
 231                        echo
 232                        echo "$line"
 233                        echo
 234                }
 235        }
 236        echo
 237        echo '---'
 238        echo
 239        git-diff-tree -p $diff_opts "$commit" | git-apply --stat --summary
 240        echo
 241        git-diff-tree -p $diff_opts "$commit"
 242        echo "-- "
 243        echo "@@GIT_VERSION@@"
 244
 245        case "$mbox" in
 246        t)
 247                echo
 248                ;;
 249        esac
 250}
 251
 252total=`wc -l <$series | tr -dc "[0-9]"`
 253case "$total,$numbered" in
 2541,*)
 255        numfmt='' ;;
 256*,t)
 257        numfmt=`echo "$total" | wc -c`
 258        numfmt=$(($numfmt-1))
 259        numfmt=" %0${numfmt}d/$total"
 260esac
 261
 262i=1
 263while read commit
 264do
 265    git-cat-file commit "$commit" | git-stripspace >$commsg
 266    title=`sed -ne "$titleScript" <$commsg`
 267    case "$numbered" in
 268    '') num= ;;
 269    *)
 270        num=`printf "$numfmt" $i` ;;
 271    esac
 272
 273    file=`printf '%04d-%stxt' $i "$title"`
 274    if test '' = "$stdout"
 275    then
 276            echo "$file"
 277            process_one >"$outdir$file"
 278            if test t = "$check"
 279            then
 280                # This is slightly modified from Andrew Morton's Perfect Patch.
 281                # Lines you introduce should not have trailing whitespace.
 282                # Also check for an indentation that has SP before a TAB.
 283                grep -n '^+\([  ]*      .*\|.*[         ]\)$' "$outdir$file"
 284                :
 285            fi
 286    else
 287            echo >&2 "$file"
 288            process_one
 289    fi
 290    i=`expr "$i" + 1`
 291done <$series