source: valtobtest/subversion-1.6.2/subversion/libsvn_diff/diff4.c @ 3

Last change on this file since 3 was 3, checked in by valtob, 15 years ago

subversion source 1.6.2 as test

File size: 8.9 KB
Line 
1/*
2 * diff.c :  routines for doing diffs
3 *
4 * ====================================================================
5 * Copyright (c) 2000-2004 CollabNet.  All rights reserved.
6 *
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution.  The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
12 *
13 * This software consists of voluntary contributions made by many
14 * individuals.  For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
17 */
18
19
20#include <apr.h>
21#include <apr_pools.h>
22#include <apr_general.h>
23
24#include "svn_pools.h"
25#include "svn_error.h"
26#include "svn_diff.h"
27#include "svn_types.h"
28
29#include "diff.h"
30
31/*
32 * Variance adjustment rules:
33 *
34 * http://subversion.tigris.org/variance-adjusted-patching.html
35 *
36 * ###: Expand this comment to contain the full set of adjustment
37 * ###: rules instead of pointing to a webpage.
38 */
39
40/*
41 * In the text below consider the following:
42 *
43 * O     = Original
44 * M     = Modified
45 * L     = Latest
46 * A     = Ancestor
47 * X:Y   = diff between X and Y
48 * X:Y:Z = 3-way diff between X, Y and Z
49 * P     = O:L, possibly adjusted
50 *
51 * diff4 -- Variance adjusted diff algorithm
52 *
53 * 1. Create a diff O:L and call that P.
54 *
55 * 2. Morph P into a 3-way diff by performing the following
56 *    transformation: O:L -> O:O:L.
57 *
58 * 3. Create a diff A:O.
59 *
60 * 4. Using A:O...
61 *
62 * #. Using M:A...
63 *
64 * #. Resolve conflicts...
65 *
66
67   1. Out-range added line: decrement the line numbers in every hunk in P
68      that comes after the addition. This undoes the effect of the add, since
69      the add never happened in D.
70
71   2. Out-range deleted line: increment the line numbers in every hunk in P
72      that comes after the deletion. This undoes the effect of the deletion,
73      since the deletion never happened in D.
74
75   3. Out-range edited line: do nothing. Out-range edits are irrelevant to P.
76
77   4. Added line in context range in P: remove the corresponding line from
78      the context, optionally replacing it with new context based on that
79      region in M, and adjust line numbers and mappings appropriately.
80
81   5. Added line in affected text range in P: this is a dependency problem
82      -- part of the change T:18-T:19 depends on changes introduced to T after
83      B branched. There are several possible behaviors, depending on what the
84      user wants. One is to generate an informative error, stating that
85      T:18-T:19 depends on some other change (T:N-T:M, where N>=8, M<=18,
86      and M-N == 1); the exact revisions can be discovered automatically using
87      the same process as "cvs annotate", though it may take some time to do
88      so. Another option is to include the change in P, as an insertion of the
89      "after" version of the text, and adjust line numbers and mappings
90      accordingly. (And if all this isn't sounding a lot like a directory
91      merge algorithm, try drinking more of the Kool-Aid.) A third option is
92      to include it as an insertion, but with metadata (such as CVS-style
93      conflict markers) indicating that the line attempting to be patched
94      does not exist in B.
95
96   6. Deleted line that is in-range in P: request another universe -- this
97      situation can't happen in ours.
98
99   7. In-range edited line: reverse that edit in the "before" version of the
100      corresponding line in the appropriate hunk in P, to obtain the version of
101      the line that will be found in B when P is applied.
102*/
103
104
105static void
106adjust_diff(svn_diff_t *diff, svn_diff_t *adjust)
107{
108  svn_diff_t *hunk;
109  apr_off_t range_start;
110  apr_off_t range_end;
111  apr_off_t adjustment;
112
113  for (; adjust; adjust = adjust->next)
114    {
115      range_start = adjust->modified_start;
116      range_end = range_start + adjust->modified_length;
117      adjustment = adjust->original_length - adjust->modified_length;
118
119      /* No change in line count, so no modifications. [3, 7] */
120      if (adjustment == 0)
121        continue;
122
123      for (hunk = diff; hunk; hunk = hunk->next)
124        {
125          /* Changes are in the range before this hunk.  Adjust the start
126           * of the hunk. [1, 2]
127           */
128          if (hunk->modified_start >= range_end)
129            {
130              hunk->modified_start += adjustment;
131              continue;
132            }
133
134          /* Changes are in the range beyond this hunk.  No adjustments
135           * needed. [1, 2]
136           */
137          if (hunk->modified_start + hunk->modified_length <= range_start)
138            continue;
139
140          /* From here on changes are in the range of this hunk. */
141
142          /* This is a context hunk.  Adjust the length. [4]
143           */
144          if (hunk->type == svn_diff__type_diff_modified)
145            {
146              hunk->modified_length += adjustment;
147              continue;
148            }
149
150          /* Mark as conflicted. This happens in the reverse case when a line
151           * is added in range and in the forward case when a line is deleted
152           * in range. [5 (reverse), 6 (forward)]
153           */
154          if (adjustment < 0)
155              hunk->type = svn_diff__type_conflict;
156
157          /* Adjust the length of this hunk (reverse the change). [5, 6] */
158          hunk->modified_length -= adjustment;
159        }
160    }
161}
162
163svn_error_t *
164svn_diff_diff4(svn_diff_t **diff,
165               void *diff_baton,
166               const svn_diff_fns_t *vtable,
167               apr_pool_t *pool)
168{
169  svn_diff__tree_t *tree;
170  svn_diff__position_t *position_list[4];
171  svn_diff__lcs_t *lcs_ol;
172  svn_diff__lcs_t *lcs_adjust;
173  svn_diff_t *diff_ol;
174  svn_diff_t *diff_adjust;
175  svn_diff_t *hunk;
176  apr_pool_t *subpool;
177  apr_pool_t *subpool2;
178  apr_pool_t *subpool3;
179
180  *diff = NULL;
181
182  subpool = svn_pool_create(pool);
183  subpool2 = svn_pool_create(subpool);
184  subpool3 = svn_pool_create(subpool2);
185
186  svn_diff__tree_create(&tree, subpool3);
187
188  SVN_ERR(svn_diff__get_tokens(&position_list[0],
189                               tree,
190                               diff_baton, vtable,
191                               svn_diff_datasource_original,
192                               subpool2));
193
194  SVN_ERR(svn_diff__get_tokens(&position_list[1],
195                               tree,
196                               diff_baton, vtable,
197                               svn_diff_datasource_modified,
198                               subpool));
199
200  SVN_ERR(svn_diff__get_tokens(&position_list[2],
201                               tree,
202                               diff_baton, vtable,
203                               svn_diff_datasource_latest,
204                               subpool));
205
206  SVN_ERR(svn_diff__get_tokens(&position_list[3],
207                               tree,
208                               diff_baton, vtable,
209                               svn_diff_datasource_ancestor,
210                               subpool2));
211
212  /* Get rid of the tokens, we don't need them to calc the diff */
213  if (vtable->token_discard_all != NULL)
214    vtable->token_discard_all(diff_baton);
215
216  /* We don't need the nodes in the tree either anymore, nor the tree itself */
217  svn_pool_clear(subpool3);
218
219  /* Get the lcs for original - latest */
220  lcs_ol = svn_diff__lcs(position_list[0], position_list[2], subpool3);
221  diff_ol = svn_diff__diff(lcs_ol, 1, 1, TRUE, pool);
222
223  svn_pool_clear(subpool3);
224
225  for (hunk = diff_ol; hunk; hunk = hunk->next)
226    {
227      hunk->latest_start = hunk->modified_start;
228      hunk->latest_length = hunk->modified_length;
229      hunk->modified_start = hunk->original_start;
230      hunk->modified_length = hunk->original_length;
231
232      if (hunk->type == svn_diff__type_diff_modified)
233          hunk->type = svn_diff__type_diff_latest;
234      else
235          hunk->type = svn_diff__type_diff_modified;
236    }
237
238  /* Get the lcs for common ancestor - original
239   * Do reverse adjustements
240   */
241  lcs_adjust = svn_diff__lcs(position_list[3], position_list[2], subpool3);
242  diff_adjust = svn_diff__diff(lcs_adjust, 1, 1, FALSE, subpool3);
243  adjust_diff(diff_ol, diff_adjust);
244
245  svn_pool_clear(subpool3);
246
247  /* Get the lcs for modified - common ancestor
248   * Do forward adjustments
249   */
250  lcs_adjust = svn_diff__lcs(position_list[1], position_list[3], subpool3);
251  diff_adjust = svn_diff__diff(lcs_adjust, 1, 1, FALSE, subpool3);
252  adjust_diff(diff_ol, diff_adjust);
253
254  /* Get rid of the position lists for original and ancestor, and delete
255   * our scratchpool.
256   */
257  svn_pool_destroy(subpool2);
258
259  /* Now we try and resolve the conflicts we encountered */
260  for (hunk = diff_ol; hunk; hunk = hunk->next)
261    {
262      if (hunk->type == svn_diff__type_conflict)
263        {
264          svn_diff__resolve_conflict(hunk, &position_list[1],
265                                     &position_list[2], pool);
266        }
267    }
268
269  svn_pool_destroy(subpool);
270
271  *diff = diff_ol;
272
273  return SVN_NO_ERROR;
274}
Note: See TracBrowser for help on using the repository browser.