1 /*
2  * merge.c: merging
3  *
4  * ====================================================================
5  *    Licensed to the Apache Software Foundation (ASF) under one
6  *    or more contributor license agreements.  See the NOTICE file
7  *    distributed with this work for additional information
8  *    regarding copyright ownership.  The ASF licenses this file
9  *    to you under the Apache License, Version 2.0 (the
10  *    "License"); you may not use this file except in compliance
11  *    with the License.  You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  *    Unless required by applicable law or agreed to in writing,
16  *    software distributed under the License is distributed on an
17  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18  *    KIND, either express or implied.  See the License for the
19  *    specific language governing permissions and limitations
20  *    under the License.
21  * ====================================================================
22  */
23 
24 /* ==================================================================== */
25 
26 
27 
28 /*** Includes ***/
29 
30 #include <assert.h>
31 #include <apr_strings.h>
32 #include <apr_tables.h>
33 #include <apr_hash.h>
34 #include "svn_types.h"
35 #include "svn_hash.h"
36 #include "svn_wc.h"
37 #include "svn_delta.h"
38 #include "svn_diff.h"
39 #include "svn_mergeinfo.h"
40 #include "svn_client.h"
41 #include "svn_string.h"
42 #include "svn_error.h"
43 #include "svn_dirent_uri.h"
44 #include "svn_path.h"
45 #include "svn_io.h"
46 #include "svn_utf.h"
47 #include "svn_pools.h"
48 #include "svn_config.h"
49 #include "svn_props.h"
50 #include "svn_time.h"
51 #include "svn_sorts.h"
52 #include "svn_subst.h"
53 #include "svn_ra.h"
54 #include "client.h"
55 #include "mergeinfo.h"
56 
57 #include "private/svn_fspath.h"
58 #include "private/svn_mergeinfo_private.h"
59 #include "private/svn_client_private.h"
60 #include "private/svn_sorts_private.h"
61 #include "private/svn_subr_private.h"
62 #include "private/svn_wc_private.h"
63 
64 #include "svn_private_config.h"
65 
66 
67 /*-----------------------------------------------------------------------*/
68 
69 /* MERGEINFO MERGE SOURCE NORMALIZATION
70  *
71  * Nearly any helper function herein that accepts two URL/revision
72  * pairs (or equivalent struct merge_source_t) expects one of two things
73  * to be true:
74  *
75  *    1.  that mergeinfo is not being recorded at all for this
76  *        operation, or
77  *
78  *    2.  that the pairs represent two locations along a single line
79  *        of version history such that there are no copies in the
80  *        history of the object between the locations when treating
81  *        the oldest of the two locations as non-inclusive.  In other
82  *        words, if there is a copy at all between them, there is only
83  *        one copy and its source was the oldest of the two locations.
84  *
85  * We use svn_ra_get_location_segments() to split a given range of
86  * revisions across an object's history into several which obey these
87  * rules.  For example, an extract from the log of Subversion's own
88  * /subversion/tags/1.4.5 directory shows the following copies between
89  * r859500 and r866500 (omitting the '/subversion' prefix for clarity):
90  *
91  *    r859598:
92  *      A /branches/1.4.x  (from /trunk:859597)
93  *
94  *    r865417:
95  *      A /tags/1.4.4      (from /branches/1.4.x:865262)
96  *    # Notice that this copy leaves a gap between 865262 and 865417.
97  *
98  *    r866420:
99  *      A /branches/1.4.5  (from /tags/1.4.4:866419)
100  *
101  *    r866425:
102  *      D /branches/1.4.5
103  *      A /tags/1.4.5      (from /branches/1.4.5:866424)
104  *
105  * In graphical form:
106  *
107  *                859500 859597 865262        866419 866424 866500
108  *                  .      .      .             .      .      .
109  *    trunk       ------------------------------------------------
110  *                         \      .             .      .
111  *    branches/1.4.x        A-------------------------------------
112  *                          .     \______       .      .
113  *                          .            \      .      .
114  *    tags/1.4.4            .             A-----------------------
115  *                          .             .     \      .
116  *    branches/1.4.5        .             .      A------D
117  *                          .             .      .     \.
118  *    tags/1.4.5            .             .      .      A---------
119  *                          .             .      .      .
120  *                       859598        865417 866420 866425
121  *
122  * A merge of the difference between r859500 and r866500 of this directory
123  * gets split into sequential merges of the following location pairs.
124  *
125  *                859500 859597 865262 865416 866419 866424 866500
126  *                  .      .      .      .      .      .      .
127  *    trunk         (======]      .      .      .      .      .
128  *                                .      .      .      .      .
129  *    trunk                (      .      .      .      .      .
130  *    branches/1.4.x        ======]      .      .      .      .
131  *                                       .      .      .      .
132  *    branches/1.4.x              (      .      .      .      .
133  *    tags/1.4.4                   =============]      .      .
134  *    implicit_src_gap            (======]      .      .      .
135  *                                              .      .      .
136  *    tags/1.4.4                                (      .      .
137  *    branches/1.4.5                             ======]      .
138  *                                                     .      .
139  *    branches/1.4.5                                   (      .
140  *    tags/1.4.5                                        ======]
141  *
142  * which are represented in merge_source_t as:
143  *
144  *    [/trunk:859500, /trunk:859597]
145  *    (recorded in svn:mergeinfo as /trunk:859501-859597)
146  *
147  *    [/trunk:859597, /branches/1.4.x:865262]
148  *    (recorded in svn:mergeinfo as /branches/1.4.x:859598-865262)
149  *
150  *    [/branches/1.4.x:865262, /tags/1.4.4@866419]
151  *    (recorded in svn:mergeinfo as /tags/1.4.4:865263-866419)
152  *    (and there is a gap, the revision range [865262, 865416])
153  *
154  *    [/tags/1.4.4@866419, /branches/1.4.5@866424]
155  *    (recorded in svn:mergeinfo as /branches/1.4.5:866420-866424)
156  *
157  *    [/branches/1.4.5@866424, /tags/1.4.5@866500]
158  *    (recorded in svn:mergeinfo as /tags/1.4.5:866425-866500)
159  *
160  * Our helper functions would then operate on one of these location
161  * pairs at a time.
162  */
163 
164 /* WHICH SVN_CLIENT_MERGE* API DO I WANT?
165  *
166  * libsvn_client has three public merge APIs; they are all wrappers
167  * around the do_merge engine.  Which one to use depends on the number
168  * of URLs passed as arguments and whether or not specific merge
169  * ranges (-c/-r) are specified.
170  *
171  *                 1 URL                        2 URLs
172  * +----+--------------------------------+---------------------+
173  * | -c |       mergeinfo-driven         |                     |
174  * | or |        cherrypicking           |                     |
175  * | -r |    (svn_client_merge_peg)      |                     |
176  * |----+--------------------------------+                     |
177  * |    |       mergeinfo-driven         |     unsupported     |
178  * |    |  'cherry harvest', i.e. merge  |                     |
179  * |    |  all revisions from URL that   |                     |
180  * | no |  have not already been merged  |                     |
181  * | -c |    (svn_client_merge_peg)      |                     |
182  * | or +--------------------------------+---------------------+
183  * | -r |      mergeinfo-driven          |   mergeinfo-writing |
184  * |    |        whole-branch            |    diff-and-apply   |
185  * |    |       heuristic merge          |  (svn_client_merge) |
186  * |    | (svn_client_merge_reintegrate) |                     |
187  * +----+--------------------------------+---------------------+
188  *
189  *
190  */
191 
192 /* THE CHILDREN_WITH_MERGEINFO ARRAY
193  *
194  * Many of the helper functions in this file pass around an
195  * apr_array_header_t *CHILDREN_WITH_MERGEINFO.  This is a depth first
196  * sorted array filled with svn_client__merge_path_t * describing the
197  * merge target and any of its subtrees which have explicit mergeinfo
198  * or otherwise need special attention during a merge.
199  *
200  * During mergeinfo unaware merges, CHILDREN_WITH_MERGEINFO contains
201  * contains only one element (added by do_mergeinfo_unaware_dir_merge)
202  * describing a contiguous range to be merged to the WC merge target.
203  *
204  * During mergeinfo aware merges CHILDREN_WITH_MERGEINFO is created
205  * by get_mergeinfo_paths() and outside of that function and its helpers
206  * should always meet the criteria dictated in get_mergeinfo_paths()'s doc
207  * string.  The elements of CHILDREN_WITH_MERGEINFO should never be NULL.
208  *
209  * For clarification on mergeinfo aware vs. mergeinfo unaware merges, see
210  * the doc string for HONOR_MERGEINFO().
211  */
212 
213 
214 /*-----------------------------------------------------------------------*/
215 
216 /*** Repos-Diff Editor Callbacks ***/
217 
218 struct merge_cmd_baton_t;
219 
220 struct notify_begin_state_t
221 {
222   /* Cache of which abspath was last notified. */
223   const char *last_abspath;
224 
225   /* Reference to the main merge baton */
226   struct merge_cmd_baton_t *merge_b;
227 
228   /* the wrapped notification callback */
229   svn_wc_notify_func2_t notify_func2;
230   void *notify_baton2;
231 };
232 
233 typedef struct merge_cmd_baton_t {
234   svn_boolean_t force_delete;         /* Delete a file/dir even if modified */
235   svn_boolean_t dry_run;
236   svn_boolean_t record_only;          /* Whether to merge only mergeinfo
237                                          differences. */
238   svn_boolean_t same_repos;           /* Whether the merge source repository
239                                          is the same repository as the
240                                          target.  Defaults to FALSE if DRY_RUN
241                                          is TRUE.*/
242   svn_boolean_t mergeinfo_capable;    /* Whether the merge source server
243                                          is capable of Merge Tracking. */
244   svn_boolean_t ignore_mergeinfo;     /* Don't honor mergeinfo; see
245                                          doc string of do_merge().  FALSE if
246                                          MERGE_SOURCE->ancestral is FALSE. */
247   svn_boolean_t diff_ignore_ancestry; /* Diff unrelated nodes as if related; see
248                                          doc string of do_merge().  FALSE if
249                                          MERGE_SOURCE->ancestral is FALSE. */
250   svn_boolean_t reintegrate_merge;    /* Whether this is a --reintegrate
251                                          merge or not. */
252   const merge_target_t *target;       /* Description of merge target node */
253 
254   /* The left and right URLs and revs.  The value of this field changes to
255      reflect the merge_source_t *currently* being merged by do_merge(). */
256   merge_source_t merge_source;
257 
258   /* Rangelist containing single range which describes the gap, if any,
259      in the natural history of the merge source currently being processed.
260      See https://issues.apache.org/jira/browse/SVN-3432.
261      Updated during each call to do_directory_merge().  May be NULL if there
262      is no gap. */
263   svn_rangelist_t *implicit_src_gap;
264 
265   /* Reference to the one-and-only CHILDREN_WITH_MERGEINFO (see global
266      comment) or a similar list for single-file-merges */
267   apr_array_header_t *children_with_mergeinfo;
268 
269   svn_client_ctx_t *ctx;              /* Client context for callbacks, etc. */
270 
271   /* The list of any paths which remained in conflict after a
272      resolution attempt was made.  We track this in-memory, rather
273      than just using WC entry state, since the latter doesn't help us
274      when in dry_run mode.
275      ### And because we only want to resolve conflicts that were
276          generated by this merge, not pre-existing ones? */
277   apr_hash_t *conflicted_paths;
278 
279   /* A list of absolute paths which had no explicit mergeinfo prior to the
280      merge but got explicit mergeinfo added by the merge.  This is populated
281      by merge_change_props() and is allocated in POOL so it is subject to the
282      lifetime limitations of POOL.  Is NULL if no paths are found which
283      meet the criteria or DRY_RUN is true. */
284   apr_hash_t *paths_with_new_mergeinfo;
285 
286   /* A list of absolute paths whose mergeinfo doesn't need updating after
287      the merge. This can be caused by the removal of mergeinfo by the merge
288      or by deleting the node itself.  This is populated by merge_change_props()
289      and the delete callbacks and is allocated in POOL so it is subject to the
290      lifetime limitations of POOL.  Is NULL if no paths are found which
291      meet the criteria or DRY_RUN is true. */
292   apr_hash_t *paths_with_deleted_mergeinfo;
293 
294   /* The list of absolute skipped paths, which should be examined and
295      cleared after each invocation of the callback.  The paths
296      are absolute.  Is NULL if MERGE_B->MERGE_SOURCE->ancestral and
297      MERGE_B->REINTEGRATE_MERGE are both false. */
298   apr_hash_t *skipped_abspaths;
299 
300   /* The list of absolute merged paths.  Unused if MERGE_B->MERGE_SOURCE->ancestral
301      and MERGE_B->REINTEGRATE_MERGE are both false. */
302   apr_hash_t *merged_abspaths;
303 
304   /* A hash of (const char *) absolute WC paths mapped to the same which
305      represent the roots of subtrees added by the merge. */
306   apr_hash_t *added_abspaths;
307 
308   /* A list of tree conflict victim absolute paths which may be NULL. */
309   apr_hash_t *tree_conflicted_abspaths;
310 
311   /* The diff3_cmd in ctx->config, if any, else null.  We could just
312      extract this as needed, but since more than one caller uses it,
313      we just set it up when this baton is created. */
314   const char *diff3_cmd;
315   const apr_array_header_t *merge_options;
316 
317   /* Array of file extension patterns to preserve as extensions in
318      generated conflict files. */
319   const apr_array_header_t *ext_patterns;
320 
321   /* RA sessions used throughout a merge operation.  Opened/re-parented
322      as needed.
323 
324      NOTE: During the actual merge editor drive, RA_SESSION1 is used
325      for the primary editing and RA_SESSION2 for fetching additional
326      information -- as necessary -- from the repository.  So during
327      this phase of the merge, you *must not* reparent RA_SESSION1; use
328      (temporarily reparenting if you must) RA_SESSION2 instead.  */
329   svn_ra_session_t *ra_session1;
330   svn_ra_session_t *ra_session2;
331 
332   /* During the merge, *USE_SLEEP is set to TRUE if a sleep will be required
333      afterwards to ensure timestamp integrity, or unchanged if not. */
334   svn_boolean_t *use_sleep;
335 
336   /* Pool which has a lifetime limited to one iteration over a given
337      merge source, i.e. it is cleared on every call to do_directory_merge()
338      or do_file_merge() in do_merge(). */
339   apr_pool_t *pool;
340 
341   /* Our notification callback, that adds a 'begin' notification */
342   svn_wc_notify_func2_t notify_func;
343   void *notify_baton;
344   struct notify_begin_state_t notify_begin;
345 
346 } merge_cmd_baton_t;
347 
348 
349 /* Return TRUE iff we should be taking account of mergeinfo in deciding what
350    changes to merge, for the merge described by MERGE_B.  Specifically, that
351    is if the merge source server is capable of merge tracking, the left-side
352    merge source is an ancestor of the right-side (or vice-versa), the merge
353    source is in the same repository as the merge target, and we are not
354    ignoring mergeinfo. */
355 static svn_boolean_t
HONOR_MERGEINFO(const merge_cmd_baton_t * merge_b)356 HONOR_MERGEINFO(const merge_cmd_baton_t *merge_b)
357 {
358   return (merge_b->mergeinfo_capable
359           && merge_b->merge_source.ancestral
360           && merge_b->same_repos
361           && (!merge_b->ignore_mergeinfo));
362 }
363 
364 
365 /* Return TRUE iff we should be recording mergeinfo for the merge described
366    by MERGE_B.  Specifically, that is if we are honoring mergeinfo and the
367    merge is not a dry run.  */
368 static svn_boolean_t
RECORD_MERGEINFO(const merge_cmd_baton_t * merge_b)369 RECORD_MERGEINFO(const merge_cmd_baton_t *merge_b)
370 {
371   return (HONOR_MERGEINFO(merge_b)
372           && !merge_b->dry_run);
373 }
374 
375 
376 /*-----------------------------------------------------------------------*/
377 
378 /*** Utilities ***/
379 
380 /* Return TRUE iff the session URL of RA_SESSION is equal to URL.  Useful in
381  * asserting preconditions. */
382 static svn_boolean_t
session_url_is(svn_ra_session_t * ra_session,const char * url,apr_pool_t * scratch_pool)383 session_url_is(svn_ra_session_t *ra_session,
384                const char *url,
385                apr_pool_t *scratch_pool)
386 {
387   const char *session_url;
388   svn_error_t *err
389     = svn_ra_get_session_url(ra_session, &session_url, scratch_pool);
390 
391   SVN_ERR_ASSERT_NO_RETURN(! err);
392   return strcmp(url, session_url) == 0;
393 }
394 
395 /* Return a new merge_source_t structure, allocated in RESULT_POOL,
396  * initialized with deep copies of LOC1 and LOC2 and ANCESTRAL. */
397 static merge_source_t *
merge_source_create(const svn_client__pathrev_t * loc1,const svn_client__pathrev_t * loc2,svn_boolean_t ancestral,apr_pool_t * result_pool)398 merge_source_create(const svn_client__pathrev_t *loc1,
399                     const svn_client__pathrev_t *loc2,
400                     svn_boolean_t ancestral,
401                     apr_pool_t *result_pool)
402 {
403   merge_source_t *s
404     = apr_palloc(result_pool, sizeof(*s));
405 
406   s->loc1 = svn_client__pathrev_dup(loc1, result_pool);
407   s->loc2 = svn_client__pathrev_dup(loc2, result_pool);
408   s->ancestral = ancestral;
409   return s;
410 }
411 
412 /* Return a deep copy of SOURCE, allocated in RESULT_POOL. */
413 static merge_source_t *
merge_source_dup(const merge_source_t * source,apr_pool_t * result_pool)414 merge_source_dup(const merge_source_t *source,
415                  apr_pool_t *result_pool)
416 {
417   merge_source_t *s = apr_palloc(result_pool, sizeof(*s));
418 
419   s->loc1 = svn_client__pathrev_dup(source->loc1, result_pool);
420   s->loc2 = svn_client__pathrev_dup(source->loc2, result_pool);
421   s->ancestral = source->ancestral;
422   return s;
423 }
424 
425 /* Return SVN_ERR_UNSUPPORTED_FEATURE if URL is not inside the repository
426    of LOCAL_ABSPATH.  Use SCRATCH_POOL for temporary allocations. */
427 static svn_error_t *
check_repos_match(const merge_target_t * target,const char * local_abspath,const char * url,apr_pool_t * scratch_pool)428 check_repos_match(const merge_target_t *target,
429                   const char *local_abspath,
430                   const char *url,
431                   apr_pool_t *scratch_pool)
432 {
433   if (!svn_uri__is_ancestor(target->loc.repos_root_url, url))
434     return svn_error_createf(
435         SVN_ERR_UNSUPPORTED_FEATURE, NULL,
436          _("URL '%s' of '%s' is not in repository '%s'"),
437          url, svn_dirent_local_style(local_abspath, scratch_pool),
438          target->loc.repos_root_url);
439 
440   return SVN_NO_ERROR;
441 }
442 
443 /* Return TRUE iff the repository of LOCATION1 is the same as
444  * that of LOCATION2.  If STRICT_URLS is true, the URLs must
445  * match (and the UUIDs, just to be sure), otherwise just the UUIDs must
446  * match and the URLs can differ (a common case is http versus https). */
447 static svn_boolean_t
is_same_repos(const svn_client__pathrev_t * location1,const svn_client__pathrev_t * location2,svn_boolean_t strict_urls)448 is_same_repos(const svn_client__pathrev_t *location1,
449               const svn_client__pathrev_t *location2,
450               svn_boolean_t strict_urls)
451 {
452   if (strict_urls)
453     return (strcmp(location1->repos_root_url, location2->repos_root_url) == 0
454             && strcmp(location1->repos_uuid, location2->repos_uuid) == 0);
455   else
456     return (strcmp(location1->repos_uuid, location2->repos_uuid) == 0);
457 }
458 
459 /* If the repository identified of LOCATION1 is not the same as that
460  * of LOCATION2, throw a SVN_ERR_CLIENT_UNRELATED_RESOURCES
461  * error mentioning PATH1 and PATH2. For STRICT_URLS, see is_same_repos().
462  */
463 static svn_error_t *
check_same_repos(const svn_client__pathrev_t * location1,const char * path1,const svn_client__pathrev_t * location2,const char * path2,svn_boolean_t strict_urls,apr_pool_t * scratch_pool)464 check_same_repos(const svn_client__pathrev_t *location1,
465                  const char *path1,
466                  const svn_client__pathrev_t *location2,
467                  const char *path2,
468                  svn_boolean_t strict_urls,
469                  apr_pool_t *scratch_pool)
470 {
471   if (! is_same_repos(location1, location2, strict_urls))
472     return svn_error_createf(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
473                              _("'%s' must be from the same repository as "
474                                "'%s'"), path1, path2);
475   return SVN_NO_ERROR;
476 }
477 
478 /* Store LOCAL_ABSPATH in PATH_HASH after duplicating it into the pool
479    containing PATH_HASH. */
480 static APR_INLINE void
store_path(apr_hash_t * path_hash,const char * local_abspath)481 store_path(apr_hash_t *path_hash, const char *local_abspath)
482 {
483   const char *dup_path = apr_pstrdup(apr_hash_pool_get(path_hash),
484                                      local_abspath);
485 
486   svn_hash_sets(path_hash, dup_path, dup_path);
487 }
488 
489 /* Store LOCAL_ABSPATH in *PATH_HASH_P after duplicating it into the pool
490    containing *PATH_HASH_P.  If *PATH_HASH_P is NULL, then first set
491    *PATH_HASH_P to a new hash allocated from POOL.  */
492 static APR_INLINE void
alloc_and_store_path(apr_hash_t ** path_hash_p,const char * local_abspath,apr_pool_t * pool)493 alloc_and_store_path(apr_hash_t **path_hash_p,
494                      const char *local_abspath,
495                      apr_pool_t *pool)
496 {
497   if (! *path_hash_p)
498     *path_hash_p = apr_hash_make(pool);
499   store_path(*path_hash_p, local_abspath);
500 }
501 
502 /* Return whether any WC path was put in conflict by the merge
503    operation corresponding to MERGE_B. */
504 static APR_INLINE svn_boolean_t
is_path_conflicted_by_merge(merge_cmd_baton_t * merge_b)505 is_path_conflicted_by_merge(merge_cmd_baton_t *merge_b)
506 {
507   return (merge_b->conflicted_paths &&
508           apr_hash_count(merge_b->conflicted_paths) > 0);
509 }
510 
511 /* Return a state indicating whether the WC metadata matches the
512  * node kind on disk of the local path LOCAL_ABSPATH.
513  * Use MERGE_B to determine the dry-run details; particularly, if a dry run
514  * noted that it deleted this path, assume matching node kinds (as if both
515  * kinds were svn_node_none).
516  *
517  *   - Return svn_wc_notify_state_inapplicable if the node kind matches.
518  *   - Return 'obstructed' if there is a node on disk where none or a
519  *     different kind is expected, or if the disk node cannot be read.
520  *   - Return 'missing' if there is no node on disk but one is expected.
521  *     Also return 'missing' for server-excluded nodes (not here due to
522  *     authz or other reasons determined by the server).
523  *
524  * Optionally return a bit more info for interested users.
525  **/
526 static svn_error_t *
perform_obstruction_check(svn_wc_notify_state_t * obstruction_state,svn_boolean_t * deleted,svn_boolean_t * excluded,svn_node_kind_t * kind,svn_depth_t * parent_depth,const merge_cmd_baton_t * merge_b,const char * local_abspath,apr_pool_t * scratch_pool)527 perform_obstruction_check(svn_wc_notify_state_t *obstruction_state,
528                           svn_boolean_t *deleted,
529                           svn_boolean_t *excluded,
530                           svn_node_kind_t *kind,
531                           svn_depth_t *parent_depth,
532                           const merge_cmd_baton_t *merge_b,
533                           const char *local_abspath,
534                           apr_pool_t *scratch_pool)
535 {
536   svn_wc_context_t *wc_ctx = merge_b->ctx->wc_ctx;
537   svn_node_kind_t wc_kind;
538   svn_boolean_t check_root;
539 
540   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
541 
542   *obstruction_state = svn_wc_notify_state_inapplicable;
543 
544   if (deleted)
545     *deleted = FALSE;
546   if (kind)
547     *kind = svn_node_none;
548 
549   if (kind == NULL)
550     kind = &wc_kind;
551 
552   check_root = ! strcmp(local_abspath, merge_b->target->abspath);
553 
554   SVN_ERR(svn_wc__check_for_obstructions(obstruction_state,
555                                          kind,
556                                          deleted,
557                                          excluded,
558                                          parent_depth,
559                                          wc_ctx, local_abspath,
560                                          check_root,
561                                          scratch_pool));
562   return SVN_NO_ERROR;
563 }
564 
565 /* Create *LEFT and *RIGHT conflict versions for conflict victim
566  * at VICTIM_ABSPATH, with merge-left node kind MERGE_LEFT_NODE_KIND
567  * and merge-right node kind MERGE_RIGHT_NODE_KIND, using information
568  * obtained from MERGE_SOURCE and TARGET.
569  * Allocate returned conflict versions in RESULT_POOL. */
570 static svn_error_t *
make_conflict_versions(const svn_wc_conflict_version_t ** left,const svn_wc_conflict_version_t ** right,const char * victim_abspath,svn_node_kind_t merge_left_node_kind,svn_node_kind_t merge_right_node_kind,const merge_source_t * merge_source,const merge_target_t * target,apr_pool_t * result_pool,apr_pool_t * scratch_pool)571 make_conflict_versions(const svn_wc_conflict_version_t **left,
572                        const svn_wc_conflict_version_t **right,
573                        const char *victim_abspath,
574                        svn_node_kind_t merge_left_node_kind,
575                        svn_node_kind_t merge_right_node_kind,
576                        const merge_source_t *merge_source,
577                        const merge_target_t *target,
578                        apr_pool_t *result_pool,
579                        apr_pool_t *scratch_pool)
580 {
581   const char *child = svn_dirent_skip_ancestor(target->abspath,
582                                                victim_abspath);
583   const char *left_relpath, *right_relpath;
584 
585   SVN_ERR_ASSERT(child != NULL);
586   left_relpath = svn_client__pathrev_relpath(merge_source->loc1,
587                                              scratch_pool);
588   right_relpath = svn_client__pathrev_relpath(merge_source->loc2,
589                                               scratch_pool);
590 
591   *left = svn_wc_conflict_version_create2(
592             merge_source->loc1->repos_root_url,
593             merge_source->loc1->repos_uuid,
594             svn_relpath_join(left_relpath, child, scratch_pool),
595             merge_source->loc1->rev,
596             merge_left_node_kind, result_pool);
597 
598   *right = svn_wc_conflict_version_create2(
599              merge_source->loc2->repos_root_url,
600              merge_source->loc2->repos_uuid,
601              svn_relpath_join(right_relpath, child, scratch_pool),
602              merge_source->loc2->rev,
603              merge_right_node_kind, result_pool);
604 
605   return SVN_NO_ERROR;
606 }
607 
608 /* Helper for filter_self_referential_mergeinfo()
609 
610    *MERGEINFO is a non-empty, non-null collection of mergeinfo.
611 
612    Remove all mergeinfo from *MERGEINFO that describes revision ranges
613    greater than REVISION.  Put a copy of any removed mergeinfo, allocated
614    in POOL, into *YOUNGER_MERGEINFO.
615 
616    If no mergeinfo is removed from *MERGEINFO then *YOUNGER_MERGEINFO is set
617    to NULL.  If all mergeinfo is removed from *MERGEINFO then *MERGEINFO is
618    set to NULL.
619    */
620 static svn_error_t*
split_mergeinfo_on_revision(svn_mergeinfo_t * younger_mergeinfo,svn_mergeinfo_t * mergeinfo,svn_revnum_t revision,apr_pool_t * pool)621 split_mergeinfo_on_revision(svn_mergeinfo_t *younger_mergeinfo,
622                             svn_mergeinfo_t *mergeinfo,
623                             svn_revnum_t revision,
624                             apr_pool_t *pool)
625 {
626   apr_hash_index_t *hi;
627   apr_pool_t *iterpool = svn_pool_create(pool);
628 
629   *younger_mergeinfo = NULL;
630   for (hi = apr_hash_first(pool, *mergeinfo); hi; hi = apr_hash_next(hi))
631     {
632       int i;
633       const char *merge_source_path = apr_hash_this_key(hi);
634       svn_rangelist_t *rangelist = apr_hash_this_val(hi);
635 
636       svn_pool_clear(iterpool);
637 
638       for (i = 0; i < rangelist->nelts; i++)
639         {
640           svn_merge_range_t *range =
641             APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
642           if (range->end <= revision)
643             {
644               /* This entirely of this range is as old or older than
645                  REVISION, so leave it in *MERGEINFO. */
646               continue;
647             }
648           else
649             {
650               /* Since the rangelists in svn_mergeinfo_t's are sorted in
651                  increasing order we know that part or all of *this* range
652                  and *all* of the remaining ranges in *RANGELIST are younger
653                  than REVISION.  Remove the younger rangelists from
654                  *MERGEINFO and put them in *YOUNGER_MERGEINFO. */
655               int j;
656               svn_rangelist_t *younger_rangelist =
657                 apr_array_make(pool, 1, sizeof(svn_merge_range_t *));
658 
659               for (j = i; j < rangelist->nelts; j++)
660                 {
661                   svn_merge_range_t *younger_range = svn_merge_range_dup(
662                     APR_ARRAY_IDX(rangelist, j, svn_merge_range_t *), pool);
663 
664                   /* REVISION might intersect with the first range where
665                      range->end > REVISION.  If that is the case then split
666                      the current range into two, putting the younger half
667                      into *YOUNGER_MERGEINFO and leaving the older half in
668                      *MERGEINFO. */
669                   if (j == i && range->start + 1 <= revision)
670                     younger_range->start = range->end = revision;
671 
672                   APR_ARRAY_PUSH(younger_rangelist, svn_merge_range_t *) =
673                     younger_range;
674                 }
675 
676               /* So far we've only been manipulating rangelists, now we
677                  actually create *YOUNGER_MERGEINFO and then remove the older
678                  ranges from *MERGEINFO */
679               if (!(*younger_mergeinfo))
680                 *younger_mergeinfo = apr_hash_make(pool);
681               svn_hash_sets(*younger_mergeinfo, merge_source_path,
682                             younger_rangelist);
683               SVN_ERR(svn_mergeinfo_remove2(mergeinfo, *younger_mergeinfo,
684                                             *mergeinfo, TRUE, pool, iterpool));
685               break; /* ...out of for (i = 0; i < rangelist->nelts; i++) */
686             }
687         }
688     }
689 
690   svn_pool_destroy(iterpool);
691 
692   return SVN_NO_ERROR;
693 }
694 
695 
696 /* Make a copy of PROPCHANGES (array of svn_prop_t) into *TRIMMED_PROPCHANGES,
697    omitting any svn:mergeinfo changes.  */
698 static svn_error_t *
omit_mergeinfo_changes(apr_array_header_t ** trimmed_propchanges,const apr_array_header_t * propchanges,apr_pool_t * result_pool)699 omit_mergeinfo_changes(apr_array_header_t **trimmed_propchanges,
700                        const apr_array_header_t *propchanges,
701                        apr_pool_t *result_pool)
702 {
703   int i;
704 
705   *trimmed_propchanges = apr_array_make(result_pool,
706                                         propchanges->nelts,
707                                         sizeof(svn_prop_t));
708 
709   for (i = 0; i < propchanges->nelts; ++i)
710     {
711       const svn_prop_t *change = &APR_ARRAY_IDX(propchanges, i, svn_prop_t);
712 
713       /* If this property is not svn:mergeinfo, then copy it.  */
714       if (strcmp(change->name, SVN_PROP_MERGEINFO) != 0)
715         APR_ARRAY_PUSH(*trimmed_propchanges, svn_prop_t) = *change;
716     }
717 
718   return SVN_NO_ERROR;
719 }
720 
721 
722 /* Helper for merge_props_changed().
723 
724    *PROPS is an array of svn_prop_t structures representing regular properties
725    to be added to the working copy TARGET_ABSPATH.
726 
727    The merge source and target are assumed to be in the same repository.
728 
729    Filter out mergeinfo property additions to TARGET_ABSPATH when
730    those additions refer to the same line of history as TARGET_ABSPATH as
731    described below.
732 
733    Examine the added mergeinfo, looking at each range (or single rev)
734    of each source path.  If a source_path/range refers to the same line of
735    history as TARGET_ABSPATH (pegged at its base revision), then filter out
736    that range.  If the entire rangelist for a given path is filtered then
737    filter out the path as well.
738 
739    RA_SESSION is an open RA session to the repository
740    in which both the source and target live, else RA_SESSION is not used. It
741    may be temporarily reparented as needed by this function.
742 
743    Use CTX for any further client operations.
744 
745    If any filtering occurs, set outgoing *PROPS to a shallow copy (allocated
746    in POOL) of incoming *PROPS minus the filtered mergeinfo. */
747 static svn_error_t *
filter_self_referential_mergeinfo(apr_array_header_t ** props,const char * target_abspath,svn_ra_session_t * ra_session,svn_client_ctx_t * ctx,apr_pool_t * pool)748 filter_self_referential_mergeinfo(apr_array_header_t **props,
749                                   const char *target_abspath,
750                                   svn_ra_session_t *ra_session,
751                                   svn_client_ctx_t *ctx,
752                                   apr_pool_t *pool)
753 {
754   apr_array_header_t *adjusted_props;
755   int i;
756   apr_pool_t *iterpool;
757   svn_boolean_t is_copy;
758   const char *repos_relpath;
759   svn_client__pathrev_t target_base;
760 
761   /* If PATH itself has been added there is no need to filter. */
762   SVN_ERR(svn_wc__node_get_origin(&is_copy,  &target_base.rev, &repos_relpath,
763                                   &target_base.repos_root_url,
764                                   &target_base.repos_uuid, NULL, NULL,
765                                   ctx->wc_ctx, target_abspath, FALSE,
766                                   pool, pool));
767 
768   if (is_copy || !repos_relpath)
769     return SVN_NO_ERROR; /* A copy or a local addition */
770 
771   target_base.url = svn_path_url_add_component2(target_base.repos_root_url,
772                                                 repos_relpath, pool);
773 
774   adjusted_props = apr_array_make(pool, (*props)->nelts, sizeof(svn_prop_t));
775   iterpool = svn_pool_create(pool);
776   for (i = 0; i < (*props)->nelts; ++i)
777     {
778       svn_prop_t *prop = &APR_ARRAY_IDX((*props), i, svn_prop_t);
779 
780       svn_mergeinfo_t mergeinfo, younger_mergeinfo;
781       svn_mergeinfo_t filtered_mergeinfo = NULL;
782       svn_mergeinfo_t filtered_younger_mergeinfo = NULL;
783       svn_error_t *err;
784 
785       /* If this property isn't mergeinfo or is NULL valued (i.e. prop removal)
786          or empty mergeinfo it does not require any special handling.  There
787          is nothing to filter out of empty mergeinfo and the concept of
788          filtering doesn't apply if we are trying to remove mergeinfo
789          entirely.  */
790       if ((strcmp(prop->name, SVN_PROP_MERGEINFO) != 0)
791           || (! prop->value)       /* Removal of mergeinfo */
792           || (! prop->value->len)) /* Empty mergeinfo */
793         {
794           APR_ARRAY_PUSH(adjusted_props, svn_prop_t) = *prop;
795           continue;
796         }
797 
798       svn_pool_clear(iterpool);
799 
800       /* Non-empty mergeinfo; filter self-referential mergeinfo out. */
801 
802       /* Parse the incoming mergeinfo to allow easier manipulation. */
803       err = svn_mergeinfo_parse(&mergeinfo, prop->value->data, iterpool);
804 
805       if (err)
806         {
807           /* Issue #3896: If we can't parse it, we certainly can't
808              filter it. */
809           if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
810             {
811               svn_error_clear(err);
812               APR_ARRAY_PUSH(adjusted_props, svn_prop_t) = *prop;
813               continue;
814             }
815           else
816             {
817               return svn_error_trace(err);
818             }
819         }
820 
821       /* The working copy target PATH is at BASE_REVISION.  Divide the
822          incoming mergeinfo into two groups.  One where all revision ranges
823          are as old or older than BASE_REVISION and one where all revision
824          ranges are younger.
825 
826          Note: You may be wondering why we do this.
827 
828          For the incoming mergeinfo "older" than target's base revision we
829          can filter out self-referential mergeinfo efficiently using
830          svn_client__get_history_as_mergeinfo().  We simply look at PATH's
831          natural history as mergeinfo and remove that from any incoming
832          mergeinfo.
833 
834          For mergeinfo "younger" than the base revision we can't use
835          svn_ra_get_location_segments() to look into PATH's future
836          history.  Instead we must use svn_client__repos_locations() and
837          look at each incoming source/range individually and see if PATH
838          at its base revision and PATH at the start of the incoming range
839          exist on the same line of history.  If they do then we can filter
840          out the incoming range.  But since we have to do this for each
841          range there is a substantial performance penalty to pay if the
842          incoming ranges are not contiguous, i.e. we call
843          svn_client__repos_locations for each discrete range and incur
844          the cost of a roundtrip communication with the repository. */
845       SVN_ERR(split_mergeinfo_on_revision(&younger_mergeinfo,
846                                           &mergeinfo,
847                                           target_base.rev,
848                                           iterpool));
849 
850       /* Filter self-referential mergeinfo from younger_mergeinfo. */
851       if (younger_mergeinfo)
852         {
853           apr_hash_index_t *hi;
854           const char *merge_source_root_url;
855 
856           SVN_ERR(svn_ra_get_repos_root2(ra_session,
857                                          &merge_source_root_url, iterpool));
858 
859           for (hi = apr_hash_first(iterpool, younger_mergeinfo);
860                hi; hi = apr_hash_next(hi))
861             {
862               int j;
863               const char *source_path = apr_hash_this_key(hi);
864               svn_rangelist_t *rangelist = apr_hash_this_val(hi);
865               const char *merge_source_url;
866               svn_rangelist_t *adjusted_rangelist =
867                 apr_array_make(iterpool, 0, sizeof(svn_merge_range_t *));
868 
869               merge_source_url =
870                     svn_path_url_add_component2(merge_source_root_url,
871                                                 source_path + 1, iterpool);
872 
873               for (j = 0; j < rangelist->nelts; j++)
874                 {
875                   svn_error_t *err2;
876                   svn_client__pathrev_t *start_loc;
877                   svn_merge_range_t *range =
878                     APR_ARRAY_IDX(rangelist, j, svn_merge_range_t *);
879 
880                   /* Because the merge source normalization code
881                      ensures mergeinfo refers to real locations on
882                      the same line of history, there's no need to
883                      look at the whole range, just the start. */
884 
885                   /* Check if PATH@BASE_REVISION exists at
886                      RANGE->START on the same line of history.
887                      (start+1 because RANGE->start is not inclusive.) */
888                   err2 = svn_client__repos_location(&start_loc, ra_session,
889                                                     &target_base,
890                                                     range->start + 1,
891                                                     ctx, iterpool, iterpool);
892                   if (err2)
893                     {
894                       if (err2->apr_err == SVN_ERR_CLIENT_UNRELATED_RESOURCES
895                           || err2->apr_err == SVN_ERR_FS_NOT_FOUND
896                           || err2->apr_err == SVN_ERR_FS_NO_SUCH_REVISION)
897                         {
898                           /* PATH@BASE_REVISION didn't exist at
899                              RANGE->START + 1 or is unrelated to the
900                              resource PATH@RANGE->START.  Some of the
901                              requested revisions may not even exist in
902                              the repository; a real possibility since
903                              mergeinfo is hand editable.  In all of these
904                              cases clear and ignore the error and don't
905                              do any filtering.
906 
907                              Note: In this last case it is possible that
908                              we will allow self-referential mergeinfo to
909                              be applied, but fixing it here is potentially
910                              very costly in terms of finding what part of
911                              a range is actually valid.  Simply allowing
912                              the merge to proceed without filtering the
913                              offending range seems the least worst
914                              option. */
915                           svn_error_clear(err2);
916                           err2 = NULL;
917                           APR_ARRAY_PUSH(adjusted_rangelist,
918                                          svn_merge_range_t *) = range;
919                         }
920                       else
921                         {
922                           return svn_error_trace(err2);
923                         }
924                      }
925                   else
926                     {
927                       /* PATH@BASE_REVISION exists on the same
928                          line of history at RANGE->START and RANGE->END.
929                          Now check that PATH@BASE_REVISION's path
930                          names at RANGE->START and RANGE->END are the same.
931                          If the names are not the same then the mergeinfo
932                          describing PATH@RANGE->START through
933                          PATH@RANGE->END actually belong to some other
934                          line of history and we want to record this
935                          mergeinfo, not filter it. */
936                       if (strcmp(start_loc->url, merge_source_url) != 0)
937                         {
938                           APR_ARRAY_PUSH(adjusted_rangelist,
939                                          svn_merge_range_t *) = range;
940                         }
941                     }
942                     /* else no need to add, this mergeinfo is
943                        all on the same line of history. */
944                 } /* for (j = 0; j < rangelist->nelts; j++) */
945 
946               /* Add any rangelists for source_path that are not
947                  self-referential. */
948               if (adjusted_rangelist->nelts)
949                 {
950                   if (!filtered_younger_mergeinfo)
951                     filtered_younger_mergeinfo = apr_hash_make(iterpool);
952                   svn_hash_sets(filtered_younger_mergeinfo, source_path,
953                                 adjusted_rangelist);
954                 }
955 
956             } /* Iteration over each merge source in younger_mergeinfo. */
957         } /* if (younger_mergeinfo) */
958 
959       /* Filter self-referential mergeinfo from "older" mergeinfo. */
960       if (mergeinfo)
961         {
962           svn_mergeinfo_t implicit_mergeinfo;
963 
964           SVN_ERR(svn_client__get_history_as_mergeinfo(
965             &implicit_mergeinfo, NULL,
966             &target_base, target_base.rev, SVN_INVALID_REVNUM,
967             ra_session, ctx, iterpool));
968 
969           /* Remove PATH's implicit mergeinfo from the incoming mergeinfo. */
970           SVN_ERR(svn_mergeinfo_remove2(&filtered_mergeinfo,
971                                         implicit_mergeinfo,
972                                         mergeinfo, TRUE, iterpool, iterpool));
973         }
974 
975       /* Combine whatever older and younger filtered mergeinfo exists
976          into filtered_mergeinfo. */
977       if (filtered_mergeinfo && filtered_younger_mergeinfo)
978         SVN_ERR(svn_mergeinfo_merge2(filtered_mergeinfo,
979                                      filtered_younger_mergeinfo, iterpool,
980                                      iterpool));
981       else if (filtered_younger_mergeinfo)
982         filtered_mergeinfo = filtered_younger_mergeinfo;
983 
984       /* If there is any incoming mergeinfo remaining after filtering
985          then put it in adjusted_props. */
986       if (filtered_mergeinfo && apr_hash_count(filtered_mergeinfo))
987         {
988           /* Convert filtered_mergeinfo to a svn_prop_t and put it
989              back in the array. */
990           svn_string_t *filtered_mergeinfo_str;
991           svn_prop_t *adjusted_prop = apr_pcalloc(pool,
992                                                   sizeof(*adjusted_prop));
993           SVN_ERR(svn_mergeinfo_to_string(&filtered_mergeinfo_str,
994                                           filtered_mergeinfo,
995                                           pool));
996           adjusted_prop->name = SVN_PROP_MERGEINFO;
997           adjusted_prop->value = filtered_mergeinfo_str;
998           APR_ARRAY_PUSH(adjusted_props, svn_prop_t) = *adjusted_prop;
999         }
1000     }
1001   svn_pool_destroy(iterpool);
1002 
1003   *props = adjusted_props;
1004   return SVN_NO_ERROR;
1005 }
1006 
1007 /* Prepare a set of property changes PROPCHANGES to be used for a merge
1008    operation on LOCAL_ABSPATH.
1009 
1010    Remove all non-regular prop-changes (entry-props and WC-props).
1011    Remove all non-mergeinfo prop-changes if it's a record-only merge.
1012    Remove self-referential mergeinfo (### in some cases...)
1013    Remove foreign-repository mergeinfo (### in some cases...)
1014 
1015    Store the resulting property changes in *PROP_UPDATES.
1016    Store information on where mergeinfo is updated in MERGE_B.
1017 
1018    Used for both file and directory property merges. */
1019 static svn_error_t *
prepare_merge_props_changed(const apr_array_header_t ** prop_updates,const char * local_abspath,const apr_array_header_t * propchanges,merge_cmd_baton_t * merge_b,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1020 prepare_merge_props_changed(const apr_array_header_t **prop_updates,
1021                             const char *local_abspath,
1022                             const apr_array_header_t *propchanges,
1023                             merge_cmd_baton_t *merge_b,
1024                             apr_pool_t *result_pool,
1025                             apr_pool_t *scratch_pool)
1026 {
1027   apr_array_header_t *props;
1028 
1029   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1030 
1031   /* We only want to merge "regular" version properties:  by
1032      definition, 'svn merge' shouldn't touch any data within .svn/  */
1033   SVN_ERR(svn_categorize_props(propchanges, NULL, NULL, &props,
1034                                result_pool));
1035 
1036   /* If we are only applying mergeinfo changes then we need to do
1037      additional filtering of PROPS so it contains only mergeinfo changes. */
1038   if (merge_b->record_only && props->nelts)
1039     {
1040       apr_array_header_t *mergeinfo_props =
1041         apr_array_make(result_pool, 1, sizeof(svn_prop_t));
1042       int i;
1043 
1044       for (i = 0; i < props->nelts; i++)
1045         {
1046           svn_prop_t *prop = &APR_ARRAY_IDX(props, i, svn_prop_t);
1047 
1048           if (strcmp(prop->name, SVN_PROP_MERGEINFO) == 0)
1049             {
1050               APR_ARRAY_PUSH(mergeinfo_props, svn_prop_t) = *prop;
1051               break;
1052             }
1053         }
1054       props = mergeinfo_props;
1055     }
1056 
1057   if (props->nelts)
1058     {
1059       /* Issue #3383: We don't want mergeinfo from a foreign repos.
1060 
1061          If this is a merge from a foreign repository we must strip all
1062          incoming mergeinfo (including mergeinfo deletions). */
1063       if (! merge_b->same_repos)
1064         SVN_ERR(omit_mergeinfo_changes(&props, props, result_pool));
1065 
1066       /* If this is a forward merge then don't add new mergeinfo to
1067          PATH that is already part of PATH's own history, see
1068          http://svn.haxx.se/dev/archive-2008-09/0006.shtml.  If the
1069          merge sources are not ancestral then there is no concept of a
1070          'forward' or 'reverse' merge and we filter unconditionally. */
1071       if (merge_b->merge_source.loc1->rev < merge_b->merge_source.loc2->rev
1072           || !merge_b->merge_source.ancestral)
1073         {
1074           if (HONOR_MERGEINFO(merge_b) || merge_b->reintegrate_merge)
1075             SVN_ERR(filter_self_referential_mergeinfo(&props,
1076                                                       local_abspath,
1077                                                       merge_b->ra_session2,
1078                                                       merge_b->ctx,
1079                                                       result_pool));
1080         }
1081     }
1082   *prop_updates = props;
1083 
1084   /* Make a record in BATON if we find a PATH where mergeinfo is added
1085      where none existed previously or PATH is having its existing
1086      mergeinfo deleted. */
1087   if (props->nelts)
1088     {
1089       int i;
1090 
1091       for (i = 0; i < props->nelts; ++i)
1092         {
1093           svn_prop_t *prop = &APR_ARRAY_IDX(props, i, svn_prop_t);
1094 
1095           if (strcmp(prop->name, SVN_PROP_MERGEINFO) == 0)
1096             {
1097               /* Does LOCAL_ABSPATH have any pristine mergeinfo? */
1098               svn_boolean_t has_pristine_mergeinfo = FALSE;
1099               apr_hash_t *pristine_props;
1100 
1101               SVN_ERR(svn_wc_get_pristine_props(&pristine_props,
1102                                                 merge_b->ctx->wc_ctx,
1103                                                 local_abspath,
1104                                                 scratch_pool,
1105                                                 scratch_pool));
1106 
1107               if (pristine_props
1108                   && svn_hash_gets(pristine_props, SVN_PROP_MERGEINFO))
1109                 has_pristine_mergeinfo = TRUE;
1110 
1111               if (!has_pristine_mergeinfo && prop->value)
1112                 {
1113                   alloc_and_store_path(&merge_b->paths_with_new_mergeinfo,
1114                                        local_abspath, merge_b->pool);
1115                 }
1116               else if (has_pristine_mergeinfo && !prop->value)
1117                 {
1118                   alloc_and_store_path(&merge_b->paths_with_deleted_mergeinfo,
1119                                        local_abspath, merge_b->pool);
1120                 }
1121             }
1122         }
1123     }
1124 
1125   return SVN_NO_ERROR;
1126 }
1127 
1128 #define CONFLICT_REASON_NONE       ((svn_wc_conflict_reason_t)-1)
1129 #define CONFLICT_REASON_SKIP       ((svn_wc_conflict_reason_t)-2)
1130 #define CONFLICT_REASON_SKIP_WC    ((svn_wc_conflict_reason_t)-3)
1131 
1132 /* Baton used for testing trees for being editted while performing tree
1133    conflict detection for incoming deletes */
1134 struct dir_delete_baton_t
1135 {
1136   /* Reference to dir baton of directory that is the root of the deletion */
1137   struct merge_dir_baton_t *del_root;
1138 
1139   /* Boolean indicating that some edit is found. Allows avoiding more work */
1140   svn_boolean_t found_edit;
1141 
1142   /* A list of paths that are compared. Kept up to date until FOUND_EDIT is
1143      set to TRUE */
1144   apr_hash_t *compared_abspaths;
1145 };
1146 
1147 /* Baton for the merge_dir_*() functions. Initialized in merge_dir_opened() */
1148 struct merge_dir_baton_t
1149 {
1150   /* Reference to the parent baton, unless the parent is the anchor, in which
1151      case PARENT_BATON is NULL */
1152   struct merge_dir_baton_t *parent_baton;
1153 
1154   /* The pool containing this baton. Use for RESULT_POOL for storing in this
1155      baton */
1156   apr_pool_t *pool;
1157 
1158   /* This directory doesn't have a representation in the working copy, so any
1159      operation on it will be skipped and possibly cause a tree conflict on the
1160      shadow root */
1161   svn_boolean_t shadowed;
1162 
1163   /* This node or one of its descendants received operational changes from the
1164      merge. If this node is the shadow root its tree conflict status has been
1165      applied */
1166   svn_boolean_t edited;
1167 
1168   /* If a tree conflict will be installed once edited, it's reason. If a skip
1169      should be produced its reason. Otherwise CONFLICT_REASON_NONE for no tree
1170      conflict.
1171 
1172      Special values:
1173        CONFLICT_REASON_SKIP:
1174             The node will be skipped with content and property state as stored in
1175             SKIP_REASON.
1176 
1177        CONFLICT_REASON_SKIP_WC:
1178             The node will be skipped as an obstructing working copy.
1179    */
1180   svn_wc_conflict_reason_t tree_conflict_reason;
1181   svn_wc_conflict_action_t tree_conflict_action;
1182   svn_node_kind_t tree_conflict_local_node_kind;
1183   svn_node_kind_t tree_conflict_merge_left_node_kind;
1184   svn_node_kind_t tree_conflict_merge_right_node_kind;
1185 
1186   /* When TREE_CONFLICT_REASON is CONFLICT_REASON_SKIP, the skip state to
1187      add to the notification */
1188   svn_wc_notify_state_t skip_reason;
1189 
1190   /* TRUE if the node was added by this merge. Otherwise FALSE */
1191   svn_boolean_t added;
1192   svn_boolean_t add_is_replace; /* Add is second part of replace */
1193 
1194   /* TRUE if we are taking over an existing directory as addition, otherwise
1195      FALSE. */
1196   svn_boolean_t add_existing;
1197 
1198   /* NULL, or an hashtable mapping const char * local_abspaths to
1199      const char *kind mapping, containing deleted nodes that still need a delete
1200      notification (which may be a replaced notification if the node is not just
1201      deleted) */
1202   apr_hash_t *pending_deletes;
1203 
1204   /* NULL, or an hashtable mapping const char * LOCAL_ABSPATHs to
1205      a const svn_wc_conflict_description2_t * instance, describing the just
1206      installed conflict */
1207   apr_hash_t *new_tree_conflicts;
1208 
1209   /* If not NULL, a reference to the information of the delete test that is
1210      currently in progress. Allocated in the root-directory baton, referenced
1211      from all descendants */
1212   struct dir_delete_baton_t *delete_state;
1213 };
1214 
1215 /* Baton for the merge_dir_*() functions. Initialized in merge_file_opened() */
1216 struct merge_file_baton_t
1217 {
1218   /* Reference to the parent baton, unless the parent is the anchor, in which
1219      case PARENT_BATON is NULL */
1220   struct merge_dir_baton_t *parent_baton;
1221 
1222   /* This file doesn't have a representation in the working copy, so any
1223      operation on it will be skipped and possibly cause a tree conflict
1224      on the shadow root */
1225   svn_boolean_t shadowed;
1226 
1227   /* This node received operational changes from the merge. If this node
1228      is the shadow root its tree conflict status has been applied */
1229   svn_boolean_t edited;
1230 
1231   /* If a tree conflict will be installed once edited, it's reason. If a skip
1232      should be produced its reason. Some special values are defined. See the
1233      merge_dir_baton_t for an explanation. */
1234   svn_wc_conflict_reason_t tree_conflict_reason;
1235   svn_wc_conflict_action_t tree_conflict_action;
1236   svn_node_kind_t tree_conflict_local_node_kind;
1237   svn_node_kind_t tree_conflict_merge_left_node_kind;
1238   svn_node_kind_t tree_conflict_merge_right_node_kind;
1239 
1240   /* When TREE_CONFLICT_REASON is CONFLICT_REASON_SKIP, the skip state to
1241      add to the notification */
1242   svn_wc_notify_state_t skip_reason;
1243 
1244   /* TRUE if the node was added by this merge. Otherwise FALSE */
1245   svn_boolean_t added;
1246   svn_boolean_t add_is_replace; /* Add is second part of replace */
1247 };
1248 
1249 /* Record the skip for future processing and (later) produce the
1250    skip notification */
1251 static svn_error_t *
record_skip(merge_cmd_baton_t * merge_b,const char * local_abspath,svn_node_kind_t kind,svn_wc_notify_action_t action,svn_wc_notify_state_t state,struct merge_dir_baton_t * pdb,apr_pool_t * scratch_pool)1252 record_skip(merge_cmd_baton_t *merge_b,
1253             const char *local_abspath,
1254             svn_node_kind_t kind,
1255             svn_wc_notify_action_t action,
1256             svn_wc_notify_state_t state,
1257             struct merge_dir_baton_t *pdb,
1258             apr_pool_t *scratch_pool)
1259 {
1260   if (merge_b->record_only)
1261     return SVN_NO_ERROR; /* ### Why? - Legacy compatibility */
1262 
1263   if ((merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
1264       && !(pdb && pdb->shadowed))
1265     {
1266       store_path(merge_b->skipped_abspaths, local_abspath);
1267     }
1268 
1269   if (merge_b->notify_func)
1270     {
1271       svn_wc_notify_t *notify;
1272 
1273       notify = svn_wc_create_notify(local_abspath, action, scratch_pool);
1274       notify->kind = kind;
1275       notify->content_state = notify->prop_state = state;
1276 
1277       merge_b->notify_func(merge_b->notify_baton, notify,
1278                            scratch_pool);
1279     }
1280   return SVN_NO_ERROR;
1281 }
1282 
1283 /* Forward declaration */
1284 static svn_client__merge_path_t *
1285 find_nearest_ancestor_with_intersecting_ranges(
1286   svn_revnum_t *start,
1287   svn_revnum_t *end,
1288   const apr_array_header_t *children_with_mergeinfo,
1289   svn_boolean_t path_is_own_ancestor,
1290   const char *local_abspath);
1291 
1292 /* Record a tree conflict in the WC, unless this is a dry run or a record-
1293  * only merge, or if a tree conflict is already flagged for the VICTIM_PATH.
1294  * (The latter can happen if a merge-tracking-aware merge is doing multiple
1295  * editor drives because of a gap in the range of eligible revisions.)
1296  *
1297  * The tree conflict, with its victim specified by VICTIM_PATH, is
1298  * assumed to have happened during a merge using merge baton MERGE_B.
1299  *
1300  * ACTION and REASON correspond to the fields
1301  * of the same names in svn_wc_tree_conflict_description_t.
1302  */
1303 static svn_error_t *
record_tree_conflict(merge_cmd_baton_t * merge_b,const char * local_abspath,struct merge_dir_baton_t * parent_baton,svn_node_kind_t local_node_kind,svn_node_kind_t merge_left_node_kind,svn_node_kind_t merge_right_node_kind,svn_wc_conflict_action_t action,svn_wc_conflict_reason_t reason,const svn_wc_conflict_description2_t * existing_conflict,svn_boolean_t notify_tc,apr_pool_t * scratch_pool)1304 record_tree_conflict(merge_cmd_baton_t *merge_b,
1305                      const char *local_abspath,
1306                      struct merge_dir_baton_t *parent_baton,
1307                      svn_node_kind_t local_node_kind,
1308                      svn_node_kind_t merge_left_node_kind,
1309                      svn_node_kind_t merge_right_node_kind,
1310                      svn_wc_conflict_action_t action,
1311                      svn_wc_conflict_reason_t reason,
1312                      const svn_wc_conflict_description2_t *existing_conflict,
1313                      svn_boolean_t notify_tc,
1314                      apr_pool_t *scratch_pool)
1315 {
1316   svn_wc_context_t *wc_ctx = merge_b->ctx->wc_ctx;
1317 
1318   if (merge_b->record_only)
1319     return SVN_NO_ERROR;
1320 
1321   if (merge_b->merge_source.ancestral
1322       || merge_b->reintegrate_merge)
1323     {
1324       store_path(merge_b->tree_conflicted_abspaths, local_abspath);
1325     }
1326 
1327   alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
1328                        merge_b->pool);
1329 
1330   if (!merge_b->dry_run)
1331     {
1332        svn_wc_conflict_description2_t *conflict;
1333        const svn_wc_conflict_version_t *left;
1334        const svn_wc_conflict_version_t *right;
1335        apr_pool_t *result_pool = parent_baton ? parent_baton->pool
1336                                               : scratch_pool;
1337 
1338       if (reason == svn_wc_conflict_reason_deleted)
1339         {
1340           const char *moved_to_abspath;
1341 
1342           SVN_ERR(svn_wc__node_was_moved_away(&moved_to_abspath, NULL,
1343                                               wc_ctx, local_abspath,
1344                                               scratch_pool, scratch_pool));
1345 
1346           if (moved_to_abspath)
1347             {
1348               /* Local abspath itself has been moved away. If only a
1349                  descendant is moved away, we call the node itself deleted */
1350               reason = svn_wc_conflict_reason_moved_away;
1351             }
1352         }
1353       else if (reason == svn_wc_conflict_reason_added)
1354         {
1355           const char *moved_from_abspath;
1356           SVN_ERR(svn_wc__node_was_moved_here(&moved_from_abspath, NULL,
1357                                               wc_ctx, local_abspath,
1358                                               scratch_pool, scratch_pool));
1359           if (moved_from_abspath)
1360             reason = svn_wc_conflict_reason_moved_here;
1361         }
1362 
1363       if (HONOR_MERGEINFO(merge_b) && merge_b->merge_source.ancestral)
1364         {
1365           struct merge_source_t *source;
1366           svn_client__pathrev_t *loc1;
1367           svn_client__pathrev_t *loc2;
1368           svn_merge_range_t range =
1369             {SVN_INVALID_REVNUM, SVN_INVALID_REVNUM, TRUE};
1370 
1371           /* We are honoring mergeinfo so do not blindly record
1372            * a conflict describing the merge of
1373            * SOURCE->LOC1->URL@SOURCE->LOC1->REV through
1374            * SOURCE->LOC2->URL@SOURCE->LOC2->REV
1375            * but figure out the actual revision range merged. */
1376           (void)find_nearest_ancestor_with_intersecting_ranges(
1377             &(range.start), &(range.end),
1378             merge_b->children_with_mergeinfo,
1379             action != svn_wc_conflict_action_delete,
1380             local_abspath);
1381           loc1 = svn_client__pathrev_dup(merge_b->merge_source.loc1,
1382                                          scratch_pool);
1383           loc2 = svn_client__pathrev_dup(merge_b->merge_source.loc2,
1384                                          scratch_pool);
1385           loc1->rev = range.start;
1386           loc2->rev = range.end;
1387           source = merge_source_create(loc1, loc2,
1388                                        merge_b->merge_source.ancestral,
1389                                        scratch_pool);
1390           SVN_ERR(make_conflict_versions(&left, &right, local_abspath,
1391                                          merge_left_node_kind,
1392                                          merge_right_node_kind,
1393                                          source, merge_b->target,
1394                                          result_pool, scratch_pool));
1395         }
1396       else
1397         SVN_ERR(make_conflict_versions(&left, &right, local_abspath,
1398                                        merge_left_node_kind,
1399                                        merge_right_node_kind,
1400                                        &merge_b->merge_source, merge_b->target,
1401                                        result_pool, scratch_pool));
1402 
1403       /* Fix up delete of file, add of dir replacement (or other way around) */
1404       if (existing_conflict != NULL && existing_conflict->src_left_version)
1405           left = existing_conflict->src_left_version;
1406 
1407       conflict = svn_wc_conflict_description_create_tree2(
1408                         local_abspath, local_node_kind,
1409                         svn_wc_operation_merge,
1410                         left, right, result_pool);
1411 
1412       conflict->action = action;
1413       conflict->reason = reason;
1414 
1415       /* May return SVN_ERR_WC_PATH_UNEXPECTED_STATUS */
1416       if (existing_conflict)
1417         SVN_ERR(svn_wc__del_tree_conflict(wc_ctx, local_abspath,
1418                                           scratch_pool));
1419 
1420       SVN_ERR(svn_wc__add_tree_conflict(merge_b->ctx->wc_ctx, conflict,
1421                                         scratch_pool));
1422 
1423       if (parent_baton)
1424         {
1425           if (! parent_baton->new_tree_conflicts)
1426             parent_baton->new_tree_conflicts = apr_hash_make(result_pool);
1427 
1428           svn_hash_sets(parent_baton->new_tree_conflicts,
1429                         apr_pstrdup(result_pool, local_abspath),
1430                         conflict);
1431         }
1432 
1433       /* ### TODO: Store in parent baton */
1434     }
1435 
1436   /* On a replacement we currently get two tree conflicts */
1437   if (merge_b->notify_func && notify_tc)
1438     {
1439       svn_wc_notify_t *notify;
1440 
1441       notify = svn_wc_create_notify(local_abspath, svn_wc_notify_tree_conflict,
1442                                     scratch_pool);
1443       notify->kind = local_node_kind;
1444 
1445       merge_b->notify_func(merge_b->notify_baton, notify,
1446                            scratch_pool);
1447     }
1448 
1449   return SVN_NO_ERROR;
1450 }
1451 
1452 /* Record the add for future processing and produce the
1453    update_add notification
1454  */
1455 static svn_error_t *
record_update_add(merge_cmd_baton_t * merge_b,const char * local_abspath,svn_node_kind_t kind,svn_boolean_t notify_replaced,apr_pool_t * scratch_pool)1456 record_update_add(merge_cmd_baton_t *merge_b,
1457                   const char *local_abspath,
1458                   svn_node_kind_t kind,
1459                   svn_boolean_t notify_replaced,
1460                   apr_pool_t *scratch_pool)
1461 {
1462   if (merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
1463     {
1464       store_path(merge_b->merged_abspaths, local_abspath);
1465     }
1466 
1467   if (merge_b->notify_func)
1468     {
1469       svn_wc_notify_t *notify;
1470       svn_wc_notify_action_t action = svn_wc_notify_update_add;
1471 
1472       if (notify_replaced)
1473         action = svn_wc_notify_update_replace;
1474 
1475       notify = svn_wc_create_notify(local_abspath, action, scratch_pool);
1476       notify->kind = kind;
1477 
1478       merge_b->notify_func(merge_b->notify_baton, notify,
1479                            scratch_pool);
1480     }
1481 
1482   return SVN_NO_ERROR;
1483 }
1484 
1485 /* Record the update for future processing and produce the
1486    update_update notification */
1487 static svn_error_t *
record_update_update(merge_cmd_baton_t * merge_b,const char * local_abspath,svn_node_kind_t kind,svn_wc_notify_state_t content_state,svn_wc_notify_state_t prop_state,apr_pool_t * scratch_pool)1488 record_update_update(merge_cmd_baton_t *merge_b,
1489                      const char *local_abspath,
1490                      svn_node_kind_t kind,
1491                      svn_wc_notify_state_t content_state,
1492                      svn_wc_notify_state_t prop_state,
1493                      apr_pool_t *scratch_pool)
1494 {
1495   if (merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
1496     {
1497       store_path(merge_b->merged_abspaths, local_abspath);
1498     }
1499 
1500   if (merge_b->notify_func)
1501     {
1502       svn_wc_notify_t *notify;
1503 
1504       notify = svn_wc_create_notify(local_abspath, svn_wc_notify_update_update,
1505                                     scratch_pool);
1506       notify->kind = kind;
1507       notify->content_state = content_state;
1508       notify->prop_state = prop_state;
1509 
1510       merge_b->notify_func(merge_b->notify_baton, notify,
1511                            scratch_pool);
1512     }
1513 
1514   return SVN_NO_ERROR;
1515 }
1516 
1517 /* Record the delete for future processing and for (later) producing the
1518    update_delete notification */
1519 static svn_error_t *
record_update_delete(merge_cmd_baton_t * merge_b,struct merge_dir_baton_t * parent_db,const char * local_abspath,svn_node_kind_t kind,apr_pool_t * scratch_pool)1520 record_update_delete(merge_cmd_baton_t *merge_b,
1521                      struct merge_dir_baton_t *parent_db,
1522                      const char *local_abspath,
1523                      svn_node_kind_t kind,
1524                      apr_pool_t *scratch_pool)
1525 {
1526   /* Update the lists of merged, skipped, tree-conflicted and added paths. */
1527   if (merge_b->merge_source.ancestral
1528       || merge_b->reintegrate_merge)
1529     {
1530       /* Issue #4166: If a previous merge added NOTIFY_ABSPATH, but we
1531          are now deleting it, then remove it from the list of added
1532          paths. */
1533       svn_hash_sets(merge_b->added_abspaths, local_abspath, NULL);
1534       store_path(merge_b->merged_abspaths, local_abspath);
1535     }
1536 
1537   if (parent_db)
1538     {
1539       const char *dup_abspath = apr_pstrdup(parent_db->pool, local_abspath);
1540 
1541       if (!parent_db->pending_deletes)
1542         parent_db->pending_deletes = apr_hash_make(parent_db->pool);
1543 
1544       svn_hash_sets(parent_db->pending_deletes, dup_abspath,
1545                     svn_node_kind_to_word(kind));
1546     }
1547 
1548   /* Note in children_with_mergeinfo that all paths in this subtree are
1549    * being deleted, to avoid trying to set mergeinfo on them later. */
1550   if (merge_b->children_with_mergeinfo)
1551     {
1552       int i;
1553 
1554       for (i = 0; i < merge_b->children_with_mergeinfo->nelts; i++)
1555         {
1556           svn_client__merge_path_t *child
1557             = APR_ARRAY_IDX(merge_b->children_with_mergeinfo, i,
1558                             svn_client__merge_path_t *);
1559 
1560           if (svn_dirent_is_ancestor(local_abspath, child->abspath))
1561             {
1562               SVN_ERR(svn_sort__array_delete2(merge_b->children_with_mergeinfo, i--, 1));
1563             }
1564         }
1565     }
1566 
1567   return SVN_NO_ERROR;
1568 }
1569 
1570 /* Notify the pending 'D'eletes, that were waiting to see if a matching 'A'dd
1571    might make them a 'R'eplace. */
1572 static svn_error_t *
handle_pending_notifications(merge_cmd_baton_t * merge_b,struct merge_dir_baton_t * db,apr_pool_t * scratch_pool)1573 handle_pending_notifications(merge_cmd_baton_t *merge_b,
1574                              struct merge_dir_baton_t *db,
1575                              apr_pool_t *scratch_pool)
1576 {
1577   if (merge_b->notify_func && db->pending_deletes)
1578     {
1579       apr_hash_index_t *hi;
1580 
1581       for (hi = apr_hash_first(scratch_pool, db->pending_deletes);
1582            hi;
1583            hi = apr_hash_next(hi))
1584         {
1585           const char *del_abspath = apr_hash_this_key(hi);
1586           svn_wc_notify_t *notify;
1587 
1588           notify = svn_wc_create_notify(del_abspath,
1589                                         svn_wc_notify_update_delete,
1590                                         scratch_pool);
1591           notify->kind = svn_node_kind_from_word(
1592                                     apr_hash_this_val(hi));
1593 
1594           merge_b->notify_func(merge_b->notify_baton,
1595                                notify, scratch_pool);
1596         }
1597 
1598       db->pending_deletes = NULL;
1599     }
1600   return SVN_NO_ERROR;
1601 }
1602 
1603 /* Helper function for the merge_dir_*() and merge_file_*() functions.
1604 
1605    Installs and notifies pre-recorded tree conflicts and skips for
1606    ancestors of operational merges
1607  */
1608 static svn_error_t *
mark_dir_edited(merge_cmd_baton_t * merge_b,struct merge_dir_baton_t * db,const char * local_abspath,apr_pool_t * scratch_pool)1609 mark_dir_edited(merge_cmd_baton_t *merge_b,
1610                 struct merge_dir_baton_t *db,
1611                 const char *local_abspath,
1612                 apr_pool_t *scratch_pool)
1613 {
1614   /* ### Too much common code with mark_file_edited */
1615   if (db->edited)
1616     return SVN_NO_ERROR;
1617 
1618   if (db->parent_baton && !db->parent_baton->edited)
1619     {
1620       const char *dir_abspath = svn_dirent_dirname(local_abspath,
1621                                                    scratch_pool);
1622 
1623       SVN_ERR(mark_dir_edited(merge_b, db->parent_baton, dir_abspath,
1624                               scratch_pool));
1625     }
1626 
1627   db->edited = TRUE;
1628 
1629   if (! db->shadowed)
1630     return SVN_NO_ERROR; /* Easy out */
1631 
1632   if (db->parent_baton
1633       && db->parent_baton->delete_state
1634       && db->tree_conflict_reason != CONFLICT_REASON_NONE)
1635     {
1636       db->parent_baton->delete_state->found_edit = TRUE;
1637     }
1638   else if (db->tree_conflict_reason == CONFLICT_REASON_SKIP
1639            || db->tree_conflict_reason == CONFLICT_REASON_SKIP_WC)
1640     {
1641       /* open_directory() decided not to flag a tree conflict, but
1642          for clarity we produce a skip for this node that
1643          most likely isn't touched by the merge itself */
1644 
1645       if (merge_b->notify_func)
1646         {
1647           svn_wc_notify_t *notify;
1648 
1649           notify = svn_wc_create_notify(
1650                             local_abspath,
1651                             (db->tree_conflict_reason == CONFLICT_REASON_SKIP)
1652                                 ? svn_wc_notify_skip
1653                                 : svn_wc_notify_update_skip_obstruction,
1654                             scratch_pool);
1655           notify->kind = svn_node_dir;
1656           notify->content_state = notify->prop_state = db->skip_reason;
1657 
1658           merge_b->notify_func(merge_b->notify_baton,
1659                                notify,
1660                                scratch_pool);
1661         }
1662 
1663       if (merge_b->merge_source.ancestral
1664           || merge_b->reintegrate_merge)
1665         {
1666           store_path(merge_b->skipped_abspaths, local_abspath);
1667         }
1668     }
1669   else if (db->tree_conflict_reason != CONFLICT_REASON_NONE)
1670     {
1671       /* open_directory() decided that a tree conflict should be raised */
1672 
1673       SVN_ERR(record_tree_conflict(merge_b, local_abspath, db->parent_baton,
1674                                    db->tree_conflict_local_node_kind,
1675                                    db->tree_conflict_merge_left_node_kind,
1676                                    db->tree_conflict_merge_right_node_kind,
1677                                    db->tree_conflict_action,
1678                                    db->tree_conflict_reason,
1679                                    NULL, TRUE,
1680                                    scratch_pool));
1681     }
1682 
1683   return SVN_NO_ERROR;
1684 }
1685 
1686 /* Helper function for the merge_file_*() functions.
1687 
1688    Installs and notifies pre-recorded tree conflicts and skips for
1689    ancestors of operational merges
1690  */
1691 static svn_error_t *
mark_file_edited(merge_cmd_baton_t * merge_b,struct merge_file_baton_t * fb,const char * local_abspath,apr_pool_t * scratch_pool)1692 mark_file_edited(merge_cmd_baton_t *merge_b,
1693                  struct merge_file_baton_t *fb,
1694                  const char *local_abspath,
1695                  apr_pool_t *scratch_pool)
1696 {
1697   /* ### Too much common code with mark_dir_edited */
1698   if (fb->edited)
1699     return SVN_NO_ERROR;
1700 
1701   if (fb->parent_baton && !fb->parent_baton->edited)
1702     {
1703       const char *dir_abspath = svn_dirent_dirname(local_abspath,
1704                                                    scratch_pool);
1705 
1706       SVN_ERR(mark_dir_edited(merge_b, fb->parent_baton, dir_abspath,
1707                               scratch_pool));
1708     }
1709 
1710   fb->edited = TRUE;
1711 
1712   if (! fb->shadowed)
1713     return SVN_NO_ERROR; /* Easy out */
1714 
1715   if (fb->parent_baton
1716       && fb->parent_baton->delete_state
1717       && fb->tree_conflict_reason != CONFLICT_REASON_NONE)
1718     {
1719       fb->parent_baton->delete_state->found_edit = TRUE;
1720     }
1721   else if (fb->tree_conflict_reason == CONFLICT_REASON_SKIP
1722            || fb->tree_conflict_reason == CONFLICT_REASON_SKIP_WC)
1723     {
1724       /* open_directory() decided not to flag a tree conflict, but
1725          for clarity we produce a skip for this node that
1726          most likely isn't touched by the merge itself */
1727 
1728       if (merge_b->notify_func)
1729         {
1730           svn_wc_notify_t *notify;
1731 
1732           notify = svn_wc_create_notify(local_abspath, svn_wc_notify_skip,
1733                                         scratch_pool);
1734           notify->kind = svn_node_file;
1735           notify->content_state = notify->prop_state = fb->skip_reason;
1736 
1737           merge_b->notify_func(merge_b->notify_baton,
1738                                notify,
1739                                scratch_pool);
1740         }
1741 
1742       if (merge_b->merge_source.ancestral
1743           || merge_b->reintegrate_merge)
1744         {
1745           store_path(merge_b->skipped_abspaths, local_abspath);
1746         }
1747     }
1748   else if (fb->tree_conflict_reason != CONFLICT_REASON_NONE)
1749     {
1750       /* open_file() decided that a tree conflict should be raised */
1751 
1752       SVN_ERR(record_tree_conflict(merge_b, local_abspath, fb->parent_baton,
1753                                    fb->tree_conflict_local_node_kind,
1754                                    fb->tree_conflict_merge_left_node_kind,
1755                                    fb->tree_conflict_merge_right_node_kind,
1756                                    fb->tree_conflict_action,
1757                                    fb->tree_conflict_reason,
1758                                    NULL, TRUE,
1759                                    scratch_pool));
1760     }
1761 
1762   return SVN_NO_ERROR;
1763 }
1764 
1765 /* An svn_diff_tree_processor_t function.
1766 
1767    Called before either merge_file_changed(), merge_file_added(),
1768    merge_file_deleted() or merge_file_closed(), unless it sets *SKIP to TRUE.
1769 
1770    When *SKIP is TRUE, the diff driver avoids work on getting the details
1771    for the closing callbacks.
1772  */
1773 static svn_error_t *
merge_file_opened(void ** new_file_baton,svn_boolean_t * skip,const char * relpath,const svn_diff_source_t * left_source,const svn_diff_source_t * right_source,const svn_diff_source_t * copyfrom_source,void * dir_baton,const struct svn_diff_tree_processor_t * processor,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1774 merge_file_opened(void **new_file_baton,
1775                   svn_boolean_t *skip,
1776                   const char *relpath,
1777                   const svn_diff_source_t *left_source,
1778                   const svn_diff_source_t *right_source,
1779                   const svn_diff_source_t *copyfrom_source,
1780                   void *dir_baton,
1781                   const struct svn_diff_tree_processor_t *processor,
1782                   apr_pool_t *result_pool,
1783                   apr_pool_t *scratch_pool)
1784 {
1785   merge_cmd_baton_t *merge_b = processor->baton;
1786   struct merge_dir_baton_t *pdb = dir_baton;
1787   struct merge_file_baton_t *fb;
1788   const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
1789                                               relpath, scratch_pool);
1790 
1791   fb = apr_pcalloc(result_pool, sizeof(*fb));
1792   fb->tree_conflict_reason = CONFLICT_REASON_NONE;
1793   fb->tree_conflict_action = svn_wc_conflict_action_edit;
1794   fb->skip_reason = svn_wc_notify_state_unknown;
1795 
1796   if (left_source)
1797     fb->tree_conflict_merge_left_node_kind = svn_node_file;
1798   else
1799     fb->tree_conflict_merge_left_node_kind = svn_node_none;
1800 
1801   if (right_source)
1802     fb->tree_conflict_merge_right_node_kind = svn_node_file;
1803   else
1804     fb->tree_conflict_merge_right_node_kind = svn_node_none;
1805 
1806   *new_file_baton = fb;
1807 
1808   if (pdb)
1809     {
1810       fb->parent_baton = pdb;
1811       fb->shadowed = pdb->shadowed;
1812       fb->skip_reason = pdb->skip_reason;
1813     }
1814 
1815   if (fb->shadowed)
1816     {
1817       /* An ancestor is tree conflicted. Nothing to do here. */
1818     }
1819   else if (left_source != NULL)
1820     {
1821       /* Node is expected to be a file, which will be changed or deleted. */
1822       svn_boolean_t is_deleted;
1823       svn_boolean_t excluded;
1824       svn_depth_t parent_depth;
1825 
1826       if (! right_source)
1827         fb->tree_conflict_action = svn_wc_conflict_action_delete;
1828 
1829       {
1830         svn_wc_notify_state_t obstr_state;
1831 
1832         SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, &excluded,
1833                                           &fb->tree_conflict_local_node_kind,
1834                                           &parent_depth,
1835                                           merge_b, local_abspath,
1836                                           scratch_pool));
1837 
1838         if (obstr_state != svn_wc_notify_state_inapplicable)
1839           {
1840             fb->shadowed = TRUE;
1841             fb->tree_conflict_reason = CONFLICT_REASON_SKIP;
1842             fb->skip_reason = obstr_state;
1843             return SVN_NO_ERROR;
1844           }
1845 
1846         if (is_deleted)
1847           fb->tree_conflict_local_node_kind = svn_node_none;
1848       }
1849 
1850       if (fb->tree_conflict_local_node_kind == svn_node_none)
1851         {
1852           fb->shadowed = TRUE;
1853 
1854           /* If this is not the merge target and the parent is too shallow to
1855              contain this directory, and the directory is not present
1856              via exclusion or depth filtering, skip it instead of recording
1857              a tree conflict.
1858 
1859              Non-inheritable mergeinfo will be recorded, allowing
1860              future merges into non-shallow working copies to merge
1861              changes we missed this time around. */
1862           if (pdb && (excluded
1863                       || (parent_depth != svn_depth_unknown &&
1864                           parent_depth < svn_depth_files)))
1865             {
1866                 fb->shadowed = TRUE;
1867 
1868                 fb->tree_conflict_reason = CONFLICT_REASON_SKIP;
1869                 fb->skip_reason = svn_wc_notify_state_missing;
1870                 return SVN_NO_ERROR;
1871             }
1872 
1873           if (is_deleted)
1874             fb->tree_conflict_reason = svn_wc_conflict_reason_deleted;
1875           else
1876             fb->tree_conflict_reason = svn_wc_conflict_reason_missing;
1877 
1878           /* ### Similar to directory */
1879           *skip = TRUE;
1880           SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
1881           return SVN_NO_ERROR;
1882           /* ### /Similar */
1883         }
1884       else if (fb->tree_conflict_local_node_kind != svn_node_file)
1885         {
1886           svn_boolean_t added;
1887           fb->shadowed = TRUE;
1888 
1889           SVN_ERR(svn_wc__node_is_added(&added, merge_b->ctx->wc_ctx,
1890                                         local_abspath, scratch_pool));
1891 
1892           fb->tree_conflict_reason = added ? svn_wc_conflict_reason_added
1893                                            : svn_wc_conflict_reason_obstructed;
1894 
1895           /* ### Similar to directory */
1896           *skip = TRUE;
1897           SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
1898           return SVN_NO_ERROR;
1899           /* ### /Similar */
1900         }
1901 
1902       if (! right_source)
1903         {
1904           /* We want to delete the directory */
1905           fb->tree_conflict_action = svn_wc_conflict_action_delete;
1906           SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
1907 
1908           if (fb->shadowed)
1909             {
1910               return SVN_NO_ERROR; /* Already set a tree conflict */
1911             }
1912 
1913           /* Comparison mode to verify for delete tree conflicts? */
1914           if (pdb && pdb->delete_state
1915               && pdb->delete_state->found_edit)
1916             {
1917               /* Earlier nodes found a conflict. Done. */
1918               *skip = TRUE;
1919             }
1920         }
1921     }
1922   else
1923     {
1924       const svn_wc_conflict_description2_t *old_tc = NULL;
1925 
1926       /* The node doesn't exist pre-merge: We have an addition */
1927       fb->added = TRUE;
1928       fb->tree_conflict_action = svn_wc_conflict_action_add;
1929 
1930       if (pdb && pdb->pending_deletes
1931           && svn_hash_gets(pdb->pending_deletes, local_abspath))
1932         {
1933           fb->add_is_replace = TRUE;
1934           fb->tree_conflict_action = svn_wc_conflict_action_replace;
1935 
1936           svn_hash_sets(pdb->pending_deletes, local_abspath, NULL);
1937         }
1938 
1939       if (pdb
1940           && pdb->new_tree_conflicts
1941           && (old_tc = svn_hash_gets(pdb->new_tree_conflicts, local_abspath)))
1942         {
1943           fb->tree_conflict_action = svn_wc_conflict_action_replace;
1944           fb->tree_conflict_reason = old_tc->reason;
1945 
1946           /* Update the tree conflict to store that this is a replace */
1947           SVN_ERR(record_tree_conflict(merge_b, local_abspath, pdb,
1948                                        old_tc->node_kind,
1949                                        old_tc->src_left_version->node_kind,
1950                                        svn_node_file,
1951                                        fb->tree_conflict_action,
1952                                        fb->tree_conflict_reason,
1953                                        old_tc, FALSE,
1954                                        scratch_pool));
1955 
1956           if (old_tc->reason == svn_wc_conflict_reason_deleted
1957               || old_tc->reason == svn_wc_conflict_reason_moved_away)
1958             {
1959               /* Issue #3806: Incoming replacements on local deletes produce
1960                  inconsistent result.
1961 
1962                  In this specific case we can continue applying the add part
1963                  of the replacement. */
1964             }
1965           else
1966             {
1967               *skip = TRUE;
1968 
1969               return SVN_NO_ERROR;
1970             }
1971         }
1972       else if (! (merge_b->dry_run
1973                   && ((pdb && pdb->added) || fb->add_is_replace)))
1974         {
1975           svn_wc_notify_state_t obstr_state;
1976           svn_boolean_t is_deleted;
1977 
1978           SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, NULL,
1979                                             &fb->tree_conflict_local_node_kind,
1980                                             NULL, merge_b, local_abspath,
1981                                             scratch_pool));
1982 
1983           if (obstr_state != svn_wc_notify_state_inapplicable)
1984             {
1985               /* Skip the obstruction */
1986               fb->shadowed = TRUE;
1987               fb->tree_conflict_reason = CONFLICT_REASON_SKIP;
1988               fb->skip_reason = obstr_state;
1989             }
1990           else if (fb->tree_conflict_local_node_kind != svn_node_none
1991                    && !is_deleted)
1992             {
1993               /* Set a tree conflict */
1994               svn_boolean_t added;
1995 
1996               fb->shadowed = TRUE;
1997               SVN_ERR(svn_wc__node_is_added(&added, merge_b->ctx->wc_ctx,
1998                                             local_abspath, scratch_pool));
1999 
2000               fb->tree_conflict_reason = added ? svn_wc_conflict_reason_added
2001                                                : svn_wc_conflict_reason_obstructed;
2002             }
2003         }
2004 
2005       /* Handle pending conflicts */
2006       SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
2007     }
2008 
2009   return SVN_NO_ERROR;
2010 }
2011 
2012 /* An svn_diff_tree_processor_t function.
2013  *
2014  * Called after merge_file_opened() when a node receives only text and/or
2015  * property changes between LEFT_SOURCE and RIGHT_SOURCE.
2016  *
2017  * left_file and right_file can be NULL when the file is not modified.
2018  * left_props and right_props are always available.
2019  */
2020 static svn_error_t *
merge_file_changed(const char * relpath,const svn_diff_source_t * left_source,const svn_diff_source_t * right_source,const char * left_file,const char * right_file,apr_hash_t * left_props,apr_hash_t * right_props,svn_boolean_t file_modified,const apr_array_header_t * prop_changes,void * file_baton,const struct svn_diff_tree_processor_t * processor,apr_pool_t * scratch_pool)2021 merge_file_changed(const char *relpath,
2022                   const svn_diff_source_t *left_source,
2023                   const svn_diff_source_t *right_source,
2024                   const char *left_file,
2025                   const char *right_file,
2026                   /*const*/ apr_hash_t *left_props,
2027                   /*const*/ apr_hash_t *right_props,
2028                   svn_boolean_t file_modified,
2029                   const apr_array_header_t *prop_changes,
2030                   void *file_baton,
2031                   const struct svn_diff_tree_processor_t *processor,
2032                   apr_pool_t *scratch_pool)
2033 {
2034   merge_cmd_baton_t *merge_b = processor->baton;
2035   struct merge_file_baton_t *fb = file_baton;
2036   svn_client_ctx_t *ctx = merge_b->ctx;
2037   const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
2038                                               relpath, scratch_pool);
2039   const svn_wc_conflict_version_t *left;
2040   const svn_wc_conflict_version_t *right;
2041   svn_wc_notify_state_t text_state;
2042   svn_wc_notify_state_t property_state;
2043 
2044   SVN_ERR_ASSERT(local_abspath && svn_dirent_is_absolute(local_abspath));
2045   SVN_ERR_ASSERT(!left_file || svn_dirent_is_absolute(left_file));
2046   SVN_ERR_ASSERT(!right_file || svn_dirent_is_absolute(right_file));
2047 
2048   SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
2049 
2050   if (fb->shadowed)
2051     {
2052       if (fb->tree_conflict_reason == CONFLICT_REASON_NONE)
2053         {
2054           /* We haven't notified for this node yet: report a skip */
2055           SVN_ERR(record_skip(merge_b, local_abspath, svn_node_file,
2056                               svn_wc_notify_update_shadowed_update,
2057                               fb->skip_reason, fb->parent_baton,
2058                               scratch_pool));
2059         }
2060 
2061       return SVN_NO_ERROR;
2062     }
2063 
2064   /* This callback is essentially no more than a wrapper around
2065      svn_wc_merge5().  Thank goodness that all the
2066      diff-editor-mechanisms are doing the hard work of getting the
2067      fulltexts! */
2068 
2069   property_state = svn_wc_notify_state_unchanged;
2070   text_state = svn_wc_notify_state_unchanged;
2071 
2072   SVN_ERR(prepare_merge_props_changed(&prop_changes, local_abspath,
2073                                       prop_changes, merge_b,
2074                                       scratch_pool, scratch_pool));
2075 
2076   SVN_ERR(make_conflict_versions(&left, &right, local_abspath,
2077                                  svn_node_file, svn_node_file,
2078                                  &merge_b->merge_source, merge_b->target,
2079                                  scratch_pool, scratch_pool));
2080 
2081   /* Do property merge now, if we are not going to perform a text merge */
2082   if ((merge_b->record_only || !left_file) && prop_changes->nelts)
2083     {
2084       SVN_ERR(svn_wc_merge_props3(&property_state, ctx->wc_ctx, local_abspath,
2085                                   left, right,
2086                                   left_props, prop_changes,
2087                                   merge_b->dry_run,
2088                                   NULL, NULL,
2089                                   ctx->cancel_func, ctx->cancel_baton,
2090                                   scratch_pool));
2091       if (property_state == svn_wc_notify_state_conflicted)
2092         {
2093           alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
2094                                merge_b->pool);
2095         }
2096     }
2097 
2098   /* Easy out: We are only applying mergeinfo differences. */
2099   if (merge_b->record_only)
2100     {
2101       /* NO-OP */
2102     }
2103   else if (left_file)
2104     {
2105       svn_boolean_t has_local_mods;
2106       enum svn_wc_merge_outcome_t content_outcome;
2107       const char *target_label;
2108       const char *left_label;
2109       const char *right_label;
2110       const char *path_ext = "";
2111 
2112       if (merge_b->ext_patterns && merge_b->ext_patterns->nelts)
2113         {
2114           svn_path_splitext(NULL, &path_ext, local_abspath, scratch_pool);
2115           if (! (*path_ext
2116                  && svn_cstring_match_glob_list(path_ext,
2117                                                 merge_b->ext_patterns)))
2118             {
2119               path_ext = "";
2120             }
2121         }
2122 
2123       /* xgettext: the '.working', '.merge-left.r%ld' and
2124          '.merge-right.r%ld' strings are used to tag onto a file
2125          name in case of a merge conflict */
2126 
2127       target_label = apr_psprintf(scratch_pool, _(".working%s%s"),
2128                                   *path_ext ? "." : "", path_ext);
2129       left_label = apr_psprintf(scratch_pool,
2130                                 _(".merge-left.r%ld%s%s"),
2131                                 left_source->revision,
2132                                 *path_ext ? "." : "", path_ext);
2133       right_label = apr_psprintf(scratch_pool,
2134                                  _(".merge-right.r%ld%s%s"),
2135                                  right_source->revision,
2136                                  *path_ext ? "." : "", path_ext);
2137 
2138       SVN_ERR(svn_wc_text_modified_p2(&has_local_mods, ctx->wc_ctx,
2139                                       local_abspath, FALSE, scratch_pool));
2140 
2141       /* Do property merge and text merge in one step so that keyword expansion
2142          takes into account the new property values. */
2143       SVN_ERR(svn_wc_merge5(&content_outcome, &property_state, ctx->wc_ctx,
2144                             left_file, right_file, local_abspath,
2145                             left_label, right_label, target_label,
2146                             left, right,
2147                             merge_b->dry_run, merge_b->diff3_cmd,
2148                             merge_b->merge_options,
2149                             left_props, prop_changes,
2150                             NULL, NULL,
2151                             ctx->cancel_func,
2152                             ctx->cancel_baton,
2153                             scratch_pool));
2154 
2155       if (content_outcome == svn_wc_merge_conflict
2156           || property_state == svn_wc_notify_state_conflicted)
2157         {
2158           alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
2159                                merge_b->pool);
2160         }
2161 
2162       if (content_outcome == svn_wc_merge_conflict)
2163         text_state = svn_wc_notify_state_conflicted;
2164       else if (has_local_mods
2165                && content_outcome != svn_wc_merge_unchanged)
2166         text_state = svn_wc_notify_state_merged;
2167       else if (content_outcome == svn_wc_merge_merged)
2168         text_state = svn_wc_notify_state_changed;
2169       else if (content_outcome == svn_wc_merge_no_merge)
2170         text_state = svn_wc_notify_state_missing;
2171       else /* merge_outcome == svn_wc_merge_unchanged */
2172         text_state = svn_wc_notify_state_unchanged;
2173     }
2174 
2175   if (text_state == svn_wc_notify_state_conflicted
2176       || text_state == svn_wc_notify_state_merged
2177       || text_state == svn_wc_notify_state_changed
2178       || property_state == svn_wc_notify_state_conflicted
2179       || property_state == svn_wc_notify_state_merged
2180       || property_state == svn_wc_notify_state_changed)
2181     {
2182       SVN_ERR(record_update_update(merge_b, local_abspath, svn_node_file,
2183                                    text_state, property_state,
2184                                    scratch_pool));
2185     }
2186 
2187   return SVN_NO_ERROR;
2188 }
2189 
2190 /* An svn_diff_tree_processor_t function.
2191  *
2192  * Called after merge_file_opened() when a node doesn't exist in LEFT_SOURCE,
2193  * but does in RIGHT_SOURCE.
2194  *
2195  * When a node is replaced instead of just added a separate opened+deleted will
2196  * be invoked before the current open+added.
2197  */
2198 static svn_error_t *
merge_file_added(const char * relpath,const svn_diff_source_t * copyfrom_source,const svn_diff_source_t * right_source,const char * copyfrom_file,const char * right_file,apr_hash_t * copyfrom_props,apr_hash_t * right_props,void * file_baton,const struct svn_diff_tree_processor_t * processor,apr_pool_t * scratch_pool)2199 merge_file_added(const char *relpath,
2200                  const svn_diff_source_t *copyfrom_source,
2201                  const svn_diff_source_t *right_source,
2202                  const char *copyfrom_file,
2203                  const char *right_file,
2204                  /*const*/ apr_hash_t *copyfrom_props,
2205                  /*const*/ apr_hash_t *right_props,
2206                  void *file_baton,
2207                  const struct svn_diff_tree_processor_t *processor,
2208                  apr_pool_t *scratch_pool)
2209 {
2210   merge_cmd_baton_t *merge_b = processor->baton;
2211   struct merge_file_baton_t *fb = file_baton;
2212   const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
2213                                               relpath, scratch_pool);
2214   apr_hash_t *pristine_props;
2215   apr_hash_t *new_props;
2216 
2217   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2218 
2219   SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
2220 
2221   if (fb->shadowed)
2222     {
2223       if (fb->tree_conflict_reason == CONFLICT_REASON_NONE)
2224         {
2225           /* We haven't notified for this node yet: report a skip */
2226           SVN_ERR(record_skip(merge_b, local_abspath, svn_node_file,
2227                               svn_wc_notify_update_shadowed_add,
2228                               fb->skip_reason, fb->parent_baton,
2229                               scratch_pool));
2230         }
2231 
2232       return SVN_NO_ERROR;
2233     }
2234 
2235   /* Easy out: We are only applying mergeinfo differences. */
2236   if (merge_b->record_only)
2237     {
2238       return SVN_NO_ERROR;
2239     }
2240 
2241   if ((merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
2242       && ( !fb->parent_baton || !fb->parent_baton->added))
2243     {
2244       /* Store the roots of added subtrees */
2245       store_path(merge_b->added_abspaths, local_abspath);
2246     }
2247 
2248   if (!merge_b->dry_run)
2249     {
2250       const char *copyfrom_url;
2251       svn_revnum_t copyfrom_rev;
2252       svn_stream_t *new_contents, *pristine_contents;
2253 
2254       /* If this is a merge from the same repository as our
2255          working copy, we handle adds as add-with-history.
2256          Otherwise, we'll use a pure add. */
2257       if (merge_b->same_repos)
2258         {
2259           copyfrom_url = svn_path_url_add_component2(
2260                                        merge_b->merge_source.loc2->url,
2261                                        relpath, scratch_pool);
2262           copyfrom_rev = right_source->revision;
2263           SVN_ERR(check_repos_match(merge_b->target, local_abspath,
2264                                     copyfrom_url, scratch_pool));
2265           SVN_ERR(svn_stream_open_readonly(&pristine_contents,
2266                                            right_file,
2267                                            scratch_pool,
2268                                            scratch_pool));
2269           new_contents = NULL; /* inherit from new_base_contents */
2270 
2271           pristine_props = right_props; /* Includes last_* information */
2272           new_props = NULL; /* No local changes */
2273 
2274           if (svn_hash_gets(pristine_props, SVN_PROP_MERGEINFO))
2275             {
2276               alloc_and_store_path(&merge_b->paths_with_new_mergeinfo,
2277                                    local_abspath, merge_b->pool);
2278             }
2279         }
2280       else
2281         {
2282           apr_array_header_t *regular_props;
2283 
2284           copyfrom_url = NULL;
2285           copyfrom_rev = SVN_INVALID_REVNUM;
2286 
2287           pristine_contents = svn_stream_empty(scratch_pool);
2288           SVN_ERR(svn_stream_open_readonly(&new_contents, right_file,
2289                                            scratch_pool, scratch_pool));
2290 
2291           pristine_props = apr_hash_make(scratch_pool); /* Local addition */
2292 
2293           /* We don't want any foreign properties */
2294           SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(right_props,
2295                                                               scratch_pool),
2296                                        NULL, NULL, &regular_props,
2297                                        scratch_pool));
2298 
2299           new_props = svn_prop_array_to_hash(regular_props, scratch_pool);
2300 
2301           /* Issue #3383: We don't want mergeinfo from a foreign repository. */
2302           svn_hash_sets(new_props, SVN_PROP_MERGEINFO, NULL);
2303         }
2304 
2305       /* Do everything like if we had called 'svn cp PATH1 PATH2'. */
2306       SVN_ERR(svn_wc_add_repos_file4(merge_b->ctx->wc_ctx,
2307                                       local_abspath,
2308                                       pristine_contents,
2309                                       new_contents,
2310                                       pristine_props, new_props,
2311                                       copyfrom_url, copyfrom_rev,
2312                                       merge_b->ctx->cancel_func,
2313                                       merge_b->ctx->cancel_baton,
2314                                       scratch_pool));
2315 
2316       /* Caller must call svn_sleep_for_timestamps() */
2317       *merge_b->use_sleep = TRUE;
2318     }
2319 
2320   SVN_ERR(record_update_add(merge_b, local_abspath, svn_node_file,
2321                             fb->add_is_replace, scratch_pool));
2322 
2323   return SVN_NO_ERROR;
2324 }
2325 
2326 /* Compare the two sets of properties PROPS1 and PROPS2, ignoring the
2327  * "svn:mergeinfo" property, and noticing only "normal" props. Set *SAME to
2328  * true if the rest of the properties are identical or false if they differ.
2329  */
2330 static svn_error_t *
properties_same_p(svn_boolean_t * same,apr_hash_t * props1,apr_hash_t * props2,apr_pool_t * scratch_pool)2331 properties_same_p(svn_boolean_t *same,
2332                   apr_hash_t *props1,
2333                   apr_hash_t *props2,
2334                   apr_pool_t *scratch_pool)
2335 {
2336   apr_array_header_t *prop_changes;
2337   int i, diffs;
2338 
2339   /* Examine the properties that differ */
2340   SVN_ERR(svn_prop_diffs(&prop_changes, props1, props2, scratch_pool));
2341   diffs = 0;
2342   for (i = 0; i < prop_changes->nelts; i++)
2343     {
2344       const char *pname = APR_ARRAY_IDX(prop_changes, i, svn_prop_t).name;
2345 
2346       /* Count the properties we're interested in; ignore the rest */
2347       if (svn_wc_is_normal_prop(pname)
2348           && strcmp(pname, SVN_PROP_MERGEINFO) != 0)
2349         diffs++;
2350     }
2351   *same = (diffs == 0);
2352   return SVN_NO_ERROR;
2353 }
2354 
2355 /* Compare the file OLDER_ABSPATH (together with its normal properties in
2356  * ORIGINAL_PROPS which may also contain WC props and entry props) with the
2357  * versioned file MINE_ABSPATH (together with its versioned properties).
2358  * Set *SAME to true if they are the same or false if they differ, ignoring
2359  * the "svn:mergeinfo" property, and ignoring differences in keyword
2360  * expansion and end-of-line style. */
2361 static svn_error_t *
files_same_p(svn_boolean_t * same,const char * older_abspath,apr_hash_t * original_props,const char * mine_abspath,svn_wc_context_t * wc_ctx,apr_pool_t * scratch_pool)2362 files_same_p(svn_boolean_t *same,
2363              const char *older_abspath,
2364              apr_hash_t *original_props,
2365              const char *mine_abspath,
2366              svn_wc_context_t *wc_ctx,
2367              apr_pool_t *scratch_pool)
2368 {
2369   apr_hash_t *working_props;
2370 
2371   SVN_ERR(svn_wc_prop_list2(&working_props, wc_ctx, mine_abspath,
2372                             scratch_pool, scratch_pool));
2373 
2374   /* Compare the properties */
2375   SVN_ERR(properties_same_p(same, original_props, working_props,
2376                             scratch_pool));
2377   if (*same)
2378     {
2379       svn_stream_t *mine_stream;
2380       svn_stream_t *older_stream;
2381       svn_string_t *special = svn_hash_gets(working_props, SVN_PROP_SPECIAL);
2382       svn_string_t *eol_style = svn_hash_gets(working_props, SVN_PROP_EOL_STYLE);
2383       svn_string_t *keywords = svn_hash_gets(working_props, SVN_PROP_KEYWORDS);
2384 
2385       /* Compare the file content, translating 'mine' to 'normal' form. */
2386       if (special != NULL)
2387         SVN_ERR(svn_subst_read_specialfile(&mine_stream, mine_abspath,
2388                                            scratch_pool, scratch_pool));
2389       else
2390         SVN_ERR(svn_stream_open_readonly(&mine_stream, mine_abspath,
2391                                          scratch_pool, scratch_pool));
2392 
2393       if (!special && (eol_style || keywords))
2394         {
2395           apr_hash_t *kw = NULL;
2396           const char *eol = NULL;
2397           svn_subst_eol_style_t style;
2398 
2399           /* We used to use svn_client__get_normalized_stream() here, but
2400              that doesn't work in 100% of the cases because it doesn't
2401              convert EOLs to the repository form; just to '\n'.
2402            */
2403 
2404           if (eol_style)
2405             {
2406               svn_subst_eol_style_from_value(&style, &eol, eol_style->data);
2407 
2408               if (style == svn_subst_eol_style_native)
2409                 eol = SVN_SUBST_NATIVE_EOL_STR;
2410               else if (style != svn_subst_eol_style_fixed
2411                        && style != svn_subst_eol_style_none)
2412                 return svn_error_create(SVN_ERR_IO_UNKNOWN_EOL, NULL, NULL);
2413             }
2414 
2415           if (keywords)
2416             SVN_ERR(svn_subst_build_keywords3(&kw, keywords->data, "", "",
2417                                               "", 0, "", scratch_pool));
2418 
2419           mine_stream = svn_subst_stream_translated(
2420             mine_stream, eol, FALSE, kw, FALSE, scratch_pool);
2421         }
2422 
2423       SVN_ERR(svn_stream_open_readonly(&older_stream, older_abspath,
2424                                        scratch_pool, scratch_pool));
2425 
2426       SVN_ERR(svn_stream_contents_same2(same, mine_stream, older_stream,
2427                                         scratch_pool));
2428 
2429     }
2430 
2431   return SVN_NO_ERROR;
2432 }
2433 
2434 /* An svn_diff_tree_processor_t function.
2435  *
2436  * Called after merge_file_opened() when a node does exist in LEFT_SOURCE, but
2437  * no longer exists (or is replaced) in RIGHT_SOURCE.
2438  *
2439  * When a node is replaced instead of just added a separate opened+added will
2440  * be invoked after the current open+deleted.
2441  */
2442 static svn_error_t *
merge_file_deleted(const char * relpath,const svn_diff_source_t * left_source,const char * left_file,apr_hash_t * left_props,void * file_baton,const struct svn_diff_tree_processor_t * processor,apr_pool_t * scratch_pool)2443 merge_file_deleted(const char *relpath,
2444                    const svn_diff_source_t *left_source,
2445                    const char *left_file,
2446                    /*const*/ apr_hash_t *left_props,
2447                    void *file_baton,
2448                    const struct svn_diff_tree_processor_t *processor,
2449                    apr_pool_t *scratch_pool)
2450 {
2451   merge_cmd_baton_t *merge_b = processor->baton;
2452   struct merge_file_baton_t *fb = file_baton;
2453   const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
2454                                               relpath, scratch_pool);
2455   svn_boolean_t same;
2456 
2457   SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
2458 
2459   if (fb->shadowed)
2460     {
2461       if (fb->tree_conflict_reason == CONFLICT_REASON_NONE)
2462         {
2463           /* We haven't notified for this node yet: report a skip */
2464           SVN_ERR(record_skip(merge_b, local_abspath, svn_node_file,
2465                               svn_wc_notify_update_shadowed_delete,
2466                               fb->skip_reason, fb->parent_baton,
2467                               scratch_pool));
2468         }
2469 
2470       return SVN_NO_ERROR;
2471     }
2472 
2473   /* Easy out: We are only applying mergeinfo differences. */
2474   if (merge_b->record_only)
2475     {
2476       return SVN_NO_ERROR;
2477     }
2478 
2479   /* If the files are identical, attempt deletion */
2480   if (merge_b->force_delete)
2481     same = TRUE;
2482   else
2483     SVN_ERR(files_same_p(&same, left_file, left_props,
2484                          local_abspath, merge_b->ctx->wc_ctx,
2485                          scratch_pool));
2486 
2487   if (fb->parent_baton
2488       && fb->parent_baton->delete_state)
2489     {
2490       if (same)
2491         {
2492           /* Note that we checked this file */
2493           store_path(fb->parent_baton->delete_state->compared_abspaths,
2494                      local_abspath);
2495         }
2496       else
2497         {
2498           /* We found some modification. Parent should raise a tree conflict */
2499           fb->parent_baton->delete_state->found_edit = TRUE;
2500         }
2501 
2502       return SVN_NO_ERROR;
2503     }
2504   else if (same)
2505     {
2506       if (!merge_b->dry_run)
2507         SVN_ERR(svn_wc_delete4(merge_b->ctx->wc_ctx, local_abspath,
2508                                FALSE /* keep_local */, FALSE /* unversioned */,
2509                                merge_b->ctx->cancel_func,
2510                                merge_b->ctx->cancel_baton,
2511                                NULL, NULL /* no notify */,
2512                                scratch_pool));
2513 
2514       /* Record that we might have deleted mergeinfo */
2515       alloc_and_store_path(&merge_b->paths_with_deleted_mergeinfo,
2516                            local_abspath, merge_b->pool);
2517 
2518       /* And notify the deletion */
2519       SVN_ERR(record_update_delete(merge_b, fb->parent_baton, local_abspath,
2520                                    svn_node_file, scratch_pool));
2521     }
2522   else
2523     {
2524       /* The files differ, so raise a conflict instead of deleting */
2525 
2526       /* This is use case 5 described in the paper attached to issue
2527        * #2282.  See also notes/tree-conflicts/detection.txt
2528        */
2529       SVN_ERR(record_tree_conflict(merge_b, local_abspath, fb->parent_baton,
2530                                    svn_node_file,
2531                                    svn_node_file,
2532                                    svn_node_none,
2533                                    svn_wc_conflict_action_delete,
2534                                    svn_wc_conflict_reason_edited,
2535                                    NULL, TRUE,
2536                                    scratch_pool));
2537     }
2538 
2539   return SVN_NO_ERROR;
2540 }
2541 
2542 /* An svn_diff_tree_processor_t function.
2543 
2544    Called before either merge_dir_changed(), merge_dir_added(),
2545    merge_dir_deleted() or merge_dir_closed(), unless it sets *SKIP to TRUE.
2546 
2547    After this call and before the close call, all descendants will receive
2548    their changes, unless *SKIP_CHILDREN is set to TRUE.
2549 
2550    When *SKIP is TRUE, the diff driver avoids work on getting the details
2551    for the closing callbacks.
2552 
2553    The SKIP and SKIP_DESCENDANTS work independently.
2554  */
2555 static svn_error_t *
merge_dir_opened(void ** new_dir_baton,svn_boolean_t * skip,svn_boolean_t * skip_children,const char * relpath,const svn_diff_source_t * left_source,const svn_diff_source_t * right_source,const svn_diff_source_t * copyfrom_source,void * parent_dir_baton,const struct svn_diff_tree_processor_t * processor,apr_pool_t * result_pool,apr_pool_t * scratch_pool)2556 merge_dir_opened(void **new_dir_baton,
2557                  svn_boolean_t *skip,
2558                  svn_boolean_t *skip_children,
2559                  const char *relpath,
2560                  const svn_diff_source_t *left_source,
2561                  const svn_diff_source_t *right_source,
2562                  const svn_diff_source_t *copyfrom_source,
2563                  void *parent_dir_baton,
2564                  const struct svn_diff_tree_processor_t *processor,
2565                  apr_pool_t *result_pool,
2566                  apr_pool_t *scratch_pool)
2567 {
2568   merge_cmd_baton_t *merge_b = processor->baton;
2569   struct merge_dir_baton_t *db;
2570   struct merge_dir_baton_t *pdb = parent_dir_baton;
2571 
2572   const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
2573                                               relpath, scratch_pool);
2574 
2575   db = apr_pcalloc(result_pool, sizeof(*db));
2576   db->pool = result_pool;
2577   db->tree_conflict_reason = CONFLICT_REASON_NONE;
2578   db->tree_conflict_action = svn_wc_conflict_action_edit;
2579   db->skip_reason = svn_wc_notify_state_unknown;
2580 
2581   *new_dir_baton = db;
2582 
2583   if (left_source)
2584     db->tree_conflict_merge_left_node_kind = svn_node_dir;
2585   else
2586     db->tree_conflict_merge_left_node_kind = svn_node_none;
2587 
2588   if (right_source)
2589     db->tree_conflict_merge_right_node_kind = svn_node_dir;
2590   else
2591     db->tree_conflict_merge_right_node_kind = svn_node_none;
2592 
2593   if (pdb)
2594     {
2595       db->parent_baton = pdb;
2596       db->shadowed = pdb->shadowed;
2597       db->skip_reason = pdb->skip_reason;
2598     }
2599 
2600   if (db->shadowed)
2601     {
2602       /* An ancestor is tree conflicted. Nothing to do here. */
2603       if (! left_source)
2604         db->added = TRUE;
2605     }
2606   else if (left_source != NULL)
2607     {
2608       /* Node is expected to be a directory. */
2609       svn_boolean_t is_deleted;
2610       svn_boolean_t excluded;
2611       svn_depth_t parent_depth;
2612 
2613       if (! right_source)
2614           db->tree_conflict_action = svn_wc_conflict_action_delete;
2615 
2616       /* Check for an obstructed or missing node on disk. */
2617       {
2618         svn_wc_notify_state_t obstr_state;
2619         SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, &excluded,
2620                                           &db->tree_conflict_local_node_kind,
2621                                           &parent_depth, merge_b,
2622                                           local_abspath, scratch_pool));
2623 
2624         if (obstr_state != svn_wc_notify_state_inapplicable)
2625           {
2626             db->shadowed = TRUE;
2627 
2628             if (obstr_state == svn_wc_notify_state_obstructed)
2629               {
2630                 svn_boolean_t is_wcroot;
2631 
2632                 SVN_ERR(svn_wc_check_root(&is_wcroot, NULL, NULL,
2633                                         merge_b->ctx->wc_ctx,
2634                                         local_abspath, scratch_pool));
2635 
2636                 if (is_wcroot)
2637                   {
2638                     db->tree_conflict_reason = CONFLICT_REASON_SKIP_WC;
2639                     return SVN_NO_ERROR;
2640                   }
2641               }
2642 
2643             db->tree_conflict_reason = CONFLICT_REASON_SKIP;
2644             db->skip_reason = obstr_state;
2645 
2646             if (! right_source)
2647               {
2648                 *skip = *skip_children = TRUE;
2649                 SVN_ERR(mark_dir_edited(merge_b, db, local_abspath,
2650                                         scratch_pool));
2651               }
2652 
2653             return SVN_NO_ERROR;
2654           }
2655 
2656         if (is_deleted)
2657           db->tree_conflict_local_node_kind = svn_node_none;
2658       }
2659 
2660       if (db->tree_conflict_local_node_kind == svn_node_none)
2661         {
2662           db->shadowed = TRUE;
2663 
2664           /* If this is not the merge target and the parent is too shallow to
2665              contain this directory, and the directory is not presen
2666              via exclusion or depth filtering, skip it instead of recording
2667              a tree conflict.
2668 
2669              Non-inheritable mergeinfo will be recorded, allowing
2670              future merges into non-shallow working copies to merge
2671              changes we missed this time around. */
2672           if (pdb && (excluded
2673                       || (parent_depth != svn_depth_unknown &&
2674                           parent_depth < svn_depth_immediates)))
2675             {
2676               db->shadowed = TRUE;
2677 
2678               db->tree_conflict_reason = CONFLICT_REASON_SKIP;
2679               db->skip_reason = svn_wc_notify_state_missing;
2680 
2681               return SVN_NO_ERROR;
2682             }
2683 
2684           if (is_deleted)
2685             db->tree_conflict_reason = svn_wc_conflict_reason_deleted;
2686           else
2687             db->tree_conflict_reason = svn_wc_conflict_reason_missing;
2688 
2689           /* ### To avoid breaking tests */
2690           *skip = TRUE;
2691           *skip_children = TRUE;
2692           SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
2693           return SVN_NO_ERROR;
2694           /* ### /avoid breaking tests */
2695         }
2696       else if (db->tree_conflict_local_node_kind != svn_node_dir)
2697         {
2698           svn_boolean_t added;
2699 
2700           db->shadowed = TRUE;
2701           SVN_ERR(svn_wc__node_is_added(&added, merge_b->ctx->wc_ctx,
2702                                         local_abspath, scratch_pool));
2703 
2704           db->tree_conflict_reason = added ? svn_wc_conflict_reason_added
2705                                            : svn_wc_conflict_reason_obstructed;
2706 
2707           /* ### To avoid breaking tests */
2708           *skip = TRUE;
2709           *skip_children = TRUE;
2710           SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
2711           return SVN_NO_ERROR;
2712           /* ### /avoid breaking tests */
2713         }
2714 
2715       if (! right_source)
2716         {
2717           /* We want to delete the directory */
2718           /* Mark PB edited now? */
2719           db->tree_conflict_action = svn_wc_conflict_action_delete;
2720           SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
2721 
2722           if (db->shadowed)
2723             {
2724               *skip_children = TRUE;
2725               return SVN_NO_ERROR; /* Already set a tree conflict */
2726             }
2727 
2728           db->delete_state = (pdb != NULL) ? pdb->delete_state : NULL;
2729 
2730           if (db->delete_state && db->delete_state->found_edit)
2731             {
2732               /* A sibling found a conflict. Done. */
2733               *skip = TRUE;
2734               *skip_children = TRUE;
2735             }
2736           else if (merge_b->force_delete)
2737             {
2738               /* No comparison necessary */
2739               *skip_children = TRUE;
2740             }
2741           else if (! db->delete_state)
2742             {
2743               /* Start descendant comparison */
2744               db->delete_state = apr_pcalloc(db->pool,
2745                                              sizeof(*db->delete_state));
2746 
2747               db->delete_state->del_root = db;
2748               db->delete_state->compared_abspaths = apr_hash_make(db->pool);
2749             }
2750         }
2751     }
2752   else
2753     {
2754       const svn_wc_conflict_description2_t *old_tc = NULL;
2755 
2756       /* The node doesn't exist pre-merge: We have an addition */
2757       db->added = TRUE;
2758       db->tree_conflict_action = svn_wc_conflict_action_add;
2759 
2760       if (pdb && pdb->pending_deletes
2761           && svn_hash_gets(pdb->pending_deletes, local_abspath))
2762         {
2763           db->add_is_replace = TRUE;
2764           db->tree_conflict_action = svn_wc_conflict_action_replace;
2765 
2766           svn_hash_sets(pdb->pending_deletes, local_abspath, NULL);
2767         }
2768 
2769       if (pdb
2770           && pdb->new_tree_conflicts
2771           && (old_tc = svn_hash_gets(pdb->new_tree_conflicts, local_abspath)))
2772         {
2773           db->tree_conflict_action = svn_wc_conflict_action_replace;
2774           db->tree_conflict_reason = old_tc->reason;
2775 
2776           if (old_tc->reason == svn_wc_conflict_reason_deleted
2777              || old_tc->reason == svn_wc_conflict_reason_moved_away)
2778             {
2779               /* Issue #3806: Incoming replacements on local deletes produce
2780                  inconsistent result.
2781 
2782                  In this specific case we can continue applying the add part
2783                  of the replacement. */
2784             }
2785           else
2786             {
2787               *skip = TRUE;
2788               *skip_children = TRUE;
2789 
2790               /* Update the tree conflict to store that this is a replace */
2791               SVN_ERR(record_tree_conflict(merge_b, local_abspath, pdb,
2792                                            old_tc->node_kind,
2793                                            old_tc->src_left_version->node_kind,
2794                                            svn_node_dir,
2795                                            db->tree_conflict_action,
2796                                            db->tree_conflict_reason,
2797                                            old_tc, FALSE,
2798                                            scratch_pool));
2799 
2800               return SVN_NO_ERROR;
2801             }
2802         }
2803 
2804       if (! (merge_b->dry_run
2805              && ((pdb && pdb->added) || db->add_is_replace)))
2806         {
2807           svn_wc_notify_state_t obstr_state;
2808           svn_boolean_t is_deleted;
2809 
2810           SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, NULL,
2811                                             &db->tree_conflict_local_node_kind,
2812                                             NULL, merge_b, local_abspath,
2813                                             scratch_pool));
2814 
2815           /* In this case of adding a directory, we have an exception to the
2816            * usual "skip if it's inconsistent" rule. If the directory exists
2817            * on disk unexpectedly, we simply make it versioned, because we can
2818            * do so without risk of destroying data. Only skip if it is
2819            * versioned but unexpectedly missing from disk, or is unversioned
2820            * but obstructed by a node of the wrong kind. */
2821           if (obstr_state == svn_wc_notify_state_obstructed
2822               && (is_deleted ||
2823                   db->tree_conflict_local_node_kind == svn_node_none))
2824             {
2825               svn_node_kind_t disk_kind;
2826 
2827               SVN_ERR(svn_io_check_path(local_abspath, &disk_kind,
2828                                         scratch_pool));
2829 
2830               if (disk_kind == svn_node_dir)
2831                 {
2832                   obstr_state = svn_wc_notify_state_inapplicable;
2833                   db->add_existing = TRUE; /* Take over existing directory */
2834                 }
2835             }
2836 
2837           if (obstr_state != svn_wc_notify_state_inapplicable)
2838             {
2839               /* Skip the obstruction */
2840               db->shadowed = TRUE;
2841               db->tree_conflict_reason = CONFLICT_REASON_SKIP;
2842               db->skip_reason = obstr_state;
2843             }
2844           else if (db->tree_conflict_local_node_kind != svn_node_none
2845                    && !is_deleted)
2846             {
2847               /* Set a tree conflict */
2848               svn_boolean_t added;
2849               db->shadowed = TRUE;
2850 
2851               SVN_ERR(svn_wc__node_is_added(&added, merge_b->ctx->wc_ctx,
2852                                             local_abspath, scratch_pool));
2853 
2854               db->tree_conflict_reason = added ? svn_wc_conflict_reason_added
2855                                                : svn_wc_conflict_reason_obstructed;
2856             }
2857         }
2858 
2859       /* Handle pending conflicts */
2860       SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
2861 
2862       if (db->shadowed)
2863         {
2864           /* Notified and done. Skip children? */
2865         }
2866       else if (merge_b->record_only)
2867         {
2868           /* Ok, we are done for this node and its descendants */
2869           *skip = TRUE;
2870           *skip_children = TRUE;
2871         }
2872       else if (! merge_b->dry_run)
2873         {
2874           /* Create the directory on disk, to allow descendants to be added */
2875           if (! db->add_existing)
2876             SVN_ERR(svn_io_dir_make(local_abspath, APR_OS_DEFAULT,
2877                                     scratch_pool));
2878 
2879           if (old_tc)
2880             {
2881               /* svn_wc_add4 and svn_wc_add_from_disk3 can't add a node
2882                  over an existing tree conflict */
2883 
2884               /* ### These functions should take some tree conflict argument
2885                      and allow overwriting the tc when one is passed */
2886 
2887               SVN_ERR(svn_wc__del_tree_conflict(merge_b->ctx->wc_ctx,
2888                                                 local_abspath,
2889                                                 scratch_pool));
2890             }
2891 
2892           if (merge_b->same_repos)
2893             {
2894               const char *original_url;
2895 
2896               original_url = svn_path_url_add_component2(
2897                                         merge_b->merge_source.loc2->url,
2898                                         relpath, scratch_pool);
2899 
2900               /* Limitation (aka HACK):
2901                  We create a newly added directory with an original URL and
2902                  revision as that in the repository, but without its properties
2903                  and children.
2904 
2905                  When the merge is cancelled before the final dir_added(), the
2906                  copy won't really represent the in-repository state of the node.
2907                */
2908               SVN_ERR(svn_wc_add4(merge_b->ctx->wc_ctx, local_abspath,
2909                                   svn_depth_infinity,
2910                                   original_url,
2911                                   right_source->revision,
2912                                   merge_b->ctx->cancel_func,
2913                                   merge_b->ctx->cancel_baton,
2914                                   NULL, NULL /* no notify! */,
2915                                   scratch_pool));
2916             }
2917           else
2918             {
2919               SVN_ERR(svn_wc_add_from_disk3(merge_b->ctx->wc_ctx, local_abspath,
2920                                             apr_hash_make(scratch_pool),
2921                                             FALSE /* skip checks */,
2922                                             NULL, NULL /* no notify! */,
2923                                             scratch_pool));
2924             }
2925 
2926           if (old_tc != NULL)
2927             {
2928               /* ### Should be atomic with svn_wc_add(4|_from_disk2)() */
2929               SVN_ERR(record_tree_conflict(merge_b, local_abspath, pdb,
2930                                            old_tc->node_kind,
2931                                            svn_node_none,
2932                                            svn_node_dir,
2933                                            db->tree_conflict_action,
2934                                            db->tree_conflict_reason,
2935                                            old_tc, FALSE,
2936                                            scratch_pool));
2937             }
2938         }
2939 
2940       if (! db->shadowed && !merge_b->record_only)
2941         SVN_ERR(record_update_add(merge_b, local_abspath, svn_node_dir,
2942                                   db->add_is_replace, scratch_pool));
2943     }
2944   return SVN_NO_ERROR;
2945 }
2946 
2947 /* An svn_diff_tree_processor_t function.
2948  *
2949  * Called after merge_dir_opened() when a node exists in both the left and
2950  * right source, but has its properties changed inbetween.
2951  *
2952  * After the merge_dir_opened() but before the call to this merge_dir_changed()
2953  * function all descendants will have been updated.
2954  */
2955 static svn_error_t *
merge_dir_changed(const char * relpath,const svn_diff_source_t * left_source,const svn_diff_source_t * right_source,apr_hash_t * left_props,apr_hash_t * right_props,const apr_array_header_t * prop_changes,void * dir_baton,const struct svn_diff_tree_processor_t * processor,apr_pool_t * scratch_pool)2956 merge_dir_changed(const char *relpath,
2957                   const svn_diff_source_t *left_source,
2958                   const svn_diff_source_t *right_source,
2959                   /*const*/ apr_hash_t *left_props,
2960                   /*const*/ apr_hash_t *right_props,
2961                   const apr_array_header_t *prop_changes,
2962                   void *dir_baton,
2963                   const struct svn_diff_tree_processor_t *processor,
2964                   apr_pool_t *scratch_pool)
2965 {
2966   merge_cmd_baton_t *merge_b = processor->baton;
2967   struct merge_dir_baton_t *db = dir_baton;
2968   const apr_array_header_t *props;
2969   const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
2970                                               relpath, scratch_pool);
2971 
2972   SVN_ERR(handle_pending_notifications(merge_b, db, scratch_pool));
2973 
2974   SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
2975 
2976   if (db->shadowed)
2977     {
2978       if (db->tree_conflict_reason == CONFLICT_REASON_NONE)
2979         {
2980           /* We haven't notified for this node yet: report a skip */
2981           SVN_ERR(record_skip(merge_b, local_abspath, svn_node_dir,
2982                               svn_wc_notify_update_shadowed_update,
2983                               db->skip_reason, db->parent_baton,
2984                               scratch_pool));
2985         }
2986 
2987       return SVN_NO_ERROR;
2988     }
2989 
2990   SVN_ERR(prepare_merge_props_changed(&props, local_abspath, prop_changes,
2991                                       merge_b, scratch_pool, scratch_pool));
2992 
2993   if (props->nelts)
2994     {
2995       const svn_wc_conflict_version_t *left;
2996       const svn_wc_conflict_version_t *right;
2997       svn_client_ctx_t *ctx = merge_b->ctx;
2998       svn_wc_notify_state_t prop_state;
2999 
3000       SVN_ERR(make_conflict_versions(&left, &right, local_abspath,
3001                                      svn_node_dir, svn_node_dir,
3002                                      &merge_b->merge_source,
3003                                      merge_b->target,
3004                                      scratch_pool, scratch_pool));
3005 
3006       SVN_ERR(svn_wc_merge_props3(&prop_state, ctx->wc_ctx, local_abspath,
3007                                   left, right,
3008                                   left_props, props,
3009                                   merge_b->dry_run,
3010                                   NULL, NULL,
3011                                   ctx->cancel_func, ctx->cancel_baton,
3012                                   scratch_pool));
3013 
3014       if (prop_state == svn_wc_notify_state_conflicted)
3015         {
3016           alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
3017                                merge_b->pool);
3018         }
3019 
3020       if (prop_state == svn_wc_notify_state_conflicted
3021           || prop_state == svn_wc_notify_state_merged
3022           || prop_state == svn_wc_notify_state_changed)
3023         {
3024           SVN_ERR(record_update_update(merge_b, local_abspath, svn_node_file,
3025                                        svn_wc_notify_state_inapplicable,
3026                                        prop_state, scratch_pool));
3027         }
3028     }
3029 
3030   return SVN_NO_ERROR;
3031 }
3032 
3033 
3034 /* An svn_diff_tree_processor_t function.
3035  *
3036  * Called after merge_dir_opened() when a node doesn't exist in LEFT_SOURCE,
3037  * but does in RIGHT_SOURCE. After the merge_dir_opened() but before the call
3038  * to this merge_dir_added() function all descendants will have been added.
3039  *
3040  * When a node is replaced instead of just added a separate opened+deleted will
3041  * be invoked before the current open+added.
3042  */
3043 static svn_error_t *
merge_dir_added(const char * relpath,const svn_diff_source_t * copyfrom_source,const svn_diff_source_t * right_source,apr_hash_t * copyfrom_props,apr_hash_t * right_props,void * dir_baton,const struct svn_diff_tree_processor_t * processor,apr_pool_t * scratch_pool)3044 merge_dir_added(const char *relpath,
3045                 const svn_diff_source_t *copyfrom_source,
3046                 const svn_diff_source_t *right_source,
3047                 /*const*/ apr_hash_t *copyfrom_props,
3048                 /*const*/ apr_hash_t *right_props,
3049                 void *dir_baton,
3050                 const struct svn_diff_tree_processor_t *processor,
3051                 apr_pool_t *scratch_pool)
3052 {
3053   merge_cmd_baton_t *merge_b = processor->baton;
3054   struct merge_dir_baton_t *db = dir_baton;
3055   const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
3056                                               relpath, scratch_pool);
3057 
3058   /* For consistency; usually a no-op from _dir_added() */
3059   SVN_ERR(handle_pending_notifications(merge_b, db, scratch_pool));
3060   SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
3061 
3062   if (db->shadowed)
3063     {
3064       if (db->tree_conflict_reason == CONFLICT_REASON_NONE)
3065         {
3066           /* We haven't notified for this node yet: report a skip */
3067           SVN_ERR(record_skip(merge_b, local_abspath, svn_node_dir,
3068                               svn_wc_notify_update_shadowed_add,
3069                               db->skip_reason, db->parent_baton,
3070                               scratch_pool));
3071         }
3072 
3073       return SVN_NO_ERROR;
3074     }
3075 
3076   SVN_ERR_ASSERT(
3077                  db->edited                  /* Marked edited from merge_open_dir() */
3078                  && ! merge_b->record_only /* Skip details from merge_open_dir() */
3079                  );
3080 
3081   if ((merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
3082       && ( !db->parent_baton || !db->parent_baton->added))
3083     {
3084       /* Store the roots of added subtrees */
3085       store_path(merge_b->added_abspaths, local_abspath);
3086     }
3087 
3088   if (merge_b->same_repos)
3089     {
3090       /* When the directory was added in merge_dir_added() we didn't update its
3091          pristine properties. Instead we receive the property changes later and
3092          apply them in this function.
3093 
3094          If we would apply them as changes (such as before fixing issue #3405),
3095          we would see the unmodified properties as local changes, and the
3096          pristine properties would be out of sync with what the repository
3097          expects for this directory.
3098 
3099          Instead of doing that we now simply set the properties as the pristine
3100          properties via a private libsvn_wc api.
3101       */
3102 
3103       const char *copyfrom_url;
3104       svn_revnum_t copyfrom_rev;
3105       const char *parent_abspath;
3106       const char *child;
3107 
3108       /* Creating a hash containing regular and entry props */
3109       apr_hash_t *new_pristine_props = right_props;
3110 
3111       parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3112       child = svn_dirent_is_child(merge_b->target->abspath, local_abspath, NULL);
3113       SVN_ERR_ASSERT(child != NULL);
3114 
3115       copyfrom_url = svn_path_url_add_component2(merge_b->merge_source.loc2->url,
3116                                                  child, scratch_pool);
3117       copyfrom_rev = right_source->revision;
3118 
3119       SVN_ERR(check_repos_match(merge_b->target, parent_abspath, copyfrom_url,
3120                                 scratch_pool));
3121 
3122       if (!merge_b->dry_run)
3123         {
3124           SVN_ERR(svn_wc__complete_directory_add(merge_b->ctx->wc_ctx,
3125                                                 local_abspath,
3126                                                 new_pristine_props,
3127                                                 copyfrom_url, copyfrom_rev,
3128                                                 scratch_pool));
3129         }
3130 
3131       if (svn_hash_gets(new_pristine_props, SVN_PROP_MERGEINFO))
3132         {
3133           alloc_and_store_path(&merge_b->paths_with_new_mergeinfo,
3134                                local_abspath, merge_b->pool);
3135         }
3136     }
3137   else
3138     {
3139       apr_array_header_t *regular_props;
3140       apr_hash_t *new_props;
3141       svn_wc_notify_state_t prop_state;
3142 
3143       SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(right_props,
3144                                                           scratch_pool),
3145                                    NULL, NULL, &regular_props, scratch_pool));
3146 
3147       new_props = svn_prop_array_to_hash(regular_props, scratch_pool);
3148 
3149       svn_hash_sets(new_props, SVN_PROP_MERGEINFO, NULL);
3150 
3151       /* ### What is the easiest way to set new_props on LOCAL_ABSPATH?
3152 
3153          ### This doesn't need a merge as we just added the node
3154          ### (or installed a tree conflict and skipped this node)*/
3155 
3156       SVN_ERR(svn_wc_merge_props3(&prop_state, merge_b->ctx->wc_ctx,
3157                                   local_abspath,
3158                                   NULL, NULL,
3159                                   apr_hash_make(scratch_pool),
3160                                   svn_prop_hash_to_array(new_props,
3161                                                          scratch_pool),
3162                                   merge_b->dry_run,
3163                                   NULL, NULL,
3164                                   merge_b->ctx->cancel_func,
3165                                   merge_b->ctx->cancel_baton,
3166                                   scratch_pool));
3167       if (prop_state == svn_wc_notify_state_conflicted)
3168         {
3169           alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
3170                                merge_b->pool);
3171         }
3172     }
3173 
3174   return SVN_NO_ERROR;
3175 }
3176 
3177 /* Helper for merge_dir_deleted. Implement svn_wc_status_func4_t */
3178 static svn_error_t *
verify_touched_by_del_check(void * baton,const char * local_abspath,const svn_wc_status3_t * status,apr_pool_t * scratch_pool)3179 verify_touched_by_del_check(void *baton,
3180                             const char *local_abspath,
3181                             const svn_wc_status3_t *status,
3182                             apr_pool_t *scratch_pool)
3183 {
3184   struct dir_delete_baton_t *delb = baton;
3185 
3186   if (svn_hash_gets(delb->compared_abspaths, local_abspath))
3187     return SVN_NO_ERROR;
3188 
3189   switch (status->node_status)
3190     {
3191       case svn_wc_status_deleted:
3192       case svn_wc_status_ignored:
3193       case svn_wc_status_none:
3194         return SVN_NO_ERROR;
3195 
3196       default:
3197         delb->found_edit = TRUE;
3198         return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
3199     }
3200 }
3201 
3202 /* An svn_diff_tree_processor_t function.
3203  *
3204  * Called after merge_dir_opened() when a node existed only in the left source.
3205  *
3206  * After the merge_dir_opened() but before the call to this merge_dir_deleted()
3207  * function all descendants that existed in left_source will have been deleted.
3208  *
3209  * If this node is replaced, an _opened() followed by a matching _add() will
3210  * be invoked after this function.
3211  */
3212 static svn_error_t *
merge_dir_deleted(const char * relpath,const svn_diff_source_t * left_source,apr_hash_t * left_props,void * dir_baton,const struct svn_diff_tree_processor_t * processor,apr_pool_t * scratch_pool)3213 merge_dir_deleted(const char *relpath,
3214                   const svn_diff_source_t *left_source,
3215                   /*const*/ apr_hash_t *left_props,
3216                   void *dir_baton,
3217                   const struct svn_diff_tree_processor_t *processor,
3218                   apr_pool_t *scratch_pool)
3219 {
3220   merge_cmd_baton_t *merge_b = processor->baton;
3221   struct merge_dir_baton_t *db = dir_baton;
3222   const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
3223                                               relpath, scratch_pool);
3224   svn_boolean_t same;
3225   apr_hash_t *working_props;
3226 
3227   SVN_ERR(handle_pending_notifications(merge_b, db, scratch_pool));
3228   SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
3229 
3230   if (db->shadowed)
3231     {
3232       if (db->tree_conflict_reason == CONFLICT_REASON_NONE)
3233         {
3234           /* We haven't notified for this node yet: report a skip */
3235           SVN_ERR(record_skip(merge_b, local_abspath, svn_node_dir,
3236                               svn_wc_notify_update_shadowed_delete,
3237                               db->skip_reason, db->parent_baton,
3238                               scratch_pool));
3239         }
3240 
3241       return SVN_NO_ERROR;
3242     }
3243 
3244   /* Easy out: We are only applying mergeinfo differences. */
3245   if (merge_b->record_only)
3246     {
3247       return SVN_NO_ERROR;
3248     }
3249 
3250   SVN_ERR(svn_wc_prop_list2(&working_props,
3251                             merge_b->ctx->wc_ctx, local_abspath,
3252                             scratch_pool, scratch_pool));
3253 
3254   if (merge_b->force_delete)
3255     {
3256       /* In this legacy mode we just assume that a directory delete
3257          matches any directory. db->delete_state is NULL */
3258       same = TRUE;
3259     }
3260   else
3261     {
3262       struct dir_delete_baton_t *delb;
3263 
3264       /* Compare the properties */
3265       SVN_ERR(properties_same_p(&same, left_props, working_props,
3266                                 scratch_pool));
3267       delb = db->delete_state;
3268       assert(delb != NULL);
3269 
3270       if (! same)
3271         {
3272           delb->found_edit = TRUE;
3273         }
3274       else
3275         {
3276           store_path(delb->compared_abspaths, local_abspath);
3277         }
3278 
3279       if (delb->del_root != db)
3280         return SVN_NO_ERROR;
3281 
3282       if (delb->found_edit)
3283         same = FALSE;
3284       else
3285         {
3286           apr_array_header_t *ignores;
3287           svn_error_t *err;
3288           same = TRUE;
3289 
3290           SVN_ERR(svn_wc_get_default_ignores(&ignores, merge_b->ctx->config,
3291                                              scratch_pool));
3292 
3293           /* None of the descendants was modified, but maybe there are
3294              descendants we haven't walked?
3295 
3296              Note that we aren't interested in changes, as we already verified
3297              changes in the paths touched by the merge. And the existence of
3298              other paths is enough to mark the directory edited */
3299           err = svn_wc_walk_status(merge_b->ctx->wc_ctx, local_abspath,
3300                                    svn_depth_infinity, TRUE /* get-all */,
3301                                    FALSE /* no-ignore */,
3302                                    TRUE /* ignore-text-mods */, ignores,
3303                                    verify_touched_by_del_check, delb,
3304                                    merge_b->ctx->cancel_func,
3305                                    merge_b->ctx->cancel_baton,
3306                                    scratch_pool);
3307 
3308           if (err)
3309             {
3310               if (err->apr_err != SVN_ERR_CEASE_INVOCATION)
3311                 return svn_error_trace(err);
3312 
3313               svn_error_clear(err);
3314             }
3315 
3316           same = ! delb->found_edit;
3317         }
3318     }
3319 
3320   if (same && !merge_b->dry_run)
3321     {
3322       svn_error_t *err;
3323 
3324       err = svn_wc_delete4(merge_b->ctx->wc_ctx, local_abspath,
3325                            FALSE /* keep_local */, FALSE /* unversioned */,
3326                            merge_b->ctx->cancel_func,
3327                            merge_b->ctx->cancel_baton,
3328                            NULL, NULL /* no notify */,
3329                            scratch_pool);
3330 
3331       if (err)
3332         {
3333           if (err->apr_err != SVN_ERR_WC_LEFT_LOCAL_MOD)
3334             return svn_error_trace(err);
3335 
3336           svn_error_clear(err);
3337           same = FALSE;
3338         }
3339     }
3340 
3341   if (! same)
3342     {
3343       /* If the attempt to delete an existing directory failed,
3344        * the directory has local modifications (e.g. locally added
3345        * files, or property changes). Flag a tree conflict. */
3346 
3347       /* This handles use case 5 described in the paper attached to issue
3348        * #2282.  See also notes/tree-conflicts/detection.txt
3349        */
3350       SVN_ERR(record_tree_conflict(merge_b, local_abspath, db->parent_baton,
3351                                    svn_node_dir,
3352                                    svn_node_dir,
3353                                    svn_node_none,
3354                                    svn_wc_conflict_action_delete,
3355                                    svn_wc_conflict_reason_edited,
3356                                    NULL, TRUE,
3357                                    scratch_pool));
3358     }
3359   else
3360     {
3361       /* Record that we might have deleted mergeinfo */
3362       if (working_props
3363           && svn_hash_gets(working_props, SVN_PROP_MERGEINFO))
3364         {
3365           alloc_and_store_path(&merge_b->paths_with_deleted_mergeinfo,
3366                                local_abspath, merge_b->pool);
3367         }
3368 
3369       SVN_ERR(record_update_delete(merge_b, db->parent_baton, local_abspath,
3370                                    svn_node_dir, scratch_pool));
3371     }
3372 
3373   return SVN_NO_ERROR;
3374 }
3375 
3376 /* An svn_diff_tree_processor_t function.
3377  *
3378  * Called after merge_dir_opened() when a node itself didn't change between
3379  * the left and right source.
3380  *
3381  * After the merge_dir_opened() but before the call to this merge_dir_closed()
3382  * function all descendants will have been processed.
3383  */
3384 static svn_error_t *
merge_dir_closed(const char * relpath,const svn_diff_source_t * left_source,const svn_diff_source_t * right_source,void * dir_baton,const struct svn_diff_tree_processor_t * processor,apr_pool_t * scratch_pool)3385 merge_dir_closed(const char *relpath,
3386                  const svn_diff_source_t *left_source,
3387                  const svn_diff_source_t *right_source,
3388                  void *dir_baton,
3389                  const struct svn_diff_tree_processor_t *processor,
3390                  apr_pool_t *scratch_pool)
3391 {
3392   merge_cmd_baton_t *merge_b = processor->baton;
3393   struct merge_dir_baton_t *db = dir_baton;
3394 
3395   SVN_ERR(handle_pending_notifications(merge_b, db, scratch_pool));
3396 
3397   return SVN_NO_ERROR;
3398 }
3399 
3400 /* An svn_diff_tree_processor_t function.
3401 
3402    Called when the diff driver wants to report an absent path.
3403 
3404    In case of merges this happens when the diff encounters a server-excluded
3405    path.
3406 
3407    We register a skipped path, which will make parent mergeinfo non-
3408    inheritable. This ensures that a future merge might see these skipped
3409    changes as eligable for merging.
3410 
3411    For legacy reasons we also notify the path as skipped.
3412  */
3413 static svn_error_t *
merge_node_absent(const char * relpath,void * dir_baton,const svn_diff_tree_processor_t * processor,apr_pool_t * scratch_pool)3414 merge_node_absent(const char *relpath,
3415                   void *dir_baton,
3416                   const svn_diff_tree_processor_t *processor,
3417                   apr_pool_t *scratch_pool)
3418 {
3419   merge_cmd_baton_t *merge_b = processor->baton;
3420   struct merge_dir_baton_t *db = dir_baton;
3421 
3422   const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
3423                                               relpath, scratch_pool);
3424 
3425   SVN_ERR(record_skip(merge_b, local_abspath, svn_node_unknown,
3426                       svn_wc_notify_skip, svn_wc_notify_state_missing,
3427                       db, scratch_pool));
3428 
3429   return SVN_NO_ERROR;
3430 }
3431 
3432 /* Return a diff processor that will apply the merge to the WC.
3433  */
3434 static svn_diff_tree_processor_t *
merge_apply_processor(merge_cmd_baton_t * merge_cmd_baton,apr_pool_t * result_pool)3435 merge_apply_processor(merge_cmd_baton_t *merge_cmd_baton,
3436                       apr_pool_t *result_pool)
3437 {
3438   svn_diff_tree_processor_t *merge_processor;
3439 
3440   merge_processor = svn_diff__tree_processor_create(merge_cmd_baton,
3441                                                     result_pool);
3442 
3443   merge_processor->dir_opened   = merge_dir_opened;
3444   merge_processor->dir_changed  = merge_dir_changed;
3445   merge_processor->dir_added    = merge_dir_added;
3446   merge_processor->dir_deleted  = merge_dir_deleted;
3447   merge_processor->dir_closed   = merge_dir_closed;
3448 
3449   merge_processor->file_opened  = merge_file_opened;
3450   merge_processor->file_changed = merge_file_changed;
3451   merge_processor->file_added   = merge_file_added;
3452   merge_processor->file_deleted = merge_file_deleted;
3453   /* Not interested in file_closed() */
3454 
3455   merge_processor->node_absent = merge_node_absent;
3456 
3457   return merge_processor;
3458 }
3459 
3460 /* Initialize minimal dir baton to allow calculating 'R'eplace
3461    from 'D'elete + 'A'dd. */
3462 static void *
open_dir_for_replace_single_file(apr_pool_t * result_pool)3463 open_dir_for_replace_single_file(apr_pool_t *result_pool)
3464 {
3465   struct merge_dir_baton_t *dir_baton = apr_pcalloc(result_pool, sizeof(*dir_baton));
3466 
3467   dir_baton->pool = result_pool;
3468   dir_baton->tree_conflict_reason = CONFLICT_REASON_NONE;
3469   dir_baton->tree_conflict_action = svn_wc_conflict_action_edit;
3470   dir_baton->skip_reason = svn_wc_notify_state_unknown;
3471 
3472   return dir_baton;
3473 }
3474 
3475 /*-----------------------------------------------------------------------*/
3476 
3477 /*** Merge Notification ***/
3478 
3479 
3480 /* Finds a nearest ancestor in CHILDREN_WITH_MERGEINFO for LOCAL_ABSPATH. If
3481    PATH_IS_OWN_ANCESTOR is TRUE then a child in CHILDREN_WITH_MERGEINFO
3482    where child->abspath == PATH is considered PATH's ancestor.  If FALSE,
3483    then child->abspath must be a proper ancestor of PATH.
3484 
3485    CHILDREN_WITH_MERGEINFO is expected to be sorted in Depth first
3486    order of path. */
3487 static svn_client__merge_path_t *
find_nearest_ancestor(const apr_array_header_t * children_with_mergeinfo,svn_boolean_t path_is_own_ancestor,const char * local_abspath)3488 find_nearest_ancestor(const apr_array_header_t *children_with_mergeinfo,
3489                       svn_boolean_t path_is_own_ancestor,
3490                       const char *local_abspath)
3491 {
3492   int i;
3493 
3494   SVN_ERR_ASSERT_NO_RETURN(children_with_mergeinfo != NULL);
3495 
3496   for (i = children_with_mergeinfo->nelts - 1; i >= 0 ; i--)
3497     {
3498       svn_client__merge_path_t *child =
3499         APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
3500 
3501       if (svn_dirent_is_ancestor(child->abspath, local_abspath)
3502           && (path_is_own_ancestor
3503               || strcmp(child->abspath, local_abspath) != 0))
3504         return child;
3505     }
3506   return NULL;
3507 }
3508 
3509 /* Find the highest level path in a merge target (possibly the merge target
3510    itself) to use in a merge notification header.
3511 
3512    Return the svn_client__merge_path_t * representing the most distant
3513    ancestor in CHILDREN_WITH_MERGEINFO of LOCAL_ABSPATH where said
3514    ancestor's first remaining ranges element (per the REMAINING_RANGES
3515    member of the ancestor) intersect with the first remaining ranges element
3516    for every intermediate ancestor svn_client__merge_path_t * of
3517    LOCAL_ABSPATH.  If no such ancestor is found return NULL.
3518 
3519    If the remaining ranges of the elements in CHILDREN_WITH_MERGEINFO
3520    represent a forward merge, then set *START to the oldest revision found
3521    in any of the intersecting ancestors and *END to the youngest revision
3522    found.  If the remaining ranges of the elements in CHILDREN_WITH_MERGEINFO
3523    represent a reverse merge, then set *START to the youngest revision
3524    found and *END to the oldest revision found.  If no ancestors are found
3525    then set *START and *END to SVN_INVALID_REVNUM.
3526 
3527    If PATH_IS_OWN_ANCESTOR is TRUE then a child in CHILDREN_WITH_MERGEINFO
3528    where child->abspath == PATH is considered PATH's ancestor.  If FALSE,
3529    then child->abspath must be a proper ancestor of PATH.
3530 
3531    See the CHILDREN_WITH_MERGEINFO ARRAY global comment for more
3532    information. */
3533 static svn_client__merge_path_t *
find_nearest_ancestor_with_intersecting_ranges(svn_revnum_t * start,svn_revnum_t * end,const apr_array_header_t * children_with_mergeinfo,svn_boolean_t path_is_own_ancestor,const char * local_abspath)3534 find_nearest_ancestor_with_intersecting_ranges(
3535   svn_revnum_t *start,
3536   svn_revnum_t *end,
3537   const apr_array_header_t *children_with_mergeinfo,
3538   svn_boolean_t path_is_own_ancestor,
3539   const char *local_abspath)
3540 {
3541   int i;
3542   svn_client__merge_path_t *nearest_ancestor = NULL;
3543 
3544   *start = SVN_INVALID_REVNUM;
3545   *end = SVN_INVALID_REVNUM;
3546 
3547   SVN_ERR_ASSERT_NO_RETURN(children_with_mergeinfo != NULL);
3548 
3549   for (i = children_with_mergeinfo->nelts - 1; i >= 0 ; i--)
3550     {
3551       svn_client__merge_path_t *child =
3552         APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
3553 
3554       if (svn_dirent_is_ancestor(child->abspath, local_abspath)
3555           && (path_is_own_ancestor
3556               || strcmp(child->abspath, local_abspath) != 0))
3557         {
3558           if (nearest_ancestor == NULL)
3559             {
3560               /* Found an ancestor. */
3561               nearest_ancestor = child;
3562 
3563               if (child->remaining_ranges)
3564                 {
3565                   svn_merge_range_t *r1 = APR_ARRAY_IDX(
3566                     child->remaining_ranges, 0, svn_merge_range_t *);
3567                   *start = r1->start;
3568                   *end = r1->end;
3569                 }
3570               else
3571                 {
3572                   /* If CHILD->REMAINING_RANGES is null then LOCAL_ABSPATH
3573                      is inside an absent subtree in the merge target. */
3574                   *start = SVN_INVALID_REVNUM;
3575                   *end = SVN_INVALID_REVNUM;
3576                   break;
3577                 }
3578             }
3579           else
3580             {
3581               /* We'e found another ancestor for LOCAL_ABSPATH.  Do its
3582                  first remaining range intersect with the previously
3583                  found ancestor? */
3584               svn_merge_range_t *r1 =
3585                 APR_ARRAY_IDX(nearest_ancestor->remaining_ranges, 0,
3586                               svn_merge_range_t *);
3587               svn_merge_range_t *r2 =
3588                 APR_ARRAY_IDX(child->remaining_ranges, 0,
3589                               svn_merge_range_t *);
3590 
3591               if (r1 && r2)
3592                 {
3593                   svn_merge_range_t range1;
3594                   svn_merge_range_t range2;
3595                   svn_boolean_t reverse_merge = r1->start > r2->end;
3596 
3597                   /* Flip endpoints if this is a reverse merge. */
3598                   if (reverse_merge)
3599                     {
3600                       range1.start = r1->end;
3601                       range1.end = r1->start;
3602                       range2.start = r2->end;
3603                       range2.end = r2->start;
3604                     }
3605                   else
3606                     {
3607                       range1.start = r1->start;
3608                       range1.end = r1->end;
3609                       range2.start = r2->start;
3610                       range2.end = r2->end;
3611                     }
3612 
3613                   if (range1.start < range2.end && range2.start < range1.end)
3614                     {
3615                       *start = reverse_merge ?
3616                         MAX(r1->start, r2->start) : MIN(r1->start, r2->start);
3617                       *end = reverse_merge ?
3618                         MIN(r1->end, r2->end) : MAX(r1->end, r2->end);
3619                       nearest_ancestor = child;
3620                     }
3621                 }
3622             }
3623         }
3624     }
3625   return nearest_ancestor;
3626 }
3627 
3628 /* Notify that we're starting to record mergeinfo for the merge of the
3629  * revision range RANGE into TARGET_ABSPATH.  RANGE should be null if the
3630  * merge sources are not from the same URL.
3631  *
3632  * This calls the client's notification receiver (as found in the client
3633  * context), with a WC abspath.
3634  */
3635 static void
notify_mergeinfo_recording(const char * target_abspath,const svn_merge_range_t * range,svn_client_ctx_t * ctx,apr_pool_t * pool)3636 notify_mergeinfo_recording(const char *target_abspath,
3637                            const svn_merge_range_t *range,
3638                            svn_client_ctx_t *ctx,
3639                            apr_pool_t *pool)
3640 {
3641   if (ctx->notify_func2)
3642     {
3643       svn_wc_notify_t *n = svn_wc_create_notify(
3644         target_abspath, svn_wc_notify_merge_record_info_begin, pool);
3645 
3646       n->merge_range = range ? svn_merge_range_dup(range, pool) : NULL;
3647       ctx->notify_func2(ctx->notify_baton2, n, pool);
3648     }
3649 }
3650 
3651 /* Notify that we're completing the merge into TARGET_ABSPATH.
3652  *
3653  * This calls the client's notification receiver (as found in the client
3654  * context), with a WC abspath.
3655  */
3656 static void
notify_merge_completed(const char * target_abspath,svn_client_ctx_t * ctx,apr_pool_t * pool)3657 notify_merge_completed(const char *target_abspath,
3658                        svn_client_ctx_t *ctx,
3659                        apr_pool_t *pool)
3660 {
3661   if (ctx->notify_func2)
3662     {
3663       svn_wc_notify_t *n
3664         = svn_wc_create_notify(target_abspath, svn_wc_notify_merge_completed,
3665                                pool);
3666       ctx->notify_func2(ctx->notify_baton2, n, pool);
3667     }
3668 }
3669 
3670 
3671 /* Remove merge source gaps from range used for merge notifications.
3672    See https://issues.apache.org/jira/browse/SVN-4138
3673 
3674    If IMPLICIT_SRC_GAP is not NULL then it is a rangelist containing a
3675    single range (see the implicit_src_gap member of merge_cmd_baton_t).
3676    RANGE describes a (possibly reverse) merge.
3677 
3678    If IMPLICIT_SRC_GAP is not NULL and it's sole range intersects with
3679    the older revision in *RANGE, then remove IMPLICIT_SRC_GAP's range
3680    from *RANGE. */
3681 static void
remove_source_gap(svn_merge_range_t * range,apr_array_header_t * implicit_src_gap)3682 remove_source_gap(svn_merge_range_t *range,
3683                   apr_array_header_t *implicit_src_gap)
3684 {
3685   if (implicit_src_gap)
3686     {
3687       svn_merge_range_t *gap_range =
3688         APR_ARRAY_IDX(implicit_src_gap, 0, svn_merge_range_t *);
3689       if (range->start < range->end)
3690         {
3691           if (gap_range->start == range->start)
3692             range->start = gap_range->end;
3693         }
3694       else /* Reverse merge */
3695         {
3696           if (gap_range->start == range->end)
3697             range->end = gap_range->end;
3698         }
3699     }
3700 }
3701 
3702 /* Notify that we're starting a merge
3703  *
3704  * This calls the client's notification receiver (as found in the client
3705  * context), with a WC abspath.
3706  */
3707 static void
notify_merge_begin(struct notify_begin_state_t * notify_begin_state,const char * local_abspath,svn_boolean_t delete_action,apr_pool_t * scratch_pool)3708 notify_merge_begin(struct notify_begin_state_t *notify_begin_state,
3709                    const char *local_abspath,
3710                    svn_boolean_t delete_action,
3711                    apr_pool_t *scratch_pool)
3712 {
3713   merge_cmd_baton_t *merge_b = notify_begin_state->merge_b;
3714   svn_wc_notify_t *notify;
3715   svn_merge_range_t n_range =
3716     {SVN_INVALID_REVNUM, SVN_INVALID_REVNUM, TRUE};
3717   const char *notify_abspath;
3718 
3719   if (! notify_begin_state->notify_func2)
3720     return;
3721 
3722   /* If our merge sources are ancestors of one another... */
3723   if (merge_b->merge_source.ancestral)
3724     {
3725       const svn_client__merge_path_t *child;
3726       /* Find LOCAL_ABSPATH's nearest ancestor in
3727          CHILDREN_WITH_MERGEINFO.  Normally we consider a child in
3728          CHILDREN_WITH_MERGEINFO representing PATH to be an
3729          ancestor of PATH, but if this is a deletion of PATH then the
3730          notification must be for a proper ancestor of PATH.  This ensures
3731          we don't get notifications like:
3732 
3733            --- Merging rX into 'PARENT/CHILD'
3734            D    PARENT/CHILD
3735 
3736          But rather:
3737 
3738            --- Merging rX into 'PARENT'
3739            D    PARENT/CHILD
3740       */
3741 
3742       child = find_nearest_ancestor_with_intersecting_ranges(
3743         &(n_range.start), &(n_range.end),
3744         merge_b->children_with_mergeinfo,
3745         ! delete_action, local_abspath);
3746 
3747       if (!child && delete_action)
3748         {
3749           /* Triggered by file replace in single-file-merge */
3750           child = find_nearest_ancestor(merge_b->children_with_mergeinfo,
3751                                         TRUE, local_abspath);
3752         }
3753 
3754       assert(child != NULL); /* Should always find the merge anchor */
3755 
3756       if (! child)
3757         return;
3758 
3759       if (notify_begin_state->last_abspath != NULL
3760           && strcmp(child->abspath, notify_begin_state->last_abspath) == 0)
3761         {
3762           /* Don't notify the same merge again */
3763           return;
3764         }
3765 
3766       notify_begin_state->last_abspath = child->abspath;
3767 
3768       if (child->absent || child->remaining_ranges->nelts == 0
3769           || !SVN_IS_VALID_REVNUM(n_range.start))
3770         {
3771           /* No valid information for an header */
3772           return;
3773         }
3774 
3775       notify_abspath = child->abspath;
3776     }
3777   else
3778     {
3779       if (notify_begin_state->last_abspath)
3780         return; /* already notified */
3781 
3782       notify_abspath = merge_b->target->abspath;
3783       /* Store something in last_abspath. Any value would do */
3784       notify_begin_state->last_abspath = merge_b->target->abspath;
3785     }
3786 
3787   notify = svn_wc_create_notify(notify_abspath,
3788                                 merge_b->same_repos
3789                                       ? svn_wc_notify_merge_begin
3790                                       : svn_wc_notify_foreign_merge_begin,
3791                                 scratch_pool);
3792 
3793   if (SVN_IS_VALID_REVNUM(n_range.start))
3794     {
3795       /* If the merge source has a gap, then don't mention
3796          those gap revisions in the notification. */
3797       remove_source_gap(&n_range, merge_b->implicit_src_gap);
3798       notify->merge_range = &n_range;
3799     }
3800   else
3801     {
3802       notify->merge_range = NULL;
3803     }
3804 
3805   notify_begin_state->notify_func2(notify_begin_state->notify_baton2, notify,
3806                                    scratch_pool);
3807 }
3808 
3809 /* Our notification callback, that adds a 'begin' notification */
3810 static void
notify_merging(void * baton,const svn_wc_notify_t * notify,apr_pool_t * pool)3811 notify_merging(void *baton,
3812                const svn_wc_notify_t *notify,
3813                apr_pool_t *pool)
3814 {
3815   struct notify_begin_state_t *b = baton;
3816 
3817   notify_merge_begin(b, notify->path,
3818                      notify->action == svn_wc_notify_update_delete,
3819                      pool);
3820 
3821   b->notify_func2(b->notify_baton2, notify, pool);
3822 }
3823 
3824 /* Set *OUT_RANGELIST to the intersection of IN_RANGELIST with the simple
3825  * (inheritable) revision range REV1:REV2, according to CONSIDER_INHERITANCE.
3826  * If REV1 is equal to REV2, the result is an empty rangelist, otherwise
3827  * REV1 must be less than REV2.
3828  *
3829  * Note: If CONSIDER_INHERITANCE is FALSE, the effect is to treat any non-
3830  * inheritable input ranges as if they were inheritable.  If it is TRUE, the
3831  * effect is to discard any non-inheritable input ranges.  Therefore the
3832  * ranges in *OUT_RANGELIST will always be inheritable. */
3833 static svn_error_t *
rangelist_intersect_range(svn_rangelist_t ** out_rangelist,const svn_rangelist_t * in_rangelist,svn_revnum_t rev1,svn_revnum_t rev2,svn_boolean_t consider_inheritance,apr_pool_t * result_pool,apr_pool_t * scratch_pool)3834 rangelist_intersect_range(svn_rangelist_t **out_rangelist,
3835                           const svn_rangelist_t *in_rangelist,
3836                           svn_revnum_t rev1,
3837                           svn_revnum_t rev2,
3838                           svn_boolean_t consider_inheritance,
3839                           apr_pool_t *result_pool,
3840                           apr_pool_t *scratch_pool)
3841 {
3842   SVN_ERR_ASSERT(rev1 <= rev2);
3843 
3844   if (rev1 < rev2)
3845     {
3846       svn_rangelist_t *simple_rangelist =
3847         svn_rangelist__initialize(rev1, rev2, TRUE, scratch_pool);
3848 
3849       SVN_ERR(svn_rangelist_intersect(out_rangelist,
3850                                       simple_rangelist, in_rangelist,
3851                                       consider_inheritance, result_pool));
3852     }
3853   else
3854     {
3855       *out_rangelist = apr_array_make(result_pool, 0,
3856                                       sizeof(svn_merge_range_t *));
3857     }
3858   return SVN_NO_ERROR;
3859 }
3860 
3861 /* Helper for fix_deleted_subtree_ranges().  Like fix_deleted_subtree_ranges()
3862    this function should only be called when honoring mergeinfo.
3863 
3864    CHILD, PARENT, REVISION1, REVISION2, and RA_SESSION are all cascaded from
3865    fix_deleted_subtree_ranges() -- see that function for more information on
3866    each.
3867 
3868    If PARENT is not the merge target then PARENT must have already have been
3869    processed by this function as a child.  Specifically, this means that
3870    PARENT->REMAINING_RANGES must already be populated -- it can be an empty
3871    rangelist but cannot be NULL.
3872 
3873    PRIMARY_URL is the merge source url of CHILD at the younger of REVISION1
3874    and REVISION2.
3875 
3876    Since this function is only invoked for subtrees of the merge target, the
3877    guarantees afforded by normalize_merge_sources() don't apply - see the
3878    'MERGEINFO MERGE SOURCE NORMALIZATION' comment at the top of this file.
3879    Therefore it is possible that PRIMARY_URL@REVISION1 and
3880    PRIMARY_URL@REVISION2 don't describe the endpoints of an unbroken line of
3881    history.  The purpose of this helper is to identify these cases of broken
3882    history and adjust CHILD->REMAINING_RANGES in such a way we don't later try
3883    to describe nonexistent path/revisions to the merge report editor -- see
3884    drive_merge_report_editor().
3885 
3886    If PRIMARY_URL@REVISION1 and PRIMARY_URL@REVISION2 describe an unbroken
3887    line of history then do nothing and leave CHILD->REMAINING_RANGES as-is.
3888 
3889    If neither PRIMARY_URL@REVISION1 nor PRIMARY_URL@REVISION2 exist then
3890    there is nothing to merge to CHILD->ABSPATH so set CHILD->REMAINING_RANGES
3891    equal to PARENT->REMAINING_RANGES.  This will cause the subtree to
3892    effectively ignore CHILD -- see 'Note: If the first svn_merge_range_t...'
3893    in drive_merge_report_editor()'s doc string.
3894 
3895    If PRIMARY_URL@REVISION1 *xor* PRIMARY_URL@REVISION2 exist then we take the
3896    subset of REVISION1:REVISION2 in CHILD->REMAINING_RANGES at which
3897    PRIMARY_URL doesn't exist and set that subset equal to
3898    PARENT->REMAINING_RANGES' intersection with that non-existent range.  Why?
3899    Because this causes CHILD->REMAINING_RANGES to be identical to
3900    PARENT->REMAINING_RANGES for revisions between REVISION1 and REVISION2 at
3901    which PRIMARY_URL doesn't exist.  As mentioned above this means that
3902    drive_merge_report_editor() won't attempt to describe these non-existent
3903    subtree path/ranges to the reporter (which would break the merge).
3904 
3905    If the preceding paragraph wasn't terribly clear then what follows spells
3906    out this function's behavior a bit more explicitly:
3907 
3908    For forward merges (REVISION1 < REVISION2)
3909 
3910      If PRIMARY_URL@REVISION1 exists but PRIMARY_URL@REVISION2 doesn't, then
3911      find the revision 'N' in which PRIMARY_URL@REVISION1 was deleted.  Leave
3912      the subset of CHILD->REMAINING_RANGES that intersects with
3913      REVISION1:(N - 1) as-is and set the subset of CHILD->REMAINING_RANGES
3914      that intersects with (N - 1):REVISION2 equal to PARENT->REMAINING_RANGES'
3915      intersection with (N - 1):REVISION2.
3916 
3917      If PRIMARY_URL@REVISION1 doesn't exist but PRIMARY_URL@REVISION2 does,
3918      then find the revision 'M' in which PRIMARY_URL@REVISION2 came into
3919      existence.  Leave the subset of CHILD->REMAINING_RANGES that intersects with
3920      (M - 1):REVISION2 as-is and set the subset of CHILD->REMAINING_RANGES
3921      that intersects with REVISION1:(M - 1) equal to PARENT->REMAINING_RANGES'
3922      intersection with REVISION1:(M - 1).
3923 
3924    For reverse merges (REVISION1 > REVISION2)
3925 
3926      If PRIMARY_URL@REVISION1 exists but PRIMARY_URL@REVISION2 doesn't, then
3927      find the revision 'N' in which PRIMARY_URL@REVISION1 came into existence.
3928      Leave the subset of CHILD->REMAINING_RANGES that intersects with
3929      REVISION2:(N - 1) as-is and set the subset of CHILD->REMAINING_RANGES
3930      that intersects with (N - 1):REVISION1 equal to PARENT->REMAINING_RANGES'
3931      intersection with (N - 1):REVISION1.
3932 
3933      If PRIMARY_URL@REVISION1 doesn't exist but PRIMARY_URL@REVISION2 does,
3934      then find the revision 'M' in which PRIMARY_URL@REVISION2 came into
3935      existence.  Leave the subset of CHILD->REMAINING_RANGES that intersects with
3936      REVISION2:(M - 1) as-is and set the subset of CHILD->REMAINING_RANGES
3937      that intersects with (M - 1):REVISION1 equal to PARENT->REMAINING_RANGES'
3938      intersection with REVISION1:(M - 1).
3939 
3940    SCRATCH_POOL is used for all temporary allocations.  Changes to CHILD are
3941    allocated in RESULT_POOL. */
3942 static svn_error_t *
adjust_deleted_subtree_ranges(svn_client__merge_path_t * child,svn_client__merge_path_t * parent,svn_revnum_t revision1,svn_revnum_t revision2,const char * primary_url,svn_ra_session_t * ra_session,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)3943 adjust_deleted_subtree_ranges(svn_client__merge_path_t *child,
3944                               svn_client__merge_path_t *parent,
3945                               svn_revnum_t revision1,
3946                               svn_revnum_t revision2,
3947                               const char *primary_url,
3948                               svn_ra_session_t *ra_session,
3949                               svn_client_ctx_t *ctx,
3950                               apr_pool_t *result_pool,
3951                               apr_pool_t *scratch_pool)
3952 {
3953   svn_boolean_t is_rollback = revision2 < revision1;
3954   svn_revnum_t younger_rev = is_rollback ? revision1 : revision2;
3955   svn_revnum_t peg_rev = younger_rev;
3956   svn_revnum_t older_rev = is_rollback ? revision2 : revision1;
3957   apr_array_header_t *segments;
3958   svn_error_t *err;
3959 
3960   SVN_ERR_ASSERT(parent->remaining_ranges);
3961 
3962   err = svn_client__repos_location_segments(&segments, ra_session,
3963                                             primary_url, peg_rev,
3964                                             younger_rev, older_rev, ctx,
3965                                             scratch_pool);
3966 
3967   if (err)
3968     {
3969       const char *rel_source_path;  /* PRIMARY_URL relative to RA_SESSION */
3970       svn_node_kind_t kind;
3971 
3972       if (err->apr_err != SVN_ERR_FS_NOT_FOUND)
3973         return svn_error_trace(err);
3974 
3975       svn_error_clear(err);
3976 
3977       /* PRIMARY_URL@peg_rev doesn't exist.  Check if PRIMARY_URL@older_rev
3978          exists, if neither exist then the editor can simply ignore this
3979          subtree. */
3980 
3981       SVN_ERR(svn_ra_get_path_relative_to_session(
3982                 ra_session, &rel_source_path, primary_url, scratch_pool));
3983 
3984       SVN_ERR(svn_ra_check_path(ra_session, rel_source_path,
3985                                 older_rev, &kind, scratch_pool));
3986       if (kind == svn_node_none)
3987         {
3988           /* Neither PRIMARY_URL@peg_rev nor PRIMARY_URL@older_rev exist,
3989              so there is nothing to merge.  Set CHILD->REMAINING_RANGES
3990              identical to PARENT's. */
3991           child->remaining_ranges =
3992             svn_rangelist_dup(parent->remaining_ranges, scratch_pool);
3993         }
3994       else
3995         {
3996           svn_rangelist_t *deleted_rangelist;
3997           svn_revnum_t rev_primary_url_deleted;
3998 
3999           /* PRIMARY_URL@older_rev exists, so it was deleted at some
4000              revision prior to peg_rev, find that revision. */
4001           SVN_ERR(svn_ra_get_deleted_rev(ra_session, rel_source_path,
4002                                          older_rev, younger_rev,
4003                                          &rev_primary_url_deleted,
4004                                          scratch_pool));
4005 
4006           /* PRIMARY_URL@older_rev exists and PRIMARY_URL@peg_rev doesn't,
4007              so svn_ra_get_deleted_rev() should always find the revision
4008              PRIMARY_URL@older_rev was deleted. */
4009           SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev_primary_url_deleted));
4010 
4011           /* If this is a reverse merge reorder CHILD->REMAINING_RANGES and
4012              PARENT->REMAINING_RANGES so both will work with the
4013              svn_rangelist_* APIs below. */
4014           if (is_rollback)
4015             {
4016               /* svn_rangelist_reverse operates in place so it's safe
4017                  to use our scratch_pool. */
4018               SVN_ERR(svn_rangelist_reverse(child->remaining_ranges,
4019                                             scratch_pool));
4020               SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges,
4021                                             scratch_pool));
4022             }
4023 
4024           /* Find the intersection of CHILD->REMAINING_RANGES with the
4025              range over which PRIMARY_URL@older_rev exists (ending at
4026              the youngest revision at which it still exists). */
4027           SVN_ERR(rangelist_intersect_range(&child->remaining_ranges,
4028                                             child->remaining_ranges,
4029                                             older_rev,
4030                                             rev_primary_url_deleted - 1,
4031                                             FALSE,
4032                                             scratch_pool, scratch_pool));
4033 
4034           /* Merge into CHILD->REMAINING_RANGES the intersection of
4035              PARENT->REMAINING_RANGES with the range beginning when
4036              PRIMARY_URL@older_rev was deleted until younger_rev. */
4037           SVN_ERR(rangelist_intersect_range(&deleted_rangelist,
4038                                             parent->remaining_ranges,
4039                                             rev_primary_url_deleted - 1,
4040                                             peg_rev,
4041                                             FALSE,
4042                                             scratch_pool, scratch_pool));
4043           SVN_ERR(svn_rangelist_merge2(child->remaining_ranges,
4044                                        deleted_rangelist, scratch_pool,
4045                                        scratch_pool));
4046 
4047           /* Return CHILD->REMAINING_RANGES and PARENT->REMAINING_RANGES
4048              to reverse order if necessary. */
4049           if (is_rollback)
4050             {
4051               SVN_ERR(svn_rangelist_reverse(child->remaining_ranges,
4052                                             scratch_pool));
4053               SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges,
4054                                             scratch_pool));
4055             }
4056         }
4057     }
4058   else /* PRIMARY_URL@peg_rev exists. */
4059     {
4060       svn_rangelist_t *non_existent_rangelist;
4061       svn_location_segment_t *segment =
4062         APR_ARRAY_IDX(segments, (segments->nelts - 1),
4063                       svn_location_segment_t *);
4064 
4065       /* We know PRIMARY_URL@peg_rev exists as the call to
4066          svn_client__repos_location_segments() succeeded.  If there is only
4067          one segment that starts at oldest_rev then we know that
4068          PRIMARY_URL@oldest_rev:PRIMARY_URL@peg_rev describes an unbroken
4069          line of history, so there is nothing more to adjust in
4070          CHILD->REMAINING_RANGES. */
4071       if (segment->range_start == older_rev)
4072         {
4073           return SVN_NO_ERROR;
4074         }
4075 
4076       /* If this is a reverse merge reorder CHILD->REMAINING_RANGES and
4077          PARENT->REMAINING_RANGES so both will work with the
4078          svn_rangelist_* APIs below. */
4079       if (is_rollback)
4080         {
4081           SVN_ERR(svn_rangelist_reverse(child->remaining_ranges,
4082                                         scratch_pool));
4083           SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges,
4084                                         scratch_pool));
4085         }
4086 
4087       /* Intersect CHILD->REMAINING_RANGES with the range where PRIMARY_URL
4088          exists.  Since segment doesn't span older_rev:peg_rev we know
4089          PRIMARY_URL@peg_rev didn't come into existence until
4090          segment->range_start + 1. */
4091       SVN_ERR(rangelist_intersect_range(&child->remaining_ranges,
4092                                         child->remaining_ranges,
4093                                         segment->range_start, peg_rev,
4094                                         FALSE, scratch_pool, scratch_pool));
4095 
4096       /* Merge into CHILD->REMAINING_RANGES the intersection of
4097          PARENT->REMAINING_RANGES with the range before PRIMARY_URL@peg_rev
4098          came into existence. */
4099       SVN_ERR(rangelist_intersect_range(&non_existent_rangelist,
4100                                         parent->remaining_ranges,
4101                                         older_rev, segment->range_start,
4102                                         FALSE, scratch_pool, scratch_pool));
4103       SVN_ERR(svn_rangelist_merge2(child->remaining_ranges,
4104                                    non_existent_rangelist, scratch_pool,
4105                                    scratch_pool));
4106 
4107       /* Return CHILD->REMAINING_RANGES and PARENT->REMAINING_RANGES
4108          to reverse order if necessary. */
4109       if (is_rollback)
4110         {
4111           SVN_ERR(svn_rangelist_reverse(child->remaining_ranges,
4112                                         scratch_pool));
4113           SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges,
4114                                         scratch_pool));
4115         }
4116     }
4117 
4118   /* Make a lasting copy of CHILD->REMAINING_RANGES using POOL. */
4119   child->remaining_ranges = svn_rangelist_dup(child->remaining_ranges,
4120                                               result_pool);
4121   return SVN_NO_ERROR;
4122 }
4123 
4124 /* Helper for do_directory_merge().
4125 
4126    SOURCE is cascaded from the argument of the same name in
4127    do_directory_merge().  TARGET is the merge target.  RA_SESSION is the
4128    session for the younger of SOURCE->loc1 and SOURCE->loc2.
4129 
4130    Adjust the subtrees in CHILDREN_WITH_MERGEINFO so that we don't
4131    later try to describe invalid paths in drive_merge_report_editor().
4132    This function is just a thin wrapper around
4133    adjust_deleted_subtree_ranges(), which see for further details.
4134 
4135    SCRATCH_POOL is used for all temporary allocations.  Changes to
4136    CHILDREN_WITH_MERGEINFO are allocated in RESULT_POOL.
4137 */
4138 static svn_error_t *
fix_deleted_subtree_ranges(const merge_source_t * source,const merge_target_t * target,svn_ra_session_t * ra_session,apr_array_header_t * children_with_mergeinfo,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)4139 fix_deleted_subtree_ranges(const merge_source_t *source,
4140                            const merge_target_t *target,
4141                            svn_ra_session_t *ra_session,
4142                            apr_array_header_t *children_with_mergeinfo,
4143                            svn_client_ctx_t *ctx,
4144                            apr_pool_t *result_pool,
4145                            apr_pool_t *scratch_pool)
4146 {
4147   int i;
4148   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
4149   svn_boolean_t is_rollback = source->loc2->rev < source->loc1->rev;
4150 
4151   assert(session_url_is(ra_session,
4152                         (is_rollback ? source->loc1 : source->loc2)->url,
4153                         scratch_pool));
4154 
4155   /* CHILDREN_WITH_MERGEINFO is sorted in depth-first order, so
4156      start at index 1 to examine only subtrees. */
4157   for (i = 1; i < children_with_mergeinfo->nelts; i++)
4158     {
4159       svn_client__merge_path_t *child =
4160         APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
4161       svn_client__merge_path_t *parent;
4162       svn_rangelist_t *deleted_rangelist, *added_rangelist;
4163 
4164       SVN_ERR_ASSERT(child);
4165       if (child->absent)
4166         continue;
4167 
4168       svn_pool_clear(iterpool);
4169 
4170       /* Find CHILD's parent. */
4171       parent = find_nearest_ancestor(children_with_mergeinfo,
4172                                      FALSE, child->abspath);
4173 
4174       /* Since CHILD is a subtree then its parent must be in
4175          CHILDREN_WITH_MERGEINFO, see the global comment
4176          'THE CHILDREN_WITH_MERGEINFO ARRAY'. */
4177       SVN_ERR_ASSERT(parent);
4178 
4179       /* If this is a reverse merge reorder CHILD->REMAINING_RANGES
4180          so it will work with the svn_rangelist_diff API. */
4181       if (is_rollback)
4182         {
4183           SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool));
4184           SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges, iterpool));
4185         }
4186 
4187       SVN_ERR(svn_rangelist_diff(&deleted_rangelist, &added_rangelist,
4188                                  child->remaining_ranges,
4189                                  parent->remaining_ranges,
4190                                  TRUE, iterpool));
4191 
4192       if (is_rollback)
4193         {
4194           SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool));
4195           SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges, iterpool));
4196         }
4197 
4198       /* If CHILD is the merge target we then know that SOURCE is provided
4199          by normalize_merge_sources() -- see 'MERGEINFO MERGE SOURCE
4200          NORMALIZATION'.  Due to this normalization we know that SOURCE
4201          describes an unbroken line of history such that the entire range
4202          described by SOURCE can potentially be merged to CHILD.
4203 
4204          But if CHILD is a subtree we don't have the same guarantees about
4205          SOURCE as we do for the merge target.  SOURCE->loc1 and/or
4206          SOURCE->loc2 might not exist.
4207 
4208          If one or both doesn't exist, then adjust CHILD->REMAINING_RANGES
4209          such that we don't later try to describe invalid subtrees in
4210          drive_merge_report_editor(), as that will break the merge.
4211          If CHILD has the same remaining ranges as PARENT however, then
4212          there is no need to make these adjustments, since
4213          drive_merge_report_editor() won't attempt to describe CHILD in this
4214          case, see the 'Note' in drive_merge_report_editor's docstring. */
4215       if (deleted_rangelist->nelts || added_rangelist->nelts)
4216         {
4217           const char *child_primary_source_url;
4218           const char *child_repos_src_path =
4219             svn_dirent_is_child(target->abspath, child->abspath, iterpool);
4220 
4221           /* This loop is only processing subtrees, so CHILD->ABSPATH
4222              better be a proper child of the merge target. */
4223           SVN_ERR_ASSERT(child_repos_src_path);
4224 
4225           child_primary_source_url =
4226             svn_path_url_add_component2((source->loc1->rev < source->loc2->rev)
4227                                         ? source->loc2->url : source->loc1->url,
4228                                         child_repos_src_path, iterpool);
4229 
4230           SVN_ERR(adjust_deleted_subtree_ranges(child, parent,
4231                                                 source->loc1->rev,
4232                                                 source->loc2->rev,
4233                                                 child_primary_source_url,
4234                                                 ra_session,
4235                                                 ctx, result_pool, iterpool));
4236         }
4237     }
4238 
4239   svn_pool_destroy(iterpool);
4240   return SVN_NO_ERROR;
4241 }
4242 
4243 /*-----------------------------------------------------------------------*/
4244 
4245 /*** Determining What Remains To Be Merged ***/
4246 
4247 /* Get explicit and/or implicit mergeinfo for the working copy path
4248    TARGET_ABSPATH.
4249 
4250    If RECORDED_MERGEINFO is not NULL then set *RECORDED_MERGEINFO
4251    to TARGET_ABSPATH's explicit or inherited mergeinfo as dictated by
4252    INHERIT.
4253 
4254    If IMPLICIT_MERGEINFO is not NULL then set *IMPLICIT_MERGEINFO
4255    to TARGET_ABSPATH's implicit mergeinfo (a.k.a. natural history).
4256 
4257    If both RECORDED_MERGEINFO and IMPLICIT_MERGEINFO are not NULL and
4258    *RECORDED_MERGEINFO is inherited, then *IMPLICIT_MERGEINFO will be
4259    removed from *RECORDED_MERGEINFO.
4260 
4261    If INHERITED is not NULL set *INHERITED to TRUE if *RECORDED_MERGEINFO
4262    is inherited rather than explicit.  If RECORDED_MERGEINFO is NULL then
4263    INHERITED is ignored.
4264 
4265 
4266    If IMPLICIT_MERGEINFO is not NULL then START and END are limits on
4267    the natural history sought, must both be valid revision numbers, and
4268    START must be greater than END.  If TARGET_ABSPATH's base revision
4269    is older than START, then the base revision is used as the younger
4270    bound in place of START.
4271 
4272    RA_SESSION is an RA session open to the repository in which TARGET_ABSPATH
4273    lives.  It may be temporarily reparented as needed by this function.
4274 
4275    Allocate *RECORDED_MERGEINFO and *IMPLICIT_MERGEINFO in RESULT_POOL.
4276    Use SCRATCH_POOL for any temporary allocations. */
4277 static svn_error_t *
get_full_mergeinfo(svn_mergeinfo_t * recorded_mergeinfo,svn_mergeinfo_t * implicit_mergeinfo,svn_boolean_t * inherited,svn_mergeinfo_inheritance_t inherit,svn_ra_session_t * ra_session,const char * target_abspath,svn_revnum_t start,svn_revnum_t end,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)4278 get_full_mergeinfo(svn_mergeinfo_t *recorded_mergeinfo,
4279                    svn_mergeinfo_t *implicit_mergeinfo,
4280                    svn_boolean_t *inherited,
4281                    svn_mergeinfo_inheritance_t inherit,
4282                    svn_ra_session_t *ra_session,
4283                    const char *target_abspath,
4284                    svn_revnum_t start,
4285                    svn_revnum_t end,
4286                    svn_client_ctx_t *ctx,
4287                    apr_pool_t *result_pool,
4288                    apr_pool_t *scratch_pool)
4289 {
4290   /* First, we get the real mergeinfo. */
4291   if (recorded_mergeinfo)
4292     {
4293       SVN_ERR(svn_client__get_wc_or_repos_mergeinfo(recorded_mergeinfo,
4294                                                     inherited,
4295                                                     NULL /* from_repos */,
4296                                                     FALSE,
4297                                                     inherit, ra_session,
4298                                                     target_abspath,
4299                                                     ctx, result_pool));
4300     }
4301 
4302   if (implicit_mergeinfo)
4303     {
4304       svn_client__pathrev_t *target;
4305 
4306       /* Assert that we have sane input. */
4307       SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(start) && SVN_IS_VALID_REVNUM(end)
4308                      && (start > end));
4309 
4310       /* Retrieve the origin (original_*) of the node, or just the
4311          url if the node was not copied. */
4312       SVN_ERR(svn_client__wc_node_get_origin(&target, target_abspath, ctx,
4313                                              scratch_pool, scratch_pool));
4314 
4315       if (! target)
4316         {
4317           /* We've been asked to operate on a locally added target, so its
4318            * implicit mergeinfo is empty. */
4319           *implicit_mergeinfo = apr_hash_make(result_pool);
4320         }
4321       else if (target->rev <= end)
4322         {
4323           /* We're asking about a range outside our natural history
4324              altogether.  That means our implicit mergeinfo is empty. */
4325           *implicit_mergeinfo = apr_hash_make(result_pool);
4326         }
4327       else
4328         {
4329           /* Fetch so-called "implicit mergeinfo" (that is, natural
4330              history). */
4331 
4332           /* Do not ask for implicit mergeinfo from TARGET_ABSPATH's future.
4333              TARGET_ABSPATH might not even exist, and even if it does the
4334              working copy is *at* TARGET_REV so its implicit history ends
4335              at TARGET_REV! */
4336           if (target->rev < start)
4337             start = target->rev;
4338 
4339           /* Fetch the implicit mergeinfo. */
4340           SVN_ERR(svn_client__get_history_as_mergeinfo(implicit_mergeinfo,
4341                                                        NULL,
4342                                                        target, start, end,
4343                                                        ra_session, ctx,
4344                                                        result_pool));
4345         }
4346     } /*if (implicit_mergeinfo) */
4347 
4348   return SVN_NO_ERROR;
4349 }
4350 
4351 /* Helper for ensure_implicit_mergeinfo().
4352 
4353    PARENT, CHILD, REVISION1, REVISION2 and CTX
4354    are all cascaded from the arguments of the same names in
4355    ensure_implicit_mergeinfo().  PARENT and CHILD must both exist, i.e.
4356    this function should never be called where CHILD is the merge target.
4357 
4358    If PARENT->IMPLICIT_MERGEINFO is NULL, obtain it from the server.
4359 
4360    Set CHILD->IMPLICIT_MERGEINFO to the mergeinfo inherited from
4361    PARENT->IMPLICIT_MERGEINFO.  CHILD->IMPLICIT_MERGEINFO is allocated
4362    in RESULT_POOL.
4363 
4364    RA_SESSION is an RA session open to the repository that contains CHILD.
4365    It may be temporarily reparented by this function.
4366    */
4367 static svn_error_t *
inherit_implicit_mergeinfo_from_parent(svn_client__merge_path_t * parent,svn_client__merge_path_t * child,svn_revnum_t revision1,svn_revnum_t revision2,svn_ra_session_t * ra_session,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)4368 inherit_implicit_mergeinfo_from_parent(svn_client__merge_path_t *parent,
4369                                        svn_client__merge_path_t *child,
4370                                        svn_revnum_t revision1,
4371                                        svn_revnum_t revision2,
4372                                        svn_ra_session_t *ra_session,
4373                                        svn_client_ctx_t *ctx,
4374                                        apr_pool_t *result_pool,
4375                                        apr_pool_t *scratch_pool)
4376 {
4377   const char *path_diff;
4378 
4379   /* This only works on subtrees! */
4380   SVN_ERR_ASSERT(parent);
4381   SVN_ERR_ASSERT(child);
4382 
4383   /* While PARENT must exist, it is possible we've deferred
4384      getting its implicit mergeinfo.  If so get it now. */
4385   if (!parent->implicit_mergeinfo)
4386     SVN_ERR(get_full_mergeinfo(NULL, &(parent->implicit_mergeinfo),
4387                                NULL, svn_mergeinfo_inherited,
4388                                ra_session, child->abspath,
4389                                MAX(revision1, revision2),
4390                                MIN(revision1, revision2),
4391                                ctx, result_pool, scratch_pool));
4392 
4393   /* Let CHILD inherit PARENT's implicit mergeinfo. */
4394 
4395   path_diff = svn_dirent_is_child(parent->abspath, child->abspath,
4396                                   scratch_pool);
4397   /* PARENT->PATH better be an ancestor of CHILD->ABSPATH! */
4398   SVN_ERR_ASSERT(path_diff);
4399 
4400   SVN_ERR(svn_mergeinfo__add_suffix_to_mergeinfo(
4401             &child->implicit_mergeinfo, parent->implicit_mergeinfo,
4402             path_diff, result_pool, scratch_pool));
4403   child->implicit_mergeinfo = svn_mergeinfo_dup(child->implicit_mergeinfo,
4404                                                 result_pool);
4405   return SVN_NO_ERROR;
4406 }
4407 
4408 /* Helper of filter_merged_revisions().
4409 
4410    If we have deferred obtaining CHILD->IMPLICIT_MERGEINFO, then get
4411    it now, allocating it in RESULT_POOL.  If CHILD_INHERITS_PARENT is true
4412    then set CHILD->IMPLICIT_MERGEINFO to the mergeinfo inherited from
4413    PARENT->IMPLICIT_MERGEINFO, otherwise contact the repository.  Use
4414    SCRATCH_POOL for all temporary allocations.
4415 
4416    RA_SESSION is an RA session open to the repository that contains CHILD.
4417    It may be temporarily reparented by this function.
4418 
4419    PARENT, CHILD, REVISION1, REVISION2 and
4420    CTX are all cascaded from the arguments of the same name in
4421    filter_merged_revisions() and the same conditions for that function
4422    hold here. */
4423 static svn_error_t *
ensure_implicit_mergeinfo(svn_client__merge_path_t * parent,svn_client__merge_path_t * child,svn_boolean_t child_inherits_parent,svn_revnum_t revision1,svn_revnum_t revision2,svn_ra_session_t * ra_session,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)4424 ensure_implicit_mergeinfo(svn_client__merge_path_t *parent,
4425                           svn_client__merge_path_t *child,
4426                           svn_boolean_t child_inherits_parent,
4427                           svn_revnum_t revision1,
4428                           svn_revnum_t revision2,
4429                           svn_ra_session_t *ra_session,
4430                           svn_client_ctx_t *ctx,
4431                           apr_pool_t *result_pool,
4432                           apr_pool_t *scratch_pool)
4433 {
4434   /* If we haven't already found CHILD->IMPLICIT_MERGEINFO then
4435      contact the server to get it. */
4436 
4437   if (child->implicit_mergeinfo)
4438     return SVN_NO_ERROR;
4439 
4440   if (child_inherits_parent)
4441     SVN_ERR(inherit_implicit_mergeinfo_from_parent(parent,
4442                                                    child,
4443                                                    revision1,
4444                                                    revision2,
4445                                                    ra_session,
4446                                                    ctx,
4447                                                    result_pool,
4448                                                    scratch_pool));
4449   else
4450     SVN_ERR(get_full_mergeinfo(NULL,
4451                                &(child->implicit_mergeinfo),
4452                                NULL, svn_mergeinfo_inherited,
4453                                ra_session, child->abspath,
4454                                MAX(revision1, revision2),
4455                                MIN(revision1, revision2),
4456                                ctx, result_pool, scratch_pool));
4457 
4458   return SVN_NO_ERROR;
4459 }
4460 
4461 /* Helper for calculate_remaining_ranges().
4462 
4463    Initialize CHILD->REMAINING_RANGES to a rangelist representing the
4464    requested merge of REVISION1:REVISION2 from MERGEINFO_PATH to
4465    CHILD->ABSPATH.
4466 
4467    For forward merges remove any ranges from CHILD->REMAINING_RANGES that
4468    have already been merged to CHILD->ABSPATH per TARGET_MERGEINFO or
4469    CHILD->IMPLICIT_MERGEINFO.  For reverse merges remove any ranges from
4470    CHILD->REMAINING_RANGES that have not already been merged to CHILD->ABSPATH
4471    per TARGET_MERGEINFO or CHILD->IMPLICIT_MERGEINFO.  If we have deferred
4472    obtaining CHILD->IMPLICIT_MERGEINFO and it is necessary to use it for
4473    these calculations, then get it from the server, allocating it in
4474    RESULT_POOL.
4475 
4476    CHILD represents a working copy path which is the merge target or one of
4477    the target's subtrees.  If not NULL, PARENT is CHILD's nearest path-wise
4478    ancestor - see 'THE CHILDREN_WITH_MERGEINFO ARRAY'.
4479 
4480    If the function needs to consider CHILD->IMPLICIT_MERGEINFO and
4481    CHILD_INHERITS_IMPLICIT is true, then set CHILD->IMPLICIT_MERGEINFO to the
4482    mergeinfo inherited from PARENT->IMPLICIT_MERGEINFO.  Otherwise contact
4483    the repository for CHILD->IMPLICIT_MERGEINFO.
4484 
4485    NOTE: If PARENT is present then this function must have previously been
4486    called for PARENT, i.e. if populate_remaining_ranges() is calling this
4487    function for a set of svn_client__merge_path_t* the calls must be made
4488    in depth-first order.
4489 
4490    MERGEINFO_PATH is the merge source relative to the repository root.
4491 
4492    REVISION1 and REVISION2 describe the merge range requested from
4493    MERGEINFO_PATH.
4494 
4495    TARGET_RANGELIST is the portion of CHILD->ABSPATH's explicit or inherited
4496    mergeinfo that intersects with the merge history described by
4497    MERGEINFO_PATH@REVISION1:MERGEINFO_PATH@REVISION2.  TARGET_RANGELIST
4498    should be NULL if there is no explicit or inherited mergeinfo on
4499    CHILD->ABSPATH or an empty list if CHILD->ABSPATH has empty mergeinfo or
4500    explicit mergeinfo that exclusively describes non-intersecting history
4501    with MERGEINFO_PATH@REVISION1:MERGEINFO_PATH@REVISION2.
4502 
4503    SCRATCH_POOL is used for all temporary allocations.
4504 
4505    NOTE: This should only be called when honoring mergeinfo.
4506 
4507    NOTE: Like calculate_remaining_ranges() if PARENT is present then this
4508    function must have previously been called for PARENT.
4509 */
4510 static svn_error_t *
filter_merged_revisions(svn_client__merge_path_t * parent,svn_client__merge_path_t * child,const char * mergeinfo_path,svn_rangelist_t * target_rangelist,svn_revnum_t revision1,svn_revnum_t revision2,svn_boolean_t child_inherits_implicit,svn_ra_session_t * ra_session,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)4511 filter_merged_revisions(svn_client__merge_path_t *parent,
4512                         svn_client__merge_path_t *child,
4513                         const char *mergeinfo_path,
4514                         svn_rangelist_t *target_rangelist,
4515                         svn_revnum_t revision1,
4516                         svn_revnum_t revision2,
4517                         svn_boolean_t child_inherits_implicit,
4518                         svn_ra_session_t *ra_session,
4519                         svn_client_ctx_t *ctx,
4520                         apr_pool_t *result_pool,
4521                         apr_pool_t *scratch_pool)
4522 {
4523   svn_rangelist_t *requested_rangelist,
4524     *target_implicit_rangelist, *explicit_rangelist;
4525 
4526   /* Convert REVISION1 and REVISION2 to a rangelist.
4527 
4528      Note: Talking about a requested merge range's inheritability
4529      doesn't make much sense, but as we are using svn_merge_range_t
4530      to describe it we need to pick *something*.  Since all the
4531      rangelist manipulations in this function either don't consider
4532      inheritance by default or we are requesting that they don't (i.e.
4533      svn_rangelist_remove and svn_rangelist_intersect) then we could
4534      set the inheritability as FALSE, it won't matter either way. */
4535   requested_rangelist = svn_rangelist__initialize(revision1, revision2,
4536                                                   TRUE, scratch_pool);
4537 
4538   /* Now filter out revisions that have already been merged to CHILD. */
4539 
4540   if (revision1 > revision2) /* This is a reverse merge. */
4541     {
4542       svn_rangelist_t *added_rangelist, *deleted_rangelist;
4543 
4544       /* The revert range and will need to be reversed for
4545          our svn_rangelist_* APIs to work properly. */
4546       SVN_ERR(svn_rangelist_reverse(requested_rangelist, scratch_pool));
4547 
4548       /* Set EXPLICIT_RANGELIST to the list of source-range revs that are
4549          already recorded as merged to target. */
4550       if (target_rangelist)
4551         {
4552           /* Return the intersection of the revs which are both already
4553              represented by CHILD's explicit or inherited mergeinfo.
4554 
4555              We don't consider inheritance when determining intersecting
4556              ranges.  If we *did* consider inheritance, then our calculation
4557              would be wrong.  For example, if the CHILD->REMAINING_RANGES is
4558              5:3 and TARGET_RANGELIST is r5* (non-inheritable) then the
4559              intersection would be r4.  And that would be wrong as we clearly
4560              want to reverse merge both r4 and r5 in this case.  Ignoring the
4561              ranges' inheritance results in an intersection of r4-5.
4562 
4563              You might be wondering about CHILD's children, doesn't the above
4564              imply that we will reverse merge r4-5 from them?  Nope, this is
4565              safe to do because any path whose parent has non-inheritable
4566              ranges is always considered a subtree with differing mergeinfo
4567              even if that path has no explicit mergeinfo prior to the
4568              merge -- See condition 3 in the doc string for
4569              merge.c:get_mergeinfo_paths(). */
4570           SVN_ERR(svn_rangelist_intersect(&explicit_rangelist,
4571                                           target_rangelist,
4572                                           requested_rangelist,
4573                                           FALSE, scratch_pool));
4574         }
4575       else
4576         {
4577           explicit_rangelist =
4578             apr_array_make(result_pool, 0, sizeof(svn_merge_range_t *));
4579         }
4580 
4581       /* Was any part of the requested reverse merge not accounted for in
4582          CHILD's explicit or inherited mergeinfo? */
4583       SVN_ERR(svn_rangelist_diff(&deleted_rangelist, &added_rangelist,
4584                                  requested_rangelist, explicit_rangelist,
4585                                  FALSE, scratch_pool));
4586 
4587       if (deleted_rangelist->nelts == 0)
4588         {
4589           /* The whole of REVISION1:REVISION2 was represented in CHILD's
4590              explicit/inherited mergeinfo, allocate CHILD's remaining
4591              ranges in POOL and then we are done. */
4592           SVN_ERR(svn_rangelist_reverse(requested_rangelist, scratch_pool));
4593           child->remaining_ranges = svn_rangelist_dup(requested_rangelist,
4594                                                       result_pool);
4595         }
4596       else /* We need to check CHILD's implicit mergeinfo. */
4597         {
4598           svn_rangelist_t *implicit_rangelist;
4599 
4600           SVN_ERR(ensure_implicit_mergeinfo(parent,
4601                                             child,
4602                                             child_inherits_implicit,
4603                                             revision1,
4604                                             revision2,
4605                                             ra_session,
4606                                             ctx,
4607                                             result_pool,
4608                                             scratch_pool));
4609 
4610           target_implicit_rangelist = svn_hash_gets(child->implicit_mergeinfo,
4611                                                     mergeinfo_path);
4612 
4613           if (target_implicit_rangelist)
4614             SVN_ERR(svn_rangelist_intersect(&implicit_rangelist,
4615                                             target_implicit_rangelist,
4616                                             requested_rangelist,
4617                                             FALSE, scratch_pool));
4618           else
4619             implicit_rangelist = apr_array_make(scratch_pool, 0,
4620                                                 sizeof(svn_merge_range_t *));
4621 
4622           SVN_ERR(svn_rangelist_merge2(implicit_rangelist,
4623                                        explicit_rangelist, scratch_pool,
4624                                        scratch_pool));
4625           SVN_ERR(svn_rangelist_reverse(implicit_rangelist, scratch_pool));
4626           child->remaining_ranges = svn_rangelist_dup(implicit_rangelist,
4627                                                       result_pool);
4628         }
4629     }
4630   else /* This is a forward merge */
4631     {
4632       /* Set EXPLICIT_RANGELIST to the list of source-range revs that are
4633          NOT already recorded as merged to target. */
4634       if (target_rangelist)
4635         {
4636           /* See earlier comment preceding svn_rangelist_intersect() for
4637              why we don't consider inheritance here. */
4638           SVN_ERR(svn_rangelist_remove(&explicit_rangelist,
4639                                        target_rangelist,
4640                                        requested_rangelist, FALSE,
4641                                        scratch_pool));
4642         }
4643       else
4644         {
4645           explicit_rangelist = svn_rangelist_dup(requested_rangelist,
4646                                                  scratch_pool);
4647         }
4648 
4649       if (explicit_rangelist->nelts == 0)
4650         {
4651           child->remaining_ranges =
4652             apr_array_make(result_pool, 0, sizeof(svn_merge_range_t *));
4653         }
4654       else
4655 /* ### TODO:  Which evil shall we choose?
4656    ###
4657    ### If we allow all forward-merges not already found in recorded
4658    ### mergeinfo, we destroy the ability to, say, merge the whole of a
4659    ### branch to the trunk while automatically ignoring the revisions
4660    ### common to both.  That's bad.
4661    ###
4662    ### If we allow only forward-merges not found in either recorded
4663    ### mergeinfo or implicit mergeinfo (natural history), then the
4664    ### previous scenario works great, but we can't reverse-merge a
4665    ### previous change made to our line of history and then remake it
4666    ### (because the reverse-merge will leave no mergeinfo trace, and
4667    ### the remake-it attempt will still find the original change in
4668    ### natural mergeinfo.  But you know, that we happen to use 'merge'
4669    ### for revision undoing is somewhat unnatural anyway, so I'm
4670    ### finding myself having little interest in caring too much about
4671    ### this.  That said, if we had a way of storing reverse merge
4672    ### ranges, we'd be in good shape either way.
4673 */
4674 #ifdef SVN_MERGE__ALLOW_ALL_FORWARD_MERGES_FROM_SELF
4675         {
4676           /* ### Don't consider implicit mergeinfo. */
4677           child->remaining_ranges = svn_rangelist_dup(explicit_rangelist,
4678                                                       pool);
4679         }
4680 #else
4681         {
4682           /* Based on CHILD's TARGET_MERGEINFO there are ranges to merge.
4683              Check CHILD's implicit mergeinfo to see if these remaining
4684              ranges are represented there. */
4685           SVN_ERR(ensure_implicit_mergeinfo(parent,
4686                                             child,
4687                                             child_inherits_implicit,
4688                                             revision1,
4689                                             revision2,
4690                                             ra_session,
4691                                             ctx,
4692                                             result_pool,
4693                                             scratch_pool));
4694 
4695           target_implicit_rangelist = svn_hash_gets(child->implicit_mergeinfo,
4696                                                     mergeinfo_path);
4697           if (target_implicit_rangelist)
4698             SVN_ERR(svn_rangelist_remove(&(child->remaining_ranges),
4699                                          target_implicit_rangelist,
4700                                          explicit_rangelist,
4701                                          FALSE, result_pool));
4702           else
4703             child->remaining_ranges = svn_rangelist_dup(explicit_rangelist,
4704                                                         result_pool);
4705         }
4706 #endif
4707     }
4708 
4709   return SVN_NO_ERROR;
4710 }
4711 
4712 /* Helper for do_file_merge and do_directory_merge (by way of
4713    populate_remaining_ranges() for the latter).
4714 
4715    Determine what portions of SOURCE have already
4716    been merged to CHILD->ABSPATH and populate CHILD->REMAINING_RANGES with
4717    the ranges that still need merging.
4718 
4719    SOURCE and CTX are all cascaded from the caller's arguments of the same
4720    names.  Note that this means SOURCE adheres to the requirements noted in
4721    `MERGEINFO MERGE SOURCE NORMALIZATION'.
4722 
4723    CHILD represents a working copy path which is the merge target or one of
4724    the target's subtrees.  If not NULL, PARENT is CHILD's nearest path-wise
4725    ancestor - see 'THE CHILDREN_WITH_MERGEINFO ARRAY'.  TARGET_MERGEINFO is
4726    the working mergeinfo on CHILD.
4727 
4728    RA_SESSION is the session for the younger of SOURCE->loc1 and
4729    SOURCE->loc2.
4730 
4731    If the function needs to consider CHILD->IMPLICIT_MERGEINFO and
4732    CHILD_INHERITS_IMPLICIT is true, then set CHILD->IMPLICIT_MERGEINFO to the
4733    mergeinfo inherited from PARENT->IMPLICIT_MERGEINFO.  Otherwise contact
4734    the repository for CHILD->IMPLICIT_MERGEINFO.
4735 
4736    If not null, IMPLICIT_SRC_GAP is the gap, if any, in the natural history
4737    of SOURCE, see merge_cmd_baton_t.implicit_src_gap.
4738 
4739    SCRATCH_POOL is used for all temporary allocations.  Changes to CHILD and
4740    PARENT are made in RESULT_POOL.
4741 
4742    NOTE: This should only be called when honoring mergeinfo.
4743 
4744    NOTE: If PARENT is present then this function must have previously been
4745    called for PARENT, i.e. if populate_remaining_ranges() is calling this
4746    function for a set of svn_client__merge_path_t* the calls must be made
4747    in depth-first order.
4748 
4749    NOTE: When performing reverse merges, return
4750    SVN_ERR_CLIENT_NOT_READY_TO_MERGE if both locations in SOURCE and
4751    CHILD->ABSPATH are all on the same line of history but CHILD->ABSPATH's
4752    base revision is older than the SOURCE->rev1:rev2 range, see comment re
4753    issue #2973 below.
4754 */
4755 static svn_error_t *
calculate_remaining_ranges(svn_client__merge_path_t * parent,svn_client__merge_path_t * child,const merge_source_t * source,svn_mergeinfo_t target_mergeinfo,const apr_array_header_t * implicit_src_gap,svn_boolean_t child_inherits_implicit,svn_ra_session_t * ra_session,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)4756 calculate_remaining_ranges(svn_client__merge_path_t *parent,
4757                            svn_client__merge_path_t *child,
4758                            const merge_source_t *source,
4759                            svn_mergeinfo_t target_mergeinfo,
4760                            const apr_array_header_t *implicit_src_gap,
4761                            svn_boolean_t child_inherits_implicit,
4762                            svn_ra_session_t *ra_session,
4763                            svn_client_ctx_t *ctx,
4764                            apr_pool_t *result_pool,
4765                            apr_pool_t *scratch_pool)
4766 {
4767   const svn_client__pathrev_t *primary_src
4768     = (source->loc1->rev < source->loc2->rev) ? source->loc2 : source->loc1;
4769   const char *mergeinfo_path = svn_client__pathrev_fspath(primary_src,
4770                                                           scratch_pool);
4771   /* Intersection of TARGET_MERGEINFO and the merge history
4772      described by SOURCE. */
4773   svn_rangelist_t *target_rangelist;
4774   svn_revnum_t child_base_revision;
4775 
4776   /* Since this function should only be called when honoring mergeinfo and
4777    * SOURCE adheres to the requirements noted in 'MERGEINFO MERGE SOURCE
4778    * NORMALIZATION', SOURCE must be 'ancestral'. */
4779   SVN_ERR_ASSERT(source->ancestral);
4780 
4781   /* Determine which of the requested ranges to consider merging... */
4782 
4783   /* Set TARGET_RANGELIST to the portion of TARGET_MERGEINFO that refers
4784      to SOURCE (excluding any gap in SOURCE): first get all ranges from
4785      TARGET_MERGEINFO that refer to the path of SOURCE, and then prune
4786      any ranges that lie in the gap in SOURCE.
4787 
4788      ### [JAF] In fact, that may still leave some ranges that lie entirely
4789      outside the range of SOURCE; it seems we don't care about that.  */
4790   if (target_mergeinfo)
4791     target_rangelist = svn_hash_gets(target_mergeinfo, mergeinfo_path);
4792   else
4793     target_rangelist = NULL;
4794   if (implicit_src_gap && target_rangelist)
4795     {
4796       /* Remove any mergeinfo referring to the 'gap' in SOURCE, as that
4797          mergeinfo doesn't really refer to SOURCE at all but instead
4798          refers to locations that are non-existent or on a different
4799          line of history.  (Issue #3242.) */
4800       SVN_ERR(svn_rangelist_remove(&target_rangelist,
4801                                    implicit_src_gap, target_rangelist,
4802                                    FALSE, result_pool));
4803     }
4804 
4805   /* Initialize CHILD->REMAINING_RANGES and filter out revisions already
4806      merged (or, in the case of reverse merges, ranges not yet merged). */
4807   SVN_ERR(filter_merged_revisions(parent, child, mergeinfo_path,
4808                                   target_rangelist,
4809                                   source->loc1->rev, source->loc2->rev,
4810                                   child_inherits_implicit,
4811                                   ra_session, ctx, result_pool,
4812                                   scratch_pool));
4813 
4814   /* Issue #2973 -- from the continuing series of "Why, since the advent of
4815      merge tracking, allowing merges into mixed rev and locally modified
4816      working copies isn't simple and could be considered downright evil".
4817 
4818      If reverse merging a range to the WC path represented by CHILD, from
4819      that path's own history, where the path inherits no locally modified
4820      mergeinfo from its WC parents (i.e. there is no uncommitted merge to
4821      the WC), and the path's base revision is older than the range, then
4822      the merge will always be a no-op.  This is because we only allow reverse
4823      merges of ranges in the path's explicit or natural mergeinfo and a
4824      reverse merge from the path's future history obviously isn't going to be
4825      in either, hence the no-op.
4826 
4827      The problem is two-fold.  First, in a mixed rev WC, the change we
4828      want to revert might actually be to some child of the target path
4829      which is at a younger base revision.  Sure, we can merge directly
4830      to that child or update the WC or even use --ignore-ancestry and then
4831      successfully run the reverse merge, but that gets to the second
4832      problem: Those courses of action are not very obvious.  Before 1.5 if
4833      a user committed a change that didn't touch the commit target, then
4834      immediately decided to revert that change via a reverse merge it would
4835      just DTRT.  But with the advent of merge tracking the user gets a no-op.
4836 
4837      So in the name of user friendliness, return an error suggesting a helpful
4838      course of action.
4839   */
4840   SVN_ERR(svn_wc__node_get_base(NULL, &child_base_revision,
4841                                 NULL, NULL, NULL, NULL,
4842                                 ctx->wc_ctx, child->abspath,
4843                                 TRUE /* ignore_enoent */,
4844                                 scratch_pool, scratch_pool));
4845   /* If CHILD has no base revision then it hasn't been committed yet, so it
4846      can't have any "future" history. */
4847   if (SVN_IS_VALID_REVNUM(child_base_revision)
4848       && ((child->remaining_ranges)->nelts == 0) /* Inoperative merge */
4849       && (source->loc2->rev < source->loc1->rev)     /* Reverse merge */
4850       && (child_base_revision <= source->loc2->rev))  /* From CHILD's future */
4851     {
4852       /* Hmmm, an inoperative reverse merge from the "future".  If it is
4853          from our own future return a helpful error. */
4854       svn_error_t *err;
4855       svn_client__pathrev_t *start_loc;
4856 
4857       err = svn_client__repos_location(&start_loc,
4858                                        ra_session,
4859                                        source->loc1,
4860                                        child_base_revision,
4861                                        ctx, scratch_pool, scratch_pool);
4862       if (err)
4863         {
4864           if (err->apr_err == SVN_ERR_FS_NOT_FOUND
4865               || err->apr_err == SVN_ERR_CLIENT_UNRELATED_RESOURCES)
4866             svn_error_clear(err);
4867           else
4868             return svn_error_trace(err);
4869         }
4870       else
4871         {
4872           const char *url;
4873 
4874           SVN_ERR(svn_wc__node_get_url(&url, ctx->wc_ctx, child->abspath,
4875                                        scratch_pool, scratch_pool));
4876           if (strcmp(start_loc->url, url) == 0)
4877             return svn_error_create(SVN_ERR_CLIENT_MERGE_UPDATE_REQUIRED, NULL,
4878                                     _("Cannot reverse-merge a range from a "
4879                                       "path's own future history; try "
4880                                       "updating first"));
4881         }
4882     }
4883 
4884   return SVN_NO_ERROR;
4885 }
4886 
4887 /* Helper for populate_remaining_ranges().
4888 
4889    SOURCE is cascaded from the arguments of the same name in
4890    populate_remaining_ranges().
4891 
4892    Note: The following comments assume a forward merge, i.e.
4893    SOURCE->loc1->rev < SOURCE->loc2->rev.  If this is a reverse merge then
4894    all the following comments still apply, but with SOURCE->loc1 switched
4895    with SOURCE->loc2.
4896 
4897    Like populate_remaining_ranges(), SOURCE must adhere to the restrictions
4898    documented in 'MERGEINFO MERGE SOURCE NORMALIZATION'.  These restrictions
4899    allow for a *single* gap in SOURCE, GAP_REV1:GAP_REV2 exclusive:inclusive
4900    (where SOURCE->loc1->rev == GAP_REV1 <= GAP_REV2 < SOURCE->loc2->rev),
4901    if SOURCE->loc2->url@(GAP_REV2+1) was copied from SOURCE->loc1.  If such
4902    a gap exists, set *GAP_START and *GAP_END to the starting and ending
4903    revisions of the gap.  Otherwise set both to SVN_INVALID_REVNUM.
4904 
4905    For example, if the natural history of URL@2:URL@9 is 'trunk/:2,7-9' this
4906    would indicate that trunk@7 was copied from trunk@2.  This function would
4907    return GAP_START:GAP_END of 2:6 in this case.  Note that a path 'trunk'
4908    might exist at r3-6, but it would not be on the same line of history as
4909    trunk@9.
4910 
4911    ### GAP_START is basically redundant, as (if there is a gap at all) it is
4912    necessarily the older revision of SOURCE.
4913 
4914    RA_SESSION is an open RA session to the repository in which SOURCE lives.
4915 */
4916 static svn_error_t *
find_gaps_in_merge_source_history(svn_revnum_t * gap_start,svn_revnum_t * gap_end,const merge_source_t * source,svn_ra_session_t * ra_session,svn_client_ctx_t * ctx,apr_pool_t * scratch_pool)4917 find_gaps_in_merge_source_history(svn_revnum_t *gap_start,
4918                                   svn_revnum_t *gap_end,
4919                                   const merge_source_t *source,
4920                                   svn_ra_session_t *ra_session,
4921                                   svn_client_ctx_t *ctx,
4922                                   apr_pool_t *scratch_pool)
4923 {
4924   svn_mergeinfo_t implicit_src_mergeinfo;
4925   svn_revnum_t old_rev = MIN(source->loc1->rev, source->loc2->rev);
4926   const svn_client__pathrev_t *primary_src
4927     = (source->loc1->rev < source->loc2->rev) ? source->loc2 : source->loc1;
4928   const char *merge_src_fspath = svn_client__pathrev_fspath(primary_src,
4929                                                             scratch_pool);
4930   svn_rangelist_t *rangelist;
4931 
4932   SVN_ERR_ASSERT(source->ancestral);
4933 
4934   /* Start by assuming there is no gap. */
4935   *gap_start = *gap_end = SVN_INVALID_REVNUM;
4936 
4937   /* Easy out: There can't be a gap between adjacent revisions. */
4938   if (labs(source->loc1->rev - source->loc2->rev) == 1)
4939     return SVN_NO_ERROR;
4940 
4941   /* Get SOURCE as mergeinfo. */
4942   SVN_ERR(svn_client__get_history_as_mergeinfo(&implicit_src_mergeinfo, NULL,
4943                                                primary_src,
4944                                                primary_src->rev, old_rev,
4945                                                ra_session,
4946                                                ctx, scratch_pool));
4947 
4948   rangelist = svn_hash_gets(implicit_src_mergeinfo, merge_src_fspath);
4949 
4950   if (!rangelist) /* ### Can we ever not find a rangelist? */
4951     return SVN_NO_ERROR;
4952 
4953   /* A gap in natural history can result from either a copy or
4954      a rename.  If from a copy then history as mergeinfo will look
4955      something like this:
4956 
4957        '/trunk:X,Y-Z'
4958 
4959      If from a rename it will look like this:
4960 
4961        '/trunk_old_name:X'
4962        '/trunk_new_name:Y-Z'
4963 
4964     In both cases the gap, if it exists, is M-N, where M = X + 1 and
4965     N = Y - 1.
4966 
4967     Note that per the rules of 'MERGEINFO MERGE SOURCE NORMALIZATION' we
4968     should never have multiple gaps, e.g. if we see anything like the
4969     following then something is quite wrong:
4970 
4971         '/trunk_old_name:A,B-C'
4972         '/trunk_new_name:D-E'
4973   */
4974 
4975   if (rangelist->nelts > 1) /* Copy */
4976     {
4977       const svn_merge_range_t *gap;
4978       /* As mentioned above, multiple gaps *shouldn't* be possible. */
4979       SVN_ERR_ASSERT(apr_hash_count(implicit_src_mergeinfo) == 1);
4980 
4981       gap = APR_ARRAY_IDX(rangelist, rangelist->nelts - 1,
4982                           const svn_merge_range_t *);
4983 
4984       *gap_start = MIN(source->loc1->rev, source->loc2->rev);
4985       *gap_end = gap->start;
4986 
4987       /* ### Issue #4132:
4988          ### This assertion triggers in merge_tests.py svnmucc_abuse_1()
4989          ### when a node is replaced by an older copy of itself.
4990 
4991          BH: I think we should review this and the 'rename' case to find
4992              out which behavior we really want, and if we can really
4993              determine what happened this way. */
4994       SVN_ERR_ASSERT(*gap_start < *gap_end);
4995     }
4996   else if (apr_hash_count(implicit_src_mergeinfo) > 1) /* Rename */
4997     {
4998       svn_rangelist_t *requested_rangelist =
4999         svn_rangelist__initialize(MIN(source->loc1->rev, source->loc2->rev),
5000                                   MAX(source->loc1->rev, source->loc2->rev),
5001                                   TRUE, scratch_pool);
5002       svn_rangelist_t *implicit_rangelist =
5003         apr_array_make(scratch_pool, 2, sizeof(svn_merge_range_t *));
5004       svn_rangelist_t *gap_rangelist;
5005 
5006       SVN_ERR(svn_rangelist__merge_many(implicit_rangelist,
5007                                         implicit_src_mergeinfo,
5008                                         scratch_pool, scratch_pool));
5009       SVN_ERR(svn_rangelist_remove(&gap_rangelist, implicit_rangelist,
5010                                    requested_rangelist, FALSE,
5011                                    scratch_pool));
5012 
5013       /* If there is anything left it is the gap. */
5014       if (gap_rangelist->nelts)
5015         {
5016           svn_merge_range_t *gap_range =
5017             APR_ARRAY_IDX(gap_rangelist, 0, svn_merge_range_t *);
5018 
5019           *gap_start = gap_range->start;
5020           *gap_end = gap_range->end;
5021         }
5022     }
5023 
5024   SVN_ERR_ASSERT(*gap_start == MIN(source->loc1->rev, source->loc2->rev)
5025                  || (*gap_start == SVN_INVALID_REVNUM
5026                      && *gap_end == SVN_INVALID_REVNUM));
5027   return SVN_NO_ERROR;
5028 }
5029 
5030 /* Helper for do_directory_merge().
5031 
5032    For each (svn_client__merge_path_t *) child in CHILDREN_WITH_MERGEINFO,
5033    populate that child's 'remaining_ranges' list with (### ... what?),
5034    and populate that child's 'implicit_mergeinfo' with its implicit
5035    mergeinfo (natural history).  CHILDREN_WITH_MERGEINFO is expected
5036    to be sorted in depth first order and each child must be processed in
5037    that order.  The inheritability of all calculated ranges is TRUE.
5038 
5039    If mergeinfo is being honored (based on MERGE_B -- see HONOR_MERGEINFO()
5040    for how this is determined), this function will actually try to be
5041    intelligent about populating remaining_ranges list.  Otherwise, it
5042    will claim that each child has a single remaining range, from
5043    SOURCE->rev1, to SOURCE->rev2.
5044    ### We also take the short-cut if doing record-only.  Why?
5045 
5046    SCRATCH_POOL is used for all temporary allocations.  Changes to
5047    CHILDREN_WITH_MERGEINFO are made in RESULT_POOL.
5048 
5049    Note that if SOURCE->rev1 > SOURCE->rev2, then each child's remaining_ranges
5050    member does not adhere to the API rules for rangelists described in
5051    svn_mergeinfo.h -- See svn_client__merge_path_t.
5052 
5053    See `MERGEINFO MERGE SOURCE NORMALIZATION' for more requirements
5054    around SOURCE.
5055 */
5056 static svn_error_t *
populate_remaining_ranges(apr_array_header_t * children_with_mergeinfo,const merge_source_t * source,svn_ra_session_t * ra_session,merge_cmd_baton_t * merge_b,apr_pool_t * result_pool,apr_pool_t * scratch_pool)5057 populate_remaining_ranges(apr_array_header_t *children_with_mergeinfo,
5058                           const merge_source_t *source,
5059                           svn_ra_session_t *ra_session,
5060                           merge_cmd_baton_t *merge_b,
5061                           apr_pool_t *result_pool,
5062                           apr_pool_t *scratch_pool)
5063 {
5064   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
5065   int i;
5066   svn_revnum_t gap_start, gap_end;
5067 
5068   /* If we aren't honoring mergeinfo or this is a --record-only merge,
5069      we'll make quick work of this by simply adding dummy SOURCE->rev1:rev2
5070      ranges for all children. */
5071   if (! HONOR_MERGEINFO(merge_b) || merge_b->record_only)
5072     {
5073       for (i = 0; i < children_with_mergeinfo->nelts; i++)
5074         {
5075           svn_client__merge_path_t *child =
5076             APR_ARRAY_IDX(children_with_mergeinfo, i,
5077                           svn_client__merge_path_t *);
5078 
5079           svn_pool_clear(iterpool);
5080 
5081           /* Issue #3646 'record-only merges create self-referential
5082              mergeinfo'.  Get the merge target's implicit mergeinfo (natural
5083              history).  We'll use it later to avoid setting self-referential
5084              mergeinfo -- see filter_natural_history_from_mergeinfo(). */
5085           if (i == 0) /* First item is always the merge target. */
5086             {
5087               SVN_ERR(get_full_mergeinfo(NULL, /* child->pre_merge_mergeinfo */
5088                                          &(child->implicit_mergeinfo),
5089                                          NULL, /* child->inherited_mergeinfo */
5090                                          svn_mergeinfo_inherited, ra_session,
5091                                          child->abspath,
5092                                          MAX(source->loc1->rev,
5093                                              source->loc2->rev),
5094                                          MIN(source->loc1->rev,
5095                                              source->loc2->rev),
5096                                          merge_b->ctx, result_pool,
5097                                          iterpool));
5098             }
5099           else
5100             {
5101               /* Issue #3443 - Subtrees of the merge target can inherit
5102                  their parent's implicit mergeinfo in most cases. */
5103               svn_client__merge_path_t *parent
5104                 = find_nearest_ancestor(children_with_mergeinfo,
5105                                         FALSE, child->abspath);
5106               svn_boolean_t child_inherits_implicit;
5107 
5108               /* If CHILD is a subtree then its parent must be in
5109                  CHILDREN_WITH_MERGEINFO, see the global comment
5110                  'THE CHILDREN_WITH_MERGEINFO ARRAY'. */
5111               SVN_ERR_ASSERT(parent);
5112 
5113               child_inherits_implicit = (parent && !child->switched);
5114               SVN_ERR(ensure_implicit_mergeinfo(parent, child,
5115                                                 child_inherits_implicit,
5116                                                 source->loc1->rev,
5117                                                 source->loc2->rev,
5118                                                 ra_session, merge_b->ctx,
5119                                                 result_pool, iterpool));
5120             }
5121 
5122           child->remaining_ranges = svn_rangelist__initialize(source->loc1->rev,
5123                                                               source->loc2->rev,
5124                                                               TRUE,
5125                                                               result_pool);
5126         }
5127       svn_pool_destroy(iterpool);
5128       return SVN_NO_ERROR;
5129     }
5130 
5131   /* If, in the merge source's history, there was a copy from an older
5132      revision, then SOURCE->loc2->url won't exist at some range M:N, where
5133      SOURCE->loc1->rev < M < N < SOURCE->loc2->rev. The rules of 'MERGEINFO
5134      MERGE SOURCE NORMALIZATION' allow this, but we must ignore these gaps
5135      when calculating what ranges remain to be merged from SOURCE. If we
5136      don't and try to merge any part of SOURCE->loc2->url@M:N we would
5137      break the editor since no part of that actually exists.  See
5138      http://svn.haxx.se/dev/archive-2008-11/0618.shtml.
5139 
5140      Find the gaps in the merge target's history, if any.  Eventually
5141      we will adjust CHILD->REMAINING_RANGES such that we don't describe
5142      non-existent paths to the editor. */
5143   SVN_ERR(find_gaps_in_merge_source_history(&gap_start, &gap_end,
5144                                             source,
5145                                             ra_session, merge_b->ctx,
5146                                             iterpool));
5147 
5148   /* Stash any gap in the merge command baton, we'll need it later when
5149      recording mergeinfo describing this merge. */
5150   if (SVN_IS_VALID_REVNUM(gap_start) && SVN_IS_VALID_REVNUM(gap_end))
5151     merge_b->implicit_src_gap = svn_rangelist__initialize(gap_start, gap_end,
5152                                                           TRUE, result_pool);
5153 
5154   for (i = 0; i < children_with_mergeinfo->nelts; i++)
5155     {
5156       svn_client__merge_path_t *child =
5157         APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
5158       const char *child_repos_path
5159         = svn_dirent_skip_ancestor(merge_b->target->abspath, child->abspath);
5160       merge_source_t child_source;
5161       svn_client__merge_path_t *parent = NULL;
5162       svn_boolean_t child_inherits_implicit;
5163 
5164       svn_pool_clear(iterpool);
5165 
5166       /* If the path is absent don't do subtree merge either. */
5167       SVN_ERR_ASSERT(child);
5168       if (child->absent)
5169         continue;
5170 
5171       SVN_ERR_ASSERT(child_repos_path != NULL);
5172       child_source.loc1 = svn_client__pathrev_join_relpath(
5173                             source->loc1, child_repos_path, iterpool);
5174       child_source.loc2 = svn_client__pathrev_join_relpath(
5175                             source->loc2, child_repos_path, iterpool);
5176       /* ### Is the child 'ancestral' over the same revision range?  It's
5177        * not necessarily true that a child is 'ancestral' if the parent is,
5178        * nor that it's not if the parent is not.  However, here we claim
5179        * that it is.  Before we had this 'ancestral' field that we need to
5180        * set explicitly, the claim was implicit.  Either way, the impact is
5181        * that we might pass calculate_remaining_ranges() a source that is
5182        * not in fact 'ancestral' (despite its 'ancestral' field being true),
5183        * contrary to its doc-string. */
5184       child_source.ancestral = source->ancestral;
5185 
5186       /* Get the explicit/inherited mergeinfo for CHILD.  If CHILD is the
5187          merge target then also get its implicit mergeinfo.  Otherwise defer
5188          this until we know it is absolutely necessary, since it requires an
5189          expensive round trip communication with the server. */
5190       SVN_ERR(get_full_mergeinfo(
5191         child->pre_merge_mergeinfo ? NULL : &(child->pre_merge_mergeinfo),
5192         /* Get implicit only for merge target. */
5193         (i == 0) ? &(child->implicit_mergeinfo) : NULL,
5194         &(child->inherited_mergeinfo),
5195         svn_mergeinfo_inherited, ra_session,
5196         child->abspath,
5197         MAX(source->loc1->rev, source->loc2->rev),
5198         MIN(source->loc1->rev, source->loc2->rev),
5199         merge_b->ctx, result_pool, iterpool));
5200 
5201       /* If CHILD isn't the merge target find its parent. */
5202       if (i > 0)
5203         {
5204           parent = find_nearest_ancestor(children_with_mergeinfo,
5205                                          FALSE, child->abspath);
5206           /* If CHILD is a subtree then its parent must be in
5207              CHILDREN_WITH_MERGEINFO, see the global comment
5208              'THE CHILDREN_WITH_MERGEINFO ARRAY'. */
5209           SVN_ERR_ASSERT(parent);
5210         }
5211 
5212       /* Issue #3443 - Can CHILD inherit PARENT's implicit mergeinfo, saving
5213          us from having to ask the repos?  The only time we can't do this is if
5214          CHILD is the merge target and so there is no PARENT to inherit from
5215          or if CHILD is the root of a switched subtree, in which case PARENT
5216          exists but is not CHILD's repository parent. */
5217       child_inherits_implicit = (parent && !child->switched);
5218 
5219       SVN_ERR(calculate_remaining_ranges(parent, child,
5220                                          &child_source,
5221                                          child->pre_merge_mergeinfo,
5222                                          merge_b->implicit_src_gap,
5223                                          child_inherits_implicit,
5224                                          ra_session,
5225                                          merge_b->ctx, result_pool,
5226                                          iterpool));
5227 
5228       /* Deal with any gap in SOURCE's natural history.
5229 
5230          If the gap is a proper subset of CHILD->REMAINING_RANGES then we can
5231          safely ignore it since we won't describe this path/rev pair.
5232 
5233          If the gap exactly matches or is a superset of a range in
5234          CHILD->REMAINING_RANGES then we must remove that range so we don't
5235          attempt to describe non-existent paths via the reporter, this will
5236          break the editor and our merge.
5237 
5238          If the gap adjoins or overlaps a range in CHILD->REMAINING_RANGES
5239          then we must *add* the gap so we span the missing revisions. */
5240       if (child->remaining_ranges->nelts
5241           && merge_b->implicit_src_gap)
5242         {
5243           int j;
5244           svn_boolean_t proper_subset = FALSE;
5245           svn_boolean_t overlaps_or_adjoins = FALSE;
5246 
5247           /* If this is a reverse merge reorder CHILD->REMAINING_RANGES
5248               so it will work with the svn_rangelist_* APIs below. */
5249           if (source->loc1->rev > source->loc2->rev)
5250             SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool));
5251 
5252           for (j = 0; j < child->remaining_ranges->nelts; j++)
5253             {
5254               svn_merge_range_t *range
5255                 = APR_ARRAY_IDX(child->remaining_ranges, j, svn_merge_range_t *);
5256 
5257               if ((range->start <= gap_start && gap_end < range->end)
5258                   || (range->start < gap_start && gap_end <= range->end))
5259                 {
5260                   proper_subset = TRUE;
5261                   break;
5262                 }
5263               else if ((gap_start == range->start) && (range->end == gap_end))
5264                 {
5265                   break;
5266                 }
5267               else if (gap_start <= range->end && range->start <= gap_end)
5268                 /* intersect */
5269                 {
5270                   overlaps_or_adjoins = TRUE;
5271                   break;
5272                 }
5273             }
5274 
5275           if (!proper_subset)
5276             {
5277               /* We need to make adjustments.  Remove from, or add the gap
5278                  to, CHILD->REMAINING_RANGES as appropriate. */
5279 
5280               if (overlaps_or_adjoins)
5281                 SVN_ERR(svn_rangelist_merge2(child->remaining_ranges,
5282                                              merge_b->implicit_src_gap,
5283                                              result_pool, iterpool));
5284               else /* equals == TRUE */
5285                 SVN_ERR(svn_rangelist_remove(&(child->remaining_ranges),
5286                                              merge_b->implicit_src_gap,
5287                                              child->remaining_ranges, FALSE,
5288                                              result_pool));
5289             }
5290 
5291           if (source->loc1->rev > source->loc2->rev) /* Reverse merge */
5292             SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool));
5293         }
5294     }
5295 
5296   svn_pool_destroy(iterpool);
5297   return SVN_NO_ERROR;
5298 }
5299 
5300 
5301 /*-----------------------------------------------------------------------*/
5302 
5303 /*** Other Helper Functions ***/
5304 
5305 /* Calculate the new mergeinfo for the target tree rooted at TARGET_ABSPATH
5306    based on MERGES (a mapping of absolute WC paths to rangelists representing
5307    a merge from the source SOURCE_FSPATH).
5308 
5309    If RESULT_CATALOG is NULL, then record the new mergeinfo in the WC (at,
5310    and possibly below, TARGET_ABSPATH).
5311 
5312    If RESULT_CATALOG is not NULL, then don't record the new mergeinfo on the
5313    WC, but instead record it in RESULT_CATALOG, where the keys are absolute
5314    working copy paths and the values are the new mergeinfos for each.
5315    Allocate additions to RESULT_CATALOG in pool which RESULT_CATALOG was
5316    created in. */
5317 static svn_error_t *
update_wc_mergeinfo(svn_mergeinfo_catalog_t result_catalog,const char * target_abspath,const char * source_fspath,apr_hash_t * merges,svn_boolean_t is_rollback,svn_client_ctx_t * ctx,apr_pool_t * scratch_pool)5318 update_wc_mergeinfo(svn_mergeinfo_catalog_t result_catalog,
5319                     const char *target_abspath,
5320                     const char *source_fspath,
5321                     apr_hash_t *merges,
5322                     svn_boolean_t is_rollback,
5323                     svn_client_ctx_t *ctx,
5324                     apr_pool_t *scratch_pool)
5325 {
5326   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
5327   apr_hash_index_t *hi;
5328 
5329   /* Combine the mergeinfo for the revision range just merged into
5330      the WC with its on-disk mergeinfo. */
5331   for (hi = apr_hash_first(scratch_pool, merges); hi; hi = apr_hash_next(hi))
5332     {
5333       const char *local_abspath = apr_hash_this_key(hi);
5334       svn_rangelist_t *ranges = apr_hash_this_val(hi);
5335       svn_rangelist_t *rangelist;
5336       svn_error_t *err;
5337       const char *local_abspath_rel_to_target;
5338       const char *fspath;
5339       svn_mergeinfo_t mergeinfo;
5340 
5341       svn_pool_clear(iterpool);
5342 
5343       /* As some of the merges may've changed the WC's mergeinfo, get
5344          a fresh copy before using it to update the WC's mergeinfo. */
5345       err = svn_client__parse_mergeinfo(&mergeinfo, ctx->wc_ctx,
5346                                         local_abspath, iterpool, iterpool);
5347 
5348       /* If a directory PATH was skipped because it is missing or was
5349          obstructed by an unversioned item then there's nothing we can
5350          do with that, so skip it. */
5351       if (err)
5352         {
5353           if (err->apr_err == SVN_ERR_WC_NOT_LOCKED
5354               || err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
5355             {
5356               svn_error_clear(err);
5357               continue;
5358             }
5359           else
5360             {
5361               return svn_error_trace(err);
5362             }
5363         }
5364 
5365       /* If we are attempting to set empty revision range override mergeinfo
5366          on a path with no explicit mergeinfo, we first need the
5367          mergeinfo that path inherits. */
5368       if (mergeinfo == NULL && ranges->nelts == 0)
5369         {
5370           SVN_ERR(svn_client__get_wc_mergeinfo(&mergeinfo, NULL,
5371                                                svn_mergeinfo_nearest_ancestor,
5372                                                local_abspath, NULL, NULL,
5373                                                FALSE, ctx, iterpool, iterpool));
5374         }
5375 
5376       if (mergeinfo == NULL)
5377         mergeinfo = apr_hash_make(iterpool);
5378 
5379       local_abspath_rel_to_target = svn_dirent_skip_ancestor(target_abspath,
5380                                                              local_abspath);
5381       SVN_ERR_ASSERT(local_abspath_rel_to_target != NULL);
5382       fspath = svn_fspath__join(source_fspath,
5383                                 local_abspath_rel_to_target,
5384                                 iterpool);
5385       rangelist = svn_hash_gets(mergeinfo, fspath);
5386       if (rangelist == NULL)
5387         rangelist = apr_array_make(iterpool, 0, sizeof(svn_merge_range_t *));
5388 
5389       if (is_rollback)
5390         {
5391           ranges = svn_rangelist_dup(ranges, iterpool);
5392           SVN_ERR(svn_rangelist_reverse(ranges, iterpool));
5393           SVN_ERR(svn_rangelist_remove(&rangelist, ranges, rangelist,
5394                                        FALSE,
5395                                        iterpool));
5396         }
5397       else
5398         {
5399           SVN_ERR(svn_rangelist_merge2(rangelist, ranges, iterpool, iterpool));
5400         }
5401       /* Update the mergeinfo by adjusting the path's rangelist. */
5402       svn_hash_sets(mergeinfo, fspath, rangelist);
5403 
5404       if (is_rollback && apr_hash_count(mergeinfo) == 0)
5405         mergeinfo = NULL;
5406 
5407       svn_mergeinfo__remove_empty_rangelists(mergeinfo, scratch_pool);
5408 
5409       if (result_catalog)
5410         {
5411           svn_mergeinfo_t existing_mergeinfo =
5412             svn_hash_gets(result_catalog, local_abspath);
5413           apr_pool_t *result_catalog_pool = apr_hash_pool_get(result_catalog);
5414 
5415           if (existing_mergeinfo)
5416             SVN_ERR(svn_mergeinfo_merge2(mergeinfo, existing_mergeinfo,
5417                                          result_catalog_pool, scratch_pool));
5418           svn_hash_sets(result_catalog,
5419                         apr_pstrdup(result_catalog_pool, local_abspath),
5420                         svn_mergeinfo_dup(mergeinfo, result_catalog_pool));
5421         }
5422       else
5423         {
5424           err = svn_client__record_wc_mergeinfo(local_abspath, mergeinfo,
5425                                                 TRUE, ctx, iterpool);
5426 
5427           if (err && err->apr_err == SVN_ERR_ENTRY_NOT_FOUND)
5428             {
5429               /* PATH isn't just missing, it's not even versioned as far
5430                  as this working copy knows.  But it was included in
5431                  MERGES, which means that the server knows about it.
5432                  Likely we don't have access to the source due to authz
5433                  restrictions.  For now just clear the error and
5434                  continue...
5435 
5436                  ### TODO:  Set non-inheritable mergeinfo on PATH's immediate
5437                  ### parent and normal mergeinfo on PATH's siblings which we
5438                  ### do have access to. */
5439               svn_error_clear(err);
5440             }
5441           else
5442             SVN_ERR(err);
5443         }
5444     }
5445 
5446   svn_pool_destroy(iterpool);
5447   return SVN_NO_ERROR;
5448 }
5449 
5450 /* Helper for record_mergeinfo_for_dir_merge().
5451 
5452    Record override mergeinfo on any paths skipped during a merge.
5453 
5454    Set empty mergeinfo on each path in MERGE_B->SKIPPED_ABSPATHS so the path
5455    does not incorrectly inherit mergeinfo that will later be describing
5456    the merge.
5457 
5458    MERGEINFO_PATH and MERGE_B are cascaded from
5459    arguments of the same name in the caller.
5460 
5461    IS_ROLLBACK is true if the caller is recording a reverse merge and false
5462    otherwise.  RANGELIST is the set of revisions being merged from
5463    MERGEINFO_PATH to MERGE_B->target. */
5464 static svn_error_t *
record_skips_in_mergeinfo(const char * mergeinfo_path,const svn_rangelist_t * rangelist,svn_boolean_t is_rollback,merge_cmd_baton_t * merge_b,apr_pool_t * scratch_pool)5465 record_skips_in_mergeinfo(const char *mergeinfo_path,
5466                           const svn_rangelist_t *rangelist,
5467                           svn_boolean_t is_rollback,
5468                           merge_cmd_baton_t *merge_b,
5469                           apr_pool_t *scratch_pool)
5470 {
5471   apr_hash_index_t *hi;
5472   apr_hash_t *merges;
5473   apr_size_t nbr_skips = apr_hash_count(merge_b->skipped_abspaths);
5474   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
5475 
5476   if (nbr_skips == 0)
5477     return SVN_NO_ERROR;
5478 
5479   merges = apr_hash_make(scratch_pool);
5480 
5481   /* Override the mergeinfo for child paths which weren't actually merged. */
5482   for (hi = apr_hash_first(scratch_pool, merge_b->skipped_abspaths); hi;
5483        hi = apr_hash_next(hi))
5484     {
5485       const char *skipped_abspath = apr_hash_this_key(hi);
5486       svn_wc_notify_state_t obstruction_state;
5487 
5488       svn_pool_clear(iterpool);
5489 
5490       /* Before we override, make sure this is a versioned path, it might
5491          be an external or missing from disk due to authz restrictions. */
5492       SVN_ERR(perform_obstruction_check(&obstruction_state, NULL, NULL,
5493                                         NULL, NULL,
5494                                         merge_b, skipped_abspath,
5495                                         iterpool));
5496       if (obstruction_state == svn_wc_notify_state_obstructed
5497           || obstruction_state == svn_wc_notify_state_missing)
5498         continue;
5499 
5500       /* Add an empty range list for this path.
5501 
5502          ### TODO: This works fine for a file path skipped because it is
5503          ### missing as long as the file's parent directory is present.
5504          ### But missing directory paths skipped are not handled yet,
5505          ### see issue #2915.
5506 
5507          ### TODO: An empty range is fine if the skipped path doesn't
5508          ### inherit any mergeinfo from a parent, but if it does
5509          ### we need to account for that.  See issue #3440
5510          ### https://issues.apache.org/jira/browse/SVN-3440. */
5511       svn_hash_sets(merges, skipped_abspath,
5512                     apr_array_make(scratch_pool, 0,
5513                                    sizeof(svn_merge_range_t *)));
5514 
5515       /* if (nbr_skips < notify_b->nbr_notifications)
5516            ### Use RANGELIST as the mergeinfo for all children of
5517            ### this path which were not also explicitly
5518            ### skipped? */
5519     }
5520   SVN_ERR(update_wc_mergeinfo(NULL, merge_b->target->abspath,
5521                               mergeinfo_path, merges,
5522                               is_rollback, merge_b->ctx, iterpool));
5523   svn_pool_destroy(iterpool);
5524   return SVN_NO_ERROR;
5525 }
5526 
5527 /* Data for reporting when a merge aborted because of raising conflicts.
5528  */
5529 typedef struct single_range_conflict_report_t
5530 {
5531   /* What sub-range of the requested source raised conflicts?
5532    * The 'inheritable' flag is ignored. */
5533   merge_source_t *conflicted_range;
5534   /* What sub-range of the requested source remains to be merged?
5535    * NULL if no more.  The 'inheritable' flag is ignored. */
5536   merge_source_t *remaining_source;
5537 
5538 } single_range_conflict_report_t;
5539 
5540 /* Create a single_range_conflict_report_t, containing deep copies of
5541  * CONFLICTED_RANGE and REMAINING_SOURCE, allocated in RESULT_POOL. */
5542 static single_range_conflict_report_t *
single_range_conflict_report_create(const merge_source_t * conflicted_range,const merge_source_t * remaining_source,apr_pool_t * result_pool)5543 single_range_conflict_report_create(const merge_source_t *conflicted_range,
5544                                     const merge_source_t *remaining_source,
5545                                     apr_pool_t *result_pool)
5546 {
5547   single_range_conflict_report_t *report
5548     = apr_palloc(result_pool, sizeof(*report));
5549 
5550   assert(conflicted_range != NULL);
5551 
5552   report->conflicted_range = merge_source_dup(conflicted_range, result_pool);
5553   report->remaining_source
5554     = remaining_source ? merge_source_dup(remaining_source, result_pool)
5555                        : NULL;
5556   return report;
5557 }
5558 
5559 /* Return a new svn_client__conflict_report_t containing deep copies of the
5560  * parameters, allocated in RESULT_POOL. */
5561 static svn_client__conflict_report_t *
conflict_report_create(const char * target_abspath,const merge_source_t * conflicted_range,svn_boolean_t was_last_range,apr_pool_t * result_pool)5562 conflict_report_create(const char *target_abspath,
5563                        const merge_source_t *conflicted_range,
5564                        svn_boolean_t was_last_range,
5565                        apr_pool_t *result_pool)
5566 {
5567   svn_client__conflict_report_t *report = apr_palloc(result_pool,
5568                                                      sizeof(*report));
5569 
5570   report->target_abspath = apr_pstrdup(result_pool, target_abspath);
5571   report->conflicted_range = merge_source_dup(conflicted_range, result_pool);
5572   report->was_last_range = was_last_range;
5573   return report;
5574 }
5575 
5576 /* Return a deep copy of REPORT, allocated in RESULT_POOL. */
5577 static svn_client__conflict_report_t *
conflict_report_dup(const svn_client__conflict_report_t * report,apr_pool_t * result_pool)5578 conflict_report_dup(const svn_client__conflict_report_t *report,
5579                     apr_pool_t *result_pool)
5580 {
5581   svn_client__conflict_report_t *new = apr_pmemdup(result_pool, report,
5582                                                    sizeof(*new));
5583 
5584   new->target_abspath = apr_pstrdup(result_pool, report->target_abspath);
5585   new->conflicted_range = merge_source_dup(report->conflicted_range,
5586                                            result_pool);
5587   return new;
5588 }
5589 
5590 svn_error_t *
svn_client__make_merge_conflict_error(svn_client__conflict_report_t * report,apr_pool_t * scratch_pool)5591 svn_client__make_merge_conflict_error(svn_client__conflict_report_t *report,
5592                                       apr_pool_t *scratch_pool)
5593 {
5594   assert(!report || svn_dirent_is_absolute(report->target_abspath));
5595 
5596   if (report && ! report->was_last_range)
5597     {
5598       svn_error_t *err = svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
5599        _("One or more conflicts were produced while merging r%ld:%ld into\n"
5600          "'%s' --\n"
5601          "resolve all conflicts and rerun the merge to apply the remaining\n"
5602          "unmerged revisions"),
5603        report->conflicted_range->loc1->rev, report->conflicted_range->loc2->rev,
5604        svn_dirent_local_style(report->target_abspath, scratch_pool));
5605       assert(report->conflicted_range->loc1->rev != report->conflicted_range->loc2->rev); /* ### is a valid case in a 2-URL merge */
5606       return err;
5607     }
5608   return SVN_NO_ERROR;
5609 }
5610 
5611 /* Helper for do_directory_merge().
5612 
5613    TARGET_WCPATH is a directory and CHILDREN_WITH_MERGEINFO is filled
5614    with paths (svn_client__merge_path_t *) arranged in depth first order,
5615    which have mergeinfo set on them or meet one of the other criteria
5616    defined in get_mergeinfo_paths().  Remove any paths absent from disk
5617    from CHILDREN_WITH_MERGEINFO which are equal to
5618    or are descendants of TARGET_WCPATH by setting those children to NULL. */
5619 static svn_error_t *
remove_absent_children(const char * target_wcpath,apr_array_header_t * children_with_mergeinfo)5620 remove_absent_children(const char *target_wcpath,
5621                        apr_array_header_t *children_with_mergeinfo)
5622 {
5623   /* Before we try to override mergeinfo for skipped paths, make sure
5624      the path isn't absent due to authz restrictions, because there's
5625      nothing we can do about those. */
5626   int i;
5627   for (i = 0; i < children_with_mergeinfo->nelts; i++)
5628     {
5629       svn_client__merge_path_t *child =
5630         APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
5631       if (child->absent
5632           && svn_dirent_is_ancestor(target_wcpath, child->abspath))
5633         {
5634           SVN_ERR(svn_sort__array_delete2(children_with_mergeinfo, i--, 1));
5635         }
5636     }
5637   return SVN_NO_ERROR;
5638 }
5639 
5640 /* Helper for do_directory_merge() to handle the case where a merge editor
5641    drive removes explicit mergeinfo from a subtree of the merge target.
5642 
5643    MERGE_B is cascaded from the argument of the same name in
5644    do_directory_merge().  For each path (if any) in
5645    MERGE_B->PATHS_WITH_DELETED_MERGEINFO remove that path from
5646    CHILDREN_WITH_MERGEINFO.
5647 
5648    The one exception is for the merge target itself,
5649    MERGE_B->target->abspath, this must always be present in
5650    CHILDREN_WITH_MERGEINFO so this is never removed by this
5651    function. */
5652 static svn_error_t *
remove_children_with_deleted_mergeinfo(merge_cmd_baton_t * merge_b,apr_array_header_t * children_with_mergeinfo)5653 remove_children_with_deleted_mergeinfo(merge_cmd_baton_t *merge_b,
5654                                        apr_array_header_t *children_with_mergeinfo)
5655 {
5656   int i;
5657 
5658   if (!merge_b->paths_with_deleted_mergeinfo)
5659     return SVN_NO_ERROR;
5660 
5661   /* CHILDREN_WITH_MERGEINFO[0] is the always the merge target
5662      so start at the first child. */
5663   for (i = 1; i < children_with_mergeinfo->nelts; i++)
5664     {
5665       svn_client__merge_path_t *child =
5666         APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
5667 
5668       if (svn_hash_gets(merge_b->paths_with_deleted_mergeinfo, child->abspath))
5669         {
5670           SVN_ERR(svn_sort__array_delete2(children_with_mergeinfo, i--, 1));
5671         }
5672     }
5673   return SVN_NO_ERROR;
5674 }
5675 
5676 /* Helper for do_directory_merge().
5677 
5678    Set up the diff editor report to merge the SOURCE diff
5679    into TARGET_ABSPATH and drive it.
5680 
5681    If mergeinfo is not being honored (based on MERGE_B -- see the doc
5682    string for HONOR_MERGEINFO() for how this is determined), then ignore
5683    CHILDREN_WITH_MERGEINFO and merge the SOURCE diff to TARGET_ABSPATH.
5684 
5685    If mergeinfo is being honored then perform a history-aware merge,
5686    describing TARGET_ABSPATH and its subtrees to the reporter in such as way
5687    as to avoid repeating merges already performed per the mergeinfo and
5688    natural history of TARGET_ABSPATH and its subtrees.
5689 
5690    The ranges that still need to be merged to the TARGET_ABSPATH and its
5691    subtrees are described in CHILDREN_WITH_MERGEINFO, an array of
5692    svn_client__merge_path_t * -- see 'THE CHILDREN_WITH_MERGEINFO ARRAY'
5693    comment at the top of this file for more info.  Note that it is possible
5694    TARGET_ABSPATH and/or some of its subtrees need only a subset, or no part,
5695    of SOURCE to be merged.  Though there is little point to
5696    calling this function if TARGET_ABSPATH and all its subtrees have already
5697    had SOURCE merged, this will work but is a no-op.
5698 
5699    SOURCE->rev1 and SOURCE->rev2 must be bound by the set of remaining_ranges
5700    fields in CHILDREN_WITH_MERGEINFO's elements, specifically:
5701 
5702    For forward merges (SOURCE->rev1 < SOURCE->rev2):
5703 
5704      1) The first svn_merge_range_t * element of each child's remaining_ranges
5705         array must meet one of the following conditions:
5706 
5707         a) The range's start field is greater than or equal to SOURCE->rev2.
5708 
5709         b) The range's end field is SOURCE->rev2.
5710 
5711      2) Among all the ranges that meet condition 'b' the oldest start
5712         revision must equal SOURCE->rev1.
5713 
5714    For reverse merges (SOURCE->rev1 > SOURCE->rev2):
5715 
5716      1) The first svn_merge_range_t * element of each child's remaining_ranges
5717         array must meet one of the following conditions:
5718 
5719         a) The range's start field is less than or equal to SOURCE->rev2.
5720 
5721         b) The range's end field is SOURCE->rev2.
5722 
5723      2) Among all the ranges that meet condition 'b' the youngest start
5724         revision must equal SOURCE->rev1.
5725 
5726    Note: If the first svn_merge_range_t * element of some subtree child's
5727    remaining_ranges array is the same as the first range of that child's
5728    nearest path-wise ancestor, then the subtree child *will not* be described
5729    to the reporter.
5730 
5731    DEPTH, NOTIFY_B, and MERGE_B are cascaded from do_directory_merge(), see
5732    that function for more info.
5733 
5734    MERGE_B->ra_session1 and MERGE_B->ra_session2 are RA sessions open to any
5735    URL in the repository of SOURCE; they may be temporarily reparented within
5736    this function.
5737 
5738    If SOURCE->ancestral is set, then SOURCE->loc1 must be a
5739    historical ancestor of SOURCE->loc2, or vice-versa (see
5740    `MERGEINFO MERGE SOURCE NORMALIZATION' for more requirements around
5741    SOURCE in this case).
5742 */
5743 static svn_error_t *
drive_merge_report_editor(const char * target_abspath,const merge_source_t * source,const apr_array_header_t * children_with_mergeinfo,const svn_diff_tree_processor_t * processor,svn_depth_t depth,merge_cmd_baton_t * merge_b,apr_pool_t * scratch_pool)5744 drive_merge_report_editor(const char *target_abspath,
5745                           const merge_source_t *source,
5746                           const apr_array_header_t *children_with_mergeinfo,
5747                           const svn_diff_tree_processor_t *processor,
5748                           svn_depth_t depth,
5749                           merge_cmd_baton_t *merge_b,
5750                           apr_pool_t *scratch_pool)
5751 {
5752   const svn_ra_reporter3_t *reporter;
5753   const svn_delta_editor_t *diff_editor;
5754   void *diff_edit_baton;
5755   void *report_baton;
5756   svn_revnum_t target_start;
5757   svn_boolean_t honor_mergeinfo = HONOR_MERGEINFO(merge_b);
5758   const char *old_sess1_url, *old_sess2_url;
5759   svn_boolean_t is_rollback = source->loc1->rev > source->loc2->rev;
5760 
5761   /* Start with a safe default starting revision for the editor and the
5762      merge target. */
5763   target_start = source->loc1->rev;
5764 
5765   /* If we are honoring mergeinfo the starting revision for the merge target
5766      might not be SOURCE->rev1, in fact the merge target might not need *any*
5767      part of SOURCE merged -- Instead some subtree of the target
5768      needs SOURCE -- So get the right starting revision for the
5769      target. */
5770   if (honor_mergeinfo)
5771     {
5772       svn_client__merge_path_t *child;
5773 
5774       /* CHILDREN_WITH_MERGEINFO must always exist if we are honoring
5775          mergeinfo and must have at least one element (describing the
5776          merge target). */
5777       SVN_ERR_ASSERT(children_with_mergeinfo);
5778       SVN_ERR_ASSERT(children_with_mergeinfo->nelts);
5779 
5780       /* Get the merge target's svn_client__merge_path_t, which is always
5781          the first in the array due to depth first sorting requirement,
5782          see 'THE CHILDREN_WITH_MERGEINFO ARRAY'. */
5783       child = APR_ARRAY_IDX(children_with_mergeinfo, 0,
5784                             svn_client__merge_path_t *);
5785       SVN_ERR_ASSERT(child);
5786       if (child->remaining_ranges->nelts == 0)
5787         {
5788           /* The merge target doesn't need anything merged. */
5789           target_start = source->loc2->rev;
5790         }
5791       else
5792         {
5793           /* The merge target has remaining revisions to merge.  These
5794              ranges may fully or partially overlap the range described
5795              by SOURCE->rev1:rev2 or may not intersect that range at
5796              all. */
5797           svn_merge_range_t *range =
5798             APR_ARRAY_IDX(child->remaining_ranges, 0,
5799                           svn_merge_range_t *);
5800           if ((!is_rollback && range->start > source->loc2->rev)
5801               || (is_rollback && range->start < source->loc2->rev))
5802             {
5803               /* Merge target's first remaining range doesn't intersect. */
5804               target_start = source->loc2->rev;
5805             }
5806           else
5807             {
5808               /* Merge target's first remaining range partially or
5809                  fully overlaps. */
5810               target_start = range->start;
5811             }
5812         }
5813     }
5814 
5815   SVN_ERR(svn_client__ensure_ra_session_url(&old_sess1_url,
5816                                             merge_b->ra_session1,
5817                                             source->loc1->url, scratch_pool));
5818   /* Temporarily point our second RA session to SOURCE->loc1->url, too.  We use
5819      this to request individual file contents. */
5820   SVN_ERR(svn_client__ensure_ra_session_url(&old_sess2_url,
5821                                             merge_b->ra_session2,
5822                                             source->loc1->url, scratch_pool));
5823 
5824   /* Get the diff editor and a reporter with which to, ultimately,
5825      drive it. */
5826   SVN_ERR(svn_client__get_diff_editor2(&diff_editor, &diff_edit_baton,
5827                                        merge_b->ra_session2,
5828                                        depth,
5829                                        source->loc1->rev,
5830                                        TRUE /* text_deltas */,
5831                                        processor,
5832                                        merge_b->ctx->cancel_func,
5833                                        merge_b->ctx->cancel_baton,
5834                                        scratch_pool));
5835   SVN_ERR(svn_ra_do_diff3(merge_b->ra_session1,
5836                           &reporter, &report_baton, source->loc2->rev,
5837                           "", depth, merge_b->diff_ignore_ancestry,
5838                           TRUE,  /* text_deltas */
5839                           source->loc2->url, diff_editor, diff_edit_baton,
5840                           scratch_pool));
5841 
5842   /* Drive the reporter. */
5843   SVN_ERR(reporter->set_path(report_baton, "", target_start, depth,
5844                              FALSE, NULL, scratch_pool));
5845   if (honor_mergeinfo && children_with_mergeinfo)
5846     {
5847       /* Describe children with mergeinfo overlapping this merge
5848          operation such that no repeated diff is retrieved for them from
5849          the repository. */
5850       int i;
5851       apr_pool_t *iterpool = svn_pool_create(scratch_pool);
5852 
5853       /* Start with CHILDREN_WITH_MERGEINFO[1], CHILDREN_WITH_MERGEINFO[0]
5854          is always the merge target (TARGET_ABSPATH). */
5855       for (i = 1; i < children_with_mergeinfo->nelts; i++)
5856         {
5857           svn_merge_range_t *range;
5858           const char *child_repos_path;
5859           const svn_client__merge_path_t *parent;
5860           const svn_client__merge_path_t *child =
5861             APR_ARRAY_IDX(children_with_mergeinfo, i,
5862                           svn_client__merge_path_t *);
5863 
5864           SVN_ERR_ASSERT(child);
5865           if (child->absent)
5866             continue;
5867 
5868           svn_pool_clear(iterpool);
5869 
5870           /* Find this child's nearest wc ancestor with mergeinfo. */
5871           parent = find_nearest_ancestor(children_with_mergeinfo,
5872                                          FALSE, child->abspath);
5873 
5874           /* If a subtree needs the same range applied as its nearest parent
5875              with mergeinfo or neither the subtree nor this parent need
5876              SOURCE->rev1:rev2 merged, then we don't need to describe the
5877              subtree separately.  In the latter case this could break the
5878              editor if child->abspath didn't exist at SOURCE->rev2 and we
5879              attempt to describe it via a reporter set_path call. */
5880           if (child->remaining_ranges->nelts)
5881             {
5882               range = APR_ARRAY_IDX(child->remaining_ranges, 0,
5883                                     svn_merge_range_t *);
5884               if ((!is_rollback && range->start > source->loc2->rev)
5885                   || (is_rollback && range->start < source->loc2->rev))
5886                 {
5887                   /* This child's first remaining range comes after the range
5888                      we are currently merging, so skip it. We expect to get
5889                      to it in a subsequent call to this function. */
5890                   continue;
5891                 }
5892               else if (parent->remaining_ranges->nelts)
5893                 {
5894                    svn_merge_range_t *parent_range =
5895                     APR_ARRAY_IDX(parent->remaining_ranges, 0,
5896                                   svn_merge_range_t *);
5897                    svn_merge_range_t *child_range =
5898                     APR_ARRAY_IDX(child->remaining_ranges, 0,
5899                                   svn_merge_range_t *);
5900                   if (parent_range->start == child_range->start)
5901                     continue; /* Subtree needs same range as parent. */
5902                 }
5903             }
5904           else /* child->remaining_ranges->nelts == 0*/
5905             {
5906               /* If both the subtree and its parent need no ranges applied
5907                  consider that as the "same ranges" and don't describe
5908                  the subtree. */
5909               if (parent->remaining_ranges->nelts == 0)
5910                 continue;
5911             }
5912 
5913           /* Ok, we really need to describe this subtree as it needs different
5914              ranges applied than its nearest working copy parent. */
5915           child_repos_path = svn_dirent_is_child(target_abspath,
5916                                                  child->abspath,
5917                                                  iterpool);
5918           /* This loop is only processing subtrees, so CHILD->ABSPATH
5919              better be a proper child of the merge target. */
5920           SVN_ERR_ASSERT(child_repos_path);
5921 
5922           if ((child->remaining_ranges->nelts == 0)
5923               || (is_rollback && (range->start < source->loc2->rev))
5924               || (!is_rollback && (range->start > source->loc2->rev)))
5925             {
5926               /* Nothing to merge to this child.  We'll claim we have
5927                  it up to date so the server doesn't send us
5928                  anything. */
5929               SVN_ERR(reporter->set_path(report_baton, child_repos_path,
5930                                          source->loc2->rev, depth, FALSE,
5931                                          NULL, iterpool));
5932             }
5933           else
5934             {
5935               SVN_ERR(reporter->set_path(report_baton, child_repos_path,
5936                                          range->start, depth, FALSE,
5937                                          NULL, iterpool));
5938             }
5939         }
5940       svn_pool_destroy(iterpool);
5941     }
5942   SVN_ERR(reporter->finish_report(report_baton, scratch_pool));
5943 
5944   /* Point the merge baton's RA sessions back where they were. */
5945   SVN_ERR(svn_ra_reparent(merge_b->ra_session1, old_sess1_url, scratch_pool));
5946   SVN_ERR(svn_ra_reparent(merge_b->ra_session2, old_sess2_url, scratch_pool));
5947 
5948   return SVN_NO_ERROR;
5949 }
5950 
5951 /* Iterate over each svn_client__merge_path_t * element in
5952    CHILDREN_WITH_MERGEINFO and, if START_REV is true, find the most inclusive
5953    start revision among those element's first remaining_ranges element.  If
5954    START_REV is false, then look for the most inclusive end revision.
5955 
5956    If IS_ROLLBACK is true the youngest start or end (as per START_REV)
5957    revision is considered the "most inclusive" otherwise the oldest revision
5958    is.
5959 
5960    If none of CHILDREN_WITH_MERGEINFO's elements have any remaining ranges
5961    return SVN_INVALID_REVNUM. */
5962 static svn_revnum_t
get_most_inclusive_rev(const apr_array_header_t * children_with_mergeinfo,svn_boolean_t is_rollback,svn_boolean_t start_rev)5963 get_most_inclusive_rev(const apr_array_header_t *children_with_mergeinfo,
5964                        svn_boolean_t is_rollback,
5965                        svn_boolean_t start_rev)
5966 {
5967   int i;
5968   svn_revnum_t most_inclusive_rev = SVN_INVALID_REVNUM;
5969 
5970   for (i = 0; i < children_with_mergeinfo->nelts; i++)
5971     {
5972       svn_client__merge_path_t *child =
5973         APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
5974 
5975       if ((! child) || child->absent)
5976         continue;
5977       if (child->remaining_ranges->nelts > 0)
5978         {
5979           svn_merge_range_t *range =
5980             APR_ARRAY_IDX(child->remaining_ranges, 0, svn_merge_range_t *);
5981 
5982           /* Are we looking for the most inclusive start or end rev? */
5983           svn_revnum_t rev = start_rev ? range->start : range->end;
5984 
5985           if ((most_inclusive_rev == SVN_INVALID_REVNUM)
5986               || (is_rollback && (rev > most_inclusive_rev))
5987               || ((! is_rollback) && (rev < most_inclusive_rev)))
5988             most_inclusive_rev = rev;
5989         }
5990     }
5991   return most_inclusive_rev;
5992 }
5993 
5994 
5995 /* If first item in each child of CHILDREN_WITH_MERGEINFO's
5996    remaining_ranges is inclusive of END_REV, Slice the first range in
5997    to two at END_REV. All the allocations are persistent and allocated
5998    from POOL. */
5999 static svn_error_t *
slice_remaining_ranges(apr_array_header_t * children_with_mergeinfo,svn_boolean_t is_rollback,svn_revnum_t end_rev,apr_pool_t * pool)6000 slice_remaining_ranges(apr_array_header_t *children_with_mergeinfo,
6001                        svn_boolean_t is_rollback, svn_revnum_t end_rev,
6002                        apr_pool_t *pool)
6003 {
6004   int i;
6005   for (i = 0; i < children_with_mergeinfo->nelts; i++)
6006     {
6007       svn_client__merge_path_t *child =
6008                                      APR_ARRAY_IDX(children_with_mergeinfo, i,
6009                                                    svn_client__merge_path_t *);
6010       if (!child || child->absent)
6011         continue;
6012       if (child->remaining_ranges->nelts > 0)
6013         {
6014           svn_merge_range_t *range = APR_ARRAY_IDX(child->remaining_ranges, 0,
6015                                                    svn_merge_range_t *);
6016           if ((is_rollback && (range->start > end_rev)
6017                && (range->end < end_rev))
6018               || (!is_rollback && (range->start < end_rev)
6019                   && (range->end > end_rev)))
6020             {
6021               svn_merge_range_t *split_range1, *split_range2;
6022 
6023               split_range1 = svn_merge_range_dup(range, pool);
6024               split_range2 = svn_merge_range_dup(range, pool);
6025               split_range1->end = end_rev;
6026               split_range2->start = end_rev;
6027               APR_ARRAY_IDX(child->remaining_ranges, 0,
6028                             svn_merge_range_t *) = split_range1;
6029               SVN_ERR(svn_sort__array_insert2(child->remaining_ranges,
6030                                               &split_range2, 1));
6031             }
6032         }
6033     }
6034   return SVN_NO_ERROR;
6035 }
6036 
6037 /* Helper for do_directory_merge().
6038 
6039    For each child in CHILDREN_WITH_MERGEINFO remove the first remaining_ranges
6040    svn_merge_range_t *element of the child if that range has an end revision
6041    equal to REVISION.
6042 
6043    If a range is removed from a child's remaining_ranges array, allocate the
6044    new remaining_ranges array in POOL.
6045  */
6046 static svn_error_t *
remove_first_range_from_remaining_ranges(svn_revnum_t revision,apr_array_header_t * children_with_mergeinfo,apr_pool_t * pool)6047 remove_first_range_from_remaining_ranges(svn_revnum_t revision,
6048                                          apr_array_header_t
6049                                            *children_with_mergeinfo,
6050                                          apr_pool_t *pool)
6051 {
6052   int i;
6053 
6054   for (i = 0; i < children_with_mergeinfo->nelts; i++)
6055     {
6056       svn_client__merge_path_t *child =
6057                                 APR_ARRAY_IDX(children_with_mergeinfo, i,
6058                                               svn_client__merge_path_t *);
6059       if (!child || child->absent)
6060         continue;
6061       if (child->remaining_ranges->nelts > 0)
6062         {
6063           svn_merge_range_t *first_range =
6064             APR_ARRAY_IDX(child->remaining_ranges, 0, svn_merge_range_t *);
6065           if (first_range->end == revision)
6066             {
6067               SVN_ERR(svn_sort__array_delete2(child->remaining_ranges, 0, 1));
6068             }
6069         }
6070     }
6071   return SVN_NO_ERROR;
6072 }
6073 
6074 /* Get a file's content and properties from the repository.
6075    Set *FILENAME to the local path to a new temporary file holding its text,
6076    and set *PROPS to a new hash of its properties.
6077 
6078    RA_SESSION is a session open to the correct repository, which will be
6079    temporarily reparented to the URL of the file itself.  LOCATION is the
6080    repository location of the file.
6081 
6082    The resulting file and the return values live as long as RESULT_POOL, all
6083    other allocations occur in SCRATCH_POOL.
6084 */
6085 static svn_error_t *
single_file_merge_get_file(const char ** filename,apr_hash_t ** props,svn_ra_session_t * ra_session,const svn_client__pathrev_t * location,const char * wc_target,apr_pool_t * result_pool,apr_pool_t * scratch_pool)6086 single_file_merge_get_file(const char **filename,
6087                            apr_hash_t **props,
6088                            svn_ra_session_t *ra_session,
6089                            const svn_client__pathrev_t *location,
6090                            const char *wc_target,
6091                            apr_pool_t *result_pool,
6092                            apr_pool_t *scratch_pool)
6093 {
6094   svn_stream_t *stream;
6095   const char *old_sess_url;
6096   svn_error_t *err;
6097 
6098   SVN_ERR(svn_stream_open_unique(&stream, filename, NULL,
6099                                  svn_io_file_del_on_pool_cleanup,
6100                                  result_pool, scratch_pool));
6101 
6102   SVN_ERR(svn_client__ensure_ra_session_url(&old_sess_url, ra_session, location->url,
6103                                             scratch_pool));
6104   err = svn_ra_get_file(ra_session, "", location->rev,
6105                         stream, NULL, props, scratch_pool);
6106   SVN_ERR(svn_error_compose_create(
6107             err, svn_ra_reparent(ra_session, old_sess_url, scratch_pool)));
6108 
6109   return svn_error_trace(svn_stream_close(stream));
6110 }
6111 
6112 /* Compare two svn_client__merge_path_t elements **A and **B, given the
6113    addresses of pointers to them. Return an integer less than, equal to, or
6114    greater than zero if A sorts before, the same as, or after B, respectively.
6115    This is a helper for qsort() and bsearch() on an array of such elements. */
6116 static int
compare_merge_path_t_as_paths(const void * a,const void * b)6117 compare_merge_path_t_as_paths(const void *a,
6118                               const void *b)
6119 {
6120   const svn_client__merge_path_t *child1
6121     = *((const svn_client__merge_path_t * const *) a);
6122   const svn_client__merge_path_t *child2
6123     = *((const svn_client__merge_path_t * const *) b);
6124 
6125   return svn_path_compare_paths(child1->abspath, child2->abspath);
6126 }
6127 
6128 /* Return a pointer to the element of CHILDREN_WITH_MERGEINFO whose path
6129  * is PATH, or return NULL if there is no such element. */
6130 static svn_client__merge_path_t *
get_child_with_mergeinfo(const apr_array_header_t * children_with_mergeinfo,const char * abspath)6131 get_child_with_mergeinfo(const apr_array_header_t *children_with_mergeinfo,
6132                          const char *abspath)
6133 {
6134   svn_client__merge_path_t merge_path;
6135   svn_client__merge_path_t *key;
6136   svn_client__merge_path_t **pchild;
6137 
6138   merge_path.abspath = abspath;
6139   key = &merge_path;
6140   pchild = bsearch(&key, children_with_mergeinfo->elts,
6141                    children_with_mergeinfo->nelts,
6142                    children_with_mergeinfo->elt_size,
6143                    compare_merge_path_t_as_paths);
6144   return pchild ? *pchild : NULL;
6145 }
6146 
6147 /* Insert a deep copy of INSERT_ELEMENT into the CHILDREN_WITH_MERGEINFO
6148    array at its correct position.  Allocate the new storage in POOL.
6149    CHILDREN_WITH_MERGEINFO is a depth first sorted array of
6150    (svn_client__merge_path_t *).
6151 
6152    ### Most callers don't need this to deep-copy the new element.
6153    ### It may be more efficient for some callers to insert a bunch of items
6154        out of order and then sort afterwards. (One caller is doing a qsort
6155        after calling this anyway.)
6156  */
6157 static svn_error_t *
insert_child_to_merge(apr_array_header_t * children_with_mergeinfo,const svn_client__merge_path_t * insert_element,apr_pool_t * pool)6158 insert_child_to_merge(apr_array_header_t *children_with_mergeinfo,
6159                       const svn_client__merge_path_t *insert_element,
6160                       apr_pool_t *pool)
6161 {
6162   int insert_index;
6163   const svn_client__merge_path_t *new_element;
6164 
6165   /* Find where to insert the new element */
6166   insert_index =
6167     svn_sort__bsearch_lower_bound(children_with_mergeinfo, &insert_element,
6168                                   compare_merge_path_t_as_paths);
6169 
6170   new_element = svn_client__merge_path_dup(insert_element, pool);
6171   SVN_ERR(svn_sort__array_insert2(children_with_mergeinfo,
6172                                   &new_element, insert_index));
6173   return SVN_NO_ERROR;
6174 }
6175 
6176 /* Helper for get_mergeinfo_paths().
6177 
6178    CHILDREN_WITH_MERGEINFO, DEPTH, and POOL are
6179    all cascaded from the arguments of the same name to get_mergeinfo_paths().
6180 
6181    TARGET is the merge target.
6182 
6183    *CHILD is the element in in CHILDREN_WITH_MERGEINFO that
6184    get_mergeinfo_paths() is iterating over and *CURR_INDEX is index for
6185    *CHILD.
6186 
6187    If CHILD->ABSPATH is equal to MERGE_CMD_BATON->target->abspath do nothing.
6188    Else if CHILD->ABSPATH is switched or absent then make sure its immediate
6189    (as opposed to nearest) parent in CHILDREN_WITH_MERGEINFO is marked as
6190    missing a child.  If the immediate parent does not exist in
6191    CHILDREN_WITH_MERGEINFO then create it (and increment *CURR_INDEX so that
6192    caller doesn't process the inserted element).  Also ensure that
6193    CHILD->ABSPATH's siblings which are not already present in
6194    CHILDREN_WITH_MERGEINFO are also added to the array, limited by DEPTH
6195    (e.g. don't add directory siblings of a switched file).
6196    Use POOL for temporary allocations only, any new CHILDREN_WITH_MERGEINFO
6197    elements are allocated in POOL. */
6198 static svn_error_t *
insert_parent_and_sibs_of_sw_absent_del_subtree(apr_array_header_t * children_with_mergeinfo,const merge_target_t * target,int * curr_index,svn_client__merge_path_t * child,svn_depth_t depth,svn_client_ctx_t * ctx,apr_pool_t * pool)6199 insert_parent_and_sibs_of_sw_absent_del_subtree(
6200                                    apr_array_header_t *children_with_mergeinfo,
6201                                    const merge_target_t *target,
6202                                    int *curr_index,
6203                                    svn_client__merge_path_t *child,
6204                                    svn_depth_t depth,
6205                                    svn_client_ctx_t *ctx,
6206                                    apr_pool_t *pool)
6207 {
6208   svn_client__merge_path_t *parent;
6209   const char *parent_abspath;
6210   apr_pool_t *iterpool;
6211   const apr_array_header_t *children;
6212   int i;
6213 
6214   if (!(child->absent
6215           || (child->switched
6216               && strcmp(target->abspath,
6217                         child->abspath) != 0)))
6218     return SVN_NO_ERROR;
6219 
6220   parent_abspath = svn_dirent_dirname(child->abspath, pool);
6221   parent = get_child_with_mergeinfo(children_with_mergeinfo, parent_abspath);
6222   if (parent)
6223     {
6224       parent->missing_child = child->absent;
6225       parent->switched_child = child->switched;
6226     }
6227   else
6228     {
6229       /* Create a new element to insert into CHILDREN_WITH_MERGEINFO. */
6230       parent = svn_client__merge_path_create(parent_abspath, pool);
6231       parent->missing_child = child->absent;
6232       parent->switched_child = child->switched;
6233       /* Insert PARENT into CHILDREN_WITH_MERGEINFO. */
6234       SVN_ERR(insert_child_to_merge(children_with_mergeinfo, parent, pool));
6235       /* Increment for loop index so we don't process the inserted element. */
6236       (*curr_index)++;
6237     } /*(parent == NULL) */
6238 
6239   /* Add all of PARENT's non-missing children that are not already present.*/
6240   SVN_ERR(svn_wc__node_get_children_of_working_node(&children, ctx->wc_ctx,
6241                                                     parent_abspath,
6242                                                     pool, pool));
6243   iterpool = svn_pool_create(pool);
6244   for (i = 0; i < children->nelts; i++)
6245     {
6246       const char *child_abspath = APR_ARRAY_IDX(children, i, const char *);
6247       svn_client__merge_path_t *sibling_of_missing;
6248 
6249       svn_pool_clear(iterpool);
6250 
6251       /* Does this child already exist in CHILDREN_WITH_MERGEINFO? */
6252       sibling_of_missing = get_child_with_mergeinfo(children_with_mergeinfo,
6253                                                     child_abspath);
6254       /* Create the missing child and insert it into CHILDREN_WITH_MERGEINFO.*/
6255       if (!sibling_of_missing)
6256         {
6257           /* Don't add directory children if DEPTH is svn_depth_files. */
6258           if (depth == svn_depth_files)
6259             {
6260               svn_node_kind_t child_kind;
6261 
6262               SVN_ERR(svn_wc_read_kind2(&child_kind,
6263                                         ctx->wc_ctx, child_abspath,
6264                                         FALSE, FALSE, iterpool));
6265               if (child_kind != svn_node_file)
6266                 continue;
6267             }
6268 
6269           sibling_of_missing = svn_client__merge_path_create(child_abspath,
6270                                                              pool);
6271           SVN_ERR(insert_child_to_merge(children_with_mergeinfo,
6272                                         sibling_of_missing, pool));
6273         }
6274     }
6275 
6276   svn_pool_destroy(iterpool);
6277 
6278   return SVN_NO_ERROR;
6279 }
6280 
6281 /* pre_merge_status_cb's baton */
6282 struct pre_merge_status_baton_t
6283 {
6284   svn_wc_context_t *wc_ctx;
6285 
6286   /* const char *absolute_wc_path to svn_depth_t * mapping for depths
6287      of empty, immediates, and files. */
6288   apr_hash_t *shallow_subtrees;
6289 
6290   /* const char *absolute_wc_path to the same, for all paths missing
6291      from the working copy. */
6292   apr_hash_t *missing_subtrees;
6293 
6294   /* const char *absolute_wc_path const char * repos relative path, describing
6295      the root of each switched subtree in the working copy and the repository
6296      relative path it is switched to. */
6297   apr_hash_t *switched_subtrees;
6298 
6299   /* A pool to allocate additions to the above hashes in. */
6300   apr_pool_t *pool;
6301 };
6302 
6303 /* A svn_wc_status_func4_t callback used by get_mergeinfo_paths to gather
6304    all switched, depth filtered and missing subtrees under a merge target.
6305 
6306    Note that this doesn't see server and user excluded trees. */
6307 static svn_error_t *
pre_merge_status_cb(void * baton,const char * local_abspath,const svn_wc_status3_t * status,apr_pool_t * scratch_pool)6308 pre_merge_status_cb(void *baton,
6309                     const char *local_abspath,
6310                     const svn_wc_status3_t *status,
6311                     apr_pool_t *scratch_pool)
6312 {
6313   struct pre_merge_status_baton_t *pmsb = baton;
6314 
6315   if (status->switched && !status->file_external)
6316     {
6317       store_path(pmsb->switched_subtrees, local_abspath);
6318     }
6319 
6320   if (status->depth == svn_depth_empty
6321       || status->depth == svn_depth_files)
6322     {
6323       const char *dup_abspath;
6324       svn_depth_t *depth = apr_pmemdup(pmsb->pool, &status->depth,
6325                                        sizeof *depth);
6326 
6327       dup_abspath = apr_pstrdup(pmsb->pool, local_abspath);
6328 
6329       svn_hash_sets(pmsb->shallow_subtrees, dup_abspath, depth);
6330     }
6331 
6332   if (status->node_status == svn_wc_status_missing)
6333     {
6334       svn_boolean_t new_missing_root = TRUE;
6335       apr_hash_index_t *hi;
6336 
6337       for (hi = apr_hash_first(scratch_pool, pmsb->missing_subtrees);
6338            hi;
6339            hi = apr_hash_next(hi))
6340         {
6341           const char *missing_root_path = apr_hash_this_key(hi);
6342 
6343           if (svn_dirent_is_ancestor(missing_root_path,
6344                                      local_abspath))
6345             {
6346               new_missing_root = FALSE;
6347               break;
6348             }
6349         }
6350 
6351       if (new_missing_root)
6352         store_path(pmsb->missing_subtrees, local_abspath);
6353     }
6354 
6355   return SVN_NO_ERROR;
6356 }
6357 
6358 /* Find all the subtrees in the working copy tree rooted at TARGET_ABSPATH
6359  * that have explicit mergeinfo.
6360  * Set *SUBTREES_WITH_MERGEINFO to a hash mapping (const char *) absolute
6361  * WC path to (svn_mergeinfo_t *) mergeinfo.
6362  *
6363  * ### Is this function equivalent to:
6364  *
6365  *   svn_client__get_wc_mergeinfo_catalog(
6366  *     subtrees_with_mergeinfo, inherited=NULL, include_descendants=TRUE,
6367  *     svn_mergeinfo_explicit, target_abspath, limit_path=NULL,
6368  *     walked_path=NULL, ignore_invalid_mergeinfo=FALSE, ...)
6369  *
6370  *   except for the catalog keys being abspaths instead of repo-relpaths?
6371  */
6372 static svn_error_t *
get_wc_explicit_mergeinfo_catalog(apr_hash_t ** subtrees_with_mergeinfo,const char * target_abspath,svn_depth_t depth,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)6373 get_wc_explicit_mergeinfo_catalog(apr_hash_t **subtrees_with_mergeinfo,
6374                                   const char *target_abspath,
6375                                   svn_depth_t depth,
6376                                   svn_client_ctx_t *ctx,
6377                                   apr_pool_t *result_pool,
6378                                   apr_pool_t *scratch_pool)
6379 {
6380   svn_opt_revision_t working_revision = { svn_opt_revision_working, { 0 } };
6381   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
6382   apr_hash_index_t *hi;
6383   apr_hash_t *externals;
6384 
6385   SVN_ERR(svn_client_propget5(subtrees_with_mergeinfo, NULL,
6386                               SVN_PROP_MERGEINFO, target_abspath,
6387                               &working_revision, &working_revision, NULL,
6388                               depth, NULL, ctx, result_pool, scratch_pool));
6389 
6390   SVN_ERR(svn_wc__externals_defined_below(&externals, ctx->wc_ctx,
6391                                           target_abspath, scratch_pool,
6392                                           scratch_pool));
6393 
6394   /* Convert property values to svn_mergeinfo_t. */
6395   for (hi = apr_hash_first(scratch_pool, *subtrees_with_mergeinfo);
6396        hi;
6397        hi = apr_hash_next(hi))
6398     {
6399       const char *wc_path = apr_hash_this_key(hi);
6400       svn_string_t *mergeinfo_string = apr_hash_this_val(hi);
6401       svn_mergeinfo_t mergeinfo;
6402       svn_error_t *err;
6403 
6404       /* svn_client_propget5 picks up file externals with
6405          mergeinfo, but we don't want those. */
6406       if (svn_hash_gets(externals, wc_path))
6407         {
6408           svn_hash_sets(*subtrees_with_mergeinfo, wc_path, NULL);
6409           continue;
6410         }
6411 
6412       svn_pool_clear(iterpool);
6413 
6414       err = svn_mergeinfo_parse(&mergeinfo, mergeinfo_string->data,
6415                                 result_pool);
6416       if (err)
6417         {
6418           if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
6419             {
6420               err = svn_error_createf(
6421                 SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING, err,
6422                 _("Invalid mergeinfo detected on '%s', "
6423                   "merge tracking not possible"),
6424                 svn_dirent_local_style(wc_path, scratch_pool));
6425             }
6426           return svn_error_trace(err);
6427         }
6428       svn_hash_sets(*subtrees_with_mergeinfo, wc_path, mergeinfo);
6429     }
6430   svn_pool_destroy(iterpool);
6431 
6432   return SVN_NO_ERROR;
6433 }
6434 
6435 /* Helper for do_directory_merge() when performing merge-tracking aware
6436    merges.
6437 
6438    Walk of the working copy tree rooted at TARGET->abspath to
6439    depth DEPTH.  Create an svn_client__merge_path_t * for any path which meets
6440    one or more of the following criteria:
6441 
6442      1) Path has working svn:mergeinfo.
6443      2) Path is switched.
6444      3) Path is a subtree of the merge target (i.e. is not equal to
6445         TARGET->abspath) and has no mergeinfo of its own but
6446         its immediate parent has mergeinfo with non-inheritable ranges.  If
6447         this isn't a dry-run and the merge is between differences in the same
6448         repository, then this function will set working mergeinfo on the path
6449         equal to the mergeinfo inheritable from its parent.
6450      4) Path has an immediate child (or children) missing from the WC because
6451         the child is switched or absent from the WC, or due to a sparse
6452         checkout.
6453      5) Path has a sibling (or siblings) missing from the WC because the
6454         sibling is switched, absent, scheduled for deletion, or missing due to
6455         a sparse checkout.
6456      6) Path is absent from disk due to an authz restriction.
6457      7) Path is equal to TARGET->abspath.
6458      8) Path is an immediate *directory* child of
6459         TARGET->abspath and DEPTH is svn_depth_immediates.
6460      9) Path is an immediate *file* child of TARGET->abspath
6461         and DEPTH is svn_depth_files.
6462      10) Path is at a depth of 'empty' or 'files'.
6463      11) Path is missing from disk (e.g. due to an OS-level deletion).
6464 
6465    If subtrees within the requested DEPTH are unexpectedly missing disk,
6466    then raise SVN_ERR_CLIENT_NOT_READY_TO_MERGE.
6467 
6468    Store the svn_client__merge_path_t *'s in *CHILDREN_WITH_MERGEINFO in
6469    depth-first order based on the svn_client__merge_path_t *s path member as
6470    sorted by svn_path_compare_paths().  Set the remaining_ranges field of each
6471    element to NULL.
6472 
6473    Note: Since the walk is rooted at TARGET->abspath, the
6474    latter is guaranteed to be in *CHILDREN_WITH_MERGEINFO and due to the
6475    depth-first ordering it is guaranteed to be the first element in
6476    *CHILDREN_WITH_MERGEINFO.
6477 
6478    MERGE_CMD_BATON is cascaded from the argument of the same name in
6479    do_directory_merge().
6480 */
6481 static svn_error_t *
get_mergeinfo_paths(apr_array_header_t * children_with_mergeinfo,const merge_target_t * target,svn_depth_t depth,svn_boolean_t dry_run,svn_boolean_t same_repos,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)6482 get_mergeinfo_paths(apr_array_header_t *children_with_mergeinfo,
6483                     const merge_target_t *target,
6484                     svn_depth_t depth,
6485                     svn_boolean_t dry_run,
6486                     svn_boolean_t same_repos,
6487                     svn_client_ctx_t *ctx,
6488                     apr_pool_t *result_pool,
6489                     apr_pool_t *scratch_pool)
6490 {
6491   int i;
6492   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
6493   apr_pool_t *swmi_pool;
6494   apr_hash_t *subtrees_with_mergeinfo;
6495   apr_hash_t *excluded_subtrees;
6496   apr_hash_t *switched_subtrees;
6497   apr_hash_t *shallow_subtrees;
6498   apr_hash_t *missing_subtrees;
6499   struct pre_merge_status_baton_t pre_merge_status_baton;
6500 
6501   /* Case 1: Subtrees with explicit mergeinfo. */
6502   /* Use a subpool for subtrees_with_mergeinfo, as it can be very large
6503      and is temporary. */
6504   swmi_pool = svn_pool_create(scratch_pool);
6505   SVN_ERR(get_wc_explicit_mergeinfo_catalog(&subtrees_with_mergeinfo,
6506                                             target->abspath,
6507                                             depth, ctx,
6508                                             swmi_pool, swmi_pool));
6509   if (subtrees_with_mergeinfo)
6510     {
6511       apr_hash_index_t *hi;
6512 
6513       for (hi = apr_hash_first(scratch_pool, subtrees_with_mergeinfo);
6514            hi;
6515            hi = apr_hash_next(hi))
6516         {
6517           const char *wc_path = apr_hash_this_key(hi);
6518           svn_mergeinfo_t mergeinfo = apr_hash_this_val(hi);
6519           svn_client__merge_path_t *mergeinfo_child =
6520             svn_client__merge_path_create(wc_path, result_pool);
6521 
6522           svn_pool_clear(iterpool);
6523 
6524           /* Stash this child's pre-existing mergeinfo. */
6525           mergeinfo_child->pre_merge_mergeinfo = mergeinfo;
6526 
6527           /* Note if this child has non-inheritable mergeinfo */
6528           mergeinfo_child->has_noninheritable
6529             = svn_mergeinfo__is_noninheritable(
6530                 mergeinfo_child->pre_merge_mergeinfo, iterpool);
6531 
6532           /* Append it.  We'll sort below. */
6533           APR_ARRAY_PUSH(children_with_mergeinfo, svn_client__merge_path_t *)
6534             = svn_client__merge_path_dup(mergeinfo_child, result_pool);
6535         }
6536 
6537       /* Sort CHILDREN_WITH_MERGEINFO by each child's path (i.e. as per
6538          compare_merge_path_t_as_paths).  Any subsequent insertions of new
6539          children with insert_child_to_merge() require this ordering. */
6540       svn_sort__array(children_with_mergeinfo, compare_merge_path_t_as_paths);
6541     }
6542   svn_pool_destroy(swmi_pool);
6543 
6544   /* Case 2: Switched subtrees
6545      Case 10: Paths at depths of 'empty' or 'files'
6546      Case 11: Paths missing from disk */
6547   pre_merge_status_baton.wc_ctx = ctx->wc_ctx;
6548   switched_subtrees = apr_hash_make(scratch_pool);
6549   pre_merge_status_baton.switched_subtrees = switched_subtrees;
6550   shallow_subtrees = apr_hash_make(scratch_pool);
6551   pre_merge_status_baton.shallow_subtrees = shallow_subtrees;
6552   missing_subtrees = apr_hash_make(scratch_pool);
6553   pre_merge_status_baton.missing_subtrees = missing_subtrees;
6554   pre_merge_status_baton.pool = scratch_pool;
6555   SVN_ERR(svn_wc_walk_status(ctx->wc_ctx,
6556                              target->abspath,
6557                              depth,
6558                              TRUE /* get_all */,
6559                              FALSE /* no_ignore */,
6560                              TRUE /* ignore_text_mods */,
6561                              NULL /* ingore_patterns */,
6562                              pre_merge_status_cb, &pre_merge_status_baton,
6563                              ctx->cancel_func, ctx->cancel_baton,
6564                              scratch_pool));
6565 
6566   /* Issue #2915: Raise an error describing the roots of any missing
6567      subtrees, i.e. those that the WC thinks are on disk but have been
6568      removed outside of Subversion. */
6569   if (apr_hash_count(missing_subtrees))
6570     {
6571       apr_hash_index_t *hi;
6572       svn_stringbuf_t *missing_subtree_err_buf =
6573         svn_stringbuf_create(_("Merge tracking not allowed with missing "
6574                                "subtrees; try restoring these items "
6575                                "first:\n"), scratch_pool);
6576 
6577       for (hi = apr_hash_first(scratch_pool, missing_subtrees);
6578            hi;
6579            hi = apr_hash_next(hi))
6580         {
6581           svn_pool_clear(iterpool);
6582           svn_stringbuf_appendcstr(missing_subtree_err_buf,
6583                                    svn_dirent_local_style(
6584                                      apr_hash_this_key(hi), iterpool));
6585           svn_stringbuf_appendcstr(missing_subtree_err_buf, "\n");
6586         }
6587 
6588       return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE,
6589                               NULL, missing_subtree_err_buf->data);
6590     }
6591 
6592   if (apr_hash_count(switched_subtrees))
6593     {
6594       apr_hash_index_t *hi;
6595 
6596       for (hi = apr_hash_first(scratch_pool, switched_subtrees);
6597            hi;
6598            hi = apr_hash_next(hi))
6599         {
6600            const char *wc_path = apr_hash_this_key(hi);
6601            svn_client__merge_path_t *child = get_child_with_mergeinfo(
6602              children_with_mergeinfo, wc_path);
6603 
6604            if (child)
6605              {
6606                child->switched = TRUE;
6607              }
6608            else
6609              {
6610                svn_client__merge_path_t *switched_child =
6611                  svn_client__merge_path_create(wc_path, result_pool);
6612                switched_child->switched = TRUE;
6613                SVN_ERR(insert_child_to_merge(children_with_mergeinfo,
6614                                              switched_child, result_pool));
6615              }
6616         }
6617     }
6618 
6619   if (apr_hash_count(shallow_subtrees))
6620     {
6621       apr_hash_index_t *hi;
6622 
6623       for (hi = apr_hash_first(scratch_pool, shallow_subtrees);
6624            hi;
6625            hi = apr_hash_next(hi))
6626         {
6627            svn_boolean_t new_shallow_child = FALSE;
6628            const char *wc_path = apr_hash_this_key(hi);
6629            svn_depth_t *child_depth = apr_hash_this_val(hi);
6630            svn_client__merge_path_t *shallow_child = get_child_with_mergeinfo(
6631              children_with_mergeinfo, wc_path);
6632 
6633            if (shallow_child)
6634              {
6635                if (*child_depth == svn_depth_empty
6636                    || *child_depth == svn_depth_files)
6637                  shallow_child->missing_child = TRUE;
6638              }
6639            else
6640              {
6641                shallow_child = svn_client__merge_path_create(wc_path,
6642                                                              result_pool);
6643                new_shallow_child = TRUE;
6644 
6645                if (*child_depth == svn_depth_empty
6646                    || *child_depth == svn_depth_files)
6647                  shallow_child->missing_child = TRUE;
6648              }
6649 
6650           /* A little trickery: If PATH doesn't have any mergeinfo or has
6651              only inheritable mergeinfo, we still describe it as having
6652              non-inheritable mergeinfo if it is missing a child due to
6653              a shallow depth.  Why? Because the mergeinfo we'll add to PATH
6654              to describe the merge must be non-inheritable, so PATH's missing
6655              children don't inherit it.  Marking these PATHs as non-
6656              inheritable allows the logic for case 3 to properly account
6657              for PATH's children. */
6658           if (!shallow_child->has_noninheritable
6659               && (*child_depth == svn_depth_empty
6660                   || *child_depth == svn_depth_files))
6661             {
6662               shallow_child->has_noninheritable = TRUE;
6663             }
6664 
6665           if (new_shallow_child)
6666             SVN_ERR(insert_child_to_merge(children_with_mergeinfo,
6667                                           shallow_child, result_pool));
6668        }
6669     }
6670 
6671   /* Case 6: Paths absent from disk due to server or user exclusion. */
6672   SVN_ERR(svn_wc__get_excluded_subtrees(&excluded_subtrees,
6673                                         ctx->wc_ctx, target->abspath,
6674                                         result_pool, scratch_pool));
6675   if (excluded_subtrees)
6676     {
6677       apr_hash_index_t *hi;
6678 
6679       for (hi = apr_hash_first(scratch_pool, excluded_subtrees);
6680            hi;
6681            hi = apr_hash_next(hi))
6682         {
6683            const char *wc_path = apr_hash_this_key(hi);
6684            svn_client__merge_path_t *child = get_child_with_mergeinfo(
6685              children_with_mergeinfo, wc_path);
6686 
6687            if (child)
6688              {
6689                child->absent = TRUE;
6690              }
6691            else
6692              {
6693                svn_client__merge_path_t *absent_child =
6694                  svn_client__merge_path_create(wc_path, result_pool);
6695                absent_child->absent = TRUE;
6696                SVN_ERR(insert_child_to_merge(children_with_mergeinfo,
6697                                              absent_child, result_pool));
6698              }
6699         }
6700     }
6701 
6702   /* Case 7: The merge target MERGE_CMD_BATON->target->abspath is always
6703      present. */
6704   if (!get_child_with_mergeinfo(children_with_mergeinfo,
6705                                 target->abspath))
6706     {
6707       svn_client__merge_path_t *target_child =
6708         svn_client__merge_path_create(target->abspath,
6709                                       result_pool);
6710       SVN_ERR(insert_child_to_merge(children_with_mergeinfo, target_child,
6711                                     result_pool));
6712     }
6713 
6714   /* Case 8: Path is an immediate *directory* child of
6715      MERGE_CMD_BATON->target->abspath and DEPTH is svn_depth_immediates.
6716 
6717      Case 9: Path is an immediate *file* child of
6718      MERGE_CMD_BATON->target->abspath and DEPTH is svn_depth_files. */
6719   if (depth == svn_depth_immediates || depth == svn_depth_files)
6720     {
6721       int j;
6722       const apr_array_header_t *immediate_children;
6723 
6724       SVN_ERR(svn_wc__node_get_children_of_working_node(
6725         &immediate_children, ctx->wc_ctx,
6726         target->abspath, scratch_pool, scratch_pool));
6727 
6728       for (j = 0; j < immediate_children->nelts; j++)
6729         {
6730           const char *immediate_child_abspath =
6731             APR_ARRAY_IDX(immediate_children, j, const char *);
6732           svn_node_kind_t immediate_child_kind;
6733 
6734           svn_pool_clear(iterpool);
6735           SVN_ERR(svn_wc_read_kind2(&immediate_child_kind,
6736                                     ctx->wc_ctx, immediate_child_abspath,
6737                                     FALSE, FALSE, iterpool));
6738           if ((immediate_child_kind == svn_node_dir
6739                && depth == svn_depth_immediates)
6740               || (immediate_child_kind == svn_node_file
6741                   && depth == svn_depth_files))
6742             {
6743               if (!get_child_with_mergeinfo(children_with_mergeinfo,
6744                                             immediate_child_abspath))
6745                 {
6746                   svn_client__merge_path_t *immediate_child =
6747                     svn_client__merge_path_create(immediate_child_abspath,
6748                                                   result_pool);
6749 
6750                   if (immediate_child_kind == svn_node_dir
6751                       && depth == svn_depth_immediates)
6752                     immediate_child->immediate_child_dir = TRUE;
6753 
6754                   SVN_ERR(insert_child_to_merge(children_with_mergeinfo,
6755                                                 immediate_child, result_pool));
6756                 }
6757             }
6758         }
6759     }
6760 
6761   /* If DEPTH isn't empty then cover cases 3), 4), and 5), possibly adding
6762      elements to CHILDREN_WITH_MERGEINFO. */
6763   if (depth <= svn_depth_empty)
6764     return SVN_NO_ERROR;
6765 
6766   for (i = 0; i < children_with_mergeinfo->nelts; i++)
6767     {
6768       svn_client__merge_path_t *child =
6769         APR_ARRAY_IDX(children_with_mergeinfo, i,
6770                       svn_client__merge_path_t *);
6771       svn_pool_clear(iterpool);
6772 
6773       /* Case 3) Where merging to a path with a switched child the path
6774          gets non-inheritable mergeinfo for the merge range performed and
6775          the child gets its own set of mergeinfo.  If the switched child
6776          later "returns", e.g. a switched path is unswitched, the child
6777          may not have any explicit mergeinfo.  If the initial merge is
6778          repeated we don't want to repeat the merge for the path, but we
6779          do want to repeat it for the previously switched child.  To
6780          ensure this we check if all of CHILD's non-missing children have
6781          explicit mergeinfo (they should already be present in
6782          CHILDREN_WITH_MERGEINFO if they do).  If not,
6783          add the children without mergeinfo to CHILDREN_WITH_MERGEINFO so
6784          do_directory_merge() will merge them independently.
6785 
6786          But that's not enough!  Since do_directory_merge() performs
6787          the merges on the paths in CHILDREN_WITH_MERGEINFO in a depth
6788          first manner it will merge the previously switched path's parent
6789          first.  As part of this merge it will update the parent's
6790          previously non-inheritable mergeinfo and make it inheritable
6791          (since it notices the path has no missing children), then when
6792          do_directory_merge() finally merges the previously missing
6793          child it needs to get mergeinfo from the child's nearest
6794          ancestor, but since do_directory_merge() already tweaked that
6795          mergeinfo, removing the non-inheritable flag, it appears that the
6796          child already has been merged to.  To prevent this we set
6797          override mergeinfo on the child now, before any merging is done,
6798          so it has explicit mergeinfo that reflects only CHILD's
6799          inheritable mergeinfo. */
6800 
6801       /* If depth is immediates or files then don't add new children if
6802          CHILD is a subtree of the merge target; those children are below
6803          the operational depth of the merge. */
6804       if (child->has_noninheritable
6805           && (i == 0 || depth == svn_depth_infinity))
6806         {
6807           const apr_array_header_t *children;
6808           int j;
6809 
6810           SVN_ERR(svn_wc__node_get_children_of_working_node(
6811                                             &children,
6812                                             ctx->wc_ctx,
6813                                             child->abspath,
6814                                             iterpool, iterpool));
6815           for (j = 0; j < children->nelts; j++)
6816             {
6817               svn_client__merge_path_t *child_of_noninheritable;
6818               const char *child_abspath = APR_ARRAY_IDX(children, j,
6819                                                         const char*);
6820 
6821               /* Does this child already exist in CHILDREN_WITH_MERGEINFO?
6822                  If not, create it and insert it into
6823                  CHILDREN_WITH_MERGEINFO and set override mergeinfo on
6824                  it. */
6825               child_of_noninheritable =
6826                 get_child_with_mergeinfo(children_with_mergeinfo,
6827                                          child_abspath);
6828               if (!child_of_noninheritable)
6829                 {
6830                   /* Don't add directory children if DEPTH
6831                      is svn_depth_files. */
6832                   if (depth == svn_depth_files)
6833                     {
6834                       svn_node_kind_t child_kind;
6835                       SVN_ERR(svn_wc_read_kind2(&child_kind,
6836                                                 ctx->wc_ctx, child_abspath,
6837                                                 FALSE, FALSE, iterpool));
6838                       if (child_kind != svn_node_file)
6839                         continue;
6840                     }
6841                   /* else DEPTH is infinity or immediates so we want both
6842                      directory and file children. */
6843 
6844                   child_of_noninheritable =
6845                     svn_client__merge_path_create(child_abspath, result_pool);
6846                   child_of_noninheritable->child_of_noninheritable = TRUE;
6847                   SVN_ERR(insert_child_to_merge(children_with_mergeinfo,
6848                                                 child_of_noninheritable,
6849                                                 result_pool));
6850                   if (!dry_run && same_repos)
6851                     {
6852                       svn_mergeinfo_t mergeinfo;
6853 
6854                       SVN_ERR(svn_client__get_wc_mergeinfo(
6855                         &mergeinfo, NULL,
6856                         svn_mergeinfo_nearest_ancestor,
6857                         child_of_noninheritable->abspath,
6858                         target->abspath, NULL, FALSE,
6859                         ctx, iterpool, iterpool));
6860 
6861                       SVN_ERR(svn_client__record_wc_mergeinfo(
6862                         child_of_noninheritable->abspath, mergeinfo,
6863                         FALSE, ctx, iterpool));
6864                     }
6865                 }
6866             }
6867         }
6868       /* Case 4 and 5 are handled by the following function. */
6869       SVN_ERR(insert_parent_and_sibs_of_sw_absent_del_subtree(
6870         children_with_mergeinfo, target, &i, child,
6871         depth, ctx, result_pool));
6872     } /* i < children_with_mergeinfo->nelts */
6873   svn_pool_destroy(iterpool);
6874 
6875   return SVN_NO_ERROR;
6876 }
6877 
6878 
6879 /* Implements the svn_log_entry_receiver_t interface.
6880  *
6881  * BATON is an 'apr_array_header_t *' array of 'svn_revnum_t'.
6882  * Push a copy of LOG_ENTRY->revision onto BATON.  Thus, a
6883  * series of invocations of this callback accumulates the
6884  * corresponding set of revisions into BATON.
6885  */
6886 static svn_error_t *
log_changed_revs(void * baton,svn_log_entry_t * log_entry,apr_pool_t * pool)6887 log_changed_revs(void *baton,
6888                  svn_log_entry_t *log_entry,
6889                  apr_pool_t *pool)
6890 {
6891   apr_array_header_t *revs = baton;
6892 
6893   APR_ARRAY_PUSH(revs, svn_revnum_t) = log_entry->revision;
6894   return SVN_NO_ERROR;
6895 }
6896 
6897 
6898 /* Set *MIN_REV_P to the oldest and *MAX_REV_P to the youngest start or end
6899  * revision occurring in RANGELIST, or to SVN_INVALID_REVNUM if RANGELIST
6900  * is empty. */
6901 static void
merge_range_find_extremes(svn_revnum_t * min_rev_p,svn_revnum_t * max_rev_p,const svn_rangelist_t * rangelist)6902 merge_range_find_extremes(svn_revnum_t *min_rev_p,
6903                           svn_revnum_t *max_rev_p,
6904                           const svn_rangelist_t *rangelist)
6905 {
6906   int i;
6907 
6908   *min_rev_p = SVN_INVALID_REVNUM;
6909   *max_rev_p = SVN_INVALID_REVNUM;
6910   for (i = 0; i < rangelist->nelts; i++)
6911     {
6912       svn_merge_range_t *range
6913         = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
6914       svn_revnum_t range_min = MIN(range->start, range->end);
6915       svn_revnum_t range_max = MAX(range->start, range->end);
6916 
6917       if ((! SVN_IS_VALID_REVNUM(*min_rev_p)) || (range_min < *min_rev_p))
6918         *min_rev_p = range_min;
6919       if ((! SVN_IS_VALID_REVNUM(*max_rev_p)) || (range_max > *max_rev_p))
6920         *max_rev_p = range_max;
6921     }
6922 }
6923 
6924 /* Wrapper around svn_ra_get_log2(). Invoke RECEIVER with RECEIVER_BATON
6925  * on each commit from YOUNGEST_REV to OLDEST_REV in which TARGET_RELPATH
6926  * changed.  TARGET_RELPATH is relative to RA_SESSION's URL.
6927  * Important: Revision properties are not retrieved by this function for
6928  * performance reasons.
6929  */
6930 static svn_error_t *
get_log(svn_ra_session_t * ra_session,const char * target_relpath,svn_revnum_t youngest_rev,svn_revnum_t oldest_rev,svn_boolean_t discover_changed_paths,svn_log_entry_receiver_t receiver,void * receiver_baton,apr_pool_t * pool)6931 get_log(svn_ra_session_t *ra_session,
6932         const char *target_relpath,
6933         svn_revnum_t youngest_rev,
6934         svn_revnum_t oldest_rev,
6935         svn_boolean_t discover_changed_paths,
6936         svn_log_entry_receiver_t receiver,
6937         void *receiver_baton,
6938         apr_pool_t *pool)
6939 {
6940   apr_array_header_t *log_targets;
6941   apr_array_header_t *revprops;
6942 
6943   log_targets = apr_array_make(pool, 1, sizeof(const char *));
6944   APR_ARRAY_PUSH(log_targets, const char *) = target_relpath;
6945 
6946   revprops = apr_array_make(pool, 0, sizeof(const char *));
6947 
6948   SVN_ERR(svn_ra_get_log2(ra_session, log_targets, youngest_rev,
6949                           oldest_rev, 0 /* limit */, discover_changed_paths,
6950                           FALSE /* strict_node_history */,
6951                           FALSE /* include_merged_revisions */,
6952                           revprops, receiver, receiver_baton, pool));
6953 
6954   return SVN_NO_ERROR;
6955 }
6956 
6957 /* Set *OPERATIVE_RANGES_P to an array of svn_merge_range_t * merge
6958    range objects copied wholesale from RANGES which have the property
6959    that in some revision within that range the object identified by
6960    RA_SESSION was modified (if by "modified" we mean "'svn log' would
6961    return that revision).  *OPERATIVE_RANGES_P is allocated from the
6962    same pool as RANGES, and the ranges within it are shared with
6963    RANGES, too.
6964 
6965    *OPERATIVE_RANGES_P may be the same as RANGES (that is, the output
6966    parameter is set only after the input is no longer used).
6967 
6968    Use POOL for temporary allocations.  */
6969 static svn_error_t *
remove_noop_merge_ranges(svn_rangelist_t ** operative_ranges_p,svn_ra_session_t * ra_session,const svn_rangelist_t * ranges,apr_pool_t * pool)6970 remove_noop_merge_ranges(svn_rangelist_t **operative_ranges_p,
6971                          svn_ra_session_t *ra_session,
6972                          const svn_rangelist_t *ranges,
6973                          apr_pool_t *pool)
6974 {
6975   int i;
6976   svn_revnum_t oldest_rev, youngest_rev;
6977   apr_array_header_t *changed_revs =
6978     apr_array_make(pool, ranges->nelts, sizeof(svn_revnum_t));
6979   svn_rangelist_t *operative_ranges =
6980     apr_array_make(ranges->pool, ranges->nelts, ranges->elt_size);
6981 
6982   /* Find the revision extremes of the RANGES we have. */
6983   merge_range_find_extremes(&oldest_rev, &youngest_rev, ranges);
6984   if (SVN_IS_VALID_REVNUM(oldest_rev))
6985     oldest_rev++;  /* make it inclusive */
6986 
6987   /* Get logs across those ranges, recording which revisions hold
6988      changes to our object's history. */
6989   SVN_ERR(get_log(ra_session, "", youngest_rev, oldest_rev, FALSE,
6990                   log_changed_revs, changed_revs, pool));
6991 
6992   /* Are there *any* changes? */
6993   if (changed_revs->nelts)
6994     {
6995       /* Our list of changed revisions should be in youngest-to-oldest
6996          order. */
6997       svn_revnum_t youngest_changed_rev
6998         = APR_ARRAY_IDX(changed_revs, 0, svn_revnum_t);
6999       svn_revnum_t oldest_changed_rev
7000         = APR_ARRAY_IDX(changed_revs, changed_revs->nelts - 1, svn_revnum_t);
7001 
7002       /* Now, copy from RANGES to *OPERATIVE_RANGES, filtering out ranges
7003          that aren't operative (by virtue of not having any revisions
7004          represented in the CHANGED_REVS array). */
7005       for (i = 0; i < ranges->nelts; i++)
7006         {
7007           svn_merge_range_t *range = APR_ARRAY_IDX(ranges, i,
7008                                                    svn_merge_range_t *);
7009           svn_revnum_t range_min = MIN(range->start, range->end) + 1;
7010           svn_revnum_t range_max = MAX(range->start, range->end);
7011           int j;
7012 
7013           /* If the merge range is entirely outside the range of changed
7014              revisions, we've no use for it. */
7015           if ((range_min > youngest_changed_rev)
7016               || (range_max < oldest_changed_rev))
7017             continue;
7018 
7019           /* Walk through the changed_revs to see if any of them fall
7020              inside our current range. */
7021           for (j = 0; j < changed_revs->nelts; j++)
7022             {
7023               svn_revnum_t changed_rev
7024                 = APR_ARRAY_IDX(changed_revs, j, svn_revnum_t);
7025               if ((changed_rev >= range_min) && (changed_rev <= range_max))
7026                 {
7027                   APR_ARRAY_PUSH(operative_ranges, svn_merge_range_t *) =
7028                     range;
7029                   break;
7030                 }
7031             }
7032         }
7033     }
7034 
7035   *operative_ranges_p = operative_ranges;
7036   return SVN_NO_ERROR;
7037 }
7038 
7039 
7040 /*-----------------------------------------------------------------------*/
7041 
7042 /*** Merge Source Normalization ***/
7043 
7044 /* qsort-compatible sort routine, rating merge_source_t * objects to
7045    be in descending (youngest-to-oldest) order based on their ->loc1->rev
7046    component. */
7047 static int
compare_merge_source_ts(const void * a,const void * b)7048 compare_merge_source_ts(const void *a,
7049                         const void *b)
7050 {
7051   svn_revnum_t a_rev = (*(const merge_source_t *const *)a)->loc1->rev;
7052   svn_revnum_t b_rev = (*(const merge_source_t *const *)b)->loc1->rev;
7053   if (a_rev == b_rev)
7054     return 0;
7055   return a_rev < b_rev ? 1 : -1;
7056 }
7057 
7058 /* Set *MERGE_SOURCE_TS_P to a list of merge sources generated by
7059    slicing history location SEGMENTS with a given requested merge
7060    RANGE.  Use SOURCE_LOC for full source URL calculation.
7061 
7062    Order the merge sources in *MERGE_SOURCE_TS_P from oldest to
7063    youngest. */
7064 static svn_error_t *
combine_range_with_segments(apr_array_header_t ** merge_source_ts_p,const svn_merge_range_t * range,const apr_array_header_t * segments,const svn_client__pathrev_t * source_loc,apr_pool_t * pool)7065 combine_range_with_segments(apr_array_header_t **merge_source_ts_p,
7066                             const svn_merge_range_t *range,
7067                             const apr_array_header_t *segments,
7068                             const svn_client__pathrev_t *source_loc,
7069                             apr_pool_t *pool)
7070 {
7071   apr_array_header_t *merge_source_ts =
7072     apr_array_make(pool, 1, sizeof(merge_source_t *));
7073   svn_revnum_t minrev = MIN(range->start, range->end) + 1;
7074   svn_revnum_t maxrev = MAX(range->start, range->end);
7075   svn_boolean_t subtractive = (range->start > range->end);
7076   int i;
7077 
7078   for (i = 0; i < segments->nelts; i++)
7079     {
7080       svn_location_segment_t *segment =
7081         APR_ARRAY_IDX(segments, i, svn_location_segment_t *);
7082       svn_client__pathrev_t *loc1, *loc2;
7083       merge_source_t *merge_source;
7084       const char *path1 = NULL;
7085       svn_revnum_t rev1;
7086 
7087       /* If this segment doesn't overlap our range at all, or
7088          represents a gap, ignore it. */
7089       if ((segment->range_end < minrev)
7090           || (segment->range_start > maxrev)
7091           || (! segment->path))
7092         continue;
7093 
7094       /* If our range spans a segment boundary, we have to point our
7095          merge_source_t's path1 to the path of the immediately older
7096          segment, else it points to the same location as its path2.  */
7097       rev1 = MAX(segment->range_start, minrev) - 1;
7098       if (minrev <= segment->range_start)
7099         {
7100           if (i > 0)
7101             {
7102               path1 = (APR_ARRAY_IDX(segments, i - 1,
7103                                      svn_location_segment_t *))->path;
7104             }
7105           /* If we've backed PATH1 up into a segment gap, let's back
7106              it up further still to the segment before the gap.  We'll
7107              have to adjust rev1, too. */
7108           if ((! path1) && (i > 1))
7109             {
7110               path1 = (APR_ARRAY_IDX(segments, i - 2,
7111                                      svn_location_segment_t *))->path;
7112               rev1 = (APR_ARRAY_IDX(segments, i - 2,
7113                                     svn_location_segment_t *))->range_end;
7114             }
7115         }
7116       else
7117         {
7118           path1 = apr_pstrdup(pool, segment->path);
7119         }
7120 
7121       /* If we don't have two valid paths, we won't know what to do
7122          when merging.  This could happen if someone requested a merge
7123          where the source didn't exist in a particular revision or
7124          something.  The merge code would probably bomb out anyway, so
7125          we'll just *not* create a merge source in this case. */
7126       if (! (path1 && segment->path))
7127         continue;
7128 
7129       /* Build our merge source structure. */
7130       loc1 = svn_client__pathrev_create_with_relpath(
7131                source_loc->repos_root_url, source_loc->repos_uuid,
7132                rev1, path1, pool);
7133       loc2 = svn_client__pathrev_create_with_relpath(
7134                source_loc->repos_root_url, source_loc->repos_uuid,
7135                MIN(segment->range_end, maxrev), segment->path, pool);
7136       /* If this is subtractive, reverse the whole calculation. */
7137       if (subtractive)
7138         merge_source = merge_source_create(loc2, loc1, TRUE /* ancestral */,
7139                                            pool);
7140       else
7141         merge_source = merge_source_create(loc1, loc2, TRUE /* ancestral */,
7142                                            pool);
7143 
7144       APR_ARRAY_PUSH(merge_source_ts, merge_source_t *) = merge_source;
7145     }
7146 
7147   /* If this was a subtractive merge, and we created more than one
7148      merge source, we need to reverse the sort ordering of our sources. */
7149   if (subtractive && (merge_source_ts->nelts > 1))
7150     svn_sort__array(merge_source_ts, compare_merge_source_ts);
7151 
7152   *merge_source_ts_p = merge_source_ts;
7153   return SVN_NO_ERROR;
7154 }
7155 
7156 /* Similar to normalize_merge_sources() except the input MERGE_RANGE_TS is a
7157  * rangelist.
7158  */
7159 static svn_error_t *
normalize_merge_sources_internal(apr_array_header_t ** merge_sources_p,const svn_client__pathrev_t * source_loc,const svn_rangelist_t * merge_range_ts,svn_ra_session_t * ra_session,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)7160 normalize_merge_sources_internal(apr_array_header_t **merge_sources_p,
7161                                  const svn_client__pathrev_t *source_loc,
7162                                  const svn_rangelist_t *merge_range_ts,
7163                                  svn_ra_session_t *ra_session,
7164                                  svn_client_ctx_t *ctx,
7165                                  apr_pool_t *result_pool,
7166                                  apr_pool_t *scratch_pool)
7167 {
7168   svn_revnum_t source_peg_revnum = source_loc->rev;
7169   svn_revnum_t oldest_requested, youngest_requested;
7170   svn_revnum_t trim_revision = SVN_INVALID_REVNUM;
7171   apr_array_header_t *segments;
7172   int i;
7173 
7174   /* Initialize our return variable. */
7175   *merge_sources_p = apr_array_make(result_pool, 1, sizeof(merge_source_t *));
7176 
7177   /* No ranges to merge?  No problem. */
7178   if (merge_range_ts->nelts == 0)
7179     return SVN_NO_ERROR;
7180 
7181   /* Find the extremes of the revisions across our set of ranges. */
7182   merge_range_find_extremes(&oldest_requested, &youngest_requested,
7183                             merge_range_ts);
7184 
7185   /* ### FIXME:  Our underlying APIs can't yet handle the case where
7186      the peg revision isn't the youngest of the three revisions.  So
7187      we'll just verify that the source in the peg revision is related
7188      to the source in the youngest requested revision (which is
7189      all the underlying APIs would do in this case right now anyway). */
7190   if (source_peg_revnum < youngest_requested)
7191     {
7192       svn_client__pathrev_t *start_loc;
7193 
7194       SVN_ERR(svn_client__repos_location(&start_loc,
7195                                          ra_session, source_loc,
7196                                          youngest_requested,
7197                                          ctx, scratch_pool, scratch_pool));
7198       source_peg_revnum = youngest_requested;
7199     }
7200 
7201   /* Fetch the locations for our merge range span. */
7202   SVN_ERR(svn_client__repos_location_segments(&segments,
7203                                               ra_session, source_loc->url,
7204                                               source_peg_revnum,
7205                                               youngest_requested,
7206                                               oldest_requested,
7207                                               ctx, result_pool));
7208 
7209   /* See if we fetched enough history to do the job.  "Surely we did,"
7210      you say.  "After all, we covered the entire requested merge
7211      range."  Yes, that's true, but if our first segment doesn't
7212      extend back to the oldest request revision, we've got a special
7213      case to deal with.  Or if the first segment represents a gap,
7214      that's another special case.  */
7215   trim_revision = SVN_INVALID_REVNUM;
7216   if (segments->nelts)
7217     {
7218       svn_location_segment_t *first_segment =
7219         APR_ARRAY_IDX(segments, 0, svn_location_segment_t *);
7220 
7221       /* If the first segment doesn't start with the OLDEST_REQUESTED
7222          revision, we'll need to pass a trim revision to our range
7223          cruncher. */
7224       if (first_segment->range_start != oldest_requested)
7225         {
7226           trim_revision = first_segment->range_start;
7227         }
7228 
7229       /* Else, if the first segment has no path (and therefore is a
7230          gap), then we'll fetch the copy source revision from the
7231          second segment (provided there is one, of course) and use it
7232          to prepend an extra pathful segment to our list.
7233 
7234          ### We could avoid this bit entirely if we'd passed
7235          ### SVN_INVALID_REVNUM instead of OLDEST_REQUESTED to
7236          ### svn_client__repos_location_segments(), but that would
7237          ### really penalize clients hitting pre-1.5 repositories with
7238          ### the typical small merge range request (because of the
7239          ### lack of a node-origins cache in the repository).  */
7240       else if (! first_segment->path)
7241         {
7242           if (segments->nelts > 1)
7243             {
7244               svn_location_segment_t *second_segment =
7245                 APR_ARRAY_IDX(segments, 1, svn_location_segment_t *);
7246               const char *segment_url;
7247               const char *original_repos_relpath;
7248               svn_revnum_t original_revision;
7249               svn_opt_revision_t range_start_rev;
7250               range_start_rev.kind = svn_opt_revision_number;
7251               range_start_rev.value.number = second_segment->range_start;
7252 
7253               segment_url = svn_path_url_add_component2(
7254                               source_loc->repos_root_url, second_segment->path,
7255                               scratch_pool);
7256               SVN_ERR(svn_client__get_copy_source(&original_repos_relpath,
7257                                                   &original_revision,
7258                                                   segment_url,
7259                                                   &range_start_rev,
7260                                                   ra_session, ctx,
7261                                                   result_pool, scratch_pool));
7262               /* Got copyfrom data?  Fix up the first segment to cover
7263                  back to COPYFROM_REV + 1, and then prepend a new
7264                  segment covering just COPYFROM_REV. */
7265               if (original_repos_relpath)
7266                 {
7267                   svn_location_segment_t *new_segment =
7268                     apr_pcalloc(result_pool, sizeof(*new_segment));
7269 
7270                   new_segment->path = original_repos_relpath;
7271                   new_segment->range_start = original_revision;
7272                   new_segment->range_end = original_revision;
7273                   SVN_ERR(svn_sort__array_insert2(segments, &new_segment, 0));
7274                 }
7275             }
7276         }
7277     }
7278 
7279   /* For each range in our requested range set, try to determine the
7280      path(s) associated with that range.  */
7281   for (i = 0; i < merge_range_ts->nelts; i++)
7282     {
7283       svn_merge_range_t *range =
7284         APR_ARRAY_IDX(merge_range_ts, i, svn_merge_range_t *);
7285       apr_array_header_t *merge_sources;
7286 
7287       if (SVN_IS_VALID_REVNUM(trim_revision))
7288         {
7289           /* If the range predates the trim revision, discard it. */
7290           if (MAX(range->start, range->end) < trim_revision)
7291             continue;
7292 
7293           /* If the range overlaps the trim revision, trim it. */
7294           if (range->start < trim_revision)
7295             range->start = trim_revision;
7296           if (range->end < trim_revision)
7297             range->end = trim_revision;
7298         }
7299 
7300       /* Copy the resulting merge sources into master list thereof. */
7301       SVN_ERR(combine_range_with_segments(&merge_sources, range,
7302                                           segments, source_loc,
7303                                           result_pool));
7304       apr_array_cat(*merge_sources_p, merge_sources);
7305     }
7306 
7307   return SVN_NO_ERROR;
7308 }
7309 
7310 /* Determine the normalized ranges to merge from a given line of history.
7311 
7312    Calculate the result by intersecting the list of location segments at
7313    which SOURCE_LOC existed along its line of history with the requested
7314    revision ranges in RANGES_TO_MERGE.  RANGES_TO_MERGE is an array of
7315    (svn_opt_revision_range_t *) revision ranges.  Use SOURCE_PATH_OR_URL to
7316    resolve any WC-relative revision specifiers (such as 'base') in
7317    RANGES_TO_MERGE.
7318 
7319    Set *MERGE_SOURCES_P to an array of merge_source_t * objects, each
7320    describing a normalized range of revisions to be merged from the line
7321    history of SOURCE_LOC.  Order the objects from oldest to youngest.
7322 
7323    RA_SESSION is an RA session open to the repository of SOURCE_LOC; it may
7324    be temporarily reparented within this function.  Use RA_SESSION to find
7325    the location segments along the line of history of SOURCE_LOC.
7326 
7327    Allocate MERGE_SOURCES_P and its contents in RESULT_POOL.
7328 
7329    See `MERGEINFO MERGE SOURCE NORMALIZATION' for more on the
7330    background of this function.
7331 */
7332 static svn_error_t *
normalize_merge_sources(apr_array_header_t ** merge_sources_p,const char * source_path_or_url,const svn_client__pathrev_t * source_loc,const apr_array_header_t * ranges_to_merge,svn_ra_session_t * ra_session,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)7333 normalize_merge_sources(apr_array_header_t **merge_sources_p,
7334                         const char *source_path_or_url,
7335                         const svn_client__pathrev_t *source_loc,
7336                         const apr_array_header_t *ranges_to_merge,
7337                         svn_ra_session_t *ra_session,
7338                         svn_client_ctx_t *ctx,
7339                         apr_pool_t *result_pool,
7340                         apr_pool_t *scratch_pool)
7341 {
7342   const char *source_abspath_or_url;
7343   svn_revnum_t youngest_rev = SVN_INVALID_REVNUM;
7344   svn_rangelist_t *merge_range_ts;
7345   int i;
7346   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
7347 
7348   if(!svn_path_is_url(source_path_or_url))
7349     SVN_ERR(svn_dirent_get_absolute(&source_abspath_or_url, source_path_or_url,
7350                                     scratch_pool));
7351   else
7352     source_abspath_or_url = source_path_or_url;
7353 
7354   /* Create a list to hold svn_merge_range_t's. */
7355   merge_range_ts = apr_array_make(scratch_pool, ranges_to_merge->nelts,
7356                                   sizeof(svn_merge_range_t *));
7357 
7358   for (i = 0; i < ranges_to_merge->nelts; i++)
7359     {
7360       svn_opt_revision_range_t *range
7361         = APR_ARRAY_IDX(ranges_to_merge, i, svn_opt_revision_range_t *);
7362       svn_merge_range_t mrange;
7363 
7364       svn_pool_clear(iterpool);
7365 
7366       /* Resolve revisions to real numbers, validating as we go. */
7367       if ((range->start.kind == svn_opt_revision_unspecified)
7368           || (range->end.kind == svn_opt_revision_unspecified))
7369         return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL,
7370                                 _("Not all required revisions are specified"));
7371 
7372       SVN_ERR(svn_client__get_revision_number(&mrange.start, &youngest_rev,
7373                                               ctx->wc_ctx,
7374                                               source_abspath_or_url,
7375                                               ra_session, &range->start,
7376                                               iterpool));
7377       SVN_ERR(svn_client__get_revision_number(&mrange.end, &youngest_rev,
7378                                               ctx->wc_ctx,
7379                                               source_abspath_or_url,
7380                                               ra_session, &range->end,
7381                                               iterpool));
7382 
7383       /* If this isn't a no-op range... */
7384       if (mrange.start != mrange.end)
7385         {
7386           /* ...then add it to the list. */
7387           mrange.inheritable = TRUE;
7388           APR_ARRAY_PUSH(merge_range_ts, svn_merge_range_t *)
7389             = svn_merge_range_dup(&mrange, scratch_pool);
7390         }
7391     }
7392 
7393   SVN_ERR(normalize_merge_sources_internal(
7394             merge_sources_p, source_loc,
7395             merge_range_ts, ra_session, ctx, result_pool, scratch_pool));
7396 
7397   svn_pool_destroy(iterpool);
7398   return SVN_NO_ERROR;
7399 }
7400 
7401 
7402 /*-----------------------------------------------------------------------*/
7403 
7404 /*** Merge Workhorse Functions ***/
7405 
7406 /* Helper for do_directory_merge() and do_file_merge() which filters out a
7407    path's own natural history from the mergeinfo describing a merge.
7408 
7409    Given the natural history IMPLICIT_MERGEINFO of some wc merge target path,
7410    the repository-relative merge source path SOURCE_REL_PATH, and the
7411    requested merge range REQUESTED_RANGE from SOURCE_REL_PATH, remove any
7412    portion of REQUESTED_RANGE which is already described in
7413    IMPLICIT_MERGEINFO.  Store the result in *FILTERED_RANGELIST.
7414 
7415    This function only filters natural history for mergeinfo that will be
7416    *added* during a forward merge.  Removing natural history from explicit
7417    mergeinfo is harmless.  If REQUESTED_RANGE describes a reverse merge,
7418    then *FILTERED_RANGELIST is simply populated with one range described
7419    by REQUESTED_RANGE.  *FILTERED_RANGELIST is never NULL.
7420 
7421    Allocate *FILTERED_RANGELIST in POOL. */
7422 static svn_error_t *
filter_natural_history_from_mergeinfo(svn_rangelist_t ** filtered_rangelist,const char * source_rel_path,svn_mergeinfo_t implicit_mergeinfo,svn_merge_range_t * requested_range,apr_pool_t * pool)7423 filter_natural_history_from_mergeinfo(svn_rangelist_t **filtered_rangelist,
7424                                       const char *source_rel_path,
7425                                       svn_mergeinfo_t implicit_mergeinfo,
7426                                       svn_merge_range_t *requested_range,
7427                                       apr_pool_t *pool)
7428 {
7429   /* Make the REQUESTED_RANGE into a rangelist. */
7430   svn_rangelist_t *requested_rangelist =
7431     svn_rangelist__initialize(requested_range->start, requested_range->end,
7432                               requested_range->inheritable, pool);
7433 
7434   *filtered_rangelist = NULL;
7435 
7436   /* For forward merges: If the IMPLICIT_MERGEINFO already describes ranges
7437      associated with SOURCE_REL_PATH then filter those ranges out. */
7438   if (implicit_mergeinfo
7439       && (requested_range->start < requested_range->end))
7440     {
7441       svn_rangelist_t *implied_rangelist =
7442                         svn_hash_gets(implicit_mergeinfo, source_rel_path);
7443 
7444       if (implied_rangelist)
7445         SVN_ERR(svn_rangelist_remove(filtered_rangelist,
7446                                      implied_rangelist,
7447                                      requested_rangelist,
7448                                      FALSE, pool));
7449     }
7450 
7451   /* If no filtering was performed the filtered rangelist is
7452      simply the requested rangelist.*/
7453   if (! (*filtered_rangelist))
7454     *filtered_rangelist = requested_rangelist;
7455 
7456   return SVN_NO_ERROR;
7457 }
7458 
7459 /* Return a merge source representing the sub-range from START_REV to
7460    END_REV of SOURCE.  SOURCE obeys the rules described in the
7461    'MERGEINFO MERGE SOURCE NORMALIZATION' comment at the top of this file.
7462    The younger of START_REV and END_REV is inclusive while the older is
7463    exclusive.
7464 
7465    Allocate the result structure in POOL but leave the URLs in it as shallow
7466    copies of the URLs in SOURCE.
7467 */
7468 static merge_source_t *
subrange_source(const merge_source_t * source,svn_revnum_t start_rev,svn_revnum_t end_rev,apr_pool_t * pool)7469 subrange_source(const merge_source_t *source,
7470                 svn_revnum_t start_rev,
7471                 svn_revnum_t end_rev,
7472                 apr_pool_t *pool)
7473 {
7474   svn_boolean_t is_rollback = (source->loc1->rev > source->loc2->rev);
7475   svn_boolean_t same_urls = (strcmp(source->loc1->url, source->loc2->url) == 0);
7476   svn_client__pathrev_t loc1 = *source->loc1;
7477   svn_client__pathrev_t loc2 = *source->loc2;
7478 
7479   /* For this function we require that the input source is 'ancestral'. */
7480   SVN_ERR_ASSERT_NO_RETURN(source->ancestral);
7481   SVN_ERR_ASSERT_NO_RETURN(start_rev != end_rev);
7482 
7483   loc1.rev = start_rev;
7484   loc2.rev = end_rev;
7485   if (! same_urls)
7486     {
7487       if (is_rollback && (end_rev != source->loc2->rev))
7488         {
7489           loc2.url = source->loc1->url;
7490         }
7491       if ((! is_rollback) && (start_rev != source->loc1->rev))
7492         {
7493           loc1.url = source->loc2->url;
7494         }
7495     }
7496   return merge_source_create(&loc1, &loc2, source->ancestral, pool);
7497 }
7498 
7499 /* The single-file, simplified version of do_directory_merge(), which see for
7500    parameter descriptions.
7501 
7502    Additional parameters:
7503 
7504    If SOURCES_RELATED is set, the "left" and "right" sides of SOURCE are
7505    historically related (ancestors, uncles, second
7506    cousins thrice removed, etc...).  (This is used to simulate the
7507    history checks that the repository logic does in the directory case.)
7508 
7509    If mergeinfo is being recorded to describe this merge, and RESULT_CATALOG
7510    is not NULL, then don't record the new mergeinfo on the TARGET_ABSPATH,
7511    but instead record it in RESULT_CATALOG, where the key is TARGET_ABSPATH
7512    and the value is the new mergeinfo for that path.  Allocate additions
7513    to RESULT_CATALOG in pool which RESULT_CATALOG was created in.
7514 
7515    CONFLICTED_RANGE is as documented for do_directory_merge().
7516 
7517    Note: MERGE_B->RA_SESSION1 must be associated with SOURCE->loc1->url and
7518    MERGE_B->RA_SESSION2 with SOURCE->loc2->url.
7519 */
7520 static svn_error_t *
do_file_merge(svn_mergeinfo_catalog_t result_catalog,single_range_conflict_report_t ** conflict_report,const merge_source_t * source,const char * target_abspath,const svn_diff_tree_processor_t * processor,svn_boolean_t sources_related,svn_boolean_t squelch_mergeinfo_notifications,merge_cmd_baton_t * merge_b,apr_pool_t * result_pool,apr_pool_t * scratch_pool)7521 do_file_merge(svn_mergeinfo_catalog_t result_catalog,
7522               single_range_conflict_report_t **conflict_report,
7523               const merge_source_t *source,
7524               const char *target_abspath,
7525               const svn_diff_tree_processor_t *processor,
7526               svn_boolean_t sources_related,
7527               svn_boolean_t squelch_mergeinfo_notifications,
7528               merge_cmd_baton_t *merge_b,
7529               apr_pool_t *result_pool,
7530               apr_pool_t *scratch_pool)
7531 {
7532   svn_rangelist_t *remaining_ranges;
7533   svn_client_ctx_t *ctx = merge_b->ctx;
7534   svn_merge_range_t range;
7535   svn_mergeinfo_t target_mergeinfo;
7536   svn_boolean_t inherited = FALSE;
7537   svn_boolean_t is_rollback = (source->loc1->rev > source->loc2->rev);
7538   const svn_client__pathrev_t *primary_src
7539     = is_rollback ? source->loc1 : source->loc2;
7540   svn_boolean_t honor_mergeinfo = HONOR_MERGEINFO(merge_b);
7541   svn_client__merge_path_t *merge_target = NULL;
7542   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
7543 
7544   SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath));
7545 
7546   *conflict_report = NULL;
7547 
7548   /* Note that this is a single-file merge. */
7549   range.start = source->loc1->rev;
7550   range.end = source->loc2->rev;
7551   range.inheritable = TRUE;
7552 
7553   merge_target = svn_client__merge_path_create(target_abspath, scratch_pool);
7554 
7555   if (honor_mergeinfo)
7556     {
7557       svn_error_t *err;
7558 
7559       /* Fetch mergeinfo. */
7560       err = get_full_mergeinfo(&target_mergeinfo,
7561                                &(merge_target->implicit_mergeinfo),
7562                                &inherited, svn_mergeinfo_inherited,
7563                                merge_b->ra_session1, target_abspath,
7564                                MAX(source->loc1->rev, source->loc2->rev),
7565                                MIN(source->loc1->rev, source->loc2->rev),
7566                                ctx, scratch_pool, iterpool);
7567 
7568       if (err)
7569         {
7570           if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
7571             {
7572               err = svn_error_createf(
7573                 SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING, err,
7574                 _("Invalid mergeinfo detected on merge target '%s', "
7575                   "merge tracking not possible"),
7576                 svn_dirent_local_style(target_abspath, scratch_pool));
7577             }
7578           return svn_error_trace(err);
7579         }
7580 
7581       /* Calculate remaining merges unless this is a record only merge.
7582          In that case the remaining range is the whole range described
7583          by SOURCE->rev1:rev2. */
7584       if (!merge_b->record_only)
7585         {
7586           /* ### Bug?  calculate_remaining_ranges() needs 'source' to adhere
7587            *   to the requirements of 'MERGEINFO MERGE SOURCE NORMALIZATION'
7588            *   here, but it doesn't appear to be guaranteed so. */
7589           SVN_ERR(calculate_remaining_ranges(NULL, merge_target,
7590                                              source,
7591                                              target_mergeinfo,
7592                                              merge_b->implicit_src_gap, FALSE,
7593                                              merge_b->ra_session1,
7594                                              ctx, scratch_pool,
7595                                              iterpool));
7596           remaining_ranges = merge_target->remaining_ranges;
7597 
7598           /* We are honoring mergeinfo and this is not a simple record only
7599              merge which blindly records mergeinfo describing the merge of
7600              SOURCE->LOC1->URL@SOURCE->LOC1->REV through
7601              SOURCE->LOC2->URL@SOURCE->LOC2->REV.  This means that the oldest
7602              and youngest revisions merged (as determined above by
7603              calculate_remaining_ranges) might differ from those described
7604              in SOURCE.  To keep the '--- Merging *' notifications consistent
7605              with the '--- Recording mergeinfo *' notifications, we adjust
7606              RANGE to account for such changes. */
7607           if (remaining_ranges->nelts)
7608             {
7609               svn_merge_range_t *adj_start_range =
7610                 APR_ARRAY_IDX(remaining_ranges, 0, svn_merge_range_t *);
7611               svn_merge_range_t *adj_end_range =
7612                 APR_ARRAY_IDX(remaining_ranges, remaining_ranges->nelts - 1,
7613                               svn_merge_range_t *);
7614               range.start = adj_start_range->start;
7615               range.end = adj_end_range->end;
7616             }
7617         }
7618     }
7619 
7620   /* The simple cases where our remaining range is SOURCE->rev1:rev2. */
7621   if (!honor_mergeinfo || merge_b->record_only)
7622     {
7623       remaining_ranges = apr_array_make(scratch_pool, 1, sizeof(&range));
7624       APR_ARRAY_PUSH(remaining_ranges, svn_merge_range_t *) = &range;
7625     }
7626 
7627   if (!merge_b->record_only)
7628     {
7629       svn_rangelist_t *ranges_to_merge = apr_array_copy(scratch_pool,
7630                                                         remaining_ranges);
7631       const char *target_relpath = "";  /* relative to root of merge */
7632 
7633       if (source->ancestral)
7634         {
7635           apr_array_header_t *child_with_mergeinfo;
7636           svn_client__merge_path_t *target_info;
7637 
7638           /* If we have ancestrally related sources and more than one
7639              range to merge, eliminate no-op ranges before going through
7640              the effort of downloading the many copies of the file
7641              required to do these merges (two copies per range). */
7642           if (remaining_ranges->nelts > 1)
7643             {
7644               const char *old_sess_url;
7645               svn_error_t *err;
7646 
7647               SVN_ERR(svn_client__ensure_ra_session_url(&old_sess_url,
7648                                                         merge_b->ra_session1,
7649                                                         primary_src->url,
7650                                                         iterpool));
7651               err = remove_noop_merge_ranges(&ranges_to_merge,
7652                                              merge_b->ra_session1,
7653                                              remaining_ranges, scratch_pool);
7654               SVN_ERR(svn_error_compose_create(
7655                         err, svn_ra_reparent(merge_b->ra_session1,
7656                                              old_sess_url, iterpool)));
7657             }
7658 
7659           /* To support notify_merge_begin() initialize our
7660              CHILD_WITH_MERGEINFO. See the comment
7661              'THE CHILDREN_WITH_MERGEINFO ARRAY' at the start of this file. */
7662 
7663           child_with_mergeinfo = apr_array_make(scratch_pool, 1,
7664                                         sizeof(svn_client__merge_path_t *));
7665 
7666           /* ### Create a fake copy of merge_target as we don't keep
7667                  remaining_ranges in sync (yet). */
7668           target_info = svn_client__merge_path_create(merge_target->abspath,
7669                                                       scratch_pool);
7670           target_info->remaining_ranges = ranges_to_merge;
7671 
7672           APR_ARRAY_PUSH(child_with_mergeinfo, svn_client__merge_path_t *)
7673                                     = target_info;
7674 
7675           /* And store in baton to allow using it from notify_merge_begin() */
7676           merge_b->children_with_mergeinfo = child_with_mergeinfo;
7677         }
7678 
7679       while (ranges_to_merge->nelts > 0)
7680         {
7681           svn_merge_range_t *r = APR_ARRAY_IDX(ranges_to_merge, 0,
7682                                                svn_merge_range_t *);
7683           const merge_source_t *real_source;
7684           const char *left_file, *right_file;
7685           apr_hash_t *left_props, *right_props;
7686           const svn_diff_source_t *left_source;
7687           const svn_diff_source_t *right_source;
7688 
7689           svn_pool_clear(iterpool);
7690 
7691           /* Ensure any subsequent drives gets their own notification. */
7692           merge_b->notify_begin.last_abspath = NULL;
7693 
7694           /* While we currently don't allow it, in theory we could be
7695              fetching two fulltexts from two different repositories here. */
7696           if (source->ancestral)
7697             real_source = subrange_source(source, r->start, r->end, iterpool);
7698           else
7699             real_source = source;
7700           SVN_ERR(single_file_merge_get_file(&left_file, &left_props,
7701                                              merge_b->ra_session1,
7702                                              real_source->loc1,
7703                                              target_abspath,
7704                                              iterpool, iterpool));
7705           SVN_ERR(single_file_merge_get_file(&right_file, &right_props,
7706                                              merge_b->ra_session2,
7707                                              real_source->loc2,
7708                                              target_abspath,
7709                                              iterpool, iterpool));
7710           /* Calculate sources for the diff processor */
7711           left_source = svn_diff__source_create(r->start, iterpool);
7712           right_source = svn_diff__source_create(r->end, iterpool);
7713 
7714 
7715           /* If the sources are related or we're ignoring ancestry in diffs,
7716              do a text-n-props merge; otherwise, do a delete-n-add merge. */
7717           if (! (merge_b->diff_ignore_ancestry || sources_related))
7718             {
7719               void *dir_baton = open_dir_for_replace_single_file(iterpool);
7720               void *file_baton;
7721               svn_boolean_t skip;
7722 
7723               /* Delete... */
7724               file_baton = NULL;
7725               skip = FALSE;
7726               SVN_ERR(processor->file_opened(&file_baton, &skip, target_relpath,
7727                                              left_source,
7728                                              NULL /* right_source */,
7729                                              NULL /* copyfrom_source */,
7730                                              dir_baton,
7731                                              processor,
7732                                              iterpool, iterpool));
7733               if (! skip)
7734                 SVN_ERR(processor->file_deleted(target_relpath,
7735                                                 left_source,
7736                                                 left_file,
7737                                                 left_props,
7738                                                 file_baton,
7739                                                 processor,
7740                                                 iterpool));
7741 
7742               /* ...plus add... */
7743               file_baton = NULL;
7744               skip = FALSE;
7745               SVN_ERR(processor->file_opened(&file_baton, &skip, target_relpath,
7746                                              NULL /* left_source */,
7747                                              right_source,
7748                                              NULL /* copyfrom_source */,
7749                                              dir_baton,
7750                                              processor,
7751                                              iterpool, iterpool));
7752               if (! skip)
7753                 SVN_ERR(processor->file_added(target_relpath,
7754                                               NULL /* copyfrom_source */,
7755                                               right_source,
7756                                               NULL /* copyfrom_file */,
7757                                               right_file,
7758                                               NULL /* copyfrom_props */,
7759                                               right_props,
7760                                               file_baton,
7761                                               processor,
7762                                               iterpool));
7763               /* ... equals replace. */
7764             }
7765           else
7766             {
7767               void *file_baton = NULL;
7768               svn_boolean_t skip = FALSE;
7769               apr_array_header_t *propchanges;
7770 
7771 
7772               /* Deduce property diffs. */
7773               SVN_ERR(svn_prop_diffs(&propchanges, right_props, left_props,
7774                                      iterpool));
7775 
7776               SVN_ERR(processor->file_opened(&file_baton, &skip, target_relpath,
7777                                              left_source,
7778                                              right_source,
7779                                              NULL /* copyfrom_source */,
7780                                              NULL /* dir_baton */,
7781                                              processor,
7782                                              iterpool, iterpool));
7783               if (! skip)
7784                 SVN_ERR(processor->file_changed(target_relpath,
7785                                               left_source,
7786                                               right_source,
7787                                               left_file,
7788                                               right_file,
7789                                               left_props,
7790                                               right_props,
7791                                               TRUE /* file changed */,
7792                                               propchanges,
7793                                               file_baton,
7794                                               processor,
7795                                               iterpool));
7796             }
7797 
7798           if (is_path_conflicted_by_merge(merge_b))
7799             {
7800               merge_source_t *remaining_range = NULL;
7801 
7802               if (real_source->loc2->rev != source->loc2->rev)
7803                 remaining_range = subrange_source(source,
7804                                                   real_source->loc2->rev,
7805                                                   source->loc2->rev,
7806                                                   scratch_pool);
7807               *conflict_report = single_range_conflict_report_create(
7808                                    real_source, remaining_range, result_pool);
7809 
7810               /* Only record partial mergeinfo if only a partial merge was
7811                  performed before a conflict was encountered. */
7812               range.end = r->end;
7813               break;
7814             }
7815 
7816           /* Now delete the just merged range from the hash
7817              (This list is used from notify_merge_begin)
7818 
7819             Directory merges use remove_first_range_from_remaining_ranges() */
7820           SVN_ERR(svn_sort__array_delete2(ranges_to_merge, 0, 1));
7821         }
7822       merge_b->notify_begin.last_abspath = NULL;
7823     } /* !merge_b->record_only */
7824 
7825   /* Record updated WC mergeinfo to account for our new merges, minus
7826      any unresolved conflicts and skips.  We use the original
7827      REMAINING_RANGES here because we want to record all the requested
7828      merge ranges, include the noop ones.  */
7829   if (RECORD_MERGEINFO(merge_b) && remaining_ranges->nelts)
7830     {
7831       const char *mergeinfo_path = svn_client__pathrev_fspath(primary_src,
7832                                                               scratch_pool);
7833       svn_rangelist_t *filtered_rangelist;
7834 
7835       /* Filter any ranges from TARGET_WCPATH's own history, there is no
7836          need to record this explicitly in mergeinfo, it is already part
7837          of TARGET_WCPATH's natural history (implicit mergeinfo). */
7838       SVN_ERR(filter_natural_history_from_mergeinfo(
7839         &filtered_rangelist,
7840         mergeinfo_path,
7841         merge_target->implicit_mergeinfo,
7842         &range,
7843         iterpool));
7844 
7845       /* Only record mergeinfo if there is something other than
7846          self-referential mergeinfo, but don't record mergeinfo if
7847          TARGET_WCPATH was skipped. */
7848       if (filtered_rangelist->nelts
7849           && (apr_hash_count(merge_b->skipped_abspaths) == 0))
7850         {
7851           apr_hash_t *merges = apr_hash_make(iterpool);
7852 
7853           /* If merge target has inherited mergeinfo set it before
7854              recording the first merge range. */
7855           if (inherited)
7856             SVN_ERR(svn_client__record_wc_mergeinfo(target_abspath,
7857                                                     target_mergeinfo,
7858                                                     FALSE, ctx,
7859                                                     iterpool));
7860 
7861           svn_hash_sets(merges, target_abspath, filtered_rangelist);
7862 
7863           if (!squelch_mergeinfo_notifications)
7864             {
7865               /* Notify that we are recording mergeinfo describing a merge. */
7866               svn_merge_range_t n_range;
7867 
7868               SVN_ERR(svn_mergeinfo__get_range_endpoints(
7869                         &n_range.end, &n_range.start, merges, iterpool));
7870               n_range.inheritable = TRUE;
7871               notify_mergeinfo_recording(target_abspath, &n_range,
7872                                          merge_b->ctx, iterpool);
7873             }
7874 
7875           SVN_ERR(update_wc_mergeinfo(result_catalog, target_abspath,
7876                                       mergeinfo_path, merges, is_rollback,
7877                                       ctx, iterpool));
7878         }
7879     }
7880 
7881   merge_b->children_with_mergeinfo = NULL;
7882 
7883   svn_pool_destroy(iterpool);
7884 
7885   return SVN_NO_ERROR;
7886 }
7887 
7888 /* Helper for do_directory_merge() to handle the case where a merge editor
7889    drive adds explicit mergeinfo to a path which didn't have any explicit
7890    mergeinfo previously.
7891 
7892    MERGE_B is cascaded from the argument of the same
7893    name in do_directory_merge().  Should be called only after
7894    do_directory_merge() has called populate_remaining_ranges() and populated
7895    the remaining_ranges field of each child in
7896    CHILDREN_WITH_MERGEINFO (i.e. the remaining_ranges fields can be
7897    empty but never NULL).
7898 
7899    If MERGE_B->DRY_RUN is true do nothing, if it is false then
7900    for each path (if any) in MERGE_B->PATHS_WITH_NEW_MERGEINFO merge that
7901    path's inherited mergeinfo (if any) with its working explicit mergeinfo
7902    and set that as the path's new explicit mergeinfo.  Then add an
7903    svn_client__merge_path_t * element representing the path to
7904    CHILDREN_WITH_MERGEINFO if it isn't already present.  All fields
7905    in any elements added to CHILDREN_WITH_MERGEINFO are initialized
7906    to FALSE/NULL with the exception of 'path' and 'remaining_ranges'.  The
7907    latter is set to a rangelist equal to the remaining_ranges of the path's
7908    nearest path-wise ancestor in CHILDREN_WITH_MERGEINFO.
7909 
7910    Any elements added to CHILDREN_WITH_MERGEINFO are allocated
7911    in POOL. */
7912 static svn_error_t *
process_children_with_new_mergeinfo(merge_cmd_baton_t * merge_b,apr_array_header_t * children_with_mergeinfo,apr_pool_t * pool)7913 process_children_with_new_mergeinfo(merge_cmd_baton_t *merge_b,
7914                                     apr_array_header_t *children_with_mergeinfo,
7915                                     apr_pool_t *pool)
7916 {
7917   apr_pool_t *iterpool;
7918   apr_array_header_t *a;
7919   int i;
7920 
7921   if (!merge_b->paths_with_new_mergeinfo || merge_b->dry_run)
7922     return SVN_NO_ERROR;
7923 
7924   /* Iterate over each path with explicit mergeinfo added by the merge. */
7925   /* Iterate over the paths in a parent-to-child order so that inherited
7926    * mergeinfo is propagated consistently from each parent path to its
7927    * children. (Issue #4862) */
7928   a = svn_sort__hash(merge_b->paths_with_new_mergeinfo,
7929                      svn_sort_compare_items_as_paths, pool);
7930   iterpool = svn_pool_create(pool);
7931   for (i = 0; i < a->nelts; i++)
7932     {
7933       svn_sort__item_t *item = &APR_ARRAY_IDX(a, i, svn_sort__item_t);
7934       const char *abspath_with_new_mergeinfo = item->key;
7935       svn_mergeinfo_t path_inherited_mergeinfo;
7936       svn_mergeinfo_t path_explicit_mergeinfo;
7937       svn_client__merge_path_t *new_child;
7938 
7939       svn_pool_clear(iterpool);
7940 
7941       /* Note: We could skip recording inherited mergeinfo here if this path
7942          was added (with preexisting mergeinfo) by the merge.  That's actually
7943          more correct, since the inherited mergeinfo likely describes
7944          non-existent or unrelated merge history, but it's not quite so simple
7945          as that, see https://issues.apache.org/jira/browse/SVN-4309
7946          */
7947 
7948       /* Get the path's new explicit mergeinfo... */
7949       SVN_ERR(svn_client__get_wc_mergeinfo(&path_explicit_mergeinfo, NULL,
7950                                            svn_mergeinfo_explicit,
7951                                            abspath_with_new_mergeinfo,
7952                                            NULL, NULL, FALSE,
7953                                            merge_b->ctx,
7954                                            iterpool, iterpool));
7955       /* ...there *should* always be explicit mergeinfo at this point
7956          but you can't be too careful. */
7957       if (path_explicit_mergeinfo)
7958         {
7959           /* Get the mergeinfo the path would have inherited before
7960              the merge. */
7961           SVN_ERR(svn_client__get_wc_or_repos_mergeinfo(
7962             &path_inherited_mergeinfo,
7963             NULL, NULL,
7964             FALSE,
7965             svn_mergeinfo_nearest_ancestor, /* We only want inherited MI */
7966             merge_b->ra_session2,
7967             abspath_with_new_mergeinfo,
7968             merge_b->ctx,
7969             iterpool));
7970 
7971           /* If the path inherited any mergeinfo then merge that with the
7972              explicit mergeinfo and record the result as the path's new
7973              explicit mergeinfo. */
7974           if (path_inherited_mergeinfo)
7975             {
7976               SVN_ERR(svn_mergeinfo_merge2(path_explicit_mergeinfo,
7977                                            path_inherited_mergeinfo,
7978                                            iterpool, iterpool));
7979               SVN_ERR(svn_client__record_wc_mergeinfo(
7980                                           abspath_with_new_mergeinfo,
7981                                           path_explicit_mergeinfo,
7982                                           FALSE, merge_b->ctx, iterpool));
7983             }
7984 
7985           /* If the path is not in CHILDREN_WITH_MERGEINFO then add it. */
7986           new_child =
7987             get_child_with_mergeinfo(children_with_mergeinfo,
7988                                      abspath_with_new_mergeinfo);
7989           if (!new_child)
7990             {
7991               const svn_client__merge_path_t *parent
7992                 = find_nearest_ancestor(children_with_mergeinfo,
7993                                         FALSE, abspath_with_new_mergeinfo);
7994               new_child
7995                 = svn_client__merge_path_create(abspath_with_new_mergeinfo,
7996                                                 pool);
7997 
7998               /* If path_with_new_mergeinfo is the merge target itself
7999                  then it should already be in
8000                  CHILDREN_WITH_MERGEINFO per the criteria of
8001                  get_mergeinfo_paths() and we shouldn't be in this block.
8002                  If path_with_new_mergeinfo is a subtree then it must have
8003                  a parent in CHILDREN_WITH_MERGEINFO if only
8004                  the merge target itself...so if we don't find a parent
8005                  the caller has done something quite wrong. */
8006               SVN_ERR_ASSERT(parent);
8007               SVN_ERR_ASSERT(parent->remaining_ranges);
8008 
8009               /* Set the path's remaining_ranges equal to its parent's. */
8010               new_child->remaining_ranges = svn_rangelist_dup(
8011                  parent->remaining_ranges, pool);
8012               SVN_ERR(insert_child_to_merge(children_with_mergeinfo,
8013                                             new_child, pool));
8014             }
8015         }
8016     }
8017   svn_pool_destroy(iterpool);
8018 
8019   return SVN_NO_ERROR;
8020 }
8021 
8022 /* Return true if any path in SUBTREES is equal to, or is a subtree of,
8023    LOCAL_ABSPATH.  Return false otherwise.  The keys of SUBTREES are
8024    (const char *) absolute paths and its values are irrelevant.
8025    If SUBTREES is NULL return false. */
8026 static svn_boolean_t
path_is_subtree(const char * local_abspath,apr_hash_t * subtrees,apr_pool_t * pool)8027 path_is_subtree(const char *local_abspath,
8028                 apr_hash_t *subtrees,
8029                 apr_pool_t *pool)
8030 {
8031   if (subtrees)
8032     {
8033       apr_hash_index_t *hi;
8034 
8035       for (hi = apr_hash_first(pool, subtrees);
8036            hi; hi = apr_hash_next(hi))
8037         {
8038           const char *path_touched_by_merge = apr_hash_this_key(hi);
8039           if (svn_dirent_is_ancestor(local_abspath, path_touched_by_merge))
8040             return TRUE;
8041         }
8042     }
8043   return FALSE;
8044 }
8045 
8046 /* Return true if any merged, skipped, added or tree-conflicted path
8047    recorded in MERGE_B is equal to, or is a subtree of LOCAL_ABSPATH.  Return
8048    false otherwise.
8049 
8050    ### Why not text- or prop-conflicted paths? Are such paths guaranteed
8051        to be recorded as 'merged' or 'skipped' or 'added', perhaps?
8052 */
8053 static svn_boolean_t
subtree_touched_by_merge(const char * local_abspath,merge_cmd_baton_t * merge_b,apr_pool_t * pool)8054 subtree_touched_by_merge(const char *local_abspath,
8055                          merge_cmd_baton_t *merge_b,
8056                          apr_pool_t *pool)
8057 {
8058   return (path_is_subtree(local_abspath, merge_b->merged_abspaths, pool)
8059           || path_is_subtree(local_abspath, merge_b->skipped_abspaths, pool)
8060           || path_is_subtree(local_abspath, merge_b->added_abspaths, pool)
8061           || path_is_subtree(local_abspath, merge_b->tree_conflicted_abspaths,
8062                              pool));
8063 }
8064 
8065 /* Helper for do_directory_merge() when performing mergeinfo unaware merges.
8066 
8067    Merge the SOURCE diff into TARGET_DIR_WCPATH.
8068 
8069    SOURCE, DEPTH, NOTIFY_B, and MERGE_B
8070    are all cascaded from do_directory_merge's arguments of the same names.
8071 
8072    CONFLICT_REPORT is as documented for do_directory_merge().
8073 
8074    NOTE: This is a very thin wrapper around drive_merge_report_editor() and
8075    exists only to populate CHILDREN_WITH_MERGEINFO with the single element
8076    expected during mergeinfo unaware merges.
8077 */
8078 static svn_error_t *
do_mergeinfo_unaware_dir_merge(single_range_conflict_report_t ** conflict_report,const merge_source_t * source,const char * target_dir_wcpath,apr_array_header_t * children_with_mergeinfo,const svn_diff_tree_processor_t * processor,svn_depth_t depth,merge_cmd_baton_t * merge_b,apr_pool_t * result_pool,apr_pool_t * scratch_pool)8079 do_mergeinfo_unaware_dir_merge(single_range_conflict_report_t **conflict_report,
8080                                const merge_source_t *source,
8081                                const char *target_dir_wcpath,
8082                                apr_array_header_t *children_with_mergeinfo,
8083                                const svn_diff_tree_processor_t *processor,
8084                                svn_depth_t depth,
8085                                merge_cmd_baton_t *merge_b,
8086                                apr_pool_t *result_pool,
8087                                apr_pool_t *scratch_pool)
8088 {
8089   /* Initialize CHILDREN_WITH_MERGEINFO and populate it with
8090      one element describing the merge of SOURCE->rev1:rev2 to
8091      TARGET_DIR_WCPATH. */
8092   svn_client__merge_path_t *item
8093     = svn_client__merge_path_create(target_dir_wcpath, scratch_pool);
8094 
8095   *conflict_report = NULL;
8096   item->remaining_ranges = svn_rangelist__initialize(source->loc1->rev,
8097                                                      source->loc2->rev,
8098                                                      TRUE, scratch_pool);
8099   APR_ARRAY_PUSH(children_with_mergeinfo,
8100                  svn_client__merge_path_t *) = item;
8101   SVN_ERR(drive_merge_report_editor(target_dir_wcpath,
8102                                     source,
8103                                     NULL, processor, depth,
8104                                     merge_b, scratch_pool));
8105   if (is_path_conflicted_by_merge(merge_b))
8106     {
8107       *conflict_report = single_range_conflict_report_create(
8108                            source, NULL, result_pool);
8109     }
8110   return SVN_NO_ERROR;
8111 }
8112 
8113 /* A svn_log_entry_receiver_t baton for log_find_operative_subtree_revs(). */
8114 typedef struct log_find_operative_subtree_baton_t
8115 {
8116   /* Mapping of const char * absolute working copy paths to those
8117      path's const char * repos absolute paths. */
8118   apr_hash_t *operative_children;
8119 
8120   /* As per the arguments of the same name to
8121      get_operative_immediate_children(). */
8122   const char *merge_source_fspath;
8123   const char *merge_target_abspath;
8124   svn_depth_t depth;
8125   svn_wc_context_t *wc_ctx;
8126 
8127   /* A pool to allocate additions to the hashes in. */
8128   apr_pool_t *result_pool;
8129 } log_find_operative_subtree_baton_t;
8130 
8131 /* A svn_log_entry_receiver_t callback for
8132    get_inoperative_immediate_children(). */
8133 static svn_error_t *
log_find_operative_subtree_revs(void * baton,svn_log_entry_t * log_entry,apr_pool_t * pool)8134 log_find_operative_subtree_revs(void *baton,
8135                                 svn_log_entry_t *log_entry,
8136                                 apr_pool_t *pool)
8137 {
8138   log_find_operative_subtree_baton_t *log_baton = baton;
8139   apr_hash_index_t *hi;
8140   apr_pool_t *iterpool;
8141 
8142   /* It's possible that authz restrictions on the merge source prevent us
8143      from knowing about any of the changes for LOG_ENTRY->REVISION. */
8144   if (!log_entry->changed_paths2)
8145     return SVN_NO_ERROR;
8146 
8147   iterpool = svn_pool_create(pool);
8148 
8149   for (hi = apr_hash_first(pool, log_entry->changed_paths2);
8150        hi;
8151        hi = apr_hash_next(hi))
8152     {
8153       const char *path = apr_hash_this_key(hi);
8154       svn_log_changed_path2_t *change = apr_hash_this_val(hi);
8155 
8156         {
8157           const char *child;
8158           const char *potential_child;
8159           const char *rel_path =
8160             svn_fspath__skip_ancestor(log_baton->merge_source_fspath, path);
8161 
8162           /* Some affected paths might be the root of the merge source or
8163              entirely outside our subtree of interest. In either case they
8164              are not operative *immediate* children. */
8165           if (rel_path == NULL
8166               || rel_path[0] == '\0')
8167             continue;
8168 
8169           svn_pool_clear(iterpool);
8170 
8171           child = svn_relpath_dirname(rel_path, iterpool);
8172           if (child[0] == '\0')
8173             {
8174               /* The svn_log_changed_path2_t.node_kind members in
8175                  LOG_ENTRY->CHANGED_PATHS2 may be set to
8176                  svn_node_unknown, see svn_log_changed_path2_t and
8177                  svn_fs_paths_changed2.  In that case we check the
8178                  type of the corresponding subtree in the merge
8179                  target. */
8180               svn_node_kind_t node_kind;
8181 
8182               if (change->node_kind == svn_node_unknown)
8183                 {
8184                   const char *wc_child_abspath =
8185                     svn_dirent_join(log_baton->merge_target_abspath,
8186                                     rel_path, iterpool);
8187 
8188                   SVN_ERR(svn_wc_read_kind2(&node_kind, log_baton->wc_ctx,
8189                                             wc_child_abspath, FALSE, FALSE,
8190                                             iterpool));
8191                 }
8192               else
8193                 {
8194                   node_kind = change->node_kind;
8195                 }
8196 
8197               /* We only care about immediate directory children if
8198                  DEPTH is svn_depth_files. */
8199               if (log_baton->depth == svn_depth_files
8200                   && node_kind != svn_node_dir)
8201                 continue;
8202 
8203               /* If depth is svn_depth_immediates, then we only care
8204                  about changes to proper subtrees of PATH.  If the change
8205                  is to PATH itself then PATH is within the operational
8206                  depth of the merge. */
8207               if (log_baton->depth == svn_depth_immediates)
8208                 continue;
8209 
8210               child = rel_path;
8211             }
8212 
8213           potential_child = svn_dirent_join(log_baton->merge_target_abspath,
8214                                             child, iterpool);
8215 
8216           if (change->action == 'A'
8217               || !svn_hash_gets(log_baton->operative_children,
8218                                 potential_child))
8219             {
8220               svn_hash_sets(log_baton->operative_children,
8221                             apr_pstrdup(log_baton->result_pool,
8222                                         potential_child),
8223                             apr_pstrdup(log_baton->result_pool, path));
8224             }
8225         }
8226     }
8227   svn_pool_destroy(iterpool);
8228   return SVN_NO_ERROR;
8229 }
8230 
8231 /* Find immediate subtrees of MERGE_TARGET_ABSPATH which would have
8232    additional differences applied if record_mergeinfo_for_dir_merge() were
8233    recording mergeinfo describing a merge at svn_depth_infinity, rather
8234    than at DEPTH (which is assumed to be shallow; if
8235    DEPTH == svn_depth_infinity then this function does nothing beyond
8236    setting *OPERATIVE_CHILDREN to an empty hash).
8237 
8238    MERGE_SOURCE_FSPATH is the absolute repository path of the merge
8239    source.  OLDEST_REV and YOUNGEST_REV are the revisions merged from
8240    MERGE_SOURCE_FSPATH to MERGE_TARGET_ABSPATH.
8241 
8242    RA_SESSION points to MERGE_SOURCE_FSPATH.
8243 
8244    Set *OPERATIVE_CHILDREN to a hash (mapping const char * absolute
8245    working copy paths to those path's const char * repos absolute paths)
8246    containing all the immediate subtrees of MERGE_TARGET_ABSPATH which would
8247    have a different diff applied if MERGE_SOURCE_FSPATH
8248    -r(OLDEST_REV - 1):YOUNGEST_REV were merged to MERGE_TARGET_ABSPATH at
8249    svn_depth_infinity rather than DEPTH.
8250 
8251    RESULT_POOL is used to allocate the contents of *OPERATIVE_CHILDREN.
8252    SCRATCH_POOL is used for temporary allocations. */
8253 static svn_error_t *
get_operative_immediate_children(apr_hash_t ** operative_children,const char * merge_source_fspath,svn_revnum_t oldest_rev,svn_revnum_t youngest_rev,const char * merge_target_abspath,svn_depth_t depth,svn_wc_context_t * wc_ctx,svn_ra_session_t * ra_session,apr_pool_t * result_pool,apr_pool_t * scratch_pool)8254 get_operative_immediate_children(apr_hash_t **operative_children,
8255                                  const char *merge_source_fspath,
8256                                  svn_revnum_t oldest_rev,
8257                                  svn_revnum_t youngest_rev,
8258                                  const char *merge_target_abspath,
8259                                  svn_depth_t depth,
8260                                  svn_wc_context_t *wc_ctx,
8261                                  svn_ra_session_t *ra_session,
8262                                  apr_pool_t *result_pool,
8263                                  apr_pool_t *scratch_pool)
8264 {
8265   log_find_operative_subtree_baton_t log_baton;
8266 
8267   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(oldest_rev));
8268   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
8269   SVN_ERR_ASSERT(oldest_rev <= youngest_rev);
8270 
8271   *operative_children = apr_hash_make(result_pool);
8272 
8273   if (depth == svn_depth_infinity)
8274     return SVN_NO_ERROR;
8275 
8276   /* Now remove any paths from *OPERATIVE_CHILDREN that are inoperative when
8277      merging MERGE_SOURCE_REPOS_PATH -r(OLDEST_REV - 1):YOUNGEST_REV to
8278      MERGE_TARGET_ABSPATH at --depth infinity. */
8279   log_baton.operative_children = *operative_children;
8280   log_baton.merge_source_fspath = merge_source_fspath;
8281   log_baton.merge_target_abspath = merge_target_abspath;
8282   log_baton.depth = depth;
8283   log_baton.wc_ctx = wc_ctx;
8284   log_baton.result_pool = result_pool;
8285 
8286   SVN_ERR(get_log(ra_session, "", youngest_rev, oldest_rev,
8287                   TRUE, /* discover_changed_paths */
8288                   log_find_operative_subtree_revs,
8289                   &log_baton, scratch_pool));
8290 
8291   return SVN_NO_ERROR;
8292 }
8293 
8294 /* Helper for record_mergeinfo_for_dir_merge(): Identify which elements of
8295    CHILDREN_WITH_MERGEINFO need new mergeinfo set to accurately
8296    describe a merge, what inheritance type such new mergeinfo should have,
8297    and what subtrees can be ignored altogether.
8298 
8299    For each svn_client__merge_path_t CHILD in CHILDREN_WITH_MERGEINFO,
8300    set CHILD->RECORD_MERGEINFO and CHILD->RECORD_NONINHERITABLE to true
8301    if the subtree needs mergeinfo to describe the merge and if that
8302    mergeinfo should be non-inheritable respectively.
8303 
8304    If OPERATIVE_MERGE is true, then the merge being described is operative
8305    as per subtree_touched_by_merge().  OPERATIVE_MERGE is false otherwise.
8306 
8307    MERGED_RANGE, MERGEINFO_FSPATH, DEPTH, NOTIFY_B, and MERGE_B are all
8308    cascaded from record_mergeinfo_for_dir_merge's arguments of the same
8309    names.
8310 
8311    SCRATCH_POOL is used for temporary allocations.
8312 */
8313 static svn_error_t *
flag_subtrees_needing_mergeinfo(svn_boolean_t operative_merge,const svn_merge_range_t * merged_range,apr_array_header_t * children_with_mergeinfo,const char * mergeinfo_fspath,svn_depth_t depth,merge_cmd_baton_t * merge_b,apr_pool_t * scratch_pool)8314 flag_subtrees_needing_mergeinfo(svn_boolean_t operative_merge,
8315                                 const svn_merge_range_t *merged_range,
8316                                 apr_array_header_t *children_with_mergeinfo,
8317                                 const char *mergeinfo_fspath,
8318                                 svn_depth_t depth,
8319                                 merge_cmd_baton_t *merge_b,
8320                                 apr_pool_t *scratch_pool)
8321 {
8322   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
8323   int i;
8324   apr_hash_t *operative_immediate_children = NULL;
8325 
8326   assert(! merge_b->dry_run);
8327 
8328   if (!merge_b->record_only
8329       && merged_range->start <= merged_range->end
8330       && (depth < svn_depth_infinity))
8331     SVN_ERR(get_operative_immediate_children(
8332       &operative_immediate_children,
8333       mergeinfo_fspath, merged_range->start + 1, merged_range->end,
8334       merge_b->target->abspath, depth, merge_b->ctx->wc_ctx,
8335       merge_b->ra_session1, scratch_pool, iterpool));
8336 
8337   /* Issue #4056: Walk CHILDREN_WITH_MERGEINFO reverse depth-first
8338      order.  This way each child knows if it has operative missing/switched
8339      children which necessitates non-inheritable mergeinfo. */
8340   for (i = children_with_mergeinfo->nelts - 1; i >= 0; i--)
8341     {
8342       svn_client__merge_path_t *child =
8343                      APR_ARRAY_IDX(children_with_mergeinfo, i,
8344                                    svn_client__merge_path_t *);
8345 
8346       /* Can't record mergeinfo on something that isn't here. */
8347       if (child->absent)
8348         continue;
8349 
8350       /* Verify that remove_children_with_deleted_mergeinfo() did its job */
8351       assert((i == 0)
8352              ||! merge_b->paths_with_deleted_mergeinfo
8353              || !svn_hash_gets(merge_b->paths_with_deleted_mergeinfo,
8354                                child->abspath));
8355 
8356       /* Don't record mergeinfo on skipped paths. */
8357       if (svn_hash_gets(merge_b->skipped_abspaths, child->abspath))
8358         continue;
8359 
8360       /* ### ptb: Yes, we could combine the following into a single
8361          ### conditional, but clarity would suffer (even more than
8362          ### it does now). */
8363       if (i == 0)
8364         {
8365           /* Always record mergeinfo on the merge target. */
8366           child->record_mergeinfo = TRUE;
8367         }
8368       else if (merge_b->record_only && !merge_b->reintegrate_merge)
8369         {
8370           /* Always record mergeinfo for --record-only merges. */
8371           child->record_mergeinfo = TRUE;
8372         }
8373       else if (child->immediate_child_dir
8374                && !child->pre_merge_mergeinfo
8375                && operative_immediate_children
8376                && svn_hash_gets(operative_immediate_children, child->abspath))
8377         {
8378           /* We must record mergeinfo on those issue #3642 children
8379              that are operative at a greater depth. */
8380           child->record_mergeinfo = TRUE;
8381         }
8382 
8383       if (operative_merge
8384           && subtree_touched_by_merge(child->abspath, merge_b, iterpool))
8385         {
8386           svn_pool_clear(iterpool);
8387 
8388           /* This subtree was affected by the merge. */
8389           child->record_mergeinfo = TRUE;
8390 
8391           /* Were any CHILD's missing children skipped by the merge?
8392              If not, then CHILD's missing children don't need to be
8393              considered when recording mergeinfo describing the merge. */
8394           if (! merge_b->reintegrate_merge
8395               && child->missing_child
8396               && !path_is_subtree(child->abspath,
8397                                   merge_b->skipped_abspaths,
8398                                   iterpool))
8399             {
8400               child->missing_child = FALSE;
8401             }
8402 
8403           /* If CHILD has an immediate switched child or children and
8404              none of these were touched by the merge, then we don't need
8405              need to do any special handling of those switched subtrees
8406              (e.g. record non-inheritable mergeinfo) when recording
8407              mergeinfo describing the merge. */
8408           if (child->switched_child)
8409             {
8410               int j;
8411               svn_boolean_t operative_switched_child = FALSE;
8412 
8413               for (j = i + 1;
8414                    j < children_with_mergeinfo->nelts;
8415                    j++)
8416                 {
8417                   svn_client__merge_path_t *potential_child =
8418                     APR_ARRAY_IDX(children_with_mergeinfo, j,
8419                                   svn_client__merge_path_t *);
8420                   if (!svn_dirent_is_ancestor(child->abspath,
8421                                               potential_child->abspath))
8422                     break;
8423 
8424                   /* POTENTIAL_CHILD is a subtree of CHILD, but is it
8425                      an immediate child? */
8426                   if (strcmp(child->abspath,
8427                              svn_dirent_dirname(potential_child->abspath,
8428                                                 iterpool)))
8429                     continue;
8430 
8431                   if (potential_child->switched
8432                       && potential_child->record_mergeinfo)
8433                     {
8434                       operative_switched_child = TRUE;
8435                       break;
8436                     }
8437                 }
8438 
8439               /* Can we treat CHILD as if it has no switched children? */
8440               if (! operative_switched_child)
8441                 child->switched_child = FALSE;
8442             }
8443         }
8444 
8445       if (child->record_mergeinfo)
8446         {
8447           /* We need to record mergeinfo, but should that mergeinfo be
8448              non-inheritable? */
8449           svn_node_kind_t path_kind;
8450           SVN_ERR(svn_wc_read_kind2(&path_kind, merge_b->ctx->wc_ctx,
8451                                     child->abspath, FALSE, FALSE, iterpool));
8452 
8453           /* Only directories can have non-inheritable mergeinfo. */
8454           if (path_kind == svn_node_dir)
8455             {
8456               /* There are two general cases where non-inheritable mergeinfo
8457                  is required:
8458 
8459                  1) There merge target has missing subtrees (due to authz
8460                     restrictions, switched subtrees, or a shallow working
8461                     copy).
8462 
8463                  2) The operational depth of the merge itself is shallow. */
8464 
8465               /* We've already determined the first case. */
8466               child->record_noninheritable =
8467                 child->missing_child || child->switched_child;
8468 
8469               /* The second case requires a bit more work. */
8470               if (i == 0)
8471                 {
8472                   /* If CHILD is the root of the merge target and the
8473                      operational depth is empty or files, then the mere
8474                      existence of operative immediate children means we
8475                      must record non-inheritable mergeinfo.
8476 
8477                      ### What about svn_depth_immediates?  In that case
8478                      ### the merge target needs only normal inheritable
8479                      ### mergeinfo and the target's immediate children will
8480                      ### get non-inheritable mergeinfo, assuming they
8481                      ### need even that. */
8482                   if (depth < svn_depth_immediates
8483                       && operative_immediate_children
8484                       && apr_hash_count(operative_immediate_children))
8485                     child->record_noninheritable = TRUE;
8486                 }
8487               else if (depth == svn_depth_immediates)
8488                 {
8489                   /* An immediate directory child of the merge target, which
8490                       was affected by a --depth=immediates merge, needs
8491                       non-inheritable mergeinfo. */
8492                   if (svn_hash_gets(operative_immediate_children,
8493                                     child->abspath))
8494                     child->record_noninheritable = TRUE;
8495                 }
8496             }
8497         }
8498       else /* child->record_mergeinfo */
8499         {
8500           /* If CHILD is in CHILDREN_WITH_MERGEINFO simply
8501              because it had no explicit mergeinfo of its own at the
8502              start of the merge but is the child of of some path with
8503              non-inheritable mergeinfo, then the explicit mergeinfo it
8504              has *now* was set by get_mergeinfo_paths() -- see criteria
8505              3 in that function's doc string.  So since CHILD->ABSPATH
8506              was not touched by the merge we can remove the
8507              mergeinfo. */
8508           if (child->child_of_noninheritable)
8509             SVN_ERR(svn_client__record_wc_mergeinfo(child->abspath,
8510                                                     NULL, FALSE,
8511                                                     merge_b->ctx,
8512                                                     iterpool));
8513         }
8514     }
8515 
8516   svn_pool_destroy(iterpool);
8517   return SVN_NO_ERROR;
8518 }
8519 
8520 /* Helper for do_directory_merge().
8521 
8522    If RESULT_CATALOG is NULL then record mergeinfo describing a merge of
8523    MERGED_RANGE->START:MERGED_RANGE->END from the repository relative path
8524    MERGEINFO_FSPATH to the merge target (and possibly its subtrees) described
8525    by CHILDREN_WITH_MERGEINFO -- see the global comment
8526    'THE CHILDREN_WITH_MERGEINFO ARRAY'.  Obviously this should only
8527    be called if recording mergeinfo -- see doc string for RECORD_MERGEINFO().
8528 
8529    If RESULT_CATALOG is not NULL, then don't record the new mergeinfo on the
8530    WC, but instead record it in RESULT_CATALOG, where the keys are absolute
8531    working copy paths and the values are the new mergeinfos for each.
8532    Allocate additions to RESULT_CATALOG in pool which RESULT_CATALOG was
8533    created in.
8534 
8535    DEPTH, NOTIFY_B, MERGE_B, and SQUELCH_MERGEINFO_NOTIFICATIONS are all
8536    cascaded from do_directory_merge's arguments of the same names.
8537 
8538    SCRATCH_POOL is used for temporary allocations.
8539 */
8540 static svn_error_t *
record_mergeinfo_for_dir_merge(svn_mergeinfo_catalog_t result_catalog,const svn_merge_range_t * merged_range,const char * mergeinfo_fspath,apr_array_header_t * children_with_mergeinfo,svn_depth_t depth,svn_boolean_t squelch_mergeinfo_notifications,merge_cmd_baton_t * merge_b,apr_pool_t * scratch_pool)8541 record_mergeinfo_for_dir_merge(svn_mergeinfo_catalog_t result_catalog,
8542                                const svn_merge_range_t *merged_range,
8543                                const char *mergeinfo_fspath,
8544                                apr_array_header_t *children_with_mergeinfo,
8545                                svn_depth_t depth,
8546                                svn_boolean_t squelch_mergeinfo_notifications,
8547                                merge_cmd_baton_t *merge_b,
8548                                apr_pool_t *scratch_pool)
8549 {
8550   int i;
8551   svn_boolean_t is_rollback = (merged_range->start > merged_range->end);
8552   svn_boolean_t operative_merge;
8553 
8554   /* Update the WC mergeinfo here to account for our new
8555      merges, minus any unresolved conflicts and skips. */
8556 
8557   /* We need a scratch pool for iterations below. */
8558   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
8559 
8560   svn_merge_range_t range = *merged_range;
8561 
8562   assert(! merge_b->dry_run);
8563 
8564   /* Regardless of what subtrees in MERGE_B->target->abspath might be missing
8565      could this merge have been operative? */
8566   operative_merge = subtree_touched_by_merge(merge_b->target->abspath,
8567                                              merge_b, iterpool);
8568 
8569   /* If this couldn't be an operative merge then don't bother with
8570      the added complexity (and user confusion) of non-inheritable ranges.
8571      There is no harm in subtrees inheriting inoperative mergeinfo. */
8572   if (!operative_merge)
8573     range.inheritable = TRUE;
8574 
8575   /* Remove absent children at or under MERGE_B->target->abspath from
8576      CHILDREN_WITH_MERGEINFO
8577      before we calculate the merges performed. */
8578   SVN_ERR(remove_absent_children(merge_b->target->abspath,
8579                                  children_with_mergeinfo));
8580 
8581   /* Determine which subtrees of interest need mergeinfo recorded... */
8582   SVN_ERR(flag_subtrees_needing_mergeinfo(operative_merge, &range,
8583                                           children_with_mergeinfo,
8584                                           mergeinfo_fspath, depth,
8585                                           merge_b, iterpool));
8586 
8587   /* ...and then record it. */
8588   for (i = 0; i < children_with_mergeinfo->nelts; i++)
8589     {
8590       const char *child_repos_path;
8591       const char *child_merge_src_fspath;
8592       svn_rangelist_t *child_merge_rangelist;
8593       apr_hash_t *child_merges;
8594       svn_client__merge_path_t *child =
8595                      APR_ARRAY_IDX(children_with_mergeinfo, i,
8596                                    svn_client__merge_path_t *);
8597       SVN_ERR_ASSERT(child);
8598 
8599       svn_pool_clear(iterpool);
8600 
8601       if (child->record_mergeinfo)
8602         {
8603           child_repos_path = svn_dirent_skip_ancestor(merge_b->target->abspath,
8604                                                       child->abspath);
8605           SVN_ERR_ASSERT(child_repos_path != NULL);
8606           child_merge_src_fspath = svn_fspath__join(mergeinfo_fspath,
8607                                                     child_repos_path,
8608                                                     iterpool);
8609           /* Filter any ranges from each child's natural history before
8610              setting mergeinfo describing the merge. */
8611           SVN_ERR(filter_natural_history_from_mergeinfo(
8612             &child_merge_rangelist, child_merge_src_fspath,
8613             child->implicit_mergeinfo, &range, iterpool));
8614 
8615           if (child_merge_rangelist->nelts == 0)
8616             continue;
8617 
8618           if (!squelch_mergeinfo_notifications)
8619             {
8620               /* If the merge source has a gap, then don't mention
8621                  those gap revisions in the notification. */
8622               remove_source_gap(&range, merge_b->implicit_src_gap);
8623               notify_mergeinfo_recording(child->abspath, &range,
8624                                          merge_b->ctx, iterpool);
8625             }
8626 
8627           /* If we are here we know we will be recording some mergeinfo, but
8628              before we do, set override mergeinfo on skipped paths so they
8629              don't incorrectly inherit the mergeinfo we are about to set. */
8630           if (i == 0)
8631             SVN_ERR(record_skips_in_mergeinfo(mergeinfo_fspath,
8632                                               child_merge_rangelist,
8633                                               is_rollback, merge_b, iterpool));
8634 
8635           /* We may need to record non-inheritable mergeinfo that applies
8636              only to CHILD->ABSPATH. */
8637           if (child->record_noninheritable)
8638             svn_rangelist__set_inheritance(child_merge_rangelist, FALSE);
8639 
8640           /* If CHILD has inherited mergeinfo set it before
8641              recording the first merge range. */
8642           if (child->inherited_mergeinfo)
8643             SVN_ERR(svn_client__record_wc_mergeinfo(
8644               child->abspath,
8645               child->pre_merge_mergeinfo,
8646               FALSE, merge_b->ctx,
8647               iterpool));
8648           if (merge_b->implicit_src_gap)
8649             {
8650               /* If this is a reverse merge reorder CHILD->REMAINING_RANGES
8651                  so it will work with the svn_rangelist_remove API. */
8652               if (is_rollback)
8653                 SVN_ERR(svn_rangelist_reverse(child_merge_rangelist,
8654                                               iterpool));
8655 
8656               SVN_ERR(svn_rangelist_remove(&child_merge_rangelist,
8657                                            merge_b->implicit_src_gap,
8658                                            child_merge_rangelist, FALSE,
8659                                            iterpool));
8660               if (is_rollback)
8661                 SVN_ERR(svn_rangelist_reverse(child_merge_rangelist,
8662                                               iterpool));
8663             }
8664 
8665           child_merges = apr_hash_make(iterpool);
8666 
8667           /* The short story:
8668 
8669              If we are describing a forward merge, then the naive mergeinfo
8670              defined by MERGE_SOURCE_PATH:MERGED_RANGE->START:
8671              MERGE_SOURCE_PATH:MERGED_RANGE->END may contain non-existent
8672              path-revs or may describe other lines of history.  We must
8673              remove these invalid portion(s) before recording mergeinfo
8674              describing the merge.
8675 
8676              The long story:
8677 
8678              If CHILD is the merge target we know that
8679              MERGE_SOURCE_PATH:MERGED_RANGE->END exists.  Further, if there
8680              were no copies in MERGE_SOURCE_PATH's history going back to
8681              RANGE->START then we know that
8682              MERGE_SOURCE_PATH:MERGED_RANGE->START exists too and the two
8683              describe an unbroken line of history, and thus
8684              MERGE_SOURCE_PATH:MERGED_RANGE->START:
8685              MERGE_SOURCE_PATH:MERGED_RANGE->END is a valid description of
8686              the merge -- see normalize_merge_sources() and the global comment
8687              'MERGEINFO MERGE SOURCE NORMALIZATION'.
8688 
8689              However, if there *was* a copy, then
8690              MERGE_SOURCE_PATH:MERGED_RANGE->START doesn't exist or is
8691              unrelated to MERGE_SOURCE_PATH:MERGED_RANGE->END.  Also, we
8692              don't know if (MERGE_SOURCE_PATH:MERGED_RANGE->START)+1 through
8693              (MERGE_SOURCE_PATH:MERGED_RANGE->END)-1 actually exist.
8694 
8695              If CHILD is a subtree of the merge target, then nothing is
8696              guaranteed beyond the fact that MERGE_SOURCE_PATH exists at
8697              MERGED_RANGE->END. */
8698           if ((!merge_b->record_only || merge_b->reintegrate_merge)
8699               && (!is_rollback))
8700             {
8701               svn_error_t *err;
8702               svn_mergeinfo_t subtree_history_as_mergeinfo;
8703               svn_rangelist_t *child_merge_src_rangelist;
8704               svn_client__pathrev_t *subtree_mergeinfo_pathrev
8705                 = svn_client__pathrev_create_with_relpath(
8706                     merge_b->target->loc.repos_root_url,
8707                     merge_b->target->loc.repos_uuid,
8708                     merged_range->end, child_merge_src_fspath + 1,
8709                     iterpool);
8710 
8711               /* Confirm that the naive mergeinfo we want to set on
8712                  CHILD->ABSPATH both exists and is part of
8713                  (MERGE_SOURCE_PATH+CHILD_REPOS_PATH)@MERGED_RANGE->END's
8714                  history. */
8715               /* We know MERGED_RANGE->END is younger than MERGE_RANGE->START
8716                  because we only do this for forward merges. */
8717               err = svn_client__get_history_as_mergeinfo(
8718                 &subtree_history_as_mergeinfo, NULL,
8719                 subtree_mergeinfo_pathrev,
8720                 merged_range->end, merged_range->start,
8721                 merge_b->ra_session2, merge_b->ctx, iterpool);
8722 
8723               /* If CHILD is a subtree it may have been deleted prior to
8724                  MERGED_RANGE->END so the above call to get its history
8725                  will fail. */
8726               if (err)
8727                 {
8728                   if (err->apr_err != SVN_ERR_FS_NOT_FOUND)
8729                       return svn_error_trace(err);
8730                   svn_error_clear(err);
8731                 }
8732               else
8733                 {
8734                   child_merge_src_rangelist = svn_hash_gets(
8735                                                 subtree_history_as_mergeinfo,
8736                                                 child_merge_src_fspath);
8737                   SVN_ERR(svn_rangelist_intersect(&child_merge_rangelist,
8738                                                   child_merge_rangelist,
8739                                                   child_merge_src_rangelist,
8740                                                   FALSE, iterpool));
8741                   if (child->record_noninheritable)
8742                     svn_rangelist__set_inheritance(child_merge_rangelist,
8743                                                    FALSE);
8744                 }
8745             }
8746 
8747           svn_hash_sets(child_merges, child->abspath, child_merge_rangelist);
8748           SVN_ERR(update_wc_mergeinfo(result_catalog,
8749                                       child->abspath,
8750                                       child_merge_src_fspath,
8751                                       child_merges, is_rollback,
8752                                       merge_b->ctx, iterpool));
8753 
8754           /* Once is enough: We don't need to record mergeinfo describing
8755              the merge a second.  If CHILD->ABSPATH is in
8756              MERGE_B->ADDED_ABSPATHS, we'll do just that, so remove the
8757              former from the latter. */
8758           svn_hash_sets(merge_b->added_abspaths, child->abspath, NULL);
8759         }
8760 
8761       /* Elide explicit subtree mergeinfo whether or not we updated it. */
8762       if (i > 0)
8763         {
8764           svn_boolean_t in_switched_subtree = FALSE;
8765 
8766           if (child->switched)
8767             in_switched_subtree = TRUE;
8768           else if (i > 1)
8769             {
8770               /* Check if CHILD is part of a switched subtree */
8771               svn_client__merge_path_t *parent;
8772               int j = i - 1;
8773               for (; j > 0; j--)
8774                 {
8775                   parent = APR_ARRAY_IDX(children_with_mergeinfo,
8776                                          j, svn_client__merge_path_t *);
8777                   if (parent
8778                       && parent->switched
8779                       && svn_dirent_is_ancestor(parent->abspath,
8780                                                 child->abspath))
8781                     {
8782                       in_switched_subtree = TRUE;
8783                       break;
8784                     }
8785                 }
8786             }
8787 
8788           /* Allow mergeinfo on switched subtrees to elide to the
8789              repository. Otherwise limit elision to the merge target
8790              for now.  do_merge() will eventually try to
8791              elide that when the merge is complete. */
8792           SVN_ERR(svn_client__elide_mergeinfo(
8793             child->abspath,
8794             in_switched_subtree ? NULL : merge_b->target->abspath,
8795             merge_b->ctx, iterpool));
8796         }
8797     } /* (i = 0; i < notify_b->children_with_mergeinfo->nelts; i++) */
8798 
8799   svn_pool_destroy(iterpool);
8800   return SVN_NO_ERROR;
8801 }
8802 
8803 /* Helper for do_directory_merge().
8804 
8805    Record mergeinfo describing a merge of
8806    MERGED_RANGE->START:MERGED_RANGE->END from the repository relative path
8807    MERGEINFO_FSPATH to each path in ADDED_ABSPATHS which has explicit
8808    mergeinfo or is the immediate child of a parent with explicit
8809    non-inheritable mergeinfo.
8810 
8811    DEPTH, MERGE_B, and SQUELCH_MERGEINFO_NOTIFICATIONS, are
8812    cascaded from do_directory_merge's arguments of the same names.
8813 
8814    Note: This is intended to support forward merges only, i.e.
8815    MERGED_RANGE->START must be older than MERGED_RANGE->END.
8816 */
8817 static svn_error_t *
record_mergeinfo_for_added_subtrees(svn_merge_range_t * merged_range,const char * mergeinfo_fspath,svn_depth_t depth,svn_boolean_t squelch_mergeinfo_notifications,apr_hash_t * added_abspaths,merge_cmd_baton_t * merge_b,apr_pool_t * pool)8818 record_mergeinfo_for_added_subtrees(
8819   svn_merge_range_t *merged_range,
8820   const char *mergeinfo_fspath,
8821   svn_depth_t depth,
8822   svn_boolean_t squelch_mergeinfo_notifications,
8823   apr_hash_t *added_abspaths,
8824   merge_cmd_baton_t *merge_b,
8825   apr_pool_t *pool)
8826 {
8827   apr_pool_t *iterpool;
8828   apr_hash_index_t *hi;
8829 
8830   /* If no paths were added by the merge then we have nothing to do. */
8831   if (!added_abspaths)
8832     return SVN_NO_ERROR;
8833 
8834   SVN_ERR_ASSERT(merged_range->start < merged_range->end);
8835 
8836   iterpool = svn_pool_create(pool);
8837   for (hi = apr_hash_first(pool, added_abspaths); hi; hi = apr_hash_next(hi))
8838     {
8839       const char *added_abspath = apr_hash_this_key(hi);
8840       const char *dir_abspath;
8841       svn_mergeinfo_t parent_mergeinfo;
8842       svn_mergeinfo_t added_path_mergeinfo;
8843 
8844       svn_pool_clear(iterpool);
8845       dir_abspath = svn_dirent_dirname(added_abspath, iterpool);
8846 
8847       /* Grab the added path's explicit mergeinfo. */
8848       SVN_ERR(svn_client__get_wc_mergeinfo(&added_path_mergeinfo, NULL,
8849                                            svn_mergeinfo_explicit,
8850                                            added_abspath, NULL, NULL, FALSE,
8851                                            merge_b->ctx, iterpool, iterpool));
8852 
8853       /* If the added path doesn't have explicit mergeinfo, does its immediate
8854          parent have non-inheritable mergeinfo? */
8855       if (!added_path_mergeinfo)
8856         SVN_ERR(svn_client__get_wc_mergeinfo(&parent_mergeinfo, NULL,
8857                                              svn_mergeinfo_explicit,
8858                                              dir_abspath, NULL, NULL, FALSE,
8859                                              merge_b->ctx,
8860                                              iterpool, iterpool));
8861 
8862       if (added_path_mergeinfo
8863           || svn_mergeinfo__is_noninheritable(parent_mergeinfo, iterpool))
8864         {
8865           svn_node_kind_t added_path_kind;
8866           svn_mergeinfo_t merge_mergeinfo;
8867           svn_mergeinfo_t adds_history_as_mergeinfo;
8868           svn_rangelist_t *rangelist;
8869           const char *rel_added_path;
8870           const char *added_path_mergeinfo_fspath;
8871           svn_client__pathrev_t *added_path_pathrev;
8872 
8873           SVN_ERR(svn_wc_read_kind2(&added_path_kind, merge_b->ctx->wc_ctx,
8874                                     added_abspath, FALSE, FALSE, iterpool));
8875 
8876           /* Calculate the naive mergeinfo describing the merge. */
8877           merge_mergeinfo = apr_hash_make(iterpool);
8878           rangelist = svn_rangelist__initialize(
8879                         merged_range->start, merged_range->end,
8880                         ((added_path_kind == svn_node_file)
8881                          || (!(depth == svn_depth_infinity
8882                                || depth == svn_depth_immediates))),
8883                         iterpool);
8884 
8885           /* Create the new mergeinfo path for added_path's mergeinfo.
8886              (added_abspath had better be a child of MERGE_B->target->abspath
8887              or something is *really* wrong.) */
8888           rel_added_path = svn_dirent_is_child(merge_b->target->abspath,
8889                                                added_abspath, iterpool);
8890           SVN_ERR_ASSERT(rel_added_path);
8891           added_path_mergeinfo_fspath = svn_fspath__join(mergeinfo_fspath,
8892                                                          rel_added_path,
8893                                                          iterpool);
8894           svn_hash_sets(merge_mergeinfo, added_path_mergeinfo_fspath,
8895                         rangelist);
8896 
8897           /* Don't add new mergeinfo to describe the merge if that mergeinfo
8898              contains non-existent merge sources.
8899 
8900              We know that MERGEINFO_PATH/rel_added_path's history does not
8901              span MERGED_RANGE->START:MERGED_RANGE->END but rather that it
8902              was added at some revions greater than MERGED_RANGE->START
8903              (assuming this is a forward merge).  It may have been added,
8904              deleted, and re-added many times.  The point is that we cannot
8905              blindly apply the naive mergeinfo calculated above because it
8906              will describe non-existent merge sources. To avoid this we get
8907              take the intersection of the naive mergeinfo with
8908              MERGEINFO_PATH/rel_added_path's history. */
8909           added_path_pathrev = svn_client__pathrev_create_with_relpath(
8910                                  merge_b->target->loc.repos_root_url,
8911                                  merge_b->target->loc.repos_uuid,
8912                                  MAX(merged_range->start, merged_range->end),
8913                                  added_path_mergeinfo_fspath + 1, iterpool);
8914           SVN_ERR(svn_client__get_history_as_mergeinfo(
8915             &adds_history_as_mergeinfo, NULL,
8916             added_path_pathrev,
8917             MAX(merged_range->start, merged_range->end),
8918             MIN(merged_range->start, merged_range->end),
8919             merge_b->ra_session2, merge_b->ctx, iterpool));
8920 
8921           SVN_ERR(svn_mergeinfo_intersect2(&merge_mergeinfo,
8922                                            merge_mergeinfo,
8923                                            adds_history_as_mergeinfo,
8924                                            FALSE, iterpool, iterpool));
8925 
8926           /* Combine the explicit mergeinfo on the added path (if any)
8927              with the mergeinfo describing this merge. */
8928           if (added_path_mergeinfo)
8929             SVN_ERR(svn_mergeinfo_merge2(merge_mergeinfo,
8930                                          added_path_mergeinfo,
8931                                          iterpool, iterpool));
8932           SVN_ERR(svn_client__record_wc_mergeinfo(
8933             added_abspath, merge_mergeinfo,
8934             !squelch_mergeinfo_notifications, merge_b->ctx, iterpool));
8935         }
8936     }
8937   svn_pool_destroy(iterpool);
8938 
8939   return SVN_NO_ERROR;
8940 }
8941 /* Baton structure for log_noop_revs. */
8942 typedef struct log_noop_baton_t
8943 {
8944   /* See the comment 'THE CHILDREN_WITH_MERGEINFO ARRAY' at the start
8945      of this file.*/
8946   apr_array_header_t *children_with_mergeinfo;
8947 
8948   /* Absolute repository path of younger of the two merge sources
8949      being diffed. */
8950   const char *source_fspath;
8951 
8952   /* The merge target. */
8953   const merge_target_t *target;
8954 
8955   /* Initially empty rangelists allocated in POOL. The rangelists are
8956    * populated across multiple invocations of log_noop_revs(). */
8957   svn_rangelist_t *operative_ranges;
8958   svn_rangelist_t *merged_ranges;
8959 
8960   /* Pool to store the rangelists. */
8961   apr_pool_t *pool;
8962 } log_noop_baton_t;
8963 
8964 /* Helper for log_noop_revs: Merge a svn_merge_range_t representation of
8965    REVISION into RANGELIST. New elements added to rangelist are allocated
8966    in RESULT_POOL.
8967 
8968    This is *not* a general purpose rangelist merge but a special replacement
8969    for svn_rangelist_merge when REVISION is guaranteed to be younger than any
8970    element in RANGELIST.  svn_rangelist_merge is O(n) worst-case (i.e. when
8971    all the ranges in output rangelist are older than the incoming changes).
8972    This turns the special case of a single incoming younger range into O(1).
8973    */
8974 static svn_error_t *
rangelist_merge_revision(svn_rangelist_t * rangelist,svn_revnum_t revision,apr_pool_t * result_pool)8975 rangelist_merge_revision(svn_rangelist_t *rangelist,
8976                          svn_revnum_t revision,
8977                          apr_pool_t *result_pool)
8978 {
8979   svn_merge_range_t *new_range;
8980   if (rangelist->nelts)
8981     {
8982       svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, rangelist->nelts - 1,
8983                                                svn_merge_range_t *);
8984       if (range->end == revision - 1)
8985         {
8986           /* REVISION is adjacent to the youngest range in RANGELIST
8987              so we can simply expand that range to encompass REVISION. */
8988           range->end = revision;
8989           return SVN_NO_ERROR;
8990         }
8991     }
8992   new_range = apr_palloc(result_pool, sizeof(*new_range));
8993   new_range->start = revision - 1;
8994   new_range->end = revision;
8995   new_range->inheritable = TRUE;
8996 
8997   APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = new_range;
8998 
8999   return SVN_NO_ERROR;
9000 }
9001 
9002 /* Implements the svn_log_entry_receiver_t interface.
9003 
9004    BATON is an log_noop_baton_t *.
9005 
9006    Add LOG_ENTRY->REVISION to BATON->OPERATIVE_RANGES.
9007 
9008    If LOG_ENTRY->REVISION has already been fully merged to
9009    BATON->target->abspath per the mergeinfo in BATON->CHILDREN_WITH_MERGEINFO,
9010    then add LOG_ENTRY->REVISION to BATON->MERGED_RANGES.
9011 
9012    Use SCRATCH_POOL for temporary allocations.  Allocate additions to
9013    BATON->MERGED_RANGES and BATON->OPERATIVE_RANGES in BATON->POOL.
9014 
9015    Note: This callback must be invoked from oldest LOG_ENTRY->REVISION
9016    to youngest LOG_ENTRY->REVISION -- see rangelist_merge_revision().
9017 */
9018 static svn_error_t *
log_noop_revs(void * baton,svn_log_entry_t * log_entry,apr_pool_t * scratch_pool)9019 log_noop_revs(void *baton,
9020               svn_log_entry_t *log_entry,
9021               apr_pool_t *scratch_pool)
9022 {
9023   log_noop_baton_t *log_gap_baton = baton;
9024   apr_hash_index_t *hi;
9025   svn_revnum_t revision;
9026   svn_boolean_t log_entry_rev_required = FALSE;
9027 
9028   revision = log_entry->revision;
9029 
9030   /* It's possible that authz restrictions on the merge source prevent us
9031      from knowing about any of the changes for LOG_ENTRY->REVISION. */
9032   if (!log_entry->changed_paths2)
9033     return SVN_NO_ERROR;
9034 
9035   /* Unconditionally add LOG_ENTRY->REVISION to BATON->OPERATIVE_MERGES. */
9036   SVN_ERR(rangelist_merge_revision(log_gap_baton->operative_ranges,
9037                                    revision,
9038                                    log_gap_baton->pool));
9039 
9040   /* Examine each path affected by LOG_ENTRY->REVISION.  If the explicit or
9041      inherited mergeinfo for *all* of the corresponding paths under
9042      BATON->target->abspath reflects that LOG_ENTRY->REVISION has been
9043      merged, then add LOG_ENTRY->REVISION to BATON->MERGED_RANGES. */
9044   for (hi = apr_hash_first(scratch_pool, log_entry->changed_paths2);
9045        hi;
9046        hi = apr_hash_next(hi))
9047     {
9048       const char *fspath = apr_hash_this_key(hi);
9049       const char *rel_path;
9050       const char *cwmi_abspath;
9051       svn_rangelist_t *paths_explicit_rangelist = NULL;
9052       svn_boolean_t mergeinfo_inherited = FALSE;
9053 
9054       /* Adjust REL_PATH so it is relative to the merge source then use it to
9055          calculate what path in the merge target would be affected by this
9056          revision. */
9057       rel_path = svn_fspath__skip_ancestor(log_gap_baton->source_fspath,
9058                                            fspath);
9059       /* Is PATH even within the merge target?  If it isn't we
9060          can disregard it altogether. */
9061       if (rel_path == NULL)
9062         continue;
9063       cwmi_abspath = svn_dirent_join(log_gap_baton->target->abspath,
9064                                      rel_path, scratch_pool);
9065 
9066       /* Find any explicit or inherited mergeinfo for PATH. */
9067       while (!log_entry_rev_required)
9068         {
9069           svn_client__merge_path_t *child = get_child_with_mergeinfo(
9070             log_gap_baton->children_with_mergeinfo, cwmi_abspath);
9071 
9072           if (child && child->pre_merge_mergeinfo)
9073             {
9074               /* Found some explicit mergeinfo, grab any ranges
9075                  for PATH. */
9076               paths_explicit_rangelist =
9077                             svn_hash_gets(child->pre_merge_mergeinfo, fspath);
9078               break;
9079             }
9080 
9081           if (cwmi_abspath[0] == '\0'
9082               || svn_dirent_is_root(cwmi_abspath, strlen(cwmi_abspath))
9083               || strcmp(log_gap_baton->target->abspath, cwmi_abspath) == 0)
9084             {
9085               /* Can't crawl any higher. */
9086               break;
9087             }
9088 
9089           /* Didn't find anything so crawl up to the parent. */
9090           cwmi_abspath = svn_dirent_dirname(cwmi_abspath, scratch_pool);
9091           fspath = svn_fspath__dirname(fspath, scratch_pool);
9092 
9093           /* At this point *if* we find mergeinfo it will be inherited. */
9094           mergeinfo_inherited = TRUE;
9095         }
9096 
9097       if (paths_explicit_rangelist)
9098         {
9099           svn_rangelist_t *intersecting_range;
9100           svn_rangelist_t *rangelist;
9101 
9102           rangelist = svn_rangelist__initialize(revision - 1, revision, TRUE,
9103                                                 scratch_pool);
9104 
9105           /* If PATH inherited mergeinfo we must consider inheritance in the
9106              event the inherited mergeinfo is actually non-inheritable. */
9107           SVN_ERR(svn_rangelist_intersect(&intersecting_range,
9108                                           paths_explicit_rangelist,
9109                                           rangelist,
9110                                           mergeinfo_inherited, scratch_pool));
9111 
9112           if (intersecting_range->nelts == 0)
9113             log_entry_rev_required = TRUE;
9114         }
9115       else
9116         {
9117           log_entry_rev_required = TRUE;
9118         }
9119     }
9120 
9121   if (!log_entry_rev_required)
9122     SVN_ERR(rangelist_merge_revision(log_gap_baton->merged_ranges,
9123                                      revision,
9124                                      log_gap_baton->pool));
9125 
9126   return SVN_NO_ERROR;
9127 }
9128 
9129 /* Helper for do_directory_merge().
9130 
9131    SOURCE is cascaded from the argument of the same name in
9132    do_directory_merge().  TARGET is the merge target.  RA_SESSION is the
9133    session for SOURCE->loc2.
9134 
9135    Find all the ranges required by subtrees in
9136    CHILDREN_WITH_MERGEINFO that are *not* required by
9137    TARGET->abspath (i.e. CHILDREN_WITH_MERGEINFO[0]).  If such
9138    ranges exist, then find any subset of ranges which, if merged, would be
9139    inoperative.  Finally, if any inoperative ranges are found then remove
9140    these ranges from all of the subtree's REMAINING_RANGES.
9141 
9142    This function should only be called when honoring mergeinfo during
9143    forward merges (i.e. SOURCE->rev1 < SOURCE->rev2).
9144 */
9145 static svn_error_t *
remove_noop_subtree_ranges(const merge_source_t * source,const merge_target_t * target,svn_ra_session_t * ra_session,apr_array_header_t * children_with_mergeinfo,apr_pool_t * result_pool,apr_pool_t * scratch_pool)9146 remove_noop_subtree_ranges(const merge_source_t *source,
9147                            const merge_target_t *target,
9148                            svn_ra_session_t *ra_session,
9149                            apr_array_header_t *children_with_mergeinfo,
9150                            apr_pool_t *result_pool,
9151                            apr_pool_t *scratch_pool)
9152 {
9153   /* ### Do we need to check that we are at a uniform working revision? */
9154   int i;
9155   svn_client__merge_path_t *root_child =
9156     APR_ARRAY_IDX(children_with_mergeinfo, 0, svn_client__merge_path_t *);
9157   svn_rangelist_t *requested_ranges;
9158   svn_rangelist_t *subtree_gap_ranges;
9159   svn_rangelist_t *subtree_remaining_ranges;
9160   log_noop_baton_t log_gap_baton;
9161   svn_merge_range_t *oldest_gap_rev;
9162   svn_merge_range_t *youngest_gap_rev;
9163   svn_rangelist_t *inoperative_ranges;
9164   apr_pool_t *iterpool;
9165   const char *longest_common_subtree_ancestor = NULL;
9166   svn_error_t *err;
9167 
9168   assert(session_url_is(ra_session, source->loc2->url, scratch_pool));
9169 
9170   /* This function is only intended to work with forward merges. */
9171   if (source->loc1->rev > source->loc2->rev)
9172     return SVN_NO_ERROR;
9173 
9174   /* Another easy out: There are no subtrees. */
9175   if (children_with_mergeinfo->nelts < 2)
9176     return SVN_NO_ERROR;
9177 
9178   subtree_remaining_ranges = apr_array_make(scratch_pool, 1,
9179                                             sizeof(svn_merge_range_t *));
9180 
9181   /* Given the requested merge of SOURCE->rev1:rev2 might there be any
9182      part of this range required for subtrees but not for the target? */
9183   requested_ranges = svn_rangelist__initialize(MIN(source->loc1->rev,
9184                                                    source->loc2->rev),
9185                                                MAX(source->loc1->rev,
9186                                                    source->loc2->rev),
9187                                                TRUE, scratch_pool);
9188   SVN_ERR(svn_rangelist_remove(&subtree_gap_ranges,
9189                                root_child->remaining_ranges,
9190                                requested_ranges, FALSE, scratch_pool));
9191 
9192   /* Early out, nothing to operate on */
9193   if (!subtree_gap_ranges->nelts)
9194     return SVN_NO_ERROR;
9195 
9196   /* Create a rangelist describing every range required across all subtrees. */
9197   iterpool = svn_pool_create(scratch_pool);
9198   for (i = 1; i < children_with_mergeinfo->nelts; i++)
9199     {
9200       svn_client__merge_path_t *child =
9201         APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
9202 
9203       svn_pool_clear(iterpool);
9204 
9205       /* CHILD->REMAINING_RANGES will be NULL if child is absent. */
9206       if (child->remaining_ranges && child->remaining_ranges->nelts)
9207         {
9208           /* Issue #4269: Keep track of the longest common ancestor of all the
9209              subtrees which require merges.  This may be a child of
9210              TARGET->ABSPATH, which will allow us to narrow the log request
9211              below. */
9212           if (longest_common_subtree_ancestor)
9213             longest_common_subtree_ancestor = svn_dirent_get_longest_ancestor(
9214               longest_common_subtree_ancestor, child->abspath, scratch_pool);
9215           else
9216             longest_common_subtree_ancestor = child->abspath;
9217 
9218           SVN_ERR(svn_rangelist_merge2(subtree_remaining_ranges,
9219                                        child->remaining_ranges,
9220                                        scratch_pool, iterpool));
9221         }
9222     }
9223   svn_pool_destroy(iterpool);
9224 
9225   /* It's possible that none of the subtrees had any remaining ranges. */
9226   if (!subtree_remaining_ranges->nelts)
9227     return SVN_NO_ERROR;
9228 
9229   /* Ok, *finally* we can answer what part(s) of SOURCE->rev1:rev2 are
9230      required for the subtrees but not the target. */
9231   SVN_ERR(svn_rangelist_intersect(&subtree_gap_ranges,
9232                                   subtree_gap_ranges,
9233                                   subtree_remaining_ranges, FALSE,
9234                                   scratch_pool));
9235 
9236   /* Another early out */
9237   if (!subtree_gap_ranges->nelts)
9238     return SVN_NO_ERROR;
9239 
9240   /* One or more subtrees need some revisions that the target doesn't need.
9241      Use log to determine if any of these revisions are inoperative. */
9242   oldest_gap_rev = APR_ARRAY_IDX(subtree_gap_ranges, 0, svn_merge_range_t *);
9243   youngest_gap_rev = APR_ARRAY_IDX(subtree_gap_ranges,
9244                          subtree_gap_ranges->nelts - 1, svn_merge_range_t *);
9245 
9246   /* Set up the log baton. */
9247   log_gap_baton.children_with_mergeinfo = children_with_mergeinfo;
9248   log_gap_baton.source_fspath
9249     = svn_client__pathrev_fspath(source->loc2, result_pool);
9250   log_gap_baton.target = target;
9251   log_gap_baton.merged_ranges = apr_array_make(scratch_pool, 0,
9252                                                sizeof(svn_revnum_t *));
9253   log_gap_baton.operative_ranges = apr_array_make(scratch_pool, 0,
9254                                                   sizeof(svn_revnum_t *));
9255   log_gap_baton.pool = svn_pool_create(scratch_pool);
9256 
9257   /* Find the longest common ancestor of all subtrees relative to
9258      RA_SESSION's URL. */
9259   if (longest_common_subtree_ancestor)
9260     longest_common_subtree_ancestor =
9261       svn_dirent_skip_ancestor(target->abspath,
9262                                longest_common_subtree_ancestor);
9263   else
9264     longest_common_subtree_ancestor = "";
9265 
9266   /* Invoke the svn_log_entry_receiver_t receiver log_noop_revs() from
9267      oldest to youngest.  The receiver is optimized to add ranges to
9268      log_gap_baton.merged_ranges and log_gap_baton.operative_ranges, but
9269      requires that the revs arrive oldest to youngest -- see log_noop_revs()
9270      and rangelist_merge_revision(). */
9271   err = get_log(ra_session, longest_common_subtree_ancestor,
9272                 oldest_gap_rev->start + 1, youngest_gap_rev->end, TRUE,
9273                 log_noop_revs, &log_gap_baton, scratch_pool);
9274 
9275   /* It's possible that the only subtrees with mergeinfo in TARGET don't have
9276      any corresponding subtree in SOURCE between SOURCE->REV1 < SOURCE->REV2.
9277      So it's also possible that we may ask for the logs of non-existent paths.
9278      If we do, then assume that no subtree requires any ranges that are not
9279      already required by the TARGET. */
9280   if (err)
9281     {
9282       if (err->apr_err != SVN_ERR_FS_NOT_FOUND
9283           && longest_common_subtree_ancestor[0] != '\0')
9284         return svn_error_trace(err);
9285 
9286       /* Asked about a non-existent subtree in SOURCE. */
9287       svn_error_clear(err);
9288       log_gap_baton.merged_ranges =
9289         svn_rangelist__initialize(oldest_gap_rev->start,
9290                                   youngest_gap_rev->end,
9291                                   TRUE, scratch_pool);
9292     }
9293   else
9294     {
9295       inoperative_ranges = svn_rangelist__initialize(oldest_gap_rev->start,
9296                                                      youngest_gap_rev->end,
9297                                                      TRUE, scratch_pool);
9298       SVN_ERR(svn_rangelist_remove(&(inoperative_ranges),
9299                                    log_gap_baton.operative_ranges,
9300                                    inoperative_ranges, FALSE, scratch_pool));
9301       SVN_ERR(svn_rangelist_merge2(log_gap_baton.merged_ranges, inoperative_ranges,
9302                                    scratch_pool, scratch_pool));
9303     }
9304 
9305   for (i = 1; i < children_with_mergeinfo->nelts; i++)
9306     {
9307       svn_client__merge_path_t *child =
9308         APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
9309 
9310       /* CHILD->REMAINING_RANGES will be NULL if child is absent. */
9311       if (child->remaining_ranges && child->remaining_ranges->nelts)
9312         {
9313           /* Remove inoperative ranges from all children so we don't perform
9314              inoperative editor drives. */
9315           SVN_ERR(svn_rangelist_remove(&(child->remaining_ranges),
9316                                        log_gap_baton.merged_ranges,
9317                                        child->remaining_ranges,
9318                                        FALSE, result_pool));
9319         }
9320     }
9321 
9322   svn_pool_destroy(log_gap_baton.pool);
9323 
9324   return SVN_NO_ERROR;
9325 }
9326 
9327 /* Perform a merge of changes in SOURCE to the working copy path
9328    TARGET_ABSPATH. Both URLs in SOURCE, and TARGET_ABSPATH all represent
9329    directories -- for the single file case, the caller should use
9330    do_file_merge().
9331 
9332    CHILDREN_WITH_MERGEINFO and MERGE_B describe the merge being performed
9333    As this function is for a mergeinfo-aware merge, SOURCE->ancestral
9334    should be TRUE, and SOURCE->loc1 must be a historical ancestor of
9335    SOURCE->loc2, or vice-versa (see `MERGEINFO MERGE SOURCE NORMALIZATION'
9336    for more requirements around SOURCE).
9337 
9338    Mergeinfo changes will be recorded unless MERGE_B->dry_run is true.
9339 
9340    If mergeinfo is being recorded, SQUELCH_MERGEINFO_NOTIFICATIONS is FALSE,
9341    and MERGE_B->CTX->NOTIFY_FUNC2 is not NULL, then call
9342    MERGE_B->CTX->NOTIFY_FUNC2 with MERGE_B->CTX->NOTIFY_BATON2 and a
9343    svn_wc_notify_merge_record_info_begin notification before any mergeinfo
9344    changes are made to describe the merge performed.
9345 
9346    If mergeinfo is being recorded to describe this merge, and RESULT_CATALOG
9347    is not NULL, then don't record the new mergeinfo on the WC, but instead
9348    record it in RESULT_CATALOG, where the keys are absolute working copy
9349    paths and the values are the new mergeinfos for each.  Allocate additions
9350    to RESULT_CATALOG in pool which RESULT_CATALOG was created in.
9351 
9352    Handle DEPTH as documented for svn_client_merge5().
9353 
9354    CONFLICT_REPORT is as documented for do_directory_merge().
9355 
9356    Perform any temporary allocations in SCRATCH_POOL.
9357 
9358    NOTE: This is a wrapper around drive_merge_report_editor() which
9359    handles the complexities inherent to situations where a given
9360    directory's children may have intersecting merges (because they
9361    meet one or more of the criteria described in get_mergeinfo_paths()).
9362 */
9363 static svn_error_t *
do_mergeinfo_aware_dir_merge(svn_mergeinfo_catalog_t result_catalog,single_range_conflict_report_t ** conflict_report,const merge_source_t * source,const char * target_abspath,apr_array_header_t * children_with_mergeinfo,const svn_diff_tree_processor_t * processor,svn_depth_t depth,svn_boolean_t squelch_mergeinfo_notifications,merge_cmd_baton_t * merge_b,apr_pool_t * result_pool,apr_pool_t * scratch_pool)9364 do_mergeinfo_aware_dir_merge(svn_mergeinfo_catalog_t result_catalog,
9365                              single_range_conflict_report_t **conflict_report,
9366                              const merge_source_t *source,
9367                              const char *target_abspath,
9368                              apr_array_header_t *children_with_mergeinfo,
9369                              const svn_diff_tree_processor_t *processor,
9370                              svn_depth_t depth,
9371                              svn_boolean_t squelch_mergeinfo_notifications,
9372                              merge_cmd_baton_t *merge_b,
9373                              apr_pool_t *result_pool,
9374                              apr_pool_t *scratch_pool)
9375 {
9376   /* The range defining the mergeinfo we will record to describe the merge
9377      (assuming we are recording mergeinfo
9378 
9379      Note: This may be a subset of SOURCE->rev1:rev2 if
9380      populate_remaining_ranges() determines that some part of
9381      SOURCE->rev1:rev2 has already been wholly merged to TARGET_ABSPATH.
9382      Also, the actual editor drive(s) may be a subset of RANGE, if
9383      remove_noop_subtree_ranges() and/or fix_deleted_subtree_ranges()
9384      further tweak things. */
9385   svn_merge_range_t range;
9386 
9387   svn_ra_session_t *ra_session;
9388   svn_client__merge_path_t *target_merge_path;
9389   svn_boolean_t is_rollback = (source->loc1->rev > source->loc2->rev);
9390 
9391   SVN_ERR_ASSERT(source->ancestral);
9392 
9393   /*** If we get here, we're dealing with related sources from the
9394        same repository as the target -- merge tracking might be
9395        happenin'! ***/
9396 
9397   *conflict_report = NULL;
9398 
9399   /* Point our RA_SESSION to the URL of our youngest merge source side. */
9400   ra_session = is_rollback ? merge_b->ra_session1 : merge_b->ra_session2;
9401 
9402   /* Fill CHILDREN_WITH_MERGEINFO with child paths (const
9403      svn_client__merge_path_t *) which might have intersecting merges
9404      because they meet one or more of the criteria described in
9405      get_mergeinfo_paths(). Here the paths are arranged in a depth
9406      first order. */
9407   SVN_ERR(get_mergeinfo_paths(children_with_mergeinfo,
9408                               merge_b->target, depth,
9409                               merge_b->dry_run, merge_b->same_repos,
9410                               merge_b->ctx, scratch_pool, scratch_pool));
9411 
9412   /* The first item from the CHILDREN_WITH_MERGEINFO is always
9413      the target thanks to depth-first ordering. */
9414   target_merge_path = APR_ARRAY_IDX(children_with_mergeinfo, 0,
9415                                     svn_client__merge_path_t *);
9416 
9417   /* If we are honoring mergeinfo, then for each item in
9418      CHILDREN_WITH_MERGEINFO, we need to calculate what needs to be
9419      merged, and then merge it.  Otherwise, we just merge what we were asked
9420      to merge across the whole tree.  */
9421   SVN_ERR(populate_remaining_ranges(children_with_mergeinfo,
9422                                     source, ra_session,
9423                                     merge_b, scratch_pool, scratch_pool));
9424 
9425   /* Always start with a range which describes the most inclusive merge
9426      possible, i.e. SOURCE->rev1:rev2. */
9427   range.start = source->loc1->rev;
9428   range.end = source->loc2->rev;
9429   range.inheritable = TRUE;
9430 
9431   if (!merge_b->reintegrate_merge)
9432     {
9433       svn_revnum_t new_range_start, start_rev;
9434       apr_pool_t *iterpool = svn_pool_create(scratch_pool);
9435 
9436       /* The merge target TARGET_ABSPATH and/or its subtrees may not need all
9437          of SOURCE->rev1:rev2 applied.  So examine
9438          CHILDREN_WITH_MERGEINFO to find the oldest starting
9439          revision that actually needs to be merged (for reverse merges this is
9440          the youngest starting revision).
9441 
9442          We'll do this twice, right now for the start of the mergeinfo we will
9443          ultimately record to describe this merge and then later for the
9444          start of the actual editor drive. */
9445       new_range_start = get_most_inclusive_rev(
9446         children_with_mergeinfo, is_rollback, TRUE);
9447       if (SVN_IS_VALID_REVNUM(new_range_start))
9448         range.start = new_range_start;
9449 
9450       /* Remove inoperative ranges from any subtrees' remaining_ranges
9451          to spare the expense of noop editor drives. */
9452       if (!is_rollback)
9453         SVN_ERR(remove_noop_subtree_ranges(source, merge_b->target,
9454                                            ra_session,
9455                                            children_with_mergeinfo,
9456                                            scratch_pool, iterpool));
9457 
9458       /* Adjust subtrees' remaining_ranges to deal with issue #3067:
9459        * "subtrees that don't exist at the start or end of a merge range
9460        * shouldn't break the merge". */
9461       SVN_ERR(fix_deleted_subtree_ranges(source, merge_b->target,
9462                                          ra_session,
9463                                          children_with_mergeinfo,
9464                                          merge_b->ctx, scratch_pool, iterpool));
9465 
9466       /* remove_noop_subtree_ranges() and/or fix_deleted_subtree_range()
9467          may have further refined the starting revision for our editor
9468          drive. */
9469       start_rev =
9470         get_most_inclusive_rev(children_with_mergeinfo,
9471                                is_rollback, TRUE);
9472 
9473       /* Is there anything to merge? */
9474       if (SVN_IS_VALID_REVNUM(start_rev))
9475         {
9476           /* Now examine CHILDREN_WITH_MERGEINFO to find the oldest
9477              ending revision that actually needs to be merged (for reverse
9478              merges this is the youngest ending revision). */
9479            svn_revnum_t end_rev =
9480              get_most_inclusive_rev(children_with_mergeinfo,
9481                                     is_rollback, FALSE);
9482 
9483           /* While END_REV is valid, do the following:
9484 
9485              1. Tweak each CHILDREN_WITH_MERGEINFO element so that
9486                 the element's remaining_ranges member has as its first element
9487                 a range that ends with end_rev.
9488 
9489              2. Starting with start_rev, call drive_merge_report_editor()
9490                 on MERGE_B->target->abspath for start_rev:end_rev.
9491 
9492              3. Remove the first element from each
9493                 CHILDREN_WITH_MERGEINFO element's remaining_ranges
9494                 member.
9495 
9496              4. Again examine CHILDREN_WITH_MERGEINFO to find the most
9497                 inclusive starting revision that actually needs to be merged and
9498                 update start_rev.  This prevents us from needlessly contacting the
9499                 repository and doing a diff where we describe the entire target
9500                 tree as *not* needing any of the requested range.  This can happen
9501                 whenever we have mergeinfo with gaps in it for the merge source.
9502 
9503              5. Again examine CHILDREN_WITH_MERGEINFO to find the most
9504                 inclusive ending revision that actually needs to be merged and
9505                 update end_rev.
9506 
9507              6. Lather, rinse, repeat.
9508           */
9509 
9510           while (end_rev != SVN_INVALID_REVNUM)
9511             {
9512               merge_source_t *real_source;
9513               svn_merge_range_t *first_target_range
9514                 = (target_merge_path->remaining_ranges->nelts == 0 ? NULL
9515                    : APR_ARRAY_IDX(target_merge_path->remaining_ranges, 0,
9516                                    svn_merge_range_t *));
9517 
9518               /* Issue #3324: Stop editor abuse!  Don't call
9519                  drive_merge_report_editor() in such a way that we request an
9520                  editor with svn_client__get_diff_editor() for some rev X,
9521                  then call svn_ra_do_diff3() for some revision Y, and then
9522                  call reporter->set_path(PATH=="") to set the root revision
9523                  for the editor drive to revision Z where
9524                  (X != Z && X < Z < Y).  This is bogus because the server will
9525                  send us the diff between X:Y but the client is expecting the
9526                  diff between Y:Z.  See issue #3324 for full details on the
9527                  problems this can cause. */
9528               if (first_target_range
9529                   && start_rev != first_target_range->start)
9530                 {
9531                   if (is_rollback)
9532                     {
9533                       if (end_rev < first_target_range->start)
9534                         end_rev = first_target_range->start;
9535                     }
9536                   else
9537                     {
9538                       if (end_rev > first_target_range->start)
9539                         end_rev = first_target_range->start;
9540                     }
9541                 }
9542 
9543               svn_pool_clear(iterpool);
9544 
9545               SVN_ERR(slice_remaining_ranges(children_with_mergeinfo,
9546                                              is_rollback, end_rev,
9547                                              scratch_pool));
9548 
9549               /* Reset variables that must be reset for every drive */
9550               merge_b->notify_begin.last_abspath = NULL;
9551 
9552               real_source = subrange_source(source, start_rev, end_rev, iterpool);
9553               SVN_ERR(drive_merge_report_editor(
9554                 merge_b->target->abspath,
9555                 real_source,
9556                 children_with_mergeinfo,
9557                 processor,
9558                 depth,
9559                 merge_b,
9560                 iterpool));
9561 
9562               /* If any paths picked up explicit mergeinfo as a result of
9563                  the merge we need to make sure any mergeinfo those paths
9564                  inherited is recorded and then add these paths to
9565                  CHILDREN_WITH_MERGEINFO.*/
9566               SVN_ERR(process_children_with_new_mergeinfo(
9567                         merge_b, children_with_mergeinfo,
9568                         scratch_pool));
9569 
9570               /* If any subtrees had their explicit mergeinfo deleted as a
9571                  result of the merge then remove these paths from
9572                  CHILDREN_WITH_MERGEINFO since there is no need
9573                  to consider these subtrees for subsequent editor drives
9574                  nor do we want to record mergeinfo on them describing
9575                  the merge itself. */
9576               SVN_ERR(remove_children_with_deleted_mergeinfo(
9577                         merge_b, children_with_mergeinfo));
9578 
9579               /* Prepare for the next iteration (if any). */
9580               SVN_ERR(remove_first_range_from_remaining_ranges(
9581                         end_rev, children_with_mergeinfo, scratch_pool));
9582 
9583               /* If we raised any conflicts, break out and report how much
9584                  we have merged. */
9585               if (is_path_conflicted_by_merge(merge_b))
9586                 {
9587                   merge_source_t *remaining_range = NULL;
9588 
9589                   if (real_source->loc2->rev != source->loc2->rev)
9590                     remaining_range = subrange_source(source,
9591                                                       real_source->loc2->rev,
9592                                                       source->loc2->rev,
9593                                                       scratch_pool);
9594                   *conflict_report = single_range_conflict_report_create(
9595                                        real_source, remaining_range,
9596                                        result_pool);
9597 
9598                   range.end = end_rev;
9599                   break;
9600                 }
9601 
9602               start_rev =
9603                 get_most_inclusive_rev(children_with_mergeinfo,
9604                                        is_rollback, TRUE);
9605               end_rev =
9606                 get_most_inclusive_rev(children_with_mergeinfo,
9607                                        is_rollback, FALSE);
9608             }
9609         }
9610       svn_pool_destroy(iterpool);
9611     }
9612   else
9613     {
9614       if (!merge_b->record_only)
9615         {
9616           /* Reset the last notification path so that subsequent cherry
9617              picked revision ranges will be notified upon subsequent
9618              operative merge. */
9619           merge_b->notify_begin.last_abspath = NULL;
9620 
9621           SVN_ERR(drive_merge_report_editor(merge_b->target->abspath,
9622                                             source,
9623                                             NULL,
9624                                             processor,
9625                                             depth,
9626                                             merge_b,
9627                                             scratch_pool));
9628         }
9629     }
9630 
9631   /* Record mergeinfo where appropriate.*/
9632   if (RECORD_MERGEINFO(merge_b))
9633     {
9634       const svn_client__pathrev_t *primary_src
9635         = is_rollback ? source->loc1 : source->loc2;
9636       const char *mergeinfo_path
9637         = svn_client__pathrev_fspath(primary_src, scratch_pool);
9638 
9639       SVN_ERR(record_mergeinfo_for_dir_merge(result_catalog,
9640                                              &range,
9641                                              mergeinfo_path,
9642                                              children_with_mergeinfo,
9643                                              depth,
9644                                              squelch_mergeinfo_notifications,
9645                                              merge_b,
9646                                              scratch_pool));
9647 
9648       /* If a path has an immediate parent with non-inheritable mergeinfo at
9649          this point, then it meets criteria 3 or 5 described in
9650          get_mergeinfo_paths' doc string.  For paths which exist prior to a
9651          merge explicit mergeinfo has already been set.  But for paths added
9652          during the merge this is not the case.  The path might have explicit
9653          mergeinfo from the merge source, but no mergeinfo yet exists
9654          describing *this* merge.  So the added path has either incomplete
9655          explicit mergeinfo or inherits incomplete mergeinfo from its
9656          immediate parent (if any, the parent might have only non-inheritable
9657          ranges in which case the path simply inherits empty mergeinfo).
9658 
9659          So here we look at the root path of each subtree added during the
9660          merge and set explicit mergeinfo on it if it meets the aforementioned
9661          conditions. */
9662       if (range.start < range.end) /* Nothing to record on added subtrees
9663                                       resulting from reverse merges. */
9664         {
9665           SVN_ERR(record_mergeinfo_for_added_subtrees(
9666                     &range, mergeinfo_path, depth,
9667                     squelch_mergeinfo_notifications,
9668                     merge_b->added_abspaths, merge_b, scratch_pool));
9669         }
9670     }
9671 
9672   return SVN_NO_ERROR;
9673 }
9674 
9675 /* Helper for do_merge() when the merge target is a directory.
9676  *
9677  * If any conflict is raised during the merge, set *CONFLICTED_RANGE to
9678  * the revision sub-range that raised the conflict.  In this case, the
9679  * merge will have ended at revision CONFLICTED_RANGE and mergeinfo will
9680  * have been recorded for all revision sub-ranges up to and including
9681  * CONFLICTED_RANGE.  Otherwise, set *CONFLICTED_RANGE to NULL.
9682  */
9683 static svn_error_t *
do_directory_merge(svn_mergeinfo_catalog_t result_catalog,single_range_conflict_report_t ** conflict_report,const merge_source_t * source,const char * target_abspath,const svn_diff_tree_processor_t * processor,svn_depth_t depth,svn_boolean_t squelch_mergeinfo_notifications,merge_cmd_baton_t * merge_b,apr_pool_t * result_pool,apr_pool_t * scratch_pool)9684 do_directory_merge(svn_mergeinfo_catalog_t result_catalog,
9685                    single_range_conflict_report_t **conflict_report,
9686                    const merge_source_t *source,
9687                    const char *target_abspath,
9688                    const svn_diff_tree_processor_t *processor,
9689                    svn_depth_t depth,
9690                    svn_boolean_t squelch_mergeinfo_notifications,
9691                    merge_cmd_baton_t *merge_b,
9692                    apr_pool_t *result_pool,
9693                    apr_pool_t *scratch_pool)
9694 {
9695   apr_array_header_t *children_with_mergeinfo;
9696 
9697   /* Initialize CHILDREN_WITH_MERGEINFO. See the comment
9698      'THE CHILDREN_WITH_MERGEINFO ARRAY' at the start of this file. */
9699   children_with_mergeinfo =
9700     apr_array_make(scratch_pool, 16, sizeof(svn_client__merge_path_t *));
9701 
9702   /* And make it read-only accessible from the baton */
9703   merge_b->children_with_mergeinfo = children_with_mergeinfo;
9704 
9705   /* If we are not honoring mergeinfo we can skip right to the
9706      business of merging changes! */
9707   if (HONOR_MERGEINFO(merge_b))
9708     SVN_ERR(do_mergeinfo_aware_dir_merge(result_catalog, conflict_report,
9709                                          source, target_abspath,
9710                                          children_with_mergeinfo,
9711                                          processor, depth,
9712                                          squelch_mergeinfo_notifications,
9713                                          merge_b, result_pool, scratch_pool));
9714   else
9715     SVN_ERR(do_mergeinfo_unaware_dir_merge(conflict_report,
9716                                            source, target_abspath,
9717                                            children_with_mergeinfo,
9718                                            processor, depth,
9719                                            merge_b, result_pool, scratch_pool));
9720 
9721   merge_b->children_with_mergeinfo = NULL;
9722 
9723   return SVN_NO_ERROR;
9724 }
9725 
9726 /** Ensure that *RA_SESSION is opened to URL, either by reusing
9727  * *RA_SESSION if it is non-null and already opened to URL's
9728  * repository, or by allocating a new *RA_SESSION in POOL.
9729  * (RA_SESSION itself cannot be null, of course.)
9730  *
9731  * CTX is used as for svn_client_open_ra_session().
9732  */
9733 static svn_error_t *
ensure_ra_session_url(svn_ra_session_t ** ra_session,const char * url,const char * wri_abspath,svn_client_ctx_t * ctx,apr_pool_t * pool)9734 ensure_ra_session_url(svn_ra_session_t **ra_session,
9735                       const char *url,
9736                       const char *wri_abspath,
9737                       svn_client_ctx_t *ctx,
9738                       apr_pool_t *pool)
9739 {
9740   svn_error_t *err = SVN_NO_ERROR;
9741 
9742   if (*ra_session)
9743     {
9744       err = svn_ra_reparent(*ra_session, url, pool);
9745     }
9746 
9747   /* SVN_ERR_RA_ILLEGAL_URL is raised when url doesn't point to the same
9748      repository as ra_session. */
9749   if (! *ra_session || (err && err->apr_err == SVN_ERR_RA_ILLEGAL_URL))
9750     {
9751       svn_error_clear(err);
9752       err = svn_client_open_ra_session2(ra_session, url, wri_abspath,
9753                                         ctx, pool, pool);
9754     }
9755   SVN_ERR(err);
9756 
9757   return SVN_NO_ERROR;
9758 }
9759 
9760 /* Drive a merge of MERGE_SOURCES into working copy node TARGET
9761    and possibly record mergeinfo describing the merge -- see
9762    RECORD_MERGEINFO().
9763 
9764    If MODIFIED_SUBTREES is not NULL and all the MERGE_SOURCES are 'ancestral'
9765    or REINTEGRATE_MERGE is true, then replace *MODIFIED_SUBTREES with a new
9766    hash containing all the paths that *MODIFIED_SUBTREES contained before,
9767    and also every path modified, skipped, added, or tree-conflicted
9768    by the merge.  Keys and values of the hash are both (const char *)
9769    absolute paths.  The contents of the hash are allocated in RESULT_POOL.
9770 
9771    If the merge raises any conflicts while merging a revision range, return
9772    early and set *CONFLICT_REPORT to describe the details.  (In this case,
9773    notify that the merge is complete if and only if this was the last
9774    revision range of the merge.)  If there are no conflicts, set
9775    *CONFLICT_REPORT to NULL.  A revision range here can be one specified
9776    in MERGE_SOURCES or an internally generated sub-range of one of those
9777    when merge tracking is in use.
9778 
9779    For every (const merge_source_t *) merge source in MERGE_SOURCES, if
9780    SOURCE->ANCESTRAL is set, then the "left" and "right" side are
9781    ancestrally related.  (See 'MERGEINFO MERGE SOURCE NORMALIZATION'
9782    for more on what that means and how it matters.)
9783 
9784    If SOURCES_RELATED is set, the "left" and "right" sides of the
9785    merge source are historically related (ancestors, uncles, second
9786    cousins thrice removed, etc...).  (This is passed through to
9787    do_file_merge() to simulate the history checks that the repository
9788    logic does in the directory case.)
9789 
9790    SAME_REPOS is TRUE iff the merge sources live in the same
9791    repository as the one from which the target working copy has been
9792    checked out.
9793 
9794    If mergeinfo is being recorded, SQUELCH_MERGEINFO_NOTIFICATIONS is FALSE,
9795    and CTX->NOTIFY_FUNC2 is not NULL, then call CTX->NOTIFY_FUNC2 with
9796    CTX->NOTIFY_BATON2 and a svn_wc_notify_merge_record_info_begin
9797    notification before any mergeinfo changes are made to describe the merge
9798    performed.
9799 
9800    If mergeinfo is being recorded to describe this merge, and RESULT_CATALOG
9801    is not NULL, then don't record the new mergeinfo on the WC, but instead
9802    record it in RESULT_CATALOG, where the keys are absolute working copy
9803    paths and the values are the new mergeinfos for each.  Allocate additions
9804    to RESULT_CATALOG in pool which RESULT_CATALOG was created in.
9805 
9806    FORCE_DELETE, DRY_RUN, RECORD_ONLY, DEPTH, MERGE_OPTIONS,
9807    and CTX are as described in the docstring for svn_client_merge_peg3().
9808 
9809    If IGNORE_MERGEINFO is true, disable merge tracking, by treating the two
9810    sources as unrelated even if they actually have a common ancestor.  See
9811    the macro HONOR_MERGEINFO().
9812 
9813    If DIFF_IGNORE_ANCESTRY is true, diff the 'left' and 'right' versions
9814    of a node (if they are the same kind) as if they were related, even if
9815    they are not related.  Otherwise, diff unrelated items as a deletion
9816    of one thing and the addition of another.
9817 
9818    If not NULL, RECORD_ONLY_PATHS is a hash of (const char *) paths mapped
9819    to the same.  If RECORD_ONLY is true and RECORD_ONLY_PATHS is not NULL,
9820    then record mergeinfo describing the merge only on subtrees which contain
9821    items from RECORD_ONLY_PATHS.  If RECORD_ONLY is true and RECORD_ONLY_PATHS
9822    is NULL, then record mergeinfo on every subtree with mergeinfo in
9823    TARGET.
9824 
9825    REINTEGRATE_MERGE is TRUE if this is a reintegrate merge.
9826 
9827    *USE_SLEEP will be set TRUE if a sleep is required to ensure timestamp
9828    integrity, *USE_SLEEP will be unchanged if no sleep is required.
9829 
9830    SCRATCH_POOL is used for all temporary allocations.
9831 */
9832 static svn_error_t *
do_merge(apr_hash_t ** modified_subtrees,svn_mergeinfo_catalog_t result_catalog,svn_client__conflict_report_t ** conflict_report,svn_boolean_t * use_sleep,const apr_array_header_t * merge_sources,const merge_target_t * target,svn_ra_session_t * src_session,svn_boolean_t sources_related,svn_boolean_t same_repos,svn_boolean_t ignore_mergeinfo,svn_boolean_t diff_ignore_ancestry,svn_boolean_t force_delete,svn_boolean_t dry_run,svn_boolean_t record_only,apr_hash_t * record_only_paths,svn_boolean_t reintegrate_merge,svn_boolean_t squelch_mergeinfo_notifications,svn_depth_t depth,const apr_array_header_t * merge_options,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)9833 do_merge(apr_hash_t **modified_subtrees,
9834          svn_mergeinfo_catalog_t result_catalog,
9835          svn_client__conflict_report_t **conflict_report,
9836          svn_boolean_t *use_sleep,
9837          const apr_array_header_t *merge_sources,
9838          const merge_target_t *target,
9839          svn_ra_session_t *src_session,
9840          svn_boolean_t sources_related,
9841          svn_boolean_t same_repos,
9842          svn_boolean_t ignore_mergeinfo,
9843          svn_boolean_t diff_ignore_ancestry,
9844          svn_boolean_t force_delete,
9845          svn_boolean_t dry_run,
9846          svn_boolean_t record_only,
9847          apr_hash_t *record_only_paths,
9848          svn_boolean_t reintegrate_merge,
9849          svn_boolean_t squelch_mergeinfo_notifications,
9850          svn_depth_t depth,
9851          const apr_array_header_t *merge_options,
9852          svn_client_ctx_t *ctx,
9853          apr_pool_t *result_pool,
9854          apr_pool_t *scratch_pool)
9855 {
9856   merge_cmd_baton_t merge_cmd_baton = { 0 };
9857   svn_config_t *cfg;
9858   const char *diff3_cmd;
9859   const char *preserved_exts_str;
9860   int i;
9861   svn_boolean_t checked_mergeinfo_capability = FALSE;
9862   svn_ra_session_t *ra_session1 = NULL, *ra_session2 = NULL;
9863   const char *old_src_session_url = NULL;
9864   apr_pool_t *iterpool;
9865   const svn_diff_tree_processor_t *processor;
9866 
9867   SVN_ERR_ASSERT(svn_dirent_is_absolute(target->abspath));
9868 
9869   *conflict_report = NULL;
9870 
9871   /* Check from some special conditions when in record-only mode
9872      (which is a merge-tracking thing). */
9873   if (record_only)
9874     {
9875       svn_boolean_t sources_ancestral = TRUE;
9876       int j;
9877 
9878       /* Find out whether all of the sources are 'ancestral'. */
9879       for (j = 0; j < merge_sources->nelts; j++)
9880         if (! APR_ARRAY_IDX(merge_sources, j, merge_source_t *)->ancestral)
9881           {
9882             sources_ancestral = FALSE;
9883             break;
9884           }
9885 
9886       /* We can't do a record-only merge if the sources aren't related. */
9887       if (! sources_ancestral)
9888         return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
9889                                 _("Use of two URLs is not compatible with "
9890                                   "mergeinfo modification"));
9891 
9892       /* We can't do a record-only merge if the sources aren't from
9893          the same repository as the target. */
9894       if (! same_repos)
9895         return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
9896                                 _("Merge from foreign repository is not "
9897                                   "compatible with mergeinfo modification"));
9898 
9899       /* If this is a dry-run record-only merge, there's nothing to do. */
9900       if (dry_run)
9901         return SVN_NO_ERROR;
9902     }
9903 
9904   iterpool = svn_pool_create(scratch_pool);
9905 
9906   /* Ensure a known depth. */
9907   if (depth == svn_depth_unknown)
9908     depth = svn_depth_infinity;
9909 
9910   /* Set up the diff3 command, so various callers don't have to. */
9911   cfg = ctx->config
9912         ? svn_hash_gets(ctx->config, SVN_CONFIG_CATEGORY_CONFIG)
9913         : NULL;
9914   svn_config_get(cfg, &diff3_cmd, SVN_CONFIG_SECTION_HELPERS,
9915                  SVN_CONFIG_OPTION_DIFF3_CMD, NULL);
9916 
9917   if (diff3_cmd != NULL)
9918     SVN_ERR(svn_path_cstring_to_utf8(&diff3_cmd, diff3_cmd, scratch_pool));
9919 
9920     /* See which files the user wants to preserve the extension of when
9921      conflict files are made. */
9922   svn_config_get(cfg, &preserved_exts_str, SVN_CONFIG_SECTION_MISCELLANY,
9923                  SVN_CONFIG_OPTION_PRESERVED_CF_EXTS, "");
9924 
9925   /* Build the merge context baton (or at least the parts of it that
9926      don't need to be reset for each merge source).  */
9927   merge_cmd_baton.force_delete = force_delete;
9928   merge_cmd_baton.dry_run = dry_run;
9929   merge_cmd_baton.record_only = record_only;
9930   merge_cmd_baton.ignore_mergeinfo = ignore_mergeinfo;
9931   merge_cmd_baton.diff_ignore_ancestry = diff_ignore_ancestry;
9932   merge_cmd_baton.same_repos = same_repos;
9933   merge_cmd_baton.mergeinfo_capable = FALSE;
9934   merge_cmd_baton.ctx = ctx;
9935   merge_cmd_baton.reintegrate_merge = reintegrate_merge;
9936   merge_cmd_baton.target = target;
9937   merge_cmd_baton.pool = iterpool;
9938   merge_cmd_baton.merge_options = merge_options;
9939   merge_cmd_baton.diff3_cmd = diff3_cmd;
9940   merge_cmd_baton.ext_patterns = *preserved_exts_str
9941                           ? svn_cstring_split(preserved_exts_str, "\n\r\t\v ",
9942                                               FALSE, scratch_pool)
9943                           : NULL;
9944 
9945   merge_cmd_baton.use_sleep = use_sleep;
9946 
9947   /* Do we already know the specific subtrees with mergeinfo we want
9948      to record-only mergeinfo on? */
9949   if (record_only && record_only_paths)
9950     merge_cmd_baton.merged_abspaths = record_only_paths;
9951   else
9952     merge_cmd_baton.merged_abspaths = apr_hash_make(result_pool);
9953 
9954   merge_cmd_baton.skipped_abspaths = apr_hash_make(result_pool);
9955   merge_cmd_baton.added_abspaths = apr_hash_make(result_pool);
9956   merge_cmd_baton.tree_conflicted_abspaths = apr_hash_make(result_pool);
9957 
9958   merge_cmd_baton.notify_func = notify_merging;
9959   merge_cmd_baton.notify_baton = &merge_cmd_baton.notify_begin;
9960   merge_cmd_baton.notify_begin.merge_b = &merge_cmd_baton;
9961   merge_cmd_baton.notify_begin.notify_func2 = ctx->notify_func2;
9962   merge_cmd_baton.notify_begin.notify_baton2 = ctx->notify_baton2;
9963 
9964   processor = merge_apply_processor(&merge_cmd_baton, scratch_pool);
9965 
9966   if (src_session)
9967     {
9968       SVN_ERR(svn_ra_get_session_url(src_session, &old_src_session_url,
9969                                      scratch_pool));
9970       ra_session1 = src_session;
9971     }
9972 
9973   for (i = 0; i < merge_sources->nelts; i++)
9974     {
9975       svn_node_kind_t src1_kind;
9976       merge_source_t *source =
9977         APR_ARRAY_IDX(merge_sources, i, merge_source_t *);
9978       single_range_conflict_report_t *conflicted_range_report;
9979 
9980       svn_pool_clear(iterpool);
9981 
9982       /* Sanity check:  if our left- and right-side merge sources are
9983          the same, there's nothing to here. */
9984       if ((strcmp(source->loc1->url, source->loc2->url) == 0)
9985           && (source->loc1->rev == source->loc2->rev))
9986         continue;
9987 
9988       /* Establish RA sessions to our URLs, reuse where possible. */
9989       SVN_ERR(ensure_ra_session_url(&ra_session1, source->loc1->url,
9990                                     target->abspath, ctx, scratch_pool));
9991       SVN_ERR(ensure_ra_session_url(&ra_session2, source->loc2->url,
9992                                     target->abspath, ctx, scratch_pool));
9993 
9994       /* Populate the portions of the merge context baton that need to
9995          be reset for each merge source iteration. */
9996       merge_cmd_baton.merge_source = *source;
9997       merge_cmd_baton.implicit_src_gap = NULL;
9998       merge_cmd_baton.conflicted_paths = NULL;
9999       merge_cmd_baton.paths_with_new_mergeinfo = NULL;
10000       merge_cmd_baton.paths_with_deleted_mergeinfo = NULL;
10001       merge_cmd_baton.ra_session1 = ra_session1;
10002       merge_cmd_baton.ra_session2 = ra_session2;
10003 
10004       merge_cmd_baton.notify_begin.last_abspath = NULL;
10005 
10006       /* Populate the portions of the merge context baton that require
10007          an RA session to set, but shouldn't be reset for each iteration. */
10008       if (! checked_mergeinfo_capability)
10009         {
10010           SVN_ERR(svn_ra_has_capability(ra_session1,
10011                                         &merge_cmd_baton.mergeinfo_capable,
10012                                         SVN_RA_CAPABILITY_MERGEINFO,
10013                                         iterpool));
10014           checked_mergeinfo_capability = TRUE;
10015         }
10016 
10017       SVN_ERR(svn_ra_check_path(ra_session1, "", source->loc1->rev,
10018                                 &src1_kind, iterpool));
10019 
10020       /* Run the merge; if there are conflicts, allow the callback to
10021        * resolve them, and if it resolves all of them, then run the
10022        * merge again with the remaining revision range, until it is all
10023        * done. */
10024       do
10025         {
10026           /* Merge as far as possible without resolving any conflicts */
10027           if (src1_kind != svn_node_dir)
10028             {
10029               SVN_ERR(do_file_merge(result_catalog, &conflicted_range_report,
10030                                     source, target->abspath,
10031                                     processor,
10032                                     sources_related,
10033                                     squelch_mergeinfo_notifications,
10034                                     &merge_cmd_baton, iterpool, iterpool));
10035             }
10036           else /* Directory */
10037             {
10038               SVN_ERR(do_directory_merge(result_catalog, &conflicted_range_report,
10039                                          source, target->abspath,
10040                                          processor,
10041                                          depth, squelch_mergeinfo_notifications,
10042                                          &merge_cmd_baton, iterpool, iterpool));
10043             }
10044 
10045           /* Give the conflict resolver callback the opportunity to
10046            * resolve any conflicts that were raised.  If it resolves all
10047            * of them, go around again to merge the next sub-range (if any). */
10048           if (conflicted_range_report && ctx->conflict_func2 && ! dry_run)
10049             {
10050               svn_boolean_t conflicts_remain;
10051 
10052               SVN_ERR(svn_client__resolve_conflicts(
10053                         &conflicts_remain, merge_cmd_baton.conflicted_paths,
10054                         ctx, iterpool));
10055               if (conflicts_remain)
10056                 break;
10057 
10058               merge_cmd_baton.conflicted_paths = NULL;
10059               /* Caution: this source is in iterpool */
10060               source = conflicted_range_report->remaining_source;
10061               conflicted_range_report = NULL;
10062             }
10063           else
10064             break;
10065         }
10066       while (source);
10067 
10068       /* The final mergeinfo on TARGET_WCPATH may itself elide. */
10069       if (! dry_run)
10070         SVN_ERR(svn_client__elide_mergeinfo(target->abspath, NULL,
10071                                             ctx, iterpool));
10072 
10073       /* If conflicts occurred while merging any but the very last
10074        * range of a multi-pass merge, we raise an error that aborts
10075        * the merge. The user will be asked to resolve conflicts
10076        * before merging subsequent revision ranges. */
10077       if (conflicted_range_report)
10078         {
10079           *conflict_report = conflict_report_create(
10080                                target->abspath, conflicted_range_report->conflicted_range,
10081                                (i == merge_sources->nelts - 1
10082                                 && ! conflicted_range_report->remaining_source),
10083                                result_pool);
10084           break;
10085         }
10086     }
10087 
10088   if (! *conflict_report || (*conflict_report)->was_last_range)
10089     {
10090       /* Let everyone know we're finished here. */
10091       notify_merge_completed(target->abspath, ctx, iterpool);
10092     }
10093 
10094   /* Does the caller want to know what the merge has done? */
10095   if (modified_subtrees)
10096     {
10097       *modified_subtrees =
10098           apr_hash_overlay(result_pool, *modified_subtrees,
10099                            merge_cmd_baton.merged_abspaths);
10100       *modified_subtrees =
10101           apr_hash_overlay(result_pool, *modified_subtrees,
10102                            merge_cmd_baton.added_abspaths);
10103       *modified_subtrees =
10104           apr_hash_overlay(result_pool, *modified_subtrees,
10105                            merge_cmd_baton.skipped_abspaths);
10106       *modified_subtrees =
10107           apr_hash_overlay(result_pool, *modified_subtrees,
10108                            merge_cmd_baton.tree_conflicted_abspaths);
10109     }
10110 
10111   if (src_session)
10112     SVN_ERR(svn_ra_reparent(src_session, old_src_session_url, iterpool));
10113 
10114   svn_pool_destroy(iterpool);
10115   return SVN_NO_ERROR;
10116 }
10117 
10118 /* Perform a two-URL merge between URLs which are related, but neither
10119    is a direct ancestor of the other.  This first does a real two-URL
10120    merge (unless this is record-only), followed by record-only merges
10121    to represent the changed mergeinfo.
10122 
10123    Set *CONFLICT_REPORT to indicate if there were any conflicts, as in
10124    do_merge().
10125 
10126    The diff to be merged is between SOURCE->loc1 (in URL1_RA_SESSION1)
10127    and SOURCE->loc2 (in URL2_RA_SESSION2); YCA is their youngest
10128    common ancestor.
10129 
10130    SAME_REPOS must be true if and only if the source URLs are in the same
10131    repository as the target working copy.
10132 
10133    DIFF_IGNORE_ANCESTRY is as in do_merge().
10134 
10135    Other arguments are as in all of the public merge APIs.
10136 
10137    *USE_SLEEP will be set TRUE if a sleep is required to ensure timestamp
10138    integrity, *USE_SLEEP will be unchanged if no sleep is required.
10139 
10140    SCRATCH_POOL is used for all temporary allocations.
10141  */
10142 static svn_error_t *
merge_cousins_and_supplement_mergeinfo(svn_client__conflict_report_t ** conflict_report,svn_boolean_t * use_sleep,const merge_target_t * target,svn_ra_session_t * URL1_ra_session,svn_ra_session_t * URL2_ra_session,const merge_source_t * source,const svn_client__pathrev_t * yca,svn_boolean_t same_repos,svn_depth_t depth,svn_boolean_t diff_ignore_ancestry,svn_boolean_t force_delete,svn_boolean_t record_only,svn_boolean_t dry_run,const apr_array_header_t * merge_options,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)10143 merge_cousins_and_supplement_mergeinfo(
10144   svn_client__conflict_report_t **conflict_report,
10145   svn_boolean_t *use_sleep,
10146   const merge_target_t *target,
10147   svn_ra_session_t *URL1_ra_session,
10148   svn_ra_session_t *URL2_ra_session,
10149   const merge_source_t *source,
10150   const svn_client__pathrev_t *yca,
10151   svn_boolean_t same_repos,
10152   svn_depth_t depth,
10153   svn_boolean_t diff_ignore_ancestry,
10154   svn_boolean_t force_delete,
10155   svn_boolean_t record_only,
10156   svn_boolean_t dry_run,
10157   const apr_array_header_t *merge_options,
10158   svn_client_ctx_t *ctx,
10159   apr_pool_t *result_pool,
10160   apr_pool_t *scratch_pool)
10161 {
10162   apr_array_header_t *remove_sources, *add_sources;
10163   apr_hash_t *modified_subtrees = NULL;
10164 
10165   /* Sure we could use SCRATCH_POOL throughout this function, but since this
10166      is a wrapper around three separate merges we'll create a subpool we can
10167      clear between each of the three.  If the merge target has a lot of
10168      subtree mergeinfo, then this will help keep memory use in check. */
10169   apr_pool_t *subpool = svn_pool_create(scratch_pool);
10170 
10171   assert(session_url_is(URL1_ra_session, source->loc1->url, scratch_pool));
10172   assert(session_url_is(URL2_ra_session, source->loc2->url, scratch_pool));
10173 
10174   SVN_ERR_ASSERT(svn_dirent_is_absolute(target->abspath));
10175   SVN_ERR_ASSERT(! source->ancestral);
10176 
10177   SVN_ERR(normalize_merge_sources_internal(
10178             &remove_sources, source->loc1,
10179             svn_rangelist__initialize(source->loc1->rev, yca->rev, TRUE,
10180                                       scratch_pool),
10181             URL1_ra_session, ctx, scratch_pool, subpool));
10182 
10183   SVN_ERR(normalize_merge_sources_internal(
10184             &add_sources, source->loc2,
10185             svn_rangelist__initialize(yca->rev, source->loc2->rev, TRUE,
10186                                       scratch_pool),
10187             URL2_ra_session, ctx, scratch_pool, subpool));
10188 
10189   *conflict_report = NULL;
10190 
10191   /* If this isn't a record-only merge, we'll first do a stupid
10192      point-to-point merge... */
10193   if (! record_only)
10194     {
10195       apr_array_header_t *faux_sources =
10196         apr_array_make(scratch_pool, 1, sizeof(merge_source_t *));
10197 
10198       modified_subtrees = apr_hash_make(scratch_pool);
10199       APR_ARRAY_PUSH(faux_sources, const merge_source_t *) = source;
10200       SVN_ERR(do_merge(&modified_subtrees, NULL, conflict_report, use_sleep,
10201                        faux_sources, target,
10202                        URL1_ra_session, TRUE, same_repos,
10203                        FALSE /*ignore_mergeinfo*/, diff_ignore_ancestry,
10204                        force_delete, dry_run, FALSE, NULL, TRUE,
10205                        FALSE, depth, merge_options, ctx,
10206                        scratch_pool, subpool));
10207       if (*conflict_report)
10208         {
10209           *conflict_report = conflict_report_dup(*conflict_report, result_pool);
10210           if (! (*conflict_report)->was_last_range)
10211             return SVN_NO_ERROR;
10212         }
10213     }
10214   else if (! same_repos)
10215     {
10216       return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
10217                               _("Merge from foreign repository is not "
10218                                 "compatible with mergeinfo modification"));
10219     }
10220 
10221   /* ... and now, if we're doing the mergeinfo thang, we execute a
10222      pair of record-only merges using the real sources we've
10223      calculated.
10224 
10225      Issue #3648: We don't actually perform these two record-only merges
10226      on the WC at first, but rather see what each would do and store that
10227      in two mergeinfo catalogs.  We then merge the catalogs together and
10228      then record the result in the WC.  This prevents the second record
10229      only merge from removing legitimate mergeinfo history, from the same
10230      source, that was made in prior merges. */
10231   if (same_repos && !dry_run)
10232     {
10233       svn_mergeinfo_catalog_t add_result_catalog =
10234         apr_hash_make(scratch_pool);
10235       svn_mergeinfo_catalog_t remove_result_catalog =
10236         apr_hash_make(scratch_pool);
10237 
10238       notify_mergeinfo_recording(target->abspath, NULL, ctx, scratch_pool);
10239       svn_pool_clear(subpool);
10240       SVN_ERR(do_merge(NULL, add_result_catalog, conflict_report, use_sleep,
10241                        add_sources, target,
10242                        URL1_ra_session, TRUE, same_repos,
10243                        FALSE /*ignore_mergeinfo*/, diff_ignore_ancestry,
10244                        force_delete, dry_run, TRUE,
10245                        modified_subtrees, TRUE,
10246                        TRUE, depth, merge_options, ctx,
10247                        scratch_pool, subpool));
10248       if (*conflict_report)
10249         {
10250           *conflict_report = conflict_report_dup(*conflict_report, result_pool);
10251           if (! (*conflict_report)->was_last_range)
10252             return SVN_NO_ERROR;
10253         }
10254       svn_pool_clear(subpool);
10255       SVN_ERR(do_merge(NULL, remove_result_catalog, conflict_report, use_sleep,
10256                        remove_sources, target,
10257                        URL1_ra_session, TRUE, same_repos,
10258                        FALSE /*ignore_mergeinfo*/, diff_ignore_ancestry,
10259                        force_delete, dry_run, TRUE,
10260                        modified_subtrees, TRUE,
10261                        TRUE, depth, merge_options, ctx,
10262                        scratch_pool, subpool));
10263       if (*conflict_report)
10264         {
10265           *conflict_report = conflict_report_dup(*conflict_report, result_pool);
10266           if (! (*conflict_report)->was_last_range)
10267             return SVN_NO_ERROR;
10268         }
10269       SVN_ERR(svn_mergeinfo_catalog_merge(add_result_catalog,
10270                                           remove_result_catalog,
10271                                           scratch_pool, scratch_pool));
10272       SVN_ERR(svn_client__record_wc_mergeinfo_catalog(add_result_catalog,
10273                                                       ctx, scratch_pool));
10274     }
10275 
10276   svn_pool_destroy(subpool);
10277   return SVN_NO_ERROR;
10278 }
10279 
10280 /* Perform checks to determine whether the working copy at TARGET_ABSPATH
10281  * can safely be used as a merge target. Checks are performed according to
10282  * the ALLOW_MIXED_REV, ALLOW_LOCAL_MODS, and ALLOW_SWITCHED_SUBTREES
10283  * parameters. If any checks fail, raise SVN_ERR_CLIENT_NOT_READY_TO_MERGE.
10284  *
10285  * E.g. if all the ALLOW_* parameters are FALSE, TARGET_ABSPATH must
10286  * be a single-revision, pristine, unswitched working copy.
10287  * In other words, it must reflect a subtree of the repository as found
10288  * at single revision -- although sparse checkouts are permitted. */
10289 static svn_error_t *
ensure_wc_is_suitable_merge_target(const char * target_abspath,svn_client_ctx_t * ctx,svn_boolean_t allow_mixed_rev,svn_boolean_t allow_local_mods,svn_boolean_t allow_switched_subtrees,apr_pool_t * scratch_pool)10290 ensure_wc_is_suitable_merge_target(const char *target_abspath,
10291                                    svn_client_ctx_t *ctx,
10292                                    svn_boolean_t allow_mixed_rev,
10293                                    svn_boolean_t allow_local_mods,
10294                                    svn_boolean_t allow_switched_subtrees,
10295                                    apr_pool_t *scratch_pool)
10296 {
10297   svn_node_kind_t target_kind;
10298 
10299   /* Check the target exists. */
10300   SVN_ERR(svn_io_check_path(target_abspath, &target_kind, scratch_pool));
10301   if (target_kind == svn_node_none)
10302     return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
10303                              _("Path '%s' does not exist"),
10304                              svn_dirent_local_style(target_abspath,
10305                                                     scratch_pool));
10306   SVN_ERR(svn_wc_read_kind2(&target_kind, ctx->wc_ctx, target_abspath,
10307                             FALSE, FALSE, scratch_pool));
10308   if (target_kind != svn_node_dir && target_kind != svn_node_file)
10309     return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
10310                              _("Merge target '%s' does not exist in the "
10311                                "working copy"), target_abspath);
10312 
10313   /* Perform the mixed-revision check first because it's the cheapest one. */
10314   if (! allow_mixed_rev)
10315     {
10316       svn_revnum_t min_rev;
10317       svn_revnum_t max_rev;
10318 
10319       SVN_ERR(svn_client_min_max_revisions(&min_rev, &max_rev, target_abspath,
10320                                            FALSE, ctx, scratch_pool));
10321 
10322       if (!(SVN_IS_VALID_REVNUM(min_rev) && SVN_IS_VALID_REVNUM(max_rev)))
10323         {
10324           svn_boolean_t is_added;
10325 
10326           /* Allow merge into added nodes. */
10327           SVN_ERR(svn_wc__node_is_added(&is_added, ctx->wc_ctx, target_abspath,
10328                                         scratch_pool));
10329           if (is_added)
10330             return SVN_NO_ERROR;
10331           else
10332             return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
10333                                     _("Cannot determine revision of working "
10334                                       "copy"));
10335         }
10336 
10337       if (min_rev != max_rev)
10338         return svn_error_createf(SVN_ERR_CLIENT_MERGE_UPDATE_REQUIRED, NULL,
10339                                  _("Cannot merge into mixed-revision working "
10340                                    "copy [%ld:%ld]; try updating first"),
10341                                    min_rev, max_rev);
10342     }
10343 
10344   /* Next, check for switched subtrees. */
10345   if (! allow_switched_subtrees)
10346     {
10347       svn_boolean_t is_switched;
10348 
10349       SVN_ERR(svn_wc__has_switched_subtrees(&is_switched, ctx->wc_ctx,
10350                                             target_abspath, NULL,
10351                                             scratch_pool));
10352       if (is_switched)
10353         return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
10354                                 _("Cannot merge into a working copy "
10355                                   "with a switched subtree"));
10356     }
10357 
10358   /* This is the most expensive check, so it is performed last.*/
10359   if (! allow_local_mods)
10360     {
10361       svn_boolean_t is_modified;
10362 
10363       SVN_ERR(svn_wc__has_local_mods(&is_modified, ctx->wc_ctx,
10364                                      target_abspath, TRUE,
10365                                      ctx->cancel_func,
10366                                      ctx->cancel_baton,
10367                                      scratch_pool));
10368       if (is_modified)
10369         return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
10370                                 _("Cannot merge into a working copy "
10371                                   "that has local modifications"));
10372     }
10373 
10374   return SVN_NO_ERROR;
10375 }
10376 
10377 /* Throw an error if PATH_OR_URL is a path and REVISION isn't a repository
10378  * revision. */
10379 static svn_error_t *
ensure_wc_path_has_repo_revision(const char * path_or_url,const svn_opt_revision_t * revision,apr_pool_t * scratch_pool)10380 ensure_wc_path_has_repo_revision(const char *path_or_url,
10381                                  const svn_opt_revision_t *revision,
10382                                  apr_pool_t *scratch_pool)
10383 {
10384   if (revision->kind != svn_opt_revision_number
10385       && revision->kind != svn_opt_revision_date
10386       && revision->kind != svn_opt_revision_head
10387       && ! svn_path_is_url(path_or_url))
10388     return svn_error_createf(
10389       SVN_ERR_CLIENT_BAD_REVISION, NULL,
10390       _("Invalid merge source '%s'; a working copy path can only be "
10391         "used with a repository revision (a number, a date, or head)"),
10392       svn_dirent_local_style(path_or_url, scratch_pool));
10393   return SVN_NO_ERROR;
10394 }
10395 
10396 /* "Open" the target WC for a merge.  That means:
10397  *   - find out its exact repository location
10398  *   - check the WC for suitability (throw an error if unsuitable)
10399  *
10400  * Set *TARGET_P to a new, fully initialized, target description structure.
10401  *
10402  * ALLOW_MIXED_REV, ALLOW_LOCAL_MODS, ALLOW_SWITCHED_SUBTREES determine
10403  * whether the WC is deemed suitable; see ensure_wc_is_suitable_merge_target()
10404  * for details.
10405  *
10406  * If the node is locally added, the rev and URL will be null/invalid. Some
10407  * kinds of merge can use such a target; others can't.
10408  */
10409 static svn_error_t *
open_target_wc(merge_target_t ** target_p,const char * wc_abspath,svn_boolean_t allow_mixed_rev,svn_boolean_t allow_local_mods,svn_boolean_t allow_switched_subtrees,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)10410 open_target_wc(merge_target_t **target_p,
10411                const char *wc_abspath,
10412                svn_boolean_t allow_mixed_rev,
10413                svn_boolean_t allow_local_mods,
10414                svn_boolean_t allow_switched_subtrees,
10415                svn_client_ctx_t *ctx,
10416                apr_pool_t *result_pool,
10417                apr_pool_t *scratch_pool)
10418 {
10419   merge_target_t *target = apr_palloc(result_pool, sizeof(*target));
10420   svn_client__pathrev_t *origin;
10421 
10422   target->abspath = apr_pstrdup(result_pool, wc_abspath);
10423 
10424   SVN_ERR(svn_client__wc_node_get_origin(&origin, wc_abspath, ctx,
10425                                          result_pool, scratch_pool));
10426   if (origin)
10427     {
10428       target->loc = *origin;
10429     }
10430   else
10431     {
10432       svn_error_t *err;
10433       /* The node has no location in the repository. It's unversioned or
10434        * locally added or locally deleted.
10435        *
10436        * If it's locally added or deleted, find the repository root
10437        * URL and UUID anyway, and leave the node URL and revision as NULL
10438        * and INVALID.  If it's unversioned, this will throw an error. */
10439       err = svn_wc__node_get_repos_info(NULL, NULL,
10440                                         &target->loc.repos_root_url,
10441                                         &target->loc.repos_uuid,
10442                                         ctx->wc_ctx, wc_abspath,
10443                                         result_pool, scratch_pool);
10444 
10445       if (err)
10446         {
10447           if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND
10448               && err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY
10449               && err->apr_err != SVN_ERR_WC_UPGRADE_REQUIRED)
10450             return svn_error_trace(err);
10451 
10452           return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, err,
10453                                    _("Merge target '%s' does not exist in the "
10454                                      "working copy"),
10455                                    svn_dirent_local_style(wc_abspath,
10456                                                           scratch_pool));
10457         }
10458 
10459       target->loc.rev = SVN_INVALID_REVNUM;
10460       target->loc.url = NULL;
10461     }
10462 
10463   SVN_ERR(ensure_wc_is_suitable_merge_target(
10464             wc_abspath, ctx,
10465             allow_mixed_rev, allow_local_mods, allow_switched_subtrees,
10466             scratch_pool));
10467 
10468   *target_p = target;
10469   return SVN_NO_ERROR;
10470 }
10471 
10472 /*-----------------------------------------------------------------------*/
10473 
10474 /*** Public APIs ***/
10475 
10476 /* The body of svn_client_merge5(), which see for details.
10477  *
10478  * If SOURCE1 @ REVISION1 is related to SOURCE2 @ REVISION2 then use merge
10479  * tracking (subject to other constraints -- see HONOR_MERGEINFO());
10480  * otherwise disable merge tracking.
10481  *
10482  * IGNORE_MERGEINFO and DIFF_IGNORE_ANCESTRY are as in do_merge().
10483  */
10484 svn_error_t *
svn_client__merge_locked(svn_client__conflict_report_t ** conflict_report,const char * source1,const svn_opt_revision_t * revision1,const char * source2,const svn_opt_revision_t * revision2,const char * target_abspath,svn_depth_t depth,svn_boolean_t ignore_mergeinfo,svn_boolean_t diff_ignore_ancestry,svn_boolean_t force_delete,svn_boolean_t record_only,svn_boolean_t dry_run,svn_boolean_t allow_mixed_rev,const apr_array_header_t * merge_options,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)10485 svn_client__merge_locked(svn_client__conflict_report_t **conflict_report,
10486                          const char *source1,
10487                          const svn_opt_revision_t *revision1,
10488                          const char *source2,
10489                          const svn_opt_revision_t *revision2,
10490                          const char *target_abspath,
10491                          svn_depth_t depth,
10492                          svn_boolean_t ignore_mergeinfo,
10493                          svn_boolean_t diff_ignore_ancestry,
10494                          svn_boolean_t force_delete,
10495                          svn_boolean_t record_only,
10496                          svn_boolean_t dry_run,
10497                          svn_boolean_t allow_mixed_rev,
10498                          const apr_array_header_t *merge_options,
10499                          svn_client_ctx_t *ctx,
10500                          apr_pool_t *result_pool,
10501                          apr_pool_t *scratch_pool)
10502 {
10503   merge_target_t *target;
10504   svn_client__pathrev_t *source1_loc, *source2_loc;
10505   svn_boolean_t sources_related = FALSE;
10506   svn_ra_session_t *ra_session1, *ra_session2;
10507   apr_array_header_t *merge_sources;
10508   svn_error_t *err;
10509   svn_boolean_t use_sleep = FALSE;
10510   svn_client__pathrev_t *yca = NULL;
10511   apr_pool_t *sesspool;
10512   svn_boolean_t same_repos;
10513 
10514   /* ### FIXME: This function really ought to do a history check on
10515      the left and right sides of the merge source, and -- if one is an
10516      ancestor of the other -- just call svn_client_merge_peg3() with
10517      the appropriate args. */
10518 
10519   SVN_ERR(open_target_wc(&target, target_abspath,
10520                          allow_mixed_rev, TRUE, TRUE,
10521                          ctx, scratch_pool, scratch_pool));
10522 
10523   /* Open RA sessions to both sides of our merge source, and resolve URLs
10524    * and revisions. */
10525   sesspool = svn_pool_create(scratch_pool);
10526   SVN_ERR(svn_client__ra_session_from_path2(
10527             &ra_session1, &source1_loc,
10528             source1, NULL, revision1, revision1, ctx, sesspool));
10529   SVN_ERR(svn_client__ra_session_from_path2(
10530             &ra_session2, &source2_loc,
10531             source2, NULL, revision2, revision2, ctx, sesspool));
10532 
10533   /* We can't do a diff between different repositories. */
10534   /* ### We should also insist that the root URLs of the two sources match,
10535    *     as we are only carrying around a single source-repos-root from now
10536    *     on, and URL calculations will go wrong if they differ.
10537    *     Alternatively, teach the code to cope with differing root URLs. */
10538   SVN_ERR(check_same_repos(source1_loc, source1_loc->url,
10539                            source2_loc, source2_loc->url,
10540                            FALSE /* strict_urls */, scratch_pool));
10541 
10542   /* Do our working copy and sources come from the same repository? */
10543   same_repos = is_same_repos(&target->loc, source1_loc, TRUE /* strict_urls */);
10544 
10545   /* Unless we're ignoring ancestry, see if the two sources are related.  */
10546   if (! ignore_mergeinfo)
10547     SVN_ERR(svn_client__get_youngest_common_ancestor(
10548                     &yca, source1_loc, source2_loc, ra_session1, ctx,
10549                     scratch_pool, scratch_pool));
10550 
10551   /* Check for a youngest common ancestor.  If we have one, we'll be
10552      doing merge tracking.
10553 
10554      So, given a requested merge of the differences between A and
10555      B, and a common ancestor of C, we will find ourselves in one of
10556      four positions, and four different approaches:
10557 
10558         A == B == C   there's nothing to merge
10559 
10560         A == C != B   we merge the changes between A (or C) and B
10561 
10562         B == C != A   we merge the changes between B (or C) and A
10563 
10564         A != B != C   we merge the changes between A and B without
10565                       merge recording, then record-only two merges:
10566                       from A to C, and from C to B
10567   */
10568   if (yca)
10569     {
10570       /* Note that our merge sources are related. */
10571       sources_related = TRUE;
10572 
10573       /* If the common ancestor matches the right side of our merge,
10574          then we only need to reverse-merge the left side. */
10575       if ((strcmp(yca->url, source2_loc->url) == 0)
10576           && (yca->rev == source2_loc->rev))
10577         {
10578           SVN_ERR(normalize_merge_sources_internal(
10579                     &merge_sources, source1_loc,
10580                     svn_rangelist__initialize(source1_loc->rev, yca->rev, TRUE,
10581                                               scratch_pool),
10582                     ra_session1, ctx, scratch_pool, scratch_pool));
10583         }
10584       /* If the common ancestor matches the left side of our merge,
10585          then we only need to merge the right side. */
10586       else if ((strcmp(yca->url, source1_loc->url) == 0)
10587                && (yca->rev == source1_loc->rev))
10588         {
10589           SVN_ERR(normalize_merge_sources_internal(
10590                     &merge_sources, source2_loc,
10591                     svn_rangelist__initialize(yca->rev, source2_loc->rev, TRUE,
10592                                               scratch_pool),
10593                     ra_session2, ctx, scratch_pool, scratch_pool));
10594         }
10595       /* And otherwise, we need to do both: reverse merge the left
10596          side, and merge the right. */
10597       else
10598         {
10599           merge_source_t source;
10600 
10601           source.loc1 = source1_loc;
10602           source.loc2 = source2_loc;
10603           source.ancestral = FALSE;
10604 
10605           err = merge_cousins_and_supplement_mergeinfo(conflict_report,
10606                                                        &use_sleep,
10607                                                        target,
10608                                                        ra_session1,
10609                                                        ra_session2,
10610                                                        &source,
10611                                                        yca,
10612                                                        same_repos,
10613                                                        depth,
10614                                                        diff_ignore_ancestry,
10615                                                        force_delete,
10616                                                        record_only, dry_run,
10617                                                        merge_options,
10618                                                        ctx,
10619                                                        result_pool,
10620                                                        scratch_pool);
10621           /* Close our temporary RA sessions (this could've happened
10622              after the second call to normalize_merge_sources() inside
10623              the merge_cousins_and_supplement_mergeinfo() routine). */
10624           svn_pool_destroy(sesspool);
10625 
10626           if (use_sleep)
10627             svn_io_sleep_for_timestamps(target->abspath, scratch_pool);
10628 
10629           SVN_ERR(err);
10630           return SVN_NO_ERROR;
10631         }
10632     }
10633   else
10634     {
10635       /* Build a single-item merge_source_t array. */
10636       merge_sources = apr_array_make(scratch_pool, 1, sizeof(merge_source_t *));
10637       APR_ARRAY_PUSH(merge_sources, merge_source_t *)
10638         = merge_source_create(source1_loc, source2_loc, FALSE, scratch_pool);
10639     }
10640 
10641   err = do_merge(NULL, NULL, conflict_report, &use_sleep,
10642                  merge_sources, target,
10643                  ra_session1, sources_related, same_repos,
10644                  ignore_mergeinfo, diff_ignore_ancestry, force_delete, dry_run,
10645                  record_only, NULL, FALSE, FALSE, depth, merge_options,
10646                  ctx, result_pool, scratch_pool);
10647 
10648   /* Close our temporary RA sessions. */
10649   svn_pool_destroy(sesspool);
10650 
10651   if (use_sleep)
10652     svn_io_sleep_for_timestamps(target->abspath, scratch_pool);
10653 
10654   SVN_ERR(err);
10655   return SVN_NO_ERROR;
10656 }
10657 
10658 /* Set *TARGET_ABSPATH to the absolute path of, and *LOCK_ABSPATH to
10659  the absolute path to lock for, TARGET_WCPATH. */
10660 static svn_error_t *
get_target_and_lock_abspath(const char ** target_abspath,const char ** lock_abspath,const char * target_wcpath,svn_client_ctx_t * ctx,apr_pool_t * result_pool)10661 get_target_and_lock_abspath(const char **target_abspath,
10662                             const char **lock_abspath,
10663                             const char *target_wcpath,
10664                             svn_client_ctx_t *ctx,
10665                             apr_pool_t *result_pool)
10666 {
10667   svn_node_kind_t kind;
10668   SVN_ERR(svn_dirent_get_absolute(target_abspath, target_wcpath,
10669                                   result_pool));
10670   SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, *target_abspath,
10671                             FALSE, FALSE, result_pool));
10672   if (kind == svn_node_dir)
10673     *lock_abspath = *target_abspath;
10674   else
10675     *lock_abspath = svn_dirent_dirname(*target_abspath, result_pool);
10676 
10677   return SVN_NO_ERROR;
10678 }
10679 
10680 svn_error_t *
svn_client_merge5(const char * source1,const svn_opt_revision_t * revision1,const char * source2,const svn_opt_revision_t * revision2,const char * target_wcpath,svn_depth_t depth,svn_boolean_t ignore_mergeinfo,svn_boolean_t diff_ignore_ancestry,svn_boolean_t force_delete,svn_boolean_t record_only,svn_boolean_t dry_run,svn_boolean_t allow_mixed_rev,const apr_array_header_t * merge_options,svn_client_ctx_t * ctx,apr_pool_t * pool)10681 svn_client_merge5(const char *source1,
10682                   const svn_opt_revision_t *revision1,
10683                   const char *source2,
10684                   const svn_opt_revision_t *revision2,
10685                   const char *target_wcpath,
10686                   svn_depth_t depth,
10687                   svn_boolean_t ignore_mergeinfo,
10688                   svn_boolean_t diff_ignore_ancestry,
10689                   svn_boolean_t force_delete,
10690                   svn_boolean_t record_only,
10691                   svn_boolean_t dry_run,
10692                   svn_boolean_t allow_mixed_rev,
10693                   const apr_array_header_t *merge_options,
10694                   svn_client_ctx_t *ctx,
10695                   apr_pool_t *pool)
10696 {
10697   const char *target_abspath, *lock_abspath;
10698   svn_client__conflict_report_t *conflict_report;
10699 
10700   /* Sanity check our input -- we require specified revisions,
10701    * and either 2 paths or 2 URLs. */
10702   if ((revision1->kind == svn_opt_revision_unspecified)
10703       || (revision2->kind == svn_opt_revision_unspecified))
10704     return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL,
10705                             _("Not all required revisions are specified"));
10706   if (svn_path_is_url(source1) != svn_path_is_url(source2))
10707     return svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL,
10708                             _("Merge sources must both be "
10709                               "either paths or URLs"));
10710   /* A WC path must be used with a repository revision, as we can't
10711    * (currently) use the WC itself as a source, we can only read the URL
10712    * from it and use that. */
10713   SVN_ERR(ensure_wc_path_has_repo_revision(source1, revision1, pool));
10714   SVN_ERR(ensure_wc_path_has_repo_revision(source2, revision2, pool));
10715 
10716   SVN_ERR(get_target_and_lock_abspath(&target_abspath, &lock_abspath,
10717                                       target_wcpath, ctx, pool));
10718 
10719   if (!dry_run)
10720     SVN_WC__CALL_WITH_WRITE_LOCK(
10721       svn_client__merge_locked(&conflict_report,
10722                                source1, revision1, source2, revision2,
10723                                target_abspath, depth, ignore_mergeinfo,
10724                                diff_ignore_ancestry,
10725                                force_delete, record_only, dry_run,
10726                                allow_mixed_rev, merge_options, ctx, pool, pool),
10727       ctx->wc_ctx, lock_abspath, FALSE /* lock_anchor */, pool);
10728   else
10729     SVN_ERR(svn_client__merge_locked(&conflict_report,
10730                                      source1, revision1, source2, revision2,
10731                                      target_abspath, depth, ignore_mergeinfo,
10732                                      diff_ignore_ancestry,
10733                                      force_delete, record_only, dry_run,
10734                                      allow_mixed_rev, merge_options, ctx, pool,
10735                                      pool));
10736 
10737   SVN_ERR(svn_client__make_merge_conflict_error(conflict_report, pool));
10738   return SVN_NO_ERROR;
10739 }
10740 
10741 
10742 /* Check if mergeinfo for a given path is described explicitly or via
10743    inheritance in a mergeinfo catalog.
10744 
10745    If REPOS_REL_PATH exists in CATALOG and has mergeinfo containing
10746    MERGEINFO, then set *IN_CATALOG to TRUE.  If REPOS_REL_PATH does
10747    not exist in CATALOG, then find its nearest parent which does exist.
10748    If the mergeinfo REPOS_REL_PATH would inherit from that parent
10749    contains MERGEINFO then set *IN_CATALOG to TRUE.  Set *IN_CATALOG
10750    to FALSE in all other cases.
10751 
10752    Set *CAT_KEY_PATH to the key path in CATALOG for REPOS_REL_PATH's
10753    explicit or inherited mergeinfo.  If no explicit or inherited mergeinfo
10754    is found for REPOS_REL_PATH then set *CAT_KEY_PATH to NULL.
10755 
10756    User RESULT_POOL to allocate *CAT_KEY_PATH.  Use SCRATCH_POOL for
10757    temporary allocations. */
10758 static svn_error_t *
mergeinfo_in_catalog(svn_boolean_t * in_catalog,const char ** cat_key_path,const char * repos_rel_path,svn_mergeinfo_t mergeinfo,svn_mergeinfo_catalog_t catalog,apr_pool_t * result_pool,apr_pool_t * scratch_pool)10759 mergeinfo_in_catalog(svn_boolean_t *in_catalog,
10760                      const char **cat_key_path,
10761                      const char *repos_rel_path,
10762                      svn_mergeinfo_t mergeinfo,
10763                      svn_mergeinfo_catalog_t catalog,
10764                      apr_pool_t *result_pool,
10765                      apr_pool_t *scratch_pool)
10766 {
10767   const char *walk_path = NULL;
10768 
10769   *in_catalog = FALSE;
10770   *cat_key_path = NULL;
10771 
10772   if (mergeinfo && catalog && apr_hash_count(catalog))
10773     {
10774       const char *path = repos_rel_path;
10775 
10776       /* Start with the assumption there is no explicit or inherited
10777          mergeinfo for REPOS_REL_PATH in CATALOG. */
10778       svn_mergeinfo_t mergeinfo_in_cat = NULL;
10779 
10780       while (1)
10781         {
10782           mergeinfo_in_cat = svn_hash_gets(catalog, path);
10783 
10784           if (mergeinfo_in_cat) /* Found it! */
10785             {
10786               *cat_key_path = apr_pstrdup(result_pool, path);
10787               break;
10788             }
10789           else /* Look for inherited mergeinfo. */
10790             {
10791               walk_path = svn_relpath_join(svn_relpath_basename(path,
10792                                                                 scratch_pool),
10793                                            walk_path ? walk_path : "",
10794                                            scratch_pool);
10795               path = svn_relpath_dirname(path, scratch_pool);
10796 
10797               if (path[0] == '\0') /* No mergeinfo to inherit. */
10798                 break;
10799             }
10800         }
10801 
10802       if (mergeinfo_in_cat)
10803         {
10804           if (walk_path)
10805             SVN_ERR(svn_mergeinfo__add_suffix_to_mergeinfo(&mergeinfo_in_cat,
10806                                                            mergeinfo_in_cat,
10807                                                            walk_path,
10808                                                            scratch_pool,
10809                                                            scratch_pool));
10810           SVN_ERR(svn_mergeinfo_intersect2(&mergeinfo_in_cat,
10811                                            mergeinfo_in_cat, mergeinfo,
10812                                            TRUE,
10813                                            scratch_pool, scratch_pool));
10814           SVN_ERR(svn_mergeinfo__equals(in_catalog, mergeinfo_in_cat,
10815                                         mergeinfo, TRUE, scratch_pool));
10816         }
10817     }
10818 
10819   return SVN_NO_ERROR;
10820 }
10821 
10822 /* A svn_log_entry_receiver_t baton for log_find_operative_revs(). */
10823 typedef struct log_find_operative_baton_t
10824 {
10825   /* The catalog of explicit mergeinfo on a reintegrate source. */
10826   svn_mergeinfo_catalog_t merged_catalog;
10827 
10828   /* The catalog of unmerged history from the reintegrate target to
10829      the source which we will create.  Allocated in RESULT_POOL. */
10830   svn_mergeinfo_catalog_t unmerged_catalog;
10831 
10832   /* The repository absolute path of the reintegrate target. */
10833   const char *target_fspath;
10834 
10835   /* The path of the reintegrate source relative to the repository root. */
10836   const char *source_repos_rel_path;
10837 
10838   apr_pool_t *result_pool;
10839 } log_find_operative_baton_t;
10840 
10841 /* A svn_log_entry_receiver_t callback for find_unsynced_ranges(). */
10842 static svn_error_t *
log_find_operative_revs(void * baton,svn_log_entry_t * log_entry,apr_pool_t * pool)10843 log_find_operative_revs(void *baton,
10844                         svn_log_entry_t *log_entry,
10845                         apr_pool_t *pool)
10846 {
10847   log_find_operative_baton_t *log_baton = baton;
10848   apr_hash_index_t *hi;
10849   svn_revnum_t revision;
10850 
10851   /* It's possible that authz restrictions on the merge source prevent us
10852      from knowing about any of the changes for LOG_ENTRY->REVISION. */
10853   if (!log_entry->changed_paths2)
10854     return SVN_NO_ERROR;
10855 
10856   revision = log_entry->revision;
10857 
10858   for (hi = apr_hash_first(pool, log_entry->changed_paths2);
10859        hi;
10860        hi = apr_hash_next(hi))
10861     {
10862       const char *subtree_missing_this_rev;
10863       const char *path = apr_hash_this_key(hi);
10864       const char *rel_path;
10865       const char *source_rel_path;
10866       svn_boolean_t in_catalog;
10867       svn_mergeinfo_t log_entry_as_mergeinfo;
10868 
10869       rel_path = svn_fspath__skip_ancestor(log_baton->target_fspath, path);
10870       /* Easy out: The path is not within the tree of interest. */
10871       if (rel_path == NULL)
10872         continue;
10873 
10874       source_rel_path = svn_relpath_join(log_baton->source_repos_rel_path,
10875                                          rel_path, pool);
10876 
10877       SVN_ERR(svn_mergeinfo_parse(&log_entry_as_mergeinfo,
10878                                   apr_psprintf(pool, "%s:%ld",
10879                                                path, revision),
10880                                   pool));
10881 
10882       SVN_ERR(mergeinfo_in_catalog(&in_catalog, &subtree_missing_this_rev,
10883                                    source_rel_path, log_entry_as_mergeinfo,
10884                                    log_baton->merged_catalog,
10885                                    pool, pool));
10886 
10887       if (!in_catalog)
10888         {
10889           svn_mergeinfo_t unmerged_for_key;
10890           const char *suffix, *missing_path;
10891 
10892           /* If there is no mergeinfo on the source tree we'll say
10893              the "subtree" missing this revision is the root of the
10894              source. */
10895           if (!subtree_missing_this_rev)
10896             subtree_missing_this_rev = log_baton->source_repos_rel_path;
10897 
10898           suffix = svn_relpath_skip_ancestor(subtree_missing_this_rev,
10899                                              source_rel_path);
10900           if (suffix && suffix[0] != '\0')
10901             {
10902               missing_path = apr_pstrmemdup(pool, path,
10903                                             strlen(path) - strlen(suffix) - 1);
10904             }
10905           else
10906             {
10907               missing_path = path;
10908             }
10909 
10910           SVN_ERR(svn_mergeinfo_parse(&log_entry_as_mergeinfo,
10911                                       apr_psprintf(pool, "%s:%ld",
10912                                                    missing_path, revision),
10913                                       log_baton->result_pool));
10914           unmerged_for_key = svn_hash_gets(log_baton->unmerged_catalog,
10915                                            subtree_missing_this_rev);
10916 
10917           if (unmerged_for_key)
10918             {
10919               SVN_ERR(svn_mergeinfo_merge2(unmerged_for_key,
10920                                            log_entry_as_mergeinfo,
10921                                            log_baton->result_pool,
10922                                            pool));
10923             }
10924           else
10925             {
10926               svn_hash_sets(log_baton->unmerged_catalog,
10927                             apr_pstrdup(log_baton->result_pool,
10928                                         subtree_missing_this_rev),
10929                             log_entry_as_mergeinfo);
10930             }
10931 
10932         }
10933     }
10934   return SVN_NO_ERROR;
10935 }
10936 
10937 /* Determine if the mergeinfo on a reintegrate source SOURCE_LOC,
10938    reflects that the source is fully synced with the reintegrate target
10939    TARGET_LOC, even if a naive interpretation of the source's
10940    mergeinfo says otherwise -- See issue #3577.
10941 
10942    UNMERGED_CATALOG represents the history (as mergeinfo) from
10943    TARGET_LOC that is not represented in SOURCE_LOC's
10944    explicit/inherited mergeinfo as represented by MERGED_CATALOG.
10945    MERGED_CATALOG may be empty if the source has no explicit or inherited
10946    mergeinfo.
10947 
10948    Check that all of the unmerged revisions in UNMERGED_CATALOG's
10949    mergeinfos are "phantoms", that is, one of the following conditions holds:
10950 
10951      1) The revision affects no corresponding paths in SOURCE_LOC.
10952 
10953      2) The revision affects corresponding paths in SOURCE_LOC,
10954         but based on the mergeinfo in MERGED_CATALOG, the change was
10955         previously merged.
10956 
10957    Make a deep copy, allocated in RESULT_POOL, of any portions of
10958    UNMERGED_CATALOG that are not phantoms, to TRUE_UNMERGED_CATALOG.
10959 
10960    Note: The keys in all mergeinfo catalogs used here are relative to the
10961    root of the repository.
10962 
10963    RA_SESSION is an RA session open to the repository of TARGET_LOC; it may
10964    be temporarily reparented within this function.
10965 
10966    Use SCRATCH_POOL for all temporary allocations. */
10967 static svn_error_t *
find_unsynced_ranges(const svn_client__pathrev_t * source_loc,const svn_client__pathrev_t * target_loc,svn_mergeinfo_catalog_t unmerged_catalog,svn_mergeinfo_catalog_t merged_catalog,svn_mergeinfo_catalog_t true_unmerged_catalog,svn_ra_session_t * ra_session,apr_pool_t * result_pool,apr_pool_t * scratch_pool)10968 find_unsynced_ranges(const svn_client__pathrev_t *source_loc,
10969                      const svn_client__pathrev_t *target_loc,
10970                      svn_mergeinfo_catalog_t unmerged_catalog,
10971                      svn_mergeinfo_catalog_t merged_catalog,
10972                      svn_mergeinfo_catalog_t true_unmerged_catalog,
10973                      svn_ra_session_t *ra_session,
10974                      apr_pool_t *result_pool,
10975                      apr_pool_t *scratch_pool)
10976 {
10977   svn_rangelist_t *potentially_unmerged_ranges = NULL;
10978 
10979   /* Convert all the unmerged history to a rangelist. */
10980   if (apr_hash_count(unmerged_catalog))
10981     {
10982       apr_hash_index_t *hi_catalog;
10983 
10984       potentially_unmerged_ranges =
10985         apr_array_make(scratch_pool, 1, sizeof(svn_merge_range_t *));
10986 
10987       for (hi_catalog = apr_hash_first(scratch_pool, unmerged_catalog);
10988            hi_catalog;
10989            hi_catalog = apr_hash_next(hi_catalog))
10990         {
10991           svn_mergeinfo_t mergeinfo = apr_hash_this_val(hi_catalog);
10992 
10993           SVN_ERR(svn_rangelist__merge_many(potentially_unmerged_ranges,
10994                                             mergeinfo,
10995                                             scratch_pool, scratch_pool));
10996         }
10997     }
10998 
10999   /* Find any unmerged revisions which both affect the source and
11000      are not yet merged to it. */
11001   if (potentially_unmerged_ranges)
11002     {
11003       svn_revnum_t oldest_rev =
11004         (APR_ARRAY_IDX(potentially_unmerged_ranges,
11005                        0,
11006                        svn_merge_range_t *))->start + 1;
11007       svn_revnum_t youngest_rev =
11008         (APR_ARRAY_IDX(potentially_unmerged_ranges,
11009                        potentially_unmerged_ranges->nelts - 1,
11010                        svn_merge_range_t *))->end;
11011       log_find_operative_baton_t log_baton;
11012       const char *old_session_url = NULL;
11013       svn_error_t *err;
11014 
11015       log_baton.merged_catalog = merged_catalog;
11016       log_baton.unmerged_catalog = true_unmerged_catalog;
11017       log_baton.source_repos_rel_path
11018         = svn_client__pathrev_relpath(source_loc, scratch_pool);
11019       log_baton.target_fspath
11020         = svn_client__pathrev_fspath(target_loc, scratch_pool);
11021       log_baton.result_pool = result_pool;
11022 
11023       /* Reparent the session to TARGET_LOC if this target location
11024        * exists within the unmerged revision range. */
11025       if (target_loc->rev <= youngest_rev && target_loc->rev >= oldest_rev)
11026         SVN_ERR(svn_client__ensure_ra_session_url(
11027                   &old_session_url, ra_session, target_loc->url, scratch_pool));
11028 
11029       err = get_log(ra_session, "", youngest_rev, oldest_rev,
11030                     TRUE, /* discover_changed_paths */
11031                     log_find_operative_revs, &log_baton,
11032                     scratch_pool);
11033       if (old_session_url)
11034         err = svn_error_compose_create(err,
11035                                        svn_ra_reparent(ra_session,
11036                                                        old_session_url,
11037                                                        scratch_pool));
11038       SVN_ERR(err);
11039     }
11040 
11041   return SVN_NO_ERROR;
11042 }
11043 
11044 
11045 /* Find the youngest revision that has been merged from target to source.
11046  *
11047  * If any location in TARGET_HISTORY_AS_MERGEINFO is mentioned in
11048  * SOURCE_MERGEINFO, then we know that at least one merge was done from the
11049  * target to the source.  In that case, set *YOUNGEST_MERGED_REV to the
11050  * youngest revision of that intersection (unless *YOUNGEST_MERGED_REV is
11051  * already younger than that).  Otherwise, leave *YOUNGEST_MERGED_REV alone.
11052  */
11053 static svn_error_t *
find_youngest_merged_rev(svn_revnum_t * youngest_merged_rev,svn_mergeinfo_t target_history_as_mergeinfo,svn_mergeinfo_t source_mergeinfo,apr_pool_t * scratch_pool)11054 find_youngest_merged_rev(svn_revnum_t *youngest_merged_rev,
11055                          svn_mergeinfo_t target_history_as_mergeinfo,
11056                          svn_mergeinfo_t source_mergeinfo,
11057                          apr_pool_t *scratch_pool)
11058 {
11059   svn_mergeinfo_t explicit_source_target_history_intersection;
11060 
11061   SVN_ERR(svn_mergeinfo_intersect2(
11062             &explicit_source_target_history_intersection,
11063             source_mergeinfo, target_history_as_mergeinfo, TRUE,
11064             scratch_pool, scratch_pool));
11065   if (apr_hash_count(explicit_source_target_history_intersection))
11066     {
11067       svn_revnum_t old_rev, young_rev;
11068 
11069       /* Keep track of the youngest revision merged from target to source. */
11070       SVN_ERR(svn_mergeinfo__get_range_endpoints(
11071                 &young_rev, &old_rev,
11072                 explicit_source_target_history_intersection, scratch_pool));
11073       if (!SVN_IS_VALID_REVNUM(*youngest_merged_rev)
11074           || (young_rev > *youngest_merged_rev))
11075         *youngest_merged_rev = young_rev;
11076     }
11077 
11078   return SVN_NO_ERROR;
11079 }
11080 
11081 /* Set *FILTERED_MERGEINFO_P to the parts of TARGET_HISTORY_AS_MERGEINFO
11082  * that are not present in the source branch.
11083  *
11084  * SOURCE_MERGEINFO is the explicit or inherited mergeinfo of the source
11085  * branch SOURCE_PATHREV.  Extend SOURCE_MERGEINFO, modifying it in
11086  * place, to include the natural history (implicit mergeinfo) of
11087  * SOURCE_PATHREV.  ### But make these additions in SCRATCH_POOL.
11088  *
11089  * SOURCE_RA_SESSION is an RA session open to the repository containing
11090  * SOURCE_PATHREV; it may be temporarily reparented within this function.
11091  *
11092  * ### [JAF] This function is named '..._subroutine' simply because I
11093  *     factored it out based on code similarity, without knowing what it's
11094  *     purpose is.  We should clarify its purpose and choose a better name.
11095  */
11096 static svn_error_t *
find_unmerged_mergeinfo_subroutine(svn_mergeinfo_t * filtered_mergeinfo_p,svn_mergeinfo_t target_history_as_mergeinfo,svn_mergeinfo_t source_mergeinfo,const svn_client__pathrev_t * source_pathrev,svn_ra_session_t * source_ra_session,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)11097 find_unmerged_mergeinfo_subroutine(svn_mergeinfo_t *filtered_mergeinfo_p,
11098                                    svn_mergeinfo_t target_history_as_mergeinfo,
11099                                    svn_mergeinfo_t source_mergeinfo,
11100                                    const svn_client__pathrev_t *source_pathrev,
11101                                    svn_ra_session_t *source_ra_session,
11102                                    svn_client_ctx_t *ctx,
11103                                    apr_pool_t *result_pool,
11104                                    apr_pool_t *scratch_pool)
11105 {
11106   svn_mergeinfo_t source_history_as_mergeinfo;
11107 
11108   /* Get the source path's natural history and merge it into source
11109      path's explicit or inherited mergeinfo. */
11110   SVN_ERR(svn_client__get_history_as_mergeinfo(
11111             &source_history_as_mergeinfo, NULL /* has_rev_zero_history */,
11112             source_pathrev, source_pathrev->rev, SVN_INVALID_REVNUM,
11113             source_ra_session, ctx, scratch_pool));
11114   SVN_ERR(svn_mergeinfo_merge2(source_mergeinfo,
11115                                source_history_as_mergeinfo,
11116                                scratch_pool, scratch_pool));
11117 
11118   /* Now source_mergeinfo represents everything we know about
11119      source_path's history.  Now we need to know what part, if any, of the
11120      corresponding target's history is *not* part of source_path's total
11121      history; because it is neither shared history nor was it ever merged
11122      from the target to the source. */
11123   SVN_ERR(svn_mergeinfo_remove2(filtered_mergeinfo_p,
11124                                 source_mergeinfo,
11125                                 target_history_as_mergeinfo, TRUE,
11126                                 result_pool, scratch_pool));
11127   return SVN_NO_ERROR;
11128 }
11129 
11130 /* Helper for calculate_left_hand_side() which produces a mergeinfo catalog
11131    describing what parts of of the reintegrate target have not previously been
11132    merged to the reintegrate source.
11133 
11134    SOURCE_CATALOG is the collection of explicit mergeinfo on SOURCE_LOC and
11135    all its children, i.e. the mergeinfo catalog for the reintegrate source.
11136 
11137    TARGET_HISTORY_HASH is a hash of (const char *) paths mapped to
11138    svn_mergeinfo_t representing the location history.  Each of these
11139    path keys represent a path in the reintegrate target, relative to the
11140    repository root, which has explicit mergeinfo and/or is the reintegrate
11141    target itself.  The svn_mergeinfo_t's contain the natural history of each
11142    path@TARGET_REV.  Effectively this is the mergeinfo catalog on the
11143    reintegrate target.
11144 
11145    YC_ANCESTOR_REV is the revision of the youngest common ancestor of the
11146    reintegrate source and the reintegrate target.
11147 
11148    SOURCE_LOC is the reintegrate source.
11149 
11150    SOURCE_RA_SESSION is a session opened to the URL of SOURCE_LOC
11151    and TARGET_RA_SESSION is open to TARGET->loc.url.
11152 
11153    For each entry in TARGET_HISTORY_HASH check that the history it
11154    represents is contained in either the explicit mergeinfo for the
11155    corresponding path in SOURCE_CATALOG, the corresponding path's inherited
11156    mergeinfo (if no explicit mergeinfo for the path is found in
11157    SOURCE_CATALOG), or the corresponding path's natural history.  Populate
11158    *UNMERGED_TO_SOURCE_CATALOG with the corresponding source paths mapped to
11159    the mergeinfo from the target's natural history which is *not* found.  Also
11160    include any mergeinfo from SOURCE_CATALOG which explicitly describes the
11161    target's history but for which *no* entry was found in
11162    TARGET_HISTORY_HASH.
11163 
11164    If no part of TARGET_HISTORY_HASH is found in SOURCE_CATALOG set
11165    *YOUNGEST_MERGED_REV to SVN_INVALID_REVNUM; otherwise set it to the youngest
11166    revision previously merged from the target to the source, and filter
11167    *UNMERGED_TO_SOURCE_CATALOG so that it contains no ranges greater than
11168    *YOUNGEST_MERGED_REV.
11169 
11170    *UNMERGED_TO_SOURCE_CATALOG is (deeply) allocated in RESULT_POOL.
11171    SCRATCH_POOL is used for all temporary allocations.  */
11172 static svn_error_t *
find_unmerged_mergeinfo(svn_mergeinfo_catalog_t * unmerged_to_source_catalog,svn_revnum_t * youngest_merged_rev,svn_revnum_t yc_ancestor_rev,svn_mergeinfo_catalog_t source_catalog,apr_hash_t * target_history_hash,const svn_client__pathrev_t * source_loc,const merge_target_t * target,svn_ra_session_t * source_ra_session,svn_ra_session_t * target_ra_session,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)11173 find_unmerged_mergeinfo(svn_mergeinfo_catalog_t *unmerged_to_source_catalog,
11174                         svn_revnum_t *youngest_merged_rev,
11175                         svn_revnum_t yc_ancestor_rev,
11176                         svn_mergeinfo_catalog_t source_catalog,
11177                         apr_hash_t *target_history_hash,
11178                         const svn_client__pathrev_t *source_loc,
11179                         const merge_target_t *target,
11180                         svn_ra_session_t *source_ra_session,
11181                         svn_ra_session_t *target_ra_session,
11182                         svn_client_ctx_t *ctx,
11183                         apr_pool_t *result_pool,
11184                         apr_pool_t *scratch_pool)
11185 {
11186   const char *source_repos_rel_path
11187     = svn_client__pathrev_relpath(source_loc, scratch_pool);
11188   const char *target_repos_rel_path
11189     = svn_client__pathrev_relpath(&target->loc, scratch_pool);
11190   apr_hash_index_t *hi;
11191   svn_mergeinfo_catalog_t new_catalog = apr_hash_make(result_pool);
11192   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
11193 
11194   assert(session_url_is(source_ra_session, source_loc->url, scratch_pool));
11195   assert(session_url_is(target_ra_session, target->loc.url, scratch_pool));
11196 
11197   *youngest_merged_rev = SVN_INVALID_REVNUM;
11198 
11199   /* Examine the natural history of each path in the reintegrate target
11200      with explicit mergeinfo. */
11201   for (hi = apr_hash_first(scratch_pool, target_history_hash);
11202        hi;
11203        hi = apr_hash_next(hi))
11204     {
11205       const char *target_path = apr_hash_this_key(hi);
11206       svn_mergeinfo_t target_history_as_mergeinfo = apr_hash_this_val(hi);
11207       const char *path_rel_to_session
11208         = svn_relpath_skip_ancestor(target_repos_rel_path, target_path);
11209       const char *source_path;
11210       svn_client__pathrev_t *source_pathrev;
11211       svn_mergeinfo_t source_mergeinfo, filtered_mergeinfo;
11212 
11213       svn_pool_clear(iterpool);
11214 
11215       source_path = svn_relpath_join(source_repos_rel_path,
11216                                      path_rel_to_session, iterpool);
11217       source_pathrev = svn_client__pathrev_join_relpath(
11218                          source_loc, path_rel_to_session, iterpool);
11219 
11220       /* Remove any target history that is also part of the source's history,
11221          i.e. their common ancestry.  By definition this has already been
11222          "merged" from the target to the source.  If the source has explicit
11223          self referential mergeinfo it would intersect with the target's
11224          history below, making it appear that some merges had been done from
11225          the target to the source, when this might not actually be the case. */
11226       SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(
11227         &target_history_as_mergeinfo, target_history_as_mergeinfo,
11228         source_loc->rev, yc_ancestor_rev, TRUE, iterpool, iterpool));
11229 
11230       /* Look for any explicit mergeinfo on the source path corresponding to
11231          the target path.  If we find any remove that from SOURCE_CATALOG.
11232          When this iteration over TARGET_HISTORY_HASH is complete all that
11233          should be left in SOURCE_CATALOG are subtrees that have explicit
11234          mergeinfo on the reintegrate source where there is no corresponding
11235          explicit mergeinfo on the reintegrate target. */
11236       source_mergeinfo = svn_hash_gets(source_catalog, source_path);
11237       if (source_mergeinfo)
11238         {
11239           svn_hash_sets(source_catalog, source_path, NULL);
11240 
11241           SVN_ERR(find_youngest_merged_rev(youngest_merged_rev,
11242                                            target_history_as_mergeinfo,
11243                                            source_mergeinfo,
11244                                            iterpool));
11245         }
11246       else
11247         {
11248           /* There is no mergeinfo on source_path *or* source_path doesn't
11249              exist at all.  If simply doesn't exist we can ignore it
11250              altogether. */
11251           svn_node_kind_t kind;
11252 
11253           SVN_ERR(svn_ra_check_path(source_ra_session,
11254                                     path_rel_to_session,
11255                                     source_loc->rev, &kind, iterpool));
11256           if (kind == svn_node_none)
11257               continue;
11258           /* Else source_path does exist though it has no explicit mergeinfo.
11259              Find its inherited mergeinfo.  If it doesn't have any then simply
11260              set source_mergeinfo to an empty hash. */
11261           SVN_ERR(svn_client__get_repos_mergeinfo(
11262                     &source_mergeinfo, source_ra_session,
11263                     source_pathrev->url, source_pathrev->rev,
11264                     svn_mergeinfo_inherited, FALSE /*squelch_incapable*/,
11265                     iterpool));
11266           if (!source_mergeinfo)
11267             source_mergeinfo = apr_hash_make(iterpool);
11268         }
11269 
11270       /* Use scratch_pool rather than iterpool because filtered_mergeinfo
11271          is going into new_catalog below and needs to last to the end of
11272          this function. */
11273       SVN_ERR(find_unmerged_mergeinfo_subroutine(
11274                 &filtered_mergeinfo, target_history_as_mergeinfo,
11275                 source_mergeinfo, source_pathrev,
11276                 source_ra_session, ctx, scratch_pool, iterpool));
11277       svn_hash_sets(new_catalog, apr_pstrdup(scratch_pool, source_path),
11278                     filtered_mergeinfo);
11279     }
11280 
11281   /* Are there any subtrees with explicit mergeinfo still left in the merge
11282      source where there was no explicit mergeinfo for the corresponding path
11283      in the merge target?  If so, add the intersection of those path's
11284      mergeinfo and the corresponding target path's mergeinfo to
11285      new_catalog. */
11286   for (hi = apr_hash_first(scratch_pool, source_catalog);
11287        hi;
11288        hi = apr_hash_next(hi))
11289     {
11290       const char *source_path = apr_hash_this_key(hi);
11291       const char *path_rel_to_session =
11292         svn_relpath_skip_ancestor(source_repos_rel_path, source_path);
11293       const char *source_url;
11294       svn_mergeinfo_t source_mergeinfo = apr_hash_this_val(hi);
11295       svn_mergeinfo_t filtered_mergeinfo;
11296       svn_client__pathrev_t *target_pathrev;
11297       svn_mergeinfo_t target_history_as_mergeinfo;
11298       svn_error_t *err;
11299 
11300       svn_pool_clear(iterpool);
11301 
11302       source_url = svn_path_url_add_component2(source_loc->url,
11303                                                path_rel_to_session, iterpool);
11304       target_pathrev = svn_client__pathrev_join_relpath(
11305                          &target->loc, path_rel_to_session, iterpool);
11306       err = svn_client__get_history_as_mergeinfo(&target_history_as_mergeinfo,
11307                                                  NULL /* has_rev_zero_history */,
11308                                                  target_pathrev,
11309                                                  target->loc.rev,
11310                                                  SVN_INVALID_REVNUM,
11311                                                  target_ra_session,
11312                                                  ctx, iterpool);
11313       if (err)
11314         {
11315           if (err->apr_err == SVN_ERR_FS_NOT_FOUND
11316               || err->apr_err == SVN_ERR_RA_DAV_REQUEST_FAILED)
11317             {
11318               /* This path with explicit mergeinfo in the source doesn't
11319                  exist on the target. */
11320               svn_error_clear(err);
11321               err = NULL;
11322             }
11323           else
11324             {
11325               return svn_error_trace(err);
11326             }
11327         }
11328       else
11329         {
11330           svn_client__pathrev_t *pathrev;
11331 
11332           SVN_ERR(find_youngest_merged_rev(youngest_merged_rev,
11333                                            target_history_as_mergeinfo,
11334                                            source_mergeinfo,
11335                                            iterpool));
11336 
11337           /* Use scratch_pool rather than iterpool because filtered_mergeinfo
11338              is going into new_catalog below and needs to last to the end of
11339              this function. */
11340           /* ### Why looking at SOURCE_url at TARGET_rev? */
11341           SVN_ERR(svn_client__pathrev_create_with_session(
11342                     &pathrev, source_ra_session, target->loc.rev, source_url,
11343                     iterpool));
11344           SVN_ERR(find_unmerged_mergeinfo_subroutine(
11345                     &filtered_mergeinfo, target_history_as_mergeinfo,
11346                     source_mergeinfo, pathrev,
11347                     source_ra_session, ctx, scratch_pool, iterpool));
11348           if (apr_hash_count(filtered_mergeinfo))
11349             svn_hash_sets(new_catalog,
11350                           apr_pstrdup(scratch_pool, source_path),
11351                           filtered_mergeinfo);
11352         }
11353     }
11354 
11355   /* Limit new_catalog to the youngest revisions previously merged from
11356      the target to the source. */
11357   if (SVN_IS_VALID_REVNUM(*youngest_merged_rev))
11358     SVN_ERR(svn_mergeinfo__filter_catalog_by_ranges(&new_catalog,
11359                                                     new_catalog,
11360                                                     *youngest_merged_rev,
11361                                                     0, /* No oldest bound. */
11362                                                     TRUE,
11363                                                     scratch_pool,
11364                                                     scratch_pool));
11365 
11366   /* Make a shiny new copy before blowing away all the temporary pools. */
11367   *unmerged_to_source_catalog = svn_mergeinfo_catalog_dup(new_catalog,
11368                                                           result_pool);
11369   svn_pool_destroy(iterpool);
11370   return SVN_NO_ERROR;
11371 }
11372 
11373 /* Helper for svn_client_merge_reintegrate() which calculates the
11374    'left hand side' of the underlying two-URL merge that a --reintegrate
11375    merge actually performs.  If no merge should be performed, set
11376    *LEFT_P to NULL.
11377 
11378    TARGET->abspath is the absolute working copy path of the reintegrate
11379    merge.
11380 
11381    SOURCE_LOC is the reintegrate source.
11382 
11383    SUBTREES_WITH_MERGEINFO is a hash of (const char *) absolute paths mapped
11384    to (svn_mergeinfo_t *) mergeinfo values for each working copy path with
11385    explicit mergeinfo in TARGET->abspath.  Actually we only need to know the
11386    paths, not the mergeinfo.
11387 
11388    TARGET->loc.rev is the working revision the entire WC tree rooted at
11389    TARGET is at.
11390 
11391    Populate *UNMERGED_TO_SOURCE_CATALOG with the mergeinfo describing what
11392    parts of TARGET->loc have not been merged to SOURCE_LOC, up to the
11393    youngest revision ever merged from the TARGET->abspath to the source if
11394    such exists, see doc string for find_unmerged_mergeinfo().
11395 
11396    SOURCE_RA_SESSION is a session opened to the SOURCE_LOC
11397    and TARGET_RA_SESSION is open to TARGET->loc.url.
11398 
11399    *LEFT_P, *MERGED_TO_SOURCE_CATALOG , and *UNMERGED_TO_SOURCE_CATALOG are
11400    allocated in RESULT_POOL.  SCRATCH_POOL is used for all temporary
11401    allocations. */
11402 static svn_error_t *
calculate_left_hand_side(svn_client__pathrev_t ** left_p,svn_mergeinfo_catalog_t * merged_to_source_catalog,svn_mergeinfo_catalog_t * unmerged_to_source_catalog,const merge_target_t * target,apr_hash_t * subtrees_with_mergeinfo,const svn_client__pathrev_t * source_loc,svn_ra_session_t * source_ra_session,svn_ra_session_t * target_ra_session,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)11403 calculate_left_hand_side(svn_client__pathrev_t **left_p,
11404                          svn_mergeinfo_catalog_t *merged_to_source_catalog,
11405                          svn_mergeinfo_catalog_t *unmerged_to_source_catalog,
11406                          const merge_target_t *target,
11407                          apr_hash_t *subtrees_with_mergeinfo,
11408                          const svn_client__pathrev_t *source_loc,
11409                          svn_ra_session_t *source_ra_session,
11410                          svn_ra_session_t *target_ra_session,
11411                          svn_client_ctx_t *ctx,
11412                          apr_pool_t *result_pool,
11413                          apr_pool_t *scratch_pool)
11414 {
11415   svn_mergeinfo_catalog_t mergeinfo_catalog, unmerged_catalog;
11416   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
11417   apr_hash_index_t *hi;
11418   /* hash of paths mapped to arrays of svn_mergeinfo_t. */
11419   apr_hash_t *target_history_hash = apr_hash_make(scratch_pool);
11420   svn_revnum_t youngest_merged_rev;
11421   svn_client__pathrev_t *yc_ancestor;
11422 
11423   assert(session_url_is(source_ra_session, source_loc->url, scratch_pool));
11424   assert(session_url_is(target_ra_session, target->loc.url, scratch_pool));
11425 
11426   /* Initialize our return variables. */
11427   *left_p = NULL;
11428 
11429   /* TARGET->abspath may not have explicit mergeinfo and thus may not be
11430      contained within SUBTREES_WITH_MERGEINFO.  If this is the case then
11431      add a dummy item for TARGET->abspath so we get its history (i.e. implicit
11432      mergeinfo) below.  */
11433   if (!svn_hash_gets(subtrees_with_mergeinfo, target->abspath))
11434     svn_hash_sets(subtrees_with_mergeinfo, target->abspath,
11435                   apr_hash_make(result_pool));
11436 
11437   /* Get the history segments (as mergeinfo) for TARGET->abspath and any of
11438      its subtrees with explicit mergeinfo. */
11439   for (hi = apr_hash_first(scratch_pool, subtrees_with_mergeinfo);
11440        hi;
11441        hi = apr_hash_next(hi))
11442     {
11443       const char *local_abspath = apr_hash_this_key(hi);
11444       svn_client__pathrev_t *target_child;
11445       const char *repos_relpath;
11446       svn_mergeinfo_t target_history_as_mergeinfo;
11447 
11448       svn_pool_clear(iterpool);
11449 
11450       /* Convert the absolute path with mergeinfo on it to a path relative
11451          to the session root. */
11452       SVN_ERR(svn_wc__node_get_repos_info(NULL, &repos_relpath, NULL, NULL,
11453                                           ctx->wc_ctx, local_abspath,
11454                                           scratch_pool, iterpool));
11455       target_child = svn_client__pathrev_create_with_relpath(
11456                        target->loc.repos_root_url, target->loc.repos_uuid,
11457                        target->loc.rev, repos_relpath, iterpool);
11458       SVN_ERR(svn_client__get_history_as_mergeinfo(&target_history_as_mergeinfo,
11459                                                    NULL /* has_rev_zero_hist */,
11460                                                    target_child,
11461                                                    target->loc.rev,
11462                                                    SVN_INVALID_REVNUM,
11463                                                    target_ra_session,
11464                                                    ctx, scratch_pool));
11465 
11466       svn_hash_sets(target_history_hash, repos_relpath,
11467                     target_history_as_mergeinfo);
11468     }
11469 
11470   /* Check that SOURCE_LOC and TARGET->loc are
11471      actually related, we can't reintegrate if they are not.  Also
11472      get an initial value for the YCA revision number. */
11473   SVN_ERR(svn_client__get_youngest_common_ancestor(
11474               &yc_ancestor, source_loc, &target->loc, target_ra_session, ctx,
11475               iterpool, iterpool));
11476   if (! yc_ancestor)
11477     return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
11478                              _("'%s@%ld' must be ancestrally related to "
11479                                "'%s@%ld'"), source_loc->url, source_loc->rev,
11480                              target->loc.url, target->loc.rev);
11481 
11482   /* If the source revision is the same as the youngest common
11483      revision, then there can't possibly be any unmerged revisions
11484      that we need to apply to target. */
11485   if (source_loc->rev == yc_ancestor->rev)
11486     {
11487       svn_pool_destroy(iterpool);
11488       return SVN_NO_ERROR;
11489     }
11490 
11491   /* Get the mergeinfo from the source, including its descendants
11492      with differing explicit mergeinfo. */
11493   SVN_ERR(svn_client__get_repos_mergeinfo_catalog(
11494             &mergeinfo_catalog, source_ra_session,
11495             source_loc->url, source_loc->rev,
11496             svn_mergeinfo_inherited, FALSE /* squelch_incapable */,
11497             TRUE /* include_descendants */, iterpool, iterpool));
11498 
11499   if (!mergeinfo_catalog)
11500     mergeinfo_catalog = apr_hash_make(iterpool);
11501 
11502   *merged_to_source_catalog = svn_mergeinfo_catalog_dup(mergeinfo_catalog,
11503                                                         result_pool);
11504 
11505   /* Filter the source's mergeinfo catalog so that we are left with
11506      mergeinfo that describes what has *not* previously been merged from
11507      TARGET->loc to SOURCE_LOC. */
11508   SVN_ERR(find_unmerged_mergeinfo(&unmerged_catalog,
11509                                   &youngest_merged_rev,
11510                                   yc_ancestor->rev,
11511                                   mergeinfo_catalog,
11512                                   target_history_hash,
11513                                   source_loc,
11514                                   target,
11515                                   source_ra_session,
11516                                   target_ra_session,
11517                                   ctx,
11518                                   iterpool, iterpool));
11519 
11520   /* Simplify unmerged_catalog through elision then make a copy in POOL. */
11521   SVN_ERR(svn_client__elide_mergeinfo_catalog(unmerged_catalog,
11522                                               iterpool));
11523   *unmerged_to_source_catalog = svn_mergeinfo_catalog_dup(unmerged_catalog,
11524                                                           result_pool);
11525 
11526   if (youngest_merged_rev == SVN_INVALID_REVNUM)
11527     {
11528       /* We never merged to the source.  Just return the branch point. */
11529       *left_p = svn_client__pathrev_dup(yc_ancestor, result_pool);
11530     }
11531   else
11532     {
11533       /* We've previously merged some or all of the target, up to
11534          youngest_merged_rev, to the source.  Set
11535          *LEFT_P to cover the youngest part of this range. */
11536       SVN_ERR(svn_client__repos_location(left_p, target_ra_session,
11537                                          &target->loc, youngest_merged_rev,
11538                                          ctx, result_pool, iterpool));
11539     }
11540 
11541   svn_pool_destroy(iterpool);
11542   return SVN_NO_ERROR;
11543 }
11544 
11545 /* Determine the URLs and revisions needed to perform a reintegrate merge
11546  * from SOURCE_LOC into the working copy at TARGET.
11547  *
11548  * SOURCE_RA_SESSION and TARGET_RA_SESSION are RA sessions opened to the
11549  * URLs of SOURCE_LOC and TARGET->loc respectively.
11550  *
11551  * Set *SOURCE_P to
11552  * the source-left and source-right locations of the required merge.  Set
11553  * *YC_ANCESTOR_P to the location of the youngest ancestor.
11554  * Any of these output pointers may be NULL if not wanted.
11555  *
11556  * See svn_client_find_reintegrate_merge() for other details.
11557  */
11558 static svn_error_t *
find_reintegrate_merge(merge_source_t ** source_p,svn_client__pathrev_t ** yc_ancestor_p,svn_ra_session_t * source_ra_session,const svn_client__pathrev_t * source_loc,svn_ra_session_t * target_ra_session,const merge_target_t * target,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)11559 find_reintegrate_merge(merge_source_t **source_p,
11560                        svn_client__pathrev_t **yc_ancestor_p,
11561                        svn_ra_session_t *source_ra_session,
11562                        const svn_client__pathrev_t *source_loc,
11563                        svn_ra_session_t *target_ra_session,
11564                        const merge_target_t *target,
11565                        svn_client_ctx_t *ctx,
11566                        apr_pool_t *result_pool,
11567                        apr_pool_t *scratch_pool)
11568 {
11569   svn_client__pathrev_t *yc_ancestor;
11570   svn_client__pathrev_t *loc1;
11571   merge_source_t source;
11572   svn_mergeinfo_catalog_t unmerged_to_source_mergeinfo_catalog;
11573   svn_mergeinfo_catalog_t merged_to_source_mergeinfo_catalog;
11574   svn_error_t *err;
11575   apr_hash_t *subtrees_with_mergeinfo;
11576 
11577   assert(session_url_is(source_ra_session, source_loc->url, scratch_pool));
11578   assert(session_url_is(target_ra_session, target->loc.url, scratch_pool));
11579 
11580   /* As the WC tree is "pure", use its last-updated-to revision as
11581      the default revision for the left side of our merge, since that's
11582      what the repository sub-tree is required to be up to date with
11583      (with regard to the WC). */
11584   /* ### Bogus/obsolete comment? */
11585 
11586   /* Can't reintegrate to or from the root of the repository. */
11587   if (strcmp(source_loc->url, source_loc->repos_root_url) == 0
11588       || strcmp(target->loc.url, target->loc.repos_root_url) == 0)
11589     return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
11590                              _("Neither the reintegrate source nor target "
11591                                "can be the root of the repository"));
11592 
11593   /* Find all the subtrees in TARGET_WCPATH that have explicit mergeinfo. */
11594   err = get_wc_explicit_mergeinfo_catalog(&subtrees_with_mergeinfo,
11595                                           target->abspath, svn_depth_infinity,
11596                                           ctx, scratch_pool, scratch_pool);
11597   /* Issue #3896: If invalid mergeinfo in the reintegrate target
11598      prevents us from proceeding, then raise the best error possible. */
11599   if (err && err->apr_err == SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING)
11600     err = svn_error_quick_wrap(err, _("Reintegrate merge not possible"));
11601   SVN_ERR(err);
11602 
11603   SVN_ERR(calculate_left_hand_side(&loc1,
11604                                    &merged_to_source_mergeinfo_catalog,
11605                                    &unmerged_to_source_mergeinfo_catalog,
11606                                    target,
11607                                    subtrees_with_mergeinfo,
11608                                    source_loc,
11609                                    source_ra_session,
11610                                    target_ra_session,
11611                                    ctx,
11612                                    scratch_pool, scratch_pool));
11613 
11614   /* Did calculate_left_hand_side() decide that there was no merge to
11615      be performed here?  */
11616   if (! loc1)
11617     {
11618       if (source_p)
11619         *source_p = NULL;
11620       if (yc_ancestor_p)
11621         *yc_ancestor_p = NULL;
11622       return SVN_NO_ERROR;
11623     }
11624 
11625   source.loc1 = loc1;
11626   source.loc2 = source_loc;
11627 
11628   /* If the target was moved after the source was branched from it,
11629      it is possible that the left URL differs from the target's current
11630      URL.  If so, then adjust TARGET_RA_SESSION to point to the old URL. */
11631   if (strcmp(source.loc1->url, target->loc.url))
11632     SVN_ERR(svn_ra_reparent(target_ra_session, source.loc1->url, scratch_pool));
11633 
11634   SVN_ERR(svn_client__get_youngest_common_ancestor(
11635             &yc_ancestor, source.loc2, source.loc1, target_ra_session,
11636             ctx, scratch_pool, scratch_pool));
11637 
11638   if (! yc_ancestor)
11639     return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
11640                              _("'%s@%ld' must be ancestrally related to "
11641                                "'%s@%ld'"),
11642                              source.loc1->url, source.loc1->rev,
11643                              source.loc2->url, source.loc2->rev);
11644 
11645   /* The source side of a reintegrate merge is not 'ancestral', except in
11646    * the degenerate case where source == YCA. */
11647   source.ancestral = (loc1->rev == yc_ancestor->rev);
11648 
11649   if (source.loc1->rev > yc_ancestor->rev)
11650     {
11651       /* Have we actually merged anything to the source from the
11652          target?  If so, make sure we've merged a contiguous
11653          prefix. */
11654       svn_mergeinfo_catalog_t final_unmerged_catalog = apr_hash_make(scratch_pool);
11655 
11656       SVN_ERR(find_unsynced_ranges(source_loc, &target->loc,
11657                                    unmerged_to_source_mergeinfo_catalog,
11658                                    merged_to_source_mergeinfo_catalog,
11659                                    final_unmerged_catalog,
11660                                    target_ra_session, scratch_pool,
11661                                    scratch_pool));
11662 
11663       if (apr_hash_count(final_unmerged_catalog))
11664         {
11665           svn_string_t *source_mergeinfo_cat_string;
11666 
11667           SVN_ERR(svn_mergeinfo__catalog_to_formatted_string(
11668             &source_mergeinfo_cat_string,
11669             final_unmerged_catalog,
11670             "  ", _("    Missing ranges: "), scratch_pool));
11671           return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE,
11672                                    NULL,
11673                                    _("Reintegrate can only be used if "
11674                                      "revisions %ld through %ld were "
11675                                      "previously merged from %s to the "
11676                                      "reintegrate source, but this is "
11677                                      "not the case:\n%s"),
11678                                    yc_ancestor->rev + 1, source.loc2->rev,
11679                                    target->loc.url,
11680                                    source_mergeinfo_cat_string->data);
11681         }
11682     }
11683 
11684   /* Left side: trunk@youngest-trunk-rev-merged-to-branch-at-specified-peg-rev
11685    * Right side: branch@specified-peg-revision */
11686   if (source_p)
11687     *source_p = merge_source_dup(&source, result_pool);
11688 
11689   if (yc_ancestor_p)
11690     *yc_ancestor_p = svn_client__pathrev_dup(yc_ancestor, result_pool);
11691   return SVN_NO_ERROR;
11692 }
11693 
11694 /* Resolve the source and target locations and open RA sessions to them, and
11695  * perform some checks appropriate for a reintegrate merge.
11696  *
11697  * Set *SOURCE_RA_SESSION_P and *SOURCE_LOC_P to a new session and the
11698  * repository location of SOURCE_PATH_OR_URL at SOURCE_PEG_REVISION.  Set
11699  * *TARGET_RA_SESSION_P and *TARGET_P to a new session and the repository
11700  * location of the WC at TARGET_ABSPATH.
11701  *
11702  * Throw a SVN_ERR_CLIENT_UNRELATED_RESOURCES error if the target WC node is
11703  * a locally added node or if the source and target are not in the same
11704  * repository.  Throw a SVN_ERR_CLIENT_NOT_READY_TO_MERGE error if the
11705  * target WC is not at a single revision without switched subtrees and
11706  * without local mods.
11707  *
11708  * Allocate all the outputs in RESULT_POOL.
11709  */
11710 static svn_error_t *
open_reintegrate_source_and_target(svn_ra_session_t ** source_ra_session_p,svn_client__pathrev_t ** source_loc_p,svn_ra_session_t ** target_ra_session_p,merge_target_t ** target_p,const char * source_path_or_url,const svn_opt_revision_t * source_peg_revision,const char * target_abspath,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)11711 open_reintegrate_source_and_target(svn_ra_session_t **source_ra_session_p,
11712                                    svn_client__pathrev_t **source_loc_p,
11713                                    svn_ra_session_t **target_ra_session_p,
11714                                    merge_target_t **target_p,
11715                                    const char *source_path_or_url,
11716                                    const svn_opt_revision_t *source_peg_revision,
11717                                    const char *target_abspath,
11718                                    svn_client_ctx_t *ctx,
11719                                    apr_pool_t *result_pool,
11720                                    apr_pool_t *scratch_pool)
11721 {
11722   svn_client__pathrev_t *source_loc;
11723   merge_target_t *target;
11724 
11725   /* Open the target WC.  A reintegrate merge requires the merge target to
11726    * reflect a subtree of the repository as found at a single revision. */
11727   SVN_ERR(open_target_wc(&target, target_abspath,
11728                          FALSE, FALSE, FALSE,
11729                          ctx, scratch_pool, scratch_pool));
11730   if (! target->loc.url)
11731     return svn_error_createf(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
11732                              _("Can't reintegrate into '%s' because it is "
11733                                "locally added and therefore not related to "
11734                                "the merge source"),
11735                              svn_dirent_local_style(target->abspath,
11736                                                     scratch_pool));
11737 
11738   SVN_ERR(svn_client_open_ra_session2(target_ra_session_p,
11739                                       target->loc.url, target->abspath,
11740                                       ctx, result_pool, scratch_pool));
11741 
11742   SVN_ERR(svn_client__ra_session_from_path2(
11743             source_ra_session_p, &source_loc,
11744             source_path_or_url, NULL, source_peg_revision, source_peg_revision,
11745             ctx, result_pool));
11746 
11747   /* source_loc and target->loc are required to be in the same repository,
11748      as mergeinfo doesn't come into play for cross-repository merging. */
11749   SVN_ERR(check_same_repos(source_loc,
11750                            svn_dirent_local_style(source_path_or_url,
11751                                                   scratch_pool),
11752                            &target->loc,
11753                            svn_dirent_local_style(target->abspath,
11754                                                   scratch_pool),
11755                            TRUE /* strict_urls */, scratch_pool));
11756 
11757   *source_loc_p = source_loc;
11758   *target_p = target;
11759   return SVN_NO_ERROR;
11760 }
11761 
11762 /* The body of svn_client_merge_reintegrate(), which see for details. */
11763 static svn_error_t *
merge_reintegrate_locked(svn_client__conflict_report_t ** conflict_report,const char * source_path_or_url,const svn_opt_revision_t * source_peg_revision,const char * target_abspath,svn_boolean_t diff_ignore_ancestry,svn_boolean_t dry_run,const apr_array_header_t * merge_options,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)11764 merge_reintegrate_locked(svn_client__conflict_report_t **conflict_report,
11765                          const char *source_path_or_url,
11766                          const svn_opt_revision_t *source_peg_revision,
11767                          const char *target_abspath,
11768                          svn_boolean_t diff_ignore_ancestry,
11769                          svn_boolean_t dry_run,
11770                          const apr_array_header_t *merge_options,
11771                          svn_client_ctx_t *ctx,
11772                          apr_pool_t *result_pool,
11773                          apr_pool_t *scratch_pool)
11774 {
11775   svn_ra_session_t *target_ra_session, *source_ra_session;
11776   merge_target_t *target;
11777   svn_client__pathrev_t *source_loc;
11778   merge_source_t *source;
11779   svn_client__pathrev_t *yc_ancestor;
11780   svn_boolean_t use_sleep = FALSE;
11781   svn_error_t *err;
11782 
11783   SVN_ERR(open_reintegrate_source_and_target(
11784             &source_ra_session, &source_loc, &target_ra_session, &target,
11785             source_path_or_url, source_peg_revision, target_abspath,
11786             ctx, scratch_pool, scratch_pool));
11787 
11788   SVN_ERR(find_reintegrate_merge(&source, &yc_ancestor,
11789                                  source_ra_session, source_loc,
11790                                  target_ra_session, target,
11791                                  ctx, scratch_pool, scratch_pool));
11792 
11793   if (! source)
11794     {
11795       *conflict_report = NULL;
11796       return SVN_NO_ERROR;
11797     }
11798 
11799   /* Do the real merge! */
11800   /* ### TODO(reint): Make sure that one isn't the same line ancestor
11801      ### of the other (what's erroneously referred to as "ancestrally
11802      ### related" in this source file).  For now, we just say the source
11803      ### isn't "ancestral" even if it is (in the degenerate case where
11804      ### source-left equals YCA). */
11805   source->ancestral = FALSE;
11806   err = merge_cousins_and_supplement_mergeinfo(conflict_report,
11807                                                &use_sleep,
11808                                                target,
11809                                                target_ra_session,
11810                                                source_ra_session,
11811                                                source, yc_ancestor,
11812                                                TRUE /* same_repos */,
11813                                                svn_depth_infinity,
11814                                                diff_ignore_ancestry,
11815                                                FALSE /* force_delete */,
11816                                                FALSE /* record_only */,
11817                                                dry_run,
11818                                                merge_options,
11819                                                ctx,
11820                                                result_pool, scratch_pool);
11821 
11822   if (use_sleep)
11823     svn_io_sleep_for_timestamps(target_abspath, scratch_pool);
11824 
11825   SVN_ERR(err);
11826   return SVN_NO_ERROR;
11827 }
11828 
11829 svn_error_t *
svn_client_merge_reintegrate(const char * source_path_or_url,const svn_opt_revision_t * source_peg_revision,const char * target_wcpath,svn_boolean_t dry_run,const apr_array_header_t * merge_options,svn_client_ctx_t * ctx,apr_pool_t * pool)11830 svn_client_merge_reintegrate(const char *source_path_or_url,
11831                              const svn_opt_revision_t *source_peg_revision,
11832                              const char *target_wcpath,
11833                              svn_boolean_t dry_run,
11834                              const apr_array_header_t *merge_options,
11835                              svn_client_ctx_t *ctx,
11836                              apr_pool_t *pool)
11837 {
11838   const char *target_abspath, *lock_abspath;
11839   svn_client__conflict_report_t *conflict_report;
11840 
11841   SVN_ERR(get_target_and_lock_abspath(&target_abspath, &lock_abspath,
11842                                       target_wcpath, ctx, pool));
11843 
11844   if (!dry_run)
11845     SVN_WC__CALL_WITH_WRITE_LOCK(
11846       merge_reintegrate_locked(&conflict_report,
11847                                source_path_or_url, source_peg_revision,
11848                                target_abspath,
11849                                FALSE /*diff_ignore_ancestry*/,
11850                                dry_run, merge_options, ctx, pool, pool),
11851       ctx->wc_ctx, lock_abspath, FALSE /* lock_anchor */, pool);
11852   else
11853     SVN_ERR(merge_reintegrate_locked(&conflict_report,
11854                                      source_path_or_url, source_peg_revision,
11855                                      target_abspath,
11856                                      FALSE /*diff_ignore_ancestry*/,
11857                                      dry_run, merge_options, ctx, pool, pool));
11858 
11859   SVN_ERR(svn_client__make_merge_conflict_error(conflict_report, pool));
11860   return SVN_NO_ERROR;
11861 }
11862 
11863 
11864 /* The body of svn_client_merge_peg5(), which see for details.
11865  *
11866  * IGNORE_MERGEINFO and DIFF_IGNORE_ANCESTRY are as in do_merge().
11867  */
11868 static svn_error_t *
merge_peg_locked(svn_client__conflict_report_t ** conflict_report,const char * source_path_or_url,const svn_opt_revision_t * source_peg_revision,const svn_rangelist_t * ranges_to_merge,const char * target_abspath,svn_depth_t depth,svn_boolean_t ignore_mergeinfo,svn_boolean_t diff_ignore_ancestry,svn_boolean_t force_delete,svn_boolean_t record_only,svn_boolean_t dry_run,svn_boolean_t allow_mixed_rev,const apr_array_header_t * merge_options,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)11869 merge_peg_locked(svn_client__conflict_report_t **conflict_report,
11870                  const char *source_path_or_url,
11871                  const svn_opt_revision_t *source_peg_revision,
11872                  const svn_rangelist_t *ranges_to_merge,
11873                  const char *target_abspath,
11874                  svn_depth_t depth,
11875                  svn_boolean_t ignore_mergeinfo,
11876                  svn_boolean_t diff_ignore_ancestry,
11877                  svn_boolean_t force_delete,
11878                  svn_boolean_t record_only,
11879                  svn_boolean_t dry_run,
11880                  svn_boolean_t allow_mixed_rev,
11881                  const apr_array_header_t *merge_options,
11882                  svn_client_ctx_t *ctx,
11883                  apr_pool_t *result_pool,
11884                  apr_pool_t *scratch_pool)
11885 {
11886   merge_target_t *target;
11887   svn_client__pathrev_t *source_loc;
11888   apr_array_header_t *merge_sources;
11889   svn_ra_session_t *ra_session;
11890   apr_pool_t *sesspool;
11891   svn_boolean_t use_sleep = FALSE;
11892   svn_error_t *err;
11893   svn_boolean_t same_repos;
11894 
11895   SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath));
11896 
11897   SVN_ERR(open_target_wc(&target, target_abspath,
11898                          allow_mixed_rev, TRUE, TRUE,
11899                          ctx, scratch_pool, scratch_pool));
11900 
11901   /* Create a short lived session pool */
11902   sesspool = svn_pool_create(scratch_pool);
11903 
11904   /* Open an RA session to our source URL, and determine its root URL. */
11905   SVN_ERR(svn_client__ra_session_from_path2(
11906             &ra_session, &source_loc,
11907             source_path_or_url, NULL, source_peg_revision, source_peg_revision,
11908             ctx, sesspool));
11909 
11910   /* Normalize our merge sources. */
11911   SVN_ERR(normalize_merge_sources(&merge_sources, source_path_or_url,
11912                                   source_loc,
11913                                   ranges_to_merge, ra_session, ctx,
11914                                   scratch_pool, scratch_pool));
11915 
11916   /* Check for same_repos. */
11917   same_repos = is_same_repos(&target->loc, source_loc, TRUE /* strict_urls */);
11918 
11919   /* Do the real merge!  (We say with confidence that our merge
11920      sources are both ancestral and related.) */
11921   if (getenv("SVN_ELEMENT_MERGE")
11922       && same_repos
11923       && (depth == svn_depth_infinity || depth == svn_depth_unknown)
11924       && ignore_mergeinfo
11925       && !record_only)
11926     {
11927       err = svn_client__merge_elements(&use_sleep,
11928                                        merge_sources, target, ra_session,
11929                                        diff_ignore_ancestry, force_delete,
11930                                        dry_run, merge_options,
11931                                        ctx, result_pool, scratch_pool);
11932       /* ### Currently this merge just errors out on any conflicts */
11933       *conflict_report = NULL;
11934     }
11935   else
11936   err = do_merge(NULL, NULL, conflict_report, &use_sleep,
11937                  merge_sources, target, ra_session,
11938                  TRUE /*sources_related*/, same_repos, ignore_mergeinfo,
11939                  diff_ignore_ancestry, force_delete, dry_run,
11940                  record_only, NULL, FALSE, FALSE, depth, merge_options,
11941                  ctx, result_pool, scratch_pool);
11942 
11943   /* We're done with our RA session. */
11944   svn_pool_destroy(sesspool);
11945 
11946   if (use_sleep)
11947     svn_io_sleep_for_timestamps(target_abspath, scratch_pool);
11948 
11949   SVN_ERR(err);
11950   return SVN_NO_ERROR;
11951 }
11952 
11953 /* Details of an automatic merge. */
11954 typedef struct automatic_merge_t
11955 {
11956   svn_client__pathrev_t *yca, *base, *right, *target;
11957   svn_boolean_t is_reintegrate_like;
11958   svn_boolean_t allow_mixed_rev, allow_local_mods, allow_switched_subtrees;
11959 } automatic_merge_t;
11960 
11961 static svn_error_t *
11962 client_find_automatic_merge(automatic_merge_t **merge_p,
11963                             const char *source_path_or_url,
11964                             const svn_opt_revision_t *source_revision,
11965                             const char *target_abspath,
11966                             svn_boolean_t allow_mixed_rev,
11967                             svn_boolean_t allow_local_mods,
11968                             svn_boolean_t allow_switched_subtrees,
11969                             svn_client_ctx_t *ctx,
11970                             apr_pool_t *result_pool,
11971                             apr_pool_t *scratch_pool);
11972 
11973 static svn_error_t *
11974 do_automatic_merge_locked(svn_client__conflict_report_t **conflict_report,
11975                           const automatic_merge_t *merge,
11976                           const char *target_abspath,
11977                           svn_depth_t depth,
11978                           svn_boolean_t diff_ignore_ancestry,
11979                           svn_boolean_t force_delete,
11980                           svn_boolean_t record_only,
11981                           svn_boolean_t dry_run,
11982                           const apr_array_header_t *merge_options,
11983                           svn_client_ctx_t *ctx,
11984                           apr_pool_t *result_pool,
11985                           apr_pool_t *scratch_pool);
11986 
11987 svn_error_t *
svn_client_merge_peg5(const char * source_path_or_url,const apr_array_header_t * ranges_to_merge,const svn_opt_revision_t * source_peg_revision,const char * target_wcpath,svn_depth_t depth,svn_boolean_t ignore_mergeinfo,svn_boolean_t diff_ignore_ancestry,svn_boolean_t force_delete,svn_boolean_t record_only,svn_boolean_t dry_run,svn_boolean_t allow_mixed_rev,const apr_array_header_t * merge_options,svn_client_ctx_t * ctx,apr_pool_t * pool)11988 svn_client_merge_peg5(const char *source_path_or_url,
11989                       const apr_array_header_t *ranges_to_merge,
11990                       const svn_opt_revision_t *source_peg_revision,
11991                       const char *target_wcpath,
11992                       svn_depth_t depth,
11993                       svn_boolean_t ignore_mergeinfo,
11994                       svn_boolean_t diff_ignore_ancestry,
11995                       svn_boolean_t force_delete,
11996                       svn_boolean_t record_only,
11997                       svn_boolean_t dry_run,
11998                       svn_boolean_t allow_mixed_rev,
11999                       const apr_array_header_t *merge_options,
12000                       svn_client_ctx_t *ctx,
12001                       apr_pool_t *pool)
12002 {
12003   const char *target_abspath, *lock_abspath;
12004   svn_client__conflict_report_t *conflict_report;
12005 
12006   /* No ranges to merge?  No problem. */
12007   if (ranges_to_merge != NULL && ranges_to_merge->nelts == 0)
12008     return SVN_NO_ERROR;
12009 
12010   SVN_ERR(get_target_and_lock_abspath(&target_abspath, &lock_abspath,
12011                                       target_wcpath, ctx, pool));
12012 
12013   /* Do an automatic merge if no revision ranges are specified. */
12014   if (ranges_to_merge == NULL)
12015     {
12016       automatic_merge_t *merge;
12017 
12018       if (ignore_mergeinfo)
12019         return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
12020                                 _("Cannot merge automatically while "
12021                                   "ignoring mergeinfo"));
12022 
12023       /* Find the details of the merge needed. */
12024       SVN_ERR(client_find_automatic_merge(
12025                                     &merge,
12026                                     source_path_or_url, source_peg_revision,
12027                                     target_abspath,
12028                                     allow_mixed_rev,
12029                                     TRUE /*allow_local_mods*/,
12030                                     TRUE /*allow_switched_subtrees*/,
12031                                     ctx, pool, pool));
12032 
12033       if (!dry_run)
12034         SVN_WC__CALL_WITH_WRITE_LOCK(
12035           do_automatic_merge_locked(&conflict_report,
12036                                     merge,
12037                                     target_abspath, depth,
12038                                     diff_ignore_ancestry,
12039                                     force_delete, record_only, dry_run,
12040                                     merge_options, ctx, pool, pool),
12041           ctx->wc_ctx, lock_abspath, FALSE /* lock_anchor */, pool);
12042       else
12043         SVN_ERR(do_automatic_merge_locked(&conflict_report,
12044                                     merge,
12045                                     target_abspath, depth,
12046                                     diff_ignore_ancestry,
12047                                     force_delete, record_only, dry_run,
12048                                     merge_options, ctx, pool, pool));
12049     }
12050   else if (!dry_run)
12051     SVN_WC__CALL_WITH_WRITE_LOCK(
12052       merge_peg_locked(&conflict_report,
12053                        source_path_or_url, source_peg_revision,
12054                        ranges_to_merge,
12055                        target_abspath, depth, ignore_mergeinfo,
12056                        diff_ignore_ancestry,
12057                        force_delete, record_only, dry_run,
12058                        allow_mixed_rev, merge_options, ctx, pool, pool),
12059       ctx->wc_ctx, lock_abspath, FALSE /* lock_anchor */, pool);
12060   else
12061     SVN_ERR(merge_peg_locked(&conflict_report,
12062                        source_path_or_url, source_peg_revision,
12063                        ranges_to_merge,
12064                        target_abspath, depth, ignore_mergeinfo,
12065                        diff_ignore_ancestry,
12066                        force_delete, record_only, dry_run,
12067                        allow_mixed_rev, merge_options, ctx, pool, pool));
12068 
12069   SVN_ERR(svn_client__make_merge_conflict_error(conflict_report, pool));
12070   return SVN_NO_ERROR;
12071 }
12072 
12073 
12074 /* The location-history of a branch.
12075  *
12076  * This structure holds the set of path-revisions occupied by a branch,
12077  * from an externally chosen 'tip' location back to its origin.  The
12078  * 'tip' location is the youngest location that we are considering on
12079  * the branch. */
12080 typedef struct branch_history_t
12081 {
12082   /* The tip location of the branch.  That is, the youngest location that's
12083    * in the repository and that we're considering.  If we're considering a
12084    * target branch right up to an uncommitted WC, then this is the WC base
12085    * (pristine) location. */
12086   svn_client__pathrev_t *tip;
12087   /* The location-segment history, as mergeinfo. */
12088   svn_mergeinfo_t history;
12089   /* Whether the location-segment history reached as far as (necessarily
12090      the root path in) revision 0 -- a fact that can't be represented as
12091      mergeinfo. */
12092   svn_boolean_t has_r0_history;
12093 } branch_history_t;
12094 
12095 /* Return the location on BRANCH_HISTORY at revision REV, or NULL if none. */
12096 static svn_client__pathrev_t *
location_on_branch_at_rev(const branch_history_t * branch_history,svn_revnum_t rev,apr_pool_t * result_pool,apr_pool_t * scratch_pool)12097 location_on_branch_at_rev(const branch_history_t *branch_history,
12098                           svn_revnum_t rev,
12099                           apr_pool_t *result_pool,
12100                           apr_pool_t *scratch_pool)
12101 {
12102   apr_hash_index_t *hi;
12103 
12104   for (hi = apr_hash_first(scratch_pool, branch_history->history); hi;
12105        hi = apr_hash_next(hi))
12106     {
12107       const char *fspath = apr_hash_this_key(hi);
12108       svn_rangelist_t *rangelist = apr_hash_this_val(hi);
12109       int i;
12110 
12111       for (i = 0; i < rangelist->nelts; i++)
12112         {
12113           svn_merge_range_t *r = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
12114           if (r->start < rev && rev <= r->end)
12115             {
12116               return svn_client__pathrev_create_with_relpath(
12117                        branch_history->tip->repos_root_url,
12118                        branch_history->tip->repos_uuid,
12119                        rev, fspath + 1, result_pool);
12120             }
12121         }
12122     }
12123   return NULL;
12124 }
12125 
12126 /* */
12127 typedef struct source_and_target_t
12128 {
12129   svn_client__pathrev_t *source;
12130   svn_ra_session_t *source_ra_session;
12131   branch_history_t source_branch;
12132 
12133   merge_target_t *target;
12134   svn_ra_session_t *target_ra_session;
12135   branch_history_t target_branch;
12136 
12137   /* Repos location of the youngest common ancestor of SOURCE and TARGET. */
12138   svn_client__pathrev_t *yca;
12139 } source_and_target_t;
12140 
12141 /* Set *INTERSECTION_P to the intersection of BRANCH_HISTORY with the
12142  * revision range OLDEST_REV to YOUNGEST_REV (inclusive).
12143  *
12144  * If the intersection is empty, the result will be a branch history object
12145  * containing an empty (not null) history.
12146  *
12147  * ### The 'tip' of the result is currently unchanged.
12148  */
12149 static svn_error_t *
branch_history_intersect_range(branch_history_t ** intersection_p,const branch_history_t * branch_history,svn_revnum_t oldest_rev,svn_revnum_t youngest_rev,apr_pool_t * result_pool,apr_pool_t * scratch_pool)12150 branch_history_intersect_range(branch_history_t **intersection_p,
12151                                const branch_history_t *branch_history,
12152                                svn_revnum_t oldest_rev,
12153                                svn_revnum_t youngest_rev,
12154                                apr_pool_t *result_pool,
12155                                apr_pool_t *scratch_pool)
12156 {
12157   branch_history_t *result = apr_palloc(result_pool, sizeof(*result));
12158 
12159   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(oldest_rev));
12160   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
12161   SVN_ERR_ASSERT(oldest_rev >= 1);
12162   /* Allow a just-empty range (oldest = youngest + 1) but not an
12163    * arbitrary reverse range (such as oldest = youngest + 2). */
12164   SVN_ERR_ASSERT(oldest_rev <= youngest_rev + 1);
12165 
12166   if (oldest_rev <= youngest_rev)
12167     {
12168       SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(
12169                 &result->history, branch_history->history,
12170                 youngest_rev, oldest_rev - 1, TRUE /* include_range */,
12171                 result_pool, scratch_pool));
12172       result->history = svn_mergeinfo_dup(result->history, result_pool);
12173     }
12174   else
12175     {
12176       result->history = apr_hash_make(result_pool);
12177     }
12178   result->has_r0_history = FALSE;
12179 
12180   /* ### TODO: Set RESULT->tip to the tip of the intersection. */
12181   result->tip = svn_client__pathrev_dup(branch_history->tip, result_pool);
12182 
12183   *intersection_p = result;
12184   return SVN_NO_ERROR;
12185 }
12186 
12187 /* Set *OLDEST_P and *YOUNGEST_P to the oldest and youngest locations
12188  * (inclusive) along BRANCH.  OLDEST_P and/or YOUNGEST_P may be NULL if not
12189  * wanted.
12190  */
12191 static svn_error_t *
branch_history_get_endpoints(svn_client__pathrev_t ** oldest_p,svn_client__pathrev_t ** youngest_p,const branch_history_t * branch,apr_pool_t * result_pool,apr_pool_t * scratch_pool)12192 branch_history_get_endpoints(svn_client__pathrev_t **oldest_p,
12193                              svn_client__pathrev_t **youngest_p,
12194                              const branch_history_t *branch,
12195                              apr_pool_t *result_pool,
12196                              apr_pool_t *scratch_pool)
12197 {
12198   svn_revnum_t youngest_rev, oldest_rev;
12199 
12200   SVN_ERR(svn_mergeinfo__get_range_endpoints(
12201             &youngest_rev, &oldest_rev,
12202             branch->history, scratch_pool));
12203   if (oldest_p)
12204     *oldest_p = location_on_branch_at_rev(
12205                   branch, oldest_rev + 1, result_pool, scratch_pool);
12206   if (youngest_p)
12207     *youngest_p = location_on_branch_at_rev(
12208                     branch, youngest_rev, result_pool, scratch_pool);
12209   return SVN_NO_ERROR;
12210 }
12211 
12212 /* Implements the svn_log_entry_receiver_t interface.
12213 
12214   Set *BATON to LOG_ENTRY->revision and return SVN_ERR_CEASE_INVOCATION. */
12215 static svn_error_t *
operative_rev_receiver(void * baton,svn_log_entry_t * log_entry,apr_pool_t * pool)12216 operative_rev_receiver(void *baton,
12217                        svn_log_entry_t *log_entry,
12218                        apr_pool_t *pool)
12219 {
12220   svn_revnum_t *operative_rev = baton;
12221 
12222   *operative_rev = log_entry->revision;
12223 
12224   /* We've found the youngest merged or oldest eligible revision, so
12225      we're done...
12226 
12227      ...but wait, shouldn't we care if LOG_ENTRY->NON_INHERITABLE is
12228      true?  Because if it is, then LOG_ENTRY->REVISION is only
12229      partially merged/elgibile!  And our only caller,
12230      find_last_merged_location (via short_circuit_mergeinfo_log) is
12231      interested in *fully* merged revisions.  That's all true, but if
12232      find_last_merged_location() finds the youngest merged revision it
12233      will also check for the oldest eligible revision.  So in the case
12234      the youngest merged rev is non-inheritable, the *same* non-inheritable
12235      rev will be found as the oldest eligible rev -- and
12236      find_last_merged_location() handles that situation. */
12237   return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
12238 }
12239 
12240 /* Wrapper around svn_client__mergeinfo_log. All arguments are as per
12241    that private API.  The discover_changed_paths, depth, and revprops args to
12242    svn_client__mergeinfo_log are always TRUE, svn_depth_infinity_t,
12243    and empty array respectively.
12244 
12245    If RECEIVER raises a SVN_ERR_CEASE_INVOCATION error, but still sets
12246    *REVISION to a valid revnum, then clear the error.  Otherwise return
12247    any error. */
12248 static svn_error_t*
short_circuit_mergeinfo_log(svn_mergeinfo_catalog_t * target_mergeinfo_cat,svn_boolean_t finding_merged,const char * target_path_or_url,const svn_opt_revision_t * target_peg_revision,const char * source_path_or_url,const svn_opt_revision_t * source_peg_revision,const svn_opt_revision_t * source_start_revision,const svn_opt_revision_t * source_end_revision,svn_log_entry_receiver_t receiver,svn_revnum_t * revision,svn_client_ctx_t * ctx,svn_ra_session_t * ra_session,apr_pool_t * result_pool,apr_pool_t * scratch_pool)12249 short_circuit_mergeinfo_log(svn_mergeinfo_catalog_t *target_mergeinfo_cat,
12250                             svn_boolean_t finding_merged,
12251                             const char *target_path_or_url,
12252                             const svn_opt_revision_t *target_peg_revision,
12253                             const char *source_path_or_url,
12254                             const svn_opt_revision_t *source_peg_revision,
12255                             const svn_opt_revision_t *source_start_revision,
12256                             const svn_opt_revision_t *source_end_revision,
12257                             svn_log_entry_receiver_t receiver,
12258                             svn_revnum_t *revision,
12259                             svn_client_ctx_t *ctx,
12260                             svn_ra_session_t *ra_session,
12261                             apr_pool_t *result_pool,
12262                             apr_pool_t *scratch_pool)
12263 {
12264   apr_array_header_t *revprops;
12265   svn_error_t *err;
12266   const char *session_url;
12267 
12268   SVN_ERR(svn_ra_get_session_url(ra_session, &session_url, scratch_pool));
12269 
12270   revprops = apr_array_make(scratch_pool, 0, sizeof(const char *));
12271   err = svn_client__mergeinfo_log(finding_merged,
12272                                   target_path_or_url,
12273                                   target_peg_revision,
12274                                   target_mergeinfo_cat,
12275                                   source_path_or_url,
12276                                   source_peg_revision,
12277                                   source_start_revision,
12278                                   source_end_revision,
12279                                   receiver, revision,
12280                                   TRUE, svn_depth_infinity,
12281                                   revprops, ctx, ra_session,
12282                                   result_pool, scratch_pool);
12283 
12284   err = svn_error_compose_create(
12285                   err,
12286                   svn_ra_reparent(ra_session, session_url, scratch_pool));
12287 
12288   if (err)
12289     {
12290       /* We expect RECEIVER to short-circuit the (potentially expensive) log
12291          by raising an SVN_ERR_CEASE_INVOCATION -- see operative_rev_receiver.
12292          So we can ignore that error, but only as long as we actually found a
12293          valid revision. */
12294       if (SVN_IS_VALID_REVNUM(*revision)
12295           && err->apr_err == SVN_ERR_CEASE_INVOCATION)
12296         {
12297           svn_error_clear(err);
12298           err = NULL;
12299         }
12300       else
12301         {
12302           return svn_error_trace(err);
12303         }
12304     }
12305   return SVN_NO_ERROR;
12306 }
12307 
12308 /* Set *BASE_P to the last location on SOURCE_BRANCH such that all changes
12309  * on SOURCE_BRANCH after YCA up to and including *BASE_P have already
12310  * been fully merged into TARGET.
12311  *
12312  *               *BASE_P       TIP
12313  *          o-------o-----------o--- SOURCE_BRANCH
12314  *         /         \
12315  *   -----o     prev. \
12316  *     YCA \    merges \
12317  *          o-----------o----------- TARGET branch
12318  *
12319  * In terms of mergeinfo:
12320  *
12321  *     Source     a--...                     o=change, -=no-op revision
12322  *       branch  /   \
12323  *     YCA -->  o     a---o---o---o---o---   d=delete, a=add-as-a-copy
12324  *
12325  *     Eligible -.eee.eeeeeeeeeeeeeeeeeeee   .=not a source branch location
12326  *
12327  *     Tgt-mi   -.mmm.mm-mm-------m-------   m=merged to root of TARGET or
12328  *                                           subtree of TARGET with no
12329  *                                           operative changes outside of that
12330  *                                           subtree, -=not merged
12331  *
12332  *     Eligible -.---.--e--eeeeeee-eeeeeee
12333  *
12334  *     Next     --------^-----------------   BASE is just before here.
12335  *
12336  *             /         \
12337  *       -----o     prev. \
12338  *         YCA \    merges \
12339  *              o-----------o-------------
12340  *
12341  * If no revisions from SOURCE_BRANCH have been completely merged to TARGET,
12342  * then set *BASE_P to the YCA.
12343  */
12344 static svn_error_t *
find_last_merged_location(svn_client__pathrev_t ** base_p,svn_client__pathrev_t * yca,const branch_history_t * source_branch,svn_client__pathrev_t * target,svn_client_ctx_t * ctx,svn_ra_session_t * ra_session,apr_pool_t * result_pool,apr_pool_t * scratch_pool)12345 find_last_merged_location(svn_client__pathrev_t **base_p,
12346                           svn_client__pathrev_t *yca,
12347                           const branch_history_t *source_branch,
12348                           svn_client__pathrev_t *target,
12349                           svn_client_ctx_t *ctx,
12350                           svn_ra_session_t *ra_session,
12351                           apr_pool_t *result_pool,
12352                           apr_pool_t *scratch_pool)
12353 {
12354   svn_opt_revision_t source_peg_rev, source_start_rev, source_end_rev,
12355     target_opt_rev;
12356   svn_revnum_t youngest_merged_rev = SVN_INVALID_REVNUM;
12357   svn_mergeinfo_catalog_t target_mergeinfo_cat = NULL;
12358 
12359   /* Using a local subpool for 'target_mergeinfo_cat' can make a big
12360      reduction in overall memory usage. */
12361   apr_pool_t *tmic_pool = svn_pool_create(scratch_pool);
12362 
12363   source_peg_rev.kind = svn_opt_revision_number;
12364   source_peg_rev.value.number = source_branch->tip->rev;
12365   source_start_rev.kind = svn_opt_revision_number;
12366   source_start_rev.value.number = yca->rev;
12367   source_end_rev.kind = svn_opt_revision_number;
12368   source_end_rev.value.number = source_branch->tip->rev;
12369   target_opt_rev.kind = svn_opt_revision_number;
12370   target_opt_rev.value.number = target->rev;
12371 
12372   /* Find the youngest revision fully merged from SOURCE_BRANCH to TARGET,
12373      if such a revision exists. */
12374   SVN_ERR(short_circuit_mergeinfo_log(&target_mergeinfo_cat,
12375                                       TRUE, /* Find merged */
12376                                       target->url, &target_opt_rev,
12377                                       source_branch->tip->url,
12378                                       &source_peg_rev,
12379                                       &source_end_rev, &source_start_rev,
12380                                       operative_rev_receiver,
12381                                       &youngest_merged_rev,
12382                                       ctx, ra_session,
12383                                       tmic_pool, tmic_pool));
12384 
12385   if (!SVN_IS_VALID_REVNUM(youngest_merged_rev))
12386     {
12387       /* No revisions have been completely merged from SOURCE_BRANCH to
12388          TARGET so the base for the next merge is the YCA. */
12389       *base_p = yca;
12390     }
12391   else
12392     {
12393       /* One or more revisions have already been completely merged from
12394          SOURCE_BRANCH to TARGET, now find the oldest revision, older
12395          than the youngest merged revision, which is still eligible to
12396          be merged, if such exists. */
12397       branch_history_t *contiguous_source;
12398       svn_revnum_t base_rev;
12399       svn_revnum_t oldest_eligible_rev = SVN_INVALID_REVNUM;
12400 
12401       /* If the only revisions eligible are younger than the youngest merged
12402          revision we can simply assume that the youngest eligible revision
12403          is the youngest merged revision.  Obviously this may not be true!
12404          The revisions between the youngest merged revision and the tip of
12405          the branch may have several inoperative revisions -- they may *all*
12406          be inoperative revisions!  But for the purpose of this function
12407          (i.e. finding the youngest revision after the YCA where all revs have
12408          been merged) that doesn't matter. */
12409       source_end_rev.value.number = youngest_merged_rev;
12410       SVN_ERR(short_circuit_mergeinfo_log(&target_mergeinfo_cat,
12411                                           FALSE, /* Find eligible */
12412                                           target->url, &target_opt_rev,
12413                                           source_branch->tip->url,
12414                                           &source_peg_rev,
12415                                           &source_start_rev, &source_end_rev,
12416                                           operative_rev_receiver,
12417                                           &oldest_eligible_rev,
12418                                           ctx, ra_session,
12419                                           tmic_pool, tmic_pool));
12420 
12421       /* If there are revisions eligible for merging, use the oldest one
12422          to calculate the base.  Otherwise there are no operative revisions
12423          to merge and we can simple set the base to the youngest revision
12424          already merged. */
12425       if (SVN_IS_VALID_REVNUM(oldest_eligible_rev))
12426         base_rev = oldest_eligible_rev - 1;
12427       else
12428         base_rev = youngest_merged_rev;
12429 
12430       /* Find the branch location just before the oldest eligible rev.
12431          (We can't just use the base revs calculated above because the branch
12432          might have a gap there.) */
12433       SVN_ERR(branch_history_intersect_range(&contiguous_source,
12434                                              source_branch, yca->rev,
12435                                              base_rev,
12436                                              scratch_pool, scratch_pool));
12437       SVN_ERR(branch_history_get_endpoints(NULL, base_p, contiguous_source,
12438                                            result_pool, scratch_pool));
12439     }
12440 
12441   svn_pool_destroy(tmic_pool);
12442   return SVN_NO_ERROR;
12443 }
12444 
12445 /* Find a merge base location on the target branch, like in a sync
12446  * merge.
12447  *
12448  *                BASE          S_T->source
12449  *          o-------o-----------o---
12450  *         /         \           \
12451  *   -----o     prev. \           \  this
12452  *     YCA \    merge  \           \ merge
12453  *          o-----------o-----------o
12454  *                                  S_T->target
12455  *
12456  * Set *BASE_P to BASE, the youngest location in the history of S_T->source
12457  * (at or after the YCA) at which all revisions up to BASE are effectively
12458  * merged into S_T->target.
12459  *
12460  * If no locations on the history of S_T->source are effectively merged to
12461  * S_T->target, set *BASE_P to the YCA.
12462  */
12463 static svn_error_t *
find_base_on_source(svn_client__pathrev_t ** base_p,source_and_target_t * s_t,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)12464 find_base_on_source(svn_client__pathrev_t **base_p,
12465                     source_and_target_t *s_t,
12466                     svn_client_ctx_t *ctx,
12467                     apr_pool_t *result_pool,
12468                     apr_pool_t *scratch_pool)
12469 {
12470   SVN_ERR(find_last_merged_location(base_p,
12471                                     s_t->yca,
12472                                     &s_t->source_branch,
12473                                     s_t->target_branch.tip,
12474                                     ctx,
12475                                     s_t->source_ra_session,
12476                                     result_pool, scratch_pool));
12477   return SVN_NO_ERROR;
12478 }
12479 
12480 /* Find a merge base location on the target branch, like in a reintegrate
12481  * merge.
12482  *
12483  *                              S_T->source
12484  *          o-----------o-------o---
12485  *         /    prev.  /         \
12486  *   -----o     merge /           \  this
12487  *     YCA \         /             \ merge
12488  *          o-------o---------------o
12489  *                BASE              S_T->target
12490  *
12491  * Set *BASE_P to BASE, the youngest location in the history of S_T->target
12492  * (at or after the YCA) at which all revisions up to BASE are effectively
12493  * merged into S_T->source.
12494  *
12495  * If no locations on the history of S_T->target are effectively merged to
12496  * S_T->source, set *BASE_P to the YCA.
12497  */
12498 static svn_error_t *
find_base_on_target(svn_client__pathrev_t ** base_p,source_and_target_t * s_t,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)12499 find_base_on_target(svn_client__pathrev_t **base_p,
12500                     source_and_target_t *s_t,
12501                     svn_client_ctx_t *ctx,
12502                     apr_pool_t *result_pool,
12503                     apr_pool_t *scratch_pool)
12504 {
12505   SVN_ERR(find_last_merged_location(base_p,
12506                                     s_t->yca,
12507                                     &s_t->target_branch,
12508                                     s_t->source,
12509                                     ctx,
12510                                     s_t->target_ra_session,
12511                                     result_pool, scratch_pool));
12512 
12513   return SVN_NO_ERROR;
12514 }
12515 
12516 /* Find the last point at which the branch at S_T->source was completely
12517  * merged to the branch at S_T->target or vice-versa.
12518  *
12519  * Fill in S_T->source_branch and S_T->target_branch and S_T->yca.
12520  * Set *BASE_P to the merge base.  Set *IS_REINTEGRATE_LIKE to true if
12521  * an automatic merge from source to target would be a reintegration
12522  * merge: that is, if the last automatic merge was in the opposite
12523  * direction; or to false otherwise.
12524  *
12525  * If there is no youngest common ancestor, throw an error.
12526  */
12527 static svn_error_t *
find_automatic_merge(svn_client__pathrev_t ** base_p,svn_boolean_t * is_reintegrate_like,source_and_target_t * s_t,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)12528 find_automatic_merge(svn_client__pathrev_t **base_p,
12529                      svn_boolean_t *is_reintegrate_like,
12530                      source_and_target_t *s_t,
12531                      svn_client_ctx_t *ctx,
12532                      apr_pool_t *result_pool,
12533                      apr_pool_t *scratch_pool)
12534 {
12535   svn_client__pathrev_t *base_on_source, *base_on_target;
12536 
12537   /* Get the location-history of each branch. */
12538   s_t->source_branch.tip = s_t->source;
12539   SVN_ERR(svn_client__get_history_as_mergeinfo(
12540             &s_t->source_branch.history, &s_t->source_branch.has_r0_history,
12541             s_t->source, SVN_INVALID_REVNUM, SVN_INVALID_REVNUM,
12542             s_t->source_ra_session, ctx, scratch_pool));
12543   s_t->target_branch.tip = &s_t->target->loc;
12544   SVN_ERR(svn_client__get_history_as_mergeinfo(
12545             &s_t->target_branch.history, &s_t->target_branch.has_r0_history,
12546             &s_t->target->loc, SVN_INVALID_REVNUM, SVN_INVALID_REVNUM,
12547             s_t->target_ra_session, ctx, scratch_pool));
12548 
12549   SVN_ERR(svn_client__calc_youngest_common_ancestor(
12550             &s_t->yca, s_t->source, s_t->source_branch.history,
12551             s_t->source_branch.has_r0_history,
12552             &s_t->target->loc, s_t->target_branch.history,
12553             s_t->target_branch.has_r0_history,
12554             result_pool, scratch_pool));
12555 
12556   if (! s_t->yca)
12557     return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
12558                              _("'%s@%ld' must be ancestrally related to "
12559                                "'%s@%ld'"),
12560                              s_t->source->url, s_t->source->rev,
12561                              s_t->target->loc.url, s_t->target->loc.rev);
12562 
12563   /* Find the latest revision of A synced to B and the latest
12564    * revision of B synced to A.
12565    *
12566    *   base_on_source = youngest_complete_synced_point(source, target)
12567    *   base_on_target = youngest_complete_synced_point(target, source)
12568    */
12569   SVN_ERR(find_base_on_source(&base_on_source, s_t,
12570                               ctx, scratch_pool, scratch_pool));
12571   SVN_ERR(find_base_on_target(&base_on_target, s_t,
12572                               ctx, scratch_pool, scratch_pool));
12573 
12574   /* Choose a base. */
12575   if (base_on_source->rev >= base_on_target->rev)
12576     {
12577       *base_p = base_on_source;
12578       *is_reintegrate_like = FALSE;
12579     }
12580   else
12581     {
12582       *base_p = base_on_target;
12583       *is_reintegrate_like = TRUE;
12584     }
12585 
12586   return SVN_NO_ERROR;
12587 }
12588 
12589 /** Find out what kind of automatic merge would be needed, when the target
12590  * is only known as a repository location rather than a WC.
12591  *
12592  * Like find_automatic_merge() except that the target is
12593  * specified by @a target_path_or_url at @a target_revision, which must
12594  * refer to a repository location, instead of by a WC path argument.
12595  *
12596  * Set *MERGE_P to a new structure with all fields filled in except the
12597  * 'allow_*' flags.
12598  */
12599 static svn_error_t *
find_automatic_merge_no_wc(automatic_merge_t ** merge_p,const char * source_path_or_url,const svn_opt_revision_t * source_revision,const char * target_path_or_url,const svn_opt_revision_t * target_revision,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)12600 find_automatic_merge_no_wc(automatic_merge_t **merge_p,
12601                            const char *source_path_or_url,
12602                            const svn_opt_revision_t *source_revision,
12603                            const char *target_path_or_url,
12604                            const svn_opt_revision_t *target_revision,
12605                            svn_client_ctx_t *ctx,
12606                            apr_pool_t *result_pool,
12607                            apr_pool_t *scratch_pool)
12608 {
12609   source_and_target_t *s_t = apr_palloc(scratch_pool, sizeof(*s_t));
12610   svn_client__pathrev_t *target_loc;
12611   automatic_merge_t *merge = apr_palloc(result_pool, sizeof(*merge));
12612 
12613   /* Source */
12614   SVN_ERR(svn_client__ra_session_from_path2(
12615             &s_t->source_ra_session, &s_t->source,
12616             source_path_or_url, NULL, source_revision, source_revision,
12617             ctx, result_pool));
12618 
12619   /* Target */
12620   SVN_ERR(svn_client__ra_session_from_path2(
12621             &s_t->target_ra_session, &target_loc,
12622             target_path_or_url, NULL, target_revision, target_revision,
12623             ctx, result_pool));
12624   s_t->target = apr_palloc(scratch_pool, sizeof(*s_t->target));
12625   s_t->target->abspath = NULL;  /* indicate the target is not a WC */
12626   s_t->target->loc = *target_loc;
12627 
12628   SVN_ERR(find_automatic_merge(&merge->base, &merge->is_reintegrate_like, s_t,
12629                                ctx, result_pool, scratch_pool));
12630 
12631   merge->right = s_t->source;
12632   merge->target = &s_t->target->loc;
12633   merge->yca = s_t->yca;
12634   *merge_p = merge;
12635 
12636   return SVN_NO_ERROR;
12637 }
12638 
12639 /* Find the information needed to merge all unmerged changes from a source
12640  * branch into a target branch.
12641  *
12642  * Set @a *merge_p to the information needed to merge all unmerged changes
12643  * (up to @a source_revision) from the source branch @a source_path_or_url
12644  * at @a source_revision into the target WC at @a target_abspath.
12645  *
12646  * The flags @a allow_mixed_rev, @a allow_local_mods and
12647  * @a allow_switched_subtrees enable merging into a WC that is in any or all
12648  * of the states described by their names, but only if this function decides
12649  * that the merge will be in the same direction as the last automatic merge.
12650  * If, on the other hand, the last automatic merge was in the opposite
12651  * direction, then such states of the WC are not allowed regardless
12652  * of these flags.  This function merely records these flags in the
12653  * @a *merge_p structure; do_automatic_merge_locked() checks the WC
12654  * state for compliance.
12655  *
12656  * Allocate the @a *merge_p structure in @a result_pool.
12657  */
12658 static svn_error_t *
client_find_automatic_merge(automatic_merge_t ** merge_p,const char * source_path_or_url,const svn_opt_revision_t * source_revision,const char * target_abspath,svn_boolean_t allow_mixed_rev,svn_boolean_t allow_local_mods,svn_boolean_t allow_switched_subtrees,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)12659 client_find_automatic_merge(automatic_merge_t **merge_p,
12660                             const char *source_path_or_url,
12661                             const svn_opt_revision_t *source_revision,
12662                             const char *target_abspath,
12663                             svn_boolean_t allow_mixed_rev,
12664                             svn_boolean_t allow_local_mods,
12665                             svn_boolean_t allow_switched_subtrees,
12666                             svn_client_ctx_t *ctx,
12667                             apr_pool_t *result_pool,
12668                             apr_pool_t *scratch_pool)
12669 {
12670   source_and_target_t *s_t = apr_palloc(result_pool, sizeof(*s_t));
12671   automatic_merge_t *merge = apr_palloc(result_pool, sizeof(*merge));
12672 
12673   SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath));
12674 
12675   /* "Open" the target WC.  Check the target WC for mixed-rev, local mods and
12676    * switched subtrees yet to faster exit and notify user before contacting
12677    * with server.  After we find out what kind of merge is required, then if a
12678    * reintegrate-like merge is required we'll do the stricter checks, in
12679    * do_automatic_merge_locked(). */
12680   SVN_ERR(open_target_wc(&s_t->target, target_abspath,
12681                          allow_mixed_rev,
12682                          allow_local_mods,
12683                          allow_switched_subtrees,
12684                          ctx, result_pool, scratch_pool));
12685 
12686   if (!s_t->target->loc.url)
12687     return svn_error_createf(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
12688                              _("Can't perform automatic merge into '%s' "
12689                                "because it is locally added and therefore "
12690                                "not related to the merge source"),
12691                              svn_dirent_local_style(target_abspath,
12692                                                     scratch_pool));
12693 
12694   /* Open RA sessions to the source and target trees. */
12695   SVN_ERR(svn_client_open_ra_session2(&s_t->target_ra_session,
12696                                       s_t->target->loc.url,
12697                                       s_t->target->abspath,
12698                                       ctx, result_pool, scratch_pool));
12699   SVN_ERR(svn_client__ra_session_from_path2(
12700             &s_t->source_ra_session, &s_t->source,
12701             source_path_or_url, NULL, source_revision, source_revision,
12702             ctx, result_pool));
12703 
12704   /* Check source is in same repos as target. */
12705   SVN_ERR(check_same_repos(s_t->source, source_path_or_url,
12706                            &s_t->target->loc, target_abspath,
12707                            TRUE /* strict_urls */, scratch_pool));
12708 
12709   SVN_ERR(find_automatic_merge(&merge->base, &merge->is_reintegrate_like, s_t,
12710                                ctx, result_pool, scratch_pool));
12711   merge->yca = s_t->yca;
12712   merge->right = s_t->source;
12713   merge->target = &s_t->target->loc;
12714   merge->allow_mixed_rev = allow_mixed_rev;
12715   merge->allow_local_mods = allow_local_mods;
12716   merge->allow_switched_subtrees = allow_switched_subtrees;
12717 
12718   *merge_p = merge;
12719 
12720   /* TODO: Close the source and target sessions here? */
12721 
12722   return SVN_NO_ERROR;
12723 }
12724 
12725 /* Perform an automatic merge, given the information in MERGE which
12726  * must have come from calling client_find_automatic_merge().
12727  *
12728  * Four locations are inputs: YCA, BASE, RIGHT, TARGET, as shown
12729  * depending on whether the base is on the source branch or the target
12730  * branch of this merge.
12731  *
12732  *                            RIGHT     (is_reintegrate_like)
12733  *          o-----------o-------o---
12734  *         /    prev.  /         \
12735  *   -----o     merge /           \  this
12736  *     YCA \         /             \ merge
12737  *          o-------o---------------o
12738  *                BASE            TARGET
12739  *
12740  * or
12741  *
12742  *                BASE        RIGHT      (! is_reintegrate_like)
12743  *          o-------o-----------o---
12744  *         /         \           \
12745  *   -----o     prev. \           \  this
12746  *     YCA \    merge  \           \ merge
12747  *          o-----------o-----------o
12748  *                                TARGET
12749  *
12750  * ### TODO: The reintegrate-like code path does not yet
12751  * eliminate already-cherry-picked revisions from the source.
12752  */
12753 static svn_error_t *
do_automatic_merge_locked(svn_client__conflict_report_t ** conflict_report,const automatic_merge_t * merge,const char * target_abspath,svn_depth_t depth,svn_boolean_t diff_ignore_ancestry,svn_boolean_t force_delete,svn_boolean_t record_only,svn_boolean_t dry_run,const apr_array_header_t * merge_options,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)12754 do_automatic_merge_locked(svn_client__conflict_report_t **conflict_report,
12755                           const automatic_merge_t *merge,
12756                           const char *target_abspath,
12757                           svn_depth_t depth,
12758                           svn_boolean_t diff_ignore_ancestry,
12759                           svn_boolean_t force_delete,
12760                           svn_boolean_t record_only,
12761                           svn_boolean_t dry_run,
12762                           const apr_array_header_t *merge_options,
12763                           svn_client_ctx_t *ctx,
12764                           apr_pool_t *result_pool,
12765                           apr_pool_t *scratch_pool)
12766 {
12767   merge_target_t *target;
12768   svn_boolean_t reintegrate_like = merge->is_reintegrate_like;
12769   svn_boolean_t use_sleep = FALSE;
12770   svn_error_t *err;
12771 
12772   SVN_ERR(open_target_wc(&target, target_abspath,
12773                          merge->allow_mixed_rev && ! reintegrate_like,
12774                          merge->allow_local_mods && ! reintegrate_like,
12775                          merge->allow_switched_subtrees && ! reintegrate_like,
12776                          ctx, scratch_pool, scratch_pool));
12777 
12778   if (reintegrate_like)
12779     {
12780       merge_source_t source;
12781       svn_ra_session_t *base_ra_session = NULL;
12782       svn_ra_session_t *right_ra_session = NULL;
12783       svn_ra_session_t *target_ra_session = NULL;
12784 
12785       if (record_only)
12786         return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
12787                                 _("The required merge is reintegrate-like, "
12788                                   "and the record-only option "
12789                                   "cannot be used with this kind of merge"));
12790 
12791       if (depth != svn_depth_unknown)
12792         return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
12793                                 _("The required merge is reintegrate-like, "
12794                                   "and the depth option "
12795                                   "cannot be used with this kind of merge"));
12796 
12797       if (force_delete)
12798         return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
12799                                 _("The required merge is reintegrate-like, "
12800                                   "and the force_delete option "
12801                                   "cannot be used with this kind of merge"));
12802 
12803       SVN_ERR(ensure_ra_session_url(&base_ra_session, merge->base->url,
12804                                     target->abspath, ctx, scratch_pool));
12805       SVN_ERR(ensure_ra_session_url(&right_ra_session, merge->right->url,
12806                                     target->abspath, ctx, scratch_pool));
12807       SVN_ERR(ensure_ra_session_url(&target_ra_session, target->loc.url,
12808                                     target->abspath, ctx, scratch_pool));
12809 
12810       /* Check for and reject any abnormalities -- such as revisions that
12811        * have not yet been merged in the opposite direction -- that a
12812        * 'reintegrate' merge would have rejected. */
12813       {
12814         merge_source_t *source2;
12815 
12816         SVN_ERR(find_reintegrate_merge(&source2, NULL,
12817                                        right_ra_session, merge->right,
12818                                        target_ra_session, target,
12819                                        ctx, scratch_pool, scratch_pool));
12820       }
12821 
12822       source.loc1 = merge->base;
12823       source.loc2 = merge->right;
12824       source.ancestral = ! merge->is_reintegrate_like;
12825 
12826       err = merge_cousins_and_supplement_mergeinfo(conflict_report,
12827                                                    &use_sleep,
12828                                                    target,
12829                                                    base_ra_session,
12830                                                    right_ra_session,
12831                                                    &source, merge->yca,
12832                                                    TRUE /* same_repos */,
12833                                                    depth,
12834                                                    FALSE /*diff_ignore_ancestry*/,
12835                                                    force_delete, record_only,
12836                                                    dry_run,
12837                                                    merge_options,
12838                                                    ctx,
12839                                                    result_pool, scratch_pool);
12840     }
12841   else /* ! merge->is_reintegrate_like */
12842     {
12843       /* Ignoring the base that we found, we pass the YCA instead and let
12844          do_merge() work out which subtrees need which revision ranges to
12845          be merged.  This enables do_merge() to fill in revision-range
12846          gaps that are older than the base that we calculated (which is
12847          for the root path of the merge).
12848 
12849          An improvement would be to change find_automatic_merge() to
12850          find the base for each sutree, and then here use the oldest base
12851          among all subtrees. */
12852       apr_array_header_t *merge_sources;
12853       svn_ra_session_t *ra_session = NULL;
12854 
12855       /* Normalize our merge sources, do_merge() requires this.  See the
12856          'MERGEINFO MERGE SOURCE NORMALIZATION' global comment. */
12857       SVN_ERR(ensure_ra_session_url(&ra_session, merge->right->url,
12858                                     target->abspath, ctx, scratch_pool));
12859       SVN_ERR(normalize_merge_sources_internal(
12860         &merge_sources, merge->right,
12861         svn_rangelist__initialize(merge->yca->rev, merge->right->rev, TRUE,
12862                                   scratch_pool),
12863         ra_session, ctx, scratch_pool, scratch_pool));
12864 
12865       err = do_merge(NULL, NULL, conflict_report, &use_sleep,
12866                      merge_sources, target, ra_session,
12867                      TRUE /*related*/, TRUE /*same_repos*/,
12868                      FALSE /*ignore_mergeinfo*/, diff_ignore_ancestry,
12869                      force_delete, dry_run,
12870                      record_only, NULL, FALSE, FALSE, depth, merge_options,
12871                      ctx, result_pool, scratch_pool);
12872     }
12873 
12874   if (use_sleep)
12875     svn_io_sleep_for_timestamps(target_abspath, scratch_pool);
12876 
12877   SVN_ERR(err);
12878 
12879   return SVN_NO_ERROR;
12880 }
12881 
12882 svn_error_t *
svn_client_get_merging_summary(svn_boolean_t * needs_reintegration,const char ** yca_url,svn_revnum_t * yca_rev,const char ** base_url,svn_revnum_t * base_rev,const char ** right_url,svn_revnum_t * right_rev,const char ** target_url,svn_revnum_t * target_rev,const char ** repos_root_url,const char * source_path_or_url,const svn_opt_revision_t * source_revision,const char * target_path_or_url,const svn_opt_revision_t * target_revision,svn_client_ctx_t * ctx,apr_pool_t * result_pool,apr_pool_t * scratch_pool)12883 svn_client_get_merging_summary(svn_boolean_t *needs_reintegration,
12884                                const char **yca_url, svn_revnum_t *yca_rev,
12885                                const char **base_url, svn_revnum_t *base_rev,
12886                                const char **right_url, svn_revnum_t *right_rev,
12887                                const char **target_url, svn_revnum_t *target_rev,
12888                                const char **repos_root_url,
12889                                const char *source_path_or_url,
12890                                const svn_opt_revision_t *source_revision,
12891                                const char *target_path_or_url,
12892                                const svn_opt_revision_t *target_revision,
12893                                svn_client_ctx_t *ctx,
12894                                apr_pool_t *result_pool,
12895                                apr_pool_t *scratch_pool)
12896 {
12897   svn_boolean_t target_is_wc;
12898   automatic_merge_t *merge;
12899 
12900   target_is_wc = (! svn_path_is_url(target_path_or_url))
12901                  && (target_revision->kind == svn_opt_revision_unspecified
12902                      || target_revision->kind == svn_opt_revision_working
12903                      || target_revision->kind == svn_opt_revision_base);
12904   if (target_is_wc)
12905     {
12906       const char *target_abspath;
12907 
12908       SVN_ERR(svn_dirent_get_absolute(&target_abspath, target_path_or_url,
12909                                       scratch_pool));
12910       SVN_ERR(client_find_automatic_merge(
12911                 &merge,
12912                 source_path_or_url, source_revision,
12913                 target_abspath,
12914                 TRUE, TRUE, TRUE,  /* allow_* */
12915                 ctx, scratch_pool, scratch_pool));
12916     }
12917   else
12918     SVN_ERR(find_automatic_merge_no_wc(
12919               &merge,
12920               source_path_or_url, source_revision,
12921               target_path_or_url, target_revision,
12922               ctx, scratch_pool, scratch_pool));
12923 
12924   if (needs_reintegration)
12925     *needs_reintegration = merge->is_reintegrate_like;
12926   if (yca_url)
12927     *yca_url = apr_pstrdup(result_pool, merge->yca->url);
12928   if (yca_rev)
12929     *yca_rev = merge->yca->rev;
12930   if (base_url)
12931     *base_url = apr_pstrdup(result_pool, merge->base->url);
12932   if (base_rev)
12933     *base_rev = merge->base->rev;
12934   if (right_url)
12935     *right_url = apr_pstrdup(result_pool, merge->right->url);
12936   if (right_rev)
12937     *right_rev = merge->right->rev;
12938   if (target_url)
12939     *target_url = apr_pstrdup(result_pool, merge->target->url);
12940   if (target_rev)
12941     *target_rev = merge->target->rev;
12942   if (repos_root_url)
12943     *repos_root_url = apr_pstrdup(result_pool, merge->yca->repos_root_url);
12944 
12945   return SVN_NO_ERROR;
12946 }
12947