@matheusbsbs / matheus-bagliano / commits / 8a98758a8d
Fork 0

stash push: support the --pathspec-from-file option

Decisions taken for simplicity: 1) For now, `--pathspec-from-file` is declared incompatible with `--patch`, even when <file> is not `-`. Such use case is not really expected. 2) It is not allowed to pass pathspec in both args and file. Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Alexandr Miloslavskiy committed Feb 17, 2020 at 17:25 UTC 8a98758a8d9dbd807fc2f1576e9141c1b720c5c1
3 files changed +139 -1
Documentation/git-stash.txt
+19 -1
@@ -15,6 +15,7 @@ SYNOPSIS
15 'git stash' branch <branchname> [<stash>]
16 'git stash' [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]
17 [-u|--include-untracked] [-a|--all] [-m|--message <message>]
18 + [--pathspec-from-file=<file> [--pathspec-file-nul]]
19 [--] [<pathspec>...]]
20 'git stash' clear
21 'git stash' create [<message>]
@@ -46,7 +47,7 @@ stash index (e.g. the integer `n` is equivalent to `stash@{n}`).
47 COMMANDS
48 --------
49
49 -push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--] [<pathspec>...]::
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>...]::
51
52 Save your local modifications to a new 'stash entry' and roll them
53 back to HEAD (in the working tree and in the index).
@@ -194,6 +195,23 @@ to learn how to operate the `--patch` mode.
195 The `--patch` option implies `--keep-index`. You can use
196 `--no-keep-index` to override this.
197
198 +--pathspec-from-file=<file>::
199 + This option is only valid for `push` command.
200 ++
201 +Pathspec is passed in `<file>` instead of commandline args. If
202 +`<file>` is exactly `-` then standard input is used. Pathspec
203 +elements are separated by LF or CR/LF. Pathspec elements can be
204 +quoted as explained for the configuration variable `core.quotePath`
205 +(see linkgit:git-config[1]). See also `--pathspec-file-nul` and
206 +global `--literal-pathspecs`.
207 +
208 +--pathspec-file-nul::
209 + This option is only valid for `push` command.
210 ++
211 +Only meaningful with `--pathspec-from-file`. Pathspec elements are
212 +separated with NUL character and all other characters are taken
213 +literally (including newlines and quotes).
214 +
215 -q::
216 --quiet::
217 This option is only valid for `apply`, `drop`, `pop`, `push`,
builtin/stash.c
+20
@@ -27,6 +27,7 @@ static const char * const git_stash_usage[] = {
27 N_("git stash clear"),
28 N_("git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
29 " [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
30 + " [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
31 " [--] [<pathspec>...]]"),
32 N_("git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
33 " [-u|--include-untracked] [-a|--all] [<message>]"),
@@ -1456,7 +1457,9 @@ static int push_stash(int argc, const char **argv, const char *prefix,
1457 int patch_mode = 0;
1458 int include_untracked = 0;
1459 int quiet = 0;
1460 + int pathspec_file_nul = 0;
1461 const char *stash_msg = NULL;
1462 + const char *pathspec_from_file = NULL;
1463 struct pathspec ps;
1464 struct option options[] = {
1465 OPT_BOOL('k', "keep-index", &keep_index,
@@ -1470,6 +1473,8 @@ static int push_stash(int argc, const char **argv, const char *prefix,
1473 N_("include ignore files"), 2),
1474 OPT_STRING('m', "message", &stash_msg, N_("message"),
1475 N_("stash message")),
1476 + OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
1477 + OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
1478 OPT_END()
1479 };
1480
@@ -1492,6 +1497,21 @@ static int push_stash(int argc, const char **argv, const char *prefix,
1497
1498 parse_pathspec(&ps, 0, PATHSPEC_PREFER_FULL | PATHSPEC_PREFIX_ORIGIN,
1499 prefix, argv);
1500 +
1501 + if (pathspec_from_file) {
1502 + if (patch_mode)
1503 + die(_("--pathspec-from-file is incompatible with --patch"));
1504 +
1505 + if (ps.nr)
1506 + die(_("--pathspec-from-file is incompatible with pathspec arguments"));
1507 +
1508 + parse_pathspec_file(&ps, 0,
1509 + PATHSPEC_PREFER_FULL | PATHSPEC_PREFIX_ORIGIN,
1510 + prefix, pathspec_from_file, pathspec_file_nul);
1511 + } else if (pathspec_file_nul) {
1512 + die(_("--pathspec-file-nul requires --pathspec-from-file"));
1513 + }
1514 +
1515 return do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode,
1516 include_untracked);
1517 }
t/t3909-stash-pathspec-file.sh new
+100
@@ -0,0 +1,100 @@
1 +#!/bin/sh
2 +
3 +test_description='stash --pathspec-from-file'
4 +
5 +. ./test-lib.sh
6 +
7 +test_tick
8 +
9 +test_expect_success setup '
10 + >fileA.t &&
11 + >fileB.t &&
12 + >fileC.t &&
13 + >fileD.t &&
14 + git add fileA.t fileB.t fileC.t fileD.t &&
15 + git commit -m "Files" &&
16 +
17 + git tag checkpoint
18 +'
19 +
20 +restore_checkpoint () {
21 + git reset --hard checkpoint
22 +}
23 +
24 +verify_expect () {
25 + git stash show --name-status >actual &&
26 + test_cmp expect actual
27 +}
28 +
29 +test_expect_success 'simplest' '
30 + restore_checkpoint &&
31 +
32 + # More files are written to make sure that git didnt ignore
33 + # --pathspec-from-file, stashing everything
34 + echo A >fileA.t &&
35 + echo B >fileB.t &&
36 + echo C >fileC.t &&
37 + echo D >fileD.t &&
38 +
39 + cat >expect <<-\EOF &&
40 + M fileA.t
41 + EOF
42 +
43 + echo fileA.t | git stash push --pathspec-from-file=- &&
44 + verify_expect
45 +'
46 +
47 +test_expect_success '--pathspec-file-nul' '
48 + restore_checkpoint &&
49 +
50 + # More files are written to make sure that git didnt ignore
51 + # --pathspec-from-file, stashing everything
52 + echo A >fileA.t &&
53 + echo B >fileB.t &&
54 + echo C >fileC.t &&
55 + echo D >fileD.t &&
56 +
57 + cat >expect <<-\EOF &&
58 + M fileA.t
59 + M fileB.t
60 + EOF
61 +
62 + printf "fileA.t\0fileB.t\0" | git stash push --pathspec-from-file=- --pathspec-file-nul &&
63 + verify_expect
64 +'
65 +
66 +test_expect_success 'only touches what was listed' '
67 + restore_checkpoint &&
68 +
69 + # More files are written to make sure that git didnt ignore
70 + # --pathspec-from-file, stashing everything
71 + echo A >fileA.t &&
72 + echo B >fileB.t &&
73 + echo C >fileC.t &&
74 + echo D >fileD.t &&
75 +
76 + cat >expect <<-\EOF &&
77 + M fileB.t
78 + M fileC.t
79 + EOF
80 +
81 + printf "fileB.t\nfileC.t\n" | git stash push --pathspec-from-file=- &&
82 + verify_expect
83 +'
84 +
85 +test_expect_success 'error conditions' '
86 + restore_checkpoint &&
87 + echo A >fileA.t &&
88 + echo fileA.t >list &&
89 +
90 + test_must_fail git stash push --pathspec-from-file=list --patch 2>err &&
91 + test_i18ngrep -e "--pathspec-from-file is incompatible with --patch" err &&
92 +
93 + test_must_fail git stash push --pathspec-from-file=list -- fileA.t 2>err &&
94 + test_i18ngrep -e "--pathspec-from-file is incompatible with pathspec arguments" err &&
95 +
96 + test_must_fail git stash push --pathspec-file-nul 2>err &&
97 + test_i18ngrep -e "--pathspec-file-nul requires --pathspec-from-file" err
98 +'
99 +
100 +test_done