Skip to content

Commit bebb5d6

Browse files
SyntevoAlexgitster
authored andcommitted
add: support the --pathspec-from-file option
Decisions taken for simplicity: 1) For now, `--pathspec-from-file` is declared incompatible with `--interactive/--patch/--edit`, even when <file> is not `stdin`. Such use case it not really expected. Also, it would require changes to `interactive_add()` and `edit_patch()`. 2) It is not allowed to pass pathspec in both args and file. Signed-off-by: Alexandr Miloslavskiy <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 21bb308 commit bebb5d6

File tree

3 files changed

+168
-5
lines changed

3 files changed

+168
-5
lines changed

Documentation/git-add.txt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ SYNOPSIS
1111
'git add' [--verbose | -v] [--dry-run | -n] [--force | -f] [--interactive | -i] [--patch | -p]
1212
[--edit | -e] [--[no-]all | --[no-]ignore-removal | [--update | -u]]
1313
[--intent-to-add | -N] [--refresh] [--ignore-errors] [--ignore-missing] [--renormalize]
14-
[--chmod=(+|-)x] [--] [<pathspec>...]
14+
[--chmod=(+|-)x] [--pathspec-from-file=<file> [--pathspec-file-nul]]
15+
[--] [<pathspec>...]
1516

1617
DESCRIPTION
1718
-----------
@@ -187,6 +188,19 @@ for "git add --no-all <pathspec>...", i.e. ignored removed files.
187188
bit is only changed in the index, the files on disk are left
188189
unchanged.
189190

191+
--pathspec-from-file=<file>::
192+
Pathspec is passed in `<file>` instead of commandline args. If
193+
`<file>` is exactly `-` then standard input is used. Pathspec
194+
elements are separated by LF or CR/LF. Pathspec elements can be
195+
quoted as explained for the configuration variable `core.quotePath`
196+
(see linkgit:git-config[1]). See also `--pathspec-file-nul` and
197+
global `--literal-pathspecs`.
198+
199+
--pathspec-file-nul::
200+
Only meaningful with `--pathspec-from-file`. Pathspec elements are
201+
separated with NUL character and all other characters are taken
202+
literally (including newlines and quotes).
203+
190204
\--::
191205
This option can be used to separate command-line options from
192206
the list of files, (useful when filenames might be mistaken

builtin/add.c

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ static const char * const builtin_add_usage[] = {
2828
static int patch_interactive, add_interactive, edit_interactive;
2929
static int take_worktree_changes;
3030
static int add_renormalize;
31+
static int pathspec_file_nul;
32+
static const char *pathspec_from_file;
3133

3234
struct update_callback_data {
3335
int flags;
@@ -309,6 +311,8 @@ static struct option builtin_add_options[] = {
309311
N_("override the executable bit of the listed files")),
310312
OPT_HIDDEN_BOOL(0, "warn-embedded-repo", &warn_on_embedded_repo,
311313
N_("warn when adding an embedded repository")),
314+
OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
315+
OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
312316
OPT_END(),
313317
};
314318

@@ -402,11 +406,17 @@ int cmd_add(int argc, const char **argv, const char *prefix)
402406
builtin_add_usage, PARSE_OPT_KEEP_ARGV0);
403407
if (patch_interactive)
404408
add_interactive = 1;
405-
if (add_interactive)
409+
if (add_interactive) {
410+
if (pathspec_from_file)
411+
die(_("--pathspec-from-file is incompatible with --interactive/--patch"));
406412
exit(interactive_add(argc - 1, argv + 1, prefix, patch_interactive));
413+
}
407414

408-
if (edit_interactive)
415+
if (edit_interactive) {
416+
if (pathspec_from_file)
417+
die(_("--pathspec-from-file is incompatible with --edit"));
409418
return(edit_patch(argc, argv, prefix));
419+
}
410420
argc--;
411421
argv++;
412422

@@ -439,13 +449,25 @@ int cmd_add(int argc, const char **argv, const char *prefix)
439449
PATHSPEC_SYMLINK_LEADING_PATH,
440450
prefix, argv);
441451

