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:$dashlesssave [--patch] [-k|--[no-]keep-index] [-q|--quiet] 11 [-u|--include-untracked] [-a|--all] [<message>] 12 or:$dashless[push [--patch] [-k|--[no-]keep-index] [-q|--quiet] 13 [-u|--include-untracked] [-a|--all] [-m <message>] 14 [-- <pathspec>...]] 15 or:$dashlessclear" 16 17SUBDIRECTORY_OK=Yes 18OPTIONS_SPEC= 19START_DIR=$(pwd) 20. git-sh-setup 21require_work_tree 22prefix=$(git rev-parse --show-prefix)||exit1 23cd_to_toplevel 24 25TMP="$GIT_DIR/.git-stash.$$" 26TMPindex=${GIT_INDEX_FILE-"$(git rev-parse --git-path index)"}.stash.$$ 27trap'rm -f "$TMP-"* "$TMPindex"'0 28 29ref_stash=refs/stash 30 31if git config --get-colorbool color.interactive;then 32 help_color="$(git config --get-color color.interactive.help 'red bold')" 33 reset_color="$(git config --get-color '' reset)" 34else 35 help_color= 36 reset_color= 37fi 38 39no_changes () { 40 git diff-index --quiet --cached HEAD --ignore-submodules --"$@"&& 41 git diff-files --quiet --ignore-submodules --"$@"&& 42(test -z"$untracked"||test -z"$(untracked_files "$@")") 43} 44 45untracked_files () { 46iftest"$1"="-z" 47then 48shift 49 z=-z 50else 51 z= 52fi 53 excl_opt=--exclude-standard 54test"$untracked"="all"&& excl_opt= 55 git ls-files -o$z $excl_opt--"$@" 56} 57 58prepare_fallback_ident () { 59if! git -c user.useconfigonly=yes var GIT_COMMITTER_IDENT >/dev/null 2>&1 60then 61 GIT_AUTHOR_NAME="git stash" 62 GIT_AUTHOR_EMAIL=git@stash 63 GIT_COMMITTER_NAME="git stash" 64 GIT_COMMITTER_EMAIL=git@stash 65export GIT_AUTHOR_NAME 66export GIT_AUTHOR_EMAIL 67export GIT_COMMITTER_NAME 68export GIT_COMMITTER_EMAIL 69fi 70} 71 72clear_stash () { 73iftest$#!=0 74then 75 die "$(gettext "git stash clear with parameters is unimplemented")" 76fi 77if current=$(git rev-parse --verify --quiet $ref_stash) 78then 79 git update-ref -d$ref_stash $current 80fi 81} 82 83create_stash () { 84 85 prepare_fallback_ident 86 87 stash_msg= 88 untracked= 89whiletest$#!=0 90do 91case"$1"in 92-m|--message) 93shift 94 stash_msg=${1?"BUG: create_stash () -m requires an argument"} 95;; 96-m*) 97 stash_msg=${1#-m} 98;; 99--message=*) 100 stash_msg=${1#--message=} 101;; 102-u|--include-untracked) 103shift 104 untracked=${1?"BUG: create_stash () -u requires an argument"} 105;; 106--) 107shift 108break 109;; 110esac 111shift 112done 113 114 git update-index -q --refresh 115if no_changes "$@" 116then 117exit0 118fi 119 120# state of the base commit 121if b_commit=$(git rev-parse --verify HEAD) 122then 123head=$(git rev-list --oneline -n 1 HEAD --) 124else 125 die "$(gettext "You do not have the initial commit yet")" 126fi 127 128if branch=$(git symbolic-ref -q HEAD) 129then 130 branch=${branch#refs/heads/} 131else 132 branch='(no branch)' 133fi 134 msg=$(printf '%s: %s' "$branch" "$head") 135 136# state of the index 137 i_tree=$(git write-tree)&& 138 i_commit=$(printf'index on %s\n'"$msg"| 139 git commit-tree$i_tree-p$b_commit) || 140 die "$(gettext "Cannot save the current index state")" 141 142iftest -n"$untracked" 143then 144# Untracked files are stored by themselves in a parentless commit, for 145# ease of unpacking later. 146 u_commit=$( 147 untracked_files -z"$@"| ( 148 GIT_INDEX_FILE="$TMPindex"&& 149export GIT_INDEX_FILE && 150rm-f"$TMPindex"&& 151 git update-index -z --add --remove --stdin&& 152 u_tree=$(git write-tree)&& 153printf'untracked files on %s\n'"$msg"| git commit-tree$u_tree&& 154rm-f"$TMPindex" 155) ) || die "$(gettext "Cannot save the untracked files")" 156 157 untracked_commit_option="-p$u_commit"; 158else 159 untracked_commit_option= 160fi 161 162iftest -z"$patch_mode" 163then 164 165# state of the working tree 166 w_tree=$( ( 167 git read-tree --index-output="$TMPindex"-m$i_tree&& 168 GIT_INDEX_FILE="$TMPindex"&& 169export GIT_INDEX_FILE && 170 git diff-index --name-only -z HEAD --"$@">"$TMP-stagenames"&& 171 git update-index -z --add --remove --stdin<"$TMP-stagenames"&& 172 git write-tree&& 173rm-f"$TMPindex" 174) ) || 175 die "$(gettext "Cannot save the current worktree state")" 176 177else 178 179rm-f"$TMP-index"&& 180 GIT_INDEX_FILE="$TMP-index" git read-tree HEAD && 181 182# find out what the user wants 183 GIT_INDEX_FILE="$TMP-index" \ 184 git add--interactive --patch=stash --"$@"&& 185 186# state of the working tree 187 w_tree=$(GIT_INDEX_FILE="$TMP-index" git write-tree)|| 188 die "$(gettext "Cannot save the current worktree state")" 189 190 git diff-tree -p HEAD $w_tree-->"$TMP-patch"&& 191test -s"$TMP-patch"|| 192 die "$(gettext "No changes selected")" 193 194rm-f"$TMP-index"|| 195 die "$(gettext "Cannot remove temporary index (can't happen)")" 196 197 fi 198 199 # create the stash 200 if test -z "$stash_msg" 201 then 202 stash_msg=$(printf 'WIP on %s' "$msg") 203 else 204 stash_msg=$(printf 'On %s: %s' "$branch" "$stash_msg") 205 fi 206 w_commit=$(printf '%s\n' "$stash_msg" | 207 git commit-tree$w_tree-p$b_commit-p$i_commit$untracked_commit_option) || 208 die "$(gettext "Cannot record working tree state")" 209} 210 211push_stash () { 212 keep_index= 213 patch_mode= 214 untracked= 215 stash_msg= 216 while test$#!= 0 217 do 218 case "$1" in 219 -k|--keep-index) 220 keep_index=t 221 ;; 222 --no-keep-index) 223 keep_index=n 224 ;; 225 -p|--patch) 226 patch_mode=t 227 # only default to keep if we don't already have an override 228 test -z "$keep_index" && keep_index=t 229 ;; 230 -q|--quiet) 231 GIT_QUIET=t 232 ;; 233 -u|--include-untracked) 234 untracked=untracked 235 ;; 236 -a|--all) 237 untracked=all 238 ;; 239 -m|--message) 240 shift 241 test -z${1+x}&& usage 242 stash_msg=$1 243 ;; 244 -m*) 245 stash_msg=${1#-m} 246 ;; 247 --message=*) 248 stash_msg=${1#--message=} 249 ;; 250 --help) 251 show_help 252 ;; 253 --) 254 shift 255 break 256 ;; 257 -*) 258 option="$1" 259 eval_gettextln "error: unknown option for'stash push': \$option" 260 usage 261 ;; 262 *) 263 break 264 ;; 265 esac 266 shift 267 done 268 269 eval "set$(git rev-parse --sq --prefix "$prefix" -- "$@")" 270 271 if test -n "$patch_mode" && test -n "$untracked" 272 then 273 die "$(gettext "Can't use --patch and --include-untracked or --all at the same time")" 274 fi 275 276 test -n "$untracked" || git ls-files --error-unmatch -- "$@" >/dev/null || exit 1 277 278 git update-index -q --refresh 279 if no_changes "$@" 280 then 281 say "$(gettext "No local changes to save")" 282 exit 0 283 fi 284 285 git reflog exists$ref_stash|| 286 clear_stash || die "$(gettext "Cannot initialize stash")" 287 288 create_stash -m "$stash_msg" -u "$untracked" -- "$@" 289 git stash--helper store -m "$stash_msg" -q$w_commit|| 290 die "$(gettext "Cannot save the current status")" 291 say "$(eval_gettext "Saved working directory and index state \$stash_msg")" 292 293 if test -z "$patch_mode" 294 then 295 test "$untracked" = "all" && CLEAN_X_OPTION=-x || CLEAN_X_OPTION= 296 if test -n "$untracked" && test$#= 0 297 then 298 git clean --force --quiet -d$CLEAN_X_OPTION 299 fi 300 301 if test$#!= 0 302 then 303 test -z "$untracked" && UPDATE_OPTION="-u" || UPDATE_OPTION= 304 test "$untracked" = "all" && FORCE_OPTION="--force" || FORCE_OPTION= 305 git add$UPDATE_OPTION$FORCE_OPTION-- "$@" 306 git diff-index -p --cached --binary HEAD -- "$@" | 307 git apply --index -R 308 else 309 git reset --hard -q 310 fi 311 312 if test "$keep_index" = "t" && test -n "$i_tree" 313 then 314 git read-tree --reset$i_tree 315 git ls-files -z --modified -- "$@" | 316 git checkout-index -z --force --stdin 317 fi 318 else 319 git apply -R < "$TMP-patch" || 320 die "$(gettext "Cannot remove worktree changes")" 321 322 if test "$keep_index" != "t" 323 then 324 git reset -q -- "$@" 325 fi 326 fi 327} 328 329save_stash () { 330 push_options= 331 while test$#!= 0 332 do 333 case "$1" in 334 --) 335 shift 336 break 337 ;; 338 -*) 339 # pass all options through to push_stash 340 push_options="$push_options $1" 341 ;; 342 *) 343 break 344 ;; 345 esac 346 shift 347 done 348 349 stash_msg="$*" 350 351 if test -z "$stash_msg" 352 then 353 push_stash$push_options 354 else 355 push_stash$push_options-m "$stash_msg" 356 fi 357} 358 359show_help () { 360 exec git help stash 361 exit 1 362} 363 364# 365# Parses the remaining options looking for flags and 366# at most one revision defaulting to${ref_stash}@{0} 367# if none found. 368# 369# Derives related tree and commit objects from the 370# revision, if one is found. 371# 372# stash records the work tree, and is a merge between the 373# base commit (first parent) and the index tree (second parent). 374# 375# REV is set to the symbolic version of the specified stash-like commit 376# IS_STASH_LIKE is non-blank if${REV}looks like a stash 377# IS_STASH_REF is non-blank if the${REV}looks like a stash ref 378# s is set to the SHA1 of the stash commit 379# w_commit is set to the commit containing the working tree 380# b_commit is set to the base commit 381# i_commit is set to the commit containing the index tree 382# u_commit is set to the commit containing the untracked files tree 383# w_tree is set to the working tree 384# b_tree is set to the base tree 385# i_tree is set to the index tree 386# u_tree is set to the untracked files tree 387# 388# GIT_QUIET is set to t if -q is specified 389# INDEX_OPTION is set to --index if --index is specified. 390# FLAGS is set to the remaining flags (if allowed) 391# 392# dies if: 393# * too many revisions specified 394# * no revision is specified and there is no stash stack 395# * a revision is specified which cannot be resolve to a SHA1 396# * a non-existent stash reference is specified 397# * unknown flags were set and ALLOW_UNKNOWN_FLAGS is not "t" 398# 399 400test "$1" = "-p" && set "push" "$@" 401 402PARSE_CACHE='--not-parsed' 403# The default command is "push" if nothing but options are given 404seen_non_option= 405for opt 406do 407 case "$opt" in 408 --) break ;; 409 -*) ;; 410 *) seen_non_option=t; break ;; 411 esac 412done 413 414test -n "$seen_non_option" || set "push" "$@" 415 416# Main command set 417case "$1" in 418list) 419 shift 420 git stash--helper list "$@" 421 ;; 422show) 423 shift 424 git stash--helper show "$@" 425 ;; 426save) 427 shift 428 save_stash "$@" 429 ;; 430push) 431 shift 432 cd "$START_DIR" 433 git stash--helper push "$@" 434 ;; 435apply) 436 shift 437 cd "$START_DIR" 438 git stash--helper apply "$@" 439 ;; 440clear) 441 shift 442 git stash--helper clear "$@" 443 ;; 444create) 445 shift 446 git stash--helper create --message "$*" 447 ;; 448store) 449 shift 450 git stash--helper store "$@" 451 ;; 452drop) 453 shift 454 git stash--helper drop "$@" 455 ;; 456pop) 457 shift 458 cd "$START_DIR" 459 git stash--helper pop "$@" 460 ;; 461branch) 462 shift 463 cd "$START_DIR" 464 git stash--helper branch "$@" 465 ;; 466*) 467 case$#in 468 0) 469 cd "$START_DIR" 470 git stash--helper push && 471 say "$(gettext "(To restore them type \"git stash apply\")")" 472;; 473*) 474 usage 475esac 476;; 477esac