6 git-range-diff - Compare two commit ranges (e.g. two versions of a branch)
11 'git range-diff' [--color=[<when>]] [--no-color] [<diff-options>]
12 [--no-dual-color] [--creation-factor=<factor>]
13 ( <range1> <range2> | <rev1>...<rev2> | <base> <rev1> <rev2> )
18 This command shows the differences between two versions of a patch
19 series, or more generally, two commit ranges (ignoring merge commits).
21 To that end, it first finds pairs of commits from both commit ranges
22 that correspond with each other. Two commits are said to correspond when
23 the diff between their patches (i.e. the author information, the commit
24 message and the commit diff) is reasonably small compared to the
25 patches' size. See ``Algorithm`` below for details.
27 Finally, the list of matching commits is shown in the order of the
28 second commit range, with unmatched commits being inserted just after
29 all of their ancestors have been shown.
35 When the commit diffs differ, `git range-diff` recreates the
36 original diffs' coloring, and adds outer -/+ diff markers with
37 the *background* being red/green to make it easier to see e.g.
38 when there was a change in what exact lines were added.
40 Additionally, the commit diff lines that are only present in the first commit
41 range are shown "dimmed" (this can be overridden using the `color.diff.<slot>`
42 config setting where `<slot>` is one of `contextDimmed`, `oldDimmed` and
43 `newDimmed`), and the commit diff lines that are only present in the second
44 commit range are shown in bold (which can be overridden using the config
45 settings `color.diff.<slot>` with `<slot>` being one of `contextBold`,
46 `oldBold` or `newBold`).
48 This is known to `range-diff` as "dual coloring". Use `--no-dual-color`
49 to revert to color all lines according to the outer diff markers
50 (and completely ignore the inner diff when it comes to color).
52 --creation-factor=<percent>::
53 Set the creation/deletion cost fudge factor to `<percent>`.
54 Defaults to 60. Try a larger value if `git range-diff` erroneously
55 considers a large change a total rewrite (deletion of one commit
56 and addition of another), and a smaller one in the reverse case.
57 See the ``Algorithm`` section below for an explanation why this is
61 Compare the commits specified by the two ranges, where
62 `<range1>` is considered an older version of `<range2>`.
65 Equivalent to passing `<rev2>..<rev1>` and `<rev1>..<rev2>`.
67 <base> <rev1> <rev2>::
68 Equivalent to passing `<base>..<rev1>` and `<base>..<rev2>`.
69 Note that `<base>` does not need to be the exact branch point
70 of the branches. Example: after rebasing a branch `my-topic`,
71 `git range-diff my-topic@{u} my-topic@{1} my-topic` would
72 show the differences introduced by the rebase.
74 `git range-diff` also accepts the regular diff options (see
75 linkgit:git-diff[1]), most notably the `--color=[<when>]` and
76 `--no-color` options. These options are used when generating the "diff
77 between patches", i.e. to compare the author, commit message and diff of
78 corresponding old/new commits. There is currently no means to tweak the
79 diff options passed to `git log` when generating those patches.
84 This command uses the `diff.color.*` and `pager.range-diff` settings
85 (the latter is on by default).
86 See linkgit:git-config[1].
92 When a rebase required merge conflicts to be resolved, compare the changes
93 introduced by the rebase directly afterwards using:
96 $ git range-diff @{u} @{1} @
100 A typical output of `git range-diff` would look like this:
103 -: ------- > 1: 0ddba11 Prepare for the inevitable!
104 1: c0debee = 2: cab005e Add a helpful message at the start
105 2: f00dbal ! 3: decafe1 Describe a bug
107 Author: A U Thor <author@example.com>
109 -TODO: Describe a bug
114 -+What is unexpected is that it will also crash.
115 ++Unexpectedly, it also crashes. This is a bug, and the jury is
116 ++still out there how to fix it best. See ticket #314 for details.
119 3: bedead < -: ------- TO-UNDO
122 In this example, there are 3 old and 3 new commits, where the developer
123 removed the 3rd, added a new one before the first two, and modified the
124 commit message of the 2nd commit as well its diff.
126 When the output goes to a terminal, it is color-coded by default, just
127 like regular `git diff`'s output. In addition, the first line (adding a
128 commit) is green, the last line (deleting a commit) is red, the second
129 line (with a perfect match) is yellow like the commit header of `git
130 show`'s output, and the third line colors the old commit red, the new
131 one green and the rest like `git show`'s commit header.
133 A naive color-coded diff of diffs is actually a bit hard to read,
134 though, as it colors the entire lines red or green. The line that added
135 "What is unexpected" in the old commit, for example, is completely red,
136 even if the intent of the old commit was to add something.
138 To help with that, `range` uses the `--dual-color` mode by default. In
139 this mode, the diff of diffs will retain the original diff colors, and
140 prefix the lines with -/+ markers that have their *background* red or
141 green, to make it more obvious that they describe how the diff itself
148 The general idea is this: we generate a cost matrix between the commits
149 in both commit ranges, then solve the least-cost assignment.
151 The cost matrix is populated thusly: for each pair of commits, both
152 diffs are generated and the "diff of diffs" is generated, with 3 context
153 lines, then the number of lines in that diff is used as cost.
155 To avoid false positives (e.g. when a patch has been removed, and an
156 unrelated patch has been added between two iterations of the same patch
157 series), the cost matrix is extended to allow for that, by adding
158 fixed-cost entries for wholesale deletes/adds.
160 Example: Let commits `1--2` be the first iteration of a patch series and
161 `A--C` the second iteration. Let's assume that `A` is a cherry-pick of
162 `2,` and `C` is a cherry-pick of `1` but with a small modification (say,
163 a fixed typo). Visualize the commits as a bipartite graph:
173 We are looking for a "best" explanation of the new series in terms of
174 the old one. We can represent an "explanation" as an edge in the graph:
185 This explanation comes for "free" because there was no change. Similarly
186 `C` could be explained using `1`, but that comes at some cost c>0
187 because of the modification:
198 In mathematical terms, what we are looking for is some sort of a minimum
199 cost bipartite matching; `1` is matched to `C` at some cost, etc. The
200 underlying graph is in fact a complete bipartite graph; the cost we
201 associate with every edge is the size of the diff between the two
202 commits' patches. To explain also new commits, we introduce dummy nodes
217 The cost of an edge `o--C` is the size of `C`'s diff, modified by a
218 fudge factor that should be smaller than 100%. The cost of an edge
219 `o--o` is free. The fudge factor is necessary because even if `1` and
220 `C` have nothing in common, they may still share a few empty lines and
221 such, possibly making the assignment `1--C`, `o--o` slightly cheaper
222 than `1--o`, `o--C` even if `1` and `C` have nothing in common. With the
223 fudge factor we require a much larger common part to consider patches as
226 The overall time needed to compute this algorithm is the time needed to
227 compute n+m commit diffs and then n*m diffs of patches, plus the time
228 needed to compute the least-cost assigment between n and m diffs. Git
229 uses an implementation of the Jonker-Volgenant algorithm to solve the
230 assignment problem, which has cubic runtime complexity. The matching
231 found in this case will look like this:
252 Part of the linkgit:git[1] suite