rerere: add documentation for conflict normalization
Add some documentation for the logic behind the conflict normalization in rerere. Helped-by: Junio C Hamano <gitster@pobox.com> Signed-off-by: Thomas Gummerer <t.gummerer@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Thomas Gummerer committed
Aug 5, 2018 at 18:20 UTC
fb90dca34c498ac1f922d579c202b4723e87455a
2 files changed
+140
-4
Documentation/technical/rerere.txt
new
+140
@@ -0,0 +1,140 @@
1
+Rerere
2
+======
3
+
4
+This document describes the rerere logic.
5
+
6
+Conflict normalization
7
+----------------------
8
+
9
+To ensure recorded conflict resolutions can be looked up in the rerere
10
+database, even when branches are merged in a different order,
11
+different branches are merged that result in the same conflict, or
12
+when different conflict style settings are used, rerere normalizes the
13
+conflicts before writing them to the rerere database.
14
+
15
+Different conflict styles and branch names are normalized by stripping
16
+the labels from the conflict markers, and removing the common ancestor
17
+version from the `diff3` conflict style. Branches that are merged
18
+in different order are normalized by sorting the conflict hunks. More
19
+on each of those steps in the following sections.
20
+
21
+Once these two normalization operations are applied, a conflict ID is
22
+calculated based on the normalized conflict, which is later used by
23
+rerere to look up the conflict in the rerere database.
24
+
25
+Removing the common ancestor version
26
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
27
+
28
+Say we have three branches AB, AC and AC2. The common ancestor of
29
+these branches has a file with a line containing the string "A" (for
30
+brevity this is called "line A" in the rest of the document). In
31
+branch AB this line is changed to "B", in AC, this line is changed to
32
+"C", and branch AC2 is forked off of AC, after the line was changed to
33
+"C".
34
+
35
+Forking a branch ABAC off of branch AB and then merging AC into it, we
36
+get a conflict like the following:
37
+
38
+ <<<<<<< HEAD
39
+ B
40
+ =======
41
+ C
42
+ >>>>>>> AC
43
+
44
+Doing the analogous with AC2 (forking a branch ABAC2 off of branch AB
45
+and then merging branch AC2 into it), using the diff3 conflict style,
46
+we get a conflict like the following:
47
+
48
+ <<<<<<< HEAD
49
+ B
50
+ ||||||| merged common ancestors
51
+ A
52
+ =======
53
+ C
54
+ >>>>>>> AC2
55
+
56
+By resolving this conflict, to leave line D, the user declares:
57
+
58
+ After examining what branches AB and AC did, I believe that making
59
+ line A into line D is the best thing to do that is compatible with
60
+ what AB and AC wanted to do.
61
+
62
+As branch AC2 refers to the same commit as AC, the above implies that
63
+this is also compatible what AB and AC2 wanted to do.
64
+
65
+By extension, this means that rerere should recognize that the above
66
+conflicts are the same. To do this, the labels on the conflict
67
+markers are stripped, and the common ancestor version is removed. The above
68
+examples would both result in the following normalized conflict:
69
+
70
+ <<<<<<<
71
+ B
72
+ =======
73
+ C
74
+ >>>>>>>
75
+
76
+Sorting hunks
77
+~~~~~~~~~~~~~
78
+
79
+As before, lets imagine that a common ancestor had a file with line A
80
+its early part, and line X in its late part. And then four branches
81
+are forked that do these things:
82
+
83
+ - AB: changes A to B
84
+ - AC: changes A to C
85
+ - XY: changes X to Y
86
+ - XZ: changes X to Z
87
+
88
+Now, forking a branch ABAC off of branch AB and then merging AC into
89
+it, and forking a branch ACAB off of branch AC and then merging AB
90
+into it, would yield the conflict in a different order. The former
91
+would say "A became B or C, what now?" while the latter would say "A
92
+became C or B, what now?"
93
+
94
+As a reminder, the act of merging AC into ABAC and resolving the
95
+conflict to leave line D means that the user declares:
96
+
97
+ After examining what branches AB and AC did, I believe that
98
+ making line A into line D is the best thing to do that is
99
+ compatible with what AB and AC wanted to do.
100
+
101
+So the conflict we would see when merging AB into ACAB should be
102
+resolved the same way---it is the resolution that is in line with that
103
+declaration.
104
+
105
+Imagine that similarly previously a branch XYXZ was forked from XY,
106
+and XZ was merged into it, and resolved "X became Y or Z" into "X
107
+became W".
108
+
109
+Now, if a branch ABXY was forked from AB and then merged XY, then ABXY
110
+would have line B in its early part and line Y in its later part.
111
+Such a merge would be quite clean. We can construct 4 combinations
112
+using these four branches ((AB, AC) x (XY, XZ)).
113
+
114
+Merging ABXY and ACXZ would make "an early A became B or C, a late X
115
+became Y or Z" conflict, while merging ACXY and ABXZ would make "an
116
+early A became C or B, a late X became Y or Z". We can see there are
117
+4 combinations of ("B or C", "C or B") x ("X or Y", "Y or X").
118
+
119
+By sorting, the conflict is given its canonical name, namely, "an
120
+early part became B or C, a late part becames X or Y", and whenever
121
+any of these four patterns appear, and we can get to the same conflict
122
+and resolution that we saw earlier.
123
+
124
+Without the sorting, we'd have to somehow find a previous resolution
125
+from combinatorial explosion.
126
+
127
+Conflict ID calculation
128
+~~~~~~~~~~~~~~~~~~~~~~~
129
+
130
+Once the conflict normalization is done, the conflict ID is calculated
131
+as the sha1 hash of the conflict hunks appended to each other,
132
+separated by <NUL> characters. The conflict markers are stripped out
133
+before the sha1 is calculated. So in the example above, where we
134
+merge branch AC which changes line A to line C, into branch AB, which
135
+changes line A to line C, the conflict ID would be
136
+SHA1('B<NUL>C<NUL>').
137
+
138
+If there are multiple conflicts in one file, the sha1 is calculated
139
+the same way with all hunks appended to each other, in the order in
140
+which they appear in the file, separated by a <NUL> character.
rerere.c
-4
@@ -394,10 +394,6 @@ static int is_cmarker(char *buf, int marker_char, int marker_size)
394
* and NUL concatenated together.
395
*
396
* Return the number of conflict hunks found.
397
- *
398
- * NEEDSWORK: the logic and theory of operation behind this conflict
399
- * normalization may deserve to be documented somewhere, perhaps in
400
- * Documentation/technical/rerere.txt.
397
*/
398
static int handle_path(unsigned char *sha1, struct rerere_io *io, int marker_size)
399
{