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:$dashlesssave [--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:$dashlessclear" 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 46test"$untracked"="all"&& excl_opt= 47 git ls-files -o -z$excl_opt--"$@" 48} 49 50clear_stash () { 51iftest$#!=0 52then 53 die "$(gettext "git stash clear with parameters is unimplemented")" 54fi 55if current=$(git rev-parse --verify --quiet $ref_stash) 56then 57 git update-ref -d$ref_stash $current 58fi 59} 60 61create_stash () { 62 stash_msg= 63 untracked= 64whiletest$#!=0 65do 66case"$1"in 67-m|--message) 68shift 69 stash_msg=${1?"BUG: create_stash () -m requires an argument"} 70;; 71-u|--include-untracked) 72shift 73 untracked=${1?"BUG: create_stash () -u requires an argument"} 74;; 75--) 76shift 77break 78;; 79esac 80shift 81done 82 83 git update-index -q --refresh 84if no_changes "$@" 85then 86exit0 87fi 88 89# state of the base commit 90if b_commit=$(git rev-parse --verify HEAD) 91then 92head=$(git rev-list --oneline -n 1 HEAD --) 93else 94 die "$(gettext "You do not have the initial commit yet")" 95fi 96 97if branch=$(git symbolic-ref -q HEAD) 98then 99 branch=${branch#refs/heads/} 100else 101 branch='(no branch)' 102fi 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 111iftest -n"$untracked" 112then 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"&& 118export GIT_INDEX_FILE && 119rm-f"$TMPindex"&& 120 git update-index -z --add --remove --stdin&& 121 u_tree=$(git write-tree)&& 122printf'untracked files on %s\n'"$msg"| git commit-tree$u_tree&& 123rm-f"$TMPindex" 124) ) || die "$(gettext "Cannot save the untracked files")" 125 126 untracked_commit_option="-p$u_commit"; 127else 128 untracked_commit_option= 129fi 130 131iftest -z"$patch_mode" 132then 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"&& 138export 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&& 142rm-f"$TMPindex" 143) ) || 144 die "$(gettext "Cannot save the current worktree state")" 145 146else 147 148rm-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"&& 160test -s"$TMP-patch"|| 161 die "$(gettext "No changes selected")" 162 163rm-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:$optionis 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'&& 581export 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 () { 646test -n"$1"|| die "$(gettext "No branch name specified")" 647 branch=$1 648shift1 649 650set -- --index"$@" 651 assert_stash_like "$@" 652 653 git checkout -b$branch $REV^ && 654 apply_stash "$@"&& { 655test -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 666case"$opt"in 667--)break;; 668-*) ;; 669*) seen_non_option=t;break;; 670esac 671done 672 673test -n"$seen_non_option"||set"push""$@" 674 675# Main command set 676case"$1"in 677list) 678shift 679 list_stash "$@" 680;; 681show) 682shift 683 show_stash "$@" 684;; 685save) 686shift 687 save_stash "$@" 688;; 689push) 690shift 691 push_stash "$@" 692;; 693apply) 694shift 695 apply_stash "$@" 696;; 697clear) 698shift 699 clear_stash "$@" 700;; 701create) 702shift 703 create_stash -m"$*"&&echo"$w_commit" 704;; 705store) 706shift 707 store_stash "$@" 708;; 709drop) 710shift 711 drop_stash "$@" 712;; 713pop) 714shift 715 pop_stash "$@" 716;; 717branch) 718shift 719 apply_to_branch "$@" 720;; 721*) 722case$#in 7230) 724 push_stash && 725 say "$(gettext "(To restore them type \"git stash apply\")")" 726 ;; 727 *) 728 usage 729 esac 730 ;; 731esac