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