Add `branch-diff`, a `tbdiff` lookalike
Subsystems (2)
v5 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:
base=^{/Start.the.merging-rebase}
tag=v2.17.0.windows.1
pre=$tag$base^2
git range-diff $pre$base..$pre $tag$base..$tagThe 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 v4:
* Fixed a typo in the commit message of "range-diff: add tests" that was introduced in v4.
* White-space fixes.
* Fixed the length of the first header underline in the man page.
* Changed the preprocessor guard in linear-assignment.h to reflect the new name (instead of the old name, which was hungarian.h).
* Likewise, changed the preprocessor guards in range-diff.h to hide the history of the thrice-renamed command.
* Fixed indentation in the completion.
* Instead of trying to paper over white-space error handling that does not apply to "diffs of diffs", dual color mode now simply disables all white-space warnings.
* When showing the "single arg must be symmetric range" error message, git range-diff now also shows the usage.
* Adjusted the commit message of "range-diff: adjust the output of the commit pairs" to avoid the surprise of the reviewer when onelines are printed all of a sudden, too.
* "range-diff: adjust the output of the commit pairs" is now using a simpler way to print onelines.
* We are now sandwiching the diff_opt_parse() loop between two parse_options(), to make sure that we caught all options, and that the -- separator is handled.
* Adjusted the lookup_commit_reference() call to the newest master (it now takes a the_repository parameter).
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 option to --creation-factor where 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: skip white-space warnings
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 | 114 +++++ color.h | 7 + command-list.txt | 1 + contrib/completion/git-completion.bash | 14 + diff.c | 105 ++++- diff.h | 10 +- git.c | 1 + linear-assignment.c | 201 ++++++++ linear-assignment.h | 22 + range-diff.c | 435 ++++++++++++++++++ range-diff.h | 9 + t/.gitattributes | 1 + t/t3206-range-diff.sh | 145 ++++++ t/t3206/history.export | 604 +++++++++++++++++++++++++ 19 files changed, 1913 insertions(+), 19 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: 1d89318c48d233d52f1db230cf622935ac3c69fa
Published-As: https://github.com/gitgitgadget/git/releases/tags/pr-1%2Fdscho%2Fbranch-diff-v5
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1/dscho/branch-diff-v5
Pull-Request: https://github.com/gitgitgadget/git/pull/1
Range-diff vs v4:
1: f7e70689e ! 1: f168da3a3 linear-assignment: a function to solve least-cost assignment problems
@@ -239,8 +239,8 @@
--- /dev/null
+++ b/linear-assignment.h
@@
-+#ifndef HUNGARIAN_H
-+#define HUNGARIAN_H
++#ifndef LINEAR_ASSIGNMENT_H
++#define LINEAR_ASSIGNMENT_H
+
+/*
+ * Compute an assignment of columns -> rows (and vice versa) such that every
2: 88134121d ! 2: 33758f361 Introduce `range-diff` to compare iterations of a topic branch
@@ -34,7 +34,7 @@
+++ b/Documentation/git-range-diff.txt
@@
+git-range-diff(1)
-+==================
++=================
+
+NAME
+----
3: 4e3fb47a1 ! 3: 08b8c3fc4 range-diff: first rudimentary implementation
@@ -70,8 +70,10 @@
+ const char *b = strstr(argv[0], "..."), *a = argv[0];
+ int a_len;
+
-+ if (!b)
-+ die(_("single arg format requires a symmetric range"));
++ if (!b) {
++ error(_("single arg format must be symmetric range"));
++ usage_with_options(builtin_range_diff_usage, options);
++ }
+
+ a_len = (int)(b - a);
+ if (!a_len) {
@@ -418,8 +420,8 @@
--- /dev/null
+++ b/range-diff.h
@@
-+#ifndef BRANCH_DIFF_H
-+#define BRANCH_DIFF_H
++#ifndef RANGE_DIFF_H
++#define RANGE_DIFF_H
+
+int show_range_diff(const char *range1, const char *range2,
+ int creation_factor);
4: 47bee09b0 = 4: 7b9091968 range-diff: improve the order of the shown commits
5: 94afaeaf2 ! 5: 9e1e66007 range-diff: also show the diff between patches
@@ -10,7 +10,7 @@
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,
+ Especially when rebasing frequently, 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.
@@ -25,9 +25,17 @@
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().
+ these diffs between patches, we prefer the `-s` (or `--no-patch`) option
+ that is automatically supported via our use of diff_opt_parse().
+ And finally note: to support diff options, we have to call
+ `parse_options()` such that it keeps unknown options, and then loop over
+ those and let `diff_opt_parse()` handle them. After that loop, we have
+ to call `parse_options()` again, to make sure that no unknown options
+ are left.
+
+ Helped-by: Thomas Gummerer <[email protected]>
+ Helped-by: Eric Sunshine <[email protected]>
Signed-off-by: Johannes Schindelin <[email protected]>
diff --git a/builtin/range-diff.c b/builtin/range-diff.c
@@ -61,10 +69,10 @@
+ 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);
++ builtin_range_diff_usage, PARSE_OPT_KEEP_UNKNOWN |
++ PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0);
+
-+ for (i = j = 0; i < argc; ) {
++ for (i = j = 1; i < argc && strcmp("--", argv[i]); ) {
+ int c = diff_opt_parse(&diffopt, argv + i, argc - i, prefix);
+
+ if (!c)
@@ -74,9 +82,13 @@
+ }
+ argc = j;
+ diff_setup_done(&diffopt);
++
++ /* Make sure that there are no unparsed options */
++ argc = parse_options(argc, argv, NULL,
++ options + ARRAY_SIZE(options) - 1, /* OPT_END */
+ builtin_range_diff_usage, 0);
if (argc == 2) {
- if (!strstr(argv[0], ".."))
@@
usage_with_options(builtin_range_diff_usage, options);
}
@@ -165,8 +177,8 @@
--- a/range-diff.h
+++ b/range-diff.h
@@
- #ifndef BRANCH_DIFF_H
- #define BRANCH_DIFF_H
+ #ifndef RANGE_DIFF_H
+ #define RANGE_DIFF_H
+#include "diff.h"
+
6: 41ab875a3 = 6: 167ca02a3 range-diff: right-trim commit messages
7: a3dd99509 ! 7: ca8de8c75 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: 61b2ff2f7 = 8: eb94d1982 range-diff: suppress the diff headers
9: 9641ab5c0 ! 9: 6330afad9 range-diff: adjust the output of the commit pairs
@@ -2,6 +2,10 @@
range-diff: adjust the output of the commit pairs
+ This not only uses "dashed stand-ins" for "pairs" where one side is
+ missing (i.e. unmatched commits that are present only in one of the two
+ commit ranges), but also adds onelines for the reader's pleasure.
+
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.
@@ -61,15 +65,10 @@
+ strbuf_addf(buf, " %d: %s", b_util->i + 1,
+ find_unique_abbrev(&b_util->oid, DEFAULT_ABBREV));
+
-+ commit = lookup_commit_reference(oid);
++ commit = lookup_commit_reference(the_repository, 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);
++ pp_commit_easy(CMIT_FMT_ONELINE, commit, buf);
+ }
+ strbuf_addch(buf, '\n');
+
10: 0a52f8878 = 10: c296675eb range-diff: do not show "function names" in hunk headers
11: 2b8d09020 ! 11: 85e0ab82f range-diff: add tests @@ -3,7 +3,7 @@
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 names `git
+ light touch-ups to account for the command now being named `git
range-diff`.
Apart from renaming tbdiff to range-diff, only one test case needed
12: fb83ce71a ! 12: f48b62644 range-diff: use color for the commit pairs
@@ -79,16 +79,14 @@
if (!b_util)
strbuf_addf(buf, " -: %s", dashes->buf);
@@
- const char *commit_buffer = get_commit_buffer(commit, NULL);
- const char *subject;
+ commit = lookup_commit_reference(the_repository, oid);
+ if (commit) {
+ 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);
+ pp_commit_easy(CMIT_FMT_ONELINE, commit, buf);
}
- strbuf_addch(buf, '\n');
+ strbuf_addf(buf, "%s\n", color_reset);
13: 9ccb9516a = 13: 1ad74f939 color: add the meta color GIT_COLOR_REVERSE
14: 9de5bd229 = 14: 39a0ecd28 diff: add an internal option to dual-color diffs of diffs
15: 21b2f9e4b ! 15: c32a24f6a range-diff: offer to dual-color the diffs
@@ -30,8 +30,8 @@
};
int i, j, res = 0;
@@
- argc = j;
- diff_setup_done(&diffopt);
+ options + ARRAY_SIZE(options) - 1, /* OPT_END */
+ builtin_range_diff_usage, 0);
+ if (dual_color) {
+ diffopt.use_color = 1;
16: f4252f2b2 < -: --------- range-diff --dual-color: fix bogus white-space warning
-: --------- > 16: 05947781f range-diff --dual-color: skip white-space warnings
17: 9e09c6be6 = 17: 3147c4440 range-diff: populate the man page
18: 9b3632324 ! 18: b08e6d937 completion: support `git range-diff`
@@ -18,16 +18,16 @@
+_git_range_diff ()
+{
-+ case "$cur" in
-+ --*)
-+ __gitcomp "
-+ --creation-factor= --dual-color
-+ $__git_diff_common_options
-+ "
-+ return
-+ ;;
-+ esac
-+ __git_complete_revlist
++ case "$cur" in
++ --*)
++ __gitcomp "
++ --creation-factor= --dual-color
++ $__git_diff_common_options
++ "
++ return
++ ;;
++ esac
++ __git_complete_revlist
+}
+
_git_rebase ()
19: 07ec215e8 ! 19: 19406283e range-diff: left-pad patch numbers
@@ -48,7 +48,7 @@
+ 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);
+ commit = lookup_commit_reference(the_repository, oid);
@@
struct diff_options *diffopt)
{ 20: b370468e7 ! 20: 6b3552386 range-diff: make --dual-color the default mode
@@ -87,8 +87,8 @@
OPT_END()
};
@@
- argc = j;
- diff_setup_done(&diffopt);
+ options + ARRAY_SIZE(options) - 1, /* OPT_END */
+ builtin_range_diff_usage, 0);
- if (dual_color) {
- diffopt.use_color = 1;
@@ -104,11 +104,11 @@
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@
- case "$cur" in
- --*)
- __gitcomp "
-- --creation-factor= --dual-color
-+ --creation-factor= --no-dual-color
- $__git_diff_common_options
- "
- return
+ case "$cur" in
+ --*)
+ __gitcomp "
+- --creation-factor= --dual-color
++ --creation-factor= --no-dual-color
+ $__git_diff_common_options
+ "
+ return
21: d8498fb32 ! 21: ccf8c1bb2 range-diff: use dim/bold cues to improve dual color mode
@@ -144,7 +144,7 @@
+ 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;
+ flags &= ~DIFF_SYMBOL_CONTENT_WS_MASK;
}
emit_line_ws_markup(o, set, reset, line, len, set_sign, '+',
@@Signature · 1 line
gitgitgadget
PATCH v5 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
@@ -870,6 +870,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 LINEAR_ASSIGNMENT_H +#define LINEAR_ASSIGNMENT_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
PATCH v5 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
@@ -1063,6 +1063,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
@@ -520,6 +520,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 v5 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 | 45 ++++++- range-diff.c | 311 +++++++++++++++++++++++++++++++++++++++++++ range-diff.h | 7 + 4 files changed, 363 insertions(+), 1 deletion(-) create mode 100644 range-diff.c create mode 100644 range-diff.h
Makefile +1 -0
@@ -925,6 +925,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 -1
59 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 RANGE_DIFF_H +#define RANGE_DIFF_H + +int show_range_diff (const char *range1,const char *range2, +int creation_factor); + +#endif
Signature · 1 line
gitgitgadget
PATCH v5 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 v5 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 frequently, 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 (or --no-patch) option that is automatically supported via our use of diff_opt_parse().
And finally note: to support diff options, we have to call parse_options() such that it keeps unknown options, and then loop over those and let diff_opt_parse() handle them. After that loop, we have to call parse_options() again, to make sure that no unknown options are left.
--- builtin/range-diff.c | 29 +++++++++++++++++++++++++++-- range-diff.c | 34 +++++++++++++++++++++++++++++++--- range-diff.h | 4 +++- 3 files changed, 61 insertions(+), 6 deletions(-)
builtin/range-diff.c +27 -2
@@ -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,15 +14,38 @@ 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,PARSE_OPT_KEEP_UNKNOWN | +PARSE_OPT_KEEP_DASHDASH |PARSE_OPT_KEEP_ARGV0 ); + +for (i = j =1 ; i < argc &&strcmp ("--" , argv[i]); ) { +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); + +/* Make sure that there are no unparsed options */ + argc =parse_options (argc, argv,NULL , + options +ARRAY_SIZE (options) -1 ,/* OPT_END */ builtin_range_diff_usage,0 );if (argc ==2 ) {
@@ -59,7 +83,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 RANGE_DIFF_H #define RANGE_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 v5 05/21] range-diff: also show the diff between patches
Hi Dscho,
Quoted 25 lines · Johannes Schindelin via GitGitGadget
From: Johannes Schindelin <[email protected]>
[..]
(quoted diff) +8
-1@@ -13,15 +14,38 @@ NULL int 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, PARSE_OPT_KEEP_UNKNOWN |
Review NULL · line 42
Quoted 10 lines
(quoted diff) +10
-0+ PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0); + + for (i = j = 1; i < argc && strcmp("--", argv[i]); ) { + int c = diff_opt_parse(&diffopt, argv + i, argc - i, prefix); + + if (!c) + argv[j++] = argv[i++]; + else + i += c; + }
I don't think this handles "--" quite as would be expected. Trying to use "git range-diff -- js/range-diff-v4...HEAD" I get:
$ ./git range-diff -- js/range-diff-v4...HEAD
error: need two commit ranges
usage: git range-diff [<options>] <old-base>..<old-tip> <new-base>..<new-tip>
or: git range-diff [<options>] <old-tip>...<new-tip>
or: git range-diff [<options>] <base> <old-tip> <new-tip> --creation-factor <n>
Percentage by which creation is weighted
--no-dual-color color both diff and diff-between-diffs
while what I would have expected is to actually get a range diff. This happens because after we break out of the loop we don't add the actual ranges to argv, but just skip them instead.
I think something like the following should be squashed in to this patch.
--->8---
builtin/range-diff.c +5 -0
@@ -53,6 +53,11 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix)else i += c; } +if (i < argc && !strcmp ("--" , argv[i])) { + i++; j++; +while (i < argc) + argv[j++] = argv[i++]; + } argc = j;diff_setup_done (&diffopt);
--->8---
Quoted 110 lines
(quoted diff) +8
-1+ argc = j; + diff_setup_done(&diffopt); + + /* Make sure that there are no unparsed options */ + argc = parse_options(argc, argv, NULL, + options + ARRAY_SIZE(options) - 1, /* OPT_END */ builtin_range_diff_usage, 0); if (argc == 2) {@@ -59,7 +83,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 RANGE_DIFF_H #define RANGE_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 v5 05/21] range-diff: also show the diff between patches
Hi Thomas,
Quoted 46 lines · Thomas Gummerer
Quoted 25 lines · Johannes Schindelin via GitGitGadget
From: Johannes Schindelin <[email protected]>
[..]
(quoted diff) +8
-1@@ -13,15 +14,38 @@ NULL int 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, PARSE_OPT_KEEP_UNKNOWN |Review NULL · line 42
Quoted 10 lines
(quoted diff) +10
-0+ PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0); + + for (i = j = 1; i < argc && strcmp("--", argv[i]); ) { + int c = diff_opt_parse(&diffopt, argv + i, argc - i, prefix); + + if (!c) + argv[j++] = argv[i++]; + else + i += c; + }I don't think this handles "--" quite as would be expected. Trying to use "git range-diff -- js/range-diff-v4...HEAD" I get:
$ ./git range-diff -- js/range-diff-v4...HEAD error: need two commit ranges usage: git range-diff [<options>] <old-base>..<old-tip> <new-base>..<new-tip> or: git range-diff [<options>] <old-tip>...<new-tip> or: git range-diff [<options>] <base> <old-tip> <new-tip>
Quoted 7 lines
--creation-factor <n>
Percentage by which creation is weighted
--no-dual-color color both diff and diff-between-diffswhile what I would have expected is to actually get a range diff. This happens because after we break out of the loop we don't add the actual ranges to argv, but just skip them instead.
Ouch, good point.
Quoted 9 lines
I think something like the following should be squashed in to this patch.
--->8---
builtin/range-diff.c +0
-0
builtin/range-diff.c int cmd_range_diff(int argc, const char **argv, const char *prefix) · line 62
Quoted 10 lines
builtin/range-diff.c +5
-0@@ -53,6 +53,11 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix)else i += c; } +if (i < argc && !strcmp ("--" , argv[i])) { + i++; j++; +while (i < argc) + argv[j++] = argv[i++]; + } argc = j;diff_setup_done (&diffopt);
I do not think that is correct. The original idea was for the first parse_options() call to keep the dashdash, for the second one to keep the dashdash, too, and for the final one to swallow it.
Also, if i < argc at this point, we already know that argv[i] refers to the dashdash, otherwise the previous loop would not have exited early.
I went with this simple version instead:
while (i < argc) argv[j++] = argv[i++];
Thanks!
Dscho
Quoted 113 lines
builtin/range-diff.c +0
-1--->8---Quoted 111 lines
(quoted diff) +8
-1+ argc = j; + diff_setup_done(&diffopt); + + /* Make sure that there are no unparsed options */ + argc = parse_options(argc, argv, NULL, + options + ARRAY_SIZE(options) - 1, /* OPT_END */ builtin_range_diff_usage, 0); if (argc == 2) {@@ -59,7 +83,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 RANGE_DIFF_H #define RANGE_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 v5 05/21] range-diff: also show the diff between patches
Quoted 52 lines · Johannes Schindelin
Hi Thomas,
Quoted 14 lines · Thomas Gummerer
Quoted 1 line · Johannes Schindelin via GitGitGadget
From: Johannes Schindelin <[email protected]>
[...]
I don't think this handles "--" quite as would be expected. Trying to use "git range-diff -- js/range-diff-v4...HEAD" I get:
$ ./git range-diff -- js/range-diff-v4...HEAD error: need two commit ranges usage: git range-diff [<options>] <old-base>..<old-tip> <new-base>..<new-tip> or: git range-diff [<options>] <old-tip>...<new-tip> or: git range-diff [<options>] <base> <old-tip> <new-tip>Quoted 7 lines
--creation-factor <n>
Percentage by which creation is weighted
--no-dual-color color both diff and diff-between-diffswhile what I would have expected is to actually get a range diff. This happens because after we break out of the loop we don't add the actual ranges to argv, but just skip them instead.
Ouch, good point.
Quoted 9 lines
I think something like the following should be squashed in to this patch.
--->8---
builtin/range-diff.c +0
-0builtin/range-diff.c int cmd_range_diff(int argc, const char **argv, const char *prefix) · line 62
Quoted 10 lines
builtin/range-diff.c +5
-0@@ -53,6 +53,11 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix)else i += c; } +if (i < argc && !strcmp ("--" , argv[i])) { + i++; j++; +while (i < argc) + argv[j++] = argv[i++]; + } argc = j;diff_setup_done (&diffopt);I do not think that is correct. The original idea was for the first
parse_options()call to keep the dashdash, for the second one to keep the dashdash, too, and for the final one to swallow it.
Quoted 7 lines
Also, if
i < argcat this point, we already know thatargv[i]refers to the dashdash, otherwise the previous loop would not have exited early.I went with this simple version instead:
while (i < argc) argv[j++] = argv[i++];
Right, that's much better, thanks!
Quoted 2 lines
Thanks!
Dscho
PATCH v5 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 v5 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 |
@@ -88,6 +97,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 v5 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
@@ -3395,13 +3395,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 v5 9/21 range-diff: adjust the output of the commit pairs
In reply to Johannes Schindelin via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
This not only uses "dashed stand-ins" for "pairs" where one side is missing (i.e. unmatched commits that are present only in one of the two commit ranges), but also adds onelines for the reader's pleasure.
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 | 59 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 9 deletions(-)
range-diff.c +50 -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,49 @@ 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 (the_repository, oid); +if (commit) { +strbuf_addch (buf,' ' ); +pp_commit_easy (CMIT_FMT_ONELINE , commit, buf); + } +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 +328,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 +350,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 +372,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
PATCH v5 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 */
@@ -302,6 +303,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);
@@ -311,6 +316,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 v5 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 named 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
PATCH v5 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);
@@ -295,10 +314,13 @@ static void output_pair_header(struct strbuf *buf, commit =lookup_commit_reference (the_repository, oid);if (commit) { +if (status =='!' ) +strbuf_addf (buf,"%s%s" , color_reset, color); +strbuf_addch (buf,' ' );pp_commit_easy (CMIT_FMT_ONELINE , commit, buf); } -strbuf_addch (buf,'\n' ); +strbuf_addf (buf,"%s\n" , color_reset);fwrite (buf->buf, buf->len,1 , stdout); }
@@ -356,21 +378,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 v5 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 v5 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
@@ -609,14 +609,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' );
@@ -634,8 +638,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);
@@ -649,7 +655,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 {
@@ -1168,7 +1174,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 ;
@@ -1179,14 +1186,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); }
@@ -1196,7 +1209,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;
@@ -1209,7 +1222,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 :
@@ -1236,7 +1249,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 :
@@ -1263,7 +1287,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 ;
@@ -1291,7 +1328,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 :
@@ -1482,6 +1532,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 ;
@@ -1502,6 +1553,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
PATCH v5 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 ;
@@ -58,6 +61,11 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix) options +ARRAY_SIZE (options) -1 ,/* OPT_END */ builtin_range_diff_usage, 0); +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 v5 16/21 range-diff --dual-color: skip white-space warnings
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.
Rather than adding a specific whitespace flag that specifically ignores the first space in the output (and might miss other problems with the white-space warnings), let's just skip handling white-space errors in dual color mode to begin with.
--- diff.c | 1 + 1 file changed, 1 insertion(+)
diff.c +1 -0
@@ -1299,6 +1299,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 &= ~DIFF_SYMBOL_CONTENT_WS_MASK ; } emit_line_ws_markup(o, set, reset, line, len, set_sign, '+', flags & DIFF_SYMBOL_CONTENT_WS_MASK,
Signature · 1 line
gitgitgadget
PATCH v5 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
PATCH v5 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
PATCH v5 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 (the_repository, oid);
@@ -357,6 +358,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 ;/*
@@ -378,7 +380,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 ;
@@ -386,7 +388,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 ; }
@@ -394,7 +396,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 v5 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 () };
@@ -61,8 +61,10 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix) options +ARRAY_SIZE (options) -1 ,/* OPT_END */ builtin_range_diff_usage, 0); -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
PATCH v5 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
@@ -1193,8 +1193,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
@@ -70,6 +70,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[] = {
@@ -89,6 +95,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)
@@ -1294,11 +1306,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 &= ~DIFF_SYMBOL_CONTENT_WS_MASK ; } emit_line_ws_markup(o, set, reset, line, len, set_sign, '+',
@@ -1336,11 +1350,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
@@ -248,7 +248,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
v6 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:
base=^{/Start.the.merging-rebase}
tag=v2.17.0.windows.1
pre=$tag$base^2
git range-diff $pre$base..$pre $tag$base..$tagThe 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 v4:
* Fixed a typo in the commit message of "range-diff: add tests" that was introduced in v4.
* White-space fixes.
* Fixed the length of the first header underline in the man page.
* Changed the preprocessor guard in linear-assignment.h to reflect the new name (instead of the old name, which was hungarian.h).
* Likewise, changed the preprocessor guards in range-diff.h to hide the history of the thrice-renamed command.
* Fixed indentation in the completion.
* Instead of trying to paper over white-space error handling that does not apply to "diffs of diffs", dual color mode now simply disables all white-space warnings.
* When showing the "single arg must be symmetric range" error message, git range-diff now also shows the usage.
* Adjusted the commit message of "range-diff: adjust the output of the commit pairs" to avoid the surprise of the reviewer when onelines are printed all of a sudden, too.
* "range-diff: adjust the output of the commit pairs" is now using a simpler way to print onelines.
* We are now sandwiching the diff_opt_parse() loop between two parse_options(), to make sure that we caught all options, and that the -- separator is handled.
* Adjusted the lookup_commit_reference() call to the newest master (it now takes a the_repository parameter).
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 option to --creation-factor where 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: skip white-space warnings
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 | 116 +++++ color.h | 7 + command-list.txt | 1 + contrib/completion/git-completion.bash | 14 + diff.c | 105 ++++- diff.h | 10 +- git.c | 1 + linear-assignment.c | 201 ++++++++ linear-assignment.h | 22 + range-diff.c | 435 ++++++++++++++++++ range-diff.h | 9 + t/.gitattributes | 1 + t/t3206-range-diff.sh | 145 ++++++ t/t3206/history.export | 604 +++++++++++++++++++++++++ 19 files changed, 1915 insertions(+), 19 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: 1d89318c48d233d52f1db230cf622935ac3c69fa
Published-As: https://github.com/gitgitgadget/git/releases/tags/pr-1%2Fdscho%2Fbranch-diff-v6
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1/dscho/branch-diff-v6
Pull-Request: https://github.com/gitgitgadget/git/pull/1
Range-diff vs v5:
1: f168da3a3 = 1: f168da3a3 linear-assignment: a function to solve least-cost assignment problems
2: 33758f361 = 2: 33758f361 Introduce `range-diff` to compare iterations of a topic branch
3: 08b8c3fc4 = 3: 08b8c3fc4 range-diff: first rudimentary implementation
4: 7b9091968 = 4: 7b9091968 range-diff: improve the order of the shown commits
5: 9e1e66007 ! 5: 8515d2f75 range-diff: also show the diff between patches
@@ -80,6 +80,8 @@
+ else
+ i += c;
+ }
++ while (i < argc)
++ argv[j++] = argv[i++];
+ argc = j;
+ diff_setup_done(&diffopt);
+
6: 167ca02a3 = 6: a10ca0163 range-diff: right-trim commit messages
7: ca8de8c75 = 7: f81cbef2c range-diff: indent the diffs just like tbdiff
8: eb94d1982 = 8: 458090ffd range-diff: suppress the diff headers
9: 6330afad9 = 9: d3be03a44 range-diff: adjust the output of the commit pairs
10: c296675eb = 10: 94b44dfe6 range-diff: do not show "function names" in hunk headers
11: 85e0ab82f = 11: 1477c58e4 range-diff: add tests
12: f48b62644 = 12: 32492c159 range-diff: use color for the commit pairs
13: 1ad74f939 = 13: 969a196f4 color: add the meta color GIT_COLOR_REVERSE
14: 39a0ecd28 = 14: f1c86f606 diff: add an internal option to dual-color diffs of diffs
15: c32a24f6a = 15: 3c7b9f339 range-diff: offer to dual-color the diffs
16: 05947781f = 16: c56c51c8b range-diff --dual-color: skip white-space warnings
17: 3147c4440 = 17: 8c5543a06 range-diff: populate the man page
18: b08e6d937 = 18: 16e3cf27b completion: support `git range-diff`
19: 19406283e = 19: d9b09abcf range-diff: left-pad patch numbers
20: 6b3552386 = 20: f6fd3955e range-diff: make --dual-color the default mode
21: ccf8c1bb2 = 21: 699cd712e range-diff: use dim/bold cues to improve dual color modeSignature · 1 line
gitgitgadget
PATCH v6 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
@@ -870,6 +870,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 LINEAR_ASSIGNMENT_H +#define LINEAR_ASSIGNMENT_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
PATCH v6 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
@@ -1063,6 +1063,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
@@ -520,6 +520,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 v6 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 | 45 ++++++- range-diff.c | 311 +++++++++++++++++++++++++++++++++++++++++++ range-diff.h | 7 + 4 files changed, 363 insertions(+), 1 deletion(-) create mode 100644 range-diff.c create mode 100644 range-diff.h
Makefile +1 -0
@@ -925,6 +925,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 -1
59 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 RANGE_DIFF_H +#define RANGE_DIFF_H + +int show_range_diff (const char *range1,const char *range2, +int creation_factor); + +#endif
Signature · 1 line
gitgitgadget
Re: [PATCH v6 03/21] range-diff: first rudimentary implementation
"Johannes Schindelin via GitGitGadget" <[email protected]>
range-diff.c line 96
Quoted 4 lines · writes:
range-diff.c new file +4
-0+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
I noticed this while wondering how somebody could teach range-diff to honor --notes=amlog while preparing the patches to be compared [*1*], but this assumption goes against what POSIX.1 says these days.
It is implementation-defined whether an empty unaffected line is written as an empty line or a line containing a single <space> character.
cf. http://pubs.opengroup.org/onlinepubs/9699919799/utilities/diff.html#tag_20_34_10_07
We need to insert ", as we disable user's diff.suppressBlankEmpty settings" before ". We skip it" (and if we get affected by the setting, we need to fix it; it is not ultra-urgent, though).
[Footnote]
*1* ... which I do not have a good answer to, yet. As discussed earlier, the diffopt passed into the show_range_diff() machinery is primarily meant for the final output (i.e. how the matching patches from the two iterations are compared) and not about how the patches to be compared are generated. Worse, --notes=amlog (and possibly other useful options) are parsed by "git log" side of the machinery, not "git diff" side that populates diffopt.
PATCH v6 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 v6 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 frequently, 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 (or --no-patch) option that is automatically supported via our use of diff_opt_parse().
And finally note: to support diff options, we have to call parse_options() such that it keeps unknown options, and then loop over those and let diff_opt_parse() handle them. After that loop, we have to call parse_options() again, to make sure that no unknown options are left.
--- builtin/range-diff.c | 31 +++++++++++++++++++++++++++++-- range-diff.c | 34 +++++++++++++++++++++++++++++++--- range-diff.h | 4 +++- 3 files changed, 63 insertions(+), 6 deletions(-)
builtin/range-diff.c +29 -2
@@ -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,15 +14,40 @@ 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,PARSE_OPT_KEEP_UNKNOWN | +PARSE_OPT_KEEP_DASHDASH |PARSE_OPT_KEEP_ARGV0 ); + +for (i = j =1 ; i < argc &&strcmp ("--" , argv[i]); ) { +int c =diff_opt_parse (&diffopt, argv + i, argc - i, prefix); + +if (!c) + argv[j++] = argv[i++]; +else + i += c; + } +while (i < argc) + argv[j++] = argv[i++]; + argc = j; +diff_setup_done (&diffopt); + +/* Make sure that there are no unparsed options */ + argc =parse_options (argc, argv,NULL , + options +ARRAY_SIZE (options) -1 ,/* OPT_END */ builtin_range_diff_usage,0 );if (argc ==2 ) {
@@ -59,7 +85,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 RANGE_DIFF_H #define RANGE_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 v6 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 v6 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 |
@@ -90,6 +99,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 v6 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
@@ -3395,13 +3395,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 v6 9/21 range-diff: adjust the output of the commit pairs
In reply to Johannes Schindelin via GitGitGadgetFrom: Johannes Schindelin <[email protected]>
This not only uses "dashed stand-ins" for "pairs" where one side is missing (i.e. unmatched commits that are present only in one of the two commit ranges), but also adds onelines for the reader's pleasure.
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 | 59 ++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 9 deletions(-)
range-diff.c +50 -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,49 @@ 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 (the_repository, oid); +if (commit) { +strbuf_addch (buf,' ' ); +pp_commit_easy (CMIT_FMT_ONELINE , commit, buf); + } +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 +328,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 +350,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 +372,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
PATCH v6 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 */
@@ -302,6 +303,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);
@@ -311,6 +316,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 v6 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 named 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 v6 11/21] range-diff: add tests
Quoted 9 lines · Thomas Rast via GitGitGadget
From: 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 named
git range-diff.Apart from renaming
tbdifftorange-diff, only one test case needed to be adjusted: 11 - 'changed message'.
Quoted 7 lines
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.
Just noticed while reading the whole series again (hopefully for the last time :)), do we need Thomas Rast's Sign-off here, as he is credited as the author here?
Quoted 785 lines
Signed-off-by Johannes Schindelin <[email protected]>--- 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.exportt/.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=lft/t3206-range-diff.sh new file +145
-0@@ -0,0 +1,145 @@ +#!/bin/sh + +test_description='range-diff tests' + +. ./test-lib.sh + +# Note that because of the range-diff's heuristics, test_commit does more +# harm than good. We need some real history. + +test_expect_success 'setup' ' + git fast-import < "$TEST_DIRECTORY"/t3206/history.export +' + +test_expect_success 'simple A..B A..C (unmodified)' ' + git range-diff --no-color master..topic master..unmodified \ + >actual && + cat >expected <<-EOF && + 1: 4de457d = 1: 35b9b25 s/5/A/ + 2: fccce22 = 2: de345ab s/4/A/ + 3: 147e64e = 3: 9af6654 s/11/B/ + 4: a63e992 = 4: 2901f77 s/12/B/ + EOF + test_cmp expected actual +' + +test_expect_success 'simple B...C (unmodified)' ' + git range-diff --no-color topic...unmodified >actual && + # same "expected" as above + test_cmp expected actual +' + +test_expect_success 'simple A B C (unmodified)' ' + git range-diff --no-color master topic unmodified >actual && + # same "expected" as above + test_cmp expected actual +' + +test_expect_success 'trivial reordering' ' + git range-diff --no-color master topic reordered >actual && + cat >expected <<-EOF && + 1: 4de457d = 1: aca177a s/5/A/ + 3: 147e64e = 2: 14ad629 s/11/B/ + 4: a63e992 = 3: ee58208 s/12/B/ + 2: fccce22 = 4: 307b27a s/4/A/ + EOF + test_cmp expected actual +' + +test_expect_success 'removed a commit' ' + git range-diff --no-color master topic removed >actual && + cat >expected <<-EOF && + 1: 4de457d = 1: 7657159 s/5/A/ + 2: fccce22 < -: ------- s/4/A/ + 3: 147e64e = 2: 43d84d3 s/11/B/ + 4: a63e992 = 3: a740396 s/12/B/ + EOF + test_cmp expected actual +' + +test_expect_success 'added a commit' ' + git range-diff --no-color master topic added >actual && + cat >expected <<-EOF && + 1: 4de457d = 1: 2716022 s/5/A/ + 2: fccce22 = 2: b62accd s/4/A/ + -: ------- > 3: df46cfa s/6/A/ + 3: 147e64e = 4: 3e64548 s/11/B/ + 4: a63e992 = 5: 12b4063 s/12/B/ + EOF + test_cmp expected actual +' + +test_expect_success 'new base, A B C' ' + git range-diff --no-color master topic rebased >actual && + cat >expected <<-EOF && + 1: 4de457d = 1: cc9c443 s/5/A/ + 2: fccce22 = 2: c5d9641 s/4/A/ + 3: 147e64e = 3: 28cc2b6 s/11/B/ + 4: a63e992 = 4: 5628ab7 s/12/B/ + EOF + test_cmp expected actual +' + +test_expect_success 'new base, B...C' ' + # this syntax includes the commits from master! + git range-diff --no-color topic...rebased >actual && + cat >expected <<-EOF && + -: ------- > 1: a31b12e unrelated + 1: 4de457d = 2: cc9c443 s/5/A/ + 2: fccce22 = 3: c5d9641 s/4/A/ + 3: 147e64e = 4: 28cc2b6 s/11/B/ + 4: a63e992 = 5: 5628ab7 s/12/B/ + EOF + test_cmp expected actual +' + +test_expect_success 'changed commit' ' + git range-diff --no-color topic...changed >actual && + cat >expected <<-EOF && + 1: 4de457d = 1: a4b3333 s/5/A/ + 2: fccce22 = 2: f51d370 s/4/A/ + 3: 147e64e ! 3: 0559556 s/11/B/ + @@ -10,7 +10,7 @@ + 9 + 10 + -11 + -+B + ++BB + 12 + 13 + 14 + 4: a63e992 ! 4: d966c5c s/12/B/ + @@ -8,7 +8,7 @@ + @@ + 9 + 10 + - B + + BB + -12 + +B + 13 + EOF + test_cmp expected actual +' + +test_expect_success 'changed message' ' + git range-diff --no-color topic...changed-message >actual && + sed s/Z/\ /g >expected <<-EOF && + 1: 4de457d = 1: f686024 s/5/A/ + 2: fccce22 ! 2: 4ab067d s/4/A/ + @@ -2,6 +2,8 @@ + Z + Z s/4/A/ + Z + + Also a silly comment here! + + + Zdiff --git a/file b/file + Z--- a/file + Z+++ b/file + 3: 147e64e = 3: b9cb956 s/11/B/ + 4: a63e992 = 4: 8add5f1 s/12/B/ + EOF + test_cmp expected actual +' + +test_donet/t3206/history.export new file +604
-0@@ -0,0 +1,604 @@ +blob +mark :1 +data 51 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 + +reset refs/heads/removed +commit refs/heads/removed +mark :2 +author Thomas Rast <[email protected]> 1374424921 +0200 +committer Thomas Rast <[email protected]> 1374484724 +0200 +data 8 +initial +M 100644 :1 file + +blob +mark :3 +data 51 +1 +2 +3 +4 +A +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 + +commit refs/heads/topic +mark :4 +author Thomas Rast <[email protected]> 1374485014 +0200 +committer Thomas Rast <[email protected]> 1374485014 +0200 +data 7 +s/5/A/ +from :2 +M 100644 :3 file + +blob +mark :5 +data 51 +1 +2 +3 +A +A +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 + +commit refs/heads/topic +mark :6 +author Thomas Rast <[email protected]> 1374485024 +0200 +committer Thomas Rast <[email protected]> 1374485024 +0200 +data 7 +s/4/A/ +from :4 +M 100644 :5 file + +blob +mark :7 +data 50 +1 +2 +3 +A +A +6 +7 +8 +9 +10 +B +12 +13 +14 +15 +16 +17 +18 +19 +20 + +commit refs/heads/topic +mark :8 +author Thomas Rast <[email protected]> 1374485036 +0200 +committer Thomas Rast <[email protected]> 1374485036 +0200 +data 8 +s/11/B/ +from :6 +M 100644 :7 file + +blob +mark :9 +data 49 +1 +2 +3 +A +A +6 +7 +8 +9 +10 +B +B +13 +14 +15 +16 +17 +18 +19 +20 + +commit refs/heads/topic +mark :10 +author Thomas Rast <[email protected]> 1374485044 +0200 +committer Thomas Rast <[email protected]> 1374485044 +0200 +data 8 +s/12/B/ +from :8 +M 100644 :9 file + +blob +mark :11 +data 10 +unrelated + +commit refs/heads/master +mark :12 +author Thomas Rast <[email protected]> 1374485127 +0200 +committer Thomas Rast <[email protected]> 1374485127 +0200 +data 10 +unrelated +from :2 +M 100644 :11 otherfile + +commit refs/heads/rebased +mark :13 +author Thomas Rast <[email protected]> 1374485014 +0200 +committer Thomas Rast <[email protected]> 1374485137 +0200 +data 7 +s/5/A/ +from :12 +M 100644 :3 file + +commit refs/heads/rebased +mark :14 +author Thomas Rast <[email protected]> 1374485024 +0200 +committer Thomas Rast <[email protected]> 1374485138 +0200 +data 7 +s/4/A/ +from :13 +M 100644 :5 file + +commit refs/heads/rebased +mark :15 +author Thomas Rast <[email protected]> 1374485036 +0200 +committer Thomas Rast <[email protected]> 1374485138 +0200 +data 8 +s/11/B/ +from :14 +M 100644 :7 file + +commit refs/heads/rebased +mark :16 +author Thomas Rast <[email protected]> 1374485044 +0200 +committer Thomas Rast <[email protected]> 1374485138 +0200 +data 8 +s/12/B/ +from :15 +M 100644 :9 file + +commit refs/heads/added +mark :17 +author Thomas Rast <[email protected]> 1374485014 +0200 +committer Thomas Rast <[email protected]> 1374485341 +0200 +data 7 +s/5/A/ +from :2 +M 100644 :3 file + +commit refs/heads/added +mark :18 +author Thomas Rast <[email protected]> 1374485024 +0200 +committer Thomas Rast <[email protected]> 1374485341 +0200 +data 7 +s/4/A/ +from :17 +M 100644 :5 file + +blob +mark :19 +data 51 +1 +2 +3 +A +A +A +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 + +commit refs/heads/added +mark :20 +author Thomas Rast <[email protected]> 1374485186 +0200 +committer Thomas Rast <[email protected]> 1374485341 +0200 +data 7 +s/6/A/ +from :18 +M 100644 :19 file + +blob +mark :21 +data 50 +1 +2 +3 +A +A +A +7 +8 +9 +10 +B +12 +13 +14 +15 +16 +17 +18 +19 +20 + +commit refs/heads/added +mark :22 +author Thomas Rast <[email protected]> 1374485036 +0200 +committer Thomas Rast <[email protected]> 1374485341 +0200 +data 8 +s/11/B/ +from :20 +M 100644 :21 file + +blob +mark :23 +data 49 +1 +2 +3 +A +A +A +7 +8 +9 +10 +B +B +13 +14 +15 +16 +17 +18 +19 +20 + +commit refs/heads/added +mark :24 +author Thomas Rast <[email protected]> 1374485044 +0200 +committer Thomas Rast <[email protected]> 1374485341 +0200 +data 8 +s/12/B/ +from :22 +M 100644 :23 file + +commit refs/heads/reordered +mark :25 +author Thomas Rast <[email protected]> 1374485014 +0200 +committer Thomas Rast <[email protected]> 1374485350 +0200 +data 7 +s/5/A/ +from :2 +M 100644 :3 file + +blob +mark :26 +data 50 +1 +2 +3 +4 +A +6 +7 +8 +9 +10 +B +12 +13 +14 +15 +16 +17 +18 +19 +20 + +commit refs/heads/reordered +mark :27 +author Thomas Rast <[email protected]> 1374485036 +0200 +committer Thomas Rast <[email protected]> 1374485350 +0200 +data 8 +s/11/B/ +from :25 +M 100644 :26 file + +blob +mark :28 +data 49 +1 +2 +3 +4 +A +6 +7 +8 +9 +10 +B +B +13 +14 +15 +16 +17 +18 +19 +20 + +commit refs/heads/reordered +mark :29 +author Thomas Rast <[email protected]> 1374485044 +0200 +committer Thomas Rast <[email protected]> 1374485350 +0200 +data 8 +s/12/B/ +from :27 +M 100644 :28 file + +commit refs/heads/reordered +mark :30 +author Thomas Rast <[email protected]> 1374485024 +0200 +committer Thomas Rast <[email protected]> 1374485350 +0200 +data 7 +s/4/A/ +from :29 +M 100644 :9 file + +commit refs/heads/changed +mark :31 +author Thomas Rast <[email protected]> 1374485014 +0200 +committer Thomas Rast <[email protected]> 1374485507 +0200 +data 7 +s/5/A/ +from :2 +M 100644 :3 file + +commit refs/heads/changed +mark :32 +author Thomas Rast <[email protected]> 1374485024 +0200 +committer Thomas Rast <[email protected]> 1374485507 +0200 +data 7 +s/4/A/ +from :31 +M 100644 :5 file + +blob +mark :33 +data 51 +1 +2 +3 +A +A +6 +7 +8 +9 +10 +BB +12 +13 +14 +15 +16 +17 +18 +19 +20 + +commit refs/heads/changed +mark :34 +author Thomas Rast <[email protected]> 1374485036 +0200 +committer Thomas Rast <[email protected]> 1374485507 +0200 +data 8 +s/11/B/ +from :32 +M 100644 :33 file + +blob +mark :35 +data 50 +1 +2 +3 +A +A +6 +7 +8 +9 +10 +BB +B +13 +14 +15 +16 +17 +18 +19 +20 + +commit refs/heads/changed +mark :36 +author Thomas Rast <[email protected]> 1374485044 +0200 +committer Thomas Rast <[email protected]> 1374485507 +0200 +data 8 +s/12/B/ +from :34 +M 100644 :35 file + +commit refs/heads/changed-message +mark :37 +author Thomas Rast <[email protected]> 1374485014 +0200 +committer Thomas Rast <[email protected]> 1374485530 +0200 +data 7 +s/5/A/ +from :2 +M 100644 :3 file + +commit refs/heads/changed-message +mark :38 +author Thomas Rast <[email protected]> 1374485024 +0200 +committer Thomas Rast <[email protected]> 1374485530 +0200 +data 35 +s/4/A/ + +Also a silly comment here! +from :37 +M 100644 :5 file + +commit refs/heads/changed-message +mark :39 +author Thomas Rast <[email protected]> 1374485036 +0200 +committer Thomas Rast <[email protected]> 1374485536 +0200 +data 8 +s/11/B/ +from :38 +M 100644 :7 file + +commit refs/heads/changed-message +mark :40 +author Thomas Rast <[email protected]> 1374485044 +0200 +committer Thomas Rast <[email protected]> 1374485536 +0200 +data 8 +s/12/B/ +from :39 +M 100644 :9 file + +commit refs/heads/unmodified +mark :41 +author Thomas Rast <[email protected]> 1374485014 +0200 +committer Thomas Rast <[email protected]> 1374485631 +0200 +data 7 +s/5/A/ +from :2 +M 100644 :3 file + +commit refs/heads/unmodified +mark :42 +author Thomas Rast <[email protected]> 1374485024 +0200 +committer Thomas Rast <[email protected]> 1374485631 +0200 +data 7 +s/4/A/ +from :41 +M 100644 :5 file + +commit refs/heads/unmodified +mark :43 +author Thomas Rast <[email protected]> 1374485036 +0200 +committer Thomas Rast <[email protected]> 1374485632 +0200 +data 8 +s/11/B/ +from :42 +M 100644 :7 file + +commit refs/heads/unmodified +mark :44 +author Thomas Rast <[email protected]> 1374485044 +0200 +committer Thomas Rast <[email protected]> 1374485632 +0200 +data 8 +s/12/B/ +from :43 +M 100644 :9 file + +commit refs/heads/removed +mark :45 +author Thomas Rast <[email protected]> 1374485014 +0200 +committer Thomas Rast <[email protected]> 1374486061 +0200 +data 7 +s/5/A/ +from :2 +M 100644 :3 file + +commit refs/heads/removed +mark :46 +author Thomas Rast <[email protected]> 1374485036 +0200 +committer Thomas Rast <[email protected]> 1374486061 +0200 +data 8 +s/11/B/ +from :45 +M 100644 :26 file + +commit refs/heads/removed +mark :47 +author Thomas Rast <[email protected]> 1374485044 +0200 +committer Thomas Rast <[email protected]> 1374486061 +0200 +data 8 +s/12/B/ +from :46 +M 100644 :28 file + +reset refs/heads/removed +from :47 +Signature · 1 line
gitgitgadget
Re: [PATCH v6 11/21] range-diff: add tests
Hi Thomas,
Quoted 18 lines · Thomas Gummerer
Quoted 9 lines · Thomas Rast via GitGitGadget
From: 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 named
git range-diff.Apart from renaming
tbdifftorange-diff, only one test case needed to be adjusted: 11 - 'changed message'.Quoted 7 lines
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.
Quoted 3 lines
Just noticed while reading the whole series again (hopefully for the last time :)), do we need Thomas Rast's Sign-off here, as he is credited as the author here?
Hmm. I hoped that my commit message was enough to indicate that while he is the author, I assembled this. Maybe I should move him to the footer, as an Original-Authored-By:?
Junio?
Ciao,
Dscho
Quoted 788 lines
Quoted 786 lines
Signed-off-by Johannes Schindelin <[email protected]>--- 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.exportt/.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=lft/t3206-range-diff.sh new file +145
-0@@ -0,0 +1,145 @@ +#!/bin/sh + +test_description='range-diff tests' + +. ./test-lib.sh + +# Note that because of the range-diff's heuristics, test_commit does more +# harm than good. We need some real history. + +test_expect_success 'setup' ' + git fast-import < "$TEST_DIRECTORY"/t3206/history.export +' + +test_expect_success 'simple A..B A..C (unmodified)' ' + git range-diff --no-color master..topic master..unmodified \ + >actual && + cat >expected <<-EOF && + 1: 4de457d = 1: 35b9b25 s/5/A/ + 2: fccce22 = 2: de345ab s/4/A/ + 3: 147e64e = 3: 9af6654 s/11/B/ + 4: a63e992 = 4: 2901f77 s/12/B/ + EOF + test_cmp expected actual +' + +test_expect_success 'simple B...C (unmodified)' ' + git range-diff --no-color topic...unmodified >actual && + # same "expected" as above + test_cmp expected actual +' + +test_expect_success 'simple A B C (unmodified)' ' + git range-diff --no-color master topic unmodified >actual && + # same "expected" as above + test_cmp expected actual +' + +test_expect_success 'trivial reordering' ' + git range-diff --no-color master topic reordered >actual && + cat >expected <<-EOF && + 1: 4de457d = 1: aca177a s/5/A/ + 3: 147e64e = 2: 14ad629 s/11/B/ + 4: a63e992 = 3: ee58208 s/12/B/ + 2: fccce22 = 4: 307b27a s/4/A/ + EOF + test_cmp expected actual +' + +test_expect_success 'removed a commit' ' + git range-diff --no-color master topic removed >actual && + cat >expected <<-EOF && + 1: 4de457d = 1: 7657159 s/5/A/ + 2: fccce22 < -: ------- s/4/A/ + 3: 147e64e = 2: 43d84d3 s/11/B/ + 4: a63e992 = 3: a740396 s/12/B/ + EOF + test_cmp expected actual +' + +test_expect_success 'added a commit' ' + git range-diff --no-color master topic added >actual && + cat >expected <<-EOF && + 1: 4de457d = 1: 2716022 s/5/A/ + 2: fccce22 = 2: b62accd s/4/A/ + -: ------- > 3: df46cfa s/6/A/ + 3: 147e64e = 4: 3e64548 s/11/B/ + 4: a63e992 = 5: 12b4063 s/12/B/ + EOF + test_cmp expected actual +' + +test_expect_success 'new base, A B C' ' + git range-diff --no-color master topic rebased >actual && + cat >expected <<-EOF && + 1: 4de457d = 1: cc9c443 s/5/A/ + 2: fccce22 = 2: c5d9641 s/4/A/ + 3: 147e64e = 3: 28cc2b6 s/11/B/ + 4: a63e992 = 4: 5628ab7 s/12/B/ + EOF + test_cmp expected actual +' + +test_expect_success 'new base, B...C' ' + # this syntax includes the commits from master! + git range-diff --no-color topic...rebased >actual && + cat >expected <<-EOF && + -: ------- > 1: a31b12e unrelated + 1: 4de457d = 2: cc9c443 s/5/A/ + 2: fccce22 = 3: c5d9641 s/4/A/ + 3: 147e64e = 4: 28cc2b6 s/11/B/ + 4: a63e992 = 5: 5628ab7 s/12/B/ + EOF + test_cmp expected actual +' + +test_expect_success 'changed commit' ' + git range-diff --no-color topic...changed >actual && + cat >expected <<-EOF && + 1: 4de457d = 1: a4b3333 s/5/A/ + 2: fccce22 = 2: f51d370 s/4/A/ + 3: 147e64e ! 3: 0559556 s/11/B/ + @@ -10,7 +10,7 @@ + 9 + 10 + -11 + -+B + ++BB + 12 + 13 + 14 + 4: a63e992 ! 4: d966c5c s/12/B/ + @@ -8,7 +8,7 @@ + @@ + 9 + 10 + - B + + BB + -12 + +B + 13 + EOF + test_cmp expected actual +' + +test_expect_success 'changed message' ' + git range-diff --no-color topic...changed-message >actual && + sed s/Z/\ /g >expected <<-EOF && + 1: 4de457d = 1: f686024 s/5/A/ + 2: fccce22 ! 2: 4ab067d s/4/A/ + @@ -2,6 +2,8 @@ + Z + Z s/4/A/ + Z + + Also a silly comment here! + + + Zdiff --git a/file b/file + Z--- a/file + Z+++ b/file + 3: 147e64e = 3: b9cb956 s/11/B/ + 4: a63e992 = 4: 8add5f1 s/12/B/ + EOF + test_cmp expected actual +' + +test_donet/t3206/history.export new file +604
-0@@ -0,0 +1,604 @@ +blob +mark :1 +data 51 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 + +reset refs/heads/removed +commit refs/heads/removed +mark :2 +author Thomas Rast <[email protected]> 1374424921 +0200 +committer Thomas Rast <[email protected]> 1374484724 +0200 +data 8 +initial +M 100644 :1 file + +blob +mark :3 +data 51 +1 +2 +3 +4 +A +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 + +commit refs/heads/topic +mark :4 +author Thomas Rast <[email protected]> 1374485014 +0200 +committer Thomas Rast <[email protected]> 1374485014 +0200 +data 7 +s/5/A/ +from :2 +M 100644 :3 file + +blob +mark :5 +data 51 +1 +2 +3 +A +A +6 +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 + +commit refs/heads/topic +mark :6 +author Thomas Rast <[email protected]> 1374485024 +0200 +committer Thomas Rast <[email protected]> 1374485024 +0200 +data 7 +s/4/A/ +from :4 +M 100644 :5 file + +blob +mark :7 +data 50 +1 +2 +3 +A +A +6 +7 +8 +9 +10 +B +12 +13 +14 +15 +16 +17 +18 +19 +20 + +commit refs/heads/topic +mark :8 +author Thomas Rast <[email protected]> 1374485036 +0200 +committer Thomas Rast <[email protected]> 1374485036 +0200 +data 8 +s/11/B/ +from :6 +M 100644 :7 file + +blob +mark :9 +data 49 +1 +2 +3 +A +A +6 +7 +8 +9 +10 +B +B +13 +14 +15 +16 +17 +18 +19 +20 + +commit refs/heads/topic +mark :10 +author Thomas Rast <[email protected]> 1374485044 +0200 +committer Thomas Rast <[email protected]> 1374485044 +0200 +data 8 +s/12/B/ +from :8 +M 100644 :9 file + +blob +mark :11 +data 10 +unrelated + +commit refs/heads/master +mark :12 +author Thomas Rast <[email protected]> 1374485127 +0200 +committer Thomas Rast <[email protected]> 1374485127 +0200 +data 10 +unrelated +from :2 +M 100644 :11 otherfile + +commit refs/heads/rebased +mark :13 +author Thomas Rast <[email protected]> 1374485014 +0200 +committer Thomas Rast <[email protected]> 1374485137 +0200 +data 7 +s/5/A/ +from :12 +M 100644 :3 file + +commit refs/heads/rebased +mark :14 +author Thomas Rast <[email protected]> 1374485024 +0200 +committer Thomas Rast <[email protected]> 1374485138 +0200 +data 7 +s/4/A/ +from :13 +M 100644 :5 file + +commit refs/heads/rebased +mark :15 +author Thomas Rast <[email protected]> 1374485036 +0200 +committer Thomas Rast <[email protected]> 1374485138 +0200 +data 8 +s/11/B/ +from :14 +M 100644 :7 file + +commit refs/heads/rebased +mark :16 +author Thomas Rast <[email protected]> 1374485044 +0200 +committer Thomas Rast <[email protected]> 1374485138 +0200 +data 8 +s/12/B/ +from :15 +M 100644 :9 file + +commit refs/heads/added +mark :17 +author Thomas Rast <[email protected]> 1374485014 +0200 +committer Thomas Rast <[email protected]> 1374485341 +0200 +data 7 +s/5/A/ +from :2 +M 100644 :3 file + +commit refs/heads/added +mark :18 +author Thomas Rast <[email protected]> 1374485024 +0200 +committer Thomas Rast <[email protected]> 1374485341 +0200 +data 7 +s/4/A/ +from :17 +M 100644 :5 file + +blob +mark :19 +data 51 +1 +2 +3 +A +A +A +7 +8 +9 +10 +11 +12 +13 +14 +15 +16 +17 +18 +19 +20 + +commit refs/heads/added +mark :20 +author Thomas Rast <[email protected]> 1374485186 +0200 +committer Thomas Rast <[email protected]> 1374485341 +0200 +data 7 +s/6/A/ +from :18 +M 100644 :19 file + +blob +mark :21 +data 50 +1 +2 +3 +A +A +A +7 +8 +9 +10 +B +12 +13 +14 +15 +16 +17 +18 +19 +20 + +commit refs/heads/added +mark :22 +author Thomas Rast <[email protected]> 1374485036 +0200 +committer Thomas Rast <[email protected]> 1374485341 +0200 +data 8 +s/11/B/ +from :20 +M 100644 :21 file + +blob +mark :23 +data 49 +1 +2 +3 +A +A +A +7 +8 +9 +10 +B +B +13 +14 +15 +16 +17 +18 +19 +20 + +commit refs/heads/added +mark :24 +author Thomas Rast <[email protected]> 1374485044 +0200 +committer Thomas Rast <[email protected]> 1374485341 +0200 +data 8 +s/12/B/ +from :22 +M 100644 :23 file + +commit refs/heads/reordered +mark :25 +author Thomas Rast <[email protected]> 1374485014 +0200 +committer Thomas Rast <[email protected]> 1374485350 +0200 +data 7 +s/5/A/ +from :2 +M 100644 :3 file + +blob +mark :26 +data 50 +1 +2 +3 +4 +A +6 +7 +8 +9 +10 +B +12 +13 +14 +15 +16 +17 +18 +19 +20 + +commit refs/heads/reordered +mark :27 +author Thomas Rast <[email protected]> 1374485036 +0200 +committer Thomas Rast <[email protected]> 1374485350 +0200 +data 8 +s/11/B/ +from :25 +M 100644 :26 file + +blob +mark :28 +data 49 +1 +2 +3 +4 +A +6 +7 +8 +9 +10 +B +B +13 +14 +15 +16 +17 +18 +19 +20 + +commit refs/heads/reordered +mark :29 +author Thomas Rast <[email protected]> 1374485044 +0200 +committer Thomas Rast <[email protected]> 1374485350 +0200 +data 8 +s/12/B/ +from :27 +M 100644 :28 file + +commit refs/heads/reordered +mark :30 +author Thomas Rast <[email protected]> 1374485024 +0200 +committer Thomas Rast <[email protected]> 1374485350 +0200 +data 7 +s/4/A/ +from :29 +M 100644 :9 file + +commit refs/heads/changed +mark :31 +author Thomas Rast <[email protected]> 1374485014 +0200 +committer Thomas Rast <[email protected]> 1374485507 +0200 +data 7 +s/5/A/ +from :2 +M 100644 :3 file + +commit refs/heads/changed +mark :32 +author Thomas Rast <[email protected]> 1374485024 +0200 +committer Thomas Rast <[email protected]> 1374485507 +0200 +data 7 +s/4/A/ +from :31 +M 100644 :5 file + +blob +mark :33 +data 51 +1 +2 +3 +A +A +6 +7 +8 +9 +10 +BB +12 +13 +14 +15 +16 +17 +18 +19 +20 + +commit refs/heads/changed +mark :34 +author Thomas Rast <[email protected]> 1374485036 +0200 +committer Thomas Rast <[email protected]> 1374485507 +0200 +data 8 +s/11/B/ +from :32 +M 100644 :33 file + +blob +mark :35 +data 50 +1 +2 +3 +A +A +6 +7 +8 +9 +10 +BB +B +13 +14 +15 +16 +17 +18 +19 +20 + +commit refs/heads/changed +mark :36 +author Thomas Rast <[email protected]> 1374485044 +0200 +committer Thomas Rast <[email protected]> 1374485507 +0200 +data 8 +s/12/B/ +from :34 +M 100644 :35 file + +commit refs/heads/changed-message +mark :37 +author Thomas Rast <[email protected]> 1374485014 +0200 +committer Thomas Rast <[email protected]> 1374485530 +0200 +data 7 +s/5/A/ +from :2 +M 100644 :3 file + +commit refs/heads/changed-message +mark :38 +author Thomas Rast <[email protected]> 1374485024 +0200 +committer Thomas Rast <[email protected]> 1374485530 +0200 +data 35 +s/4/A/ + +Also a silly comment here! +from :37 +M 100644 :5 file + +commit refs/heads/changed-message +mark :39 +author Thomas Rast <[email protected]> 1374485036 +0200 +committer Thomas Rast <[email protected]> 1374485536 +0200 +data 8 +s/11/B/ +from :38 +M 100644 :7 file + +commit refs/heads/changed-message +mark :40 +author Thomas Rast <[email protected]> 1374485044 +0200 +committer Thomas Rast <[email protected]> 1374485536 +0200 +data 8 +s/12/B/ +from :39 +M 100644 :9 file + +commit refs/heads/unmodified +mark :41 +author Thomas Rast <[email protected]> 1374485014 +0200 +committer Thomas Rast <[email protected]> 1374485631 +0200 +data 7 +s/5/A/ +from :2 +M 100644 :3 file + +commit refs/heads/unmodified +mark :42 +author Thomas Rast <[email protected]> 1374485024 +0200 +committer Thomas Rast <[email protected]> 1374485631 +0200 +data 7 +s/4/A/ +from :41 +M 100644 :5 file + +commit refs/heads/unmodified +mark :43 +author Thomas Rast <[email protected]> 1374485036 +0200 +committer Thomas Rast <[email protected]> 1374485632 +0200 +data 8 +s/11/B/ +from :42 +M 100644 :7 file + +commit refs/heads/unmodified +mark :44 +author Thomas Rast <[email protected]> 1374485044 +0200 +committer Thomas Rast <[email protected]> 1374485632 +0200 +data 8 +s/12/B/ +from :43 +M 100644 :9 file + +commit refs/heads/removed +mark :45 +author Thomas Rast <[email protected]> 1374485014 +0200 +committer Thomas Rast <[email protected]> 1374486061 +0200 +data 7 +s/5/A/ +from :2 +M 100644 :3 file + +commit refs/heads/removed +mark :46 +author Thomas Rast <[email protected]> 1374485036 +0200 +committer Thomas Rast <[email protected]> 1374486061 +0200 +data 8 +s/11/B/ +from :45 +M 100644 :26 file + +commit refs/heads/removed +mark :47 +author Thomas Rast <[email protected]> 1374485044 +0200 +committer Thomas Rast <[email protected]> 1374486061 +0200 +data 8 +s/12/B/ +from :46 +M 100644 :28 file + +reset refs/heads/removed +from :47 +Signature · 1 line
gitgitgadget
Re: [PATCH v6 11/21] range-diff: add tests
Quoted 11 lines · Johannes Schindelin
Quoted 3 lines
Quoted 3 lines
These are essentially lifted from https://github.com/trast/tbdiff, with light touch-ups to account for the command now being named
git range-diff.[...]
Quoted 3 lines
Just noticed while reading the whole series again (hopefully for the last time :)), do we need Thomas Rast's Sign-off here, as he is credited as the author here?
Hmm. I hoped that my commit message was enough to indicate that while he is the author, I assembled this. Maybe I should move him to the footer, as an Original-Authored-By:?
I think the "Author" field is actually distinct from the copyright provenance. In this case it ought to be perfectly fine to add your signed-off-by under the DCO's point b:
The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications [...]
This is based on the tests in tbdiff, which is explicitly GPL'd by Thomas. So your signoff certifies that, which is fine.
As for the author field, IMHO it serves two purposes:
- to give credit where it is due
- so that people digging in history know who to contact for questions/problems
In this case it probably makes sense for it to be you, as you'd take responsibility for the code in _this_ project. And as you note, you can give credit in the commit message (the only unfortunate thing is that most automated statistics would not credit Thomas, but in theory they could by mentioning him in the trailer).
-Peff
Re: [PATCH v6 11/21] range-diff: add tests
Quoted 20 lines · Jeff King
Quoted 3 lines
Hmm. I hoped that my commit message was enough to indicate that while he is the author, I assembled this. Maybe I should move him to the footer, as an Original-Authored-By:?
I think the "Author" field is actually distinct from the copyright provenance. In this case it ought to be perfectly fine to add your signed-off-by under the DCO's point b:
The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications [...]
This is based on the tests in tbdiff, which is explicitly GPL'd by Thomas. So your signoff certifies that, which is fine.
As for the author field, IMHO it serves two purposes:
- to give credit where it is due
Quoted 8 lines
- so that people digging in history know who to contact for questions/problems
In this case it probably makes sense for it to be you, as you'd take responsibility for the code in _this_ project. And as you note, you can give credit in the commit message (the only unfortunate thing is that most automated statistics would not credit Thomas, but in theory they could by mentioning him in the trailer).
One thing I should have made clear: this is all my opinion, and anything Thomas expresses trumps that. But since he hasn't been active lately, this is all what I would do in the absence of input from him. Obviously a sign-off from him is better than none. :)
-Peff
Re: [PATCH v6 11/21] range-diff: add tests
In reply to Johannes SchindelinQuoted 5 lines · Johannes Schindelin
Hmm. I hoped that my commit message was enough to indicate that while he is the author, I assembled this. Maybe I should move him to the footer, as an Original-Authored-By:?
Junio?
I think the log message gives a clear enough statement to credit the original author. Sign-off is not about credit, but is about making sure we know the provenance of the contribution we would use in our codebase, so it would be nice to have, but lifting code from another project (i.e. TRast's tbdiff) that is appropriately licensed (GPLv2) verbatim is something you can do with your own sign-off, without the original author's sign-off, so I think what we have is good.
PATCH v6 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);
@@ -295,10 +314,13 @@ static void output_pair_header(struct strbuf *buf, commit =lookup_commit_reference (the_repository, oid);if (commit) { +if (status =='!' ) +strbuf_addf (buf,"%s%s" , color_reset, color); +strbuf_addch (buf,' ' );pp_commit_easy (CMIT_FMT_ONELINE , commit, buf); } -strbuf_addch (buf,'\n' ); +strbuf_addf (buf,"%s\n" , color_reset);fwrite (buf->buf, buf->len,1 , stdout); }
@@ -356,21 +378,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 v6 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 v6 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
@@ -609,14 +609,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' );
@@ -634,8 +638,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);
@@ -649,7 +655,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 {
@@ -1168,7 +1174,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 ;
@@ -1179,14 +1186,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); }
@@ -1196,7 +1209,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;
@@ -1209,7 +1222,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 :
@@ -1236,7 +1249,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 :
@@ -1263,7 +1287,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 ;
@@ -1291,7 +1328,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 :
@@ -1482,6 +1532,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 ;
@@ -1502,6 +1553,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
PATCH v6 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 ;
@@ -60,6 +63,11 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix) options +ARRAY_SIZE (options) -1 ,/* OPT_END */ builtin_range_diff_usage, 0); +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 v6 16/21 range-diff --dual-color: skip white-space warnings
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.
Rather than adding a specific whitespace flag that specifically ignores the first space in the output (and might miss other problems with the white-space warnings), let's just skip handling white-space errors in dual color mode to begin with.
--- diff.c | 1 + 1 file changed, 1 insertion(+)
diff.c +1 -0
@@ -1299,6 +1299,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 &= ~DIFF_SYMBOL_CONTENT_WS_MASK ; } emit_line_ws_markup(o, set, reset, line, len, set_sign, '+', flags & DIFF_SYMBOL_CONTENT_WS_MASK,
Signature · 1 line
gitgitgadget
Re: [PATCH v6 16/21] range-diff --dual-color: skip white-space warnings
"Johannes Schindelin via GitGitGadget" <[email protected]>
Quoted 8 lines · writes:
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.
Also it is possible that there is an outer +, ` , or - before an added line (i.e. the second column is +`), and that line introduces a leading whitespace error (e.g. the whole line begins with two plusses, SP and HT, meaning that the new iteration of the patch introduces a space-before-tab whitespace error), but feeding that to our normal diff machinery would of course not catch it as introducing a new whitespace error.
I think it is a good design decision to give up showing whitespace errors correctly in this round, because the problem is not limited to the first space on an outer context line. The next paragraph would want to lose the mention of the "Rather than". We do want to list the problems we know about for future reference, but that is what we already did in the above paragraph, so all we need to say after this point in the next paragraph would be "let's punt and leave whitespace error highlighting for future enhancements" or something like that.
With the dim/bold cues step applied, the resulting dual-color mode shows the differences between the previous few rounds and this one pretty nicely. After seeing the huccup between v5 and v6, I am a bit hesitant to merge this immediately to 'next' but hopefully we can have it there in a few days (I definitely will merge the topic to my previate edition that I use for my everyday work during this integration cycle).
Thanks.
PATCH v6 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 v6 17/21] range-diff: populate the man page
On Mon, Aug 13 2018, Johannes Schindelin via GitGitGadget wrote:
I realize this topic has long since landed, just seemed like a good thing to reply to to ask this question:
Quoted 6 lines
[...]
Documentation/git-range-diff.txt +4
-0+ ( <range1> <range2> | <rev1>...<rev2> | <base> <rev1> <rev2> ) +<range1> <range2>:: + Compare the commits specified by the two ranges, where + `<range1>` is considered an older version of `<range2>`.
Documentation/git-range-diff.txt NAME · line 60
Quoted 10 lines
Documentation/git-range-diff.txt +10
-0+ +<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.
I find myself using range-diff often by watching forced pushes to public repos to see what others are doing, e.g. just now:
+ 38b5f0fe72...718fbdedbc split-index-racy -> szeder/split-index-racy (forced update)
And then I turn that into:
# @{u} because I happen to be on 'master' and it's shorter to type # than origin/master...
git range-diff @{u} 38b5f0fe72...718fbdedbc
Only to get an error because it doesn't support that, but just:
git range-diff @{u} 38b5f0fe72 718fbdedbcI think it would be convenient given that "fetch" produces this output to support this sort of invocation as synonymous with the three-arg form. Then you can directly copy/paste that from terminals that have a convenient feature to highlight a continuous \S+ reason to copy/paste it.
I can patch it in, but maybe there's UI reasons not to do this that I'm missing, e.g. confusion with the existing <rev1>...<rev2> syntax. What do you think?
Re: [PATCH v6 17/21] range-diff: populate the man page
Quoted 26 lines · Ævar Arnfjörð Bjarmason
On Mon, Aug 13 2018, Johannes Schindelin via GitGitGadget wrote:
I realize this topic has long since landed, just seemed like a good thing to reply to to ask this question:
Quoted 6 lines
[...]
Documentation/git-range-diff.txt +4
-0+ ( <range1> <range2> | <rev1>...<rev2> | <base> <rev1> <rev2> ) +<range1> <range2>:: + Compare the commits specified by the two ranges, where + `<range1>` is considered an older version of `<range2>`.Documentation/git-range-diff.txt NAME · line 60
Quoted 10 lines
Documentation/git-range-diff.txt +10
-0+ +<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.I find myself using range-diff often by watching forced pushes to public repos to see what others are doing, e.g. just now:
Quoted 1 line
+
38b5f0fe72...718fbdedbcsplit-index-racy -> szeder/split-index-racy (forced update)
Heh, spying on my wip bugfixes :)
Quoted 5 lines
And then I turn that into:
# @{u} because I happen to be on 'master' and it's shorter to type # than origin/master...
git range-diff @{u}38b5f0fe72...718fbdedbc
I don't understand what you want with that @{u} or 'origin/master' in the first place. It's unnecessary, the three-dot notation on its own works just fine.
Quoted 13 lines
Only to get an error because it doesn't support that, but just:
git range-diff @{u} 38b5f0fe72 718fbdedbcI think it would be convenient given that "fetch" produces this output to support this sort of invocation as synonymous with the three-arg form. Then you can directly copy/paste that from terminals that have a convenient feature to highlight a continuous \S+ reason to copy/paste it.
I can patch it in, but maybe there's UI reasons not to do this that I'm missing, e.g. confusion with the existing <rev1>...<rev2> syntax. What do you think?
Re: [PATCH v6 17/21] range-diff: populate the man page
Quoted 33 lines · SZEDER Gábor
Quoted 26 lines · Ævar Arnfjörð Bjarmason
On Mon, Aug 13 2018, Johannes Schindelin via GitGitGadget wrote:
I realize this topic has long since landed, just seemed like a good thing to reply to to ask this question:
Quoted 6 lines
[...]
Documentation/git-range-diff.txt +4
-0+ ( <range1> <range2> | <rev1>...<rev2> | <base> <rev1> <rev2> ) +<range1> <range2>:: + Compare the commits specified by the two ranges, where + `<range1>` is considered an older version of `<range2>`.Documentation/git-range-diff.txt NAME · line 60
Quoted 10 lines
Documentation/git-range-diff.txt +10
-0+ +<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.I find myself using range-diff often by watching forced pushes to public repos to see what others are doing, e.g. just now:
Quoted 1 line
+
38b5f0fe72...718fbdedbcsplit-index-racy -> szeder/split-index-racy (forced update)Heh, spying on my wip bugfixes :)
Quoted 2 lines
And then I turn that into:
Quoted 7 lines
Quoted 3 lines
# @{u} because I happen to be on 'master' and it's shorter to type # than origin/master...
git range-diff @{u}38b5f0fe72...718fbdedbcI don't understand what you want with that @{u} or 'origin/master' in the first place. It's unnecessary, the three-dot notation on its own works just fine.
Maybe I've been using the wrong mode all along, I passed over by habits from tbdiff, which were surely copy/pasted from somewhere.
Looking at the git-range-diff manpage though it recommends <base> <rev1> <rev2> over <rev1>...<rev2> when the topic has been rebased, which is usually the case for e.g. a topic that's submitted to git.git (usually be the time feedback has been gathered & a re-submission has been made Junio has pushed another "master").
So isn't "<base> <rev1> <rev2>" the right thing to use over "<rev1>...<rev2>" for git.git use? I think so, but I'm not sure.
In any case, there are going to be those use-case where you should be using "<base> <rev1> <rev2>", and a rebase will be propagated by a force-push, so I thought it made sense that range-diff could directly consume the output of "fetch" in that case...
Quoted 13 lines
Quoted 13 lines
Only to get an error because it doesn't support that, but just:
git range-diff @{u} 38b5f0fe72 718fbdedbcI think it would be convenient given that "fetch" produces this output to support this sort of invocation as synonymous with the three-arg form. Then you can directly copy/paste that from terminals that have a convenient feature to highlight a continuous \S+ reason to copy/paste it.
I can patch it in, but maybe there's UI reasons not to do this that I'm missing, e.g. confusion with the existing <rev1>...<rev2> syntax. What do you think?
Re: [PATCH v6 17/21] range-diff: populate the man page
Quoted 13 lines · Ævar Arnfjörð Bjarmason
Quoted 9 lines
Quoted 5 lines
And then I turn that into:
# @{u} because I happen to be on 'master' and it's shorter to type # than origin/master...
git range-diff @{u}38b5f0fe72...718fbdedbcI don't understand what you want with that @{u} or 'origin/master' in the first place. It's unnecessary, the three-dot notation on its own works just fine.
Maybe I've been using the wrong mode all along, I passed over by habits from tbdiff, which were surely copy/pasted from somewhere.
Quoted 8 lines
Looking at the git-range-diff manpage though it recommends <base> <rev1> <rev2> over <rev1>...<rev2> when the topic has been rebased, which is usually the case for e.g. a topic that's submitted to git.git (usually be the time feedback has been gathered & a re-submission has been made Junio has pushed another "master").
So isn't "<base> <rev1> <rev2>" the right thing to use over "<rev1>...<rev2>" for git.git use? I think so, but I'm not sure.
The problem with <rev1>...<rev2> is that it finds the actual merge base, not the beginning of the topic. So if you have a 5-patch topic, but the first two patches weren't changed in the rebase, it won't show them at all! I made this mistake in [1], for example.
For a force-push, though, you may not care about seeing the topic as a whole, and that mid-topic merge-base could be just fine. So pasting just the "A...B" works.
I don't think your "@{u} A...B" makes any sense. You're giving _two_ bases, which is weird. But even if you wanted to ignore the "..." base as a convenience to users of fetch, @{u} does not necessarily have anything to do with the @{upstream} of the topic at "A". You really want branch@{u}, which is on a separate part of the fetch output line (and your branch@{u} and the remote's are not necessarily the same, either; in this case you probably do not even have that branch checked out).
-Peff
Re: [PATCH v6 17/21] range-diff: populate the man page
Hi Peff,
Quoted 21 lines · Jeff King
Quoted 19 lines · Ævar Arnfjörð Bjarmason
Quoted 9 lines
Quoted 5 lines
And then I turn that into:
# @{u} because I happen to be on 'master' and it's shorter to type # than origin/master...
git range-diff @{u}38b5f0fe72...718fbdedbcI don't understand what you want with that @{u} or 'origin/master' in the first place. It's unnecessary, the three-dot notation on its own works just fine.
Maybe I've been using the wrong mode all along, I passed over by habits from tbdiff, which were surely copy/pasted from somewhere.
Looking at the git-range-diff manpage though it recommends <base> <rev1> <rev2> over <rev1>...<rev2> when the topic has been rebased, which is usually the case for e.g. a topic that's submitted to git.git (usually be the time feedback has been gathered & a re-submission has been made Junio has pushed another "master").
Quoted 5 lines
Quoted 2 lines
So isn't "<base> <rev1> <rev2>" the right thing to use over "<rev1>...<rev2>" for git.git use? I think so, but I'm not sure.
The problem with <rev1>...<rev2> is that it finds the actual merge base, not the beginning of the topic.
That is actually not true, not for range-diff. If it sees A...B, it will automatically generate B..A A..B from it.
That matters if the branches A and B have multiple merge bases.
Quoted 3 lines
So if you have a 5-patch topic, but the first two patches weren't changed in the rebase, it won't show them at all! I made this mistake in [1], for example.
Yep, that is very easy to do.
Another thing to note is that often A...B is not doing the right thing with branches that go into pu because some of us contributors rebase to master (or next) between iterations. For such a use case, I myself prefer the @{u} version that Ævar wants to use. (Although I leave off the three dots, in which case everything works quite magically.)
Quoted 11 lines
For a force-push, though, you may not care about seeing the topic as a whole, and that mid-topic merge-base could be just fine. So pasting just the "A...B" works.
I don't think your "@{u} A...B" makes any sense. You're giving _two_ bases, which is weird. But even if you wanted to ignore the "..." base as a convenience to users of fetch, @{u} does not necessarily have anything to do with the @{upstream} of the topic at "A". You really want branch@{u}, which is on a separate part of the fetch output line (and your branch@{u} and the remote's are not necessarily the same, either; in this case you probably do not even have that branch checked out).
While @{u} in general does not relate to A nor B, it is quite possible that it always does in Ævar's scenario. I would not want to limit them in how they want to use Git from this point of view.
However, I would have a little bit of a problem with special-casing the two-arg version when there are no dots in the first arg, and three dots in the second one.
The problem here: the two-arg version already has a meaning: two commit ranges. And it *is* conceivable that somebody wants to compare, say, the full history of git-gui.git with a certain symmetric range in pu. Granted, that is very obscure a use case, but it would be hard to explain why the two-arg case refers to two commit ranges in some cases, and in other cases not.
Ciao,
Dscho
Quoted 5 lines
-Peff
Re: [PATCH v6 17/21] range-diff: populate the man page
In reply to Ævar Arnfjörð BjarmasonQuoted 8 lines · Ævar Arnfjörð Bjarmason
Looking at the git-range-diff manpage though it recommends <base> <rev1> <rev2> over <rev1>...<rev2> when the topic has been rebased, which is usually the case for e.g. a topic that's submitted to git.git (usually be the time feedback has been gathered & a re-submission has been made Junio has pushed another "master").
So isn't "<base> <rev1> <rev2>" the right thing to use over "<rev1>...<rev2>" for git.git use? I think so, but I'm not sure.
If <rev2> is forked from different base than where <rev1> was forked, then <base> <rev1> <rev2> would give you more sensible range. And such an update is inevitable when <rev2> must rely on new things that recently appeared on <base> since <rev1> forked from the mainline. But otherwise <rev1>...<rev2> should work just fine.
Quoted 4 lines
In any case, there are going to be those use-case where you should be using "<base> <rev1> <rev2>", and a rebase will be propagated by a force-push, so I thought it made sense that range-diff could directly consume the output of "fetch" in that case...
I am not absolutely sure if there is *more* useful interpretation that "<base> <rev1>...<rev2>" may want to mean than to serve as a synonym for "<base> <rev1> <rev2>" for those who are too lazy to type. But if there isn't, I'd say it is a reasonable synonym to want.
PATCH v6 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
PATCH v6 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 (the_repository, oid);
@@ -357,6 +358,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 ;/*
@@ -378,7 +380,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 ;
@@ -386,7 +388,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 ; }
@@ -394,7 +396,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 v6 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 () };
@@ -63,8 +63,10 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix) options +ARRAY_SIZE (options) -1 ,/* OPT_END */ builtin_range_diff_usage, 0); -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
PATCH v6 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
@@ -1193,8 +1193,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
@@ -70,6 +70,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[] = {
@@ -89,6 +95,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)
@@ -1294,11 +1306,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 &= ~DIFF_SYMBOL_CONTENT_WS_MASK ; } emit_line_ws_markup(o, set, reset, line, len, set_sign, '+',
@@ -1336,11 +1350,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
@@ -248,7 +248,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 v6 00/21] Add range-diff, a tbdiff lookalike
In reply to Johannes Schindelin via GitGitGadgetHi,
Quoted 31 lines · 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 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:
base=^{/Start.the.merging-rebase} tag=v2.17.0.windows.1 pre=$tag$base^2 git range-diff $pre$base..$pre $tag$base..$tag
Quoted 6 lines
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 v5:
- Fixed the bug (introduced in v5) where a dashdash would not be handled appropriately.
Quoted 211 lines
Changes since v4:
* Fixed a typo in the commit message of "range-diff: add tests" that was introduced in v4.
* White-space fixes.
* Fixed the length of the first header underline in the man page.
* Changed the preprocessor guard inlinear-assignment.hto reflect the new name (instead of the old name, which washungarian.h).
* Likewise, changed the preprocessor guards inrange-diff.hto hide the history of the thrice-renamed command.
* Fixed indentation in the completion.
* Instead of trying to paper over white-space error handling that does not apply to "diffs of diffs", dual color mode now simply disables all white-space warnings.
* When showing the "single arg must be symmetric range" error message, git range-diff now also shows the usage.
* Adjusted the commit message of "range-diff: adjust the output of the commit pairs" to avoid the surprise of the reviewer when onelines are printed all of a sudden, too.
* "range-diff: adjust the output of the commit pairs" is now using a simpler way to print onelines.
* We are now sandwiching thediff_opt_parse()loop between twoparse_options(), to make sure that we caught all options, and that the -- separator is handled.
* Adjusted thelookup_commit_reference()call to the newest master (it now takes athe_repositoryparameter).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 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 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.
* 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 option to --creation-factor where 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 tooutput_pair_header().
* Removed support for the --no-patches option: we inheritdiff_options' support for -s already (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 --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 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).
* Renamed branch --diff, which had been renamed from branch-diff (which was picked to avoid re-using tbdiff) to range-diff.
* Renamedhungarian.cand its header tolinear-assignment.c
* Made --dual-color the 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: skip white-space warnings
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 | 116 +++++ color.h | 7 + command-list.txt | 1 + contrib/completion/git-completion.bash | 14 + diff.c | 105 ++++- diff.h | 10 +- git.c | 1 + linear-assignment.c | 201 ++++++++ linear-assignment.h | 22 + range-diff.c | 435 ++++++++++++++++++ range-diff.h | 9 + t/.gitattributes | 1 + t/t3206-range-diff.sh | 145 ++++++ t/t3206/history.export | 604 +++++++++++++++++++++++++ 19 files changed, 1915 insertions(+), 19 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:
1d89318c48d233d52f1db230cf622935ac3c69fa
Published-As: https://github.com/gitgitgadget/git/releases/tags/pr-1%2Fdscho%2Fbranch-diff-v6
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-1/dscho/branch-diff-v6
Pull-Request: https://github.com/gitgitgadget/git/pull/1Range-diff vs v5:
1: f168da3a3 = 1: f168da3a3 linear-assignment: a function to solve least-cost assignment problems 2: 33758f361 = 2: 33758f361 Introduce `range-diff` to compare iterations of a topic branch 3: 08b8c3fc4 = 3: 08b8c3fc4 range-diff: first rudimentary implementation 4: 7b9091968 = 4: 7b9091968 range-diff: improve the order of the shown commits 5: 9e1e66007 ! 5: 8515d2f75 range-diff: also show the diff between patches @@ -80,6 +80,8 @@ + else + i += c; + } ++ while (i < argc) ++ argv[j++] = argv[i++]; + argc = j; + diff_setup_done(&diffopt); + 6: 167ca02a3 = 6: a10ca0163 range-diff: right-trim commit messages 7: ca8de8c75 = 7: f81cbef2c range-diff: indent the diffs just like tbdiff 8: eb94d1982 = 8: 458090ffd range-diff: suppress the diff headers 9: 6330afad9 = 9: d3be03a44 range-diff: adjust the output of the commit pairs 10: c296675eb = 10: 94b44dfe6 range-diff: do not show "function names" in hunk headers 11: 85e0ab82f = 11: 1477c58e4 range-diff: add tests 12: f48b62644 = 12: 32492c159 range-diff: use color for the commit pairs 13: 1ad74f939 = 13: 969a196f4 color: add the meta color GIT_COLOR_REVERSE 14: 39a0ecd28 = 14: f1c86f606 diff: add an internal option to dual-color diffs of diffs 15: c32a24f6a = 15: 3c7b9f339 range-diff: offer to dual-color the diffs 16: 05947781f = 16: c56c51c8b range-diff --dual-color: skip white-space warnings 17: 3147c4440 = 17: 8c5543a06 range-diff: populate the man page 18: b08e6d937 = 18: 16e3cf27b completion: support `git range-diff` 19: 19406283e = 19: d9b09abcf range-diff: left-pad patch numbers 20: 6b3552386 = 20: f6fd3955e range-diff: make --dual-color the default mode 21: ccf8c1bb2 = 21: 699cd712e range-diff: use dim/bold cues to improve dual color modeSignature · 1 line
gitgitgadget
Re: [PATCH v6 00/21] Add range-diff, a tbdiff lookalike
Quoted 42 lines · Johannes Schindelin
Hi,
Quoted 31 lines · 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 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:
base=^{/Start.the.merging-rebase} tag=v2.17.0.windows.1 pre=$tag$base^2 git range-diff $pre$base..$pre $tag$base..$tagQuoted 6 lines
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.
Quoted 4 lines
Changes since v5:
- Fixed the bug (introduced in v5) where a dashdash would not be handled appropriately.
Thanks! I've read through all the patches (and the range-diff :)) again and played around a bit with the newest version, and I think this is ready for 'next'.
While playing around with it I did find one error message that reads slightly odd, but it's still understandable, so I'm not sure it's worth worrying about now (we can always improve it on top):
$ ./git range-diff -- js/range-diff-v4...HEADt
fatal: ambiguous argument 'HEADt..js/range-diff-v4': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'
error: could not parse log for 'HEADt..js/range-diff-v4'Quoted 1 line
[...]
I've been using both branch-diff and tbdiff while comparing rerolled topics before accepting them. One obvious difference between the two is that the speed to compute pairing is quite different but that is expected ;-)
Another difference that is somewhat irritating is that a SP that leads a context line in a diff hunk that is introduced in the newer iteration is often painted in reverse, and I do not know what aspect of that single SP on these lines the branch-diff wants to pull my attention to.
https://pasteboard.co/Hm9ujI7F.png
In the picture, the three pre-context lines that are all indented by a HT are prefixed by a SP, and that is prefixed by a '+' sign of the outer diff.
We can use --dual-color mode to unsee these but it is somewhat frustrating not to know what the branch-diff program wants to tell me by highlighting the single SPs on these lines.
Thanks.
Hi Junio,
Quoted 4 lines · Junio C Hamano
I've been using both branch-diff and tbdiff while comparing rerolled topics before accepting them. One obvious difference between the two is that the speed to compute pairing is quite different but that is expected ;-)
Yep.
It is also expected that the branch --diff actually works without you having to make sure that the Python modules numpy and hungarian are built correctly (which you Linux folks couldn't care less about because it is done for ya).
I spent such a lot on time trying to get it to work on Windows that it probably only now reaches parity with the time I spent on implementing branch --diff.
Quoted 5 lines
Another difference that is somewhat irritating is that a SP that leads a context line in a diff hunk that is introduced in the newer iteration is often painted in reverse, and I do not know what aspect of that single SP on these lines the branch-diff wants to pull my attention to.
Right. This is due to the fact that the diff does not realize that it looks at pre/post images being diffs. And hence it does not understand that the single space indicates a context line and is therefore not a whitespace error before the tab.
Quoted 5 lines
https://pasteboard.co/Hm9ujI7F.png
In the picture, the three pre-context lines that are all indented by a HT are prefixed by a SP, and that is prefixed by a '+' sign of the outer diff.
Yep, that's exactly it.
The way tbdiff did it was to parse the diff and re-roll the coloring on its own. I am not really keen on doing that in branch --diff, too.
Quoted 3 lines
We can use --dual-color mode to unsee these but it is somewhat frustrating not to know what the branch-diff program wants to tell me by highlighting the single SPs on these lines.
I was wondering from the get-go whether it would make sense to make --dual-color the default.
But now I wonder whether there is actually *any* use in --color without --dual-color.
What do you think? Should I make the dual color mode the *only* color mode?
Ciao,
Dscho
Quoted 8 lines · Johannes Schindelin
Quoted 3 lines
In the picture, the three pre-context lines that are all indented by a HT are prefixed by a SP, and that is prefixed by a '+' sign of the outer diff.
Yep, that's exactly it.
The way tbdiff did it was to parse the diff and re-roll the coloring on its own. I am not really keen on doing that in
branch --diff, too.
Are you saying that these are "whitespace errors" getting painted? It is somewhat odd because my whitespace errors are configured to be painted in "reverse blue". Perhaps you are forcing the internal diff not to pay attention to the end-user configuration---which actually does make sense, as reusing of "git diff" to take "diff of diff" is a mere implementation detail.
In any case, the "whitespace errors" in "diff of diff" are mostly distracting.
Quoted 8 lines
I was wondering from the get-go whether it would make sense to make --dual-color the default.
But now I wonder whether there is actually *any* use in
--colorwithout--dual-color.What do you think? Should I make the dual color mode the *only* color mode?
Sorry but you are asking a good question to a wrong person.
I normally do not seek much useful information in colored output, so my reaction would not be very useful. Non dual-color mode irritates me due to the false whitespace errors, and dual-color mode irritates me because it looks sufficiently different from tbdiff output that I am used to see.
Hi Junio,
Quoted 6 lines · Junio C Hamano
Quoted 4 lines · Johannes Schindelin
Quoted 3 lines
In the picture, the three pre-context lines that are all indented by a HT are prefixed by a SP, and that is prefixed by a '+' sign of the outer diff.
Quoted 6 lines
Quoted 4 lines
Yep, that's exactly it.
The way tbdiff did it was to parse the diff and re-roll the coloring on its own. I am not really keen on doing that in
branch --diff, too.Are you saying that these are "whitespace errors" getting painted?
Indentation errors, to be precise. Yes.
Quoted 5 lines
It is somewhat odd because my whitespace errors are configured to be painted in "reverse blue". Perhaps you are forcing the internal diff not to pay attention to the end-user configuration---which actually does make sense, as reusing of "git diff" to take "diff of diff" is a mere implementation detail.
It may have been the case before I introduced that call to git_diff_ui_config(), but that happened after -v2, and I did not contribute -v3 yet.
Quoted 2 lines
In any case, the "whitespace errors" in "diff of diff" are mostly distracting.
Precisely. That's why I tried to suppress them in --dual-color mode.
I did not try to suppress them in --color (--no-dual-color) mode, as I find that mode pretty useless.
Quoted 9 lines
Quoted 8 lines
I was wondering from the get-go whether it would make sense to make --dual-color the default.
But now I wonder whether there is actually *any* use in
--colorwithout--dual-color.What do you think? Should I make the dual color mode the *only* color mode?
Quoted 7 lines
Sorry but you are asking a good question to a wrong person.
I normally do not seek much useful information in colored output, so my reaction would not be very useful. Non dual-color mode irritates me due to the false whitespace errors, and dual-color mode irritates me because it looks sufficiently different from tbdiff output that I am used to see.
Do you use --dual-color normally?
I derive *a ton* of information from the colored diff. It really helps me navigate the output of range-diff very quickly.
I ask whether you use --dual-color because in that case I would consider scrapping the way I handle color right now and try to imitate tbdiff's way. But that would lose whitespace error coloring *altogether*. So I, for one, would be unable to see where a subsequent patch series iteration fixes whitespace errors of a previous iteration.
Ciao,
Dscho