Skip to content

Commit 44c3f09

Browse files
committed
Merge branch 'tg/stash-push'
"git stash save" takes a pathspec so that the local changes can be stashed away only partially. * tg/stash-push: stash: allow pathspecs in the no verb form stash: use stash_push for no verb form stash: teach 'push' (and 'create_stash') to honor pathspec stash: refactor stash_create stash: add test for the create command line arguments stash: introduce push verb
2 parents ae900eb + 9e14090 commit 44c3f09

File tree

4 files changed

+274
-30
lines changed

4 files changed

+274
-30
lines changed

Documentation/git-stash.txt

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,11 @@ SYNOPSIS
1313
'git stash' drop [-q|--quiet] [<stash>]
1414
'git stash' ( pop | apply ) [--index] [-q|--quiet] [<stash>]
1515
'git stash' branch <branchname> [<stash>]
16-
'git stash' [save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]
17-
[-u|--include-untracked] [-a|--all] [<message>]]
16+
'git stash' save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]
17+
[-u|--include-untracked] [-a|--all] [<message>]
18+
'git stash' [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]
19+
[-u|--include-untracked] [-a|--all] [-m|--message <message>]]
20+
[--] [<pathspec>...]]
1821
'git stash' clear
1922
'git stash' create [<message>]
2023
'git stash' store [-m|--message <message>] [-q|--quiet] <commit>
@@ -46,14 +49,24 @@ OPTIONS
4649
-------
4750

4851
save [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [<message>]::
52+
push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--] [<pathspec>...]::
4953

5054
Save your local modifications to a new 'stash' and roll them
5155
back to HEAD (in the working tree and in the index).
5256
The <message> part is optional and gives
53-
the description along with the stashed state. For quickly making
54-
a snapshot, you can omit _both_ "save" and <message>, but giving
55-
only <message> does not trigger this action to prevent a misspelled
56-
subcommand from making an unwanted stash.
57+
the description along with the stashed state.
58+
+
59+
For quickly making a snapshot, you can omit "push". In this mode,
60+
non-option arguments are not allowed to prevent a misspelled
61+
subcommand from making an unwanted stash. The two exceptions to this
62+
are `stash -p` which acts as alias for `stash push -p` and pathspecs,
63+
which are allowed after a double hyphen `--` for disambiguation.
64+
+
65+
When pathspec is given to 'git stash push', the new stash records the
66+
modified states only for the files that match the pathspec. The index
67+
entries and working tree files are then rolled back to the state in
68+
HEAD only for these files, too, leaving files that do not match the
69+
pathspec intact.
5770
+
5871
If the `--keep-index` option is used, all changes already added to the
5972
index are left intact.

git-stash.sh

Lines changed: 94 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,11 @@ USAGE="list [<options>]
77
or: $dashless drop [-q|--quiet] [<stash>]
88
or: $dashless ( pop | apply ) [--index] [-q|--quiet] [<stash>]
99
or: $dashless branch <branchname> [<stash>]
10-
or: $dashless [save [--patch] [-k|--[no-]keep-index] [-q|--quiet]
11-
[-u|--include-untracked] [-a|--all] [<message>]]
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>...]]
1215
or: $dashless clear"
1316

1417
SUBDIRECTORY_OK=Yes
@@ -33,15 +36,15 @@ else
3336
fi
3437

3538
no_changes () {
36-
git diff-index --quiet --cached HEAD --ignore-submodules -- &&
37-
git diff-files --quiet --ignore-submodules &&
39+
git diff-index --quiet --cached HEAD --ignore-submodules -- "$@" &&
40+
git diff-files --quiet --ignore-submodules -- "$@" &&
3841
(test -z "$untracked" || test -z "$(untracked_files)")
3942
}
4043

4144
untracked_files () {
4245
excl_opt=--exclude-standard
4346
test "$untracked" = "all" && excl_opt=
44-
git ls-files -o -z $excl_opt
47+
git ls-files -o -z $excl_opt -- "$@"
4548
}
4649

4750
clear_stash () {
@@ -56,11 +59,29 @@ clear_stash () {
5659
}
5760

5861
create_stash () {
59-
stash_msg="$1"
60-
untracked="$2"
62+
stash_msg=
63+
untracked=
64+
while test $# != 0
65+
do
66+
case "$1" in
67+
-m|--message)
68+
shift
69+
stash_msg=${1?"BUG: create_stash () -m requires an argument"}
70+
;;
71+
-u|--include-untracked)
72+
shift
73+
untracked=${1?"BUG: create_stash () -u requires an argument"}
74+
;;
75+
--)
76+
shift
77+
break
78+
;;
79+
esac
80+
shift
81+
done
6182

