git@vger.kernel.org list mirror (unofficial, one of many)
 help / color / Atom feed
* [PATCH 0/5] Add --pathspec-from-file option for reset, commit
@ 2019-11-04 19:26 Alexandr Miloslavskiy via GitGitGadget
  2019-11-04 19:26 ` [PATCH 1/5] pathspec: add new function to parse file Alexandr Miloslavskiy via GitGitGadget
                   ` (5 more replies)
  0 siblings, 6 replies; 80+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-04 19:26 UTC (permalink / raw)
  To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano

Adds --pathspec-from-file option for porcelain commands to avoid commandline
length limit.

So far I implemented it for git commit and git reset, but my goal is to
support other commands as well after these patches are reviewed.

The patches are based on the following discussions:
https://public-inbox.org/git/c3be6eff-365b-96b8-16d2-0528612fc1fc@syntevo.com/T/#u
There, --stdin-paths was suggested.
https://public-inbox.org/git/a38bc928-7ccd-e2d9-b89b-23298e9fa95d@syntevo.com/T/#u
There, --stdin-paths was extended to --paths-file.
https://public-inbox.org/git/pull.133.git.gitgitgadget@gmail.com/
https://github.com/gitgitgadget/git/pull/133Patch from Johannes, I used it
as base of my patch. There, --pathspec-from-file is suggested in discussion.

Major changes compared to patch from Johannes:

1) --pathspec-from-file allows to read file, not just stdin 2) 
--literal-pathspecs should be honored. This is a good improvement because it
keeps --pathspec-from-file much closer to passing args on commandline. Since
the goal of the new option is to avoid commandline length limit, both
behaviors should be close to each other. 3) Patches are designed with all
other git commands in mind

Alexandr Miloslavskiy (5):
  pathspec: add new function to parse file
  doc: reset: unify <pathspec> description
  reset: support the --pathspec-from-file option
  doc: commit: unify <pathspec> description
  commit: support the --pathspec-from-file option

 Documentation/git-commit.txt    |  19 ++++-
 Documentation/git-reset.txt     |  40 +++++++---
 builtin/commit.c                |  25 ++++++-
 builtin/reset.c                 |  22 +++++-
 pathspec.c                      |  41 +++++++++++
 pathspec.h                      |  10 +++
 t/t7107-reset-pathspec-file.sh  | 126 ++++++++++++++++++++++++++++++++
 t/t7526-commit-pathspec-file.sh | 107 +++++++++++++++++++++++++++
 8 files changed, 371 insertions(+), 19 deletions(-)
 create mode 100755 t/t7107-reset-pathspec-file.sh
 create mode 100755 t/t7526-commit-pathspec-file.sh


base-commit: da72936f544fec5a335e66432610e4cef4430991
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-445%2FSyntevoAlex%2F%230207_pathspec_from_file-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-445/SyntevoAlex/#0207_pathspec_from_file-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/445
-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH 1/5] pathspec: add new function to parse file
  2019-11-04 19:26 [PATCH 0/5] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget
@ 2019-11-04 19:26 ` Alexandr Miloslavskiy via GitGitGadget
  2019-11-05 15:02   ` Phillip Wood
  2019-11-04 19:26 ` [PATCH 2/5] doc: reset: unify <pathspec> description Alexandr Miloslavskiy via GitGitGadget
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 80+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-04 19:26 UTC (permalink / raw)
  To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy

From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>

This will be used to support the new option '--pathspec-from-file' in
`git add`, `git-commit`, `git reset` etc.

Note also that we specifically handle CR/LF line endings to support
Windows better.

To simplify code, file is first parsed into `argv_array`. This allows
to avoid refactoring `parse_pathspec()`.

I considered adding `nul_term_line` to `flags` instead, but decided
that it doesn't fit there.

The new code is mostly taken from `cmd_update_index()` and
`split_mail_conv()`.

Co-authored-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 pathspec.c | 41 +++++++++++++++++++++++++++++++++++++++++
 pathspec.h | 10 ++++++++++
 2 files changed, 51 insertions(+)

diff --git a/pathspec.c b/pathspec.c
index 12c2b322b3..97d4e77875 100644
--- a/pathspec.c
+++ b/pathspec.c
@@ -3,6 +3,8 @@
 #include "dir.h"
 #include "pathspec.h"
 #include "attr.h"
+#include "argv-array.h"
+#include "quote.h"
 
 /*
  * Finds which of the given pathspecs match items in the index.
@@ -613,6 +615,45 @@ void parse_pathspec(struct pathspec *pathspec,
 	}
 }
 
+void parse_pathspec_file(struct pathspec *pathspec, unsigned magic_mask,
+			 unsigned flags, const char *prefix,
+			 const char *file, int nul_term_line)
+{
+	struct argv_array parsed_file = ARGV_ARRAY_INIT;
+	strbuf_getline_fn getline_fn = nul_term_line ? strbuf_getline_nul :
+						       strbuf_getline;
+	struct strbuf buf = STRBUF_INIT;
+	struct strbuf unquoted = STRBUF_INIT;
+	FILE *in = NULL;
+
+	if (!strcmp(file, "-"))
+		in = stdin;
+	else
+		in = fopen(file, "r");
+
+	if (!in)
+		die(_("could not open '%s' for reading"), file);
+
+	while (getline_fn(&buf, in) != EOF) {
+		if (!nul_term_line && buf.buf[0] == '"') {
+			strbuf_reset(&unquoted);
+			if (unquote_c_style(&unquoted, buf.buf, NULL))
+				die(_("line is badly quoted"));
+			strbuf_swap(&buf, &unquoted);
+		}
+		argv_array_push(&parsed_file, buf.buf);
+		strbuf_reset(&buf);
+	}
+
+	strbuf_release(&unquoted);
+	strbuf_release(&buf);
+	if (in != stdin)
+		fclose(in);
+
+	parse_pathspec(pathspec, magic_mask, flags, prefix, parsed_file.argv);
+	argv_array_clear(&parsed_file);
+}
+
 void copy_pathspec(struct pathspec *dst, const struct pathspec *src)
 {
 	int i, j;
diff --git a/pathspec.h b/pathspec.h
index 1c18a2c90c..d72e0b4c4a 100644
--- a/pathspec.h
+++ b/pathspec.h
@@ -85,6 +85,16 @@ void parse_pathspec(struct pathspec *pathspec,
 		    unsigned flags,
 		    const char *prefix,
 		    const char **args);
+/*
+ * Same as parse_pathspec() but uses file as input.
+ * When 'file' is exactly "-" it uses 'stdin' instead.
+ */
+void parse_pathspec_file(struct pathspec *pathspec,
+		    unsigned magic_mask,
+		    unsigned flags,
+		    const char *prefix,
+		    const char *file,
+		    int nul_term_line);
 void copy_pathspec(struct pathspec *dst, const struct pathspec *src);
 void clear_pathspec(struct pathspec *);
 
-- 
gitgitgadget


^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH 2/5] doc: reset: unify <pathspec> description
  2019-11-04 19:26 [PATCH 0/5] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget
  2019-11-04 19:26 ` [PATCH 1/5] pathspec: add new function to parse file Alexandr Miloslavskiy via GitGitGadget
@ 2019-11-04 19:26 ` Alexandr Miloslavskiy via GitGitGadget
  2019-11-06  4:01   ` Junio C Hamano
  2019-11-04 19:26 ` [PATCH 3/5] reset: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 80+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-04 19:26 UTC (permalink / raw)
  To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy

From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>

Synchronize it to `git add`, which has a pretty good description.

Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 Documentation/git-reset.txt | 24 +++++++++++++++---------
 1 file changed, 15 insertions(+), 9 deletions(-)

diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index 97e0544d9e..b0ea6e0ce5 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -8,8 +8,8 @@ git-reset - Reset current HEAD to the specified state
 SYNOPSIS
 --------
 [verse]
-'git reset' [-q] [<tree-ish>] [--] <paths>...
-'git reset' (--patch | -p) [<tree-ish>] [--] [<paths>...]
+'git reset' [-q] [<tree-ish>] [--] <pathspec>...
+'git reset' (--patch | -p) [<tree-ish>] [--] [<pathspec>...]
 'git reset' [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>]
 
 DESCRIPTION
@@ -19,27 +19,33 @@ In the third form, set the current branch head (`HEAD`) to `<commit>`,
 optionally modifying index and working tree to match.
 The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms.
 
-'git reset' [-q] [<tree-ish>] [--] <paths>...::
-	This form resets the index entries for all `<paths>` to their
+'git reset' [-q] [<tree-ish>] [--] <pathspec>...::
+	This form resets the index entries for all `<pathspec>` to their
 	state at `<tree-ish>`.  (It does not affect the working tree or
 	the current branch.)
 +
-This means that `git reset <paths>` is the opposite of `git add
-<paths>`. This command is equivalent to
-`git restore [--source=<tree-ish>] --staged <paths>...`.
+For more details about the <pathspec> syntax, see the 'pathspec' entry
+in linkgit:gitglossary[7].
 +
-After running `git reset <paths>` to update the index entry, you can
+This means that `git reset <pathspec>` is the opposite of `git add
+<pathspec>`. This command is equivalent to
+`git restore [--source=<tree-ish>] --staged <pathspec>...`.
++
+After running `git reset <pathspec>` to update the index entry, you can
 use linkgit:git-restore[1] to check the contents out of the index to
 the working tree. Alternatively, using linkgit:git-restore[1]
 and specifying a commit with `--source`, you
 can copy the contents of a path out of a commit to the index and to the
 working tree in one go.
 
-'git reset' (--patch | -p) [<tree-ish>] [--] [<paths>...]::
+'git reset' (--patch | -p) [<tree-ish>] [--] [<pathspec>...]::
 	Interactively select hunks in the difference between the index
 	and `<tree-ish>` (defaults to `HEAD`).  The chosen hunks are applied
 	in reverse to the index.
 +
+For more details about the <pathspec> syntax, see the 'pathspec' entry
+in linkgit:gitglossary[7].
++
 This means that `git reset -p` is the opposite of `git add -p`, i.e.
 you can use it to selectively reset hunks. See the ``Interactive Mode''
 section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
-- 
gitgitgadget


^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH 3/5] reset: support the --pathspec-from-file option
  2019-11-04 19:26 [PATCH 0/5] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget
  2019-11-04 19:26 ` [PATCH 1/5] pathspec: add new function to parse file Alexandr Miloslavskiy via GitGitGadget
  2019-11-04 19:26 ` [PATCH 2/5] doc: reset: unify <pathspec> description Alexandr Miloslavskiy via GitGitGadget
@ 2019-11-04 19:26 ` Alexandr Miloslavskiy via GitGitGadget
  2019-11-05 15:03   ` Phillip Wood
                     ` (2 more replies)
  2019-11-04 19:26 ` [PATCH 4/5] doc: commit: unify <pathspec> description Alexandr Miloslavskiy via GitGitGadget
                   ` (2 subsequent siblings)
  5 siblings, 3 replies; 80+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-04 19:26 UTC (permalink / raw)
  To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy

From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>

This option solves the problem of commandline length limit for UI's
built on top of git. Plumbing commands are not always a good fit, for
two major reasons:
1) Some UI's serve as assistants that help user run git commands. In
   this case, replacing familiar commands with plumbing commands will
   confuse most users.
2) Some UI's have started and grown with porcelain commands. Replacing
   existing logic with plumbing commands could be cumbersome and prone
   to various new problems.

The new option is designed to behave very close to pathspecs passed in
commandline args, so that switching from one to another is simple.

The new option allows to read either a specified file or `stdin`.
Reading from file is a good way to avoid competing for stdin, and
also gives some extra flexibility.

Decisions taken for simplicity:
1) The new option is declared incompatible with other options that
   could use `stdin`.
2) It is not allowed to pass some refspecs in args and others in file.
3) New options do not have shorthands to avoid shorthand conflicts.

Also add new '--pathspec-file-null' switch that mirrors '-z' used in
various places. Some porcelain commands, such as `git commit`, already
use '-z', therefore it needed a new unambiguous name.

Co-authored-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 Documentation/git-reset.txt    |  18 ++++-
 builtin/reset.c                |  22 +++++-
 t/t7107-reset-pathspec-file.sh | 126 +++++++++++++++++++++++++++++++++
 3 files changed, 161 insertions(+), 5 deletions(-)
 create mode 100755 t/t7107-reset-pathspec-file.sh

diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index b0ea6e0ce5..d484cd2827 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -9,18 +9,20 @@ SYNOPSIS
 --------
 [verse]
 'git reset' [-q] [<tree-ish>] [--] <pathspec>...
+'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-null]] [<tree-ish>]
 'git reset' (--patch | -p) [<tree-ish>] [--] [<pathspec>...]
 'git reset' [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>]
 
 DESCRIPTION
 -----------
-In the first and second form, copy entries from `<tree-ish>` to the index.
-In the third form, set the current branch head (`HEAD`) to `<commit>`,
+In the first three forms, copy entries from `<tree-ish>` to the index.
+In the last form, set the current branch head (`HEAD`) to `<commit>`,
 optionally modifying index and working tree to match.
 The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms.
 
 'git reset' [-q] [<tree-ish>] [--] <pathspec>...::
-	This form resets the index entries for all `<pathspec>` to their
+'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-null]] [<tree-ish>]::
+	These forms reset the index entries for all `<pathspec>` to their
 	state at `<tree-ish>`.  (It does not affect the working tree or
 	the current branch.)
 +
@@ -107,6 +109,16 @@ OPTIONS
 	`reset.quiet` config option. `--quiet` and `--no-quiet` will
 	override the default behavior.
 
+--pathspec-from-file=<file>::
+	Read `<pathspec>` from `<file>` instead. If `<file>` is exactly `-`
+	then read from standard input. Pathspecs are separated by LF or
+	CR/LF. Pathspecs can be quoted as explained for the configuration
+	variable `core.quotePath` (see linkgit:git-config[1]). See also
+	`--pathspec-file-null` and global `--literal-pathspecs`.
+
+--pathspec-file-null::
+	Only meaningful with `--pathspec-from-file`. Pathspecs are
+	separated with NUL character and are not expected to be quoted.
 
 EXAMPLES
 --------
diff --git a/builtin/reset.c b/builtin/reset.c
index fdd572168b..0eaa6b0bca 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -31,6 +31,7 @@
 static const char * const git_reset_usage[] = {
 	N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"),
 	N_("git reset [-q] [<tree-ish>] [--] <paths>..."),
+	N_("git reset [-q] [--pathspec-from-file [--pathspec-file-null]] [<tree-ish>]"),
 	N_("git reset --patch [<tree-ish>] [--] [<paths>...]"),
 	NULL
 };
@@ -284,8 +285,8 @@ static int git_reset_config(const char *var, const char *value, void *cb)
 int cmd_reset(int argc, const char **argv, const char *prefix)
 {
 	int reset_type = NONE, update_ref_status = 0, quiet = 0;
-	int patch_mode = 0, unborn;
-	const char *rev;
+	int patch_mode = 0, pathspec_file_null = 0, unborn;
+	const char *rev, *pathspec_from_file = NULL;
 	struct object_id oid;
 	struct pathspec pathspec;
 	int intent_to_add = 0;
@@ -306,6 +307,10 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 		OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")),
 		OPT_BOOL('N', "intent-to-add", &intent_to_add,
 				N_("record only the fact that removed paths will be added later")),
+		OPT_FILENAME(0, "pathspec-from-file", &pathspec_from_file,
+				N_("read pathspecs from file")),
+		OPT_BOOL(0, "pathspec-file-null", &pathspec_file_null,
+				N_("with --pathspec-from-file, pathspecs are separated with NUL character")),
 		OPT_END()
 	};
 
@@ -316,6 +321,19 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 						PARSE_OPT_KEEP_DASHDASH);
 	parse_args(&pathspec, argv, prefix, patch_mode, &rev);
 
+	if (pathspec_from_file) {
+		if (patch_mode)
+			die(_("--pathspec-from-file is incompatible with --patch"));
+
+		if (pathspec.nr)
+			die(_("--pathspec-from-file is incompatible with path arguments"));
+
+		parse_pathspec_file(&pathspec, 0,
+				    PATHSPEC_PREFER_FULL,
+				    prefix, pathspec_from_file, pathspec_file_null);
+	} else if (pathspec_file_null)
+		die(_("--pathspec-file-null requires --pathspec-from-file"));
+
 	unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid);
 	if (unborn) {
 		/* reset on unborn branch: treat as reset to empty tree */
diff --git a/t/t7107-reset-pathspec-file.sh b/t/t7107-reset-pathspec-file.sh
new file mode 100755
index 0000000000..cf7f085ad5
--- /dev/null
+++ b/t/t7107-reset-pathspec-file.sh
@@ -0,0 +1,126 @@
+#!/bin/sh
+
+test_description='reset --pathspec-from-file'
+
+. ./test-lib.sh
+
+cat > expect.a <<EOF
+ D fileA.t
+EOF
+
+cat > expect.ab <<EOF
+ D fileA.t
+ D fileB.t
+EOF
+
+cat > expect.a_bc_d <<EOF
+D  fileA.t
+ D fileB.t
+ D fileC.t
+D  fileD.t
+EOF
+
+test_expect_success setup '
+	echo A >fileA.t &&
+	echo B >fileB.t &&
+	echo C >fileC.t &&
+	echo D >fileD.t &&
+	git add . &&
+	git commit --include . -m "Commit" &&
+	checkpoint=$(git rev-parse --verify HEAD)
+'
+
+restore_checkpoint () {
+	git reset --hard "$checkpoint"
+}
+
+verify_state () {
+	git status --porcelain -- fileA.t fileB.t fileC.t fileD.t >actual &&
+	test_cmp "$1" actual
+}
+
+test_expect_success '--pathspec-from-file from stdin' '
+	restore_checkpoint &&
+
+	git rm fileA.t &&
+	echo fileA.t | git reset --pathspec-from-file=- &&
+
+	verify_state expect.a
+'
+
+test_expect_success '--pathspec-from-file from file' '
+	restore_checkpoint &&
+
+	git rm fileA.t &&
+	echo fileA.t >list &&
+	git reset --pathspec-from-file=list &&
+
+	verify_state expect.a
+'
+
+test_expect_success 'NUL delimiters' '
+	restore_checkpoint &&
+
+	git rm fileA.t fileB.t &&
+	printf fileA.tQfileB.t | q_to_nul | git reset --pathspec-from-file=- --pathspec-file-null &&
+
+	verify_state expect.ab
+'
+
+test_expect_success 'LF delimiters' '
+	restore_checkpoint &&
+
+	git rm fileA.t fileB.t &&
+	printf "fileA.t\nfileB.t" | git reset --pathspec-from-file=- &&
+
+	verify_state expect.ab
+'
+
+test_expect_success 'CRLF delimiters' '
+	restore_checkpoint &&
+
+	git rm fileA.t fileB.t &&
+	printf "fileA.t\r\nfileB.t" | git reset --pathspec-from-file=- &&
+
+	verify_state expect.ab
+'
+
+test_expect_success 'quotes' '
+	restore_checkpoint &&
+
+	git rm fileA.t &&
+	printf "\"file\\101.t\"" | git reset --pathspec-from-file=- &&
+
+	verify_state expect.a
+'
+
+test_expect_success 'quotes not compatible with --pathspec-file-null' '
+	restore_checkpoint &&
+
+	git rm fileA.t &&
+	printf "\"file\\101.t\"" >list &&
+	# Note: "git reset" has not yet learned to fail on wrong pathspecs
+	git reset --pathspec-from-file=list --pathspec-file-null &&
+	
+	test_must_fail verify_state expect.a
+'
+
+test_expect_success '--pathspec-from-file is not compatible with --soft --hard' '
+	restore_checkpoint &&
+
+	git rm fileA.t &&
+	echo fileA.t >list &&
+	test_must_fail git reset --soft --pathspec-from-file=list &&
+	test_must_fail git reset --hard --pathspec-from-file=list
+'
+
+test_expect_success 'only touches what was listed' '
+	restore_checkpoint &&
+
+	git rm fileA.t fileB.t fileC.t fileD.t &&
+	printf "fileB.t\nfileC.t" | git reset --pathspec-from-file=- &&
+
+	verify_state expect.a_bc_d
+'
+
+test_done
-- 
gitgitgadget


^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH 4/5] doc: commit: unify <pathspec> description
  2019-11-04 19:26 [PATCH 0/5] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget
                   ` (2 preceding siblings ...)
  2019-11-04 19:26 ` [PATCH 3/5] reset: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
@ 2019-11-04 19:26 ` Alexandr Miloslavskiy via GitGitGadget
  2019-11-06  4:50   ` Junio C Hamano
  2019-11-04 19:26 ` [PATCH 5/5] commit: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
  2019-11-06 15:51 ` [PATCH v2 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget
  5 siblings, 1 reply; 80+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-04 19:26 UTC (permalink / raw)
  To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy

From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>

Synchronize it to `git add`, which has a pretty good description.
This also better disambiguates <file>... header.

Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 Documentation/git-commit.txt | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index afa7b75a23..4341d0e3ab 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -13,7 +13,7 @@ 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>]] [--] [<file>...]
+	   [-i | -o] [-S[<keyid>]] [--] [<pathspec>...]
 
 DESCRIPTION
 -----------
@@ -345,12 +345,15 @@ changes to tracked files.
 \--::
 	Do not interpret any more arguments as options.
 
-<file>...::
+<pathspec>...::
 	When files are given on the command line, the command
 	commits the contents of the named files, without
 	recording the changes already staged.  The contents of
 	these files are also staged for the next commit on top
 	of what have been staged before.
++
+For more details about the <pathspec> syntax, see the 'pathspec' entry
+in linkgit:gitglossary[7].
 
 :git-commit: 1
 include::date-formats.txt[]
-- 
gitgitgadget


^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH 5/5] commit: support the --pathspec-from-file option
  2019-11-04 19:26 [PATCH 0/5] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget
                   ` (3 preceding siblings ...)
  2019-11-04 19:26 ` [PATCH 4/5] doc: commit: unify <pathspec> description Alexandr Miloslavskiy via GitGitGadget
@ 2019-11-04 19:26 ` Alexandr Miloslavskiy via GitGitGadget
  2019-11-05 16:27   ` Phillip Wood
  2019-11-06  4:51   ` Junio C Hamano
  2019-11-06 15:51 ` [PATCH v2 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget
  5 siblings, 2 replies; 80+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-04 19:26 UTC (permalink / raw)
  To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy

From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>

This option solves the problem of commandline length limit for UI's
built on top of git. Plumbing commands are not always a good fit, for
two major reasons:
1) Some UI's serve as assistants that help user run git commands. In
   this case, replacing familiar commands with plumbing commands will
   confuse most users.
2) Some UI's have started and grown with porcelain commands. Replacing
   existing logic with plumbing commands could be cumbersome and prone
   to various new problems.

The new option is designed to behave very close to pathspecs passed in
commandline args, so that switching from one to another is simple.

The new option allows to read either a specified file or `stdin`.
Reading from file is a good way to avoid competing for stdin, and
also gives some extra flexibility.

Decisions taken for simplicity:
1) The new option is declared incompatible with other options that
   could use `stdin`.
2) It is not allowed to pass some refspecs in args and others in file.
3) New options do not have shorthands to avoid shorthand conflicts.

Also add new '--pathspec-file-null' switch that mirrors '-z' used in
various places. Some porcelain commands, such as `git commit`, already
use '-z', therefore it needed a new unambiguous name.

Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 Documentation/git-commit.txt    |  14 ++++-
 builtin/commit.c                |  25 ++++++--
 t/t7526-commit-pathspec-file.sh | 107 ++++++++++++++++++++++++++++++++
 3 files changed, 141 insertions(+), 5 deletions(-)
 create mode 100755 t/t7526-commit-pathspec-file.sh

diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index 4341d0e3ab..ec4752298d 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -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-null]]
+	   [-S[<keyid>]] [--] [<pathspec>...]
 
 DESCRIPTION
 -----------
@@ -277,6 +278,17 @@ FROM UPSTREAM REBASE" section in linkgit:git-rebase[1].)
 	the last commit without committing changes that have
 	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>::
+	Read `<pathspec>` from `<file>` instead. If `<file>` is exactly `-`
+	then read from standard input. Pathspecs are separated by LF or
+	CR/LF. Pathspecs can be quoted as explained for the configuration
+	variable `core.quotePath` (see linkgit:git-config[1]). See also
+	`--pathspec-file-null` and global `--literal-pathspecs`.
+
+--pathspec-file-null::
+	Only meaningful with `--pathspec-from-file`. Pathspecs are
+	separated with NUL character and are not expected to be quoted.
 
 -u[<mode>]::
 --untracked-files[=<mode>]::
diff --git a/builtin/commit.c b/builtin/commit.c
index e588bc6ad3..532f305926 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -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_null;
 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 path arguments"));
+
+		parse_pathspec_file(&pathspec, 0,
+				    PATHSPEC_PREFER_FULL,
+				    prefix, pathspec_from_file, pathspec_file_null);
+	}
+	else if (pathspec_file_null)
+		die(_("--pathspec-file-null 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_FILENAME(0, "pathspec-from-file", &pathspec_from_file, N_("read pathspecs from file")),
+		OPT_BOOL(0, "pathspec-file-null", &pathspec_file_null, N_("with --pathspec-from-file, pathspecs are separated with NUL character")),
 		/* 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
index 0000000000..c5d68e01e6
--- /dev/null
+++ b/t/t7526-commit-pathspec-file.sh
@@ -0,0 +1,107 @@
+#!/bin/sh
+
+test_description='commit --pathspec-from-file'
+
+. ./test-lib.sh
+
+test_tick
+
+cat > expect.a <<EOF
+A	fileA.t
+EOF
+
+cat > expect.ab <<EOF
+A	fileA.t
+A	fileB.t
+EOF
+
+cat > expect.bc <<EOF
+A	fileB.t
+A	fileC.t
+EOF
+
+test_expect_success setup '
+	test_commit file0 &&
+	checkpoint=$(git rev-parse --verify HEAD) &&
+	
+	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_state () {
+	git diff-tree --no-commit-id --name-status -r HEAD >actual &&
+	test_cmp "$1" actual
+}
+
+test_expect_success '--pathspec-from-file from stdin' '
+	restore_checkpoint &&
+
+	echo fileA.t | git commit --pathspec-from-file=- -m "Commit" &&
+
+	verify_state expect.a
+'
+
+test_expect_success '--pathspec-from-file from file' '
+	restore_checkpoint &&
+
+	echo fileA.t >list &&
+	git commit --pathspec-from-file=list -m "Commit" &&
+
+	verify_state expect.a
+'
+
+test_expect_success 'NUL delimiters' '
+	restore_checkpoint &&
+
+	printf fileA.tQfileB.t | q_to_nul | git commit --pathspec-from-file=- --pathspec-file-null -m "Commit" &&
+
+	verify_state expect.ab
+'
+
+test_expect_success 'LF delimiters' '
+	restore_checkpoint &&
+
+	printf "fileA.t\nfileB.t" | git commit --pathspec-from-file=- -m "Commit" &&
+
+	verify_state expect.ab
+'
+
+test_expect_success 'CRLF delimiters' '
+	restore_checkpoint &&
+
+	printf "fileA.t\r\nfileB.t" | git commit --pathspec-from-file=- -m "Commit" &&
+
+	verify_state expect.ab
+'
+
+test_expect_success 'quotes' '
+	restore_checkpoint &&
+
+	printf "\"file\\101.t\"" | git commit --pathspec-from-file=- -m "Commit" &&
+
+	verify_state expect.a
+'
+
+test_expect_success 'quotes not compatible with --pathspec-file-null' '
+	restore_checkpoint &&
+
+	printf "\"file\\101.t\"" >list &&
+	test_must_fail git commit --pathspec-from-file=list --pathspec-file-null -m "Commit"
+'
+
+test_expect_success 'only touches what was listed' '
+	restore_checkpoint &&
+
+	printf "fileB.t\nfileC.t" | git commit --pathspec-from-file=- -m "Commit" &&
+
+	verify_state expect.bc
+'
+
+test_done
-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 1/5] pathspec: add new function to parse file
  2019-11-04 19:26 ` [PATCH 1/5] pathspec: add new function to parse file Alexandr Miloslavskiy via GitGitGadget
@ 2019-11-05 15:02   ` Phillip Wood
  2019-11-05 19:14     ` Alexandr Miloslavskiy
  2019-11-06 15:56     ` Alexandr Miloslavskiy
  0 siblings, 2 replies; 80+ messages in thread
From: Phillip Wood @ 2019-11-05 15:02 UTC (permalink / raw)
  To: Alexandr Miloslavskiy via GitGitGadget, git
  Cc: Alexandr Miloslavskiy, Junio C Hamano

Hi Alexandr

Thanks for working on this. I've got a couple of comments about 
improving the error messages but this looks fine to me

On 04/11/2019 19:26, Alexandr Miloslavskiy via GitGitGadget wrote:
> From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
> 
> This will be used to support the new option '--pathspec-from-file' in
> `git add`, `git-commit`, `git reset` etc.
> 
> Note also that we specifically handle CR/LF line endings to support
> Windows better.
> 
> To simplify code, file is first parsed into `argv_array`. This allows
> to avoid refactoring `parse_pathspec()`.
> 
> I considered adding `nul_term_line` to `flags` instead, but decided
> that it doesn't fit there.
> 
> The new code is mostly taken from `cmd_update_index()` and
> `split_mail_conv()`.
> 
> Co-authored-by: Johannes Schindelin <johannes.schindelin@gmx.de>
> Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
> ---
>   pathspec.c | 41 +++++++++++++++++++++++++++++++++++++++++
>   pathspec.h | 10 ++++++++++
>   2 files changed, 51 insertions(+)
> 
> diff --git a/pathspec.c b/pathspec.c
> index 12c2b322b3..97d4e77875 100644
> --- a/pathspec.c
> +++ b/pathspec.c
> @@ -3,6 +3,8 @@
>   #include "dir.h"
>   #include "pathspec.h"
>   #include "attr.h"
> +#include "argv-array.h"
> +#include "quote.h"
>   
>   /*
>    * Finds which of the given pathspecs match items in the index.
> @@ -613,6 +615,45 @@ void parse_pathspec(struct pathspec *pathspec,
>   	}
>   }
>   
> +void parse_pathspec_file(struct pathspec *pathspec, unsigned magic_mask,
> +			 unsigned flags, const char *prefix,
> +			 const char *file, int nul_term_line)
> +{
> +	struct argv_array parsed_file = ARGV_ARRAY_INIT;
> +	strbuf_getline_fn getline_fn = nul_term_line ? strbuf_getline_nul :
> +						       strbuf_getline;
> +	struct strbuf buf = STRBUF_INIT;
> +	struct strbuf unquoted = STRBUF_INIT;
> +	FILE *in = NULL;
> +
> +	if (!strcmp(file, "-"))
> +		in = stdin;
> +	else
> +		in = fopen(file, "r");
> +
> +	if (!in)
> +		die(_("could not open '%s' for reading"), file);

die_errno() would give a more informative message here

> +	while (getline_fn(&buf, in) != EOF) {
> +		if (!nul_term_line && buf.buf[0] == '"') {
> +			strbuf_reset(&unquoted);
> +			if (unquote_c_style(&unquoted, buf.buf, NULL))
> +				die(_("line is badly quoted"));

It would be nice to show the offending line in the error message

> +			strbuf_swap(&buf, &unquoted);
> +		}
> +		argv_array_push(&parsed_file, buf.buf);
> +		strbuf_reset(&buf);
> +	}
> +
> +	strbuf_release(&unquoted);
> +	strbuf_release(&buf);
> +	if (in != stdin)
> +		fclose(in);
> +
> +	parse_pathspec(pathspec, magic_mask, flags, prefix, parsed_file.argv);
> +	argv_array_clear(&parsed_file);
> +}
> +
>   void copy_pathspec(struct pathspec *dst, const struct pathspec *src)
>   {
>   	int i, j;
> diff --git a/pathspec.h b/pathspec.h
> index 1c18a2c90c..d72e0b4c4a 100644
> --- a/pathspec.h
> +++ b/pathspec.h
> @@ -85,6 +85,16 @@ void parse_pathspec(struct pathspec *pathspec,
>   		    unsigned flags,
>   		    const char *prefix,
>   		    const char **args);
> +/*
> + * Same as parse_pathspec() but uses file as input.
> + * When 'file' is exactly "-" it uses 'stdin' instead.
> + */
> +void parse_pathspec_file(struct pathspec *pathspec,
> +		    unsigned magic_mask,
> +		    unsigned flags,
> +		    const char *prefix,
> +		    const char *file,
> +		    int nul_term_line);

Do these align with the 's' in "struct pathspec" ?

Best Wishes

Phillip

>   void copy_pathspec(struct pathspec *dst, const struct pathspec *src);
>   void clear_pathspec(struct pathspec *);
>   
> 

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 3/5] reset: support the --pathspec-from-file option
  2019-11-04 19:26 ` [PATCH 3/5] reset: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
@ 2019-11-05 15:03   ` Phillip Wood
  2019-11-05 19:22     ` Phillip Wood
                       ` (2 more replies)
  2019-11-05 16:14   ` Phillip Wood
  2019-11-06  4:40   ` Junio C Hamano
  2 siblings, 3 replies; 80+ messages in thread
From: Phillip Wood @ 2019-11-05 15:03 UTC (permalink / raw)
  To: Alexandr Miloslavskiy via GitGitGadget, git
  Cc: Alexandr Miloslavskiy, Junio C Hamano

Hi Alexandr

This also looks good, I've got some minor comments below. If I'm 
complaining about whitespace and style issues you know the patch is 
quite good!

On 04/11/2019 19:26, Alexandr Miloslavskiy via GitGitGadget wrote:
> From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
> 
> This option solves the problem of commandline length limit for UI's
> built on top of git. Plumbing commands are not always a good fit, for
> two major reasons:
> 1) Some UI's serve as assistants that help user run git commands. In
>     this case, replacing familiar commands with plumbing commands will
>     confuse most users.
> 2) Some UI's have started and grown with porcelain commands. Replacing
>     existing logic with plumbing commands could be cumbersome and prone
>     to various new problems.

I think reset is a good example of the second reason - it's not straight 
forward to implement it in plumbing commands

> The new option is designed to behave very close to pathspecs passed in
> commandline args, so that switching from one to another is simple.
> 
> The new option allows to read either a specified file or `stdin`.
> Reading from file is a good way to avoid competing for stdin, and
> also gives some extra flexibility.

I think this flexibility is a good idea

> Decisions taken for simplicity:
> 1) The new option is declared incompatible with other options that
>     could use `stdin`.

I'm confused reset does not use stdin does it?

> 2) It is not allowed to pass some refspecs in args and others in file.

s/refspecs/pathspecs/ ?

> 3) New options do not have shorthands to avoid shorthand conflicts.

> Also add new '--pathspec-file-null' switch that mirrors '-z' used in
> various places. Some porcelain commands, such as `git commit`, already
> use '-z', therefore it needed a new unambiguous name.

