config: preserve scope in do_git_config_sequence
[git] / git-legacy-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 maybe_quiet () {
84         case "$1" in
85         --keep-stdout)
86                 shift
87                 if test -n "$GIT_QUIET"
88                 then
89                         "$@" 2>/dev/null
90                 else
91                         "$@"
92                 fi
93                 ;;
94         *)
95                 if test -n "$GIT_QUIET"
96                 then
97                         "$@" >/dev/null 2>&1
98                 else
99                         "$@"
100                 fi
101                 ;;
102         esac
103 }
104
105 create_stash () {
106
107         prepare_fallback_ident
108
109         stash_msg=
110         untracked=
111         while test $# != 0
112         do
113                 case "$1" in
114                 -m|--message)
115                         shift
116                         stash_msg=${1?"BUG: create_stash () -m requires an argument"}
117                         ;;
118                 -m*)
119                         stash_msg=${1#-m}
120                         ;;
121                 --message=*)
122                         stash_msg=${1#--message=}
123                         ;;
124                 -u|--include-untracked)
125                         shift
126                         untracked=${1?"BUG: create_stash () -u requires an argument"}
127                         ;;
128                 --)
129                         shift
130                         break
131                         ;;
132                 esac
133                 shift
134         done
135
136         git update-index -q --refresh
137         if maybe_quiet no_changes "$@"
138         then
139                 exit 0
140         fi
141
142         # state of the base commit
143         if b_commit=$(maybe_quiet --keep-stdout git rev-parse --verify HEAD)
144         then
145                 head=$(git rev-list --oneline -n 1 HEAD --)
146         elif test -n "$GIT_QUIET"
147         then
148                 exit 1
149         else
150                 die "$(gettext "You do not have the initial commit yet")"
151         fi
152
153         if branch=$(git symbolic-ref -q HEAD)
154         then
155                 branch=${branch#refs/heads/}
156         else
157                 branch='(no branch)'
158         fi
159         msg=$(printf '%s: %s' "$branch" "$head")
160
161         # state of the index
162         i_tree=$(git write-tree) &&
163         i_commit=$(printf 'index on %s\n' "$msg" |
164                 git commit-tree $i_tree -p $b_commit) ||
165                 die "$(gettext "Cannot save the current index state")"
166
167         if test -n "$untracked"
168         then
169                 # Untracked files are stored by themselves in a parentless commit, for
170                 # ease of unpacking later.
171                 u_commit=$(
172                         untracked_files -z "$@" | (
173                                 GIT_INDEX_FILE="$TMPindex" &&
174                                 export GIT_INDEX_FILE &&
175                                 rm -f "$TMPindex" &&
176                                 git update-index -z --add --remove --stdin &&
177                                 u_tree=$(git write-tree) &&
178                                 printf 'untracked files on %s\n' "$msg" | git commit-tree $u_tree  &&
179                                 rm -f "$TMPindex"
180                 ) ) || die "$(gettext "Cannot save the untracked files")"
181
182                 untracked_commit_option="-p $u_commit";
183         else
184                 untracked_commit_option=
185         fi
186
187         if test -z "$patch_mode"
188         then
189
190                 # state of the working tree
191                 w_tree=$( (
192                         git read-tree --index-output="$TMPindex" -m $i_tree &&
193                         GIT_INDEX_FILE="$TMPindex" &&
194                         export GIT_INDEX_FILE &&
195                         git diff-index --name-only -z HEAD -- "$@" >"$TMP-stagenames" &&
196                         git update-index --ignore-skip-worktree-entries \
197                                 -z --add --remove --stdin <"$TMP-stagenames" &&
198                         git write-tree &&
199                         rm -f "$TMPindex"
200                 ) ) ||
201                         die "$(gettext "Cannot save the current worktree state")"
202
203         else
204
205                 rm -f "$TMP-index" &&
206                 GIT_INDEX_FILE="$TMP-index" git read-tree HEAD &&
207
208                 # find out what the user wants
209                 GIT_INDEX_FILE="$TMP-index" \
210                         git add--interactive --patch=stash -- "$@" &&
211
212                 # state of the working tree
213                 w_tree=$(GIT_INDEX_FILE="$TMP-index" git write-tree) ||
214                 die "$(gettext "Cannot save the current worktree state")"
215
216                 git diff-tree -p HEAD $w_tree -- >"$TMP-patch" &&
217                 test -s "$TMP-patch" ||
218                 die "$(gettext "No changes selected")"
219
220                 rm -f "$TMP-index" ||
221                 die "$(gettext "Cannot remove temporary index (can't happen)")"
222
223         fi
224
225         # create the stash
226         if test -z "$stash_msg"
227         then
228                 stash_msg=$(printf 'WIP on %s' "$msg")
229         else
230                 stash_msg=$(printf 'On %s: %s' "$branch" "$stash_msg")
231         fi
232         w_commit=$(printf '%s\n' "$stash_msg" |
233         git commit-tree $w_tree -p $b_commit -p $i_commit $untracked_commit_option) ||
234         die "$(gettext "Cannot record working tree state")"
235 }
236
237 store_stash () {
238         while test $# != 0
239         do
240                 case "$1" in
241                 -m|--message)
242                         shift
243                         stash_msg="$1"
244                         ;;
245                 -m*)
246                         stash_msg=${1#-m}
247                         ;;
248                 --message=*)
249                         stash_msg=${1#--message=}
250                         ;;
251                 -q|--quiet)
252                         quiet=t
253                         ;;
254                 *)
255                         break
256                         ;;
257                 esac
258                 shift
259         done
260         test $# = 1 ||
261         die "$(eval_gettext "\"$dashless store\" requires one <commit> argument")"
262
263         w_commit="$1"
264         if test -z "$stash_msg"
265         then
266                 stash_msg="Created via \"git stash store\"."
267         fi
268
269         git update-ref --create-reflog -m "$stash_msg" $ref_stash $w_commit
270         ret=$?
271         test $ret != 0 && test -z "$quiet" &&
272         die "$(eval_gettext "Cannot update \$ref_stash with \$w_commit")"
273         return $ret
274 }
275
276 push_stash () {
277         keep_index=
278         patch_mode=
279         untracked=
280         stash_msg=
281         while test $# != 0
282         do
283                 case "$1" in
284                 -k|--keep-index)
285                         keep_index=t
286                         ;;
287                 --no-keep-index)
288                         keep_index=n
289                         ;;
290                 -p|--patch)
291                         patch_mode=t
292                         # only default to keep if we don't already have an override
293                         test -z "$keep_index" && keep_index=t
294                         ;;
295                 -q|--quiet)
296                         GIT_QUIET=t
297                         ;;
298                 -u|--include-untracked)
299                         untracked=untracked
300                         ;;
301                 -a|--all)
302                         untracked=all
303                         ;;
304                 -m|--message)
305                         shift
306                         test -z ${1+x} && usage
307                         stash_msg=$1
308                         ;;
309                 -m*)
310                         stash_msg=${1#-m}
311                         ;;
312                 --message=*)
313                         stash_msg=${1#--message=}
314                         ;;
315                 --help)
316                         show_help
317                         ;;
318                 --)
319                         shift
320                         break
321                         ;;
322                 -*)
323                         option="$1"
324                         eval_gettextln "error: unknown option for 'stash push': \$option"
325                         usage
326                         ;;
327                 *)
328                         break
329                         ;;
330                 esac
331                 shift
332         done
333
334         eval "set $(git rev-parse --sq --prefix "$prefix" -- "$@")"
335
336         if test -n "$patch_mode" && test -n "$untracked"
337         then
338                 die "$(gettext "Can't use --patch and --include-untracked or --all at the same time")"
339         fi
340
341         test -n "$untracked" || git ls-files --error-unmatch -- "$@" >/dev/null || exit 1
342
343         git update-index -q --refresh
344         if maybe_quiet no_changes "$@"
345         then
346                 say "$(gettext "No local changes to save")"
347                 exit 0
348         fi
349
350         git reflog exists $ref_stash ||
351                 clear_stash || die "$(gettext "Cannot initialize stash")"
352
353         create_stash -m "$stash_msg" -u "$untracked" -- "$@"
354         store_stash -m "$stash_msg" -q $w_commit ||
355         die "$(gettext "Cannot save the current status")"
356         say "$(eval_gettext "Saved working directory and index state \$stash_msg")"
357
358         if test -z "$patch_mode"
359         then
360                 test "$untracked" = "all" && CLEAN_X_OPTION=-x || CLEAN_X_OPTION=
361                 if test -n "$untracked" && test $# = 0
362                 then
363                         git clean --force --quiet -d $CLEAN_X_OPTION
364                 fi
365
366                 if test $# != 0
367                 then
368                         test -z "$untracked" && UPDATE_OPTION="-u" || UPDATE_OPTION=
369                         test "$untracked" = "all" && FORCE_OPTION="--force" || FORCE_OPTION=
370                         git add $UPDATE_OPTION $FORCE_OPTION -- "$@"
371                         git diff-index -p --cached --binary HEAD -- "$@" |
372                         git apply --index -R
373                 else
374                         git reset --hard -q --no-recurse-submodules
375                 fi
376
377                 if test "$keep_index" = "t" && test -n "$i_tree"
378                 then
379                         git read-tree --reset $i_tree
380                         git ls-files -z --modified -- "$@" |
381                         git checkout-index -z --force --stdin
382                 fi
383         else
384                 git apply -R < "$TMP-patch" ||
385                 die "$(gettext "Cannot remove worktree changes")"
386
387                 if test "$keep_index" != "t"
388                 then
389                         git reset -q -- "$@"
390                 fi
391         fi
392 }
393
394 save_stash () {
395         push_options=
396         while test $# != 0
397         do
398                 case "$1" in
399                 -q|--quiet)
400                         GIT_QUIET=t
401                         ;;
402                 --)
403                         shift
404                         break
405                         ;;
406                 -*)
407                         # pass all options through to push_stash
408                         push_options="$push_options $1"
409                         ;;
410                 *)
411                         break
412                         ;;
413                 esac
414                 shift
415         done
416
417         stash_msg="$*"
418
419         if test -z "$stash_msg"
420         then
421                 push_stash $push_options
422         else
423                 push_stash $push_options -m "$stash_msg"
424         fi
425 }
426
427 have_stash () {
428         git rev-parse --verify --quiet $ref_stash >/dev/null
429 }
430
431 list_stash () {
432         have_stash || return 0
433         git log --format="%gd: %gs" -g --first-parent -m "$@" $ref_stash --
434 }
435
436 show_stash () {
437         ALLOW_UNKNOWN_FLAGS=t
438         assert_stash_like "$@"
439
440         if test -z "$FLAGS"
441         then
442                 if test "$(git config --bool stash.showStat || echo true)" = "true"
443                 then
444                         FLAGS=--stat
445                 fi
446
447                 if test "$(git config --bool stash.showPatch || echo false)" = "true"
448                 then
449                         FLAGS=${FLAGS}${FLAGS:+ }-p
450                 fi
451
452                 if test -z "$FLAGS"
453                 then
454                         return 0
455                 fi
456         fi
457
458         git diff ${FLAGS} $b_commit $w_commit
459 }
460
461 show_help () {
462         exec git help stash
463         exit 1
464 }
465
466 #
467 # Parses the remaining options looking for flags and
468 # at most one revision defaulting to ${ref_stash}@{0}
469 # if none found.
470 #
471 # Derives related tree and commit objects from the
472 # revision, if one is found.
473 #
474 # stash records the work tree, and is a merge between the
475 # base commit (first parent) and the index tree (second parent).
476 #
477 #   REV is set to the symbolic version of the specified stash-like commit
478 #   IS_STASH_LIKE is non-blank if ${REV} looks like a stash
479 #   IS_STASH_REF is non-blank if the ${REV} looks like a stash ref
480 #   s is set to the SHA1 of the stash commit
481 #   w_commit is set to the commit containing the working tree
482 #   b_commit is set to the base commit
483 #   i_commit is set to the commit containing the index tree
484 #   u_commit is set to the commit containing the untracked files tree
485 #   w_tree is set to the working tree
486 #   b_tree is set to the base tree
487 #   i_tree is set to the index tree
488 #   u_tree is set to the untracked files tree
489 #
490 #   GIT_QUIET is set to t if -q is specified
491 #   INDEX_OPTION is set to --index if --index is specified.
492 #   FLAGS is set to the remaining flags (if allowed)
493 #
494 # dies if:
495 #   * too many revisions specified
496 #   * no revision is specified and there is no stash stack
497 #   * a revision is specified which cannot be resolve to a SHA1
498 #   * a non-existent stash reference is specified
499 #   * unknown flags were set and ALLOW_UNKNOWN_FLAGS is not "t"
500 #
501
502 parse_flags_and_rev()
503 {
504         test "$PARSE_CACHE" = "$*" && return 0 # optimisation
505         PARSE_CACHE="$*"
506
507         IS_STASH_LIKE=
508         IS_STASH_REF=
509         INDEX_OPTION=
510         s=
511         w_commit=
512         b_commit=
513         i_commit=
514         u_commit=
515         w_tree=
516         b_tree=
517         i_tree=
518         u_tree=
519
520         FLAGS=
521         REV=
522         for opt
523         do
524                 case "$opt" in
525                         -q|--quiet)
526                                 GIT_QUIET=-t
527                         ;;
528                         --index)
529                                 INDEX_OPTION=--index
530                         ;;
531                         --help)
532                                 show_help
533                         ;;
534                         -*)
535                                 test "$ALLOW_UNKNOWN_FLAGS" = t ||
536                                         die "$(eval_gettext "unknown option: \$opt")"
537                                 FLAGS="${FLAGS}${FLAGS:+ }$opt"
538                         ;;
539                         *)
540                                 REV="${REV}${REV:+ }'$opt'"
541                         ;;
542                 esac
543         done
544
545         eval set -- $REV
546
547         case $# in
548                 0)
549                         have_stash || die "$(gettext "No stash entries found.")"
550                         set -- ${ref_stash}@{0}
551                 ;;
552                 1)
553                         :
554                 ;;
555                 *)
556                         die "$(eval_gettext "Too many revisions specified: \$REV")"
557                 ;;
558         esac
559
560         case "$1" in
561                 *[!0-9]*)
562                         :
563                 ;;
564                 *)
565                         set -- "${ref_stash}@{$1}"
566                 ;;
567         esac
568
569         REV=$(git rev-parse --symbolic --verify --quiet "$1") || {
570                 reference="$1"
571                 die "$(eval_gettext "\$reference is not a valid reference")"
572         }
573
574         i_commit=$(git rev-parse --verify --quiet "$REV^2") &&
575         set -- $(git rev-parse "$REV" "$REV^1" "$REV:" "$REV^1:" "$REV^2:" 2>/dev/null) &&
576         s=$1 &&
577         w_commit=$1 &&
578         b_commit=$2 &&
579         w_tree=$3 &&
580         b_tree=$4 &&
581         i_tree=$5 &&
582         IS_STASH_LIKE=t &&
583         test "$ref_stash" = "$(git rev-parse --symbolic-full-name "${REV%@*}")" &&
584         IS_STASH_REF=t
585
586         u_commit=$(git rev-parse --verify --quiet "$REV^3") &&
587         u_tree=$(git rev-parse "$REV^3:" 2>/dev/null)
588 }
589
590 is_stash_like()
591 {
592         parse_flags_and_rev "$@"
593         test -n "$IS_STASH_LIKE"
594 }
595
596 assert_stash_like() {
597         is_stash_like "$@" || {
598                 args="$*"
599                 die "$(eval_gettext "'\$args' is not a stash-like commit")"
600         }
601 }
602
603 is_stash_ref() {
604         is_stash_like "$@" && test -n "$IS_STASH_REF"
605 }
606
607 assert_stash_ref() {
608         is_stash_ref "$@" || {
609                 args="$*"
610                 die "$(eval_gettext "'\$args' is not a stash reference")"
611         }
612 }
613
614 apply_stash () {
615
616         assert_stash_like "$@"
617
618         git update-index -q --refresh || die "$(gettext "unable to refresh index")"
619
620         # current index state
621         c_tree=$(git write-tree) ||
622                 die "$(gettext "Cannot apply a stash in the middle of a merge")"
623
624         unstashed_index_tree=
625         if test -n "$INDEX_OPTION" && test "$b_tree" != "$i_tree" &&
626                         test "$c_tree" != "$i_tree"
627         then
628                 git diff-tree --binary $s^2^..$s^2 | git apply --cached
629                 test $? -ne 0 &&
630                         die "$(gettext "Conflicts in index. Try without --index.")"
631                 unstashed_index_tree=$(git write-tree) ||
632                         die "$(gettext "Could not save index tree")"
633                 git reset
634         fi
635
636         if test -n "$u_tree"
637         then
638                 GIT_INDEX_FILE="$TMPindex" git read-tree "$u_tree" &&
639                 GIT_INDEX_FILE="$TMPindex" git checkout-index --all &&
640                 rm -f "$TMPindex" ||
641                 die "$(gettext "Could not restore untracked files from stash entry")"
642         fi
643
644         eval "
645                 GITHEAD_$w_tree='Stashed changes' &&
646                 GITHEAD_$c_tree='Updated upstream' &&
647                 GITHEAD_$b_tree='Version stash was based on' &&
648                 export GITHEAD_$w_tree GITHEAD_$c_tree GITHEAD_$b_tree
649         "
650
651         if test -n "$GIT_QUIET"
652         then
653                 GIT_MERGE_VERBOSITY=0 && export GIT_MERGE_VERBOSITY
654         fi
655         if git merge-recursive $b_tree -- $c_tree $w_tree
656         then
657                 # No conflict
658                 if test -n "$unstashed_index_tree"
659                 then
660                         git read-tree "$unstashed_index_tree"
661                 else
662                         a="$TMP-added" &&
663                         git diff-index --cached --name-only --diff-filter=A $c_tree >"$a" &&
664                         git read-tree --reset $c_tree &&
665                         git update-index --add --stdin <"$a" ||
666                                 die "$(gettext "Cannot unstage modified files")"
667                         rm -f "$a"
668                 fi
669                 squelch=
670                 if test -n "$GIT_QUIET"
671                 then
672                         squelch='>/dev/null 2>&1'
673                 fi
674                 (cd "$START_DIR" && eval "git status $squelch") || :
675         else
676                 # Merge conflict; keep the exit status from merge-recursive
677                 status=$?
678                 git rerere
679                 if test -n "$INDEX_OPTION"
680                 then
681                         gettextln "Index was not unstashed." >&2
682                 fi
683                 exit $status
684         fi
685 }
686
687 pop_stash() {
688         assert_stash_ref "$@"
689
690         if apply_stash "$@"
691         then
692                 drop_stash "$@"
693         else
694                 status=$?
695                 say "$(gettext "The stash entry is kept in case you need it again.")"
696                 exit $status
697         fi
698 }
699
700 drop_stash () {
701         assert_stash_ref "$@"
702
703         git reflog delete --updateref --rewrite "${REV}" &&
704                 say "$(eval_gettext "Dropped \${REV} (\$s)")" ||
705                 die "$(eval_gettext "\${REV}: Could not drop stash entry")"
706
707         # clear_stash if we just dropped the last stash entry
708         git rev-parse --verify --quiet "$ref_stash@{0}" >/dev/null ||
709         clear_stash
710 }
711
712 apply_to_branch () {
713         test -n "$1" || die "$(gettext "No branch name specified")"
714         branch=$1
715         shift 1
716
717         set -- --index "$@"
718         assert_stash_like "$@"
719
720         git checkout -b $branch $REV^ &&
721         apply_stash "$@" && {
722                 test -z "$IS_STASH_REF" || drop_stash "$@"
723         }
724 }
725
726 test "$1" = "-p" && set "push" "$@"
727
728 PARSE_CACHE='--not-parsed'
729 # The default command is "push" if nothing but options are given
730 seen_non_option=
731 for opt
732 do
733         case "$opt" in
734         --) break ;;
735         -*) ;;
736         *) seen_non_option=t; break ;;
737         esac
738 done
739
740 test -n "$seen_non_option" || set "push" "$@"
741
742 # Main command set
743 case "$1" in
744 list)
745         shift
746         list_stash "$@"
747         ;;
748 show)
749         shift
750         show_stash "$@"
751         ;;
752 save)
753         shift
754         save_stash "$@"
755         ;;
756 push)
757         shift
758         push_stash "$@"
759         ;;
760 apply)
761         shift
762         apply_stash "$@"
763         ;;
764 clear)
765         shift
766         clear_stash "$@"
767         ;;
768 create)
769         shift
770         create_stash -m "$*" && echo "$w_commit"
771         ;;
772 store)
773         shift
774         store_stash "$@"
775         ;;
776 drop)
777         shift
778         drop_stash "$@"
779         ;;
780 pop)
781         shift
782         pop_stash "$@"
783         ;;
784 branch)
785         shift
786         apply_to_branch "$@"
787         ;;
788 *)
789         case $# in
790         0)
791                 push_stash &&
792                 say "$(gettext "(To restore them type \"git stash apply\")")"
793                 ;;
794         *)
795                 usage
796         esac
797         ;;
798 esac