Merge branch 'np/maint-1.6.3-deepen' into next
[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 [-k|--keep-index] [-q|--quiet] [<message>]]
11    or: $dashless [-k|--keep-index]
12    or: $dashless clear"
13
14 SUBDIRECTORY_OK=Yes
15 OPTIONS_SPEC=
16 . git-sh-setup
17 require_work_tree
18 cd_to_toplevel
19
20 TMP="$GIT_DIR/.git-stash.$$"
21 trap 'rm -f "$TMP-*"' 0
22
23 ref_stash=refs/stash
24
25 if git config --get-colorbool color.interactive; then
26        help_color="$(git config --get-color color.interactive.help 'red bold')"
27        reset_color="$(git config --get-color '' reset)"
28 else
29        help_color=
30        reset_color=
31 fi
32
33 no_changes () {
34         git diff-index --quiet --cached HEAD --ignore-submodules -- &&
35         git diff-files --quiet --ignore-submodules
36 }
37
38 clear_stash () {
39         if test $# != 0
40         then
41                 die "git stash clear with parameters is unimplemented"
42         fi
43         if current=$(git rev-parse --verify $ref_stash 2>/dev/null)
44         then
45                 git update-ref -d $ref_stash $current
46         fi
47 }
48
49 create_stash () {
50         stash_msg="$1"
51
52         git update-index -q --refresh
53         if no_changes
54         then
55                 exit 0
56         fi
57
58         # state of the base commit
59         if b_commit=$(git rev-parse --verify HEAD)
60         then
61                 head=$(git log --no-color --abbrev-commit --pretty=oneline -n 1 HEAD --)
62         else
63                 die "You do not have the initial commit yet"
64         fi
65
66         if branch=$(git symbolic-ref -q HEAD)
67         then
68                 branch=${branch#refs/heads/}
69         else
70                 branch='(no branch)'
71         fi
72         msg=$(printf '%s: %s' "$branch" "$head")
73
74         # state of the index
75         i_tree=$(git write-tree) &&
76         i_commit=$(printf 'index on %s\n' "$msg" |
77                 git commit-tree $i_tree -p $b_commit) ||
78                 die "Cannot save the current index state"
79
80         if test -z "$patch_mode"
81         then
82
83                 # state of the working tree
84                 w_tree=$( (
85                         rm -f "$TMP-index" &&
86                         cp -p ${GIT_INDEX_FILE-"$GIT_DIR/index"} "$TMP-index" &&
87                         GIT_INDEX_FILE="$TMP-index" &&
88                         export GIT_INDEX_FILE &&
89                         git read-tree -m $i_tree &&
90                         git add -u &&
91                         git write-tree &&
92                         rm -f "$TMP-index"
93                 ) ) ||
94                         die "Cannot save the current worktree state"
95
96         else
97
98                 rm -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 "Cannot save the current worktree state"
108
109                 git diff-tree -p HEAD $w_tree > "$TMP-patch" &&
110                 test -s "$TMP-patch" ||
111                 die "No changes selected"
112
113                 rm -f "$TMP-index" ||
114                 die "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 "Cannot record working tree state"
128 }
129
130 save_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=
141                         ;;
142                 -p|--patch)
143                         patch_mode=t
144                         keep_index=t
145                         ;;
146                 -q|--quiet)
147                         GIT_QUIET=t
148                         ;;
149                 *)
150                         break
151                         ;;
152                 esac
153                 shift
154         done
155
156         stash_msg="$*"
157
158         git update-index -q --refresh
159         if no_changes
160         then
161                 say 'No local changes to save'
162                 exit 0
163         fi
164         test -f "$GIT_DIR/logs/$ref_stash" ||
165                 clear_stash || die "Cannot initialize stash"
166
167         create_stash "$stash_msg"
168
169         # Make sure the reflog for stash is kept.
170         : >>"$GIT_DIR/logs/$ref_stash"
171
172         git update-ref -m "$stash_msg" $ref_stash $w_commit ||
173                 die "Cannot save the current status"
174         say Saved working directory and index state "$stash_msg"
175
176         if test -z "$patch_mode"
177         then
178                 git reset --hard ${GIT_QUIET:+-q}
179
180                 if test -n "$keep_index" && test -n $i_tree
181                 then
182                         git read-tree --reset -u $i_tree
183                 fi
184         else
185                 git apply -R < "$TMP-patch" ||
186                 die "Cannot remove worktree changes"
187
188                 if test -z "$keep_index"
189                 then
190                         git reset
191                 fi
192         fi
193 }
194
195 have_stash () {
196         git rev-parse --verify $ref_stash >/dev/null 2>&1
197 }
198
199 list_stash () {
200         have_stash || return 0
201         git log --no-color --pretty=oneline -g "$@" $ref_stash -- |
202         sed -n -e 's/^[.0-9a-f]* refs\///p'
203 }
204
205 show_stash () {
206         flags=$(git rev-parse --no-revs --flags "$@")
207         if test -z "$flags"
208         then
209                 flags=--stat
210         fi
211
212         w_commit=$(git rev-parse --verify --default $ref_stash "$@") &&
213         b_commit=$(git rev-parse --verify "$w_commit^") &&
214         git diff $flags $b_commit $w_commit
215 }
216
217 apply_stash () {
218         unstash_index=
219
220         while test $# != 0
221         do
222                 case "$1" in
223                 --index)
224                         unstash_index=t
225                         ;;
226                 -q|--quiet)
227                         GIT_QUIET=t
228                         ;;
229                 *)
230                         break
231                         ;;
232                 esac
233                 shift
234         done
235
236         if test $# = 0
237         then
238                 have_stash || die 'Nothing to apply'
239         fi
240
241         # stash records the work tree, and is a merge between the
242         # base commit (first parent) and the index tree (second parent).
243         s=$(git rev-parse --quiet --verify --default $ref_stash "$@") &&
244         w_tree=$(git rev-parse --quiet --verify "$s:") &&
245         b_tree=$(git rev-parse --quiet --verify "$s^1:") &&
246         i_tree=$(git rev-parse --quiet --verify "$s^2:") ||
247                 die "$*: no valid stashed state found"
248
249         git update-index -q --refresh &&
250         git diff-files --quiet --ignore-submodules ||
251                 die 'Cannot apply to a dirty working tree, please stage your changes'
252
253         # current index state
254         c_tree=$(git write-tree) ||
255                 die 'Cannot apply a stash in the middle of a merge'
256
257         unstashed_index_tree=
258         if test -n "$unstash_index" && test "$b_tree" != "$i_tree" &&
259                         test "$c_tree" != "$i_tree"
260         then
261                 git diff-tree --binary $s^2^..$s^2 | git apply --cached
262                 test $? -ne 0 &&
263                         die 'Conflicts in index. Try without --index.'
264                 unstashed_index_tree=$(git write-tree) ||
265                         die 'Could not save index tree'
266                 git reset
267         fi
268
269         eval "
270                 GITHEAD_$w_tree='Stashed changes' &&
271                 GITHEAD_$c_tree='Updated upstream' &&
272                 GITHEAD_$b_tree='Version stash was based on' &&
273                 export GITHEAD_$w_tree GITHEAD_$c_tree GITHEAD_$b_tree
274         "
275
276         if test -n "$GIT_QUIET"
277         then
278                 export GIT_MERGE_VERBOSITY=0
279         fi
280         if git merge-recursive $b_tree -- $c_tree $w_tree
281         then
282                 # No conflict
283                 if test -n "$unstashed_index_tree"
284                 then
285                         git read-tree "$unstashed_index_tree"
286                 else
287                         a="$TMP-added" &&
288                         git diff-index --cached --name-only --diff-filter=A $c_tree >"$a" &&
289                         git read-tree --reset $c_tree &&
290                         git update-index --add --stdin <"$a" ||
291                                 die "Cannot unstage modified files"
292                         rm -f "$a"
293                 fi
294                 squelch=
295                 if test -n "$GIT_QUIET"
296                 then
297                         squelch='>/dev/null 2>&1'
298                 fi
299                 eval "git status $squelch" || :
300         else
301                 # Merge conflict; keep the exit status from merge-recursive
302                 status=$?
303                 if test -n "$unstash_index"
304                 then
305                         echo >&2 'Index was not unstashed.'
306                 fi
307                 exit $status
308         fi
309 }
310
311 drop_stash () {
312         have_stash || die 'No stash entries to drop'
313
314         while test $# != 0
315         do
316                 case "$1" in
317                 -q|--quiet)
318                         GIT_QUIET=t
319                         ;;
320                 *)
321                         break
322                         ;;
323                 esac
324                 shift
325         done
326
327         if test $# = 0
328         then
329                 set x "$ref_stash@{0}"
330                 shift
331         fi
332         # Verify supplied argument looks like a stash entry
333         s=$(git rev-parse --verify "$@") &&
334         git rev-parse --verify "$s:"   > /dev/null 2>&1 &&
335         git rev-parse --verify "$s^1:" > /dev/null 2>&1 &&
336         git rev-parse --verify "$s^2:" > /dev/null 2>&1 ||
337                 die "$*: not a valid stashed state"
338
339         git reflog delete --updateref --rewrite "$@" &&
340                 say "Dropped $* ($s)" || die "$*: Could not drop stash entry"
341
342         # clear_stash if we just dropped the last stash entry
343         git rev-parse --verify "$ref_stash@{0}" > /dev/null 2>&1 || clear_stash
344 }
345
346 apply_to_branch () {
347         have_stash || die 'Nothing to apply'
348
349         test -n "$1" || die 'No branch name specified'
350         branch=$1
351
352         if test -z "$2"
353         then
354                 set x "$ref_stash@{0}"
355         fi
356         stash=$2
357
358         git checkout -b $branch $stash^ &&
359         apply_stash --index $stash &&
360         drop_stash $stash
361 }
362
363 # Main command set
364 case "$1" in
365 list)
366         shift
367         if test $# = 0
368         then
369                 set x -n 10
370                 shift
371         fi
372         list_stash "$@"
373         ;;
374 show)
375         shift
376         show_stash "$@"
377         ;;
378 save)
379         shift
380         save_stash "$@"
381         ;;
382 apply)
383         shift
384         apply_stash "$@"
385         ;;
386 clear)
387         shift
388         clear_stash "$@"
389         ;;
390 create)
391         if test $# -gt 0 && test "$1" = create
392         then
393                 shift
394         fi
395         create_stash "$*" && echo "$w_commit"
396         ;;
397 drop)
398         shift
399         drop_stash "$@"
400         ;;
401 pop)
402         shift
403         if apply_stash "$@"
404         then
405                 test -z "$unstash_index" || shift
406                 drop_stash "$@"
407         fi
408         ;;
409 branch)
410         shift
411         apply_to_branch "$@"
412         ;;
413 *)
414         case $#,"$1","$2" in
415         0,,|1,-k,|1,--keep-index,|1,-p,|1,--patch,|2,-p,--no-keep-index|2,--patch,--no-keep-index)
416                 save_stash "$@" &&
417                 say '(To restore them type "git stash apply")'
418                 ;;
419         *)
420                 usage
421         esac
422         ;;
423 esac