[RFC] wt-status: show amended content when verbose
diff mbox series

Message ID 20191116161856.28883-1-me@yadavpratyush.com
State New
Headers show
Series
  • [RFC] wt-status: show amended content when verbose
Related show

Commit Message

Pratyush Yadav Nov. 16, 2019, 4:18 p.m. UTC
Hi,

I am working on a simple little feature which shows the "amended
content" when running 'git-commit -v'. Currently, only the changes in
the _entire_ commit are shown. In a large commit, it is difficult to
spot a line or two that were amended. So, show just the amended content
in a different section.

I'm having trouble working with the internal diff API. 'rev' in the
function here is used to diff against HEAD^1. I want to do the exact
same thing, but against HEAD instead.

The diff below works, but it is obviously an ugly hack that just resets
'rev' and duplicates all the initialization code. I added it here as a
"proof of concept". What would be the cleaner way to do it?

I tried a bunch of things, but they either end up in me hitting

  BUG("run_diff_index must be passed exactly one tree");

in 'run_diff_index', or just doing something completely
unexpected/useless.

Some help/pointers would be appreciated. Thanks.

Regards,
Pratyush Yadav

-- 8< --

Signed-off-by: Pratyush Yadav <me@yadavpratyush.com>
---
 wt-status.c | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

--
2.24.0

Comments

Junio C Hamano Nov. 18, 2019, 3:36 a.m. UTC | #1
Pratyush Yadav <me@yadavpratyush.com> writes:

> I am working on a simple little feature which shows the "amended
> content" when running 'git-commit -v'. Currently, only the changes in
> the _entire_ commit are shown. In a large commit, it is difficult to
> spot a line or two that were amended. So, show just the amended content
> in a different section.

[jc: even though the diff generation is done before the final commit
is made, let me refer to the commits with refs _after_ the amend is
done].

You want to show changes between HEAD@{1}..HEAD (which is what the
"amend" did) in addition to changes between HEAD^..HEAD (which is
what the "amended commit" does) separately.

The reason why "git commit -v" lets you see the diff since HEAD^ is
to help you write the commit log message.  So it is wrong to show
only "what the amend did", as the message you would be writing while
amending is to explain the entire "why the amended commit does what
it does" and by definition the log message for "amend" should not
talk about "why the amend did what it did"---the readers would not
even have access to the older version before the amend.

It too makes quite a lot of sense to allow readers to see what the
'amend' did, but that is not something that would help write the log
message.  And that is why "git commit -v --amend" does not show it.
It should be inspected even _before_ the user contemplates to run
"git commit --amend" (e.g. "git diff HEAD" before starting to amend).

So, I am not enthused with this change---it sends a wrong message
(i.e. what the diff in the editor "commit -v" gives the user for).
Junio C Hamano Nov. 18, 2019, 4:02 a.m. UTC | #2
Junio C Hamano <gitster@pobox.com> writes:

> Pratyush Yadav <me@yadavpratyush.com> writes:
>
>> I am working on a simple little feature which shows the "amended
>> content" when running 'git-commit -v'. Currently, only the changes in
>> the _entire_ commit are shown. In a large commit, it is difficult to
>> spot a line or two that were amended. So, show just the amended content
>> in a different section.
>
> [jc: even though the diff generation is done before the final commit
> is made, let me refer to the commits with refs _after_ the amend is
> done].
>
> You want to show changes between HEAD@{1}..HEAD (which is what the
> "amend" did) in addition to changes between HEAD^..HEAD (which is
> what the "amended commit" does) separately.
>
> The reason why "git commit -v" lets you see the diff since HEAD^ is
> to help you write the commit log message.  So it is wrong to show
> only "what the amend did", as the message you would be writing while
> amending is to explain the entire "why the amended commit does what
> it does" and by definition the log message for "amend" should not
> talk about "why the amend did what it did"---the readers would not
> even have access to the older version before the amend.
>
> It too makes quite a lot of sense to allow readers to see what the
> 'amend' did, but that is not something that would help write the log
> message.  And that is why "git commit -v --amend" does not show it.
> It should be inspected even _before_ the user contemplates to run
> "git commit --amend" (e.g. "git diff HEAD" before starting to amend).
>
> So, I am not enthused with this change---it sends a wrong message
> (i.e. what the diff in the editor "commit -v" gives the user for).

