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