1GIT-RANGE-DIFF(1) Git Manual GIT-RANGE-DIFF(1)
2
3
4
6 git-range-diff - Compare two commit ranges (e.g. two versions of a
7 branch)
8
10 git range-diff [--color=[<when>]] [--no-color] [<diff-options>]
11 [--no-dual-color] [--creation-factor=<factor>]
12 ( <range1> <range2> | <rev1>...<rev2> | <base> <rev1> <rev2> )
13
14
16 This command shows the differences between two versions of a patch
17 series, or more generally, two commit ranges (ignoring merge commits).
18
19 To that end, it first finds pairs of commits from both commit ranges
20 that correspond with each other. Two commits are said to correspond
21 when the diff between their patches (i.e. the author information, the
22 commit message and the commit diff) is reasonably small compared to the
23 patches' size. See ``Algorithm`` below for details.
24
25 Finally, the list of matching commits is shown in the order of the
26 second commit range, with unmatched commits being inserted just after
27 all of their ancestors have been shown.
28
30 --no-dual-color
31 When the commit diffs differ, ‘git range-diff` recreates the
32 original diffs’ coloring, and adds outer -/+ diff markers with the
33 background being red/green to make it easier to see e.g. when there
34 was a change in what exact lines were added.
35
36 Additionally, the commit diff lines that are only present in the
37 first commit range are shown "dimmed" (this can be overridden using
38 the color.diff.<slot> config setting where <slot> is one of
39 contextDimmed, oldDimmed and newDimmed), and the commit diff lines
40 that are only present in the second commit range are shown in bold
41 (which can be overridden using the config settings
42 color.diff.<slot> with <slot> being one of contextBold, oldBold or
43 newBold).
44
45 This is known to range-diff as "dual coloring". Use --no-dual-color
46 to revert to color all lines according to the outer diff markers
47 (and completely ignore the inner diff when it comes to color).
48
49 --creation-factor=<percent>
50 Set the creation/deletion cost fudge factor to <percent>. Defaults
51 to 60. Try a larger value if git range-diff erroneously considers a
52 large change a total rewrite (deletion of one commit and addition
53 of another), and a smaller one in the reverse case. See the
54 ``Algorithm`` section below for an explanation why this is needed.
55
56 <range1> <range2>
57 Compare the commits specified by the two ranges, where <range1> is
58 considered an older version of <range2>.
59
60 <rev1>...<rev2>
61 Equivalent to passing <rev2>..<rev1> and <rev1>..<rev2>.
62
63 <base> <rev1> <rev2>
64 Equivalent to passing <base>..<rev1> and <base>..<rev2>. Note that
65 <base> does not need to be the exact branch point of the branches.
66 Example: after rebasing a branch my-topic, git range-diff
67 my-topic@{u} my-topic@{1} my-topic would show the differences
68 introduced by the rebase.
69
70 git range-diff also accepts the regular diff options (see git-diff(1)),
71 most notably the --color=[<when>] and --no-color options. These options
72 are used when generating the "diff between patches", i.e. to compare
73 the author, commit message and diff of corresponding old/new commits.
74 There is currently no means to tweak the diff options passed to git log
75 when generating those patches.
76
78 The output of the range-diff command is subject to change. It is
79 intended to be human-readable porcelain output, not something that can
80 be used across versions of Git to get a textually stable range-diff (as
81 opposed to something like the --stable option to git-patch-id(1)).
82 There’s also no equivalent of git-apply(1) for range-diff, the output
83 is not intended to be machine-readable.
84
85 This is particularly true when passing in diff options. Currently some
86 options like --stat can, as an emergent effect, produce output that’s
87 quite useless in the context of range-diff. Future versions of
88 range-diff may learn to interpret such options in a manner specific to
89 range-diff (e.g. for --stat producing human-readable output which
90 summarizes how the diffstat changed).
91
93 This command uses the diff.color.* and pager.range-diff settings (the
94 latter is on by default). See git-config(1).
95
97 When a rebase required merge conflicts to be resolved, compare the
98 changes introduced by the rebase directly afterwards using:
99
100 $ git range-diff @{u} @{1} @
101
102
103 A typical output of git range-diff would look like this:
104
105 -: ------- > 1: 0ddba11 Prepare for the inevitable!
106 1: c0debee = 2: cab005e Add a helpful message at the start
107 2: f00dbal ! 3: decafe1 Describe a bug
108 @@ -1,3 +1,3 @@
109 Author: A U Thor <author@example.com>
110
111 -TODO: Describe a bug
112 +Describe a bug
113 @@ -324,5 +324,6
114 This is expected.
115
116 -+What is unexpected is that it will also crash.
117 ++Unexpectedly, it also crashes. This is a bug, and the jury is
118 ++still out there how to fix it best. See ticket #314 for details.
119
120 Contact
121 3: bedead < -: ------- TO-UNDO
122
123
124 In this example, there are 3 old and 3 new commits, where the developer
125 removed the 3rd, added a new one before the first two, and modified the
126 commit message of the 2nd commit as well its diff.
127
128 When the output goes to a terminal, it is color-coded by default, just
129 like regular git diff's output. In addition, the first line (adding a
130 commit) is green, the last line (deleting a commit) is red, the second
131 line (with a perfect match) is yellow like the commit header of git
132 show's output, and the third line colors the old commit red, the new
133 one green and the rest like git show's commit header.
134
135 A naive color-coded diff of diffs is actually a bit hard to read,
136 though, as it colors the entire lines red or green. The line that added
137 "What is unexpected" in the old commit, for example, is completely red,
138 even if the intent of the old commit was to add something.
139
140 To help with that, range uses the --dual-color mode by default. In this
141 mode, the diff of diffs will retain the original diff colors, and
142 prefix the lines with -/+ markers that have their background red or
143 green, to make it more obvious that they describe how the diff itself
144 changed.
145
147 The general idea is this: we generate a cost matrix between the commits
148 in both commit ranges, then solve the least-cost assignment.
149
150 The cost matrix is populated thusly: for each pair of commits, both
151 diffs are generated and the "diff of diffs" is generated, with 3
152 context lines, then the number of lines in that diff is used as cost.
153
154 To avoid false positives (e.g. when a patch has been removed, and an
155 unrelated patch has been added between two iterations of the same patch
156 series), the cost matrix is extended to allow for that, by adding
157 fixed-cost entries for wholesale deletes/adds.
158
159 Example: Let commits 1--2 be the first iteration of a patch series and
160 A--C the second iteration. Let’s assume that A is a cherry-pick of 2,
161 and C is a cherry-pick of 1 but with a small modification (say, a fixed
162 typo). Visualize the commits as a bipartite graph:
163
164 1 A
165
166 2 B
167
168 C
169
170
171 We are looking for a "best" explanation of the new series in terms of
172 the old one. We can represent an "explanation" as an edge in the graph:
173
174 1 A
175 /
176 2 --------' B
177
178 C
179
180
181 This explanation comes for "free" because there was no change.
182 Similarly C could be explained using 1, but that comes at some cost c>0
183 because of the modification:
184
185 1 ----. A
186 | /
187 2 ----+---' B
188 |
189 `----- C
190 c>0
191
192
193 In mathematical terms, what we are looking for is some sort of a
194 minimum cost bipartite matching; ‘1` is matched to C at some cost, etc.
195 The underlying graph is in fact a complete bipartite graph; the cost we
196 associate with every edge is the size of the diff between the two
197 commits’ patches. To explain also new commits, we introduce dummy nodes
198 on both sides:
199
200 1 ----. A
201 | /
202 2 ----+---' B
203 |
204 o `----- C
205 c>0
206 o o
207
208 o o
209
210
211 The cost of an edge o--C is the size of C's diff, modified by a fudge
212 factor that should be smaller than 100%. The cost of an edge o--o is
213 free. The fudge factor is necessary because even if 1 and C have
214 nothing in common, they may still share a few empty lines and such,
215 possibly making the assignment 1--C, o--o slightly cheaper than 1--o,
216 o--C even if 1 and C have nothing in common. With the fudge factor we
217 require a much larger common part to consider patches as corresponding.
218
219 The overall time needed to compute this algorithm is the time needed to
220 compute n+m commit diffs and then n*m diffs of patches, plus the time
221 needed to compute the least-cost assigment between n and m diffs. Git
222 uses an implementation of the Jonker-Volgenant algorithm to solve the
223 assignment problem, which has cubic runtime complexity. The matching
224 found in this case will look like this:
225
226 1 ----. A
227 | /
228 2 ----+---' B
229 .--+-----'
230 o -' `----- C
231 c>0
232 o ---------- o
233
234 o ---------- o
235
236
238 git-log(1)
239
241 Part of the git(1) suite
242
243
244
245Git 2.24.1 12/10/2019 GIT-RANGE-DIFF(1)