Add --pathspec-from-file option for reset, commit
cover Add --pathspec-from-file option for reset, commit
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/[email protected]/T/#u
There, --stdin-paths was suggested.
https://public-inbox.org/git/[email protected]/T/#u
There, --stdin-paths was extended to --paths-file.
https://public-inbox.org/git/[email protected]/
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
Signature · 1 line
gitgitgadget
PATCH 1/5 pathspec: add new function to parse file
From: Alexandr Miloslavskiy <[email protected]>
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().
--- pathspec.c | 41 +++++++++++++++++++++++++++++++++++++++++ pathspec.h | 10 ++++++++++ 2 files changed, 51 insertions(+)
pathspec.c +41 -0
@@ -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;
pathspec.h +10 -0
@@ -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 *);
Signature · 1 line
gitgitgadget
Re: [PATCH 1/5] pathspec: add new function to parse file
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
Quoted 49 lines · Alexandr Miloslavskiy via GitGitGadget
From: Alexandr Miloslavskiy <[email protected]>
This will be used to support the new option '--pathspec-from-file' in
git add,git-commit,git resetetc.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 refactoringparse_pathspec().I considered adding
nul_term_linetoflagsinstead, but decided that it doesn't fit there.The new code is mostly taken from
cmd_update_index()andsplit_mail_conv().Co-authored-by Johannes Schindelin <[email protected]>Signed-off-by Alexandr Miloslavskiy <[email protected]>--- pathspec.c | 41 +++++++++++++++++++++++++++++++++++++++++ pathspec.h | 10 ++++++++++ 2 files changed, 51 insertions(+)pathspec.c +10
-0@@ -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 ;
pathspec.c void parse_pathspec(struct pathspec *pathspec, · line 635
Quoted 10 lines
pathspec.c +10
-0+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
pathspec.c void parse_pathspec(struct pathspec *pathspec, · line 641
Quoted 5 lines
pathspec.c +5
-0+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
Quoted 26 lines
pathspec.c +15
-0+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;pathspec.h +0
-0@@ -85,6 +85,16 @@ void parse_pathspec(struct pathspec *pathspec,unsigned flags, const char *prefix, const char **args);
pathspec.h void parse_pathspec(struct pathspec *pathspec, · line 97
Quoted 10 lines
pathspec.h +10
-0+/* +* 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
Quoted 4 lines
pathspec.h +0
-0void copy_pathspec (struct pathspec *dst,const struct pathspec *src);void clear_pathspec (struct pathspec *);
Re: [PATCH 1/5] pathspec: add new function to parse file
Quoted 4 lines · Phillip Wood
Quoted 2 lines
+ if (!in)
+die(_("could not open '%s' for reading"), file);
die_errno()would give a more informative message here
Thanks for the pointer!
Quoted 4 lines
Quoted 2 lines
+ 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.
Quoted 8 lines
Quoted 6 lines
+void parse_pathspec_file(struct pathspec *pathspec, + unsigned
magic_mask,
+ unsigned flags,
+ const char *prefix,
+ const char *file,
+ intnul_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.
Re: [PATCH 1/5] pathspec: add new function to parse file
In reply to Phillip WoodI think I have implemented all suggestions in PatchV2. Thanks!
PATCH 2/5 doc: reset: unify <pathspec> description
In reply to Alexandr Miloslavskiy via GitGitGadgetFrom: Alexandr Miloslavskiy <[email protected]>
Synchronize it to git add, which has a pretty good description.
--- Documentation/git-reset.txt | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-)
Documentation/git-reset.txt +15 -9
@@ -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.
Signature · 1 line
gitgitgadget
Re: [PATCH 2/5] doc: reset: unify <pathspec> description
"Alexandr Miloslavskiy via GitGitGadget" <[email protected]>
Quoted 8 lines · writes:
optionally modifying index and working tree to match. The
<tree-ish>/<commit>defaults toHEADin all forms.-'git reset' [-q] [<tree-ish>] [--] <paths>...::
Documentation/git-reset.txt +2
-1- 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
Documentation/git-reset.txt In the third form, set the current branch head (`HEAD`) to `<commit>`, · line 30
Quoted 10 lines
Documentation/git-reset.txt +3
-4the 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).
Quoted 3 lines
+This means that
git reset <pathspec>is the opposite ofgit 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.
Re: [PATCH 2/5] doc: reset: unify <pathspec> description
I think I have implemented most suggestions in PatchV2. Thanks!
Quoted 19 lines
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 newfileand then further
Quoted 7 lines
$ edit newfile $ git add newfile $ git reset -- newfilewe 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?
Re: [PATCH 2/5] doc: reset: unify <pathspec> description
Quoted 12 lines · Alexandr Miloslavskiy
Quoted 12 lines
It is not quite the opposite of adding to the index from the working tree. In this sequence:
$ edit newfile $ git add newfileand then further
$ edit newfile $ git add newfile $ git reset -- newfile
Quoted 6 lines
Quoted 3 lines
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.
Re: [PATCH 2/5] doc: reset: unify <pathspec> description
Quoted 5 lines · Junio C Hamano
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.
Re: [PATCH 2/5] doc: reset: unify <pathspec> description
Quoted 7 lines · Alexandr Miloslavskiy
Quoted 5 lines · Junio C Hamano
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.
Quoted 6 lines
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.
PATCH 3/5 reset: support the --pathspec-from-file option
In reply to Alexandr Miloslavskiy via GitGitGadgetFrom: Alexandr Miloslavskiy <[email protected]>
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.
--- 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
Documentation/git-reset.txt +15 -3
@@ -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 --------
builtin/reset.c +20 -2
@@ -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 */
t/t7107-reset-pathspec-file.sh new file +126 -0
126 lines not on this page · raw message
Signature · 1 line
gitgitgadget
Re: [PATCH 3/5] reset: support the --pathspec-from-file option
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!
Quoted 11 lines · Alexandr Miloslavskiy via GitGitGadget
From: Alexandr Miloslavskiy <[email protected]>
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
Quoted 6 lines
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
Quoted 3 lines
Decisions taken for simplicity:
1) The new option is declared incompatible with other options that could usestdin.
I'm confused reset does not use stdin does it?
Quoted 1 line
2) It is not allowed to pass some refspecs in args and others in file.
s/refspecs/pathspecs/ ?
Quoted 5 lines
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).
Quoted 15 lines
Co-authored-by Johannes Schindelin <[email protected]>Signed-off-by Alexandr Miloslavskiy <[email protected]>--- 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.shDocumentation/git-reset.txt +0
-0
Documentation/git-reset.txt SYNOPSIS · line 12
Quoted 4 lines
Documentation/git-reset.txt +1
-0@@ -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?
Quoted 5 lines
Documentation/git-reset.txt +0
-0'git reset' (--patch | -p) [<tree-ish>] [--] [<pathspec>...] 'git reset' [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>] DESCRIPTION -----------
Documentation/git-reset.txt SYNOPSIS · line 24
Quoted 10 lines
Documentation/git-reset.txt +3
-3-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
Documentation/git-reset.txt SYNOPSIS · line 25
Quoted 1 line
Documentation/git-reset.txt +1
-0+ These forms reset the index entries for all `<pathspec>` to their
The new form does not mention <pathspec> so this could be confusing
Documentation/git-reset.txt OPTIONS · line 113
Quoted 9 lines
Documentation/git-reset.txt +2
-0state 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".
Quoted 47 lines
If
<file>is exactly-Documentation/git-reset.txt +8
-0+ 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 --------builtin/reset.c +7
-2@@ -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() };
builtin/reset.c int cmd_reset(int argc, const char **argv, const char *prefix) · line 326
Quoted 6 lines
builtin/reset.c +3
-0@@ -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
builtin/reset.c int cmd_reset(int argc, const char **argv, const char *prefix) · line 335
Quoted 9 lines
builtin/reset.c +9
-0+ +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.
Quoted 21 lines
builtin/reset.c +1
-0+ unborn = !strcmp (rev,"HEAD" ) &&get_oid ("HEAD" , &oid);if (unborn) {/* reset on unborn branch: treat as reset to empty tree */ t/t7107-reset-pathspec-file.sh new file +11
-0@@ -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
t/t7107-reset-pathspec-file.sh line 21
Quoted 10 lines
t/t7107-reset-pathspec-file.sh new file +10
-0+ 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)
Quoted 62 lines
t/t7107-reset-pathspec-file.sh new file +62
-0+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=- && +
t/t7107-reset-pathspec-file.sh line 94
Quoted 10 lines
t/t7107-reset-pathspec-file.sh new file +10
-0+ 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.
Quoted 4 lines
t/t7107-reset-pathspec-file.sh new file +4
-0+' + +test_expect_success 'quotes not compatible with --pathspec-file-null' ' + restore_checkpoint &&
t/t7107-reset-pathspec-file.sh line 108
Quoted 10 lines
t/t7107-reset-pathspec-file.sh new file +10
-0+ + 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/
t/t7107-reset-pathspec-file.sh line 117
Quoted 9 lines
t/t7107-reset-pathspec-file.sh new file +9
-0+ 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
Quoted 10 lines
t/t7107-reset-pathspec-file.sh new file +9
-0+ 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
Re: [PATCH 3/5] reset: support the --pathspec-from-file option
Hi Alexandr
Quoted 10 lines · Phillip Wood
[...]
t/t7107-reset-pathspec-file.sh line 94
Quoted 7 lines
t/t7107-reset-pathspec-file.sh new file +7
-0+test_expect_success 'quotes' ' + restore_checkpoint && + + git rm fileA.t && + printf "\"file\\101.t\"" | git reset --pathspec-from-file=- && + + verify_state expect.aIf 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
Quoted 45 lines
Quoted 6 lines
+'
t/t7107-reset-pathspec-file.sh new file +4
-0+ +test_expect_success 'quotes not compatible with --pathspec-file-null' ' + restore_checkpoint && +t/t7107-reset-pathspec-file.sh
Quoted 10 lines
t/t7107-reset-pathspec-file.sh new file +9
-1+ 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/
t/t7107-reset-pathspec-file.sh line 117
Quoted 9 lines
+
restore_checkpoint&&t/t7107-reset-pathspec-file.sh new file +8
-0+ + 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
Quoted 10 lines
+
restore_checkpoint&&t/t7107-reset-pathspec-file.sh new file +8
-0+ + 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
Re: [PATCH 3/5] reset: support the --pathspec-from-file option
In reply to Phillip WoodQuoted 5 lines · Phillip Wood
Quoted 3 lines
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.
Quoted 5 lines
Quoted 3 lines
Decisions taken for simplicity:
1) The new option is declared incompatible with other options that could usestdin.I'm confused reset does not use stdin does it?
I understand that '--patch' interacts with user via stdin. Will double-check tomorrow.
Quoted 3 lines
Quoted 1 line
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.
Quoted 5 lines
Quoted 3 lines
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'
Quoted 5 lines
Quoted 2 lines
+'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/[email protected]/
so Junio added '-from' to previous writing. Hmm. What do you think, taking Junio's message into account?
As for whitespaces, sorry, will fix.
Quoted 4 lines
Quoted 2 lines
+'git reset' [-q] [--pathspec-from-file=<file> [--pathspec-file-null]] [<tree-ish>]::
Alignment again
Will fix
Quoted 3 lines
Quoted 1 line
+ These forms reset the index entries for all
<pathspec>to theirThe new form does not mention <pathspec> so this could be confusing
Shall I replace '<pathspec>' with 'pathspecs' ?
Quoted 1 line
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
Quoted 5 lines
Quoted 3 lines
+ 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.
Quoted 4 lines
Quoted 2 lines
+ } 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.
Quoted 1 line
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.
Quoted 9 lines
t/t7107-reset-pathspec-file.sh line 94
Quoted 7 lines
t/t7107-reset-pathspec-file.sh new file +7
-0+test_expect_success 'quotes' ' + restore_checkpoint && + + git rm fileA.t && + printf "\"file\\101.t\"" | git reset --pathspec-from-file=- && + + verify_state expect.aIf 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?
Quoted 4 lines
Quoted 2 lines
+test_expect_success '--pathspec-from-file is not compatible with --soft --hard' '
s/--soft --hard/--soft or --hard/
Good idea.
Quoted 3 lines
Quoted 1 line
+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?
Re: [PATCH 3/5] reset: support the --pathspec-from-file option
In reply to Phillip WoodI think I have implemented most suggestions in PatchV2. Thanks!
Quoted 1 line · Phillip Wood
I'm confused reset does not use stdin does it?
I have improved commit message to clarify.
Quoted 2 lines
--pathspec-file would be shorter and still conveys the intent of the option.
I decided to stay with writing suggested by Junio.
Quoted 1 line
Is this line missing a leading space?
I think it's good. Maybe you were confused by patch formatting?
Quoted 1 line
Alignment again
I think it's good again :)
Quoted 1 line
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.
Quoted 1 line
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?
Re: [PATCH 3/5] reset: support the --pathspec-from-file option
In reply to Alexandr Miloslavskiy via GitGitGadgetHi Alexandr
Quoted 26 lines · Alexandr Miloslavskiy via GitGitGadget
From: Alexandr Miloslavskiy <[email protected]> [...]
builtin/reset.c +3
-2@@ -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 ;
builtin/reset.c int cmd_reset(int argc, const char **argv, const char *prefix) · line 313
Quoted 7 lines
builtin/reset.c +4
-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
Quoted 156 lines
builtin/reset.c +13
-0OPT_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 */ t/t7107-reset-pathspec-file.sh new file +126
-0@@ -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
Re: [PATCH 3/5] reset: support the --pathspec-from-file option
Quoted 7 lines · Phillip Wood
Quoted 5 lines
+ 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!
Re: [PATCH 3/5] reset: support the --pathspec-from-file option
In reply to Alexandr Miloslavskiy via GitGitGadget"Alexandr Miloslavskiy via GitGitGadget" <[email protected]>
Quoted 8 lines · writes:
From: Alexandr Miloslavskiy <[email protected]>
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?
Quoted 3 lines
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 ;-)
Quoted 1 line
2) It is not allowed to pass some refspecs in args and others in file.
Did you mean refspec, not pathspec?
Quoted 5 lines
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.
Documentation/git-reset.txt OPTIONS · line 115
Quoted 4 lines
+--pathspec-from-file=<file>::
Documentation/git-reset.txt +3
-0+ 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.
Documentation/git-reset.txt OPTIONS · line 121
Quoted 3 lines
+--pathspec-file-null::
Documentation/git-reset.txt +2
-0+ Only meaningful with `--pathspec-from-file`. Pathspecs are + separated with NUL character and are not expected to be quoted.
OK.
builtin/reset.c int cmd_reset(int argc, const char **argv, const char *prefix) · line 329
Quoted 6 lines
builtin/reset.c +6
-0+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?
t/t7107-reset-pathspec-file.sh line 7
Quoted 13 lines
t/t7107-reset-pathspec-file.sh new file +7
-0@@ -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.
Quoted 18 lines
t/t7107-reset-pathspec-file.sh new file +18
-0+ 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 &&
t/t7107-reset-pathspec-file.sh line 35
Quoted 10 lines
t/t7107-reset-pathspec-file.sh new file +10
-0+ 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?
Quoted 18 lines
t/t7107-reset-pathspec-file.sh new file +18
-0+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 &&
t/t7107-reset-pathspec-file.sh line 65
Quoted 10 lines
t/t7107-reset-pathspec-file.sh new file +10
-0+ 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.
Re: [PATCH 3/5] reset: support the --pathspec-from-file option
I think I have implemented most suggestions in PatchV2. Thanks!
Quoted 5 lines
Quoted 3 lines
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 :)
Quoted 11 lines
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'.
Quoted 7 lines
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".
Quoted 7 lines
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.
Quoted 3 lines
- 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.
Quoted 2 lines
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.
PATCH 4/5 doc: commit: unify <pathspec> description
In reply to Alexandr Miloslavskiy via GitGitGadgetFrom: Alexandr Miloslavskiy <[email protected]>
Synchronize it to git add, which has a pretty good description. This also better disambiguates <file>... header.
--- Documentation/git-commit.txt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
Documentation/git-commit.txt +5 -2
@@ -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[]
Signature · 1 line
gitgitgadget
Re: [PATCH 4/5] doc: commit: unify <pathspec> description
"Alexandr Miloslavskiy via GitGitGadget" <[email protected]>
Quoted 4 lines · writes:
From: Alexandr Miloslavskiy <[email protected]>
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.
Documentation/git-commit.txt changes to tracked files. · line 356
Quoted 2 lines
Documentation/git-commit.txt +2
-0+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>)?
Re: [PATCH 4/5] doc: commit: unify <pathspec> description
I think I have implemented most suggestions in PatchV2. Thanks!
Quoted 10 lines
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.
Re: [PATCH 4/5] doc: commit: unify <pathspec> description
Quoted 8 lines · Alexandr Miloslavskiy
I think I have implemented most suggestions in PatchV2. Thanks!
Quoted 6 lines
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.
Quoted 7 lines
Quoted 4 lines
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:
Documentation/git-commit.txt changes to tracked files. · line 356
Quoted 2 lines
Documentation/git-commit.txt +2
-0+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.
Quoted 1 line
Pathspec, however, is something new.
Compared to CVS, everything in Git may be new, but that was a news in 2010, not this year.
Re: [PATCH 4/5] doc: commit: unify <pathspec> description
Quoted 8 lines · Junio C Hamano
I am reacting to this from your change that you omitted quoting in your reply:
Documentation/git-commit.txt changes to tracked files. · line 356
Quoted 2 lines
Documentation/git-commit.txt +2
-0+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.
Quoted 7 lines
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
PATCH 5/5 commit: support the --pathspec-from-file option
In reply to Alexandr Miloslavskiy via GitGitGadgetFrom: Alexandr Miloslavskiy <[email protected]>
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.
--- 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
Documentation/git-commit.txt +13 -1
@@ -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>]::
builtin/commit.c +21 -4
@@ -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 *prefixPATHSPEC_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,
t/t7526-commit-pathspec-file.sh new file +107 -0
107 lines not on this page · raw message
Signature · 1 line
gitgitgadget
Re: [PATCH 5/5] commit: support the --pathspec-from-file option
Hi Alexandr
Quoted 25 lines · Alexandr Miloslavskiy via GitGitGadget
From: Alexandr Miloslavskiy <[email protected]>
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 usestdin.
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.
Quoted 3 lines
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
Quoted 28 lines
Signed-off-by Alexandr Miloslavskiy <[email protected]>--- 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.shDocumentation/git-commit.txt +3
-1@@ -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. +
Documentation/git-commit.txt FROM UPSTREAM REBASE" section in linkgit:git-rebase[1].) · line 291
Quoted 10 lines
Documentation/git-commit.txt +10
-0+--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
Quoted 28 lines
Documentation/git-commit.txt +0
-0-u[<mode>]:: --untracked-files[=<mode>]::builtin/commit.c +8
-2@@ -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 *prefixPATHSPEC_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" ));
builtin/commit.c static const char *prepare_index(int argc, const char **argv, const char *prefix · line 361
Quoted 10 lines
builtin/commit.c +10
-0+ +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.
t/t7526-commit-pathspec-file.sh
Quoted 26 lines
builtin/commit.c +2
-2if (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,t/t7526-commit-pathspec-file.sh new file +0
-0
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
Quoted 109 lines
t/t7526-commit-pathspec-file.sh new file +107
-0@@ -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
Re: [PATCH 5/5] commit: support the --pathspec-from-file option
Quoted 5 lines · Phillip Wood
Quoted 3 lines
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)" ?
Quoted 1 line
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 :)
Quoted 4 lines
Quoted 2 lines
+ 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()fromparse_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.
Quoted 1 line
Overall this series is nicely structured and is looking pretty good
Thanks, and also thanks for reviewing my patches!
Re: [PATCH 5/5] commit: support the --pathspec-from-file option
In reply to Phillip WoodI think I have implemented most suggestions in PatchV2. Thanks!
Quoted 1 line
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.
Quoted 1 line
I wonder if there is a way of calling
parse_pathspec_file()fromparse_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.
Re: [PATCH 5/5] commit: support the --pathspec-from-file option
Hi Alexandr
Sorry it has taken me so long to reply
Quoted 8 lines · Alexandr Miloslavskiy
I think I have implemented most suggestions in PatchV2. Thanks!
Quoted 2 lines
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.
Quoted 8 lines
Quoted 3 lines
I wonder if there is a way of calling
parse_pathspec_file()fromparse_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 populatepathspecand some other function will need to remember to clean it up. I like it better whenpathspecis 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.
Quoted 6 lines
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 ---
builtin/commit.c +39 -31
@@ -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 *prefixdie (_ ("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 *prefixread_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. */
Re: [PATCH 5/5] commit: support the --pathspec-from-file option
Quoted 4 lines · Phillip Wood
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.
Re: [PATCH 5/5] commit: support the --pathspec-from-file option
Hi Alexandr
Quoted 7 lines · Alexandr Miloslavskiy
Quoted 4 lines · Phillip Wood
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.
Quoted 8 lines
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).
Quoted 7 lines
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 throughdry_run_commit().
It would be easy enough to clear pathspec in cmd_commit() I just didn't bother to do it.
Quoted 6 lines
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 inset_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
Re: [PATCH 5/5] commit: support the --pathspec-from-file option
Quoted 3 lines · Phillip Wood
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.
Quoted 3 lines
Quoted 1 line
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 throughdry_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?
Quoted 1 line
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/[email protected]/
[2] https://public-inbox.org/git/[email protected]/T/#u
Re: [PATCH 5/5] commit: support the --pathspec-from-file option
Quoted 4 lines · Alexandr Miloslavskiy
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.
Quoted 4 lines
[1]
https://public-inbox.org/git/[email protected]/
[2]
https://public-inbox.org/git/[email protected]/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.
Re: [PATCH 5/5] commit: support the --pathspec-from-file option
Quoted 7 lines · Junio C Hamano
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.
Re: [PATCH 5/5] commit: support the --pathspec-from-file option
In reply to Phillip WoodThe new topic is now submitted:
https://lore.kernel.org/git/[email protected]/
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
Re: [PATCH 5/5] commit: support the --pathspec-from-file option
In reply to Alexandr Miloslavskiy via GitGitGadget"Alexandr Miloslavskiy via GitGitGadget" <[email protected]>
Quoted 11 lines · writes:
From: Alexandr Miloslavskiy <[email protected]>
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.
v2 cover Add --pathspec-from-file option for reset, commit
In reply to Alexandr Miloslavskiy via GitGitGadgetAdds --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/[email protected]/T/#u
There, --stdin-paths was suggested.
https://public-inbox.org/git/[email protected]/T/#u
There, --stdin-paths was extended to --paths-file.
https://public-inbox.org/git/[email protected]/
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 <[email protected]>
- 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 <[email protected]>
Signed-off-by: Alexandr Miloslavskiy <[email protected]>
@@ -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 <[email protected]>
@@ -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_doneSignature · 1 line
gitgitgadget
PATCH v2 1/6 parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul`
From: Alexandr Miloslavskiy <[email protected]>
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.
--- parse-options.h | 2 ++ 1 file changed, 2 insertions(+)
parse-options.h +2 -0
@@ -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
Signature · 1 line
gitgitgadget
PATCH v2 2/6 pathspec: add new function to parse file
In reply to Alexandr Miloslavskiy via GitGitGadgetFrom: Alexandr Miloslavskiy <[email protected]>
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().
--- pathspec.c | 38 ++++++++++++++++++++++++++++++++++++++ pathspec.h | 10 ++++++++++ 2 files changed, 48 insertions(+)
pathspec.c +38 -0
@@ -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;
pathspec.h +10 -0
@@ -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 *);
Signature · 1 line
gitgitgadget
Re: [PATCH v2 2/6] pathspec: add new function to parse file
"Alexandr Miloslavskiy via GitGitGadget" <[email protected]>
pathspec.c void parse_pathspec(struct pathspec *pathspec, · line 627
Quoted 10 lines · writes:
pathspec.c +10
-0+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.
Quoted 4 lines
pathspec.c +4
-0+if (!strcmp (file,"-" )) + in = stdin; +else + in =xfopen (file,"r" );
Re: [PATCH v2 2/6] pathspec: add new function to parse file
Quoted 3 lines · Junio C Hamano
pathspec.c void parse_pathspec(struct pathspec *pathspec, · line 627
Quoted 1 line
pathspec.c +1
-0+FILE *in =NULL ;Useless initialization to NULL can be dropped here.
Changed in V3.
PATCH v2 3/6 doc: reset: unify <pathspec> description
In reply to Alexandr Miloslavskiy via GitGitGadgetFrom: Alexandr Miloslavskiy <[email protected]>
Synchronize it to git add, which has a pretty good description.
--- Documentation/git-reset.txt | 20 +++++++++++--------- builtin/reset.c | 4 ++-- 2 files changed, 13 insertions(+), 11 deletions(-)
Documentation/git-reset.txt +11 -9
@@ -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.
builtin/reset.c +2 -2
@@ -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 };
Signature · 1 line
gitgitgadget
Re: [PATCH v2 3/6] doc: reset: unify <pathspec> description
"Alexandr Miloslavskiy via GitGitGadget" <[email protected]>
Quoted 4 lines · 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.
Documentation/git-reset.txt In the first and second form, copy entries from `<tree-ish>` to the index. · line 22
Quoted 6 lines
Documentation/git-reset.txt +2
-0@@ -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.
Documentation/git-reset.txt In the first and second form, copy entries from `<tree-ish>` to the index. · line 25
Quoted 4 lines
Documentation/git-reset.txt +2
-2-'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...
Documentation/git-reset.txt In the first and second form, copy entries from `<tree-ish>` to the index. · line 27
Quoted 2 lines
Documentation/git-reset.txt +0
-0state at `<tree-ish>`. (It does not affect the working tree or the current branch.)
Thanks.
Re: [PATCH v2 3/6] doc: reset: unify <pathspec> description
Quoted 11 lines · Junio C Hamano
Documentation/git-reset.txt In the first and second form, copy entries from `<tree-ish>` to the index. · line 25
Quoted 4 lines
-'git reset' [-q] [<tree-ish>] [--] <paths>...::
Documentation/git-reset.txt +2
-1- 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 theirThis 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
Quoted 5 lines
ths form resets the index entries for all paths that match the
<pathspec>to their...Quoted 2 lines
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!
PATCH v2 4/6 reset: support the `--pathspec-from-file` option
In reply to Alexandr Miloslavskiy via GitGitGadgetFrom: Alexandr Miloslavskiy <[email protected]>
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.
--- 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
Documentation/git-reset.txt +16 -3
@@ -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 --------
builtin/reset.c +19 -2
@@ -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 */
t/t7107-reset-pathspec-file.sh new file +155 -0
155 lines not on this page · raw message
Signature · 1 line
gitgitgadget
PATCH v2 5/6 doc: commit: unify <pathspec> description
In reply to Alexandr Miloslavskiy via GitGitGadgetFrom: Alexandr Miloslavskiy <[email protected]>
Synchronize it to git add, which has a pretty good description. This also better disambiguates <file>... header.
--- Documentation/git-commit.txt | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-)
Documentation/git-commit.txt +9 -7
@@ -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[]
Signature · 1 line
gitgitgadget
Re: [PATCH v2 5/6] doc: commit: unify <pathspec> description
"Alexandr Miloslavskiy via GitGitGadget" <[email protected]>
Documentation/git-commit.txt changes to tracked files. · line 355
Quoted 8 lines · writes:
+<pathspec>...::
Documentation/git-commit.txt +7
-0+ 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.
Re: [PATCH v2 5/6] doc: commit: unify <pathspec> description
Quoted 9 lines · Junio C Hamano
Documentation/git-commit.txt changes to tracked files. · line 355
Quoted 2 lines
Documentation/git-commit.txt +2
-0+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).
Quoted 6 lines
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?
Re: [PATCH v2 5/6] doc: commit: unify <pathspec> description
In reply to Junio C HamanoQuoted 6 lines · Junio C Hamano
Documentation/git-commit.txt changes to tracked files. · line 355
Quoted 2 lines
Documentation/git-commit.txt +2
-0+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.
Quoted 6 lines
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.
PATCH v2 6/6 commit: support the --pathspec-from-file option
In reply to Alexandr Miloslavskiy via GitGitGadgetFrom: Alexandr Miloslavskiy <[email protected]>
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.
--- 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
Documentation/git-commit.txt +14 -1
@@ -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>]::
builtin/commit.c +21 -4
@@ -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 *prefixPATHSPEC_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,
t/t7526-commit-pathspec-file.sh new file +130 -0
130 lines not on this page · raw message
Signature · 1 line
gitgitgadget
Re: [PATCH v2 6/6] commit: support the --pathspec-from-file option
"Alexandr Miloslavskiy via GitGitGadget" <[email protected]>
Documentation/git-commit.txt FROM UPSTREAM REBASE" section in linkgit:git-rebase[1].) · line 288
Quoted 12 lines · writes:
Documentation/git-commit.txt +8
-0@@ -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.
Documentation/git-commit.txt FROM UPSTREAM REBASE" section in linkgit:git-rebase[1].) · line 292
Quoted 3 lines
Documentation/git-commit.txt +3
-0+--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.
Re: [PATCH v2 6/6] commit: support the --pathspec-from-file option
Quoted 9 lines · Junio C Hamano
Documentation/git-commit.txt FROM UPSTREAM REBASE" section in linkgit:git-rebase[1].) · line 292
Quoted 3 lines
+--pathspec-file-nul::
Documentation/git-commit.txt +2
-0+ 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?
Quoted 7 lines
Rephrasing to avoid such misinterpretation may be worth doing, perhaps
... and are taken as-is without unquoting (i.e. as if
core.quotePathis set tofalse).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!
v3 cover Add --pathspec-from-file option for reset, commit
In reply to Alexandr Miloslavskiy via GitGitGadgetChanges 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 <[email protected]>
- 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.
@@ -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 <[email protected]>
- 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.
@@ -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 &&Signature · 1 line
gitgitgadget
PATCH v3 1/6 parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul`
From: Alexandr Miloslavskiy <[email protected]>
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.
--- parse-options.h | 2 ++ 1 file changed, 2 insertions(+)
parse-options.h +2 -0
@@ -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
Signature · 1 line
gitgitgadget
PATCH v3 2/6 pathspec: add new function to parse file
In reply to Alexandr Miloslavskiy via GitGitGadgetFrom: Alexandr Miloslavskiy <[email protected]>
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().
--- pathspec.c | 38 ++++++++++++++++++++++++++++++++++++++ pathspec.h | 10 ++++++++++ 2 files changed, 48 insertions(+)
pathspec.c +38 -0
@@ -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;
pathspec.h +10 -0
@@ -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 *);
Signature · 1 line
gitgitgadget
PATCH v3 3/6 doc: reset: synchronize <pathspec> description
In reply to Alexandr Miloslavskiy via GitGitGadgetFrom: Alexandr Miloslavskiy <[email protected]>
git add shows an example of good writing, follow it.
--- Documentation/git-reset.txt | 29 ++++++++++++++++++----------- builtin/reset.c | 4 ++-- 2 files changed, 20 insertions(+), 13 deletions(-)
Documentation/git-reset.txt +18 -11
@@ -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 --------
builtin/reset.c +2 -2
@@ -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 };
Signature · 1 line
gitgitgadget
PATCH v3 4/6 reset: support the `--pathspec-from-file` option
In reply to Alexandr Miloslavskiy via GitGitGadgetFrom: Alexandr Miloslavskiy <[email protected]>
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.
--- 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
Documentation/git-reset.txt +18 -3
@@ -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.
builtin/reset.c +19 -2
@@ -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 */
t/t7107-reset-pathspec-file.sh new file +155 -0
155 lines not on this page · raw message
Signature · 1 line
gitgitgadget
PATCH v3 5/6 doc: commit: synchronize <pathspec> description
In reply to Alexandr Miloslavskiy via GitGitGadgetFrom: Alexandr Miloslavskiy <[email protected]>
git add shows an example of good writing, follow it. This also better disambiguates <file>... header.
--- Documentation/git-commit.txt | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-)
Documentation/git-commit.txt +8 -7
@@ -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[]
Signature · 1 line
gitgitgadget
PATCH v3 6/6 commit: support the --pathspec-from-file option
In reply to Alexandr Miloslavskiy via GitGitGadgetFrom: Alexandr Miloslavskiy <[email protected]>
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.
--- 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
Documentation/git-commit.txt +15 -1
@@ -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.
builtin/commit.c +21 -4
@@ -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 *prefixPATHSPEC_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,
t/t7526-commit-pathspec-file.sh new file +130 -0
130 lines not on this page · raw message
Signature · 1 line
gitgitgadget
"Alexandr Miloslavskiy via GitGitGadget" <[email protected]>
Quoted 5 lines · writes:
Changes from V2:
1) Some polishing in documentation 2) = NULL removed from
parse_pathspec_file()
Quoted 8 lines
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-fileoption
doc: commit: synchronize <pathspec> description
commit: support the --pathspec-from-file option
Nicely done. I think this is now ready for 'next'.
Thanks.
Quoted 1 line · Junio C Hamano
Nicely done. I think this is now ready for 'next'.
Wonderful, thanks for your assistance!
I will continue with patches for other commands, then.
v4 cover Add --pathspec-from-file option
In reply to Alexandr Miloslavskiy via GitGitGadgetChanges 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
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
Signature · 1 line
gitgitgadget
PATCH v4 1/13 parse-options.h: add new options `--pathspec-from-file`, `--pathspec-file-nul`
From: Alexandr Miloslavskiy <[email protected]>
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.
--- parse-options.h | 2 ++ 1 file changed, 2 insertions(+)
parse-options.h +2 -0
@@ -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
Signature · 1 line
gitgitgadget
PATCH v4 2/13 pathspec: add new function to parse file
In reply to Alexandr Miloslavskiy via GitGitGadgetFrom: Alexandr Miloslavskiy <[email protected]>
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().
--- pathspec.c | 38 ++++++++++++++++++++++++++++++++++++++ pathspec.h | 10 ++++++++++ 2 files changed, 48 insertions(+)
pathspec.c +38 -0
@@ -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;
pathspec.h +10 -0
@@ -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 *);
Signature · 1 line
gitgitgadget
PATCH v4 3/13 doc: reset: synchronize <pathspec> description
In reply to Alexandr Miloslavskiy via GitGitGadgetFrom: Alexandr Miloslavskiy <[email protected]>
git add shows an example of good writing, follow it.
--- Documentation/git-reset.txt | 29 ++++++++++++++++++----------- builtin/reset.c | 4 ++-- 2 files changed, 20 insertions(+), 13 deletions(-)
Documentation/git-reset.txt +18 -11
@@ -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 --------
builtin/reset.c +2 -2
@@ -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 };
Signature · 1 line
gitgitgadget
PATCH v4 4/13 reset: support the `--pathspec-from-file` option
In reply to Alexandr Miloslavskiy via GitGitGadgetFrom: Alexandr Miloslavskiy <[email protected]>
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.
--- 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
Documentation/git-reset.txt +18 -3
@@ -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.
builtin/reset.c +19 -2
@@ -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 */
t/t7107-reset-pathspec-file.sh new file +155 -0
155 lines not on this page · raw message
Signature · 1 line
gitgitgadget
PATCH v4 5/13 doc: commit: synchronize <pathspec> description
In reply to Alexandr Miloslavskiy via GitGitGadgetFrom: Alexandr Miloslavskiy <[email protected]>
git add shows an example of good writing, follow it. This also better disambiguates <file>... header.
--- Documentation/git-commit.txt | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-)
Documentation/git-commit.txt +8 -7
@@ -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[]
Signature · 1 line
gitgitgadget
PATCH v4 6/13 commit: support the --pathspec-from-file option
In reply to Alexandr Miloslavskiy via GitGitGadgetFrom: Alexandr Miloslavskiy <[email protected]>
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.
--- 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
Documentation/git-commit.txt +15 -1
@@ -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.
builtin/commit.c +21 -4
@@ -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 *prefixPATHSPEC_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,
t/t7526-commit-pathspec-file.sh new file +130 -0
130 lines not on this page · raw message
Signature · 1 line
gitgitgadget
PATCH v4 7/13 cmd_add: prepare for next patch
In reply to Alexandr Miloslavskiy via GitGitGadgetFrom: Alexandr Miloslavskiy <[email protected]>
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.
--- builtin/add.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-)
builtin/add.c +17 -17
@@ -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" ));
Signature · 1 line
gitgitgadget
PATCH v4 8/13 add: support the --pathspec-from-file option
In reply to Alexandr Miloslavskiy via GitGitGadgetFrom: Alexandr Miloslavskiy <[email protected]>
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.
--- 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
Documentation/git-add.txt +15 -1
@@ -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
builtin/add.c +26 -4
@@ -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 ;
t/t3704-add-pathspec-file.sh new file +127 -0
127 lines not on this page · raw message
Signature · 1 line
gitgitgadget
PATCH v4 9/13 doc: checkout: remove duplicate synopsis
In reply to Alexandr Miloslavskiy via GitGitGadgetFrom: Alexandr Miloslavskiy <[email protected]>
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)
--- Documentation/git-checkout.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
Documentation/git-checkout.txt +1 -2
@@ -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
Signature · 1 line
gitgitgadget
PATCH v4 10/13 doc: checkout: fix broken text reference
In reply to Alexandr Miloslavskiy via GitGitGadgetFrom: Alexandr Miloslavskiy <[email protected]>
--- Documentation/git-checkout.txt | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-)
Documentation/git-checkout.txt +4 -6
@@ -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 -------
Signature · 1 line
gitgitgadget
PATCH v4 11/13 doc: checkout: synchronize <pathspec> description
In reply to Alexandr Miloslavskiy via GitGitGadgetFrom: Alexandr Miloslavskiy <[email protected]>
git add shows an example of good writing, follow it.
--- Documentation/git-checkout.txt | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-)
Documentation/git-checkout.txt +15 -9
@@ -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 -------------
Signature · 1 line
gitgitgadget
PATCH v4 12/13 doc: restore: synchronize <pathspec> description
In reply to Alexandr Miloslavskiy via GitGitGadgetFrom: Alexandr Miloslavskiy <[email protected]>
git add shows an example of good writing, follow it.
--- Documentation/git-restore.txt | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-)
Documentation/git-restore.txt +10 -2
@@ -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 --------
Signature · 1 line
gitgitgadget
PATCH v4 13/13 checkout, restore: support the --pathspec-from-file option
In reply to Alexandr Miloslavskiy via GitGitGadgetFrom: Alexandr Miloslavskiy <[email protected]>
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.
--- 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
Documentation/git-checkout.txt +15 -0
@@ -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
Documentation/git-restore.txt +14 -0
@@ -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.
builtin/checkout.c +27 -4
@@ -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) {
t/t2026-checkout-pathspec-file.sh new file +139 -0
139 lines not on this page · raw message
t/t2072-restore-pathspec-file.sh new file +139 -0
139 lines not on this page · raw message
t/t9902-completion.sh +2 -0
@@ -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 '
Signature · 1 line
gitgitgadget
Re: [PATCH v4 00/13] Add --pathspec-from-file option
In reply to Alexandr Miloslavskiy via GitGitGadget"Alexandr Miloslavskiy via GitGitGadget" <[email protected]>
Quoted 1 line · 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.
Re: [PATCH v4 00/13] Add --pathspec-from-file option
Quoted 9 lines · Junio C Hamano
Quoted 1 line
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?
Re: [PATCH v4 00/13] Add --pathspec-from-file option
Quoted 12 lines · Alexandr Miloslavskiy
Quoted 9 lines · Junio C Hamano
Quoted 1 line
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).
Quoted 4 lines
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.
Quoted 4 lines
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.
Re: [PATCH v4 00/13] Add --pathspec-from-file option
Quoted 4 lines · Junio C Hamano
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.