Skip to content

Commit 64bac8d

Browse files
SyntevoAlexdscho
authored andcommitted
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 <[email protected]> Signed-off-by: Alexandr Miloslavskiy <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent d137b50 commit 64bac8d

File tree

3 files changed

+192
-5
lines changed

3 files changed

+192
-5
lines changed

Documentation/git-reset.txt

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,20 @@ SYNOPSIS
99
--------
1010
[verse]
1111
'git reset' [-q] [<tree-ish>] [--] <pathspec>...
12+
'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-nul]] [<tree-ish>]
1213
'git reset' (--patch | -p) [<tree-ish>] [--] [<pathspec>...]
1314
'git reset' [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>]
1415

1516
DESCRIPTION
1617
-----------
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>`,
1920
optionally modifying index and working tree to match.
2021
The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms.
2122

2223
'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
2426
`<pathspec>` to their state at `<tree-ish>`. (It does not affect
2527
the working tree or the current branch.)
2628
+
@@ -101,6 +103,19 @@ OPTIONS
101103
`reset.quiet` config option. `--quiet` and `--no-quiet` will
102104
override the default behavior.
103105

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+
104119
\--::
105120
Do not interpret any more arguments as options.
106121

builtin/reset.c

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
static const char * const git_reset_usage[] = {
3232
N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"),
3333
N_("git reset [-q] [<tree-ish>] [--] <pathspec>..."),
34+
N_("git reset [-q] [--pathspec-from-file [--pathspec-file-nul]] [<tree-ish>]"),
3435
N_("git reset --patch [<tree-ish>] [--] [<pathspec>...]"),
3536
NULL
3637
};
@@ -284,8 +285,8 @@ static int git_reset_config(const char *var, const char *value, void *cb)
284285
int cmd_reset(int argc, const char **argv, const char *prefix)
285286
{
286287
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;
289290
struct object_id oid;
290291
struct pathspec pathspec;
291292
int intent_to_add = 0;
@@ -306,6 +307,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
306307
OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")),
307308
OPT_BOOL('N', "intent-to-add", &intent_to_add,
308309
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),
309312
OPT_END()
310313
};
311314

@@ -316,6 +319,20 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
316319
PARSE_OPT_KEEP_DASHDASH);
317320
parse_args(&pathspec, argv, prefix, patch_mode, &rev);
318321

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+
319336
unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid);
320337
if (unborn) {
321338
/* reset on unborn branch: treat as reset to empty tree */

t/t7107-reset-pathspec-file.sh

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

0 commit comments

Comments
 (0)