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] [<message>]] 11 or:$dashlessclear" 12 13SUBDIRECTORY_OK=Yes 14OPTIONS_SPEC= 15. git-sh-setup 16require_work_tree 17cd_to_toplevel 18 19TMP="$GIT_DIR/.git-stash.$$" 20trap'rm -f "$TMP-*"'0 21 22ref_stash=refs/stash 23 24if git config --get-colorbool color.interactive;then 25 help_color="$(git config --get-color color.interactive.help 'red bold')" 26 reset_color="$(git config --get-color '' reset)" 27else 28 help_color= 29 reset_color= 30fi 31 32no_changes () { 33 git diff-index --quiet --cached HEAD --ignore-submodules --&& 34 git diff-files --quiet --ignore-submodules 35} 36 37clear_stash () { 38iftest$#!=0 39then 40 die "git stash clear with parameters is unimplemented" 41fi 42if current=$(git rev-parse --verify $ref_stash 2>/dev/null) 43then 44 git update-ref -d$ref_stash $current 45fi 46} 47 48create_stash () { 49 stash_msg="$1" 50 51 git update-index -q --refresh 52if no_changes 53then 54exit0 55fi 56 57# state of the base commit 58if b_commit=$(git rev-parse --verify HEAD) 59then 60head=$(git rev-list --oneline -n 1 HEAD --) 61else 62 die "You do not have the initial commit yet" 63fi 64 65if branch=$(git symbolic-ref -q HEAD) 66then 67 branch=${branch#refs/heads/} 68else 69 branch='(no branch)' 70fi 71 msg=$(printf '%s: %s' "$branch" "$head") 72 73# state of the index 74 i_tree=$(git write-tree)&& 75 i_commit=$(printf'index on %s\n'"$msg"| 76 git commit-tree$i_tree-p$b_commit) || 77 die "Cannot save the current index state" 78 79iftest -z"$patch_mode" 80then 81 82# state of the working tree 83 w_tree=$( ( 84rm-f"$TMP-index"&& 85cp-p${GIT_INDEX_FILE-"$GIT_DIR/index"}"$TMP-index"&& 86 GIT_INDEX_FILE="$TMP-index"&& 87export GIT_INDEX_FILE && 88 git read-tree -m$i_tree&& 89 git diff--name-only -z HEAD | git update-index -z --add --remove --stdin&& 90 git write-tree&& 91rm-f"$TMP-index" 92) ) || 93 die "Cannot save the current worktree state" 94 95else 96 97rm-f"$TMP-index"&& 98 GIT_INDEX_FILE="$TMP-index" git read-tree HEAD && 99 100# find out what the user wants 101 GIT_INDEX_FILE="$TMP-index" \ 102 git add--interactive --patch=stash --&& 103 104# state of the working tree 105 w_tree=$(GIT_INDEX_FILE="$TMP-index" git write-tree)|| 106 die "Cannot save the current worktree state" 107 108 git diff-tree -p HEAD $w_tree>"$TMP-patch"&& 109test -s"$TMP-patch"|| 110 die "No changes selected" 111 112rm-f"$TMP-index"|| 113 die "Cannot remove temporary index (can't happen)" 114 115fi 116 117# create the stash 118iftest -z"$stash_msg" 119then 120 stash_msg=$(printf 'WIP on %s' "$msg") 121else 122 stash_msg=$(printf 'On %s: %s' "$branch" "$stash_msg") 123fi 124 w_commit=$(printf'%s\n'"$stash_msg"| 125 git commit-tree$w_tree-p$b_commit-p$i_commit) || 126 die "Cannot record working tree state" 127} 128 129save_stash () { 130 keep_index= 131 patch_mode= 132whiletest$#!=0 133do 134case"$1"in 135-k|--keep-index) 136 keep_index=t 137;; 138--no-keep-index) 139 keep_index= 140;; 141-p|--patch) 142 patch_mode=t 143 keep_index=t 144;; 145-q|--quiet) 146 GIT_QUIET=t 147;; 148--) 149shift 150break 151;; 152-*) 153echo"error: unknown option for 'stash save':$1" 154echo" To provide a message, use git stash save -- '$1'" 155 usage 156;; 157*) 158break 159;; 160esac 161shift 162done 163 164 stash_msg="$*" 165 166 git update-index -q --refresh 167if no_changes 168then 169 say 'No local changes to save' 170exit0 171fi 172test -f"$GIT_DIR/logs/$ref_stash"|| 173 clear_stash || die "Cannot initialize stash" 174 175 create_stash "$stash_msg" 176 177# Make sure the reflog for stash is kept. 178: >>"$GIT_DIR/logs/$ref_stash" 179 180 git update-ref -m"$stash_msg"$ref_stash $w_commit|| 181 die "Cannot save the current status" 182 say Saved working directory and index state "$stash_msg" 183 184iftest -z"$patch_mode" 185then 186 git reset--hard${GIT_QUIET:+-q} 187 188iftest -n"$keep_index"&&test -n$i_tree 189then 190 git read-tree --reset -u$i_tree 191fi 192else 193 git apply -R<"$TMP-patch"|| 194 die "Cannot remove worktree changes" 195 196iftest -z"$keep_index" 197then 198 git reset 199fi 200fi 201} 202 203have_stash () { 204 git rev-parse --verify$ref_stash>/dev/null 2>&1 205} 206 207list_stash () { 208 have_stash ||return0 209 git log --format="%gd: %gs"-g"$@"$ref_stash-- 210} 211 212show_stash () { 213 have_stash || die 'No stash found' 214 215 flags=$(git rev-parse --no-revs --flags "$@") 216iftest -z"$flags" 217then 218 flags=--stat 219fi 220 221 w_commit=$(git rev-parse --quiet --verify --default $ref_stash "$@")&& 222 b_commit=$(git rev-parse --quiet --verify "$w_commit^")|| 223 die "'$*' is not a stash" 224 225 git diff$flags $b_commit $w_commit 226} 227 228# 229# Parses the remaining options looking for flags and 230# at most one revision defaulting to ${ref_stash}@{0} 231# if none found. 232# 233# Derives related tree and commit objects from the 234# revision, if one is found. 235# 236# stash records the work tree, and is a merge between the 237# base commit (first parent) and the index tree (second parent). 238# 239# REV is set to the symbolic version of the specified stash-like commit 240# IS_STASH_LIKE is non-blank if ${REV} looks like a stash 241# IS_STASH_REF is non-blank if the ${REV} looks like a stash ref 242# s is set to the SHA1 of the stash commit 243# w_commit is set to the commit containing the working tree 244# b_commit is set to the base commit 245# i_commit is set to the commit containing the index tree 246# w_tree is set to the working tree 247# b_tree is set to the base tree 248# i_tree is set to the index tree 249# 250# GIT_QUIET is set to t if -q is specified 251# INDEX_OPTION is set to --index if --index is specified. 252# FLAGS is set to the remaining flags 253# 254# dies if: 255# * too many revisions specified 256# * no revision is specified and there is no stash stack 257# * a revision is specified which cannot be resolve to a SHA1 258# * a non-existent stash reference is specified 259# 260 261parse_flags_and_rev() 262{ 263test"$PARSE_CACHE"="$*"&&return0# optimisation 264 PARSE_CACHE="$*" 265 266 IS_STASH_LIKE= 267 IS_STASH_REF= 268 INDEX_OPTION= 269 s= 270 w_commit= 271 b_commit= 272 i_commit= 273 w_tree= 274 b_tree= 275 i_tree= 276 277 REV=$(git rev-parse --no-flags --symbolic "$@" 2>/dev/null) 278 FLAGS=$(git rev-parse --no-revs -- "$@" 2>/dev/null) 279 280set --$FLAGS 281 282 FLAGS= 283whiletest$#-ne0 284do 285case"$1"in 286-q|--quiet) 287 GIT_QUIET=-t 288;; 289--index) 290 INDEX_OPTION=--index 291;; 292--) 293: 294;; 295*) 296 FLAGS="${FLAGS}${FLAGS:+ }$1" 297;; 298esac 299shift 300done 301 302set --$REV 303 304case$#in 3050) 306 have_stash || die "No stash found." 307set --${ref_stash}@{0} 308;; 3091) 310: 311;; 312*) 313 die "Too many revisions specified:$REV" 314;; 315esac 316 317 REV=$(git rev-parse --quiet --symbolic --verify $1 2>/dev/null)|| die "$1is not valid reference" 318 319 i_commit=$(git rev-parse --quiet --verify $REV^2 2>/dev/null)&& 320set --$(git rev-parse $REV $REV^1 $REV: $REV^1: $REV^2: 2>/dev/null)&& 321 s=$1&& 322 w_commit=$1&& 323 b_commit=$2&& 324 w_tree=$3&& 325 b_tree=$4&& 326 i_tree=$5&& 327 IS_STASH_LIKE=t && 328test"$ref_stash"="$(git rev-parse --symbolic-full-name "${REV%@*}")"&& 329 IS_STASH_REF=t 330 331iftest"${REV}"!="${REV%{*\}}" 332then 333# maintainers: it would be better if git rev-parse indicated 334# this condition with a non-zero status code but as of 1.7.2.1 it 335# it did not. So, we use non-empty stderr output as a proxy for the 336# condition of interest. 337test -z"$(git rev-parse "$REV" 2>&1 >/dev/null)"|| die "$REVdoes not exist in the stash log" 338fi 339 340} 341 342is_stash_like() 343{ 344 parse_flags_and_rev "$@" 345test -n"$IS_STASH_LIKE" 346} 347 348assert_stash_like() { 349 is_stash_like "$@"|| die "'$*' is not a stash-like commit" 350} 351 352is_stash_ref() { 353 is_stash_like "$@"&&test -n"$IS_STASH_REF" 354} 355 356assert_stash_ref() { 357 is_stash_ref "$@"|| die "'$*' is not a stash reference" 358} 359 360apply_stash () { 361 362 assert_stash_like "$@" 363 364 git update-index -q --refresh&& 365 git diff-files --quiet --ignore-submodules|| 366 die 'Cannot apply to a dirty working tree, please stage your changes' 367 368# current index state 369 c_tree=$(git write-tree)|| 370 die 'Cannot apply a stash in the middle of a merge' 371 372 unstashed_index_tree= 373iftest -n"$INDEX_OPTION"&&test"$b_tree"!="$i_tree"&& 374test"$c_tree"!="$i_tree" 375then 376 git diff-tree --binary$s^2^..$s^2| git apply --cached 377test $? -ne0&& 378 die 'Conflicts in index. Try without --index.' 379 unstashed_index_tree=$(git write-tree)|| 380 die 'Could not save index tree' 381 git reset 382fi 383 384eval" 385 GITHEAD_$w_tree='Stashed changes' && 386 GITHEAD_$c_tree='Updated upstream' && 387 GITHEAD_$b_tree='Version stash was based on' && 388 export GITHEAD_$w_treeGITHEAD_$c_treeGITHEAD_$b_tree 389 " 390 391iftest -n"$GIT_QUIET" 392then 393export GIT_MERGE_VERBOSITY=0 394fi 395if git merge-recursive$b_tree--$c_tree $w_tree 396then 397# No conflict 398iftest -n"$unstashed_index_tree" 399then 400 git read-tree"$unstashed_index_tree" 401else 402 a="$TMP-added"&& 403 git diff-index --cached --name-only --diff-filter=A $c_tree>"$a"&& 404 git read-tree --reset$c_tree&& 405 git update-index --add --stdin<"$a"|| 406 die "Cannot unstage modified files" 407rm-f"$a" 408fi 409 squelch= 410iftest -n"$GIT_QUIET" 411then 412 squelch='>/dev/null 2>&1' 413fi 414eval"git status$squelch"|| : 415else 416# Merge conflict; keep the exit status from merge-recursive 417 status=$? 418iftest -n"$INDEX_OPTION" 419then 420echo>&2'Index was not unstashed.' 421fi 422exit$status 423fi 424} 425 426drop_stash () { 427 assert_stash_ref "$@" 428 429 git reflog delete --updateref --rewrite"${REV}"&& 430 say "Dropped${REV}($s)"|| die "${REV}: Could not drop stash entry" 431 432# clear_stash if we just dropped the last stash entry 433 git rev-parse --verify"$ref_stash@{0}"> /dev/null 2>&1|| clear_stash 434} 435 436apply_to_branch () { 437 have_stash || die 'Nothing to apply' 438 439test -n"$1"|| die 'No branch name specified' 440 branch=$1 441 442iftest -z"$2" 443then 444set x "$ref_stash@{0}" 445fi 446 stash=$2 447 448 git checkout -b$branch $stash^ && 449 apply_stash --index$stash&& 450 drop_stash $stash 451} 452 453PARSE_CACHE='--not-parsed' 454# The default command is "save" if nothing but options are given 455seen_non_option= 456for opt 457do 458case"$opt"in 459-*) ;; 460*) seen_non_option=t;break;; 461esac 462done 463 464test -n"$seen_non_option"||set"save""$@" 465 466# Main command set 467case"$1"in 468list) 469shift 470 list_stash "$@" 471;; 472show) 473shift 474 show_stash "$@" 475;; 476save) 477shift 478 save_stash "$@" 479;; 480apply) 481shift 482 apply_stash "$@" 483;; 484clear) 485shift 486 clear_stash "$@" 487;; 488create) 489iftest$#-gt0&&test"$1"= create 490then 491shift 492fi 493 create_stash "$*"&&echo"$w_commit" 494;; 495drop) 496shift 497 drop_stash "$@" 498;; 499pop) 500shift 501if apply_stash "$@" 502then 503 drop_stash "$applied_stash" 504fi 505;; 506branch) 507shift 508 apply_to_branch "$@" 509;; 510*) 511case$#in 5120) 513 save_stash && 514 say '(To restore them type "git stash apply")' 515;; 516*) 517 usage 518esac 519;; 520esac