git@vger.kernel.org list mirror (unofficial, one of many)
 help / color / mirror / Atom feed
* [PATCH 0/8] Support --pathspec-from-file in rm, stash
@ 2020-01-16 16:09 Alexandr Miloslavskiy via GitGitGadget
  2020-01-16 16:09 ` [PATCH 1/8] doc: rm: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget
                   ` (8 more replies)
  0 siblings, 9 replies; 41+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2020-01-16 16:09 UTC (permalink / raw)
  To: git; +Cc: Paul-Sebastian Ungureanu, Alexandr Miloslavskiy

This series continues the effort to support `--pathspec-from-file`
in various git commands. Series already in `master`: [1][2]

Cc'ing Paul-Sebastian Ungureanu because I touched his git stash code.

[1] https://public-inbox.org/git/pull.445.git.1572895605.gitgitgadget@gmail.com/
[2] https://lore.kernel.org/git/pull.490.git.1576161385.gitgitgadget@gmail.com/

Alexandr Miloslavskiy (8):
  doc: rm: synchronize <pathspec> description
  rm: support the --pathspec-from-file option
  doc: stash: split options from description (1)
  doc: stash: split options from description (2)
  doc: stash: document more options
  doc: stash: synchronize <pathspec> description
  stash: eliminate crude option parsing
  stash push: support the --pathspec-from-file option

 Documentation/git-rm.txt       |  61 ++++++++++--------
 Documentation/git-stash.txt    | 111 +++++++++++++++++++++------------
 builtin/rm.c                   |  28 +++++++--
 builtin/stash.c                |  79 ++++++++++++-----------
 t/t3601-rm-pathspec-file.sh    |  79 +++++++++++++++++++++++
 t/t3903-stash.sh               |   5 ++
 t/t3909-stash-pathspec-file.sh | 100 +++++++++++++++++++++++++++++
 7 files changed, 353 insertions(+), 110 deletions(-)
 create mode 100755 t/t3601-rm-pathspec-file.sh
 create mode 100755 t/t3909-stash-pathspec-file.sh


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

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

* [PATCH 1/8] doc: rm: synchronize <pathspec> description
  2020-01-16 16:09 [PATCH 0/8] Support --pathspec-from-file in rm, stash Alexandr Miloslavskiy via GitGitGadget
@ 2020-01-16 16:09 ` Alexandr Miloslavskiy via GitGitGadget
  2020-01-21 19:14   ` Junio C Hamano
  2020-01-16 16:09 ` [PATCH 2/8] rm: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
                   ` (7 subsequent siblings)
  8 siblings, 1 reply; 41+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2020-01-16 16:09 UTC (permalink / raw)
  To: git
  Cc: Paul-Sebastian Ungureanu, Alexandr Miloslavskiy, Alexandr Miloslavskiy

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

This patch continues the effort that is already applied to
`git commit`, `git reset`, `git checkout` etc.

1) Changed outdated descriptions to mention pathspec instead.
2) Added reference to 'linkgit:gitglossary[7]'.
3) Removed content that merely repeated gitglossary.
4) Merged the remainder of "discussion" into `<patchspec>`.

Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 Documentation/git-rm.txt | 50 +++++++++++++++++-----------------------
 1 file changed, 21 insertions(+), 29 deletions(-)

diff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt
index b5c46223c4..e02a08e5ef 100644
--- a/Documentation/git-rm.txt
+++ b/Documentation/git-rm.txt
@@ -8,16 +8,16 @@ git-rm - Remove files from the working tree and from the index
 SYNOPSIS
 --------
 [verse]
-'git rm' [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] <file>...
+'git rm' [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] <pathspec>...
 
 DESCRIPTION
 -----------
-Remove files from the index, or from the working tree and the index.
-`git rm` will not remove a file from just your working directory.
-(There is no option to remove a file only from the working tree
-and yet keep it in the index; use `/bin/rm` if you want to do that.)
-The files being removed have to be identical to the tip of the branch,
-and no updates to their contents can be staged in the index,
+Remove files matching pathspec from the index, or from the working tree
+and the index. `git rm` will not remove a file from just your working
+directory. (There is no option to remove a file only from the working
+tree and yet keep it in the index; use `/bin/rm` if you want to do
+that.) The files being removed have to be identical to the tip of the
+branch, and no updates to their contents can be staged in the index,
 though that default behavior can be overridden with the `-f` option.
 When `--cached` is given, the staged content has to
 match either the tip of the branch or the file on disk,
@@ -26,15 +26,20 @@ allowing the file to be removed from just the index.
 
 OPTIONS
 -------
-<file>...::
-	Files to remove.  Fileglobs (e.g. `*.c`) can be given to
-	remove all matching files.  If you want Git to expand
-	file glob characters, you may need to shell-escape them.
-	A leading directory name
-	(e.g. `dir` to remove `dir/file1` and `dir/file2`) can be
-	given to remove all files in the directory, and recursively
-	all sub-directories,
-	but this requires the `-r` option to be explicitly given.
+<pathspec>...::
+	Files to remove.  A leading directory name (e.g. `dir` to remove
+	`dir/file1` and `dir/file2`) can be given to remove all files in
+	the directory, and recursively all sub-directories, but this
+	requires the `-r` option to be explicitly given.
++
+The command removes only the paths that are known to Git.
++
+File globbing matches across directory boundaries.  Thus, given two
+directories `d` and `d2`, there is a difference between using
+`git rm 'd*'` and `git rm 'd/*'`, as the former will also remove all
+of directory `d2`.
++
+For more details, see the 'pathspec' entry in linkgit:gitglossary[7].
 
 -f::
 --force::
@@ -69,19 +74,6 @@ OPTIONS
 	for each file removed. This option suppresses that output.
 
 
-DISCUSSION
-----------
-
-The <file> list given to the command can be exact pathnames,
-file glob patterns, or leading directory names.  The command
-removes only the paths that are known to Git.  Giving the name of
-a file that you have not told Git about does not remove that file.
-
-File globbing matches across directory boundaries.  Thus, given
-two directories `d` and `d2`, there is a difference between
-using `git rm 'd*'` and `git rm 'd/*'`, as the former will
-also remove all of directory `d2`.
-
 REMOVING FILES THAT HAVE DISAPPEARED FROM THE FILESYSTEM
 --------------------------------------------------------
 There is no option for `git rm` to remove from the index only
-- 
gitgitgadget


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

* [PATCH 2/8] rm: support the --pathspec-from-file option
  2020-01-16 16:09 [PATCH 0/8] Support --pathspec-from-file in rm, stash Alexandr Miloslavskiy via GitGitGadget
  2020-01-16 16:09 ` [PATCH 1/8] doc: rm: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget
@ 2020-01-16 16:09 ` Alexandr Miloslavskiy via GitGitGadget
  2020-01-21 19:36   ` Junio C Hamano
  2020-01-16 16:09 ` [PATCH 3/8] doc: stash: split options from description (1) Alexandr Miloslavskiy via GitGitGadget
                   ` (6 subsequent siblings)
  8 siblings, 1 reply; 41+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2020-01-16 16:09 UTC (permalink / raw)
  To: git
  Cc: Paul-Sebastian Ungureanu, Alexandr Miloslavskiy, Alexandr Miloslavskiy

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

Decisions taken for simplicity:
1) It is not allowed to pass pathspec in both args and file.

`if (!argc)` block was adapted to work with --pathspec-from-file. For
that, I also had to parse pathspec earlier. Now it happens before
`read_cache()` / `hold_locked_index()` / `setup_work_tree()`, which
sounds fine to me.

In case of empty pathspec, there is now a clear error message instead
of showing usage. As a consequence, exit code has also changed. Judging
from [1] it doesn't seem that showing usage in this case was important
(according to commit message, it was to avoid segfault), and it doesn't
fit into how other commands react to empty pathspec. Finally, the new
error message is easier to understand.

[1] Commit 7612a1ef ("git-rm: honor -n flag" 2006-06-09)

Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 Documentation/git-rm.txt    | 17 +++++++-
 builtin/rm.c                | 28 ++++++++++---
 t/t3601-rm-pathspec-file.sh | 79 +++++++++++++++++++++++++++++++++++++
 3 files changed, 117 insertions(+), 7 deletions(-)
 create mode 100755 t/t3601-rm-pathspec-file.sh

diff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt
index e02a08e5ef..ab750367fd 100644
--- a/Documentation/git-rm.txt
+++ b/Documentation/git-rm.txt
@@ -8,7 +8,9 @@ git-rm - Remove files from the working tree and from the index
 SYNOPSIS
 --------
 [verse]
-'git rm' [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] <pathspec>...
+'git rm' [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch]
+	  [--quiet] [--pathspec-from-file=<file> [--pathspec-file-nul]]
+	  [--] [<pathspec>...]
 
 DESCRIPTION
 -----------
@@ -73,6 +75,19 @@ For more details, see the 'pathspec' entry in linkgit:gitglossary[7].
 	`git rm` normally outputs one line (in the form of an `rm` command)
 	for each file removed. This option suppresses that output.
 
+--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).
+
 
 REMOVING FILES THAT HAVE DISAPPEARED FROM THE FILESYSTEM
 --------------------------------------------------------
diff --git a/builtin/rm.c b/builtin/rm.c
index 19ce95a901..8e40795751 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -235,7 +235,8 @@ static int check_local_mod(struct object_id *head, int index_only)
 }
 
 static int show_only = 0, force = 0, index_only = 0, recursive = 0, quiet = 0;
-static int ignore_unmatch = 0;
+static int ignore_unmatch = 0, pathspec_file_nul = 0;
+static char *pathspec_from_file = NULL;
 
 static struct option builtin_rm_options[] = {
 	OPT__DRY_RUN(&show_only, N_("dry run")),
@@ -245,6 +246,8 @@ static struct option builtin_rm_options[] = {
 	OPT_BOOL('r', NULL,             &recursive,  N_("allow recursive removal")),
 	OPT_BOOL( 0 , "ignore-unmatch", &ignore_unmatch,
 				N_("exit with a zero status even if nothing matched")),
+	OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
+	OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
 	OPT_END(),
 };
 
@@ -259,8 +262,24 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
 
 	argc = parse_options(argc, argv, prefix, builtin_rm_options,
 			     builtin_rm_usage, 0);
-	if (!argc)
-		usage_with_options(builtin_rm_usage, builtin_rm_options);
+
+	parse_pathspec(&pathspec, 0,
+		       PATHSPEC_PREFER_CWD,
+		       prefix, argv);
+
+	if (pathspec_from_file) {
+		if (pathspec.nr)
+			die(_("--pathspec-from-file is incompatible with pathspec arguments"));
+
+		parse_pathspec_file(&pathspec, 0,
+				    PATHSPEC_PREFER_CWD,
+				    prefix, pathspec_from_file, pathspec_file_nul);
+	} else if (pathspec_file_nul) {
+		die(_("--pathspec-file-nul requires --pathspec-from-file"));
+	}
+
+	if (!pathspec.nr)
+		die(_("Nothing specified, nothing removed"));
 
 	if (!index_only)
 		setup_work_tree();
@@ -270,9 +289,6 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
 	if (read_cache() < 0)
 		die(_("index file corrupt"));
 
-	parse_pathspec(&pathspec, 0,
-		       PATHSPEC_PREFER_CWD,
-		       prefix, argv);
 	refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &pathspec, NULL, NULL);
 
 	seen = xcalloc(pathspec.nr, 1);
diff --git a/t/t3601-rm-pathspec-file.sh b/t/t3601-rm-pathspec-file.sh
new file mode 100755
index 0000000000..0f69ae8478
--- /dev/null
+++ b/t/t3601-rm-pathspec-file.sh
@@ -0,0 +1,79 @@
+#!/bin/sh
+
+test_description='rm --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 fileA.t fileB.t fileC.t fileD.t &&
+	git commit -m "files" &&
+	
+	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 'simplest' '
+	restore_checkpoint &&
+
+	cat >expect <<-\EOF &&
+	D  fileA.t
+	EOF
+
+	echo fileA.t | git rm --pathspec-from-file=- &&
+	verify_expect
+'
+
+test_expect_success '--pathspec-file-nul' '
+	restore_checkpoint &&
+
+	cat >expect <<-\EOF &&
+	D  fileA.t
+	D  fileB.t
+	EOF
+
+	printf "fileA.t\0fileB.t\0" | git rm --pathspec-from-file=- --pathspec-file-nul &&
+	verify_expect
+'
+
+test_expect_success 'only touches what was listed' '
+	restore_checkpoint &&
+
+	cat >expect <<-\EOF &&
+	D  fileB.t
+	D  fileC.t
+	EOF
+
+	printf "fileB.t\nfileC.t\n" | git rm --pathspec-from-file=- &&
+	verify_expect
+'
+
+test_expect_success 'error conditions' '
+	restore_checkpoint &&
+	echo fileA.t >list &&
+
+	test_must_fail git rm --pathspec-from-file=list -- fileA.t 2>err &&
+	test_i18ngrep -e "--pathspec-from-file is incompatible with pathspec arguments" err &&
+
+	test_must_fail git rm --pathspec-file-nul 2>err &&
+	test_i18ngrep -e "--pathspec-file-nul requires --pathspec-from-file" err &&
+	
+	>empty_list &&
+	test_must_fail git rm --pathspec-from-file=empty_list 2>err &&
+	test_i18ngrep -e "Nothing specified, nothing removed" err
+'
+
+test_done
-- 
gitgitgadget


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

* [PATCH 3/8] doc: stash: split options from description (1)
  2020-01-16 16:09 [PATCH 0/8] Support --pathspec-from-file in rm, stash Alexandr Miloslavskiy via GitGitGadget
  2020-01-16 16:09 ` [PATCH 1/8] doc: rm: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget
  2020-01-16 16:09 ` [PATCH 2/8] rm: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
@ 2020-01-16 16:09 ` Alexandr Miloslavskiy via GitGitGadget
  2020-01-16 16:09 ` [PATCH 4/8] doc: stash: split options from description (2) Alexandr Miloslavskiy via GitGitGadget
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 41+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2020-01-16 16:09 UTC (permalink / raw)
  To: git
  Cc: Paul-Sebastian Ungureanu, Alexandr Miloslavskiy, Alexandr Miloslavskiy

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

This patch moves blocks of text as-is to make it easier to review the
next patch.

Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 Documentation/git-stash.txt | 68 +++++++++++++++++++------------------
 1 file changed, 35 insertions(+), 33 deletions(-)

diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
index 53e1a1205d..2dedc21997 100644
--- a/Documentation/git-stash.txt
+++ b/Documentation/git-stash.txt
@@ -58,31 +58,6 @@ non-option arguments are not allowed to prevent a misspelled
 subcommand from making an unwanted stash entry.  The two exceptions to this
 are `stash -p` which acts as alias for `stash push -p` and pathspecs,
 which are allowed after a double hyphen `--` for disambiguation.
-+
-When pathspec is given to 'git stash push', the new stash entry records the
-modified states only for the files that match the pathspec.  The index
-entries and working tree files are then rolled back to the state in
-HEAD only for these files, too, leaving files that do not match the
-pathspec intact.
-+
-If the `--keep-index` option is used, all changes already added to the
-index are left intact.
-+
-If the `--include-untracked` option is used, all untracked files are also
-stashed and then cleaned up with `git clean`, leaving the working directory
-in a very clean state. If the `--all` option is used instead then the
-ignored files are stashed and cleaned in addition to the untracked files.
-+
-With `--patch`, you can interactively select hunks from the diff
-between HEAD and the working tree to be stashed.  The stash entry is
-constructed such that its index state is the same as the index state
-of your repository, and its worktree contains only the changes you
-selected interactively.  The selected changes are then rolled back
-from your worktree. See the ``Interactive Mode'' section of
-linkgit:git-add[1] to learn how to operate the `--patch` mode.
-+
-The `--patch` option implies `--keep-index`.  You can use
-`--no-keep-index` to override this.
 
 save [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [<message>]::
 
@@ -128,14 +103,6 @@ pop [--index] [-q|--quiet] [<stash>]::
 Applying the state can fail with conflicts; in this case, it is not
 removed from the stash list. You need to resolve the conflicts by hand
 and call `git stash drop` manually afterwards.
-+
-If the `--index` option is used, then tries to reinstate not only the working
-tree's changes, but also the index's ones. However, this can fail, when you
-have conflicts (which are stored in the index, where you therefore can no
-longer apply the changes as they were originally).
-+
-When no `<stash>` is given, `stash@{0}` is assumed, otherwise `<stash>` must
-be a reference of the form `stash@{<revision>}`.
 
 apply [--index] [-q|--quiet] [<stash>]::
 
@@ -185,6 +152,41 @@ store::
 	reflog.  This is intended to be useful for scripts.  It is
 	probably not the command you want to use; see "push" above.
 
+If the `--all` option is used instead then the
+ignored files are stashed and cleaned in addition to the untracked files.
+
+If the `--include-untracked` option is used, all untracked files are also
+stashed and then cleaned up with `git clean`, leaving the working directory
+in a very clean state.
+
+If the `--index` option is used, then tries to reinstate not only the working
+tree's changes, but also the index's ones. However, this can fail, when you
+have conflicts (which are stored in the index, where you therefore can no
+longer apply the changes as they were originally).
+
+If the `--keep-index` option is used, all changes already added to the
+index are left intact.
+
+With `--patch`, you can interactively select hunks from the diff
+between HEAD and the working tree to be stashed.  The stash entry is
+constructed such that its index state is the same as the index state
+of your repository, and its worktree contains only the changes you
+selected interactively.  The selected changes are then rolled back
+from your worktree. See the ``Interactive Mode'' section of
+linkgit:git-add[1] to learn how to operate the `--patch` mode.
++
+The `--patch` option implies `--keep-index`.  You can use 
+`--no-keep-index` to override this.
+
+When pathspec is given to 'git stash push', the new stash entry records the
+modified states only for the files that match the pathspec.  The index
+entries and working tree files are then rolled back to the state in
+HEAD only for these files, too, leaving files that do not match the
+pathspec intact.
+
+When no `<stash>` is given, `stash@{0}` is assumed, otherwise `<stash>` must
+be a reference of the form `stash@{<revision>}`.
+
 DISCUSSION
 ----------
 
-- 
gitgitgadget


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

* [PATCH 4/8] doc: stash: split options from description (2)
  2020-01-16 16:09 [PATCH 0/8] Support --pathspec-from-file in rm, stash Alexandr Miloslavskiy via GitGitGadget
                   ` (2 preceding siblings ...)
  2020-01-16 16:09 ` [PATCH 3/8] doc: stash: split options from description (1) Alexandr Miloslavskiy via GitGitGadget
@ 2020-01-16 16:09 ` Alexandr Miloslavskiy via GitGitGadget
  2020-01-21 20:21   ` Junio C Hamano
  2020-01-16 16:09 ` [PATCH 5/8] doc: stash: document more options Alexandr Miloslavskiy via GitGitGadget
                   ` (4 subsequent siblings)
  8 siblings, 1 reply; 41+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2020-01-16 16:09 UTC (permalink / raw)
  To: git
  Cc: Paul-Sebastian Ungureanu, Alexandr Miloslavskiy, Alexandr Miloslavskiy

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

Together with the previous patch, this brings docs for `git stash` to
the common layout used for most other commands (see for example docs for
`git add`, `git commit`, `git checkout`, `git reset`) where all options
are documented in a separate list.

I have decided to use alphabetical sorting in the list of options. Other
docs often sort in order of appearance or order of importance, but in
this case it wouldn't be easy to read the list where options from
multiple sub-commands are mixed together.

There is some text editing done to make old descriptions better fit into
the list-style format.

Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 Documentation/git-stash.txt | 72 ++++++++++++++++++++-----------------
 1 file changed, 40 insertions(+), 32 deletions(-)

diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
index 2dedc21997..f75b80a720 100644
--- a/Documentation/git-stash.txt
+++ b/Documentation/git-stash.txt
@@ -43,9 +43,6 @@ created stash, `stash@{1}` is the one before it, `stash@{2.hours.ago}`
 is also possible). Stashes may also be referenced by specifying just the
 stash index (e.g. the integer `n` is equivalent to `stash@{n}`).
 
-OPTIONS
--------
-
 push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--] [<pathspec>...]::
 
 	Save your local modifications to a new 'stash entry' and roll them
@@ -152,40 +149,51 @@ store::
 	reflog.  This is intended to be useful for scripts.  It is
 	probably not the command you want to use; see "push" above.
 
-If the `--all` option is used instead then the
-ignored files are stashed and cleaned in addition to the untracked files.
-
-If the `--include-untracked` option is used, all untracked files are also
-stashed and then cleaned up with `git clean`, leaving the working directory
-in a very clean state.
-
-If the `--index` option is used, then tries to reinstate not only the working
-tree's changes, but also the index's ones. However, this can fail, when you
-have conflicts (which are stored in the index, where you therefore can no
-longer apply the changes as they were originally).
-
-If the `--keep-index` option is used, all changes already added to the
-index are left intact.
-
-With `--patch`, you can interactively select hunks from the diff
-between HEAD and the working tree to be stashed.  The stash entry is
-constructed such that its index state is the same as the index state
-of your repository, and its worktree contains only the changes you
-selected interactively.  The selected changes are then rolled back
-from your worktree. See the ``Interactive Mode'' section of
-linkgit:git-add[1] to learn how to operate the `--patch` mode.
+OPTIONS
+-------
+-a::
+--all::
+	All ignored and untracked files are also stashed and then cleaned
+	up with `git clean`.
+
+-u::
+--include-untracked::
+	All untracked files are also stashed and then cleaned up with
+	`git clean`.
+
+--index::
+	Tries to reinstate not only the working tree's changes, but also
+	the index's ones. However, this can fail, when you have conflicts
+	(which are stored in the index, where you therefore can no longer
+	apply the changes as they were originally).
+
+-k::
+--keep-index::
+--no-keep-index::
+	All changes already added to the index are left intact.
+
+-p::
+--patch::
+	Interactively select hunks from the diff between HEAD and the
+	working tree to be stashed.  The stash entry is constructed such
+	that its index state is the same as the index state of your
+	repository, and its worktree contains only the changes you selected
+	interactively.  The selected changes are then rolled back from your
+	worktree. See the ``Interactive Mode'' section of linkgit:git-add[1]
+	to learn how to operate the `--patch` mode.
 +
 The `--patch` option implies `--keep-index`.  You can use 
 `--no-keep-index` to override this.
 
-When pathspec is given to 'git stash push', the new stash entry records the
-modified states only for the files that match the pathspec.  The index
-entries and working tree files are then rolled back to the state in
-HEAD only for these files, too, leaving files that do not match the
-pathspec intact.
+<pathspec>...::
+	The new stash entry records the modified states only for the files
+	that match the pathspec.  The index entries and working tree files
+	are then rolled back to the state in HEAD only for these files,
+	too, leaving files that do not match the pathspec intact.
 
-When no `<stash>` is given, `stash@{0}` is assumed, otherwise `<stash>` must
-be a reference of the form `stash@{<revision>}`.
+<stash>::
+	A reference of the form `stash@{<revision>}`. When no `<stash>` is
+	given, the latest stash is assumed (that is, `stash@{0}`).
 
 DISCUSSION
 ----------
-- 
gitgitgadget


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

* [PATCH 5/8] doc: stash: document more options
  2020-01-16 16:09 [PATCH 0/8] Support --pathspec-from-file in rm, stash Alexandr Miloslavskiy via GitGitGadget
                   ` (3 preceding siblings ...)
  2020-01-16 16:09 ` [PATCH 4/8] doc: stash: split options from description (2) Alexandr Miloslavskiy via GitGitGadget
@ 2020-01-16 16:09 ` Alexandr Miloslavskiy via GitGitGadget
  2020-01-21 20:29   ` Junio C Hamano
  2020-01-16 16:09 ` [PATCH 6/8] doc: stash: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget
                   ` (3 subsequent siblings)
  8 siblings, 1 reply; 41+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2020-01-16 16:09 UTC (permalink / raw)
  To: git
  Cc: Paul-Sebastian Ungureanu, Alexandr Miloslavskiy, Alexandr Miloslavskiy

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

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

diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
index f75b80a720..f5fa62dc7c 100644
--- a/Documentation/git-stash.txt
+++ b/Documentation/git-stash.txt
@@ -185,6 +185,13 @@ OPTIONS
 The `--patch` option implies `--keep-index`.  You can use 
 `--no-keep-index` to override this.
 
+-q::
+--quiet::
+	Quiet, suppress feedback messages.
+
+\--::
+	Separates pathspec from options for disambiguation purposes.
+
 <pathspec>...::
 	The new stash entry records the modified states only for the files
 	that match the pathspec.  The index entries and working tree files
-- 
gitgitgadget


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

* [PATCH 6/8] doc: stash: synchronize <pathspec> description
  2020-01-16 16:09 [PATCH 0/8] Support --pathspec-from-file in rm, stash Alexandr Miloslavskiy via GitGitGadget
                   ` (4 preceding siblings ...)
  2020-01-16 16:09 ` [PATCH 5/8] doc: stash: document more options Alexandr Miloslavskiy via GitGitGadget
@ 2020-01-16 16:09 ` Alexandr Miloslavskiy via GitGitGadget
  2020-01-21 20:29   ` Junio C Hamano
  2020-01-16 16:09 ` [PATCH 7/8] stash: eliminate crude option parsing Alexandr Miloslavskiy via GitGitGadget
                   ` (2 subsequent siblings)
  8 siblings, 1 reply; 41+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2020-01-16 16:09 UTC (permalink / raw)
  To: git
  Cc: Paul-Sebastian Ungureanu, Alexandr Miloslavskiy, Alexandr Miloslavskiy

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

This patch continues the effort that is already applied to
`git commit`, `git reset`, `git checkout` etc.

1) Added reference to 'linkgit:gitglossary[7]'.
2) Fixed mentions of incorrectly plural "pathspecs".

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

diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
index f5fa62dc7c..576ad757d9 100644
--- a/Documentation/git-stash.txt
+++ b/Documentation/git-stash.txt
@@ -53,13 +53,13 @@ push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q
 For quickly making a snapshot, you can omit "push".  In this mode,
 non-option arguments are not allowed to prevent a misspelled
 subcommand from making an unwanted stash entry.  The two exceptions to this
-are `stash -p` which acts as alias for `stash push -p` and pathspecs,
+are `stash -p` which acts as alias for `stash push -p` and pathspec elements,
 which are allowed after a double hyphen `--` for disambiguation.
 
 save [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [<message>]::
 
 	This option is deprecated in favour of 'git stash push'.  It
-	differs from "stash push" in that it cannot take pathspecs.
+	differs from "stash push" in that it cannot take pathspec.
 	Instead, all non-option arguments are concatenated to form the stash
 	message.
 
@@ -197,6 +197,8 @@ The `--patch` option implies `--keep-index`.  You can use
 	that match the pathspec.  The index entries and working tree files
 	are then rolled back to the state in HEAD only for these files,
 	too, leaving files that do not match the pathspec intact.
++
+For more details, see the 'pathspec' entry in linkgit:gitglossary[7].
 
 <stash>::
 	A reference of the form `stash@{<revision>}`. When no `<stash>` is
-- 
gitgitgadget


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

* [PATCH 7/8] stash: eliminate crude option parsing
  2020-01-16 16:09 [PATCH 0/8] Support --pathspec-from-file in rm, stash Alexandr Miloslavskiy via GitGitGadget
                   ` (5 preceding siblings ...)
  2020-01-16 16:09 ` [PATCH 6/8] doc: stash: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget
@ 2020-01-16 16:09 ` Alexandr Miloslavskiy via GitGitGadget
  2020-01-16 16:09 ` [PATCH 8/8] stash push: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
  2020-02-10 14:45 ` [PATCH v2 0/8] Support --pathspec-from-file in rm, stash Alexandr Miloslavskiy via GitGitGadget
  8 siblings, 0 replies; 41+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2020-01-16 16:09 UTC (permalink / raw)
  To: git
  Cc: Paul-Sebastian Ungureanu, Alexandr Miloslavskiy, Alexandr Miloslavskiy

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

