threads / discuss / 63367

Discussion for interactive --patch commands to get --unified support

Subject: Discussion for interactive --patch commands to get --unified support

## tl;dr

12 messages between Apr 29, 2025 and May 2, 2025.

replies: 11people: 4as markdown or json

Leon Michalak· Apr 29, 2025, 09:16 UTC · lore
Hi everyone!

I am a big fan of `git diff` with a custom `diff.context` setting to increase the context lines (which also extends to other commands like `git show` which I really like). As well as this, I am also a frequent user of `git add --patch` (and also the other interactive `--patch` variants such as reset).

As I've grown to use and appreciate these features even more, I have noticed and been bothered that `git add --patch` doesn't have a (easy) way of configuring how many context lines you see. There is a stackoverflow post (https://stackoverflow.com/questions/6711670/git-show-more-context-when-using-git-add-i-or-git-add-e) which mentions you can do `GIT_DIFF_OPTS=-u<number> git add -p` which does work however isn't very user friendly or convenient.

It would be great if it was possible to start a discussion how this
could be made better (and I would be happy to submit a patch if all is
good). Whilst brainstorming briefly, here are a few options I have
thought of that could solve this pain point, some not mutually
exclusive:
- `-U<number>/--unified=<number>` command line options to the
interactive patch commands (all builtins who call `run_add_p`)
- make `diff.context` setting extend to the interactive patch commands
(not sure how a change like this would be welcomed considering it
could change users command outputs seemingly out of nowhere)
- add an `interactive.context` setting that would work like the
existing `diff.context` setting but apply only to the interactive
patch commands
Is this feature something people would welcome, and what are your thoughts?
Thanks!
Junio C Hamano· Apr 29, 2025, 16:48 UTC · re: Leon Michalak · lore

Re: Discussion for interactive --patch commands to get --unified support

Leon Michalak <leonmichalak6@gmail.com> writes:
Show 7 quoted lines
> As I've grown to use and appreciate these features even more, I have
> noticed and been bothered that `git add --patch` doesn't have a (easy)
> way of configuring how many context lines you see. There is a
> stackoverflow post
> (https://stackoverflow.com/questions/6711670/git-show-more-context-when-using-git-add-i-or-git-add-e)
> which mentions you can do `GIT_DIFF_OPTS=-u<number> git add -p` which
> does work however isn't very user friendly or convenient.

If it is only to specify how many context lines to ask for the diff machinery when preparing the initial patch that is presented in the "add -p" UI, it should be fairly easy.

I would expect that development of such a feature would progress roughly in the following order.

 - Define "struct add_p_opt {}" that has one "unsigned int" member,
   which is the unified context length, probably in add-interactive.h;
 - Teach add-interactive.c:run_add_p() to take an extra parameter of
   type "struct add_p_opt *opt";
 - Teach builtin/add.c to take -U<n> argument, make sure to make it
   an error when '-p' or '-i' is not given and -U<n> is.  Pass it in
   that new parameter when calling run_add_p() you modified above.
 - Do the same for builtin/{checkout,reset,stash}.c where they also
   call run_add_p().
 - Add a new command (sits next to "add untracked", "patch", "diff",
   etc.) to set -U<n> in add-interactive.c:run_add_i(), so that the
   default context length of 3 can be overridden before choosing
   "patch" or "diff" commands in "git add -i".

Because we generate diff once, and then let the end-user dice and slice freely, without keeping track of the correspondence between what the original diff looked like and the current diff that is a result of end-user dicing and slicing, I think extending the context length on demand (i.e. "I started an 'add -p' session, chose a few hunks, edited a handful hunks, and then realized that this single hunk I want to see a bit more context") is _significantly_ harder. The current code structure is simply not designed for it.

It would take a significant rewrite to allow you to say "OK, let me regenerate the diff with wider context (this is the easy part) and find the hunk with larger context that corresponds with the hunk you are talking about (harder)".

Jeff King· Apr 29, 2025, 22:09 UTC · re: Leon Michalak · lore

Re: Discussion for interactive --patch commands to get --unified support

On Tue, Apr 29, 2025 at 10:16:15AM +0100, Leon Michalak wrote:
Show 6 quoted lines
> - make `diff.context` setting extend to the interactive patch commands
> (not sure how a change like this would be welcomed considering it
> could change users command outputs seemingly out of nowhere)
> - add an `interactive.context` setting that would work like the
> existing `diff.context` setting but apply only to the interactive
> patch commands

In my opinion it would be fine to respect diff.context (and probably diff.interhunkcontext[1]) by default. Though it does change the command output, the interactive output is by definition user-facing, so we shouldn't be breaking scripts. And we already respect other porcelain level config like colorizing.

I think the only reason we don't already do so is that the interactive code is built around the plumbing commands, which conservatively avoid various config options. So the calling code has to explicitly check the config itself.

-Peff
[1] Looking at git_diff_ui_config(), which are all the options read by
    porcelain git-diff but not by plumbing git-diff-files, etc, there
    may be other config in the same boat. E.g., I'd guess that people
    with diff.colormoved set would appreciate seeing that effect in the
    colorized versions we show. But I think it is OK to just consider
    diff.context for now, and see if anybody ever cares enough about
    other options to look into them.
Leon Michalak· Apr 30, 2025, 08:04 UTC · re: Jeff King · lore

Re: Discussion for interactive --patch commands to get --unified support

> If it is only to specify how many context lines to ask for the diff
> machinery when preparing the initial patch that is presented in the
> "add -p" UI, it should be fairly easy.

This is the intention, I personally don't have a need for changing hunk context *during* an interactive add and as you say, this looks to be a big task.

> I would expect that development of such a feature would progress
> roughly in the following order.

As someone who has never contributed to (or even very familiar with) the codebase, this is very helpful and I really appreciate it!

> In my opinion it would be fine to respect diff.context (and probably
> diff.interhunkcontext[1]) by default.
I think this could work nicely and agree with this approach.
Show 5 quoted lines
> Looking at git_diff_ui_config(), which are all the options read by
> porcelain git-diff but not by plumbing git-diff-files, etc, there
> may be other config in the same boat. E.g., I'd guess that people
> with diff.colormoved set would appreciate seeing that effect in the
> colorized versions we show.

Agreed. As you mention it, I recently discovered `diff.colormoved` and I'd welcome this to the `--patch` functionality, although I also agree that it would make sense to take things one step at a time and it's not necessary to batch anything else into this change.

With all that being said, I propose the following:
- inheriting `diff.context` and `diff.interhunkcontext` as the
defaults for the various interactive/patch commands
- be able to override these defaults on the command line with
`-U<n>/--unified=<n>` and `--inter-hunk-context=<n>` respectively

These would cover both a "permanent" config that you wouldn't need to specify every time when writing the command/s out, but also would provide a way to override the individual built-ins as/when you want.

You guys obviously have better knowledge and experience with git development so it would be great to hear your feedback on this.

Thanks!
On Tue, 29 Apr 2025 at 23:09, Jeff King <peff@peff.net> wrote:
Show 30 quoted lines
>
> On Tue, Apr 29, 2025 at 10:16:15AM +0100, Leon Michalak wrote:
>
> > - make `diff.context` setting extend to the interactive patch commands
> > (not sure how a change like this would be welcomed considering it
> > could change users command outputs seemingly out of nowhere)
> > - add an `interactive.context` setting that would work like the
> > existing `diff.context` setting but apply only to the interactive
> > patch commands
>
> In my opinion it would be fine to respect diff.context (and probably
> diff.interhunkcontext[1]) by default. Though it does change the command
> output, the interactive output is by definition user-facing, so we
> shouldn't be breaking scripts. And we already respect other porcelain
> level config like colorizing.
>
> I think the only reason we don't already do so is that the interactive
> code is built around the plumbing commands, which conservatively avoid
> various config options. So the calling code has to explicitly check the
> config itself.
>
> -Peff
>
> [1] Looking at git_diff_ui_config(), which are all the options read by
>     porcelain git-diff but not by plumbing git-diff-files, etc, there
>     may be other config in the same boat. E.g., I'd guess that people
>     with diff.colormoved set would appreciate seeing that effect in the
>     colorized versions we show. But I think it is OK to just consider
>     diff.context for now, and see if anybody ever cares enough about
>     other options to look into them.
Junio C Hamano· Apr 30, 2025, 14:40 UTC · re: Leon Michalak · lore

Re: Discussion for interactive --patch commands to get --unified support

Leon Michalak <leonmichalak6@gmail.com> writes:
Show 5 quoted lines
> With all that being said, I propose the following:
> - inheriting `diff.context` and `diff.interhunkcontext` as the
> defaults for the various interactive/patch commands
> - be able to override these defaults on the command line with
> `-U<n>/--unified=<n>` and `--inter-hunk-context=<n>` respectively

I think the usage pattern when people use "add -p" and "diff" are different (with "add -p", you get only one hunk at a time, and you may want a bit wider context to avoid getting disoriented, for example) and I agree that it may be useful to be able to specify different context lengths for them, which the above should cover nicely.

Phillip Wood· May 2, 2025, 14:33 UTC · re: Jeff King · lore

Re: Discussion for interactive --patch commands to get --unified support

Hi Leon and Jeff
On 29/04/2025 23:09, Jeff King wrote:
Show 14 quoted lines
> On Tue, Apr 29, 2025 at 10:16:15AM +0100, Leon Michalak wrote:
> 
>> - make `diff.context` setting extend to the interactive patch commands
>> (not sure how a change like this would be welcomed considering it
>> could change users command outputs seemingly out of nowhere)
>> - add an `interactive.context` setting that would work like the
>> existing `diff.context` setting but apply only to the interactive
>> patch commands
> 
> In my opinion it would be fine to respect diff.context (and probably
> diff.interhunkcontext[1]) by default. Though it does change the command
> output, the interactive output is by definition user-facing, so we
> shouldn't be breaking scripts. And we already respect other porcelain
> level config like colorizing.

I think that would be a good place to start and would be a useful improvement on the status quo. To implement it one can copy what we do to respect diff.algorithm. I'm not sure it is worth the effort to support `git add -p -U <context>`. Being able to interactively change the context as suggested elsewhere sounds more interesting to me but it would be more work to implement as we'd have to regenerate the diff each time.

Best Wishes
Phillip
Show 15 quoted lines
> I think the only reason we don't already do so is that the interactive
> code is built around the plumbing commands, which conservatively avoid
> various config options. So the calling code has to explicitly check the
> config itself.
> 
> -Peff
> 
> [1] Looking at git_diff_ui_config(), which are all the options read by
>      porcelain git-diff but not by plumbing git-diff-files, etc, there
>      may be other config in the same boat. E.g., I'd guess that people
>      with diff.colormoved set would appreciate seeing that effect in the
>      colorized versions we show. But I think it is OK to just consider
>      diff.context for now, and see if anybody ever cares enough about
>      other options to look into them.
> 
Phillip Wood· May 2, 2025, 14:39 UTC · re: Leon Michalak · lore

Re: Discussion for interactive --patch commands to get --unified support

On 29/04/2025 10:16, Leon Michalak wrote:
> 
> (https://stackoverflow.com/questions/6711670/git-show-more-context-when-using-git-add-i-or-git-add-e)
> which mentions you can do `GIT_DIFF_OPTS=-u<number> git add -p` which
> does work however isn't very user friendly or convenient.

This is a question for others on the list rather than Leon - is it intentional that the plumbing diff commands respect GIT_DIFF_OPTS? If a script that wants to create a diff with a certain number of context lines runs `git diff-index -U <context>` is it helpful for that to be overridden if GIT_DIFF_OPTS happens to be set in the environment? Looking at the history it seems that environment variable used to be the only way to override the default context setting but that's not the case now.

Best Wishes
Phillip
Leon Michalak· May 2, 2025, 16:14 UTC · re: Phillip Wood · lore

Re: Discussion for interactive --patch commands to get --unified support

Inheriting the diff.context setting is what scratches my itch the most, although also being able to set the context in the command list of `add -i` sounds interesting too. Personally, I don't think I would use the command line overrides too much myself as most of the time (like with diff) I'd like to set the option and forget it but it does have a certain consistency to it.

Slightly off-topic to the discussion, but does anyone have advice on how to deal with providing a sentinel value for something like context? I'd expect to pass `--unified` to the underlying diff command *only* if the user specifically has overridden it via command line option or a diff.context config, just like diff.algorithm has done, however diff.algorithm is a `char *` so the value can be NULL which is a good sentinel value. My thinking is then the underlying command can just deal with the value as it sees it, such as giving a default if not provided or making sure it's a minimum of 0 etc. Otherwise the level above has to deal with it which then probably involves `git_diff_ui_config` and other validations which I don't even think is it's responsibility and would probably duplicate logic unncessarily?

I may be completely off in my assumptions here being new to the codebase, so if anyone has any thoughts I'd greatly appreciate any comments!

On Fri, 2 May 2025 at 15:39, Phillip Wood <phillip.wood123@gmail.com> wrote:
Show 18 quoted lines
>
> On 29/04/2025 10:16, Leon Michalak wrote:
> >
> > (https://stackoverflow.com/questions/6711670/git-show-more-context-when-using-git-add-i-or-git-add-e)
> > which mentions you can do `GIT_DIFF_OPTS=-u<number> git add -p` which
> > does work however isn't very user friendly or convenient.
> This is a question for others on the list rather than Leon - is it
> intentional that the plumbing diff commands respect GIT_DIFF_OPTS? If a
> script that wants to create a diff with a certain number of context
> lines runs `git diff-index -U <context>` is it helpful for that to be
> overridden if GIT_DIFF_OPTS happens to be set in the environment?
> Looking at the history it seems that environment variable used to be the
> only way to override the default context setting but that's not the case
> now.
>
> Best Wishes
>
> Phillip
Leon Michalak· May 2, 2025, 16:23 UTC · re: Leon Michalak · lore

Re: Discussion for interactive --patch commands to get --unified support

Correction to my above message, I meant to reference `repo_diff_setup`, not `git_diff_ui_config` as that lets you initialise a struct with the default context value that diff uses (assuming I have understood correctly)

On Fri, 2 May 2025 at 17:14, Leon Michalak <leonmichalak6@gmail.com> wrote:
Show 44 quoted lines
>
> Inheriting the diff.context setting is what scratches my itch the most, although
> also being able to set the context in the command list of `add -i`
> sounds interesting too. Personally, I don't think I would use the
> command line overrides too much myself as most of the time (like with
> diff) I'd like to set the option and forget it but it does have a
> certain consistency to it.
>
> Slightly off-topic to the discussion, but does anyone have advice on
> how to deal with providing a sentinel value for something like
> context? I'd expect to pass `--unified` to the underlying diff command
> *only* if the user specifically has overridden it via command line
> option or a diff.context config, just like diff.algorithm has done,
> however diff.algorithm is a `char *` so the value can be NULL which is
> a good sentinel value. My thinking is then the underlying command can
> just deal with the value as it sees it, such as giving a default if
> not provided or making sure it's a minimum of 0 etc. Otherwise the
> level above has to deal with it which then probably involves
> `git_diff_ui_config` and other validations which I don't even think is
> it's responsibility and would probably duplicate logic unncessarily?
>
> I may be completely off in my assumptions here being new to the
> codebase, so if anyone has any thoughts I'd greatly appreciate any
> comments!
>
> On Fri, 2 May 2025 at 15:39, Phillip Wood <phillip.wood123@gmail.com> wrote:
> >
> > On 29/04/2025 10:16, Leon Michalak wrote:
> > >
> > > (https://stackoverflow.com/questions/6711670/git-show-more-context-when-using-git-add-i-or-git-add-e)
> > > which mentions you can do `GIT_DIFF_OPTS=-u<number> git add -p` which
> > > does work however isn't very user friendly or convenient.
> > This is a question for others on the list rather than Leon - is it
> > intentional that the plumbing diff commands respect GIT_DIFF_OPTS? If a
> > script that wants to create a diff with a certain number of context
> > lines runs `git diff-index -U <context>` is it helpful for that to be
> > overridden if GIT_DIFF_OPTS happens to be set in the environment?
> > Looking at the history it seems that environment variable used to be the
> > only way to override the default context setting but that's not the case
> > now.
> >
> > Best Wishes
> >
> > Phillip
Junio C Hamano· May 2, 2025, 16:57 UTC · re: Leon Michalak · lore

Re: Discussion for interactive --patch commands to get --unified support

Leon Michalak <leonmichalak6@gmail.com> writes:
Show 6 quoted lines
> Inheriting the diff.context setting is what scratches my itch the most, although
> also being able to set the context in the command list of `add -i`
> sounds interesting too. Personally, I don't think I would use the
> command line overrides too much myself as most of the time (like with
> diff) I'd like to set the option and forget it but it does have a
> certain consistency to it.
Sounds good.
> Slightly off-topic to the discussion, but does anyone have advice on
> how to deal with providing a sentinel value for something like
> context?
Seeing in diff.c
    static int diff_context_default = 3;
    static int diff_interhunk_context_default;

that they are of signed type, and negative context would not make sense (would it???), wouldn't -1 be a good "they haven't touched this from the command line or configuration" value?

Leon Michalak· May 2, 2025, 17:13 UTC · re: Junio C Hamano · lore

Re: Discussion for interactive --patch commands to get --unified support

This is what I have been using currently although the perfectionist in me says that a user could pass `--unified=-1` and the code would treat that as if nothing was passed by the user.

In practice, I guess this doesn't really matter. I think that was probably a needed sanity check, thanks

On Fri, 2 May 2025, 17:57 Junio C Hamano, <gitster@pobox.com> wrote:
Show 24 quoted lines
>
> Leon Michalak <leonmichalak6@gmail.com> writes:
>
> > Inheriting the diff.context setting is what scratches my itch the most, although
> > also being able to set the context in the command list of `add -i`
> > sounds interesting too. Personally, I don't think I would use the
> > command line overrides too much myself as most of the time (like with
> > diff) I'd like to set the option and forget it but it does have a
> > certain consistency to it.
>
> Sounds good.
>
> > Slightly off-topic to the discussion, but does anyone have advice on
> > how to deal with providing a sentinel value for something like
> > context?
>
> Seeing in diff.c
>
>     static int diff_context_default = 3;
>     static int diff_interhunk_context_default;
>
> that they are of signed type, and negative context would not make
> sense (would it???), wouldn't -1 be a good "they haven't touched
> this from the command line or configuration" value?
Junio C Hamano· May 2, 2025, 18:36 UTC · re: Leon Michalak · lore

Re: Discussion for interactive --patch commands to get --unified support

Leon Michalak <leonmichalak6@gmail.com> writes:
> This is what I have been using currently although the perfectionist in
> me says that a user could pass `--unified=-1` and the code would treat
> that as if nothing was passed by the user.

The command line parser and corresponding config parser callback can be careful to check the value they get from the end user to avoid that, can't they?

In any case, it is PEBKAC when the end-users do that, and they cannot complain about their --unified=-1 are ignored. We do not have to care, and can just tell them "Don't do that, then."

← back to recent threads