Skip to content

Commit 44ac8fd

Browse files
committed
Merge branch 'so/stash-staged'
"git stash" learned the "--staged" option to stash away what has been added to the index (and nothing else). * so/stash-staged: stash: get rid of unused argument in stash_staged() stash: implement '--staged' option for 'push' and 'save'
2 parents 9b96d91 + a8a6e06 commit 44ac8fd

File tree

3 files changed

+113
-12
lines changed

3 files changed

+113
-12
lines changed

Documentation/git-stash.txt

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ 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' [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]
16+
'git stash' [push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--quiet]
1717
[-u|--include-untracked] [-a|--all] [-m|--message <message>]
1818
[--pathspec-from-file=<file> [--pathspec-file-nul]]
1919
[--] [<pathspec>...]]
@@ -47,7 +47,7 @@ stash index (e.g. the integer `n` is equivalent to `stash@{n}`).
4747
COMMANDS
4848
--------
4949

50-
push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--pathspec-from-file=<file> [--pathspec-file-nul]] [--] [<pathspec>...]::
50+
push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--pathspec-from-file=<file> [--pathspec-file-nul]] [--] [<pathspec>...]::
5151

5252
Save your local modifications to a new 'stash entry' and roll them
5353
back to HEAD (in the working tree and in the index).
@@ -60,7 +60,7 @@ subcommand from making an unwanted stash entry. The two exceptions to this
6060
are `stash -p` which acts as alias for `stash push -p` and pathspec elements,
6161
which are allowed after a double hyphen `--` for disambiguation.
6262