As the 'lines' in the file are nul terminated perhaps it would be better 
to call this --pathspec-file-nul or --nul-termination. I think the use 
of --null to mean nul termination for config was a mistake (for grep it 
matches what GUN grep does but it's still unfortunate).

> 
> Co-authored-by: Johannes Schindelin <johannes.schindelin@gmx.de>
> Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
> ---
>   Documentation/git-reset.txt    |  18 ++++-
>   builtin/reset.c                |  22 +++++-
>   t/t7107-reset-pathspec-file.sh | 126 +++++++++++++++++++++++++++++++++
>   3 files changed, 161 insertions(+), 5 deletions(-)
>   create mode 100755 t/t7107-reset-pathspec-file.sh
> 
> diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
> index b0ea6e0ce5..d484cd2827 100644
> --- a/Documentation/git-reset.txt
> +++ b/Documentation/git-reset.txt
> @@ -9,18 +9,20 @@ SYNOPSIS
>   --------
>   [verse]
>   'git reset' [-q] [<tree-ish>] [--] <pathspec>...
> +'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-null]] [<tree-ish>]

--pathspec-file would be shorter and still conveys the intent of the 
option. Is this line missing a leading space?

>   'git reset' (--patch | -p) [<tree-ish>] [--] [<pathspec>...]
>   'git reset' [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>]
>   
>   DESCRIPTION
>   -----------
> -In the first and second form, copy entries from `<tree-ish>` to the index.
> -In the third form, set the current branch head (`HEAD`) to `<commit>`,
> +In the first three forms, copy entries from `<tree-ish>` to the index.
> +In the last form, set the current branch head (`HEAD`) to `<commit>`,
>   optionally modifying index and working tree to match.
>   The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms.
>   
>   'git reset' [-q] [<tree-ish>] [--] <pathspec>...::
> -	This form resets the index entries for all `<pathspec>` to their
> +'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-null]] [<tree-ish>]::

Alignment again

> +	These forms reset the index entries for all `<pathspec>` to their

The new form does not mention <pathspec> so this could be confusing

>   	state at `<tree-ish>`.  (It does not affect the working tree or
>   	the current branch.)
>   +
> @@ -107,6 +109,16 @@ OPTIONS
>   	`reset.quiet` config option. `--quiet` and `--no-quiet` will
>   	override the default behavior.
>   
> +--pathspec-from-file=<file>::
> +	Read `<pathspec>` from `<file>` instead. 

As we have a separate synopsis line for --pathspec-from-file which does 
not mention <pathspec> it might be better just to say "read pathspecs 
from `<file>` instead of the command line".

> If `<file>` is exactly `-`
> +	then read from standard input. Pathspecs are separated by LF or
> +	CR/LF. Pathspecs can be quoted as explained for the configuration
> +	variable `core.quotePath` (see linkgit:git-config[1]). See also
> +	`--pathspec-file-null` and global `--literal-pathspecs`.
> +
> +--pathspec-file-null::
> +	Only meaningful with `--pathspec-from-file`. Pathspecs are
> +	separated with NUL character and are not expected to be quoted.
>   
>   EXAMPLES
>   --------
> diff --git a/builtin/reset.c b/builtin/reset.c
> index fdd572168b..0eaa6b0bca 100644
> --- a/builtin/reset.c
> +++ b/builtin/reset.c
> @@ -31,6 +31,7 @@
>   static const char * const git_reset_usage[] = {
>   	N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"),
>   	N_("git reset [-q] [<tree-ish>] [--] <paths>..."),
> +	N_("git reset [-q] [--pathspec-from-file [--pathspec-file-null]] [<tree-ish>]"),
>   	N_("git reset --patch [<tree-ish>] [--] [<paths>...]"),
>   	NULL
>   };
> @@ -284,8 +285,8 @@ static int git_reset_config(const char *var, const char *value, void *cb)
>   int cmd_reset(int argc, const char **argv, const char *prefix)
>   {
>   	int reset_type = NONE, update_ref_status = 0, quiet = 0;
> -	int patch_mode = 0, unborn;
> -	const char *rev;
> +	int patch_mode = 0, pathspec_file_null = 0, unborn;
> +	const char *rev, *pathspec_from_file = NULL;
>   	struct object_id oid;
>   	struct pathspec pathspec;
>   	int intent_to_add = 0;
> @@ -306,6 +307,10 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
>   		OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")),
>   		OPT_BOOL('N', "intent-to-add", &intent_to_add,
>   				N_("record only the fact that removed paths will be added later")),
> +		OPT_FILENAME(0, "pathspec-from-file", &pathspec_from_file,
> +				N_("read pathspecs from file")),
> +		OPT_BOOL(0, "pathspec-file-null", &pathspec_file_null,
> +				N_("with --pathspec-from-file, pathspecs are separated with NUL character")),
>   		OPT_END()
>   	};
>   
> @@ -316,6 +321,19 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
>   						PARSE_OPT_KEEP_DASHDASH);
>   	parse_args(&pathspec, argv, prefix, patch_mode, &rev);
>   
> +	if (pathspec_from_file) {
> +		if (patch_mode)
> +			die(_("--pathspec-from-file is incompatible with --patch"));

This is sensible as -p is interactive so we wouldn't expect command line 
length to be an issue

> +
> +		if (pathspec.nr)
> +			die(_("--pathspec-from-file is incompatible with path arguments"));
> +
> +		parse_pathspec_file(&pathspec, 0,
> +				    PATHSPEC_PREFER_FULL,
> +				    prefix, pathspec_from_file, pathspec_file_null);
> +	} else if (pathspec_file_null)
> +		die(_("--pathspec-file-null requires --pathspec-from-file"));

Style nit: the coding guidelines state that if any branch of an if 
statement requires braces then all the branches should be braced. This 
is widely ignored though.

> +
>   	unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid);
>   	if (unborn) {
>   		/* reset on unborn branch: treat as reset to empty tree */
> diff --git a/t/t7107-reset-pathspec-file.sh b/t/t7107-reset-pathspec-file.sh
> new file mode 100755
> index 0000000000..cf7f085ad5
> --- /dev/null
> +++ b/t/t7107-reset-pathspec-file.sh
> @@ -0,0 +1,126 @@
> +#!/bin/sh
> +
> +test_description='reset --pathspec-from-file'
> +
> +. ./test-lib.sh
> +
> +cat > expect.a <<EOF
> + D fileA.t
> +EOF
> +
> +cat > expect.ab <<EOF
> + D fileA.t
> + D fileB.t
> +EOF
> +
> +cat > expect.a_bc_d <<EOF
> +D  fileA.t
> + D fileB.t
> + D fileC.t
> +D  fileD.t
> +EOF

These days we tend to set up the expected files within the relevant test 
case using <<-\EOF to allow indentation and disallow substitution 
(unless it's needed of course)

> +test_expect_success setup '
> +	echo A >fileA.t &&
> +	echo B >fileB.t &&
> +	echo C >fileC.t &&
> +	echo D >fileD.t &&
> +	git add . &&
> +	git commit --include . -m "Commit" &&
> +	checkpoint=$(git rev-parse --verify HEAD)
> +'
> +
> +restore_checkpoint () {
> +	git reset --hard "$checkpoint"
> +}
> +
> +verify_state () {
> +	git status --porcelain -- fileA.t fileB.t fileC.t fileD.t >actual &&
> +	test_cmp "$1" actual
> +}
> +
> +test_expect_success '--pathspec-from-file from stdin' '
> +	restore_checkpoint &&
> +
> +	git rm fileA.t &&
> +	echo fileA.t | git reset --pathspec-from-file=- &&
> +
> +	verify_state expect.a
> +'
> +
> +test_expect_success '--pathspec-from-file from file' '
> +	restore_checkpoint &&
> +
> +	git rm fileA.t &&
> +	echo fileA.t >list &&
> +	git reset --pathspec-from-file=list &&
> +
> +	verify_state expect.a
> +'
> +
> +test_expect_success 'NUL delimiters' '
> +	restore_checkpoint &&
> +
> +	git rm fileA.t fileB.t &&
> +	printf fileA.tQfileB.t | q_to_nul | git reset --pathspec-from-file=- --pathspec-file-null &&
> +
> +	verify_state expect.ab
> +'
> +
> +test_expect_success 'LF delimiters' '
> +	restore_checkpoint &&
> +
> +	git rm fileA.t fileB.t &&
> +	printf "fileA.t\nfileB.t" | git reset --pathspec-from-file=- &&
> +
> +	verify_state expect.ab
> +'
> +
> +test_expect_success 'CRLF delimiters' '
> +	restore_checkpoint &&
> +
> +	git rm fileA.t fileB.t &&
> +	printf "fileA.t\r\nfileB.t" | git reset --pathspec-from-file=- &&
> +
> +	verify_state expect.ab
> +'
> +
> +test_expect_success 'quotes' '
> +	restore_checkpoint &&
> +
> +	git rm fileA.t &&
> +	printf "\"file\\101.t\"" | git reset --pathspec-from-file=- &&
> +
> +	verify_state expect.a

If I've understood correctly this doesn't test if a path is correctly 
unquoted, only that it is accepted.

> +'
> +
> +test_expect_success 'quotes not compatible with --pathspec-file-null' '
> +	restore_checkpoint &&
> +
> +	git rm fileA.t &&
> +	printf "\"file\\101.t\"" >list &&
> +	# Note: "git reset" has not yet learned to fail on wrong pathspecs
> +	git reset --pathspec-from-file=list --pathspec-file-null &&
> +	
> +	test_must_fail verify_state expect.a
> +'
> +
> +test_expect_success '--pathspec-from-file is not compatible with --soft --hard' '

s/--soft --hard/--soft or --hard/

> +	restore_checkpoint &&
> +
> +	git rm fileA.t &&
> +	echo fileA.t >list &&
> +	test_must_fail git reset --soft --pathspec-from-file=list &&
> +	test_must_fail git reset --hard --pathspec-from-file=list
> +'
> +
> +test_expect_success 'only touches what was listed' '

s/^/--pathspec-from-file / ?

Best Wishes

Phillip

> +	restore_checkpoint &&
> +
> +	git rm fileA.t fileB.t fileC.t fileD.t &&
> +	printf "fileB.t\nfileC.t" | git reset --pathspec-from-file=- &&
> +
> +	verify_state expect.a_bc_d
> +'
> +
> +test_done
> 

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 3/5] reset: support the --pathspec-from-file option
  2019-11-04 19:26 ` [PATCH 3/5] reset: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
  2019-11-05 15:03   ` Phillip Wood
@ 2019-11-05 16:14   ` Phillip Wood
  2019-11-05 19:37     ` Alexandr Miloslavskiy
  2019-11-06  4:40   ` Junio C Hamano
  2 siblings, 1 reply; 80+ messages in thread
From: Phillip Wood @ 2019-11-05 16:14 UTC (permalink / raw)
  To: Alexandr Miloslavskiy via GitGitGadget, git
  Cc: Alexandr Miloslavskiy, Junio C Hamano

Hi Alexandr

On 04/11/2019 19:26, Alexandr Miloslavskiy via GitGitGadget wrote:
> From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
> [...]
> diff --git a/builtin/reset.c b/builtin/reset.c
> index fdd572168b..0eaa6b0bca 100644
> --- a/builtin/reset.c
> +++ b/builtin/reset.c
> @@ -31,6 +31,7 @@
>   static const char * const git_reset_usage[] = {
>   	N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"),
>   	N_("git reset [-q] [<tree-ish>] [--] <paths>..."),
> +	N_("git reset [-q] [--pathspec-from-file [--pathspec-file-null]] [<tree-ish>]"),
>   	N_("git reset --patch [<tree-ish>] [--] [<paths>...]"),
>   	NULL
>   };
> @@ -284,8 +285,8 @@ static int git_reset_config(const char *var, const char *value, void *cb)
>   int cmd_reset(int argc, const char **argv, const char *prefix)
>   {
>   	int reset_type = NONE, update_ref_status = 0, quiet = 0;
> -	int patch_mode = 0, unborn;
> -	const char *rev;
> +	int patch_mode = 0, pathspec_file_null = 0, unborn;
> +	const char *rev, *pathspec_from_file = NULL;
>   	struct object_id oid;
>   	struct pathspec pathspec;
>   	int intent_to_add = 0;
> @@ -306,6 +307,10 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
>   		OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")),
>   		OPT_BOOL('N', "intent-to-add", &intent_to_add,
>   				N_("record only the fact that removed paths will be added later")),
> +		OPT_FILENAME(0, "pathspec-from-file", &pathspec_from_file,
> +				N_("read pathspecs from file")),
> +		OPT_BOOL(0, "pathspec-file-null", &pathspec_file_null,
> +				N_("with --pathspec-from-file, pathspecs are separated with NUL character")),

One thing I forgot to mention before is that if you're going to add 
these options to a number of commands then maybe it is worth defining a 
macro for each one in parse-options.h. There are a couple of examples of 
this at the end of that file.

Best Wishes

Phillip

>   		OPT_END()
>   	};
>   
> @@ -316,6 +321,19 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
>   						PARSE_OPT_KEEP_DASHDASH);
>   	parse_args(&pathspec, argv, prefix, patch_mode, &rev);
>   
> +	if (pathspec_from_file) {
> +		if (patch_mode)
> +			die(_("--pathspec-from-file is incompatible with --patch"));
> +
> +		if (pathspec.nr)
> +			die(_("--pathspec-from-file is incompatible with path arguments"));
> +
> +		parse_pathspec_file(&pathspec, 0,
> +				    PATHSPEC_PREFER_FULL,
> +				    prefix, pathspec_from_file, pathspec_file_null);
> +	} else if (pathspec_file_null)
> +		die(_("--pathspec-file-null requires --pathspec-from-file"));
> +
>   	unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid);
>   	if (unborn) {
>   		/* reset on unborn branch: treat as reset to empty tree */
> diff --git a/t/t7107-reset-pathspec-file.sh b/t/t7107-reset-pathspec-file.sh
> new file mode 100755
> index 0000000000..cf7f085ad5
> --- /dev/null
> +++ b/t/t7107-reset-pathspec-file.sh
> @@ -0,0 +1,126 @@
> +#!/bin/sh
> +
> +test_description='reset --pathspec-from-file'
> +
> +. ./test-lib.sh
> +
> +cat > expect.a <<EOF
> + D fileA.t
> +EOF
> +
> +cat > expect.ab <<EOF
> + D fileA.t
> + D fileB.t
> +EOF
> +
> +cat > expect.a_bc_d <<EOF
> +D  fileA.t
> + D fileB.t
> + D fileC.t
> +D  fileD.t
> +EOF
> +
> +test_expect_success setup '
> +	echo A >fileA.t &&
> +	echo B >fileB.t &&
> +	echo C >fileC.t &&
> +	echo D >fileD.t &&
> +	git add . &&
> +	git commit --include . -m "Commit" &&
> +	checkpoint=$(git rev-parse --verify HEAD)
> +'
> +
> +restore_checkpoint () {
> +	git reset --hard "$checkpoint"
> +}
> +
> +verify_state () {
> +	git status --porcelain -- fileA.t fileB.t fileC.t fileD.t >actual &&
> +	test_cmp "$1" actual
> +}
> +
> +test_expect_success '--pathspec-from-file from stdin' '
> +	restore_checkpoint &&
> +
> +	git rm fileA.t &&
> +	echo fileA.t | git reset --pathspec-from-file=- &&
> +
> +	verify_state expect.a
> +'
> +
> +test_expect_success '--pathspec-from-file from file' '
> +	restore_checkpoint &&
> +
> +	git rm fileA.t &&
> +	echo fileA.t >list &&
> +	git reset --pathspec-from-file=list &&
> +
> +	verify_state expect.a
> +'
> +
> +test_expect_success 'NUL delimiters' '
> +	restore_checkpoint &&
> +
> +	git rm fileA.t fileB.t &&
> +	printf fileA.tQfileB.t | q_to_nul | git reset --pathspec-from-file=- --pathspec-file-null &&
> +
> +	verify_state expect.ab
> +'
> +
> +test_expect_success 'LF delimiters' '
> +	restore_checkpoint &&
> +
> +	git rm fileA.t fileB.t &&
> +	printf "fileA.t\nfileB.t" | git reset --pathspec-from-file=- &&
> +
> +	verify_state expect.ab
> +'
> +
> +test_expect_success 'CRLF delimiters' '
> +	restore_checkpoint &&
> +
> +	git rm fileA.t fileB.t &&
> +	printf "fileA.t\r\nfileB.t" | git reset --pathspec-from-file=- &&
> +
> +	verify_state expect.ab
> +'
> +
> +test_expect_success 'quotes' '
> +	restore_checkpoint &&
> +
> +	git rm fileA.t &&
> +	printf "\"file\\101.t\"" | git reset --pathspec-from-file=- &&
> +
> +	verify_state expect.a
> +'
> +
> +test_expect_success 'quotes not compatible with --pathspec-file-null' '
> +	restore_checkpoint &&
> +
> +	git rm fileA.t &&
> +	printf "\"file\\101.t\"" >list &&
> +	# Note: "git reset" has not yet learned to fail on wrong pathspecs
> +	git reset --pathspec-from-file=list --pathspec-file-null &&
> +	
> +	test_must_fail verify_state expect.a
> +'
> +
> +test_expect_success '--pathspec-from-file is not compatible with --soft --hard' '
> +	restore_checkpoint &&
> +
> +	git rm fileA.t &&
> +	echo fileA.t >list &&
> +	test_must_fail git reset --soft --pathspec-from-file=list &&
> +	test_must_fail git reset --hard --pathspec-from-file=list
> +'
> +
> +test_expect_success 'only touches what was listed' '
> +	restore_checkpoint &&
> +
> +	git rm fileA.t fileB.t fileC.t fileD.t &&
> +	printf "fileB.t\nfileC.t" | git reset --pathspec-from-file=- &&
> +
> +	verify_state expect.a_bc_d
> +'
> +
> +test_done
> 

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 5/5] commit: support the --pathspec-from-file option
  2019-11-04 19:26 ` [PATCH 5/5] commit: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
@ 2019-11-05 16:27   ` Phillip Wood
  2019-11-05 19:42     ` Alexandr Miloslavskiy
  2019-11-06 15:56     ` Alexandr Miloslavskiy
  2019-11-06  4:51   ` Junio C Hamano
  1 sibling, 2 replies; 80+ messages in thread
From: Phillip Wood @ 2019-11-05 16:27 UTC (permalink / raw)
  To: Alexandr Miloslavskiy via GitGitGadget, git
  Cc: Alexandr Miloslavskiy, Junio C Hamano

Hi Alexandr

On 04/11/2019 19:26, Alexandr Miloslavskiy via GitGitGadget wrote:
> From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
> 
> This option solves the problem of commandline length limit for UI's
> built on top of git. Plumbing commands are not always a good fit, for
> two major reasons:
> 1) Some UI's serve as assistants that help user run git commands. In
>     this case, replacing familiar commands with plumbing commands will
>     confuse most users.
> 2) Some UI's have started and grown with porcelain commands. Replacing
>     existing logic with plumbing commands could be cumbersome and prone
>     to various new problems.
> 
> The new option is designed to behave very close to pathspecs passed in
> commandline args, so that switching from one to another is simple.
> 
> The new option allows to read either a specified file or `stdin`.
> Reading from file is a good way to avoid competing for stdin, and
> also gives some extra flexibility.
> 
> Decisions taken for simplicity:
> 1) The new option is declared incompatible with other options that
>     could use `stdin`.
> 2) It is not allowed to pass some refspecs in args and others in file.
> 3) New options do not have shorthands to avoid shorthand conflicts.
> 
> Also add new '--pathspec-file-null' switch that mirrors '-z' used in
> various places. Some porcelain commands, such as `git commit`, already
> use '-z', therefore it needed a new unambiguous name.

It might be worth tailoring the message to the command rather than 
having exactly the same message for commit and reset

> 
> Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
> ---
>   Documentation/git-commit.txt    |  14 ++++-
>   builtin/commit.c                |  25 ++++++--
>   t/t7526-commit-pathspec-file.sh | 107 ++++++++++++++++++++++++++++++++
>   3 files changed, 141 insertions(+), 5 deletions(-)
>   create mode 100755 t/t7526-commit-pathspec-file.sh
> 
> diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
> index 4341d0e3ab..ec4752298d 100644
> --- a/Documentation/git-commit.txt
> +++ b/Documentation/git-commit.txt
> @@ -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-null]]
> +	   [-S[<keyid>]] [--] [<pathspec>...]
>   
>   DESCRIPTION
>   -----------
> @@ -277,6 +278,17 @@ FROM UPSTREAM REBASE" section in linkgit:git-rebase[1].)
>   	the last commit without committing changes that have
>   	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>::
> +	Read `<pathspec>` from `<file>` instead. If `<file>` is exactly `-`
> +	then read from standard input. Pathspecs are separated by LF or
> +	CR/LF. Pathspecs can be quoted as explained for the configuration
> +	variable `core.quotePath` (see linkgit:git-config[1]). See also
> +	`--pathspec-file-null` and global `--literal-pathspecs`.
> +
> +--pathspec-file-null::
> +	Only meaningful with `--pathspec-from-file`. Pathspecs are
> +	separated with NUL character and are not expected to be quoted.

I think my comments from patch 3 about <pathspecs> and the option names 
apply here as well

>   -u[<mode>]::
>   --untracked-files[=<mode>]::
> diff --git a/builtin/commit.c b/builtin/commit.c
> index e588bc6ad3..532f305926 100644
> --- a/builtin/commit.c
> +++ b/builtin/commit.c
> @@ -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_null;
>   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 path arguments"));
> +
> +		parse_pathspec_file(&pathspec, 0,
> +				    PATHSPEC_PREFER_FULL,
> +				    prefix, pathspec_from_file, pathspec_file_null);
> +	}
> +	else if (pathspec_file_null)
> +		die(_("--pathspec-file-null requires --pathspec-from-file"));
> +
> +	if (!pathspec.nr && (also || (only && !amend && !allow_empty)))
> +		die(_("No paths with --include/--only does not make sense."));

I wonder if there is a way of calling parse_pathspec_file() from 
parse_and_validate_options() instead. Otherwise we end up validating 
options here instead which is a bit messy.

