Add `branch-diff`, a `tbdiff` lookalike
Subsystems (2)
Re: refs/notes/amlog problems, was Re: [PATCH v3 01/20] linear-assignment: a function to solve least-cost assignment problems
In reply to Junio C HamanoHi Junio,
Quoted 9 lines · Junio C Hamano
Quoted 2 lines · Johannes Schindelin
AFAICT there is at least one scenario where you run
rebase -i, the notes get updated, and of course the *reverse mapping* does *not* get updated:It turns out that I never had a rewrite hook; the notes.rewriteref mechanism is the only thing that has been used to maintain amlog.
I've stopped populating the reverse mapping, by the way.
That's just great. I ask you to make my life easier by keeping the information correct, and now you just drop it altogether? Just great.
Seriously, I am trying to *improve* something here, because I really do care about contributors, and how hard we make it on them. I would not have expected such a backlash against that.
Quoted 4 lines
The script that I feed a message from gmane or public-inbox when I need to learn the set of commits that resulted from the message instead uses "git grep $message-id notes/amlog". And that is fast enough for my purpose.
Awesome. You might want to make sure that Peff stops advertising the amlog notes, then, though.
Quoted 4 lines
There is no good reason to abuse the notes mechanism to map a random object-name looking string (i.e. hash result of message id), other than the ease of "quick access" when somebody is making a lot of inquiry, but that "database" does not have to be stored in notes.
Right. And it does not have to be stored anywhere, because nobody used it anyway, right?
Well, I hate to break it to you: I just found a really excellent use case, and you are making it very, very hard for me. Deliberately so. I don't know how I deserve that.
Quoted 5 lines
It certainly does not belong to cycles worth spending by me *while* I work during the say with various history reshaping tools to record and/or update the reverse mapping and that is why my post-applypatch hook no longer has the "reverse map" hack.
Quoted 9 lines
It is not like anybody (including me) needs realtime up-to-date reverse mapping from amlog while I run my "commit --amend", "rebase -i", etc. and the reverse map is constructable by reversing the forward map, obviously, with a postprocessing. And I think that is a reasonably way forward if anybody wants to have a reverse mapping. The postprocessing can be done either by me before pushing out the amlog ref, or done by any consumer after fetching the amlog ref from me. If I did the postprocessing and refuse to use rewrite hook you wouldn't even know ;-)
The idea that you publish the amlog notes just for your own use cases, sounds a bit strange to me.
So to reiterate: the information you have in amlog is useful, if faulty. Rather than "fixing" it by stopping the useful reverse-mapping, it would make a ton more sense to instate that post-rewrite hook I already drafted for you.
Besides, while you spent all of that time to make things harder for me, you still did not look into the most worrisome of my findings: there are apparently Message-Id mappings where *none* of the commits returned by said git grep you mentioned above are valid. Not a single one. I will dig out the mail for you on Monday, because I care that much, where I provided one example of a Message-Id with two commits that match in amlog, none of which is actually reachable from any of your public branches, and I also provided the commit that *actually* corresponds to that Message-Id, and it is not annotated.
So at least in this case *even you* should have a vested interest in figuring out what goes wrong because even your own use case is affected by it.
Ciao,
Dscho
Re: refs/notes/amlog problems, was Re: [PATCH v3 01/20] linear-assignment: a function to solve least-cost assignment problems
Quoted 7 lines · Johannes Schindelin
Quoted 4 lines
The script that I feed a message from gmane or public-inbox when I need to learn the set of commits that resulted from the message instead uses "git grep $message-id notes/amlog". And that is fast enough for my purpose.
Awesome. You might want to make sure that Peff stops advertising the amlog notes, then, though.
Woah, what did I do now?
Quoted 7 lines
Quoted 4 lines
There is no good reason to abuse the notes mechanism to map a random object-name looking string (i.e. hash result of message id), other than the ease of "quick access" when somebody is making a lot of inquiry, but that "database" does not have to be stored in notes.
Right. And it does not have to be stored anywhere, because nobody used it anyway, right?
If I understand the situation correctly, Junio is saying that he will continue to produce the amlog mapping, and that it contains sufficient information to produce the reverse mapping (which, as an aside, I did not even know existed -- I mostly want to go the other way, from digging in history to a mailing list conversation).
E.g., the script below builds and queries an incremental reverse mapping.
-- >8 --
#!/usr/bin/perl
my $REF = 'refs/notes/amlog';
my $DBFILE = '.git/amlog.rev';
use DB_File;
my %h;
my $db = tie %h, 'DB_File', $DBFILE, O_CREAT|O_RDWR, 0644
or die "unable to open/create $DBFILE: $!";
my $db_tip = $h{TIP};
chomp(my $rev_tip = git rev-parse $REF);
if (!defined $db_tip || $db_tip ne $rev_tip) {
print STDERR "Updating reverse mapping...\n";
# using -p here is quick and easy, since we know the # shape of the data. Using --raw and cat-file might be less # hacky, though. my @cmd = (qw(git log --format= --reverse -p), $rev_tip); push @cmd, "^$db_tip" if defined $db_tip; open(my $fh, "-|", @cmd);
my $commit;
while (<$fh>) {
if (m{^\+\+\+ b/([0-9a-f/]+)}) {
$commit = $1;
$commit =~ s/[^0-9a-f]//g;
} elsif (/^\+Message-Id: <(.*)>/i) {
print STDERR "Imported $commit => $1\n";
$h{$1} = $commit;
} }
$h{TIP} = $rev_tip;
}
print "$h{$_} $_\n" for @ARGV;
-- >8 --
That stores it in a local dbm. But it could also build a git-notes tree if you really want that.
And if I understand what is being said here:
Quoted 5 lines
Quoted 5 lines
It certainly does not belong to cycles worth spending by me *while* I work during the say with various history reshaping tools to record and/or update the reverse mapping and that is why my post-applypatch hook no longer has the "reverse map" hack.
Quoted 9 lines
Quoted 9 lines
It is not like anybody (including me) needs realtime up-to-date reverse mapping from amlog while I run my "commit --amend", "rebase -i", etc. and the reverse map is constructable by reversing the forward map, obviously, with a postprocessing. And I think that is a reasonably way forward if anybody wants to have a reverse mapping. The postprocessing can be done either by me before pushing out the amlog ref, or done by any consumer after fetching the amlog ref from me. If I did the postprocessing and refuse to use rewrite hook you wouldn't even know ;-)
It is not "I refuse to push out a reverse mapping". It is "I could make the reverse mapping before push-out, and you would not need to know or care if I did it all at once, or using a rewrite hook".
Though personally, I do not know if there is much point in pushing it out, given that receivers can reverse the mapping themselves.
Or is there some argument that there is information in the reverse map that _cannot_ be generated from the forward map?
-Peff
Re: refs/notes/amlog problems, was Re: [PATCH v3 01/20] linear-assignment: a function to solve least-cost assignment problems
Quoted 5 lines · Jeff King
If I understand the situation correctly, Junio is saying that he will continue to produce the amlog mapping, and that it contains sufficient information to produce the reverse mapping (which, as an aside, I did not even know existed -- I mostly want to go the other way, from digging in history to a mailing list conversation).
Yes, the reverse mapping in amlog was an experiment that did not work well in the end.
When I use "git am" to make a commit out of a message, a post-applypatch hook picks up the "Message-Id:" from the original message and adds a git note to the resulting commit. This is in line with how the notes are meant to be used. We have a commit object, and a piece of information that we want to associate with the commit object, which is not recorded as a part of the commit object. So we say "git notes add -m 'that piece of info' $commit" (the message-id happens to be that piece of info in this example).
And with notes.rewriteRef, "git commit --amend" etc. would copy the piece of info about the original commit to the rewritten commit.
Side Note: there are a few workflow elements I do want to keep using but they currently *lose* the mapping info. An obvious one is
$ git checkout -b to/pic master &&
... review in MUA and then ...
$ git am -s mbox &&
... review in tree, attempt to build, tweak, etc. $ git format-patch --stdout master..to/pic >P && $ edit P &&
$ git reset --hard master &&
$ git am P
which is far more versatile and efficient when doing certain transformations on the series than running "rebase -i" and reopening and editing the target files of the patches one by one in each step. But because format-patch does not generate Message-Id header of the original one out of the commit, the post-applypatch hook run by "am" at the end of the steps would not have a chance to record that for the newly created commit.
For this one, I think I can use "format-patch --notes=amlog" to produce the patch file and then teach post-applypatch script to pay attention to the Notes annotation without changing anything else to record the message id of the original. Other workflow elements that lose the notes need to be identified and either a fix implemented or a workaround found for each of them. For example, I suspect there is no workaround for "cherry-pick" and it would take a real fix.
A reverse mapping entry used to get created by post-applypatch to map the blob that represents the notes text added to the $commit to another text blob that contains the 40-hex of the commit object. This is the experiment that did not work well. As none of the later integrator's work e.g. "commit --amend", "rebase", "cherry-pick", etc. is about rewriting that blob, notes.rewriteRef mechanism would not kick in, and that is understandasble.
And these (incomplete) reverse mapping entries get in the way to maintain and correct the forward mapping. When a commit that got unreachable gets expired, I want "git notes prune" to remove notes on them, and I do not want to even think about what should happen to the entries in the notes tree that abuse the mechanism to map blobs that are otherwise *not* even reachable from the main history.
A much more important task is to make sure that the forward mapping that annotates invidual commits reachable from 'pu' and/or 'master' is maintained correctly by various tools. From a correctly maintained forward mapping, it should be straight forward to get a reverse mapping if needed.
Quoted 2 lines
Though personally, I do not know if there is much point in pushing it out, given that receivers can reverse the mapping themselves.
Before this thread, I was planning to construct and publish the reverse mapping at the end of the day, but do so on a separate notes ref (see above---the hacky abuse gets in the way of maintaining and debugging the forward mapping, but a separate notes-ref that only contains hacks is less worrysome). But I have changed my mind and decided not to generate or publish one. It is sort of similar to the way the pack .idx is constructed only by the receiver [*1*].
Quoted 2 lines
Or is there some argument that there is information in the reverse map that _cannot_ be generated from the forward map?
I know there is no information loss (after all I was the only one who ran that experimental hack), but there is one objection that is still possible, even though I admit that is a weak argument.
If a plumbing "diff-{files,tree,index}" family had a sibling "diff-notes" to compare two notes-shaped trees while pretending that the object-name fan-out did not exist (i.e. instead, the trees being compared is without a subtree and full of 40-hex filenames), then it would be less cumbersome to incrementally update the reverse mapping by reading forward mapping with something like:
git diff-notes --raw amlog@{1} amlogto learn the commits whose notes have changed. But without such a plumbing, it is cumbersome to do so correctly. "git diff-tree -r" could serve as a rough substitute, until the note tree grows and get rebalanced by reorganizing the fan-out, and on the day it happens the reverse mapper needs to read and discard ghost changes that are only due to tree reorganizing [*2*].
[Footnotes]
*1* Even if the sender could give one when it creates a .pack, the
receiver would not trust that it is matches the corresponding .pack before using it, and the cost to validate is similar to the cost to generate.
*2* That makes it less efficient on that day (which hopefully would
happen once in a blue moon) but would not affect correctness.
Re: refs/notes/amlog problems, was Re: [PATCH v3 01/20] linear-assignment: a function to solve least-cost assignment problems
Quoted 22 lines · Junio C Hamano
Side Note: there are a few workflow elements I do want to keep using but they currently *lose* the mapping info. An obvious one is
$ git checkout -b to/pic master &&
... review in MUA and then ...
$ git am -s mbox &&
... review in tree, attempt to build, tweak, etc. $ git format-patch --stdout master..to/pic >P && $ edit P &&
$ git reset --hard master &&
$ git am Pwhich is far more versatile and efficient when doing certain transformations on the series than running "rebase -i" and reopening and editing the target files of the patches one by one in each step. But because format-patch does not generate Message-Id header of the original one out of the commit, the post-applypatch hook run by "am" at the end of the steps would not have a chance to record that for the newly created commit.
Quoted 5 lines
For this one, I think I can use "format-patch --notes=amlog" to produce the patch file and then teach post-applypatch script to pay attention to the Notes annotation without changing anything else to record the message id of the original.
Yes. I wonder if it would make sense to teach format-patch/am a micro-format to automatically handle this case. I.e., some machine-readable way of passing the notes in the email message. Of course it's easy to design a format that covers the relatively restricted form of these amlog notes, and much harder to cover the general case.
Quoted 5 lines
Other workflow elements that lose the notes need
to be identified and either a fix implemented or a workaround found for each of them. For example, I suspect there is no workaround for "cherry-pick" and it would take a real fix.
I think the existing notes.rewriteRef is probably a good match here. I can definitely think of notes you wouldn't want to cherry-pick, but I'm having trouble coming up with an example that should survive a rebase but not a cherry-pick.
Quoted 6 lines
And these (incomplete) reverse mapping entries get in the way to maintain and correct the forward mapping. When a commit that got unreachable gets expired, I want "git notes prune" to remove notes on them, and I do not want to even think about what should happen to the entries in the notes tree that abuse the mechanism to map blobs that are otherwise *not* even reachable from the main history.
Right, I think the notes tree is a poor distribution method for that reason.
Quoted 10 lines
Quoted 2 lines
Though personally, I do not know if there is much point in pushing it out, given that receivers can reverse the mapping themselves.
Before this thread, I was planning to construct and publish the reverse mapping at the end of the day, but do so on a separate notes ref (see above---the hacky abuse gets in the way of maintaining and debugging the forward mapping, but a separate notes-ref that only contains hacks is less worrysome). But I have changed my mind and decided not to generate or publish one. It is sort of similar to the way the pack .idx is constructed only by the receiver [*1*].
Yes, the pack .idx was the same mental model I had when writing my earlier message.
Quoted 6 lines
Quoted 2 lines
Or is there some argument that there is information in the reverse map that _cannot_ be generated from the forward map?
I know there is no information loss (after all I was the only one who ran that experimental hack), but there is one objection that is still possible, even though I admit that is a weak argument.
I wondered if you might have a case like this (building as we go):
- message-id M becomes commit X
- we write the forward map X->M
- we write the reverse map M->X
- during a rewrite (e.g., --amend), commit X becomes commit Y
- we write the forward map Y->M
- we write the reverse map M->Y
The difference between that result and an inverted map created at the end is that we know that M->Y is the final result. Whereas by looking at the inverted map, we do not know which of M->X and M->Y is correct. In fact they are _both_ correct. But only one of X and Y would eventually get merged (both may make it into the repo's of people fetching from you if we imagine that X is on "pu" and you push between the two steps).
So I think the inverted mapping is not actually one-to-one, and in either case you'd want to retain all possible matches (pruning only when a commit is eventually dropped from the forward mapping, which rewritten things from "pu" would eventually do). And in that case it does not matter if you generate it incrementally or all at once.
Quoted 7 lines
If a plumbing "diff-{files,tree,index}" family had a sibling "diff-notes" to compare two notes-shaped trees while pretending that the object-name fan-out did not exist (i.e. instead, the trees being compared is without a subtree and full of 40-hex filenames), then it would be less cumbersome to incrementally update the reverse mapping by reading forward mapping with something like:
Quoted 8 lines
git diff-notes --raw amlog@{1} amlogto learn the commits whose notes have changed. But without such a plumbing, it is cumbersome to do so correctly. "git diff-tree -r" could serve as a rough substitute, until the note tree grows and get rebalanced by reorganizing the fan-out, and on the day it happens the reverse mapper needs to read and discard ghost changes that are only due to tree reorganizing [*2*].
Yeah. My "log" hackery was trying to do that incremental comparison, but it did not handle the multiple-commit case (nor did it handle deletions). I agree an end-point diff is sufficient (and more efficient).
-Peff
Re: [PATCH v3 01/20] linear-assignment: a function to solve least-cost assignment problems
In reply to Johannes SchindelinQuoted 9 lines · Johannes Schindelin
Speaking of GitGitGadget: I just encoutered a problem with your
refs/notes/amlogand I hope you can help me with that.
...
When I askgit notes --ref=refs/notes/gitster-amlog show 4cec3986f017d84c8d6a2c4233d2eba4a3ffa60d(the SHA-1 is the one corresponding toMessage-Id: <...>for that mail), it insists on outputting5902152ab02291af4454f24a8ccaf2adddefc306
It is not uncommon for me to have to do "am" the same patch twice when attempting to find the right branch/commit to base a change on, so the reverse direction that abuses the notes mechanism to map message id to resulting commits would be unreliable, especially given that they may need to further go through "rebase -i" or manual "cherry-pick <range>" depending on the situation.
I am kind of surprised that the message-to-commit mapping still records any data that is remotely useful (these days, I only use it to run "show --notes=amlog" for commit-to-message mapping). I do not think I have anything special when amending the commit, but amlog notes should be updated in both diretions for its entries to stay correct across amending, I would think.
refs/notes/amlog woes, was Re: [PATCH v3 01/20] linear-assignment: a function to solve least-cost assignment problems
Hi Junio,
Quoted 10 lines · Junio C Hamano
Quoted 8 lines · Johannes Schindelin
Speaking of GitGitGadget: I just encoutered a problem with your
refs/notes/amlogand I hope you can help me with that.
...
When I askgit notes --ref=refs/notes/gitster-amlog show 4cec3986f017d84c8d6a2c4233d2eba4a3ffa60d(the SHA-1 is the one corresponding toMessage-Id: <...>for that mail), it insists on outputting
Quoted 4 lines
Quoted 1 line
5902152ab02291af4454f24a8ccaf2adddefc306It is not uncommon for me to have to do "am" the same patch twice when attempting to find the right branch/commit to base a change on,
But then the post-applypatch hook just kicks in twice, leaving the correct mapping in place, no?
Quoted 4 lines
so the reverse direction that abuses the notes mechanism to map message id to resulting commits would be unreliable, especially given that they may need to further go through "rebase -i" or manual "cherry-pick <range>" depending on the situation.
We already have a mechanism in place that rewrites notes in rebase -i's case. Not so sure about cherry-pick, but if it is missing, then that is definitely something we will want to address.
In other words, let's not let shortcomings of our own software dictate what we record and what we don't record.
This is highly important information that we willfully lose by using the patch contribution process we are going with. And we *can* at least record that information.
Quoted 3 lines
I am kind of surprised that the message-to-commit mapping still records any data that is remotely useful (these days, I only use it to run "show --notes=amlog" for commit-to-message mapping).
Please do understand that this information is the only remotely sane way to work around the limitations of the mailing list-based approach we use here.
It costs me a ton of time to figure out these mappings manually, and I think that others simply are not as tenacious as I am and simply drop the ball, which is not good for the project.
Quoted 3 lines
I do not think I have anything special when amending the commit, but amlog notes should be updated in both diretions for its entries to stay correct across amending, I would think.
Indeed. See my other mail about the post-rewrite hook I suggest you to install (I did not test this code, of course, but you will probably be able to validate/fix it without much trouble).
Ciao,
Dscho
Re: [PATCH v3 01/20] linear-assignment: a function to solve least-cost assignment problems
In reply to Johannes Schindelin via GitGitGadgetQuoted 23 lines
linear-assignment.c new file +17
-0@@ -0,0 +1,203 @@ +/* +* Based on: Jonker, R., & Volgenant, A. (1987). <i>A shortest augmenting path +* algorithm for dense and sparse linear assignment problems</i>. Computing, +* 38(4), 325-340. +*/ +#include "cache.h" +#include "linear-assignment.h" + +#define COST (column, row) cost[(column) + column_count * (row)] + +/* +* The parameter `cost` is the cost matrix: the cost to assign column j to row +* i is `cost[j + column_count * i]. +*/ +void compute_assignment (int column_count,int row_count,int *cost, +int *column2row,int *row2column) +{
[...]
Quoted 6 lines
linear-assignment.c new file +6
-0+update: +/* updating of the column pieces */ +for (k =0 ; k < last; k++) { +int j1 = col[k]; + v[j1] += d[j1] - min; + }
linear-assignment.c line 194
Quoted 10 lines
linear-assignment.c new file +10
-0+ +/* augmentation */ + do { + if (j < 0) + BUG("negative j: %d", j); + i = pred[j]; + column2row[j] = i; + k = j; + j = row2column[i]; + row2column[i] = k;
Coccinelle suggests using SWAP(j, row2column[i]) instead of the last three lines above.
It's more idiomatic, and it avoids (ab)using the 'k' variable (elsewhere used as loop variable) as a temporary variable.
Quoted 9 lines
linear-assignment.c new file +9
-0+ }while (i1 != i); + } + +free (col); +free (pred); +free (d); +free (v); +free (free_row); +}
Re: [PATCH v3 01/20] linear-assignment: a function to solve least-cost assignment problems
Hi Gábor,
Quoted 39 lines · SZEDER Gábor
Quoted 23 lines
linear-assignment.c new file +17
-0@@ -0,0 +1,203 @@ +/* +* Based on: Jonker, R., & Volgenant, A. (1987). <i>A shortest augmenting path +* algorithm for dense and sparse linear assignment problems</i>. Computing, +* 38(4), 325-340. +*/ +#include "cache.h" +#include "linear-assignment.h" + +#define COST (column, row) cost[(column) + column_count * (row)] + +/* +* The parameter `cost` is the cost matrix: the cost to assign column j to row +* i is `cost[j + column_count * i]. +*/ +void compute_assignment (int column_count,int row_count,int *cost, +int *column2row,int *row2column) +{[...]
Quoted 13 lines
linear-assignment.c new file +13
-0+update: +/* updating of the column pieces */ +for (k =0 ; k < last; k++) { +int j1 = col[k]; + v[j1] += d[j1] - min; + } + +/* augmentation */ + do { + if (j < 0) + BUG("negative j: %d", j); + i = pred[j]; + column2row[j] = i;
Quoted 8 lines
linear-assignment.c line 194
Quoted 3 lines
linear-assignment.c new file +3
-0+ k = j; + j = row2column[i]; + row2column[i] = k;Coccinelle suggests using
SWAP(j, row2column[i])instead of the last three lines above.
It's more idiomatic, and it avoids (ab)using the 'k' variable (elsewhere used as loop variable) as a temporary variable.
Good point.
I audited the rest of the code in this file, and there are no more swap operations.
Thanks,
Dscho
PATCH v3 2/20 Introduce `range-diff` to compare iterations of a topic branch
In reply to Johannes Schindelin via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
This command does not do a whole lot so far, apart from showing a usage that is oddly similar to that of git tbdiff. And for a good reason:
the next commits will turn range-branch into a full-blown replacement for tbdiff.
At this point, we ignore tbdiff's color options, as they will all be implemented later using diff_options.
--- .gitignore | 1 + Makefile | 1 + builtin.h | 1 + builtin/range-diff.c | 25 +++++++++++++++++++++++++ command-list.txt | 1 + git.c | 1 + 6 files changed, 30 insertions(+) create mode 100644 builtin/range-diff.c
.gitignore +1 -0
@@ -113,6 +113,7 @@ /git-pull /git-push /git-quiltimport +/git-range-diff /git-read-tree /git-rebase /git-rebase--am
Makefile +1 -0
@@ -1059,6 +1059,7 @@ BUILTIN_OBJS += builtin/prune-packed.o BUILTIN_OBJS += builtin/prune.o BUILTIN_OBJS += builtin/pull.o BUILTIN_OBJS += builtin/push.o +BUILTIN_OBJS += builtin/range-diff.o BUILTIN_OBJS += builtin/read-tree.o BUILTIN_OBJS += builtin/rebase--helper.o BUILTIN_OBJS += builtin/receive-pack.o
builtin.h +1 -0
@@ -201,6 +201,7 @@ extern int cmd_prune(int argc, const char **argv, const char *prefix);extern int cmd_prune_packed (int argc,const char **argv,const char *prefix);extern int cmd_pull (int argc,const char **argv,const char *prefix);extern int cmd_push (int argc,const char **argv,const char *prefix); +extern int cmd_range_diff (int argc,const char **argv,const char *prefix);extern int cmd_read_tree (int argc,const char **argv,const char *prefix);extern int cmd_rebase__helper (int argc,const char **argv,const char *prefix);extern int cmd_receive_pack (int argc,const char **argv,const char *prefix);
builtin/range-diff.c new file +25 -0
@@ -0,0 +1,25 @@ +#include "cache.h" +#include "builtin.h" +#include "parse-options.h" + +static const char *const builtin_range_diff_usage[] = { +N_ ("git range-diff [<options>] <old-base>..<old-tip> <new-base>..<new-tip>" ), +N_ ("git range-diff [<options>] <old-tip>...<new-tip>" ), +N_ ("git range-diff [<options>] <base> <old-tip> <new-tip>" ), +NULL +}; + +int cmd_range_diff (int argc,const char **argv,const char *prefix) +{ +int creation_factor =60 ; +struct option options[] = { +OPT_INTEGER (0 ,"creation-factor" , &creation_factor, +N_ ("Percentage by which creation is weighted" )), +OPT_END () + }; + + argc =parse_options (argc, argv,NULL , options, + builtin_range_diff_usage,0 ); + +return 0 ; +}
command-list.txt +1 -0
@@ -139,6 +139,7 @@ git-prune-packed plumbingmanipulators git-pull mainporcelain remote git-push mainporcelain remote git-quiltimport foreignscminterface +git-range-diff mainporcelain git-read-tree plumbingmanipulators git-rebase mainporcelain history git-receive-pack synchelpers
git.c +1 -0
@@ -517,6 +517,7 @@ static struct cmd_struct commands[] = { { "prune-packed", cmd_prune_packed, RUN_SETUP }, { "pull", cmd_pull, RUN_SETUP | NEED_WORK_TREE }, { "push", cmd_push, RUN_SETUP }, + { "range-diff", cmd_range_diff, RUN_SETUP | USE_PAGER }, { "read-tree", cmd_read_tree, RUN_SETUP | SUPPORT_SUPER_PREFIX}, { "rebase--helper", cmd_rebase__helper, RUN_SETUP | NEED_WORK_TREE }, { "receive-pack", cmd_receive_pack },
Signature · 1 line
gitgitgadget
PATCH v3 3/20 range-diff: first rudimentary implementation
In reply to Johannes Schindelin via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
At this stage, git range-diff can determine corresponding commits of two related commit ranges. This makes use of the recently introduced implementation of the Hungarian algorithm.
The core of this patch is a straight port of the ideas of tbdiff, the apparently dormant project at https://github.com/trast/tbdiff.
The output does not at all match tbdiff's output yet, as this patch really concentrates on getting the patch matching part right.
Note: due to differences in the diff algorithm (tbdiff uses the Python module difflib, Git uses its xdiff fork), the cost matrix calculated by range-diff is different (but very similar) to the one calculated by tbdiff. Therefore, it is possible that they find different matching commits in corner cases (e.g. when a patch was split into two patches of roughly equal length).
--- Makefile | 1 + builtin/range-diff.c | 47 ++++++- range-diff.c | 307 +++++++++++++++++++++++++++++++++++++++++++ range-diff.h | 7 + 4 files changed, 359 insertions(+), 3 deletions(-) create mode 100644 range-diff.c create mode 100644 range-diff.h
Makefile +1 -0
@@ -921,6 +921,7 @@ LIB_OBJS += progress.o LIB_OBJS += prompt.o LIB_OBJS += protocol.o LIB_OBJS += quote.o +LIB_OBJS += range-diff.o LIB_OBJS += reachable.o LIB_OBJS += read-cache.o LIB_OBJS += reflog-walk.o
builtin/range-diff.c +44 -3
59 lines not on this page · raw message
range-diff.c new file +307 -0
307 lines not on this page · raw message
range-diff.h new file +7 -0
@@ -0,0 +1,7 @@ +#ifndef BRANCH_DIFF_H +#define BRANCH_DIFF_H + +int show_range_diff (const char *range1,const char *range2, +int creation_factor); + +#endif
Signature · 1 line
gitgitgadget
Re: [PATCH v3 03/20] range-diff: first rudimentary implementation
Quoted 3 lines · Johannes Schindelin via GitGitGadget
At this stage,
git range-diffcan determine corresponding commits of two related commit ranges. This makes use of the recently introduced implementation of the Hungarian algorithm.
Did you want s/Hungarian/Jonker-Volgenant/ here? (Not worth a re-roll.)
Quoted 7 lines
The core of this patch is a straight port of the ideas of tbdiff, the apparently dormant project at https://github.com/trast/tbdiff.
[...]Signed-off-by Johannes Schindelin <[email protected]>---
builtin/range-diff.c int cmd_range_diff(int argc, const char **argv, const char *prefix) · line 22
Quoted 7 lines
builtin/range-diff.c +4
-2@@ -17,9 +18,49 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix) +int res =0 ; +struct strbuf range1 =STRBUF_INIT , range2 =STRBUF_INIT ; - argc =parse_options (argc, argv,NULL , options, - builtin_range_diff_usage,0 ); + argc =parse_options (argc, argv,NULL , options, builtin_range_diff_usage, +0 );
This parse_options() change appears to be merely a re-wrapping of the line between patches 2 and 3.
builtin/range-diff.c int cmd_range_diff(int argc, const char **argv, const char *prefix) · line 34
Quoted 9 lines
builtin/range-diff.c +8
-1-return 0 ; +if (argc ==2 ) { +if (!strstr (argv[0 ],".." )) +warning (_ ("no .. in range: '%s'" ), argv[0 ]); +strbuf_addstr (&range1, argv[0 ]); + +if (!strstr (argv[1 ],".." )) +warning (_ ("no .. in range: '%s'" ), argv[1 ]); +strbuf_addstr (&range2, argv[1 ]);
Should these die() (like the "..." case below) rather than warning()? Warning and continuing doesn't seem like intended behavior. When I test this with on git.git and omit the "..", git sits for a long, long time consuming the CPU. I guess it's git-log'ing pretty much the entire history.
% GIT_TRACE=1 git range-diff v1 v2
warning: no .. in range: 'v1'
warning: no .. in range: 'v2'
trace: git log --no-color -p --no-merges --reverse \
--date-order --decorate=no --no-abbrev-commit v1
^C
%Quoted 30 lines
builtin/range-diff.c +9
-0+ }else if (argc ==3 ) { +strbuf_addf (&range1,"%s..%s" , argv[0 ], argv[1 ]); +strbuf_addf (&range2,"%s..%s" , argv[0 ], argv[2 ]); + }else if (argc ==1 ) { +const char *b =strstr (argv[0 ],"..." ), *a = argv[0 ]; +int a_len; + +if (!b) +die (_ ("single arg format requires a symmetric range" ));range-diff.c +19
-0@@ -0,0 +1,307 @@ +static int read_patches (const char *range,struct string_list *list) +{ +while (strbuf_getline (&line, in) !=EOF ) { +if (skip_prefix (line.buf,"commit " , &p)) { + [...] + in_header =1 ; +continue ; + } +if (starts_with (line.buf,"diff --git" )) { + in_header =0 ; + [...] + }else if (in_header) { +if (starts_with (line.buf,"Author: " )) { + [...] + }else if (starts_with (line.buf," " )) { + [...] + } +continue ; + }else if (starts_with (line.buf,"@@ " ))
range-diff.c line 29
Quoted 10 lines
range-diff.c +10
-0+strbuf_addstr (&buf,"@@" ); + else if (line.buf[0] && !starts_with(line.buf, "index ")) + /* + * A completely blank (not ' \n', which is context) + * line is not valid in a diff. We skip it + * silently, because this neatly handles the blank + * separator line between commits in git-log + * output. + */ + strbuf_addbuf(&buf, &line);
This comment had me confused for a bit since it doesn't seem to agree with the 'then' part of the 'if', but rather applies more to the 'else'. Had it been split into two parts (one for 'then' and one for 'else'), it might have been easier to digest. That is, something like:
else if (line.buf[0] && !starts_with(..., "index "))
/* A line we wish to keep. */
strbuf_addbuf(...);
else
/*
* A completely blank line between commits or
* or one in which we are otherwise not interested.
*/
continue;or something. Structuring it a bit differently might have helped, as well:
else if (!line.buf[0])
/* A completely blank line between commits. */
continue;
else if (starts_with(..., "index "))
/* A line in which we are not interested. */
continue;
else
strbuf_addbuf(&buf, &line);Not at all worth a re-roll.
range-diff.c line 112
Quoted 4 lines
range-diff.c +4
-0+else +continue ; +if (util) +string_list_append (list, buf.buf)->util = util;
So, the parser is grabbing each commit and shoving all the "interesting" information about the commit in a 'patch_util'. It grabs the OID, author, the commit message (indented), the "diff --git", "+++", "---" lines (but ignores "index" line), "@@" lines (but ignoring the gunk after "@@"), and all context and patch lines.
Looks good.
Quoted 7 lines
range-diff.c +7
-0+strbuf_release (&buf); + +if (finish_command (&cp)) +return -1 ; + +return 0 ; +}
Re: [PATCH v3 03/20] range-diff: first rudimentary implementation
Hi Eric,
Quoted 7 lines · Eric Sunshine
Quoted 3 lines · Johannes Schindelin via GitGitGadget
At this stage,
git range-diffcan determine corresponding commits of two related commit ranges. This makes use of the recently introduced implementation of the Hungarian algorithm.Did you want s/Hungarian/Jonker-Volgenant/ here? (Not worth a re-roll.)
It is worth a new iteration, and I'd rather say "linear assignment" than either Hungarian or Jonker-Volgenant. Thanks for pointing this out.
Quoted 10 lines
Quoted 10 lines
The core of this patch is a straight port of the ideas of tbdiff, the apparently dormant project at https://github.com/trast/tbdiff.
[...]Signed-off-by Johannes Schindelin <[email protected]>---
builtin/range-diff.c +2
-0@@ -17,9 +18,49 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix) +int res =0 ; +struct strbuf range1 =STRBUF_INIT , range2 =STRBUF_INIT ;
Quoted 7 lines
builtin/range-diff.c int cmd_range_diff(int argc, const char **argv, const char *prefix) · line 25
Quoted 4 lines
builtin/range-diff.c +2
-2- argc =parse_options (argc, argv,NULL , options, - builtin_range_diff_usage,0 ); + argc =parse_options (argc, argv,NULL , options, builtin_range_diff_usage, +0 );This
parse_options()change appears to be merely a re-wrapping of the line between patches 2 and 3.
True, and it is a bad change because it makes the line longer than 80 columns.
Fixed.
Quoted 10 lines
builtin/range-diff.c int cmd_range_diff(int argc, const char **argv, const char *prefix) · line 34
Quoted 9 lines
builtin/range-diff.c +8
-1-return 0 ; +if (argc ==2 ) { +if (!strstr (argv[0 ],".." )) +warning (_ ("no .. in range: '%s'" ), argv[0 ]); +strbuf_addstr (&range1, argv[0 ]); + +if (!strstr (argv[1 ],".." )) +warning (_ ("no .. in range: '%s'" ), argv[1 ]); +strbuf_addstr (&range2, argv[1 ]);
Quoted 5 lines
Should these
die()(like the "..." case below) rather thanwarning()? Warning and continuing doesn't seem like intended behavior. When I test this with on git.git and omit the "..", git sits for a long, long time consuming the CPU. I guess it's git-log'ing pretty much the entire history.
I had to go back to git-tbdiff.py to see how it handles this, and you are right: it should die().
Fixed.
(Technically, it is conceivable that some user wants to compare two independent commit histories, e.g. when a repository was imported from a different SCM two times, independently. I guess when that happens, we can always implement a range-diff --root <tip1> <tip2> or some such.)
Quoted 66 lines
% GIT_TRACE=1 git range-diff v1 v2 warning: no .. in range: 'v1' warning: no .. in range: 'v2' trace: git log --no-color -p --no-merges --reverse \ --date-order --decorate=no --no-abbrev-commit v1 ^C %Quoted 30 lines
builtin/range-diff.c +9
-0+ }else if (argc ==3 ) { +strbuf_addf (&range1,"%s..%s" , argv[0 ], argv[1 ]); +strbuf_addf (&range2,"%s..%s" , argv[0 ], argv[2 ]); + }else if (argc ==1 ) { +const char *b =strstr (argv[0 ],"..." ), *a = argv[0 ]; +int a_len; + +if (!b) +die (_ ("single arg format requires a symmetric range" ));range-diff.c +19
-0@@ -0,0 +1,307 @@ +static int read_patches (const char *range,struct string_list *list) +{ +while (strbuf_getline (&line, in) !=EOF ) { +if (skip_prefix (line.buf,"commit " , &p)) { + [...] + in_header =1 ; +continue ; + } +if (starts_with (line.buf,"diff --git" )) { + in_header =0 ; + [...] + }else if (in_header) { +if (starts_with (line.buf,"Author: " )) { + [...] + }else if (starts_with (line.buf," " )) { + [...] + } +continue ; + }else if (starts_with (line.buf,"@@ " ))range-diff.c line 29
Quoted 10 lines
range-diff.c +10
-0+strbuf_addstr (&buf,"@@" ); + else if (line.buf[0] && !starts_with(line.buf, "index ")) + /* + * A completely blank (not ' \n', which is context) + * line is not valid in a diff. We skip it + * silently, because this neatly handles the blank + * separator line between commits in git-log + * output. + */ + strbuf_addbuf(&buf, &line);This comment had me confused for a bit since it doesn't seem to agree with the 'then' part of the 'if', but rather applies more to the 'else'. Had it been split into two parts (one for 'then' and one for 'else'), it might have been easier to digest. That is, something like:
else if (line.buf[0] && !starts_with(..., "index ")) /* A line we wish to keep. */ strbuf_addbuf(...); else /* * A completely blank line between commits or * or one in which we are otherwise not interested. */ continue;or something. Structuring it a bit differently might have helped, as well:
Quoted 8 lines
else if (!line.buf[0])
/* A completely blank line between commits. */
continue;
else if (starts_with(..., "index "))
/* A line in which we are not interested. */
continue;
else
strbuf_addbuf(&buf, &line);I like this much better, too.
Quoted 1 line
Not at all worth a re-roll.
I'll have to send a new iteration anyway, after digging into ws.c, I think.
Also: I had this idea that dimming the "old" diff would make a ton of sense, so I want to try that.
Quoted 5 lines
range-diff.c line 112
Quoted 4 lines
range-diff.c +4
-0+else +continue ; +if (util) +string_list_append (list, buf.buf)->util = util;
Quoted 7 lines
So, the parser is grabbing each commit and shoving all the "interesting" information about the commit in a '
patch_util'. It grabs the OID, author, the commit message (indented), the "diff --git", "+++", "---" lines (but ignores "index" line), "@@" lines (but ignoring the gunk after "@@"), and all context and patch lines.Looks good.
Correct.
Quoted 7 lines
range-diff.c line 119
Quoted 7 lines
range-diff.c +7
-0+strbuf_release (&buf); + +if (finish_command (&cp)) +return -1 ; + +return 0 ; +}
Thank you for your suggestions!
Dscho
PATCH v3 4/20 range-diff: improve the order of the shown commits
In reply to Johannes Schindelin via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
This patch lets git range-diff use the same order as tbdiff.
The idea is simple: for left-to-right readers, it is natural to assume that the git range-diff is performed between an older vs a newer version of the branch. As such, the user is probably more interested in the question "where did this come from?" rather than "where did that one go?".
To that end, we list the commits in the order of the second commit range ("the newer version"), inserting the unmatched commits of the first commit range as soon as all their predecessors have been shown.
--- range-diff.c | 59 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 19 deletions(-)
range-diff.c +40 -19
@@ -12,7 +12,7 @@ struct patch_util {struct hashmap_entry e;const char *diff, *patch; -int i; +int i, shown;int diffsize;size_t diff_offset;/* the index of the matching item in the other branch, or -1 */
@@ -256,28 +256,49 @@ static const char *short_oid(struct patch_util *util)static void output (struct string_list *a,struct string_list *b) { -int i; - -for (i =0 ; i < b->nr; i++) { -struct patch_util *util = b->items[i].util, *prev; +int i =0 , j =0 ; + +/* +* We assume the user is really more interested in the second argument +* ("newer" version). To that end, we print the output in the order of +* the RHS (the `b` parameter). To put the LHS (the `a` parameter) +* commits that are no longer in the RHS into a good place, we place +* them once we have shown all of their predecessors in the LHS. +*/ + +while (i < a->nr || j < b->nr) { +struct patch_util *a_util, *b_util; + a_util = i < a->nr ? a->items[i].util :NULL ; + b_util = j < b->nr ? b->items[j].util :NULL ; + +/* Skip all the already-shown commits from the LHS. */ +while (i < a->nr && a_util->shown) + a_util = ++i < a->nr ? a->items[i].util :NULL ; + +/* Show unmatched LHS commit whose predecessors were shown. */ +if (i < a->nr && a_util->matching <0 ) { +printf ("%d: %s < -: --------\n" , + i +1 ,short_oid (a_util)); + i++; +continue ; + } -if (util->matching <0 ) +/* Show unmatched RHS commits. */ +while (j < b->nr && b_util->matching <0 ) {printf ("-: -------- > %d: %s\n" , - i +1 ,short_oid (util)); -else { - prev = a->items[util->matching].util; -printf ("%d: %s ! %d: %s\n" , - util->matching +1 ,short_oid (prev), - i +1 ,short_oid (util)); + j +1 ,short_oid (b_util)); + b_util = ++j < b->nr ? b->items[j].util :NULL ; } - } - -for (i =0 ; i < a->nr; i++) { -struct patch_util *util = a->items[i].util; -if (util->matching <0 ) -printf ("%d: %s < -: --------\n" , - i +1 ,short_oid (util)); +/* Show matching LHS/RHS pair. */ +if (j < b->nr) { + a_util = a->items[b_util->matching].util; +printf ("%d: %s ! %d: %s\n" , + b_util->matching +1 ,short_oid (a_util), + j +1 ,short_oid (b_util)); + a_util->shown =1 ; + j++; + } } }
Signature · 1 line
gitgitgadget
PATCH v3 5/20 range-diff: also show the diff between patches
In reply to Johannes Schindelin via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
Just like tbdiff, we now show the diff between matching patches. This is a "diff of two diffs", so it can be a bit daunting to read for the beginner.
An alternative would be to display an interdiff, i.e. the hypothetical diff which is the result of first reverting the old diff and then applying the new diff.
Especially when rebasing often, an interdiff is often not feasible,
though: if the old diff cannot be applied in reverse (due to a moving upstream), an interdiff can simply not be inferred.
This commit brings range-diff closer to feature parity with regard to tbdiff.
To make git range-diff respect e.g. color.diff.* settings, we have to adjust git_branch_config() accordingly.
Note: while we now parse diff options such as --color, the effect is not yet the same as in tbdiff, where also the commit pairs would be colored. This is left for a later commit.
Note also: while tbdiff accepts the --no-patches option to suppress these diffs between patches, we prefer the -s option that is automatically supported via our use of diff_opt_parse().
--- builtin/range-diff.c | 25 +++++++++++++++++++++---- range-diff.c | 34 +++++++++++++++++++++++++++++++--- range-diff.h | 4 +++- 3 files changed, 55 insertions(+), 8 deletions(-)
builtin/range-diff.c +21 -4
@@ -2,6 +2,7 @@#include "builtin.h" #include "parse-options.h" #include "range-diff.h" +#include "config.h" static const char *const builtin_range_diff_usage[] = {N_ ("git range-diff [<options>] <old-base>..<old-tip> <new-base>..<new-tip>" ),
@@ -13,16 +14,31 @@ NULLint cmd_range_diff (int argc,const char **argv,const char *prefix) {int creation_factor =60 ; +struct diff_options diffopt = {NULL };struct option options[] = {OPT_INTEGER (0 ,"creation-factor" , &creation_factor,N_ ("Percentage by which creation is weighted" )),OPT_END () }; -int res =0 ; +int i, j, res =0 ;struct strbuf range1 =STRBUF_INIT , range2 =STRBUF_INIT ; - argc =parse_options (argc, argv,NULL , options, builtin_range_diff_usage, -0 ); +git_config (git_diff_ui_config,NULL ); + +diff_setup (&diffopt); + diffopt.output_format =DIFF_FORMAT_PATCH ; + + argc =parse_options (argc, argv,NULL , options, + builtin_range_diff_usage,PARSE_OPT_KEEP_UNKNOWN ); + +for (i = j =0 ; i < argc; i++) { +int c =diff_opt_parse (&diffopt, argv + i, argc - i, prefix); + +if (!c) + argv[j++] = argv[i]; + } + argc = j; +diff_setup_done (&diffopt);if (argc ==2 ) {if (!strstr (argv[0 ],".." ))
@@ -57,7 +73,8 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix)usage_with_options (builtin_range_diff_usage, options); } - res =show_range_diff (range1.buf, range2.buf, creation_factor); + res =show_range_diff (range1.buf, range2.buf, creation_factor, + &diffopt);strbuf_release (&range1);strbuf_release (&range2);
range-diff.c +31 -3
@@ -6,6 +6,7 @@#include "hashmap.h" #include "xdiff-interface.h" #include "linear-assignment.h" +#include "diffcore.h" struct patch_util {/* For the search for an exact match */
@@ -254,7 +255,31 @@ static const char *short_oid(struct patch_util *util)return find_unique_abbrev (&util->oid,DEFAULT_ABBREV ); } -static void output (struct string_list *a,struct string_list *b) +static struct diff_filespec *get_filespec (const char *name,const char *p) +{ +struct diff_filespec *spec =alloc_filespec (name); + +fill_filespec (spec, &null_oid,0 ,0644 ); + spec->data = (char *)p; + spec->size =strlen (p); + spec->should_munmap =0 ; + spec->is_stdin =1 ; + +return spec; +} + +static void patch_diff (const char *a,const char *b, +struct diff_options *diffopt) +{ +diff_queue (&diff_queued_diff, +get_filespec ("a" , a),get_filespec ("b" , b)); + +diffcore_std (diffopt); +diff_flush (diffopt); +} + +static void output (struct string_list *a,struct string_list *b, +struct diff_options *diffopt) {int i =0 , j =0 ;
@@ -296,6 +321,9 @@ static void output(struct string_list *a, struct string_list *b)printf ("%d: %s ! %d: %s\n" , b_util->matching +1 ,short_oid (a_util), j +1 ,short_oid (b_util)); +if (!(diffopt->output_format &DIFF_FORMAT_NO_OUTPUT )) +patch_diff (a->items[b_util->matching].string, + b->items[j].string, diffopt); a_util->shown =1 ; j++; }
@@ -303,7 +331,7 @@ static void output(struct string_list *a, struct string_list *b) }int show_range_diff (const char *range1,const char *range2, -int creation_factor) +int creation_factor,struct diff_options *diffopt) {int res =0 ;
@@ -318,7 +346,7 @@ int show_range_diff(const char *range1, const char *range2,if (!res) {find_exact_matches (&branch1, &branch2);get_correspondences (&branch1, &branch2, creation_factor); -output (&branch1, &branch2); +output (&branch1, &branch2, diffopt); }string_list_clear (&branch1,1 );
range-diff.h +3 -1
@@ -1,7 +1,9 @@#ifndef BRANCH_DIFF_H #define BRANCH_DIFF_H +#include "diff.h" +int show_range_diff (const char *range1,const char *range2, -int creation_factor); +int creation_factor,struct diff_options *diffopt);#endif
Signature · 1 line
gitgitgadget
PATCH v3 6/20 range-diff: right-trim commit messages
In reply to Johannes Schindelin via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
When comparing commit messages, we need to keep in mind that they are indented by four spaces. That is, empty lines are no longer empty, but have "trailing whitespace". When displaying them in color, that results in those nagging red lines.
Let's just right-trim the lines in the commit message, it's not like trailing white-space in the commit messages are important enough to care about in git range-diff.
--- range-diff.c | 1 + 1 file changed, 1 insertion(+)
range-diff.c +1 -0
@@ -85,6 +85,7 @@ static int read_patches(const char *range, struct string_list *list)strbuf_addbuf (&buf, &line);strbuf_addstr (&buf,"\n\n" ); }else if (starts_with (line.buf," " )) { +strbuf_rtrim (&line);strbuf_addbuf (&buf, &line);strbuf_addch (&buf,'\n' ); }
Signature · 1 line
gitgitgadget
PATCH v3 7/20 range-diff: indent the diffs just like tbdiff
In reply to Johannes Schindelin via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
The main information in the range-diff view comes from the list of matching and non-matching commits, the diffs are additional information. Indenting them helps with the reading flow.
--- builtin/range-diff.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
builtin/range-diff.c +10 -0
@@ -11,6 +11,11 @@ N_("git range-diff [<options>] <base> <old-tip> <new-tip>"), NULL }; +static struct strbuf *output_prefix_cb (struct diff_options *opt,void *data) +{ +return data; +} +int cmd_range_diff (int argc,const char **argv,const char *prefix) {int creation_factor =60 ;
@@ -21,12 +26,16 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix) OPT_END() };int i, j, res =0 ; +struct strbuf four_spaces =STRBUF_INIT ;struct strbuf range1 =STRBUF_INIT , range2 =STRBUF_INIT ;git_config (git_diff_ui_config,NULL );diff_setup (&diffopt); diffopt.output_format =DIFF_FORMAT_PATCH ; + diffopt.output_prefix = output_prefix_cb; +strbuf_addstr (&four_spaces," " ); + diffopt.output_prefix_data = &four_spaces; argc =parse_options (argc, argv,NULL , options, builtin_range_diff_usage,PARSE_OPT_KEEP_UNKNOWN );
@@ -78,6 +87,7 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix)strbuf_release (&range1);strbuf_release (&range2); +strbuf_release (&four_spaces);return res; }
Signature · 1 line
gitgitgadget
PATCH v3 8/20 range-diff: suppress the diff headers
In reply to Johannes Schindelin via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
When showing the diff between corresponding patches of the two branch versions, we have to make up a fake filename to run the diff machinery.
That filename does not carry any meaningful information, hence tbdiff suppresses it. So we should, too.
--- builtin/range-diff.c | 1 + diff.c | 5 ++++- diff.h | 1 + 3 files changed, 6 insertions(+), 1 deletion(-)
builtin/range-diff.c +1 -0
@@ -33,6 +33,7 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix)diff_setup (&diffopt); diffopt.output_format =DIFF_FORMAT_PATCH ; + diffopt.flags.suppress_diff_headers =1 ; diffopt.output_prefix = output_prefix_cb;strbuf_addstr (&four_spaces," " ); diffopt.output_prefix_data = &four_spaces;
diff.c +4 -1
@@ -3189,13 +3189,16 @@ static void builtin_diff(const char *name_a,memset (&xpp,0 ,sizeof (xpp));memset (&xecfg,0 ,sizeof (xecfg));memset (&ecbdata,0 ,sizeof (ecbdata)); +if (o->flags.suppress_diff_headers) + lbl[0 ] =NULL ; ecbdata.label_path = lbl; ecbdata.color_diff =want_color (o->use_color); ecbdata.ws_rule =whitespace_rule (name_b);if (ecbdata.ws_rule &WS_BLANK_AT_EOF )check_blank_at_eof (&mf1, &mf2, &ecbdata); ecbdata.opt = o; - ecbdata.header = header.len ? &header :NULL ; +if (header.len && !o->flags.suppress_diff_headers) + ecbdata.header = &header; xpp.flags = o->xdl_opts; xpp.anchors = o->anchors; xpp.anchors_nr = o->anchors_nr;
diff.h +1 -0
@@ -94,6 +94,7 @@ struct diff_flags { unsigned funccontext:1; unsigned default_follow_renames:1; unsigned stat_with_summary:1; + unsigned suppress_diff_headers:1; }; static inline void diff_flags_or(struct diff_flags *a,
Signature · 1 line
gitgitgadget
PATCH v3 9/20 range-diff: adjust the output of the commit pairs
In reply to Johannes Schindelin via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
This change brings git range-diff yet another step closer to feature parity with tbdiff: it now shows the oneline, too, and indicates with = when the commits have identical diffs.
--- range-diff.c | 66 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 57 insertions(+), 9 deletions(-)
range-diff.c +57 -9
@@ -7,6 +7,8 @@#include "xdiff-interface.h" #include "linear-assignment.h" #include "diffcore.h" +#include "commit.h" +#include "pretty.h" struct patch_util {/* For the search for an exact match */
@@ -251,9 +253,57 @@ static void get_correspondences(struct string_list *a, struct string_list *b,free (b2a); } -static const char *short_oid (struct patch_util *util) +static void output_pair_header (struct strbuf *buf, +struct patch_util *a_util, +struct patch_util *b_util) { -return find_unique_abbrev (&util->oid,DEFAULT_ABBREV ); +static char *dashes; +struct object_id *oid = a_util ? &a_util->oid : &b_util->oid; +struct commit *commit; + +if (!dashes) { +char *p; + + dashes =xstrdup (find_unique_abbrev (oid,DEFAULT_ABBREV )); +for (p = dashes; *p; p++) + *p ='-' ; + } + +strbuf_reset (buf); +if (!a_util) +strbuf_addf (buf,"-: %s " , dashes); +else +strbuf_addf (buf,"%d: %s " , a_util->i +1 , +find_unique_abbrev (&a_util->oid,DEFAULT_ABBREV )); + +if (!a_util) +strbuf_addch (buf,'>' ); +else if (!b_util) +strbuf_addch (buf,'<' ); +else if (strcmp (a_util->patch, b_util->patch)) +strbuf_addch (buf,'!' ); +else +strbuf_addch (buf,'=' ); + +if (!b_util) +strbuf_addf (buf," -: %s" , dashes); +else +strbuf_addf (buf," %d: %s" , b_util->i +1 , +find_unique_abbrev (&b_util->oid,DEFAULT_ABBREV )); + + commit =lookup_commit_reference (oid); +if (commit) { +const char *commit_buffer =get_commit_buffer (commit,NULL ); +const char *subject; + +find_commit_subject (commit_buffer, &subject); +strbuf_addch (buf,' ' ); +format_subject (buf, subject," " ); +unuse_commit_buffer (commit, commit_buffer); + } +strbuf_addch (buf,'\n' ); + +fwrite (buf->buf, buf->len,1 , stdout); }static struct diff_filespec *get_filespec (const char *name,const char *p)
@@ -282,6 +332,7 @@ static void patch_diff(const char *a, const char *b,static void output (struct string_list *a,struct string_list *b,struct diff_options *diffopt) { +struct strbuf buf =STRBUF_INIT ;int i =0 , j =0 ;/*
@@ -303,25 +354,21 @@ static void output(struct string_list *a, struct string_list *b,/* Show unmatched LHS commit whose predecessors were shown. */ if (i < a->nr && a_util->matching <0 ) { -printf ("%d: %s < -: --------\n" , - i +1 ,short_oid (a_util)); +output_pair_header (&buf, a_util,NULL ); i++;continue ; }/* Show unmatched RHS commits. */ while (j < b->nr && b_util->matching <0 ) { -printf ("-: -------- > %d: %s\n" , - j +1 ,short_oid (b_util)); +output_pair_header (&buf,NULL , b_util); b_util = ++j < b->nr ? b->items[j].util :NULL ; }/* Show matching LHS/RHS pair. */ if (j < b->nr) { a_util = a->items[b_util->matching].util; -printf ("%d: %s ! %d: %s\n" , - b_util->matching +1 ,short_oid (a_util), - j +1 ,short_oid (b_util)); +output_pair_header (&buf, a_util, b_util);if (!(diffopt->output_format &DIFF_FORMAT_NO_OUTPUT ))patch_diff (a->items[b_util->matching].string, b->items[j].string, diffopt);
@@ -329,6 +376,7 @@ static void output(struct string_list *a, struct string_list *b, j++; } } +strbuf_release (&buf); } int show_range_diff(const char *range1, const char *range2,
Signature · 1 line
gitgitgadget
Re: [PATCH v3 09/20] range-diff: adjust the output of the commit pairs
Quoted 11 lines · Johannes Schindelin via GitGitGadget
This change brings
git range-diffyet another step closer to feature parity with tbdiff: it now shows the oneline, too, and indicates with=when the commits have identical diffs.Signed-off-by Johannes Schindelin <[email protected]>---
range-diff.c +3
-0@@ -251,9 +253,57 @@ static void get_correspondences(struct string_list *a, struct string_list *b, +static void output_pair_header (struct strbuf *buf, +struct patch_util *a_util, +struct patch_util *b_util)
range-diff.c static void get_correspondences(struct string_list *a, struct string_list *b, · line 264
Quoted 10 lines
range-diff.c +8
-1{ -return find_unique_abbrev (&util->oid,DEFAULT_ABBREV ); +static char *dashes; +struct object_id *oid = a_util ? &a_util->oid : &b_util->oid; +struct commit *commit; + +if (!dashes) { +char *p; + + dashes =xstrdup (find_unique_abbrev (oid,DEFAULT_ABBREV ));
It's nice to see that the bulk of the range-diff functionality has been libified in this re-roll (residing in range-diff.c rather than builtin/range-diff.c as in earlier versions), so it's somewhat surprising to see libified code holding onto the 'dashes' buffer like this in a static variable. An alternative would have been for the caller to pass in the same buffer to output_pair_header() for re-use, and then dispose of it at the end of processing.
range-diff.c static void get_correspondences(struct string_list *a, struct string_list *b, · line 272
Quoted 5 lines
range-diff.c +5
-0+for (p = dashes; *p; p++) + *p ='-' ; + } + +strbuf_reset (buf);
...much like 'buf' is allocated by the caller, passed in and re-used for each invocation, then released by the caller at the end.
Re: [PATCH v3 09/20] range-diff: adjust the output of the commit pairs
Hi Eric,
Quoted 18 lines · Eric Sunshine
Quoted 16 lines · Johannes Schindelin via GitGitGadget
This change brings
git range-diffyet another step closer to feature parity with tbdiff: it now shows the oneline, too, and indicates with=when the commits have identical diffs.Signed-off-by Johannes Schindelin <[email protected]>---
range-diff.c +6
-1@@ -251,9 +253,57 @@ static void get_correspondences(struct string_list *a, struct string_list *b, +static void output_pair_header (struct strbuf *buf, +struct patch_util *a_util, +struct patch_util *b_util) { -return find_unique_abbrev (&util->oid,DEFAULT_ABBREV ); +static char *dashes; +struct object_id *oid = a_util ? &a_util->oid : &b_util->oid; +struct commit *commit;
Quoted 8 lines
range-diff.c static void get_correspondences(struct string_list *a, struct string_list *b, · line 267
Quoted 5 lines
range-diff.c +5
-0+ +if (!dashes) { +char *p; + + dashes =xstrdup (find_unique_abbrev (oid,DEFAULT_ABBREV ));It's nice to see that the bulk of the range-diff functionality has been libified in this re-roll (residing in
range-diff.crather than
Can we *please* stop calling it "re-roll"? Thanks.
(Or are you really "never gonna give you up, never gonna let you down"?)
Quoted 5 lines
builtin/range-diff.cas in earlier versions), so it's somewhat surprising to see libified code holding onto the 'dashes' buffer like this in a static variable. An alternative would have been for the caller to pass in the same buffer tooutput_pair_header()for re-use, and then dispose of it at the end of processing.
Sure, to be honest, I had completely forgotten about what I did there, and had to read up on it to fix it.
Quoted 9 lines
range-diff.c static void get_correspondences(struct string_list *a, struct string_list *b, · line 272
Quoted 6 lines
range-diff.c +5
-0+for (p = dashes; *p; p++) + *p ='-' ; + } + +strbuf_reset (buf);...much like 'buf' is allocated by the caller, passed in and re-used for each invocation, then released by the caller at the end.
Yep, I now pass in another strbuf, dashes.
Thanks,
Dscho
Re: [PATCH v3 09/20] range-diff: adjust the output of the commit pairs
Hi Johannes,
Quoted 4 lines
Quoted 2 lines
It's nice to see that the bulk of the range-diff functionality has been libified in this re-roll (residing in
range-diff.crather thanCan we *please* stop calling it "re-roll"? Thanks.
Fun fact of the day:
First appearance of "reroll" in the public archive is (09 Dec 2007)
https://public-inbox.org/git/[email protected]/
which is predated by "re-roll" (05 May 2006)
https://public-inbox.org/git/[email protected]/
Stefan
Re: [PATCH v3 09/20] range-diff: adjust the output of the commit pairs
Hi Stefan,
Quoted 11 lines · Stefan Beller
Quoted 4 lines
Quoted 2 lines
It's nice to see that the bulk of the range-diff functionality has been libified in this re-roll (residing in
range-diff.crather thanCan we *please* stop calling it "re-roll"? Thanks.
Fun fact of the day:
First appearance of "reroll" in the public archive is (09 Dec 2007)
https://public-inbox.org/git/[email protected]/
which is predated by "re-roll" (05 May 2006)
https://public-inbox.org/git/[email protected]/
Real fun fact of the day:
https://en.wiktionary.org/wiki/reroll says
Verb
reroll (third-person singular simple present rerolls, present participle rerolling, simple past and past participle rerolled)
1. To roll again.
A player who rolls two sixes can reroll the dice for an additional turn.
2. (programming) To convert (an unrolled instruction sequence) back into a loop. quotations ▼
Noun
reroll (plural rerolls)
(dice games) A situation in the rules of certain dice games where a player is given the option to reroll an undesirable roll of the dice.
You will notice how this does not list *any* hint at referring to something that Junio calls "reroll".
Likewise, I have to admit that Wiktionary's idea of an "iteration" disagrees with *my* use of the term.
The correct term would be "revision"
(https://en.wiktionary.org/wiki/revision). But we, the core Git contributors, in our collective infinite wisdom, chose to use that term as yet another way to refer to a commit [*1*].
So we got it all wrong, believe it or not.
Ciao,
Dscho
Footnote *1*: https://en.wiktionary.org/wiki/commit#Noun does not even bother to acknowledge our use of referring to a snapshot of a source code base as a "commit".
Re: [PATCH v3 09/20] range-diff: adjust the output of the commit pairs
Quoted 4 lines
1. To roll again.
A player who rolls two sixes can reroll the dice for an additional turn.
This is where I had my AHA moment!
(Consider my software development process as chaotic as a dice roll So rerolling is really just rolling the dice again to "get my patch accepted" ;-)
Quoted 2 lines
2. (programming) To convert (an unrolled instruction sequence) back into a loop. quotations ▼
We do not have unrolled loops?
This was good back in the day where the cost of each instruction weighted heavy on the CPU, such that the JMPs that are needed (and the loop variable check that might have had a bad branch prediction) for the loop were slowing down the execution.
Nowadays (when I was studying 5 years ago) the branch prediction and individual instruction execution are really good, but the bottleneck that I measured (when I had a lot of time at my disposal and attending a class/project on micro architectures), was the CPU instruction cache size, i.e. loop unrolling made the code *slower* than keeping tight loops loaded in memory.
https://stackoverflow.com/questions/24196076/is-gcc-loop-unrolling-flag-really-effective
Quoted 10 lines
Noun
reroll (plural rerolls)
(dice games) A situation in the rules of certain dice games where a player is given the option to reroll an undesirable roll of the dice.
You will notice how this does not list *any* hint at referring to something that Junio calls "reroll".
We have undesirable patches that were 'rolled' onto the mailing list, so they have to be rerolled?
Quoted 3 lines
Footnote *1*: https://en.wiktionary.org/wiki/commit#Noun does not even bother to acknowledge our use of referring to a snapshot of a source code base as a "commit".
When Git was a content addressable file system, a commit was precisely "a database transaction, [...] making it a permanent change."
Side note:
I was just giving a talk to my colleagues about diff aglorithms (and eventually describing a bug in the histogram diff algorithm) and we got really riled up with "Longest Common Subsequence", as the mathematical definition is different than what the code or I (after studying the code) had in mind.
Naming things is hard, and sometimes the collective wisdom got it wrong, but changing it would be very costly in the short/medium term.
Another note about "rolling things": At $DAYJOB I review changes that are committed to the another revision control system w.r.t. its compliance of open source licenses (hence I am exposed to a lot of different projects), and some of those changes are titled "Roll up to version $X" which I found strange, but knew what was meant.
Stefan
Re: [PATCH v3 09/20] range-diff: adjust the output of the commit pairs
Hi Stefan,
Quoted 9 lines · Stefan Beller
Quoted 4 lines
1. To roll again.
A player who rolls two sixes can reroll the dice for an additional turn.
This is where I had my AHA moment!
(Consider my software development process as chaotic as a dice roll So rerolling is really just rolling the dice again to "get my patch accepted" ;-)
Wouldn't that be nice? But you only get to reroll if you had two sixes. Tough luck for you, Stefan.
Quoted 4 lines
Quoted 2 lines
2. (programming) To convert (an unrolled instruction sequence) back into a loop. quotations ▼
We do not have unrolled loops?
When resending patch series? *rolls eyes*
Quoted 41 lines
This was good back in the day where the cost of each instruction weighted heavy on the CPU, such that the JMPs that are needed (and the loop variable check that might have had a bad branch prediction) for the loop were slowing down the execution.
Nowadays (when I was studying 5 years ago) the branch prediction and individual instruction execution are really good, but the bottleneck that I measured (when I had a lot of time at my disposal and attending a class/project on micro architectures), was the CPU instruction cache size, i.e. loop unrolling made the code *slower* than keeping tight loops loaded in memory.
https://stackoverflow.com/questions/24196076/is-gcc-loop-unrolling-flag-really-effectiveQuoted 10 lines
Noun
reroll (plural rerolls)
(dice games) A situation in the rules of certain dice games where a player is given the option to reroll an undesirable roll of the dice.
You will notice how this does not list *any* hint at referring to something that Junio calls "reroll".
We have undesirable patches that were 'rolled' onto the mailing list, so they have to be rerolled?
Quoted 3 lines
Footnote *1*: https://en.wiktionary.org/wiki/commit#Noun does not even bother to acknowledge our use of referring to a snapshot of a source code base as a "commit".
When Git was a content addressable file system, a commit was precisely "a database transaction, [...] making it a permanent change."
Side note:
I was just giving a talk to my colleagues about diff aglorithms (and eventually describing a bug in the histogram diff algorithm) and we got really riled up with "Longest Common Subsequence", as the mathematical definition is different than what the code or I (after studying the code) had in mind.
Quoted 3 lines
Naming things is hard, and sometimes the collective wisdom got it wrong, but changing it would be very costly in the short/medium term.
My point is not that naming is hard. But picking names that are *different* from what is established nomenclature is... unwise.
In this case, it makes an already unnecessarily awkward code contribution process even more unnecessarily uninviting.
Quoted 6 lines
Another note about "rolling things": At $DAYJOB I review changes that are committed to the another revision control system w.r.t. its compliance of open source licenses (hence I am exposed to a lot of different projects), and some of those changes are titled "Roll up to version $X" which I found strange, but knew what was meant.
To "roll up" is, as far as this non-native speaker can tell, an established way to express this action.
In short: nothing you wrote can adequately defend why the Git project chooses to confuse new contributors seemingly on purpose.
Ciao,
Dscho
PATCH v3 10/20 range-diff: do not show "function names" in hunk headers
In reply to Johannes Schindelin via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
We are comparing complete, formatted commit messages with patches. There are no function names here, so stop looking for them.
--- range-diff.c | 6 ++++++ 1 file changed, 6 insertions(+)
range-diff.c +6 -0
@@ -9,6 +9,7 @@#include "diffcore.h" #include "commit.h" #include "pretty.h" +#include "userdiff.h" struct patch_util {/* For the search for an exact match */
@@ -306,6 +307,10 @@ static void output_pair_header(struct strbuf *buf,fwrite (buf->buf, buf->len,1 , stdout); } +static struct userdiff_driver no_func_name = { + .funcname = {"$^" ,0 } +}; +static struct diff_filespec *get_filespec (const char *name,const char *p) {struct diff_filespec *spec =alloc_filespec (name);
@@ -315,6 +320,7 @@ static struct diff_filespec *get_filespec(const char *name, const char *p) spec->size =strlen (p); spec->should_munmap =0 ; spec->is_stdin =1 ; + spec->driver = &no_func_name;return spec; }
Signature · 1 line
gitgitgadget
PATCH v3 11/20 range-diff: add tests
In reply to Johannes Schindelin via GitGitGadgetFrom: Thomas Rast <[email protected]>
These are essentially lifted from https://github.com/trast/tbdiff, with light touch-ups to account for the command now being an option of git branch.
Apart from renaming tbdiff to range-diff, only one test case needed to be adjusted: 11 - 'changed message'.
The underlying reason it had to be adjusted is that diff generation is sometimes ambiguous. In this case, a comment line and an empty line are added, but it is ambiguous whether they were added after the existing empty line, or whether an empty line and the comment line are added *before* the existing empty line. And apparently xdiff picks a different option here than Python's difflib.
--- t/.gitattributes | 1 + t/t3206-range-diff.sh | 145 ++++++++++ t/t3206/history.export | 604 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 750 insertions(+) create mode 100755 t/t3206-range-diff.sh create mode 100644 t/t3206/history.export
t/.gitattributes +1 -0
@@ -18,5 +18,6 @@ t[0-9][0-9][0-9][0-9]/* -whitespace /t5515/* eol=lf /t556x_common eol=lf /t7500/* eol=lf +/t7910/* eol=lf /t8005/*.txt eol=lf /t9*/*.dump eol=lf
t/t3206-range-diff.sh new file +145 -0
145 lines not on this page · raw message
t/t3206/history.export new file +604 -0
604 lines not on this page · raw message
Signature · 1 line
gitgitgadget
Re: [PATCH v3 11/20] range-diff: add tests
Quoted 3 lines · Thomas Rast via GitGitGadget
These are essentially lifted from https://github.com/trast/tbdiff, with light touch-ups to account for the command now being an option of
git branch.
The "option of git branch" mention is outdated. Perhaps just drop everything after "...touch-ups" (or mention "range-diff" instead).
t/.gitattributes t[0-9][0-9][0-9][0-9]/* -whitespace · line 19
Quoted 9 lines
Apart from renaming
tbdifftorange-diff, only one test case needed to be adjusted: 11 - 'changed message'.[...]Signed-off-by Johannes Schindelin <[email protected]>---
t/.gitattributes +1
-0@@ -18,5 +18,6 @@ t[0-9][0-9][0-9][0-9]/* -whitespace /t7500/* eol=lf +/t7910/* eol=lf
Does this need to be changed to t3206?
Quoted 6 lines
t/.gitattributes +0
-0/t8005/*.txt eol=lft/t3206-range-diff.sh new file +0
-0
Re: [PATCH v3 11/20] range-diff: add tests
Hi Eric,
Quoted 8 lines · Eric Sunshine
Quoted 3 lines · Thomas Rast via GitGitGadget
These are essentially lifted from https://github.com/trast/tbdiff, with light touch-ups to account for the command now being an option of
git branch.The "option of
git branch" mention is outdated. Perhaps just drop everything after "...touch-ups" (or mention "range-diff" instead).
Ah, the line break made my grep fail :-(
Quoted 11 lines
t/.gitattributes t[0-9][0-9][0-9][0-9]/* -whitespace · line 19
Quoted 9 lines
Apart from renaming
tbdifftorange-diff, only one test case needed to be adjusted: 11 - 'changed message'.[...]Signed-off-by Johannes Schindelin <[email protected]>---
t/.gitattributes +1
-0@@ -18,5 +18,6 @@ t[0-9][0-9][0-9][0-9]/* -whitespace /t7500/* eol=lf +/t7910/* eol=lfDoes this need to be changed to t3206?
Absolutely.
Quoted 7 lines
t/t3206-range-diff.sh
Quoted 7 lines
t/.gitattributes +0
-0/t8005/*.txt eol=lft/t3206-range-diff.sh new file +0
-0
Thanks for your thorough review,
Dscho
PATCH v3 12/20 range-diff: use color for the commit pairs
In reply to Johannes Schindelin via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
Arguably the most important part of git range-diff's output is the list of commits in the two branches, together with their relationships.
For that reason, tbdiff introduced color-coding that is pretty intuitive, especially for unchanged patches (all dim yellow, like the first line in git show's output) vs modified patches (old commit is red, new commit is green). Let's imitate that color scheme.
--- range-diff.c | 47 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 13 deletions(-)
range-diff.c +34 -13
@@ -254,13 +254,19 @@ static void get_correspondences(struct string_list *a, struct string_list *b,free (b2a); } -static void output_pair_header (struct strbuf *buf, +static void output_pair_header (struct diff_options *diffopt,struct strbuf *buf,struct patch_util *a_util,struct patch_util *b_util) {static char *dashes;struct object_id *oid = a_util ? &a_util->oid : &b_util->oid;struct commit *commit; +char status; +const char *color_reset =diff_get_color_opt (diffopt,DIFF_RESET ); +const char *color_old =diff_get_color_opt (diffopt,DIFF_FILE_OLD ); +const char *color_new =diff_get_color_opt (diffopt,DIFF_FILE_NEW ); +const char *color_commit =diff_get_color_opt (diffopt,DIFF_COMMIT ); +const char *color;if (!dashes) {char *p;
@@ -270,21 +276,33 @@ static void output_pair_header(struct strbuf *buf, *p ='-' ; } +if (!b_util) { + color = color_old; + status ='<' ; + }else if (!a_util) { + color = color_new; + status ='>' ; + }else if (strcmp (a_util->patch, b_util->patch)) { + color = color_commit; + status ='!' ; + }else { + color = color_commit; + status ='=' ; + } +strbuf_reset (buf); +strbuf_addstr (buf, status =='!' ? color_old : color);if (!a_util)strbuf_addf (buf,"-: %s " , dashes);else strbuf_addf (buf,"%d: %s " , a_util->i +1 ,find_unique_abbrev (&a_util->oid,DEFAULT_ABBREV )); -if (!a_util) -strbuf_addch (buf,'>' ); -else if (!b_util) -strbuf_addch (buf,'<' ); -else if (strcmp (a_util->patch, b_util->patch)) -strbuf_addch (buf,'!' ); -else -strbuf_addch (buf,'=' ); +if (status =='!' ) +strbuf_addf (buf,"%s%s" , color_reset, color); +strbuf_addch (buf, status); +if (status =='!' ) +strbuf_addf (buf,"%s%s" , color_reset, color_new);if (!b_util)strbuf_addf (buf," -: %s" , dashes);
@@ -297,12 +315,15 @@ static void output_pair_header(struct strbuf *buf,const char *commit_buffer =get_commit_buffer (commit,NULL );const char *subject; +if (status =='!' ) +strbuf_addf (buf,"%s%s" , color_reset, color); +find_commit_subject (commit_buffer, &subject);strbuf_addch (buf,' ' );format_subject (buf, subject," " );unuse_commit_buffer (commit, commit_buffer); } -strbuf_addch (buf,'\n' ); +strbuf_addf (buf,"%s\n" , color_reset);fwrite (buf->buf, buf->len,1 , stdout); }
@@ -360,21 +381,21 @@ static void output(struct string_list *a, struct string_list *b,/* Show unmatched LHS commit whose predecessors were shown. */ if (i < a->nr && a_util->matching <0 ) { -output_pair_header (&buf, a_util,NULL ); +output_pair_header (diffopt, &buf, a_util,NULL ); i++;continue ; }/* Show unmatched RHS commits. */ while (j < b->nr && b_util->matching <0 ) { -output_pair_header (&buf,NULL , b_util); +output_pair_header (diffopt, &buf,NULL , b_util); b_util = ++j < b->nr ? b->items[j].util :NULL ; }/* Show matching LHS/RHS pair. */ if (j < b->nr) { a_util = a->items[b_util->matching].util; -output_pair_header (&buf, a_util, b_util); +output_pair_header (diffopt, &buf, a_util, b_util);if (!(diffopt->output_format &DIFF_FORMAT_NO_OUTPUT ))patch_diff (a->items[b_util->matching].string, b->items[j].string, diffopt);
Signature · 1 line
gitgitgadget
PATCH v3 13/20 color: add the meta color GIT_COLOR_REVERSE
In reply to Johannes Schindelin via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
This "color" simply reverts background and foreground. It will be used in the upcoming "dual color" mode of git range-diff, where we will reverse colors for the -/+ markers and the fragment headers of the "outer" diff.
--- color.h | 1 + 1 file changed, 1 insertion(+)
color.h +1 -0
@@ -44,6 +44,7 @@ struct strbuf;#define GIT_COLOR_BG_CYAN "\033[46m"#define GIT_COLOR_FAINT "\033[2m"#define GIT_COLOR_FAINT_ITALIC "\033[2;3m" +#define GIT_COLOR_REVERSE "\033[7m"/* A special value meaning "no color selected" */ #define GIT_COLOR_NIL "NIL"
Signature · 1 line
gitgitgadget
PATCH v3 14/20 diff: add an internal option to dual-color diffs of diffs
In reply to Johannes Schindelin via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
When diffing diffs, it can be quite daunting to figure out what the heck is going on, as there are nested +/- signs.
Let's make this easier by adding a flag in diff_options that allows color-coding the outer diff sign with inverted colors, so that the preimage and postimage is colored like the diff it is.
Of course, this really only makes sense when the preimage and postimage *are* diffs. So let's not expose this flag via a command-line option for now.
This is a feature that was invented by git-tbdiff, and it will be used by git range-diff in the next commit, by offering it via a new option:--dual-color.
--- diff.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++----------- diff.h | 1 + 2 files changed, 69 insertions(+), 15 deletions(-)
diff.c +68 -15
@@ -562,14 +562,18 @@ static void check_blank_at_eof(mmfile_t *mf1, mmfile_t *mf2, ecbdata->blank_at_eof_in_postimage = (at - l2) +1 ; } -static void emit_line_0 (struct diff_options *o,const char *set,const char *reset, +static void emit_line_0 (struct diff_options *o, +const char *set,unsigned reverse,const char *reset,int first,const char *line,int len) {int has_trailing_newline, has_trailing_carriage_return;int nofirst;FILE *file = o->file; -fputs (diff_line_prefix (o), file); +if (first) +fputs (diff_line_prefix (o), file); +else if (!len) +return ;if (len ==0 ) { has_trailing_newline = (first =='\n' );
@@ -587,8 +591,10 @@ static void emit_line_0(struct diff_options *o, const char *set, const char *res }if (len || !nofirst) { +if (reverse &&want_color (o->use_color)) +fputs (GIT_COLOR_REVERSE , file);fputs (set, file); -if (!nofirst) +if (first && !nofirst)fputc (first, file);fwrite (line, len,1 , file);fputs (reset, file);
@@ -602,7 +608,7 @@ static void emit_line_0(struct diff_options *o, const char *set, const char *resstatic void emit_line (struct diff_options *o,const char *set,const char *reset,const char *line,int len) { -emit_line_0 (o, set, reset, line[0 ], line+1 , len-1 ); +emit_line_0 (o, set,0 , reset, line[0 ], line+1 , len-1 ); }enum diff_symbol {
@@ -962,7 +968,8 @@ static void dim_moved_lines(struct diff_options *o)static void emit_line_ws_markup (struct diff_options *o,const char *set,const char *reset, -const char *line,int len,char sign, +const char *line,int len, +const char *set_sign,char sign,unsigned ws_rule,int blank_at_eof) {const char *ws =NULL ;
@@ -973,14 +980,20 @@ static void emit_line_ws_markup(struct diff_options *o, ws =NULL ; } -if (!ws) -emit_line_0 (o, set, reset, sign, line, len); -else if (blank_at_eof) +if (!ws && !set_sign) +emit_line_0 (o, set,0 , reset, sign, line, len); +else if (!ws) { +/* Emit just the prefix, then the rest. */ +emit_line_0 (o, set_sign ? set_sign : set, !!set_sign, reset, + sign,"" ,0 ); +emit_line_0 (o, set,0 , reset,0 , line, len); + }else if (blank_at_eof)/* Blank line at EOF - paint '+' as well */ -emit_line_0 (o, ws, reset, sign, line, len); +emit_line_0 (o, ws,0 , reset, sign, line, len);else {/* Emit just the prefix, then the rest. */ -emit_line_0 (o, set, reset, sign,"" ,0 ); +emit_line_0 (o, set_sign ? set_sign : set, !!set_sign, reset, + sign,"" ,0 );ws_check_emit (line, len, ws_rule, o->file, set, reset, ws); }
@@ -990,7 +1003,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o, struct emitted_diff_symbol *eds) {static const char *nneof =" No newline at end of file\n" ; -const char *context, *reset, *set, *meta, *fraginfo; +const char *context, *reset, *set, *set_sign, *meta, *fraginfo;struct strbuf sb =STRBUF_INIT ;enum diff_symbol s = eds->s;
@@ -1003,7 +1016,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o, context =diff_get_color_opt (o,DIFF_CONTEXT ); reset =diff_get_color_opt (o,DIFF_RESET );putc ('\n' , o->file); -emit_line_0 (o, context, reset,'\\' , +emit_line_0 (o, context,0 , reset,'\\' , nneof,strlen (nneof));break ;case DIFF_SYMBOL_SUBMODULE_HEADER :
@@ -1030,7 +1043,18 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,case DIFF_SYMBOL_CONTEXT : set =diff_get_color_opt (o,DIFF_CONTEXT ); reset =diff_get_color_opt (o,DIFF_RESET ); -emit_line_ws_markup (o, set, reset, line, len,' ' , + set_sign =NULL ; +if (o->flags.dual_color_diffed_diffs) { +char c = !len ?0 : line[0 ]; + +if (c =='+' ) + set =diff_get_color_opt (o,DIFF_FILE_NEW ); +else if (c =='@' ) + set =diff_get_color_opt (o,DIFF_FRAGINFO ); +else if (c =='-' ) + set =diff_get_color_opt (o,DIFF_FILE_OLD ); + } +emit_line_ws_markup (o, set, reset, line, len, set_sign,' ' , flags & (DIFF_SYMBOL_CONTENT_WS_MASK ),0 );break ;case DIFF_SYMBOL_PLUS :
@@ -1057,7 +1081,20 @@ static void emit_diff_symbol_from_struct(struct diff_options *o, set =diff_get_color_opt (o,DIFF_FILE_NEW ); } reset =diff_get_color_opt (o,DIFF_RESET ); -emit_line_ws_markup (o, set, reset, line, len,'+' , +if (!o->flags.dual_color_diffed_diffs) + set_sign =NULL ; +else { +char c = !len ?0 : line[0 ]; + + set_sign = set; +if (c =='-' ) + set =diff_get_color_opt (o,DIFF_FILE_OLD ); +else if (c =='@' ) + set =diff_get_color_opt (o,DIFF_FRAGINFO ); +else if (c !='+' ) + set =diff_get_color_opt (o,DIFF_CONTEXT ); + } +emit_line_ws_markup (o, set, reset, line, len, set_sign,'+' , flags &DIFF_SYMBOL_CONTENT_WS_MASK , flags &DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF );break ;
@@ -1085,7 +1122,20 @@ static void emit_diff_symbol_from_struct(struct diff_options *o, set =diff_get_color_opt (o,DIFF_FILE_OLD ); } reset =diff_get_color_opt (o,DIFF_RESET ); -emit_line_ws_markup (o, set, reset, line, len,'-' , +if (!o->flags.dual_color_diffed_diffs) + set_sign =NULL ; +else { +char c = !len ?0 : line[0 ]; + + set_sign = set; +if (c =='+' ) + set =diff_get_color_opt (o,DIFF_FILE_NEW ); +else if (c =='@' ) + set =diff_get_color_opt (o,DIFF_FRAGINFO ); +else if (c !='-' ) + set =diff_get_color_opt (o,DIFF_CONTEXT ); + } +emit_line_ws_markup (o, set, reset, line, len, set_sign,'-' , flags &DIFF_SYMBOL_CONTENT_WS_MASK ,0 );break ;case DIFF_SYMBOL_WORDS_PORCELAIN :
@@ -1276,6 +1326,7 @@ static void emit_hunk_header(struct emit_callback *ecbdata,const char *frag =diff_get_color (ecbdata->color_diff,DIFF_FRAGINFO );const char *func =diff_get_color (ecbdata->color_diff,DIFF_FUNCINFO );const char *reset =diff_get_color (ecbdata->color_diff,DIFF_RESET ); +const char *reverse = ecbdata->color_diff ?GIT_COLOR_REVERSE :"" ;static const char atat[2 ] = {'@' ,'@' };const char *cp, *ep;struct strbuf msgbuf =STRBUF_INIT ;
@@ -1296,6 +1347,8 @@ static void emit_hunk_header(struct emit_callback *ecbdata, ep +=2 ;/* skip over @@ */ /* The hunk header in fraginfo color */ +if (ecbdata->opt->flags.dual_color_diffed_diffs) +strbuf_addstr (&msgbuf, reverse);strbuf_addstr (&msgbuf, frag);strbuf_add (&msgbuf, line, ep - line);strbuf_addstr (&msgbuf, reset);
diff.h +1 -0
@@ -95,6 +95,7 @@ struct diff_flags { unsigned default_follow_renames:1; unsigned stat_with_summary:1; unsigned suppress_diff_headers:1; + unsigned dual_color_diffed_diffs:1; }; static inline void diff_flags_or(struct diff_flags *a,
Signature · 1 line
gitgitgadget
Re: [PATCH v3 14/20] diff: add an internal option to dual-color diffs of diffs
Quoted 36 lines · Johannes Schindelin via GitGitGadget
From: Johannes Schindelin <[email protected]>
When diffing diffs, it can be quite daunting to figure out what the heck is going on, as there are nested +/- signs.
Let's make this easier by adding a flag in
diff_optionsthat allows color-coding the outer diff sign with inverted colors, so that the preimage and postimage is colored like the diff it is.Of course, this really only makes sense when the preimage and postimage *are* diffs. So let's not expose this flag via a command-line option for now.
This is a feature that was invented by git-tbdiff, and it will be used by
git range-diffin the next commit, by offering it via a new option:--dual-color.Signed-off-by Johannes Schindelin <[email protected]>--- diff.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++----------- diff.h | 1 + 2 files changed, 69 insertions(+), 15 deletions(-)diff.c +2
-1@@ -562,14 +562,18 @@ static void check_blank_at_eof(mmfile_t *mf1, mmfile_t *mf2, ecbdata->blank_at_eof_in_postimage = (at - l2) +1 ; } -static void emit_line_0 (struct diff_options *o,const char *set,const char *reset, +static void emit_line_0 (struct diff_options *o, +const char *set,unsigned reverse,const char *reset,int first,const char *line,int len)
diff.c static void check_blank_at_eof(mmfile_t *mf1, mmfile_t *mf2, · line 576
Quoted 10 lines
diff.c +4
-1{int has_trailing_newline, has_trailing_carriage_return;int nofirst;FILE *file = o->file; -fputs (diff_line_prefix (o), file); +if (first) +fputs (diff_line_prefix (o), file); +else if (!len) +return ;
This case is not a problem for empty lines in e.g. "git-log --line-prefix" because first would contain the LF.
diff.c static void emit_line_0(struct diff_options *o, const char *set, const char *res · line 595
Quoted 8 lines
diff.c +2
-0if (len ==0 ) { has_trailing_newline = (first =='\n' );@@ -587,8 +591,10 @@ static void emit_line_0(struct diff_options *o, const char *set, const char *res }if (len || !nofirst) { +if (reverse &&want_color (o->use_color)) +fputs (GIT_COLOR_REVERSE , file);
Would it make sense to have the function signature take a char* for reverse and we pass in diff_get_color(o, GIT_COLOR_REVERSE), that would align with the set and reset color passed in?
diff.c static void emit_line_0(struct diff_options *o, const char *set, const char *res · line 598
Quoted 4 lines
diff.c +1
-1fputs (set, file); -if (!nofirst) +if (first && !nofirst)fputc (first, file);
'first' is line[0] and comes from user data, so I think this could change the output of diffs that has lines with the NUL character first in a line as then that character would be silently eaten?
The 'nofirst' (which is a bad name) is used to detect if we do not want to double print the first character in case of an empty line. (Before this series we always had 'first' as a valid character, now we also have 0 encoded for "do not print anything?"
Quoted 14 lines
diff.c +2
-1@@ -962,7 +968,8 @@ static void dim_moved_lines(struct diff_options *o)static void emit_line_ws_markup (struct diff_options *o,const char *set,const char *reset, -const char *line,int len,char sign, +const char *line,int len, +const char *set_sign,char sign,unsigned ws_rule,int blank_at_eof) {const char *ws =NULL ;@@ -973,14 +980,20 @@ static void emit_line_ws_markup(struct diff_options *o, ws =NULL ; }
diff.c static void emit_line_ws_markup(struct diff_options *o, · line 989
Quoted 10 lines
diff.c +7
-3-if (!ws) -emit_line_0 (o, set, reset, sign, line, len); -else if (blank_at_eof) +if (!ws && !set_sign) +emit_line_0 (o, set,0 , reset, sign, line, len); +else if (!ws) { +/* Emit just the prefix, then the rest. */ +emit_line_0 (o, set_sign ? set_sign : set, !!set_sign, reset, + sign,"" ,0 ); +emit_line_0 (o, set,0 , reset,0 , line, len);
(FYI:)
My long term vision for the emit_line_* functions was to have them actually line oriented, and here we observe that the preimage already breaks this assumption but just uses it as it sees fit.
I added that wart when refactoring the diff code to use the emit_ functionality as I wanted to stay backwards compatible.
The actual issue is that each emit_line_0 will encapsulate its content with its designated color and then end it with a reset. Looking at t4015 a common occurrence is output like:
<GREEN>+<RESET><GREEN>{<RESET>
which we'd add one more layer to it now when set_sign is set.
I think this is ok for now (I value having this series land over insisting on the perfect code), but just wanted to note my concern.
Ideally we'd refactor to only call emit_line once per line and when the set sign (and the newly introduced set_sign) are the same as the line color we'd avoid the intermediate RESET-and-SAME_COLOR pattern that the test suite expects a lot currently.
Quoted 24 lines
diff.c +3
-3@@ -990,7 +1003,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o, struct emitted_diff_symbol *eds) {static const char *nneof =" No newline at end of file\n" ; -const char *context, *reset, *set, *meta, *fraginfo; +const char *context, *reset, *set, *set_sign, *meta, *fraginfo;struct strbuf sb =STRBUF_INIT ;enum diff_symbol s = eds->s;@@ -1003,7 +1016,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o, context =diff_get_color_opt (o,DIFF_CONTEXT ); reset =diff_get_color_opt (o,DIFF_RESET );putc ('\n' , o->file); - emit_line_0(o, context, reset, '\\', + emit_line_0(o, context, 0, reset, '\\',@@ -1030,7 +1043,18 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,case DIFF_SYMBOL_CONTEXT : set =diff_get_color_opt (o,DIFF_CONTEXT ); reset =diff_get_color_opt (o,DIFF_RESET ); - emit_line_ws_markup(o, set, reset, line, len, ' ', + set_sign =NULL ;
diff.c static void emit_diff_symbol_from_struct(struct diff_options *o, · line 1056
Quoted 10 lines
diff.c +10
-0+if (o->flags.dual_color_diffed_diffs) { +char c = !len ?0 : line[0 ]; + +if (c =='+' ) + set =diff_get_color_opt (o,DIFF_FILE_NEW ); +else if (c =='@' ) + set =diff_get_color_opt (o,DIFF_FRAGINFO ); +else if (c =='-' ) + set =diff_get_color_opt (o,DIFF_FILE_OLD ); + }
This hunk is replicated below very similarly/
'set' depends on the initial symbol (the case we're in), and the first character of line.
Would it make sense to have a function
diff_get_color_set_sign(struct diffopt *, \
struct emitted_diff_symbol *)that takes care of all the computation and doesn't need repetition in each case? For example @ always maps to DIFF_FRAGINFO, and +/- have either DIFF_FILE_{OLD, NEW}, with the exception of (sign == set_sign), when they become CONTEXT AFAICT?
Quoted 80 lines
diff.c +32
-2+emit_line_ws_markup (o, set, reset, line, len, set_sign,' ' , flags & (DIFF_SYMBOL_CONTENT_WS_MASK ),0 );break ;case DIFF_SYMBOL_PLUS :@@ -1057,7 +1081,20 @@ static void emit_diff_symbol_from_struct(struct diff_options *o, set =diff_get_color_opt (o,DIFF_FILE_NEW ); } reset =diff_get_color_opt (o,DIFF_RESET ); -emit_line_ws_markup (o, set, reset, line, len,'+' , +if (!o->flags.dual_color_diffed_diffs) + set_sign =NULL ; +else { +char c = !len ?0 : line[0 ]; + + set_sign = set; +if (c =='-' ) + set =diff_get_color_opt (o,DIFF_FILE_OLD ); +else if (c =='@' ) + set =diff_get_color_opt (o,DIFF_FRAGINFO ); +else if (c !='+' ) + set =diff_get_color_opt (o,DIFF_CONTEXT ); + } +emit_line_ws_markup (o, set, reset, line, len, set_sign,'+' , flags &DIFF_SYMBOL_CONTENT_WS_MASK , flags &DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF );break ;@@ -1085,7 +1122,20 @@ static void emit_diff_symbol_from_struct(struct diff_options *o, set =diff_get_color_opt (o,DIFF_FILE_OLD ); } reset =diff_get_color_opt (o,DIFF_RESET ); -emit_line_ws_markup (o, set, reset, line, len,'-' , +if (!o->flags.dual_color_diffed_diffs) + set_sign =NULL ; +else { +char c = !len ?0 : line[0 ]; + + set_sign = set; +if (c =='+' ) + set =diff_get_color_opt (o,DIFF_FILE_NEW ); +else if (c =='@' ) + set =diff_get_color_opt (o,DIFF_FRAGINFO ); +else if (c !='-' ) + set =diff_get_color_opt (o,DIFF_CONTEXT ); + } +emit_line_ws_markup (o, set, reset, line, len, set_sign,'-' , flags &DIFF_SYMBOL_CONTENT_WS_MASK ,0 );break ;case DIFF_SYMBOL_WORDS_PORCELAIN :@@ -1276,6 +1326,7 @@ static void emit_hunk_header(struct emit_callback *ecbdata,const char *frag =diff_get_color (ecbdata->color_diff,DIFF_FRAGINFO );const char *func =diff_get_color (ecbdata->color_diff,DIFF_FUNCINFO );const char *reset =diff_get_color (ecbdata->color_diff,DIFF_RESET ); +const char *reverse = ecbdata->color_diff ?GIT_COLOR_REVERSE :"" ;static const char atat[2 ] = {'@' ,'@' };const char *cp, *ep;struct strbuf msgbuf =STRBUF_INIT ;@@ -1296,6 +1347,8 @@ static void emit_hunk_header(struct emit_callback *ecbdata, ep +=2 ;/* skip over @@ */ /* The hunk header in fraginfo color */ +if (ecbdata->opt->flags.dual_color_diffed_diffs) +strbuf_addstr (&msgbuf, reverse);strbuf_addstr (&msgbuf, frag);strbuf_add (&msgbuf, line, ep - line);strbuf_addstr (&msgbuf, reset);diff.h +1
-1@@ -95,6 +95,7 @@ struct diff_flags { unsigned default_follow_renames:1; unsigned stat_with_summary:1; unsigned suppress_diff_headers:1; + unsigned dual_color_diffed_diffs:1; }; static inline void diff_flags_or(struct diff_flags *a, --gitgitgadget
[PATCH 0/2] Re: [PATCH v3 14/20] diff: add an internal option to dual-color diffs of diffs
This is developed on top of 4a68b95ce2a6 (your series here)
This is an attempt to explain the previous email better, specially the second (yet unfinished) patch, but the resulting emit_line_0 is way clearer in my mind, dropping the 'first' character and instead having a 'char *sign' that (a) we can color differently for dual color and (b) can have multiple chars, the refactoring for the multiple chars would need to happen at a slightly higher level.
Feel free to draw inspiration from here, but if not that is fine, too (as I did not fully understand the word diffing problem yet, this may add a burden instead of just taking these patches).
I can send up a cleanup series after yours lands, as well.
Thanks,
Stefan
Stefan Beller (2):
diff.c: convert emit_line_ws_markup to take string for sign WIP diff.c: clarify emit_line_0
diff.c | 84 ++++++++++++++++++++++++++++------------------------------ 1 file changed, 40 insertions(+), 44 deletions(-)
Signature · 1 line
2.18.0.203.gfac676dfb9-goog
PATCH 1/2 diff.c: convert emit_line_ws_markup to take string for sign
For the diff of diffs, we have more than one character at the beginning of the line with special meaning, so let's pass around a string that contains all the markup for the line
--- diff.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-)
diff.c +8 -8
@@ -969,7 +969,7 @@ static void dim_moved_lines(struct diff_options *o)static void emit_line_ws_markup (struct diff_options *o,const char *set,const char *reset,const char *line,int len, -const char *set_sign,char sign, +const char *set_sign,const char *sign,unsigned ws_rule,int blank_at_eof) {const char *ws =NULL ;
@@ -981,19 +981,19 @@ static void emit_line_ws_markup(struct diff_options *o, }if (!ws && !set_sign) -emit_line_0 (o, set,0 , reset, sign, line, len); +emit_line_0 (o, set,0 , reset, sign[0 ], line, len);else if (!ws) {/* Emit just the prefix, then the rest. */ emit_line_0 (o, set_sign ? set_sign : set, !!set_sign, reset, - sign,"" ,0 ); + sign[0 ],"" ,0 );emit_line_0 (o, set,0 , reset,0 , line, len); }else if (blank_at_eof)/* Blank line at EOF - paint '+' as well */ -emit_line_0 (o, ws,0 , reset, sign, line, len); +emit_line_0 (o, ws,0 , reset, sign[0 ], line, len);else {/* Emit just the prefix, then the rest. */ emit_line_0 (o, set_sign ? set_sign : set, !!set_sign, reset, - sign,"" ,0 ); + sign[0 ],"" ,0 );ws_check_emit (line, len, ws_rule, o->file, set, reset, ws); }
@@ -1054,7 +1054,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,else if (c =='-' ) set =diff_get_color_opt (o,DIFF_FILE_OLD ); } -emit_line_ws_markup (o, set, reset, line, len, set_sign,' ' , +emit_line_ws_markup (o, set, reset, line, len, set_sign," " , flags & (DIFF_SYMBOL_CONTENT_WS_MASK ),0 );break ;case DIFF_SYMBOL_PLUS :
@@ -1100,7 +1100,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o, len--; } } -emit_line_ws_markup (o, set, reset, line, len, set_sign,'+' , +emit_line_ws_markup (o, set, reset, line, len, set_sign,"+" , flags &DIFF_SYMBOL_CONTENT_WS_MASK , flags &DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF );break ;
@@ -1141,7 +1141,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,else if (c !='-' ) set =diff_get_color_opt (o,DIFF_CONTEXT ); } -emit_line_ws_markup (o, set, reset, line, len, set_sign,'-' , +emit_line_ws_markup (o, set, reset, line, len, set_sign,"-" , flags &DIFF_SYMBOL_CONTENT_WS_MASK ,0 );break ;case DIFF_SYMBOL_WORDS_PORCELAIN :
Signature · 1 line
2.18.0.203.gfac676dfb9-goog
PATCH 2/2 WIP diff.c: clarify emit_line_0
In reply to Stefan BellerThis breaks t4034 (word diffs), but all other tests pass.
emit_line_0 grew complicated again, so here is an attempt to make it a bit simpler. emit_line_0 is called for all lines that are added, removed or context lines, and it follows the format:
<sign color> <sign> <main color> <content of length 'len'> <reset> <CR> <LF>
with each of the components optional. However a few rules apply:
* The CR/LF is passed to the function as part of line/len, so we have to figure out if we need to separate them
* As the sign color is a rather recent addition, we do not optimize it yet
* the main color insertion is new, as the color used to be inserted before the sign
* another follow up cleanup (that also touches the tests) could be a stricter check to consolidate with ws_check_emit (and not emit the color/reset twice)
--- diff.c | 78 ++++++++++++++++++++++++++++------------------------------ 1 file changed, 37 insertions(+), 41 deletions(-)
diff.c +37 -41
@@ -563,44 +563,44 @@ static void check_blank_at_eof(mmfile_t *mf1, mmfile_t *mf2, }static void emit_line_0 (struct diff_options *o, -const char *set,unsigned reverse,const char *reset, -int first,const char *line,int len) +const char *maincolor,const char *signcolor, +const char *reset,const char *sign, +const char *line,int len) {int has_trailing_newline, has_trailing_carriage_return; -int nofirst;FILE *file = o->file; -if (first) -fputs (diff_line_prefix (o), file); -else if (!len) -return ; +fputs (diff_line_prefix (o), file); -if (len ==0 ) { - has_trailing_newline = (first =='\n' ); - has_trailing_carriage_return = (!has_trailing_newline && - (first =='\r' )); - nofirst = has_trailing_newline || has_trailing_carriage_return; - }else { - has_trailing_newline = (len >0 && line[len-1 ] =='\n' ); -if (has_trailing_newline) - len--; - has_trailing_carriage_return = (len >0 && line[len-1 ] =='\r' ); -if (has_trailing_carriage_return) - len--; - nofirst =0 ; - } + has_trailing_newline = (len >0 && line[len-1 ] =='\n' ); +if (has_trailing_newline) + len--; + has_trailing_carriage_return = (len >0 && line[len-1 ] =='\r' ); +if (has_trailing_carriage_return) + len--; -if (len || !nofirst) { -if (reverse &&want_color (o->use_color)) -fputs (GIT_COLOR_REVERSE , file); -fputs (set, file); -if (first && !nofirst) -fputc (first, file); +if (signcolor) +fputs (signcolor, file); +else if (maincolor) +fputs (maincolor, file); + + +if (sign) +fputs (sign, file); + +/* only put main color here if it we did not color the sign the same way */ +if (signcolor && maincolor && signcolor != maincolor) +fputs (maincolor, file); + +if (len)fwrite (line, len,1 , file); + +if ((signcolor || maincolor) && reset)fputs (reset, file); - } +if (has_trailing_carriage_return)fputc ('\r' , file); +if (has_trailing_newline)fputc ('\n' , file); }
@@ -608,7 +608,7 @@ static void emit_line_0(struct diff_options *o,static void emit_line (struct diff_options *o,const char *set,const char *reset,const char *line,int len) { -emit_line_0 (o, set,0 , reset, line[0 ], line+1 , len-1 ); +emit_line_0 (o, set,NULL , reset,NULL , line, len); }enum diff_symbol {
@@ -980,20 +980,16 @@ static void emit_line_ws_markup(struct diff_options *o, ws =NULL ; } -if (!ws && !set_sign) -emit_line_0 (o, set,0 , reset, sign[0 ], line, len); -else if (!ws) { -/* Emit just the prefix, then the rest. */ -emit_line_0 (o, set_sign ? set_sign : set, !!set_sign, reset, - sign[0 ],"" ,0 ); -emit_line_0 (o, set,0 , reset,0 , line, len); - }else if (blank_at_eof) +if (!ws) +emit_line_0 (o, set, set_sign, reset, + sign, line, len); +else if (blank_at_eof)/* Blank line at EOF - paint '+' as well */ -emit_line_0 (o, ws,0 , reset, sign[0 ], line, len); +emit_line_0 (o, ws, set_sign, reset, sign, line, len);else {/* Emit just the prefix, then the rest. */ -emit_line_0 (o, set_sign ? set_sign : set, !!set_sign, reset, - sign[0 ],"" ,0 ); +emit_line_0 (o, set, set_sign, reset, + sign,"" ,0 );ws_check_emit (line, len, ws_rule, o->file, set, reset, ws); }
@@ -1016,7 +1012,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o, context =diff_get_color_opt (o,DIFF_CONTEXT ); reset =diff_get_color_opt (o,DIFF_RESET );putc ('\n' , o->file); -emit_line_0 (o, context,0 , reset,'\\' , +emit_line_0 (o, context,0 , reset,"\\" , nneof,strlen (nneof));break ;case DIFF_SYMBOL_SUBMODULE_HEADER :
Signature · 1 line
2.18.0.203.gfac676dfb9-goog
PATCH 1/2 diff.c: convert emit_line_ws_markup to take string for sign
For the diff of diffs, we have more than one character at the beginning of the line with special meaning, so let's pass around a string that contains all the markup for the line
--- diff.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-)
diff.c +8 -8
@@ -969,7 +969,7 @@ static void dim_moved_lines(struct diff_options *o)static void emit_line_ws_markup (struct diff_options *o,const char *set,const char *reset,const char *line,int len, -const char *set_sign,char sign, +const char *set_sign,const char *sign,unsigned ws_rule,int blank_at_eof) {const char *ws =NULL ;
@@ -981,19 +981,19 @@ static void emit_line_ws_markup(struct diff_options *o, }if (!ws && !set_sign) -emit_line_0 (o, set,0 , reset, sign, line, len); +emit_line_0 (o, set,0 , reset, sign[0 ], line, len);else if (!ws) {/* Emit just the prefix, then the rest. */ emit_line_0 (o, set_sign ? set_sign : set, !!set_sign, reset, - sign,"" ,0 ); + sign[0 ],"" ,0 );emit_line_0 (o, set,0 , reset,0 , line, len); }else if (blank_at_eof)/* Blank line at EOF - paint '+' as well */ -emit_line_0 (o, ws,0 , reset, sign, line, len); +emit_line_0 (o, ws,0 , reset, sign[0 ], line, len);else {/* Emit just the prefix, then the rest. */ emit_line_0 (o, set_sign ? set_sign : set, !!set_sign, reset, - sign,"" ,0 ); + sign[0 ],"" ,0 );ws_check_emit (line, len, ws_rule, o->file, set, reset, ws); }
@@ -1054,7 +1054,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,else if (c =='-' ) set =diff_get_color_opt (o,DIFF_FILE_OLD ); } -emit_line_ws_markup (o, set, reset, line, len, set_sign,' ' , +emit_line_ws_markup (o, set, reset, line, len, set_sign," " , flags & (DIFF_SYMBOL_CONTENT_WS_MASK ),0 );break ;case DIFF_SYMBOL_PLUS :
@@ -1100,7 +1100,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o, len--; } } -emit_line_ws_markup (o, set, reset, line, len, set_sign,'+' , +emit_line_ws_markup (o, set, reset, line, len, set_sign,"+" , flags &DIFF_SYMBOL_CONTENT_WS_MASK , flags &DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF );break ;
@@ -1141,7 +1141,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,else if (c !='-' ) set =diff_get_color_opt (o,DIFF_CONTEXT ); } -emit_line_ws_markup (o, set, reset, line, len, set_sign,'-' , +emit_line_ws_markup (o, set, reset, line, len, set_sign,"-" , flags &DIFF_SYMBOL_CONTENT_WS_MASK ,0 );break ;case DIFF_SYMBOL_WORDS_PORCELAIN :
Signature · 1 line
2.18.0.203.gfac676dfb9-goog
PATCH 1/1 diff.c: clarify emit_line_0
In reply to Stefan Belleremit_line_0 grew complicated again, so here is an attempt to make it a bit simpler. emit_line_0 is called for all lines that are added, removed or context lines, and it follows the format:
<sign color> <sign> <main color> <content of length 'len'> <reset> \
<CR> <LF>with each of the components optional.
Another follow up cleanup (that also touches the tests) could be a stricter check to consolidate with ws_check_emit (and not emit the color/reset twice).
---
oops wrong patch, this one should do.
Now this passes all tests.
diff.c | 84 +++++++++++++++++++------------------- t/t4015-diff-whitespace.sh | 10 ++--- 2 files changed, 48 insertions(+), 46 deletions(-)
diff.c +43 -41
@@ -563,42 +563,48 @@ static void check_blank_at_eof(mmfile_t *mf1, mmfile_t *mf2, }static void emit_line_0 (struct diff_options *o, -const char *set,unsigned reverse,const char *reset, -int first,const char *line,int len) +const char *maincolor,const char *signcolor, +const char *reset,const char *sign, +const char *line,int len) {int has_trailing_newline, has_trailing_carriage_return; -int nofirst;FILE *file = o->file; -if (first) -fputs (diff_line_prefix (o), file); -else if (!len) -return ; +fputs (diff_line_prefix (o), file); -if (len ==0 ) { - has_trailing_newline = (first =='\n' ); - has_trailing_carriage_return = (!has_trailing_newline && - (first =='\r' )); - nofirst = has_trailing_newline || has_trailing_carriage_return; - }else { - has_trailing_newline = (len >0 && line[len-1 ] =='\n' ); -if (has_trailing_newline) - len--; - has_trailing_carriage_return = (len >0 && line[len-1 ] =='\r' ); -if (has_trailing_carriage_return) - len--; - nofirst =0 ; - } + has_trailing_newline = (len >0 && line[len-1 ] =='\n' ); +if (has_trailing_newline) + len--; + has_trailing_carriage_return = (len >0 && line[len-1 ] =='\r' ); +if (has_trailing_carriage_return) + len--; + +/* +* Color the sign differently if requested, otherwise use the main +* color. +*/ +if (signcolor) +fputs (signcolor, file); +else if (maincolor) +fputs (maincolor, file); + +if (sign) +fputs (sign, file); + +/* +* Only put the main color here if it we did not color the sign the +* same way already +*/ +if (signcolor && maincolor &&strcmp (signcolor, maincolor)) +fputs (maincolor, file); -if (len || !nofirst) { -if (reverse &&want_color (o->use_color)) -fputs (GIT_COLOR_REVERSE , file); -fputs (set, file); -if (first && !nofirst) -fputc (first, file); +if (len)fwrite (line, len,1 , file); + +if (((maincolor && *maincolor) || (signcolor && *signcolor) || len >0 ) + && reset)fputs (reset, file); - } +if (has_trailing_carriage_return)fputc ('\r' , file);if (has_trailing_newline)
@@ -608,7 +614,7 @@ static void emit_line_0(struct diff_options *o,static void emit_line (struct diff_options *o,const char *set,const char *reset,const char *line,int len) { -emit_line_0 (o, set,0 , reset, line[0 ], line+1 , len-1 ); +emit_line_0 (o, set,NULL , reset,NULL , line, len); }enum diff_symbol {
@@ -980,20 +986,16 @@ static void emit_line_ws_markup(struct diff_options *o, ws =NULL ; } -if (!ws && !set_sign) -emit_line_0 (o, set,0 , reset, sign[0 ], line, len); -else if (!ws) { -/* Emit just the prefix, then the rest. */ -emit_line_0 (o, set_sign ? set_sign : set, !!set_sign, reset, - sign[0 ],"" ,0 ); -emit_line_0 (o, set,0 , reset,0 , line, len); - }else if (blank_at_eof) +if (!ws) +emit_line_0 (o, set, set_sign, reset, + sign, line, len); +else if (blank_at_eof)/* Blank line at EOF - paint '+' as well */ -emit_line_0 (o, ws,0 , reset, sign[0 ], line, len); +emit_line_0 (o, ws, set_sign, reset, sign, line, len);else {/* Emit just the prefix, then the rest. */ -emit_line_0 (o, set_sign ? set_sign : set, !!set_sign, reset, - sign[0 ],"" ,0 ); +emit_line_0 (o, set, set_sign, reset, + sign,"" ,0 );ws_check_emit (line, len, ws_rule, o->file, set, reset, ws); }
@@ -1016,7 +1018,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o, context =diff_get_color_opt (o,DIFF_CONTEXT ); reset =diff_get_color_opt (o,DIFF_RESET );putc ('\n' , o->file); -emit_line_0 (o, context,0 , reset,'\\' , +emit_line_0 (o, context,0 , reset,"\\" , nneof,strlen (nneof));break ;case DIFF_SYMBOL_SUBMODULE_HEADER :
t/t4015-diff-whitespace.sh +5 -5
@@ -945,7 +945,7 @@ test_expect_success 'ws-error-highlight test setup' ' <BOLD>--- a/x<RESET> <BOLD>+++ b/x<RESET> <CYAN>@@ -1,2 +1,3 @@<RESET> - <RESET>0. blank-at-eol<RESET><BLUE> <RESET> + 0. blank-at-eol<RESET><BLUE> <RESET> <RED>-<RESET><RED>1. blank-at-eol<RESET><BLUE> <RESET> <GREEN>+<RESET><GREEN>1. still-blank-at-eol<RESET><BLUE> <RESET> <GREEN>+<RESET><GREEN>2. and a new line<RESET><BLUE> <RESET>
@@ -1140,7 +1140,7 @@ test_expect_success 'detect malicious moved code, inside file' ' <CYAN>@@ -5,13 +5,6 @@<RESET> <RESET>printf("Hello ");<RESET> printf("World\n");<RESET> }<RESET> - <RESET> + <BRED>-int secure_foo(struct user *u)<RESET> <BRED>-{<RESET> <BLUE>-if (!u->is_allowed_foo)<RESET>
@@ -1158,7 +1158,7 @@ test_expect_success 'detect malicious moved code, inside file' ' <CYAN>@@ -4,6 +4,13 @@<RESET> <RESET>int bar()<RESET> printf("Hello World, but different\n");<RESET> }<RESET> - <RESET> + <BGREEN>+<RESET><BGREEN>int secure_foo(struct user *u)<RESET> <BGREEN>+<RESET><BGREEN>{<RESET> <GREEN>+<RESET><GREEN>foo(u);<RESET>
@@ -1189,7 +1189,7 @@ test_expect_success 'plain moved code, inside file' ' <CYAN>@@ -5,13 +5,6 @@<RESET> <RESET>printf("Hello ");<RESET> printf("World\n");<RESET> }<RESET> - <RESET> + <BRED>-int secure_foo(struct user *u)<RESET> <BRED>-{<RESET> <BRED>-if (!u->is_allowed_foo)<RESET>
@@ -1207,7 +1207,7 @@ test_expect_success 'plain moved code, inside file' ' <CYAN>@@ -4,6 +4,13 @@<RESET> <RESET>int bar()<RESET> printf("Hello World, but different\n");<RESET> }<RESET> - <RESET> + <BGREEN>+<RESET><BGREEN>int secure_foo(struct user *u)<RESET> <BGREEN>+<RESET><BGREEN>{<RESET> <BGREEN>+<RESET><BGREEN>foo(u);<RESET>
Signature · 1 line
2.18.0.203.gfac676dfb9-goog
PATCH 1/1 ws: do not reset and set color twice
When outputting lines that are checked for white space, we first use emit_line_0 to emit the prefix, and then the ws specific code. The code at each site carefully sets the color and then resets it, though it is the same color.
Avoid setting the color twice by passing a newly introduced flag that indicates if the color is already set.
---
The whole series is also available via
git fetch http://github.com/stefanbeller/git ws_cleanup-ontop-range-diff and concludes my cleanup on top of the range-diff series for now.
What is left is to refactor the diff-diff from the range-diff series to utilize the marker at the beginning of the line that can be more than one character. I left that as I do not want to collide with your work.
Thanks,
Stefan
cache.h | 2 +- diff.c | 8 +-- t/t4015-diff-whitespace.sh | 122 ++++++++++++++++++------------------- ws.c | 28 +++++++-- 4 files changed, 89 insertions(+), 71 deletions(-)
cache.h +1 -1
@@ -1807,7 +1807,7 @@ extern unsigned whitespace_rule_cfg;extern unsigned whitespace_rule (const char *);extern unsigned parse_whitespace_rule (const char *);extern unsigned ws_check (const char *line,int len,unsigned ws_rule); -extern void ws_check_emit (const char *line,int len,unsigned ws_rule,FILE *stream,const char *set,const char *reset,const char *ws); +extern void ws_check_emit (const char *line,int len,unsigned ws_rule,FILE *stream,const char *set,const char *reset,const char *ws,int already_set);extern char *whitespace_error_string (unsigned ws);extern void ws_fix_copy (struct strbuf *,const char *,int ,unsigned ,int *);extern int ws_blank_line (const char *line,int len,unsigned ws_rule);
diff.c +4 -4
@@ -993,11 +993,11 @@ static void emit_line_ws_markup(struct diff_options *o,/* Blank line at EOF - paint '+' as well */ emit_line_0 (o, ws, set_sign, reset, sign, line, len); else { -/* Emit just the prefix, then the rest. */ -emit_line_0 (o, set, set_sign, reset, +/* Emit just the prefix (with no RESET), then the rest. */ +emit_line_0 (o, set, set_sign,NULL , sign,"" ,0 );ws_check_emit (line, len, ws_rule, - o->file, set, reset, ws); + o->file, set, reset, ws,1 ); } }
@@ -2918,7 +2918,7 @@ static void checkdiff_consume(void *priv, char *line, unsigned long len)free (err);emit_line (data->o, set, reset, line,1 );ws_check_emit (line +1 , len -1 , data->ws_rule, - data->o->file, set, reset, ws); + data->o->file, set, reset, ws,0 ); }else if (line[0 ] ==' ' ) { data->lineno++; }else if (line[0 ] =='@' ) {
t/t4015-diff-whitespace.sh +61 -61
219 lines not on this page · raw message
ws.c +23 -5
@@ -139,10 +139,19 @@ char *whitespace_error_string(unsigned ws)return strbuf_detach (&err,NULL ); } +static inline void optional_reset (FILE *stream,const char *reset,int *already_set) +{ +if (*already_set) { +fputs (reset, stream); + *already_set =0 ; + } +} +/* If stream is non-NULL, emits the line after checking. */ static unsigned ws_check_emit_1 (const char *line,int len,unsigned ws_rule,FILE *stream,const char *set, -const char *reset,const char *ws) +const char *reset,const char *ws, +int already_set) {unsigned result =0 ;int written =0 ;
@@ -186,6 +195,7 @@ static unsigned ws_check_emit_1(const char *line, int len, unsigned ws_rule,if ((ws_rule &WS_SPACE_BEFORE_TAB ) && written < i) { result |=WS_SPACE_BEFORE_TAB ;if (stream) { +optional_reset (stream, reset, &already_set);fputs (ws, stream);fwrite (line + written, i - written,1 , stream);fputs (reset, stream);
@@ -194,12 +204,14 @@ static unsigned ws_check_emit_1(const char *line, int len, unsigned ws_rule, }else if (ws_rule &WS_TAB_IN_INDENT ) { result |=WS_TAB_IN_INDENT ;if (stream) { +optional_reset (stream, reset, &already_set);fwrite (line + written, i - written,1 , stream);fputs (ws, stream);fwrite (line + i,1 ,1 , stream);fputs (reset, stream); } }else if (stream) { +optional_reset (stream, reset, &already_set);fwrite (line + written, i - written +1 ,1 , stream); } written = i +1 ;
@@ -209,6 +221,7 @@ static unsigned ws_check_emit_1(const char *line, int len, unsigned ws_rule,if ((ws_rule &WS_INDENT_WITH_NON_TAB ) && i - written >=ws_tab_width (ws_rule)) { result |=WS_INDENT_WITH_NON_TAB ;if (stream) { +optional_reset (stream, reset, &already_set);fputs (ws, stream);fwrite (line + written, i - written,1 , stream);fputs (reset, stream);
@@ -224,19 +237,24 @@ static unsigned ws_check_emit_1(const char *line, int len, unsigned ws_rule,/* Emit non-highlighted (middle) segment. */ if (trailing_whitespace - written >0 ) { -fputs (set, stream); +if (!already_set) +fputs (set, stream);fwrite (line + written, trailing_whitespace - written,1 , stream);fputs (reset, stream); + already_set =0 ; }/* Highlight errors in trailing whitespace. */ if (trailing_whitespace != len) { +optional_reset (stream, reset, &already_set);fputs (ws, stream);fwrite (line + trailing_whitespace, len - trailing_whitespace,1 , stream);fputs (reset, stream); } +if (already_set) +fputs (reset, stream);if (trailing_carriage_return)fputc ('\r' , stream);if (trailing_newline)
@@ -247,14 +265,14 @@ static unsigned ws_check_emit_1(const char *line, int len, unsigned ws_rule,void ws_check_emit (const char *line,int len,unsigned ws_rule,FILE *stream,const char *set, -const char *reset,const char *ws) +const char *reset,const char *ws,int already_set) { - (void )ws_check_emit_1 (line, len, ws_rule, stream, set, reset, ws); + (void )ws_check_emit_1 (line, len, ws_rule, stream, set, reset, ws, already_set); }unsigned ws_check (const char *line,int len,unsigned ws_rule) { -return ws_check_emit_1 (line, len, ws_rule,NULL ,NULL ,NULL ,NULL ); +return ws_check_emit_1 (line, len, ws_rule,NULL ,NULL ,NULL ,NULL ,0 ); }int ws_blank_line (const char *line,int len,unsigned ws_rule)
Signature · 1 line
2.18.0.203.gfac676dfb9-goog
Re: [PATCH 0/2] Re: [PATCH v3 14/20] diff: add an internal option to dual-color diffs of diffs
In reply to Stefan BellerHi Stefan,
Quoted 9 lines · Stefan Beller
This is developed on top of
4a68b95ce2a6(your series here)This is an attempt to explain the previous email better, specially the second (yet unfinished) patch, but the resulting
emit_line_0is way clearer in my mind, dropping the 'first' character and instead having a 'char *sign' that (a) we can color differently for dual color and (b) can have multiple chars, the refactoring for the multiple chars would need to happen at a slightly higher level.
Quoted 4 lines
Feel free to draw inspiration from here, but if not that is fine, too (as I did not fully understand the word diffing problem yet, this may add a burden instead of just taking these patches).
I can send up a cleanup series after yours lands, as well.
We discussed this on IRC a couple of days ago, and I think that this patch pair was designed under the assumption that the dual color mode would use diff markers of the same color, always. However, the dual color mode is about *inverting* the color of the first marker (and using the marker itself to determine the color) and then using a potentially *different* color on the second marker (using *that* marker to determine the color). Example:
<background red>-</background><foreground green>+ Hello</foreground>
So I allowed myself to focus on trying to wrap my head around the way the whitespace flags work, and how to adjust the code in cache.h/diff.c/diff.h to replace the relatively simple workaround by a full blown correct patch (which is sadly a *lot* larger).
Ciao,
Dscho
PATCH v3 15/20 range-diff: offer to dual-color the diffs
In reply to Johannes Schindelin via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
When showing what changed between old and new commits, we show a diff of the patches. This diff is a diff between diffs, therefore there are nested +/- signs, and it can be relatively hard to understand what is going on.
With the --dual-color option, the preimage and the postimage are colored like the diffs they are, and the *outer* +/- sign is inverted for clarity.
--- builtin/range-diff.c | 8 ++++++++ 1 file changed, 8 insertions(+)
builtin/range-diff.c +8 -0
@@ -20,9 +20,12 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix) {int creation_factor =60 ;struct diff_options diffopt = {NULL }; +int dual_color =0 ;struct option options[] = {OPT_INTEGER (0 ,"creation-factor" , &creation_factor,N_ ("Percentage by which creation is weighted" )), +OPT_BOOL (0 ,"dual-color" , &dual_color, +N_ ("color both diff and diff-between-diffs" )),OPT_END () };int i, j, res =0 ;
@@ -50,6 +53,11 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix) argc = j;diff_setup_done (&diffopt); +if (dual_color) { + diffopt.use_color =1 ; + diffopt.flags.dual_color_diffed_diffs =1 ; + } +if (argc ==2 ) {if (!strstr (argv[0 ],".." ))warning (_ ("no .. in range: '%s'" ), argv[0 ]);
Signature · 1 line
gitgitgadget
PATCH v3 16/20 range-diff --dual-color: work around bogus white-space warning
In reply to Johannes Schindelin via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
When displaying a diff of diffs, it is possible that there is an outer + before a context line. That happens when the context changed between old and new commit. When that context line starts with a tab (after the space that marks it as context line), our diff machinery spits out a white-space error (space before tab), but in this case, that is incorrect.
Work around this by detecting that situation and simply *not* printing the space in that case.
This is slightly improper a fix because it is conceivable that an output_prefix might be configured with *just* the right length to let that tab jump to a different tab stop depending whether we emit that space or not.
However, the proper fix would be relatively ugly and intrusive because it would have to *weaken* the WS_SPACE_BEFORE_TAB option in ws.c. Besides, we do not expose the --dual-color option in cases other than the git range-diff command, which only uses a hard-coded output_prefix of four spaces (which misses the problem by one column... ;-)).
--- diff.c | 6 ++++++ 1 file changed, 6 insertions(+)
diff.c +6 -0
@@ -1093,6 +1093,12 @@ static void emit_diff_symbol_from_struct(struct diff_options *o, set =diff_get_color_opt (o,DIFF_FRAGINFO ); else if (c != '+') set =diff_get_color_opt (o,DIFF_CONTEXT ); +/* Avoid space-before-tab warning */ +if (c ==' ' && (len <2 || line[1 ] =='\t' || + line[1 ] =='\r' || line[1 ] =='\n' )) { + line++; + len--; + } } emit_line_ws_markup(o, set, reset, line, len, set_sign, '+', flags & DIFF_SYMBOL_CONTENT_WS_MASK,
Signature · 1 line
gitgitgadget
Re: [PATCH v3 16/20] range-diff --dual-color: work around bogus white-space warning
Quoted 10 lines · Johannes Schindelin via GitGitGadget
From: Johannes Schindelin <[email protected]>
When displaying a diff of diffs, it is possible that there is an outer
+before a context line. That happens when the context changed between old and new commit. When that context line starts with a tab (after the space that marks it as context line), our diff machinery spits out a white-space error (space before tab), but in this case, that is incorrect.
Quoted 2 lines
Work around this by detecting that situation and simply *not* printing the space in that case.
ok. If that is the workaround that you deem to be the right thing for now. (I do not have an opinion if that is the right approach, or if we'd want to s/<TAB>/<SPACE>/ for example.)
Quoted 11 lines
This is slightly improper a fix because it is conceivable that an
output_prefixmight be configured with *just* the right length to let that tab jump to a different tab stop depending whether we emit that space or not.However, the proper fix would be relatively ugly and intrusive because it would have to *weaken* the
WS_SPACE_BEFORE_TABoption inws.c. Besides, we do not expose the --dual-color option in cases other than thegit range-diffcommand, which only uses a hard-codedoutput_prefixof four spaces (which misses the problem by one column... ;-)).
That makes sense!
Quoted 10 lines
Signed-off-by Johannes Schindelin <[email protected]>--- diff.c | 6 ++++++ 1 file changed, 6 insertions(+)diff.c +0
-0
diff.c static void emit_diff_symbol_from_struct(struct diff_options *o, · line 1102
Quoted 10 lines
diff.c +6
-0@@ -1093,6 +1093,12 @@ static void emit_diff_symbol_from_struct(struct diff_options *o, set =diff_get_color_opt (o,DIFF_FRAGINFO ); else if (c != '+') set =diff_get_color_opt (o,DIFF_CONTEXT ); +/* Avoid space-before-tab warning */ +if (c ==' ' && (len <2 || line[1 ] =='\t' || + line[1 ] =='\r' || line[1 ] =='\n' )) { + line++; + len--; + } }
And this is inside the check for 'o->flags.dual_color_diffed_diffs', so that is protected against other diffs.
Thanks,
Stefan
Re: [PATCH v3 16/20] range-diff --dual-color: work around bogus white-space warning
Quoted 12 lines · Stefan Beller
Quoted 10 lines · Johannes Schindelin via GitGitGadget
From: Johannes Schindelin <[email protected]>
When displaying a diff of diffs, it is possible that there is an outer
+before a context line. That happens when the context changed between old and new commit. When that context line starts with a tab (after the space that marks it as context line), our diff machinery spits out a white-space error (space before tab), but in this case, that is incorrect.
Quoted 14 lines
Quoted 2 lines
Work around this by detecting that situation and simply *not* printing the space in that case.
ok. If that is the workaround that you deem to be the right thing for now. (I do not have an opinion if that is the right approach, or if we'd want to s/<TAB>/<SPACE>/ for example.)
Quoted 7 lines
This is slightly improper a fix because it is conceivable that an
output_prefixmight be configured with *just* the right length to let that tab jump to a different tab stop depending whether we emit that space or not.However, the proper fix would be relatively ugly and intrusive because it would have to *weaken* the
WS_SPACE_BEFORE_TABoption inws.c.
I agree that weaking the error checking is a wrong solution. Is the root cause of this whole problem because for a diff of diff e.g.
context that did not change between iterations
- context in old interation
-+whatever new contents added by old iteration
+ context in new interation updated by earlier step ++whatever new contents added by new iteration
there needs to be a way to tell the ws.c whitespace breakage checking logic that the very first column is not interesting at all, and the "+" before "whatever" and " " before "context" should be considered to actually sit at the first (or zero-th) column of the diff output to be checked, but there is no interface to tell the machinery that wish, because there is no such need when inspecting a diff of contents? If the word "context" above were indented with HT, I can understand that the one common between iterations would trigger SP+HT violation that way. Is that what is happening here?
Adding a way to tell that the apparent first column is to be ignored to ws.c machinery (or arranging the caller to skip the first column) may be more intrusive than it is worth, only to support this tool. Ignoring the problem altogether and live with an incorrectly colored SP-before-HT might be a less noisy but still acceptable solution from that point of view, though.
I also wonder if we should be feeding the context lines to ws.c machinery in the first place though. In the above hypothetical diff-of-diff output, I _think_ the only two lines we want to check for ws.c breakage are the ones that begin with "whatever". We may find that both iterations are trying to introduce a ws breakage, or we may find that old one had violation which the new one corrected. A whitespace breakage on "context" lines, whether they are the ones being removed by the patch or the ones staying the same across the patch, is not worth painting---the normal diff-of-contents do not by default show them as violation, no?
Re: [PATCH v3 16/20] range-diff --dual-color: work around bogus white-space warning
Hi Junio,
Quoted 2 lines · Junio C Hamano
I also wonder if we should be feeding the context lines to
ws.cmachinery in the first place though.
It *is* confusing, I know. The entire "diff of diffs" concept *is* confusing. I just don't know about a better alternative.
So hear me out, because there is a big misconception here: there are *two* levels of diffs. The outer one and the inner one.
Context lines of the outer diffs have no problem [*1*].
The problem arises when the outer diff shows a - or + line (i.e. the line is present *either* in the old patch set or in the new patch set, but not both), *and* that line is *not* a context line of the inner diff.
Let's illustrate this via an example. Let's assume that both the old patch set and the new patch set add a comment to a statement, and that the context of that statement changed between old and new patch set. Something like this would be in the old patch set:
```diff int quiet = 0; + /* This is only needed for the reflog message */ const char *branch = "HEAD"; ```
And this would be in the new patch set:
```diff int quiet = 0, try_harder = 0; + /* This is only needed for the reflog message */ const char *branch = "HEAD"; ```
So as you see, both old and new revision of the same patch add that comment, and it is just a context line that changed, which a regular reviewer would want to *not* consider a "real" change between the patch set iterations.
Now, let's look at the "diff of diffs":
```diff - int quiet = 0; + int quiet = 0, try_harder = 0; + /* This is only needed for the reflog message */ const char *branch = "HEAD"; ```
Please understand that in the dual color mode:
- The first line's - would have a red background color, the rest of that line would be uncolored (because it is a context line of the inner diff),
- the second line's + would have a green background color, the rest would be just as uncolored as the rest of the first line,
- the third line would be a context line of the outer diff, but a + line of the inner diff, therefore that rest of the line would be green, and
- the fourth line is completely uncolored; It is a context line both of the inner and the outer diff.
That's it for the diff colors. Now for the white space: The first two lines start with a - and a + respectively (outer diff marker), and then most crucially continue with a space to indicate the inner diff's context line, *and then continue with a horizontal tab*.
As far as the inner diff is concerned, this *is* a context line.
As far as the outer diff is concerned, this is *not* a context line.
And that is the conundrum: the whitespace checker is called because the outer diff claims that the second line is a + line and the whitespace checker has no idea that it should treat it as a context line instead.
I'll try to find some time this afternoon to study Stefan's reply, as I have a hunch that there is a deep insight hidden that helps me to figure out the proper path ahead (because I do not want to uglify the diff.c code the way my current iteration does, and I'd rather have a way to color the diff more intelligently myself, in a function in range-diff.c).
Ciao,
Dscho
Footnote *1*: Actually, that is only half the truth. In dual color mode, if a line is a context line of the outer diff, but a - or + line of the inner diff, *we still want it colored*. And of course, ideally we still want whitespace checking for the + lines.
Re: [PATCH v3 16/20] range-diff --dual-color: work around bogus white-space warning
Quoted 58 lines · Johannes Schindelin
Hi Junio,
Quoted 2 lines · Junio C Hamano
I also wonder if we should be feeding the context lines to
ws.cmachinery in the first place though.It *is* confusing, I know. The entire "diff of diffs" concept *is* confusing. I just don't know about a better alternative.
So hear me out, because there is a big misconception here: there are *two* levels of diffs. The outer one and the inner one.
Context lines of the outer diffs have no problem [*1*].
The problem arises when the outer diff shows a - or + line (i.e. the line is present *either* in the old patch set or in the new patch set, but not both), *and* that line is *not* a context line of the inner diff.
Let's illustrate this via an example. Let's assume that both the old patch set and the new patch set add a comment to a statement, and that the context of that statement changed between old and new patch set. Something like this would be in the old patch set:
```diff
(quoted diff) +1
-0int quiet = 0; + /* This is only needed for the reflog message */ const char *branch = "HEAD";``` And this would be in the new patch set: ```diff(quoted diff) +1
-0int quiet = 0, try_harder = 0; + /* This is only needed for the reflog message */ const char *branch = "HEAD";``` So as you see, both old and new revision of the same patch add that comment, and it is just a context line that changed, which a regular reviewer would want to *not* consider a "real" change between the patch set iterations. Now, let's look at the "diff of diffs": ```diff(quoted diff) +1
-1- int quiet = 0; + int quiet = 0, try_harder = 0; + /* This is only needed for the reflog message */ const char *branch = "HEAD";```
Please understand that in the dual color mode:
- The first line's
-would have a red background color, the rest of that line would be uncolored (because it is a context line of the inner diff),
Quoted 8 lines
- the second line's
+would have a green background color, the rest would be just as uncolored as the rest of the first line,- the third line would be a context line of the outer diff, but a
+line of the inner diff, therefore that rest of the line would be green, and- the fourth line is completely uncolored; It is a context line both of the inner and the outer diff.
All of the above about colouring I find sensible.
Quoted 8 lines
That's it for the diff colors. Now for the white space: The first two lines start with a
-and a+respectively (outer diff marker), and then most crucially continue with a space to indicate the inner diff's context line, *and then continue with a horizontal tab*.As far as the inner diff is concerned, this *is* a context line.
As far as the outer diff is concerned, this is *not* a context line.
What I meant was that there is no point checking ws errors in the outer diff. The fact that the older and the newer revisions have an unchanged context line (i.e. begins with two SPs in the outer diff) or different one (i.e. begins with "- " and "+ ") are worth knowing (and you have red and green leading "-/+" for that), but the fact that the outer diff's new context line begins with "+ <HT>" has nothing to do with the goodness of the new patch, as such a line shows that the new patch touches a line near an unchanged [*1*] line that happens to begin with a <HT>, which has no whitespace breakage to begin with, and even if such a line had a trailing whitespace, it is not something the new patch introduces.
side note *1*; the fact that it has leading "+" means that there are differences in the previous steps between old and new patch series and that is why the context in this step is different between old and new series. But in the context of applying the new series, the patch does *not* change that line.
Quoted 3 lines
And that is the conundrum: the whitespace checker is called because the outer diff claims that the second line is a
+line and the whitespace checker has no idea that it should treat it as a context line instead.
I think you are saying the same thing as I said in the previous message. Trying to reuse ws.c without first giving it a way to be told that the caller does not care about the early columns (in the "+ <HT>" example, you want to tell ws.c machinery that it is *not* an added line that begins with SP + HT; you want it to know that it is a context line whose contents begins with a HT) will of course cause headaches.
Re: [PATCH v3 16/20] range-diff --dual-color: work around bogus white-space warning
In reply to Johannes SchindelinQuoted 10 lines · Johannes Schindelin
Hi Junio,
Quoted 2 lines · Junio C Hamano
I also wonder if we should be feeding the context lines to
ws.cmachinery in the first place though.It *is* confusing, I know. The entire "diff of diffs" concept *is* confusing. I just don't know about a better alternative.
I agree, but I am sure we'll get used to it quickly.
Quoted 2 lines
So hear me out, because there is a big misconception here: there are *two* levels of diffs. The outer one and the inner one.
Yes, the inner diff is just input that was generated before because it is so convenient to generate. Recently when using this too (back then when it was called branch-diff), I came across the following:
Patch 1 looked like:
line 1
+ new line
line 2
line 3
and in the next iteration it looked like:
line 1
line 2
+ new line
line 3
such that the diff of diffs showed the move correctly, but as the inner diffs had different context ranges, other lines looked like added/removed in the outer diff, though it was both context.
So I wonder if eventually (not in this series) we want to tweak the context lines, generate more than needed in the inner diffs and cut them off in the outer diff "at the same line".
I digress again w.r.t. white space.
Quoted 5 lines
Context lines of the outer diffs have no problem [*1*].
The problem arises when the outer diff shows a - or + line (i.e. the line is present *either* in the old patch set or in the new patch set, but not both), *and* that line is *not* a context line of the inner diff.
So an actual change in the patches; an incremental reviewer would want to spend most care on these.
Quoted 54 lines
Let's illustrate this via an example. Let's assume that both the old patch set and the new patch set add a comment to a statement, and that the context of that statement changed between old and new patch set. Something like this would be in the old patch set:
```diff int quiet = 0; + /* This is only needed for the reflog message */ const char *branch = "HEAD"; ```And this would be in the new patch set:
```diff int quiet = 0, try_harder = 0; + /* This is only needed for the reflog message */ const char *branch = "HEAD"; ```So as you see, both old and new revision of the same patch add that comment, and it is just a context line that changed, which a regular reviewer would want to *not* consider a "real" change between the patch set iterations.
Now, let's look at the "diff of diffs":
```diff
(quoted diff) +1
-1- int quiet = 0; + int quiet = 0, try_harder = 0; + /* This is only needed for the reflog message */ const char *branch = "HEAD";```
Please understand that in the dual color mode:
- The first line's
-would have a red background color, the rest of that line would be uncolored (because it is a context line of the inner diff),- the second line's
+would have a green background color, the rest would be just as uncolored as the rest of the first line,- the third line would be a context line of the outer diff, but a
+line of the inner diff, therefore that rest of the line would be green, and- the fourth line is completely uncolored; It is a context line both of the inner and the outer diff.
That's it for the diff colors. Now for the white space: The first two lines start with a
-and a+respectively (outer diff marker), and then most crucially continue with a space to indicate the inner diff's context line, *and then continue with a horizontal tab*.
Quoted 7 lines
As far as the inner diff is concerned, this *is* a context line.
As far as the outer diff is concerned, this is *not* a context line.
And that is the conundrum: the whitespace checker is called because the outer diff claims that the second line is a
+line and the whitespace checker has no idea that it should treat it as a context line instead.
Spelled out this way, we might want to add more symbols to enum diff_symbol, such as
DIFF_SYMBOL_DUAL_DIFF_PLUS_PLUS DIFF_SYMBOL_DUAL_DIFF_PLUS_MINUS DIFF_SYMBOL_PLUS_MINUS
or so.
These would need to get generated when we create the diff of diffs in emit_{del,add,context}_line or even fn_out_consume; and then have their own treatment regarding white spaces in emit_diff_symbol_from_struct.
I am not sure if that would help for the series as-is, as I am thinking already how
to move these diff-diffs in-core (as that would help a lot with the context line cutting mentioned above).
Quoted 5 lines
I'll try to find some time this afternoon to study Stefan's reply, as I have a hunch that there is a deep insight hidden that helps me to figure out the proper path ahead (because I do not want to uglify the
diff.ccode the way my current iteration does, and I'd rather have a way to color the diff more intelligently myself, in a function inrange-diff.c).
I considered trying a cleanup on top of your series as I had the impression the move detection added some ugliness as well.
Thanks,
Stefan
Re: [PATCH v3 16/20] range-diff --dual-color: work around bogus white-space warning
Hi Stefan,
Quoted 5 lines · Stefan Beller
Quoted 3 lines · Johannes Schindelin
On Mon, 9 Jul 2018, Junio C Hamano wrote:
Quoted 7 lines
Quoted 5 lines
Quoted 2 lines
I also wonder if we should be feeding the context lines to
ws.cmachinery in the first place though.It *is* confusing, I know. The entire "diff of diffs" concept *is* confusing. I just don't know about a better alternative.
I agree, but I am sure we'll get used to it quickly.
Maybe you. Not me, though, I use range-diff extensively, and I still got confused quite a bit.
Until, that is, I implemented the change where the "old-only" changes are dimmed and the "new-only" changes are displayed in bold.
(The colors stay the same, it's just that the brightness indicates whether this is a change that was made obsolete, a change that stayed the same, or a change that was introduced in the latest iteration.)
With this change, I am quite confident that I read the range-diffs correctly all the time.
Quoted 20 lines
Quoted 2 lines
So hear me out, because there is a big misconception here: there are *two* levels of diffs. The outer one and the inner one.
Yes, the inner diff is just input that was generated before because it is so convenient to generate. Recently when using this too (back then when it was called branch-diff), I came across the following:
Patch 1 looked like:
line 1
+ new line
line 2
line 3and in the next iteration it looked like:
line 1
line 2
+ new line
line 3
Quoted 6 lines
such that the diff of diffs showed the move correctly, but as the inner diffs had different context ranges, other lines looked like added/removed in the outer diff, though it was both context.
So I wonder if eventually (not in this series) we want to tweak the context lines, generate more than needed in the inner diffs and cut them off in the outer diff "at the same line".
That would be a welcome improvement, although I fear that it will be relatively intrusive. For one, you can forget about using different diff consumers (such as word-diff) if you hack up the diff of diffs generation at *such* a low level.
Quoted 10 lines
I digress again w.r.t. white space.
Quoted 5 lines
Context lines of the outer diffs have no problem [*1*].
The problem arises when the outer diff shows a - or + line (i.e. the line is present *either* in the old patch set or in the new patch set, but not both), *and* that line is *not* a context line of the inner diff.
So an actual change in the patches; an incremental reviewer would want to spend most care on these.
Precisely.
With above-mentioned dimming/brightening, there is a strong visual cue to focus on those parts.
Quoted 68 lines
Quoted 53 lines
Let's illustrate this via an example. Let's assume that both the old patch set and the new patch set add a comment to a statement, and that the context of that statement changed between old and new patch set. Something like this would be in the old patch set:
```diff int quiet = 0; + /* This is only needed for the reflog message */ const char *branch = "HEAD"; ```And this would be in the new patch set:
```diff int quiet = 0, try_harder = 0; + /* This is only needed for the reflog message */ const char *branch = "HEAD"; ```So as you see, both old and new revision of the same patch add that comment, and it is just a context line that changed, which a regular reviewer would want to *not* consider a "real" change between the patch set iterations.
Now, let's look at the "diff of diffs":
```diff
(quoted diff) +1
-1- int quiet = 0; + int quiet = 0, try_harder = 0; + /* This is only needed for the reflog message */ const char *branch = "HEAD";```
Please understand that in the dual color mode:
- The first line's
-would have a red background color, the rest of that line would be uncolored (because it is a context line of the inner diff),- the second line's
+would have a green background color, the rest would be just as uncolored as the rest of the first line,- the third line would be a context line of the outer diff, but a
+line of the inner diff, therefore that rest of the line would be green, and- the fourth line is completely uncolored; It is a context line both of the inner and the outer diff.
That's it for the diff colors. Now for the white space: The first two lines start with a
-and a+respectively (outer diff marker), and then most crucially continue with a space to indicate the inner diff's context line, *and then continue with a horizontal tab*.Quoted 7 lines
As far as the inner diff is concerned, this *is* a context line.
As far as the outer diff is concerned, this is *not* a context line.
And that is the conundrum: the whitespace checker is called because the outer diff claims that the second line is a
+line and the whitespace checker has no idea that it should treat it as a context line instead.Spelled out this way, we might want to add more symbols to enum
diff_symbol, such as
DIFF_SYMBOL_DUAL_DIFF_PLUS_PLUSDIFF_SYMBOL_DUAL_DIFF_PLUS_MINUSDIFF_SYMBOL_PLUS_MINUS
or so.
Quoted 7 lines
These would need to get generated when we create the diff of diffs in emit_{del,add,context}_line or even
fn_out_consume; and then have their own treatment regarding white spaces inemit_diff_symbol_from_struct.I am not sure if that would help for the series as-is, as I am thinking already how to move these diff-diffs in-core (as that would help a lot with the context line cutting mentioned above).
I settled on _DIM and _BOLD versions for CONTEXT, FILE_OLD and FILE_NEW.
Quoted 8 lines
Quoted 5 lines
I'll try to find some time this afternoon to study Stefan's reply, as I have a hunch that there is a deep insight hidden that helps me to figure out the proper path ahead (because I do not want to uglify the
diff.ccode the way my current iteration does, and I'd rather have a way to color the diff more intelligently myself, in a function inrange-diff.c).I considered trying a cleanup on top of your series as I had the impression the move detection added some ugliness as well.
I will be glad to review the patches after this coming week. Should I forget, please remind me.
Thanks,
Dscho
PATCH v3 17/20 range-diff: add a man page
In reply to Johannes Schindelin via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
The bulk of this patch consists of a heavily butchered version of tbdiff's README written by Thomas Rast and Thomas Gummerer, lifted from
https://github.com/trast/tbdiff.
--- Documentation/git-range-diff.txt | 235 +++++++++++++++++++++++++++++++ 1 file changed, 235 insertions(+) create mode 100644 Documentation/git-range-diff.txt
Documentation/git-range-diff.txt new file +235 -0
@@ -0,0 +1,235 @@ +git-range-diff(1) +================== + +NAME +---- +git-range-diff - Compare two commit ranges (e.g. two versions of a branch) + +SYNOPSIS +-------- +[verse] +'git range-diff' [--color=[<when>]] [--no-color] [<diff-options>] + [--dual-color] [--creation-factor=<factor>] + ( <range1> <range2> | <rev1>...<rev2> | <base> <rev1> <rev2> ) + +DESCRIPTION +----------- + +This command shows the differences between two versions of a patch +series, or more generally, two commit ranges (ignoring merges). + +To that end, it first finds pairs of commits from both commit ranges +that correspond with each other. Two commits are said to correspond when +the diff between their patches (i.e. the author information, the commit +message and the commit diff) is reasonably small compared to the +patches' size. See ``Algorithm` below for details. + +Finally, the list of matching commits is shown in the order of the +second commit range, with unmatched commits being inserted just after +all of their ancestors have been shown. + + +OPTIONS +------- +--dual-color:: + When the commit diffs differ, recreate the original diffs' + coloring, and add outer -/+ diff markers with the *background* + being red/green to make it easier to see e.g. when there was a + change in what exact lines were added. + +--creation-factor=<percent>:: + Set the creation/deletion cost fudge factor to `<percent>`. + Defaults to 60. Try a larger value if `git range-diff` erroneously + considers a large change a total rewrite (deletion of one commit + and addition of another), and a smaller one in the reverse case. + See the ``Algorithm`` section below for an explanation why this is + needed. + +<range1> <range2>:: + Compare the commits specified by the two ranges, where + `<range1>` is considered an older version of `<range2>`. + +<rev1>...<rev2>:: + Equivalent to passing `<rev2>..<rev1>` and `<rev1>..<rev2>`. + +<base> <rev1> <rev2>:: + Equivalent to passing `<base>..<rev1>` and `<base>..<rev2>`. + Note that `<base>` does not need to be the exact branch point + of the branches. Example: after rebasing a branch `my-topic`, + `git range-diff my-topic@{u} my-topic@{1} my-topic` would + show the differences introduced by the rebase. + +`git range-diff` also accepts the regular diff options (see +linkgit:git-diff[1]), most notably the `--color=[<when>]` and +`--no-color` options. These options are used when generating the "diff +between patches", i.e. to compare the author, commit message and diff of +corresponding old/new commits. There is currently no means to tweak the +diff options passed to `git log` when generating those patches. + + +CONFIGURATION +------------- +This command uses the `diff.color.*` and `pager.range-diff` settings +(the latter is on by default). +See linkgit:git-config[1]. + + +EXAMPLES +-------- + +When a rebase required merge conflicts to be resolved, compare the changes +introduced by the rebase directly afterwards using: + +------------ +$ git range-diff @{u} @{1} @ +------------ + + +A typical output of `git range-diff` would look like this: + +------------ +-: ------- > 1: 0ddba11 Prepare for the inevitable! +1: c0debee = 2: cab005e Add a helpful message at the start +2: f00dbal ! 3: decafe1 Describe a bug + @@ -1,3 +1,3 @@ + Author: A U Thor <[email protected]> + + -TODO: Describe a bug + +Describe a bug + @@ -324,5 +324,6 + This is expected. + + -+What is unexpected is that it will also crash. + ++Unexpectedly, it also crashes. This is a bug, and the jury is + ++still out there how to fix it best. See ticket #314 for details. + + Contact +3: bedead < -: ------- TO-UNDO +------------ + +In this example, there are 3 old and 3 new commits, where the developer +removed the 3rd, added a new one before the first two, and modified the +commit message of the 2nd commit as well its diff. + +When the output goes to a terminal, it is color-coded by default, just +like regular `git diff`'s output. In addition, the first line (adding a +commit) is green, the last line (deleting a commit) is red, the second +line (with a perfect match) is yellow like the commit header of `git +show`'s output, and the third line colors the old commit red, the new +one green and the rest like `git show`'s commit header. + +The color-coded diff is actually a bit hard to read, though, as it +colors the entire lines red or green. The line that added "What is +unexpected" in the old commit, for example, is completely red, even if +the intent of the old commit was to add something. + +To help with that, use the `--dual-color` mode. In this mode, the diff +of diffs will retain the original diff colors, and prefix the lines with +-/+ markers that have their *background* red or green, to make it more +obvious that they describe how the diff itself changed. + + +Algorithm +--------- + +The general idea is this: we generate a cost matrix between the commits +in both commit ranges, then solve the least-cost assignment. + +To avoid false positives (e.g. when a patch has been removed, and an +unrelated patch has been added between two iterations of the same patch +series), the cost matrix is extended to allow for that, by adding +fixed-cost entries for wholesale deletes/adds. + +Example: Let commits `1--2` be the first iteration of a patch series and +`A--C` the second iteration. Let's assume that `A` is a cherry-pick of +`2,` and `C` is a cherry-pick of `1` but with a small modification (say, +a fixed typo). Visualize the commits as a bipartite graph: + +------------ + 1 A + + 2 B + + C +------------ + +We are looking for a "best" explanation of the new series in terms of +the old one. We can represent an "explanation" as an edge in the graph: + + +------------ + 1 A + / + 2 --------' B + + C +------------ + +This explanation comes for "free" because there was no change. Similarly +`C` could be explained using `1`, but that comes at some cost c>0 +because of the modification: + +------------ + 1 ----. A + | / + 2 ----+---' B + | + `----- C + c>0 +------------ + +In mathematical terms, what we are looking for is some sort of a minimum +cost bipartite matching; `1` is matched to `C` at some cost, etc. The +underlying graph is in fact a complete bipartite graph; the cost we +associate with every edge is the size of the diff between the two +commits' patches. To explain also new commits, we introduce dummy nodes +on both sides: + +------------ + 1 ----. A + | / + 2 ----+---' B + | + o `----- C + c>0 + o o + + o o +------------ + +The cost of an edge `o--C` is the size of `C`'s diff, modified by a +fudge factor that should be smaller than 100%. The cost of an edge +`o--o` is free. The fudge factor is necessary because even if `1` and +`C` have nothing in common, they may still share a few empty lines and +such, possibly making the assignment `1--C`, `o--o` slightly cheaper +than `1--o`, `o--C` even if `1` and `C` have nothing in common. With the +fudge factor we require a much larger common part to consider patches as +corresponding. + +The overall time needed to compute this algorithm is the time needed to +compute n+m commit diffs and then n*m diffs of patches, plus the time +needed to compute the least-cost assigment between n and m diffs. Git +uses an implementation of the Jonker-Volgenant algorithm to solve the +assignment problem, which has cubic runtime complexity. The matching +found in this case will look like this: + +------------ + 1 ----. A + | / + 2 ----+---' B + .--+-----' + o -' `----- C + c>0 + o ---------- o + + o ---------- o +------------ + + +SEE ALSO +-------- +linkgit:git-log[1] + +GIT +--- +Part of the linkgit:git[1] suite
Signature · 1 line
gitgitgadget
Re: [PATCH v3 17/20] range-diff: add a man page
Documentation/git-range-diff.txt line 19
Quoted 9 lines · Johannes Schindelin via GitGitGadget
+'git range-diff' [--color=[<when>]] [--no-color] [<diff-options>]
Documentation/git-range-diff.txt new file +8
-0+ [--dual-color] [--creation-factor=<factor>] + ( <range1> <range2> | <rev1>...<rev2> | <base> <rev1> <rev2> ) + +DESCRIPTION +----------- + +This command shows the differences between two versions of a patch +series, or more generally, two commit ranges (ignoring merges).
Does it completely ignore merges or does it die("not supported"), how is the user expected to cope with the accidental merge in the given range?
Quoted 8 lines
Documentation/git-range-diff.txt new file +8
-0+To that end, it first finds pairs of commits from both commit ranges +that correspond with each other. Two commits are said to correspond when +the diff between their patches (i.e. the author information, the commit +message and the commit diff) is reasonably small compared to the +patches' size. See ``Algorithm` below for details. + +Finally, the list of matching commits is shown in the order of the +second commit range, with unmatched commits being inserted just after
Documentation/git-range-diff.txt line 38
Quoted 10 lines
Documentation/git-range-diff.txt new file +10
-0+all of their ancestors have been shown. + + +OPTIONS +------- +--dual-color:: + When the commit diffs differ, recreate the original diffs' + coloring, and add outer -/+ diff markers with the *background* + being red/green to make it easier to see e.g. when there was a + change in what exact lines were added.
I presume this is a boolean option, and can be turned off with --no-dual-color, but not with --dual-color=no. Would it be worth to give the --no-option here as well.
The more pressing question I had when reading this, is whether this is the default.
Documentation/git-range-diff.txt line 50
Quoted 11 lines
+--creation-factor=<percent>::
Documentation/git-range-diff.txt new file +10
-0+ Set the creation/deletion cost fudge factor to `<percent>`. + Defaults to 60. Try a larger value if `git range-diff` erroneously + considers a large change a total rewrite (deletion of one commit + and addition of another), and a smaller one in the reverse case. + See the ``Algorithm`` section below for an explanation why this is + needed. + +<range1> <range2>:: + Compare the commits specified by the two ranges, where + `<range1>` is considered an older version of `<range2>`.
Is it really older? How does that help the user?
I think this comes from the notion of e.g. patch 4 ("range-diff: improve the order of the shown commits "), that assume the user wants the range-diff to be expressed with range2 as its "base range".
Quoted 2 lines
+<rev1>...<rev2>::
+ Equivalent to passing<rev2>..<rev1>and<rev1>..<rev2>.
That is cool.
Documentation/git-range-diff.txt line 136
Quoted 5 lines
Documentation/git-range-diff.txt new file +5
-0+Algorithm +--------- + +The general idea is this: we generate a cost matrix between the commits +in both commit ranges, then solve the least-cost assignment.
Can you say more about the generation of the cost matrix? I assume that it counts the number of lines added/deleted to make one patch into the other patch.
If that assumption was correct, an edit of a commit message adding one line is just as costly as adding one line in the diff.
Further I would assume that the context lines are ignored?
I think this is worth spelling out.
Another spot to look at is further metadata, such as author and author-date, which are kept the same in a rebase workflow.
Maybe worth noting that this algorithm doesn't pay special attention to these, but a change in them would be strong signal that the two patches compared are not the same?
I like the example below, thanks!
Stefan
Re: [PATCH v3 17/20] range-diff: add a man page
Hi Stefan,
Quoted 15 lines · Stefan Beller
Documentation/git-range-diff.txt line 19
Quoted 9 lines · Johannes Schindelin via GitGitGadget
+'git range-diff' [--color=[<when>]] [--no-color] [<diff-options>]
Documentation/git-range-diff.txt new file +8
-0+ [--dual-color] [--creation-factor=<factor>] + ( <range1> <range2> | <rev1>...<rev2> | <base> <rev1> <rev2> ) + +DESCRIPTION +----------- + +This command shows the differences between two versions of a patch +series, or more generally, two commit ranges (ignoring merges).Does it completely ignore merges or does it
die("not supported"), how is the user expected to cope with the accidental merge in the given range?
It ignores merges. It does not reject them. It simply ignores them and won't talk about them as a consequence.
Could you suggest an improved way to say that?
Quoted 16 lines
Quoted 16 lines
Documentation/git-range-diff.txt new file +16
-0+To that end, it first finds pairs of commits from both commit ranges +that correspond with each other. Two commits are said to correspond when +the diff between their patches (i.e. the author information, the commit +message and the commit diff) is reasonably small compared to the +patches' size. See ``Algorithm` below for details. + +Finally, the list of matching commits is shown in the order of the +second commit range, with unmatched commits being inserted just after +all of their ancestors have been shown. + + +OPTIONS +------- +--dual-color:: + When the commit diffs differ, recreate the original diffs' + coloring, and add outer -/+ diff markers with the *background*
Quoted 8 lines
Documentation/git-range-diff.txt line 38
Quoted 2 lines
Documentation/git-range-diff.txt new file +2
-0+ being red/green to make it easier to see e.g. when there was a + change in what exact lines were added.I presume this is a boolean option, and can be turned off with --no-dual-color, but not with --dual-color=no. Would it be worth to give the --no-option here as well.
The more pressing question I had when reading this, is whether this is the default.
In the final patch (which I mulled about adding or not for a couple of weeks), the --dual-color mode is the default, and the man page talks about --no-dual-color.
Do you want me to change this intermediate commit, even if that change will be reverted anyway?
Quoted 12 lines
Documentation/git-range-diff.txt line 50
Quoted 11 lines
+--creation-factor=<percent>::
Documentation/git-range-diff.txt new file +10
-0+ Set the creation/deletion cost fudge factor to `<percent>`. + Defaults to 60. Try a larger value if `git range-diff` erroneously + considers a large change a total rewrite (deletion of one commit + and addition of another), and a smaller one in the reverse case. + See the ``Algorithm`` section below for an explanation why this is + needed. + +<range1> <range2>:: + Compare the commits specified by the two ranges, where + `<range1>` is considered an older version of `<range2>`.
Quoted 1 line
Is it really older? How does that help the user?
It is important to get your ducks in a row, so to speak, when looking at range-diffs. They are even more unintuitive than diffs, so it makes sense to have a very clear mental picture of what you are trying to compare here.
The coloring gives a strong hint of "pre" vs "post", i.e. old vs new: the changes that are only in the "old" patches are marked with a minus with a red background color, which only really makes sense if you think about these changes as "dropped" or "removed" from the "new" changes.
So yes, it is really considered an older version, in my mind.
Again, if you have suggestions how to improve my patch (giving rise to a "new" patch :-)), let's hear them.
Quoted 3 lines
I think this comes from the notion of e.g. patch 4 ("range-diff: improve the order of the shown commits "), that assume the user wants the range-diff to be expressed with range2 as its "base range".
No, it is motivated by the fact that we use -/+ markers to indicate differences between the "old" and the "new" patches.
Quoted 9 lines
Documentation/git-range-diff.txt line 136
Quoted 5 lines
Documentation/git-range-diff.txt new file +5
-0+Algorithm +--------- + +The general idea is this: we generate a cost matrix between the commits +in both commit ranges, then solve the least-cost assignment.Can you say more about the generation of the cost matrix? I assume that it counts the number of lines added/deleted to make one patch into the other patch.
I think that is correct.
*reading the patch*
Actually, no, I was wrong. For the cost matrix, the *length* of the diff *of the diffs* is computed. Think of it as
git diff --no-index <(git diff A^!) <(git diff B^!) | wc -l
Quoted 2 lines
If that assumption was correct, an edit of a commit message adding one line is just as costly as adding one line in the diff.
Nope, editing a commit message does not have any influence on the algorithm's idea whether the commit matches or not. Only the content changes associated with the commit have any say over this.
Quoted 1 line
Further I would assume that the context lines are ignored?
No.
Quoted 1 line
I think this is worth spelling out.
Sure.
Quoted 2 lines
Another spot to look at is further metadata, such as author and author-date, which are kept the same in a rebase workflow.
I encourage you to offer that as an add-on patch series. Because what you suggest is not necessary for my use cases, so I'd rather not spend time on it.
Ciao,
Dscho
Re: [PATCH v3 17/20] range-diff: add a man page
Quoted 21 lines · Johannes Schindelin
Hi Stefan,
Quoted 15 lines · Stefan Beller
Documentation/git-range-diff.txt line 19
Quoted 9 lines · Johannes Schindelin via GitGitGadget
+'git range-diff' [--color=[<when>]] [--no-color] [<diff-options>]
Documentation/git-range-diff.txt new file +8
-0+ [--dual-color] [--creation-factor=<factor>] + ( <range1> <range2> | <rev1>...<rev2> | <base> <rev1> <rev2> ) + +DESCRIPTION +----------- + +This command shows the differences between two versions of a patch +series, or more generally, two commit ranges (ignoring merges).Does it completely ignore merges or does it
die("not supported"), how is the user expected to cope with the accidental merge in the given range?
Quoted 4 lines
It ignores merges. It does not reject them. It simply ignores them and won't talk about them as a consequence.
Could you suggest an improved way to say that?
Well that is what the patch said already; I was just dense in reading. I just tested it, and the commit with more than one parent itself is ignored (not showing up in the output), but the commits that are merged in are still considered. So giving range1 as f7761a5a065..0a5677f6f68 with
0a5677f6f68 (Merge branch 'js/branch-diff' into pu, 2018-07-06) f7761a5a065 (Merge branch 'jk/fsck-gitmodules-gently' into jch, 2018-07-06)
still produces cool output and with --word-diff it is even more amazing, as it just tells me a large part was s/branch-diff/range-diff/ :-)
12: cbc752c57ce ! 12: 7273cc64797 branch-diff: use color for the commit pairs
@@ -1,31 +1,28 @@
Author: Johannes Schindelin <[email protected]>
[-branch-diff:-]{+range-diff:+} use color for the commit pairs
Arguably the most important part of [-branch-diff's-]{+`git
range-diff`'s+} output is the
list of commits in the two branches, together with their relationships.
For that reason, tbdiff introduced color-coding that is pretty intuitive, especially for unchanged patches (all dim yellow, like the first line in git show's output) vs modified patches (old commit is red, new commit is green). Let's imitate that color scheme.
[- While at it, also copy tbdiff's change of the fragment color
to magenta.-]
Sorry for being offtopic here; I do not have a better suggestion than what is already said.
Quoted 6 lines
Quoted 5 lines
I presume this is a boolean option, and can be turned off with --no-dual-color, but not with --dual-color=no. Would it be worth to give the --no-option here as well.
The more pressing question I had when reading this, is whether this is the default.
Quoted 6 lines
In the final patch (which I mulled about adding or not for a couple of weeks), the
--dual-colormode is the default, and the man page talks about--no-dual-color.Do you want me to change this intermediate commit, even if that change will be reverted anyway?
No, I just wasn't aware of that part, yet, as I have seen some patch series that add the man page/documentation as their final patch. This looked so similar that I assumed this is the final man page. My bad!
Quoted 9 lines
The coloring gives a strong hint of "pre" vs "post", i.e. old vs new: the changes that are only in the "old" patches are marked with a minus with a red background color, which only really makes sense if you think about these changes as "dropped" or "removed" from the "new" changes.
So yes, it is really considered an older version, in my mind.
Again, if you have suggestions how to improve my patch (giving rise to a "new" patch :-)), let's hear them.
I will send patches as I get more used to this new tool.
Quoted 7 lines
Quoted 4 lines
I think this comes from the notion of e.g. patch 4 ("range-diff: improve the order of the shown commits "), that assume the user wants the range-diff to be expressed with range2 as its "base range".
No, it is motivated by the fact that we use -/+ markers to indicate differences between the "old" and the "new" patches.
And at this point in time we do not want to question the use of -/+ markers for the next layer of abstraction. While +/- are well understood on the patch level
we could argue for a different set of characters for diffs of diffs, as that helps
to differentiate between the layers ("is it added in the diff or the diff of the diff
due to e.g. different context?"), but then it would not recurse. (I am not sure I
want to read diff of diffs of diffs)
Okay, for now I accept the terms of old and new patches, as I have no better idea.
Quoted 11 lines
Quoted 10 lines
Documentation/git-range-diff.txt line 136
Quoted 6 lines
Documentation/git-range-diff.txt new file +5
-0+Algorithm +--------- + +The general idea is this: we generate a cost matrix between the commits +in both commit ranges, then solve the least-cost assignment.Can you say more about the generation of the cost matrix? I assume that it counts the number of lines added/deleted to make one patch into the other patch.
Quoted 8 lines
I think that is correct.
*reading the patch*
Actually, no, I was wrong. For the cost matrix, the *length* of the diff *of the diffs* is computed. Think of it as
git diff --no-index <(git diff A^!) <(git diff B^!) | wc -l
So the matching is based only on diffs, but the output still takes the commit messages into account. So when diffing my series to the series that Junio applies, (merely adding his sign off,) would be a "cost of 0" in this context, but I still have output.
Quoted 7 lines
Quoted 3 lines
Another spot to look at is further metadata, such as author and author-date, which are kept the same in a rebase workflow.
I encourage you to offer that as an add-on patch series. Because what you suggest is not necessary for my use cases, so I'd rather not spend time on it.
Makes sense. When I stumble about this yet theoretical problem to materialize in practice I will send a patch. In my mind this is not another use case, but just
an improved matching, with the matching that this series provides being good enough for now.
Thanks,
Stefan
Re: [PATCH v3 17/20] range-diff: add a man page
Hi Stefan,
Quoted 24 lines · Stefan Beller
Quoted 22 lines · Johannes Schindelin
Quoted 15 lines · Stefan Beller
Documentation/git-range-diff.txt line 19
Quoted 9 lines · Johannes Schindelin via GitGitGadget
+'git range-diff' [--color=[<when>]] [--no-color] [<diff-options>]
Documentation/git-range-diff.txt new file +8
-0+ [--dual-color] [--creation-factor=<factor>] + ( <range1> <range2> | <rev1>...<rev2> | <base> <rev1> <rev2> ) + +DESCRIPTION +----------- + +This command shows the differences between two versions of a patch +series, or more generally, two commit ranges (ignoring merges).Does it completely ignore merges or does it
die("not supported"), how is the user expected to cope with the accidental merge in the given range?It ignores merges. It does not reject them. It simply ignores them and won't talk about them as a consequence.
Quoted 6 lines
Quoted 1 line
Could you suggest an improved way to say that?
Well that is what the patch said already; I was just dense in reading. I just tested it, and the commit with more than one parent itself is ignored (not showing up in the output), but the commits that are merged in are still considered.
So a more accurate wording would be "ignoring merge commits" rather than "ignoring merges".
Makes sense?
Quoted 7 lines
So giving range1 as
f7761a5a065..0a5677f6f68with
0a5677f6f68(Merge branch 'js/branch-diff' into pu, 2018-07-06)f7761a5a065(Merge branch 'jk/fsck-gitmodules-gently' into jch, 2018-07-06)still produces cool output and with --word-diff it is even more amazing, as it just tells me a large part was s/branch-diff/range-diff/ :-)
I never thought about using --word-diff with range-diff.
Sadly, for me it gives rather stupid output, e.g.
4: 6927c11a311 ! 4: ebf3fea2517 range-diff: make --dual-color the default mode
@@ -14,6 +14,15 @@
diff --git a/Documentation/git-range-diff.txt b/Documentation/git-range-diff.txt
--- a/Documentation/git-range-diff.txt
+++ b/Documentation/git-range-diff.txt
{+@@+}
{+ --------+}
{+ [verse]+}
{+ 'git range-diff' [--color=[<when>]] [--no-color] [<diff-options>]+}
{+- [--dual-color] [--creation-factor=<factor>]+}
{++ [--no-dual-color] [--creation-factor=<factor>]+}
{+ ( <range1> <range2> | <rev1>...<rev2> | <base> <rev1> <rev2> )+}
{+ +}
{+ DESCRIPTION+}
@@
OPTIONSQuoted 17 lines
Quoted 17 lines
Quoted 9 lines
Documentation/git-range-diff.txt line 136
Quoted 5 lines
Documentation/git-range-diff.txt new file +5
-0+Algorithm +--------- + +The general idea is this: we generate a cost matrix between the commits +in both commit ranges, then solve the least-cost assignment.Can you say more about the generation of the cost matrix? I assume that it counts the number of lines added/deleted to make one patch into the other patch.
I think that is correct.
*reading the patch*
Actually, no, I was wrong. For the cost matrix, the *length* of the diff *of the diffs* is computed. Think of it as
Quoted 6 lines
Quoted 1 line
git diff --no-index <(git diff A^!) <(git diff B^!) | wc -lSo the matching is based only on diffs, but the output still takes the commit messages into account. So when diffing my series to the series that Junio applies, (merely adding his sign off,) would be a "cost of 0" in this context, but I still have output.
Exactly.
Quoted 11 lines
Quoted 6 lines
Quoted 2 lines
Another spot to look at is further metadata, such as author and author-date, which are kept the same in a rebase workflow.
I encourage you to offer that as an add-on patch series. Because what you suggest is not necessary for my use cases, so I'd rather not spend time on it.
Makes sense. When I stumble about this yet theoretical problem to materialize in practice I will send a patch. In my mind this is not another use case, but just an improved matching, with the matching that this series provides being good enough for now.
Sure.
Ciao,
Dscho
Re: [PATCH v3 17/20] range-diff: add a man page
In reply to Johannes Schindelin via GitGitGadgetDocumentation/git-range-diff.txt line 5
Quoted 13 lines · Johannes Schindelin via GitGitGadget
The bulk of this patch consists of a heavily butchered version of tbdiff's README written by Thomas Rast and Thomas Gummerer, lifted from
https://github.com/trast/tbdiff.Signed-off-by Johannes Schindelin <[email protected]>---
Documentation/git-range-diff.txt +5
-0@@ -0,0 +1,235 @@ +To that end, it first finds pairs of commits from both commit ranges +that correspond with each other. Two commits are said to correspond when +the diff between their patches (i.e. the author information, the commit +message and the commit diff) is reasonably small compared to the +patches' size. See ``Algorithm` below for details.
Unbalanced number of backticks on "Algorithm".
Quoted 3 lines
Documentation/git-range-diff.txt +3
-0+Finally, the list of matching commits is shown in the order of the +second commit range, with unmatched commits being inserted just after +all of their ancestors have been shown.
Re: [PATCH v3 17/20] range-diff: add a man page
Hi Eric,
Quoted 6 lines · Eric Sunshine
Quoted 4 lines · Johannes Schindelin via GitGitGadget
The bulk of this patch consists of a heavily butchered version of tbdiff's README written by Thomas Rast and Thomas Gummerer, lifted from
https://github.com/trast/tbdiff.
Quoted 11 lines
Documentation/git-range-diff.txt line 5
Quoted 9 lines
Signed-off-by Johannes Schindelin <[email protected]>---
Documentation/git-range-diff.txt +5
-0@@ -0,0 +1,235 @@ +To that end, it first finds pairs of commits from both commit ranges +that correspond with each other. Two commits are said to correspond when +the diff between their patches (i.e. the author information, the commit +message and the commit diff) is reasonably small compared to the +patches' size. See ``Algorithm` below for details.Unbalanced number of backticks on "Algorithm".
Of course!
Thanks,
Dscho
Quoted 5 lines
Quoted 4 lines
Documentation/git-range-diff.txt +3
-0+Finally, the list of matching commits is shown in the order of the +second commit range, with unmatched commits being inserted just after +all of their ancestors have been shown.
PATCH v3 18/20 completion: support `git range-diff`
In reply to Johannes Schindelin via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
Tab completion of git range-diff is very convenient, especially given that the revision arguments to specify the commit ranges to compare are typically more complex than, say, your grandfather's git log arguments.
squash! WIP completion: support git range-diff
Revert "WIP completion: support git range-diff"
This reverts commit 2e7af652af9e53a19fd947f8ebe37a78043afa49.
--- contrib/completion/git-completion.bash | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
contrib/completion/git-completion.bash +14 -0
@@ -1976,6 +1976,20 @@ _git_push () __git_complete_remote_or_refspec } +_git_range_diff () +{ + case "$cur" in + --*) + __gitcomp " + --creation-factor= --dual-color + $__git_diff_common_options + " + return + ;; + esac + __git_complete_revlist +} + _git_rebase () { __git_find_repo_path
Signature · 1 line
gitgitgadget
Re: [PATCH v3 18/20] completion: support `git range-diff`
"Johannes Schindelin via GitGitGadget" <[email protected]>
Quoted 8 lines · writes:
From: Johannes Schindelin <[email protected]>
Tab completion of
git range-diffis very convenient, especially given that the revision arguments to specify the commit ranges to compare are typically more complex than, say, your grandfather'sgit logarguments.Signed-off-by Johannes Schindelin <[email protected]>
Have three-dash lines here, or perhaps have some validation hook in the garden-shears tool to notice these leftoer bits we see below?
Quoted 35 lines
squash! WIP completion: support
git range-diffRevert "WIP completion: support
git range-diff"This reverts commit
2e7af652af9e53a19fd947f8ebe37a78043afa49.--- contrib/completion/git-completion.bash | 14 ++++++++++++++ 1 file changed, 14 insertions(+)contrib/completion/git-completion.bash +14
-0@@ -1976,6 +1976,20 @@ _git_push () __git_complete_remote_or_refspec } +_git_range_diff () +{ + case "$cur" in + --*) + __gitcomp " + --creation-factor= --dual-color + $__git_diff_common_options + " + return + ;; + esac + __git_complete_revlist +} + _git_rebase () { __git_find_repo_path
Re: [PATCH v3 18/20] completion: support `git range-diff`
Hi Junio,
Quoted 10 lines · Junio C Hamano
"Johannes Schindelin via GitGitGadget" <[email protected]>
Quoted 7 lines · writes:
From: Johannes Schindelin <[email protected]>
Tab completion of
git range-diffis very convenient, especially given that the revision arguments to specify the commit ranges to compare are typically more complex than, say, your grandfather'sgit logarguments.
Quoted 4 lines
Quoted 1 line
Signed-off-by Johannes Schindelin <[email protected]>Have three-dash lines here, or perhaps have some validation hook in the garden-shears tool to notice these leftoer bits we see below?
This is just a simple case of me overlooking the squash! while rebasing. The shears were not involved, as it is not a branch thicket, it is a simple, linear branch.
I guess I could install some sort of post-rewrite hook, but then, I'd rather have this as a more generally useful feature, directly in rebase -i: if running in autosquash mode, when offering to edit squashed commit messages, git rebase -i should refuse by default to accept a commit message that contains a line that starts with squash! .
Would make for a nice micro-project, methinks.
Quoted 6 lines
Quoted 6 lines
squash! WIP completion: support
git range-diffRevert "WIP completion: support
git range-diff"This reverts commit
2e7af652af9e53a19fd947f8ebe37a78043afa49.
---
I will fix this in my branch, of course, and force-push, but will wait with sending out a new revision of the patch series in case more reviews roll in.
Ciao,
Dscho
PATCH v3 19/20 range-diff: left-pad patch numbers
In reply to Johannes Schindelin via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
As pointed out by Elijah Newren, tbdiff has this neat little alignment trick where it outputs the commit pairs with patch numbers that are padded to the maximal patch number's width:
1: cafedead = 1: acefade first patch [...] 314: beefeada < 314: facecab up to PI!
Let's do the same in range-diff, too.
--- range-diff.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-)
range-diff.c +13 -8
@@ -254,7 +254,8 @@ static void get_correspondences(struct string_list *a, struct string_list *b,free (b2a); } -static void output_pair_header (struct diff_options *diffopt,struct strbuf *buf, +static void output_pair_header (struct diff_options *diffopt,int patch_no_width, +struct strbuf *buf,struct patch_util *a_util,struct patch_util *b_util) {
@@ -293,9 +294,9 @@ static void output_pair_header(struct diff_options *diffopt, struct strbuf *buf,strbuf_reset (buf);strbuf_addstr (buf, status =='!' ? color_old : color);if (!a_util) -strbuf_addf (buf,"-: %s " , dashes); +strbuf_addf (buf,"%*s: %s " , patch_no_width,"-" , dashes);else -strbuf_addf (buf,"%d: %s " , a_util->i +1 , +strbuf_addf (buf,"%*d: %s " , patch_no_width, a_util->i +1 ,find_unique_abbrev (&a_util->oid,DEFAULT_ABBREV ));if (status =='!' )
@@ -305,9 +306,9 @@ static void output_pair_header(struct diff_options *diffopt, struct strbuf *buf,strbuf_addf (buf,"%s%s" , color_reset, color_new);if (!b_util) -strbuf_addf (buf," -: %s" , dashes); +strbuf_addf (buf," %*s: %s" , patch_no_width,"-" , dashes);else -strbuf_addf (buf," %d: %s" , b_util->i +1 , +strbuf_addf (buf," %*d: %s" , patch_no_width, b_util->i +1 ,find_unique_abbrev (&b_util->oid,DEFAULT_ABBREV )); commit =lookup_commit_reference (oid);
@@ -360,6 +361,7 @@ static void output(struct string_list *a, struct string_list *b, struct diff_options *diffopt) {struct strbuf buf =STRBUF_INIT ; +int patch_no_width =decimal_width (1 + (a->nr > b->nr ? a->nr : b->nr));int i =0 , j =0 ;/*
@@ -381,21 +383,24 @@ static void output(struct string_list *a, struct string_list *b,/* Show unmatched LHS commit whose predecessors were shown. */ if (i < a->nr && a_util->matching <0 ) { -output_pair_header (diffopt, &buf, a_util,NULL ); +output_pair_header (diffopt, patch_no_width, &buf, + a_util,NULL ); i++;continue ; }/* Show unmatched RHS commits. */ while (j < b->nr && b_util->matching <0 ) { -output_pair_header (diffopt, &buf,NULL , b_util); +output_pair_header (diffopt, patch_no_width, &buf, +NULL , b_util); b_util = ++j < b->nr ? b->items[j].util :NULL ; }/* Show matching LHS/RHS pair. */ if (j < b->nr) { a_util = a->items[b_util->matching].util; -output_pair_header (diffopt, &buf, a_util, b_util); +output_pair_header (diffopt, patch_no_width, &buf, + a_util, b_util);if (!(diffopt->output_format &DIFF_FORMAT_NO_OUTPUT ))patch_diff (a->items[b_util->matching].string, b->items[j].string, diffopt);
Signature · 1 line
gitgitgadget
PATCH v3 20/20 range-diff: make --dual-color the default mode
In reply to Johannes Schindelin via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
After using this command extensively for the last two months, this developer came to the conclusion that even if the dual color mode still leaves a lot of room for confusion what was actually changed, the non-dual color mode is substantially worse in that regard.
Therefore, we really want to make the dual color mode the default.
--- Documentation/git-range-diff.txt | 13 ++++++++----- builtin/range-diff.c | 10 ++++++---- contrib/completion/git-completion.bash | 2 +- 3 files changed, 15 insertions(+), 10 deletions(-)
Documentation/git-range-diff.txt +8 -5
@@ -31,11 +31,14 @@ all of their ancestors have been shown. OPTIONS ------- ---dual-color:: - When the commit diffs differ, recreate the original diffs' - coloring, and add outer -/+ diff markers with the *background* - being red/green to make it easier to see e.g. when there was a - change in what exact lines were added. +--no-dual-color:: + When the commit diffs differ, `git range-diff` recreates the + original diffs' coloring, and add outer -/+ diff markers with + the *background* being red/green to make it easier to see e.g. + when there was a change in what exact lines were added. This is + known to `range-diff` as "dual coloring". Use `--no-dual-color` + to revert to color all lines according to the outer diff markers + (and completely ignore the inner diff when it comes to color). --creation-factor=<percent>:: Set the creation/deletion cost fudge factor to `<percent>`.
builtin/range-diff.c +6 -4
@@ -20,11 +20,11 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix) {int creation_factor =60 ;struct diff_options diffopt = {NULL }; -int dual_color =0 ; +int simple_color =-1 ;struct option options[] = {OPT_INTEGER (0 ,"creation-factor" , &creation_factor,N_ ("Percentage by which creation is weighted" )), -OPT_BOOL (0 ,"dual-color" , &dual_color, +OPT_BOOL (0 ,"no-dual-color" , &simple_color,N_ ("color both diff and diff-between-diffs" )),OPT_END () };
@@ -53,8 +53,10 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix) argc = j;diff_setup_done (&diffopt); -if (dual_color) { - diffopt.use_color =1 ; +if (simple_color <1 ) { +if (!simple_color) +/* force color when --dual-color was used */ + diffopt.use_color =1 ; diffopt.flags.dual_color_diffed_diffs =1 ; }
contrib/completion/git-completion.bash +1 -1
@@ -1981,7 +1981,7 @@ _git_range_diff () case "$cur" in --*) __gitcomp " - --creation-factor= --dual-color + --creation-factor= --no-dual-color $__git_diff_common_options " return
Signature · 1 line
gitgitgadget
Re: [PATCH v3 20/20] range-diff: make --dual-color the default mode
Quoted 3 lines · Johannes Schindelin via GitGitGadget
After using this command extensively for the last two months, this developer came to the conclusion that even if the dual color mode still leaves a lot of room for confusion what was actually changed, the
"...confusion _about_ what..."
Quoted 8 lines
non-dual color mode is substantially worse in that regard.
Therefore, we really want to make the dual color mode the default.
Signed-off-by Johannes Schindelin <[email protected]>---
Documentation/git-range-diff.txt all of their ancestors have been shown. · line 33
Quoted 8 lines
Documentation/git-range-diff.txt +3
-5@@ -31,11 +31,14 @@ all of their ancestors have been shown. ---dual-color:: - When the commit diffs differ, recreate the original diffs' - coloring, and add outer -/+ diff markers with the *background* - being red/green to make it easier to see e.g. when there was a - change in what exact lines were added. +--no-dual-color:: + When the commit diffs differ, `git range-diff` recreates the + original diffs' coloring, and add outer -/+ diff markers with
s/add/adds/
Quoted 5 lines
Documentation/git-range-diff.txt +5
-0+ the *background* being red/green to make it easier to see e.g. + when there was a change in what exact lines were added. This is + known to `range-diff` as "dual coloring". Use `--no-dual-color` + to revert to color all lines according to the outer diff markers + (and completely ignore the inner diff when it comes to color).
Re: [PATCH v3 20/20] range-diff: make --dual-color the default mode
Hi Eric,
Quoted 12 lines · Eric Sunshine
Quoted 3 lines · Johannes Schindelin via GitGitGadget
After using this command extensively for the last two months, this developer came to the conclusion that even if the dual color mode still leaves a lot of room for confusion what was actually changed, the
"...confusion _about_ what..."
Quoted 4 lines
non-dual color mode is substantially worse in that regard.
Therefore, we really want to make the dual color mode the default.
Quoted 20 lines
Documentation/git-range-diff.txt all of their ancestors have been shown. · line 33
Quoted 12 lines
Signed-off-by Johannes Schindelin <[email protected]>---
Documentation/git-range-diff.txt +3
-5@@ -31,11 +31,14 @@ all of their ancestors have been shown. ---dual-color:: - When the commit diffs differ, recreate the original diffs' - coloring, and add outer -/+ diff markers with the *background* - being red/green to make it easier to see e.g. when there was a - change in what exact lines were added. +--no-dual-color:: + When the commit diffs differ, `git range-diff` recreates the + original diffs' coloring, and add outer -/+ diff markers withs/add/adds/
Quoted 5 lines
Documentation/git-range-diff.txt +5
-0+ the *background* being red/green to make it easier to see e.g. + when there was a change in what exact lines were added. This is + known to `range-diff` as "dual coloring". Use `--no-dual-color` + to revert to color all lines according to the outer diff markers + (and completely ignore the inner diff when it comes to color).
Yep, thank you very much!
Dscho
v4 cover Add `range-diff`, a `tbdiff` lookalike
In reply to Johannes Schindelin via GitGitGadgetThe incredibly useful [git-tbdiff](https://github.com/trast/tbdiff) tool to compare patch series (say, to see what changed between two iterations sent to the Git mailing list) is slightly less useful for this developer due to the fact that it requires the hungarian and numpy Python packages which are for some reason really hard to build in MSYS2. So hard that I even had to give up, because it was simply easier to re-implement the whole shebang as a builtin command.
The project at https://github.com/trast/tbdiff seems to be dormant, anyway. Funny (and true) story: I looked at the open Pull Requests to see how active that project is, only to find to my surprise that I had submitted one in August 2015, and that it was still unanswered let alone merged.
While at it, I forward-ported AEvar's patch to force --decorate=no because git -p tbdiff would fail otherwise.
Side note: I work on implementing range-diff not only to make life easier for reviewers who have to suffer through v2, v3, ... of my patch series, but also to verify my changes before submitting a new iteration. And also, maybe even more importantly, I plan to use it to verify my merging-rebases of Git for Windows (for which I previously used to redirect the pre-rebase/post-rebase diffs vs upstream and then compare them using git diff --no-index). And of course any interested person can see what changes were necessary e.g. in the merging-rebase of Git for Windows onto v2.17.0 by running a command like:
```sh
base=^{/Start.the.merging-rebase}
tag=v2.17.0.windows.1
pre=$tag$base^2
git range-diff $pre$base..$pre $tag$base..$tag
```The command uses what it calls the "dual color mode" (can be disabled via --no-dual-color) which helps identifying what *actually* changed: it prefixes lines with a - (and red background) that correspond to the first commit range, and with a + (and green background) that correspond to the second range. The rest of the lines will be colored according to the original diffs.
Changes since v3:
- The cover letter was adjusted to reflect the new reality (the command is called range-diff now, not branch-diff, and --dual-color is the default).
- The documentation was adjusted a bit more in the patch that makes --dual-color the default.
- Clarified the calculation of the cost matrix, as per Stefan Beller's request.
- The man page now spells out that merge *commits* are ignored in the commit ranges (not merges per se).
- The code in linear-assignment.c was adjusted to use the SWAP() macro.
- The commit message of the patch introducing the first rudimentary implementation no longer talks about the "Hungarian" algorithm, but about the "linear assignment algorithm" instead.
- A bogus indentation change was backed out from the patch introducing the first rudimentary implementation.
- Instead of merely warning about missing .. in the 2-parameter invocation, we now exit with the error message.
- The diff_opt_parse() function is allowed to return a value larger than 1, indicating that more than just one command-line parameter was parsed. We now advance by the indicated value instead of always advancing exactly 1 (which is still correct much of the time).
- A lengthy if...else if...else if...else was simplified (from a logical point of view) by reordering it.
- The unnecessarily static variable dashes was turned into a local variable of the caller.
- The commit message talking about the new man page still referred to git branch --diff, which has been fixed.
- A forgotten t7910 reference was changed to t3206.
- An unbalanced double-tick was fixed in the man page.
- Fixed grammar both of the commit message and the description of the --no-dual-color option.
- To fix the build, a blank man page is now introduced together with the new range-diff command, even if it is populated for real only at a later patch (i.e. at the same time as before).
- The headaches Junio fears would be incurred by that simple workaround to avoid bogus white-space error reporting are fended off: a more complex patch is now in place that adds (and uses) a new white-space flag. Sadly, as is all too common when Junio "encourages" me to replace a simple workaround by something "proper", it caused all kinds of headaches to get this right, so I am rather less certain that the "proper" fix will cause us less headaches than the simple workaround would have done. But whatever.
- The dual color mode now also dims the changes that are exclusively in the first specified commit range, and uses bold face on the changes exclusively in the second one. This matches the intuition when using range-diff to compare an older iteration of a patch series to a newer one: the changes from the previous iteration that were replaced by new ones "fade", while the changes that replace them are "shiny new".
Changes since v2:
- Right-aligned the patch numbers in the commit pairs.
- Used ALLOC_ARRAY() in hungarian.c instead of xmalloc(sizeof()*size).
- Changed compute_assignment()s return type from int to void, as it always succeeds.
- Changed the Hungarian Algorithm to use an integer cost matrix.
- Changed the --creation-weight <double> option to --creation-factor <percent> where <percent> is an integer.
- Retitled 1/19 and 2/19 to better conform with the current conventions, as pointed out (and suggested) by Junio.
- Shut up Coverity, and at the same time avoided passing the unnecessary i and j parameters to output_pair_header().
- Removed support for the --no-patches option: we inherit diff_options' support for -s already (and much more).
- Removed the ugly _INV enum values, and introduced a beautiful GIT_COLOR_REVERSE instead. This way, whatever the user configured as color.diff.new (or .old) will be used in reverse in the dual color mode.
- Instead of overriding the fragment header color, the dual color mode will now reverse the "outer" fragment headers, too.
- Turned the stand-alone branch-diff command into the --diff option of git branch. Adjusted pretty much *all* commit messages to account for this. This change should no longer be visible: see below.
- Pretty much re-wrote the completion, to support the new --diff mode of git-branch. See below: it was reverted for range-diff.
- Renamed t7910 to t3206, to be closer to the git-branch tests.
- Ensured that git_diff_ui_config() gets called, and therefore color.diff.* respected.
- Avoided leaking four_spaces.
- Fixed a declaration in a for (;;) statement (which Junio had as a fixup! that I almost missed).
- Renamed branch --diff, which had been renamed from branch-diff (which was picked to avoid re-using tbdiff) to range-diff.
- Renamed hungarian.c and its header to linear-assignment.c
- Made --dual-color the default, and changed it to still auto-detect whether color should be used rather than forcing it
Johannes Schindelin (20):
linear-assignment: a function to solve least-cost assignment problems Introduce range-diff to compare iterations of a topic branch
range-diff: first rudimentary implementation
range-diff: improve the order of the shown commits
range-diff: also show the diff between patches
range-diff: right-trim commit messages
range-diff: indent the diffs just like tbdiff
range-diff: suppress the diff headers
range-diff: adjust the output of the commit pairs
range-diff: do not show "function names" in hunk headers
range-diff: use color for the commit pairs
color: add the meta color GIT_COLOR_REVERSE
diff: add an internal option to dual-color diffs of diffs
range-diff: offer to dual-color the diffs
range-diff --dual-color: fix bogus white-space warning
range-diff: populate the man page
completion: support git range-diff
range-diff: left-pad patch numbers
range-diff: make --dual-color the default mode
range-diff: use dim/bold cues to improve dual color mode
Thomas Rast (1):
range-diff: add tests
.gitignore | 1 + Documentation/config.txt | 6 +- Documentation/git-range-diff.txt | 252 +++++++++++ Makefile | 3 + builtin.h | 1 + builtin/range-diff.c | 106 +++++ cache.h | 3 +- color.h | 7 + command-list.txt | 1 + contrib/completion/git-completion.bash | 14 + diff.c | 119 ++++- diff.h | 16 +- git.c | 1 + linear-assignment.c | 201 ++++++++ linear-assignment.h | 22 + range-diff.c | 440 ++++++++++++++++++ range-diff.h | 9 + t/.gitattributes | 1 + t/t3206-range-diff.sh | 145 ++++++ t/t3206/history.export | 604 +++++++++++++++++++++++++ ws.c | 11 +- 21 files changed, 1932 insertions(+), 31 deletions(-) create mode 100644 Documentation/git-range-diff.txt create mode 100644 builtin/range-diff.c create mode 100644 linear-assignment.c create mode 100644 linear-assignment.h create mode 100644 range-diff.c create mode 100644 range-diff.h create mode 100755 t/t3206-range-diff.sh create mode 100644 t/t3206/history.export
base-commit: b7bd9486b055c3f967a870311e704e3bb0654e4f
Published-As: https://github.com/gitgitgadget/git/releases/tags/pr-1%2Fdscho%2Fbranch-diff-v4
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1/dscho/branch-diff-v4
Pull-Request: https://github.com/gitgitgadget/git/pull/1
Range-diff vs v3:
1: 39272eefc ! 1: f7e70689e linear-assignment: a function to solve least-cost assignment problems
@@ -223,9 +223,7 @@
+ BUG("negative j: %d", j);
+ i = pred[j];
+ column2row[j] = i;
-+ k = j;
-+ j = row2column[i];
-+ row2column[i] = k;
++ SWAP(j, row2column[i]);
+ } while (i1 != i);
+ }
+
2: 7f15b26d4 ! 2: 88134121d Introduce `range-diff` to compare iterations of a topic branch
@@ -10,6 +10,10 @@
At this point, we ignore tbdiff's color options, as they will all be
implemented later using diff_options. + Since f318d739159 (generate-cmds.sh: export all commands to
+ command-list.h, 2018-05-10), every new command *requires* a man page to
+ build right away, so let's also add a blank man page, too.
+
Signed-off-by: Johannes Schindelin <[email protected]>
diff --git a/.gitignore b/.gitignore
@@ -24,6 +28,22 @@
/git-rebase
/git-rebase--am
+diff --git a/Documentation/git-range-diff.txt b/Documentation/git-range-diff.txt
+new file mode 100644
+--- /dev/null
++++ b/Documentation/git-range-diff.txt
+@@
++git-range-diff(1)
++==================
++
++NAME
++----
++git-range-diff - Compare two commit ranges (e.g. two versions of a branch)
++
++GIT
++---
++Part of the linkgit:git[1] suite
+
diff --git a/Makefile b/Makefile
--- a/Makefile
+++ b/Makefile
3: 076e1192d ! 3: 4e3fb47a1 range-diff: first rudimentary implementation
@@ -4,7 +4,7 @@
At this stage, `git range-diff` can determine corresponding commits
of two related commit ranges. This makes use of the recently introduced
- implementation of the Hungarian algorithm.
+ implementation of the linear assignment algorithm.
The core of this patch is a straight port of the ideas of tbdiff, the
apparently dormant project at https://github.com/trast/tbdiff.
@@ -51,19 +51,17 @@
+ int res = 0;
+ struct strbuf range1 = STRBUF_INIT, range2 = STRBUF_INIT;
-- argc = parse_options(argc, argv, NULL, options,
-- builtin_range_diff_usage, 0);
-+ argc = parse_options(argc, argv, NULL, options, builtin_range_diff_usage,
-+ 0);
+ argc = parse_options(argc, argv, NULL, options,
+ builtin_range_diff_usage, 0);
- return 0;
+ if (argc == 2) {
+ if (!strstr(argv[0], ".."))
-+ warning(_("no .. in range: '%s'"), argv[0]);
++ die(_("no .. in range: '%s'"), argv[0]);
+ strbuf_addstr(&range1, argv[0]);
+
+ if (!strstr(argv[1], ".."))
-+ warning(_("no .. in range: '%s'"), argv[1]);
++ die(_("no .. in range: '%s'"), argv[1]);
+ strbuf_addstr(&range2, argv[1]);
+ } else if (argc == 3) {
+ strbuf_addf(&range1, "%s..%s", argv[0], argv[1]);
@@ -195,17 +193,21 @@
+ continue;
+ } else if (starts_with(line.buf, "@@ "))
+ strbuf_addstr(&buf, "@@");
-+ else if (line.buf[0] && !starts_with(line.buf, "index "))
++ else if (!line.buf[0] || starts_with(line.buf, "index "))
+ /*
+ * A completely blank (not ' \n', which is context)
+ * line is not valid in a diff. We skip it
+ * silently, because this neatly handles the blank
+ * separator line between commits in git-log
+ * output.
++ *
++ * We also want to ignore the diff's `index` lines
++ * because they contain exact blob hashes in which
++ * we are not interested.
+ */
-+ strbuf_addbuf(&buf, &line);
-+ else
+ continue;
++ else
++ strbuf_addbuf(&buf, &line);
+
+ strbuf_addch(&buf, '\n');
+ util->diffsize++;
4: e98489c8c = 4: 47bee09b0 range-diff: improve the order of the shown commits
5: 935cad180 ! 5: 94afaeaf2 range-diff: also show the diff between patches
@@ -55,21 +55,22 @@
+ int i, j, res = 0;
struct strbuf range1 = STRBUF_INIT, range2 = STRBUF_INIT;
-- argc = parse_options(argc, argv, NULL, options, builtin_range_diff_usage,
-- 0);
+ git_config(git_diff_ui_config, NULL);
+
+ diff_setup(&diffopt);
+ diffopt.output_format = DIFF_FORMAT_PATCH;
+
-+ argc = parse_options(argc, argv, NULL, options,
-+ builtin_range_diff_usage, PARSE_OPT_KEEP_UNKNOWN);
+ argc = parse_options(argc, argv, NULL, options,
+- builtin_range_diff_usage, 0);
++ builtin_range_diff_usage, PARSE_OPT_KEEP_UNKNOWN);
+
-+ for (i = j = 0; i < argc; i++) {
++ for (i = j = 0; i < argc; ) {
+ int c = diff_opt_parse(&diffopt, argv + i, argc - i, prefix);
+
+ if (!c)
-+ argv[j++] = argv[i];
++ argv[j++] = argv[i++];
++ else
++ i += c;
+ }
+ argc = j;
+ diff_setup_done(&diffopt);
6: 93ac1931d = 6: 41ab875a3 range-diff: right-trim commit messages
7: ca5282815 ! 7: a3dd99509 range-diff: indent the diffs just like tbdiff
@@ -39,7 +39,7 @@
+ diffopt.output_prefix_data = &four_spaces;
argc = parse_options(argc, argv, NULL, options,
- builtin_range_diff_usage, PARSE_OPT_KEEP_UNKNOWN);
+ builtin_range_diff_usage, PARSE_OPT_KEEP_UNKNOWN);
@@
strbuf_release(&range1);
8: 80622685f = 8: 61b2ff2f7 range-diff: suppress the diff headers
9: 6b31cbf72 ! 9: 9641ab5c0 range-diff: adjust the output of the commit pairs
@@ -26,25 +26,22 @@
-static const char *short_oid(struct patch_util *util)
+static void output_pair_header(struct strbuf *buf,
++ struct strbuf *dashes,
+ struct patch_util *a_util,
+ struct patch_util *b_util)
{
- return find_unique_abbrev(&util->oid, DEFAULT_ABBREV);
-+ static char *dashes;
+ struct object_id *oid = a_util ? &a_util->oid : &b_util->oid;
+ struct commit *commit;
+
-+ if (!dashes) {
-+ char *p;
-+
-+ dashes = xstrdup(find_unique_abbrev(oid, DEFAULT_ABBREV));
-+ for (p = dashes; *p; p++)
-+ *p = '-';
-+ }
++ if (!dashes->len)
++ strbuf_addchars(dashes, '-',
++ strlen(find_unique_abbrev(oid,
++ DEFAULT_ABBREV)));
+
+ strbuf_reset(buf);
+ if (!a_util)
-+ strbuf_addf(buf, "-: %s ", dashes);
++ strbuf_addf(buf, "-: %s ", dashes->buf);
+ else
+ strbuf_addf(buf, "%d: %s ", a_util->i + 1,
+ find_unique_abbrev(&a_util->oid, DEFAULT_ABBREV));
@@ -59,7 +56,7 @@
+ strbuf_addch(buf, '=');
+
+ if (!b_util)
-+ strbuf_addf(buf, " -: %s", dashes);
++ strbuf_addf(buf, " -: %s", dashes->buf);
+ else
+ strbuf_addf(buf, " %d: %s", b_util->i + 1,
+ find_unique_abbrev(&b_util->oid, DEFAULT_ABBREV));
@@ -84,7 +81,7 @@
static void output(struct string_list *a, struct string_list *b,
struct diff_options *diffopt)
{
-+ struct strbuf buf = STRBUF_INIT;
++ struct strbuf buf = STRBUF_INIT, dashes = STRBUF_INIT;
int i = 0, j = 0;
/*
@@ -94,7 +91,7 @@
if (i < a->nr && a_util->matching < 0) {
- printf("%d: %s < -: --------\n",
- i + 1, short_oid(a_util));
-+ output_pair_header(&buf, a_util, NULL);
++ output_pair_header(&buf, &dashes, a_util, NULL);
i++;
continue;
}
@@ -103,7 +100,7 @@
while (j < b->nr && b_util->matching < 0) {
- printf("-: -------- > %d: %s\n",
- j + 1, short_oid(b_util));
-+ output_pair_header(&buf, NULL, b_util);
++ output_pair_header(&buf, &dashes, NULL, b_util);
b_util = ++j < b->nr ? b->items[j].util : NULL;
}
@@ -113,7 +110,7 @@
- printf("%d: %s ! %d: %s\n",
- b_util->matching + 1, short_oid(a_util),
- j + 1, short_oid(b_util));
-+ output_pair_header(&buf, a_util, b_util);
++ output_pair_header(&buf, &dashes, a_util, b_util);
if (!(diffopt->output_format & DIFF_FORMAT_NO_OUTPUT))
patch_diff(a->items[b_util->matching].string,
b->items[j].string, diffopt);
@@ -122,6 +119,7 @@
}
}
+ strbuf_release(&buf);
++ strbuf_release(&dashes);
}
int show_range_diff(const char *range1, const char *range2,
10: ef997bb8b = 10: 0a52f8878 range-diff: do not show "function names" in hunk headers
11: 3d9e5b0ba ! 11: 2b8d09020 range-diff: add tests @@ -3,8 +3,8 @@
range-diff: add tests
These are essentially lifted from https://github.com/trast/tbdiff, with
- light touch-ups to account for the command now being an option of `git
- branch`.
+ light touch-ups to account for the command now being names `git
+ range-diff`.
Apart from renaming tbdiff to range-diff, only one test case needed to be adjusted: 11 - 'changed message'.
@@ -22,12 +22,13 @@
--- a/t/.gitattributes
+++ b/t/.gitattributes
@@
- /t5515/* eol=lf
- /t556x_common eol=lf
- /t7500/* eol=lf
-+/t7910/* eol=lf
- /t8005/*.txt eol=lf
- /t9*/*.dump eol=lf
+ t[0-9][0-9][0-9][0-9]/* -whitespace
+ /diff-lib/* eol=lf
+ /t0110/url-* binary
++/t3206/* eol=lf
+ /t3900/*.txt eol=lf
+ /t3901/*.txt eol=lf
+ /t4034/*/* eol=lf
diff --git a/t/t3206-range-diff.sh b/t/t3206-range-diff.sh new file mode 100755
12: 7273cc647 ! 12: fb83ce71a range-diff: use color for the commit pairs
@@ -20,11 +20,12 @@
}
-static void output_pair_header(struct strbuf *buf,
-+static void output_pair_header(struct diff_options *diffopt, struct strbuf *buf,
++static void output_pair_header(struct diff_options *diffopt,
++ struct strbuf *buf,
+ struct strbuf *dashes,
struct patch_util *a_util,
struct patch_util *b_util)
{
- static char *dashes;
struct object_id *oid = a_util ? &a_util->oid : &b_util->oid;
struct commit *commit;
+ char status;
@@ -34,11 +35,10 @@
+ const char *color_commit = diff_get_color_opt(diffopt, DIFF_COMMIT);
+ const char *color;
- if (!dashes) {
- char *p;
-@@
- *p = '-';
- }
+ if (!dashes->len)
+ strbuf_addchars(dashes, '-',
+ strlen(find_unique_abbrev(oid,
+ DEFAULT_ABBREV)));
+ if (!b_util) {
+ color = color_old;
@@ -57,7 +57,7 @@
strbuf_reset(buf);
+ strbuf_addstr(buf, status == '!' ? color_old : color);
if (!a_util)
- strbuf_addf(buf, "-: %s ", dashes);
+ strbuf_addf(buf, "-: %s ", dashes->buf);
else
strbuf_addf(buf, "%d: %s ", a_util->i + 1,
find_unique_abbrev(&a_util->oid, DEFAULT_ABBREV));
@@ -77,7 +77,7 @@
+ strbuf_addf(buf, "%s%s", color_reset, color_new);
if (!b_util)
- strbuf_addf(buf, " -: %s", dashes);
+ strbuf_addf(buf, " -: %s", dashes->buf);
@@
const char *commit_buffer = get_commit_buffer(commit, NULL);
const char *subject;
@@ -99,24 +99,27 @@
/* Show unmatched LHS commit whose predecessors were shown. */
if (i < a->nr && a_util->matching < 0) {
-- output_pair_header(&buf, a_util, NULL);
-+ output_pair_header(diffopt, &buf, a_util, NULL);
+- output_pair_header(&buf, &dashes, a_util, NULL);
++ output_pair_header(diffopt,
++ &buf, &dashes, a_util, NULL);
i++;
continue;
}
/* Show unmatched RHS commits. */
while (j < b->nr && b_util->matching < 0) {
-- output_pair_header(&buf, NULL, b_util);
-+ output_pair_header(diffopt, &buf, NULL, b_util);
+- output_pair_header(&buf, &dashes, NULL, b_util);
++ output_pair_header(diffopt,
++ &buf, &dashes, NULL, b_util);
b_util = ++j < b->nr ? b->items[j].util : NULL;
}
/* Show matching LHS/RHS pair. */
if (j < b->nr) {
a_util = a->items[b_util->matching].util;
-- output_pair_header(&buf, a_util, b_util);
-+ output_pair_header(diffopt, &buf, a_util, b_util);
+- output_pair_header(&buf, &dashes, a_util, b_util);
++ output_pair_header(diffopt,
++ &buf, &dashes, a_util, b_util);
if (!(diffopt->output_format & DIFF_FORMAT_NO_OUTPUT))
patch_diff(a->items[b_util->matching].string,
b->items[j].string, diffopt);
13: 96a3073fb = 13: 9ccb9516a color: add the meta color GIT_COLOR_REVERSE
14: 6be4baf60 = 14: 9de5bd229 diff: add an internal option to dual-color diffs of diffs
15: 02e13c0c6 ! 15: 21b2f9e4b range-diff: offer to dual-color the diffs
@@ -40,4 +40,4 @@
+
if (argc == 2) {
if (!strstr(argv[0], ".."))
- warning(_("no .. in range: '%s'"), argv[0]);
+ die(_("no .. in range: '%s'"), argv[0]);
16: dfa7b1e71 < -: --------- range-diff --dual-color: work around bogus white-space warning
-: --------- > 16: f4252f2b2 range-diff --dual-color: fix bogus white-space warning
17: 799da25ef ! 17: 9e09c6be6 range-diff: add a man page @@ -1,6 +1,6 @@
Author: Johannes Schindelin <[email protected]>
- range-diff: add a man page
+ range-diff: populate the man page
The bulk of this patch consists of a heavily butchered version of tbdiff's README written by Thomas Rast and Thomas Gummerer, lifted from
@@ -9,17 +9,12 @@
diff --git a/Documentation/git-range-diff.txt b/Documentation/git-range-diff.txt
-new file mode 100644
---- /dev/null
+--- a/Documentation/git-range-diff.txt
+++ b/Documentation/git-range-diff.txt
@@
-+git-range-diff(1)
-+==================
-+
-+NAME
-+----
-+git-range-diff - Compare two commit ranges (e.g. two versions of a branch) -+
+ ----
+ git-range-diff - Compare two commit ranges (e.g. two versions of a branch) +
+SYNOPSIS
+--------
+[verse]
@@ -31,13 +26,13 @@
+-----------
+
+This command shows the differences between two versions of a patch
-+series, or more generally, two commit ranges (ignoring merges). ++series, or more generally, two commit ranges (ignoring merge commits).
+
+To that end, it first finds pairs of commits from both commit ranges +that correspond with each other. Two commits are said to correspond when +the diff between their patches (i.e. the author information, the commit +message and the commit diff) is reasonably small compared to the
-+patches' size. See ``Algorithm` below for details.
++patches' size. See ``Algorithm`` below for details.
+
+Finally, the list of matching commits is shown in the order of the +second commit range, with unmatched commits being inserted just after
@@ -150,6 +145,10 @@
+The general idea is this: we generate a cost matrix between the commits +in both commit ranges, then solve the least-cost assignment.
+
++The cost matrix is populated thusly: for each pair of commits, both ++diffs are generated and the "diff of diffs" is generated, with 3 context ++lines, then the number of lines in that diff is used as cost.
++
+To avoid false positives (e.g. when a patch has been removed, and an +unrelated patch has been added between two iterations of the same patch +series), the cost matrix is extended to allow for that, by adding
@@ -245,6 +244,6 @@
+--------
+linkgit:git-log[1]
+
-+GIT
-+---
-+Part of the linkgit:git[1] suite
+ GIT
+ ---
+ Part of the linkgit:git[1] suite
18: d05b54c60 ! 18: 9b3632324 completion: support git range-diff
@@ -4,17 +4,11 @@
Tab completion of git range-diff is very convenient, especially given that the revision arguments to specify the commit ranges to
- compare are typically more complex than, say, your grandfather's `git
- log` arguments.
+ compare are typically more complex than, say, what is normally passed
+ to git log.
- squash! WIP completion: support git range-diff
-
- Revert "WIP completion: support git range-diff"
-
- This reverts commit 2e7af652af9e53a19fd947f8ebe37a78043afa49.
-
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash --- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
19: 144363006 < -: --------- range-diff: left-pad patch numbers -: --------- > 19: 07ec215e8 range-diff: left-pad patch numbers 20: 4a68b95ce ! 20: b370468e7 range-diff: make --dual-color the default mode
@@ -4,7 +4,7 @@
After using this command extensively for the last two months, this developer came to the conclusion that even if the dual color mode still
- leaves a lot of room for confusion what was actually changed, the
+ leaves a lot of room for confusion about what was actually changed, the
non-dual color mode is substantially worse in that regard.
Therefore, we really want to make the dual color mode the default.
@@ -14,6 +14,15 @@
diff --git a/Documentation/git-range-diff.txt b/Documentation/git-range-diff.txt --- a/Documentation/git-range-diff.txt
+++ b/Documentation/git-range-diff.txt
+@@
+ --------
+ [verse]
+ 'git range-diff' [--color=[<when>]] [--no-color] [<diff-options>]
+- [--dual-color] [--creation-factor=<factor>]
++ [--no-dual-color] [--creation-factor=<factor>]
+ ( <range1> <range2> | <rev1>...<rev2> | <base> <rev1> <rev2> )
+
+ DESCRIPTION
@@
OPTIONS
@@ -25,7 +34,7 @@
- change in what exact lines were added.
+--no-dual-color::
+ When the commit diffs differ, git range-diff recreates the
-+ original diffs' coloring, and add outer -/+ diff markers with
++ original diffs' coloring, and adds outer -/+ diff markers with
+ the *background* being red/green to make it easier to see e.g.
+ when there was a change in what exact lines were added. This is
+ known to range-diff as "dual coloring". Use --no-dual-color
@@ -34,6 +43,31 @@
--creation-factor=<percent>::
Set the creation/deletion cost fudge factor to <percent>.
+@@
+ show`'s output, and the third line colors the old commit red, the new
+ one green and the rest like git show's commit header.
+
+-The color-coded diff is actually a bit hard to read, though, as it +-colors the entire lines red or green. The line that added "What is +-unexpected" in the old commit, for example, is completely red, even if +-the intent of the old commit was to add something.
++A naive color-coded diff of diffs is actually a bit hard to read, ++though, as it colors the entire lines red or green. The line that added ++"What is unexpected" in the old commit, for example, is completely red, ++even if the intent of the old commit was to add something. +
+-To help with that, use the --dual-color mode. In this mode, the diff +-of diffs will retain the original diff colors, and prefix the lines with +--/+ markers that have their *background* red or green, to make it more +-obvious that they describe how the diff itself changed. ++To help with that, range uses the --dual-color mode by default. In ++this mode, the diff of diffs will retain the original diff colors, and ++prefix the lines with -/+ markers that have their *background* red or ++green, to make it more obvious that they describe how the diff itself ++changed.
+
+
+ Algorithm
diff --git a/builtin/range-diff.c b/builtin/range-diff.c
--- a/builtin/range-diff.c
-: --------- > 21: d8498fb32 range-diff: use dim/bold cues to improve dual color mode
Signature · 1 line
gitgitgadget
PATCH v4 1/21 linear-assignment: a function to solve least-cost assignment problems
From: Johannes Schindelin <[email protected]>
The problem solved by the code introduced in this commit goes like this:
given two sets of items, and a cost matrix which says how much it "costs" to assign any given item of the first set to any given item of the second, assign all items (except when the sets have different size) in the cheapest way.
We use the Jonker-Volgenant algorithm to solve the assignment problem to answer questions such as: given two different versions of a topic branch (or iterations of a patch series), what is the best pairing of commits/patches between the different versions?
--- Makefile | 1 + linear-assignment.c | 201 ++++++++++++++++++++++++++++++++++++++++++++ linear-assignment.h | 22 +++++ 3 files changed, 224 insertions(+) create mode 100644 linear-assignment.c create mode 100644 linear-assignment.h
Makefile +1 -0
@@ -868,6 +868,7 @@ LIB_OBJS += gpg-interface.o LIB_OBJS += graph.o LIB_OBJS += grep.o LIB_OBJS += hashmap.o +LIB_OBJS += linear-assignment.o LIB_OBJS += help.o LIB_OBJS += hex.o LIB_OBJS += ident.o
linear-assignment.c new file +201 -0
201 lines not on this page · raw message
linear-assignment.h new file +22 -0
@@ -0,0 +1,22 @@ +#ifndef HUNGARIAN_H +#define HUNGARIAN_H + +/* +* Compute an assignment of columns -> rows (and vice versa) such that every +* column is assigned to at most one row (and vice versa) minimizing the +* overall cost. +* +* The parameter `cost` is the cost matrix: the cost to assign column j to row +* i is `cost[j + column_count * i]. +* +* The arrays column2row and row2column will be populated with the respective +* assignments (-1 for unassigned, which can happen only if column_count != +* row_count). +*/ +void compute_assignment (int column_count,int row_count,int *cost, +int *column2row,int *row2column); + +/* The maximal cost in the cost matrix (to prevent integer overflows). */ +#define COST_MAX (1<<16) + +#endif
Signature · 1 line
gitgitgadget
Re: [PATCH v4 01/21] linear-assignment: a function to solve least-cost assignment problems
Quoted 30 lines · Johannes Schindelin via GitGitGadget
From: Johannes Schindelin <[email protected]>
The problem solved by the code introduced in this commit goes like this:
given two sets of items, and a cost matrix which says how much it "costs" to assign any given item of the first set to any given item of the second, assign all items (except when the sets have different size) in the cheapest way.We use the Jonker-Volgenant algorithm to solve the assignment problem to answer questions such as: given two different versions of a topic branch (or iterations of a patch series), what is the best pairing of commits/patches between the different versions?
Signed-off-by Johannes Schindelin <[email protected]>--- Makefile | 1 + linear-assignment.c | 201 ++++++++++++++++++++++++++++++++++++++++++++ linear-assignment.h | 22 +++++ 3 files changed, 224 insertions(+) create mode 100644 linear-assignment.c create mode 100644 linear-assignment.h[...]
linear-assignment.h new file +0
-0
linear-assignment.h line 2
Quoted 2 lines
linear-assignment.h new file +2
-0@@ -0,0 +1,22 @@ +#ifndef HUNGARIAN_H +#define HUNGARIAN_H
nit: maybe s/HUNGARIAN/LINEAR_ASSIGNMENT/ in the two lines above.
Quoted 23 lines
linear-assignment.h new file +20
-0+ +/* +* Compute an assignment of columns -> rows (and vice versa) such that every +* column is assigned to at most one row (and vice versa) minimizing the +* overall cost. +* +* The parameter `cost` is the cost matrix: the cost to assign column j to row +* i is `cost[j + column_count * i]. +* +* The arrays column2row and row2column will be populated with the respective +* assignments (-1 for unassigned, which can happen only if column_count != +* row_count). +*/ +void compute_assignment (int column_count,int row_count,int *cost, +int *column2row,int *row2column); + +/* The maximal cost in the cost matrix (to prevent integer overflows). */ +#define COST_MAX (1<<16) + +#endif Signature · 1 line
gitgitgadget
Re: [PATCH v4 01/21] linear-assignment: a function to solve least-cost assignment problems
Hi Thomas,
Quoted 25 lines · Thomas Gummerer
Quoted 24 lines · Johannes Schindelin via GitGitGadget
From: Johannes Schindelin <[email protected]>
The problem solved by the code introduced in this commit goes like this:
given two sets of items, and a cost matrix which says how much it "costs" to assign any given item of the first set to any given item of the second, assign all items (except when the sets have different size) in the cheapest way.We use the Jonker-Volgenant algorithm to solve the assignment problem to answer questions such as: given two different versions of a topic branch (or iterations of a patch series), what is the best pairing of commits/patches between the different versions?
Signed-off-by Johannes Schindelin <[email protected]>--- Makefile | 1 + linear-assignment.c | 201 ++++++++++++++++++++++++++++++++++++++++++++ linear-assignment.h | 22 +++++ 3 files changed, 224 insertions(+) create mode 100644 linear-assignment.c create mode 100644 linear-assignment.h[...]
Quoted 10 lines
linear-assignment.h line 2
Quoted 8 lines
linear-assignment.h new file +2
-0@@ -0,0 +1,22 @@ +#ifndef HUNGARIAN_H +#define HUNGARIAN_H nit: maybe s/HUNGARIAN/LINEAR_ASSIGNMENT/ in the two lines above.
Makes sense.
Ciao,
Dscho
Quoted 25 lines
Quoted 24 lines
linear-assignment.h new file +20
-0+ +/* +* Compute an assignment of columns -> rows (and vice versa) such that every +* column is assigned to at most one row (and vice versa) minimizing the +* overall cost. +* +* The parameter `cost` is the cost matrix: the cost to assign column j to row +* i is `cost[j + column_count * i]. +* +* The arrays column2row and row2column will be populated with the respective +* assignments (-1 for unassigned, which can happen only if column_count != +* row_count). +*/ +void compute_assignment (int column_count,int row_count,int *cost, +int *column2row,int *row2column); + +/* The maximal cost in the cost matrix (to prevent integer overflows). */ +#define COST_MAX (1<<16) + +#endif Signature · 1 line
gitgitgadget
PATCH v4 2/21 Introduce `range-diff` to compare iterations of a topic branch
In reply to Johannes Schindelin via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
This command does not do a whole lot so far, apart from showing a usage that is oddly similar to that of git tbdiff. And for a good reason:
the next commits will turn range-branch into a full-blown replacement for tbdiff.
At this point, we ignore tbdiff's color options, as they will all be implemented later using diff_options.
Since f318d739159 (generate-cmds.sh: export all commands to command-list.h, 2018-05-10), every new command *requires* a man page to build right away, so let's also add a blank man page, too.
--- .gitignore | 1 + Documentation/git-range-diff.txt | 10 ++++++++++ Makefile | 1 + builtin.h | 1 + builtin/range-diff.c | 25 +++++++++++++++++++++++++ command-list.txt | 1 + git.c | 1 + 7 files changed, 40 insertions(+) create mode 100644 Documentation/git-range-diff.txt create mode 100644 builtin/range-diff.c
.gitignore +1 -0
@@ -113,6 +113,7 @@ /git-pull /git-push /git-quiltimport +/git-range-diff /git-read-tree /git-rebase /git-rebase--am
Documentation/git-range-diff.txt new file +10 -0
@@ -0,0 +1,10 @@ +git-range-diff(1) +================== + +NAME +---- +git-range-diff - Compare two commit ranges (e.g. two versions of a branch) + +GIT +--- +Part of the linkgit:git[1] suite
Makefile +1 -0
@@ -1059,6 +1059,7 @@ BUILTIN_OBJS += builtin/prune-packed.o BUILTIN_OBJS += builtin/prune.o BUILTIN_OBJS += builtin/pull.o BUILTIN_OBJS += builtin/push.o +BUILTIN_OBJS += builtin/range-diff.o BUILTIN_OBJS += builtin/read-tree.o BUILTIN_OBJS += builtin/rebase--helper.o BUILTIN_OBJS += builtin/receive-pack.o
builtin.h +1 -0
@@ -201,6 +201,7 @@ extern int cmd_prune(int argc, const char **argv, const char *prefix);extern int cmd_prune_packed (int argc,const char **argv,const char *prefix);extern int cmd_pull (int argc,const char **argv,const char *prefix);extern int cmd_push (int argc,const char **argv,const char *prefix); +extern int cmd_range_diff (int argc,const char **argv,const char *prefix);extern int cmd_read_tree (int argc,const char **argv,const char *prefix);extern int cmd_rebase__helper (int argc,const char **argv,const char *prefix);extern int cmd_receive_pack (int argc,const char **argv,const char *prefix);
builtin/range-diff.c new file +25 -0
@@ -0,0 +1,25 @@ +#include "cache.h" +#include "builtin.h" +#include "parse-options.h" + +static const char *const builtin_range_diff_usage[] = { +N_ ("git range-diff [<options>] <old-base>..<old-tip> <new-base>..<new-tip>" ), +N_ ("git range-diff [<options>] <old-tip>...<new-tip>" ), +N_ ("git range-diff [<options>] <base> <old-tip> <new-tip>" ), +NULL +}; + +int cmd_range_diff (int argc,const char **argv,const char *prefix) +{ +int creation_factor =60 ; +struct option options[] = { +OPT_INTEGER (0 ,"creation-factor" , &creation_factor, +N_ ("Percentage by which creation is weighted" )), +OPT_END () + }; + + argc =parse_options (argc, argv,NULL , options, + builtin_range_diff_usage,0 ); + +return 0 ; +}
command-list.txt +1 -0
@@ -139,6 +139,7 @@ git-prune-packed plumbingmanipulators git-pull mainporcelain remote git-push mainporcelain remote git-quiltimport foreignscminterface +git-range-diff mainporcelain git-read-tree plumbingmanipulators git-rebase mainporcelain history git-receive-pack synchelpers
git.c +1 -0
@@ -517,6 +517,7 @@ static struct cmd_struct commands[] = { { "prune-packed", cmd_prune_packed, RUN_SETUP }, { "pull", cmd_pull, RUN_SETUP | NEED_WORK_TREE }, { "push", cmd_push, RUN_SETUP }, + { "range-diff", cmd_range_diff, RUN_SETUP | USE_PAGER }, { "read-tree", cmd_read_tree, RUN_SETUP | SUPPORT_SUPER_PREFIX}, { "rebase--helper", cmd_rebase__helper, RUN_SETUP | NEED_WORK_TREE }, { "receive-pack", cmd_receive_pack },
Signature · 1 line
gitgitgadget
PATCH v4 3/21 range-diff: first rudimentary implementation
In reply to Johannes Schindelin via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
At this stage, git range-diff can determine corresponding commits of two related commit ranges. This makes use of the recently introduced implementation of the linear assignment algorithm.
The core of this patch is a straight port of the ideas of tbdiff, the apparently dormant project at https://github.com/trast/tbdiff.
The output does not at all match tbdiff's output yet, as this patch really concentrates on getting the patch matching part right.
Note: due to differences in the diff algorithm (tbdiff uses the Python module difflib, Git uses its xdiff fork), the cost matrix calculated by range-diff is different (but very similar) to the one calculated by tbdiff. Therefore, it is possible that they find different matching commits in corner cases (e.g. when a patch was split into two patches of roughly equal length).
--- Makefile | 1 + builtin/range-diff.c | 43 +++++- range-diff.c | 311 +++++++++++++++++++++++++++++++++++++++++++ range-diff.h | 7 + 4 files changed, 361 insertions(+), 1 deletion(-) create mode 100644 range-diff.c create mode 100644 range-diff.h
Makefile +1 -0
@@ -921,6 +921,7 @@ LIB_OBJS += progress.o LIB_OBJS += prompt.o LIB_OBJS += protocol.o LIB_OBJS += quote.o +LIB_OBJS += range-diff.o LIB_OBJS += reachable.o LIB_OBJS += read-cache.o LIB_OBJS += reflog-walk.o
builtin/range-diff.c +42 -1
57 lines not on this page · raw message
range-diff.c new file +311 -0
311 lines not on this page · raw message
range-diff.h new file +7 -0
@@ -0,0 +1,7 @@ +#ifndef BRANCH_DIFF_H +#define BRANCH_DIFF_H + +int show_range_diff (const char *range1,const char *range2, +int creation_factor); + +#endif
Signature · 1 line
gitgitgadget
Re: [PATCH v4 03/21] range-diff: first rudimentary implementation
Quoted 65 lines · Johannes Schindelin via GitGitGadget
From: Johannes Schindelin <[email protected]>
At this stage,
git range-diffcan determine corresponding commits of two related commit ranges. This makes use of the recently introduced implementation of the linear assignment algorithm.The core of this patch is a straight port of the ideas of tbdiff, the apparently dormant project at https://github.com/trast/tbdiff.
The output does not at all match
tbdiff's output yet, as this patch really concentrates on getting the patch matching part right.Note: due to differences in the diff algorithm (
tbdiffuses the Python moduledifflib, Git uses its xdiff fork), the cost matrix calculated byrange-diffis different (but very similar) to the one calculated bytbdiff. Therefore, it is possible that they find different matching commits in corner cases (e.g. when a patch was split into two patches of roughly equal length).Signed-off-by Johannes Schindelin <[email protected]>--- Makefile | 1 + builtin/range-diff.c | 43 +++++- range-diff.c | 311 +++++++++++++++++++++++++++++++++++++++++++ range-diff.h | 7 + 4 files changed, 361 insertions(+), 1 deletion(-) create mode 100644 range-diff.c create mode 100644 range-diff.h[...]
range-diff.c new file +28
-0@@ -0,0 +1,311 @@ +#include "cache.h" +#include "range-diff.h" +#include "string-list.h" +#include "run-command.h" +#include "argv-array.h" +#include "hashmap.h" +#include "xdiff-interface.h" +#include "linear-assignment.h" + +struct patch_util { +/* For the search for an exact match */ +struct hashmap_entry e; +const char *diff, *patch; + +int i; +int diffsize; +size_t diff_offset; +/* the index of the matching item in the other branch, or -1 */ +int matching; +struct object_id oid; +}; + +/* +* Reads the patches into a string list, with the `util` field being populated +* as struct object_id (will need to be free()d). +*/ +static int read_patches (const char *range,struct string_list *list) +{
range-diff.c line 38
Quoted 10 lines
range-diff.c new file +10
-0+struct child_process cp =CHILD_PROCESS_INIT ; +FILE *in; +struct strbuf buf =STRBUF_INIT , line =STRBUF_INIT ; +struct patch_util *util =NULL ; +int in_header =1 ; + +argv_array_pushl (&cp.args,"log" ,"--no-color" ,"-p" ,"--no-merges" , +"--reverse" ,"--date-order" ,"--decorate=no" , +"--no-abbrev-commit" , range, +NULL );
Compared to tbdiff, add "--decorate=no", and "--no-abbrev-commit". I see we're abbreviating the commit hashes later. We don't want ref names here, so "--decorate=no" makes sense as well.
Quoted 63 lines
range-diff.c new file +63
-0+ cp.out =-1 ; + cp.no_stdin =1 ; + cp.git_cmd =1 ; + +if (start_command (&cp)) +return error_errno (_ ("could not start `log`" )); + in =fdopen (cp.out,"r" ); +if (!in) { +error_errno (_ ("could not read `log` output" )); +finish_command (&cp); +return -1 ; + } + +while (strbuf_getline (&line, in) !=EOF ) { +const char *p; + +if (skip_prefix (line.buf,"commit " , &p)) { +if (util) { +string_list_append (list, buf.buf)->util = util; +strbuf_reset (&buf); + } + util =xcalloc (sizeof (*util),1 ); +if (get_oid (p, &util->oid)) { +error (_ ("could not parse commit '%s'" ), p); +free (util); +string_list_clear (list,1 ); +strbuf_release (&buf); +strbuf_release (&line); +fclose (in); +finish_command (&cp); +return -1 ; + } + util->matching =-1 ; + in_header =1 ; +continue ; + } + +if (starts_with (line.buf,"diff --git" )) { + in_header =0 ; +strbuf_addch (&buf,'\n' ); +if (!util->diff_offset) + util->diff_offset = buf.len; +strbuf_addbuf (&buf, &line); + }else if (in_header) { +if (starts_with (line.buf,"Author: " )) { +strbuf_addbuf (&buf, &line); +strbuf_addstr (&buf,"\n\n" ); + }else if (starts_with (line.buf," " )) { +strbuf_addbuf (&buf, &line); +strbuf_addch (&buf,'\n' ); + } +continue ; + }else if (starts_with (line.buf,"@@ " )) +strbuf_addstr (&buf,"@@" ); +else if (!line.buf[0 ] ||starts_with (line.buf,"index " )) +/* +* A completely blank (not ' \n', which is context) +* line is not valid in a diff. We skip it +* silently, because this neatly handles the blank +* separator line between commits in git-log +* output. +* +* We also want to ignore the diff's `index` lines
range-diff.c line 111
Quoted 10 lines
range-diff.c new file +10
-0+* because they contain exact blob hashes in which +* we are not interested. +*/ +continue ; + else + strbuf_addbuf(&buf, &line); + +strbuf_addch (&buf,'\n' ); + util->diffsize++; + }
This seems to differ from tbdiff in the number of newlines we're adding in various places, however I think as long as it's consistent in itself, and with the way we're printing the output the differences shouldn't matter.
Quoted 44 lines
range-diff.c new file +44
-0+fclose (in); +strbuf_release (&line); + +if (util) +string_list_append (list, buf.buf)->util = util; +strbuf_release (&buf); + +if (finish_command (&cp)) +return -1 ; + +return 0 ; +} + +static int patch_util_cmp (const void *dummy,const struct patch_util *a, +const struct patch_util *b,const char *keydata) +{ +return strcmp (a->diff, keydata ? keydata : b->diff); +} + +static void find_exact_matches (struct string_list *a,struct string_list *b) +{ +struct hashmap map; +int i; + +hashmap_init (&map, (hashmap_cmp_fn )patch_util_cmp,NULL ,0 ); + +/* First, add the patches of a to a hash map */ +for (i =0 ; i < a->nr; i++) { +struct patch_util *util = a->items[i].util; + + util->i = i; + util->patch = a->items[i].string; + util->diff = util->patch + util->diff_offset; +hashmap_entry_init (util,strhash (util->diff)); +hashmap_add (&map, util); + } + +/* Now try to find exact matches in b */ +for (i =0 ; i < b->nr; i++) { +struct patch_util *util = b->items[i].util, *other; + + util->i = i; + util->patch = b->items[i].string; + util->diff = util->patch + util->diff_offset;
range-diff.c line 165
Quoted 10 lines
range-diff.c new file +10
-0+hashmap_entry_init (util,strhash (util->diff)); + other =hashmap_remove (&map, util,NULL ); +if (other) { +if (other->matching >=0 ) +BUG ("already assigned!" ); + + other->matching = i; + util->matching = other->i; + } + }
One possibly interesting corner case here is what happens when there are two patches that have the exact same diff, for example in the pathological case of commit A doing something, commit B reverting commit A, and then commit C reverting commit B, so it ends up with the same diff.
Having those same commits unchanged in both ranges (e.g. if a commit earlier in the range has been changed, and range B has been rebased on top of that), we'd get the following mapping from range A to range B for the commits in question:
A -> C
B -> B
C -> A
Which is not quite what I would expect as the user (even though it is a valid mapping, and it probably doesn't matter too much for the end result of the range diff, as nothing has changed between the commits anyway). So I'm not sure it's worth fixing this, as it is a pathological case, and nothing really breaks.
Quoted 73 lines
range-diff.c new file +73
-0+ +hashmap_free (&map,0 ); +} + +static void diffsize_consume (void *data,char *line,unsigned long len) +{ + (*(int *)data)++; +} + +static int diffsize (const char *a,const char *b) +{ +xpparam_t pp = {0 }; +xdemitconf_t cfg = {0 }; +mmfile_t mf1, mf2; +int count =0 ; + + mf1.ptr = (char *)a; + mf1.size =strlen (a); + mf2.ptr = (char *)b; + mf2.size =strlen (b); + + cfg.ctxlen =3 ; +if (!xdi_diff_outf (&mf1, &mf2, diffsize_consume, &count, &pp, &cfg)) +return count; + +error (_ ("failed to generate diff" )); +return COST_MAX ; +} + +static void get_correspondences (struct string_list *a,struct string_list *b, +int creation_factor) +{ +int n = a->nr + b->nr; +int *cost, c, *a2b, *b2a; +int i, j; + +ALLOC_ARRAY (cost,st_mult (n, n)); +ALLOC_ARRAY (a2b, n); +ALLOC_ARRAY (b2a, n); + +for (i =0 ; i < a->nr; i++) { +struct patch_util *a_util = a->items[i].util; + +for (j =0 ; j < b->nr; j++) { +struct patch_util *b_util = b->items[j].util; + +if (a_util->matching == j) + c =0 ; +else if (a_util->matching <0 && b_util->matching <0 ) + c =diffsize (a_util->diff, b_util->diff); +else + c =COST_MAX ; + cost[i + n * j] = c; + } + + c = a_util->matching <0 ? + a_util->diffsize * creation_factor /100 :COST_MAX ; +for (j = b->nr; j < n; j++) + cost[i + n * j] = c; + } + +for (j =0 ; j < b->nr; j++) { +struct patch_util *util = b->items[j].util; + + c = util->matching <0 ? + util->diffsize * creation_factor /100 :COST_MAX ; +for (i = a->nr; i < n; i++) + cost[i + n * j] = c; + } + +for (i = a->nr; i < n; i++) +for (j = b->nr; j < n; j++) + cost[i + n * j] =0 ;
range-diff.c line 248
Quoted 10 lines
range-diff.c new file +10
-0+ +compute_assignment (n, n, cost, a2b, b2a); + +for (i =0 ; i < a->nr; i++) +if (a2b[i] >=0 && a2b[i] < b->nr) { +struct patch_util *a_util = a->items[i].util; +struct patch_util *b_util = b->items[a2b[i]].util; + + a_util->matching = a2b[i]; + b_util->matching = i;
So here we re-assign 'matching' in the struct regardless of whether it was assigned before while searching for exact matches or not.
Shouldn't diffsize for matching patches also be 0? So are we doing the 'find_exact_matches()' bit only as an optimization, or am I missing some other reason why that is beneficial?
Quoted 44 lines
range-diff.c new file +44
-0+ } + +free (cost); +free (a2b); +free (b2a); +} + +static const char *short_oid (struct patch_util *util) +{ +return find_unique_abbrev (&util->oid,DEFAULT_ABBREV ); +} + +static void output (struct string_list *a,struct string_list *b) +{ +int i; + +for (i =0 ; i < b->nr; i++) { +struct patch_util *util = b->items[i].util, *prev; + +if (util->matching <0 ) +printf ("-: -------- > %d: %s\n" , + i +1 ,short_oid (util)); +else { + prev = a->items[util->matching].util; +printf ("%d: %s ! %d: %s\n" , + util->matching +1 ,short_oid (prev), + i +1 ,short_oid (util)); + } + } + +for (i =0 ; i < a->nr; i++) { +struct patch_util *util = a->items[i].util; + +if (util->matching <0 ) +printf ("%d: %s < -: --------\n" , + i +1 ,short_oid (util)); + } +} + +int show_range_diff (const char *range1,const char *range2, +int creation_factor) +{ +int res =0 ; +
range-diff.c line 302
Quoted 10 lines
range-diff.c new file +10
-0+struct string_list branch1 =STRING_LIST_INIT_DUP ; +struct string_list branch2 =STRING_LIST_INIT_DUP ; + +if (read_patches (range1, &branch1)) + res =error (_ ("could not parse log for '%s'" ), range1); +if (!res &&read_patches (range2, &branch2)) + res =error (_ ("could not parse log for '%s'" ), range2); + +if (!res) { +find_exact_matches (&branch1, &branch2);
Note to self: here we assign the matching member of struct patch_util for each patch in both ranges to a patch number in the other range if it is an exact match.
We also assign the patch and diff members, and number the patches using the 'i' member of struct patch_util. Let's see if that numbering is still useful later.
range-diff.c line 303
Quoted 1 line
range-diff.c new file +1
-0+get_correspondences (&branch1, &branch2, creation_factor);
And here we use the linear assignment algorithm to match the rest of the commits.
range-diff.c line 304
Quoted 1 line
range-diff.c new file +1
-0+output (&branch1, &branch2);
And finally we print the output. We don't seem to use the util->i that's assigned for range b (or range 2) anywhere at the moment, which I was wondering about earlier, so I assume it's there mainly for symmetry, but it doesn't really hurt other than me wondering what it was for.
Quoted 13 lines
range-diff.c new file +7
-0+ } + +string_list_clear (&branch1,1 ); +string_list_clear (&branch2,1 ); + +return res; +}range-diff.h new file +0
-0
range-diff.h line 2
Quoted 2 lines
range-diff.h new file +2
-0@@ -0,0 +1,7 @@ +#ifndef BRANCH_DIFF_H +#define BRANCH_DIFF_H
s/BRANCH/RANGE/ above?
Quoted 7 lines
range-diff.h new file +4
-0+int show_range_diff (const char *range1,const char *range2, +int creation_factor); + +#endif Signature · 1 line
gitgitgadget
Re: [PATCH v4 03/21] range-diff: first rudimentary implementation
Hi Thomas,
Quoted 72 lines · Thomas Gummerer
Quoted 71 lines · Johannes Schindelin via GitGitGadget
From: Johannes Schindelin <[email protected]>
At this stage,
git range-diffcan determine corresponding commits of two related commit ranges. This makes use of the recently introduced implementation of the linear assignment algorithm.The core of this patch is a straight port of the ideas of tbdiff, the apparently dormant project at https://github.com/trast/tbdiff.
The output does not at all match
tbdiff's output yet, as this patch really concentrates on getting the patch matching part right.Note: due to differences in the diff algorithm (
tbdiffuses the Python moduledifflib, Git uses its xdiff fork), the cost matrix calculated byrange-diffis different (but very similar) to the one calculated bytbdiff. Therefore, it is possible that they find different matching commits in corner cases (e.g. when a patch was split into two patches of roughly equal length).Signed-off-by Johannes Schindelin <[email protected]>--- Makefile | 1 + builtin/range-diff.c | 43 +++++- range-diff.c | 311 +++++++++++++++++++++++++++++++++++++++++++ range-diff.h | 7 + 4 files changed, 361 insertions(+), 1 deletion(-) create mode 100644 range-diff.c create mode 100644 range-diff.h[...]
range-diff.c new file +34
-0@@ -0,0 +1,311 @@ +#include "cache.h" +#include "range-diff.h" +#include "string-list.h" +#include "run-command.h" +#include "argv-array.h" +#include "hashmap.h" +#include "xdiff-interface.h" +#include "linear-assignment.h" + +struct patch_util { +/* For the search for an exact match */ +struct hashmap_entry e; +const char *diff, *patch; + +int i; +int diffsize; +size_t diff_offset; +/* the index of the matching item in the other branch, or -1 */ +int matching; +struct object_id oid; +}; + +/* +* Reads the patches into a string list, with the `util` field being populated +* as struct object_id (will need to be free()d). +*/ +static int read_patches (const char *range,struct string_list *list) +{ +struct child_process cp =CHILD_PROCESS_INIT ; +FILE *in; +struct strbuf buf =STRBUF_INIT , line =STRBUF_INIT ; +struct patch_util *util =NULL ; +int in_header =1 ; +
Quoted 8 lines
range-diff.c line 38
Quoted 4 lines
range-diff.c new file +4
-0+argv_array_pushl (&cp.args,"log" ,"--no-color" ,"-p" ,"--no-merges" , +"--reverse" ,"--date-order" ,"--decorate=no" , +"--no-abbrev-commit" , range, +NULL );Compared to tbdiff, add "--decorate=no", and "--no-abbrev-commit". I see we're abbreviating the commit hashes later. We don't want ref names here, so "--decorate=no" makes sense as well.
Indeed. Compare also to https://github.com/trast/tbdiff/pull/8
Quoted 70 lines
Quoted 70 lines
range-diff.c new file +70
-0+ cp.out =-1 ; + cp.no_stdin =1 ; + cp.git_cmd =1 ; + +if (start_command (&cp)) +return error_errno (_ ("could not start `log`" )); + in =fdopen (cp.out,"r" ); +if (!in) { +error_errno (_ ("could not read `log` output" )); +finish_command (&cp); +return -1 ; + } + +while (strbuf_getline (&line, in) !=EOF ) { +const char *p; + +if (skip_prefix (line.buf,"commit " , &p)) { +if (util) { +string_list_append (list, buf.buf)->util = util; +strbuf_reset (&buf); + } + util =xcalloc (sizeof (*util),1 ); +if (get_oid (p, &util->oid)) { +error (_ ("could not parse commit '%s'" ), p); +free (util); +string_list_clear (list,1 ); +strbuf_release (&buf); +strbuf_release (&line); +fclose (in); +finish_command (&cp); +return -1 ; + } + util->matching =-1 ; + in_header =1 ; +continue ; + } + +if (starts_with (line.buf,"diff --git" )) { + in_header =0 ; +strbuf_addch (&buf,'\n' ); +if (!util->diff_offset) + util->diff_offset = buf.len; +strbuf_addbuf (&buf, &line); + }else if (in_header) { +if (starts_with (line.buf,"Author: " )) { +strbuf_addbuf (&buf, &line); +strbuf_addstr (&buf,"\n\n" ); + }else if (starts_with (line.buf," " )) { +strbuf_addbuf (&buf, &line); +strbuf_addch (&buf,'\n' ); + } +continue ; + }else if (starts_with (line.buf,"@@ " )) +strbuf_addstr (&buf,"@@" ); +else if (!line.buf[0 ] ||starts_with (line.buf,"index " )) +/* +* A completely blank (not ' \n', which is context) +* line is not valid in a diff. We skip it +* silently, because this neatly handles the blank +* separator line between commits in git-log +* output. +* +* We also want to ignore the diff's `index` lines +* because they contain exact blob hashes in which +* we are not interested. +*/ +continue ; +else +strbuf_addbuf (&buf, &line); +
Quoted 8 lines
range-diff.c line 111
Quoted 3 lines
range-diff.c new file +3
-0+strbuf_addch (&buf,'\n' ); + util->diffsize++; + }This seems to differ from tbdiff in the number of newlines we're adding in various places, however I think as long as it's consistent in itself, and with the way we're printing the output the differences shouldn't matter.
Right.
Quoted 70 lines
Quoted 44 lines
range-diff.c new file +44
-0+fclose (in); +strbuf_release (&line); + +if (util) +string_list_append (list, buf.buf)->util = util; +strbuf_release (&buf); + +if (finish_command (&cp)) +return -1 ; + +return 0 ; +} + +static int patch_util_cmp (const void *dummy,const struct patch_util *a, +const struct patch_util *b,const char *keydata) +{ +return strcmp (a->diff, keydata ? keydata : b->diff); +} + +static void find_exact_matches (struct string_list *a,struct string_list *b) +{ +struct hashmap map; +int i; + +hashmap_init (&map, (hashmap_cmp_fn )patch_util_cmp,NULL ,0 ); + +/* First, add the patches of a to a hash map */ +for (i =0 ; i < a->nr; i++) { +struct patch_util *util = a->items[i].util; + + util->i = i; + util->patch = a->items[i].string; + util->diff = util->patch + util->diff_offset; +hashmap_entry_init (util,strhash (util->diff)); +hashmap_add (&map, util); + } + +/* Now try to find exact matches in b */ +for (i =0 ; i < b->nr; i++) { +struct patch_util *util = b->items[i].util, *other; + + util->i = i; + util->patch = b->items[i].string; + util->diff = util->patch + util->diff_offset;range-diff.c line 165
Quoted 10 lines
range-diff.c new file +10
-0+hashmap_entry_init (util,strhash (util->diff)); + other =hashmap_remove (&map, util,NULL ); +if (other) { +if (other->matching >=0 ) +BUG ("already assigned!" ); + + other->matching = i; + util->matching = other->i; + } + }One possibly interesting corner case here is what happens when there are two patches that have the exact same diff, for example in the pathological case of commit A doing something, commit B reverting commit A, and then commit C reverting commit B, so it ends up with the same diff.
Having those same commits unchanged in both ranges (e.g. if a commit earlier in the range has been changed, and range B has been rebased on top of that), we'd get the following mapping from range A to range B for the commits in question:
A -> C
B -> B
C -> A
Quoted 5 lines
Which is not quite what I would expect as the user (even though it is a valid mapping, and it probably doesn't matter too much for the end result of the range diff, as nothing has changed between the commits anyway). So I'm not sure it's worth fixing this, as it is a pathological case, and nothing really breaks.
Indeed. As far as I am concerned, this falls squarely into the "let's cross that bridge when, or if, we reach it" category.
Quoted 78 lines
Quoted 78 lines
range-diff.c new file +78
-0+ +hashmap_free (&map,0 ); +} + +static void diffsize_consume (void *data,char *line,unsigned long len) +{ + (*(int *)data)++; +} + +static int diffsize (const char *a,const char *b) +{ +xpparam_t pp = {0 }; +xdemitconf_t cfg = {0 }; +mmfile_t mf1, mf2; +int count =0 ; + + mf1.ptr = (char *)a; + mf1.size =strlen (a); + mf2.ptr = (char *)b; + mf2.size =strlen (b); + + cfg.ctxlen =3 ; +if (!xdi_diff_outf (&mf1, &mf2, diffsize_consume, &count, &pp, &cfg)) +return count; + +error (_ ("failed to generate diff" )); +return COST_MAX ; +} + +static void get_correspondences (struct string_list *a,struct string_list *b, +int creation_factor) +{ +int n = a->nr + b->nr; +int *cost, c, *a2b, *b2a; +int i, j; + +ALLOC_ARRAY (cost,st_mult (n, n)); +ALLOC_ARRAY (a2b, n); +ALLOC_ARRAY (b2a, n); + +for (i =0 ; i < a->nr; i++) { +struct patch_util *a_util = a->items[i].util; + +for (j =0 ; j < b->nr; j++) { +struct patch_util *b_util = b->items[j].util; + +if (a_util->matching == j) + c =0 ; +else if (a_util->matching <0 && b_util->matching <0 ) + c =diffsize (a_util->diff, b_util->diff); +else + c =COST_MAX ; + cost[i + n * j] = c; + } + + c = a_util->matching <0 ? + a_util->diffsize * creation_factor /100 :COST_MAX ; +for (j = b->nr; j < n; j++) + cost[i + n * j] = c; + } + +for (j =0 ; j < b->nr; j++) { +struct patch_util *util = b->items[j].util; + + c = util->matching <0 ? + util->diffsize * creation_factor /100 :COST_MAX ; +for (i = a->nr; i < n; i++) + cost[i + n * j] = c; + } + +for (i = a->nr; i < n; i++) +for (j = b->nr; j < n; j++) + cost[i + n * j] =0 ; + +compute_assignment (n, n, cost, a2b, b2a); + +for (i =0 ; i < a->nr; i++) +if (a2b[i] >=0 && a2b[i] < b->nr) {
Quoted 8 lines
range-diff.c line 248
Quoted 5 lines
range-diff.c new file +5
-0+struct patch_util *a_util = a->items[i].util; +struct patch_util *b_util = b->items[a2b[i]].util; + + a_util->matching = a2b[i]; + b_util->matching = i;So here we re-assign 'matching' in the struct regardless of whether it was assigned before while searching for exact matches or not.
If matching were assigned here, it would indicate a bug in the code, I think. Before this loop, all of the matching fields should be NULL, and the compute_assignment() function is expected to populate a2b and b2a appropriately, i.e. no "double booking".
Quoted 3 lines
Shouldn't diffsize for matching patches also be 0? So are we doing the '
find_exact_matches()' bit only as an optimization, or am I missing some other reason why that is beneficial?
An optimization.
Remember, the linear assignment algorithm runs in O(n^3). That's not a laughing matter by any stretch of imagination. The more stuff we can get out of the way, as quickly as possible, the less it hurts to have a cubic runtime complexity.
Quoted 68 lines
Quoted 44 lines
range-diff.c new file +44
-0+ } + +free (cost); +free (a2b); +free (b2a); +} + +static const char *short_oid (struct patch_util *util) +{ +return find_unique_abbrev (&util->oid,DEFAULT_ABBREV ); +} + +static void output (struct string_list *a,struct string_list *b) +{ +int i; + +for (i =0 ; i < b->nr; i++) { +struct patch_util *util = b->items[i].util, *prev; + +if (util->matching <0 ) +printf ("-: -------- > %d: %s\n" , + i +1 ,short_oid (util)); +else { + prev = a->items[util->matching].util; +printf ("%d: %s ! %d: %s\n" , + util->matching +1 ,short_oid (prev), + i +1 ,short_oid (util)); + } + } + +for (i =0 ; i < a->nr; i++) { +struct patch_util *util = a->items[i].util; + +if (util->matching <0 ) +printf ("%d: %s < -: --------\n" , + i +1 ,short_oid (util)); + } +} + +int show_range_diff (const char *range1,const char *range2, +int creation_factor) +{ +int res =0 ; +range-diff.c line 302
Quoted 10 lines
range-diff.c new file +10
-0+struct string_list branch1 =STRING_LIST_INIT_DUP ; +struct string_list branch2 =STRING_LIST_INIT_DUP ; + +if (read_patches (range1, &branch1)) + res =error (_ ("could not parse log for '%s'" ), range1); +if (!res &&read_patches (range2, &branch2)) + res =error (_ ("could not parse log for '%s'" ), range2); + +if (!res) { +find_exact_matches (&branch1, &branch2);Note to self: here we assign the matching member of struct
patch_utilfor each patch in both ranges to a patch number in the other range if it is an exact match.We also assign the patch and diff members, and number the patches using the 'i' member of struct
patch_util. Let's see if that numbering is still useful later.range-diff.c line 303
Quoted 1 line
range-diff.c new file +1
-0+get_correspondences (&branch1, &branch2, creation_factor);And here we use the linear assignment algorithm to match the rest of the commits.
Quoted 7 lines
range-diff.c line 304
Quoted 1 line
range-diff.c new file +1
-0+output (&branch1, &branch2);And finally we print the output. We don't seem to use the
util->ithat's assigned for range b (or range 2) anywhere at the moment, which I was wondering about earlier, so I assume it's there mainly for symmetry, but it doesn't really hurt other than me wondering what it was for.
It's there because it would require extra care to do it only for one side.
Quoted 9 lines
Quoted 9 lines
range-diff.c new file +7
-0+ } + +string_list_clear (&branch1,1 ); +string_list_clear (&branch2,1 ); + +return res; +}range-diff.h new file +0
-0
Quoted 8 lines
range-diff.h line 2
Quoted 6 lines
index 000000000..dd30449c4
range-diff.h new file +2
-0@@ -0,0 +1,7 @@ +#ifndef BRANCH_DIFF_H +#define BRANCH_DIFF_H s/BRANCH/RANGE/ above?
Good eyes.
Thank you for your review. It is nice to see that you can follow the code and come to the same conclusions as I. Can I always have you as reviewer?
Ciao,
Dscho
Quoted 8 lines
Quoted 7 lines
range-diff.h new file +4
-0+int show_range_diff (const char *range1,const char *range2, +int creation_factor); + +#endif Signature · 1 line
gitgitgadget
Re: [PATCH v4 03/21] range-diff: first rudimentary implementation
Quoted 59 lines · Johannes Schindelin
Hi Thomas,
Quoted 55 lines · Thomas Gummerer
Quoted 28 lines · Johannes Schindelin via GitGitGadget
[...]
range-diff.c new file +25
-0+static void find_exact_matches (struct string_list *a,struct string_list *b) +{ +struct hashmap map; +int i; + +hashmap_init (&map, (hashmap_cmp_fn )patch_util_cmp,NULL ,0 ); + +/* First, add the patches of a to a hash map */ +for (i =0 ; i < a->nr; i++) { +struct patch_util *util = a->items[i].util; + + util->i = i; + util->patch = a->items[i].string; + util->diff = util->patch + util->diff_offset; +hashmap_entry_init (util,strhash (util->diff)); +hashmap_add (&map, util); + } + +/* Now try to find exact matches in b */ +for (i =0 ; i < b->nr; i++) { +struct patch_util *util = b->items[i].util, *other; + + util->i = i; + util->patch = b->items[i].string; + util->diff = util->patch + util->diff_offset;range-diff.c line 165
Quoted 10 lines
range-diff.c new file +10
-0+hashmap_entry_init (util,strhash (util->diff)); + other =hashmap_remove (&map, util,NULL ); +if (other) { +if (other->matching >=0 ) +BUG ("already assigned!" ); + + other->matching = i; + util->matching = other->i; + } + }One possibly interesting corner case here is what happens when there are two patches that have the exact same diff, for example in the pathological case of commit A doing something, commit B reverting commit A, and then commit C reverting commit B, so it ends up with the same diff.
Having those same commits unchanged in both ranges (e.g. if a commit earlier in the range has been changed, and range B has been rebased on top of that), we'd get the following mapping from range A to range B for the commits in question:
A -> C
B -> B
C -> A
Quoted 8 lines
Quoted 5 lines
Which is not quite what I would expect as the user (even though it is a valid mapping, and it probably doesn't matter too much for the end result of the range diff, as nothing has changed between the commits anyway). So I'm not sure it's worth fixing this, as it is a pathological case, and nothing really breaks.
Indeed. As far as I am concerned, this falls squarely into the "let's cross that bridge when, or if, we reach it" category.
Makes sense, this can definitely be addressed later.
Quoted 87 lines
Quoted 78 lines
Quoted 78 lines
range-diff.c new file +78
-0+ +hashmap_free (&map,0 ); +} + +static void diffsize_consume (void *data,char *line,unsigned long len) +{ + (*(int *)data)++; +} + +static int diffsize (const char *a,const char *b) +{ +xpparam_t pp = {0 }; +xdemitconf_t cfg = {0 }; +mmfile_t mf1, mf2; +int count =0 ; + + mf1.ptr = (char *)a; + mf1.size =strlen (a); + mf2.ptr = (char *)b; + mf2.size =strlen (b); + + cfg.ctxlen =3 ; +if (!xdi_diff_outf (&mf1, &mf2, diffsize_consume, &count, &pp, &cfg)) +return count; + +error (_ ("failed to generate diff" )); +return COST_MAX ; +} + +static void get_correspondences (struct string_list *a,struct string_list *b, +int creation_factor) +{ +int n = a->nr + b->nr; +int *cost, c, *a2b, *b2a; +int i, j; + +ALLOC_ARRAY (cost,st_mult (n, n)); +ALLOC_ARRAY (a2b, n); +ALLOC_ARRAY (b2a, n); + +for (i =0 ; i < a->nr; i++) { +struct patch_util *a_util = a->items[i].util; + +for (j =0 ; j < b->nr; j++) { +struct patch_util *b_util = b->items[j].util; + +if (a_util->matching == j) + c =0 ; +else if (a_util->matching <0 && b_util->matching <0 ) + c =diffsize (a_util->diff, b_util->diff); +else + c =COST_MAX ; + cost[i + n * j] = c; + } + + c = a_util->matching <0 ? + a_util->diffsize * creation_factor /100 :COST_MAX ; +for (j = b->nr; j < n; j++) + cost[i + n * j] = c; + } + +for (j =0 ; j < b->nr; j++) { +struct patch_util *util = b->items[j].util; + + c = util->matching <0 ? + util->diffsize * creation_factor /100 :COST_MAX ; +for (i = a->nr; i < n; i++) + cost[i + n * j] = c; + } + +for (i = a->nr; i < n; i++) +for (j = b->nr; j < n; j++) + cost[i + n * j] =0 ; + +compute_assignment (n, n, cost, a2b, b2a); + +for (i =0 ; i < a->nr; i++) +if (a2b[i] >=0 && a2b[i] < b->nr) {Quoted 8 lines
range-diff.c line 248
Quoted 5 lines
range-diff.c new file +5
-0+struct patch_util *a_util = a->items[i].util; +struct patch_util *b_util = b->items[a2b[i]].util; + + a_util->matching = a2b[i]; + b_util->matching = i;So here we re-assign 'matching' in the struct regardless of whether it was assigned before while searching for exact matches or not.
Quoted 4 lines
If
matchingwere assigned here, it would indicate a bug in the code, I think. Before this loop, all of thematchingfields should beNULL, and thecompute_assignment()function is expected to populatea2bandb2aappropriately, i.e. no "double booking".
Hmm we are using the 'matching' fields in the loops above, e.g.:
if (a_util->matching == j) c = 0;
They're also ints, so they can't be NULL, right? So what I was thinking is that we could do
if (a_util->matching < 0) {
a_util->matching = a2b[i];
b_util->matching = i;
}Anyway, I don't think it really matters, as there is no "double booking", so we should essentially get the same results.
Quoted 10 lines
Quoted 3 lines
Shouldn't diffsize for matching patches also be 0? So are we doing the '
find_exact_matches()' bit only as an optimization, or am I missing some other reason why that is beneficial?An optimization.
Remember, the linear assignment algorithm runs in O(n^3). That's not a laughing matter by any stretch of imagination. The more stuff we can get out of the way, as quickly as possible, the less it hurts to have a cubic runtime complexity.
Makes sense, that's what I was expecting, just wanted to double check that I didn't miss something. Thanks for confirming!
Quoted 96 lines
Quoted 68 lines
Quoted 44 lines
range-diff.c new file +44
-0+ } + +free (cost); +free (a2b); +free (b2a); +} + +static const char *short_oid (struct patch_util *util) +{ +return find_unique_abbrev (&util->oid,DEFAULT_ABBREV ); +} + +static void output (struct string_list *a,struct string_list *b) +{ +int i; + +for (i =0 ; i < b->nr; i++) { +struct patch_util *util = b->items[i].util, *prev; + +if (util->matching <0 ) +printf ("-: -------- > %d: %s\n" , + i +1 ,short_oid (util)); +else { + prev = a->items[util->matching].util; +printf ("%d: %s ! %d: %s\n" , + util->matching +1 ,short_oid (prev), + i +1 ,short_oid (util)); + } + } + +for (i =0 ; i < a->nr; i++) { +struct patch_util *util = a->items[i].util; + +if (util->matching <0 ) +printf ("%d: %s < -: --------\n" , + i +1 ,short_oid (util)); + } +} + +int show_range_diff (const char *range1,const char *range2, +int creation_factor) +{ +int res =0 ; +range-diff.c line 302
Quoted 10 lines
range-diff.c new file +10
-0+struct string_list branch1 =STRING_LIST_INIT_DUP ; +struct string_list branch2 =STRING_LIST_INIT_DUP ; + +if (read_patches (range1, &branch1)) + res =error (_ ("could not parse log for '%s'" ), range1); +if (!res &&read_patches (range2, &branch2)) + res =error (_ ("could not parse log for '%s'" ), range2); + +if (!res) { +find_exact_matches (&branch1, &branch2);Note to self: here we assign the matching member of struct
patch_utilfor each patch in both ranges to a patch number in the other range if it is an exact match.We also assign the patch and diff members, and number the patches using the 'i' member of struct
patch_util. Let's see if that numbering is still useful later.range-diff.c line 303
Quoted 1 line
range-diff.c new file +1
-0+get_correspondences (&branch1, &branch2, creation_factor);And here we use the linear assignment algorithm to match the rest of the commits.
Quoted 7 lines
range-diff.c line 304
Quoted 1 line
range-diff.c new file +1
-0+output (&branch1, &branch2);And finally we print the output. We don't seem to use the
util->ithat's assigned for range b (or range 2) anywhere at the moment, which I was wondering about earlier, so I assume it's there mainly for symmetry, but it doesn't really hurt other than me wondering what it was for.It's there because it would require extra care to do it only for one side.
Quoted 9 lines
Quoted 9 lines
range-diff.c new file +7
-0+ } + +string_list_clear (&branch1,1 ); +string_list_clear (&branch2,1 ); + +return res; +}range-diff.h new file +0
-0Quoted 8 lines
range-diff.h line 2
Quoted 6 lines
index 000000000..dd30449c4
range-diff.h new file +2
-0@@ -0,0 +1,7 @@ +#ifndef BRANCH_DIFF_H +#define BRANCH_DIFF_H s/BRANCH/RANGE/ above?
Quoted 4 lines
Good eyes.
Thank you for your review. It is nice to see that you can follow the code and come to the same conclusions as I. Can I always have you as reviewer?
Glad I could help. Can I have more hours in a day? ;) I wish I had more time for reviewing code, but sadly the time I have is limited. Which is why I only got to review this now as well. I did enjoy the read!
Quoted 11 lines
Ciao,
DschoQuoted 8 lines
Quoted 7 lines
range-diff.h new file +4
-0+int show_range_diff (const char *range1,const char *range2, +int creation_factor); + +#endif Signature · 1 line
gitgitgadget
Re: [PATCH v4 03/21] range-diff: first rudimentary implementation
Hi Thomas,
Quoted 162 lines · Thomas Gummerer
Quoted 58 lines · Johannes Schindelin
Quoted 55 lines · Thomas Gummerer
Quoted 28 lines · Johannes Schindelin via GitGitGadget
[...]
range-diff.c new file +25
-0+static void find_exact_matches (struct string_list *a,struct string_list *b) +{ +struct hashmap map; +int i; + +hashmap_init (&map, (hashmap_cmp_fn )patch_util_cmp,NULL ,0 ); + +/* First, add the patches of a to a hash map */ +for (i =0 ; i < a->nr; i++) { +struct patch_util *util = a->items[i].util; + + util->i = i; + util->patch = a->items[i].string; + util->diff = util->patch + util->diff_offset; +hashmap_entry_init (util,strhash (util->diff)); +hashmap_add (&map, util); + } + +/* Now try to find exact matches in b */ +for (i =0 ; i < b->nr; i++) { +struct patch_util *util = b->items[i].util, *other; + + util->i = i; + util->patch = b->items[i].string; + util->diff = util->patch + util->diff_offset;range-diff.c line 165
Quoted 10 lines
range-diff.c new file +10
-0+hashmap_entry_init (util,strhash (util->diff)); + other =hashmap_remove (&map, util,NULL ); +if (other) { +if (other->matching >=0 ) +BUG ("already assigned!" ); + + other->matching = i; + util->matching = other->i; + } + }One possibly interesting corner case here is what happens when there are two patches that have the exact same diff, for example in the pathological case of commit A doing something, commit B reverting commit A, and then commit C reverting commit B, so it ends up with the same diff.
Having those same commits unchanged in both ranges (e.g. if a commit earlier in the range has been changed, and range B has been rebased on top of that), we'd get the following mapping from range A to range B for the commits in question:
A -> C
B -> B
C -> AQuoted 8 lines
Quoted 5 lines
Which is not quite what I would expect as the user (even though it is a valid mapping, and it probably doesn't matter too much for the end result of the range diff, as nothing has changed between the commits anyway). So I'm not sure it's worth fixing this, as it is a pathological case, and nothing really breaks.
Indeed. As far as I am concerned, this falls squarely into the "let's cross that bridge when, or if, we reach it" category.
Makes sense, this can definitely be addressed later.
Quoted 87 lines
Quoted 78 lines
Quoted 78 lines
range-diff.c new file +78
-0+ +hashmap_free (&map,0 ); +} + +static void diffsize_consume (void *data,char *line,unsigned long len) +{ + (*(int *)data)++; +} + +static int diffsize (const char *a,const char *b) +{ +xpparam_t pp = {0 }; +xdemitconf_t cfg = {0 }; +mmfile_t mf1, mf2; +int count =0 ; + + mf1.ptr = (char *)a; + mf1.size =strlen (a); + mf2.ptr = (char *)b; + mf2.size =strlen (b); + + cfg.ctxlen =3 ; +if (!xdi_diff_outf (&mf1, &mf2, diffsize_consume, &count, &pp, &cfg)) +return count; + +error (_ ("failed to generate diff" )); +return COST_MAX ; +} + +static void get_correspondences (struct string_list *a,struct string_list *b, +int creation_factor) +{ +int n = a->nr + b->nr; +int *cost, c, *a2b, *b2a; +int i, j; + +ALLOC_ARRAY (cost,st_mult (n, n)); +ALLOC_ARRAY (a2b, n); +ALLOC_ARRAY (b2a, n); + +for (i =0 ; i < a->nr; i++) { +struct patch_util *a_util = a->items[i].util; + +for (j =0 ; j < b->nr; j++) { +struct patch_util *b_util = b->items[j].util; + +if (a_util->matching == j) + c =0 ; +else if (a_util->matching <0 && b_util->matching <0 ) + c =diffsize (a_util->diff, b_util->diff); +else + c =COST_MAX ; + cost[i + n * j] = c; + } + + c = a_util->matching <0 ? + a_util->diffsize * creation_factor /100 :COST_MAX ; +for (j = b->nr; j < n; j++) + cost[i + n * j] = c; + } + +for (j =0 ; j < b->nr; j++) { +struct patch_util *util = b->items[j].util; + + c = util->matching <0 ? + util->diffsize * creation_factor /100 :COST_MAX ; +for (i = a->nr; i < n; i++) + cost[i + n * j] = c; + } + +for (i = a->nr; i < n; i++) +for (j = b->nr; j < n; j++) + cost[i + n * j] =0 ; + +compute_assignment (n, n, cost, a2b, b2a); + +for (i =0 ; i < a->nr; i++) +if (a2b[i] >=0 && a2b[i] < b->nr) {Quoted 8 lines
range-diff.c line 248
Quoted 5 lines
range-diff.c new file +5
-0+struct patch_util *a_util = a->items[i].util; +struct patch_util *b_util = b->items[a2b[i]].util; + + a_util->matching = a2b[i]; + b_util->matching = i;So here we re-assign 'matching' in the struct regardless of whether it was assigned before while searching for exact matches or not.
Quoted 4 lines
If
matchingwere assigned here, it would indicate a bug in the code, I think. Before this loop, all of thematchingfields should beNULL, and thecompute_assignment()function is expected to populatea2bandb2aappropriately, i.e. no "double booking".
Quoted 6 lines
Hmm we are using the 'matching' fields in the loops above, e.g.:
if (a_util->matching == j) c = 0;They're also ints, so they can't be
NULL, right?
Right, I was confused, they are not pointers. They are initialized to -1, though.
Quoted 6 lines
So what I was thinking is that we could do
if (a_util->matching < 0) { a_util->matching = a2b[i]; b_util->matching = i; }
Yes, we could have that safeguard. But then, we should actually rather say
if (a_util->matching >= 0)
BUG("a[%d] already assigned to %d, cannot assign to %d",
i, a_util->matching, a2b[i]);The reason this is a bug is that we really just calculated a linear assignment, and a2b was initialized accordingly: it contains a 1:1 mapping between a->items and b->items.
Side note: technically, the linear assignment is able to handle two differently-sized sets. However, due to implementation details, we do not use this, as a->nr and b->nr are both set to n. The reason is that we want to avoid forcing matches where there are none, i.e. we add specific entries to the cost matrix to allow for the "creation" or "destruction" if that is cheaper than to match two completely unrelated commits. It just so happens that these filler entries result in identical set sizes.
So practically, that if() guard is not necessary, and honestly, I would stumble over it every time ("Why is this needed? Should a2b and b2a not have a complete assignment already?")), so I'd rather not have it.
Quoted 2 lines
Anyway, I don't think it really matters, as there is no "double booking", so we should essentially get the same results.
Exactly.
If you were worried about it, I would introduce that guarded BUG(), but it is not an off-by-one error, so I probably did not introduce a bug here.
Quoted 4 lines
Glad I could help. Can I have more hours in a day? ;) I wish I had more time for reviewing code, but sadly the time I have is limited. Which is why I only got to review this now as well. I did enjoy the read!
Thank you so much! I appreciate your help, and it was a very pleasant and useful review that really made the patch series better. There were three distinct issues that would have otherwise been missed before this hit next.
Thanks!
Dscho
PATCH v4 4/21 range-diff: improve the order of the shown commits
In reply to Johannes Schindelin via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
This patch lets git range-diff use the same order as tbdiff.
The idea is simple: for left-to-right readers, it is natural to assume that the git range-diff is performed between an older vs a newer version of the branch. As such, the user is probably more interested in the question "where did this come from?" rather than "where did that one go?".
To that end, we list the commits in the order of the second commit range ("the newer version"), inserting the unmatched commits of the first commit range as soon as all their predecessors have been shown.
--- range-diff.c | 59 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 19 deletions(-)
range-diff.c +40 -19
@@ -12,7 +12,7 @@ struct patch_util {struct hashmap_entry e;const char *diff, *patch; -int i; +int i, shown;int diffsize;size_t diff_offset;/* the index of the matching item in the other branch, or -1 */
@@ -260,28 +260,49 @@ static const char *short_oid(struct patch_util *util)static void output (struct string_list *a,struct string_list *b) { -int i; - -for (i =0 ; i < b->nr; i++) { -struct patch_util *util = b->items[i].util, *prev; +int i =0 , j =0 ; + +/* +* We assume the user is really more interested in the second argument +* ("newer" version). To that end, we print the output in the order of +* the RHS (the `b` parameter). To put the LHS (the `a` parameter) +* commits that are no longer in the RHS into a good place, we place +* them once we have shown all of their predecessors in the LHS. +*/ + +while (i < a->nr || j < b->nr) { +struct patch_util *a_util, *b_util; + a_util = i < a->nr ? a->items[i].util :NULL ; + b_util = j < b->nr ? b->items[j].util :NULL ; + +/* Skip all the already-shown commits from the LHS. */ +while (i < a->nr && a_util->shown) + a_util = ++i < a->nr ? a->items[i].util :NULL ; + +/* Show unmatched LHS commit whose predecessors were shown. */ +if (i < a->nr && a_util->matching <0 ) { +printf ("%d: %s < -: --------\n" , + i +1 ,short_oid (a_util)); + i++; +continue ; + } -if (util->matching <0 ) +/* Show unmatched RHS commits. */ +while (j < b->nr && b_util->matching <0 ) {printf ("-: -------- > %d: %s\n" , - i +1 ,short_oid (util)); -else { - prev = a->items[util->matching].util; -printf ("%d: %s ! %d: %s\n" , - util->matching +1 ,short_oid (prev), - i +1 ,short_oid (util)); + j +1 ,short_oid (b_util)); + b_util = ++j < b->nr ? b->items[j].util :NULL ; } - } - -for (i =0 ; i < a->nr; i++) { -struct patch_util *util = a->items[i].util; -if (util->matching <0 ) -printf ("%d: %s < -: --------\n" , - i +1 ,short_oid (util)); +/* Show matching LHS/RHS pair. */ +if (j < b->nr) { + a_util = a->items[b_util->matching].util; +printf ("%d: %s ! %d: %s\n" , + b_util->matching +1 ,short_oid (a_util), + j +1 ,short_oid (b_util)); + a_util->shown =1 ; + j++; + } } }
Signature · 1 line
gitgitgadget
PATCH v4 5/21 range-diff: also show the diff between patches
In reply to Johannes Schindelin via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
Just like tbdiff, we now show the diff between matching patches. This is a "diff of two diffs", so it can be a bit daunting to read for the beginner.
An alternative would be to display an interdiff, i.e. the hypothetical diff which is the result of first reverting the old diff and then applying the new diff.
Especially when rebasing often, an interdiff is often not feasible,
though: if the old diff cannot be applied in reverse (due to a moving upstream), an interdiff can simply not be inferred.
This commit brings range-diff closer to feature parity with regard to tbdiff.
To make git range-diff respect e.g. color.diff.* settings, we have to adjust git_branch_config() accordingly.
Note: while we now parse diff options such as --color, the effect is not yet the same as in tbdiff, where also the commit pairs would be colored. This is left for a later commit.
Note also: while tbdiff accepts the --no-patches option to suppress these diffs between patches, we prefer the -s option that is automatically supported via our use of diff_opt_parse().
--- builtin/range-diff.c | 25 ++++++++++++++++++++++--- range-diff.c | 34 +++++++++++++++++++++++++++++++--- range-diff.h | 4 +++- 3 files changed, 56 insertions(+), 7 deletions(-)
builtin/range-diff.c +22 -3
@@ -2,6 +2,7 @@#include "builtin.h" #include "parse-options.h" #include "range-diff.h" +#include "config.h" static const char *const builtin_range_diff_usage[] = {N_ ("git range-diff [<options>] <old-base>..<old-tip> <new-base>..<new-tip>" ),
@@ -13,16 +14,33 @@ NULLint cmd_range_diff (int argc,const char **argv,const char *prefix) {int creation_factor =60 ; +struct diff_options diffopt = {NULL };struct option options[] = {OPT_INTEGER (0 ,"creation-factor" , &creation_factor,N_ ("Percentage by which creation is weighted" )),OPT_END () }; -int res =0 ; +int i, j, res =0 ;struct strbuf range1 =STRBUF_INIT , range2 =STRBUF_INIT ; +git_config (git_diff_ui_config,NULL ); + +diff_setup (&diffopt); + diffopt.output_format =DIFF_FORMAT_PATCH ; + argc =parse_options (argc, argv,NULL , options, - builtin_range_diff_usage,0 ); + builtin_range_diff_usage,PARSE_OPT_KEEP_UNKNOWN ); + +for (i = j =0 ; i < argc; ) { +int c =diff_opt_parse (&diffopt, argv + i, argc - i, prefix); + +if (!c) + argv[j++] = argv[i++]; +else + i += c; + } + argc = j; +diff_setup_done (&diffopt);if (argc ==2 ) {if (!strstr (argv[0 ],".." ))
@@ -57,7 +75,8 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix)usage_with_options (builtin_range_diff_usage, options); } - res =show_range_diff (range1.buf, range2.buf, creation_factor); + res =show_range_diff (range1.buf, range2.buf, creation_factor, + &diffopt);strbuf_release (&range1);strbuf_release (&range2);
range-diff.c +31 -3
@@ -6,6 +6,7 @@#include "hashmap.h" #include "xdiff-interface.h" #include "linear-assignment.h" +#include "diffcore.h" struct patch_util {/* For the search for an exact match */
@@ -258,7 +259,31 @@ static const char *short_oid(struct patch_util *util)return find_unique_abbrev (&util->oid,DEFAULT_ABBREV ); } -static void output (struct string_list *a,struct string_list *b) +static struct diff_filespec *get_filespec (const char *name,const char *p) +{ +struct diff_filespec *spec =alloc_filespec (name); + +fill_filespec (spec, &null_oid,0 ,0644 ); + spec->data = (char *)p; + spec->size =strlen (p); + spec->should_munmap =0 ; + spec->is_stdin =1 ; + +return spec; +} + +static void patch_diff (const char *a,const char *b, +struct diff_options *diffopt) +{ +diff_queue (&diff_queued_diff, +get_filespec ("a" , a),get_filespec ("b" , b)); + +diffcore_std (diffopt); +diff_flush (diffopt); +} + +static void output (struct string_list *a,struct string_list *b, +struct diff_options *diffopt) {int i =0 , j =0 ;
@@ -300,6 +325,9 @@ static void output(struct string_list *a, struct string_list *b)printf ("%d: %s ! %d: %s\n" , b_util->matching +1 ,short_oid (a_util), j +1 ,short_oid (b_util)); +if (!(diffopt->output_format &DIFF_FORMAT_NO_OUTPUT )) +patch_diff (a->items[b_util->matching].string, + b->items[j].string, diffopt); a_util->shown =1 ; j++; }
@@ -307,7 +335,7 @@ static void output(struct string_list *a, struct string_list *b) }int show_range_diff (const char *range1,const char *range2, -int creation_factor) +int creation_factor,struct diff_options *diffopt) {int res =0 ;
@@ -322,7 +350,7 @@ int show_range_diff(const char *range1, const char *range2,if (!res) {find_exact_matches (&branch1, &branch2);get_correspondences (&branch1, &branch2, creation_factor); -output (&branch1, &branch2); +output (&branch1, &branch2, diffopt); }string_list_clear (&branch1,1 );
range-diff.h +3 -1
@@ -1,7 +1,9 @@#ifndef BRANCH_DIFF_H #define BRANCH_DIFF_H +#include "diff.h" +int show_range_diff (const char *range1,const char *range2, -int creation_factor); +int creation_factor,struct diff_options *diffopt);#endif
Signature · 1 line
gitgitgadget
Re: [PATCH v4 05/21] range-diff: also show the diff between patches
Quoted 20 lines · Johannes Schindelin via GitGitGadget
From: Johannes Schindelin <[email protected]>
Just like tbdiff, we now show the diff between matching patches. This is a "diff of two diffs", so it can be a bit daunting to read for the beginner.
An alternative would be to display an interdiff, i.e. the hypothetical diff which is the result of first reverting the old diff and then applying the new diff.
Especially when rebasing often, an interdiff is often not feasible,
though: if the old diff cannot be applied in reverse (due to a moving upstream), an interdiff can simply not be inferred.This commit brings
range-diffcloser to feature parity with regard to tbdiff.To make
git range-diffrespect e.g. color.diff.* settings, we have to adjustgit_branch_config()accordingly.
Quoted 7 lines
Note: while we now parse diff options such as --color, the effect is not yet the same as in tbdiff, where also the commit pairs would be colored. This is left for a later commit.
Note also: while tbdiff accepts the
--no-patchesoption to suppress these diffs between patches, we prefer the-soption that is automatically supported via our use ofdiff_opt_parse().
One slightly unfortunate thing here is that we don't show these options in 'git range-diff -h', which would be nice to have. I don't know if that's possible in git right now, if it's not easily possible, I definitely wouldn't want to delay this series for that, and we could just add it to the list of possible future enhancements that other people mentioned.
Quoted 55 lines
Signed-off-by Johannes Schindelin <[email protected]>--- builtin/range-diff.c | 25 ++++++++++++++++++++++--- range-diff.c | 34 +++++++++++++++++++++++++++++++--- range-diff.h | 4 +++- 3 files changed, 56 insertions(+), 7 deletions(-)[...]
range-diff.c +26
-1@@ -6,6 +6,7 @@#include "hashmap.h" #include "xdiff-interface.h" #include "linear-assignment.h" +#include "diffcore.h" struct patch_util {/* For the search for an exact match */ @@ -258,7 +259,31 @@ static const char *short_oid(struct patch_util *util)return find_unique_abbrev (&util->oid,DEFAULT_ABBREV ); } -static void output (struct string_list *a,struct string_list *b) +static struct diff_filespec *get_filespec (const char *name,const char *p) +{ +struct diff_filespec *spec =alloc_filespec (name); + +fill_filespec (spec, &null_oid,0 ,0644 ); + spec->data = (char *)p; + spec->size =strlen (p); + spec->should_munmap =0 ; + spec->is_stdin =1 ; + +return spec; +} + +static void patch_diff (const char *a,const char *b, +struct diff_options *diffopt) +{ +diff_queue (&diff_queued_diff, +get_filespec ("a" , a),get_filespec ("b" , b)); + +diffcore_std (diffopt); +diff_flush (diffopt); +} + +static void output (struct string_list *a,struct string_list *b, +struct diff_options *diffopt) {int i =0 , j =0 ;
range-diff.c static void output(struct string_list *a, struct string_list *b) · line 328
Quoted 4 lines
range-diff.c +1
-0@@ -300,6 +325,9 @@ static void output(struct string_list *a, struct string_list *b)printf ("%d: %s ! %d: %s\n" , b_util->matching +1 ,short_oid (a_util), j +1 ,short_oid (b_util)); +if (!(diffopt->output_format &DIFF_FORMAT_NO_OUTPUT ))
Looking at this line, it looks like it would be easy to support '--no-patches' as well, which may be slightly easier to understand that '-s' to someone new to the command. But again that can be added later if someone actually cares about it.
Quoted 41 lines
range-diff.c +4
-2+patch_diff (a->items[b_util->matching].string, + b->items[j].string, diffopt); a_util->shown =1 ; j++; }@@ -307,7 +335,7 @@ static void output(struct string_list *a, struct string_list *b) }int show_range_diff (const char *range1,const char *range2, -int creation_factor) +int creation_factor,struct diff_options *diffopt) {int res =0 ;@@ -322,7 +350,7 @@ int show_range_diff(const char *range1, const char *range2,if (!res) {find_exact_matches (&branch1, &branch2);get_correspondences (&branch1, &branch2, creation_factor); -output (&branch1, &branch2); +output (&branch1, &branch2, diffopt); }string_list_clear (&branch1,1 );range-diff.h +3
-1@@ -1,7 +1,9 @@#ifndef BRANCH_DIFF_H #define BRANCH_DIFF_H +#include "diff.h" +int show_range_diff (const char *range1,const char *range2, -int creation_factor); +int creation_factor,struct diff_options *diffopt);#endif Signature · 1 line
gitgitgadget
Re: [PATCH v4 05/21] range-diff: also show the diff between patches
Quoted 9 lines · Thomas Gummerer
Quoted 7 lines · Johannes Schindelin via GitGitGadget
Just like tbdiff, we now show the diff between matching patches. This is a "diff of two diffs", so it can be a bit daunting to read for the beginner.
[...]
Note also: while tbdiff accepts the--no-patchesoption to suppress these diffs between patches, we prefer the-soption that is automatically supported via our use ofdiff_opt_parse().
Quoted 6 lines
One slightly unfortunate thing here is that we don't show these options in 'git range-diff -h', which would be nice to have. I don't know if that's possible in git right now, if it's not easily possible, I definitely wouldn't want to delay this series for that, and we could just add it to the list of possible future enhancements that other people mentioned.
This issue is not specific to git-range-diff; it's shared by other commands which inherit diff options via diff_opt_parse(). For instance, "git log -h" doesn't show diff-related options either, yet it accepts them.
Quoted 11 lines
range-diff.c static void output(struct string_list *a, struct string_list *b) · line 328
Quoted 6 lines
range-diff.c +1
-0@@ -300,6 +325,9 @@ static void output(struct string_list *a, struct string_list *b)printf ("%d: %s ! %d: %s\n" , b_util->matching +1 ,short_oid (a_util), j +1 ,short_oid (b_util)); +if (!(diffopt->output_format &DIFF_FORMAT_NO_OUTPUT ))Looking at this line, it looks like it would be easy to support '--no-patches' as well, which may be slightly easier to understand that '-s' to someone new to the command. But again that can be added later if someone actually cares about it.
What wasn't mentioned (but was implied) by the commit message is that "-s" is short for "--no-patch", which also comes for free via diff_opt_parse(). True, "--no-patch" isn't spelled exactly the same as "--no-patches", but git-range-diff isn't exactly a perfect tbdiff clone, so hopefully not a git problem. Moreover, "--no-patch" is internally consistent within the Git builtin commands.
Re: [PATCH v4 05/21] range-diff: also show the diff between patches
Quoted 17 lines · Eric Sunshine
Quoted 9 lines · Thomas Gummerer
Quoted 7 lines · Johannes Schindelin via GitGitGadget
Just like tbdiff, we now show the diff between matching patches. This is a "diff of two diffs", so it can be a bit daunting to read for the beginner.
[...]
Note also: while tbdiff accepts the--no-patchesoption to suppress these diffs between patches, we prefer the-soption that is automatically supported via our use ofdiff_opt_parse().Quoted 6 lines
One slightly unfortunate thing here is that we don't show these options in 'git range-diff -h', which would be nice to have. I don't know if that's possible in git right now, if it's not easily possible, I definitely wouldn't want to delay this series for that, and we could just add it to the list of possible future enhancements that other people mentioned.
Quoted 4 lines
This issue is not specific to git-range-diff; it's shared by other commands which inherit diff options via
diff_opt_parse(). For instance, "git log -h" doesn't show diff-related options either, yet it accepts them.
Fair enough, that makes sense. Thanks for the pointer!
There's one more thing that I noticed here:
git range-diff --no-patches
fatal: single arg format requires a symmetric range
Which is a slightly confusing error message. In contrast git log does the following on an unrecognized argument:
git log --no-patches
fatal: unrecognized argument: --no-patcheswhich is a little better I think. I do however also thing the "fatal:
single arg format requires a symmetric range" is useful when someone genuinely tries to use the single argument version of the command. So I don't know what a good solution for this would be.
Quoted 12 lines
Quoted 11 lines
range-diff.c static void output(struct string_list *a, struct string_list *b) · line 328
Quoted 6 lines
range-diff.c +1
-0@@ -300,6 +325,9 @@ static void output(struct string_list *a, struct string_list *b)printf ("%d: %s ! %d: %s\n" , b_util->matching +1 ,short_oid (a_util), j +1 ,short_oid (b_util)); +if (!(diffopt->output_format &DIFF_FORMAT_NO_OUTPUT ))Looking at this line, it looks like it would be easy to support '--no-patches' as well, which may be slightly easier to understand that '-s' to someone new to the command. But again that can be added later if someone actually cares about it.
Quoted 6 lines
What wasn't mentioned (but was implied) by the commit message is that "-s" is short for "--no-patch", which also comes for free via
diff_opt_parse(). True, "--no-patch" isn't spelled exactly the same as "--no-patches", but git-range-diff isn't exactly a perfect tbdiff clone, so hopefully not a git problem. Moreover, "--no-patch" is internally consistent within the Git builtin commands.
Makes sense, thanks! "--no-patch" does make sense to me. There's still a lot of command line flags in git to learn for me, even after all this time using it ;) Might be nice to spell it out in the commit message for someone like me, especially as "--no-patches" is already mentioned. Though I guess most regulars here would know about "--no-patch", so maybe it's not worth it. Anyway that is definitely not worth another round here.
Re: [PATCH v4 05/21] range-diff: also show the diff between patches
Hi Thomas & Eric,
Quoted 33 lines · Thomas Gummerer
Quoted 17 lines · Eric Sunshine
Quoted 9 lines · Thomas Gummerer
Quoted 7 lines · Johannes Schindelin via GitGitGadget
Just like tbdiff, we now show the diff between matching patches. This is a "diff of two diffs", so it can be a bit daunting to read for the beginner.
[...]
Note also: while tbdiff accepts the--no-patchesoption to suppress these diffs between patches, we prefer the-soption that is automatically supported via our use ofdiff_opt_parse().Quoted 6 lines
One slightly unfortunate thing here is that we don't show these options in 'git range-diff -h', which would be nice to have. I don't know if that's possible in git right now, if it's not easily possible, I definitely wouldn't want to delay this series for that, and we could just add it to the list of possible future enhancements that other people mentioned.
Quoted 4 lines
This issue is not specific to git-range-diff; it's shared by other commands which inherit diff options via
diff_opt_parse(). For instance, "git log -h" doesn't show diff-related options either, yet it accepts them.Fair enough, that makes sense. Thanks for the pointer!
There's one more thing that I noticed here:
git range-diff --no-patches
fatal: single arg format requires a symmetric rangeWhich is a slightly confusing error message. In contrast git log does the following on an unrecognized argument:
Quoted 7 lines
git log --no-patches fatal: unrecognized argument: --no-patcheswhich is a little better I think. I do however also thing the "fatal:
single arg format requires a symmetric range" is useful when someone genuinely tries to use the single argument version of the command. So I don't know what a good solution for this would be.
I immediately thought of testing for a leading - of the remaining argument, but I could imagine that somebody enterprisey uses
git range-diff -- -my-first-attempt...-my-second-attempt
and I do not really want to complexify the code... Ideas?
Quoted 19 lines
Quoted 12 lines
Quoted 11 lines
range-diff.c static void output(struct string_list *a, struct string_list *b) · line 328
Quoted 6 lines
range-diff.c +1
-0@@ -300,6 +325,9 @@ static void output(struct string_list *a, struct string_list *b)printf ("%d: %s ! %d: %s\n" , b_util->matching +1 ,short_oid (a_util), j +1 ,short_oid (b_util)); +if (!(diffopt->output_format &DIFF_FORMAT_NO_OUTPUT ))Looking at this line, it looks like it would be easy to support '--no-patches' as well, which may be slightly easier to understand that '-s' to someone new to the command. But again that can be added later if someone actually cares about it.
Quoted 6 lines
What wasn't mentioned (but was implied) by the commit message is that "-s" is short for "--no-patch", which also comes for free via
diff_opt_parse(). True, "--no-patch" isn't spelled exactly the same as "--no-patches", but git-range-diff isn't exactly a perfect tbdiff clone, so hopefully not a git problem. Moreover, "--no-patch" is internally consistent within the Git builtin commands.
Quoted 7 lines
Makes sense, thanks! "--no-patch" does make sense to me. There's still a lot of command line flags in git to learn for me, even after all this time using it ;) Might be nice to spell it out in the commit message for someone like me, especially as "--no-patches" is already mentioned. Though I guess most regulars here would know about "--no-patch", so maybe it's not worth it. Anyway that is definitely not worth another round here.
Sure, but not many users learn from reading the commit history...
:-)
Ciao,
Dscho
Re: [PATCH v4 05/21] range-diff: also show the diff between patches
Quoted 45 lines · Johannes Schindelin
Hi Thomas & Eric,
Quoted 33 lines · Thomas Gummerer
Quoted 17 lines · Eric Sunshine
Quoted 9 lines · Thomas Gummerer
Quoted 7 lines · Johannes Schindelin via GitGitGadget
Just like tbdiff, we now show the diff between matching patches. This is a "diff of two diffs", so it can be a bit daunting to read for the beginner.
[...]
Note also: while tbdiff accepts the--no-patchesoption to suppress these diffs between patches, we prefer the-soption that is automatically supported via our use ofdiff_opt_parse().Quoted 6 lines
One slightly unfortunate thing here is that we don't show these options in 'git range-diff -h', which would be nice to have. I don't know if that's possible in git right now, if it's not easily possible, I definitely wouldn't want to delay this series for that, and we could just add it to the list of possible future enhancements that other people mentioned.
Quoted 4 lines
This issue is not specific to git-range-diff; it's shared by other commands which inherit diff options via
diff_opt_parse(). For instance, "git log -h" doesn't show diff-related options either, yet it accepts them.Fair enough, that makes sense. Thanks for the pointer!
There's one more thing that I noticed here:
git range-diff --no-patches
fatal: single arg format requires a symmetric rangeWhich is a slightly confusing error message. In contrast git log does the following on an unrecognized argument:
Quoted 7 lines
git log --no-patches fatal: unrecognized argument: --no-patcheswhich is a little better I think. I do however also thing the "fatal:
single arg format requires a symmetric range" is useful when someone genuinely tries to use the single argument version of the command. So I don't know what a good solution for this would be.
Quoted 6 lines
I immediately thought of testing for a leading
-of the remaining argument, but I could imagine that somebody enterprisey usesgit range-diff -- -my-first-attempt...-my-second-attempt
and I do not really want to complexify the code... Ideas?
Good point. I can't really come up with a good option right now either. It's not too bad, as users just typed the command, so it should be easy enough to see from the previous line what went wrong.
One potential option may be to turn "die(_("single arg format requires a symmetric range"));" into an 'error()', and show the usage? I think that may be nice anyway, as "symmetric range" may not be immediately obvious to everyone, but together with the usage it may be clearer?
Quoted 33 lines
Quoted 19 lines
Quoted 12 lines
Quoted 11 lines
range-diff.c static void output(struct string_list *a, struct string_list *b) · line 328
Quoted 6 lines
range-diff.c +1
-0@@ -300,6 +325,9 @@ static void output(struct string_list *a, struct string_list *b)printf ("%d: %s ! %d: %s\n" , b_util->matching +1 ,short_oid (a_util), j +1 ,short_oid (b_util)); +if (!(diffopt->output_format &DIFF_FORMAT_NO_OUTPUT ))Looking at this line, it looks like it would be easy to support '--no-patches' as well, which may be slightly easier to understand that '-s' to someone new to the command. But again that can be added later if someone actually cares about it.
Quoted 6 lines
What wasn't mentioned (but was implied) by the commit message is that "-s" is short for "--no-patch", which also comes for free via
diff_opt_parse(). True, "--no-patch" isn't spelled exactly the same as "--no-patches", but git-range-diff isn't exactly a perfect tbdiff clone, so hopefully not a git problem. Moreover, "--no-patch" is internally consistent within the Git builtin commands.Quoted 7 lines
Makes sense, thanks! "--no-patch" does make sense to me. There's still a lot of command line flags in git to learn for me, even after all this time using it ;) Might be nice to spell it out in the commit message for someone like me, especially as "--no-patches" is already mentioned. Though I guess most regulars here would know about "--no-patch", so maybe it's not worth it. Anyway that is definitely not worth another round here.
Sure, but not many users learn from reading the commit history...
:-)
Ciao,
Dscho
Re: [PATCH v4 05/21] range-diff: also show the diff between patches
Quoted 10 lines · Thomas Gummerer
Quoted 9 lines · Johannes Schindelin
Quoted 5 lines · Thomas Gummerer
There's one more thing that I noticed here:
git range-diff --no-patches
fatal: single arg format requires a symmetric rangeI immediately thought of testing for a leading
-of the remaining argument, but I could imagine that somebody enterprisey uses
Quoted 7 lines
Quoted 3 lines
git range-diff -- -my-first-attempt...-my-second-attempt
and I do not really want to complexify the code... Ideas?
Good point. I can't really come up with a good option right now either. It's not too bad, as users just typed the command, so it should be easy enough to see from the previous line what went wrong.
I think you can attain the desired behavior by making a final parse_options() call with empty 'options' list after the call to diff_setup_done(). It's pretty much a one-line fix, but can probably be done as an incremental change rather than rerolling.
Re: [PATCH v4 05/21] range-diff: also show the diff between patches
Hi Eric,
Quoted 15 lines · Eric Sunshine
Quoted 14 lines · Thomas Gummerer
Quoted 12 lines · Johannes Schindelin
Quoted 5 lines · Thomas Gummerer
There's one more thing that I noticed here:
git range-diff --no-patches
fatal: single arg format requires a symmetric rangeI immediately thought of testing for a leading
-of the remaining argument, but I could imagine that somebody enterprisey usesgit range-diff -- -my-first-attempt...-my-second-attempt
and I do not really want to complexify the code... Ideas?
Quoted 8 lines
Quoted 3 lines
Good point. I can't really come up with a good option right now either. It's not too bad, as users just typed the command, so it should be easy enough to see from the previous line what went wrong.
I think you can attain the desired behavior by making a final
parse_options()call with empty 'options' list after the call todiff_setup_done(). It's pretty much a one-line fix, but can probably be done as an incremental change rather than rerolling.
But then we would have to keep -- in the first, and not in the second parse_options() call, right? We would also have to handle that -- properly in the loop that calls diff_opt_parse(), I think.
A bit more involved than just a one-line fix, but I guess I'll give it a try.
Ciao,
Dscho
Re: [PATCH v4 05/21] range-diff: also show the diff between patches
Quoted 11 lines · Johannes Schindelin
Quoted 4 lines · Eric Sunshine
I think you can attain the desired behavior by making a final
parse_options()call with empty 'options' list after the call todiff_setup_done(). It's pretty much a one-line fix, but can probably be done as an incremental change rather than rerolling.But then we would have to keep
--in the first, and not in the secondparse_options()call, right? We would also have to handle that--properly in the loop that callsdiff_opt_parse(), I think.
A bit more involved than just a one-line fix, but I guess I'll give it a try.
It's something that could easily wait until after this series lands. After all, it's just a slightly confusing error message, not some fundamental problem.
As for '--', I'll have to go back and look at the code. I thought I had thought it all through at the time I made the suggestion, but my brain needs a refresh by now.
Re: [PATCH v4 05/21] range-diff: also show the diff between patches
Hi Eric,
Quoted 14 lines · Eric Sunshine
Quoted 11 lines · Johannes Schindelin
Quoted 4 lines · Eric Sunshine
I think you can attain the desired behavior by making a final
parse_options()call with empty 'options' list after the call todiff_setup_done(). It's pretty much a one-line fix, but can probably be done as an incremental change rather than rerolling.But then we would have to keep
--in the first, and not in the secondparse_options()call, right? We would also have to handle that--properly in the loop that callsdiff_opt_parse(), I think.
A bit more involved than just a one-line fix, but I guess I'll give it a try.
Quoted 7 lines
It's something that could easily wait until after this series lands. After all, it's just a slightly confusing error message, not some fundamental problem.
As for '--', I'll have to go back and look at the code. I thought I had thought it all through at the time I made the suggestion, but my brain needs a refresh by now.
Your suggestion might have started out by trying to fix the error message, but after staring at the code for a couple of minutes, I think the issue would have left all kinds of opportunities for bad user experience.
So I fixed it ;-)
Ciao,
Dscho
Re: [PATCH v4 05/21] range-diff: also show the diff between patches
In reply to Thomas GummererHi Thomas,
Quoted 52 lines · Thomas Gummerer
Quoted 44 lines · Johannes Schindelin
Quoted 33 lines · Thomas Gummerer
Quoted 17 lines · Eric Sunshine
Quoted 9 lines · Thomas Gummerer
Quoted 7 lines · Johannes Schindelin via GitGitGadget
Just like tbdiff, we now show the diff between matching patches. This is a "diff of two diffs", so it can be a bit daunting to read for the beginner.
[...]
Note also: while tbdiff accepts the--no-patchesoption to suppress these diffs between patches, we prefer the-soption that is automatically supported via our use ofdiff_opt_parse().Quoted 6 lines
One slightly unfortunate thing here is that we don't show these options in 'git range-diff -h', which would be nice to have. I don't know if that's possible in git right now, if it's not easily possible, I definitely wouldn't want to delay this series for that, and we could just add it to the list of possible future enhancements that other people mentioned.
Quoted 4 lines
This issue is not specific to git-range-diff; it's shared by other commands which inherit diff options via
diff_opt_parse(). For instance, "git log -h" doesn't show diff-related options either, yet it accepts them.Fair enough, that makes sense. Thanks for the pointer!
There's one more thing that I noticed here:
git range-diff --no-patches
fatal: single arg format requires a symmetric rangeWhich is a slightly confusing error message. In contrast git log does the following on an unrecognized argument:
Quoted 7 lines
git log --no-patches fatal: unrecognized argument: --no-patcheswhich is a little better I think. I do however also thing the "fatal:
single arg format requires a symmetric range" is useful when someone genuinely tries to use the single argument version of the command. So I don't know what a good solution for this would be.Quoted 6 lines
I immediately thought of testing for a leading
-of the remaining argument, but I could imagine that somebody enterprisey usesgit range-diff -- -my-first-attempt...-my-second-attempt
and I do not really want to complexify the code... Ideas?
Quoted 8 lines
Good point. I can't really come up with a good option right now either. It's not too bad, as users just typed the command, so it should be easy enough to see from the previous line what went wrong.
One potential option may be to turn "
die(_("single arg format requires a symmetric range"));" into an 'error()', and show the usage? I think that may be nice anyway, as "symmetric range" may not be immediately obvious to everyone, but together with the usage it may be clearer?
Agreed. Will be made so.
Ciao,
Dscho
Quoted 35 lines
Quoted 34 lines
Quoted 20 lines
Quoted 13 lines
Quoted 12 lines
range-diff.c static void output(struct string_list *a, struct string_list *b) · line 328
Quoted 7 lines
range-diff.c +1
-0@@ -300,6 +325,9 @@ static void output(struct string_list *a, struct string_list *b)printf ("%d: %s ! %d: %s\n" , b_util->matching +1 ,short_oid (a_util), j +1 ,short_oid (b_util)); +if (!(diffopt->output_format &DIFF_FORMAT_NO_OUTPUT ))Looking at this line, it looks like it would be easy to support '--no-patches' as well, which may be slightly easier to understand that '-s' to someone new to the command. But again that can be added later if someone actually cares about it.
Quoted 6 lines
What wasn't mentioned (but was implied) by the commit message is that "-s" is short for "--no-patch", which also comes for free via
diff_opt_parse(). True, "--no-patch" isn't spelled exactly the same as "--no-patches", but git-range-diff isn't exactly a perfect tbdiff clone, so hopefully not a git problem. Moreover, "--no-patch" is internally consistent within the Git builtin commands.Quoted 7 lines
Makes sense, thanks! "--no-patch" does make sense to me. There's still a lot of command line flags in git to learn for me, even after all this time using it ;) Might be nice to spell it out in the commit message for someone like me, especially as "--no-patches" is already mentioned. Though I guess most regulars here would know about "--no-patch", so maybe it's not worth it. Anyway that is definitely not worth another round here.
Sure, but not many users learn from reading the commit history...
:-)
Ciao,
Dscho
PATCH v4 6/21 range-diff: right-trim commit messages
In reply to Johannes Schindelin via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
When comparing commit messages, we need to keep in mind that they are indented by four spaces. That is, empty lines are no longer empty, but have "trailing whitespace". When displaying them in color, that results in those nagging red lines.
Let's just right-trim the lines in the commit message, it's not like trailing white-space in the commit messages are important enough to care about in git range-diff.
--- range-diff.c | 1 + 1 file changed, 1 insertion(+)
range-diff.c +1 -0
@@ -85,6 +85,7 @@ static int read_patches(const char *range, struct string_list *list)strbuf_addbuf (&buf, &line);strbuf_addstr (&buf,"\n\n" ); }else if (starts_with (line.buf," " )) { +strbuf_rtrim (&line);strbuf_addbuf (&buf, &line);strbuf_addch (&buf,'\n' ); }
Signature · 1 line
gitgitgadget
PATCH v4 7/21 range-diff: indent the diffs just like tbdiff
In reply to Johannes Schindelin via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
The main information in the range-diff view comes from the list of matching and non-matching commits, the diffs are additional information. Indenting them helps with the reading flow.
--- builtin/range-diff.c | 10 ++++++++++ 1 file changed, 10 insertions(+)
builtin/range-diff.c +10 -0
@@ -11,6 +11,11 @@ N_("git range-diff [<options>] <base> <old-tip> <new-tip>"), NULL }; +static struct strbuf *output_prefix_cb (struct diff_options *opt,void *data) +{ +return data; +} +int cmd_range_diff (int argc,const char **argv,const char *prefix) {int creation_factor =60 ;
@@ -21,12 +26,16 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix) OPT_END() };int i, j, res =0 ; +struct strbuf four_spaces =STRBUF_INIT ;struct strbuf range1 =STRBUF_INIT , range2 =STRBUF_INIT ;git_config (git_diff_ui_config,NULL );diff_setup (&diffopt); diffopt.output_format =DIFF_FORMAT_PATCH ; + diffopt.output_prefix = output_prefix_cb; +strbuf_addstr (&four_spaces," " ); + diffopt.output_prefix_data = &four_spaces; argc =parse_options (argc, argv,NULL , options, builtin_range_diff_usage,PARSE_OPT_KEEP_UNKNOWN );
@@ -80,6 +89,7 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix)strbuf_release (&range1);strbuf_release (&range2); +strbuf_release (&four_spaces);return res; }
Signature · 1 line
gitgitgadget
PATCH v4 8/21 range-diff: suppress the diff headers
In reply to Johannes Schindelin via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
When showing the diff between corresponding patches of the two branch versions, we have to make up a fake filename to run the diff machinery.
That filename does not carry any meaningful information, hence tbdiff suppresses it. So we should, too.
--- builtin/range-diff.c | 1 + diff.c | 5 ++++- diff.h | 1 + 3 files changed, 6 insertions(+), 1 deletion(-)
builtin/range-diff.c +1 -0
@@ -33,6 +33,7 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix)diff_setup (&diffopt); diffopt.output_format =DIFF_FORMAT_PATCH ; + diffopt.flags.suppress_diff_headers =1 ; diffopt.output_prefix = output_prefix_cb;strbuf_addstr (&four_spaces," " ); diffopt.output_prefix_data = &four_spaces;
diff.c +4 -1
@@ -3190,13 +3190,16 @@ static void builtin_diff(const char *name_a,memset (&xpp,0 ,sizeof (xpp));memset (&xecfg,0 ,sizeof (xecfg));memset (&ecbdata,0 ,sizeof (ecbdata)); +if (o->flags.suppress_diff_headers) + lbl[0 ] =NULL ; ecbdata.label_path = lbl; ecbdata.color_diff =want_color (o->use_color); ecbdata.ws_rule =whitespace_rule (name_b);if (ecbdata.ws_rule &WS_BLANK_AT_EOF )check_blank_at_eof (&mf1, &mf2, &ecbdata); ecbdata.opt = o; - ecbdata.header = header.len ? &header :NULL ; +if (header.len && !o->flags.suppress_diff_headers) + ecbdata.header = &header; xpp.flags = o->xdl_opts; xpp.anchors = o->anchors; xpp.anchors_nr = o->anchors_nr;
diff.h +1 -0
@@ -94,6 +94,7 @@ struct diff_flags { unsigned funccontext:1; unsigned default_follow_renames:1; unsigned stat_with_summary:1; + unsigned suppress_diff_headers:1; }; static inline void diff_flags_or(struct diff_flags *a,
Signature · 1 line
gitgitgadget
PATCH v4 9/21 range-diff: adjust the output of the commit pairs
In reply to Johannes Schindelin via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
This change brings git range-diff yet another step closer to feature parity with tbdiff: it now shows the oneline, too, and indicates with = when the commits have identical diffs.
--- range-diff.c | 64 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 55 insertions(+), 9 deletions(-)
range-diff.c +55 -9
@@ -7,6 +7,8 @@#include "xdiff-interface.h" #include "linear-assignment.h" #include "diffcore.h" +#include "commit.h" +#include "pretty.h" struct patch_util {/* For the search for an exact match */
@@ -255,9 +257,54 @@ static void get_correspondences(struct string_list *a, struct string_list *b,free (b2a); } -static const char *short_oid (struct patch_util *util) +static void output_pair_header (struct strbuf *buf, +struct strbuf *dashes, +struct patch_util *a_util, +struct patch_util *b_util) { -return find_unique_abbrev (&util->oid,DEFAULT_ABBREV ); +struct object_id *oid = a_util ? &a_util->oid : &b_util->oid; +struct commit *commit; + +if (!dashes->len) +strbuf_addchars (dashes,'-' , +strlen (find_unique_abbrev (oid, +DEFAULT_ABBREV ))); + +strbuf_reset (buf); +if (!a_util) +strbuf_addf (buf,"-: %s " , dashes->buf); +else +strbuf_addf (buf,"%d: %s " , a_util->i +1 , +find_unique_abbrev (&a_util->oid,DEFAULT_ABBREV )); + +if (!a_util) +strbuf_addch (buf,'>' ); +else if (!b_util) +strbuf_addch (buf,'<' ); +else if (strcmp (a_util->patch, b_util->patch)) +strbuf_addch (buf,'!' ); +else +strbuf_addch (buf,'=' ); + +if (!b_util) +strbuf_addf (buf," -: %s" , dashes->buf); +else +strbuf_addf (buf," %d: %s" , b_util->i +1 , +find_unique_abbrev (&b_util->oid,DEFAULT_ABBREV )); + + commit =lookup_commit_reference (oid); +if (commit) { +const char *commit_buffer =get_commit_buffer (commit,NULL ); +const char *subject; + +find_commit_subject (commit_buffer, &subject); +strbuf_addch (buf,' ' ); +format_subject (buf, subject," " ); +unuse_commit_buffer (commit, commit_buffer); + } +strbuf_addch (buf,'\n' ); + +fwrite (buf->buf, buf->len,1 , stdout); }static struct diff_filespec *get_filespec (const char *name,const char *p)
@@ -286,6 +333,7 @@ static void patch_diff(const char *a, const char *b,static void output (struct string_list *a,struct string_list *b,struct diff_options *diffopt) { +struct strbuf buf =STRBUF_INIT , dashes =STRBUF_INIT ;int i =0 , j =0 ;/*
@@ -307,25 +355,21 @@ static void output(struct string_list *a, struct string_list *b,/* Show unmatched LHS commit whose predecessors were shown. */ if (i < a->nr && a_util->matching <0 ) { -printf ("%d: %s < -: --------\n" , - i +1 ,short_oid (a_util)); +output_pair_header (&buf, &dashes, a_util,NULL ); i++;continue ; }/* Show unmatched RHS commits. */ while (j < b->nr && b_util->matching <0 ) { -printf ("-: -------- > %d: %s\n" , - j +1 ,short_oid (b_util)); +output_pair_header (&buf, &dashes,NULL , b_util); b_util = ++j < b->nr ? b->items[j].util :NULL ; }/* Show matching LHS/RHS pair. */ if (j < b->nr) { a_util = a->items[b_util->matching].util; -printf ("%d: %s ! %d: %s\n" , - b_util->matching +1 ,short_oid (a_util), - j +1 ,short_oid (b_util)); +output_pair_header (&buf, &dashes, a_util, b_util);if (!(diffopt->output_format &DIFF_FORMAT_NO_OUTPUT ))patch_diff (a->items[b_util->matching].string, b->items[j].string, diffopt);
@@ -333,6 +377,8 @@ static void output(struct string_list *a, struct string_list *b, j++; } } +strbuf_release (&buf); +strbuf_release (&dashes); } int show_range_diff(const char *range1, const char *range2,
Signature · 1 line
gitgitgadget
Re: [PATCH v4 09/21] range-diff: adjust the output of the commit pairs
Quoted 32 lines · Johannes Schindelin via GitGitGadget
From: Johannes Schindelin <[email protected]>
This change brings
git range-diffyet another step closer to feature parity with tbdiff: it now shows the oneline, too, and indicates with=when the commits have identical diffs.Signed-off-by Johannes Schindelin <[email protected]>--- range-diff.c | 64 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 55 insertions(+), 9 deletions(-)range-diff.c +5
-1@@ -7,6 +7,8 @@#include "xdiff-interface.h" #include "linear-assignment.h" #include "diffcore.h" +#include "commit.h" +#include "pretty.h" struct patch_util {/* For the search for an exact match */ @@ -255,9 +257,54 @@ static void get_correspondences(struct string_list *a, struct string_list *b,free (b2a); } -static const char *short_oid (struct patch_util *util) +static void output_pair_header(struct strbuf *buf, + struct strbuf *dashes, + struct patch_util *a_util,
range-diff.c static void get_correspondences(struct string_list *a, struct string_list *b, · line 271
Quoted 10 lines
range-diff.c +8
-1+ struct patch_util *b_util) { -return find_unique_abbrev (&util->oid,DEFAULT_ABBREV ); +struct object_id *oid = a_util ? &a_util->oid : &b_util->oid; +struct commit *commit; + +if (!dashes->len) +strbuf_addchars (dashes,'-' , +strlen (find_unique_abbrev (oid, +DEFAULT_ABBREV )));
We're doing this only once, which makes sense. What's a bit unfortunate here I guess is that if the first commit we're dealing with in the range-diff has a longer unique abbreviation, the dashes will be longer for all commits, even if all the others have a shorter abbreviation.
Tbh I don't really know what the right thing to do here is, so this is probably as good a heuristic as any. It would probably be worse to have different length dashes lines, than guessing based on the first commit.
Quoted 14 lines
range-diff.c +14
-0+ +strbuf_reset (buf); +if (!a_util) +strbuf_addf (buf,"-: %s " , dashes->buf); +else +strbuf_addf (buf,"%d: %s " , a_util->i +1 , +find_unique_abbrev (&a_util->oid,DEFAULT_ABBREV )); + +if (!a_util) +strbuf_addch (buf,'>' ); +else if (!b_util) +strbuf_addch (buf,'<' ); +else if (strcmp (a_util->patch, b_util->patch)) +strbuf_addch (buf,'!' );
range-diff.c static void get_correspondences(struct string_list *a, struct string_list *b, · line 295
Quoted 10 lines
range-diff.c +10
-0+else +strbuf_addch (buf,'=' ); + +if (!b_util) +strbuf_addf (buf," -: %s" , dashes->buf); +else +strbuf_addf (buf," %d: %s" , b_util->i +1 , +find_unique_abbrev (&b_util->oid,DEFAULT_ABBREV )); + + commit =lookup_commit_reference (oid);
This bit surprised me slightly. May be worth mentioning that we now also show the first line of the commit message here.
range-diff.c static void get_correspondences(struct string_list *a, struct string_list *b, · line 303
Quoted 8 lines
range-diff.c +8
-0+if (commit) { +const char *commit_buffer =get_commit_buffer (commit,NULL ); +const char *subject; + +find_commit_subject (commit_buffer, &subject); +strbuf_addch (buf,' ' ); +format_subject (buf, subject," " ); +unuse_commit_buffer (commit, commit_buffer);
I think the above could be written slightly shorter as
strbuf_addch(buf, ' ');
pp_commit_easy(CMIT_FMT_ONELINE, commit, &buf);Not sure if it's worth changing this at this stage of the series though, or if there is something in the above that I'm missing, that would make the shorter version not workable.
Quoted 56 lines
range-diff.c +10
-7+ } +strbuf_addch (buf,'\n' ); + +fwrite (buf->buf, buf->len,1 , stdout); }static struct diff_filespec *get_filespec (const char *name,const char *p)@@ -286,6 +333,7 @@ static void patch_diff(const char *a, const char *b,static void output (struct string_list *a,struct string_list *b,struct diff_options *diffopt) { +struct strbuf buf =STRBUF_INIT , dashes =STRBUF_INIT ;int i =0 , j =0 ;/* @@ -307,25 +355,21 @@ static void output(struct string_list *a, struct string_list *b,/* Show unmatched LHS commit whose predecessors were shown. */ if (i < a->nr && a_util->matching <0 ) { -printf ("%d: %s < -: --------\n" , - i +1 ,short_oid (a_util)); +output_pair_header (&buf, &dashes, a_util,NULL ); i++;continue ; }/* Show unmatched RHS commits. */ while (j < b->nr && b_util->matching <0 ) { -printf ("-: -------- > %d: %s\n" , - j +1 ,short_oid (b_util)); +output_pair_header (&buf, &dashes,NULL , b_util); b_util = ++j < b->nr ? b->items[j].util :NULL ; }/* Show matching LHS/RHS pair. */ if (j < b->nr) { a_util = a->items[b_util->matching].util; -printf ("%d: %s ! %d: %s\n" , - b_util->matching +1 ,short_oid (a_util), - j +1 ,short_oid (b_util)); +output_pair_header (&buf, &dashes, a_util, b_util);if (!(diffopt->output_format &DIFF_FORMAT_NO_OUTPUT ))patch_diff (a->items[b_util->matching].string, b->items[j].string, diffopt);@@ -333,6 +377,8 @@ static void output(struct string_list *a, struct string_list *b, j++; } } +strbuf_release (&buf); +strbuf_release (&dashes); } int show_range_diff(const char *range1, const char *range2,Signature · 1 line
gitgitgadget
Re: [PATCH v4 09/21] range-diff: adjust the output of the commit pairs
Hi Thomas,
Quoted 50 lines · Thomas Gummerer
Quoted 32 lines · Johannes Schindelin via GitGitGadget
From: Johannes Schindelin <[email protected]>
This change brings
git range-diffyet another step closer to feature parity with tbdiff: it now shows the oneline, too, and indicates with=when the commits have identical diffs.Signed-off-by Johannes Schindelin <[email protected]>--- range-diff.c | 64 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 55 insertions(+), 9 deletions(-)range-diff.c +5
-1@@ -7,6 +7,8 @@#include "xdiff-interface.h" #include "linear-assignment.h" #include "diffcore.h" +#include "commit.h" +#include "pretty.h" struct patch_util {/* For the search for an exact match */ @@ -255,9 +257,54 @@ static void get_correspondences(struct string_list *a, struct string_list *b,free (b2a); } -static const char *short_oid (struct patch_util *util) +static void output_pair_header(struct strbuf *buf, + struct strbuf *dashes, + struct patch_util *a_util,range-diff.c static void get_correspondences(struct string_list *a, struct string_list *b, · line 271
Quoted 10 lines
range-diff.c +8
-1+ struct patch_util *b_util) { -return find_unique_abbrev (&util->oid,DEFAULT_ABBREV ); +struct object_id *oid = a_util ? &a_util->oid : &b_util->oid; +struct commit *commit; + +if (!dashes->len) +strbuf_addchars (dashes,'-' , +strlen (find_unique_abbrev (oid, +DEFAULT_ABBREV )));We're doing this only once, which makes sense. What's a bit unfortunate here I guess is that if the first commit we're dealing with in the range-diff has a longer unique abbreviation, the dashes will be longer for all commits, even if all the others have a shorter abbreviation.
Quoted 4 lines
Tbh I don't really know what the right thing to do here is, so this is probably as good a heuristic as any. It would probably be worse to have different length dashes lines, than guessing based on the first commit.
Yes, I had the same reaction as you did. But still, I think it is the best we can do, and I don't think it is worth spending a lot of thought about ways to fix this, up and until the day when somebody experiences a real problem there (and that it will be *their* responsibility to fix it).
Quoted 19 lines
Quoted 19 lines
range-diff.c +19
-0+ +strbuf_reset (buf); +if (!a_util) +strbuf_addf (buf,"-: %s " , dashes->buf); +else +strbuf_addf (buf,"%d: %s " , a_util->i +1 , +find_unique_abbrev (&a_util->oid,DEFAULT_ABBREV )); + +if (!a_util) +strbuf_addch (buf,'>' ); +else if (!b_util) +strbuf_addch (buf,'<' ); +else if (strcmp (a_util->patch, b_util->patch)) +strbuf_addch (buf,'!' ); +else +strbuf_addch (buf,'=' ); + +if (!b_util) +strbuf_addf (buf," -: %s" , dashes->buf);
Quoted 8 lines
range-diff.c static void get_correspondences(struct string_list *a, struct string_list *b, · line 295
Quoted 5 lines
range-diff.c +5
-0+else +strbuf_addf (buf," %d: %s" , b_util->i +1 , +find_unique_abbrev (&b_util->oid,DEFAULT_ABBREV )); + + commit =lookup_commit_reference (oid);This bit surprised me slightly. May be worth mentioning that we now also show the first line of the commit message here.
Right...
Quoted 12 lines
range-diff.c static void get_correspondences(struct string_list *a, struct string_list *b, · line 303
Quoted 9 lines
range-diff.c +8
-0+if (commit) { +const char *commit_buffer =get_commit_buffer (commit,NULL ); +const char *subject; + +find_commit_subject (commit_buffer, &subject); +strbuf_addch (buf,' ' ); +format_subject (buf, subject," " ); +unuse_commit_buffer (commit, commit_buffer);I think the above could be written slightly shorter as
Quoted 2 lines
strbuf_addch(buf, ' ');
pp_commit_easy(CMIT_FMT_ONELINE, commit, &buf);I guess so. I shied away from the pretty-printing machinery because last time I tried to use it in a libified manner I had to put in a major fight to get the code into git.git. But I guess that was because of a user format (which uses global state, something I still would like to fix, but that fight just cost me too much time), which is not the case here.
Quoted 3 lines
Not sure if it's worth changing this at this stage of the series though, or if there is something in the above that I'm missing, that would make the shorter version not workable.
I think your version is not only shorter, but also possibly safer.
Ciao,
Dscho
Re: [PATCH v4 09/21] range-diff: adjust the output of the commit pairs
In reply to Johannes Schindelin via GitGitGadgetQuoted 39 lines · Johannes Schindelin via GitGitGadget
From: Johannes Schindelin <[email protected]>
This change brings
git range-diffyet another step closer to feature parity with tbdiff: it now shows the oneline, too, and indicates with=when the commits have identical diffs.Signed-off-by Johannes Schindelin <[email protected]>--- range-diff.c | 64 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 55 insertions(+), 9 deletions(-)range-diff.c +10
-2@@ -7,6 +7,8 @@#include "xdiff-interface.h" #include "linear-assignment.h" #include "diffcore.h" +#include "commit.h" +#include "pretty.h" struct patch_util {/* For the search for an exact match */ @@ -255,9 +257,54 @@ static void get_correspondences(struct string_list *a, struct string_list *b,free (b2a); } -static const char *short_oid (struct patch_util *util) +static void output_pair_header (struct strbuf *buf, +struct strbuf *dashes, +struct patch_util *a_util, +struct patch_util *b_util) { -return find_unique_abbrev (&util->oid,DEFAULT_ABBREV ); +struct object_id *oid = a_util ? &a_util->oid : &b_util->oid; +struct commit *commit; + +if (!dashes->len)
range-diff.c static void get_correspondences(struct string_list *a, struct string_list *b, · line 278
Quoted 10 lines
range-diff.c +10
-0+strbuf_addchars (dashes,'-' , +strlen (find_unique_abbrev (oid, +DEFAULT_ABBREV ))); + +strbuf_reset (buf); +if (!a_util) +strbuf_addf (buf,"-: %s " , dashes->buf); +else +strbuf_addf (buf,"%d: %s " , a_util->i +1 , +find_unique_abbrev (&a_util->oid,DEFAULT_ABBREV ));
I failed to notice this earlier, but here we are starting to use util->i, which I was wondering about earlier. Good :)
Quoted 4 lines
range-diff.c +4
-0+if (!a_util) +strbuf_addch (buf,'>' ); +else if (!b_util) +strbuf_addch (buf,'<' );
range-diff.c static void get_correspondences(struct string_list *a, struct string_list *b, · line 293
Quoted 10 lines
range-diff.c +10
-0+else if (strcmp (a_util->patch, b_util->patch)) +strbuf_addch (buf,'!' ); +else +strbuf_addch (buf,'=' ); + +if (!b_util) +strbuf_addf (buf," -: %s" , dashes->buf); +else +strbuf_addf (buf," %d: %s" , b_util->i +1 , +find_unique_abbrev (&b_util->oid,DEFAULT_ABBREV ));
And here for range b.
Quoted 2 lines
range-diff.c +1
-0+
PATCH v4 10/21 range-diff: do not show "function names" in hunk headers
In reply to Johannes Schindelin via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
We are comparing complete, formatted commit messages with patches. There are no function names here, so stop looking for them.
--- range-diff.c | 6 ++++++ 1 file changed, 6 insertions(+)
range-diff.c +6 -0
@@ -9,6 +9,7 @@#include "diffcore.h" #include "commit.h" #include "pretty.h" +#include "userdiff.h" struct patch_util {/* For the search for an exact match */
@@ -307,6 +308,10 @@ static void output_pair_header(struct strbuf *buf,fwrite (buf->buf, buf->len,1 , stdout); } +static struct userdiff_driver no_func_name = { + .funcname = {"$^" ,0 } +}; +static struct diff_filespec *get_filespec (const char *name,const char *p) {struct diff_filespec *spec =alloc_filespec (name);
@@ -316,6 +321,7 @@ static struct diff_filespec *get_filespec(const char *name, const char *p) spec->size =strlen (p); spec->should_munmap =0 ; spec->is_stdin =1 ; + spec->driver = &no_func_name;return spec; }
Signature · 1 line
gitgitgadget
Re: [PATCH v4 10/21] range-diff: do not show "function names" in hunk headers
Quoted 4 lines · Johannes Schindelin via GitGitGadget
From: Johannes Schindelin <[email protected]>
We are comparing complete, formatted commit messages with patches. There are no function names here, so stop looking for them.
While there are no function names here, trying out range-diff without this patch applied, the headers were getting here do seem kind of useful:
1: 92588fc6b6 ! 3: 43c9ef552c
@@ -8,8 +8,16 @@ diff --git a/read-cache.c b/read-cache.c
[...]The filename can be quite useful in this output. I guess this is a bit brittle though, so I'm also happy to defer changing this to show something useful to the list of possible future enhancements (obviously doesn't necessarily have to be implemented by you at that point).
Quoted 39 lines
Signed-off-by Johannes Schindelin <[email protected]>--- range-diff.c | 6 ++++++ 1 file changed, 6 insertions(+)range-diff.c +6
-0@@ -9,6 +9,7 @@#include "diffcore.h" #include "commit.h" #include "pretty.h" +#include "userdiff.h" struct patch_util {/* For the search for an exact match */ @@ -307,6 +308,10 @@ static void output_pair_header(struct strbuf *buf,fwrite (buf->buf, buf->len,1 , stdout); } +static struct userdiff_driver no_func_name = { + .funcname = {"$^" ,0 } +}; +static struct diff_filespec *get_filespec (const char *name,const char *p) {struct diff_filespec *spec =alloc_filespec (name);@@ -316,6 +321,7 @@ static struct diff_filespec *get_filespec(const char *name, const char *p) spec->size =strlen (p); spec->should_munmap =0 ; spec->is_stdin =1 ; + spec->driver = &no_func_name;return spec; }Signature · 1 line
gitgitgadget
Re: [PATCH v4 10/21] range-diff: do not show "function names" in hunk headers
Hi Thomas,
Quoted 14 lines · Thomas Gummerer
Quoted 4 lines · Johannes Schindelin via GitGitGadget
From: Johannes Schindelin <[email protected]>
We are comparing complete, formatted commit messages with patches. There are no function names here, so stop looking for them.
While there are no function names here, trying out range-diff without this patch applied, the headers were getting here do seem kind of useful:
1: 92588fc6b6 ! 3: 43c9ef552c @@ -8,8 +8,16 @@ diff --git a/read-cache.c b/read-cache.c [...]
Quoted 5 lines
The filename can be quite useful in this output. I guess this is a bit brittle though, so I'm also happy to defer changing this to show something useful to the list of possible future enhancements (obviously doesn't necessarily have to be implemented by you at that point).
To be honest, I never thought about this, I just assumed that tbdiff had good reasons to strip this.
I agree, though, that we (as in: you :-)) can look at this after range-diff lands in next.
Ciao,
Dscho
PATCH v4 11/21 range-diff: add tests
In reply to Johannes Schindelin via GitGitGadgetFrom: Thomas Rast <[email protected]>
These are essentially lifted from https://github.com/trast/tbdiff, with light touch-ups to account for the command now being names git range-diff.
Apart from renaming tbdiff to range-diff, only one test case needed to be adjusted: 11 - 'changed message'.
The underlying reason it had to be adjusted is that diff generation is sometimes ambiguous. In this case, a comment line and an empty line are added, but it is ambiguous whether they were added after the existing empty line, or whether an empty line and the comment line are added *before* the existing empty line. And apparently xdiff picks a different option here than Python's difflib.
--- t/.gitattributes | 1 + t/t3206-range-diff.sh | 145 ++++++++++ t/t3206/history.export | 604 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 750 insertions(+) create mode 100755 t/t3206-range-diff.sh create mode 100644 t/t3206/history.export
t/.gitattributes +1 -0
@@ -1,6 +1,7 @@ t[0-9][0-9][0-9][0-9]/* -whitespace /diff-lib/* eol=lf /t0110/url-* binary +/t3206/* eol=lf /t3900/*.txt eol=lf /t3901/*.txt eol=lf /t4034/*/* eol=lf
t/t3206-range-diff.sh new file +145 -0
145 lines not on this page · raw message
t/t3206/history.export new file +604 -0
604 lines not on this page · raw message
Signature · 1 line
gitgitgadget
Re: [PATCH v4 11/21] range-diff: add tests
Quoted 2 lines · Thomas Rast via GitGitGadget
These are essentially lifted from https://github.com/trast/tbdiff, with light touch-ups to account for the command now being names `git
s/names/named/
Quoted 13 lines
range-diff`.
Apart from renaming
tbdifftorange-diff, only one test case needed to be adjusted: 11 - 'changed message'.The underlying reason it had to be adjusted is that diff generation is sometimes ambiguous. In this case, a comment line and an empty line are added, but it is ambiguous whether they were added after the existing empty line, or whether an empty line and the comment line are added *before* the existing empty line. And apparently xdiff picks a different option here than Python's difflib.
Signed-off-by Johannes Schindelin <[email protected]>
Re: [PATCH v4 11/21] range-diff: add tests
Hi Eric,
Quoted 6 lines · Eric Sunshine
Quoted 2 lines · Thomas Rast via GitGitGadget
These are essentially lifted from https://github.com/trast/tbdiff, with light touch-ups to account for the command now being names `git
s/names/named/
Thanks.
I already pushed an update to https://github.com/gitgitgadget/git/pull/1.
Ciao,
Dscho
Quoted 15 lines
Quoted 14 lines
range-diff`.
Apart from renaming
tbdifftorange-diff, only one test case needed to be adjusted: 11 - 'changed message'.The underlying reason it had to be adjusted is that diff generation is sometimes ambiguous. In this case, a comment line and an empty line are added, but it is ambiguous whether they were added after the existing empty line, or whether an empty line and the comment line are added *before* the existing empty line. And apparently xdiff picks a different option here than Python's difflib.
Signed-off-by Johannes Schindelin <[email protected]>
Re: [PATCH v4 11/21] range-diff: add tests
Quoted 4 lines · Johannes Schindelin
Quoted 2 lines · Eric Sunshine
On Sat, Jul 21, 2018 at 6:05 PM Thomas Rast via GitGitGadget <[email protected]> wrote:
Quoted 8 lines
Quoted 4 lines
Quoted 2 lines
These are essentially lifted from https://github.com/trast/tbdiff, with light touch-ups to account for the command now being names `git
s/names/named/
Thanks.
I already pushed an update to https://github.com/gitgitgadget/git/pull/1.
Should I take "pushed to ... GGG" to mean "do not merge what you have to 'next' yet, as there will be an updated series (not incremental) being prepared"?
Re: [PATCH v4 11/21] range-diff: add tests
Quoted 5 lines · Junio C Hamano
Quoted 1 line
I already pushed an update to https://github.com/gitgitgadget/git/pull/1.
Should I take "pushed to ... GGG" to mean "do not merge what you have to 'next' yet, as there will be an updated series (not incremental) being prepared"?
Not speaking for Johannes, but I think that *could* work. On the other hand full rerolls are more expensive to prep (at least for me, I guess GGG might change that)
git range-diff gitgitgadget/pr/1...origin/js/range-diff shows there is
* a different header guard:
@@ -418,8 +419,8 @@
--- /dev/null
+++ b/range-diff.h
@@
-+#ifndef RANGE_DIFF_H
-+#define RANGE_DIFF_H
++#ifndef BRANCH_DIFF_H
++#define BRANCH_DIFF_H
+* another different header guard:
@@ -239,8 +240,8 @@
--- /dev/null
+++ b/linear-assignment.h
@@
-+#ifndef LINEAR_ASSIGNMENT_H
-+#define LINEAR_ASSIGNMENT_H
++#ifndef HUNGARIAN_H
++#define HUNGARIAN_H
+although this one is the other way round. Did you squash in one header guard and Johannes fixed a header guard in a different file?
With that said, I can send my series for more color testing and better diff.c code on top of either.
(https://public-inbox.org/git/[email protected]/)
Stefan
Re: [PATCH v4 11/21] range-diff: add tests
Quoted 9 lines · Stefan Beller
Quoted 5 lines · Junio C Hamano
Quoted 1 line
I already pushed an update to https://github.com/gitgitgadget/git/pull/1.
Should I take "pushed to ... GGG" to mean "do not merge what you have to 'next' yet, as there will be an updated series (not incremental) being prepared"?
Not speaking for Johannes, but I think that *could* work.
I wasn't asking if it could or could not work. I was asking for his intentions.
Not knowing it means I cannot merge the topic as-is merged to 'next' and have to wait just in case, but his not knowing my waiting would mean an update version may never come.
Re: [PATCH v4 11/21] range-diff: add tests
In reply to Thomas Rast via GitGitGadgetReview
Quoted 2 lines
(quoted diff) +2
-0+test_expect_success 'simple B...C (unmodified)' ' + git range-diff --no-color
I wonder if we want to have tests for colors, too, eventually. (Feel free to push back on it or put it into the left over hashtag. But given how much time we (or I) spent discussing colors, this would be a welcome extension for the tests)
Stefan
PATCH v4 12/21 range-diff: use color for the commit pairs
In reply to Johannes Schindelin via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
Arguably the most important part of git range-diff's output is the list of commits in the two branches, together with their relationships.
For that reason, tbdiff introduced color-coding that is pretty intuitive, especially for unchanged patches (all dim yellow, like the first line in git show's output) vs modified patches (old commit is red, new commit is green). Let's imitate that color scheme.
--- range-diff.c | 51 ++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 13 deletions(-)
range-diff.c +38 -13
@@ -258,34 +258,53 @@ static void get_correspondences(struct string_list *a, struct string_list *b,free (b2a); } -static void output_pair_header (struct strbuf *buf, +static void output_pair_header (struct diff_options *diffopt, +struct strbuf *buf,struct strbuf *dashes,struct patch_util *a_util,struct patch_util *b_util) {struct object_id *oid = a_util ? &a_util->oid : &b_util->oid;struct commit *commit; +char status; +const char *color_reset =diff_get_color_opt (diffopt,DIFF_RESET ); +const char *color_old =diff_get_color_opt (diffopt,DIFF_FILE_OLD ); +const char *color_new =diff_get_color_opt (diffopt,DIFF_FILE_NEW ); +const char *color_commit =diff_get_color_opt (diffopt,DIFF_COMMIT ); +const char *color;if (!dashes->len)strbuf_addchars (dashes,'-' ,strlen (find_unique_abbrev (oid,DEFAULT_ABBREV ))); +if (!b_util) { + color = color_old; + status ='<' ; + }else if (!a_util) { + color = color_new; + status ='>' ; + }else if (strcmp (a_util->patch, b_util->patch)) { + color = color_commit; + status ='!' ; + }else { + color = color_commit; + status ='=' ; + } +strbuf_reset (buf); +strbuf_addstr (buf, status =='!' ? color_old : color);if (!a_util)strbuf_addf (buf,"-: %s " , dashes->buf);else strbuf_addf (buf,"%d: %s " , a_util->i +1 ,find_unique_abbrev (&a_util->oid,DEFAULT_ABBREV )); -if (!a_util) -strbuf_addch (buf,'>' ); -else if (!b_util) -strbuf_addch (buf,'<' ); -else if (strcmp (a_util->patch, b_util->patch)) -strbuf_addch (buf,'!' ); -else -strbuf_addch (buf,'=' ); +if (status =='!' ) +strbuf_addf (buf,"%s%s" , color_reset, color); +strbuf_addch (buf, status); +if (status =='!' ) +strbuf_addf (buf,"%s%s" , color_reset, color_new);if (!b_util)strbuf_addf (buf," -: %s" , dashes->buf);
@@ -298,12 +317,15 @@ static void output_pair_header(struct strbuf *buf,const char *commit_buffer =get_commit_buffer (commit,NULL );const char *subject; +if (status =='!' ) +strbuf_addf (buf,"%s%s" , color_reset, color); +find_commit_subject (commit_buffer, &subject);strbuf_addch (buf,' ' );format_subject (buf, subject," " );unuse_commit_buffer (commit, commit_buffer); } -strbuf_addch (buf,'\n' ); +strbuf_addf (buf,"%s\n" , color_reset);fwrite (buf->buf, buf->len,1 , stdout); }
@@ -361,21 +383,24 @@ static void output(struct string_list *a, struct string_list *b,/* Show unmatched LHS commit whose predecessors were shown. */ if (i < a->nr && a_util->matching <0 ) { -output_pair_header (&buf, &dashes, a_util,NULL ); +output_pair_header (diffopt, + &buf, &dashes, a_util,NULL ); i++;continue ; }/* Show unmatched RHS commits. */ while (j < b->nr && b_util->matching <0 ) { -output_pair_header (&buf, &dashes,NULL , b_util); +output_pair_header (diffopt, + &buf, &dashes,NULL , b_util); b_util = ++j < b->nr ? b->items[j].util :NULL ; }/* Show matching LHS/RHS pair. */ if (j < b->nr) { a_util = a->items[b_util->matching].util; -output_pair_header (&buf, &dashes, a_util, b_util); +output_pair_header (diffopt, + &buf, &dashes, a_util, b_util);if (!(diffopt->output_format &DIFF_FORMAT_NO_OUTPUT ))patch_diff (a->items[b_util->matching].string, b->items[j].string, diffopt);
Signature · 1 line
gitgitgadget
PATCH v4 13/21 color: add the meta color GIT_COLOR_REVERSE
In reply to Johannes Schindelin via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
This "color" simply reverts background and foreground. It will be used in the upcoming "dual color" mode of git range-diff, where we will reverse colors for the -/+ markers and the fragment headers of the "outer" diff.
--- color.h | 1 + 1 file changed, 1 insertion(+)
color.h +1 -0
@@ -44,6 +44,7 @@ struct strbuf;#define GIT_COLOR_BG_CYAN "\033[46m"#define GIT_COLOR_FAINT "\033[2m"#define GIT_COLOR_FAINT_ITALIC "\033[2;3m" +#define GIT_COLOR_REVERSE "\033[7m"/* A special value meaning "no color selected" */ #define GIT_COLOR_NIL "NIL"
Signature · 1 line
gitgitgadget
PATCH v4 14/21 diff: add an internal option to dual-color diffs of diffs
In reply to Johannes Schindelin via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
When diffing diffs, it can be quite daunting to figure out what the heck is going on, as there are nested +/- signs.
Let's make this easier by adding a flag in diff_options that allows color-coding the outer diff sign with inverted colors, so that the preimage and postimage is colored like the diff it is.
Of course, this really only makes sense when the preimage and postimage *are* diffs. So let's not expose this flag via a command-line option for now.
This is a feature that was invented by git-tbdiff, and it will be used by git range-diff in the next commit, by offering it via a new option:--dual-color.
--- diff.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++----------- diff.h | 1 + 2 files changed, 69 insertions(+), 15 deletions(-)
diff.c +68 -15
@@ -563,14 +563,18 @@ static void check_blank_at_eof(mmfile_t *mf1, mmfile_t *mf2, ecbdata->blank_at_eof_in_postimage = (at - l2) +1 ; } -static void emit_line_0 (struct diff_options *o,const char *set,const char *reset, +static void emit_line_0 (struct diff_options *o, +const char *set,unsigned reverse,const char *reset,int first,const char *line,int len) {int has_trailing_newline, has_trailing_carriage_return;int nofirst;FILE *file = o->file; -fputs (diff_line_prefix (o), file); +if (first) +fputs (diff_line_prefix (o), file); +else if (!len) +return ;if (len ==0 ) { has_trailing_newline = (first =='\n' );
@@ -588,8 +592,10 @@ static void emit_line_0(struct diff_options *o, const char *set, const char *res }if (len || !nofirst) { +if (reverse &&want_color (o->use_color)) +fputs (GIT_COLOR_REVERSE , file);fputs (set, file); -if (!nofirst) +if (first && !nofirst)fputc (first, file);fwrite (line, len,1 , file);fputs (reset, file);
@@ -603,7 +609,7 @@ static void emit_line_0(struct diff_options *o, const char *set, const char *resstatic void emit_line (struct diff_options *o,const char *set,const char *reset,const char *line,int len) { -emit_line_0 (o, set, reset, line[0 ], line+1 , len-1 ); +emit_line_0 (o, set,0 , reset, line[0 ], line+1 , len-1 ); }enum diff_symbol {
@@ -963,7 +969,8 @@ static void dim_moved_lines(struct diff_options *o)static void emit_line_ws_markup (struct diff_options *o,const char *set,const char *reset, -const char *line,int len,char sign, +const char *line,int len, +const char *set_sign,char sign,unsigned ws_rule,int blank_at_eof) {const char *ws =NULL ;
@@ -974,14 +981,20 @@ static void emit_line_ws_markup(struct diff_options *o, ws =NULL ; } -if (!ws) -emit_line_0 (o, set, reset, sign, line, len); -else if (blank_at_eof) +if (!ws && !set_sign) +emit_line_0 (o, set,0 , reset, sign, line, len); +else if (!ws) { +/* Emit just the prefix, then the rest. */ +emit_line_0 (o, set_sign ? set_sign : set, !!set_sign, reset, + sign,"" ,0 ); +emit_line_0 (o, set,0 , reset,0 , line, len); + }else if (blank_at_eof)/* Blank line at EOF - paint '+' as well */ -emit_line_0 (o, ws, reset, sign, line, len); +emit_line_0 (o, ws,0 , reset, sign, line, len);else {/* Emit just the prefix, then the rest. */ -emit_line_0 (o, set, reset, sign,"" ,0 ); +emit_line_0 (o, set_sign ? set_sign : set, !!set_sign, reset, + sign,"" ,0 );ws_check_emit (line, len, ws_rule, o->file, set, reset, ws); }
@@ -991,7 +1004,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o, struct emitted_diff_symbol *eds) {static const char *nneof =" No newline at end of file\n" ; -const char *context, *reset, *set, *meta, *fraginfo; +const char *context, *reset, *set, *set_sign, *meta, *fraginfo;struct strbuf sb =STRBUF_INIT ;enum diff_symbol s = eds->s;
@@ -1004,7 +1017,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o, context =diff_get_color_opt (o,DIFF_CONTEXT ); reset =diff_get_color_opt (o,DIFF_RESET );putc ('\n' , o->file); -emit_line_0 (o, context, reset,'\\' , +emit_line_0 (o, context,0 , reset,'\\' , nneof,strlen (nneof));break ;case DIFF_SYMBOL_SUBMODULE_HEADER :
@@ -1031,7 +1044,18 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,case DIFF_SYMBOL_CONTEXT : set =diff_get_color_opt (o,DIFF_CONTEXT ); reset =diff_get_color_opt (o,DIFF_RESET ); -emit_line_ws_markup (o, set, reset, line, len,' ' , + set_sign =NULL ; +if (o->flags.dual_color_diffed_diffs) { +char c = !len ?0 : line[0 ]; + +if (c =='+' ) + set =diff_get_color_opt (o,DIFF_FILE_NEW ); +else if (c =='@' ) + set =diff_get_color_opt (o,DIFF_FRAGINFO ); +else if (c =='-' ) + set =diff_get_color_opt (o,DIFF_FILE_OLD ); + } +emit_line_ws_markup (o, set, reset, line, len, set_sign,' ' , flags & (DIFF_SYMBOL_CONTENT_WS_MASK ),0 );break ;case DIFF_SYMBOL_PLUS :
@@ -1058,7 +1082,20 @@ static void emit_diff_symbol_from_struct(struct diff_options *o, set =diff_get_color_opt (o,DIFF_FILE_NEW ); } reset =diff_get_color_opt (o,DIFF_RESET ); -emit_line_ws_markup (o, set, reset, line, len,'+' , +if (!o->flags.dual_color_diffed_diffs) + set_sign =NULL ; +else { +char c = !len ?0 : line[0 ]; + + set_sign = set; +if (c =='-' ) + set =diff_get_color_opt (o,DIFF_FILE_OLD ); +else if (c =='@' ) + set =diff_get_color_opt (o,DIFF_FRAGINFO ); +else if (c !='+' ) + set =diff_get_color_opt (o,DIFF_CONTEXT ); + } +emit_line_ws_markup (o, set, reset, line, len, set_sign,'+' , flags &DIFF_SYMBOL_CONTENT_WS_MASK , flags &DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF );break ;
@@ -1086,7 +1123,20 @@ static void emit_diff_symbol_from_struct(struct diff_options *o, set =diff_get_color_opt (o,DIFF_FILE_OLD ); } reset =diff_get_color_opt (o,DIFF_RESET ); -emit_line_ws_markup (o, set, reset, line, len,'-' , +if (!o->flags.dual_color_diffed_diffs) + set_sign =NULL ; +else { +char c = !len ?0 : line[0 ]; + + set_sign = set; +if (c =='+' ) + set =diff_get_color_opt (o,DIFF_FILE_NEW ); +else if (c =='@' ) + set =diff_get_color_opt (o,DIFF_FRAGINFO ); +else if (c !='-' ) + set =diff_get_color_opt (o,DIFF_CONTEXT ); + } +emit_line_ws_markup (o, set, reset, line, len, set_sign,'-' , flags &DIFF_SYMBOL_CONTENT_WS_MASK ,0 );break ;case DIFF_SYMBOL_WORDS_PORCELAIN :
@@ -1277,6 +1327,7 @@ static void emit_hunk_header(struct emit_callback *ecbdata,const char *frag =diff_get_color (ecbdata->color_diff,DIFF_FRAGINFO );const char *func =diff_get_color (ecbdata->color_diff,DIFF_FUNCINFO );const char *reset =diff_get_color (ecbdata->color_diff,DIFF_RESET ); +const char *reverse = ecbdata->color_diff ?GIT_COLOR_REVERSE :"" ;static const char atat[2 ] = {'@' ,'@' };const char *cp, *ep;struct strbuf msgbuf =STRBUF_INIT ;
@@ -1297,6 +1348,8 @@ static void emit_hunk_header(struct emit_callback *ecbdata, ep +=2 ;/* skip over @@ */ /* The hunk header in fraginfo color */ +if (ecbdata->opt->flags.dual_color_diffed_diffs) +strbuf_addstr (&msgbuf, reverse);strbuf_addstr (&msgbuf, frag);strbuf_add (&msgbuf, line, ep - line);strbuf_addstr (&msgbuf, reset);
diff.h +1 -0
@@ -95,6 +95,7 @@ struct diff_flags { unsigned default_follow_renames:1; unsigned stat_with_summary:1; unsigned suppress_diff_headers:1; + unsigned dual_color_diffed_diffs:1; }; static inline void diff_flags_or(struct diff_flags *a,
Signature · 1 line
gitgitgadget
Re: [PATCH v4 14/21] diff: add an internal option to dual-color diffs of diffs
"Johannes Schindelin via GitGitGadget" <[email protected]>
Quoted 35 lines · writes:
From: Johannes Schindelin <[email protected]>
When diffing diffs, it can be quite daunting to figure out what the heck is going on, as there are nested +/- signs.
Let's make this easier by adding a flag in
diff_optionsthat allows color-coding the outer diff sign with inverted colors, so that the preimage and postimage is colored like the diff it is.Of course, this really only makes sense when the preimage and postimage *are* diffs. So let's not expose this flag via a command-line option for now.
This is a feature that was invented by git-tbdiff, and it will be used by
git range-diffin the next commit, by offering it via a new option:--dual-color.Signed-off-by Johannes Schindelin <[email protected]>--- diff.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++----------- diff.h | 1 + 2 files changed, 69 insertions(+), 15 deletions(-)diff.c +2
-1@@ -563,14 +563,18 @@ static void check_blank_at_eof(mmfile_t *mf1, mmfile_t *mf2, ecbdata->blank_at_eof_in_postimage = (at - l2) +1 ; } -static void emit_line_0 (struct diff_options *o,const char *set,const char *reset, +static void emit_line_0 (struct diff_options *o, +const char *set,unsigned reverse,const char *reset,int first,const char *line,int len)
diff.c static void check_blank_at_eof(mmfile_t *mf1, mmfile_t *mf2, · line 577
Quoted 10 lines
diff.c +4
-1{int has_trailing_newline, has_trailing_carriage_return;int nofirst;FILE *file = o->file; -fputs (diff_line_prefix (o), file); +if (first) +fputs (diff_line_prefix (o), file); +else if (!len) +return ;
Can you explain this hunk in the log message? I am not sure how the description in the log message relates to this change. Is the idea of this change essentially "all the existing callers that aren't doing the diff-of-diffs send a non-NUL first character, and for them this change is a no-op. New callers share most of the remainder of emit_line_0() logic but do not want to show the prefix, so the support for it is piggy-backing by a special case where first could be NUL"?
Re: [PATCH v4 14/21] diff: add an internal option to dual-color diffs of diffs
Quoted 6 lines
diff.c static void check_blank_at_eof(mmfile_t *mf1, mmfile_t *mf2, · line 577
Quoted 5 lines
diff.c +4
-1-fputs (diff_line_prefix (o), file); +if (first) +fputs (diff_line_prefix (o), file); +else if (!len) +return ;
Quoted 8 lines
Can you explain this hunk in the log message? I am not sure how the description in the log message relates to this change. Is the idea of this change essentially "all the existing callers that aren't doing the diff-of-diffs send a non-NUL first character, and for them this change is a no-op. New callers share most of the remainder of
emit_line_0()logic but do not want to show the prefix, so the support for it is piggy-backing by a special case where first could be NUL"?
All but two caller have 'reverse' set to 0, using the arguments as before.
The other two callers are using the function twice to get the prefix and set sign going, and then the second call to get the rest of the line going (which needs to omit the prefix as that was done in the first call) :
+ /* Emit just the prefix, then the rest. */
+ emit_line_0(o, set_sign ? set_sign : set, !!set_sign, reset,
+ sign, "", 0);
+ emit_line_0(o, set, 0, reset, 0, line, len);
I attempted to clean it up on top, but likely got it wrong as we have no tests for colored range diffs, yet.
https://public-inbox.org/git/[email protected]/
My suggestion would be to first clarify emit_line_0 and have its arguments and its execution map better to each other, (and as a result only needing to have one call of emit_line_0 instead of two)
That is my understanding of the situation.
Thanks,
Stefan
PATCH v4 15/21 range-diff: offer to dual-color the diffs
In reply to Johannes Schindelin via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
When showing what changed between old and new commits, we show a diff of the patches. This diff is a diff between diffs, therefore there are nested +/- signs, and it can be relatively hard to understand what is going on.
With the --dual-color option, the preimage and the postimage are colored like the diffs they are, and the *outer* +/- sign is inverted for clarity.
--- builtin/range-diff.c | 8 ++++++++ 1 file changed, 8 insertions(+)
builtin/range-diff.c +8 -0
@@ -20,9 +20,12 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix) {int creation_factor =60 ;struct diff_options diffopt = {NULL }; +int dual_color =0 ;struct option options[] = {OPT_INTEGER (0 ,"creation-factor" , &creation_factor,N_ ("Percentage by which creation is weighted" )), +OPT_BOOL (0 ,"dual-color" , &dual_color, +N_ ("color both diff and diff-between-diffs" )),OPT_END () };int i, j, res =0 ;
@@ -52,6 +55,11 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix) argc = j;diff_setup_done (&diffopt); +if (dual_color) { + diffopt.use_color =1 ; + diffopt.flags.dual_color_diffed_diffs =1 ; + } +if (argc ==2 ) {if (!strstr (argv[0 ],".." ))die (_ ("no .. in range: '%s'" ), argv[0 ]);
Signature · 1 line
gitgitgadget
PATCH v4 16/21 range-diff --dual-color: fix bogus white-space warning
In reply to Johannes Schindelin via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
When displaying a diff of diffs, it is possible that there is an outer + before a context line. That happens when the context changed between old and new commit. When that context line starts with a tab (after the space that marks it as context line), our diff machinery spits out a white-space error (space before tab), but in this case, that is incorrect.
Fix this by adding a specific whitespace flag that simply ignores the first space in the output.
Of course, this flag is *really* specific to the "diff of diffs" use case. The original idea was to simply skip the space from the output, but that workaround was rejected by the Git maintainer as causing headaches.
Note: as the original code did not leave any space in the bit mask before the WSEH_* bits, the diff of this commit looks unnecessarily
involved: the diff is dominated by making room for one more bit to be used by the whitespace rules.
--- cache.h | 3 ++- diff.c | 15 ++++++++------- diff.h | 6 +++--- ws.c | 11 ++++++++++- 4 files changed, 23 insertions(+), 12 deletions(-)
cache.h +2 -1
@@ -1681,11 +1681,12 @@ void shift_tree_by(const struct object_id *, const struct object_id *, struct ob#define WS_CR_AT_EOL 01000#define WS_BLANK_AT_EOF 02000#define WS_TAB_IN_INDENT 04000 +#define WS_IGNORE_FIRST_SPACE 010000#define WS_TRAILING_SPACE (WS_BLANK_AT_EOL|WS_BLANK_AT_EOF)#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB|8)#define WS_TAB_WIDTH_MASK 077/* All WS_* -- when extended, adapt diff.c emit_symbol */ -#define WS_RULE_MASK 07777 +#define WS_RULE_MASK 017777extern unsigned whitespace_rule_cfg;extern unsigned whitespace_rule (const char *);extern unsigned parse_whitespace_rule (const char *);
diff.c +8 -7
@@ -650,14 +650,14 @@ enum diff_symbol { };/* * Flags for content lines: -* 0..12 are whitespace rules -* 13-15 are WSEH_NEW | WSEH_OLD | WSEH_CONTEXT -* 16 is marking if the line is blank at EOF +* 0..14 are whitespace rules +* 14-16 are WSEH_NEW | WSEH_OLD | WSEH_CONTEXT +* 17 is marking if the line is blank at EOF */ -#define DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF (1<<16) -#define DIFF_SYMBOL_MOVED_LINE (1<<17) -#define DIFF_SYMBOL_MOVED_LINE_ALT (1<<18) -#define DIFF_SYMBOL_MOVED_LINE_UNINTERESTING (1<<19) +#define DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF (1<<17) +#define DIFF_SYMBOL_MOVED_LINE (1<<18) +#define DIFF_SYMBOL_MOVED_LINE_ALT (1<<19) +#define DIFF_SYMBOL_MOVED_LINE_UNINTERESTING (1<<20)#define DIFF_SYMBOL_CONTENT_WS_MASK (WSEH_NEW | WSEH_OLD | WSEH_CONTEXT | WS_RULE_MASK)/*
@@ -1094,6 +1094,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o, set =diff_get_color_opt (o,DIFF_FRAGINFO ); else if (c != '+') set =diff_get_color_opt (o,DIFF_CONTEXT ); + flags |=WS_IGNORE_FIRST_SPACE ; } emit_line_ws_markup(o, set, reset, line, len, set_sign, '+', flags & DIFF_SYMBOL_CONTENT_WS_MASK,
diff.h +3 -3
@@ -160,9 +160,9 @@ struct diff_options {int abbrev;int ita_invisible_in_index;/* white-space error highlighting */ -#define WSEH_NEW (1<<12) -#define WSEH_CONTEXT (1<<13) -#define WSEH_OLD (1<<14) +#define WSEH_NEW (1<<13) +#define WSEH_CONTEXT (1<<14) +#define WSEH_OLD (1<<15)unsigned ws_error_highlight;const char *prefix;int prefix_length;
ws.c +10 -1
@@ -20,6 +20,7 @@ static struct whitespace_rule { { "blank-at-eol", WS_BLANK_AT_EOL, 0 }, { "blank-at-eof", WS_BLANK_AT_EOF, 0 }, { "tab-in-indent", WS_TAB_IN_INDENT, 0, 1 }, + { "ignore-first-space", WS_IGNORE_FIRST_SPACE, 0, 1 }, };unsigned parse_whitespace_rule (const char *string)
@@ -177,8 +178,16 @@ static unsigned ws_check_emit_1(const char *line, int len, unsigned ws_rule,if (trailing_whitespace ==-1 ) trailing_whitespace = len; +if ((ws_rule &WS_IGNORE_FIRST_SPACE ) && len && line[0 ] ==' ' ) { +if (stream) +fwrite (line,1 ,1 , stream); + written++; +if (!trailing_whitespace) + trailing_whitespace++; + } +/* Check indentation */ -for (i =0 ; i < trailing_whitespace; i++) { +for (i = written; i < trailing_whitespace; i++) {if (line[i] ==' ' )continue ;if (line[i] !='\t' )
Signature · 1 line
gitgitgadget
Re: [PATCH v4 16/21] range-diff --dual-color: fix bogus white-space warning
Quoted 10 lines · Johannes Schindelin via GitGitGadget
From: Johannes Schindelin <[email protected]>
When displaying a diff of diffs, it is possible that there is an outer
+before a context line. That happens when the context changed between old and new commit. When that context line starts with a tab (after the space that marks it as context line), our diff machinery spits out a white-space error (space before tab), but in this case, that is incorrect.
Quoted 2 lines
Fix this by adding a specific whitespace flag that simply ignores the first space in the output.
That sounds like a simple (not easy) solution, which sounds acceptable to me here.
I guess you dropped all ideas that I originally proposed for the cleanup regarding ws. that is fine, I can roll the cleanup on top of your patches here.
Quoted 4 lines
Of course, this flag is *really* specific to the "diff of diffs" use case. The original idea was to simply skip the space from the output, but that workaround was rejected by the Git maintainer as causing headaches.
By that you mean
https://public-inbox.org/git/[email protected]/
?
Quoted 4 lines
Note: as the original code did not leave any space in the bit mask before the WSEH_* bits, the diff of this commit looks unnecessarily
involved: the diff is dominated by making room for one more bit to be used by the whitespace rules.
It took me some minutes, but I am reasonably convinced this patch is correct (and doesn't collide with other series in flight, sb/diff-color-more adds another flag to move detection in another bit field at (1<<23))
Thanks for writing this patch instead of the other, though I'll leave it to Junio to weigh in if this approach is the best design.
Stefan
Quoted 111 lines
Signed-off-by Johannes Schindelin <[email protected]>--- cache.h | 3 ++- diff.c | 15 ++++++++------- diff.h | 6 +++--- ws.c | 11 ++++++++++- 4 files changed, 23 insertions(+), 12 deletions(-)cache.h +2
-1@@ -1681,11 +1681,12 @@ void shift_tree_by(const struct object_id *, const struct object_id *, struct ob#define WS_CR_AT_EOL 01000#define WS_BLANK_AT_EOF 02000#define WS_TAB_IN_INDENT 04000 +#define WS_IGNORE_FIRST_SPACE 010000#define WS_TRAILING_SPACE (WS_BLANK_AT_EOL|WS_BLANK_AT_EOF)#define WS_DEFAULT_RULE (WS_TRAILING_SPACE|WS_SPACE_BEFORE_TAB|8)#define WS_TAB_WIDTH_MASK 077/* All WS_* -- when extended, adapt diff.c emit_symbol */ -#define WS_RULE_MASK 07777 +#define WS_RULE_MASK 017777extern unsigned whitespace_rule_cfg;extern unsigned whitespace_rule (const char *);extern unsigned parse_whitespace_rule (const char *);diff.c +8
-7@@ -650,14 +650,14 @@ enum diff_symbol { };/* * Flags for content lines: -* 0..12 are whitespace rules -* 13-15 are WSEH_NEW | WSEH_OLD | WSEH_CONTEXT -* 16 is marking if the line is blank at EOF +* 0..14 are whitespace rules +* 14-16 are WSEH_NEW | WSEH_OLD | WSEH_CONTEXT +* 17 is marking if the line is blank at EOF */ -#define DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF (1<<16) -#define DIFF_SYMBOL_MOVED_LINE (1<<17) -#define DIFF_SYMBOL_MOVED_LINE_ALT (1<<18) -#define DIFF_SYMBOL_MOVED_LINE_UNINTERESTING (1<<19) +#define DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF (1<<17) +#define DIFF_SYMBOL_MOVED_LINE (1<<18) +#define DIFF_SYMBOL_MOVED_LINE_ALT (1<<19) +#define DIFF_SYMBOL_MOVED_LINE_UNINTERESTING (1<<20)#define DIFF_SYMBOL_CONTENT_WS_MASK (WSEH_NEW | WSEH_OLD | WSEH_CONTEXT | WS_RULE_MASK)/* @@ -1094,6 +1094,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o, set =diff_get_color_opt (o,DIFF_FRAGINFO ); else if (c != '+') set =diff_get_color_opt (o,DIFF_CONTEXT ); + flags |=WS_IGNORE_FIRST_SPACE ; } emit_line_ws_markup(o, set, reset, line, len, set_sign, '+', flags & DIFF_SYMBOL_CONTENT_WS_MASK,diff.h +3
-3@@ -160,9 +160,9 @@ struct diff_options {int abbrev;int ita_invisible_in_index;/* white-space error highlighting */ -#define WSEH_NEW (1<<12) -#define WSEH_CONTEXT (1<<13) -#define WSEH_OLD (1<<14) +#define WSEH_NEW (1<<13) +#define WSEH_CONTEXT (1<<14) +#define WSEH_OLD (1<<15)unsigned ws_error_highlight;const char *prefix;int prefix_length;ws.c +10
-2@@ -20,6 +20,7 @@ static struct whitespace_rule { { "blank-at-eol", WS_BLANK_AT_EOL, 0 }, { "blank-at-eof", WS_BLANK_AT_EOF, 0 }, { "tab-in-indent", WS_TAB_IN_INDENT, 0, 1 }, + { "ignore-first-space", WS_IGNORE_FIRST_SPACE, 0, 1 }, };unsigned parse_whitespace_rule (const char *string)@@ -177,8 +178,16 @@ static unsigned ws_check_emit_1(const char *line, int len, unsigned ws_rule,if (trailing_whitespace ==-1 ) trailing_whitespace = len; +if ((ws_rule &WS_IGNORE_FIRST_SPACE ) && len && line[0 ] ==' ' ) { +if (stream) +fwrite (line,1 ,1 , stream); + written++; +if (!trailing_whitespace) + trailing_whitespace++; + } +/* Check indentation */ -for (i =0 ; i < trailing_whitespace; i++) { +for (i = written; i < trailing_whitespace; i++) {if (line[i] ==' ' )continue ;if (line[i] !='\t' ) --gitgitgadget
Re: [PATCH v4 16/21] range-diff --dual-color: fix bogus white-space warning
Hi Stefan,
Quoted 15 lines · Stefan Beller
Quoted 10 lines · Johannes Schindelin via GitGitGadget
From: Johannes Schindelin <[email protected]>
When displaying a diff of diffs, it is possible that there is an outer
+before a context line. That happens when the context changed between old and new commit. When that context line starts with a tab (after the space that marks it as context line), our diff machinery spits out a white-space error (space before tab), but in this case, that is incorrect.Quoted 2 lines
Fix this by adding a specific whitespace flag that simply ignores the first space in the output.
Quoted 6 lines
That sounds like a simple (not easy) solution, which sounds acceptable to me here.
I guess you dropped all ideas that I originally proposed for the cleanup regarding ws. that is fine, I can roll the cleanup on top of your patches here.
Yes, sorry, I got the impression after our chat on IRC that you tried to address something different from what I needed, anyway?
Quoted 11 lines
Quoted 4 lines
Note: as the original code did not leave any space in the bit mask before the WSEH_* bits, the diff of this commit looks unnecessarily
involved: the diff is dominated by making room for one more bit to be used by the whitespace rules.It took me some minutes, but I am reasonably convinced this patch is correct (and doesn't collide with other series in flight, sb/diff-color-more adds another flag to move detection in another bit field at (1<<23))
Thanks for writing this patch instead of the other, though I'll leave it to Junio to weigh in if this approach is the best design.
I am sorry that your time was wasted in addition to mine: I will go with a simple one-line patch in v5 instead, a single line that simply disables white-space errors altogether in dual color mode.
Ciao,
Dscho
Re: [PATCH v4 16/21] range-diff --dual-color: fix bogus white-space warning
In reply to Johannes Schindelin via GitGitGadget"Johannes Schindelin via GitGitGadget" <[email protected]>
Quoted 5 lines · writes:
ws.c +1
-0@@ -177,8 +178,16 @@ static unsigned ws_check_emit_1(const char *line, int len, unsigned ws_rule,if (trailing_whitespace ==-1 ) trailing_whitespace = len; +if ((ws_rule &WS_IGNORE_FIRST_SPACE ) && len && line[0 ] ==' ' ) {
ws.c static unsigned ws_check_emit_1(const char *line, int len, unsigned ws_rule, · line 190
Quoted 10 lines
ws.c +8
-1+if (stream) +fwrite (line,1 ,1 , stream); + written++; +if (!trailing_whitespace) + trailing_whitespace++; + } +/* Check indentation */ -for (i =0 ; i < trailing_whitespace; i++) { +for (i = written; i < trailing_whitespace; i++) {
It is pleasing to see that with a surprisingly clean and small change like this we can exempt the initial space byte from SP-before-HT check and from Indent-with-non-tab at the same time.
Very nice.
One reason why a surprisingly small special case is required is perhaps because we are blessed with the original code being clean [*1*], and the fact that a line[0] that is not ' ' will not trigger any indentation related whitespace errors without this special case, I guess.
ws.c static unsigned ws_check_emit_1(const char *line, int len, unsigned ws_rule, · line 193
Quoted 3 lines
ws.c +0
-0if (line[i] ==' ' )continue ;if (line[i] !='\t' )
[Footnote]
*1* ws.c used to be almost all my code long time ago, but most of
the shape of the current whitespace_error checking code comes from c1795bb08aa which is not mine, and I can say good things about it without feeling embarrassingly boasty ;-)
Re: [PATCH v4 16/21] range-diff --dual-color: fix bogus white-space warning
Quoted 11 lines · Junio C Hamano
It is pleasing to see that with a surprisingly clean and small change like this we can exempt the initial space byte from SP-before-HT check and from Indent-with-non-tab at the same time.
Very nice.
One reason why a surprisingly small special case is required is perhaps because we are blessed with the original code being clean [*1*], and the fact that a line[0] that is not ' ' will not trigger any indentation related whitespace errors without this special case, I guess.
Having said good things about the patch, I unfortunately realized that we weren't that lucky. As we do want to see whitespace errors on lines with line[0] != ' ' to be flagged. So "... will not trigger" in the above is not a blessing, but something that further needs to be fixed. The special case should also be made for line[0] that is '+' and possibly '-' (and I also suspect that the changes in this patch may mostly be reusable with little tweaks if any).
Imagine we start from this commit that "git show" shows us like so:
int main(int ac, char **av)
{
________ printf("Hello");
+________ putchar(',');
+ putchar(' ');
printf("World\n");
return 0;
}I've drawn a horizontal-tab as long underscore to clarify in the above picture. If you have "core.whitespace=indent-with-non-tab" "git show" would paint the line that adds " " as violating (as it types 10 SPs, when it could have been a tab and 2 SPs), but it does not highlight the line with "World" or "return", which is sensible as they are pre-existing violations.
Then imagine we did "git commit --amend" and "git show" would give this instead:
int main(int ac, char **av)
{
________ printf("Hello");
+ putchar(',');
+________ putchar(' ');
printf("World\n");
return 0;
}That is, relative to the previous attempt, we stopped introducing new indent-with-non-tab violation to the line that adds " ", but added a new violation to the line that adds ",".
After such "git commit --amend", what do we want to see in the output from "git range-diff @{1}..."?
My quick test of the current code does not show any whitespace breakage for either versions. I *think* what we want to actually see is
- just like WS_IGNORE_FIRST_SPACE logic shifted the column by incrementing written (and final condition) for the loop in your patch for line[0]==' ', detect the overlong run of SP correctly by ignoring the first column that is '+', and complaining that the new commit is typing 10 SPs before putchar(',').
- Earlier I thought that lines with '-' in the outer diff should become exempt from whitespace-error highlighting, but I think that is a mistake. If a line in diff-of-diff that begins with "-+" has whitespace violation (e.g "-+" followed by 10 SPs), that is "the old version of the patch used to introduce whitespace violation", which is a useful piece of information when we look at the corresponding line in the same diff-of-diff that begins with "++". We could either say "and you cleaned that mistake up in your newer patch", or "and you still have that mistake in your newer patch" (another possibility is there is no whitespace error on a "-+" line, and the corresponding "++" line has one---"you got it right in the previous round, but you somehow made it worse").
Here is the reproduction of the sample data I based on the above thought experiment on.
script.sh new file +48 -0
@@ -0,0 +1,48 @@ +#!/bin/sh + +git init +git config core.whitespace indent-with-non-tab + +tr _ '\011' <<\EOF >hello.c +int main(int ac, char **av) +{ +_ printf("Hello"); + printf("World\n"); + return 0; +} +EOF + +git add hello.c && git commit -m initial + +tr _ '\011' <<\EOF >hello.c +int main(int ac, char **av) +{ +_ printf("Hello"); + putchar(','); +_ putchar(' '); + printf("World\n"); + return 0; +} +EOF + +git commit -a -m second + +tr _ '\011' <<\EOF >hello.c +int main(int ac, char **av) +{ +_ printf("Hello"); +_ putchar(','); + putchar(' '); + printf("World\n"); + return 0; +} +EOF + +git commit -a --amend -m third + + +git show @{1} +git show HEAD + +git range-diff ..@{1} @{1}.. +
Signature · 1 line
2.18.0-232-gb7bd9486b0
PATCH v4 17/21 range-diff: populate the man page
In reply to Johannes Schindelin via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
The bulk of this patch consists of a heavily butchered version of tbdiff's README written by Thomas Rast and Thomas Gummerer, lifted from
https://github.com/trast/tbdiff.
--- Documentation/git-range-diff.txt | 229 +++++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+)
Documentation/git-range-diff.txt +229 -0
@@ -5,6 +5,235 @@ NAME ---- git-range-diff - Compare two commit ranges (e.g. two versions of a branch) +SYNOPSIS +-------- +[verse] +'git range-diff' [--color=[<when>]] [--no-color] [<diff-options>] + [--dual-color] [--creation-factor=<factor>] + ( <range1> <range2> | <rev1>...<rev2> | <base> <rev1> <rev2> ) + +DESCRIPTION +----------- + +This command shows the differences between two versions of a patch +series, or more generally, two commit ranges (ignoring merge commits). + +To that end, it first finds pairs of commits from both commit ranges +that correspond with each other. Two commits are said to correspond when +the diff between their patches (i.e. the author information, the commit +message and the commit diff) is reasonably small compared to the +patches' size. See ``Algorithm`` below for details. + +Finally, the list of matching commits is shown in the order of the +second commit range, with unmatched commits being inserted just after +all of their ancestors have been shown. + + +OPTIONS +------- +--dual-color:: + When the commit diffs differ, recreate the original diffs' + coloring, and add outer -/+ diff markers with the *background* + being red/green to make it easier to see e.g. when there was a + change in what exact lines were added. + +--creation-factor=<percent>:: + Set the creation/deletion cost fudge factor to `<percent>`. + Defaults to 60. Try a larger value if `git range-diff` erroneously + considers a large change a total rewrite (deletion of one commit + and addition of another), and a smaller one in the reverse case. + See the ``Algorithm`` section below for an explanation why this is + needed. + +<range1> <range2>:: + Compare the commits specified by the two ranges, where + `<range1>` is considered an older version of `<range2>`. + +<rev1>...<rev2>:: + Equivalent to passing `<rev2>..<rev1>` and `<rev1>..<rev2>`. + +<base> <rev1> <rev2>:: + Equivalent to passing `<base>..<rev1>` and `<base>..<rev2>`. + Note that `<base>` does not need to be the exact branch point + of the branches. Example: after rebasing a branch `my-topic`, + `git range-diff my-topic@{u} my-topic@{1} my-topic` would + show the differences introduced by the rebase. + +`git range-diff` also accepts the regular diff options (see +linkgit:git-diff[1]), most notably the `--color=[<when>]` and +`--no-color` options. These options are used when generating the "diff +between patches", i.e. to compare the author, commit message and diff of +corresponding old/new commits. There is currently no means to tweak the +diff options passed to `git log` when generating those patches. + + +CONFIGURATION +------------- +This command uses the `diff.color.*` and `pager.range-diff` settings +(the latter is on by default). +See linkgit:git-config[1]. + + +EXAMPLES +-------- + +When a rebase required merge conflicts to be resolved, compare the changes +introduced by the rebase directly afterwards using: + +------------ +$ git range-diff @{u} @{1} @ +------------ + + +A typical output of `git range-diff` would look like this: + +------------ +-: ------- > 1: 0ddba11 Prepare for the inevitable! +1: c0debee = 2: cab005e Add a helpful message at the start +2: f00dbal ! 3: decafe1 Describe a bug + @@ -1,3 +1,3 @@ + Author: A U Thor <[email protected]> + + -TODO: Describe a bug + +Describe a bug + @@ -324,5 +324,6 + This is expected. + + -+What is unexpected is that it will also crash. + ++Unexpectedly, it also crashes. This is a bug, and the jury is + ++still out there how to fix it best. See ticket #314 for details. + + Contact +3: bedead < -: ------- TO-UNDO +------------ + +In this example, there are 3 old and 3 new commits, where the developer +removed the 3rd, added a new one before the first two, and modified the +commit message of the 2nd commit as well its diff. + +When the output goes to a terminal, it is color-coded by default, just +like regular `git diff`'s output. In addition, the first line (adding a +commit) is green, the last line (deleting a commit) is red, the second +line (with a perfect match) is yellow like the commit header of `git +show`'s output, and the third line colors the old commit red, the new +one green and the rest like `git show`'s commit header. + +The color-coded diff is actually a bit hard to read, though, as it +colors the entire lines red or green. The line that added "What is +unexpected" in the old commit, for example, is completely red, even if +the intent of the old commit was to add something. + +To help with that, use the `--dual-color` mode. In this mode, the diff +of diffs will retain the original diff colors, and prefix the lines with +-/+ markers that have their *background* red or green, to make it more +obvious that they describe how the diff itself changed. + + +Algorithm +--------- + +The general idea is this: we generate a cost matrix between the commits +in both commit ranges, then solve the least-cost assignment. + +The cost matrix is populated thusly: for each pair of commits, both +diffs are generated and the "diff of diffs" is generated, with 3 context +lines, then the number of lines in that diff is used as cost. + +To avoid false positives (e.g. when a patch has been removed, and an +unrelated patch has been added between two iterations of the same patch +series), the cost matrix is extended to allow for that, by adding +fixed-cost entries for wholesale deletes/adds. + +Example: Let commits `1--2` be the first iteration of a patch series and +`A--C` the second iteration. Let's assume that `A` is a cherry-pick of +`2,` and `C` is a cherry-pick of `1` but with a small modification (say, +a fixed typo). Visualize the commits as a bipartite graph: + +------------ + 1 A + + 2 B + + C +------------ + +We are looking for a "best" explanation of the new series in terms of +the old one. We can represent an "explanation" as an edge in the graph: + + +------------ + 1 A + / + 2 --------' B + + C +------------ + +This explanation comes for "free" because there was no change. Similarly +`C` could be explained using `1`, but that comes at some cost c>0 +because of the modification: + +------------ + 1 ----. A + | / + 2 ----+---' B + | + `----- C + c>0 +------------ + +In mathematical terms, what we are looking for is some sort of a minimum +cost bipartite matching; `1` is matched to `C` at some cost, etc. The +underlying graph is in fact a complete bipartite graph; the cost we +associate with every edge is the size of the diff between the two +commits' patches. To explain also new commits, we introduce dummy nodes +on both sides: + +------------ + 1 ----. A + | / + 2 ----+---' B + | + o `----- C + c>0 + o o + + o o +------------ + +The cost of an edge `o--C` is the size of `C`'s diff, modified by a +fudge factor that should be smaller than 100%. The cost of an edge +`o--o` is free. The fudge factor is necessary because even if `1` and +`C` have nothing in common, they may still share a few empty lines and +such, possibly making the assignment `1--C`, `o--o` slightly cheaper +than `1--o`, `o--C` even if `1` and `C` have nothing in common. With the +fudge factor we require a much larger common part to consider patches as +corresponding. + +The overall time needed to compute this algorithm is the time needed to +compute n+m commit diffs and then n*m diffs of patches, plus the time +needed to compute the least-cost assigment between n and m diffs. Git +uses an implementation of the Jonker-Volgenant algorithm to solve the +assignment problem, which has cubic runtime complexity. The matching +found in this case will look like this: + +------------ + 1 ----. A + | / + 2 ----+---' B + .--+-----' + o -' `----- C + c>0 + o ---------- o + + o ---------- o +------------ + + +SEE ALSO +-------- +linkgit:git-log[1] + GIT --- Part of the linkgit:git[1] suite
Signature · 1 line
gitgitgadget
Re: [PATCH v4 17/21] range-diff: populate the man page
Quoted 4 lines · Johannes Schindelin via GitGitGadget
From: Johannes Schindelin <[email protected]>
The bulk of this patch consists of a heavily butchered version of tbdiff's README written by Thomas Rast and Thomas Gummerer, lifted from
Thanks for the mention here, but this was really mostly Thomas Rast's writing. My contributions here are very minor compared to his :)
Quoted 9 lines
https://github.com/trast/tbdiff.
Signed-off-by Johannes Schindelin <[email protected]>--- Documentation/git-range-diff.txt | 229 +++++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+)[...]
Documentation/git-range-diff.txt NAME · line 74
Quoted 5 lines
Documentation/git-range-diff.txt +5
-0+CONFIGURATION +------------- +This command uses the `diff.color.*` and `pager.range-diff` settings +(the latter is on by default). +See linkgit:git-config[1].
Would it be worth implementing a rangeDiff.dualColor configuration at some point? Dual color mode seems like something I would like to have on by default, even if we are not making it the default for the command itself.
(Again this is something that can be a future enhancement).
Quoted 6 lines
+EXAMPLES
+--------
[...]Signature · 1 line
gitgitgadget
Re: [PATCH v4 17/21] range-diff: populate the man page
Hi Thomas,
Quoted 19 lines · Thomas Gummerer
Documentation/git-range-diff.txt NAME · line 74
Quoted 12 lines · Johannes Schindelin via GitGitGadget
From: Johannes Schindelin <[email protected]>
Documentation/git-range-diff.txt | 229 +++++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+)[...]
Documentation/git-range-diff.txt +5
-0+CONFIGURATION +------------- +This command uses the `diff.color.*` and `pager.range-diff` settings +(the latter is on by default). +See linkgit:git-config[1].Would it be worth implementing a
rangeDiff.dualColorconfiguration at some point? Dual color mode seems like something I would like to have on by default, even if we are not making it the default for the command itself.
Quoted 1 line
(Again this is something that can be a future enhancement).
Sure, go wild!
Ciao,
Dscho
PATCH v4 18/21 completion: support `git range-diff`
In reply to Johannes Schindelin via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
Tab completion of git range-diff is very convenient, especially given that the revision arguments to specify the commit ranges to compare are typically more complex than, say, what is normally passed to git log.
--- contrib/completion/git-completion.bash | 14 ++++++++++++++ 1 file changed, 14 insertions(+)
contrib/completion/git-completion.bash +14 -0
@@ -1976,6 +1976,20 @@ _git_push () __git_complete_remote_or_refspec } +_git_range_diff () +{ + case "$cur" in + --*) + __gitcomp " + --creation-factor= --dual-color + $__git_diff_common_options + " + return + ;; + esac + __git_complete_revlist +} + _git_rebase () { __git_find_repo_path
Signature · 1 line
gitgitgadget
Re: [PATCH v4 18/21] completion: support `git range-diff`
Quoted 9 lines · Johannes Schindelin via GitGitGadget
Tab completion of
git range-diffis very convenient, especially given that the revision arguments to specify the commit ranges to compare are typically more complex than, say, what is normally passed togit log.Signed-off-by Johannes Schindelin <[email protected]>---
contrib/completion/git-completion.bash _git_push () · line 1983
Quoted 8 lines
contrib/completion/git-completion.bash +8
-0@@ -1976,6 +1976,20 @@ _git_push () +_git_range_diff () +{ + case "$cur" in + --*) + __gitcomp " + --creation-factor= --dual-color + $__git_diff_common_options + "
This is indented with a mix of spaces and tabs.
Applying: completion: support git range-diff .git/rebase-apply/patch:18: space before tab in indent.
--creation-factor= --dual-color
warning: 1 line adds whitespace errors.
Applying: range-diff: make --dual-color the default mode .git/rebase-apply/patch:105: space before tab in indent.
--creation-factor= --no-dual-color
warning: 1 line adds whitespace errors.
Other parts of this script seem to use tabs for indentation.
Re: [PATCH v4 18/21] completion: support `git range-diff`
Hi Eric,
Quoted 31 lines · Eric Sunshine
Quoted 9 lines · Johannes Schindelin via GitGitGadget
Tab completion of
git range-diffis very convenient, especially given that the revision arguments to specify the commit ranges to compare are typically more complex than, say, what is normally passed togit log.Signed-off-by Johannes Schindelin <[email protected]>---
contrib/completion/git-completion.bash _git_push () · line 1983
Quoted 8 lines
contrib/completion/git-completion.bash +8
-0@@ -1976,6 +1976,20 @@ _git_push () +_git_range_diff () +{ + case "$cur" in + --*) + __gitcomp " + --creation-factor= --dual-color + $__git_diff_common_options + "This is indented with a mix of spaces and tabs.
Applying: completion: support
git range-diff.git/rebase-apply/patch:18: space before tab in indent.
--creation-factor= --dual-color
warning: 1 line adds whitespace errors.
Applying: range-diff: make --dual-color the default mode .git/rebase-apply/patch:105: space before tab in indent.
--creation-factor= --no-dual-color
warning: 1 line adds whitespace errors.
Quoted 1 line
Other parts of this script seem to use tabs for indentation.
Thanks.
I guess that this is due to my playing with VS Code and failing to adjust indentation rules of anything but C code...
Will be fixed in v5.
Ciao,
Dscho
PATCH v4 19/21 range-diff: left-pad patch numbers
In reply to Johannes Schindelin via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
As pointed out by Elijah Newren, tbdiff has this neat little alignment trick where it outputs the commit pairs with patch numbers that are padded to the maximal patch number's width:
1: cafedead = 1: acefade first patch [...] 314: beefeada < 314: facecab up to PI!
Let's do the same in range-diff, too.
--- range-diff.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-)
range-diff.c +9 -7
@@ -259,6 +259,7 @@ static void get_correspondences(struct string_list *a, struct string_list *b, } static void output_pair_header(struct diff_options *diffopt, + int patch_no_width, struct strbuf *buf, struct strbuf *dashes, struct patch_util *a_util,
@@ -295,9 +296,9 @@ static void output_pair_header(struct diff_options *diffopt,strbuf_reset (buf);strbuf_addstr (buf, status =='!' ? color_old : color);if (!a_util) -strbuf_addf (buf,"-: %s " , dashes->buf); +strbuf_addf (buf,"%*s: %s " , patch_no_width,"-" , dashes->buf);else -strbuf_addf (buf,"%d: %s " , a_util->i +1 , +strbuf_addf (buf,"%*d: %s " , patch_no_width, a_util->i +1 ,find_unique_abbrev (&a_util->oid,DEFAULT_ABBREV ));if (status =='!' )
@@ -307,9 +308,9 @@ static void output_pair_header(struct diff_options *diffopt,strbuf_addf (buf,"%s%s" , color_reset, color_new);if (!b_util) -strbuf_addf (buf," -: %s" , dashes->buf); +strbuf_addf (buf," %*s: %s" , patch_no_width,"-" , dashes->buf);else -strbuf_addf (buf," %d: %s" , b_util->i +1 , +strbuf_addf (buf," %*d: %s" , patch_no_width, b_util->i +1 ,find_unique_abbrev (&b_util->oid,DEFAULT_ABBREV )); commit =lookup_commit_reference (oid);
@@ -362,6 +363,7 @@ static void output(struct string_list *a, struct string_list *b, struct diff_options *diffopt) {struct strbuf buf =STRBUF_INIT , dashes =STRBUF_INIT ; +int patch_no_width =decimal_width (1 + (a->nr > b->nr ? a->nr : b->nr));int i =0 , j =0 ;/*
@@ -383,7 +385,7 @@ static void output(struct string_list *a, struct string_list *b,/* Show unmatched LHS commit whose predecessors were shown. */ if (i < a->nr && a_util->matching <0 ) { -output_pair_header (diffopt, +output_pair_header (diffopt, patch_no_width, &buf, &dashes, a_util,NULL ); i++;continue ;
@@ -391,7 +393,7 @@ static void output(struct string_list *a, struct string_list *b,/* Show unmatched RHS commits. */ while (j < b->nr && b_util->matching <0 ) { -output_pair_header (diffopt, +output_pair_header (diffopt, patch_no_width, &buf, &dashes,NULL , b_util); b_util = ++j < b->nr ? b->items[j].util :NULL ; }
@@ -399,7 +401,7 @@ static void output(struct string_list *a, struct string_list *b,/* Show matching LHS/RHS pair. */ if (j < b->nr) { a_util = a->items[b_util->matching].util; -output_pair_header (diffopt, +output_pair_header (diffopt, patch_no_width, &buf, &dashes, a_util, b_util);if (!(diffopt->output_format &DIFF_FORMAT_NO_OUTPUT )) patch_diff(a->items[b_util->matching].string,
Signature · 1 line
gitgitgadget
PATCH v4 20/21 range-diff: make --dual-color the default mode
In reply to Johannes Schindelin via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
After using this command extensively for the last two months, this developer came to the conclusion that even if the dual color mode still leaves a lot of room for confusion about what was actually changed, the non-dual color mode is substantially worse in that regard.
Therefore, we really want to make the dual color mode the default.
--- Documentation/git-range-diff.txt | 32 +++++++++++++++----------- builtin/range-diff.c | 10 ++++---- contrib/completion/git-completion.bash | 2 +- 3 files changed, 25 insertions(+), 19 deletions(-)
Documentation/git-range-diff.txt +18 -14
@@ -9,7 +9,7 @@ SYNOPSIS -------- [verse] 'git range-diff' [--color=[<when>]] [--no-color] [<diff-options>] - [--dual-color] [--creation-factor=<factor>] + [--no-dual-color] [--creation-factor=<factor>] ( <range1> <range2> | <rev1>...<rev2> | <base> <rev1> <rev2> ) DESCRIPTION
@@ -31,11 +31,14 @@ all of their ancestors have been shown. OPTIONS ------- ---dual-color:: - When the commit diffs differ, recreate the original diffs' - coloring, and add outer -/+ diff markers with the *background* - being red/green to make it easier to see e.g. when there was a - change in what exact lines were added. +--no-dual-color:: + When the commit diffs differ, `git range-diff` recreates the + original diffs' coloring, and adds outer -/+ diff markers with + the *background* being red/green to make it easier to see e.g. + when there was a change in what exact lines were added. This is + known to `range-diff` as "dual coloring". Use `--no-dual-color` + to revert to color all lines according to the outer diff markers + (and completely ignore the inner diff when it comes to color). --creation-factor=<percent>:: Set the creation/deletion cost fudge factor to `<percent>`.
@@ -118,15 +121,16 @@ line (with a perfect match) is yellow like the commit header of `git show`'s output, and the third line colors the old commit red, the new one green and the rest like `git show`'s commit header. -The color-coded diff is actually a bit hard to read, though, as it -colors the entire lines red or green. The line that added "What is -unexpected" in the old commit, for example, is completely red, even if -the intent of the old commit was to add something. +A naive color-coded diff of diffs is actually a bit hard to read, +though, as it colors the entire lines red or green. The line that added +"What is unexpected" in the old commit, for example, is completely red, +even if the intent of the old commit was to add something. -To help with that, use the `--dual-color` mode. In this mode, the diff -of diffs will retain the original diff colors, and prefix the lines with --/+ markers that have their *background* red or green, to make it more -obvious that they describe how the diff itself changed. +To help with that, `range` uses the `--dual-color` mode by default. In +this mode, the diff of diffs will retain the original diff colors, and +prefix the lines with -/+ markers that have their *background* red or +green, to make it more obvious that they describe how the diff itself +changed. Algorithm
builtin/range-diff.c +6 -4
@@ -20,11 +20,11 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix) {int creation_factor =60 ;struct diff_options diffopt = {NULL }; -int dual_color =0 ; +int simple_color =-1 ;struct option options[] = {OPT_INTEGER (0 ,"creation-factor" , &creation_factor,N_ ("Percentage by which creation is weighted" )), -OPT_BOOL (0 ,"dual-color" , &dual_color, +OPT_BOOL (0 ,"no-dual-color" , &simple_color,N_ ("color both diff and diff-between-diffs" )),OPT_END () };
@@ -55,8 +55,10 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix) argc = j;diff_setup_done (&diffopt); -if (dual_color) { - diffopt.use_color =1 ; +if (simple_color <1 ) { +if (!simple_color) +/* force color when --dual-color was used */ + diffopt.use_color =1 ; diffopt.flags.dual_color_diffed_diffs =1 ; }
contrib/completion/git-completion.bash +1 -1
@@ -1981,7 +1981,7 @@ _git_range_diff () case "$cur" in --*) __gitcomp " - --creation-factor= --dual-color + --creation-factor= --no-dual-color $__git_diff_common_options " return
Signature · 1 line
gitgitgadget
Re: [PATCH v4 20/21] range-diff: make --dual-color the default mode
Quoted 8 lines · Johannes Schindelin via GitGitGadget
From: Johannes Schindelin <[email protected]>
After using this command extensively for the last two months, this developer came to the conclusion that even if the dual color mode still leaves a lot of room for confusion about what was actually changed, the non-dual color mode is substantially worse in that regard.
Therefore, we really want to make the dual color mode the default.
Ah and here we're making it default, so I wouldn't need a rangeDiff.dualColor config variable anymore. Even better!
Quoted 8 lines
Signed-off-by Johannes Schindelin <[email protected]>--- Documentation/git-range-diff.txt | 32 +++++++++++++++----------- builtin/range-diff.c | 10 ++++---- contrib/completion/git-completion.bash | 2 +- 3 files changed, 25 insertions(+), 19 deletions(-)[...]
Re: [PATCH v4 20/21] range-diff: make --dual-color the default mode
Hi Thomas,
Quoted 8 lines · Thomas Gummerer
Quoted 7 lines · Johannes Schindelin via GitGitGadget
From: Johannes Schindelin <[email protected]>
After using this command extensively for the last two months, this developer came to the conclusion that even if the dual color mode still leaves a lot of room for confusion about what was actually changed, the non-dual color mode is substantially worse in that regard.
Quoted 4 lines
Quoted 1 line
Therefore, we really want to make the dual color mode the default.
Ah and here we're making it default, so I wouldn't need a
rangeDiff.dualColorconfig variable anymore. Even better!
... except if you want to switch it off ;-)
Ciao,
Dscho
PATCH v4 21/21 range-diff: use dim/bold cues to improve dual color mode
In reply to Johannes Schindelin via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
It *is* a confusing thing to look at a diff of diffs. All too easy is it to mix up whether the -/+ markers refer to the "inner" or the "outer" diff, i.e. whether a + indicates that a line was added by either the old or the new diff (or both), or whether the new diff does something different than the old diff.
To make things easier to process for normal developers, we introduced the dual color mode which colors the lines according to the commit diff, i.e. lines that are added by a commit (whether old, new, or both) are colored in green. In non-dual color mode, the lines would be colored according to the outer diff: if the old commit added a line, it would be colored red (because that line addition is only present in the first commit range that was specified on the command-line, i.e. the "old" commit, but not in the second commit range, i.e. the "new" commit).
However, this dual color mode is still not making things clear enough, as we are looking at two levels of diffs, and we still only pick a color according to *one* of them (the outer diff marker is colored differently, of course, but in particular with deep indentation, it is easy to lose track of that outer diff marker's background color).
Therefore, let's add another dimension to the mix. Still use green/red/normal according to the commit diffs, but now also dim the lines that were only in the old commit, and use bold face for the lines that are only in the new commit.
That way, it is much easier not to lose track of, say, when we are looking at a line that was added in the previous iteration of a patch series but the new iteration adds a slightly different version: the obsolete change will be dimmed, the current version of the patch will be bold.
At least this developer has a much easier time reading the range-diffs that way.
--- Documentation/config.txt | 6 ++++-- Documentation/git-range-diff.txt | 17 +++++++++++++---- color.h | 6 ++++++ diff.c | 28 ++++++++++++++++++++++------ diff.h | 8 +++++++- 5 files changed, 52 insertions(+), 13 deletions(-)
Documentation/config.txt +4 -2
@@ -1159,8 +1159,10 @@ color.diff.<slot>:: (highlighting whitespace errors), `oldMoved` (deleted lines), `newMoved` (added lines), `oldMovedDimmed`, `oldMovedAlternative`, `oldMovedAlternativeDimmed`, `newMovedDimmed`, `newMovedAlternative` - and `newMovedAlternativeDimmed` (See the '<mode>' - setting of '--color-moved' in linkgit:git-diff[1] for details). + `newMovedAlternativeDimmed` (See the '<mode>' + setting of '--color-moved' in linkgit:git-diff[1] for details), + `contextDimmed`, `oldDimmed`, `newDimmed`, `contextBold`, + `oldBold`, and `newBold` (see linkgit:git-range-diff[1] for details). color.decorate.<slot>:: Use customized color for 'git log --decorate' output. `<slot>` is one
Documentation/git-range-diff.txt +13 -4
@@ -35,10 +35,19 @@ OPTIONS When the commit diffs differ, `git range-diff` recreates the original diffs' coloring, and adds outer -/+ diff markers with the *background* being red/green to make it easier to see e.g. - when there was a change in what exact lines were added. This is - known to `range-diff` as "dual coloring". Use `--no-dual-color` - to revert to color all lines according to the outer diff markers - (and completely ignore the inner diff when it comes to color). + when there was a change in what exact lines were added. ++ +Additionally, the commit diff lines that are only present in the first commit +range are shown "dimmed" (this can be overridden using the `color.diff.<slot>` +config setting where `<slot>` is one of `contextDimmed`, `oldDimmed` and +`newDimmed`), and the commit diff lines that are only present in the second +commit range are shown in bold (which can be overridden using the config +settings `color.diff.<slot>` with `<slot>` being one of `contextBold`, +`oldBold` or `newBold`). ++ +This is known to `range-diff` as "dual coloring". Use `--no-dual-color` +to revert to color all lines according to the outer diff markers +(and completely ignore the inner diff when it comes to color). --creation-factor=<percent>:: Set the creation/deletion cost fudge factor to `<percent>`.
color.h +6 -0
@@ -36,6 +36,12 @@ struct strbuf;#define GIT_COLOR_BOLD_BLUE "\033[1;34m"#define GIT_COLOR_BOLD_MAGENTA "\033[1;35m"#define GIT_COLOR_BOLD_CYAN "\033[1;36m" +#define GIT_COLOR_FAINT_RED "\033[2;31m" +#define GIT_COLOR_FAINT_GREEN "\033[2;32m" +#define GIT_COLOR_FAINT_YELLOW "\033[2;33m" +#define GIT_COLOR_FAINT_BLUE "\033[2;34m" +#define GIT_COLOR_FAINT_MAGENTA "\033[2;35m" +#define GIT_COLOR_FAINT_CYAN "\033[2;36m"#define GIT_COLOR_BG_RED "\033[41m"#define GIT_COLOR_BG_GREEN "\033[42m"#define GIT_COLOR_BG_YELLOW "\033[43m"
diff.c +22 -6
@@ -69,6 +69,12 @@ static char diff_colors[][COLOR_MAXLEN] = {GIT_COLOR_BOLD_YELLOW ,/* NEW_MOVED ALTERNATIVE */ GIT_COLOR_FAINT ,/* NEW_MOVED_DIM */ GIT_COLOR_FAINT_ITALIC ,/* NEW_MOVED_ALTERNATIVE_DIM */ +GIT_COLOR_FAINT ,/* CONTEXT_DIM */ +GIT_COLOR_FAINT_RED ,/* OLD_DIM */ +GIT_COLOR_FAINT_GREEN ,/* NEW_DIM */ +GIT_COLOR_BOLD ,/* CONTEXT_BOLD */ +GIT_COLOR_BOLD_RED ,/* OLD_BOLD */ + GIT_COLOR_BOLD_GREEN, /* NEW_BOLD */ }; static const char *color_diff_slots[] = {
@@ -88,6 +94,12 @@ static const char *color_diff_slots[] = { [DIFF_FILE_NEW_MOVED_ALT] = "newMovedAlternative", [DIFF_FILE_NEW_MOVED_DIM] = "newMovedDimmed", [DIFF_FILE_NEW_MOVED_ALT_DIM] = "newMovedAlternativeDimmed", + [DIFF_CONTEXT_DIM] = "contextDimmed", + [DIFF_FILE_OLD_DIM] = "oldDimmed", + [DIFF_FILE_NEW_DIM] = "newDimmed", + [DIFF_CONTEXT_BOLD] = "contextBold", + [DIFF_FILE_OLD_BOLD] = "oldBold", + [DIFF_FILE_NEW_BOLD] = "newBold", }; static NORETURN void die_want_option(const char *option_name)
@@ -1089,11 +1101,13 @@ static void emit_diff_symbol_from_struct(struct diff_options *o, set_sign = set;if (c =='-' ) - set =diff_get_color_opt (o,DIFF_FILE_OLD ); + set =diff_get_color_opt (o,DIFF_FILE_OLD_BOLD );else if (c =='@' ) set =diff_get_color_opt (o,DIFF_FRAGINFO ); -else if (c !='+' ) - set =diff_get_color_opt (o,DIFF_CONTEXT ); +else if (c =='+' ) + set =diff_get_color_opt (o,DIFF_FILE_NEW_BOLD ); +else + set =diff_get_color_opt (o,DIFF_CONTEXT_BOLD ); flags |=WS_IGNORE_FIRST_SPACE ; } emit_line_ws_markup(o, set, reset, line, len, set_sign, '+',
@@ -1131,11 +1145,13 @@ static void emit_diff_symbol_from_struct(struct diff_options *o, set_sign = set;if (c =='+' ) - set =diff_get_color_opt (o,DIFF_FILE_NEW ); + set =diff_get_color_opt (o,DIFF_FILE_NEW_DIM );else if (c =='@' ) set =diff_get_color_opt (o,DIFF_FRAGINFO ); -else if (c !='-' ) - set =diff_get_color_opt (o,DIFF_CONTEXT ); +else if (c =='-' ) + set =diff_get_color_opt (o,DIFF_FILE_OLD_DIM ); +else + set =diff_get_color_opt (o,DIFF_CONTEXT_DIM ); }emit_line_ws_markup (o, set, reset, line, len, set_sign,'-' , flags &DIFF_SYMBOL_CONTENT_WS_MASK ,0 );
diff.h +7 -1
@@ -243,7 +243,13 @@ enum color_diff {DIFF_FILE_NEW_MOVED =13 ,DIFF_FILE_NEW_MOVED_ALT =14 ,DIFF_FILE_NEW_MOVED_DIM =15 , - DIFF_FILE_NEW_MOVED_ALT_DIM = 16 +DIFF_FILE_NEW_MOVED_ALT_DIM =16 , +DIFF_CONTEXT_DIM =17 , +DIFF_FILE_OLD_DIM =18 , +DIFF_FILE_NEW_DIM =19 , +DIFF_CONTEXT_BOLD =20 , +DIFF_FILE_OLD_BOLD =21 , + DIFF_FILE_NEW_BOLD = 22, };const char *diff_get_color (int diff_use_color,enum color_diff ix);#define diff_get_color_opt (o, ix) \
Signature · 1 line
gitgitgadget
Re: [PATCH v4 00/21] Add `range-diff`, a `tbdiff` lookalike
In reply to Johannes Schindelin via GitGitGadgetQuoted 2 lines · Johannes Schindelin via GitGitGadget
Side note: I work on implementing range-diff not only to make life easier for reviewers who have to suffer through v2, v3, ... of my patch series, but also to verify my changes before submitting a new iteration. And also, maybe even more importantly, I plan to use it to verify my merging-rebases of Git for Windows (for which I previously used to redirect the pre-rebase/post-rebase diffs vs upstream and then compare them using
git diff --no-index). And of course any interested person can see what changes were necessary e.g. in the merging-rebase of Git for Windows onto v2.17.0 by running a command like:
Thanks for making tools that makes the life of a Git developer easier! (Just filed https://github.com/gitgitgadget/gitgitgadget/issues/26
which asks to break lines for this cover letter)
Quoted 4 lines
base-commit:
b7bd9486b055c3f967a870311e704e3bb0654e4f
Published-As: https://github.com/gitgitgadget/git/releases/tags/pr-1%2Fdscho%2Fbranch-diff-v4
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1/dscho/branch-diff-v4
Pull-Request: https://github.com/gitgitgadget/git/pull/1
I just realized that if I had ideal memory of the previous review, I could contain my whole review answer to this email only.
Quoted 12 lines
Range-diff vs v3:
1: 39272eefc ! 1: f7e70689e linear-assignment: a function to solve least-cost assignment problems @@ -223,9 +223,7 @@ + BUG("negative j: %d", j); + i = pred[j]; + column2row[j] = i; -+ k = j; -+ j = row2column[i]; -+ row2column[i] = k; ++ SWAP(j, row2column[i]);
The dual color option (as a default) really helps here. Thanks for that! Does it have to be renamed though? (It's more than two colors; originally it was inverting the beginning signs)
Maybe --color=emphasize-later
assuming there will be other modes for coloring, such as "diff-only", which would correspond with --no-dual-color, or "none" that will correspond would be --no-color. I imagine there could be more fancy things, hence I would propose a mode rather than a switch.
(Feel free to push back if discussing naming here feels like bike shedding)
2: 7f15b26d4ea ! 82: 88134121d2a Introduce range-diff to compare iterations of a topic branch
[...]
Quoted 3 lines
diff --git a/Makefile b/Makefile
--- a/Makefile
+++ b/MakefileThe line starting with --- is red (default removed color) and the line with +++ is green (default add color).
Ideally these two lines and the third line above starting with "diff --git" would render in GIT_COLOR_BOLD ("METAINFO").
Quoted 14 lines
3:
076e1192d! 3:4e3fb47a1range-diff: first rudimentary implementation
@@ -4,7 +4,7 @@At this stage,
git range-diffcan determine corresponding commits of two related commit ranges. This makes use of the recently introduced
- implementation of the Hungarian algorithm.
+ implementation of the linear assignment algorithm.The core of this patch is a straight port of the ideas of tbdiff, the apparently dormant project at https://github.com/trast/tbdiff.
@@ -51,19 +51,17 @@
+ int res = 0;
+ struct strbuf range1 =STRBUF_INIT, range2 =STRBUF_INIT;
Quoted 6 lines
-- argc = parse_options(argc, argv, NULL, options,
--builtin_range_diff_usage, 0);
-+ argc = parse_options(argc, argv, NULL, options,builtin_range_diff_usage,
-+ 0);
+ argc = parse_options(argc, argv, NULL, options,
+builtin_range_diff_usage, 0);
This is really nice in colors when viewed locally.
Quoted 2 lines
16: dfa7b1e71 < -: --------- range-diff --dual-color: work around bogus white-space warning -: --------- > 16: f4252f2b2 range-diff --dual-color: fix bogus white-space warning
Ah; here my initial assumption of only reviewing the range-diff breaks down now. I'll dig into patch 16 separately.
Maybe it is worth having an option to expand all "new" patches. (Given that the range-diff pr-1/dscho/branch-diff-v3...pr-1/dscho/branch-diff-v4 told me you used a different base, this is a hard problem, as I certainly would want to skip over all new base commits, but this one is interesting to look at. An easy way out: Maybe an option to expand any new commits/patches after the first expansion? Asking for opinions rather than implementing it)
Quoted 4 lines
19: 144363006 < -: --------- range-diff: left-pad patch numbers -: --------- > 19: 07ec215e8 range-diff: left-pad patch numbers-: --------- > 21:
d8498fb32range-diff: use dim/bold cues to improve dual color mode
Those are interesting, I'll look at them separately, too.
Thanks,
Stefan
Re: [PATCH v4 00/21] Add `range-diff`, a `tbdiff` lookalike
Quoted 9 lines · Stefan Beller
Quoted 2 lines · Johannes Schindelin via GitGitGadget
Side note: I work on implementing range-diff not only to make life easier for reviewers who have to suffer through v2, v3, ... of my patch series, but also to verify my changes before submitting a new iteration. And also, maybe even more importantly, I plan to use it to verify my merging-rebases of Git for Windows (for which I previously used to redirect the pre-rebase/post-rebase diffs vs upstream and then compare them using
git diff --no-index). And of course any interested person can see what changes were necessary e.g. in the merging-rebase of Git for Windows onto v2.17.0 by running a command like:Thanks for making tools that makes the life of a Git developer easier! (Just filed https://github.com/gitgitgadget/gitgitgadget/issues/26
which asks to break lines for this cover letter)
Thanks. These cover letters are unreadable without W Q (gnus-article-fill-long-lines)
Re: [PATCH v4 00/21] Add `range-diff`, a `tbdiff` lookalike
Quoted 9 lines · Junio C Hamano
Quoted 6 lines · Stefan Beller
Quoted 2 lines · Johannes Schindelin via GitGitGadget
Side note: I work on implementing range-diff not only to make life easier for reviewers who have to suffer through v2, v3, ... of my patch series, but also to verify my changes before submitting a new iteration. And also, maybe even more importantly, I plan to use it to verify my merging-rebases of Git for Windows (for which I previously used to redirect the pre-rebase/post-rebase diffs vs upstream and then compare them using
git diff --no-index). And of course any interested person can see what changes were necessary e.g. in the merging-rebase of Git for Windows onto v2.17.0 by running a command like:
Quoted 6 lines
Quoted 3 lines
Thanks for making tools that makes the life of a Git developer easier! (Just filed https://github.com/gitgitgadget/gitgitgadget/issues/26
which asks to break lines for this cover letter)Thanks. These cover letters are unreadable without W Q (gnus-article-fill-long-lines)
While I had some comments on how I dislike some aspects of the implementation, I think it proves its usefulness by my usage, so I would suggest to merge it down to next as-is (and as soon as possible); deferring the issues in the implementation to later.
I found running the range-diff on origin/pu to be a pleasant experience, although that still highlights the issues of in-exact coloring (the colors are chosen by the first two characters of the diff, which leads to mis-coloring of diff headers of the inner diff in the outer diff.
But despite the imperfection, I strongly urge to consider the series as-is as good enough for inclusion.
Thanks,
Stefan
Thanks,
Stefan
Re: [PATCH v4 00/21] Add `range-diff`, a `tbdiff` lookalike
In reply to Stefan BellerHi Stefan,
Quoted 15 lines · Stefan Beller
Quoted 11 lines · Johannes Schindelin via GitGitGadget
Range-diff vs v3:
1: 39272eefc ! 1: f7e70689e linear-assignment: a function to solve least-cost assignment problems @@ -223,9 +223,7 @@ + BUG("negative j: %d", j); + i = pred[j]; + column2row[j] = i; -+ k = j; -+ j = row2column[i]; -+ row2column[i] = k; ++ SWAP(j, row2column[i]);
Quoted 3 lines
The dual color option (as a default) really helps here. Thanks for that! Does it have to be renamed though? (It's more than two colors; originally it was inverting the beginning signs)
I understand (and understood) the "dual" to mean that there are two separate coloring methods, the coloring of the inner, and the coloring of the outer diff. (And in my mind, the dimming is not so much an "inner" diff things as an "outer" diff thing.)
Quoted 6 lines
Maybe --color=emphasize-later assuming there will be other modes for coloring, such as "diff-only", which would correspond with --no-dual-color, or "none" that will correspond would be --no-color. I imagine there could be more fancy things, hence I would propose a mode rather than a switch. (Feel free to push back if discussing naming here feels like bike shedding)
Your suggestion does not really feel like bike-shedding to me, here, I can see the merit of it.
It's just that 1) overloading --color here would be cumbersome, as --color is *already* a diff option that we actually use, and 2) I am not all that certain that new fancy things will crop up anytime soon. It was hard enough to think of the dimming feature, and then implementing it.
So while I think your idea has merit, I still think that we can do that later. The --no-dual-color option can easily be deprecated in favor of, say, --color-mode=<mode>, when (and if) new modes crop up.
Quoted 7 lines
2:
7f15b26d4ea! 82:88134121d2aIntroducerange-diffto compare iterations of a topic branch
[...]Quoted 3 lines
diff --git a/Makefile b/Makefile --- a/Makefile +++ b/Makefile
Quoted 5 lines
The line starting with --- is red (default removed color) and the line with +++ is green (default add color).
Ideally these two lines and the third line above starting with "diff --git" would render in
GIT_COLOR_BOLD("METAINFO").
I see where you are coming from, but given how complicated it seems to me to implement this (dual color mode gets away with working line-based for the moment, and what you ask for would require state, and would not even be fool-proof, as the diff --git line might not even be part of the context.
Seeing how long this patch series has already simmered, I would want to invoke that old adage "the perfect is the enemy of the good", and rather see a version of range-diff enter Git's source code, if need be marked as "EXPERIMENTAL" so that the maintainer can claim that it is okay to be buggy, and then invite contributions from other sides than from me.
Quoted 5 lines
Quoted 2 lines
16: dfa7b1e71 < -: --------- range-diff --dual-color: work around bogus white-space warning -: --------- > 16: f4252f2b2 range-diff --dual-color: fix bogus white-space warningAh; here my initial assumption of only reviewing the range-diff breaks down now. I'll dig into patch 16 separately.
Right. In this case, it is a total rewrite, anyway (and I'll have to ask you to overlook my frustration with how complex and hard it is to work on ws.c without breaking anything). For the sake of review, you should ignore the old patch. Unless you find that the new version is so complex and prone to introduce bugs (with which I would agree) that we should go back to the original workaround, which is so easy to understand that there are no obvious bugs lurking inside it.
Quoted 1 line
Maybe it is worth having an option to expand all "new" patches.
Sure. And I would love to have this in a separate patch series, as it is well beyond the original purpose of this patch series to simply make tbdiff a builtin.
I should have known better, of course, but I was really not keen on improving range-diff *before* it enters the code base, to the point of introducing new features that might very well introduce new regressions in unrelated commands, too.
Essentially, I am declaring a feature-freeze on this patch series until it enters next.
Quoted 6 lines
(Given that the range-diff pr-1/dscho/branch-diff-v3...pr-1/dscho/branch-diff-v4 told me you used a different base, this is a hard problem, as I certainly would want to skip over all new base commits, but this one is interesting to look at. An easy way out: Maybe an option to expand any new commits/patches after the first expansion? Asking for opinions rather than implementing it)
Any fulfilled wish is immediately welcomed with offspring, it seems.
Again, this is a very nice feature, I think, and even nicer: it can be implemented by somebody else than me, on top of my patch series, after it stabilized and entered next.
Quoted 2 lines
Quoted 2 lines
19: 144363006 < -: --------- range-diff: left-pad patch numbers -: --------- > 19: 07ec215e8 range-diff: left-pad patch numbers
Yes, this is something where I would have used a different --creation-factor locally, but I did not want to hack up GitGitGadget *just* for this patch series.
Quoted 3 lines
Quoted 1 line
-: --------- > 21:
d8498fb32range-diff: use dim/bold cues to improve dual color modeThose are interesting, I'll look at them separately, too.
That last one is indeed very interesting, from a feature point of view, and a little intimidating from a review point of view: it entered the patch series only in v4. Combined, this is a clear sign that I should not have included that feature in this here patch series. But I did fall prey to "featuritis". I'll try to be better about this. The most important thing now is to stabilize the range-diff command and to get it included. It already simmers for way too long.
Ciao,
Dscho
Re: [PATCH v4 00/21] Add `range-diff`, a `tbdiff` lookalike
In reply to Stefan BellerHi Stefan,
Quoted 17 lines · Stefan Beller
Quoted 9 lines · Johannes Schindelin via GitGitGadget
1: 39272eefc ! 1: f7e70689e linear-assignment: a function to solve least-cost assignment problems @@ -223,9 +223,7 @@ + BUG("negative j: %d", j); + i = pred[j]; + column2row[j] = i; -+ k = j; -+ j = row2column[i]; -+ row2column[i] = k; ++ SWAP(j, row2column[i]);The dual color option (as a default) really helps here. Thanks for that! Does it have to be renamed though? (It's more than two colors; originally it was inverting the beginning signs)
Quoted 6 lines
Maybe --color=emphasize-later
assuming there will be other modes for coloring, such as "diff-only", which would correspond with --no-dual-color, or "none" that will correspond would be --no-color. I imagine there could be more fancy things, hence I would propose a mode rather than a switch.
(Feel free to push back if discussing naming here feels like bike shedding)
I do feel free to push back on that.
Quoted 7 lines
2:
7f15b26d4ea! 82:88134121d2aIntroducerange-diffto compare iterations of a topic branch
[...]Quoted 3 lines
diff --git a/Makefile b/Makefile --- a/Makefile +++ b/Makefile
Quoted 5 lines
The line starting with --- is red (default removed color) and the line with +++ is green (default add color).
Ideally these two lines and the third line above starting with "diff --git" would render in
GIT_COLOR_BOLD("METAINFO").
I agree that is not the best coloring here, but as you remarked elsewhere, it would require content-aware dual coloring, and I am loathe to try to implement that for two reasons: 1) it would take most likely a long time to design and implement that, and 2) I don't have that time.
So I would like to declare that good enough is good enough in this case.
Quoted 21 lines
Quoted 14 lines
3:
076e1192d! 3:4e3fb47a1range-diff: first rudimentary implementation
@@ -4,7 +4,7 @@At this stage,
git range-diffcan determine corresponding commits of two related commit ranges. This makes use of the recently introduced
- implementation of the Hungarian algorithm.
+ implementation of the linear assignment algorithm.The core of this patch is a straight port of the ideas of tbdiff, the apparently dormant project at https://github.com/trast/tbdiff.
@@ -51,19 +51,17 @@
+ int res = 0;
+ struct strbuf range1 =STRBUF_INIT, range2 =STRBUF_INIT;Quoted 6 lines
-- argc = parse_options(argc, argv, NULL, options,
--builtin_range_diff_usage, 0);
-+ argc = parse_options(argc, argv, NULL, options,builtin_range_diff_usage,
-+ 0);
+ argc = parse_options(argc, argv, NULL, options,
+builtin_range_diff_usage, 0);
Quoted 7 lines
This is really nice in colors when viewed locally.
Quoted 2 lines
16: dfa7b1e71 < -: --------- range-diff --dual-color: work around bogus white-space warning -: --------- > 16: f4252f2b2 range-diff --dual-color: fix bogus white-space warningAh; here my initial assumption of only reviewing the range-diff breaks down now. I'll dig into patch 16 separately.
Right. This was an almost complete rewrite, and then next iteration will hopefully bring another complete rewrite: disabling whitespace warnings in dual color mode.
Quoted 1 line
Maybe it is worth having an option to expand all "new" patches.
Sure.
And I also have a use case for --left-only/--right-only.
And I also have a strong use case (and so does Junio, it seems, or for that matter, anybody contributing to Git due to Junio's insistence on signing off on each patch, rather than on the merge commit) for something like --ignore-lines=<regex>.
And you probably guess what I will say next: these features will make for really fantastic patch series *on top* of mine. There really is no good reason to delay the current patch series just to cram more features into it that had not been planned in the first place.
Quoted 7 lines
(Given that the range-diff pr-1/dscho/branch-diff-v3...pr-1/dscho/branch-diff-v4 told me you used a different base, this is a hard problem, as I certainly would want to skip over all new base commits, but this one is interesting to look at. An easy way out: Maybe an option to expand any new commits/patches after the first expansion? Asking for opinions rather than implementing it)
Quoted 6 lines
Quoted 4 lines
19: 144363006 < -: --------- range-diff: left-pad patch numbers -: --------- > 19: 07ec215e8 range-diff: left-pad patch numbers-: --------- > 21:
d8498fb32range-diff: use dim/bold cues to improve dual color modeThose are interesting, I'll look at them separately, too.
Thanks,
Dscho
Re: [PATCH v4 00/21] Add `range-diff`, a `tbdiff` lookalike
Quoted 11 lines · Johannes Schindelin
Quoted 10 lines
[...]
Quoted 3 lines
diff --git a/Makefile b/Makefile --- a/Makefile +++ b/MakefileThe line starting with --- is red (default removed color) and the line with +++ is green (default add color).
Ideally these two lines and the third line above starting with "diff --git" would render in
GIT_COLOR_BOLD("METAINFO").
Quoted 6 lines
I agree that is not the best coloring here, but as you remarked elsewhere, it would require content-aware dual coloring, and I am loathe to try to implement that for two reasons: 1) it would take most likely a long time to design and implement that, and 2) I don't have that time.
So I would like to declare that good enough is good enough in this case.
I anticipated this answer, so I wrote some patches myself, starting at
https://public-inbox.org/git/[email protected]/
specifically
https://public-inbox.org/git/[email protected]/
I plan on resending these on top of your resend (if any) at a later convenient time for both you and Junio, as noted in
https://public-inbox.org/git/CAGZ79kZnVEsvpicNu7LXkRcHuRqGvESfvG3DL5O_2kPVYrW-Gg@mail.gmail.com/
Quoted 45 lines
Quoted 22 lines
Quoted 15 lines
3:
076e1192d! 3:4e3fb47a1range-diff: first rudimentary implementation
@@ -4,7 +4,7 @@At this stage,
git range-diffcan determine corresponding commits of two related commit ranges. This makes use of the recently introduced
- implementation of the Hungarian algorithm.
+ implementation of the linear assignment algorithm.The core of this patch is a straight port of the ideas of tbdiff, the apparently dormant project at https://github.com/trast/tbdiff.
@@ -51,19 +51,17 @@
+ int res = 0;
+ struct strbuf range1 =STRBUF_INIT, range2 =STRBUF_INIT;Quoted 6 lines
-- argc = parse_options(argc, argv, NULL, options,
--builtin_range_diff_usage, 0);
-+ argc = parse_options(argc, argv, NULL, options,builtin_range_diff_usage,
-+ 0);
+ argc = parse_options(argc, argv, NULL, options,
+builtin_range_diff_usage, 0);Quoted 7 lines
This is really nice in colors when viewed locally.
Quoted 2 lines
16: dfa7b1e71 < -: --------- range-diff --dual-color: work around bogus white-space warning -: --------- > 16: f4252f2b2 range-diff --dual-color: fix bogus white-space warningAh; here my initial assumption of only reviewing the range-diff breaks down now. I'll dig into patch 16 separately.
Right. This was an almost complete rewrite, and then next iteration will hopefully bring another complete rewrite: disabling whitespace warnings in dual color mode.
Quoted 1 line
Maybe it is worth having an option to expand all "new" patches.
Sure.
And I also have a use case for --left-only/--right-only.
And I also have a strong use case (and so does Junio, it seems, or for that matter, anybody contributing to Git due to Junio's insistence on signing off on each patch, rather than on the merge commit) for something like --ignore-lines=<regex>.
Quoted 4 lines
And you probably guess what I will say next: these features will make for really fantastic patch series *on top* of mine. There really is no good reason to delay the current patch series just to cram more features into it that had not been planned in the first place.
Yes, I agree. I am unsure about the current state of your series, though;
Junio thinks (expects?) a resend, whereas you seem to call it good enough but also said (some time back) that you want to resend due to Thomas feedback.
I do have 2 series on top of the current range-diff.
* The first (queued by Junio as origin/sb/range-diff-colors) adds a basic test for colors and improves diff.c readability
* The second (linked above) changes colors for some lines.
I do not want to build more on top as long as I do not know if you resend (and how much it'll change)
Thanks,
Stefan
Re: [PATCH v4 00/21] Add `range-diff`, a `tbdiff` lookalike
Hi Stefan,
Quoted 21 lines · Stefan Beller
Quoted 11 lines · Johannes Schindelin
Quoted 10 lines
[...]
Quoted 3 lines
diff --git a/Makefile b/Makefile --- a/Makefile +++ b/MakefileThe line starting with --- is red (default removed color) and the line with +++ is green (default add color).
Ideally these two lines and the third line above starting with "diff --git" would render in
GIT_COLOR_BOLD("METAINFO").Quoted 6 lines
I agree that is not the best coloring here, but as you remarked elsewhere, it would require content-aware dual coloring, and I am loathe to try to implement that for two reasons: 1) it would take most likely a long time to design and implement that, and 2) I don't have that time.
So I would like to declare that good enough is good enough in this case.
Quoted 8 lines
I anticipated this answer, so I wrote some patches myself, starting at
https://public-inbox.org/git/[email protected]/
specifically
https://public-inbox.org/git/[email protected]/I plan on resending these on top of your resend (if any) at a later convenient time for both you and Junio, as noted in
https://public-inbox.org/git/CAGZ79kZnVEsvpicNu7LXkRcHuRqGvESfvG3DL5O_2kPVYrW-Gg@mail.gmail.com/
Thank you!
Quoted 44 lines
Quoted 44 lines
Quoted 21 lines
Quoted 14 lines
3:
076e1192d! 3:4e3fb47a1range-diff: first rudimentary implementation
@@ -4,7 +4,7 @@At this stage,
git range-diffcan determine corresponding commits of two related commit ranges. This makes use of the recently introduced
- implementation of the Hungarian algorithm.
+ implementation of the linear assignment algorithm.The core of this patch is a straight port of the ideas of tbdiff, the apparently dormant project at https://github.com/trast/tbdiff.
@@ -51,19 +51,17 @@
+ int res = 0;
+ struct strbuf range1 =STRBUF_INIT, range2 =STRBUF_INIT;Quoted 6 lines
-- argc = parse_options(argc, argv, NULL, options,
--builtin_range_diff_usage, 0);
-+ argc = parse_options(argc, argv, NULL, options,builtin_range_diff_usage,
-+ 0);
+ argc = parse_options(argc, argv, NULL, options,
+builtin_range_diff_usage, 0);Quoted 7 lines
This is really nice in colors when viewed locally.
Quoted 2 lines
16: dfa7b1e71 < -: --------- range-diff --dual-color: work around bogus white-space warning -: --------- > 16: f4252f2b2 range-diff --dual-color: fix bogus white-space warningAh; here my initial assumption of only reviewing the range-diff breaks down now. I'll dig into patch 16 separately.
Right. This was an almost complete rewrite, and then next iteration will hopefully bring another complete rewrite: disabling whitespace warnings in dual color mode.
Quoted 1 line
Maybe it is worth having an option to expand all "new" patches.
Sure.
And I also have a use case for --left-only/--right-only.
And I also have a strong use case (and so does Junio, it seems, or for that matter, anybody contributing to Git due to Junio's insistence on signing off on each patch, rather than on the merge commit) for something like --ignore-lines=<regex>.
Quoted 6 lines
Quoted 4 lines
And you probably guess what I will say next: these features will make for really fantastic patch series *on top* of mine. There really is no good reason to delay the current patch series just to cram more features into it that had not been planned in the first place.
Yes, I agree.
I am happy to hear that.
Quoted 5 lines
I am unsure about the current state of your series, though;
Junio thinks (expects?) a resend, whereas you seem to call it good enough but also said (some time back) that you want to resend due to Thomas feedback.
Sorry about being so unclear. When time gets scarce, sometimes I get too stressed (and too short on time) to communicate properly.
Yes, I want to limit the new features put into this patch series, in the interest of getting things into next (and maybe still into master before v2.19, but I am not allowing myself to hope for that too much).
And yes, I want to send another iteration, as there have been too many changes that I do not want to ask Junio to touch up, in particular because I am a little bit of a detail-oriented person and want my fixes just so.
Quoted 7 lines
I do have 2 series on top of the current range-diff.
* The first (queued by Junio as origin/sb/range-diff-colors) adds a basic test for colors and improvesdiff.creadability
* The second (linked above) changes colors for some lines.I do not want to build more on top as long as I do not know if you resend (and how much it'll change)
It will not change much. The biggest change is that the white-space warning thing is done completely differently, so that I do not even have to be in ws.c's author list.
I'll just try to get that option parsing change in that Eric suggested, force-push, then wait for macOS and Linux builds to pass (trusting that Windows will follow suite) and hit /submit.
Ciao,
Dscho
Re: [PATCH v4 00/21] Add `range-diff`, a `tbdiff` lookalike
Quoted 3 lines · Johannes Schindelin
I'll just try to get that option parsing change in that Eric suggested, force-push, then wait for macOS and Linux builds to pass (trusting that Windows will follow suite) and hit /submit.
OK. Obviously receiving, applying and inspecting that result will not be done in time for today's integration cycle, but having a version that limits its cope and is suitable for 'next' is a good thing to have at this point during the cycle. Correct whitespace error colouring, etc., can be done on top after the basics settle, and the basics were good in a few versions ago already IIRC.
Re: [PATCH v4 00/21] Add `range-diff`, a `tbdiff` lookalike
Hi Junio,
Quoted 6 lines · Junio C Hamano
Quoted 3 lines · Johannes Schindelin
I'll just try to get that option parsing change in that Eric suggested, force-push, then wait for macOS and Linux builds to pass (trusting that Windows will follow suite) and hit /submit.
Quoted 6 lines
OK. Obviously receiving, applying and inspecting that result will not be done in time for today's integration cycle, but having a version that limits its cope and is suitable for 'next' is a good thing to have at this point during the cycle. Correct whitespace error colouring, etc., can be done on top after the basics settle, and the basics were good in a few versions ago already IIRC.
No, a couple of issues were identified, still, that merited several dozen rebases on my side.
Ciao,
Dscho
Re: [PATCH v4 00/21] Add `range-diff`, a `tbdiff` lookalike
In reply to Johannes Schindelin via GitGitGadgetQuoted 1 line · Johannes Schindelin via GitGitGadget
The incredibly useful [
git-tbdiff](https://github.com/trast/tbdiff) tool to compare patch series (say, to see what changed between two iterations sent to the Git mailing list) is slightly less useful for this developer due to the fact that it requires thehungarianandnumpyPython packages which are for some reason really hard to build in MSYS2. So hard that I even had to give up, because it was simply easier to re-implement the whole shebang as a builtin command.
Thanks for your work making this a built-in! I finally found some time to go through the series, and overall it makes sense to me. I left a few comments here and there, and fwiw I didn't think there's anything major that needs to be addressed.
Hope it helps!
Quoted 648 lines
The project at https://github.com/trast/tbdiff seems to be dormant, anyway. Funny (and true) story: I looked at the open Pull Requests to see how active that project is, only to find to my surprise that I had submitted one in August 2015, and that it was still unanswered let alone merged.
While at it, I forward-ported AEvar's patch to force
--decorate=nobecausegit -p tbdiffwould fail otherwise.Side note: I work on implementing range-diff not only to make life easier for reviewers who have to suffer through v2, v3, ... of my patch series, but also to verify my changes before submitting a new iteration. And also, maybe even more importantly, I plan to use it to verify my merging-rebases of Git for Windows (for which I previously used to redirect the pre-rebase/post-rebase diffs vs upstream and then compare them using
git diff --no-index). And of course any interested person can see what changes were necessary e.g. in the merging-rebase of Git for Windows onto v2.17.0 by running a command like:```sh base=^{/Start.the.merging-rebase} tag=v2.17.0.windows.1 pre=$tag$base^2 git range-diff $pre$base..$pre $tag$base..$tag ```The command uses what it calls the "dual color mode" (can be disabled via
--no-dual-color) which helps identifying what *actually* changed: it prefixes lines with a-(and red background) that correspond to the first commit range, and with a+(and green background) that correspond to the second range. The rest of the lines will be colored according to the original diffs.Changes since v3:
- The cover letter was adjusted to reflect the new reality (the command is called
range-diffnow, notbranch-diff, and--dual-coloris the default).
- The documentation was adjusted a bit more in the patch that makes--dual-colorthe default.
- Clarified the calculation of the cost matrix, as per Stefan Beller's request.
- The man page now spells out that merge *commits* are ignored in the commit ranges (not merges per se).
- The code inlinear-assignment.cwas adjusted to use theSWAP()macro.
- The commit message of the patch introducing the first rudimentary implementation no longer talks about the "Hungarian" algorithm, but about the "linear assignment algorithm" instead.
- A bogus indentation change was backed out from the patch introducing the first rudimentary implementation.
- Instead of merely warning about missing..in the 2-parameter invocation, we now exit with the error message.
- Thediff_opt_parse()function is allowed to return a value larger than 1, indicating that more than just one command-line parameter was parsed. We now advance by the indicated value instead of always advancing exactly 1 (which is still correct much of the time).
- A lengthyif...else if...else if...elsewas simplified (from a logical point of view) by reordering it.
- The unnecessarilystaticvariabledasheswas turned into a local variable of the caller.
- The commit message talking about the new man page still referred togit branch --diff, which has been fixed.
- A forgotten t7910 reference was changed to t3206.
- An unbalanced double-tick was fixed in the man page.
- Fixed grammar both of the commit message and the description of the--no-dual-coloroption.
- To fix the build, a blank man page is now introduced together with the newrange-diffcommand, even if it is populated for real only at a later patch (i.e. at the same time as before).
- The headaches Junio fears would be incurred by that simple workaround to avoid bogus white-space error reporting are fended off: a more complex patch is now in place that adds (and uses) a new white-space flag. Sadly, as is all too common when Junio "encourages" me to replace a simple workaround by something "proper", it caused all kinds of headaches to get this right, so I am rather less certain that the "proper" fix will cause us less headaches than the simple workaround would have done. But whatever.
- The dual color mode now also dims the changes that are exclusively in the first specified commit range, and uses bold face on the changes exclusively in the second one. This matches the intuition when usingrange-diffto compare an older iteration of a patch series to a newer one: the changes from the previous iteration that were replaced by new ones "fade", while the changes that replace them are "shiny new".Changes since v2:
- Right-aligned the patch numbers in the commit pairs.
- UsedALLOC_ARRAY()inhungarian.cinstead of xmalloc(sizeof()*size).
- Changed compute_assignment()s return type from int to void, as it always succeeds.
- Changed the Hungarian Algorithm to use an integer cost matrix.
- Changed the --creation-weight <double> option to --creation-factor <percent> where <percent> is an integer.
- Retitled 1/19 and 2/19 to better conform with the current conventions, as pointed out (and suggested) by Junio.
- Shut up Coverity, and at the same time avoided passing the unnecessaryiandjparameters tooutput_pair_header().
- Removed support for the--no-patchesoption: we inheritdiff_options' support for-salready (and much more).
- Removed the ugly_INVenum values, and introduced a beautifulGIT_COLOR_REVERSEinstead. This way, whatever the user configured as color.diff.new (or .old) will be used in reverse in the dual color mode.
- Instead of overriding the fragment header color, the dual color mode will now reverse the "outer" fragment headers, too.
- Turned the stand-alone branch-diff command into the--diffoption ofgit branch. Adjusted pretty much *all* commit messages to account for this. This change should no longer be visible: see below.
- Pretty much re-wrote the completion, to support the new --diff mode of git-branch. See below: it was reverted for range-diff.
- Renamed t7910 to t3206, to be closer to the git-branch tests.
- Ensured thatgit_diff_ui_config()gets called, and therefore color.diff.* respected.
- Avoided leakingfour_spaces.
- Fixed a declaration in a for (;;) statement (which Junio had as a fixup! that I almost missed).
- Renamedbranch --diff, which had been renamed frombranch-diff(which was picked to avoid re-usingtbdiff) torange-diff.
- Renamedhungarian.cand its header tolinear-assignment.c
- Made--dual-colorthe default, and changed it to still auto-detect whether color should be used rather than forcing itJohannes Schindelin (20):
linear-assignment: a function to solve least-cost assignment problems Introducerange-diffto compare iterations of a topic branch
range-diff: first rudimentary implementation
range-diff: improve the order of the shown commits
range-diff: also show the diff between patches
range-diff: right-trim commit messages
range-diff: indent the diffs just like tbdiff
range-diff: suppress the diff headers
range-diff: adjust the output of the commit pairs
range-diff: do not show "function names" in hunk headers
range-diff: use color for the commit pairs
color: add the meta colorGIT_COLOR_REVERSE
diff: add an internal option to dual-color diffs of diffs
range-diff: offer to dual-color the diffs
range-diff --dual-color: fix bogus white-space warning
range-diff: populate the man page
completion: supportgit range-diff
range-diff: left-pad patch numbers
range-diff: make --dual-color the default mode
range-diff: use dim/bold cues to improve dual color modeThomas Rast (1):
range-diff: add tests.gitignore | 1 + Documentation/config.txt | 6 +- Documentation/git-range-diff.txt | 252 +++++++++++ Makefile | 3 + builtin.h | 1 + builtin/range-diff.c | 106 +++++ cache.h | 3 +- color.h | 7 + command-list.txt | 1 + contrib/completion/git-completion.bash | 14 + diff.c | 119 ++++- diff.h | 16 +- git.c | 1 + linear-assignment.c | 201 ++++++++ linear-assignment.h | 22 + range-diff.c | 440 ++++++++++++++++++ range-diff.h | 9 + t/.gitattributes | 1 + t/t3206-range-diff.sh | 145 ++++++ t/t3206/history.export | 604 +++++++++++++++++++++++++ ws.c | 11 +- 21 files changed, 1932 insertions(+), 31 deletions(-) create mode 100644 Documentation/git-range-diff.txt create mode 100644 builtin/range-diff.c create mode 100644 linear-assignment.c create mode 100644 linear-assignment.h create mode 100644 range-diff.c create mode 100644 range-diff.h create mode 100755 t/t3206-range-diff.sh create mode 100644 t/t3206/history.exportbase-commit:
b7bd9486b055c3f967a870311e704e3bb0654e4f
Published-As: https://github.com/gitgitgadget/git/releases/tags/pr-1%2Fdscho%2Fbranch-diff-v4
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1/dscho/branch-diff-v4
Pull-Request: https://github.com/gitgitgadget/git/pull/1Range-diff vs v3:
1: 39272eefc ! 1: f7e70689e linear-assignment: a function to solve least-cost assignment problems @@ -223,9 +223,7 @@ + BUG("negative j: %d", j); + i = pred[j]; + column2row[j] = i; -+ k = j; -+ j = row2column[i]; -+ row2column[i] = k; ++ SWAP(j, row2column[i]); + } while (i1 != i); + } + 2: 7f15b26d4 ! 2: 88134121d Introduce `range-diff` to compare iterations of a topic branch @@ -10,6 +10,10 @@ At this point, we ignore tbdiff's color options, as they will all be implemented later using diff_options.+ Since f318d739159 (generate-cmds.sh: export all commands to + command-list.h, 2018-05-10), every new command *requires* a man page to + build right away, so let's also add a blank man page, too. + Signed-off-by: Johannes Schindelin <[email protected]> diff --git a/.gitignore b/.gitignore @@ -24,6 +28,22 @@ /git-rebase /git-rebase--am +diff --git a/Documentation/git-range-diff.txt b/Documentation/git-range-diff.txt +new file mode 100644 +--- /dev/null ++++ b/Documentation/git-range-diff.txt +@@ ++git-range-diff(1) ++================== ++ ++NAME ++---- ++git-range-diff - Compare two commit ranges (e.g. two versions of a branch) ++ ++GIT ++--- ++Part of the linkgit:git[1] suite + diff --git a/Makefile b/Makefile --- a/Makefile +++ b/Makefile 3: 076e1192d ! 3: 4e3fb47a1 range-diff: first rudimentary implementation @@ -4,7 +4,7 @@ At this stage, `git range-diff` can determine corresponding commits of two related commit ranges. This makes use of the recently introduced - implementation of the Hungarian algorithm. + implementation of the linear assignment algorithm. The core of this patch is a straight port of the ideas of tbdiff, the apparently dormant project at https://github.com/trast/tbdiff. @@ -51,19 +51,17 @@ + int res = 0; + struct strbuf range1 = STRBUF_INIT, range2 = STRBUF_INIT; -- argc = parse_options(argc, argv, NULL, options, -- builtin_range_diff_usage, 0); -+ argc = parse_options(argc, argv, NULL, options, builtin_range_diff_usage, -+ 0); + argc = parse_options(argc, argv, NULL, options, + builtin_range_diff_usage, 0); - return 0; + if (argc == 2) { + if (!strstr(argv[0], "..")) -+ warning(_("no .. in range: '%s'"), argv[0]); ++ die(_("no .. in range: '%s'"), argv[0]); + strbuf_addstr(&range1, argv[0]); + + if (!strstr(argv[1], "..")) -+ warning(_("no .. in range: '%s'"), argv[1]); ++ die(_("no .. in range: '%s'"), argv[1]); + strbuf_addstr(&range2, argv[1]); + } else if (argc == 3) { + strbuf_addf(&range1, "%s..%s", argv[0], argv[1]); @@ -195,17 +193,21 @@ + continue; + } else if (starts_with(line.buf, "@@ ")) + strbuf_addstr(&buf, "@@"); -+ else if (line.buf[0] && !starts_with(line.buf, "index ")) ++ else if (!line.buf[0] || starts_with(line.buf, "index ")) + /* + * A completely blank (not ' \n', which is context) + * line is not valid in a diff. We skip it + * silently, because this neatly handles the blank + * separator line between commits in git-log + * output. ++ * ++ * We also want to ignore the diff's `index` lines ++ * because they contain exact blob hashes in which ++ * we are not interested. + */ -+ strbuf_addbuf(&buf, &line); -+ else + continue; ++ else ++ strbuf_addbuf(&buf, &line); + + strbuf_addch(&buf, '\n'); + util->diffsize++; 4: e98489c8c = 4: 47bee09b0 range-diff: improve the order of the shown commits 5: 935cad180 ! 5: 94afaeaf2 range-diff: also show the diff between patches @@ -55,21 +55,22 @@ + int i, j, res = 0; struct strbuf range1 = STRBUF_INIT, range2 = STRBUF_INIT; -- argc = parse_options(argc, argv, NULL, options, builtin_range_diff_usage, -- 0); + git_config(git_diff_ui_config, NULL); + + diff_setup(&diffopt); + diffopt.output_format = DIFF_FORMAT_PATCH; + -+ argc = parse_options(argc, argv, NULL, options, -+ builtin_range_diff_usage, PARSE_OPT_KEEP_UNKNOWN); + argc = parse_options(argc, argv, NULL, options, +- builtin_range_diff_usage, 0); ++ builtin_range_diff_usage, PARSE_OPT_KEEP_UNKNOWN); + -+ for (i = j = 0; i < argc; i++) { ++ for (i = j = 0; i < argc; ) { + int c = diff_opt_parse(&diffopt, argv + i, argc - i, prefix); + + if (!c) -+ argv[j++] = argv[i]; ++ argv[j++] = argv[i++]; ++ else ++ i += c; + } + argc = j; + diff_setup_done(&diffopt); 6: 93ac1931d = 6: 41ab875a3 range-diff: right-trim commit messages 7: ca5282815 ! 7: a3dd99509 range-diff: indent the diffs just like tbdiff @@ -39,7 +39,7 @@ + diffopt.output_prefix_data = &four_spaces; argc = parse_options(argc, argv, NULL, options, - builtin_range_diff_usage, PARSE_OPT_KEEP_UNKNOWN); + builtin_range_diff_usage, PARSE_OPT_KEEP_UNKNOWN); @@ strbuf_release(&range1); 8: 80622685f = 8: 61b2ff2f7 range-diff: suppress the diff headers 9: 6b31cbf72 ! 9: 9641ab5c0 range-diff: adjust the output of the commit pairs @@ -26,25 +26,22 @@ -static const char *short_oid(struct patch_util *util) +static void output_pair_header(struct strbuf *buf, ++ struct strbuf *dashes, + struct patch_util *a_util, + struct patch_util *b_util) { - return find_unique_abbrev(&util->oid, DEFAULT_ABBREV); -+ static char *dashes; + struct object_id *oid = a_util ? &a_util->oid : &b_util->oid; + struct commit *commit; + -+ if (!dashes) { -+ char *p; -+ -+ dashes = xstrdup(find_unique_abbrev(oid, DEFAULT_ABBREV)); -+ for (p = dashes; *p; p++) -+ *p = '-'; -+ } ++ if (!dashes->len) ++ strbuf_addchars(dashes, '-', ++ strlen(find_unique_abbrev(oid, ++ DEFAULT_ABBREV))); + + strbuf_reset(buf); + if (!a_util) -+ strbuf_addf(buf, "-: %s ", dashes); ++ strbuf_addf(buf, "-: %s ", dashes->buf); + else + strbuf_addf(buf, "%d: %s ", a_util->i + 1, + find_unique_abbrev(&a_util->oid, DEFAULT_ABBREV)); @@ -59,7 +56,7 @@ + strbuf_addch(buf, '='); + + if (!b_util) -+ strbuf_addf(buf, " -: %s", dashes); ++ strbuf_addf(buf, " -: %s", dashes->buf); + else + strbuf_addf(buf, " %d: %s", b_util->i + 1, + find_unique_abbrev(&b_util->oid, DEFAULT_ABBREV)); @@ -84,7 +81,7 @@ static void output(struct string_list *a, struct string_list *b, struct diff_options *diffopt) { -+ struct strbuf buf = STRBUF_INIT; ++ struct strbuf buf = STRBUF_INIT, dashes = STRBUF_INIT; int i = 0, j = 0; /* @@ -94,7 +91,7 @@ if (i < a->nr && a_util->matching < 0) { - printf("%d: %s < -: --------\n", - i + 1, short_oid(a_util)); -+ output_pair_header(&buf, a_util, NULL); ++ output_pair_header(&buf, &dashes, a_util, NULL); i++; continue; } @@ -103,7 +100,7 @@ while (j < b->nr && b_util->matching < 0) { - printf("-: -------- > %d: %s\n", - j + 1, short_oid(b_util)); -+ output_pair_header(&buf, NULL, b_util); ++ output_pair_header(&buf, &dashes, NULL, b_util); b_util = ++j < b->nr ? b->items[j].util : NULL; } @@ -113,7 +110,7 @@ - printf("%d: %s ! %d: %s\n", - b_util->matching + 1, short_oid(a_util), - j + 1, short_oid(b_util)); -+ output_pair_header(&buf, a_util, b_util); ++ output_pair_header(&buf, &dashes, a_util, b_util); if (!(diffopt->output_format & DIFF_FORMAT_NO_OUTPUT)) patch_diff(a->items[b_util->matching].string, b->items[j].string, diffopt); @@ -122,6 +119,7 @@ } } + strbuf_release(&buf); ++ strbuf_release(&dashes); } int show_range_diff(const char *range1, const char *range2, 10: ef997bb8b = 10: 0a52f8878 range-diff: do not show "function names" in hunk headers 11: 3d9e5b0ba ! 11: 2b8d09020 range-diff: add tests@@ -3,8 +3,8 @@
range-diff: add testsThese are essentially lifted from https://github.com/trast/tbdiff, with
- light touch-ups to account for the command now being an option of `git
- branch`.
+ light touch-ups to account for the command now being names `git
+ range-diff`.Apart from renaming
tbdifftorange-diff, only one test case needed to be adjusted: 11 - 'changed message'.
@@ -22,12 +22,13 @@
--- a/t/.gitattributes
+++ b/t/.gitattributes
@@
- /t5515/* eol=lf
- /t556x_common eol=lf
- /t7500/* eol=lf
-+/t7910/* eol=lf
- /t8005/*.txt eol=lf
- /t9*/*.dump eol=lf
+ t[0-9][0-9][0-9][0-9]/* -whitespace
+ /diff-lib/* eol=lf
+ /t0110/url-* binary
++/t3206/* eol=lf
+ /t3900/*.txt eol=lf
+ /t3901/*.txt eol=lf
+ /t4034/*/* eol=lfdiff --git
a/t/t3206-range-diff.shb/t/t3206-range-diff.shnew file mode 100755
12:7273cc647! 12:fb83ce71arange-diff: use color for the commit pairs@@ -20,11 +20,12 @@ } -static void output_pair_header(struct strbuf *buf, -+static void output_pair_header(struct diff_options *diffopt, struct strbuf *buf, ++static void output_pair_header(struct diff_options *diffopt, ++ struct strbuf *buf, + struct strbuf *dashes, struct patch_util *a_util, struct patch_util *b_util) { - static char *dashes; struct object_id *oid = a_util ? &a_util->oid : &b_util->oid; struct commit *commit; + char status; @@ -34,11 +35,10 @@ + const char *color_commit = diff_get_color_opt(diffopt, DIFF_COMMIT); + const char *color; - if (!dashes) { - char *p; -@@ - *p = '-'; - } + if (!dashes->len) + strbuf_addchars(dashes, '-', + strlen(find_unique_abbrev(oid, + DEFAULT_ABBREV))); + if (!b_util) { + color = color_old; @@ -57,7 +57,7 @@ strbuf_reset(buf); + strbuf_addstr(buf, status == '!' ? color_old : color); if (!a_util) - strbuf_addf(buf, "-: %s ", dashes); + strbuf_addf(buf, "-: %s ", dashes->buf); else strbuf_addf(buf, "%d: %s ", a_util->i + 1, find_unique_abbrev(&a_util->oid, DEFAULT_ABBREV)); @@ -77,7 +77,7 @@ + strbuf_addf(buf, "%s%s", color_reset, color_new); if (!b_util) - strbuf_addf(buf, " -: %s", dashes); + strbuf_addf(buf, " -: %s", dashes->buf); @@ const char *commit_buffer = get_commit_buffer(commit, NULL); const char *subject; @@ -99,24 +99,27 @@ /* Show unmatched LHS commit whose predecessors were shown. */ if (i < a->nr && a_util->matching < 0) { -- output_pair_header(&buf, a_util, NULL); -+ output_pair_header(diffopt, &buf, a_util, NULL); +- output_pair_header(&buf, &dashes, a_util, NULL); ++ output_pair_header(diffopt, ++ &buf, &dashes, a_util, NULL); i++; continue; } /* Show unmatched RHS commits. */ while (j < b->nr && b_util->matching < 0) { -- output_pair_header(&buf, NULL, b_util); -+ output_pair_header(diffopt, &buf, NULL, b_util); +- output_pair_header(&buf, &dashes, NULL, b_util); ++ output_pair_header(diffopt, ++ &buf, &dashes, NULL, b_util); b_util = ++j < b->nr ? b->items[j].util : NULL; } /* Show matching LHS/RHS pair. */ if (j < b->nr) { a_util = a->items[b_util->matching].util; -- output_pair_header(&buf, a_util, b_util); -+ output_pair_header(diffopt, &buf, a_util, b_util); +- output_pair_header(&buf, &dashes, a_util, b_util); ++ output_pair_header(diffopt, ++ &buf, &dashes, a_util, b_util); if (!(diffopt->output_format & DIFF_FORMAT_NO_OUTPUT)) patch_diff(a->items[b_util->matching].string, b->items[j].string, diffopt); 13: 96a3073fb = 13: 9ccb9516a color: add the meta color GIT_COLOR_REVERSE 14: 6be4baf60 = 14: 9de5bd229 diff: add an internal option to dual-color diffs of diffs 15: 02e13c0c6 ! 15: 21b2f9e4b range-diff: offer to dual-color the diffs @@ -40,4 +40,4 @@ + if (argc == 2) { if (!strstr(argv[0], "..")) - warning(_("no .. in range: '%s'"), argv[0]); + die(_("no .. in range: '%s'"), argv[0]); 16: dfa7b1e71 < -: --------- range-diff --dual-color: work around bogus white-space warning -: --------- > 16: f4252f2b2 range-diff --dual-color: fix bogus white-space warning 17: 799da25ef ! 17: 9e09c6be6 range-diff: add a man page@@ -1,6 +1,6 @@
Author: Johannes Schindelin <[email protected]>- range-diff: add a man page
+ range-diff: populate the man pageThe bulk of this patch consists of a heavily butchered version of tbdiff's README written by Thomas Rast and Thomas Gummerer, lifted from
@@ -9,17 +9,12 @@Signed-off-by Johannes Schindelin <[email protected]>diff --git
a/Documentation/git-range-diff.txtb/Documentation/git-range-diff.txt
-new file mode 100644
----/dev/null
+---a/Documentation/git-range-diff.txt
+++b/Documentation/git-range-diff.txt
@@
-+git-range-diff(1)
-+==================
-+
-+NAME
-+----
-+git-range-diff - Compare two commit ranges (e.g. two versions of a branch) -+
+ ----
+ git-range-diff - Compare two commit ranges (e.g. two versions of a branch) +
+SYNOPSIS
+--------
+[verse]
@@ -31,13 +26,13 @@
+-----------
+
+This command shows the differences between two versions of a patch
-+series, or more generally, two commit ranges (ignoring merges). ++series, or more generally, two commit ranges (ignoring merge commits).
+
+To that end, it first finds pairs of commits from both commit ranges +that correspond with each other. Two commits are said to correspond when +the diff between their patches (i.e. the author information, the commit +message and the commit diff) is reasonably small compared to the
-+patches' size. See ``Algorithm` below for details.
++patches' size. See ``Algorithm`` below for details.
+
+Finally, the list of matching commits is shown in the order of the +second commit range, with unmatched commits being inserted just after
@@ -150,6 +145,10 @@
+The general idea is this: we generate a cost matrix between the commits +in both commit ranges, then solve the least-cost assignment.
+
++The cost matrix is populated thusly: for each pair of commits, both ++diffs are generated and the "diff of diffs" is generated, with 3 context ++lines, then the number of lines in that diff is used as cost.
++
+To avoid false positives (e.g. when a patch has been removed, and an +unrelated patch has been added between two iterations of the same patch +series), the cost matrix is extended to allow for that, by adding
@@ -245,6 +244,6 @@
+--------
+linkgit:git-log[1]
+
-+GIT
-+---
-+Part of the linkgit:git[1] suite
+ GIT
+ ---
+ Part of the linkgit:git[1] suite
18:d05b54c60! 18:9b3632324completion: supportgit range-diff
@@ -4,17 +4,11 @@Tab completion of
git range-diffis very convenient, especially given that the revision arguments to specify the commit ranges to
- compare are typically more complex than, say, your grandfather's `git
- log` arguments.
+ compare are typically more complex than, say, what is normally passed
+ togit log.Signed-off-by Johannes Schindelin <[email protected]>- squash! WIP completion: support
git range-diff
-
- Revert "WIP completion: supportgit range-diff"
-
- This reverts commit2e7af652af9e53a19fd947f8ebe37a78043afa49.
-
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash --- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash19: 144363006 < -: --------- range-diff: left-pad patch numbers -: --------- > 19: 07ec215e8 range-diff: left-pad patch numbers 20: 4a68b95ce ! 20: b370468e7 range-diff: make --dual-color the default mode@@ -4,7 +4,7 @@
After using this command extensively for the last two months, this developer came to the conclusion that even if the dual color mode still
- leaves a lot of room for confusion what was actually changed, the
+ leaves a lot of room for confusion about what was actually changed, the
non-dual color mode is substantially worse in that regard.Therefore, we really want to make the dual color mode the default.
@@ -14,6 +14,15 @@
diff --gita/Documentation/git-range-diff.txtb/Documentation/git-range-diff.txt---a/Documentation/git-range-diff.txt
+++b/Documentation/git-range-diff.txt
+@@
+ --------
+ [verse]
+ 'git range-diff' [--color=[<when>]] [--no-color] [<diff-options>]
+- [--dual-color] [--creation-factor=<factor>]
++ [--no-dual-color] [--creation-factor=<factor>]
+ ( <range1> <range2> | <rev1>...<rev2> | <base> <rev1> <rev2> )
+
+ DESCRIPTION
@@OPTIONS
@@ -25,7 +34,7 @@
- change in what exact lines were added.
+--no-dual-color::
+ When the commit diffs differ,git range-diffrecreates the
-+ original diffs' coloring, and add outer -/+ diff markers with
++ original diffs' coloring, and adds outer -/+ diff markers with
+ the *background* being red/green to make it easier to see e.g.
+ when there was a change in what exact lines were added. This is
+ known torange-diffas "dual coloring". Use--no-dual-color
@@ -34,6 +43,31 @@--creation-factor=<percent>::
Set the creation/deletion cost fudge factor to<percent>.
+@@
+ show`'s output, and the third line colors the old commit red, the new
+ one green and the rest likegit show's commit header.
+
+-The color-coded diff is actually a bit hard to read, though, as it +-colors the entire lines red or green. The line that added "What is +-unexpected" in the old commit, for example, is completely red, even if +-the intent of the old commit was to add something.
++A naive color-coded diff of diffs is actually a bit hard to read, ++though, as it colors the entire lines red or green. The line that added ++"What is unexpected" in the old commit, for example, is completely red, ++even if the intent of the old commit was to add something. +
+-To help with that, use the--dual-colormode. In this mode, the diff +-of diffs will retain the original diff colors, and prefix the lines with +--/+ markers that have their *background* red or green, to make it more +-obvious that they describe how the diff itself changed. ++To help with that,rangeuses the--dual-colormode by default. In ++this mode, the diff of diffs will retain the original diff colors, and ++prefix the lines with -/+ markers that have their *background* red or ++green, to make it more obvious that they describe how the diff itself ++changed.
+
+
+ Algorithmdiff --git
a/builtin/range-diff.cb/builtin/range-diff.c
---a/builtin/range-diff.c
-: --------- > 21:d8498fb32range-diff: use dim/bold cues to improve dual color modeSignature · 1 line
gitgitgadget