Eliminate crude option parsing and rely on real parsing instead, because
1) Crude parsing is crude, for example it's not capable of
   handling things like `git stash -m Message`
2) Adding options in two places is inconvenient and prone to bugs

As a side result, the case of `git stash -m Message` gets fixed.
Also give a good error message instead of just throwing usage at user.

----

Some review of what's been happening to this code:

Before [1], `git-stash.sh` only verified that all args begin with `-` :

	# The default command is "push" if nothing but options are given
	seen_non_option=
	for opt
	do
		case "$opt" in
		--) break ;;
		-*) ;;
		*) seen_non_option=t; break ;;
		esac
	done

Later, [1] introduced the duplicate code I'm now removing, also making
the previous test more strict by white-listing options.

----

[1] Commit 40af1468 ("stash: convert `stash--helper.c` into `stash.c`" 2019-02-26)

Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 builtin/stash.c  | 59 +++++++++++++++++-------------------------------
 t/t3903-stash.sh |  5 ++++
 2 files changed, 26 insertions(+), 38 deletions(-)

diff --git a/builtin/stash.c b/builtin/stash.c
index 4ad3adf4ba..7bc4c5d696 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -1448,8 +1448,10 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
 	return ret;
 }
 
-static int push_stash(int argc, const char **argv, const char *prefix)
+static int push_stash(int argc, const char **argv, const char *prefix,
+		      int push_assumed)
 {
+	int force_assume = 0;
 	int keep_index = -1;
 	int patch_mode = 0;
 	int include_untracked = 0;
@@ -1471,10 +1473,22 @@ static int push_stash(int argc, const char **argv, const char *prefix)
 		OPT_END()
 	};
 
-	if (argc)
+	if (argc) {
+		force_assume = !strcmp(argv[0], "-p");
 		argc = parse_options(argc, argv, prefix, options,
 				     git_stash_push_usage,
-				     0);
+				     PARSE_OPT_KEEP_DASHDASH);
+	}
+
+	if (argc) {
+		if (!strcmp(argv[0], "--")) {
+			argc--;
+			argv++;
+		} else if (push_assumed && !force_assume) {
+			die("subcommand wasn't specified; 'push' can't be assumed due to unexpected token '%s'",
+			    argv[0]);
+		}
+	}
 
 	parse_pathspec(&ps, 0, PATHSPEC_PREFER_FULL | PATHSPEC_PREFIX_ORIGIN,
 		       prefix, argv);
@@ -1547,7 +1561,6 @@ static int use_builtin_stash(void)
 
 int cmd_stash(int argc, const char **argv, const char *prefix)
 {
-	int i = -1;
 	pid_t pid = getpid();
 	const char *index_file;
 	struct argv_array args = ARGV_ARRAY_INIT;
@@ -1580,7 +1593,7 @@ int cmd_stash(int argc, const char **argv, const char *prefix)
 		    (uintmax_t)pid);
 
 	if (!argc)
-		return !!push_stash(0, NULL, prefix);
+		return !!push_stash(0, NULL, prefix, 0);
 	else if (!strcmp(argv[0], "apply"))
 		return !!apply_stash(argc, argv, prefix);
 	else if (!strcmp(argv[0], "clear"))
@@ -1600,45 +1613,15 @@ int cmd_stash(int argc, const char **argv, const char *prefix)
 	else if (!strcmp(argv[0], "create"))
 		return !!create_stash(argc, argv, prefix);
 	else if (!strcmp(argv[0], "push"))
-		return !!push_stash(argc, argv, prefix);
+		return !!push_stash(argc, argv, prefix, 0);
 	else if (!strcmp(argv[0], "save"))
 		return !!save_stash(argc, argv, prefix);
 	else if (*argv[0] != '-')
 		usage_msg_opt(xstrfmt(_("unknown subcommand: %s"), argv[0]),
 			      git_stash_usage, options);
 
-	if (strcmp(argv[0], "-p")) {
-		while (++i < argc && strcmp(argv[i], "--")) {
-			/*
-			 * `akpqu` is a string which contains all short options,
-			 * except `-m` which is verified separately.
-			 */
-			if ((strlen(argv[i]) == 2) && *argv[i] == '-' &&
-			    strchr("akpqu", argv[i][1]))
-				continue;
-
-			if (!strcmp(argv[i], "--all") ||
-			    !strcmp(argv[i], "--keep-index") ||
-			    !strcmp(argv[i], "--no-keep-index") ||
-			    !strcmp(argv[i], "--patch") ||
-			    !strcmp(argv[i], "--quiet") ||
-			    !strcmp(argv[i], "--include-untracked"))
-				continue;
-
-			/*
-			 * `-m` and `--message=` are verified separately because
-			 * they need to be immediately followed by a string
-			 * (i.e.`-m"foobar"` or `--message="foobar"`).
-			 */
-			if (starts_with(argv[i], "-m") ||
-			    starts_with(argv[i], "--message="))
-				continue;
-
-			usage_with_options(git_stash_usage, options);
-		}
-	}
-
+	/* Assume 'stash push' */
 	argv_array_push(&args, "push");
 	argv_array_pushv(&args, argv);
-	return !!push_stash(args.argc, args.argv, prefix);
+	return !!push_stash(args.argc, args.argv, prefix, 1);
 }
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index ea56e85e70..3ad23e2502 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -285,6 +285,11 @@ test_expect_success 'stash --no-keep-index' '
 	test bar,bar2 = $(cat file),$(cat file2)
 '
 
+test_expect_success 'dont assume push with non-option args' '
+	test_must_fail git stash -q drop 2>err &&
+	test_i18ngrep -e "subcommand wasn'\''t specified; '\''push'\'' can'\''t be assumed due to unexpected token '\''drop'\''" err
+'
+
 test_expect_success 'stash --invalid-option' '
 	echo bar5 >file &&
 	echo bar6 >file2 &&
-- 
gitgitgadget


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

* [PATCH 8/8] stash push: support the --pathspec-from-file option
  2020-01-16 16:09 [PATCH 0/8] Support --pathspec-from-file in rm, stash Alexandr Miloslavskiy via GitGitGadget
                   ` (6 preceding siblings ...)
  2020-01-16 16:09 ` [PATCH 7/8] stash: eliminate crude option parsing Alexandr Miloslavskiy via GitGitGadget
@ 2020-01-16 16:09 ` Alexandr Miloslavskiy via GitGitGadget
  2020-02-10 14:45 ` [PATCH v2 0/8] Support --pathspec-from-file in rm, stash Alexandr Miloslavskiy via GitGitGadget
  8 siblings, 0 replies; 41+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2020-01-16 16:09 UTC (permalink / raw)
  To: git
  Cc: Paul-Sebastian Ungureanu, Alexandr Miloslavskiy, 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 `-`. Such use case is not
   really expected.
2) It is not allowed to pass pathspec in both args and file.

Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 Documentation/git-stash.txt    |  16 +++++-
 builtin/stash.c                |  20 +++++++
 t/t3909-stash-pathspec-file.sh | 100 +++++++++++++++++++++++++++++++++
 3 files changed, 135 insertions(+), 1 deletion(-)
 create mode 100755 t/t3909-stash-pathspec-file.sh

diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
index 576ad757d9..4e6a27c4fd 100644
--- a/Documentation/git-stash.txt
+++ b/Documentation/git-stash.txt
@@ -15,6 +15,7 @@ SYNOPSIS
 'git stash' branch <branchname> [<stash>]
 'git stash' [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]
 	     [-u|--include-untracked] [-a|--all] [-m|--message <message>]
+	     [--pathspec-from-file=<file> [--pathspec-file-nul]]
 	     [--] [<pathspec>...]]
 'git stash' clear
 'git stash' create [<message>]
@@ -43,7 +44,7 @@ created stash, `stash@{1}` is the one before it, `stash@{2.hours.ago}`
 is also possible). Stashes may also be referenced by specifying just the
 stash index (e.g. the integer `n` is equivalent to `stash@{n}`).
 
-push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--] [<pathspec>...]::
+push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--pathspec-from-file=<file> [--pathspec-file-nul]] [--] [<pathspec>...]::
 
 	Save your local modifications to a new 'stash entry' and roll them
 	back to HEAD (in the working tree and in the index).
@@ -185,6 +186,19 @@ OPTIONS
 The `--patch` option implies `--keep-index`.  You can use 
 `--no-keep-index` to override this.
 
+--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).
+
 -q::
 --quiet::
 	Quiet, suppress feedback messages.
diff --git a/builtin/stash.c b/builtin/stash.c
index 7bc4c5d696..74d92595a2 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -27,6 +27,7 @@ static const char * const git_stash_usage[] = {
 	N_("git stash clear"),
 	N_("git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
 	   "          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
+	   "          [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
 	   "          [--] [<pathspec>...]]"),
 	N_("git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
 	   "          [-u|--include-untracked] [-a|--all] [<message>]"),
@@ -1456,7 +1457,9 @@ static int push_stash(int argc, const char **argv, const char *prefix,
 	int patch_mode = 0;
 	int include_untracked = 0;
 	int quiet = 0;
+	int pathspec_file_nul = 0;
 	const char *stash_msg = NULL;
+	const char *pathspec_from_file = NULL;
 	struct pathspec ps;
 	struct option options[] = {
 		OPT_BOOL('k', "keep-index", &keep_index,
@@ -1470,6 +1473,8 @@ static int push_stash(int argc, const char **argv, const char *prefix,
 			    N_("include ignore files"), 2),
 		OPT_STRING('m', "message", &stash_msg, N_("message"),
 			   N_("stash message")),
+		OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
+		OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
 		OPT_END()
 	};
 
@@ -1492,6 +1497,21 @@ static int push_stash(int argc, const char **argv, const char *prefix,
 
 	parse_pathspec(&ps, 0, PATHSPEC_PREFER_FULL | PATHSPEC_PREFIX_ORIGIN,
 		       prefix, argv);
+
+	if (pathspec_from_file) {
+		if (patch_mode)
+			die(_("--pathspec-from-file is incompatible with --patch"));
+
+		if (ps.nr)
+			die(_("--pathspec-from-file is incompatible with pathspec arguments"));
+
+		parse_pathspec_file(&ps, 0,
+				    PATHSPEC_PREFER_FULL | PATHSPEC_PREFIX_ORIGIN,
+				    prefix, pathspec_from_file, pathspec_file_nul);
+	} else if (pathspec_file_nul) {
+		die(_("--pathspec-file-nul requires --pathspec-from-file"));
+	}
+
 	return do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode,
 			     include_untracked);
 }
diff --git a/t/t3909-stash-pathspec-file.sh b/t/t3909-stash-pathspec-file.sh
new file mode 100755
index 0000000000..55e050cfd4
--- /dev/null
+++ b/t/t3909-stash-pathspec-file.sh
@@ -0,0 +1,100 @@
+#!/bin/sh
+
+test_description='stash --pathspec-from-file'
+
+. ./test-lib.sh
+
+test_tick
+
+test_expect_success setup '
+	>fileA.t &&
+	>fileB.t &&
+	>fileC.t &&
+	>fileD.t &&
+	git add fileA.t fileB.t fileC.t fileD.t &&
+	git commit -m "Files" &&
+
+	git tag checkpoint
+'
+
+restore_checkpoint () {
+	git reset --hard checkpoint
+}
+
+verify_expect () {
+	git stash show --name-status >actual &&
+	test_cmp expect actual
+}
+
+test_expect_success 'simplest' '
+	restore_checkpoint &&
+
+	# More files are written to make sure that git didnt ignore
+	# --pathspec-from-file, stashing everything
+	echo A >fileA.t &&
+	echo B >fileB.t &&
+	echo C >fileC.t &&
+	echo D >fileD.t &&
+
+	cat >expect <<-\EOF &&
+	M	fileA.t
+	EOF
+
+	echo fileA.t | git stash push --pathspec-from-file=- &&
+	verify_expect
+'
+
+test_expect_success '--pathspec-file-nul' '
+	restore_checkpoint &&
+
+	# More files are written to make sure that git didnt ignore
+	# --pathspec-from-file, stashing everything
+	echo A >fileA.t &&
+	echo B >fileB.t &&
+	echo C >fileC.t &&
+	echo D >fileD.t &&
+
+	cat >expect <<-\EOF &&
+	M	fileA.t
+	M	fileB.t
+	EOF
+
+	printf "fileA.t\0fileB.t\0" | git stash push --pathspec-from-file=- --pathspec-file-nul &&
+	verify_expect
+'
+
+test_expect_success 'only touches what was listed' '
+	restore_checkpoint &&
+
+	# More files are written to make sure that git didnt ignore
+	# --pathspec-from-file, stashing everything
+	echo A >fileA.t &&
+	echo B >fileB.t &&
+	echo C >fileC.t &&
+	echo D >fileD.t &&
+
+	cat >expect <<-\EOF &&
+	M	fileB.t
+	M	fileC.t
+	EOF
+
+	printf "fileB.t\nfileC.t\n" | git stash push --pathspec-from-file=- &&
+	verify_expect
+'
+
+test_expect_success 'error conditions' '
+	restore_checkpoint &&
+	echo A >fileA.t &&
+	echo fileA.t >list &&
+
+	test_must_fail git stash push --pathspec-from-file=list --patch 2>err &&
+	test_i18ngrep -e "--pathspec-from-file is incompatible with --patch" err &&
+
+	test_must_fail git stash push --pathspec-from-file=list -- fileA.t 2>err &&
+	test_i18ngrep -e "--pathspec-from-file is incompatible with pathspec arguments" err &&
+
+	test_must_fail git stash push --pathspec-file-nul 2>err &&
+	test_i18ngrep -e "--pathspec-file-nul requires --pathspec-from-file" err
+'
+
+test_done
-- 
gitgitgadget

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

* Re: [PATCH 1/8] doc: rm: synchronize <pathspec> description
  2020-01-16 16:09 ` [PATCH 1/8] doc: rm: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget
@ 2020-01-21 19:14   ` Junio C Hamano
  0 siblings, 0 replies; 41+ messages in thread
From: Junio C Hamano @ 2020-01-21 19:14 UTC (permalink / raw)
  To: Alexandr Miloslavskiy via GitGitGadget
  Cc: git, Paul-Sebastian Ungureanu, Alexandr Miloslavskiy

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

> From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
>
> This patch continues the effort that is already applied to
> `git commit`, `git reset`, `git checkout` etc.
>
> 1) Changed outdated descriptions to mention pathspec instead.
> 2) Added reference to 'linkgit:gitglossary[7]'.
> 3) Removed content that merely repeated gitglossary.
> 4) Merged the remainder of "discussion" into `<patchspec>`.

Thanks.  Will queue.

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

