1937a2000SPeter Wemm /*
2937a2000SPeter Wemm * diff.c : routines for doing diffs
3937a2000SPeter Wemm *
4937a2000SPeter Wemm * ====================================================================
5937a2000SPeter Wemm * Licensed to the Apache Software Foundation (ASF) under one
6937a2000SPeter Wemm * or more contributor license agreements. See the NOTICE file
7937a2000SPeter Wemm * distributed with this work for additional information
8937a2000SPeter Wemm * regarding copyright ownership. The ASF licenses this file
9937a2000SPeter Wemm * to you under the Apache License, Version 2.0 (the
10937a2000SPeter Wemm * "License"); you may not use this file except in compliance
11937a2000SPeter Wemm * with the License. You may obtain a copy of the License at
12937a2000SPeter Wemm *
13937a2000SPeter Wemm * http://www.apache.org/licenses/LICENSE-2.0
14937a2000SPeter Wemm *
15937a2000SPeter Wemm * Unless required by applicable law or agreed to in writing,
16937a2000SPeter Wemm * software distributed under the License is distributed on an
17937a2000SPeter Wemm * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18937a2000SPeter Wemm * KIND, either express or implied. See the License for the
19937a2000SPeter Wemm * specific language governing permissions and limitations
20937a2000SPeter Wemm * under the License.
21937a2000SPeter Wemm * ====================================================================
22937a2000SPeter Wemm */
23937a2000SPeter Wemm
24937a2000SPeter Wemm
25937a2000SPeter Wemm #include <apr.h>
26937a2000SPeter Wemm #include <apr_pools.h>
27937a2000SPeter Wemm #include <apr_general.h>
28937a2000SPeter Wemm
29937a2000SPeter Wemm #include "svn_pools.h"
30937a2000SPeter Wemm #include "svn_error.h"
31937a2000SPeter Wemm #include "svn_diff.h"
32937a2000SPeter Wemm #include "svn_types.h"
33937a2000SPeter Wemm
34937a2000SPeter Wemm #include "diff.h"
35937a2000SPeter Wemm
36937a2000SPeter Wemm /*
37937a2000SPeter Wemm * Variance adjustment rules:
38937a2000SPeter Wemm *
39937a2000SPeter Wemm * See notes/variance-adjusted-patching.html
40937a2000SPeter Wemm *
41937a2000SPeter Wemm * ###: Expand this comment to contain the full set of adjustment
42937a2000SPeter Wemm * ###: rules instead of pointing to a webpage.
43937a2000SPeter Wemm */
44937a2000SPeter Wemm
45937a2000SPeter Wemm /*
46937a2000SPeter Wemm * In the text below consider the following:
47937a2000SPeter Wemm *
48937a2000SPeter Wemm * O = Original
49937a2000SPeter Wemm * M = Modified
50937a2000SPeter Wemm * L = Latest
51937a2000SPeter Wemm * A = Ancestor
52937a2000SPeter Wemm * X:Y = diff between X and Y
53937a2000SPeter Wemm * X:Y:Z = 3-way diff between X, Y and Z
54937a2000SPeter Wemm * P = O:L, possibly adjusted
55937a2000SPeter Wemm *
56937a2000SPeter Wemm * diff4 -- Variance adjusted diff algorithm
57937a2000SPeter Wemm *
58937a2000SPeter Wemm * 1. Create a diff O:L and call that P.
59937a2000SPeter Wemm *
60937a2000SPeter Wemm * 2. Morph P into a 3-way diff by performing the following
61937a2000SPeter Wemm * transformation: O:L -> O:O:L.
62937a2000SPeter Wemm *
63937a2000SPeter Wemm * 3. Create a diff A:O.
64937a2000SPeter Wemm *
65937a2000SPeter Wemm * 4. Using A:O...
66937a2000SPeter Wemm *
67937a2000SPeter Wemm * #. Using M:A...
68937a2000SPeter Wemm *
69937a2000SPeter Wemm * #. Resolve conflicts...
70937a2000SPeter Wemm *
71937a2000SPeter Wemm
72937a2000SPeter Wemm 1. Out-range added line: decrement the line numbers in every hunk in P
73937a2000SPeter Wemm that comes after the addition. This undoes the effect of the add, since
74937a2000SPeter Wemm the add never happened in D.
75937a2000SPeter Wemm
76937a2000SPeter Wemm 2. Out-range deleted line: increment the line numbers in every hunk in P
77937a2000SPeter Wemm that comes after the deletion. This undoes the effect of the deletion,
78937a2000SPeter Wemm since the deletion never happened in D.
79937a2000SPeter Wemm
80937a2000SPeter Wemm 3. Out-range edited line: do nothing. Out-range edits are irrelevant to P.
81937a2000SPeter Wemm
82937a2000SPeter Wemm 4. Added line in context range in P: remove the corresponding line from
83937a2000SPeter Wemm the context, optionally replacing it with new context based on that
84937a2000SPeter Wemm region in M, and adjust line numbers and mappings appropriately.
85937a2000SPeter Wemm
86937a2000SPeter Wemm 5. Added line in affected text range in P: this is a dependency problem
87937a2000SPeter Wemm -- part of the change T:18-T:19 depends on changes introduced to T after
88937a2000SPeter Wemm B branched. There are several possible behaviors, depending on what the
89937a2000SPeter Wemm user wants. One is to generate an informative error, stating that
90937a2000SPeter Wemm T:18-T:19 depends on some other change (T:N-T:M, where N>=8, M<=18,
91937a2000SPeter Wemm and M-N == 1); the exact revisions can be discovered automatically using
92937a2000SPeter Wemm the same process as "cvs annotate", though it may take some time to do
93937a2000SPeter Wemm so. Another option is to include the change in P, as an insertion of the
94937a2000SPeter Wemm "after" version of the text, and adjust line numbers and mappings
95937a2000SPeter Wemm accordingly. (And if all this isn't sounding a lot like a directory
96937a2000SPeter Wemm merge algorithm, try drinking more of the Kool-Aid.) A third option is
97937a2000SPeter Wemm to include it as an insertion, but with metadata (such as CVS-style
98937a2000SPeter Wemm conflict markers) indicating that the line attempting to be patched
99937a2000SPeter Wemm does not exist in B.
100937a2000SPeter Wemm
101937a2000SPeter Wemm 6. Deleted line that is in-range in P: request another universe -- this
102937a2000SPeter Wemm situation can't happen in ours.
103937a2000SPeter Wemm
104937a2000SPeter Wemm 7. In-range edited line: reverse that edit in the "before" version of the
105937a2000SPeter Wemm corresponding line in the appropriate hunk in P, to obtain the version of
106937a2000SPeter Wemm the line that will be found in B when P is applied.
107937a2000SPeter Wemm */
108937a2000SPeter Wemm
109937a2000SPeter Wemm
110937a2000SPeter Wemm static void
adjust_diff(svn_diff_t * diff,svn_diff_t * adjust)111937a2000SPeter Wemm adjust_diff(svn_diff_t *diff, svn_diff_t *adjust)
112937a2000SPeter Wemm {
113937a2000SPeter Wemm svn_diff_t *hunk;
114937a2000SPeter Wemm apr_off_t range_start;
115937a2000SPeter Wemm apr_off_t range_end;
116937a2000SPeter Wemm apr_off_t adjustment;
117937a2000SPeter Wemm
118937a2000SPeter Wemm for (; adjust; adjust = adjust->next)
119937a2000SPeter Wemm {
120937a2000SPeter Wemm range_start = adjust->modified_start;
121937a2000SPeter Wemm range_end = range_start + adjust->modified_length;
122937a2000SPeter Wemm adjustment = adjust->original_length - adjust->modified_length;
123937a2000SPeter Wemm
124937a2000SPeter Wemm /* No change in line count, so no modifications. [3, 7] */
125937a2000SPeter Wemm if (adjustment == 0)
126937a2000SPeter Wemm continue;
127937a2000SPeter Wemm
128937a2000SPeter Wemm for (hunk = diff; hunk; hunk = hunk->next)
129937a2000SPeter Wemm {
130937a2000SPeter Wemm /* Changes are in the range before this hunk. Adjust the start
131937a2000SPeter Wemm * of the hunk. [1, 2]
132937a2000SPeter Wemm */
133937a2000SPeter Wemm if (hunk->modified_start >= range_end)
134937a2000SPeter Wemm {
135937a2000SPeter Wemm hunk->modified_start += adjustment;
136937a2000SPeter Wemm continue;
137937a2000SPeter Wemm }
138937a2000SPeter Wemm
139937a2000SPeter Wemm /* Changes are in the range beyond this hunk. No adjustments
140937a2000SPeter Wemm * needed. [1, 2]
141937a2000SPeter Wemm */
142937a2000SPeter Wemm if (hunk->modified_start + hunk->modified_length <= range_start)
143937a2000SPeter Wemm continue;
144937a2000SPeter Wemm
145937a2000SPeter Wemm /* From here on changes are in the range of this hunk. */
146937a2000SPeter Wemm
147937a2000SPeter Wemm /* This is a context hunk. Adjust the length. [4]
148937a2000SPeter Wemm */
149937a2000SPeter Wemm if (hunk->type == svn_diff__type_diff_modified)
150937a2000SPeter Wemm {
151937a2000SPeter Wemm hunk->modified_length += adjustment;
152937a2000SPeter Wemm continue;
153937a2000SPeter Wemm }
154937a2000SPeter Wemm
155937a2000SPeter Wemm /* Mark as conflicted. This happens in the reverse case when a line
156937a2000SPeter Wemm * is added in range and in the forward case when a line is deleted
157937a2000SPeter Wemm * in range. [5 (reverse), 6 (forward)]
158937a2000SPeter Wemm */
159937a2000SPeter Wemm if (adjustment < 0)
160937a2000SPeter Wemm hunk->type = svn_diff__type_conflict;
161937a2000SPeter Wemm
162937a2000SPeter Wemm /* Adjust the length of this hunk (reverse the change). [5, 6] */
163937a2000SPeter Wemm hunk->modified_length -= adjustment;
164937a2000SPeter Wemm }
165937a2000SPeter Wemm }
166937a2000SPeter Wemm }
167937a2000SPeter Wemm
168937a2000SPeter Wemm svn_error_t *
svn_diff_diff4_2(svn_diff_t ** diff,void * diff_baton,const svn_diff_fns2_t * vtable,apr_pool_t * pool)169937a2000SPeter Wemm svn_diff_diff4_2(svn_diff_t **diff,
170937a2000SPeter Wemm void *diff_baton,
171937a2000SPeter Wemm const svn_diff_fns2_t *vtable,
172937a2000SPeter Wemm apr_pool_t *pool)
173937a2000SPeter Wemm {
174937a2000SPeter Wemm svn_diff__tree_t *tree;
175937a2000SPeter Wemm svn_diff__position_t *position_list[4];
176937a2000SPeter Wemm svn_diff__token_index_t num_tokens;
177937a2000SPeter Wemm svn_diff__token_index_t *token_counts[4];
178937a2000SPeter Wemm svn_diff_datasource_e datasource[] = {svn_diff_datasource_original,
179937a2000SPeter Wemm svn_diff_datasource_modified,
180937a2000SPeter Wemm svn_diff_datasource_latest,
181937a2000SPeter Wemm svn_diff_datasource_ancestor};
182937a2000SPeter Wemm svn_diff__lcs_t *lcs_ol;
183937a2000SPeter Wemm svn_diff__lcs_t *lcs_adjust;
184937a2000SPeter Wemm svn_diff_t *diff_ol;
185937a2000SPeter Wemm svn_diff_t *diff_adjust;
186937a2000SPeter Wemm svn_diff_t *hunk;
187937a2000SPeter Wemm apr_pool_t *subpool;
188937a2000SPeter Wemm apr_pool_t *subpool2;
189937a2000SPeter Wemm apr_pool_t *subpool3;
190937a2000SPeter Wemm apr_off_t prefix_lines = 0;
191937a2000SPeter Wemm apr_off_t suffix_lines = 0;
192937a2000SPeter Wemm
193937a2000SPeter Wemm *diff = NULL;
194937a2000SPeter Wemm
195937a2000SPeter Wemm subpool = svn_pool_create(pool);
196937a2000SPeter Wemm subpool2 = svn_pool_create(subpool);
197937a2000SPeter Wemm subpool3 = svn_pool_create(subpool2);
198937a2000SPeter Wemm
199937a2000SPeter Wemm svn_diff__tree_create(&tree, subpool3);
200937a2000SPeter Wemm
201937a2000SPeter Wemm SVN_ERR(vtable->datasources_open(diff_baton, &prefix_lines, &suffix_lines,
202937a2000SPeter Wemm datasource, 4));
203937a2000SPeter Wemm
204937a2000SPeter Wemm SVN_ERR(svn_diff__get_tokens(&position_list[0],
205937a2000SPeter Wemm tree,
206937a2000SPeter Wemm diff_baton, vtable,
207937a2000SPeter Wemm svn_diff_datasource_original,
208937a2000SPeter Wemm prefix_lines,
209937a2000SPeter Wemm subpool2));
210937a2000SPeter Wemm
211937a2000SPeter Wemm SVN_ERR(svn_diff__get_tokens(&position_list[1],
212937a2000SPeter Wemm tree,
213937a2000SPeter Wemm diff_baton, vtable,
214937a2000SPeter Wemm svn_diff_datasource_modified,
215937a2000SPeter Wemm prefix_lines,
216937a2000SPeter Wemm subpool));
217937a2000SPeter Wemm
218937a2000SPeter Wemm SVN_ERR(svn_diff__get_tokens(&position_list[2],
219937a2000SPeter Wemm tree,
220937a2000SPeter Wemm diff_baton, vtable,
221937a2000SPeter Wemm svn_diff_datasource_latest,
222937a2000SPeter Wemm prefix_lines,
223937a2000SPeter Wemm subpool));
224937a2000SPeter Wemm
225937a2000SPeter Wemm SVN_ERR(svn_diff__get_tokens(&position_list[3],
226937a2000SPeter Wemm tree,
227937a2000SPeter Wemm diff_baton, vtable,
228937a2000SPeter Wemm svn_diff_datasource_ancestor,
229937a2000SPeter Wemm prefix_lines,
230937a2000SPeter Wemm subpool2));
231937a2000SPeter Wemm
232937a2000SPeter Wemm num_tokens = svn_diff__get_node_count(tree);
233937a2000SPeter Wemm
234937a2000SPeter Wemm /* Get rid of the tokens, we don't need them to calc the diff */
235937a2000SPeter Wemm if (vtable->token_discard_all != NULL)
236937a2000SPeter Wemm vtable->token_discard_all(diff_baton);
237937a2000SPeter Wemm
238937a2000SPeter Wemm /* We don't need the nodes in the tree either anymore, nor the tree itself */
239937a2000SPeter Wemm svn_pool_clear(subpool3);
240937a2000SPeter Wemm
241937a2000SPeter Wemm token_counts[0] = svn_diff__get_token_counts(position_list[0], num_tokens,
242937a2000SPeter Wemm subpool);
243937a2000SPeter Wemm token_counts[1] = svn_diff__get_token_counts(position_list[1], num_tokens,
244937a2000SPeter Wemm subpool);
245937a2000SPeter Wemm token_counts[2] = svn_diff__get_token_counts(position_list[2], num_tokens,
246937a2000SPeter Wemm subpool);
247937a2000SPeter Wemm token_counts[3] = svn_diff__get_token_counts(position_list[3], num_tokens,
248937a2000SPeter Wemm subpool);
249937a2000SPeter Wemm
250937a2000SPeter Wemm /* Get the lcs for original - latest */
251937a2000SPeter Wemm lcs_ol = svn_diff__lcs(position_list[0], position_list[2],
252937a2000SPeter Wemm token_counts[0], token_counts[2],
253937a2000SPeter Wemm num_tokens, prefix_lines,
254937a2000SPeter Wemm suffix_lines, subpool3);
255937a2000SPeter Wemm diff_ol = svn_diff__diff(lcs_ol, 1, 1, TRUE, pool);
256937a2000SPeter Wemm
257937a2000SPeter Wemm svn_pool_clear(subpool3);
258937a2000SPeter Wemm
259937a2000SPeter Wemm for (hunk = diff_ol; hunk; hunk = hunk->next)
260937a2000SPeter Wemm {
261937a2000SPeter Wemm hunk->latest_start = hunk->modified_start;
262937a2000SPeter Wemm hunk->latest_length = hunk->modified_length;
263937a2000SPeter Wemm hunk->modified_start = hunk->original_start;
264937a2000SPeter Wemm hunk->modified_length = hunk->original_length;
265937a2000SPeter Wemm
266937a2000SPeter Wemm if (hunk->type == svn_diff__type_diff_modified)
267937a2000SPeter Wemm hunk->type = svn_diff__type_diff_latest;
268937a2000SPeter Wemm else
269937a2000SPeter Wemm hunk->type = svn_diff__type_diff_modified;
270937a2000SPeter Wemm }
271937a2000SPeter Wemm
272937a2000SPeter Wemm /* Get the lcs for common ancestor - original
273*af80ed6aSPeter Wemm * Do reverse adjustments
274937a2000SPeter Wemm */
275937a2000SPeter Wemm lcs_adjust = svn_diff__lcs(position_list[3], position_list[2],
276937a2000SPeter Wemm token_counts[3], token_counts[2],
277937a2000SPeter Wemm num_tokens, prefix_lines,
278937a2000SPeter Wemm suffix_lines, subpool3);
279937a2000SPeter Wemm diff_adjust = svn_diff__diff(lcs_adjust, 1, 1, FALSE, subpool3);
280937a2000SPeter Wemm adjust_diff(diff_ol, diff_adjust);
281937a2000SPeter Wemm
282937a2000SPeter Wemm svn_pool_clear(subpool3);
283937a2000SPeter Wemm
284937a2000SPeter Wemm /* Get the lcs for modified - common ancestor
285937a2000SPeter Wemm * Do forward adjustments
286937a2000SPeter Wemm */
287937a2000SPeter Wemm lcs_adjust = svn_diff__lcs(position_list[1], position_list[3],
288937a2000SPeter Wemm token_counts[1], token_counts[3],
289937a2000SPeter Wemm num_tokens, prefix_lines,
290937a2000SPeter Wemm suffix_lines, subpool3);
291937a2000SPeter Wemm diff_adjust = svn_diff__diff(lcs_adjust, 1, 1, FALSE, subpool3);
292937a2000SPeter Wemm adjust_diff(diff_ol, diff_adjust);
293937a2000SPeter Wemm
294937a2000SPeter Wemm /* Get rid of the position lists for original and ancestor, and delete
295937a2000SPeter Wemm * our scratchpool.
296937a2000SPeter Wemm */
297937a2000SPeter Wemm svn_pool_destroy(subpool2);
298937a2000SPeter Wemm
299937a2000SPeter Wemm /* Now we try and resolve the conflicts we encountered */
300937a2000SPeter Wemm for (hunk = diff_ol; hunk; hunk = hunk->next)
301937a2000SPeter Wemm {
302937a2000SPeter Wemm if (hunk->type == svn_diff__type_conflict)
303937a2000SPeter Wemm {
304937a2000SPeter Wemm svn_diff__resolve_conflict(hunk, &position_list[1],
305937a2000SPeter Wemm &position_list[2], num_tokens, pool);
306937a2000SPeter Wemm }
307937a2000SPeter Wemm }
308937a2000SPeter Wemm
309937a2000SPeter Wemm svn_pool_destroy(subpool);
310937a2000SPeter Wemm
311937a2000SPeter Wemm *diff = diff_ol;
312937a2000SPeter Wemm
313937a2000SPeter Wemm return SVN_NO_ERROR;
314937a2000SPeter Wemm }
315