{"thread":{"id":"52648","subject":"[PATCH 0/8] Support --pathspec-from-file in rm, stash","startedAt":"2020-01-16T16:09:30Z","lastAt":"2020-02-17T21:03:59Z","messageCount":41,"participants":["Alexandr Miloslavskiy via GitGitGadget","Junio C Hamano","Alexandr Miloslavskiy"],"isPatch":true,"patchVersion":1,"patchTotal":8},"messages":[{"id":"389891","messageId":"pull.530.git.1579190965.gitgitgadget@gmail.com","threadId":"52648","inReplyTo":null,"subject":"[PATCH 0/8] Support --pathspec-from-file in rm, stash","fromName":"Alexandr Miloslavskiy via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-01-16T16:09:17Z","receivedAt":"2020-01-16T16:09:30Z","isPatch":true,"sender":{"key":"alexandr.miloslavskiy@syntevo.com","avatar":null},"body":"This series continues the effort to support `--pathspec-from-file`\nin various git commands. Series already in `master`: [1][2]\n\nCc'ing Paul-Sebastian Ungureanu because I touched his git stash code.\n\n[1] https://public-inbox.org/git/pull.445.git.1572895605.gitgitgadget@gmail.com/\n[2] https://lore.kernel.org/git/pull.490.git.1576161385.gitgitgadget@gmail.com/\n\nAlexandr Miloslavskiy (8):\n  doc: rm: synchronize <pathspec> description\n  rm: support the --pathspec-from-file option\n  doc: stash: split options from description (1)\n  doc: stash: split options from description (2)\n  doc: stash: document more options\n  doc: stash: synchronize <pathspec> description\n  stash: eliminate crude option parsing\n  stash push: support the --pathspec-from-file option\n\n Documentation/git-rm.txt       |  61 ++++++++++--------\n Documentation/git-stash.txt    | 111 +++++++++++++++++++++------------\n builtin/rm.c                   |  28 +++++++--\n builtin/stash.c                |  79 ++++++++++++-----------\n t/t3601-rm-pathspec-file.sh    |  79 +++++++++++++++++++++++\n t/t3903-stash.sh               |   5 ++\n t/t3909-stash-pathspec-file.sh | 100 +++++++++++++++++++++++++++++\n 7 files changed, 353 insertions(+), 110 deletions(-)\n create mode 100755 t/t3601-rm-pathspec-file.sh\n create mode 100755 t/t3909-stash-pathspec-file.sh\n\n\nbase-commit: d0654dc308b0ba76dd8ed7bbb33c8d8f7aacd783\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-530%2FSyntevoAlex%2F%230207(git)_pathspec_from_file_3-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-530/SyntevoAlex/#0207(git)_pathspec_from_file_3-v1\nPull-Request: https://github.com/gitgitgadget/git/pull/530\n-- \ngitgitgadget\n"},{"id":"389892","messageId":"23387f83912d1f5aef17a28d466db162f50fd722.1579190965.git.gitgitgadget@gmail.com","threadId":"52648","inReplyTo":"pull.530.git.1579190965.gitgitgadget@gmail.com","subject":"[PATCH 1/8] doc: rm: synchronize <pathspec> description","fromName":"Alexandr Miloslavskiy via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-01-16T16:09:18Z","receivedAt":"2020-01-16T16:09:31Z","isPatch":true,"sender":{"key":"alexandr.miloslavskiy@syntevo.com","avatar":null},"body":"From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n\nThis patch continues the effort that is already applied to\n`git commit`, `git reset`, `git checkout` etc.\n\n1) Changed outdated descriptions to mention pathspec instead.\n2) Added reference to 'linkgit:gitglossary[7]'.\n3) Removed content that merely repeated gitglossary.\n4) Merged the remainder of \"discussion\" into `<patchspec>`.\n\nSigned-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n---\n Documentation/git-rm.txt | 50 +++++++++++++++++-----------------------\n 1 file changed, 21 insertions(+), 29 deletions(-)\n\ndiff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt\nindex b5c46223c4..e02a08e5ef 100644\n--- a/Documentation/git-rm.txt\n+++ b/Documentation/git-rm.txt\n@@ -8,16 +8,16 @@ git-rm - Remove files from the working tree and from the index\n SYNOPSIS\n --------\n [verse]\n-'git rm' [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] <file>...\n+'git rm' [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] <pathspec>...\n \n DESCRIPTION\n -----------\n-Remove files from the index, or from the working tree and the index.\n-`git rm` will not remove a file from just your working directory.\n-(There is no option to remove a file only from the working tree\n-and yet keep it in the index; use `/bin/rm` if you want to do that.)\n-The files being removed have to be identical to the tip of the branch,\n-and no updates to their contents can be staged in the index,\n+Remove files matching pathspec from the index, or from the working tree\n+and the index. `git rm` will not remove a file from just your working\n+directory. (There is no option to remove a file only from the working\n+tree and yet keep it in the index; use `/bin/rm` if you want to do\n+that.) The files being removed have to be identical to the tip of the\n+branch, and no updates to their contents can be staged in the index,\n though that default behavior can be overridden with the `-f` option.\n When `--cached` is given, the staged content has to\n match either the tip of the branch or the file on disk,\n@@ -26,15 +26,20 @@ allowing the file to be removed from just the index.\n \n OPTIONS\n -------\n-<file>...::\n-\tFiles to remove.  Fileglobs (e.g. `*.c`) can be given to\n-\tremove all matching files.  If you want Git to expand\n-\tfile glob characters, you may need to shell-escape them.\n-\tA leading directory name\n-\t(e.g. `dir` to remove `dir/file1` and `dir/file2`) can be\n-\tgiven to remove all files in the directory, and recursively\n-\tall sub-directories,\n-\tbut this requires the `-r` option to be explicitly given.\n+<pathspec>...::\n+\tFiles to remove.  A leading directory name (e.g. `dir` to remove\n+\t`dir/file1` and `dir/file2`) can be given to remove all files in\n+\tthe directory, and recursively all sub-directories, but this\n+\trequires the `-r` option to be explicitly given.\n++\n+The command removes only the paths that are known to Git.\n++\n+File globbing matches across directory boundaries.  Thus, given two\n+directories `d` and `d2`, there is a difference between using\n+`git rm 'd*'` and `git rm 'd/*'`, as the former will also remove all\n+of directory `d2`.\n++\n+For more details, see the 'pathspec' entry in linkgit:gitglossary[7].\n \n -f::\n --force::\n@@ -69,19 +74,6 @@ OPTIONS\n \tfor each file removed. This option suppresses that output.\n \n \n-DISCUSSION\n-----------\n-\n-The <file> list given to the command can be exact pathnames,\n-file glob patterns, or leading directory names.  The command\n-removes only the paths that are known to Git.  Giving the name of\n-a file that you have not told Git about does not remove that file.\n-\n-File globbing matches across directory boundaries.  Thus, given\n-two directories `d` and `d2`, there is a difference between\n-using `git rm 'd*'` and `git rm 'd/*'`, as the former will\n-also remove all of directory `d2`.\n-\n REMOVING FILES THAT HAVE DISAPPEARED FROM THE FILESYSTEM\n --------------------------------------------------------\n There is no option for `git rm` to remove from the index only\n-- \ngitgitgadget\n\n"},{"id":"389893","messageId":"5611e3ae326bb7f61abf870e3b2851226b6af1d8.1579190965.git.gitgitgadget@gmail.com","threadId":"52648","inReplyTo":"pull.530.git.1579190965.gitgitgadget@gmail.com","subject":"[PATCH 2/8] rm: support the --pathspec-from-file option","fromName":"Alexandr Miloslavskiy via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-01-16T16:09:19Z","receivedAt":"2020-01-16T16:09:33Z","isPatch":true,"sender":{"key":"alexandr.miloslavskiy@syntevo.com","avatar":null},"body":"From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n\nDecisions taken for simplicity:\n1) It is not allowed to pass pathspec in both args and file.\n\n`if (!argc)` block was adapted to work with --pathspec-from-file. For\nthat, I also had to parse pathspec earlier. Now it happens before\n`read_cache()` / `hold_locked_index()` / `setup_work_tree()`, which\nsounds fine to me.\n\nIn case of empty pathspec, there is now a clear error message instead\nof showing usage. As a consequence, exit code has also changed. Judging\nfrom [1] it doesn't seem that showing usage in this case was important\n(according to commit message, it was to avoid segfault), and it doesn't\nfit into how other commands react to empty pathspec. Finally, the new\nerror message is easier to understand.\n\n[1] Commit 7612a1ef (\"git-rm: honor -n flag\" 2006-06-09)\n\nSigned-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n---\n Documentation/git-rm.txt    | 17 +++++++-\n builtin/rm.c                | 28 ++++++++++---\n t/t3601-rm-pathspec-file.sh | 79 +++++++++++++++++++++++++++++++++++++\n 3 files changed, 117 insertions(+), 7 deletions(-)\n create mode 100755 t/t3601-rm-pathspec-file.sh\n\ndiff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt\nindex e02a08e5ef..ab750367fd 100644\n--- a/Documentation/git-rm.txt\n+++ b/Documentation/git-rm.txt\n@@ -8,7 +8,9 @@ git-rm - Remove files from the working tree and from the index\n SYNOPSIS\n --------\n [verse]\n-'git rm' [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] <pathspec>...\n+'git rm' [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch]\n+\t  [--quiet] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n+\t  [--] [<pathspec>...]\n \n DESCRIPTION\n -----------\n@@ -73,6 +75,19 @@ For more details, see the 'pathspec' entry in linkgit:gitglossary[7].\n \t`git rm` normally outputs one line (in the form of an `rm` command)\n \tfor each file removed. This option suppresses that output.\n \n+--pathspec-from-file=<file>::\n+\tPathspec is passed in `<file>` instead of commandline args. If\n+\t`<file>` is exactly `-` then standard input is used. Pathspec\n+\telements are separated by LF or CR/LF. Pathspec elements can be\n+\tquoted as explained for the configuration variable `core.quotePath`\n+\t(see linkgit:git-config[1]). See also `--pathspec-file-nul` and\n+\tglobal `--literal-pathspecs`.\n+\n+--pathspec-file-nul::\n+\tOnly meaningful with `--pathspec-from-file`. Pathspec elements are\n+\tseparated with NUL character and all other characters are taken\n+\tliterally (including newlines and quotes).\n+\n \n REMOVING FILES THAT HAVE DISAPPEARED FROM THE FILESYSTEM\n --------------------------------------------------------\ndiff --git a/builtin/rm.c b/builtin/rm.c\nindex 19ce95a901..8e40795751 100644\n--- a/builtin/rm.c\n+++ b/builtin/rm.c\n@@ -235,7 +235,8 @@ static int check_local_mod(struct object_id *head, int index_only)\n }\n \n static int show_only = 0, force = 0, index_only = 0, recursive = 0, quiet = 0;\n-static int ignore_unmatch = 0;\n+static int ignore_unmatch = 0, pathspec_file_nul = 0;\n+static char *pathspec_from_file = NULL;\n \n static struct option builtin_rm_options[] = {\n \tOPT__DRY_RUN(&show_only, N_(\"dry run\")),\n@@ -245,6 +246,8 @@ static struct option builtin_rm_options[] = {\n \tOPT_BOOL('r', NULL,             &recursive,  N_(\"allow recursive removal\")),\n \tOPT_BOOL( 0 , \"ignore-unmatch\", &ignore_unmatch,\n \t\t\t\tN_(\"exit with a zero status even if nothing matched\")),\n+\tOPT_PATHSPEC_FROM_FILE(&pathspec_from_file),\n+\tOPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),\n \tOPT_END(),\n };\n \n@@ -259,8 +262,24 @@ int cmd_rm(int argc, const char **argv, const char *prefix)\n \n \targc = parse_options(argc, argv, prefix, builtin_rm_options,\n \t\t\t     builtin_rm_usage, 0);\n-\tif (!argc)\n-\t\tusage_with_options(builtin_rm_usage, builtin_rm_options);\n+\n+\tparse_pathspec(&pathspec, 0,\n+\t\t       PATHSPEC_PREFER_CWD,\n+\t\t       prefix, argv);\n+\n+\tif (pathspec_from_file) {\n+\t\tif (pathspec.nr)\n+\t\t\tdie(_(\"--pathspec-from-file is incompatible with pathspec arguments\"));\n+\n+\t\tparse_pathspec_file(&pathspec, 0,\n+\t\t\t\t    PATHSPEC_PREFER_CWD,\n+\t\t\t\t    prefix, pathspec_from_file, pathspec_file_nul);\n+\t} else if (pathspec_file_nul) {\n+\t\tdie(_(\"--pathspec-file-nul requires --pathspec-from-file\"));\n+\t}\n+\n+\tif (!pathspec.nr)\n+\t\tdie(_(\"Nothing specified, nothing removed\"));\n \n \tif (!index_only)\n \t\tsetup_work_tree();\n@@ -270,9 +289,6 @@ int cmd_rm(int argc, const char **argv, const char *prefix)\n \tif (read_cache() < 0)\n \t\tdie(_(\"index file corrupt\"));\n \n-\tparse_pathspec(&pathspec, 0,\n-\t\t       PATHSPEC_PREFER_CWD,\n-\t\t       prefix, argv);\n \trefresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &pathspec, NULL, NULL);\n \n \tseen = xcalloc(pathspec.nr, 1);\ndiff --git a/t/t3601-rm-pathspec-file.sh b/t/t3601-rm-pathspec-file.sh\nnew file mode 100755\nindex 0000000000..0f69ae8478\n--- /dev/null\n+++ b/t/t3601-rm-pathspec-file.sh\n@@ -0,0 +1,79 @@\n+#!/bin/sh\n+\n+test_description='rm --pathspec-from-file'\n+\n+. ./test-lib.sh\n+\n+test_tick\n+\n+test_expect_success setup '\n+\techo A >fileA.t &&\n+\techo B >fileB.t &&\n+\techo C >fileC.t &&\n+\techo D >fileD.t &&\n+\tgit add fileA.t fileB.t fileC.t fileD.t &&\n+\tgit commit -m \"files\" &&\n+\t\n+\tgit tag checkpoint\n+'\n+\n+restore_checkpoint () {\n+\tgit reset --hard checkpoint\n+}\n+\n+verify_expect () {\n+\tgit status --porcelain --untracked-files=no -- fileA.t fileB.t fileC.t fileD.t >actual &&\n+\ttest_cmp expect actual\n+}\n+\n+test_expect_success 'simplest' '\n+\trestore_checkpoint &&\n+\n+\tcat >expect <<-\\EOF &&\n+\tD  fileA.t\n+\tEOF\n+\n+\techo fileA.t | git rm --pathspec-from-file=- &&\n+\tverify_expect\n+'\n+\n+test_expect_success '--pathspec-file-nul' '\n+\trestore_checkpoint &&\n+\n+\tcat >expect <<-\\EOF &&\n+\tD  fileA.t\n+\tD  fileB.t\n+\tEOF\n+\n+\tprintf \"fileA.t\\0fileB.t\\0\" | git rm --pathspec-from-file=- --pathspec-file-nul &&\n+\tverify_expect\n+'\n+\n+test_expect_success 'only touches what was listed' '\n+\trestore_checkpoint &&\n+\n+\tcat >expect <<-\\EOF &&\n+\tD  fileB.t\n+\tD  fileC.t\n+\tEOF\n+\n+\tprintf \"fileB.t\\nfileC.t\\n\" | git rm --pathspec-from-file=- &&\n+\tverify_expect\n+'\n+\n+test_expect_success 'error conditions' '\n+\trestore_checkpoint &&\n+\techo fileA.t >list &&\n+\n+\ttest_must_fail git rm --pathspec-from-file=list -- fileA.t 2>err &&\n+\ttest_i18ngrep -e \"--pathspec-from-file is incompatible with pathspec arguments\" err &&\n+\n+\ttest_must_fail git rm --pathspec-file-nul 2>err &&\n+\ttest_i18ngrep -e \"--pathspec-file-nul requires --pathspec-from-file\" err &&\n+\t\n+\t>empty_list &&\n+\ttest_must_fail git rm --pathspec-from-file=empty_list 2>err &&\n+\ttest_i18ngrep -e \"Nothing specified, nothing removed\" err\n+'\n+\n+test_done\n-- \ngitgitgadget\n\n"},{"id":"389894","messageId":"708363241f4940e5b627af8519345b762deb77ab.1579190965.git.gitgitgadget@gmail.com","threadId":"52648","inReplyTo":"pull.530.git.1579190965.gitgitgadget@gmail.com","subject":"[PATCH 4/8] doc: stash: split options from description (2)","fromName":"Alexandr Miloslavskiy via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-01-16T16:09:21Z","receivedAt":"2020-01-16T16:09:36Z","isPatch":true,"sender":{"key":"alexandr.miloslavskiy@syntevo.com","avatar":null},"body":"From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n\nTogether with the previous patch, this brings docs for `git stash` to\nthe common layout used for most other commands (see for example docs for\n`git add`, `git commit`, `git checkout`, `git reset`) where all options\nare documented in a separate list.\n\nI have decided to use alphabetical sorting in the list of options. Other\ndocs often sort in order of appearance or order of importance, but in\nthis case it wouldn't be easy to read the list where options from\nmultiple sub-commands are mixed together.\n\nThere is some text editing done to make old descriptions better fit into\nthe list-style format.\n\nSigned-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n---\n Documentation/git-stash.txt | 72 ++++++++++++++++++++-----------------\n 1 file changed, 40 insertions(+), 32 deletions(-)\n\ndiff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt\nindex 2dedc21997..f75b80a720 100644\n--- a/Documentation/git-stash.txt\n+++ b/Documentation/git-stash.txt\n@@ -43,9 +43,6 @@ created stash, `stash@{1}` is the one before it, `stash@{2.hours.ago}`\n is also possible). Stashes may also be referenced by specifying just the\n stash index (e.g. the integer `n` is equivalent to `stash@{n}`).\n \n-OPTIONS\n--------\n-\n push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--] [<pathspec>...]::\n \n \tSave your local modifications to a new 'stash entry' and roll them\n@@ -152,40 +149,51 @@ store::\n \treflog.  This is intended to be useful for scripts.  It is\n \tprobably not the command you want to use; see \"push\" above.\n \n-If the `--all` option is used instead then the\n-ignored files are stashed and cleaned in addition to the untracked files.\n-\n-If the `--include-untracked` option is used, all untracked files are also\n-stashed and then cleaned up with `git clean`, leaving the working directory\n-in a very clean state.\n-\n-If the `--index` option is used, then tries to reinstate not only the working\n-tree's changes, but also the index's ones. However, this can fail, when you\n-have conflicts (which are stored in the index, where you therefore can no\n-longer apply the changes as they were originally).\n-\n-If the `--keep-index` option is used, all changes already added to the\n-index are left intact.\n-\n-With `--patch`, you can interactively select hunks from the diff\n-between HEAD and the working tree to be stashed.  The stash entry is\n-constructed such that its index state is the same as the index state\n-of your repository, and its worktree contains only the changes you\n-selected interactively.  The selected changes are then rolled back\n-from your worktree. See the ``Interactive Mode'' section of\n-linkgit:git-add[1] to learn how to operate the `--patch` mode.\n+OPTIONS\n+-------\n+-a::\n+--all::\n+\tAll ignored and untracked files are also stashed and then cleaned\n+\tup with `git clean`.\n+\n+-u::\n+--include-untracked::\n+\tAll untracked files are also stashed and then cleaned up with\n+\t`git clean`.\n+\n+--index::\n+\tTries to reinstate not only the working tree's changes, but also\n+\tthe index's ones. However, this can fail, when you have conflicts\n+\t(which are stored in the index, where you therefore can no longer\n+\tapply the changes as they were originally).\n+\n+-k::\n+--keep-index::\n+--no-keep-index::\n+\tAll changes already added to the index are left intact.\n+\n+-p::\n+--patch::\n+\tInteractively select hunks from the diff between HEAD and the\n+\tworking tree to be stashed.  The stash entry is constructed such\n+\tthat its index state is the same as the index state of your\n+\trepository, and its worktree contains only the changes you selected\n+\tinteractively.  The selected changes are then rolled back from your\n+\tworktree. See the ``Interactive Mode'' section of linkgit:git-add[1]\n+\tto learn how to operate the `--patch` mode.\n +\n The `--patch` option implies `--keep-index`.  You can use \n `--no-keep-index` to override this.\n \n-When pathspec is given to 'git stash push', the new stash entry records the\n-modified states only for the files that match the pathspec.  The index\n-entries and working tree files are then rolled back to the state in\n-HEAD only for these files, too, leaving files that do not match the\n-pathspec intact.\n+<pathspec>...::\n+\tThe new stash entry records the modified states only for the files\n+\tthat match the pathspec.  The index entries and working tree files\n+\tare then rolled back to the state in HEAD only for these files,\n+\ttoo, leaving files that do not match the pathspec intact.\n \n-When no `<stash>` is given, `stash@{0}` is assumed, otherwise `<stash>` must\n-be a reference of the form `stash@{<revision>}`.\n+<stash>::\n+\tA reference of the form `stash@{<revision>}`. When no `<stash>` is\n+\tgiven, the latest stash is assumed (that is, `stash@{0}`).\n \n DISCUSSION\n ----------\n-- \ngitgitgadget\n\n"},{"id":"389898","messageId":"8a5f2dbe9eef0d6fe360a8ac1130b854e73245bf.1579190965.git.gitgitgadget@gmail.com","threadId":"52648","inReplyTo":"pull.530.git.1579190965.gitgitgadget@gmail.com","subject":"[PATCH 5/8] doc: stash: document more options","fromName":"Alexandr Miloslavskiy via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-01-16T16:09:22Z","receivedAt":"2020-01-16T16:09:36Z","isPatch":true,"sender":{"key":"alexandr.miloslavskiy@syntevo.com","avatar":null},"body":"From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n\nSigned-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n---\n Documentation/git-stash.txt | 7 +++++++\n 1 file changed, 7 insertions(+)\n\ndiff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt\nindex f75b80a720..f5fa62dc7c 100644\n--- a/Documentation/git-stash.txt\n+++ b/Documentation/git-stash.txt\n@@ -185,6 +185,13 @@ OPTIONS\n The `--patch` option implies `--keep-index`.  You can use \n `--no-keep-index` to override this.\n \n+-q::\n+--quiet::\n+\tQuiet, suppress feedback messages.\n+\n+\\--::\n+\tSeparates pathspec from options for disambiguation purposes.\n+\n <pathspec>...::\n \tThe new stash entry records the modified states only for the files\n \tthat match the pathspec.  The index entries and working tree files\n-- \ngitgitgadget\n\n"},{"id":"389895","messageId":"5e17a0c470ec576d9dc4c24acecbad5ac2ddd9ed.1579190965.git.gitgitgadget@gmail.com","threadId":"52648","inReplyTo":"pull.530.git.1579190965.gitgitgadget@gmail.com","subject":"[PATCH 6/8] doc: stash: synchronize <pathspec> description","fromName":"Alexandr Miloslavskiy via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-01-16T16:09:23Z","receivedAt":"2020-01-16T16:09:38Z","isPatch":true,"sender":{"key":"alexandr.miloslavskiy@syntevo.com","avatar":null},"body":"From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n\nThis patch continues the effort that is already applied to\n`git commit`, `git reset`, `git checkout` etc.\n\n1) Added reference to 'linkgit:gitglossary[7]'.\n2) Fixed mentions of incorrectly plural \"pathspecs\".\n\nSigned-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n---\n Documentation/git-stash.txt | 6 ++++--\n 1 file changed, 4 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt\nindex f5fa62dc7c..576ad757d9 100644\n--- a/Documentation/git-stash.txt\n+++ b/Documentation/git-stash.txt\n@@ -53,13 +53,13 @@ push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q\n For quickly making a snapshot, you can omit \"push\".  In this mode,\n non-option arguments are not allowed to prevent a misspelled\n subcommand from making an unwanted stash entry.  The two exceptions to this\n-are `stash -p` which acts as alias for `stash push -p` and pathspecs,\n+are `stash -p` which acts as alias for `stash push -p` and pathspec elements,\n which are allowed after a double hyphen `--` for disambiguation.\n \n save [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [<message>]::\n \n \tThis option is deprecated in favour of 'git stash push'.  It\n-\tdiffers from \"stash push\" in that it cannot take pathspecs.\n+\tdiffers from \"stash push\" in that it cannot take pathspec.\n \tInstead, all non-option arguments are concatenated to form the stash\n \tmessage.\n \n@@ -197,6 +197,8 @@ The `--patch` option implies `--keep-index`.  You can use\n \tthat match the pathspec.  The index entries and working tree files\n \tare then rolled back to the state in HEAD only for these files,\n \ttoo, leaving files that do not match the pathspec intact.\n++\n+For more details, see the 'pathspec' entry in linkgit:gitglossary[7].\n \n <stash>::\n \tA reference of the form `stash@{<revision>}`. When no `<stash>` is\n-- \ngitgitgadget\n\n"},{"id":"389896","messageId":"7a8d36d49f85940d95f989741e68ba480abaa4eb.1579190965.git.gitgitgadget@gmail.com","threadId":"52648","inReplyTo":"pull.530.git.1579190965.gitgitgadget@gmail.com","subject":"[PATCH 7/8] stash: eliminate crude option parsing","fromName":"Alexandr Miloslavskiy via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-01-16T16:09:24Z","receivedAt":"2020-01-16T16:09:39Z","isPatch":true,"sender":{"key":"alexandr.miloslavskiy@syntevo.com","avatar":null},"body":"From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n\nEliminate crude option parsing and rely on real parsing instead, because\n1) Crude parsing is crude, for example it's not capable of\n   handling things like `git stash -m Message`\n2) Adding options in two places is inconvenient and prone to bugs\n\nAs a side result, the case of `git stash -m Message` gets fixed.\nAlso give a good error message instead of just throwing usage at user.\n\n----\n\nSome review of what's been happening to this code:\n\nBefore [1], `git-stash.sh` only verified that all args begin with `-` :\n\n\t# The default command is \"push\" if nothing but options are given\n\tseen_non_option=\n\tfor opt\n\tdo\n\t\tcase \"$opt\" in\n\t\t--) break ;;\n\t\t-*) ;;\n\t\t*) seen_non_option=t; break ;;\n\t\tesac\n\tdone\n\nLater, [1] introduced the duplicate code I'm now removing, also making\nthe previous test more strict by white-listing options.\n\n----\n\n[1] Commit 40af1468 (\"stash: convert `stash--helper.c` into `stash.c`\" 2019-02-26)\n\nSigned-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n---\n builtin/stash.c  | 59 +++++++++++++++++-------------------------------\n t/t3903-stash.sh |  5 ++++\n 2 files changed, 26 insertions(+), 38 deletions(-)\n\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex 4ad3adf4ba..7bc4c5d696 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -1448,8 +1448,10 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q\n \treturn ret;\n }\n \n-static int push_stash(int argc, const char **argv, const char *prefix)\n+static int push_stash(int argc, const char **argv, const char *prefix,\n+\t\t      int push_assumed)\n {\n+\tint force_assume = 0;\n \tint keep_index = -1;\n \tint patch_mode = 0;\n \tint include_untracked = 0;\n@@ -1471,10 +1473,22 @@ static int push_stash(int argc, const char **argv, const char *prefix)\n \t\tOPT_END()\n \t};\n \n-\tif (argc)\n+\tif (argc) {\n+\t\tforce_assume = !strcmp(argv[0], \"-p\");\n \t\targc = parse_options(argc, argv, prefix, options,\n \t\t\t\t     git_stash_push_usage,\n-\t\t\t\t     0);\n+\t\t\t\t     PARSE_OPT_KEEP_DASHDASH);\n+\t}\n+\n+\tif (argc) {\n+\t\tif (!strcmp(argv[0], \"--\")) {\n+\t\t\targc--;\n+\t\t\targv++;\n+\t\t} else if (push_assumed && !force_assume) {\n+\t\t\tdie(\"subcommand wasn't specified; 'push' can't be assumed due to unexpected token '%s'\",\n+\t\t\t    argv[0]);\n+\t\t}\n+\t}\n \n \tparse_pathspec(&ps, 0, PATHSPEC_PREFER_FULL | PATHSPEC_PREFIX_ORIGIN,\n \t\t       prefix, argv);\n@@ -1547,7 +1561,6 @@ static int use_builtin_stash(void)\n \n int cmd_stash(int argc, const char **argv, const char *prefix)\n {\n-\tint i = -1;\n \tpid_t pid = getpid();\n \tconst char *index_file;\n \tstruct argv_array args = ARGV_ARRAY_INIT;\n@@ -1580,7 +1593,7 @@ int cmd_stash(int argc, const char **argv, const char *prefix)\n \t\t    (uintmax_t)pid);\n \n \tif (!argc)\n-\t\treturn !!push_stash(0, NULL, prefix);\n+\t\treturn !!push_stash(0, NULL, prefix, 0);\n \telse if (!strcmp(argv[0], \"apply\"))\n \t\treturn !!apply_stash(argc, argv, prefix);\n \telse if (!strcmp(argv[0], \"clear\"))\n@@ -1600,45 +1613,15 @@ int cmd_stash(int argc, const char **argv, const char *prefix)\n \telse if (!strcmp(argv[0], \"create\"))\n \t\treturn !!create_stash(argc, argv, prefix);\n \telse if (!strcmp(argv[0], \"push\"))\n-\t\treturn !!push_stash(argc, argv, prefix);\n+\t\treturn !!push_stash(argc, argv, prefix, 0);\n \telse if (!strcmp(argv[0], \"save\"))\n \t\treturn !!save_stash(argc, argv, prefix);\n \telse if (*argv[0] != '-')\n \t\tusage_msg_opt(xstrfmt(_(\"unknown subcommand: %s\"), argv[0]),\n \t\t\t      git_stash_usage, options);\n \n-\tif (strcmp(argv[0], \"-p\")) {\n-\t\twhile (++i < argc && strcmp(argv[i], \"--\")) {\n-\t\t\t/*\n-\t\t\t * `akpqu` is a string which contains all short options,\n-\t\t\t * except `-m` which is verified separately.\n-\t\t\t */\n-\t\t\tif ((strlen(argv[i]) == 2) && *argv[i] == '-' &&\n-\t\t\t    strchr(\"akpqu\", argv[i][1]))\n-\t\t\t\tcontinue;\n-\n-\t\t\tif (!strcmp(argv[i], \"--all\") ||\n-\t\t\t    !strcmp(argv[i], \"--keep-index\") ||\n-\t\t\t    !strcmp(argv[i], \"--no-keep-index\") ||\n-\t\t\t    !strcmp(argv[i], \"--patch\") ||\n-\t\t\t    !strcmp(argv[i], \"--quiet\") ||\n-\t\t\t    !strcmp(argv[i], \"--include-untracked\"))\n-\t\t\t\tcontinue;\n-\n-\t\t\t/*\n-\t\t\t * `-m` and `--message=` are verified separately because\n-\t\t\t * they need to be immediately followed by a string\n-\t\t\t * (i.e.`-m\"foobar\"` or `--message=\"foobar\"`).\n-\t\t\t */\n-\t\t\tif (starts_with(argv[i], \"-m\") ||\n-\t\t\t    starts_with(argv[i], \"--message=\"))\n-\t\t\t\tcontinue;\n-\n-\t\t\tusage_with_options(git_stash_usage, options);\n-\t\t}\n-\t}\n-\n+\t/* Assume 'stash push' */\n \targv_array_push(&args, \"push\");\n \targv_array_pushv(&args, argv);\n-\treturn !!push_stash(args.argc, args.argv, prefix);\n+\treturn !!push_stash(args.argc, args.argv, prefix, 1);\n }\ndiff --git a/t/t3903-stash.sh b/t/t3903-stash.sh\nindex ea56e85e70..3ad23e2502 100755\n--- a/t/t3903-stash.sh\n+++ b/t/t3903-stash.sh\n@@ -285,6 +285,11 @@ test_expect_success 'stash --no-keep-index' '\n \ttest bar,bar2 = $(cat file),$(cat file2)\n '\n \n+test_expect_success 'dont assume push with non-option args' '\n+\ttest_must_fail git stash -q drop 2>err &&\n+\ttest_i18ngrep -e \"subcommand wasn'\\''t specified; '\\''push'\\'' can'\\''t be assumed due to unexpected token '\\''drop'\\''\" err\n+'\n+\n test_expect_success 'stash --invalid-option' '\n \techo bar5 >file &&\n \techo bar6 >file2 &&\n-- \ngitgitgadget\n\n"},{"id":"389899","messageId":"721410233bbee49fa13773ae9c04b4c6ed076a1a.1579190965.git.gitgitgadget@gmail.com","threadId":"52648","inReplyTo":"pull.530.git.1579190965.gitgitgadget@gmail.com","subject":"[PATCH 8/8] stash push: support the --pathspec-from-file option","fromName":"Alexandr Miloslavskiy via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-01-16T16:09:25Z","receivedAt":"2020-01-16T16:09:39Z","isPatch":true,"sender":{"key":"alexandr.miloslavskiy@syntevo.com","avatar":null},"body":"From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n\nDecisions taken for simplicity:\n1) For now, `--pathspec-from-file` is declared incompatible with\n   `--patch`, even when <file> is not `-`. Such use case is not\n   really expected.\n2) It is not allowed to pass pathspec in both args and file.\n\nSigned-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n---\n Documentation/git-stash.txt    |  16 +++++-\n builtin/stash.c                |  20 +++++++\n t/t3909-stash-pathspec-file.sh | 100 +++++++++++++++++++++++++++++++++\n 3 files changed, 135 insertions(+), 1 deletion(-)\n create mode 100755 t/t3909-stash-pathspec-file.sh\n\ndiff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt\nindex 576ad757d9..4e6a27c4fd 100644\n--- a/Documentation/git-stash.txt\n+++ b/Documentation/git-stash.txt\n@@ -15,6 +15,7 @@ SYNOPSIS\n 'git stash' branch <branchname> [<stash>]\n 'git stash' [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n \t     [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n+\t     [--pathspec-from-file=<file> [--pathspec-file-nul]]\n \t     [--] [<pathspec>...]]\n 'git stash' clear\n 'git stash' create [<message>]\n@@ -43,7 +44,7 @@ created stash, `stash@{1}` is the one before it, `stash@{2.hours.ago}`\n is also possible). Stashes may also be referenced by specifying just the\n stash index (e.g. the integer `n` is equivalent to `stash@{n}`).\n \n-push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--] [<pathspec>...]::\n+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>...]::\n \n \tSave your local modifications to a new 'stash entry' and roll them\n \tback to HEAD (in the working tree and in the index).\n@@ -185,6 +186,19 @@ OPTIONS\n The `--patch` option implies `--keep-index`.  You can use \n `--no-keep-index` to override this.\n \n+--pathspec-from-file=<file>::\n+\tPathspec is passed in `<file>` instead of commandline args. If\n+\t`<file>` is exactly `-` then standard input is used. Pathspec\n+\telements are separated by LF or CR/LF. Pathspec elements can be\n+\tquoted as explained for the configuration variable `core.quotePath`\n+\t(see linkgit:git-config[1]). See also `--pathspec-file-nul` and\n+\tglobal `--literal-pathspecs`.\n+\n+--pathspec-file-nul::\n+\tOnly meaningful with `--pathspec-from-file`. Pathspec elements are\n+\tseparated with NUL character and all other characters are taken\n+\tliterally (including newlines and quotes).\n+\n -q::\n --quiet::\n \tQuiet, suppress feedback messages.\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex 7bc4c5d696..74d92595a2 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -27,6 +27,7 @@ static const char * const git_stash_usage[] = {\n \tN_(\"git stash clear\"),\n \tN_(\"git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\\n\"\n \t   \"          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\\n\"\n+\t   \"          [--pathspec-from-file=<file> [--pathspec-file-nul]]\\n\"\n \t   \"          [--] [<pathspec>...]]\"),\n \tN_(\"git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\\n\"\n \t   \"          [-u|--include-untracked] [-a|--all] [<message>]\"),\n@@ -1456,7 +1457,9 @@ static int push_stash(int argc, const char **argv, const char *prefix,\n \tint patch_mode = 0;\n \tint include_untracked = 0;\n \tint quiet = 0;\n+\tint pathspec_file_nul = 0;\n \tconst char *stash_msg = NULL;\n+\tconst char *pathspec_from_file = NULL;\n \tstruct pathspec ps;\n \tstruct option options[] = {\n \t\tOPT_BOOL('k', \"keep-index\", &keep_index,\n@@ -1470,6 +1473,8 @@ static int push_stash(int argc, const char **argv, const char *prefix,\n \t\t\t    N_(\"include ignore files\"), 2),\n \t\tOPT_STRING('m', \"message\", &stash_msg, N_(\"message\"),\n \t\t\t   N_(\"stash message\")),\n+\t\tOPT_PATHSPEC_FROM_FILE(&pathspec_from_file),\n+\t\tOPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),\n \t\tOPT_END()\n \t};\n \n@@ -1492,6 +1497,21 @@ static int push_stash(int argc, const char **argv, const char *prefix,\n \n \tparse_pathspec(&ps, 0, PATHSPEC_PREFER_FULL | PATHSPEC_PREFIX_ORIGIN,\n \t\t       prefix, argv);\n+\n+\tif (pathspec_from_file) {\n+\t\tif (patch_mode)\n+\t\t\tdie(_(\"--pathspec-from-file is incompatible with --patch\"));\n+\n+\t\tif (ps.nr)\n+\t\t\tdie(_(\"--pathspec-from-file is incompatible with pathspec arguments\"));\n+\n+\t\tparse_pathspec_file(&ps, 0,\n+\t\t\t\t    PATHSPEC_PREFER_FULL | PATHSPEC_PREFIX_ORIGIN,\n+\t\t\t\t    prefix, pathspec_from_file, pathspec_file_nul);\n+\t} else if (pathspec_file_nul) {\n+\t\tdie(_(\"--pathspec-file-nul requires --pathspec-from-file\"));\n+\t}\n+\n \treturn do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode,\n \t\t\t     include_untracked);\n }\ndiff --git a/t/t3909-stash-pathspec-file.sh b/t/t3909-stash-pathspec-file.sh\nnew file mode 100755\nindex 0000000000..55e050cfd4\n--- /dev/null\n+++ b/t/t3909-stash-pathspec-file.sh\n@@ -0,0 +1,100 @@\n+#!/bin/sh\n+\n+test_description='stash --pathspec-from-file'\n+\n+. ./test-lib.sh\n+\n+test_tick\n+\n+test_expect_success setup '\n+\t>fileA.t &&\n+\t>fileB.t &&\n+\t>fileC.t &&\n+\t>fileD.t &&\n+\tgit add fileA.t fileB.t fileC.t fileD.t &&\n+\tgit commit -m \"Files\" &&\n+\n+\tgit tag checkpoint\n+'\n+\n+restore_checkpoint () {\n+\tgit reset --hard checkpoint\n+}\n+\n+verify_expect () {\n+\tgit stash show --name-status >actual &&\n+\ttest_cmp expect actual\n+}\n+\n+test_expect_success 'simplest' '\n+\trestore_checkpoint &&\n+\n+\t# More files are written to make sure that git didnt ignore\n+\t# --pathspec-from-file, stashing everything\n+\techo A >fileA.t &&\n+\techo B >fileB.t &&\n+\techo C >fileC.t &&\n+\techo D >fileD.t &&\n+\n+\tcat >expect <<-\\EOF &&\n+\tM\tfileA.t\n+\tEOF\n+\n+\techo fileA.t | git stash push --pathspec-from-file=- &&\n+\tverify_expect\n+'\n+\n+test_expect_success '--pathspec-file-nul' '\n+\trestore_checkpoint &&\n+\n+\t# More files are written to make sure that git didnt ignore\n+\t# --pathspec-from-file, stashing everything\n+\techo A >fileA.t &&\n+\techo B >fileB.t &&\n+\techo C >fileC.t &&\n+\techo D >fileD.t &&\n+\n+\tcat >expect <<-\\EOF &&\n+\tM\tfileA.t\n+\tM\tfileB.t\n+\tEOF\n+\n+\tprintf \"fileA.t\\0fileB.t\\0\" | git stash push --pathspec-from-file=- --pathspec-file-nul &&\n+\tverify_expect\n+'\n+\n+test_expect_success 'only touches what was listed' '\n+\trestore_checkpoint &&\n+\n+\t# More files are written to make sure that git didnt ignore\n+\t# --pathspec-from-file, stashing everything\n+\techo A >fileA.t &&\n+\techo B >fileB.t &&\n+\techo C >fileC.t &&\n+\techo D >fileD.t &&\n+\n+\tcat >expect <<-\\EOF &&\n+\tM\tfileB.t\n+\tM\tfileC.t\n+\tEOF\n+\n+\tprintf \"fileB.t\\nfileC.t\\n\" | git stash push --pathspec-from-file=- &&\n+\tverify_expect\n+'\n+\n+test_expect_success 'error conditions' '\n+\trestore_checkpoint &&\n+\techo A >fileA.t &&\n+\techo fileA.t >list &&\n+\n+\ttest_must_fail git stash push --pathspec-from-file=list --patch 2>err &&\n+\ttest_i18ngrep -e \"--pathspec-from-file is incompatible with --patch\" err &&\n+\n+\ttest_must_fail git stash push --pathspec-from-file=list -- fileA.t 2>err &&\n+\ttest_i18ngrep -e \"--pathspec-from-file is incompatible with pathspec arguments\" err &&\n+\n+\ttest_must_fail git stash push --pathspec-file-nul 2>err &&\n+\ttest_i18ngrep -e \"--pathspec-file-nul requires --pathspec-from-file\" err\n+'\n+\n+test_done\n-- \ngitgitgadget\n"},{"id":"389897","messageId":"0824bba2101bb034bafb124d2b810721923233f3.1579190965.git.gitgitgadget@gmail.com","threadId":"52648","inReplyTo":"pull.530.git.1579190965.gitgitgadget@gmail.com","subject":"[PATCH 3/8] doc: stash: split options from description (1)","fromName":"Alexandr Miloslavskiy via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-01-16T16:09:20Z","receivedAt":"2020-01-16T16:09:40Z","isPatch":true,"sender":{"key":"alexandr.miloslavskiy@syntevo.com","avatar":null},"body":"From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n\nThis patch moves blocks of text as-is to make it easier to review the\nnext patch.\n\nSigned-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n---\n Documentation/git-stash.txt | 68 +++++++++++++++++++------------------\n 1 file changed, 35 insertions(+), 33 deletions(-)\n\ndiff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt\nindex 53e1a1205d..2dedc21997 100644\n--- a/Documentation/git-stash.txt\n+++ b/Documentation/git-stash.txt\n@@ -58,31 +58,6 @@ non-option arguments are not allowed to prevent a misspelled\n subcommand from making an unwanted stash entry.  The two exceptions to this\n are `stash -p` which acts as alias for `stash push -p` and pathspecs,\n which are allowed after a double hyphen `--` for disambiguation.\n-+\n-When pathspec is given to 'git stash push', the new stash entry records the\n-modified states only for the files that match the pathspec.  The index\n-entries and working tree files are then rolled back to the state in\n-HEAD only for these files, too, leaving files that do not match the\n-pathspec intact.\n-+\n-If the `--keep-index` option is used, all changes already added to the\n-index are left intact.\n-+\n-If the `--include-untracked` option is used, all untracked files are also\n-stashed and then cleaned up with `git clean`, leaving the working directory\n-in a very clean state. If the `--all` option is used instead then the\n-ignored files are stashed and cleaned in addition to the untracked files.\n-+\n-With `--patch`, you can interactively select hunks from the diff\n-between HEAD and the working tree to be stashed.  The stash entry is\n-constructed such that its index state is the same as the index state\n-of your repository, and its worktree contains only the changes you\n-selected interactively.  The selected changes are then rolled back\n-from your worktree. See the ``Interactive Mode'' section of\n-linkgit:git-add[1] to learn how to operate the `--patch` mode.\n-+\n-The `--patch` option implies `--keep-index`.  You can use\n-`--no-keep-index` to override this.\n \n save [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [<message>]::\n \n@@ -128,14 +103,6 @@ pop [--index] [-q|--quiet] [<stash>]::\n Applying the state can fail with conflicts; in this case, it is not\n removed from the stash list. You need to resolve the conflicts by hand\n and call `git stash drop` manually afterwards.\n-+\n-If the `--index` option is used, then tries to reinstate not only the working\n-tree's changes, but also the index's ones. However, this can fail, when you\n-have conflicts (which are stored in the index, where you therefore can no\n-longer apply the changes as they were originally).\n-+\n-When no `<stash>` is given, `stash@{0}` is assumed, otherwise `<stash>` must\n-be a reference of the form `stash@{<revision>}`.\n \n apply [--index] [-q|--quiet] [<stash>]::\n \n@@ -185,6 +152,41 @@ store::\n \treflog.  This is intended to be useful for scripts.  It is\n \tprobably not the command you want to use; see \"push\" above.\n \n+If the `--all` option is used instead then the\n+ignored files are stashed and cleaned in addition to the untracked files.\n+\n+If the `--include-untracked` option is used, all untracked files are also\n+stashed and then cleaned up with `git clean`, leaving the working directory\n+in a very clean state.\n+\n+If the `--index` option is used, then tries to reinstate not only the working\n+tree's changes, but also the index's ones. However, this can fail, when you\n+have conflicts (which are stored in the index, where you therefore can no\n+longer apply the changes as they were originally).\n+\n+If the `--keep-index` option is used, all changes already added to the\n+index are left intact.\n+\n+With `--patch`, you can interactively select hunks from the diff\n+between HEAD and the working tree to be stashed.  The stash entry is\n+constructed such that its index state is the same as the index state\n+of your repository, and its worktree contains only the changes you\n+selected interactively.  The selected changes are then rolled back\n+from your worktree. See the ``Interactive Mode'' section of\n+linkgit:git-add[1] to learn how to operate the `--patch` mode.\n++\n+The `--patch` option implies `--keep-index`.  You can use \n+`--no-keep-index` to override this.\n+\n+When pathspec is given to 'git stash push', the new stash entry records the\n+modified states only for the files that match the pathspec.  The index\n+entries and working tree files are then rolled back to the state in\n+HEAD only for these files, too, leaving files that do not match the\n+pathspec intact.\n+\n+When no `<stash>` is given, `stash@{0}` is assumed, otherwise `<stash>` must\n+be a reference of the form `stash@{<revision>}`.\n+\n DISCUSSION\n ----------\n \n-- \ngitgitgadget\n\n"},{"id":"390194","messageId":"xmqqk15kaaga.fsf@gitster-ct.c.googlers.com","threadId":"52648","inReplyTo":"23387f83912d1f5aef17a28d466db162f50fd722.1579190965.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 1/8] doc: rm: synchronize <pathspec> description","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-01-21T19:14:29Z","receivedAt":"2020-01-21T19:14:35Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Alexandr Miloslavskiy via GitGitGadget\" <gitgitgadget@gmail.com>\nwrites:\n\n> From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n>\n> This patch continues the effort that is already applied to\n> `git commit`, `git reset`, `git checkout` etc.\n>\n> 1) Changed outdated descriptions to mention pathspec instead.\n> 2) Added reference to 'linkgit:gitglossary[7]'.\n> 3) Removed content that merely repeated gitglossary.\n> 4) Merged the remainder of \"discussion\" into `<patchspec>`.\n\nThanks.  Will queue.\n"},{"id":"390197","messageId":"xmqqftg8a9fp.fsf@gitster-ct.c.googlers.com","threadId":"52648","inReplyTo":"5611e3ae326bb7f61abf870e3b2851226b6af1d8.1579190965.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 2/8] rm: support the --pathspec-from-file option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-01-21T19:36:26Z","receivedAt":"2020-01-21T19:36:35Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Alexandr Miloslavskiy via GitGitGadget\" <gitgitgadget@gmail.com>\nwrites:\n\n> From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n>\n> Decisions taken for simplicity:\n> 1) It is not allowed to pass pathspec in both args and file.\n>\n> `if (!argc)` block was adapted to work with --pathspec-from-file. For\n> that, I also had to parse pathspec earlier. Now it happens before\n> `read_cache()` / `hold_locked_index()` / `setup_work_tree()`, which\n> sounds fine to me.\n\nThat is not an explanation nor justification.\n\n> In case of empty pathspec, there is now a clear error message instead\n> of showing usage.\n\nHmph, \"git rm --pathspec-from-file=/dev/null\" would say \"nothing\nspecified, nothing removed\" and it makes perfect sense, but I am not\nsure \"git rm\" that gives the same message is better than the output\nby usage_with_options(builtin_rm_usage, builtin_rm_options).\n\n> -'git rm' [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] <pathspec>...\n> +'git rm' [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch]\n> +\t  [--quiet] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n> +\t  [--] [<pathspec>...]\n\nOK.\n\n> +--pathspec-from-file=<file>::\n> +\tPathspec is passed in `<file>` instead of commandline args. If\n> +\t`<file>` is exactly `-` then standard input is used. Pathspec\n> +\telements are separated by LF or CR/LF. Pathspec elements can be\n> +\tquoted as explained for the configuration variable `core.quotePath`\n> +\t(see linkgit:git-config[1]). See also `--pathspec-file-nul` and\n> +\tglobal `--literal-pathspecs`.\n> +\n> +--pathspec-file-nul::\n> +\tOnly meaningful with `--pathspec-from-file`. Pathspec elements are\n> +\tseparated with NUL character and all other characters are taken\n> +\tliterally (including newlines and quotes).\n> +\n\nOK.\n\n> diff --git a/builtin/rm.c b/builtin/rm.c\n> index 19ce95a901..8e40795751 100644\n> --- a/builtin/rm.c\n> +++ b/builtin/rm.c\n> @@ -235,7 +235,8 @@ static int check_local_mod(struct object_id *head, int index_only)\n>  }\n>  \n>  static int show_only = 0, force = 0, index_only = 0, recursive = 0, quiet = 0;\n> -static int ignore_unmatch = 0;\n> +static int ignore_unmatch = 0, pathspec_file_nul = 0;\n> +static char *pathspec_from_file = NULL;\n\nWe may want to clean these \"explicitly initialize to 0/NULL\" up at\nsome point.  The clean-up itself would not be in the scope of this\npatch, of course, but not making it worse is something this patch\ncan do to help.\n\n> @@ -259,8 +262,24 @@ int cmd_rm(int argc, const char **argv, const char *prefix)\n>  \n>  \targc = parse_options(argc, argv, prefix, builtin_rm_options,\n>  \t\t\t     builtin_rm_usage, 0);\n> -\tif (!argc)\n> -\t\tusage_with_options(builtin_rm_usage, builtin_rm_options);\n> +\n> +\tparse_pathspec(&pathspec, 0,\n> +\t\t       PATHSPEC_PREFER_CWD,\n> +\t\t       prefix, argv);\n> +\n> +\tif (pathspec_from_file) {\n> +\t\tif (pathspec.nr)\n> +\t\t\tdie(_(\"--pathspec-from-file is incompatible with pathspec arguments\"));\n> +\n> +\t\tparse_pathspec_file(&pathspec, 0,\n> +\t\t\t\t    PATHSPEC_PREFER_CWD,\n> +\t\t\t\t    prefix, pathspec_from_file, pathspec_file_nul);\n> +\t} else if (pathspec_file_nul) {\n> +\t\tdie(_(\"--pathspec-file-nul requires --pathspec-from-file\"));\n> +\t}\n> +\n> +\tif (!pathspec.nr)\n> +\t\tdie(_(\"Nothing specified, nothing removed\"));\n\nI wonder if doing these in this order instead would make more sense\nwithout making unnecessary behaviour change.\n\n    - parse the options (which would make pathspec_f_f available to\n      us)\n\n    - if pathspec_f_f is given, call parse_pathspec_file()\n\n    - otherwise complain if pathspec_file_nul is set\n\n    - otherwise check argc and give the usage_with_options()\n\nI dunno.\n\nThanks.\n"},{"id":"390199","messageId":"xmqqblqwa7d3.fsf@gitster-ct.c.googlers.com","threadId":"52648","inReplyTo":"708363241f4940e5b627af8519345b762deb77ab.1579190965.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 4/8] doc: stash: split options from description (2)","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-01-21T20:21:12Z","receivedAt":"2020-01-21T20:21:21Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"> diff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt\n> index 2dedc21997..f75b80a720 100644\n> --- a/Documentation/git-stash.txt\n> +++ b/Documentation/git-stash.txt\n> @@ -43,9 +43,6 @@ created stash, `stash@{1}` is the one before it, `stash@{2.hours.ago}`\n>  is also possible). Stashes may also be referenced by specifying just the\n>  stash index (e.g. the integer `n` is equivalent to `stash@{n}`).\n>  \n> -OPTIONS\n> --------\n> -\n>  push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--] [<pathspec>...]::\n>  \n>  \tSave your local modifications to a new 'stash entry' and roll them\n> @@ -152,40 +149,51 @@ store::\n>  \treflog.  This is intended to be useful for scripts.  It is\n>  \tprobably not the command you want to use; see \"push\" above.\n>  \n> -If the `--all` option is used instead then the\n> -ignored files are stashed and cleaned in addition to the untracked files.\n> -\n> -If the `--include-untracked` option is used, all untracked files are also\n> -stashed and then cleaned up with `git clean`, leaving the working directory\n> -in a very clean state.\n> -\n> -If the `--index` option is used, then tries to reinstate not only the working\n> -tree's changes, but also the index's ones. However, this can fail, when you\n> -have conflicts (which are stored in the index, where you therefore can no\n> -longer apply the changes as they were originally).\n> -\n> -If the `--keep-index` option is used, all changes already added to the\n> -index are left intact.\n> -\n> -With `--patch`, you can interactively select hunks from the diff\n> -between HEAD and the working tree to be stashed.  The stash entry is\n> -constructed such that its index state is the same as the index state\n> -of your repository, and its worktree contains only the changes you\n> -selected interactively.  The selected changes are then rolled back\n> -from your worktree. See the ``Interactive Mode'' section of\n> -linkgit:git-add[1] to learn how to operate the `--patch` mode.\n> +OPTIONS\n> +-------\n> +-a::\n> +--all::\n> +\tAll ignored and untracked files are also stashed and then cleaned\n> +\tup with `git clean`.\n> +\n> +-u::\n> +--include-untracked::\n> +\tAll untracked files are also stashed and then cleaned up with\n> +\t`git clean`.\n> +\n> +--index::\n> +\tTries to reinstate not only the working tree's changes, but also\n> +\tthe index's ones. However, this can fail, when you have conflicts\n> +\t(which are stored in the index, where you therefore can no longer\n> +\tapply the changes as they were originally).\n> +\n> +-k::\n> +--keep-index::\n> +--no-keep-index::\n> +\tAll changes already added to the index are left intact.\n> +\n> +-p::\n> +--patch::\n> +\tInteractively select hunks from the diff between HEAD and the\n> +\tworking tree to be stashed.  The stash entry is constructed such\n> +\tthat its index state is the same as the index state of your\n> +\trepository, and its worktree contains only the changes you selected\n> +\tinteractively.  The selected changes are then rolled back from your\n> +\tworktree. See the ``Interactive Mode'' section of linkgit:git-add[1]\n> +\tto learn how to operate the `--patch` mode.\n\nI have a mixed feelings about this approach.  While I am sympathetic\nto the \"have a single place to describe all\" approach this patch\ntakes, the approach needs to be executed with care when subcommands\ndo not share much of the options at all.  Those readers who jump to\nthe \"OPTIONS\" section and try to ignore anything outside the section\nmay not easily notice that --keep-index only applies to subcommands\nthat creates a new stash, and meaningless to subcommands that lets\nyou inspect existing stashes, or apply one to the working tree (and\noptionally to the index), for example.  If the orinal documentation\ndid not use \"OPTIONS\" as the section header and instead said perhaps\n\"SUBCOMMANDS\", it would have been even better, but otherwise I would\nsuspect that the original \"the options understood by 'push' are all\ndescribed under the part that begins with 'push [-p] [-k] ...'\ncommand line\" arrangement was much easier to understand when reading\nthem through for the first time to learn and also to find what the\nuser is looking for after learning the \"concept\" (e.g. \"with\n'stash', there is a way to stash-away the changes made to the\nworking tree\") but before becoming familiar with exact set of\noptions for each subcommand (e.g. \"and there was an option that let\nme stash only partial changes piecemeal, but what was it spelled?\").\n\nIf we were to make the result of \"a single place to describe all\"\napproach anything useful, I think at least\n\n (1) the list itself should make it clear that it does not talk\n     about options related to listing and showing at all,\n     before enumerating dashed options.\n\n (2) each item in the enumeration should identify which\n     subcommand(s) accept(s) it.\n\nSo, I dunno.\n\n\n\n"},{"id":"390200","messageId":"xmqq7e1ka6zd.fsf@gitster-ct.c.googlers.com","threadId":"52648","inReplyTo":"8a5f2dbe9eef0d6fe360a8ac1130b854e73245bf.1579190965.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 5/8] doc: stash: document more options","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-01-21T20:29:26Z","receivedAt":"2020-01-21T20:29:32Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Alexandr Miloslavskiy via GitGitGadget\" <gitgitgadget@gmail.com>\nwrites:\n\n> +-q::\n> +--quiet::\n> +\tQuiet, suppress feedback messages.\n> +\n> +\\--::\n> +\tSeparates pathspec from options for disambiguation purposes.\n> +\n>  <pathspec>...::\n>  \tThe new stash entry records the modified states only for the files\n>  \tthat match the pathspec.  The index entries and working tree files\n\nOK.  Describing these in the documentation is a good thing.  How\nthey should be added depends on in what shape the patch 4/8 should\nsettle, though.\n\nThanks.\n"},{"id":"390201","messageId":"xmqq36c8a6yl.fsf@gitster-ct.c.googlers.com","threadId":"52648","inReplyTo":"5e17a0c470ec576d9dc4c24acecbad5ac2ddd9ed.1579190965.git.gitgitgadget@gmail.com","subject":"Re: [PATCH 6/8] doc: stash: synchronize <pathspec> description","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-01-21T20:29:54Z","receivedAt":"2020-01-21T20:30:02Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Alexandr Miloslavskiy via GitGitGadget\" <gitgitgadget@gmail.com>\nwrites:\n\n> From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n>\n> This patch continues the effort that is already applied to\n> `git commit`, `git reset`, `git checkout` etc.\n\nMakes sense.\n"},{"id":"391453","messageId":"f91ec08b472db47b88443d68208ca6b2ef067f3c.1581345948.git.gitgitgadget@gmail.com","threadId":"52648","inReplyTo":"pull.530.v2.git.1581345948.gitgitgadget@gmail.com","subject":"[PATCH v2 5/8] doc: stash: document more options","fromName":"Alexandr Miloslavskiy via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-02-10T14:45:44Z","receivedAt":"2020-02-10T14:45:56Z","isPatch":true,"sender":{"key":"alexandr.miloslavskiy@syntevo.com","avatar":null},"body":"From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n\nSigned-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n---\n Documentation/git-stash.txt | 12 ++++++++++++\n 1 file changed, 12 insertions(+)\n\ndiff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt\nindex c1c16623cb..6dc0a5b0ee 100644\n--- a/Documentation/git-stash.txt\n+++ b/Documentation/git-stash.txt\n@@ -194,6 +194,18 @@ to learn how to operate the `--patch` mode.\n The `--patch` option implies `--keep-index`.  You can use \n `--no-keep-index` to override this.\n \n+-q::\n+--quiet::\n+\tThis option is only valid for `apply`, `drop`, `pop`, `push`,\n+\t`save`, `store` commands.\n++\n+Quiet, suppress feedback messages.\n+\n+\\--::\n+\tThis option is only valid for `push` command.\n++\n+Separates pathspec from options for disambiguation purposes.\n+\n <pathspec>...::\n \tThis option is only valid for `push` command.\n +\n-- \ngitgitgadget\n\n"},{"id":"391454","messageId":"8c212fc0ed416fcd6a2da798cf92d4f2edae3608.1581345948.git.gitgitgadget@gmail.com","threadId":"52648","inReplyTo":"pull.530.v2.git.1581345948.gitgitgadget@gmail.com","subject":"[PATCH v2 3/8] doc: stash: split options from description (1)","fromName":"Alexandr Miloslavskiy via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-02-10T14:45:42Z","receivedAt":"2020-02-10T14:45:58Z","isPatch":true,"sender":{"key":"alexandr.miloslavskiy@syntevo.com","avatar":null},"body":"From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n\nThis patch moves blocks of text as-is to make it easier to review the\nnext patch.\n\nSigned-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n---\n Documentation/git-stash.txt | 68 +++++++++++++++++++------------------\n 1 file changed, 35 insertions(+), 33 deletions(-)\n\ndiff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt\nindex 53e1a1205d..2dedc21997 100644\n--- a/Documentation/git-stash.txt\n+++ b/Documentation/git-stash.txt\n@@ -58,31 +58,6 @@ non-option arguments are not allowed to prevent a misspelled\n subcommand from making an unwanted stash entry.  The two exceptions to this\n are `stash -p` which acts as alias for `stash push -p` and pathspecs,\n which are allowed after a double hyphen `--` for disambiguation.\n-+\n-When pathspec is given to 'git stash push', the new stash entry records the\n-modified states only for the files that match the pathspec.  The index\n-entries and working tree files are then rolled back to the state in\n-HEAD only for these files, too, leaving files that do not match the\n-pathspec intact.\n-+\n-If the `--keep-index` option is used, all changes already added to the\n-index are left intact.\n-+\n-If the `--include-untracked` option is used, all untracked files are also\n-stashed and then cleaned up with `git clean`, leaving the working directory\n-in a very clean state. If the `--all` option is used instead then the\n-ignored files are stashed and cleaned in addition to the untracked files.\n-+\n-With `--patch`, you can interactively select hunks from the diff\n-between HEAD and the working tree to be stashed.  The stash entry is\n-constructed such that its index state is the same as the index state\n-of your repository, and its worktree contains only the changes you\n-selected interactively.  The selected changes are then rolled back\n-from your worktree. See the ``Interactive Mode'' section of\n-linkgit:git-add[1] to learn how to operate the `--patch` mode.\n-+\n-The `--patch` option implies `--keep-index`.  You can use\n-`--no-keep-index` to override this.\n \n save [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [<message>]::\n \n@@ -128,14 +103,6 @@ pop [--index] [-q|--quiet] [<stash>]::\n Applying the state can fail with conflicts; in this case, it is not\n removed from the stash list. You need to resolve the conflicts by hand\n and call `git stash drop` manually afterwards.\n-+\n-If the `--index` option is used, then tries to reinstate not only the working\n-tree's changes, but also the index's ones. However, this can fail, when you\n-have conflicts (which are stored in the index, where you therefore can no\n-longer apply the changes as they were originally).\n-+\n-When no `<stash>` is given, `stash@{0}` is assumed, otherwise `<stash>` must\n-be a reference of the form `stash@{<revision>}`.\n \n apply [--index] [-q|--quiet] [<stash>]::\n \n@@ -185,6 +152,41 @@ store::\n \treflog.  This is intended to be useful for scripts.  It is\n \tprobably not the command you want to use; see \"push\" above.\n \n+If the `--all` option is used instead then the\n+ignored files are stashed and cleaned in addition to the untracked files.\n+\n+If the `--include-untracked` option is used, all untracked files are also\n+stashed and then cleaned up with `git clean`, leaving the working directory\n+in a very clean state.\n+\n+If the `--index` option is used, then tries to reinstate not only the working\n+tree's changes, but also the index's ones. However, this can fail, when you\n+have conflicts (which are stored in the index, where you therefore can no\n+longer apply the changes as they were originally).\n+\n+If the `--keep-index` option is used, all changes already added to the\n+index are left intact.\n+\n+With `--patch`, you can interactively select hunks from the diff\n+between HEAD and the working tree to be stashed.  The stash entry is\n+constructed such that its index state is the same as the index state\n+of your repository, and its worktree contains only the changes you\n+selected interactively.  The selected changes are then rolled back\n+from your worktree. See the ``Interactive Mode'' section of\n+linkgit:git-add[1] to learn how to operate the `--patch` mode.\n++\n+The `--patch` option implies `--keep-index`.  You can use \n+`--no-keep-index` to override this.\n+\n+When pathspec is given to 'git stash push', the new stash entry records the\n+modified states only for the files that match the pathspec.  The index\n+entries and working tree files are then rolled back to the state in\n+HEAD only for these files, too, leaving files that do not match the\n+pathspec intact.\n+\n+When no `<stash>` is given, `stash@{0}` is assumed, otherwise `<stash>` must\n+be a reference of the form `stash@{<revision>}`.\n+\n DISCUSSION\n ----------\n \n-- \ngitgitgadget\n\n"},{"id":"391455","messageId":"2e8c8ad8158aae391b485394868a8c2a48d5118d.1581345948.git.gitgitgadget@gmail.com","threadId":"52648","inReplyTo":"pull.530.v2.git.1581345948.gitgitgadget@gmail.com","subject":"[PATCH v2 1/8] doc: rm: synchronize <pathspec> description","fromName":"Alexandr Miloslavskiy via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-02-10T14:45:40Z","receivedAt":"2020-02-10T14:45:58Z","isPatch":true,"sender":{"key":"alexandr.miloslavskiy@syntevo.com","avatar":null},"body":"From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n\nThis patch continues the effort that is already applied to\n`git commit`, `git reset`, `git checkout` etc.\n\n1) Changed outdated descriptions to mention pathspec instead.\n2) Added reference to 'linkgit:gitglossary[7]'.\n3) Removed content that merely repeated gitglossary.\n4) Merged the remainder of \"discussion\" into `<patchspec>`.\n\nSigned-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n---\n Documentation/git-rm.txt | 50 +++++++++++++++++-----------------------\n 1 file changed, 21 insertions(+), 29 deletions(-)\n\ndiff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt\nindex b5c46223c4..e02a08e5ef 100644\n--- a/Documentation/git-rm.txt\n+++ b/Documentation/git-rm.txt\n@@ -8,16 +8,16 @@ git-rm - Remove files from the working tree and from the index\n SYNOPSIS\n --------\n [verse]\n-'git rm' [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] <file>...\n+'git rm' [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] <pathspec>...\n \n DESCRIPTION\n -----------\n-Remove files from the index, or from the working tree and the index.\n-`git rm` will not remove a file from just your working directory.\n-(There is no option to remove a file only from the working tree\n-and yet keep it in the index; use `/bin/rm` if you want to do that.)\n-The files being removed have to be identical to the tip of the branch,\n-and no updates to their contents can be staged in the index,\n+Remove files matching pathspec from the index, or from the working tree\n+and the index. `git rm` will not remove a file from just your working\n+directory. (There is no option to remove a file only from the working\n+tree and yet keep it in the index; use `/bin/rm` if you want to do\n+that.) The files being removed have to be identical to the tip of the\n+branch, and no updates to their contents can be staged in the index,\n though that default behavior can be overridden with the `-f` option.\n When `--cached` is given, the staged content has to\n match either the tip of the branch or the file on disk,\n@@ -26,15 +26,20 @@ allowing the file to be removed from just the index.\n \n OPTIONS\n -------\n-<file>...::\n-\tFiles to remove.  Fileglobs (e.g. `*.c`) can be given to\n-\tremove all matching files.  If you want Git to expand\n-\tfile glob characters, you may need to shell-escape them.\n-\tA leading directory name\n-\t(e.g. `dir` to remove `dir/file1` and `dir/file2`) can be\n-\tgiven to remove all files in the directory, and recursively\n-\tall sub-directories,\n-\tbut this requires the `-r` option to be explicitly given.\n+<pathspec>...::\n+\tFiles to remove.  A leading directory name (e.g. `dir` to remove\n+\t`dir/file1` and `dir/file2`) can be given to remove all files in\n+\tthe directory, and recursively all sub-directories, but this\n+\trequires the `-r` option to be explicitly given.\n++\n+The command removes only the paths that are known to Git.\n++\n+File globbing matches across directory boundaries.  Thus, given two\n+directories `d` and `d2`, there is a difference between using\n+`git rm 'd*'` and `git rm 'd/*'`, as the former will also remove all\n+of directory `d2`.\n++\n+For more details, see the 'pathspec' entry in linkgit:gitglossary[7].\n \n -f::\n --force::\n@@ -69,19 +74,6 @@ OPTIONS\n \tfor each file removed. This option suppresses that output.\n \n \n-DISCUSSION\n-----------\n-\n-The <file> list given to the command can be exact pathnames,\n-file glob patterns, or leading directory names.  The command\n-removes only the paths that are known to Git.  Giving the name of\n-a file that you have not told Git about does not remove that file.\n-\n-File globbing matches across directory boundaries.  Thus, given\n-two directories `d` and `d2`, there is a difference between\n-using `git rm 'd*'` and `git rm 'd/*'`, as the former will\n-also remove all of directory `d2`.\n-\n REMOVING FILES THAT HAVE DISAPPEARED FROM THE FILESYSTEM\n --------------------------------------------------------\n There is no option for `git rm` to remove from the index only\n-- \ngitgitgadget\n\n"},{"id":"391457","messageId":"pull.530.v2.git.1581345948.gitgitgadget@gmail.com","threadId":"52648","inReplyTo":"pull.530.git.1579190965.gitgitgadget@gmail.com","subject":"[PATCH v2 0/8] Support --pathspec-from-file in rm, stash","fromName":"Alexandr Miloslavskiy via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-02-10T14:45:39Z","receivedAt":"2020-02-10T14:45:59Z","isPatch":true,"sender":{"key":"alexandr.miloslavskiy@syntevo.com","avatar":null},"body":"Changes since V1\n------------------\nSome polishing based on code review in V1\n1) Improved error message for the case where pathspec is not given to `git rm`\n2) Removed explicit variable initialization to 0 / NULL\n3) Polishing in docs for `git stash`\n\n------------------\nThis series continues the effort to support `--pathspec-from-file`\nin various git commands. Series already in `master`: [1][2]\n\nCc'ing Paul-Sebastian Ungureanu because I touched his git stash code.\n\n[1] https://public-inbox.org/git/pull.445.git.1572895605.gitgitgadget@gmail.com/\n[2] https://lore.kernel.org/git/pull.490.git.1576161385.gitgitgadget@gmail.com/\n\nAlexandr Miloslavskiy (8):\n  doc: rm: synchronize <pathspec> description\n  rm: support the --pathspec-from-file option\n  doc: stash: split options from description (1)\n  doc: stash: split options from description (2)\n  doc: stash: document more options\n  doc: stash: synchronize <pathspec> description\n  stash: eliminate crude option parsing\n  stash push: support the --pathspec-from-file option\n\n Documentation/git-rm.txt       |  61 +++++++-------\n Documentation/git-stash.txt    | 144 +++++++++++++++++++++++----------\n builtin/rm.c                   |  28 +++++--\n builtin/stash.c                |  79 +++++++++---------\n t/t3601-rm-pathspec-file.sh    |  79 ++++++++++++++++++\n t/t3903-stash.sh               |   5 ++\n t/t3909-stash-pathspec-file.sh | 100 +++++++++++++++++++++++\n 7 files changed, 381 insertions(+), 115 deletions(-)\n create mode 100755 t/t3601-rm-pathspec-file.sh\n create mode 100755 t/t3909-stash-pathspec-file.sh\n\n\nbase-commit: de93cc14ab7e8db7645d8dbe4fd2603f76d5851f\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-530%2FSyntevoAlex%2F%230207(git)_pathspec_from_file_3-v2\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-530/SyntevoAlex/#0207(git)_pathspec_from_file_3-v2\nPull-Request: https://github.com/gitgitgadget/git/pull/530\n\nRange-diff vs v1:\n\n 1:  23387f8391 = 1:  2e8c8ad815 doc: rm: synchronize <pathspec> description\n 2:  5611e3ae32 ! 2:  7ccbab52e5 rm: support the --pathspec-from-file option\n     @@ -64,8 +64,8 @@\n       \n       static int show_only = 0, force = 0, index_only = 0, recursive = 0, quiet = 0;\n      -static int ignore_unmatch = 0;\n     -+static int ignore_unmatch = 0, pathspec_file_nul = 0;\n     -+static char *pathspec_from_file = NULL;\n     ++static int ignore_unmatch = 0, pathspec_file_nul;\n     ++static char *pathspec_from_file;\n       \n       static struct option builtin_rm_options[] = {\n       \tOPT__DRY_RUN(&show_only, N_(\"dry run\")),\n     @@ -101,7 +101,7 @@\n      +\t}\n      +\n      +\tif (!pathspec.nr)\n     -+\t\tdie(_(\"Nothing specified, nothing removed\"));\n     ++\t\tdie(_(\"No pathspec was given. Which files should I remove?\"));\n       \n       \tif (!index_only)\n       \t\tsetup_work_tree();\n     @@ -196,7 +196,7 @@\n      +\t\n      +\t>empty_list &&\n      +\ttest_must_fail git rm --pathspec-from-file=empty_list 2>err &&\n     -+\ttest_i18ngrep -e \"Nothing specified, nothing removed\" err\n     ++\ttest_i18ngrep -e \"No pathspec was given. Which files should I remove?\" err\n      +'\n      +\n      +test_done\n 3:  0824bba210 = 3:  8c212fc0ed doc: stash: split options from description (1)\n 4:  708363241f ! 4:  db3a96720c doc: stash: split options from description (2)\n     @@ -3,17 +3,28 @@\n          doc: stash: split options from description (2)\n      \n          Together with the previous patch, this brings docs for `git stash` to\n     -    the common layout used for most other commands (see for example docs for\n     -    `git add`, `git commit`, `git checkout`, `git reset`) where all options\n     -    are documented in a separate list.\n     +    the common layout used for most other commands (see for example docs\n     +    for `git add`, `git commit`, `git checkout`, `git reset`) where all\n     +    options are documented in a separate list.\n      \n     -    I have decided to use alphabetical sorting in the list of options. Other\n     -    docs often sort in order of appearance or order of importance, but in\n     -    this case it wouldn't be easy to read the list where options from\n     -    multiple sub-commands are mixed together.\n     +    After some thinking and having a look at docs for `git svn` and\n     +    `git `submodule`, I have arrived at following conclusions:\n     +      * Options should be described in a list rather then text to\n     +        facilitate lookup for user.\n     +      * Single list is better then multiple lists because it avoids\n     +        copy&pasting descriptions between subcommands (or, without\n     +        copy&pasting, user will have to look up missing options in other\n     +        subcommands).\n     +      * As a consequence, commands section should only give brief info and\n     +        list possible options. Since options have good enough names, user\n     +            will only need to look up the \"interesting\" options.\n     +      * Every option should list which subcommands support it.\n      \n     -    There is some text editing done to make old descriptions better fit into\n     -    the list-style format.\n     +    I have decided to use alphabetical sorting in the list of options to\n     +    facilitate lookup for user.\n     +\n     +    There is some text editing done to make old descriptions better fit\n     +    into the list-style format.\n      \n          Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n      \n     @@ -26,10 +37,40 @@\n       \n      -OPTIONS\n      --------\n     --\n     ++COMMANDS\n     ++--------\n     + \n       push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--] [<pathspec>...]::\n       \n     - \tSave your local modifications to a new 'stash entry' and roll them\n     +@@\n     + \n     + \tShow the changes recorded in the stash entry as a diff between the\n     + \tstashed contents and the commit back when the stash entry was first\n     +-\tcreated. When no `<stash>` is given, it shows the latest one.\n     ++\tcreated.\n     + \tBy default, the command shows the diffstat, but it will accept any\n     + \tformat known to 'git diff' (e.g., `git stash show -p stash@{1}`\n     + \tto view the second most recent entry in patch form).\n     +@@\n     + \tthe commit at which the `<stash>` was originally created, applies the\n     + \tchanges recorded in `<stash>` to the new working tree and index.\n     + \tIf that succeeds, and `<stash>` is a reference of the form\n     +-\t`stash@{<revision>}`, it then drops the `<stash>`. When no `<stash>`\n     +-\tis given, applies the latest one.\n     ++\t`stash@{<revision>}`, it then drops the `<stash>`.\n     + +\n     + This is useful if the branch on which you ran `git stash push` has\n     + changed enough that `git stash apply` fails due to conflicts. Since\n     +@@\n     + drop [-q|--quiet] [<stash>]::\n     + \n     + \tRemove a single stash entry from the list of stash entries.\n     +-\tWhen no `<stash>` is given, it removes the latest one.\n     +-\ti.e. `stash@{0}`, otherwise `<stash>` must be a valid stash\n     +-\tlog reference of the form `stash@{<revision>}`.\n     + \n     + create::\n     + \n      @@\n       \treflog.  This is intended to be useful for scripts.  It is\n       \tprobably not the command you want to use; see \"push\" above.\n     @@ -40,15 +81,43 @@\n      -If the `--include-untracked` option is used, all untracked files are also\n      -stashed and then cleaned up with `git clean`, leaving the working directory\n      -in a very clean state.\n     --\n     ++OPTIONS\n     ++-------\n     ++-a::\n     ++--all::\n     ++\tThis option is only valid for `push` and `save` commands.\n     +++\n     ++All ignored and untracked files are also stashed and then cleaned\n     ++up with `git clean`.\n     + \n      -If the `--index` option is used, then tries to reinstate not only the working\n      -tree's changes, but also the index's ones. However, this can fail, when you\n      -have conflicts (which are stored in the index, where you therefore can no\n      -longer apply the changes as they were originally).\n     --\n     ++-u::\n     ++--include-untracked::\n     ++\tThis option is only valid for `push` and `save` commands.\n     +++\n     ++All untracked files are also stashed and then cleaned up with\n     ++`git clean`.\n     + \n      -If the `--keep-index` option is used, all changes already added to the\n      -index are left intact.\n     --\n     ++--index::\n     ++\tThis option is only valid for `pop` and `apply` commands.\n     +++\n     ++Tries to reinstate not only the working tree's changes, but also\n     ++the index's ones. However, this can fail, when you have conflicts\n     ++(which are stored in the index, where you therefore can no longer\n     ++apply the changes as they were originally).\n     ++\n     ++-k::\n     ++--keep-index::\n     ++--no-keep-index::\n     ++\tThis option is only valid for `push` and `save` commands.\n     +++\n     ++All changes already added to the index are left intact.\n     + \n      -With `--patch`, you can interactively select hunks from the diff\n      -between HEAD and the working tree to be stashed.  The stash entry is\n      -constructed such that its index state is the same as the index state\n     @@ -56,38 +125,17 @@\n      -selected interactively.  The selected changes are then rolled back\n      -from your worktree. See the ``Interactive Mode'' section of\n      -linkgit:git-add[1] to learn how to operate the `--patch` mode.\n     -+OPTIONS\n     -+-------\n     -+-a::\n     -+--all::\n     -+\tAll ignored and untracked files are also stashed and then cleaned\n     -+\tup with `git clean`.\n     -+\n     -+-u::\n     -+--include-untracked::\n     -+\tAll untracked files are also stashed and then cleaned up with\n     -+\t`git clean`.\n     -+\n     -+--index::\n     -+\tTries to reinstate not only the working tree's changes, but also\n     -+\tthe index's ones. However, this can fail, when you have conflicts\n     -+\t(which are stored in the index, where you therefore can no longer\n     -+\tapply the changes as they were originally).\n     -+\n     -+-k::\n     -+--keep-index::\n     -+--no-keep-index::\n     -+\tAll changes already added to the index are left intact.\n     -+\n      +-p::\n      +--patch::\n     -+\tInteractively select hunks from the diff between HEAD and the\n     -+\tworking tree to be stashed.  The stash entry is constructed such\n     -+\tthat its index state is the same as the index state of your\n     -+\trepository, and its worktree contains only the changes you selected\n     -+\tinteractively.  The selected changes are then rolled back from your\n     -+\tworktree. See the ``Interactive Mode'' section of linkgit:git-add[1]\n     -+\tto learn how to operate the `--patch` mode.\n     ++\tThis option is only valid for `push` and `save` commands.\n     +++\n     ++Interactively select hunks from the diff between HEAD and the\n     ++working tree to be stashed.  The stash entry is constructed such\n     ++that its index state is the same as the index state of your\n     ++repository, and its worktree contains only the changes you selected\n     ++interactively.  The selected changes are then rolled back from your\n     ++worktree. See the ``Interactive Mode'' section of linkgit:git-add[1]\n     ++to learn how to operate the `--patch` mode.\n       +\n       The `--patch` option implies `--keep-index`.  You can use \n       `--no-keep-index` to override this.\n     @@ -97,17 +145,23 @@\n      -entries and working tree files are then rolled back to the state in\n      -HEAD only for these files, too, leaving files that do not match the\n      -pathspec intact.\n     -+<pathspec>...::\n     -+\tThe new stash entry records the modified states only for the files\n     -+\tthat match the pathspec.  The index entries and working tree files\n     -+\tare then rolled back to the state in HEAD only for these files,\n     -+\ttoo, leaving files that do not match the pathspec intact.\n     - \n     +-\n      -When no `<stash>` is given, `stash@{0}` is assumed, otherwise `<stash>` must\n      -be a reference of the form `stash@{<revision>}`.\n     ++<pathspec>...::\n     ++\tThis option is only valid for `push` command.\n     +++\n     ++The new stash entry records the modified states only for the files\n     ++that match the pathspec.  The index entries and working tree files\n     ++are then rolled back to the state in HEAD only for these files,\n     ++too, leaving files that do not match the pathspec intact.\n     ++\n      +<stash>::\n     -+\tA reference of the form `stash@{<revision>}`. When no `<stash>` is\n     -+\tgiven, the latest stash is assumed (that is, `stash@{0}`).\n     ++\tThis option is only valid for `apply`, `branch`, `drop`, `pop`,\n     ++\t`show` commands.\n     +++\n     ++A reference of the form `stash@{<revision>}`. When no `<stash>` is\n     ++given, the latest stash is assumed (that is, `stash@{0}`).\n       \n       DISCUSSION\n       ----------\n 5:  8a5f2dbe9e ! 5:  f91ec08b47 doc: stash: document more options\n     @@ -13,11 +13,16 @@\n       \n      +-q::\n      +--quiet::\n     -+\tQuiet, suppress feedback messages.\n     ++\tThis option is only valid for `apply`, `drop`, `pop`, `push`,\n     ++\t`save`, `store` commands.\n     +++\n     ++Quiet, suppress feedback messages.\n      +\n      +\\--::\n     -+\tSeparates pathspec from options for disambiguation purposes.\n     ++\tThis option is only valid for `push` command.\n     +++\n     ++Separates pathspec from options for disambiguation purposes.\n      +\n       <pathspec>...::\n     - \tThe new stash entry records the modified states only for the files\n     - \tthat match the pathspec.  The index entries and working tree files\n     + \tThis option is only valid for `push` command.\n     + +\n 6:  5e17a0c470 ! 6:  04e2fd5865 doc: stash: synchronize <pathspec> description\n     @@ -30,11 +30,11 @@\n       \tmessage.\n       \n      @@\n     - \tthat match the pathspec.  The index entries and working tree files\n     - \tare then rolled back to the state in HEAD only for these files,\n     - \ttoo, leaving files that do not match the pathspec intact.\n     + that match the pathspec.  The index entries and working tree files\n     + are then rolled back to the state in HEAD only for these files,\n     + too, leaving files that do not match the pathspec intact.\n      ++\n      +For more details, see the 'pathspec' entry in linkgit:gitglossary[7].\n       \n       <stash>::\n     - \tA reference of the form `stash@{<revision>}`. When no `<stash>` is\n     + \tThis option is only valid for `apply`, `branch`, `drop`, `pop`,\n 7:  7a8d36d49f = 7:  0558cbbe38 stash: eliminate crude option parsing\n 8:  721410233b ! 8:  0c6f28dc68 stash push: support the --pathspec-from-file option\n     @@ -22,8 +22,8 @@\n       'git stash' clear\n       'git stash' create [<message>]\n      @@\n     - is also possible). Stashes may also be referenced by specifying just the\n     - stash index (e.g. the integer `n` is equivalent to `stash@{n}`).\n     + COMMANDS\n     + --------\n       \n      -push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--] [<pathspec>...]::\n      +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>...]::\n     @@ -35,21 +35,25 @@\n       `--no-keep-index` to override this.\n       \n      +--pathspec-from-file=<file>::\n     -+\tPathspec is passed in `<file>` instead of commandline args. If\n     -+\t`<file>` is exactly `-` then standard input is used. Pathspec\n     -+\telements are separated by LF or CR/LF. Pathspec elements can be\n     -+\tquoted as explained for the configuration variable `core.quotePath`\n     -+\t(see linkgit:git-config[1]). See also `--pathspec-file-nul` and\n     -+\tglobal `--literal-pathspecs`.\n     ++\tThis option is only valid for `push` command.\n     +++\n     ++Pathspec is passed in `<file>` instead of commandline args. If\n     ++`<file>` is exactly `-` then standard input is used. Pathspec\n     ++elements are separated by LF or CR/LF. Pathspec elements can be\n     ++quoted as explained for the configuration variable `core.quotePath`\n     ++(see linkgit:git-config[1]). See also `--pathspec-file-nul` and\n     ++global `--literal-pathspecs`.\n      +\n      +--pathspec-file-nul::\n     -+\tOnly meaningful with `--pathspec-from-file`. Pathspec elements are\n     -+\tseparated with NUL character and all other characters are taken\n     -+\tliterally (including newlines and quotes).\n     ++\tThis option is only valid for `push` command.\n     +++\n     ++Only meaningful with `--pathspec-from-file`. Pathspec elements are\n     ++separated with NUL character and all other characters are taken\n     ++literally (including newlines and quotes).\n      +\n       -q::\n       --quiet::\n     - \tQuiet, suppress feedback messages.\n     + \tThis option is only valid for `apply`, `drop`, `pop`, `push`,\n      \n       diff --git a/builtin/stash.c b/builtin/stash.c\n       --- a/builtin/stash.c\n\n-- \ngitgitgadget\n"},{"id":"391456","messageId":"7ccbab52e51423a9ba74c0cab77448ceabb9dcdc.1581345948.git.gitgitgadget@gmail.com","threadId":"52648","inReplyTo":"pull.530.v2.git.1581345948.gitgitgadget@gmail.com","subject":"[PATCH v2 2/8] rm: support the --pathspec-from-file option","fromName":"Alexandr Miloslavskiy via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-02-10T14:45:41Z","receivedAt":"2020-02-10T14:46:00Z","isPatch":true,"sender":{"key":"alexandr.miloslavskiy@syntevo.com","avatar":null},"body":"From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n\nDecisions taken for simplicity:\n1) It is not allowed to pass pathspec in both args and file.\n\n`if (!argc)` block was adapted to work with --pathspec-from-file. For\nthat, I also had to parse pathspec earlier. Now it happens before\n`read_cache()` / `hold_locked_index()` / `setup_work_tree()`, which\nsounds fine to me.\n\nIn case of empty pathspec, there is now a clear error message instead\nof showing usage. As a consequence, exit code has also changed. Judging\nfrom [1] it doesn't seem that showing usage in this case was important\n(according to commit message, it was to avoid segfault), and it doesn't\nfit into how other commands react to empty pathspec. Finally, the new\nerror message is easier to understand.\n\n[1] Commit 7612a1ef (\"git-rm: honor -n flag\" 2006-06-09)\n\nSigned-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n---\n Documentation/git-rm.txt    | 17 +++++++-\n builtin/rm.c                | 28 ++++++++++---\n t/t3601-rm-pathspec-file.sh | 79 +++++++++++++++++++++++++++++++++++++\n 3 files changed, 117 insertions(+), 7 deletions(-)\n create mode 100755 t/t3601-rm-pathspec-file.sh\n\ndiff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt\nindex e02a08e5ef..ab750367fd 100644\n--- a/Documentation/git-rm.txt\n+++ b/Documentation/git-rm.txt\n@@ -8,7 +8,9 @@ git-rm - Remove files from the working tree and from the index\n SYNOPSIS\n --------\n [verse]\n-'git rm' [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] <pathspec>...\n+'git rm' [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch]\n+\t  [--quiet] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n+\t  [--] [<pathspec>...]\n \n DESCRIPTION\n -----------\n@@ -73,6 +75,19 @@ For more details, see the 'pathspec' entry in linkgit:gitglossary[7].\n \t`git rm` normally outputs one line (in the form of an `rm` command)\n \tfor each file removed. This option suppresses that output.\n \n+--pathspec-from-file=<file>::\n+\tPathspec is passed in `<file>` instead of commandline args. If\n+\t`<file>` is exactly `-` then standard input is used. Pathspec\n+\telements are separated by LF or CR/LF. Pathspec elements can be\n+\tquoted as explained for the configuration variable `core.quotePath`\n+\t(see linkgit:git-config[1]). See also `--pathspec-file-nul` and\n+\tglobal `--literal-pathspecs`.\n+\n+--pathspec-file-nul::\n+\tOnly meaningful with `--pathspec-from-file`. Pathspec elements are\n+\tseparated with NUL character and all other characters are taken\n+\tliterally (including newlines and quotes).\n+\n \n REMOVING FILES THAT HAVE DISAPPEARED FROM THE FILESYSTEM\n --------------------------------------------------------\ndiff --git a/builtin/rm.c b/builtin/rm.c\nindex 19ce95a901..4858631e0f 100644\n--- a/builtin/rm.c\n+++ b/builtin/rm.c\n@@ -235,7 +235,8 @@ static int check_local_mod(struct object_id *head, int index_only)\n }\n \n static int show_only = 0, force = 0, index_only = 0, recursive = 0, quiet = 0;\n-static int ignore_unmatch = 0;\n+static int ignore_unmatch = 0, pathspec_file_nul;\n+static char *pathspec_from_file;\n \n static struct option builtin_rm_options[] = {\n \tOPT__DRY_RUN(&show_only, N_(\"dry run\")),\n@@ -245,6 +246,8 @@ static struct option builtin_rm_options[] = {\n \tOPT_BOOL('r', NULL,             &recursive,  N_(\"allow recursive removal\")),\n \tOPT_BOOL( 0 , \"ignore-unmatch\", &ignore_unmatch,\n \t\t\t\tN_(\"exit with a zero status even if nothing matched\")),\n+\tOPT_PATHSPEC_FROM_FILE(&pathspec_from_file),\n+\tOPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),\n \tOPT_END(),\n };\n \n@@ -259,8 +262,24 @@ int cmd_rm(int argc, const char **argv, const char *prefix)\n \n \targc = parse_options(argc, argv, prefix, builtin_rm_options,\n \t\t\t     builtin_rm_usage, 0);\n-\tif (!argc)\n-\t\tusage_with_options(builtin_rm_usage, builtin_rm_options);\n+\n+\tparse_pathspec(&pathspec, 0,\n+\t\t       PATHSPEC_PREFER_CWD,\n+\t\t       prefix, argv);\n+\n+\tif (pathspec_from_file) {\n+\t\tif (pathspec.nr)\n+\t\t\tdie(_(\"--pathspec-from-file is incompatible with pathspec arguments\"));\n+\n+\t\tparse_pathspec_file(&pathspec, 0,\n+\t\t\t\t    PATHSPEC_PREFER_CWD,\n+\t\t\t\t    prefix, pathspec_from_file, pathspec_file_nul);\n+\t} else if (pathspec_file_nul) {\n+\t\tdie(_(\"--pathspec-file-nul requires --pathspec-from-file\"));\n+\t}\n+\n+\tif (!pathspec.nr)\n+\t\tdie(_(\"No pathspec was given. Which files should I remove?\"));\n \n \tif (!index_only)\n \t\tsetup_work_tree();\n@@ -270,9 +289,6 @@ int cmd_rm(int argc, const char **argv, const char *prefix)\n \tif (read_cache() < 0)\n \t\tdie(_(\"index file corrupt\"));\n \n-\tparse_pathspec(&pathspec, 0,\n-\t\t       PATHSPEC_PREFER_CWD,\n-\t\t       prefix, argv);\n \trefresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &pathspec, NULL, NULL);\n \n \tseen = xcalloc(pathspec.nr, 1);\ndiff --git a/t/t3601-rm-pathspec-file.sh b/t/t3601-rm-pathspec-file.sh\nnew file mode 100755\nindex 0000000000..4542a0f02f\n--- /dev/null\n+++ b/t/t3601-rm-pathspec-file.sh\n@@ -0,0 +1,79 @@\n+#!/bin/sh\n+\n+test_description='rm --pathspec-from-file'\n+\n+. ./test-lib.sh\n+\n+test_tick\n+\n+test_expect_success setup '\n+\techo A >fileA.t &&\n+\techo B >fileB.t &&\n+\techo C >fileC.t &&\n+\techo D >fileD.t &&\n+\tgit add fileA.t fileB.t fileC.t fileD.t &&\n+\tgit commit -m \"files\" &&\n+\t\n+\tgit tag checkpoint\n+'\n+\n+restore_checkpoint () {\n+\tgit reset --hard checkpoint\n+}\n+\n+verify_expect () {\n+\tgit status --porcelain --untracked-files=no -- fileA.t fileB.t fileC.t fileD.t >actual &&\n+\ttest_cmp expect actual\n+}\n+\n+test_expect_success 'simplest' '\n+\trestore_checkpoint &&\n+\n+\tcat >expect <<-\\EOF &&\n+\tD  fileA.t\n+\tEOF\n+\n+\techo fileA.t | git rm --pathspec-from-file=- &&\n+\tverify_expect\n+'\n+\n+test_expect_success '--pathspec-file-nul' '\n+\trestore_checkpoint &&\n+\n+\tcat >expect <<-\\EOF &&\n+\tD  fileA.t\n+\tD  fileB.t\n+\tEOF\n+\n+\tprintf \"fileA.t\\0fileB.t\\0\" | git rm --pathspec-from-file=- --pathspec-file-nul &&\n+\tverify_expect\n+'\n+\n+test_expect_success 'only touches what was listed' '\n+\trestore_checkpoint &&\n+\n+\tcat >expect <<-\\EOF &&\n+\tD  fileB.t\n+\tD  fileC.t\n+\tEOF\n+\n+\tprintf \"fileB.t\\nfileC.t\\n\" | git rm --pathspec-from-file=- &&\n+\tverify_expect\n+'\n+\n+test_expect_success 'error conditions' '\n+\trestore_checkpoint &&\n+\techo fileA.t >list &&\n+\n+\ttest_must_fail git rm --pathspec-from-file=list -- fileA.t 2>err &&\n+\ttest_i18ngrep -e \"--pathspec-from-file is incompatible with pathspec arguments\" err &&\n+\n+\ttest_must_fail git rm --pathspec-file-nul 2>err &&\n+\ttest_i18ngrep -e \"--pathspec-file-nul requires --pathspec-from-file\" err &&\n+\t\n+\t>empty_list &&\n+\ttest_must_fail git rm --pathspec-from-file=empty_list 2>err &&\n+\ttest_i18ngrep -e \"No pathspec was given. Which files should I remove?\" err\n+'\n+\n+test_done\n-- \ngitgitgadget\n\n"},{"id":"391461","messageId":"db3a96720ce94cc93c8bcefe08d3340ae4e08cae.1581345948.git.gitgitgadget@gmail.com","threadId":"52648","inReplyTo":"pull.530.v2.git.1581345948.gitgitgadget@gmail.com","subject":"[PATCH v2 4/8] doc: stash: split options from description (2)","fromName":"Alexandr Miloslavskiy via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-02-10T14:45:43Z","receivedAt":"2020-02-10T14:46:01Z","isPatch":true,"sender":{"key":"alexandr.miloslavskiy@syntevo.com","avatar":null},"body":"From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n\nTogether with the previous patch, this brings docs for `git stash` to\nthe common layout used for most other commands (see for example docs\nfor `git add`, `git commit`, `git checkout`, `git reset`) where all\noptions are documented in a separate list.\n\nAfter some thinking and having a look at docs for `git svn` and\n`git `submodule`, I have arrived at following conclusions:\n  * Options should be described in a list rather then text to\n    facilitate lookup for user.\n  * Single list is better then multiple lists because it avoids\n    copy&pasting descriptions between subcommands (or, without\n    copy&pasting, user will have to look up missing options in other\n    subcommands).\n  * As a consequence, commands section should only give brief info and\n    list possible options. Since options have good enough names, user\n\twill only need to look up the \"interesting\" options.\n  * Every option should list which subcommands support it.\n\nI have decided to use alphabetical sorting in the list of options to\nfacilitate lookup for user.\n\nThere is some text editing done to make old descriptions better fit\ninto the list-style format.\n\nSigned-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n---\n Documentation/git-stash.txt | 92 +++++++++++++++++++++++--------------\n 1 file changed, 57 insertions(+), 35 deletions(-)\n\ndiff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt\nindex 2dedc21997..c1c16623cb 100644\n--- a/Documentation/git-stash.txt\n+++ b/Documentation/git-stash.txt\n@@ -43,8 +43,8 @@ created stash, `stash@{1}` is the one before it, `stash@{2.hours.ago}`\n is also possible). Stashes may also be referenced by specifying just the\n stash index (e.g. the integer `n` is equivalent to `stash@{n}`).\n \n-OPTIONS\n--------\n+COMMANDS\n+--------\n \n push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--] [<pathspec>...]::\n \n@@ -86,7 +86,7 @@ show [<options>] [<stash>]::\n \n \tShow the changes recorded in the stash entry as a diff between the\n \tstashed contents and the commit back when the stash entry was first\n-\tcreated. When no `<stash>` is given, it shows the latest one.\n+\tcreated.\n \tBy default, the command shows the diffstat, but it will accept any\n \tformat known to 'git diff' (e.g., `git stash show -p stash@{1}`\n \tto view the second most recent entry in patch form).\n@@ -116,8 +116,7 @@ branch <branchname> [<stash>]::\n \tthe commit at which the `<stash>` was originally created, applies the\n \tchanges recorded in `<stash>` to the new working tree and index.\n \tIf that succeeds, and `<stash>` is a reference of the form\n-\t`stash@{<revision>}`, it then drops the `<stash>`. When no `<stash>`\n-\tis given, applies the latest one.\n+\t`stash@{<revision>}`, it then drops the `<stash>`.\n +\n This is useful if the branch on which you ran `git stash push` has\n changed enough that `git stash apply` fails due to conflicts. Since\n@@ -133,9 +132,6 @@ clear::\n drop [-q|--quiet] [<stash>]::\n \n \tRemove a single stash entry from the list of stash entries.\n-\tWhen no `<stash>` is given, it removes the latest one.\n-\ti.e. `stash@{0}`, otherwise `<stash>` must be a valid stash\n-\tlog reference of the form `stash@{<revision>}`.\n \n create::\n \n@@ -152,40 +148,66 @@ store::\n \treflog.  This is intended to be useful for scripts.  It is\n \tprobably not the command you want to use; see \"push\" above.\n \n-If the `--all` option is used instead then the\n-ignored files are stashed and cleaned in addition to the untracked files.\n-\n-If the `--include-untracked` option is used, all untracked files are also\n-stashed and then cleaned up with `git clean`, leaving the working directory\n-in a very clean state.\n+OPTIONS\n+-------\n+-a::\n+--all::\n+\tThis option is only valid for `push` and `save` commands.\n++\n+All ignored and untracked files are also stashed and then cleaned\n+up with `git clean`.\n \n-If the `--index` option is used, then tries to reinstate not only the working\n-tree's changes, but also the index's ones. However, this can fail, when you\n-have conflicts (which are stored in the index, where you therefore can no\n-longer apply the changes as they were originally).\n+-u::\n+--include-untracked::\n+\tThis option is only valid for `push` and `save` commands.\n++\n+All untracked files are also stashed and then cleaned up with\n+`git clean`.\n \n-If the `--keep-index` option is used, all changes already added to the\n-index are left intact.\n+--index::\n+\tThis option is only valid for `pop` and `apply` commands.\n++\n+Tries to reinstate not only the working tree's changes, but also\n+the index's ones. However, this can fail, when you have conflicts\n+(which are stored in the index, where you therefore can no longer\n+apply the changes as they were originally).\n+\n+-k::\n+--keep-index::\n+--no-keep-index::\n+\tThis option is only valid for `push` and `save` commands.\n++\n+All changes already added to the index are left intact.\n \n-With `--patch`, you can interactively select hunks from the diff\n-between HEAD and the working tree to be stashed.  The stash entry is\n-constructed such that its index state is the same as the index state\n-of your repository, and its worktree contains only the changes you\n-selected interactively.  The selected changes are then rolled back\n-from your worktree. See the ``Interactive Mode'' section of\n-linkgit:git-add[1] to learn how to operate the `--patch` mode.\n+-p::\n+--patch::\n+\tThis option is only valid for `push` and `save` commands.\n++\n+Interactively select hunks from the diff between HEAD and the\n+working tree to be stashed.  The stash entry is constructed such\n+that its index state is the same as the index state of your\n+repository, and its worktree contains only the changes you selected\n+interactively.  The selected changes are then rolled back from your\n+worktree. See the ``Interactive Mode'' section of linkgit:git-add[1]\n+to learn how to operate the `--patch` mode.\n +\n The `--patch` option implies `--keep-index`.  You can use \n `--no-keep-index` to override this.\n \n-When pathspec is given to 'git stash push', the new stash entry records the\n-modified states only for the files that match the pathspec.  The index\n-entries and working tree files are then rolled back to the state in\n-HEAD only for these files, too, leaving files that do not match the\n-pathspec intact.\n-\n-When no `<stash>` is given, `stash@{0}` is assumed, otherwise `<stash>` must\n-be a reference of the form `stash@{<revision>}`.\n+<pathspec>...::\n+\tThis option is only valid for `push` command.\n++\n+The new stash entry records the modified states only for the files\n+that match the pathspec.  The index entries and working tree files\n+are then rolled back to the state in HEAD only for these files,\n+too, leaving files that do not match the pathspec intact.\n+\n+<stash>::\n+\tThis option is only valid for `apply`, `branch`, `drop`, `pop`,\n+\t`show` commands.\n++\n+A reference of the form `stash@{<revision>}`. When no `<stash>` is\n+given, the latest stash is assumed (that is, `stash@{0}`).\n \n DISCUSSION\n ----------\n-- \ngitgitgadget\n\n"},{"id":"391458","messageId":"04e2fd5865fa9bc36027e318977f0b217d4154ef.1581345948.git.gitgitgadget@gmail.com","threadId":"52648","inReplyTo":"pull.530.v2.git.1581345948.gitgitgadget@gmail.com","subject":"[PATCH v2 6/8] doc: stash: synchronize <pathspec> description","fromName":"Alexandr Miloslavskiy via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-02-10T14:45:45Z","receivedAt":"2020-02-10T14:46:02Z","isPatch":true,"sender":{"key":"alexandr.miloslavskiy@syntevo.com","avatar":null},"body":"From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n\nThis patch continues the effort that is already applied to\n`git commit`, `git reset`, `git checkout` etc.\n\n1) Added reference to 'linkgit:gitglossary[7]'.\n2) Fixed mentions of incorrectly plural \"pathspecs\".\n\nSigned-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n---\n Documentation/git-stash.txt | 6 ++++--\n 1 file changed, 4 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt\nindex 6dc0a5b0ee..52e64985bd 100644\n--- a/Documentation/git-stash.txt\n+++ b/Documentation/git-stash.txt\n@@ -56,13 +56,13 @@ push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q\n For quickly making a snapshot, you can omit \"push\".  In this mode,\n non-option arguments are not allowed to prevent a misspelled\n subcommand from making an unwanted stash entry.  The two exceptions to this\n-are `stash -p` which acts as alias for `stash push -p` and pathspecs,\n+are `stash -p` which acts as alias for `stash push -p` and pathspec elements,\n which are allowed after a double hyphen `--` for disambiguation.\n \n save [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [<message>]::\n \n \tThis option is deprecated in favour of 'git stash push'.  It\n-\tdiffers from \"stash push\" in that it cannot take pathspecs.\n+\tdiffers from \"stash push\" in that it cannot take pathspec.\n \tInstead, all non-option arguments are concatenated to form the stash\n \tmessage.\n \n@@ -213,6 +213,8 @@ The new stash entry records the modified states only for the files\n that match the pathspec.  The index entries and working tree files\n are then rolled back to the state in HEAD only for these files,\n too, leaving files that do not match the pathspec intact.\n++\n+For more details, see the 'pathspec' entry in linkgit:gitglossary[7].\n \n <stash>::\n \tThis option is only valid for `apply`, `branch`, `drop`, `pop`,\n-- \ngitgitgadget\n\n"},{"id":"391459","messageId":"0c6f28dc68d4ca9190f57d8b9cb56e0592ee27ff.1581345948.git.gitgitgadget@gmail.com","threadId":"52648","inReplyTo":"pull.530.v2.git.1581345948.gitgitgadget@gmail.com","subject":"[PATCH v2 8/8] stash push: support the --pathspec-from-file option","fromName":"Alexandr Miloslavskiy via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-02-10T14:45:47Z","receivedAt":"2020-02-10T14:46:04Z","isPatch":true,"sender":{"key":"alexandr.miloslavskiy@syntevo.com","avatar":null},"body":"From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n\nDecisions taken for simplicity:\n1) For now, `--pathspec-from-file` is declared incompatible with\n   `--patch`, even when <file> is not `-`. Such use case is not\n   really expected.\n2) It is not allowed to pass pathspec in both args and file.\n\nSigned-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n---\n Documentation/git-stash.txt    |  20 ++++++-\n builtin/stash.c                |  20 +++++++\n t/t3909-stash-pathspec-file.sh | 100 +++++++++++++++++++++++++++++++++\n 3 files changed, 139 insertions(+), 1 deletion(-)\n create mode 100755 t/t3909-stash-pathspec-file.sh\n\ndiff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt\nindex 52e64985bd..79949f8617 100644\n--- a/Documentation/git-stash.txt\n+++ b/Documentation/git-stash.txt\n@@ -15,6 +15,7 @@ SYNOPSIS\n 'git stash' branch <branchname> [<stash>]\n 'git stash' [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n \t     [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n+\t     [--pathspec-from-file=<file> [--pathspec-file-nul]]\n \t     [--] [<pathspec>...]]\n 'git stash' clear\n 'git stash' create [<message>]\n@@ -46,7 +47,7 @@ stash index (e.g. the integer `n` is equivalent to `stash@{n}`).\n COMMANDS\n --------\n \n-push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--] [<pathspec>...]::\n+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>...]::\n \n \tSave your local modifications to a new 'stash entry' and roll them\n \tback to HEAD (in the working tree and in the index).\n@@ -194,6 +195,23 @@ to learn how to operate the `--patch` mode.\n The `--patch` option implies `--keep-index`.  You can use \n `--no-keep-index` to override this.\n \n+--pathspec-from-file=<file>::\n+\tThis option is only valid for `push` command.\n++\n+Pathspec is passed in `<file>` instead of commandline args. If\n+`<file>` is exactly `-` then standard input is used. Pathspec\n+elements are separated by LF or CR/LF. Pathspec elements can be\n+quoted as explained for the configuration variable `core.quotePath`\n+(see linkgit:git-config[1]). See also `--pathspec-file-nul` and\n+global `--literal-pathspecs`.\n+\n+--pathspec-file-nul::\n+\tThis option is only valid for `push` command.\n++\n+Only meaningful with `--pathspec-from-file`. Pathspec elements are\n+separated with NUL character and all other characters are taken\n+literally (including newlines and quotes).\n+\n -q::\n --quiet::\n \tThis option is only valid for `apply`, `drop`, `pop`, `push`,\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex ed84ff2e16..78af6ce564 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -27,6 +27,7 @@ static const char * const git_stash_usage[] = {\n \tN_(\"git stash clear\"),\n \tN_(\"git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\\n\"\n \t   \"          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\\n\"\n+\t   \"          [--pathspec-from-file=<file> [--pathspec-file-nul]]\\n\"\n \t   \"          [--] [<pathspec>...]]\"),\n \tN_(\"git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\\n\"\n \t   \"          [-u|--include-untracked] [-a|--all] [<message>]\"),\n@@ -1459,7 +1460,9 @@ static int push_stash(int argc, const char **argv, const char *prefix,\n \tint patch_mode = 0;\n \tint include_untracked = 0;\n \tint quiet = 0;\n+\tint pathspec_file_nul = 0;\n \tconst char *stash_msg = NULL;\n+\tconst char *pathspec_from_file = NULL;\n \tstruct pathspec ps;\n \tstruct option options[] = {\n \t\tOPT_BOOL('k', \"keep-index\", &keep_index,\n@@ -1473,6 +1476,8 @@ static int push_stash(int argc, const char **argv, const char *prefix,\n \t\t\t    N_(\"include ignore files\"), 2),\n \t\tOPT_STRING('m', \"message\", &stash_msg, N_(\"message\"),\n \t\t\t   N_(\"stash message\")),\n+\t\tOPT_PATHSPEC_FROM_FILE(&pathspec_from_file),\n+\t\tOPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),\n \t\tOPT_END()\n \t};\n \n@@ -1495,6 +1500,21 @@ static int push_stash(int argc, const char **argv, const char *prefix,\n \n \tparse_pathspec(&ps, 0, PATHSPEC_PREFER_FULL | PATHSPEC_PREFIX_ORIGIN,\n \t\t       prefix, argv);\n+\n+\tif (pathspec_from_file) {\n+\t\tif (patch_mode)\n+\t\t\tdie(_(\"--pathspec-from-file is incompatible with --patch\"));\n+\n+\t\tif (ps.nr)\n+\t\t\tdie(_(\"--pathspec-from-file is incompatible with pathspec arguments\"));\n+\n+\t\tparse_pathspec_file(&ps, 0,\n+\t\t\t\t    PATHSPEC_PREFER_FULL | PATHSPEC_PREFIX_ORIGIN,\n+\t\t\t\t    prefix, pathspec_from_file, pathspec_file_nul);\n+\t} else if (pathspec_file_nul) {\n+\t\tdie(_(\"--pathspec-file-nul requires --pathspec-from-file\"));\n+\t}\n+\n \treturn do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode,\n \t\t\t     include_untracked);\n }\ndiff --git a/t/t3909-stash-pathspec-file.sh b/t/t3909-stash-pathspec-file.sh\nnew file mode 100755\nindex 0000000000..55e050cfd4\n--- /dev/null\n+++ b/t/t3909-stash-pathspec-file.sh\n@@ -0,0 +1,100 @@\n+#!/bin/sh\n+\n+test_description='stash --pathspec-from-file'\n+\n+. ./test-lib.sh\n+\n+test_tick\n+\n+test_expect_success setup '\n+\t>fileA.t &&\n+\t>fileB.t &&\n+\t>fileC.t &&\n+\t>fileD.t &&\n+\tgit add fileA.t fileB.t fileC.t fileD.t &&\n+\tgit commit -m \"Files\" &&\n+\n+\tgit tag checkpoint\n+'\n+\n+restore_checkpoint () {\n+\tgit reset --hard checkpoint\n+}\n+\n+verify_expect () {\n+\tgit stash show --name-status >actual &&\n+\ttest_cmp expect actual\n+}\n+\n+test_expect_success 'simplest' '\n+\trestore_checkpoint &&\n+\n+\t# More files are written to make sure that git didnt ignore\n+\t# --pathspec-from-file, stashing everything\n+\techo A >fileA.t &&\n+\techo B >fileB.t &&\n+\techo C >fileC.t &&\n+\techo D >fileD.t &&\n+\n+\tcat >expect <<-\\EOF &&\n+\tM\tfileA.t\n+\tEOF\n+\n+\techo fileA.t | git stash push --pathspec-from-file=- &&\n+\tverify_expect\n+'\n+\n+test_expect_success '--pathspec-file-nul' '\n+\trestore_checkpoint &&\n+\n+\t# More files are written to make sure that git didnt ignore\n+\t# --pathspec-from-file, stashing everything\n+\techo A >fileA.t &&\n+\techo B >fileB.t &&\n+\techo C >fileC.t &&\n+\techo D >fileD.t &&\n+\n+\tcat >expect <<-\\EOF &&\n+\tM\tfileA.t\n+\tM\tfileB.t\n+\tEOF\n+\n+\tprintf \"fileA.t\\0fileB.t\\0\" | git stash push --pathspec-from-file=- --pathspec-file-nul &&\n+\tverify_expect\n+'\n+\n+test_expect_success 'only touches what was listed' '\n+\trestore_checkpoint &&\n+\n+\t# More files are written to make sure that git didnt ignore\n+\t# --pathspec-from-file, stashing everything\n+\techo A >fileA.t &&\n+\techo B >fileB.t &&\n+\techo C >fileC.t &&\n+\techo D >fileD.t &&\n+\n+\tcat >expect <<-\\EOF &&\n+\tM\tfileB.t\n+\tM\tfileC.t\n+\tEOF\n+\n+\tprintf \"fileB.t\\nfileC.t\\n\" | git stash push --pathspec-from-file=- &&\n+\tverify_expect\n+'\n+\n+test_expect_success 'error conditions' '\n+\trestore_checkpoint &&\n+\techo A >fileA.t &&\n+\techo fileA.t >list &&\n+\n+\ttest_must_fail git stash push --pathspec-from-file=list --patch 2>err &&\n+\ttest_i18ngrep -e \"--pathspec-from-file is incompatible with --patch\" err &&\n+\n+\ttest_must_fail git stash push --pathspec-from-file=list -- fileA.t 2>err &&\n+\ttest_i18ngrep -e \"--pathspec-from-file is incompatible with pathspec arguments\" err &&\n+\n+\ttest_must_fail git stash push --pathspec-file-nul 2>err &&\n+\ttest_i18ngrep -e \"--pathspec-file-nul requires --pathspec-from-file\" err\n+'\n+\n+test_done\n-- \ngitgitgadget\n"},{"id":"391460","messageId":"0558cbbe38e695bd59766d0046ec18ad08e12909.1581345948.git.gitgitgadget@gmail.com","threadId":"52648","inReplyTo":"pull.530.v2.git.1581345948.gitgitgadget@gmail.com","subject":"[PATCH v2 7/8] stash: eliminate crude option parsing","fromName":"Alexandr Miloslavskiy via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-02-10T14:45:46Z","receivedAt":"2020-02-10T14:46:04Z","isPatch":true,"sender":{"key":"alexandr.miloslavskiy@syntevo.com","avatar":null},"body":"From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n\nEliminate crude option parsing and rely on real parsing instead, because\n1) Crude parsing is crude, for example it's not capable of\n   handling things like `git stash -m Message`\n2) Adding options in two places is inconvenient and prone to bugs\n\nAs a side result, the case of `git stash -m Message` gets fixed.\nAlso give a good error message instead of just throwing usage at user.\n\n----\n\nSome review of what's been happening to this code:\n\nBefore [1], `git-stash.sh` only verified that all args begin with `-` :\n\n\t# The default command is \"push\" if nothing but options are given\n\tseen_non_option=\n\tfor opt\n\tdo\n\t\tcase \"$opt\" in\n\t\t--) break ;;\n\t\t-*) ;;\n\t\t*) seen_non_option=t; break ;;\n\t\tesac\n\tdone\n\nLater, [1] introduced the duplicate code I'm now removing, also making\nthe previous test more strict by white-listing options.\n\n----\n\n[1] Commit 40af1468 (\"stash: convert `stash--helper.c` into `stash.c`\" 2019-02-26)\n\nSigned-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n---\n builtin/stash.c  | 59 +++++++++++++++++-------------------------------\n t/t3903-stash.sh |  5 ++++\n 2 files changed, 26 insertions(+), 38 deletions(-)\n\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex 879fc5f368..ed84ff2e16 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -1451,8 +1451,10 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q\n \treturn ret;\n }\n \n-static int push_stash(int argc, const char **argv, const char *prefix)\n+static int push_stash(int argc, const char **argv, const char *prefix,\n+\t\t      int push_assumed)\n {\n+\tint force_assume = 0;\n \tint keep_index = -1;\n \tint patch_mode = 0;\n \tint include_untracked = 0;\n@@ -1474,10 +1476,22 @@ static int push_stash(int argc, const char **argv, const char *prefix)\n \t\tOPT_END()\n \t};\n \n-\tif (argc)\n+\tif (argc) {\n+\t\tforce_assume = !strcmp(argv[0], \"-p\");\n \t\targc = parse_options(argc, argv, prefix, options,\n \t\t\t\t     git_stash_push_usage,\n-\t\t\t\t     0);\n+\t\t\t\t     PARSE_OPT_KEEP_DASHDASH);\n+\t}\n+\n+\tif (argc) {\n+\t\tif (!strcmp(argv[0], \"--\")) {\n+\t\t\targc--;\n+\t\t\targv++;\n+\t\t} else if (push_assumed && !force_assume) {\n+\t\t\tdie(\"subcommand wasn't specified; 'push' can't be assumed due to unexpected token '%s'\",\n+\t\t\t    argv[0]);\n+\t\t}\n+\t}\n \n \tparse_pathspec(&ps, 0, PATHSPEC_PREFER_FULL | PATHSPEC_PREFIX_ORIGIN,\n \t\t       prefix, argv);\n@@ -1550,7 +1564,6 @@ static int use_builtin_stash(void)\n \n int cmd_stash(int argc, const char **argv, const char *prefix)\n {\n-\tint i = -1;\n \tpid_t pid = getpid();\n \tconst char *index_file;\n \tstruct argv_array args = ARGV_ARRAY_INIT;\n@@ -1583,7 +1596,7 @@ int cmd_stash(int argc, const char **argv, const char *prefix)\n \t\t    (uintmax_t)pid);\n \n \tif (!argc)\n-\t\treturn !!push_stash(0, NULL, prefix);\n+\t\treturn !!push_stash(0, NULL, prefix, 0);\n \telse if (!strcmp(argv[0], \"apply\"))\n \t\treturn !!apply_stash(argc, argv, prefix);\n \telse if (!strcmp(argv[0], \"clear\"))\n@@ -1603,45 +1616,15 @@ int cmd_stash(int argc, const char **argv, const char *prefix)\n \telse if (!strcmp(argv[0], \"create\"))\n \t\treturn !!create_stash(argc, argv, prefix);\n \telse if (!strcmp(argv[0], \"push\"))\n-\t\treturn !!push_stash(argc, argv, prefix);\n+\t\treturn !!push_stash(argc, argv, prefix, 0);\n \telse if (!strcmp(argv[0], \"save\"))\n \t\treturn !!save_stash(argc, argv, prefix);\n \telse if (*argv[0] != '-')\n \t\tusage_msg_opt(xstrfmt(_(\"unknown subcommand: %s\"), argv[0]),\n \t\t\t      git_stash_usage, options);\n \n-\tif (strcmp(argv[0], \"-p\")) {\n-\t\twhile (++i < argc && strcmp(argv[i], \"--\")) {\n-\t\t\t/*\n-\t\t\t * `akpqu` is a string which contains all short options,\n-\t\t\t * except `-m` which is verified separately.\n-\t\t\t */\n-\t\t\tif ((strlen(argv[i]) == 2) && *argv[i] == '-' &&\n-\t\t\t    strchr(\"akpqu\", argv[i][1]))\n-\t\t\t\tcontinue;\n-\n-\t\t\tif (!strcmp(argv[i], \"--all\") ||\n-\t\t\t    !strcmp(argv[i], \"--keep-index\") ||\n-\t\t\t    !strcmp(argv[i], \"--no-keep-index\") ||\n-\t\t\t    !strcmp(argv[i], \"--patch\") ||\n-\t\t\t    !strcmp(argv[i], \"--quiet\") ||\n-\t\t\t    !strcmp(argv[i], \"--include-untracked\"))\n-\t\t\t\tcontinue;\n-\n-\t\t\t/*\n-\t\t\t * `-m` and `--message=` are verified separately because\n-\t\t\t * they need to be immediately followed by a string\n-\t\t\t * (i.e.`-m\"foobar\"` or `--message=\"foobar\"`).\n-\t\t\t */\n-\t\t\tif (starts_with(argv[i], \"-m\") ||\n-\t\t\t    starts_with(argv[i], \"--message=\"))\n-\t\t\t\tcontinue;\n-\n-\t\t\tusage_with_options(git_stash_usage, options);\n-\t\t}\n-\t}\n-\n+\t/* Assume 'stash push' */\n \targv_array_push(&args, \"push\");\n \targv_array_pushv(&args, argv);\n-\treturn !!push_stash(args.argc, args.argv, prefix);\n+\treturn !!push_stash(args.argc, args.argv, prefix, 1);\n }\ndiff --git a/t/t3903-stash.sh b/t/t3903-stash.sh\nindex ea56e85e70..3ad23e2502 100755\n--- a/t/t3903-stash.sh\n+++ b/t/t3903-stash.sh\n@@ -285,6 +285,11 @@ test_expect_success 'stash --no-keep-index' '\n \ttest bar,bar2 = $(cat file),$(cat file2)\n '\n \n+test_expect_success 'dont assume push with non-option args' '\n+\ttest_must_fail git stash -q drop 2>err &&\n+\ttest_i18ngrep -e \"subcommand wasn'\\''t specified; '\\''push'\\'' can'\\''t be assumed due to unexpected token '\\''drop'\\''\" err\n+'\n+\n test_expect_success 'stash --invalid-option' '\n \techo bar5 >file &&\n \techo bar6 >file2 &&\n-- \ngitgitgadget\n\n"},{"id":"391466","messageId":"19ab18db-3149-02b1-41d8-7ddb42c3757d@syntevo.com","threadId":"52648","inReplyTo":"xmqqftg8a9fp.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH 2/8] rm: support the --pathspec-from-file option","fromName":"Alexandr Miloslavskiy","fromEmail":"alexandr.miloslavskiy@syntevo.com","sentAt":"2020-02-10T14:46:58Z","receivedAt":"2020-02-10T15:06:49Z","isPatch":true,"sender":{"key":"alexandr.miloslavskiy@syntevo.com","avatar":null},"body":"Sorry for late reply, I was on vacation. Now I'm back and ready to \ncontinue :)\n\nThanks for your review!\n\nOn 21.01.2020 20:36, Junio C Hamano wrote:\n>> Decisions taken for simplicity:\n>> 1) It is not allowed to pass pathspec in both args and file.\n>>\n>> `if (!argc)` block was adapted to work with --pathspec-from-file. For\n>> that, I also had to parse pathspec earlier. Now it happens before\n>> `read_cache()` / `hold_locked_index()` / `setup_work_tree()`, which\n>> sounds fine to me.\n> \n> That is not an explanation nor justification.\n\nI'm not exactly sure what are you suggesting. My best guess is that you \nwant to remove \"`if (!argc)` block was adapted\" paragraph from commit \nmessage? I thought about it and it feels wrong to leave this change \nunexplained. Or are you suggesting to reword it? If so, please give a hint.\n\n>> In case of empty pathspec, there is now a clear error message instead\n>> of showing usage.\n> \n> Hmph, \"git rm --pathspec-from-file=/dev/null\" would say \"nothing\n> specified, nothing removed\" and it makes perfect sense, but I am not\n> sure \"git rm\" that gives the same message is better than the output\n> by usage_with_options(builtin_rm_usage, builtin_rm_options).\n\nWhat feels wrong to me is when I make a mistake and git just slams me \nwith usage, and then it's up to me to figure what could be wrong. I \nmyself struggled to find a mistake a couple times (in similar cases, not \nin this specific one) and didn't like the experience.\n\nThis could be a lot worse when there's no mistake, just the file was \nempty - but you already agreed that showing a new error message is \nreasonable with '--pathspec-from-file'.\n\nStill, without '--pathspec-from-file', it should still be better to \npoint to a specific error rather then \"here's usage and try to find a \ndifference\". I have reworded the error message in V2 in hopes that it \nwill be less controversial.\n\nIf you still don't like it, I will change it to only show the new error \nwith '--pathspec-from-file'.\n\n>> +static int ignore_unmatch = 0, pathspec_file_nul = 0;\n>> +static char *pathspec_from_file = NULL;\n> \n> We may want to clean these \"explicitly initialize to 0/NULL\" up at\n> some point.  The clean-up itself would not be in the scope of this\n> patch, of course, but not making it worse is something this patch\n> can do to help.\n\nChanged in V2.\n"},{"id":"391467","messageId":"eed07f5d-4554-6d26-4d71-f1f975e0ff12@syntevo.com","threadId":"52648","inReplyTo":"xmqqblqwa7d3.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH 4/8] doc: stash: split options from description (2)","fromName":"Alexandr Miloslavskiy","fromEmail":"alexandr.miloslavskiy@syntevo.com","sentAt":"2020-02-10T14:47:13Z","receivedAt":"2020-02-10T15:10:37Z","isPatch":true,"sender":{"key":"alexandr.miloslavskiy@syntevo.com","avatar":null},"body":"On 21.01.2020 21:21, Junio C Hamano wrote:\n> I have a mixed feelings about this approach.  While I am sympathetic\n> to the \"have a single place to describe all\" approach this patch\n> takes, the approach needs to be executed with care when subcommands\n> do not share much of the options at all.  Those readers who jump to\n> the \"OPTIONS\" section and try to ignore anything outside the section\n> may not easily notice that --keep-index only applies to subcommands\n> that creates a new stash, and meaningless to subcommands that lets\n> you inspect existing stashes, or apply one to the working tree (and\n> optionally to the index), for example.  If the orinal documentation\n> did not use \"OPTIONS\" as the section header and instead said perhaps\n> \"SUBCOMMANDS\", it would have been even better, but otherwise I would\n> suspect that the original \"the options understood by 'push' are all\n> described under the part that begins with 'push [-p] [-k] ...'\n> command line\" arrangement was much easier to understand when reading\n> them through for the first time to learn and also to find what the\n> user is looking for after learning the \"concept\" (e.g. \"with\n> 'stash', there is a way to stash-away the changes made to the\n> working tree\") but before becoming familiar with exact set of\n> options for each subcommand (e.g. \"and there was an option that let\n> me stash only partial changes piecemeal, but what was it spelled?\").\n> \n> If we were to make the result of \"a single place to describe all\"\n> approach anything useful, I think at least\n> \n>   (1) the list itself should make it clear that it does not talk\n>       about options related to listing and showing at all,\n>       before enumerating dashed options.\n> \n>   (2) each item in the enumeration should identify which\n>       subcommand(s) accept(s) it.\n> \n> So, I dunno.\n\nI have updated the patch with (2). Sorry, I didn't understand what you \nmean in (1).\n\nI have included my reasoning in commit message. If you feel against this \nchange, I guess I'll just revert it. Afterall, my only goal was to \ndescribe new options. Tried to change things because I didn't like how \nthis doc goes against the layout I have seen in all previous docs I edited.\n"},{"id":"391481","messageId":"xmqqd0amz3ai.fsf@gitster-ct.c.googlers.com","threadId":"52648","inReplyTo":"19ab18db-3149-02b1-41d8-7ddb42c3757d@syntevo.com","subject":"Re: [PATCH 2/8] rm: support the --pathspec-from-file option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-02-10T18:48:53Z","receivedAt":"2020-02-10T18:49:01Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> writes:\n\n> Sorry for late reply, I was on vacation. Now I'm back and ready to\n> continue :)\n>\n> Thanks for your review!\n>\n> On 21.01.2020 20:36, Junio C Hamano wrote:\n>>> Decisions taken for simplicity:\n>>> 1) It is not allowed to pass pathspec in both args and file.\n>>>\n>>> `if (!argc)` block was adapted to work with --pathspec-from-file. For\n>>> that, I also had to parse pathspec earlier. Now it happens before\n>>> `read_cache()` / `hold_locked_index()` / `setup_work_tree()`, which\n>>> sounds fine to me.\n>>\n>> That is not an explanation nor justification.\n>\n> I'm not exactly sure what are you suggesting.\n\nI expected that the proposed log message to explain and justify why\na change (in behaviour, in design, etc.) is made.  \"There is this\nlimitation\" is not a justification---\"because of such and such\nreasons, there is this limitation, otherwise such and such bad\nthings happen\" is.\n\n"},{"id":"391490","messageId":"xmqq4kvyyy5d.fsf@gitster-ct.c.googlers.com","threadId":"52648","inReplyTo":"7ccbab52e51423a9ba74c0cab77448ceabb9dcdc.1581345948.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v2 2/8] rm: support the --pathspec-from-file option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-02-10T20:39:58Z","receivedAt":"2020-02-10T20:40:07Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"Alexandr Miloslavskiy via GitGitGadget\" <gitgitgadget@gmail.com>\nwrites:\n\n> diff --git a/t/t3601-rm-pathspec-file.sh b/t/t3601-rm-pathspec-file.sh\n> new file mode 100755\n> index 0000000000..4542a0f02f\n> --- /dev/null\n> +++ b/t/t3601-rm-pathspec-file.sh\n> @@ -0,0 +1,79 @@\n> +#!/bin/sh\n> +\n> +test_description='rm --pathspec-from-file'\n> +\n> +. ./test-lib.sh\n> +\n> +test_tick\n> +\n> +test_expect_success setup '\n> +\techo A >fileA.t &&\n> +\techo B >fileB.t &&\n> +\techo C >fileC.t &&\n> +\techo D >fileD.t &&\n> +\tgit add fileA.t fileB.t fileC.t fileD.t &&\n> +\tgit commit -m \"files\" &&\n> +\t\n\nTrailing whitespace on this line.\n\n> +\tgit tag checkpoint\n> +'\n> + ...\n> +test_expect_success 'error conditions' '\n> +\trestore_checkpoint &&\n> +\techo fileA.t >list &&\n> +\n> +\ttest_must_fail git rm --pathspec-from-file=list -- fileA.t 2>err &&\n> +\ttest_i18ngrep -e \"--pathspec-from-file is incompatible with pathspec arguments\" err &&\n> +\n> +\ttest_must_fail git rm --pathspec-file-nul 2>err &&\n> +\ttest_i18ngrep -e \"--pathspec-file-nul requires --pathspec-from-file\" err &&\n> +\t\n\nAnd here too.\n\n> +\t>empty_list &&\n> +\ttest_must_fail git rm --pathspec-from-file=empty_list 2>err &&\n> +\ttest_i18ngrep -e \"No pathspec was given. Which files should I remove?\" err\n> +'\n> +\n> +test_done\n"},{"id":"391928","messageId":"pull.530.v3.git.1581960322.gitgitgadget@gmail.com","threadId":"52648","inReplyTo":"pull.530.v2.git.1581345948.gitgitgadget@gmail.com","subject":"[PATCH v3 0/8] Support --pathspec-from-file in rm, stash","fromName":"Alexandr Miloslavskiy via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-02-17T17:25:14Z","receivedAt":"2020-02-17T17:25:26Z","isPatch":true,"sender":{"key":"alexandr.miloslavskiy@syntevo.com","avatar":null},"body":"Changes since V1\n------------------\nSome polishing based on code review in V1\n1) Improved error message for the case where pathspec is not given to `git rm`\n2) Removed explicit variable initialization to 0 / NULL\n3) Polishing in docs for `git stash`\n\n------------------\nThis series continues the effort to support `--pathspec-from-file`\nin various git commands. Series already in `master`: [1][2]\n\nCc'ing Paul-Sebastian Ungureanu because I touched his git stash code.\n\n[1] https://public-inbox.org/git/pull.445.git.1572895605.gitgitgadget@gmail.com/\n[2] https://lore.kernel.org/git/pull.490.git.1576161385.gitgitgadget@gmail.com/\n\nAlexandr Miloslavskiy (8):\n  doc: rm: synchronize <pathspec> description\n  rm: support the --pathspec-from-file option\n  doc: stash: split options from description (1)\n  doc: stash: split options from description (2)\n  doc: stash: document more options\n  doc: stash: synchronize <pathspec> description\n  stash: eliminate crude option parsing\n  stash push: support the --pathspec-from-file option\n\n Documentation/git-rm.txt       |  61 +++++++-------\n Documentation/git-stash.txt    | 144 +++++++++++++++++++++++----------\n builtin/rm.c                   |  28 +++++--\n builtin/stash.c                |  79 +++++++++---------\n t/t3601-rm-pathspec-file.sh    |  79 ++++++++++++++++++\n t/t3903-stash.sh               |   5 ++\n t/t3909-stash-pathspec-file.sh | 100 +++++++++++++++++++++++\n 7 files changed, 381 insertions(+), 115 deletions(-)\n create mode 100755 t/t3601-rm-pathspec-file.sh\n create mode 100755 t/t3909-stash-pathspec-file.sh\n\n\nbase-commit: de93cc14ab7e8db7645d8dbe4fd2603f76d5851f\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-530%2FSyntevoAlex%2F%230207(git)_pathspec_from_file_3-v3\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-530/SyntevoAlex/#0207(git)_pathspec_from_file_3-v3\nPull-Request: https://github.com/gitgitgadget/git/pull/530\n\nRange-diff vs v2:\n\n 1:  2e8c8ad8158 = 1:  cf065e905dc doc: rm: synchronize <pathspec> description\n 2:  7ccbab52e51 ! 2:  7c657dea89e rm: support the --pathspec-from-file option\n     @@ -5,17 +5,36 @@\n          Decisions taken for simplicity:\n          1) It is not allowed to pass pathspec in both args and file.\n      \n     -    `if (!argc)` block was adapted to work with --pathspec-from-file. For\n     -    that, I also had to parse pathspec earlier. Now it happens before\n     -    `read_cache()` / `hold_locked_index()` / `setup_work_tree()`, which\n     -    sounds fine to me.\n     +    Adjustments were needed for `if (!argc)` block:\n      \n     -    In case of empty pathspec, there is now a clear error message instead\n     -    of showing usage. As a consequence, exit code has also changed. Judging\n     -    from [1] it doesn't seem that showing usage in this case was important\n     -    (according to commit message, it was to avoid segfault), and it doesn't\n     -    fit into how other commands react to empty pathspec. Finally, the new\n     -    error message is easier to understand.\n     +    This code actually means \"pathspec is not present\". Previously, pathspec\n     +    could only come from commandline arguments, so testing for `argc` was a\n     +    valid way of testing for the presence of pathspec. But this is no longer\n     +    true with `--pathspec-from-file`.\n     +\n     +    During the entire `--pathspec-from-file` story, I tried to keep its\n     +    behavior very close to giving pathspec on commandline, so that switching\n     +    from one to another doesn't involve any surprises.\n     +\n     +    However, throwing usage at user in the case of empty\n     +    `--pathspec-from-file` would puzzle because there's nothing wrong with\n     +    \"usage\" (that is, argc/argv array).\n     +\n     +    On the other hand, throwing usage in the old case also feels bad to me.\n     +    While it's less of a puzzle, I (as user) never liked the experience of\n     +    comparing my commandline to \"usage\", trying to spot a difference. Since\n     +    it's already known what the error is, it feels a lot better to give that\n     +    specific error to user.\n     +\n     +    Judging from [1] it doesn't seem that showing usage in this case was\n     +    important (the patch was to avoid segfault), and it doesn't fit into how\n     +    other commands react to empty pathspec (see for example `git add` with a\n     +    custom message).\n     +\n     +    Therefore, I decided to show new error text in both cases. In order to\n     +    continue testing for error early, I moved `parse_pathspec()` higher. Now\n     +    it happens before `read_cache()` / `hold_locked_index()` /\n     +    `setup_work_tree()`, which shouldn't cause any issues.\n      \n          [1] Commit 7612a1ef (\"git-rm: honor -n flag\" 2006-06-09)\n      \n     @@ -136,7 +155,7 @@\n      +\techo D >fileD.t &&\n      +\tgit add fileA.t fileB.t fileC.t fileD.t &&\n      +\tgit commit -m \"files\" &&\n     -+\t\n     ++\n      +\tgit tag checkpoint\n      +'\n      +\n     @@ -193,7 +212,7 @@\n      +\n      +\ttest_must_fail git rm --pathspec-file-nul 2>err &&\n      +\ttest_i18ngrep -e \"--pathspec-file-nul requires --pathspec-from-file\" err &&\n     -+\t\n     ++\n      +\t>empty_list &&\n      +\ttest_must_fail git rm --pathspec-from-file=empty_list 2>err &&\n      +\ttest_i18ngrep -e \"No pathspec was given. Which files should I remove?\" err\n 3:  8c212fc0ed4 = 3:  bb300215d49 doc: stash: split options from description (1)\n 4:  db3a96720ce = 4:  fdaf4532404 doc: stash: split options from description (2)\n 5:  f91ec08b472 = 5:  764b8668d10 doc: stash: document more options\n 6:  04e2fd5865f = 6:  7353b06e30e doc: stash: synchronize <pathspec> description\n 7:  0558cbbe38e = 7:  d34eaf4a272 stash: eliminate crude option parsing\n 8:  0c6f28dc68d = 8:  6465a292b55 stash push: support the --pathspec-from-file option\n\n-- \ngitgitgadget\n"},{"id":"391935","messageId":"cf065e905dcd8e44a1ee87602a7525c537eab27d.1581960322.git.gitgitgadget@gmail.com","threadId":"52648","inReplyTo":"pull.530.v3.git.1581960322.gitgitgadget@gmail.com","subject":"[PATCH v3 1/8] doc: rm: synchronize <pathspec> description","fromName":"Alexandr Miloslavskiy via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-02-17T17:25:15Z","receivedAt":"2020-02-17T17:25:28Z","isPatch":true,"sender":{"key":"alexandr.miloslavskiy@syntevo.com","avatar":null},"body":"From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n\nThis patch continues the effort that is already applied to\n`git commit`, `git reset`, `git checkout` etc.\n\n1) Changed outdated descriptions to mention pathspec instead.\n2) Added reference to 'linkgit:gitglossary[7]'.\n3) Removed content that merely repeated gitglossary.\n4) Merged the remainder of \"discussion\" into `<patchspec>`.\n\nSigned-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n---\n Documentation/git-rm.txt | 50 +++++++++++++++++-----------------------\n 1 file changed, 21 insertions(+), 29 deletions(-)\n\ndiff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt\nindex b5c46223c44..e02a08e5efd 100644\n--- a/Documentation/git-rm.txt\n+++ b/Documentation/git-rm.txt\n@@ -8,16 +8,16 @@ git-rm - Remove files from the working tree and from the index\n SYNOPSIS\n --------\n [verse]\n-'git rm' [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] <file>...\n+'git rm' [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] <pathspec>...\n \n DESCRIPTION\n -----------\n-Remove files from the index, or from the working tree and the index.\n-`git rm` will not remove a file from just your working directory.\n-(There is no option to remove a file only from the working tree\n-and yet keep it in the index; use `/bin/rm` if you want to do that.)\n-The files being removed have to be identical to the tip of the branch,\n-and no updates to their contents can be staged in the index,\n+Remove files matching pathspec from the index, or from the working tree\n+and the index. `git rm` will not remove a file from just your working\n+directory. (There is no option to remove a file only from the working\n+tree and yet keep it in the index; use `/bin/rm` if you want to do\n+that.) The files being removed have to be identical to the tip of the\n+branch, and no updates to their contents can be staged in the index,\n though that default behavior can be overridden with the `-f` option.\n When `--cached` is given, the staged content has to\n match either the tip of the branch or the file on disk,\n@@ -26,15 +26,20 @@ allowing the file to be removed from just the index.\n \n OPTIONS\n -------\n-<file>...::\n-\tFiles to remove.  Fileglobs (e.g. `*.c`) can be given to\n-\tremove all matching files.  If you want Git to expand\n-\tfile glob characters, you may need to shell-escape them.\n-\tA leading directory name\n-\t(e.g. `dir` to remove `dir/file1` and `dir/file2`) can be\n-\tgiven to remove all files in the directory, and recursively\n-\tall sub-directories,\n-\tbut this requires the `-r` option to be explicitly given.\n+<pathspec>...::\n+\tFiles to remove.  A leading directory name (e.g. `dir` to remove\n+\t`dir/file1` and `dir/file2`) can be given to remove all files in\n+\tthe directory, and recursively all sub-directories, but this\n+\trequires the `-r` option to be explicitly given.\n++\n+The command removes only the paths that are known to Git.\n++\n+File globbing matches across directory boundaries.  Thus, given two\n+directories `d` and `d2`, there is a difference between using\n+`git rm 'd*'` and `git rm 'd/*'`, as the former will also remove all\n+of directory `d2`.\n++\n+For more details, see the 'pathspec' entry in linkgit:gitglossary[7].\n \n -f::\n --force::\n@@ -69,19 +74,6 @@ OPTIONS\n \tfor each file removed. This option suppresses that output.\n \n \n-DISCUSSION\n-----------\n-\n-The <file> list given to the command can be exact pathnames,\n-file glob patterns, or leading directory names.  The command\n-removes only the paths that are known to Git.  Giving the name of\n-a file that you have not told Git about does not remove that file.\n-\n-File globbing matches across directory boundaries.  Thus, given\n-two directories `d` and `d2`, there is a difference between\n-using `git rm 'd*'` and `git rm 'd/*'`, as the former will\n-also remove all of directory `d2`.\n-\n REMOVING FILES THAT HAVE DISAPPEARED FROM THE FILESYSTEM\n --------------------------------------------------------\n There is no option for `git rm` to remove from the index only\n-- \ngitgitgadget\n\n"},{"id":"391929","messageId":"7c657dea89e900a023af1130545f6c3341841840.1581960322.git.gitgitgadget@gmail.com","threadId":"52648","inReplyTo":"pull.530.v3.git.1581960322.gitgitgadget@gmail.com","subject":"[PATCH v3 2/8] rm: support the --pathspec-from-file option","fromName":"Alexandr Miloslavskiy via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-02-17T17:25:16Z","receivedAt":"2020-02-17T17:25:29Z","isPatch":true,"sender":{"key":"alexandr.miloslavskiy@syntevo.com","avatar":null},"body":"From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n\nDecisions taken for simplicity:\n1) It is not allowed to pass pathspec in both args and file.\n\nAdjustments were needed for `if (!argc)` block:\n\nThis code actually means \"pathspec is not present\". Previously, pathspec\ncould only come from commandline arguments, so testing for `argc` was a\nvalid way of testing for the presence of pathspec. But this is no longer\ntrue with `--pathspec-from-file`.\n\nDuring the entire `--pathspec-from-file` story, I tried to keep its\nbehavior very close to giving pathspec on commandline, so that switching\nfrom one to another doesn't involve any surprises.\n\nHowever, throwing usage at user in the case of empty\n`--pathspec-from-file` would puzzle because there's nothing wrong with\n\"usage\" (that is, argc/argv array).\n\nOn the other hand, throwing usage in the old case also feels bad to me.\nWhile it's less of a puzzle, I (as user) never liked the experience of\ncomparing my commandline to \"usage\", trying to spot a difference. Since\nit's already known what the error is, it feels a lot better to give that\nspecific error to user.\n\nJudging from [1] it doesn't seem that showing usage in this case was\nimportant (the patch was to avoid segfault), and it doesn't fit into how\nother commands react to empty pathspec (see for example `git add` with a\ncustom message).\n\nTherefore, I decided to show new error text in both cases. In order to\ncontinue testing for error early, I moved `parse_pathspec()` higher. Now\nit happens before `read_cache()` / `hold_locked_index()` /\n`setup_work_tree()`, which shouldn't cause any issues.\n\n[1] Commit 7612a1ef (\"git-rm: honor -n flag\" 2006-06-09)\n\nSigned-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n---\n Documentation/git-rm.txt    | 17 +++++++-\n builtin/rm.c                | 28 ++++++++++---\n t/t3601-rm-pathspec-file.sh | 79 +++++++++++++++++++++++++++++++++++++\n 3 files changed, 117 insertions(+), 7 deletions(-)\n create mode 100755 t/t3601-rm-pathspec-file.sh\n\ndiff --git a/Documentation/git-rm.txt b/Documentation/git-rm.txt\nindex e02a08e5efd..ab750367fde 100644\n--- a/Documentation/git-rm.txt\n+++ b/Documentation/git-rm.txt\n@@ -8,7 +8,9 @@ git-rm - Remove files from the working tree and from the index\n SYNOPSIS\n --------\n [verse]\n-'git rm' [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch] [--quiet] [--] <pathspec>...\n+'git rm' [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch]\n+\t  [--quiet] [--pathspec-from-file=<file> [--pathspec-file-nul]]\n+\t  [--] [<pathspec>...]\n \n DESCRIPTION\n -----------\n@@ -73,6 +75,19 @@ For more details, see the 'pathspec' entry in linkgit:gitglossary[7].\n \t`git rm` normally outputs one line (in the form of an `rm` command)\n \tfor each file removed. This option suppresses that output.\n \n+--pathspec-from-file=<file>::\n+\tPathspec is passed in `<file>` instead of commandline args. If\n+\t`<file>` is exactly `-` then standard input is used. Pathspec\n+\telements are separated by LF or CR/LF. Pathspec elements can be\n+\tquoted as explained for the configuration variable `core.quotePath`\n+\t(see linkgit:git-config[1]). See also `--pathspec-file-nul` and\n+\tglobal `--literal-pathspecs`.\n+\n+--pathspec-file-nul::\n+\tOnly meaningful with `--pathspec-from-file`. Pathspec elements are\n+\tseparated with NUL character and all other characters are taken\n+\tliterally (including newlines and quotes).\n+\n \n REMOVING FILES THAT HAVE DISAPPEARED FROM THE FILESYSTEM\n --------------------------------------------------------\ndiff --git a/builtin/rm.c b/builtin/rm.c\nindex 19ce95a901b..4858631e0f0 100644\n--- a/builtin/rm.c\n+++ b/builtin/rm.c\n@@ -235,7 +235,8 @@ static int check_local_mod(struct object_id *head, int index_only)\n }\n \n static int show_only = 0, force = 0, index_only = 0, recursive = 0, quiet = 0;\n-static int ignore_unmatch = 0;\n+static int ignore_unmatch = 0, pathspec_file_nul;\n+static char *pathspec_from_file;\n \n static struct option builtin_rm_options[] = {\n \tOPT__DRY_RUN(&show_only, N_(\"dry run\")),\n@@ -245,6 +246,8 @@ static struct option builtin_rm_options[] = {\n \tOPT_BOOL('r', NULL,             &recursive,  N_(\"allow recursive removal\")),\n \tOPT_BOOL( 0 , \"ignore-unmatch\", &ignore_unmatch,\n \t\t\t\tN_(\"exit with a zero status even if nothing matched\")),\n+\tOPT_PATHSPEC_FROM_FILE(&pathspec_from_file),\n+\tOPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),\n \tOPT_END(),\n };\n \n@@ -259,8 +262,24 @@ int cmd_rm(int argc, const char **argv, const char *prefix)\n \n \targc = parse_options(argc, argv, prefix, builtin_rm_options,\n \t\t\t     builtin_rm_usage, 0);\n-\tif (!argc)\n-\t\tusage_with_options(builtin_rm_usage, builtin_rm_options);\n+\n+\tparse_pathspec(&pathspec, 0,\n+\t\t       PATHSPEC_PREFER_CWD,\n+\t\t       prefix, argv);\n+\n+\tif (pathspec_from_file) {\n+\t\tif (pathspec.nr)\n+\t\t\tdie(_(\"--pathspec-from-file is incompatible with pathspec arguments\"));\n+\n+\t\tparse_pathspec_file(&pathspec, 0,\n+\t\t\t\t    PATHSPEC_PREFER_CWD,\n+\t\t\t\t    prefix, pathspec_from_file, pathspec_file_nul);\n+\t} else if (pathspec_file_nul) {\n+\t\tdie(_(\"--pathspec-file-nul requires --pathspec-from-file\"));\n+\t}\n+\n+\tif (!pathspec.nr)\n+\t\tdie(_(\"No pathspec was given. Which files should I remove?\"));\n \n \tif (!index_only)\n \t\tsetup_work_tree();\n@@ -270,9 +289,6 @@ int cmd_rm(int argc, const char **argv, const char *prefix)\n \tif (read_cache() < 0)\n \t\tdie(_(\"index file corrupt\"));\n \n-\tparse_pathspec(&pathspec, 0,\n-\t\t       PATHSPEC_PREFER_CWD,\n-\t\t       prefix, argv);\n \trefresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &pathspec, NULL, NULL);\n \n \tseen = xcalloc(pathspec.nr, 1);\ndiff --git a/t/t3601-rm-pathspec-file.sh b/t/t3601-rm-pathspec-file.sh\nnew file mode 100755\nindex 00000000000..7de21f8bcff\n--- /dev/null\n+++ b/t/t3601-rm-pathspec-file.sh\n@@ -0,0 +1,79 @@\n+#!/bin/sh\n+\n+test_description='rm --pathspec-from-file'\n+\n+. ./test-lib.sh\n+\n+test_tick\n+\n+test_expect_success setup '\n+\techo A >fileA.t &&\n+\techo B >fileB.t &&\n+\techo C >fileC.t &&\n+\techo D >fileD.t &&\n+\tgit add fileA.t fileB.t fileC.t fileD.t &&\n+\tgit commit -m \"files\" &&\n+\n+\tgit tag checkpoint\n+'\n+\n+restore_checkpoint () {\n+\tgit reset --hard checkpoint\n+}\n+\n+verify_expect () {\n+\tgit status --porcelain --untracked-files=no -- fileA.t fileB.t fileC.t fileD.t >actual &&\n+\ttest_cmp expect actual\n+}\n+\n+test_expect_success 'simplest' '\n+\trestore_checkpoint &&\n+\n+\tcat >expect <<-\\EOF &&\n+\tD  fileA.t\n+\tEOF\n+\n+\techo fileA.t | git rm --pathspec-from-file=- &&\n+\tverify_expect\n+'\n+\n+test_expect_success '--pathspec-file-nul' '\n+\trestore_checkpoint &&\n+\n+\tcat >expect <<-\\EOF &&\n+\tD  fileA.t\n+\tD  fileB.t\n+\tEOF\n+\n+\tprintf \"fileA.t\\0fileB.t\\0\" | git rm --pathspec-from-file=- --pathspec-file-nul &&\n+\tverify_expect\n+'\n+\n+test_expect_success 'only touches what was listed' '\n+\trestore_checkpoint &&\n+\n+\tcat >expect <<-\\EOF &&\n+\tD  fileB.t\n+\tD  fileC.t\n+\tEOF\n+\n+\tprintf \"fileB.t\\nfileC.t\\n\" | git rm --pathspec-from-file=- &&\n+\tverify_expect\n+'\n+\n+test_expect_success 'error conditions' '\n+\trestore_checkpoint &&\n+\techo fileA.t >list &&\n+\n+\ttest_must_fail git rm --pathspec-from-file=list -- fileA.t 2>err &&\n+\ttest_i18ngrep -e \"--pathspec-from-file is incompatible with pathspec arguments\" err &&\n+\n+\ttest_must_fail git rm --pathspec-file-nul 2>err &&\n+\ttest_i18ngrep -e \"--pathspec-file-nul requires --pathspec-from-file\" err &&\n+\n+\t>empty_list &&\n+\ttest_must_fail git rm --pathspec-from-file=empty_list 2>err &&\n+\ttest_i18ngrep -e \"No pathspec was given. Which files should I remove?\" err\n+'\n+\n+test_done\n-- \ngitgitgadget\n\n"},{"id":"391930","messageId":"bb300215d4978cf64d4c60d92c13acd7464b545b.1581960322.git.gitgitgadget@gmail.com","threadId":"52648","inReplyTo":"pull.530.v3.git.1581960322.gitgitgadget@gmail.com","subject":"[PATCH v3 3/8] doc: stash: split options from description (1)","fromName":"Alexandr Miloslavskiy via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-02-17T17:25:17Z","receivedAt":"2020-02-17T17:25:30Z","isPatch":true,"sender":{"key":"alexandr.miloslavskiy@syntevo.com","avatar":null},"body":"From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n\nThis patch moves blocks of text as-is to make it easier to review the\nnext patch.\n\nSigned-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n---\n Documentation/git-stash.txt | 68 +++++++++++++++++++------------------\n 1 file changed, 35 insertions(+), 33 deletions(-)\n\ndiff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt\nindex 53e1a1205d3..2dedc219974 100644\n--- a/Documentation/git-stash.txt\n+++ b/Documentation/git-stash.txt\n@@ -58,31 +58,6 @@ non-option arguments are not allowed to prevent a misspelled\n subcommand from making an unwanted stash entry.  The two exceptions to this\n are `stash -p` which acts as alias for `stash push -p` and pathspecs,\n which are allowed after a double hyphen `--` for disambiguation.\n-+\n-When pathspec is given to 'git stash push', the new stash entry records the\n-modified states only for the files that match the pathspec.  The index\n-entries and working tree files are then rolled back to the state in\n-HEAD only for these files, too, leaving files that do not match the\n-pathspec intact.\n-+\n-If the `--keep-index` option is used, all changes already added to the\n-index are left intact.\n-+\n-If the `--include-untracked` option is used, all untracked files are also\n-stashed and then cleaned up with `git clean`, leaving the working directory\n-in a very clean state. If the `--all` option is used instead then the\n-ignored files are stashed and cleaned in addition to the untracked files.\n-+\n-With `--patch`, you can interactively select hunks from the diff\n-between HEAD and the working tree to be stashed.  The stash entry is\n-constructed such that its index state is the same as the index state\n-of your repository, and its worktree contains only the changes you\n-selected interactively.  The selected changes are then rolled back\n-from your worktree. See the ``Interactive Mode'' section of\n-linkgit:git-add[1] to learn how to operate the `--patch` mode.\n-+\n-The `--patch` option implies `--keep-index`.  You can use\n-`--no-keep-index` to override this.\n \n save [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [<message>]::\n \n@@ -128,14 +103,6 @@ pop [--index] [-q|--quiet] [<stash>]::\n Applying the state can fail with conflicts; in this case, it is not\n removed from the stash list. You need to resolve the conflicts by hand\n and call `git stash drop` manually afterwards.\n-+\n-If the `--index` option is used, then tries to reinstate not only the working\n-tree's changes, but also the index's ones. However, this can fail, when you\n-have conflicts (which are stored in the index, where you therefore can no\n-longer apply the changes as they were originally).\n-+\n-When no `<stash>` is given, `stash@{0}` is assumed, otherwise `<stash>` must\n-be a reference of the form `stash@{<revision>}`.\n \n apply [--index] [-q|--quiet] [<stash>]::\n \n@@ -185,6 +152,41 @@ store::\n \treflog.  This is intended to be useful for scripts.  It is\n \tprobably not the command you want to use; see \"push\" above.\n \n+If the `--all` option is used instead then the\n+ignored files are stashed and cleaned in addition to the untracked files.\n+\n+If the `--include-untracked` option is used, all untracked files are also\n+stashed and then cleaned up with `git clean`, leaving the working directory\n+in a very clean state.\n+\n+If the `--index` option is used, then tries to reinstate not only the working\n+tree's changes, but also the index's ones. However, this can fail, when you\n+have conflicts (which are stored in the index, where you therefore can no\n+longer apply the changes as they were originally).\n+\n+If the `--keep-index` option is used, all changes already added to the\n+index are left intact.\n+\n+With `--patch`, you can interactively select hunks from the diff\n+between HEAD and the working tree to be stashed.  The stash entry is\n+constructed such that its index state is the same as the index state\n+of your repository, and its worktree contains only the changes you\n+selected interactively.  The selected changes are then rolled back\n+from your worktree. See the ``Interactive Mode'' section of\n+linkgit:git-add[1] to learn how to operate the `--patch` mode.\n++\n+The `--patch` option implies `--keep-index`.  You can use \n+`--no-keep-index` to override this.\n+\n+When pathspec is given to 'git stash push', the new stash entry records the\n+modified states only for the files that match the pathspec.  The index\n+entries and working tree files are then rolled back to the state in\n+HEAD only for these files, too, leaving files that do not match the\n+pathspec intact.\n+\n+When no `<stash>` is given, `stash@{0}` is assumed, otherwise `<stash>` must\n+be a reference of the form `stash@{<revision>}`.\n+\n DISCUSSION\n ----------\n \n-- \ngitgitgadget\n\n"},{"id":"391932","messageId":"764b8668d10d691d3bde5185e32eb5f7264d0348.1581960322.git.gitgitgadget@gmail.com","threadId":"52648","inReplyTo":"pull.530.v3.git.1581960322.gitgitgadget@gmail.com","subject":"[PATCH v3 5/8] doc: stash: document more options","fromName":"Alexandr Miloslavskiy via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-02-17T17:25:19Z","receivedAt":"2020-02-17T17:25:31Z","isPatch":true,"sender":{"key":"alexandr.miloslavskiy@syntevo.com","avatar":null},"body":"From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n\nSigned-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n---\n Documentation/git-stash.txt | 12 ++++++++++++\n 1 file changed, 12 insertions(+)\n\ndiff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt\nindex c1c16623cbc..6dc0a5b0eef 100644\n--- a/Documentation/git-stash.txt\n+++ b/Documentation/git-stash.txt\n@@ -194,6 +194,18 @@ to learn how to operate the `--patch` mode.\n The `--patch` option implies `--keep-index`.  You can use \n `--no-keep-index` to override this.\n \n+-q::\n+--quiet::\n+\tThis option is only valid for `apply`, `drop`, `pop`, `push`,\n+\t`save`, `store` commands.\n++\n+Quiet, suppress feedback messages.\n+\n+\\--::\n+\tThis option is only valid for `push` command.\n++\n+Separates pathspec from options for disambiguation purposes.\n+\n <pathspec>...::\n \tThis option is only valid for `push` command.\n +\n-- \ngitgitgadget\n\n"},{"id":"391931","messageId":"6465a292b5541e9bc50c1f31ed0165bb4ba6e1e1.1581960322.git.gitgitgadget@gmail.com","threadId":"52648","inReplyTo":"pull.530.v3.git.1581960322.gitgitgadget@gmail.com","subject":"[PATCH v3 8/8] stash push: support the --pathspec-from-file option","fromName":"Alexandr Miloslavskiy via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-02-17T17:25:22Z","receivedAt":"2020-02-17T17:25:33Z","isPatch":true,"sender":{"key":"alexandr.miloslavskiy@syntevo.com","avatar":null},"body":"From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n\nDecisions taken for simplicity:\n1) For now, `--pathspec-from-file` is declared incompatible with\n   `--patch`, even when <file> is not `-`. Such use case is not\n   really expected.\n2) It is not allowed to pass pathspec in both args and file.\n\nSigned-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n---\n Documentation/git-stash.txt    |  20 ++++++-\n builtin/stash.c                |  20 +++++++\n t/t3909-stash-pathspec-file.sh | 100 +++++++++++++++++++++++++++++++++\n 3 files changed, 139 insertions(+), 1 deletion(-)\n create mode 100755 t/t3909-stash-pathspec-file.sh\n\ndiff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt\nindex 52e64985bda..79949f86175 100644\n--- a/Documentation/git-stash.txt\n+++ b/Documentation/git-stash.txt\n@@ -15,6 +15,7 @@ SYNOPSIS\n 'git stash' branch <branchname> [<stash>]\n 'git stash' [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n \t     [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n+\t     [--pathspec-from-file=<file> [--pathspec-file-nul]]\n \t     [--] [<pathspec>...]]\n 'git stash' clear\n 'git stash' create [<message>]\n@@ -46,7 +47,7 @@ stash index (e.g. the integer `n` is equivalent to `stash@{n}`).\n COMMANDS\n --------\n \n-push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--] [<pathspec>...]::\n+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>...]::\n \n \tSave your local modifications to a new 'stash entry' and roll them\n \tback to HEAD (in the working tree and in the index).\n@@ -194,6 +195,23 @@ to learn how to operate the `--patch` mode.\n The `--patch` option implies `--keep-index`.  You can use \n `--no-keep-index` to override this.\n \n+--pathspec-from-file=<file>::\n+\tThis option is only valid for `push` command.\n++\n+Pathspec is passed in `<file>` instead of commandline args. If\n+`<file>` is exactly `-` then standard input is used. Pathspec\n+elements are separated by LF or CR/LF. Pathspec elements can be\n+quoted as explained for the configuration variable `core.quotePath`\n+(see linkgit:git-config[1]). See also `--pathspec-file-nul` and\n+global `--literal-pathspecs`.\n+\n+--pathspec-file-nul::\n+\tThis option is only valid for `push` command.\n++\n+Only meaningful with `--pathspec-from-file`. Pathspec elements are\n+separated with NUL character and all other characters are taken\n+literally (including newlines and quotes).\n+\n -q::\n --quiet::\n \tThis option is only valid for `apply`, `drop`, `pop`, `push`,\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex ed84ff2e168..78af6ce5643 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -27,6 +27,7 @@ static const char * const git_stash_usage[] = {\n \tN_(\"git stash clear\"),\n \tN_(\"git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\\n\"\n \t   \"          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\\n\"\n+\t   \"          [--pathspec-from-file=<file> [--pathspec-file-nul]]\\n\"\n \t   \"          [--] [<pathspec>...]]\"),\n \tN_(\"git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\\n\"\n \t   \"          [-u|--include-untracked] [-a|--all] [<message>]\"),\n@@ -1459,7 +1460,9 @@ static int push_stash(int argc, const char **argv, const char *prefix,\n \tint patch_mode = 0;\n \tint include_untracked = 0;\n \tint quiet = 0;\n+\tint pathspec_file_nul = 0;\n \tconst char *stash_msg = NULL;\n+\tconst char *pathspec_from_file = NULL;\n \tstruct pathspec ps;\n \tstruct option options[] = {\n \t\tOPT_BOOL('k', \"keep-index\", &keep_index,\n@@ -1473,6 +1476,8 @@ static int push_stash(int argc, const char **argv, const char *prefix,\n \t\t\t    N_(\"include ignore files\"), 2),\n \t\tOPT_STRING('m', \"message\", &stash_msg, N_(\"message\"),\n \t\t\t   N_(\"stash message\")),\n+\t\tOPT_PATHSPEC_FROM_FILE(&pathspec_from_file),\n+\t\tOPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),\n \t\tOPT_END()\n \t};\n \n@@ -1495,6 +1500,21 @@ static int push_stash(int argc, const char **argv, const char *prefix,\n \n \tparse_pathspec(&ps, 0, PATHSPEC_PREFER_FULL | PATHSPEC_PREFIX_ORIGIN,\n \t\t       prefix, argv);\n+\n+\tif (pathspec_from_file) {\n+\t\tif (patch_mode)\n+\t\t\tdie(_(\"--pathspec-from-file is incompatible with --patch\"));\n+\n+\t\tif (ps.nr)\n+\t\t\tdie(_(\"--pathspec-from-file is incompatible with pathspec arguments\"));\n+\n+\t\tparse_pathspec_file(&ps, 0,\n+\t\t\t\t    PATHSPEC_PREFER_FULL | PATHSPEC_PREFIX_ORIGIN,\n+\t\t\t\t    prefix, pathspec_from_file, pathspec_file_nul);\n+\t} else if (pathspec_file_nul) {\n+\t\tdie(_(\"--pathspec-file-nul requires --pathspec-from-file\"));\n+\t}\n+\n \treturn do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode,\n \t\t\t     include_untracked);\n }\ndiff --git a/t/t3909-stash-pathspec-file.sh b/t/t3909-stash-pathspec-file.sh\nnew file mode 100755\nindex 00000000000..55e050cfd4d\n--- /dev/null\n+++ b/t/t3909-stash-pathspec-file.sh\n@@ -0,0 +1,100 @@\n+#!/bin/sh\n+\n+test_description='stash --pathspec-from-file'\n+\n+. ./test-lib.sh\n+\n+test_tick\n+\n+test_expect_success setup '\n+\t>fileA.t &&\n+\t>fileB.t &&\n+\t>fileC.t &&\n+\t>fileD.t &&\n+\tgit add fileA.t fileB.t fileC.t fileD.t &&\n+\tgit commit -m \"Files\" &&\n+\n+\tgit tag checkpoint\n+'\n+\n+restore_checkpoint () {\n+\tgit reset --hard checkpoint\n+}\n+\n+verify_expect () {\n+\tgit stash show --name-status >actual &&\n+\ttest_cmp expect actual\n+}\n+\n+test_expect_success 'simplest' '\n+\trestore_checkpoint &&\n+\n+\t# More files are written to make sure that git didnt ignore\n+\t# --pathspec-from-file, stashing everything\n+\techo A >fileA.t &&\n+\techo B >fileB.t &&\n+\techo C >fileC.t &&\n+\techo D >fileD.t &&\n+\n+\tcat >expect <<-\\EOF &&\n+\tM\tfileA.t\n+\tEOF\n+\n+\techo fileA.t | git stash push --pathspec-from-file=- &&\n+\tverify_expect\n+'\n+\n+test_expect_success '--pathspec-file-nul' '\n+\trestore_checkpoint &&\n+\n+\t# More files are written to make sure that git didnt ignore\n+\t# --pathspec-from-file, stashing everything\n+\techo A >fileA.t &&\n+\techo B >fileB.t &&\n+\techo C >fileC.t &&\n+\techo D >fileD.t &&\n+\n+\tcat >expect <<-\\EOF &&\n+\tM\tfileA.t\n+\tM\tfileB.t\n+\tEOF\n+\n+\tprintf \"fileA.t\\0fileB.t\\0\" | git stash push --pathspec-from-file=- --pathspec-file-nul &&\n+\tverify_expect\n+'\n+\n+test_expect_success 'only touches what was listed' '\n+\trestore_checkpoint &&\n+\n+\t# More files are written to make sure that git didnt ignore\n+\t# --pathspec-from-file, stashing everything\n+\techo A >fileA.t &&\n+\techo B >fileB.t &&\n+\techo C >fileC.t &&\n+\techo D >fileD.t &&\n+\n+\tcat >expect <<-\\EOF &&\n+\tM\tfileB.t\n+\tM\tfileC.t\n+\tEOF\n+\n+\tprintf \"fileB.t\\nfileC.t\\n\" | git stash push --pathspec-from-file=- &&\n+\tverify_expect\n+'\n+\n+test_expect_success 'error conditions' '\n+\trestore_checkpoint &&\n+\techo A >fileA.t &&\n+\techo fileA.t >list &&\n+\n+\ttest_must_fail git stash push --pathspec-from-file=list --patch 2>err &&\n+\ttest_i18ngrep -e \"--pathspec-from-file is incompatible with --patch\" err &&\n+\n+\ttest_must_fail git stash push --pathspec-from-file=list -- fileA.t 2>err &&\n+\ttest_i18ngrep -e \"--pathspec-from-file is incompatible with pathspec arguments\" err &&\n+\n+\ttest_must_fail git stash push --pathspec-file-nul 2>err &&\n+\ttest_i18ngrep -e \"--pathspec-file-nul requires --pathspec-from-file\" err\n+'\n+\n+test_done\n-- \ngitgitgadget\n"},{"id":"391933","messageId":"fdaf45324047f850f6b870c628351f53e4980d96.1581960322.git.gitgitgadget@gmail.com","threadId":"52648","inReplyTo":"pull.530.v3.git.1581960322.gitgitgadget@gmail.com","subject":"[PATCH v3 4/8] doc: stash: split options from description (2)","fromName":"Alexandr Miloslavskiy via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-02-17T17:25:18Z","receivedAt":"2020-02-17T17:25:35Z","isPatch":true,"sender":{"key":"alexandr.miloslavskiy@syntevo.com","avatar":null},"body":"From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n\nTogether with the previous patch, this brings docs for `git stash` to\nthe common layout used for most other commands (see for example docs\nfor `git add`, `git commit`, `git checkout`, `git reset`) where all\noptions are documented in a separate list.\n\nAfter some thinking and having a look at docs for `git svn` and\n`git `submodule`, I have arrived at following conclusions:\n  * Options should be described in a list rather then text to\n    facilitate lookup for user.\n  * Single list is better then multiple lists because it avoids\n    copy&pasting descriptions between subcommands (or, without\n    copy&pasting, user will have to look up missing options in other\n    subcommands).\n  * As a consequence, commands section should only give brief info and\n    list possible options. Since options have good enough names, user\n\twill only need to look up the \"interesting\" options.\n  * Every option should list which subcommands support it.\n\nI have decided to use alphabetical sorting in the list of options to\nfacilitate lookup for user.\n\nThere is some text editing done to make old descriptions better fit\ninto the list-style format.\n\nSigned-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n---\n Documentation/git-stash.txt | 92 +++++++++++++++++++++++--------------\n 1 file changed, 57 insertions(+), 35 deletions(-)\n\ndiff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt\nindex 2dedc219974..c1c16623cbc 100644\n--- a/Documentation/git-stash.txt\n+++ b/Documentation/git-stash.txt\n@@ -43,8 +43,8 @@ created stash, `stash@{1}` is the one before it, `stash@{2.hours.ago}`\n is also possible). Stashes may also be referenced by specifying just the\n stash index (e.g. the integer `n` is equivalent to `stash@{n}`).\n \n-OPTIONS\n--------\n+COMMANDS\n+--------\n \n push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--] [<pathspec>...]::\n \n@@ -86,7 +86,7 @@ show [<options>] [<stash>]::\n \n \tShow the changes recorded in the stash entry as a diff between the\n \tstashed contents and the commit back when the stash entry was first\n-\tcreated. When no `<stash>` is given, it shows the latest one.\n+\tcreated.\n \tBy default, the command shows the diffstat, but it will accept any\n \tformat known to 'git diff' (e.g., `git stash show -p stash@{1}`\n \tto view the second most recent entry in patch form).\n@@ -116,8 +116,7 @@ branch <branchname> [<stash>]::\n \tthe commit at which the `<stash>` was originally created, applies the\n \tchanges recorded in `<stash>` to the new working tree and index.\n \tIf that succeeds, and `<stash>` is a reference of the form\n-\t`stash@{<revision>}`, it then drops the `<stash>`. When no `<stash>`\n-\tis given, applies the latest one.\n+\t`stash@{<revision>}`, it then drops the `<stash>`.\n +\n This is useful if the branch on which you ran `git stash push` has\n changed enough that `git stash apply` fails due to conflicts. Since\n@@ -133,9 +132,6 @@ clear::\n drop [-q|--quiet] [<stash>]::\n \n \tRemove a single stash entry from the list of stash entries.\n-\tWhen no `<stash>` is given, it removes the latest one.\n-\ti.e. `stash@{0}`, otherwise `<stash>` must be a valid stash\n-\tlog reference of the form `stash@{<revision>}`.\n \n create::\n \n@@ -152,40 +148,66 @@ store::\n \treflog.  This is intended to be useful for scripts.  It is\n \tprobably not the command you want to use; see \"push\" above.\n \n-If the `--all` option is used instead then the\n-ignored files are stashed and cleaned in addition to the untracked files.\n-\n-If the `--include-untracked` option is used, all untracked files are also\n-stashed and then cleaned up with `git clean`, leaving the working directory\n-in a very clean state.\n+OPTIONS\n+-------\n+-a::\n+--all::\n+\tThis option is only valid for `push` and `save` commands.\n++\n+All ignored and untracked files are also stashed and then cleaned\n+up with `git clean`.\n \n-If the `--index` option is used, then tries to reinstate not only the working\n-tree's changes, but also the index's ones. However, this can fail, when you\n-have conflicts (which are stored in the index, where you therefore can no\n-longer apply the changes as they were originally).\n+-u::\n+--include-untracked::\n+\tThis option is only valid for `push` and `save` commands.\n++\n+All untracked files are also stashed and then cleaned up with\n+`git clean`.\n \n-If the `--keep-index` option is used, all changes already added to the\n-index are left intact.\n+--index::\n+\tThis option is only valid for `pop` and `apply` commands.\n++\n+Tries to reinstate not only the working tree's changes, but also\n+the index's ones. However, this can fail, when you have conflicts\n+(which are stored in the index, where you therefore can no longer\n+apply the changes as they were originally).\n+\n+-k::\n+--keep-index::\n+--no-keep-index::\n+\tThis option is only valid for `push` and `save` commands.\n++\n+All changes already added to the index are left intact.\n \n-With `--patch`, you can interactively select hunks from the diff\n-between HEAD and the working tree to be stashed.  The stash entry is\n-constructed such that its index state is the same as the index state\n-of your repository, and its worktree contains only the changes you\n-selected interactively.  The selected changes are then rolled back\n-from your worktree. See the ``Interactive Mode'' section of\n-linkgit:git-add[1] to learn how to operate the `--patch` mode.\n+-p::\n+--patch::\n+\tThis option is only valid for `push` and `save` commands.\n++\n+Interactively select hunks from the diff between HEAD and the\n+working tree to be stashed.  The stash entry is constructed such\n+that its index state is the same as the index state of your\n+repository, and its worktree contains only the changes you selected\n+interactively.  The selected changes are then rolled back from your\n+worktree. See the ``Interactive Mode'' section of linkgit:git-add[1]\n+to learn how to operate the `--patch` mode.\n +\n The `--patch` option implies `--keep-index`.  You can use \n `--no-keep-index` to override this.\n \n-When pathspec is given to 'git stash push', the new stash entry records the\n-modified states only for the files that match the pathspec.  The index\n-entries and working tree files are then rolled back to the state in\n-HEAD only for these files, too, leaving files that do not match the\n-pathspec intact.\n-\n-When no `<stash>` is given, `stash@{0}` is assumed, otherwise `<stash>` must\n-be a reference of the form `stash@{<revision>}`.\n+<pathspec>...::\n+\tThis option is only valid for `push` command.\n++\n+The new stash entry records the modified states only for the files\n+that match the pathspec.  The index entries and working tree files\n+are then rolled back to the state in HEAD only for these files,\n+too, leaving files that do not match the pathspec intact.\n+\n+<stash>::\n+\tThis option is only valid for `apply`, `branch`, `drop`, `pop`,\n+\t`show` commands.\n++\n+A reference of the form `stash@{<revision>}`. When no `<stash>` is\n+given, the latest stash is assumed (that is, `stash@{0}`).\n \n DISCUSSION\n ----------\n-- \ngitgitgadget\n\n"},{"id":"391936","messageId":"d34eaf4a27236591a9bc345d5a2cb465542a3243.1581960322.git.gitgitgadget@gmail.com","threadId":"52648","inReplyTo":"pull.530.v3.git.1581960322.gitgitgadget@gmail.com","subject":"[PATCH v3 7/8] stash: eliminate crude option parsing","fromName":"Alexandr Miloslavskiy via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-02-17T17:25:21Z","receivedAt":"2020-02-17T17:25:36Z","isPatch":true,"sender":{"key":"alexandr.miloslavskiy@syntevo.com","avatar":null},"body":"From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n\nEliminate crude option parsing and rely on real parsing instead, because\n1) Crude parsing is crude, for example it's not capable of\n   handling things like `git stash -m Message`\n2) Adding options in two places is inconvenient and prone to bugs\n\nAs a side result, the case of `git stash -m Message` gets fixed.\nAlso give a good error message instead of just throwing usage at user.\n\n----\n\nSome review of what's been happening to this code:\n\nBefore [1], `git-stash.sh` only verified that all args begin with `-` :\n\n\t# The default command is \"push\" if nothing but options are given\n\tseen_non_option=\n\tfor opt\n\tdo\n\t\tcase \"$opt\" in\n\t\t--) break ;;\n\t\t-*) ;;\n\t\t*) seen_non_option=t; break ;;\n\t\tesac\n\tdone\n\nLater, [1] introduced the duplicate code I'm now removing, also making\nthe previous test more strict by white-listing options.\n\n----\n\n[1] Commit 40af1468 (\"stash: convert `stash--helper.c` into `stash.c`\" 2019-02-26)\n\nSigned-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n---\n builtin/stash.c  | 59 +++++++++++++++++-------------------------------\n t/t3903-stash.sh |  5 ++++\n 2 files changed, 26 insertions(+), 38 deletions(-)\n\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex 879fc5f3683..ed84ff2e168 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -1451,8 +1451,10 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q\n \treturn ret;\n }\n \n-static int push_stash(int argc, const char **argv, const char *prefix)\n+static int push_stash(int argc, const char **argv, const char *prefix,\n+\t\t      int push_assumed)\n {\n+\tint force_assume = 0;\n \tint keep_index = -1;\n \tint patch_mode = 0;\n \tint include_untracked = 0;\n@@ -1474,10 +1476,22 @@ static int push_stash(int argc, const char **argv, const char *prefix)\n \t\tOPT_END()\n \t};\n \n-\tif (argc)\n+\tif (argc) {\n+\t\tforce_assume = !strcmp(argv[0], \"-p\");\n \t\targc = parse_options(argc, argv, prefix, options,\n \t\t\t\t     git_stash_push_usage,\n-\t\t\t\t     0);\n+\t\t\t\t     PARSE_OPT_KEEP_DASHDASH);\n+\t}\n+\n+\tif (argc) {\n+\t\tif (!strcmp(argv[0], \"--\")) {\n+\t\t\targc--;\n+\t\t\targv++;\n+\t\t} else if (push_assumed && !force_assume) {\n+\t\t\tdie(\"subcommand wasn't specified; 'push' can't be assumed due to unexpected token '%s'\",\n+\t\t\t    argv[0]);\n+\t\t}\n+\t}\n \n \tparse_pathspec(&ps, 0, PATHSPEC_PREFER_FULL | PATHSPEC_PREFIX_ORIGIN,\n \t\t       prefix, argv);\n@@ -1550,7 +1564,6 @@ static int use_builtin_stash(void)\n \n int cmd_stash(int argc, const char **argv, const char *prefix)\n {\n-\tint i = -1;\n \tpid_t pid = getpid();\n \tconst char *index_file;\n \tstruct argv_array args = ARGV_ARRAY_INIT;\n@@ -1583,7 +1596,7 @@ int cmd_stash(int argc, const char **argv, const char *prefix)\n \t\t    (uintmax_t)pid);\n \n \tif (!argc)\n-\t\treturn !!push_stash(0, NULL, prefix);\n+\t\treturn !!push_stash(0, NULL, prefix, 0);\n \telse if (!strcmp(argv[0], \"apply\"))\n \t\treturn !!apply_stash(argc, argv, prefix);\n \telse if (!strcmp(argv[0], \"clear\"))\n@@ -1603,45 +1616,15 @@ int cmd_stash(int argc, const char **argv, const char *prefix)\n \telse if (!strcmp(argv[0], \"create\"))\n \t\treturn !!create_stash(argc, argv, prefix);\n \telse if (!strcmp(argv[0], \"push\"))\n-\t\treturn !!push_stash(argc, argv, prefix);\n+\t\treturn !!push_stash(argc, argv, prefix, 0);\n \telse if (!strcmp(argv[0], \"save\"))\n \t\treturn !!save_stash(argc, argv, prefix);\n \telse if (*argv[0] != '-')\n \t\tusage_msg_opt(xstrfmt(_(\"unknown subcommand: %s\"), argv[0]),\n \t\t\t      git_stash_usage, options);\n \n-\tif (strcmp(argv[0], \"-p\")) {\n-\t\twhile (++i < argc && strcmp(argv[i], \"--\")) {\n-\t\t\t/*\n-\t\t\t * `akpqu` is a string which contains all short options,\n-\t\t\t * except `-m` which is verified separately.\n-\t\t\t */\n-\t\t\tif ((strlen(argv[i]) == 2) && *argv[i] == '-' &&\n-\t\t\t    strchr(\"akpqu\", argv[i][1]))\n-\t\t\t\tcontinue;\n-\n-\t\t\tif (!strcmp(argv[i], \"--all\") ||\n-\t\t\t    !strcmp(argv[i], \"--keep-index\") ||\n-\t\t\t    !strcmp(argv[i], \"--no-keep-index\") ||\n-\t\t\t    !strcmp(argv[i], \"--patch\") ||\n-\t\t\t    !strcmp(argv[i], \"--quiet\") ||\n-\t\t\t    !strcmp(argv[i], \"--include-untracked\"))\n-\t\t\t\tcontinue;\n-\n-\t\t\t/*\n-\t\t\t * `-m` and `--message=` are verified separately because\n-\t\t\t * they need to be immediately followed by a string\n-\t\t\t * (i.e.`-m\"foobar\"` or `--message=\"foobar\"`).\n-\t\t\t */\n-\t\t\tif (starts_with(argv[i], \"-m\") ||\n-\t\t\t    starts_with(argv[i], \"--message=\"))\n-\t\t\t\tcontinue;\n-\n-\t\t\tusage_with_options(git_stash_usage, options);\n-\t\t}\n-\t}\n-\n+\t/* Assume 'stash push' */\n \targv_array_push(&args, \"push\");\n \targv_array_pushv(&args, argv);\n-\treturn !!push_stash(args.argc, args.argv, prefix);\n+\treturn !!push_stash(args.argc, args.argv, prefix, 1);\n }\ndiff --git a/t/t3903-stash.sh b/t/t3903-stash.sh\nindex ea56e85e70d..3ad23e2502b 100755\n--- a/t/t3903-stash.sh\n+++ b/t/t3903-stash.sh\n@@ -285,6 +285,11 @@ test_expect_success 'stash --no-keep-index' '\n \ttest bar,bar2 = $(cat file),$(cat file2)\n '\n \n+test_expect_success 'dont assume push with non-option args' '\n+\ttest_must_fail git stash -q drop 2>err &&\n+\ttest_i18ngrep -e \"subcommand wasn'\\''t specified; '\\''push'\\'' can'\\''t be assumed due to unexpected token '\\''drop'\\''\" err\n+'\n+\n test_expect_success 'stash --invalid-option' '\n \techo bar5 >file &&\n \techo bar6 >file2 &&\n-- \ngitgitgadget\n\n"},{"id":"391934","messageId":"7353b06e30e6c3d14df0f598489fa55e8c991f26.1581960322.git.gitgitgadget@gmail.com","threadId":"52648","inReplyTo":"pull.530.v3.git.1581960322.gitgitgadget@gmail.com","subject":"[PATCH v3 6/8] doc: stash: synchronize <pathspec> description","fromName":"Alexandr Miloslavskiy via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2020-02-17T17:25:20Z","receivedAt":"2020-02-17T17:25:39Z","isPatch":true,"sender":{"key":"alexandr.miloslavskiy@syntevo.com","avatar":null},"body":"From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n\nThis patch continues the effort that is already applied to\n`git commit`, `git reset`, `git checkout` etc.\n\n1) Added reference to 'linkgit:gitglossary[7]'.\n2) Fixed mentions of incorrectly plural \"pathspecs\".\n\nSigned-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com>\n---\n Documentation/git-stash.txt | 6 ++++--\n 1 file changed, 4 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-stash.txt b/Documentation/git-stash.txt\nindex 6dc0a5b0eef..52e64985bda 100644\n--- a/Documentation/git-stash.txt\n+++ b/Documentation/git-stash.txt\n@@ -56,13 +56,13 @@ push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q\n For quickly making a snapshot, you can omit \"push\".  In this mode,\n non-option arguments are not allowed to prevent a misspelled\n subcommand from making an unwanted stash entry.  The two exceptions to this\n-are `stash -p` which acts as alias for `stash push -p` and pathspecs,\n+are `stash -p` which acts as alias for `stash push -p` and pathspec elements,\n which are allowed after a double hyphen `--` for disambiguation.\n \n save [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [<message>]::\n \n \tThis option is deprecated in favour of 'git stash push'.  It\n-\tdiffers from \"stash push\" in that it cannot take pathspecs.\n+\tdiffers from \"stash push\" in that it cannot take pathspec.\n \tInstead, all non-option arguments are concatenated to form the stash\n \tmessage.\n \n@@ -213,6 +213,8 @@ The new stash entry records the modified states only for the files\n that match the pathspec.  The index entries and working tree files\n are then rolled back to the state in HEAD only for these files,\n too, leaving files that do not match the pathspec intact.\n++\n+For more details, see the 'pathspec' entry in linkgit:gitglossary[7].\n \n <stash>::\n \tThis option is only valid for `apply`, `branch`, `drop`, `pop`,\n-- \ngitgitgadget\n\n"},{"id":"391937","messageId":"994b082b-cd86-a7cb-f70c-1753ad988abb@syntevo.com","threadId":"52648","inReplyTo":"xmqq4kvyyy5d.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v2 2/8] rm: support the --pathspec-from-file option","fromName":"Alexandr Miloslavskiy","fromEmail":"alexandr.miloslavskiy@syntevo.com","sentAt":"2020-02-17T17:27:12Z","receivedAt":"2020-02-17T17:27:18Z","isPatch":true,"sender":{"key":"alexandr.miloslavskiy@syntevo.com","avatar":null},"body":"On 10.02.2020 21:39, Junio C Hamano wrote:\n> Trailing whitespace on this line.\n\nWhitespaces fixed in V3; I have also activated pre-commit hook. Sorry!\n"},{"id":"391939","messageId":"8e24eab8-fc75-d676-8b11-ee01355813c4@syntevo.com","threadId":"52648","inReplyTo":"xmqqd0amz3ai.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH 2/8] rm: support the --pathspec-from-file option","fromName":"Alexandr Miloslavskiy","fromEmail":"alexandr.miloslavskiy@syntevo.com","sentAt":"2020-02-17T17:25:42Z","receivedAt":"2020-02-17T17:40:17Z","isPatch":true,"sender":{"key":"alexandr.miloslavskiy@syntevo.com","avatar":null},"body":"On 10.02.2020 19:48, Junio C Hamano wrote:\n> I expected that the proposed log message to explain and justify why\n> a change (in behaviour, in design, etc.) is made.  \"There is this\n> limitation\" is not a justification---\"because of such and such\n> reasons, there is this limitation, otherwise such and such bad\n> things happen\" is.\n\nI have rewritten the commit message in V3. Is that better?\n"},{"id":"391941","messageId":"xmqqmu9hksby.fsf@gitster-ct.c.googlers.com","threadId":"52648","inReplyTo":"994b082b-cd86-a7cb-f70c-1753ad988abb@syntevo.com","subject":"Re: [PATCH v2 2/8] rm: support the --pathspec-from-file option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-02-17T17:59:45Z","receivedAt":"2020-02-17T17:59:50Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> writes:\n\n> On 10.02.2020 21:39, Junio C Hamano wrote:\n>> Trailing whitespace on this line.\n>\n> Whitespaces fixed in V3; I have also activated pre-commit hook. Sorry!\n\nI may not have time to read it over at least in a few days, but lack\nif v3 in the title will make it cumbersome to come back to the\nthread later X-<.  Thanks for a heads-up, anyway, thoguh.\n\n"},{"id":"391945","messageId":"20667c6b-ae49-1629-b063-05aec96c064f@syntevo.com","threadId":"52648","inReplyTo":"7c657dea89e900a023af1130545f6c3341841840.1581960322.git.gitgitgadget@gmail.com","subject":"Re: [PATCH v3 2/8] rm: support the --pathspec-from-file option","fromName":"Alexandr Miloslavskiy","fromEmail":"alexandr.miloslavskiy@syntevo.com","sentAt":"2020-02-17T18:06:37Z","receivedAt":"2020-02-17T18:06:40Z","isPatch":true,"sender":{"key":"alexandr.miloslavskiy@syntevo.com","avatar":null},"body":"On 17.02.2020 18:59, Junio C Hamano wrote:\n > I may not have time to read it over at least in a few days, but lack\n > if v3 in the title will make it cumbersome to come back to the\n > thread later X-<.  Thanks for a heads-up, anyway, thoguh.\n\nHere's the V3 in title :)\n"},{"id":"391953","messageId":"xmqqa75hkjt1.fsf@gitster-ct.c.googlers.com","threadId":"52648","inReplyTo":"xmqqmu9hksby.fsf@gitster-ct.c.googlers.com","subject":"Re: [PATCH v2 2/8] rm: support the --pathspec-from-file option","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2020-02-17T21:03:54Z","receivedAt":"2020-02-17T21:03:59Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> I may not have time to read it over at least in a few days, but lack\n> if v3 in the title will make it cumbersome to come back ...\n\nOops, please disregard.  I must have been looking at some wrong\nthread.\n"}]}