* Re: [PATCH 2/8] rm: support the --pathspec-from-file option
  2020-01-16 16:09 ` [PATCH 2/8] rm: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
@ 2020-01-21 19:36   ` Junio C Hamano
  2020-02-10 14:46     ` Alexandr Miloslavskiy
  0 siblings, 1 reply; 41+ messages in thread
From: Junio C Hamano @ 2020-01-21 19:36 UTC (permalink / raw)
  To: Alexandr Miloslavskiy via GitGitGadget
  Cc: git, Paul-Sebastian Ungureanu, Alexandr Miloslavskiy

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

> From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
>
> Decisions taken for simplicity:
> 1) It is not allowed to pass pathspec in both args and file.
>
> `if (!argc)` block was adapted to work with --pathspec-from-file. For
> that, I also had to parse pathspec earlier. Now it happens before
> `read_cache()` / `hold_locked_index()` / `setup_work_tree()`, which
> sounds fine to me.

That is not an explanation nor justification.

> In case of empty pathspec, there is now a clear error message instead
> of showing usage.

Hmph, "git rm --pathspec-from-file=/dev/null" would say "nothing
specified, nothing removed" and it makes perfect sense, but I am not
sure "git rm" that gives the same message is better than the output
by usage_with_options(builtin_rm_usage, builtin_rm_options).

> -'git rm' [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] <pathspec>...
> +'git rm' [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch]
> +	  [--quiet] [--pathspec-from-file=<file> [--pathspec-file-nul]]
> +	  [--] [<pathspec>...]

OK.

> +--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).
> +

OK.

> diff --git a/builtin/rm.c b/builtin/rm.c
> index 19ce95a901..8e40795751 100644
> --- a/builtin/rm.c
> +++ b/builtin/rm.c
> @@ -235,7 +235,8 @@ static int check_local_mod(struct object_id *head, int index_only)
>  }
>  
>  static int show_only = 0, force = 0, index_only = 0, recursive = 0, quiet = 0;
> -static int ignore_unmatch = 0;
> +static int ignore_unmatch = 0, pathspec_file_nul = 0;
> +static char *pathspec_from_file = NULL;

We may want to clean these "explicitly initialize to 0/NULL" up at
some point.  The clean-up itself would not be in the scope of this
patch, of course, but not making it worse is something this patch
can do to help.

> @@ -259,8 +262,24 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
>  
>  	argc = parse_options(argc, argv, prefix, builtin_rm_options,
>  			     builtin_rm_usage, 0);
> -	if (!argc)
> -		usage_with_options(builtin_rm_usage, builtin_rm_options);
> +
> +	parse_pathspec(&pathspec, 0,
> +		       PATHSPEC_PREFER_CWD,
> +		       prefix, argv);
> +
> +	if (pathspec_from_file) {
> +		if (pathspec.nr)
> +			die(_("--pathspec-from-file is incompatible with pathspec arguments"));
> +
> +		parse_pathspec_file(&pathspec, 0,
> +				    PATHSPEC_PREFER_CWD,
> +				    prefix, pathspec_from_file, pathspec_file_nul);
> +	} else if (pathspec_file_nul) {
> +		die(_("--pathspec-file-nul requires --pathspec-from-file"));
> +	}
> +
> +	if (!pathspec.nr)
> +		die(_("Nothing specified, nothing removed"));

I wonder if doing these in this order instead would make more sense
without making unnecessary behaviour change.

    - parse the options (which would make pathspec_f_f available to
      us)

    - if pathspec_f_f is given, call parse_pathspec_file()

    - otherwise complain if pathspec_file_nul is set

    - otherwise check argc and give the usage_with_options()

I dunno.

Thanks.

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

* Re: [PATCH 4/8] doc: stash: split options from description (2)
  2020-01-16 16:09 ` [PATCH 4/8] doc: stash: split options from description (2) Alexandr Miloslavskiy via GitGitGadget
@ 2020-01-21 20:21   ` Junio C Hamano
  2020-02-10 14:47     ` Alexandr Miloslavskiy
  0 siblings, 1 reply; 41+ messages in thread
From: Junio C Hamano @ 2020-01-21 20:21 UTC (permalink / raw)
  To: Alexandr Miloslavskiy via GitGitGadget
  Cc: git, Paul-Sebastian Ungureanu, Alexandr Miloslavskiy

> diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
> index 2dedc21997..f75b80a720 100644
> --- a/Documentation/git-stash.txt
> +++ b/Documentation/git-stash.txt
> @@ -43,9 +43,6 @@ created stash, `stash@{1}` is the one before it, `stash@{2.hours.ago}`
>  is also possible). Stashes may also be referenced by specifying just the
>  stash index (e.g. the integer `n` is equivalent to `stash@{n}`).
>  
> -OPTIONS
> --------
> -
>  push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--] [<pathspec>...]::
>  
>  	Save your local modifications to a new 'stash entry' and roll them
> @@ -152,40 +149,51 @@ store::
>  	reflog.  This is intended to be useful for scripts.  It is
>  	probably not the command you want to use; see "push" above.
>  
> -If the `--all` option is used instead then the
> -ignored files are stashed and cleaned in addition to the untracked files.
> -
> -If the `--include-untracked` option is used, all untracked files are also
> -stashed and then cleaned up with `git clean`, leaving the working directory
> -in a very clean state.
> -
> -If the `--index` option is used, then tries to reinstate not only the working
> -tree's changes, but also the index's ones. However, this can fail, when you
> -have conflicts (which are stored in the index, where you therefore can no
> -longer apply the changes as they were originally).
> -
> -If the `--keep-index` option is used, all changes already added to the
> -index are left intact.
> -
> -With `--patch`, you can interactively select hunks from the diff
> -between HEAD and the working tree to be stashed.  The stash entry is
> -constructed such that its index state is the same as the index state
> -of your repository, and its worktree contains only the changes you
> -selected interactively.  The selected changes are then rolled back
> -from your worktree. See the ``Interactive Mode'' section of
> -linkgit:git-add[1] to learn how to operate the `--patch` mode.
> +OPTIONS
> +-------
> +-a::
> +--all::
> +	All ignored and untracked files are also stashed and then cleaned
> +	up with `git clean`.
> +
> +-u::
> +--include-untracked::
> +	All untracked files are also stashed and then cleaned up with
> +	`git clean`.
> +
> +--index::
> +	Tries to reinstate not only the working tree's changes, but also
> +	the index's ones. However, this can fail, when you have conflicts
> +	(which are stored in the index, where you therefore can no longer
> +	apply the changes as they were originally).
> +
> +-k::
> +--keep-index::
> +--no-keep-index::
> +	All changes already added to the index are left intact.
> +
> +-p::
> +--patch::
> +	Interactively select hunks from the diff between HEAD and the
> +	working tree to be stashed.  The stash entry is constructed such
> +	that its index state is the same as the index state of your
> +	repository, and its worktree contains only the changes you selected
> +	interactively.  The selected changes are then rolled back from your
> +	worktree. See the ``Interactive Mode'' section of linkgit:git-add[1]
> +	to learn how to operate the `--patch` mode.

I have a mixed feelings about this approach.  While I am sympathetic
to the "have a single place to describe all" approach this patch
takes, the approach needs to be executed with care when subcommands
do not share much of the options at all.  Those readers who jump to
the "OPTIONS" section and try to ignore anything outside the section
may not easily notice that --keep-index only applies to subcommands
that creates a new stash, and meaningless to subcommands that lets
you inspect existing stashes, or apply one to the working tree (and
optionally to the index), for example.  If the orinal documentation
did not use "OPTIONS" as the section header and instead said perhaps
"SUBCOMMANDS", it would have been even better, but otherwise I would
suspect that the original "the options understood by 'push' are all
described under the part that begins with 'push [-p] [-k] ...'
command line" arrangement was much easier to understand when reading
them through for the first time to learn and also to find what the
user is looking for after learning the "concept" (e.g. "with
'stash', there is a way to stash-away the changes made to the
working tree") but before becoming familiar with exact set of
options for each subcommand (e.g. "and there was an option that let
me stash only partial changes piecemeal, but what was it spelled?").

If we were to make the result of "a single place to describe all"
approach anything useful, I think at least

 (1) the list itself should make it clear that it does not talk
     about options related to listing and showing at all,
     before enumerating dashed options.

 (2) each item in the enumeration should identify which
     subcommand(s) accept(s) it.

So, I dunno.




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

* Re: [PATCH 5/8] doc: stash: document more options
  2020-01-16 16:09 ` [PATCH 5/8] doc: stash: document more options Alexandr Miloslavskiy via GitGitGadget
@ 2020-01-21 20:29   ` Junio C Hamano
  0 siblings, 0 replies; 41+ messages in thread
From: Junio C Hamano @ 2020-01-21 20:29 UTC (permalink / raw)
  To: Alexandr Miloslavskiy via GitGitGadget
  Cc: git, Paul-Sebastian Ungureanu, Alexandr Miloslavskiy

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

> +-q::
> +--quiet::
> +	Quiet, suppress feedback messages.
> +
> +\--::
> +	Separates pathspec from options for disambiguation purposes.
> +
>  <pathspec>...::
>  	The new stash entry records the modified states only for the files
>  	that match the pathspec.  The index entries and working tree files

OK.  Describing these in the documentation is a good thing.  How
they should be added depends on in what shape the patch 4/8 should
settle, though.

Thanks.

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

* Re: [PATCH 6/8] doc: stash: synchronize <pathspec> description
  2020-01-16 16:09 ` [PATCH 6/8] doc: stash: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget
@ 2020-01-21 20:29   ` Junio C Hamano
  0 siblings, 0 replies; 41+ messages in thread
From: Junio C Hamano @ 2020-01-21 20:29 UTC (permalink / raw)
  To: Alexandr Miloslavskiy via GitGitGadget
  Cc: git, Paul-Sebastian Ungureanu, Alexandr Miloslavskiy

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

> From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
>
> This patch continues the effort that is already applied to
> `git commit`, `git reset`, `git checkout` etc.

Makes sense.

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

* [PATCH v2 0/8] Support --pathspec-from-file in rm, stash
  2020-01-16 16:09 [PATCH 0/8] Support --pathspec-from-file in rm, stash Alexandr Miloslavskiy via GitGitGadget
                   ` (7 preceding siblings ...)
  2020-01-16 16:09 ` [PATCH 8/8] stash push: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
@ 2020-02-10 14:45 ` Alexandr Miloslavskiy via GitGitGadget
  2020-02-10 14:45   ` [PATCH v2 1/8] doc: rm: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget
                     ` (8 more replies)
  8 siblings, 9 replies; 41+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2020-02-10 14:45 UTC (permalink / raw)
  To: git; +Cc: Paul-Sebastian Ungureanu, Alexandr Miloslavskiy

Changes since V1
------------------
Some polishing based on code review in V1
1) Improved error message for the case where pathspec is not given to `git rm`
2) Removed explicit variable initialization to 0 / NULL
3) Polishing in docs for `git stash`

------------------
This series continues the effort to support `--pathspec-from-file`
in various git commands. Series already in `master`: [1][2]

Cc'ing Paul-Sebastian Ungureanu because I touched his git stash code.

[1] https://public-inbox.org/git/pull.445.git.1572895605.gitgitgadget@gmail.com/
[2] https://lore.kernel.org/git/pull.490.git.1576161385.gitgitgadget@gmail.com/

Alexandr Miloslavskiy (8):
  doc: rm: synchronize <pathspec> description
  rm: support the --pathspec-from-file option
  doc: stash: split options from description (1)
  doc: stash: split options from description (2)
  doc: stash: document more options
  doc: stash: synchronize <pathspec> description
  stash: eliminate crude option parsing
  stash push: support the --pathspec-from-file option

 Documentation/git-rm.txt       |  61 +++++++-------
 Documentation/git-stash.txt    | 144 +++++++++++++++++++++++----------
 builtin/rm.c                   |  28 +++++--
 builtin/stash.c                |  79 +++++++++---------
 t/t3601-rm-pathspec-file.sh    |  79 ++++++++++++++++++
 t/t3903-stash.sh               |   5 ++
 t/t3909-stash-pathspec-file.sh | 100 +++++++++++++++++++++++
 7 files changed, 381 insertions(+), 115 deletions(-)
 create mode 100755 t/t3601-rm-pathspec-file.sh
 create mode 100755 t/t3909-stash-pathspec-file.sh


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

Range-diff vs v1:

 1:  23387f8391 = 1:  2e8c8ad815 doc: rm: synchronize <pathspec> description
 2:  5611e3ae32 ! 2:  7ccbab52e5 rm: support the --pathspec-from-file option
     @@ -64,8 +64,8 @@
       
       static int show_only = 0, force = 0, index_only = 0, recursive = 0, quiet = 0;
      -static int ignore_unmatch = 0;
     -+static int ignore_unmatch = 0, pathspec_file_nul = 0;
     -+static char *pathspec_from_file = NULL;
     ++static int ignore_unmatch = 0, pathspec_file_nul;
     ++static char *pathspec_from_file;
       
       static struct option builtin_rm_options[] = {
       	OPT__DRY_RUN(&show_only, N_("dry run")),
     @@ -101,7 +101,7 @@
      +	}
      +
      +	if (!pathspec.nr)
     -+		die(_("Nothing specified, nothing removed"));
     ++		die(_("No pathspec was given. Which files should I remove?"));
       
       	if (!index_only)
       		setup_work_tree();
     @@ -196,7 +196,7 @@
      +	
      +	>empty_list &&
      +	test_must_fail git rm --pathspec-from-file=empty_list 2>err &&
     -+	test_i18ngrep -e "Nothing specified, nothing removed" err
     ++	test_i18ngrep -e "No pathspec was given. Which files should I remove?" err
      +'
      +
      +test_done
 3:  0824bba210 = 3:  8c212fc0ed doc: stash: split options from description (1)
 4:  708363241f ! 4:  db3a96720c doc: stash: split options from description (2)
     @@ -3,17 +3,28 @@
          doc: stash: split options from description (2)
      
          Together with the previous patch, this brings docs for `git stash` to
     -    the common layout used for most other commands (see for example docs for
     -    `git add`, `git commit`, `git checkout`, `git reset`) where all options
     -    are documented in a separate list.
     +    the common layout used for most other commands (see for example docs
     +    for `git add`, `git commit`, `git checkout`, `git reset`) where all
     +    options are documented in a separate list.
      
     -    I have decided to use alphabetical sorting in the list of options. Other
     -    docs often sort in order of appearance or order of importance, but in
     -    this case it wouldn't be easy to read the list where options from
     -    multiple sub-commands are mixed together.
     +    After some thinking and having a look at docs for `git svn` and
     +    `git `submodule`, I have arrived at following conclusions:
     +      * Options should be described in a list rather then text to
     +        facilitate lookup for user.
     +      * Single list is better then multiple lists because it avoids
     +        copy&pasting descriptions between subcommands (or, without
     +        copy&pasting, user will have to look up missing options in other
     +        subcommands).
     +      * As a consequence, commands section should only give brief info and
     +        list possible options. Since options have good enough names, user
     +            will only need to look up the "interesting" options.
     +      * Every option should list which subcommands support it.
      
     -    There is some text editing done to make old descriptions better fit into
     -    the list-style format.
     +    I have decided to use alphabetical sorting in the list of options to
     +    facilitate lookup for user.
     +
     +    There is some text editing done to make old descriptions better fit
     +    into the list-style format.
      
          Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
      
     @@ -26,10 +37,40 @@
       
      -OPTIONS
      --------
     --
     ++COMMANDS
     ++--------
     + 
       push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--] [<pathspec>...]::
       
     - 	Save your local modifications to a new 'stash entry' and roll them
     +@@
     + 
     + 	Show the changes recorded in the stash entry as a diff between the
     + 	stashed contents and the commit back when the stash entry was first
     +-	created. When no `<stash>` is given, it shows the latest one.
     ++	created.
     + 	By default, the command shows the diffstat, but it will accept any
     + 	format known to 'git diff' (e.g., `git stash show -p stash@{1}`
     + 	to view the second most recent entry in patch form).
     +@@
     + 	the commit at which the `<stash>` was originally created, applies the
     + 	changes recorded in `<stash>` to the new working tree and index.
     + 	If that succeeds, and `<stash>` is a reference of the form
     +-	`stash@{<revision>}`, it then drops the `<stash>`. When no `<stash>`
     +-	is given, applies the latest one.
     ++	`stash@{<revision>}`, it then drops the `<stash>`.
     + +
     + This is useful if the branch on which you ran `git stash push` has
     + changed enough that `git stash apply` fails due to conflicts. Since
     +@@
     + drop [-q|--quiet] [<stash>]::
     + 
     + 	Remove a single stash entry from the list of stash entries.
     +-	When no `<stash>` is given, it removes the latest one.
     +-	i.e. `stash@{0}`, otherwise `<stash>` must be a valid stash
     +-	log reference of the form `stash@{<revision>}`.
     + 
     + create::
     + 
      @@
       	reflog.  This is intended to be useful for scripts.  It is
       	probably not the command you want to use; see "push" above.
     @@ -40,15 +81,43 @@
      -If the `--include-untracked` option is used, all untracked files are also
      -stashed and then cleaned up with `git clean`, leaving the working directory
      -in a very clean state.
     --
     ++OPTIONS
     ++-------
     ++-a::
     ++--all::
     ++	This option is only valid for `push` and `save` commands.
     +++
     ++All ignored and untracked files are also stashed and then cleaned
     ++up with `git clean`.
     + 
      -If the `--index` option is used, then tries to reinstate not only the working
      -tree's changes, but also the index's ones. However, this can fail, when you
      -have conflicts (which are stored in the index, where you therefore can no
      -longer apply the changes as they were originally).
     --
     ++-u::
     ++--include-untracked::
     ++	This option is only valid for `push` and `save` commands.
     +++
     ++All untracked files are also stashed and then cleaned up with
     ++`git clean`.
     + 
      -If the `--keep-index` option is used, all changes already added to the
      -index are left intact.
     --
     ++--index::
     ++	This option is only valid for `pop` and `apply` commands.
     +++
     ++Tries to reinstate not only the working tree's changes, but also
     ++the index's ones. However, this can fail, when you have conflicts
     ++(which are stored in the index, where you therefore can no longer
     ++apply the changes as they were originally).
     ++
     ++-k::
     ++--keep-index::
     ++--no-keep-index::
     ++	This option is only valid for `push` and `save` commands.
     +++
     ++All changes already added to the index are left intact.
     + 
      -With `--patch`, you can interactively select hunks from the diff
      -between HEAD and the working tree to be stashed.  The stash entry is
      -constructed such that its index state is the same as the index state
     @@ -56,38 +125,17 @@
      -selected interactively.  The selected changes are then rolled back
      -from your worktree. See the ``Interactive Mode'' section of
      -linkgit:git-add[1] to learn how to operate the `--patch` mode.
     -+OPTIONS
     -+-------
     -+-a::
     -+--all::
     -+	All ignored and untracked files are also stashed and then cleaned
     -+	up with `git clean`.
     -+
     -+-u::
     -+--include-untracked::
     -+	All untracked files are also stashed and then cleaned up with
     -+	`git clean`.
     -+
     -+--index::
     -+	Tries to reinstate not only the working tree's changes, but also
     -+	the index's ones. However, this can fail, when you have conflicts
     -+	(which are stored in the index, where you therefore can no longer
     -+	apply the changes as they were originally).
     -+
     -+-k::
     -+--keep-index::
     -+--no-keep-index::
     -+	All changes already added to the index are left intact.
     -+
      +-p::
      +--patch::
     -+	Interactively select hunks from the diff between HEAD and the
     -+	working tree to be stashed.  The stash entry is constructed such
     -+	that its index state is the same as the index state of your
     -+	repository, and its worktree contains only the changes you selected
     -+	interactively.  The selected changes are then rolled back from your
     -+	worktree. See the ``Interactive Mode'' section of linkgit:git-add[1]
     -+	to learn how to operate the `--patch` mode.
     ++	This option is only valid for `push` and `save` commands.
     +++
     ++Interactively select hunks from the diff between HEAD and the
     ++working tree to be stashed.  The stash entry is constructed such
     ++that its index state is the same as the index state of your
     ++repository, and its worktree contains only the changes you selected
     ++interactively.  The selected changes are then rolled back from your
     ++worktree. See the ``Interactive Mode'' section of linkgit:git-add[1]
     ++to learn how to operate the `--patch` mode.
       +
       The `--patch` option implies `--keep-index`.  You can use 
       `--no-keep-index` to override this.
     @@ -97,17 +145,23 @@
      -entries and working tree files are then rolled back to the state in
      -HEAD only for these files, too, leaving files that do not match the
      -pathspec intact.
     -+<pathspec>...::
     -+	The new stash entry records the modified states only for the files
     -+	that match the pathspec.  The index entries and working tree files
     -+	are then rolled back to the state in HEAD only for these files,
     -+	too, leaving files that do not match the pathspec intact.
     - 
     +-
      -When no `<stash>` is given, `stash@{0}` is assumed, otherwise `<stash>` must
      -be a reference of the form `stash@{<revision>}`.
     ++<pathspec>...::
     ++	This option is only valid for `push` command.
     +++
     ++The new stash entry records the modified states only for the files
     ++that match the pathspec.  The index entries and working tree files
     ++are then rolled back to the state in HEAD only for these files,
     ++too, leaving files that do not match the pathspec intact.
     ++
      +<stash>::
     -+	A reference of the form `stash@{<revision>}`. When no `<stash>` is
     -+	given, the latest stash is assumed (that is, `stash@{0}`).
     ++	This option is only valid for `apply`, `branch`, `drop`, `pop`,
     ++	`show` commands.
     +++
     ++A reference of the form `stash@{<revision>}`. When no `<stash>` is
     ++given, the latest stash is assumed (that is, `stash@{0}`).
       
       DISCUSSION
       ----------
 5:  8a5f2dbe9e ! 5:  f91ec08b47 doc: stash: document more options
     @@ -13,11 +13,16 @@
       
      +-q::
      +--quiet::
     -+	Quiet, suppress feedback messages.
     ++	This option is only valid for `apply`, `drop`, `pop`, `push`,
     ++	`save`, `store` commands.
     +++
     ++Quiet, suppress feedback messages.
      +
      +\--::
     -+	Separates pathspec from options for disambiguation purposes.
     ++	This option is only valid for `push` command.
     +++
     ++Separates pathspec from options for disambiguation purposes.
      +
       <pathspec>...::
     - 	The new stash entry records the modified states only for the files
     - 	that match the pathspec.  The index entries and working tree files
     + 	This option is only valid for `push` command.
     + +
 6:  5e17a0c470 ! 6:  04e2fd5865 doc: stash: synchronize <pathspec> description
     @@ -30,11 +30,11 @@
       	message.
       
      @@
     - 	that match the pathspec.  The index entries and working tree files
     - 	are then rolled back to the state in HEAD only for these files,
     - 	too, leaving files that do not match the pathspec intact.
     + that match the pathspec.  The index entries and working tree files
     + are then rolled back to the state in HEAD only for these files,
     + too, leaving files that do not match the pathspec intact.
      ++
      +For more details, see the 'pathspec' entry in linkgit:gitglossary[7].
       
       <stash>::
     - 	A reference of the form `stash@{<revision>}`. When no `<stash>` is
     + 	This option is only valid for `apply`, `branch`, `drop`, `pop`,
 7:  7a8d36d49f = 7:  0558cbbe38 stash: eliminate crude option parsing
 8:  721410233b ! 8:  0c6f28dc68 stash push: support the --pathspec-from-file option
     @@ -22,8 +22,8 @@
       'git stash' clear
       'git stash' create [<message>]
      @@
     - is also possible). Stashes may also be referenced by specifying just the
     - stash index (e.g. the integer `n` is equivalent to `stash@{n}`).
     + COMMANDS
     + --------
       
      -push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--] [<pathspec>...]::
      +push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--pathspec-from-file=<file> [--pathspec-file-nul]] [--] [<pathspec>...]::
     @@ -35,21 +35,25 @@
       `--no-keep-index` to override this.
       
      +--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`.
     ++	This option is only valid for `push` command.
     +++
     ++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 is only valid for `push` command.
     +++
     ++Only meaningful with `--pathspec-from-file`. Pathspec elements are
     ++separated with NUL character and all other characters are taken
     ++literally (including newlines and quotes).
      +
       -q::
       --quiet::
     - 	Quiet, suppress feedback messages.
     + 	This option is only valid for `apply`, `drop`, `pop`, `push`,
      
       diff --git a/builtin/stash.c b/builtin/stash.c
       --- a/builtin/stash.c

-- 
gitgitgadget

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

* [PATCH v2 1/8] doc: rm: synchronize <pathspec> description
  2020-02-10 14:45 ` [PATCH v2 0/8] Support --pathspec-from-file in rm, stash Alexandr Miloslavskiy via GitGitGadget
