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