Code

Rename ".dotest/" to ".git/rebase" and ".dotest-merge" to "rebase-merge"
[git.git] / git-stash.sh
1 #!/bin/sh
2 # Copyright (c) 2007, Nanako Shiraishi
4 USAGE='[  | save | list | show | apply | clear | drop | pop | create ]'
6 SUBDIRECTORY_OK=Yes
7 OPTIONS_SPEC=
8 . git-sh-setup
9 require_work_tree
10 cd_to_toplevel
12 TMP="$GIT_DIR/.git-stash.$$"
13 trap 'rm -f "$TMP-*"' 0
15 ref_stash=refs/stash
17 no_changes () {
18         git diff-index --quiet --cached HEAD --ignore-submodules -- &&
19         git diff-files --quiet --ignore-submodules
20 }
22 clear_stash () {
23         if test $# != 0
24         then
25                 die "git stash clear with parameters is unimplemented"
26         fi
27         if current=$(git rev-parse --verify $ref_stash 2>/dev/null)
28         then
29                 git update-ref -d $ref_stash $current
30         fi
31 }
33 create_stash () {
34         stash_msg="$1"
36         if no_changes
37         then
38                 exit 0
39         fi
41         # state of the base commit
42         if b_commit=$(git rev-parse --verify HEAD)
43         then
44                 head=$(git log --no-color --abbrev-commit --pretty=oneline -n 1 HEAD --)
45         else
46                 die "You do not have the initial commit yet"
47         fi
49         if branch=$(git symbolic-ref -q HEAD)
50         then
51                 branch=${branch#refs/heads/}
52         else
53                 branch='(no branch)'
54         fi
55         msg=$(printf '%s: %s' "$branch" "$head")
57         # state of the index
58         i_tree=$(git write-tree) &&
59         i_commit=$(printf 'index on %s\n' "$msg" |
60                 git commit-tree $i_tree -p $b_commit) ||
61                 die "Cannot save the current index state"
63         # state of the working tree
64         w_tree=$( (
65                 rm -f "$TMP-index" &&
66                 cp -p ${GIT_INDEX_FILE-"$GIT_DIR/index"} "$TMP-index" &&
67                 GIT_INDEX_FILE="$TMP-index" &&
68                 export GIT_INDEX_FILE &&
69                 git read-tree -m $i_tree &&
70                 git add -u &&
71                 git write-tree &&
72                 rm -f "$TMP-index"
73         ) ) ||
74                 die "Cannot save the current worktree state"
76         # create the stash
77         if test -z "$stash_msg"
78         then
79                 stash_msg=$(printf 'WIP on %s' "$msg")
80         else
81                 stash_msg=$(printf 'On %s: %s' "$branch" "$stash_msg")
82         fi
83         w_commit=$(printf '%s\n' "$stash_msg" |
84                 git commit-tree $w_tree -p $b_commit -p $i_commit) ||
85                 die "Cannot record working tree state"
86 }
88 save_stash () {
89         keep_index=
90         case "$1" in
91         --keep-index)
92                 keep_index=t
93                 shift
94         esac
96         stash_msg="$1"
98         if no_changes
99         then
100                 echo 'No local changes to save'
101                 exit 0
102         fi
103         test -f "$GIT_DIR/logs/$ref_stash" ||
104                 clear_stash || die "Cannot initialize stash"
106         create_stash "$stash_msg"
108         # Make sure the reflog for stash is kept.
109         : >>"$GIT_DIR/logs/$ref_stash"
111         git update-ref -m "$stash_msg" $ref_stash $w_commit ||
112                 die "Cannot save the current status"
113         printf 'Saved working directory and index state "%s"\n' "$stash_msg"
115         git reset --hard
117         if test -n "$keep_index" && test -n $i_tree
118         then
119                 git read-tree --reset -u $i_tree
120         fi
123 have_stash () {
124         git rev-parse --verify $ref_stash >/dev/null 2>&1
127 list_stash () {
128         have_stash || return 0
129         git log --no-color --pretty=oneline -g "$@" $ref_stash -- |
130         sed -n -e 's/^[.0-9a-f]* refs\///p'
133 show_stash () {
134         flags=$(git rev-parse --no-revs --flags "$@")
135         if test -z "$flags"
136         then
137                 flags=--stat
138         fi
139         s=$(git rev-parse --revs-only --no-flags --default $ref_stash "$@")
141         w_commit=$(git rev-parse --verify "$s") &&
142         b_commit=$(git rev-parse --verify "$s^") &&
143         git diff $flags $b_commit $w_commit
146 apply_stash () {
147         git diff-files --quiet --ignore-submodules ||
148                 die 'Cannot restore on top of a dirty state'
150         unstash_index=
151         case "$1" in
152         --index)
153                 unstash_index=t
154                 shift
155         esac
157         # current index state
158         c_tree=$(git write-tree) ||
159                 die 'Cannot apply a stash in the middle of a merge'
161         # stash records the work tree, and is a merge between the
162         # base commit (first parent) and the index tree (second parent).
163         s=$(git rev-parse --revs-only --no-flags --default $ref_stash "$@") &&
164         w_tree=$(git rev-parse --verify "$s:") &&
165         b_tree=$(git rev-parse --verify "$s^1:") &&
166         i_tree=$(git rev-parse --verify "$s^2:") ||
167                 die "$*: no valid stashed state found"
169         unstashed_index_tree=
170         if test -n "$unstash_index" && test "$b_tree" != "$i_tree" &&
171                         test "$c_tree" != "$i_tree"
172         then
173                 git diff-tree --binary $s^2^..$s^2 | git apply --cached
174                 test $? -ne 0 &&
175                         die 'Conflicts in index. Try without --index.'
176                 unstashed_index_tree=$(git-write-tree) ||
177                         die 'Could not save index tree'
178                 git reset
179         fi
181         eval "
182                 GITHEAD_$w_tree='Stashed changes' &&
183                 GITHEAD_$c_tree='Updated upstream' &&
184                 GITHEAD_$b_tree='Version stash was based on' &&
185                 export GITHEAD_$w_tree GITHEAD_$c_tree GITHEAD_$b_tree
186         "
188         if git-merge-recursive $b_tree -- $c_tree $w_tree
189         then
190                 # No conflict
191                 if test -n "$unstashed_index_tree"
192                 then
193                         git read-tree "$unstashed_index_tree"
194                 else
195                         a="$TMP-added" &&
196                         git diff-index --cached --name-only --diff-filter=A $c_tree >"$a" &&
197                         git read-tree --reset $c_tree &&
198                         git update-index --add --stdin <"$a" ||
199                                 die "Cannot unstage modified files"
200                         rm -f "$a"
201                 fi
202                 git status || :
203         else
204                 # Merge conflict; keep the exit status from merge-recursive
205                 status=$?
206                 if test -n "$unstash_index"
207                 then
208                         echo >&2 'Index was not unstashed.'
209                 fi
210                 exit $status
211         fi
214 drop_stash () {
215         have_stash || die 'No stash entries to drop'
217         if test $# = 0
218         then
219                 set x "$ref_stash@{0}"
220                 shift
221         fi
222         # Verify supplied argument looks like a stash entry
223         s=$(git rev-parse --revs-only --no-flags "$@") &&
224         git rev-parse --verify "$s:"   > /dev/null 2>&1 &&
225         git rev-parse --verify "$s^1:" > /dev/null 2>&1 &&
226         git rev-parse --verify "$s^2:" > /dev/null 2>&1 ||
227                 die "$*: not a valid stashed state"
229         git reflog delete --updateref --rewrite "$@" &&
230                 echo "Dropped $* ($s)" || die "$*: Could not drop stash entry"
232         # clear_stash if we just dropped the last stash entry
233         git rev-parse --verify "$ref_stash@{0}" > /dev/null 2>&1 || clear_stash
236 apply_to_branch () {
237         have_stash || die 'Nothing to apply'
239         test -n "$1" || die 'No branch name specified'
240         branch=$1
242         if test -z "$2"
243         then
244                 set x "$ref_stash@{0}"
245         fi
246         stash=$2
248         git-checkout -b $branch $stash^ &&
249         apply_stash --index $stash &&
250         drop_stash $stash
253 # Main command set
254 case "$1" in
255 list)
256         shift
257         if test $# = 0
258         then
259                 set x -n 10
260                 shift
261         fi
262         list_stash "$@"
263         ;;
264 show)
265         shift
266         show_stash "$@"
267         ;;
268 save)
269         shift
270         save_stash "$*"
271         ;;
272 apply)
273         shift
274         apply_stash "$@"
275         ;;
276 clear)
277         shift
278         clear_stash "$@"
279         ;;
280 create)
281         if test $# -gt 0 && test "$1" = create
282         then
283                 shift
284         fi
285         create_stash "$*" && echo "$w_commit"
286         ;;
287 drop)
288         shift
289         drop_stash "$@"
290         ;;
291 pop)
292         shift
293         if apply_stash "$@"
294         then
295                 test -z "$unstash_index" || shift
296                 drop_stash "$@"
297         fi
298         ;;
299 branch)
300         shift
301         apply_to_branch "$@"
302         ;;
303 *)
304         if test $# -eq 0
305         then
306                 save_stash &&
307                 echo '(To restore them type "git stash apply")'
308         else
309                 usage
310         fi
311         ;;
312 esac