@ 2020-02-10 14:45   ` Alexandr Miloslavskiy via GitGitGadget
  2020-02-10 14:45   ` [PATCH v2 2/8] rm: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
                     ` (7 subsequent siblings)
  8 siblings, 0 replies; 41+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2020-02-10 14:45 UTC (permalink / raw)
  To: git
  Cc: Paul-Sebastian Ungureanu, Alexandr Miloslavskiy, Alexandr Miloslavskiy

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

This patch continues the effort that is already applied to
`git commit`, `git reset`, `git checkout` etc.

1) Changed outdated descriptions to mention pathspec instead.
2) Added reference to 'linkgit:gitglossary[7]'.
3) Removed content that merely repeated gitglossary.
4) Merged the remainder of "discussion" into `<patchspec>`.

Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 Documentation/git-rm.txt | 50 +++++++++++++++++-----------------------
 1 file changed, 21 insertions(+), 29 deletions(-)

diff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt
index b5c46223c4..e02a08e5ef 100644
--- a/Documentation/git-rm.txt
+++ b/Documentation/git-rm.txt
@@ -8,16 +8,16 @@ git-rm - Remove files from the working tree and from the index
 SYNOPSIS
 --------
 [verse]
-'git rm' [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] <file>...
+'git rm' [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] <pathspec>...
 
 DESCRIPTION
 -----------
-Remove files from the index, or from the working tree and the index.
-`git rm` will not remove a file from just your working directory.
-(There is no option to remove a file only from the working tree
-and yet keep it in the index; use `/bin/rm` if you want to do that.)
-The files being removed have to be identical to the tip of the branch,
-and no updates to their contents can be staged in the index,
+Remove files matching pathspec from the index, or from the working tree
+and the index. `git rm` will not remove a file from just your working
+directory. (There is no option to remove a file only from the working
+tree and yet keep it in the index; use `/bin/rm` if you want to do
+that.) The files being removed have to be identical to the tip of the
+branch, and no updates to their contents can be staged in the index,
 though that default behavior can be overridden with the `-f` option.
 When `--cached` is given, the staged content has to
 match either the tip of the branch or the file on disk,
@@ -26,15 +26,20 @@ allowing the file to be removed from just the index.
 
 OPTIONS
 -------
-<file>...::
-	Files to remove.  Fileglobs (e.g. `*.c`) can be given to
-	remove all matching files.  If you want Git to expand
-	file glob characters, you may need to shell-escape them.
-	A leading directory name
-	(e.g. `dir` to remove `dir/file1` and `dir/file2`) can be
-	given to remove all files in the directory, and recursively
-	all sub-directories,
-	but this requires the `-r` option to be explicitly given.
+<pathspec>...::
+	Files to remove.  A leading directory name (e.g. `dir` to remove
+	`dir/file1` and `dir/file2`) can be given to remove all files in
+	the directory, and recursively all sub-directories, but this
+	requires the `-r` option to be explicitly given.
++
+The command removes only the paths that are known to Git.
++
+File globbing matches across directory boundaries.  Thus, given two
+directories `d` and `d2`, there is a difference between using
+`git rm 'd*'` and `git rm 'd/*'`, as the former will also remove all
+of directory `d2`.
++
+For more details, see the 'pathspec' entry in linkgit:gitglossary[7].
 
 -f::
 --force::
@@ -69,19 +74,6 @@ OPTIONS
 	for each file removed. This option suppresses that output.
 
 
-DISCUSSION
-----------
-
-The <file> list given to the command can be exact pathnames,
-file glob patterns, or leading directory names.  The command
-removes only the paths that are known to Git.  Giving the name of
-a file that you have not told Git about does not remove that file.
-
-File globbing matches across directory boundaries.  Thus, given
-two directories `d` and `d2`, there is a difference between
-using `git rm 'd*'` and `git rm 'd/*'`, as the former will
-also remove all of directory `d2`.
-
 REMOVING FILES THAT HAVE DISAPPEARED FROM THE FILESYSTEM
 --------------------------------------------------------
 There is no option for `git rm` to remove from the index only
-- 
gitgitgadget


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

* [PATCH v2 2/8] rm: support the --pathspec-from-file option
  2020-02-10 14:45 ` [PATCH v2 0/8] Support --pathspec-from-file in rm, stash Alexandr Miloslavskiy via GitGitGadget
  2020-02-10 14:45   ` [PATCH v2 1/8] doc: rm: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget
@ 2020-02-10 14:45   ` Alexandr Miloslavskiy via GitGitGadget
  2020-02-10 20:39     ` Junio C Hamano
  2020-02-10 14:45   ` [PATCH v2 3/8] doc: stash: split options from description (1) Alexandr Miloslavskiy via GitGitGadget
                     ` (6 subsequent siblings)
  8 siblings, 1 reply; 41+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2020-02-10 14:45 UTC (permalink / raw)
  To: git
  Cc: Paul-Sebastian Ungureanu, Alexandr Miloslavskiy, Alexandr Miloslavskiy

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

Decisions taken for simplicity:
1) It is not allowed to pass pathspec in both args and file.

`if (!argc)` block was adapted to work with --pathspec-from-file. For
that, I also had to parse pathspec earlier. Now it happens before
`read_cache()` / `hold_locked_index()` / `setup_work_tree()`, which
sounds fine to me.

In case of empty pathspec, there is now a clear error message instead
of showing usage. As a consequence, exit code has also changed. Judging
from [1] it doesn't seem that showing usage in this case was important
(according to commit message, it was to avoid segfault), and it doesn't
fit into how other commands react to empty pathspec. Finally, the new
error message is easier to understand.

[1] Commit 7612a1ef ("git-rm: honor -n flag" 2006-06-09)

Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 Documentation/git-rm.txt    | 17 +++++++-
 builtin/rm.c                | 28 ++++++++++---
 t/t3601-rm-pathspec-file.sh | 79 +++++++++++++++++++++++++++++++++++++
 3 files changed, 117 insertions(+), 7 deletions(-)
 create mode 100755 t/t3601-rm-pathspec-file.sh

diff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt
index e02a08e5ef..ab750367fd 100644
--- a/Documentation/git-rm.txt
+++ b/Documentation/git-rm.txt
@@ -8,7 +8,9 @@ git-rm - Remove files from the working tree and from the index
 SYNOPSIS
 --------
 [verse]
-'git rm' [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] <pathspec>...
+'git rm' [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch]
+	  [--quiet] [--pathspec-from-file=<file> [--pathspec-file-nul]]
+	  [--] [<pathspec>...]
 
 DESCRIPTION
 -----------
@@ -73,6 +75,19 @@ For more details, see the 'pathspec' entry in linkgit:gitglossary[7].
 	`git rm` normally outputs one line (in the form of an `rm` command)
 	for each file removed. This option suppresses that output.
 
+--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).
+
 
 REMOVING FILES THAT HAVE DISAPPEARED FROM THE FILESYSTEM
 --------------------------------------------------------
diff --git a/builtin/rm.c b/builtin/rm.c
index 19ce95a901..4858631e0f 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -235,7 +235,8 @@ static int check_local_mod(struct object_id *head, int index_only)
 }
 
 static int show_only = 0, force = 0, index_only = 0, recursive = 0, quiet = 0;
-static int ignore_unmatch = 0;
+static int ignore_unmatch = 0, pathspec_file_nul;
+static char *pathspec_from_file;
 
 static struct option builtin_rm_options[] = {
 	OPT__DRY_RUN(&show_only, N_("dry run")),
@@ -245,6 +246,8 @@ static struct option builtin_rm_options[] = {
 	OPT_BOOL('r', NULL,             &recursive,  N_("allow recursive removal")),
 	OPT_BOOL( 0 , "ignore-unmatch", &ignore_unmatch,
 				N_("exit with a zero status even if nothing matched")),
+	OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
+	OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
 	OPT_END(),
 };
 
@@ -259,8 +262,24 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
 
 	argc = parse_options(argc, argv, prefix, builtin_rm_options,
 			     builtin_rm_usage, 0);
-	if (!argc)
-		usage_with_options(builtin_rm_usage, builtin_rm_options);
+
+	parse_pathspec(&pathspec, 0,
+		       PATHSPEC_PREFER_CWD,
+		       prefix, argv);
+
+	if (pathspec_from_file) {
+		if (pathspec.nr)
+			die(_("--pathspec-from-file is incompatible with pathspec arguments"));
+
+		parse_pathspec_file(&pathspec, 0,
+				    PATHSPEC_PREFER_CWD,
+				    prefix, pathspec_from_file, pathspec_file_nul);
+	} else if (pathspec_file_nul) {
+		die(_("--pathspec-file-nul requires --pathspec-from-file"));
+	}
+
+	if (!pathspec.nr)
+		die(_("No pathspec was given. Which files should I remove?"));
 
 	if (!index_only)
 		setup_work_tree();
@@ -270,9 +289,6 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
 	if (read_cache() < 0)
 		die(_("index file corrupt"));
 
-	parse_pathspec(&pathspec, 0,
-		       PATHSPEC_PREFER_CWD,
-		       prefix, argv);
 	refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &pathspec, NULL, NULL);
 
 	seen = xcalloc(pathspec.nr, 1);
diff --git a/t/t3601-rm-pathspec-file.sh b/t/t3601-rm-pathspec-file.sh
new file mode 100755
index 0000000000..4542a0f02f
--- /dev/null
+++ b/t/t3601-rm-pathspec-file.sh
@@ -0,0 +1,79 @@
+#!/bin/sh
+
+test_description='rm --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 fileA.t fileB.t fileC.t fileD.t &&
+	git commit -m "files" &&
+	
+	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 'simplest' '
+	restore_checkpoint &&
+
+	cat >expect <<-\EOF &&
+	D  fileA.t
+	EOF
+
+	echo fileA.t | git rm --pathspec-from-file=- &&
+	verify_expect
+'
+
+test_expect_success '--pathspec-file-nul' '
+	restore_checkpoint &&
+
+	cat >expect <<-\EOF &&
+	D  fileA.t
+	D  fileB.t
+	EOF
+
+	printf "fileA.t\0fileB.t\0" | git rm --pathspec-from-file=- --pathspec-file-nul &&
+	verify_expect
+'
+
+test_expect_success 'only touches what was listed' '
+	restore_checkpoint &&
+
+	cat >expect <<-\EOF &&
+	D  fileB.t
+	D  fileC.t
+	EOF
+
+	printf "fileB.t\nfileC.t\n" | git rm --pathspec-from-file=- &&
+	verify_expect
+'
+
+test_expect_success 'error conditions' '
+	restore_checkpoint &&
+	echo fileA.t >list &&
+
+	test_must_fail git rm --pathspec-from-file=list -- fileA.t 2>err &&
+	test_i18ngrep -e "--pathspec-from-file is incompatible with pathspec arguments" err &&
+
+	test_must_fail git rm --pathspec-file-nul 2>err &&
+	test_i18ngrep -e "--pathspec-file-nul requires --pathspec-from-file" err &&
+	
+	>empty_list &&
+	test_must_fail git rm --pathspec-from-file=empty_list 2>err &&
+	test_i18ngrep -e "No pathspec was given. Which files should I remove?" err
+'
+
+test_done
-- 
gitgitgadget


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

* [PATCH v2 3/8] doc: stash: split options from description (1)
  2020-02-10 14:45 ` [PATCH v2 0/8] Support --pathspec-from-file in rm, stash Alexandr Miloslavskiy via GitGitGadget
  2020-02-10 14:45   ` [PATCH v2 1/8] doc: rm: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget
  2020-02-10 14:45   ` [PATCH v2 2/8] rm: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
@ 2020-02-10 14:45   ` Alexandr Miloslavskiy via GitGitGadget
  2020-02-10 14:45   ` [PATCH v2 4/8] doc: stash: split options from description (2) Alexandr Miloslavskiy via GitGitGadget
                     ` (5 subsequent siblings)
  8 siblings, 0 replies; 41+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2020-02-10 14:45 UTC (permalink / raw)
  To: git
  Cc: Paul-Sebastian Ungureanu, Alexandr Miloslavskiy, Alexandr Miloslavskiy

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

This patch moves blocks of text as-is to make it easier to review the
next patch.

Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 Documentation/git-stash.txt | 68 +++++++++++++++++++------------------
 1 file changed, 35 insertions(+), 33 deletions(-)

diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
index 53e1a1205d..2dedc21997 100644
--- a/Documentation/git-stash.txt
+++ b/Documentation/git-stash.txt
@@ -58,31 +58,6 @@ non-option arguments are not allowed to prevent a misspelled
 subcommand from making an unwanted stash entry.  The two exceptions to this
 are `stash -p` which acts as alias for `stash push -p` and pathspecs,
 which are allowed after a double hyphen `--` for disambiguation.
-+
-When pathspec is given to 'git stash push', the new stash entry records the
-modified states only for the files that match the pathspec.  The index
-entries and working tree files are then rolled back to the state in
-HEAD only for these files, too, leaving files that do not match the
-pathspec intact.
-+
-If the `--keep-index` option is used, all changes already added to the
-index are left intact.
-+
-If the `--include-untracked` option is used, all untracked files are also
-stashed and then cleaned up with `git clean`, leaving the working directory
-in a very clean state. If the `--all` option is used instead then the
-ignored files are stashed and cleaned in addition to the untracked files.
-+
-With `--patch`, you can interactively select hunks from the diff
-between HEAD and the working tree to be stashed.  The stash entry is
-constructed such that its index state is the same as the index state
-of your repository, and its worktree contains only the changes you
-selected interactively.  The selected changes are then rolled back
-from your worktree. See the ``Interactive Mode'' section of
-linkgit:git-add[1] to learn how to operate the `--patch` mode.
-+
-The `--patch` option implies `--keep-index`.  You can use
-`--no-keep-index` to override this.
 
 save [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [<message>]::
 
@@ -128,14 +103,6 @@ pop [--index] [-q|--quiet] [<stash>]::
 Applying the state can fail with conflicts; in this case, it is not
 removed from the stash list. You need to resolve the conflicts by hand
 and call `git stash drop` manually afterwards.
-+
-If the `--index` option is used, then tries to reinstate not only the working
-tree's changes, but also the index's ones. However, this can fail, when you
-have conflicts (which are stored in the index, where you therefore can no
-longer apply the changes as they were originally).
-+
-When no `<stash>` is given, `stash@{0}` is assumed, otherwise `<stash>` must
-be a reference of the form `stash@{<revision>}`.
 
 apply [--index] [-q|--quiet] [<stash>]::
 
@@ -185,6 +152,41 @@ store::
 	reflog.  This is intended to be useful for scripts.  It is
 	probably not the command you want to use; see "push" above.
 
+If the `--all` option is used instead then the
+ignored files are stashed and cleaned in addition to the untracked files.
+
+If the `--include-untracked` option is used, all untracked files are also
+stashed and then cleaned up with `git clean`, leaving the working directory
+in a very clean state.
+
+If the `--index` option is used, then tries to reinstate not only the working
+tree's changes, but also the index's ones. However, this can fail, when you
+have conflicts (which are stored in the index, where you therefore can no
+longer apply the changes as they were originally).
+
+If the `--keep-index` option is used, all changes already added to the
+index are left intact.
+
+With `--patch`, you can interactively select hunks from the diff
+between HEAD and the working tree to be stashed.  The stash entry is
+constructed such that its index state is the same as the index state
+of your repository, and its worktree contains only the changes you
+selected interactively.  The selected changes are then rolled back
+from your worktree. See the ``Interactive Mode'' section of
+linkgit:git-add[1] to learn how to operate the `--patch` mode.
++
+The `--patch` option implies `--keep-index`.  You can use 
+`--no-keep-index` to override this.
+
+When pathspec is given to 'git stash push', the new stash entry records the
+modified states only for the files that match the pathspec.  The index
+entries and working tree files are then rolled back to the state in
+HEAD only for these files, too, leaving files that do not match the
+pathspec intact.
+
+When no `<stash>` is given, `stash@{0}` is assumed, otherwise `<stash>` must
+be a reference of the form `stash@{<revision>}`.
+
 DISCUSSION
 ----------
 
-- 
gitgitgadget


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

* [PATCH v2 4/8] doc: stash: split options from description (2)
  2020-02-10 14:45 ` [PATCH v2 0/8] Support --pathspec-from-file in rm, stash Alexandr Miloslavskiy via GitGitGadget
                     ` (2 preceding siblings ...)
  2020-02-10 14:45   ` [PATCH v2 3/8] doc: stash: split options from description (1) Alexandr Miloslavskiy via GitGitGadget
@ 2020-02-10 14:45   ` Alexandr Miloslavskiy via GitGitGadget
  2020-02-10 14:45   ` [PATCH v2 5/8] doc: stash: document more options Alexandr Miloslavskiy via GitGitGadget
                     ` (4 subsequent siblings)
  8 siblings, 0 replies; 41+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2020-02-10 14:45 UTC (permalink / raw)
  To: git
  Cc: Paul-Sebastian Ungureanu, Alexandr Miloslavskiy, Alexandr Miloslavskiy

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

Together with the previous patch, this brings docs for `git stash` to
the common layout used for most other commands (see for example docs
for `git add`, `git commit`, `git checkout`, `git reset`) where all
options are documented in a separate list.

After some thinking and having a look at docs for `git svn` and
`git `submodule`, I have arrived at following conclusions:
  * Options should be described in a list rather then text to
    facilitate lookup for user.
  * Single list is better then multiple lists because it avoids
    copy&pasting descriptions between subcommands (or, without
    copy&pasting, user will have to look up missing options in other
    subcommands).
  * As a consequence, commands section should only give brief info and
    list possible options. Since options have good enough names, user
	will only need to look up the "interesting" options.
  * Every option should list which subcommands support it.

I have decided to use alphabetical sorting in the list of options to
facilitate lookup for user.

There is some text editing done to make old descriptions better fit
into the list-style format.

Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 Documentation/git-stash.txt | 92 +++++++++++++++++++++++--------------
 1 file changed, 57 insertions(+), 35 deletions(-)

diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
index 2dedc21997..c1c16623cb 100644
--- a/Documentation/git-stash.txt
+++ b/Documentation/git-stash.txt
@@ -43,8 +43,8 @@ created stash, `stash@{1}` is the one before it, `stash@{2.hours.ago}`
 is also possible). Stashes may also be referenced by specifying just the
 stash index (e.g. the integer `n` is equivalent to `stash@{n}`).
 
-OPTIONS
--------
+COMMANDS
+--------
 
 push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--] [<pathspec>...]::
 
@@ -86,7 +86,7 @@ show [<options>] [<stash>]::
 
 	Show the changes recorded in the stash entry as a diff between the
 	stashed contents and the commit back when the stash entry was first
-	created. When no `<stash>` is given, it shows the latest one.
+	created.
 	By default, the command shows the diffstat, but it will accept any
 	format known to 'git diff' (e.g., `git stash show -p stash@{1}`
 	to view the second most recent entry in patch form).
@@ -116,8 +116,7 @@ branch <branchname> [<stash>]::
 	the commit at which the `<stash>` was originally created, applies the
 	changes recorded in `<stash>` to the new working tree and index.
 	If that succeeds, and `<stash>` is a reference of the form
-	`stash@{<revision>}`, it then drops the `<stash>`. When no `<stash>`
-	is given, applies the latest one.
+	`stash@{<revision>}`, it then drops the `<stash>`.
 +
 This is useful if the branch on which you ran `git stash push` has
 changed enough that `git stash apply` fails due to conflicts. Since
@@ -133,9 +132,6 @@ clear::
 drop [-q|--quiet] [<stash>]::
 
 	Remove a single stash entry from the list of stash entries.
-	When no `<stash>` is given, it removes the latest one.
-	i.e. `stash@{0}`, otherwise `<stash>` must be a valid stash
-	log reference of the form `stash@{<revision>}`.
 
 create::
 
@@ -152,40 +148,66 @@ store::
 	reflog.  This is intended to be useful for scripts.  It is
 	probably not the command you want to use; see "push" above.
 
