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