>   	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_FILENAME(0, "pathspec-from-file", &pathspec_from_file, N_("read pathspecs from file")),
> +		OPT_BOOL(0, "pathspec-file-null", &pathspec_file_null, N_("with --pathspec-from-file, pathspecs are separated with NUL character")),
>   		/* 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
> index 0000000000..c5d68e01e6
> --- /dev/null
> +++ b/t/t7526-commit-pathspec-file.sh

The test comments from patch 3 apply here as well I think.

Overall this series is nicely structured and is looking pretty good

Best Wishes

Phillip

> @@ -0,0 +1,107 @@
> +#!/bin/sh
> +
> +test_description='commit --pathspec-from-file'
> +
> +. ./test-lib.sh
> +
> +test_tick
> +
> +cat > expect.a <<EOF
> +A	fileA.t
> +EOF
> +
> +cat > expect.ab <<EOF
> +A	fileA.t
> +A	fileB.t
> +EOF
> +
> +cat > expect.bc <<EOF
> +A	fileB.t
> +A	fileC.t
> +EOF
> +
> +test_expect_success setup '
> +	test_commit file0 &&
> +	checkpoint=$(git rev-parse --verify HEAD) &&
> +	
> +	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_state () {
> +	git diff-tree --no-commit-id --name-status -r HEAD >actual &&
> +	test_cmp "$1" actual
> +}
> +
> +test_expect_success '--pathspec-from-file from stdin' '
> +	restore_checkpoint &&
> +
> +	echo fileA.t | git commit --pathspec-from-file=- -m "Commit" &&
> +
> +	verify_state expect.a
> +'
> +
> +test_expect_success '--pathspec-from-file from file' '
> +	restore_checkpoint &&
> +
> +	echo fileA.t >list &&
> +	git commit --pathspec-from-file=list -m "Commit" &&
> +
> +	verify_state expect.a
> +'
> +
> +test_expect_success 'NUL delimiters' '
> +	restore_checkpoint &&
> +
> +	printf fileA.tQfileB.t | q_to_nul | git commit --pathspec-from-file=- --pathspec-file-null -m "Commit" &&
> +
> +	verify_state expect.ab
> +'
> +
> +test_expect_success 'LF delimiters' '
> +	restore_checkpoint &&
> +
> +	printf "fileA.t\nfileB.t" | git commit --pathspec-from-file=- -m "Commit" &&
> +
> +	verify_state expect.ab
> +'
> +
> +test_expect_success 'CRLF delimiters' '
> +	restore_checkpoint &&
> +
> +	printf "fileA.t\r\nfileB.t" | git commit --pathspec-from-file=- -m "Commit" &&
> +
> +	verify_state expect.ab
> +'
> +
> +test_expect_success 'quotes' '
> +	restore_checkpoint &&
> +
> +	printf "\"file\\101.t\"" | git commit --pathspec-from-file=- -m "Commit" &&
> +
> +	verify_state expect.a
> +'
> +
> +test_expect_success 'quotes not compatible with --pathspec-file-null' '
> +	restore_checkpoint &&
> +
> +	printf "\"file\\101.t\"" >list &&
> +	test_must_fail git commit --pathspec-from-file=list --pathspec-file-null -m "Commit"
> +'
> +
> +test_expect_success 'only touches what was listed' '
> +	restore_checkpoint &&
> +
> +	printf "fileB.t\nfileC.t" | git commit --pathspec-from-file=- -m "Commit" &&
> +
> +	verify_state expect.bc
> +'
> +
> +test_done
> 

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 1/5] pathspec: add new function to parse file
  2019-11-05 15:02   ` Phillip Wood
@ 2019-11-05 19:14     ` Alexandr Miloslavskiy
  2019-11-06 15:56     ` Alexandr Miloslavskiy
  1 sibling, 0 replies; 80+ messages in thread
From: Alexandr Miloslavskiy @ 2019-11-05 19:14 UTC (permalink / raw)
  To: phillip.wood, Alexandr Miloslavskiy via GitGitGadget, git; +Cc: Junio C Hamano

On 05.11.2019 16:02, Phillip Wood wrote:

>> +    if (!in)
>> +        die(_("could not open '%s' for reading"), file);
> 
> die_errno() would give a more informative message here

Thanks for the pointer!

>> +            if (unquote_c_style(&unquoted, buf.buf, NULL))
>> +                die(_("line is badly quoted"));
> 
> It would be nice to show the offending line in the error message

Good idea.

>> +void parse_pathspec_file(struct pathspec *pathspec,
>> +            unsigned magic_mask,
>> +            unsigned flags,
>> +            const char *prefix,
>> +            const char *file,
>> +            int nul_term_line);
> 
> Do these align with the 's' in "struct pathspec" ?

Sorry, still struggling with my VS that tries to do crazy things. Will fix.

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 3/5] reset: support the --pathspec-from-file option
  2019-11-05 15:03   ` Phillip Wood
@ 2019-11-05 19:22     ` Phillip Wood
  2019-11-05 19:36     ` Alexandr Miloslavskiy
  2019-11-06 15:56     ` Alexandr Miloslavskiy
  2 siblings, 0 replies; 80+ messages in thread
From: Phillip Wood @ 2019-11-05 19:22 UTC (permalink / raw)
  To: Alexandr Miloslavskiy via GitGitGadget, git
  Cc: Alexandr Miloslavskiy, Junio C Hamano

Hi Alexandr

On 05/11/2019 15:03, Phillip Wood wrote:
> [...]
>> +test_expect_success 'quotes' '
>> +    restore_checkpoint &&
>> +
>> +    git rm fileA.t &&
>> +    printf "\"file\\101.t\"" | git reset --pathspec-from-file=- &&
>> +
>> +    verify_state expect.a
> 
> If I've understood correctly this doesn't test if a path is correctly 
> unquoted, only that it is accepted.

Oh I think I've misunderstood. The '\\' in unquoted by printf, so git 
sees '\101' which is A so that is a real file.

Sorry for the confusion

Phillip

> 
>> +'
>> +
>> +test_expect_success 'quotes not compatible with --pathspec-file-null' '
>> +    restore_checkpoint &&
>> +
>> +    git rm fileA.t &&
>> +    printf "\"file\\101.t\"" >list &&
>> +    # Note: "git reset" has not yet learned to fail on wrong pathspecs
>> +    git reset --pathspec-from-file=list --pathspec-file-null &&
>> +
>> +    test_must_fail verify_state expect.a
>> +'
>> +
>> +test_expect_success '--pathspec-from-file is not compatible with 
>> --soft --hard' '
> 
> s/--soft --hard/--soft or --hard/
> 
>> +    restore_checkpoint &&
>> +
>> +    git rm fileA.t &&
>> +    echo fileA.t >list &&
>> +    test_must_fail git reset --soft --pathspec-from-file=list &&
>> +    test_must_fail git reset --hard --pathspec-from-file=list
>> +'
>> +
>> +test_expect_success 'only touches what was listed' '
> 
> s/^/--pathspec-from-file / ?
> 
> Best Wishes
> 
> Phillip
> 
>> +    restore_checkpoint &&
>> +
>> +    git rm fileA.t fileB.t fileC.t fileD.t &&
>> +    printf "fileB.t\nfileC.t" | git reset --pathspec-from-file=- &&
>> +
>> +    verify_state expect.a_bc_d
>> +'
>> +
>> +test_done
>>

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 3/5] reset: support the --pathspec-from-file option
  2019-11-05 15:03   ` Phillip Wood
  2019-11-05 19:22     ` Phillip Wood
@ 2019-11-05 19:36     ` Alexandr Miloslavskiy
  2019-11-06 15:56     ` Alexandr Miloslavskiy
  2 siblings, 0 replies; 80+ messages in thread
From: Alexandr Miloslavskiy @ 2019-11-05 19:36 UTC (permalink / raw)
  To: phillip.wood, Alexandr Miloslavskiy via GitGitGadget, git; +Cc: Junio C Hamano

On 05.11.2019 16:03, Phillip Wood wrote:

>> The new option allows to read either a specified file or `stdin`.
>> Reading from file is a good way to avoid competing for stdin, and
>> also gives some extra flexibility.
> 
> I think this flexibility is a good idea

Thanks for your support :) Previously the opinions were mixed and I was 
a bit afraid that this will invoke a new round of discussions.

>> Decisions taken for simplicity:
>> 1) The new option is declared incompatible with other options that
>>     could use `stdin`.
> 
> I'm confused reset does not use stdin does it?

I understand that '--patch' interacts with user via stdin. Will 
double-check tomorrow.

>> 2) It is not allowed to pass some refspecs in args and others in file.
> 
> s/refspecs/pathspecs/ ?

Thanks! Not quite used to git speak and mix up things sometimes.

>> Also add new '--pathspec-file-null' switch that mirrors '-z' used in
>> various places. Some porcelain commands, such as `git commit`, already
>> use '-z', therefore it needed a new unambiguous name.
> 
> As the 'lines' in the file are nul terminated perhaps it would be better 
> to call this --pathspec-file-nul or --nul-termination. I think the use 
> of --null to mean nul termination for config was a mistake (for grep it 
> matches what GUN grep does but it's still unfortunate).

OK, will change to '--pathspec-file-nul'

>> +'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-null]] 
>> [<tree-ish>]
> 
> --pathspec-file would be shorter and still conveys the intent of the 
> option. Is this line missing a leading space?

'--pathspec-from-file' was kind of suggested instead of '--paths-file' 
by Junio here:
https://public-inbox.org/git/xmqqtv9qr82q.fsf@gitster-ct.c.googlers.com/

so Junio added '-from' to previous writing. Hmm. What do you think, 
taking Junio's message into account?

As for whitespaces, sorry, will fix.

>> +'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-null]] 
>> [<tree-ish>]::
> 
> Alignment again

Will fix

>> +    These forms reset the index entries for all `<pathspec>` to their
> 
> The new form does not mention <pathspec> so this could be confusing

Shall I replace '<pathspec>' with 'pathspecs' ?

> As we have a separate synopsis line for --pathspec-from-file which does 
> not mention <pathspec> it might be better just to say "read pathspecs 
> from `<file>` instead of the command line".

OK

>> +    if (pathspec_from_file) {
>> +        if (patch_mode)
>> +            die(_("--pathspec-from-file is incompatible with --patch"));
> 
> This is sensible as -p is interactive so we wouldn't expect command line 
> length to be an issue

Yes, I also thought so. I doubt that user is willing to interactively 
decide on hundreds of files.

>> +    } else if (pathspec_file_null)
>> +        die(_("--pathspec-file-null requires --pathspec-from-file"));
> 
> Style nit: the coding guidelines state that if any branch of an if 
> statement requires braces then all the branches should be braced. This 
> is widely ignored though.

Took this code from a guy who ignored it :) But sure, will change.

> These days we tend to set up the expected files within the relevant test 
> case using <<-\EOF to allow indentation and disallow substitution 
> (unless it's needed of course)

I'll try to change this.

>> +test_expect_success 'quotes' '
>> +    restore_checkpoint &&
>> +
>> +    git rm fileA.t &&
>> +    printf "\"file\\101.t\"" | git reset --pathspec-from-file=- &&
>> +
>> +    verify_state expect.a
> 
> If I've understood correctly this doesn't test if a path is correctly 
> unquoted, only that it is accepted.

In my understanding, 'verify_state expect.a' should test that it's 
correctly understood. Am I wrong?

>> +test_expect_success '--pathspec-from-file is not compatible with 
>> --soft --hard' '
> 
> s/--soft --hard/--soft or --hard/

Good idea.

>> +test_expect_success 'only touches what was listed' '
> 
> s/^/--pathspec-from-file / ?

I thought that whole test package is about '--pathspec-from-file' so I'd 
rather not repeat that in every test name. Shall I change that?

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 3/5] reset: support the --pathspec-from-file option
  2019-11-05 16:14   ` Phillip Wood
@ 2019-11-05 19:37     ` Alexandr Miloslavskiy
  0 siblings, 0 replies; 80+ messages in thread
From: Alexandr Miloslavskiy @ 2019-11-05 19:37 UTC (permalink / raw)
  To: phillip.wood, Alexandr Miloslavskiy via GitGitGadget, git; +Cc: Junio C Hamano

On 05.11.2019 17:14, Phillip Wood wrote:

>> +        OPT_FILENAME(0, "pathspec-from-file", &pathspec_from_file,
>> +                N_("read pathspecs from file")),
>> +        OPT_BOOL(0, "pathspec-file-null", &pathspec_file_null,
>> +                N_("with --pathspec-from-file, pathspecs are 
>> separated with NUL character")),
> 
> One thing I forgot to mention before is that if you're going to add 
> these options to a number of commands then maybe it is worth defining a 
> macro for each one in parse-options.h. There are a couple of examples of 
> this at the end of that file.

Thanks for the pointer!

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 5/5] commit: support the --pathspec-from-file option
  2019-11-05 16:27   ` Phillip Wood
@ 2019-11-05 19:42     ` Alexandr Miloslavskiy
  2019-11-06 15:56     ` Alexandr Miloslavskiy
  1 sibling, 0 replies; 80+ messages in thread
From: Alexandr Miloslavskiy @ 2019-11-05 19:42 UTC (permalink / raw)
  To: phillip.wood, Alexandr Miloslavskiy via GitGitGadget, git; +Cc: Junio C Hamano

On 05.11.2019 17:27, Phillip Wood wrote:

>> Also add new '--pathspec-file-null' switch that mirrors '-z' used in
>> various places. Some porcelain commands, such as `git commit`, already
>> use '-z', therefore it needed a new unambiguous name.
> 
> It might be worth tailoring the message to the command rather than 
> having exactly the same message for commit and reset

I also was somewhat unhappy about duplication. But I didn't figure how 
to do that correctly. Currently the messages for 'git reset' and 'git 
commit' are almost identical.

Maybe in 2nd commit I should say something like "Extend 
`--pathspec-file-null` support to `git commit` (see previous patch for 
`git reset`)" ?

> I think my comments from patch 3 about <pathspecs> and the option names 
> apply here as well

Yes, sure, I will try to apply your suggestions to all patches. 
Hopefully without forgetting things :)

>> +    if (!pathspec.nr && (also || (only && !amend && !allow_empty)))
>> +        die(_("No paths with --include/--only does not make sense."));
> 
> I wonder if there is a way of calling parse_pathspec_file() from 
> parse_and_validate_options() instead. Otherwise we end up validating 
> options here instead which is a bit messy.

Yes, I was also somewhat unhappy about that. I will give it more thought.

> Overall this series is nicely structured and is looking pretty good

Thanks, and also thanks for reviewing my patches!


^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 2/5] doc: reset: unify <pathspec> description
  2019-11-04 19:26 ` [PATCH 2/5] doc: reset: unify <pathspec> description Alexandr Miloslavskiy via GitGitGadget
@ 2019-11-06  4:01   ` Junio C Hamano
  2019-11-06 15:56     ` Alexandr Miloslavskiy
  0 siblings, 1 reply; 80+ messages in thread
From: Junio C Hamano @ 2019-11-06  4:01 UTC (permalink / raw)
  To: Alexandr Miloslavskiy via GitGitGadget; +Cc: git, Alexandr Miloslavskiy

"Alexandr Miloslavskiy via GitGitGadget" <gitgitgadget@gmail.com>
writes:

>  optionally modifying index and working tree to match.
>  The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms.
>  
> -'git reset' [-q] [<tree-ish>] [--] <paths>...::
> -	This form resets the index entries for all `<paths>` to their
> +'git reset' [-q] [<tree-ish>] [--] <pathspec>...::
> +	This form resets the index entries for all `<pathspec>` to their
>  	state at `<tree-ish>`.  (It does not affect the working tree or
>  	the current branch.)
>  +
> -This means that `git reset <paths>` is the opposite of `git add
> -<paths>`. This command is equivalent to
> -`git restore [--source=<tree-ish>] --staged <paths>...`.
> +For more details about the <pathspec> syntax, see the 'pathspec' entry
> +in linkgit:gitglossary[7].
>  +
> -After running `git reset <paths>` to update the index entry, you can
> +This means that `git reset <pathspec>` is the opposite of `git add

It is good to show which document to read more on <pathspec>, but
inserting it just before "This means..." breaks the flow of thought.

As we introduce/explain tree-ish and commit used in the synopsis in
the ealier part of the description, it probably is a much better
place to also introduce/explain pathspec, perhaps like so:

	 In the first and second form, ...
	 In the third form,  ...
	 The <tree-ish>/<commit> defaults to HEAD in all forms.
	+The <pathspec> is used to limit the paths affected by the
	+operation in the first two forms (see the entry for
	+'pathspec' in linkgit:gitglossary[7] for more details).

> +This means that `git reset <pathspec>` is the opposite of `git add
> +<pathspec>`. This command is equivalent to
> +`git restore [--source=<tree-ish>] --staged <pathspec>...`.

Any time I see "... X. This means Y." either in the doc or in the
proposed log message, I wish the author (not you in this case,
obviously) thought twice about rewriting so that they do not say one
thing and immediately have to rephrase it, i.e. either just say Y
without saying X, or saying X more clearly without having to say Y.

In this case, however, I think X and Y are related but both relevant.
The subcommand resets the index entries for chosen paths to match
what is in the tree-ish, which is the same as restoring from a tree
to the index.

It is not quite the opposite of adding to the index from the working
tree.  In this sequence:

	$ edit newfile
	$ git add newfile

and then further

	$ edit newfile
	$ git add newfile
	$ git reset -- newfile

we are taken back to the state _before_ any of the changes made to
newfile (in fact, since HEAD does not have newfile, the resulting
index would not know about it, either).

Thanks.

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 3/5] reset: support the --pathspec-from-file option
  2019-11-04 19:26 ` [PATCH 3/5] reset: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
  2019-11-05 15:03   ` Phillip Wood
  2019-11-05 16:14   ` Phillip Wood
@ 2019-11-06  4:40   ` Junio C Hamano
  2019-11-06 15:56     ` Alexandr Miloslavskiy
  2 siblings, 1 reply; 80+ messages in thread
From: Junio C Hamano @ 2019-11-06  4:40 UTC (permalink / raw)
  To: Alexandr Miloslavskiy via GitGitGadget; +Cc: git, Alexandr Miloslavskiy

"Alexandr Miloslavskiy via GitGitGadget" <gitgitgadget@gmail.com>
writes:

> From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
>
> This option solves the problem of commandline length limit for UI's
> built on top of git. Plumbing commands are not always a good fit, for
> two major reasons:
> 1) Some UI's serve as assistants that help user run git commands. In
>    this case, replacing familiar commands with plumbing commands will
>    confuse most users.

"UI's that serve as assistants that help user run git commands" does
not have to avoid plumbing commands at all.  Only the ones that "show"
the commands that are run on behalf of the users (perhaps so that the
users can learn from such examples?) do, and I think I learned that
it was your motivating use case from an earlier discussion.  Perhaps

	UIs that help users to formulate git commands to run need to
	present Porcelain commands to be used, as it is not reasonable
	to demonstrate arcane combination of plumbing commands as an
	example for their interactive use.

would probably be more readable without bending what you wanted to
say too much?

> 2) Some UI's have started and grown with porcelain commands. Replacing
>    existing logic with plumbing commands could be cumbersome and prone
>    to various new problems.

There is not a lot of sympathy for such argument ;-)

> 2) It is not allowed to pass some refspecs in args and others in file.

Did you mean refspec, not pathspec?

> 3) New options do not have shorthands to avoid shorthand conflicts.
>
> Also add new '--pathspec-file-null' switch that mirrors '-z' used in
> various places. Some porcelain commands, such as `git commit`, already
> use '-z', therefore it needed a new unambiguous name.

I do not understand this part.  Wouldn't it natural to expect that
"-z", when used with "--pathspec-from-file", tell us that the named
file is NUL delimited collection of records?  What's different about
"--pathspec-file-null" that it is confusing to all it "-z"?

I actually do not mind not having "-z" and using only
"--pathspec-file-null".  A new option with long name that feels
similar to existing "-z" but does sufficiently different things so
that it needs a different name, however, feels counter-productive
for the purpose of using it in the UI that produces commands to be
learned by end-users.

> +--pathspec-from-file=<file>::
> +	Read `<pathspec>` from `<file>` instead. If `<file>` is exactly `-`
> +	then read from standard input. Pathspecs are separated by LF or
> +	CR/LF. Pathspecs can be quoted as explained for the configuration

When I invented the terms "pathspec", "refspec", etc. for this
project, I meant them to be collective nouns.  A pathspec is a set
of pathspec elements, each of which is usually given as a separate
argument on the command line.  So "Read pathspec from file" is good
(not "Read pathspecs from file").

And "Pathspec elements" are separated by LF or CR/LF.

A tangent.  Since we do not allow NUL in a pathspec element, we do
not even need the "-z" option.  When we read pathspec from a file,
we can take any of CRLF, LF or NUL as the separator.

Ah, sorry, that would not help very much.  With "-z" we are allowing
to express pathspec elements inside which there are embedded LF or
CR/LF.  Sorry about the noise.

> +--pathspec-file-null::
> +	Only meaningful with `--pathspec-from-file`. Pathspecs are
> +	separated with NUL character and are not expected to be quoted.

OK.

> +	if (pathspec_from_file) {
> +		if (patch_mode)
> +			die(_("--pathspec-from-file is incompatible with --patch"));
> +
> +		if (pathspec.nr)
> +			die(_("--pathspec-from-file is incompatible with path arguments"));

Shouldn't the error message say pathspec arguments instead?

> diff --git a/t/t7107-reset-pathspec-file.sh b/t/t7107-reset-pathspec-file.sh
> new file mode 100755
> index 0000000000..cf7f085ad5
> --- /dev/null
> +++ b/t/t7107-reset-pathspec-file.sh
> @@ -0,0 +1,126 @@
> +#!/bin/sh
> +
> +test_description='reset --pathspec-from-file'
> +
> +. ./test-lib.sh
> +
> +cat > expect.a <<EOF

Style:

 - Modern test scripts strive to perform these set-up procedure
   inside (the first) test_expect_success.

 - No SP between the redirection operator and its source/destination
   filename.

 - Quote end-of-here-document (EOF in the above) if you do not rely
   on parameter interpolation inside the here document; this helps
   readers by telling them that what is presented is used verbatim.

> + D fileA.t
> +EOF
> +
> +cat > expect.ab <<EOF
> + D fileA.t
> + D fileB.t
> +EOF
> +
> +cat > expect.a_bc_d <<EOF
> +D  fileA.t
> + D fileB.t
> + D fileC.t
> +D  fileD.t
> +EOF
> +
> +test_expect_success setup '
> +	echo A >fileA.t &&
> +	echo B >fileB.t &&
> +	echo C >fileC.t &&
> +	echo D >fileD.t &&
> +	git add . &&
> +	git commit --include . -m "Commit" &&
> +	checkpoint=$(git rev-parse --verify HEAD)
> +'
> +
> +restore_checkpoint () {
> +	git reset --hard "$checkpoint"
> +}

Hmm, wouldn't it be cleaner to use a lightweight tag or something to
keep checkpoint, instead of a variable that is hard to examine when
tests break and needs debugging?

> +verify_state () {
> +	git status --porcelain -- fileA.t fileB.t fileC.t fileD.t >actual &&
> +	test_cmp "$1" actual
> +}
> +
> +test_expect_success '--pathspec-from-file from stdin' '
> +	restore_checkpoint &&
> +
> +	git rm fileA.t &&
> +	echo fileA.t | git reset --pathspec-from-file=- &&
> +	verify_state expect.a
> +'
> +
> +test_expect_success '--pathspec-from-file from file' '
> +	restore_checkpoint &&
> +
> +	git rm fileA.t &&
> +	echo fileA.t >list &&
> +	git reset --pathspec-from-file=list &&
> +
> +	verify_state expect.a
> +'
> +
> +test_expect_success 'NUL delimiters' '
> +	restore_checkpoint &&
> +
> +	git rm fileA.t fileB.t &&
> +	printf fileA.tQfileB.t | q_to_nul | git reset --pathspec-from-file=- --pathspec-file-null &&

This feeds "fileA.t<NUL>fileB.t" without <NUL> after "fileB.t" to
the command.  Intended?

Rather, perhaps

	printf "%s\0" fileA.t fileB.t

without q-to-nul, once you use printf anyway?

If you truly mean "delimiter" (as opposed to "terminator"),

	printf "fileA.t\0fileB.t"

can also lose " | q_to_nul".

Thanks.

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 4/5] doc: commit: unify <pathspec> description
  2019-11-04 19:26 ` [PATCH 4/5] doc: commit: unify <pathspec> description Alexandr Miloslavskiy via GitGitGadget
@ 2019-11-06  4:50   ` Junio C Hamano
  2019-11-06 15:56     ` Alexandr Miloslavskiy
  0 siblings, 1 reply; 80+ messages in thread
From: Junio C Hamano @ 2019-11-06  4:50 UTC (permalink / raw)
  To: Alexandr Miloslavskiy via GitGitGadget; +Cc: git, Alexandr Miloslavskiy

"Alexandr Miloslavskiy via GitGitGadget" <gitgitgadget@gmail.com>
writes:

> From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
>
> Synchronize it to `git add`, which has a pretty good description.
> This also better disambiguates <file>... header.

"When files are given on..." is no longer true with this change (it
wasn't true with the code before this change anyway).

	When pathspec is given on the command line, commit the
	contents of the files that match the pathspec without
	recording the changes already added to the index. ...

The second sentence also says "these files", but that can be left
as-is, since it would refer to "the files that match ..." explained
in the above sentence.

> +For more details about the <pathspec> syntax, see the 'pathspec' entry
> +in linkgit:gitglossary[7].

I am not sure if we want to repeat this all over the place.

We do not say "For details about the <commit> syntax, see the
'SPECIFYING REVISIONS' section of linkgit:git-rev-parse[1]" for
every command that takes <commit> from the command line.

Is your urge to suggest adding this sentence coming from that you
are much more familiar with <commit> than <pathspec>?  In other
words, if you were more familiar with Git, would you still be adding
this (and not corresponding one for <commit>)?


^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 5/5] commit: support the --pathspec-from-file option
  2019-11-04 19:26 ` [PATCH 5/5] commit: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
  2019-11-05 16:27   ` Phillip Wood
@ 2019-11-06  4:51   ` Junio C Hamano
  1 sibling, 0 replies; 80+ messages in thread
From: Junio C Hamano @ 2019-11-06  4:51 UTC (permalink / raw)
  To: Alexandr Miloslavskiy via GitGitGadget; +Cc: git, Alexandr Miloslavskiy

"Alexandr Miloslavskiy via GitGitGadget" <gitgitgadget@gmail.com>
writes:

> From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
>
> This option solves the problem of commandline length limit for UI's
> built on top of git. Plumbing commands are not always a good fit, for
> two major reasons:
> 1) Some UI's serve as assistants that help user run git commands. In
>    this case, replacing familiar commands with plumbing commands will
>    confuse most users.
> 2) Some UI's have started and grown with porcelain commands. Replacing
>    existing logic with plumbing commands could be cumbersome and prone
>    to various new problems.

I think all the comments I made on "reset" with these two options,
both to the proposed log message and to the patch text, applies to
this this step, too, so I won't repeat them.

Thanks for working on this topic.

^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH v2 0/6] Add --pathspec-from-file option for reset, commit
  2019-11-04 19:26 [PATCH 0/5] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget
                   ` (4 preceding siblings ...)
  2019-11-04 19:26 ` [PATCH 5/5] commit: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
@ 2019-11-06 15:51 ` Alexandr Miloslavskiy via GitGitGadget
  2019-11-06 15:51   ` [PATCH v2 1/6] parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul` Alexandr Miloslavskiy via GitGitGadget
                     ` (6 more replies)
  5 siblings, 7 replies; 80+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-06 15:51 UTC (permalink / raw)
  To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano

Adds --pathspec-from-file option for porcelain commands to avoid commandline
length limit.

So far I implemented it for git commit and git reset, but my goal is to
support other commands as well after these patches are reviewed.

The patches are based on the following discussions:
https://public-inbox.org/git/c3be6eff-365b-96b8-16d2-0528612fc1fc@syntevo.com/T/#u
There, --stdin-paths was suggested.
https://public-inbox.org/git/a38bc928-7ccd-e2d9-b89b-23298e9fa95d@syntevo.com/T/#u
There, --stdin-paths was extended to --paths-file.
https://public-inbox.org/git/pull.133.git.gitgitgadget@gmail.com/
https://github.com/gitgitgadget/git/pull/133Patch from Johannes, I used it
as base of my patch. There, --pathspec-from-file is suggested in discussion.

Major changes compared to patch from Johannes:

1) --pathspec-from-file allows to read file, not just stdin

2) --literal-pathspecs should be honored. This is a good improvement because
it keeps --pathspec-from-file much closer to passing args on commandline.
Since the goal of the new option is to avoid commandline length limit, both
behaviors should be close to each other.

3) Patches are designed with all other git commands in mind

Changes from V1: A lot of small changes here and there, in accordance with
review suggestions.

Alexandr Miloslavskiy (6):
  parse-options.h: add new options `--pathspec-from-file`,
    `--pathspec-file-nul`
  pathspec: add new function to parse file
  doc: reset: unify <pathspec> description
  reset: support the `--pathspec-from-file` option
  doc: commit: unify <pathspec> description
  commit: support the --pathspec-from-file option

 Documentation/git-commit.txt    |  29 ++++--
 Documentation/git-reset.txt     |  37 +++++---
 builtin/commit.c                |  25 +++++-
 builtin/reset.c                 |  25 +++++-
 parse-options.h                 |   2 +
 pathspec.c                      |  38 ++++++++
 pathspec.h                      |  10 +++
 t/t7107-reset-pathspec-file.sh  | 155 ++++++++++++++++++++++++++++++++
 t/t7526-commit-pathspec-file.sh | 130 +++++++++++++++++++++++++++
 9 files changed, 425 insertions(+), 26 deletions(-)
 create mode 100755 t/t7107-reset-pathspec-file.sh
 create mode 100755 t/t7526-commit-pathspec-file.sh


base-commit: da72936f544fec5a335e66432610e4cef4430991
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-445%2FSyntevoAlex%2F%230207_pathspec_from_file-v2
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-445/SyntevoAlex/#0207_pathspec_from_file-v2
Pull-Request: https://github.com/gitgitgadget/git/pull/445

Range-diff vs v1:

 -:  ---------- > 1:  2dfaccf0d5 parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul`
 1:  52e7a84a2e ! 2:  96697ba072 pathspec: add new function to parse file
     @@ -50,16 +50,13 @@
      +	if (!strcmp(file, "-"))
      +		in = stdin;
      +	else
     -+		in = fopen(file, "r");
     -+
     -+	if (!in)
     -+		die(_("could not open '%s' for reading"), file);
     ++		in = xfopen(file, "r");
      +
      +	while (getline_fn(&buf, in) != EOF) {
      +		if (!nul_term_line && buf.buf[0] == '"') {
      +			strbuf_reset(&unquoted);
      +			if (unquote_c_style(&unquoted, buf.buf, NULL))
     -+				die(_("line is badly quoted"));
     ++				die(_("line is badly quoted: %s"), buf.buf);
      +			strbuf_swap(&buf, &unquoted);
      +		}
      +		argv_array_push(&parsed_file, buf.buf);
     @@ -91,11 +88,11 @@
      + * When 'file' is exactly "-" it uses 'stdin' instead.
      + */
      +void parse_pathspec_file(struct pathspec *pathspec,
     -+		    unsigned magic_mask,
     -+		    unsigned flags,
     -+		    const char *prefix,
     -+		    const char *file,
     -+		    int nul_term_line);
     ++			 unsigned magic_mask,
     ++			 unsigned flags,
     ++			 const char *prefix,
     ++			 const char *file,
     ++			 int nul_term_line);
       void copy_pathspec(struct pathspec *dst, const struct pathspec *src);
       void clear_pathspec(struct pathspec *);
       
 2:  1740ac7a29 ! 3:  f961a5155a doc: reset: unify <pathspec> description
     @@ -21,8 +21,11 @@
       
       DESCRIPTION
      @@
     + In the third form, set the current branch head (`HEAD`) to `<commit>`,
       optionally modifying index and working tree to match.
       The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms.
     ++The <pathspec> is used to limit the paths affected by the operation
     ++(see the entry for 'pathspec' in linkgit:gitglossary[7] for more details).
       
      -'git reset' [-q] [<tree-ish>] [--] <paths>...::
      -	This form resets the index entries for all `<paths>` to their
     @@ -34,14 +37,11 @@
      -This means that `git reset <paths>` is the opposite of `git add
      -<paths>`. This command is equivalent to
      -`git restore [--source=<tree-ish>] --staged <paths>...`.
     -+For more details about the <pathspec> syntax, see the 'pathspec' entry
     -+in linkgit:gitglossary[7].
     - +
     --After running `git reset <paths>` to update the index entry, you can
      +This means that `git reset <pathspec>` is the opposite of `git add
      +<pathspec>`. This command is equivalent to
      +`git restore [--source=<tree-ish>] --staged <pathspec>...`.
     -++
     + +
     +-After running `git reset <paths>` to update the index entry, you can
      +After running `git reset <pathspec>` to update the index entry, you can
       use linkgit:git-restore[1] to check the contents out of the index to
       the working tree. Alternatively, using linkgit:git-restore[1]
     @@ -54,10 +54,18 @@
       	Interactively select hunks in the difference between the index
       	and `<tree-ish>` (defaults to `HEAD`).  The chosen hunks are applied
       	in reverse to the index.
     - +
     -+For more details about the <pathspec> syntax, see the 'pathspec' entry
     -+in linkgit:gitglossary[7].
     -++
     - This means that `git reset -p` is the opposite of `git add -p`, i.e.
     - you can use it to selectively reset hunks. See the ``Interactive Mode''
     - section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
     +
     + diff --git a/builtin/reset.c b/builtin/reset.c
     + --- a/builtin/reset.c
     + +++ b/builtin/reset.c
     +@@
     + 
     + static const char * const git_reset_usage[] = {
     + 	N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"),
     +-	N_("git reset [-q] [<tree-ish>] [--] <paths>..."),
     +-	N_("git reset --patch [<tree-ish>] [--] [<paths>...]"),
     ++	N_("git reset [-q] [<tree-ish>] [--] <pathspec>..."),
     ++	N_("git reset --patch [<tree-ish>] [--] [<pathspec>...]"),
     + 	NULL
     + };
     + 
 3:  8d9f1fbc18 ! 4:  d72d4f16b5 reset: support the --pathspec-from-file option
     @@ -1,33 +1,13 @@
      Author: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
      
     -    reset: support the --pathspec-from-file option
     -
     -    This option solves the problem of commandline length limit for UI's
     -    built on top of git. Plumbing commands are not always a good fit, for
     -    two major reasons:
     -    1) Some UI's serve as assistants that help user run git commands. In
     -       this case, replacing familiar commands with plumbing commands will
     -       confuse most users.
     -    2) Some UI's have started and grown with porcelain commands. Replacing
     -       existing logic with plumbing commands could be cumbersome and prone
     -       to various new problems.
     -
     -    The new option is designed to behave very close to pathspecs passed in
     -    commandline args, so that switching from one to another is simple.
     -
     -    The new option allows to read either a specified file or `stdin`.
     -    Reading from file is a good way to avoid competing for stdin, and
     -    also gives some extra flexibility.
     +    reset: support the `--pathspec-from-file` option
      
          Decisions taken for simplicity:
     -    1) The new option is declared incompatible with other options that
     -       could use `stdin`.
     -    2) It is not allowed to pass some refspecs in args and others in file.
     -    3) New options do not have shorthands to avoid shorthand conflicts.
     -
     -    Also add new '--pathspec-file-null' switch that mirrors '-z' used in
     -    various places. Some porcelain commands, such as `git commit`, already
     -    use '-z', therefore it needed a new unambiguous name.
     +    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 <johannes.schindelin@gmx.de>
          Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
     @@ -39,7 +19,7 @@
       --------
       [verse]
       'git reset' [-q] [<tree-ish>] [--] <pathspec>...
     -+'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-null]] [<tree-ish>]
     ++'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-nul]] [<tree-ish>]
       'git reset' (--patch | -p) [<tree-ish>] [--] [<pathspec>...]
       'git reset' [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>]
       
     @@ -51,11 +31,13 @@
      +In the last form, set the current branch head (`HEAD`) to `<commit>`,
       optionally modifying index and working tree to match.
       The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms.
     + The <pathspec> is used to limit the paths affected by the operation
     + (see the entry for 'pathspec' in linkgit:gitglossary[7] for more details).
       
       'git reset' [-q] [<tree-ish>] [--] <pathspec>...::
      -	This form resets the index entries for all `<pathspec>` to their
     -+'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-null]] [<tree-ish>]::
     -+	These forms reset the index entries for all `<pathspec>` to their
     ++'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-nul]] [<tree-ish>]::
     ++	These forms reset the index entries matching pathspec to their
       	state at `<tree-ish>`.  (It does not affect the working tree or
       	the current branch.)
       +
     @@ -64,14 +46,15 @@
       	override the default behavior.
       
      +--pathspec-from-file=<file>::
     -+	Read `<pathspec>` from `<file>` instead. If `<file>` is exactly `-`
     -+	then read from standard input. Pathspecs are separated by LF or
     -+	CR/LF. Pathspecs can be quoted as explained for the configuration
     -+	variable `core.quotePath` (see linkgit:git-config[1]). See also
     -+	`--pathspec-file-null` and global `--literal-pathspecs`.
     -+
     -+--pathspec-file-null::
     -+	Only meaningful with `--pathspec-from-file`. Pathspecs are
     ++	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 are not expected to be quoted.
       
       EXAMPLES
     @@ -83,9 +66,9 @@
      @@
       static const char * const git_reset_usage[] = {
       	N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"),
     - 	N_("git reset [-q] [<tree-ish>] [--] <paths>..."),
     -+	N_("git reset [-q] [--pathspec-from-file [--pathspec-file-null]] [<tree-ish>]"),
     - 	N_("git reset --patch [<tree-ish>] [--] [<paths>...]"),
     + 	N_("git reset [-q] [<tree-ish>] [--] <pathspec>..."),
     ++	N_("git reset [-q] [--pathspec-from-file [--pathspec-file-nul]] [<tree-ish>]"),
     + 	N_("git reset --patch [<tree-ish>] [--] [<pathspec>...]"),
       	NULL
       };
      @@
     @@ -94,7 +77,7 @@
       	int reset_type = NONE, update_ref_status = 0, quiet = 0;
      -	int patch_mode = 0, unborn;
      -	const char *rev;
     -+	int patch_mode = 0, pathspec_file_null = 0, unborn;
     ++	int patch_mode = 0, pathspec_file_nul = 0, unborn;
      +	const char *rev, *pathspec_from_file = NULL;
       	struct object_id oid;
       	struct pathspec pathspec;
     @@ -103,10 +86,8 @@
       		OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")),
       		OPT_BOOL('N', "intent-to-add", &intent_to_add,
       				N_("record only the fact that removed paths will be added later")),
     -+		OPT_FILENAME(0, "pathspec-from-file", &pathspec_from_file,
     -+				N_("read pathspecs from file")),
     -+		OPT_BOOL(0, "pathspec-file-null", &pathspec_file_null,
     -+				N_("with --pathspec-from-file, pathspecs are separated with NUL character")),
     ++		OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
     ++		OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
       		OPT_END()
       	};
       
     @@ -119,13 +100,14 @@
      +			die(_("--pathspec-from-file is incompatible with --patch"));
      +
      +		if (pathspec.nr)
     -+			die(_("--pathspec-from-file is incompatible with path arguments"));
     ++			die(_("--pathspec-from-file is incompatible with pathspec arguments"));
      +
      +		parse_pathspec_file(&pathspec, 0,
      +				    PATHSPEC_PREFER_FULL,
     -+				    prefix, pathspec_from_file, pathspec_file_null);
     -+	} else if (pathspec_file_null)
     -+		die(_("--pathspec-file-null requires --pathspec-from-file"));
     ++				    prefix, pathspec_from_file, pathspec_file_nul);
     ++	} else if (pathspec_file_nul) {
     ++		die(_("--pathspec-file-nul requires --pathspec-from-file"));
     ++	}
      +
       	unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid);
       	if (unborn) {
     @@ -142,21 +124,7 @@
      +
      +. ./test-lib.sh
      +
     -+cat > expect.a <<EOF
     -+ D fileA.t
     -+EOF
     -+
     -+cat > expect.ab <<EOF
     -+ D fileA.t
     -+ D fileB.t
     -+EOF
     -+
     -+cat > expect.a_bc_d <<EOF
     -+D  fileA.t
     -+ D fileB.t
     -+ D fileC.t
     -+D  fileD.t
     -+EOF
     ++test_tick
      +
      +test_expect_success setup '
      +	echo A >fileA.t &&
     @@ -165,16 +133,16 @@
      +	echo D >fileD.t &&
      +	git add . &&
      +	git commit --include . -m "Commit" &&
     -+	checkpoint=$(git rev-parse --verify HEAD)
     ++	git tag checkpoint
      +'
      +
      +restore_checkpoint () {
     -+	git reset --hard "$checkpoint"
     ++	git reset --hard checkpoint
      +}
      +
     -+verify_state () {
     ++verify_expect () {
      +	git status --porcelain -- fileA.t fileB.t fileC.t fileD.t >actual &&
     -+	test_cmp "$1" actual
     ++	test_cmp expect actual
      +}
      +
      +test_expect_success '--pathspec-from-file from stdin' '
     @@ -183,7 +151,10 @@
      +	git rm fileA.t &&
      +	echo fileA.t | git reset --pathspec-from-file=- &&
      +
     -+	verify_state expect.a
     ++	cat >expect <<-\EOF &&
     ++	 D fileA.t
     ++	EOF
     ++	verify_expect
      +'
      +
      +test_expect_success '--pathspec-from-file from file' '
     @@ -193,34 +164,62 @@
      +	echo fileA.t >list &&
      +	git reset --pathspec-from-file=list &&
      +
     -+	verify_state expect.a
     ++	cat >expect <<-\EOF &&
     ++	 D fileA.t
     ++	EOF
     ++	verify_expect
      +'
      +
      +test_expect_success 'NUL delimiters' '
      +	restore_checkpoint &&
      +
      +	git rm fileA.t fileB.t &&
     -+	printf fileA.tQfileB.t | q_to_nul | git reset --pathspec-from-file=- --pathspec-file-null &&
     ++	printf "fileA.t\0fileB.t\0" | git reset --pathspec-from-file=- --pathspec-file-nul &&
      +
     -+	verify_state expect.ab
     ++	cat >expect <<-\EOF &&
     ++	 D fileA.t
     ++	 D fileB.t
     ++	EOF
     ++	verify_expect
      +'
      +
      +test_expect_success 'LF delimiters' '
      +	restore_checkpoint &&
      +
      +	git rm fileA.t fileB.t &&
     ++	printf "fileA.t\nfileB.t\n" | git reset --pathspec-from-file=- &&
     ++
     ++	cat >expect <<-\EOF &&
     ++	 D fileA.t
     ++	 D fileB.t
     ++	EOF
     ++	verify_expect
     ++'
     ++
     ++test_expect_success 'no trailing delimiter' '
     ++	restore_checkpoint &&
     ++
     ++	git rm fileA.t fileB.t &&
      +	printf "fileA.t\nfileB.t" | git reset --pathspec-from-file=- &&
      +
     -+	verify_state expect.ab
     ++	cat >expect <<-\EOF &&
     ++	 D fileA.t
     ++	 D fileB.t
     ++	EOF
     ++	verify_expect
      +'
      +
      +test_expect_success 'CRLF delimiters' '
      +	restore_checkpoint &&
      +
      +	git rm fileA.t fileB.t &&
     -+	printf "fileA.t\r\nfileB.t" | git reset --pathspec-from-file=- &&
     ++	printf "fileA.t\r\nfileB.t\r\n" | git reset --pathspec-from-file=- &&
      +
     -+	verify_state expect.ab
     ++	cat >expect <<-\EOF &&
     ++	 D fileA.t
     ++	 D fileB.t
     ++	EOF
     ++	verify_expect
      +'
      +
      +test_expect_success 'quotes' '
     @@ -229,21 +228,27 @@
      +	git rm fileA.t &&
      +	printf "\"file\\101.t\"" | git reset --pathspec-from-file=- &&
      +
     -+	verify_state expect.a
     ++	cat >expect <<-\EOF &&
     ++	 D fileA.t
     ++	EOF
     ++	verify_expect
      +'
      +
     -+test_expect_success 'quotes not compatible with --pathspec-file-null' '
     ++test_expect_success 'quotes not compatible with --pathspec-file-nul' '
      +	restore_checkpoint &&
      +
      +	git rm fileA.t &&
      +	printf "\"file\\101.t\"" >list &&
      +	# Note: "git reset" has not yet learned to fail on wrong pathspecs
     -+	git reset --pathspec-from-file=list --pathspec-file-null &&
     ++	git reset --pathspec-from-file=list --pathspec-file-nul &&
      +	
     -+	test_must_fail verify_state expect.a
     ++	cat >expect <<-\EOF &&
     ++	 D fileA.t
     ++	EOF
     ++	test_must_fail verify_expect
      +'
      +
     -+test_expect_success '--pathspec-from-file is not compatible with --soft --hard' '
     ++test_expect_success '--pathspec-from-file is not compatible with --soft or --hard' '
      +	restore_checkpoint &&
      +
      +	git rm fileA.t &&
     @@ -256,9 +261,15 @@
      +	restore_checkpoint &&
      +
      +	git rm fileA.t fileB.t fileC.t fileD.t &&
     -+	printf "fileB.t\nfileC.t" | git reset --pathspec-from-file=- &&
     -+
     -+	verify_state expect.a_bc_d
     ++	printf "fileB.t\nfileC.t\n" | git reset --pathspec-from-file=- &&
     ++
     ++	cat >expect <<-\EOF &&
     ++	D  fileA.t
     ++	 D fileB.t
     ++	 D fileC.t
     ++	D  fileD.t
     ++	EOF
     ++	verify_expect
      +'
      +
      +test_done
 4:  251d06e27f ! 5:  20c4495fd3 doc: commit: unify <pathspec> description
     @@ -24,12 +24,16 @@
       	Do not interpret any more arguments as options.
       
      -<file>...::
     +-	When files are given on the command line, the command
     +-	commits the contents of the named files, without
     +-	recording the changes already staged.  The contents of
     +-	these files are also staged for the next commit on top
     +-	of what have been staged before.
      +<pathspec>...::
     - 	When files are given on the command line, the command
     - 	commits the contents of the named files, without
     - 	recording the changes already staged.  The contents of
     - 	these files are also staged for the next commit on top
     - 	of what have been staged before.
     ++	When pathspec is given on the command line, commit the contents of
     ++	the files that match the pathspec without recording the changes
     ++	already added to the index. The contents of these files are also
     ++	staged for the next commit on top of what have been staged before.
      ++
      +For more details about the <pathspec> syntax, see the 'pathspec' entry
      +in linkgit:gitglossary[7].
 5:  f484704689 ! 6:  cb5fc9b14d commit: support the --pathspec-from-file option
     @@ -2,32 +2,12 @@
      
          commit: support the --pathspec-from-file option
      
     -    This option solves the problem of commandline length limit for UI's
     -    built on top of git. Plumbing commands are not always a good fit, for
     -    two major reasons:
     -    1) Some UI's serve as assistants that help user run git commands. In
     -       this case, replacing familiar commands with plumbing commands will
     -       confuse most users.
     -    2) Some UI's have started and grown with porcelain commands. Replacing
     -       existing logic with plumbing commands could be cumbersome and prone
     -       to various new problems.
     -
     -    The new option is designed to behave very close to pathspecs passed in
     -    commandline args, so that switching from one to another is simple.
     -
     -    The new option allows to read either a specified file or `stdin`.
     -    Reading from file is a good way to avoid competing for stdin, and
     -    also gives some extra flexibility.
     -
          Decisions taken for simplicity:
     -    1) The new option is declared incompatible with other options that
     -       could use `stdin`.
     -    2) It is not allowed to pass some refspecs in args and others in file.
     -    3) New options do not have shorthands to avoid shorthand conflicts.
     -
     -    Also add new '--pathspec-file-null' switch that mirrors '-z' used in
     -    various places. Some porcelain commands, such as `git commit`, already
     -    use '-z', therefore it needed a new unambiguous name.
     +    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>
      
     @@ -39,7 +19,7 @@
       	   [--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-null]]
     ++	   [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]]
      +	   [-S[<keyid>]] [--] [<pathspec>...]
       
       DESCRIPTION
     @@ -50,14 +30,15 @@
       	paths are also not required, and an empty commit will be created.
      +	
      +--pathspec-from-file=<file>::
     -+	Read `<pathspec>` from `<file>` instead. If `<file>` is exactly `-`
     -+	then read from standard input. Pathspecs are separated by LF or
     -+	CR/LF. Pathspecs can be quoted as explained for the configuration
     -+	variable `core.quotePath` (see linkgit:git-config[1]). See also
     -+	`--pathspec-file-null` and global `--literal-pathspecs`.
     -+
     -+--pathspec-file-null::
     -+	Only meaningful with `--pathspec-from-file`. Pathspecs are
     ++	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 are not expected to be quoted.
       
       -u[<mode>]::
     @@ -71,7 +52,7 @@
       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_null;
     ++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;
     @@ -87,14 +68,14 @@
      +			die(_("--pathspec-from-file is incompatible with --interactive/--patch"));
      +
      +		if (pathspec.nr)
     -+			die(_("--pathspec-from-file is incompatible with path arguments"));
     ++			die(_("--pathspec-from-file is incompatible with pathspec arguments"));
      +
      +		parse_pathspec_file(&pathspec, 0,
      +				    PATHSPEC_PREFER_FULL,
     -+				    prefix, pathspec_from_file, pathspec_file_null);
     ++				    prefix, pathspec_from_file, pathspec_file_nul);
     ++	} else if (pathspec_file_nul) {
     ++		die(_("--pathspec-file-nul requires --pathspec-from-file"));
      +	}
     -+	else if (pathspec_file_null)
     -+		die(_("--pathspec-file-null requires --pathspec-from-file"));
      +
      +	if (!pathspec.nr && (also || (only && !amend && !allow_empty)))
      +		die(_("No paths with --include/--only does not make sense."));
     @@ -115,8 +96,8 @@
       		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_FILENAME(0, "pathspec-from-file", &pathspec_from_file, N_("read pathspecs from file")),
     -+		OPT_BOOL(0, "pathspec-file-null", &pathspec_file_null, N_("with --pathspec-from-file, pathspecs are separated with NUL character")),
     ++		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,
     @@ -134,23 +115,9 @@
      +
      +test_tick
      +
     -+cat > expect.a <<EOF
     -+A	fileA.t
     -+EOF
     -+
     -+cat > expect.ab <<EOF
     -+A	fileA.t
     -+A	fileB.t
     -+EOF
     -+
     -+cat > expect.bc <<EOF
     -+A	fileB.t
     -+A	fileC.t
     -+EOF
     -+
      +test_expect_success setup '
      +	test_commit file0 &&
     -+	checkpoint=$(git rev-parse --verify HEAD) &&
     ++	git tag checkpoint &&
      +	
      +	echo A >fileA.t &&
      +	echo B >fileB.t &&
     @@ -160,12 +127,12 @@
      +'
      +
      +restore_checkpoint () {
     -+	git reset --soft "$checkpoint"
     ++	git reset --soft checkpoint
      +}
      +
     -+verify_state () {
     ++verify_expect () {
      +	git diff-tree --no-commit-id --name-status -r HEAD >actual &&
     -+	test_cmp "$1" actual
     ++	test_cmp expect actual
      +}
      +
      +test_expect_success '--pathspec-from-file from stdin' '
     @@ -173,7 +140,10 @@
      +
      +	echo fileA.t | git commit --pathspec-from-file=- -m "Commit" &&
      +
     -+	verify_state expect.a
     ++	cat >expect <<-\EOF &&
     ++	A	fileA.t
     ++	EOF
     ++	verify_expect
      +'
      +
      +test_expect_success '--pathspec-from-file from file' '
     @@ -182,31 +152,58 @@
      +	echo fileA.t >list &&
      +	git commit --pathspec-from-file=list -m "Commit" &&
      +
     -+	verify_state expect.a
     ++	cat >expect <<-\EOF &&
     ++	A	fileA.t
     ++	EOF
     ++	verify_expect
      +'
      +
      +test_expect_success 'NUL delimiters' '
      +	restore_checkpoint &&
      +
     -+	printf fileA.tQfileB.t | q_to_nul | git commit --pathspec-from-file=- --pathspec-file-null -m "Commit" &&
     ++	printf "fileA.t\0fileB.t\0" | git commit --pathspec-from-file=- --pathspec-file-nul -m "Commit" &&
      +
     -+	verify_state expect.ab
     ++	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" &&
      +
     -+	verify_state expect.ab
     ++	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" | git commit --pathspec-from-file=- -m "Commit" &&
     ++	printf "fileA.t\r\nfileB.t\r\n" | git commit --pathspec-from-file=- -m "Commit" &&
      +
     -+	verify_state expect.ab
     ++	cat >expect <<-\EOF &&
     ++	A	fileA.t
     ++	A	fileB.t
     ++	EOF
     ++	verify_expect
      +'
      +
      +test_expect_success 'quotes' '
     @@ -214,22 +211,29 @@
      +
      +	printf "\"file\\101.t\"" | git commit --pathspec-from-file=- -m "Commit" &&
      +
     -+	verify_state expect.a
     ++	cat >expect <<-\EOF &&
     ++	A	fileA.t
     ++	EOF
     ++	verify_expect expect
      +'
      +
     -+test_expect_success 'quotes not compatible with --pathspec-file-null' '
     ++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-null -m "Commit"
     ++	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" | git commit --pathspec-from-file=- -m "Commit" &&
     ++	printf "fileB.t\nfileC.t\n" | git commit --pathspec-from-file=- -m "Commit" &&
      +
     -+	verify_state expect.bc
     ++	cat >expect <<-\EOF &&
     ++	A	fileB.t
     ++	A	fileC.t
     ++	EOF
     ++	verify_expect
      +'
      +
      +test_done

-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH v2 1/6] parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul`
  2019-11-06 15:51 ` [PATCH v2 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget
@ 2019-11-06 15:51   ` Alexandr Miloslavskiy via GitGitGadget
  2019-11-06 15:51   ` [PATCH v2 2/6] pathspec: add new function to parse file Alexandr Miloslavskiy via GitGitGadget
                     ` (5 subsequent siblings)
  6 siblings, 0 replies; 80+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-06 15:51 UTC (permalink / raw)
  To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy

From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>

Support for various porcelain commands will arrive via additional
patches.

`--pathspec-from-file` solves the problem of commandline length limit
for UIs built on top of git. Plumbing commands are not always a good
fit, for two major reasons:
1) Some UIs show executed commands to user. In this case, porcelain
   commands are expected. One reason for that is letting user learn git
   commands by clicking UI buttons. The other reason is letting user
   study the history of commands in case of any unexpected results. Both
   of these will lose most of their value if UI uses combinations of
   arcane plumbing commands.
2) Some UIs have started and grown with porcelain commands. Replacing
   existing logic with plumbing commands could be cumbersome and prone
   to various new problems.

`--pathspec-from-file` will behave very close to pathspec passed in
commandline args, so that switching from one to another is simple.

`--pathspec-from-file` will read either a specified file or `stdin`
(when file is exactly "-"). Reading from file is a good way to avoid
competing for `stdin`, and also gives some extra flexibility.

`--pathspec-file-nul` switch mirrors `-z` already used in various
places. Some porcelain commands, such as `git commit`, already use
`-z`, therefore it needed a new unambiguous name.

New options do not have shorthands to avoid shorthand conflicts. It is
not expected that they will be typed in console.

Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 parse-options.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/parse-options.h b/parse-options.h
index 38a33a087e..c6cc01e715 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -330,5 +330,7 @@ int parse_opt_passthru_argv(const struct option *, const char *, int);
 #define OPT_WITH(v, h) _OPT_CONTAINS_OR_WITH("with", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG)
 #define OPT_WITHOUT(v, h) _OPT_CONTAINS_OR_WITH("without", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG)
 #define OPT_CLEANUP(v) OPT_STRING(0, "cleanup", v, N_("mode"), N_("how to strip spaces and #comments from message"))
+#define OPT_PATHSPEC_FROM_FILE(v) OPT_FILENAME(0, "pathspec-from-file", v, N_("read pathspec from file"))
+#define OPT_PATHSPEC_FILE_NUL(v)  OPT_BOOL(0, "pathspec-file-nul", v, N_("with --pathspec-from-file, pathspec elements are separated with NUL character"))
 
 #endif
-- 
gitgitgadget


^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH v2 2/6] pathspec: add new function to parse file
  2019-11-06 15:51 ` [PATCH v2 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget
  2019-11-06 15:51   ` [PATCH v2 1/6] parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul` Alexandr Miloslavskiy via GitGitGadget
@ 2019-11-06 15:51   ` Alexandr Miloslavskiy via GitGitGadget
  2019-11-19  5:59     ` Junio C Hamano
  2019-11-06 15:51   ` [PATCH v2 3/6] doc: reset: unify <pathspec> description Alexandr Miloslavskiy via GitGitGadget
                     ` (4 subsequent siblings)
  6 siblings, 1 reply; 80+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-06 15:51 UTC (permalink / raw)
  To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy

From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>

This will be used to support the new option '--pathspec-from-file' in
`git add`, `git-commit`, `git reset` etc.

Note also that we specifically handle CR/LF line endings to support
Windows better.

To simplify code, file is first parsed into `argv_array`. This allows
to avoid refactoring `parse_pathspec()`.

I considered adding `nul_term_line` to `flags` instead, but decided
that it doesn't fit there.

The new code is mostly taken from `cmd_update_index()` and
`split_mail_conv()`.

Co-authored-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 pathspec.c | 38 ++++++++++++++++++++++++++++++++++++++
 pathspec.h | 10 ++++++++++
 2 files changed, 48 insertions(+)

diff --git a/pathspec.c b/pathspec.c
index 12c2b322b3..2e9b376318 100644
--- a/pathspec.c
+++ b/pathspec.c
@@ -3,6 +3,8 @@
 #include "dir.h"
 #include "pathspec.h"
 #include "attr.h"
+#include "argv-array.h"
+#include "quote.h"
 
 /*
  * Finds which of the given pathspecs match items in the index.
@@ -613,6 +615,42 @@ void parse_pathspec(struct pathspec *pathspec,
 	}
 }
 
+void parse_pathspec_file(struct pathspec *pathspec, unsigned magic_mask,
+			 unsigned flags, const char *prefix,
+			 const char *file, int nul_term_line)
+{
+	struct argv_array parsed_file = ARGV_ARRAY_INIT;
+	strbuf_getline_fn getline_fn = nul_term_line ? strbuf_getline_nul :
+						       strbuf_getline;
+	struct strbuf buf = STRBUF_INIT;
+	struct strbuf unquoted = STRBUF_INIT;
+	FILE *in = NULL;
+
+	if (!strcmp(file, "-"))
+		in = stdin;
+	else
+		in = xfopen(file, "r");
+
+	while (getline_fn(&buf, in) != EOF) {
+		if (!nul_term_line && buf.buf[0] == '"') {
+			strbuf_reset(&unquoted);
+			if (unquote_c_style(&unquoted, buf.buf, NULL))
+				die(_("line is badly quoted: %s"), buf.buf);
+			strbuf_swap(&buf, &unquoted);
+		}
+		argv_array_push(&parsed_file, buf.buf);
+		strbuf_reset(&buf);
+	}
+
+	strbuf_release(&unquoted);
+	strbuf_release(&buf);
+	if (in != stdin)
+		fclose(in);
+
+	parse_pathspec(pathspec, magic_mask, flags, prefix, parsed_file.argv);
+	argv_array_clear(&parsed_file);
+}
+
 void copy_pathspec(struct pathspec *dst, const struct pathspec *src)
 {
 	int i, j;
diff --git a/pathspec.h b/pathspec.h
index 1c18a2c90c..a27dc81ba2 100644
--- a/pathspec.h
+++ b/pathspec.h
@@ -85,6 +85,16 @@ void parse_pathspec(struct pathspec *pathspec,
 		    unsigned flags,
 		    const char *prefix,
 		    const char **args);
+/*
+ * Same as parse_pathspec() but uses file as input.
+ * When 'file' is exactly "-" it uses 'stdin' instead.
+ */
+void parse_pathspec_file(struct pathspec *pathspec,
+			 unsigned magic_mask,
+			 unsigned flags,
+			 const char *prefix,
+			 const char *file,
+			 int nul_term_line);
 void copy_pathspec(struct pathspec *dst, const struct pathspec *src);
 void clear_pathspec(struct pathspec *);
 
-- 
gitgitgadget


^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH v2 3/6] doc: reset: unify <pathspec> description
  2019-11-06 15:51 ` [PATCH v2 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget
  2019-11-06 15:51   ` [PATCH v2 1/6] parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul` Alexandr Miloslavskiy via GitGitGadget
  2019-11-06 15:51   ` [PATCH v2 2/6] pathspec: add new function to parse file Alexandr Miloslavskiy via GitGitGadget
@ 2019-11-06 15:51   ` Alexandr Miloslavskiy via GitGitGadget
  2019-11-19  6:05     ` Junio C Hamano
  2019-11-06 15:51   ` [PATCH v2 4/6] reset: support the `--pathspec-from-file` option Alexandr Miloslavskiy via GitGitGadget
                     ` (3 subsequent siblings)
  6 siblings, 1 reply; 80+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-06 15:51 UTC (permalink / raw)
  To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy

From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>

Synchronize it to `git add`, which has a pretty good description.

Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 Documentation/git-reset.txt | 20 +++++++++++---------
 builtin/reset.c             |  4 ++--
 2 files changed, 13 insertions(+), 11 deletions(-)

diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index 97e0544d9e..f2ccb1426c 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -8,8 +8,8 @@ git-reset - Reset current HEAD to the specified state
 SYNOPSIS
 --------
 [verse]
-'git reset' [-q] [<tree-ish>] [--] <paths>...
-'git reset' (--patch | -p) [<tree-ish>] [--] [<paths>...]
+'git reset' [-q] [<tree-ish>] [--] <pathspec>...
+'git reset' (--patch | -p) [<tree-ish>] [--] [<pathspec>...]
 'git reset' [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>]
 
 DESCRIPTION
@@ -18,24 +18,26 @@ In the first and second form, copy entries from `<tree-ish>` to the index.
 In the third form, set the current branch head (`HEAD`) to `<commit>`,
 optionally modifying index and working tree to match.
 The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms.
+The <pathspec> is used to limit the paths affected by the operation
+(see the entry for 'pathspec' in linkgit:gitglossary[7] for more details).
 
-'git reset' [-q] [<tree-ish>] [--] <paths>...::
-	This form resets the index entries for all `<paths>` to their
+'git reset' [-q] [<tree-ish>] [--] <pathspec>...::
+	This form resets the index entries for all `<pathspec>` to their
 	state at `<tree-ish>`.  (It does not affect the working tree or
 	the current branch.)
 +
-This means that `git reset <paths>` is the opposite of `git add
-<paths>`. This command is equivalent to
-`git restore [--source=<tree-ish>] --staged <paths>...`.
+This means that `git reset <pathspec>` is the opposite of `git add
+<pathspec>`. This command is equivalent to
+`git restore [--source=<tree-ish>] --staged <pathspec>...`.
 +
-After running `git reset <paths>` to update the index entry, you can
+After running `git reset <pathspec>` to update the index entry, you can
 use linkgit:git-restore[1] to check the contents out of the index to
 the working tree. Alternatively, using linkgit:git-restore[1]
 and specifying a commit with `--source`, you
 can copy the contents of a path out of a commit to the index and to the
 working tree in one go.
 
-'git reset' (--patch | -p) [<tree-ish>] [--] [<paths>...]::
+'git reset' (--patch | -p) [<tree-ish>] [--] [<pathspec>...]::
 	Interactively select hunks in the difference between the index
 	and `<tree-ish>` (defaults to `HEAD`).  The chosen hunks are applied
 	in reverse to the index.
diff --git a/builtin/reset.c b/builtin/reset.c
index fdd572168b..9291c0fd72 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -30,8 +30,8 @@
 
 static const char * const git_reset_usage[] = {
 	N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"),
-	N_("git reset [-q] [<tree-ish>] [--] <paths>..."),
-	N_("git reset --patch [<tree-ish>] [--] [<paths>...]"),
+	N_("git reset [-q] [<tree-ish>] [--] <pathspec>..."),
+	N_("git reset --patch [<tree-ish>] [--] [<pathspec>...]"),
 	NULL
 };
 
-- 
gitgitgadget


^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH v2 4/6] reset: support the `--pathspec-from-file` option
  2019-11-06 15:51 ` [PATCH v2 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget
                     ` (2 preceding siblings ...)
  2019-11-06 15:51   ` [PATCH v2 3/6] doc: reset: unify <pathspec> description Alexandr Miloslavskiy via GitGitGadget
@ 2019-11-06 15:51   ` Alexandr Miloslavskiy via GitGitGadget
  2019-11-06 15:51   ` [PATCH v2 5/6] doc: commit: unify <pathspec> description Alexandr Miloslavskiy via GitGitGadget
                     ` (2 subsequent siblings)
  6 siblings, 0 replies; 80+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-06 15:51 UTC (permalink / raw)
  To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy

From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>

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 <johannes.schindelin@gmx.de>
Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 Documentation/git-reset.txt    |  19 +++-
 builtin/reset.c                |  21 ++++-
 t/t7107-reset-pathspec-file.sh | 155 +++++++++++++++++++++++++++++++++
 3 files changed, 190 insertions(+), 5 deletions(-)
 create mode 100755 t/t7107-reset-pathspec-file.sh

diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index f2ccb1426c..d2cd0af6a4 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -9,20 +9,22 @@ SYNOPSIS
 --------
 [verse]
 'git reset' [-q] [<tree-ish>] [--] <pathspec>...
+'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-nul]] [<tree-ish>]
 'git reset' (--patch | -p) [<tree-ish>] [--] [<pathspec>...]
 'git reset' [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>]
 
 DESCRIPTION
 -----------
-In the first and second form, copy entries from `<tree-ish>` to the index.
-In the third form, set the current branch head (`HEAD`) to `<commit>`,
+In the first three forms, copy entries from `<tree-ish>` to the index.
+In the last form, set the current branch head (`HEAD`) to `<commit>`,
 optionally modifying index and working tree to match.
 The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms.
 The <pathspec> is used to limit the paths affected by the operation
 (see the entry for 'pathspec' in linkgit:gitglossary[7] for more details).
 
 'git reset' [-q] [<tree-ish>] [--] <pathspec>...::
-	This form resets the index entries for all `<pathspec>` to their
+'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-nul]] [<tree-ish>]::
+	These forms reset the index entries matching pathspec to their
 	state at `<tree-ish>`.  (It does not affect the working tree or
 	the current branch.)
 +
@@ -103,6 +105,17 @@ OPTIONS
 	`reset.quiet` config option. `--quiet` and `--no-quiet` will
 	override the default behavior.
 
+--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 are not expected to be quoted.
 
 EXAMPLES
 --------
diff --git a/builtin/reset.c b/builtin/reset.c
index 9291c0fd72..246bf9d737 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -31,6 +31,7 @@
 static const char * const git_reset_usage[] = {
 	N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"),
 	N_("git reset [-q] [<tree-ish>] [--] <pathspec>..."),
+	N_("git reset [-q] [--pathspec-from-file [--pathspec-file-nul]] [<tree-ish>]"),
 	N_("git reset --patch [<tree-ish>] [--] [<pathspec>...]"),
 	NULL
 };
@@ -284,8 +285,8 @@ static int git_reset_config(const char *var, const char *value, void *cb)
 int cmd_reset(int argc, const char **argv, const char *prefix)
 {
 	int reset_type = NONE, update_ref_status = 0, quiet = 0;
-	int patch_mode = 0, unborn;
-	const char *rev;
+	int patch_mode = 0, pathspec_file_nul = 0, unborn;
+	const char *rev, *pathspec_from_file = NULL;
 	struct object_id oid;
 	struct pathspec pathspec;
 	int intent_to_add = 0;
@@ -306,6 +307,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 		OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")),
 		OPT_BOOL('N', "intent-to-add", &intent_to_add,
 				N_("record only the fact that removed paths will be added later")),
+		OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
+		OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
 		OPT_END()
 	};
 
@@ -316,6 +319,20 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 						PARSE_OPT_KEEP_DASHDASH);
 	parse_args(&pathspec, argv, prefix, patch_mode, &rev);
 
+	if (pathspec_from_file) {
+		if (patch_mode)
+			die(_("--pathspec-from-file is incompatible with --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"));
+	}
+
 	unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid);
 	if (unborn) {
 		/* reset on unborn branch: treat as reset to empty tree */
diff --git a/t/t7107-reset-pathspec-file.sh b/t/t7107-reset-pathspec-file.sh
new file mode 100755
index 0000000000..987cc77b7d
--- /dev/null
+++ b/t/t7107-reset-pathspec-file.sh
@@ -0,0 +1,155 @@
+#!/bin/sh
+
+test_description='reset --pathspec-from-file'
+
+. ./test-lib.sh
+
+test_tick
+
+test_expect_success setup '
+	echo A >fileA.t &&
+	echo B >fileB.t &&
+	echo C >fileC.t &&
+	echo D >fileD.t &&
+	git add . &&
+	git commit --include . -m "Commit" &&
+	git tag checkpoint
+'
+
+restore_checkpoint () {
+	git reset --hard checkpoint
+}
+
+verify_expect () {
+	git status --porcelain -- fileA.t fileB.t fileC.t fileD.t >actual &&
+	test_cmp expect actual
+}
+
+test_expect_success '--pathspec-from-file from stdin' '
+	restore_checkpoint &&
+
+	git rm fileA.t &&
+	echo fileA.t | git reset --pathspec-from-file=- &&
+
+	cat >expect <<-\EOF &&
+	 D fileA.t
+	EOF
+	verify_expect
+'
+
+test_expect_success '--pathspec-from-file from file' '
+	restore_checkpoint &&
+
+	git rm fileA.t &&
+	echo fileA.t >list &&
+	git reset --pathspec-from-file=list &&
+
+	cat >expect <<-\EOF &&
+	 D fileA.t
+	EOF
+	verify_expect
+'
+
+test_expect_success 'NUL delimiters' '
+	restore_checkpoint &&
+
+	git rm fileA.t fileB.t &&
+	printf "fileA.t\0fileB.t\0" | git reset --pathspec-from-file=- --pathspec-file-nul &&
+
+	cat >expect <<-\EOF &&
+	 D fileA.t
+	 D fileB.t
+	EOF
+	verify_expect
+'
+
+test_expect_success 'LF delimiters' '
+	restore_checkpoint &&
+
+	git rm fileA.t fileB.t &&
+	printf "fileA.t\nfileB.t\n" | git reset --pathspec-from-file=- &&
+
+	cat >expect <<-\EOF &&
+	 D fileA.t
+	 D fileB.t
+	EOF
+	verify_expect
+'
+
+test_expect_success 'no trailing delimiter' '
+	restore_checkpoint &&
+
+	git rm fileA.t fileB.t &&
+	printf "fileA.t\nfileB.t" | git reset --pathspec-from-file=- &&
+
+	cat >expect <<-\EOF &&
+	 D fileA.t
+	 D fileB.t
+	EOF
+	verify_expect
+'
+
+test_expect_success 'CRLF delimiters' '
+	restore_checkpoint &&
+
+	git rm fileA.t fileB.t &&
+	printf "fileA.t\r\nfileB.t\r\n" | git reset --pathspec-from-file=- &&
+
+	cat >expect <<-\EOF &&
+	 D fileA.t
+	 D fileB.t
+	EOF
+	verify_expect
+'
+
+test_expect_success 'quotes' '
+	restore_checkpoint &&
+
+	git rm fileA.t &&
+	printf "\"file\\101.t\"" | git reset --pathspec-from-file=- &&
+
+	cat >expect <<-\EOF &&
+	 D fileA.t
+	EOF
+	verify_expect
+'
+
+test_expect_success 'quotes not compatible with --pathspec-file-nul' '
+	restore_checkpoint &&
+
+	git rm fileA.t &&
+	printf "\"file\\101.t\"" >list &&
+	# Note: "git reset" has not yet learned to fail on wrong pathspecs
+	git reset --pathspec-from-file=list --pathspec-file-nul &&
+	
+	cat >expect <<-\EOF &&
+	 D fileA.t
+	EOF
+	test_must_fail verify_expect
+'
+
+test_expect_success '--pathspec-from-file is not compatible with --soft or --hard' '
+	restore_checkpoint &&
+
+	git rm fileA.t &&
+	echo fileA.t >list &&
+	test_must_fail git reset --soft --pathspec-from-file=list &&
+	test_must_fail git reset --hard --pathspec-from-file=list
+'
+
+test_expect_success 'only touches what was listed' '
+	restore_checkpoint &&
+
+	git rm fileA.t fileB.t fileC.t fileD.t &&
+	printf "fileB.t\nfileC.t\n" | git reset --pathspec-from-file=- &&
+
+	cat >expect <<-\EOF &&
+	D  fileA.t
+	 D fileB.t
+	 D fileC.t
+	D  fileD.t
+	EOF
+	verify_expect
+'
+
+test_done
-- 
gitgitgadget


^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH v2 5/6] doc: commit: unify <pathspec> description
  2019-11-06 15:51 ` [PATCH v2 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget
                     ` (3 preceding siblings ...)
  2019-11-06 15:51   ` [PATCH v2 4/6] reset: support the `--pathspec-from-file` option Alexandr Miloslavskiy via GitGitGadget
@ 2019-11-06 15:51   ` Alexandr Miloslavskiy via GitGitGadget
  2019-11-19  6:16     ` Junio C Hamano
  2019-11-06 15:51   ` [PATCH v2 6/6] commit: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
  2019-11-19 16:48   ` [PATCH v3 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget
  6 siblings, 1 reply; 80+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-06 15:51 UTC (permalink / raw)
  To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy

From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>

Synchronize it to `git add`, which has a pretty good description.
This also better disambiguates <file>... header.

Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 Documentation/git-commit.txt | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index afa7b75a23..915c212a0d 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -13,7 +13,7 @@ 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>]] [--] [<file>...]
+	   [-i | -o] [-S[<keyid>]] [--] [<pathspec>...]
 
 DESCRIPTION
 -----------
@@ -345,12 +345,14 @@ changes to tracked files.
 \--::
 	Do not interpret any more arguments as options.
 
-<file>...::
-	When files are given on the command line, the command
-	commits the contents of the named files, without
-	recording the changes already staged.  The contents of
-	these files are also staged for the next commit on top
-	of what have been staged before.
+<pathspec>...::
+	When pathspec is given on the command line, commit the contents of
+	the files that match the pathspec without recording the changes
+	already added to the index. The contents of these files are also
+	staged for the next commit on top of what have been staged before.
++
+For more details about the <pathspec> syntax, see the 'pathspec' entry
+in linkgit:gitglossary[7].
 
 :git-commit: 1
 include::date-formats.txt[]
-- 
gitgitgadget


^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH v2 6/6] commit: support the --pathspec-from-file option
  2019-11-06 15:51 ` [PATCH v2 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget
                     ` (4 preceding siblings ...)
  2019-11-06 15:51   ` [PATCH v2 5/6] doc: commit: unify <pathspec> description Alexandr Miloslavskiy via GitGitGadget
@ 2019-11-06 15:51   ` Alexandr Miloslavskiy via GitGitGadget
  2019-11-19  6:10     ` Junio C Hamano
  2019-11-19 16:48   ` [PATCH v3 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget
  6 siblings, 1 reply; 80+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-06 15:51 UTC (permalink / raw)
  To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy

From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>

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>
---
 Documentation/git-commit.txt    |  15 +++-
 builtin/commit.c                |  25 +++++-
 t/t7526-commit-pathspec-file.sh | 130 ++++++++++++++++++++++++++++++++
 3 files changed, 165 insertions(+), 5 deletions(-)
 create mode 100755 t/t7526-commit-pathspec-file.sh

diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index 915c212a0d..bbd53959bd 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -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
 -----------
@@ -277,6 +278,18 @@ FROM UPSTREAM REBASE" section in linkgit:git-rebase[1].)
 	the last commit without committing changes that have
 	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 are not expected to be quoted.
 
 -u[<mode>]::
 --untracked-files[=<mode>]::
diff --git a/builtin/commit.c b/builtin/commit.c
index e588bc6ad3..ed40729355 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -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
index 0000000000..4fdf206dd0
--- /dev/null
+++ b/t/t7526-commit-pathspec-file.sh
@@ -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
-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 1/5] pathspec: add new function to parse file
  2019-11-05 15:02   ` Phillip Wood
  2019-11-05 19:14     ` Alexandr Miloslavskiy
@ 2019-11-06 15:56     ` Alexandr Miloslavskiy
  1 sibling, 0 replies; 80+ messages in thread
From: Alexandr Miloslavskiy @ 2019-11-06 15:56 UTC (permalink / raw)
  To: phillip.wood, Alexandr Miloslavskiy via GitGitGadget, git; +Cc: Junio C Hamano

I think I have implemented all suggestions in PatchV2. Thanks!

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 3/5] reset: support the --pathspec-from-file option
  2019-11-05 15:03   ` Phillip Wood
  2019-11-05 19:22     ` Phillip Wood
  2019-11-05 19:36     ` Alexandr Miloslavskiy
@ 2019-11-06 15:56     ` Alexandr Miloslavskiy
  2 siblings, 0 replies; 80+ messages in thread
From: Alexandr Miloslavskiy @ 2019-11-06 15:56 UTC (permalink / raw)
  To: phillip.wood, Alexandr Miloslavskiy via GitGitGadget, git; +Cc: Junio C Hamano

I think I have implemented most suggestions in PatchV2. Thanks!

On 05.11.2019 16:03, Phillip Wood wrote:
> I'm confused reset does not use stdin does it?

I have improved commit message to clarify.

> --pathspec-file would be shorter and still conveys the intent of the 
> option.

I decided to stay with writing suggested by Junio.

> Is this line missing a leading space?

I think it's good. Maybe you were confused by patch formatting?

> Alignment again

I think it's good again :)

> If I've understood correctly this doesn't test if a path is correctly 
> unquoted, only that it is accepted.

Already agreed that test is good, just a bit non-obvious.

> s/^/--pathspec-from-file / ?

I decided not to repeat '--pathspec-from-file' in every test, because 
test package already has it in description. Is that good?

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 5/5] commit: support the --pathspec-from-file option
  2019-11-05 16:27   ` Phillip Wood
  2019-11-05 19:42     ` Alexandr Miloslavskiy
@ 2019-11-06 15:56     ` Alexandr Miloslavskiy
  2019-12-10 10:42       ` Phillip Wood
  1 sibling, 1 reply; 80+ messages in thread
From: Alexandr Miloslavskiy @ 2019-11-06 15:56 UTC (permalink / raw)
  To: phillip.wood, Alexandr Miloslavskiy via GitGitGadget, git; +Cc: Junio C Hamano

I think I have implemented most suggestions in PatchV2. Thanks!

> It might be worth tailoring the message to the command rather than 
> having exactly the same message for commit and reset

I decided to move the general comment to base commit where options are 
introduced and not repeat it where options are supported.

> I wonder if there is a way of calling parse_pathspec_file() from 
> parse_and_validate_options() instead. Otherwise we end up validating 
> options here instead which is a bit messy.

The code looks a bit too entangled to support that without making it 
worse. The biggest concern I have is that parse_and_validate_options() 
will populate `pathspec` and some other function will need to remember 
to clean it up. I like it better when `pathspec` is handled in one place.

I agree that things are not perfect, but this seems to be a consequence 
of other existing problems. For example, I would have expected a 
structure instead of a handful of global variables. That would have 
solved many problems. However, I didn't have the bravery to dive into 
this refactoring.

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 2/5] doc: reset: unify <pathspec> description
  2019-11-06  4:01   ` Junio C Hamano
@ 2019-11-06 15:56     ` Alexandr Miloslavskiy
  2019-11-07  5:46       ` Junio C Hamano
  0 siblings, 1 reply; 80+ messages in thread
From: Alexandr Miloslavskiy @ 2019-11-06 15:56 UTC (permalink / raw)
  To: Junio C Hamano, Alexandr Miloslavskiy via GitGitGadget; +Cc: git

I think I have implemented most suggestions in PatchV2. Thanks!

> Any time I see "... X. This means Y." either in the doc or in the
> proposed log message, I wish the author (not you in this case,
> obviously) thought twice about rewriting so that they do not say one
> thing and immediately have to rephrase it, i.e. either just say Y
> without saying X, or saying X more clearly without having to say Y.
> 
> In this case, however, I think X and Y are related but both relevant.
> The subcommand resets the index entries for chosen paths to match
> what is in the tree-ish, which is the same as restoring from a tree
> to the index.
> 
> It is not quite the opposite of adding to the index from the working
> tree.  In this sequence:
> 
> 	$ edit newfile
> 	$ git add newfile
> 
> and then further
> 
> 	$ edit newfile
> 	$ git add newfile
> 	$ git reset -- newfile
> 
> we are taken back to the state _before_ any of the changes made to
> newfile (in fact, since HEAD does not have newfile, the resulting
> index would not know about it, either).

I am a bit confused, is it correct that you don't expect me to change my 
patches?

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 3/5] reset: support the --pathspec-from-file option
  2019-11-06  4:40   ` Junio C Hamano
@ 2019-11-06 15:56     ` Alexandr Miloslavskiy
  0 siblings, 0 replies; 80+ messages in thread
From: Alexandr Miloslavskiy @ 2019-11-06 15:56 UTC (permalink / raw)
  To: Junio C Hamano, Alexandr Miloslavskiy via GitGitGadget; +Cc: git

I think I have implemented most suggestions in PatchV2. Thanks!

>> 2) Some UI's have started and grown with porcelain commands. Replacing
>>     existing logic with plumbing commands could be cumbersome and prone
>>     to various new problems.
> 
> There is not a lot of sympathy for such argument ;-)

Let's think that this merely reinforces the first point :)

> I do not understand this part.  Wouldn't it natural to expect that
> "-z", when used with "--pathspec-from-file", tell us that the named
> file is NUL delimited collection of records?  What's different about
> "--pathspec-file-null" that it is confusing to all it "-z"?
> 
> I actually do not mind not having "-z" and using only
> "--pathspec-file-null".  A new option with long name that feels
> similar to existing "-z" but does sufficiently different things so
> that it needs a different name, however, feels counter-productive
> for the purpose of using it in the UI that produces commands to be
> learned by end-users.

I didn't want to run into a situation where '-z' suddenly applies to 
multiple things (for example, if later it's decided to allow --patch 
with --pathspec-from-file).

Regarding UI, I think that (insert our UI name here) will not use 
'--pathspec-from-file' unless there are really many files. When it does 
happen, user will probably have an easier time understanding 
'--pathspec-file-nul' then '-z'.

> When I invented the terms "pathspec", "refspec", etc. for this
> project, I meant them to be collective nouns.  A pathspec is a set
> of pathspec elements, each of which is usually given as a separate
> argument on the command line.  So "Read pathspec from file" is good
> (not "Read pathspecs from file").
> 
> And "Pathspec elements" are separated by LF or CR/LF.

I changed that. However, `git grep pathspecs` is quite interesting. My 
own taste says that "pathspecs" is more expected then "pathspec".

> A tangent.  Since we do not allow NUL in a pathspec element, we do
> not even need the "-z" option.  When we read pathspec from a file,
> we can take any of CRLF, LF or NUL as the separator.
> 
> Ah, sorry, that would not help very much.  With "-z" we are allowing
> to express pathspec elements inside which there are embedded LF or
> CR/LF.  Sorry about the noise.

Yes, NUL delimiter allows to avoid escaping.

>   - Quote end-of-here-document (EOF in the above) if you do not rely
>     on parameter interpolation inside the here document; this helps
>     readers by telling them that what is presented is used verbatim.

Tests overwhelmingly prefer escaped form (\EOF) instead, so I used that.

> This feeds "fileA.t<NUL>fileB.t" without <NUL> after "fileB.t" to
> the command.  Intended?

Kind of intended. I changed tests to always use trailing delimiters, and 
also added a new dedicated test without trailing delimiter.

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 4/5] doc: commit: unify <pathspec> description
  2019-11-06  4:50   ` Junio C Hamano
@ 2019-11-06 15:56     ` Alexandr Miloslavskiy
  2019-11-07  5:54       ` Junio C Hamano
  0 siblings, 1 reply; 80+ messages in thread
From: Alexandr Miloslavskiy @ 2019-11-06 15:56 UTC (permalink / raw)
  To: Junio C Hamano, Alexandr Miloslavskiy via GitGitGadget; +Cc: git

I think I have implemented most suggestions in PatchV2. Thanks!

> I am not sure if we want to repeat this all over the place.
> 
> We do not say "For details about the <commit> syntax, see the
> 'SPECIFYING REVISIONS' section of linkgit:git-rev-parse[1]" for
> every command that takes <commit> from the command line.
> 
> Is your urge to suggest adding this sentence coming from that you
> are much more familiar with <commit> than <pathspec>?  In other
> words, if you were more familiar with Git, would you still be adding
> this (and not corresponding one for <commit>)?

Commit is a well known term, dating from ancient times like CVS or even 
older.

Pathspec, however, is something new. When I pretend to be someone new to 
git, I see it this way:
1) Let's read "git commit" documentation
2) Where on this commandline do I put my filename?!

So yes, I would repeat it in every location that could be popular for 
new users.

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 2/5] doc: reset: unify <pathspec> description
  2019-11-06 15:56     ` Alexandr Miloslavskiy
@ 2019-11-07  5:46       ` Junio C Hamano
  2019-11-07 11:05         ` Alexandr Miloslavskiy
  0 siblings, 1 reply; 80+ messages in thread
From: Junio C Hamano @ 2019-11-07  5:46 UTC (permalink / raw)
  To: Alexandr Miloslavskiy; +Cc: Alexandr Miloslavskiy via GitGitGadget, git

Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> writes:

>> It is not quite the opposite of adding to the index from the working
>> tree.  In this sequence:
>>
>> 	$ edit newfile
>> 	$ git add newfile
>>
>> and then further
>>
>> 	$ edit newfile
>> 	$ git add newfile
>> 	$ git reset -- newfile
>>
>> we are taken back to the state _before_ any of the changes made to
>> newfile (in fact, since HEAD does not have newfile, the resulting
>> index would not know about it, either).
>
> I am a bit confused, is it correct that you don't expect me to change
> my patches?

I do not know if removal of the only-half-correct "This is the
opposite of add" should be part of this change, or it should be a
separate fix.  The half-wrong sentene was not introduced by this
patch, so leaving it as-is is OK.  It just leaves another thing for
us to think about later.

Thanks.

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 4/5] doc: commit: unify <pathspec> description
  2019-11-06 15:56     ` Alexandr Miloslavskiy
@ 2019-11-07  5:54       ` Junio C Hamano
  2019-11-07 11:39         ` Alexandr Miloslavskiy
  0 siblings, 1 reply; 80+ messages in thread
From: Junio C Hamano @ 2019-11-07  5:54 UTC (permalink / raw)
  To: Alexandr Miloslavskiy; +Cc: Alexandr Miloslavskiy via GitGitGadget, git

Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> writes:

> I think I have implemented most suggestions in PatchV2. Thanks!
>
>> I am not sure if we want to repeat this all over the place.
>>
>> We do not say "For details about the <commit> syntax, see the
>> 'SPECIFYING REVISIONS' section of linkgit:git-rev-parse[1]" for
>> every command that takes <commit> from the command line.
>>
>> Is your urge to suggest adding this sentence coming from that you
>> are much more familiar with <commit> than <pathspec>?  In other
>> words, if you were more familiar with Git, would you still be adding
>> this (and not corresponding one for <commit>)?
>
> Commit is a well known term, dating from ancient times like CVS or
> even older.

That's more or less irrelevant.  

I am reacting to this from your change that you omitted quoting in
your reply:

> +For more details about the <pathspec> syntax, see the 'pathspec' entry
> +in linkgit:gitglossary[7].

That sentence is for those who have some notion of <pathspec> but
does not know enough about its syntax.

CVS does not let you specify <commit> like "master^{/^fix frotz}~4";
A user a user who is familiar with CVS's commits would still want to
refer to the section "For details about the <commit> syntax".

I am not advocating to add the reference to SPECIFYING REVISIONS
section; instead we should let readers know that every time they see
<defined word>, they can refer to the glossary for more details.

> Pathspec, however, is something new.

Compared to CVS, everything in Git may be new, but that was a news
in 2010, not this year.

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 2/5] doc: reset: unify <pathspec> description
  2019-11-07  5:46       ` Junio C Hamano
@ 2019-11-07 11:05         ` Alexandr Miloslavskiy
  2019-11-08  3:04           ` Junio C Hamano
  0 siblings, 1 reply; 80+ messages in thread
From: Alexandr Miloslavskiy @ 2019-11-07 11:05 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Alexandr Miloslavskiy via GitGitGadget, git

On 07.11.2019 6:46, Junio C Hamano wrote:
> I do not know if removal of the only-half-correct "This is the
> opposite of add" should be part of this change, or it should be a
> separate fix.  The half-wrong sentene was not introduced by this
> patch, so leaving it as-is is OK.  It just leaves another thing for
> us to think about later.

My feeling is that this problem is separate from the topic I'm working 
on. I only touched docs to synchronize <pathspec>, so that I can 
copy&paste new option description in next commit without tailoring it to 
local speech. Also, I must admit that upon reading your explanation, I 
felt that I lack the experience to update the writing properly.

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 4/5] doc: commit: unify <pathspec> description
  2019-11-07  5:54       ` Junio C Hamano
@ 2019-11-07 11:39         ` Alexandr Miloslavskiy
  0 siblings, 0 replies; 80+ messages in thread
From: Alexandr Miloslavskiy @ 2019-11-07 11:39 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Alexandr Miloslavskiy via GitGitGadget, git

On 07.11.2019 6:54, Junio C Hamano wrote:
> I am reacting to this from your change that you omitted quoting in
> your reply:
> 
>> +For more details about the <pathspec> syntax, see the 'pathspec' entry
>> +in linkgit:gitglossary[7].
> 
> That sentence is for those who have some notion of <pathspec> but
> does not know enough about its syntax.

In the perfect world, I would expect _every_ 'pathspec' word in text to 
be an HTML-style link to a dedicated article, not just a paragraph in 
glossary.

MSDN is in general a good example [1]: there, it's easy to read only a 
small portion of article, ignoring anything you're not interested in, 
and still have all links at hand.

Regarding dedicated page: the content of 'pathspec' in glossary is 
already long enough for a page, and it could benefit from additional 
examples. Also, having a dedicated page makes linking easier, so that 
user doesn't have to scroll glossary.

Regarding links: I don't really understand what git's doc format allows. 
Is it just pure text in worst (or even average) case?

If it's usually something with clickable links, it could be worth to 
just insert links everywhere.

If it's usually plaintext, then "see the 'pathspec' entry in 
linkgit:gitglossary[7]" is a bit too verbose to repeat on every 
occasion. Still, I'd like to see links everywhere. One big reason is to 
let reader know that the explanation actually exists!

A compromise solution is to give every article header like this:

     This article uses the following terms which are explained
     in linkgit:gitglossary[7]:
     * pathspec
     * tree-ish
     * refspec

If it's close to top of article, then chances are that everyone will 
notice it. Also, it will not require extra verbosity in plaintext.

> CVS does not let you specify <commit> like "master^{/^fix frotz}~4";
> A user a user who is familiar with CVS's commits would still want to
> refer to the section "For details about the <commit> syntax".
> 
> I am not advocating to add the reference to SPECIFYING REVISIONS
> section; instead we should let readers know that every time they see
> <defined word>, they can refer to the glossary for more details.

I now think that my arguments apply to <pathspec> AND <commit> in the 
same way. Indeed a user can't know complex <commit> variants until 
he/she reads it in git docs.

----

[1] 
https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 2/5] doc: reset: unify <pathspec> description
  2019-11-07 11:05         ` Alexandr Miloslavskiy
@ 2019-11-08  3:04           ` Junio C Hamano
  0 siblings, 0 replies; 80+ messages in thread
From: Junio C Hamano @ 2019-11-08  3:04 UTC (permalink / raw)
  To: Alexandr Miloslavskiy; +Cc: Alexandr Miloslavskiy via GitGitGadget, git

Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> writes:

> On 07.11.2019 6:46, Junio C Hamano wrote:
>> I do not know if removal of the only-half-correct "This is the
>> opposite of add" should be part of this change, or it should be a
>> separate fix.  The half-wrong sentene was not introduced by this
>> patch, so leaving it as-is is OK.  It just leaves another thing for
>> us to think about later.
>
> My feeling is that this problem is separate from the topic I'm working
> on. I only touched docs to synchronize <pathspec>, so that I can
> copy&paste new option description in next commit without tailoring it
> to local speech. Also, I must admit that upon reading your
> explanation, I felt that I lack the experience to update the writing
> properly.

Yeah, I do think it is cleaner to leave it as a separate issue.

Thanks.


^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v2 2/6] pathspec: add new function to parse file
  2019-11-06 15:51   ` [PATCH v2 2/6] pathspec: add new function to parse file Alexandr Miloslavskiy via GitGitGadget
@ 2019-11-19  5:59     ` Junio C Hamano
  2019-11-19 16:50       ` Alexandr Miloslavskiy
  0 siblings, 1 reply; 80+ messages in thread
From: Junio C Hamano @ 2019-11-19  5:59 UTC (permalink / raw)
  To: Alexandr Miloslavskiy via GitGitGadget; +Cc: git, Alexandr Miloslavskiy

"Alexandr Miloslavskiy via GitGitGadget" <gitgitgadget@gmail.com>
writes:

> +void parse_pathspec_file(struct pathspec *pathspec, unsigned magic_mask,
> +			 unsigned flags, const char *prefix,
> +			 const char *file, int nul_term_line)
> +{
> +	struct argv_array parsed_file = ARGV_ARRAY_INIT;
> +	strbuf_getline_fn getline_fn = nul_term_line ? strbuf_getline_nul :
> +						       strbuf_getline;
> +	struct strbuf buf = STRBUF_INIT;
> +	struct strbuf unquoted = STRBUF_INIT;
> +	FILE *in = NULL;

Useless initialization to NULL can be dropped here.

> +	if (!strcmp(file, "-"))
> +		in = stdin;
> +	else
> +		in = xfopen(file, "r");

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v2 3/6] doc: reset: unify <pathspec> description
  2019-11-06 15:51   ` [PATCH v2 3/6] doc: reset: unify <pathspec> description Alexandr Miloslavskiy via GitGitGadget
@ 2019-11-19  6:05     ` Junio C Hamano
  2019-11-19 16:52       ` Alexandr Miloslavskiy
  0 siblings, 1 reply; 80+ messages in thread
From: Junio C Hamano @ 2019-11-19  6:05 UTC (permalink / raw)
  To: Alexandr Miloslavskiy via GitGitGadget; +Cc: git, Alexandr Miloslavskiy

"Alexandr Miloslavskiy via GitGitGadget" <gitgitgadget@gmail.com>
writes:

> -'git reset' [-q] [<tree-ish>] [--] <paths>...
> -'git reset' (--patch | -p) [<tree-ish>] [--] [<paths>...]
> +'git reset' [-q] [<tree-ish>] [--] <pathspec>...
> +'git reset' (--patch | -p) [<tree-ish>] [--] [<pathspec>...]

Good.

> @@ -18,24 +18,26 @@ In the first and second form, copy entries from `<tree-ish>` to the index.
>  In the third form, set the current branch head (`HEAD`) to `<commit>`,
>  optionally modifying index and working tree to match.
>  The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms.
> +The <pathspec> is used to limit the paths affected by the operation
> +(see the entry for 'pathspec' in linkgit:gitglossary[7] for more details).

Good.

> -'git reset' [-q] [<tree-ish>] [--] <paths>...::
> -	This form resets the index entries for all `<paths>` to their
> +'git reset' [-q] [<tree-ish>] [--] <pathspec>...::
> +	This form resets the index entries for all `<pathspec>` to their

This is not so good.  The original pretended as if <paths> are the
exact pathnames, so it was sort-of OK for it to say "for all paths".
Since we are highlighting the fact that these are not pathnames but
the patterns to match pathnames, however, the description needs a
slight update to match, perhaps like

	ths form resets the index entries for all paths that match
	the `<pathspec>` to their...

>  	state at `<tree-ish>`.  (It does not affect the working tree or
>  	the current branch.)

Thanks.

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v2 6/6] commit: support the --pathspec-from-file option
  2019-11-06 15:51   ` [PATCH v2 6/6] commit: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
@ 2019-11-19  6:10     ` Junio C Hamano
  2019-11-19 16:56       ` Alexandr Miloslavskiy
  0 siblings, 1 reply; 80+ messages in thread
From: Junio C Hamano @ 2019-11-19  6:10 UTC (permalink / raw)
  To: Alexandr Miloslavskiy via GitGitGadget; +Cc: git, Alexandr Miloslavskiy

"Alexandr Miloslavskiy via GitGitGadget" <gitgitgadget@gmail.com>
writes:

> @@ -277,6 +278,18 @@ FROM UPSTREAM REBASE" section in linkgit:git-rebase[1].)
>  	the last commit without committing changes that have
>  	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`.

OK.

> +--pathspec-file-nul::
> +	Only meaningful with `--pathspec-from-file`. Pathspec elements are
> +	separated with NUL character and are not expected to be quoted.

Although it is not incorrect, "are not expected to be quoted" feels
a bit weak as the technical description.  Are they not expected to
be quoted, but the command gracefully works on them even when they
are found to be quoted?

Rephrasing to avoid such misinterpretation may be worth doing,
perhaps

	... and are taken as-is without unquoting (i.e. as if
	`core.quotePath` is set to `false`).

or something like that?

Thanks.

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v2 5/6] doc: commit: unify <pathspec> description
  2019-11-06 15:51   ` [PATCH v2 5/6] doc: commit: unify <pathspec> description Alexandr Miloslavskiy via GitGitGadget
@ 2019-11-19  6:16     ` Junio C Hamano
  2019-11-19 16:53       ` Alexandr Miloslavskiy
  2019-11-19 17:02       ` Alexandr Miloslavskiy
  0 siblings, 2 replies; 80+ messages in thread
From: Junio C Hamano @ 2019-11-19  6:16 UTC (permalink / raw)
  To: Alexandr Miloslavskiy via GitGitGadget; +Cc: git, Alexandr Miloslavskiy

"Alexandr Miloslavskiy via GitGitGadget" <gitgitgadget@gmail.com>
writes:
> +<pathspec>...::
> +	When pathspec is given on the command line, commit the contents of
> +	the files that match the pathspec without recording the changes
> +	already added to the index. The contents of these files are also
> +	staged for the next commit on top of what have been staged before.
> ++
> +For more details about the <pathspec> syntax, see the 'pathspec' entry
> +in linkgit:gitglossary[7].

What you added in [PATCH 3/6] (git reset doc) sends a slightly
different message, i.e.

    The <pathspec> is used to limit the paths affected by the operation
    (see the entry for 'pathspec' in linkgit:gitglossary[7] for more details).

and I think that was more appropriate than what we see here.  You
are referring your readers to the glossary entry not just for the
syntax but also the entire concept of <pathspec>.

IOW, it would be needed to drop "syntax" from "about the <pathspec>
syntax" from here to match the update to "git reset" documentation.

Thanks.

^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH v3 0/6] Add --pathspec-from-file option for reset, commit
  2019-11-06 15:51 ` [PATCH v2 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget
                     ` (5 preceding siblings ...)
  2019-11-06 15:51   ` [PATCH v2 6/6] commit: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
@ 2019-11-19 16:48   ` Alexandr Miloslavskiy via GitGitGadget
  2019-11-19 16:48     ` [PATCH v3 1/6] parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul` Alexandr Miloslavskiy via GitGitGadget
                       ` (7 more replies)
  6 siblings, 8 replies; 80+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-19 16:48 UTC (permalink / raw)
  To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano

Changes from V2:

1) Some polishing in documentation 2) = NULL removed from 
parse_pathspec_file()

Alexandr Miloslavskiy (6):
  parse-options.h: add new options `--pathspec-from-file`,
    `--pathspec-file-nul`
  pathspec: add new function to parse file
  doc: reset: synchronize <pathspec> description
  reset: support the `--pathspec-from-file` option
  doc: commit: synchronize <pathspec> description
  commit: support the --pathspec-from-file option

 Documentation/git-commit.txt    |  29 ++++--
 Documentation/git-reset.txt     |  48 +++++++---
 builtin/commit.c                |  25 +++++-
 builtin/reset.c                 |  25 +++++-
 parse-options.h                 |   2 +
 pathspec.c                      |  38 ++++++++
 pathspec.h                      |  10 +++
 t/t7107-reset-pathspec-file.sh  | 155 ++++++++++++++++++++++++++++++++
 t/t7526-commit-pathspec-file.sh | 130 +++++++++++++++++++++++++++
 9 files changed, 434 insertions(+), 28 deletions(-)
 create mode 100755 t/t7107-reset-pathspec-file.sh
 create mode 100755 t/t7526-commit-pathspec-file.sh


base-commit: da72936f544fec5a335e66432610e4cef4430991
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-445%2FSyntevoAlex%2F%230207_pathspec_from_file-v3
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-445/SyntevoAlex/#0207_pathspec_from_file-v3
Pull-Request: https://github.com/gitgitgadget/git/pull/445

Range-diff vs v2:

 1:  2dfaccf0d5 = 1:  19b80326ea parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul`
 2:  96697ba072 ! 2:  55a7c6ec3c pathspec: add new function to parse file
     @@ -45,7 +45,7 @@
      +						       strbuf_getline;
      +	struct strbuf buf = STRBUF_INIT;
      +	struct strbuf unquoted = STRBUF_INIT;
     -+	FILE *in = NULL;
     ++	FILE *in;
      +
      +	if (!strcmp(file, "-"))
      +		in = stdin;
 3:  f961a5155a ! 3:  d9f32e523c doc: reset: unify <pathspec> description
     @@ -1,8 +1,8 @@
      Author: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
      
     -    doc: reset: unify <pathspec> description
     +    doc: reset: synchronize <pathspec> description
      
     -    Synchronize it to `git add`, which has a pretty good description.
     +    `git add` shows an example of good writing, follow it.
      
          Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
      
     @@ -21,18 +21,17 @@
       
       DESCRIPTION
      @@
     - In the third form, set the current branch head (`HEAD`) to `<commit>`,
       optionally modifying index and working tree to match.
       The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms.
     -+The <pathspec> is used to limit the paths affected by the operation
     -+(see the entry for 'pathspec' in linkgit:gitglossary[7] for more details).
       
      -'git reset' [-q] [<tree-ish>] [--] <paths>...::
      -	This form resets the index entries for all `<paths>` to their
     +-	state at `<tree-ish>`.  (It does not affect the working tree or
     +-	the current branch.)
      +'git reset' [-q] [<tree-ish>] [--] <pathspec>...::
     -+	This form resets the index entries for all `<pathspec>` to their
     - 	state at `<tree-ish>`.  (It does not affect the working tree or
     - 	the current branch.)
     ++	This form resets the index entries for all paths that match the
     ++	`<pathspec>` to their state at `<tree-ish>`.  (It does not affect
     ++	the working tree or the current branch.)
       +
      -This means that `git reset <paths>` is the opposite of `git add
      -<paths>`. This command is equivalent to
     @@ -54,6 +53,20 @@
       	Interactively select hunks in the difference between the index
       	and `<tree-ish>` (defaults to `HEAD`).  The chosen hunks are applied
       	in reverse to the index.
     +@@
     + 	`reset.quiet` config option. `--quiet` and `--no-quiet` will
     + 	override the default behavior.
     + 
     ++\--::
     ++	Do not interpret any more arguments as options.
     ++
     ++<pathspec>...::
     ++	Limits the paths affected by the operation.
     +++
     ++For more details, see the 'pathspec' entry in linkgit:gitglossary[7].
     + 
     + EXAMPLES
     + --------
      
       diff --git a/builtin/reset.c b/builtin/reset.c
       --- a/builtin/reset.c
 4:  d72d4f16b5 ! 4:  8a10ff881b reset: support the `--pathspec-from-file` option
     @@ -31,15 +31,13 @@
      +In the last form, set the current branch head (`HEAD`) to `<commit>`,
       optionally modifying index and working tree to match.
       The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms.
     - The <pathspec> is used to limit the paths affected by the operation
     - (see the entry for 'pathspec' in linkgit:gitglossary[7] for more details).
       
       'git reset' [-q] [<tree-ish>] [--] <pathspec>...::
     --	This form resets the index entries for all `<pathspec>` to their
     +-	This form resets the index entries for all paths that match the
      +'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-nul]] [<tree-ish>]::
     -+	These forms reset the index entries matching pathspec to their
     - 	state at `<tree-ish>`.  (It does not affect the working tree or
     - 	the current branch.)
     ++	These forms reset the index entries for all paths that match the
     + 	`<pathspec>` to their state at `<tree-ish>`.  (It does not affect
     + 	the working tree or the current branch.)
       +
      @@
       	`reset.quiet` config option. `--quiet` and `--no-quiet` will
     @@ -55,10 +53,12 @@
      +
      +--pathspec-file-nul::
      +	Only meaningful with `--pathspec-from-file`. Pathspec elements are
     -+	separated with NUL character and are not expected to be quoted.
     ++	separated with NUL character and all other characters are taken
     ++	literally (including newlines and quotes).
     ++
     + \--::
     + 	Do not interpret any more arguments as options.
       
     - EXAMPLES
     - --------
      
       diff --git a/builtin/reset.c b/builtin/reset.c
       --- a/builtin/reset.c
     @@ -241,7 +241,7 @@
      +	printf "\"file\\101.t\"" >list &&
      +	# Note: "git reset" has not yet learned to fail on wrong pathspecs
      +	git reset --pathspec-from-file=list --pathspec-file-nul &&
     -+	
     ++
      +	cat >expect <<-\EOF &&
      +	 D fileA.t
      +	EOF
 5:  20c4495fd3 ! 5:  0b79797e77 doc: commit: unify <pathspec> description
     @@ -1,8 +1,8 @@
      Author: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
      
     -    doc: commit: unify <pathspec> description
     +    doc: commit: synchronize <pathspec> description
      
     -    Synchronize it to `git add`, which has a pretty good description.
     +    `git add` shows an example of good writing, follow it.
          This also better disambiguates <file>... header.
      
          Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
     @@ -35,8 +35,7 @@
      +	already added to the index. The contents of these files are also
      +	staged for the next commit on top of what have been staged before.
      ++
     -+For more details about the <pathspec> syntax, see the 'pathspec' entry
     -+in linkgit:gitglossary[7].
     ++For more details, see the 'pathspec' entry in linkgit:gitglossary[7].
       
       :git-commit: 1
       include::date-formats.txt[]
 6:  cb5fc9b14d ! 6:  7e48212002 commit: support the --pathspec-from-file option
     @@ -25,10 +25,9 @@
       DESCRIPTION
       -----------
      @@
     - 	the last commit without committing changes that have
       	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
     @@ -39,10 +38,12 @@
      +
      +--pathspec-file-nul::
      +	Only meaningful with `--pathspec-from-file`. Pathspec elements are
     -+	separated with NUL character and are not expected to be quoted.
     - 
     ++	separated with NUL character and all other characters are taken
     ++	literally (including newlines and quotes).
     ++
       -u[<mode>]::
       --untracked-files[=<mode>]::
     + 	Show untracked files.
      
       diff --git a/builtin/commit.c b/builtin/commit.c
       --- a/builtin/commit.c
     @@ -118,7 +119,7 @@
      +test_expect_success setup '
      +	test_commit file0 &&
      +	git tag checkpoint &&
     -+	
     ++
      +	echo A >fileA.t &&
      +	echo B >fileB.t &&
      +	echo C >fileC.t &&

-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH v3 1/6] parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul`
  2019-11-19 16:48   ` [PATCH v3 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget
@ 2019-11-19 16:48     ` Alexandr Miloslavskiy via GitGitGadget
  2019-11-19 16:48     ` [PATCH v3 2/6] pathspec: add new function to parse file Alexandr Miloslavskiy via GitGitGadget
                       ` (6 subsequent siblings)
  7 siblings, 0 replies; 80+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-19 16:48 UTC (permalink / raw)
  To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy

From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>

Support for various porcelain commands will arrive via additional
patches.

`--pathspec-from-file` solves the problem of commandline length limit
for UIs built on top of git. Plumbing commands are not always a good
fit, for two major reasons:
1) Some UIs show executed commands to user. In this case, porcelain
   commands are expected. One reason for that is letting user learn git
   commands by clicking UI buttons. The other reason is letting user
   study the history of commands in case of any unexpected results. Both
   of these will lose most of their value if UI uses combinations of
   arcane plumbing commands.
2) Some UIs have started and grown with porcelain commands. Replacing
   existing logic with plumbing commands could be cumbersome and prone
   to various new problems.

`--pathspec-from-file` will behave very close to pathspec passed in
commandline args, so that switching from one to another is simple.

`--pathspec-from-file` will read either a specified file or `stdin`
(when file is exactly "-"). Reading from file is a good way to avoid
competing for `stdin`, and also gives some extra flexibility.

`--pathspec-file-nul` switch mirrors `-z` already used in various
places. Some porcelain commands, such as `git commit`, already use
`-z`, therefore it needed a new unambiguous name.

New options do not have shorthands to avoid shorthand conflicts. It is
not expected that they will be typed in console.

Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 parse-options.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/parse-options.h b/parse-options.h
index 38a33a087e..c6cc01e715 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -330,5 +330,7 @@ int parse_opt_passthru_argv(const struct option *, const char *, int);
 #define OPT_WITH(v, h) _OPT_CONTAINS_OR_WITH("with", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG)
 #define OPT_WITHOUT(v, h) _OPT_CONTAINS_OR_WITH("without", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG)
 #define OPT_CLEANUP(v) OPT_STRING(0, "cleanup", v, N_("mode"), N_("how to strip spaces and #comments from message"))
+#define OPT_PATHSPEC_FROM_FILE(v) OPT_FILENAME(0, "pathspec-from-file", v, N_("read pathspec from file"))
+#define OPT_PATHSPEC_FILE_NUL(v)  OPT_BOOL(0, "pathspec-file-nul", v, N_("with --pathspec-from-file, pathspec elements are separated with NUL character"))
 
 #endif
-- 
gitgitgadget


^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH v3 2/6] pathspec: add new function to parse file
  2019-11-19 16:48   ` [PATCH v3 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget
  2019-11-19 16:48     ` [PATCH v3 1/6] parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul` Alexandr Miloslavskiy via GitGitGadget
@ 2019-11-19 16:48     ` Alexandr Miloslavskiy via GitGitGadget
  2019-11-19 16:48     ` [PATCH v3 3/6] doc: reset: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget
                       ` (5 subsequent siblings)
  7 siblings, 0 replies; 80+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-19 16:48 UTC (permalink / raw)
  To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy

From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>

This will be used to support the new option '--pathspec-from-file' in
`git add`, `git-commit`, `git reset` etc.

Note also that we specifically handle CR/LF line endings to support
Windows better.

To simplify code, file is first parsed into `argv_array`. This allows
to avoid refactoring `parse_pathspec()`.

I considered adding `nul_term_line` to `flags` instead, but decided
that it doesn't fit there.

The new code is mostly taken from `cmd_update_index()` and
`split_mail_conv()`.

Co-authored-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 pathspec.c | 38 ++++++++++++++++++++++++++++++++++++++
 pathspec.h | 10 ++++++++++
 2 files changed, 48 insertions(+)

diff --git a/pathspec.c b/pathspec.c
index 12c2b322b3..128f27fcb7 100644
--- a/pathspec.c
+++ b/pathspec.c
@@ -3,6 +3,8 @@
 #include "dir.h"
 #include "pathspec.h"
 #include "attr.h"
+#include "argv-array.h"
+#include "quote.h"
 
 /*
  * Finds which of the given pathspecs match items in the index.
@@ -613,6 +615,42 @@ void parse_pathspec(struct pathspec *pathspec,
 	}
 }
 
+void parse_pathspec_file(struct pathspec *pathspec, unsigned magic_mask,
+			 unsigned flags, const char *prefix,
+			 const char *file, int nul_term_line)
+{
+	struct argv_array parsed_file = ARGV_ARRAY_INIT;
+	strbuf_getline_fn getline_fn = nul_term_line ? strbuf_getline_nul :
+						       strbuf_getline;
+	struct strbuf buf = STRBUF_INIT;
+	struct strbuf unquoted = STRBUF_INIT;
+	FILE *in;
+
+	if (!strcmp(file, "-"))
+		in = stdin;
+	else
+		in = xfopen(file, "r");
+
+	while (getline_fn(&buf, in) != EOF) {
+		if (!nul_term_line && buf.buf[0] == '"') {
+			strbuf_reset(&unquoted);
+			if (unquote_c_style(&unquoted, buf.buf, NULL))
+				die(_("line is badly quoted: %s"), buf.buf);
+			strbuf_swap(&buf, &unquoted);
+		}
+		argv_array_push(&parsed_file, buf.buf);
+		strbuf_reset(&buf);
+	}
+
+	strbuf_release(&unquoted);
+	strbuf_release(&buf);
+	if (in != stdin)
+		fclose(in);
+
+	parse_pathspec(pathspec, magic_mask, flags, prefix, parsed_file.argv);
+	argv_array_clear(&parsed_file);
+}
+
 void copy_pathspec(struct pathspec *dst, const struct pathspec *src)
 {
 	int i, j;
diff --git a/pathspec.h b/pathspec.h
index 1c18a2c90c..a27dc81ba2 100644
--- a/pathspec.h
+++ b/pathspec.h
@@ -85,6 +85,16 @@ void parse_pathspec(struct pathspec *pathspec,
 		    unsigned flags,
 		    const char *prefix,
 		    const char **args);
+/*
+ * Same as parse_pathspec() but uses file as input.
+ * When 'file' is exactly "-" it uses 'stdin' instead.
+ */
+void parse_pathspec_file(struct pathspec *pathspec,
+			 unsigned magic_mask,
+			 unsigned flags,
+			 const char *prefix,
+			 const char *file,
+			 int nul_term_line);
 void copy_pathspec(struct pathspec *dst, const struct pathspec *src);
 void clear_pathspec(struct pathspec *);
 
-- 
gitgitgadget


^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH v3 3/6] doc: reset: synchronize <pathspec> description
  2019-11-19 16:48   ` [PATCH v3 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget
  2019-11-19 16:48     ` [PATCH v3 1/6] parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul` Alexandr Miloslavskiy via GitGitGadget
  2019-11-19 16:48     ` [PATCH v3 2/6] pathspec: add new function to parse file Alexandr Miloslavskiy via GitGitGadget