-If the `--all` option is used instead then the
-ignored files are stashed and cleaned in addition to the untracked files.
-
-If the `--include-untracked` option is used, all untracked files are also
-stashed and then cleaned up with `git clean`, leaving the working directory
-in a very clean state.
+OPTIONS
+-------
+-a::
+--all::
+	This option is only valid for `push` and `save` commands.
++
+All ignored and untracked files are also stashed and then cleaned
+up with `git clean`.
 
-If the `--index` option is used, then tries to reinstate not only the working
-tree's changes, but also the index's ones. However, this can fail, when you
-have conflicts (which are stored in the index, where you therefore can no
-longer apply the changes as they were originally).
+-u::
+--include-untracked::
+	This option is only valid for `push` and `save` commands.
++
+All untracked files are also stashed and then cleaned up with
+`git clean`.
 
-If the `--keep-index` option is used, all changes already added to the
-index are left intact.
+--index::
+	This option is only valid for `pop` and `apply` commands.
++
+Tries to reinstate not only the working tree's changes, but also
+the index's ones. However, this can fail, when you have conflicts
+(which are stored in the index, where you therefore can no longer
+apply the changes as they were originally).
+
+-k::
+--keep-index::
+--no-keep-index::
+	This option is only valid for `push` and `save` commands.
++
+All changes already added to the index are left intact.
 
-With `--patch`, you can interactively select hunks from the diff
-between HEAD and the working tree to be stashed.  The stash entry is
-constructed such that its index state is the same as the index state
-of your repository, and its worktree contains only the changes you
-selected interactively.  The selected changes are then rolled back
-from your worktree. See the ``Interactive Mode'' section of
-linkgit:git-add[1] to learn how to operate the `--patch` mode.
+-p::
+--patch::
+	This option is only valid for `push` and `save` commands.
++
+Interactively select hunks from the diff between HEAD and the
+working tree to be stashed.  The stash entry is constructed such
+that its index state is the same as the index state of your
+repository, and its worktree contains only the changes you selected
+interactively.  The selected changes are then rolled back from your
+worktree. See the ``Interactive Mode'' section of linkgit:git-add[1]
+to learn how to operate the `--patch` mode.
 +
 The `--patch` option implies `--keep-index`.  You can use 
 `--no-keep-index` to override this.
 
-When pathspec is given to 'git stash push', the new stash entry records the
-modified states only for the files that match the pathspec.  The index
-entries and working tree files are then rolled back to the state in
-HEAD only for these files, too, leaving files that do not match the
-pathspec intact.
-
-When no `<stash>` is given, `stash@{0}` is assumed, otherwise `<stash>` must
-be a reference of the form `stash@{<revision>}`.
+<pathspec>...::
+	This option is only valid for `push` command.
++
+The new stash entry records the modified states only for the files
+that match the pathspec.  The index entries and working tree files
+are then rolled back to the state in HEAD only for these files,
+too, leaving files that do not match the pathspec intact.
+
+<stash>::
+	This option is only valid for `apply`, `branch`, `drop`, `pop`,
+	`show` commands.
++
+A reference of the form `stash@{<revision>}`. When no `<stash>` is
+given, the latest stash is assumed (that is, `stash@{0}`).
 
 DISCUSSION
 ----------
-- 
gitgitgadget


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

* [PATCH v2 5/8] doc: stash: document more options
  2020-02-10 14:45 ` [PATCH v2 0/8] Support --pathspec-from-file in rm, stash Alexandr Miloslavskiy via GitGitGadget
                     ` (3 preceding siblings ...)
  2020-02-10 14:45   ` [PATCH v2 4/8] doc: stash: split options from description (2) Alexandr Miloslavskiy via GitGitGadget
@ 2020-02-10 14:45   ` Alexandr Miloslavskiy via GitGitGadget
  2020-02-10 14:45   ` [PATCH v2 6/8] doc: stash: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget
                     ` (3 subsequent siblings)
  8 siblings, 0 replies; 41+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2020-02-10 14:45 UTC (permalink / raw)
  To: git
  Cc: Paul-Sebastian Ungureanu, Alexandr Miloslavskiy, Alexandr Miloslavskiy

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

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

diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
index c1c16623cb..6dc0a5b0ee 100644
--- a/Documentation/git-stash.txt
+++ b/Documentation/git-stash.txt
@@ -194,6 +194,18 @@ to learn how to operate the `--patch` mode.
 The `--patch` option implies `--keep-index`.  You can use 
 `--no-keep-index` to override this.
 
+-q::
+--quiet::
+	This option is only valid for `apply`, `drop`, `pop`, `push`,
+	`save`, `store` commands.
++
+Quiet, suppress feedback messages.
+
+\--::
+	This option is only valid for `push` command.
++
+Separates pathspec from options for disambiguation purposes.
+
 <pathspec>...::
 	This option is only valid for `push` command.
 +
-- 
gitgitgadget


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

* [PATCH v2 6/8] doc: stash: synchronize <pathspec> description
  2020-02-10 14:45 ` [PATCH v2 0/8] Support --pathspec-from-file in rm, stash Alexandr Miloslavskiy via GitGitGadget
                     ` (4 preceding siblings ...)
  2020-02-10 14:45   ` [PATCH v2 5/8] doc: stash: document more options Alexandr Miloslavskiy via GitGitGadget
@ 2020-02-10 14:45   ` Alexandr Miloslavskiy via GitGitGadget
  2020-02-10 14:45   ` [PATCH v2 7/8] stash: eliminate crude option parsing Alexandr Miloslavskiy via GitGitGadget
                     ` (2 subsequent siblings)
  8 siblings, 0 replies; 41+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2020-02-10 14:45 UTC (permalink / raw)
  To: git
  Cc: Paul-Sebastian Ungureanu, Alexandr Miloslavskiy, Alexandr Miloslavskiy

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

This patch continues the effort that is already applied to
`git commit`, `git reset`, `git checkout` etc.

1) Added reference to 'linkgit:gitglossary[7]'.
2) Fixed mentions of incorrectly plural "pathspecs".

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

diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
index 6dc0a5b0ee..52e64985bd 100644
--- a/Documentation/git-stash.txt
+++ b/Documentation/git-stash.txt
@@ -56,13 +56,13 @@ push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q
 For quickly making a snapshot, you can omit "push".  In this mode,
 non-option arguments are not allowed to prevent a misspelled
 subcommand from making an unwanted stash entry.  The two exceptions to this
-are `stash -p` which acts as alias for `stash push -p` and pathspecs,
+are `stash -p` which acts as alias for `stash push -p` and pathspec elements,
 which are allowed after a double hyphen `--` for disambiguation.
 
 save [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [<message>]::
 
 	This option is deprecated in favour of 'git stash push'.  It
-	differs from "stash push" in that it cannot take pathspecs.
+	differs from "stash push" in that it cannot take pathspec.
 	Instead, all non-option arguments are concatenated to form the stash
 	message.
 
@@ -213,6 +213,8 @@ The new stash entry records the modified states only for the files
 that match the pathspec.  The index entries and working tree files
 are then rolled back to the state in HEAD only for these files,
 too, leaving files that do not match the pathspec intact.
++
+For more details, see the 'pathspec' entry in linkgit:gitglossary[7].
 
 <stash>::
 	This option is only valid for `apply`, `branch`, `drop`, `pop`,
-- 
gitgitgadget


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

* [PATCH v2 7/8] stash: eliminate crude option parsing
  2020-02-10 14:45 ` [PATCH v2 0/8] Support --pathspec-from-file in rm, stash Alexandr Miloslavskiy via GitGitGadget
                     ` (5 preceding siblings ...)
  2020-02-10 14:45   ` [PATCH v2 6/8] doc: stash: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget
@ 2020-02-10 14:45   ` Alexandr Miloslavskiy via GitGitGadget
  2020-02-10 14:45   ` [PATCH v2 8/8] stash push: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
  2020-02-17 17:25   ` [PATCH v3 0/8] Support --pathspec-from-file in rm, stash Alexandr Miloslavskiy via GitGitGadget
  8 siblings, 0 replies; 41+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2020-02-10 14:45 UTC (permalink / raw)
  To: git
  Cc: Paul-Sebastian Ungureanu, Alexandr Miloslavskiy, Alexandr Miloslavskiy

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

Eliminate crude option parsing and rely on real parsing instead, because
1) Crude parsing is crude, for example it's not capable of
   handling things like `git stash -m Message`
2) Adding options in two places is inconvenient and prone to bugs

As a side result, the case of `git stash -m Message` gets fixed.
Also give a good error message instead of just throwing usage at user.

----

Some review of what's been happening to this code:

Before [1], `git-stash.sh` only verified that all args begin with `-` :

	# The default command is "push" if nothing but options are given
	seen_non_option=
	for opt
	do
		case "$opt" in
		--) break ;;
		-*) ;;
		*) seen_non_option=t; break ;;
		esac
	done

Later, [1] introduced the duplicate code I'm now removing, also making
the previous test more strict by white-listing options.

----

[1] Commit 40af1468 ("stash: convert `stash--helper.c` into `stash.c`" 2019-02-26)

Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 builtin/stash.c  | 59 +++++++++++++++++-------------------------------
 t/t3903-stash.sh |  5 ++++
 2 files changed, 26 insertions(+), 38 deletions(-)

diff --git a/builtin/stash.c b/builtin/stash.c
index 879fc5f368..ed84ff2e16 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -1451,8 +1451,10 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
 	return ret;
 }
 
-static int push_stash(int argc, const char **argv, const char *prefix)
+static int push_stash(int argc, const char **argv, const char *prefix,
+		      int push_assumed)
 {
+	int force_assume = 0;
 	int keep_index = -1;
 	int patch_mode = 0;
 	int include_untracked = 0;
@@ -1474,10 +1476,22 @@ static int push_stash(int argc, const char **argv, const char *prefix)
 		OPT_END()
 	};
 
-	if (argc)
+	if (argc) {
+		force_assume = !strcmp(argv[0], "-p");
 		argc = parse_options(argc, argv, prefix, options,
 				     git_stash_push_usage,
-				     0);
+				     PARSE_OPT_KEEP_DASHDASH);
+	}
+
+	if (argc) {
+		if (!strcmp(argv[0], "--")) {
+			argc--;
+			argv++;
+		} else if (push_assumed && !force_assume) {
+			die("subcommand wasn't specified; 'push' can't be assumed due to unexpected token '%s'",
+			    argv[0]);
+		}
+	}
 
 	parse_pathspec(&ps, 0, PATHSPEC_PREFER_FULL | PATHSPEC_PREFIX_ORIGIN,
 		       prefix, argv);
@@ -1550,7 +1564,6 @@ static int use_builtin_stash(void)
 
 int cmd_stash(int argc, const char **argv, const char *prefix)
 {
-	int i = -1;
 	pid_t pid = getpid();
 	const char *index_file;
 	struct argv_array args = ARGV_ARRAY_INIT;
@@ -1583,7 +1596,7 @@ int cmd_stash(int argc, const char **argv, const char *prefix)
 		    (uintmax_t)pid);
 
 	if (!argc)
-		return !!push_stash(0, NULL, prefix);
+		return !!push_stash(0, NULL, prefix, 0);
 	else if (!strcmp(argv[0], "apply"))
 		return !!apply_stash(argc, argv, prefix);
 	else if (!strcmp(argv[0], "clear"))
@@ -1603,45 +1616,15 @@ int cmd_stash(int argc, const char **argv, const char *prefix)
 	else if (!strcmp(argv[0], "create"))
 		return !!create_stash(argc, argv, prefix);
 	else if (!strcmp(argv[0], "push"))
-		return !!push_stash(argc, argv, prefix);
+		return !!push_stash(argc, argv, prefix, 0);
 	else if (!strcmp(argv[0], "save"))
 		return !!save_stash(argc, argv, prefix);
 	else if (*argv[0] != '-')
 		usage_msg_opt(xstrfmt(_("unknown subcommand: %s"), argv[0]),
 			      git_stash_usage, options);
 
-	if (strcmp(argv[0], "-p")) {
-		while (++i < argc && strcmp(argv[i], "--")) {
-			/*
-			 * `akpqu` is a string which contains all short options,
-			 * except `-m` which is verified separately.
-			 */
-			if ((strlen(argv[i]) == 2) && *argv[i] == '-' &&
-			    strchr("akpqu", argv[i][1]))
-				continue;
-
-			if (!strcmp(argv[i], "--all") ||
-			    !strcmp(argv[i], "--keep-index") ||
-			    !strcmp(argv[i], "--no-keep-index") ||
-			    !strcmp(argv[i], "--patch") ||
-			    !strcmp(argv[i], "--quiet") ||
-			    !strcmp(argv[i], "--include-untracked"))
-				continue;
-
-			/*
-			 * `-m` and `--message=` are verified separately because
-			 * they need to be immediately followed by a string
-			 * (i.e.`-m"foobar"` or `--message="foobar"`).
-			 */
-			if (starts_with(argv[i], "-m") ||
-			    starts_with(argv[i], "--message="))
-				continue;
-
-			usage_with_options(git_stash_usage, options);
-		}
-	}
-
+	/* Assume 'stash push' */
 	argv_array_push(&args, "push");
 	argv_array_pushv(&args, argv);
-	return !!push_stash(args.argc, args.argv, prefix);
+	return !!push_stash(args.argc, args.argv, prefix, 1);
 }
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index ea56e85e70..3ad23e2502 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -285,6 +285,11 @@ test_expect_success 'stash --no-keep-index' '
 	test bar,bar2 = $(cat file),$(cat file2)
 '
 
+test_expect_success 'dont assume push with non-option args' '
+	test_must_fail git stash -q drop 2>err &&
+	test_i18ngrep -e "subcommand wasn'\''t specified; '\''push'\'' can'\''t be assumed due to unexpected token '\''drop'\''" err
+'
+
 test_expect_success 'stash --invalid-option' '
 	echo bar5 >file &&
 	echo bar6 >file2 &&
-- 
gitgitgadget


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

* [PATCH v2 8/8] stash push: support the --pathspec-from-file option
  2020-02-10 14:45 ` [PATCH v2 0/8] Support --pathspec-from-file in rm, stash Alexandr Miloslavskiy via GitGitGadget
                     ` (6 preceding siblings ...)
  2020-02-10 14:45   ` [PATCH v2 7/8] stash: eliminate crude option parsing Alexandr Miloslavskiy via GitGitGadget
@ 2020-02-10 14:45   ` Alexandr Miloslavskiy via GitGitGadget
  2020-02-17 17:25   ` [PATCH v3 0/8] Support --pathspec-from-file in rm, stash Alexandr Miloslavskiy via GitGitGadget
  8 siblings, 0 replies; 41+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2020-02-10 14:45 UTC (permalink / raw)
  To: git
  Cc: Paul-Sebastian Ungureanu, Alexandr Miloslavskiy, 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 `-`. Such use case is not
   really expected.
2) It is not allowed to pass pathspec in both args and file.

Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 Documentation/git-stash.txt    |  20 ++++++-
 builtin/stash.c                |  20 +++++++
 t/t3909-stash-pathspec-file.sh | 100 +++++++++++++++++++++++++++++++++
 3 files changed, 139 insertions(+), 1 deletion(-)
 create mode 100755 t/t3909-stash-pathspec-file.sh

diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
index 52e64985bd..79949f8617 100644
--- a/Documentation/git-stash.txt
+++ b/Documentation/git-stash.txt
@@ -15,6 +15,7 @@ SYNOPSIS
 'git stash' branch <branchname> [<stash>]
 'git stash' [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]
 	     [-u|--include-untracked] [-a|--all] [-m|--message <message>]
+	     [--pathspec-from-file=<file> [--pathspec-file-nul]]
 	     [--] [<pathspec>...]]
 'git stash' clear
 'git stash' create [<message>]
@@ -46,7 +47,7 @@ stash index (e.g. the integer `n` is equivalent to `stash@{n}`).
 COMMANDS
 --------
 
-push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--] [<pathspec>...]::
+push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--pathspec-from-file=<file> [--pathspec-file-nul]] [--] [<pathspec>...]::
 
 	Save your local modifications to a new 'stash entry' and roll them
 	back to HEAD (in the working tree and in the index).
@@ -194,6 +195,23 @@ to learn how to operate the `--patch` mode.
 The `--patch` option implies `--keep-index`.  You can use 
 `--no-keep-index` to override this.
 
+--pathspec-from-file=<file>::
+	This option is only valid for `push` command.
++
+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::
+	This option is only valid for `push` command.
++
+Only meaningful with `--pathspec-from-file`. Pathspec elements are
+separated with NUL character and all other characters are taken
+literally (including newlines and quotes).
+
 -q::
 --quiet::
 	This option is only valid for `apply`, `drop`, `pop`, `push`,
diff --git a/builtin/stash.c b/builtin/stash.c
index ed84ff2e16..78af6ce564 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -27,6 +27,7 @@ static const char * const git_stash_usage[] = {
 	N_("git stash clear"),
 	N_("git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
 	   "          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
+	   "          [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
 	   "          [--] [<pathspec>...]]"),
 	N_("git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
 	   "          [-u|--include-untracked] [-a|--all] [<message>]"),
@@ -1459,7 +1460,9 @@ static int push_stash(int argc, const char **argv, const char *prefix,
 	int patch_mode = 0;
 	int include_untracked = 0;
 	int quiet = 0;
+	int pathspec_file_nul = 0;
 	const char *stash_msg = NULL;
+	const char *pathspec_from_file = NULL;
 	struct pathspec ps;
 	struct option options[] = {
 		OPT_BOOL('k', "keep-index", &keep_index,
@@ -1473,6 +1476,8 @@ static int push_stash(int argc, const char **argv, const char *prefix,
 			    N_("include ignore files"), 2),
 		OPT_STRING('m', "message", &stash_msg, N_("message"),
 			   N_("stash message")),
+		OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
+		OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
 		OPT_END()
 	};
 
@@ -1495,6 +1500,21 @@ static int push_stash(int argc, const char **argv, const char *prefix,
 
 	parse_pathspec(&ps, 0, PATHSPEC_PREFER_FULL | PATHSPEC_PREFIX_ORIGIN,
 		       prefix, argv);
+
+	if (pathspec_from_file) {
+		if (patch_mode)
+			die(_("--pathspec-from-file is incompatible with --patch"));
+
+		if (ps.nr)
+			die(_("--pathspec-from-file is incompatible with pathspec arguments"));
+
+		parse_pathspec_file(&ps, 0,
+				    PATHSPEC_PREFER_FULL | PATHSPEC_PREFIX_ORIGIN,
+				    prefix, pathspec_from_file, pathspec_file_nul);
+	} else if (pathspec_file_nul) {
+		die(_("--pathspec-file-nul requires --pathspec-from-file"));
+	}
+
 	return do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode,
 			     include_untracked);
 }
diff --git a/t/t3909-stash-pathspec-file.sh b/t/t3909-stash-pathspec-file.sh
new file mode 100755
index 0000000000..55e050cfd4
--- /dev/null
+++ b/t/t3909-stash-pathspec-file.sh
@@ -0,0 +1,100 @@
+#!/bin/sh
+
+test_description='stash --pathspec-from-file'
+
+. ./test-lib.sh
+
+test_tick
+
+test_expect_success setup '
+	>fileA.t &&
+	>fileB.t &&
+	>fileC.t &&
+	>fileD.t &&
+	git add fileA.t fileB.t fileC.t fileD.t &&
+	git commit -m "Files" &&
+
+	git tag checkpoint
+'
+
+restore_checkpoint () {
+	git reset --hard checkpoint
+}
+
+verify_expect () {
+	git stash show --name-status >actual &&
+	test_cmp expect actual
+}
+
+test_expect_success 'simplest' '
+	restore_checkpoint &&
+
+	# More files are written to make sure that git didnt ignore
+	# --pathspec-from-file, stashing everything
+	echo A >fileA.t &&
+	echo B >fileB.t &&
+	echo C >fileC.t &&
+	echo D >fileD.t &&
+
+	cat >expect <<-\EOF &&
+	M	fileA.t
+	EOF
+
+	echo fileA.t | git stash push --pathspec-from-file=- &&
+	verify_expect
+'
+
+test_expect_success '--pathspec-file-nul' '
+	restore_checkpoint &&
+
+	# More files are written to make sure that git didnt ignore
+	# --pathspec-from-file, stashing everything
+	echo A >fileA.t &&
+	echo B >fileB.t &&
+	echo C >fileC.t &&
+	echo D >fileD.t &&
+
+	cat >expect <<-\EOF &&
+	M	fileA.t
+	M	fileB.t
+	EOF
+
+	printf "fileA.t\0fileB.t\0" | git stash push --pathspec-from-file=- --pathspec-file-nul &&
+	verify_expect
+'
+
+test_expect_success 'only touches what was listed' '
+	restore_checkpoint &&
+
+	# More files are written to make sure that git didnt ignore
+	# --pathspec-from-file, stashing everything
+	echo A >fileA.t &&
+	echo B >fileB.t &&
+	echo C >fileC.t &&
+	echo D >fileD.t &&
+
+	cat >expect <<-\EOF &&
+	M	fileB.t
+	M	fileC.t
+	EOF
+
+	printf "fileB.t\nfileC.t\n" | git stash push --pathspec-from-file=- &&
+	verify_expect
+'
+
+test_expect_success 'error conditions' '
+	restore_checkpoint &&
+	echo A >fileA.t &&
+	echo fileA.t >list &&
+
+	test_must_fail git stash push --pathspec-from-file=list --patch 2>err &&
+	test_i18ngrep -e "--pathspec-from-file is incompatible with --patch" err &&
+
+	test_must_fail git stash push --pathspec-from-file=list -- fileA.t 2>err &&
+	test_i18ngrep -e "--pathspec-from-file is incompatible with pathspec arguments" err &&
+
+	test_must_fail git stash push --pathspec-file-nul 2>err &&
+	test_i18ngrep -e "--pathspec-file-nul requires --pathspec-from-file" err
+'
+
+test_done
-- 
gitgitgadget

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

* Re: [PATCH 2/8] rm: support the --pathspec-from-file option
  2020-01-21 19:36   ` Junio C Hamano