Having said that, I also wonder two things.  Assuming that it may be
a good idea to show "what the amend does" in addition to "what the
amended commit does",

 1. would it make sense to show a combined diff to show the
    differences among the state being recorded in the amended commit
    as if it were a merge between the state in the original commit
    and the state in the parent commit?

 2. would it make sense to show the differences between
    HEAD^..HEAD@{1} and between HEAD^..HEAD using the range-diff
    machinery.

I think #1 may turn out to be more useful (I haven't tried it,
though) because we already show a moral equivalent elsewhere, namely
in "git stash show".

Conceptually, it would be similar to showing a stash entry that
records the state where some changes have been already added to the
index and some other changes are still in the working tree---the
base commit of such a stash entry corresponds to the parent commit
of the commit being amended, the contents from the index of such a
stash entry corresponds to the commit being amended, and the
contents from the working tree of such a stash entry corresponds to
the final contents you are trying to record as an amended commit.
Pratyush Yadav Nov. 19, 2019, 2:56 p.m. UTC | #3
On 18/11/19 01:02PM, Junio C Hamano wrote:
> Junio C Hamano <gitster@pobox.com> writes:
> 
> > Pratyush Yadav <me@yadavpratyush.com> writes:
> >
> >> I am working on a simple little feature which shows the "amended
> >> content" when running 'git-commit -v'. Currently, only the changes in
> >> the _entire_ commit are shown. In a large commit, it is difficult to
> >> spot a line or two that were amended. So, show just the amended content
> >> in a different section.
> >
> > [jc: even though the diff generation is done before the final commit
> > is made, let me refer to the commits with refs _after_ the amend is
> > done].
> >
> > You want to show changes between HEAD@{1}..HEAD (which is what the
> > "amend" did) in addition to changes between HEAD^..HEAD (which is
> > what the "amended commit" does) separately.

Yes, that is what the patch does. It shows _both_ the entire diff and 
the "amended diff".

> > The reason why "git commit -v" lets you see the diff since HEAD^ is
> > to help you write the commit log message.  So it is wrong to show
> > only "what the amend did", as the message you would be writing while
> > amending is to explain the entire "why the amended commit does what
> > it does" and by definition the log message for "amend" should not
> > talk about "why the amend did what it did"---the readers would not
> > even have access to the older version before the amend.
> >
> > It too makes quite a lot of sense to allow readers to see what the
> > 'amend' did, but that is not something that would help write the log
> > message.

It would help _amend_ the log message though. This is the use-case which 
motivated me to write this patch. When I make some changes to a commit 
(like when re-rolling), I often want to update the commit message too. 
If the commit content is a lot, then it becomes difficult to easily see 
what exactly I changed, and in turn makes it difficult to quickly spot 
what parts of the log message need updating.

> > And that is why "git commit -v --amend" does not show it.
> > It should be inspected even _before_ the user contemplates to run
> > "git commit --amend" (e.g. "git diff HEAD" before starting to amend).
> >
> > So, I am not enthused with this change---it sends a wrong message
> > (i.e. what the diff in the editor "commit -v" gives the user for).
> 
> Having said that, I also wonder two things.  Assuming that it may be
> a good idea to show "what the amend does" in addition to "what the
> amended commit does",
>  1. would it make sense to show a combined diff to show the
>     differences among the state being recorded in the amended commit
>     as if it were a merge between the state in the original commit
>     and the state in the parent commit?