@ 2019-11-19 16:48     ` Alexandr Miloslavskiy via GitGitGadget
  2019-11-19 16:48     ` [PATCH v3 4/6] reset: support the `--pathspec-from-file` option Alexandr Miloslavskiy via GitGitGadget
                       ` (4 subsequent siblings)
  7 siblings, 0 replies; 80+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-19 16:48 UTC (permalink / raw)
  To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy

From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>

`git add` shows an example of good writing, follow it.

Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 Documentation/git-reset.txt | 29 ++++++++++++++++++-----------
 builtin/reset.c             |  4 ++--
 2 files changed, 20 insertions(+), 13 deletions(-)

diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index 97e0544d9e..d517a43e73 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -8,8 +8,8 @@ git-reset - Reset current HEAD to the specified state
 SYNOPSIS
 --------
 [verse]
-'git reset' [-q] [<tree-ish>] [--] <paths>...
-'git reset' (--patch | -p) [<tree-ish>] [--] [<paths>...]
+'git reset' [-q] [<tree-ish>] [--] <pathspec>...
+'git reset' (--patch | -p) [<tree-ish>] [--] [<pathspec>...]
 'git reset' [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>]
 
 DESCRIPTION
@@ -19,23 +19,23 @@ In the third form, set the current branch head (`HEAD`) to `<commit>`,
 optionally modifying index and working tree to match.
 The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms.
 
-'git reset' [-q] [<tree-ish>] [--] <paths>...::
-	This form resets the index entries for all `<paths>` to their
-	state at `<tree-ish>`.  (It does not affect the working tree or
-	the current branch.)
+'git reset' [-q] [<tree-ish>] [--] <pathspec>...::
+	This form resets the index entries for all paths that match the
+	`<pathspec>` to their state at `<tree-ish>`.  (It does not affect
+	the working tree or the current branch.)
 +
-This means that `git reset <paths>` is the opposite of `git add
-<paths>`. This command is equivalent to
-`git restore [--source=<tree-ish>] --staged <paths>...`.
+This means that `git reset <pathspec>` is the opposite of `git add
+<pathspec>`. This command is equivalent to
+`git restore [--source=<tree-ish>] --staged <pathspec>...`.
 +
-After running `git reset <paths>` to update the index entry, you can
+After running `git reset <pathspec>` to update the index entry, you can
 use linkgit:git-restore[1] to check the contents out of the index to
 the working tree. Alternatively, using linkgit:git-restore[1]
 and specifying a commit with `--source`, you
 can copy the contents of a path out of a commit to the index and to the
 working tree in one go.
 
-'git reset' (--patch | -p) [<tree-ish>] [--] [<paths>...]::
+'git reset' (--patch | -p) [<tree-ish>] [--] [<pathspec>...]::
 	Interactively select hunks in the difference between the index
 	and `<tree-ish>` (defaults to `HEAD`).  The chosen hunks are applied
 	in reverse to the index.
@@ -101,6 +101,13 @@ OPTIONS
 	`reset.quiet` config option. `--quiet` and `--no-quiet` will
 	override the default behavior.
 
+\--::
+	Do not interpret any more arguments as options.
+
+<pathspec>...::
+	Limits the paths affected by the operation.
++
+For more details, see the 'pathspec' entry in linkgit:gitglossary[7].
 
 EXAMPLES
 --------
diff --git a/builtin/reset.c b/builtin/reset.c
index fdd572168b..9291c0fd72 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -30,8 +30,8 @@
 
 static const char * const git_reset_usage[] = {
 	N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"),
-	N_("git reset [-q] [<tree-ish>] [--] <paths>..."),
-	N_("git reset --patch [<tree-ish>] [--] [<paths>...]"),
+	N_("git reset [-q] [<tree-ish>] [--] <pathspec>..."),
+	N_("git reset --patch [<tree-ish>] [--] [<pathspec>...]"),
 	NULL
 };
 
-- 
gitgitgadget


^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH v3 4/6] reset: support the `--pathspec-from-file` option
  2019-11-19 16:48   ` [PATCH v3 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget
                       ` (2 preceding siblings ...)
  2019-11-19 16:48     ` [PATCH v3 3/6] doc: reset: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget
@ 2019-11-19 16:48     ` Alexandr Miloslavskiy via GitGitGadget
  2019-11-19 16:48     ` [PATCH v3 5/6] doc: commit: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget
                       ` (3 subsequent siblings)
  7 siblings, 0 replies; 80+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-19 16:48 UTC (permalink / raw)
  To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy

From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>

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 <johannes.schindelin@gmx.de>
Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 Documentation/git-reset.txt    |  21 ++++-
 builtin/reset.c                |  21 ++++-
 t/t7107-reset-pathspec-file.sh | 155 +++++++++++++++++++++++++++++++++
 3 files changed, 192 insertions(+), 5 deletions(-)
 create mode 100755 t/t7107-reset-pathspec-file.sh

diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index d517a43e73..932080c55d 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -9,18 +9,20 @@ SYNOPSIS
 --------
 [verse]
 'git reset' [-q] [<tree-ish>] [--] <pathspec>...
+'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-nul]] [<tree-ish>]
 'git reset' (--patch | -p) [<tree-ish>] [--] [<pathspec>...]
 'git reset' [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>]
 
 DESCRIPTION
 -----------
-In the first and second form, copy entries from `<tree-ish>` to the index.
-In the third form, set the current branch head (`HEAD`) to `<commit>`,
+In the first three forms, copy entries from `<tree-ish>` to the index.
+In the last form, set the current branch head (`HEAD`) to `<commit>`,
 optionally modifying index and working tree to match.
 The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms.
 
 'git reset' [-q] [<tree-ish>] [--] <pathspec>...::
-	This form resets the index entries for all paths that match the
+'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-nul]] [<tree-ish>]::
+	These forms reset the index entries for all paths that match the
 	`<pathspec>` to their state at `<tree-ish>`.  (It does not affect
 	the working tree or the current branch.)
 +
@@ -101,6 +103,19 @@ OPTIONS
 	`reset.quiet` config option. `--quiet` and `--no-quiet` will
 	override the default behavior.
 
+--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).
+
 \--::
 	Do not interpret any more arguments as options.
 
diff --git a/builtin/reset.c b/builtin/reset.c
index 9291c0fd72..246bf9d737 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -31,6 +31,7 @@
 static const char * const git_reset_usage[] = {
 	N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"),
 	N_("git reset [-q] [<tree-ish>] [--] <pathspec>..."),
+	N_("git reset [-q] [--pathspec-from-file [--pathspec-file-nul]] [<tree-ish>]"),
 	N_("git reset --patch [<tree-ish>] [--] [<pathspec>...]"),
 	NULL
 };
@@ -284,8 +285,8 @@ static int git_reset_config(const char *var, const char *value, void *cb)
 int cmd_reset(int argc, const char **argv, const char *prefix)
 {
 	int reset_type = NONE, update_ref_status = 0, quiet = 0;
-	int patch_mode = 0, unborn;
-	const char *rev;
+	int patch_mode = 0, pathspec_file_nul = 0, unborn;
+	const char *rev, *pathspec_from_file = NULL;
 	struct object_id oid;
 	struct pathspec pathspec;
 	int intent_to_add = 0;
@@ -306,6 +307,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 		OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")),
 		OPT_BOOL('N', "intent-to-add", &intent_to_add,
 				N_("record only the fact that removed paths will be added later")),
+		OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
+		OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
 		OPT_END()
 	};
 
@@ -316,6 +319,20 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 						PARSE_OPT_KEEP_DASHDASH);
 	parse_args(&pathspec, argv, prefix, patch_mode, &rev);
 
+	if (pathspec_from_file) {
+		if (patch_mode)
+			die(_("--pathspec-from-file is incompatible with --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"));
+	}
+
 	unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid);
 	if (unborn) {
 		/* reset on unborn branch: treat as reset to empty tree */
diff --git a/t/t7107-reset-pathspec-file.sh b/t/t7107-reset-pathspec-file.sh
new file mode 100755
index 0000000000..6b1a731fff
--- /dev/null
+++ b/t/t7107-reset-pathspec-file.sh
@@ -0,0 +1,155 @@
+#!/bin/sh
+
+test_description='reset --pathspec-from-file'
+
+. ./test-lib.sh
+
+test_tick
+
+test_expect_success setup '
+	echo A >fileA.t &&
+	echo B >fileB.t &&
+	echo C >fileC.t &&
+	echo D >fileD.t &&
+	git add . &&
+	git commit --include . -m "Commit" &&
+	git tag checkpoint
+'
+
+restore_checkpoint () {
+	git reset --hard checkpoint
+}
+
+verify_expect () {
+	git status --porcelain -- fileA.t fileB.t fileC.t fileD.t >actual &&
+	test_cmp expect actual
+}
+
+test_expect_success '--pathspec-from-file from stdin' '
+	restore_checkpoint &&
+
+	git rm fileA.t &&
+	echo fileA.t | git reset --pathspec-from-file=- &&
+
+	cat >expect <<-\EOF &&
+	 D fileA.t
+	EOF
+	verify_expect
+'
+
+test_expect_success '--pathspec-from-file from file' '
+	restore_checkpoint &&
+
+	git rm fileA.t &&
+	echo fileA.t >list &&
+	git reset --pathspec-from-file=list &&
+
+	cat >expect <<-\EOF &&
+	 D fileA.t
+	EOF
+	verify_expect
+'
+
+test_expect_success 'NUL delimiters' '
+	restore_checkpoint &&
+
+	git rm fileA.t fileB.t &&
+	printf "fileA.t\0fileB.t\0" | git reset --pathspec-from-file=- --pathspec-file-nul &&
+
+	cat >expect <<-\EOF &&
+	 D fileA.t
+	 D fileB.t
+	EOF
+	verify_expect
+'
+
+test_expect_success 'LF delimiters' '
+	restore_checkpoint &&
+
+	git rm fileA.t fileB.t &&
+	printf "fileA.t\nfileB.t\n" | git reset --pathspec-from-file=- &&
+
+	cat >expect <<-\EOF &&
+	 D fileA.t
+	 D fileB.t
+	EOF
+	verify_expect
+'
+
+test_expect_success 'no trailing delimiter' '
+	restore_checkpoint &&
+
+	git rm fileA.t fileB.t &&
+	printf "fileA.t\nfileB.t" | git reset --pathspec-from-file=- &&
+
+	cat >expect <<-\EOF &&
+	 D fileA.t
+	 D fileB.t
+	EOF
+	verify_expect
+'
+
+test_expect_success 'CRLF delimiters' '
+	restore_checkpoint &&
+
+	git rm fileA.t fileB.t &&
+	printf "fileA.t\r\nfileB.t\r\n" | git reset --pathspec-from-file=- &&
+
+	cat >expect <<-\EOF &&
+	 D fileA.t
+	 D fileB.t
+	EOF
+	verify_expect
+'
+
+test_expect_success 'quotes' '
+	restore_checkpoint &&
+
+	git rm fileA.t &&
+	printf "\"file\\101.t\"" | git reset --pathspec-from-file=- &&
+
+	cat >expect <<-\EOF &&
+	 D fileA.t
+	EOF
+	verify_expect
+'
+
+test_expect_success 'quotes not compatible with --pathspec-file-nul' '
+	restore_checkpoint &&
+
+	git rm fileA.t &&
+	printf "\"file\\101.t\"" >list &&
+	# Note: "git reset" has not yet learned to fail on wrong pathspecs
+	git reset --pathspec-from-file=list --pathspec-file-nul &&
+
+	cat >expect <<-\EOF &&
+	 D fileA.t
+	EOF
+	test_must_fail verify_expect
+'
+
+test_expect_success '--pathspec-from-file is not compatible with --soft or --hard' '
+	restore_checkpoint &&
+
+	git rm fileA.t &&
+	echo fileA.t >list &&
+	test_must_fail git reset --soft --pathspec-from-file=list &&
+	test_must_fail git reset --hard --pathspec-from-file=list
+'
+
+test_expect_success 'only touches what was listed' '
+	restore_checkpoint &&
+
+	git rm fileA.t fileB.t fileC.t fileD.t &&
+	printf "fileB.t\nfileC.t\n" | git reset --pathspec-from-file=- &&
+
+	cat >expect <<-\EOF &&
+	D  fileA.t
+	 D fileB.t
+	 D fileC.t
+	D  fileD.t
+	EOF
+	verify_expect
+'
+
+test_done
-- 
gitgitgadget


^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH v3 5/6] doc: commit: synchronize <pathspec> description
  2019-11-19 16:48   ` [PATCH v3 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget
                       ` (3 preceding siblings ...)
  2019-11-19 16:48     ` [PATCH v3 4/6] reset: support the `--pathspec-from-file` option Alexandr Miloslavskiy via GitGitGadget
@ 2019-11-19 16:48     ` Alexandr Miloslavskiy via GitGitGadget
  2019-11-19 16:48     ` [PATCH v3 6/6] commit: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
                       ` (2 subsequent siblings)
  7 siblings, 0 replies; 80+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-19 16:48 UTC (permalink / raw)
  To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy

From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>

`git add` shows an example of good writing, follow it.
This also better disambiguates <file>... header.

Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 Documentation/git-commit.txt | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index afa7b75a23..a0c44978ee 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -13,7 +13,7 @@ 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>]] [--] [<file>...]
+	   [-i | -o] [-S[<keyid>]] [--] [<pathspec>...]
 
 DESCRIPTION
 -----------
@@ -345,12 +345,13 @@ changes to tracked files.
 \--::
 	Do not interpret any more arguments as options.
 
-<file>...::
-	When files are given on the command line, the command
-	commits the contents of the named files, without
-	recording the changes already staged.  The contents of
-	these files are also staged for the next commit on top
-	of what have been staged before.
+<pathspec>...::
+	When pathspec is given on the command line, commit the contents of
+	the files that match the pathspec without recording the changes
+	already added to the index. The contents of these files are also
+	staged for the next commit on top of what have been staged before.
++
+For more details, see the 'pathspec' entry in linkgit:gitglossary[7].
 
 :git-commit: 1
 include::date-formats.txt[]
-- 
gitgitgadget


^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH v3 6/6] commit: support the --pathspec-from-file option
  2019-11-19 16:48   ` [PATCH v3 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget
                       ` (4 preceding siblings ...)
  2019-11-19 16:48     ` [PATCH v3 5/6] doc: commit: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget
@ 2019-11-19 16:48     ` Alexandr Miloslavskiy via GitGitGadget
  2019-11-20  4:04     ` [PATCH v3 0/6] Add --pathspec-from-file option for reset, commit Junio C Hamano
  2019-12-03 14:02     ` [PATCH v4 00/13] Add --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
  7 siblings, 0 replies; 80+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-19 16:48 UTC (permalink / raw)
  To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy

From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>

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>
---
 Documentation/git-commit.txt    |  16 +++-
 builtin/commit.c                |  25 +++++-
 t/t7526-commit-pathspec-file.sh | 130 ++++++++++++++++++++++++++++++++
 3 files changed, 166 insertions(+), 5 deletions(-)
 create mode 100755 t/t7526-commit-pathspec-file.sh

diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index a0c44978ee..ced5a9beab 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -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.
diff --git a/builtin/commit.c b/builtin/commit.c
index e588bc6ad3..ed40729355 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -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
index 0000000000..a06b683534
--- /dev/null
+++ b/t/t7526-commit-pathspec-file.sh
@@ -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
-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v2 2/6] pathspec: add new function to parse file
  2019-11-19  5:59     ` Junio C Hamano
@ 2019-11-19 16:50       ` Alexandr Miloslavskiy
  0 siblings, 0 replies; 80+ messages in thread
From: Alexandr Miloslavskiy @ 2019-11-19 16:50 UTC (permalink / raw)
  To: Junio C Hamano, Alexandr Miloslavskiy via GitGitGadget; +Cc: git

On 19.11.2019 6:59, Junio C Hamano wrote:
>> +	FILE *in = NULL;
> 
> Useless initialization to NULL can be dropped here.

Changed in V3.

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v2 3/6] doc: reset: unify <pathspec> description
  2019-11-19  6:05     ` Junio C Hamano
@ 2019-11-19 16:52       ` Alexandr Miloslavskiy
  0 siblings, 0 replies; 80+ messages in thread
From: Alexandr Miloslavskiy @ 2019-11-19 16:52 UTC (permalink / raw)
  To: Junio C Hamano, Alexandr Miloslavskiy via GitGitGadget; +Cc: git

On 19.11.2019 7:05, Junio C Hamano wrote:

>> -'git reset' [-q] [<tree-ish>] [--] <paths>...::
>> -	This form resets the index entries for all `<paths>` to their
>> +'git reset' [-q] [<tree-ish>] [--] <pathspec>...::
>> +	This form resets the index entries for all `<pathspec>` to their
> 
> This is not so good.  The original pretended as if <paths> are the
> exact pathnames, so it was sort-of OK for it to say "for all paths".
> Since we are highlighting the fact that these are not pathnames but
> the patterns to match pathnames, however, the description needs a
> slight update to match, perhaps like
> 
> 	ths form resets the index entries for all paths that match
> 	the `<pathspec>` to their...
> 
>>   	state at `<tree-ish>`.  (It does not affect the working tree or
>>   	the current branch.)

Yes, I wasn't paying enough attention.
Changed in V3, thanks!

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v2 5/6] doc: commit: unify <pathspec> description
  2019-11-19  6:16     ` Junio C Hamano
@ 2019-11-19 16:53       ` Alexandr Miloslavskiy
  2019-11-19 17:02       ` Alexandr Miloslavskiy
  1 sibling, 0 replies; 80+ messages in thread
From: Alexandr Miloslavskiy @ 2019-11-19 16:53 UTC (permalink / raw)
  To: Junio C Hamano, Alexandr Miloslavskiy via GitGitGadget; +Cc: git

On 19.11.2019 7:16, Junio C Hamano wrote:
>> +For more details about the <pathspec> syntax, see the 'pathspec' entry
>> +in linkgit:gitglossary[7].
> 
> What you added in [PATCH 3/6] (git reset doc) sends a slightly
> different message, i.e.
> 
>      The <pathspec> is used to limit the paths affected by the operation
>      (see the entry for 'pathspec' in linkgit:gitglossary[7] for more details).
> 
> and I think that was more appropriate than what we see here.  You
> are referring your readers to the glossary entry not just for the
> syntax but also the entire concept of <pathspec>.
> 
> IOW, it would be needed to drop "syntax" from "about the <pathspec>
> syntax" from here to match the update to "git reset" documentation.

I shortened it even more in V3, I think it's better now?

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v2 6/6] commit: support the --pathspec-from-file option
  2019-11-19  6:10     ` Junio C Hamano
@ 2019-11-19 16:56       ` Alexandr Miloslavskiy
  0 siblings, 0 replies; 80+ messages in thread
From: Alexandr Miloslavskiy @ 2019-11-19 16:56 UTC (permalink / raw)
  To: Junio C Hamano, Alexandr Miloslavskiy via GitGitGadget; +Cc: git

On 19.11.2019 7:10, Junio C Hamano wrote:
>> +--pathspec-file-nul::
>> +	Only meaningful with `--pathspec-from-file`. Pathspec elements are
>> +	separated with NUL character and are not expected to be quoted.
> 
> Although it is not incorrect, "are not expected to be quoted" feels
> a bit weak as the technical description.  Are they not expected to
> be quoted, but the command gracefully works on them even when they
> are found to be quoted?
> 
> Rephrasing to avoid such misinterpretation may be worth doing,
> perhaps
> 
> 	... and are taken as-is without unquoting (i.e. as if
> 	`core.quotePath` is set to `false`).
> 
> or something like that?

I think that a reference to `core.quotePath` could be confusing here, 
because it doesn't really affect the option. I have reworded it in V3, 
please see if you like it.

Thanks for pointing out!

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v2 5/6] doc: commit: unify <pathspec> description
  2019-11-19  6:16     ` Junio C Hamano
  2019-11-19 16:53       ` Alexandr Miloslavskiy
@ 2019-11-19 17:02       ` Alexandr Miloslavskiy
  1 sibling, 0 replies; 80+ messages in thread
From: Alexandr Miloslavskiy @ 2019-11-19 17:02 UTC (permalink / raw)
  To: Junio C Hamano, Alexandr Miloslavskiy via GitGitGadget; +Cc: git

On 19.11.2019 7:16, Junio C Hamano wrote:

>> +For more details about the <pathspec> syntax, see the 'pathspec' entry
>> +in linkgit:gitglossary[7].
> 
> What you added in [PATCH 3/6] (git reset doc) sends a slightly
> different message, i.e.
> 
>      The <pathspec> is used to limit the paths affected by the operation
>      (see the entry for 'pathspec' in linkgit:gitglossary[7] for more details).
> 
> and I think that was more appropriate than what we see here.  You
> are referring your readers to the glossary entry not just for the
> syntax but also the entire concept of <pathspec>.

This has shown me that I didn't synchronize docs enough. I have studied 
docs for other commands and found out that most of them list <pathspec> 
in a separate paragraph under options.

I find it very reasonable, because that's where I would normally expect 
it as a reader, together with other options.

So I adjusted 'git-reset' docs to also list <pathspec> under options.


^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v3 0/6] Add --pathspec-from-file option for reset, commit
  2019-11-19 16:48   ` [PATCH v3 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget
                       ` (5 preceding siblings ...)
  2019-11-19 16:48     ` [PATCH v3 6/6] commit: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
@ 2019-11-20  4:04     ` Junio C Hamano
  2019-11-20  9:22       ` Alexandr Miloslavskiy
  2019-12-03 14:02     ` [PATCH v4 00/13] Add --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
  7 siblings, 1 reply; 80+ messages in thread
From: Junio C Hamano @ 2019-11-20  4:04 UTC (permalink / raw)
  To: Alexandr Miloslavskiy via GitGitGadget; +Cc: git, Alexandr Miloslavskiy

"Alexandr Miloslavskiy via GitGitGadget" <gitgitgadget@gmail.com>
writes:

> Changes from V2:
>
> 1) Some polishing in documentation 2) = NULL removed from 
> parse_pathspec_file()
>
> Alexandr Miloslavskiy (6):
>   parse-options.h: add new options `--pathspec-from-file`,
>     `--pathspec-file-nul`
>   pathspec: add new function to parse file
>   doc: reset: synchronize <pathspec> description
>   reset: support the `--pathspec-from-file` option
>   doc: commit: synchronize <pathspec> description
>   commit: support the --pathspec-from-file option

Nicely done.  I think this is now ready for 'next'.

Thanks.

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v3 0/6] Add --pathspec-from-file option for reset, commit
  2019-11-20  4:04     ` [PATCH v3 0/6] Add --pathspec-from-file option for reset, commit Junio C Hamano
@ 2019-11-20  9:22       ` Alexandr Miloslavskiy
  0 siblings, 0 replies; 80+ messages in thread
From: Alexandr Miloslavskiy @ 2019-11-20  9:22 UTC (permalink / raw)
  To: Junio C Hamano, Alexandr Miloslavskiy via GitGitGadget; +Cc: git

On 20.11.2019 5:04, Junio C Hamano wrote:
> Nicely done.  I think this is now ready for 'next'.

Wonderful, thanks for your assistance!

I will continue with patches for other commands, then.

^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH v4 00/13] Add --pathspec-from-file option
  2019-11-19 16:48   ` [PATCH v3 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget
                       ` (6 preceding siblings ...)
  2019-11-20  4:04     ` [PATCH v3 0/6] Add --pathspec-from-file option for reset, commit Junio C Hamano
@ 2019-12-03 14:02     ` Alexandr Miloslavskiy via GitGitGadget
  2019-12-03 14:02       ` [PATCH v4 01/13] parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul` Alexandr Miloslavskiy via GitGitGadget
                         ` (13 more replies)
  7 siblings, 14 replies; 80+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 UTC (permalink / raw)
  To: git; +Cc: Junio C Hamano, Alexandr Miloslavskiy, Junio C Hamano

Changes from V3:
================

The branch was rebased onto latest master. These patches remain unchanged
since they were accepted in V3: parse-options.h: add new options 
--pathspec-from-file, --pathspec-file-nulpathspec: add new function to parse
file doc: reset: synchronize description reset: support the 
--pathspec-from-file option doc: commit: synchronize description commit:
support the --pathspec-from-file option These patches are new, extending
support to more git commands: cmd_add: prepare for next patch add: support
the --pathspec-from-file option doc: checkout: remove duplicate synopsis
doc: checkout: fix broken text reference doc: checkout: synchronize 
description doc: restore: synchronize description checkout, restore: support
the --pathspec-from-file option

Cc: Phillip Wood phillip.wood123@gmail.com [phillip.wood123@gmail.com]

Alexandr Miloslavskiy (13):
  parse-options.h: add new options `--pathspec-from-file`,
    `--pathspec-file-nul`
  pathspec: add new function to parse file
  doc: reset: synchronize <pathspec> description
  reset: support the `--pathspec-from-file` option
  doc: commit: synchronize <pathspec> description
  commit: support the --pathspec-from-file option
  cmd_add: prepare for next patch
  add: support the --pathspec-from-file option
  doc: checkout: remove duplicate synopsis
  doc: checkout: fix broken text reference
  doc: checkout: synchronize <pathspec> description
  doc: restore: synchronize <pathspec> description
  checkout, restore: support the --pathspec-from-file option

 Documentation/git-add.txt         |  16 ++-
 Documentation/git-checkout.txt    |  50 +++++++---
 Documentation/git-commit.txt      |  29 ++++--
 Documentation/git-reset.txt       |  48 ++++++---
 Documentation/git-restore.txt     |  26 ++++-
 builtin/add.c                     |  60 ++++++++----
 builtin/checkout.c                |  31 +++++-
 builtin/commit.c                  |  25 ++++-
 builtin/reset.c                   |  25 ++++-
 parse-options.h                   |   2 +
 pathspec.c                        |  38 ++++++++
 pathspec.h                        |  10 ++
 t/t2026-checkout-pathspec-file.sh | 139 +++++++++++++++++++++++++++
 t/t2072-restore-pathspec-file.sh  | 139 +++++++++++++++++++++++++++
 t/t3704-add-pathspec-file.sh      | 127 ++++++++++++++++++++++++
 t/t7107-reset-pathspec-file.sh    | 155 ++++++++++++++++++++++++++++++
 t/t7526-commit-pathspec-file.sh   | 130 +++++++++++++++++++++++++
 t/t9902-completion.sh             |   2 +
 18 files changed, 982 insertions(+), 70 deletions(-)
 create mode 100755 t/t2026-checkout-pathspec-file.sh
 create mode 100755 t/t2072-restore-pathspec-file.sh
 create mode 100755 t/t3704-add-pathspec-file.sh
 create mode 100755 t/t7107-reset-pathspec-file.sh
 create mode 100755 t/t7526-commit-pathspec-file.sh


base-commit: 228f53135a4a41a37b6be8e4d6e2b6153db4a8ed
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-445%2FSyntevoAlex%2F%230207_pathspec_from_file-v4
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-445/SyntevoAlex/#0207_pathspec_from_file-v4
Pull-Request: https://github.com/gitgitgadget/git/pull/445

Range-diff vs v3:

  1:  19b80326ea =  1:  cca5aee392 parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul`
  2:  55a7c6ec3c =  2:  fea64dfbf9 pathspec: add new function to parse file
  3:  d9f32e523c =  3:  1182ba3953 doc: reset: synchronize <pathspec> description
  4:  8a10ff881b =  4:  cea470fc91 reset: support the `--pathspec-from-file` option
  5:  0b79797e77 =  5:  0e1ac7e8a7 doc: commit: synchronize <pathspec> description
  6:  7e48212002 =  6:  c877866c13 commit: support the --pathspec-from-file option
  -:  ---------- >  7:  a97910cb55 cmd_add: prepare for next patch
  -:  ---------- >  8:  9a62da3470 add: support the --pathspec-from-file option
  -:  ---------- >  9:  5e449c8d29 doc: checkout: remove duplicate synopsis
  -:  ---------- > 10:  a498dda97b doc: checkout: fix broken text reference
  -:  ---------- > 11:  fd166755aa doc: checkout: synchronize <pathspec> description
  -:  ---------- > 12:  9e37a740e6 doc: restore: synchronize <pathspec> description
  -:  ---------- > 13:  c4dd4eaf13 checkout, restore: support the --pathspec-from-file option

-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH v4 01/13] parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul`
  2019-12-03 14:02     ` [PATCH v4 00/13] Add --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
@ 2019-12-03 14:02       ` Alexandr Miloslavskiy via GitGitGadget
  2019-12-03 14:02       ` [PATCH v4 02/13] pathspec: add new function to parse file Alexandr Miloslavskiy via GitGitGadget
                         ` (12 subsequent siblings)
  13 siblings, 0 replies; 80+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Alexandr Miloslavskiy, Junio C Hamano,
	Alexandr Miloslavskiy

From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>

Support for various porcelain commands will arrive via additional
patches.

`--pathspec-from-file` solves the problem of commandline length limit
for UIs built on top of git. Plumbing commands are not always a good
fit, for two major reasons:
1) Some UIs show executed commands to user. In this case, porcelain
   commands are expected. One reason for that is letting user learn git
   commands by clicking UI buttons. The other reason is letting user
   study the history of commands in case of any unexpected results. Both
   of these will lose most of their value if UI uses combinations of
   arcane plumbing commands.
2) Some UIs have started and grown with porcelain commands. Replacing
   existing logic with plumbing commands could be cumbersome and prone
   to various new problems.

`--pathspec-from-file` will behave very close to pathspec passed in
commandline args, so that switching from one to another is simple.

`--pathspec-from-file` will read either a specified file or `stdin`
(when file is exactly "-"). Reading from file is a good way to avoid
competing for `stdin`, and also gives some extra flexibility.

`--pathspec-file-nul` switch mirrors `-z` already used in various
places. Some porcelain commands, such as `git commit`, already use
`-z`, therefore it needed a new unambiguous name.

New options do not have shorthands to avoid shorthand conflicts. It is
not expected that they will be typed in console.

Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 parse-options.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/parse-options.h b/parse-options.h
index 38a33a087e..c6cc01e715 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -330,5 +330,7 @@ int parse_opt_passthru_argv(const struct option *, const char *, int);
 #define OPT_WITH(v, h) _OPT_CONTAINS_OR_WITH("with", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG)
 #define OPT_WITHOUT(v, h) _OPT_CONTAINS_OR_WITH("without", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG)
 #define OPT_CLEANUP(v) OPT_STRING(0, "cleanup", v, N_("mode"), N_("how to strip spaces and #comments from message"))
+#define OPT_PATHSPEC_FROM_FILE(v) OPT_FILENAME(0, "pathspec-from-file", v, N_("read pathspec from file"))
+#define OPT_PATHSPEC_FILE_NUL(v)  OPT_BOOL(0, "pathspec-file-nul", v, N_("with --pathspec-from-file, pathspec elements are separated with NUL character"))
 
 #endif
-- 
gitgitgadget


^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH v4 02/13] pathspec: add new function to parse file
  2019-12-03 14:02     ` [PATCH v4 00/13] Add --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
  2019-12-03 14:02       ` [PATCH v4 01/13] parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul` Alexandr Miloslavskiy via GitGitGadget
@ 2019-12-03 14:02       ` Alexandr Miloslavskiy via GitGitGadget
  2019-12-03 14:02       ` [PATCH v4 03/13] doc: reset: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget
                         ` (11 subsequent siblings)
  13 siblings, 0 replies; 80+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Alexandr Miloslavskiy, Junio C Hamano,
	Alexandr Miloslavskiy

From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>

This will be used to support the new option '--pathspec-from-file' in
`git add`, `git-commit`, `git reset` etc.

Note also that we specifically handle CR/LF line endings to support
Windows better.

To simplify code, file is first parsed into `argv_array`. This allows
to avoid refactoring `parse_pathspec()`.

I considered adding `nul_term_line` to `flags` instead, but decided
that it doesn't fit there.

The new code is mostly taken from `cmd_update_index()` and
`split_mail_conv()`.

Co-authored-by: Johannes Schindelin <johannes.schindelin@gmx.de>
Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 pathspec.c | 38 ++++++++++++++++++++++++++++++++++++++
 pathspec.h | 10 ++++++++++
 2 files changed, 48 insertions(+)

diff --git a/pathspec.c b/pathspec.c
index 12c2b322b3..128f27fcb7 100644
--- a/pathspec.c
+++ b/pathspec.c
@@ -3,6 +3,8 @@
 #include "dir.h"
 #include "pathspec.h"
 #include "attr.h"
+#include "argv-array.h"
+#include "quote.h"
 
 /*
  * Finds which of the given pathspecs match items in the index.
@@ -613,6 +615,42 @@ void parse_pathspec(struct pathspec *pathspec,
 	}
 }
 
+void parse_pathspec_file(struct pathspec *pathspec, unsigned magic_mask,
+			 unsigned flags, const char *prefix,
+			 const char *file, int nul_term_line)
+{
+	struct argv_array parsed_file = ARGV_ARRAY_INIT;
+	strbuf_getline_fn getline_fn = nul_term_line ? strbuf_getline_nul :
+						       strbuf_getline;
+	struct strbuf buf = STRBUF_INIT;
+	struct strbuf unquoted = STRBUF_INIT;
+	FILE *in;
+
+	if (!strcmp(file, "-"))
+		in = stdin;
+	else
+		in = xfopen(file, "r");
+
+	while (getline_fn(&buf, in) != EOF) {
+		if (!nul_term_line && buf.buf[0] == '"') {
+			strbuf_reset(&unquoted);
+			if (unquote_c_style(&unquoted, buf.buf, NULL))
+				die(_("line is badly quoted: %s"), buf.buf);
+			strbuf_swap(&buf, &unquoted);
+		}
+		argv_array_push(&parsed_file, buf.buf);
+		strbuf_reset(&buf);
+	}
+
+	strbuf_release(&unquoted);
+	strbuf_release(&buf);
+	if (in != stdin)
+		fclose(in);
+
+	parse_pathspec(pathspec, magic_mask, flags, prefix, parsed_file.argv);
+	argv_array_clear(&parsed_file);
+}
+
 void copy_pathspec(struct pathspec *dst, const struct pathspec *src)
 {
 	int i, j;
diff --git a/pathspec.h b/pathspec.h
index 1c18a2c90c..a27dc81ba2 100644
--- a/pathspec.h
+++ b/pathspec.h
@@ -85,6 +85,16 @@ void parse_pathspec(struct pathspec *pathspec,
 		    unsigned flags,
 		    const char *prefix,
 		    const char **args);
+/*
+ * Same as parse_pathspec() but uses file as input.
+ * When 'file' is exactly "-" it uses 'stdin' instead.
+ */
+void parse_pathspec_file(struct pathspec *pathspec,
+			 unsigned magic_mask,
+			 unsigned flags,
+			 const char *prefix,
+			 const char *file,
+			 int nul_term_line);
 void copy_pathspec(struct pathspec *dst, const struct pathspec *src);
 void clear_pathspec(struct pathspec *);
 
-- 
gitgitgadget


^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH v4 03/13] doc: reset: synchronize <pathspec> description
  2019-12-03 14:02     ` [PATCH v4 00/13] Add --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
  2019-12-03 14:02       ` [PATCH v4 01/13] parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul` Alexandr Miloslavskiy via GitGitGadget
  2019-12-03 14:02       ` [PATCH v4 02/13] pathspec: add new function to parse file Alexandr Miloslavskiy via GitGitGadget
@ 2019-12-03 14:02       ` Alexandr Miloslavskiy via GitGitGadget
  2019-12-03 14:02       ` [PATCH v4 04/13] reset: support the `--pathspec-from-file` option Alexandr Miloslavskiy via GitGitGadget
                         ` (10 subsequent siblings)
  13 siblings, 0 replies; 80+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Alexandr Miloslavskiy, Junio C Hamano,
	Alexandr Miloslavskiy

From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>

`git add` shows an example of good writing, follow it.

Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 Documentation/git-reset.txt | 29 ++++++++++++++++++-----------
 builtin/reset.c             |  4 ++--
 2 files changed, 20 insertions(+), 13 deletions(-)

diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index 97e0544d9e..d517a43e73 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -8,8 +8,8 @@ git-reset - Reset current HEAD to the specified state
 SYNOPSIS
 --------
 [verse]
-'git reset' [-q] [<tree-ish>] [--] <paths>...
-'git reset' (--patch | -p) [<tree-ish>] [--] [<paths>...]
+'git reset' [-q] [<tree-ish>] [--] <pathspec>...
+'git reset' (--patch | -p) [<tree-ish>] [--] [<pathspec>...]
 'git reset' [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>]
 
 DESCRIPTION
@@ -19,23 +19,23 @@ In the third form, set the current branch head (`HEAD`) to `<commit>`,
 optionally modifying index and working tree to match.
 The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms.
 
-'git reset' [-q] [<tree-ish>] [--] <paths>...::
-	This form resets the index entries for all `<paths>` to their
-	state at `<tree-ish>`.  (It does not affect the working tree or
-	the current branch.)
+'git reset' [-q] [<tree-ish>] [--] <pathspec>...::
+	This form resets the index entries for all paths that match the
+	`<pathspec>` to their state at `<tree-ish>`.  (It does not affect
+	the working tree or the current branch.)
 +
-This means that `git reset <paths>` is the opposite of `git add
-<paths>`. This command is equivalent to
-`git restore [--source=<tree-ish>] --staged <paths>...`.
+This means that `git reset <pathspec>` is the opposite of `git add
+<pathspec>`. This command is equivalent to
+`git restore [--source=<tree-ish>] --staged <pathspec>...`.
 +
-After running `git reset <paths>` to update the index entry, you can
+After running `git reset <pathspec>` to update the index entry, you can
 use linkgit:git-restore[1] to check the contents out of the index to
 the working tree. Alternatively, using linkgit:git-restore[1]
 and specifying a commit with `--source`, you
 can copy the contents of a path out of a commit to the index and to the
 working tree in one go.
 
-'git reset' (--patch | -p) [<tree-ish>] [--] [<paths>...]::
+'git reset' (--patch | -p) [<tree-ish>] [--] [<pathspec>...]::
 	Interactively select hunks in the difference between the index
 	and `<tree-ish>` (defaults to `HEAD`).  The chosen hunks are applied
 	in reverse to the index.
@@ -101,6 +101,13 @@ OPTIONS
 	`reset.quiet` config option. `--quiet` and `--no-quiet` will
 	override the default behavior.
 
+\--::
+	Do not interpret any more arguments as options.
+
+<pathspec>...::
+	Limits the paths affected by the operation.
++
+For more details, see the 'pathspec' entry in linkgit:gitglossary[7].
 
 EXAMPLES
 --------
diff --git a/builtin/reset.c b/builtin/reset.c
index fdd572168b..9291c0fd72 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -30,8 +30,8 @@
 
 static const char * const git_reset_usage[] = {
 	N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"),
-	N_("git reset [-q] [<tree-ish>] [--] <paths>..."),
-	N_("git reset --patch [<tree-ish>] [--] [<paths>...]"),
+	N_("git reset [-q] [<tree-ish>] [--] <pathspec>..."),
+	N_("git reset --patch [<tree-ish>] [--] [<pathspec>...]"),
 	NULL
 };
 
-- 
gitgitgadget


^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH v4 04/13] reset: support the `--pathspec-from-file` option
  2019-12-03 14:02     ` [PATCH v4 00/13] Add --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
                         ` (2 preceding siblings ...)
  2019-12-03 14:02       ` [PATCH v4 03/13] doc: reset: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget
@ 2019-12-03 14:02       ` Alexandr Miloslavskiy via GitGitGadget
  2019-12-03 14:02       ` [PATCH v4 05/13] doc: commit: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget
                         ` (9 subsequent siblings)
  13 siblings, 0 replies; 80+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Alexandr Miloslavskiy, Junio C Hamano,
	Alexandr Miloslavskiy

From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>

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 <johannes.schindelin@gmx.de>
Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 Documentation/git-reset.txt    |  21 ++++-
 builtin/reset.c                |  21 ++++-
 t/t7107-reset-pathspec-file.sh | 155 +++++++++++++++++++++++++++++++++
 3 files changed, 192 insertions(+), 5 deletions(-)
 create mode 100755 t/t7107-reset-pathspec-file.sh

diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt
index d517a43e73..932080c55d 100644
--- a/Documentation/git-reset.txt
+++ b/Documentation/git-reset.txt
@@ -9,18 +9,20 @@ SYNOPSIS
 --------
 [verse]
 'git reset' [-q] [<tree-ish>] [--] <pathspec>...
+'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-nul]] [<tree-ish>]
 'git reset' (--patch | -p) [<tree-ish>] [--] [<pathspec>...]
 'git reset' [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>]
 
 DESCRIPTION
 -----------
-In the first and second form, copy entries from `<tree-ish>` to the index.
-In the third form, set the current branch head (`HEAD`) to `<commit>`,
+In the first three forms, copy entries from `<tree-ish>` to the index.
+In the last form, set the current branch head (`HEAD`) to `<commit>`,
 optionally modifying index and working tree to match.
 The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms.
 
 'git reset' [-q] [<tree-ish>] [--] <pathspec>...::
-	This form resets the index entries for all paths that match the
+'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-nul]] [<tree-ish>]::
+	These forms reset the index entries for all paths that match the
 	`<pathspec>` to their state at `<tree-ish>`.  (It does not affect
 	the working tree or the current branch.)
 +
@@ -101,6 +103,19 @@ OPTIONS
 	`reset.quiet` config option. `--quiet` and `--no-quiet` will
 	override the default behavior.
 
+--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).
+
 \--::
 	Do not interpret any more arguments as options.
 
diff --git a/builtin/reset.c b/builtin/reset.c
index 9291c0fd72..246bf9d737 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -31,6 +31,7 @@
 static const char * const git_reset_usage[] = {
 	N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"),
 	N_("git reset [-q] [<tree-ish>] [--] <pathspec>..."),
+	N_("git reset [-q] [--pathspec-from-file [--pathspec-file-nul]] [<tree-ish>]"),
 	N_("git reset --patch [<tree-ish>] [--] [<pathspec>...]"),
 	NULL
 };
@@ -284,8 +285,8 @@ static int git_reset_config(const char *var, const char *value, void *cb)
 int cmd_reset(int argc, const char **argv, const char *prefix)
 {
 	int reset_type = NONE, update_ref_status = 0, quiet = 0;
-	int patch_mode = 0, unborn;
-	const char *rev;
+	int patch_mode = 0, pathspec_file_nul = 0, unborn;
+	const char *rev, *pathspec_from_file = NULL;
 	struct object_id oid;
 	struct pathspec pathspec;
 	int intent_to_add = 0;
@@ -306,6 +307,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 		OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")),
 		OPT_BOOL('N', "intent-to-add", &intent_to_add,
 				N_("record only the fact that removed paths will be added later")),
+		OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
+		OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
 		OPT_END()
 	};
 
@@ -316,6 +319,20 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
 						PARSE_OPT_KEEP_DASHDASH);
 	parse_args(&pathspec, argv, prefix, patch_mode, &rev);
 
+	if (pathspec_from_file) {
+		if (patch_mode)
+			die(_("--pathspec-from-file is incompatible with --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"));
+	}
+
 	unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid);
 	if (unborn) {
 		/* reset on unborn branch: treat as reset to empty tree */
diff --git a/t/t7107-reset-pathspec-file.sh b/t/t7107-reset-pathspec-file.sh
new file mode 100755
index 0000000000..6b1a731fff
--- /dev/null
+++ b/t/t7107-reset-pathspec-file.sh
@@ -0,0 +1,155 @@
+#!/bin/sh
+
+test_description='reset --pathspec-from-file'
+
+. ./test-lib.sh
+
+test_tick
+
+test_expect_success setup '
+	echo A >fileA.t &&
+	echo B >fileB.t &&
+	echo C >fileC.t &&
+	echo D >fileD.t &&
+	git add . &&
+	git commit --include . -m "Commit" &&
+	git tag checkpoint
+'
+
+restore_checkpoint () {
+	git reset --hard checkpoint
+}
+
+verify_expect () {
+	git status --porcelain -- fileA.t fileB.t fileC.t fileD.t >actual &&
+	test_cmp expect actual
+}
+
+test_expect_success '--pathspec-from-file from stdin' '
+	restore_checkpoint &&
+
+	git rm fileA.t &&
+	echo fileA.t | git reset --pathspec-from-file=- &&
+
+	cat >expect <<-\EOF &&
+	 D fileA.t
+	EOF
+	verify_expect
+'
+
+test_expect_success '--pathspec-from-file from file' '
+	restore_checkpoint &&
+
+	git rm fileA.t &&
+	echo fileA.t >list &&
+	git reset --pathspec-from-file=list &&
+
+	cat >expect <<-\EOF &&
+	 D fileA.t
+	EOF
+	verify_expect
+'
+
+test_expect_success 'NUL delimiters' '
+	restore_checkpoint &&
+
+	git rm fileA.t fileB.t &&
+	printf "fileA.t\0fileB.t\0" | git reset --pathspec-from-file=- --pathspec-file-nul &&
+
+	cat >expect <<-\EOF &&
+	 D fileA.t
+	 D fileB.t
+	EOF
+	verify_expect
+'
+
+test_expect_success 'LF delimiters' '
+	restore_checkpoint &&
+
+	git rm fileA.t fileB.t &&
+	printf "fileA.t\nfileB.t\n" | git reset --pathspec-from-file=- &&
+
+	cat >expect <<-\EOF &&
+	 D fileA.t
+	 D fileB.t
+	EOF
+	verify_expect
+'
+
+test_expect_success 'no trailing delimiter' '
+	restore_checkpoint &&
+
+	git rm fileA.t fileB.t &&
+	printf "fileA.t\nfileB.t" | git reset --pathspec-from-file=- &&
+
+	cat >expect <<-\EOF &&
+	 D fileA.t
+	 D fileB.t
+	EOF
+	verify_expect
+'
+
+test_expect_success 'CRLF delimiters' '
+	restore_checkpoint &&
+
+	git rm fileA.t fileB.t &&
+	printf "fileA.t\r\nfileB.t\r\n" | git reset --pathspec-from-file=- &&
+
+	cat >expect <<-\EOF &&
+	 D fileA.t
+	 D fileB.t
+	EOF
+	verify_expect
+'
+
+test_expect_success 'quotes' '
+	restore_checkpoint &&
+
+	git rm fileA.t &&
+	printf "\"file\\101.t\"" | git reset --pathspec-from-file=- &&
+
+	cat >expect <<-\EOF &&
+	 D fileA.t
+	EOF
+	verify_expect
+'
+
+test_expect_success 'quotes not compatible with --pathspec-file-nul' '
+	restore_checkpoint &&
+
+	git rm fileA.t &&
+	printf "\"file\\101.t\"" >list &&
+	# Note: "git reset" has not yet learned to fail on wrong pathspecs
+	git reset --pathspec-from-file=list --pathspec-file-nul &&
+
+	cat >expect <<-\EOF &&
+	 D fileA.t
+	EOF
+	test_must_fail verify_expect
+'
+
+test_expect_success '--pathspec-from-file is not compatible with --soft or --hard' '
+	restore_checkpoint &&
+
+	git rm fileA.t &&
+	echo fileA.t >list &&
+	test_must_fail git reset --soft --pathspec-from-file=list &&
+	test_must_fail git reset --hard --pathspec-from-file=list
+'
+
+test_expect_success 'only touches what was listed' '
+	restore_checkpoint &&
+
+	git rm fileA.t fileB.t fileC.t fileD.t &&
+	printf "fileB.t\nfileC.t\n" | git reset --pathspec-from-file=- &&
+
+	cat >expect <<-\EOF &&
+	D  fileA.t
+	 D fileB.t
+	 D fileC.t
+	D  fileD.t
+	EOF
+	verify_expect
+'
+
+test_done
-- 
gitgitgadget


^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH v4 05/13] doc: commit: synchronize <pathspec> description
  2019-12-03 14:02     ` [PATCH v4 00/13] Add --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
                         ` (3 preceding siblings ...)
  2019-12-03 14:02       ` [PATCH v4 04/13] reset: support the `--pathspec-from-file` option Alexandr Miloslavskiy via GitGitGadget