63-
save [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [<message>]::
63+
save [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [<message>]::
6464

6565
This option is deprecated in favour of 'git stash push'. It
6666
differs from "stash push" in that it cannot take pathspec.
@@ -205,6 +205,16 @@ to learn how to operate the `--patch` mode.
205205
The `--patch` option implies `--keep-index`. You can use
206206
`--no-keep-index` to override this.
207207

208+
-S::
209+
--staged::
210+
This option is only valid for `push` and `save` commands.
211+
+
212+
Stash only the changes that are currently staged. This is similar to
213+
basic `git commit` except the state is committed to the stash instead
214+
of current branch.
215+
+
216+
The `--patch` option has priority over this one.
217+
208218
--pathspec-from-file=<file>::
209219
This option is only valid for `push` command.
210220
+
@@ -341,6 +351,24 @@ $ edit/build/test remaining parts
341351
$ git commit foo -m 'Remaining parts'
342352
----------------------------------------------------------------
343353

354+
Saving unrelated changes for future use::
355+
356+
When you are in the middle of massive changes and you find some
357+
unrelated issue that you don't want to forget to fix, you can do the
358+
change(s), stage them, and use `git stash push --staged` to stash them
359+
out for future use. This is similar to committing the staged changes,
360+
only the commit ends-up being in the stash and not on the current branch.
361+
+
362+
----------------------------------------------------------------
363+
# ... hack hack hack ...
364+
$ git add --patch foo # add unrelated changes to the index
365+
$ git stash push --staged # save these changes to the stash
366+
# ... hack hack hack, finish curent changes ...
367+
$ git commit -m 'Massive' # commit fully tested changes
368+
$ git switch fixup-branch # switch to another branch
369+
$ git stash pop # to finish work on the saved changes
370+
----------------------------------------------------------------
371+
344372
Recovering stash entries that were cleared/dropped erroneously::
345373

346374
If you mistakenly drop or clear stash entries, they cannot be recovered

builtin/stash.c

Lines changed: 71 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,11 @@ static const char * const git_stash_usage[] = {
2727
N_("git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>]"),
2828
N_("git stash branch <branchname> [<stash>]"),
2929
"git stash clear",
30-
N_("git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
30+
N_("git stash [push [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--quiet]\n"
3131
" [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
3232
" [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
3333
" [--] [<pathspec>...]]"),
34-
N_("git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
34+
N_("git stash save [-p|--patch] [-S|--staged] [-k|--[no-]keep-index] [-q|--quiet]\n"
3535
" [-u|--include-untracked] [-a|--all] [<message>]"),
3636
NULL
3737
};
@@ -1132,6 +1132,38 @@ static int save_untracked_files(struct stash_info *info, struct strbuf *msg,
11321132
return ret;
11331133
}
11341134

1135+
static int stash_staged(struct stash_info *info, struct strbuf *out_patch,
1136+
int quiet)
1137+
{
1138+
int ret = 0;
1139+
struct child_process cp_diff_tree = CHILD_PROCESS_INIT;
1140+
struct index_state istate = { NULL };
1141+
1142+
if (write_index_as_tree(&info->w_tree, &istate, the_repository->index_file,
1143+
0, NULL)) {
1144+
ret = -1;
1145+
goto done;
1146+
}
1147+
1148+
cp_diff_tree.git_cmd = 1;
1149+
strvec_pushl(&cp_diff_tree.args, "diff-tree", "-p", "-U1", "HEAD",
1150+
oid_to_hex(&info->w_tree), "--", NULL);
1151+
if (pipe_command(&cp_diff_tree, NULL, 0, out_patch, 0, NULL, 0)) {
1152+
ret = -1;
1153+
goto done;
1154+
}
1155+
1156+
if (!out_patch->len) {
1157+
if (!quiet)
1158+
fprintf_ln(stderr, _("No staged changes"));
1159+
ret = 1;
1160+
}
1161+
1162+
done:
1163+
discard_index(&istate);
1164+
return ret;
1165+
}
1166+
11351167
static int stash_patch(struct stash_info *info, const struct pathspec *ps,
11361168
struct strbuf *out_patch, int quiet)
11371169
{
@@ -1258,7 +1290,7 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps
12581290
}
12591291

12601292
static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_buf,
1261-
int include_untracked, int patch_mode,
1293+
int include_untracked, int patch_mode, int only_staged,
12621294
struct stash_info *info, struct strbuf *patch,
12631295
int quiet)
12641296
{
@@ -1337,6 +1369,16 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b
13371369
} else if (ret > 0) {
13381370
goto done;
13391371
}
1372+
} else if (only_staged) {
1373+
ret = stash_staged(info, patch, quiet);
1374+
if (ret < 0) {
1375+
if (!quiet)
1376+
fprintf_ln(stderr, _("Cannot save the current "
1377+
"staged state"));
1378+
goto done;
1379+
} else if (ret > 0) {
1380+
goto done;
1381+
}
13401382
} else {
13411383
if (stash_working_tree(info, ps)) {
13421384
if (!quiet)
@@ -1395,7 +1437,7 @@ static int create_stash(int argc, const char **argv, const char *prefix)
13951437
if (!check_changes_tracked_files(&ps))
13961438
return 0;
13971439

1398-
ret = do_create_stash(&ps, &stash_msg_buf, 0, 0, &info,
1440+
ret = do_create_stash(&ps, &stash_msg_buf, 0, 0, 0, &info,
13991441
NULL, 0);
14001442
if (!ret)
14011443
printf_ln("%s", oid_to_hex(&info.w_commit));
@@ -1405,7 +1447,7 @@ static int create_stash(int argc, const char **argv, const char *prefix)
14051447
}
14061448

14071449
static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int quiet,
1408-
int keep_index, int patch_mode, int include_untracked)
1450+
int keep_index, int patch_mode, int include_untracked, int only_staged)
14091451
{
14101452
int ret = 0;
14111453
struct stash_info info;
@@ -1423,6 +1465,17 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
14231465
goto done;
14241466
}
14251467

1468+
/* --patch overrides --staged */
1469+
if (patch_mode)
1470+
only_staged = 0;
1471+
1472+
if (only_staged && include_untracked) {
1473+
fprintf_ln(stderr, _("Can't use --staged and --include-untracked"
1474+
" or --all at the same time"));
1475+
ret = -1;
1476+
goto done;
1477+
}
1478+
14261479
read_cache_preload(NULL);
14271480
if (!include_untracked && ps->nr) {
14281481
int i;
@@ -1463,7 +1516,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
14631516

14641517
if (stash_msg)
14651518
strbuf_addstr(&stash_msg_buf, stash_msg);
1466-
if (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode,
1519+
if (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode, only_staged,
14671520
&info, &patch, quiet)) {
14681521
ret = -1;
14691522
goto done;
@@ -1480,7 +1533,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
14801533
printf_ln(_("Saved working directory and index state %s"),
14811534
stash_msg_buf.buf);
14821535

1483-
if (!patch_mode) {
1536+
if (!(patch_mode || only_staged)) {
14841537
if (include_untracked && !ps->nr) {
14851538
struct child_process cp = CHILD_PROCESS_INIT;
14861539

@@ -1598,6 +1651,7 @@ static int push_stash(int argc, const char **argv, const char *prefix,
15981651
{
15991652
int force_assume = 0;
16001653
int keep_index = -1;
1654+
int only_staged = 0;
16011655
int patch_mode = 0;
16021656
int include_untracked = 0;
16031657
int quiet = 0;
@@ -1608,6 +1662,8 @@ static int push_stash(int argc, const char **argv, const char *prefix,
16081662
struct option options[] = {
16091663
OPT_BOOL('k', "keep-index", &keep_index,
16101664
N_("keep index")),
1665+
OPT_BOOL('S', "staged", &only_staged,
1666+
N_("stash staged changes only")),
16111667
OPT_BOOL('p', "patch", &patch_mode,
16121668
N_("stash in patch mode")),
16131669
OPT__QUIET(&quiet, N_("quiet mode")),
@@ -1646,6 +1702,9 @@ static int push_stash(int argc, const char **argv, const char *prefix,
16461702
if (patch_mode)
16471703
die(_("--pathspec-from-file is incompatible with --patch"));
16481704

1705+
if (only_staged)
1706+
die(_("--pathspec-from-file is incompatible with --staged"));
1707+
16491708
if (ps.nr)
16501709
die(_("--pathspec-from-file is incompatible with pathspec arguments"));
16511710

@@ -1657,12 +1716,13 @@ static int push_stash(int argc, const char **argv, const char *prefix,
16571716
}
16581717

16591718
return do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode,
1660-
include_untracked);
1719+
include_untracked, only_staged);
16611720
}
16621721

16631722
static int save_stash(int argc, const char **argv, const char *prefix)
16641723
{
16651724
int keep_index = -1;
1725+
int only_staged = 0;
16661726
int patch_mode = 0;
16671727
int include_untracked = 0;
16681728
int quiet = 0;
@@ -1673,6 +1733,8 @@ static int save_stash(int argc, const char **argv, const char *prefix)
16731733
struct option options[] = {
16741734
OPT_BOOL('k', "keep-index", &keep_index,
16751735
N_("keep index")),
1736+
OPT_BOOL('S', "staged", &only_staged,
1737+
N_("stash staged changes only")),
16761738
OPT_BOOL('p', "patch", &patch_mode,
16771739
N_("stash in patch mode")),
16781740
OPT__QUIET(&quiet, N_("quiet mode")),
@@ -1694,7 +1756,7 @@ static int save_stash(int argc, const char **argv, const char *prefix)
16941756

16951757
memset(&ps, 0, sizeof(ps));
16961758
ret = do_push_stash(&ps, stash_msg, quiet, keep_index,
1697-
patch_mode, include_untracked);
1759+
patch_mode, include_untracked, only_staged);
16981760

16991761
strbuf_release(&stash_msg_buf);
17001762
return ret;

t/t3903-stash.sh

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,17 @@ test_expect_success 'stash --no-keep-index' '
288288
test bar,bar2 = $(cat file),$(cat file2)
289289
'
290290

291+
test_expect_success 'stash --staged' '
292+
echo bar3 >file &&
293+
echo bar4 >file2 &&
294+
git add file2 &&
295+
git stash --staged &&
296+
test bar3,bar2 = $(cat file),$(cat file2) &&
297+
git reset --hard &&
298+
git stash pop &&
299+
test bar,bar4 = $(cat file),$(cat file2)
300+
'
301+
291302
test_expect_success 'dont assume push with non-option args' '
292303
test_must_fail git stash -q drop 2>err &&
293304
test_i18ngrep -e "subcommand wasn'\''t specified; '\''push'\'' can'\''t be assumed due to unexpected token '\''drop'\''" err

0 commit comments

Comments
 (0)