I'm afraid I don't follow what exactly this would do, and how it would 
help differentiate between the "what the amend does" and "what the 
amended commit does". Wouldn't the diff of a merge between the original 
commit and the parent be exactly the diff (iow, the output of 'git 
show') of the original commit, since the merge is a fast-forward?
 
>  2. would it make sense to show the differences between
>     HEAD^..HEAD@{1} and between HEAD^..HEAD using the range-diff
>     machinery.

I considered using range-diff, but didn't go with it because of my 
personal dislike for range-diff. But if you strongly think that 
range-diff is a better idea, then I can do that too.
 
> I think #1 may turn out to be more useful (I haven't tried it,
> though) because we already show a moral equivalent elsewhere, namely
> in "git stash show".
> 
> Conceptually, it would be similar to showing a stash entry that
> records the state where some changes have been already added to the
> index and some other changes are still in the working tree---the
> base commit of such a stash entry corresponds to the parent commit
> of the commit being amended, the contents from the index of such a
> stash entry corresponds to the commit being amended, and the
> contents from the working tree of such a stash entry corresponds to
> the final contents you are trying to record as an amended commit.
>
Aaron Schrab Nov. 20, 2019, 12:27 a.m. UTC | #4
At 20:26 +0530 19 Nov 2019, Pratyush Yadav <me@yadavpratyush.com> wrote:
>> Junio C Hamano <gitster@pobox.com> writes:

>> > It too makes quite a lot of sense to allow readers to see what the
>> > 'amend' did, but that is not something that would help write the log
>> > message.
>
>It would help _amend_ the log message though.

Indeed. Another possible use is for sanity checking the amendment. I'll 
often look over the diff in my editor as a final check of what I'm about 
to commit, and when amending a commit I think I would find it helpful to 
be able to review the changes being amended separately from the full set 
of changes.
Junio C Hamano Nov. 20, 2019, 1:04 a.m. UTC | #5
Pratyush Yadav <me@yadavpratyush.com> writes:

> I'm afraid I don't follow what exactly this would do, and how it would 
> help differentiate between the "what the amend does" and "what the 
> amended commit does".

The resulting history would be

	O---A
         \
          B


where

	O = HEAD^ = HEAD@{1}^
	A = HEAD@{1}		- HEAD before the amend
	B = HEAD		- result of the amend

I wonder if

    git diff -c B O A

(with possibly different permutations of three revisions) is a
reasonable way to show what the final state is and where it differs
from the previous one (i.e. HEAD@{1}) and the original one
(i.e. HEAD^) in the combined diff format.

>>  2. would it make sense to show the differences between
>>     HEAD^..HEAD@{1} and between HEAD^..HEAD using the range-diff
>>     machinery.
>
> I considered using range-diff, but didn't go with it because of my 
> personal dislike for range-diff.

For a single-commit amend, the normal diff between HEAD@{1} and HEAD
would be far easier to read than such a range-diff, I would think.

Patch
diff mbox series

diff --git a/wt-status.c b/wt-status.c
index cc6f94504d..efa01c7ed6 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1086,6 +1086,27 @@  static void wt_longstatus_print_verbose(struct wt_status *s)
 		rev.diffopt.b_prefix = "w/";
 		run_diff_files(&rev, 0);
 	}
+
+	if (s->amend) {
+		repo_init_revisions(s->repo, &rev, NULL);
+		rev.diffopt.flags.allow_textconv = 1;
+		rev.diffopt.ita_invisible_in_index = 1;
+
+		memset(&opt, 0, sizeof(opt));
+		opt.def = "HEAD";
+		setup_revisions(0, NULL, &rev, &opt);
+
+		rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
+		rev.diffopt.detect_rename = s->detect_rename >= 0 ? s->detect_rename : rev.diffopt.detect_rename;
+		rev.diffopt.rename_limit = s->rename_limit >= 0 ? s->rename_limit : rev.diffopt.rename_limit;
+		rev.diffopt.rename_score = s->rename_score >= 0 ? s->rename_score : rev.diffopt.rename_score;
+		rev.diffopt.file = s->fp;
+		rev.diffopt.close_file = 0;
+		rev.diffopt.use_color = 0;
+		status_printf_ln(s, c, "Changes to amend:\n");
+
+		run_diff_index(&rev, 1);
+	}
 }

 static void wt_longstatus_print_tracking(struct wt_status *s)