* [PATCH 0/3] Better support for customising context lines in --patch commands @ 2025-05-05 9:18 Leon Michalak via GitGitGadget 2025-05-05 9:18 ` [PATCH 1/3] add-patch: respect diff.context configuration Leon Michalak via GitGitGadget ` (3 more replies) 0 siblings, 4 replies; 77+ messages in thread From: Leon Michalak via GitGitGadget @ 2025-05-05 9:18 UTC (permalink / raw) To: git; +Cc: Leon Michalak This series of patches attempt to give --interactive/--patch compatible builtins ("add", "commit", "checkout", "reset", "restore" and "stash") better support and nicer experience for configuring how many context lines are shown in diffs through a variety of ways. Prior to these patches, the user could not choose how many context lines they saw in --patch commands (apart from one workaround by using GIT_DIFF_OPTS=-u<number> ..., however this isn't a good user experience or a persistent solution). Additionally, the behaviour around reading from the diff.context and diff.interHunkContext configs was also inconsistent with other diff generating commands such as "log -p". The summarised changes below hopefully make this experience better and fix some inconsistencies: * diff.context and diff.interHunkContext configs are now respected by --patch compatible commands * --unified and --inter-hunk-context command line options have been added to --patch compatible commands (which take prescendence over file configs) * "add" and "commit" in --interactive mode now expose a new "context" subcommand which configures the amount of context lines you wish to see in subsequent diffs generated from other subcommands such as "patch" or "diff" The original discussion for this can be read at: * https://lore.kernel.org/git/CAP9jKjGb-Rcr=RLJEzeFdtrekYM+qmHy+1T1fykU3n9cV4GhGw@mail.gmail.com/ Leon Michalak (3): add-patch: respect diff.context configuration add-patch: add diff.context command line overrides add-interactive: add new "context" subcommand Documentation/git-add.adoc | 21 ++++++- Documentation/git-checkout.adoc | 11 ++++ Documentation/git-commit.adoc | 11 ++++ Documentation/git-reset.adoc | 11 ++++ Documentation/git-restore.adoc | 11 ++++ Documentation/git-stash.adoc | 11 ++++ add-interactive.c | 107 +++++++++++++++++++++++++++++--- add-interactive.h | 17 ++++- add-patch.c | 11 +++- builtin/add.c | 21 +++++-- builtin/checkout.c | 28 ++++++++- builtin/commit.c | 15 ++++- builtin/reset.c | 16 ++++- builtin/stash.c | 54 ++++++++++++---- commit.h | 3 +- t/t3701-add-interactive.sh | 36 +++++++++-- t/t4055-diff-context.sh | 78 ++++++++++++++++++++++- t/t9902-completion.sh | 2 + 18 files changed, 420 insertions(+), 44 deletions(-) base-commit: f65182a99e545d2f2bc22e6c1c2da192133b16a3 Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1915%2FNinjaInShade%2Finteractive-patch-context-v1 Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1915/NinjaInShade/interactive-patch-context-v1 Pull-Request: https://github.com/gitgitgadget/git/pull/1915 -- gitgitgadget ^ permalink raw reply [flat|nested] 77+ messages in thread
* [PATCH 1/3] add-patch: respect diff.context configuration 2025-05-05 9:18 [PATCH 0/3] Better support for customising context lines in --patch commands Leon Michalak via GitGitGadget @ 2025-05-05 9:18 ` Leon Michalak via GitGitGadget 2025-05-05 20:29 ` Eric Sunshine ` (2 more replies) 2025-05-05 9:18 ` [PATCH 2/3] add-patch: add diff.context command line overrides Leon Michalak via GitGitGadget ` (2 subsequent siblings) 3 siblings, 3 replies; 77+ messages in thread From: Leon Michalak via GitGitGadget @ 2025-05-05 9:18 UTC (permalink / raw) To: git; +Cc: Leon Michalak, Leon Michalak From: Leon Michalak <leonmichalak6@gmail.com> This aims to teach relevant builtins (that take in `--patch`) to respect the user's diff.context and diff.interHunkContext file configurations. Since these are both UI options and `--patch` is designed for the end user, I believe this was previously just an inconsistency, which this patch hopes to address. Signed-off-by: Leon Michalak <leonmichalak6@gmail.com> --- add-interactive.c | 16 ++++++++++---- add-patch.c | 6 ++++++ t/t4055-diff-context.sh | 48 ++++++++++++++++++++++++++++++++++++++++- 3 files changed, 65 insertions(+), 5 deletions(-) diff --git a/add-interactive.c b/add-interactive.c index 97ff35b6f12a..ad12dc416598 100644 --- a/add-interactive.c +++ b/add-interactive.c @@ -41,6 +41,8 @@ void init_add_i_state(struct add_i_state *s, struct repository *r) const char *value; s->r = r; + s->context = -1; + s->interhunkcontext = -1; if (repo_config_get_value(r, "color.interactive", &value)) s->use_color = -1; @@ -78,6 +80,9 @@ void init_add_i_state(struct add_i_state *s, struct repository *r) repo_config_get_string(r, "diff.algorithm", &s->interactive_diff_algorithm); + repo_config_get_int(r, "diff.context", &s->context); + repo_config_get_int(r, "diff.interHunkContext", &s->interhunkcontext); + repo_config_get_bool(r, "interactive.singlekey", &s->use_single_key); if (s->use_single_key) setbuf(stdin, NULL); @@ -1014,10 +1019,13 @@ static int run_diff(struct add_i_state *s, const struct pathspec *ps, if (count > 0) { struct child_process cmd = CHILD_PROCESS_INIT; - strvec_pushl(&cmd.args, "git", "diff", "-p", "--cached", - oid_to_hex(!is_initial ? &oid : - s->r->hash_algo->empty_tree), - "--", NULL); + strvec_pushl(&cmd.args, "git", "diff", "-p", "--cached", NULL); + if (s->context != -1) + strvec_pushf(&cmd.args, "--unified=%i", s->context); + if (s->interhunkcontext != -1) + strvec_pushf(&cmd.args, "--inter-hunk-context=%i", s->interhunkcontext); + strvec_pushl(&cmd.args, oid_to_hex(!is_initial ? &oid : + s->r->hash_algo->empty_tree), "--", NULL); for (i = 0; i < files->items.nr; i++) if (files->selected[i]) strvec_push(&cmd.args, diff --git a/add-patch.c b/add-patch.c index 95c67d8c80c4..b43ca1600738 100644 --- a/add-patch.c +++ b/add-patch.c @@ -415,6 +415,8 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps) { struct strvec args = STRVEC_INIT; const char *diff_algorithm = s->s.interactive_diff_algorithm; + int diff_context = s->s.context; + int diff_interhunkcontext = s->s.interhunkcontext; struct strbuf *plain = &s->plain, *colored = NULL; struct child_process cp = CHILD_PROCESS_INIT; char *p, *pend, *colored_p = NULL, *colored_pend = NULL, marker = '\0'; @@ -424,6 +426,10 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps) int res; strvec_pushv(&args, s->mode->diff_cmd); + if (diff_context != -1) + strvec_pushf(&args, "--unified=%i", diff_context); + if (diff_interhunkcontext != -1) + strvec_pushf(&args, "--inter-hunk-context=%i", diff_interhunkcontext); if (diff_algorithm) strvec_pushf(&args, "--diff-algorithm=%s", diff_algorithm); if (s->revision) { diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh index ec2804eea67c..9c024200ade7 100755 --- a/t/t4055-diff-context.sh +++ b/t/t4055-diff-context.sh @@ -49,7 +49,53 @@ test_expect_success 'diff.context honored by "log"' ' ! grep firstline output && git config diff.context 8 && git log -1 -p >output && - grep "^ firstline" output + grep "^ firstline" output && + git config --unset diff.context +' + +test_expect_success 'diff.context honored by "add"' ' + git add -p >output && + ! grep firstline output && + git config diff.context 8 && + git add -p >output && + grep "^ firstline" output && + git config --unset diff.context +' + +test_expect_success 'diff.context honored by "commit"' ' + ! git commit -p >output && + ! grep firstline output && + git config diff.context 8 && + ! git commit -p >output && + grep "^ firstline" output && + git config --unset diff.context +' + +test_expect_success 'diff.context honored by "checkout"' ' + git checkout -p >output && + ! grep firstline output && + git config diff.context 8 && + git checkout -p >output && + grep "^ firstline" output && + git config --unset diff.context +' + +test_expect_success 'diff.context honored by "stash"' ' + ! git stash -p >output && + ! grep firstline output && + git config diff.context 8 && + ! git stash -p >output && + grep "^ firstline" output && + git config --unset diff.context +' + +test_expect_success 'diff.context honored by "restore"' ' + git restore -p >output && + ! grep firstline output && + git config diff.context 8 && + git restore -p >output && + grep "^ firstline" output && + git config --unset diff.context ' test_expect_success 'The -U option overrides diff.context' ' -- gitgitgadget ^ permalink raw reply related [flat|nested] 77+ messages in thread
* Re: [PATCH 1/3] add-patch: respect diff.context configuration 2025-05-05 9:18 ` [PATCH 1/3] add-patch: respect diff.context configuration Leon Michalak via GitGitGadget @ 2025-05-05 20:29 ` Eric Sunshine 2025-05-06 8:55 ` Phillip Wood 2025-05-06 17:29 ` Junio C Hamano 2 siblings, 0 replies; 77+ messages in thread From: Eric Sunshine @ 2025-05-05 20:29 UTC (permalink / raw) To: Leon Michalak via GitGitGadget; +Cc: git, Leon Michalak On Mon, May 5, 2025 at 5:18 AM Leon Michalak via GitGitGadget <gitgitgadget@gmail.com> wrote: > This aims to teach relevant builtins (that take in `--patch`) to respect > the user's diff.context and diff.interHunkContext file configurations. > > Since these are both UI options and `--patch` is designed for the end user, > I believe this was previously just an inconsistency, which this patch hopes > to address. > > Signed-off-by: Leon Michalak <leonmichalak6@gmail.com> > --- > diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh > @@ -49,7 +49,53 @@ test_expect_success 'diff.context honored by "log"' ' > ! grep firstline output && > git config diff.context 8 && > git log -1 -p >output && > - grep "^ firstline" output > + grep "^ firstline" output && > + git config --unset diff.context > +' > + > +test_expect_success 'diff.context honored by "add"' ' > + git add -p >output && > + ! grep firstline output && > + git config diff.context 8 && > + git add -p >output && > + grep "^ firstline" output && > + git config --unset diff.context > +' Be aware that if any command in the &&-chain prior to `git config --unset` fails, then `git config --unset` itself will not be executed, hence the cleanup won't happen. The way to address this is to instead use `test_config` to configure the value since it will ensure that the value gets "unset" regardless of whether the test succeeds or fails: test_config diff.context 8 && The same comment applies to all the new tests. ^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH 1/3] add-patch: respect diff.context configuration 2025-05-05 9:18 ` [PATCH 1/3] add-patch: respect diff.context configuration Leon Michalak via GitGitGadget 2025-05-05 20:29 ` Eric Sunshine @ 2025-05-06 8:55 ` Phillip Wood 2025-05-06 17:29 ` Junio C Hamano 2 siblings, 0 replies; 77+ messages in thread From: Phillip Wood @ 2025-05-06 8:55 UTC (permalink / raw) To: Leon Michalak via GitGitGadget, git; +Cc: Leon Michalak, Eric Sunshine Hi Leon Thanks for working on this. I've left a few comments below but for your first submission this is very good. On 05/05/2025 10:18, Leon Michalak via GitGitGadget wrote: > From: Leon Michalak <leonmichalak6@gmail.com> > > This aims to teach relevant builtins (that take in `--patch`) to respect > the user's diff.context and diff.interHunkContext file configurations. This is a good summary of the intent of the patch. I'd maybe drop "This aims" and instead say that the various commands do not respect the config and this patch fixes that. > Since these are both UI options and `--patch` is designed for the end user, > I believe this was previously just an inconsistency, which this patch hopes > to address. > > Signed-off-by: Leon Michalak <leonmichalak6@gmail.com> > --- > add-interactive.c | 16 ++++++++++---- > add-patch.c | 6 ++++++ > t/t4055-diff-context.sh | 48 ++++++++++++++++++++++++++++++++++++++++- > 3 files changed, 65 insertions(+), 5 deletions(-) > > diff --git a/add-interactive.c b/add-interactive.c > index 97ff35b6f12a..ad12dc416598 100644 > --- a/add-interactive.c > +++ b/add-interactive.c > @@ -41,6 +41,8 @@ void init_add_i_state(struct add_i_state *s, struct repository *r) > const char *value; > > s->r = r; > + s->context = -1; > + s->interhunkcontext = -1; > > if (repo_config_get_value(r, "color.interactive", &value)) > s->use_color = -1; > @@ -78,6 +80,9 @@ void init_add_i_state(struct add_i_state *s, struct repository *r) > repo_config_get_string(r, "diff.algorithm", > &s->interactive_diff_algorithm); > > + repo_config_get_int(r, "diff.context", &s->context); > + repo_config_get_int(r, "diff.interHunkContext", &s->interhunkcontext); This looks good > @@ -1014,10 +1019,13 @@ static int run_diff(struct add_i_state *s, const struct pathspec *ps, > if (count > 0) { > struct child_process cmd = CHILD_PROCESS_INIT; > > - strvec_pushl(&cmd.args, "git", "diff", "-p", "--cached", > - oid_to_hex(!is_initial ? &oid : > - s->r->hash_algo->empty_tree), > - "--", NULL); > + strvec_pushl(&cmd.args, "git", "diff", "-p", "--cached", NULL); As we're running "git diff" here nothing needs to be changed because it reads all of the relevant config variables itself. This is why the existing code does not worry about adding "--algorithm". > diff --git a/add-patch.c b/add-patch.c > index 95c67d8c80c4..b43ca1600738 100644 > --- a/add-patch.c > +++ b/add-patch.c > @@ -415,6 +415,8 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps) > { > struct strvec args = STRVEC_INIT; > const char *diff_algorithm = s->s.interactive_diff_algorithm; > + int diff_context = s->s.context; > + int diff_interhunkcontext = s->s.interhunkcontext; > struct strbuf *plain = &s->plain, *colored = NULL; > struct child_process cp = CHILD_PROCESS_INIT; > char *p, *pend, *colored_p = NULL, *colored_pend = NULL, marker = '\0'; > @@ -424,6 +426,10 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps) > int res; > > strvec_pushv(&args, s->mode->diff_cmd); > + if (diff_context != -1) > + strvec_pushf(&args, "--unified=%i", diff_context); > + if (diff_interhunkcontext != -1) > + strvec_pushf(&args, "--inter-hunk-context=%i", diff_interhunkcontext); This is good - if the user has set diff.config or diff.interhunkcontext then we pass the appropriate values to "git diff-index" or "git diff-files" which unlike "git diff" do not read those config values themselves. > if (diff_algorithm) > strvec_pushf(&args, "--diff-algorithm=%s", diff_algorithm); > if (s->revision) { > diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh > index ec2804eea67c..9c024200ade7 100755 > --- a/t/t4055-diff-context.sh > +++ b/t/t4055-diff-context.sh I'm not sure this is the best home for the new tests. Normally tests related to the code in add-patch.c go into t3701-add-interactive.sh > @@ -49,7 +49,53 @@ test_expect_success 'diff.context honored by "log"' ' > ! grep firstline output && > git config diff.context 8 && > git log -1 -p >output && > - grep "^ firstline" output > + grep "^ firstline" output && > + git config --unset diff.context > +' > + > +test_expect_success 'diff.context honored by "add"' ' > + git add -p >output && > + ! grep firstline output && > + git config diff.context 8 && > + git add -p >output && > + grep "^ firstline" output && > + git config --unset diff.context Eric has already mentioned using "test_config", I would go one step further and say that as we're only running a single command we should use "git add -c diff.context=* add -p" to avoid having to change the on disk config at all. I would also recommend using "test_grep ..." rather than "grep ..." and "test_grep ! .." instead of "! grep ..." as they provide useful debugging information if the test fails. Given the way the code is structured with the config being read in add-patch.c I'm not sure how much value there is in testing all the different "-p" commands. It would however be very useful to check that "git -c diff.interhunkcontext=8 add -p" works as expected. I'll leave it there for now, I'll try and look at the other patches later today or maybe tomorrow. Best Wishes Phillip > +' > + > +test_expect_success 'diff.context honored by "commit"' ' > + ! git commit -p >output && > + ! grep firstline output && > + git config diff.context 8 && > + ! git commit -p >output && > + grep "^ firstline" output && > + git config --unset diff.context > +' > + > +test_expect_success 'diff.context honored by "checkout"' ' > + git checkout -p >output && > + ! grep firstline output && > + git config diff.context 8 && > + git checkout -p >output && > + grep "^ firstline" output && > + git config --unset diff.context > +' > + > +test_expect_success 'diff.context honored by "stash"' ' > + ! git stash -p >output && > + ! grep firstline output && > + git config diff.context 8 && > + ! git stash -p >output && > + grep "^ firstline" output && > + git config --unset diff.context > +' > + > +test_expect_success 'diff.context honored by "restore"' ' > + git restore -p >output && > + ! grep firstline output && > + git config diff.context 8 && > + git restore -p >output && > + grep "^ firstline" output && > + git config --unset diff.context > ' > > test_expect_success 'The -U option overrides diff.context' ' ^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH 1/3] add-patch: respect diff.context configuration 2025-05-05 9:18 ` [PATCH 1/3] add-patch: respect diff.context configuration Leon Michalak via GitGitGadget 2025-05-05 20:29 ` Eric Sunshine 2025-05-06 8:55 ` Phillip Wood @ 2025-05-06 17:29 ` Junio C Hamano 2 siblings, 0 replies; 77+ messages in thread From: Junio C Hamano @ 2025-05-06 17:29 UTC (permalink / raw) To: Leon Michalak via GitGitGadget; +Cc: git, Leon Michalak "Leon Michalak via GitGitGadget" <gitgitgadget@gmail.com> writes: > From: Leon Michalak <leonmichalak6@gmail.com> > > This aims to teach relevant builtins (that take in `--patch`) to respect > the user's diff.context and diff.interHunkContext file configurations. > > Since these are both UI options and `--patch` is designed for the end user, > I believe this was previously just an inconsistency, which this patch hopes > to address. The usual way to compose a log message of this project is to - Give an observation on how the current system works in the present tense (so no need to say "Currently X is Y", just "X is Y"), and discuss what you perceive as a problem in it. - Propose a solution (optional---often, problem description trivially leads to an obvious solution in reader's minds). - Give commands to the codebase to "become like so". in this order. "Various built-in commands that use add-patch infrastructure do not honor diff.context and diff.interHunkContext configuration variables." Would be the first sentence to explain the current situation. Follow that sentence with an explanation why it is a bad thing (i.e. it is possible that "do not honor" may be a deliberate design decision---perhaps the implementation of "add -p" is impossible if it has to honor diff.context that is set to 0; show evidence that no such deliberate design decision was there when the feature was introduced from the list archive, and say that not honoring the configuration is an inconvenient inconsistency). Such an explanation would be sufficient for readers to guess the "solution", so stopping there should be fine. Thanks. > @@ -1014,10 +1019,13 @@ static int run_diff(struct add_i_state *s, const struct pathspec *ps, > if (count > 0) { > struct child_process cmd = CHILD_PROCESS_INIT; > > - strvec_pushl(&cmd.args, "git", "diff", "-p", "--cached", > - oid_to_hex(!is_initial ? &oid : > - s->r->hash_algo->empty_tree), > - "--", NULL); > + strvec_pushl(&cmd.args, "git", "diff", "-p", "--cached", NULL); > + if (s->context != -1) > + strvec_pushf(&cmd.args, "--unified=%i", s->context); > + if (s->interhunkcontext != -1) > + strvec_pushf(&cmd.args, "--inter-hunk-context=%i", s->interhunkcontext); > + strvec_pushl(&cmd.args, oid_to_hex(!is_initial ? &oid : > + s->r->hash_algo->empty_tree), "--", NULL); OK. > +test_expect_success 'diff.context honored by "add"' ' > + git add -p >output && > + ! grep firstline output && test_grep ! firstline output ? > + git config diff.context 8 && > + git add -p >output && > + grep "^ firstline" output && Likewise. test_grep "^firstline" output ? > + git config --unset diff.context Not like this. Either use test_when_finished to arrange that this unset is run when you leave, or use test_config. test_expect_success title ' test_config diff.context 8 && do your tests ' will reset diff.conftext when the above piece is done. test_expect_success title ' test_when_finished "git config unset diff.context" && git config set diff.context 8 && do your tests ' is an equivalent. ^ permalink raw reply [flat|nested] 77+ messages in thread
* [PATCH 2/3] add-patch: add diff.context command line overrides 2025-05-05 9:18 [PATCH 0/3] Better support for customising context lines in --patch commands Leon Michalak via GitGitGadget 2025-05-05 9:18 ` [PATCH 1/3] add-patch: respect diff.context configuration Leon Michalak via GitGitGadget @ 2025-05-05 9:18 ` Leon Michalak via GitGitGadget 2025-05-05 9:49 ` Kristoffer Haugsbakk 2025-05-07 9:51 ` Phillip Wood 2025-05-05 9:18 ` [PATCH 3/3] add-interactive: add new "context" subcommand Leon Michalak via GitGitGadget 2025-05-10 13:46 ` [PATCH v2 0/4] Better support for customising context lines in --patch commands Leon Michalak via GitGitGadget 3 siblings, 2 replies; 77+ messages in thread From: Leon Michalak via GitGitGadget @ 2025-05-05 9:18 UTC (permalink / raw) To: git; +Cc: Leon Michalak, Leon Michalak From: Leon Michalak <leonmichalak6@gmail.com> This patch compliments `8b91eef812`, where builtins that accept `--patch` options now respect `diff.context` and `diff.interHunkContext` file configurations. In particular, this patch helps users who don't want to set persistent context configurations or just want a way to override them on a one-time basis, by allowing the relevant builtins to accept corresponding command line options that override the file configurations. This mimics `diff` which allows for both context file configuration and command line overrides. Signed-off-by: Leon Michalak <leonmichalak6@gmail.com> --- Documentation/git-add.adoc | 11 +++++++ Documentation/git-checkout.adoc | 11 +++++++ Documentation/git-commit.adoc | 11 +++++++ Documentation/git-reset.adoc | 11 +++++++ Documentation/git-restore.adoc | 11 +++++++ Documentation/git-stash.adoc | 11 +++++++ add-interactive.c | 19 +++++++++--- add-interactive.h | 17 +++++++++-- add-patch.c | 5 +-- builtin/add.c | 21 ++++++++++--- builtin/checkout.c | 28 +++++++++++++++-- builtin/commit.c | 15 ++++++++- builtin/reset.c | 16 ++++++++-- builtin/stash.c | 54 ++++++++++++++++++++++++++------- commit.h | 3 +- t/t4055-diff-context.sh | 30 ++++++++++++++++++ t/t9902-completion.sh | 2 ++ 17 files changed, 245 insertions(+), 31 deletions(-) diff --git a/Documentation/git-add.adoc b/Documentation/git-add.adoc index eba0b419ce50..e3a50706acd5 100644 --- a/Documentation/git-add.adoc +++ b/Documentation/git-add.adoc @@ -104,6 +104,17 @@ This effectively runs `add --interactive`, but bypasses the initial command menu and directly jumps to the `patch` subcommand. See ``Interactive mode'' for details. +`-U<n>`:: +`--unified=<n>`:: + Generate diffs with _<n>_ lines of context. Defaults to `diff.context` + or 3 if the config option is unset. Implies `--interactive/--patch`. + +`--inter-hunk-context=<n>`:: + Show the context between diff hunks, up to the specified _<number>_ + of lines, thereby fusing hunks that are close to each other. + Defaults to `diff.interHunkContext` or 0 if the config option + is unset. Implies `--interactive/--patch`. + `-e`:: `--edit`:: Open the diff vs. the index in an editor and let the user diff --git a/Documentation/git-checkout.adoc b/Documentation/git-checkout.adoc index a66c53a5cd1e..46286a6c8c9e 100644 --- a/Documentation/git-checkout.adoc +++ b/Documentation/git-checkout.adoc @@ -289,6 +289,17 @@ section of linkgit:git-add[1] to learn how to operate the `--patch` mode. Note that this option uses the no overlay mode by default (see also `--overlay`), and currently doesn't support overlay mode. +`-U<n>`:: +`--unified=<n>`:: + Generate diffs with _<n>_ lines of context. Defaults to `diff.context` + or 3 if the config option is unset. Implies `--patch`. + +`--inter-hunk-context=<n>`:: + Show the context between diff hunks, up to the specified _<number>_ + of lines, thereby fusing hunks that are close to each other. + Defaults to `diff.interHunkContext` or 0 if the config option + is unset. Implies `--patch`. + --ignore-other-worktrees:: `git checkout` refuses when the wanted branch is already checked out or otherwise in use by another worktree. This option makes diff --git a/Documentation/git-commit.adoc b/Documentation/git-commit.adoc index dc219025f1eb..c0b206aee9c9 100644 --- a/Documentation/git-commit.adoc +++ b/Documentation/git-commit.adoc @@ -76,6 +76,17 @@ OPTIONS which changes to commit. See linkgit:git-add[1] for details. +`-U<n>`:: +`--unified=<n>`:: + Generate diffs with _<n>_ lines of context. Defaults to `diff.context` + or 3 if the config option is unset. Implies `--interactive/--patch`. + +`--inter-hunk-context=<n>`:: + Show the context between diff hunks, up to the specified _<number>_ + of lines, thereby fusing hunks that are close to each other. + Defaults to `diff.interHunkContext` or 0 if the config option + is unset. Implies `--interactive/--patch`. + `-C <commit>`:: `--reuse-message=<commit>`:: Take an existing _<commit>_ object, and reuse the log message diff --git a/Documentation/git-reset.adoc b/Documentation/git-reset.adoc index 53ab88c5451c..6fcc86a3b960 100644 --- a/Documentation/git-reset.adoc +++ b/Documentation/git-reset.adoc @@ -125,6 +125,17 @@ OPTIONS separated with _NUL_ character and all other characters are taken literally (including newlines and quotes). +`-U<n>`:: +`--unified=<n>`:: + Generate diffs with _<n>_ lines of context. Defaults to `diff.context` + or 3 if the config option is unset. Implies `--patch`. + +`--inter-hunk-context=<n>`:: + Show the context between diff hunks, up to the specified _<number>_ + of lines, thereby fusing hunks that are close to each other. + Defaults to `diff.interHunkContext` or 0 if the config option + is unset. Implies `--patch`. + `--`:: Do not interpret any more arguments as options. diff --git a/Documentation/git-restore.adoc b/Documentation/git-restore.adoc index 877b7772e667..49d7e6b5a5c8 100644 --- a/Documentation/git-restore.adoc +++ b/Documentation/git-restore.adoc @@ -52,6 +52,17 @@ leave out at most one of _<rev-A>__ and _<rev-B>_, in which case it defaults to Mode" section of linkgit:git-add[1] to learn how to operate the `--patch` mode. +`-U<n>`:: +`--unified=<n>`:: + Generate diffs with _<n>_ lines of context. Defaults to `diff.context` + or 3 if the config option is unset. Implies `--patch`. + +`--inter-hunk-context=<n>`:: + Show the context between diff hunks, up to the specified _<number>_ + of lines, thereby fusing hunks that are close to each other. + Defaults to `diff.interHunkContext` or 0 if the config option + is unset. Implies `--patch`. + `-W`:: `--worktree`:: `-S`:: diff --git a/Documentation/git-stash.adoc b/Documentation/git-stash.adoc index 1a5177f4986c..24916fa0723e 100644 --- a/Documentation/git-stash.adoc +++ b/Documentation/git-stash.adoc @@ -208,6 +208,17 @@ to learn how to operate the `--patch` mode. The `--patch` option implies `--keep-index`. You can use `--no-keep-index` to override this. +`-U<n>`:: +`--unified=<n>`:: + Generate diffs with _<n>_ lines of context. Defaults to `diff.context` + or 3 if the config option is unset. Implies `--patch`. + +`--inter-hunk-context=<n>`:: + Show the context between diff hunks, up to the specified _<number>_ + of lines, thereby fusing hunks that are close to each other. + Defaults to `diff.interHunkContext` or 0 if the config option + is unset. Implies `--patch`. + -S:: --staged:: This option is only valid for `push` and `save` commands. diff --git a/add-interactive.c b/add-interactive.c index ad12dc416598..1ea8eb711a60 100644 --- a/add-interactive.c +++ b/add-interactive.c @@ -36,7 +36,8 @@ static void init_color(struct repository *r, struct add_i_state *s, free(key); } -void init_add_i_state(struct add_i_state *s, struct repository *r) +void init_add_i_state(struct add_i_state *s, struct repository *r, + struct add_p_opt *add_p_opt) { const char *value; @@ -86,6 +87,11 @@ void init_add_i_state(struct add_i_state *s, struct repository *r) repo_config_get_bool(r, "interactive.singlekey", &s->use_single_key); if (s->use_single_key) setbuf(stdin, NULL); + + if (add_p_opt->context != -1) + s->context = add_p_opt->context; + if (add_p_opt->interhunkcontext != -1) + s->interhunkcontext = add_p_opt->interhunkcontext; } void clear_add_i_state(struct add_i_state *s) @@ -974,6 +980,10 @@ static int run_patch(struct add_i_state *s, const struct pathspec *ps, opts->prompt = N_("Patch update"); count = list_and_choose(s, files, opts); if (count > 0) { + struct add_p_opt add_p_opt = { + .context = s->context, + .interhunkcontext = s->interhunkcontext, + }; struct strvec args = STRVEC_INIT; struct pathspec ps_selected = { 0 }; @@ -984,7 +994,7 @@ static int run_patch(struct add_i_state *s, const struct pathspec *ps, parse_pathspec(&ps_selected, PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL, PATHSPEC_LITERAL_PATH, "", args.v); - res = run_add_p(s->r, ADD_P_ADD, NULL, &ps_selected); + res = run_add_p(s->r, ADD_P_ADD, &add_p_opt, NULL, &ps_selected); strvec_clear(&args); clear_pathspec(&ps_selected); } @@ -1118,7 +1128,8 @@ static void command_prompt_help(struct add_i_state *s) _("(empty) select nothing")); } -int run_add_i(struct repository *r, const struct pathspec *ps) +int run_add_i(struct repository *r, const struct pathspec *ps, + struct add_p_opt *add_p_opt) { struct add_i_state s = { NULL }; struct print_command_item_data data = { "[", "]" }; @@ -1161,7 +1172,7 @@ int run_add_i(struct repository *r, const struct pathspec *ps) ->util = util; } - init_add_i_state(&s, r); + init_add_i_state(&s, r, add_p_opt); /* * When color was asked for, use the prompt color for diff --git a/add-interactive.h b/add-interactive.h index 693f125e8e4b..653f07a917b8 100644 --- a/add-interactive.h +++ b/add-interactive.h @@ -3,6 +3,13 @@ #include "color.h" +#define ADD_P_OPT_INIT { .context = -1, .interhunkcontext = -1 } + +struct add_p_opt { + int context; + int interhunkcontext; +}; + struct add_i_state { struct repository *r; int use_color; @@ -18,14 +25,17 @@ struct add_i_state { int use_single_key; char *interactive_diff_filter, *interactive_diff_algorithm; + int context, interhunkcontext; }; -void init_add_i_state(struct add_i_state *s, struct repository *r); +void init_add_i_state(struct add_i_state *s, struct repository *r, + struct add_p_opt *add_p_opt); void clear_add_i_state(struct add_i_state *s); struct repository; struct pathspec; -int run_add_i(struct repository *r, const struct pathspec *ps); +int run_add_i(struct repository *r, const struct pathspec *ps, + struct add_p_opt *add_p_opt); enum add_p_mode { ADD_P_ADD, @@ -36,6 +46,7 @@ enum add_p_mode { }; int run_add_p(struct repository *r, enum add_p_mode mode, - const char *revision, const struct pathspec *ps); + struct add_p_opt *o, const char *revision, + const struct pathspec *ps); #endif diff --git a/add-patch.c b/add-patch.c index b43ca1600738..c0d33820c558 100644 --- a/add-patch.c +++ b/add-patch.c @@ -1766,14 +1766,15 @@ soft_increment: } int run_add_p(struct repository *r, enum add_p_mode mode, - const char *revision, const struct pathspec *ps) + struct add_p_opt *o, const char *revision, + const struct pathspec *ps) { struct add_p_state s = { { r }, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT }; size_t i, binary_count = 0; - init_add_i_state(&s.s, r); + init_add_i_state(&s.s, r, o); if (mode == ADD_P_STASH) s.mode = &patch_mode_stash; diff --git a/builtin/add.c b/builtin/add.c index 78dfb2657767..9b4e618b3fe2 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -29,6 +29,7 @@ static const char * const builtin_add_usage[] = { NULL }; static int patch_interactive, add_interactive, edit_interactive; +static struct add_p_opt add_p_opt = ADD_P_OPT_INIT; static int take_worktree_changes; static int add_renormalize; static int pathspec_file_nul; @@ -157,7 +158,7 @@ static int refresh(struct repository *repo, int verbose, const struct pathspec * int interactive_add(struct repository *repo, const char **argv, const char *prefix, - int patch) + int patch, struct add_p_opt *add_p_opt) { struct pathspec pathspec; int ret; @@ -169,9 +170,10 @@ int interactive_add(struct repository *repo, prefix, argv); if (patch) - ret = !!run_add_p(repo, ADD_P_ADD, NULL, &pathspec); + ret = !!run_add_p(repo, ADD_P_ADD, add_p_opt, NULL, + &pathspec); else - ret = !!run_add_i(repo, &pathspec); + ret = !!run_add_i(repo, &pathspec, add_p_opt); clear_pathspec(&pathspec); return ret; @@ -253,6 +255,12 @@ static struct option builtin_add_options[] = { OPT_GROUP(""), OPT_BOOL('i', "interactive", &add_interactive, N_("interactive picking")), OPT_BOOL('p', "patch", &patch_interactive, N_("select hunks interactively")), + OPT_INTEGER_F('U', "unified", &add_p_opt.context, + N_("generate diffs with <n> lines context, implies --interactive/--patch"), + PARSE_OPT_NONEG), + OPT_INTEGER_F(0, "inter-hunk-context", &add_p_opt.interhunkcontext, + N_("show context between diff hunks up to the specified number of lines, implies --interactive/--patch"), + PARSE_OPT_NONEG), OPT_BOOL('e', "edit", &edit_interactive, N_("edit current diff and apply")), OPT__FORCE(&ignored_too, N_("allow adding otherwise ignored files"), 0), OPT_BOOL('u', "update", &take_worktree_changes, N_("update tracked files")), @@ -398,7 +406,12 @@ int cmd_add(int argc, die(_("options '%s' and '%s' cannot be used together"), "--dry-run", "--interactive/--patch"); if (pathspec_from_file) die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--interactive/--patch"); - exit(interactive_add(repo, argv + 1, prefix, patch_interactive)); + exit(interactive_add(repo, argv + 1, prefix, patch_interactive, &add_p_opt)); + } else { + if (add_p_opt.context != -1) + die(_("the option '%s' requires '%s'"), "--unified", "--interactive/--patch"); + if (add_p_opt.interhunkcontext != -1) + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--interactive/--patch"); } if (edit_interactive) { diff --git a/builtin/checkout.c b/builtin/checkout.c index d185982f3a63..22bd7b1d2b3b 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -61,6 +61,8 @@ static const char * const restore_usage[] = { struct checkout_opts { int patch_mode; + int patch_context; + int patch_interhunk_context; int quiet; int merge; int force; @@ -104,7 +106,12 @@ struct checkout_opts { struct tree *source_tree; }; -#define CHECKOUT_OPTS_INIT { .conflict_style = -1, .merge = -1 } +#define CHECKOUT_OPTS_INIT { \ + .conflict_style = -1, \ + .merge = -1, \ + .patch_context = -1, \ + .patch_interhunk_context = -1, \ +} struct branch_info { char *name; /* The short name used */ @@ -539,6 +546,10 @@ static int checkout_paths(const struct checkout_opts *opts, if (opts->patch_mode) { enum add_p_mode patch_mode; + struct add_p_opt add_p_opt = { + .context = opts->patch_context, + .interhunkcontext = opts->patch_interhunk_context, + }; const char *rev = new_branch_info->name; char rev_oid[GIT_MAX_HEXSZ + 1]; @@ -564,8 +575,13 @@ static int checkout_paths(const struct checkout_opts *opts, else BUG("either flag must have been set, worktree=%d, index=%d", opts->checkout_worktree, opts->checkout_index); - return !!run_add_p(the_repository, patch_mode, rev, - &opts->pathspec); + return !!run_add_p(the_repository, patch_mode, &add_p_opt, + rev, &opts->pathspec); + } else { + if (opts->patch_context != -1) + die(_("the option '%s' requires '%s'"), "--unified", "--patch"); + if (opts->patch_interhunk_context != -1) + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch"); } repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR); @@ -1738,6 +1754,12 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts, N_("checkout their version for unmerged files"), 3, PARSE_OPT_NONEG), OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")), + OPT_INTEGER_F('U', "unified", &opts->patch_context, + N_("generate diffs with <n> lines context, implies --patch"), + PARSE_OPT_NONEG), + OPT_INTEGER_F(0, "inter-hunk-context", &opts->patch_interhunk_context, + N_("show context between diff hunks up to the specified number of lines, implies --patch"), + PARSE_OPT_NONEG), 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), diff --git a/builtin/commit.c b/builtin/commit.c index 66bd91fd523d..5831fd019717 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -19,6 +19,7 @@ #include "environment.h" #include "diff.h" #include "commit.h" +#include "add-interactive.h" #include "gettext.h" #include "revision.h" #include "wt-status.h" @@ -122,6 +123,7 @@ static const char *edit_message, *use_message; static char *fixup_message, *fixup_commit, *squash_message; static const char *fixup_prefix; static int all, also, interactive, patch_interactive, only, amend, signoff; +static struct add_p_opt add_p_opt = ADD_P_OPT_INIT; 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 */ @@ -400,7 +402,7 @@ static const char *prepare_index(const char **argv, const char *prefix, old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT)); setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1); - if (interactive_add(the_repository, argv, prefix, patch_interactive) != 0) + if (interactive_add(the_repository, argv, prefix, patch_interactive, &add_p_opt) != 0) die(_("interactive add failed")); the_repository->index_file = old_repo_index_file; @@ -424,6 +426,11 @@ static const char *prepare_index(const char **argv, const char *prefix, commit_style = COMMIT_NORMAL; ret = get_lock_file_path(&index_lock); goto out; + } else { + if (add_p_opt.context != -1) + die(_("the option '%s' requires '%s'"), "--unified", "--interactive/--patch"); + if (add_p_opt.interhunkcontext != -1) + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--interactive/--patch"); } /* @@ -1722,6 +1729,12 @@ int cmd_commit(int argc, OPT_BOOL('i', "include", &also, N_("add specified files to index for commit")), OPT_BOOL(0, "interactive", &interactive, N_("interactively add files")), OPT_BOOL('p', "patch", &patch_interactive, N_("interactively add changes")), + OPT_INTEGER_F('U', "unified", &add_p_opt.context, + N_("generate diffs with <n> lines context, implies --interactive/--patch"), + PARSE_OPT_NONEG), + OPT_INTEGER_F(0, "inter-hunk-context", &add_p_opt.interhunkcontext, + N_("show context between diff hunks up to the specified number of lines, implies --interactive/--patch"), + PARSE_OPT_NONEG), OPT_BOOL('o', "only", &only, N_("commit only specified files")), OPT_BOOL('n', "no-verify", &no_verify, N_("bypass pre-commit and commit-msg hooks")), OPT_BOOL(0, "dry-run", &dry_run, N_("show what would be committed")), diff --git a/builtin/reset.c b/builtin/reset.c index 73b4537a9a56..ee39d6277381 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -346,6 +346,7 @@ int cmd_reset(int argc, struct object_id oid; struct pathspec pathspec; int intent_to_add = 0; + struct add_p_opt add_p_opt = ADD_P_OPT_INIT; const struct option options[] = { OPT__QUIET(&quiet, N_("be quiet, only report errors")), OPT_BOOL(0, "no-refresh", &no_refresh, @@ -370,6 +371,12 @@ int cmd_reset(int argc, PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater), OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")), + OPT_INTEGER_F('U', "unified", &add_p_opt.context, + N_("generate diffs with <n> lines context, implies --patch"), + PARSE_OPT_NONEG), + OPT_INTEGER_F(0, "inter-hunk-context", &add_p_opt.interhunkcontext, + N_("show context between diff hunks up to the specified number of lines, implies --patch"), + PARSE_OPT_NONEG), 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), @@ -424,9 +431,14 @@ int cmd_reset(int argc, if (reset_type != NONE) die(_("options '%s' and '%s' cannot be used together"), "--patch", "--{hard,mixed,soft}"); trace2_cmd_mode("patch-interactive"); - update_ref_status = !!run_add_p(the_repository, ADD_P_RESET, rev, - &pathspec); + update_ref_status = !!run_add_p(the_repository, ADD_P_RESET, + &add_p_opt, rev, &pathspec); goto cleanup; + } else { + if (add_p_opt.context != -1) + die(_("the option '%s' requires '%s'"), "--unified", "--patch"); + if (add_p_opt.interhunkcontext != -1) + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch"); } /* git reset tree [--] paths... can be used to diff --git a/builtin/stash.c b/builtin/stash.c index cfbd92852a65..dc82bc34b762 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -1242,7 +1242,8 @@ done: } static int stash_patch(struct stash_info *info, const struct pathspec *ps, - struct strbuf *out_patch, int quiet) + struct strbuf *out_patch, int quiet, + struct add_p_opt *add_p_opt) { int ret = 0; struct child_process cp_read_tree = CHILD_PROCESS_INIT; @@ -1267,7 +1268,7 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps, old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT)); setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1); - ret = !!run_add_p(the_repository, ADD_P_STASH, NULL, ps); + ret = !!run_add_p(the_repository, ADD_P_STASH, add_p_opt, NULL, ps); the_repository->index_file = old_repo_index_file; if (old_index_env && *old_index_env) @@ -1362,8 +1363,8 @@ done: } static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_buf, - int include_untracked, int patch_mode, int only_staged, - struct stash_info *info, struct strbuf *patch, + int include_untracked, int patch_mode, struct add_p_opt *add_p_opt, + int only_staged, struct stash_info *info, struct strbuf *patch, int quiet) { int ret = 0; @@ -1439,7 +1440,7 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b untracked_commit_option = 1; } if (patch_mode) { - ret = stash_patch(info, ps, patch, quiet); + ret = stash_patch(info, ps, patch, quiet, add_p_opt); if (ret < 0) { if (!quiet) fprintf_ln(stderr, _("Cannot save the current " @@ -1513,7 +1514,7 @@ static int create_stash(int argc, const char **argv, const char *prefix UNUSED, if (!check_changes_tracked_files(&ps)) return 0; - ret = do_create_stash(&ps, &stash_msg_buf, 0, 0, 0, &info, + ret = do_create_stash(&ps, &stash_msg_buf, 0, 0, NULL, 0, &info, NULL, 0); if (!ret) printf_ln("%s", oid_to_hex(&info.w_commit)); @@ -1524,7 +1525,8 @@ static int create_stash(int argc, const char **argv, const char *prefix UNUSED, } static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int quiet, - int keep_index, int patch_mode, int include_untracked, int only_staged) + int keep_index, int patch_mode, struct add_p_opt *add_p_opt, + int include_untracked, int only_staged) { int ret = 0; struct stash_info info = STASH_INFO_INIT; @@ -1594,8 +1596,8 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q if (stash_msg) strbuf_addstr(&stash_msg_buf, stash_msg); - if (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode, only_staged, - &info, &patch, quiet)) { + if (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode, + add_p_opt, only_staged, &info, &patch, quiet)) { ret = -1; goto done; } @@ -1768,6 +1770,7 @@ static int push_stash(int argc, const char **argv, const char *prefix, const char *stash_msg = NULL; char *pathspec_from_file = NULL; struct pathspec ps; + struct add_p_opt add_p_opt = ADD_P_OPT_INIT; struct option options[] = { OPT_BOOL('k', "keep-index", &keep_index, N_("keep index")), @@ -1775,6 +1778,12 @@ static int push_stash(int argc, const char **argv, const char *prefix, N_("stash staged changes only")), OPT_BOOL('p', "patch", &patch_mode, N_("stash in patch mode")), + OPT_INTEGER_F('U', "unified", &add_p_opt.context, + N_("generate diffs with <n> lines context, implies --patch"), + PARSE_OPT_NONEG), + OPT_INTEGER_F(0, "inter-hunk-context", &add_p_opt.interhunkcontext, + N_("show context between diff hunks up to the specified number of lines, implies --patch"), + PARSE_OPT_NONEG), OPT__QUIET(&quiet, N_("quiet mode")), OPT_BOOL('u', "include-untracked", &include_untracked, N_("include untracked files in stash")), @@ -1826,8 +1835,15 @@ static int push_stash(int argc, const char **argv, const char *prefix, die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file"); } + if (!patch_mode) { + if (add_p_opt.context != -1) + die(_("the option '%s' requires '%s'"), "--unified", "--patch"); + if (add_p_opt.interhunkcontext != -1) + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch"); + } + ret = do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode, - include_untracked, only_staged); + &add_p_opt, include_untracked, only_staged); clear_pathspec(&ps); free(pathspec_from_file); @@ -1852,6 +1868,7 @@ static int save_stash(int argc, const char **argv, const char *prefix, const char *stash_msg = NULL; struct pathspec ps; struct strbuf stash_msg_buf = STRBUF_INIT; + struct add_p_opt add_p_opt = ADD_P_OPT_INIT; struct option options[] = { OPT_BOOL('k', "keep-index", &keep_index, N_("keep index")), @@ -1859,6 +1876,12 @@ static int save_stash(int argc, const char **argv, const char *prefix, N_("stash staged changes only")), OPT_BOOL('p', "patch", &patch_mode, N_("stash in patch mode")), + OPT_INTEGER_F('U', "unified", &add_p_opt.context, + N_("generate diffs with <n> lines context, implies --patch"), + PARSE_OPT_NONEG), + OPT_INTEGER_F(0, "inter-hunk-context", &add_p_opt.interhunkcontext, + N_("show context between diff hunks up to the specified number of lines, implies --patch"), + PARSE_OPT_NONEG), OPT__QUIET(&quiet, N_("quiet mode")), OPT_BOOL('u', "include-untracked", &include_untracked, N_("include untracked files in stash")), @@ -1877,8 +1900,17 @@ static int save_stash(int argc, const char **argv, const char *prefix, stash_msg = strbuf_join_argv(&stash_msg_buf, argc, argv, ' '); memset(&ps, 0, sizeof(ps)); + + if (!patch_mode) { + if (add_p_opt.context != -1) + die(_("the option '%s' requires '%s'"), "--unified", "--patch"); + if (add_p_opt.interhunkcontext != -1) + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch"); + } + ret = do_push_stash(&ps, stash_msg, quiet, keep_index, - patch_mode, include_untracked, only_staged); + patch_mode, &add_p_opt, include_untracked, + only_staged); strbuf_release(&stash_msg_buf); return ret; diff --git a/commit.h b/commit.h index 70c870dae4d4..7a7fedbc2f14 100644 --- a/commit.h +++ b/commit.h @@ -2,6 +2,7 @@ #define COMMIT_H #include "object.h" +#include "add-interactive.h" struct signature_check; struct strbuf; @@ -257,7 +258,7 @@ int for_each_commit_graft(each_commit_graft_fn, void *); int interactive_add(struct repository *repo, const char **argv, const char *prefix, - int patch); + int patch, struct add_p_opt *add_p_opt); struct commit_extra_header { struct commit_extra_header *next; diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh index 9c024200ade7..1c1e62b434ec 100755 --- a/t/t4055-diff-context.sh +++ b/t/t4055-diff-context.sh @@ -104,6 +104,36 @@ test_expect_success 'The -U option overrides diff.context' ' ! grep "^ firstline" output ' +test_expect_success 'The -U option overrides diff.context for "add"' ' + git config diff.context 8 && + git add -U4 -p >output && + ! grep "^ firstline" output +' + +test_expect_success 'The -U option overrides diff.context for "commit"' ' + git config diff.context 8 && + ! git commit -U4 -p >output && + ! grep "^ firstline" output +' + +test_expect_success 'The -U option overrides diff.context for "checkout"' ' + git config diff.context 8 && + git checkout -U4 -p >output && + ! grep "^ firstline" output +' + +test_expect_success 'The -U option overrides diff.context for "stash"' ' + git config diff.context 8 && + ! git stash -U4 -p >output && + ! grep "^ firstline" output +' + +test_expect_success 'The -U option overrides diff.context for "restore"' ' + git config diff.context 8 && + git restore -U4 -p >output && + ! grep "^ firstline" output +' + test_expect_success 'diff.context honored by "diff"' ' git config diff.context 8 && git diff >output && diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh index 343b8cd1912b..6650d33fba69 100755 --- a/t/t9902-completion.sh +++ b/t/t9902-completion.sh @@ -2596,6 +2596,8 @@ test_expect_success 'double dash "git checkout"' ' --merge Z --conflict=Z --patch Z + --unified=Z + --inter-hunk-context=Z --ignore-skip-worktree-bits Z --ignore-other-worktrees Z --recurse-submodules Z -- gitgitgadget ^ permalink raw reply related [flat|nested] 77+ messages in thread
* Re: [PATCH 2/3] add-patch: add diff.context command line overrides 2025-05-05 9:18 ` [PATCH 2/3] add-patch: add diff.context command line overrides Leon Michalak via GitGitGadget @ 2025-05-05 9:49 ` Kristoffer Haugsbakk [not found] ` <CAP9jKjHj7WP91aKA9SE94zYj+naBGLUs99mF3G4BhTGcGjFDUQ@mail.gmail.com> 2025-05-07 9:51 ` Phillip Wood 1 sibling, 1 reply; 77+ messages in thread From: Kristoffer Haugsbakk @ 2025-05-05 9:49 UTC (permalink / raw) To: Josh Soref, git; +Cc: Leon Michalak On Mon, May 5, 2025, at 11:18, Leon Michalak via GitGitGadget wrote: > From: Leon Michalak <leonmichalak6@gmail.com> > > This patch compliments `8b91eef812`, where builtins that accept > `--patch` options now respect `diff.context` and `diff.interHunkContext` > file configurations. 8b91eef812 is patch 1. This hash will change once the patches have been imported via git-am(1). So it won’t make sense when these patches land as commits. I think the usual approach is to refer to a previous commit in the series as “in a previous commit we...”. Or maybe “in the previous commit” for this patch and “two commits ago” for patch 3. For commits that are in the stable history (like `master`) the convention is to use: git show -s --pretty=reference <commit> Without backticks (`). See SubmittingPatches, commit-reference. ^ permalink raw reply [flat|nested] 77+ messages in thread
[parent not found: <CAP9jKjHj7WP91aKA9SE94zYj+naBGLUs99mF3G4BhTGcGjFDUQ@mail.gmail.com>]
* Re: [PATCH 2/3] add-patch: add diff.context command line overrides [not found] ` <CAP9jKjHj7WP91aKA9SE94zYj+naBGLUs99mF3G4BhTGcGjFDUQ@mail.gmail.com> @ 2025-05-05 10:11 ` Leon Michalak 0 siblings, 0 replies; 77+ messages in thread From: Leon Michalak @ 2025-05-05 10:11 UTC (permalink / raw) To: Kristoffer Haugsbakk; +Cc: Josh Soref, git Thanks! Part of me did wonder if the reference would be updated when merging, but I can see now the correct way to approach this. This being my first ever contribution, I hope I am at least a little forgiven for any mistakes, which of course I'm more than happy to correct and learn from. On Mon, 5 May 2025 at 11:08, Leon Michalak <leonmichalak6@gmail.com> wrote: > > Thanks! Part of me did wonder if the reference would be updated when merging, but I can see now the correct way to approach this. > > This being my first ever contribution, I hope I am at least a little forgiven for any mistakes, which of course I can correct :) > > > On Mon, 5 May 2025, 10:50 Kristoffer Haugsbakk, <kristofferhaugsbakk@fastmail.com> wrote: >> >> On Mon, May 5, 2025, at 11:18, Leon Michalak via GitGitGadget wrote: >> > From: Leon Michalak <leonmichalak6@gmail.com> >> > >> > This patch compliments `8b91eef812`, where builtins that accept >> > `--patch` options now respect `diff.context` and `diff.interHunkContext` >> > file configurations. >> >> 8b91eef812 is patch 1. This hash will change once the patches have been >> imported via git-am(1). So it won’t make sense when these patches land >> as commits. >> >> I think the usual approach is to refer to a previous commit in the >> series as “in a previous commit we...”. Or maybe “in the previous >> commit” for this patch and “two commits ago” for patch 3. >> >> For commits that are in the stable history (like `master`) the >> convention is to use: >> >> git show -s --pretty=reference <commit> >> >> Without backticks (`). See SubmittingPatches, commit-reference. ^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH 2/3] add-patch: add diff.context command line overrides 2025-05-05 9:18 ` [PATCH 2/3] add-patch: add diff.context command line overrides Leon Michalak via GitGitGadget 2025-05-05 9:49 ` Kristoffer Haugsbakk @ 2025-05-07 9:51 ` Phillip Wood 2025-05-07 18:07 ` Junio C Hamano 1 sibling, 1 reply; 77+ messages in thread From: Phillip Wood @ 2025-05-07 9:51 UTC (permalink / raw) To: Leon Michalak via GitGitGadget, git; +Cc: Leon Michalak Hi Leon On 05/05/2025 10:18, Leon Michalak via GitGitGadget wrote: > From: Leon Michalak <leonmichalak6@gmail.com> > > This patch compliments `8b91eef812`, where builtins that accept > `--patch` options now respect `diff.context` and `diff.interHunkContext` > file configurations. > > In particular, this patch helps users who don't want to set persistent > context configurations or just want a way to override them on a one-time > basis, by allowing the relevant builtins to accept corresponding command > line options that override the file configurations. > > This mimics `diff` which allows for both context file configuration and > command line overrides. > > Signed-off-by: Leon Michalak <leonmichalak6@gmail.com> I'm somewhat torn about the name "--unified" as I don't think it is particularly informative - it only makes sense if one knows the diff option and we only support a single diff format. Having said that it is probably better to reuse the name from "git diff" for consistency. > +`-U<n>`:: > +`--unified=<n>`:: > + Generate diffs with _<n>_ lines of context. Defaults to `diff.context` > + or 3 if the config option is unset. Implies `--interactive/--patch`. > + > +`--inter-hunk-context=<n>`:: > + Show the context between diff hunks, up to the specified _<number>_ > + of lines, thereby fusing hunks that are close to each other. > + Defaults to `diff.interHunkContext` or 0 if the config option > + is unset. Implies `--interactive/--patch`. This documentation is repeated for each command. I think it would be better to put this in separate file that is then included where it is needed. That way if we need to update the documentation in the future we only have one copy to worry about. The syntax to include a file called diff-context-options.adoc is include::diff-context-options.adoc[] > --- a/add-interactive.c > +++ b/add-interactive.c > @@ -86,6 +87,11 @@ void init_add_i_state(struct add_i_state *s, struct repository *r) > repo_config_get_bool(r, "interactive.singlekey", &s->use_single_key); > if (s->use_single_key) > setbuf(stdin, NULL); > + > + if (add_p_opt->context != -1) > + s->context = add_p_opt->context; > + if (add_p_opt->interhunkcontext != -1) > + s->interhunkcontext = add_p_opt->interhunkcontext; This happens after we read the config so the command line option wins - good. > index 693f125e8e4b..653f07a917b8 100644 > --- a/add-interactive.h > +++ b/add-interactive.h > @@ -3,6 +3,13 @@ > > #include "color.h" > > +#define ADD_P_OPT_INIT { .context = -1, .interhunkcontext = -1 } I think we normally define initializer macros after the struct they initialize so the reader can more easily check the fields are all initialized correctly > +struct add_p_opt { > + int context; > + int interhunkcontext; > +}; > + > struct add_i_state { > struct repository *r; > int use_color; > @@ -18,14 +25,17 @@ struct add_i_state { > > int use_single_key; > char *interactive_diff_filter, *interactive_diff_algorithm; > + int context, interhunkcontext; Should this be in the previous patch? It is a good idea to run git rebase --keep-base --exec 'make && cd t && prove -j8 tests-that-you-think-might-fail :: --root=/dev/shm' before submitting a patch series so that you can be sure the patches all compile and the tests pass. Running the whole test suite can be pretty slow so just run the tests that are relevant to your changes. > @@ -169,9 +170,10 @@ int interactive_add(struct repository *repo, > prefix, argv); > > if (patch) > - ret = !!run_add_p(repo, ADD_P_ADD, NULL, &pathspec); > + ret = !!run_add_p(repo, ADD_P_ADD, add_p_opt, NULL, > + &pathspec); This line could be unwraped and still fit within 80 columns I think. > else > - ret = !!run_add_i(repo, &pathspec); > + ret = !!run_add_i(repo, &pathspec, add_p_opt); > > clear_pathspec(&pathspec); > return ret; > @@ -253,6 +255,12 @@ static struct option builtin_add_options[] = { > OPT_GROUP(""), > OPT_BOOL('i', "interactive", &add_interactive, N_("interactive picking")), > OPT_BOOL('p', "patch", &patch_interactive, N_("select hunks interactively")), > + OPT_INTEGER_F('U', "unified", &add_p_opt.context, > + N_("generate diffs with <n> lines context, implies --interactive/--patch"), It cannot imply both --interactive and --patch as they are two different operations. I think it should imply --patch because that option is supported by all the commands we're adding -U to. > + PARSE_OPT_NONEG), > + OPT_INTEGER_F(0, "inter-hunk-context", &add_p_opt.interhunkcontext, > + N_("show context between diff hunks up to the specified number of lines, implies --interactive/--patch"), > + PARSE_OPT_NONEG), As these two options are duplicated in many commands we should define a preprocessor macro for them in parse-options.h. If you look at the end of that file there are a bunch of similar definitions. > OPT_BOOL('e', "edit", &edit_interactive, N_("edit current diff and apply")), > OPT__FORCE(&ignored_too, N_("allow adding otherwise ignored files"), 0), > OPT_BOOL('u', "update", &take_worktree_changes, N_("update tracked files")), > @@ -398,7 +406,12 @@ int cmd_add(int argc, > die(_("options '%s' and '%s' cannot be used together"), "--dry-run", "--interactive/--patch"); > if (pathspec_from_file) > die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--interactive/--patch"); > - exit(interactive_add(repo, argv + 1, prefix, patch_interactive)); > + exit(interactive_add(repo, argv + 1, prefix, patch_interactive, &add_p_opt)); > + } else { > + if (add_p_opt.context != -1) > + die(_("the option '%s' requires '%s'"), "--unified", "--interactive/--patch"); > + if (add_p_opt.interhunkcontext != -1) > + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--interactive/--patch"); I don't understand this - I thought the help for --unified said it implied --patch but here it is erroring out if --patch is not given. Somewhere we should check that the values given for the context and interhunk context are non-negative. git_diff_ui_config() errors out if the context is negative and we should do the same in add-patch.c otherwise we'll get some wierd error about "git diff-index" failing. We should also think about -U0 as "git apply" rejects hunks without any context unless by default and I dont think "add -p" passes the option to enable that and I'm not sure it should. > +test_expect_success 'The -U option overrides diff.context for "add"' ' > + git config diff.context 8 && > + git add -U4 -p >output && > + ! grep "^ firstline" output > +' It is great that you've added tests and I do think we should test all the commands here as unlike the previous patch there isn't a common code path. My other comments about the tests in the previous patch apply here though I think. We should have a test to check negative context values and possibly zero are rejected. Thanks Phillip ^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH 2/3] add-patch: add diff.context command line overrides 2025-05-07 9:51 ` Phillip Wood @ 2025-05-07 18:07 ` Junio C Hamano 2025-05-07 18:28 ` Leon Michalak 0 siblings, 1 reply; 77+ messages in thread From: Junio C Hamano @ 2025-05-07 18:07 UTC (permalink / raw) To: Phillip Wood; +Cc: Leon Michalak via GitGitGadget, git, Leon Michalak Phillip Wood <phillip.wood123@gmail.com> writes: > >> +`-U<n>`:: >> +`--unified=<n>`:: >> + Generate diffs with _<n>_ lines of context. Defaults to `diff.context` >> + or 3 if the config option is unset. Implies `--interactive/--patch`. >> + >> +`--inter-hunk-context=<n>`:: >> + Show the context between diff hunks, up to the specified _<number>_ >> + of lines, thereby fusing hunks that are close to each other. >> + Defaults to `diff.interHunkContext` or 0 if the config option >> + is unset. Implies `--interactive/--patch`. > > This documentation is repeated for each command. I think it would be > better to put this in separate file that is then included where it is > needed. That way if we need to update the documentation in the future > we only have one copy to worry about. The syntax to include a file > called diff-context-options.adoc is > > include::diff-context-options.adoc[] Excellent suggestion. I however think "-U implies -i/-p" makes no sense at all. What if the user said "git add -U6"? Do we run "git add -i -U6" or go directly to "git add -p -U6"? I think it is better to encourage the user to be more explicit to require them to say "git revert -U6 -p" or "git add -U6 -i". If -U<n> is given and there is no "-i" (for "add" only) or "-p" (for everything), just error out *until* other modes of these commands find a good use of context width information, at which point we can use the context length information to drive these commands in their non-"patch" mode. Even if we cannot think of a way to use the diff output (and with configurable context length) in modes that are not "--patch" for these commands RIGHT NOW, that does not mean -U<n> would never become useful in these modes for the commands. Let's not close the door prematurely from our future developers by making -U<n> imply anything, and make it an error to use -U<N> without -p/-i for now. ^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH 2/3] add-patch: add diff.context command line overrides 2025-05-07 18:07 ` Junio C Hamano @ 2025-05-07 18:28 ` Leon Michalak 2025-05-07 20:25 ` Junio C Hamano 0 siblings, 1 reply; 77+ messages in thread From: Leon Michalak @ 2025-05-07 18:28 UTC (permalink / raw) To: Junio C Hamano; +Cc: Phillip Wood, Leon Michalak via GitGitGadget, git I think I may be misunderstanding, so I'll elaborate on what I personally intended my documentation to mean. When I put in the documentation "implies --interactive/--patch" it reads to me as "this assumes you are also using either --interactive or --patch and that if you don't specify one or the other it will do nothing or possibly error (which is what I chose in the end, based on the initial discussion in a separate thread)". I didn't think it would read as "you must have both settings" or "if you don't specify these the command will assume it as if you had and effectively act as if you had". I'm not sure if the wording was confusing or it generally has different meanings to others so perhaps that might clarify at least what I intended :) ^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH 2/3] add-patch: add diff.context command line overrides 2025-05-07 18:28 ` Leon Michalak @ 2025-05-07 20:25 ` Junio C Hamano 0 siblings, 0 replies; 77+ messages in thread From: Junio C Hamano @ 2025-05-07 20:25 UTC (permalink / raw) To: Leon Michalak; +Cc: Phillip Wood, Leon Michalak via GitGitGadget, git Leon Michalak <leonmichalak6@gmail.com> writes: > I think I may be misunderstanding, so I'll elaborate on what I > personally intended my documentation to mean. > > When I put in the documentation "implies --interactive/--patch" it > reads to me as "this assumes you are also using either --interactive > or --patch and that if you don't specify one or the other it will do > nothing or possibly error (which is what I chose in the end, based on > the initial discussion in a separate thread)". I didn't think it would > read as "you must have both settings" or "if you don't specify these > the command will assume it as if you had and effectively act as if you > had". > > I'm not sure if the wording was confusing or it generally has > different meanings to others so perhaps that might clarify at least > what I intended :) We use "imply" to mean quite a different thing from what you said in the above explanation, and that is where my reaction came from. We say "option A implies option B" only when the command behaves as if the user gave option B when only option A is given (and without giving B). For example, "git commit --help" has `--short`:: When doing a dry-run, give the output in the short-format. See linkgit:git-status[1] for details. Implies `--dry-run`. The `--short` option does not make any sense when you are actually creating a commit; if you say "git commit --short", the command behaves as if you said "git commit --dry-run --short". If something works only under certain condition, it seems we tend to say "this only works when/with ...", so in this case, we would have said "The -U<n> option is only effective for --interactive or --patch mode of this command" to express what you meant to convey, I think. Thanks. ^ permalink raw reply [flat|nested] 77+ messages in thread
* [PATCH 3/3] add-interactive: add new "context" subcommand 2025-05-05 9:18 [PATCH 0/3] Better support for customising context lines in --patch commands Leon Michalak via GitGitGadget 2025-05-05 9:18 ` [PATCH 1/3] add-patch: respect diff.context configuration Leon Michalak via GitGitGadget 2025-05-05 9:18 ` [PATCH 2/3] add-patch: add diff.context command line overrides Leon Michalak via GitGitGadget @ 2025-05-05 9:18 ` Leon Michalak via GitGitGadget 2025-05-06 0:02 ` Eric Sunshine 2025-05-10 13:46 ` [PATCH v2 0/4] Better support for customising context lines in --patch commands Leon Michalak via GitGitGadget 3 siblings, 1 reply; 77+ messages in thread From: Leon Michalak via GitGitGadget @ 2025-05-05 9:18 UTC (permalink / raw) To: git; +Cc: Leon Michalak, Leon Michalak From: Leon Michalak <leonmichalak6@gmail.com> This teaches `add/commit --interactive` a new "context" subcommand, which changes the amount of context lines subsequent subcommands like "patch" or "diff" generate in their diffs. Signed-off-by: Leon Michalak <leonmichalak6@gmail.com> --- Documentation/git-add.adoc | 10 ++++-- add-interactive.c | 72 +++++++++++++++++++++++++++++++++++++- t/t3701-add-interactive.sh | 36 ++++++++++++++++--- 3 files changed, 110 insertions(+), 8 deletions(-) diff --git a/Documentation/git-add.adoc b/Documentation/git-add.adoc index e3a50706acd5..1ab98c9b876e 100644 --- a/Documentation/git-add.adoc +++ b/Documentation/git-add.adoc @@ -265,14 +265,15 @@ and type return, like this: ------------ *** Commands *** 1: status 2: update 3: revert 4: add untracked - 5: patch 6: diff 7: quit 8: help + 5: patch 6: diff 7: context 8: quit + 9: help What now> 1 ------------ You also could say `s` or `sta` or `status` above as long as the choice is unique. -The main command loop has 6 subcommands (plus help and quit). +The main command loop has 7 subcommands (plus help and quit). status:: @@ -373,6 +374,11 @@ diff:: This lets you review what will be committed (i.e. between `HEAD` and index). +context:: + + This lets you change the amount of context lines shown in diffs that + the 'patch' and 'diff' subcommands generate. + EDITING PATCHES --------------- diff --git a/add-interactive.c b/add-interactive.c index 1ea8eb711a60..42b1bb8c5b64 100644 --- a/add-interactive.c +++ b/add-interactive.c @@ -20,6 +20,8 @@ #include "prompt.h" #include "tree.h" +static void choose_prompt_help_context(struct add_i_state *s); + static void init_color(struct repository *r, struct add_i_state *s, const char *section_and_slot, char *dst, const char *default_color) @@ -259,7 +261,8 @@ static void list(struct add_i_state *s, struct string_list *list, int *selected, opts->print_item(i, selected ? selected[i] : 0, list->items + i, opts->print_item_data); - if ((opts->columns) && ((i + 1) % (opts->columns))) { + if (i < list->nr - 1 && + (opts->columns) && ((i + 1) % (opts->columns))) { putchar('\t'); last_lf = 0; } @@ -1047,6 +1050,60 @@ static int run_diff(struct add_i_state *s, const struct pathspec *ps, return res; } +static int run_context(struct add_i_state *s, const struct pathspec *ps UNUSED, + struct prefix_item_list *files UNUSED, + struct list_and_choose_options *opts UNUSED) +{ + struct diff_options diffopts; + struct strbuf input = STRBUF_INIT; + int res = 0; + + repo_diff_setup(s->r, &diffopts); + + for (;;) { + int new_context; + char *endp; + + strbuf_reset(&input); + + color_fprintf(stdout, s->header_color, " %s:", N_("Current")); + fprintf(stdout, " %i\n", s->context == -1 ? + diffopts.context : s->context); + + color_fprintf(stdout, s->prompt_color, "%s", N_("Change context")); + fputs("> ", stdout); + fflush(stdout); + + if (git_read_line_interactively(&input) == EOF) { + putchar('\n'); + break; + } + + if (!input.len) + break; + + if (!strcmp(input.buf, "?")) { + choose_prompt_help_context(s); + continue; + } + + new_context = strtol(input.buf, &endp, 10); + if (*endp) { + color_fprintf_ln(stderr, s->error_color, + _("Context must be a numerical value")); + continue; + } + + s->context = new_context; + + break; + } + + strbuf_release(&input); + putchar('\n'); + return res; +} + static int run_help(struct add_i_state *s, const struct pathspec *ps UNUSED, struct prefix_item_list *files UNUSED, struct list_and_choose_options *opts UNUSED) @@ -1061,6 +1118,8 @@ static int run_help(struct add_i_state *s, const struct pathspec *ps UNUSED, _("pick hunks and update selectively")); color_fprintf_ln(stdout, s->help_color, "diff - %s", _("view diff between HEAD and index")); + color_fprintf_ln(stdout, s->help_color, "context - %s", + _("change how many context lines diffs are generated with")); color_fprintf_ln(stdout, s->help_color, "add untracked - %s", _("add contents of untracked files to the staged set of changes")); @@ -1087,6 +1146,16 @@ static void choose_prompt_help(struct add_i_state *s) _("(empty) finish selecting")); } +static void choose_prompt_help_context(struct add_i_state *s) +{ + color_fprintf_ln(stdout, s->help_color, "%s", + _("Prompt help:")); + color_fprintf_ln(stdout, s->help_color, "<n> - %s", + _("specify new context lines amount")); + color_fprintf_ln(stdout, s->help_color, " - %s", + _("(empty) finish selecting")); +} + typedef int (*command_t)(struct add_i_state *s, const struct pathspec *ps, struct prefix_item_list *files, struct list_and_choose_options *opts); @@ -1147,6 +1216,7 @@ int run_add_i(struct repository *r, const struct pathspec *ps, { "add untracked", run_add_untracked }, { "patch", run_patch }, { "diff", run_diff }, + { "context", run_context }, { "quit", NULL }, { "help", run_help }, }; diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index b8a05d95f3f1..9dcbc07d5876 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -758,16 +758,19 @@ test_expect_success 'colors can be overridden' ' <RED>*** Commands ***<RESET> 1: <YELLOW>s<RESET>tatus 2: <YELLOW>u<RESET>pdate 3: <YELLOW>r<RESET>evert 4: <YELLOW>a<RESET>dd untracked - 5: <YELLOW>p<RESET>atch 6: <YELLOW>d<RESET>iff 7: <YELLOW>q<RESET>uit 8: <YELLOW>h<RESET>elp + 5: <YELLOW>p<RESET>atch 6: <YELLOW>d<RESET>iff 7: <YELLOW>c<RESET>ontext 8: <YELLOW>q<RESET>uit + 9: <YELLOW>h<RESET>elp <YELLOW>What now<RESET>> <GREEN>status - show paths with changes<RESET> <GREEN>update - add working tree state to the staged set of changes<RESET> <GREEN>revert - revert staged set of changes back to the HEAD version<RESET> <GREEN>patch - pick hunks and update selectively<RESET> <GREEN>diff - view diff between HEAD and index<RESET> + <GREEN>context - change how many context lines diffs are generated with<RESET> <GREEN>add untracked - add contents of untracked files to the staged set of changes<RESET> <RED>*** Commands ***<RESET> 1: <YELLOW>s<RESET>tatus 2: <YELLOW>u<RESET>pdate 3: <YELLOW>r<RESET>evert 4: <YELLOW>a<RESET>dd untracked - 5: <YELLOW>p<RESET>atch 6: <YELLOW>d<RESET>iff 7: <YELLOW>q<RESET>uit 8: <YELLOW>h<RESET>elp + 5: <YELLOW>p<RESET>atch 6: <YELLOW>d<RESET>iff 7: <YELLOW>c<RESET>ontext 8: <YELLOW>q<RESET>uit + 9: <YELLOW>h<RESET>elp <YELLOW>What now<RESET>> Bye. EOF test_cmp expect actual && @@ -831,7 +834,8 @@ test_expect_success 'brackets appear without color' ' | |*** Commands *** | 1: [s]tatus 2: [u]pdate 3: [r]evert 4: [a]dd untracked - | 5: [p]atch 6: [d]iff 7: [q]uit 8: [h]elp + | 5: [p]atch 6: [d]iff 7: [c]ontext 8: [q]uit + | 9: [h]elp |What now> Bye. EOF @@ -1172,16 +1176,19 @@ test_expect_success 'show help from add--helper' ' <BOLD>*** Commands ***<RESET> 1: <BOLD;BLUE>s<RESET>tatus 2: <BOLD;BLUE>u<RESET>pdate 3: <BOLD;BLUE>r<RESET>evert 4: <BOLD;BLUE>a<RESET>dd untracked - 5: <BOLD;BLUE>p<RESET>atch 6: <BOLD;BLUE>d<RESET>iff 7: <BOLD;BLUE>q<RESET>uit 8: <BOLD;BLUE>h<RESET>elp + 5: <BOLD;BLUE>p<RESET>atch 6: <BOLD;BLUE>d<RESET>iff 7: <BOLD;BLUE>c<RESET>ontext 8: <BOLD;BLUE>q<RESET>uit + 9: <BOLD;BLUE>h<RESET>elp <BOLD;BLUE>What now<RESET>> <BOLD;RED>status - show paths with changes<RESET> <BOLD;RED>update - add working tree state to the staged set of changes<RESET> <BOLD;RED>revert - revert staged set of changes back to the HEAD version<RESET> <BOLD;RED>patch - pick hunks and update selectively<RESET> <BOLD;RED>diff - view diff between HEAD and index<RESET> + <BOLD;RED>context - change how many context lines diffs are generated with<RESET> <BOLD;RED>add untracked - add contents of untracked files to the staged set of changes<RESET> <BOLD>*** Commands ***<RESET> 1: <BOLD;BLUE>s<RESET>tatus 2: <BOLD;BLUE>u<RESET>pdate 3: <BOLD;BLUE>r<RESET>evert 4: <BOLD;BLUE>a<RESET>dd untracked - 5: <BOLD;BLUE>p<RESET>atch 6: <BOLD;BLUE>d<RESET>iff 7: <BOLD;BLUE>q<RESET>uit 8: <BOLD;BLUE>h<RESET>elp + 5: <BOLD;BLUE>p<RESET>atch 6: <BOLD;BLUE>d<RESET>iff 7: <BOLD;BLUE>c<RESET>ontext 8: <BOLD;BLUE>q<RESET>uit + 9: <BOLD;BLUE>h<RESET>elp <BOLD;BLUE>What now<RESET>>$SP Bye. EOF @@ -1230,4 +1237,23 @@ test_expect_success 'hunk splitting works with diff.suppressBlankEmpty' ' test_cmp expect actual ' +test_expect_success 'change context works' ' + git reset --hard && + cat >template <<-\EOF && + firstline + preline + TARGET + postline + lastline + EOF + sed "/TARGET/d" >x <template && + git update-index --add x && + git commit -m initial && + sed "s/TARGET/ADDED/" >x <template && + test_write_lines p 1 | git add -i >output && + grep firstline output && + test_write_lines c 0 p 1 | git add -i >output && + ! grep firstline output +' + test_done -- gitgitgadget ^ permalink raw reply related [flat|nested] 77+ messages in thread
* Re: [PATCH 3/3] add-interactive: add new "context" subcommand 2025-05-05 9:18 ` [PATCH 3/3] add-interactive: add new "context" subcommand Leon Michalak via GitGitGadget @ 2025-05-06 0:02 ` Eric Sunshine 2025-05-06 7:20 ` Leon Michalak 2025-05-06 16:37 ` Junio C Hamano 0 siblings, 2 replies; 77+ messages in thread From: Eric Sunshine @ 2025-05-06 0:02 UTC (permalink / raw) To: Leon Michalak via GitGitGadget; +Cc: git, Leon Michalak On Mon, May 5, 2025 at 5:19 AM Leon Michalak via GitGitGadget <gitgitgadget@gmail.com> wrote: > This teaches `add/commit --interactive` a new "context" subcommand, which > changes the amount of context lines subsequent subcommands like "patch" > or "diff" generate in their diffs. > > Signed-off-by: Leon Michalak <leonmichalak6@gmail.com> > --- > diff --git a/Documentation/git-add.adoc b/Documentation/git-add.adoc > @@ -265,14 +265,15 @@ and type return, like this: > ------------ > *** Commands *** > 1: status 2: update 3: revert 4: add untracked > - 5: patch 6: diff 7: quit 8: help > + 5: patch 6: diff 7: context 8: quit > + 9: help > What now> 1 I'm not a `git add/commit --interactive' user, but I can imagine that inserting "context" at 7 and bumping "quit" and "help" to 8 and 9, respectively, is going to play havoc with muscle memory people have built up over the years. To make this more friendly for existing users, I'd suggest adding this new command at the end of the list without changing the existing command numbers. Also, looking at this list, I can't help but think that "context" feels out of place among the other action-oriented commands. Moreover, if --interactive mode grows more configuration/setting-like commands in the future, do we really want to keep extending this menu for them? Specifically, I'm wondering if it would instead make sense to introduce a new item "9: settings" which takes the user to a "Settings" submenu from which the number of context lines can be set. > -The main command loop has 6 subcommands (plus help and quit). > +The main command loop has 7 subcommands (plus help and quit). Since you're touching this anyhow, let's fix this maintenance burden once and for all by writing more it generically, perhaps like this: The main command loop has several subcommands (plus help and quit). > +context:: > + > + This lets you change the amount of context lines shown in diffs that > + the 'patch' and 'diff' subcommands generate. s/amount/number/ > diff --git a/add-interactive.c b/add-interactive.c > @@ -1061,6 +1118,8 @@ static int run_help(struct add_i_state *s, const struct pathspec *ps UNUSED, > + color_fprintf_ln(stdout, s->help_color, "context - %s", > + _("change how many context lines diffs are generated with")); Perhaps: _("change the number of diff context lines")); > @@ -1087,6 +1146,16 @@ static void choose_prompt_help(struct add_i_state *s) > +static void choose_prompt_help_context(struct add_i_state *s) > +{ > + color_fprintf_ln(stdout, s->help_color, "%s", > + _("Prompt help:")); > + color_fprintf_ln(stdout, s->help_color, "<n> - %s", > + _("specify new context lines amount")); Likewise: _("change number of diff context lines")); > + color_fprintf_ln(stdout, s->help_color, " - %s", > + _("(empty) finish selecting")); "finish selecting" looks like a copy/paste error from elsewhere in this source file. Perhaps you meant something like: _("(empty) don't change number of context lines")); > diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh > @@ -1230,4 +1237,23 @@ test_expect_success 'hunk splitting works with diff.suppressBlankEmpty' ' > +test_expect_success 'change context works' ' > + git reset --hard && > + cat >template <<-\EOF && > + firstline > + preline > + TARGET > + postline > + lastline > + EOF > + sed "/TARGET/d" >x <template && > + git update-index --add x && > + git commit -m initial && > + sed "s/TARGET/ADDED/" >x <template && > + test_write_lines p 1 | git add -i >output && > + grep firstline output && > + test_write_lines c 0 p 1 | git add -i >output && > + ! grep firstline output > +' This script does have its share of bare `grep` invocations, but these days we prefer `test_grep`, which also appears often in this script, so the following would be more appropriate: test_grep firstline output && ... test_grep ! firstline output Note the placement of "!" when used with `test_grep`. ^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH 3/3] add-interactive: add new "context" subcommand 2025-05-06 0:02 ` Eric Sunshine @ 2025-05-06 7:20 ` Leon Michalak 2025-05-06 8:28 ` Christian Couder 2025-05-06 16:37 ` Junio C Hamano 1 sibling, 1 reply; 77+ messages in thread From: Leon Michalak @ 2025-05-06 7:20 UTC (permalink / raw) To: Eric Sunshine; +Cc: Leon Michalak via GitGitGadget, git Valid points, I don't think I have any objections to anything listed. Would it be recommended to update to test_grep (and test_config from previous message) in the same test files whilst I'm at it? Thanks for the review :) On Tue, 6 May 2025 at 01:02, Eric Sunshine <sunshine@sunshineco.com> wrote: > > On Mon, May 5, 2025 at 5:19 AM Leon Michalak via GitGitGadget > <gitgitgadget@gmail.com> wrote: > > This teaches `add/commit --interactive` a new "context" subcommand, which > > changes the amount of context lines subsequent subcommands like "patch" > > or "diff" generate in their diffs. > > > > Signed-off-by: Leon Michalak <leonmichalak6@gmail.com> > > --- > > diff --git a/Documentation/git-add.adoc b/Documentation/git-add.adoc > > @@ -265,14 +265,15 @@ and type return, like this: > > ------------ > > *** Commands *** > > 1: status 2: update 3: revert 4: add untracked > > - 5: patch 6: diff 7: quit 8: help > > + 5: patch 6: diff 7: context 8: quit > > + 9: help > > What now> 1 > > I'm not a `git add/commit --interactive' user, but I can imagine that > inserting "context" at 7 and bumping "quit" and "help" to 8 and 9, > respectively, is going to play havoc with muscle memory people have > built up over the years. To make this more friendly for existing > users, I'd suggest adding this new command at the end of the list > without changing the existing command numbers. > > Also, looking at this list, I can't help but think that "context" > feels out of place among the other action-oriented commands. Moreover, > if --interactive mode grows more configuration/setting-like commands > in the future, do we really want to keep extending this menu for them? > Specifically, I'm wondering if it would instead make sense to > introduce a new item "9: settings" which takes the user to a > "Settings" submenu from which the number of context lines can be set. > > > -The main command loop has 6 subcommands (plus help and quit). > > +The main command loop has 7 subcommands (plus help and quit). > > Since you're touching this anyhow, let's fix this maintenance burden > once and for all by writing more it generically, perhaps like this: > > The main command loop has several subcommands (plus help and quit). > > > +context:: > > + > > + This lets you change the amount of context lines shown in diffs that > > + the 'patch' and 'diff' subcommands generate. > > s/amount/number/ > > > diff --git a/add-interactive.c b/add-interactive.c > > @@ -1061,6 +1118,8 @@ static int run_help(struct add_i_state *s, const struct pathspec *ps UNUSED, > > + color_fprintf_ln(stdout, s->help_color, "context - %s", > > + _("change how many context lines diffs are generated with")); > > Perhaps: > > _("change the number of diff context lines")); > > > @@ -1087,6 +1146,16 @@ static void choose_prompt_help(struct add_i_state *s) > > +static void choose_prompt_help_context(struct add_i_state *s) > > +{ > > + color_fprintf_ln(stdout, s->help_color, "%s", > > + _("Prompt help:")); > > + color_fprintf_ln(stdout, s->help_color, "<n> - %s", > > + _("specify new context lines amount")); > > Likewise: > > _("change number of diff context lines")); > > > + color_fprintf_ln(stdout, s->help_color, " - %s", > > + _("(empty) finish selecting")); > > "finish selecting" looks like a copy/paste error from elsewhere in > this source file. Perhaps you meant something like: > > _("(empty) don't change number of context lines")); > > > diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh > > @@ -1230,4 +1237,23 @@ test_expect_success 'hunk splitting works with diff.suppressBlankEmpty' ' > > +test_expect_success 'change context works' ' > > + git reset --hard && > > + cat >template <<-\EOF && > > + firstline > > + preline > > + TARGET > > + postline > > + lastline > > + EOF > > + sed "/TARGET/d" >x <template && > > + git update-index --add x && > > + git commit -m initial && > > + sed "s/TARGET/ADDED/" >x <template && > > + test_write_lines p 1 | git add -i >output && > > + grep firstline output && > > + test_write_lines c 0 p 1 | git add -i >output && > > + ! grep firstline output > > +' > > This script does have its share of bare `grep` invocations, but these > days we prefer `test_grep`, which also appears often in this script, > so the following would be more appropriate: > > test_grep firstline output && > ... > test_grep ! firstline output > > Note the placement of "!" when used with `test_grep`. ^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH 3/3] add-interactive: add new "context" subcommand 2025-05-06 7:20 ` Leon Michalak @ 2025-05-06 8:28 ` Christian Couder 2025-05-06 17:07 ` Leon Michalak 0 siblings, 1 reply; 77+ messages in thread From: Christian Couder @ 2025-05-06 8:28 UTC (permalink / raw) To: Leon Michalak; +Cc: Eric Sunshine, Leon Michalak via GitGitGadget, git On Tue, May 6, 2025 at 9:37 AM Leon Michalak <leonmichalak6@gmail.com> wrote: > > Valid points, I don't think I have any objections to anything listed. Please don't "top post" and reply inline instead (see https://en.wikipedia.org/wiki/Posting_style) as it's the standard on this list. > Would it be recommended to update to test_grep (and test_config from > previous message) in the same test files whilst I'm at it? If you want to replace pre existing "grep" commands with "test_grep" and pre existing `git config` with `test_config`, I would recommend doing it in preparatory patches that go first in your patch series. Thanks! ^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH 3/3] add-interactive: add new "context" subcommand 2025-05-06 8:28 ` Christian Couder @ 2025-05-06 17:07 ` Leon Michalak 0 siblings, 0 replies; 77+ messages in thread From: Leon Michalak @ 2025-05-06 17:07 UTC (permalink / raw) To: Christian Couder; +Cc: Eric Sunshine, Leon Michalak via GitGitGadget, git On Tue, 6 May 2025 at 09:28, Christian Couder <christian.couder@gmail.com> wrote: > Please don't "top post" and reply inline instead (see > https://en.wikipedia.org/wiki/Posting_style) as it's the standard on > this list. Thanks, I rarely use email like this so overlooked this aspect. I hope this email is better? If not let me know. > If you want to replace pre existing "grep" commands with "test_grep" > and pre existing `git config` with `test_config`, I would recommend > doing it in preparatory patches that go first in your patch series. Yes that makes sense :) ^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH 3/3] add-interactive: add new "context" subcommand 2025-05-06 0:02 ` Eric Sunshine 2025-05-06 7:20 ` Leon Michalak @ 2025-05-06 16:37 ` Junio C Hamano 2025-05-06 17:25 ` Leon Michalak 2025-05-07 13:30 ` Phillip Wood 1 sibling, 2 replies; 77+ messages in thread From: Junio C Hamano @ 2025-05-06 16:37 UTC (permalink / raw) To: Eric Sunshine; +Cc: Leon Michalak via GitGitGadget, git, Leon Michalak Eric Sunshine <sunshine@sunshineco.com> writes: > On Mon, May 5, 2025 at 5:19 AM Leon Michalak via GitGitGadget > <gitgitgadget@gmail.com> wrote: >> This teaches `add/commit --interactive` a new "context" subcommand, which >> changes the amount of context lines subsequent subcommands like "patch" >> or "diff" generate in their diffs. >> >> Signed-off-by: Leon Michalak <leonmichalak6@gmail.com> >> --- >> diff --git a/Documentation/git-add.adoc b/Documentation/git-add.adoc >> @@ -265,14 +265,15 @@ and type return, like this: >> ------------ >> *** Commands *** >> 1: status 2: update 3: revert 4: add untracked >> - 5: patch 6: diff 7: quit 8: help >> + 5: patch 6: diff 7: context 8: quit >> + 9: help >> What now> 1 > > I'm not a `git add/commit --interactive' user, but I can imagine that > inserting "context" at 7 and bumping "quit" and "help" to 8 and 9, > respectively, is going to play havoc with muscle memory people have > built up over the years. To make this more friendly for existing > users, I'd suggest adding this new command at the end of the list > without changing the existing command numbers. Hmph, in the real UI, the first letters of these commands (which have deliberately been chosen to be unique) are shown in different color to hint that the answer to the prompt can be like 's' (for status). I would hate to see that quit/help, which are somewhat special, move from the very end position. Perhaps the example of the documentation page shown above should respond with 's', not '1' to show more prominently that it is possible and encouraged? I dunno. > Also, looking at this list, I can't help but think that "context" > feels out of place among the other action-oriented commands. Moreover, > if --interactive mode grows more configuration/setting-like commands > in the future, do we really want to keep extending this menu for them? That is a valid concern. > Specifically, I'm wondering if it would instead make sense to > introduce a new item "9: settings" which takes the user to a > "Settings" submenu from which the number of context lines can be set. 's' is taken for 'status', so it shouldn't be '9: settings' (it should come before 'quit' instead), but I think I know where you are going. An alternative that may work better is to allow subcommand specific parameters in the answer to the "What now>" prompt, e.g. What now> r -U7 to choose an equivalent to "git revert -p -U7". Thanks. ^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH 3/3] add-interactive: add new "context" subcommand 2025-05-06 16:37 ` Junio C Hamano @ 2025-05-06 17:25 ` Leon Michalak 2025-05-07 13:30 ` Phillip Wood 1 sibling, 0 replies; 77+ messages in thread From: Leon Michalak @ 2025-05-06 17:25 UTC (permalink / raw) To: Junio C Hamano; +Cc: Eric Sunshine, Leon Michalak via GitGitGadget, git On Tue, 6 May 2025 at 17:37, Junio C Hamano <gitster@pobox.com> wrote: > Hmph, in the real UI, the first letters of these commands (which > have deliberately been chosen to be unique) are shown in different > color to hint that the answer to the prompt can be like 's' (for > status). I would hate to see that quit/help, which are somewhat > special, move from the very end position. This is the main reason I chose to put the new subcommand before as I also feel quit/help are better suited to be last, although I do see the argument against muscle memory and is valid. > Perhaps the example of the documentation page shown above should > respond with 's', not '1' to show more prominently that it is > possible and encouraged? I dunno. Writing the first letters as opposed to the numbers is how I personally use this, just to add another perspective. Would "config" as opposed to "settings" fix the unique lettering problem? Or perhaps config is not the best description for what it would do (I sometimes find myself using config/settings interchangeably but the majority may not...)? Overall I feel the first two patches cover my use case the most so I am also not opposed to completely dropping the last patch about the `--interactive` context subcommand. Inherited diff context and command line overrides I feel are a great base to begin with either way. Just some thoughts :) ^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH 3/3] add-interactive: add new "context" subcommand 2025-05-06 16:37 ` Junio C Hamano 2025-05-06 17:25 ` Leon Michalak @ 2025-05-07 13:30 ` Phillip Wood 2025-05-07 19:10 ` Junio C Hamano 1 sibling, 1 reply; 77+ messages in thread From: Phillip Wood @ 2025-05-07 13:30 UTC (permalink / raw) To: Junio C Hamano, Eric Sunshine Cc: Leon Michalak via GitGitGadget, git, Leon Michalak On 06/05/2025 17:37, Junio C Hamano wrote: > > An alternative that may work better is to allow subcommand specific > parameters in the answer to the "What now>" prompt, e.g. > > What now> r -U7 > > to choose an equivalent to "git revert -p -U7". I think the best solution would be to allow users to re-display the current hunk with more context inside "add -p" rather than changing "add -i". That way a user who needs more context to make a decision on the current hunk can get it without restarting the whole process of selecting hunks [1]. If the user knows up front that they want a certain amount of context they can use "git add -p -U <context>" or "git add -i -U <context>" which was added in the preceding patch. Best Wishes Phillip [1] With "git add -p" they can quit and carry on from where they left off but "git stash -p" doesn't let you amend an existing stash and git refuses to pop a stash if the working tree is dirty. This means if a stash is split in two because the user exited to change the context they cannot pop both stashes on top of each other. ^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH 3/3] add-interactive: add new "context" subcommand 2025-05-07 13:30 ` Phillip Wood @ 2025-05-07 19:10 ` Junio C Hamano 0 siblings, 0 replies; 77+ messages in thread From: Junio C Hamano @ 2025-05-07 19:10 UTC (permalink / raw) To: Phillip Wood Cc: Eric Sunshine, Leon Michalak via GitGitGadget, git, Leon Michalak Phillip Wood <phillip.wood123@gmail.com> writes: > On 06/05/2025 17:37, Junio C Hamano wrote: >> An alternative that may work better is to allow subcommand specific >> parameters in the answer to the "What now>" prompt, e.g. >> What now> r -U7 >> to choose an equivalent to "git revert -p -U7". > > I think the best solution would be to allow users to re-display the > current hunk with more context inside "add -p" rather than changing > "add -i". That way a user who needs more context to make a decision on > the current hunk can get it without restarting the whole process of > selecting hunks [1]. If the user knows up front that they want a > certain amount of context they can use "git add -p -U <context>" or > "git add -i -U <context>" which was added in the preceding patch. Yes, but notice that the above is about interactive mode "add -i" that is setting the diff.context to 7 for subsequent "patch" command, not against "Stage this hunk [y,n,q,...]?" prompt inside "add -p". Once you go into the "--patch" mode, especially after you let the user to edit some hunks, "redisplay this hunk with wider context" is an impossible operation with the current code structure, I think, as the implementation does not keep track of information that are needed to do so. So, this is like "restarting 'add -p' with -U7", but done from the "add -i" session. ^ permalink raw reply [flat|nested] 77+ messages in thread
* [PATCH v2 0/4] Better support for customising context lines in --patch commands 2025-05-05 9:18 [PATCH 0/3] Better support for customising context lines in --patch commands Leon Michalak via GitGitGadget ` (2 preceding siblings ...) 2025-05-05 9:18 ` [PATCH 3/3] add-interactive: add new "context" subcommand Leon Michalak via GitGitGadget @ 2025-05-10 13:46 ` Leon Michalak via GitGitGadget 2025-05-10 13:46 ` [PATCH v2 1/4] test: refactor to use "test_grep" Leon Michalak via GitGitGadget ` (4 more replies) 3 siblings, 5 replies; 77+ messages in thread From: Leon Michalak via GitGitGadget @ 2025-05-10 13:46 UTC (permalink / raw) To: git Cc: Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Phillip Wood, Leon Michalak This series of patches attempt to give --interactive/--patch compatible builtins ("add", "commit", "checkout", "reset", "restore" and "stash") better support and nicer experience for configuring how many context lines are shown in diffs through a variety of ways. Prior to these patches, the user could not choose how many context lines they saw in --patch commands (apart from one workaround by using GIT_DIFF_OPTS=-u<number> ..., however this isn't a good user experience or a persistent solution). Additionally, the behaviour around reading from the diff.context and diff.interHunkContext configs was also inconsistent with other diff generating commands such as "log -p". The summarised changes below hopefully make this experience better and fix some inconsistencies: * diff.context and diff.interHunkContext configs are now respected by --patch compatible commands * --unified and --inter-hunk-context command line options have been added to --patch compatible commands (which take prescendence over file configs) * "add" and "commit" in --interactive mode now expose a new "context" subcommand which configures the amount of context lines you wish to see in subsequent diffs generated from other subcommands such as "patch" or "diff" The original discussion for this can be read at: * https://lore.kernel.org/git/CAP9jKjGb-Rcr=RLJEzeFdtrekYM+qmHy+1T1fykU3n9cV4GhGw@mail.gmail.com/ Changes since v1: * Update commit descriptions * Update tests to use the more modern and robust test_grep and test_config utils * Reword some documentation / user messages * Ensure each commit is atomic and builds/passes tests on it's own * Make new command line options DRY * Add tests for interhunk context interaction * Error if context config/command line options are negative * Drop previous last commit to do with new subcommand for --interactive add/commit. My motivations behind this patch series originally where quite simple, just for add-patch commands to respect context configs. This subcommand, after the discussion in v1, will require more thought and a larger implementation that what I had anticipated. I would prefer to leave this for another time as it's the least impactful but the most time intensive and complicated idea. Leon Michalak (4): test: refactor to use "test_grep" test: refactor to use "test_config" add-patch: respect diff.context configuration add-patch: add diff.context command line overrides Documentation/diff-context-options.adoc | 10 ++ Documentation/git-add.adoc | 2 + Documentation/git-checkout.adoc | 2 + Documentation/git-commit.adoc | 2 + Documentation/git-reset.adoc | 2 + Documentation/git-restore.adoc | 2 + Documentation/git-stash.adoc | 2 + add-interactive.c | 53 +++++++-- add-interactive.h | 17 ++- add-patch.c | 11 +- builtin/add.c | 16 ++- builtin/checkout.c | 24 +++- builtin/commit.c | 11 +- builtin/reset.c | 12 +- builtin/stash.c | 46 ++++++-- commit.h | 3 +- parse-options.h | 2 + t/t3701-add-interactive.sh | 48 ++++---- t/t4032-diff-inter-hunk-context.sh | 61 ++++++++++ t/t4055-diff-context.sh | 142 ++++++++++++++++++++---- t/t9902-completion.sh | 2 + 21 files changed, 390 insertions(+), 80 deletions(-) create mode 100644 Documentation/diff-context-options.adoc base-commit: f65182a99e545d2f2bc22e6c1c2da192133b16a3 Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1915%2FNinjaInShade%2Finteractive-patch-context-v2 Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1915/NinjaInShade/interactive-patch-context-v2 Pull-Request: https://github.com/gitgitgadget/git/pull/1915 Range-diff vs v1: -: ----------- > 1: 4f92a1b4c24 test: refactor to use "test_grep" -: ----------- > 2: 75424cb8e1c test: refactor to use "test_config" 1: 8b91eef8120 ! 3: f16d3de8611 add-patch: respect diff.context configuration @@ Metadata ## Commit message ## add-patch: respect diff.context configuration - This aims to teach relevant builtins (that take in `--patch`) to respect + Various builtins that use add-patch infrastructure do not respect the user's diff.context and diff.interHunkContext file configurations. - - Since these are both UI options and `--patch` is designed for the end user, - I believe this was previously just an inconsistency, which this patch hopes - to address. + This patch fixes this inconsistency. Signed-off-by: Leon Michalak <leonmichalak6@gmail.com> ## add-interactive.c ## -@@ add-interactive.c: void init_add_i_state(struct add_i_state *s, struct repository *r) +@@ add-interactive.c: static void init_color(struct repository *r, struct add_i_state *s, + void init_add_i_state(struct add_i_state *s, struct repository *r) + { const char *value; ++ int context; ++ int interhunkcontext; s->r = r; + s->context = -1; @@ add-interactive.c: void init_add_i_state(struct add_i_state *s, struct repositor repo_config_get_string(r, "diff.algorithm", &s->interactive_diff_algorithm); -+ repo_config_get_int(r, "diff.context", &s->context); -+ repo_config_get_int(r, "diff.interHunkContext", &s->interhunkcontext); ++ if (!repo_config_get_int(r, "diff.context", &context)) { ++ if (context < 0) ++ die(_("%s cannot be negative"), "diff.context"); ++ else ++ s->context = context; ++ }; ++ if (!repo_config_get_int(r, "diff.interHunkContext", &interhunkcontext)) { ++ if (interhunkcontext < 0) ++ die(_("%s cannot be negative"), "diff.interHunkContext"); ++ else ++ s->interhunkcontext = interhunkcontext; ++ }; + repo_config_get_bool(r, "interactive.singlekey", &s->use_single_key); if (s->use_single_key) setbuf(stdin, NULL); -@@ add-interactive.c: static int run_diff(struct add_i_state *s, const struct pathspec *ps, - if (count > 0) { - struct child_process cmd = CHILD_PROCESS_INIT; + + ## add-interactive.h ## +@@ add-interactive.h: struct add_i_state { + + int use_single_key; + char *interactive_diff_filter, *interactive_diff_algorithm; ++ int context, interhunkcontext; + }; -- strvec_pushl(&cmd.args, "git", "diff", "-p", "--cached", -- oid_to_hex(!is_initial ? &oid : -- s->r->hash_algo->empty_tree), -- "--", NULL); -+ strvec_pushl(&cmd.args, "git", "diff", "-p", "--cached", NULL); -+ if (s->context != -1) -+ strvec_pushf(&cmd.args, "--unified=%i", s->context); -+ if (s->interhunkcontext != -1) -+ strvec_pushf(&cmd.args, "--inter-hunk-context=%i", s->interhunkcontext); -+ strvec_pushl(&cmd.args, oid_to_hex(!is_initial ? &oid : -+ s->r->hash_algo->empty_tree), "--", NULL); - for (i = 0; i < files->items.nr; i++) - if (files->selected[i]) - strvec_push(&cmd.args, + void init_add_i_state(struct add_i_state *s, struct repository *r); ## add-patch.c ## @@ add-patch.c: static int parse_diff(struct add_p_state *s, const struct pathspec *ps) @@ add-patch.c: static int parse_diff(struct add_p_state *s, const struct pathspec ## t/t4055-diff-context.sh ## @@ t/t4055-diff-context.sh: test_expect_success 'diff.context honored by "log"' ' - ! grep firstline output && - git config diff.context 8 && - git log -1 -p >output && -- grep "^ firstline" output -+ grep "^ firstline" output && -+ git config --unset diff.context -+' -+ + test_grep "^ firstline" output + ' + +test_expect_success 'diff.context honored by "add"' ' + git add -p >output && -+ ! grep firstline output && -+ git config diff.context 8 && -+ git add -p >output && -+ grep "^ firstline" output && -+ git config --unset diff.context ++ test_grep ! firstline output && ++ test_config diff.context 8 && ++ git log -1 -p >output && ++ test_grep "^ firstline" output +' + +test_expect_success 'diff.context honored by "commit"' ' + ! git commit -p >output && -+ ! grep firstline output && -+ git config diff.context 8 && ++ test_grep ! firstline output && ++ test_config diff.context 8 && + ! git commit -p >output && -+ grep "^ firstline" output && -+ git config --unset diff.context ++ test_grep "^ firstline" output +' + +test_expect_success 'diff.context honored by "checkout"' ' + git checkout -p >output && -+ ! grep firstline output && -+ git config diff.context 8 && ++ test_grep ! firstline output && ++ test_config diff.context 8 && + git checkout -p >output && -+ grep "^ firstline" output && -+ git config --unset diff.context ++ test_grep "^ firstline" output +' + +test_expect_success 'diff.context honored by "stash"' ' + ! git stash -p >output && -+ ! grep firstline output && -+ git config diff.context 8 && ++ test_grep ! firstline output && ++ test_config diff.context 8 && + ! git stash -p >output && -+ grep "^ firstline" output && -+ git config --unset diff.context ++ test_grep "^ firstline" output +' + +test_expect_success 'diff.context honored by "restore"' ' + git restore -p >output && -+ ! grep firstline output && -+ git config diff.context 8 && ++ test_grep ! firstline output && ++ test_config diff.context 8 && + git restore -p >output && -+ grep "^ firstline" output && -+ git config --unset diff.context ++ test_grep "^ firstline" output ++' ++ + test_expect_success 'The -U option overrides diff.context' ' + test_config diff.context 8 && + git log -U4 -1 >output && +@@ t/t4055-diff-context.sh: test_expect_success 'negative integer config parsing' ' + test_grep "bad config variable" output ' - test_expect_success 'The -U option overrides diff.context' ' ++test_expect_success 'negative integer config parsing by "add"' ' ++ test_config diff.context -1 && ++ test_must_fail git add -p 2>output && ++ test_grep "diff.context cannot be negative" output ++' ++ ++test_expect_success 'negative integer config parsing by "commit"' ' ++ test_config diff.context -1 && ++ test_must_fail git commit -p 2>output && ++ test_grep "bad config variable" output ++' ++ ++test_expect_success 'negative integer config parsing by "checkout"' ' ++ test_config diff.context -1 && ++ test_must_fail git checkout -p 2>output && ++ test_grep "diff.context cannot be negative" output ++' ++ ++test_expect_success 'negative integer config parsing by "stash"' ' ++ test_config diff.context -1 && ++ test_must_fail git stash -p 2>output && ++ test_grep "diff.context cannot be negative" output ++' ++ ++test_expect_success 'negative integer config parsing by "restore"' ' ++ test_config diff.context -1 && ++ test_must_fail git restore -p 2>output && ++ test_grep "diff.context cannot be negative" output ++' ++ + test_expect_success '-U0 is valid, so is diff.context=0' ' + test_config diff.context 0 && + git diff >output && 2: 7700eb173e7 ! 4: 973dfadd1b3 add-patch: add diff.context command line overrides @@ Metadata ## Commit message ## add-patch: add diff.context command line overrides - This patch compliments `8b91eef812`, where builtins that accept - `--patch` options now respect `diff.context` and `diff.interHunkContext` - file configurations. + This patch compliments the previous commit, where builtins that use + add-patch infrastructure now respect diff.context and + diff.interHunkContext file configurations. In particular, this patch helps users who don't want to set persistent context configurations or just want a way to override them on a one-time basis, by allowing the relevant builtins to accept corresponding command line options that override the file configurations. - This mimics `diff` which allows for both context file configuration and - command line overrides. + This mimics commands such as diff and log, which allow for both context + file configuration and command line overrides. Signed-off-by: Leon Michalak <leonmichalak6@gmail.com> - ## Documentation/git-add.adoc ## -@@ Documentation/git-add.adoc: This effectively runs `add --interactive`, but bypasses the - initial command menu and directly jumps to the `patch` subcommand. - See ``Interactive mode'' for details. - + ## Documentation/diff-context-options.adoc (new) ## +@@ +`-U<n>`:: +`--unified=<n>`:: + Generate diffs with _<n>_ lines of context. Defaults to `diff.context` -+ or 3 if the config option is unset. Implies `--interactive/--patch`. ++ or 3 if the config option is unset. + +`--inter-hunk-context=<n>`:: + Show the context between diff hunks, up to the specified _<number>_ + of lines, thereby fusing hunks that are close to each other. + Defaults to `diff.interHunkContext` or 0 if the config option -+ is unset. Implies `--interactive/--patch`. ++ is unset. + + ## Documentation/git-add.adoc ## +@@ Documentation/git-add.adoc: This effectively runs `add --interactive`, but bypasses the + initial command menu and directly jumps to the `patch` subcommand. + See ``Interactive mode'' for details. + ++include::diff-context-options.adoc[] + `-e`:: `--edit`:: @@ Documentation/git-checkout.adoc: section of linkgit:git-add[1] to learn how to o Note that this option uses the no overlay mode by default (see also `--overlay`), and currently doesn't support overlay mode. -+`-U<n>`:: -+`--unified=<n>`:: -+ Generate diffs with _<n>_ lines of context. Defaults to `diff.context` -+ or 3 if the config option is unset. Implies `--patch`. -+ -+`--inter-hunk-context=<n>`:: -+ Show the context between diff hunks, up to the specified _<number>_ -+ of lines, thereby fusing hunks that are close to each other. -+ Defaults to `diff.interHunkContext` or 0 if the config option -+ is unset. Implies `--patch`. ++include::diff-context-options.adoc[] + --ignore-other-worktrees:: `git checkout` refuses when the wanted branch is already checked @@ Documentation/git-commit.adoc: OPTIONS which changes to commit. See linkgit:git-add[1] for details. -+`-U<n>`:: -+`--unified=<n>`:: -+ Generate diffs with _<n>_ lines of context. Defaults to `diff.context` -+ or 3 if the config option is unset. Implies `--interactive/--patch`. -+ -+`--inter-hunk-context=<n>`:: -+ Show the context between diff hunks, up to the specified _<number>_ -+ of lines, thereby fusing hunks that are close to each other. -+ Defaults to `diff.interHunkContext` or 0 if the config option -+ is unset. Implies `--interactive/--patch`. ++include::diff-context-options.adoc[] + `-C <commit>`:: `--reuse-message=<commit>`:: @@ Documentation/git-reset.adoc: OPTIONS separated with _NUL_ character and all other characters are taken literally (including newlines and quotes). -+`-U<n>`:: -+`--unified=<n>`:: -+ Generate diffs with _<n>_ lines of context. Defaults to `diff.context` -+ or 3 if the config option is unset. Implies `--patch`. -+ -+`--inter-hunk-context=<n>`:: -+ Show the context between diff hunks, up to the specified _<number>_ -+ of lines, thereby fusing hunks that are close to each other. -+ Defaults to `diff.interHunkContext` or 0 if the config option -+ is unset. Implies `--patch`. ++include::diff-context-options.adoc[] + `--`:: Do not interpret any more arguments as options. @@ Documentation/git-restore.adoc: leave out at most one of _<rev-A>__ and _<rev-B> Mode" section of linkgit:git-add[1] to learn how to operate the `--patch` mode. -+`-U<n>`:: -+`--unified=<n>`:: -+ Generate diffs with _<n>_ lines of context. Defaults to `diff.context` -+ or 3 if the config option is unset. Implies `--patch`. -+ -+`--inter-hunk-context=<n>`:: -+ Show the context between diff hunks, up to the specified _<number>_ -+ of lines, thereby fusing hunks that are close to each other. -+ Defaults to `diff.interHunkContext` or 0 if the config option -+ is unset. Implies `--patch`. ++include::diff-context-options.adoc[] + `-W`:: `--worktree`:: @@ Documentation/git-stash.adoc: to learn how to operate the `--patch` mode. The `--patch` option implies `--keep-index`. You can use `--no-keep-index` to override this. -+`-U<n>`:: -+`--unified=<n>`:: -+ Generate diffs with _<n>_ lines of context. Defaults to `diff.context` -+ or 3 if the config option is unset. Implies `--patch`. -+ -+`--inter-hunk-context=<n>`:: -+ Show the context between diff hunks, up to the specified _<number>_ -+ of lines, thereby fusing hunks that are close to each other. -+ Defaults to `diff.interHunkContext` or 0 if the config option -+ is unset. Implies `--patch`. ++include::diff-context-options.adoc[] + -S:: --staged:: @@ add-interactive.c: static void init_color(struct repository *r, struct add_i_sta + struct add_p_opt *add_p_opt) { const char *value; - + int context; @@ add-interactive.c: void init_add_i_state(struct add_i_state *s, struct repository *r) repo_config_get_bool(r, "interactive.singlekey", &s->use_single_key); if (s->use_single_key) setbuf(stdin, NULL); + -+ if (add_p_opt->context != -1) ++ if (add_p_opt->context != -1) { ++ if (add_p_opt->context < 0) ++ die(_("%s cannot be negative"), "--unified"); + s->context = add_p_opt->context; -+ if (add_p_opt->interhunkcontext != -1) ++ } ++ if (add_p_opt->interhunkcontext != -1) { ++ if (add_p_opt->interhunkcontext < 0) ++ die(_("%s cannot be negative"), "--inter-hunk-context"); + s->interhunkcontext = add_p_opt->interhunkcontext; ++ } } void clear_add_i_state(struct add_i_state *s) @@ add-interactive.c: static int run_patch(struct add_i_state *s, const struct path strvec_clear(&args); clear_pathspec(&ps_selected); } +@@ add-interactive.c: static int run_diff(struct add_i_state *s, const struct pathspec *ps, + if (count > 0) { + struct child_process cmd = CHILD_PROCESS_INIT; + +- strvec_pushl(&cmd.args, "git", "diff", "-p", "--cached", +- oid_to_hex(!is_initial ? &oid : +- s->r->hash_algo->empty_tree), +- "--", NULL); ++ strvec_pushl(&cmd.args, "git", "diff", "-p", "--cached", NULL); ++ if (s->context != -1) ++ strvec_pushf(&cmd.args, "--unified=%i", s->context); ++ if (s->interhunkcontext != -1) ++ strvec_pushf(&cmd.args, "--inter-hunk-context=%i", s->interhunkcontext); ++ strvec_pushl(&cmd.args, oid_to_hex(!is_initial ? &oid : ++ s->r->hash_algo->empty_tree), "--", NULL); + for (i = 0; i < files->items.nr; i++) + if (files->selected[i]) + strvec_push(&cmd.args, @@ add-interactive.c: static void command_prompt_help(struct add_i_state *s) _("(empty) select nothing")); } @@ add-interactive.h #include "color.h" -+#define ADD_P_OPT_INIT { .context = -1, .interhunkcontext = -1 } -+ +struct add_p_opt { + int context; + int interhunkcontext; +}; ++ ++#define ADD_P_OPT_INIT { .context = -1, .interhunkcontext = -1 } + struct add_i_state { struct repository *r; int use_color; @@ add-interactive.h: struct add_i_state { - - int use_single_key; - char *interactive_diff_filter, *interactive_diff_algorithm; -+ int context, interhunkcontext; + int context, interhunkcontext; }; -void init_add_i_state(struct add_i_state *s, struct repository *r); @@ builtin/add.c: int interactive_add(struct repository *repo, if (patch) - ret = !!run_add_p(repo, ADD_P_ADD, NULL, &pathspec); -+ ret = !!run_add_p(repo, ADD_P_ADD, add_p_opt, NULL, -+ &pathspec); ++ ret = !!run_add_p(repo, ADD_P_ADD, add_p_opt, NULL, &pathspec); else - ret = !!run_add_i(repo, &pathspec); + ret = !!run_add_i(repo, &pathspec, add_p_opt); @@ builtin/add.c: static struct option builtin_add_options[] = { OPT_GROUP(""), OPT_BOOL('i', "interactive", &add_interactive, N_("interactive picking")), OPT_BOOL('p', "patch", &patch_interactive, N_("select hunks interactively")), -+ OPT_INTEGER_F('U', "unified", &add_p_opt.context, -+ N_("generate diffs with <n> lines context, implies --interactive/--patch"), -+ PARSE_OPT_NONEG), -+ OPT_INTEGER_F(0, "inter-hunk-context", &add_p_opt.interhunkcontext, -+ N_("show context between diff hunks up to the specified number of lines, implies --interactive/--patch"), -+ PARSE_OPT_NONEG), ++ OPT_DIFF_UNIFIED(&add_p_opt.context), ++ OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext), OPT_BOOL('e', "edit", &edit_interactive, N_("edit current diff and apply")), OPT__FORCE(&ignored_too, N_("allow adding otherwise ignored files"), 0), OPT_BOOL('u', "update", &take_worktree_changes, N_("update tracked files")), @@ builtin/checkout.c: static struct option *add_checkout_path_options(struct check N_("checkout their version for unmerged files"), 3, PARSE_OPT_NONEG), OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")), -+ OPT_INTEGER_F('U', "unified", &opts->patch_context, -+ N_("generate diffs with <n> lines context, implies --patch"), -+ PARSE_OPT_NONEG), -+ OPT_INTEGER_F(0, "inter-hunk-context", &opts->patch_interhunk_context, -+ N_("show context between diff hunks up to the specified number of lines, implies --patch"), -+ PARSE_OPT_NONEG), ++ OPT_DIFF_UNIFIED(&opts->patch_context), ++ OPT_DIFF_INTERHUNK_CONTEXT(&opts->patch_interhunk_context), 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), @@ builtin/commit.c: int cmd_commit(int argc, OPT_BOOL('i', "include", &also, N_("add specified files to index for commit")), OPT_BOOL(0, "interactive", &interactive, N_("interactively add files")), OPT_BOOL('p', "patch", &patch_interactive, N_("interactively add changes")), -+ OPT_INTEGER_F('U', "unified", &add_p_opt.context, -+ N_("generate diffs with <n> lines context, implies --interactive/--patch"), -+ PARSE_OPT_NONEG), -+ OPT_INTEGER_F(0, "inter-hunk-context", &add_p_opt.interhunkcontext, -+ N_("show context between diff hunks up to the specified number of lines, implies --interactive/--patch"), -+ PARSE_OPT_NONEG), ++ OPT_DIFF_UNIFIED(&add_p_opt.context), ++ OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext), OPT_BOOL('o', "only", &only, N_("commit only specified files")), OPT_BOOL('n', "no-verify", &no_verify, N_("bypass pre-commit and commit-msg hooks")), OPT_BOOL(0, "dry-run", &dry_run, N_("show what would be committed")), @@ builtin/reset.c: int cmd_reset(int argc, PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater), OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")), -+ OPT_INTEGER_F('U', "unified", &add_p_opt.context, -+ N_("generate diffs with <n> lines context, implies --patch"), -+ PARSE_OPT_NONEG), -+ OPT_INTEGER_F(0, "inter-hunk-context", &add_p_opt.interhunkcontext, -+ N_("show context between diff hunks up to the specified number of lines, implies --patch"), -+ PARSE_OPT_NONEG), ++ OPT_DIFF_UNIFIED(&add_p_opt.context), ++ OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext), 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), @@ builtin/stash.c: static int push_stash(int argc, const char **argv, const char * N_("stash staged changes only")), OPT_BOOL('p', "patch", &patch_mode, N_("stash in patch mode")), -+ OPT_INTEGER_F('U', "unified", &add_p_opt.context, -+ N_("generate diffs with <n> lines context, implies --patch"), -+ PARSE_OPT_NONEG), -+ OPT_INTEGER_F(0, "inter-hunk-context", &add_p_opt.interhunkcontext, -+ N_("show context between diff hunks up to the specified number of lines, implies --patch"), -+ PARSE_OPT_NONEG), ++ OPT_DIFF_UNIFIED(&add_p_opt.context), ++ OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext), OPT__QUIET(&quiet, N_("quiet mode")), OPT_BOOL('u', "include-untracked", &include_untracked, N_("include untracked files in stash")), @@ builtin/stash.c: static int save_stash(int argc, const char **argv, const char * N_("stash staged changes only")), OPT_BOOL('p', "patch", &patch_mode, N_("stash in patch mode")), -+ OPT_INTEGER_F('U', "unified", &add_p_opt.context, -+ N_("generate diffs with <n> lines context, implies --patch"), -+ PARSE_OPT_NONEG), -+ OPT_INTEGER_F(0, "inter-hunk-context", &add_p_opt.interhunkcontext, -+ N_("show context between diff hunks up to the specified number of lines, implies --patch"), -+ PARSE_OPT_NONEG), ++ OPT_DIFF_UNIFIED(&add_p_opt.context), ++ OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext), OPT__QUIET(&quiet, N_("quiet mode")), OPT_BOOL('u', "include-untracked", &include_untracked, N_("include untracked files in stash")), @@ commit.h: int for_each_commit_graft(each_commit_graft_fn, void *); struct commit_extra_header { struct commit_extra_header *next; + ## parse-options.h ## +@@ parse-options.h: int parse_opt_tracking_mode(const struct option *, const char *, int); + #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")) + #define OPT_AUTOSTASH(v) OPT_BOOL(0, "autostash", v, N_("automatically stash/stash pop before and after")) ++#define OPT_DIFF_UNIFIED(v) OPT_INTEGER_F('U', "unified", v, N_("generate diffs with <n> lines context"), PARSE_OPT_NONEG) ++#define OPT_DIFF_INTERHUNK_CONTEXT(v) OPT_INTEGER_F(0, "inter-hunk-context", v, N_("show context between diff hunks up to the specified number of lines"), PARSE_OPT_NONEG) + + #define OPT_IPVERSION(v) \ + OPT_SET_INT_F('4', "ipv4", (v), N_("use IPv4 addresses only"), \ + + ## t/t4032-diff-inter-hunk-context.sh ## +@@ t/t4032-diff-inter-hunk-context.sh: t() { + " + } + ++t_patch() { ++ use_config= ++ git config --unset diff.interHunkContext ++ ++ case $# in ++ 4) hunks=$4; cmd="add -p -U$3";; ++ 5) hunks=$5; cmd="add -p -U$3 --inter-hunk-context=$4";; ++ 6) hunks=$5; cmd="add -p -U$3"; git config diff.interHunkContext $4; use_config="(diff.interHunkContext=$4) ";; ++ esac ++ label="$use_config$cmd, $1 common $2" ++ file=f$1 ++ ++ if ! test -f $file ++ then ++ f A $1 B >$file ++ git add $file ++ git commit -q -m. $file ++ f X $1 Y >$file ++ fi ++ ++ test_expect_success "$label: count hunks ($hunks)" " ++ test $(test_write_lines q | git $cmd $file | sed -n 's/^([0-9]*\/\([0-9]*\)) Stage this hunk.*/\1/p') = $hunks ++ " ++} ++ + cat <<EOF >expected.f1.0.1 || exit 1 + diff --git a/f1 b/f1 + --- a/f1 +@@ t/t4032-diff-inter-hunk-context.sh: t 3 lines 1 2 1 config + t 9 lines 3 2 2 config + t 9 lines 3 3 1 config + ++# common lines ctx intrctx hunks ++t_patch 1 line 0 2 ++t_patch 1 line 0 0 2 ++t_patch 1 line 0 1 1 ++t_patch 1 line 0 2 1 ++t_patch 1 line 1 1 ++ ++t_patch 2 lines 0 2 ++t_patch 2 lines 0 0 2 ++t_patch 2 lines 0 1 2 ++t_patch 2 lines 0 2 1 ++t_patch 2 lines 1 1 ++ ++t_patch 3 lines 1 2 ++t_patch 3 lines 1 0 2 ++t_patch 3 lines 1 1 1 ++t_patch 3 lines 1 2 1 ++ ++t_patch 9 lines 3 2 ++t_patch 9 lines 3 2 2 ++t_patch 9 lines 3 3 1 ++ ++# use diff.interHunkContext? ++t_patch 1 line 0 0 2 config ++t_patch 1 line 0 1 1 config ++t_patch 1 line 0 2 1 config ++t_patch 9 lines 3 3 1 config ++t_patch 2 lines 0 0 2 config ++t_patch 2 lines 0 1 2 config ++t_patch 2 lines 0 2 1 config ++t_patch 3 lines 1 0 2 config ++t_patch 3 lines 1 1 1 config ++t_patch 3 lines 1 2 1 config ++t_patch 9 lines 3 2 2 config ++t_patch 9 lines 3 3 1 config ++ + test_expect_success 'diff.interHunkContext invalid' ' + git config diff.interHunkContext asdf && + test_must_fail git diff && + ## t/t4055-diff-context.sh ## @@ t/t4055-diff-context.sh: test_expect_success 'The -U option overrides diff.context' ' - ! grep "^ firstline" output + test_grep ! "^ firstline" output ' +test_expect_success 'The -U option overrides diff.context for "add"' ' -+ git config diff.context 8 && ++ test_config diff.context 8 && + git add -U4 -p >output && -+ ! grep "^ firstline" output ++ test_grep ! "^ firstline" output +' + +test_expect_success 'The -U option overrides diff.context for "commit"' ' -+ git config diff.context 8 && ++ test_config diff.context 8 && + ! git commit -U4 -p >output && -+ ! grep "^ firstline" output ++ test_grep ! "^ firstline" output +' + +test_expect_success 'The -U option overrides diff.context for "checkout"' ' -+ git config diff.context 8 && ++ test_config diff.context 8 && + git checkout -U4 -p >output && -+ ! grep "^ firstline" output ++ test_grep ! "^ firstline" output +' + +test_expect_success 'The -U option overrides diff.context for "stash"' ' -+ git config diff.context 8 && ++ test_config diff.context 8 && + ! git stash -U4 -p >output && -+ ! grep "^ firstline" output ++ test_grep ! "^ firstline" output +' + +test_expect_success 'The -U option overrides diff.context for "restore"' ' -+ git config diff.context 8 && ++ test_config diff.context 8 && + git restore -U4 -p >output && -+ ! grep "^ firstline" output ++ test_grep ! "^ firstline" output +' + test_expect_success 'diff.context honored by "diff"' ' - git config diff.context 8 && + test_config diff.context 8 && git diff >output && ## t/t9902-completion.sh ## 3: b4b7854f330 < -: ----------- add-interactive: add new "context" subcommand -- gitgitgadget ^ permalink raw reply [flat|nested] 77+ messages in thread
* [PATCH v2 1/4] test: refactor to use "test_grep" 2025-05-10 13:46 ` [PATCH v2 0/4] Better support for customising context lines in --patch commands Leon Michalak via GitGitGadget @ 2025-05-10 13:46 ` Leon Michalak via GitGitGadget 2025-05-12 13:42 ` Junio C Hamano 2025-05-10 13:46 ` [PATCH v2 2/4] test: refactor to use "test_config" Leon Michalak via GitGitGadget ` (3 subsequent siblings) 4 siblings, 1 reply; 77+ messages in thread From: Leon Michalak via GitGitGadget @ 2025-05-10 13:46 UTC (permalink / raw) To: git Cc: Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Phillip Wood, Leon Michalak, Leon Michalak From: Leon Michalak <leonmichalak6@gmail.com> Refactor to use the modern "test_grep" test utility instead of regular "grep" which provides better debug information if tests fail. This is a prerequisite to the commits that follow which add to both test files. Signed-off-by: Leon Michalak <leonmichalak6@gmail.com> --- t/t3701-add-interactive.sh | 48 +++++++++++++++++++------------------- t/t4055-diff-context.sh | 28 +++++++++++----------- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index b8a05d95f3f1..b088ee141ff4 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -63,7 +63,7 @@ test_expect_success 'setup (initial)' ' ' test_expect_success 'status works (initial)' ' git add -i </dev/null >output && - grep "+1/-0 *+2/-0 file" output + test_grep "+1/-0 *+2/-0 file" output ' test_expect_success 'setup expected' ' @@ -86,7 +86,7 @@ test_expect_success 'revert works (initial)' ' git add file && test_write_lines r 1 | git add -i && git ls-files >output && - ! grep . output + test_grep ! . output ' test_expect_success 'add untracked (multiple)' ' @@ -109,7 +109,7 @@ test_expect_success 'setup (commit)' ' ' test_expect_success 'status works (commit)' ' git add -i </dev/null >output && - grep "+1/-0 *+2/-0 file" output + test_grep "+1/-0 *+2/-0 file" output ' test_expect_success 'update can stage deletions' ' @@ -141,7 +141,7 @@ test_expect_success 'revert works (commit)' ' git add file && test_write_lines r 1 | git add -i && git add -i </dev/null >output && - grep "unchanged *+3/-0 file" output + test_grep "unchanged *+3/-0 file" output ' test_expect_success 'reject multi-key input' ' @@ -185,7 +185,7 @@ test_expect_success 'setup fake editor' ' test_expect_success 'bad edit rejected' ' git reset && test_write_lines e n d | git add -p >output && - grep "hunk does not apply" output + test_grep "hunk does not apply" output ' test_expect_success 'setup patch' ' @@ -198,7 +198,7 @@ test_expect_success 'setup patch' ' test_expect_success 'garbage edit rejected' ' git reset && test_write_lines e n d | git add -p >output && - grep "hunk does not apply" output + test_grep "hunk does not apply" output ' test_expect_success 'setup patch' ' @@ -313,8 +313,8 @@ test_expect_success FILEMODE 'stage mode and hunk' ' chmod +x file && printf "y\\ny\\n" | git add -p && git diff --cached file >out && - grep "new mode" out && - grep "+content" out && + test_grep "new mode" out && + test_grep "+content" out && git diff file >out && test_must_be_empty out ' @@ -636,7 +636,7 @@ test_expect_success 'split hunk "add -p (edit)"' ' printf "%s\n" s e q n q q | EDITOR=: git add -p && git diff >actual && - ! grep "^+15" actual + test_grep ! "^+15" actual ' test_expect_success 'split hunk "add -p (no, yes, edit)"' ' @@ -648,7 +648,7 @@ test_expect_success 'split hunk "add -p (no, yes, edit)"' ' EDITOR=: git add -p 2>error && test_must_be_empty error && git diff >actual && - ! grep "^+31" actual + test_grep ! "^+31" actual ' test_expect_success 'split hunk with incomplete line at end' ' @@ -682,7 +682,7 @@ test_expect_success 'edit, adding lines to the first hunk' ' EDITOR=./fake_editor.sh git add -p 2>error && test_must_be_empty error && git diff --cached >actual && - grep "^+22" actual + test_grep "^+22" actual ' test_expect_success 'patch mode ignores unmerged entries' ' @@ -696,7 +696,7 @@ test_expect_success 'patch mode ignores unmerged entries' ' test_must_fail git merge side && echo changed >non-conflict.t && echo y | git add -p >output && - ! grep a/conflict.t output && + test_grep ! a/conflict.t output && cat >expected <<-\EOF && * Unmerged path conflict.t diff --git a/non-conflict.t b/non-conflict.t @@ -728,7 +728,7 @@ test_expect_success 'diffs can be colorized' ' # We do not want to depend on the exact coloring scheme # git uses for diffs, so just check that we saw some kind of color. - grep "$(printf "\\033")" output + test_grep "$(printf "\\033")" output ' test_expect_success 'colors can be overridden' ' @@ -743,7 +743,7 @@ test_expect_success 'colors can be overridden' ' -c color.interactive.error=blue \ add -i 2>err.raw <input && test_decode_color <err.raw >err && - grep "<BLUE>Huh (trigger)?<RESET>" err && + test_grep "<BLUE>Huh (trigger)?<RESET>" err && test_write_lines help quit >input && force_color git \ @@ -863,7 +863,7 @@ test_expect_success 'colorized diffs respect diff.wsErrorHighlight' ' printf y >y && force_color git -c diff.wsErrorHighlight=all add -p >output.raw 2>&1 <y && test_decode_color <output.raw >output && - grep "old<" output + test_grep "old<" output ' test_expect_success 'diffFilter filters diff' ' @@ -876,7 +876,7 @@ test_expect_success 'diffFilter filters diff' ' # avoid depending on the exact coloring or content of the prompts, # and just make sure we saw our diff prefixed - grep foo:.*content output + test_grep foo:.*content output ' test_expect_success 'detect bogus diffFilter output' ' @@ -886,7 +886,7 @@ test_expect_success 'detect bogus diffFilter output' ' test_config interactive.diffFilter "sed 6d" && printf y >y && force_color test_must_fail git add -p <y >output 2>&1 && - grep "mismatched output" output + test_grep "mismatched output" output ' test_expect_success 'handle iffy colored hunk headers' ' @@ -896,7 +896,7 @@ test_expect_success 'handle iffy colored hunk headers' ' printf n >n && force_color git -c interactive.diffFilter="sed s/.*@@.*/XX/" \ add -p >output 2>&1 <n && - grep "^XX$" output + test_grep "^XX$" output ' test_expect_success 'handle very large filtered diff' ' @@ -1002,7 +1002,7 @@ test_expect_success 'add -p does not expand argument lists' ' # update it, but we want to be sure that our "." pathspec # was not expanded into the argument list of any command. # So look only for "not-changed". - ! grep -E "^trace: (built-in|exec|run_command): .*not-changed" trace.out + test_grep ! -E "^trace: (built-in|exec|run_command): .*not-changed" trace.out ' test_expect_success 'hunk-editing handles custom comment char' ' @@ -1072,21 +1072,21 @@ test_expect_success 'setup different kinds of dirty submodules' ' test_expect_success 'status ignores dirty submodules (except HEAD)' ' git -C for-submodules add -i </dev/null >output && - grep dirty-head output && - grep dirty-both-ways output && - ! grep dirty-otherwise output + test_grep dirty-head output && + test_grep dirty-both-ways output && + test_grep ! dirty-otherwise output ' test_expect_success 'handle submodules' ' echo 123 >>for-submodules/dirty-otherwise/initial.t && force_color git -C for-submodules add -p dirty-otherwise >output 2>&1 && - grep "No changes" output && + test_grep "No changes" output && force_color git -C for-submodules add -p dirty-head >output 2>&1 <y && git -C for-submodules ls-files --stage dirty-head >actual && rev="$(git -C for-submodules/dirty-head rev-parse HEAD)" && - grep "$rev" actual + test_grep "$rev" actual ' test_expect_success 'set up pathological context' ' diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh index ec2804eea67c..c66f966a3ab3 100755 --- a/t/t4055-diff-context.sh +++ b/t/t4055-diff-context.sh @@ -38,36 +38,36 @@ test_expect_success 'setup' ' test_expect_success 'the default number of context lines is 3' ' git diff >output && - ! grep "^ d" output && - grep "^ e" output && - grep "^ j" output && - ! grep "^ k" output + test_grep ! "^ d" output && + test_grep "^ e" output && + test_grep "^ j" output && + test_grep ! "^ k" output ' test_expect_success 'diff.context honored by "log"' ' git log -1 -p >output && - ! grep firstline output && + test_grep ! firstline output && git config diff.context 8 && git log -1 -p >output && - grep "^ firstline" output + test_grep "^ firstline" output ' test_expect_success 'The -U option overrides diff.context' ' git config diff.context 8 && git log -U4 -1 >output && - ! grep "^ firstline" output + test_grep ! "^ firstline" output ' test_expect_success 'diff.context honored by "diff"' ' git config diff.context 8 && git diff >output && - grep "^ firstline" output + test_grep "^ firstline" output ' test_expect_success 'plumbing not affected' ' git config diff.context 8 && git diff-files -p >output && - ! grep "^ firstline" output + test_grep ! "^ firstline" output ' test_expect_success 'non-integer config parsing' ' @@ -85,8 +85,8 @@ test_expect_success 'negative integer config parsing' ' test_expect_success '-U0 is valid, so is diff.context=0' ' git config diff.context 0 && git diff >output && - grep "^-ADDED" output && - grep "^+MODIFIED" output + test_grep "^-ADDED" output && + test_grep "^+MODIFIED" output ' test_expect_success '-U2147483647 works' ' @@ -94,9 +94,9 @@ test_expect_success '-U2147483647 works' ' test_line_count = 16 x && git diff -U2147483647 >output && test_line_count = 22 output && - grep "^-ADDED" output && - grep "^+MODIFIED" output && - grep "^+APPENDED" output + test_grep "^-ADDED" output && + test_grep "^+MODIFIED" output && + test_grep "^+APPENDED" output ' test_done -- gitgitgadget ^ permalink raw reply related [flat|nested] 77+ messages in thread
* Re: [PATCH v2 1/4] test: refactor to use "test_grep" 2025-05-10 13:46 ` [PATCH v2 1/4] test: refactor to use "test_grep" Leon Michalak via GitGitGadget @ 2025-05-12 13:42 ` Junio C Hamano 2025-05-12 16:58 ` Leon Michalak 0 siblings, 1 reply; 77+ messages in thread From: Junio C Hamano @ 2025-05-12 13:42 UTC (permalink / raw) To: Leon Michalak via GitGitGadget Cc: git, Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Phillip Wood, Leon Michalak "Leon Michalak via GitGitGadget" <gitgitgadget@gmail.com> writes: > From: Leon Michalak <leonmichalak6@gmail.com> > > Refactor to use the modern "test_grep" test utility instead of regular > "grep" which provides better debug information if tests fail. > > This is a prerequisite to the commits that follow which add to both test > files. > > Signed-off-by: Leon Michalak <leonmichalak6@gmail.com> > --- These mostly look sensible, but I would title & phrase the commit description to 'use "test_grep"', not 'refactor to &'. It's shorter and more direct ;-) Thanks. ^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v2 1/4] test: refactor to use "test_grep" 2025-05-12 13:42 ` Junio C Hamano @ 2025-05-12 16:58 ` Leon Michalak 0 siblings, 0 replies; 77+ messages in thread From: Leon Michalak @ 2025-05-12 16:58 UTC (permalink / raw) To: Junio C Hamano Cc: Leon Michalak via GitGitGadget, git, Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Phillip Wood On Mon, 12 May 2025 at 14:42, Junio C Hamano <gitster@pobox.com> wrote: > These mostly look sensible, but I would title & phrase the commit > description to 'use "test_grep"', not 'refactor to &'. It's shorter > and more direct ;-) Thanks - will make sure to change that in v3 :) ^ permalink raw reply [flat|nested] 77+ messages in thread
* [PATCH v2 2/4] test: refactor to use "test_config" 2025-05-10 13:46 ` [PATCH v2 0/4] Better support for customising context lines in --patch commands Leon Michalak via GitGitGadget 2025-05-10 13:46 ` [PATCH v2 1/4] test: refactor to use "test_grep" Leon Michalak via GitGitGadget @ 2025-05-10 13:46 ` Leon Michalak via GitGitGadget 2025-05-10 13:46 ` [PATCH v2 3/4] add-patch: respect diff.context configuration Leon Michalak via GitGitGadget ` (2 subsequent siblings) 4 siblings, 0 replies; 77+ messages in thread From: Leon Michalak via GitGitGadget @ 2025-05-10 13:46 UTC (permalink / raw) To: git Cc: Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Phillip Wood, Leon Michalak, Leon Michalak From: Leon Michalak <leonmichalak6@gmail.com> Refactor to use the modern "test_config" test utility instead of manual "git config" as the former provides clean up on test completion. This is a prerequisite to the commits that follow which add to this test file. Signed-off-by: Leon Michalak <leonmichalak6@gmail.com> --- t/t4055-diff-context.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh index c66f966a3ab3..1384a8195705 100755 --- a/t/t4055-diff-context.sh +++ b/t/t4055-diff-context.sh @@ -47,43 +47,43 @@ test_expect_success 'the default number of context lines is 3' ' test_expect_success 'diff.context honored by "log"' ' git log -1 -p >output && test_grep ! firstline output && - git config diff.context 8 && + test_config diff.context 8 && git log -1 -p >output && test_grep "^ firstline" output ' test_expect_success 'The -U option overrides diff.context' ' - git config diff.context 8 && + test_config diff.context 8 && git log -U4 -1 >output && test_grep ! "^ firstline" output ' test_expect_success 'diff.context honored by "diff"' ' - git config diff.context 8 && + test_config diff.context 8 && git diff >output && test_grep "^ firstline" output ' test_expect_success 'plumbing not affected' ' - git config diff.context 8 && + test_config diff.context 8 && git diff-files -p >output && test_grep ! "^ firstline" output ' test_expect_success 'non-integer config parsing' ' - git config diff.context no && + test_config diff.context no && test_must_fail git diff 2>output && test_grep "bad numeric config value" output ' test_expect_success 'negative integer config parsing' ' - git config diff.context -1 && + test_config diff.context -1 && test_must_fail git diff 2>output && test_grep "bad config variable" output ' test_expect_success '-U0 is valid, so is diff.context=0' ' - git config diff.context 0 && + test_config diff.context 0 && git diff >output && test_grep "^-ADDED" output && test_grep "^+MODIFIED" output -- gitgitgadget ^ permalink raw reply related [flat|nested] 77+ messages in thread
* [PATCH v2 3/4] add-patch: respect diff.context configuration 2025-05-10 13:46 ` [PATCH v2 0/4] Better support for customising context lines in --patch commands Leon Michalak via GitGitGadget 2025-05-10 13:46 ` [PATCH v2 1/4] test: refactor to use "test_grep" Leon Michalak via GitGitGadget 2025-05-10 13:46 ` [PATCH v2 2/4] test: refactor to use "test_config" Leon Michalak via GitGitGadget @ 2025-05-10 13:46 ` Leon Michalak via GitGitGadget 2025-05-13 13:52 ` Phillip Wood 2025-05-10 13:46 ` [PATCH v2 4/4] add-patch: add diff.context command line overrides Leon Michalak via GitGitGadget 2025-06-28 16:34 ` [PATCH v3 0/4] Better support for customising context lines in --patch commands Leon Michalak via GitGitGadget 4 siblings, 1 reply; 77+ messages in thread From: Leon Michalak via GitGitGadget @ 2025-05-10 13:46 UTC (permalink / raw) To: git Cc: Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Phillip Wood, Leon Michalak, Leon Michalak From: Leon Michalak <leonmichalak6@gmail.com> Various builtins that use add-patch infrastructure do not respect the user's diff.context and diff.interHunkContext file configurations. This patch fixes this inconsistency. Signed-off-by: Leon Michalak <leonmichalak6@gmail.com> --- add-interactive.c | 17 ++++++++++ add-interactive.h | 1 + add-patch.c | 6 ++++ t/t4055-diff-context.sh | 70 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 94 insertions(+) diff --git a/add-interactive.c b/add-interactive.c index 97ff35b6f12a..cac036441caf 100644 --- a/add-interactive.c +++ b/add-interactive.c @@ -39,8 +39,12 @@ static void init_color(struct repository *r, struct add_i_state *s, void init_add_i_state(struct add_i_state *s, struct repository *r) { const char *value; + int context; + int interhunkcontext; s->r = r; + s->context = -1; + s->interhunkcontext = -1; if (repo_config_get_value(r, "color.interactive", &value)) s->use_color = -1; @@ -78,6 +82,19 @@ void init_add_i_state(struct add_i_state *s, struct repository *r) repo_config_get_string(r, "diff.algorithm", &s->interactive_diff_algorithm); + if (!repo_config_get_int(r, "diff.context", &context)) { + if (context < 0) + die(_("%s cannot be negative"), "diff.context"); + else + s->context = context; + }; + if (!repo_config_get_int(r, "diff.interHunkContext", &interhunkcontext)) { + if (interhunkcontext < 0) + die(_("%s cannot be negative"), "diff.interHunkContext"); + else + s->interhunkcontext = interhunkcontext; + }; + repo_config_get_bool(r, "interactive.singlekey", &s->use_single_key); if (s->use_single_key) setbuf(stdin, NULL); diff --git a/add-interactive.h b/add-interactive.h index 693f125e8e4b..c63f35b14be8 100644 --- a/add-interactive.h +++ b/add-interactive.h @@ -18,6 +18,7 @@ struct add_i_state { int use_single_key; char *interactive_diff_filter, *interactive_diff_algorithm; + int context, interhunkcontext; }; void init_add_i_state(struct add_i_state *s, struct repository *r); diff --git a/add-patch.c b/add-patch.c index 95c67d8c80c4..b43ca1600738 100644 --- a/add-patch.c +++ b/add-patch.c @@ -415,6 +415,8 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps) { struct strvec args = STRVEC_INIT; const char *diff_algorithm = s->s.interactive_diff_algorithm; + int diff_context = s->s.context; + int diff_interhunkcontext = s->s.interhunkcontext; struct strbuf *plain = &s->plain, *colored = NULL; struct child_process cp = CHILD_PROCESS_INIT; char *p, *pend, *colored_p = NULL, *colored_pend = NULL, marker = '\0'; @@ -424,6 +426,10 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps) int res; strvec_pushv(&args, s->mode->diff_cmd); + if (diff_context != -1) + strvec_pushf(&args, "--unified=%i", diff_context); + if (diff_interhunkcontext != -1) + strvec_pushf(&args, "--inter-hunk-context=%i", diff_interhunkcontext); if (diff_algorithm) strvec_pushf(&args, "--diff-algorithm=%s", diff_algorithm); if (s->revision) { diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh index 1384a8195705..c4b861c360cc 100755 --- a/t/t4055-diff-context.sh +++ b/t/t4055-diff-context.sh @@ -52,6 +52,46 @@ test_expect_success 'diff.context honored by "log"' ' test_grep "^ firstline" output ' +test_expect_success 'diff.context honored by "add"' ' + git add -p >output && + test_grep ! firstline output && + test_config diff.context 8 && + git log -1 -p >output && + test_grep "^ firstline" output +' + +test_expect_success 'diff.context honored by "commit"' ' + ! git commit -p >output && + test_grep ! firstline output && + test_config diff.context 8 && + ! git commit -p >output && + test_grep "^ firstline" output +' + +test_expect_success 'diff.context honored by "checkout"' ' + git checkout -p >output && + test_grep ! firstline output && + test_config diff.context 8 && + git checkout -p >output && + test_grep "^ firstline" output +' + +test_expect_success 'diff.context honored by "stash"' ' + ! git stash -p >output && + test_grep ! firstline output && + test_config diff.context 8 && + ! git stash -p >output && + test_grep "^ firstline" output +' + +test_expect_success 'diff.context honored by "restore"' ' + git restore -p >output && + test_grep ! firstline output && + test_config diff.context 8 && + git restore -p >output && + test_grep "^ firstline" output +' + test_expect_success 'The -U option overrides diff.context' ' test_config diff.context 8 && git log -U4 -1 >output && @@ -82,6 +122,36 @@ test_expect_success 'negative integer config parsing' ' test_grep "bad config variable" output ' +test_expect_success 'negative integer config parsing by "add"' ' + test_config diff.context -1 && + test_must_fail git add -p 2>output && + test_grep "diff.context cannot be negative" output +' + +test_expect_success 'negative integer config parsing by "commit"' ' + test_config diff.context -1 && + test_must_fail git commit -p 2>output && + test_grep "bad config variable" output +' + +test_expect_success 'negative integer config parsing by "checkout"' ' + test_config diff.context -1 && + test_must_fail git checkout -p 2>output && + test_grep "diff.context cannot be negative" output +' + +test_expect_success 'negative integer config parsing by "stash"' ' + test_config diff.context -1 && + test_must_fail git stash -p 2>output && + test_grep "diff.context cannot be negative" output +' + +test_expect_success 'negative integer config parsing by "restore"' ' + test_config diff.context -1 && + test_must_fail git restore -p 2>output && + test_grep "diff.context cannot be negative" output +' + test_expect_success '-U0 is valid, so is diff.context=0' ' test_config diff.context 0 && git diff >output && -- gitgitgadget ^ permalink raw reply related [flat|nested] 77+ messages in thread
* Re: [PATCH v2 3/4] add-patch: respect diff.context configuration 2025-05-10 13:46 ` [PATCH v2 3/4] add-patch: respect diff.context configuration Leon Michalak via GitGitGadget @ 2025-05-13 13:52 ` Phillip Wood 2025-05-13 15:47 ` Junio C Hamano 0 siblings, 1 reply; 77+ messages in thread From: Phillip Wood @ 2025-05-13 13:52 UTC (permalink / raw) To: Leon Michalak via GitGitGadget, git Cc: Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Leon Michalak Hi Leon On 10/05/2025 14:46, Leon Michalak via GitGitGadget wrote: > From: Leon Michalak <leonmichalak6@gmail.com> > > Various builtins that use add-patch infrastructure do not respect > the user's diff.context and diff.interHunkContext file configurations. We could expand this slightly by adding This is because the plumbing commands used by "git add -p" to generate the diff do not read those config settings. Fix this by reading the config before generating the patch and passing it along to the diff command with the "-U" and "--inter-hunk-context" command-line options. > This patch fixes this inconsistency. > > Signed-off-by: Leon Michalak <leonmichalak6@gmail.com> > --- > @@ -78,6 +82,19 @@ void init_add_i_state(struct add_i_state *s, struct repository *r) > repo_config_get_string(r, "diff.algorithm", > &s->interactive_diff_algorithm); > > + if (!repo_config_get_int(r, "diff.context", &context)) { > + if (context < 0) > + die(_("%s cannot be negative"), "diff.context"); > + else > + s->context = context; > + }; > + if (!repo_config_get_int(r, "diff.interHunkContext", &interhunkcontext)) { > + if (interhunkcontext < 0) > + die(_("%s cannot be negative"), "diff.interHunkContext"); > + else > + s->interhunkcontext = interhunkcontext; > + }; Thanks for changing this. This iteration of the code changes looks good > diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh > index 1384a8195705..c4b861c360cc 100755 > --- a/t/t4055-diff-context.sh > +++ b/t/t4055-diff-context.sh > @@ -52,6 +52,46 @@ test_expect_success 'diff.context honored by "log"' ' > test_grep "^ firstline" output > ' It's great that you have written tests for this patch but as I said last time I think the new tests should be in t3701-add-interactive.sh as we're interested in testing whether "git add -p" passes on diff.context to "git diff" , not whether "git diff" respects diff.context. I still think there are too many tests here as we know that all the different "-p" commands share a single code path. Our test suite is slow enough already so we do not want to add new tests that do not increase our code coverage. I would suggest removing these tests and instead add the following in t3701 test_expect_success 'add -p respects diff.context' ' test_write_lines a b c d e f g h i j k l m >file && git add file && test_write_lines a b c d e f G h i j k l m >file && echo y | git -c diff.context=5 add -p >actual && test_grep "@@ -2,11 +2,11 @@" actual ' test_expect_success 'add -p respects diff.interHunkContext' ' test_write_lines a b c d e f g h i j k l m n o p q r s >file && git add file && test_write_lines a b c d E f g i i j k l m N o p q r s >file && echo y | git -c diff.interhunkcontext=2 add -p >actual && test_grep "@@ -2,16 +2,16 @@" actual ' > +test_expect_success 'negative integer config parsing by "add"' ' Perhaps "add -p rejects negative diff.context" would be clearer? > + test_config diff.context -1 && > + test_must_fail git add -p 2>output && > + test_grep "diff.context cannot be negative" output > +' This is great but again we only need to test a single command and we should do so in t3701. We should also check that negative values of diff.interHunkContext are also rejected. Best Wishes Phillip ^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v2 3/4] add-patch: respect diff.context configuration 2025-05-13 13:52 ` Phillip Wood @ 2025-05-13 15:47 ` Junio C Hamano 2025-05-14 15:13 ` Phillip Wood 0 siblings, 1 reply; 77+ messages in thread From: Junio C Hamano @ 2025-05-13 15:47 UTC (permalink / raw) To: Phillip Wood Cc: Leon Michalak via GitGitGadget, git, Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Leon Michalak Phillip Wood <phillip.wood123@gmail.com> writes: > Hi Leon > > On 10/05/2025 14:46, Leon Michalak via GitGitGadget wrote: >> From: Leon Michalak <leonmichalak6@gmail.com> >> Various builtins that use add-patch infrastructure do not respect >> the user's diff.context and diff.interHunkContext file configurations. > > We could expand this slightly by adding > > This is because the plumbing commands used by "git add -p" to generate > the diff do not read those config settings. Fix this by reading the > config before generating the patch and passing it along to the diff > command with the "-U" and "--inter-hunk-context" command-line options. > >> This patch fixes this inconsistency. >> Signed-off-by: Leon Michalak <leonmichalak6@gmail.com> >> --- > >> @@ -78,6 +82,19 @@ void init_add_i_state(struct add_i_state *s, struct repository *r) >> repo_config_get_string(r, "diff.algorithm", >> &s->interactive_diff_algorithm); >> + if (!repo_config_get_int(r, "diff.context", &context)) { >> + if (context < 0) >> + die(_("%s cannot be negative"), "diff.context"); >> + else >> + s->context = context; >> + }; >> + if (!repo_config_get_int(r, "diff.interHunkContext", &interhunkcontext)) { >> + if (interhunkcontext < 0) >> + die(_("%s cannot be negative"), "diff.interHunkContext"); >> + else >> + s->interhunkcontext = interhunkcontext; >> + }; > > Thanks for changing this. This iteration of the code changes looks good Lose the ';' (semicolon) after closing {brace}s. This is C; you do not need an empty statement after a {block}. Everything in your review I am very happy to see. Thanks for giving a great review. ^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v2 3/4] add-patch: respect diff.context configuration 2025-05-13 15:47 ` Junio C Hamano @ 2025-05-14 15:13 ` Phillip Wood 2025-05-15 12:58 ` Junio C Hamano 0 siblings, 1 reply; 77+ messages in thread From: Phillip Wood @ 2025-05-14 15:13 UTC (permalink / raw) To: Junio C Hamano Cc: Leon Michalak via GitGitGadget, git, Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Leon Michalak On 13/05/2025 16:47, Junio C Hamano wrote: > Phillip Wood <phillip.wood123@gmail.com> writes: >> On 10/05/2025 14:46, Leon Michalak via GitGitGadget wrote: >> >>> @@ -78,6 +82,19 @@ void init_add_i_state(struct add_i_state *s, struct repository *r) >>> repo_config_get_string(r, "diff.algorithm", >>> &s->interactive_diff_algorithm); >>> + if (!repo_config_get_int(r, "diff.context", &context)) { >>> + if (context < 0) >>> + die(_("%s cannot be negative"), "diff.context"); >>> + else >>> + s->context = context; >>> + }; >>> + if (!repo_config_get_int(r, "diff.interHunkContext", &interhunkcontext)) { >>> + if (interhunkcontext < 0) >>> + die(_("%s cannot be negative"), "diff.interHunkContext"); >>> + else >>> + s->interhunkcontext = interhunkcontext; >>> + }; >> >> Thanks for changing this. This iteration of the code changes looks good > > Lose the ';' (semicolon) after closing {brace}s. > This is C; you do not need an empty statement after a {block}. Oh well spotted, I'd missed that Thanks Phillip ^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v2 3/4] add-patch: respect diff.context configuration 2025-05-14 15:13 ` Phillip Wood @ 2025-05-15 12:58 ` Junio C Hamano 0 siblings, 0 replies; 77+ messages in thread From: Junio C Hamano @ 2025-05-15 12:58 UTC (permalink / raw) To: Phillip Wood Cc: Leon Michalak via GitGitGadget, git, Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Leon Michalak Phillip Wood <phillip.wood123@gmail.com> writes: > On 13/05/2025 16:47, Junio C Hamano wrote: >> Phillip Wood <phillip.wood123@gmail.com> writes: >>> On 10/05/2025 14:46, Leon Michalak via GitGitGadget wrote: >>> >>>> @@ -78,6 +82,19 @@ void init_add_i_state(struct add_i_state *s, struct repository *r) >>>> repo_config_get_string(r, "diff.algorithm", >>>> &s->interactive_diff_algorithm); >>>> + if (!repo_config_get_int(r, "diff.context", &context)) { >>>> + if (context < 0) >>>> + die(_("%s cannot be negative"), "diff.context"); >>>> + else >>>> + s->context = context; >>>> + }; >>>> + if (!repo_config_get_int(r, "diff.interHunkContext", &interhunkcontext)) { >>>> + if (interhunkcontext < 0) >>>> + die(_("%s cannot be negative"), "diff.interHunkContext"); >>>> + else >>>> + s->interhunkcontext = interhunkcontext; >>>> + }; >>> >>> Thanks for changing this. This iteration of the code changes looks good >> Lose the ';' (semicolon) after closing {brace}s. >> This is C; you do not need an empty statement after a {block}. > > Oh well spotted, I'd missed that Heh, with enough number of eyeballs, all the bugs are shallow. ^ permalink raw reply [flat|nested] 77+ messages in thread
* [PATCH v2 4/4] add-patch: add diff.context command line overrides 2025-05-10 13:46 ` [PATCH v2 0/4] Better support for customising context lines in --patch commands Leon Michalak via GitGitGadget ` (2 preceding siblings ...) 2025-05-10 13:46 ` [PATCH v2 3/4] add-patch: respect diff.context configuration Leon Michalak via GitGitGadget @ 2025-05-10 13:46 ` Leon Michalak via GitGitGadget 2025-05-12 16:45 ` Junio C Hamano 2025-05-13 13:52 ` Phillip Wood 2025-06-28 16:34 ` [PATCH v3 0/4] Better support for customising context lines in --patch commands Leon Michalak via GitGitGadget 4 siblings, 2 replies; 77+ messages in thread From: Leon Michalak via GitGitGadget @ 2025-05-10 13:46 UTC (permalink / raw) To: git Cc: Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Phillip Wood, Leon Michalak, Leon Michalak From: Leon Michalak <leonmichalak6@gmail.com> This patch compliments the previous commit, where builtins that use add-patch infrastructure now respect diff.context and diff.interHunkContext file configurations. In particular, this patch helps users who don't want to set persistent context configurations or just want a way to override them on a one-time basis, by allowing the relevant builtins to accept corresponding command line options that override the file configurations. This mimics commands such as diff and log, which allow for both context file configuration and command line overrides. Signed-off-by: Leon Michalak <leonmichalak6@gmail.com> --- Documentation/diff-context-options.adoc | 10 ++++ Documentation/git-add.adoc | 2 + Documentation/git-checkout.adoc | 2 + Documentation/git-commit.adoc | 2 + Documentation/git-reset.adoc | 2 + Documentation/git-restore.adoc | 2 + Documentation/git-stash.adoc | 2 + add-interactive.c | 36 +++++++++++---- add-interactive.h | 16 +++++-- add-patch.c | 5 +- builtin/add.c | 16 +++++-- builtin/checkout.c | 24 ++++++++-- builtin/commit.c | 11 ++++- builtin/reset.c | 12 ++++- builtin/stash.c | 46 ++++++++++++++----- commit.h | 3 +- parse-options.h | 2 + t/t4032-diff-inter-hunk-context.sh | 61 +++++++++++++++++++++++++ t/t4055-diff-context.sh | 30 ++++++++++++ t/t9902-completion.sh | 2 + 20 files changed, 251 insertions(+), 35 deletions(-) create mode 100644 Documentation/diff-context-options.adoc diff --git a/Documentation/diff-context-options.adoc b/Documentation/diff-context-options.adoc new file mode 100644 index 000000000000..e161260358ff --- /dev/null +++ b/Documentation/diff-context-options.adoc @@ -0,0 +1,10 @@ +`-U<n>`:: +`--unified=<n>`:: + Generate diffs with _<n>_ lines of context. Defaults to `diff.context` + or 3 if the config option is unset. + +`--inter-hunk-context=<n>`:: + Show the context between diff hunks, up to the specified _<number>_ + of lines, thereby fusing hunks that are close to each other. + Defaults to `diff.interHunkContext` or 0 if the config option + is unset. diff --git a/Documentation/git-add.adoc b/Documentation/git-add.adoc index eba0b419ce50..b7a735824d6c 100644 --- a/Documentation/git-add.adoc +++ b/Documentation/git-add.adoc @@ -104,6 +104,8 @@ This effectively runs `add --interactive`, but bypasses the initial command menu and directly jumps to the `patch` subcommand. See ``Interactive mode'' for details. +include::diff-context-options.adoc[] + `-e`:: `--edit`:: Open the diff vs. the index in an editor and let the user diff --git a/Documentation/git-checkout.adoc b/Documentation/git-checkout.adoc index a66c53a5cd1e..896372fd7a29 100644 --- a/Documentation/git-checkout.adoc +++ b/Documentation/git-checkout.adoc @@ -289,6 +289,8 @@ section of linkgit:git-add[1] to learn how to operate the `--patch` mode. Note that this option uses the no overlay mode by default (see also `--overlay`), and currently doesn't support overlay mode. +include::diff-context-options.adoc[] + --ignore-other-worktrees:: `git checkout` refuses when the wanted branch is already checked out or otherwise in use by another worktree. This option makes diff --git a/Documentation/git-commit.adoc b/Documentation/git-commit.adoc index dc219025f1eb..ae988a883b5b 100644 --- a/Documentation/git-commit.adoc +++ b/Documentation/git-commit.adoc @@ -76,6 +76,8 @@ OPTIONS which changes to commit. See linkgit:git-add[1] for details. +include::diff-context-options.adoc[] + `-C <commit>`:: `--reuse-message=<commit>`:: Take an existing _<commit>_ object, and reuse the log message diff --git a/Documentation/git-reset.adoc b/Documentation/git-reset.adoc index 53ab88c5451c..50e8a0ba6f66 100644 --- a/Documentation/git-reset.adoc +++ b/Documentation/git-reset.adoc @@ -125,6 +125,8 @@ OPTIONS separated with _NUL_ character and all other characters are taken literally (including newlines and quotes). +include::diff-context-options.adoc[] + `--`:: Do not interpret any more arguments as options. diff --git a/Documentation/git-restore.adoc b/Documentation/git-restore.adoc index 877b7772e667..1dcc2bb7aea3 100644 --- a/Documentation/git-restore.adoc +++ b/Documentation/git-restore.adoc @@ -52,6 +52,8 @@ leave out at most one of _<rev-A>__ and _<rev-B>_, in which case it defaults to Mode" section of linkgit:git-add[1] to learn how to operate the `--patch` mode. +include::diff-context-options.adoc[] + `-W`:: `--worktree`:: `-S`:: diff --git a/Documentation/git-stash.adoc b/Documentation/git-stash.adoc index 1a5177f4986c..0578c619c410 100644 --- a/Documentation/git-stash.adoc +++ b/Documentation/git-stash.adoc @@ -208,6 +208,8 @@ to learn how to operate the `--patch` mode. The `--patch` option implies `--keep-index`. You can use `--no-keep-index` to override this. +include::diff-context-options.adoc[] + -S:: --staged:: This option is only valid for `push` and `save` commands. diff --git a/add-interactive.c b/add-interactive.c index cac036441caf..496a44cfe4b6 100644 --- a/add-interactive.c +++ b/add-interactive.c @@ -36,7 +36,8 @@ static void init_color(struct repository *r, struct add_i_state *s, free(key); } -void init_add_i_state(struct add_i_state *s, struct repository *r) +void init_add_i_state(struct add_i_state *s, struct repository *r, + struct add_p_opt *add_p_opt) { const char *value; int context; @@ -98,6 +99,17 @@ void init_add_i_state(struct add_i_state *s, struct repository *r) repo_config_get_bool(r, "interactive.singlekey", &s->use_single_key); if (s->use_single_key) setbuf(stdin, NULL); + + if (add_p_opt->context != -1) { + if (add_p_opt->context < 0) + die(_("%s cannot be negative"), "--unified"); + s->context = add_p_opt->context; + } + if (add_p_opt->interhunkcontext != -1) { + if (add_p_opt->interhunkcontext < 0) + die(_("%s cannot be negative"), "--inter-hunk-context"); + s->interhunkcontext = add_p_opt->interhunkcontext; + } } void clear_add_i_state(struct add_i_state *s) @@ -986,6 +998,10 @@ static int run_patch(struct add_i_state *s, const struct pathspec *ps, opts->prompt = N_("Patch update"); count = list_and_choose(s, files, opts); if (count > 0) { + struct add_p_opt add_p_opt = { + .context = s->context, + .interhunkcontext = s->interhunkcontext, + }; struct strvec args = STRVEC_INIT; struct pathspec ps_selected = { 0 }; @@ -996,7 +1012,7 @@ static int run_patch(struct add_i_state *s, const struct pathspec *ps, parse_pathspec(&ps_selected, PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL, PATHSPEC_LITERAL_PATH, "", args.v); - res = run_add_p(s->r, ADD_P_ADD, NULL, &ps_selected); + res = run_add_p(s->r, ADD_P_ADD, &add_p_opt, NULL, &ps_selected); strvec_clear(&args); clear_pathspec(&ps_selected); } @@ -1031,10 +1047,13 @@ static int run_diff(struct add_i_state *s, const struct pathspec *ps, if (count > 0) { struct child_process cmd = CHILD_PROCESS_INIT; - strvec_pushl(&cmd.args, "git", "diff", "-p", "--cached", - oid_to_hex(!is_initial ? &oid : - s->r->hash_algo->empty_tree), - "--", NULL); + strvec_pushl(&cmd.args, "git", "diff", "-p", "--cached", NULL); + if (s->context != -1) + strvec_pushf(&cmd.args, "--unified=%i", s->context); + if (s->interhunkcontext != -1) + strvec_pushf(&cmd.args, "--inter-hunk-context=%i", s->interhunkcontext); + strvec_pushl(&cmd.args, oid_to_hex(!is_initial ? &oid : + s->r->hash_algo->empty_tree), "--", NULL); for (i = 0; i < files->items.nr; i++) if (files->selected[i]) strvec_push(&cmd.args, @@ -1127,7 +1146,8 @@ static void command_prompt_help(struct add_i_state *s) _("(empty) select nothing")); } -int run_add_i(struct repository *r, const struct pathspec *ps) +int run_add_i(struct repository *r, const struct pathspec *ps, + struct add_p_opt *add_p_opt) { struct add_i_state s = { NULL }; struct print_command_item_data data = { "[", "]" }; @@ -1170,7 +1190,7 @@ int run_add_i(struct repository *r, const struct pathspec *ps) ->util = util; } - init_add_i_state(&s, r); + init_add_i_state(&s, r, add_p_opt); /* * When color was asked for, use the prompt color for diff --git a/add-interactive.h b/add-interactive.h index c63f35b14be8..4213dcd67b9a 100644 --- a/add-interactive.h +++ b/add-interactive.h @@ -3,6 +3,13 @@ #include "color.h" +struct add_p_opt { + int context; + int interhunkcontext; +}; + +#define ADD_P_OPT_INIT { .context = -1, .interhunkcontext = -1 } + struct add_i_state { struct repository *r; int use_color; @@ -21,12 +28,14 @@ struct add_i_state { int context, interhunkcontext; }; -void init_add_i_state(struct add_i_state *s, struct repository *r); +void init_add_i_state(struct add_i_state *s, struct repository *r, + struct add_p_opt *add_p_opt); void clear_add_i_state(struct add_i_state *s); struct repository; struct pathspec; -int run_add_i(struct repository *r, const struct pathspec *ps); +int run_add_i(struct repository *r, const struct pathspec *ps, + struct add_p_opt *add_p_opt); enum add_p_mode { ADD_P_ADD, @@ -37,6 +46,7 @@ enum add_p_mode { }; int run_add_p(struct repository *r, enum add_p_mode mode, - const char *revision, const struct pathspec *ps); + struct add_p_opt *o, const char *revision, + const struct pathspec *ps); #endif diff --git a/add-patch.c b/add-patch.c index b43ca1600738..c0d33820c558 100644 --- a/add-patch.c +++ b/add-patch.c @@ -1766,14 +1766,15 @@ soft_increment: } int run_add_p(struct repository *r, enum add_p_mode mode, - const char *revision, const struct pathspec *ps) + struct add_p_opt *o, const char *revision, + const struct pathspec *ps) { struct add_p_state s = { { r }, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT }; size_t i, binary_count = 0; - init_add_i_state(&s.s, r); + init_add_i_state(&s.s, r, o); if (mode == ADD_P_STASH) s.mode = &patch_mode_stash; diff --git a/builtin/add.c b/builtin/add.c index 78dfb2657767..a7e6c77e7a74 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -29,6 +29,7 @@ static const char * const builtin_add_usage[] = { NULL }; static int patch_interactive, add_interactive, edit_interactive; +static struct add_p_opt add_p_opt = ADD_P_OPT_INIT; static int take_worktree_changes; static int add_renormalize; static int pathspec_file_nul; @@ -157,7 +158,7 @@ static int refresh(struct repository *repo, int verbose, const struct pathspec * int interactive_add(struct repository *repo, const char **argv, const char *prefix, - int patch) + int patch, struct add_p_opt *add_p_opt) { struct pathspec pathspec; int ret; @@ -169,9 +170,9 @@ int interactive_add(struct repository *repo, prefix, argv); if (patch) - ret = !!run_add_p(repo, ADD_P_ADD, NULL, &pathspec); + ret = !!run_add_p(repo, ADD_P_ADD, add_p_opt, NULL, &pathspec); else - ret = !!run_add_i(repo, &pathspec); + ret = !!run_add_i(repo, &pathspec, add_p_opt); clear_pathspec(&pathspec); return ret; @@ -253,6 +254,8 @@ static struct option builtin_add_options[] = { OPT_GROUP(""), OPT_BOOL('i', "interactive", &add_interactive, N_("interactive picking")), OPT_BOOL('p', "patch", &patch_interactive, N_("select hunks interactively")), + OPT_DIFF_UNIFIED(&add_p_opt.context), + OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext), OPT_BOOL('e', "edit", &edit_interactive, N_("edit current diff and apply")), OPT__FORCE(&ignored_too, N_("allow adding otherwise ignored files"), 0), OPT_BOOL('u', "update", &take_worktree_changes, N_("update tracked files")), @@ -398,7 +401,12 @@ int cmd_add(int argc, die(_("options '%s' and '%s' cannot be used together"), "--dry-run", "--interactive/--patch"); if (pathspec_from_file) die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--interactive/--patch"); - exit(interactive_add(repo, argv + 1, prefix, patch_interactive)); + exit(interactive_add(repo, argv + 1, prefix, patch_interactive, &add_p_opt)); + } else { + if (add_p_opt.context != -1) + die(_("the option '%s' requires '%s'"), "--unified", "--interactive/--patch"); + if (add_p_opt.interhunkcontext != -1) + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--interactive/--patch"); } if (edit_interactive) { diff --git a/builtin/checkout.c b/builtin/checkout.c index d185982f3a63..b0b05b71bc29 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -61,6 +61,8 @@ static const char * const restore_usage[] = { struct checkout_opts { int patch_mode; + int patch_context; + int patch_interhunk_context; int quiet; int merge; int force; @@ -104,7 +106,12 @@ struct checkout_opts { struct tree *source_tree; }; -#define CHECKOUT_OPTS_INIT { .conflict_style = -1, .merge = -1 } +#define CHECKOUT_OPTS_INIT { \ + .conflict_style = -1, \ + .merge = -1, \ + .patch_context = -1, \ + .patch_interhunk_context = -1, \ +} struct branch_info { char *name; /* The short name used */ @@ -539,6 +546,10 @@ static int checkout_paths(const struct checkout_opts *opts, if (opts->patch_mode) { enum add_p_mode patch_mode; + struct add_p_opt add_p_opt = { + .context = opts->patch_context, + .interhunkcontext = opts->patch_interhunk_context, + }; const char *rev = new_branch_info->name; char rev_oid[GIT_MAX_HEXSZ + 1]; @@ -564,8 +575,13 @@ static int checkout_paths(const struct checkout_opts *opts, else BUG("either flag must have been set, worktree=%d, index=%d", opts->checkout_worktree, opts->checkout_index); - return !!run_add_p(the_repository, patch_mode, rev, - &opts->pathspec); + return !!run_add_p(the_repository, patch_mode, &add_p_opt, + rev, &opts->pathspec); + } else { + if (opts->patch_context != -1) + die(_("the option '%s' requires '%s'"), "--unified", "--patch"); + if (opts->patch_interhunk_context != -1) + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch"); } repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR); @@ -1738,6 +1754,8 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts, N_("checkout their version for unmerged files"), 3, PARSE_OPT_NONEG), OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")), + OPT_DIFF_UNIFIED(&opts->patch_context), + OPT_DIFF_INTERHUNK_CONTEXT(&opts->patch_interhunk_context), 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), diff --git a/builtin/commit.c b/builtin/commit.c index 66bd91fd523d..19ec0ccb2bef 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -19,6 +19,7 @@ #include "environment.h" #include "diff.h" #include "commit.h" +#include "add-interactive.h" #include "gettext.h" #include "revision.h" #include "wt-status.h" @@ -122,6 +123,7 @@ static const char *edit_message, *use_message; static char *fixup_message, *fixup_commit, *squash_message; static const char *fixup_prefix; static int all, also, interactive, patch_interactive, only, amend, signoff; +static struct add_p_opt add_p_opt = ADD_P_OPT_INIT; 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 */ @@ -400,7 +402,7 @@ static const char *prepare_index(const char **argv, const char *prefix, old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT)); setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1); - if (interactive_add(the_repository, argv, prefix, patch_interactive) != 0) + if (interactive_add(the_repository, argv, prefix, patch_interactive, &add_p_opt) != 0) die(_("interactive add failed")); the_repository->index_file = old_repo_index_file; @@ -424,6 +426,11 @@ static const char *prepare_index(const char **argv, const char *prefix, commit_style = COMMIT_NORMAL; ret = get_lock_file_path(&index_lock); goto out; + } else { + if (add_p_opt.context != -1) + die(_("the option '%s' requires '%s'"), "--unified", "--interactive/--patch"); + if (add_p_opt.interhunkcontext != -1) + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--interactive/--patch"); } /* @@ -1722,6 +1729,8 @@ int cmd_commit(int argc, OPT_BOOL('i', "include", &also, N_("add specified files to index for commit")), OPT_BOOL(0, "interactive", &interactive, N_("interactively add files")), OPT_BOOL('p', "patch", &patch_interactive, N_("interactively add changes")), + OPT_DIFF_UNIFIED(&add_p_opt.context), + OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext), OPT_BOOL('o', "only", &only, N_("commit only specified files")), OPT_BOOL('n', "no-verify", &no_verify, N_("bypass pre-commit and commit-msg hooks")), OPT_BOOL(0, "dry-run", &dry_run, N_("show what would be committed")), diff --git a/builtin/reset.c b/builtin/reset.c index 73b4537a9a56..62db07b0c231 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -346,6 +346,7 @@ int cmd_reset(int argc, struct object_id oid; struct pathspec pathspec; int intent_to_add = 0; + struct add_p_opt add_p_opt = ADD_P_OPT_INIT; const struct option options[] = { OPT__QUIET(&quiet, N_("be quiet, only report errors")), OPT_BOOL(0, "no-refresh", &no_refresh, @@ -370,6 +371,8 @@ int cmd_reset(int argc, PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater), OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")), + OPT_DIFF_UNIFIED(&add_p_opt.context), + OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext), 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), @@ -424,9 +427,14 @@ int cmd_reset(int argc, if (reset_type != NONE) die(_("options '%s' and '%s' cannot be used together"), "--patch", "--{hard,mixed,soft}"); trace2_cmd_mode("patch-interactive"); - update_ref_status = !!run_add_p(the_repository, ADD_P_RESET, rev, - &pathspec); + update_ref_status = !!run_add_p(the_repository, ADD_P_RESET, + &add_p_opt, rev, &pathspec); goto cleanup; + } else { + if (add_p_opt.context != -1) + die(_("the option '%s' requires '%s'"), "--unified", "--patch"); + if (add_p_opt.interhunkcontext != -1) + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch"); } /* git reset tree [--] paths... can be used to diff --git a/builtin/stash.c b/builtin/stash.c index cfbd92852a65..1c68d50ce543 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -1242,7 +1242,8 @@ done: } static int stash_patch(struct stash_info *info, const struct pathspec *ps, - struct strbuf *out_patch, int quiet) + struct strbuf *out_patch, int quiet, + struct add_p_opt *add_p_opt) { int ret = 0; struct child_process cp_read_tree = CHILD_PROCESS_INIT; @@ -1267,7 +1268,7 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps, old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT)); setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1); - ret = !!run_add_p(the_repository, ADD_P_STASH, NULL, ps); + ret = !!run_add_p(the_repository, ADD_P_STASH, add_p_opt, NULL, ps); the_repository->index_file = old_repo_index_file; if (old_index_env && *old_index_env) @@ -1362,8 +1363,8 @@ done: } static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_buf, - int include_untracked, int patch_mode, int only_staged, - struct stash_info *info, struct strbuf *patch, + int include_untracked, int patch_mode, struct add_p_opt *add_p_opt, + int only_staged, struct stash_info *info, struct strbuf *patch, int quiet) { int ret = 0; @@ -1439,7 +1440,7 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b untracked_commit_option = 1; } if (patch_mode) { - ret = stash_patch(info, ps, patch, quiet); + ret = stash_patch(info, ps, patch, quiet, add_p_opt); if (ret < 0) { if (!quiet) fprintf_ln(stderr, _("Cannot save the current " @@ -1513,7 +1514,7 @@ static int create_stash(int argc, const char **argv, const char *prefix UNUSED, if (!check_changes_tracked_files(&ps)) return 0; - ret = do_create_stash(&ps, &stash_msg_buf, 0, 0, 0, &info, + ret = do_create_stash(&ps, &stash_msg_buf, 0, 0, NULL, 0, &info, NULL, 0); if (!ret) printf_ln("%s", oid_to_hex(&info.w_commit)); @@ -1524,7 +1525,8 @@ static int create_stash(int argc, const char **argv, const char *prefix UNUSED, } static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int quiet, - int keep_index, int patch_mode, int include_untracked, int only_staged) + int keep_index, int patch_mode, struct add_p_opt *add_p_opt, + int include_untracked, int only_staged) { int ret = 0; struct stash_info info = STASH_INFO_INIT; @@ -1594,8 +1596,8 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q if (stash_msg) strbuf_addstr(&stash_msg_buf, stash_msg); - if (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode, only_staged, - &info, &patch, quiet)) { + if (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode, + add_p_opt, only_staged, &info, &patch, quiet)) { ret = -1; goto done; } @@ -1768,6 +1770,7 @@ static int push_stash(int argc, const char **argv, const char *prefix, const char *stash_msg = NULL; char *pathspec_from_file = NULL; struct pathspec ps; + struct add_p_opt add_p_opt = ADD_P_OPT_INIT; struct option options[] = { OPT_BOOL('k', "keep-index", &keep_index, N_("keep index")), @@ -1775,6 +1778,8 @@ static int push_stash(int argc, const char **argv, const char *prefix, N_("stash staged changes only")), OPT_BOOL('p', "patch", &patch_mode, N_("stash in patch mode")), + OPT_DIFF_UNIFIED(&add_p_opt.context), + OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext), OPT__QUIET(&quiet, N_("quiet mode")), OPT_BOOL('u', "include-untracked", &include_untracked, N_("include untracked files in stash")), @@ -1826,8 +1831,15 @@ static int push_stash(int argc, const char **argv, const char *prefix, die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file"); } + if (!patch_mode) { + if (add_p_opt.context != -1) + die(_("the option '%s' requires '%s'"), "--unified", "--patch"); + if (add_p_opt.interhunkcontext != -1) + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch"); + } + ret = do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode, - include_untracked, only_staged); + &add_p_opt, include_untracked, only_staged); clear_pathspec(&ps); free(pathspec_from_file); @@ -1852,6 +1864,7 @@ static int save_stash(int argc, const char **argv, const char *prefix, const char *stash_msg = NULL; struct pathspec ps; struct strbuf stash_msg_buf = STRBUF_INIT; + struct add_p_opt add_p_opt = ADD_P_OPT_INIT; struct option options[] = { OPT_BOOL('k', "keep-index", &keep_index, N_("keep index")), @@ -1859,6 +1872,8 @@ static int save_stash(int argc, const char **argv, const char *prefix, N_("stash staged changes only")), OPT_BOOL('p', "patch", &patch_mode, N_("stash in patch mode")), + OPT_DIFF_UNIFIED(&add_p_opt.context), + OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext), OPT__QUIET(&quiet, N_("quiet mode")), OPT_BOOL('u', "include-untracked", &include_untracked, N_("include untracked files in stash")), @@ -1877,8 +1892,17 @@ static int save_stash(int argc, const char **argv, const char *prefix, stash_msg = strbuf_join_argv(&stash_msg_buf, argc, argv, ' '); memset(&ps, 0, sizeof(ps)); + + if (!patch_mode) { + if (add_p_opt.context != -1) + die(_("the option '%s' requires '%s'"), "--unified", "--patch"); + if (add_p_opt.interhunkcontext != -1) + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch"); + } + ret = do_push_stash(&ps, stash_msg, quiet, keep_index, - patch_mode, include_untracked, only_staged); + patch_mode, &add_p_opt, include_untracked, + only_staged); strbuf_release(&stash_msg_buf); return ret; diff --git a/commit.h b/commit.h index 70c870dae4d4..7a7fedbc2f14 100644 --- a/commit.h +++ b/commit.h @@ -2,6 +2,7 @@ #define COMMIT_H #include "object.h" +#include "add-interactive.h" struct signature_check; struct strbuf; @@ -257,7 +258,7 @@ int for_each_commit_graft(each_commit_graft_fn, void *); int interactive_add(struct repository *repo, const char **argv, const char *prefix, - int patch); + int patch, struct add_p_opt *add_p_opt); struct commit_extra_header { struct commit_extra_header *next; diff --git a/parse-options.h b/parse-options.h index 91c3e3c29b3d..bdae8f116198 100644 --- a/parse-options.h +++ b/parse-options.h @@ -616,6 +616,8 @@ int parse_opt_tracking_mode(const struct option *, const char *, int); #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")) #define OPT_AUTOSTASH(v) OPT_BOOL(0, "autostash", v, N_("automatically stash/stash pop before and after")) +#define OPT_DIFF_UNIFIED(v) OPT_INTEGER_F('U', "unified", v, N_("generate diffs with <n> lines context"), PARSE_OPT_NONEG) +#define OPT_DIFF_INTERHUNK_CONTEXT(v) OPT_INTEGER_F(0, "inter-hunk-context", v, N_("show context between diff hunks up to the specified number of lines"), PARSE_OPT_NONEG) #define OPT_IPVERSION(v) \ OPT_SET_INT_F('4', "ipv4", (v), N_("use IPv4 addresses only"), \ diff --git a/t/t4032-diff-inter-hunk-context.sh b/t/t4032-diff-inter-hunk-context.sh index bada0cbd32f7..d5aad6e143a7 100755 --- a/t/t4032-diff-inter-hunk-context.sh +++ b/t/t4032-diff-inter-hunk-context.sh @@ -47,6 +47,31 @@ t() { " } +t_patch() { + use_config= + git config --unset diff.interHunkContext + + case $# in + 4) hunks=$4; cmd="add -p -U$3";; + 5) hunks=$5; cmd="add -p -U$3 --inter-hunk-context=$4";; + 6) hunks=$5; cmd="add -p -U$3"; git config diff.interHunkContext $4; use_config="(diff.interHunkContext=$4) ";; + esac + label="$use_config$cmd, $1 common $2" + file=f$1 + + if ! test -f $file + then + f A $1 B >$file + git add $file + git commit -q -m. $file + f X $1 Y >$file + fi + + test_expect_success "$label: count hunks ($hunks)" " + test $(test_write_lines q | git $cmd $file | sed -n 's/^([0-9]*\/\([0-9]*\)) Stage this hunk.*/\1/p') = $hunks + " +} + cat <<EOF >expected.f1.0.1 || exit 1 diff --git a/f1 b/f1 --- a/f1 @@ -107,6 +132,42 @@ t 3 lines 1 2 1 config t 9 lines 3 2 2 config t 9 lines 3 3 1 config +# common lines ctx intrctx hunks +t_patch 1 line 0 2 +t_patch 1 line 0 0 2 +t_patch 1 line 0 1 1 +t_patch 1 line 0 2 1 +t_patch 1 line 1 1 + +t_patch 2 lines 0 2 +t_patch 2 lines 0 0 2 +t_patch 2 lines 0 1 2 +t_patch 2 lines 0 2 1 +t_patch 2 lines 1 1 + +t_patch 3 lines 1 2 +t_patch 3 lines 1 0 2 +t_patch 3 lines 1 1 1 +t_patch 3 lines 1 2 1 + +t_patch 9 lines 3 2 +t_patch 9 lines 3 2 2 +t_patch 9 lines 3 3 1 + +# use diff.interHunkContext? +t_patch 1 line 0 0 2 config +t_patch 1 line 0 1 1 config +t_patch 1 line 0 2 1 config +t_patch 9 lines 3 3 1 config +t_patch 2 lines 0 0 2 config +t_patch 2 lines 0 1 2 config +t_patch 2 lines 0 2 1 config +t_patch 3 lines 1 0 2 config +t_patch 3 lines 1 1 1 config +t_patch 3 lines 1 2 1 config +t_patch 9 lines 3 2 2 config +t_patch 9 lines 3 3 1 config + test_expect_success 'diff.interHunkContext invalid' ' git config diff.interHunkContext asdf && test_must_fail git diff && diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh index c4b861c360cc..07d993ba6762 100755 --- a/t/t4055-diff-context.sh +++ b/t/t4055-diff-context.sh @@ -98,6 +98,36 @@ test_expect_success 'The -U option overrides diff.context' ' test_grep ! "^ firstline" output ' +test_expect_success 'The -U option overrides diff.context for "add"' ' + test_config diff.context 8 && + git add -U4 -p >output && + test_grep ! "^ firstline" output +' + +test_expect_success 'The -U option overrides diff.context for "commit"' ' + test_config diff.context 8 && + ! git commit -U4 -p >output && + test_grep ! "^ firstline" output +' + +test_expect_success 'The -U option overrides diff.context for "checkout"' ' + test_config diff.context 8 && + git checkout -U4 -p >output && + test_grep ! "^ firstline" output +' + +test_expect_success 'The -U option overrides diff.context for "stash"' ' + test_config diff.context 8 && + ! git stash -U4 -p >output && + test_grep ! "^ firstline" output +' + +test_expect_success 'The -U option overrides diff.context for "restore"' ' + test_config diff.context 8 && + git restore -U4 -p >output && + test_grep ! "^ firstline" output +' + test_expect_success 'diff.context honored by "diff"' ' test_config diff.context 8 && git diff >output && diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh index 343b8cd1912b..6650d33fba69 100755 --- a/t/t9902-completion.sh +++ b/t/t9902-completion.sh @@ -2596,6 +2596,8 @@ test_expect_success 'double dash "git checkout"' ' --merge Z --conflict=Z --patch Z + --unified=Z + --inter-hunk-context=Z --ignore-skip-worktree-bits Z --ignore-other-worktrees Z --recurse-submodules Z -- gitgitgadget ^ permalink raw reply related [flat|nested] 77+ messages in thread
* Re: [PATCH v2 4/4] add-patch: add diff.context command line overrides 2025-05-10 13:46 ` [PATCH v2 4/4] add-patch: add diff.context command line overrides Leon Michalak via GitGitGadget @ 2025-05-12 16:45 ` Junio C Hamano 2025-05-12 17:03 ` Leon Michalak 2025-05-13 13:52 ` Phillip Wood 1 sibling, 1 reply; 77+ messages in thread From: Junio C Hamano @ 2025-05-12 16:45 UTC (permalink / raw) To: Leon Michalak via GitGitGadget Cc: git, Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Phillip Wood, Leon Michalak "Leon Michalak via GitGitGadget" <gitgitgadget@gmail.com> writes: > diff --git a/Documentation/diff-context-options.adoc b/Documentation/diff-context-options.adoc > new file mode 100644 > index 000000000000..e161260358ff > --- /dev/null > +++ b/Documentation/diff-context-options.adoc > @@ -0,0 +1,10 @@ > +`-U<n>`:: > +`--unified=<n>`:: > + Generate diffs with _<n>_ lines of context. Defaults to `diff.context` > + or 3 if the config option is unset. > + > +`--inter-hunk-context=<n>`:: > + Show the context between diff hunks, up to the specified _<number>_ > + of lines, thereby fusing hunks that are close to each other. > + Defaults to `diff.interHunkContext` or 0 if the config option > + is unset. It might not be trivial to do but I wonder if we cannot do better than this to share more of the same text across manual pages. These two being options understood by `git diff`, we certainly have an existing description for them, no? Other than that, looking good to me. Thanks. ^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v2 4/4] add-patch: add diff.context command line overrides 2025-05-12 16:45 ` Junio C Hamano @ 2025-05-12 17:03 ` Leon Michalak 0 siblings, 0 replies; 77+ messages in thread From: Leon Michalak @ 2025-05-12 17:03 UTC (permalink / raw) To: Junio C Hamano Cc: Leon Michalak via GitGitGadget, git, Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Phillip Wood On Mon, 12 May 2025 at 17:45, Junio C Hamano <gitster@pobox.com> wrote: > It might not be trivial to do but I wonder if we cannot do better > than this to share more of the same text across manual pages. These > two being options understood by `git diff`, we certainly have an > existing description for them, no? Yes, I did of course notice documentation for `git diff` also has these; ultimately my justification for not changing that to use this new .adoc include as well was for a couple reasons: - these two options are not together in the `git diff` documentation (not *so* important, and they probably should actually be together?) - there is an extra if def which adds on "implies --patch" text in the `git diff` documentation which isn't the behaviour the add-patch commands are going for, so that makes the intent a little different here But would be good to hear if anyone else has any thoughts. ^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v2 4/4] add-patch: add diff.context command line overrides 2025-05-10 13:46 ` [PATCH v2 4/4] add-patch: add diff.context command line overrides Leon Michalak via GitGitGadget 2025-05-12 16:45 ` Junio C Hamano @ 2025-05-13 13:52 ` Phillip Wood 2025-05-13 14:39 ` Phillip Wood 1 sibling, 1 reply; 77+ messages in thread From: Phillip Wood @ 2025-05-13 13:52 UTC (permalink / raw) To: Leon Michalak via GitGitGadget, git Cc: Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Leon Michalak Hi Leon On 10/05/2025 14:46, Leon Michalak via GitGitGadget wrote: > From: Leon Michalak <leonmichalak6@gmail.com> > > This patch compliments the previous commit, where builtins that use > add-patch infrastructure now respect diff.context and > diff.interHunkContext file configurations. > > In particular, this patch helps users who don't want to set persistent > context configurations or just want a way to override them on a one-time > basis, by allowing the relevant builtins to accept corresponding command > line options that override the file configurations. > > This mimics commands such as diff and log, which allow for both context > file configuration and command line overrides. The code changes here mostly look good, I've left a few comments below. I think the tests could be improved, I've left some suggestions on limiting the number of tests while improving the coverage. The new tests I'm suggesting that check invalid option combinations are the basis for most of my code comments. There is still the issue of what to do with -U0. As I mentioned previously "git apply" will fail when we try to apply the patch. We can either pass the appropriate flag when the context is zero or possibly use -U0 to mean the default number of context lines. > diff --git a/Documentation/diff-context-options.adoc b/Documentation/diff-context-options.adoc > new file mode 100644 > index 000000000000..e161260358ff > --- /dev/null > +++ b/Documentation/diff-context-options.adoc > @@ -0,0 +1,10 @@ > +`-U<n>`:: > +`--unified=<n>`:: > + Generate diffs with _<n>_ lines of context. Defaults to `diff.context` > + or 3 if the config option is unset. > + > +`--inter-hunk-context=<n>`:: > + Show the context between diff hunks, up to the specified _<number>_ > + of lines, thereby fusing hunks that are close to each other. > + Defaults to `diff.interHunkContext` or 0 if the config option > + is unset. Nice - we reuse the same text for all the "-p" commands. > diff --git a/add-interactive.c b/add-interactive.c > [...] > @@ -98,6 +99,17 @@ void init_add_i_state(struct add_i_state *s, struct repository *r) > repo_config_get_bool(r, "interactive.singlekey", &s->use_single_key); > if (s->use_single_key) > setbuf(stdin, NULL); > + > + if (add_p_opt->context != -1) { > + if (add_p_opt->context < 0) > + die(_("%s cannot be negative"), "--unified"); > + s->context = add_p_opt->context; > + } > + if (add_p_opt->interhunkcontext != -1) { > + if (add_p_opt->interhunkcontext < 0) > + die(_("%s cannot be negative"), "--inter-hunk-context"); > + s->interhunkcontext = add_p_opt->interhunkcontext; > + } Centralizing these checks like this is a good idea. > @@ -1031,10 +1047,13 @@ static int run_diff(struct add_i_state *s, const struct pathspec *ps, > if (count > 0) { > struct child_process cmd = CHILD_PROCESS_INIT; > > - strvec_pushl(&cmd.args, "git", "diff", "-p", "--cached", > - oid_to_hex(!is_initial ? &oid : > - s->r->hash_algo->empty_tree), > - "--", NULL); > + strvec_pushl(&cmd.args, "git", "diff", "-p", "--cached", NULL); > + if (s->context != -1) > + strvec_pushf(&cmd.args, "--unified=%i", s->context); > + if (s->interhunkcontext != -1) > + strvec_pushf(&cmd.args, "--inter-hunk-context=%i", s->interhunkcontext); > + strvec_pushl(&cmd.args, oid_to_hex(!is_initial ? &oid : > + s->r->hash_algo->empty_tree), "--", NULL); This is good - we propagate the values we were given on the command-line. > diff --git a/builtin/checkout.c b/builtin/checkout.c > [...] > @@ -564,8 +575,13 @@ static int checkout_paths(const struct checkout_opts *opts, > else > BUG("either flag must have been set, worktree=%d, index=%d", > opts->checkout_worktree, opts->checkout_index); > - return !!run_add_p(the_repository, patch_mode, rev, > - &opts->pathspec); > + return !!run_add_p(the_repository, patch_mode, &add_p_opt, > + rev, &opts->pathspec); > + } else { > + if (opts->patch_context != -1) > + die(_("the option '%s' requires '%s'"), "--unified", "--patch"); > + if (opts->patch_interhunk_context != -1) > + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch"); > } This does not catch "git checkout -U 7" because this code is only run if we're checking out paths. I think you need to check this is checkout_main() instead. > diff --git a/builtin/stash.c b/builtin/stash.c > [...] > @@ -1826,8 +1831,15 @@ static int push_stash(int argc, const char **argv, const char *prefix, > die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file"); > } > > + if (!patch_mode) { > + if (add_p_opt.context != -1) > + die(_("the option '%s' requires '%s'"), "--unified", "--patch"); > + if (add_p_opt.interhunkcontext != -1) > + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch"); > + } > + This needs to die on invalid context values as "git stash" seems to ignore the exit code of the subprocess that checks for negative values. > @@ -1877,8 +1892,17 @@ static int save_stash(int argc, const char **argv, const char *prefix, > stash_msg = strbuf_join_argv(&stash_msg_buf, argc, argv, ' '); > > memset(&ps, 0, sizeof(ps)); > + > + if (!patch_mode) { > + if (add_p_opt.context != -1) > + die(_("the option '%s' requires '%s'"), "--unified", "--patch"); > + if (add_p_opt.interhunkcontext != -1) > + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch"); This needs to die on invalid context values as "git stash" seems to ignore the exit code of the subprocess that checks for negative values. > diff --git a/commit.h b/commit.h > [...] > #include "object.h" > +#include "add-interactive.h" > struct signature_check; > struct strbuf; Lets not add this. Instead lets just add a declaration for "struct add_p_opt" like the ones in the context line so that we don't end up including everything from add-interactive.h when we only need a single struct declaration. > diff --git a/parse-options.h b/parse-options.h > [...] > +#define OPT_DIFF_UNIFIED(v) OPT_INTEGER_F('U', "unified", v, > N_("generate diffs with <n> lines context"), PARSE_OPT_NONEG) This looks good > +#define OPT_DIFF_INTERHUNK_CONTEXT(v) OPT_INTEGER_F(0, "inter-hunk-context", v, N_("show context between diff hunks up to the specified number of lines"), PARSE_OPT_NONEG) This is a bit verbose but it matches what is in diff.c. > diff --git a/t/t4032-diff-inter-hunk-context.sh b/t/t4032-diff-inter-hunk-context.sh > index bada0cbd32f7..d5aad6e143a7 100755 > --- a/t/t4032-diff-inter-hunk-context.sh > +++ b/t/t4032-diff-inter-hunk-context.sh > @@ -47,6 +47,31 @@ t() { > " > } > > +t_patch() { > + use_config= > + git config --unset diff.interHunkContext > + > + case $# in > + 4) hunks=$4; cmd="add -p -U$3";; > + 5) hunks=$5; cmd="add -p -U$3 --inter-hunk-context=$4";; > + 6) hunks=$5; cmd="add -p -U$3"; git config diff.interHunkContext $4; use_config="(diff.interHunkContext=$4) ";; > + esac > + label="$use_config$cmd, $1 common $2" > + file=f$1 > + > + if ! test -f $file > + then > + f A $1 B >$file > + git add $file > + git commit -q -m. $file > + f X $1 Y >$file > + fi > + > + test_expect_success "$label: count hunks ($hunks)" " > + test $(test_write_lines q | git $cmd $file | sed -n 's/^([0-9]*\/\([0-9]*\)) Stage this hunk.*/\1/p') = $hunks > + " > +} > + > cat <<EOF >expected.f1.0.1 || exit 1 > diff --git a/f1 b/f1 > --- a/f1 > @@ -107,6 +132,42 @@ t 3 lines 1 2 1 config > t 9 lines 3 2 2 config > t 9 lines 3 3 1 config > > +# common lines ctx intrctx hunks > +t_patch 1 line 0 2 > +t_patch 1 line 0 0 2 > +t_patch 1 line 0 1 1 > +t_patch 1 line 0 2 1 > +t_patch 1 line 1 1 > + > +t_patch 2 lines 0 2 > +t_patch 2 lines 0 0 2 > +t_patch 2 lines 0 1 2 > +t_patch 2 lines 0 2 1 > +t_patch 2 lines 1 1 > + > +t_patch 3 lines 1 2 > +t_patch 3 lines 1 0 2 > +t_patch 3 lines 1 1 1 > +t_patch 3 lines 1 2 1 > + > +t_patch 9 lines 3 2 > +t_patch 9 lines 3 2 2 > +t_patch 9 lines 3 3 1 > + > +# use diff.interHunkContext? > +t_patch 1 line 0 0 2 config > +t_patch 1 line 0 1 1 config > +t_patch 1 line 0 2 1 config > +t_patch 9 lines 3 3 1 config > +t_patch 2 lines 0 0 2 config > +t_patch 2 lines 0 1 2 config > +t_patch 2 lines 0 2 1 config > +t_patch 3 lines 1 0 2 config > +t_patch 3 lines 1 1 1 config > +t_patch 3 lines 1 2 1 config > +t_patch 9 lines 3 2 2 config > +t_patch 9 lines 3 3 1 config > + There are 29 tests here and yet more below. I think we can get the test coverage we need much more efficiently with the following added to t3701 for cmd in add checkout restore 'commit -m file' do test_expect_success "${cmd%% *} accepts -U and --inter-hunk-context" " test_write_lines a b c d e f g h i j k l m n o p q r s t u v >file && git add file && test_write_lines a b c d e F g h i j k l m n o p Q r s t u v >file && echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \ $cmd -p -U 4 --inter-hunk-context 2 >actual && test_grep \"@@ -2,20 +2,20 @@\" actual " done test_expect_success 'reset accepts -U and --inter-hunk-context' ' test_write_lines a b c d e f g h i j k l m n o p q r s t u v >file && git commit -m file file && test_write_lines a b c d e F g h i j k l m n o p Q r s t u v >file && git add file && echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \ reset -p -U 4 --inter-hunk-context 2 >actual && test_grep "@@ -2,20 +2,20 @@" actual ' test_expect_success 'stash accepts -U and --inter-hunk-context' ' test_write_lines a b c d e F g h i j k l m n o p Q r s t u v >file && git commit -m file file && test_write_lines a b c d e f g h i j k l m n o p q r s t u v >file && echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \ stash -p -U 4 --inter-hunk-context 2 >actual && test_grep "@@ -2,20 +2,20 @@" actual ' Those tests will fail if any of the commands that accept "-p" do not accept "-U" or "--inter-hunk-context" or if command-line arguments do not override the config settings. We should also add tests in t3701 to check that invalid option combinations and values are rejected like so for cmd in add checkout commit reset restore 'stash save' 'stash push' do test_expect_success "$cmd rejects invalid context options" " test_must_fail git $cmd -p -U -3 2>actual && test_grep -e \"--unified cannot be negative\" actual && test_must_fail git $cmd -p --inter-hunk-context -3 2>actual && test_grep -e \"--inter-hunk-context cannot be negative\" actual && test_must_fail git $cmd -U 7 2>actual && test_grep -E \".--unified. requires .(--interactive/)?--patch.\" actual && test_must_fail git $cmd --inter-hunk-context 2 2>actual && test_grep -E \".--inter-hunk-context. requires .(--interactive/)?--patch.\" actual " done The "checkout", "stash save" and "stash push" tests above currently fail because the implementation does not implement those checks properly. With a few tweaks this series will be looking very good Best Wishes Phillip ^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v2 4/4] add-patch: add diff.context command line overrides 2025-05-13 13:52 ` Phillip Wood @ 2025-05-13 14:39 ` Phillip Wood 2025-05-13 15:05 ` Leon Michalak 0 siblings, 1 reply; 77+ messages in thread From: Phillip Wood @ 2025-05-13 14:39 UTC (permalink / raw) To: Leon Michalak via GitGitGadget, git Cc: Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Leon Michalak On 13/05/2025 14:52, Phillip Wood wrote: >> diff --git a/builtin/stash.c b/builtin/stash.c >> [...] >> @@ -1826,8 +1831,15 @@ static int push_stash(int argc, const char >> **argv, const char *prefix, >> die(_("the option '%s' requires '%s'"), "--pathspec-file- >> nul", "--pathspec-from-file"); >> } >> + if (!patch_mode) { >> + if (add_p_opt.context != -1) >> + die(_("the option '%s' requires '%s'"), "--unified", "-- >> patch"); >> + if (add_p_opt.interhunkcontext != -1) >> + die(_("the option '%s' requires '%s'"), "--inter-hunk- >> context", "--patch"); >> + } >> + > > This needs to die on invalid context values as "git stash" seems to > ignore the exit code of the subprocess that checks for negative values. Looking more closely the problem is that it quits if there are no changes to stash before validating -U or --inter-hunk-context. I think it should validate the options before checking if there is anything to stash. Best Wishes Phillip ^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v2 4/4] add-patch: add diff.context command line overrides 2025-05-13 14:39 ` Phillip Wood @ 2025-05-13 15:05 ` Leon Michalak 2025-05-14 15:13 ` phillip.wood123 0 siblings, 1 reply; 77+ messages in thread From: Leon Michalak @ 2025-05-13 15:05 UTC (permalink / raw) To: phillip.wood Cc: Leon Michalak via GitGitGadget, git, Kristoffer Haugsbakk, Eric Sunshine, Christian Couder Hey, thanks for the thorough review Philip. I will properly digest this when I get some free time, but I just wanted to say (I probably should have mentioned this so my bad) that the reason I didn't change to test just the singular command (yet, anyway) is that someone else thought this was a good idea testing all of them, so I wasn't sure whether to touch it or not in the end, and thought I'd just submit this v2 and gather more opinions. Was this perhaps the wrong approach though? ^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v2 4/4] add-patch: add diff.context command line overrides 2025-05-13 15:05 ` Leon Michalak @ 2025-05-14 15:13 ` phillip.wood123 0 siblings, 0 replies; 77+ messages in thread From: phillip.wood123 @ 2025-05-14 15:13 UTC (permalink / raw) To: Leon Michalak, phillip.wood Cc: Leon Michalak via GitGitGadget, git, Kristoffer Haugsbakk, Eric Sunshine, Christian Couder Hi Leon On 13/05/2025 16:05, Leon Michalak wrote: > Hey, thanks for the thorough review Philip. I will properly digest > this when I get some free time, but I just wanted to say (I probably > should have mentioned this so my bad) that the reason I didn't change > to test just the singular command (yet, anyway) is that someone else > thought this was a good idea testing all of them, I'd missed that message - have you got a link to it please > so I wasn't sure > whether to touch it or not in the end, and thought I'd just submit > this v2 and gather more opinions. Was this perhaps the wrong approach > though? If you get conflicting advise then it is a good idea to mention that in the cover letter and explain which option you went with and why. Best Wishes Phillip ^ permalink raw reply [flat|nested] 77+ messages in thread
* [PATCH v3 0/4] Better support for customising context lines in --patch commands 2025-05-10 13:46 ` [PATCH v2 0/4] Better support for customising context lines in --patch commands Leon Michalak via GitGitGadget ` (3 preceding siblings ...) 2025-05-10 13:46 ` [PATCH v2 4/4] add-patch: add diff.context command line overrides Leon Michalak via GitGitGadget @ 2025-06-28 16:34 ` Leon Michalak via GitGitGadget 2025-06-28 16:34 ` [PATCH v3 1/4] test: use "test_grep" Leon Michalak via GitGitGadget ` (6 more replies) 4 siblings, 7 replies; 77+ messages in thread From: Leon Michalak via GitGitGadget @ 2025-06-28 16:34 UTC (permalink / raw) To: git Cc: Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Phillip Wood, Leon Michalak This series of patches attempt to give --interactive/--patch compatible builtins ("add", "commit", "checkout", "reset", "restore" and "stash") better support and nicer experience for configuring how many context lines are shown in diffs through a variety of ways. Prior to these patches, the user could not choose how many context lines they saw in --patch commands (apart from one workaround by using GIT_DIFF_OPTS=-u<number> ..., however this isn't a good user experience or a persistent solution). Additionally, the behaviour around reading from the diff.context and diff.interHunkContext configs was also inconsistent with other diff generating commands such as "log -p". The summarised changes below hopefully make this experience better and fix some inconsistencies: * diff.context and diff.interHunkContext configs are now respected by --patch compatible commands * --unified and --inter-hunk-context command line options have been added to --patch compatible commands (which take prescendence over file configs) * "add" and "commit" in --interactive mode now expose a new "context" subcommand which configures the amount of context lines you wish to see in subsequent diffs generated from other subcommands such as "patch" or "diff" The original discussion for this can be read at: * https://lore.kernel.org/git/CAP9jKjGb-Rcr=RLJEzeFdtrekYM+qmHy+1T1fykU3n9cV4GhGw@mail.gmail.com/ Changes since v1: * Update commit descriptions * Update tests to use the more modern and robust test_grep and test_config utils * Reword some documentation / user messages * Ensure each commit is atomic and builds/passes tests on it's own * Make new command line options DRY * Add tests for interhunk context interaction * Error if context config/command line options are negative * Drop previous last commit to do with new subcommand for --interactive add/commit. My motivations behind this patch series originally where quite simple, just for add-patch commands to respect context configs. This subcommand, after the discussion in v1, will require more thought and a larger implementation that what I had anticipated. I would prefer to leave this for another time as it's the least impactful but the most time intensive and complicated idea. Changes since v2: * Update tests to only test single command (following Philip's suggestion) * Add negative option checks * Minor commit re-wording Leon Michalak (4): test: use "test_grep" test: use "test_config" add-patch: respect diff.context configuration add-patch: add diff.context command line overrides Documentation/diff-context-options.adoc | 10 ++ Documentation/git-add.adoc | 2 + Documentation/git-checkout.adoc | 2 + Documentation/git-commit.adoc | 2 + Documentation/git-reset.adoc | 2 + Documentation/git-restore.adoc | 2 + Documentation/git-stash.adoc | 2 + add-interactive.c | 53 +++++++++-- add-interactive.h | 17 +++- add-patch.c | 11 ++- builtin/add.c | 21 ++++- builtin/checkout.c | 31 +++++- builtin/commit.c | 16 +++- builtin/reset.c | 17 +++- builtin/stash.c | 56 ++++++++--- commit.h | 3 +- parse-options.h | 2 + t/t3701-add-interactive.sh | 119 +++++++++++++++++++----- t/t4055-diff-context.sh | 72 +++++++++----- t/t9902-completion.sh | 2 + 20 files changed, 362 insertions(+), 80 deletions(-) create mode 100644 Documentation/diff-context-options.adoc base-commit: cf6f63ea6bf35173e02e18bdc6a4ba41288acff9 Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1915%2FNinjaInShade%2Finteractive-patch-context-v3 Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1915/NinjaInShade/interactive-patch-context-v3 Pull-Request: https://github.com/gitgitgadget/git/pull/1915 Range-diff vs v2: 1: 4f92a1b4c24 ! 1: 044a93014b6 test: refactor to use "test_grep" @@ Metadata Author: Leon Michalak <leonmichalak6@gmail.com> ## Commit message ## - test: refactor to use "test_grep" + test: use "test_grep" - Refactor to use the modern "test_grep" test utility instead of regular - "grep" which provides better debug information if tests fail. + Use the modern "test_grep" test utility instead of regular "grep" which + provides better debug information if tests fail. This is a prerequisite to the commits that follow which add to both test files. 2: 75424cb8e1c ! 2: e5c40d37750 test: refactor to use "test_config" @@ Metadata Author: Leon Michalak <leonmichalak6@gmail.com> ## Commit message ## - test: refactor to use "test_config" + test: use "test_config" - Refactor to use the modern "test_config" test utility instead of manual - "git config" as the former provides clean up on test completion. + Use the modern "test_config" test utility instead of manual"git config" + as the former provides clean up on test completion. This is a prerequisite to the commits that follow which add to this test file. 3: f16d3de8611 ! 3: 1ec8a138486 add-patch: respect diff.context configuration @@ Commit message the user's diff.context and diff.interHunkContext file configurations. This patch fixes this inconsistency. + This is because the plumbing commands used by "git add -p" to generate + the diff do not read those config settings. Fix this by reading the + config before generating the patch and passing it along to the diff + command with the "-U" and "--inter-hunk-context" command-line options. + Signed-off-by: Leon Michalak <leonmichalak6@gmail.com> ## add-interactive.c ## @@ add-interactive.c: void init_add_i_state(struct add_i_state *s, struct repositor + die(_("%s cannot be negative"), "diff.context"); + else + s->context = context; -+ }; ++ } + if (!repo_config_get_int(r, "diff.interHunkContext", &interhunkcontext)) { + if (interhunkcontext < 0) + die(_("%s cannot be negative"), "diff.interHunkContext"); + else + s->interhunkcontext = interhunkcontext; -+ }; ++ } + repo_config_get_bool(r, "interactive.singlekey", &s->use_single_key); if (s->use_single_key) @@ add-patch.c: static int parse_diff(struct add_p_state *s, const struct pathspec strvec_pushf(&args, "--diff-algorithm=%s", diff_algorithm); if (s->revision) { - ## t/t4055-diff-context.sh ## -@@ t/t4055-diff-context.sh: test_expect_success 'diff.context honored by "log"' ' - test_grep "^ firstline" output + ## t/t3701-add-interactive.sh ## +@@ t/t3701-add-interactive.sh: test_expect_success 'hunk splitting works with diff.suppressBlankEmpty' ' + test_cmp expect actual ' -+test_expect_success 'diff.context honored by "add"' ' -+ git add -p >output && -+ test_grep ! firstline output && -+ test_config diff.context 8 && -+ git log -1 -p >output && -+ test_grep "^ firstline" output -+' -+ -+test_expect_success 'diff.context honored by "commit"' ' -+ ! git commit -p >output && -+ test_grep ! firstline output && -+ test_config diff.context 8 && -+ ! git commit -p >output && -+ test_grep "^ firstline" output -+' -+ -+test_expect_success 'diff.context honored by "checkout"' ' -+ git checkout -p >output && -+ test_grep ! firstline output && -+ test_config diff.context 8 && -+ git checkout -p >output && -+ test_grep "^ firstline" output -+' -+ -+test_expect_success 'diff.context honored by "stash"' ' -+ ! git stash -p >output && -+ test_grep ! firstline output && -+ test_config diff.context 8 && -+ ! git stash -p >output && -+ test_grep "^ firstline" output ++test_expect_success 'add -p respects diff.context' ' ++ test_write_lines a b c d e f g h i j k l m >file && ++ git add file && ++ test_write_lines a b c d e f G h i j k l m >file && ++ echo y | git -c diff.context=5 add -p >actual && ++ test_grep "@@ -2,11 +2,11 @@" actual +' + -+test_expect_success 'diff.context honored by "restore"' ' -+ git restore -p >output && -+ test_grep ! firstline output && -+ test_config diff.context 8 && -+ git restore -p >output && -+ test_grep "^ firstline" output ++test_expect_success 'add -p respects diff.interHunkContext' ' ++ test_write_lines a b c d e f g h i j k l m n o p q r s >file && ++ git add file && ++ test_write_lines a b c d E f g i i j k l m N o p q r s >file && ++ echo y | git -c diff.interhunkcontext=2 add -p >actual && ++ test_grep "@@ -2,16 +2,16 @@" actual +' + - test_expect_success 'The -U option overrides diff.context' ' - test_config diff.context 8 && - git log -U4 -1 >output && -@@ t/t4055-diff-context.sh: test_expect_success 'negative integer config parsing' ' - test_grep "bad config variable" output - ' - -+test_expect_success 'negative integer config parsing by "add"' ' ++test_expect_success 'add -p rejects negative diff.context' ' + test_config diff.context -1 && + test_must_fail git add -p 2>output && + test_grep "diff.context cannot be negative" output +' + -+test_expect_success 'negative integer config parsing by "commit"' ' -+ test_config diff.context -1 && -+ test_must_fail git commit -p 2>output && -+ test_grep "bad config variable" output -+' -+ -+test_expect_success 'negative integer config parsing by "checkout"' ' -+ test_config diff.context -1 && -+ test_must_fail git checkout -p 2>output && -+ test_grep "diff.context cannot be negative" output -+' -+ -+test_expect_success 'negative integer config parsing by "stash"' ' -+ test_config diff.context -1 && -+ test_must_fail git stash -p 2>output && -+ test_grep "diff.context cannot be negative" output -+' -+ -+test_expect_success 'negative integer config parsing by "restore"' ' -+ test_config diff.context -1 && -+ test_must_fail git restore -p 2>output && -+ test_grep "diff.context cannot be negative" output -+' -+ - test_expect_success '-U0 is valid, so is diff.context=0' ' - test_config diff.context 0 && - git diff >output && + test_done 4: 973dfadd1b3 ! 4: b68c58b667c add-patch: add diff.context command line overrides @@ Documentation/git-checkout.adoc: section of linkgit:git-add[1] to learn how to o +include::diff-context-options.adoc[] + - --ignore-other-worktrees:: + `--ignore-other-worktrees`:: `git checkout` refuses when the wanted branch is already checked out or otherwise in use by another worktree. This option makes @@ builtin/add.c: static struct option builtin_add_options[] = { OPT_BOOL('e', "edit", &edit_interactive, N_("edit current diff and apply")), OPT__FORCE(&ignored_too, N_("allow adding otherwise ignored files"), 0), OPT_BOOL('u', "update", &take_worktree_changes, N_("update tracked files")), +@@ builtin/add.c: int cmd_add(int argc, + prepare_repo_settings(repo); + repo->settings.command_requires_full_index = 0; + ++ if (add_p_opt.context < -1) ++ die(_("'%s' cannot be negative"), "--unified"); ++ if (add_p_opt.interhunkcontext < -1) ++ die(_("'%s' cannot be negative"), "--inter-hunk-context"); ++ + if (patch_interactive) + add_interactive = 1; + if (add_interactive) { @@ builtin/add.c: int cmd_add(int argc, die(_("options '%s' and '%s' cannot be used together"), "--dry-run", "--interactive/--patch"); if (pathspec_from_file) @@ builtin/checkout.c: static int checkout_paths(const struct checkout_opts *opts, - &opts->pathspec); + return !!run_add_p(the_repository, patch_mode, &add_p_opt, + rev, &opts->pathspec); -+ } else { -+ if (opts->patch_context != -1) -+ die(_("the option '%s' requires '%s'"), "--unified", "--patch"); -+ if (opts->patch_interhunk_context != -1) -+ die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch"); } repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR); @@ builtin/checkout.c: static struct option *add_checkout_path_options(struct check 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), +@@ builtin/checkout.c: static int checkout_main(int argc, const char **argv, const char *prefix, + argc = parse_options(argc, argv, prefix, options, + usagestr, parseopt_flags); + ++ if (opts->patch_context < -1) ++ die(_("'%s' cannot be negative"), "--unified"); ++ if (opts->patch_interhunk_context < -1) ++ die(_("'%s' cannot be negative"), "--inter-hunk-context"); ++ ++ if (!opts->patch_mode) { ++ if (opts->patch_context != -1) ++ die(_("the option '%s' requires '%s'"), "--unified", "--patch"); ++ if (opts->patch_interhunk_context != -1) ++ die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch"); ++ } ++ + if (opts->show_progress < 0) { + if (opts->quiet) + opts->show_progress = 0; ## builtin/commit.c ## @@ @@ builtin/commit.c: static const char *edit_message, *use_message; 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 */ +@@ builtin/commit.c: static const char *prepare_index(const char **argv, const char *prefix, + const char *ret; + char *path = NULL; + ++ if (add_p_opt.context < -1) ++ die(_("'%s' cannot be negative"), "--unified"); ++ if (add_p_opt.interhunkcontext < -1) ++ die(_("'%s' cannot be negative"), "--inter-hunk-context"); ++ + if (is_status) + refresh_flags |= REFRESH_UNMERGED; + parse_pathspec(&pathspec, 0, @@ builtin/commit.c: static const char *prepare_index(const char **argv, const char *prefix, old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT)); setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1); @@ builtin/reset.c: int cmd_reset(int argc, 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), +@@ builtin/reset.c: int cmd_reset(int argc, + oidcpy(&oid, &tree->object.oid); + } + ++ if (add_p_opt.context < -1) ++ die(_("'%s' cannot be negative"), "--unified"); ++ if (add_p_opt.interhunkcontext < -1) ++ die(_("'%s' cannot be negative"), "--inter-hunk-context"); ++ + prepare_repo_settings(the_repository); + the_repository->settings.command_requires_full_index = 0; + @@ builtin/reset.c: int cmd_reset(int argc, if (reset_type != NONE) die(_("options '%s' and '%s' cannot be used together"), "--patch", "--{hard,mixed,soft}"); @@ builtin/stash.c: static int push_stash(int argc, const char **argv, const char * + if (add_p_opt.interhunkcontext != -1) + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch"); + } ++ ++ if (add_p_opt.context < -1) ++ die(_("'%s' cannot be negative"), "--unified"); ++ if (add_p_opt.interhunkcontext < -1) ++ die(_("'%s' cannot be negative"), "--inter-hunk-context"); + ret = do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode, - include_untracked, only_staged); @@ builtin/stash.c: static int save_stash(int argc, const char **argv, const char * memset(&ps, 0, sizeof(ps)); + ++ if (add_p_opt.context < -1) ++ die(_("'%s' cannot be negative"), "--unified"); ++ if (add_p_opt.interhunkcontext < -1) ++ die(_("'%s' cannot be negative"), "--inter-hunk-context"); ++ + if (!patch_mode) { + if (add_p_opt.context != -1) + die(_("the option '%s' requires '%s'"), "--unified", "--patch"); @@ parse-options.h: int parse_opt_tracking_mode(const struct option *, const char * #define OPT_IPVERSION(v) \ OPT_SET_INT_F('4', "ipv4", (v), N_("use IPv4 addresses only"), \ - ## t/t4032-diff-inter-hunk-context.sh ## -@@ t/t4032-diff-inter-hunk-context.sh: t() { - " - } + ## t/t3701-add-interactive.sh ## +@@ t/t3701-add-interactive.sh: test_expect_success 'add -p rejects negative diff.context' ' + test_grep "diff.context cannot be negative" output + ' -+t_patch() { -+ use_config= -+ git config --unset diff.interHunkContext -+ -+ case $# in -+ 4) hunks=$4; cmd="add -p -U$3";; -+ 5) hunks=$5; cmd="add -p -U$3 --inter-hunk-context=$4";; -+ 6) hunks=$5; cmd="add -p -U$3"; git config diff.interHunkContext $4; use_config="(diff.interHunkContext=$4) ";; -+ esac -+ label="$use_config$cmd, $1 common $2" -+ file=f$1 -+ -+ if ! test -f $file -+ then -+ f A $1 B >$file -+ git add $file -+ git commit -q -m. $file -+ f X $1 Y >$file -+ fi -+ -+ test_expect_success "$label: count hunks ($hunks)" " -+ test $(test_write_lines q | git $cmd $file | sed -n 's/^([0-9]*\/\([0-9]*\)) Stage this hunk.*/\1/p') = $hunks ++for cmd in add checkout restore 'commit -m file' ++do ++ test_expect_success "${cmd%% *} accepts -U and --inter-hunk-context" " ++ test_write_lines a b c d e f g h i j k l m n o p q r s t u v >file && ++ git add file && ++ test_write_lines a b c d e F g h i j k l m n o p Q r s t u v >file && ++ echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \ ++ $cmd -p -U 4 --inter-hunk-context 2 >actual && ++ test_grep \"@@ -2,20 +2,20 @@\" actual + " -+} ++done ++ ++test_expect_success 'reset accepts -U and --inter-hunk-context' ' ++ test_write_lines a b c d e f g h i j k l m n o p q r s t u v >file && ++ git commit -m file file && ++ test_write_lines a b c d e F g h i j k l m n o p Q r s t u v >file && ++ git add file && ++ echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \ ++ reset -p -U 4 --inter-hunk-context 2 >actual && ++ test_grep "@@ -2,20 +2,20 @@" actual ++' ++ ++test_expect_success 'stash accepts -U and --inter-hunk-context' ' ++ test_write_lines a b c d e F g h i j k l m n o p Q r s t u v >file && ++ git commit -m file file && ++ test_write_lines a b c d e f g h i j k l m n o p q r s t u v >file && ++ echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \ ++ stash -p -U 4 --inter-hunk-context 2 >actual && ++ test_grep "@@ -2,20 +2,20 @@" actual ++' ++ ++for cmd in add checkout commit reset restore 'stash save' 'stash push' ++do ++ test_expect_success "$cmd rejects invalid context options" " ++ test_must_fail git $cmd -p -U -3 2>actual && ++ cat actual | echo && ++ test_grep -e \"'--unified' cannot be negative\" actual && ++ ++ test_must_fail git $cmd -p --inter-hunk-context -3 2>actual && ++ test_grep -e \"'--inter-hunk-context' cannot be negative\" actual && ++ ++ test_must_fail git $cmd -U 7 2>actual && ++ test_grep -E \"'--unified' requires '(--interactive/)?--patch'\" actual && ++ ++ test_must_fail git $cmd --inter-hunk-context 2 2>actual && ++ test_grep -E \"'--inter-hunk-context' requires '(--interactive/)?--patch'\" actual ++ " ++done + - cat <<EOF >expected.f1.0.1 || exit 1 - diff --git a/f1 b/f1 - --- a/f1 -@@ t/t4032-diff-inter-hunk-context.sh: t 3 lines 1 2 1 config - t 9 lines 3 2 2 config - t 9 lines 3 3 1 config - -+# common lines ctx intrctx hunks -+t_patch 1 line 0 2 -+t_patch 1 line 0 0 2 -+t_patch 1 line 0 1 1 -+t_patch 1 line 0 2 1 -+t_patch 1 line 1 1 -+ -+t_patch 2 lines 0 2 -+t_patch 2 lines 0 0 2 -+t_patch 2 lines 0 1 2 -+t_patch 2 lines 0 2 1 -+t_patch 2 lines 1 1 -+ -+t_patch 3 lines 1 2 -+t_patch 3 lines 1 0 2 -+t_patch 3 lines 1 1 1 -+t_patch 3 lines 1 2 1 -+ -+t_patch 9 lines 3 2 -+t_patch 9 lines 3 2 2 -+t_patch 9 lines 3 3 1 -+ -+# use diff.interHunkContext? -+t_patch 1 line 0 0 2 config -+t_patch 1 line 0 1 1 config -+t_patch 1 line 0 2 1 config -+t_patch 9 lines 3 3 1 config -+t_patch 2 lines 0 0 2 config -+t_patch 2 lines 0 1 2 config -+t_patch 2 lines 0 2 1 config -+t_patch 3 lines 1 0 2 config -+t_patch 3 lines 1 1 1 config -+t_patch 3 lines 1 2 1 config -+t_patch 9 lines 3 2 2 config -+t_patch 9 lines 3 3 1 config -+ - test_expect_success 'diff.interHunkContext invalid' ' - git config diff.interHunkContext asdf && - test_must_fail git diff && + test_done ## t/t4055-diff-context.sh ## @@ t/t4055-diff-context.sh: test_expect_success 'The -U option overrides diff.context' ' -- gitgitgadget ^ permalink raw reply [flat|nested] 77+ messages in thread
* [PATCH v3 1/4] test: use "test_grep" 2025-06-28 16:34 ` [PATCH v3 0/4] Better support for customising context lines in --patch commands Leon Michalak via GitGitGadget @ 2025-06-28 16:34 ` Leon Michalak via GitGitGadget 2025-06-30 16:23 ` Junio C Hamano 2025-06-28 16:34 ` [PATCH v3 2/4] test: use "test_config" Leon Michalak via GitGitGadget ` (5 subsequent siblings) 6 siblings, 1 reply; 77+ messages in thread From: Leon Michalak via GitGitGadget @ 2025-06-28 16:34 UTC (permalink / raw) To: git Cc: Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Phillip Wood, Leon Michalak, Leon Michalak From: Leon Michalak <leonmichalak6@gmail.com> Use the modern "test_grep" test utility instead of regular "grep" which provides better debug information if tests fail. This is a prerequisite to the commits that follow which add to both test files. Signed-off-by: Leon Michalak <leonmichalak6@gmail.com> --- t/t3701-add-interactive.sh | 48 +++++++++++++++++++------------------- t/t4055-diff-context.sh | 28 +++++++++++----------- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index b8a05d95f3f1..b088ee141ff4 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -63,7 +63,7 @@ test_expect_success 'setup (initial)' ' ' test_expect_success 'status works (initial)' ' git add -i </dev/null >output && - grep "+1/-0 *+2/-0 file" output + test_grep "+1/-0 *+2/-0 file" output ' test_expect_success 'setup expected' ' @@ -86,7 +86,7 @@ test_expect_success 'revert works (initial)' ' git add file && test_write_lines r 1 | git add -i && git ls-files >output && - ! grep . output + test_grep ! . output ' test_expect_success 'add untracked (multiple)' ' @@ -109,7 +109,7 @@ test_expect_success 'setup (commit)' ' ' test_expect_success 'status works (commit)' ' git add -i </dev/null >output && - grep "+1/-0 *+2/-0 file" output + test_grep "+1/-0 *+2/-0 file" output ' test_expect_success 'update can stage deletions' ' @@ -141,7 +141,7 @@ test_expect_success 'revert works (commit)' ' git add file && test_write_lines r 1 | git add -i && git add -i </dev/null >output && - grep "unchanged *+3/-0 file" output + test_grep "unchanged *+3/-0 file" output ' test_expect_success 'reject multi-key input' ' @@ -185,7 +185,7 @@ test_expect_success 'setup fake editor' ' test_expect_success 'bad edit rejected' ' git reset && test_write_lines e n d | git add -p >output && - grep "hunk does not apply" output + test_grep "hunk does not apply" output ' test_expect_success 'setup patch' ' @@ -198,7 +198,7 @@ test_expect_success 'setup patch' ' test_expect_success 'garbage edit rejected' ' git reset && test_write_lines e n d | git add -p >output && - grep "hunk does not apply" output + test_grep "hunk does not apply" output ' test_expect_success 'setup patch' ' @@ -313,8 +313,8 @@ test_expect_success FILEMODE 'stage mode and hunk' ' chmod +x file && printf "y\\ny\\n" | git add -p && git diff --cached file >out && - grep "new mode" out && - grep "+content" out && + test_grep "new mode" out && + test_grep "+content" out && git diff file >out && test_must_be_empty out ' @@ -636,7 +636,7 @@ test_expect_success 'split hunk "add -p (edit)"' ' printf "%s\n" s e q n q q | EDITOR=: git add -p && git diff >actual && - ! grep "^+15" actual + test_grep ! "^+15" actual ' test_expect_success 'split hunk "add -p (no, yes, edit)"' ' @@ -648,7 +648,7 @@ test_expect_success 'split hunk "add -p (no, yes, edit)"' ' EDITOR=: git add -p 2>error && test_must_be_empty error && git diff >actual && - ! grep "^+31" actual + test_grep ! "^+31" actual ' test_expect_success 'split hunk with incomplete line at end' ' @@ -682,7 +682,7 @@ test_expect_success 'edit, adding lines to the first hunk' ' EDITOR=./fake_editor.sh git add -p 2>error && test_must_be_empty error && git diff --cached >actual && - grep "^+22" actual + test_grep "^+22" actual ' test_expect_success 'patch mode ignores unmerged entries' ' @@ -696,7 +696,7 @@ test_expect_success 'patch mode ignores unmerged entries' ' test_must_fail git merge side && echo changed >non-conflict.t && echo y | git add -p >output && - ! grep a/conflict.t output && + test_grep ! a/conflict.t output && cat >expected <<-\EOF && * Unmerged path conflict.t diff --git a/non-conflict.t b/non-conflict.t @@ -728,7 +728,7 @@ test_expect_success 'diffs can be colorized' ' # We do not want to depend on the exact coloring scheme # git uses for diffs, so just check that we saw some kind of color. - grep "$(printf "\\033")" output + test_grep "$(printf "\\033")" output ' test_expect_success 'colors can be overridden' ' @@ -743,7 +743,7 @@ test_expect_success 'colors can be overridden' ' -c color.interactive.error=blue \ add -i 2>err.raw <input && test_decode_color <err.raw >err && - grep "<BLUE>Huh (trigger)?<RESET>" err && + test_grep "<BLUE>Huh (trigger)?<RESET>" err && test_write_lines help quit >input && force_color git \ @@ -863,7 +863,7 @@ test_expect_success 'colorized diffs respect diff.wsErrorHighlight' ' printf y >y && force_color git -c diff.wsErrorHighlight=all add -p >output.raw 2>&1 <y && test_decode_color <output.raw >output && - grep "old<" output + test_grep "old<" output ' test_expect_success 'diffFilter filters diff' ' @@ -876,7 +876,7 @@ test_expect_success 'diffFilter filters diff' ' # avoid depending on the exact coloring or content of the prompts, # and just make sure we saw our diff prefixed - grep foo:.*content output + test_grep foo:.*content output ' test_expect_success 'detect bogus diffFilter output' ' @@ -886,7 +886,7 @@ test_expect_success 'detect bogus diffFilter output' ' test_config interactive.diffFilter "sed 6d" && printf y >y && force_color test_must_fail git add -p <y >output 2>&1 && - grep "mismatched output" output + test_grep "mismatched output" output ' test_expect_success 'handle iffy colored hunk headers' ' @@ -896,7 +896,7 @@ test_expect_success 'handle iffy colored hunk headers' ' printf n >n && force_color git -c interactive.diffFilter="sed s/.*@@.*/XX/" \ add -p >output 2>&1 <n && - grep "^XX$" output + test_grep "^XX$" output ' test_expect_success 'handle very large filtered diff' ' @@ -1002,7 +1002,7 @@ test_expect_success 'add -p does not expand argument lists' ' # update it, but we want to be sure that our "." pathspec # was not expanded into the argument list of any command. # So look only for "not-changed". - ! grep -E "^trace: (built-in|exec|run_command): .*not-changed" trace.out + test_grep ! -E "^trace: (built-in|exec|run_command): .*not-changed" trace.out ' test_expect_success 'hunk-editing handles custom comment char' ' @@ -1072,21 +1072,21 @@ test_expect_success 'setup different kinds of dirty submodules' ' test_expect_success 'status ignores dirty submodules (except HEAD)' ' git -C for-submodules add -i </dev/null >output && - grep dirty-head output && - grep dirty-both-ways output && - ! grep dirty-otherwise output + test_grep dirty-head output && + test_grep dirty-both-ways output && + test_grep ! dirty-otherwise output ' test_expect_success 'handle submodules' ' echo 123 >>for-submodules/dirty-otherwise/initial.t && force_color git -C for-submodules add -p dirty-otherwise >output 2>&1 && - grep "No changes" output && + test_grep "No changes" output && force_color git -C for-submodules add -p dirty-head >output 2>&1 <y && git -C for-submodules ls-files --stage dirty-head >actual && rev="$(git -C for-submodules/dirty-head rev-parse HEAD)" && - grep "$rev" actual + test_grep "$rev" actual ' test_expect_success 'set up pathological context' ' diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh index ec2804eea67c..c66f966a3ab3 100755 --- a/t/t4055-diff-context.sh +++ b/t/t4055-diff-context.sh @@ -38,36 +38,36 @@ test_expect_success 'setup' ' test_expect_success 'the default number of context lines is 3' ' git diff >output && - ! grep "^ d" output && - grep "^ e" output && - grep "^ j" output && - ! grep "^ k" output + test_grep ! "^ d" output && + test_grep "^ e" output && + test_grep "^ j" output && + test_grep ! "^ k" output ' test_expect_success 'diff.context honored by "log"' ' git log -1 -p >output && - ! grep firstline output && + test_grep ! firstline output && git config diff.context 8 && git log -1 -p >output && - grep "^ firstline" output + test_grep "^ firstline" output ' test_expect_success 'The -U option overrides diff.context' ' git config diff.context 8 && git log -U4 -1 >output && - ! grep "^ firstline" output + test_grep ! "^ firstline" output ' test_expect_success 'diff.context honored by "diff"' ' git config diff.context 8 && git diff >output && - grep "^ firstline" output + test_grep "^ firstline" output ' test_expect_success 'plumbing not affected' ' git config diff.context 8 && git diff-files -p >output && - ! grep "^ firstline" output + test_grep ! "^ firstline" output ' test_expect_success 'non-integer config parsing' ' @@ -85,8 +85,8 @@ test_expect_success 'negative integer config parsing' ' test_expect_success '-U0 is valid, so is diff.context=0' ' git config diff.context 0 && git diff >output && - grep "^-ADDED" output && - grep "^+MODIFIED" output + test_grep "^-ADDED" output && + test_grep "^+MODIFIED" output ' test_expect_success '-U2147483647 works' ' @@ -94,9 +94,9 @@ test_expect_success '-U2147483647 works' ' test_line_count = 16 x && git diff -U2147483647 >output && test_line_count = 22 output && - grep "^-ADDED" output && - grep "^+MODIFIED" output && - grep "^+APPENDED" output + test_grep "^-ADDED" output && + test_grep "^+MODIFIED" output && + test_grep "^+APPENDED" output ' test_done -- gitgitgadget ^ permalink raw reply related [flat|nested] 77+ messages in thread
* Re: [PATCH v3 1/4] test: use "test_grep" 2025-06-28 16:34 ` [PATCH v3 1/4] test: use "test_grep" Leon Michalak via GitGitGadget @ 2025-06-30 16:23 ` Junio C Hamano 0 siblings, 0 replies; 77+ messages in thread From: Junio C Hamano @ 2025-06-30 16:23 UTC (permalink / raw) To: Leon Michalak via GitGitGadget Cc: git, Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Phillip Wood, Leon Michalak "Leon Michalak via GitGitGadget" <gitgitgadget@gmail.com> writes: > From: Leon Michalak <leonmichalak6@gmail.com> > > Use the modern "test_grep" test utility instead of regular "grep" which > provides better debug information if tests fail. > > This is a prerequisite to the commits that follow which add to both test > files. Just a terminology thing, but we would phrase the last paragraph more like As a preparatory clean-up, use the "test_grep" test utility instead of regular "grep" which provides better debug information if tests fail. to avoid saying "This does X", "This commit is Y", etc. It also avoids giving a wrong impression by misusing the word "prerequisite" which we almost always use for a step that cannot be skipped. While we add new tests to the same file, we _could_ leave these existing tests as-is, but there is a good reason to making this change beforehand, which we often call is a "preparatory clean-up". > @@ -86,7 +86,7 @@ test_expect_success 'revert works (initial)' ' > git add file && > test_write_lines r 1 | git add -i && > git ls-files >output && > - ! grep . output > + test_grep ! . output > ' Good (we sometime see people got the negation wrong). Thanks. ^ permalink raw reply [flat|nested] 77+ messages in thread
* [PATCH v3 2/4] test: use "test_config" 2025-06-28 16:34 ` [PATCH v3 0/4] Better support for customising context lines in --patch commands Leon Michalak via GitGitGadget 2025-06-28 16:34 ` [PATCH v3 1/4] test: use "test_grep" Leon Michalak via GitGitGadget @ 2025-06-28 16:34 ` Leon Michalak via GitGitGadget 2025-06-30 16:35 ` Junio C Hamano 2025-06-28 16:34 ` [PATCH v3 3/4] add-patch: respect diff.context configuration Leon Michalak via GitGitGadget ` (4 subsequent siblings) 6 siblings, 1 reply; 77+ messages in thread From: Leon Michalak via GitGitGadget @ 2025-06-28 16:34 UTC (permalink / raw) To: git Cc: Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Phillip Wood, Leon Michalak, Leon Michalak From: Leon Michalak <leonmichalak6@gmail.com> Use the modern "test_config" test utility instead of manual"git config" as the former provides clean up on test completion. This is a prerequisite to the commits that follow which add to this test file. Signed-off-by: Leon Michalak <leonmichalak6@gmail.com> --- t/t4055-diff-context.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh index c66f966a3ab3..1384a8195705 100755 --- a/t/t4055-diff-context.sh +++ b/t/t4055-diff-context.sh @@ -47,43 +47,43 @@ test_expect_success 'the default number of context lines is 3' ' test_expect_success 'diff.context honored by "log"' ' git log -1 -p >output && test_grep ! firstline output && - git config diff.context 8 && + test_config diff.context 8 && git log -1 -p >output && test_grep "^ firstline" output ' test_expect_success 'The -U option overrides diff.context' ' - git config diff.context 8 && + test_config diff.context 8 && git log -U4 -1 >output && test_grep ! "^ firstline" output ' test_expect_success 'diff.context honored by "diff"' ' - git config diff.context 8 && + test_config diff.context 8 && git diff >output && test_grep "^ firstline" output ' test_expect_success 'plumbing not affected' ' - git config diff.context 8 && + test_config diff.context 8 && git diff-files -p >output && test_grep ! "^ firstline" output ' test_expect_success 'non-integer config parsing' ' - git config diff.context no && + test_config diff.context no && test_must_fail git diff 2>output && test_grep "bad numeric config value" output ' test_expect_success 'negative integer config parsing' ' - git config diff.context -1 && + test_config diff.context -1 && test_must_fail git diff 2>output && test_grep "bad config variable" output ' test_expect_success '-U0 is valid, so is diff.context=0' ' - git config diff.context 0 && + test_config diff.context 0 && git diff >output && test_grep "^-ADDED" output && test_grep "^+MODIFIED" output -- gitgitgadget ^ permalink raw reply related [flat|nested] 77+ messages in thread
* Re: [PATCH v3 2/4] test: use "test_config" 2025-06-28 16:34 ` [PATCH v3 2/4] test: use "test_config" Leon Michalak via GitGitGadget @ 2025-06-30 16:35 ` Junio C Hamano 0 siblings, 0 replies; 77+ messages in thread From: Junio C Hamano @ 2025-06-30 16:35 UTC (permalink / raw) To: Leon Michalak via GitGitGadget Cc: git, Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Phillip Wood, Leon Michalak "Leon Michalak via GitGitGadget" <gitgitgadget@gmail.com> writes: > From: Leon Michalak <leonmichalak6@gmail.com> > > Use the modern "test_config" test utility instead of manual"git config" > as the former provides clean up on test completion. Here "completion" is not "all the tests in the script are done", but "each of the test_expect_{success,failure} piece that uses test_config". Drop "modern". It was invented 14 years ago (the same can be said for test_grep which was called test_i18ngrep and had an extra purpose, which was invented in the same year). This conversion, unlike "test_grep" needs to be done a bit carefully. The fact the configuration is removed after the test piece "test_config" was used means any tests after the test that was originally using "git config" needs to be inspected to make sure it was *not* relying on the value that was left by the previous test piece. For example... > test_expect_success 'diff.context honored by "log"' ' > git log -1 -p >output && > test_grep ! firstline output && > - git config diff.context 8 && > + test_config diff.context 8 && > git log -1 -p >output && > test_grep "^ firstline" output > ' ... the test piece after this one may have assumed (wrongly! The assumption does not hold if this test failed before reaching "git config") that diff.context is still set to 8 but that is no longer the case. But that one is OK because ... > test_expect_success 'The -U option overrides diff.context' ' > - git config diff.context 8 && > + test_config diff.context 8 && > git log -U4 -1 >output && > test_grep ! "^ firstline" output > ' ... it was setting it for itself. The same can be said with other tests (not quoted in this reply). They all set things up for themselves, which is a good hygiene. That means the last one in the patch needs to be inspected carefully. We do not know from the post-context of the patch what the next test used to expect. > test_expect_success '-U0 is valid, so is diff.context=0' ' > - git config diff.context 0 && > + test_config diff.context 0 && > git diff >output && > test_grep "^-ADDED" output && > test_grep "^+MODIFIED" output The one that comes after this one is about giving an explicit command line option -U<n>. It should not be affected by a diff.context conffiguration variable that is lower-precedence so we should be OK. Thanks. ^ permalink raw reply [flat|nested] 77+ messages in thread
* [PATCH v3 3/4] add-patch: respect diff.context configuration 2025-06-28 16:34 ` [PATCH v3 0/4] Better support for customising context lines in --patch commands Leon Michalak via GitGitGadget 2025-06-28 16:34 ` [PATCH v3 1/4] test: use "test_grep" Leon Michalak via GitGitGadget 2025-06-28 16:34 ` [PATCH v3 2/4] test: use "test_config" Leon Michalak via GitGitGadget @ 2025-06-28 16:34 ` Leon Michalak via GitGitGadget 2025-06-30 16:55 ` Junio C Hamano 2025-07-01 10:00 ` Phillip Wood 2025-06-28 16:34 ` [PATCH v3 4/4] add-patch: add diff.context command line overrides Leon Michalak via GitGitGadget ` (3 subsequent siblings) 6 siblings, 2 replies; 77+ messages in thread From: Leon Michalak via GitGitGadget @ 2025-06-28 16:34 UTC (permalink / raw) To: git Cc: Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Phillip Wood, Leon Michalak, Leon Michalak From: Leon Michalak <leonmichalak6@gmail.com> Various builtins that use add-patch infrastructure do not respect the user's diff.context and diff.interHunkContext file configurations. This patch fixes this inconsistency. This is because the plumbing commands used by "git add -p" to generate the diff do not read those config settings. Fix this by reading the config before generating the patch and passing it along to the diff command with the "-U" and "--inter-hunk-context" command-line options. Signed-off-by: Leon Michalak <leonmichalak6@gmail.com> --- add-interactive.c | 17 +++++++++++++++++ add-interactive.h | 1 + add-patch.c | 6 ++++++ t/t3701-add-interactive.sh | 22 ++++++++++++++++++++++ 4 files changed, 46 insertions(+) diff --git a/add-interactive.c b/add-interactive.c index 97ff35b6f12a..e0aafb8dd02a 100644 --- a/add-interactive.c +++ b/add-interactive.c @@ -39,8 +39,12 @@ static void init_color(struct repository *r, struct add_i_state *s, void init_add_i_state(struct add_i_state *s, struct repository *r) { const char *value; + int context; + int interhunkcontext; s->r = r; + s->context = -1; + s->interhunkcontext = -1; if (repo_config_get_value(r, "color.interactive", &value)) s->use_color = -1; @@ -78,6 +82,19 @@ void init_add_i_state(struct add_i_state *s, struct repository *r) repo_config_get_string(r, "diff.algorithm", &s->interactive_diff_algorithm); + if (!repo_config_get_int(r, "diff.context", &context)) { + if (context < 0) + die(_("%s cannot be negative"), "diff.context"); + else + s->context = context; + } + if (!repo_config_get_int(r, "diff.interHunkContext", &interhunkcontext)) { + if (interhunkcontext < 0) + die(_("%s cannot be negative"), "diff.interHunkContext"); + else + s->interhunkcontext = interhunkcontext; + } + repo_config_get_bool(r, "interactive.singlekey", &s->use_single_key); if (s->use_single_key) setbuf(stdin, NULL); diff --git a/add-interactive.h b/add-interactive.h index 693f125e8e4b..c63f35b14be8 100644 --- a/add-interactive.h +++ b/add-interactive.h @@ -18,6 +18,7 @@ struct add_i_state { int use_single_key; char *interactive_diff_filter, *interactive_diff_algorithm; + int context, interhunkcontext; }; void init_add_i_state(struct add_i_state *s, struct repository *r); diff --git a/add-patch.c b/add-patch.c index 95c67d8c80c4..b43ca1600738 100644 --- a/add-patch.c +++ b/add-patch.c @@ -415,6 +415,8 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps) { struct strvec args = STRVEC_INIT; const char *diff_algorithm = s->s.interactive_diff_algorithm; + int diff_context = s->s.context; + int diff_interhunkcontext = s->s.interhunkcontext; struct strbuf *plain = &s->plain, *colored = NULL; struct child_process cp = CHILD_PROCESS_INIT; char *p, *pend, *colored_p = NULL, *colored_pend = NULL, marker = '\0'; @@ -424,6 +426,10 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps) int res; strvec_pushv(&args, s->mode->diff_cmd); + if (diff_context != -1) + strvec_pushf(&args, "--unified=%i", diff_context); + if (diff_interhunkcontext != -1) + strvec_pushf(&args, "--inter-hunk-context=%i", diff_interhunkcontext); if (diff_algorithm) strvec_pushf(&args, "--diff-algorithm=%s", diff_algorithm); if (s->revision) { diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index b088ee141ff4..18dc329ea1f6 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -1230,4 +1230,26 @@ test_expect_success 'hunk splitting works with diff.suppressBlankEmpty' ' test_cmp expect actual ' +test_expect_success 'add -p respects diff.context' ' + test_write_lines a b c d e f g h i j k l m >file && + git add file && + test_write_lines a b c d e f G h i j k l m >file && + echo y | git -c diff.context=5 add -p >actual && + test_grep "@@ -2,11 +2,11 @@" actual +' + +test_expect_success 'add -p respects diff.interHunkContext' ' + test_write_lines a b c d e f g h i j k l m n o p q r s >file && + git add file && + test_write_lines a b c d E f g i i j k l m N o p q r s >file && + echo y | git -c diff.interhunkcontext=2 add -p >actual && + test_grep "@@ -2,16 +2,16 @@" actual +' + +test_expect_success 'add -p rejects negative diff.context' ' + test_config diff.context -1 && + test_must_fail git add -p 2>output && + test_grep "diff.context cannot be negative" output +' + test_done -- gitgitgadget ^ permalink raw reply related [flat|nested] 77+ messages in thread
* Re: [PATCH v3 3/4] add-patch: respect diff.context configuration 2025-06-28 16:34 ` [PATCH v3 3/4] add-patch: respect diff.context configuration Leon Michalak via GitGitGadget @ 2025-06-30 16:55 ` Junio C Hamano 2025-07-01 10:00 ` Phillip Wood 1 sibling, 0 replies; 77+ messages in thread From: Junio C Hamano @ 2025-06-30 16:55 UTC (permalink / raw) To: Leon Michalak via GitGitGadget Cc: git, Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Phillip Wood, Leon Michalak "Leon Michalak via GitGitGadget" <gitgitgadget@gmail.com> writes: > From: Leon Michalak <leonmichalak6@gmail.com> > > Various builtins that use add-patch infrastructure do not respect > the user's diff.context and diff.interHunkContext file configurations. Great. "add-patch.c" invokes "diff-files", "diff-index" plumbing commands to do its thing, and these plumbing commands deliberately ignore such configuration variables, unlike "diff" Porcelain command that is meant for end-user consumption. > This patch fixes this inconsistency. If we were spelling it out, we would say "Fix this inconsistency" in imperative. But you never talked about "this inconsistency" so far, so it is not just confusing. It hints an incorrect conclusion that the difference between plumbing diff-{files,index,tree} and Porcelain diff is an inconsistency that needs to be "fixed", which is not true. Follow the first paragraph with an explanation why it is a bad thing. For example: The user may be used to seeing their diffs with customized context size, but not in the patches "git add -p" shows them to pick from. That would implicitly tell readers that we would want the patch shown by "add -p" generated with diff.context given by the user. So we can outline the solution next. Teach add-patch infrastructure to read these configuration variables and pass their values when spawning the underlying plumbing commands as their command line option. or something. > @@ -39,8 +39,12 @@ static void init_color(struct repository *r, struct add_i_state *s, > void init_add_i_state(struct add_i_state *s, struct repository *r) > { > const char *value; > + int context; > + int interhunkcontext; > > s->r = r; > + s->context = -1; > + s->interhunkcontext = -1; Hmph, context/interhunkcontext variables serve no purpose other than peeking into the value before assigning it to s->{context,interhunkcontext} members. In a sense, they may be confusing than they are worth. > if (repo_config_get_value(r, "color.interactive", &value)) > s->use_color = -1; > @@ -78,6 +82,19 @@ void init_add_i_state(struct add_i_state *s, struct repository *r) > repo_config_get_string(r, "diff.algorithm", > &s->interactive_diff_algorithm); > > + if (!repo_config_get_int(r, "diff.context", &context)) { > + if (context < 0) > + die(_("%s cannot be negative"), "diff.context"); > + else > + s->context = context; > + } Would the code be easier to understand if it is written more like if (!repo_config_get_int(r, "diff.context", &s->context)) { if (s->context < 0) die(...); } with or without {braces} around the (technically) single statement block? > + if (!repo_config_get_int(r, "diff.interHunkContext", &interhunkcontext)) { > + if (interhunkcontext < 0) > + die(_("%s cannot be negative"), "diff.interHunkContext"); > + else > + s->interhunkcontext = interhunkcontext; > + } Ditto. > diff --git a/add-interactive.h b/add-interactive.h > index 693f125e8e4b..c63f35b14be8 100644 > --- a/add-interactive.h > +++ b/add-interactive.h > @@ -18,6 +18,7 @@ struct add_i_state { > > int use_single_key; > char *interactive_diff_filter, *interactive_diff_algorithm; > + int context, interhunkcontext; > }; > > void init_add_i_state(struct add_i_state *s, struct repository *r); > diff --git a/add-patch.c b/add-patch.c > index 95c67d8c80c4..b43ca1600738 100644 > --- a/add-patch.c > +++ b/add-patch.c > @@ -415,6 +415,8 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps) > { > struct strvec args = STRVEC_INIT; > const char *diff_algorithm = s->s.interactive_diff_algorithm; > + int diff_context = s->s.context; > + int diff_interhunkcontext = s->s.interhunkcontext; > struct strbuf *plain = &s->plain, *colored = NULL; > struct child_process cp = CHILD_PROCESS_INIT; > char *p, *pend, *colored_p = NULL, *colored_pend = NULL, marker = '\0'; > @@ -424,6 +426,10 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps) > int res; > > strvec_pushv(&args, s->mode->diff_cmd); > + if (diff_context != -1) > + strvec_pushf(&args, "--unified=%i", diff_context); > + if (diff_interhunkcontext != -1) > + strvec_pushf(&args, "--inter-hunk-context=%i", diff_interhunkcontext); Ditto. What does it buy us to have these two local variables? We have the state object 's' available to us here, right? Thanks. ^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v3 3/4] add-patch: respect diff.context configuration 2025-06-28 16:34 ` [PATCH v3 3/4] add-patch: respect diff.context configuration Leon Michalak via GitGitGadget 2025-06-30 16:55 ` Junio C Hamano @ 2025-07-01 10:00 ` Phillip Wood 1 sibling, 0 replies; 77+ messages in thread From: Phillip Wood @ 2025-07-01 10:00 UTC (permalink / raw) To: Leon Michalak via GitGitGadget, git Cc: Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Leon Michalak Hi Leon On 28/06/2025 17:34, Leon Michalak via GitGitGadget wrote: > From: Leon Michalak <leonmichalak6@gmail.com> > > Various builtins that use add-patch infrastructure do not respect > the user's diff.context and diff.interHunkContext file configurations. > This patch fixes this inconsistency. > > This is because the plumbing commands used by "git add -p" to generate > the diff do not read those config settings. Fix this by reading the > config before generating the patch and passing it along to the diff > command with the "-U" and "--inter-hunk-context" command-line options. This looks good to me, thanks for working on it Phillip > Signed-off-by: Leon Michalak <leonmichalak6@gmail.com> > --- > add-interactive.c | 17 +++++++++++++++++ > add-interactive.h | 1 + > add-patch.c | 6 ++++++ > t/t3701-add-interactive.sh | 22 ++++++++++++++++++++++ > 4 files changed, 46 insertions(+) > > diff --git a/add-interactive.c b/add-interactive.c > index 97ff35b6f12a..e0aafb8dd02a 100644 > --- a/add-interactive.c > +++ b/add-interactive.c > @@ -39,8 +39,12 @@ static void init_color(struct repository *r, struct add_i_state *s, > void init_add_i_state(struct add_i_state *s, struct repository *r) > { > const char *value; > + int context; > + int interhunkcontext; > > s->r = r; > + s->context = -1; > + s->interhunkcontext = -1; > > if (repo_config_get_value(r, "color.interactive", &value)) > s->use_color = -1; > @@ -78,6 +82,19 @@ void init_add_i_state(struct add_i_state *s, struct repository *r) > repo_config_get_string(r, "diff.algorithm", > &s->interactive_diff_algorithm); > > + if (!repo_config_get_int(r, "diff.context", &context)) { > + if (context < 0) > + die(_("%s cannot be negative"), "diff.context"); > + else > + s->context = context; > + } > + if (!repo_config_get_int(r, "diff.interHunkContext", &interhunkcontext)) { > + if (interhunkcontext < 0) > + die(_("%s cannot be negative"), "diff.interHunkContext"); > + else > + s->interhunkcontext = interhunkcontext; > + } > + > repo_config_get_bool(r, "interactive.singlekey", &s->use_single_key); > if (s->use_single_key) > setbuf(stdin, NULL); > diff --git a/add-interactive.h b/add-interactive.h > index 693f125e8e4b..c63f35b14be8 100644 > --- a/add-interactive.h > +++ b/add-interactive.h > @@ -18,6 +18,7 @@ struct add_i_state { > > int use_single_key; > char *interactive_diff_filter, *interactive_diff_algorithm; > + int context, interhunkcontext; > }; > > void init_add_i_state(struct add_i_state *s, struct repository *r); > diff --git a/add-patch.c b/add-patch.c > index 95c67d8c80c4..b43ca1600738 100644 > --- a/add-patch.c > +++ b/add-patch.c > @@ -415,6 +415,8 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps) > { > struct strvec args = STRVEC_INIT; > const char *diff_algorithm = s->s.interactive_diff_algorithm; > + int diff_context = s->s.context; > + int diff_interhunkcontext = s->s.interhunkcontext; > struct strbuf *plain = &s->plain, *colored = NULL; > struct child_process cp = CHILD_PROCESS_INIT; > char *p, *pend, *colored_p = NULL, *colored_pend = NULL, marker = '\0'; > @@ -424,6 +426,10 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps) > int res; > > strvec_pushv(&args, s->mode->diff_cmd); > + if (diff_context != -1) > + strvec_pushf(&args, "--unified=%i", diff_context); > + if (diff_interhunkcontext != -1) > + strvec_pushf(&args, "--inter-hunk-context=%i", diff_interhunkcontext); > if (diff_algorithm) > strvec_pushf(&args, "--diff-algorithm=%s", diff_algorithm); > if (s->revision) { > diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh > index b088ee141ff4..18dc329ea1f6 100755 > --- a/t/t3701-add-interactive.sh > +++ b/t/t3701-add-interactive.sh > @@ -1230,4 +1230,26 @@ test_expect_success 'hunk splitting works with diff.suppressBlankEmpty' ' > test_cmp expect actual > ' > > +test_expect_success 'add -p respects diff.context' ' > + test_write_lines a b c d e f g h i j k l m >file && > + git add file && > + test_write_lines a b c d e f G h i j k l m >file && > + echo y | git -c diff.context=5 add -p >actual && > + test_grep "@@ -2,11 +2,11 @@" actual > +' > + > +test_expect_success 'add -p respects diff.interHunkContext' ' > + test_write_lines a b c d e f g h i j k l m n o p q r s >file && > + git add file && > + test_write_lines a b c d E f g i i j k l m N o p q r s >file && > + echo y | git -c diff.interhunkcontext=2 add -p >actual && > + test_grep "@@ -2,16 +2,16 @@" actual > +' > + > +test_expect_success 'add -p rejects negative diff.context' ' > + test_config diff.context -1 && > + test_must_fail git add -p 2>output && > + test_grep "diff.context cannot be negative" output > +' > + > test_done ^ permalink raw reply [flat|nested] 77+ messages in thread
* [PATCH v3 4/4] add-patch: add diff.context command line overrides 2025-06-28 16:34 ` [PATCH v3 0/4] Better support for customising context lines in --patch commands Leon Michalak via GitGitGadget ` (2 preceding siblings ...) 2025-06-28 16:34 ` [PATCH v3 3/4] add-patch: respect diff.context configuration Leon Michalak via GitGitGadget @ 2025-06-28 16:34 ` Leon Michalak via GitGitGadget 2025-06-30 17:03 ` Junio C Hamano 2025-07-01 9:59 ` Phillip Wood 2025-06-30 16:16 ` [PATCH v3 0/4] Better support for customising context lines in --patch commands Junio C Hamano ` (2 subsequent siblings) 6 siblings, 2 replies; 77+ messages in thread From: Leon Michalak via GitGitGadget @ 2025-06-28 16:34 UTC (permalink / raw) To: git Cc: Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Phillip Wood, Leon Michalak, Leon Michalak From: Leon Michalak <leonmichalak6@gmail.com> This patch compliments the previous commit, where builtins that use add-patch infrastructure now respect diff.context and diff.interHunkContext file configurations. In particular, this patch helps users who don't want to set persistent context configurations or just want a way to override them on a one-time basis, by allowing the relevant builtins to accept corresponding command line options that override the file configurations. This mimics commands such as diff and log, which allow for both context file configuration and command line overrides. Signed-off-by: Leon Michalak <leonmichalak6@gmail.com> --- Documentation/diff-context-options.adoc | 10 +++++ Documentation/git-add.adoc | 2 + Documentation/git-checkout.adoc | 2 + Documentation/git-commit.adoc | 2 + Documentation/git-reset.adoc | 2 + Documentation/git-restore.adoc | 2 + Documentation/git-stash.adoc | 2 + add-interactive.c | 36 ++++++++++++---- add-interactive.h | 16 +++++-- add-patch.c | 5 ++- builtin/add.c | 21 ++++++++-- builtin/checkout.c | 31 ++++++++++++-- builtin/commit.c | 16 ++++++- builtin/reset.c | 17 +++++++- builtin/stash.c | 56 ++++++++++++++++++++----- commit.h | 3 +- parse-options.h | 2 + t/t3701-add-interactive.sh | 49 ++++++++++++++++++++++ t/t4055-diff-context.sh | 30 +++++++++++++ t/t9902-completion.sh | 2 + 20 files changed, 271 insertions(+), 35 deletions(-) create mode 100644 Documentation/diff-context-options.adoc diff --git a/Documentation/diff-context-options.adoc b/Documentation/diff-context-options.adoc new file mode 100644 index 000000000000..e161260358ff --- /dev/null +++ b/Documentation/diff-context-options.adoc @@ -0,0 +1,10 @@ +`-U<n>`:: +`--unified=<n>`:: + Generate diffs with _<n>_ lines of context. Defaults to `diff.context` + or 3 if the config option is unset. + +`--inter-hunk-context=<n>`:: + Show the context between diff hunks, up to the specified _<number>_ + of lines, thereby fusing hunks that are close to each other. + Defaults to `diff.interHunkContext` or 0 if the config option + is unset. diff --git a/Documentation/git-add.adoc b/Documentation/git-add.adoc index eba0b419ce50..b7a735824d6c 100644 --- a/Documentation/git-add.adoc +++ b/Documentation/git-add.adoc @@ -104,6 +104,8 @@ This effectively runs `add --interactive`, but bypasses the initial command menu and directly jumps to the `patch` subcommand. See ``Interactive mode'' for details. +include::diff-context-options.adoc[] + `-e`:: `--edit`:: Open the diff vs. the index in an editor and let the user diff --git a/Documentation/git-checkout.adoc b/Documentation/git-checkout.adoc index ee83b6d9ba9a..40e02cfd6562 100644 --- a/Documentation/git-checkout.adoc +++ b/Documentation/git-checkout.adoc @@ -289,6 +289,8 @@ section of linkgit:git-add[1] to learn how to operate the `--patch` mode. Note that this option uses the no overlay mode by default (see also `--overlay`), and currently doesn't support overlay mode. +include::diff-context-options.adoc[] + `--ignore-other-worktrees`:: `git checkout` refuses when the wanted branch is already checked out or otherwise in use by another worktree. This option makes diff --git a/Documentation/git-commit.adoc b/Documentation/git-commit.adoc index dc219025f1eb..ae988a883b5b 100644 --- a/Documentation/git-commit.adoc +++ b/Documentation/git-commit.adoc @@ -76,6 +76,8 @@ OPTIONS which changes to commit. See linkgit:git-add[1] for details. +include::diff-context-options.adoc[] + `-C <commit>`:: `--reuse-message=<commit>`:: Take an existing _<commit>_ object, and reuse the log message diff --git a/Documentation/git-reset.adoc b/Documentation/git-reset.adoc index 53ab88c5451c..50e8a0ba6f66 100644 --- a/Documentation/git-reset.adoc +++ b/Documentation/git-reset.adoc @@ -125,6 +125,8 @@ OPTIONS separated with _NUL_ character and all other characters are taken literally (including newlines and quotes). +include::diff-context-options.adoc[] + `--`:: Do not interpret any more arguments as options. diff --git a/Documentation/git-restore.adoc b/Documentation/git-restore.adoc index 877b7772e667..1dcc2bb7aea3 100644 --- a/Documentation/git-restore.adoc +++ b/Documentation/git-restore.adoc @@ -52,6 +52,8 @@ leave out at most one of _<rev-A>__ and _<rev-B>_, in which case it defaults to Mode" section of linkgit:git-add[1] to learn how to operate the `--patch` mode. +include::diff-context-options.adoc[] + `-W`:: `--worktree`:: `-S`:: diff --git a/Documentation/git-stash.adoc b/Documentation/git-stash.adoc index 1a5177f4986c..0578c619c410 100644 --- a/Documentation/git-stash.adoc +++ b/Documentation/git-stash.adoc @@ -208,6 +208,8 @@ to learn how to operate the `--patch` mode. The `--patch` option implies `--keep-index`. You can use `--no-keep-index` to override this. +include::diff-context-options.adoc[] + -S:: --staged:: This option is only valid for `push` and `save` commands. diff --git a/add-interactive.c b/add-interactive.c index e0aafb8dd02a..c343568bf7ec 100644 --- a/add-interactive.c +++ b/add-interactive.c @@ -36,7 +36,8 @@ static void init_color(struct repository *r, struct add_i_state *s, free(key); } -void init_add_i_state(struct add_i_state *s, struct repository *r) +void init_add_i_state(struct add_i_state *s, struct repository *r, + struct add_p_opt *add_p_opt) { const char *value; int context; @@ -98,6 +99,17 @@ void init_add_i_state(struct add_i_state *s, struct repository *r) repo_config_get_bool(r, "interactive.singlekey", &s->use_single_key); if (s->use_single_key) setbuf(stdin, NULL); + + if (add_p_opt->context != -1) { + if (add_p_opt->context < 0) + die(_("%s cannot be negative"), "--unified"); + s->context = add_p_opt->context; + } + if (add_p_opt->interhunkcontext != -1) { + if (add_p_opt->interhunkcontext < 0) + die(_("%s cannot be negative"), "--inter-hunk-context"); + s->interhunkcontext = add_p_opt->interhunkcontext; + } } void clear_add_i_state(struct add_i_state *s) @@ -986,6 +998,10 @@ static int run_patch(struct add_i_state *s, const struct pathspec *ps, opts->prompt = N_("Patch update"); count = list_and_choose(s, files, opts); if (count > 0) { + struct add_p_opt add_p_opt = { + .context = s->context, + .interhunkcontext = s->interhunkcontext, + }; struct strvec args = STRVEC_INIT; struct pathspec ps_selected = { 0 }; @@ -996,7 +1012,7 @@ static int run_patch(struct add_i_state *s, const struct pathspec *ps, parse_pathspec(&ps_selected, PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL, PATHSPEC_LITERAL_PATH, "", args.v); - res = run_add_p(s->r, ADD_P_ADD, NULL, &ps_selected); + res = run_add_p(s->r, ADD_P_ADD, &add_p_opt, NULL, &ps_selected); strvec_clear(&args); clear_pathspec(&ps_selected); } @@ -1031,10 +1047,13 @@ static int run_diff(struct add_i_state *s, const struct pathspec *ps, if (count > 0) { struct child_process cmd = CHILD_PROCESS_INIT; - strvec_pushl(&cmd.args, "git", "diff", "-p", "--cached", - oid_to_hex(!is_initial ? &oid : - s->r->hash_algo->empty_tree), - "--", NULL); + strvec_pushl(&cmd.args, "git", "diff", "-p", "--cached", NULL); + if (s->context != -1) + strvec_pushf(&cmd.args, "--unified=%i", s->context); + if (s->interhunkcontext != -1) + strvec_pushf(&cmd.args, "--inter-hunk-context=%i", s->interhunkcontext); + strvec_pushl(&cmd.args, oid_to_hex(!is_initial ? &oid : + s->r->hash_algo->empty_tree), "--", NULL); for (i = 0; i < files->items.nr; i++) if (files->selected[i]) strvec_push(&cmd.args, @@ -1127,7 +1146,8 @@ static void command_prompt_help(struct add_i_state *s) _("(empty) select nothing")); } -int run_add_i(struct repository *r, const struct pathspec *ps) +int run_add_i(struct repository *r, const struct pathspec *ps, + struct add_p_opt *add_p_opt) { struct add_i_state s = { NULL }; struct print_command_item_data data = { "[", "]" }; @@ -1170,7 +1190,7 @@ int run_add_i(struct repository *r, const struct pathspec *ps) ->util = util; } - init_add_i_state(&s, r); + init_add_i_state(&s, r, add_p_opt); /* * When color was asked for, use the prompt color for diff --git a/add-interactive.h b/add-interactive.h index c63f35b14be8..4213dcd67b9a 100644 --- a/add-interactive.h +++ b/add-interactive.h @@ -3,6 +3,13 @@ #include "color.h" +struct add_p_opt { + int context; + int interhunkcontext; +}; + +#define ADD_P_OPT_INIT { .context = -1, .interhunkcontext = -1 } + struct add_i_state { struct repository *r; int use_color; @@ -21,12 +28,14 @@ struct add_i_state { int context, interhunkcontext; }; -void init_add_i_state(struct add_i_state *s, struct repository *r); +void init_add_i_state(struct add_i_state *s, struct repository *r, + struct add_p_opt *add_p_opt); void clear_add_i_state(struct add_i_state *s); struct repository; struct pathspec; -int run_add_i(struct repository *r, const struct pathspec *ps); +int run_add_i(struct repository *r, const struct pathspec *ps, + struct add_p_opt *add_p_opt); enum add_p_mode { ADD_P_ADD, @@ -37,6 +46,7 @@ enum add_p_mode { }; int run_add_p(struct repository *r, enum add_p_mode mode, - const char *revision, const struct pathspec *ps); + struct add_p_opt *o, const char *revision, + const struct pathspec *ps); #endif diff --git a/add-patch.c b/add-patch.c index b43ca1600738..c0d33820c558 100644 --- a/add-patch.c +++ b/add-patch.c @@ -1766,14 +1766,15 @@ soft_increment: } int run_add_p(struct repository *r, enum add_p_mode mode, - const char *revision, const struct pathspec *ps) + struct add_p_opt *o, const char *revision, + const struct pathspec *ps) { struct add_p_state s = { { r }, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT }; size_t i, binary_count = 0; - init_add_i_state(&s.s, r); + init_add_i_state(&s.s, r, o); if (mode == ADD_P_STASH) s.mode = &patch_mode_stash; diff --git a/builtin/add.c b/builtin/add.c index 7c292ffdc6c2..a00dab9b8fa0 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -29,6 +29,7 @@ static const char * const builtin_add_usage[] = { NULL }; static int patch_interactive, add_interactive, edit_interactive; +static struct add_p_opt add_p_opt = ADD_P_OPT_INIT; static int take_worktree_changes; static int add_renormalize; static int pathspec_file_nul; @@ -157,7 +158,7 @@ static int refresh(struct repository *repo, int verbose, const struct pathspec * int interactive_add(struct repository *repo, const char **argv, const char *prefix, - int patch) + int patch, struct add_p_opt *add_p_opt) { struct pathspec pathspec; int ret; @@ -169,9 +170,9 @@ int interactive_add(struct repository *repo, prefix, argv); if (patch) - ret = !!run_add_p(repo, ADD_P_ADD, NULL, &pathspec); + ret = !!run_add_p(repo, ADD_P_ADD, add_p_opt, NULL, &pathspec); else - ret = !!run_add_i(repo, &pathspec); + ret = !!run_add_i(repo, &pathspec, add_p_opt); clear_pathspec(&pathspec); return ret; @@ -253,6 +254,8 @@ static struct option builtin_add_options[] = { OPT_GROUP(""), OPT_BOOL('i', "interactive", &add_interactive, N_("interactive picking")), OPT_BOOL('p', "patch", &patch_interactive, N_("select hunks interactively")), + OPT_DIFF_UNIFIED(&add_p_opt.context), + OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext), OPT_BOOL('e', "edit", &edit_interactive, N_("edit current diff and apply")), OPT__FORCE(&ignored_too, N_("allow adding otherwise ignored files"), 0), OPT_BOOL('u', "update", &take_worktree_changes, N_("update tracked files")), @@ -394,6 +397,11 @@ int cmd_add(int argc, prepare_repo_settings(repo); repo->settings.command_requires_full_index = 0; + if (add_p_opt.context < -1) + die(_("'%s' cannot be negative"), "--unified"); + if (add_p_opt.interhunkcontext < -1) + die(_("'%s' cannot be negative"), "--inter-hunk-context"); + if (patch_interactive) add_interactive = 1; if (add_interactive) { @@ -401,7 +409,12 @@ int cmd_add(int argc, die(_("options '%s' and '%s' cannot be used together"), "--dry-run", "--interactive/--patch"); if (pathspec_from_file) die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--interactive/--patch"); - exit(interactive_add(repo, argv + 1, prefix, patch_interactive)); + exit(interactive_add(repo, argv + 1, prefix, patch_interactive, &add_p_opt)); + } else { + if (add_p_opt.context != -1) + die(_("the option '%s' requires '%s'"), "--unified", "--interactive/--patch"); + if (add_p_opt.interhunkcontext != -1) + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--interactive/--patch"); } if (edit_interactive) { diff --git a/builtin/checkout.c b/builtin/checkout.c index 536192d3456c..3737ba4c3920 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -61,6 +61,8 @@ static const char * const restore_usage[] = { struct checkout_opts { int patch_mode; + int patch_context; + int patch_interhunk_context; int quiet; int merge; int force; @@ -104,7 +106,12 @@ struct checkout_opts { struct tree *source_tree; }; -#define CHECKOUT_OPTS_INIT { .conflict_style = -1, .merge = -1 } +#define CHECKOUT_OPTS_INIT { \ + .conflict_style = -1, \ + .merge = -1, \ + .patch_context = -1, \ + .patch_interhunk_context = -1, \ +} struct branch_info { char *name; /* The short name used */ @@ -539,6 +546,10 @@ static int checkout_paths(const struct checkout_opts *opts, if (opts->patch_mode) { enum add_p_mode patch_mode; + struct add_p_opt add_p_opt = { + .context = opts->patch_context, + .interhunkcontext = opts->patch_interhunk_context, + }; const char *rev = new_branch_info->name; char rev_oid[GIT_MAX_HEXSZ + 1]; @@ -564,8 +575,8 @@ static int checkout_paths(const struct checkout_opts *opts, else BUG("either flag must have been set, worktree=%d, index=%d", opts->checkout_worktree, opts->checkout_index); - return !!run_add_p(the_repository, patch_mode, rev, - &opts->pathspec); + return !!run_add_p(the_repository, patch_mode, &add_p_opt, + rev, &opts->pathspec); } repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR); @@ -1738,6 +1749,8 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts, N_("checkout their version for unmerged files"), 3, PARSE_OPT_NONEG), OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")), + OPT_DIFF_UNIFIED(&opts->patch_context), + OPT_DIFF_INTERHUNK_CONTEXT(&opts->patch_interhunk_context), 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), @@ -1780,6 +1793,18 @@ static int checkout_main(int argc, const char **argv, const char *prefix, argc = parse_options(argc, argv, prefix, options, usagestr, parseopt_flags); + if (opts->patch_context < -1) + die(_("'%s' cannot be negative"), "--unified"); + if (opts->patch_interhunk_context < -1) + die(_("'%s' cannot be negative"), "--inter-hunk-context"); + + if (!opts->patch_mode) { + if (opts->patch_context != -1) + die(_("the option '%s' requires '%s'"), "--unified", "--patch"); + if (opts->patch_interhunk_context != -1) + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch"); + } + if (opts->show_progress < 0) { if (opts->quiet) opts->show_progress = 0; diff --git a/builtin/commit.c b/builtin/commit.c index fba0dded64a7..73673bc7db9f 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -19,6 +19,7 @@ #include "environment.h" #include "diff.h" #include "commit.h" +#include "add-interactive.h" #include "gettext.h" #include "revision.h" #include "wt-status.h" @@ -122,6 +123,7 @@ static const char *edit_message, *use_message; static char *fixup_message, *fixup_commit, *squash_message; static const char *fixup_prefix; static int all, also, interactive, patch_interactive, only, amend, signoff; +static struct add_p_opt add_p_opt = ADD_P_OPT_INIT; 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 */ @@ -354,6 +356,11 @@ static const char *prepare_index(const char **argv, const char *prefix, const char *ret; char *path = NULL; + if (add_p_opt.context < -1) + die(_("'%s' cannot be negative"), "--unified"); + if (add_p_opt.interhunkcontext < -1) + die(_("'%s' cannot be negative"), "--inter-hunk-context"); + if (is_status) refresh_flags |= REFRESH_UNMERGED; parse_pathspec(&pathspec, 0, @@ -400,7 +407,7 @@ static const char *prepare_index(const char **argv, const char *prefix, old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT)); setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1); - if (interactive_add(the_repository, argv, prefix, patch_interactive) != 0) + if (interactive_add(the_repository, argv, prefix, patch_interactive, &add_p_opt) != 0) die(_("interactive add failed")); the_repository->index_file = old_repo_index_file; @@ -424,6 +431,11 @@ static const char *prepare_index(const char **argv, const char *prefix, commit_style = COMMIT_NORMAL; ret = get_lock_file_path(&index_lock); goto out; + } else { + if (add_p_opt.context != -1) + die(_("the option '%s' requires '%s'"), "--unified", "--interactive/--patch"); + if (add_p_opt.interhunkcontext != -1) + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--interactive/--patch"); } /* @@ -1722,6 +1734,8 @@ int cmd_commit(int argc, OPT_BOOL('i', "include", &also, N_("add specified files to index for commit")), OPT_BOOL(0, "interactive", &interactive, N_("interactively add files")), OPT_BOOL('p', "patch", &patch_interactive, N_("interactively add changes")), + OPT_DIFF_UNIFIED(&add_p_opt.context), + OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext), OPT_BOOL('o', "only", &only, N_("commit only specified files")), OPT_BOOL('n', "no-verify", &no_verify, N_("bypass pre-commit and commit-msg hooks")), OPT_BOOL(0, "dry-run", &dry_run, N_("show what would be committed")), diff --git a/builtin/reset.c b/builtin/reset.c index dc50ffc1ac59..9fb32795c9c5 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -346,6 +346,7 @@ int cmd_reset(int argc, struct object_id oid; struct pathspec pathspec; int intent_to_add = 0; + struct add_p_opt add_p_opt = ADD_P_OPT_INIT; const struct option options[] = { OPT__QUIET(&quiet, N_("be quiet, only report errors")), OPT_BOOL(0, "no-refresh", &no_refresh, @@ -370,6 +371,8 @@ int cmd_reset(int argc, PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater), OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")), + OPT_DIFF_UNIFIED(&add_p_opt.context), + OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext), 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), @@ -420,6 +423,11 @@ int cmd_reset(int argc, oidcpy(&oid, &tree->object.oid); } + if (add_p_opt.context < -1) + die(_("'%s' cannot be negative"), "--unified"); + if (add_p_opt.interhunkcontext < -1) + die(_("'%s' cannot be negative"), "--inter-hunk-context"); + prepare_repo_settings(the_repository); the_repository->settings.command_requires_full_index = 0; @@ -427,9 +435,14 @@ int cmd_reset(int argc, if (reset_type != NONE) die(_("options '%s' and '%s' cannot be used together"), "--patch", "--{hard,mixed,soft}"); trace2_cmd_mode("patch-interactive"); - update_ref_status = !!run_add_p(the_repository, ADD_P_RESET, rev, - &pathspec); + update_ref_status = !!run_add_p(the_repository, ADD_P_RESET, + &add_p_opt, rev, &pathspec); goto cleanup; + } else { + if (add_p_opt.context != -1) + die(_("the option '%s' requires '%s'"), "--unified", "--patch"); + if (add_p_opt.interhunkcontext != -1) + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch"); } /* git reset tree [--] paths... can be used to diff --git a/builtin/stash.c b/builtin/stash.c index 7cd3ad8aa48e..6da162e5e69a 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -1242,7 +1242,8 @@ done: } static int stash_patch(struct stash_info *info, const struct pathspec *ps, - struct strbuf *out_patch, int quiet) + struct strbuf *out_patch, int quiet, + struct add_p_opt *add_p_opt) { int ret = 0; struct child_process cp_read_tree = CHILD_PROCESS_INIT; @@ -1267,7 +1268,7 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps, old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT)); setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1); - ret = !!run_add_p(the_repository, ADD_P_STASH, NULL, ps); + ret = !!run_add_p(the_repository, ADD_P_STASH, add_p_opt, NULL, ps); the_repository->index_file = old_repo_index_file; if (old_index_env && *old_index_env) @@ -1362,8 +1363,8 @@ done: } static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_buf, - int include_untracked, int patch_mode, int only_staged, - struct stash_info *info, struct strbuf *patch, + int include_untracked, int patch_mode, struct add_p_opt *add_p_opt, + int only_staged, struct stash_info *info, struct strbuf *patch, int quiet) { int ret = 0; @@ -1444,7 +1445,7 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b untracked_commit_option = 1; } if (patch_mode) { - ret = stash_patch(info, ps, patch, quiet); + ret = stash_patch(info, ps, patch, quiet, add_p_opt); if (ret < 0) { if (!quiet) fprintf_ln(stderr, _("Cannot save the current " @@ -1519,7 +1520,7 @@ static int create_stash(int argc, const char **argv, const char *prefix UNUSED, if (!check_changes_tracked_files(&ps)) return 0; - ret = do_create_stash(&ps, &stash_msg_buf, 0, 0, 0, &info, + ret = do_create_stash(&ps, &stash_msg_buf, 0, 0, NULL, 0, &info, NULL, 0); if (!ret) printf_ln("%s", oid_to_hex(&info.w_commit)); @@ -1530,7 +1531,8 @@ static int create_stash(int argc, const char **argv, const char *prefix UNUSED, } static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int quiet, - int keep_index, int patch_mode, int include_untracked, int only_staged) + int keep_index, int patch_mode, struct add_p_opt *add_p_opt, + int include_untracked, int only_staged) { int ret = 0; struct stash_info info = STASH_INFO_INIT; @@ -1600,8 +1602,8 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q if (stash_msg) strbuf_addstr(&stash_msg_buf, stash_msg); - if (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode, only_staged, - &info, &patch, quiet)) { + if (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode, + add_p_opt, only_staged, &info, &patch, quiet)) { ret = -1; goto done; } @@ -1774,6 +1776,7 @@ static int push_stash(int argc, const char **argv, const char *prefix, const char *stash_msg = NULL; char *pathspec_from_file = NULL; struct pathspec ps; + struct add_p_opt add_p_opt = ADD_P_OPT_INIT; struct option options[] = { OPT_BOOL('k', "keep-index", &keep_index, N_("keep index")), @@ -1781,6 +1784,8 @@ static int push_stash(int argc, const char **argv, const char *prefix, N_("stash staged changes only")), OPT_BOOL('p', "patch", &patch_mode, N_("stash in patch mode")), + OPT_DIFF_UNIFIED(&add_p_opt.context), + OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext), OPT__QUIET(&quiet, N_("quiet mode")), OPT_BOOL('u', "include-untracked", &include_untracked, N_("include untracked files in stash")), @@ -1836,8 +1841,20 @@ static int push_stash(int argc, const char **argv, const char *prefix, die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file"); } + if (!patch_mode) { + if (add_p_opt.context != -1) + die(_("the option '%s' requires '%s'"), "--unified", "--patch"); + if (add_p_opt.interhunkcontext != -1) + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch"); + } + + if (add_p_opt.context < -1) + die(_("'%s' cannot be negative"), "--unified"); + if (add_p_opt.interhunkcontext < -1) + die(_("'%s' cannot be negative"), "--inter-hunk-context"); + ret = do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode, - include_untracked, only_staged); + &add_p_opt, include_untracked, only_staged); clear_pathspec(&ps); free(pathspec_from_file); @@ -1862,6 +1879,7 @@ static int save_stash(int argc, const char **argv, const char *prefix, const char *stash_msg = NULL; struct pathspec ps; struct strbuf stash_msg_buf = STRBUF_INIT; + struct add_p_opt add_p_opt = ADD_P_OPT_INIT; struct option options[] = { OPT_BOOL('k', "keep-index", &keep_index, N_("keep index")), @@ -1869,6 +1887,8 @@ static int save_stash(int argc, const char **argv, const char *prefix, N_("stash staged changes only")), OPT_BOOL('p', "patch", &patch_mode, N_("stash in patch mode")), + OPT_DIFF_UNIFIED(&add_p_opt.context), + OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext), OPT__QUIET(&quiet, N_("quiet mode")), OPT_BOOL('u', "include-untracked", &include_untracked, N_("include untracked files in stash")), @@ -1887,8 +1907,22 @@ static int save_stash(int argc, const char **argv, const char *prefix, stash_msg = strbuf_join_argv(&stash_msg_buf, argc, argv, ' '); memset(&ps, 0, sizeof(ps)); + + if (add_p_opt.context < -1) + die(_("'%s' cannot be negative"), "--unified"); + if (add_p_opt.interhunkcontext < -1) + die(_("'%s' cannot be negative"), "--inter-hunk-context"); + + if (!patch_mode) { + if (add_p_opt.context != -1) + die(_("the option '%s' requires '%s'"), "--unified", "--patch"); + if (add_p_opt.interhunkcontext != -1) + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch"); + } + ret = do_push_stash(&ps, stash_msg, quiet, keep_index, - patch_mode, include_untracked, only_staged); + patch_mode, &add_p_opt, include_untracked, + only_staged); strbuf_release(&stash_msg_buf); return ret; diff --git a/commit.h b/commit.h index 70c870dae4d4..7a7fedbc2f14 100644 --- a/commit.h +++ b/commit.h @@ -2,6 +2,7 @@ #define COMMIT_H #include "object.h" +#include "add-interactive.h" struct signature_check; struct strbuf; @@ -257,7 +258,7 @@ int for_each_commit_graft(each_commit_graft_fn, void *); int interactive_add(struct repository *repo, const char **argv, const char *prefix, - int patch); + int patch, struct add_p_opt *add_p_opt); struct commit_extra_header { struct commit_extra_header *next; diff --git a/parse-options.h b/parse-options.h index 91c3e3c29b3d..bdae8f116198 100644 --- a/parse-options.h +++ b/parse-options.h @@ -616,6 +616,8 @@ int parse_opt_tracking_mode(const struct option *, const char *, int); #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")) #define OPT_AUTOSTASH(v) OPT_BOOL(0, "autostash", v, N_("automatically stash/stash pop before and after")) +#define OPT_DIFF_UNIFIED(v) OPT_INTEGER_F('U', "unified", v, N_("generate diffs with <n> lines context"), PARSE_OPT_NONEG) +#define OPT_DIFF_INTERHUNK_CONTEXT(v) OPT_INTEGER_F(0, "inter-hunk-context", v, N_("show context between diff hunks up to the specified number of lines"), PARSE_OPT_NONEG) #define OPT_IPVERSION(v) \ OPT_SET_INT_F('4', "ipv4", (v), N_("use IPv4 addresses only"), \ diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index 18dc329ea1f6..d08a87f9ec53 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -1252,4 +1252,53 @@ test_expect_success 'add -p rejects negative diff.context' ' test_grep "diff.context cannot be negative" output ' +for cmd in add checkout restore 'commit -m file' +do + test_expect_success "${cmd%% *} accepts -U and --inter-hunk-context" " + test_write_lines a b c d e f g h i j k l m n o p q r s t u v >file && + git add file && + test_write_lines a b c d e F g h i j k l m n o p Q r s t u v >file && + echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \ + $cmd -p -U 4 --inter-hunk-context 2 >actual && + test_grep \"@@ -2,20 +2,20 @@\" actual + " +done + +test_expect_success 'reset accepts -U and --inter-hunk-context' ' + test_write_lines a b c d e f g h i j k l m n o p q r s t u v >file && + git commit -m file file && + test_write_lines a b c d e F g h i j k l m n o p Q r s t u v >file && + git add file && + echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \ + reset -p -U 4 --inter-hunk-context 2 >actual && + test_grep "@@ -2,20 +2,20 @@" actual +' + +test_expect_success 'stash accepts -U and --inter-hunk-context' ' + test_write_lines a b c d e F g h i j k l m n o p Q r s t u v >file && + git commit -m file file && + test_write_lines a b c d e f g h i j k l m n o p q r s t u v >file && + echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \ + stash -p -U 4 --inter-hunk-context 2 >actual && + test_grep "@@ -2,20 +2,20 @@" actual +' + +for cmd in add checkout commit reset restore 'stash save' 'stash push' +do + test_expect_success "$cmd rejects invalid context options" " + test_must_fail git $cmd -p -U -3 2>actual && + cat actual | echo && + test_grep -e \"'--unified' cannot be negative\" actual && + + test_must_fail git $cmd -p --inter-hunk-context -3 2>actual && + test_grep -e \"'--inter-hunk-context' cannot be negative\" actual && + + test_must_fail git $cmd -U 7 2>actual && + test_grep -E \"'--unified' requires '(--interactive/)?--patch'\" actual && + + test_must_fail git $cmd --inter-hunk-context 2 2>actual && + test_grep -E \"'--inter-hunk-context' requires '(--interactive/)?--patch'\" actual + " +done + test_done diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh index 1384a8195705..0158fe6568cb 100755 --- a/t/t4055-diff-context.sh +++ b/t/t4055-diff-context.sh @@ -58,6 +58,36 @@ test_expect_success 'The -U option overrides diff.context' ' test_grep ! "^ firstline" output ' +test_expect_success 'The -U option overrides diff.context for "add"' ' + test_config diff.context 8 && + git add -U4 -p >output && + test_grep ! "^ firstline" output +' + +test_expect_success 'The -U option overrides diff.context for "commit"' ' + test_config diff.context 8 && + ! git commit -U4 -p >output && + test_grep ! "^ firstline" output +' + +test_expect_success 'The -U option overrides diff.context for "checkout"' ' + test_config diff.context 8 && + git checkout -U4 -p >output && + test_grep ! "^ firstline" output +' + +test_expect_success 'The -U option overrides diff.context for "stash"' ' + test_config diff.context 8 && + ! git stash -U4 -p >output && + test_grep ! "^ firstline" output +' + +test_expect_success 'The -U option overrides diff.context for "restore"' ' + test_config diff.context 8 && + git restore -U4 -p >output && + test_grep ! "^ firstline" output +' + test_expect_success 'diff.context honored by "diff"' ' test_config diff.context 8 && git diff >output && diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh index 343b8cd1912b..6650d33fba69 100755 --- a/t/t9902-completion.sh +++ b/t/t9902-completion.sh @@ -2596,6 +2596,8 @@ test_expect_success 'double dash "git checkout"' ' --merge Z --conflict=Z --patch Z + --unified=Z + --inter-hunk-context=Z --ignore-skip-worktree-bits Z --ignore-other-worktrees Z --recurse-submodules Z -- gitgitgadget ^ permalink raw reply related [flat|nested] 77+ messages in thread
* Re: [PATCH v3 4/4] add-patch: add diff.context command line overrides 2025-06-28 16:34 ` [PATCH v3 4/4] add-patch: add diff.context command line overrides Leon Michalak via GitGitGadget @ 2025-06-30 17:03 ` Junio C Hamano 2025-07-01 9:59 ` Phillip Wood 2025-07-01 9:59 ` Phillip Wood 1 sibling, 1 reply; 77+ messages in thread From: Junio C Hamano @ 2025-06-30 17:03 UTC (permalink / raw) To: Leon Michalak via GitGitGadget Cc: git, Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Phillip Wood, Leon Michalak "Leon Michalak via GitGitGadget" <gitgitgadget@gmail.com> writes: > From: Leon Michalak <leonmichalak6@gmail.com> > > This patch compliments the previous commit, where builtins that use > add-patch infrastructure now respect diff.context and > diff.interHunkContext file configurations. > > In particular, this patch helps users who don't want to set persistent > context configurations or just want a way to override them on a one-time > basis, by allowing the relevant builtins to accept corresponding command > line options that override the file configurations. > > This mimics commands such as diff and log, which allow for both context > file configuration and command line overrides. I skimmed the patch briefly. I am not sure if it is a good idea to * add OPT_DIFF_*() macros to parse-options API, as its utility is very narrow, and forces those who are learning parse-options API to learn one more thing. * validation of the value range to be duplicated for each and every users of the new OPT_DIFF_*() macros. but other than that, looked reasonable to me. Thanks. ^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v3 4/4] add-patch: add diff.context command line overrides 2025-06-30 17:03 ` Junio C Hamano @ 2025-07-01 9:59 ` Phillip Wood 2025-07-01 15:54 ` Junio C Hamano 0 siblings, 1 reply; 77+ messages in thread From: Phillip Wood @ 2025-07-01 9:59 UTC (permalink / raw) To: Junio C Hamano, Leon Michalak via GitGitGadget Cc: git, Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Leon Michalak On 30/06/2025 18:03, Junio C Hamano wrote: > "Leon Michalak via GitGitGadget" <gitgitgadget@gmail.com> writes: > >> From: Leon Michalak <leonmichalak6@gmail.com> >> >> This patch compliments the previous commit, where builtins that use >> add-patch infrastructure now respect diff.context and >> diff.interHunkContext file configurations. >> >> In particular, this patch helps users who don't want to set persistent >> context configurations or just want a way to override them on a one-time >> basis, by allowing the relevant builtins to accept corresponding command >> line options that override the file configurations. >> >> This mimics commands such as diff and log, which allow for both context >> file configuration and command line overrides. > > I skimmed the patch briefly. I am not sure if it is a good idea to > > * add OPT_DIFF_*() macros to parse-options API, as its utility is > very narrow, and forces those who are learning parse-options API > to learn one more thing. It means that we have consistent help for all the commands with these options which I think is valuable. We have a number of other macros that define options that are shared between commands and I think that works quite well. > > * validation of the value range to be duplicated for each and every > users of the new OPT_DIFF_*() macros. Yes the validation is awkward. If we changed the OPT_DIFF_* to use a callback that rejected negative values that would reduce the duplication. > but other than that, looked reasonable to me. I've left a couple of comments on the tests but the code changes look reasonable to me too Thanks Phillip ^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v3 4/4] add-patch: add diff.context command line overrides 2025-07-01 9:59 ` Phillip Wood @ 2025-07-01 15:54 ` Junio C Hamano 2025-07-02 14:07 ` Phillip Wood 0 siblings, 1 reply; 77+ messages in thread From: Junio C Hamano @ 2025-07-01 15:54 UTC (permalink / raw) To: Phillip Wood Cc: Leon Michalak via GitGitGadget, git, Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Leon Michalak Phillip Wood <phillip.wood123@gmail.com> writes: >> * add OPT_DIFF_*() macros to parse-options API, as its utility is >> very narrow, and forces those who are learning parse-options API >> to learn one more thing. > > It means that we have consistent help for all the commands with these > options which I think is valuable. We have a number of other macros > that define options that are shared between commands and I think that > works quite well. I understand that principe. What I was wondering was if there are enough places to use these particular ones to make it worthwhile to enlarge the set of OPT_* macros. >> * validation of the value range to be duplicated for each and >> every >> users of the new OPT_DIFF_*() macros. > > Yes the validation is awkward. If we changed the OPT_DIFF_* to use a > callback that rejected negative values that would reduce the > duplication. Yeah, I was wondering about that approach, too. Another benefit with the "validate just after we parse the value before we assign the result to a variable or a struct member" approach is that we can also complain about -1 that is given from the command line (which the current code ignores, if I am not mistaken, because it needs to be silent if that -1 is there merely because it is the "not set yet" sentinel value). Or perhaps the valid value range Réne has been workingon canbe used here? ^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v3 4/4] add-patch: add diff.context command line overrides 2025-07-01 15:54 ` Junio C Hamano @ 2025-07-02 14:07 ` Phillip Wood 2025-07-02 18:28 ` Junio C Hamano 0 siblings, 1 reply; 77+ messages in thread From: Phillip Wood @ 2025-07-02 14:07 UTC (permalink / raw) To: Junio C Hamano Cc: Leon Michalak via GitGitGadget, git, Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Leon Michalak On 01/07/2025 16:54, Junio C Hamano wrote: > Phillip Wood <phillip.wood123@gmail.com> writes: > >>> * add OPT_DIFF_*() macros to parse-options API, as its utility is >>> very narrow, and forces those who are learning parse-options API >>> to learn one more thing. >> >> It means that we have consistent help for all the commands with these >> options which I think is valuable. We have a number of other macros >> that define options that are shared between commands and I think that >> works quite well. > > I understand that principe. What I was wondering was if there are > enough places to use these particular ones to make it worthwhile to > enlarge the set of OPT_* macros. There are six users of each of these macros so I think it is worthwhile. That's two more users than there are for OPT_RERERE_AUTOUPDATE() and twice as many users as OPT_CONTAINS(). >>> * validation of the value range to be duplicated for each and >>> every >>> users of the new OPT_DIFF_*() macros. >> >> Yes the validation is awkward. If we changed the OPT_DIFF_* to use a >> callback that rejected negative values that would reduce the >> duplication. > > Yeah, I was wondering about that approach, too. Another benefit > with the "validate just after we parse the value before we assign > the result to a variable or a struct member" approach is that we can > also complain about -1 that is given from the command line (which > the current code ignores, if I am not mistaken, because it needs to > be silent if that -1 is there merely because it is the "not set yet" > sentinel value). > > Or perhaps the valid value range Réne has been workingon canbe used > here? That would be nice Thanks Phillip ^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v3 4/4] add-patch: add diff.context command line overrides 2025-07-02 14:07 ` Phillip Wood @ 2025-07-02 18:28 ` Junio C Hamano 0 siblings, 0 replies; 77+ messages in thread From: Junio C Hamano @ 2025-07-02 18:28 UTC (permalink / raw) To: Phillip Wood Cc: Leon Michalak via GitGitGadget, git, Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Leon Michalak Phillip Wood <phillip.wood123@gmail.com> writes: > On 01/07/2025 16:54, Junio C Hamano wrote: >> Phillip Wood <phillip.wood123@gmail.com> writes: >> >>>> * add OPT_DIFF_*() macros to parse-options API, as its utility is >>>> very narrow, and forces those who are learning parse-options API >>>> to learn one more thing. >>> >>> It means that we have consistent help for all the commands with these >>> options which I think is valuable. We have a number of other macros >>> that define options that are shared between commands and I think that >>> works quite well. >> I understand that principe. What I was wondering was if there are >> enough places to use these particular ones to make it worthwhile to >> enlarge the set of OPT_* macros. > > There are six users of each of these macros so I think it is > worthwhile. That's two more users than there are for > OPT_RERERE_AUTOUPDATE() and twice as many users as OPT_CONTAINS(). OK. Thanks. ^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v3 4/4] add-patch: add diff.context command line overrides 2025-06-28 16:34 ` [PATCH v3 4/4] add-patch: add diff.context command line overrides Leon Michalak via GitGitGadget 2025-06-30 17:03 ` Junio C Hamano @ 2025-07-01 9:59 ` Phillip Wood 1 sibling, 0 replies; 77+ messages in thread From: Phillip Wood @ 2025-07-01 9:59 UTC (permalink / raw) To: Leon Michalak via GitGitGadget, git Cc: Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Leon Michalak Hi Leon On 28/06/2025 17:34, Leon Michalak via GitGitGadget wrote: > From: Leon Michalak <leonmichalak6@gmail.com> > > +for cmd in add checkout restore 'commit -m file' > +do > + test_expect_success "${cmd%% *} accepts -U and --inter-hunk-context" " Looking at this again, I think the test bodies here and below should be wrapped in single quotes because they are passed to eval and we want to expand $cmd when the body is evaluated, not before. That would also simplify the quoting inside the tests as we don't need to escape double quotes. That's not your fault - you've just copied what I suggested before. > +test_expect_success 'The -U option overrides diff.context for "add"' ' > + test_config diff.context 8 && > + git add -U4 -p >output && > + test_grep ! "^ firstline" output > +' Don't the tests above check this as they set diff.context and diff.interhunkcontext and pass different values to -U and --inter-hunk-context? Thanks Phillip ^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v3 0/4] Better support for customising context lines in --patch commands 2025-06-28 16:34 ` [PATCH v3 0/4] Better support for customising context lines in --patch commands Leon Michalak via GitGitGadget ` (3 preceding siblings ...) 2025-06-28 16:34 ` [PATCH v3 4/4] add-patch: add diff.context command line overrides Leon Michalak via GitGitGadget @ 2025-06-30 16:16 ` Junio C Hamano 2025-07-09 0:09 ` Junio C Hamano 2025-07-19 12:28 ` [PATCH v4 " Leon Michalak via GitGitGadget 6 siblings, 0 replies; 77+ messages in thread From: Junio C Hamano @ 2025-06-30 16:16 UTC (permalink / raw) To: Leon Michalak via GitGitGadget Cc: git, Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Phillip Wood, Leon Michalak "Leon Michalak via GitGitGadget" <gitgitgadget@gmail.com> writes: > Leon Michalak (4): > test: use "test_grep" > test: use "test_config" These make it sound as if they touch all test scripts under t/ but apparently that is not what this series is doing (and we do not want to see a huge churn like that anyway). Would something like t: use test_grep in t3701, t4055, and t9902 work better? > add-patch: respect diff.context configuration > add-patch: add diff.context command line overrides ^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v3 0/4] Better support for customising context lines in --patch commands 2025-06-28 16:34 ` [PATCH v3 0/4] Better support for customising context lines in --patch commands Leon Michalak via GitGitGadget ` (4 preceding siblings ...) 2025-06-30 16:16 ` [PATCH v3 0/4] Better support for customising context lines in --patch commands Junio C Hamano @ 2025-07-09 0:09 ` Junio C Hamano 2025-07-09 7:57 ` Leon Michalak 2025-07-19 12:28 ` [PATCH v4 " Leon Michalak via GitGitGadget 6 siblings, 1 reply; 77+ messages in thread From: Junio C Hamano @ 2025-07-09 0:09 UTC (permalink / raw) To: Leon Michalak via GitGitGadget Cc: git, Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Phillip Wood, Leon Michalak "Leon Michalak via GitGitGadget" <gitgitgadget@gmail.com> writes: > This series of patches attempt to give --interactive/--patch compatible > builtins ("add", "commit", "checkout", "reset", "restore" and "stash") > better support and nicer experience for configuring how many context lines > are shown in diffs through a variety of ways. > > Prior to these patches, the user could not choose how many context lines > they saw in --patch commands (apart from one workaround by using > GIT_DIFF_OPTS=-u<number> ..., however this isn't a good user experience or a > persistent solution). Additionally, the behaviour around reading from the > diff.context and diff.interHunkContext configs was also inconsistent with > other diff generating commands such as "log -p". > > The summarised changes below hopefully make this experience better and fix > some inconsistencies: > > * diff.context and diff.interHunkContext configs are now respected by > --patch compatible commands > * --unified and --inter-hunk-context command line options have been added > to --patch compatible commands (which take prescendence over file > configs) > * "add" and "commit" in --interactive mode now expose a new "context" > subcommand which configures the amount of context lines you wish to see > in subsequent diffs generated from other subcommands such as "patch" or > "diff" > > The original discussion for this can be read at: > > * https://lore.kernel.org/git/CAP9jKjGb-Rcr=RLJEzeFdtrekYM+qmHy+1T1fykU3n9cV4GhGw@mail.gmail.com/ > > Changes since v1: > > * Update commit descriptions > * Update tests to use the more modern and robust test_grep and test_config > utils > * Reword some documentation / user messages > * Ensure each commit is atomic and builds/passes tests on it's own > * Make new command line options DRY > * Add tests for interhunk context interaction > * Error if context config/command line options are negative > * Drop previous last commit to do with new subcommand for --interactive > add/commit. My motivations behind this patch series originally where > quite simple, just for add-patch commands to respect context configs. > This subcommand, after the discussion in v1, will require more thought > and a larger implementation that what I had anticipated. I would prefer > to leave this for another time as it's the least impactful but the most > time intensive and complicated idea. > > Changes since v2: > > * Update tests to only test single command (following Philip's suggestion) > * Add negative option checks > * Minor commit re-wording This iteration seems to have attracted a few review comments that are left ananswered. Will we see responses to them, and/or, a hopefully small and final update sometime soon? Thanks. ^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v3 0/4] Better support for customising context lines in --patch commands 2025-07-09 0:09 ` Junio C Hamano @ 2025-07-09 7:57 ` Leon Michalak 2025-07-09 15:32 ` Junio C Hamano 0 siblings, 1 reply; 77+ messages in thread From: Leon Michalak @ 2025-07-09 7:57 UTC (permalink / raw) To: Junio C Hamano Cc: Leon Michalak via GitGitGadget, git, Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Phillip Wood I will respond to everything soon I hope, life has been in the way and I didn't anticipate so many comments :-) Based on a skim read of the comments, the main thing I'm not sure I will be able to achieve is implementing the valid value range that a Rene has been working on as I don't have that context, but I'm not sure if this is a necessity to add? ^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v3 0/4] Better support for customising context lines in --patch commands 2025-07-09 7:57 ` Leon Michalak @ 2025-07-09 15:32 ` Junio C Hamano 0 siblings, 0 replies; 77+ messages in thread From: Junio C Hamano @ 2025-07-09 15:32 UTC (permalink / raw) To: Leon Michalak Cc: Leon Michalak via GitGitGadget, git, Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Phillip Wood Leon Michalak <leonmichalak6@gmail.com> writes: > I will respond to everything soon I hope, life has been in the way and > I didn't anticipate so many comments :-) Sure, no problem. Historically, summer is a slower season and these messages I sent are primarily for me to keep track of topics on flight. "No, I am on vacation for a few more weeks" would have been perfectly fine response ;-). > Based on a skim read of the comments, the main thing I'm not sure I > will be able to achieve is implementing the valid value range that a > Rene has been working on as I don't have that context, but I'm not > sure if this is a necessity to add? ^ permalink raw reply [flat|nested] 77+ messages in thread
* [PATCH v4 0/4] Better support for customising context lines in --patch commands 2025-06-28 16:34 ` [PATCH v3 0/4] Better support for customising context lines in --patch commands Leon Michalak via GitGitGadget ` (5 preceding siblings ...) 2025-07-09 0:09 ` Junio C Hamano @ 2025-07-19 12:28 ` Leon Michalak via GitGitGadget 2025-07-19 12:28 ` [PATCH v4 1/4] t: use test_grep in t3701 and t4055 Leon Michalak via GitGitGadget ` (5 more replies) 6 siblings, 6 replies; 77+ messages in thread From: Leon Michalak via GitGitGadget @ 2025-07-19 12:28 UTC (permalink / raw) To: git Cc: Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Phillip Wood, Leon Michalak This series of patches attempt to give --interactive/--patch compatible builtins ("add", "commit", "checkout", "reset", "restore" and "stash") better support and nicer experience for configuring how many context lines are shown in diffs through a variety of ways. Prior to these patches, the user could not choose how many context lines they saw in --patch commands (apart from one workaround by using GIT_DIFF_OPTS=-u<number> ..., however this isn't a good user experience or a persistent solution). Additionally, the behaviour around reading from the diff.context and diff.interHunkContext configs was also inconsistent with other diff generating commands such as "log -p". The summarised changes below hopefully make this experience better and fix some inconsistencies: * diff.context and diff.interHunkContext configs are now respected by --patch compatible commands * --unified and --inter-hunk-context command line options have been added to --patch compatible commands (which take prescendence over file configs) * "add" and "commit" in --interactive mode now expose a new "context" subcommand which configures the amount of context lines you wish to see in subsequent diffs generated from other subcommands such as "patch" or "diff" The original discussion for this can be read at: * https://lore.kernel.org/git/CAP9jKjGb-Rcr=RLJEzeFdtrekYM+qmHy+1T1fykU3n9cV4GhGw@mail.gmail.com/ Changes since v1: * Update commit descriptions * Update tests to use the more modern and robust test_grep and test_config utils * Reword some documentation / user messages * Ensure each commit is atomic and builds/passes tests on it's own * Make new command line options DRY * Add tests for interhunk context interaction * Error if context config/command line options are negative * Drop previous last commit to do with new subcommand for --interactive add/commit. My motivations behind this patch series originally where quite simple, just for add-patch commands to respect context configs. This subcommand, after the discussion in v1, will require more thought and a larger implementation that what I had anticipated. I would prefer to leave this for another time as it's the least impactful but the most time intensive and complicated idea. Changes since v2: * Update tests to only test single command (following Philip's suggestion) * Add negative option checks * Minor commit re-wording Changes since v3: * Update commit descriptions * Read struct properties directly instead of assigning to variables first * Simplify config setting / error checking * Remove redundant tests in later commit as they were replaced with better test(s) * Change tests to use single quotes (this messes with the grep so was unable to explicitly test single quotes in the error messages, so decided to use regex . instead, which is what some other tests that have this problem seem to use as well) Leon Michalak (4): t: use test_grep in t3701 and t4055 t: use test_config in t4055 add-patch: respect diff.context configuration add-patch: add diff.context command line overrides Documentation/diff-context-options.adoc | 10 +++ Documentation/git-add.adoc | 2 + Documentation/git-checkout.adoc | 2 + Documentation/git-commit.adoc | 2 + Documentation/git-reset.adoc | 2 + Documentation/git-restore.adoc | 2 + Documentation/git-stash.adoc | 2 + add-interactive.c | 45 ++++++++++-- add-interactive.h | 17 ++++- add-patch.c | 14 ++-- builtin/add.c | 21 +++++- builtin/checkout.c | 31 +++++++- builtin/commit.c | 16 +++- builtin/reset.c | 17 ++++- builtin/stash.c | 56 +++++++++++--- commit.h | 3 +- parse-options.h | 2 + t/t3701-add-interactive.sh | 97 +++++++++++++++++++------ t/t4055-diff-context.sh | 72 ++++++++++++------ t/t9902-completion.sh | 2 + 20 files changed, 332 insertions(+), 83 deletions(-) create mode 100644 Documentation/diff-context-options.adoc base-commit: cf6f63ea6bf35173e02e18bdc6a4ba41288acff9 Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1915%2FNinjaInShade%2Finteractive-patch-context-v4 Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1915/NinjaInShade/interactive-patch-context-v4 Pull-Request: https://github.com/gitgitgadget/git/pull/1915 Range-diff vs v3: 1: 044a93014b6 ! 1: bbb2bc7082b test: use "test_grep" @@ Metadata Author: Leon Michalak <leonmichalak6@gmail.com> ## Commit message ## - test: use "test_grep" + t: use test_grep in t3701 and t4055 - Use the modern "test_grep" test utility instead of regular "grep" which - provides better debug information if tests fail. - - This is a prerequisite to the commits that follow which add to both test - files. + As a preparatory clean-up, use the "test_grep" test utility instead of + regular "grep" which provides better debug information if tests fail. Signed-off-by: Leon Michalak <leonmichalak6@gmail.com> 2: e5c40d37750 ! 2: feace2d3676 test: use "test_config" @@ Metadata Author: Leon Michalak <leonmichalak6@gmail.com> ## Commit message ## - test: use "test_config" + t: use test_config in t4055 Use the modern "test_config" test utility instead of manual"git config" as the former provides clean up on test completion. 3: 1ec8a138486 ! 3: 994029d6602 add-patch: respect diff.context configuration @@ Commit message Various builtins that use add-patch infrastructure do not respect the user's diff.context and diff.interHunkContext file configurations. - This patch fixes this inconsistency. - This is because the plumbing commands used by "git add -p" to generate - the diff do not read those config settings. Fix this by reading the - config before generating the patch and passing it along to the diff - command with the "-U" and "--inter-hunk-context" command-line options. + The user may be used to seeing their diffs with customized context size, + but not in the patches "git add -p" shows them to pick from. + + Teach add-patch infrastructure to read these configuration variables and + pass their values when spawning the underlying plumbing commands as + their command line option. Signed-off-by: Leon Michalak <leonmichalak6@gmail.com> ## add-interactive.c ## -@@ add-interactive.c: static void init_color(struct repository *r, struct add_i_state *s, - void init_add_i_state(struct add_i_state *s, struct repository *r) - { +@@ add-interactive.c: void init_add_i_state(struct add_i_state *s, struct repository *r) const char *value; -+ int context; -+ int interhunkcontext; s->r = r; + s->context = -1; @@ add-interactive.c: void init_add_i_state(struct add_i_state *s, struct repositor repo_config_get_string(r, "diff.algorithm", &s->interactive_diff_algorithm); -+ if (!repo_config_get_int(r, "diff.context", &context)) { -+ if (context < 0) ++ if (!repo_config_get_int(r, "diff.context", &s->context)) ++ if (s->context < 0) + die(_("%s cannot be negative"), "diff.context"); -+ else -+ s->context = context; -+ } -+ if (!repo_config_get_int(r, "diff.interHunkContext", &interhunkcontext)) { -+ if (interhunkcontext < 0) ++ if (!repo_config_get_int(r, "diff.interHunkContext", &s->interhunkcontext)) ++ if (s->interhunkcontext < 0) + die(_("%s cannot be negative"), "diff.interHunkContext"); -+ else -+ s->interhunkcontext = interhunkcontext; -+ } + repo_config_get_bool(r, "interactive.singlekey", &s->use_single_key); if (s->use_single_key) @@ add-interactive.h: struct add_i_state { void init_add_i_state(struct add_i_state *s, struct repository *r); ## add-patch.c ## -@@ add-patch.c: static int parse_diff(struct add_p_state *s, const struct pathspec *ps) +@@ add-patch.c: static int normalize_marker(const char *p) + static int parse_diff(struct add_p_state *s, const struct pathspec *ps) { struct strvec args = STRVEC_INIT; - const char *diff_algorithm = s->s.interactive_diff_algorithm; -+ int diff_context = s->s.context; -+ int diff_interhunkcontext = s->s.interhunkcontext; +- const char *diff_algorithm = s->s.interactive_diff_algorithm; struct strbuf *plain = &s->plain, *colored = NULL; struct child_process cp = CHILD_PROCESS_INIT; char *p, *pend, *colored_p = NULL, *colored_pend = NULL, marker = '\0'; @@ add-patch.c: static int parse_diff(struct add_p_state *s, const struct pathspec int res; strvec_pushv(&args, s->mode->diff_cmd); -+ if (diff_context != -1) -+ strvec_pushf(&args, "--unified=%i", diff_context); -+ if (diff_interhunkcontext != -1) -+ strvec_pushf(&args, "--inter-hunk-context=%i", diff_interhunkcontext); - if (diff_algorithm) - strvec_pushf(&args, "--diff-algorithm=%s", diff_algorithm); +- if (diff_algorithm) +- strvec_pushf(&args, "--diff-algorithm=%s", diff_algorithm); ++ if (s->s.context != -1) ++ strvec_pushf(&args, "--unified=%i", s->s.context); ++ if (s->s.interhunkcontext != -1) ++ strvec_pushf(&args, "--inter-hunk-context=%i", s->s.interhunkcontext); ++ if (s->s.interactive_diff_algorithm) ++ strvec_pushf(&args, "--diff-algorithm=%s", s->s.interactive_diff_algorithm); if (s->revision) { + struct object_id oid; + strvec_push(&args, ## t/t3701-add-interactive.sh ## @@ t/t3701-add-interactive.sh: test_expect_success 'hunk splitting works with diff.suppressBlankEmpty' ' 4: b68c58b667c ! 4: 2774b930406 add-patch: add diff.context command line overrides @@ add-interactive.c: static void init_color(struct repository *r, struct add_i_sta + struct add_p_opt *add_p_opt) { const char *value; - int context; + @@ add-interactive.c: void init_add_i_state(struct add_i_state *s, struct repository *r) repo_config_get_bool(r, "interactive.singlekey", &s->use_single_key); if (s->use_single_key) @@ parse-options.h: int parse_opt_tracking_mode(const struct option *, const char * OPT_SET_INT_F('4', "ipv4", (v), N_("use IPv4 addresses only"), \ ## t/t3701-add-interactive.sh ## -@@ t/t3701-add-interactive.sh: test_expect_success 'add -p rejects negative diff.context' ' - test_grep "diff.context cannot be negative" output +@@ t/t3701-add-interactive.sh: test_expect_success 'hunk splitting works with diff.suppressBlankEmpty' ' + test_cmp expect actual ' +-test_expect_success 'add -p respects diff.context' ' +- test_write_lines a b c d e f g h i j k l m >file && +for cmd in add checkout restore 'commit -m file' +do -+ test_expect_success "${cmd%% *} accepts -U and --inter-hunk-context" " ++ test_expect_success "${cmd%% *} accepts -U and --inter-hunk-context" ' + test_write_lines a b c d e f g h i j k l m n o p q r s t u v >file && + git add file && + test_write_lines a b c d e F g h i j k l m n o p Q r s t u v >file && + echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \ + $cmd -p -U 4 --inter-hunk-context 2 >actual && -+ test_grep \"@@ -2,20 +2,20 @@\" actual -+ " ++ test_grep "@@ -2,20 +2,20 @@" actual ++ ' +done + +test_expect_success 'reset accepts -U and --inter-hunk-context' ' + test_write_lines a b c d e f g h i j k l m n o p q r s t u v >file && + git commit -m file file && + test_write_lines a b c d e F g h i j k l m n o p Q r s t u v >file && -+ git add file && + git add file && +- test_write_lines a b c d e f G h i j k l m >file && +- echo y | git -c diff.context=5 add -p >actual && +- test_grep "@@ -2,11 +2,11 @@" actual + echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \ + reset -p -U 4 --inter-hunk-context 2 >actual && + test_grep "@@ -2,20 +2,20 @@" actual -+' -+ + ' + +-test_expect_success 'add -p respects diff.interHunkContext' ' +- test_write_lines a b c d e f g h i j k l m n o p q r s >file && +- git add file && +- test_write_lines a b c d E f g i i j k l m N o p q r s >file && +- echo y | git -c diff.interhunkcontext=2 add -p >actual && +- test_grep "@@ -2,16 +2,16 @@" actual +test_expect_success 'stash accepts -U and --inter-hunk-context' ' + test_write_lines a b c d e F g h i j k l m n o p Q r s t u v >file && + git commit -m file file && @@ t/t3701-add-interactive.sh: test_expect_success 'add -p rejects negative diff.co + echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \ + stash -p -U 4 --inter-hunk-context 2 >actual && + test_grep "@@ -2,20 +2,20 @@" actual -+' -+ -+for cmd in add checkout commit reset restore 'stash save' 'stash push' + ' + +-test_expect_success 'add -p rejects negative diff.context' ' +- test_config diff.context -1 && +- test_must_fail git add -p 2>output && +- test_grep "diff.context cannot be negative" output +-' ++for cmd in add checkout commit reset restore "stash save" "stash push" +do -+ test_expect_success "$cmd rejects invalid context options" " ++ test_expect_success "$cmd rejects invalid context options" ' + test_must_fail git $cmd -p -U -3 2>actual && + cat actual | echo && -+ test_grep -e \"'--unified' cannot be negative\" actual && ++ test_grep -e ".--unified. cannot be negative" actual && + + test_must_fail git $cmd -p --inter-hunk-context -3 2>actual && -+ test_grep -e \"'--inter-hunk-context' cannot be negative\" actual && ++ test_grep -e ".--inter-hunk-context. cannot be negative" actual && + + test_must_fail git $cmd -U 7 2>actual && -+ test_grep -E \"'--unified' requires '(--interactive/)?--patch'\" actual && ++ test_grep -E ".--unified. requires .(--interactive/)?--patch." actual && + + test_must_fail git $cmd --inter-hunk-context 2 2>actual && -+ test_grep -E \"'--inter-hunk-context' requires '(--interactive/)?--patch'\" actual -+ " ++ test_grep -E ".--inter-hunk-context. requires .(--interactive/)?--patch." actual ++ ' +done -+ + test_done ## t/t4055-diff-context.sh ## -- gitgitgadget ^ permalink raw reply [flat|nested] 77+ messages in thread
* [PATCH v4 1/4] t: use test_grep in t3701 and t4055 2025-07-19 12:28 ` [PATCH v4 " Leon Michalak via GitGitGadget @ 2025-07-19 12:28 ` Leon Michalak via GitGitGadget 2025-07-19 12:28 ` [PATCH v4 2/4] t: use test_config in t4055 Leon Michalak via GitGitGadget ` (4 subsequent siblings) 5 siblings, 0 replies; 77+ messages in thread From: Leon Michalak via GitGitGadget @ 2025-07-19 12:28 UTC (permalink / raw) To: git Cc: Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Phillip Wood, Leon Michalak, Leon Michalak From: Leon Michalak <leonmichalak6@gmail.com> As a preparatory clean-up, use the "test_grep" test utility instead of regular "grep" which provides better debug information if tests fail. Signed-off-by: Leon Michalak <leonmichalak6@gmail.com> --- t/t3701-add-interactive.sh | 48 +++++++++++++++++++------------------- t/t4055-diff-context.sh | 28 +++++++++++----------- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index b8a05d95f3f1..b088ee141ff4 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -63,7 +63,7 @@ test_expect_success 'setup (initial)' ' ' test_expect_success 'status works (initial)' ' git add -i </dev/null >output && - grep "+1/-0 *+2/-0 file" output + test_grep "+1/-0 *+2/-0 file" output ' test_expect_success 'setup expected' ' @@ -86,7 +86,7 @@ test_expect_success 'revert works (initial)' ' git add file && test_write_lines r 1 | git add -i && git ls-files >output && - ! grep . output + test_grep ! . output ' test_expect_success 'add untracked (multiple)' ' @@ -109,7 +109,7 @@ test_expect_success 'setup (commit)' ' ' test_expect_success 'status works (commit)' ' git add -i </dev/null >output && - grep "+1/-0 *+2/-0 file" output + test_grep "+1/-0 *+2/-0 file" output ' test_expect_success 'update can stage deletions' ' @@ -141,7 +141,7 @@ test_expect_success 'revert works (commit)' ' git add file && test_write_lines r 1 | git add -i && git add -i </dev/null >output && - grep "unchanged *+3/-0 file" output + test_grep "unchanged *+3/-0 file" output ' test_expect_success 'reject multi-key input' ' @@ -185,7 +185,7 @@ test_expect_success 'setup fake editor' ' test_expect_success 'bad edit rejected' ' git reset && test_write_lines e n d | git add -p >output && - grep "hunk does not apply" output + test_grep "hunk does not apply" output ' test_expect_success 'setup patch' ' @@ -198,7 +198,7 @@ test_expect_success 'setup patch' ' test_expect_success 'garbage edit rejected' ' git reset && test_write_lines e n d | git add -p >output && - grep "hunk does not apply" output + test_grep "hunk does not apply" output ' test_expect_success 'setup patch' ' @@ -313,8 +313,8 @@ test_expect_success FILEMODE 'stage mode and hunk' ' chmod +x file && printf "y\\ny\\n" | git add -p && git diff --cached file >out && - grep "new mode" out && - grep "+content" out && + test_grep "new mode" out && + test_grep "+content" out && git diff file >out && test_must_be_empty out ' @@ -636,7 +636,7 @@ test_expect_success 'split hunk "add -p (edit)"' ' printf "%s\n" s e q n q q | EDITOR=: git add -p && git diff >actual && - ! grep "^+15" actual + test_grep ! "^+15" actual ' test_expect_success 'split hunk "add -p (no, yes, edit)"' ' @@ -648,7 +648,7 @@ test_expect_success 'split hunk "add -p (no, yes, edit)"' ' EDITOR=: git add -p 2>error && test_must_be_empty error && git diff >actual && - ! grep "^+31" actual + test_grep ! "^+31" actual ' test_expect_success 'split hunk with incomplete line at end' ' @@ -682,7 +682,7 @@ test_expect_success 'edit, adding lines to the first hunk' ' EDITOR=./fake_editor.sh git add -p 2>error && test_must_be_empty error && git diff --cached >actual && - grep "^+22" actual + test_grep "^+22" actual ' test_expect_success 'patch mode ignores unmerged entries' ' @@ -696,7 +696,7 @@ test_expect_success 'patch mode ignores unmerged entries' ' test_must_fail git merge side && echo changed >non-conflict.t && echo y | git add -p >output && - ! grep a/conflict.t output && + test_grep ! a/conflict.t output && cat >expected <<-\EOF && * Unmerged path conflict.t diff --git a/non-conflict.t b/non-conflict.t @@ -728,7 +728,7 @@ test_expect_success 'diffs can be colorized' ' # We do not want to depend on the exact coloring scheme # git uses for diffs, so just check that we saw some kind of color. - grep "$(printf "\\033")" output + test_grep "$(printf "\\033")" output ' test_expect_success 'colors can be overridden' ' @@ -743,7 +743,7 @@ test_expect_success 'colors can be overridden' ' -c color.interactive.error=blue \ add -i 2>err.raw <input && test_decode_color <err.raw >err && - grep "<BLUE>Huh (trigger)?<RESET>" err && + test_grep "<BLUE>Huh (trigger)?<RESET>" err && test_write_lines help quit >input && force_color git \ @@ -863,7 +863,7 @@ test_expect_success 'colorized diffs respect diff.wsErrorHighlight' ' printf y >y && force_color git -c diff.wsErrorHighlight=all add -p >output.raw 2>&1 <y && test_decode_color <output.raw >output && - grep "old<" output + test_grep "old<" output ' test_expect_success 'diffFilter filters diff' ' @@ -876,7 +876,7 @@ test_expect_success 'diffFilter filters diff' ' # avoid depending on the exact coloring or content of the prompts, # and just make sure we saw our diff prefixed - grep foo:.*content output + test_grep foo:.*content output ' test_expect_success 'detect bogus diffFilter output' ' @@ -886,7 +886,7 @@ test_expect_success 'detect bogus diffFilter output' ' test_config interactive.diffFilter "sed 6d" && printf y >y && force_color test_must_fail git add -p <y >output 2>&1 && - grep "mismatched output" output + test_grep "mismatched output" output ' test_expect_success 'handle iffy colored hunk headers' ' @@ -896,7 +896,7 @@ test_expect_success 'handle iffy colored hunk headers' ' printf n >n && force_color git -c interactive.diffFilter="sed s/.*@@.*/XX/" \ add -p >output 2>&1 <n && - grep "^XX$" output + test_grep "^XX$" output ' test_expect_success 'handle very large filtered diff' ' @@ -1002,7 +1002,7 @@ test_expect_success 'add -p does not expand argument lists' ' # update it, but we want to be sure that our "." pathspec # was not expanded into the argument list of any command. # So look only for "not-changed". - ! grep -E "^trace: (built-in|exec|run_command): .*not-changed" trace.out + test_grep ! -E "^trace: (built-in|exec|run_command): .*not-changed" trace.out ' test_expect_success 'hunk-editing handles custom comment char' ' @@ -1072,21 +1072,21 @@ test_expect_success 'setup different kinds of dirty submodules' ' test_expect_success 'status ignores dirty submodules (except HEAD)' ' git -C for-submodules add -i </dev/null >output && - grep dirty-head output && - grep dirty-both-ways output && - ! grep dirty-otherwise output + test_grep dirty-head output && + test_grep dirty-both-ways output && + test_grep ! dirty-otherwise output ' test_expect_success 'handle submodules' ' echo 123 >>for-submodules/dirty-otherwise/initial.t && force_color git -C for-submodules add -p dirty-otherwise >output 2>&1 && - grep "No changes" output && + test_grep "No changes" output && force_color git -C for-submodules add -p dirty-head >output 2>&1 <y && git -C for-submodules ls-files --stage dirty-head >actual && rev="$(git -C for-submodules/dirty-head rev-parse HEAD)" && - grep "$rev" actual + test_grep "$rev" actual ' test_expect_success 'set up pathological context' ' diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh index ec2804eea67c..c66f966a3ab3 100755 --- a/t/t4055-diff-context.sh +++ b/t/t4055-diff-context.sh @@ -38,36 +38,36 @@ test_expect_success 'setup' ' test_expect_success 'the default number of context lines is 3' ' git diff >output && - ! grep "^ d" output && - grep "^ e" output && - grep "^ j" output && - ! grep "^ k" output + test_grep ! "^ d" output && + test_grep "^ e" output && + test_grep "^ j" output && + test_grep ! "^ k" output ' test_expect_success 'diff.context honored by "log"' ' git log -1 -p >output && - ! grep firstline output && + test_grep ! firstline output && git config diff.context 8 && git log -1 -p >output && - grep "^ firstline" output + test_grep "^ firstline" output ' test_expect_success 'The -U option overrides diff.context' ' git config diff.context 8 && git log -U4 -1 >output && - ! grep "^ firstline" output + test_grep ! "^ firstline" output ' test_expect_success 'diff.context honored by "diff"' ' git config diff.context 8 && git diff >output && - grep "^ firstline" output + test_grep "^ firstline" output ' test_expect_success 'plumbing not affected' ' git config diff.context 8 && git diff-files -p >output && - ! grep "^ firstline" output + test_grep ! "^ firstline" output ' test_expect_success 'non-integer config parsing' ' @@ -85,8 +85,8 @@ test_expect_success 'negative integer config parsing' ' test_expect_success '-U0 is valid, so is diff.context=0' ' git config diff.context 0 && git diff >output && - grep "^-ADDED" output && - grep "^+MODIFIED" output + test_grep "^-ADDED" output && + test_grep "^+MODIFIED" output ' test_expect_success '-U2147483647 works' ' @@ -94,9 +94,9 @@ test_expect_success '-U2147483647 works' ' test_line_count = 16 x && git diff -U2147483647 >output && test_line_count = 22 output && - grep "^-ADDED" output && - grep "^+MODIFIED" output && - grep "^+APPENDED" output + test_grep "^-ADDED" output && + test_grep "^+MODIFIED" output && + test_grep "^+APPENDED" output ' test_done -- gitgitgadget ^ permalink raw reply related [flat|nested] 77+ messages in thread
* [PATCH v4 2/4] t: use test_config in t4055 2025-07-19 12:28 ` [PATCH v4 " Leon Michalak via GitGitGadget 2025-07-19 12:28 ` [PATCH v4 1/4] t: use test_grep in t3701 and t4055 Leon Michalak via GitGitGadget @ 2025-07-19 12:28 ` Leon Michalak via GitGitGadget 2025-07-19 12:28 ` [PATCH v4 3/4] add-patch: respect diff.context configuration Leon Michalak via GitGitGadget ` (3 subsequent siblings) 5 siblings, 0 replies; 77+ messages in thread From: Leon Michalak via GitGitGadget @ 2025-07-19 12:28 UTC (permalink / raw) To: git Cc: Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Phillip Wood, Leon Michalak, Leon Michalak From: Leon Michalak <leonmichalak6@gmail.com> Use the modern "test_config" test utility instead of manual"git config" as the former provides clean up on test completion. This is a prerequisite to the commits that follow which add to this test file. Signed-off-by: Leon Michalak <leonmichalak6@gmail.com> --- t/t4055-diff-context.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh index c66f966a3ab3..1384a8195705 100755 --- a/t/t4055-diff-context.sh +++ b/t/t4055-diff-context.sh @@ -47,43 +47,43 @@ test_expect_success 'the default number of context lines is 3' ' test_expect_success 'diff.context honored by "log"' ' git log -1 -p >output && test_grep ! firstline output && - git config diff.context 8 && + test_config diff.context 8 && git log -1 -p >output && test_grep "^ firstline" output ' test_expect_success 'The -U option overrides diff.context' ' - git config diff.context 8 && + test_config diff.context 8 && git log -U4 -1 >output && test_grep ! "^ firstline" output ' test_expect_success 'diff.context honored by "diff"' ' - git config diff.context 8 && + test_config diff.context 8 && git diff >output && test_grep "^ firstline" output ' test_expect_success 'plumbing not affected' ' - git config diff.context 8 && + test_config diff.context 8 && git diff-files -p >output && test_grep ! "^ firstline" output ' test_expect_success 'non-integer config parsing' ' - git config diff.context no && + test_config diff.context no && test_must_fail git diff 2>output && test_grep "bad numeric config value" output ' test_expect_success 'negative integer config parsing' ' - git config diff.context -1 && + test_config diff.context -1 && test_must_fail git diff 2>output && test_grep "bad config variable" output ' test_expect_success '-U0 is valid, so is diff.context=0' ' - git config diff.context 0 && + test_config diff.context 0 && git diff >output && test_grep "^-ADDED" output && test_grep "^+MODIFIED" output -- gitgitgadget ^ permalink raw reply related [flat|nested] 77+ messages in thread
* [PATCH v4 3/4] add-patch: respect diff.context configuration 2025-07-19 12:28 ` [PATCH v4 " Leon Michalak via GitGitGadget 2025-07-19 12:28 ` [PATCH v4 1/4] t: use test_grep in t3701 and t4055 Leon Michalak via GitGitGadget 2025-07-19 12:28 ` [PATCH v4 2/4] t: use test_config in t4055 Leon Michalak via GitGitGadget @ 2025-07-19 12:28 ` Leon Michalak via GitGitGadget 2025-07-19 12:28 ` [PATCH v4 4/4] add-patch: add diff.context command line overrides Leon Michalak via GitGitGadget ` (2 subsequent siblings) 5 siblings, 0 replies; 77+ messages in thread From: Leon Michalak via GitGitGadget @ 2025-07-19 12:28 UTC (permalink / raw) To: git Cc: Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Phillip Wood, Leon Michalak, Leon Michalak From: Leon Michalak <leonmichalak6@gmail.com> Various builtins that use add-patch infrastructure do not respect the user's diff.context and diff.interHunkContext file configurations. The user may be used to seeing their diffs with customized context size, but not in the patches "git add -p" shows them to pick from. Teach add-patch infrastructure to read these configuration variables and pass their values when spawning the underlying plumbing commands as their command line option. Signed-off-by: Leon Michalak <leonmichalak6@gmail.com> --- add-interactive.c | 9 +++++++++ add-interactive.h | 1 + add-patch.c | 9 ++++++--- t/t3701-add-interactive.sh | 22 ++++++++++++++++++++++ 4 files changed, 38 insertions(+), 3 deletions(-) diff --git a/add-interactive.c b/add-interactive.c index 97ff35b6f12a..eb3d0d3ada84 100644 --- a/add-interactive.c +++ b/add-interactive.c @@ -41,6 +41,8 @@ void init_add_i_state(struct add_i_state *s, struct repository *r) const char *value; s->r = r; + s->context = -1; + s->interhunkcontext = -1; if (repo_config_get_value(r, "color.interactive", &value)) s->use_color = -1; @@ -78,6 +80,13 @@ void init_add_i_state(struct add_i_state *s, struct repository *r) repo_config_get_string(r, "diff.algorithm", &s->interactive_diff_algorithm); + if (!repo_config_get_int(r, "diff.context", &s->context)) + if (s->context < 0) + die(_("%s cannot be negative"), "diff.context"); + if (!repo_config_get_int(r, "diff.interHunkContext", &s->interhunkcontext)) + if (s->interhunkcontext < 0) + die(_("%s cannot be negative"), "diff.interHunkContext"); + repo_config_get_bool(r, "interactive.singlekey", &s->use_single_key); if (s->use_single_key) setbuf(stdin, NULL); diff --git a/add-interactive.h b/add-interactive.h index 693f125e8e4b..c63f35b14be8 100644 --- a/add-interactive.h +++ b/add-interactive.h @@ -18,6 +18,7 @@ struct add_i_state { int use_single_key; char *interactive_diff_filter, *interactive_diff_algorithm; + int context, interhunkcontext; }; void init_add_i_state(struct add_i_state *s, struct repository *r); diff --git a/add-patch.c b/add-patch.c index 95c67d8c80c4..b0125b51ba45 100644 --- a/add-patch.c +++ b/add-patch.c @@ -414,7 +414,6 @@ static int normalize_marker(const char *p) static int parse_diff(struct add_p_state *s, const struct pathspec *ps) { struct strvec args = STRVEC_INIT; - const char *diff_algorithm = s->s.interactive_diff_algorithm; struct strbuf *plain = &s->plain, *colored = NULL; struct child_process cp = CHILD_PROCESS_INIT; char *p, *pend, *colored_p = NULL, *colored_pend = NULL, marker = '\0'; @@ -424,8 +423,12 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps) int res; strvec_pushv(&args, s->mode->diff_cmd); - if (diff_algorithm) - strvec_pushf(&args, "--diff-algorithm=%s", diff_algorithm); + if (s->s.context != -1) + strvec_pushf(&args, "--unified=%i", s->s.context); + if (s->s.interhunkcontext != -1) + strvec_pushf(&args, "--inter-hunk-context=%i", s->s.interhunkcontext); + if (s->s.interactive_diff_algorithm) + strvec_pushf(&args, "--diff-algorithm=%s", s->s.interactive_diff_algorithm); if (s->revision) { struct object_id oid; strvec_push(&args, diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index b088ee141ff4..18dc329ea1f6 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -1230,4 +1230,26 @@ test_expect_success 'hunk splitting works with diff.suppressBlankEmpty' ' test_cmp expect actual ' +test_expect_success 'add -p respects diff.context' ' + test_write_lines a b c d e f g h i j k l m >file && + git add file && + test_write_lines a b c d e f G h i j k l m >file && + echo y | git -c diff.context=5 add -p >actual && + test_grep "@@ -2,11 +2,11 @@" actual +' + +test_expect_success 'add -p respects diff.interHunkContext' ' + test_write_lines a b c d e f g h i j k l m n o p q r s >file && + git add file && + test_write_lines a b c d E f g i i j k l m N o p q r s >file && + echo y | git -c diff.interhunkcontext=2 add -p >actual && + test_grep "@@ -2,16 +2,16 @@" actual +' + +test_expect_success 'add -p rejects negative diff.context' ' + test_config diff.context -1 && + test_must_fail git add -p 2>output && + test_grep "diff.context cannot be negative" output +' + test_done -- gitgitgadget ^ permalink raw reply related [flat|nested] 77+ messages in thread
* [PATCH v4 4/4] add-patch: add diff.context command line overrides 2025-07-19 12:28 ` [PATCH v4 " Leon Michalak via GitGitGadget ` (2 preceding siblings ...) 2025-07-19 12:28 ` [PATCH v4 3/4] add-patch: respect diff.context configuration Leon Michalak via GitGitGadget @ 2025-07-19 12:28 ` Leon Michalak via GitGitGadget 2025-07-22 16:01 ` Phillip Wood 2025-07-21 16:50 ` [PATCH v4 0/4] Better support for customising context lines in --patch commands Junio C Hamano 2025-07-29 7:01 ` [PATCH v5 " Leon Michalak via GitGitGadget 5 siblings, 1 reply; 77+ messages in thread From: Leon Michalak via GitGitGadget @ 2025-07-19 12:28 UTC (permalink / raw) To: git Cc: Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Phillip Wood, Leon Michalak, Leon Michalak From: Leon Michalak <leonmichalak6@gmail.com> This patch compliments the previous commit, where builtins that use add-patch infrastructure now respect diff.context and diff.interHunkContext file configurations. In particular, this patch helps users who don't want to set persistent context configurations or just want a way to override them on a one-time basis, by allowing the relevant builtins to accept corresponding command line options that override the file configurations. This mimics commands such as diff and log, which allow for both context file configuration and command line overrides. Signed-off-by: Leon Michalak <leonmichalak6@gmail.com> --- Documentation/diff-context-options.adoc | 10 +++++ Documentation/git-add.adoc | 2 + Documentation/git-checkout.adoc | 2 + Documentation/git-commit.adoc | 2 + Documentation/git-reset.adoc | 2 + Documentation/git-restore.adoc | 2 + Documentation/git-stash.adoc | 2 + add-interactive.c | 36 +++++++++++---- add-interactive.h | 16 +++++-- add-patch.c | 5 ++- builtin/add.c | 21 +++++++-- builtin/checkout.c | 31 +++++++++++-- builtin/commit.c | 16 ++++++- builtin/reset.c | 17 ++++++- builtin/stash.c | 56 ++++++++++++++++++----- commit.h | 3 +- parse-options.h | 2 + t/t3701-add-interactive.sh | 59 ++++++++++++++++++------- t/t4055-diff-context.sh | 30 +++++++++++++ t/t9902-completion.sh | 2 + 20 files changed, 265 insertions(+), 51 deletions(-) create mode 100644 Documentation/diff-context-options.adoc diff --git a/Documentation/diff-context-options.adoc b/Documentation/diff-context-options.adoc new file mode 100644 index 000000000000..e161260358ff --- /dev/null +++ b/Documentation/diff-context-options.adoc @@ -0,0 +1,10 @@ +`-U<n>`:: +`--unified=<n>`:: + Generate diffs with _<n>_ lines of context. Defaults to `diff.context` + or 3 if the config option is unset. + +`--inter-hunk-context=<n>`:: + Show the context between diff hunks, up to the specified _<number>_ + of lines, thereby fusing hunks that are close to each other. + Defaults to `diff.interHunkContext` or 0 if the config option + is unset. diff --git a/Documentation/git-add.adoc b/Documentation/git-add.adoc index eba0b419ce50..b7a735824d6c 100644 --- a/Documentation/git-add.adoc +++ b/Documentation/git-add.adoc @@ -104,6 +104,8 @@ This effectively runs `add --interactive`, but bypasses the initial command menu and directly jumps to the `patch` subcommand. See ``Interactive mode'' for details. +include::diff-context-options.adoc[] + `-e`:: `--edit`:: Open the diff vs. the index in an editor and let the user diff --git a/Documentation/git-checkout.adoc b/Documentation/git-checkout.adoc index ee83b6d9ba9a..40e02cfd6562 100644 --- a/Documentation/git-checkout.adoc +++ b/Documentation/git-checkout.adoc @@ -289,6 +289,8 @@ section of linkgit:git-add[1] to learn how to operate the `--patch` mode. Note that this option uses the no overlay mode by default (see also `--overlay`), and currently doesn't support overlay mode. +include::diff-context-options.adoc[] + `--ignore-other-worktrees`:: `git checkout` refuses when the wanted branch is already checked out or otherwise in use by another worktree. This option makes diff --git a/Documentation/git-commit.adoc b/Documentation/git-commit.adoc index dc219025f1eb..ae988a883b5b 100644 --- a/Documentation/git-commit.adoc +++ b/Documentation/git-commit.adoc @@ -76,6 +76,8 @@ OPTIONS which changes to commit. See linkgit:git-add[1] for details. +include::diff-context-options.adoc[] + `-C <commit>`:: `--reuse-message=<commit>`:: Take an existing _<commit>_ object, and reuse the log message diff --git a/Documentation/git-reset.adoc b/Documentation/git-reset.adoc index 53ab88c5451c..50e8a0ba6f66 100644 --- a/Documentation/git-reset.adoc +++ b/Documentation/git-reset.adoc @@ -125,6 +125,8 @@ OPTIONS separated with _NUL_ character and all other characters are taken literally (including newlines and quotes). +include::diff-context-options.adoc[] + `--`:: Do not interpret any more arguments as options. diff --git a/Documentation/git-restore.adoc b/Documentation/git-restore.adoc index 877b7772e667..1dcc2bb7aea3 100644 --- a/Documentation/git-restore.adoc +++ b/Documentation/git-restore.adoc @@ -52,6 +52,8 @@ leave out at most one of _<rev-A>__ and _<rev-B>_, in which case it defaults to Mode" section of linkgit:git-add[1] to learn how to operate the `--patch` mode. +include::diff-context-options.adoc[] + `-W`:: `--worktree`:: `-S`:: diff --git a/Documentation/git-stash.adoc b/Documentation/git-stash.adoc index 1a5177f4986c..0578c619c410 100644 --- a/Documentation/git-stash.adoc +++ b/Documentation/git-stash.adoc @@ -208,6 +208,8 @@ to learn how to operate the `--patch` mode. The `--patch` option implies `--keep-index`. You can use `--no-keep-index` to override this. +include::diff-context-options.adoc[] + -S:: --staged:: This option is only valid for `push` and `save` commands. diff --git a/add-interactive.c b/add-interactive.c index eb3d0d3ada84..3e692b47eca0 100644 --- a/add-interactive.c +++ b/add-interactive.c @@ -36,7 +36,8 @@ static void init_color(struct repository *r, struct add_i_state *s, free(key); } -void init_add_i_state(struct add_i_state *s, struct repository *r) +void init_add_i_state(struct add_i_state *s, struct repository *r, + struct add_p_opt *add_p_opt) { const char *value; @@ -90,6 +91,17 @@ void init_add_i_state(struct add_i_state *s, struct repository *r) repo_config_get_bool(r, "interactive.singlekey", &s->use_single_key); if (s->use_single_key) setbuf(stdin, NULL); + + if (add_p_opt->context != -1) { + if (add_p_opt->context < 0) + die(_("%s cannot be negative"), "--unified"); + s->context = add_p_opt->context; + } + if (add_p_opt->interhunkcontext != -1) { + if (add_p_opt->interhunkcontext < 0) + die(_("%s cannot be negative"), "--inter-hunk-context"); + s->interhunkcontext = add_p_opt->interhunkcontext; + } } void clear_add_i_state(struct add_i_state *s) @@ -978,6 +990,10 @@ static int run_patch(struct add_i_state *s, const struct pathspec *ps, opts->prompt = N_("Patch update"); count = list_and_choose(s, files, opts); if (count > 0) { + struct add_p_opt add_p_opt = { + .context = s->context, + .interhunkcontext = s->interhunkcontext, + }; struct strvec args = STRVEC_INIT; struct pathspec ps_selected = { 0 }; @@ -988,7 +1004,7 @@ static int run_patch(struct add_i_state *s, const struct pathspec *ps, parse_pathspec(&ps_selected, PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL, PATHSPEC_LITERAL_PATH, "", args.v); - res = run_add_p(s->r, ADD_P_ADD, NULL, &ps_selected); + res = run_add_p(s->r, ADD_P_ADD, &add_p_opt, NULL, &ps_selected); strvec_clear(&args); clear_pathspec(&ps_selected); } @@ -1023,10 +1039,13 @@ static int run_diff(struct add_i_state *s, const struct pathspec *ps, if (count > 0) { struct child_process cmd = CHILD_PROCESS_INIT; - strvec_pushl(&cmd.args, "git", "diff", "-p", "--cached", - oid_to_hex(!is_initial ? &oid : - s->r->hash_algo->empty_tree), - "--", NULL); + strvec_pushl(&cmd.args, "git", "diff", "-p", "--cached", NULL); + if (s->context != -1) + strvec_pushf(&cmd.args, "--unified=%i", s->context); + if (s->interhunkcontext != -1) + strvec_pushf(&cmd.args, "--inter-hunk-context=%i", s->interhunkcontext); + strvec_pushl(&cmd.args, oid_to_hex(!is_initial ? &oid : + s->r->hash_algo->empty_tree), "--", NULL); for (i = 0; i < files->items.nr; i++) if (files->selected[i]) strvec_push(&cmd.args, @@ -1119,7 +1138,8 @@ static void command_prompt_help(struct add_i_state *s) _("(empty) select nothing")); } -int run_add_i(struct repository *r, const struct pathspec *ps) +int run_add_i(struct repository *r, const struct pathspec *ps, + struct add_p_opt *add_p_opt) { struct add_i_state s = { NULL }; struct print_command_item_data data = { "[", "]" }; @@ -1162,7 +1182,7 @@ int run_add_i(struct repository *r, const struct pathspec *ps) ->util = util; } - init_add_i_state(&s, r); + init_add_i_state(&s, r, add_p_opt); /* * When color was asked for, use the prompt color for diff --git a/add-interactive.h b/add-interactive.h index c63f35b14be8..4213dcd67b9a 100644 --- a/add-interactive.h +++ b/add-interactive.h @@ -3,6 +3,13 @@ #include "color.h" +struct add_p_opt { + int context; + int interhunkcontext; +}; + +#define ADD_P_OPT_INIT { .context = -1, .interhunkcontext = -1 } + struct add_i_state { struct repository *r; int use_color; @@ -21,12 +28,14 @@ struct add_i_state { int context, interhunkcontext; }; -void init_add_i_state(struct add_i_state *s, struct repository *r); +void init_add_i_state(struct add_i_state *s, struct repository *r, + struct add_p_opt *add_p_opt); void clear_add_i_state(struct add_i_state *s); struct repository; struct pathspec; -int run_add_i(struct repository *r, const struct pathspec *ps); +int run_add_i(struct repository *r, const struct pathspec *ps, + struct add_p_opt *add_p_opt); enum add_p_mode { ADD_P_ADD, @@ -37,6 +46,7 @@ enum add_p_mode { }; int run_add_p(struct repository *r, enum add_p_mode mode, - const char *revision, const struct pathspec *ps); + struct add_p_opt *o, const char *revision, + const struct pathspec *ps); #endif diff --git a/add-patch.c b/add-patch.c index b0125b51ba45..302e6ba7d9a3 100644 --- a/add-patch.c +++ b/add-patch.c @@ -1763,14 +1763,15 @@ soft_increment: } int run_add_p(struct repository *r, enum add_p_mode mode, - const char *revision, const struct pathspec *ps) + struct add_p_opt *o, const char *revision, + const struct pathspec *ps) { struct add_p_state s = { { r }, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT }; size_t i, binary_count = 0; - init_add_i_state(&s.s, r); + init_add_i_state(&s.s, r, o); if (mode == ADD_P_STASH) s.mode = &patch_mode_stash; diff --git a/builtin/add.c b/builtin/add.c index 7c292ffdc6c2..a00dab9b8fa0 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -29,6 +29,7 @@ static const char * const builtin_add_usage[] = { NULL }; static int patch_interactive, add_interactive, edit_interactive; +static struct add_p_opt add_p_opt = ADD_P_OPT_INIT; static int take_worktree_changes; static int add_renormalize; static int pathspec_file_nul; @@ -157,7 +158,7 @@ static int refresh(struct repository *repo, int verbose, const struct pathspec * int interactive_add(struct repository *repo, const char **argv, const char *prefix, - int patch) + int patch, struct add_p_opt *add_p_opt) { struct pathspec pathspec; int ret; @@ -169,9 +170,9 @@ int interactive_add(struct repository *repo, prefix, argv); if (patch) - ret = !!run_add_p(repo, ADD_P_ADD, NULL, &pathspec); + ret = !!run_add_p(repo, ADD_P_ADD, add_p_opt, NULL, &pathspec); else - ret = !!run_add_i(repo, &pathspec); + ret = !!run_add_i(repo, &pathspec, add_p_opt); clear_pathspec(&pathspec); return ret; @@ -253,6 +254,8 @@ static struct option builtin_add_options[] = { OPT_GROUP(""), OPT_BOOL('i', "interactive", &add_interactive, N_("interactive picking")), OPT_BOOL('p', "patch", &patch_interactive, N_("select hunks interactively")), + OPT_DIFF_UNIFIED(&add_p_opt.context), + OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext), OPT_BOOL('e', "edit", &edit_interactive, N_("edit current diff and apply")), OPT__FORCE(&ignored_too, N_("allow adding otherwise ignored files"), 0), OPT_BOOL('u', "update", &take_worktree_changes, N_("update tracked files")), @@ -394,6 +397,11 @@ int cmd_add(int argc, prepare_repo_settings(repo); repo->settings.command_requires_full_index = 0; + if (add_p_opt.context < -1) + die(_("'%s' cannot be negative"), "--unified"); + if (add_p_opt.interhunkcontext < -1) + die(_("'%s' cannot be negative"), "--inter-hunk-context"); + if (patch_interactive) add_interactive = 1; if (add_interactive) { @@ -401,7 +409,12 @@ int cmd_add(int argc, die(_("options '%s' and '%s' cannot be used together"), "--dry-run", "--interactive/--patch"); if (pathspec_from_file) die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--interactive/--patch"); - exit(interactive_add(repo, argv + 1, prefix, patch_interactive)); + exit(interactive_add(repo, argv + 1, prefix, patch_interactive, &add_p_opt)); + } else { + if (add_p_opt.context != -1) + die(_("the option '%s' requires '%s'"), "--unified", "--interactive/--patch"); + if (add_p_opt.interhunkcontext != -1) + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--interactive/--patch"); } if (edit_interactive) { diff --git a/builtin/checkout.c b/builtin/checkout.c index 536192d3456c..3737ba4c3920 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -61,6 +61,8 @@ static const char * const restore_usage[] = { struct checkout_opts { int patch_mode; + int patch_context; + int patch_interhunk_context; int quiet; int merge; int force; @@ -104,7 +106,12 @@ struct checkout_opts { struct tree *source_tree; }; -#define CHECKOUT_OPTS_INIT { .conflict_style = -1, .merge = -1 } +#define CHECKOUT_OPTS_INIT { \ + .conflict_style = -1, \ + .merge = -1, \ + .patch_context = -1, \ + .patch_interhunk_context = -1, \ +} struct branch_info { char *name; /* The short name used */ @@ -539,6 +546,10 @@ static int checkout_paths(const struct checkout_opts *opts, if (opts->patch_mode) { enum add_p_mode patch_mode; + struct add_p_opt add_p_opt = { + .context = opts->patch_context, + .interhunkcontext = opts->patch_interhunk_context, + }; const char *rev = new_branch_info->name; char rev_oid[GIT_MAX_HEXSZ + 1]; @@ -564,8 +575,8 @@ static int checkout_paths(const struct checkout_opts *opts, else BUG("either flag must have been set, worktree=%d, index=%d", opts->checkout_worktree, opts->checkout_index); - return !!run_add_p(the_repository, patch_mode, rev, - &opts->pathspec); + return !!run_add_p(the_repository, patch_mode, &add_p_opt, + rev, &opts->pathspec); } repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR); @@ -1738,6 +1749,8 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts, N_("checkout their version for unmerged files"), 3, PARSE_OPT_NONEG), OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")), + OPT_DIFF_UNIFIED(&opts->patch_context), + OPT_DIFF_INTERHUNK_CONTEXT(&opts->patch_interhunk_context), 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), @@ -1780,6 +1793,18 @@ static int checkout_main(int argc, const char **argv, const char *prefix, argc = parse_options(argc, argv, prefix, options, usagestr, parseopt_flags); + if (opts->patch_context < -1) + die(_("'%s' cannot be negative"), "--unified"); + if (opts->patch_interhunk_context < -1) + die(_("'%s' cannot be negative"), "--inter-hunk-context"); + + if (!opts->patch_mode) { + if (opts->patch_context != -1) + die(_("the option '%s' requires '%s'"), "--unified", "--patch"); + if (opts->patch_interhunk_context != -1) + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch"); + } + if (opts->show_progress < 0) { if (opts->quiet) opts->show_progress = 0; diff --git a/builtin/commit.c b/builtin/commit.c index fba0dded64a7..73673bc7db9f 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -19,6 +19,7 @@ #include "environment.h" #include "diff.h" #include "commit.h" +#include "add-interactive.h" #include "gettext.h" #include "revision.h" #include "wt-status.h" @@ -122,6 +123,7 @@ static const char *edit_message, *use_message; static char *fixup_message, *fixup_commit, *squash_message; static const char *fixup_prefix; static int all, also, interactive, patch_interactive, only, amend, signoff; +static struct add_p_opt add_p_opt = ADD_P_OPT_INIT; 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 */ @@ -354,6 +356,11 @@ static const char *prepare_index(const char **argv, const char *prefix, const char *ret; char *path = NULL; + if (add_p_opt.context < -1) + die(_("'%s' cannot be negative"), "--unified"); + if (add_p_opt.interhunkcontext < -1) + die(_("'%s' cannot be negative"), "--inter-hunk-context"); + if (is_status) refresh_flags |= REFRESH_UNMERGED; parse_pathspec(&pathspec, 0, @@ -400,7 +407,7 @@ static const char *prepare_index(const char **argv, const char *prefix, old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT)); setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1); - if (interactive_add(the_repository, argv, prefix, patch_interactive) != 0) + if (interactive_add(the_repository, argv, prefix, patch_interactive, &add_p_opt) != 0) die(_("interactive add failed")); the_repository->index_file = old_repo_index_file; @@ -424,6 +431,11 @@ static const char *prepare_index(const char **argv, const char *prefix, commit_style = COMMIT_NORMAL; ret = get_lock_file_path(&index_lock); goto out; + } else { + if (add_p_opt.context != -1) + die(_("the option '%s' requires '%s'"), "--unified", "--interactive/--patch"); + if (add_p_opt.interhunkcontext != -1) + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--interactive/--patch"); } /* @@ -1722,6 +1734,8 @@ int cmd_commit(int argc, OPT_BOOL('i', "include", &also, N_("add specified files to index for commit")), OPT_BOOL(0, "interactive", &interactive, N_("interactively add files")), OPT_BOOL('p', "patch", &patch_interactive, N_("interactively add changes")), + OPT_DIFF_UNIFIED(&add_p_opt.context), + OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext), OPT_BOOL('o', "only", &only, N_("commit only specified files")), OPT_BOOL('n', "no-verify", &no_verify, N_("bypass pre-commit and commit-msg hooks")), OPT_BOOL(0, "dry-run", &dry_run, N_("show what would be committed")), diff --git a/builtin/reset.c b/builtin/reset.c index dc50ffc1ac59..9fb32795c9c5 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -346,6 +346,7 @@ int cmd_reset(int argc, struct object_id oid; struct pathspec pathspec; int intent_to_add = 0; + struct add_p_opt add_p_opt = ADD_P_OPT_INIT; const struct option options[] = { OPT__QUIET(&quiet, N_("be quiet, only report errors")), OPT_BOOL(0, "no-refresh", &no_refresh, @@ -370,6 +371,8 @@ int cmd_reset(int argc, PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater), OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")), + OPT_DIFF_UNIFIED(&add_p_opt.context), + OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext), 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), @@ -420,6 +423,11 @@ int cmd_reset(int argc, oidcpy(&oid, &tree->object.oid); } + if (add_p_opt.context < -1) + die(_("'%s' cannot be negative"), "--unified"); + if (add_p_opt.interhunkcontext < -1) + die(_("'%s' cannot be negative"), "--inter-hunk-context"); + prepare_repo_settings(the_repository); the_repository->settings.command_requires_full_index = 0; @@ -427,9 +435,14 @@ int cmd_reset(int argc, if (reset_type != NONE) die(_("options '%s' and '%s' cannot be used together"), "--patch", "--{hard,mixed,soft}"); trace2_cmd_mode("patch-interactive"); - update_ref_status = !!run_add_p(the_repository, ADD_P_RESET, rev, - &pathspec); + update_ref_status = !!run_add_p(the_repository, ADD_P_RESET, + &add_p_opt, rev, &pathspec); goto cleanup; + } else { + if (add_p_opt.context != -1) + die(_("the option '%s' requires '%s'"), "--unified", "--patch"); + if (add_p_opt.interhunkcontext != -1) + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch"); } /* git reset tree [--] paths... can be used to diff --git a/builtin/stash.c b/builtin/stash.c index 7cd3ad8aa48e..6da162e5e69a 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -1242,7 +1242,8 @@ done: } static int stash_patch(struct stash_info *info, const struct pathspec *ps, - struct strbuf *out_patch, int quiet) + struct strbuf *out_patch, int quiet, + struct add_p_opt *add_p_opt) { int ret = 0; struct child_process cp_read_tree = CHILD_PROCESS_INIT; @@ -1267,7 +1268,7 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps, old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT)); setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1); - ret = !!run_add_p(the_repository, ADD_P_STASH, NULL, ps); + ret = !!run_add_p(the_repository, ADD_P_STASH, add_p_opt, NULL, ps); the_repository->index_file = old_repo_index_file; if (old_index_env && *old_index_env) @@ -1362,8 +1363,8 @@ done: } static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_buf, - int include_untracked, int patch_mode, int only_staged, - struct stash_info *info, struct strbuf *patch, + int include_untracked, int patch_mode, struct add_p_opt *add_p_opt, + int only_staged, struct stash_info *info, struct strbuf *patch, int quiet) { int ret = 0; @@ -1444,7 +1445,7 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b untracked_commit_option = 1; } if (patch_mode) { - ret = stash_patch(info, ps, patch, quiet); + ret = stash_patch(info, ps, patch, quiet, add_p_opt); if (ret < 0) { if (!quiet) fprintf_ln(stderr, _("Cannot save the current " @@ -1519,7 +1520,7 @@ static int create_stash(int argc, const char **argv, const char *prefix UNUSED, if (!check_changes_tracked_files(&ps)) return 0; - ret = do_create_stash(&ps, &stash_msg_buf, 0, 0, 0, &info, + ret = do_create_stash(&ps, &stash_msg_buf, 0, 0, NULL, 0, &info, NULL, 0); if (!ret) printf_ln("%s", oid_to_hex(&info.w_commit)); @@ -1530,7 +1531,8 @@ static int create_stash(int argc, const char **argv, const char *prefix UNUSED, } static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int quiet, - int keep_index, int patch_mode, int include_untracked, int only_staged) + int keep_index, int patch_mode, struct add_p_opt *add_p_opt, + int include_untracked, int only_staged) { int ret = 0; struct stash_info info = STASH_INFO_INIT; @@ -1600,8 +1602,8 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q if (stash_msg) strbuf_addstr(&stash_msg_buf, stash_msg); - if (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode, only_staged, - &info, &patch, quiet)) { + if (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode, + add_p_opt, only_staged, &info, &patch, quiet)) { ret = -1; goto done; } @@ -1774,6 +1776,7 @@ static int push_stash(int argc, const char **argv, const char *prefix, const char *stash_msg = NULL; char *pathspec_from_file = NULL; struct pathspec ps; + struct add_p_opt add_p_opt = ADD_P_OPT_INIT; struct option options[] = { OPT_BOOL('k', "keep-index", &keep_index, N_("keep index")), @@ -1781,6 +1784,8 @@ static int push_stash(int argc, const char **argv, const char *prefix, N_("stash staged changes only")), OPT_BOOL('p', "patch", &patch_mode, N_("stash in patch mode")), + OPT_DIFF_UNIFIED(&add_p_opt.context), + OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext), OPT__QUIET(&quiet, N_("quiet mode")), OPT_BOOL('u', "include-untracked", &include_untracked, N_("include untracked files in stash")), @@ -1836,8 +1841,20 @@ static int push_stash(int argc, const char **argv, const char *prefix, die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file"); } + if (!patch_mode) { + if (add_p_opt.context != -1) + die(_("the option '%s' requires '%s'"), "--unified", "--patch"); + if (add_p_opt.interhunkcontext != -1) + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch"); + } + + if (add_p_opt.context < -1) + die(_("'%s' cannot be negative"), "--unified"); + if (add_p_opt.interhunkcontext < -1) + die(_("'%s' cannot be negative"), "--inter-hunk-context"); + ret = do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode, - include_untracked, only_staged); + &add_p_opt, include_untracked, only_staged); clear_pathspec(&ps); free(pathspec_from_file); @@ -1862,6 +1879,7 @@ static int save_stash(int argc, const char **argv, const char *prefix, const char *stash_msg = NULL; struct pathspec ps; struct strbuf stash_msg_buf = STRBUF_INIT; + struct add_p_opt add_p_opt = ADD_P_OPT_INIT; struct option options[] = { OPT_BOOL('k', "keep-index", &keep_index, N_("keep index")), @@ -1869,6 +1887,8 @@ static int save_stash(int argc, const char **argv, const char *prefix, N_("stash staged changes only")), OPT_BOOL('p', "patch", &patch_mode, N_("stash in patch mode")), + OPT_DIFF_UNIFIED(&add_p_opt.context), + OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext), OPT__QUIET(&quiet, N_("quiet mode")), OPT_BOOL('u', "include-untracked", &include_untracked, N_("include untracked files in stash")), @@ -1887,8 +1907,22 @@ static int save_stash(int argc, const char **argv, const char *prefix, stash_msg = strbuf_join_argv(&stash_msg_buf, argc, argv, ' '); memset(&ps, 0, sizeof(ps)); + + if (add_p_opt.context < -1) + die(_("'%s' cannot be negative"), "--unified"); + if (add_p_opt.interhunkcontext < -1) + die(_("'%s' cannot be negative"), "--inter-hunk-context"); + + if (!patch_mode) { + if (add_p_opt.context != -1) + die(_("the option '%s' requires '%s'"), "--unified", "--patch"); + if (add_p_opt.interhunkcontext != -1) + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch"); + } + ret = do_push_stash(&ps, stash_msg, quiet, keep_index, - patch_mode, include_untracked, only_staged); + patch_mode, &add_p_opt, include_untracked, + only_staged); strbuf_release(&stash_msg_buf); return ret; diff --git a/commit.h b/commit.h index 70c870dae4d4..7a7fedbc2f14 100644 --- a/commit.h +++ b/commit.h @@ -2,6 +2,7 @@ #define COMMIT_H #include "object.h" +#include "add-interactive.h" struct signature_check; struct strbuf; @@ -257,7 +258,7 @@ int for_each_commit_graft(each_commit_graft_fn, void *); int interactive_add(struct repository *repo, const char **argv, const char *prefix, - int patch); + int patch, struct add_p_opt *add_p_opt); struct commit_extra_header { struct commit_extra_header *next; diff --git a/parse-options.h b/parse-options.h index 91c3e3c29b3d..bdae8f116198 100644 --- a/parse-options.h +++ b/parse-options.h @@ -616,6 +616,8 @@ int parse_opt_tracking_mode(const struct option *, const char *, int); #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")) #define OPT_AUTOSTASH(v) OPT_BOOL(0, "autostash", v, N_("automatically stash/stash pop before and after")) +#define OPT_DIFF_UNIFIED(v) OPT_INTEGER_F('U', "unified", v, N_("generate diffs with <n> lines context"), PARSE_OPT_NONEG) +#define OPT_DIFF_INTERHUNK_CONTEXT(v) OPT_INTEGER_F(0, "inter-hunk-context", v, N_("show context between diff hunks up to the specified number of lines"), PARSE_OPT_NONEG) #define OPT_IPVERSION(v) \ OPT_SET_INT_F('4', "ipv4", (v), N_("use IPv4 addresses only"), \ diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index 18dc329ea1f6..7fd9a23c9987 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -1230,26 +1230,53 @@ test_expect_success 'hunk splitting works with diff.suppressBlankEmpty' ' test_cmp expect actual ' -test_expect_success 'add -p respects diff.context' ' - test_write_lines a b c d e f g h i j k l m >file && +for cmd in add checkout restore 'commit -m file' +do + test_expect_success "${cmd%% *} accepts -U and --inter-hunk-context" ' + test_write_lines a b c d e f g h i j k l m n o p q r s t u v >file && + git add file && + test_write_lines a b c d e F g h i j k l m n o p Q r s t u v >file && + echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \ + $cmd -p -U 4 --inter-hunk-context 2 >actual && + test_grep "@@ -2,20 +2,20 @@" actual + ' +done + +test_expect_success 'reset accepts -U and --inter-hunk-context' ' + test_write_lines a b c d e f g h i j k l m n o p q r s t u v >file && + git commit -m file file && + test_write_lines a b c d e F g h i j k l m n o p Q r s t u v >file && git add file && - test_write_lines a b c d e f G h i j k l m >file && - echo y | git -c diff.context=5 add -p >actual && - test_grep "@@ -2,11 +2,11 @@" actual + echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \ + reset -p -U 4 --inter-hunk-context 2 >actual && + test_grep "@@ -2,20 +2,20 @@" actual ' -test_expect_success 'add -p respects diff.interHunkContext' ' - test_write_lines a b c d e f g h i j k l m n o p q r s >file && - git add file && - test_write_lines a b c d E f g i i j k l m N o p q r s >file && - echo y | git -c diff.interhunkcontext=2 add -p >actual && - test_grep "@@ -2,16 +2,16 @@" actual +test_expect_success 'stash accepts -U and --inter-hunk-context' ' + test_write_lines a b c d e F g h i j k l m n o p Q r s t u v >file && + git commit -m file file && + test_write_lines a b c d e f g h i j k l m n o p q r s t u v >file && + echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \ + stash -p -U 4 --inter-hunk-context 2 >actual && + test_grep "@@ -2,20 +2,20 @@" actual ' -test_expect_success 'add -p rejects negative diff.context' ' - test_config diff.context -1 && - test_must_fail git add -p 2>output && - test_grep "diff.context cannot be negative" output -' +for cmd in add checkout commit reset restore "stash save" "stash push" +do + test_expect_success "$cmd rejects invalid context options" ' + test_must_fail git $cmd -p -U -3 2>actual && + cat actual | echo && + test_grep -e ".--unified. cannot be negative" actual && + + test_must_fail git $cmd -p --inter-hunk-context -3 2>actual && + test_grep -e ".--inter-hunk-context. cannot be negative" actual && + + test_must_fail git $cmd -U 7 2>actual && + test_grep -E ".--unified. requires .(--interactive/)?--patch." actual && + + test_must_fail git $cmd --inter-hunk-context 2 2>actual && + test_grep -E ".--inter-hunk-context. requires .(--interactive/)?--patch." actual + ' +done test_done diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh index 1384a8195705..0158fe6568cb 100755 --- a/t/t4055-diff-context.sh +++ b/t/t4055-diff-context.sh @@ -58,6 +58,36 @@ test_expect_success 'The -U option overrides diff.context' ' test_grep ! "^ firstline" output ' +test_expect_success 'The -U option overrides diff.context for "add"' ' + test_config diff.context 8 && + git add -U4 -p >output && + test_grep ! "^ firstline" output +' + +test_expect_success 'The -U option overrides diff.context for "commit"' ' + test_config diff.context 8 && + ! git commit -U4 -p >output && + test_grep ! "^ firstline" output +' + +test_expect_success 'The -U option overrides diff.context for "checkout"' ' + test_config diff.context 8 && + git checkout -U4 -p >output && + test_grep ! "^ firstline" output +' + +test_expect_success 'The -U option overrides diff.context for "stash"' ' + test_config diff.context 8 && + ! git stash -U4 -p >output && + test_grep ! "^ firstline" output +' + +test_expect_success 'The -U option overrides diff.context for "restore"' ' + test_config diff.context 8 && + git restore -U4 -p >output && + test_grep ! "^ firstline" output +' + test_expect_success 'diff.context honored by "diff"' ' test_config diff.context 8 && git diff >output && diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh index 343b8cd1912b..6650d33fba69 100755 --- a/t/t9902-completion.sh +++ b/t/t9902-completion.sh @@ -2596,6 +2596,8 @@ test_expect_success 'double dash "git checkout"' ' --merge Z --conflict=Z --patch Z + --unified=Z + --inter-hunk-context=Z --ignore-skip-worktree-bits Z --ignore-other-worktrees Z --recurse-submodules Z -- gitgitgadget ^ permalink raw reply related [flat|nested] 77+ messages in thread
* Re: [PATCH v4 4/4] add-patch: add diff.context command line overrides 2025-07-19 12:28 ` [PATCH v4 4/4] add-patch: add diff.context command line overrides Leon Michalak via GitGitGadget @ 2025-07-22 16:01 ` Phillip Wood 2025-07-22 18:02 ` Leon Michalak 0 siblings, 1 reply; 77+ messages in thread From: Phillip Wood @ 2025-07-22 16:01 UTC (permalink / raw) To: Leon Michalak via GitGitGadget, git Cc: Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Leon Michalak Hi Leon On 19/07/2025 13:28, Leon Michalak via GitGitGadget wrote: > From: Leon Michalak <leonmichalak6@gmail.com> > > This patch compliments the previous commit, where builtins that use > add-patch infrastructure now respect diff.context and > diff.interHunkContext file configurations. > > In particular, this patch helps users who don't want to set persistent > context configurations or just want a way to override them on a one-time > basis, by allowing the relevant builtins to accept corresponding command > line options that override the file configurations. > > This mimics commands such as diff and log, which allow for both context > file configuration and command line overrides. Thanks for updating the quoting in the tests. Unfortunately this patch now deletes the tests added in the last commit which I don't think is correct. > > -test_expect_success 'add -p respects diff.context' ' > - test_write_lines a b c d e f g h i j k l m >file && I think there is some confusion here - why are we deleting the tests added in the last commit? This removes the test coverage for diff.context and diff.interHunkContext > +for cmd in add checkout restore 'commit -m file' > +do > + test_expect_success "${cmd%% *} accepts -U and --inter-hunk-context" ' > + test_write_lines a b c d e f g h i j k l m n o p q r s t u v >file && > + git add file && > + test_write_lines a b c d e F g h i j k l m n o p Q r s t u v >file && > + echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \ > + $cmd -p -U 4 --inter-hunk-context 2 >actual && > + test_grep "@@ -2,20 +2,20 @@" actual > + ' > +done > + > +test_expect_success 'reset accepts -U and --inter-hunk-context' ' > + test_write_lines a b c d e f g h i j k l m n o p q r s t u v >file && > + git commit -m file file && > + test_write_lines a b c d e F g h i j k l m n o p Q r s t u v >file && > git add file && > - test_write_lines a b c d e f G h i j k l m >file && > - echo y | git -c diff.context=5 add -p >actual && > - test_grep "@@ -2,11 +2,11 @@" actual > + echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \ > + reset -p -U 4 --inter-hunk-context 2 >actual && > + test_grep "@@ -2,20 +2,20 @@" actual > ' > > -test_expect_success 'add -p respects diff.interHunkContext' ' > - test_write_lines a b c d e f g h i j k l m n o p q r s >file && > - git add file && > - test_write_lines a b c d E f g i i j k l m N o p q r s >file && > - echo y | git -c diff.interhunkcontext=2 add -p >actual && > - test_grep "@@ -2,16 +2,16 @@" actual This is also deleting a test added in the last patch > +test_expect_success 'stash accepts -U and --inter-hunk-context' ' > + test_write_lines a b c d e F g h i j k l m n o p Q r s t u v >file && > + git commit -m file file && > + test_write_lines a b c d e f g h i j k l m n o p q r s t u v >file && > + echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \ > + stash -p -U 4 --inter-hunk-context 2 >actual && > + test_grep "@@ -2,20 +2,20 @@" actual > ' > > -test_expect_success 'add -p rejects negative diff.context' ' > - test_config diff.context -1 && > - test_must_fail git add -p 2>output && > - test_grep "diff.context cannot be negative" output > -' and so is this. The tests you're adding look good but we shouldn't be deleting the existing ones. > +for cmd in add checkout commit reset restore "stash save" "stash push" > +do > + test_expect_success "$cmd rejects invalid context options" ' > + test_must_fail git $cmd -p -U -3 2>actual && > + cat actual | echo && > + test_grep -e ".--unified. cannot be negative" actual && > + > + test_must_fail git $cmd -p --inter-hunk-context -3 2>actual && > + test_grep -e ".--inter-hunk-context. cannot be negative" actual && > + > + test_must_fail git $cmd -U 7 2>actual && > + test_grep -E ".--unified. requires .(--interactive/)?--patch." actual && > + > + test_must_fail git $cmd --inter-hunk-context 2 2>actual && > + test_grep -E ".--inter-hunk-context. requires .(--interactive/)?--patch." actual > + ' > +done This looks good as well > test_done As I said last time I do not think the tests below add any value. They also do not compensate for the removal of the tests for diff.context that are deleted above as they all pass -U on the commandline. > diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh > index 1384a8195705..0158fe6568cb 100755 > --- a/t/t4055-diff-context.sh > +++ b/t/t4055-diff-context.sh > @@ -58,6 +58,36 @@ test_expect_success 'The -U option overrides diff.context' ' > test_grep ! "^ firstline" output > ' > > +test_expect_success 'The -U option overrides diff.context for "add"' ' > + test_config diff.context 8 && > + git add -U4 -p >output && > + test_grep ! "^ firstline" output > +' > + > +test_expect_success 'The -U option overrides diff.context for "commit"' ' > + test_config diff.context 8 && > + ! git commit -U4 -p >output && > + test_grep ! "^ firstline" output > +' > + > +test_expect_success 'The -U option overrides diff.context for "checkout"' ' > + test_config diff.context 8 && > + git checkout -U4 -p >output && > + test_grep ! "^ firstline" output > +' > + > +test_expect_success 'The -U option overrides diff.context for "stash"' ' > + test_config diff.context 8 && > + ! git stash -U4 -p >output && > + test_grep ! "^ firstline" output > +' > + > +test_expect_success 'The -U option overrides diff.context for "restore"' ' > + test_config diff.context 8 && > + git restore -U4 -p >output && > + test_grep ! "^ firstline" output > +' > + > test_expect_success 'diff.context honored by "diff"' ' > test_config diff.context 8 && > git diff >output && Thanks Phillip ^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v4 4/4] add-patch: add diff.context command line overrides 2025-07-22 16:01 ` Phillip Wood @ 2025-07-22 18:02 ` Leon Michalak 2025-07-22 18:05 ` Leon Michalak 2025-07-23 9:41 ` Phillip Wood 0 siblings, 2 replies; 77+ messages in thread From: Leon Michalak @ 2025-07-22 18:02 UTC (permalink / raw) To: phillip.wood Cc: Leon Michalak via GitGitGadget, git, Kristoffer Haugsbakk, Eric Sunshine, Christian Couder Hey Philip, looking again for a second time and re-reading previous replies on this thread, I think I misunderstood a previous reply. I will add these back as soon as I can get back to the PC, thanks for spotting that! On Tue, 22 Jul 2025 at 17:02, Phillip Wood <phillip.wood123@gmail.com> wrote: > > Hi Leon > > On 19/07/2025 13:28, Leon Michalak via GitGitGadget wrote: > > From: Leon Michalak <leonmichalak6@gmail.com> > > > > This patch compliments the previous commit, where builtins that use > > add-patch infrastructure now respect diff.context and > > diff.interHunkContext file configurations. > > > > In particular, this patch helps users who don't want to set persistent > > context configurations or just want a way to override them on a one-time > > basis, by allowing the relevant builtins to accept corresponding command > > line options that override the file configurations. > > > > This mimics commands such as diff and log, which allow for both context > > file configuration and command line overrides. > > Thanks for updating the quoting in the tests. Unfortunately this patch > now deletes the tests added in the last commit which I don't think is > correct. > > > > > -test_expect_success 'add -p respects diff.context' ' > > - test_write_lines a b c d e f g h i j k l m >file && > I think there is some confusion here - why are we deleting the tests > added in the last commit? This removes the test coverage for > diff.context and diff.interHunkContext > > > +for cmd in add checkout restore 'commit -m file' > > +do > > + test_expect_success "${cmd%% *} accepts -U and --inter-hunk-context" ' > > + test_write_lines a b c d e f g h i j k l m n o p q r s t u v >file && > > + git add file && > > + test_write_lines a b c d e F g h i j k l m n o p Q r s t u v >file && > > + echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \ > > + $cmd -p -U 4 --inter-hunk-context 2 >actual && > > + test_grep "@@ -2,20 +2,20 @@" actual > > + ' > > +done > > + > > +test_expect_success 'reset accepts -U and --inter-hunk-context' ' > > + test_write_lines a b c d e f g h i j k l m n o p q r s t u v >file && > > + git commit -m file file && > > + test_write_lines a b c d e F g h i j k l m n o p Q r s t u v >file && > > git add file && > > - test_write_lines a b c d e f G h i j k l m >file && > > - echo y | git -c diff.context=5 add -p >actual && > > - test_grep "@@ -2,11 +2,11 @@" actual > > + echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \ > > + reset -p -U 4 --inter-hunk-context 2 >actual && > > + test_grep "@@ -2,20 +2,20 @@" actual > > ' > > > > -test_expect_success 'add -p respects diff.interHunkContext' ' > > - test_write_lines a b c d e f g h i j k l m n o p q r s >file && > > - git add file && > > - test_write_lines a b c d E f g i i j k l m N o p q r s >file && > > - echo y | git -c diff.interhunkcontext=2 add -p >actual && > > - test_grep "@@ -2,16 +2,16 @@" actual > > This is also deleting a test added in the last patch > > > +test_expect_success 'stash accepts -U and --inter-hunk-context' ' > > + test_write_lines a b c d e F g h i j k l m n o p Q r s t u v >file && > > + git commit -m file file && > > + test_write_lines a b c d e f g h i j k l m n o p q r s t u v >file && > > + echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \ > > + stash -p -U 4 --inter-hunk-context 2 >actual && > > + test_grep "@@ -2,20 +2,20 @@" actual > > ' > > > > -test_expect_success 'add -p rejects negative diff.context' ' > > - test_config diff.context -1 && > > - test_must_fail git add -p 2>output && > > - test_grep "diff.context cannot be negative" output > > -' > > and so is this. The tests you're adding look good but we shouldn't be > deleting the existing ones. > > > +for cmd in add checkout commit reset restore "stash save" "stash push" > > +do > > + test_expect_success "$cmd rejects invalid context options" ' > > + test_must_fail git $cmd -p -U -3 2>actual && > > + cat actual | echo && > > + test_grep -e ".--unified. cannot be negative" actual && > > + > > + test_must_fail git $cmd -p --inter-hunk-context -3 2>actual && > > + test_grep -e ".--inter-hunk-context. cannot be negative" actual && > > + > > + test_must_fail git $cmd -U 7 2>actual && > > + test_grep -E ".--unified. requires .(--interactive/)?--patch." actual && > > + > > + test_must_fail git $cmd --inter-hunk-context 2 2>actual && > > + test_grep -E ".--inter-hunk-context. requires .(--interactive/)?--patch." actual > > + ' > > +done > > This looks good as well > > test_done > As I said last time I do not think the tests below add any value. They > also do not compensate for the removal of the tests for diff.context > that are deleted above as they all pass -U on the commandline. > > > diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh > > index 1384a8195705..0158fe6568cb 100755 > > --- a/t/t4055-diff-context.sh > > +++ b/t/t4055-diff-context.sh > > @@ -58,6 +58,36 @@ test_expect_success 'The -U option overrides diff.context' ' > > test_grep ! "^ firstline" output > > ' > > > > +test_expect_success 'The -U option overrides diff.context for "add"' ' > > + test_config diff.context 8 && > > + git add -U4 -p >output && > > + test_grep ! "^ firstline" output > > +' > > + > > +test_expect_success 'The -U option overrides diff.context for "commit"' ' > > + test_config diff.context 8 && > > + ! git commit -U4 -p >output && > > + test_grep ! "^ firstline" output > > +' > > + > > +test_expect_success 'The -U option overrides diff.context for "checkout"' ' > > + test_config diff.context 8 && > > + git checkout -U4 -p >output && > > + test_grep ! "^ firstline" output > > +' > > + > > +test_expect_success 'The -U option overrides diff.context for "stash"' ' > > + test_config diff.context 8 && > > + ! git stash -U4 -p >output && > > + test_grep ! "^ firstline" output > > +' > > + > > +test_expect_success 'The -U option overrides diff.context for "restore"' ' > > + test_config diff.context 8 && > > + git restore -U4 -p >output && > > + test_grep ! "^ firstline" output > > +' > > + > > test_expect_success 'diff.context honored by "diff"' ' > > test_config diff.context 8 && > > git diff >output && > > Thanks > > Phillip > ^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v4 4/4] add-patch: add diff.context command line overrides 2025-07-22 18:02 ` Leon Michalak @ 2025-07-22 18:05 ` Leon Michalak 2025-07-23 9:41 ` Phillip Wood 1 sibling, 0 replies; 77+ messages in thread From: Leon Michalak @ 2025-07-22 18:05 UTC (permalink / raw) To: phillip.wood Cc: Leon Michalak via GitGitGadget, git, Kristoffer Haugsbakk, Eric Sunshine, Christian Couder Apologies for the top-posting above, this is not my regular workflow! ^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v4 4/4] add-patch: add diff.context command line overrides 2025-07-22 18:02 ` Leon Michalak 2025-07-22 18:05 ` Leon Michalak @ 2025-07-23 9:41 ` Phillip Wood 1 sibling, 0 replies; 77+ messages in thread From: Phillip Wood @ 2025-07-23 9:41 UTC (permalink / raw) To: leonmichalak6 Cc: christian.couder, git, gitgitgadget, kristofferhaugsbakk, phillip.wood, sunshine Hi Leon Here is a fixup commit for the tests which can be squashed into patch 4 Best Wishes Phillip ---- 8< ---- From: Phillip Wood <phillip.wood@dunelm.org.uk> Subject: [PATCH] fixup! add-patch: add diff.context command line overrides Restore the test coverage for diff.context and diff.interHunkContext added in f08d4ae6e56 (add-patch: respect diff.context configuration, 2025-07-19) and remove the redunant tests in t4055 added by bd6d6ba1321 (add-patch: add diff.context command line overrides, 2025-07-19) which duplicate the coverage of the tests added to t3071 in the same commit. Signed-off-by: Phillip Wood <phillip.wood@dunelm.org.uk> --- t/t3701-add-interactive.sh | 22 ++++++++++++++++++++++ t/t4055-diff-context.sh | 30 ------------------------------ 2 files changed, 22 insertions(+), 30 deletions(-) diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index 7fd9a23c998..04d2a198352 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -1230,6 +1230,28 @@ test_expect_success 'hunk splitting works with diff.suppressBlankEmpty' ' test_cmp expect actual ' +test_expect_success 'add -p respects diff.context' ' + test_write_lines a b c d e f g h i j k l m >file && + git add file && + test_write_lines a b c d e f G h i j k l m >file && + echo y | git -c diff.context=5 add -p >actual && + test_grep "@@ -2,11 +2,11 @@" actual +' + +test_expect_success 'add -p respects diff.interHunkContext' ' + test_write_lines a b c d e f g h i j k l m n o p q r s >file && + git add file && + test_write_lines a b c d E f g i i j k l m N o p q r s >file && + echo y | git -c diff.interhunkcontext=2 add -p >actual && + test_grep "@@ -2,16 +2,16 @@" actual +' + +test_expect_success 'add -p rejects negative diff.context' ' + test_config diff.context -1 && + test_must_fail git add -p 2>output && + test_grep "diff.context cannot be negative" output +' + for cmd in add checkout restore 'commit -m file' do test_expect_success "${cmd%% *} accepts -U and --inter-hunk-context" ' diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh index 0158fe6568c..1384a819570 100755 --- a/t/t4055-diff-context.sh +++ b/t/t4055-diff-context.sh @@ -58,36 +58,6 @@ test_expect_success 'The -U option overrides diff.context' ' test_grep ! "^ firstline" output ' -test_expect_success 'The -U option overrides diff.context for "add"' ' - test_config diff.context 8 && - git add -U4 -p >output && - test_grep ! "^ firstline" output -' - -test_expect_success 'The -U option overrides diff.context for "commit"' ' - test_config diff.context 8 && - ! git commit -U4 -p >output && - test_grep ! "^ firstline" output -' - -test_expect_success 'The -U option overrides diff.context for "checkout"' ' - test_config diff.context 8 && - git checkout -U4 -p >output && - test_grep ! "^ firstline" output -' - -test_expect_success 'The -U option overrides diff.context for "stash"' ' - test_config diff.context 8 && - ! git stash -U4 -p >output && - test_grep ! "^ firstline" output -' - -test_expect_success 'The -U option overrides diff.context for "restore"' ' - test_config diff.context 8 && - git restore -U4 -p >output && - test_grep ! "^ firstline" output -' - test_expect_success 'diff.context honored by "diff"' ' test_config diff.context 8 && git diff >output && -- 2.49.0.897.gfad3eb7d210 ^ permalink raw reply related [flat|nested] 77+ messages in thread
* Re: [PATCH v4 0/4] Better support for customising context lines in --patch commands 2025-07-19 12:28 ` [PATCH v4 " Leon Michalak via GitGitGadget ` (3 preceding siblings ...) 2025-07-19 12:28 ` [PATCH v4 4/4] add-patch: add diff.context command line overrides Leon Michalak via GitGitGadget @ 2025-07-21 16:50 ` Junio C Hamano 2025-07-22 16:05 ` Phillip Wood 2025-07-29 7:01 ` [PATCH v5 " Leon Michalak via GitGitGadget 5 siblings, 1 reply; 77+ messages in thread From: Junio C Hamano @ 2025-07-21 16:50 UTC (permalink / raw) To: Leon Michalak via GitGitGadget Cc: git, Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Phillip Wood, Leon Michalak "Leon Michalak via GitGitGadget" <gitgitgadget@gmail.com> writes: > Changes since v3: > > * Update commit descriptions > * Read struct properties directly instead of assigning to variables first > * Simplify config setting / error checking > * Remove redundant tests in later commit as they were replaced with better > test(s) > * Change tests to use single quotes (this messes with the grep so was > unable to explicitly test single quotes in the error messages, so decided > to use regex . instead, which is what some other tests that have this > problem seem to use as well) All of the above looked reasonably well done. Will replace. Unless there are objections, let me mark the topic for 'next' soonish. Thanks. ^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v4 0/4] Better support for customising context lines in --patch commands 2025-07-21 16:50 ` [PATCH v4 0/4] Better support for customising context lines in --patch commands Junio C Hamano @ 2025-07-22 16:05 ` Phillip Wood 2025-07-22 17:20 ` Junio C Hamano 0 siblings, 1 reply; 77+ messages in thread From: Phillip Wood @ 2025-07-22 16:05 UTC (permalink / raw) To: Junio C Hamano, Leon Michalak via GitGitGadget Cc: git, Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Leon Michalak On 21/07/2025 17:50, Junio C Hamano wrote: > "Leon Michalak via GitGitGadget" <gitgitgadget@gmail.com> writes: > >> Changes since v3: >> >> * Update commit descriptions >> * Read struct properties directly instead of assigning to variables first >> * Simplify config setting / error checking >> * Remove redundant tests in later commit as they were replaced with better >> test(s) I've left some comments on patch 4 about this. It seems to me that it is deleting useful tests and replacing them with redundant tests. >> * Change tests to use single quotes (this messes with the grep so was >> unable to explicitly test single quotes in the error messages, so decided >> to use regex . instead, which is what some other tests that have this >> problem seem to use as well) Thanks for doing that > > All of the above looked reasonably well done. Will replace. > > Unless there are objections, let me mark the topic for 'next' soonish. I think we want to sort out the test changes in patch 4. Previously we discussed centralizing the option parsing in that patch as well but we can always do that as a follow up later. Thanks Phillip ^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v4 0/4] Better support for customising context lines in --patch commands 2025-07-22 16:05 ` Phillip Wood @ 2025-07-22 17:20 ` Junio C Hamano 0 siblings, 0 replies; 77+ messages in thread From: Junio C Hamano @ 2025-07-22 17:20 UTC (permalink / raw) To: Phillip Wood Cc: Leon Michalak via GitGitGadget, git, Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Leon Michalak Phillip Wood <phillip.wood123@gmail.com> writes: >> All of the above looked reasonably well done. Will replace. >> Unless there are objections, let me mark the topic for 'next' >> soonish. > > I think we want to sort out the test changes in patch 4. Previously we > discussed centralizing the option parsing in that patch as well but we > can always do that as a follow up later. Yeah, the tests can probably use a bit more polish before we can move forward. Thanks for carefully reading them over. ^ permalink raw reply [flat|nested] 77+ messages in thread
* [PATCH v5 0/4] Better support for customising context lines in --patch commands 2025-07-19 12:28 ` [PATCH v4 " Leon Michalak via GitGitGadget ` (4 preceding siblings ...) 2025-07-21 16:50 ` [PATCH v4 0/4] Better support for customising context lines in --patch commands Junio C Hamano @ 2025-07-29 7:01 ` Leon Michalak via GitGitGadget 2025-07-29 7:01 ` [PATCH v5 1/4] t: use test_grep in t3701 and t4055 Leon Michalak via GitGitGadget ` (4 more replies) 5 siblings, 5 replies; 77+ messages in thread From: Leon Michalak via GitGitGadget @ 2025-07-29 7:01 UTC (permalink / raw) To: git Cc: Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Phillip Wood, Leon Michalak This series of patches attempt to give --interactive/--patch compatible builtins ("add", "commit", "checkout", "reset", "restore" and "stash") better support and nicer experience for configuring how many context lines are shown in diffs through a variety of ways. Prior to these patches, the user could not choose how many context lines they saw in --patch commands (apart from one workaround by using GIT_DIFF_OPTS=-u<number> ..., however this isn't a good user experience or a persistent solution). Additionally, the behaviour around reading from the diff.context and diff.interHunkContext configs was also inconsistent with other diff generating commands such as "log -p". The summarised changes below hopefully make this experience better and fix some inconsistencies: * diff.context and diff.interHunkContext configs are now respected by --patch compatible commands * --unified and --inter-hunk-context command line options have been added to --patch compatible commands (which take prescendence over file configs) * "add" and "commit" in --interactive mode now expose a new "context" subcommand which configures the amount of context lines you wish to see in subsequent diffs generated from other subcommands such as "patch" or "diff" The original discussion for this can be read at: * https://lore.kernel.org/git/CAP9jKjGb-Rcr=RLJEzeFdtrekYM+qmHy+1T1fykU3n9cV4GhGw@mail.gmail.com/ Changes since v1: * Update commit descriptions * Update tests to use the more modern and robust test_grep and test_config utils * Reword some documentation / user messages * Ensure each commit is atomic and builds/passes tests on it's own * Make new command line options DRY * Add tests for interhunk context interaction * Error if context config/command line options are negative * Drop previous last commit to do with new subcommand for --interactive add/commit. My motivations behind this patch series originally where quite simple, just for add-patch commands to respect context configs. This subcommand, after the discussion in v1, will require more thought and a larger implementation that what I had anticipated. I would prefer to leave this for another time as it's the least impactful but the most time intensive and complicated idea. Changes since v2: * Update tests to only test single command (following Philip's suggestion) * Add negative option checks * Minor commit re-wording Changes since v3: * Update commit descriptions * Read struct properties directly instead of assigning to variables first * Simplify config setting / error checking * Remove redundant tests in later commit as they were replaced with better test(s) * Change tests to use single quotes (this messes with the grep so was unable to explicitly test single quotes in the error messages, so decided to use regex . instead, which is what some other tests that have this problem seem to use as well) Changes since v4: * Add back tests to maintain good coverage and remove redundant tests Leon Michalak (4): t: use test_grep in t3701 and t4055 t: use test_config in t4055 add-patch: respect diff.context configuration add-patch: add diff.context command line overrides Documentation/diff-context-options.adoc | 10 ++ Documentation/git-add.adoc | 2 + Documentation/git-checkout.adoc | 2 + Documentation/git-commit.adoc | 2 + Documentation/git-reset.adoc | 2 + Documentation/git-restore.adoc | 2 + Documentation/git-stash.adoc | 2 + add-interactive.c | 45 +++++++-- add-interactive.h | 17 +++- add-patch.c | 14 ++- builtin/add.c | 21 ++++- builtin/checkout.c | 31 +++++- builtin/commit.c | 16 +++- builtin/reset.c | 17 +++- builtin/stash.c | 56 ++++++++--- commit.h | 3 +- parse-options.h | 2 + t/t3701-add-interactive.sh | 119 +++++++++++++++++++----- t/t4055-diff-context.sh | 42 ++++----- t/t9902-completion.sh | 2 + 20 files changed, 324 insertions(+), 83 deletions(-) create mode 100644 Documentation/diff-context-options.adoc base-commit: cf6f63ea6bf35173e02e18bdc6a4ba41288acff9 Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1915%2FNinjaInShade%2Finteractive-patch-context-v5 Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1915/NinjaInShade/interactive-patch-context-v5 Pull-Request: https://github.com/gitgitgadget/git/pull/1915 Range-diff vs v4: 1: bbb2bc7082b = 1: bbb2bc7082b t: use test_grep in t3701 and t4055 2: feace2d3676 = 2: feace2d3676 t: use test_config in t4055 3: 994029d6602 = 3: 994029d6602 add-patch: respect diff.context configuration 4: 2774b930406 ! 4: 9731e5b76fb add-patch: add diff.context command line overrides @@ parse-options.h: int parse_opt_tracking_mode(const struct option *, const char * OPT_SET_INT_F('4', "ipv4", (v), N_("use IPv4 addresses only"), \ ## t/t3701-add-interactive.sh ## -@@ t/t3701-add-interactive.sh: test_expect_success 'hunk splitting works with diff.suppressBlankEmpty' ' - test_cmp expect actual +@@ t/t3701-add-interactive.sh: test_expect_success 'add -p rejects negative diff.context' ' + test_grep "diff.context cannot be negative" output ' --test_expect_success 'add -p respects diff.context' ' -- test_write_lines a b c d e f g h i j k l m >file && +for cmd in add checkout restore 'commit -m file' +do + test_expect_success "${cmd%% *} accepts -U and --inter-hunk-context" ' @@ t/t3701-add-interactive.sh: test_expect_success 'hunk splitting works with diff. + test_write_lines a b c d e f g h i j k l m n o p q r s t u v >file && + git commit -m file file && + test_write_lines a b c d e F g h i j k l m n o p Q r s t u v >file && - git add file && -- test_write_lines a b c d e f G h i j k l m >file && -- echo y | git -c diff.context=5 add -p >actual && -- test_grep "@@ -2,11 +2,11 @@" actual ++ git add file && + echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \ + reset -p -U 4 --inter-hunk-context 2 >actual && + test_grep "@@ -2,20 +2,20 @@" actual - ' - --test_expect_success 'add -p respects diff.interHunkContext' ' -- test_write_lines a b c d e f g h i j k l m n o p q r s >file && -- git add file && -- test_write_lines a b c d E f g i i j k l m N o p q r s >file && -- echo y | git -c diff.interhunkcontext=2 add -p >actual && -- test_grep "@@ -2,16 +2,16 @@" actual ++' ++ +test_expect_success 'stash accepts -U and --inter-hunk-context' ' + test_write_lines a b c d e F g h i j k l m n o p Q r s t u v >file && + git commit -m file file && @@ t/t3701-add-interactive.sh: test_expect_success 'hunk splitting works with diff. + echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \ + stash -p -U 4 --inter-hunk-context 2 >actual && + test_grep "@@ -2,20 +2,20 @@" actual - ' - --test_expect_success 'add -p rejects negative diff.context' ' -- test_config diff.context -1 && -- test_must_fail git add -p 2>output && -- test_grep "diff.context cannot be negative" output --' ++' ++ +for cmd in add checkout commit reset restore "stash save" "stash push" +do + test_expect_success "$cmd rejects invalid context options" ' @@ t/t3701-add-interactive.sh: test_expect_success 'hunk splitting works with diff. + test_grep -E ".--inter-hunk-context. requires .(--interactive/)?--patch." actual + ' +done - - test_done - - ## t/t4055-diff-context.sh ## -@@ t/t4055-diff-context.sh: test_expect_success 'The -U option overrides diff.context' ' - test_grep ! "^ firstline" output - ' - -+test_expect_success 'The -U option overrides diff.context for "add"' ' -+ test_config diff.context 8 && -+ git add -U4 -p >output && -+ test_grep ! "^ firstline" output -+' -+ -+test_expect_success 'The -U option overrides diff.context for "commit"' ' -+ test_config diff.context 8 && -+ ! git commit -U4 -p >output && -+ test_grep ! "^ firstline" output -+' -+ -+test_expect_success 'The -U option overrides diff.context for "checkout"' ' -+ test_config diff.context 8 && -+ git checkout -U4 -p >output && -+ test_grep ! "^ firstline" output -+' + -+test_expect_success 'The -U option overrides diff.context for "stash"' ' -+ test_config diff.context 8 && -+ ! git stash -U4 -p >output && -+ test_grep ! "^ firstline" output -+' -+ -+test_expect_success 'The -U option overrides diff.context for "restore"' ' -+ test_config diff.context 8 && -+ git restore -U4 -p >output && -+ test_grep ! "^ firstline" output -+' -+ - test_expect_success 'diff.context honored by "diff"' ' - test_config diff.context 8 && - git diff >output && + test_done ## t/t9902-completion.sh ## @@ t/t9902-completion.sh: test_expect_success 'double dash "git checkout"' ' -- gitgitgadget ^ permalink raw reply [flat|nested] 77+ messages in thread
* [PATCH v5 1/4] t: use test_grep in t3701 and t4055 2025-07-29 7:01 ` [PATCH v5 " Leon Michalak via GitGitGadget @ 2025-07-29 7:01 ` Leon Michalak via GitGitGadget 2025-07-29 7:01 ` [PATCH v5 2/4] t: use test_config in t4055 Leon Michalak via GitGitGadget ` (3 subsequent siblings) 4 siblings, 0 replies; 77+ messages in thread From: Leon Michalak via GitGitGadget @ 2025-07-29 7:01 UTC (permalink / raw) To: git Cc: Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Phillip Wood, Leon Michalak, Leon Michalak From: Leon Michalak <leonmichalak6@gmail.com> As a preparatory clean-up, use the "test_grep" test utility instead of regular "grep" which provides better debug information if tests fail. Signed-off-by: Leon Michalak <leonmichalak6@gmail.com> --- t/t3701-add-interactive.sh | 48 +++++++++++++++++++------------------- t/t4055-diff-context.sh | 28 +++++++++++----------- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index b8a05d95f3f1..b088ee141ff4 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -63,7 +63,7 @@ test_expect_success 'setup (initial)' ' ' test_expect_success 'status works (initial)' ' git add -i </dev/null >output && - grep "+1/-0 *+2/-0 file" output + test_grep "+1/-0 *+2/-0 file" output ' test_expect_success 'setup expected' ' @@ -86,7 +86,7 @@ test_expect_success 'revert works (initial)' ' git add file && test_write_lines r 1 | git add -i && git ls-files >output && - ! grep . output + test_grep ! . output ' test_expect_success 'add untracked (multiple)' ' @@ -109,7 +109,7 @@ test_expect_success 'setup (commit)' ' ' test_expect_success 'status works (commit)' ' git add -i </dev/null >output && - grep "+1/-0 *+2/-0 file" output + test_grep "+1/-0 *+2/-0 file" output ' test_expect_success 'update can stage deletions' ' @@ -141,7 +141,7 @@ test_expect_success 'revert works (commit)' ' git add file && test_write_lines r 1 | git add -i && git add -i </dev/null >output && - grep "unchanged *+3/-0 file" output + test_grep "unchanged *+3/-0 file" output ' test_expect_success 'reject multi-key input' ' @@ -185,7 +185,7 @@ test_expect_success 'setup fake editor' ' test_expect_success 'bad edit rejected' ' git reset && test_write_lines e n d | git add -p >output && - grep "hunk does not apply" output + test_grep "hunk does not apply" output ' test_expect_success 'setup patch' ' @@ -198,7 +198,7 @@ test_expect_success 'setup patch' ' test_expect_success 'garbage edit rejected' ' git reset && test_write_lines e n d | git add -p >output && - grep "hunk does not apply" output + test_grep "hunk does not apply" output ' test_expect_success 'setup patch' ' @@ -313,8 +313,8 @@ test_expect_success FILEMODE 'stage mode and hunk' ' chmod +x file && printf "y\\ny\\n" | git add -p && git diff --cached file >out && - grep "new mode" out && - grep "+content" out && + test_grep "new mode" out && + test_grep "+content" out && git diff file >out && test_must_be_empty out ' @@ -636,7 +636,7 @@ test_expect_success 'split hunk "add -p (edit)"' ' printf "%s\n" s e q n q q | EDITOR=: git add -p && git diff >actual && - ! grep "^+15" actual + test_grep ! "^+15" actual ' test_expect_success 'split hunk "add -p (no, yes, edit)"' ' @@ -648,7 +648,7 @@ test_expect_success 'split hunk "add -p (no, yes, edit)"' ' EDITOR=: git add -p 2>error && test_must_be_empty error && git diff >actual && - ! grep "^+31" actual + test_grep ! "^+31" actual ' test_expect_success 'split hunk with incomplete line at end' ' @@ -682,7 +682,7 @@ test_expect_success 'edit, adding lines to the first hunk' ' EDITOR=./fake_editor.sh git add -p 2>error && test_must_be_empty error && git diff --cached >actual && - grep "^+22" actual + test_grep "^+22" actual ' test_expect_success 'patch mode ignores unmerged entries' ' @@ -696,7 +696,7 @@ test_expect_success 'patch mode ignores unmerged entries' ' test_must_fail git merge side && echo changed >non-conflict.t && echo y | git add -p >output && - ! grep a/conflict.t output && + test_grep ! a/conflict.t output && cat >expected <<-\EOF && * Unmerged path conflict.t diff --git a/non-conflict.t b/non-conflict.t @@ -728,7 +728,7 @@ test_expect_success 'diffs can be colorized' ' # We do not want to depend on the exact coloring scheme # git uses for diffs, so just check that we saw some kind of color. - grep "$(printf "\\033")" output + test_grep "$(printf "\\033")" output ' test_expect_success 'colors can be overridden' ' @@ -743,7 +743,7 @@ test_expect_success 'colors can be overridden' ' -c color.interactive.error=blue \ add -i 2>err.raw <input && test_decode_color <err.raw >err && - grep "<BLUE>Huh (trigger)?<RESET>" err && + test_grep "<BLUE>Huh (trigger)?<RESET>" err && test_write_lines help quit >input && force_color git \ @@ -863,7 +863,7 @@ test_expect_success 'colorized diffs respect diff.wsErrorHighlight' ' printf y >y && force_color git -c diff.wsErrorHighlight=all add -p >output.raw 2>&1 <y && test_decode_color <output.raw >output && - grep "old<" output + test_grep "old<" output ' test_expect_success 'diffFilter filters diff' ' @@ -876,7 +876,7 @@ test_expect_success 'diffFilter filters diff' ' # avoid depending on the exact coloring or content of the prompts, # and just make sure we saw our diff prefixed - grep foo:.*content output + test_grep foo:.*content output ' test_expect_success 'detect bogus diffFilter output' ' @@ -886,7 +886,7 @@ test_expect_success 'detect bogus diffFilter output' ' test_config interactive.diffFilter "sed 6d" && printf y >y && force_color test_must_fail git add -p <y >output 2>&1 && - grep "mismatched output" output + test_grep "mismatched output" output ' test_expect_success 'handle iffy colored hunk headers' ' @@ -896,7 +896,7 @@ test_expect_success 'handle iffy colored hunk headers' ' printf n >n && force_color git -c interactive.diffFilter="sed s/.*@@.*/XX/" \ add -p >output 2>&1 <n && - grep "^XX$" output + test_grep "^XX$" output ' test_expect_success 'handle very large filtered diff' ' @@ -1002,7 +1002,7 @@ test_expect_success 'add -p does not expand argument lists' ' # update it, but we want to be sure that our "." pathspec # was not expanded into the argument list of any command. # So look only for "not-changed". - ! grep -E "^trace: (built-in|exec|run_command): .*not-changed" trace.out + test_grep ! -E "^trace: (built-in|exec|run_command): .*not-changed" trace.out ' test_expect_success 'hunk-editing handles custom comment char' ' @@ -1072,21 +1072,21 @@ test_expect_success 'setup different kinds of dirty submodules' ' test_expect_success 'status ignores dirty submodules (except HEAD)' ' git -C for-submodules add -i </dev/null >output && - grep dirty-head output && - grep dirty-both-ways output && - ! grep dirty-otherwise output + test_grep dirty-head output && + test_grep dirty-both-ways output && + test_grep ! dirty-otherwise output ' test_expect_success 'handle submodules' ' echo 123 >>for-submodules/dirty-otherwise/initial.t && force_color git -C for-submodules add -p dirty-otherwise >output 2>&1 && - grep "No changes" output && + test_grep "No changes" output && force_color git -C for-submodules add -p dirty-head >output 2>&1 <y && git -C for-submodules ls-files --stage dirty-head >actual && rev="$(git -C for-submodules/dirty-head rev-parse HEAD)" && - grep "$rev" actual + test_grep "$rev" actual ' test_expect_success 'set up pathological context' ' diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh index ec2804eea67c..c66f966a3ab3 100755 --- a/t/t4055-diff-context.sh +++ b/t/t4055-diff-context.sh @@ -38,36 +38,36 @@ test_expect_success 'setup' ' test_expect_success 'the default number of context lines is 3' ' git diff >output && - ! grep "^ d" output && - grep "^ e" output && - grep "^ j" output && - ! grep "^ k" output + test_grep ! "^ d" output && + test_grep "^ e" output && + test_grep "^ j" output && + test_grep ! "^ k" output ' test_expect_success 'diff.context honored by "log"' ' git log -1 -p >output && - ! grep firstline output && + test_grep ! firstline output && git config diff.context 8 && git log -1 -p >output && - grep "^ firstline" output + test_grep "^ firstline" output ' test_expect_success 'The -U option overrides diff.context' ' git config diff.context 8 && git log -U4 -1 >output && - ! grep "^ firstline" output + test_grep ! "^ firstline" output ' test_expect_success 'diff.context honored by "diff"' ' git config diff.context 8 && git diff >output && - grep "^ firstline" output + test_grep "^ firstline" output ' test_expect_success 'plumbing not affected' ' git config diff.context 8 && git diff-files -p >output && - ! grep "^ firstline" output + test_grep ! "^ firstline" output ' test_expect_success 'non-integer config parsing' ' @@ -85,8 +85,8 @@ test_expect_success 'negative integer config parsing' ' test_expect_success '-U0 is valid, so is diff.context=0' ' git config diff.context 0 && git diff >output && - grep "^-ADDED" output && - grep "^+MODIFIED" output + test_grep "^-ADDED" output && + test_grep "^+MODIFIED" output ' test_expect_success '-U2147483647 works' ' @@ -94,9 +94,9 @@ test_expect_success '-U2147483647 works' ' test_line_count = 16 x && git diff -U2147483647 >output && test_line_count = 22 output && - grep "^-ADDED" output && - grep "^+MODIFIED" output && - grep "^+APPENDED" output + test_grep "^-ADDED" output && + test_grep "^+MODIFIED" output && + test_grep "^+APPENDED" output ' test_done -- gitgitgadget ^ permalink raw reply related [flat|nested] 77+ messages in thread
* [PATCH v5 2/4] t: use test_config in t4055 2025-07-29 7:01 ` [PATCH v5 " Leon Michalak via GitGitGadget 2025-07-29 7:01 ` [PATCH v5 1/4] t: use test_grep in t3701 and t4055 Leon Michalak via GitGitGadget @ 2025-07-29 7:01 ` Leon Michalak via GitGitGadget 2025-07-29 7:01 ` [PATCH v5 3/4] add-patch: respect diff.context configuration Leon Michalak via GitGitGadget ` (2 subsequent siblings) 4 siblings, 0 replies; 77+ messages in thread From: Leon Michalak via GitGitGadget @ 2025-07-29 7:01 UTC (permalink / raw) To: git Cc: Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Phillip Wood, Leon Michalak, Leon Michalak From: Leon Michalak <leonmichalak6@gmail.com> Use the modern "test_config" test utility instead of manual"git config" as the former provides clean up on test completion. This is a prerequisite to the commits that follow which add to this test file. Signed-off-by: Leon Michalak <leonmichalak6@gmail.com> --- t/t4055-diff-context.sh | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh index c66f966a3ab3..1384a8195705 100755 --- a/t/t4055-diff-context.sh +++ b/t/t4055-diff-context.sh @@ -47,43 +47,43 @@ test_expect_success 'the default number of context lines is 3' ' test_expect_success 'diff.context honored by "log"' ' git log -1 -p >output && test_grep ! firstline output && - git config diff.context 8 && + test_config diff.context 8 && git log -1 -p >output && test_grep "^ firstline" output ' test_expect_success 'The -U option overrides diff.context' ' - git config diff.context 8 && + test_config diff.context 8 && git log -U4 -1 >output && test_grep ! "^ firstline" output ' test_expect_success 'diff.context honored by "diff"' ' - git config diff.context 8 && + test_config diff.context 8 && git diff >output && test_grep "^ firstline" output ' test_expect_success 'plumbing not affected' ' - git config diff.context 8 && + test_config diff.context 8 && git diff-files -p >output && test_grep ! "^ firstline" output ' test_expect_success 'non-integer config parsing' ' - git config diff.context no && + test_config diff.context no && test_must_fail git diff 2>output && test_grep "bad numeric config value" output ' test_expect_success 'negative integer config parsing' ' - git config diff.context -1 && + test_config diff.context -1 && test_must_fail git diff 2>output && test_grep "bad config variable" output ' test_expect_success '-U0 is valid, so is diff.context=0' ' - git config diff.context 0 && + test_config diff.context 0 && git diff >output && test_grep "^-ADDED" output && test_grep "^+MODIFIED" output -- gitgitgadget ^ permalink raw reply related [flat|nested] 77+ messages in thread
* [PATCH v5 3/4] add-patch: respect diff.context configuration 2025-07-29 7:01 ` [PATCH v5 " Leon Michalak via GitGitGadget 2025-07-29 7:01 ` [PATCH v5 1/4] t: use test_grep in t3701 and t4055 Leon Michalak via GitGitGadget 2025-07-29 7:01 ` [PATCH v5 2/4] t: use test_config in t4055 Leon Michalak via GitGitGadget @ 2025-07-29 7:01 ` Leon Michalak via GitGitGadget 2025-07-29 7:01 ` [PATCH v5 4/4] add-patch: add diff.context command line overrides Leon Michalak via GitGitGadget 2025-07-29 15:21 ` [PATCH v5 0/4] Better support for customising context lines in --patch commands Phillip Wood 4 siblings, 0 replies; 77+ messages in thread From: Leon Michalak via GitGitGadget @ 2025-07-29 7:01 UTC (permalink / raw) To: git Cc: Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Phillip Wood, Leon Michalak, Leon Michalak From: Leon Michalak <leonmichalak6@gmail.com> Various builtins that use add-patch infrastructure do not respect the user's diff.context and diff.interHunkContext file configurations. The user may be used to seeing their diffs with customized context size, but not in the patches "git add -p" shows them to pick from. Teach add-patch infrastructure to read these configuration variables and pass their values when spawning the underlying plumbing commands as their command line option. Signed-off-by: Leon Michalak <leonmichalak6@gmail.com> --- add-interactive.c | 9 +++++++++ add-interactive.h | 1 + add-patch.c | 9 ++++++--- t/t3701-add-interactive.sh | 22 ++++++++++++++++++++++ 4 files changed, 38 insertions(+), 3 deletions(-) diff --git a/add-interactive.c b/add-interactive.c index 97ff35b6f12a..eb3d0d3ada84 100644 --- a/add-interactive.c +++ b/add-interactive.c @@ -41,6 +41,8 @@ void init_add_i_state(struct add_i_state *s, struct repository *r) const char *value; s->r = r; + s->context = -1; + s->interhunkcontext = -1; if (repo_config_get_value(r, "color.interactive", &value)) s->use_color = -1; @@ -78,6 +80,13 @@ void init_add_i_state(struct add_i_state *s, struct repository *r) repo_config_get_string(r, "diff.algorithm", &s->interactive_diff_algorithm); + if (!repo_config_get_int(r, "diff.context", &s->context)) + if (s->context < 0) + die(_("%s cannot be negative"), "diff.context"); + if (!repo_config_get_int(r, "diff.interHunkContext", &s->interhunkcontext)) + if (s->interhunkcontext < 0) + die(_("%s cannot be negative"), "diff.interHunkContext"); + repo_config_get_bool(r, "interactive.singlekey", &s->use_single_key); if (s->use_single_key) setbuf(stdin, NULL); diff --git a/add-interactive.h b/add-interactive.h index 693f125e8e4b..c63f35b14be8 100644 --- a/add-interactive.h +++ b/add-interactive.h @@ -18,6 +18,7 @@ struct add_i_state { int use_single_key; char *interactive_diff_filter, *interactive_diff_algorithm; + int context, interhunkcontext; }; void init_add_i_state(struct add_i_state *s, struct repository *r); diff --git a/add-patch.c b/add-patch.c index 95c67d8c80c4..b0125b51ba45 100644 --- a/add-patch.c +++ b/add-patch.c @@ -414,7 +414,6 @@ static int normalize_marker(const char *p) static int parse_diff(struct add_p_state *s, const struct pathspec *ps) { struct strvec args = STRVEC_INIT; - const char *diff_algorithm = s->s.interactive_diff_algorithm; struct strbuf *plain = &s->plain, *colored = NULL; struct child_process cp = CHILD_PROCESS_INIT; char *p, *pend, *colored_p = NULL, *colored_pend = NULL, marker = '\0'; @@ -424,8 +423,12 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps) int res; strvec_pushv(&args, s->mode->diff_cmd); - if (diff_algorithm) - strvec_pushf(&args, "--diff-algorithm=%s", diff_algorithm); + if (s->s.context != -1) + strvec_pushf(&args, "--unified=%i", s->s.context); + if (s->s.interhunkcontext != -1) + strvec_pushf(&args, "--inter-hunk-context=%i", s->s.interhunkcontext); + if (s->s.interactive_diff_algorithm) + strvec_pushf(&args, "--diff-algorithm=%s", s->s.interactive_diff_algorithm); if (s->revision) { struct object_id oid; strvec_push(&args, diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index b088ee141ff4..18dc329ea1f6 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -1230,4 +1230,26 @@ test_expect_success 'hunk splitting works with diff.suppressBlankEmpty' ' test_cmp expect actual ' +test_expect_success 'add -p respects diff.context' ' + test_write_lines a b c d e f g h i j k l m >file && + git add file && + test_write_lines a b c d e f G h i j k l m >file && + echo y | git -c diff.context=5 add -p >actual && + test_grep "@@ -2,11 +2,11 @@" actual +' + +test_expect_success 'add -p respects diff.interHunkContext' ' + test_write_lines a b c d e f g h i j k l m n o p q r s >file && + git add file && + test_write_lines a b c d E f g i i j k l m N o p q r s >file && + echo y | git -c diff.interhunkcontext=2 add -p >actual && + test_grep "@@ -2,16 +2,16 @@" actual +' + +test_expect_success 'add -p rejects negative diff.context' ' + test_config diff.context -1 && + test_must_fail git add -p 2>output && + test_grep "diff.context cannot be negative" output +' + test_done -- gitgitgadget ^ permalink raw reply related [flat|nested] 77+ messages in thread
* [PATCH v5 4/4] add-patch: add diff.context command line overrides 2025-07-29 7:01 ` [PATCH v5 " Leon Michalak via GitGitGadget ` (2 preceding siblings ...) 2025-07-29 7:01 ` [PATCH v5 3/4] add-patch: respect diff.context configuration Leon Michalak via GitGitGadget @ 2025-07-29 7:01 ` Leon Michalak via GitGitGadget 2025-07-29 15:21 ` [PATCH v5 0/4] Better support for customising context lines in --patch commands Phillip Wood 4 siblings, 0 replies; 77+ messages in thread From: Leon Michalak via GitGitGadget @ 2025-07-29 7:01 UTC (permalink / raw) To: git Cc: Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Phillip Wood, Leon Michalak, Leon Michalak From: Leon Michalak <leonmichalak6@gmail.com> This patch compliments the previous commit, where builtins that use add-patch infrastructure now respect diff.context and diff.interHunkContext file configurations. In particular, this patch helps users who don't want to set persistent context configurations or just want a way to override them on a one-time basis, by allowing the relevant builtins to accept corresponding command line options that override the file configurations. This mimics commands such as diff and log, which allow for both context file configuration and command line overrides. Signed-off-by: Leon Michalak <leonmichalak6@gmail.com> --- Documentation/diff-context-options.adoc | 10 +++++ Documentation/git-add.adoc | 2 + Documentation/git-checkout.adoc | 2 + Documentation/git-commit.adoc | 2 + Documentation/git-reset.adoc | 2 + Documentation/git-restore.adoc | 2 + Documentation/git-stash.adoc | 2 + add-interactive.c | 36 ++++++++++++---- add-interactive.h | 16 +++++-- add-patch.c | 5 ++- builtin/add.c | 21 ++++++++-- builtin/checkout.c | 31 ++++++++++++-- builtin/commit.c | 16 ++++++- builtin/reset.c | 17 +++++++- builtin/stash.c | 56 ++++++++++++++++++++----- commit.h | 3 +- parse-options.h | 2 + t/t3701-add-interactive.sh | 49 ++++++++++++++++++++++ t/t9902-completion.sh | 2 + 19 files changed, 241 insertions(+), 35 deletions(-) create mode 100644 Documentation/diff-context-options.adoc diff --git a/Documentation/diff-context-options.adoc b/Documentation/diff-context-options.adoc new file mode 100644 index 000000000000..e161260358ff --- /dev/null +++ b/Documentation/diff-context-options.adoc @@ -0,0 +1,10 @@ +`-U<n>`:: +`--unified=<n>`:: + Generate diffs with _<n>_ lines of context. Defaults to `diff.context` + or 3 if the config option is unset. + +`--inter-hunk-context=<n>`:: + Show the context between diff hunks, up to the specified _<number>_ + of lines, thereby fusing hunks that are close to each other. + Defaults to `diff.interHunkContext` or 0 if the config option + is unset. diff --git a/Documentation/git-add.adoc b/Documentation/git-add.adoc index eba0b419ce50..b7a735824d6c 100644 --- a/Documentation/git-add.adoc +++ b/Documentation/git-add.adoc @@ -104,6 +104,8 @@ This effectively runs `add --interactive`, but bypasses the initial command menu and directly jumps to the `patch` subcommand. See ``Interactive mode'' for details. +include::diff-context-options.adoc[] + `-e`:: `--edit`:: Open the diff vs. the index in an editor and let the user diff --git a/Documentation/git-checkout.adoc b/Documentation/git-checkout.adoc index ee83b6d9ba9a..40e02cfd6562 100644 --- a/Documentation/git-checkout.adoc +++ b/Documentation/git-checkout.adoc @@ -289,6 +289,8 @@ section of linkgit:git-add[1] to learn how to operate the `--patch` mode. Note that this option uses the no overlay mode by default (see also `--overlay`), and currently doesn't support overlay mode. +include::diff-context-options.adoc[] + `--ignore-other-worktrees`:: `git checkout` refuses when the wanted branch is already checked out or otherwise in use by another worktree. This option makes diff --git a/Documentation/git-commit.adoc b/Documentation/git-commit.adoc index dc219025f1eb..ae988a883b5b 100644 --- a/Documentation/git-commit.adoc +++ b/Documentation/git-commit.adoc @@ -76,6 +76,8 @@ OPTIONS which changes to commit. See linkgit:git-add[1] for details. +include::diff-context-options.adoc[] + `-C <commit>`:: `--reuse-message=<commit>`:: Take an existing _<commit>_ object, and reuse the log message diff --git a/Documentation/git-reset.adoc b/Documentation/git-reset.adoc index 53ab88c5451c..50e8a0ba6f66 100644 --- a/Documentation/git-reset.adoc +++ b/Documentation/git-reset.adoc @@ -125,6 +125,8 @@ OPTIONS separated with _NUL_ character and all other characters are taken literally (including newlines and quotes). +include::diff-context-options.adoc[] + `--`:: Do not interpret any more arguments as options. diff --git a/Documentation/git-restore.adoc b/Documentation/git-restore.adoc index 877b7772e667..1dcc2bb7aea3 100644 --- a/Documentation/git-restore.adoc +++ b/Documentation/git-restore.adoc @@ -52,6 +52,8 @@ leave out at most one of _<rev-A>__ and _<rev-B>_, in which case it defaults to Mode" section of linkgit:git-add[1] to learn how to operate the `--patch` mode. +include::diff-context-options.adoc[] + `-W`:: `--worktree`:: `-S`:: diff --git a/Documentation/git-stash.adoc b/Documentation/git-stash.adoc index 1a5177f4986c..0578c619c410 100644 --- a/Documentation/git-stash.adoc +++ b/Documentation/git-stash.adoc @@ -208,6 +208,8 @@ to learn how to operate the `--patch` mode. The `--patch` option implies `--keep-index`. You can use `--no-keep-index` to override this. +include::diff-context-options.adoc[] + -S:: --staged:: This option is only valid for `push` and `save` commands. diff --git a/add-interactive.c b/add-interactive.c index eb3d0d3ada84..3e692b47eca0 100644 --- a/add-interactive.c +++ b/add-interactive.c @@ -36,7 +36,8 @@ static void init_color(struct repository *r, struct add_i_state *s, free(key); } -void init_add_i_state(struct add_i_state *s, struct repository *r) +void init_add_i_state(struct add_i_state *s, struct repository *r, + struct add_p_opt *add_p_opt) { const char *value; @@ -90,6 +91,17 @@ void init_add_i_state(struct add_i_state *s, struct repository *r) repo_config_get_bool(r, "interactive.singlekey", &s->use_single_key); if (s->use_single_key) setbuf(stdin, NULL); + + if (add_p_opt->context != -1) { + if (add_p_opt->context < 0) + die(_("%s cannot be negative"), "--unified"); + s->context = add_p_opt->context; + } + if (add_p_opt->interhunkcontext != -1) { + if (add_p_opt->interhunkcontext < 0) + die(_("%s cannot be negative"), "--inter-hunk-context"); + s->interhunkcontext = add_p_opt->interhunkcontext; + } } void clear_add_i_state(struct add_i_state *s) @@ -978,6 +990,10 @@ static int run_patch(struct add_i_state *s, const struct pathspec *ps, opts->prompt = N_("Patch update"); count = list_and_choose(s, files, opts); if (count > 0) { + struct add_p_opt add_p_opt = { + .context = s->context, + .interhunkcontext = s->interhunkcontext, + }; struct strvec args = STRVEC_INIT; struct pathspec ps_selected = { 0 }; @@ -988,7 +1004,7 @@ static int run_patch(struct add_i_state *s, const struct pathspec *ps, parse_pathspec(&ps_selected, PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL, PATHSPEC_LITERAL_PATH, "", args.v); - res = run_add_p(s->r, ADD_P_ADD, NULL, &ps_selected); + res = run_add_p(s->r, ADD_P_ADD, &add_p_opt, NULL, &ps_selected); strvec_clear(&args); clear_pathspec(&ps_selected); } @@ -1023,10 +1039,13 @@ static int run_diff(struct add_i_state *s, const struct pathspec *ps, if (count > 0) { struct child_process cmd = CHILD_PROCESS_INIT; - strvec_pushl(&cmd.args, "git", "diff", "-p", "--cached", - oid_to_hex(!is_initial ? &oid : - s->r->hash_algo->empty_tree), - "--", NULL); + strvec_pushl(&cmd.args, "git", "diff", "-p", "--cached", NULL); + if (s->context != -1) + strvec_pushf(&cmd.args, "--unified=%i", s->context); + if (s->interhunkcontext != -1) + strvec_pushf(&cmd.args, "--inter-hunk-context=%i", s->interhunkcontext); + strvec_pushl(&cmd.args, oid_to_hex(!is_initial ? &oid : + s->r->hash_algo->empty_tree), "--", NULL); for (i = 0; i < files->items.nr; i++) if (files->selected[i]) strvec_push(&cmd.args, @@ -1119,7 +1138,8 @@ static void command_prompt_help(struct add_i_state *s) _("(empty) select nothing")); } -int run_add_i(struct repository *r, const struct pathspec *ps) +int run_add_i(struct repository *r, const struct pathspec *ps, + struct add_p_opt *add_p_opt) { struct add_i_state s = { NULL }; struct print_command_item_data data = { "[", "]" }; @@ -1162,7 +1182,7 @@ int run_add_i(struct repository *r, const struct pathspec *ps) ->util = util; } - init_add_i_state(&s, r); + init_add_i_state(&s, r, add_p_opt); /* * When color was asked for, use the prompt color for diff --git a/add-interactive.h b/add-interactive.h index c63f35b14be8..4213dcd67b9a 100644 --- a/add-interactive.h +++ b/add-interactive.h @@ -3,6 +3,13 @@ #include "color.h" +struct add_p_opt { + int context; + int interhunkcontext; +}; + +#define ADD_P_OPT_INIT { .context = -1, .interhunkcontext = -1 } + struct add_i_state { struct repository *r; int use_color; @@ -21,12 +28,14 @@ struct add_i_state { int context, interhunkcontext; }; -void init_add_i_state(struct add_i_state *s, struct repository *r); +void init_add_i_state(struct add_i_state *s, struct repository *r, + struct add_p_opt *add_p_opt); void clear_add_i_state(struct add_i_state *s); struct repository; struct pathspec; -int run_add_i(struct repository *r, const struct pathspec *ps); +int run_add_i(struct repository *r, const struct pathspec *ps, + struct add_p_opt *add_p_opt); enum add_p_mode { ADD_P_ADD, @@ -37,6 +46,7 @@ enum add_p_mode { }; int run_add_p(struct repository *r, enum add_p_mode mode, - const char *revision, const struct pathspec *ps); + struct add_p_opt *o, const char *revision, + const struct pathspec *ps); #endif diff --git a/add-patch.c b/add-patch.c index b0125b51ba45..302e6ba7d9a3 100644 --- a/add-patch.c +++ b/add-patch.c @@ -1763,14 +1763,15 @@ soft_increment: } int run_add_p(struct repository *r, enum add_p_mode mode, - const char *revision, const struct pathspec *ps) + struct add_p_opt *o, const char *revision, + const struct pathspec *ps) { struct add_p_state s = { { r }, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT }; size_t i, binary_count = 0; - init_add_i_state(&s.s, r); + init_add_i_state(&s.s, r, o); if (mode == ADD_P_STASH) s.mode = &patch_mode_stash; diff --git a/builtin/add.c b/builtin/add.c index 7c292ffdc6c2..a00dab9b8fa0 100644 --- a/builtin/add.c +++ b/builtin/add.c @@ -29,6 +29,7 @@ static const char * const builtin_add_usage[] = { NULL }; static int patch_interactive, add_interactive, edit_interactive; +static struct add_p_opt add_p_opt = ADD_P_OPT_INIT; static int take_worktree_changes; static int add_renormalize; static int pathspec_file_nul; @@ -157,7 +158,7 @@ static int refresh(struct repository *repo, int verbose, const struct pathspec * int interactive_add(struct repository *repo, const char **argv, const char *prefix, - int patch) + int patch, struct add_p_opt *add_p_opt) { struct pathspec pathspec; int ret; @@ -169,9 +170,9 @@ int interactive_add(struct repository *repo, prefix, argv); if (patch) - ret = !!run_add_p(repo, ADD_P_ADD, NULL, &pathspec); + ret = !!run_add_p(repo, ADD_P_ADD, add_p_opt, NULL, &pathspec); else - ret = !!run_add_i(repo, &pathspec); + ret = !!run_add_i(repo, &pathspec, add_p_opt); clear_pathspec(&pathspec); return ret; @@ -253,6 +254,8 @@ static struct option builtin_add_options[] = { OPT_GROUP(""), OPT_BOOL('i', "interactive", &add_interactive, N_("interactive picking")), OPT_BOOL('p', "patch", &patch_interactive, N_("select hunks interactively")), + OPT_DIFF_UNIFIED(&add_p_opt.context), + OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext), OPT_BOOL('e', "edit", &edit_interactive, N_("edit current diff and apply")), OPT__FORCE(&ignored_too, N_("allow adding otherwise ignored files"), 0), OPT_BOOL('u', "update", &take_worktree_changes, N_("update tracked files")), @@ -394,6 +397,11 @@ int cmd_add(int argc, prepare_repo_settings(repo); repo->settings.command_requires_full_index = 0; + if (add_p_opt.context < -1) + die(_("'%s' cannot be negative"), "--unified"); + if (add_p_opt.interhunkcontext < -1) + die(_("'%s' cannot be negative"), "--inter-hunk-context"); + if (patch_interactive) add_interactive = 1; if (add_interactive) { @@ -401,7 +409,12 @@ int cmd_add(int argc, die(_("options '%s' and '%s' cannot be used together"), "--dry-run", "--interactive/--patch"); if (pathspec_from_file) die(_("options '%s' and '%s' cannot be used together"), "--pathspec-from-file", "--interactive/--patch"); - exit(interactive_add(repo, argv + 1, prefix, patch_interactive)); + exit(interactive_add(repo, argv + 1, prefix, patch_interactive, &add_p_opt)); + } else { + if (add_p_opt.context != -1) + die(_("the option '%s' requires '%s'"), "--unified", "--interactive/--patch"); + if (add_p_opt.interhunkcontext != -1) + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--interactive/--patch"); } if (edit_interactive) { diff --git a/builtin/checkout.c b/builtin/checkout.c index 536192d3456c..3737ba4c3920 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -61,6 +61,8 @@ static const char * const restore_usage[] = { struct checkout_opts { int patch_mode; + int patch_context; + int patch_interhunk_context; int quiet; int merge; int force; @@ -104,7 +106,12 @@ struct checkout_opts { struct tree *source_tree; }; -#define CHECKOUT_OPTS_INIT { .conflict_style = -1, .merge = -1 } +#define CHECKOUT_OPTS_INIT { \ + .conflict_style = -1, \ + .merge = -1, \ + .patch_context = -1, \ + .patch_interhunk_context = -1, \ +} struct branch_info { char *name; /* The short name used */ @@ -539,6 +546,10 @@ static int checkout_paths(const struct checkout_opts *opts, if (opts->patch_mode) { enum add_p_mode patch_mode; + struct add_p_opt add_p_opt = { + .context = opts->patch_context, + .interhunkcontext = opts->patch_interhunk_context, + }; const char *rev = new_branch_info->name; char rev_oid[GIT_MAX_HEXSZ + 1]; @@ -564,8 +575,8 @@ static int checkout_paths(const struct checkout_opts *opts, else BUG("either flag must have been set, worktree=%d, index=%d", opts->checkout_worktree, opts->checkout_index); - return !!run_add_p(the_repository, patch_mode, rev, - &opts->pathspec); + return !!run_add_p(the_repository, patch_mode, &add_p_opt, + rev, &opts->pathspec); } repo_hold_locked_index(the_repository, &lock_file, LOCK_DIE_ON_ERROR); @@ -1738,6 +1749,8 @@ static struct option *add_checkout_path_options(struct checkout_opts *opts, N_("checkout their version for unmerged files"), 3, PARSE_OPT_NONEG), OPT_BOOL('p', "patch", &opts->patch_mode, N_("select hunks interactively")), + OPT_DIFF_UNIFIED(&opts->patch_context), + OPT_DIFF_INTERHUNK_CONTEXT(&opts->patch_interhunk_context), 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), @@ -1780,6 +1793,18 @@ static int checkout_main(int argc, const char **argv, const char *prefix, argc = parse_options(argc, argv, prefix, options, usagestr, parseopt_flags); + if (opts->patch_context < -1) + die(_("'%s' cannot be negative"), "--unified"); + if (opts->patch_interhunk_context < -1) + die(_("'%s' cannot be negative"), "--inter-hunk-context"); + + if (!opts->patch_mode) { + if (opts->patch_context != -1) + die(_("the option '%s' requires '%s'"), "--unified", "--patch"); + if (opts->patch_interhunk_context != -1) + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch"); + } + if (opts->show_progress < 0) { if (opts->quiet) opts->show_progress = 0; diff --git a/builtin/commit.c b/builtin/commit.c index fba0dded64a7..73673bc7db9f 100644 --- a/builtin/commit.c +++ b/builtin/commit.c @@ -19,6 +19,7 @@ #include "environment.h" #include "diff.h" #include "commit.h" +#include "add-interactive.h" #include "gettext.h" #include "revision.h" #include "wt-status.h" @@ -122,6 +123,7 @@ static const char *edit_message, *use_message; static char *fixup_message, *fixup_commit, *squash_message; static const char *fixup_prefix; static int all, also, interactive, patch_interactive, only, amend, signoff; +static struct add_p_opt add_p_opt = ADD_P_OPT_INIT; 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 */ @@ -354,6 +356,11 @@ static const char *prepare_index(const char **argv, const char *prefix, const char *ret; char *path = NULL; + if (add_p_opt.context < -1) + die(_("'%s' cannot be negative"), "--unified"); + if (add_p_opt.interhunkcontext < -1) + die(_("'%s' cannot be negative"), "--inter-hunk-context"); + if (is_status) refresh_flags |= REFRESH_UNMERGED; parse_pathspec(&pathspec, 0, @@ -400,7 +407,7 @@ static const char *prepare_index(const char **argv, const char *prefix, old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT)); setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1); - if (interactive_add(the_repository, argv, prefix, patch_interactive) != 0) + if (interactive_add(the_repository, argv, prefix, patch_interactive, &add_p_opt) != 0) die(_("interactive add failed")); the_repository->index_file = old_repo_index_file; @@ -424,6 +431,11 @@ static const char *prepare_index(const char **argv, const char *prefix, commit_style = COMMIT_NORMAL; ret = get_lock_file_path(&index_lock); goto out; + } else { + if (add_p_opt.context != -1) + die(_("the option '%s' requires '%s'"), "--unified", "--interactive/--patch"); + if (add_p_opt.interhunkcontext != -1) + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--interactive/--patch"); } /* @@ -1722,6 +1734,8 @@ int cmd_commit(int argc, OPT_BOOL('i', "include", &also, N_("add specified files to index for commit")), OPT_BOOL(0, "interactive", &interactive, N_("interactively add files")), OPT_BOOL('p', "patch", &patch_interactive, N_("interactively add changes")), + OPT_DIFF_UNIFIED(&add_p_opt.context), + OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext), OPT_BOOL('o', "only", &only, N_("commit only specified files")), OPT_BOOL('n', "no-verify", &no_verify, N_("bypass pre-commit and commit-msg hooks")), OPT_BOOL(0, "dry-run", &dry_run, N_("show what would be committed")), diff --git a/builtin/reset.c b/builtin/reset.c index dc50ffc1ac59..9fb32795c9c5 100644 --- a/builtin/reset.c +++ b/builtin/reset.c @@ -346,6 +346,7 @@ int cmd_reset(int argc, struct object_id oid; struct pathspec pathspec; int intent_to_add = 0; + struct add_p_opt add_p_opt = ADD_P_OPT_INIT; const struct option options[] = { OPT__QUIET(&quiet, N_("be quiet, only report errors")), OPT_BOOL(0, "no-refresh", &no_refresh, @@ -370,6 +371,8 @@ int cmd_reset(int argc, PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater), OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")), + OPT_DIFF_UNIFIED(&add_p_opt.context), + OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext), 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), @@ -420,6 +423,11 @@ int cmd_reset(int argc, oidcpy(&oid, &tree->object.oid); } + if (add_p_opt.context < -1) + die(_("'%s' cannot be negative"), "--unified"); + if (add_p_opt.interhunkcontext < -1) + die(_("'%s' cannot be negative"), "--inter-hunk-context"); + prepare_repo_settings(the_repository); the_repository->settings.command_requires_full_index = 0; @@ -427,9 +435,14 @@ int cmd_reset(int argc, if (reset_type != NONE) die(_("options '%s' and '%s' cannot be used together"), "--patch", "--{hard,mixed,soft}"); trace2_cmd_mode("patch-interactive"); - update_ref_status = !!run_add_p(the_repository, ADD_P_RESET, rev, - &pathspec); + update_ref_status = !!run_add_p(the_repository, ADD_P_RESET, + &add_p_opt, rev, &pathspec); goto cleanup; + } else { + if (add_p_opt.context != -1) + die(_("the option '%s' requires '%s'"), "--unified", "--patch"); + if (add_p_opt.interhunkcontext != -1) + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch"); } /* git reset tree [--] paths... can be used to diff --git a/builtin/stash.c b/builtin/stash.c index 7cd3ad8aa48e..6da162e5e69a 100644 --- a/builtin/stash.c +++ b/builtin/stash.c @@ -1242,7 +1242,8 @@ done: } static int stash_patch(struct stash_info *info, const struct pathspec *ps, - struct strbuf *out_patch, int quiet) + struct strbuf *out_patch, int quiet, + struct add_p_opt *add_p_opt) { int ret = 0; struct child_process cp_read_tree = CHILD_PROCESS_INIT; @@ -1267,7 +1268,7 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps, old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT)); setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1); - ret = !!run_add_p(the_repository, ADD_P_STASH, NULL, ps); + ret = !!run_add_p(the_repository, ADD_P_STASH, add_p_opt, NULL, ps); the_repository->index_file = old_repo_index_file; if (old_index_env && *old_index_env) @@ -1362,8 +1363,8 @@ done: } static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_buf, - int include_untracked, int patch_mode, int only_staged, - struct stash_info *info, struct strbuf *patch, + int include_untracked, int patch_mode, struct add_p_opt *add_p_opt, + int only_staged, struct stash_info *info, struct strbuf *patch, int quiet) { int ret = 0; @@ -1444,7 +1445,7 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b untracked_commit_option = 1; } if (patch_mode) { - ret = stash_patch(info, ps, patch, quiet); + ret = stash_patch(info, ps, patch, quiet, add_p_opt); if (ret < 0) { if (!quiet) fprintf_ln(stderr, _("Cannot save the current " @@ -1519,7 +1520,7 @@ static int create_stash(int argc, const char **argv, const char *prefix UNUSED, if (!check_changes_tracked_files(&ps)) return 0; - ret = do_create_stash(&ps, &stash_msg_buf, 0, 0, 0, &info, + ret = do_create_stash(&ps, &stash_msg_buf, 0, 0, NULL, 0, &info, NULL, 0); if (!ret) printf_ln("%s", oid_to_hex(&info.w_commit)); @@ -1530,7 +1531,8 @@ static int create_stash(int argc, const char **argv, const char *prefix UNUSED, } static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int quiet, - int keep_index, int patch_mode, int include_untracked, int only_staged) + int keep_index, int patch_mode, struct add_p_opt *add_p_opt, + int include_untracked, int only_staged) { int ret = 0; struct stash_info info = STASH_INFO_INIT; @@ -1600,8 +1602,8 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q if (stash_msg) strbuf_addstr(&stash_msg_buf, stash_msg); - if (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode, only_staged, - &info, &patch, quiet)) { + if (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode, + add_p_opt, only_staged, &info, &patch, quiet)) { ret = -1; goto done; } @@ -1774,6 +1776,7 @@ static int push_stash(int argc, const char **argv, const char *prefix, const char *stash_msg = NULL; char *pathspec_from_file = NULL; struct pathspec ps; + struct add_p_opt add_p_opt = ADD_P_OPT_INIT; struct option options[] = { OPT_BOOL('k', "keep-index", &keep_index, N_("keep index")), @@ -1781,6 +1784,8 @@ static int push_stash(int argc, const char **argv, const char *prefix, N_("stash staged changes only")), OPT_BOOL('p', "patch", &patch_mode, N_("stash in patch mode")), + OPT_DIFF_UNIFIED(&add_p_opt.context), + OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext), OPT__QUIET(&quiet, N_("quiet mode")), OPT_BOOL('u', "include-untracked", &include_untracked, N_("include untracked files in stash")), @@ -1836,8 +1841,20 @@ static int push_stash(int argc, const char **argv, const char *prefix, die(_("the option '%s' requires '%s'"), "--pathspec-file-nul", "--pathspec-from-file"); } + if (!patch_mode) { + if (add_p_opt.context != -1) + die(_("the option '%s' requires '%s'"), "--unified", "--patch"); + if (add_p_opt.interhunkcontext != -1) + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch"); + } + + if (add_p_opt.context < -1) + die(_("'%s' cannot be negative"), "--unified"); + if (add_p_opt.interhunkcontext < -1) + die(_("'%s' cannot be negative"), "--inter-hunk-context"); + ret = do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode, - include_untracked, only_staged); + &add_p_opt, include_untracked, only_staged); clear_pathspec(&ps); free(pathspec_from_file); @@ -1862,6 +1879,7 @@ static int save_stash(int argc, const char **argv, const char *prefix, const char *stash_msg = NULL; struct pathspec ps; struct strbuf stash_msg_buf = STRBUF_INIT; + struct add_p_opt add_p_opt = ADD_P_OPT_INIT; struct option options[] = { OPT_BOOL('k', "keep-index", &keep_index, N_("keep index")), @@ -1869,6 +1887,8 @@ static int save_stash(int argc, const char **argv, const char *prefix, N_("stash staged changes only")), OPT_BOOL('p', "patch", &patch_mode, N_("stash in patch mode")), + OPT_DIFF_UNIFIED(&add_p_opt.context), + OPT_DIFF_INTERHUNK_CONTEXT(&add_p_opt.interhunkcontext), OPT__QUIET(&quiet, N_("quiet mode")), OPT_BOOL('u', "include-untracked", &include_untracked, N_("include untracked files in stash")), @@ -1887,8 +1907,22 @@ static int save_stash(int argc, const char **argv, const char *prefix, stash_msg = strbuf_join_argv(&stash_msg_buf, argc, argv, ' '); memset(&ps, 0, sizeof(ps)); + + if (add_p_opt.context < -1) + die(_("'%s' cannot be negative"), "--unified"); + if (add_p_opt.interhunkcontext < -1) + die(_("'%s' cannot be negative"), "--inter-hunk-context"); + + if (!patch_mode) { + if (add_p_opt.context != -1) + die(_("the option '%s' requires '%s'"), "--unified", "--patch"); + if (add_p_opt.interhunkcontext != -1) + die(_("the option '%s' requires '%s'"), "--inter-hunk-context", "--patch"); + } + ret = do_push_stash(&ps, stash_msg, quiet, keep_index, - patch_mode, include_untracked, only_staged); + patch_mode, &add_p_opt, include_untracked, + only_staged); strbuf_release(&stash_msg_buf); return ret; diff --git a/commit.h b/commit.h index 70c870dae4d4..7a7fedbc2f14 100644 --- a/commit.h +++ b/commit.h @@ -2,6 +2,7 @@ #define COMMIT_H #include "object.h" +#include "add-interactive.h" struct signature_check; struct strbuf; @@ -257,7 +258,7 @@ int for_each_commit_graft(each_commit_graft_fn, void *); int interactive_add(struct repository *repo, const char **argv, const char *prefix, - int patch); + int patch, struct add_p_opt *add_p_opt); struct commit_extra_header { struct commit_extra_header *next; diff --git a/parse-options.h b/parse-options.h index 91c3e3c29b3d..bdae8f116198 100644 --- a/parse-options.h +++ b/parse-options.h @@ -616,6 +616,8 @@ int parse_opt_tracking_mode(const struct option *, const char *, int); #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")) #define OPT_AUTOSTASH(v) OPT_BOOL(0, "autostash", v, N_("automatically stash/stash pop before and after")) +#define OPT_DIFF_UNIFIED(v) OPT_INTEGER_F('U', "unified", v, N_("generate diffs with <n> lines context"), PARSE_OPT_NONEG) +#define OPT_DIFF_INTERHUNK_CONTEXT(v) OPT_INTEGER_F(0, "inter-hunk-context", v, N_("show context between diff hunks up to the specified number of lines"), PARSE_OPT_NONEG) #define OPT_IPVERSION(v) \ OPT_SET_INT_F('4', "ipv4", (v), N_("use IPv4 addresses only"), \ diff --git a/t/t3701-add-interactive.sh b/t/t3701-add-interactive.sh index 18dc329ea1f6..04d2a1983525 100755 --- a/t/t3701-add-interactive.sh +++ b/t/t3701-add-interactive.sh @@ -1252,4 +1252,53 @@ test_expect_success 'add -p rejects negative diff.context' ' test_grep "diff.context cannot be negative" output ' +for cmd in add checkout restore 'commit -m file' +do + test_expect_success "${cmd%% *} accepts -U and --inter-hunk-context" ' + test_write_lines a b c d e f g h i j k l m n o p q r s t u v >file && + git add file && + test_write_lines a b c d e F g h i j k l m n o p Q r s t u v >file && + echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \ + $cmd -p -U 4 --inter-hunk-context 2 >actual && + test_grep "@@ -2,20 +2,20 @@" actual + ' +done + +test_expect_success 'reset accepts -U and --inter-hunk-context' ' + test_write_lines a b c d e f g h i j k l m n o p q r s t u v >file && + git commit -m file file && + test_write_lines a b c d e F g h i j k l m n o p Q r s t u v >file && + git add file && + echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \ + reset -p -U 4 --inter-hunk-context 2 >actual && + test_grep "@@ -2,20 +2,20 @@" actual +' + +test_expect_success 'stash accepts -U and --inter-hunk-context' ' + test_write_lines a b c d e F g h i j k l m n o p Q r s t u v >file && + git commit -m file file && + test_write_lines a b c d e f g h i j k l m n o p q r s t u v >file && + echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \ + stash -p -U 4 --inter-hunk-context 2 >actual && + test_grep "@@ -2,20 +2,20 @@" actual +' + +for cmd in add checkout commit reset restore "stash save" "stash push" +do + test_expect_success "$cmd rejects invalid context options" ' + test_must_fail git $cmd -p -U -3 2>actual && + cat actual | echo && + test_grep -e ".--unified. cannot be negative" actual && + + test_must_fail git $cmd -p --inter-hunk-context -3 2>actual && + test_grep -e ".--inter-hunk-context. cannot be negative" actual && + + test_must_fail git $cmd -U 7 2>actual && + test_grep -E ".--unified. requires .(--interactive/)?--patch." actual && + + test_must_fail git $cmd --inter-hunk-context 2 2>actual && + test_grep -E ".--inter-hunk-context. requires .(--interactive/)?--patch." actual + ' +done + test_done diff --git a/t/t9902-completion.sh b/t/t9902-completion.sh index 343b8cd1912b..6650d33fba69 100755 --- a/t/t9902-completion.sh +++ b/t/t9902-completion.sh @@ -2596,6 +2596,8 @@ test_expect_success 'double dash "git checkout"' ' --merge Z --conflict=Z --patch Z + --unified=Z + --inter-hunk-context=Z --ignore-skip-worktree-bits Z --ignore-other-worktrees Z --recurse-submodules Z -- gitgitgadget ^ permalink raw reply related [flat|nested] 77+ messages in thread
* Re: [PATCH v5 0/4] Better support for customising context lines in --patch commands 2025-07-29 7:01 ` [PATCH v5 " Leon Michalak via GitGitGadget ` (3 preceding siblings ...) 2025-07-29 7:01 ` [PATCH v5 4/4] add-patch: add diff.context command line overrides Leon Michalak via GitGitGadget @ 2025-07-29 15:21 ` Phillip Wood 2025-07-29 15:55 ` Junio C Hamano 4 siblings, 1 reply; 77+ messages in thread From: Phillip Wood @ 2025-07-29 15:21 UTC (permalink / raw) To: Leon Michalak via GitGitGadget, git Cc: Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Leon Michalak Hi Leon The range-diff below looks good to me. Thanks for working on this Phillip On 29/07/2025 08:01, Leon Michalak via GitGitGadget wrote: > This series of patches attempt to give --interactive/--patch compatible > builtins ("add", "commit", "checkout", "reset", "restore" and "stash") > better support and nicer experience for configuring how many context lines > are shown in diffs through a variety of ways. > > Prior to these patches, the user could not choose how many context lines > they saw in --patch commands (apart from one workaround by using > GIT_DIFF_OPTS=-u<number> ..., however this isn't a good user experience or a > persistent solution). Additionally, the behaviour around reading from the > diff.context and diff.interHunkContext configs was also inconsistent with > other diff generating commands such as "log -p". > > The summarised changes below hopefully make this experience better and fix > some inconsistencies: > > * diff.context and diff.interHunkContext configs are now respected by > --patch compatible commands > * --unified and --inter-hunk-context command line options have been added > to --patch compatible commands (which take prescendence over file > configs) > * "add" and "commit" in --interactive mode now expose a new "context" > subcommand which configures the amount of context lines you wish to see > in subsequent diffs generated from other subcommands such as "patch" or > "diff" > > The original discussion for this can be read at: > > * https://lore.kernel.org/git/CAP9jKjGb-Rcr=RLJEzeFdtrekYM+qmHy+1T1fykU3n9cV4GhGw@mail.gmail.com/ > > Changes since v1: > > * Update commit descriptions > * Update tests to use the more modern and robust test_grep and test_config > utils > * Reword some documentation / user messages > * Ensure each commit is atomic and builds/passes tests on it's own > * Make new command line options DRY > * Add tests for interhunk context interaction > * Error if context config/command line options are negative > * Drop previous last commit to do with new subcommand for --interactive > add/commit. My motivations behind this patch series originally where > quite simple, just for add-patch commands to respect context configs. > This subcommand, after the discussion in v1, will require more thought > and a larger implementation that what I had anticipated. I would prefer > to leave this for another time as it's the least impactful but the most > time intensive and complicated idea. > > Changes since v2: > > * Update tests to only test single command (following Philip's suggestion) > * Add negative option checks > * Minor commit re-wording > > Changes since v3: > > * Update commit descriptions > * Read struct properties directly instead of assigning to variables first > * Simplify config setting / error checking > * Remove redundant tests in later commit as they were replaced with better > test(s) > * Change tests to use single quotes (this messes with the grep so was > unable to explicitly test single quotes in the error messages, so decided > to use regex . instead, which is what some other tests that have this > problem seem to use as well) > > Changes since v4: > > * Add back tests to maintain good coverage and remove redundant tests > > Leon Michalak (4): > t: use test_grep in t3701 and t4055 > t: use test_config in t4055 > add-patch: respect diff.context configuration > add-patch: add diff.context command line overrides > > Documentation/diff-context-options.adoc | 10 ++ > Documentation/git-add.adoc | 2 + > Documentation/git-checkout.adoc | 2 + > Documentation/git-commit.adoc | 2 + > Documentation/git-reset.adoc | 2 + > Documentation/git-restore.adoc | 2 + > Documentation/git-stash.adoc | 2 + > add-interactive.c | 45 +++++++-- > add-interactive.h | 17 +++- > add-patch.c | 14 ++- > builtin/add.c | 21 ++++- > builtin/checkout.c | 31 +++++- > builtin/commit.c | 16 +++- > builtin/reset.c | 17 +++- > builtin/stash.c | 56 ++++++++--- > commit.h | 3 +- > parse-options.h | 2 + > t/t3701-add-interactive.sh | 119 +++++++++++++++++++----- > t/t4055-diff-context.sh | 42 ++++----- > t/t9902-completion.sh | 2 + > 20 files changed, 324 insertions(+), 83 deletions(-) > create mode 100644 Documentation/diff-context-options.adoc > > > base-commit: cf6f63ea6bf35173e02e18bdc6a4ba41288acff9 > Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-1915%2FNinjaInShade%2Finteractive-patch-context-v5 > Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1915/NinjaInShade/interactive-patch-context-v5 > Pull-Request: https://github.com/gitgitgadget/git/pull/1915 > > Range-diff vs v4: > > 1: bbb2bc7082b = 1: bbb2bc7082b t: use test_grep in t3701 and t4055 > 2: feace2d3676 = 2: feace2d3676 t: use test_config in t4055 > 3: 994029d6602 = 3: 994029d6602 add-patch: respect diff.context configuration > 4: 2774b930406 ! 4: 9731e5b76fb add-patch: add diff.context command line overrides > @@ parse-options.h: int parse_opt_tracking_mode(const struct option *, const char * > OPT_SET_INT_F('4', "ipv4", (v), N_("use IPv4 addresses only"), \ > > ## t/t3701-add-interactive.sh ## > -@@ t/t3701-add-interactive.sh: test_expect_success 'hunk splitting works with diff.suppressBlankEmpty' ' > - test_cmp expect actual > +@@ t/t3701-add-interactive.sh: test_expect_success 'add -p rejects negative diff.context' ' > + test_grep "diff.context cannot be negative" output > ' > > --test_expect_success 'add -p respects diff.context' ' > -- test_write_lines a b c d e f g h i j k l m >file && > +for cmd in add checkout restore 'commit -m file' > +do > + test_expect_success "${cmd%% *} accepts -U and --inter-hunk-context" ' > @@ t/t3701-add-interactive.sh: test_expect_success 'hunk splitting works with diff. > + test_write_lines a b c d e f g h i j k l m n o p q r s t u v >file && > + git commit -m file file && > + test_write_lines a b c d e F g h i j k l m n o p Q r s t u v >file && > - git add file && > -- test_write_lines a b c d e f G h i j k l m >file && > -- echo y | git -c diff.context=5 add -p >actual && > -- test_grep "@@ -2,11 +2,11 @@" actual > ++ git add file && > + echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \ > + reset -p -U 4 --inter-hunk-context 2 >actual && > + test_grep "@@ -2,20 +2,20 @@" actual > - ' > - > --test_expect_success 'add -p respects diff.interHunkContext' ' > -- test_write_lines a b c d e f g h i j k l m n o p q r s >file && > -- git add file && > -- test_write_lines a b c d E f g i i j k l m N o p q r s >file && > -- echo y | git -c diff.interhunkcontext=2 add -p >actual && > -- test_grep "@@ -2,16 +2,16 @@" actual > ++' > ++ > +test_expect_success 'stash accepts -U and --inter-hunk-context' ' > + test_write_lines a b c d e F g h i j k l m n o p Q r s t u v >file && > + git commit -m file file && > @@ t/t3701-add-interactive.sh: test_expect_success 'hunk splitting works with diff. > + echo y | git -c diff.context=5 -c diff.interhunkcontext=1 \ > + stash -p -U 4 --inter-hunk-context 2 >actual && > + test_grep "@@ -2,20 +2,20 @@" actual > - ' > - > --test_expect_success 'add -p rejects negative diff.context' ' > -- test_config diff.context -1 && > -- test_must_fail git add -p 2>output && > -- test_grep "diff.context cannot be negative" output > --' > ++' > ++ > +for cmd in add checkout commit reset restore "stash save" "stash push" > +do > + test_expect_success "$cmd rejects invalid context options" ' > @@ t/t3701-add-interactive.sh: test_expect_success 'hunk splitting works with diff. > + test_grep -E ".--inter-hunk-context. requires .(--interactive/)?--patch." actual > + ' > +done > - > - test_done > - > - ## t/t4055-diff-context.sh ## > -@@ t/t4055-diff-context.sh: test_expect_success 'The -U option overrides diff.context' ' > - test_grep ! "^ firstline" output > - ' > - > -+test_expect_success 'The -U option overrides diff.context for "add"' ' > -+ test_config diff.context 8 && > -+ git add -U4 -p >output && > -+ test_grep ! "^ firstline" output > -+' > -+ > -+test_expect_success 'The -U option overrides diff.context for "commit"' ' > -+ test_config diff.context 8 && > -+ ! git commit -U4 -p >output && > -+ test_grep ! "^ firstline" output > -+' > -+ > -+test_expect_success 'The -U option overrides diff.context for "checkout"' ' > -+ test_config diff.context 8 && > -+ git checkout -U4 -p >output && > -+ test_grep ! "^ firstline" output > -+' > + > -+test_expect_success 'The -U option overrides diff.context for "stash"' ' > -+ test_config diff.context 8 && > -+ ! git stash -U4 -p >output && > -+ test_grep ! "^ firstline" output > -+' > -+ > -+test_expect_success 'The -U option overrides diff.context for "restore"' ' > -+ test_config diff.context 8 && > -+ git restore -U4 -p >output && > -+ test_grep ! "^ firstline" output > -+' > -+ > - test_expect_success 'diff.context honored by "diff"' ' > - test_config diff.context 8 && > - git diff >output && > + test_done > > ## t/t9902-completion.sh ## > @@ t/t9902-completion.sh: test_expect_success 'double dash "git checkout"' ' > ^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v5 0/4] Better support for customising context lines in --patch commands 2025-07-29 15:21 ` [PATCH v5 0/4] Better support for customising context lines in --patch commands Phillip Wood @ 2025-07-29 15:55 ` Junio C Hamano 2025-07-29 16:18 ` Leon Michalak 0 siblings, 1 reply; 77+ messages in thread From: Junio C Hamano @ 2025-07-29 15:55 UTC (permalink / raw) To: Phillip Wood Cc: Leon Michalak via GitGitGadget, git, Kristoffer Haugsbakk, Eric Sunshine, Christian Couder, Leon Michalak Phillip Wood <phillip.wood123@gmail.com> writes: > Hi Leon > > The range-diff below looks good to me. Thanks for working on this > > Phillip Yeah, the new one matches the previous one plus your fixes to the tests, which I agree with fully. Will queue. Let's mark the topic for 'next'. Thanks, both of you. ^ permalink raw reply [flat|nested] 77+ messages in thread
* Re: [PATCH v5 0/4] Better support for customising context lines in --patch commands 2025-07-29 15:55 ` Junio C Hamano @ 2025-07-29 16:18 ` Leon Michalak 0 siblings, 0 replies; 77+ messages in thread From: Leon Michalak @ 2025-07-29 16:18 UTC (permalink / raw) To: Junio C Hamano Cc: Phillip Wood, Leon Michalak via GitGitGadget, git, Kristoffer Haugsbakk, Eric Sunshine, Christian Couder Great! Thanks Philip, Junio and everyone else who helped me get this finished and in. Was a good experience :) ^ permalink raw reply [flat|nested] 77+ messages in thread
end of thread, other threads:[~2025-07-29 16:18 UTC | newest] Thread overview: 77+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-05-05 9:18 [PATCH 0/3] Better support for customising context lines in --patch commands Leon Michalak via GitGitGadget 2025-05-05 9:18 ` [PATCH 1/3] add-patch: respect diff.context configuration Leon Michalak via GitGitGadget 2025-05-05 20:29 ` Eric Sunshine 2025-05-06 8:55 ` Phillip Wood 2025-05-06 17:29 ` Junio C Hamano 2025-05-05 9:18 ` [PATCH 2/3] add-patch: add diff.context command line overrides Leon Michalak via GitGitGadget 2025-05-05 9:49 ` Kristoffer Haugsbakk [not found] ` <CAP9jKjHj7WP91aKA9SE94zYj+naBGLUs99mF3G4BhTGcGjFDUQ@mail.gmail.com> 2025-05-05 10:11 ` Leon Michalak 2025-05-07 9:51 ` Phillip Wood 2025-05-07 18:07 ` Junio C Hamano 2025-05-07 18:28 ` Leon Michalak 2025-05-07 20:25 ` Junio C Hamano 2025-05-05 9:18 ` [PATCH 3/3] add-interactive: add new "context" subcommand Leon Michalak via GitGitGadget 2025-05-06 0:02 ` Eric Sunshine 2025-05-06 7:20 ` Leon Michalak 2025-05-06 8:28 ` Christian Couder 2025-05-06 17:07 ` Leon Michalak 2025-05-06 16:37 ` Junio C Hamano 2025-05-06 17:25 ` Leon Michalak 2025-05-07 13:30 ` Phillip Wood 2025-05-07 19:10 ` Junio C Hamano 2025-05-10 13:46 ` [PATCH v2 0/4] Better support for customising context lines in --patch commands Leon Michalak via GitGitGadget 2025-05-10 13:46 ` [PATCH v2 1/4] test: refactor to use "test_grep" Leon Michalak via GitGitGadget 2025-05-12 13:42 ` Junio C Hamano 2025-05-12 16:58 ` Leon Michalak 2025-05-10 13:46 ` [PATCH v2 2/4] test: refactor to use "test_config" Leon Michalak via GitGitGadget 2025-05-10 13:46 ` [PATCH v2 3/4] add-patch: respect diff.context configuration Leon Michalak via GitGitGadget 2025-05-13 13:52 ` Phillip Wood 2025-05-13 15:47 ` Junio C Hamano 2025-05-14 15:13 ` Phillip Wood 2025-05-15 12:58 ` Junio C Hamano 2025-05-10 13:46 ` [PATCH v2 4/4] add-patch: add diff.context command line overrides Leon Michalak via GitGitGadget 2025-05-12 16:45 ` Junio C Hamano 2025-05-12 17:03 ` Leon Michalak 2025-05-13 13:52 ` Phillip Wood 2025-05-13 14:39 ` Phillip Wood 2025-05-13 15:05 ` Leon Michalak 2025-05-14 15:13 ` phillip.wood123 2025-06-28 16:34 ` [PATCH v3 0/4] Better support for customising context lines in --patch commands Leon Michalak via GitGitGadget 2025-06-28 16:34 ` [PATCH v3 1/4] test: use "test_grep" Leon Michalak via GitGitGadget 2025-06-30 16:23 ` Junio C Hamano 2025-06-28 16:34 ` [PATCH v3 2/4] test: use "test_config" Leon Michalak via GitGitGadget 2025-06-30 16:35 ` Junio C Hamano 2025-06-28 16:34 ` [PATCH v3 3/4] add-patch: respect diff.context configuration Leon Michalak via GitGitGadget 2025-06-30 16:55 ` Junio C Hamano 2025-07-01 10:00 ` Phillip Wood 2025-06-28 16:34 ` [PATCH v3 4/4] add-patch: add diff.context command line overrides Leon Michalak via GitGitGadget 2025-06-30 17:03 ` Junio C Hamano 2025-07-01 9:59 ` Phillip Wood 2025-07-01 15:54 ` Junio C Hamano 2025-07-02 14:07 ` Phillip Wood 2025-07-02 18:28 ` Junio C Hamano 2025-07-01 9:59 ` Phillip Wood 2025-06-30 16:16 ` [PATCH v3 0/4] Better support for customising context lines in --patch commands Junio C Hamano 2025-07-09 0:09 ` Junio C Hamano 2025-07-09 7:57 ` Leon Michalak 2025-07-09 15:32 ` Junio C Hamano 2025-07-19 12:28 ` [PATCH v4 " Leon Michalak via GitGitGadget 2025-07-19 12:28 ` [PATCH v4 1/4] t: use test_grep in t3701 and t4055 Leon Michalak via GitGitGadget 2025-07-19 12:28 ` [PATCH v4 2/4] t: use test_config in t4055 Leon Michalak via GitGitGadget 2025-07-19 12:28 ` [PATCH v4 3/4] add-patch: respect diff.context configuration Leon Michalak via GitGitGadget 2025-07-19 12:28 ` [PATCH v4 4/4] add-patch: add diff.context command line overrides Leon Michalak via GitGitGadget 2025-07-22 16:01 ` Phillip Wood 2025-07-22 18:02 ` Leon Michalak 2025-07-22 18:05 ` Leon Michalak 2025-07-23 9:41 ` Phillip Wood 2025-07-21 16:50 ` [PATCH v4 0/4] Better support for customising context lines in --patch commands Junio C Hamano 2025-07-22 16:05 ` Phillip Wood 2025-07-22 17:20 ` Junio C Hamano 2025-07-29 7:01 ` [PATCH v5 " Leon Michalak via GitGitGadget 2025-07-29 7:01 ` [PATCH v5 1/4] t: use test_grep in t3701 and t4055 Leon Michalak via GitGitGadget 2025-07-29 7:01 ` [PATCH v5 2/4] t: use test_config in t4055 Leon Michalak via GitGitGadget 2025-07-29 7:01 ` [PATCH v5 3/4] add-patch: respect diff.context configuration Leon Michalak via GitGitGadget 2025-07-29 7:01 ` [PATCH v5 4/4] add-patch: add diff.context command line overrides Leon Michalak via GitGitGadget 2025-07-29 15:21 ` [PATCH v5 0/4] Better support for customising context lines in --patch commands Phillip Wood 2025-07-29 15:55 ` Junio C Hamano 2025-07-29 16:18 ` Leon Michalak
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for NNTP newsgroup(s).