git@vger.kernel.org list mirror (unofficial, one of many)
 help / color / mirror / code / Atom feed
af5f9fc24f9343e73aca771d310570251fe2b922 blob 6485 bytes (raw)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
 
Rerere
======

This document describes the rerere logic.

Conflict normalization
----------------------

To ensure recorded conflict resolutions can be looked up in the rerere
database, even when branches are merged in a different order,
different branches are merged that result in the same conflict, or
when different conflict style settings are used, rerere normalizes the
conflicts before writing them to the rerere database.

Different conflict styles and branch names are normalized by stripping
the labels from the conflict markers, and removing the common ancestor
version from the `diff3` conflict style. Branches that are merged
in different order are normalized by sorting the conflict hunks.  More
on each of those steps in the following sections.

Once these two normalization operations are applied, a conflict ID is
calculated based on the normalized conflict, which is later used by
rerere to look up the conflict in the rerere database.

Removing the common ancestor version
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Say we have three branches AB, AC and AC2.  The common ancestor of
these branches has a file with a line containing the string "A" (for
brevity this is called "line A" in the rest of the document).  In
branch AB this line is changed to "B", in AC, this line is changed to
"C", and branch AC2 is forked off of AC, after the line was changed to
"C".

Forking a branch ABAC off of branch AB and then merging AC into it, we
get a conflict like the following:

    <<<<<<< HEAD
    B
    =======
    C
    >>>>>>> AC

Doing the analogous with AC2 (forking a branch ABAC2 off of branch AB
and then merging branch AC2 into it), using the diff3 conflict style,
we get a conflict like the following:

    <<<<<<< HEAD
    B
    ||||||| merged common ancestors
    A
    =======
    C
    >>>>>>> AC2

By resolving this conflict, to leave line D, the user declares:

    After examining what branches AB and AC did, I believe that making
    line A into line D is the best thing to do that is compatible with
    what AB and AC wanted to do.

As branch AC2 refers to the same commit as AC, the above implies that
this is also compatible what AB and AC2 wanted to do.

By extension, this means that rerere should recognize that the above
conflicts are the same.  To do this, the labels on the conflict
markers are stripped, and the common ancestor version is removed.  The above
examples would both result in the following normalized conflict:

    <<<<<<<
    B
    =======
    C
    >>>>>>>

Sorting hunks
~~~~~~~~~~~~~

As before, lets imagine that a common ancestor had a file with line A
its early part, and line X in its late part.  And then four branches
are forked that do these things:

    - AB: changes A to B
    - AC: changes A to C
    - XY: changes X to Y
    - XZ: changes X to Z

Now, forking a branch ABAC off of branch AB and then merging AC into
it, and forking a branch ACAB off of branch AC and then merging AB
into it, would yield the conflict in a different order.  The former
would say "A became B or C, what now?" while the latter would say "A
became C or B, what now?"

As a reminder, the act of merging AC into ABAC and resolving the
conflict to leave line D means that the user declares:

    After examining what branches AB and AC did, I believe that
    making line A into line D is the best thing to do that is
    compatible with what AB and AC wanted to do.

So the conflict we would see when merging AB into ACAB should be
resolved the same way---it is the resolution that is in line with that
declaration.

Imagine that similarly previously a branch XYXZ was forked from XY,
and XZ was merged into it, and resolved "X became Y or Z" into "X
became W".

Now, if a branch ABXY was forked from AB and then merged XY, then ABXY
would have line B in its early part and line Y in its later part.
Such a merge would be quite clean.  We can construct 4 combinations
using these four branches ((AB, AC) x (XY, XZ)).

Merging ABXY and ACXZ would make "an early A became B or C, a late X
became Y or Z" conflict, while merging ACXY and ABXZ would make "an
early A became C or B, a late X became Y or Z".  We can see there are
4 combinations of ("B or C", "C or B") x ("X or Y", "Y or X").

By sorting, the conflict is given its canonical name, namely, "an
early part became B or C, a late part became X or Y", and whenever
any of these four patterns appear, and we can get to the same conflict
and resolution that we saw earlier.

Without the sorting, we'd have to somehow find a previous resolution
from combinatorial explosion.

Conflict ID calculation
~~~~~~~~~~~~~~~~~~~~~~~

Once the conflict normalization is done, the conflict ID is calculated
as the sha1 hash of the conflict hunks appended to each other,
separated by <NUL> characters.  The conflict markers are stripped out
before the sha1 is calculated.  So in the example above, where we
merge branch AC which changes line A to line C, into branch AB, which
changes line A to line C, the conflict ID would be
SHA1('B<NUL>C<NUL>').

If there are multiple conflicts in one file, the sha1 is calculated
the same way with all hunks appended to each other, in the order in
which they appear in the file, separated by a <NUL> character.

Nested conflicts
~~~~~~~~~~~~~~~~

Nested conflicts are handled very similarly to "simple" conflicts.
Similar to simple conflicts, the conflict is first normalized by
stripping the labels from conflict markers, stripping the common ancestor
version, and the sorting the conflict hunks, both for the outer and the
inner conflict.  This is done recursively, so any number of nested
conflicts can be handled.

Note that this only works for conflict markers that "cleanly nest".  If
there are any unmatched conflict markers, rerere will fail to handle
the conflict and record a conflict resolution.

The only difference is in how the conflict ID is calculated.  For the
inner conflict, the conflict markers themselves are not stripped out
before calculating the sha1.

Say we have the following conflict for example:

    <<<<<<< HEAD
    1
    =======
    <<<<<<< HEAD
    3
    =======
    2
    >>>>>>> branch-2
    >>>>>>> branch-3~

After stripping out the labels of the conflict markers, and sorting
the hunks, the conflict would look as follows:

    <<<<<<<
    1
    =======
    <<<<<<<
    2
    =======
    3
    >>>>>>>
    >>>>>>>

and finally the conflict ID would be calculated as:
`sha1('1<NUL><<<<<<<\n3\n=======\n2\n>>>>>>><NUL>')`
debug log:

solving af5f9fc24f ...
found af5f9fc24f in https://80x24.org/mirrors/git.git

Code repositories for project(s) associated with this inbox:

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).