git-stash.shon commit Merge branch 'js/stash-dwim' into tr/reset-checkout-patch (367ea19)
   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 [-k|--keep-index] [-q|--quiet] [<message>]]
  11   or: $dashless [-k|--keep-index]
  12   or: $dashless clear"
  13
  14SUBDIRECTORY_OK=Yes
  15OPTIONS_SPEC=
  16. git-sh-setup
  17require_work_tree
  18cd_to_toplevel
  19
  20TMP="$GIT_DIR/.git-stash.$$"
  21trap 'rm -f "$TMP-*"' 0
  22
  23ref_stash=refs/stash
  24
  25if git config --get-colorbool color.interactive; then
  26       help_color="$(git config --get-color color.interactive.help 'red bold')"
  27       reset_color="$(git config --get-color '' reset)"
  28else
  29       help_color=
  30       reset_color=
  31fi
  32
  33no_changes () {
  34        git diff-index --quiet --cached HEAD --ignore-submodules -- &&
  35        git diff-files --quiet --ignore-submodules
  36}
  37
  38clear_stash () {
  39        if test $# != 0
  40        then
  41                die "git stash clear with parameters is unimplemented"
  42        fi
  43        if current=$(git rev-parse --verify $ref_stash 2>/dev/null)
  44        then
  45                git update-ref -d $ref_stash $current
  46        fi
  47}
  48
  49create_stash () {
  50        stash_msg="$1"
  51
  52        git update-index -q --refresh
  53        if no_changes
  54        then
  55                exit 0
  56        fi
  57
  58        # state of the base commit
  59        if b_commit=$(git rev-parse --verify HEAD)
  60        then
  61                head=$(git log --no-color --abbrev-commit --pretty=oneline -n 1 HEAD --)
  62        else
  63                die "You do not have the initial commit yet"
  64        fi
  65
  66        if branch=$(git symbolic-ref -q HEAD)
  67        then
  68                branch=${branch#refs/heads/}
  69        else
  70                branch='(no branch)'
  71        fi
  72        msg=$(printf '%s: %s' "$branch" "$head")
  73
  74        # state of the index
  75        i_tree=$(git write-tree) &&
  76        i_commit=$(printf 'index on %s\n' "$msg" |
  77                git commit-tree $i_tree -p $b_commit) ||
  78                die "Cannot save the current index state"
  79
  80        if test -z "$patch_mode"
  81        then
  82
  83                # state of the working tree
  84                w_tree=$( (
  85                        rm -f "$TMP-index" &&
  86                        cp -p ${GIT_INDEX_FILE-"$GIT_DIR/index"} "$TMP-index" &&
  87                        GIT_INDEX_FILE="$TMP-index" &&
  88                        export GIT_INDEX_FILE &&
  89                        git read-tree -m $i_tree &&
  90                        git add -u &&
  91                        git write-tree &&
  92                        rm -f "$TMP-index"
  93                ) ) ||
  94                        die "Cannot save the current worktree state"
  95
  96        else
  97
  98                rm -f "$TMP-index" &&
  99                GIT_INDEX_FILE="$TMP-index" git read-tree HEAD &&
 100
 101                # find out what the user wants
 102                GIT_INDEX_FILE="$TMP-index" \
 103                        git add--interactive --patch=stash -- &&
 104
 105                # state of the working tree
 106                w_tree=$(GIT_INDEX_FILE="$TMP-index" git write-tree) ||
 107                die "Cannot save the current worktree state"
 108
 109                git diff-tree -p HEAD $w_tree > "$TMP-patch" &&
 110                test -s "$TMP-patch" ||
 111                die "No changes selected"
 112
 113                rm -f "$TMP-index" ||
 114                die "Cannot remove temporary index (can't happen)"
 115
 116        fi
 117
 118        # create the stash
 119        if test -z "$stash_msg"
 120        then
 121                stash_msg=$(printf 'WIP on %s' "$msg")
 122        else
 123                stash_msg=$(printf 'On %s: %s' "$branch" "$stash_msg")
 124        fi
 125        w_commit=$(printf '%s\n' "$stash_msg" |
 126                git commit-tree $w_tree -p $b_commit -p $i_commit) ||
 127                die "Cannot record working tree state"
 128}
 129
 130save_stash () {
 131        keep_index=
 132        patch_mode=
 133        while test $# != 0
 134        do
 135                case "$1" in
 136                -k|--keep-index)
 137                        keep_index=t
 138                        ;;
 139                --no-keep-index)
 140                        keep_index=
 141                        ;;
 142                -p|--patch)
 143                        patch_mode=t
 144                        keep_index=t
 145                        ;;
 146                -q|--quiet)
 147                        GIT_QUIET=t
 148                        ;;
 149                *)
 150                        break
 151                        ;;
 152                esac
 153                shift
 154        done
 155
 156        stash_msg="$*"
 157
 158        git update-index -q --refresh
 159        if no_changes
 160        then
 161                say 'No local changes to save'
 162                exit 0
 163        fi
 164        test -f "$GIT_DIR/logs/$ref_stash" ||
 165                clear_stash || die "Cannot initialize stash"
 166
 167        create_stash "$stash_msg"
 168
 169        # Make sure the reflog for stash is kept.
 170        : >>"$GIT_DIR/logs/$ref_stash"
 171
 172        git update-ref -m "$stash_msg" $ref_stash $w_commit ||
 173                die "Cannot save the current status"
 174        say Saved working directory and index state "$stash_msg"
 175
 176        if test -z "$patch_mode"
 177        then
 178                git reset --hard ${GIT_QUIET:+-q}
 179
 180                if test -n "$keep_index" && test -n $i_tree
 181                then
 182                        git read-tree --reset -u $i_tree
 183                fi
 184        else
 185                git apply -R < "$TMP-patch" ||
 186                die "Cannot remove worktree changes"
 187
 188                if test -z "$keep_index"
 189                then
 190                        git reset
 191                fi
 192        fi
 193}
 194
 195have_stash () {
 196        git rev-parse --verify $ref_stash >/dev/null 2>&1
 197}
 198
 199list_stash () {
 200        have_stash || return 0
 201        git log --no-color --pretty=oneline -g "$@" $ref_stash -- |
 202        sed -n -e 's/^[.0-9a-f]* refs\///p'
 203}
 204
 205show_stash () {
 206        flags=$(git rev-parse --no-revs --flags "$@")
 207        if test -z "$flags"
 208        then
 209                flags=--stat
 210        fi
 211
 212        w_commit=$(git rev-parse --verify --default $ref_stash "$@") &&
 213        b_commit=$(git rev-parse --verify "$w_commit^") &&
 214        git diff $flags $b_commit $w_commit
 215}
 216
 217apply_stash () {
 218        git update-index -q --refresh &&
 219        git diff-files --quiet --ignore-submodules ||
 220                die 'Cannot apply to a dirty working tree, please stage your changes'
 221
 222        unstash_index=
 223
 224        while test $# != 0
 225        do
 226                case "$1" in
 227                --index)
 228                        unstash_index=t
 229                        ;;
 230                -q|--quiet)
 231                        GIT_QUIET=t
 232                        ;;
 233                *)
 234                        break
 235                        ;;
 236                esac
 237                shift
 238        done
 239
 240        # current index state
 241        c_tree=$(git write-tree) ||
 242                die 'Cannot apply a stash in the middle of a merge'
 243
 244        # stash records the work tree, and is a merge between the
 245        # base commit (first parent) and the index tree (second parent).
 246        s=$(git rev-parse --verify --default $ref_stash "$@") &&
 247        w_tree=$(git rev-parse --verify "$s:") &&
 248        b_tree=$(git rev-parse --verify "$s^1:") &&
 249        i_tree=$(git rev-parse --verify "$s^2:") ||
 250                die "$*: no valid stashed state found"
 251
 252        unstashed_index_tree=
 253        if test -n "$unstash_index" && test "$b_tree" != "$i_tree" &&
 254                        test "$c_tree" != "$i_tree"
 255        then
 256                git diff-tree --binary $s^2^..$s^2 | git apply --cached
 257                test $? -ne 0 &&
 258                        die 'Conflicts in index. Try without --index.'
 259                unstashed_index_tree=$(git write-tree) ||
 260                        die 'Could not save index tree'
 261                git reset
 262        fi
 263
 264        eval "
 265                GITHEAD_$w_tree='Stashed changes' &&
 266                GITHEAD_$c_tree='Updated upstream' &&
 267                GITHEAD_$b_tree='Version stash was based on' &&
 268                export GITHEAD_$w_tree GITHEAD_$c_tree GITHEAD_$b_tree
 269        "
 270
 271        if test -n "$GIT_QUIET"
 272        then
 273                export GIT_MERGE_VERBOSITY=0
 274        fi
 275        if git merge-recursive $b_tree -- $c_tree $w_tree
 276        then
 277                # No conflict
 278                if test -n "$unstashed_index_tree"
 279                then
 280                        git read-tree "$unstashed_index_tree"
 281                else
 282                        a="$TMP-added" &&
 283                        git diff-index --cached --name-only --diff-filter=A $c_tree >"$a" &&
 284                        git read-tree --reset $c_tree &&
 285                        git update-index --add --stdin <"$a" ||
 286                                die "Cannot unstage modified files"
 287                        rm -f "$a"
 288                fi
 289                squelch=
 290                if test -n "$GIT_QUIET"
 291                then
 292                        squelch='>/dev/null 2>&1'
 293                fi
 294                eval "git status $squelch" || :
 295        else
 296                # Merge conflict; keep the exit status from merge-recursive
 297                status=$?
 298                if test -n "$unstash_index"
 299                then
 300                        echo >&2 'Index was not unstashed.'
 301                fi
 302                exit $status
 303        fi
 304}
 305
 306drop_stash () {
 307        have_stash || die 'No stash entries to drop'
 308
 309        while test $# != 0
 310        do
 311                case "$1" in
 312                -q|--quiet)
 313                        GIT_QUIET=t
 314                        ;;
 315                *)
 316                        break
 317                        ;;
 318                esac
 319                shift
 320        done
 321
 322        if test $# = 0
 323        then
 324                set x "$ref_stash@{0}"
 325                shift
 326        fi
 327        # Verify supplied argument looks like a stash entry
 328        s=$(git rev-parse --verify "$@") &&
 329        git rev-parse --verify "$s:"   > /dev/null 2>&1 &&
 330        git rev-parse --verify "$s^1:" > /dev/null 2>&1 &&
 331        git rev-parse --verify "$s^2:" > /dev/null 2>&1 ||
 332                die "$*: not a valid stashed state"
 333
 334        git reflog delete --updateref --rewrite "$@" &&
 335                say "Dropped $* ($s)" || die "$*: Could not drop stash entry"
 336
 337        # clear_stash if we just dropped the last stash entry
 338        git rev-parse --verify "$ref_stash@{0}" > /dev/null 2>&1 || clear_stash
 339}
 340
 341apply_to_branch () {
 342        have_stash || die 'Nothing to apply'
 343
 344        test -n "$1" || die 'No branch name specified'
 345        branch=$1
 346
 347        if test -z "$2"
 348        then
 349                set x "$ref_stash@{0}"
 350        fi
 351        stash=$2
 352
 353        git checkout -b $branch $stash^ &&
 354        apply_stash --index $stash &&
 355        drop_stash $stash
 356}
 357
 358# Main command set
 359case "$1" in
 360list)
 361        shift
 362        if test $# = 0
 363        then
 364                set x -n 10
 365                shift
 366        fi
 367        list_stash "$@"
 368        ;;
 369show)
 370        shift
 371        show_stash "$@"
 372        ;;
 373save)
 374        shift
 375        save_stash "$@"
 376        ;;
 377apply)
 378        shift
 379        apply_stash "$@"
 380        ;;
 381clear)
 382        shift
 383        clear_stash "$@"
 384        ;;
 385create)
 386        if test $# -gt 0 && test "$1" = create
 387        then
 388                shift
 389        fi
 390        create_stash "$*" && echo "$w_commit"
 391        ;;
 392drop)
 393        shift
 394        drop_stash "$@"
 395        ;;
 396pop)
 397        shift
 398        if apply_stash "$@"
 399        then
 400                test -z "$unstash_index" || shift
 401                drop_stash "$@"
 402        fi
 403        ;;
 404branch)
 405        shift
 406        apply_to_branch "$@"
 407        ;;
 408*)
 409        case $#,"$1" in
 410        0,|1,-k|1,--keep-index)
 411                save_stash "$@" &&
 412                say '(To restore them type "git stash apply")'
 413                ;;
 414        *)
 415                usage
 416        esac
 417        ;;
 418esac