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