@@ -210,56 +210,146 @@ list_stash () {
210
210
}
211
211
212
212
show_stash () {
213
- have_stash || die ' No stash found '
213
+ assert_stash_like " $@ "
214
214
215
- flags=$( git rev-parse --no-revs --flags " $@ " )
216
- if test -z " $flags "
217
- then
218
- flags=--stat
219
- fi
220
-
221
- w_commit=$( git rev-parse --quiet --verify --default $ref_stash " $@ " ) &&
222
- b_commit=$( git rev-parse --quiet --verify " $w_commit ^" ) ||
223
- die " '$* ' is not a stash"
224
-
225
- git diff $flags $b_commit $w_commit
215
+ git diff ${FLAGS:- --stat} $b_commit $w_commit
226
216
}
227
217
228
- apply_stash () {
229
- applied_stash=
230
- unstash_index=
231
-
232
- while test $# ! = 0
218
+ #
219
+ # Parses the remaining options looking for flags and
220
+ # at most one revision defaulting to ${ref_stash}@{0}
221
+ # if none found.
222
+ #
223
+ # Derives related tree and commit objects from the
224
+ # revision, if one is found.
225
+ #
226
+ # stash records the work tree, and is a merge between the
227
+ # base commit (first parent) and the index tree (second parent).
228
+ #
229
+ # REV is set to the symbolic version of the specified stash-like commit
230
+ # IS_STASH_LIKE is non-blank if ${REV} looks like a stash
231
+ # IS_STASH_REF is non-blank if the ${REV} looks like a stash ref
232
+ # s is set to the SHA1 of the stash commit
233
+ # w_commit is set to the commit containing the working tree
234
+ # b_commit is set to the base commit
235
+ # i_commit is set to the commit containing the index tree
236
+ # w_tree is set to the working tree
237
+ # b_tree is set to the base tree
238
+ # i_tree is set to the index tree
239
+ #
240
+ # GIT_QUIET is set to t if -q is specified
241
+ # INDEX_OPTION is set to --index if --index is specified.
242
+ # FLAGS is set to the remaining flags
243
+ #
244
+ # dies if:
245
+ # * too many revisions specified
246
+ # * no revision is specified and there is no stash stack
247
+ # * a revision is specified which cannot be resolve to a SHA1
248
+ # * a non-existent stash reference is specified
249
+ #
250
+
251
+ parse_flags_and_rev ()
252
+ {
253
+ test " $PARSE_CACHE " = " $* " && return 0 # optimisation
254
+ PARSE_CACHE=" $* "
255
+
256
+ IS_STASH_LIKE=
257
+ IS_STASH_REF=
258
+ INDEX_OPTION=
259
+ s=
260
+ w_commit=
261
+ b_commit=
262
+ i_commit=
263
+ w_tree=
264
+ b_tree=
265
+ i_tree=
266
+
267
+ REV=$( git rev-parse --no-flags --symbolic " $@ " 2> /dev/null)
268
+ FLAGS=$( git rev-parse --no-revs -- " $@ " 2> /dev/null)
269
+
270
+ set -- $FLAGS
271
+
272
+ FLAGS=
273
+ while test $# -ne 0
233
274
do
234
275
case " $1 " in
235
- --index )
236
- unstash_index= t
276
+ -q|--quiet )
277
+ GIT_QUIET=- t
237
278
;;
238
- -q|--quiet )
239
- GIT_QUIET=t
279
+ --index )
280
+ INDEX_OPTION=--index
240
281
;;
241
- * )
242
- break
282
+ --)
283
+ :
284
+ ;;
285
+ * )
286
+ FLAGS=" ${FLAGS}${FLAGS: + } $1 "
243
287
;;
244
288
esac
245
289
shift
246
290
done
247
291
248
- if test $# = 0
292
+ set -- $REV
293
+
294
+ case $# in
295
+ 0)
296
+ have_stash || die " No stash found."
297
+ set -- ${ref_stash} @{0}
298
+ ;;
299
+ 1)
300
+ :
301
+ ;;
302
+ * )
303
+ die " Too many revisions specified: $REV "
304
+ ;;
305
+ esac
306
+
307
+ REV=$( git rev-parse --quiet --symbolic --verify $1 2> /dev/null) || die " $1 is not valid reference"
308
+
309
+ i_commit=$( git rev-parse --quiet --verify $REV ^2 2> /dev/null) &&
310
+ set -- $( git rev-parse $REV $REV ^1 $REV : $REV ^1: $REV ^2: 2> /dev/null) &&
311
+ s=$1 &&
312
+ w_commit=$1 &&
313
+ b_commit=$2 &&
314
+ w_tree=$3 &&
315
+ b_tree=$4 &&
316
+ i_tree=$5 &&
317
+ IS_STASH_LIKE=t &&
318
+ test " $ref_stash " = " $( git rev-parse --symbolic-full-name " ${REV%@* } " ) " &&
319
+ IS_STASH_REF=t
320
+
321
+ if test " ${REV} " ! = " ${REV% {* \} } "
249
322
then
250
- have_stash || die ' Nothing to apply'
251
- applied_stash=" $ref_stash @{0}"
252
- else
253
- applied_stash=" $* "
323
+ # maintainers: it would be better if git rev-parse indicated
324
+ # this condition with a non-zero status code but as of 1.7.2.1 it
325
+ # it did not. So, we use non-empty stderr output as a proxy for the
326
+ # condition of interest.
327
+ test -z " $( git rev-parse " $REV " 2>&1 > /dev/null) " || die " $REV does not exist in the stash log"
254
328
fi
255
329
256
- # stash records the work tree, and is a merge between the
257
- # base commit (first parent) and the index tree (second parent).
258
- s=$( git rev-parse --quiet --verify --default $ref_stash " $@ " ) &&
259
- w_tree=$( git rev-parse --quiet --verify " $s :" ) &&
260
- b_tree=$( git rev-parse --quiet --verify " $s ^1:" ) &&
261
- i_tree=$( git rev-parse --quiet --verify " $s ^2:" ) ||
262
- die " $* : no valid stashed state found"
330
+ }
331
+
332
+ is_stash_like ()
333
+ {
334
+ parse_flags_and_rev " $@ "
335
+ test -n " $IS_STASH_LIKE "
336
+ }
337
+
338
+ assert_stash_like () {
339
+ is_stash_like " $@ " || die " '$* ' is not a stash-like commit"
340
+ }
341
+
342
+ is_stash_ref () {
343
+ is_stash_like " $@ " && test -n " $IS_STASH_REF "
344
+ }
345
+
346
+ assert_stash_ref () {
347
+ is_stash_ref " $@ " || die " '$* ' is not a stash reference"
348
+ }
349
+
350
+ apply_stash () {
351
+
352
+ assert_stash_like " $@ "
263
353
264
354
git update-index -q --refresh &&
265
355
git diff-files --quiet --ignore-submodules ||
@@ -270,7 +360,7 @@ apply_stash () {
270
360
die ' Cannot apply a stash in the middle of a merge'
271
361
272
362
unstashed_index_tree=
273
- if test -n " $unstash_index " && test " $b_tree " ! = " $i_tree " &&
363
+ if test -n " $INDEX_OPTION " && test " $b_tree " ! = " $i_tree " &&
274
364
test " $c_tree " ! = " $i_tree "
275
365
then
276
366
git diff-tree --binary $s ^2^..$s ^2 | git apply --cached
@@ -315,66 +405,46 @@ apply_stash () {
315
405
else
316
406
# Merge conflict; keep the exit status from merge-recursive
317
407
status=$?
318
- if test -n " $unstash_index "
408
+ if test -n " $INDEX_OPTION "
319
409
then
320
410
echo >&2 ' Index was not unstashed.'
321
411
fi
322
412
exit $status
323
413
fi
324
414
}
325
415
326
- drop_stash () {
327
- have_stash || die ' No stash entries to drop '
416
+ pop_stash () {
417
+ assert_stash_ref " $@ "
328
418
329
- while test $# ! = 0
330
- do
331
- case " $1 " in
332
- -q|--quiet)
333
- GIT_QUIET=t
334
- ;;
335
- * )
336
- break
337
- ;;
338
- esac
339
- shift
340
- done
419
+ apply_stash " $@ " &&
420
+ drop_stash " $@ "
421
+ }
341
422
342
- if test $# = 0
343
- then
344
- set x " $ref_stash @{0}"
345
- shift
346
- fi
347
- # Verify supplied argument looks like a stash entry
348
- s=$( git rev-parse --verify " $@ " ) &&
349
- git rev-parse --verify " $s :" > /dev/null 2>&1 &&
350
- git rev-parse --verify " $s ^1:" > /dev/null 2>&1 &&
351
- git rev-parse --verify " $s ^2:" > /dev/null 2>&1 ||
352
- die " $* : not a valid stashed state"
423
+ drop_stash () {
424
+ assert_stash_ref " $@ "
353
425
354
- git reflog delete --updateref --rewrite " $@ " &&
355
- say " Dropped $* ($s )" || die " $* : Could not drop stash entry"
426
+ git reflog delete --updateref --rewrite " ${REV} " &&
427
+ say " Dropped ${REV} ($s )" || die " ${REV} : Could not drop stash entry"
356
428
357
429
# clear_stash if we just dropped the last stash entry
358
430
git rev-parse --verify " $ref_stash @{0}" > /dev/null 2>&1 || clear_stash
359
431
}
360
432
361
433
apply_to_branch () {
362
- have_stash || die ' Nothing to apply'
363
-
364
434
test -n " $1 " || die ' No branch name specified'
365
435
branch=$1
436
+ shift 1
366
437
367
- if test -z " $2 "
368
- then
369
- set x " $ref_stash @{0}"
370
- fi
371
- stash=$2
438
+ set -- --index " $@ "
439
+ assert_stash_like " $@ "
372
440
373
- git checkout -b $branch $stash ^ &&
374
- apply_stash --index $stash &&
375
- drop_stash $stash
441
+ git checkout -b $branch $REV ^ &&
442
+ apply_stash " $@ "
443
+
444
+ test -z " $IS_STASH_REF " || drop_stash " $@ "
376
445
}
377
446
447
+ PARSE_CACHE=' --not-parsed'
378
448
# The default command is "save" if nothing but options are given
379
449
seen_non_option=
380
450
for opt
@@ -422,10 +492,7 @@ drop)
422
492
;;
423
493
pop)
424
494
shift
425
- if apply_stash " $@ "
426
- then
427
- drop_stash " $applied_stash "
428
- fi
495
+ pop_stash " $@ "
429
496
;;
430
497
branch)
431
498
shift
0 commit comments