6283
git update-index -q --refresh
63-
if no_changes
84+
if no_changes "$@"
6485
then
6586
exit 0
6687
fi
@@ -92,7 +113,7 @@ create_stash () {
92113
# Untracked files are stored by themselves in a parentless commit, for
93114
# ease of unpacking later.
94115
u_commit=$(
95-
untracked_files | (
116+
untracked_files "$@" | (
96117
GIT_INDEX_FILE="$TMPindex" &&
97118
export GIT_INDEX_FILE &&
98119
rm -f "$TMPindex" &&
@@ -115,7 +136,7 @@ create_stash () {
115136
git read-tree --index-output="$TMPindex" -m $i_tree &&
116137
GIT_INDEX_FILE="$TMPindex" &&
117138
export GIT_INDEX_FILE &&
118-
git diff-index --name-only -z HEAD -- >"$TMP-stagenames" &&
139+
git diff-index --name-only -z HEAD -- "$@" >"$TMP-stagenames" &&
119140
git update-index -z --add --remove --stdin <"$TMP-stagenames" &&
120141
git write-tree &&
121142
rm -f "$TMPindex"
@@ -129,7 +150,7 @@ create_stash () {
129150

130151
# find out what the user wants
131152
GIT_INDEX_FILE="$TMP-index" \
132-
git add--interactive --patch=stash -- &&
153+
git add--interactive --patch=stash -- "$@" &&
133154

134155
# state of the working tree
135156
w_tree=$(GIT_INDEX_FILE="$TMP-index" git write-tree) ||
@@ -189,10 +210,11 @@ store_stash () {
189210
return $ret
190211
}
191212

192-
save_stash () {
213+
push_stash () {
193214
keep_index=
194215
patch_mode=
195216
untracked=
217+
stash_msg=
196218
while test $# != 0
197219
do
198220
case "$1" in
@@ -216,6 +238,11 @@ save_stash () {
216238
-a|--all)
217239
untracked=all
218240
;;
241+
-m|--message)
242+
shift
243+
test -z ${1+x} && usage
244+
stash_msg=$1
245+
;;
219246
--help)
220247
show_help
221248
;;
@@ -251,29 +278,38 @@ save_stash () {
251278
die "$(gettext "Can't use --patch and --include-untracked or --all at the same time")"
252279
fi
253280

254-
stash_msg="$*"
281+
test -n "$untracked" || git ls-files --error-unmatch -- "$@" >/dev/null || exit 1
255282

256283
git update-index -q --refresh
257-
if no_changes
284+
if no_changes "$@"
258285
then
259286
say "$(gettext "No local changes to save")"
260287
exit 0
261288
fi
289+
262290
git reflog exists $ref_stash ||
263291
clear_stash || die "$(gettext "Cannot initialize stash")"
264292

265-
create_stash "$stash_msg" $untracked
293+
create_stash -m "$stash_msg" -u "$untracked" -- "$@"
266294
store_stash -m "$stash_msg" -q $w_commit ||
267295
die "$(gettext "Cannot save the current status")"
268296
say "$(eval_gettext "Saved working directory and index state \$stash_msg")"
269297

270298
if test -z "$patch_mode"
271299
then
272-
git reset --hard ${GIT_QUIET:+-q}
300+
if test $# != 0
301+
then
302+
git reset ${GIT_QUIET:+-q} -- "$@"
303+
git ls-files -z --modified -- "$@" |
304+
git checkout-index -z --force --stdin
305+
git clean --force ${GIT_QUIET:+-q} -d -- "$@"
306+
else
307+
git reset --hard ${GIT_QUIET:+-q}
308+
fi
273309
test "$untracked" = "all" && CLEAN_X_OPTION=-x || CLEAN_X_OPTION=
274310
if test -n "$untracked"
275311
then
276-
git clean --force --quiet -d $CLEAN_X_OPTION
312+
git clean --force --quiet -d $CLEAN_X_OPTION -- "$@"
277313
fi
278314

279315
if test "$keep_index" = "t" && test -n "$i_tree"
@@ -291,6 +327,36 @@ save_stash () {
291327
fi
292328
}
293329

330+
save_stash () {
331+
push_options=
332+
while test $# != 0
333+
do
334+
case "$1" in
335+
--)
336+
shift
337+
break
338+
;;
339+
-*)
340+
# pass all options through to push_stash
341+
push_options="$push_options $1"
342+
;;
343+
*)
344+
break
345+
;;
346+
esac
347+
shift
348+
done
349+
350+
stash_msg="$*"
351+
352+
if test -z "$stash_msg"
353+
then
354+
push_stash $push_options
355+
else
356+
push_stash $push_options -m "$stash_msg"
357+
fi
358+
}
359+
294360
have_stash () {
295361
git rev-parse --verify --quiet $ref_stash >/dev/null
296362
}
@@ -590,18 +656,21 @@ apply_to_branch () {
590656
}
591657
}
592658

659+
test "$1" = "-p" && set "push" "$@"
660+
593661
PARSE_CACHE='--not-parsed'
594-
# The default command is "save" if nothing but options are given
662+
# The default command is "push" if nothing but options are given
595663
seen_non_option=
596664
for opt
597665
do
598666
case "$opt" in
667+
--) break ;;
599668
-*) ;;
600669
*) seen_non_option=t; break ;;
601670
esac
602671
done
603672

604-
test -n "$seen_non_option" || set "save" "$@"
673+
test -n "$seen_non_option" || set "push" "$@"
605674

606675
# Main command set
607676
case "$1" in
@@ -617,6 +686,10 @@ save)
617686
shift
618687
save_stash "$@"
619688
;;
689+
push)
690+
shift
691+
push_stash "$@"
692+
;;
620693
apply)
621694
shift
622695
apply_stash "$@"
@@ -627,7 +700,7 @@ clear)
627700
;;
628701
create)
629702
shift
630-
create_stash "$*" && echo "$w_commit"
703+
create_stash -m "$*" && echo "$w_commit"
631704
;;
632705
store)
633706
shift
@@ -648,7 +721,7 @@ branch)
648721
*)
649722
case $# in
650723
0)
651-
save_stash &&
724+
push_stash &&
652725
say "$(gettext "(To restore them type \"git stash apply\")")"
653726
;;
654727
*)

0 commit comments

Comments
 (0)