]> git.ipfire.org Git - thirdparty/git.git/commitdiff
commit: support the --pathspec-from-file option
authorAlexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
Tue, 19 Nov 2019 16:48:55 +0000 (16:48 +0000)
committerJunio C Hamano <gitster@pobox.com>
Wed, 20 Nov 2019 04:01:53 +0000 (13:01 +0900)
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 <alexandr.miloslavskiy@syntevo.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Documentation/git-commit.txt
builtin/commit.c
t/t7526-commit-pathspec-file.sh [new file with mode: 0755]

index a0c44978ee0bbb0de53432ca78fd9f305607ae3f..ced5a9beab159de86148b94bc1825247ead79390 100644 (file)
@@ -13,7 +13,8 @@ SYNOPSIS
           [-F <file> | -m <msg>] [--reset-author] [--allow-empty]
           [--allow-empty-message] [--no-verify] [-e] [--author=<author>]
           [--date=<date>] [--cleanup=<mode>] [--[no-]status]
-          [-i | -o] [-S[<keyid>]] [--] [<pathspec>...]
+          [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]
+          [-S[<keyid>]] [--] [<pathspec>...]
 
 DESCRIPTION
 -----------
@@ -278,6 +279,19 @@ FROM UPSTREAM REBASE" section in linkgit:git-rebase[1].)
        already been staged. If used together with `--allow-empty`
        paths are also not required, and an empty commit will be created.
 
+--pathspec-from-file=<file>::
+       Pathspec is passed in `<file>` instead of commandline args. If
+       `<file>` is exactly `-` then standard input is used. Pathspec
+       elements are separated by LF or CR/LF. Pathspec elements can be
+       quoted as explained for the configuration variable `core.quotePath`
+       (see linkgit:git-config[1]). See also `--pathspec-file-nul` and
+       global `--literal-pathspecs`.
+
+--pathspec-file-nul::
+       Only meaningful with `--pathspec-from-file`. Pathspec elements are
+       separated with NUL character and all other characters are taken
+       literally (including newlines and quotes).
+
 -u[<mode>]::
 --untracked-files[=<mode>]::
        Show untracked files.
index e588bc6ad3c66c46ba904c14e39b325055209734..ed40729355423e9cec59318c8e6d46dc53229b41 100644 (file)
@@ -107,9 +107,9 @@ static int all, also, interactive, patch_interactive, only, amend, signoff;
 static int edit_flag = -1; /* unspecified */
 static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship;
 static int config_commit_verbose = -1; /* unspecified */
-static int no_post_rewrite, allow_empty_message;
+static int no_post_rewrite, allow_empty_message, pathspec_file_nul;
 static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg;
