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