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