@matheusbsbs / matheus-bagliano / commits / 64bac8df97
Fork 0

reset: 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 `stdin`. Such use case it not really expected. Also, it is harder to support in `git commit`, so I decided to make it incompatible in all places. 2) It is not allowed to pass pathspec in both args and file. Co-authored-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Alexandr Miloslavskiy committed Nov 19, 2019 at 16:48 UTC 64bac8df972782b3b3dd2841699f076c7573c1db
3 files changed +192 -5
Documentation/git-reset.txt
+18 -3
@@ -9,18 +9,20 @@ SYNOPSIS
9 --------
10 [verse]
11 'git reset' [-q] [<tree-ish>] [--] <pathspec>...
12 +'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-nul]] [<tree-ish>]
13 'git reset' (--patch | -p) [<tree-ish>] [--] [<pathspec>...]
14 'git reset' [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>]
15
16 DESCRIPTION
17 -----------
17 -In the first and second form, copy entries from `<tree-ish>` to the index.
18 -In the third form, set the current branch head (`HEAD`) to `<commit>`,
18 +In the first three forms, copy entries from `<tree-ish>` to the index.
19 +In the last form, set the current branch head (`HEAD`) to `<commit>`,
20 optionally modifying index and working tree to match.
21 The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms.
22
23 'git reset' [-q] [<tree-ish>] [--] <pathspec>...::
23 - This form resets the index entries for all paths that match the
24 +'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-nul]] [<tree-ish>]::
25 + These forms reset the index entries for all paths that match the
26 `<pathspec>` to their state at `<tree-ish>`. (It does not affect
27 the working tree or the current branch.)
28 +
@@ -101,6 +103,19 @@ OPTIONS
103 `reset.quiet` config option. `--quiet` and `--no-quiet` will
104 override the default behavior.
105
106 +--pathspec-from-file=<file>::
107 + Pathspec is passed in `<file>` instead of commandline args. If
108 + `<file>` is exactly `-` then standard input is used. Pathspec
109 + elements are separated by LF or CR/LF. Pathspec elements can be
110 + quoted as explained for the configuration variable `core.quotePath`
111 + (see linkgit:git-config[1]). See also `--pathspec-file-nul` and
112 + global `--literal-pathspecs`.
113 +
114 +--pathspec-file-nul::
115 + Only meaningful with `--pathspec-from-file`. Pathspec elements are
116 + separated with NUL character and all other characters are taken
117 + literally (including newlines and quotes).
118 +
119 \--::
120 Do not interpret any more arguments as options.
121
builtin/reset.c
+19 -2
@@ -31,6 +31,7 @@
31 static const char * const git_reset_usage[] = {
32 N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"),
33 N_("git reset [-q] [<tree-ish>] [--] <pathspec>..."),
34 + N_("git reset [-q] [--pathspec-from-file [--pathspec-file-nul]] [<tree-ish>]"),
35 N_("git reset --patch [<tree-ish>] [--] [<pathspec>...]"),
36 NULL
37 };
@@ -284,8 +285,8 @@ static int git_reset_config(const char *var, const char *value, void *cb)
285 int cmd_reset(int argc, const char **argv, const char *prefix)
286 {
287 int reset_type = NONE, update_ref_status = 0, quiet = 0;
287 - int patch_mode = 0, unborn;
288 - const char *rev;
288 + int patch_mode = 0, pathspec_file_nul = 0, unborn;
289 + const char *rev, *pathspec_from_file = NULL;
290 struct object_id oid;
291 struct pathspec pathspec;
292 int intent_to_add = 0;
@@ -306,6 +307,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
307 OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")),
308 OPT_BOOL('N', "intent-to-add", &intent_to_add,
309 N_("record only the fact that removed paths will be added later")),
310 + OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
311 + OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
312 OPT_END()
313 };
314
@@ -316,6 +319,20 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
319 PARSE_OPT_KEEP_DASHDASH);
320 parse_args(&pathspec, argv, prefix, patch_mode, &rev);
321
322 + if (pathspec_from_file) {
323 + if (patch_mode)
324 + die(_("--pathspec-from-file is incompatible with --patch"));
325 +
326 + if (pathspec.nr)
327 + die(_("--pathspec-from-file is incompatible with pathspec arguments"));
328 +
329 + parse_pathspec_file(&pathspec, 0,
330 + PATHSPEC_PREFER_FULL,
331 + prefix, pathspec_from_file, pathspec_file_nul);
332 + } else if (pathspec_file_nul) {
333 + die(_("--pathspec-file-nul requires --pathspec-from-file"));
334 + }
335 +
336 unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid);
337 if (unborn) {
338 /* reset on unborn branch: treat as reset to empty tree */
t/t7107-reset-pathspec-file.sh new
+155
@@ -0,0 +1,155 @@
1 +#!/bin/sh
2 +
3 +test_description='reset --pathspec-from-file'
4 +
5 +. ./test-lib.sh
6 +
7 +test_tick
8 +
9 +test_expect_success setup '
10 + echo A >fileA.t &&
11 + echo B >fileB.t &&
12 + echo C >fileC.t &&
13 + echo D >fileD.t &&
14 + git add . &&
15 + git commit --include . -m "Commit" &&
16 + git tag checkpoint
17 +'
18 +
19 +restore_checkpoint () {
20 + git reset --hard checkpoint
21 +}
22 +
23 +verify_expect () {
24 + git status --porcelain -- fileA.t fileB.t fileC.t fileD.t >actual &&
25 + test_cmp expect actual
26 +}
27 +
28 +test_expect_success '--pathspec-from-file from stdin' '
29 + restore_checkpoint &&
30 +
31 + git rm fileA.t &&
32 + echo fileA.t | git reset --pathspec-from-file=- &&
33 +
34 + cat >expect <<-\EOF &&
35 + D fileA.t
36 + EOF
37 + verify_expect
38 +'
39 +
40 +test_expect_success '--pathspec-from-file from file' '
41 + restore_checkpoint &&
42 +
43 + git rm fileA.t &&
44 + echo fileA.t >list &&
45 + git reset --pathspec-from-file=list &&
46 +
47 + cat >expect <<-\EOF &&
48 + D fileA.t
49 + EOF
50 + verify_expect
51 +'
52 +
53 +test_expect_success 'NUL delimiters' '
54 + restore_checkpoint &&
55 +
56 + git rm fileA.t fileB.t &&
57 + printf "fileA.t\0fileB.t\0" | git reset --pathspec-from-file=- --pathspec-file-nul &&
58 +
59 + cat >expect <<-\EOF &&
60 + D fileA.t
61 + D fileB.t
62 + EOF
63 + verify_expect
64 +'
65 +
66 +test_expect_success 'LF delimiters' '
67 + restore_checkpoint &&
68 +
69 + git rm fileA.t fileB.t &&
70 + printf "fileA.t\nfileB.t\n" | git reset --pathspec-from-file=- &&
71 +
72 + cat >expect <<-\EOF &&
73 + D fileA.t
74 + D fileB.t
75 + EOF
76 + verify_expect
77 +'
78 +
79 +test_expect_success 'no trailing delimiter' '
80 + restore_checkpoint &&
81 +
82 + git rm fileA.t fileB.t &&
83 + printf "fileA.t\nfileB.t" | git reset --pathspec-from-file=- &&
84 +
85 + cat >expect <<-\EOF &&
86 + D fileA.t
87 + D fileB.t
88 + EOF
89 + verify_expect
90 +'
91 +
92 +test_expect_success 'CRLF delimiters' '
93 + restore_checkpoint &&
94 +
95 + git rm fileA.t fileB.t &&
96 + printf "fileA.t\r\nfileB.t\r\n" | git reset --pathspec-from-file=- &&
97 +
98 + cat >expect <<-\EOF &&
99 + D fileA.t
100 + D fileB.t
101 + EOF
102 + verify_expect
103 +'
104 +
105 +test_expect_success 'quotes' '
106 + restore_checkpoint &&
107 +
108 + git rm fileA.t &&
109 + printf "\"file\\101.t\"" | git reset --pathspec-from-file=- &&
110 +
111 + cat >expect <<-\EOF &&
112 + D fileA.t
113 + EOF
114 + verify_expect
115 +'
116 +
117 +test_expect_success 'quotes not compatible with --pathspec-file-nul' '
118 + restore_checkpoint &&
119 +
120 + git rm fileA.t &&
121 + printf "\"file\\101.t\"" >list &&
122 + # Note: "git reset" has not yet learned to fail on wrong pathspecs
123 + git reset --pathspec-from-file=list --pathspec-file-nul &&
124 +
125 + cat >expect <<-\EOF &&
126 + D fileA.t
127 + EOF
128 + test_must_fail verify_expect
129 +'
130 +
131 +test_expect_success '--pathspec-from-file is not compatible with --soft or --hard' '
132 + restore_checkpoint &&
133 +
134 + git rm fileA.t &&
135 + echo fileA.t >list &&
136 + test_must_fail git reset --soft --pathspec-from-file=list &&
137 + test_must_fail git reset --hard --pathspec-from-file=list
138 +'
139 +
140 +test_expect_success 'only touches what was listed' '
141 + restore_checkpoint &&
142 +
143 + git rm fileA.t fileB.t fileC.t fileD.t &&
144 + printf "fileB.t\nfileC.t\n" | git reset --pathspec-from-file=- &&
145 +
146 + cat >expect <<-\EOF &&
147 + D fileA.t
148 + D fileB.t
149 + D fileC.t
150 + D fileD.t
151 + EOF
152 + verify_expect
153 +'
154 +
155 +test_done