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