* [PATCH 0/5] Add --pathspec-from-file option for reset, commit @ 2019-11-04 19:26 Alexandr Miloslavskiy via GitGitGadget 2019-11-04 19:26 ` [PATCH 1/5] pathspec: add new function to parse file Alexandr Miloslavskiy via GitGitGadget ` (5 more replies) 0 siblings, 6 replies; 80+ messages in thread From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-04 19:26 UTC (permalink / raw) To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano Adds --pathspec-from-file option for porcelain commands to avoid commandline length limit. So far I implemented it for git commit and git reset, but my goal is to support other commands as well after these patches are reviewed. The patches are based on the following discussions: https://public-inbox.org/git/c3be6eff-365b-96b8-16d2-0528612fc1fc@syntevo.com/T/#u There, --stdin-paths was suggested. https://public-inbox.org/git/a38bc928-7ccd-e2d9-b89b-23298e9fa95d@syntevo.com/T/#u There, --stdin-paths was extended to --paths-file. https://public-inbox.org/git/pull.133.git.gitgitgadget@gmail.com/ https://github.com/gitgitgadget/git/pull/133Patch from Johannes, I used it as base of my patch. There, --pathspec-from-file is suggested in discussion. Major changes compared to patch from Johannes: 1) --pathspec-from-file allows to read file, not just stdin 2) --literal-pathspecs should be honored. This is a good improvement because it keeps --pathspec-from-file much closer to passing args on commandline. Since the goal of the new option is to avoid commandline length limit, both behaviors should be close to each other. 3) Patches are designed with all other git commands in mind Alexandr Miloslavskiy (5): pathspec: add new function to parse file doc: reset: unify <pathspec> description reset: support the --pathspec-from-file option doc: commit: unify <pathspec> description commit: support the --pathspec-from-file option Documentation/git-commit.txt | 19 ++++- Documentation/git-reset.txt | 40 +++++++--- builtin/commit.c | 25 ++++++- builtin/reset.c | 22 +++++- pathspec.c | 41 +++++++++++ pathspec.h | 10 +++ t/t7107-reset-pathspec-file.sh | 126 ++++++++++++++++++++++++++++++++ t/t7526-commit-pathspec-file.sh | 107 +++++++++++++++++++++++++++ 8 files changed, 371 insertions(+), 19 deletions(-) create mode 100755 t/t7107-reset-pathspec-file.sh create mode 100755 t/t7526-commit-pathspec-file.sh base-commit: da72936f544fec5a335e66432610e4cef4430991 Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-445%2FSyntevoAlex%2F%230207_pathspec_from_file-v1 Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-445/SyntevoAlex/#0207_pathspec_from_file-v1 Pull-Request: https://github.com/gitgitgadget/git/pull/445 -- gitgitgadget ^ permalink raw reply [flat|nested] 80+ messages in thread
* [PATCH 1/5] pathspec: add new function to parse file 2019-11-04 19:26 [PATCH 0/5] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget @ 2019-11-04 19:26 ` Alexandr Miloslavskiy via GitGitGadget 2019-11-05 15:02 ` Phillip Wood 2019-11-04 19:26 ` [PATCH 2/5] doc: reset: unify <pathspec> description Alexandr Miloslavskiy via GitGitGadget ` (4 subsequent siblings) 5 siblings, 1 reply; 80+ messages in thread From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-04 19:26 UTC (permalink / raw) To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> This will be used to support the new option '--pathspec-from-file' in `git add`, `git-commit`, `git reset` etc. Note also that we specifically handle CR/LF line endings to support Windows better. To simplify code, file is first parsed into `argv_array`. This allows to avoid refactoring `parse_pathspec()`. I considered adding `nul_term_line` to `flags` instead, but decided that it doesn't fit there. The new code is mostly taken from `cmd_update_index()` and `split_mail_conv()`. Co-authored-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> --- pathspec.c | 41 +++++++++++++++++++++++++++++++++++++++++ pathspec.h | 10 ++++++++++ 2 files changed, 51 insertions(+) diff --git a/pathspec.c b/pathspec.c index 12c2b322b3..97d4e77875 100644 --- a/pathspec.c +++ b/pathspec.c @@ -3,6 +3,8 @@ #include "dir.h" #include "pathspec.h" #include "attr.h" +#include "argv-array.h" +#include "quote.h" /* * Finds which of the given pathspecs match items in the index. @@ -613,6 +615,45 @@ void parse_pathspec(struct pathspec *pathspec, } } +void parse_pathspec_file(struct pathspec *pathspec, unsigned magic_mask, + unsigned flags, const char *prefix, + const char *file, int nul_term_line) +{ + struct argv_array parsed_file = ARGV_ARRAY_INIT; + strbuf_getline_fn getline_fn = nul_term_line ? strbuf_getline_nul : + strbuf_getline; + struct strbuf buf = STRBUF_INIT; + struct strbuf unquoted = STRBUF_INIT; + FILE *in = NULL; + + if (!strcmp(file, "-")) + in = stdin; + else + in = fopen(file, "r"); + + if (!in) + die(_("could not open '%s' for reading"), file); + + while (getline_fn(&buf, in) != EOF) { + if (!nul_term_line && buf.buf[0] == '"') { + strbuf_reset(&unquoted); + if (unquote_c_style(&unquoted, buf.buf, NULL)) + die(_("line is badly quoted")); + strbuf_swap(&buf, &unquoted); + } + argv_array_push(&parsed_file, buf.buf); + strbuf_reset(&buf); + } + + strbuf_release(&unquoted); + strbuf_release(&buf); + if (in != stdin) + fclose(in); + + parse_pathspec(pathspec, magic_mask, flags, prefix, parsed_file.argv); + argv_array_clear(&parsed_file); +} + void copy_pathspec(struct pathspec *dst, const struct pathspec *src) { int i, j; diff --git a/pathspec.h b/pathspec.h index 1c18a2c90c..d72e0b4c4a 100644 --- a/pathspec.h +++ b/pathspec.h @@ -85,6 +85,16 @@ void parse_pathspec(struct pathspec *pathspec, unsigned flags, const char *prefix, const char **args); +/* + * Same as parse_pathspec() but uses file as input. + * When 'file' is exactly "-" it uses 'stdin' instead. + */ +void parse_pathspec_file(struct pathspec *pathspec, + unsigned magic_mask, + unsigned flags, + const char *prefix, + const char *file, + int nul_term_line); void copy_pathspec(struct pathspec *dst, const struct pathspec *src); void clear_pathspec(struct pathspec *); -- gitgitgadget ^ permalink raw reply related [flat|nested] 80+ messages in thread
* Re: [PATCH 1/5] pathspec: add new function to parse file 2019-11-04 19:26 ` [PATCH 1/5] pathspec: add new function to parse file Alexandr Miloslavskiy via GitGitGadget @ 2019-11-05 15:02 ` Phillip Wood 2019-11-05 19:14 ` Alexandr Miloslavskiy 2019-11-06 15:56 ` Alexandr Miloslavskiy 0 siblings, 2 replies; 80+ messages in thread From: Phillip Wood @ 2019-11-05 15:02 UTC (permalink / raw) To: Alexandr Miloslavskiy via GitGitGadget, git Cc: Alexandr Miloslavskiy, Junio C Hamano Hi Alexandr Thanks for working on this. I've got a couple of comments about improving the error messages but this looks fine to me On 04/11/2019 19:26, Alexandr Miloslavskiy via GitGitGadget wrote: > From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> > > This will be used to support the new option '--pathspec-from-file' in > `git add`, `git-commit`, `git reset` etc. > > Note also that we specifically handle CR/LF line endings to support > Windows better. > > To simplify code, file is first parsed into `argv_array`. This allows > to avoid refactoring `parse_pathspec()`. > > I considered adding `nul_term_line` to `flags` instead, but decided > that it doesn't fit there. > > The new code is mostly taken from `cmd_update_index()` and > `split_mail_conv()`. > > Co-authored-by: Johannes Schindelin <johannes.schindelin@gmx.de> > Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> > --- > pathspec.c | 41 +++++++++++++++++++++++++++++++++++++++++ > pathspec.h | 10 ++++++++++ > 2 files changed, 51 insertions(+) > > diff --git a/pathspec.c b/pathspec.c > index 12c2b322b3..97d4e77875 100644 > --- a/pathspec.c > +++ b/pathspec.c > @@ -3,6 +3,8 @@ > #include "dir.h" > #include "pathspec.h" > #include "attr.h" > +#include "argv-array.h" > +#include "quote.h" > > /* > * Finds which of the given pathspecs match items in the index. > @@ -613,6 +615,45 @@ void parse_pathspec(struct pathspec *pathspec, > } > } > > +void parse_pathspec_file(struct pathspec *pathspec, unsigned magic_mask, > + unsigned flags, const char *prefix, > + const char *file, int nul_term_line) > +{ > + struct argv_array parsed_file = ARGV_ARRAY_INIT; > + strbuf_getline_fn getline_fn = nul_term_line ? strbuf_getline_nul : > + strbuf_getline; > + struct strbuf buf = STRBUF_INIT; > + struct strbuf unquoted = STRBUF_INIT; > + FILE *in = NULL; > + > + if (!strcmp(file, "-")) > + in = stdin; > + else > + in = fopen(file, "r"); > + > + if (!in) > + die(_("could not open '%s' for reading"), file); die_errno() would give a more informative message here > + while (getline_fn(&buf, in) != EOF) { > + if (!nul_term_line && buf.buf[0] == '"') { > + strbuf_reset(&unquoted); > + if (unquote_c_style(&unquoted, buf.buf, NULL)) > + die(_("line is badly quoted")); It would be nice to show the offending line in the error message > + strbuf_swap(&buf, &unquoted); > + } > + argv_array_push(&parsed_file, buf.buf); > + strbuf_reset(&buf); > + } > + > + strbuf_release(&unquoted); > + strbuf_release(&buf); > + if (in != stdin) > + fclose(in); > + > + parse_pathspec(pathspec, magic_mask, flags, prefix, parsed_file.argv); > + argv_array_clear(&parsed_file); > +} > + > void copy_pathspec(struct pathspec *dst, const struct pathspec *src) > { > int i, j; > diff --git a/pathspec.h b/pathspec.h > index 1c18a2c90c..d72e0b4c4a 100644 > --- a/pathspec.h > +++ b/pathspec.h > @@ -85,6 +85,16 @@ void parse_pathspec(struct pathspec *pathspec, > unsigned flags, > const char *prefix, > const char **args); > +/* > + * Same as parse_pathspec() but uses file as input. > + * When 'file' is exactly "-" it uses 'stdin' instead. > + */ > +void parse_pathspec_file(struct pathspec *pathspec, > + unsigned magic_mask, > + unsigned flags, > + const char *prefix, > + const char *file, > + int nul_term_line); Do these align with the 's' in "struct pathspec" ? Best Wishes Phillip > void copy_pathspec(struct pathspec *dst, const struct pathspec *src); > void clear_pathspec(struct pathspec *); > > ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 1/5] pathspec: add new function to parse file 2019-11-05 15:02 ` Phillip Wood @ 2019-11-05 19:14 ` Alexandr Miloslavskiy 2019-11-06 15:56 ` Alexandr Miloslavskiy 1 sibling, 0 replies; 80+ messages in thread From: Alexandr Miloslavskiy @ 2019-11-05 19:14 UTC (permalink / raw) To: phillip.wood, Alexandr Miloslavskiy via GitGitGadget, git; +Cc: Junio C Hamano On 05.11.2019 16:02, Phillip Wood wrote: >> + if (!in) >> + die(_("could not open '%s' for reading"), file); > > die_errno() would give a more informative message here Thanks for the pointer! >> + if (unquote_c_style(&unquoted, buf.buf, NULL)) >> + die(_("line is badly quoted")); > > It would be nice to show the offending line in the error message Good idea. >> +void parse_pathspec_file(struct pathspec *pathspec, >> + unsigned magic_mask, >> + unsigned flags, >> + const char *prefix, >> + const char *file, >> + int nul_term_line); > > Do these align with the 's' in "struct pathspec" ? Sorry, still struggling with my VS that tries to do crazy things. Will fix. ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 1/5] pathspec: add new function to parse file 2019-11-05 15:02 ` Phillip Wood 2019-11-05 19:14 ` Alexandr Miloslavskiy @ 2019-11-06 15:56 ` Alexandr Miloslavskiy 1 sibling, 0 replies; 80+ messages in thread From: Alexandr Miloslavskiy @ 2019-11-06 15:56 UTC (permalink / raw) To: phillip.wood, Alexandr Miloslavskiy via GitGitGadget, git; +Cc: Junio C Hamano I think I have implemented all suggestions in PatchV2. Thanks! ^ permalink raw reply [flat|nested] 80+ messages in thread
* [PATCH 2/5] doc: reset: unify <pathspec> description 2019-11-04 19:26 [PATCH 0/5] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget 2019-11-04 19:26 ` [PATCH 1/5] pathspec: add new function to parse file Alexandr Miloslavskiy via GitGitGadget @ 2019-11-04 19:26 ` Alexandr Miloslavskiy via GitGitGadget 2019-11-06 4:01 ` Junio C Hamano 2019-11-04 19:26 ` [PATCH 3/5] reset: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget ` (3 subsequent siblings) 5 siblings, 1 reply; 80+ messages in thread From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-04 19:26 UTC (permalink / raw) To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> Synchronize it to `git add`, which has a pretty good description. Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> --- Documentation/git-reset.txt | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt index 97e0544d9e..b0ea6e0ce5 100644 --- a/Documentation/git-reset.txt +++ b/Documentation/git-reset.txt @@ -8,8 +8,8 @@ git-reset - Reset current HEAD to the specified state SYNOPSIS -------- [verse] -'git reset' [-q] [<tree-ish>] [--] <paths>... -'git reset' (--patch | -p) [<tree-ish>] [--] [<paths>...] +'git reset' [-q] [<tree-ish>] [--] <pathspec>... +'git reset' (--patch | -p) [<tree-ish>] [--] [<pathspec>...] 'git reset' [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>] DESCRIPTION @@ -19,27 +19,33 @@ In the third form, set the current branch head (`HEAD`) to `<commit>`, optionally modifying index and working tree to match. The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms. -'git reset' [-q] [<tree-ish>] [--] <paths>...:: - This form resets the index entries for all `<paths>` to their +'git reset' [-q] [<tree-ish>] [--] <pathspec>...:: + This form resets the index entries for all `<pathspec>` to their state at `<tree-ish>`. (It does not affect the working tree or the current branch.) + -This means that `git reset <paths>` is the opposite of `git add -<paths>`. This command is equivalent to -`git restore [--source=<tree-ish>] --staged <paths>...`. +For more details about the <pathspec> syntax, see the 'pathspec' entry +in linkgit:gitglossary[7]. + -After running `git reset <paths>` to update the index entry, you can +This means that `git reset <pathspec>` is the opposite of `git add +<pathspec>`. This command is equivalent to +`git restore [--source=<tree-ish>] --staged <pathspec>...`. ++ +After running `git reset <pathspec>` to update the index entry, you can use linkgit:git-restore[1] to check the contents out of the index to the working tree. Alternatively, using linkgit:git-restore[1] and specifying a commit with `--source`, you can copy the contents of a path out of a commit to the index and to the working tree in one go. -'git reset' (--patch | -p) [<tree-ish>] [--] [<paths>...]:: +'git reset' (--patch | -p) [<tree-ish>] [--] [<pathspec>...]:: Interactively select hunks in the difference between the index and `<tree-ish>` (defaults to `HEAD`). The chosen hunks are applied in reverse to the index. + +For more details about the <pathspec> syntax, see the 'pathspec' entry +in linkgit:gitglossary[7]. ++ This means that `git reset -p` is the opposite of `git add -p`, i.e. you can use it to selectively reset hunks. See the ``Interactive Mode'' section of linkgit:git-add[1] to learn how to operate the `--patch` mode. -- gitgitgadget ^ permalink raw reply related [flat|nested] 80+ messages in thread
* Re: [PATCH 2/5] doc: reset: unify <pathspec> description 2019-11-04 19:26 ` [PATCH 2/5] doc: reset: unify <pathspec> description Alexandr Miloslavskiy via GitGitGadget @ 2019-11-06 4:01 ` Junio C Hamano 2019-11-06 15:56 ` Alexandr Miloslavskiy 0 siblings, 1 reply; 80+ messages in thread From: Junio C Hamano @ 2019-11-06 4:01 UTC (permalink / raw) To: Alexandr Miloslavskiy via GitGitGadget; +Cc: git, Alexandr Miloslavskiy "Alexandr Miloslavskiy via GitGitGadget" <gitgitgadget@gmail.com> writes: > optionally modifying index and working tree to match. > The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms. > > -'git reset' [-q] [<tree-ish>] [--] <paths>...:: > - This form resets the index entries for all `<paths>` to their > +'git reset' [-q] [<tree-ish>] [--] <pathspec>...:: > + This form resets the index entries for all `<pathspec>` to their > state at `<tree-ish>`. (It does not affect the working tree or > the current branch.) > + > -This means that `git reset <paths>` is the opposite of `git add > -<paths>`. This command is equivalent to > -`git restore [--source=<tree-ish>] --staged <paths>...`. > +For more details about the <pathspec> syntax, see the 'pathspec' entry > +in linkgit:gitglossary[7]. > + > -After running `git reset <paths>` to update the index entry, you can > +This means that `git reset <pathspec>` is the opposite of `git add It is good to show which document to read more on <pathspec>, but inserting it just before "This means..." breaks the flow of thought. As we introduce/explain tree-ish and commit used in the synopsis in the ealier part of the description, it probably is a much better place to also introduce/explain pathspec, perhaps like so: In the first and second form, ... In the third form, ... The <tree-ish>/<commit> defaults to HEAD in all forms. +The <pathspec> is used to limit the paths affected by the +operation in the first two forms (see the entry for +'pathspec' in linkgit:gitglossary[7] for more details). > +This means that `git reset <pathspec>` is the opposite of `git add > +<pathspec>`. This command is equivalent to > +`git restore [--source=<tree-ish>] --staged <pathspec>...`. Any time I see "... X. This means Y." either in the doc or in the proposed log message, I wish the author (not you in this case, obviously) thought twice about rewriting so that they do not say one thing and immediately have to rephrase it, i.e. either just say Y without saying X, or saying X more clearly without having to say Y. In this case, however, I think X and Y are related but both relevant. The subcommand resets the index entries for chosen paths to match what is in the tree-ish, which is the same as restoring from a tree to the index. It is not quite the opposite of adding to the index from the working tree. In this sequence: $ edit newfile $ git add newfile and then further $ edit newfile $ git add newfile $ git reset -- newfile we are taken back to the state _before_ any of the changes made to newfile (in fact, since HEAD does not have newfile, the resulting index would not know about it, either). Thanks. ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 2/5] doc: reset: unify <pathspec> description 2019-11-06 4:01 ` Junio C Hamano @ 2019-11-06 15:56 ` Alexandr Miloslavskiy 2019-11-07 5:46 ` Junio C Hamano 0 siblings, 1 reply; 80+ messages in thread From: Alexandr Miloslavskiy @ 2019-11-06 15:56 UTC (permalink / raw) To: Junio C Hamano, Alexandr Miloslavskiy via GitGitGadget; +Cc: git I think I have implemented most suggestions in PatchV2. Thanks! > Any time I see "... X. This means Y." either in the doc or in the > proposed log message, I wish the author (not you in this case, > obviously) thought twice about rewriting so that they do not say one > thing and immediately have to rephrase it, i.e. either just say Y > without saying X, or saying X more clearly without having to say Y. > > In this case, however, I think X and Y are related but both relevant. > The subcommand resets the index entries for chosen paths to match > what is in the tree-ish, which is the same as restoring from a tree > to the index. > > It is not quite the opposite of adding to the index from the working > tree. In this sequence: > > $ edit newfile > $ git add newfile > > and then further > > $ edit newfile > $ git add newfile > $ git reset -- newfile > > we are taken back to the state _before_ any of the changes made to > newfile (in fact, since HEAD does not have newfile, the resulting > index would not know about it, either). I am a bit confused, is it correct that you don't expect me to change my patches? ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 2/5] doc: reset: unify <pathspec> description 2019-11-06 15:56 ` Alexandr Miloslavskiy @ 2019-11-07 5:46 ` Junio C Hamano 2019-11-07 11:05 ` Alexandr Miloslavskiy 0 siblings, 1 reply; 80+ messages in thread From: Junio C Hamano @ 2019-11-07 5:46 UTC (permalink / raw) To: Alexandr Miloslavskiy; +Cc: Alexandr Miloslavskiy via GitGitGadget, git Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> writes: >> It is not quite the opposite of adding to the index from the working >> tree. In this sequence: >> >> $ edit newfile >> $ git add newfile >> >> and then further >> >> $ edit newfile >> $ git add newfile >> $ git reset -- newfile >> >> we are taken back to the state _before_ any of the changes made to >> newfile (in fact, since HEAD does not have newfile, the resulting >> index would not know about it, either). > > I am a bit confused, is it correct that you don't expect me to change > my patches? I do not know if removal of the only-half-correct "This is the opposite of add" should be part of this change, or it should be a separate fix. The half-wrong sentene was not introduced by this patch, so leaving it as-is is OK. It just leaves another thing for us to think about later. Thanks. ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 2/5] doc: reset: unify <pathspec> description 2019-11-07 5:46 ` Junio C Hamano @ 2019-11-07 11:05 ` Alexandr Miloslavskiy 2019-11-08 3:04 ` Junio C Hamano 0 siblings, 1 reply; 80+ messages in thread From: Alexandr Miloslavskiy @ 2019-11-07 11:05 UTC (permalink / raw) To: Junio C Hamano; +Cc: Alexandr Miloslavskiy via GitGitGadget, git On 07.11.2019 6:46, Junio C Hamano wrote: > I do not know if removal of the only-half-correct "This is the > opposite of add" should be part of this change, or it should be a > separate fix. The half-wrong sentene was not introduced by this > patch, so leaving it as-is is OK. It just leaves another thing for > us to think about later. My feeling is that this problem is separate from the topic I'm working on. I only touched docs to synchronize <pathspec>, so that I can copy&paste new option description in next commit without tailoring it to local speech. Also, I must admit that upon reading your explanation, I felt that I lack the experience to update the writing properly. ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 2/5] doc: reset: unify <pathspec> description 2019-11-07 11:05 ` Alexandr Miloslavskiy @ 2019-11-08 3:04 ` Junio C Hamano 0 siblings, 0 replies; 80+ messages in thread From: Junio C Hamano @ 2019-11-08 3:04 UTC (permalink / raw) To: Alexandr Miloslavskiy; +Cc: Alexandr Miloslavskiy via GitGitGadget, git Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> writes: > On 07.11.2019 6:46, Junio C Hamano wrote: >> I do not know if removal of the only-half-correct "This is the >> opposite of add" should be part of this change, or it should be a >> separate fix. The half-wrong sentene was not introduced by this >> patch, so leaving it as-is is OK. It just leaves another thing for >> us to think about later. > > My feeling is that this problem is separate from the topic I'm working > on. I only touched docs to synchronize <pathspec>, so that I can > copy&paste new option description in next commit without tailoring it > to local speech. Also, I must admit that upon reading your > explanation, I felt that I lack the experience to update the writing > properly. Yeah, I do think it is cleaner to leave it as a separate issue. Thanks. ^ permalink raw reply [flat|nested] 80+ messages in thread
* [PATCH 3/5] reset: support the --pathspec-from-file option 2019-11-04 19:26 [PATCH 0/5] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget 2019-11-04 19:26 ` [PATCH 1/5] pathspec: add new function to parse file Alexandr Miloslavskiy via GitGitGadget 2019-11-04 19:26 ` [PATCH 2/5] doc: reset: unify <pathspec> description Alexandr Miloslavskiy via GitGitGadget @ 2019-11-04 19:26 ` Alexandr Miloslavskiy via GitGitGadget 2019-11-05 15:03 ` Phillip Wood ` (2 more replies) 2019-11-04 19:26 ` [PATCH 4/5] doc: commit: unify <pathspec> description Alexandr Miloslavskiy via GitGitGadget ` (2 subsequent siblings) 5 siblings, 3 replies; 80+ messages in thread From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-04 19:26 UTC (permalink / raw) To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> This option solves the problem of commandline length limit for UI's built on top of git. Plumbing commands are not always a good fit, for two major reasons: 1) Some UI's serve as assistants that help user run git commands. In this case, replacing familiar commands with plumbing commands will confuse most users. 2) Some UI's have started and grown with porcelain commands. Replacing existing logic with plumbing commands could be cumbersome and prone to various new problems. The new option is designed to behave very close to pathspecs passed in commandline args, so that switching from one to another is simple. The new option allows to read either a specified file or `stdin`. Reading from file is a good way to avoid competing for stdin, and also gives some extra flexibility. Decisions taken for simplicity: 1) The new option is declared incompatible with other options that could use `stdin`. 2) It is not allowed to pass some refspecs in args and others in file. 3) New options do not have shorthands to avoid shorthand conflicts. Also add new '--pathspec-file-null' switch that mirrors '-z' used in various places. Some porcelain commands, such as `git commit`, already use '-z', therefore it needed a new unambiguous name. Co-authored-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> --- Documentation/git-reset.txt | 18 ++++- builtin/reset.c | 22 +++++- t/t7107-reset-pathspec-file.sh | 126 +++++++++++++++++++++++++++++++++ 3 files changed, 161 insertions(+), 5 deletions(-) create mode 100755 t/t7107-reset-pathspec-file.sh diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt index b0ea6e0ce5..d484cd2827 100644 --- a/Documentation/git-reset.txt +++ b/Documentation/git-reset.txt @@ -9,18 +9,20 @@ SYNOPSIS -------- [verse] 'git reset' [-q] [<tree-ish>] [--] <pathspec>... +'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-null]] [<tree-ish>] 'git reset' (--patch | -p) [<tree-ish>] [--] [<pathspec>...] 'git reset' [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>] DESCRIPTION ----------- -In the first and second form, copy entries from `<tree-ish>` to the index. -In the third form, set the current branch head (`HEAD`) to `<commit>`, +In the first three forms, copy entries from `<tree-ish>` to the index. +In the last form, set the current branch head (`HEAD`) to `<commit>`, optionally modifying index and working tree to match. The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms. 'git reset' [-q] [<tree-ish>] [--] <pathspec>...:: - This form resets the index entries for all `<pathspec>` to their +'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-null]] [<tree-ish>]:: + These forms reset the index entries for all `<pathspec>` to their state at `<tree-ish>`. (It does not affect the working tree or the current branch.) + @@ -107,6 +109,16 @@ OPTIONS `reset.quiet` config option. `--quiet` and `--no-quiet` will override the default behavior. +--pathspec-from-file=<file>:: + Read `<pathspec>` from `<file>` instead. If `<file>` is exactly `-` + then read from standard input. Pathspecs are separated by LF or + CR/LF. Pathspecs can be quoted as explained for the configuration + variable `core.quotePath` (see linkgit:git-config[1]). See also + `--pathspec-file-null` and global `--literal-pathspecs`. + +--pathspec-file-null:: + Only meaningful with `--pathspec-from-file`. Pathspecs are + separated with NUL character and are not expected to be quoted. EXAMPLES -------- diff --git a/builtin/reset.c b/builtin/reset.c index fdd572168b..0eaa6b0bca 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -31,6 +31,7 @@ static const char * const git_reset_usage[] = { N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"), N_("git reset [-q] [<tree-ish>] [--] <paths>..."), + N_("git reset [-q] [--pathspec-from-file [--pathspec-file-null]] [<tree-ish>]"), N_("git reset --patch [<tree-ish>] [--] [<paths>...]"), NULL }; @@ -284,8 +285,8 @@ static int git_reset_config(const char *var, const char *value, void *cb) int cmd_reset(int argc, const char **argv, const char *prefix) { int reset_type = NONE, update_ref_status = 0, quiet = 0; - int patch_mode = 0, unborn; - const char *rev; + int patch_mode = 0, pathspec_file_null = 0, unborn; + const char *rev, *pathspec_from_file = NULL; struct object_id oid; struct pathspec pathspec; int intent_to_add = 0; @@ -306,6 +307,10 @@ int cmd_reset(int argc, const char **argv, const char *prefix) OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")), OPT_BOOL('N', "intent-to-add", &intent_to_add, N_("record only the fact that removed paths will be added later")), + OPT_FILENAME(0, "pathspec-from-file", &pathspec_from_file, + N_("read pathspecs from file")), + OPT_BOOL(0, "pathspec-file-null", &pathspec_file_null, + N_("with --pathspec-from-file, pathspecs are separated with NUL character")), OPT_END() }; @@ -316,6 +321,19 @@ int cmd_reset(int argc, const char **argv, const char *prefix) PARSE_OPT_KEEP_DASHDASH); parse_args(&pathspec, argv, prefix, patch_mode, &rev); + if (pathspec_from_file) { + if (patch_mode) + die(_("--pathspec-from-file is incompatible with --patch")); + + if (pathspec.nr) + die(_("--pathspec-from-file is incompatible with path arguments")); + + parse_pathspec_file(&pathspec, 0, + PATHSPEC_PREFER_FULL, + prefix, pathspec_from_file, pathspec_file_null); + } else if (pathspec_file_null) + die(_("--pathspec-file-null requires --pathspec-from-file")); + unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid); if (unborn) { /* reset on unborn branch: treat as reset to empty tree */ diff --git a/t/t7107-reset-pathspec-file.sh b/t/t7107-reset-pathspec-file.sh new file mode 100755 index 0000000000..cf7f085ad5 --- /dev/null +++ b/t/t7107-reset-pathspec-file.sh @@ -0,0 +1,126 @@ +#!/bin/sh + +test_description='reset --pathspec-from-file' + +. ./test-lib.sh + +cat > expect.a <<EOF + D fileA.t +EOF + +cat > expect.ab <<EOF + D fileA.t + D fileB.t +EOF + +cat > expect.a_bc_d <<EOF +D fileA.t + D fileB.t + D fileC.t +D fileD.t +EOF + +test_expect_success setup ' + echo A >fileA.t && + echo B >fileB.t && + echo C >fileC.t && + echo D >fileD.t && + git add . && + git commit --include . -m "Commit" && + checkpoint=$(git rev-parse --verify HEAD) +' + +restore_checkpoint () { + git reset --hard "$checkpoint" +} + +verify_state () { + git status --porcelain -- fileA.t fileB.t fileC.t fileD.t >actual && + test_cmp "$1" actual +} + +test_expect_success '--pathspec-from-file from stdin' ' + restore_checkpoint && + + git rm fileA.t && + echo fileA.t | git reset --pathspec-from-file=- && + + verify_state expect.a +' + +test_expect_success '--pathspec-from-file from file' ' + restore_checkpoint && + + git rm fileA.t && + echo fileA.t >list && + git reset --pathspec-from-file=list && + + verify_state expect.a +' + +test_expect_success 'NUL delimiters' ' + restore_checkpoint && + + git rm fileA.t fileB.t && + printf fileA.tQfileB.t | q_to_nul | git reset --pathspec-from-file=- --pathspec-file-null && + + verify_state expect.ab +' + +test_expect_success 'LF delimiters' ' + restore_checkpoint && + + git rm fileA.t fileB.t && + printf "fileA.t\nfileB.t" | git reset --pathspec-from-file=- && + + verify_state expect.ab +' + +test_expect_success 'CRLF delimiters' ' + restore_checkpoint && + + git rm fileA.t fileB.t && + printf "fileA.t\r\nfileB.t" | git reset --pathspec-from-file=- && + + verify_state expect.ab +' + +test_expect_success 'quotes' ' + restore_checkpoint && + + git rm fileA.t && + printf "\"file\\101.t\"" | git reset --pathspec-from-file=- && + + verify_state expect.a +' + +test_expect_success 'quotes not compatible with --pathspec-file-null' ' + restore_checkpoint && + + git rm fileA.t && + printf "\"file\\101.t\"" >list && + # Note: "git reset" has not yet learned to fail on wrong pathspecs + git reset --pathspec-from-file=list --pathspec-file-null && + + test_must_fail verify_state expect.a +' + +test_expect_success '--pathspec-from-file is not compatible with --soft --hard' ' + restore_checkpoint && + + git rm fileA.t && + echo fileA.t >list && + test_must_fail git reset --soft --pathspec-from-file=list && + test_must_fail git reset --hard --pathspec-from-file=list +' + +test_expect_success 'only touches what was listed' ' + restore_checkpoint && + + git rm fileA.t fileB.t fileC.t fileD.t && + printf "fileB.t\nfileC.t" | git reset --pathspec-from-file=- && + + verify_state expect.a_bc_d +' + +test_done -- gitgitgadget ^ permalink raw reply related [flat|nested] 80+ messages in thread
* Re: [PATCH 3/5] reset: support the --pathspec-from-file option 2019-11-04 19:26 ` [PATCH 3/5] reset: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget @ 2019-11-05 15:03 ` Phillip Wood 2019-11-05 19:22 ` Phillip Wood ` (2 more replies) 2019-11-05 16:14 ` Phillip Wood 2019-11-06 4:40 ` Junio C Hamano 2 siblings, 3 replies; 80+ messages in thread From: Phillip Wood @ 2019-11-05 15:03 UTC (permalink / raw) To: Alexandr Miloslavskiy via GitGitGadget, git Cc: Alexandr Miloslavskiy, Junio C Hamano Hi Alexandr This also looks good, I've got some minor comments below. If I'm complaining about whitespace and style issues you know the patch is quite good! On 04/11/2019 19:26, Alexandr Miloslavskiy via GitGitGadget wrote: > From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> > > This option solves the problem of commandline length limit for UI's > built on top of git. Plumbing commands are not always a good fit, for > two major reasons: > 1) Some UI's serve as assistants that help user run git commands. In > this case, replacing familiar commands with plumbing commands will > confuse most users. > 2) Some UI's have started and grown with porcelain commands. Replacing > existing logic with plumbing commands could be cumbersome and prone > to various new problems. I think reset is a good example of the second reason - it's not straight forward to implement it in plumbing commands > The new option is designed to behave very close to pathspecs passed in > commandline args, so that switching from one to another is simple. > > The new option allows to read either a specified file or `stdin`. > Reading from file is a good way to avoid competing for stdin, and > also gives some extra flexibility. I think this flexibility is a good idea > Decisions taken for simplicity: > 1) The new option is declared incompatible with other options that > could use `stdin`. I'm confused reset does not use stdin does it? > 2) It is not allowed to pass some refspecs in args and others in file. s/refspecs/pathspecs/ ? > 3) New options do not have shorthands to avoid shorthand conflicts. > Also add new '--pathspec-file-null' switch that mirrors '-z' used in > various places. Some porcelain commands, such as `git commit`, already > use '-z', therefore it needed a new unambiguous name. As the 'lines' in the file are nul terminated perhaps it would be better to call this --pathspec-file-nul or --nul-termination. I think the use of --null to mean nul termination for config was a mistake (for grep it matches what GUN grep does but it's still unfortunate). > > Co-authored-by: Johannes Schindelin <johannes.schindelin@gmx.de> > Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> > --- > Documentation/git-reset.txt | 18 ++++- > builtin/reset.c | 22 +++++- > t/t7107-reset-pathspec-file.sh | 126 +++++++++++++++++++++++++++++++++ > 3 files changed, 161 insertions(+), 5 deletions(-) > create mode 100755 t/t7107-reset-pathspec-file.sh > > diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt > index b0ea6e0ce5..d484cd2827 100644 > --- a/Documentation/git-reset.txt > +++ b/Documentation/git-reset.txt > @@ -9,18 +9,20 @@ SYNOPSIS > -------- > [verse] > 'git reset' [-q] [<tree-ish>] [--] <pathspec>... > +'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-null]] [<tree-ish>] --pathspec-file would be shorter and still conveys the intent of the option. Is this line missing a leading space? > 'git reset' (--patch | -p) [<tree-ish>] [--] [<pathspec>...] > 'git reset' [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>] > > DESCRIPTION > ----------- > -In the first and second form, copy entries from `<tree-ish>` to the index. > -In the third form, set the current branch head (`HEAD`) to `<commit>`, > +In the first three forms, copy entries from `<tree-ish>` to the index. > +In the last form, set the current branch head (`HEAD`) to `<commit>`, > optionally modifying index and working tree to match. > The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms. > > 'git reset' [-q] [<tree-ish>] [--] <pathspec>...:: > - This form resets the index entries for all `<pathspec>` to their > +'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-null]] [<tree-ish>]:: Alignment again > + These forms reset the index entries for all `<pathspec>` to their The new form does not mention <pathspec> so this could be confusing > state at `<tree-ish>`. (It does not affect the working tree or > the current branch.) > + > @@ -107,6 +109,16 @@ OPTIONS > `reset.quiet` config option. `--quiet` and `--no-quiet` will > override the default behavior. > > +--pathspec-from-file=<file>:: > + Read `<pathspec>` from `<file>` instead. As we have a separate synopsis line for --pathspec-from-file which does not mention <pathspec> it might be better just to say "read pathspecs from `<file>` instead of the command line". > If `<file>` is exactly `-` > + then read from standard input. Pathspecs are separated by LF or > + CR/LF. Pathspecs can be quoted as explained for the configuration > + variable `core.quotePath` (see linkgit:git-config[1]). See also > + `--pathspec-file-null` and global `--literal-pathspecs`. > + > +--pathspec-file-null:: > + Only meaningful with `--pathspec-from-file`. Pathspecs are > + separated with NUL character and are not expected to be quoted. > > EXAMPLES > -------- > diff --git a/builtin/reset.c b/builtin/reset.c > index fdd572168b..0eaa6b0bca 100644 > --- a/builtin/reset.c > +++ b/builtin/reset.c > @@ -31,6 +31,7 @@ > static const char * const git_reset_usage[] = { > N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"), > N_("git reset [-q] [<tree-ish>] [--] <paths>..."), > + N_("git reset [-q] [--pathspec-from-file [--pathspec-file-null]] [<tree-ish>]"), > N_("git reset --patch [<tree-ish>] [--] [<paths>...]"), > NULL > }; > @@ -284,8 +285,8 @@ static int git_reset_config(const char *var, const char *value, void *cb) > int cmd_reset(int argc, const char **argv, const char *prefix) > { > int reset_type = NONE, update_ref_status = 0, quiet = 0; > - int patch_mode = 0, unborn; > - const char *rev; > + int patch_mode = 0, pathspec_file_null = 0, unborn; > + const char *rev, *pathspec_from_file = NULL; > struct object_id oid; > struct pathspec pathspec; > int intent_to_add = 0; > @@ -306,6 +307,10 @@ int cmd_reset(int argc, const char **argv, const char *prefix) > OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")), > OPT_BOOL('N', "intent-to-add", &intent_to_add, > N_("record only the fact that removed paths will be added later")), > + OPT_FILENAME(0, "pathspec-from-file", &pathspec_from_file, > + N_("read pathspecs from file")), > + OPT_BOOL(0, "pathspec-file-null", &pathspec_file_null, > + N_("with --pathspec-from-file, pathspecs are separated with NUL character")), > OPT_END() > }; > > @@ -316,6 +321,19 @@ int cmd_reset(int argc, const char **argv, const char *prefix) > PARSE_OPT_KEEP_DASHDASH); > parse_args(&pathspec, argv, prefix, patch_mode, &rev); > > + if (pathspec_from_file) { > + if (patch_mode) > + die(_("--pathspec-from-file is incompatible with --patch")); This is sensible as -p is interactive so we wouldn't expect command line length to be an issue > + > + if (pathspec.nr) > + die(_("--pathspec-from-file is incompatible with path arguments")); > + > + parse_pathspec_file(&pathspec, 0, > + PATHSPEC_PREFER_FULL, > + prefix, pathspec_from_file, pathspec_file_null); > + } else if (pathspec_file_null) > + die(_("--pathspec-file-null requires --pathspec-from-file")); Style nit: the coding guidelines state that if any branch of an if statement requires braces then all the branches should be braced. This is widely ignored though. > + > unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid); > if (unborn) { > /* reset on unborn branch: treat as reset to empty tree */ > diff --git a/t/t7107-reset-pathspec-file.sh b/t/t7107-reset-pathspec-file.sh > new file mode 100755 > index 0000000000..cf7f085ad5 > --- /dev/null > +++ b/t/t7107-reset-pathspec-file.sh > @@ -0,0 +1,126 @@ > +#!/bin/sh > + > +test_description='reset --pathspec-from-file' > + > +. ./test-lib.sh > + > +cat > expect.a <<EOF > + D fileA.t > +EOF > + > +cat > expect.ab <<EOF > + D fileA.t > + D fileB.t > +EOF > + > +cat > expect.a_bc_d <<EOF > +D fileA.t > + D fileB.t > + D fileC.t > +D fileD.t > +EOF These days we tend to set up the expected files within the relevant test case using <<-\EOF to allow indentation and disallow substitution (unless it's needed of course) > +test_expect_success setup ' > + echo A >fileA.t && > + echo B >fileB.t && > + echo C >fileC.t && > + echo D >fileD.t && > + git add . && > + git commit --include . -m "Commit" && > + checkpoint=$(git rev-parse --verify HEAD) > +' > + > +restore_checkpoint () { > + git reset --hard "$checkpoint" > +} > + > +verify_state () { > + git status --porcelain -- fileA.t fileB.t fileC.t fileD.t >actual && > + test_cmp "$1" actual > +} > + > +test_expect_success '--pathspec-from-file from stdin' ' > + restore_checkpoint && > + > + git rm fileA.t && > + echo fileA.t | git reset --pathspec-from-file=- && > + > + verify_state expect.a > +' > + > +test_expect_success '--pathspec-from-file from file' ' > + restore_checkpoint && > + > + git rm fileA.t && > + echo fileA.t >list && > + git reset --pathspec-from-file=list && > + > + verify_state expect.a > +' > + > +test_expect_success 'NUL delimiters' ' > + restore_checkpoint && > + > + git rm fileA.t fileB.t && > + printf fileA.tQfileB.t | q_to_nul | git reset --pathspec-from-file=- --pathspec-file-null && > + > + verify_state expect.ab > +' > + > +test_expect_success 'LF delimiters' ' > + restore_checkpoint && > + > + git rm fileA.t fileB.t && > + printf "fileA.t\nfileB.t" | git reset --pathspec-from-file=- && > + > + verify_state expect.ab > +' > + > +test_expect_success 'CRLF delimiters' ' > + restore_checkpoint && > + > + git rm fileA.t fileB.t && > + printf "fileA.t\r\nfileB.t" | git reset --pathspec-from-file=- && > + > + verify_state expect.ab > +' > + > +test_expect_success 'quotes' ' > + restore_checkpoint && > + > + git rm fileA.t && > + printf "\"file\\101.t\"" | git reset --pathspec-from-file=- && > + > + verify_state expect.a If I've understood correctly this doesn't test if a path is correctly unquoted, only that it is accepted. > +' > + > +test_expect_success 'quotes not compatible with --pathspec-file-null' ' > + restore_checkpoint && > + > + git rm fileA.t && > + printf "\"file\\101.t\"" >list && > + # Note: "git reset" has not yet learned to fail on wrong pathspecs > + git reset --pathspec-from-file=list --pathspec-file-null && > + > + test_must_fail verify_state expect.a > +' > + > +test_expect_success '--pathspec-from-file is not compatible with --soft --hard' ' s/--soft --hard/--soft or --hard/ > + restore_checkpoint && > + > + git rm fileA.t && > + echo fileA.t >list && > + test_must_fail git reset --soft --pathspec-from-file=list && > + test_must_fail git reset --hard --pathspec-from-file=list > +' > + > +test_expect_success 'only touches what was listed' ' s/^/--pathspec-from-file / ? Best Wishes Phillip > + restore_checkpoint && > + > + git rm fileA.t fileB.t fileC.t fileD.t && > + printf "fileB.t\nfileC.t" | git reset --pathspec-from-file=- && > + > + verify_state expect.a_bc_d > +' > + > +test_done > ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 3/5] reset: support the --pathspec-from-file option 2019-11-05 15:03 ` Phillip Wood @ 2019-11-05 19:22 ` Phillip Wood 2019-11-05 19:36 ` Alexandr Miloslavskiy 2019-11-06 15:56 ` Alexandr Miloslavskiy 2 siblings, 0 replies; 80+ messages in thread From: Phillip Wood @ 2019-11-05 19:22 UTC (permalink / raw) To: Alexandr Miloslavskiy via GitGitGadget, git Cc: Alexandr Miloslavskiy, Junio C Hamano Hi Alexandr On 05/11/2019 15:03, Phillip Wood wrote: > [...] >> +test_expect_success 'quotes' ' >> + restore_checkpoint && >> + >> + git rm fileA.t && >> + printf "\"file\\101.t\"" | git reset --pathspec-from-file=- && >> + >> + verify_state expect.a > > If I've understood correctly this doesn't test if a path is correctly > unquoted, only that it is accepted. Oh I think I've misunderstood. The '\\' in unquoted by printf, so git sees '\101' which is A so that is a real file. Sorry for the confusion Phillip > >> +' >> + >> +test_expect_success 'quotes not compatible with --pathspec-file-null' ' >> + restore_checkpoint && >> + >> + git rm fileA.t && >> + printf "\"file\\101.t\"" >list && >> + # Note: "git reset" has not yet learned to fail on wrong pathspecs >> + git reset --pathspec-from-file=list --pathspec-file-null && >> + >> + test_must_fail verify_state expect.a >> +' >> + >> +test_expect_success '--pathspec-from-file is not compatible with >> --soft --hard' ' > > s/--soft --hard/--soft or --hard/ > >> + restore_checkpoint && >> + >> + git rm fileA.t && >> + echo fileA.t >list && >> + test_must_fail git reset --soft --pathspec-from-file=list && >> + test_must_fail git reset --hard --pathspec-from-file=list >> +' >> + >> +test_expect_success 'only touches what was listed' ' > > s/^/--pathspec-from-file / ? > > Best Wishes > > Phillip > >> + restore_checkpoint && >> + >> + git rm fileA.t fileB.t fileC.t fileD.t && >> + printf "fileB.t\nfileC.t" | git reset --pathspec-from-file=- && >> + >> + verify_state expect.a_bc_d >> +' >> + >> +test_done >> ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 3/5] reset: support the --pathspec-from-file option 2019-11-05 15:03 ` Phillip Wood 2019-11-05 19:22 ` Phillip Wood @ 2019-11-05 19:36 ` Alexandr Miloslavskiy 2019-11-06 15:56 ` Alexandr Miloslavskiy 2 siblings, 0 replies; 80+ messages in thread From: Alexandr Miloslavskiy @ 2019-11-05 19:36 UTC (permalink / raw) To: phillip.wood, Alexandr Miloslavskiy via GitGitGadget, git; +Cc: Junio C Hamano On 05.11.2019 16:03, Phillip Wood wrote: >> The new option allows to read either a specified file or `stdin`. >> Reading from file is a good way to avoid competing for stdin, and >> also gives some extra flexibility. > > I think this flexibility is a good idea Thanks for your support :) Previously the opinions were mixed and I was a bit afraid that this will invoke a new round of discussions. >> Decisions taken for simplicity: >> 1) The new option is declared incompatible with other options that >> could use `stdin`. > > I'm confused reset does not use stdin does it? I understand that '--patch' interacts with user via stdin. Will double-check tomorrow. >> 2) It is not allowed to pass some refspecs in args and others in file. > > s/refspecs/pathspecs/ ? Thanks! Not quite used to git speak and mix up things sometimes. >> Also add new '--pathspec-file-null' switch that mirrors '-z' used in >> various places. Some porcelain commands, such as `git commit`, already >> use '-z', therefore it needed a new unambiguous name. > > As the 'lines' in the file are nul terminated perhaps it would be better > to call this --pathspec-file-nul or --nul-termination. I think the use > of --null to mean nul termination for config was a mistake (for grep it > matches what GUN grep does but it's still unfortunate). OK, will change to '--pathspec-file-nul' >> +'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-null]] >> [<tree-ish>] > > --pathspec-file would be shorter and still conveys the intent of the > option. Is this line missing a leading space? '--pathspec-from-file' was kind of suggested instead of '--paths-file' by Junio here: https://public-inbox.org/git/xmqqtv9qr82q.fsf@gitster-ct.c.googlers.com/ so Junio added '-from' to previous writing. Hmm. What do you think, taking Junio's message into account? As for whitespaces, sorry, will fix. >> +'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-null]] >> [<tree-ish>]:: > > Alignment again Will fix >> + These forms reset the index entries for all `<pathspec>` to their > > The new form does not mention <pathspec> so this could be confusing Shall I replace '<pathspec>' with 'pathspecs' ? > As we have a separate synopsis line for --pathspec-from-file which does > not mention <pathspec> it might be better just to say "read pathspecs > from `<file>` instead of the command line". OK >> + if (pathspec_from_file) { >> + if (patch_mode) >> + die(_("--pathspec-from-file is incompatible with --patch")); > > This is sensible as -p is interactive so we wouldn't expect command line > length to be an issue Yes, I also thought so. I doubt that user is willing to interactively decide on hundreds of files. >> + } else if (pathspec_file_null) >> + die(_("--pathspec-file-null requires --pathspec-from-file")); > > Style nit: the coding guidelines state that if any branch of an if > statement requires braces then all the branches should be braced. This > is widely ignored though. Took this code from a guy who ignored it :) But sure, will change. > These days we tend to set up the expected files within the relevant test > case using <<-\EOF to allow indentation and disallow substitution > (unless it's needed of course) I'll try to change this. >> +test_expect_success 'quotes' ' >> + restore_checkpoint && >> + >> + git rm fileA.t && >> + printf "\"file\\101.t\"" | git reset --pathspec-from-file=- && >> + >> + verify_state expect.a > > If I've understood correctly this doesn't test if a path is correctly > unquoted, only that it is accepted. In my understanding, 'verify_state expect.a' should test that it's correctly understood. Am I wrong? >> +test_expect_success '--pathspec-from-file is not compatible with >> --soft --hard' ' > > s/--soft --hard/--soft or --hard/ Good idea. >> +test_expect_success 'only touches what was listed' ' > > s/^/--pathspec-from-file / ? I thought that whole test package is about '--pathspec-from-file' so I'd rather not repeat that in every test name. Shall I change that? ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 3/5] reset: support the --pathspec-from-file option 2019-11-05 15:03 ` Phillip Wood 2019-11-05 19:22 ` Phillip Wood 2019-11-05 19:36 ` Alexandr Miloslavskiy @ 2019-11-06 15:56 ` Alexandr Miloslavskiy 2 siblings, 0 replies; 80+ messages in thread From: Alexandr Miloslavskiy @ 2019-11-06 15:56 UTC (permalink / raw) To: phillip.wood, Alexandr Miloslavskiy via GitGitGadget, git; +Cc: Junio C Hamano I think I have implemented most suggestions in PatchV2. Thanks! On 05.11.2019 16:03, Phillip Wood wrote: > I'm confused reset does not use stdin does it? I have improved commit message to clarify. > --pathspec-file would be shorter and still conveys the intent of the > option. I decided to stay with writing suggested by Junio. > Is this line missing a leading space? I think it's good. Maybe you were confused by patch formatting? > Alignment again I think it's good again :) > If I've understood correctly this doesn't test if a path is correctly > unquoted, only that it is accepted. Already agreed that test is good, just a bit non-obvious. > s/^/--pathspec-from-file / ? I decided not to repeat '--pathspec-from-file' in every test, because test package already has it in description. Is that good? ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 3/5] reset: support the --pathspec-from-file option 2019-11-04 19:26 ` [PATCH 3/5] reset: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget 2019-11-05 15:03 ` Phillip Wood @ 2019-11-05 16:14 ` Phillip Wood 2019-11-05 19:37 ` Alexandr Miloslavskiy 2019-11-06 4:40 ` Junio C Hamano 2 siblings, 1 reply; 80+ messages in thread From: Phillip Wood @ 2019-11-05 16:14 UTC (permalink / raw) To: Alexandr Miloslavskiy via GitGitGadget, git Cc: Alexandr Miloslavskiy, Junio C Hamano Hi Alexandr On 04/11/2019 19:26, Alexandr Miloslavskiy via GitGitGadget wrote: > From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> > [...] > diff --git a/builtin/reset.c b/builtin/reset.c > index fdd572168b..0eaa6b0bca 100644 > --- a/builtin/reset.c > +++ b/builtin/reset.c > @@ -31,6 +31,7 @@ > static const char * const git_reset_usage[] = { > N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"), > N_("git reset [-q] [<tree-ish>] [--] <paths>..."), > + N_("git reset [-q] [--pathspec-from-file [--pathspec-file-null]] [<tree-ish>]"), > N_("git reset --patch [<tree-ish>] [--] [<paths>...]"), > NULL > }; > @@ -284,8 +285,8 @@ static int git_reset_config(const char *var, const char *value, void *cb) > int cmd_reset(int argc, const char **argv, const char *prefix) > { > int reset_type = NONE, update_ref_status = 0, quiet = 0; > - int patch_mode = 0, unborn; > - const char *rev; > + int patch_mode = 0, pathspec_file_null = 0, unborn; > + const char *rev, *pathspec_from_file = NULL; > struct object_id oid; > struct pathspec pathspec; > int intent_to_add = 0; > @@ -306,6 +307,10 @@ int cmd_reset(int argc, const char **argv, const char *prefix) > OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")), > OPT_BOOL('N', "intent-to-add", &intent_to_add, > N_("record only the fact that removed paths will be added later")), > + OPT_FILENAME(0, "pathspec-from-file", &pathspec_from_file, > + N_("read pathspecs from file")), > + OPT_BOOL(0, "pathspec-file-null", &pathspec_file_null, > + N_("with --pathspec-from-file, pathspecs are separated with NUL character")), One thing I forgot to mention before is that if you're going to add these options to a number of commands then maybe it is worth defining a macro for each one in parse-options.h. There are a couple of examples of this at the end of that file. Best Wishes Phillip > OPT_END() > }; > > @@ -316,6 +321,19 @@ int cmd_reset(int argc, const char **argv, const char *prefix) > PARSE_OPT_KEEP_DASHDASH); > parse_args(&pathspec, argv, prefix, patch_mode, &rev); > > + if (pathspec_from_file) { > + if (patch_mode) > + die(_("--pathspec-from-file is incompatible with --patch")); > + > + if (pathspec.nr) > + die(_("--pathspec-from-file is incompatible with path arguments")); > + > + parse_pathspec_file(&pathspec, 0, > + PATHSPEC_PREFER_FULL, > + prefix, pathspec_from_file, pathspec_file_null); > + } else if (pathspec_file_null) > + die(_("--pathspec-file-null requires --pathspec-from-file")); > + > unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid); > if (unborn) { > /* reset on unborn branch: treat as reset to empty tree */ > diff --git a/t/t7107-reset-pathspec-file.sh b/t/t7107-reset-pathspec-file.sh > new file mode 100755 > index 0000000000..cf7f085ad5 > --- /dev/null > +++ b/t/t7107-reset-pathspec-file.sh > @@ -0,0 +1,126 @@ > +#!/bin/sh > + > +test_description='reset --pathspec-from-file' > + > +. ./test-lib.sh > + > +cat > expect.a <<EOF > + D fileA.t > +EOF > + > +cat > expect.ab <<EOF > + D fileA.t > + D fileB.t > +EOF > + > +cat > expect.a_bc_d <<EOF > +D fileA.t > + D fileB.t > + D fileC.t > +D fileD.t > +EOF > + > +test_expect_success setup ' > + echo A >fileA.t && > + echo B >fileB.t && > + echo C >fileC.t && > + echo D >fileD.t && > + git add . && > + git commit --include . -m "Commit" && > + checkpoint=$(git rev-parse --verify HEAD) > +' > + > +restore_checkpoint () { > + git reset --hard "$checkpoint" > +} > + > +verify_state () { > + git status --porcelain -- fileA.t fileB.t fileC.t fileD.t >actual && > + test_cmp "$1" actual > +} > + > +test_expect_success '--pathspec-from-file from stdin' ' > + restore_checkpoint && > + > + git rm fileA.t && > + echo fileA.t | git reset --pathspec-from-file=- && > + > + verify_state expect.a > +' > + > +test_expect_success '--pathspec-from-file from file' ' > + restore_checkpoint && > + > + git rm fileA.t && > + echo fileA.t >list && > + git reset --pathspec-from-file=list && > + > + verify_state expect.a > +' > + > +test_expect_success 'NUL delimiters' ' > + restore_checkpoint && > + > + git rm fileA.t fileB.t && > + printf fileA.tQfileB.t | q_to_nul | git reset --pathspec-from-file=- --pathspec-file-null && > + > + verify_state expect.ab > +' > + > +test_expect_success 'LF delimiters' ' > + restore_checkpoint && > + > + git rm fileA.t fileB.t && > + printf "fileA.t\nfileB.t" | git reset --pathspec-from-file=- && > + > + verify_state expect.ab > +' > + > +test_expect_success 'CRLF delimiters' ' > + restore_checkpoint && > + > + git rm fileA.t fileB.t && > + printf "fileA.t\r\nfileB.t" | git reset --pathspec-from-file=- && > + > + verify_state expect.ab > +' > + > +test_expect_success 'quotes' ' > + restore_checkpoint && > + > + git rm fileA.t && > + printf "\"file\\101.t\"" | git reset --pathspec-from-file=- && > + > + verify_state expect.a > +' > + > +test_expect_success 'quotes not compatible with --pathspec-file-null' ' > + restore_checkpoint && > + > + git rm fileA.t && > + printf "\"file\\101.t\"" >list && > + # Note: "git reset" has not yet learned to fail on wrong pathspecs > + git reset --pathspec-from-file=list --pathspec-file-null && > + > + test_must_fail verify_state expect.a > +' > + > +test_expect_success '--pathspec-from-file is not compatible with --soft --hard' ' > + restore_checkpoint && > + > + git rm fileA.t && > + echo fileA.t >list && > + test_must_fail git reset --soft --pathspec-from-file=list && > + test_must_fail git reset --hard --pathspec-from-file=list > +' > + > +test_expect_success 'only touches what was listed' ' > + restore_checkpoint && > + > + git rm fileA.t fileB.t fileC.t fileD.t && > + printf "fileB.t\nfileC.t" | git reset --pathspec-from-file=- && > + > + verify_state expect.a_bc_d > +' > + > +test_done > ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 3/5] reset: support the --pathspec-from-file option 2019-11-05 16:14 ` Phillip Wood @ 2019-11-05 19:37 ` Alexandr Miloslavskiy 0 siblings, 0 replies; 80+ messages in thread From: Alexandr Miloslavskiy @ 2019-11-05 19:37 UTC (permalink / raw) To: phillip.wood, Alexandr Miloslavskiy via GitGitGadget, git; +Cc: Junio C Hamano On 05.11.2019 17:14, Phillip Wood wrote: >> + OPT_FILENAME(0, "pathspec-from-file", &pathspec_from_file, >> + N_("read pathspecs from file")), >> + OPT_BOOL(0, "pathspec-file-null", &pathspec_file_null, >> + N_("with --pathspec-from-file, pathspecs are >> separated with NUL character")), > > One thing I forgot to mention before is that if you're going to add > these options to a number of commands then maybe it is worth defining a > macro for each one in parse-options.h. There are a couple of examples of > this at the end of that file. Thanks for the pointer! ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 3/5] reset: support the --pathspec-from-file option 2019-11-04 19:26 ` [PATCH 3/5] reset: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget 2019-11-05 15:03 ` Phillip Wood 2019-11-05 16:14 ` Phillip Wood @ 2019-11-06 4:40 ` Junio C Hamano 2019-11-06 15:56 ` Alexandr Miloslavskiy 2 siblings, 1 reply; 80+ messages in thread From: Junio C Hamano @ 2019-11-06 4:40 UTC (permalink / raw) To: Alexandr Miloslavskiy via GitGitGadget; +Cc: git, Alexandr Miloslavskiy "Alexandr Miloslavskiy via GitGitGadget" <gitgitgadget@gmail.com> writes: > From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> > > This option solves the problem of commandline length limit for UI's > built on top of git. Plumbing commands are not always a good fit, for > two major reasons: > 1) Some UI's serve as assistants that help user run git commands. In > this case, replacing familiar commands with plumbing commands will > confuse most users. "UI's that serve as assistants that help user run git commands" does not have to avoid plumbing commands at all. Only the ones that "show" the commands that are run on behalf of the users (perhaps so that the users can learn from such examples?) do, and I think I learned that it was your motivating use case from an earlier discussion. Perhaps UIs that help users to formulate git commands to run need to present Porcelain commands to be used, as it is not reasonable to demonstrate arcane combination of plumbing commands as an example for their interactive use. would probably be more readable without bending what you wanted to say too much? > 2) Some UI's have started and grown with porcelain commands. Replacing > existing logic with plumbing commands could be cumbersome and prone > to various new problems. There is not a lot of sympathy for such argument ;-) > 2) It is not allowed to pass some refspecs in args and others in file. Did you mean refspec, not pathspec? > 3) New options do not have shorthands to avoid shorthand conflicts. > > Also add new '--pathspec-file-null' switch that mirrors '-z' used in > various places. Some porcelain commands, such as `git commit`, already > use '-z', therefore it needed a new unambiguous name. I do not understand this part. Wouldn't it natural to expect that "-z", when used with "--pathspec-from-file", tell us that the named file is NUL delimited collection of records? What's different about "--pathspec-file-null" that it is confusing to all it "-z"? I actually do not mind not having "-z" and using only "--pathspec-file-null". A new option with long name that feels similar to existing "-z" but does sufficiently different things so that it needs a different name, however, feels counter-productive for the purpose of using it in the UI that produces commands to be learned by end-users. > +--pathspec-from-file=<file>:: > + Read `<pathspec>` from `<file>` instead. If `<file>` is exactly `-` > + then read from standard input. Pathspecs are separated by LF or > + CR/LF. Pathspecs can be quoted as explained for the configuration When I invented the terms "pathspec", "refspec", etc. for this project, I meant them to be collective nouns. A pathspec is a set of pathspec elements, each of which is usually given as a separate argument on the command line. So "Read pathspec from file" is good (not "Read pathspecs from file"). And "Pathspec elements" are separated by LF or CR/LF. A tangent. Since we do not allow NUL in a pathspec element, we do not even need the "-z" option. When we read pathspec from a file, we can take any of CRLF, LF or NUL as the separator. Ah, sorry, that would not help very much. With "-z" we are allowing to express pathspec elements inside which there are embedded LF or CR/LF. Sorry about the noise. > +--pathspec-file-null:: > + Only meaningful with `--pathspec-from-file`. Pathspecs are > + separated with NUL character and are not expected to be quoted. OK. > + if (pathspec_from_file) { > + if (patch_mode) > + die(_("--pathspec-from-file is incompatible with --patch")); > + > + if (pathspec.nr) > + die(_("--pathspec-from-file is incompatible with path arguments")); Shouldn't the error message say pathspec arguments instead? > diff --git a/t/t7107-reset-pathspec-file.sh b/t/t7107-reset-pathspec-file.sh > new file mode 100755 > index 0000000000..cf7f085ad5 > --- /dev/null > +++ b/t/t7107-reset-pathspec-file.sh > @@ -0,0 +1,126 @@ > +#!/bin/sh > + > +test_description='reset --pathspec-from-file' > + > +. ./test-lib.sh > + > +cat > expect.a <<EOF Style: - Modern test scripts strive to perform these set-up procedure inside (the first) test_expect_success. - No SP between the redirection operator and its source/destination filename. - Quote end-of-here-document (EOF in the above) if you do not rely on parameter interpolation inside the here document; this helps readers by telling them that what is presented is used verbatim. > + D fileA.t > +EOF > + > +cat > expect.ab <<EOF > + D fileA.t > + D fileB.t > +EOF > + > +cat > expect.a_bc_d <<EOF > +D fileA.t > + D fileB.t > + D fileC.t > +D fileD.t > +EOF > + > +test_expect_success setup ' > + echo A >fileA.t && > + echo B >fileB.t && > + echo C >fileC.t && > + echo D >fileD.t && > + git add . && > + git commit --include . -m "Commit" && > + checkpoint=$(git rev-parse --verify HEAD) > +' > + > +restore_checkpoint () { > + git reset --hard "$checkpoint" > +} Hmm, wouldn't it be cleaner to use a lightweight tag or something to keep checkpoint, instead of a variable that is hard to examine when tests break and needs debugging? > +verify_state () { > + git status --porcelain -- fileA.t fileB.t fileC.t fileD.t >actual && > + test_cmp "$1" actual > +} > + > +test_expect_success '--pathspec-from-file from stdin' ' > + restore_checkpoint && > + > + git rm fileA.t && > + echo fileA.t | git reset --pathspec-from-file=- && > + verify_state expect.a > +' > + > +test_expect_success '--pathspec-from-file from file' ' > + restore_checkpoint && > + > + git rm fileA.t && > + echo fileA.t >list && > + git reset --pathspec-from-file=list && > + > + verify_state expect.a > +' > + > +test_expect_success 'NUL delimiters' ' > + restore_checkpoint && > + > + git rm fileA.t fileB.t && > + printf fileA.tQfileB.t | q_to_nul | git reset --pathspec-from-file=- --pathspec-file-null && This feeds "fileA.t<NUL>fileB.t" without <NUL> after "fileB.t" to the command. Intended? Rather, perhaps printf "%s\0" fileA.t fileB.t without q-to-nul, once you use printf anyway? If you truly mean "delimiter" (as opposed to "terminator"), printf "fileA.t\0fileB.t" can also lose " | q_to_nul". Thanks. ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 3/5] reset: support the --pathspec-from-file option 2019-11-06 4:40 ` Junio C Hamano @ 2019-11-06 15:56 ` Alexandr Miloslavskiy 0 siblings, 0 replies; 80+ messages in thread From: Alexandr Miloslavskiy @ 2019-11-06 15:56 UTC (permalink / raw) To: Junio C Hamano, Alexandr Miloslavskiy via GitGitGadget; +Cc: git I think I have implemented most suggestions in PatchV2. Thanks! >> 2) Some UI's have started and grown with porcelain commands. Replacing >> existing logic with plumbing commands could be cumbersome and prone >> to various new problems. > > There is not a lot of sympathy for such argument ;-) Let's think that this merely reinforces the first point :) > I do not understand this part. Wouldn't it natural to expect that > "-z", when used with "--pathspec-from-file", tell us that the named > file is NUL delimited collection of records? What's different about > "--pathspec-file-null" that it is confusing to all it "-z"? > > I actually do not mind not having "-z" and using only > "--pathspec-file-null". A new option with long name that feels > similar to existing "-z" but does sufficiently different things so > that it needs a different name, however, feels counter-productive > for the purpose of using it in the UI that produces commands to be > learned by end-users. I didn't want to run into a situation where '-z' suddenly applies to multiple things (for example, if later it's decided to allow --patch with --pathspec-from-file). Regarding UI, I think that (insert our UI name here) will not use '--pathspec-from-file' unless there are really many files. When it does happen, user will probably have an easier time understanding '--pathspec-file-nul' then '-z'. > When I invented the terms "pathspec", "refspec", etc. for this > project, I meant them to be collective nouns. A pathspec is a set > of pathspec elements, each of which is usually given as a separate > argument on the command line. So "Read pathspec from file" is good > (not "Read pathspecs from file"). > > And "Pathspec elements" are separated by LF or CR/LF. I changed that. However, `git grep pathspecs` is quite interesting. My own taste says that "pathspecs" is more expected then "pathspec". > A tangent. Since we do not allow NUL in a pathspec element, we do > not even need the "-z" option. When we read pathspec from a file, > we can take any of CRLF, LF or NUL as the separator. > > Ah, sorry, that would not help very much. With "-z" we are allowing > to express pathspec elements inside which there are embedded LF or > CR/LF. Sorry about the noise. Yes, NUL delimiter allows to avoid escaping. > - Quote end-of-here-document (EOF in the above) if you do not rely > on parameter interpolation inside the here document; this helps > readers by telling them that what is presented is used verbatim. Tests overwhelmingly prefer escaped form (\EOF) instead, so I used that. > This feeds "fileA.t<NUL>fileB.t" without <NUL> after "fileB.t" to > the command. Intended? Kind of intended. I changed tests to always use trailing delimiters, and also added a new dedicated test without trailing delimiter. ^ permalink raw reply [flat|nested] 80+ messages in thread
* [PATCH 4/5] doc: commit: unify <pathspec> description 2019-11-04 19:26 [PATCH 0/5] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget ` (2 preceding siblings ...) 2019-11-04 19:26 ` [PATCH 3/5] reset: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget @ 2019-11-04 19:26 ` Alexandr Miloslavskiy via GitGitGadget 2019-11-06 4:50 ` Junio C Hamano 2019-11-04 19:26 ` [PATCH 5/5] commit: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget 2019-11-06 15:51 ` [PATCH v2 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget 5 siblings, 1 reply; 80+ messages in thread From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-04 19:26 UTC (permalink / raw) To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> Synchronize it to `git add`, which has a pretty good description. This also better disambiguates <file>... header. Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> --- Documentation/git-commit.txt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt index afa7b75a23..4341d0e3ab 100644 --- a/Documentation/git-commit.txt +++ b/Documentation/git-commit.txt @@ -13,7 +13,7 @@ SYNOPSIS [-F <file> | -m <msg>] [--reset-author] [--allow-empty] [--allow-empty-message] [--no-verify] [-e] [--author=<author>] [--date=<date>] [--cleanup=<mode>] [--[no-]status] - [-i | -o] [-S[<keyid>]] [--] [<file>...] + [-i | -o] [-S[<keyid>]] [--] [<pathspec>...] DESCRIPTION ----------- @@ -345,12 +345,15 @@ changes to tracked files. \--:: Do not interpret any more arguments as options. -<file>...:: +<pathspec>...:: When files are given on the command line, the command commits the contents of the named files, without recording the changes already staged. The contents of these files are also staged for the next commit on top of what have been staged before. ++ +For more details about the <pathspec> syntax, see the 'pathspec' entry +in linkgit:gitglossary[7]. :git-commit: 1 include::date-formats.txt[] -- gitgitgadget ^ permalink raw reply related [flat|nested] 80+ messages in thread
* Re: [PATCH 4/5] doc: commit: unify <pathspec> description 2019-11-04 19:26 ` [PATCH 4/5] doc: commit: unify <pathspec> description Alexandr Miloslavskiy via GitGitGadget @ 2019-11-06 4:50 ` Junio C Hamano 2019-11-06 15:56 ` Alexandr Miloslavskiy 0 siblings, 1 reply; 80+ messages in thread From: Junio C Hamano @ 2019-11-06 4:50 UTC (permalink / raw) To: Alexandr Miloslavskiy via GitGitGadget; +Cc: git, Alexandr Miloslavskiy "Alexandr Miloslavskiy via GitGitGadget" <gitgitgadget@gmail.com> writes: > From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> > > Synchronize it to `git add`, which has a pretty good description. > This also better disambiguates <file>... header. "When files are given on..." is no longer true with this change (it wasn't true with the code before this change anyway). When pathspec is given on the command line, commit the contents of the files that match the pathspec without recording the changes already added to the index. ... The second sentence also says "these files", but that can be left as-is, since it would refer to "the files that match ..." explained in the above sentence. > +For more details about the <pathspec> syntax, see the 'pathspec' entry > +in linkgit:gitglossary[7]. I am not sure if we want to repeat this all over the place. We do not say "For details about the <commit> syntax, see the 'SPECIFYING REVISIONS' section of linkgit:git-rev-parse[1]" for every command that takes <commit> from the command line. Is your urge to suggest adding this sentence coming from that you are much more familiar with <commit> than <pathspec>? In other words, if you were more familiar with Git, would you still be adding this (and not corresponding one for <commit>)? ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 4/5] doc: commit: unify <pathspec> description 2019-11-06 4:50 ` Junio C Hamano @ 2019-11-06 15:56 ` Alexandr Miloslavskiy 2019-11-07 5:54 ` Junio C Hamano 0 siblings, 1 reply; 80+ messages in thread From: Alexandr Miloslavskiy @ 2019-11-06 15:56 UTC (permalink / raw) To: Junio C Hamano, Alexandr Miloslavskiy via GitGitGadget; +Cc: git I think I have implemented most suggestions in PatchV2. Thanks! > I am not sure if we want to repeat this all over the place. > > We do not say "For details about the <commit> syntax, see the > 'SPECIFYING REVISIONS' section of linkgit:git-rev-parse[1]" for > every command that takes <commit> from the command line. > > Is your urge to suggest adding this sentence coming from that you > are much more familiar with <commit> than <pathspec>? In other > words, if you were more familiar with Git, would you still be adding > this (and not corresponding one for <commit>)? Commit is a well known term, dating from ancient times like CVS or even older. Pathspec, however, is something new. When I pretend to be someone new to git, I see it this way: 1) Let's read "git commit" documentation 2) Where on this commandline do I put my filename?! So yes, I would repeat it in every location that could be popular for new users. ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 4/5] doc: commit: unify <pathspec> description 2019-11-06 15:56 ` Alexandr Miloslavskiy @ 2019-11-07 5:54 ` Junio C Hamano 2019-11-07 11:39 ` Alexandr Miloslavskiy 0 siblings, 1 reply; 80+ messages in thread From: Junio C Hamano @ 2019-11-07 5:54 UTC (permalink / raw) To: Alexandr Miloslavskiy; +Cc: Alexandr Miloslavskiy via GitGitGadget, git Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> writes: > I think I have implemented most suggestions in PatchV2. Thanks! > >> I am not sure if we want to repeat this all over the place. >> >> We do not say "For details about the <commit> syntax, see the >> 'SPECIFYING REVISIONS' section of linkgit:git-rev-parse[1]" for >> every command that takes <commit> from the command line. >> >> Is your urge to suggest adding this sentence coming from that you >> are much more familiar with <commit> than <pathspec>? In other >> words, if you were more familiar with Git, would you still be adding >> this (and not corresponding one for <commit>)? > > Commit is a well known term, dating from ancient times like CVS or > even older. That's more or less irrelevant. I am reacting to this from your change that you omitted quoting in your reply: > +For more details about the <pathspec> syntax, see the 'pathspec' entry > +in linkgit:gitglossary[7]. That sentence is for those who have some notion of <pathspec> but does not know enough about its syntax. CVS does not let you specify <commit> like "master^{/^fix frotz}~4"; A user a user who is familiar with CVS's commits would still want to refer to the section "For details about the <commit> syntax". I am not advocating to add the reference to SPECIFYING REVISIONS section; instead we should let readers know that every time they see <defined word>, they can refer to the glossary for more details. > Pathspec, however, is something new. Compared to CVS, everything in Git may be new, but that was a news in 2010, not this year. ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 4/5] doc: commit: unify <pathspec> description 2019-11-07 5:54 ` Junio C Hamano @ 2019-11-07 11:39 ` Alexandr Miloslavskiy 0 siblings, 0 replies; 80+ messages in thread From: Alexandr Miloslavskiy @ 2019-11-07 11:39 UTC (permalink / raw) To: Junio C Hamano; +Cc: Alexandr Miloslavskiy via GitGitGadget, git On 07.11.2019 6:54, Junio C Hamano wrote: > I am reacting to this from your change that you omitted quoting in > your reply: > >> +For more details about the <pathspec> syntax, see the 'pathspec' entry >> +in linkgit:gitglossary[7]. > > That sentence is for those who have some notion of <pathspec> but > does not know enough about its syntax. In the perfect world, I would expect _every_ 'pathspec' word in text to be an HTML-style link to a dedicated article, not just a paragraph in glossary. MSDN is in general a good example [1]: there, it's easy to read only a small portion of article, ignoring anything you're not interested in, and still have all links at hand. Regarding dedicated page: the content of 'pathspec' in glossary is already long enough for a page, and it could benefit from additional examples. Also, having a dedicated page makes linking easier, so that user doesn't have to scroll glossary. Regarding links: I don't really understand what git's doc format allows. Is it just pure text in worst (or even average) case? If it's usually something with clickable links, it could be worth to just insert links everywhere. If it's usually plaintext, then "see the 'pathspec' entry in linkgit:gitglossary[7]" is a bit too verbose to repeat on every occasion. Still, I'd like to see links everywhere. One big reason is to let reader know that the explanation actually exists! A compromise solution is to give every article header like this: This article uses the following terms which are explained in linkgit:gitglossary[7]: * pathspec * tree-ish * refspec If it's close to top of article, then chances are that everyone will notice it. Also, it will not require extra verbosity in plaintext. > CVS does not let you specify <commit> like "master^{/^fix frotz}~4"; > A user a user who is familiar with CVS's commits would still want to > refer to the section "For details about the <commit> syntax". > > I am not advocating to add the reference to SPECIFYING REVISIONS > section; instead we should let readers know that every time they see > <defined word>, they can refer to the glossary for more details. I now think that my arguments apply to <pathspec> AND <commit> in the same way. Indeed a user can't know complex <commit> variants until he/she reads it in git docs. ---- [1] https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea ^ permalink raw reply [flat|nested] 80+ messages in thread
* [PATCH 5/5] commit: support the --pathspec-from-file option 2019-11-04 19:26 [PATCH 0/5] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget ` (3 preceding siblings ...) 2019-11-04 19:26 ` [PATCH 4/5] doc: commit: unify <pathspec> description Alexandr Miloslavskiy via GitGitGadget @ 2019-11-04 19:26 ` Alexandr Miloslavskiy via GitGitGadget 2019-11-05 16:27 ` Phillip Wood 2019-11-06 4:51 ` Junio C Hamano 2019-11-06 15:51 ` [PATCH v2 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget 5 siblings, 2 replies; 80+ messages in thread From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-04 19:26 UTC (permalink / raw) To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> This option solves the problem of commandline length limit for UI's built on top of git. Plumbing commands are not always a good fit, for two major reasons: 1) Some UI's serve as assistants that help user run git commands. In this case, replacing familiar commands with plumbing commands will confuse most users. 2) Some UI's have started and grown with porcelain commands. Replacing existing logic with plumbing commands could be cumbersome and prone to various new problems. The new option is designed to behave very close to pathspecs passed in commandline args, so that switching from one to another is simple. The new option allows to read either a specified file or `stdin`. Reading from file is a good way to avoid competing for stdin, and also gives some extra flexibility. Decisions taken for simplicity: 1) The new option is declared incompatible with other options that could use `stdin`. 2) It is not allowed to pass some refspecs in args and others in file. 3) New options do not have shorthands to avoid shorthand conflicts. Also add new '--pathspec-file-null' switch that mirrors '-z' used in various places. Some porcelain commands, such as `git commit`, already use '-z', therefore it needed a new unambiguous name. Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> --- Documentation/git-commit.txt | 14 ++++- builtin/commit.c | 25 ++++++-- t/t7526-commit-pathspec-file.sh | 107 ++++++++++++++++++++++++++++++++ 3 files changed, 141 insertions(+), 5 deletions(-) create mode 100755 t/t7526-commit-pathspec-file.sh diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt index 4341d0e3ab..ec4752298d 100644 --- a/Documentation/git-commit.txt +++ b/Documentation/git-commit.txt @@ -13,7 +13,8 @@ SYNOPSIS [-F <file> | -m <msg>] [--reset-author] [--allow-empty] [--allow-empty-message] [--no-verify] [-e] [--author=<author>] [--date=<date>] [--cleanup=<mode>] [--[no-]status] - [-i | -o] [-S[<keyid>]] [--] [<pathspec>...] + [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-null]] + [-S[<keyid>]] [--] [<pathspec>...] DESCRIPTION ----------- @@ -277,6 +278,17 @@ FROM UPSTREAM REBASE" section in linkgit:git-rebase[1].) the last commit without committing changes that have already been staged. If used together with `--allow-empty` paths are also not required, and an empty commit will be created. + +--pathspec-from-file=<file>:: + Read `<pathspec>` from `<file>` instead. If `<file>` is exactly `-` + then read from standard input. Pathspecs are separated by LF or + CR/LF. Pathspecs can be quoted as explained for the configuration + variable `core.quotePath` (see linkgit:git-config[1]). See also + `--pathspec-file-null` and global `--literal-pathspecs`. + +--pathspec-file-null:: + Only meaningful with `--pathspec-from-file`. Pathspecs are + separated with NUL character and are not expected to be quoted. -u[<mode>]:: --untracked-files[=<mode>]:: diff --git a/builtin/commit.c b/builtin/commit.c index e588bc6ad3..532f305926 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -107,9 +107,9 @@ static int all, also, interactive, patch_interactive, only, amend, signoff; static int edit_flag = -1; /* unspecified */ static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship; static int config_commit_verbose = -1; /* unspecified */ -static int no_post_rewrite, allow_empty_message; +static int no_post_rewrite, allow_empty_message, pathspec_file_null; static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg; -static char *sign_commit; +static char *sign_commit, *pathspec_from_file; /* * The default commit message cleanup mode will remove the lines @@ -343,6 +343,23 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix PATHSPEC_PREFER_FULL, prefix, argv); + if (pathspec_from_file) { + if (interactive) + die(_("--pathspec-from-file is incompatible with --interactive/--patch")); + + if (pathspec.nr) + die(_("--pathspec-from-file is incompatible with path arguments")); + + parse_pathspec_file(&pathspec, 0, + PATHSPEC_PREFER_FULL, + prefix, pathspec_from_file, pathspec_file_null); + } + else if (pathspec_file_null) + die(_("--pathspec-file-null requires --pathspec-from-file")); + + if (!pathspec.nr && (also || (only && !amend && !allow_empty))) + die(_("No paths with --include/--only does not make sense.")); + if (read_cache_preload(&pathspec) < 0) die(_("index file corrupt")); @@ -1198,8 +1215,6 @@ static int parse_and_validate_options(int argc, const char *argv[], if (also + only + all + interactive > 1) die(_("Only one of --include/--only/--all/--interactive/--patch can be used.")); - if (argc == 0 && (also || (only && !amend && !allow_empty))) - die(_("No paths with --include/--only does not make sense.")); cleanup_mode = get_cleanup_mode(cleanup_arg, use_editor); handle_untracked_files_arg(s); @@ -1535,6 +1550,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "amend", &amend, N_("amend previous commit")), OPT_BOOL(0, "no-post-rewrite", &no_post_rewrite, N_("bypass post-rewrite hook")), { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, N_("mode"), N_("show untracked files, optional modes: all, normal, no. (Default: all)"), PARSE_OPT_OPTARG, NULL, (intptr_t)"all" }, + OPT_FILENAME(0, "pathspec-from-file", &pathspec_from_file, N_("read pathspecs from file")), + OPT_BOOL(0, "pathspec-file-null", &pathspec_file_null, N_("with --pathspec-from-file, pathspecs are separated with NUL character")), /* end commit contents options */ OPT_HIDDEN_BOOL(0, "allow-empty", &allow_empty, diff --git a/t/t7526-commit-pathspec-file.sh b/t/t7526-commit-pathspec-file.sh new file mode 100755 index 0000000000..c5d68e01e6 --- /dev/null +++ b/t/t7526-commit-pathspec-file.sh @@ -0,0 +1,107 @@ +#!/bin/sh + +test_description='commit --pathspec-from-file' + +. ./test-lib.sh + +test_tick + +cat > expect.a <<EOF +A fileA.t +EOF + +cat > expect.ab <<EOF +A fileA.t +A fileB.t +EOF + +cat > expect.bc <<EOF +A fileB.t +A fileC.t +EOF + +test_expect_success setup ' + test_commit file0 && + checkpoint=$(git rev-parse --verify HEAD) && + + echo A >fileA.t && + echo B >fileB.t && + echo C >fileC.t && + echo D >fileD.t && + git add fileA.t fileB.t fileC.t fileD.t +' + +restore_checkpoint () { + git reset --soft "$checkpoint" +} + +verify_state () { + git diff-tree --no-commit-id --name-status -r HEAD >actual && + test_cmp "$1" actual +} + +test_expect_success '--pathspec-from-file from stdin' ' + restore_checkpoint && + + echo fileA.t | git commit --pathspec-from-file=- -m "Commit" && + + verify_state expect.a +' + +test_expect_success '--pathspec-from-file from file' ' + restore_checkpoint && + + echo fileA.t >list && + git commit --pathspec-from-file=list -m "Commit" && + + verify_state expect.a +' + +test_expect_success 'NUL delimiters' ' + restore_checkpoint && + + printf fileA.tQfileB.t | q_to_nul | git commit --pathspec-from-file=- --pathspec-file-null -m "Commit" && + + verify_state expect.ab +' + +test_expect_success 'LF delimiters' ' + restore_checkpoint && + + printf "fileA.t\nfileB.t" | git commit --pathspec-from-file=- -m "Commit" && + + verify_state expect.ab +' + +test_expect_success 'CRLF delimiters' ' + restore_checkpoint && + + printf "fileA.t\r\nfileB.t" | git commit --pathspec-from-file=- -m "Commit" && + + verify_state expect.ab +' + +test_expect_success 'quotes' ' + restore_checkpoint && + + printf "\"file\\101.t\"" | git commit --pathspec-from-file=- -m "Commit" && + + verify_state expect.a +' + +test_expect_success 'quotes not compatible with --pathspec-file-null' ' + restore_checkpoint && + + printf "\"file\\101.t\"" >list && + test_must_fail git commit --pathspec-from-file=list --pathspec-file-null -m "Commit" +' + +test_expect_success 'only touches what was listed' ' + restore_checkpoint && + + printf "fileB.t\nfileC.t" | git commit --pathspec-from-file=- -m "Commit" && + + verify_state expect.bc +' + +test_done -- gitgitgadget ^ permalink raw reply related [flat|nested] 80+ messages in thread
* Re: [PATCH 5/5] commit: support the --pathspec-from-file option 2019-11-04 19:26 ` [PATCH 5/5] commit: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget @ 2019-11-05 16:27 ` Phillip Wood 2019-11-05 19:42 ` Alexandr Miloslavskiy 2019-11-06 15:56 ` Alexandr Miloslavskiy 2019-11-06 4:51 ` Junio C Hamano 1 sibling, 2 replies; 80+ messages in thread From: Phillip Wood @ 2019-11-05 16:27 UTC (permalink / raw) To: Alexandr Miloslavskiy via GitGitGadget, git Cc: Alexandr Miloslavskiy, Junio C Hamano Hi Alexandr On 04/11/2019 19:26, Alexandr Miloslavskiy via GitGitGadget wrote: > From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> > > This option solves the problem of commandline length limit for UI's > built on top of git. Plumbing commands are not always a good fit, for > two major reasons: > 1) Some UI's serve as assistants that help user run git commands. In > this case, replacing familiar commands with plumbing commands will > confuse most users. > 2) Some UI's have started and grown with porcelain commands. Replacing > existing logic with plumbing commands could be cumbersome and prone > to various new problems. > > The new option is designed to behave very close to pathspecs passed in > commandline args, so that switching from one to another is simple. > > The new option allows to read either a specified file or `stdin`. > Reading from file is a good way to avoid competing for stdin, and > also gives some extra flexibility. > > Decisions taken for simplicity: > 1) The new option is declared incompatible with other options that > could use `stdin`. > 2) It is not allowed to pass some refspecs in args and others in file. > 3) New options do not have shorthands to avoid shorthand conflicts. > > Also add new '--pathspec-file-null' switch that mirrors '-z' used in > various places. Some porcelain commands, such as `git commit`, already > use '-z', therefore it needed a new unambiguous name. It might be worth tailoring the message to the command rather than having exactly the same message for commit and reset > > Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> > --- > Documentation/git-commit.txt | 14 ++++- > builtin/commit.c | 25 ++++++-- > t/t7526-commit-pathspec-file.sh | 107 ++++++++++++++++++++++++++++++++ > 3 files changed, 141 insertions(+), 5 deletions(-) > create mode 100755 t/t7526-commit-pathspec-file.sh > > diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt > index 4341d0e3ab..ec4752298d 100644 > --- a/Documentation/git-commit.txt > +++ b/Documentation/git-commit.txt > @@ -13,7 +13,8 @@ SYNOPSIS > [-F <file> | -m <msg>] [--reset-author] [--allow-empty] > [--allow-empty-message] [--no-verify] [-e] [--author=<author>] > [--date=<date>] [--cleanup=<mode>] [--[no-]status] > - [-i | -o] [-S[<keyid>]] [--] [<pathspec>...] > + [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-null]] > + [-S[<keyid>]] [--] [<pathspec>...] > > DESCRIPTION > ----------- > @@ -277,6 +278,17 @@ FROM UPSTREAM REBASE" section in linkgit:git-rebase[1].) > the last commit without committing changes that have > already been staged. If used together with `--allow-empty` > paths are also not required, and an empty commit will be created. > + > +--pathspec-from-file=<file>:: > + Read `<pathspec>` from `<file>` instead. If `<file>` is exactly `-` > + then read from standard input. Pathspecs are separated by LF or > + CR/LF. Pathspecs can be quoted as explained for the configuration > + variable `core.quotePath` (see linkgit:git-config[1]). See also > + `--pathspec-file-null` and global `--literal-pathspecs`. > + > +--pathspec-file-null:: > + Only meaningful with `--pathspec-from-file`. Pathspecs are > + separated with NUL character and are not expected to be quoted. I think my comments from patch 3 about <pathspecs> and the option names apply here as well > -u[<mode>]:: > --untracked-files[=<mode>]:: > diff --git a/builtin/commit.c b/builtin/commit.c > index e588bc6ad3..532f305926 100644 > --- a/builtin/commit.c > +++ b/builtin/commit.c > @@ -107,9 +107,9 @@ static int all, also, interactive, patch_interactive, only, amend, signoff; > static int edit_flag = -1; /* unspecified */ > static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship; > static int config_commit_verbose = -1; /* unspecified */ > -static int no_post_rewrite, allow_empty_message; > +static int no_post_rewrite, allow_empty_message, pathspec_file_null; > static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg; > -static char *sign_commit; > +static char *sign_commit, *pathspec_from_file; > > /* > * The default commit message cleanup mode will remove the lines > @@ -343,6 +343,23 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix > PATHSPEC_PREFER_FULL, > prefix, argv); > > + if (pathspec_from_file) { > + if (interactive) > + die(_("--pathspec-from-file is incompatible with --interactive/--patch")); > + > + if (pathspec.nr) > + die(_("--pathspec-from-file is incompatible with path arguments")); > + > + parse_pathspec_file(&pathspec, 0, > + PATHSPEC_PREFER_FULL, > + prefix, pathspec_from_file, pathspec_file_null); > + } > + else if (pathspec_file_null) > + die(_("--pathspec-file-null requires --pathspec-from-file")); > + > + if (!pathspec.nr && (also || (only && !amend && !allow_empty))) > + die(_("No paths with --include/--only does not make sense.")); I wonder if there is a way of calling parse_pathspec_file() from parse_and_validate_options() instead. Otherwise we end up validating options here instead which is a bit messy. > if (read_cache_preload(&pathspec) < 0) > die(_("index file corrupt")); > > @@ -1198,8 +1215,6 @@ static int parse_and_validate_options(int argc, const char *argv[], > > if (also + only + all + interactive > 1) > die(_("Only one of --include/--only/--all/--interactive/--patch can be used.")); > - if (argc == 0 && (also || (only && !amend && !allow_empty))) > - die(_("No paths with --include/--only does not make sense.")); > cleanup_mode = get_cleanup_mode(cleanup_arg, use_editor); > > handle_untracked_files_arg(s); > @@ -1535,6 +1550,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix) > OPT_BOOL(0, "amend", &amend, N_("amend previous commit")), > OPT_BOOL(0, "no-post-rewrite", &no_post_rewrite, N_("bypass post-rewrite hook")), > { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, N_("mode"), N_("show untracked files, optional modes: all, normal, no. (Default: all)"), PARSE_OPT_OPTARG, NULL, (intptr_t)"all" }, > + OPT_FILENAME(0, "pathspec-from-file", &pathspec_from_file, N_("read pathspecs from file")), > + OPT_BOOL(0, "pathspec-file-null", &pathspec_file_null, N_("with --pathspec-from-file, pathspecs are separated with NUL character")), > /* end commit contents options */ > > OPT_HIDDEN_BOOL(0, "allow-empty", &allow_empty, > diff --git a/t/t7526-commit-pathspec-file.sh b/t/t7526-commit-pathspec-file.sh > new file mode 100755 > index 0000000000..c5d68e01e6 > --- /dev/null > +++ b/t/t7526-commit-pathspec-file.sh The test comments from patch 3 apply here as well I think. Overall this series is nicely structured and is looking pretty good Best Wishes Phillip > @@ -0,0 +1,107 @@ > +#!/bin/sh > + > +test_description='commit --pathspec-from-file' > + > +. ./test-lib.sh > + > +test_tick > + > +cat > expect.a <<EOF > +A fileA.t > +EOF > + > +cat > expect.ab <<EOF > +A fileA.t > +A fileB.t > +EOF > + > +cat > expect.bc <<EOF > +A fileB.t > +A fileC.t > +EOF > + > +test_expect_success setup ' > + test_commit file0 && > + checkpoint=$(git rev-parse --verify HEAD) && > + > + echo A >fileA.t && > + echo B >fileB.t && > + echo C >fileC.t && > + echo D >fileD.t && > + git add fileA.t fileB.t fileC.t fileD.t > +' > + > +restore_checkpoint () { > + git reset --soft "$checkpoint" > +} > + > +verify_state () { > + git diff-tree --no-commit-id --name-status -r HEAD >actual && > + test_cmp "$1" actual > +} > + > +test_expect_success '--pathspec-from-file from stdin' ' > + restore_checkpoint && > + > + echo fileA.t | git commit --pathspec-from-file=- -m "Commit" && > + > + verify_state expect.a > +' > + > +test_expect_success '--pathspec-from-file from file' ' > + restore_checkpoint && > + > + echo fileA.t >list && > + git commit --pathspec-from-file=list -m "Commit" && > + > + verify_state expect.a > +' > + > +test_expect_success 'NUL delimiters' ' > + restore_checkpoint && > + > + printf fileA.tQfileB.t | q_to_nul | git commit --pathspec-from-file=- --pathspec-file-null -m "Commit" && > + > + verify_state expect.ab > +' > + > +test_expect_success 'LF delimiters' ' > + restore_checkpoint && > + > + printf "fileA.t\nfileB.t" | git commit --pathspec-from-file=- -m "Commit" && > + > + verify_state expect.ab > +' > + > +test_expect_success 'CRLF delimiters' ' > + restore_checkpoint && > + > + printf "fileA.t\r\nfileB.t" | git commit --pathspec-from-file=- -m "Commit" && > + > + verify_state expect.ab > +' > + > +test_expect_success 'quotes' ' > + restore_checkpoint && > + > + printf "\"file\\101.t\"" | git commit --pathspec-from-file=- -m "Commit" && > + > + verify_state expect.a > +' > + > +test_expect_success 'quotes not compatible with --pathspec-file-null' ' > + restore_checkpoint && > + > + printf "\"file\\101.t\"" >list && > + test_must_fail git commit --pathspec-from-file=list --pathspec-file-null -m "Commit" > +' > + > +test_expect_success 'only touches what was listed' ' > + restore_checkpoint && > + > + printf "fileB.t\nfileC.t" | git commit --pathspec-from-file=- -m "Commit" && > + > + verify_state expect.bc > +' > + > +test_done > ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 5/5] commit: support the --pathspec-from-file option 2019-11-05 16:27 ` Phillip Wood @ 2019-11-05 19:42 ` Alexandr Miloslavskiy 2019-11-06 15:56 ` Alexandr Miloslavskiy 1 sibling, 0 replies; 80+ messages in thread From: Alexandr Miloslavskiy @ 2019-11-05 19:42 UTC (permalink / raw) To: phillip.wood, Alexandr Miloslavskiy via GitGitGadget, git; +Cc: Junio C Hamano On 05.11.2019 17:27, Phillip Wood wrote: >> Also add new '--pathspec-file-null' switch that mirrors '-z' used in >> various places. Some porcelain commands, such as `git commit`, already >> use '-z', therefore it needed a new unambiguous name. > > It might be worth tailoring the message to the command rather than > having exactly the same message for commit and reset I also was somewhat unhappy about duplication. But I didn't figure how to do that correctly. Currently the messages for 'git reset' and 'git commit' are almost identical. Maybe in 2nd commit I should say something like "Extend `--pathspec-file-null` support to `git commit` (see previous patch for `git reset`)" ? > I think my comments from patch 3 about <pathspecs> and the option names > apply here as well Yes, sure, I will try to apply your suggestions to all patches. Hopefully without forgetting things :) >> + if (!pathspec.nr && (also || (only && !amend && !allow_empty))) >> + die(_("No paths with --include/--only does not make sense.")); > > I wonder if there is a way of calling parse_pathspec_file() from > parse_and_validate_options() instead. Otherwise we end up validating > options here instead which is a bit messy. Yes, I was also somewhat unhappy about that. I will give it more thought. > Overall this series is nicely structured and is looking pretty good Thanks, and also thanks for reviewing my patches! ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 5/5] commit: support the --pathspec-from-file option 2019-11-05 16:27 ` Phillip Wood 2019-11-05 19:42 ` Alexandr Miloslavskiy @ 2019-11-06 15:56 ` Alexandr Miloslavskiy 2019-12-10 10:42 ` Phillip Wood 1 sibling, 1 reply; 80+ messages in thread From: Alexandr Miloslavskiy @ 2019-11-06 15:56 UTC (permalink / raw) To: phillip.wood, Alexandr Miloslavskiy via GitGitGadget, git; +Cc: Junio C Hamano I think I have implemented most suggestions in PatchV2. Thanks! > It might be worth tailoring the message to the command rather than > having exactly the same message for commit and reset I decided to move the general comment to base commit where options are introduced and not repeat it where options are supported. > I wonder if there is a way of calling parse_pathspec_file() from > parse_and_validate_options() instead. Otherwise we end up validating > options here instead which is a bit messy. The code looks a bit too entangled to support that without making it worse. The biggest concern I have is that parse_and_validate_options() will populate `pathspec` and some other function will need to remember to clean it up. I like it better when `pathspec` is handled in one place. I agree that things are not perfect, but this seems to be a consequence of other existing problems. For example, I would have expected a structure instead of a handful of global variables. That would have solved many problems. However, I didn't have the bravery to dive into this refactoring. ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 5/5] commit: support the --pathspec-from-file option 2019-11-06 15:56 ` Alexandr Miloslavskiy @ 2019-12-10 10:42 ` Phillip Wood 2019-12-11 11:43 ` Alexandr Miloslavskiy 0 siblings, 1 reply; 80+ messages in thread From: Phillip Wood @ 2019-12-10 10:42 UTC (permalink / raw) To: Alexandr Miloslavskiy, phillip.wood, Alexandr Miloslavskiy via GitGitGadget, git Cc: Junio C Hamano Hi Alexandr Sorry it has taken me so long to reply On 06/11/2019 15:56, Alexandr Miloslavskiy wrote: > I think I have implemented most suggestions in PatchV2. Thanks! > >> It might be worth tailoring the message to the command rather than >> having exactly the same message for commit and reset > > I decided to move the general comment to base commit where options are > introduced and not repeat it where options are supported. > >> I wonder if there is a way of calling parse_pathspec_file() from >> parse_and_validate_options() instead. Otherwise we end up validating >> options here instead which is a bit messy. > > The code looks a bit too entangled to support that without making it > worse. The biggest concern I have is that parse_and_validate_options() > will populate `pathspec` and some other function will need to remember > to clean it up. I like it better when `pathspec` is handled in one place. I don't think it's so bad if the pathspec is cleaned up just after it is used, the diff below applies on top of your patch - see what you think. The diff also adds dies if --all is given with --pathspec-from-file which (together with a test) would be worth adding to you series I think. > > I agree that things are not perfect, but this seems to be a consequence > of other existing problems. For example, I would have expected a > structure instead of a handful of global variables. That would have > solved many problems. However, I didn't have the bravery to dive into > this refactoring. Yes it is a pain that the builtin functions tend to use a lot of global variables rather than a structure. Best Wishes Phillip --- >8 --- diff --git a/builtin/commit.c b/builtin/commit.c index ed40729355..bb9515c44b 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -330,37 +330,18 @@ static void refresh_cache_or_die(int refresh_flags) } static const char *prepare_index(int argc, const char **argv, const char *prefix, + struct pathspec *pathspec, const struct commit *current_head, int is_status) { struct string_list partial = STRING_LIST_INIT_DUP; - struct pathspec pathspec; + int refresh_flags = REFRESH_QUIET; const char *ret; if (is_status) refresh_flags |= REFRESH_UNMERGED; - parse_pathspec(&pathspec, 0, - PATHSPEC_PREFER_FULL, - prefix, argv); - if (pathspec_from_file) { - if (interactive) - die(_("--pathspec-from-file is incompatible with --interactive/--patch")); - - if (pathspec.nr) - die(_("--pathspec-from-file is incompatible with pathspec arguments")); - - parse_pathspec_file(&pathspec, 0, - PATHSPEC_PREFER_FULL, - prefix, pathspec_from_file, pathspec_file_nul); - } else if (pathspec_file_nul) { - die(_("--pathspec-file-nul requires --pathspec-from-file")); - } - - if (!pathspec.nr && (also || (only && !amend && !allow_empty))) - die(_("No paths with --include/--only does not make sense.")); - - if (read_cache_preload(&pathspec) < 0) + if (read_cache_preload(pathspec) < 0) die(_("index file corrupt")); if (interactive) { @@ -411,9 +392,9 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix * (A) if all goes well, commit the real index; * (B) on failure, rollback the real index. */ - if (all || (also && pathspec.nr)) { + if (all || (also && pathspec->nr)) { hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR); - add_files_to_cache(also ? prefix : NULL, &pathspec, 0); + add_files_to_cache(also ? prefix : NULL, pathspec, 0); refresh_cache_or_die(refresh_flags); update_main_cache_tree(WRITE_TREE_SILENT); if (write_locked_index(&the_index, &index_lock, 0)) @@ -432,7 +413,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix * and create commit from the_index. * We still need to refresh the index here. */ - if (!only && !pathspec.nr) { + if (!only && !pathspec->nr) { hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR); refresh_cache_or_die(refresh_flags); if (active_cache_changed @@ -474,7 +455,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix die(_("cannot do a partial commit during a cherry-pick.")); } - if (list_paths(&partial, !current_head ? NULL : "HEAD", &pathspec)) + if (list_paths(&partial, !current_head ? NULL : "HEAD", pathspec)) exit(1); discard_cache(); @@ -505,7 +486,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix read_cache_from(ret); out: string_list_clear(&partial, 0); - clear_pathspec(&pathspec); + clear_pathspec(pathspec); return ret; } @@ -1148,6 +1129,7 @@ static int parse_and_validate_options(int argc, const char *argv[], const struct option *options, const char * const usage[], const char *prefix, + struct pathspec *pathspec, struct commit *current_head, struct wt_status *s) { @@ -1223,19 +1205,42 @@ static int parse_and_validate_options(int argc, const char *argv[], die(_("paths '%s ...' with -a does not make sense"), argv[0]); + if (pathspec_from_file) { + if (interactive) + die(_("--pathspec-from-file is incompatible with --interactive/--patch")); + + if (argc) + die(_("--pathspec-from-file is incompatible with pathspec arguments")); + + if (all) + die(_("--pathspec-from-file is incompatible with --all")); + + parse_pathspec_file(pathspec, 0, + PATHSPEC_PREFER_FULL, + prefix, pathspec_from_file, pathspec_file_nul); + } else if (pathspec_file_nul) { + die(_("--pathspec-file-nul requires --pathspec-from-file")); + } else { + parse_pathspec(pathspec, 0, PATHSPEC_PREFER_FULL, prefix, argv); + } + + if (!pathspec->nr && (also || (only && !amend && !allow_empty))) + die(_("No paths with --include/--only does not make sense.")); + if (status_format != STATUS_FORMAT_NONE) dry_run = 1; return argc; } static int dry_run_commit(int argc, const char **argv, const char *prefix, + struct pathspec *pathspec, const struct commit *current_head, struct wt_status *s) { int committable; const char *index_file; - index_file = prepare_index(argc, argv, prefix, current_head, 1); + index_file = prepare_index(argc, argv, prefix, pathspec, current_head, 1); committable = run_status(stdout, index_file, prefix, 0, s); rollback_index_files(); @@ -1571,6 +1576,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix) struct commit *current_head = NULL; struct commit_extra_header *extra = NULL; struct strbuf err = STRBUF_INIT; + struct pathspec pathspec; if (argc == 2 && !strcmp(argv[1], "-h")) usage_with_options(builtin_commit_usage, builtin_commit_options); @@ -1590,13 +1596,15 @@ int cmd_commit(int argc, const char **argv, const char *prefix) verbose = -1; /* unspecified */ argc = parse_and_validate_options(argc, argv, builtin_commit_options, builtin_commit_usage, - prefix, current_head, &s); + prefix, &pathspec, current_head, &s); if (verbose == -1) verbose = (config_commit_verbose < 0) ? 0 : config_commit_verbose; if (dry_run) - return dry_run_commit(argc, argv, prefix, current_head, &s); - index_file = prepare_index(argc, argv, prefix, current_head, 0); + return dry_run_commit(argc, argv, prefix, &pathspec, + current_head, &s); + index_file = prepare_index(argc, argv, prefix, &pathspec, current_head, + 0); /* Set up everything for writing the commit object. This includes running hooks, writing the trees, and interacting with the user. */ ^ permalink raw reply related [flat|nested] 80+ messages in thread
* Re: [PATCH 5/5] commit: support the --pathspec-from-file option 2019-12-10 10:42 ` Phillip Wood @ 2019-12-11 11:43 ` Alexandr Miloslavskiy 2019-12-11 14:27 ` Phillip Wood 0 siblings, 1 reply; 80+ messages in thread From: Alexandr Miloslavskiy @ 2019-12-11 11:43 UTC (permalink / raw) To: phillip.wood, Alexandr Miloslavskiy via GitGitGadget, git; +Cc: Junio C Hamano On 10.12.2019 11:42, Phillip Wood wrote: > I don't think it's so bad if the pathspec is cleaned up just after it is used, > the diff below applies on top of your patch - see what you think. The diff > also adds dies if --all is given with --pathspec-from-file which (together > with a test) would be worth adding to you series I think. Unfortunately, your reply came too late, topic was cooking in pu/next for a while and merged into master yesterday: c58ae96f. I understand that your patch consists of two parts: 1) Adding test for --all ------------------------ I must admit that I overlooked that there was a similar test for args-based pathspec. I will add this part in my next topic for --pathspec-from-file. I expect it to appear in the next day or two. I will try to remember to CC you to it. 2) Moving parsing/validation into `parse_and_validate_options()` ------------------------ Again, I agree that having parsing/validation outside is suboptimal. However, with current code, I find it to be a choice between two evils, and my choice was "outside but clear" to "inside but obscure". What I find obscure in your suggestion/patch is that innocently looking `prepare_index()` suddenly clears pathspec as well. It's even harder to see when called through `dry_run_commit()`. Now, let me illustrate. There's a similar case in my TODO list, this time involving a real bug in git. In `init_db()`, the bug occurs in `set_git_dir(real_path(git_dir))`, also due to unexpected clearing. Now that I pointed my finger at it, please try to find what's wrong. I imagine that it won't be easy at all. And it's way harder when there's no reason to dig deep into a very specific line of code. I really try to avoid such type of pitfalls. That's why my choice between two evils is what I did. ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 5/5] commit: support the --pathspec-from-file option 2019-12-11 11:43 ` Alexandr Miloslavskiy @ 2019-12-11 14:27 ` Phillip Wood 2019-12-11 15:06 ` Alexandr Miloslavskiy 2019-12-12 14:56 ` Alexandr Miloslavskiy 0 siblings, 2 replies; 80+ messages in thread From: Phillip Wood @ 2019-12-11 14:27 UTC (permalink / raw) To: Alexandr Miloslavskiy, phillip.wood, Alexandr Miloslavskiy via GitGitGadget, git Cc: Junio C Hamano Hi Alexandr On 11/12/2019 11:43, Alexandr Miloslavskiy wrote: > On 10.12.2019 11:42, Phillip Wood wrote: >> I don't think it's so bad if the pathspec is cleaned up just after it >> is used, >> the diff below applies on top of your patch - see what you think. The >> diff >> also adds dies if --all is given with --pathspec-from-file which >> (together >> with a test) would be worth adding to you series I think. > > Unfortunately, your reply came too late, topic was cooking in pu/next > for a while and merged into master yesterday: c58ae96f. Ah I thought it might be a bit late to fix up the patch, there could always be a follow patch though. > > I understand that your patch consists of two parts: > > 1) Adding test for --all > ------------------------ > I must admit that I overlooked that there was a similar test for > args-based pathspec. I will add this part in my next topic for > --pathspec-from-file. I expect it to appear in the next day or two. I > will try to remember to CC you to it. Thanks, one other thing I forgot to mention yesterday is to ask what the expected behavior is if the user passes an empty file to --pathspec-from-file. With no pathspecs on the command line commit, checkout, reset and restore-files all default to operating on all paths but passing --pathspec-from-file implies the user wants to specify specific paths so I think it would perhaps be better to error out if no paths are given. There is a precedent for this in checkout-index which does nothing if no paths are given (though I can't remember if it errors out or not). > > 2) Moving parsing/validation into `parse_and_validate_options()` > ------------------------ > Again, I agree that having parsing/validation outside is suboptimal. > However, with current code, I find it to be a choice between two evils, > and my choice was "outside but clear" to "inside but obscure". > > What I find obscure in your suggestion/patch is that innocently looking > `prepare_index()` suddenly clears pathspec as well. It's even harder to > see when called through `dry_run_commit()`. It would be easy enough to clear pathspec in cmd_commit() I just didn't bother to do it. > > Now, let me illustrate. There's a similar case in my TODO list, this > time involving a real bug in git. In `init_db()`, the bug occurs in > `set_git_dir(real_path(git_dir))`, also due to unexpected clearing. > > Now that I pointed my finger at it, please try to find what's wrong. I > imagine that it won't be easy at all. And it's way harder when there's > no reason to dig deep into a very specific line of code. > > I really try to avoid such type of pitfalls. That's why my choice > between two evils is what I did. If I had to hazard a guess I'd say that either set_git_dir() calls real_path() which messes up the path passed to it or it does not copy the path passed to it and it is messed up by some other code calling real_path() after set_git_dir() has returned. If my guess is correct (I wouldn't be surprised if it's wrong) I think that's a bit different to the pathspec case as it's about the lifetime of the return value of a function rather than a function freeing an argument passed to it. Best Wishes Phillip ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 5/5] commit: support the --pathspec-from-file option 2019-12-11 14:27 ` Phillip Wood @ 2019-12-11 15:06 ` Alexandr Miloslavskiy 2019-12-11 16:14 ` Junio C Hamano 2019-12-12 14:56 ` Alexandr Miloslavskiy 1 sibling, 1 reply; 80+ messages in thread From: Alexandr Miloslavskiy @ 2019-12-11 15:06 UTC (permalink / raw) To: phillip.wood, Alexandr Miloslavskiy via GitGitGadget, git; +Cc: Junio C Hamano On 11.12.2019 15:27, Phillip Wood wrote: > Thanks, one other thing I forgot to mention yesterday is to ask what the > expected behavior is if the user passes an empty file to > --pathspec-from-file. With no pathspecs on the command line commit, > checkout, reset and restore-files all default to operating on all paths > but passing --pathspec-from-file implies the user wants to specify > specific paths so I think it would perhaps be better to error out if no > paths are given. There is a precedent for this in checkout-index which > does nothing if no paths are given (though I can't remember if it errors > out or not). Back when I composed patch for `git commit`, I spent some time thinking about this question. I agree that empty `--pathspec-from-file` is weird. Eventually I decided that `--pathspec-from-file` shall be as close as possible to passing pathspec args. So empty file should behave the same way as passing zero pathspec args. It's more or less the question "if user does something unexpected, what is expected?". I decided that treating as zero args doesn't sound dangerous to me, and it's a very weird case anyway, so let's just treat as zero args. >> What I find obscure in your suggestion/patch is that innocently >> looking `prepare_index()` suddenly clears pathspec as well. It's even >> harder to see when called through `dry_run_commit()`. > > It would be easy enough to clear pathspec in cmd_commit() I just didn't > bother to do it. Yes, this thought also came to me after I replied. I agree that this is viable and seems to solve most problems. Now I'm in the situation where before me, code already wasn't perfect, and I can continue to extend it in the same direction, or try to refactor to make it better. I actually choose the second path: [1][2]. These were two "don't" lessons for me, as both topics now gather dust in mail graveyard. No good deed remains unpunished, I guess? > I think that's a bit different to > the pathspec case as it's about the lifetime of the return value of a > function rather than a function freeing an argument passed to it. Both cases are about lifetime that ends where it was not expected. You can probably agree that it never makes code easier to understand, and at best it will be properly accounted for and does not explode. Again, both choices were somewhat evil. [1] https://public-inbox.org/git/pull.479.git.1574969538.gitgitgadget@gmail.com/ [2] https://public-inbox.org/git/pull.477.git.1574848137.gitgitgadget@gmail.com/T/#u ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 5/5] commit: support the --pathspec-from-file option 2019-12-11 15:06 ` Alexandr Miloslavskiy @ 2019-12-11 16:14 ` Junio C Hamano 2019-12-11 16:20 ` Alexandr Miloslavskiy 0 siblings, 1 reply; 80+ messages in thread From: Junio C Hamano @ 2019-12-11 16:14 UTC (permalink / raw) To: Alexandr Miloslavskiy Cc: phillip.wood, Alexandr Miloslavskiy via GitGitGadget, git Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> writes: > and I can continue to extend it in the same direction, or try to > refactor to make it better. I actually choose the second path: > [1][2]. These were two "don't" lessons for me, as both topics now > gather dust in mail graveyard. If nobody commented on your patches, that is not a punishment. It could have been that reviewers were busy addressing other issues back then, in which case a gentle ping by resending a polished version (it could be that the changes were not presented well to attract reviewers' attention---polishing the proposed log messages without changing the patch text might be all it takes to make them realize that the topic is worth looking at) would help. > [1] > https://public-inbox.org/git/pull.479.git.1574969538.gitgitgadget@gmail.com/ > [2] > https://public-inbox.org/git/pull.477.git.1574848137.gitgitgadget@gmail.com/T/#u I actually was meaning to read and comment on [1], but lack of time pushed it down in my queue. Sorry about that. ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 5/5] commit: support the --pathspec-from-file option 2019-12-11 16:14 ` Junio C Hamano @ 2019-12-11 16:20 ` Alexandr Miloslavskiy 0 siblings, 0 replies; 80+ messages in thread From: Alexandr Miloslavskiy @ 2019-12-11 16:20 UTC (permalink / raw) To: Junio C Hamano; +Cc: phillip.wood, Alexandr Miloslavskiy via GitGitGadget, git On 11.12.2019 17:14, Junio C Hamano wrote: > If nobody commented on your patches, that is not a punishment. It > could have been that reviewers were busy addressing other issues > back then, in which case a gentle ping by resending a polished > version (it could be that the changes were not presented well to > attract reviewers' attention---polishing the proposed log messages > without changing the patch text might be all it takes to make them > realize that the topic is worth looking at) would help. Thanks for letting me know! This is actually a relief. I will then merge both topics with my next '--pathspec-from-file' batch, because they are all parts of one work. I just thought that since they can be submitted separately I should do that, maybe that was a mistake. ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 5/5] commit: support the --pathspec-from-file option 2019-12-11 14:27 ` Phillip Wood 2019-12-11 15:06 ` Alexandr Miloslavskiy @ 2019-12-12 14:56 ` Alexandr Miloslavskiy 1 sibling, 0 replies; 80+ messages in thread From: Alexandr Miloslavskiy @ 2019-12-12 14:56 UTC (permalink / raw) To: phillip.wood, Alexandr Miloslavskiy via GitGitGadget, git; +Cc: Junio C Hamano The new topic is now submitted: https://lore.kernel.org/git/pull.490.git.1576161385.gitgitgadget@gmail.com/ Phillip, I tried to CC you, but GitGitGadget did something weird. I'm currently trying to fix CC issues there: https://github.com/gitgitgadget/gitgitgadget/pull/163 ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH 5/5] commit: support the --pathspec-from-file option 2019-11-04 19:26 ` [PATCH 5/5] commit: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget 2019-11-05 16:27 ` Phillip Wood @ 2019-11-06 4:51 ` Junio C Hamano 1 sibling, 0 replies; 80+ messages in thread From: Junio C Hamano @ 2019-11-06 4:51 UTC (permalink / raw) To: Alexandr Miloslavskiy via GitGitGadget; +Cc: git, Alexandr Miloslavskiy "Alexandr Miloslavskiy via GitGitGadget" <gitgitgadget@gmail.com> writes: > From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> > > This option solves the problem of commandline length limit for UI's > built on top of git. Plumbing commands are not always a good fit, for > two major reasons: > 1) Some UI's serve as assistants that help user run git commands. In > this case, replacing familiar commands with plumbing commands will > confuse most users. > 2) Some UI's have started and grown with porcelain commands. Replacing > existing logic with plumbing commands could be cumbersome and prone > to various new problems. I think all the comments I made on "reset" with these two options, both to the proposed log message and to the patch text, applies to this this step, too, so I won't repeat them. Thanks for working on this topic. ^ permalink raw reply [flat|nested] 80+ messages in thread
* [PATCH v2 0/6] Add --pathspec-from-file option for reset, commit 2019-11-04 19:26 [PATCH 0/5] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget ` (4 preceding siblings ...) 2019-11-04 19:26 ` [PATCH 5/5] commit: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget @ 2019-11-06 15:51 ` Alexandr Miloslavskiy via GitGitGadget 2019-11-06 15:51 ` [PATCH v2 1/6] parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul` Alexandr Miloslavskiy via GitGitGadget ` (6 more replies) 5 siblings, 7 replies; 80+ messages in thread From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-06 15:51 UTC (permalink / raw) To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano Adds --pathspec-from-file option for porcelain commands to avoid commandline length limit. So far I implemented it for git commit and git reset, but my goal is to support other commands as well after these patches are reviewed. The patches are based on the following discussions: https://public-inbox.org/git/c3be6eff-365b-96b8-16d2-0528612fc1fc@syntevo.com/T/#u There, --stdin-paths was suggested. https://public-inbox.org/git/a38bc928-7ccd-e2d9-b89b-23298e9fa95d@syntevo.com/T/#u There, --stdin-paths was extended to --paths-file. https://public-inbox.org/git/pull.133.git.gitgitgadget@gmail.com/ https://github.com/gitgitgadget/git/pull/133Patch from Johannes, I used it as base of my patch. There, --pathspec-from-file is suggested in discussion. Major changes compared to patch from Johannes: 1) --pathspec-from-file allows to read file, not just stdin 2) --literal-pathspecs should be honored. This is a good improvement because it keeps --pathspec-from-file much closer to passing args on commandline. Since the goal of the new option is to avoid commandline length limit, both behaviors should be close to each other. 3) Patches are designed with all other git commands in mind Changes from V1: A lot of small changes here and there, in accordance with review suggestions. Alexandr Miloslavskiy (6): parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul` pathspec: add new function to parse file doc: reset: unify <pathspec> description reset: support the `--pathspec-from-file` option doc: commit: unify <pathspec> description commit: support the --pathspec-from-file option Documentation/git-commit.txt | 29 ++++-- Documentation/git-reset.txt | 37 +++++--- builtin/commit.c | 25 +++++- builtin/reset.c | 25 +++++- parse-options.h | 2 + pathspec.c | 38 ++++++++ pathspec.h | 10 +++ t/t7107-reset-pathspec-file.sh | 155 ++++++++++++++++++++++++++++++++ t/t7526-commit-pathspec-file.sh | 130 +++++++++++++++++++++++++++ 9 files changed, 425 insertions(+), 26 deletions(-) create mode 100755 t/t7107-reset-pathspec-file.sh create mode 100755 t/t7526-commit-pathspec-file.sh base-commit: da72936f544fec5a335e66432610e4cef4430991 Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-445%2FSyntevoAlex%2F%230207_pathspec_from_file-v2 Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-445/SyntevoAlex/#0207_pathspec_from_file-v2 Pull-Request: https://github.com/gitgitgadget/git/pull/445 Range-diff vs v1: -: ---------- > 1: 2dfaccf0d5 parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul` 1: 52e7a84a2e ! 2: 96697ba072 pathspec: add new function to parse file @@ -50,16 +50,13 @@ + if (!strcmp(file, "-")) + in = stdin; + else -+ in = fopen(file, "r"); -+ -+ if (!in) -+ die(_("could not open '%s' for reading"), file); ++ in = xfopen(file, "r"); + + while (getline_fn(&buf, in) != EOF) { + if (!nul_term_line && buf.buf[0] == '"') { + strbuf_reset(&unquoted); + if (unquote_c_style(&unquoted, buf.buf, NULL)) -+ die(_("line is badly quoted")); ++ die(_("line is badly quoted: %s"), buf.buf); + strbuf_swap(&buf, &unquoted); + } + argv_array_push(&parsed_file, buf.buf); @@ -91,11 +88,11 @@ + * When 'file' is exactly "-" it uses 'stdin' instead. + */ +void parse_pathspec_file(struct pathspec *pathspec, -+ unsigned magic_mask, -+ unsigned flags, -+ const char *prefix, -+ const char *file, -+ int nul_term_line); ++ unsigned magic_mask, ++ unsigned flags, ++ const char *prefix, ++ const char *file, ++ int nul_term_line); void copy_pathspec(struct pathspec *dst, const struct pathspec *src); void clear_pathspec(struct pathspec *); 2: 1740ac7a29 ! 3: f961a5155a doc: reset: unify <pathspec> description @@ -21,8 +21,11 @@ DESCRIPTION @@ + In the third form, set the current branch head (`HEAD`) to `<commit>`, optionally modifying index and working tree to match. The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms. ++The <pathspec> is used to limit the paths affected by the operation ++(see the entry for 'pathspec' in linkgit:gitglossary[7] for more details). -'git reset' [-q] [<tree-ish>] [--] <paths>...:: - This form resets the index entries for all `<paths>` to their @@ -34,14 +37,11 @@ -This means that `git reset <paths>` is the opposite of `git add -<paths>`. This command is equivalent to -`git restore [--source=<tree-ish>] --staged <paths>...`. -+For more details about the <pathspec> syntax, see the 'pathspec' entry -+in linkgit:gitglossary[7]. - + --After running `git reset <paths>` to update the index entry, you can +This means that `git reset <pathspec>` is the opposite of `git add +<pathspec>`. This command is equivalent to +`git restore [--source=<tree-ish>] --staged <pathspec>...`. -++ + + +-After running `git reset <paths>` to update the index entry, you can +After running `git reset <pathspec>` to update the index entry, you can use linkgit:git-restore[1] to check the contents out of the index to the working tree. Alternatively, using linkgit:git-restore[1] @@ -54,10 +54,18 @@ Interactively select hunks in the difference between the index and `<tree-ish>` (defaults to `HEAD`). The chosen hunks are applied in reverse to the index. - + -+For more details about the <pathspec> syntax, see the 'pathspec' entry -+in linkgit:gitglossary[7]. -++ - This means that `git reset -p` is the opposite of `git add -p`, i.e. - you can use it to selectively reset hunks. See the ``Interactive Mode'' - section of linkgit:git-add[1] to learn how to operate the `--patch` mode. + + diff --git a/builtin/reset.c b/builtin/reset.c + --- a/builtin/reset.c + +++ b/builtin/reset.c +@@ + + static const char * const git_reset_usage[] = { + N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"), +- N_("git reset [-q] [<tree-ish>] [--] <paths>..."), +- N_("git reset --patch [<tree-ish>] [--] [<paths>...]"), ++ N_("git reset [-q] [<tree-ish>] [--] <pathspec>..."), ++ N_("git reset --patch [<tree-ish>] [--] [<pathspec>...]"), + NULL + }; + 3: 8d9f1fbc18 ! 4: d72d4f16b5 reset: support the --pathspec-from-file option @@ -1,33 +1,13 @@ Author: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> - reset: support the --pathspec-from-file option - - This option solves the problem of commandline length limit for UI's - built on top of git. Plumbing commands are not always a good fit, for - two major reasons: - 1) Some UI's serve as assistants that help user run git commands. In - this case, replacing familiar commands with plumbing commands will - confuse most users. - 2) Some UI's have started and grown with porcelain commands. Replacing - existing logic with plumbing commands could be cumbersome and prone - to various new problems. - - The new option is designed to behave very close to pathspecs passed in - commandline args, so that switching from one to another is simple. - - The new option allows to read either a specified file or `stdin`. - Reading from file is a good way to avoid competing for stdin, and - also gives some extra flexibility. + reset: support the `--pathspec-from-file` option Decisions taken for simplicity: - 1) The new option is declared incompatible with other options that - could use `stdin`. - 2) It is not allowed to pass some refspecs in args and others in file. - 3) New options do not have shorthands to avoid shorthand conflicts. - - Also add new '--pathspec-file-null' switch that mirrors '-z' used in - various places. Some porcelain commands, such as `git commit`, already - use '-z', therefore it needed a new unambiguous name. + 1) For now, `--pathspec-from-file` is declared incompatible with + `--patch`, even when <file> is not `stdin`. Such use case it not + really expected. Also, it is harder to support in `git commit`, so + I decided to make it incompatible in all places. + 2) It is not allowed to pass pathspec in both args and file. Co-authored-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> @@ -39,7 +19,7 @@ -------- [verse] 'git reset' [-q] [<tree-ish>] [--] <pathspec>... -+'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-null]] [<tree-ish>] ++'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-nul]] [<tree-ish>] 'git reset' (--patch | -p) [<tree-ish>] [--] [<pathspec>...] 'git reset' [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>] @@ -51,11 +31,13 @@ +In the last form, set the current branch head (`HEAD`) to `<commit>`, optionally modifying index and working tree to match. The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms. + The <pathspec> is used to limit the paths affected by the operation + (see the entry for 'pathspec' in linkgit:gitglossary[7] for more details). 'git reset' [-q] [<tree-ish>] [--] <pathspec>...:: - This form resets the index entries for all `<pathspec>` to their -+'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-null]] [<tree-ish>]:: -+ These forms reset the index entries for all `<pathspec>` to their ++'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-nul]] [<tree-ish>]:: ++ These forms reset the index entries matching pathspec to their state at `<tree-ish>`. (It does not affect the working tree or the current branch.) + @@ -64,14 +46,15 @@ override the default behavior. +--pathspec-from-file=<file>:: -+ Read `<pathspec>` from `<file>` instead. If `<file>` is exactly `-` -+ then read from standard input. Pathspecs are separated by LF or -+ CR/LF. Pathspecs can be quoted as explained for the configuration -+ variable `core.quotePath` (see linkgit:git-config[1]). See also -+ `--pathspec-file-null` and global `--literal-pathspecs`. -+ -+--pathspec-file-null:: -+ Only meaningful with `--pathspec-from-file`. Pathspecs are ++ Pathspec is passed in `<file>` instead of commandline args. If ++ `<file>` is exactly `-` then standard input is used. Pathspec ++ elements are separated by LF or CR/LF. Pathspec elements can be ++ quoted as explained for the configuration variable `core.quotePath` ++ (see linkgit:git-config[1]). See also `--pathspec-file-nul` and ++ global `--literal-pathspecs`. ++ ++--pathspec-file-nul:: ++ Only meaningful with `--pathspec-from-file`. Pathspec elements are + separated with NUL character and are not expected to be quoted. EXAMPLES @@ -83,9 +66,9 @@ @@ static const char * const git_reset_usage[] = { N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"), - N_("git reset [-q] [<tree-ish>] [--] <paths>..."), -+ N_("git reset [-q] [--pathspec-from-file [--pathspec-file-null]] [<tree-ish>]"), - N_("git reset --patch [<tree-ish>] [--] [<paths>...]"), + N_("git reset [-q] [<tree-ish>] [--] <pathspec>..."), ++ N_("git reset [-q] [--pathspec-from-file [--pathspec-file-nul]] [<tree-ish>]"), + N_("git reset --patch [<tree-ish>] [--] [<pathspec>...]"), NULL }; @@ @@ -94,7 +77,7 @@ int reset_type = NONE, update_ref_status = 0, quiet = 0; - int patch_mode = 0, unborn; - const char *rev; -+ int patch_mode = 0, pathspec_file_null = 0, unborn; ++ int patch_mode = 0, pathspec_file_nul = 0, unborn; + const char *rev, *pathspec_from_file = NULL; struct object_id oid; struct pathspec pathspec; @@ -103,10 +86,8 @@ OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")), OPT_BOOL('N', "intent-to-add", &intent_to_add, N_("record only the fact that removed paths will be added later")), -+ OPT_FILENAME(0, "pathspec-from-file", &pathspec_from_file, -+ N_("read pathspecs from file")), -+ OPT_BOOL(0, "pathspec-file-null", &pathspec_file_null, -+ N_("with --pathspec-from-file, pathspecs are separated with NUL character")), ++ OPT_PATHSPEC_FROM_FILE(&pathspec_from_file), ++ OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul), OPT_END() }; @@ -119,13 +100,14 @@ + die(_("--pathspec-from-file is incompatible with --patch")); + + if (pathspec.nr) -+ die(_("--pathspec-from-file is incompatible with path arguments")); ++ die(_("--pathspec-from-file is incompatible with pathspec arguments")); + + parse_pathspec_file(&pathspec, 0, + PATHSPEC_PREFER_FULL, -+ prefix, pathspec_from_file, pathspec_file_null); -+ } else if (pathspec_file_null) -+ die(_("--pathspec-file-null requires --pathspec-from-file")); ++ prefix, pathspec_from_file, pathspec_file_nul); ++ } else if (pathspec_file_nul) { ++ die(_("--pathspec-file-nul requires --pathspec-from-file")); ++ } + unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid); if (unborn) { @@ -142,21 +124,7 @@ + +. ./test-lib.sh + -+cat > expect.a <<EOF -+ D fileA.t -+EOF -+ -+cat > expect.ab <<EOF -+ D fileA.t -+ D fileB.t -+EOF -+ -+cat > expect.a_bc_d <<EOF -+D fileA.t -+ D fileB.t -+ D fileC.t -+D fileD.t -+EOF ++test_tick + +test_expect_success setup ' + echo A >fileA.t && @@ -165,16 +133,16 @@ + echo D >fileD.t && + git add . && + git commit --include . -m "Commit" && -+ checkpoint=$(git rev-parse --verify HEAD) ++ git tag checkpoint +' + +restore_checkpoint () { -+ git reset --hard "$checkpoint" ++ git reset --hard checkpoint +} + -+verify_state () { ++verify_expect () { + git status --porcelain -- fileA.t fileB.t fileC.t fileD.t >actual && -+ test_cmp "$1" actual ++ test_cmp expect actual +} + +test_expect_success '--pathspec-from-file from stdin' ' @@ -183,7 +151,10 @@ + git rm fileA.t && + echo fileA.t | git reset --pathspec-from-file=- && + -+ verify_state expect.a ++ cat >expect <<-\EOF && ++ D fileA.t ++ EOF ++ verify_expect +' + +test_expect_success '--pathspec-from-file from file' ' @@ -193,34 +164,62 @@ + echo fileA.t >list && + git reset --pathspec-from-file=list && + -+ verify_state expect.a ++ cat >expect <<-\EOF && ++ D fileA.t ++ EOF ++ verify_expect +' + +test_expect_success 'NUL delimiters' ' + restore_checkpoint && + + git rm fileA.t fileB.t && -+ printf fileA.tQfileB.t | q_to_nul | git reset --pathspec-from-file=- --pathspec-file-null && ++ printf "fileA.t\0fileB.t\0" | git reset --pathspec-from-file=- --pathspec-file-nul && + -+ verify_state expect.ab ++ cat >expect <<-\EOF && ++ D fileA.t ++ D fileB.t ++ EOF ++ verify_expect +' + +test_expect_success 'LF delimiters' ' + restore_checkpoint && + + git rm fileA.t fileB.t && ++ printf "fileA.t\nfileB.t\n" | git reset --pathspec-from-file=- && ++ ++ cat >expect <<-\EOF && ++ D fileA.t ++ D fileB.t ++ EOF ++ verify_expect ++' ++ ++test_expect_success 'no trailing delimiter' ' ++ restore_checkpoint && ++ ++ git rm fileA.t fileB.t && + printf "fileA.t\nfileB.t" | git reset --pathspec-from-file=- && + -+ verify_state expect.ab ++ cat >expect <<-\EOF && ++ D fileA.t ++ D fileB.t ++ EOF ++ verify_expect +' + +test_expect_success 'CRLF delimiters' ' + restore_checkpoint && + + git rm fileA.t fileB.t && -+ printf "fileA.t\r\nfileB.t" | git reset --pathspec-from-file=- && ++ printf "fileA.t\r\nfileB.t\r\n" | git reset --pathspec-from-file=- && + -+ verify_state expect.ab ++ cat >expect <<-\EOF && ++ D fileA.t ++ D fileB.t ++ EOF ++ verify_expect +' + +test_expect_success 'quotes' ' @@ -229,21 +228,27 @@ + git rm fileA.t && + printf "\"file\\101.t\"" | git reset --pathspec-from-file=- && + -+ verify_state expect.a ++ cat >expect <<-\EOF && ++ D fileA.t ++ EOF ++ verify_expect +' + -+test_expect_success 'quotes not compatible with --pathspec-file-null' ' ++test_expect_success 'quotes not compatible with --pathspec-file-nul' ' + restore_checkpoint && + + git rm fileA.t && + printf "\"file\\101.t\"" >list && + # Note: "git reset" has not yet learned to fail on wrong pathspecs -+ git reset --pathspec-from-file=list --pathspec-file-null && ++ git reset --pathspec-from-file=list --pathspec-file-nul && + -+ test_must_fail verify_state expect.a ++ cat >expect <<-\EOF && ++ D fileA.t ++ EOF ++ test_must_fail verify_expect +' + -+test_expect_success '--pathspec-from-file is not compatible with --soft --hard' ' ++test_expect_success '--pathspec-from-file is not compatible with --soft or --hard' ' + restore_checkpoint && + + git rm fileA.t && @@ -256,9 +261,15 @@ + restore_checkpoint && + + git rm fileA.t fileB.t fileC.t fileD.t && -+ printf "fileB.t\nfileC.t" | git reset --pathspec-from-file=- && -+ -+ verify_state expect.a_bc_d ++ printf "fileB.t\nfileC.t\n" | git reset --pathspec-from-file=- && ++ ++ cat >expect <<-\EOF && ++ D fileA.t ++ D fileB.t ++ D fileC.t ++ D fileD.t ++ EOF ++ verify_expect +' + +test_done 4: 251d06e27f ! 5: 20c4495fd3 doc: commit: unify <pathspec> description @@ -24,12 +24,16 @@ Do not interpret any more arguments as options. -<file>...:: +- When files are given on the command line, the command +- commits the contents of the named files, without +- recording the changes already staged. The contents of +- these files are also staged for the next commit on top +- of what have been staged before. +<pathspec>...:: - When files are given on the command line, the command - commits the contents of the named files, without - recording the changes already staged. The contents of - these files are also staged for the next commit on top - of what have been staged before. ++ When pathspec is given on the command line, commit the contents of ++ the files that match the pathspec without recording the changes ++ already added to the index. The contents of these files are also ++ staged for the next commit on top of what have been staged before. ++ +For more details about the <pathspec> syntax, see the 'pathspec' entry +in linkgit:gitglossary[7]. 5: f484704689 ! 6: cb5fc9b14d commit: support the --pathspec-from-file option @@ -2,32 +2,12 @@ commit: support the --pathspec-from-file option - This option solves the problem of commandline length limit for UI's - built on top of git. Plumbing commands are not always a good fit, for - two major reasons: - 1) Some UI's serve as assistants that help user run git commands. In - this case, replacing familiar commands with plumbing commands will - confuse most users. - 2) Some UI's have started and grown with porcelain commands. Replacing - existing logic with plumbing commands could be cumbersome and prone - to various new problems. - - The new option is designed to behave very close to pathspecs passed in - commandline args, so that switching from one to another is simple. - - The new option allows to read either a specified file or `stdin`. - Reading from file is a good way to avoid competing for stdin, and - also gives some extra flexibility. - Decisions taken for simplicity: - 1) The new option is declared incompatible with other options that - could use `stdin`. - 2) It is not allowed to pass some refspecs in args and others in file. - 3) New options do not have shorthands to avoid shorthand conflicts. - - Also add new '--pathspec-file-null' switch that mirrors '-z' used in - various places. Some porcelain commands, such as `git commit`, already - use '-z', therefore it needed a new unambiguous name. + 1) For now, `--pathspec-from-file` is declared incompatible with + `--interactive/--patch`, even when <file> is not `stdin`. Such use + case it not really expected. Also, it would require changes to + `interactive_add()`. + 2) It is not allowed to pass pathspec in both args and file. Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> @@ -39,7 +19,7 @@ [--allow-empty-message] [--no-verify] [-e] [--author=<author>] [--date=<date>] [--cleanup=<mode>] [--[no-]status] - [-i | -o] [-S[<keyid>]] [--] [<pathspec>...] -+ [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-null]] ++ [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]] + [-S[<keyid>]] [--] [<pathspec>...] DESCRIPTION @@ -50,14 +30,15 @@ paths are also not required, and an empty commit will be created. + +--pathspec-from-file=<file>:: -+ Read `<pathspec>` from `<file>` instead. If `<file>` is exactly `-` -+ then read from standard input. Pathspecs are separated by LF or -+ CR/LF. Pathspecs can be quoted as explained for the configuration -+ variable `core.quotePath` (see linkgit:git-config[1]). See also -+ `--pathspec-file-null` and global `--literal-pathspecs`. -+ -+--pathspec-file-null:: -+ Only meaningful with `--pathspec-from-file`. Pathspecs are ++ Pathspec is passed in `<file>` instead of commandline args. If ++ `<file>` is exactly `-` then standard input is used. Pathspec ++ elements are separated by LF or CR/LF. Pathspec elements can be ++ quoted as explained for the configuration variable `core.quotePath` ++ (see linkgit:git-config[1]). See also `--pathspec-file-nul` and ++ global `--literal-pathspecs`. ++ ++--pathspec-file-nul:: ++ Only meaningful with `--pathspec-from-file`. Pathspec elements are + separated with NUL character and are not expected to be quoted. -u[<mode>]:: @@ -71,7 +52,7 @@ static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship; static int config_commit_verbose = -1; /* unspecified */ -static int no_post_rewrite, allow_empty_message; -+static int no_post_rewrite, allow_empty_message, pathspec_file_null; ++static int no_post_rewrite, allow_empty_message, pathspec_file_nul; static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg; -static char *sign_commit; +static char *sign_commit, *pathspec_from_file; @@ -87,14 +68,14 @@ + die(_("--pathspec-from-file is incompatible with --interactive/--patch")); + + if (pathspec.nr) -+ die(_("--pathspec-from-file is incompatible with path arguments")); ++ die(_("--pathspec-from-file is incompatible with pathspec arguments")); + + parse_pathspec_file(&pathspec, 0, + PATHSPEC_PREFER_FULL, -+ prefix, pathspec_from_file, pathspec_file_null); ++ prefix, pathspec_from_file, pathspec_file_nul); ++ } else if (pathspec_file_nul) { ++ die(_("--pathspec-file-nul requires --pathspec-from-file")); + } -+ else if (pathspec_file_null) -+ die(_("--pathspec-file-null requires --pathspec-from-file")); + + if (!pathspec.nr && (also || (only && !amend && !allow_empty))) + die(_("No paths with --include/--only does not make sense.")); @@ -115,8 +96,8 @@ OPT_BOOL(0, "amend", &amend, N_("amend previous commit")), OPT_BOOL(0, "no-post-rewrite", &no_post_rewrite, N_("bypass post-rewrite hook")), { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, N_("mode"), N_("show untracked files, optional modes: all, normal, no. (Default: all)"), PARSE_OPT_OPTARG, NULL, (intptr_t)"all" }, -+ OPT_FILENAME(0, "pathspec-from-file", &pathspec_from_file, N_("read pathspecs from file")), -+ OPT_BOOL(0, "pathspec-file-null", &pathspec_file_null, N_("with --pathspec-from-file, pathspecs are separated with NUL character")), ++ OPT_PATHSPEC_FROM_FILE(&pathspec_from_file), ++ OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul), /* end commit contents options */ OPT_HIDDEN_BOOL(0, "allow-empty", &allow_empty, @@ -134,23 +115,9 @@ + +test_tick + -+cat > expect.a <<EOF -+A fileA.t -+EOF -+ -+cat > expect.ab <<EOF -+A fileA.t -+A fileB.t -+EOF -+ -+cat > expect.bc <<EOF -+A fileB.t -+A fileC.t -+EOF -+ +test_expect_success setup ' + test_commit file0 && -+ checkpoint=$(git rev-parse --verify HEAD) && ++ git tag checkpoint && + + echo A >fileA.t && + echo B >fileB.t && @@ -160,12 +127,12 @@ +' + +restore_checkpoint () { -+ git reset --soft "$checkpoint" ++ git reset --soft checkpoint +} + -+verify_state () { ++verify_expect () { + git diff-tree --no-commit-id --name-status -r HEAD >actual && -+ test_cmp "$1" actual ++ test_cmp expect actual +} + +test_expect_success '--pathspec-from-file from stdin' ' @@ -173,7 +140,10 @@ + + echo fileA.t | git commit --pathspec-from-file=- -m "Commit" && + -+ verify_state expect.a ++ cat >expect <<-\EOF && ++ A fileA.t ++ EOF ++ verify_expect +' + +test_expect_success '--pathspec-from-file from file' ' @@ -182,31 +152,58 @@ + echo fileA.t >list && + git commit --pathspec-from-file=list -m "Commit" && + -+ verify_state expect.a ++ cat >expect <<-\EOF && ++ A fileA.t ++ EOF ++ verify_expect +' + +test_expect_success 'NUL delimiters' ' + restore_checkpoint && + -+ printf fileA.tQfileB.t | q_to_nul | git commit --pathspec-from-file=- --pathspec-file-null -m "Commit" && ++ printf "fileA.t\0fileB.t\0" | git commit --pathspec-from-file=- --pathspec-file-nul -m "Commit" && + -+ verify_state expect.ab ++ cat >expect <<-\EOF && ++ A fileA.t ++ A fileB.t ++ EOF ++ verify_expect +' + +test_expect_success 'LF delimiters' ' + restore_checkpoint && + ++ printf "fileA.t\nfileB.t\n" | git commit --pathspec-from-file=- -m "Commit" && ++ ++ cat >expect <<-\EOF && ++ A fileA.t ++ A fileB.t ++ EOF ++ verify_expect ++' ++ ++test_expect_success 'no trailing delimiter' ' ++ restore_checkpoint && ++ + printf "fileA.t\nfileB.t" | git commit --pathspec-from-file=- -m "Commit" && + -+ verify_state expect.ab ++ cat >expect <<-\EOF && ++ A fileA.t ++ A fileB.t ++ EOF ++ verify_expect +' + +test_expect_success 'CRLF delimiters' ' + restore_checkpoint && + -+ printf "fileA.t\r\nfileB.t" | git commit --pathspec-from-file=- -m "Commit" && ++ printf "fileA.t\r\nfileB.t\r\n" | git commit --pathspec-from-file=- -m "Commit" && + -+ verify_state expect.ab ++ cat >expect <<-\EOF && ++ A fileA.t ++ A fileB.t ++ EOF ++ verify_expect +' + +test_expect_success 'quotes' ' @@ -214,22 +211,29 @@ + + printf "\"file\\101.t\"" | git commit --pathspec-from-file=- -m "Commit" && + -+ verify_state expect.a ++ cat >expect <<-\EOF && ++ A fileA.t ++ EOF ++ verify_expect expect +' + -+test_expect_success 'quotes not compatible with --pathspec-file-null' ' ++test_expect_success 'quotes not compatible with --pathspec-file-nul' ' + restore_checkpoint && + + printf "\"file\\101.t\"" >list && -+ test_must_fail git commit --pathspec-from-file=list --pathspec-file-null -m "Commit" ++ test_must_fail git commit --pathspec-from-file=list --pathspec-file-nul -m "Commit" +' + +test_expect_success 'only touches what was listed' ' + restore_checkpoint && + -+ printf "fileB.t\nfileC.t" | git commit --pathspec-from-file=- -m "Commit" && ++ printf "fileB.t\nfileC.t\n" | git commit --pathspec-from-file=- -m "Commit" && + -+ verify_state expect.bc ++ cat >expect <<-\EOF && ++ A fileB.t ++ A fileC.t ++ EOF ++ verify_expect +' + +test_done -- gitgitgadget ^ permalink raw reply [flat|nested] 80+ messages in thread
* [PATCH v2 1/6] parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul` 2019-11-06 15:51 ` [PATCH v2 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget @ 2019-11-06 15:51 ` Alexandr Miloslavskiy via GitGitGadget 2019-11-06 15:51 ` [PATCH v2 2/6] pathspec: add new function to parse file Alexandr Miloslavskiy via GitGitGadget ` (5 subsequent siblings) 6 siblings, 0 replies; 80+ messages in thread From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-06 15:51 UTC (permalink / raw) To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> Support for various porcelain commands will arrive via additional patches. `--pathspec-from-file` solves the problem of commandline length limit for UIs built on top of git. Plumbing commands are not always a good fit, for two major reasons: 1) Some UIs show executed commands to user. In this case, porcelain commands are expected. One reason for that is letting user learn git commands by clicking UI buttons. The other reason is letting user study the history of commands in case of any unexpected results. Both of these will lose most of their value if UI uses combinations of arcane plumbing commands. 2) Some UIs have started and grown with porcelain commands. Replacing existing logic with plumbing commands could be cumbersome and prone to various new problems. `--pathspec-from-file` will behave very close to pathspec passed in commandline args, so that switching from one to another is simple. `--pathspec-from-file` will read either a specified file or `stdin` (when file is exactly "-"). Reading from file is a good way to avoid competing for `stdin`, and also gives some extra flexibility. `--pathspec-file-nul` switch mirrors `-z` already used in various places. Some porcelain commands, such as `git commit`, already use `-z`, therefore it needed a new unambiguous name. New options do not have shorthands to avoid shorthand conflicts. It is not expected that they will be typed in console. Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> --- parse-options.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/parse-options.h b/parse-options.h index 38a33a087e..c6cc01e715 100644 --- a/parse-options.h +++ b/parse-options.h @@ -330,5 +330,7 @@ int parse_opt_passthru_argv(const struct option *, const char *, int); #define OPT_WITH(v, h) _OPT_CONTAINS_OR_WITH("with", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG) #define OPT_WITHOUT(v, h) _OPT_CONTAINS_OR_WITH("without", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG) #define OPT_CLEANUP(v) OPT_STRING(0, "cleanup", v, N_("mode"), N_("how to strip spaces and #comments from message")) +#define OPT_PATHSPEC_FROM_FILE(v) OPT_FILENAME(0, "pathspec-from-file", v, N_("read pathspec from file")) +#define OPT_PATHSPEC_FILE_NUL(v) OPT_BOOL(0, "pathspec-file-nul", v, N_("with --pathspec-from-file, pathspec elements are separated with NUL character")) #endif -- gitgitgadget ^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH v2 2/6] pathspec: add new function to parse file 2019-11-06 15:51 ` [PATCH v2 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget 2019-11-06 15:51 ` [PATCH v2 1/6] parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul` Alexandr Miloslavskiy via GitGitGadget @ 2019-11-06 15:51 ` Alexandr Miloslavskiy via GitGitGadget 2019-11-19 5:59 ` Junio C Hamano 2019-11-06 15:51 ` [PATCH v2 3/6] doc: reset: unify <pathspec> description Alexandr Miloslavskiy via GitGitGadget ` (4 subsequent siblings) 6 siblings, 1 reply; 80+ messages in thread From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-06 15:51 UTC (permalink / raw) To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> This will be used to support the new option '--pathspec-from-file' in `git add`, `git-commit`, `git reset` etc. Note also that we specifically handle CR/LF line endings to support Windows better. To simplify code, file is first parsed into `argv_array`. This allows to avoid refactoring `parse_pathspec()`. I considered adding `nul_term_line` to `flags` instead, but decided that it doesn't fit there. The new code is mostly taken from `cmd_update_index()` and `split_mail_conv()`. Co-authored-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> --- pathspec.c | 38 ++++++++++++++++++++++++++++++++++++++ pathspec.h | 10 ++++++++++ 2 files changed, 48 insertions(+) diff --git a/pathspec.c b/pathspec.c index 12c2b322b3..2e9b376318 100644 --- a/pathspec.c +++ b/pathspec.c @@ -3,6 +3,8 @@ #include "dir.h" #include "pathspec.h" #include "attr.h" +#include "argv-array.h" +#include "quote.h" /* * Finds which of the given pathspecs match items in the index. @@ -613,6 +615,42 @@ void parse_pathspec(struct pathspec *pathspec, } } +void parse_pathspec_file(struct pathspec *pathspec, unsigned magic_mask, + unsigned flags, const char *prefix, + const char *file, int nul_term_line) +{ + struct argv_array parsed_file = ARGV_ARRAY_INIT; + strbuf_getline_fn getline_fn = nul_term_line ? strbuf_getline_nul : + strbuf_getline; + struct strbuf buf = STRBUF_INIT; + struct strbuf unquoted = STRBUF_INIT; + FILE *in = NULL; + + if (!strcmp(file, "-")) + in = stdin; + else + in = xfopen(file, "r"); + + while (getline_fn(&buf, in) != EOF) { + if (!nul_term_line && buf.buf[0] == '"') { + strbuf_reset(&unquoted); + if (unquote_c_style(&unquoted, buf.buf, NULL)) + die(_("line is badly quoted: %s"), buf.buf); + strbuf_swap(&buf, &unquoted); + } + argv_array_push(&parsed_file, buf.buf); + strbuf_reset(&buf); + } + + strbuf_release(&unquoted); + strbuf_release(&buf); + if (in != stdin) + fclose(in); + + parse_pathspec(pathspec, magic_mask, flags, prefix, parsed_file.argv); + argv_array_clear(&parsed_file); +} + void copy_pathspec(struct pathspec *dst, const struct pathspec *src) { int i, j; diff --git a/pathspec.h b/pathspec.h index 1c18a2c90c..a27dc81ba2 100644 --- a/pathspec.h +++ b/pathspec.h @@ -85,6 +85,16 @@ void parse_pathspec(struct pathspec *pathspec, unsigned flags, const char *prefix, const char **args); +/* + * Same as parse_pathspec() but uses file as input. + * When 'file' is exactly "-" it uses 'stdin' instead. + */ +void parse_pathspec_file(struct pathspec *pathspec, + unsigned magic_mask, + unsigned flags, + const char *prefix, + const char *file, + int nul_term_line); void copy_pathspec(struct pathspec *dst, const struct pathspec *src); void clear_pathspec(struct pathspec *); -- gitgitgadget ^ permalink raw reply related [flat|nested] 80+ messages in thread
* Re: [PATCH v2 2/6] pathspec: add new function to parse file 2019-11-06 15:51 ` [PATCH v2 2/6] pathspec: add new function to parse file Alexandr Miloslavskiy via GitGitGadget @ 2019-11-19 5:59 ` Junio C Hamano 2019-11-19 16:50 ` Alexandr Miloslavskiy 0 siblings, 1 reply; 80+ messages in thread From: Junio C Hamano @ 2019-11-19 5:59 UTC (permalink / raw) To: Alexandr Miloslavskiy via GitGitGadget; +Cc: git, Alexandr Miloslavskiy "Alexandr Miloslavskiy via GitGitGadget" <gitgitgadget@gmail.com> writes: > +void parse_pathspec_file(struct pathspec *pathspec, unsigned magic_mask, > + unsigned flags, const char *prefix, > + const char *file, int nul_term_line) > +{ > + struct argv_array parsed_file = ARGV_ARRAY_INIT; > + strbuf_getline_fn getline_fn = nul_term_line ? strbuf_getline_nul : > + strbuf_getline; > + struct strbuf buf = STRBUF_INIT; > + struct strbuf unquoted = STRBUF_INIT; > + FILE *in = NULL; Useless initialization to NULL can be dropped here. > + if (!strcmp(file, "-")) > + in = stdin; > + else > + in = xfopen(file, "r"); ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH v2 2/6] pathspec: add new function to parse file 2019-11-19 5:59 ` Junio C Hamano @ 2019-11-19 16:50 ` Alexandr Miloslavskiy 0 siblings, 0 replies; 80+ messages in thread From: Alexandr Miloslavskiy @ 2019-11-19 16:50 UTC (permalink / raw) To: Junio C Hamano, Alexandr Miloslavskiy via GitGitGadget; +Cc: git On 19.11.2019 6:59, Junio C Hamano wrote: >> + FILE *in = NULL; > > Useless initialization to NULL can be dropped here. Changed in V3. ^ permalink raw reply [flat|nested] 80+ messages in thread
* [PATCH v2 3/6] doc: reset: unify <pathspec> description 2019-11-06 15:51 ` [PATCH v2 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget 2019-11-06 15:51 ` [PATCH v2 1/6] parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul` Alexandr Miloslavskiy via GitGitGadget 2019-11-06 15:51 ` [PATCH v2 2/6] pathspec: add new function to parse file Alexandr Miloslavskiy via GitGitGadget @ 2019-11-06 15:51 ` Alexandr Miloslavskiy via GitGitGadget 2019-11-19 6:05 ` Junio C Hamano 2019-11-06 15:51 ` [PATCH v2 4/6] reset: support the `--pathspec-from-file` option Alexandr Miloslavskiy via GitGitGadget ` (3 subsequent siblings) 6 siblings, 1 reply; 80+ messages in thread From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-06 15:51 UTC (permalink / raw) To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> Synchronize it to `git add`, which has a pretty good description. Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> --- Documentation/git-reset.txt | 20 +++++++++++--------- builtin/reset.c | 4 ++-- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt index 97e0544d9e..f2ccb1426c 100644 --- a/Documentation/git-reset.txt +++ b/Documentation/git-reset.txt @@ -8,8 +8,8 @@ git-reset - Reset current HEAD to the specified state SYNOPSIS -------- [verse] -'git reset' [-q] [<tree-ish>] [--] <paths>... -'git reset' (--patch | -p) [<tree-ish>] [--] [<paths>...] +'git reset' [-q] [<tree-ish>] [--] <pathspec>... +'git reset' (--patch | -p) [<tree-ish>] [--] [<pathspec>...] 'git reset' [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>] DESCRIPTION @@ -18,24 +18,26 @@ In the first and second form, copy entries from `<tree-ish>` to the index. In the third form, set the current branch head (`HEAD`) to `<commit>`, optionally modifying index and working tree to match. The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms. +The <pathspec> is used to limit the paths affected by the operation +(see the entry for 'pathspec' in linkgit:gitglossary[7] for more details). -'git reset' [-q] [<tree-ish>] [--] <paths>...:: - This form resets the index entries for all `<paths>` to their +'git reset' [-q] [<tree-ish>] [--] <pathspec>...:: + This form resets the index entries for all `<pathspec>` to their state at `<tree-ish>`. (It does not affect the working tree or the current branch.) + -This means that `git reset <paths>` is the opposite of `git add -<paths>`. This command is equivalent to -`git restore [--source=<tree-ish>] --staged <paths>...`. +This means that `git reset <pathspec>` is the opposite of `git add +<pathspec>`. This command is equivalent to +`git restore [--source=<tree-ish>] --staged <pathspec>...`. + -After running `git reset <paths>` to update the index entry, you can +After running `git reset <pathspec>` to update the index entry, you can use linkgit:git-restore[1] to check the contents out of the index to the working tree. Alternatively, using linkgit:git-restore[1] and specifying a commit with `--source`, you can copy the contents of a path out of a commit to the index and to the working tree in one go. -'git reset' (--patch | -p) [<tree-ish>] [--] [<paths>...]:: +'git reset' (--patch | -p) [<tree-ish>] [--] [<pathspec>...]:: Interactively select hunks in the difference between the index and `<tree-ish>` (defaults to `HEAD`). The chosen hunks are applied in reverse to the index. diff --git a/builtin/reset.c b/builtin/reset.c index fdd572168b..9291c0fd72 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -30,8 +30,8 @@ static const char * const git_reset_usage[] = { N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"), - N_("git reset [-q] [<tree-ish>] [--] <paths>..."), - N_("git reset --patch [<tree-ish>] [--] [<paths>...]"), + N_("git reset [-q] [<tree-ish>] [--] <pathspec>..."), + N_("git reset --patch [<tree-ish>] [--] [<pathspec>...]"), NULL }; -- gitgitgadget ^ permalink raw reply related [flat|nested] 80+ messages in thread
* Re: [PATCH v2 3/6] doc: reset: unify <pathspec> description 2019-11-06 15:51 ` [PATCH v2 3/6] doc: reset: unify <pathspec> description Alexandr Miloslavskiy via GitGitGadget @ 2019-11-19 6:05 ` Junio C Hamano 2019-11-19 16:52 ` Alexandr Miloslavskiy 0 siblings, 1 reply; 80+ messages in thread From: Junio C Hamano @ 2019-11-19 6:05 UTC (permalink / raw) To: Alexandr Miloslavskiy via GitGitGadget; +Cc: git, Alexandr Miloslavskiy "Alexandr Miloslavskiy via GitGitGadget" <gitgitgadget@gmail.com> writes: > -'git reset' [-q] [<tree-ish>] [--] <paths>... > -'git reset' (--patch | -p) [<tree-ish>] [--] [<paths>...] > +'git reset' [-q] [<tree-ish>] [--] <pathspec>... > +'git reset' (--patch | -p) [<tree-ish>] [--] [<pathspec>...] Good. > @@ -18,24 +18,26 @@ In the first and second form, copy entries from `<tree-ish>` to the index. > In the third form, set the current branch head (`HEAD`) to `<commit>`, > optionally modifying index and working tree to match. > The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms. > +The <pathspec> is used to limit the paths affected by the operation > +(see the entry for 'pathspec' in linkgit:gitglossary[7] for more details). Good. > -'git reset' [-q] [<tree-ish>] [--] <paths>...:: > - This form resets the index entries for all `<paths>` to their > +'git reset' [-q] [<tree-ish>] [--] <pathspec>...:: > + This form resets the index entries for all `<pathspec>` to their This is not so good. The original pretended as if <paths> are the exact pathnames, so it was sort-of OK for it to say "for all paths". Since we are highlighting the fact that these are not pathnames but the patterns to match pathnames, however, the description needs a slight update to match, perhaps like ths form resets the index entries for all paths that match the `<pathspec>` to their... > state at `<tree-ish>`. (It does not affect the working tree or > the current branch.) Thanks. ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH v2 3/6] doc: reset: unify <pathspec> description 2019-11-19 6:05 ` Junio C Hamano @ 2019-11-19 16:52 ` Alexandr Miloslavskiy 0 siblings, 0 replies; 80+ messages in thread From: Alexandr Miloslavskiy @ 2019-11-19 16:52 UTC (permalink / raw) To: Junio C Hamano, Alexandr Miloslavskiy via GitGitGadget; +Cc: git On 19.11.2019 7:05, Junio C Hamano wrote: >> -'git reset' [-q] [<tree-ish>] [--] <paths>...:: >> - This form resets the index entries for all `<paths>` to their >> +'git reset' [-q] [<tree-ish>] [--] <pathspec>...:: >> + This form resets the index entries for all `<pathspec>` to their > > This is not so good. The original pretended as if <paths> are the > exact pathnames, so it was sort-of OK for it to say "for all paths". > Since we are highlighting the fact that these are not pathnames but > the patterns to match pathnames, however, the description needs a > slight update to match, perhaps like > > ths form resets the index entries for all paths that match > the `<pathspec>` to their... > >> state at `<tree-ish>`. (It does not affect the working tree or >> the current branch.) Yes, I wasn't paying enough attention. Changed in V3, thanks! ^ permalink raw reply [flat|nested] 80+ messages in thread
* [PATCH v2 4/6] reset: support the `--pathspec-from-file` option 2019-11-06 15:51 ` [PATCH v2 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget ` (2 preceding siblings ...) 2019-11-06 15:51 ` [PATCH v2 3/6] doc: reset: unify <pathspec> description Alexandr Miloslavskiy via GitGitGadget @ 2019-11-06 15:51 ` Alexandr Miloslavskiy via GitGitGadget 2019-11-06 15:51 ` [PATCH v2 5/6] doc: commit: unify <pathspec> description Alexandr Miloslavskiy via GitGitGadget ` (2 subsequent siblings) 6 siblings, 0 replies; 80+ messages in thread From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-06 15:51 UTC (permalink / raw) To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> Decisions taken for simplicity: 1) For now, `--pathspec-from-file` is declared incompatible with `--patch`, even when <file> is not `stdin`. Such use case it not really expected. Also, it is harder to support in `git commit`, so I decided to make it incompatible in all places. 2) It is not allowed to pass pathspec in both args and file. Co-authored-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> --- Documentation/git-reset.txt | 19 +++- builtin/reset.c | 21 ++++- t/t7107-reset-pathspec-file.sh | 155 +++++++++++++++++++++++++++++++++ 3 files changed, 190 insertions(+), 5 deletions(-) create mode 100755 t/t7107-reset-pathspec-file.sh diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt index f2ccb1426c..d2cd0af6a4 100644 --- a/Documentation/git-reset.txt +++ b/Documentation/git-reset.txt @@ -9,20 +9,22 @@ SYNOPSIS -------- [verse] 'git reset' [-q] [<tree-ish>] [--] <pathspec>... +'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-nul]] [<tree-ish>] 'git reset' (--patch | -p) [<tree-ish>] [--] [<pathspec>...] 'git reset' [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>] DESCRIPTION ----------- -In the first and second form, copy entries from `<tree-ish>` to the index. -In the third form, set the current branch head (`HEAD`) to `<commit>`, +In the first three forms, copy entries from `<tree-ish>` to the index. +In the last form, set the current branch head (`HEAD`) to `<commit>`, optionally modifying index and working tree to match. The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms. The <pathspec> is used to limit the paths affected by the operation (see the entry for 'pathspec' in linkgit:gitglossary[7] for more details). 'git reset' [-q] [<tree-ish>] [--] <pathspec>...:: - This form resets the index entries for all `<pathspec>` to their +'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-nul]] [<tree-ish>]:: + These forms reset the index entries matching pathspec to their state at `<tree-ish>`. (It does not affect the working tree or the current branch.) + @@ -103,6 +105,17 @@ OPTIONS `reset.quiet` config option. `--quiet` and `--no-quiet` will override the default behavior. +--pathspec-from-file=<file>:: + Pathspec is passed in `<file>` instead of commandline args. If + `<file>` is exactly `-` then standard input is used. Pathspec + elements are separated by LF or CR/LF. Pathspec elements can be + quoted as explained for the configuration variable `core.quotePath` + (see linkgit:git-config[1]). See also `--pathspec-file-nul` and + global `--literal-pathspecs`. + +--pathspec-file-nul:: + Only meaningful with `--pathspec-from-file`. Pathspec elements are + separated with NUL character and are not expected to be quoted. EXAMPLES -------- diff --git a/builtin/reset.c b/builtin/reset.c index 9291c0fd72..246bf9d737 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -31,6 +31,7 @@ static const char * const git_reset_usage[] = { N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"), N_("git reset [-q] [<tree-ish>] [--] <pathspec>..."), + N_("git reset [-q] [--pathspec-from-file [--pathspec-file-nul]] [<tree-ish>]"), N_("git reset --patch [<tree-ish>] [--] [<pathspec>...]"), NULL }; @@ -284,8 +285,8 @@ static int git_reset_config(const char *var, const char *value, void *cb) int cmd_reset(int argc, const char **argv, const char *prefix) { int reset_type = NONE, update_ref_status = 0, quiet = 0; - int patch_mode = 0, unborn; - const char *rev; + int patch_mode = 0, pathspec_file_nul = 0, unborn; + const char *rev, *pathspec_from_file = NULL; struct object_id oid; struct pathspec pathspec; int intent_to_add = 0; @@ -306,6 +307,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix) OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")), OPT_BOOL('N', "intent-to-add", &intent_to_add, N_("record only the fact that removed paths will be added later")), + OPT_PATHSPEC_FROM_FILE(&pathspec_from_file), + OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul), OPT_END() }; @@ -316,6 +319,20 @@ int cmd_reset(int argc, const char **argv, const char *prefix) PARSE_OPT_KEEP_DASHDASH); parse_args(&pathspec, argv, prefix, patch_mode, &rev); + if (pathspec_from_file) { + if (patch_mode) + die(_("--pathspec-from-file is incompatible with --patch")); + + if (pathspec.nr) + die(_("--pathspec-from-file is incompatible with pathspec arguments")); + + parse_pathspec_file(&pathspec, 0, + PATHSPEC_PREFER_FULL, + prefix, pathspec_from_file, pathspec_file_nul); + } else if (pathspec_file_nul) { + die(_("--pathspec-file-nul requires --pathspec-from-file")); + } + unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid); if (unborn) { /* reset on unborn branch: treat as reset to empty tree */ diff --git a/t/t7107-reset-pathspec-file.sh b/t/t7107-reset-pathspec-file.sh new file mode 100755 index 0000000000..987cc77b7d --- /dev/null +++ b/t/t7107-reset-pathspec-file.sh @@ -0,0 +1,155 @@ +#!/bin/sh + +test_description='reset --pathspec-from-file' + +. ./test-lib.sh + +test_tick + +test_expect_success setup ' + echo A >fileA.t && + echo B >fileB.t && + echo C >fileC.t && + echo D >fileD.t && + git add . && + git commit --include . -m "Commit" && + git tag checkpoint +' + +restore_checkpoint () { + git reset --hard checkpoint +} + +verify_expect () { + git status --porcelain -- fileA.t fileB.t fileC.t fileD.t >actual && + test_cmp expect actual +} + +test_expect_success '--pathspec-from-file from stdin' ' + restore_checkpoint && + + git rm fileA.t && + echo fileA.t | git reset --pathspec-from-file=- && + + cat >expect <<-\EOF && + D fileA.t + EOF + verify_expect +' + +test_expect_success '--pathspec-from-file from file' ' + restore_checkpoint && + + git rm fileA.t && + echo fileA.t >list && + git reset --pathspec-from-file=list && + + cat >expect <<-\EOF && + D fileA.t + EOF + verify_expect +' + +test_expect_success 'NUL delimiters' ' + restore_checkpoint && + + git rm fileA.t fileB.t && + printf "fileA.t\0fileB.t\0" | git reset --pathspec-from-file=- --pathspec-file-nul && + + cat >expect <<-\EOF && + D fileA.t + D fileB.t + EOF + verify_expect +' + +test_expect_success 'LF delimiters' ' + restore_checkpoint && + + git rm fileA.t fileB.t && + printf "fileA.t\nfileB.t\n" | git reset --pathspec-from-file=- && + + cat >expect <<-\EOF && + D fileA.t + D fileB.t + EOF + verify_expect +' + +test_expect_success 'no trailing delimiter' ' + restore_checkpoint && + + git rm fileA.t fileB.t && + printf "fileA.t\nfileB.t" | git reset --pathspec-from-file=- && + + cat >expect <<-\EOF && + D fileA.t + D fileB.t + EOF + verify_expect +' + +test_expect_success 'CRLF delimiters' ' + restore_checkpoint && + + git rm fileA.t fileB.t && + printf "fileA.t\r\nfileB.t\r\n" | git reset --pathspec-from-file=- && + + cat >expect <<-\EOF && + D fileA.t + D fileB.t + EOF + verify_expect +' + +test_expect_success 'quotes' ' + restore_checkpoint && + + git rm fileA.t && + printf "\"file\\101.t\"" | git reset --pathspec-from-file=- && + + cat >expect <<-\EOF && + D fileA.t + EOF + verify_expect +' + +test_expect_success 'quotes not compatible with --pathspec-file-nul' ' + restore_checkpoint && + + git rm fileA.t && + printf "\"file\\101.t\"" >list && + # Note: "git reset" has not yet learned to fail on wrong pathspecs + git reset --pathspec-from-file=list --pathspec-file-nul && + + cat >expect <<-\EOF && + D fileA.t + EOF + test_must_fail verify_expect +' + +test_expect_success '--pathspec-from-file is not compatible with --soft or --hard' ' + restore_checkpoint && + + git rm fileA.t && + echo fileA.t >list && + test_must_fail git reset --soft --pathspec-from-file=list && + test_must_fail git reset --hard --pathspec-from-file=list +' + +test_expect_success 'only touches what was listed' ' + restore_checkpoint && + + git rm fileA.t fileB.t fileC.t fileD.t && + printf "fileB.t\nfileC.t\n" | git reset --pathspec-from-file=- && + + cat >expect <<-\EOF && + D fileA.t + D fileB.t + D fileC.t + D fileD.t + EOF + verify_expect +' + +test_done -- gitgitgadget ^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH v2 5/6] doc: commit: unify <pathspec> description 2019-11-06 15:51 ` [PATCH v2 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget ` (3 preceding siblings ...) 2019-11-06 15:51 ` [PATCH v2 4/6] reset: support the `--pathspec-from-file` option Alexandr Miloslavskiy via GitGitGadget @ 2019-11-06 15:51 ` Alexandr Miloslavskiy via GitGitGadget 2019-11-19 6:16 ` Junio C Hamano 2019-11-06 15:51 ` [PATCH v2 6/6] commit: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget 2019-11-19 16:48 ` [PATCH v3 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget 6 siblings, 1 reply; 80+ messages in thread From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-06 15:51 UTC (permalink / raw) To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> Synchronize it to `git add`, which has a pretty good description. This also better disambiguates <file>... header. Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> --- Documentation/git-commit.txt | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt index afa7b75a23..915c212a0d 100644 --- a/Documentation/git-commit.txt +++ b/Documentation/git-commit.txt @@ -13,7 +13,7 @@ SYNOPSIS [-F <file> | -m <msg>] [--reset-author] [--allow-empty] [--allow-empty-message] [--no-verify] [-e] [--author=<author>] [--date=<date>] [--cleanup=<mode>] [--[no-]status] - [-i | -o] [-S[<keyid>]] [--] [<file>...] + [-i | -o] [-S[<keyid>]] [--] [<pathspec>...] DESCRIPTION ----------- @@ -345,12 +345,14 @@ changes to tracked files. \--:: Do not interpret any more arguments as options. -<file>...:: - When files are given on the command line, the command - commits the contents of the named files, without - recording the changes already staged. The contents of - these files are also staged for the next commit on top - of what have been staged before. +<pathspec>...:: + When pathspec is given on the command line, commit the contents of + the files that match the pathspec without recording the changes + already added to the index. The contents of these files are also + staged for the next commit on top of what have been staged before. ++ +For more details about the <pathspec> syntax, see the 'pathspec' entry +in linkgit:gitglossary[7]. :git-commit: 1 include::date-formats.txt[] -- gitgitgadget ^ permalink raw reply related [flat|nested] 80+ messages in thread
* Re: [PATCH v2 5/6] doc: commit: unify <pathspec> description 2019-11-06 15:51 ` [PATCH v2 5/6] doc: commit: unify <pathspec> description Alexandr Miloslavskiy via GitGitGadget @ 2019-11-19 6:16 ` Junio C Hamano 2019-11-19 16:53 ` Alexandr Miloslavskiy 2019-11-19 17:02 ` Alexandr Miloslavskiy 0 siblings, 2 replies; 80+ messages in thread From: Junio C Hamano @ 2019-11-19 6:16 UTC (permalink / raw) To: Alexandr Miloslavskiy via GitGitGadget; +Cc: git, Alexandr Miloslavskiy "Alexandr Miloslavskiy via GitGitGadget" <gitgitgadget@gmail.com> writes: > +<pathspec>...:: > + When pathspec is given on the command line, commit the contents of > + the files that match the pathspec without recording the changes > + already added to the index. The contents of these files are also > + staged for the next commit on top of what have been staged before. > ++ > +For more details about the <pathspec> syntax, see the 'pathspec' entry > +in linkgit:gitglossary[7]. What you added in [PATCH 3/6] (git reset doc) sends a slightly different message, i.e. The <pathspec> is used to limit the paths affected by the operation (see the entry for 'pathspec' in linkgit:gitglossary[7] for more details). and I think that was more appropriate than what we see here. You are referring your readers to the glossary entry not just for the syntax but also the entire concept of <pathspec>. IOW, it would be needed to drop "syntax" from "about the <pathspec> syntax" from here to match the update to "git reset" documentation. Thanks. ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH v2 5/6] doc: commit: unify <pathspec> description 2019-11-19 6:16 ` Junio C Hamano @ 2019-11-19 16:53 ` Alexandr Miloslavskiy 2019-11-19 17:02 ` Alexandr Miloslavskiy 1 sibling, 0 replies; 80+ messages in thread From: Alexandr Miloslavskiy @ 2019-11-19 16:53 UTC (permalink / raw) To: Junio C Hamano, Alexandr Miloslavskiy via GitGitGadget; +Cc: git On 19.11.2019 7:16, Junio C Hamano wrote: >> +For more details about the <pathspec> syntax, see the 'pathspec' entry >> +in linkgit:gitglossary[7]. > > What you added in [PATCH 3/6] (git reset doc) sends a slightly > different message, i.e. > > The <pathspec> is used to limit the paths affected by the operation > (see the entry for 'pathspec' in linkgit:gitglossary[7] for more details). > > and I think that was more appropriate than what we see here. You > are referring your readers to the glossary entry not just for the > syntax but also the entire concept of <pathspec>. > > IOW, it would be needed to drop "syntax" from "about the <pathspec> > syntax" from here to match the update to "git reset" documentation. I shortened it even more in V3, I think it's better now? ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH v2 5/6] doc: commit: unify <pathspec> description 2019-11-19 6:16 ` Junio C Hamano 2019-11-19 16:53 ` Alexandr Miloslavskiy @ 2019-11-19 17:02 ` Alexandr Miloslavskiy 1 sibling, 0 replies; 80+ messages in thread From: Alexandr Miloslavskiy @ 2019-11-19 17:02 UTC (permalink / raw) To: Junio C Hamano, Alexandr Miloslavskiy via GitGitGadget; +Cc: git On 19.11.2019 7:16, Junio C Hamano wrote: >> +For more details about the <pathspec> syntax, see the 'pathspec' entry >> +in linkgit:gitglossary[7]. > > What you added in [PATCH 3/6] (git reset doc) sends a slightly > different message, i.e. > > The <pathspec> is used to limit the paths affected by the operation > (see the entry for 'pathspec' in linkgit:gitglossary[7] for more details). > > and I think that was more appropriate than what we see here. You > are referring your readers to the glossary entry not just for the > syntax but also the entire concept of <pathspec>. This has shown me that I didn't synchronize docs enough. I have studied docs for other commands and found out that most of them list <pathspec> in a separate paragraph under options. I find it very reasonable, because that's where I would normally expect it as a reader, together with other options. So I adjusted 'git-reset' docs to also list <pathspec> under options. ^ permalink raw reply [flat|nested] 80+ messages in thread
* [PATCH v2 6/6] commit: support the --pathspec-from-file option 2019-11-06 15:51 ` [PATCH v2 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget ` (4 preceding siblings ...) 2019-11-06 15:51 ` [PATCH v2 5/6] doc: commit: unify <pathspec> description Alexandr Miloslavskiy via GitGitGadget @ 2019-11-06 15:51 ` Alexandr Miloslavskiy via GitGitGadget 2019-11-19 6:10 ` Junio C Hamano 2019-11-19 16:48 ` [PATCH v3 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget 6 siblings, 1 reply; 80+ messages in thread From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-06 15:51 UTC (permalink / raw) To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> Decisions taken for simplicity: 1) For now, `--pathspec-from-file` is declared incompatible with `--interactive/--patch`, even when <file> is not `stdin`. Such use case it not really expected. Also, it would require changes to `interactive_add()`. 2) It is not allowed to pass pathspec in both args and file. Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> --- Documentation/git-commit.txt | 15 +++- builtin/commit.c | 25 +++++- t/t7526-commit-pathspec-file.sh | 130 ++++++++++++++++++++++++++++++++ 3 files changed, 165 insertions(+), 5 deletions(-) create mode 100755 t/t7526-commit-pathspec-file.sh diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt index 915c212a0d..bbd53959bd 100644 --- a/Documentation/git-commit.txt +++ b/Documentation/git-commit.txt @@ -13,7 +13,8 @@ SYNOPSIS [-F <file> | -m <msg>] [--reset-author] [--allow-empty] [--allow-empty-message] [--no-verify] [-e] [--author=<author>] [--date=<date>] [--cleanup=<mode>] [--[no-]status] - [-i | -o] [-S[<keyid>]] [--] [<pathspec>...] + [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]] + [-S[<keyid>]] [--] [<pathspec>...] DESCRIPTION ----------- @@ -277,6 +278,18 @@ FROM UPSTREAM REBASE" section in linkgit:git-rebase[1].) the last commit without committing changes that have already been staged. If used together with `--allow-empty` paths are also not required, and an empty commit will be created. + +--pathspec-from-file=<file>:: + Pathspec is passed in `<file>` instead of commandline args. If + `<file>` is exactly `-` then standard input is used. Pathspec + elements are separated by LF or CR/LF. Pathspec elements can be + quoted as explained for the configuration variable `core.quotePath` + (see linkgit:git-config[1]). See also `--pathspec-file-nul` and + global `--literal-pathspecs`. + +--pathspec-file-nul:: + Only meaningful with `--pathspec-from-file`. Pathspec elements are + separated with NUL character and are not expected to be quoted. -u[<mode>]:: --untracked-files[=<mode>]:: diff --git a/builtin/commit.c b/builtin/commit.c index e588bc6ad3..ed40729355 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -107,9 +107,9 @@ static int all, also, interactive, patch_interactive, only, amend, signoff; static int edit_flag = -1; /* unspecified */ static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship; static int config_commit_verbose = -1; /* unspecified */ -static int no_post_rewrite, allow_empty_message; +static int no_post_rewrite, allow_empty_message, pathspec_file_nul; static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg; -static char *sign_commit; +static char *sign_commit, *pathspec_from_file; /* * The default commit message cleanup mode will remove the lines @@ -343,6 +343,23 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix PATHSPEC_PREFER_FULL, prefix, argv); + if (pathspec_from_file) { + if (interactive) + die(_("--pathspec-from-file is incompatible with --interactive/--patch")); + + if (pathspec.nr) + die(_("--pathspec-from-file is incompatible with pathspec arguments")); + + parse_pathspec_file(&pathspec, 0, + PATHSPEC_PREFER_FULL, + prefix, pathspec_from_file, pathspec_file_nul); + } else if (pathspec_file_nul) { + die(_("--pathspec-file-nul requires --pathspec-from-file")); + } + + if (!pathspec.nr && (also || (only && !amend && !allow_empty))) + die(_("No paths with --include/--only does not make sense.")); + if (read_cache_preload(&pathspec) < 0) die(_("index file corrupt")); @@ -1198,8 +1215,6 @@ static int parse_and_validate_options(int argc, const char *argv[], if (also + only + all + interactive > 1) die(_("Only one of --include/--only/--all/--interactive/--patch can be used.")); - if (argc == 0 && (also || (only && !amend && !allow_empty))) - die(_("No paths with --include/--only does not make sense.")); cleanup_mode = get_cleanup_mode(cleanup_arg, use_editor); handle_untracked_files_arg(s); @@ -1535,6 +1550,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "amend", &amend, N_("amend previous commit")), OPT_BOOL(0, "no-post-rewrite", &no_post_rewrite, N_("bypass post-rewrite hook")), { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, N_("mode"), N_("show untracked files, optional modes: all, normal, no. (Default: all)"), PARSE_OPT_OPTARG, NULL, (intptr_t)"all" }, + OPT_PATHSPEC_FROM_FILE(&pathspec_from_file), + OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul), /* end commit contents options */ OPT_HIDDEN_BOOL(0, "allow-empty", &allow_empty, diff --git a/t/t7526-commit-pathspec-file.sh b/t/t7526-commit-pathspec-file.sh new file mode 100755 index 0000000000..4fdf206dd0 --- /dev/null +++ b/t/t7526-commit-pathspec-file.sh @@ -0,0 +1,130 @@ +#!/bin/sh + +test_description='commit --pathspec-from-file' + +. ./test-lib.sh + +test_tick + +test_expect_success setup ' + test_commit file0 && + git tag checkpoint && + + echo A >fileA.t && + echo B >fileB.t && + echo C >fileC.t && + echo D >fileD.t && + git add fileA.t fileB.t fileC.t fileD.t +' + +restore_checkpoint () { + git reset --soft checkpoint +} + +verify_expect () { + git diff-tree --no-commit-id --name-status -r HEAD >actual && + test_cmp expect actual +} + +test_expect_success '--pathspec-from-file from stdin' ' + restore_checkpoint && + + echo fileA.t | git commit --pathspec-from-file=- -m "Commit" && + + cat >expect <<-\EOF && + A fileA.t + EOF + verify_expect +' + +test_expect_success '--pathspec-from-file from file' ' + restore_checkpoint && + + echo fileA.t >list && + git commit --pathspec-from-file=list -m "Commit" && + + cat >expect <<-\EOF && + A fileA.t + EOF + verify_expect +' + +test_expect_success 'NUL delimiters' ' + restore_checkpoint && + + printf "fileA.t\0fileB.t\0" | git commit --pathspec-from-file=- --pathspec-file-nul -m "Commit" && + + cat >expect <<-\EOF && + A fileA.t + A fileB.t + EOF + verify_expect +' + +test_expect_success 'LF delimiters' ' + restore_checkpoint && + + printf "fileA.t\nfileB.t\n" | git commit --pathspec-from-file=- -m "Commit" && + + cat >expect <<-\EOF && + A fileA.t + A fileB.t + EOF + verify_expect +' + +test_expect_success 'no trailing delimiter' ' + restore_checkpoint && + + printf "fileA.t\nfileB.t" | git commit --pathspec-from-file=- -m "Commit" && + + cat >expect <<-\EOF && + A fileA.t + A fileB.t + EOF + verify_expect +' + +test_expect_success 'CRLF delimiters' ' + restore_checkpoint && + + printf "fileA.t\r\nfileB.t\r\n" | git commit --pathspec-from-file=- -m "Commit" && + + cat >expect <<-\EOF && + A fileA.t + A fileB.t + EOF + verify_expect +' + +test_expect_success 'quotes' ' + restore_checkpoint && + + printf "\"file\\101.t\"" | git commit --pathspec-from-file=- -m "Commit" && + + cat >expect <<-\EOF && + A fileA.t + EOF + verify_expect expect +' + +test_expect_success 'quotes not compatible with --pathspec-file-nul' ' + restore_checkpoint && + + printf "\"file\\101.t\"" >list && + test_must_fail git commit --pathspec-from-file=list --pathspec-file-nul -m "Commit" +' + +test_expect_success 'only touches what was listed' ' + restore_checkpoint && + + printf "fileB.t\nfileC.t\n" | git commit --pathspec-from-file=- -m "Commit" && + + cat >expect <<-\EOF && + A fileB.t + A fileC.t + EOF + verify_expect +' + +test_done -- gitgitgadget ^ permalink raw reply related [flat|nested] 80+ messages in thread
* Re: [PATCH v2 6/6] commit: support the --pathspec-from-file option 2019-11-06 15:51 ` [PATCH v2 6/6] commit: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget @ 2019-11-19 6:10 ` Junio C Hamano 2019-11-19 16:56 ` Alexandr Miloslavskiy 0 siblings, 1 reply; 80+ messages in thread From: Junio C Hamano @ 2019-11-19 6:10 UTC (permalink / raw) To: Alexandr Miloslavskiy via GitGitGadget; +Cc: git, Alexandr Miloslavskiy "Alexandr Miloslavskiy via GitGitGadget" <gitgitgadget@gmail.com> writes: > @@ -277,6 +278,18 @@ FROM UPSTREAM REBASE" section in linkgit:git-rebase[1].) > the last commit without committing changes that have > already been staged. If used together with `--allow-empty` > paths are also not required, and an empty commit will be created. > + > +--pathspec-from-file=<file>:: > + Pathspec is passed in `<file>` instead of commandline args. If > + `<file>` is exactly `-` then standard input is used. Pathspec > + elements are separated by LF or CR/LF. Pathspec elements can be > + quoted as explained for the configuration variable `core.quotePath` > + (see linkgit:git-config[1]). See also `--pathspec-file-nul` and > + global `--literal-pathspecs`. OK. > +--pathspec-file-nul:: > + Only meaningful with `--pathspec-from-file`. Pathspec elements are > + separated with NUL character and are not expected to be quoted. Although it is not incorrect, "are not expected to be quoted" feels a bit weak as the technical description. Are they not expected to be quoted, but the command gracefully works on them even when they are found to be quoted? Rephrasing to avoid such misinterpretation may be worth doing, perhaps ... and are taken as-is without unquoting (i.e. as if `core.quotePath` is set to `false`). or something like that? Thanks. ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH v2 6/6] commit: support the --pathspec-from-file option 2019-11-19 6:10 ` Junio C Hamano @ 2019-11-19 16:56 ` Alexandr Miloslavskiy 0 siblings, 0 replies; 80+ messages in thread From: Alexandr Miloslavskiy @ 2019-11-19 16:56 UTC (permalink / raw) To: Junio C Hamano, Alexandr Miloslavskiy via GitGitGadget; +Cc: git On 19.11.2019 7:10, Junio C Hamano wrote: >> +--pathspec-file-nul:: >> + Only meaningful with `--pathspec-from-file`. Pathspec elements are >> + separated with NUL character and are not expected to be quoted. > > Although it is not incorrect, "are not expected to be quoted" feels > a bit weak as the technical description. Are they not expected to > be quoted, but the command gracefully works on them even when they > are found to be quoted? > > Rephrasing to avoid such misinterpretation may be worth doing, > perhaps > > ... and are taken as-is without unquoting (i.e. as if > `core.quotePath` is set to `false`). > > or something like that? I think that a reference to `core.quotePath` could be confusing here, because it doesn't really affect the option. I have reworded it in V3, please see if you like it. Thanks for pointing out! ^ permalink raw reply [flat|nested] 80+ messages in thread
* [PATCH v3 0/6] Add --pathspec-from-file option for reset, commit 2019-11-06 15:51 ` [PATCH v2 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget ` (5 preceding siblings ...) 2019-11-06 15:51 ` [PATCH v2 6/6] commit: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget @ 2019-11-19 16:48 ` Alexandr Miloslavskiy via GitGitGadget 2019-11-19 16:48 ` [PATCH v3 1/6] parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul` Alexandr Miloslavskiy via GitGitGadget ` (7 more replies) 6 siblings, 8 replies; 80+ messages in thread From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-19 16:48 UTC (permalink / raw) To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano Changes from V2: 1) Some polishing in documentation 2) = NULL removed from parse_pathspec_file() Alexandr Miloslavskiy (6): parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul` pathspec: add new function to parse file doc: reset: synchronize <pathspec> description reset: support the `--pathspec-from-file` option doc: commit: synchronize <pathspec> description commit: support the --pathspec-from-file option Documentation/git-commit.txt | 29 ++++-- Documentation/git-reset.txt | 48 +++++++--- builtin/commit.c | 25 +++++- builtin/reset.c | 25 +++++- parse-options.h | 2 + pathspec.c | 38 ++++++++ pathspec.h | 10 +++ t/t7107-reset-pathspec-file.sh | 155 ++++++++++++++++++++++++++++++++ t/t7526-commit-pathspec-file.sh | 130 +++++++++++++++++++++++++++ 9 files changed, 434 insertions(+), 28 deletions(-) create mode 100755 t/t7107-reset-pathspec-file.sh create mode 100755 t/t7526-commit-pathspec-file.sh base-commit: da72936f544fec5a335e66432610e4cef4430991 Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-445%2FSyntevoAlex%2F%230207_pathspec_from_file-v3 Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-445/SyntevoAlex/#0207_pathspec_from_file-v3 Pull-Request: https://github.com/gitgitgadget/git/pull/445 Range-diff vs v2: 1: 2dfaccf0d5 = 1: 19b80326ea parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul` 2: 96697ba072 ! 2: 55a7c6ec3c pathspec: add new function to parse file @@ -45,7 +45,7 @@ + strbuf_getline; + struct strbuf buf = STRBUF_INIT; + struct strbuf unquoted = STRBUF_INIT; -+ FILE *in = NULL; ++ FILE *in; + + if (!strcmp(file, "-")) + in = stdin; 3: f961a5155a ! 3: d9f32e523c doc: reset: unify <pathspec> description @@ -1,8 +1,8 @@ Author: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> - doc: reset: unify <pathspec> description + doc: reset: synchronize <pathspec> description - Synchronize it to `git add`, which has a pretty good description. + `git add` shows an example of good writing, follow it. Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> @@ -21,18 +21,17 @@ DESCRIPTION @@ - In the third form, set the current branch head (`HEAD`) to `<commit>`, optionally modifying index and working tree to match. The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms. -+The <pathspec> is used to limit the paths affected by the operation -+(see the entry for 'pathspec' in linkgit:gitglossary[7] for more details). -'git reset' [-q] [<tree-ish>] [--] <paths>...:: - This form resets the index entries for all `<paths>` to their +- state at `<tree-ish>`. (It does not affect the working tree or +- the current branch.) +'git reset' [-q] [<tree-ish>] [--] <pathspec>...:: -+ This form resets the index entries for all `<pathspec>` to their - state at `<tree-ish>`. (It does not affect the working tree or - the current branch.) ++ This form resets the index entries for all paths that match the ++ `<pathspec>` to their state at `<tree-ish>`. (It does not affect ++ the working tree or the current branch.) + -This means that `git reset <paths>` is the opposite of `git add -<paths>`. This command is equivalent to @@ -54,6 +53,20 @@ Interactively select hunks in the difference between the index and `<tree-ish>` (defaults to `HEAD`). The chosen hunks are applied in reverse to the index. +@@ + `reset.quiet` config option. `--quiet` and `--no-quiet` will + override the default behavior. + ++\--:: ++ Do not interpret any more arguments as options. ++ ++<pathspec>...:: ++ Limits the paths affected by the operation. +++ ++For more details, see the 'pathspec' entry in linkgit:gitglossary[7]. + + EXAMPLES + -------- diff --git a/builtin/reset.c b/builtin/reset.c --- a/builtin/reset.c 4: d72d4f16b5 ! 4: 8a10ff881b reset: support the `--pathspec-from-file` option @@ -31,15 +31,13 @@ +In the last form, set the current branch head (`HEAD`) to `<commit>`, optionally modifying index and working tree to match. The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms. - The <pathspec> is used to limit the paths affected by the operation - (see the entry for 'pathspec' in linkgit:gitglossary[7] for more details). 'git reset' [-q] [<tree-ish>] [--] <pathspec>...:: -- This form resets the index entries for all `<pathspec>` to their +- This form resets the index entries for all paths that match the +'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-nul]] [<tree-ish>]:: -+ These forms reset the index entries matching pathspec to their - state at `<tree-ish>`. (It does not affect the working tree or - the current branch.) ++ These forms reset the index entries for all paths that match the + `<pathspec>` to their state at `<tree-ish>`. (It does not affect + the working tree or the current branch.) + @@ `reset.quiet` config option. `--quiet` and `--no-quiet` will @@ -55,10 +53,12 @@ + +--pathspec-file-nul:: + Only meaningful with `--pathspec-from-file`. Pathspec elements are -+ separated with NUL character and are not expected to be quoted. ++ separated with NUL character and all other characters are taken ++ literally (including newlines and quotes). ++ + \--:: + Do not interpret any more arguments as options. - EXAMPLES - -------- diff --git a/builtin/reset.c b/builtin/reset.c --- a/builtin/reset.c @@ -241,7 +241,7 @@ + printf "\"file\\101.t\"" >list && + # Note: "git reset" has not yet learned to fail on wrong pathspecs + git reset --pathspec-from-file=list --pathspec-file-nul && -+ ++ + cat >expect <<-\EOF && + D fileA.t + EOF 5: 20c4495fd3 ! 5: 0b79797e77 doc: commit: unify <pathspec> description @@ -1,8 +1,8 @@ Author: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> - doc: commit: unify <pathspec> description + doc: commit: synchronize <pathspec> description - Synchronize it to `git add`, which has a pretty good description. + `git add` shows an example of good writing, follow it. This also better disambiguates <file>... header. Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> @@ -35,8 +35,7 @@ + already added to the index. The contents of these files are also + staged for the next commit on top of what have been staged before. ++ -+For more details about the <pathspec> syntax, see the 'pathspec' entry -+in linkgit:gitglossary[7]. ++For more details, see the 'pathspec' entry in linkgit:gitglossary[7]. :git-commit: 1 include::date-formats.txt[] 6: cb5fc9b14d ! 6: 7e48212002 commit: support the --pathspec-from-file option @@ -25,10 +25,9 @@ DESCRIPTION ----------- @@ - the last commit without committing changes that have already been staged. If used together with `--allow-empty` paths are also not required, and an empty commit will be created. -+ + +--pathspec-from-file=<file>:: + Pathspec is passed in `<file>` instead of commandline args. If + `<file>` is exactly `-` then standard input is used. Pathspec @@ -39,10 +38,12 @@ + +--pathspec-file-nul:: + Only meaningful with `--pathspec-from-file`. Pathspec elements are -+ separated with NUL character and are not expected to be quoted. - ++ separated with NUL character and all other characters are taken ++ literally (including newlines and quotes). ++ -u[<mode>]:: --untracked-files[=<mode>]:: + Show untracked files. diff --git a/builtin/commit.c b/builtin/commit.c --- a/builtin/commit.c @@ -118,7 +119,7 @@ +test_expect_success setup ' + test_commit file0 && + git tag checkpoint && -+ ++ + echo A >fileA.t && + echo B >fileB.t && + echo C >fileC.t && -- gitgitgadget ^ permalink raw reply [flat|nested] 80+ messages in thread
* [PATCH v3 1/6] parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul` 2019-11-19 16:48 ` [PATCH v3 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget @ 2019-11-19 16:48 ` Alexandr Miloslavskiy via GitGitGadget 2019-11-19 16:48 ` [PATCH v3 2/6] pathspec: add new function to parse file Alexandr Miloslavskiy via GitGitGadget ` (6 subsequent siblings) 7 siblings, 0 replies; 80+ messages in thread From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-19 16:48 UTC (permalink / raw) To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> Support for various porcelain commands will arrive via additional patches. `--pathspec-from-file` solves the problem of commandline length limit for UIs built on top of git. Plumbing commands are not always a good fit, for two major reasons: 1) Some UIs show executed commands to user. In this case, porcelain commands are expected. One reason for that is letting user learn git commands by clicking UI buttons. The other reason is letting user study the history of commands in case of any unexpected results. Both of these will lose most of their value if UI uses combinations of arcane plumbing commands. 2) Some UIs have started and grown with porcelain commands. Replacing existing logic with plumbing commands could be cumbersome and prone to various new problems. `--pathspec-from-file` will behave very close to pathspec passed in commandline args, so that switching from one to another is simple. `--pathspec-from-file` will read either a specified file or `stdin` (when file is exactly "-"). Reading from file is a good way to avoid competing for `stdin`, and also gives some extra flexibility. `--pathspec-file-nul` switch mirrors `-z` already used in various places. Some porcelain commands, such as `git commit`, already use `-z`, therefore it needed a new unambiguous name. New options do not have shorthands to avoid shorthand conflicts. It is not expected that they will be typed in console. Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> --- parse-options.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/parse-options.h b/parse-options.h index 38a33a087e..c6cc01e715 100644 --- a/parse-options.h +++ b/parse-options.h @@ -330,5 +330,7 @@ int parse_opt_passthru_argv(const struct option *, const char *, int); #define OPT_WITH(v, h) _OPT_CONTAINS_OR_WITH("with", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG) #define OPT_WITHOUT(v, h) _OPT_CONTAINS_OR_WITH("without", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG) #define OPT_CLEANUP(v) OPT_STRING(0, "cleanup", v, N_("mode"), N_("how to strip spaces and #comments from message")) +#define OPT_PATHSPEC_FROM_FILE(v) OPT_FILENAME(0, "pathspec-from-file", v, N_("read pathspec from file")) +#define OPT_PATHSPEC_FILE_NUL(v) OPT_BOOL(0, "pathspec-file-nul", v, N_("with --pathspec-from-file, pathspec elements are separated with NUL character")) #endif -- gitgitgadget ^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH v3 2/6] pathspec: add new function to parse file 2019-11-19 16:48 ` [PATCH v3 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget 2019-11-19 16:48 ` [PATCH v3 1/6] parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul` Alexandr Miloslavskiy via GitGitGadget @ 2019-11-19 16:48 ` Alexandr Miloslavskiy via GitGitGadget 2019-11-19 16:48 ` [PATCH v3 3/6] doc: reset: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget ` (5 subsequent siblings) 7 siblings, 0 replies; 80+ messages in thread From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-19 16:48 UTC (permalink / raw) To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> This will be used to support the new option '--pathspec-from-file' in `git add`, `git-commit`, `git reset` etc. Note also that we specifically handle CR/LF line endings to support Windows better. To simplify code, file is first parsed into `argv_array`. This allows to avoid refactoring `parse_pathspec()`. I considered adding `nul_term_line` to `flags` instead, but decided that it doesn't fit there. The new code is mostly taken from `cmd_update_index()` and `split_mail_conv()`. Co-authored-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> --- pathspec.c | 38 ++++++++++++++++++++++++++++++++++++++ pathspec.h | 10 ++++++++++ 2 files changed, 48 insertions(+) diff --git a/pathspec.c b/pathspec.c index 12c2b322b3..128f27fcb7 100644 --- a/pathspec.c +++ b/pathspec.c @@ -3,6 +3,8 @@ #include "dir.h" #include "pathspec.h" #include "attr.h" +#include "argv-array.h" +#include "quote.h" /* * Finds which of the given pathspecs match items in the index. @@ -613,6 +615,42 @@ void parse_pathspec(struct pathspec *pathspec, } } +void parse_pathspec_file(struct pathspec *pathspec, unsigned magic_mask, + unsigned flags, const char *prefix, + const char *file, int nul_term_line) +{ + struct argv_array parsed_file = ARGV_ARRAY_INIT; + strbuf_getline_fn getline_fn = nul_term_line ? strbuf_getline_nul : + strbuf_getline; + struct strbuf buf = STRBUF_INIT; + struct strbuf unquoted = STRBUF_INIT; + FILE *in; + + if (!strcmp(file, "-")) + in = stdin; + else + in = xfopen(file, "r"); + + while (getline_fn(&buf, in) != EOF) { + if (!nul_term_line && buf.buf[0] == '"') { + strbuf_reset(&unquoted); + if (unquote_c_style(&unquoted, buf.buf, NULL)) + die(_("line is badly quoted: %s"), buf.buf); + strbuf_swap(&buf, &unquoted); + } + argv_array_push(&parsed_file, buf.buf); + strbuf_reset(&buf); + } + + strbuf_release(&unquoted); + strbuf_release(&buf); + if (in != stdin) + fclose(in); + + parse_pathspec(pathspec, magic_mask, flags, prefix, parsed_file.argv); + argv_array_clear(&parsed_file); +} + void copy_pathspec(struct pathspec *dst, const struct pathspec *src) { int i, j; diff --git a/pathspec.h b/pathspec.h index 1c18a2c90c..a27dc81ba2 100644 --- a/pathspec.h +++ b/pathspec.h @@ -85,6 +85,16 @@ void parse_pathspec(struct pathspec *pathspec, unsigned flags, const char *prefix, const char **args); +/* + * Same as parse_pathspec() but uses file as input. + * When 'file' is exactly "-" it uses 'stdin' instead. + */ +void parse_pathspec_file(struct pathspec *pathspec, + unsigned magic_mask, + unsigned flags, + const char *prefix, + const char *file, + int nul_term_line); void copy_pathspec(struct pathspec *dst, const struct pathspec *src); void clear_pathspec(struct pathspec *); -- gitgitgadget ^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH v3 3/6] doc: reset: synchronize <pathspec> description 2019-11-19 16:48 ` [PATCH v3 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget 2019-11-19 16:48 ` [PATCH v3 1/6] parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul` Alexandr Miloslavskiy via GitGitGadget 2019-11-19 16:48 ` [PATCH v3 2/6] pathspec: add new function to parse file Alexandr Miloslavskiy via GitGitGadget @ 2019-11-19 16:48 ` Alexandr Miloslavskiy via GitGitGadget 2019-11-19 16:48 ` [PATCH v3 4/6] reset: support the `--pathspec-from-file` option Alexandr Miloslavskiy via GitGitGadget ` (4 subsequent siblings) 7 siblings, 0 replies; 80+ messages in thread From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-19 16:48 UTC (permalink / raw) To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> `git add` shows an example of good writing, follow it. Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> --- Documentation/git-reset.txt | 29 ++++++++++++++++++----------- builtin/reset.c | 4 ++-- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt index 97e0544d9e..d517a43e73 100644 --- a/Documentation/git-reset.txt +++ b/Documentation/git-reset.txt @@ -8,8 +8,8 @@ git-reset - Reset current HEAD to the specified state SYNOPSIS -------- [verse] -'git reset' [-q] [<tree-ish>] [--] <paths>... -'git reset' (--patch | -p) [<tree-ish>] [--] [<paths>...] +'git reset' [-q] [<tree-ish>] [--] <pathspec>... +'git reset' (--patch | -p) [<tree-ish>] [--] [<pathspec>...] 'git reset' [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>] DESCRIPTION @@ -19,23 +19,23 @@ In the third form, set the current branch head (`HEAD`) to `<commit>`, optionally modifying index and working tree to match. The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms. -'git reset' [-q] [<tree-ish>] [--] <paths>...:: - This form resets the index entries for all `<paths>` to their - state at `<tree-ish>`. (It does not affect the working tree or - the current branch.) +'git reset' [-q] [<tree-ish>] [--] <pathspec>...:: + This form resets the index entries for all paths that match the + `<pathspec>` to their state at `<tree-ish>`. (It does not affect + the working tree or the current branch.) + -This means that `git reset <paths>` is the opposite of `git add -<paths>`. This command is equivalent to -`git restore [--source=<tree-ish>] --staged <paths>...`. +This means that `git reset <pathspec>` is the opposite of `git add +<pathspec>`. This command is equivalent to +`git restore [--source=<tree-ish>] --staged <pathspec>...`. + -After running `git reset <paths>` to update the index entry, you can +After running `git reset <pathspec>` to update the index entry, you can use linkgit:git-restore[1] to check the contents out of the index to the working tree. Alternatively, using linkgit:git-restore[1] and specifying a commit with `--source`, you can copy the contents of a path out of a commit to the index and to the working tree in one go. -'git reset' (--patch | -p) [<tree-ish>] [--] [<paths>...]:: +'git reset' (--patch | -p) [<tree-ish>] [--] [<pathspec>...]:: Interactively select hunks in the difference between the index and `<tree-ish>` (defaults to `HEAD`). The chosen hunks are applied in reverse to the index. @@ -101,6 +101,13 @@ OPTIONS `reset.quiet` config option. `--quiet` and `--no-quiet` will override the default behavior. +\--:: + Do not interpret any more arguments as options. + +<pathspec>...:: + Limits the paths affected by the operation. ++ +For more details, see the 'pathspec' entry in linkgit:gitglossary[7]. EXAMPLES -------- diff --git a/builtin/reset.c b/builtin/reset.c index fdd572168b..9291c0fd72 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -30,8 +30,8 @@ static const char * const git_reset_usage[] = { N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"), - N_("git reset [-q] [<tree-ish>] [--] <paths>..."), - N_("git reset --patch [<tree-ish>] [--] [<paths>...]"), + N_("git reset [-q] [<tree-ish>] [--] <pathspec>..."), + N_("git reset --patch [<tree-ish>] [--] [<pathspec>...]"), NULL }; -- gitgitgadget ^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH v3 4/6] reset: support the `--pathspec-from-file` option 2019-11-19 16:48 ` [PATCH v3 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget ` (2 preceding siblings ...) 2019-11-19 16:48 ` [PATCH v3 3/6] doc: reset: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget @ 2019-11-19 16:48 ` Alexandr Miloslavskiy via GitGitGadget 2019-11-19 16:48 ` [PATCH v3 5/6] doc: commit: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget ` (3 subsequent siblings) 7 siblings, 0 replies; 80+ messages in thread From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-19 16:48 UTC (permalink / raw) To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> Decisions taken for simplicity: 1) For now, `--pathspec-from-file` is declared incompatible with `--patch`, even when <file> is not `stdin`. Such use case it not really expected. Also, it is harder to support in `git commit`, so I decided to make it incompatible in all places. 2) It is not allowed to pass pathspec in both args and file. Co-authored-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> --- Documentation/git-reset.txt | 21 ++++- builtin/reset.c | 21 ++++- t/t7107-reset-pathspec-file.sh | 155 +++++++++++++++++++++++++++++++++ 3 files changed, 192 insertions(+), 5 deletions(-) create mode 100755 t/t7107-reset-pathspec-file.sh diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt index d517a43e73..932080c55d 100644 --- a/Documentation/git-reset.txt +++ b/Documentation/git-reset.txt @@ -9,18 +9,20 @@ SYNOPSIS -------- [verse] 'git reset' [-q] [<tree-ish>] [--] <pathspec>... +'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-nul]] [<tree-ish>] 'git reset' (--patch | -p) [<tree-ish>] [--] [<pathspec>...] 'git reset' [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>] DESCRIPTION ----------- -In the first and second form, copy entries from `<tree-ish>` to the index. -In the third form, set the current branch head (`HEAD`) to `<commit>`, +In the first three forms, copy entries from `<tree-ish>` to the index. +In the last form, set the current branch head (`HEAD`) to `<commit>`, optionally modifying index and working tree to match. The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms. 'git reset' [-q] [<tree-ish>] [--] <pathspec>...:: - This form resets the index entries for all paths that match the +'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-nul]] [<tree-ish>]:: + These forms reset the index entries for all paths that match the `<pathspec>` to their state at `<tree-ish>`. (It does not affect the working tree or the current branch.) + @@ -101,6 +103,19 @@ OPTIONS `reset.quiet` config option. `--quiet` and `--no-quiet` will override the default behavior. +--pathspec-from-file=<file>:: + Pathspec is passed in `<file>` instead of commandline args. If + `<file>` is exactly `-` then standard input is used. Pathspec + elements are separated by LF or CR/LF. Pathspec elements can be + quoted as explained for the configuration variable `core.quotePath` + (see linkgit:git-config[1]). See also `--pathspec-file-nul` and + global `--literal-pathspecs`. + +--pathspec-file-nul:: + Only meaningful with `--pathspec-from-file`. Pathspec elements are + separated with NUL character and all other characters are taken + literally (including newlines and quotes). + \--:: Do not interpret any more arguments as options. diff --git a/builtin/reset.c b/builtin/reset.c index 9291c0fd72..246bf9d737 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -31,6 +31,7 @@ static const char * const git_reset_usage[] = { N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"), N_("git reset [-q] [<tree-ish>] [--] <pathspec>..."), + N_("git reset [-q] [--pathspec-from-file [--pathspec-file-nul]] [<tree-ish>]"), N_("git reset --patch [<tree-ish>] [--] [<pathspec>...]"), NULL }; @@ -284,8 +285,8 @@ static int git_reset_config(const char *var, const char *value, void *cb) int cmd_reset(int argc, const char **argv, const char *prefix) { int reset_type = NONE, update_ref_status = 0, quiet = 0; - int patch_mode = 0, unborn; - const char *rev; + int patch_mode = 0, pathspec_file_nul = 0, unborn; + const char *rev, *pathspec_from_file = NULL; struct object_id oid; struct pathspec pathspec; int intent_to_add = 0; @@ -306,6 +307,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix) OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")), OPT_BOOL('N', "intent-to-add", &intent_to_add, N_("record only the fact that removed paths will be added later")), + OPT_PATHSPEC_FROM_FILE(&pathspec_from_file), + OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul), OPT_END() }; @@ -316,6 +319,20 @@ int cmd_reset(int argc, const char **argv, const char *prefix) PARSE_OPT_KEEP_DASHDASH); parse_args(&pathspec, argv, prefix, patch_mode, &rev); + if (pathspec_from_file) { + if (patch_mode) + die(_("--pathspec-from-file is incompatible with --patch")); + + if (pathspec.nr) + die(_("--pathspec-from-file is incompatible with pathspec arguments")); + + parse_pathspec_file(&pathspec, 0, + PATHSPEC_PREFER_FULL, + prefix, pathspec_from_file, pathspec_file_nul); + } else if (pathspec_file_nul) { + die(_("--pathspec-file-nul requires --pathspec-from-file")); + } + unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid); if (unborn) { /* reset on unborn branch: treat as reset to empty tree */ diff --git a/t/t7107-reset-pathspec-file.sh b/t/t7107-reset-pathspec-file.sh new file mode 100755 index 0000000000..6b1a731fff --- /dev/null +++ b/t/t7107-reset-pathspec-file.sh @@ -0,0 +1,155 @@ +#!/bin/sh + +test_description='reset --pathspec-from-file' + +. ./test-lib.sh + +test_tick + +test_expect_success setup ' + echo A >fileA.t && + echo B >fileB.t && + echo C >fileC.t && + echo D >fileD.t && + git add . && + git commit --include . -m "Commit" && + git tag checkpoint +' + +restore_checkpoint () { + git reset --hard checkpoint +} + +verify_expect () { + git status --porcelain -- fileA.t fileB.t fileC.t fileD.t >actual && + test_cmp expect actual +} + +test_expect_success '--pathspec-from-file from stdin' ' + restore_checkpoint && + + git rm fileA.t && + echo fileA.t | git reset --pathspec-from-file=- && + + cat >expect <<-\EOF && + D fileA.t + EOF + verify_expect +' + +test_expect_success '--pathspec-from-file from file' ' + restore_checkpoint && + + git rm fileA.t && + echo fileA.t >list && + git reset --pathspec-from-file=list && + + cat >expect <<-\EOF && + D fileA.t + EOF + verify_expect +' + +test_expect_success 'NUL delimiters' ' + restore_checkpoint && + + git rm fileA.t fileB.t && + printf "fileA.t\0fileB.t\0" | git reset --pathspec-from-file=- --pathspec-file-nul && + + cat >expect <<-\EOF && + D fileA.t + D fileB.t + EOF + verify_expect +' + +test_expect_success 'LF delimiters' ' + restore_checkpoint && + + git rm fileA.t fileB.t && + printf "fileA.t\nfileB.t\n" | git reset --pathspec-from-file=- && + + cat >expect <<-\EOF && + D fileA.t + D fileB.t + EOF + verify_expect +' + +test_expect_success 'no trailing delimiter' ' + restore_checkpoint && + + git rm fileA.t fileB.t && + printf "fileA.t\nfileB.t" | git reset --pathspec-from-file=- && + + cat >expect <<-\EOF && + D fileA.t + D fileB.t + EOF + verify_expect +' + +test_expect_success 'CRLF delimiters' ' + restore_checkpoint && + + git rm fileA.t fileB.t && + printf "fileA.t\r\nfileB.t\r\n" | git reset --pathspec-from-file=- && + + cat >expect <<-\EOF && + D fileA.t + D fileB.t + EOF + verify_expect +' + +test_expect_success 'quotes' ' + restore_checkpoint && + + git rm fileA.t && + printf "\"file\\101.t\"" | git reset --pathspec-from-file=- && + + cat >expect <<-\EOF && + D fileA.t + EOF + verify_expect +' + +test_expect_success 'quotes not compatible with --pathspec-file-nul' ' + restore_checkpoint && + + git rm fileA.t && + printf "\"file\\101.t\"" >list && + # Note: "git reset" has not yet learned to fail on wrong pathspecs + git reset --pathspec-from-file=list --pathspec-file-nul && + + cat >expect <<-\EOF && + D fileA.t + EOF + test_must_fail verify_expect +' + +test_expect_success '--pathspec-from-file is not compatible with --soft or --hard' ' + restore_checkpoint && + + git rm fileA.t && + echo fileA.t >list && + test_must_fail git reset --soft --pathspec-from-file=list && + test_must_fail git reset --hard --pathspec-from-file=list +' + +test_expect_success 'only touches what was listed' ' + restore_checkpoint && + + git rm fileA.t fileB.t fileC.t fileD.t && + printf "fileB.t\nfileC.t\n" | git reset --pathspec-from-file=- && + + cat >expect <<-\EOF && + D fileA.t + D fileB.t + D fileC.t + D fileD.t + EOF + verify_expect +' + +test_done -- gitgitgadget ^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH v3 5/6] doc: commit: synchronize <pathspec> description 2019-11-19 16:48 ` [PATCH v3 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget ` (3 preceding siblings ...) 2019-11-19 16:48 ` [PATCH v3 4/6] reset: support the `--pathspec-from-file` option Alexandr Miloslavskiy via GitGitGadget @ 2019-11-19 16:48 ` Alexandr Miloslavskiy via GitGitGadget 2019-11-19 16:48 ` [PATCH v3 6/6] commit: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget ` (2 subsequent siblings) 7 siblings, 0 replies; 80+ messages in thread From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-19 16:48 UTC (permalink / raw) To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> `git add` shows an example of good writing, follow it. This also better disambiguates <file>... header. Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> --- Documentation/git-commit.txt | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt index afa7b75a23..a0c44978ee 100644 --- a/Documentation/git-commit.txt +++ b/Documentation/git-commit.txt @@ -13,7 +13,7 @@ SYNOPSIS [-F <file> | -m <msg>] [--reset-author] [--allow-empty] [--allow-empty-message] [--no-verify] [-e] [--author=<author>] [--date=<date>] [--cleanup=<mode>] [--[no-]status] - [-i | -o] [-S[<keyid>]] [--] [<file>...] + [-i | -o] [-S[<keyid>]] [--] [<pathspec>...] DESCRIPTION ----------- @@ -345,12 +345,13 @@ changes to tracked files. \--:: Do not interpret any more arguments as options. -<file>...:: - When files are given on the command line, the command - commits the contents of the named files, without - recording the changes already staged. The contents of - these files are also staged for the next commit on top - of what have been staged before. +<pathspec>...:: + When pathspec is given on the command line, commit the contents of + the files that match the pathspec without recording the changes + already added to the index. The contents of these files are also + staged for the next commit on top of what have been staged before. ++ +For more details, see the 'pathspec' entry in linkgit:gitglossary[7]. :git-commit: 1 include::date-formats.txt[] -- gitgitgadget ^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH v3 6/6] commit: support the --pathspec-from-file option 2019-11-19 16:48 ` [PATCH v3 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget ` (4 preceding siblings ...) 2019-11-19 16:48 ` [PATCH v3 5/6] doc: commit: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget @ 2019-11-19 16:48 ` Alexandr Miloslavskiy via GitGitGadget 2019-11-20 4:04 ` [PATCH v3 0/6] Add --pathspec-from-file option for reset, commit Junio C Hamano 2019-12-03 14:02 ` [PATCH v4 00/13] Add --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget 7 siblings, 0 replies; 80+ messages in thread From: Alexandr Miloslavskiy via GitGitGadget @ 2019-11-19 16:48 UTC (permalink / raw) To: git; +Cc: Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> Decisions taken for simplicity: 1) For now, `--pathspec-from-file` is declared incompatible with `--interactive/--patch`, even when <file> is not `stdin`. Such use case it not really expected. Also, it would require changes to `interactive_add()`. 2) It is not allowed to pass pathspec in both args and file. Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> --- Documentation/git-commit.txt | 16 +++- builtin/commit.c | 25 +++++- t/t7526-commit-pathspec-file.sh | 130 ++++++++++++++++++++++++++++++++ 3 files changed, 166 insertions(+), 5 deletions(-) create mode 100755 t/t7526-commit-pathspec-file.sh diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt index a0c44978ee..ced5a9beab 100644 --- a/Documentation/git-commit.txt +++ b/Documentation/git-commit.txt @@ -13,7 +13,8 @@ SYNOPSIS [-F <file> | -m <msg>] [--reset-author] [--allow-empty] [--allow-empty-message] [--no-verify] [-e] [--author=<author>] [--date=<date>] [--cleanup=<mode>] [--[no-]status] - [-i | -o] [-S[<keyid>]] [--] [<pathspec>...] + [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]] + [-S[<keyid>]] [--] [<pathspec>...] DESCRIPTION ----------- @@ -278,6 +279,19 @@ FROM UPSTREAM REBASE" section in linkgit:git-rebase[1].) already been staged. If used together with `--allow-empty` paths are also not required, and an empty commit will be created. +--pathspec-from-file=<file>:: + Pathspec is passed in `<file>` instead of commandline args. If + `<file>` is exactly `-` then standard input is used. Pathspec + elements are separated by LF or CR/LF. Pathspec elements can be + quoted as explained for the configuration variable `core.quotePath` + (see linkgit:git-config[1]). See also `--pathspec-file-nul` and + global `--literal-pathspecs`. + +--pathspec-file-nul:: + Only meaningful with `--pathspec-from-file`. Pathspec elements are + separated with NUL character and all other characters are taken + literally (including newlines and quotes). + -u[<mode>]:: --untracked-files[=<mode>]:: Show untracked files. diff --git a/builtin/commit.c b/builtin/commit.c index e588bc6ad3..ed40729355 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -107,9 +107,9 @@ static int all, also, interactive, patch_interactive, only, amend, signoff; static int edit_flag = -1; /* unspecified */ static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship; static int config_commit_verbose = -1; /* unspecified */ -static int no_post_rewrite, allow_empty_message; +static int no_post_rewrite, allow_empty_message, pathspec_file_nul; static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg; -static char *sign_commit; +static char *sign_commit, *pathspec_from_file; /* * The default commit message cleanup mode will remove the lines @@ -343,6 +343,23 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix PATHSPEC_PREFER_FULL, prefix, argv); + if (pathspec_from_file) { + if (interactive) + die(_("--pathspec-from-file is incompatible with --interactive/--patch")); + + if (pathspec.nr) + die(_("--pathspec-from-file is incompatible with pathspec arguments")); + + parse_pathspec_file(&pathspec, 0, + PATHSPEC_PREFER_FULL, + prefix, pathspec_from_file, pathspec_file_nul); + } else if (pathspec_file_nul) { + die(_("--pathspec-file-nul requires --pathspec-from-file")); + } + + if (!pathspec.nr && (also || (only && !amend && !allow_empty))) + die(_("No paths with --include/--only does not make sense.")); + if (read_cache_preload(&pathspec) < 0) die(_("index file corrupt")); @@ -1198,8 +1215,6 @@ static int parse_and_validate_options(int argc, const char *argv[], if (also + only + all + interactive > 1) die(_("Only one of --include/--only/--all/--interactive/--patch can be used.")); - if (argc == 0 && (also || (only && !amend && !allow_empty))) - die(_("No paths with --include/--only does not make sense.")); cleanup_mode = get_cleanup_mode(cleanup_arg, use_editor); handle_untracked_files_arg(s); @@ -1535,6 +1550,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "amend", &amend, N_("amend previous commit")), OPT_BOOL(0, "no-post-rewrite", &no_post_rewrite, N_("bypass post-rewrite hook")), { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, N_("mode"), N_("show untracked files, optional modes: all, normal, no. (Default: all)"), PARSE_OPT_OPTARG, NULL, (intptr_t)"all" }, + OPT_PATHSPEC_FROM_FILE(&pathspec_from_file), + OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul), /* end commit contents options */ OPT_HIDDEN_BOOL(0, "allow-empty", &allow_empty, diff --git a/t/t7526-commit-pathspec-file.sh b/t/t7526-commit-pathspec-file.sh new file mode 100755 index 0000000000..a06b683534 --- /dev/null +++ b/t/t7526-commit-pathspec-file.sh @@ -0,0 +1,130 @@ +#!/bin/sh + +test_description='commit --pathspec-from-file' + +. ./test-lib.sh + +test_tick + +test_expect_success setup ' + test_commit file0 && + git tag checkpoint && + + echo A >fileA.t && + echo B >fileB.t && + echo C >fileC.t && + echo D >fileD.t && + git add fileA.t fileB.t fileC.t fileD.t +' + +restore_checkpoint () { + git reset --soft checkpoint +} + +verify_expect () { + git diff-tree --no-commit-id --name-status -r HEAD >actual && + test_cmp expect actual +} + +test_expect_success '--pathspec-from-file from stdin' ' + restore_checkpoint && + + echo fileA.t | git commit --pathspec-from-file=- -m "Commit" && + + cat >expect <<-\EOF && + A fileA.t + EOF + verify_expect +' + +test_expect_success '--pathspec-from-file from file' ' + restore_checkpoint && + + echo fileA.t >list && + git commit --pathspec-from-file=list -m "Commit" && + + cat >expect <<-\EOF && + A fileA.t + EOF + verify_expect +' + +test_expect_success 'NUL delimiters' ' + restore_checkpoint && + + printf "fileA.t\0fileB.t\0" | git commit --pathspec-from-file=- --pathspec-file-nul -m "Commit" && + + cat >expect <<-\EOF && + A fileA.t + A fileB.t + EOF + verify_expect +' + +test_expect_success 'LF delimiters' ' + restore_checkpoint && + + printf "fileA.t\nfileB.t\n" | git commit --pathspec-from-file=- -m "Commit" && + + cat >expect <<-\EOF && + A fileA.t + A fileB.t + EOF + verify_expect +' + +test_expect_success 'no trailing delimiter' ' + restore_checkpoint && + + printf "fileA.t\nfileB.t" | git commit --pathspec-from-file=- -m "Commit" && + + cat >expect <<-\EOF && + A fileA.t + A fileB.t + EOF + verify_expect +' + +test_expect_success 'CRLF delimiters' ' + restore_checkpoint && + + printf "fileA.t\r\nfileB.t\r\n" | git commit --pathspec-from-file=- -m "Commit" && + + cat >expect <<-\EOF && + A fileA.t + A fileB.t + EOF + verify_expect +' + +test_expect_success 'quotes' ' + restore_checkpoint && + + printf "\"file\\101.t\"" | git commit --pathspec-from-file=- -m "Commit" && + + cat >expect <<-\EOF && + A fileA.t + EOF + verify_expect expect +' + +test_expect_success 'quotes not compatible with --pathspec-file-nul' ' + restore_checkpoint && + + printf "\"file\\101.t\"" >list && + test_must_fail git commit --pathspec-from-file=list --pathspec-file-nul -m "Commit" +' + +test_expect_success 'only touches what was listed' ' + restore_checkpoint && + + printf "fileB.t\nfileC.t\n" | git commit --pathspec-from-file=- -m "Commit" && + + cat >expect <<-\EOF && + A fileB.t + A fileC.t + EOF + verify_expect +' + +test_done -- gitgitgadget ^ permalink raw reply related [flat|nested] 80+ messages in thread
* Re: [PATCH v3 0/6] Add --pathspec-from-file option for reset, commit 2019-11-19 16:48 ` [PATCH v3 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget ` (5 preceding siblings ...) 2019-11-19 16:48 ` [PATCH v3 6/6] commit: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget @ 2019-11-20 4:04 ` Junio C Hamano 2019-11-20 9:22 ` Alexandr Miloslavskiy 2019-12-03 14:02 ` [PATCH v4 00/13] Add --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget 7 siblings, 1 reply; 80+ messages in thread From: Junio C Hamano @ 2019-11-20 4:04 UTC (permalink / raw) To: Alexandr Miloslavskiy via GitGitGadget; +Cc: git, Alexandr Miloslavskiy "Alexandr Miloslavskiy via GitGitGadget" <gitgitgadget@gmail.com> writes: > Changes from V2: > > 1) Some polishing in documentation 2) = NULL removed from > parse_pathspec_file() > > Alexandr Miloslavskiy (6): > parse-options.h: add new options `--pathspec-from-file`, > `--pathspec-file-nul` > pathspec: add new function to parse file > doc: reset: synchronize <pathspec> description > reset: support the `--pathspec-from-file` option > doc: commit: synchronize <pathspec> description > commit: support the --pathspec-from-file option Nicely done. I think this is now ready for 'next'. Thanks. ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH v3 0/6] Add --pathspec-from-file option for reset, commit 2019-11-20 4:04 ` [PATCH v3 0/6] Add --pathspec-from-file option for reset, commit Junio C Hamano @ 2019-11-20 9:22 ` Alexandr Miloslavskiy 0 siblings, 0 replies; 80+ messages in thread From: Alexandr Miloslavskiy @ 2019-11-20 9:22 UTC (permalink / raw) To: Junio C Hamano, Alexandr Miloslavskiy via GitGitGadget; +Cc: git On 20.11.2019 5:04, Junio C Hamano wrote: > Nicely done. I think this is now ready for 'next'. Wonderful, thanks for your assistance! I will continue with patches for other commands, then. ^ permalink raw reply [flat|nested] 80+ messages in thread
* [PATCH v4 00/13] Add --pathspec-from-file option 2019-11-19 16:48 ` [PATCH v3 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget ` (6 preceding siblings ...) 2019-11-20 4:04 ` [PATCH v3 0/6] Add --pathspec-from-file option for reset, commit Junio C Hamano @ 2019-12-03 14:02 ` Alexandr Miloslavskiy via GitGitGadget 2019-12-03 14:02 ` [PATCH v4 01/13] parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul` Alexandr Miloslavskiy via GitGitGadget ` (13 more replies) 7 siblings, 14 replies; 80+ messages in thread From: Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 UTC (permalink / raw) To: git; +Cc: Junio C Hamano, Alexandr Miloslavskiy, Junio C Hamano Changes from V3: ================ The branch was rebased onto latest master. These patches remain unchanged since they were accepted in V3: parse-options.h: add new options --pathspec-from-file, --pathspec-file-nulpathspec: add new function to parse file doc: reset: synchronize description reset: support the --pathspec-from-file option doc: commit: synchronize description commit: support the --pathspec-from-file option These patches are new, extending support to more git commands: cmd_add: prepare for next patch add: support the --pathspec-from-file option doc: checkout: remove duplicate synopsis doc: checkout: fix broken text reference doc: checkout: synchronize description doc: restore: synchronize description checkout, restore: support the --pathspec-from-file option Cc: Phillip Wood phillip.wood123@gmail.com [phillip.wood123@gmail.com] Alexandr Miloslavskiy (13): parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul` pathspec: add new function to parse file doc: reset: synchronize <pathspec> description reset: support the `--pathspec-from-file` option doc: commit: synchronize <pathspec> description commit: support the --pathspec-from-file option cmd_add: prepare for next patch add: support the --pathspec-from-file option doc: checkout: remove duplicate synopsis doc: checkout: fix broken text reference doc: checkout: synchronize <pathspec> description doc: restore: synchronize <pathspec> description checkout, restore: support the --pathspec-from-file option Documentation/git-add.txt | 16 ++- Documentation/git-checkout.txt | 50 +++++++--- Documentation/git-commit.txt | 29 ++++-- Documentation/git-reset.txt | 48 ++++++--- Documentation/git-restore.txt | 26 ++++- builtin/add.c | 60 ++++++++---- builtin/checkout.c | 31 +++++- builtin/commit.c | 25 ++++- builtin/reset.c | 25 ++++- parse-options.h | 2 + pathspec.c | 38 ++++++++ pathspec.h | 10 ++ t/t2026-checkout-pathspec-file.sh | 139 +++++++++++++++++++++++++++ t/t2072-restore-pathspec-file.sh | 139 +++++++++++++++++++++++++++ t/t3704-add-pathspec-file.sh | 127 ++++++++++++++++++++++++ t/t7107-reset-pathspec-file.sh | 155 ++++++++++++++++++++++++++++++ t/t7526-commit-pathspec-file.sh | 130 +++++++++++++++++++++++++ t/t9902-completion.sh | 2 + 18 files changed, 982 insertions(+), 70 deletions(-) create mode 100755 t/t2026-checkout-pathspec-file.sh create mode 100755 t/t2072-restore-pathspec-file.sh create mode 100755 t/t3704-add-pathspec-file.sh create mode 100755 t/t7107-reset-pathspec-file.sh create mode 100755 t/t7526-commit-pathspec-file.sh base-commit: 228f53135a4a41a37b6be8e4d6e2b6153db4a8ed Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-445%2FSyntevoAlex%2F%230207_pathspec_from_file-v4 Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-445/SyntevoAlex/#0207_pathspec_from_file-v4 Pull-Request: https://github.com/gitgitgadget/git/pull/445 Range-diff vs v3: 1: 19b80326ea = 1: cca5aee392 parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul` 2: 55a7c6ec3c = 2: fea64dfbf9 pathspec: add new function to parse file 3: d9f32e523c = 3: 1182ba3953 doc: reset: synchronize <pathspec> description 4: 8a10ff881b = 4: cea470fc91 reset: support the `--pathspec-from-file` option 5: 0b79797e77 = 5: 0e1ac7e8a7 doc: commit: synchronize <pathspec> description 6: 7e48212002 = 6: c877866c13 commit: support the --pathspec-from-file option -: ---------- > 7: a97910cb55 cmd_add: prepare for next patch -: ---------- > 8: 9a62da3470 add: support the --pathspec-from-file option -: ---------- > 9: 5e449c8d29 doc: checkout: remove duplicate synopsis -: ---------- > 10: a498dda97b doc: checkout: fix broken text reference -: ---------- > 11: fd166755aa doc: checkout: synchronize <pathspec> description -: ---------- > 12: 9e37a740e6 doc: restore: synchronize <pathspec> description -: ---------- > 13: c4dd4eaf13 checkout, restore: support the --pathspec-from-file option -- gitgitgadget ^ permalink raw reply [flat|nested] 80+ messages in thread
* [PATCH v4 01/13] parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul` 2019-12-03 14:02 ` [PATCH v4 00/13] Add --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 ` Alexandr Miloslavskiy via GitGitGadget 2019-12-03 14:02 ` [PATCH v4 02/13] pathspec: add new function to parse file Alexandr Miloslavskiy via GitGitGadget ` (12 subsequent siblings) 13 siblings, 0 replies; 80+ messages in thread From: Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 UTC (permalink / raw) To: git Cc: Junio C Hamano, Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> Support for various porcelain commands will arrive via additional patches. `--pathspec-from-file` solves the problem of commandline length limit for UIs built on top of git. Plumbing commands are not always a good fit, for two major reasons: 1) Some UIs show executed commands to user. In this case, porcelain commands are expected. One reason for that is letting user learn git commands by clicking UI buttons. The other reason is letting user study the history of commands in case of any unexpected results. Both of these will lose most of their value if UI uses combinations of arcane plumbing commands. 2) Some UIs have started and grown with porcelain commands. Replacing existing logic with plumbing commands could be cumbersome and prone to various new problems. `--pathspec-from-file` will behave very close to pathspec passed in commandline args, so that switching from one to another is simple. `--pathspec-from-file` will read either a specified file or `stdin` (when file is exactly "-"). Reading from file is a good way to avoid competing for `stdin`, and also gives some extra flexibility. `--pathspec-file-nul` switch mirrors `-z` already used in various places. Some porcelain commands, such as `git commit`, already use `-z`, therefore it needed a new unambiguous name. New options do not have shorthands to avoid shorthand conflicts. It is not expected that they will be typed in console. Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> --- parse-options.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/parse-options.h b/parse-options.h index 38a33a087e..c6cc01e715 100644 --- a/parse-options.h +++ b/parse-options.h @@ -330,5 +330,7 @@ int parse_opt_passthru_argv(const struct option *, const char *, int); #define OPT_WITH(v, h) _OPT_CONTAINS_OR_WITH("with", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG) #define OPT_WITHOUT(v, h) _OPT_CONTAINS_OR_WITH("without", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG) #define OPT_CLEANUP(v) OPT_STRING(0, "cleanup", v, N_("mode"), N_("how to strip spaces and #comments from message")) +#define OPT_PATHSPEC_FROM_FILE(v) OPT_FILENAME(0, "pathspec-from-file", v, N_("read pathspec from file")) +#define OPT_PATHSPEC_FILE_NUL(v) OPT_BOOL(0, "pathspec-file-nul", v, N_("with --pathspec-from-file, pathspec elements are separated with NUL character")) #endif -- gitgitgadget ^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH v4 02/13] pathspec: add new function to parse file 2019-12-03 14:02 ` [PATCH v4 00/13] Add --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget 2019-12-03 14:02 ` [PATCH v4 01/13] parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul` Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 ` Alexandr Miloslavskiy via GitGitGadget 2019-12-03 14:02 ` [PATCH v4 03/13] doc: reset: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget ` (11 subsequent siblings) 13 siblings, 0 replies; 80+ messages in thread From: Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 UTC (permalink / raw) To: git Cc: Junio C Hamano, Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> This will be used to support the new option '--pathspec-from-file' in `git add`, `git-commit`, `git reset` etc. Note also that we specifically handle CR/LF line endings to support Windows better. To simplify code, file is first parsed into `argv_array`. This allows to avoid refactoring `parse_pathspec()`. I considered adding `nul_term_line` to `flags` instead, but decided that it doesn't fit there. The new code is mostly taken from `cmd_update_index()` and `split_mail_conv()`. Co-authored-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> --- pathspec.c | 38 ++++++++++++++++++++++++++++++++++++++ pathspec.h | 10 ++++++++++ 2 files changed, 48 insertions(+) diff --git a/pathspec.c b/pathspec.c index 12c2b322b3..128f27fcb7 100644 --- a/pathspec.c +++ b/pathspec.c @@ -3,6 +3,8 @@ #include "dir.h" #include "pathspec.h" #include "attr.h" +#include "argv-array.h" +#include "quote.h" /* * Finds which of the given pathspecs match items in the index. @@ -613,6 +615,42 @@ void parse_pathspec(struct pathspec *pathspec, } } +void parse_pathspec_file(struct pathspec *pathspec, unsigned magic_mask, + unsigned flags, const char *prefix, + const char *file, int nul_term_line) +{ + struct argv_array parsed_file = ARGV_ARRAY_INIT; + strbuf_getline_fn getline_fn = nul_term_line ? strbuf_getline_nul : + strbuf_getline; + struct strbuf buf = STRBUF_INIT; + struct strbuf unquoted = STRBUF_INIT; + FILE *in; + + if (!strcmp(file, "-")) + in = stdin; + else + in = xfopen(file, "r"); + + while (getline_fn(&buf, in) != EOF) { + if (!nul_term_line && buf.buf[0] == '"') { + strbuf_reset(&unquoted); + if (unquote_c_style(&unquoted, buf.buf, NULL)) + die(_("line is badly quoted: %s"), buf.buf); + strbuf_swap(&buf, &unquoted); + } + argv_array_push(&parsed_file, buf.buf); + strbuf_reset(&buf); + } + + strbuf_release(&unquoted); + strbuf_release(&buf); + if (in != stdin) + fclose(in); + + parse_pathspec(pathspec, magic_mask, flags, prefix, parsed_file.argv); + argv_array_clear(&parsed_file); +} + void copy_pathspec(struct pathspec *dst, const struct pathspec *src) { int i, j; diff --git a/pathspec.h b/pathspec.h index 1c18a2c90c..a27dc81ba2 100644 --- a/pathspec.h +++ b/pathspec.h @@ -85,6 +85,16 @@ void parse_pathspec(struct pathspec *pathspec, unsigned flags, const char *prefix, const char **args); +/* + * Same as parse_pathspec() but uses file as input. + * When 'file' is exactly "-" it uses 'stdin' instead. + */ +void parse_pathspec_file(struct pathspec *pathspec, + unsigned magic_mask, + unsigned flags, + const char *prefix, + const char *file, + int nul_term_line); void copy_pathspec(struct pathspec *dst, const struct pathspec *src); void clear_pathspec(struct pathspec *); -- gitgitgadget ^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH v4 03/13] doc: reset: synchronize <pathspec> description 2019-12-03 14:02 ` [PATCH v4 00/13] Add --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget 2019-12-03 14:02 ` [PATCH v4 01/13] parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul` Alexandr Miloslavskiy via GitGitGadget 2019-12-03 14:02 ` [PATCH v4 02/13] pathspec: add new function to parse file Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 ` Alexandr Miloslavskiy via GitGitGadget 2019-12-03 14:02 ` [PATCH v4 04/13] reset: support the `--pathspec-from-file` option Alexandr Miloslavskiy via GitGitGadget ` (10 subsequent siblings) 13 siblings, 0 replies; 80+ messages in thread From: Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 UTC (permalink / raw) To: git Cc: Junio C Hamano, Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> `git add` shows an example of good writing, follow it. Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> --- Documentation/git-reset.txt | 29 ++++++++++++++++++----------- builtin/reset.c | 4 ++-- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt index 97e0544d9e..d517a43e73 100644 --- a/Documentation/git-reset.txt +++ b/Documentation/git-reset.txt @@ -8,8 +8,8 @@ git-reset - Reset current HEAD to the specified state SYNOPSIS -------- [verse] -'git reset' [-q] [<tree-ish>] [--] <paths>... -'git reset' (--patch | -p) [<tree-ish>] [--] [<paths>...] +'git reset' [-q] [<tree-ish>] [--] <pathspec>... +'git reset' (--patch | -p) [<tree-ish>] [--] [<pathspec>...] 'git reset' [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>] DESCRIPTION @@ -19,23 +19,23 @@ In the third form, set the current branch head (`HEAD`) to `<commit>`, optionally modifying index and working tree to match. The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms. -'git reset' [-q] [<tree-ish>] [--] <paths>...:: - This form resets the index entries for all `<paths>` to their - state at `<tree-ish>`. (It does not affect the working tree or - the current branch.) +'git reset' [-q] [<tree-ish>] [--] <pathspec>...:: + This form resets the index entries for all paths that match the + `<pathspec>` to their state at `<tree-ish>`. (It does not affect + the working tree or the current branch.) + -This means that `git reset <paths>` is the opposite of `git add -<paths>`. This command is equivalent to -`git restore [--source=<tree-ish>] --staged <paths>...`. +This means that `git reset <pathspec>` is the opposite of `git add +<pathspec>`. This command is equivalent to +`git restore [--source=<tree-ish>] --staged <pathspec>...`. + -After running `git reset <paths>` to update the index entry, you can +After running `git reset <pathspec>` to update the index entry, you can use linkgit:git-restore[1] to check the contents out of the index to the working tree. Alternatively, using linkgit:git-restore[1] and specifying a commit with `--source`, you can copy the contents of a path out of a commit to the index and to the working tree in one go. -'git reset' (--patch | -p) [<tree-ish>] [--] [<paths>...]:: +'git reset' (--patch | -p) [<tree-ish>] [--] [<pathspec>...]:: Interactively select hunks in the difference between the index and `<tree-ish>` (defaults to `HEAD`). The chosen hunks are applied in reverse to the index. @@ -101,6 +101,13 @@ OPTIONS `reset.quiet` config option. `--quiet` and `--no-quiet` will override the default behavior. +\--:: + Do not interpret any more arguments as options. + +<pathspec>...:: + Limits the paths affected by the operation. ++ +For more details, see the 'pathspec' entry in linkgit:gitglossary[7]. EXAMPLES -------- diff --git a/builtin/reset.c b/builtin/reset.c index fdd572168b..9291c0fd72 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -30,8 +30,8 @@ static const char * const git_reset_usage[] = { N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"), - N_("git reset [-q] [<tree-ish>] [--] <paths>..."), - N_("git reset --patch [<tree-ish>] [--] [<paths>...]"), + N_("git reset [-q] [<tree-ish>] [--] <pathspec>..."), + N_("git reset --patch [<tree-ish>] [--] [<pathspec>...]"), NULL }; -- gitgitgadget ^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH v4 04/13] reset: support the `--pathspec-from-file` option 2019-12-03 14:02 ` [PATCH v4 00/13] Add --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget ` (2 preceding siblings ...) 2019-12-03 14:02 ` [PATCH v4 03/13] doc: reset: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 ` Alexandr Miloslavskiy via GitGitGadget 2019-12-03 14:02 ` [PATCH v4 05/13] doc: commit: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget ` (9 subsequent siblings) 13 siblings, 0 replies; 80+ messages in thread From: Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 UTC (permalink / raw) To: git Cc: Junio C Hamano, Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> Decisions taken for simplicity: 1) For now, `--pathspec-from-file` is declared incompatible with `--patch`, even when <file> is not `stdin`. Such use case it not really expected. Also, it is harder to support in `git commit`, so I decided to make it incompatible in all places. 2) It is not allowed to pass pathspec in both args and file. Co-authored-by: Johannes Schindelin <johannes.schindelin@gmx.de> Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> --- Documentation/git-reset.txt | 21 ++++- builtin/reset.c | 21 ++++- t/t7107-reset-pathspec-file.sh | 155 +++++++++++++++++++++++++++++++++ 3 files changed, 192 insertions(+), 5 deletions(-) create mode 100755 t/t7107-reset-pathspec-file.sh diff --git a/Documentation/git-reset.txt b/Documentation/git-reset.txt index d517a43e73..932080c55d 100644 --- a/Documentation/git-reset.txt +++ b/Documentation/git-reset.txt @@ -9,18 +9,20 @@ SYNOPSIS -------- [verse] 'git reset' [-q] [<tree-ish>] [--] <pathspec>... +'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-nul]] [<tree-ish>] 'git reset' (--patch | -p) [<tree-ish>] [--] [<pathspec>...] 'git reset' [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>] DESCRIPTION ----------- -In the first and second form, copy entries from `<tree-ish>` to the index. -In the third form, set the current branch head (`HEAD`) to `<commit>`, +In the first three forms, copy entries from `<tree-ish>` to the index. +In the last form, set the current branch head (`HEAD`) to `<commit>`, optionally modifying index and working tree to match. The `<tree-ish>`/`<commit>` defaults to `HEAD` in all forms. 'git reset' [-q] [<tree-ish>] [--] <pathspec>...:: - This form resets the index entries for all paths that match the +'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-nul]] [<tree-ish>]:: + These forms reset the index entries for all paths that match the `<pathspec>` to their state at `<tree-ish>`. (It does not affect the working tree or the current branch.) + @@ -101,6 +103,19 @@ OPTIONS `reset.quiet` config option. `--quiet` and `--no-quiet` will override the default behavior. +--pathspec-from-file=<file>:: + Pathspec is passed in `<file>` instead of commandline args. If + `<file>` is exactly `-` then standard input is used. Pathspec + elements are separated by LF or CR/LF. Pathspec elements can be + quoted as explained for the configuration variable `core.quotePath` + (see linkgit:git-config[1]). See also `--pathspec-file-nul` and + global `--literal-pathspecs`. + +--pathspec-file-nul:: + Only meaningful with `--pathspec-from-file`. Pathspec elements are + separated with NUL character and all other characters are taken + literally (including newlines and quotes). + \--:: Do not interpret any more arguments as options. diff --git a/builtin/reset.c b/builtin/reset.c index 9291c0fd72..246bf9d737 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -31,6 +31,7 @@ static const char * const git_reset_usage[] = { N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"), N_("git reset [-q] [<tree-ish>] [--] <pathspec>..."), + N_("git reset [-q] [--pathspec-from-file [--pathspec-file-nul]] [<tree-ish>]"), N_("git reset --patch [<tree-ish>] [--] [<pathspec>...]"), NULL }; @@ -284,8 +285,8 @@ static int git_reset_config(const char *var, const char *value, void *cb) int cmd_reset(int argc, const char **argv, const char *prefix) { int reset_type = NONE, update_ref_status = 0, quiet = 0; - int patch_mode = 0, unborn; - const char *rev; + int patch_mode = 0, pathspec_file_nul = 0, unborn; + const char *rev, *pathspec_from_file = NULL; struct object_id oid; struct pathspec pathspec; int intent_to_add = 0; @@ -306,6 +307,8 @@ int cmd_reset(int argc, const char **argv, const char *prefix) OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")), OPT_BOOL('N', "intent-to-add", &intent_to_add, N_("record only the fact that removed paths will be added later")), + OPT_PATHSPEC_FROM_FILE(&pathspec_from_file), + OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul), OPT_END() }; @@ -316,6 +319,20 @@ int cmd_reset(int argc, const char **argv, const char *prefix) PARSE_OPT_KEEP_DASHDASH); parse_args(&pathspec, argv, prefix, patch_mode, &rev); + if (pathspec_from_file) { + if (patch_mode) + die(_("--pathspec-from-file is incompatible with --patch")); + + if (pathspec.nr) + die(_("--pathspec-from-file is incompatible with pathspec arguments")); + + parse_pathspec_file(&pathspec, 0, + PATHSPEC_PREFER_FULL, + prefix, pathspec_from_file, pathspec_file_nul); + } else if (pathspec_file_nul) { + die(_("--pathspec-file-nul requires --pathspec-from-file")); + } + unborn = !strcmp(rev, "HEAD") && get_oid("HEAD", &oid); if (unborn) { /* reset on unborn branch: treat as reset to empty tree */ diff --git a/t/t7107-reset-pathspec-file.sh b/t/t7107-reset-pathspec-file.sh new file mode 100755 index 0000000000..6b1a731fff --- /dev/null +++ b/t/t7107-reset-pathspec-file.sh @@ -0,0 +1,155 @@ +#!/bin/sh + +test_description='reset --pathspec-from-file' + +. ./test-lib.sh + +test_tick + +test_expect_success setup ' + echo A >fileA.t && + echo B >fileB.t && + echo C >fileC.t && + echo D >fileD.t && + git add . && + git commit --include . -m "Commit" && + git tag checkpoint +' + +restore_checkpoint () { + git reset --hard checkpoint +} + +verify_expect () { + git status --porcelain -- fileA.t fileB.t fileC.t fileD.t >actual && + test_cmp expect actual +} + +test_expect_success '--pathspec-from-file from stdin' ' + restore_checkpoint && + + git rm fileA.t && + echo fileA.t | git reset --pathspec-from-file=- && + + cat >expect <<-\EOF && + D fileA.t + EOF + verify_expect +' + +test_expect_success '--pathspec-from-file from file' ' + restore_checkpoint && + + git rm fileA.t && + echo fileA.t >list && + git reset --pathspec-from-file=list && + + cat >expect <<-\EOF && + D fileA.t + EOF + verify_expect +' + +test_expect_success 'NUL delimiters' ' + restore_checkpoint && + + git rm fileA.t fileB.t && + printf "fileA.t\0fileB.t\0" | git reset --pathspec-from-file=- --pathspec-file-nul && + + cat >expect <<-\EOF && + D fileA.t + D fileB.t + EOF + verify_expect +' + +test_expect_success 'LF delimiters' ' + restore_checkpoint && + + git rm fileA.t fileB.t && + printf "fileA.t\nfileB.t\n" | git reset --pathspec-from-file=- && + + cat >expect <<-\EOF && + D fileA.t + D fileB.t + EOF + verify_expect +' + +test_expect_success 'no trailing delimiter' ' + restore_checkpoint && + + git rm fileA.t fileB.t && + printf "fileA.t\nfileB.t" | git reset --pathspec-from-file=- && + + cat >expect <<-\EOF && + D fileA.t + D fileB.t + EOF + verify_expect +' + +test_expect_success 'CRLF delimiters' ' + restore_checkpoint && + + git rm fileA.t fileB.t && + printf "fileA.t\r\nfileB.t\r\n" | git reset --pathspec-from-file=- && + + cat >expect <<-\EOF && + D fileA.t + D fileB.t + EOF + verify_expect +' + +test_expect_success 'quotes' ' + restore_checkpoint && + + git rm fileA.t && + printf "\"file\\101.t\"" | git reset --pathspec-from-file=- && + + cat >expect <<-\EOF && + D fileA.t + EOF + verify_expect +' + +test_expect_success 'quotes not compatible with --pathspec-file-nul' ' + restore_checkpoint && + + git rm fileA.t && + printf "\"file\\101.t\"" >list && + # Note: "git reset" has not yet learned to fail on wrong pathspecs + git reset --pathspec-from-file=list --pathspec-file-nul && + + cat >expect <<-\EOF && + D fileA.t + EOF + test_must_fail verify_expect +' + +test_expect_success '--pathspec-from-file is not compatible with --soft or --hard' ' + restore_checkpoint && + + git rm fileA.t && + echo fileA.t >list && + test_must_fail git reset --soft --pathspec-from-file=list && + test_must_fail git reset --hard --pathspec-from-file=list +' + +test_expect_success 'only touches what was listed' ' + restore_checkpoint && + + git rm fileA.t fileB.t fileC.t fileD.t && + printf "fileB.t\nfileC.t\n" | git reset --pathspec-from-file=- && + + cat >expect <<-\EOF && + D fileA.t + D fileB.t + D fileC.t + D fileD.t + EOF + verify_expect +' + +test_done -- gitgitgadget ^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH v4 05/13] doc: commit: synchronize <pathspec> description 2019-12-03 14:02 ` [PATCH v4 00/13] Add --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget ` (3 preceding siblings ...) 2019-12-03 14:02 ` [PATCH v4 04/13] reset: support the `--pathspec-from-file` option Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 ` Alexandr Miloslavskiy via GitGitGadget 2019-12-03 14:02 ` [PATCH v4 06/13] commit: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget ` (8 subsequent siblings) 13 siblings, 0 replies; 80+ messages in thread From: Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 UTC (permalink / raw) To: git Cc: Junio C Hamano, Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> `git add` shows an example of good writing, follow it. This also better disambiguates <file>... header. Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> --- Documentation/git-commit.txt | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt index afa7b75a23..a0c44978ee 100644 --- a/Documentation/git-commit.txt +++ b/Documentation/git-commit.txt @@ -13,7 +13,7 @@ SYNOPSIS [-F <file> | -m <msg>] [--reset-author] [--allow-empty] [--allow-empty-message] [--no-verify] [-e] [--author=<author>] [--date=<date>] [--cleanup=<mode>] [--[no-]status] - [-i | -o] [-S[<keyid>]] [--] [<file>...] + [-i | -o] [-S[<keyid>]] [--] [<pathspec>...] DESCRIPTION ----------- @@ -345,12 +345,13 @@ changes to tracked files. \--:: Do not interpret any more arguments as options. -<file>...:: - When files are given on the command line, the command - commits the contents of the named files, without - recording the changes already staged. The contents of - these files are also staged for the next commit on top - of what have been staged before. +<pathspec>...:: + When pathspec is given on the command line, commit the contents of + the files that match the pathspec without recording the changes + already added to the index. The contents of these files are also + staged for the next commit on top of what have been staged before. ++ +For more details, see the 'pathspec' entry in linkgit:gitglossary[7]. :git-commit: 1 include::date-formats.txt[] -- gitgitgadget ^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH v4 06/13] commit: support the --pathspec-from-file option 2019-12-03 14:02 ` [PATCH v4 00/13] Add --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget ` (4 preceding siblings ...) 2019-12-03 14:02 ` [PATCH v4 05/13] doc: commit: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 ` Alexandr Miloslavskiy via GitGitGadget 2019-12-03 14:02 ` [PATCH v4 07/13] cmd_add: prepare for next patch Alexandr Miloslavskiy via GitGitGadget ` (7 subsequent siblings) 13 siblings, 0 replies; 80+ messages in thread From: Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 UTC (permalink / raw) To: git Cc: Junio C Hamano, Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> Decisions taken for simplicity: 1) For now, `--pathspec-from-file` is declared incompatible with `--interactive/--patch`, even when <file> is not `stdin`. Such use case it not really expected. Also, it would require changes to `interactive_add()`. 2) It is not allowed to pass pathspec in both args and file. Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> --- Documentation/git-commit.txt | 16 +++- builtin/commit.c | 25 +++++- t/t7526-commit-pathspec-file.sh | 130 ++++++++++++++++++++++++++++++++ 3 files changed, 166 insertions(+), 5 deletions(-) create mode 100755 t/t7526-commit-pathspec-file.sh diff --git a/Documentation/git-commit.txt b/Documentation/git-commit.txt index a0c44978ee..ced5a9beab 100644 --- a/Documentation/git-commit.txt +++ b/Documentation/git-commit.txt @@ -13,7 +13,8 @@ SYNOPSIS [-F <file> | -m <msg>] [--reset-author] [--allow-empty] [--allow-empty-message] [--no-verify] [-e] [--author=<author>] [--date=<date>] [--cleanup=<mode>] [--[no-]status] - [-i | -o] [-S[<keyid>]] [--] [<pathspec>...] + [-i | -o] [--pathspec-from-file=<file> [--pathspec-file-nul]] + [-S[<keyid>]] [--] [<pathspec>...] DESCRIPTION ----------- @@ -278,6 +279,19 @@ FROM UPSTREAM REBASE" section in linkgit:git-rebase[1].) already been staged. If used together with `--allow-empty` paths are also not required, and an empty commit will be created. +--pathspec-from-file=<file>:: + Pathspec is passed in `<file>` instead of commandline args. If + `<file>` is exactly `-` then standard input is used. Pathspec + elements are separated by LF or CR/LF. Pathspec elements can be + quoted as explained for the configuration variable `core.quotePath` + (see linkgit:git-config[1]). See also `--pathspec-file-nul` and + global `--literal-pathspecs`. + +--pathspec-file-nul:: + Only meaningful with `--pathspec-from-file`. Pathspec elements are + separated with NUL character and all other characters are taken + literally (including newlines and quotes). + -u[<mode>]:: --untracked-files[=<mode>]:: Show untracked files. diff --git a/builtin/commit.c b/builtin/commit.c index 294dc574cd..2db2ad0de4 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -107,9 +107,9 @@ static int all, also, interactive, patch_interactive, only, amend, signoff; static int edit_flag = -1; /* unspecified */ static int quiet, verbose, no_verify, allow_empty, dry_run, renew_authorship; static int config_commit_verbose = -1; /* unspecified */ -static int no_post_rewrite, allow_empty_message; +static int no_post_rewrite, allow_empty_message, pathspec_file_nul; static char *untracked_files_arg, *force_date, *ignore_submodule_arg, *ignored_arg; -static char *sign_commit; +static char *sign_commit, *pathspec_from_file; /* * The default commit message cleanup mode will remove the lines @@ -343,6 +343,23 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix PATHSPEC_PREFER_FULL, prefix, argv); + if (pathspec_from_file) { + if (interactive) + die(_("--pathspec-from-file is incompatible with --interactive/--patch")); + + if (pathspec.nr) + die(_("--pathspec-from-file is incompatible with pathspec arguments")); + + parse_pathspec_file(&pathspec, 0, + PATHSPEC_PREFER_FULL, + prefix, pathspec_from_file, pathspec_file_nul); + } else if (pathspec_file_nul) { + die(_("--pathspec-file-nul requires --pathspec-from-file")); + } + + if (!pathspec.nr && (also || (only && !amend && !allow_empty))) + die(_("No paths with --include/--only does not make sense.")); + if (read_cache_preload(&pathspec) < 0) die(_("index file corrupt")); @@ -1198,8 +1215,6 @@ static int parse_and_validate_options(int argc, const char *argv[], if (also + only + all + interactive > 1) die(_("Only one of --include/--only/--all/--interactive/--patch can be used.")); - if (argc == 0 && (also || (only && !amend && !allow_empty))) - die(_("No paths with --include/--only does not make sense.")); cleanup_mode = get_cleanup_mode(cleanup_arg, use_editor); handle_untracked_files_arg(s); @@ -1513,6 +1528,8 @@ int cmd_commit(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "amend", &amend, N_("amend previous commit")), OPT_BOOL(0, "no-post-rewrite", &no_post_rewrite, N_("bypass post-rewrite hook")), { OPTION_STRING, 'u', "untracked-files", &untracked_files_arg, N_("mode"), N_("show untracked files, optional modes: all, normal, no. (Default: all)"), PARSE_OPT_OPTARG, NULL, (intptr_t)"all" }, + OPT_PATHSPEC_FROM_FILE(&pathspec_from_file), + OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul), /* end commit contents options */ OPT_HIDDEN_BOOL(0, "allow-empty", &allow_empty, diff --git a/t/t7526-commit-pathspec-file.sh b/t/t7526-commit-pathspec-file.sh new file mode 100755 index 0000000000..a06b683534 --- /dev/null +++ b/t/t7526-commit-pathspec-file.sh @@ -0,0 +1,130 @@ +#!/bin/sh + +test_description='commit --pathspec-from-file' + +. ./test-lib.sh + +test_tick + +test_expect_success setup ' + test_commit file0 && + git tag checkpoint && + + echo A >fileA.t && + echo B >fileB.t && + echo C >fileC.t && + echo D >fileD.t && + git add fileA.t fileB.t fileC.t fileD.t +' + +restore_checkpoint () { + git reset --soft checkpoint +} + +verify_expect () { + git diff-tree --no-commit-id --name-status -r HEAD >actual && + test_cmp expect actual +} + +test_expect_success '--pathspec-from-file from stdin' ' + restore_checkpoint && + + echo fileA.t | git commit --pathspec-from-file=- -m "Commit" && + + cat >expect <<-\EOF && + A fileA.t + EOF + verify_expect +' + +test_expect_success '--pathspec-from-file from file' ' + restore_checkpoint && + + echo fileA.t >list && + git commit --pathspec-from-file=list -m "Commit" && + + cat >expect <<-\EOF && + A fileA.t + EOF + verify_expect +' + +test_expect_success 'NUL delimiters' ' + restore_checkpoint && + + printf "fileA.t\0fileB.t\0" | git commit --pathspec-from-file=- --pathspec-file-nul -m "Commit" && + + cat >expect <<-\EOF && + A fileA.t + A fileB.t + EOF + verify_expect +' + +test_expect_success 'LF delimiters' ' + restore_checkpoint && + + printf "fileA.t\nfileB.t\n" | git commit --pathspec-from-file=- -m "Commit" && + + cat >expect <<-\EOF && + A fileA.t + A fileB.t + EOF + verify_expect +' + +test_expect_success 'no trailing delimiter' ' + restore_checkpoint && + + printf "fileA.t\nfileB.t" | git commit --pathspec-from-file=- -m "Commit" && + + cat >expect <<-\EOF && + A fileA.t + A fileB.t + EOF + verify_expect +' + +test_expect_success 'CRLF delimiters' ' + restore_checkpoint && + + printf "fileA.t\r\nfileB.t\r\n" | git commit --pathspec-from-file=- -m "Commit" && + + cat >expect <<-\EOF && + A fileA.t + A fileB.t + EOF + verify_expect +' + +test_expect_success 'quotes' ' + restore_checkpoint && + + printf "\"file\\101.t\"" | git commit --pathspec-from-file=- -m "Commit" && + + cat >expect <<-\EOF && + A fileA.t + EOF + verify_expect expect +' + +test_expect_success 'quotes not compatible with --pathspec-file-nul' ' + restore_checkpoint && + + printf "\"file\\101.t\"" >list && + test_must_fail git commit --pathspec-from-file=list --pathspec-file-nul -m "Commit" +' + +test_expect_success 'only touches what was listed' ' + restore_checkpoint && + + printf "fileB.t\nfileC.t\n" | git commit --pathspec-from-file=- -m "Commit" && + + cat >expect <<-\EOF && + A fileB.t + A fileC.t + EOF + verify_expect +' + +test_done -- gitgitgadget ^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH v4 07/13] cmd_add: prepare for next patch 2019-12-03 14:02 ` [PATCH v4 00/13] Add --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget ` (5 preceding siblings ...) 2019-12-03 14:02 ` [PATCH v4 06/13] commit: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 ` Alexandr Miloslavskiy via GitGitGadget 2019-12-03 14:02 ` [PATCH v4 08/13] add: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget ` (6 subsequent siblings) 13 siblings, 0 replies; 80+ messages in thread From: Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 UTC (permalink / raw) To: git Cc: Junio C Hamano, Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> Some code blocks were moved down to be able to test for `pathspec.nr` in the next patch. Blocks are moved as is without any changes. This is done as separate patch to reduce the amount of diffs in next patch. Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> --- builtin/add.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/builtin/add.c b/builtin/add.c index dd18e5c9b6..4fabdc72e6 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -418,10 +418,6 @@ int cmd_add(int argc, const char **argv, const char *prefix) if (addremove && take_worktree_changes) die(_("-A and -u are mutually incompatible")); - if (!take_worktree_changes && addremove_explicit < 0 && argc) - /* Turn "git add pathspec..." to "git add -A pathspec..." */ - addremove = 1; - if (!show_only && ignore_missing) die(_("Option --ignore-missing can only be used together with --dry-run")); @@ -434,19 +430,6 @@ int cmd_add(int argc, const char **argv, const char *prefix) hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR); - flags = ((verbose ? ADD_CACHE_VERBOSE : 0) | - (show_only ? ADD_CACHE_PRETEND : 0) | - (intent_to_add ? ADD_CACHE_INTENT : 0) | - (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) | - (!(addremove || take_worktree_changes) - ? ADD_CACHE_IGNORE_REMOVAL : 0)); - - if (require_pathspec && argc == 0) { - fprintf(stderr, _("Nothing specified, nothing added.\n")); - fprintf(stderr, _("Maybe you wanted to say 'git add .'?\n")); - return 0; - } - /* * Check the "pathspec '%s' did not match any files" block * below before enabling new magic. @@ -456,6 +439,23 @@ int cmd_add(int argc, const char **argv, const char *prefix) PATHSPEC_SYMLINK_LEADING_PATH, prefix, argv); + if (require_pathspec && argc == 0) { + fprintf(stderr, _("Nothing specified, nothing added.\n")); + fprintf(stderr, _("Maybe you wanted to say 'git add .'?\n")); + return 0; + } + + if (!take_worktree_changes && addremove_explicit < 0 && argc) + /* Turn "git add pathspec..." to "git add -A pathspec..." */ + addremove = 1; + + flags = ((verbose ? ADD_CACHE_VERBOSE : 0) | + (show_only ? ADD_CACHE_PRETEND : 0) | + (intent_to_add ? ADD_CACHE_INTENT : 0) | + (ignore_add_errors ? ADD_CACHE_IGNORE_ERRORS : 0) | + (!(addremove || take_worktree_changes) + ? ADD_CACHE_IGNORE_REMOVAL : 0)); + if (read_cache_preload(&pathspec) < 0) die(_("index file corrupt")); -- gitgitgadget ^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH v4 08/13] add: support the --pathspec-from-file option 2019-12-03 14:02 ` [PATCH v4 00/13] Add --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget ` (6 preceding siblings ...) 2019-12-03 14:02 ` [PATCH v4 07/13] cmd_add: prepare for next patch Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 ` Alexandr Miloslavskiy via GitGitGadget 2019-12-03 14:02 ` [PATCH v4 09/13] doc: checkout: remove duplicate synopsis Alexandr Miloslavskiy via GitGitGadget ` (5 subsequent siblings) 13 siblings, 0 replies; 80+ messages in thread From: Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 UTC (permalink / raw) To: git Cc: Junio C Hamano, Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> Decisions taken for simplicity: 1) For now, `--pathspec-from-file` is declared incompatible with `--interactive/--patch/--edit`, even when <file> is not `stdin`. Such use case it not really expected. Also, it would require changes to `interactive_add()` and `edit_patch()`. 2) It is not allowed to pass pathspec in both args and file. Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> --- Documentation/git-add.txt | 16 ++++- builtin/add.c | 30 +++++++-- t/t3704-add-pathspec-file.sh | 127 +++++++++++++++++++++++++++++++++++ 3 files changed, 168 insertions(+), 5 deletions(-) create mode 100755 t/t3704-add-pathspec-file.sh diff --git a/Documentation/git-add.txt b/Documentation/git-add.txt index 8b0e4c7fa8..be5e3ac54b 100644 --- a/Documentation/git-add.txt +++ b/Documentation/git-add.txt @@ -11,7 +11,8 @@ SYNOPSIS 'git add' [--verbose | -v] [--dry-run | -n] [--force | -f] [--interactive | -i] [--patch | -p] [--edit | -e] [--[no-]all | --[no-]ignore-removal | [--update | -u]] [--intent-to-add | -N] [--refresh] [--ignore-errors] [--ignore-missing] [--renormalize] - [--chmod=(+|-)x] [--] [<pathspec>...] + [--chmod=(+|-)x] [--pathspec-from-file=<file> [--pathspec-file-nul]] + [--] [<pathspec>...] DESCRIPTION ----------- @@ -187,6 +188,19 @@ for "git add --no-all <pathspec>...", i.e. ignored removed files. bit is only changed in the index, the files on disk are left unchanged. +--pathspec-from-file=<file>:: + Pathspec is passed in `<file>` instead of commandline args. If + `<file>` is exactly `-` then standard input is used. Pathspec + elements are separated by LF or CR/LF. Pathspec elements can be + quoted as explained for the configuration variable `core.quotePath` + (see linkgit:git-config[1]). See also `--pathspec-file-nul` and + global `--literal-pathspecs`. + +--pathspec-file-nul:: + Only meaningful with `--pathspec-from-file`. Pathspec elements are + separated with NUL character and all other characters are taken + literally (including newlines and quotes). + \--:: This option can be used to separate command-line options from the list of files, (useful when filenames might be mistaken diff --git a/builtin/add.c b/builtin/add.c index 4fabdc72e6..9f6b263aba 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -28,6 +28,8 @@ static const char * const builtin_add_usage[] = { static int patch_interactive, add_interactive, edit_interactive; static int take_worktree_changes; static int add_renormalize; +static int pathspec_file_nul; +static const char *pathspec_from_file; struct update_callback_data { int flags; @@ -309,6 +311,8 @@ static struct option builtin_add_options[] = { N_("override the executable bit of the listed files")), OPT_HIDDEN_BOOL(0, "warn-embedded-repo", &warn_on_embedded_repo, N_("warn when adding an embedded repository")), + OPT_PATHSPEC_FROM_FILE(&pathspec_from_file), + OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul), OPT_END(), }; @@ -402,11 +406,17 @@ int cmd_add(int argc, const char **argv, const char *prefix) builtin_add_usage, PARSE_OPT_KEEP_ARGV0); if (patch_interactive) add_interactive = 1; - if (add_interactive) + if (add_interactive) { + if (pathspec_from_file) + die(_("--pathspec-from-file is incompatible with --interactive/--patch")); exit(interactive_add(argc - 1, argv + 1, prefix, patch_interactive)); + } - if (edit_interactive) + if (edit_interactive) { + if (pathspec_from_file) + die(_("--pathspec-from-file is incompatible with --edit")); return(edit_patch(argc, argv, prefix)); + } argc--; argv++; @@ -439,13 +449,25 @@ int cmd_add(int argc, const char **argv, const char *prefix) PATHSPEC_SYMLINK_LEADING_PATH, prefix, argv); - if (require_pathspec && argc == 0) { + if (pathspec_from_file) { + if (pathspec.nr) + die(_("--pathspec-from-file is incompatible with pathspec arguments")); + + parse_pathspec_file(&pathspec, PATHSPEC_ATTR, + PATHSPEC_PREFER_FULL | + PATHSPEC_SYMLINK_LEADING_PATH, + prefix, pathspec_from_file, pathspec_file_nul); + } else if (pathspec_file_nul) { + die(_("--pathspec-file-nul requires --pathspec-from-file")); + } + + if (require_pathspec && pathspec.nr == 0) { fprintf(stderr, _("Nothing specified, nothing added.\n")); fprintf(stderr, _("Maybe you wanted to say 'git add .'?\n")); return 0; } - if (!take_worktree_changes && addremove_explicit < 0 && argc) + if (!take_worktree_changes && addremove_explicit < 0 && pathspec.nr) /* Turn "git add pathspec..." to "git add -A pathspec..." */ addremove = 1; diff --git a/t/t3704-add-pathspec-file.sh b/t/t3704-add-pathspec-file.sh new file mode 100755 index 0000000000..3cfdb669b7 --- /dev/null +++ b/t/t3704-add-pathspec-file.sh @@ -0,0 +1,127 @@ +#!/bin/sh + +test_description='add --pathspec-from-file' + +. ./test-lib.sh + +test_tick + +test_expect_success setup ' + test_commit file0 && + echo A >fileA.t && + echo B >fileB.t && + echo C >fileC.t && + echo D >fileD.t +' + +restore_checkpoint () { + git reset +} + +verify_expect () { + git status --porcelain --untracked-files=no -- fileA.t fileB.t fileC.t fileD.t >actual && + test_cmp expect actual +} + +test_expect_success '--pathspec-from-file from stdin' ' + restore_checkpoint && + + echo fileA.t | git add --pathspec-from-file=- && + + cat >expect <<-\EOF && + A fileA.t + EOF + verify_expect +' + +test_expect_success '--pathspec-from-file from file' ' + restore_checkpoint && + + echo fileA.t >list && + git add --pathspec-from-file=list && + + cat >expect <<-\EOF && + A fileA.t + EOF + verify_expect +' + +test_expect_success 'NUL delimiters' ' + restore_checkpoint && + + printf "fileA.t\0fileB.t\0" | git add --pathspec-from-file=- --pathspec-file-nul && + + cat >expect <<-\EOF && + A fileA.t + A fileB.t + EOF + verify_expect +' + +test_expect_success 'LF delimiters' ' + restore_checkpoint && + + printf "fileA.t\nfileB.t\n" | git add --pathspec-from-file=- && + + cat >expect <<-\EOF && + A fileA.t + A fileB.t + EOF + verify_expect +' + +test_expect_success 'no trailing delimiter' ' + restore_checkpoint && + + printf "fileA.t\nfileB.t" | git add --pathspec-from-file=- && + + cat >expect <<-\EOF && + A fileA.t + A fileB.t + EOF + verify_expect +' + +test_expect_success 'CRLF delimiters' ' + restore_checkpoint && + + printf "fileA.t\r\nfileB.t\r\n" | git add --pathspec-from-file=- && + + cat >expect <<-\EOF && + A fileA.t + A fileB.t + EOF + verify_expect +' + +test_expect_success 'quotes' ' + restore_checkpoint && + + printf "\"file\\101.t\"" | git add --pathspec-from-file=- && + + cat >expect <<-\EOF && + A fileA.t + EOF + verify_expect +' + +test_expect_success 'quotes not compatible with --pathspec-file-nul' ' + restore_checkpoint && + + printf "\"file\\101.t\"" >list && + test_must_fail git add --pathspec-from-file=list --pathspec-file-nul +' + +test_expect_success 'only touches what was listed' ' + restore_checkpoint && + + printf "fileB.t\nfileC.t\n" | git add --pathspec-from-file=- && + + cat >expect <<-\EOF && + A fileB.t + A fileC.t + EOF + verify_expect +' + +test_done -- gitgitgadget ^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH v4 09/13] doc: checkout: remove duplicate synopsis 2019-12-03 14:02 ` [PATCH v4 00/13] Add --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget ` (7 preceding siblings ...) 2019-12-03 14:02 ` [PATCH v4 08/13] add: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 ` Alexandr Miloslavskiy via GitGitGadget 2019-12-03 14:02 ` [PATCH v4 10/13] doc: checkout: fix broken text reference Alexandr Miloslavskiy via GitGitGadget ` (4 subsequent siblings) 13 siblings, 0 replies; 80+ messages in thread From: Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 UTC (permalink / raw) To: git Cc: Junio C Hamano, Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> It was added in [1]. I understand that the duplicate change was not intentional and comes from an oversight. Also, in explanation, there was only one section for two synopsis entries. Fix both problems by removing duplicate synopsis. <paths> vs <pathspec> is resolved in next patch. [1] Commit b59698ae ("checkout doc: clarify command line args for "checkout paths" mode" 2017-10-11) Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> --- Documentation/git-checkout.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index cf3cac0a2b..2011fdbb1d 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -13,7 +13,6 @@ SYNOPSIS 'git checkout' [-q] [-f] [-m] [--detach] <commit> 'git checkout' [-q] [-f] [-m] [[-b|-B|--orphan] <new_branch>] [<start_point>] 'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>... -'git checkout' [<tree-ish>] [--] <pathspec>... 'git checkout' (-p|--patch) [<tree-ish>] [--] [<paths>...] DESCRIPTION @@ -79,7 +78,7 @@ be used to detach `HEAD` at the tip of the branch (`git checkout + Omitting `<branch>` detaches `HEAD` at the tip of the current branch. -'git checkout' [<tree-ish>] [--] <pathspec>...:: +'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>...:: Overwrite paths in the working tree by replacing with the contents in the index or in the `<tree-ish>` (most often a -- gitgitgadget ^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH v4 10/13] doc: checkout: fix broken text reference 2019-12-03 14:02 ` [PATCH v4 00/13] Add --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget ` (8 preceding siblings ...) 2019-12-03 14:02 ` [PATCH v4 09/13] doc: checkout: remove duplicate synopsis Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 ` Alexandr Miloslavskiy via GitGitGadget 2019-12-03 14:02 ` [PATCH v4 11/13] doc: checkout: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget ` (3 subsequent siblings) 13 siblings, 0 replies; 80+ messages in thread From: Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 UTC (permalink / raw) To: git Cc: Junio C Hamano, Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> --- Documentation/git-checkout.txt | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index 2011fdbb1d..d47046e050 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -95,12 +95,10 @@ using `--ours` or `--theirs`. With `-m`, changes made to the working tree file can be discarded to re-create the original conflicted merge result. 'git checkout' (-p|--patch) [<tree-ish>] [--] [<pathspec>...]:: - This is similar to the "check out paths to the working tree - from either the index or from a tree-ish" mode described - above, but lets you use the interactive interface to show - the "diff" output and choose which hunks to use in the - result. See below for the description of `--patch` option. - + This is similar to the previous mode, but lets you use the + interactive interface to show the "diff" output and choose which + hunks to use in the result. See below for the description of + `--patch` option. OPTIONS ------- -- gitgitgadget ^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH v4 11/13] doc: checkout: synchronize <pathspec> description 2019-12-03 14:02 ` [PATCH v4 00/13] Add --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget ` (9 preceding siblings ...) 2019-12-03 14:02 ` [PATCH v4 10/13] doc: checkout: fix broken text reference Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 ` Alexandr Miloslavskiy via GitGitGadget 2019-12-03 14:02 ` [PATCH v4 12/13] doc: restore: " Alexandr Miloslavskiy via GitGitGadget ` (2 subsequent siblings) 13 siblings, 0 replies; 80+ messages in thread From: Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 UTC (permalink / raw) To: git Cc: Junio C Hamano, Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> `git add` shows an example of good writing, follow it. Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> --- Documentation/git-checkout.txt | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index d47046e050..93124f3ad9 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -12,13 +12,13 @@ SYNOPSIS 'git checkout' [-q] [-f] [-m] --detach [<branch>] 'git checkout' [-q] [-f] [-m] [--detach] <commit> 'git checkout' [-q] [-f] [-m] [[-b|-B|--orphan] <new_branch>] [<start_point>] -'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>... -'git checkout' (-p|--patch) [<tree-ish>] [--] [<paths>...] +'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <pathspec>... +'git checkout' (-p|--patch) [<tree-ish>] [--] [<pathspec>...] DESCRIPTION ----------- Updates files in the working tree to match the version in the index -or the specified tree. If no paths are given, 'git checkout' will +or the specified tree. If no pathspec was given, 'git checkout' will also update `HEAD` to set the specified branch as the current branch. @@ -78,13 +78,13 @@ be used to detach `HEAD` at the tip of the branch (`git checkout + Omitting `<branch>` detaches `HEAD` at the tip of the current branch. -'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <paths>...:: +'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <pathspec>...:: - Overwrite paths in the working tree by replacing with the - contents in the index or in the `<tree-ish>` (most often a - commit). When a `<tree-ish>` is given, the paths that - match the `<pathspec>` are updated both in the index and in - the working tree. + Overwrite the contents of the files that match the pathspec. + When the `<tree-ish>` (most often a commit) is not given, + overwrite working tree with the contents in the index. + When the `<tree-ish>` is given, overwrite both the index and + the working tree with the contents at the `<tree-ish>`. + The index may contain unmerged entries because of a previous failed merge. By default, if you try to check out such an entry from the index, the @@ -336,7 +336,13 @@ leave out at most one of `A` and `B`, in which case it defaults to `HEAD`. Tree to checkout from (when paths are given). If not specified, the index will be used. +\--:: + Do not interpret any more arguments as options. +<pathspec>...:: + Limits the paths affected by the operation. ++ +For more details, see the 'pathspec' entry in linkgit:gitglossary[7]. DETACHED HEAD ------------- -- gitgitgadget ^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH v4 12/13] doc: restore: synchronize <pathspec> description 2019-12-03 14:02 ` [PATCH v4 00/13] Add --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget ` (10 preceding siblings ...) 2019-12-03 14:02 ` [PATCH v4 11/13] doc: checkout: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 ` Alexandr Miloslavskiy via GitGitGadget 2019-12-03 14:02 ` [PATCH v4 13/13] checkout, restore: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget 2019-12-03 16:55 ` [PATCH v4 00/13] Add " Junio C Hamano 13 siblings, 0 replies; 80+ messages in thread From: Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 UTC (permalink / raw) To: git Cc: Junio C Hamano, Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> `git add` shows an example of good writing, follow it. Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> --- Documentation/git-restore.txt | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Documentation/git-restore.txt b/Documentation/git-restore.txt index 1ab2e40ea9..d7bf016bba 100644 --- a/Documentation/git-restore.txt +++ b/Documentation/git-restore.txt @@ -8,8 +8,8 @@ git-restore - Restore working tree files SYNOPSIS -------- [verse] -'git restore' [<options>] [--source=<tree>] [--staged] [--worktree] <pathspec>... -'git restore' (-p|--patch) [<options>] [--source=<tree>] [--staged] [--worktree] [<pathspec>...] +'git restore' [<options>] [--source=<tree>] [--staged] [--worktree] [--] <pathspec>... +'git restore' (-p|--patch) [<options>] [--source=<tree>] [--staged] [--worktree] [--] [<pathspec>...] DESCRIPTION ----------- @@ -113,6 +113,14 @@ in linkgit:git-checkout[1] for details. appear in the `--source` tree are removed, to make them match `<tree>` exactly. The default is no-overlay mode. +\--:: + Do not interpret any more arguments as options. + +<pathspec>...:: + Limits the paths affected by the operation. ++ +For more details, see the 'pathspec' entry in linkgit:gitglossary[7]. + EXAMPLES -------- -- gitgitgadget ^ permalink raw reply related [flat|nested] 80+ messages in thread
* [PATCH v4 13/13] checkout, restore: support the --pathspec-from-file option 2019-12-03 14:02 ` [PATCH v4 00/13] Add --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget ` (11 preceding siblings ...) 2019-12-03 14:02 ` [PATCH v4 12/13] doc: restore: " Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 ` Alexandr Miloslavskiy via GitGitGadget 2019-12-03 16:55 ` [PATCH v4 00/13] Add " Junio C Hamano 13 siblings, 0 replies; 80+ messages in thread From: Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 14:02 UTC (permalink / raw) To: git Cc: Junio C Hamano, Alexandr Miloslavskiy, Junio C Hamano, Alexandr Miloslavskiy From: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> Decisions taken for simplicity: 1) For now, `--pathspec-from-file` is declared incompatible with `--patch`, even when <file> is not `stdin`. Such use case it not really expected. 2) It is not allowed to pass pathspec in both args and file. `you must specify path(s) to restore` block was moved down to be able to test for `pathspec.nr` instead, because testing for `argc` is no longer correct. `git switch` does not support the new options because it doesn't expect `<pathspec>` arguments. Signed-off-by: Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> --- Documentation/git-checkout.txt | 15 ++++ Documentation/git-restore.txt | 14 +++ builtin/checkout.c | 31 ++++++- t/t2026-checkout-pathspec-file.sh | 139 ++++++++++++++++++++++++++++++ t/t2072-restore-pathspec-file.sh | 139 ++++++++++++++++++++++++++++++ t/t9902-completion.sh | 2 + 6 files changed, 336 insertions(+), 4 deletions(-) create mode 100755 t/t2026-checkout-pathspec-file.sh create mode 100755 t/t2072-restore-pathspec-file.sh diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index 93124f3ad9..ffe3c1bff2 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -13,6 +13,7 @@ SYNOPSIS 'git checkout' [-q] [-f] [-m] [--detach] <commit> 'git checkout' [-q] [-f] [-m] [[-b|-B|--orphan] <new_branch>] [<start_point>] 'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <pathspec>... +'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] --pathspec-from-file=<file> [--pathspec-file-nul] 'git checkout' (-p|--patch) [<tree-ish>] [--] [<pathspec>...] DESCRIPTION @@ -79,6 +80,7 @@ be used to detach `HEAD` at the tip of the branch (`git checkout Omitting `<branch>` detaches `HEAD` at the tip of the current branch. 'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <pathspec>...:: +'git checkout' [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] --pathspec-from-file=<file> [--pathspec-file-nul]:: Overwrite the contents of the files that match the pathspec. When the `<tree-ish>` (most often a commit) is not given, @@ -306,6 +308,19 @@ Note that this option uses the no overlay mode by default (see also working tree, but not in `<tree-ish>` are removed, to make them match `<tree-ish>` exactly. +--pathspec-from-file=<file>:: + Pathspec is passed in `<file>` instead of commandline args. If + `<file>` is exactly `-` then standard input is used. Pathspec + elements are separated by LF or CR/LF. Pathspec elements can be + quoted as explained for the configuration variable `core.quotePath` + (see linkgit:git-config[1]). See also `--pathspec-file-nul` and + global `--literal-pathspecs`. + +--pathspec-file-nul:: + Only meaningful with `--pathspec-from-file`. Pathspec elements are + separated with NUL character and all other characters are taken + literally (including newlines and quotes). + <branch>:: Branch to checkout; if it refers to a branch (i.e., a name that, when prepended with "refs/heads/", is a valid ref), then that diff --git a/Documentation/git-restore.txt b/Documentation/git-restore.txt index d7bf016bba..5bf60d4943 100644 --- a/Documentation/git-restore.txt +++ b/Documentation/git-restore.txt @@ -9,6 +9,7 @@ SYNOPSIS -------- [verse] 'git restore' [<options>] [--source=<tree>] [--staged] [--worktree] [--] <pathspec>... +'git restore' [<options>] [--source=<tree>] [--staged] [--worktree] --pathspec-from-file=<file> [--pathspec-file-nul] 'git restore' (-p|--patch) [<options>] [--source=<tree>] [--staged] [--worktree] [--] [<pathspec>...] DESCRIPTION @@ -113,6 +114,19 @@ in linkgit:git-checkout[1] for details. appear in the `--source` tree are removed, to make them match `<tree>` exactly. The default is no-overlay mode. +--pathspec-from-file=<file>:: + Pathspec is passed in `<file>` instead of commandline args. If + `<file>` is exactly `-` then standard input is used. Pathspec + elements are separated by LF or CR/LF. Pathspec elements can be + quoted as explained for the configuration variable `core.quotePath` + (see linkgit:git-config[1]). See also `--pathspec-file-nul` and + global `--literal-pathspecs`. + +--pathspec-file-nul:: + Only meaningful with `--pathspec-from-file`. Pathspec elements are + separated with NUL character and all other characters are taken + literally (including newlines and quotes). + \--:: Do not interpret any more arguments as options. diff --git a/builtin/checkout.c b/builtin/checkout.c index 3634a3dac1..b52c490c8f 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -70,6 +70,8 @@ struct checkout_opts { int checkout_worktree; const char *ignore_unmerged_opt; int ignore_unmerged; + int pathspec_file_nul; + const char *pathspec_from_file; const char *new_branch; const char *new_branch_force; @@ -1480,6 +1482,8 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts, OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")), OPT_BOOL(0, "ignore-skip-worktree-bits", &opts->ignore_skipworktree, N_("do not limit pathspecs to sparse entries only")), + OPT_PATHSPEC_FROM_FILE(&opts->pathspec_from_file), + OPT_PATHSPEC_FILE_NUL(&opts->pathspec_file_nul), OPT_END() }; struct option *newopts = parse_options_concat(prevopts, options); @@ -1618,10 +1622,6 @@ static int checkout_main(int argc, const char **argv, const char *prefix, die(_("reference is not a tree: %s"), opts->from_treeish); } - if (opts->accept_pathspec && !opts->empty_pathspec_ok && !argc && - !opts->patch_mode) /* patch mode is special */ - die(_("you must specify path(s) to restore")); - if (argc) { parse_pathspec(&opts->pathspec, 0, opts->patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0, @@ -1641,10 +1641,33 @@ static int checkout_main(int argc, const char **argv, const char *prefix, if (opts->force_detach) die(_("git checkout: --detach does not take a path argument '%s'"), argv[0]); + } + + if (opts->pathspec_from_file) { + if (opts->pathspec.nr) + die(_("--pathspec-from-file is incompatible with pathspec arguments")); + + if (opts->force_detach) + die(_("--pathspec-from-file is incompatible with --detach")); + if (opts->patch_mode) + die(_("--pathspec-from-file is incompatible with --patch")); + + parse_pathspec_file(&opts->pathspec, 0, + 0, + prefix, opts->pathspec_from_file, opts->pathspec_file_nul); + } else if (opts->pathspec_file_nul) { + die(_("--pathspec-file-nul requires --pathspec-from-file")); + } + + if (opts->pathspec.nr) { if (1 < !!opts->writeout_stage + !!opts->force + !!opts->merge) die(_("git checkout: --ours/--theirs, --force and --merge are incompatible when\n" "checking out of the index.")); + } else { + if (opts->accept_pathspec && !opts->empty_pathspec_ok && + !opts->patch_mode) /* patch mode is special */ + die(_("you must specify path(s) to restore")); } if (opts->new_branch) { diff --git a/t/t2026-checkout-pathspec-file.sh b/t/t2026-checkout-pathspec-file.sh new file mode 100755 index 0000000000..f62fd27440 --- /dev/null +++ b/t/t2026-checkout-pathspec-file.sh @@ -0,0 +1,139 @@ +#!/bin/sh + +test_description='checkout --pathspec-from-file' + +. ./test-lib.sh + +test_tick + +test_expect_success setup ' + test_commit file0 && + + echo 1 >fileA.t && + echo 1 >fileB.t && + echo 1 >fileC.t && + echo 1 >fileD.t && + git add fileA.t fileB.t fileC.t fileD.t && + git commit -m "files 1" && + + echo 2 >fileA.t && + echo 2 >fileB.t && + echo 2 >fileC.t && + echo 2 >fileD.t && + git add fileA.t fileB.t fileC.t fileD.t && + git commit -m "files 2" && + + git tag checkpoint +' + +restore_checkpoint () { + git reset --hard checkpoint +} + +verify_expect () { + git status --porcelain --untracked-files=no -- fileA.t fileB.t fileC.t fileD.t >actual && + test_cmp expect actual +} + +test_expect_success '--pathspec-from-file from stdin' ' + restore_checkpoint && + + echo fileA.t | git checkout --pathspec-from-file=- HEAD^1 && + + cat >expect <<-\EOF && + M fileA.t + EOF + verify_expect +' + +test_expect_success '--pathspec-from-file from file' ' + restore_checkpoint && + + echo fileA.t >list && + git checkout --pathspec-from-file=list HEAD^1 && + + cat >expect <<-\EOF && + M fileA.t + EOF + verify_expect +' + +test_expect_success 'NUL delimiters' ' + restore_checkpoint && + + printf "fileA.t\0fileB.t\0" | git checkout --pathspec-from-file=- --pathspec-file-nul HEAD^1 && + + cat >expect <<-\EOF && + M fileA.t + M fileB.t + EOF + verify_expect +' + +test_expect_success 'LF delimiters' ' + restore_checkpoint && + + printf "fileA.t\nfileB.t\n" | git checkout --pathspec-from-file=- HEAD^1 && + + cat >expect <<-\EOF && + M fileA.t + M fileB.t + EOF + verify_expect +' + +test_expect_success 'no trailing delimiter' ' + restore_checkpoint && + + printf "fileA.t\nfileB.t" | git checkout --pathspec-from-file=- HEAD^1 && + + cat >expect <<-\EOF && + M fileA.t + M fileB.t + EOF + verify_expect +' + +test_expect_success 'CRLF delimiters' ' + restore_checkpoint && + + printf "fileA.t\r\nfileB.t\r\n" | git checkout --pathspec-from-file=- HEAD^1 && + + cat >expect <<-\EOF && + M fileA.t + M fileB.t + EOF + verify_expect +' + +test_expect_success 'quotes' ' + restore_checkpoint && + + printf "\"file\\101.t\"" | git checkout --pathspec-from-file=- HEAD^1 && + + cat >expect <<-\EOF && + M fileA.t + EOF + verify_expect +' + +test_expect_success 'quotes not compatible with --pathspec-file-nul' ' + restore_checkpoint && + + printf "\"file\\101.t\"" >list && + test_must_fail git checkout --pathspec-from-file=list --pathspec-file-nul HEAD^1 +' + +test_expect_success 'only touches what was listed' ' + restore_checkpoint && + + printf "fileB.t\nfileC.t\n" | git checkout --pathspec-from-file=- HEAD^1 && + + cat >expect <<-\EOF && + M fileB.t + M fileC.t + EOF + verify_expect +' + +test_done diff --git a/t/t2072-restore-pathspec-file.sh b/t/t2072-restore-pathspec-file.sh new file mode 100755 index 0000000000..db58e83735 --- /dev/null +++ b/t/t2072-restore-pathspec-file.sh @@ -0,0 +1,139 @@ +#!/bin/sh + +test_description='restore --pathspec-from-file' + +. ./test-lib.sh + +test_tick + +test_expect_success setup ' + test_commit file0 && + + echo 1 >fileA.t && + echo 1 >fileB.t && + echo 1 >fileC.t && + echo 1 >fileD.t && + git add fileA.t fileB.t fileC.t fileD.t && + git commit -m "files 1" && + + echo 2 >fileA.t && + echo 2 >fileB.t && + echo 2 >fileC.t && + echo 2 >fileD.t && + git add fileA.t fileB.t fileC.t fileD.t && + git commit -m "files 2" && + + git tag checkpoint +' + +restore_checkpoint () { + git reset --hard checkpoint +} + +verify_expect () { + git status --porcelain --untracked-files=no -- fileA.t fileB.t fileC.t fileD.t >actual && + test_cmp expect actual +} + +test_expect_success '--pathspec-from-file from stdin' ' + restore_checkpoint && + + echo fileA.t | git restore --pathspec-from-file=- --source=HEAD^1 && + + cat >expect <<-\EOF && + M fileA.t + EOF + verify_expect +' + +test_expect_success '--pathspec-from-file from file' ' + restore_checkpoint && + + echo fileA.t >list && + git restore --pathspec-from-file=list --source=HEAD^1 && + + cat >expect <<-\EOF && + M fileA.t + EOF + verify_expect +' + +test_expect_success 'NUL delimiters' ' + restore_checkpoint && + + printf "fileA.t\0fileB.t\0" | git restore --pathspec-from-file=- --pathspec-file-nul --source=HEAD^1 && + + cat >expect <<-\EOF && + M fileA.t + M fileB.t + EOF + verify_expect +' + +test_expect_success 'LF delimiters' ' + restore_checkpoint && + + printf "fileA.t\nfileB.t\n" | git restore --pathspec-from-file=- --source=HEAD^1 && + + cat >expect <<-\EOF && + M fileA.t + M fileB.t + EOF + verify_expect +' + +test_expect_success 'no trailing delimiter' ' + restore_checkpoint && + + printf "fileA.t\nfileB.t" | git restore --pathspec-from-file=- --source=HEAD^1 && + + cat >expect <<-\EOF && + M fileA.t + M fileB.t + EOF + verify_expect +' + +test_expect_success 'CRLF delimiters' ' + restore_checkpoint && + + printf "fileA.t\r\nfileB.t\r\n" | git restore --pathspec-from-file=- --source=HEAD^1 && + + cat >expect <<-\EOF && + M fileA.t + M fileB.t + EOF + verify_expect +' + +test_expect_success 'quotes' ' + restore_checkpoint && + + printf "\"file\\101.t\"" | git restore --pathspec-from-file=- --source=HEAD^1 && + + cat >expect <<-\EOF && + M fileA.t + EOF + verify_expect +' + +test_expect_success 'quotes not compatible with --pathspec-file-nul' ' + restore_checkpoint && + + printf "\"file\\101.t\"" >list && + test_must_fail git restore --pathspec-from-file=list --pathspec-file-nul --source=HEAD^1 +' + +test_expect_success 'only touches what was listed' ' + restore_checkpoint && + + printf "fileB.t\nfileC.t\n" | git restore --pathspec-from-file=- --source=HEAD^1 && + + cat >expect <<-\EOF && + M fileB.t + M fileC.t + EOF + verify_expect +' + +test_done diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh index ec3eccfd3d..93877ba9cd 100755 --- a/t/t9902-completion.sh +++ b/t/t9902-completion.sh @@ -1438,6 +1438,8 @@ test_expect_success 'double dash "git checkout"' ' --no-guess Z --no-... Z --overlay Z + --pathspec-file-nul Z + --pathspec-from-file=Z EOF ' -- gitgitgadget ^ permalink raw reply related [flat|nested] 80+ messages in thread
* Re: [PATCH v4 00/13] Add --pathspec-from-file option 2019-12-03 14:02 ` [PATCH v4 00/13] Add --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget ` (12 preceding siblings ...) 2019-12-03 14:02 ` [PATCH v4 13/13] checkout, restore: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget @ 2019-12-03 16:55 ` Junio C Hamano 2019-12-03 17:06 ` Alexandr Miloslavskiy 13 siblings, 1 reply; 80+ messages in thread From: Junio C Hamano @ 2019-12-03 16:55 UTC (permalink / raw) To: Alexandr Miloslavskiy via GitGitGadget; +Cc: git, Alexandr Miloslavskiy "Alexandr Miloslavskiy via GitGitGadget" <gitgitgadget@gmail.com> writes: > Changes from V3: Yikes, perhaps our mails crossed or something? I think the previous round is already in 'next'. Let's wait and see they cook enough to graduate to 'master', and build a separate series on top to teach other commands the option using the facility introduced by the current series (which is the first 6 patches you sent here). Thanks. ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH v4 00/13] Add --pathspec-from-file option 2019-12-03 16:55 ` [PATCH v4 00/13] Add " Junio C Hamano @ 2019-12-03 17:06 ` Alexandr Miloslavskiy 2019-12-04 19:25 ` Junio C Hamano 0 siblings, 1 reply; 80+ messages in thread From: Alexandr Miloslavskiy @ 2019-12-03 17:06 UTC (permalink / raw) To: Junio C Hamano, Alexandr Miloslavskiy via GitGitGadget; +Cc: git On 03.12.2019 17:55, Junio C Hamano wrote: >> Changes from V3: > > Yikes, perhaps our mails crossed or something? I think the previous > round is already in 'next'. > > Let's wait and see they cook enough to graduate to 'master', and > build a separate series on top to teach other commands the option > using the facility introduced by the current series (which is the > first 6 patches you sent here). My intent is to support more commands, so I was working on other patches in the background. Today more patches were ready and I wasn't sure whether to submit another topic or continue the old one. After some thinking, I decided to continue the old one. Apparently, I guessed wrong :) Roger: I will wait until the first part graduates to 'master', then submit more patches. Please give me an advice: when the time comes, shall I prepare even more patches and submit a massive branch, or shall I submit today's remaining patches, then wait again? I imagine that massive branches are scary and will deter reviewers? ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH v4 00/13] Add --pathspec-from-file option 2019-12-03 17:06 ` Alexandr Miloslavskiy @ 2019-12-04 19:25 ` Junio C Hamano 2019-12-05 10:43 ` Alexandr Miloslavskiy 0 siblings, 1 reply; 80+ messages in thread From: Junio C Hamano @ 2019-12-04 19:25 UTC (permalink / raw) To: Alexandr Miloslavskiy; +Cc: Alexandr Miloslavskiy via GitGitGadget, git Alexandr Miloslavskiy <alexandr.miloslavskiy@syntevo.com> writes: > On 03.12.2019 17:55, Junio C Hamano wrote: > >>> Changes from V3: >> >> Yikes, perhaps our mails crossed or something? I think the previous >> round is already in 'next'. >> >> Let's wait and see they cook enough to graduate to 'master', and >> build a separate series on top to teach other commands the option >> using the facility introduced by the current series (which is the >> first 6 patches you sent here). > > My intent is to support more commands, so I was working on other > patches in the background. Today more patches were ready and I wasn't > sure whether to submit another topic or continue the old one. After > some thinking, I decided to continue the old one. Well I've split the new patches into its own topic to queue on the am/pathspec-f-f-checkout branch, that builds directly on top of the am/pathspec-from-file branch, for now. I suspect that they may want to be two topics (i.e. for "add" and for "checkout/restore"), but I'd like to keep them out of 'next' either way for a while until the base topic proves to be solid enough. > Please give me an advice: when the time comes, shall I prepare even > more patches and submit a massive branch, or shall I submit today's > remaining patches, then wait again? I imagine that massive branches > are scary and will deter reviewers? Scary will probably not be an issue for the follow-up topics around the pathspec-from-file theme, but a huge topic tends to wear out the author and the reviewers, inviting trivial bugs that would otherwise be found easily go unnoticed. Thanks. ^ permalink raw reply [flat|nested] 80+ messages in thread
* Re: [PATCH v4 00/13] Add --pathspec-from-file option 2019-12-04 19:25 ` Junio C Hamano @ 2019-12-05 10:43 ` Alexandr Miloslavskiy 0 siblings, 0 replies; 80+ messages in thread From: Alexandr Miloslavskiy @ 2019-12-05 10:43 UTC (permalink / raw) To: Junio C Hamano; +Cc: Alexandr Miloslavskiy via GitGitGadget, git On 04.12.2019 20:25, Junio C Hamano wrote: > Scary will probably not be an issue for the follow-up topics around > the pathspec-from-file theme, but a huge topic tends to wear out the > author and the reviewers, inviting trivial bugs that would otherwise > be found easily go unnoticed. OK, thanks! I will pause for now and wait until existing patches are in `master`. ^ permalink raw reply [flat|nested] 80+ messages in thread
end of thread, other threads:[~2019-12-12 14:56 UTC | newest] Thread overview: 80+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2019-11-04 19:26 [PATCH 0/5] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget 2019-11-04 19:26 ` [PATCH 1/5] pathspec: add new function to parse file Alexandr Miloslavskiy via GitGitGadget 2019-11-05 15:02 ` Phillip Wood 2019-11-05 19:14 ` Alexandr Miloslavskiy 2019-11-06 15:56 ` Alexandr Miloslavskiy 2019-11-04 19:26 ` [PATCH 2/5] doc: reset: unify <pathspec> description Alexandr Miloslavskiy via GitGitGadget 2019-11-06 4:01 ` Junio C Hamano 2019-11-06 15:56 ` Alexandr Miloslavskiy 2019-11-07 5:46 ` Junio C Hamano 2019-11-07 11:05 ` Alexandr Miloslavskiy 2019-11-08 3:04 ` Junio C Hamano 2019-11-04 19:26 ` [PATCH 3/5] reset: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget 2019-11-05 15:03 ` Phillip Wood 2019-11-05 19:22 ` Phillip Wood 2019-11-05 19:36 ` Alexandr Miloslavskiy 2019-11-06 15:56 ` Alexandr Miloslavskiy 2019-11-05 16:14 ` Phillip Wood 2019-11-05 19:37 ` Alexandr Miloslavskiy 2019-11-06 4:40 ` Junio C Hamano 2019-11-06 15:56 ` Alexandr Miloslavskiy 2019-11-04 19:26 ` [PATCH 4/5] doc: commit: unify <pathspec> description Alexandr Miloslavskiy via GitGitGadget 2019-11-06 4:50 ` Junio C Hamano 2019-11-06 15:56 ` Alexandr Miloslavskiy 2019-11-07 5:54 ` Junio C Hamano 2019-11-07 11:39 ` Alexandr Miloslavskiy 2019-11-04 19:26 ` [PATCH 5/5] commit: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget 2019-11-05 16:27 ` Phillip Wood 2019-11-05 19:42 ` Alexandr Miloslavskiy 2019-11-06 15:56 ` Alexandr Miloslavskiy 2019-12-10 10:42 ` Phillip Wood 2019-12-11 11:43 ` Alexandr Miloslavskiy 2019-12-11 14:27 ` Phillip Wood 2019-12-11 15:06 ` Alexandr Miloslavskiy 2019-12-11 16:14 ` Junio C Hamano 2019-12-11 16:20 ` Alexandr Miloslavskiy 2019-12-12 14:56 ` Alexandr Miloslavskiy 2019-11-06 4:51 ` Junio C Hamano 2019-11-06 15:51 ` [PATCH v2 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget 2019-11-06 15:51 ` [PATCH v2 1/6] parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul` Alexandr Miloslavskiy via GitGitGadget 2019-11-06 15:51 ` [PATCH v2 2/6] pathspec: add new function to parse file Alexandr Miloslavskiy via GitGitGadget 2019-11-19 5:59 ` Junio C Hamano 2019-11-19 16:50 ` Alexandr Miloslavskiy 2019-11-06 15:51 ` [PATCH v2 3/6] doc: reset: unify <pathspec> description Alexandr Miloslavskiy via GitGitGadget 2019-11-19 6:05 ` Junio C Hamano 2019-11-19 16:52 ` Alexandr Miloslavskiy 2019-11-06 15:51 ` [PATCH v2 4/6] reset: support the `--pathspec-from-file` option Alexandr Miloslavskiy via GitGitGadget 2019-11-06 15:51 ` [PATCH v2 5/6] doc: commit: unify <pathspec> description Alexandr Miloslavskiy via GitGitGadget 2019-11-19 6:16 ` Junio C Hamano 2019-11-19 16:53 ` Alexandr Miloslavskiy 2019-11-19 17:02 ` Alexandr Miloslavskiy 2019-11-06 15:51 ` [PATCH v2 6/6] commit: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget 2019-11-19 6:10 ` Junio C Hamano 2019-11-19 16:56 ` Alexandr Miloslavskiy 2019-11-19 16:48 ` [PATCH v3 0/6] Add --pathspec-from-file option for reset, commit Alexandr Miloslavskiy via GitGitGadget 2019-11-19 16:48 ` [PATCH v3 1/6] parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul` Alexandr Miloslavskiy via GitGitGadget 2019-11-19 16:48 ` [PATCH v3 2/6] pathspec: add new function to parse file Alexandr Miloslavskiy via GitGitGadget 2019-11-19 16:48 ` [PATCH v3 3/6] doc: reset: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget 2019-11-19 16:48 ` [PATCH v3 4/6] reset: support the `--pathspec-from-file` option Alexandr Miloslavskiy via GitGitGadget 2019-11-19 16:48 ` [PATCH v3 5/6] doc: commit: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget 2019-11-19 16:48 ` [PATCH v3 6/6] commit: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget 2019-11-20 4:04 ` [PATCH v3 0/6] Add --pathspec-from-file option for reset, commit Junio C Hamano 2019-11-20 9:22 ` Alexandr Miloslavskiy 2019-12-03 14:02 ` [PATCH v4 00/13] Add --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget 2019-12-03 14:02 ` [PATCH v4 01/13] parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul` Alexandr Miloslavskiy via GitGitGadget 2019-12-03 14:02 ` [PATCH v4 02/13] pathspec: add new function to parse file Alexandr Miloslavskiy via GitGitGadget 2019-12-03 14:02 ` [PATCH v4 03/13] doc: reset: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget 2019-12-03 14:02 ` [PATCH v4 04/13] reset: support the `--pathspec-from-file` option Alexandr Miloslavskiy via GitGitGadget 2019-12-03 14:02 ` [PATCH v4 05/13] doc: commit: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget 2019-12-03 14:02 ` [PATCH v4 06/13] commit: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget 2019-12-03 14:02 ` [PATCH v4 07/13] cmd_add: prepare for next patch Alexandr Miloslavskiy via GitGitGadget 2019-12-03 14:02 ` [PATCH v4 08/13] add: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget 2019-12-03 14:02 ` [PATCH v4 09/13] doc: checkout: remove duplicate synopsis Alexandr Miloslavskiy via GitGitGadget 2019-12-03 14:02 ` [PATCH v4 10/13] doc: checkout: fix broken text reference Alexandr Miloslavskiy via GitGitGadget 2019-12-03 14:02 ` [PATCH v4 11/13] doc: checkout: synchronize <pathspec> description Alexandr Miloslavskiy via GitGitGadget 2019-12-03 14:02 ` [PATCH v4 12/13] doc: restore: " Alexandr Miloslavskiy via GitGitGadget 2019-12-03 14:02 ` [PATCH v4 13/13] checkout, restore: support the --pathspec-from-file option Alexandr Miloslavskiy via GitGitGadget 2019-12-03 16:55 ` [PATCH v4 00/13] Add " Junio C Hamano 2019-12-03 17:06 ` Alexandr Miloslavskiy 2019-12-04 19:25 ` Junio C Hamano 2019-12-05 10:43 ` Alexandr Miloslavskiy
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).