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