Skip to content

Commit e6f4d1d

Browse files
SyntevoAlexgitster
authored andcommitted
commit: support the --pathspec-from-file option
Decisions taken for simplicity: 1) For now, `--pathspec-from-file` is declared incompatible with `--interactive/--patch`, even when <file> is not `stdin`. Such use case it not really expected. Also, it would require changes to `interactive_add()`. 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 6bd5e32 commit e6f4d1d

File tree

3 files changed

+165
-5
lines changed

3 files changed

+165
-5
lines changed

Documentation/git-commit.txt

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ SYNOPSIS
1313
[-F <file> | -m <msg>] [--reset-author] [--allow-empty]
1414
[--allow-empty-message] [--no-verify] [-e] [--author=<author>]
1515
[--date=<date>] [--cleanup=<mode>] [--[no-]status]
16-
[-i | -o] [-S[<keyid>]] [--] [<pathspec>...]
16+
[-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]
17+
[-S[<keyid>]] [--] [<pathspec>...]
1718

1819
DESCRIPTION
1920
-----------
@@ -278,6 +279,18 @@ FROM UPSTREAM REBASE" section in linkgit:git-rebase[1].)
278279
already been staged. If used together with `--allow-empty`
279280
paths are also not required, and an empty commit will be created.
280281

282+
--pathspec-from-file=<file>::
283+
Pathspec is passed in `<file>` instead of commandline args. If
284+
`<file>` is exactly `-` then standard input is used. Pathspec
285+
elements are separated by LF or CR/LF. Pathspec elements can be
286+
quoted as explained for the configuration variable `core.quotePath`
287+
(see linkgit:git-config[1]). See also `--pathspec-file-nul` and
288+
global `--literal-pathspecs`.
289+
290+
--pathspec-file-nul::
291+
Only meaningful with `--pathspec-from-file`. Pathspec elements are
292+
separated with NUL character and are not expected to be quoted.
293+
281294
-u[<mode>]::
282295
--untracked-files[=<mode>]::
283296
Show untracked files.

builtin/commit.c

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,9 @@ static int all, also, interactive, patch_interactive, only, amend, signoff;
107107
static int edit_flag = -1; /* unspecified */
108108
static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
109109
static int config_commit_verbose = -1; /* unspecified */
110-
static int no_post_rewrite, allow_empty_message;
110+
static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
111111
static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
112-
static char *sign_commit;
112+
static char *sign_commit, *pathspec_from_file;
113113

114114
/*
115115
* The default commit message cleanup mode will remove the lines
@@ -343,6 +343,23 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
343343
PATHSPEC_PREFER_FULL,
344344
prefix, argv);
345345

346+
if (pathspec_from_file) {
347+
if (interactive)
348+
die(_("--pathspec-from-file is incompatible with --interactive/--patch"));
349+
350+
if (pathspec.nr)
351+
die(_("--pathspec-from-file is incompatible with pathspec arguments"));
352+
353+
parse_pathspec_file(&pathspec, 0,
354+
PATHSPEC_PREFER_FULL,
355+
prefix, pathspec_from_file, pathspec_file_nul);
356+
} else if (pathspec_file_nul) {
357+
die(_("--pathspec-file-nul requires --pathspec-from-file"));
358+
}
359+
360+
if (!pathspec.nr && (also || (only && !amend && !allow_empty)))
361+
die(_("No paths with --include/--only does not make sense."));
362+
346363
if (read_cache_preload(&pathspec) < 0)
347364
die(_("index file corrupt"));
348365

@@ -1198,8 +1215,6 @@ static int parse_and_validate_options(int argc, const char *argv[],
11981215

11991216
if (also + only + all + interactive > 1)
12001217
die(_("Only one of --include/--only/--all/--interactive/--patch can be used."));
1201-
if (argc == 0 && (also || (only && !amend && !allow_empty)))
1202-
die(_("No paths with --include/--only does not make sense."));
12031218
cleanup_mode = get_cleanup_mode(cleanup_arg, use_editor);
12041219

12051220
handle_untracked_files_arg(s);
@@ -1535,6 +1550,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
15351550
OPT_BOOL(0, "amend", &amend, N_("amend previous commit")),
15361551
OPT_BOOL(0, "no-post-rewrite", &no_post_rewrite, N_("bypass post-rewrite hook")),
15371552
{ OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, N_("mode"), N_("show untracked files, optional modes: all, normal, no. (Default: all)"), PARSE_OPT_OPTARG, NULL, (intptr_t)"all" },
1553+
OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
1554+
OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
15381555
/* end commit contents options */
15391556

15401557
OPT_HIDDEN_BOOL(0, "allow-empty", &allow_empty,

t/t7526-commit-pathspec-file.sh

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

0 commit comments

Comments
 (0)