stash: convert push to builtin
[git] / git-stash.sh
1 #!/bin/sh
2 # Copyright (c) 2007, Nanako Shiraishi
3
4 dashless=$(basename "$0" | sed -e 's/-/ /')
5 USAGE="list [<options>]
6    or: $dashless show [<stash>]
7    or: $dashless drop [-q|--quiet] [<stash>]
8    or: $dashless ( pop | apply ) [--index] [-q|--quiet] [<stash>]
9    or: $dashless branch <branchname> [<stash>]
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>...]]
15    or: $dashless clear"
16
17 SUBDIRECTORY_OK=Yes
18 OPTIONS_SPEC=
19 START_DIR=$(pwd)
20 . git-sh-setup
21 require_work_tree
22 prefix=$(git rev-parse --show-prefix) || exit 1
23 cd_to_toplevel
24
25 TMP="$GIT_DIR/.git-stash.$$"
26 TMPindex=${GIT_INDEX_FILE-"$(git rev-parse --git-path index)"}.stash.$$
27 trap 'rm -f "$TMP-"* "$TMPindex"' 0
28
29 ref_stash=refs/stash
30
31 if 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)"
34 else
35        help_color=
36        reset_color=
37 fi
38
39 no_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
45 untracked_files () {
46         if test "$1" = "-z"
47         then
48                 shift
49                 z=-z
50         else
51                 z=
52         fi
53         excl_opt=--exclude-standard
54         test "$untracked" = "all" && excl_opt=
55         git ls-files -o $z $excl_opt -- "$@"
56 }
57
58 prepare_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
72 clear_stash () {
73         if test $# != 0
74         then
75                 die "$(gettext "git stash clear with parameters is unimplemented")"
76         fi
77         if current=$(git rev-parse --verify --quiet $ref_stash)
78         then
79                 git update-ref -d $ref_stash $current
80         fi
81 }
82
83 create_stash () {
84
85         prepare_fallback_ident
86
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                         ;;
96                 -m*)
97                         stash_msg=${1#-m}
98                         ;;
99                 --message=*)
100                         stash_msg=${1#--message=}
101                         ;;
102                 -u|--include-untracked)
103                         shift
104                         untracked=${1?"BUG: create_stash () -u requires an argument"}
105                         ;;
106                 --)
107                         shift
108                         break
109                         ;;
110                 esac
111                 shift
112         done
113
114         git update-index -q --refresh
115         if no_changes "$@"
116         then
117                 exit 0
118         fi
119
120         # state of the base commit
121         if b_commit=$(git rev-parse --verify HEAD)
122         then
123                 head=$(git rev-list --oneline -n 1 HEAD --)
124         else
125                 die "$(gettext "You do not have the initial commit yet")"
126         fi
127
128         if branch=$(git symbolic-ref -q HEAD)
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
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
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=$(
147                         untracked_files -z "$@" | (
148                                 GIT_INDEX_FILE="$TMPindex" &&
149                                 export GIT_INDEX_FILE &&
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"
155                 ) ) || die "$(gettext "Cannot save the untracked files")"
156
157                 untracked_commit_option="-p $u_commit";
158         else
159                 untracked_commit_option=
160         fi
161
162         if test -z "$patch_mode"
163         then
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" &&
169                         export 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 &&
173                         rm -f "$TMPindex"
174                 ) ) ||
175                         die "$(gettext "Cannot save the current worktree state")"
176
177         else
178
179                 rm -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" &&
191                 test -s "$TMP-patch" ||
192                 die "$(gettext "No changes selected")"
193
194                 rm -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
211 push_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
329 save_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
359 show_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
400 test "$1" = "-p" && set "push" "$@"
401
402 PARSE_CACHE='--not-parsed'
403 # The default command is "push" if nothing but options are given
404 seen_non_option=
405 for opt
406 do
407         case "$opt" in
408         --) break ;;
409         -*) ;;
410         *) seen_non_option=t; break ;;
411         esac
412 done
413
414 test -n "$seen_non_option" || set "push" "$@"
415
416 # Main command set
417 case "$1" in
418 list)
419         shift
420         git stash--helper list "$@"
421         ;;
422 show)
423         shift
424         git stash--helper show "$@"
425         ;;
426 save)
427         shift
428         save_stash "$@"
429         ;;
430 push)
431         shift
432         cd "$START_DIR"
433         git stash--helper push "$@"
434         ;;
435 apply)
436         shift
437         cd "$START_DIR"
438         git stash--helper apply "$@"
439         ;;
440 clear)
441         shift
442         git stash--helper clear "$@"
443         ;;
444 create)
445         shift
446         git stash--helper create --message "$*"
447         ;;
448 store)
449         shift
450         git stash--helper store "$@"
451         ;;
452 drop)
453         shift
454         git stash--helper drop "$@"
455         ;;
456 pop)
457         shift
458         cd "$START_DIR"
459         git stash--helper pop "$@"
460         ;;
461 branch)
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
475         esac
476         ;;
477 esac