@ 2020-02-10 14:46     ` Alexandr Miloslavskiy
  2020-02-10 18:48       ` Junio C Hamano
  0 siblings, 1 reply; 41+ messages in thread
From: Alexandr Miloslavskiy @ 2020-02-10 14:46 UTC (permalink / raw)
  To: Junio C Hamano, Alexandr Miloslavskiy via GitGitGadget
  Cc: git, Paul-Sebastian Ungureanu

Sorry for late reply, I was on vacation. Now I'm back and ready to 
continue :)

Thanks for your review!

On 21.01.2020 20:36, Junio C Hamano wrote:
>> Decisions taken for simplicity:
>> 1) It is not allowed to pass pathspec in both args and file.
>>
>> `if (!argc)` block was adapted to work with --pathspec-from-file. For
>> that, I also had to parse pathspec earlier. Now it happens before
>> `read_cache()` / `hold_locked_index()` / `setup_work_tree()`, which
>> sounds fine to me.
> 
> That is not an explanation nor justification.

I'm not exactly sure what are you suggesting. My best guess is that you 
want to remove "`if (!argc)` block was adapted" paragraph from commit 
message? I thought about it and it feels wrong to leave this change 
unexplained. Or are you suggesting to reword it? If so, please give a hint.

>> In case of empty pathspec, there is now a clear error message instead
>> of showing usage.
> 
> Hmph, "git rm --pathspec-from-file=/dev/null" would say "nothing
> specified, nothing removed" and it makes perfect sense, but I am not
> sure "git rm" that gives the same message is better than the output
> by usage_with_options(builtin_rm_usage, builtin_rm_options).

What feels wrong to me is when I make a mistake and git just slams me 
with usage, and then it's up to me to figure what could be wrong. I 
myself struggled to find a mistake a couple times (in similar cases, not 
in this specific one) and didn't like the experience.

This could be a lot worse when there's no mistake, just the file was 
empty - but you already agreed that showing a new error message is 
reasonable with '--pathspec-from-file'.

Still, without '--pathspec-from-file', it should still be better to 
point to a specific error rather then "here's usage and try to find a 
difference". I have reworded the error message in V2 in hopes that it 
will be less controversial.

If you still don't like it, I will change it to only show the new error 
with '--pathspec-from-file'.

>> +static int ignore_unmatch = 0, pathspec_file_nul = 0;
>> +static char *pathspec_from_file = NULL;
> 
> We may want to clean these "explicitly initialize to 0/NULL" up at
> some point.  The clean-up itself would not be in the scope of this
> patch, of course, but not making it worse is something this patch
> can do to help.

Changed in V2.

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

* Re: [PATCH 4/8] doc: stash: split options from description (2)
  2020-01-21 20:21   ` Junio C Hamano
@ 2020-02-10 14:47     ` Alexandr Miloslavskiy
  0 siblings, 0 replies; 41+ messages in thread
From: Alexandr Miloslavskiy @ 2020-02-10 14:47 UTC (permalink / raw)
  To: Junio C Hamano, Alexandr Miloslavskiy via GitGitGadget
  Cc: git, Paul-Sebastian Ungureanu

On 21.01.2020 21:21, Junio C Hamano wrote:
> I have a mixed feelings about this approach.  While I am sympathetic
> to the "have a single place to describe all" approach this patch
> takes, the approach needs to be executed with care when subcommands
> do not share much of the options at all.  Those readers who jump to
> the "OPTIONS" section and try to ignore anything outside the section
> may not easily notice that --keep-index only applies to subcommands
> that creates a new stash, and meaningless to subcommands that lets
> you inspect existing stashes, or apply one to the working tree (and
> optionally to the index), for example.  If the orinal documentation
> did not use "OPTIONS" as the section header and instead said perhaps
> "SUBCOMMANDS", it would have been even better, but otherwise I would
> suspect that the original "the options understood by 'push' are all
> described under the part that begins with 'push [-p] [-k] ...'
> command line" arrangement was much easier to understand when reading
> them through for the first time to learn and also to find what the
> user is looking for after learning the "concept" (e.g. "with
> 'stash', there is a way to stash-away the changes made to the
> working tree") but before becoming familiar with exact set of
> options for each subcommand (e.g. "and there was an option that let
> me stash only partial changes piecemeal, but what was it spelled?").
> 
> If we were to make the result of "a single place to describe all"
> approach anything useful, I think at least
> 
>   (1) the list itself should make it clear that it does not talk
>       about options related to listing and showing at all,
>       before enumerating dashed options.
> 
>   (2) each item in the enumeration should identify which
>       subcommand(s) accept(s) it.
> 
> So, I dunno.

I have updated the patch with (2). Sorry, I didn't understand what you 
mean in (1).

I have included my reasoning in commit message. If you feel against this 
change, I guess I'll just revert it. Afterall, my only goal was to 
describe new options. Tried to change things because I didn't like how 
this doc goes against the layout I have seen in all previous docs I edited.

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

* Re: [PATCH 2/8] rm: support the --pathspec-from-file option
  2020-02-10 14:46     ` Alexandr Miloslavskiy
@ 2020-02-10 18:48       ` Junio C Hamano
  2020-02-17 17:25         ` Alexandr Miloslavskiy
  0 siblings, 1 reply; 41+ messages in thread
From: Junio C Hamano @ 2020-02-10 18:48 UTC (permalink / raw)
  To: Alexandr Miloslavskiy
  Cc: Alexandr Miloslavskiy via GitGitGadget, git, Paul-Sebastian Ungureanu

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

> Sorry for late reply, I was on vacation. Now I'm back and ready to
> continue :)
>
> Thanks for your review!
>
> On 21.01.2020 20:36, Junio C Hamano wrote:
>>> Decisions taken for simplicity:
>>> 1) It is not allowed to pass pathspec in both args and file.
>>>
>>> `if (!argc)` block was adapted to work with --pathspec-from-file. For
>>> that, I also had to parse pathspec earlier. Now it happens before
>>> `read_cache()` / `hold_locked_index()` / `setup_work_tree()`, which
>>> sounds fine to me.
>>
>> That is not an explanation nor justification.
>
> I'm not exactly sure what are you suggesting.

I expected that the proposed log message to explain and justify why
a change (in behaviour, in design, etc.) is made.  "There is this
limitation" is not a justification---"because of such and such
reasons, there is this limitation, otherwise such and such bad
things happen" is.


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

* Re: [PATCH v2 2/8] rm: support the --pathspec-from-file option
  2020-02-10 14:45   ` [PATCH v2 2/8] rm: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
@ 2020-02-10 20:39     ` Junio C Hamano
  2020-02-17 17:27       ` Alexandr Miloslavskiy
  0 siblings, 1 reply; 41+ messages in thread
From: Junio C Hamano @ 2020-02-10 20:39 UTC (permalink / raw)
  To: Alexandr Miloslavskiy via GitGitGadget
  Cc: git, Paul-Sebastian Ungureanu, Alexandr Miloslavskiy

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

> diff --git a/t/t3601-rm-pathspec-file.sh b/t/t3601-rm-pathspec-file.sh
> new file mode 100755
> index 0000000000..4542a0f02f
> --- /dev/null
> +++ b/t/t3601-rm-pathspec-file.sh
> @@ -0,0 +1,79 @@
> +#!/bin/sh
> +
> +test_description='rm --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 fileA.t fileB.t fileC.t fileD.t &&
> +	git commit -m "files" &&
> +	

Trailing whitespace on this line.

> +	git tag checkpoint
> +'
> + ...
> +test_expect_success 'error conditions' '
> +	restore_checkpoint &&
> +	echo fileA.t >list &&
> +
> +	test_must_fail git rm --pathspec-from-file=list -- fileA.t 2>err &&
> +	test_i18ngrep -e "--pathspec-from-file is incompatible with pathspec arguments" err &&
> +
> +	test_must_fail git rm --pathspec-file-nul 2>err &&
> +	test_i18ngrep -e "--pathspec-file-nul requires --pathspec-from-file" err &&
> +	

And here too.

> +	>empty_list &&
> +	test_must_fail git rm --pathspec-from-file=empty_list 2>err &&
> +	test_i18ngrep -e "No pathspec was given. Which files should I remove?" err
> +'
> +
> +test_done

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

* [PATCH v3 0/8] Support --pathspec-from-file in rm, stash
  2020-02-10 14:45 ` [PATCH v2 0/8] Support --pathspec-from-file in rm, stash Alexandr Miloslavskiy via GitGitGadget
                     ` (7 preceding siblings ...)
  2020-02-10 14:45   ` [PATCH v2 8/8] stash push: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
@ 2020-02-17 17:25   ` Alexandr Miloslavskiy via GitGitGadget
  2020-02-17 17:25     ` [PATCH v3 1/8] doc: rm: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget
                       ` (7 more replies)
  8 siblings, 8 replies; 41+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2020-02-17 17:25 UTC (permalink / raw)
  To: git; +Cc: Paul-Sebastian Ungureanu, Alexandr Miloslavskiy

Changes since V1
------------------
Some polishing based on code review in V1
1) Improved error message for the case where pathspec is not given to `git rm`
2) Removed explicit variable initialization to 0 / NULL
3) Polishing in docs for `git stash`

------------------
This series continues the effort to support `--pathspec-from-file`
in various git commands. Series already in `master`: [1][2]

Cc'ing Paul-Sebastian Ungureanu because I touched his git stash code.

[1] https://public-inbox.org/git/pull.445.git.1572895605.gitgitgadget@gmail.com/
[2] https://lore.kernel.org/git/pull.490.git.1576161385.gitgitgadget@gmail.com/

Alexandr Miloslavskiy (8):
  doc: rm: synchronize <pathspec> description
  rm: support the --pathspec-from-file option
  doc: stash: split options from description (1)
  doc: stash: split options from description (2)
  doc: stash: document more options
  doc: stash: synchronize <pathspec> description
  stash: eliminate crude option parsing
  stash push: support the --pathspec-from-file option

 Documentation/git-rm.txt       |  61 +++++++-------
 Documentation/git-stash.txt    | 144 +++++++++++++++++++++++----------
 builtin/rm.c                   |  28 +++++--
 builtin/stash.c                |  79 +++++++++---------
 t/t3601-rm-pathspec-file.sh    |  79 ++++++++++++++++++
 t/t3903-stash.sh               |   5 ++
 t/t3909-stash-pathspec-file.sh | 100 +++++++++++++++++++++++
 7 files changed, 381 insertions(+), 115 deletions(-)
 create mode 100755 t/t3601-rm-pathspec-file.sh
 create mode 100755 t/t3909-stash-pathspec-file.sh


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

Range-diff vs v2:

 1:  2e8c8ad8158 = 1:  cf065e905dc doc: rm: synchronize <pathspec> description
 2:  7ccbab52e51 ! 2:  7c657dea89e rm: support the --pathspec-from-file option
     @@ -5,17 +5,36 @@
          Decisions taken for simplicity:
          1) It is not allowed to pass pathspec in both args and file.
      
     -    `if (!argc)` block was adapted to work with --pathspec-from-file. For
     -    that, I also had to parse pathspec earlier. Now it happens before
     -    `read_cache()` / `hold_locked_index()` / `setup_work_tree()`, which
     -    sounds fine to me.
     +    Adjustments were needed for `if (!argc)` block:
      
     -    In case of empty pathspec, there is now a clear error message instead
     -    of showing usage. As a consequence, exit code has also changed. Judging
     -    from [1] it doesn't seem that showing usage in this case was important
     -    (according to commit message, it was to avoid segfault), and it doesn't
     -    fit into how other commands react to empty pathspec. Finally, the new
     -    error message is easier to understand.
     +    This code actually means "pathspec is not present". Previously, pathspec
     +    could only come from commandline arguments, so testing for `argc` was a
     +    valid way of testing for the presence of pathspec. But this is no longer
     +    true with `--pathspec-from-file`.
     +
     +    During the entire `--pathspec-from-file` story, I tried to keep its
     +    behavior very close to giving pathspec on commandline, so that switching
     +    from one to another doesn't involve any surprises.
     +
     +    However, throwing usage at user in the case of empty
     +    `--pathspec-from-file` would puzzle because there's nothing wrong with
     +    "usage" (that is, argc/argv array).
     +
     +    On the other hand, throwing usage in the old case also feels bad to me.
     +    While it's less of a puzzle, I (as user) never liked the experience of
     +    comparing my commandline to "usage", trying to spot a difference. Since
     +    it's already known what the error is, it feels a lot better to give that
     +    specific error to user.
     +
     +    Judging from [1] it doesn't seem that showing usage in this case was
     +    important (the patch was to avoid segfault), and it doesn't fit into how
     +    other commands react to empty pathspec (see for example `git add` with a
     +    custom message).
     +
     +    Therefore, I decided to show new error text in both cases. In order to
     +    continue testing for error early, I moved `parse_pathspec()` higher. Now
     +    it happens before `read_cache()` / `hold_locked_index()` /
     +    `setup_work_tree()`, which shouldn't cause any issues.
      
          [1] Commit 7612a1ef ("git-rm: honor -n flag" 2006-06-09)
      
     @@ -136,7 +155,7 @@
      +	echo D >fileD.t &&
      +	git add fileA.t fileB.t fileC.t fileD.t &&
      +	git commit -m "files" &&
     -+	
     ++
      +	git tag checkpoint
      +'
      +
     @@ -193,7 +212,7 @@
      +
      +	test_must_fail git rm --pathspec-file-nul 2>err &&
      +	test_i18ngrep -e "--pathspec-file-nul requires --pathspec-from-file" err &&
     -+	
     ++
      +	>empty_list &&
      +	test_must_fail git rm --pathspec-from-file=empty_list 2>err &&
      +	test_i18ngrep -e "No pathspec was given. Which files should I remove?" err
 3:  8c212fc0ed4 = 3:  bb300215d49 doc: stash: split options from description (1)
 4:  db3a96720ce = 4:  fdaf4532404 doc: stash: split options from description (2)
 5:  f91ec08b472 = 5:  764b8668d10 doc: stash: document more options
 6:  04e2fd5865f = 6:  7353b06e30e doc: stash: synchronize <pathspec> description
 7:  0558cbbe38e = 7:  d34eaf4a272 stash: eliminate crude option parsing
 8:  0c6f28dc68d = 8:  6465a292b55 stash push: support the --pathspec-from-file option

-- 
gitgitgadget

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

* [PATCH v3 1/8] doc: rm: synchronize <pathspec> description
  2020-02-17 17:25   ` [PATCH v3 0/8] Support --pathspec-from-file in rm, stash Alexandr Miloslavskiy via GitGitGadget
@ 2020-02-17 17:25     ` Alexandr Miloslavskiy via GitGitGadget
  2020-02-17 17:25     ` [PATCH v3 2/8] rm: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
                       ` (6 subsequent siblings)
  7 siblings, 0 replies; 41+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2020-02-17 17:25 UTC (permalink / raw)
  To: git
  Cc: Paul-Sebastian Ungureanu, Alexandr Miloslavskiy, Alexandr Miloslavskiy

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

This patch continues the effort that is already applied to
`git commit`, `git reset`, `git checkout` etc.

1) Changed outdated descriptions to mention pathspec instead.
2) Added reference to 'linkgit:gitglossary[7]'.
3) Removed content that merely repeated gitglossary.
4) Merged the remainder of "discussion" into `<patchspec>`.

Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 Documentation/git-rm.txt | 50 +++++++++++++++++-----------------------
 1 file changed, 21 insertions(+), 29 deletions(-)

diff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt
index b5c46223c44..e02a08e5efd 100644
--- a/Documentation/git-rm.txt
+++ b/Documentation/git-rm.txt
@@ -8,16 +8,16 @@ git-rm - Remove files from the working tree and from the index
 SYNOPSIS
 --------
 [verse]
-'git rm' [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] <file>...
+'git rm' [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] <pathspec>...
 
 DESCRIPTION
 -----------
-Remove files from the index, or from the working tree and the index.
-`git rm` will not remove a file from just your working directory.
-(There is no option to remove a file only from the working tree
-and yet keep it in the index; use `/bin/rm` if you want to do that.)
-The files being removed have to be identical to the tip of the branch,
-and no updates to their contents can be staged in the index,
+Remove files matching pathspec from the index, or from the working tree
+and the index. `git rm` will not remove a file from just your working
+directory. (There is no option to remove a file only from the working
+tree and yet keep it in the index; use `/bin/rm` if you want to do
+that.) The files being removed have to be identical to the tip of the
+branch, and no updates to their contents can be staged in the index,
 though that default behavior can be overridden with the `-f` option.
 When `--cached` is given, the staged content has to
 match either the tip of the branch or the file on disk,
@@ -26,15 +26,20 @@ allowing the file to be removed from just the index.
 
 OPTIONS
 -------
-<file>...::
-	Files to remove.  Fileglobs (e.g. `*.c`) can be given to
-	remove all matching files.  If you want Git to expand
-	file glob characters, you may need to shell-escape them.
-	A leading directory name
-	(e.g. `dir` to remove `dir/file1` and `dir/file2`) can be
-	given to remove all files in the directory, and recursively
-	all sub-directories,
-	but this requires the `-r` option to be explicitly given.
+<pathspec>...::
+	Files to remove.  A leading directory name (e.g. `dir` to remove
+	`dir/file1` and `dir/file2`) can be given to remove all files in
+	the directory, and recursively all sub-directories, but this
+	requires the `-r` option to be explicitly given.
++
+The command removes only the paths that are known to Git.
++
+File globbing matches across directory boundaries.  Thus, given two
+directories `d` and `d2`, there is a difference between using
+`git rm 'd*'` and `git rm 'd/*'`, as the former will also remove all
+of directory `d2`.
++
+For more details, see the 'pathspec' entry in linkgit:gitglossary[7].
 
 -f::
 --force::
@@ -69,19 +74,6 @@ OPTIONS
 	for each file removed. This option suppresses that output.
 
 
-DISCUSSION
-----------
-
-The <file> list given to the command can be exact pathnames,
-file glob patterns, or leading directory names.  The command
-removes only the paths that are known to Git.  Giving the name of
-a file that you have not told Git about does not remove that file.
-
-File globbing matches across directory boundaries.  Thus, given
-two directories `d` and `d2`, there is a difference between
-using `git rm 'd*'` and `git rm 'd/*'`, as the former will
-also remove all of directory `d2`.
-
 REMOVING FILES THAT HAVE DISAPPEARED FROM THE FILESYSTEM
 --------------------------------------------------------
 There is no option for `git rm` to remove from the index only
-- 
gitgitgadget


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

* [PATCH v3 2/8] rm: support the --pathspec-from-file option
  2020-02-17 17:25   ` [PATCH v3 0/8] Support --pathspec-from-file in rm, stash Alexandr Miloslavskiy via GitGitGadget
  2020-02-17 17:25     ` [PATCH v3 1/8] doc: rm: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget
@ 2020-02-17 17:25     ` Alexandr Miloslavskiy via GitGitGadget
  2020-02-17 18:06       ` Alexandr Miloslavskiy
  2020-02-17 17:25     ` [PATCH v3 3/8] doc: stash: split options from description (1) Alexandr Miloslavskiy via GitGitGadget
                       ` (5 subsequent siblings)
  7 siblings, 1 reply; 41+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2020-02-17 17:25 UTC (permalink / raw)
  To: git
  Cc: Paul-Sebastian Ungureanu, Alexandr Miloslavskiy, Alexandr Miloslavskiy

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

Decisions taken for simplicity:
1) It is not allowed to pass pathspec in both args and file.

Adjustments were needed for `if (!argc)` block:

This code actually means "pathspec is not present". Previously, pathspec
could only come from commandline arguments, so testing for `argc` was a
valid way of testing for the presence of pathspec. But this is no longer
true with `--pathspec-from-file`.

During the entire `--pathspec-from-file` story, I tried to keep its
behavior very close to giving pathspec on commandline, so that switching
from one to another doesn't involve any surprises.

However, throwing usage at user in the case of empty
`--pathspec-from-file` would puzzle because there's nothing wrong with
"usage" (that is, argc/argv array).

On the other hand, throwing usage in the old case also feels bad to me.
While it's less of a puzzle, I (as user) never liked the experience of
comparing my commandline to "usage", trying to spot a difference. Since
it's already known what the error is, it feels a lot better to give that
specific error to user.

Judging from [1] it doesn't seem that showing usage in this case was
important (the patch was to avoid segfault), and it doesn't fit into how
other commands react to empty pathspec (see for example `git add` with a
custom message).

Therefore, I decided to show new error text in both cases. In order to
continue testing for error early, I moved `parse_pathspec()` higher. Now
it happens before `read_cache()` / `hold_locked_index()` /
`setup_work_tree()`, which shouldn't cause any issues.

[1] Commit 7612a1ef ("git-rm: honor -n flag" 2006-06-09)

Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 Documentation/git-rm.txt    | 17 +++++++-
 builtin/rm.c                | 28 ++++++++++---
 t/t3601-rm-pathspec-file.sh | 79 +++++++++++++++++++++++++++++++++++++
 3 files changed, 117 insertions(+), 7 deletions(-)
 create mode 100755 t/t3601-rm-pathspec-file.sh

diff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt
index e02a08e5efd..ab750367fde 100644
--- a/Documentation/git-rm.txt
+++ b/Documentation/git-rm.txt
@@ -8,7 +8,9 @@ git-rm - Remove files from the working tree and from the index
 SYNOPSIS
 --------
 [verse]
-'git rm' [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] <pathspec>...
+'git rm' [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch]
+	  [--quiet] [--pathspec-from-file=<file> [--pathspec-file-nul]]
+	  [--] [<pathspec>...]
 
 DESCRIPTION
 -----------
