1#!/bin/sh 2# Copyright (c) 2007, Nanako Shiraishi 3 4dashless=$(basename "$0" | sed -e 's/-/ /') 5USAGE="list [<options>] 6 or:$dashlessshow [<stash>] 7 or:$dashlessdrop [-q|--quiet] [<stash>] 8 or:$dashless( pop | apply ) [--index] [-q|--quiet] [<stash>] 9 or:$dashlessbranch <branchname> [<stash>] 10 or:$dashless[save [--patch] [-k|--[no-]keep-index] [-q|--quiet] 11 [-u|--include-untracked] [-a|--all] [<message>]] 12 or:$dashlesspush [--patch] [-k|--[no-]keep-index] [-q|--quiet] 13 [-u|--include-untracked] [-a|--all] [-m <message>] 14 or:$dashlessclear" 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 45test"$untracked"="all"&& excl_opt= 46 git ls-files -o -z$excl_opt 47} 48 49clear_stash () { 50iftest$#!=0 51then 52 die "$(gettext "git stash clear with parameters is unimplemented")" 53fi 54if current=$(git rev-parse --verify --quiet $ref_stash) 55then 56 git update-ref -d$ref_stash $current 57fi 58} 59 60create_stash () { 61 stash_msg= 62 untracked= 63whiletest$#!=0 64do 65case"$1"in 66-m|--message) 67shift 68 stash_msg=${1?"BUG: create_stash () -m requires an argument"} 69;; 70-u|--include-untracked) 71shift 72 untracked=${1?"BUG: create_stash () -u requires an argument"} 73;; 74esac 75shift 76done 77 78 git update-index -q --refresh 79if no_changes 80then 81exit0 82fi 83 84# state of the base commit 85if b_commit=$(git rev-parse --verify HEAD) 86then 87head=$(git rev-list --oneline -n 1 HEAD --) 88else 89 die "$(gettext "You do not have the initial commit yet")" 90fi 91 92if branch=$(git symbolic-ref -q HEAD) 93then 94 branch=${branch#refs/heads/} 95else 96 branch='(no branch)' 97fi 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 106iftest -n"$untracked" 107then 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"&& 113export GIT_INDEX_FILE && 114rm-f"$TMPindex"&& 115 git update-index -z --add --remove --stdin&& 116 u_tree=$(git write-tree)&& 117printf'untracked files on %s\n'"$msg"| git commit-tree$u_tree&& 118rm-f"$TMPindex" 119) ) || die "$(gettext "Cannot save the untracked files")" 120 121 untracked_commit_option="-p$u_commit"; 122else 123 untracked_commit_option= 124fi 125 126iftest -z"$patch_mode" 127then 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"&& 133export 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&& 137rm-f"$TMPindex" 138) ) || 139 die "$(gettext "Cannot save the current worktree state")" 140 141else 142 143rm-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"&& 155test -s"$TMP-patch"|| 156 die "$(gettext "No changes selected")" 157 158rm-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:$optionis 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'&& 565export 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 () { 630test -n"$1"|| die "$(gettext "No branch name specified")" 631 branch=$1 632shift1 633 634set -- --index"$@" 635 assert_stash_like "$@" 636 637 git checkout -b$branch $REV^ && 638 apply_stash "$@"&& { 639test -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 648case"$opt"in 649-*) ;; 650*) seen_non_option=t;break;; 651esac 652done 653 654test -n"$seen_non_option"||set"save""$@" 655 656# Main command set 657case"$1"in 658list) 659shift 660 list_stash "$@" 661;; 662show) 663shift 664 show_stash "$@" 665;; 666save) 667shift 668 save_stash "$@" 669;; 670push) 671shift 672 push_stash "$@" 673;; 674apply) 675shift 676 apply_stash "$@" 677;; 678clear) 679shift 680 clear_stash "$@" 681;; 682create) 683shift 684 create_stash -m"$*"&&echo"$w_commit" 685;; 686store) 687shift 688 store_stash "$@" 689;; 690drop) 691shift 692 drop_stash "$@" 693;; 694pop) 695shift 696 pop_stash "$@" 697;; 698branch) 699shift 700 apply_to_branch "$@" 701;; 702*) 703case$#in 7040) 705 save_stash && 706 say "$(gettext "(To restore them type \"git stash apply\")")" 707 ;; 708 *) 709 usage 710 esac 711 ;; 712esac