442-
if (require_pathspec && argc == 0) {
452+
if (pathspec_from_file) {
453+
if (pathspec.nr)
454+
die(_("--pathspec-from-file is incompatible with pathspec arguments"));
455+
456+
parse_pathspec_file(&pathspec, PATHSPEC_ATTR,
457+
PATHSPEC_PREFER_FULL |
458+
PATHSPEC_SYMLINK_LEADING_PATH,
459+
prefix, pathspec_from_file, pathspec_file_nul);
460+
} else if (pathspec_file_nul) {
461+
die(_("--pathspec-file-nul requires --pathspec-from-file"));
462+
}
463+
464+
if (require_pathspec && pathspec.nr == 0) {
443465
fprintf(stderr, _("Nothing specified, nothing added.\n"));
444466
fprintf(stderr, _("Maybe you wanted to say 'git add .'?\n"));
445467
return 0;
446468
}
447469

448-
if (!take_worktree_changes && addremove_explicit < 0 && argc)
470+
if (!take_worktree_changes && addremove_explicit < 0 && pathspec.nr)
449471
/* Turn "git add pathspec..." to "git add -A pathspec..." */
450472
addremove = 1;
451473

t/t3704-add-pathspec-file.sh

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
#!/bin/sh
2+
3+
test_description='add --pathspec-from-file'
4+
5+
. ./test-lib.sh
6+
7+
test_tick
8+
9+
test_expect_success setup '
10+
test_commit file0 &&
11+
echo A >fileA.t &&
12+
echo B >fileB.t &&
13+
echo C >fileC.t &&
14+
echo D >fileD.t
15+
'
16+
17+
restore_checkpoint () {
18+
git reset
19+
}
20+
21+
verify_expect () {
22+
git status --porcelain --untracked-files=no -- fileA.t fileB.t fileC.t fileD.t >actual &&
23+
test_cmp expect actual
24+
}
25+
26+
test_expect_success '--pathspec-from-file from stdin' '
27+
restore_checkpoint &&
28+
29+
echo fileA.t | git add --pathspec-from-file=- &&
30+
31+
cat >expect <<-\EOF &&
32+
A fileA.t
33+
EOF
34+
verify_expect
35+
'
36+
37+
test_expect_success '--pathspec-from-file from file' '
38+
restore_checkpoint &&
39+
40+
echo fileA.t >list &&
41+
git add --pathspec-from-file=list &&
42+
43+
cat >expect <<-\EOF &&
44+
A fileA.t
45+
EOF
46+
verify_expect
47+
'
48+
49+
test_expect_success 'NUL delimiters' '
50+
restore_checkpoint &&
51+
52+
printf "fileA.t\0fileB.t\0" | git add --pathspec-from-file=- --pathspec-file-nul &&
53+
54+
cat >expect <<-\EOF &&
55+
A fileA.t
56+
A fileB.t
57+
EOF
58+
verify_expect
59+
'
60+
61+
test_expect_success 'LF delimiters' '
62+
restore_checkpoint &&
63+
64+
printf "fileA.t\nfileB.t\n" | git add --pathspec-from-file=- &&
65+
66+
cat >expect <<-\EOF &&
67+
A fileA.t
68+
A fileB.t
69+
EOF
70+
verify_expect
71+
'
72+
73+
test_expect_success 'no trailing delimiter' '
74+
restore_checkpoint &&
75+
76+
printf "fileA.t\nfileB.t" | git add --pathspec-from-file=- &&
77+
78+
cat >expect <<-\EOF &&
79+
A fileA.t
80+
A fileB.t
81+
EOF
82+
verify_expect
83+
'
84+
85+
test_expect_success 'CRLF delimiters' '
86+
restore_checkpoint &&
87+
88+
printf "fileA.t\r\nfileB.t\r\n" | git add --pathspec-from-file=- &&
89+
90+
cat >expect <<-\EOF &&
91+
A fileA.t
92+
A fileB.t
93+
EOF
94+
verify_expect
95+
'
96+
97+
test_expect_success 'quotes' '
98+
restore_checkpoint &&
99+
100+
printf "\"file\\101.t\"" | git add --pathspec-from-file=- &&
101+
102+
cat >expect <<-\EOF &&
103+
A fileA.t
104+
EOF
105+
verify_expect
106+
'
107+
108+
test_expect_success 'quotes not compatible with --pathspec-file-nul' '
109+
restore_checkpoint &&
110+
111+
printf "\"file\\101.t\"" >list &&
112+
test_must_fail git add --pathspec-from-file=list --pathspec-file-nul
113+
'
114+
115+
test_expect_success 'only touches what was listed' '
116+
restore_checkpoint &&
117+
118+
printf "fileB.t\nfileC.t\n" | git add --pathspec-from-file=- &&
119+
120+
cat >expect <<-\EOF &&
121+
A fileB.t
122+
A fileC.t
123+
EOF
124+
verify_expect
125+
'
126+
127+
test_done

0 commit comments

Comments
 (0)