]> git.ipfire.org Git - thirdparty/git.git/blame - git-stash.sh
stash: convert create to builtin
[thirdparty/git.git] / git-stash.sh
CommitLineData
f2c66ed1
NS
1#!/bin/sh
2# Copyright (c) 2007, Nanako Shiraishi
3
a5ab00c5
SB
4dashless=$(basename "$0" | sed -e 's/-/ /')
5USAGE="list [<options>]
fcdd0e92
SB
6 or: $dashless show [<stash>]
7 or: $dashless drop [-q|--quiet] [<stash>]
8 or: $dashless ( pop | apply ) [--index] [-q|--quiet] [<stash>]
a5ab00c5 9 or: $dashless branch <branchname> [<stash>]
1ada5020
TG
10 or: $dashless save [--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>...]]
a5ab00c5 15 or: $dashless clear"
f2c66ed1 16
3cd2491a 17SUBDIRECTORY_OK=Yes
8f321a39 18OPTIONS_SPEC=
d0ea45bf 19START_DIR=$(pwd)
f2c66ed1
NS
20. git-sh-setup
21require_work_tree
22fc703e 22prefix=$(git rev-parse --show-prefix) || exit 1
ceff079b 23cd_to_toplevel
f2c66ed1
NS
24
25TMP="$GIT_DIR/.git-stash.$$"
c697b577 26TMPindex=${GIT_INDEX_FILE-"$(git rev-parse --git-path index)"}.stash.$$
3ba2e865 27trap 'rm -f "$TMP-"* "$TMPindex"' 0
f2c66ed1
NS
28
29ref_stash=refs/stash
30
dda1f2a5
TR
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
f2c66ed1 39no_changes () {
df6bba09
TG
40 git diff-index --quiet --cached HEAD --ignore-submodules -- "$@" &&
41 git diff-files --quiet --ignore-submodules -- "$@" &&
d319bb18 42 (test -z "$untracked" || test -z "$(untracked_files "$@")")
78751302
DC
43}
44
45untracked_files () {
5fc92f88
KD
46 if test "$1" = "-z"
47 then
48 shift
49 z=-z
50 else
51 z=
52 fi
78751302
DC
53 excl_opt=--exclude-standard
54 test "$untracked" = "all" && excl_opt=
5fc92f88 55 git ls-files -o $z $excl_opt -- "$@"
f2c66ed1
NS
56}
57
3bc2111f
SD
58prepare_fallback_ident () {
59 if ! git -c user.useconfigonly=yes var GIT_COMMITTER_IDENT >/dev/null 2>&1
60 then
61 GIT_AUTHOR_NAME="git stash"
62 GIT_AUTHOR_EMAIL=git@stash
63 GIT_COMMITTER_NAME="git stash"
64 GIT_COMMITTER_EMAIL=git@stash
65 export GIT_AUTHOR_NAME
66 export GIT_AUTHOR_EMAIL
67 export GIT_COMMITTER_NAME
68 export GIT_COMMITTER_EMAIL
69 fi
70}
71
f2c66ed1 72clear_stash () {
3023dc69
JH
73 if test $# != 0
74 then
b1440ce2 75 die "$(gettext "git stash clear with parameters is unimplemented")"
3023dc69 76 fi
1956dfa8 77 if current=$(git rev-parse --verify --quiet $ref_stash)
7ab3cc70 78 then
a9ee9bf9 79 git update-ref -d $ref_stash $current
7ab3cc70 80 fi
f2c66ed1
NS
81}
82
bc9e7399 83create_stash () {
3bc2111f
SD
84
85 prepare_fallback_ident
86
9ca6326d
TG
87 stash_msg=
88 untracked=
89 while test $# != 0
90 do
91 case "$1" in
92 -m|--message)
93 shift
94 stash_msg=${1?"BUG: create_stash () -m requires an argument"}
95 ;;
5675473f
PH
96 -m*)
97 stash_msg=${1#-m}
98 ;;
99 --message=*)
100 stash_msg=${1#--message=}
101 ;;
9ca6326d
TG
102 -u|--include-untracked)
103 shift
104 untracked=${1?"BUG: create_stash () -u requires an argument"}
105 ;;
df6bba09
TG
106 --)
107 shift
108 break
109 ;;
9ca6326d
TG
110 esac
111 shift
112 done
9f62e18a 113
1eff26c0 114 git update-index -q --refresh
df6bba09 115 if no_changes "$@"
f2c66ed1 116 then
f2c66ed1
NS
117 exit 0
118 fi
f12e925a 119
f2c66ed1 120 # state of the base commit
5be60078 121 if b_commit=$(git rev-parse --verify HEAD)
f2c66ed1 122 then
b0e621ad 123 head=$(git rev-list --oneline -n 1 HEAD --)
f2c66ed1 124 else
b1440ce2 125 die "$(gettext "You do not have the initial commit yet")"
f2c66ed1
NS
126 fi
127
5be60078 128 if branch=$(git symbolic-ref -q HEAD)
f2c66ed1
NS
129 then
130 branch=${branch#refs/heads/}
131 else
132 branch='(no branch)'
133 fi
134 msg=$(printf '%s: %s' "$branch" "$head")
135
136 # state of the index
5be60078 137 i_tree=$(git write-tree) &&
6143fa2c 138 i_commit=$(printf 'index on %s\n' "$msg" |
5be60078 139 git commit-tree $i_tree -p $b_commit) ||
b1440ce2 140 die "$(gettext "Cannot save the current index state")"
f2c66ed1 141
78751302
DC
142 if test -n "$untracked"
143 then
144 # Untracked files are stored by themselves in a parentless commit, for
145 # ease of unpacking later.
146 u_commit=$(
5fc92f88 147 untracked_files -z "$@" | (
bed137d2
EP
148 GIT_INDEX_FILE="$TMPindex" &&
149 export GIT_INDEX_FILE &&
78751302
DC
150 rm -f "$TMPindex" &&
151 git update-index -z --add --remove --stdin &&
152 u_tree=$(git write-tree) &&
153 printf 'untracked files on %s\n' "$msg" | git commit-tree $u_tree &&
154 rm -f "$TMPindex"
850251f3 155 ) ) || die "$(gettext "Cannot save the untracked files")"
78751302
DC
156
157 untracked_commit_option="-p $u_commit";
158 else
159 untracked_commit_option=
160 fi
161
dda1f2a5
TR
162 if test -z "$patch_mode"
163 then
164
165 # state of the working tree
166 w_tree=$( (
3ba2e865
JS
167 git read-tree --index-output="$TMPindex" -m $i_tree &&
168 GIT_INDEX_FILE="$TMPindex" &&
dda1f2a5 169 export GIT_INDEX_FILE &&
df6bba09 170 git diff-index --name-only -z HEAD -- "$@" >"$TMP-stagenames" &&
44df2e29 171 git update-index -z --add --remove --stdin <"$TMP-stagenames" &&
dda1f2a5 172 git write-tree &&
3ba2e865 173 rm -f "$TMPindex"
dda1f2a5 174 ) ) ||
b1440ce2 175 die "$(gettext "Cannot save the current worktree state")"
dda1f2a5
TR
176
177 else
178
b24f56d6 179 rm -f "$TMP-index" &&
dda1f2a5
TR
180 GIT_INDEX_FILE="$TMP-index" git read-tree HEAD &&
181
182 # find out what the user wants
183 GIT_INDEX_FILE="$TMP-index" \
df6bba09 184 git add--interactive --patch=stash -- "$@" &&
dda1f2a5
TR
185
186 # state of the working tree
187 w_tree=$(GIT_INDEX_FILE="$TMP-index" git write-tree) ||
b1440ce2 188 die "$(gettext "Cannot save the current worktree state")"
f2c66ed1 189
44df2e29 190 git diff-tree -p HEAD $w_tree -- >"$TMP-patch" &&
dda1f2a5 191 test -s "$TMP-patch" ||
b1440ce2 192 die "$(gettext "No changes selected")"
dda1f2a5
TR
193
194 rm -f "$TMP-index" ||
b1440ce2 195 die "$(gettext "Cannot remove temporary index (can't happen)")"
dda1f2a5
TR
196
197 fi
198
f2c66ed1 199 # create the stash
9f62e18a
JH
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" |
22f41286
JH
207 git commit-tree $w_tree -p $b_commit -p $i_commit $untracked_commit_option) ||
208 die "$(gettext "Cannot record working tree state")"
bc9e7399 209}
f2c66ed1 210
f5727e26 211push_stash () {
7bedebca 212 keep_index=
dda1f2a5 213 patch_mode=
78751302 214 untracked=
f5727e26 215 stash_msg=
fcdd0e92
SB
216 while test $# != 0
217 do
218 case "$1" in
ea41cfc4 219 -k|--keep-index)
fcdd0e92
SB
220 keep_index=t
221 ;;
dda1f2a5 222 --no-keep-index)
3a243f70 223 keep_index=n
dda1f2a5
TR
224 ;;
225 -p|--patch)
226 patch_mode=t
3a243f70
DM
227 # only default to keep if we don't already have an override
228 test -z "$keep_index" && keep_index=t
fcdd0e92
SB
229 ;;
230 -q|--quiet)
231 GIT_QUIET=t
232 ;;
78751302
DC
233 -u|--include-untracked)
234 untracked=untracked
235 ;;
236 -a|--all)
237 untracked=all
238 ;;
f5727e26
TG
239 -m|--message)
240 shift
241 test -z ${1+x} && usage
242 stash_msg=$1
243 ;;
5675473f
PH
244 -m*)
245 stash_msg=${1#-m}
246 ;;
247 --message=*)
248 stash_msg=${1#--message=}
249 ;;
5ba28313
JK
250 --help)
251 show_help
252 ;;
3c2eb80f
MM
253 --)
254 shift
255 break
256 ;;
257 -*)
eed10649 258 option="$1"
c0c0c825 259 eval_gettextln "error: unknown option for 'stash push': \$option"
3c2eb80f
MM
260 usage
261 ;;
fcdd0e92
SB
262 *)
263 break
264 ;;
265 esac
7bedebca 266 shift
fcdd0e92 267 done
7bedebca 268
22fc703e
PS
269 eval "set $(git rev-parse --sq --prefix "$prefix" -- "$@")"
270
78751302
DC
271 if test -n "$patch_mode" && test -n "$untracked"
272 then
850251f3 273 die "$(gettext "Can't use --patch and --include-untracked or --all at the same time")"
78751302
DC
274 fi
275
df6bba09
TG
276 test -n "$untracked" || git ls-files --error-unmatch -- "$@" >/dev/null || exit 1
277
1eff26c0 278 git update-index -q --refresh
df6bba09 279 if no_changes "$@"
bc9e7399 280 then
5a175871 281 say "$(gettext "No local changes to save")"
bc9e7399
JH
282 exit 0
283 fi
df6bba09 284
89dea973 285 git reflog exists $ref_stash ||
b1440ce2 286 clear_stash || die "$(gettext "Cannot initialize stash")"
bc9e7399 287
df6bba09 288 create_stash -m "$stash_msg" -u "$untracked" -- "$@"
41e0dd55 289 git stash--helper store -m "$stash_msg" -q $w_commit ||
bd514cad 290 die "$(gettext "Cannot save the current status")"
599e7a0b 291 say "$(eval_gettext "Saved working directory and index state \$stash_msg")"
7bedebca 292
dda1f2a5 293 if test -z "$patch_mode"
7bedebca 294 then
bbffd87d 295 test "$untracked" = "all" && CLEAN_X_OPTION=-x || CLEAN_X_OPTION=
833622a9 296 if test -n "$untracked" && test $# = 0
bbffd87d 297 then
35327868 298 git clean --force --quiet -d $CLEAN_X_OPTION
bbffd87d
NMC
299 fi
300
df6bba09
TG
301 if test $# != 0
302 then
833622a9
TG
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 -- "$@"
d97e4fa7
JH
306 git diff-index -p --cached --binary HEAD -- "$@" |
307 git apply --index -R
df6bba09 308 else
1790f4fe 309 git reset --hard -q
df6bba09 310 fi
dda1f2a5 311
268ef4d3 312 if test "$keep_index" = "t" && test -n "$i_tree"
dda1f2a5 313 then
e0e7f99e
TG
314 git read-tree --reset $i_tree
315 git ls-files -z --modified -- "$@" |
316 git checkout-index -z --force --stdin
dda1f2a5
TR
317 fi
318 else
319 git apply -R < "$TMP-patch" ||
b1440ce2 320 die "$(gettext "Cannot remove worktree changes")"
dda1f2a5 321
3a243f70 322 if test "$keep_index" != "t"
dda1f2a5 323 then
869fb8f7 324 git reset -q -- "$@"
dda1f2a5 325 fi
7bedebca 326 fi
f2c66ed1
NS
327}
328
f5727e26
TG
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
5ba28313
JK
359show_help () {
360 exec git help stash
361 exit 1
362}
363
ef763129
JS
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
78751302 382# u_commit is set to the commit containing the untracked files tree
ef763129
JS
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
78751302 386# u_tree is set to the untracked files tree
ef763129
JS
387#
388# GIT_QUIET is set to t if -q is specified
389# INDEX_OPTION is set to --index if --index is specified.
d6cc2df5 390# FLAGS is set to the remaining flags (if allowed)
ef763129
JS
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
d6cc2df5 397# * unknown flags were set and ALLOW_UNKNOWN_FLAGS is not "t"
ef763129
JS
398#
399
9e140909
TG
400test "$1" = "-p" && set "push" "$@"
401
ef763129 402PARSE_CACHE='--not-parsed'
1ada5020 403# The default command is "push" if nothing but options are given
3c2eb80f
MM
404seen_non_option=
405for opt
406do
407 case "$opt" in
9e140909 408 --) break ;;
3c2eb80f
MM
409 -*) ;;
410 *) seen_non_option=t; break ;;
411 esac
412done
413
1ada5020 414test -n "$seen_non_option" || set "push" "$@"
3c2eb80f 415
f2c66ed1
NS
416# Main command set
417case "$1" in
fcb10a96
JH
418list)
419 shift
130f2697 420 git stash--helper list "$@"
f2c66ed1
NS
421 ;;
422show)
423 shift
dc7bd382 424 git stash--helper show "$@"
f2c66ed1 425 ;;
683befa1
KL
426save)
427 shift
47629dcf 428 save_stash "$@"
683befa1 429 ;;
f5727e26
TG
430push)
431 shift
432 push_stash "$@"
433 ;;
f2c66ed1
NS
434apply)
435 shift
8a0fc8d1
JT
436 cd "$START_DIR"
437 git stash--helper apply "$@"
f2c66ed1
NS
438 ;;
439clear)
3023dc69 440 shift
4e2dd393 441 git stash--helper clear "$@"
f2c66ed1 442 ;;
bc9e7399 443create)
0719f300 444 shift
d4788af8 445 git stash--helper create --message "$*"
bc9e7399 446 ;;
bd514cad
RR
447store)
448 shift
41e0dd55 449 git stash--helper store "$@"
bd514cad 450 ;;
e25d5f9c
BC
451drop)
452 shift
4e2dd393 453 git stash--helper drop "$@"
e25d5f9c 454 ;;
bd56ff54
BC
455pop)
456 shift
c4de61d7
JT
457 cd "$START_DIR"
458 git stash--helper pop "$@"
bd56ff54 459 ;;
656b5034
AMS
460branch)
461 shift
577c1995
JT
462 cd "$START_DIR"
463 git stash--helper branch "$@"
656b5034 464 ;;
f2c66ed1 465*)
3c2eb80f
MM
466 case $# in
467 0)
1ada5020 468 push_stash &&
5a175871 469 say "$(gettext "(To restore them type \"git stash apply\")")"
ea41cfc4
JS
470 ;;
471 *)
683befa1 472 usage
ea41cfc4 473 esac
9f62e18a 474 ;;
f2c66ed1 475esac