@ 2019-12-03 14:02       ` Alexandr Miloslavskiy via GitGitGadget
  2019-12-03 14:02       ` [PATCH v4 06/13] commit: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
                         ` (8 subsequent siblings)
  13 siblings, 0 replies; 80+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Alexandr Miloslavskiy, Junio C Hamano,
	Alexandr Miloslavskiy

From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>

`git add` shows an example of good writing, follow it.
This also better disambiguates <file>... header.

Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 Documentation/git-commit.txt | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index afa7b75a23..a0c44978ee 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -13,7 +13,7 @@ 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>]] [--] [<file>...]
+	   [-i | -o] [-S[<keyid>]] [--] [<pathspec>...]
 
 DESCRIPTION
 -----------
@@ -345,12 +345,13 @@ changes to tracked files.
 \--::
 	Do not interpret any more arguments as options.
 
-<file>...::
-	When files are given on the command line, the command
-	commits the contents of the named files, without
-	recording the changes already staged.  The contents of
-	these files are also staged for the next commit on top
-	of what have been staged before.
+<pathspec>...::
+	When pathspec is given on the command line, commit the contents of
+	the files that match the pathspec without recording the changes
+	already added to the index. The contents of these files are also
+	staged for the next commit on top of what have been staged before.
++
+For more details, see the 'pathspec' entry in linkgit:gitglossary[7].
 
 :git-commit: 1
 include::date-formats.txt[]
-- 
gitgitgadget


^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH v4 06/13] commit: support the --pathspec-from-file option
  2019-12-03 14:02     ` [PATCH v4 00/13] Add --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
                         ` (4 preceding siblings ...)
  2019-12-03 14:02       ` [PATCH v4 05/13] doc: commit: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget
@ 2019-12-03 14:02       ` Alexandr Miloslavskiy via GitGitGadget
  2019-12-03 14:02       ` [PATCH v4 07/13] cmd_add: prepare for next patch Alexandr Miloslavskiy via GitGitGadget
                         ` (7 subsequent siblings)
  13 siblings, 0 replies; 80+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Alexandr Miloslavskiy, Junio C Hamano,
	Alexandr Miloslavskiy

From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>

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>
---
 Documentation/git-commit.txt    |  16 +++-
 builtin/commit.c                |  25 +++++-
 t/t7526-commit-pathspec-file.sh | 130 ++++++++++++++++++++++++++++++++
 3 files changed, 166 insertions(+), 5 deletions(-)
 create mode 100755 t/t7526-commit-pathspec-file.sh

diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt
index a0c44978ee..ced5a9beab 100644
--- a/Documentation/git-commit.txt
+++ b/Documentation/git-commit.txt
@@ -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.
diff --git a/builtin/commit.c b/builtin/commit.c
index 294dc574cd..2db2ad0de4 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -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);
@@ -1513,6 +1528,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
index 0000000000..a06b683534
--- /dev/null
+++ b/t/t7526-commit-pathspec-file.sh
@@ -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
-- 
gitgitgadget


^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH v4 07/13] cmd_add: prepare for next patch
  2019-12-03 14:02     ` [PATCH v4 00/13] Add --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
                         ` (5 preceding siblings ...)
  2019-12-03 14:02       ` [PATCH v4 06/13] commit: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
@ 2019-12-03 14:02       ` Alexandr Miloslavskiy via GitGitGadget
  2019-12-03 14:02       ` [PATCH v4 08/13] add: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
                         ` (6 subsequent siblings)
  13 siblings, 0 replies; 80+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Alexandr Miloslavskiy, Junio C Hamano,
	Alexandr Miloslavskiy

From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>

Some code blocks were moved down to be able to test for `pathspec.nr`
in the next patch. Blocks are moved as is without any changes. This
is done as separate patch to reduce the amount of diffs in next patch.

Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 builtin/add.c | 34 +++++++++++++++++-----------------
 1 file changed, 17 insertions(+), 17 deletions(-)

diff --git a/builtin/add.c b/builtin/add.c
index dd18e5c9b6..4fabdc72e6 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -418,10 +418,6 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 	if (addremove && take_worktree_changes)
 		die(_("-A and -u are mutually incompatible"));
 
-	if (!take_worktree_changes && addremove_explicit < 0 && argc)
-		/* Turn "git add pathspec..." to "git add -A pathspec..." */
-		addremove = 1;
-
 	if (!show_only && ignore_missing)
 		die(_("Option --ignore-missing can only be used together with --dry-run"));
 
@@ -434,19 +430,6 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 
 	hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
 
-	flags = ((verbose ? ADD_CACHE_VERBOSE : 0) |
-		 (show_only ? ADD_CACHE_PRETEND : 0) |
-		 (intent_to_add ? ADD_CACHE_INTENT : 0) |
-		 (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) |
-		 (!(addremove || take_worktree_changes)
-		  ? ADD_CACHE_IGNORE_REMOVAL : 0));
-
-	if (require_pathspec && argc == 0) {
-		fprintf(stderr, _("Nothing specified, nothing added.\n"));
-		fprintf(stderr, _("Maybe you wanted to say 'git add .'?\n"));
-		return 0;
-	}
-
 	/*
 	 * Check the "pathspec '%s' did not match any files" block
 	 * below before enabling new magic.
@@ -456,6 +439,23 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 		       PATHSPEC_SYMLINK_LEADING_PATH,
 		       prefix, argv);
 
+	if (require_pathspec && argc == 0) {
+		fprintf(stderr, _("Nothing specified, nothing added.\n"));
+		fprintf(stderr, _("Maybe you wanted to say 'git add .'?\n"));
+		return 0;
+	}
+
+	if (!take_worktree_changes && addremove_explicit < 0 && argc)
+		/* Turn "git add pathspec..." to "git add -A pathspec..." */
+		addremove = 1;
+
+	flags = ((verbose ? ADD_CACHE_VERBOSE : 0) |
+		 (show_only ? ADD_CACHE_PRETEND : 0) |
+		 (intent_to_add ? ADD_CACHE_INTENT : 0) |
+		 (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) |
+		 (!(addremove || take_worktree_changes)
+		  ? ADD_CACHE_IGNORE_REMOVAL : 0));
+
 	if (read_cache_preload(&pathspec) < 0)
 		die(_("index file corrupt"));
 
-- 
gitgitgadget


^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH v4 08/13] add: support the --pathspec-from-file option
  2019-12-03 14:02     ` [PATCH v4 00/13] Add --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
                         ` (6 preceding siblings ...)
  2019-12-03 14:02       ` [PATCH v4 07/13] cmd_add: prepare for next patch Alexandr Miloslavskiy via GitGitGadget
@ 2019-12-03 14:02       ` Alexandr Miloslavskiy via GitGitGadget
  2019-12-03 14:02       ` [PATCH v4 09/13] doc: checkout: remove duplicate synopsis Alexandr Miloslavskiy via GitGitGadget
                         ` (5 subsequent siblings)
  13 siblings, 0 replies; 80+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Alexandr Miloslavskiy, Junio C Hamano,
	Alexandr Miloslavskiy

From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>

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>
---
 Documentation/git-add.txt    |  16 ++++-
 builtin/add.c                |  30 +++++++--
 t/t3704-add-pathspec-file.sh | 127 +++++++++++++++++++++++++++++++++++
 3 files changed, 168 insertions(+), 5 deletions(-)
 create mode 100755 t/t3704-add-pathspec-file.sh

diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt
index 8b0e4c7fa8..be5e3ac54b 100644
--- a/Documentation/git-add.txt
+++ b/Documentation/git-add.txt
@@ -11,7 +11,8 @@ SYNOPSIS
 'git add' [--verbose | -v] [--dry-run | -n] [--force | -f] [--interactive | -i] [--patch | -p]
 	  [--edit | -e] [--[no-]all | --[no-]ignore-removal | [--update | -u]]
 	  [--intent-to-add | -N] [--refresh] [--ignore-errors] [--ignore-missing] [--renormalize]
-	  [--chmod=(+|-)x] [--] [<pathspec>...]
+	  [--chmod=(+|-)x] [--pathspec-from-file=<file> [--pathspec-file-nul]]
+	  [--] [<pathspec>...]
 
 DESCRIPTION
 -----------
@@ -187,6 +188,19 @@ for "git add --no-all <pathspec>...", i.e. ignored removed files.
 	bit is only changed in the index, the files on disk are left
 	unchanged.
 
+--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).
+
 \--::
 	This option can be used to separate command-line options from
 	the list of files, (useful when filenames might be mistaken
diff --git a/builtin/add.c b/builtin/add.c
index 4fabdc72e6..9f6b263aba 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -28,6 +28,8 @@ static const char * const builtin_add_usage[] = {
 static int patch_interactive, add_interactive, edit_interactive;
 static int take_worktree_changes;
 static int add_renormalize;
+static int pathspec_file_nul;
+static const char *pathspec_from_file;
 
 struct update_callback_data {
 	int flags;
@@ -309,6 +311,8 @@ static struct option builtin_add_options[] = {
 		   N_("override the executable bit of the listed files")),
 	OPT_HIDDEN_BOOL(0, "warn-embedded-repo", &warn_on_embedded_repo,
 			N_("warn when adding an embedded repository")),
+	OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
+	OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
 	OPT_END(),
 };
 
@@ -402,11 +406,17 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 			  builtin_add_usage, PARSE_OPT_KEEP_ARGV0);
 	if (patch_interactive)
 		add_interactive = 1;
-	if (add_interactive)
+	if (add_interactive) {
+		if (pathspec_from_file)
+			die(_("--pathspec-from-file is incompatible with --interactive/--patch"));
 		exit(interactive_add(argc - 1, argv + 1, prefix, patch_interactive));
+	}
 
-	if (edit_interactive)
+	if (edit_interactive) {
+		if (pathspec_from_file)
+			die(_("--pathspec-from-file is incompatible with --edit"));
 		return(edit_patch(argc, argv, prefix));
+	}
 	argc--;
 	argv++;
 
@@ -439,13 +449,25 @@ int cmd_add(int argc, const char **argv, const char *prefix)
 		       PATHSPEC_SYMLINK_LEADING_PATH,
 		       prefix, argv);
 
-	if (require_pathspec && argc == 0) {
+	if (pathspec_from_file) {
+		if (pathspec.nr)
+			die(_("--pathspec-from-file is incompatible with pathspec arguments"));
+
+		parse_pathspec_file(&pathspec, PATHSPEC_ATTR,
+				    PATHSPEC_PREFER_FULL |
+				    PATHSPEC_SYMLINK_LEADING_PATH,
+				    prefix, pathspec_from_file, pathspec_file_nul);
+	} else if (pathspec_file_nul) {
+		die(_("--pathspec-file-nul requires --pathspec-from-file"));
+	}
+
+	if (require_pathspec && pathspec.nr == 0) {
 		fprintf(stderr, _("Nothing specified, nothing added.\n"));
 		fprintf(stderr, _("Maybe you wanted to say 'git add .'?\n"));
 		return 0;
 	}
 
-	if (!take_worktree_changes && addremove_explicit < 0 && argc)
+	if (!take_worktree_changes && addremove_explicit < 0 && pathspec.nr)
 		/* Turn "git add pathspec..." to "git add -A pathspec..." */
 		addremove = 1;
 
diff --git a/t/t3704-add-pathspec-file.sh b/t/t3704-add-pathspec-file.sh
new file mode 100755
index 0000000000..3cfdb669b7
--- /dev/null
+++ b/t/t3704-add-pathspec-file.sh
@@ -0,0 +1,127 @@
+#!/bin/sh
+
+test_description='add --pathspec-from-file'
+
+. ./test-lib.sh
+
+test_tick
+
+test_expect_success setup '
+	test_commit file0 &&
+	echo A >fileA.t &&
+	echo B >fileB.t &&
+	echo C >fileC.t &&
+	echo D >fileD.t
+'
+
+restore_checkpoint () {
+	git reset
+}
+
+verify_expect () {
+	git status --porcelain --untracked-files=no -- fileA.t fileB.t fileC.t fileD.t >actual &&
+	test_cmp expect actual
+}
+
+test_expect_success '--pathspec-from-file from stdin' '
+	restore_checkpoint &&
+
+	echo fileA.t | git add --pathspec-from-file=- &&
+
+	cat >expect <<-\EOF &&
+	A  fileA.t
+	EOF
+	verify_expect
+'
+
+test_expect_success '--pathspec-from-file from file' '
+	restore_checkpoint &&
+
+	echo fileA.t >list &&
+	git add --pathspec-from-file=list &&
+
+	cat >expect <<-\EOF &&
+	A  fileA.t
+	EOF
+	verify_expect
+'
+
+test_expect_success 'NUL delimiters' '
+	restore_checkpoint &&
+
+	printf "fileA.t\0fileB.t\0" | git add --pathspec-from-file=- --pathspec-file-nul &&
+
+	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 add --pathspec-from-file=- &&
+
+	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 add --pathspec-from-file=- &&
+
+	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 add --pathspec-from-file=- &&
+
+	cat >expect <<-\EOF &&
+	A  fileA.t
+	A  fileB.t
+	EOF
+	verify_expect
+'
+
+test_expect_success 'quotes' '
+	restore_checkpoint &&
+
+	printf "\"file\\101.t\"" | git add --pathspec-from-file=- &&
+
+	cat >expect <<-\EOF &&
+	A  fileA.t
+	EOF
+	verify_expect
+'
+
+test_expect_success 'quotes not compatible with --pathspec-file-nul' '
+	restore_checkpoint &&
+
+	printf "\"file\\101.t\"" >list &&
+	test_must_fail git add --pathspec-from-file=list --pathspec-file-nul
+'
+
+test_expect_success 'only touches what was listed' '
+	restore_checkpoint &&
+
+	printf "fileB.t\nfileC.t\n" | git add --pathspec-from-file=- &&
+
+	cat >expect <<-\EOF &&
+	A  fileB.t
+	A  fileC.t
+	EOF
+	verify_expect
+'
+
+test_done
-- 
gitgitgadget


^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH v4 09/13] doc: checkout: remove duplicate synopsis
  2019-12-03 14:02     ` [PATCH v4 00/13] Add --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
                         ` (7 preceding siblings ...)
  2019-12-03 14:02       ` [PATCH v4 08/13] add: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
@ 2019-12-03 14:02       ` Alexandr Miloslavskiy via GitGitGadget
  2019-12-03 14:02       ` [PATCH v4 10/13] doc: checkout: fix broken text reference Alexandr Miloslavskiy via GitGitGadget
                         ` (4 subsequent siblings)
  13 siblings, 0 replies; 80+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Alexandr Miloslavskiy, Junio C Hamano,
	Alexandr Miloslavskiy

From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>

It was added in [1]. I understand that the duplicate change was not
intentional and comes from an oversight.

Also, in explanation, there was only one section for two synopsis
entries.

Fix both problems by removing duplicate synopsis.

<paths> vs <pathspec> is resolved in next patch.

[1] Commit b59698ae ("checkout doc: clarify command line args for "checkout paths" mode" 2017-10-11)

Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 Documentation/git-checkout.txt | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index cf3cac0a2b..2011fdbb1d 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -13,7 +13,6 @@ SYNOPSIS
 'git checkout' [-q] [-f] [-m] [--detach] <commit>
 'git checkout' [-q] [-f] [-m] [[-b|-B|--orphan] <new_branch>] [<start_point>]
 'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>...
-'git checkout' [<tree-ish>] [--] <pathspec>...
 'git checkout' (-p|--patch) [<tree-ish>] [--] [<paths>...]
 
 DESCRIPTION
@@ -79,7 +78,7 @@ be used to detach `HEAD` at the tip of the branch (`git checkout
 +
 Omitting `<branch>` detaches `HEAD` at the tip of the current branch.
 
-'git checkout' [<tree-ish>] [--] <pathspec>...::
+'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>...::
 
 	Overwrite paths in the working tree by replacing with the
 	contents in the index or in the `<tree-ish>` (most often a
-- 
gitgitgadget


^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH v4 10/13] doc: checkout: fix broken text reference
  2019-12-03 14:02     ` [PATCH v4 00/13] Add --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
                         ` (8 preceding siblings ...)
  2019-12-03 14:02       ` [PATCH v4 09/13] doc: checkout: remove duplicate synopsis Alexandr Miloslavskiy via GitGitGadget
@ 2019-12-03 14:02       ` Alexandr Miloslavskiy via GitGitGadget
  2019-12-03 14:02       ` [PATCH v4 11/13] doc: checkout: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget
                         ` (3 subsequent siblings)
  13 siblings, 0 replies; 80+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Alexandr Miloslavskiy, Junio C Hamano,
	Alexandr Miloslavskiy

From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>

Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 Documentation/git-checkout.txt | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 2011fdbb1d..d47046e050 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -95,12 +95,10 @@ using `--ours` or `--theirs`.  With `-m`, changes made to the working tree
 file can be discarded to re-create the original conflicted merge result.
 
 'git checkout' (-p|--patch) [<tree-ish>] [--] [<pathspec>...]::
-	This is similar to the "check out paths to the working tree
-	from either the index or from a tree-ish" mode described
-	above, but lets you use the interactive interface to show
-	the "diff" output and choose which hunks to use in the
-	result.  See below for the description of `--patch` option.
-
+	This is similar to the previous mode, but lets you use the
+	interactive interface to show the "diff" output and choose which
+	hunks to use in the result.  See below for the description of
+	`--patch` option.
 
 OPTIONS
 -------
-- 
gitgitgadget


^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH v4 11/13] doc: checkout: synchronize <pathspec> description
  2019-12-03 14:02     ` [PATCH v4 00/13] Add --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
                         ` (9 preceding siblings ...)
  2019-12-03 14:02       ` [PATCH v4 10/13] doc: checkout: fix broken text reference Alexandr Miloslavskiy via GitGitGadget
@ 2019-12-03 14:02       ` Alexandr Miloslavskiy via GitGitGadget
  2019-12-03 14:02       ` [PATCH v4 12/13] doc: restore: " Alexandr Miloslavskiy via GitGitGadget
                         ` (2 subsequent siblings)
  13 siblings, 0 replies; 80+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Alexandr Miloslavskiy, Junio C Hamano,
	Alexandr Miloslavskiy

From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>

`git add` shows an example of good writing, follow it.

Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 Documentation/git-checkout.txt | 24 +++++++++++++++---------
 1 file changed, 15 insertions(+), 9 deletions(-)

diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index d47046e050..93124f3ad9 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -12,13 +12,13 @@ SYNOPSIS
 'git checkout' [-q] [-f] [-m] --detach [<branch>]
 'git checkout' [-q] [-f] [-m] [--detach] <commit>
 'git checkout' [-q] [-f] [-m] [[-b|-B|--orphan] <new_branch>] [<start_point>]
-'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>...
-'git checkout' (-p|--patch) [<tree-ish>] [--] [<paths>...]
+'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <pathspec>...
+'git checkout' (-p|--patch) [<tree-ish>] [--] [<pathspec>...]
 
 DESCRIPTION
 -----------
 Updates files in the working tree to match the version in the index
-or the specified tree.  If no paths are given, 'git checkout' will
+or the specified tree.  If no pathspec was given, 'git checkout' will
 also update `HEAD` to set the specified branch as the current
 branch.
 
@@ -78,13 +78,13 @@ be used to detach `HEAD` at the tip of the branch (`git checkout
 +
 Omitting `<branch>` detaches `HEAD` at the tip of the current branch.
 
-'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>...::
+'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <pathspec>...::
 
-	Overwrite paths in the working tree by replacing with the
-	contents in the index or in the `<tree-ish>` (most often a
-	commit).  When a `<tree-ish>` is given, the paths that
-	match the `<pathspec>` are updated both in the index and in
-	the working tree.
+	Overwrite the contents of the files that match the pathspec.
+	When the `<tree-ish>` (most often a commit) is not given, 
+	overwrite working tree with the contents in the index.
+	When the `<tree-ish>` is given, overwrite both the index and
+	the working tree with the contents at the `<tree-ish>`.
 +
 The index may contain unmerged entries because of a previous failed merge.
 By default, if you try to check out such an entry from the index, the
@@ -336,7 +336,13 @@ leave out at most one of `A` and `B`, in which case it defaults to `HEAD`.
 	Tree to checkout from (when paths are given). If not specified,
 	the index will be used.
 
+\--::
+	Do not interpret any more arguments as options.
 
+<pathspec>...::
+	Limits the paths affected by the operation.
++
+For more details, see the 'pathspec' entry in linkgit:gitglossary[7].
 
 DETACHED HEAD
 -------------
-- 
gitgitgadget


^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH v4 12/13] doc: restore: synchronize <pathspec> description
  2019-12-03 14:02     ` [PATCH v4 00/13] Add --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
                         ` (10 preceding siblings ...)
  2019-12-03 14:02       ` [PATCH v4 11/13] doc: checkout: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget
@ 2019-12-03 14:02       ` " Alexandr Miloslavskiy via GitGitGadget
  2019-12-03 14:02       ` [PATCH v4 13/13] checkout, restore: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
  2019-12-03 16:55       ` [PATCH v4 00/13] Add " Junio C Hamano
  13 siblings, 0 replies; 80+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Alexandr Miloslavskiy, Junio C Hamano,
	Alexandr Miloslavskiy

From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>

`git add` shows an example of good writing, follow it.

Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 Documentation/git-restore.txt | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/Documentation/git-restore.txt b/Documentation/git-restore.txt
index 1ab2e40ea9..d7bf016bba 100644
--- a/Documentation/git-restore.txt
+++ b/Documentation/git-restore.txt
@@ -8,8 +8,8 @@ git-restore - Restore working tree files
 SYNOPSIS
 --------
 [verse]
-'git restore' [<options>] [--source=<tree>] [--staged] [--worktree] <pathspec>...
-'git restore' (-p|--patch) [<options>] [--source=<tree>] [--staged] [--worktree] [<pathspec>...]
+'git restore' [<options>] [--source=<tree>] [--staged] [--worktree] [--] <pathspec>...
+'git restore' (-p|--patch) [<options>] [--source=<tree>] [--staged] [--worktree] [--] [<pathspec>...]
 
 DESCRIPTION
 -----------
@@ -113,6 +113,14 @@ in linkgit:git-checkout[1] for details.
 	appear in the `--source` tree are removed, to make them match
 	`<tree>` exactly. The default is no-overlay mode.
 
+\--::
+	Do not interpret any more arguments as options.
+
+<pathspec>...::
+	Limits the paths affected by the operation.
++
+For more details, see the 'pathspec' entry in linkgit:gitglossary[7].
+
 EXAMPLES
 --------
 
-- 
gitgitgadget


^ permalink raw reply	[flat|nested] 80+ messages in thread

* [PATCH v4 13/13] checkout, restore: support the --pathspec-from-file option
  2019-12-03 14:02     ` [PATCH v4 00/13] Add --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
                         ` (11 preceding siblings ...)
  2019-12-03 14:02       ` [PATCH v4 12/13] doc: restore: " Alexandr Miloslavskiy via GitGitGadget
@ 2019-12-03 14:02       ` Alexandr Miloslavskiy via GitGitGadget
  2019-12-03 16:55       ` [PATCH v4 00/13] Add " Junio C Hamano
  13 siblings, 0 replies; 80+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Alexandr Miloslavskiy, Junio C Hamano,
	Alexandr Miloslavskiy

From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>

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.
2) It is not allowed to pass pathspec in both args and file.

`you must specify path(s) to restore` block was moved down to be able to
test for `pathspec.nr` instead, because testing for `argc` is no longer
correct.

`git switch` does not support the new options because it doesn't expect
`<pathspec>` arguments.

Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 Documentation/git-checkout.txt    |  15 ++++
 Documentation/git-restore.txt     |  14 +++
 builtin/checkout.c                |  31 ++++++-
 t/t2026-checkout-pathspec-file.sh | 139 ++++++++++++++++++++++++++++++
 t/t2072-restore-pathspec-file.sh  | 139 ++++++++++++++++++++++++++++++
 t/t9902-completion.sh             |   2 +
 6 files changed, 336 insertions(+), 4 deletions(-)
 create mode 100755 t/t2026-checkout-pathspec-file.sh
 create mode 100755 t/t2072-restore-pathspec-file.sh

diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index 93124f3ad9..ffe3c1bff2 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -13,6 +13,7 @@ SYNOPSIS
 'git checkout' [-q] [-f] [-m] [--detach] <commit>
 'git checkout' [-q] [-f] [-m] [[-b|-B|--orphan] <new_branch>] [<start_point>]
 'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <pathspec>...
+'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] --pathspec-from-file=<file> [--pathspec-file-nul]
 'git checkout' (-p|--patch) [<tree-ish>] [--] [<pathspec>...]
 
 DESCRIPTION
@@ -79,6 +80,7 @@ be used to detach `HEAD` at the tip of the branch (`git checkout
 Omitting `<branch>` detaches `HEAD` at the tip of the current branch.
 
 'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <pathspec>...::
+'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] --pathspec-from-file=<file> [--pathspec-file-nul]::
 
 	Overwrite the contents of the files that match the pathspec.
 	When the `<tree-ish>` (most often a commit) is not given, 
@@ -306,6 +308,19 @@ Note that this option uses the no overlay mode by default (see also
 	working tree, but not in `<tree-ish>` are removed, to make them
 	match `<tree-ish>` exactly.
 
+--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).
+
 <branch>::
 	Branch to checkout; if it refers to a branch (i.e., a name that,
 	when prepended with "refs/heads/", is a valid ref), then that
diff --git a/Documentation/git-restore.txt b/Documentation/git-restore.txt
index d7bf016bba..5bf60d4943 100644
--- a/Documentation/git-restore.txt
+++ b/Documentation/git-restore.txt
@@ -9,6 +9,7 @@ SYNOPSIS
 --------
 [verse]
 'git restore' [<options>] [--source=<tree>] [--staged] [--worktree] [--] <pathspec>...
+'git restore' [<options>] [--source=<tree>] [--staged] [--worktree] --pathspec-from-file=<file> [--pathspec-file-nul]
 'git restore' (-p|--patch) [<options>] [--source=<tree>] [--staged] [--worktree] [--] [<pathspec>...]
 
 DESCRIPTION
@@ -113,6 +114,19 @@ in linkgit:git-checkout[1] for details.
 	appear in the `--source` tree are removed, to make them match
 	`<tree>` exactly. The default is no-overlay mode.
 
+--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).
+
 \--::
 	Do not interpret any more arguments as options.
 
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 3634a3dac1..b52c490c8f 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -70,6 +70,8 @@ struct checkout_opts {
 	int checkout_worktree;
 	const char *ignore_unmerged_opt;
 	int ignore_unmerged;
+	int pathspec_file_nul;
+	const char *pathspec_from_file;
 
 	const char *new_branch;
 	const char *new_branch_force;
@@ -1480,6 +1482,8 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts,
 		OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")),
 		OPT_BOOL(0, "ignore-skip-worktree-bits", &opts->ignore_skipworktree,
 			 N_("do not limit pathspecs to sparse entries only")),
+		OPT_PATHSPEC_FROM_FILE(&opts->pathspec_from_file),
+		OPT_PATHSPEC_FILE_NUL(&opts->pathspec_file_nul),
 		OPT_END()
 	};
 	struct option *newopts = parse_options_concat(prevopts, options);
@@ -1618,10 +1622,6 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 			die(_("reference is not a tree: %s"), opts->from_treeish);
 	}
 
-	if (opts->accept_pathspec && !opts->empty_pathspec_ok && !argc &&
-	    !opts->patch_mode)	/* patch mode is special */
-		die(_("you must specify path(s) to restore"));
-
 	if (argc) {
 		parse_pathspec(&opts->pathspec, 0,
 			       opts->patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0,
@@ -1641,10 +1641,33 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
 		if (opts->force_detach)
 			die(_("git checkout: --detach does not take a path argument '%s'"),
 			    argv[0]);
+	}
+
+	if (opts->pathspec_from_file) {
+		if (opts->pathspec.nr)
+			die(_("--pathspec-from-file is incompatible with pathspec arguments"));
+
+		if (opts->force_detach)
+			die(_("--pathspec-from-file is incompatible with --detach"));
 
+		if (opts->patch_mode)
+			die(_("--pathspec-from-file is incompatible with --patch"));
+
+		parse_pathspec_file(&opts->pathspec, 0,
+				    0,
+				    prefix, opts->pathspec_from_file, opts->pathspec_file_nul);
+	} else if (opts->pathspec_file_nul) {
+		die(_("--pathspec-file-nul requires --pathspec-from-file"));
+	}
+
+	if (opts->pathspec.nr) {
 		if (1 < !!opts->writeout_stage + !!opts->force + !!opts->merge)
 			die(_("git checkout: --ours/--theirs, --force and --merge are incompatible when\n"
 			      "checking out of the index."));
+	} else {
+		if (opts->accept_pathspec && !opts->empty_pathspec_ok &&
+		    !opts->patch_mode)	/* patch mode is special */
+			die(_("you must specify path(s) to restore"));
 	}
 
 	if (opts->new_branch) {
diff --git a/t/t2026-checkout-pathspec-file.sh b/t/t2026-checkout-pathspec-file.sh
new file mode 100755
index 0000000000..f62fd27440
--- /dev/null
+++ b/t/t2026-checkout-pathspec-file.sh
@@ -0,0 +1,139 @@
+#!/bin/sh
+
+test_description='checkout --pathspec-from-file'
+
+. ./test-lib.sh
+
+test_tick
+
+test_expect_success setup '
+	test_commit file0 &&
+
+	echo 1 >fileA.t &&
+	echo 1 >fileB.t &&
+	echo 1 >fileC.t &&
+	echo 1 >fileD.t &&
+	git add fileA.t fileB.t fileC.t fileD.t &&
+	git commit -m "files 1" &&
+
+	echo 2 >fileA.t &&
+	echo 2 >fileB.t &&
+	echo 2 >fileC.t &&
+	echo 2 >fileD.t &&
+	git add fileA.t fileB.t fileC.t fileD.t &&
+	git commit -m "files 2" &&
+
+	git tag checkpoint
+'
+
+restore_checkpoint () {
+	git reset --hard checkpoint
+}
+
+verify_expect () {
+	git status --porcelain --untracked-files=no -- fileA.t fileB.t fileC.t fileD.t >actual &&
+	test_cmp expect actual
+}
+
+test_expect_success '--pathspec-from-file from stdin' '
+	restore_checkpoint &&
+
+	echo fileA.t | git checkout --pathspec-from-file=- HEAD^1 &&
+
+	cat >expect <<-\EOF &&
+	M  fileA.t
+	EOF
+	verify_expect
+'
+
+test_expect_success '--pathspec-from-file from file' '
+	restore_checkpoint &&
+
+	echo fileA.t >list &&
+	git checkout --pathspec-from-file=list HEAD^1 &&
+
+	cat >expect <<-\EOF &&
+	M  fileA.t
+	EOF
+	verify_expect
+'
+
+test_expect_success 'NUL delimiters' '
+	restore_checkpoint &&
+
+	printf "fileA.t\0fileB.t\0" | git checkout --pathspec-from-file=- --pathspec-file-nul HEAD^1 &&
+
+	cat >expect <<-\EOF &&
+	M  fileA.t
+	M  fileB.t
+	EOF
+	verify_expect
+'
+
+test_expect_success 'LF delimiters' '
+	restore_checkpoint &&
+
+	printf "fileA.t\nfileB.t\n" | git checkout --pathspec-from-file=- HEAD^1 &&
+
+	cat >expect <<-\EOF &&
+	M  fileA.t
+	M  fileB.t
+	EOF
+	verify_expect
+'
+
+test_expect_success 'no trailing delimiter' '
+	restore_checkpoint &&
+
+	printf "fileA.t\nfileB.t" | git checkout --pathspec-from-file=- HEAD^1 &&
+
+	cat >expect <<-\EOF &&
+	M  fileA.t
+	M  fileB.t
+	EOF
+	verify_expect
+'
+
+test_expect_success 'CRLF delimiters' '
+	restore_checkpoint &&
+
+	printf "fileA.t\r\nfileB.t\r\n" | git checkout --pathspec-from-file=- HEAD^1 &&
+
+	cat >expect <<-\EOF &&
+	M  fileA.t
+	M  fileB.t
+	EOF
+	verify_expect
+'
+
+test_expect_success 'quotes' '
+	restore_checkpoint &&
+
+	printf "\"file\\101.t\"" | git checkout --pathspec-from-file=- HEAD^1 &&
+
+	cat >expect <<-\EOF &&
+	M  fileA.t
+	EOF
+	verify_expect
+'
+
+test_expect_success 'quotes not compatible with --pathspec-file-nul' '
+	restore_checkpoint &&
+
+	printf "\"file\\101.t\"" >list &&
+	test_must_fail git checkout --pathspec-from-file=list --pathspec-file-nul HEAD^1
+'
+
+test_expect_success 'only touches what was listed' '
+	restore_checkpoint &&
+
+	printf "fileB.t\nfileC.t\n" | git checkout --pathspec-from-file=- HEAD^1 &&
+
+	cat >expect <<-\EOF &&
+	M  fileB.t
+	M  fileC.t
+	EOF
+	verify_expect
+'
+
+test_done
diff --git a/t/t2072-restore-pathspec-file.sh b/t/t2072-restore-pathspec-file.sh
new file mode 100755
index 0000000000..db58e83735
--- /dev/null
+++ b/t/t2072-restore-pathspec-file.sh
@@ -0,0 +1,139 @@
+#!/bin/sh
+
+test_description='restore --pathspec-from-file'
+
+. ./test-lib.sh
+
+test_tick
+
+test_expect_success setup '
+	test_commit file0 &&
+
+	echo 1 >fileA.t &&
+	echo 1 >fileB.t &&
+	echo 1 >fileC.t &&
+	echo 1 >fileD.t &&
+	git add fileA.t fileB.t fileC.t fileD.t &&
+	git commit -m "files 1" &&
+
+	echo 2 >fileA.t &&
+	echo 2 >fileB.t &&
+	echo 2 >fileC.t &&
+	echo 2 >fileD.t &&
+	git add fileA.t fileB.t fileC.t fileD.t &&
+	git commit -m "files 2" &&
+
+	git tag checkpoint
+'
+
+restore_checkpoint () {
+	git reset --hard checkpoint
+}
+
+verify_expect () {
+	git status --porcelain --untracked-files=no -- fileA.t fileB.t fileC.t fileD.t >actual &&
+	test_cmp expect actual
+}
+
+test_expect_success '--pathspec-from-file from stdin' '
+	restore_checkpoint &&
+
+	echo fileA.t | git restore --pathspec-from-file=- --source=HEAD^1 &&
+
+	cat >expect <<-\EOF &&
+	 M fileA.t
+	EOF
+	verify_expect
+'
+
+test_expect_success '--pathspec-from-file from file' '
+	restore_checkpoint &&
+
+	echo fileA.t >list &&
+	git restore --pathspec-from-file=list --source=HEAD^1 &&
+
+	cat >expect <<-\EOF &&
+	 M fileA.t
+	EOF
+	verify_expect
+'
+
+test_expect_success 'NUL delimiters' '
+	restore_checkpoint &&
+
+	printf "fileA.t\0fileB.t\0" | git restore --pathspec-from-file=- --pathspec-file-nul --source=HEAD^1 &&
+
+	cat >expect <<-\EOF &&
+	 M fileA.t
+	 M fileB.t
+	EOF
+	verify_expect
+'
+
+test_expect_success 'LF delimiters' '
+	restore_checkpoint &&
+
+	printf "fileA.t\nfileB.t\n" | git restore --pathspec-from-file=- --source=HEAD^1 &&
+
+	cat >expect <<-\EOF &&
+	 M fileA.t
+	 M fileB.t
+	EOF
+	verify_expect
+'
+
+test_expect_success 'no trailing delimiter' '
+	restore_checkpoint &&
+
+	printf "fileA.t\nfileB.t" | git restore --pathspec-from-file=- --source=HEAD^1 &&
+
+	cat >expect <<-\EOF &&
+	 M fileA.t
+	 M fileB.t
+	EOF
+	verify_expect
+'
+
+test_expect_success 'CRLF delimiters' '
+	restore_checkpoint &&
+
+	printf "fileA.t\r\nfileB.t\r\n" | git restore --pathspec-from-file=- --source=HEAD^1 &&
+
+	cat >expect <<-\EOF &&
+	 M fileA.t
+	 M fileB.t
+	EOF
+	verify_expect
+'
+
+test_expect_success 'quotes' '
+	restore_checkpoint &&
+
+	printf "\"file\\101.t\"" | git restore --pathspec-from-file=- --source=HEAD^1 &&
+
+	cat >expect <<-\EOF &&
+	 M fileA.t
+	EOF
+	verify_expect
+'
+
+test_expect_success 'quotes not compatible with --pathspec-file-nul' '
+	restore_checkpoint &&
+
+	printf "\"file\\101.t\"" >list &&
+	test_must_fail git restore --pathspec-from-file=list --pathspec-file-nul --source=HEAD^1
+'
+
+test_expect_success 'only touches what was listed' '
+	restore_checkpoint &&
+
+	printf "fileB.t\nfileC.t\n" | git restore --pathspec-from-file=- --source=HEAD^1 &&
+
+	cat >expect <<-\EOF &&
+	 M fileB.t
+	 M fileC.t
+	EOF
+	verify_expect
+'
+
+test_done
diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh
index ec3eccfd3d..93877ba9cd 100755
--- a/t/t9902-completion.sh
+++ b/t/t9902-completion.sh
@@ -1438,6 +1438,8 @@ test_expect_success 'double dash "git checkout"' '
 	--no-guess Z
 	--no-... Z
 	--overlay Z
+	--pathspec-file-nul Z
+	--pathspec-from-file=Z
 	EOF
 '
 
-- 
gitgitgadget

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 00/13] Add --pathspec-from-file option
  2019-12-03 14:02     ` [PATCH v4 00/13] Add --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
                         ` (12 preceding siblings ...)
  2019-12-03 14:02       ` [PATCH v4 13/13] checkout, restore: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
@ 2019-12-03 16:55       ` " Junio C Hamano
  2019-12-03 17:06         ` Alexandr Miloslavskiy
  13 siblings, 1 reply; 80+ messages in thread
From: Junio C Hamano @ 2019-12-03 16:55 UTC (permalink / raw)
  To: Alexandr Miloslavskiy via GitGitGadget; +Cc: git, Alexandr Miloslavskiy

"Alexandr Miloslavskiy via GitGitGadget" <gitgitgadget@gmail.com>
writes:

> Changes from V3:

Yikes, perhaps our mails crossed or something?  I think the previous
round is already in 'next'.

Let's wait and see they cook enough to graduate to 'master', and
build a separate series on top to teach other commands the option
using the facility introduced by the current series (which is the
first 6 patches you sent here).

Thanks.

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 00/13] Add --pathspec-from-file option
  2019-12-03 16:55       ` [PATCH v4 00/13] Add " Junio C Hamano
@ 2019-12-03 17:06         ` Alexandr Miloslavskiy
  2019-12-04 19:25           ` Junio C Hamano
  0 siblings, 1 reply; 80+ messages in thread
From: Alexandr Miloslavskiy @ 2019-12-03 17:06 UTC (permalink / raw)
  To: Junio C Hamano, Alexandr Miloslavskiy via GitGitGadget; +Cc: git

On 03.12.2019 17:55, Junio C Hamano wrote:

>> Changes from V3:
> 
> Yikes, perhaps our mails crossed or something?  I think the previous
> round is already in 'next'.
> 
> Let's wait and see they cook enough to graduate to 'master', and
> build a separate series on top to teach other commands the option
> using the facility introduced by the current series (which is the
> first 6 patches you sent here).

My intent is to support more commands, so I was working on other patches 
in the background. Today more patches were ready and I wasn't sure 
whether to submit another topic or continue the old one. After some 
thinking, I decided to continue the old one.

Apparently, I guessed wrong :) Roger: I will wait until the first part 
graduates to 'master', then submit more patches.

Please give me an advice: when the time comes, shall I prepare even more 
patches and submit a massive branch, or shall I submit today's remaining 
patches, then wait again? I imagine that massive branches are scary and 
will deter reviewers?

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 00/13] Add --pathspec-from-file option
  2019-12-03 17:06         ` Alexandr Miloslavskiy
