@matheusbsbs / matheus-bagliano / commits / bebb5d6d6b
Fork 0

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 <alexandr.miloslavskiy@syntevo.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Alexandr Miloslavskiy committed Dec 3, 2019 at 14:02 UTC bebb5d6d6bf64d5b5f175af81db1e0e2fa1c5ccb
3 files changed +168 -5
Documentation/git-add.txt
+15 -1
@@ -11,7 +11,8 @@ SYNOPSIS
11 'git add' [--verbose | -v] [--dry-run | -n] [--force | -f] [--interactive | -i] [--patch | -p]
12 [--edit | -e] [--[no-]all | --[no-]ignore-removal | [--update | -u]]
13 [--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>...]
16
17 DESCRIPTION
18 -----------
@@ -187,6 +188,19 @@ for "git add --no-all <pathspec>...", i.e. ignored removed files.
188 bit is only changed in the index, the files on disk are left
189 unchanged.
190
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 +
204 \--::
205 This option can be used to separate command-line options from
206 the list of files, (useful when filenames might be mistaken
builtin/add.c
+26 -4
@@ -28,6 +28,8 @@ static const char * const builtin_add_usage[] = {
28 static int patch_interactive, add_interactive, edit_interactive;
29 static int take_worktree_changes;
30 static int add_renormalize;
31 +static int pathspec_file_nul;
32 +static const char *pathspec_from_file;
33
34 struct update_callback_data {
35 int flags;
@@ -309,6 +311,8 @@ static struct option builtin_add_options[] = {
311 N_("override the executable bit of the listed files")),
312 OPT_HIDDEN_BOOL(0, "warn-embedded-repo", &warn_on_embedded_repo,
313 N_("warn when adding an embedded repository")),
314 + OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
315 + OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
316 OPT_END(),
317 };
318
@@ -402,11 +406,17 @@ int cmd_add(int argc, const char **argv, const char *prefix)
406 builtin_add_usage, PARSE_OPT_KEEP_ARGV0);
407 if (patch_interactive)
408 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"));
412 exit(interactive_add(argc - 1, argv + 1, prefix, patch_interactive));
413 + }
414
408 - if (edit_interactive)
415 + if (edit_interactive) {
416 + if (pathspec_from_file)
417 + die(_("--pathspec-from-file is incompatible with --edit"));
418 return(edit_patch(argc, argv, prefix));
419 + }
420 argc--;
421 argv++;
422
@@ -439,13 +449,25 @@ int cmd_add(int argc, const char **argv, const char *prefix)
449 PATHSPEC_SYMLINK_LEADING_PATH,
450 prefix, argv);
451
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) {
465 fprintf(stderr, _("Nothing specified, nothing added.\n"));
466 fprintf(stderr, _("Maybe you wanted to say 'git add .'?\n"));
467 return 0;
468 }
469
448 - if (!take_worktree_changes && addremove_explicit < 0 && argc)
470 + if (!take_worktree_changes && addremove_explicit < 0 && pathspec.nr)
471 /* Turn "git add pathspec..." to "git add -A pathspec..." */
472 addremove = 1;
473
t/t3704-add-pathspec-file.sh new
+127
@@ -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