@@ -210,56 +210,146 @@ list_stash () {
210210}
211211
212212show_stash () {
213- have_stash || die ' No stash found '
213+ assert_stash_like " $@ "
214214
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
226216}
227217
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
233274 do
234275 case " $1 " in
235- --index )
236- unstash_index= t
276+ -q|--quiet )
277+ GIT_QUIET=- t
237278 ;;
238- -q|--quiet )
239- GIT_QUIET=t
279+ --index )
280+ INDEX_OPTION=--index
240281 ;;
241- * )
242- break
282+ --)
283+ :
284+ ;;
285+ * )
286+ FLAGS=" ${FLAGS}${FLAGS: + } $1 "
243287 ;;
244288 esac
245289 shift
246290 done
247291
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% {* \} } "
249322 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"
254328 fi
255329
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 " $@ "
263353
264354 git update-index -q --refresh &&
265355 git diff-files --quiet --ignore-submodules ||
@@ -270,7 +360,7 @@ apply_stash () {
270360 die ' Cannot apply a stash in the middle of a merge'
271361
272362 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 " &&
274364 test " $c_tree " ! = " $i_tree "
275365 then
276366 git diff-tree --binary $s ^2^..$s ^2 | git apply --cached
@@ -315,66 +405,46 @@ apply_stash () {
315405 else
316406 # Merge conflict; keep the exit status from merge-recursive
317407 status=$?
318- if test -n " $unstash_index "
408+ if test -n " $INDEX_OPTION "
319409 then
320410 echo >&2 ' Index was not unstashed.'
321411 fi
322412 exit $status
323413 fi
324414}
325415
326- drop_stash () {
327- have_stash || die ' No stash entries to drop '
416+ pop_stash () {
417+ assert_stash_ref " $@ "
328418
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+ }
341422
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 " $@ "
353425
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"
356428
357429 # clear_stash if we just dropped the last stash entry
358430 git rev-parse --verify " $ref_stash @{0}" > /dev/null 2>&1 || clear_stash
359431}
360432
361433apply_to_branch () {
362- have_stash || die ' Nothing to apply'
363-
364434 test -n " $1 " || die ' No branch name specified'
365435 branch=$1
436+ shift 1
366437
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 " $@ "
372440
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 " $@ "
376445}
377446
447+ PARSE_CACHE=' --not-parsed'
378448# The default command is "save" if nothing but options are given
379449seen_non_option=
380450for opt
@@ -422,10 +492,7 @@ drop)
422492 ;;
423493pop)
424494 shift
425- if apply_stash " $@ "
426- then
427- drop_stash " $applied_stash "
428- fi
495+ pop_stash " $@ "
429496 ;;
430497branch)
431498 shift
0 commit comments