git-commit.shon commit test format-patch -s: make sure MIME content type is shown as needed (6b945b9)
   1#!/bin/sh
   2#
   3# Copyright (c) 2005 Linus Torvalds
   4# Copyright (c) 2006 Junio C Hamano
   5
   6USAGE='[-a | --interactive] [-s] [-v] [--no-verify] [-m <message> | -F <logfile> | (-C|-c) <commit> | --amend] [-u] [-e] [--author <author>] [--template <file>] [[-i | -o] <path>...]'
   7SUBDIRECTORY_OK=Yes
   8. git-sh-setup
   9require_work_tree
  10
  11git rev-parse --verify HEAD >/dev/null 2>&1 || initial_commit=t
  12
  13case "$0" in
  14*status)
  15        status_only=t
  16        ;;
  17*commit)
  18        status_only=
  19        ;;
  20esac
  21
  22refuse_partial () {
  23        echo >&2 "$1"
  24        echo >&2 "You might have meant to say 'git commit -i paths...', perhaps?"
  25        exit 1
  26}
  27
  28TMP_INDEX=
  29THIS_INDEX="$GIT_DIR/index"
  30NEXT_INDEX="$GIT_DIR/next-index$$"
  31rm -f "$NEXT_INDEX"
  32save_index () {
  33        cp -p "$THIS_INDEX" "$NEXT_INDEX"
  34}
  35
  36run_status () {
  37        # If TMP_INDEX is defined, that means we are doing
  38        # "--only" partial commit, and that index file is used
  39        # to build the tree for the commit.  Otherwise, if
  40        # NEXT_INDEX exists, that is the index file used to
  41        # make the commit.  Otherwise we are using as-is commit
  42        # so the regular index file is what we use to compare.
  43        if test '' != "$TMP_INDEX"
  44        then
  45                GIT_INDEX_FILE="$TMP_INDEX"
  46                export GIT_INDEX_FILE
  47        elif test -f "$NEXT_INDEX"
  48        then
  49                GIT_INDEX_FILE="$NEXT_INDEX"
  50                export GIT_INDEX_FILE
  51        fi
  52
  53        if test "$status_only" = "t" -o "$use_status_color" = "t"; then
  54                color=
  55        else
  56                color=--nocolor
  57        fi
  58        git runstatus ${color} \
  59                ${verbose:+--verbose} \
  60                ${amend:+--amend} \
  61                ${untracked_files:+--untracked}
  62}
  63
  64trap '
  65        test -z "$TMP_INDEX" || {
  66                test -f "$TMP_INDEX" && rm -f "$TMP_INDEX"
  67        }
  68        rm -f "$NEXT_INDEX"
  69' 0
  70
  71################################################################
  72# Command line argument parsing and sanity checking
  73
  74all=
  75also=
  76interactive=
  77only=
  78logfile=
  79use_commit=
  80amend=
  81edit_flag=
  82no_edit=
  83log_given=
  84log_message=
  85verify=t
  86quiet=
  87verbose=
  88signoff=
  89force_author=
  90only_include_assumed=
  91untracked_files=
  92templatefile="`git config commit.template`"
  93while test $# != 0
  94do
  95        case "$1" in
  96        -F|--F|-f|--f|--fi|--fil|--file)
  97                case "$#" in 1) usage ;; esac
  98                shift
  99                no_edit=t
 100                log_given=t$log_given
 101                logfile="$1"
 102                shift
 103                ;;
 104        -F*|-f*)
 105                no_edit=t
 106                log_given=t$log_given
 107                logfile=`expr "z$1" : 'z-[Ff]\(.*\)'`
 108                shift
 109                ;;
 110        --F=*|--f=*|--fi=*|--fil=*|--file=*)
 111                no_edit=t
 112                log_given=t$log_given
 113                logfile=`expr "z$1" : 'z-[^=]*=\(.*\)'`
 114                shift
 115                ;;
 116        -a|--a|--al|--all)
 117                all=t
 118                shift
 119                ;;
 120        --au=*|--aut=*|--auth=*|--autho=*|--author=*)
 121                force_author=`expr "z$1" : 'z-[^=]*=\(.*\)'`
 122                shift
 123                ;;
 124        --au|--aut|--auth|--autho|--author)
 125                case "$#" in 1) usage ;; esac
 126                shift
 127                force_author="$1"
 128                shift
 129                ;;
 130        -e|--e|--ed|--edi|--edit)
 131                edit_flag=t
 132                shift
 133                ;;
 134        -i|--i|--in|--inc|--incl|--inclu|--includ|--include)
 135                also=t
 136                shift
 137                ;;
 138        --int|--inte|--inter|--intera|--interac|--interact|--interacti|\
 139        --interactiv|--interactive)
 140                interactive=t
 141                shift
 142                ;;
 143        -o|--o|--on|--onl|--only)
 144                only=t
 145                shift
 146                ;;
 147        -m|--m|--me|--mes|--mess|--messa|--messag|--message)
 148                case "$#" in 1) usage ;; esac
 149                shift
 150                log_given=m$log_given
 151                if test "$log_message" = ''
 152                then
 153                    log_message="$1"
 154                else
 155                    log_message="$log_message
 156
 157$1"
 158                fi
 159                no_edit=t
 160                shift
 161                ;;
 162        -m*)
 163                log_given=m$log_given
 164                if test "$log_message" = ''
 165                then
 166                    log_message=`expr "z$1" : 'z-m\(.*\)'`
 167                else
 168                    log_message="$log_message
 169
 170`expr "z$1" : 'z-m\(.*\)'`"
 171                fi
 172                no_edit=t
 173                shift
 174                ;;
 175        --m=*|--me=*|--mes=*|--mess=*|--messa=*|--messag=*|--message=*)
 176                log_given=m$log_given
 177                if test "$log_message" = ''
 178                then
 179                    log_message=`expr "z$1" : 'z-[^=]*=\(.*\)'`
 180                else
 181                    log_message="$log_message
 182
 183`expr "z$1" : 'zq-[^=]*=\(.*\)'`"
 184                fi
 185                no_edit=t
 186                shift
 187                ;;
 188        -n|--n|--no|--no-|--no-v|--no-ve|--no-ver|--no-veri|--no-verif|\
 189        --no-verify)
 190                verify=
 191                shift
 192                ;;
 193        --a|--am|--ame|--amen|--amend)
 194                amend=t
 195                use_commit=HEAD
 196                shift
 197                ;;
 198        -c)
 199                case "$#" in 1) usage ;; esac
 200                shift
 201                log_given=t$log_given
 202                use_commit="$1"
 203                no_edit=
 204                shift
 205                ;;
 206        --ree=*|--reed=*|--reedi=*|--reedit=*|--reedit-=*|--reedit-m=*|\
 207        --reedit-me=*|--reedit-mes=*|--reedit-mess=*|--reedit-messa=*|\
 208        --reedit-messag=*|--reedit-message=*)
 209                log_given=t$log_given
 210                use_commit=`expr "z$1" : 'z-[^=]*=\(.*\)'`
 211                no_edit=
 212                shift
 213                ;;
 214        --ree|--reed|--reedi|--reedit|--reedit-|--reedit-m|--reedit-me|\
 215        --reedit-mes|--reedit-mess|--reedit-messa|--reedit-messag|\
 216        --reedit-message)
 217                case "$#" in 1) usage ;; esac
 218                shift
 219                log_given=t$log_given
 220                use_commit="$1"
 221                no_edit=
 222                shift
 223                ;;
 224        -C)
 225                case "$#" in 1) usage ;; esac
 226                shift
 227                log_given=t$log_given
 228                use_commit="$1"
 229                no_edit=t
 230                shift
 231                ;;
 232        --reu=*|--reus=*|--reuse=*|--reuse-=*|--reuse-m=*|--reuse-me=*|\
 233        --reuse-mes=*|--reuse-mess=*|--reuse-messa=*|--reuse-messag=*|\
 234        --reuse-message=*)
 235                log_given=t$log_given
 236                use_commit=`expr "z$1" : 'z-[^=]*=\(.*\)'`
 237                no_edit=t
 238                shift
 239                ;;
 240        --reu|--reus|--reuse|--reuse-|--reuse-m|--reuse-me|--reuse-mes|\
 241        --reuse-mess|--reuse-messa|--reuse-messag|--reuse-message)
 242                case "$#" in 1) usage ;; esac
 243                shift
 244                log_given=t$log_given
 245                use_commit="$1"
 246                no_edit=t
 247                shift
 248                ;;
 249        -s|--s|--si|--sig|--sign|--signo|--signof|--signoff)
 250                signoff=t
 251                shift
 252                ;;
 253        -t|--t|--te|--tem|--temp|--templ|--templa|--templat|--template)
 254                case "$#" in 1) usage ;; esac
 255                shift
 256                templatefile="$1"
 257                no_edit=
 258                shift
 259                ;;
 260        -q|--q|--qu|--qui|--quie|--quiet)
 261                quiet=t
 262                shift
 263                ;;
 264        -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
 265                verbose=t
 266                shift
 267                ;;
 268        -u|--u|--un|--unt|--untr|--untra|--untrac|--untrack|--untracke|\
 269        --untracked|--untracked-|--untracked-f|--untracked-fi|--untracked-fil|\
 270        --untracked-file|--untracked-files)
 271                untracked_files=t
 272                shift
 273                ;;
 274        --)
 275                shift
 276                break
 277                ;;
 278        -*)
 279                usage
 280                ;;
 281        *)
 282                break
 283                ;;
 284        esac
 285done
 286case "$edit_flag" in t) no_edit= ;; esac
 287
 288################################################################
 289# Sanity check options
 290
 291case "$amend,$initial_commit" in
 292t,t)
 293        die "You do not have anything to amend." ;;
 294t,)
 295        if [ -f "$GIT_DIR/MERGE_HEAD" ]; then
 296                die "You are in the middle of a merge -- cannot amend."
 297        fi ;;
 298esac
 299
 300case "$log_given" in
 301tt*)
 302        die "Only one of -c/-C/-F can be used." ;;
 303*tm*|*mt*)
 304        die "Option -m cannot be combined with -c/-C/-F." ;;
 305esac
 306
 307case "$#,$also,$only,$amend" in
 308*,t,t,*)
 309        die "Only one of --include/--only can be used." ;;
 3100,t,,* | 0,,t,)
 311        die "No paths with --include/--only does not make sense." ;;
 3120,,t,t)
 313        only_include_assumed="# Clever... amending the last one with dirty index." ;;
 3140,,,*)
 315        ;;
 316*,,,*)
 317        only_include_assumed="# Explicit paths specified without -i nor -o; assuming --only paths..."
 318        also=
 319        ;;
 320esac
 321unset only
 322case "$all,$interactive,$also,$#" in
 323*t,*t,*)
 324        die "Cannot use -a, --interactive or -i at the same time." ;;
 325t,,[1-9]*)
 326        die "Paths with -a does not make sense." ;;
 327,t,[1-9]*)
 328        die "Paths with --interactive does not make sense." ;;
 329,,t,0)
 330        die "No paths with -i does not make sense." ;;
 331esac
 332
 333if test ! -z "$templatefile" -a -z "$log_given"
 334then
 335        if test ! -f "$templatefile"
 336        then
 337                die "Commit template file does not exist."
 338        fi
 339fi
 340
 341################################################################
 342# Prepare index to have a tree to be committed
 343
 344case "$all,$also" in
 345t,)
 346        if test ! -f "$THIS_INDEX"
 347        then
 348                die 'nothing to commit (use "git add file1 file2" to include for commit)'
 349        fi
 350        save_index &&
 351        (
 352                cd_to_toplevel &&
 353                GIT_INDEX_FILE="$NEXT_INDEX" &&
 354                export GIT_INDEX_FILE &&
 355                git diff-files --name-only -z |
 356                git update-index --remove -z --stdin
 357        ) || exit
 358        ;;
 359,t)
 360        save_index &&
 361        git ls-files --error-unmatch -- "$@" >/dev/null || exit
 362
 363        git diff-files --name-only -z -- "$@"  |
 364        (
 365                cd_to_toplevel &&
 366                GIT_INDEX_FILE="$NEXT_INDEX" &&
 367                export GIT_INDEX_FILE &&
 368                git update-index --remove -z --stdin
 369        ) || exit
 370        ;;
 371,)
 372        if test "$interactive" = t; then
 373                git add --interactive || exit
 374        fi
 375        case "$#" in
 376        0)
 377                ;; # commit as-is
 378        *)
 379                if test -f "$GIT_DIR/MERGE_HEAD"
 380                then
 381                        refuse_partial "Cannot do a partial commit during a merge."
 382                fi
 383
 384                TMP_INDEX="$GIT_DIR/tmp-index$$"
 385                W=
 386                test -z "$initial_commit" && W=--with-tree=HEAD
 387                commit_only=`git ls-files --error-unmatch $W -- "$@"` || exit
 388
 389                # Build a temporary index and update the real index
 390                # the same way.
 391                if test -z "$initial_commit"
 392                then
 393                        GIT_INDEX_FILE="$THIS_INDEX" \
 394                        git read-tree --index-output="$TMP_INDEX" -i -m HEAD
 395                else
 396                        rm -f "$TMP_INDEX"
 397                fi || exit
 398
 399                printf '%s\n' "$commit_only" |
 400                GIT_INDEX_FILE="$TMP_INDEX" \
 401                git update-index --add --remove --stdin &&
 402
 403                save_index &&
 404                printf '%s\n' "$commit_only" |
 405                (
 406                        GIT_INDEX_FILE="$NEXT_INDEX"
 407                        export GIT_INDEX_FILE
 408                        git update-index --add --remove --stdin
 409                ) || exit
 410                ;;
 411        esac
 412        ;;
 413esac
 414
 415################################################################
 416# If we do as-is commit, the index file will be THIS_INDEX,
 417# otherwise NEXT_INDEX after we make this commit.  We leave
 418# the index as is if we abort.
 419
 420if test -f "$NEXT_INDEX"
 421then
 422        USE_INDEX="$NEXT_INDEX"
 423else
 424        USE_INDEX="$THIS_INDEX"
 425fi
 426
 427case "$status_only" in
 428t)
 429        # This will silently fail in a read-only repository, which is
 430        # what we want.
 431        GIT_INDEX_FILE="$USE_INDEX" git update-index -q --unmerged --refresh
 432        run_status
 433        exit $?
 434        ;;
 435'')
 436        GIT_INDEX_FILE="$USE_INDEX" git update-index -q --refresh || exit
 437        ;;
 438esac
 439
 440################################################################
 441# Grab commit message, write out tree and make commit.
 442
 443if test t = "$verify" && test -x "$GIT_DIR"/hooks/pre-commit
 444then
 445        if test "$TMP_INDEX"
 446        then
 447                GIT_INDEX_FILE="$TMP_INDEX" "$GIT_DIR"/hooks/pre-commit
 448        else
 449                GIT_INDEX_FILE="$USE_INDEX" "$GIT_DIR"/hooks/pre-commit
 450        fi || exit
 451fi
 452
 453if test "$log_message" != ''
 454then
 455        printf '%s\n' "$log_message"
 456elif test "$logfile" != ""
 457then
 458        if test "$logfile" = -
 459        then
 460                test -t 0 &&
 461                echo >&2 "(reading log message from standard input)"
 462                cat
 463        else
 464                cat <"$logfile"
 465        fi
 466elif test "$use_commit" != ""
 467then
 468        encoding=$(git config i18n.commitencoding || echo UTF-8)
 469        git show -s --pretty=raw --encoding="$encoding" "$use_commit" |
 470        sed -e '1,/^$/d' -e 's/^    //'
 471elif test -f "$GIT_DIR/MERGE_MSG"
 472then
 473        cat "$GIT_DIR/MERGE_MSG"
 474elif test -f "$GIT_DIR/SQUASH_MSG"
 475then
 476        cat "$GIT_DIR/SQUASH_MSG"
 477elif test "$templatefile" != ""
 478then
 479        cat "$templatefile"
 480fi | git stripspace >"$GIT_DIR"/COMMIT_EDITMSG
 481
 482case "$signoff" in
 483t)
 484        sign=$(git-var GIT_COMMITTER_IDENT | sed -e '
 485                s/>.*/>/
 486                s/^/Signed-off-by: /
 487                ')
 488        blank_before_signoff=
 489        tail -n 1 "$GIT_DIR"/COMMIT_EDITMSG |
 490        grep 'Signed-off-by:' >/dev/null || blank_before_signoff='
 491'
 492        tail -n 1 "$GIT_DIR"/COMMIT_EDITMSG |
 493        grep "$sign"$ >/dev/null ||
 494        printf '%s%s\n' "$blank_before_signoff" "$sign" \
 495                >>"$GIT_DIR"/COMMIT_EDITMSG
 496        ;;
 497esac
 498
 499if test -f "$GIT_DIR/MERGE_HEAD" && test -z "$no_edit"; then
 500        echo "#"
 501        echo "# It looks like you may be committing a MERGE."
 502        echo "# If this is not correct, please remove the file"
 503        printf '%s\n' "#        $GIT_DIR/MERGE_HEAD"
 504        echo "# and try again"
 505        echo "#"
 506fi >>"$GIT_DIR"/COMMIT_EDITMSG
 507
 508# Author
 509if test '' != "$use_commit"
 510then
 511        eval "$(get_author_ident_from_commit "$use_commit")"
 512        export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL GIT_AUTHOR_DATE
 513fi
 514if test '' != "$force_author"
 515then
 516        GIT_AUTHOR_NAME=`expr "z$force_author" : 'z\(.*[^ ]\) *<.*'` &&
 517        GIT_AUTHOR_EMAIL=`expr "z$force_author" : '.*\(<.*\)'` &&
 518        test '' != "$GIT_AUTHOR_NAME" &&
 519        test '' != "$GIT_AUTHOR_EMAIL" ||
 520        die "malformed --author parameter"
 521        export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL
 522fi
 523
 524PARENTS="-p HEAD"
 525if test -z "$initial_commit"
 526then
 527        rloga='commit'
 528        if [ -f "$GIT_DIR/MERGE_HEAD" ]; then
 529                rloga='commit (merge)'
 530                PARENTS="-p HEAD "`sed -e 's/^/-p /' "$GIT_DIR/MERGE_HEAD"`
 531        elif test -n "$amend"; then
 532                rloga='commit (amend)'
 533                PARENTS=$(git cat-file commit HEAD |
 534                        sed -n -e '/^$/q' -e 's/^parent /-p /p')
 535        fi
 536        current="$(git rev-parse --verify HEAD)"
 537else
 538        if [ -z "$(git ls-files)" ]; then
 539                echo >&2 'nothing to commit (use "git add file1 file2" to include for commit)'
 540                exit 1
 541        fi
 542        PARENTS=""
 543        rloga='commit (initial)'
 544        current=''
 545fi
 546set_reflog_action "$rloga"
 547
 548if test -z "$no_edit"
 549then
 550        {
 551                echo ""
 552                echo "# Please enter the commit message for your changes."
 553                echo "# (Comment lines starting with '#' will not be included)"
 554                test -z "$only_include_assumed" || echo "$only_include_assumed"
 555                run_status
 556        } >>"$GIT_DIR"/COMMIT_EDITMSG
 557else
 558        # we need to check if there is anything to commit
 559        run_status >/dev/null
 560fi
 561if [ "$?" != "0" -a ! -f "$GIT_DIR/MERGE_HEAD" ]
 562then
 563        rm -f "$GIT_DIR/COMMIT_EDITMSG" "$GIT_DIR/SQUASH_MSG"
 564        use_status_color=t
 565        run_status
 566        exit 1
 567fi
 568
 569case "$no_edit" in
 570'')
 571        git-var GIT_AUTHOR_IDENT > /dev/null  || die
 572        git-var GIT_COMMITTER_IDENT > /dev/null  || die
 573        git_editor "$GIT_DIR/COMMIT_EDITMSG"
 574        ;;
 575esac
 576
 577case "$verify" in
 578t)
 579        if test -x "$GIT_DIR"/hooks/commit-msg
 580        then
 581                "$GIT_DIR"/hooks/commit-msg "$GIT_DIR"/COMMIT_EDITMSG || exit
 582        fi
 583esac
 584
 585if test -z "$no_edit"
 586then
 587    sed -e '
 588        /^diff --git a\/.*/{
 589            s///
 590            q
 591        }
 592        /^#/d
 593    ' "$GIT_DIR"/COMMIT_EDITMSG
 594else
 595    cat "$GIT_DIR"/COMMIT_EDITMSG
 596fi |
 597git stripspace >"$GIT_DIR"/COMMIT_MSG
 598
 599# Test whether the commit message has any content we didn't supply.
 600have_commitmsg=
 601grep -v -i '^Signed-off-by' "$GIT_DIR"/COMMIT_MSG |
 602        git stripspace > "$GIT_DIR"/COMMIT_BAREMSG
 603
 604# Is the commit message totally empty?
 605if test -s "$GIT_DIR"/COMMIT_BAREMSG
 606then
 607        if test "$templatefile" != ""
 608        then
 609                # Test whether this is just the unaltered template.
 610                if cnt=`sed -e '/^#/d' < "$templatefile" |
 611                        git stripspace |
 612                        diff "$GIT_DIR"/COMMIT_BAREMSG - |
 613                        wc -l` &&
 614                   test 0 -lt $cnt
 615                then
 616                        have_commitmsg=t
 617                fi
 618        else
 619                # No template, so the content in the commit message must
 620                # have come from the user.
 621                have_commitmsg=t
 622        fi
 623fi
 624
 625rm -f "$GIT_DIR"/COMMIT_BAREMSG
 626
 627if test "$have_commitmsg" = "t"
 628then
 629        if test -z "$TMP_INDEX"
 630        then
 631                tree=$(GIT_INDEX_FILE="$USE_INDEX" git write-tree)
 632        else
 633                tree=$(GIT_INDEX_FILE="$TMP_INDEX" git write-tree) &&
 634                rm -f "$TMP_INDEX"
 635        fi &&
 636        commit=$(git commit-tree $tree $PARENTS <"$GIT_DIR/COMMIT_MSG") &&
 637        rlogm=$(sed -e 1q "$GIT_DIR"/COMMIT_MSG) &&
 638        git update-ref -m "$GIT_REFLOG_ACTION: $rlogm" HEAD $commit "$current" &&
 639        rm -f -- "$GIT_DIR/MERGE_HEAD" "$GIT_DIR/MERGE_MSG" &&
 640        if test -f "$NEXT_INDEX"
 641        then
 642                mv "$NEXT_INDEX" "$THIS_INDEX"
 643        else
 644                : ;# happy
 645        fi
 646else
 647        echo >&2 "* no commit message?  aborting commit."
 648        false
 649fi
 650ret="$?"
 651rm -f "$GIT_DIR/COMMIT_MSG" "$GIT_DIR/COMMIT_EDITMSG" "$GIT_DIR/SQUASH_MSG"
 652
 653cd_to_toplevel
 654
 655git rerere
 656
 657if test "$ret" = 0
 658then
 659        if test -x "$GIT_DIR"/hooks/post-commit
 660        then
 661                "$GIT_DIR"/hooks/post-commit
 662        fi
 663        if test -z "$quiet"
 664        then
 665                commit=`git diff-tree --always --shortstat --pretty="format:%h: %s"\
 666                       --summary --root HEAD --`
 667                echo "Created${initial_commit:+ initial} commit $commit"
 668        fi
 669fi
 670
 671exit "$ret"