@@ -73,6 +75,19 @@ For more details, see the 'pathspec' entry in linkgit:gitglossary[7].
 	`git rm` normally outputs one line (in the form of an `rm` command)
 	for each file removed. This option suppresses that output.
 
+--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).
+
 
 REMOVING FILES THAT HAVE DISAPPEARED FROM THE FILESYSTEM
 --------------------------------------------------------
diff --git a/builtin/rm.c b/builtin/rm.c
index 19ce95a901b..4858631e0f0 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -235,7 +235,8 @@ static int check_local_mod(struct object_id *head, int index_only)
 }
 
 static int show_only = 0, force = 0, index_only = 0, recursive = 0, quiet = 0;
-static int ignore_unmatch = 0;
+static int ignore_unmatch = 0, pathspec_file_nul;
+static char *pathspec_from_file;
 
 static struct option builtin_rm_options[] = {
 	OPT__DRY_RUN(&show_only, N_("dry run")),
@@ -245,6 +246,8 @@ static struct option builtin_rm_options[] = {
 	OPT_BOOL('r', NULL,             &recursive,  N_("allow recursive removal")),
 	OPT_BOOL( 0 , "ignore-unmatch", &ignore_unmatch,
 				N_("exit with a zero status even if nothing matched")),
+	OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
+	OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
 	OPT_END(),
 };
 
@@ -259,8 +262,24 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
 
 	argc = parse_options(argc, argv, prefix, builtin_rm_options,
 			     builtin_rm_usage, 0);
-	if (!argc)
-		usage_with_options(builtin_rm_usage, builtin_rm_options);
+
+	parse_pathspec(&pathspec, 0,
+		       PATHSPEC_PREFER_CWD,
+		       prefix, argv);
+
+	if (pathspec_from_file) {
+		if (pathspec.nr)
+			die(_("--pathspec-from-file is incompatible with pathspec arguments"));
+
+		parse_pathspec_file(&pathspec, 0,
+				    PATHSPEC_PREFER_CWD,
+				    prefix, pathspec_from_file, pathspec_file_nul);
+	} else if (pathspec_file_nul) {
+		die(_("--pathspec-file-nul requires --pathspec-from-file"));
+	}
+
+	if (!pathspec.nr)
+		die(_("No pathspec was given. Which files should I remove?"));
 
 	if (!index_only)
 		setup_work_tree();
@@ -270,9 +289,6 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
 	if (read_cache() < 0)
 		die(_("index file corrupt"));
 
-	parse_pathspec(&pathspec, 0,
-		       PATHSPEC_PREFER_CWD,
-		       prefix, argv);
 	refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &pathspec, NULL, NULL);
 
 	seen = xcalloc(pathspec.nr, 1);
diff --git a/t/t3601-rm-pathspec-file.sh b/t/t3601-rm-pathspec-file.sh
new file mode 100755
index 00000000000..7de21f8bcff
--- /dev/null
+++ b/t/t3601-rm-pathspec-file.sh
@@ -0,0 +1,79 @@
+#!/bin/sh
+
+test_description='rm --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 fileA.t fileB.t fileC.t fileD.t &&
+	git commit -m "files" &&
+
+	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 'simplest' '
+	restore_checkpoint &&
+
+	cat >expect <<-\EOF &&
+	D  fileA.t
+	EOF
+
+	echo fileA.t | git rm --pathspec-from-file=- &&
+	verify_expect
+'
+
+test_expect_success '--pathspec-file-nul' '
+	restore_checkpoint &&
+
+	cat >expect <<-\EOF &&
+	D  fileA.t
+	D  fileB.t
+	EOF
+
+	printf "fileA.t\0fileB.t\0" | git rm --pathspec-from-file=- --pathspec-file-nul &&
+	verify_expect
+'
+
+test_expect_success 'only touches what was listed' '
+	restore_checkpoint &&
+
+	cat >expect <<-\EOF &&
+	D  fileB.t
+	D  fileC.t
+	EOF
+
+	printf "fileB.t\nfileC.t\n" | git rm --pathspec-from-file=- &&
+	verify_expect
+'
+
+test_expect_success 'error conditions' '
+	restore_checkpoint &&
+	echo fileA.t >list &&
+
+	test_must_fail git rm --pathspec-from-file=list -- fileA.t 2>err &&
+	test_i18ngrep -e "--pathspec-from-file is incompatible with pathspec arguments" err &&
+
+	test_must_fail git rm --pathspec-file-nul 2>err &&
+	test_i18ngrep -e "--pathspec-file-nul requires --pathspec-from-file" err &&
+
+	>empty_list &&
+	test_must_fail git rm --pathspec-from-file=empty_list 2>err &&
+	test_i18ngrep -e "No pathspec was given. Which files should I remove?" err
+'
+
+test_done
-- 
gitgitgadget


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

* [PATCH v3 3/8] doc: stash: split options from description (1)
  2020-02-17 17:25   ` [PATCH v3 0/8] Support --pathspec-from-file in rm, stash Alexandr Miloslavskiy via GitGitGadget
  2020-02-17 17:25     ` [PATCH v3 1/8] doc: rm: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget
  2020-02-17 17:25     ` [PATCH v3 2/8] rm: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
@ 2020-02-17 17:25     ` Alexandr Miloslavskiy via GitGitGadget
  2020-02-17 17:25     ` [PATCH v3 4/8] doc: stash: split options from description (2) Alexandr Miloslavskiy via GitGitGadget
                       ` (4 subsequent siblings)
  7 siblings, 0 replies; 41+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2020-02-17 17:25 UTC (permalink / raw)
  To: git
  Cc: Paul-Sebastian Ungureanu, Alexandr Miloslavskiy, Alexandr Miloslavskiy

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

This patch moves blocks of text as-is to make it easier to review the
next patch.

Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 Documentation/git-stash.txt | 68 +++++++++++++++++++------------------
 1 file changed, 35 insertions(+), 33 deletions(-)

diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
index 53e1a1205d3..2dedc219974 100644
--- a/Documentation/git-stash.txt
+++ b/Documentation/git-stash.txt
@@ -58,31 +58,6 @@ non-option arguments are not allowed to prevent a misspelled
 subcommand from making an unwanted stash entry.  The two exceptions to this
 are `stash -p` which acts as alias for `stash push -p` and pathspecs,
 which are allowed after a double hyphen `--` for disambiguation.
-+
-When pathspec is given to 'git stash push', the new stash entry records the
-modified states only for the files that match the pathspec.  The index
-entries and working tree files are then rolled back to the state in
-HEAD only for these files, too, leaving files that do not match the
-pathspec intact.
-+
-If the `--keep-index` option is used, all changes already added to the
-index are left intact.
-+
-If the `--include-untracked` option is used, all untracked files are also
-stashed and then cleaned up with `git clean`, leaving the working directory
-in a very clean state. If the `--all` option is used instead then the
-ignored files are stashed and cleaned in addition to the untracked files.
-+
-With `--patch`, you can interactively select hunks from the diff
-between HEAD and the working tree to be stashed.  The stash entry is
-constructed such that its index state is the same as the index state
-of your repository, and its worktree contains only the changes you
-selected interactively.  The selected changes are then rolled back
-from your worktree. See the ``Interactive Mode'' section of
-linkgit:git-add[1] to learn how to operate the `--patch` mode.
-+
-The `--patch` option implies `--keep-index`.  You can use
-`--no-keep-index` to override this.
 
 save [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [<message>]::
 
@@ -128,14 +103,6 @@ pop [--index] [-q|--quiet] [<stash>]::
 Applying the state can fail with conflicts; in this case, it is not
 removed from the stash list. You need to resolve the conflicts by hand
 and call `git stash drop` manually afterwards.
-+
-If the `--index` option is used, then tries to reinstate not only the working
-tree's changes, but also the index's ones. However, this can fail, when you
-have conflicts (which are stored in the index, where you therefore can no
-longer apply the changes as they were originally).
-+
-When no `<stash>` is given, `stash@{0}` is assumed, otherwise `<stash>` must
-be a reference of the form `stash@{<revision>}`.
 
 apply [--index] [-q|--quiet] [<stash>]::
 
@@ -185,6 +152,41 @@ store::
 	reflog.  This is intended to be useful for scripts.  It is
 	probably not the command you want to use; see "push" above.
 
+If the `--all` option is used instead then the
+ignored files are stashed and cleaned in addition to the untracked files.
+
+If the `--include-untracked` option is used, all untracked files are also
+stashed and then cleaned up with `git clean`, leaving the working directory
+in a very clean state.
+
+If the `--index` option is used, then tries to reinstate not only the working
+tree's changes, but also the index's ones. However, this can fail, when you
+have conflicts (which are stored in the index, where you therefore can no
+longer apply the changes as they were originally).
+
+If the `--keep-index` option is used, all changes already added to the
+index are left intact.
+
+With `--patch`, you can interactively select hunks from the diff
+between HEAD and the working tree to be stashed.  The stash entry is
+constructed such that its index state is the same as the index state
+of your repository, and its worktree contains only the changes you
+selected interactively.  The selected changes are then rolled back
+from your worktree. See the ``Interactive Mode'' section of
+linkgit:git-add[1] to learn how to operate the `--patch` mode.
++
+The `--patch` option implies `--keep-index`.  You can use 
+`--no-keep-index` to override this.
+
+When pathspec is given to 'git stash push', the new stash entry records the
+modified states only for the files that match the pathspec.  The index
+entries and working tree files are then rolled back to the state in
+HEAD only for these files, too, leaving files that do not match the
+pathspec intact.
+
+When no `<stash>` is given, `stash@{0}` is assumed, otherwise `<stash>` must
+be a reference of the form `stash@{<revision>}`.
+
 DISCUSSION
 ----------
 
-- 
gitgitgadget


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

* [PATCH v3 4/8] doc: stash: split options from description (2)
  2020-02-17 17:25   ` [PATCH v3 0/8] Support --pathspec-from-file in rm, stash Alexandr Miloslavskiy via GitGitGadget
                       ` (2 preceding siblings ...)
  2020-02-17 17:25     ` [PATCH v3 3/8] doc: stash: split options from description (1) Alexandr Miloslavskiy via GitGitGadget
@ 2020-02-17 17:25     ` Alexandr Miloslavskiy via GitGitGadget
  2020-02-17 17:25     ` [PATCH v3 5/8] doc: stash: document more options Alexandr Miloslavskiy via GitGitGadget
                       ` (3 subsequent siblings)
  7 siblings, 0 replies; 41+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2020-02-17 17:25 UTC (permalink / raw)
  To: git
  Cc: Paul-Sebastian Ungureanu, Alexandr Miloslavskiy, Alexandr Miloslavskiy

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

Together with the previous patch, this brings docs for `git stash` to
the common layout used for most other commands (see for example docs
for `git add`, `git commit`, `git checkout`, `git reset`) where all
options are documented in a separate list.

After some thinking and having a look at docs for `git svn` and
`git `submodule`, I have arrived at following conclusions:
  * Options should be described in a list rather then text to
    facilitate lookup for user.
  * Single list is better then multiple lists because it avoids
    copy&pasting descriptions between subcommands (or, without
    copy&pasting, user will have to look up missing options in other
    subcommands).
  * As a consequence, commands section should only give brief info and
    list possible options. Since options have good enough names, user
	will only need to look up the "interesting" options.
  * Every option should list which subcommands support it.

I have decided to use alphabetical sorting in the list of options to
facilitate lookup for user.

There is some text editing done to make old descriptions better fit
into the list-style format.

Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 Documentation/git-stash.txt | 92 +++++++++++++++++++++++--------------
 1 file changed, 57 insertions(+), 35 deletions(-)

diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
index 2dedc219974..c1c16623cbc 100644
--- a/Documentation/git-stash.txt
+++ b/Documentation/git-stash.txt
@@ -43,8 +43,8 @@ created stash, `stash@{1}` is the one before it, `stash@{2.hours.ago}`
 is also possible). Stashes may also be referenced by specifying just the
 stash index (e.g. the integer `n` is equivalent to `stash@{n}`).
 
-OPTIONS
--------
+COMMANDS
+--------
 
 push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--] [<pathspec>...]::
 
@@ -86,7 +86,7 @@ show [<options>] [<stash>]::
 
 	Show the changes recorded in the stash entry as a diff between the
 	stashed contents and the commit back when the stash entry was first
-	created. When no `<stash>` is given, it shows the latest one.
+	created.
 	By default, the command shows the diffstat, but it will accept any
 	format known to 'git diff' (e.g., `git stash show -p stash@{1}`
 	to view the second most recent entry in patch form).
@@ -116,8 +116,7 @@ branch <branchname> [<stash>]::
 	the commit at which the `<stash>` was originally created, applies the
 	changes recorded in `<stash>` to the new working tree and index.
 	If that succeeds, and `<stash>` is a reference of the form
-	`stash@{<revision>}`, it then drops the `<stash>`. When no `<stash>`
-	is given, applies the latest one.
+	`stash@{<revision>}`, it then drops the `<stash>`.
 +
 This is useful if the branch on which you ran `git stash push` has
 changed enough that `git stash apply` fails due to conflicts. Since
@@ -133,9 +132,6 @@ clear::
 drop [-q|--quiet] [<stash>]::
 
 	Remove a single stash entry from the list of stash entries.
-	When no `<stash>` is given, it removes the latest one.
-	i.e. `stash@{0}`, otherwise `<stash>` must be a valid stash
-	log reference of the form `stash@{<revision>}`.
 
 create::
 
@@ -152,40 +148,66 @@ store::
 	reflog.  This is intended to be useful for scripts.  It is
 	probably not the command you want to use; see "push" above.
 
-If the `--all` option is used instead then the
-ignored files are stashed and cleaned in addition to the untracked files.
-
-If the `--include-untracked` option is used, all untracked files are also
-stashed and then cleaned up with `git clean`, leaving the working directory
-in a very clean state.
+OPTIONS
+-------
+-a::
+--all::
+	This option is only valid for `push` and `save` commands.
++
+All ignored and untracked files are also stashed and then cleaned
+up with `git clean`.
 
-If the `--index` option is used, then tries to reinstate not only the working
-tree's changes, but also the index's ones. However, this can fail, when you
-have conflicts (which are stored in the index, where you therefore can no
-longer apply the changes as they were originally).
+-u::
+--include-untracked::
+	This option is only valid for `push` and `save` commands.
++
+All untracked files are also stashed and then cleaned up with
+`git clean`.
 
-If the `--keep-index` option is used, all changes already added to the
-index are left intact.
+--index::
+	This option is only valid for `pop` and `apply` commands.
++
+Tries to reinstate not only the working tree's changes, but also
+the index's ones. However, this can fail, when you have conflicts
+(which are stored in the index, where you therefore can no longer
+apply the changes as they were originally).
+
+-k::
+--keep-index::
+--no-keep-index::
+	This option is only valid for `push` and `save` commands.
++
+All changes already added to the index are left intact.
 
-With `--patch`, you can interactively select hunks from the diff
-between HEAD and the working tree to be stashed.  The stash entry is
-constructed such that its index state is the same as the index state
-of your repository, and its worktree contains only the changes you
-selected interactively.  The selected changes are then rolled back
-from your worktree. See the ``Interactive Mode'' section of
-linkgit:git-add[1] to learn how to operate the `--patch` mode.
+-p::
+--patch::
+	This option is only valid for `push` and `save` commands.
++
+Interactively select hunks from the diff between HEAD and the
+working tree to be stashed.  The stash entry is constructed such
+that its index state is the same as the index state of your
+repository, and its worktree contains only the changes you selected
+interactively.  The selected changes are then rolled back from your
+worktree. See the ``Interactive Mode'' section of linkgit:git-add[1]
+to learn how to operate the `--patch` mode.
 +
 The `--patch` option implies `--keep-index`.  You can use 
 `--no-keep-index` to override this.
 
-When pathspec is given to 'git stash push', the new stash entry records the
-modified states only for the files that match the pathspec.  The index
-entries and working tree files are then rolled back to the state in
-HEAD only for these files, too, leaving files that do not match the
-pathspec intact.
-
-When no `<stash>` is given, `stash@{0}` is assumed, otherwise `<stash>` must
-be a reference of the form `stash@{<revision>}`.
+<pathspec>...::
+	This option is only valid for `push` command.
++
+The new stash entry records the modified states only for the files
+that match the pathspec.  The index entries and working tree files
+are then rolled back to the state in HEAD only for these files,
+too, leaving files that do not match the pathspec intact.
+
+<stash>::
+	This option is only valid for `apply`, `branch`, `drop`, `pop`,
+	`show` commands.
++
+A reference of the form `stash@{<revision>}`. When no `<stash>` is
+given, the latest stash is assumed (that is, `stash@{0}`).
 
 DISCUSSION
 ----------
-- 
gitgitgadget


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

* [PATCH v3 5/8] doc: stash: document more options
  2020-02-17 17:25   ` [PATCH v3 0/8] Support --pathspec-from-file in rm, stash Alexandr Miloslavskiy via GitGitGadget
                       ` (3 preceding siblings ...)
  2020-02-17 17:25     ` [PATCH v3 4/8] doc: stash: split options from description (2) Alexandr Miloslavskiy via GitGitGadget
@ 2020-02-17 17:25     ` Alexandr Miloslavskiy via GitGitGadget
  2020-02-17 17:25     ` [PATCH v3 6/8] doc: stash: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget
                       ` (2 subsequent siblings)
  7 siblings, 0 replies; 41+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2020-02-17 17:25 UTC (permalink / raw)
  To: git
  Cc: Paul-Sebastian Ungureanu, Alexandr Miloslavskiy, Alexandr Miloslavskiy

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

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

diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
index c1c16623cbc..6dc0a5b0eef 100644
--- a/Documentation/git-stash.txt
+++ b/Documentation/git-stash.txt
@@ -194,6 +194,18 @@ to learn how to operate the `--patch` mode.
 The `--patch` option implies `--keep-index`.  You can use 
 `--no-keep-index` to override this.
 
+-q::
+--quiet::
+	This option is only valid for `apply`, `drop`, `pop`, `push`,
+	`save`, `store` commands.
++
+Quiet, suppress feedback messages.
+
+\--::
+	This option is only valid for `push` command.
++
+Separates pathspec from options for disambiguation purposes.
+
 <pathspec>...::
 	This option is only valid for `push` command.
 +
-- 
gitgitgadget


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

* [PATCH v3 6/8] doc: stash: synchronize <pathspec> description
  2020-02-17 17:25   ` [PATCH v3 0/8] Support --pathspec-from-file in rm, stash Alexandr Miloslavskiy via GitGitGadget
                       ` (4 preceding siblings ...)
  2020-02-17 17:25     ` [PATCH v3 5/8] doc: stash: document more options Alexandr Miloslavskiy via GitGitGadget
@ 2020-02-17 17:25     ` Alexandr Miloslavskiy via GitGitGadget
  2020-02-17 17:25     ` [PATCH v3 7/8] stash: eliminate crude option parsing Alexandr Miloslavskiy via GitGitGadget
  2020-02-17 17:25     ` [PATCH v3 8/8] stash push: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
  7 siblings, 0 replies; 41+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2020-02-17 17:25 UTC (permalink / raw)
  To: git
  Cc: Paul-Sebastian Ungureanu, Alexandr Miloslavskiy, Alexandr Miloslavskiy

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

This patch continues the effort that is already applied to
`git commit`, `git reset`, `git checkout` etc.

1) Added reference to 'linkgit:gitglossary[7]'.
2) Fixed mentions of incorrectly plural "pathspecs".

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

diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
index 6dc0a5b0eef..52e64985bda 100644
--- a/Documentation/git-stash.txt
+++ b/Documentation/git-stash.txt
@@ -56,13 +56,13 @@ push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q
 For quickly making a snapshot, you can omit "push".  In this mode,
 non-option arguments are not allowed to prevent a misspelled
 subcommand from making an unwanted stash entry.  The two exceptions to this
-are `stash -p` which acts as alias for `stash push -p` and pathspecs,
+are `stash -p` which acts as alias for `stash push -p` and pathspec elements,
 which are allowed after a double hyphen `--` for disambiguation.
 
 save [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [<message>]::
 
 	This option is deprecated in favour of 'git stash push'.  It
-	differs from "stash push" in that it cannot take pathspecs.
+	differs from "stash push" in that it cannot take pathspec.
 	Instead, all non-option arguments are concatenated to form the stash
 	message.
 
@@ -213,6 +213,8 @@ The new stash entry records the modified states only for the files
 that match the pathspec.  The index entries and working tree files
 are then rolled back to the state in HEAD only for these files,
 too, leaving files that do not match the pathspec intact.
++
+For more details, see the 'pathspec' entry in linkgit:gitglossary[7].
 
 <stash>::
 	This option is only valid for `apply`, `branch`, `drop`, `pop`,
-- 
gitgitgadget


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

* [PATCH v3 7/8] stash: eliminate crude option parsing
  2020-02-17 17:25   ` [PATCH v3 0/8] Support --pathspec-from-file in rm, stash Alexandr Miloslavskiy via GitGitGadget
                       ` (5 preceding siblings ...)
  2020-02-17 17:25     ` [PATCH v3 6/8] doc: stash: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget
@ 2020-02-17 17:25     ` Alexandr Miloslavskiy via GitGitGadget
  2020-02-17 17:25     ` [PATCH v3 8/8] stash push: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
  7 siblings, 0 replies; 41+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2020-02-17 17:25 UTC (permalink / raw)
  To: git
  Cc: Paul-Sebastian Ungureanu, Alexandr Miloslavskiy, Alexandr Miloslavskiy

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

Eliminate crude option parsing and rely on real parsing instead, because
1) Crude parsing is crude, for example it's not capable of
   handling things like `git stash -m Message`
2) Adding options in two places is inconvenient and prone to bugs

As a side result, the case of `git stash -m Message` gets fixed.
Also give a good error message instead of just throwing usage at user.