-static char *sign_commit;
+static char *sign_commit, *pathspec_from_file;
 
 /*
  * 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
                       PATHSPEC_PREFER_FULL,
                       prefix, argv);
 
+       if (pathspec_from_file) {
+               if (interactive)
+                       die(_("--pathspec-from-file is incompatible with --interactive/--patch"));
+
+               if (pathspec.nr)
+                       die(_("--pathspec-from-file is incompatible with pathspec arguments"));
+
+               parse_pathspec_file(&pathspec, 0,
+                                   PATHSPEC_PREFER_FULL,
+                                   prefix, pathspec_from_file, pathspec_file_nul);
+       } else if (pathspec_file_nul) {
+               die(_("--pathspec-file-nul requires --pathspec-from-file"));
+       }
+
+       if (!pathspec.nr && (also || (only && !amend && !allow_empty)))
+               die(_("No paths with --include/--only does not make sense."));
+
        if (read_cache_preload(&pathspec) < 0)
                die(_("index file corrupt"));
 
@@ -1198,8 +1215,6 @@ static int parse_and_validate_options(int argc, const char *argv[],
 
        if (also + only + all + interactive > 1)
                die(_("Only one of --include/--only/--all/--interactive/--patch can be used."));
-       if (argc == 0 && (also || (only && !amend && !allow_empty)))
-               die(_("No paths with --include/--only does not make sense."));
        cleanup_mode = get_cleanup_mode(cleanup_arg, use_editor);
 
        handle_untracked_files_arg(s);
@@ -1535,6 +1550,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                OPT_BOOL(0, "amend", &amend, N_("amend previous commit")),
                OPT_BOOL(0, "no-post-rewrite", &no_post_rewrite, N_("bypass post-rewrite hook")),
                { 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" },
+               OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
+               OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
                /* end commit contents options */
 
                OPT_HIDDEN_BOOL(0, "allow-empty", &allow_empty,
diff --git a/t/t7526-commit-pathspec-file.sh b/t/t7526-commit-pathspec-file.sh
new file mode 100755 (executable)
index 0000000..a06b683
--- /dev/null
@@ -0,0 +1,130 @@
+#!/bin/sh
+
+test_description='commit --pathspec-from-file'
+
+. ./test-lib.sh
+
+test_tick
+
+test_expect_success setup '
+       test_commit file0 &&
+       git tag checkpoint &&
+
+       echo A >fileA.t &&
+       echo B >fileB.t &&
+       echo C >fileC.t &&
+       echo D >fileD.t &&
+       git add fileA.t fileB.t fileC.t fileD.t
+'
+
+restore_checkpoint () {
+       git reset --soft checkpoint
+}
+
+verify_expect () {
+       git diff-tree --no-commit-id --name-status -r HEAD >actual &&
+       test_cmp expect actual
+}
+
+test_expect_success '--pathspec-from-file from stdin' '
+       restore_checkpoint &&
+
+       echo fileA.t | git commit --pathspec-from-file=- -m "Commit" &&
+
+       cat >expect <<-\EOF &&
+       A       fileA.t
+       EOF
+       verify_expect
+'
+
+test_expect_success '--pathspec-from-file from file' '
+       restore_checkpoint &&
+
+       echo fileA.t >list &&
+       git commit --pathspec-from-file=list -m "Commit" &&
+
+       cat >expect <<-\EOF &&
+       A       fileA.t
+       EOF
+       verify_expect
+'
+
+test_expect_success 'NUL delimiters' '
+       restore_checkpoint &&
+
+       printf "fileA.t\0fileB.t\0" | git commit --pathspec-from-file=- --pathspec-file-nul -m "Commit" &&
+
+       cat >expect <<-\EOF &&
+       A       fileA.t
+       A       fileB.t
+       EOF
+       verify_expect
+'
+
+test_expect_success 'LF delimiters' '
+       restore_checkpoint &&
+
+       printf "fileA.t\nfileB.t\n" | git commit --pathspec-from-file=- -m "Commit" &&
+
+       cat >expect <<-\EOF &&
+       A       fileA.t
+       A       fileB.t
+       EOF
+       verify_expect
+'
+
+test_expect_success 'no trailing delimiter' '
+       restore_checkpoint &&
+
+       printf "fileA.t\nfileB.t" | git commit --pathspec-from-file=- -m "Commit" &&
+
+       cat >expect <<-\EOF &&
+       A       fileA.t
+       A       fileB.t
+       EOF
+       verify_expect
+'
+
+test_expect_success 'CRLF delimiters' '
+       restore_checkpoint &&
+
+       printf "fileA.t\r\nfileB.t\r\n" | git commit --pathspec-from-file=- -m "Commit" &&
+
+       cat >expect <<-\EOF &&
+       A       fileA.t
+       A       fileB.t
+       EOF
+       verify_expect
+'
+
+test_expect_success 'quotes' '
+       restore_checkpoint &&
+
+       printf "\"file\\101.t\"" | git commit --pathspec-from-file=- -m "Commit" &&
+
+       cat >expect <<-\EOF &&
+       A       fileA.t
+       EOF
+       verify_expect expect
+'
+
+test_expect_success 'quotes not compatible with --pathspec-file-nul' '
+       restore_checkpoint &&
+
+       printf "\"file\\101.t\"" >list &&
+       test_must_fail git commit --pathspec-from-file=list --pathspec-file-nul -m "Commit"
+'
+
+test_expect_success 'only touches what was listed' '
+       restore_checkpoint &&
+
+       printf "fileB.t\nfileC.t\n" | git commit --pathspec-from-file=- -m "Commit" &&
+
+       cat >expect <<-\EOF &&
+       A       fileB.t
+       A       fileC.t
+       EOF
+       verify_expect
+'
+
+test_done