git-stash.shon commit stash: introduce push verb (f5727e2)
   1#!/bin/sh
   2# Copyright (c) 2007, Nanako Shiraishi
   3
   4dashless=$(basename "$0" | sed -e 's/-/ /')
   5USAGE="list [<options>]
   6   or: $dashless show [<stash>]
   7   or: $dashless drop [-q|--quiet] [<stash>]
   8   or: $dashless ( pop | apply ) [--index] [-q|--quiet] [<stash>]
   9   or: $dashless branch <branchname> [<stash>]
  10   or: $dashless [save [--patch] [-k|--[no-]keep-index] [-q|--quiet]
  11                       [-u|--include-untracked] [-a|--all] [<message>]]
  12   or: $dashless push [--patch] [-k|--[no-]keep-index] [-q|--quiet]
  13                      [-u|--include-untracked] [-a|--all] [-m <message>]
  14   or: $dashless clear"
  15
  16SUBDIRECTORY_OK=Yes
  17OPTIONS_SPEC=
  18START_DIR=$(pwd)
  19. git-sh-setup
  20require_work_tree
  21cd_to_toplevel
  22
  23TMP="$GIT_DIR/.git-stash.$$"
  24TMPindex=${GIT_INDEX_FILE-"$(git rev-parse --git-path index)"}.stash.$$
  25trap 'rm -f "$TMP-"* "$TMPindex"' 0
  26
  27ref_stash=refs/stash
  28
  29if git config --get-colorbool color.interactive; then
  30       help_color="$(git config --get-color color.interactive.help 'red bold')"
  31       reset_color="$(git config --get-color '' reset)"
  32else
  33       help_color=
  34       reset_color=
  35fi
  36
  37no_changes () {
  38        git diff-index --quiet --cached HEAD --ignore-submodules -- &&
  39        git diff-files --quiet --ignore-submodules &&
  40        (test -z "$untracked" || test -z "$(untracked_files)")
  41}
  42
  43untracked_files () {
  44        excl_opt=--exclude-standard
  45        test "$untracked" = "all" && excl_opt=
  46        git ls-files -o -z $excl_opt
  47}
  48
  49clear_stash () {
  50        if test $# != 0
  51        then
  52                die "$(gettext "git stash clear with parameters is unimplemented")"
  53        fi
  54        if current=$(git rev-parse --verify --quiet $ref_stash)
  55        then
  56                git update-ref -d $ref_stash $current
  57        fi
  58}
  59
  60create_stash () {
  61        stash_msg="$1"
  62        untracked="$2"
  63
  64        git update-index -q --refresh
  65        if no_changes
  66        then
  67                exit 0
  68        fi
  69
  70        # state of the base commit
  71        if b_commit=$(git rev-parse --verify HEAD)
  72        then
  73                head=$(git rev-list --oneline -n 1 HEAD --)
  74        else
  75                die "$(gettext "You do not have the initial commit yet")"
  76        fi
  77
  78        if branch=$(git symbolic-ref -q HEAD)
  79        then
  80                branch=${branch#refs/heads/}
  81        else
  82                branch='(no branch)'
  83        fi
  84        msg=$(printf '%s: %s' "$branch" "$head")
  85
  86        # state of the index
  87        i_tree=$(git write-tree) &&
  88        i_commit=$(printf 'index on %s\n' "$msg" |
  89                git commit-tree $i_tree -p $b_commit) ||
  90                die "$(gettext "Cannot save the current index state")"
  91
  92        if test -n "$untracked"
  93        then
  94                # Untracked files are stored by themselves in a parentless commit, for
  95                # ease of unpacking later.
  96                u_commit=$(
  97                        untracked_files | (
  98                                GIT_INDEX_FILE="$TMPindex" &&
  99                                export GIT_INDEX_FILE &&
 100                                rm -f "$TMPindex" &&
 101                                git update-index -z --add --remove --stdin &&
 102                                u_tree=$(git write-tree) &&
 103                                printf 'untracked files on %s\n' "$msg" | git commit-tree $u_tree  &&
 104                                rm -f "$TMPindex"
 105                ) ) || die "$(gettext "Cannot save the untracked files")"
 106
 107                untracked_commit_option="-p $u_commit";
 108        else
 109                untracked_commit_option=
 110        fi
 111
 112        if test -z "$patch_mode"
 113        then
 114
 115                # state of the working tree
 116                w_tree=$( (
 117                        git read-tree --index-output="$TMPindex" -m $i_tree &&
 118                        GIT_INDEX_FILE="$TMPindex" &&
 119                        export GIT_INDEX_FILE &&
 120                        git diff-index --name-only -z HEAD -- >"$TMP-stagenames" &&
 121                        git update-index -z --add --remove --stdin <"$TMP-stagenames" &&
 122                        git write-tree &&
 123                        rm -f "$TMPindex"
 124                ) ) ||
 125                        die "$(gettext "Cannot save the current worktree state")"
 126
 127        else
 128
 129                rm -f "$TMP-index" &&
 130                GIT_INDEX_FILE="$TMP-index" git read-tree HEAD &&
 131
 132                # find out what the user wants
 133                GIT_INDEX_FILE="$TMP-index" \
 134                        git add--interactive --patch=stash -- &&
 135
 136                # state of the working tree
 137                w_tree=$(GIT_INDEX_FILE="$TMP-index" git write-tree) ||
 138                die "$(gettext "Cannot save the current worktree state")"
 139
 140                git diff-tree -p HEAD $w_tree -- >"$TMP-patch" &&
 141                test -s "$TMP-patch" ||
 142                die "$(gettext "No changes selected")"
 143
 144                rm -f "$TMP-index" ||
 145                die "$(gettext "Cannot remove temporary index (can't happen)")"
 146
 147        fi
 148
 149        # create the stash
 150        if test -z "$stash_msg"
 151        then
 152                stash_msg=$(printf 'WIP on %s' "$msg")
 153        else
 154                stash_msg=$(printf 'On %s: %s' "$branch" "$stash_msg")
 155        fi
 156        w_commit=$(printf '%s\n' "$stash_msg" |
 157        git commit-tree $w_tree -p $b_commit -p $i_commit $untracked_commit_option) ||
 158        die "$(gettext "Cannot record working tree state")"
 159}
 160
 161store_stash () {
 162        while test $# != 0
 163        do
 164                case "$1" in
 165                -m|--message)
 166                        shift
 167                        stash_msg="$1"
 168                        ;;
 169                -q|--quiet)
 170                        quiet=t
 171                        ;;
 172                *)
 173                        break
 174                        ;;
 175                esac
 176                shift
 177        done
 178        test $# = 1 ||
 179        die "$(eval_gettext "\"$dashless store\" requires one <commit> argument")"
 180
 181        w_commit="$1"
 182        if test -z "$stash_msg"
 183        then
 184                stash_msg="Created via \"git stash store\"."
 185        fi
 186
 187        git update-ref --create-reflog -m "$stash_msg" $ref_stash $w_commit
 188        ret=$?
 189        test $ret != 0 && test -z "$quiet" &&
 190        die "$(eval_gettext "Cannot update \$ref_stash with \$w_commit")"
 191        return $ret
 192}
 193
 194push_stash () {
 195        keep_index=
 196        patch_mode=
 197        untracked=
 198        stash_msg=
 199        while test $# != 0
 200        do
 201                case "$1" in
 202                -k|--keep-index)
 203                        keep_index=t
 204                        ;;
 205                --no-keep-index)
 206                        keep_index=n
 207                        ;;
 208                -p|--patch)
 209                        patch_mode=t
 210                        # only default to keep if we don't already have an override
 211                        test -z "$keep_index" && keep_index=t
 212                        ;;
 213                -q|--quiet)
 214                        GIT_QUIET=t
 215                        ;;
 216                -u|--include-untracked)
 217                        untracked=untracked
 218                        ;;
 219                -a|--all)
 220                        untracked=all
 221                        ;;
 222                -m|--message)
 223                        shift
 224                        test -z ${1+x} && usage
 225                        stash_msg=$1
 226                        ;;
 227                --help)
 228                        show_help
 229                        ;;
 230                --)
 231                        shift
 232                        break
 233                        ;;
 234                -*)
 235                        option="$1"
 236                        # TRANSLATORS: $option is an invalid option, like
 237                        # `--blah-blah'. The 7 spaces at the beginning of the
 238                        # second line correspond to "error: ". So you should line
 239                        # up the second line with however many characters the
 240                        # translation of "error: " takes in your language. E.g. in
 241                        # English this is:
 242                        #
 243                        #    $ git stash save --blah-blah 2>&1 | head -n 2
 244                        #    error: unknown option for 'stash save': --blah-blah
 245                        #           To provide a message, use git stash save -- '--blah-blah'
 246                        eval_gettextln "error: unknown option for 'stash save': \$option
 247       To provide a message, use git stash save -- '\$option'"
 248                        usage
 249                        ;;
 250                *)
 251                        break
 252                        ;;
 253                esac
 254                shift
 255        done
 256
 257        if test -n "$patch_mode" && test -n "$untracked"
 258        then
 259                die "$(gettext "Can't use --patch and --include-untracked or --all at the same time")"
 260        fi
 261
 262        git update-index -q --refresh
 263        if no_changes
 264        then
 265                say "$(gettext "No local changes to save")"
 266                exit 0
 267        fi
 268        git reflog exists $ref_stash ||
 269                clear_stash || die "$(gettext "Cannot initialize stash")"
 270
 271        create_stash "$stash_msg" $untracked
 272        store_stash -m "$stash_msg" -q $w_commit ||
 273        die "$(gettext "Cannot save the current status")"
 274        say "$(eval_gettext "Saved working directory and index state \$stash_msg")"
 275
 276        if test -z "$patch_mode"
 277        then
 278                git reset --hard ${GIT_QUIET:+-q}
 279                test "$untracked" = "all" && CLEAN_X_OPTION=-x || CLEAN_X_OPTION=
 280                if test -n "$untracked"
 281                then
 282                        git clean --force --quiet -d $CLEAN_X_OPTION
 283                fi
 284
 285                if test "$keep_index" = "t" && test -n "$i_tree"
 286                then
 287                        git read-tree --reset -u $i_tree
 288                fi
 289        else
 290                git apply -R < "$TMP-patch" ||
 291                die "$(gettext "Cannot remove worktree changes")"
 292
 293                if test "$keep_index" != "t"
 294                then
 295                        git reset
 296                fi
 297        fi
 298}
 299
 300save_stash () {
 301        push_options=
 302        while test $# != 0
 303        do
 304                case "$1" in
 305                --)
 306                        shift
 307                        break
 308                        ;;
 309                -*)
 310                        # pass all options through to push_stash
 311                        push_options="$push_options $1"
 312                        ;;
 313                *)
 314                        break
 315                        ;;
 316                esac
 317                shift
 318        done
 319
 320        stash_msg="$*"
 321
 322        if test -z "$stash_msg"
 323        then
 324                push_stash $push_options
 325        else
 326                push_stash $push_options -m "$stash_msg"
 327        fi
 328}
 329
 330have_stash () {
 331        git rev-parse --verify --quiet $ref_stash >/dev/null
 332}
 333
 334list_stash () {
 335        have_stash || return 0
 336        git log --format="%gd: %gs" -g --first-parent -m "$@" $ref_stash --
 337}
 338
 339show_stash () {
 340        ALLOW_UNKNOWN_FLAGS=t
 341        assert_stash_like "$@"
 342
 343        if test -z "$FLAGS"
 344        then
 345                if test "$(git config --bool stash.showStat || echo true)" = "true"
 346                then
 347                        FLAGS=--stat
 348                fi
 349
 350                if test "$(git config --bool stash.showPatch || echo false)" = "true"
 351                then
 352                        FLAGS=${FLAGS}${FLAGS:+ }-p
 353                fi
 354
 355                if test -z "$FLAGS"
 356                then
 357                        return 0
 358                fi
 359        fi
 360
 361        git diff ${FLAGS} $b_commit $w_commit
 362}
 363
 364show_help () {
 365        exec git help stash
 366        exit 1
 367}
 368
 369#
 370# Parses the remaining options looking for flags and
 371# at most one revision defaulting to ${ref_stash}@{0}
 372# if none found.
 373#
 374# Derives related tree and commit objects from the
 375# revision, if one is found.
 376#
 377# stash records the work tree, and is a merge between the
 378# base commit (first parent) and the index tree (second parent).
 379#
 380#   REV is set to the symbolic version of the specified stash-like commit
 381#   IS_STASH_LIKE is non-blank if ${REV} looks like a stash
 382#   IS_STASH_REF is non-blank if the ${REV} looks like a stash ref
 383#   s is set to the SHA1 of the stash commit
 384#   w_commit is set to the commit containing the working tree
 385#   b_commit is set to the base commit
 386#   i_commit is set to the commit containing the index tree
 387#   u_commit is set to the commit containing the untracked files tree
 388#   w_tree is set to the working tree
 389#   b_tree is set to the base tree
 390#   i_tree is set to the index tree
 391#   u_tree is set to the untracked files tree
 392#
 393#   GIT_QUIET is set to t if -q is specified
 394#   INDEX_OPTION is set to --index if --index is specified.
 395#   FLAGS is set to the remaining flags (if allowed)
 396#
 397# dies if:
 398#   * too many revisions specified
 399#   * no revision is specified and there is no stash stack
 400#   * a revision is specified which cannot be resolve to a SHA1
 401#   * a non-existent stash reference is specified
 402#   * unknown flags were set and ALLOW_UNKNOWN_FLAGS is not "t"
 403#
 404
 405parse_flags_and_rev()
 406{
 407        test "$PARSE_CACHE" = "$*" && return 0 # optimisation
 408        PARSE_CACHE="$*"
 409
 410        IS_STASH_LIKE=
 411        IS_STASH_REF=
 412        INDEX_OPTION=
 413        s=
 414        w_commit=
 415        b_commit=
 416        i_commit=
 417        u_commit=
 418        w_tree=
 419        b_tree=
 420        i_tree=
 421        u_tree=
 422
 423        FLAGS=
 424        REV=
 425        for opt
 426        do
 427                case "$opt" in
 428                        -q|--quiet)
 429                                GIT_QUIET=-t
 430                        ;;
 431                        --index)
 432                                INDEX_OPTION=--index
 433                        ;;
 434                        --help)
 435                                show_help
 436                        ;;
 437                        -*)
 438                                test "$ALLOW_UNKNOWN_FLAGS" = t ||
 439                                        die "$(eval_gettext "unknown option: \$opt")"
 440                                FLAGS="${FLAGS}${FLAGS:+ }$opt"
 441                        ;;
 442                        *)
 443                                REV="${REV}${REV:+ }'$opt'"
 444                        ;;
 445                esac
 446        done
 447
 448        eval set -- $REV
 449
 450        case $# in
 451                0)
 452                        have_stash || die "$(gettext "No stash found.")"
 453                        set -- ${ref_stash}@{0}
 454                ;;
 455                1)
 456                        :
 457                ;;
 458                *)
 459                        die "$(eval_gettext "Too many revisions specified: \$REV")"
 460                ;;
 461        esac
 462
 463        case "$1" in
 464                *[!0-9]*)
 465                        :
 466                ;;
 467                *)
 468                        set -- "${ref_stash}@{$1}"
 469                ;;
 470        esac
 471
 472        REV=$(git rev-parse --symbolic --verify --quiet "$1") || {
 473                reference="$1"
 474                die "$(eval_gettext "\$reference is not a valid reference")"
 475        }
 476
 477        i_commit=$(git rev-parse --verify --quiet "$REV^2") &&
 478        set -- $(git rev-parse "$REV" "$REV^1" "$REV:" "$REV^1:" "$REV^2:" 2>/dev/null) &&
 479        s=$1 &&
 480        w_commit=$1 &&
 481        b_commit=$2 &&
 482        w_tree=$3 &&
 483        b_tree=$4 &&
 484        i_tree=$5 &&
 485        IS_STASH_LIKE=t &&
 486        test "$ref_stash" = "$(git rev-parse --symbolic-full-name "${REV%@*}")" &&
 487        IS_STASH_REF=t
 488
 489        u_commit=$(git rev-parse --verify --quiet "$REV^3") &&
 490        u_tree=$(git rev-parse "$REV^3:" 2>/dev/null)
 491}
 492
 493is_stash_like()
 494{
 495        parse_flags_and_rev "$@"
 496        test -n "$IS_STASH_LIKE"
 497}
 498
 499assert_stash_like() {
 500        is_stash_like "$@" || {
 501                args="$*"
 502                die "$(eval_gettext "'\$args' is not a stash-like commit")"
 503        }
 504}
 505
 506is_stash_ref() {
 507        is_stash_like "$@" && test -n "$IS_STASH_REF"
 508}
 509
 510assert_stash_ref() {
 511        is_stash_ref "$@" || {
 512                args="$*"
 513                die "$(eval_gettext "'\$args' is not a stash reference")"
 514        }
 515}
 516
 517apply_stash () {
 518
 519        assert_stash_like "$@"
 520
 521        git update-index -q --refresh || die "$(gettext "unable to refresh index")"
 522
 523        # current index state
 524        c_tree=$(git write-tree) ||
 525                die "$(gettext "Cannot apply a stash in the middle of a merge")"
 526
 527        unstashed_index_tree=
 528        if test -n "$INDEX_OPTION" && test "$b_tree" != "$i_tree" &&
 529                        test "$c_tree" != "$i_tree"
 530        then
 531                git diff-tree --binary $s^2^..$s^2 | git apply --cached
 532                test $? -ne 0 &&
 533                        die "$(gettext "Conflicts in index. Try without --index.")"
 534                unstashed_index_tree=$(git write-tree) ||
 535                        die "$(gettext "Could not save index tree")"
 536                git reset
 537        fi
 538
 539        if test -n "$u_tree"
 540        then
 541                GIT_INDEX_FILE="$TMPindex" git-read-tree "$u_tree" &&
 542                GIT_INDEX_FILE="$TMPindex" git checkout-index --all &&
 543                rm -f "$TMPindex" ||
 544                die "$(gettext "Could not restore untracked files from stash")"
 545        fi
 546
 547        eval "
 548                GITHEAD_$w_tree='Stashed changes' &&
 549                GITHEAD_$c_tree='Updated upstream' &&
 550                GITHEAD_$b_tree='Version stash was based on' &&
 551                export GITHEAD_$w_tree GITHEAD_$c_tree GITHEAD_$b_tree
 552        "
 553
 554        if test -n "$GIT_QUIET"
 555        then
 556                GIT_MERGE_VERBOSITY=0 && export GIT_MERGE_VERBOSITY
 557        fi
 558        if git merge-recursive $b_tree -- $c_tree $w_tree
 559        then
 560                # No conflict
 561                if test -n "$unstashed_index_tree"
 562                then
 563                        git read-tree "$unstashed_index_tree"
 564                else
 565                        a="$TMP-added" &&
 566                        git diff-index --cached --name-only --diff-filter=A $c_tree >"$a" &&
 567                        git read-tree --reset $c_tree &&
 568                        git update-index --add --stdin <"$a" ||
 569                                die "$(gettext "Cannot unstage modified files")"
 570                        rm -f "$a"
 571                fi
 572                squelch=
 573                if test -n "$GIT_QUIET"
 574                then
 575                        squelch='>/dev/null 2>&1'
 576                fi
 577                (cd "$START_DIR" && eval "git status $squelch") || :
 578        else
 579                # Merge conflict; keep the exit status from merge-recursive
 580                status=$?
 581                git rerere
 582                if test -n "$INDEX_OPTION"
 583                then
 584                        gettextln "Index was not unstashed." >&2
 585                fi
 586                exit $status
 587        fi
 588}
 589
 590pop_stash() {
 591        assert_stash_ref "$@"
 592
 593        if apply_stash "$@"
 594        then
 595                drop_stash "$@"
 596        else
 597                status=$?
 598                say "$(gettext "The stash is kept in case you need it again.")"
 599                exit $status
 600        fi
 601}
 602
 603drop_stash () {
 604        assert_stash_ref "$@"
 605
 606        git reflog delete --updateref --rewrite "${REV}" &&
 607                say "$(eval_gettext "Dropped \${REV} (\$s)")" ||
 608                die "$(eval_gettext "\${REV}: Could not drop stash entry")"
 609
 610        # clear_stash if we just dropped the last stash entry
 611        git rev-parse --verify --quiet "$ref_stash@{0}" >/dev/null ||
 612        clear_stash
 613}
 614
 615apply_to_branch () {
 616        test -n "$1" || die "$(gettext "No branch name specified")"
 617        branch=$1
 618        shift 1
 619
 620        set -- --index "$@"
 621        assert_stash_like "$@"
 622
 623        git checkout -b $branch $REV^ &&
 624        apply_stash "$@" && {
 625                test -z "$IS_STASH_REF" || drop_stash "$@"
 626        }
 627}
 628
 629PARSE_CACHE='--not-parsed'
 630# The default command is "save" if nothing but options are given
 631seen_non_option=
 632for opt
 633do
 634        case "$opt" in
 635        -*) ;;
 636        *) seen_non_option=t; break ;;
 637        esac
 638done
 639
 640test -n "$seen_non_option" || set "save" "$@"
 641
 642# Main command set
 643case "$1" in
 644list)
 645        shift
 646        list_stash "$@"
 647        ;;
 648show)
 649        shift
 650        show_stash "$@"
 651        ;;
 652save)
 653        shift
 654        save_stash "$@"
 655        ;;
 656push)
 657        shift
 658        push_stash "$@"
 659        ;;
 660apply)
 661        shift
 662        apply_stash "$@"
 663        ;;
 664clear)
 665        shift
 666        clear_stash "$@"
 667        ;;
 668create)
 669        shift
 670        create_stash "$*" && echo "$w_commit"
 671        ;;
 672store)
 673        shift
 674        store_stash "$@"
 675        ;;
 676drop)
 677        shift
 678        drop_stash "$@"
 679        ;;
 680pop)
 681        shift
 682        pop_stash "$@"
 683        ;;
 684branch)
 685        shift
 686        apply_to_branch "$@"
 687        ;;
 688*)
 689        case $# in
 690        0)
 691                save_stash &&
 692                say "$(gettext "(To restore them type \"git stash apply\")")"
 693                ;;
 694        *)
 695                usage
 696        esac
 697        ;;
 698esac