@ 2019-12-04 19:25           ` Junio C Hamano
  2019-12-05 10:43             ` Alexandr Miloslavskiy
  0 siblings, 1 reply; 80+ messages in thread
From: Junio C Hamano @ 2019-12-04 19:25 UTC (permalink / raw)
  To: Alexandr Miloslavskiy; +Cc: Alexandr Miloslavskiy via GitGitGadget, git

Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> writes:

> On 03.12.2019 17:55, Junio C Hamano wrote:
>
>>> Changes from V3:
>>
>> Yikes, perhaps our mails crossed or something?  I think the previous
>> round is already in 'next'.
>>
>> Let's wait and see they cook enough to graduate to 'master', and
>> build a separate series on top to teach other commands the option
>> using the facility introduced by the current series (which is the
>> first 6 patches you sent here).
>
> My intent is to support more commands, so I was working on other
> patches in the background. Today more patches were ready and I wasn't
> sure whether to submit another topic or continue the old one. After
> some thinking, I decided to continue the old one.

Well I've split the new patches into its own topic to queue on the
am/pathspec-f-f-checkout branch, that builds directly on top of the
am/pathspec-from-file branch, for now.  I suspect that they may want
to be two topics (i.e. for "add" and for "checkout/restore"), but
I'd like to keep them out of 'next' either way for a while until the
base topic proves to be solid enough.

> Please give me an advice: when the time comes, shall I prepare even
> more patches and submit a massive branch, or shall I submit today's
> remaining patches, then wait again? I imagine that massive branches
> are scary and will deter reviewers?

Scary will probably not be an issue for the follow-up topics around
the pathspec-from-file theme, but a huge topic tends to wear out the
author and the reviewers, inviting trivial bugs that would otherwise
be found easily go unnoticed.

Thanks.

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH v4 00/13] Add --pathspec-from-file option
  2019-12-04 19:25           ` Junio C Hamano
@ 2019-12-05 10:43             ` Alexandr Miloslavskiy
  0 siblings, 0 replies; 80+ messages in thread
From: Alexandr Miloslavskiy @ 2019-12-05 10:43 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Alexandr Miloslavskiy via GitGitGadget, git

On 04.12.2019 20:25, Junio C Hamano wrote:

> Scary will probably not be an issue for the follow-up topics around
> the pathspec-from-file theme, but a huge topic tends to wear out the
> author and the reviewers, inviting trivial bugs that would otherwise
> be found easily go unnoticed.

OK, thanks! I will pause for now and wait until existing patches are in 
`master`.

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 5/5] commit: support the --pathspec-from-file option
  2019-11-06 15:56     ` Alexandr Miloslavskiy
@ 2019-12-10 10:42       ` Phillip Wood
  2019-12-11 11:43         ` Alexandr Miloslavskiy
  0 siblings, 1 reply; 80+ messages in thread
From: Phillip Wood @ 2019-12-10 10:42 UTC (permalink / raw)
  To: Alexandr Miloslavskiy, phillip.wood,
	Alexandr Miloslavskiy via GitGitGadget, git
  Cc: Junio C Hamano

Hi Alexandr

Sorry it has taken me so long to reply

On 06/11/2019 15:56, Alexandr Miloslavskiy wrote:
> I think I have implemented most suggestions in PatchV2. Thanks!
> 
>> It might be worth tailoring the message to the command rather than 
>> having exactly the same message for commit and reset
> 
> I decided to move the general comment to base commit where options are 
> introduced and not repeat it where options are supported.
> 
>> I wonder if there is a way of calling parse_pathspec_file() from 
>> parse_and_validate_options() instead. Otherwise we end up validating 
>> options here instead which is a bit messy.
> 
> The code looks a bit too entangled to support that without making it 
> worse. The biggest concern I have is that parse_and_validate_options() 
> will populate `pathspec` and some other function will need to remember 
> to clean it up. I like it better when `pathspec` is handled in one place.

I don't think it's so bad if the pathspec is cleaned up just after it is used, 
the diff below applies on top of your patch - see what you think. The diff 
also adds dies if --all is given with --pathspec-from-file which (together 
with a test) would be worth adding to you series I think.

> 
> I agree that things are not perfect, but this seems to be a consequence 
> of other existing problems. For example, I would have expected a 
> structure instead of a handful of global variables. That would have 
> solved many problems. However, I didn't have the bravery to dive into 
> this refactoring.

Yes it is a pain that the builtin functions tend to use a lot of global 
variables rather than a structure.

Best Wishes

Phillip

--- >8 ---
diff --git a/builtin/commit.c b/builtin/commit.c
index ed40729355..bb9515c44b 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -330,37 +330,18 @@ static void refresh_cache_or_die(int refresh_flags)
 }
 
 static const char *prepare_index(int argc, const char **argv, const char *prefix,
+				 struct pathspec *pathspec,
 				 const struct commit *current_head, int is_status)
 {
 	struct string_list partial = STRING_LIST_INIT_DUP;
-	struct pathspec pathspec;
+
 	int refresh_flags = REFRESH_QUIET;
 	const char *ret;
 
 	if (is_status)
 		refresh_flags |= REFRESH_UNMERGED;
-	parse_pathspec(&pathspec, 0,
-		       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)
+	if (read_cache_preload(pathspec) < 0)
 		die(_("index file corrupt"));
 
 	if (interactive) {
@@ -411,9 +392,9 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
 	 * (A) if all goes well, commit the real index;
 	 * (B) on failure, rollback the real index.
 	 */
-	if (all || (also && pathspec.nr)) {
+	if (all || (also && pathspec->nr)) {
 		hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
-		add_files_to_cache(also ? prefix : NULL, &pathspec, 0);
+		add_files_to_cache(also ? prefix : NULL, pathspec, 0);
 		refresh_cache_or_die(refresh_flags);
 		update_main_cache_tree(WRITE_TREE_SILENT);
 		if (write_locked_index(&the_index, &index_lock, 0))
@@ -432,7 +413,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
 	 * and create commit from the_index.
 	 * We still need to refresh the index here.
 	 */
-	if (!only && !pathspec.nr) {
+	if (!only && !pathspec->nr) {
 		hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
 		refresh_cache_or_die(refresh_flags);
 		if (active_cache_changed
@@ -474,7 +455,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
 			die(_("cannot do a partial commit during a cherry-pick."));
 	}
 
-	if (list_paths(&partial, !current_head ? NULL : "HEAD", &pathspec))
+	if (list_paths(&partial, !current_head ? NULL : "HEAD", pathspec))
 		exit(1);
 
 	discard_cache();
@@ -505,7 +486,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
 	read_cache_from(ret);
 out:
 	string_list_clear(&partial, 0);
-	clear_pathspec(&pathspec);
+	clear_pathspec(pathspec);
 	return ret;
 }
 
@@ -1148,6 +1129,7 @@ static int parse_and_validate_options(int argc, const char *argv[],
 				      const struct option *options,
 				      const char * const usage[],
 				      const char *prefix,
+				      struct pathspec *pathspec,
 				      struct commit *current_head,
 				      struct wt_status *s)
 {
@@ -1223,19 +1205,42 @@ static int parse_and_validate_options(int argc, const char *argv[],
 		die(_("paths '%s ...' with -a does not make sense"),
 		    argv[0]);
 
+	if (pathspec_from_file) {
+		if (interactive)
+			die(_("--pathspec-from-file is incompatible with --interactive/--patch"));
+
+		if (argc)
+			die(_("--pathspec-from-file is incompatible with pathspec arguments"));
+
+		if (all)
+			die(_("--pathspec-from-file is incompatible with --all"));
+
+		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"));
+	} else {
+		parse_pathspec(pathspec, 0, PATHSPEC_PREFER_FULL, prefix, argv);
+	}
+
+	if (!pathspec->nr && (also || (only && !amend && !allow_empty)))
+		die(_("No paths with --include/--only does not make sense."));
+
 	if (status_format != STATUS_FORMAT_NONE)
 		dry_run = 1;
 
 	return argc;
 }
 
 static int dry_run_commit(int argc, const char **argv, const char *prefix,
+			  struct pathspec *pathspec,
 			  const struct commit *current_head, struct wt_status *s)
 {
 	int committable;
 	const char *index_file;
 
-	index_file = prepare_index(argc, argv, prefix, current_head, 1);
+	index_file = prepare_index(argc, argv, prefix, pathspec, current_head, 1);
 	committable = run_status(stdout, index_file, prefix, 0, s);
 	rollback_index_files();
 
@@ -1571,6 +1576,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	struct commit *current_head = NULL;
 	struct commit_extra_header *extra = NULL;
 	struct strbuf err = STRBUF_INIT;
+	struct pathspec pathspec;
 
 	if (argc == 2 && !strcmp(argv[1], "-h"))
 		usage_with_options(builtin_commit_usage, builtin_commit_options);
@@ -1590,13 +1596,15 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 	verbose = -1; /* unspecified */
 	argc = parse_and_validate_options(argc, argv, builtin_commit_options,
 					  builtin_commit_usage,
-					  prefix, current_head, &s);
+					  prefix, &pathspec, current_head, &s);
 	if (verbose == -1)
 		verbose = (config_commit_verbose < 0) ? 0 : config_commit_verbose;
 
 	if (dry_run)
-		return dry_run_commit(argc, argv, prefix, current_head, &s);
-	index_file = prepare_index(argc, argv, prefix, current_head, 0);
+		return dry_run_commit(argc, argv, prefix, &pathspec,
+				     current_head, &s);
+	index_file = prepare_index(argc, argv, prefix, &pathspec, current_head,
+				   0);
 
 	/* Set up everything for writing the commit object.  This includes
 	   running hooks, writing the trees, and interacting with the user.  */


^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 5/5] commit: support the --pathspec-from-file option
  2019-12-10 10:42       ` Phillip Wood
@ 2019-12-11 11:43         ` Alexandr Miloslavskiy
  2019-12-11 14:27           ` Phillip Wood
  0 siblings, 1 reply; 80+ messages in thread
From: Alexandr Miloslavskiy @ 2019-12-11 11:43 UTC (permalink / raw)
  To: phillip.wood, Alexandr Miloslavskiy via GitGitGadget, git; +Cc: Junio C Hamano

On 10.12.2019 11:42, Phillip Wood wrote:
> I don't think it's so bad if the pathspec is cleaned up just after it is used,
> the diff below applies on top of your patch - see what you think. The diff
> also adds dies if --all is given with --pathspec-from-file which (together
> with a test) would be worth adding to you series I think.

Unfortunately, your reply came too late, topic was cooking in pu/next 
for a while and merged into master yesterday: c58ae96f.

I understand that your patch consists of two parts:

1) Adding test for --all
------------------------
I must admit that I overlooked that there was a similar test for 
args-based pathspec. I will add this part in my next topic for 
--pathspec-from-file. I expect it to appear in the next day or two. I 
will try to remember to CC you to it.

2) Moving parsing/validation into `parse_and_validate_options()`
------------------------
Again, I agree that having parsing/validation outside is suboptimal.
However, with current code, I find it to be a choice between two evils, 
and my choice was "outside but clear" to "inside but obscure".

What I find obscure in your suggestion/patch is that innocently looking 
`prepare_index()` suddenly clears pathspec as well. It's even harder to 
see when called through `dry_run_commit()`.

Now, let me illustrate. There's a similar case in my TODO list, this 
time involving a real bug in git. In `init_db()`, the bug occurs in 
`set_git_dir(real_path(git_dir))`, also due to unexpected clearing.

Now that I pointed my finger at it, please try to find what's wrong. I 
imagine that it won't be easy at all. And it's way harder when there's 
no reason to dig deep into a very specific line of code.

I really try to avoid such type of pitfalls. That's why my choice 
between two evils is what I did.


^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 5/5] commit: support the --pathspec-from-file option
  2019-12-11 11:43         ` Alexandr Miloslavskiy
@ 2019-12-11 14:27           ` Phillip Wood
  2019-12-11 15:06             ` Alexandr Miloslavskiy
  2019-12-12 14:56             ` Alexandr Miloslavskiy
  0 siblings, 2 replies; 80+ messages in thread
From: Phillip Wood @ 2019-12-11 14:27 UTC (permalink / raw)
  To: Alexandr Miloslavskiy, phillip.wood,
	Alexandr Miloslavskiy via GitGitGadget, git
  Cc: Junio C Hamano

Hi Alexandr

On 11/12/2019 11:43, Alexandr Miloslavskiy wrote:
> On 10.12.2019 11:42, Phillip Wood wrote:
>> I don't think it's so bad if the pathspec is cleaned up just after it 
>> is used,
>> the diff below applies on top of your patch - see what you think. The 
>> diff
>> also adds dies if --all is given with --pathspec-from-file which 
>> (together
>> with a test) would be worth adding to you series I think.
> 
> Unfortunately, your reply came too late, topic was cooking in pu/next 
> for a while and merged into master yesterday: c58ae96f.

Ah I thought it might be a bit late to fix up the patch, there could 
always be a follow patch though.

> 
> I understand that your patch consists of two parts:
> 
> 1) Adding test for --all
> ------------------------
> I must admit that I overlooked that there was a similar test for 
> args-based pathspec. I will add this part in my next topic for 
> --pathspec-from-file. I expect it to appear in the next day or two. I 
> will try to remember to CC you to it.

Thanks, one other thing I forgot to mention yesterday is to ask what the 
expected behavior is if the user passes an empty file to 
--pathspec-from-file. With no pathspecs on the command line commit, 
checkout, reset and restore-files all default to operating on all paths 
but passing --pathspec-from-file implies the user wants to specify 
specific paths so I think it would perhaps be better to error out if no 
paths are given. There is a precedent for this in checkout-index which 
does nothing if no paths are given (though I can't remember if it errors 
out or not).

> 
> 2) Moving parsing/validation into `parse_and_validate_options()`
> ------------------------
> Again, I agree that having parsing/validation outside is suboptimal.
> However, with current code, I find it to be a choice between two evils, 
> and my choice was "outside but clear" to "inside but obscure".
> 
> What I find obscure in your suggestion/patch is that innocently looking 
> `prepare_index()` suddenly clears pathspec as well. It's even harder to 
> see when called through `dry_run_commit()`.

It would be easy enough to clear pathspec in cmd_commit() I just didn't 
bother to do it.

> 
> Now, let me illustrate. There's a similar case in my TODO list, this 
> time involving a real bug in git. In `init_db()`, the bug occurs in 
> `set_git_dir(real_path(git_dir))`, also due to unexpected clearing.
> 
> Now that I pointed my finger at it, please try to find what's wrong. I 
> imagine that it won't be easy at all. And it's way harder when there's 
> no reason to dig deep into a very specific line of code.
> 
> I really try to avoid such type of pitfalls. That's why my choice 
> between two evils is what I did.

If I had to hazard a guess I'd say that either set_git_dir() calls 
real_path() which messes up the path passed to it or it does not copy 
the path passed to it and it is messed up by some other code calling 
real_path() after set_git_dir() has returned. If my guess is correct (I 
wouldn't be surprised if it's wrong) I think that's a bit different to 
the pathspec case as it's about the lifetime of the return value of a 
function rather than a function freeing an argument passed to it.

Best Wishes

Phillip

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 5/5] commit: support the --pathspec-from-file option
  2019-12-11 14:27           ` Phillip Wood
@ 2019-12-11 15:06             ` Alexandr Miloslavskiy
  2019-12-11 16:14               ` Junio C Hamano
  2019-12-12 14:56             ` Alexandr Miloslavskiy
  1 sibling, 1 reply; 80+ messages in thread
From: Alexandr Miloslavskiy @ 2019-12-11 15:06 UTC (permalink / raw)
  To: phillip.wood, Alexandr Miloslavskiy via GitGitGadget, git; +Cc: Junio C Hamano

On 11.12.2019 15:27, Phillip Wood wrote:

> Thanks, one other thing I forgot to mention yesterday is to ask what the 
> expected behavior is if the user passes an empty file to 
> --pathspec-from-file. With no pathspecs on the command line commit, 
> checkout, reset and restore-files all default to operating on all paths 
> but passing --pathspec-from-file implies the user wants to specify 
> specific paths so I think it would perhaps be better to error out if no 
> paths are given. There is a precedent for this in checkout-index which 
> does nothing if no paths are given (though I can't remember if it errors 
> out or not).

Back when I composed patch for `git commit`, I spent some time thinking 
about this question. I agree that empty `--pathspec-from-file` is weird. 
Eventually I decided that `--pathspec-from-file` shall be as close as 
possible to passing pathspec args. So empty file should behave the same 
way as passing zero pathspec args. It's more or less the question "if 
user does something unexpected, what is expected?". I decided that 
treating as zero args doesn't sound dangerous to me, and it's a very 
weird case anyway, so let's just treat as zero args.

>> What I find obscure in your suggestion/patch is that innocently 
>> looking `prepare_index()` suddenly clears pathspec as well. It's even 
>> harder to see when called through `dry_run_commit()`.
> 
> It would be easy enough to clear pathspec in cmd_commit() I just didn't 
> bother to do it.

Yes, this thought also came to me after I replied. I agree that this is 
viable and seems to solve most problems.

Now I'm in the situation where before me, code already wasn't perfect, 
and I can continue to extend it in the same direction, or try to 
refactor to make it better. I actually choose the second path: [1][2]. 
These were two "don't" lessons for me, as both topics now gather dust in 
mail graveyard. No good deed remains unpunished, I guess?

> I think that's a bit different to 
> the pathspec case as it's about the lifetime of the return value of a 
> function rather than a function freeing an argument passed to it.

Both cases are about lifetime that ends where it was not expected. You 
can probably agree that it never makes code easier to understand, and at 
best it will be properly accounted for and does not explode. Again, both 
choices were somewhat evil.

[1] 
https://public-inbox.org/git/pull.479.git.1574969538.gitgitgadget@gmail.com/
[2] 
https://public-inbox.org/git/pull.477.git.1574848137.gitgitgadget@gmail.com/T/#u

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 5/5] commit: support the --pathspec-from-file option
  2019-12-11 15:06             ` Alexandr Miloslavskiy
@ 2019-12-11 16:14               ` Junio C Hamano
  2019-12-11 16:20                 ` Alexandr Miloslavskiy
  0 siblings, 1 reply; 80+ messages in thread
From: Junio C Hamano @ 2019-12-11 16:14 UTC (permalink / raw)
  To: Alexandr Miloslavskiy
  Cc: phillip.wood, Alexandr Miloslavskiy via GitGitGadget, git

Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> writes:

> and I can continue to extend it in the same direction, or try to
> refactor to make it better. I actually choose the second path:
> [1][2]. These were two "don't" lessons for me, as both topics now
> gather dust in mail graveyard.

If nobody commented on your patches, that is not a punishment.  It
could have been that reviewers were busy addressing other issues
back then, in which case a gentle ping by resending a polished
version (it could be that the changes were not presented well to
attract reviewers' attention---polishing the proposed log messages
without changing the patch text might be all it takes to make them
realize that the topic is worth looking at) would help.

> [1]
> https://public-inbox.org/git/pull.479.git.1574969538.gitgitgadget@gmail.com/
> [2]
> https://public-inbox.org/git/pull.477.git.1574848137.gitgitgadget@gmail.com/T/#u

I actually was meaning to read and comment on [1], but lack of time
pushed it down in my queue.  Sorry about that.


^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 5/5] commit: support the --pathspec-from-file option
  2019-12-11 16:14               ` Junio C Hamano
@ 2019-12-11 16:20                 ` Alexandr Miloslavskiy
  0 siblings, 0 replies; 80+ messages in thread
From: Alexandr Miloslavskiy @ 2019-12-11 16:20 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: phillip.wood, Alexandr Miloslavskiy via GitGitGadget, git

On 11.12.2019 17:14, Junio C Hamano wrote:
> If nobody commented on your patches, that is not a punishment.  It
> could have been that reviewers were busy addressing other issues
> back then, in which case a gentle ping by resending a polished
> version (it could be that the changes were not presented well to
> attract reviewers' attention---polishing the proposed log messages
> without changing the patch text might be all it takes to make them
> realize that the topic is worth looking at) would help.

Thanks for letting me know! This is actually a relief.

I will then merge both topics with my next '--pathspec-from-file' batch, 
because they are all parts of one work. I just thought that since they 
can be submitted separately I should do that, maybe that was a mistake.

^ permalink raw reply	[flat|nested] 80+ messages in thread

* Re: [PATCH 5/5] commit: support the --pathspec-from-file option
  2019-12-11 14:27           ` Phillip Wood
  2019-12-11 15:06             ` Alexandr Miloslavskiy
@ 2019-12-12 14:56             ` Alexandr Miloslavskiy
  1 sibling, 0 replies; 80+ messages in thread
From: Alexandr Miloslavskiy @ 2019-12-12 14:56 UTC (permalink / raw)
  To: phillip.wood, Alexandr Miloslavskiy via GitGitGadget, git; +Cc: Junio C Hamano

The new topic is now submitted:
https://lore.kernel.org/git/pull.490.git.1576161385.gitgitgadget@gmail.com/
					
Phillip, I tried to CC you, but GitGitGadget did something weird.
I'm currently trying to fix CC issues there:
https://github.com/gitgitgadget/gitgitgadget/pull/163

^ permalink raw reply	[flat|nested] 80+ messages in thread

end of thread, back to index

Thread overview: 80+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-11-04 19:26 [PATCH 0/5] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget
2019-11-04 19:26 ` [PATCH 1/5] pathspec: add new function to parse file Alexandr Miloslavskiy via GitGitGadget
2019-11-05 15:02   ` Phillip Wood
2019-11-05 19:14     ` Alexandr Miloslavskiy
2019-11-06 15:56     ` Alexandr Miloslavskiy
2019-11-04 19:26 ` [PATCH 2/5] doc: reset: unify <pathspec> description Alexandr Miloslavskiy via GitGitGadget
2019-11-06  4:01   ` Junio C Hamano
2019-11-06 15:56     ` Alexandr Miloslavskiy
2019-11-07  5:46       ` Junio C Hamano
2019-11-07 11:05         ` Alexandr Miloslavskiy
2019-11-08  3:04           ` Junio C Hamano
2019-11-04 19:26 ` [PATCH 3/5] reset: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
2019-11-05 15:03   ` Phillip Wood
2019-11-05 19:22     ` Phillip Wood
2019-11-05 19:36     ` Alexandr Miloslavskiy
2019-11-06 15:56     ` Alexandr Miloslavskiy
2019-11-05 16:14   ` Phillip Wood
2019-11-05 19:37     ` Alexandr Miloslavskiy
2019-11-06  4:40   ` Junio C Hamano
2019-11-06 15:56     ` Alexandr Miloslavskiy
2019-11-04 19:26 ` [PATCH 4/5] doc: commit: unify <pathspec> description Alexandr Miloslavskiy via GitGitGadget
2019-11-06  4:50   ` Junio C Hamano
2019-11-06 15:56     ` Alexandr Miloslavskiy
2019-11-07  5:54       ` Junio C Hamano
2019-11-07 11:39         ` Alexandr Miloslavskiy
2019-11-04 19:26 ` [PATCH 5/5] commit: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
2019-11-05 16:27   ` Phillip Wood
2019-11-05 19:42     ` Alexandr Miloslavskiy
2019-11-06 15:56     ` Alexandr Miloslavskiy
2019-12-10 10:42       ` Phillip Wood
2019-12-11 11:43         ` Alexandr Miloslavskiy
2019-12-11 14:27           ` Phillip Wood
2019-12-11 15:06             ` Alexandr Miloslavskiy
2019-12-11 16:14               ` Junio C Hamano
2019-12-11 16:20                 ` Alexandr Miloslavskiy
2019-12-12 14:56             ` Alexandr Miloslavskiy
2019-11-06  4:51   ` Junio C Hamano
2019-11-06 15:51 ` [PATCH v2 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget
2019-11-06 15:51   ` [PATCH v2 1/6] parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul` Alexandr Miloslavskiy via GitGitGadget
2019-11-06 15:51   ` [PATCH v2 2/6] pathspec: add new function to parse file Alexandr Miloslavskiy via GitGitGadget
2019-11-19  5:59     ` Junio C Hamano
2019-11-19 16:50       ` Alexandr Miloslavskiy
2019-11-06 15:51   ` [PATCH v2 3/6] doc: reset: unify <pathspec> description Alexandr Miloslavskiy via GitGitGadget
2019-11-19  6:05     ` Junio C Hamano
2019-11-19 16:52       ` Alexandr Miloslavskiy
2019-11-06 15:51   ` [PATCH v2 4/6] reset: support the `--pathspec-from-file` option Alexandr Miloslavskiy via GitGitGadget
2019-11-06 15:51   ` [PATCH v2 5/6] doc: commit: unify <pathspec> description Alexandr Miloslavskiy via GitGitGadget
2019-11-19  6:16     ` Junio C Hamano
2019-11-19 16:53       ` Alexandr Miloslavskiy
2019-11-19 17:02       ` Alexandr Miloslavskiy
2019-11-06 15:51   ` [PATCH v2 6/6] commit: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
2019-11-19  6:10     ` Junio C Hamano
2019-11-19 16:56       ` Alexandr Miloslavskiy
2019-11-19 16:48   ` [PATCH v3 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget
2019-11-19 16:48     ` [PATCH v3 1/6] parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul` Alexandr Miloslavskiy via GitGitGadget
2019-11-19 16:48     ` [PATCH v3 2/6] pathspec: add new function to parse file Alexandr Miloslavskiy via GitGitGadget
2019-11-19 16:48     ` [PATCH v3 3/6] doc: reset: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget
2019-11-19 16:48     ` [PATCH v3 4/6] reset: support the `--pathspec-from-file` option Alexandr Miloslavskiy via GitGitGadget
2019-11-19 16:48     ` [PATCH v3 5/6] doc: commit: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget
2019-11-19 16:48     ` [PATCH v3 6/6] commit: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
2019-11-20  4:04     ` [PATCH v3 0/6] Add --pathspec-from-file option for reset, commit Junio C Hamano
2019-11-20  9:22       ` Alexandr Miloslavskiy
2019-12-03 14:02     ` [PATCH v4 00/13] Add --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
2019-12-03 14:02       ` [PATCH v4 01/13] parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul` Alexandr Miloslavskiy via GitGitGadget
2019-12-03 14:02       ` [PATCH v4 02/13] pathspec: add new function to parse file Alexandr Miloslavskiy via GitGitGadget
2019-12-03 14:02       ` [PATCH v4 03/13] doc: reset: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget
2019-12-03 14:02       ` [PATCH v4 04/13] reset: support the `--pathspec-from-file` option Alexandr Miloslavskiy via GitGitGadget
2019-12-03 14:02       ` [PATCH v4 05/13] doc: commit: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget
2019-12-03 14:02       ` [PATCH v4 06/13] commit: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
2019-12-03 14:02       ` [PATCH v4 07/13] cmd_add: prepare for next patch Alexandr Miloslavskiy via GitGitGadget
2019-12-03 14:02       ` [PATCH v4 08/13] add: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
2019-12-03 14:02       ` [PATCH v4 09/13] doc: checkout: remove duplicate synopsis Alexandr Miloslavskiy via GitGitGadget
2019-12-03 14:02       ` [PATCH v4 10/13] doc: checkout: fix broken text reference Alexandr Miloslavskiy via GitGitGadget
2019-12-03 14:02       ` [PATCH v4 11/13] doc: checkout: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget
2019-12-03 14:02       ` [PATCH v4 12/13] doc: restore: " Alexandr Miloslavskiy via GitGitGadget
2019-12-03 14:02       ` [PATCH v4 13/13] checkout, restore: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
2019-12-03 16:55       ` [PATCH v4 00/13] Add " Junio C Hamano
2019-12-03 17:06         ` Alexandr Miloslavskiy
2019-12-04 19:25           ` Junio C Hamano
2019-12-05 10:43             ` Alexandr Miloslavskiy

git@vger.kernel.org list mirror (unofficial, one of many)

Archives are clonable:
	git clone --mirror https://public-inbox.org/git
	git clone --mirror http://ou63pmih66umazou.onion/git
	git clone --mirror http://czquwvybam4bgbro.onion/git
	git clone --mirror http://hjrcffqmbrq6wope.onion/git

Example config snippet for mirrors

Newsgroups are available over NNTP:
	nntp://news.public-inbox.org/inbox.comp.version-control.git
	nntp://ou63pmih66umazou.onion/inbox.comp.version-control.git
	nntp://czquwvybam4bgbro.onion/inbox.comp.version-control.git
	nntp://hjrcffqmbrq6wope.onion/inbox.comp.version-control.git
	nntp://news.gmane.org/gmane.comp.version-control.git

 note: .onion URLs require Tor: https://www.torproject.org/

AGPL code for this site: git clone https://public-inbox.org/public-inbox.git