----

Some review of what's been happening to this code:

Before [1], `git-stash.sh` only verified that all args begin with `-` :

	# The default command is "push" if nothing but options are given
	seen_non_option=
	for opt
	do
		case "$opt" in
		--) break ;;
		-*) ;;
		*) seen_non_option=t; break ;;
		esac
	done

Later, [1] introduced the duplicate code I'm now removing, also making
the previous test more strict by white-listing options.

----

[1] Commit 40af1468 ("stash: convert `stash--helper.c` into `stash.c`" 2019-02-26)

Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 builtin/stash.c  | 59 +++++++++++++++++-------------------------------
 t/t3903-stash.sh |  5 ++++
 2 files changed, 26 insertions(+), 38 deletions(-)

diff --git a/builtin/stash.c b/builtin/stash.c
index 879fc5f3683..ed84ff2e168 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -1451,8 +1451,10 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
 	return ret;
 }
 
-static int push_stash(int argc, const char **argv, const char *prefix)
+static int push_stash(int argc, const char **argv, const char *prefix,
+		      int push_assumed)
 {
+	int force_assume = 0;
 	int keep_index = -1;
 	int patch_mode = 0;
 	int include_untracked = 0;
@@ -1474,10 +1476,22 @@ static int push_stash(int argc, const char **argv, const char *prefix)
 		OPT_END()
 	};
 
-	if (argc)
+	if (argc) {
+		force_assume = !strcmp(argv[0], "-p");
 		argc = parse_options(argc, argv, prefix, options,
 				     git_stash_push_usage,
-				     0);
+				     PARSE_OPT_KEEP_DASHDASH);
+	}
+
+	if (argc) {
+		if (!strcmp(argv[0], "--")) {
+			argc--;
+			argv++;
+		} else if (push_assumed && !force_assume) {
+			die("subcommand wasn't specified; 'push' can't be assumed due to unexpected token '%s'",
+			    argv[0]);
+		}
+	}
 
 	parse_pathspec(&ps, 0, PATHSPEC_PREFER_FULL | PATHSPEC_PREFIX_ORIGIN,
 		       prefix, argv);
@@ -1550,7 +1564,6 @@ static int use_builtin_stash(void)
 
 int cmd_stash(int argc, const char **argv, const char *prefix)
 {
-	int i = -1;
 	pid_t pid = getpid();
 	const char *index_file;
 	struct argv_array args = ARGV_ARRAY_INIT;
@@ -1583,7 +1596,7 @@ int cmd_stash(int argc, const char **argv, const char *prefix)
 		    (uintmax_t)pid);
 
 	if (!argc)
-		return !!push_stash(0, NULL, prefix);
+		return !!push_stash(0, NULL, prefix, 0);
 	else if (!strcmp(argv[0], "apply"))
 		return !!apply_stash(argc, argv, prefix);
 	else if (!strcmp(argv[0], "clear"))
@@ -1603,45 +1616,15 @@ int cmd_stash(int argc, const char **argv, const char *prefix)
 	else if (!strcmp(argv[0], "create"))
 		return !!create_stash(argc, argv, prefix);
 	else if (!strcmp(argv[0], "push"))
-		return !!push_stash(argc, argv, prefix);
+		return !!push_stash(argc, argv, prefix, 0);
 	else if (!strcmp(argv[0], "save"))
 		return !!save_stash(argc, argv, prefix);
 	else if (*argv[0] != '-')
 		usage_msg_opt(xstrfmt(_("unknown subcommand: %s"), argv[0]),
 			      git_stash_usage, options);
 
-	if (strcmp(argv[0], "-p")) {
-		while (++i < argc && strcmp(argv[i], "--")) {
-			/*
-			 * `akpqu` is a string which contains all short options,
-			 * except `-m` which is verified separately.
-			 */
-			if ((strlen(argv[i]) == 2) && *argv[i] == '-' &&
-			    strchr("akpqu", argv[i][1]))
-				continue;
-
-			if (!strcmp(argv[i], "--all") ||
-			    !strcmp(argv[i], "--keep-index") ||
-			    !strcmp(argv[i], "--no-keep-index") ||
-			    !strcmp(argv[i], "--patch") ||
-			    !strcmp(argv[i], "--quiet") ||
-			    !strcmp(argv[i], "--include-untracked"))
-				continue;
-
-			/*
-			 * `-m` and `--message=` are verified separately because
-			 * they need to be immediately followed by a string
-			 * (i.e.`-m"foobar"` or `--message="foobar"`).
-			 */
-			if (starts_with(argv[i], "-m") ||
-			    starts_with(argv[i], "--message="))
-				continue;
-
-			usage_with_options(git_stash_usage, options);
-		}
-	}
-
+	/* Assume 'stash push' */
 	argv_array_push(&args, "push");
 	argv_array_pushv(&args, argv);
-	return !!push_stash(args.argc, args.argv, prefix);
+	return !!push_stash(args.argc, args.argv, prefix, 1);
 }
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index ea56e85e70d..3ad23e2502b 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -285,6 +285,11 @@ test_expect_success 'stash --no-keep-index' '
 	test bar,bar2 = $(cat file),$(cat file2)
 '
 
+test_expect_success 'dont assume push with non-option args' '
+	test_must_fail git stash -q drop 2>err &&
+	test_i18ngrep -e "subcommand wasn'\''t specified; '\''push'\'' can'\''t be assumed due to unexpected token '\''drop'\''" err
+'
+
 test_expect_success 'stash --invalid-option' '
 	echo bar5 >file &&
 	echo bar6 >file2 &&
-- 
gitgitgadget


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

* [PATCH v3 8/8] stash push: support the --pathspec-from-file option
  2020-02-17 17:25   ` [PATCH v3 0/8] Support --pathspec-from-file in rm, stash Alexandr Miloslavskiy via GitGitGadget
                       ` (6 preceding siblings ...)
  2020-02-17 17:25     ` [PATCH v3 7/8] stash: eliminate crude option parsing Alexandr Miloslavskiy via GitGitGadget
@ 2020-02-17 17:25     ` Alexandr Miloslavskiy via GitGitGadget
  7 siblings, 0 replies; 41+ messages in thread
From: Alexandr Miloslavskiy via GitGitGadget @ 2020-02-17 17:25 UTC (permalink / raw)
  To: git
  Cc: Paul-Sebastian Ungureanu, Alexandr Miloslavskiy, 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 `-`. Such use case is not
   really expected.
2) It is not allowed to pass pathspec in both args and file.

Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>
---
 Documentation/git-stash.txt    |  20 ++++++-
 builtin/stash.c                |  20 +++++++
 t/t3909-stash-pathspec-file.sh | 100 +++++++++++++++++++++++++++++++++
 3 files changed, 139 insertions(+), 1 deletion(-)
 create mode 100755 t/t3909-stash-pathspec-file.sh

diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt
index 52e64985bda..79949f86175 100644
--- a/Documentation/git-stash.txt
+++ b/Documentation/git-stash.txt
@@ -15,6 +15,7 @@ SYNOPSIS
 'git stash' branch <branchname> [<stash>]
 'git stash' [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]
 	     [-u|--include-untracked] [-a|--all] [-m|--message <message>]
+	     [--pathspec-from-file=<file> [--pathspec-file-nul]]
 	     [--] [<pathspec>...]]
 'git stash' clear
 'git stash' create [<message>]
@@ -46,7 +47,7 @@ stash index (e.g. the integer `n` is equivalent to `stash@{n}`).
 COMMANDS
 --------
 
-push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--] [<pathspec>...]::
+push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--pathspec-from-file=<file> [--pathspec-file-nul]] [--] [<pathspec>...]::
 
 	Save your local modifications to a new 'stash entry' and roll them
 	back to HEAD (in the working tree and in the index).
@@ -194,6 +195,23 @@ to learn how to operate the `--patch` mode.
 The `--patch` option implies `--keep-index`.  You can use 
 `--no-keep-index` to override this.
 
+--pathspec-from-file=<file>::
+	This option is only valid for `push` command.
++
+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::
+	This option is only valid for `push` command.
++
+Only meaningful with `--pathspec-from-file`. Pathspec elements are
+separated with NUL character and all other characters are taken
+literally (including newlines and quotes).
+
 -q::
 --quiet::
 	This option is only valid for `apply`, `drop`, `pop`, `push`,
diff --git a/builtin/stash.c b/builtin/stash.c
index ed84ff2e168..78af6ce5643 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -27,6 +27,7 @@ static const char * const git_stash_usage[] = {
 	N_("git stash clear"),
 	N_("git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
 	   "          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
+	   "          [--pathspec-from-file=<file> [--pathspec-file-nul]]\n"
 	   "          [--] [<pathspec>...]]"),
 	N_("git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
 	   "          [-u|--include-untracked] [-a|--all] [<message>]"),
@@ -1459,7 +1460,9 @@ static int push_stash(int argc, const char **argv, const char *prefix,
 	int patch_mode = 0;
 	int include_untracked = 0;
 	int quiet = 0;
+	int pathspec_file_nul = 0;
 	const char *stash_msg = NULL;
+	const char *pathspec_from_file = NULL;
 	struct pathspec ps;
 	struct option options[] = {
 		OPT_BOOL('k', "keep-index", &keep_index,
@@ -1473,6 +1476,8 @@ static int push_stash(int argc, const char **argv, const char *prefix,
 			    N_("include ignore files"), 2),
 		OPT_STRING('m', "message", &stash_msg, N_("message"),
 			   N_("stash message")),
+		OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
+		OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
 		OPT_END()
 	};
 
@@ -1495,6 +1500,21 @@ static int push_stash(int argc, const char **argv, const char *prefix,
 
 	parse_pathspec(&ps, 0, PATHSPEC_PREFER_FULL | PATHSPEC_PREFIX_ORIGIN,
 		       prefix, argv);
+
+	if (pathspec_from_file) {
+		if (patch_mode)
+			die(_("--pathspec-from-file is incompatible with --patch"));
+
+		if (ps.nr)
+			die(_("--pathspec-from-file is incompatible with pathspec arguments"));
+
+		parse_pathspec_file(&ps, 0,
+				    PATHSPEC_PREFER_FULL | PATHSPEC_PREFIX_ORIGIN,
+				    prefix, pathspec_from_file, pathspec_file_nul);
+	} else if (pathspec_file_nul) {
+		die(_("--pathspec-file-nul requires --pathspec-from-file"));
+	}
+
 	return do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode,
 			     include_untracked);
 }
diff --git a/t/t3909-stash-pathspec-file.sh b/t/t3909-stash-pathspec-file.sh
new file mode 100755
index 00000000000..55e050cfd4d
--- /dev/null
+++ b/t/t3909-stash-pathspec-file.sh
@@ -0,0 +1,100 @@
+#!/bin/sh
+
+test_description='stash --pathspec-from-file'
+
+. ./test-lib.sh
+
+test_tick
+
+test_expect_success setup '
+	>fileA.t &&
+	>fileB.t &&
+	>fileC.t &&
+	>fileD.t &&
+	git add fileA.t fileB.t fileC.t fileD.t &&
+	git commit -m "Files" &&
+
+	git tag checkpoint
+'
+
+restore_checkpoint () {
+	git reset --hard checkpoint
+}
+
+verify_expect () {
+	git stash show --name-status >actual &&
+	test_cmp expect actual
+}
+
+test_expect_success 'simplest' '
+	restore_checkpoint &&
+
+	# More files are written to make sure that git didnt ignore
+	# --pathspec-from-file, stashing everything
+	echo A >fileA.t &&
+	echo B >fileB.t &&
+	echo C >fileC.t &&
+	echo D >fileD.t &&
+
+	cat >expect <<-\EOF &&
+	M	fileA.t
+	EOF
+
+	echo fileA.t | git stash push --pathspec-from-file=- &&
+	verify_expect
+'
+
+test_expect_success '--pathspec-file-nul' '
+	restore_checkpoint &&
+
+	# More files are written to make sure that git didnt ignore
+	# --pathspec-from-file, stashing everything
+	echo A >fileA.t &&
+	echo B >fileB.t &&
+	echo C >fileC.t &&
+	echo D >fileD.t &&
+
+	cat >expect <<-\EOF &&
+	M	fileA.t
+	M	fileB.t
+	EOF
+
+	printf "fileA.t\0fileB.t\0" | git stash push --pathspec-from-file=- --pathspec-file-nul &&
+	verify_expect
+'
+
+test_expect_success 'only touches what was listed' '
+	restore_checkpoint &&
+
+	# More files are written to make sure that git didnt ignore
+	# --pathspec-from-file, stashing everything
+	echo A >fileA.t &&
+	echo B >fileB.t &&
+	echo C >fileC.t &&
+	echo D >fileD.t &&
+
+	cat >expect <<-\EOF &&
+	M	fileB.t
+	M	fileC.t
+	EOF
+
+	printf "fileB.t\nfileC.t\n" | git stash push --pathspec-from-file=- &&
+	verify_expect
+'
+
+test_expect_success 'error conditions' '
+	restore_checkpoint &&
+	echo A >fileA.t &&
+	echo fileA.t >list &&
+
+	test_must_fail git stash push --pathspec-from-file=list --patch 2>err &&
+	test_i18ngrep -e "--pathspec-from-file is incompatible with --patch" err &&
+
+	test_must_fail git stash push --pathspec-from-file=list -- fileA.t 2>err &&
+	test_i18ngrep -e "--pathspec-from-file is incompatible with pathspec arguments" err &&
+
+	test_must_fail git stash push --pathspec-file-nul 2>err &&
+	test_i18ngrep -e "--pathspec-file-nul requires --pathspec-from-file" err
+'
+
+test_done
-- 
gitgitgadget

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

* Re: [PATCH 2/8] rm: support the --pathspec-from-file option
  2020-02-10 18:48       ` Junio C Hamano
@ 2020-02-17 17:25         ` Alexandr Miloslavskiy
  0 siblings, 0 replies; 41+ messages in thread
From: Alexandr Miloslavskiy @ 2020-02-17 17:25 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Alexandr Miloslavskiy via GitGitGadget, git, Paul-Sebastian Ungureanu

On 10.02.2020 19:48, Junio C Hamano wrote:
> I expected that the proposed log message to explain and justify why
> a change (in behaviour, in design, etc.) is made.  "There is this
> limitation" is not a justification---"because of such and such
> reasons, there is this limitation, otherwise such and such bad
> things happen" is.

I have rewritten the commit message in V3. Is that better?

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

* Re: [PATCH v2 2/8] rm: support the --pathspec-from-file option
  2020-02-10 20:39     ` Junio C Hamano
@ 2020-02-17 17:27       ` Alexandr Miloslavskiy
  2020-02-17 17:59         ` Junio C Hamano
  0 siblings, 1 reply; 41+ messages in thread
From: Alexandr Miloslavskiy @ 2020-02-17 17:27 UTC (permalink / raw)
  To: Junio C Hamano, Alexandr Miloslavskiy via GitGitGadget
  Cc: git, Paul-Sebastian Ungureanu

On 10.02.2020 21:39, Junio C Hamano wrote:
> Trailing whitespace on this line.

Whitespaces fixed in V3; I have also activated pre-commit hook. Sorry!

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

* Re: [PATCH v2 2/8] rm: support the --pathspec-from-file option
  2020-02-17 17:27       ` Alexandr Miloslavskiy
@ 2020-02-17 17:59         ` Junio C Hamano
  2020-02-17 21:03           ` Junio C Hamano
  0 siblings, 1 reply; 41+ messages in thread
From: Junio C Hamano @ 2020-02-17 17:59 UTC (permalink / raw)
  To: Alexandr Miloslavskiy
  Cc: Alexandr Miloslavskiy via GitGitGadget, git, Paul-Sebastian Ungureanu

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

> On 10.02.2020 21:39, Junio C Hamano wrote:
>> Trailing whitespace on this line.
>
> Whitespaces fixed in V3; I have also activated pre-commit hook. Sorry!

I may not have time to read it over at least in a few days, but lack
if v3 in the title will make it cumbersome to come back to the
thread later X-<.  Thanks for a heads-up, anyway, thoguh.


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

* Re: [PATCH v3 2/8] rm: support the --pathspec-from-file option
  2020-02-17 17:25     ` [PATCH v3 2/8] rm: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
@ 2020-02-17 18:06       ` Alexandr Miloslavskiy
  0 siblings, 0 replies; 41+ messages in thread
From: Alexandr Miloslavskiy @ 2020-02-17 18:06 UTC (permalink / raw)
  To: Alexandr Miloslavskiy via GitGitGadget, git; +Cc: Paul-Sebastian Ungureanu

On 17.02.2020 18:59, Junio C Hamano wrote:
 > I may not have time to read it over at least in a few days, but lack
 > if v3 in the title will make it cumbersome to come back to the
 > thread later X-<.  Thanks for a heads-up, anyway, thoguh.

Here's the V3 in title :)

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

* Re: [PATCH v2 2/8] rm: support the --pathspec-from-file option
  2020-02-17 17:59         ` Junio C Hamano
@ 2020-02-17 21:03           ` Junio C Hamano
  0 siblings, 0 replies; 41+ messages in thread
From: Junio C Hamano @ 2020-02-17 21:03 UTC (permalink / raw)
  To: Alexandr Miloslavskiy
  Cc: Alexandr Miloslavskiy via GitGitGadget, git, Paul-Sebastian Ungureanu

Junio C Hamano <gitster@pobox.com> writes:

> I may not have time to read it over at least in a few days, but lack
> if v3 in the title will make it cumbersome to come back ...

Oops, please disregard.  I must have been looking at some wrong
thread.

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

end of thread, other threads:[~2020-02-17 21:03 UTC | newest]

Thread overview: 41+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-01-16 16:09 [PATCH 0/8] Support --pathspec-from-file in rm, stash Alexandr Miloslavskiy via GitGitGadget
2020-01-16 16:09 ` [PATCH 1/8] doc: rm: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget
2020-01-21 19:14   ` Junio C Hamano
2020-01-16 16:09 ` [PATCH 2/8] rm: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
2020-01-21 19:36   ` Junio C Hamano
2020-02-10 14:46     ` Alexandr Miloslavskiy
2020-02-10 18:48       ` Junio C Hamano
2020-02-17 17:25         ` Alexandr Miloslavskiy
2020-01-16 16:09 ` [PATCH 3/8] doc: stash: split options from description (1) Alexandr Miloslavskiy via GitGitGadget
2020-01-16 16:09 ` [PATCH 4/8] doc: stash: split options from description (2) Alexandr Miloslavskiy via GitGitGadget
2020-01-21 20:21   ` Junio C Hamano
2020-02-10 14:47     ` Alexandr Miloslavskiy
2020-01-16 16:09 ` [PATCH 5/8] doc: stash: document more options Alexandr Miloslavskiy via GitGitGadget
2020-01-21 20:29   ` Junio C Hamano
2020-01-16 16:09 ` [PATCH 6/8] doc: stash: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget
2020-01-21 20:29   ` Junio C Hamano
2020-01-16 16:09 ` [PATCH 7/8] stash: eliminate crude option parsing Alexandr Miloslavskiy via GitGitGadget
2020-01-16 16:09 ` [PATCH 8/8] stash push: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
2020-02-10 14:45 ` [PATCH v2 0/8] Support --pathspec-from-file in rm, stash Alexandr Miloslavskiy via GitGitGadget
2020-02-10 14:45   ` [PATCH v2 1/8] doc: rm: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget
2020-02-10 14:45   ` [PATCH v2 2/8] rm: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
2020-02-10 20:39     ` Junio C Hamano
2020-02-17 17:27       ` Alexandr Miloslavskiy
2020-02-17 17:59         ` Junio C Hamano
2020-02-17 21:03           ` Junio C Hamano
2020-02-10 14:45   ` [PATCH v2 3/8] doc: stash: split options from description (1) Alexandr Miloslavskiy via GitGitGadget
2020-02-10 14:45   ` [PATCH v2 4/8] doc: stash: split options from description (2) Alexandr Miloslavskiy via GitGitGadget
2020-02-10 14:45   ` [PATCH v2 5/8] doc: stash: document more options Alexandr Miloslavskiy via GitGitGadget
2020-02-10 14:45   ` [PATCH v2 6/8] doc: stash: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget
2020-02-10 14:45   ` [PATCH v2 7/8] stash: eliminate crude option parsing Alexandr Miloslavskiy via GitGitGadget
2020-02-10 14:45   ` [PATCH v2 8/8] stash push: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
2020-02-17 17:25   ` [PATCH v3 0/8] Support --pathspec-from-file in rm, stash Alexandr Miloslavskiy via GitGitGadget
2020-02-17 17:25     ` [PATCH v3 1/8] doc: rm: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget
2020-02-17 17:25     ` [PATCH v3 2/8] rm: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget
2020-02-17 18:06       ` Alexandr Miloslavskiy
2020-02-17 17:25     ` [PATCH v3 3/8] doc: stash: split options from description (1) Alexandr Miloslavskiy via GitGitGadget
2020-02-17 17:25     ` [PATCH v3 4/8] doc: stash: split options from description (2) Alexandr Miloslavskiy via GitGitGadget
2020-02-17 17:25     ` [PATCH v3 5/8] doc: stash: document more options Alexandr Miloslavskiy via GitGitGadget
2020-02-17 17:25     ` [PATCH v3 6/8] doc: stash: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget
2020-02-17 17:25     ` [PATCH v3 7/8] stash: eliminate crude option parsing Alexandr Miloslavskiy via GitGitGadget
2020-02-17 17:25     ` [PATCH v3 8/8] stash push: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget

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

This inbox may be cloned and mirrored by anyone:

	git clone --mirror http://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

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V1 git git/ http://public-inbox.org/git \
		git@vger.kernel.org
	public-inbox-index 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.io/gmane.comp.version-control.git
 note: .onion URLs require Tor: https://www.torproject.org/

code repositories for the project(s) associated with this inbox:

	https://80x24.org/mirrors/git.git

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