1251881Speter/*
2251881Speter * merge.c: merging
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter/* ==================================================================== */
25251881Speter
26251881Speter
27251881Speter
28251881Speter/*** Includes ***/
29251881Speter
30251881Speter#include <assert.h>
31251881Speter#include <apr_strings.h>
32251881Speter#include <apr_tables.h>
33251881Speter#include <apr_hash.h>
34251881Speter#include "svn_types.h"
35251881Speter#include "svn_hash.h"
36251881Speter#include "svn_wc.h"
37251881Speter#include "svn_delta.h"
38251881Speter#include "svn_diff.h"
39251881Speter#include "svn_mergeinfo.h"
40251881Speter#include "svn_client.h"
41251881Speter#include "svn_string.h"
42251881Speter#include "svn_error.h"
43251881Speter#include "svn_dirent_uri.h"
44251881Speter#include "svn_path.h"
45251881Speter#include "svn_io.h"
46251881Speter#include "svn_utf.h"
47251881Speter#include "svn_pools.h"
48251881Speter#include "svn_config.h"
49251881Speter#include "svn_props.h"
50251881Speter#include "svn_time.h"
51251881Speter#include "svn_sorts.h"
52251881Speter#include "svn_subst.h"
53251881Speter#include "svn_ra.h"
54251881Speter#include "client.h"
55251881Speter#include "mergeinfo.h"
56251881Speter
57251881Speter#include "private/svn_opt_private.h"
58251881Speter#include "private/svn_wc_private.h"
59251881Speter#include "private/svn_mergeinfo_private.h"
60251881Speter#include "private/svn_fspath.h"
61251881Speter#include "private/svn_ra_private.h"
62251881Speter#include "private/svn_client_private.h"
63251881Speter#include "private/svn_subr_private.h"
64251881Speter
65251881Speter#include "svn_private_config.h"
66251881Speter
67251881Speter
68251881Speter/*-----------------------------------------------------------------------*/
69251881Speter
70251881Speter/* MERGEINFO MERGE SOURCE NORMALIZATION
71251881Speter *
72251881Speter * Nearly any helper function herein that accepts two URL/revision
73251881Speter * pairs (or equivalent struct merge_source_t) expects one of two things
74251881Speter * to be true:
75251881Speter *
76251881Speter *    1.  that mergeinfo is not being recorded at all for this
77251881Speter *        operation, or
78251881Speter *
79251881Speter *    2.  that the pairs represent two locations along a single line
80251881Speter *        of version history such that there are no copies in the
81251881Speter *        history of the object between the locations when treating
82251881Speter *        the oldest of the two locations as non-inclusive.  In other
83251881Speter *        words, if there is a copy at all between them, there is only
84251881Speter *        one copy and its source was the oldest of the two locations.
85251881Speter *
86251881Speter * We use svn_ra_get_location_segments() to split a given range of
87251881Speter * revisions across an object's history into several which obey these
88251881Speter * rules.  For example, an extract from the log of Subversion's own
89251881Speter * /subversion/tags/1.4.5 directory shows the following copies between
90251881Speter * r859500 and r866500 (omitting the '/subversion' prefix for clarity):
91251881Speter *
92251881Speter *    r859598:
93251881Speter *      A /branches/1.4.x  (from /trunk:859597)
94251881Speter *
95251881Speter *    r865417:
96251881Speter *      A /tags/1.4.4      (from /branches/1.4.x:865262)
97251881Speter *    # Notice that this copy leaves a gap between 865262 and 865417.
98251881Speter *
99251881Speter *    r866420:
100251881Speter *      A /branches/1.4.5  (from /tags/1.4.4:866419)
101251881Speter *
102251881Speter *    r866425:
103251881Speter *      D /branches/1.4.5
104251881Speter *      A /tags/1.4.5      (from /branches/1.4.5:866424)
105251881Speter *
106251881Speter * In graphical form:
107251881Speter *
108251881Speter *                859500 859597 865262        866419 866424 866500
109251881Speter *                  .      .      .             .      .      .
110251881Speter *    trunk       ------------------------------------------------
111251881Speter *                         \      .             .      .
112251881Speter *    branches/1.4.x        A-------------------------------------
113251881Speter *                          .     \______       .      .
114251881Speter *                          .            \      .      .
115251881Speter *    tags/1.4.4            .             A-----------------------
116251881Speter *                          .             .     \      .
117251881Speter *    branches/1.4.5        .             .      A------D
118251881Speter *                          .             .      .     \.
119251881Speter *    tags/1.4.5            .             .      .      A---------
120251881Speter *                          .             .      .      .
121251881Speter *                       859598        865417 866420 866425
122251881Speter *
123251881Speter * A merge of the difference between r859500 and r866500 of this directory
124251881Speter * gets split into sequential merges of the following location pairs.
125251881Speter *
126251881Speter *                859500 859597 865262 865416 866419 866424 866500
127251881Speter *                  .      .      .      .      .      .      .
128251881Speter *    trunk         (======]      .      .      .      .      .
129251881Speter *                                .      .      .      .      .
130251881Speter *    trunk                (      .      .      .      .      .
131251881Speter *    branches/1.4.x        ======]      .      .      .      .
132251881Speter *                                       .      .      .      .
133251881Speter *    branches/1.4.x              (      .      .      .      .
134251881Speter *    tags/1.4.4                   =============]      .      .
135251881Speter *    implicit_src_gap            (======]      .      .      .
136251881Speter *                                              .      .      .
137251881Speter *    tags/1.4.4                                (      .      .
138251881Speter *    branches/1.4.5                             ======]      .
139251881Speter *                                                     .      .
140251881Speter *    branches/1.4.5                                   (      .
141251881Speter *    tags/1.4.5                                        ======]
142251881Speter *
143251881Speter * which are represented in merge_source_t as:
144251881Speter *
145251881Speter *    [/trunk:859500, /trunk:859597]
146251881Speter *    (recorded in svn:mergeinfo as /trunk:859501-859597)
147251881Speter *
148251881Speter *    [/trunk:859597, /branches/1.4.x:865262]
149251881Speter *    (recorded in svn:mergeinfo as /branches/1.4.x:859598-865262)
150251881Speter *
151251881Speter *    [/branches/1.4.x:865262, /tags/1.4.4@866419]
152251881Speter *    (recorded in svn:mergeinfo as /tags/1.4.4:865263-866419)
153251881Speter *    (and there is a gap, the revision range [865262, 865416])
154251881Speter *
155251881Speter *    [/tags/1.4.4@866419, /branches/1.4.5@866424]
156251881Speter *    (recorded in svn:mergeinfo as /branches/1.4.5:866420-866424)
157251881Speter *
158251881Speter *    [/branches/1.4.5@866424, /tags/1.4.5@866500]
159251881Speter *    (recorded in svn:mergeinfo as /tags/1.4.5:866425-866500)
160251881Speter *
161251881Speter * Our helper functions would then operate on one of these location
162251881Speter * pairs at a time.
163251881Speter */
164251881Speter
165251881Speter/* WHICH SVN_CLIENT_MERGE* API DO I WANT?
166251881Speter *
167251881Speter * libsvn_client has three public merge APIs; they are all wrappers
168251881Speter * around the do_merge engine.  Which one to use depends on the number
169251881Speter * of URLs passed as arguments and whether or not specific merge
170251881Speter * ranges (-c/-r) are specified.
171251881Speter *
172251881Speter *                 1 URL                        2 URLs
173251881Speter * +----+--------------------------------+---------------------+
174251881Speter * | -c |       mergeinfo-driven         |                     |
175251881Speter * | or |        cherrypicking           |                     |
176251881Speter * | -r |    (svn_client_merge_peg)      |                     |
177251881Speter * |----+--------------------------------+                     |
178251881Speter * |    |       mergeinfo-driven         |     unsupported     |
179251881Speter * |    |  'cherry harvest', i.e. merge  |                     |
180251881Speter * |    |  all revisions from URL that   |                     |
181251881Speter * | no |  have not already been merged  |                     |
182251881Speter * | -c |    (svn_client_merge_peg)      |                     |
183251881Speter * | or +--------------------------------+---------------------+
184251881Speter * | -r |      mergeinfo-driven          |   mergeinfo-writing |
185251881Speter * |    |        whole-branch            |    diff-and-apply   |
186251881Speter * |    |       heuristic merge          |  (svn_client_merge) |
187251881Speter * |    | (svn_client_merge_reintegrate) |                     |
188251881Speter * +----+--------------------------------+---------------------+
189251881Speter *
190251881Speter *
191251881Speter */
192251881Speter
193251881Speter/* THE CHILDREN_WITH_MERGEINFO ARRAY
194251881Speter *
195251881Speter * Many of the helper functions in this file pass around an
196251881Speter * apr_array_header_t *CHILDREN_WITH_MERGEINFO.  This is a depth first
197251881Speter * sorted array filled with svn_client__merge_path_t * describing the
198251881Speter * merge target and any of its subtrees which have explicit mergeinfo
199251881Speter * or otherwise need special attention during a merge.
200251881Speter *
201251881Speter * During mergeinfo unaware merges, CHILDREN_WITH_MERGEINFO contains
202251881Speter * contains only one element (added by do_mergeinfo_unaware_dir_merge)
203251881Speter * describing a contiguous range to be merged to the WC merge target.
204251881Speter *
205251881Speter * During mergeinfo aware merges CHILDREN_WITH_MERGEINFO is created
206251881Speter * by get_mergeinfo_paths() and outside of that function and its helpers
207251881Speter * should always meet the criteria dictated in get_mergeinfo_paths()'s doc
208251881Speter * string.  The elements of CHILDREN_WITH_MERGEINFO should never be NULL.
209251881Speter *
210251881Speter * For clarification on mergeinfo aware vs. mergeinfo unaware merges, see
211251881Speter * the doc string for HONOR_MERGEINFO().
212251881Speter */
213251881Speter
214251881Speter
215251881Speter/*-----------------------------------------------------------------------*/
216251881Speter
217251881Speter/*** Repos-Diff Editor Callbacks ***/
218251881Speter
219251881Speter/* */
220251881Spetertypedef struct merge_source_t
221251881Speter{
222251881Speter  /* "left" side URL and revision (inclusive iff youngest) */
223251881Speter  const svn_client__pathrev_t *loc1;
224251881Speter
225251881Speter  /* "right" side URL and revision (inclusive iff youngest) */
226251881Speter  const svn_client__pathrev_t *loc2;
227251881Speter
228251881Speter  /* True iff LOC1 is an ancestor of LOC2 or vice-versa (history-wise). */
229251881Speter  svn_boolean_t ancestral;
230251881Speter} merge_source_t;
231251881Speter
232251881Speter/* Description of the merge target root node (a WC working node) */
233251881Spetertypedef struct merge_target_t
234251881Speter{
235251881Speter  /* Absolute path to the WC node */
236251881Speter  const char *abspath;
237251881Speter
238251881Speter  /* The repository location of the base node of the target WC.  If the node
239251881Speter   * is locally added, then URL & REV are NULL & SVN_INVALID_REVNUM.
240251881Speter   * REPOS_ROOT_URL and REPOS_UUID are always valid. */
241251881Speter  svn_client__pathrev_t loc;
242251881Speter
243251881Speter} merge_target_t;
244251881Speter
245251881Spetertypedef struct merge_cmd_baton_t {
246251881Speter  svn_boolean_t force_delete;         /* Delete a file/dir even if modified */
247251881Speter  svn_boolean_t dry_run;
248251881Speter  svn_boolean_t record_only;          /* Whether to merge only mergeinfo
249251881Speter                                         differences. */
250251881Speter  svn_boolean_t same_repos;           /* Whether the merge source repository
251251881Speter                                         is the same repository as the
252251881Speter                                         target.  Defaults to FALSE if DRY_RUN
253251881Speter                                         is TRUE.*/
254251881Speter  svn_boolean_t mergeinfo_capable;    /* Whether the merge source server
255251881Speter                                         is capable of Merge Tracking. */
256251881Speter  svn_boolean_t ignore_mergeinfo;     /* Don't honor mergeinfo; see
257251881Speter                                         doc string of do_merge().  FALSE if
258251881Speter                                         MERGE_SOURCE->ancestral is FALSE. */
259251881Speter  svn_boolean_t diff_ignore_ancestry; /* Diff unrelated nodes as if related; see
260251881Speter                                         doc string of do_merge().  FALSE if
261251881Speter                                         MERGE_SOURCE->ancestral is FALSE. */
262251881Speter  svn_boolean_t reintegrate_merge;    /* Whether this is a --reintegrate
263251881Speter                                         merge or not. */
264251881Speter  const merge_target_t *target;       /* Description of merge target node */
265251881Speter
266251881Speter  /* The left and right URLs and revs.  The value of this field changes to
267251881Speter     reflect the merge_source_t *currently* being merged by do_merge(). */
268251881Speter  merge_source_t merge_source;
269251881Speter
270251881Speter  /* Rangelist containing single range which describes the gap, if any,
271251881Speter     in the natural history of the merge source currently being processed.
272251881Speter     See http://subversion.tigris.org/issues/show_bug.cgi?id=3432.
273251881Speter     Updated during each call to do_directory_merge().  May be NULL if there
274251881Speter     is no gap. */
275251881Speter  svn_rangelist_t *implicit_src_gap;
276251881Speter
277251881Speter  svn_client_ctx_t *ctx;              /* Client context for callbacks, etc. */
278251881Speter
279251881Speter  /* The list of any paths which remained in conflict after a
280251881Speter     resolution attempt was made.  We track this in-memory, rather
281251881Speter     than just using WC entry state, since the latter doesn't help us
282251881Speter     when in dry_run mode.
283251881Speter     ### And because we only want to resolve conflicts that were
284251881Speter         generated by this merge, not pre-existing ones? */
285251881Speter  apr_hash_t *conflicted_paths;
286251881Speter
287251881Speter  /* A list of absolute paths which had no explicit mergeinfo prior to the
288251881Speter     merge but got explicit mergeinfo added by the merge.  This is populated
289251881Speter     by merge_change_props() and is allocated in POOL so it is subject to the
290251881Speter     lifetime limitations of POOL.  Is NULL if no paths are found which
291251881Speter     meet the criteria or DRY_RUN is true. */
292251881Speter  apr_hash_t *paths_with_new_mergeinfo;
293251881Speter
294251881Speter  /* A list of absolute paths whose mergeinfo doesn't need updating after
295251881Speter     the merge. This can be caused by the removal of mergeinfo by the merge
296251881Speter     or by deleting the node itself.  This is populated by merge_change_props()
297251881Speter     and the delete callbacks and is allocated in POOL so it is subject to the
298251881Speter     lifetime limitations of POOL.  Is NULL if no paths are found which
299251881Speter     meet the criteria or DRY_RUN is true. */
300251881Speter  apr_hash_t *paths_with_deleted_mergeinfo;
301251881Speter
302251881Speter  /* The list of absolute skipped paths, which should be examined and
303251881Speter     cleared after each invocation of the callback.  The paths
304251881Speter     are absolute.  Is NULL if MERGE_B->MERGE_SOURCE->ancestral and
305251881Speter     MERGE_B->REINTEGRATE_MERGE are both false. */
306251881Speter  apr_hash_t *skipped_abspaths;
307251881Speter
308251881Speter  /* The list of absolute merged paths.  Unused if MERGE_B->MERGE_SOURCE->ancestral
309251881Speter     and MERGE_B->REINTEGRATE_MERGE are both false. */
310251881Speter  apr_hash_t *merged_abspaths;
311251881Speter
312251881Speter  /* A hash of (const char *) absolute WC paths mapped to the same which
313251881Speter     represent the roots of subtrees added by the merge. */
314251881Speter  apr_hash_t *added_abspaths;
315251881Speter
316251881Speter  /* A list of tree conflict victim absolute paths which may be NULL. */
317251881Speter  apr_hash_t *tree_conflicted_abspaths;
318251881Speter
319251881Speter  /* The diff3_cmd in ctx->config, if any, else null.  We could just
320251881Speter     extract this as needed, but since more than one caller uses it,
321251881Speter     we just set it up when this baton is created. */
322251881Speter  const char *diff3_cmd;
323251881Speter  const apr_array_header_t *merge_options;
324251881Speter
325251881Speter  /* RA sessions used throughout a merge operation.  Opened/re-parented
326251881Speter     as needed.
327251881Speter
328251881Speter     NOTE: During the actual merge editor drive, RA_SESSION1 is used
329251881Speter     for the primary editing and RA_SESSION2 for fetching additional
330251881Speter     information -- as necessary -- from the repository.  So during
331251881Speter     this phase of the merge, you *must not* reparent RA_SESSION1; use
332251881Speter     (temporarily reparenting if you must) RA_SESSION2 instead.  */
333251881Speter  svn_ra_session_t *ra_session1;
334251881Speter  svn_ra_session_t *ra_session2;
335251881Speter
336251881Speter  /* During the merge, *USE_SLEEP is set to TRUE if a sleep will be required
337251881Speter     afterwards to ensure timestamp integrity, or unchanged if not. */
338251881Speter  svn_boolean_t *use_sleep;
339251881Speter
340251881Speter  /* Pool which has a lifetime limited to one iteration over a given
341251881Speter     merge source, i.e. it is cleared on every call to do_directory_merge()
342251881Speter     or do_file_merge() in do_merge(). */
343251881Speter  apr_pool_t *pool;
344251881Speter
345251881Speter
346251881Speter  /* State for notify_merge_begin() */
347251881Speter  struct notify_begin_state_t
348251881Speter  {
349251881Speter    /* Cache of which abspath was last notified. */
350251881Speter    const char *last_abspath;
351251881Speter
352251881Speter    /* Reference to the one-and-only CHILDREN_WITH_MERGEINFO (see global
353251881Speter       comment) or a similar list for single-file-merges */
354251881Speter    const apr_array_header_t *nodes_with_mergeinfo;
355251881Speter  } notify_begin;
356251881Speter
357251881Speter} merge_cmd_baton_t;
358251881Speter
359251881Speter
360251881Speter/* Return TRUE iff we should be taking account of mergeinfo in deciding what
361251881Speter   changes to merge, for the merge described by MERGE_B.  Specifically, that
362251881Speter   is if the merge source server is capable of merge tracking, the left-side
363251881Speter   merge source is an ancestor of the right-side (or vice-versa), the merge
364251881Speter   source is in the same repository as the merge target, and we are not
365251881Speter   ignoring mergeinfo. */
366251881Speter#define HONOR_MERGEINFO(merge_b) ((merge_b)->mergeinfo_capable      \
367251881Speter                                  && (merge_b)->merge_source.ancestral  \
368251881Speter                                  && (merge_b)->same_repos          \
369251881Speter                                  && (! (merge_b)->ignore_mergeinfo))
370251881Speter
371251881Speter
372251881Speter/* Return TRUE iff we should be recording mergeinfo for the merge described
373251881Speter   by MERGE_B.  Specifically, that is if we are honoring mergeinfo and the
374251881Speter   merge is not a dry run.  */
375251881Speter#define RECORD_MERGEINFO(merge_b) (HONOR_MERGEINFO(merge_b) \
376251881Speter                                   && !(merge_b)->dry_run)
377251881Speter
378251881Speter
379251881Speter/*-----------------------------------------------------------------------*/
380251881Speter
381251881Speter/*** Utilities ***/
382251881Speter
383251881Speter/* Return TRUE iff the session URL of RA_SESSION is equal to URL.  Useful in
384251881Speter * asserting preconditions. */
385251881Speterstatic svn_boolean_t
386251881Spetersession_url_is(svn_ra_session_t *ra_session,
387251881Speter               const char *url,
388251881Speter               apr_pool_t *scratch_pool)
389251881Speter{
390251881Speter  const char *session_url;
391251881Speter  svn_error_t *err
392251881Speter    = svn_ra_get_session_url(ra_session, &session_url, scratch_pool);
393251881Speter
394251881Speter  SVN_ERR_ASSERT_NO_RETURN(! err);
395251881Speter  return strcmp(url, session_url) == 0;
396251881Speter}
397251881Speter
398251881Speter/* Return a new merge_source_t structure, allocated in RESULT_POOL,
399251881Speter * initialized with deep copies of LOC1 and LOC2 and ANCESTRAL. */
400251881Speterstatic merge_source_t *
401251881Spetermerge_source_create(const svn_client__pathrev_t *loc1,
402251881Speter                    const svn_client__pathrev_t *loc2,
403251881Speter                    svn_boolean_t ancestral,
404251881Speter                    apr_pool_t *result_pool)
405251881Speter{
406251881Speter  merge_source_t *s
407251881Speter    = apr_palloc(result_pool, sizeof(*s));
408251881Speter
409251881Speter  s->loc1 = svn_client__pathrev_dup(loc1, result_pool);
410251881Speter  s->loc2 = svn_client__pathrev_dup(loc2, result_pool);
411251881Speter  s->ancestral = ancestral;
412251881Speter  return s;
413251881Speter}
414251881Speter
415251881Speter/* Return a deep copy of SOURCE, allocated in RESULT_POOL. */
416251881Speterstatic merge_source_t *
417251881Spetermerge_source_dup(const merge_source_t *source,
418251881Speter                 apr_pool_t *result_pool)
419251881Speter{
420251881Speter  merge_source_t *s = apr_palloc(result_pool, sizeof(*s));
421251881Speter
422251881Speter  s->loc1 = svn_client__pathrev_dup(source->loc1, result_pool);
423251881Speter  s->loc2 = svn_client__pathrev_dup(source->loc2, result_pool);
424251881Speter  s->ancestral = source->ancestral;
425251881Speter  return s;
426251881Speter}
427251881Speter
428251881Speter/* Return SVN_ERR_UNSUPPORTED_FEATURE if URL is not inside the repository
429251881Speter   of LOCAL_ABSPATH.  Use SCRATCH_POOL for temporary allocations. */
430251881Speterstatic svn_error_t *
431251881Spetercheck_repos_match(const merge_target_t *target,
432251881Speter                  const char *local_abspath,
433251881Speter                  const char *url,
434251881Speter                  apr_pool_t *scratch_pool)
435251881Speter{
436251881Speter  if (!svn_uri__is_ancestor(target->loc.repos_root_url, url))
437251881Speter    return svn_error_createf(
438251881Speter        SVN_ERR_UNSUPPORTED_FEATURE, NULL,
439251881Speter         _("URL '%s' of '%s' is not in repository '%s'"),
440251881Speter         url, svn_dirent_local_style(local_abspath, scratch_pool),
441251881Speter         target->loc.repos_root_url);
442251881Speter
443251881Speter  return SVN_NO_ERROR;
444251881Speter}
445251881Speter
446251881Speter/* Return TRUE iff the repository of LOCATION1 is the same as
447251881Speter * that of LOCATION2.  If STRICT_URLS is true, the URLs must
448251881Speter * match (and the UUIDs, just to be sure), otherwise just the UUIDs must
449251881Speter * match and the URLs can differ (a common case is http versus https). */
450251881Speterstatic svn_boolean_t
451251881Speteris_same_repos(const svn_client__pathrev_t *location1,
452251881Speter              const svn_client__pathrev_t *location2,
453251881Speter              svn_boolean_t strict_urls)
454251881Speter{
455251881Speter  if (strict_urls)
456251881Speter    return (strcmp(location1->repos_root_url, location2->repos_root_url) == 0
457251881Speter            && strcmp(location1->repos_uuid, location2->repos_uuid) == 0);
458251881Speter  else
459251881Speter    return (strcmp(location1->repos_uuid, location2->repos_uuid) == 0);
460251881Speter}
461251881Speter
462251881Speter/* If the repository identified of LOCATION1 is not the same as that
463251881Speter * of LOCATION2, throw a SVN_ERR_CLIENT_UNRELATED_RESOURCES
464251881Speter * error mentioning PATH1 and PATH2. For STRICT_URLS, see is_same_repos().
465251881Speter */
466251881Speterstatic svn_error_t *
467251881Spetercheck_same_repos(const svn_client__pathrev_t *location1,
468251881Speter                 const char *path1,
469251881Speter                 const svn_client__pathrev_t *location2,
470251881Speter                 const char *path2,
471251881Speter                 svn_boolean_t strict_urls,
472251881Speter                 apr_pool_t *scratch_pool)
473251881Speter{
474251881Speter  if (! is_same_repos(location1, location2, strict_urls))
475251881Speter    return svn_error_createf(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
476251881Speter                             _("'%s' must be from the same repository as "
477251881Speter                               "'%s'"), path1, path2);
478251881Speter  return SVN_NO_ERROR;
479251881Speter}
480251881Speter
481251881Speter/* Store LOCAL_ABSPATH in PATH_HASH after duplicating it into the pool
482251881Speter   containing PATH_HASH. */
483251881Speterstatic APR_INLINE void
484251881Speterstore_path(apr_hash_t *path_hash, const char *local_abspath)
485251881Speter{
486251881Speter  const char *dup_path = apr_pstrdup(apr_hash_pool_get(path_hash),
487251881Speter                                     local_abspath);
488251881Speter
489251881Speter  svn_hash_sets(path_hash, dup_path, dup_path);
490251881Speter}
491251881Speter
492251881Speter/* Store LOCAL_ABSPATH in *PATH_HASH_P after duplicating it into the pool
493251881Speter   containing *PATH_HASH_P.  If *PATH_HASH_P is NULL, then first set
494251881Speter   *PATH_HASH_P to a new hash allocated from POOL.  */
495251881Speterstatic APR_INLINE void
496251881Speteralloc_and_store_path(apr_hash_t **path_hash_p,
497251881Speter                     const char *local_abspath,
498251881Speter                     apr_pool_t *pool)
499251881Speter{
500251881Speter  if (! *path_hash_p)
501251881Speter    *path_hash_p = apr_hash_make(pool);
502251881Speter  store_path(*path_hash_p, local_abspath);
503251881Speter}
504251881Speter
505251881Speter/* Return whether any WC path was put in conflict by the merge
506251881Speter   operation corresponding to MERGE_B. */
507251881Speterstatic APR_INLINE svn_boolean_t
508251881Speteris_path_conflicted_by_merge(merge_cmd_baton_t *merge_b)
509251881Speter{
510251881Speter  return (merge_b->conflicted_paths &&
511251881Speter          apr_hash_count(merge_b->conflicted_paths) > 0);
512251881Speter}
513251881Speter
514251881Speter/* Return a state indicating whether the WC metadata matches the
515251881Speter * node kind on disk of the local path LOCAL_ABSPATH.
516251881Speter * Use MERGE_B to determine the dry-run details; particularly, if a dry run
517251881Speter * noted that it deleted this path, assume matching node kinds (as if both
518251881Speter * kinds were svn_node_none).
519251881Speter *
520251881Speter *   - Return svn_wc_notify_state_inapplicable if the node kind matches.
521251881Speter *   - Return 'obstructed' if there is a node on disk where none or a
522251881Speter *     different kind is expected, or if the disk node cannot be read.
523251881Speter *   - Return 'missing' if there is no node on disk but one is expected.
524251881Speter *     Also return 'missing' for server-excluded nodes (not here due to
525251881Speter *     authz or other reasons determined by the server).
526251881Speter *
527251881Speter * Optionally return a bit more info for interested users.
528251881Speter **/
529251881Speterstatic svn_error_t *
530251881Speterperform_obstruction_check(svn_wc_notify_state_t *obstruction_state,
531251881Speter                          svn_boolean_t *deleted,
532251881Speter                          svn_boolean_t *excluded,
533251881Speter                          svn_node_kind_t *kind,
534251881Speter                          svn_depth_t *parent_depth,
535251881Speter                          const merge_cmd_baton_t *merge_b,
536251881Speter                          const char *local_abspath,
537251881Speter                          apr_pool_t *scratch_pool)
538251881Speter{
539251881Speter  svn_wc_context_t *wc_ctx = merge_b->ctx->wc_ctx;
540251881Speter  svn_node_kind_t wc_kind;
541251881Speter  svn_boolean_t check_root;
542251881Speter
543251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
544251881Speter
545251881Speter  *obstruction_state = svn_wc_notify_state_inapplicable;
546251881Speter
547251881Speter  if (deleted)
548251881Speter    *deleted = FALSE;
549251881Speter  if (kind)
550251881Speter    *kind = svn_node_none;
551251881Speter
552251881Speter  if (kind == NULL)
553251881Speter    kind = &wc_kind;
554251881Speter
555251881Speter  check_root = ! strcmp(local_abspath, merge_b->target->abspath);
556251881Speter
557251881Speter  SVN_ERR(svn_wc__check_for_obstructions(obstruction_state,
558251881Speter                                         kind,
559251881Speter                                         deleted,
560251881Speter                                         excluded,
561251881Speter                                         parent_depth,
562251881Speter                                         wc_ctx, local_abspath,
563251881Speter                                         check_root,
564251881Speter                                         scratch_pool));
565251881Speter  return SVN_NO_ERROR;
566251881Speter}
567251881Speter
568251881Speter/* Create *LEFT and *RIGHT conflict versions for conflict victim
569251881Speter * at VICTIM_ABSPATH, with kind NODE_KIND, using information obtained
570251881Speter * from MERGE_SOURCE and TARGET.
571251881Speter * Allocate returned conflict versions in RESULT_POOL. */
572251881Speterstatic svn_error_t *
573251881Spetermake_conflict_versions(const svn_wc_conflict_version_t **left,
574251881Speter                       const svn_wc_conflict_version_t **right,
575251881Speter                       const char *victim_abspath,
576251881Speter                       svn_node_kind_t node_kind,
577251881Speter                       const merge_source_t *merge_source,
578251881Speter                       const merge_target_t *target,
579251881Speter                       apr_pool_t *result_pool,
580251881Speter                       apr_pool_t *scratch_pool)
581251881Speter{
582251881Speter  const char *child = svn_dirent_skip_ancestor(target->abspath,
583251881Speter                                               victim_abspath);
584251881Speter  const char *left_relpath, *right_relpath;
585251881Speter
586251881Speter  SVN_ERR_ASSERT(child != NULL);
587251881Speter  left_relpath = svn_client__pathrev_relpath(merge_source->loc1,
588251881Speter                                             scratch_pool);
589251881Speter  right_relpath = svn_client__pathrev_relpath(merge_source->loc2,
590251881Speter                                              scratch_pool);
591251881Speter
592251881Speter  *left = svn_wc_conflict_version_create2(
593251881Speter            merge_source->loc1->repos_root_url,
594251881Speter            merge_source->loc1->repos_uuid,
595251881Speter            svn_relpath_join(left_relpath, child, scratch_pool),
596251881Speter            merge_source->loc1->rev, node_kind, result_pool);
597251881Speter
598251881Speter  *right = svn_wc_conflict_version_create2(
599251881Speter             merge_source->loc2->repos_root_url,
600251881Speter             merge_source->loc2->repos_uuid,
601251881Speter             svn_relpath_join(right_relpath, child, scratch_pool),
602251881Speter             merge_source->loc2->rev, node_kind, result_pool);
603251881Speter
604251881Speter  return SVN_NO_ERROR;
605251881Speter}
606251881Speter
607251881Speter/* Helper for filter_self_referential_mergeinfo()
608251881Speter
609251881Speter   *MERGEINFO is a non-empty, non-null collection of mergeinfo.
610251881Speter
611251881Speter   Remove all mergeinfo from *MERGEINFO that describes revision ranges
612251881Speter   greater than REVISION.  Put a copy of any removed mergeinfo, allocated
613251881Speter   in POOL, into *YOUNGER_MERGEINFO.
614251881Speter
615251881Speter   If no mergeinfo is removed from *MERGEINFO then *YOUNGER_MERGEINFO is set
616251881Speter   to NULL.  If all mergeinfo is removed from *MERGEINFO then *MERGEINFO is
617251881Speter   set to NULL.
618251881Speter   */
619251881Speterstatic svn_error_t*
620251881Spetersplit_mergeinfo_on_revision(svn_mergeinfo_t *younger_mergeinfo,
621251881Speter                            svn_mergeinfo_t *mergeinfo,
622251881Speter                            svn_revnum_t revision,
623251881Speter                            apr_pool_t *pool)
624251881Speter{
625251881Speter  apr_hash_index_t *hi;
626251881Speter  apr_pool_t *iterpool = svn_pool_create(pool);
627251881Speter
628251881Speter  *younger_mergeinfo = NULL;
629251881Speter  for (hi = apr_hash_first(pool, *mergeinfo); hi; hi = apr_hash_next(hi))
630251881Speter    {
631251881Speter      int i;
632251881Speter      const char *merge_source_path = svn__apr_hash_index_key(hi);
633251881Speter      svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
634251881Speter
635251881Speter      svn_pool_clear(iterpool);
636251881Speter
637251881Speter      for (i = 0; i < rangelist->nelts; i++)
638251881Speter        {
639251881Speter          svn_merge_range_t *range =
640251881Speter            APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
641251881Speter          if (range->end <= revision)
642251881Speter            {
643251881Speter              /* This entirely of this range is as old or older than
644251881Speter                 REVISION, so leave it in *MERGEINFO. */
645251881Speter              continue;
646251881Speter            }
647251881Speter          else
648251881Speter            {
649251881Speter              /* Since the rangelists in svn_mergeinfo_t's are sorted in
650251881Speter                 increasing order we know that part or all of *this* range
651251881Speter                 and *all* of the remaining ranges in *RANGELIST are younger
652251881Speter                 than REVISION.  Remove the younger rangelists from
653251881Speter                 *MERGEINFO and put them in *YOUNGER_MERGEINFO. */
654251881Speter              int j;
655251881Speter              svn_rangelist_t *younger_rangelist =
656251881Speter                apr_array_make(pool, 1, sizeof(svn_merge_range_t *));
657251881Speter
658251881Speter              for (j = i; j < rangelist->nelts; j++)
659251881Speter                {
660251881Speter                  svn_merge_range_t *younger_range = svn_merge_range_dup(
661251881Speter                    APR_ARRAY_IDX(rangelist, j, svn_merge_range_t *), pool);
662251881Speter
663251881Speter                  /* REVISION might intersect with the first range where
664251881Speter                     range->end > REVISION.  If that is the case then split
665251881Speter                     the current range into two, putting the younger half
666251881Speter                     into *YOUNGER_MERGEINFO and leaving the older half in
667251881Speter                     *MERGEINFO. */
668251881Speter                  if (j == i && range->start + 1 <= revision)
669251881Speter                    younger_range->start = range->end = revision;
670251881Speter
671251881Speter                  APR_ARRAY_PUSH(younger_rangelist, svn_merge_range_t *) =
672251881Speter                    younger_range;
673251881Speter                }
674251881Speter
675251881Speter              /* So far we've only been manipulating rangelists, now we
676251881Speter                 actually create *YOUNGER_MERGEINFO and then remove the older
677251881Speter                 ranges from *MERGEINFO */
678251881Speter              if (!(*younger_mergeinfo))
679251881Speter                *younger_mergeinfo = apr_hash_make(pool);
680251881Speter              svn_hash_sets(*younger_mergeinfo, merge_source_path,
681251881Speter                            younger_rangelist);
682251881Speter              SVN_ERR(svn_mergeinfo_remove2(mergeinfo, *younger_mergeinfo,
683251881Speter                                            *mergeinfo, TRUE, pool, iterpool));
684251881Speter              break; /* ...out of for (i = 0; i < rangelist->nelts; i++) */
685251881Speter            }
686251881Speter        }
687251881Speter    }
688251881Speter
689251881Speter  svn_pool_destroy(iterpool);
690251881Speter
691251881Speter  return SVN_NO_ERROR;
692251881Speter}
693251881Speter
694251881Speter
695251881Speter/* Make a copy of PROPCHANGES (array of svn_prop_t) into *TRIMMED_PROPCHANGES,
696251881Speter   omitting any svn:mergeinfo changes.  */
697251881Speterstatic svn_error_t *
698251881Speteromit_mergeinfo_changes(apr_array_header_t **trimmed_propchanges,
699251881Speter                       const apr_array_header_t *propchanges,
700251881Speter                       apr_pool_t *result_pool)
701251881Speter{
702251881Speter  int i;
703251881Speter
704251881Speter  *trimmed_propchanges = apr_array_make(result_pool,
705251881Speter                                        propchanges->nelts,
706251881Speter                                        sizeof(svn_prop_t));
707251881Speter
708251881Speter  for (i = 0; i < propchanges->nelts; ++i)
709251881Speter    {
710251881Speter      const svn_prop_t *change = &APR_ARRAY_IDX(propchanges, i, svn_prop_t);
711251881Speter
712251881Speter      /* If this property is not svn:mergeinfo, then copy it.  */
713251881Speter      if (strcmp(change->name, SVN_PROP_MERGEINFO) != 0)
714251881Speter        APR_ARRAY_PUSH(*trimmed_propchanges, svn_prop_t) = *change;
715251881Speter    }
716251881Speter
717251881Speter  return SVN_NO_ERROR;
718251881Speter}
719251881Speter
720251881Speter
721251881Speter/* Helper for merge_props_changed().
722251881Speter
723251881Speter   *PROPS is an array of svn_prop_t structures representing regular properties
724251881Speter   to be added to the working copy TARGET_ABSPATH.
725251881Speter
726251881Speter   The merge source and target are assumed to be in the same repository.
727251881Speter
728251881Speter   Filter out mergeinfo property additions to TARGET_ABSPATH when
729251881Speter   those additions refer to the same line of history as TARGET_ABSPATH as
730251881Speter   described below.
731251881Speter
732251881Speter   Examine the added mergeinfo, looking at each range (or single rev)
733251881Speter   of each source path.  If a source_path/range refers to the same line of
734251881Speter   history as TARGET_ABSPATH (pegged at its base revision), then filter out
735251881Speter   that range.  If the entire rangelist for a given path is filtered then
736251881Speter   filter out the path as well.
737251881Speter
738251881Speter   RA_SESSION is an open RA session to the repository
739251881Speter   in which both the source and target live, else RA_SESSION is not used. It
740251881Speter   may be temporarily reparented as needed by this function.
741251881Speter
742251881Speter   Use CTX for any further client operations.
743251881Speter
744251881Speter   If any filtering occurs, set outgoing *PROPS to a shallow copy (allocated
745251881Speter   in POOL) of incoming *PROPS minus the filtered mergeinfo. */
746251881Speterstatic svn_error_t *
747251881Speterfilter_self_referential_mergeinfo(apr_array_header_t **props,
748251881Speter                                  const char *target_abspath,
749251881Speter                                  svn_ra_session_t *ra_session,
750251881Speter                                  svn_client_ctx_t *ctx,
751251881Speter                                  apr_pool_t *pool)
752251881Speter{
753251881Speter  apr_array_header_t *adjusted_props;
754251881Speter  int i;
755251881Speter  apr_pool_t *iterpool;
756251881Speter  svn_boolean_t is_copy;
757251881Speter  const char *repos_relpath;
758251881Speter  svn_client__pathrev_t target_base;
759251881Speter
760251881Speter  /* If PATH itself has been added there is no need to filter. */
761251881Speter  SVN_ERR(svn_wc__node_get_origin(&is_copy,  &target_base.rev, &repos_relpath,
762251881Speter                                  &target_base.repos_root_url,
763251881Speter                                  &target_base.repos_uuid, NULL,
764251881Speter                                  ctx->wc_ctx, target_abspath, FALSE,
765251881Speter                                  pool, pool));
766251881Speter
767251881Speter  if (is_copy || !repos_relpath)
768251881Speter    return SVN_NO_ERROR; /* A copy or a local addition */
769251881Speter
770251881Speter  target_base.url = svn_path_url_add_component2(target_base.repos_root_url,
771251881Speter                                                repos_relpath, pool);
772251881Speter
773251881Speter  adjusted_props = apr_array_make(pool, (*props)->nelts, sizeof(svn_prop_t));
774251881Speter  iterpool = svn_pool_create(pool);
775251881Speter  for (i = 0; i < (*props)->nelts; ++i)
776251881Speter    {
777251881Speter      svn_prop_t *prop = &APR_ARRAY_IDX((*props), i, svn_prop_t);
778251881Speter
779251881Speter      svn_mergeinfo_t mergeinfo, younger_mergeinfo;
780251881Speter      svn_mergeinfo_t filtered_mergeinfo = NULL;
781251881Speter      svn_mergeinfo_t filtered_younger_mergeinfo = NULL;
782251881Speter      svn_error_t *err;
783251881Speter
784251881Speter      /* If this property isn't mergeinfo or is NULL valued (i.e. prop removal)
785251881Speter         or empty mergeinfo it does not require any special handling.  There
786251881Speter         is nothing to filter out of empty mergeinfo and the concept of
787251881Speter         filtering doesn't apply if we are trying to remove mergeinfo
788251881Speter         entirely.  */
789251881Speter      if ((strcmp(prop->name, SVN_PROP_MERGEINFO) != 0)
790251881Speter          || (! prop->value)       /* Removal of mergeinfo */
791251881Speter          || (! prop->value->len)) /* Empty mergeinfo */
792251881Speter        {
793251881Speter          APR_ARRAY_PUSH(adjusted_props, svn_prop_t) = *prop;
794251881Speter          continue;
795251881Speter        }
796251881Speter
797251881Speter      svn_pool_clear(iterpool);
798251881Speter
799251881Speter      /* Non-empty mergeinfo; filter self-referential mergeinfo out. */
800251881Speter
801251881Speter      /* Parse the incoming mergeinfo to allow easier manipulation. */
802251881Speter      err = svn_mergeinfo_parse(&mergeinfo, prop->value->data, iterpool);
803251881Speter
804251881Speter      if (err)
805251881Speter        {
806251881Speter          /* Issue #3896: If we can't parse it, we certainly can't
807251881Speter             filter it. */
808251881Speter          if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
809251881Speter            {
810251881Speter              svn_error_clear(err);
811251881Speter              APR_ARRAY_PUSH(adjusted_props, svn_prop_t) = *prop;
812251881Speter              continue;
813251881Speter            }
814251881Speter          else
815251881Speter            {
816251881Speter              return svn_error_trace(err);
817251881Speter            }
818251881Speter        }
819251881Speter
820251881Speter      /* The working copy target PATH is at BASE_REVISION.  Divide the
821251881Speter         incoming mergeinfo into two groups.  One where all revision ranges
822251881Speter         are as old or older than BASE_REVISION and one where all revision
823251881Speter         ranges are younger.
824251881Speter
825251881Speter         Note: You may be wondering why we do this.
826251881Speter
827251881Speter         For the incoming mergeinfo "older" than target's base revision we
828251881Speter         can filter out self-referential mergeinfo efficiently using
829251881Speter         svn_client__get_history_as_mergeinfo().  We simply look at PATH's
830251881Speter         natural history as mergeinfo and remove that from any incoming
831251881Speter         mergeinfo.
832251881Speter
833251881Speter         For mergeinfo "younger" than the base revision we can't use
834251881Speter         svn_ra_get_location_segments() to look into PATH's future
835251881Speter         history.  Instead we must use svn_client__repos_locations() and
836251881Speter         look at each incoming source/range individually and see if PATH
837251881Speter         at its base revision and PATH at the start of the incoming range
838251881Speter         exist on the same line of history.  If they do then we can filter
839251881Speter         out the incoming range.  But since we have to do this for each
840251881Speter         range there is a substantial performance penalty to pay if the
841251881Speter         incoming ranges are not contiguous, i.e. we call
842251881Speter         svn_client__repos_locations for each discrete range and incur
843251881Speter         the cost of a roundtrip communication with the repository. */
844251881Speter      SVN_ERR(split_mergeinfo_on_revision(&younger_mergeinfo,
845251881Speter                                          &mergeinfo,
846251881Speter                                          target_base.rev,
847251881Speter                                          iterpool));
848251881Speter
849251881Speter      /* Filter self-referential mergeinfo from younger_mergeinfo. */
850251881Speter      if (younger_mergeinfo)
851251881Speter        {
852251881Speter          apr_hash_index_t *hi;
853251881Speter          const char *merge_source_root_url;
854251881Speter
855251881Speter          SVN_ERR(svn_ra_get_repos_root2(ra_session,
856251881Speter                                         &merge_source_root_url, iterpool));
857251881Speter
858251881Speter          for (hi = apr_hash_first(iterpool, younger_mergeinfo);
859251881Speter               hi; hi = apr_hash_next(hi))
860251881Speter            {
861251881Speter              int j;
862251881Speter              const char *source_path = svn__apr_hash_index_key(hi);
863251881Speter              svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
864251881Speter              const char *merge_source_url;
865251881Speter              svn_rangelist_t *adjusted_rangelist =
866251881Speter                apr_array_make(iterpool, 0, sizeof(svn_merge_range_t *));
867251881Speter
868251881Speter              merge_source_url =
869251881Speter                    svn_path_url_add_component2(merge_source_root_url,
870251881Speter                                                source_path + 1, iterpool);
871251881Speter
872251881Speter              for (j = 0; j < rangelist->nelts; j++)
873251881Speter                {
874251881Speter                  svn_error_t *err2;
875251881Speter                  svn_client__pathrev_t *start_loc;
876251881Speter                  svn_merge_range_t *range =
877251881Speter                    APR_ARRAY_IDX(rangelist, j, svn_merge_range_t *);
878251881Speter
879251881Speter                  /* Because the merge source normalization code
880251881Speter                     ensures mergeinfo refers to real locations on
881251881Speter                     the same line of history, there's no need to
882251881Speter                     look at the whole range, just the start. */
883251881Speter
884251881Speter                  /* Check if PATH@BASE_REVISION exists at
885251881Speter                     RANGE->START on the same line of history.
886251881Speter                     (start+1 because RANGE->start is not inclusive.) */
887251881Speter                  err2 = svn_client__repos_location(&start_loc, ra_session,
888251881Speter                                                    &target_base,
889251881Speter                                                    range->start + 1,
890251881Speter                                                    ctx, iterpool, iterpool);
891251881Speter                  if (err2)
892251881Speter                    {
893251881Speter                      if (err2->apr_err == SVN_ERR_CLIENT_UNRELATED_RESOURCES
894251881Speter                          || err2->apr_err == SVN_ERR_FS_NOT_FOUND
895251881Speter                          || err2->apr_err == SVN_ERR_FS_NO_SUCH_REVISION)
896251881Speter                        {
897251881Speter                          /* PATH@BASE_REVISION didn't exist at
898251881Speter                             RANGE->START + 1 or is unrelated to the
899251881Speter                             resource PATH@RANGE->START.  Some of the
900251881Speter                             requested revisions may not even exist in
901251881Speter                             the repository; a real possibility since
902251881Speter                             mergeinfo is hand editable.  In all of these
903251881Speter                             cases clear and ignore the error and don't
904251881Speter                             do any filtering.
905251881Speter
906251881Speter                             Note: In this last case it is possible that
907251881Speter                             we will allow self-referential mergeinfo to
908251881Speter                             be applied, but fixing it here is potentially
909251881Speter                             very costly in terms of finding what part of
910251881Speter                             a range is actually valid.  Simply allowing
911251881Speter                             the merge to proceed without filtering the
912251881Speter                             offending range seems the least worst
913251881Speter                             option. */
914251881Speter                          svn_error_clear(err2);
915251881Speter                          err2 = NULL;
916251881Speter                          APR_ARRAY_PUSH(adjusted_rangelist,
917251881Speter                                         svn_merge_range_t *) = range;
918251881Speter                        }
919251881Speter                      else
920251881Speter                        {
921251881Speter                          return svn_error_trace(err2);
922251881Speter                        }
923251881Speter                     }
924251881Speter                  else
925251881Speter                    {
926251881Speter                      /* PATH@BASE_REVISION exists on the same
927251881Speter                         line of history at RANGE->START and RANGE->END.
928251881Speter                         Now check that PATH@BASE_REVISION's path
929251881Speter                         names at RANGE->START and RANGE->END are the same.
930251881Speter                         If the names are not the same then the mergeinfo
931251881Speter                         describing PATH@RANGE->START through
932251881Speter                         PATH@RANGE->END actually belong to some other
933251881Speter                         line of history and we want to record this
934251881Speter                         mergeinfo, not filter it. */
935251881Speter                      if (strcmp(start_loc->url, merge_source_url) != 0)
936251881Speter                        {
937251881Speter                          APR_ARRAY_PUSH(adjusted_rangelist,
938251881Speter                                         svn_merge_range_t *) = range;
939251881Speter                        }
940251881Speter                    }
941251881Speter                    /* else no need to add, this mergeinfo is
942251881Speter                       all on the same line of history. */
943251881Speter                } /* for (j = 0; j < rangelist->nelts; j++) */
944251881Speter
945251881Speter              /* Add any rangelists for source_path that are not
946251881Speter                 self-referential. */
947251881Speter              if (adjusted_rangelist->nelts)
948251881Speter                {
949251881Speter                  if (!filtered_younger_mergeinfo)
950251881Speter                    filtered_younger_mergeinfo = apr_hash_make(iterpool);
951251881Speter                  svn_hash_sets(filtered_younger_mergeinfo, source_path,
952251881Speter                                adjusted_rangelist);
953251881Speter                }
954251881Speter
955251881Speter            } /* Iteration over each merge source in younger_mergeinfo. */
956251881Speter        } /* if (younger_mergeinfo) */
957251881Speter
958251881Speter      /* Filter self-referential mergeinfo from "older" mergeinfo. */
959251881Speter      if (mergeinfo)
960251881Speter        {
961251881Speter          svn_mergeinfo_t implicit_mergeinfo;
962251881Speter
963251881Speter          SVN_ERR(svn_client__get_history_as_mergeinfo(
964251881Speter            &implicit_mergeinfo, NULL,
965251881Speter            &target_base, target_base.rev, SVN_INVALID_REVNUM,
966251881Speter            ra_session, ctx, iterpool));
967251881Speter
968251881Speter          /* Remove PATH's implicit mergeinfo from the incoming mergeinfo. */
969251881Speter          SVN_ERR(svn_mergeinfo_remove2(&filtered_mergeinfo,
970251881Speter                                        implicit_mergeinfo,
971251881Speter                                        mergeinfo, TRUE, iterpool, iterpool));
972251881Speter        }
973251881Speter
974251881Speter      /* Combine whatever older and younger filtered mergeinfo exists
975251881Speter         into filtered_mergeinfo. */
976251881Speter      if (filtered_mergeinfo && filtered_younger_mergeinfo)
977251881Speter        SVN_ERR(svn_mergeinfo_merge2(filtered_mergeinfo,
978251881Speter                                     filtered_younger_mergeinfo, iterpool,
979251881Speter                                     iterpool));
980251881Speter      else if (filtered_younger_mergeinfo)
981251881Speter        filtered_mergeinfo = filtered_younger_mergeinfo;
982251881Speter
983251881Speter      /* If there is any incoming mergeinfo remaining after filtering
984251881Speter         then put it in adjusted_props. */
985251881Speter      if (filtered_mergeinfo && apr_hash_count(filtered_mergeinfo))
986251881Speter        {
987251881Speter          /* Convert filtered_mergeinfo to a svn_prop_t and put it
988251881Speter             back in the array. */
989251881Speter          svn_string_t *filtered_mergeinfo_str;
990251881Speter          svn_prop_t *adjusted_prop = apr_pcalloc(pool,
991251881Speter                                                  sizeof(*adjusted_prop));
992251881Speter          SVN_ERR(svn_mergeinfo_to_string(&filtered_mergeinfo_str,
993251881Speter                                          filtered_mergeinfo,
994251881Speter                                          pool));
995251881Speter          adjusted_prop->name = SVN_PROP_MERGEINFO;
996251881Speter          adjusted_prop->value = filtered_mergeinfo_str;
997251881Speter          APR_ARRAY_PUSH(adjusted_props, svn_prop_t) = *adjusted_prop;
998251881Speter        }
999251881Speter    }
1000251881Speter  svn_pool_destroy(iterpool);
1001251881Speter
1002251881Speter  *props = adjusted_props;
1003251881Speter  return SVN_NO_ERROR;
1004251881Speter}
1005251881Speter
1006251881Speter/* Prepare a set of property changes PROPCHANGES to be used for a merge
1007251881Speter   operation on LOCAL_ABSPATH.
1008251881Speter
1009251881Speter   Remove all non-regular prop-changes (entry-props and WC-props).
1010251881Speter   Remove all non-mergeinfo prop-changes if it's a record-only merge.
1011251881Speter   Remove self-referential mergeinfo (### in some cases...)
1012251881Speter   Remove foreign-repository mergeinfo (### in some cases...)
1013251881Speter
1014251881Speter   Store the resulting property changes in *PROP_UPDATES.
1015251881Speter   Store information on where mergeinfo is updated in MERGE_B.
1016251881Speter
1017251881Speter   Used for both file and directory property merges. */
1018251881Speterstatic svn_error_t *
1019251881Speterprepare_merge_props_changed(const apr_array_header_t **prop_updates,
1020251881Speter                            const char *local_abspath,
1021251881Speter                            const apr_array_header_t *propchanges,
1022251881Speter                            merge_cmd_baton_t *merge_b,
1023251881Speter                            apr_pool_t *result_pool,
1024251881Speter                            apr_pool_t *scratch_pool)
1025251881Speter{
1026251881Speter  apr_array_header_t *props;
1027251881Speter
1028251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1029251881Speter
1030251881Speter  /* We only want to merge "regular" version properties:  by
1031251881Speter     definition, 'svn merge' shouldn't touch any data within .svn/  */
1032251881Speter  SVN_ERR(svn_categorize_props(propchanges, NULL, NULL, &props,
1033251881Speter                               result_pool));
1034251881Speter
1035251881Speter  /* If we are only applying mergeinfo changes then we need to do
1036251881Speter     additional filtering of PROPS so it contains only mergeinfo changes. */
1037251881Speter  if (merge_b->record_only && props->nelts)
1038251881Speter    {
1039251881Speter      apr_array_header_t *mergeinfo_props =
1040251881Speter        apr_array_make(result_pool, 1, sizeof(svn_prop_t));
1041251881Speter      int i;
1042251881Speter
1043251881Speter      for (i = 0; i < props->nelts; i++)
1044251881Speter        {
1045251881Speter          svn_prop_t *prop = &APR_ARRAY_IDX(props, i, svn_prop_t);
1046251881Speter
1047251881Speter          if (strcmp(prop->name, SVN_PROP_MERGEINFO) == 0)
1048251881Speter            {
1049251881Speter              APR_ARRAY_PUSH(mergeinfo_props, svn_prop_t) = *prop;
1050251881Speter              break;
1051251881Speter            }
1052251881Speter        }
1053251881Speter      props = mergeinfo_props;
1054251881Speter    }
1055251881Speter
1056251881Speter  if (props->nelts)
1057251881Speter    {
1058251881Speter      /* Issue #3383: We don't want mergeinfo from a foreign repos.
1059251881Speter
1060251881Speter         If this is a merge from a foreign repository we must strip all
1061251881Speter         incoming mergeinfo (including mergeinfo deletions). */
1062251881Speter      if (! merge_b->same_repos)
1063251881Speter        SVN_ERR(omit_mergeinfo_changes(&props, props, result_pool));
1064251881Speter
1065251881Speter      /* If this is a forward merge then don't add new mergeinfo to
1066251881Speter         PATH that is already part of PATH's own history, see
1067251881Speter         http://svn.haxx.se/dev/archive-2008-09/0006.shtml.  If the
1068251881Speter         merge sources are not ancestral then there is no concept of a
1069251881Speter         'forward' or 'reverse' merge and we filter unconditionally. */
1070251881Speter      if (merge_b->merge_source.loc1->rev < merge_b->merge_source.loc2->rev
1071251881Speter          || !merge_b->merge_source.ancestral)
1072251881Speter        {
1073251881Speter          if (HONOR_MERGEINFO(merge_b) || merge_b->reintegrate_merge)
1074251881Speter            SVN_ERR(filter_self_referential_mergeinfo(&props,
1075251881Speter                                                      local_abspath,
1076251881Speter                                                      merge_b->ra_session2,
1077251881Speter                                                      merge_b->ctx,
1078251881Speter                                                      result_pool));
1079251881Speter        }
1080251881Speter    }
1081251881Speter  *prop_updates = props;
1082251881Speter
1083251881Speter  /* Make a record in BATON if we find a PATH where mergeinfo is added
1084251881Speter     where none existed previously or PATH is having its existing
1085251881Speter     mergeinfo deleted. */
1086251881Speter  if (props->nelts)
1087251881Speter    {
1088251881Speter      int i;
1089251881Speter
1090251881Speter      for (i = 0; i < props->nelts; ++i)
1091251881Speter        {
1092251881Speter          svn_prop_t *prop = &APR_ARRAY_IDX(props, i, svn_prop_t);
1093251881Speter
1094251881Speter          if (strcmp(prop->name, SVN_PROP_MERGEINFO) == 0)
1095251881Speter            {
1096251881Speter              /* Does LOCAL_ABSPATH have any pristine mergeinfo? */
1097251881Speter              svn_boolean_t has_pristine_mergeinfo = FALSE;
1098251881Speter              apr_hash_t *pristine_props;
1099251881Speter
1100251881Speter              SVN_ERR(svn_wc_get_pristine_props(&pristine_props,
1101251881Speter                                                merge_b->ctx->wc_ctx,
1102251881Speter                                                local_abspath,
1103251881Speter                                                scratch_pool,
1104251881Speter                                                scratch_pool));
1105251881Speter
1106251881Speter              if (pristine_props
1107251881Speter                  && svn_hash_gets(pristine_props, SVN_PROP_MERGEINFO))
1108251881Speter                has_pristine_mergeinfo = TRUE;
1109251881Speter
1110251881Speter              if (!has_pristine_mergeinfo && prop->value)
1111251881Speter                {
1112251881Speter                  alloc_and_store_path(&merge_b->paths_with_new_mergeinfo,
1113251881Speter                                       local_abspath, merge_b->pool);
1114251881Speter                }
1115251881Speter              else if (has_pristine_mergeinfo && !prop->value)
1116251881Speter                {
1117251881Speter                  alloc_and_store_path(&merge_b->paths_with_deleted_mergeinfo,
1118251881Speter                                       local_abspath, merge_b->pool);
1119251881Speter                }
1120251881Speter            }
1121251881Speter        }
1122251881Speter    }
1123251881Speter
1124251881Speter  return SVN_NO_ERROR;
1125251881Speter}
1126251881Speter
1127251881Speter#define CONFLICT_REASON_NONE       ((svn_wc_conflict_reason_t)-1)
1128251881Speter#define CONFLICT_REASON_SKIP       ((svn_wc_conflict_reason_t)-2)
1129251881Speter#define CONFLICT_REASON_SKIP_WC    ((svn_wc_conflict_reason_t)-3)
1130251881Speter
1131251881Speter/* Baton used for testing trees for being editted while performing tree
1132251881Speter   conflict detection for incoming deletes */
1133251881Speterstruct dir_delete_baton_t
1134251881Speter{
1135251881Speter  /* Reference to dir baton of directory that is the root of the deletion */
1136251881Speter  struct merge_dir_baton_t *del_root;
1137251881Speter
1138251881Speter  /* Boolean indicating that some edit is found. Allows avoiding more work */
1139251881Speter  svn_boolean_t found_edit;
1140251881Speter
1141251881Speter  /* A list of paths that are compared. Kept up to date until FOUND_EDIT is
1142251881Speter     set to TRUE */
1143251881Speter  apr_hash_t *compared_abspaths;
1144251881Speter};
1145251881Speter
1146251881Speter/* Baton for the merge_dir_*() functions. Initialized in merge_dir_opened() */
1147251881Speterstruct merge_dir_baton_t
1148251881Speter{
1149251881Speter  /* Reference to the parent baton, unless the parent is the anchor, in which
1150251881Speter     case PARENT_BATON is NULL */
1151251881Speter  struct merge_dir_baton_t *parent_baton;
1152251881Speter
1153251881Speter  /* The pool containing this baton. Use for RESULT_POOL for storing in this
1154251881Speter     baton */
1155251881Speter  apr_pool_t *pool;
1156251881Speter
1157251881Speter  /* This directory doesn't have a representation in the working copy, so any
1158251881Speter     operation on it will be skipped and possibly cause a tree conflict on the
1159251881Speter     shadow root */
1160251881Speter  svn_boolean_t shadowed;
1161251881Speter
1162251881Speter  /* This node or one of its descendants received operational changes from the
1163251881Speter     merge. If this node is the shadow root its tree conflict status has been
1164251881Speter     applied */
1165251881Speter  svn_boolean_t edited;
1166251881Speter
1167251881Speter  /* If a tree conflict will be installed once edited, it's reason. If a skip
1168251881Speter     should be produced its reason. Otherwise CONFLICT_REASON_NONE for no tree
1169251881Speter     conflict.
1170251881Speter
1171251881Speter     Special values:
1172251881Speter       CONFLICT_REASON_SKIP:
1173251881Speter            The node will be skipped with content and property state as stored in
1174251881Speter            SKIP_REASON.
1175251881Speter
1176251881Speter       CONFLICT_REASON_SKIP_WC:
1177251881Speter            The node will be skipped as an obstructing working copy.
1178251881Speter   */
1179251881Speter  svn_wc_conflict_reason_t tree_conflict_reason;
1180251881Speter  svn_wc_conflict_action_t tree_conflict_action;
1181251881Speter
1182251881Speter  /* When TREE_CONFLICT_REASON is CONFLICT_REASON_SKIP, the skip state to
1183251881Speter     add to the notification */
1184251881Speter  svn_wc_notify_state_t skip_reason;
1185251881Speter
1186251881Speter  /* TRUE if the node was added by this merge. Otherwise FALSE */
1187251881Speter  svn_boolean_t added;
1188251881Speter  svn_boolean_t add_is_replace; /* Add is second part of replace */
1189251881Speter
1190251881Speter  /* TRUE if we are taking over an existing directory as addition, otherwise
1191251881Speter     FALSE. */
1192251881Speter  svn_boolean_t add_existing;
1193251881Speter
1194251881Speter  /* NULL, or an hashtable mapping const char * local_abspaths to
1195251881Speter     const char *kind mapping, containing deleted nodes that still need a delete
1196251881Speter     notification (which may be a replaced notification if the node is not just
1197251881Speter     deleted) */
1198251881Speter  apr_hash_t *pending_deletes;
1199251881Speter
1200251881Speter  /* NULL, or an hashtable mapping const char * LOCAL_ABSPATHs to
1201251881Speter     a const svn_wc_conflict_description2_t * instance, describing the just
1202251881Speter     installed conflict */
1203251881Speter  apr_hash_t *new_tree_conflicts;
1204251881Speter
1205251881Speter  /* If not NULL, a reference to the information of the delete test that is
1206251881Speter     currently in progress. Allocated in the root-directory baton, referenced
1207251881Speter     from all descendants */
1208251881Speter  struct dir_delete_baton_t *delete_state;
1209251881Speter};
1210251881Speter
1211251881Speter/* Baton for the merge_dir_*() functions. Initialized in merge_file_opened() */
1212251881Speterstruct merge_file_baton_t
1213251881Speter{
1214251881Speter  /* Reference to the parent baton, unless the parent is the anchor, in which
1215251881Speter     case PARENT_BATON is NULL */
1216251881Speter  struct merge_dir_baton_t *parent_baton;
1217251881Speter
1218251881Speter  /* This file doesn't have a representation in the working copy, so any
1219251881Speter     operation on it will be skipped and possibly cause a tree conflict
1220251881Speter     on the shadow root */
1221251881Speter  svn_boolean_t shadowed;
1222251881Speter
1223251881Speter  /* This node received operational changes from the merge. If this node
1224251881Speter     is the shadow root its tree conflict status has been applied */
1225251881Speter  svn_boolean_t edited;
1226251881Speter
1227251881Speter  /* If a tree conflict will be installed once edited, it's reason. If a skip
1228251881Speter     should be produced its reason. Some special values are defined. See the
1229251881Speter     merge_tree_baton_t for an explanation. */
1230251881Speter  svn_wc_conflict_reason_t tree_conflict_reason;
1231251881Speter  svn_wc_conflict_action_t tree_conflict_action;
1232251881Speter
1233251881Speter  /* When TREE_CONFLICT_REASON is CONFLICT_REASON_SKIP, the skip state to
1234251881Speter     add to the notification */
1235251881Speter  svn_wc_notify_state_t skip_reason;
1236251881Speter
1237251881Speter  /* TRUE if the node was added by this merge. Otherwise FALSE */
1238251881Speter  svn_boolean_t added;
1239251881Speter  svn_boolean_t add_is_replace; /* Add is second part of replace */
1240251881Speter};
1241251881Speter
1242251881Speter/* Forward declaration */
1243251881Speterstatic svn_error_t *
1244251881Speternotify_merge_begin(merge_cmd_baton_t *merge_b,
1245251881Speter                   const char *local_abspath,
1246251881Speter                   svn_boolean_t delete_action,
1247251881Speter                   apr_pool_t *scratch_pool);
1248251881Speter
1249251881Speter/* Record the skip for future processing and (later) produce the
1250251881Speter   skip notification */
1251251881Speterstatic svn_error_t *
1252251881Speterrecord_skip(merge_cmd_baton_t *merge_b,
1253251881Speter            const char *local_abspath,
1254251881Speter            svn_node_kind_t kind,
1255251881Speter            svn_wc_notify_action_t action,
1256251881Speter            svn_wc_notify_state_t state,
1257251881Speter            apr_pool_t *scratch_pool)
1258251881Speter{
1259251881Speter  if (merge_b->record_only)
1260251881Speter    return SVN_NO_ERROR; /* ### Why? - Legacy compatibility */
1261251881Speter
1262251881Speter  if (merge_b->merge_source.ancestral
1263251881Speter      || merge_b->reintegrate_merge)
1264251881Speter    {
1265251881Speter      store_path(merge_b->skipped_abspaths, local_abspath);
1266251881Speter    }
1267251881Speter
1268251881Speter  if (merge_b->ctx->notify_func2)
1269251881Speter    {
1270251881Speter      svn_wc_notify_t *notify;
1271251881Speter
1272251881Speter      SVN_ERR(notify_merge_begin(merge_b, local_abspath, FALSE, scratch_pool));
1273251881Speter
1274251881Speter      notify = svn_wc_create_notify(local_abspath, action, scratch_pool);
1275251881Speter      notify->kind = kind;
1276251881Speter      notify->content_state = notify->prop_state = state;
1277251881Speter
1278251881Speter      (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify,
1279251881Speter                                    scratch_pool);
1280251881Speter    }
1281251881Speter  return SVN_NO_ERROR;
1282251881Speter}
1283251881Speter
1284251881Speter/* Record a tree conflict in the WC, unless this is a dry run or a record-
1285251881Speter * only merge, or if a tree conflict is already flagged for the VICTIM_PATH.
1286251881Speter * (The latter can happen if a merge-tracking-aware merge is doing multiple
1287251881Speter * editor drives because of a gap in the range of eligible revisions.)
1288251881Speter *
1289251881Speter * The tree conflict, with its victim specified by VICTIM_PATH, is
1290251881Speter * assumed to have happened during a merge using merge baton MERGE_B.
1291251881Speter *
1292251881Speter * NODE_KIND must be the node kind of "old" and "theirs" and "mine";
1293251881Speter * this function cannot cope with node kind clashes.
1294251881Speter * ACTION and REASON correspond to the fields
1295251881Speter * of the same names in svn_wc_tree_conflict_description_t.
1296251881Speter */
1297251881Speterstatic svn_error_t *
1298251881Speterrecord_tree_conflict(merge_cmd_baton_t *merge_b,
1299251881Speter                     const char *local_abspath,
1300251881Speter                     struct merge_dir_baton_t *parent_baton,
1301251881Speter                     svn_node_kind_t node_kind,
1302251881Speter                     svn_wc_conflict_action_t action,
1303251881Speter                     svn_wc_conflict_reason_t reason,
1304251881Speter                     const svn_wc_conflict_description2_t *existing_conflict,
1305251881Speter                     svn_boolean_t notify_tc,
1306251881Speter                     apr_pool_t *scratch_pool)
1307251881Speter{
1308251881Speter  svn_wc_context_t *wc_ctx = merge_b->ctx->wc_ctx;
1309251881Speter
1310253734Speter  if (merge_b->record_only)
1311253734Speter    return SVN_NO_ERROR;
1312253734Speter
1313251881Speter  if (merge_b->merge_source.ancestral
1314251881Speter      || merge_b->reintegrate_merge)
1315251881Speter    {
1316251881Speter      store_path(merge_b->tree_conflicted_abspaths, local_abspath);
1317251881Speter    }
1318251881Speter
1319251881Speter  alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
1320251881Speter                       merge_b->pool);
1321251881Speter
1322253734Speter  if (!merge_b->dry_run)
1323251881Speter    {
1324251881Speter       svn_wc_conflict_description2_t *conflict;
1325251881Speter       const svn_wc_conflict_version_t *left;
1326251881Speter       const svn_wc_conflict_version_t *right;
1327251881Speter       apr_pool_t *result_pool = parent_baton ? parent_baton->pool
1328251881Speter                                              : scratch_pool;
1329251881Speter
1330251881Speter      if (reason == svn_wc_conflict_reason_deleted)
1331251881Speter        {
1332251881Speter          const char *moved_to_abspath;
1333251881Speter
1334251881Speter          SVN_ERR(svn_wc__node_was_moved_away(&moved_to_abspath, NULL,
1335251881Speter                                              wc_ctx, local_abspath,
1336251881Speter                                              scratch_pool, scratch_pool));
1337251881Speter
1338251881Speter          if (moved_to_abspath)
1339251881Speter            {
1340251881Speter              /* Local abspath itself has been moved away. If only a
1341251881Speter                 descendant is moved away, we call the node itself deleted */
1342251881Speter              reason = svn_wc_conflict_reason_moved_away;
1343251881Speter            }
1344251881Speter        }
1345251881Speter      else if (reason == svn_wc_conflict_reason_added)
1346251881Speter        {
1347251881Speter          const char *moved_from_abspath;
1348251881Speter          SVN_ERR(svn_wc__node_was_moved_here(&moved_from_abspath, NULL,
1349251881Speter                                              wc_ctx, local_abspath,
1350251881Speter                                              scratch_pool, scratch_pool));
1351251881Speter          if (moved_from_abspath)
1352251881Speter            reason = svn_wc_conflict_reason_moved_here;
1353251881Speter        }
1354251881Speter
1355251881Speter      SVN_ERR(make_conflict_versions(&left, &right, local_abspath, node_kind,
1356251881Speter                                     &merge_b->merge_source, merge_b->target,
1357251881Speter                                     result_pool, scratch_pool));
1358251881Speter
1359251881Speter      /* Fix up delete of file, add of dir replacement (or other way around) */
1360251881Speter      if (existing_conflict != NULL && existing_conflict->src_left_version)
1361251881Speter          left = existing_conflict->src_left_version;
1362251881Speter
1363251881Speter      conflict = svn_wc_conflict_description_create_tree2(
1364251881Speter                        local_abspath, node_kind, svn_wc_operation_merge,
1365251881Speter                        left, right, result_pool);
1366251881Speter
1367251881Speter      conflict->action = action;
1368251881Speter      conflict->reason = reason;
1369251881Speter
1370251881Speter      /* May return SVN_ERR_WC_PATH_UNEXPECTED_STATUS */
1371251881Speter      if (existing_conflict)
1372251881Speter        SVN_ERR(svn_wc__del_tree_conflict(wc_ctx, local_abspath,
1373251881Speter                                          scratch_pool));
1374251881Speter
1375251881Speter      SVN_ERR(svn_wc__add_tree_conflict(merge_b->ctx->wc_ctx, conflict,
1376251881Speter                                        scratch_pool));
1377251881Speter
1378251881Speter      if (parent_baton)
1379251881Speter        {
1380251881Speter          if (! parent_baton->new_tree_conflicts)
1381251881Speter            parent_baton->new_tree_conflicts = apr_hash_make(result_pool);
1382251881Speter
1383251881Speter          svn_hash_sets(parent_baton->new_tree_conflicts,
1384251881Speter                        apr_pstrdup(result_pool, local_abspath),
1385251881Speter                        conflict);
1386251881Speter        }
1387251881Speter
1388251881Speter      /* ### TODO: Store in parent baton */
1389251881Speter    }
1390251881Speter
1391251881Speter  /* On a replacement we currently get two tree conflicts */
1392251881Speter  if (merge_b->ctx->notify_func2 && notify_tc)
1393251881Speter    {
1394251881Speter      svn_wc_notify_t *notify;
1395251881Speter
1396251881Speter      SVN_ERR(notify_merge_begin(merge_b, local_abspath, FALSE, scratch_pool));
1397251881Speter
1398251881Speter      notify = svn_wc_create_notify(local_abspath, svn_wc_notify_tree_conflict,
1399251881Speter                                    scratch_pool);
1400251881Speter      notify->kind = node_kind;
1401251881Speter
1402251881Speter      (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify,
1403251881Speter                                    scratch_pool);
1404251881Speter    }
1405251881Speter
1406251881Speter  return SVN_NO_ERROR;
1407251881Speter}
1408251881Speter
1409251881Speter/* Record the add for future processing and produce the
1410251881Speter   update_add notification
1411251881Speter */
1412251881Speterstatic svn_error_t *
1413251881Speterrecord_update_add(merge_cmd_baton_t *merge_b,
1414251881Speter                  const char *local_abspath,
1415251881Speter                  svn_node_kind_t kind,
1416251881Speter                  svn_boolean_t notify_replaced,
1417251881Speter                  apr_pool_t *scratch_pool)
1418251881Speter{
1419251881Speter  if (merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
1420251881Speter    {
1421251881Speter      store_path(merge_b->merged_abspaths, local_abspath);
1422251881Speter    }
1423251881Speter
1424251881Speter  if (merge_b->ctx->notify_func2)
1425251881Speter    {
1426251881Speter      svn_wc_notify_t *notify;
1427251881Speter      svn_wc_notify_action_t action = svn_wc_notify_update_add;
1428251881Speter
1429251881Speter      SVN_ERR(notify_merge_begin(merge_b, local_abspath, FALSE, scratch_pool));
1430251881Speter
1431251881Speter      if (notify_replaced)
1432251881Speter        action = svn_wc_notify_update_replace;
1433251881Speter
1434251881Speter      notify = svn_wc_create_notify(local_abspath, action, scratch_pool);
1435251881Speter      notify->kind = kind;
1436251881Speter
1437251881Speter      (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify,
1438251881Speter                                    scratch_pool);
1439251881Speter    }
1440251881Speter
1441251881Speter  return SVN_NO_ERROR;
1442251881Speter}
1443251881Speter
1444251881Speter/* Record the update for future processing and produce the
1445251881Speter   update_update notification */
1446251881Speterstatic svn_error_t *
1447251881Speterrecord_update_update(merge_cmd_baton_t *merge_b,
1448251881Speter                     const char *local_abspath,
1449251881Speter                     svn_node_kind_t kind,
1450251881Speter                     svn_wc_notify_state_t content_state,
1451251881Speter                     svn_wc_notify_state_t prop_state,
1452251881Speter                     apr_pool_t *scratch_pool)
1453251881Speter{
1454251881Speter  if (merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
1455251881Speter    {
1456251881Speter      store_path(merge_b->merged_abspaths, local_abspath);
1457251881Speter    }
1458251881Speter
1459251881Speter  if (merge_b->ctx->notify_func2)
1460251881Speter    {
1461251881Speter      svn_wc_notify_t *notify;
1462251881Speter
1463251881Speter      SVN_ERR(notify_merge_begin(merge_b, local_abspath, FALSE, scratch_pool));
1464251881Speter
1465251881Speter      notify = svn_wc_create_notify(local_abspath, svn_wc_notify_update_update,
1466251881Speter                                    scratch_pool);
1467251881Speter      notify->kind = kind;
1468251881Speter      notify->content_state = content_state;
1469251881Speter      notify->prop_state = prop_state;
1470251881Speter
1471251881Speter      (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify,
1472251881Speter                                    scratch_pool);
1473251881Speter    }
1474251881Speter
1475251881Speter  return SVN_NO_ERROR;
1476251881Speter}
1477251881Speter
1478251881Speter/* Record the delete for future processing and for (later) producing the
1479251881Speter   update_delete notification */
1480251881Speterstatic svn_error_t *
1481251881Speterrecord_update_delete(merge_cmd_baton_t *merge_b,
1482251881Speter                     struct merge_dir_baton_t *parent_db,
1483251881Speter                     const char *local_abspath,
1484251881Speter                     svn_node_kind_t kind,
1485251881Speter                     apr_pool_t *scratch_pool)
1486251881Speter{
1487251881Speter  /* Update the lists of merged, skipped, tree-conflicted and added paths. */
1488251881Speter  if (merge_b->merge_source.ancestral
1489251881Speter      || merge_b->reintegrate_merge)
1490251881Speter    {
1491251881Speter      /* Issue #4166: If a previous merge added NOTIFY_ABSPATH, but we
1492251881Speter         are now deleting it, then remove it from the list of added
1493251881Speter         paths. */
1494251881Speter      svn_hash_sets(merge_b->added_abspaths, local_abspath, NULL);
1495251881Speter      store_path(merge_b->merged_abspaths, local_abspath);
1496251881Speter    }
1497251881Speter
1498251881Speter  SVN_ERR(notify_merge_begin(merge_b, local_abspath, TRUE, scratch_pool));
1499251881Speter
1500251881Speter  if (parent_db)
1501251881Speter    {
1502251881Speter      const char *dup_abspath = apr_pstrdup(parent_db->pool, local_abspath);
1503251881Speter
1504251881Speter      if (!parent_db->pending_deletes)
1505251881Speter        parent_db->pending_deletes = apr_hash_make(parent_db->pool);
1506251881Speter
1507251881Speter      svn_hash_sets(parent_db->pending_deletes, dup_abspath,
1508251881Speter                    svn_node_kind_to_word(kind));
1509251881Speter    }
1510251881Speter
1511251881Speter  return SVN_NO_ERROR;
1512251881Speter}
1513251881Speter
1514251881Speter/* Notify the pending 'D'eletes, that were waiting to see if a matching 'A'dd
1515251881Speter   might make them a 'R'eplace. */
1516251881Speterstatic svn_error_t *
1517251881Speterhandle_pending_notifications(merge_cmd_baton_t *merge_b,
1518251881Speter                             struct merge_dir_baton_t *db,
1519251881Speter                             apr_pool_t *scratch_pool)
1520251881Speter{
1521251881Speter  if (merge_b->ctx->notify_func2 && db->pending_deletes)
1522251881Speter    {
1523251881Speter      apr_hash_index_t *hi;
1524251881Speter
1525251881Speter      for (hi = apr_hash_first(scratch_pool, db->pending_deletes);
1526251881Speter           hi;
1527251881Speter           hi = apr_hash_next(hi))
1528251881Speter        {
1529251881Speter          const char *del_abspath = svn__apr_hash_index_key(hi);
1530251881Speter          svn_wc_notify_t *notify;
1531251881Speter
1532251881Speter          notify = svn_wc_create_notify(del_abspath,
1533251881Speter                                        svn_wc_notify_update_delete,
1534251881Speter                                        scratch_pool);
1535251881Speter          notify->kind = svn_node_kind_from_word(
1536251881Speter                                    svn__apr_hash_index_val(hi));
1537251881Speter
1538251881Speter          (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2,
1539251881Speter                                        notify, scratch_pool);
1540251881Speter        }
1541251881Speter
1542251881Speter      db->pending_deletes = NULL;
1543251881Speter    }
1544251881Speter  return SVN_NO_ERROR;
1545251881Speter}
1546251881Speter
1547251881Speter/* Helper function for the merge_dir_*() and merge_file_*() functions.
1548251881Speter
1549251881Speter   Installs and notifies pre-recorded tree conflicts and skips for
1550251881Speter   ancestors of operational merges
1551251881Speter */
1552251881Speterstatic svn_error_t *
1553251881Spetermark_dir_edited(merge_cmd_baton_t *merge_b,
1554251881Speter                struct merge_dir_baton_t *db,
1555251881Speter                const char *local_abspath,
1556251881Speter                apr_pool_t *scratch_pool)
1557251881Speter{
1558251881Speter  /* ### Too much common code with mark_file_edited */
1559251881Speter  if (db->edited)
1560251881Speter    return SVN_NO_ERROR;
1561251881Speter
1562251881Speter  if (db->parent_baton && !db->parent_baton->edited)
1563251881Speter    {
1564251881Speter      const char *dir_abspath = svn_dirent_dirname(local_abspath,
1565251881Speter                                                   scratch_pool);
1566251881Speter
1567251881Speter      SVN_ERR(mark_dir_edited(merge_b, db->parent_baton, dir_abspath,
1568251881Speter                              scratch_pool));
1569251881Speter    }
1570251881Speter
1571251881Speter  db->edited = TRUE;
1572251881Speter
1573251881Speter  if (! db->shadowed)
1574251881Speter    return SVN_NO_ERROR; /* Easy out */
1575251881Speter
1576251881Speter  if (db->parent_baton
1577251881Speter      && db->parent_baton->delete_state
1578251881Speter      && db->tree_conflict_reason != CONFLICT_REASON_NONE)
1579251881Speter    {
1580251881Speter      db->parent_baton->delete_state->found_edit = TRUE;
1581251881Speter    }
1582251881Speter  else if (db->tree_conflict_reason == CONFLICT_REASON_SKIP
1583251881Speter           || db->tree_conflict_reason == CONFLICT_REASON_SKIP_WC)
1584251881Speter    {
1585251881Speter      /* open_directory() decided not to flag a tree conflict, but
1586251881Speter         for clarity we produce a skip for this node that
1587251881Speter         most likely isn't touched by the merge itself */
1588251881Speter
1589251881Speter      if (merge_b->ctx->notify_func2)
1590251881Speter        {
1591251881Speter          svn_wc_notify_t *notify;
1592251881Speter
1593251881Speter          SVN_ERR(notify_merge_begin(merge_b, local_abspath, FALSE,
1594251881Speter                                     scratch_pool));
1595251881Speter
1596251881Speter          notify = svn_wc_create_notify(
1597251881Speter                            local_abspath,
1598251881Speter                            (db->tree_conflict_reason == CONFLICT_REASON_SKIP)
1599251881Speter                                ? svn_wc_notify_skip
1600251881Speter                                : svn_wc_notify_update_skip_obstruction,
1601251881Speter                            scratch_pool);
1602251881Speter          notify->kind = svn_node_dir;
1603251881Speter          notify->content_state = notify->prop_state = db->skip_reason;
1604251881Speter
1605251881Speter          (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2,
1606251881Speter                                        notify,
1607251881Speter                                        scratch_pool);
1608251881Speter        }
1609251881Speter
1610251881Speter      if (merge_b->merge_source.ancestral
1611251881Speter          || merge_b->reintegrate_merge)
1612251881Speter        {
1613251881Speter          store_path(merge_b->skipped_abspaths, local_abspath);
1614251881Speter        }
1615251881Speter    }
1616251881Speter  else if (db->tree_conflict_reason != CONFLICT_REASON_NONE)
1617251881Speter    {
1618251881Speter      /* open_directory() decided that a tree conflict should be raised */
1619251881Speter
1620251881Speter      SVN_ERR(record_tree_conflict(merge_b, local_abspath, db->parent_baton,
1621251881Speter                                   svn_node_dir, db->tree_conflict_action,
1622251881Speter                                   db->tree_conflict_reason,
1623251881Speter                                   NULL, TRUE,
1624251881Speter                                   scratch_pool));
1625251881Speter    }
1626251881Speter
1627251881Speter  return SVN_NO_ERROR;
1628251881Speter}
1629251881Speter
1630251881Speter/* Helper function for the merge_file_*() functions.
1631251881Speter
1632251881Speter   Installs and notifies pre-recorded tree conflicts and skips for
1633251881Speter   ancestors of operational merges
1634251881Speter */
1635251881Speterstatic svn_error_t *
1636251881Spetermark_file_edited(merge_cmd_baton_t *merge_b,
1637251881Speter                 struct merge_file_baton_t *fb,
1638251881Speter                 const char *local_abspath,
1639251881Speter                 apr_pool_t *scratch_pool)
1640251881Speter{
1641251881Speter  /* ### Too much common code with mark_dir_edited */
1642251881Speter  if (fb->edited)
1643251881Speter    return SVN_NO_ERROR;
1644251881Speter
1645251881Speter  if (fb->parent_baton && !fb->parent_baton->edited)
1646251881Speter    {
1647251881Speter      const char *dir_abspath = svn_dirent_dirname(local_abspath,
1648251881Speter                                                   scratch_pool);
1649251881Speter
1650251881Speter      SVN_ERR(mark_dir_edited(merge_b, fb->parent_baton, dir_abspath,
1651251881Speter                              scratch_pool));
1652251881Speter    }
1653251881Speter
1654251881Speter  fb->edited = TRUE;
1655251881Speter
1656251881Speter  if (! fb->shadowed)
1657251881Speter    return SVN_NO_ERROR; /* Easy out */
1658251881Speter
1659251881Speter  if (fb->parent_baton
1660251881Speter      && fb->parent_baton->delete_state
1661251881Speter      && fb->tree_conflict_reason != CONFLICT_REASON_NONE)
1662251881Speter    {
1663251881Speter      fb->parent_baton->delete_state->found_edit = TRUE;
1664251881Speter    }
1665251881Speter  else if (fb->tree_conflict_reason == CONFLICT_REASON_SKIP
1666251881Speter           || fb->tree_conflict_reason == CONFLICT_REASON_SKIP_WC)
1667251881Speter    {
1668251881Speter      /* open_directory() decided not to flag a tree conflict, but
1669251881Speter         for clarity we produce a skip for this node that
1670251881Speter         most likely isn't touched by the merge itself */
1671251881Speter
1672251881Speter      if (merge_b->ctx->notify_func2)
1673251881Speter        {
1674251881Speter          svn_wc_notify_t *notify;
1675251881Speter
1676251881Speter          SVN_ERR(notify_merge_begin(merge_b, local_abspath, FALSE,
1677251881Speter                                     scratch_pool));
1678251881Speter
1679251881Speter          notify = svn_wc_create_notify(local_abspath, svn_wc_notify_skip,
1680251881Speter                                        scratch_pool);
1681251881Speter          notify->kind = svn_node_file;
1682251881Speter          notify->content_state = notify->prop_state = fb->skip_reason;
1683251881Speter
1684251881Speter          (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2,
1685251881Speter                                        notify,
1686251881Speter                                        scratch_pool);
1687251881Speter        }
1688251881Speter
1689251881Speter      if (merge_b->merge_source.ancestral
1690251881Speter          || merge_b->reintegrate_merge)
1691251881Speter        {
1692251881Speter          store_path(merge_b->skipped_abspaths, local_abspath);
1693251881Speter        }
1694251881Speter    }
1695251881Speter  else if (fb->tree_conflict_reason != CONFLICT_REASON_NONE)
1696251881Speter    {
1697251881Speter      /* open_file() decided that a tree conflict should be raised */
1698251881Speter
1699251881Speter      SVN_ERR(record_tree_conflict(merge_b, local_abspath, fb->parent_baton,
1700251881Speter                                   svn_node_file, fb->tree_conflict_action,
1701251881Speter                                   fb->tree_conflict_reason,
1702251881Speter                                   NULL, TRUE,
1703251881Speter                                   scratch_pool));
1704251881Speter    }
1705251881Speter
1706251881Speter  return SVN_NO_ERROR;
1707251881Speter}
1708251881Speter
1709251881Speter/* An svn_diff_tree_processor_t function.
1710251881Speter
1711251881Speter   Called before either merge_file_changed(), merge_file_added(),
1712251881Speter   merge_file_deleted() or merge_file_closed(), unless it sets *SKIP to TRUE.
1713251881Speter
1714251881Speter   When *SKIP is TRUE, the diff driver avoids work on getting the details
1715251881Speter   for the closing callbacks.
1716251881Speter */
1717251881Speterstatic svn_error_t *
1718251881Spetermerge_file_opened(void **new_file_baton,
1719251881Speter                  svn_boolean_t *skip,
1720251881Speter                  const char *relpath,
1721251881Speter                  const svn_diff_source_t *left_source,
1722251881Speter                  const svn_diff_source_t *right_source,
1723251881Speter                  const svn_diff_source_t *copyfrom_source,
1724251881Speter                  void *dir_baton,
1725251881Speter                  const struct svn_diff_tree_processor_t *processor,
1726251881Speter                  apr_pool_t *result_pool,
1727251881Speter                  apr_pool_t *scratch_pool)
1728251881Speter{
1729251881Speter  merge_cmd_baton_t *merge_b = processor->baton;
1730251881Speter  struct merge_dir_baton_t *pdb = dir_baton;
1731251881Speter  struct merge_file_baton_t *fb;
1732251881Speter  const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
1733251881Speter                                              relpath, scratch_pool);
1734251881Speter
1735251881Speter  fb = apr_pcalloc(result_pool, sizeof(*fb));
1736251881Speter  fb->tree_conflict_reason = CONFLICT_REASON_NONE;
1737251881Speter  fb->tree_conflict_action = svn_wc_conflict_action_edit;
1738251881Speter  fb->skip_reason = svn_wc_notify_state_unknown;
1739251881Speter
1740251881Speter  *new_file_baton = fb;
1741251881Speter
1742251881Speter  if (pdb)
1743251881Speter    {
1744251881Speter      fb->parent_baton = pdb;
1745251881Speter      fb->shadowed = pdb->shadowed;
1746251881Speter      fb->skip_reason = pdb->skip_reason;
1747251881Speter    }
1748251881Speter
1749251881Speter  if (fb->shadowed)
1750251881Speter    {
1751251881Speter      /* An ancestor is tree conflicted. Nothing to do here. */
1752251881Speter    }
1753251881Speter  else if (left_source != NULL)
1754251881Speter    {
1755251881Speter      /* Node is expected to be a file, which will be changed or deleted. */
1756251881Speter      svn_node_kind_t kind;
1757251881Speter      svn_boolean_t is_deleted;
1758251881Speter      svn_boolean_t excluded;
1759251881Speter      svn_depth_t parent_depth;
1760251881Speter
1761251881Speter      if (! right_source)
1762251881Speter        fb->tree_conflict_action = svn_wc_conflict_action_delete;
1763251881Speter
1764251881Speter      {
1765251881Speter        svn_wc_notify_state_t obstr_state;
1766251881Speter
1767251881Speter        SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, &excluded,
1768251881Speter                                          &kind, &parent_depth,
1769251881Speter                                          merge_b, local_abspath,
1770251881Speter                                          scratch_pool));
1771251881Speter
1772251881Speter        if (obstr_state != svn_wc_notify_state_inapplicable)
1773251881Speter          {
1774251881Speter            fb->shadowed = TRUE;
1775251881Speter            fb->tree_conflict_reason = CONFLICT_REASON_SKIP;
1776251881Speter            fb->skip_reason = obstr_state;
1777251881Speter            return SVN_NO_ERROR;
1778251881Speter          }
1779251881Speter
1780251881Speter        if (is_deleted)
1781251881Speter          kind = svn_node_none;
1782251881Speter      }
1783251881Speter
1784251881Speter      if (kind == svn_node_none)
1785251881Speter        {
1786251881Speter          fb->shadowed = TRUE;
1787251881Speter
1788251881Speter          /* If this is not the merge target and the parent is too shallow to
1789251881Speter             contain this directory, and the directory is not present
1790251881Speter             via exclusion or depth filtering, skip it instead of recording
1791251881Speter             a tree conflict.
1792251881Speter
1793251881Speter             Non-inheritable mergeinfo will be recorded, allowing
1794251881Speter             future merges into non-shallow working copies to merge
1795251881Speter             changes we missed this time around. */
1796251881Speter          if (pdb && (excluded
1797251881Speter                      || (parent_depth != svn_depth_unknown &&
1798251881Speter                          parent_depth < svn_depth_files)))
1799251881Speter            {
1800251881Speter                fb->shadowed = TRUE;
1801251881Speter
1802251881Speter                fb->tree_conflict_reason = CONFLICT_REASON_SKIP;
1803251881Speter                fb->skip_reason = svn_wc_notify_state_missing;
1804251881Speter                return SVN_NO_ERROR;
1805251881Speter            }
1806251881Speter
1807251881Speter          if (is_deleted)
1808251881Speter            fb->tree_conflict_reason = svn_wc_conflict_reason_deleted;
1809251881Speter          else
1810251881Speter            fb->tree_conflict_reason = svn_wc_conflict_reason_missing;
1811251881Speter
1812251881Speter          /* ### Similar to directory */
1813251881Speter          *skip = TRUE;
1814251881Speter          SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
1815251881Speter          return SVN_NO_ERROR;
1816251881Speter          /* ### /Similar */
1817251881Speter        }
1818251881Speter      else if (kind != svn_node_file)
1819251881Speter        {
1820251881Speter          fb->shadowed = TRUE;
1821251881Speter
1822251881Speter          fb->tree_conflict_reason = svn_wc_conflict_reason_obstructed;
1823251881Speter
1824251881Speter          /* ### Similar to directory */
1825251881Speter          *skip = TRUE;
1826251881Speter          SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
1827251881Speter          return SVN_NO_ERROR;
1828251881Speter          /* ### /Similar */
1829251881Speter        }
1830251881Speter
1831251881Speter      if (! right_source)
1832251881Speter        {
1833251881Speter          /* We want to delete the directory */
1834251881Speter          fb->tree_conflict_action = svn_wc_conflict_action_delete;
1835251881Speter          SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
1836251881Speter
1837251881Speter          if (fb->shadowed)
1838251881Speter            {
1839251881Speter              return SVN_NO_ERROR; /* Already set a tree conflict */
1840251881Speter            }
1841251881Speter
1842251881Speter          /* Comparison mode to verify for delete tree conflicts? */
1843251881Speter          if (pdb && pdb->delete_state
1844251881Speter              && pdb->delete_state->found_edit)
1845251881Speter            {
1846251881Speter              /* Earlier nodes found a conflict. Done. */
1847251881Speter              *skip = TRUE;
1848251881Speter            }
1849251881Speter        }
1850251881Speter    }
1851251881Speter  else
1852251881Speter    {
1853251881Speter      const svn_wc_conflict_description2_t *old_tc = NULL;
1854251881Speter
1855251881Speter      /* The node doesn't exist pre-merge: We have an addition */
1856251881Speter      fb->added = TRUE;
1857251881Speter      fb->tree_conflict_action = svn_wc_conflict_action_add;
1858251881Speter
1859251881Speter      if (pdb && pdb->pending_deletes
1860251881Speter          && svn_hash_gets(pdb->pending_deletes, local_abspath))
1861251881Speter        {
1862251881Speter          fb->add_is_replace = TRUE;
1863251881Speter          fb->tree_conflict_action = svn_wc_conflict_action_replace;
1864251881Speter
1865251881Speter          svn_hash_sets(pdb->pending_deletes, local_abspath, NULL);
1866251881Speter        }
1867251881Speter
1868251881Speter      if (pdb
1869251881Speter          && pdb->new_tree_conflicts
1870251881Speter          && (old_tc = svn_hash_gets(pdb->new_tree_conflicts, local_abspath)))
1871251881Speter        {
1872251881Speter          fb->tree_conflict_action = svn_wc_conflict_action_replace;
1873251881Speter          fb->tree_conflict_reason = old_tc->reason;
1874251881Speter
1875251881Speter          /* Update the tree conflict to store that this is a replace */
1876251881Speter          SVN_ERR(record_tree_conflict(merge_b, local_abspath, pdb,
1877251881Speter                                       svn_node_file,
1878251881Speter                                       fb->tree_conflict_action,
1879251881Speter                                       fb->tree_conflict_reason,
1880251881Speter                                       old_tc, FALSE,
1881251881Speter                                       scratch_pool));
1882251881Speter
1883251881Speter          if (old_tc->reason == svn_wc_conflict_reason_deleted
1884251881Speter              || old_tc->reason == svn_wc_conflict_reason_moved_away)
1885251881Speter            {
1886251881Speter              /* Issue #3806: Incoming replacements on local deletes produce
1887251881Speter                 inconsistent result.
1888251881Speter
1889251881Speter                 In this specific case we can continue applying the add part
1890251881Speter                 of the replacement. */
1891251881Speter            }
1892251881Speter          else
1893251881Speter            {
1894251881Speter              *skip = TRUE;
1895251881Speter
1896251881Speter              return SVN_NO_ERROR;
1897251881Speter            }
1898251881Speter        }
1899251881Speter      else if (! (merge_b->dry_run
1900251881Speter                  && ((pdb && pdb->added) || fb->add_is_replace)))
1901251881Speter        {
1902251881Speter          svn_wc_notify_state_t obstr_state;
1903251881Speter          svn_node_kind_t kind;
1904251881Speter          svn_boolean_t is_deleted;
1905251881Speter
1906251881Speter          SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, NULL,
1907251881Speter                                            &kind, NULL,
1908251881Speter                                            merge_b, local_abspath,
1909251881Speter                                            scratch_pool));
1910251881Speter
1911251881Speter          if (obstr_state != svn_wc_notify_state_inapplicable)
1912251881Speter            {
1913251881Speter              /* Skip the obstruction */
1914251881Speter              fb->shadowed = TRUE;
1915251881Speter              fb->tree_conflict_reason = CONFLICT_REASON_SKIP;
1916251881Speter              fb->skip_reason = obstr_state;
1917251881Speter            }
1918251881Speter          else if (kind != svn_node_none && !is_deleted)
1919251881Speter            {
1920251881Speter              /* Set a tree conflict */
1921251881Speter              fb->shadowed = TRUE;
1922251881Speter              fb->tree_conflict_reason = svn_wc_conflict_reason_obstructed;
1923251881Speter            }
1924251881Speter        }
1925251881Speter
1926251881Speter      /* Handle pending conflicts */
1927251881Speter      SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
1928251881Speter    }
1929251881Speter
1930251881Speter  return SVN_NO_ERROR;
1931251881Speter}
1932251881Speter
1933251881Speter/* An svn_diff_tree_processor_t function.
1934251881Speter *
1935251881Speter * Called after merge_file_opened() when a node receives only text and/or
1936251881Speter * property changes between LEFT_SOURCE and RIGHT_SOURCE.
1937251881Speter *
1938251881Speter * left_file and right_file can be NULL when the file is not modified.
1939251881Speter * left_props and right_props are always available.
1940251881Speter */
1941251881Speterstatic svn_error_t *
1942251881Spetermerge_file_changed(const char *relpath,
1943251881Speter                  const svn_diff_source_t *left_source,
1944251881Speter                  const svn_diff_source_t *right_source,
1945251881Speter                  const char *left_file,
1946251881Speter                  const char *right_file,
1947251881Speter                  /*const*/ apr_hash_t *left_props,
1948251881Speter                  /*const*/ apr_hash_t *right_props,
1949251881Speter                  svn_boolean_t file_modified,
1950251881Speter                  const apr_array_header_t *prop_changes,
1951251881Speter                  void *file_baton,
1952251881Speter                  const struct svn_diff_tree_processor_t *processor,
1953251881Speter                  apr_pool_t *scratch_pool)
1954251881Speter{
1955251881Speter  merge_cmd_baton_t *merge_b = processor->baton;
1956251881Speter  struct merge_file_baton_t *fb = file_baton;
1957251881Speter  svn_client_ctx_t *ctx = merge_b->ctx;
1958251881Speter  const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
1959251881Speter                                              relpath, scratch_pool);
1960251881Speter  const svn_wc_conflict_version_t *left;
1961251881Speter  const svn_wc_conflict_version_t *right;
1962251881Speter  svn_wc_notify_state_t text_state;
1963251881Speter  svn_wc_notify_state_t property_state;
1964251881Speter
1965251881Speter  SVN_ERR_ASSERT(local_abspath && svn_dirent_is_absolute(local_abspath));
1966251881Speter  SVN_ERR_ASSERT(!left_file || svn_dirent_is_absolute(left_file));
1967251881Speter  SVN_ERR_ASSERT(!right_file || svn_dirent_is_absolute(right_file));
1968251881Speter
1969251881Speter  SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
1970251881Speter
1971251881Speter  if (fb->shadowed)
1972251881Speter    {
1973251881Speter      if (fb->tree_conflict_reason == CONFLICT_REASON_NONE)
1974251881Speter        {
1975251881Speter          /* We haven't notified for this node yet: report a skip */
1976251881Speter          SVN_ERR(record_skip(merge_b, local_abspath, svn_node_file,
1977251881Speter                              svn_wc_notify_update_shadowed_update,
1978251881Speter                              fb->skip_reason, scratch_pool));
1979251881Speter        }
1980251881Speter
1981251881Speter      return SVN_NO_ERROR;
1982251881Speter    }
1983251881Speter
1984251881Speter  /* This callback is essentially no more than a wrapper around
1985251881Speter     svn_wc_merge5().  Thank goodness that all the
1986251881Speter     diff-editor-mechanisms are doing the hard work of getting the
1987251881Speter     fulltexts! */
1988251881Speter
1989251881Speter  property_state = svn_wc_notify_state_unchanged;
1990251881Speter  text_state = svn_wc_notify_state_unchanged;
1991251881Speter
1992251881Speter  SVN_ERR(prepare_merge_props_changed(&prop_changes, local_abspath,
1993251881Speter                                      prop_changes, merge_b,
1994251881Speter                                      scratch_pool, scratch_pool));
1995251881Speter
1996251881Speter  SVN_ERR(make_conflict_versions(&left, &right, local_abspath,
1997251881Speter                                 svn_node_file, &merge_b->merge_source, merge_b->target,
1998251881Speter                                 scratch_pool, scratch_pool));
1999251881Speter
2000251881Speter  /* Do property merge now, if we are not going to perform a text merge */
2001251881Speter  if ((merge_b->record_only || !left_file) && prop_changes->nelts)
2002251881Speter    {
2003251881Speter      SVN_ERR(svn_wc_merge_props3(&property_state, ctx->wc_ctx, local_abspath,
2004251881Speter                                  left, right,
2005251881Speter                                  left_props, prop_changes,
2006251881Speter                                  merge_b->dry_run,
2007251881Speter                                  NULL, NULL,
2008251881Speter                                  ctx->cancel_func, ctx->cancel_baton,
2009251881Speter                                  scratch_pool));
2010251881Speter      if (property_state == svn_wc_notify_state_conflicted)
2011251881Speter        {
2012251881Speter          alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
2013251881Speter                               merge_b->pool);
2014251881Speter        }
2015251881Speter    }
2016251881Speter
2017251881Speter  /* Easy out: We are only applying mergeinfo differences. */
2018251881Speter  if (merge_b->record_only)
2019251881Speter    {
2020251881Speter      /* NO-OP */
2021251881Speter    }
2022251881Speter  else if (left_file)
2023251881Speter    {
2024251881Speter      svn_boolean_t has_local_mods;
2025251881Speter      enum svn_wc_merge_outcome_t content_outcome;
2026251881Speter
2027251881Speter      /* xgettext: the '.working', '.merge-left.r%ld' and
2028251881Speter         '.merge-right.r%ld' strings are used to tag onto a file
2029251881Speter         name in case of a merge conflict */
2030251881Speter      const char *target_label = _(".working");
2031251881Speter      const char *left_label = apr_psprintf(scratch_pool,
2032251881Speter                                            _(".merge-left.r%ld"),
2033251881Speter                                            left_source->revision);
2034251881Speter      const char *right_label = apr_psprintf(scratch_pool,
2035251881Speter                                             _(".merge-right.r%ld"),
2036251881Speter                                             right_source->revision);
2037251881Speter
2038251881Speter      SVN_ERR(svn_wc_text_modified_p2(&has_local_mods, ctx->wc_ctx,
2039251881Speter                                      local_abspath, FALSE, scratch_pool));
2040251881Speter
2041251881Speter      /* Do property merge and text merge in one step so that keyword expansion
2042251881Speter         takes into account the new property values. */
2043251881Speter      SVN_ERR(svn_wc_merge5(&content_outcome, &property_state, ctx->wc_ctx,
2044251881Speter                            left_file, right_file, local_abspath,
2045251881Speter                            left_label, right_label, target_label,
2046251881Speter                            left, right,
2047251881Speter                            merge_b->dry_run, merge_b->diff3_cmd,
2048251881Speter                            merge_b->merge_options,
2049251881Speter                            left_props, prop_changes,
2050251881Speter                            NULL, NULL,
2051251881Speter                            ctx->cancel_func,
2052251881Speter                            ctx->cancel_baton,
2053251881Speter                            scratch_pool));
2054251881Speter
2055251881Speter      if (content_outcome == svn_wc_merge_conflict
2056251881Speter          || property_state == svn_wc_notify_state_conflicted)
2057251881Speter        {
2058251881Speter          alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
2059251881Speter                               merge_b->pool);
2060251881Speter        }
2061251881Speter
2062251881Speter      if (content_outcome == svn_wc_merge_conflict)
2063251881Speter        text_state = svn_wc_notify_state_conflicted;
2064251881Speter      else if (has_local_mods
2065251881Speter               && content_outcome != svn_wc_merge_unchanged)
2066251881Speter        text_state = svn_wc_notify_state_merged;
2067251881Speter      else if (content_outcome == svn_wc_merge_merged)
2068251881Speter        text_state = svn_wc_notify_state_changed;
2069251881Speter      else if (content_outcome == svn_wc_merge_no_merge)
2070251881Speter        text_state = svn_wc_notify_state_missing;
2071251881Speter      else /* merge_outcome == svn_wc_merge_unchanged */
2072251881Speter        text_state = svn_wc_notify_state_unchanged;
2073251881Speter    }
2074251881Speter
2075251881Speter  if (text_state == svn_wc_notify_state_conflicted
2076251881Speter      || text_state == svn_wc_notify_state_merged
2077251881Speter      || text_state == svn_wc_notify_state_changed
2078251881Speter      || property_state == svn_wc_notify_state_conflicted
2079251881Speter      || property_state == svn_wc_notify_state_merged
2080251881Speter      || property_state == svn_wc_notify_state_changed)
2081251881Speter    {
2082251881Speter      SVN_ERR(record_update_update(merge_b, local_abspath, svn_node_file,
2083251881Speter                                   text_state, property_state,
2084251881Speter                                   scratch_pool));
2085251881Speter    }
2086251881Speter
2087251881Speter  return SVN_NO_ERROR;
2088251881Speter}
2089251881Speter
2090251881Speter/* An svn_diff_tree_processor_t function.
2091251881Speter *
2092251881Speter * Called after merge_file_opened() when a node doesn't exist in LEFT_SOURCE,
2093251881Speter * but does in RIGHT_SOURCE.
2094251881Speter *
2095251881Speter * When a node is replaced instead of just added a separate opened+deleted will
2096251881Speter * be invoked before the current open+added.
2097251881Speter */
2098251881Speterstatic svn_error_t *
2099251881Spetermerge_file_added(const char *relpath,
2100251881Speter                 const svn_diff_source_t *copyfrom_source,
2101251881Speter                 const svn_diff_source_t *right_source,
2102251881Speter                 const char *copyfrom_file,
2103251881Speter                 const char *right_file,
2104251881Speter                 /*const*/ apr_hash_t *copyfrom_props,
2105251881Speter                 /*const*/ apr_hash_t *right_props,
2106251881Speter                 void *file_baton,
2107251881Speter                 const struct svn_diff_tree_processor_t *processor,
2108251881Speter                 apr_pool_t *scratch_pool)
2109251881Speter{
2110251881Speter  merge_cmd_baton_t *merge_b = processor->baton;
2111251881Speter  struct merge_file_baton_t *fb = file_baton;
2112251881Speter  const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
2113251881Speter                                              relpath, scratch_pool);
2114251881Speter  apr_hash_t *pristine_props;
2115251881Speter  apr_hash_t *new_props;
2116251881Speter
2117251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2118251881Speter
2119251881Speter  SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
2120251881Speter
2121251881Speter  if (fb->shadowed)
2122251881Speter    {
2123251881Speter      if (fb->tree_conflict_reason == CONFLICT_REASON_NONE)
2124251881Speter        {
2125251881Speter          /* We haven't notified for this node yet: report a skip */
2126251881Speter          SVN_ERR(record_skip(merge_b, local_abspath, svn_node_file,
2127251881Speter                              svn_wc_notify_update_shadowed_add,
2128251881Speter                              fb->skip_reason, scratch_pool));
2129251881Speter        }
2130251881Speter
2131251881Speter      return SVN_NO_ERROR;
2132251881Speter    }
2133251881Speter
2134251881Speter  /* Easy out: We are only applying mergeinfo differences. */
2135251881Speter  if (merge_b->record_only)
2136251881Speter    {
2137251881Speter      return SVN_NO_ERROR;
2138251881Speter    }
2139251881Speter
2140251881Speter  if ((merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
2141251881Speter      && ( !fb->parent_baton || !fb->parent_baton->added))
2142251881Speter    {
2143251881Speter      /* Store the roots of added subtrees */
2144251881Speter      store_path(merge_b->added_abspaths, local_abspath);
2145251881Speter    }
2146251881Speter
2147251881Speter  if (!merge_b->dry_run)
2148251881Speter    {
2149251881Speter      const char *copyfrom_url;
2150251881Speter      svn_revnum_t copyfrom_rev;
2151251881Speter      svn_stream_t *new_contents, *pristine_contents;
2152251881Speter
2153251881Speter      /* If this is a merge from the same repository as our
2154251881Speter         working copy, we handle adds as add-with-history.
2155251881Speter         Otherwise, we'll use a pure add. */
2156251881Speter      if (merge_b->same_repos)
2157251881Speter        {
2158251881Speter          const char *child =
2159251881Speter            svn_dirent_skip_ancestor(merge_b->target->abspath,
2160251881Speter                                     local_abspath);
2161251881Speter          SVN_ERR_ASSERT(child != NULL);
2162251881Speter          copyfrom_url = svn_path_url_add_component2(
2163251881Speter                                       merge_b->merge_source.loc2->url,
2164251881Speter                                       child, scratch_pool);
2165251881Speter          copyfrom_rev = right_source->revision;
2166251881Speter          SVN_ERR(check_repos_match(merge_b->target, local_abspath,
2167251881Speter                                    copyfrom_url, scratch_pool));
2168251881Speter          SVN_ERR(svn_stream_open_readonly(&pristine_contents,
2169251881Speter                                           right_file,
2170251881Speter                                           scratch_pool,
2171251881Speter                                           scratch_pool));
2172251881Speter          new_contents = NULL; /* inherit from new_base_contents */
2173251881Speter
2174251881Speter          pristine_props = right_props; /* Includes last_* information */
2175251881Speter          new_props = NULL; /* No local changes */
2176251881Speter
2177251881Speter          if (svn_hash_gets(pristine_props, SVN_PROP_MERGEINFO))
2178251881Speter            {
2179251881Speter              alloc_and_store_path(&merge_b->paths_with_new_mergeinfo,
2180251881Speter                                   local_abspath, merge_b->pool);
2181251881Speter            }
2182251881Speter        }
2183251881Speter      else
2184251881Speter        {
2185251881Speter          apr_array_header_t *regular_props;
2186251881Speter
2187251881Speter          copyfrom_url = NULL;
2188251881Speter          copyfrom_rev = SVN_INVALID_REVNUM;
2189251881Speter
2190251881Speter          pristine_contents = svn_stream_empty(scratch_pool);
2191251881Speter          SVN_ERR(svn_stream_open_readonly(&new_contents, right_file,
2192251881Speter                                           scratch_pool, scratch_pool));
2193251881Speter
2194251881Speter          pristine_props = apr_hash_make(scratch_pool); /* Local addition */
2195251881Speter
2196251881Speter          /* We don't want any foreign properties */
2197251881Speter          SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(right_props,
2198251881Speter                                                              scratch_pool),
2199251881Speter                                       NULL, NULL, &regular_props,
2200251881Speter                                       scratch_pool));
2201251881Speter
2202251881Speter          new_props = svn_prop_array_to_hash(regular_props, scratch_pool);
2203251881Speter
2204251881Speter          /* Issue #3383: We don't want mergeinfo from a foreign repository. */
2205251881Speter          svn_hash_sets(new_props, SVN_PROP_MERGEINFO, NULL);
2206251881Speter        }
2207251881Speter
2208251881Speter      /* Do everything like if we had called 'svn cp PATH1 PATH2'. */
2209251881Speter      SVN_ERR(svn_wc_add_repos_file4(merge_b->ctx->wc_ctx,
2210251881Speter                                      local_abspath,
2211251881Speter                                      pristine_contents,
2212251881Speter                                      new_contents,
2213251881Speter                                      pristine_props, new_props,
2214251881Speter                                      copyfrom_url, copyfrom_rev,
2215251881Speter                                      merge_b->ctx->cancel_func,
2216251881Speter                                      merge_b->ctx->cancel_baton,
2217251881Speter                                      scratch_pool));
2218251881Speter
2219251881Speter      /* Caller must call svn_sleep_for_timestamps() */
2220251881Speter      *merge_b->use_sleep = TRUE;
2221251881Speter    }
2222251881Speter
2223251881Speter  SVN_ERR(record_update_add(merge_b, local_abspath, svn_node_file,
2224251881Speter                            fb->add_is_replace, scratch_pool));
2225251881Speter
2226251881Speter  return SVN_NO_ERROR;
2227251881Speter}
2228251881Speter
2229251881Speter/* Compare the two sets of properties PROPS1 and PROPS2, ignoring the
2230251881Speter * "svn:mergeinfo" property, and noticing only "normal" props. Set *SAME to
2231251881Speter * true if the rest of the properties are identical or false if they differ.
2232251881Speter */
2233251881Speterstatic svn_error_t *
2234251881Speterproperties_same_p(svn_boolean_t *same,
2235251881Speter                  apr_hash_t *props1,
2236251881Speter                  apr_hash_t *props2,
2237251881Speter                  apr_pool_t *scratch_pool)
2238251881Speter{
2239251881Speter  apr_array_header_t *prop_changes;
2240251881Speter  int i, diffs;
2241251881Speter
2242251881Speter  /* Examine the properties that differ */
2243251881Speter  SVN_ERR(svn_prop_diffs(&prop_changes, props1, props2, scratch_pool));
2244251881Speter  diffs = 0;
2245251881Speter  for (i = 0; i < prop_changes->nelts; i++)
2246251881Speter    {
2247251881Speter      const char *pname = APR_ARRAY_IDX(prop_changes, i, svn_prop_t).name;
2248251881Speter
2249251881Speter      /* Count the properties we're interested in; ignore the rest */
2250251881Speter      if (svn_wc_is_normal_prop(pname)
2251251881Speter          && strcmp(pname, SVN_PROP_MERGEINFO) != 0)
2252251881Speter        diffs++;
2253251881Speter    }
2254251881Speter  *same = (diffs == 0);
2255251881Speter  return SVN_NO_ERROR;
2256251881Speter}
2257251881Speter
2258251881Speter/* Compare the file OLDER_ABSPATH (together with its normal properties in
2259251881Speter * ORIGINAL_PROPS which may also contain WC props and entry props) with the
2260251881Speter * versioned file MINE_ABSPATH (together with its versioned properties).
2261251881Speter * Set *SAME to true if they are the same or false if they differ, ignoring
2262251881Speter * the "svn:mergeinfo" property, and ignoring differences in keyword
2263251881Speter * expansion and end-of-line style. */
2264251881Speterstatic svn_error_t *
2265251881Speterfiles_same_p(svn_boolean_t *same,
2266251881Speter             const char *older_abspath,
2267251881Speter             apr_hash_t *original_props,
2268251881Speter             const char *mine_abspath,
2269251881Speter             svn_wc_context_t *wc_ctx,
2270251881Speter             apr_pool_t *scratch_pool)
2271251881Speter{
2272251881Speter  apr_hash_t *working_props;
2273251881Speter
2274251881Speter  SVN_ERR(svn_wc_prop_list2(&working_props, wc_ctx, mine_abspath,
2275251881Speter                            scratch_pool, scratch_pool));
2276251881Speter
2277251881Speter  /* Compare the properties */
2278251881Speter  SVN_ERR(properties_same_p(same, original_props, working_props,
2279251881Speter                            scratch_pool));
2280251881Speter  if (*same)
2281251881Speter    {
2282251881Speter      svn_stream_t *mine_stream;
2283251881Speter      svn_stream_t *older_stream;
2284251881Speter      svn_opt_revision_t working_rev = { svn_opt_revision_working, { 0 } };
2285251881Speter
2286251881Speter      /* Compare the file content, translating 'mine' to 'normal' form. */
2287251881Speter      if (svn_prop_get_value(working_props, SVN_PROP_SPECIAL) != NULL)
2288251881Speter        SVN_ERR(svn_subst_read_specialfile(&mine_stream, mine_abspath,
2289251881Speter                                           scratch_pool, scratch_pool));
2290251881Speter      else
2291251881Speter        SVN_ERR(svn_client__get_normalized_stream(&mine_stream, wc_ctx,
2292251881Speter                                                  mine_abspath, &working_rev,
2293251881Speter                                                  FALSE, TRUE, NULL, NULL,
2294251881Speter                                                  scratch_pool, scratch_pool));
2295251881Speter
2296251881Speter      SVN_ERR(svn_stream_open_readonly(&older_stream, older_abspath,
2297251881Speter                                       scratch_pool, scratch_pool));
2298251881Speter
2299251881Speter      SVN_ERR(svn_stream_contents_same2(same, mine_stream, older_stream,
2300251881Speter                                        scratch_pool));
2301251881Speter
2302251881Speter    }
2303251881Speter
2304251881Speter  return SVN_NO_ERROR;
2305251881Speter}
2306251881Speter
2307251881Speter/* An svn_diff_tree_processor_t function.
2308251881Speter *
2309251881Speter * Called after merge_file_opened() when a node does exist in LEFT_SOURCE, but
2310251881Speter * no longer exists (or is replaced) in RIGHT_SOURCE.
2311251881Speter *
2312251881Speter * When a node is replaced instead of just added a separate opened+added will
2313251881Speter * be invoked after the current open+deleted.
2314251881Speter */
2315251881Speterstatic svn_error_t *
2316251881Spetermerge_file_deleted(const char *relpath,
2317251881Speter                   const svn_diff_source_t *left_source,
2318251881Speter                   const char *left_file,
2319251881Speter                   /*const*/ apr_hash_t *left_props,
2320251881Speter                   void *file_baton,
2321251881Speter                   const struct svn_diff_tree_processor_t *processor,
2322251881Speter                   apr_pool_t *scratch_pool)
2323251881Speter{
2324251881Speter  merge_cmd_baton_t *merge_b = processor->baton;
2325251881Speter  struct merge_file_baton_t *fb = file_baton;
2326251881Speter  const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
2327251881Speter                                              relpath, scratch_pool);
2328251881Speter  svn_boolean_t same;
2329251881Speter
2330251881Speter  SVN_ERR(mark_file_edited(merge_b, fb, local_abspath, scratch_pool));
2331251881Speter
2332251881Speter  if (fb->shadowed)
2333251881Speter    {
2334251881Speter      if (fb->tree_conflict_reason == CONFLICT_REASON_NONE)
2335251881Speter        {
2336251881Speter          /* We haven't notified for this node yet: report a skip */
2337251881Speter          SVN_ERR(record_skip(merge_b, local_abspath, svn_node_file,
2338251881Speter                              svn_wc_notify_update_shadowed_delete,
2339251881Speter                              fb->skip_reason, scratch_pool));
2340251881Speter        }
2341251881Speter
2342251881Speter      return SVN_NO_ERROR;
2343251881Speter    }
2344251881Speter
2345251881Speter  /* Easy out: We are only applying mergeinfo differences. */
2346251881Speter  if (merge_b->record_only)
2347251881Speter    {
2348251881Speter      return SVN_NO_ERROR;
2349251881Speter    }
2350251881Speter
2351251881Speter  /* If the files are identical, attempt deletion */
2352251881Speter  if (merge_b->force_delete)
2353251881Speter    same = TRUE;
2354251881Speter  else
2355251881Speter    SVN_ERR(files_same_p(&same, left_file, left_props,
2356251881Speter                         local_abspath, merge_b->ctx->wc_ctx,
2357251881Speter                         scratch_pool));
2358251881Speter
2359251881Speter  if (fb->parent_baton
2360251881Speter      && fb->parent_baton->delete_state)
2361251881Speter    {
2362251881Speter      if (same)
2363251881Speter        {
2364251881Speter          /* Note that we checked this file */
2365251881Speter          store_path(fb->parent_baton->delete_state->compared_abspaths,
2366251881Speter                     local_abspath);
2367251881Speter        }
2368251881Speter      else
2369251881Speter        {
2370251881Speter          /* We found some modification. Parent should raise a tree conflict */
2371251881Speter          fb->parent_baton->delete_state->found_edit = TRUE;
2372251881Speter        }
2373251881Speter
2374251881Speter      return SVN_NO_ERROR;
2375251881Speter    }
2376251881Speter  else if (same)
2377251881Speter    {
2378251881Speter      if (!merge_b->dry_run)
2379251881Speter        SVN_ERR(svn_wc_delete4(merge_b->ctx->wc_ctx, local_abspath,
2380251881Speter                               FALSE /* keep_local */, FALSE /* unversioned */,
2381251881Speter                               merge_b->ctx->cancel_func,
2382251881Speter                               merge_b->ctx->cancel_baton,
2383251881Speter                               NULL, NULL /* no notify */,
2384251881Speter                               scratch_pool));
2385251881Speter
2386251881Speter      /* Record that we might have deleted mergeinfo */
2387251881Speter      alloc_and_store_path(&merge_b->paths_with_deleted_mergeinfo,
2388251881Speter                           local_abspath, merge_b->pool);
2389251881Speter
2390251881Speter      /* And notify the deletion */
2391251881Speter      SVN_ERR(record_update_delete(merge_b, fb->parent_baton, local_abspath,
2392251881Speter                                   svn_node_file, scratch_pool));
2393251881Speter    }
2394251881Speter  else
2395251881Speter    {
2396251881Speter      /* The files differ, so raise a conflict instead of deleting */
2397251881Speter
2398251881Speter      /* This is use case 5 described in the paper attached to issue
2399251881Speter       * #2282.  See also notes/tree-conflicts/detection.txt
2400251881Speter       */
2401251881Speter      SVN_ERR(record_tree_conflict(merge_b, local_abspath, fb->parent_baton,
2402251881Speter                                   svn_node_file,
2403251881Speter                                   svn_wc_conflict_action_delete,
2404251881Speter                                   svn_wc_conflict_reason_edited,
2405251881Speter                                   NULL, TRUE,
2406251881Speter                                   scratch_pool));
2407251881Speter    }
2408251881Speter
2409251881Speter  return SVN_NO_ERROR;
2410251881Speter}
2411251881Speter
2412251881Speter/* An svn_diff_tree_processor_t function.
2413251881Speter
2414251881Speter   Called before either merge_dir_changed(), merge_dir_added(),
2415251881Speter   merge_dir_deleted() or merge_dir_closed(), unless it sets *SKIP to TRUE.
2416251881Speter
2417251881Speter   After this call and before the close call, all descendants will receive
2418251881Speter   their changes, unless *SKIP_CHILDREN is set to TRUE.
2419251881Speter
2420251881Speter   When *SKIP is TRUE, the diff driver avoids work on getting the details
2421251881Speter   for the closing callbacks.
2422251881Speter
2423251881Speter   The SKIP and SKIP_DESCENDANTS work independantly.
2424251881Speter */
2425251881Speterstatic svn_error_t *
2426251881Spetermerge_dir_opened(void **new_dir_baton,
2427251881Speter                 svn_boolean_t *skip,
2428251881Speter                 svn_boolean_t *skip_children,
2429251881Speter                 const char *relpath,
2430251881Speter                 const svn_diff_source_t *left_source,
2431251881Speter                 const svn_diff_source_t *right_source,
2432251881Speter                 const svn_diff_source_t *copyfrom_source,
2433251881Speter                 void *parent_dir_baton,
2434251881Speter                 const struct svn_diff_tree_processor_t *processor,
2435251881Speter                 apr_pool_t *result_pool,
2436251881Speter                 apr_pool_t *scratch_pool)
2437251881Speter{
2438251881Speter  merge_cmd_baton_t *merge_b = processor->baton;
2439251881Speter  struct merge_dir_baton_t *db;
2440251881Speter  struct merge_dir_baton_t *pdb = parent_dir_baton;
2441251881Speter
2442251881Speter  const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
2443251881Speter                                              relpath, scratch_pool);
2444251881Speter
2445251881Speter  db = apr_pcalloc(result_pool, sizeof(*db));
2446251881Speter  db->pool = result_pool;
2447251881Speter  db->tree_conflict_reason = CONFLICT_REASON_NONE;
2448251881Speter  db->tree_conflict_action = svn_wc_conflict_action_edit;
2449251881Speter  db->skip_reason = svn_wc_notify_state_unknown;
2450251881Speter
2451251881Speter  *new_dir_baton = db;
2452251881Speter
2453251881Speter  if (pdb)
2454251881Speter    {
2455251881Speter      db->parent_baton = pdb;
2456251881Speter      db->shadowed = pdb->shadowed;
2457251881Speter      db->skip_reason = pdb->skip_reason;
2458251881Speter    }
2459251881Speter
2460251881Speter  if (db->shadowed)
2461251881Speter    {
2462251881Speter      /* An ancestor is tree conflicted. Nothing to do here. */
2463251881Speter      if (! left_source)
2464251881Speter        db->added = TRUE;
2465251881Speter    }
2466251881Speter  else if (left_source != NULL)
2467251881Speter    {
2468251881Speter      /* Node is expected to be a directory. */
2469251881Speter      svn_node_kind_t kind;
2470251881Speter      svn_boolean_t is_deleted;
2471251881Speter      svn_boolean_t excluded;
2472251881Speter      svn_depth_t parent_depth;
2473251881Speter
2474251881Speter      if (! right_source)
2475251881Speter          db->tree_conflict_action = svn_wc_conflict_action_delete;
2476251881Speter
2477251881Speter      /* Check for an obstructed or missing node on disk. */
2478251881Speter      {
2479251881Speter        svn_wc_notify_state_t obstr_state;
2480251881Speter        SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, &excluded,
2481251881Speter                                          &kind, &parent_depth,
2482251881Speter                                          merge_b, local_abspath,
2483251881Speter                                          scratch_pool));
2484251881Speter
2485251881Speter        if (obstr_state != svn_wc_notify_state_inapplicable)
2486251881Speter          {
2487251881Speter            db->shadowed = TRUE;
2488251881Speter
2489251881Speter            if (obstr_state == svn_wc_notify_state_obstructed)
2490251881Speter              {
2491251881Speter                svn_boolean_t is_wcroot;
2492251881Speter
2493251881Speter                SVN_ERR(svn_wc_check_root(&is_wcroot, NULL, NULL,
2494251881Speter                                        merge_b->ctx->wc_ctx,
2495251881Speter                                        local_abspath, scratch_pool));
2496251881Speter
2497251881Speter                if (is_wcroot)
2498251881Speter                  {
2499251881Speter                    db->tree_conflict_reason = CONFLICT_REASON_SKIP_WC;
2500251881Speter                    return SVN_NO_ERROR;
2501251881Speter                  }
2502251881Speter              }
2503251881Speter
2504251881Speter            db->tree_conflict_reason = CONFLICT_REASON_SKIP;
2505251881Speter            db->skip_reason = obstr_state;
2506251881Speter
2507251881Speter            if (! right_source)
2508251881Speter              {
2509251881Speter                *skip = *skip_children = TRUE;
2510251881Speter                SVN_ERR(mark_dir_edited(merge_b, db, local_abspath,
2511251881Speter                                        scratch_pool));
2512251881Speter              }
2513251881Speter
2514251881Speter            return SVN_NO_ERROR;
2515251881Speter          }
2516251881Speter
2517251881Speter        if (is_deleted)
2518251881Speter          kind = svn_node_none;
2519251881Speter      }
2520251881Speter
2521251881Speter      if (kind == svn_node_none)
2522251881Speter        {
2523251881Speter          db->shadowed = TRUE;
2524251881Speter
2525251881Speter          /* If this is not the merge target and the parent is too shallow to
2526251881Speter             contain this directory, and the directory is not presen
2527251881Speter             via exclusion or depth filtering, skip it instead of recording
2528251881Speter             a tree conflict.
2529251881Speter
2530251881Speter             Non-inheritable mergeinfo will be recorded, allowing
2531251881Speter             future merges into non-shallow working copies to merge
2532251881Speter             changes we missed this time around. */
2533251881Speter          if (pdb && (excluded
2534251881Speter                      || (parent_depth != svn_depth_unknown &&
2535251881Speter                          parent_depth < svn_depth_immediates)))
2536251881Speter            {
2537251881Speter              db->shadowed = TRUE;
2538251881Speter
2539251881Speter              db->tree_conflict_reason = CONFLICT_REASON_SKIP;
2540251881Speter              db->skip_reason = svn_wc_notify_state_missing;
2541251881Speter
2542251881Speter              return SVN_NO_ERROR;
2543251881Speter            }
2544251881Speter
2545251881Speter          if (is_deleted)
2546251881Speter            db->tree_conflict_reason = svn_wc_conflict_reason_deleted;
2547251881Speter          else
2548251881Speter            db->tree_conflict_reason = svn_wc_conflict_reason_missing;
2549251881Speter
2550251881Speter          /* ### To avoid breaking tests */
2551251881Speter          *skip = TRUE;
2552251881Speter          *skip_children = TRUE;
2553251881Speter          SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
2554251881Speter          return SVN_NO_ERROR;
2555251881Speter          /* ### /avoid breaking tests */
2556251881Speter        }
2557251881Speter      else if (kind != svn_node_dir)
2558251881Speter        {
2559251881Speter          db->shadowed = TRUE;
2560251881Speter
2561251881Speter          db->tree_conflict_reason = svn_wc_conflict_reason_obstructed;
2562251881Speter
2563251881Speter          /* ### To avoid breaking tests */
2564251881Speter          *skip = TRUE;
2565251881Speter          *skip_children = TRUE;
2566251881Speter          SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
2567251881Speter          return SVN_NO_ERROR;
2568251881Speter          /* ### /avoid breaking tests */
2569251881Speter        }
2570251881Speter
2571251881Speter      if (! right_source)
2572251881Speter        {
2573251881Speter          /* We want to delete the directory */
2574251881Speter          /* Mark PB edited now? */
2575251881Speter          db->tree_conflict_action = svn_wc_conflict_action_delete;
2576251881Speter          SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
2577251881Speter
2578251881Speter          if (db->shadowed)
2579251881Speter            {
2580251881Speter              *skip_children = TRUE;
2581251881Speter              return SVN_NO_ERROR; /* Already set a tree conflict */
2582251881Speter            }
2583251881Speter
2584251881Speter          db->delete_state = (pdb != NULL) ? pdb->delete_state : NULL;
2585251881Speter
2586251881Speter          if (db->delete_state && db->delete_state->found_edit)
2587251881Speter            {
2588251881Speter              /* A sibling found a conflict. Done. */
2589251881Speter              *skip = TRUE;
2590251881Speter              *skip_children = TRUE;
2591251881Speter            }
2592251881Speter          else if (merge_b->force_delete)
2593251881Speter            {
2594251881Speter              /* No comparison necessary */
2595251881Speter              *skip_children = TRUE;
2596251881Speter            }
2597251881Speter          else if (! db->delete_state)
2598251881Speter            {
2599251881Speter              /* Start descendant comparison */
2600251881Speter              db->delete_state = apr_pcalloc(db->pool,
2601251881Speter                                             sizeof(*db->delete_state));
2602251881Speter
2603251881Speter              db->delete_state->del_root = db;
2604251881Speter              db->delete_state->compared_abspaths = apr_hash_make(db->pool);
2605251881Speter            }
2606251881Speter        }
2607251881Speter    }
2608251881Speter  else
2609251881Speter    {
2610251881Speter      const svn_wc_conflict_description2_t *old_tc = NULL;
2611251881Speter
2612251881Speter      /* The node doesn't exist pre-merge: We have an addition */
2613251881Speter      db->added = TRUE;
2614251881Speter      db->tree_conflict_action = svn_wc_conflict_action_add;
2615251881Speter
2616251881Speter      if (pdb && pdb->pending_deletes
2617251881Speter          && svn_hash_gets(pdb->pending_deletes, local_abspath))
2618251881Speter        {
2619251881Speter          db->add_is_replace = TRUE;
2620251881Speter          db->tree_conflict_action = svn_wc_conflict_action_replace;
2621251881Speter
2622251881Speter          svn_hash_sets(pdb->pending_deletes, local_abspath, NULL);
2623251881Speter        }
2624251881Speter
2625251881Speter      if (pdb
2626251881Speter          && pdb->new_tree_conflicts
2627251881Speter          && (old_tc = svn_hash_gets(pdb->new_tree_conflicts, local_abspath)))
2628251881Speter        {
2629251881Speter          db->tree_conflict_action = svn_wc_conflict_action_replace;
2630251881Speter          db->tree_conflict_reason = old_tc->reason;
2631251881Speter
2632251881Speter          if (old_tc->reason == svn_wc_conflict_reason_deleted
2633251881Speter             || old_tc->reason == svn_wc_conflict_reason_moved_away)
2634251881Speter            {
2635251881Speter              /* Issue #3806: Incoming replacements on local deletes produce
2636251881Speter                 inconsistent result.
2637251881Speter
2638251881Speter                 In this specific case we can continue applying the add part
2639251881Speter                 of the replacement. */
2640251881Speter            }
2641251881Speter          else
2642251881Speter            {
2643251881Speter              *skip = TRUE;
2644251881Speter              *skip_children = TRUE;
2645251881Speter
2646251881Speter              /* Update the tree conflict to store that this is a replace */
2647251881Speter              SVN_ERR(record_tree_conflict(merge_b, local_abspath, pdb,
2648251881Speter                                           svn_node_dir,
2649251881Speter                                           db->tree_conflict_action,
2650251881Speter                                           db->tree_conflict_reason,
2651251881Speter                                           old_tc, FALSE,
2652251881Speter                                           scratch_pool));
2653251881Speter
2654251881Speter              return SVN_NO_ERROR;
2655251881Speter            }
2656251881Speter        }
2657251881Speter
2658251881Speter      if (! (merge_b->dry_run
2659251881Speter             && ((pdb && pdb->added) || db->add_is_replace)))
2660251881Speter        {
2661251881Speter          svn_wc_notify_state_t obstr_state;
2662251881Speter          svn_node_kind_t kind;
2663251881Speter          svn_boolean_t is_deleted;
2664251881Speter
2665251881Speter          SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, NULL,
2666251881Speter                                            &kind, NULL,
2667251881Speter                                            merge_b, local_abspath,
2668251881Speter                                            scratch_pool));
2669251881Speter
2670251881Speter          /* In this case of adding a directory, we have an exception to the
2671251881Speter           * usual "skip if it's inconsistent" rule. If the directory exists
2672251881Speter           * on disk unexpectedly, we simply make it versioned, because we can
2673251881Speter           * do so without risk of destroying data. Only skip if it is
2674251881Speter           * versioned but unexpectedly missing from disk, or is unversioned
2675251881Speter           * but obstructed by a node of the wrong kind. */
2676251881Speter          if (obstr_state == svn_wc_notify_state_obstructed
2677251881Speter              && (is_deleted || kind == svn_node_none))
2678251881Speter            {
2679251881Speter              svn_node_kind_t disk_kind;
2680251881Speter
2681251881Speter              SVN_ERR(svn_io_check_path(local_abspath, &disk_kind,
2682251881Speter                                        scratch_pool));
2683251881Speter
2684251881Speter              if (disk_kind == svn_node_dir)
2685251881Speter                {
2686251881Speter                  obstr_state = svn_wc_notify_state_inapplicable;
2687251881Speter                  db->add_existing = TRUE; /* Take over existing directory */
2688251881Speter                }
2689251881Speter            }
2690251881Speter
2691251881Speter          if (obstr_state != svn_wc_notify_state_inapplicable)
2692251881Speter            {
2693251881Speter              /* Skip the obstruction */
2694251881Speter              db->shadowed = TRUE;
2695251881Speter              db->tree_conflict_reason = CONFLICT_REASON_SKIP;
2696251881Speter              db->skip_reason = obstr_state;
2697251881Speter            }
2698251881Speter          else if (kind != svn_node_none && !is_deleted)
2699251881Speter            {
2700251881Speter              /* Set a tree conflict */
2701251881Speter              db->shadowed = TRUE;
2702251881Speter              db->tree_conflict_reason = svn_wc_conflict_reason_obstructed;
2703251881Speter            }
2704251881Speter        }
2705251881Speter
2706251881Speter      /* Handle pending conflicts */
2707251881Speter      SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
2708251881Speter
2709251881Speter      if (db->shadowed)
2710251881Speter        {
2711251881Speter          /* Notified and done. Skip children? */
2712251881Speter        }
2713251881Speter      else if (merge_b->record_only)
2714251881Speter        {
2715251881Speter          /* Ok, we are done for this node and its descendants */
2716251881Speter          *skip = TRUE;
2717251881Speter          *skip_children = TRUE;
2718251881Speter        }
2719251881Speter      else if (! merge_b->dry_run)
2720251881Speter        {
2721251881Speter          /* Create the directory on disk, to allow descendants to be added */
2722251881Speter          if (! db->add_existing)
2723251881Speter            SVN_ERR(svn_io_dir_make(local_abspath, APR_OS_DEFAULT,
2724251881Speter                                    scratch_pool));
2725251881Speter
2726251881Speter          if (old_tc)
2727251881Speter            {
2728251881Speter              /* svn_wc_add4 and svn_wc_add_from_disk2 can't add a node
2729251881Speter                 over an existing tree conflict */
2730251881Speter
2731251881Speter              /* ### These functions should take some tree conflict argument
2732251881Speter                     and allow overwriting the tc when one is passed */
2733251881Speter
2734251881Speter              SVN_ERR(svn_wc__del_tree_conflict(merge_b->ctx->wc_ctx,
2735251881Speter                                                local_abspath,
2736251881Speter                                                scratch_pool));
2737251881Speter            }
2738251881Speter
2739251881Speter          if (merge_b->same_repos)
2740251881Speter            {
2741251881Speter              const char *original_url;
2742251881Speter
2743251881Speter              original_url = svn_path_url_add_component2(
2744251881Speter                                        merge_b->merge_source.loc2->url,
2745251881Speter                                        relpath, scratch_pool);
2746251881Speter
2747251881Speter              /* Limitation (aka HACK):
2748251881Speter                 We create a newly added directory with an original URL and
2749251881Speter                 revision as that in the repository, but without its properties
2750251881Speter                 and children.
2751251881Speter
2752251881Speter                 When the merge is cancelled before the final dir_added(), the
2753251881Speter                 copy won't really represent the in-repository state of the node.
2754251881Speter               */
2755251881Speter              SVN_ERR(svn_wc_add4(merge_b->ctx->wc_ctx, local_abspath,
2756251881Speter                                  svn_depth_infinity,
2757251881Speter                                  original_url,
2758251881Speter                                  right_source->revision,
2759251881Speter                                  merge_b->ctx->cancel_func,
2760251881Speter                                  merge_b->ctx->cancel_baton,
2761251881Speter                                  NULL, NULL /* no notify! */,
2762251881Speter                                  scratch_pool));
2763251881Speter            }
2764251881Speter          else
2765251881Speter            {
2766251881Speter              SVN_ERR(svn_wc_add_from_disk2(merge_b->ctx->wc_ctx, local_abspath,
2767251881Speter                                            apr_hash_make(scratch_pool),
2768251881Speter                                            NULL, NULL /* no notify! */,
2769251881Speter                                            scratch_pool));
2770251881Speter            }
2771251881Speter
2772251881Speter          if (old_tc != NULL)
2773251881Speter            {
2774251881Speter              /* ### Should be atomic with svn_wc_add(4|_from_disk2)() */
2775251881Speter              SVN_ERR(record_tree_conflict(merge_b, local_abspath, pdb,
2776251881Speter                                           svn_node_dir,
2777251881Speter                                           db->tree_conflict_action,
2778251881Speter                                           db->tree_conflict_reason,
2779251881Speter                                           old_tc, FALSE,
2780251881Speter                                           scratch_pool));
2781251881Speter            }
2782251881Speter        }
2783251881Speter
2784251881Speter      if (! db->shadowed && !merge_b->record_only)
2785251881Speter        SVN_ERR(record_update_add(merge_b, local_abspath, svn_node_dir,
2786251881Speter                                  db->add_is_replace, scratch_pool));
2787251881Speter    }
2788251881Speter  return SVN_NO_ERROR;
2789251881Speter}
2790251881Speter
2791251881Speter/* An svn_diff_tree_processor_t function.
2792251881Speter *
2793251881Speter * Called after merge_dir_opened() when a node exists in both the left and
2794251881Speter * right source, but has its properties changed inbetween.
2795251881Speter *
2796251881Speter * After the merge_dir_opened() but before the call to this merge_dir_changed()
2797251881Speter * function all descendants will have been updated.
2798251881Speter */
2799251881Speterstatic svn_error_t *
2800251881Spetermerge_dir_changed(const char *relpath,
2801251881Speter                  const svn_diff_source_t *left_source,
2802251881Speter                  const svn_diff_source_t *right_source,
2803251881Speter                  /*const*/ apr_hash_t *left_props,
2804251881Speter                  /*const*/ apr_hash_t *right_props,
2805251881Speter                  const apr_array_header_t *prop_changes,
2806251881Speter                  void *dir_baton,
2807251881Speter                  const struct svn_diff_tree_processor_t *processor,
2808251881Speter                  apr_pool_t *scratch_pool)
2809251881Speter{
2810251881Speter  merge_cmd_baton_t *merge_b = processor->baton;
2811251881Speter  struct merge_dir_baton_t *db = dir_baton;
2812251881Speter  const apr_array_header_t *props;
2813251881Speter  const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
2814251881Speter                                              relpath, scratch_pool);
2815251881Speter
2816251881Speter  SVN_ERR(handle_pending_notifications(merge_b, db, scratch_pool));
2817251881Speter
2818251881Speter  SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
2819251881Speter
2820251881Speter  if (db->shadowed)
2821251881Speter    {
2822251881Speter      if (db->tree_conflict_reason == CONFLICT_REASON_NONE)
2823251881Speter        {
2824251881Speter          /* We haven't notified for this node yet: report a skip */
2825251881Speter          SVN_ERR(record_skip(merge_b, local_abspath, svn_node_dir,
2826251881Speter                              svn_wc_notify_update_shadowed_update,
2827251881Speter                              db->skip_reason, scratch_pool));
2828251881Speter        }
2829251881Speter
2830251881Speter      return SVN_NO_ERROR;
2831251881Speter    }
2832251881Speter
2833251881Speter  SVN_ERR(prepare_merge_props_changed(&props, local_abspath, prop_changes,
2834251881Speter                                      merge_b, scratch_pool, scratch_pool));
2835251881Speter
2836251881Speter  if (props->nelts)
2837251881Speter    {
2838251881Speter      const svn_wc_conflict_version_t *left;
2839251881Speter      const svn_wc_conflict_version_t *right;
2840251881Speter      svn_client_ctx_t *ctx = merge_b->ctx;
2841251881Speter      svn_wc_notify_state_t prop_state;
2842251881Speter
2843251881Speter      SVN_ERR(make_conflict_versions(&left, &right, local_abspath,
2844251881Speter                                     svn_node_dir, &merge_b->merge_source,
2845251881Speter                                     merge_b->target,
2846251881Speter                                     scratch_pool, scratch_pool));
2847251881Speter
2848251881Speter      SVN_ERR(svn_wc_merge_props3(&prop_state, ctx->wc_ctx, local_abspath,
2849251881Speter                                  left, right,
2850251881Speter                                  left_props, props,
2851251881Speter                                  merge_b->dry_run,
2852251881Speter                                  NULL, NULL,
2853251881Speter                                  ctx->cancel_func, ctx->cancel_baton,
2854251881Speter                                  scratch_pool));
2855251881Speter
2856251881Speter      if (prop_state == svn_wc_notify_state_conflicted)
2857251881Speter        {
2858251881Speter          alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
2859251881Speter                               merge_b->pool);
2860251881Speter        }
2861251881Speter
2862251881Speter      if (prop_state == svn_wc_notify_state_conflicted
2863251881Speter          || prop_state == svn_wc_notify_state_merged
2864251881Speter          || prop_state == svn_wc_notify_state_changed)
2865251881Speter        {
2866251881Speter          SVN_ERR(record_update_update(merge_b, local_abspath, svn_node_file,
2867251881Speter                                       svn_wc_notify_state_inapplicable,
2868251881Speter                                       prop_state, scratch_pool));
2869251881Speter        }
2870251881Speter    }
2871251881Speter
2872251881Speter  return SVN_NO_ERROR;
2873251881Speter}
2874251881Speter
2875251881Speter
2876251881Speter/* An svn_diff_tree_processor_t function.
2877251881Speter *
2878251881Speter * Called after merge_dir_opened() when a node doesn't exist in LEFT_SOURCE,
2879251881Speter * but does in RIGHT_SOURCE. After the merge_dir_opened() but before the call
2880251881Speter * to this merge_dir_added() function all descendants will have been added.
2881251881Speter *
2882251881Speter * When a node is replaced instead of just added a separate opened+deleted will
2883251881Speter * be invoked before the current open+added.
2884251881Speter */
2885251881Speterstatic svn_error_t *
2886251881Spetermerge_dir_added(const char *relpath,
2887251881Speter                const svn_diff_source_t *copyfrom_source,
2888251881Speter                const svn_diff_source_t *right_source,
2889251881Speter                /*const*/ apr_hash_t *copyfrom_props,
2890251881Speter                /*const*/ apr_hash_t *right_props,
2891251881Speter                void *dir_baton,
2892251881Speter                const struct svn_diff_tree_processor_t *processor,
2893251881Speter                apr_pool_t *scratch_pool)
2894251881Speter{
2895251881Speter  merge_cmd_baton_t *merge_b = processor->baton;
2896251881Speter  struct merge_dir_baton_t *db = dir_baton;
2897251881Speter  const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
2898251881Speter                                              relpath, scratch_pool);
2899251881Speter
2900251881Speter  /* For consistency; usually a no-op from _dir_added() */
2901251881Speter  SVN_ERR(handle_pending_notifications(merge_b, db, scratch_pool));
2902251881Speter  SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
2903251881Speter
2904251881Speter  if (db->shadowed)
2905251881Speter    {
2906251881Speter      if (db->tree_conflict_reason == CONFLICT_REASON_NONE)
2907251881Speter        {
2908251881Speter          /* We haven't notified for this node yet: report a skip */
2909251881Speter          SVN_ERR(record_skip(merge_b, local_abspath, svn_node_dir,
2910251881Speter                              svn_wc_notify_update_shadowed_add,
2911251881Speter                              db->skip_reason, scratch_pool));
2912251881Speter        }
2913251881Speter
2914251881Speter      return SVN_NO_ERROR;
2915251881Speter    }
2916251881Speter
2917251881Speter  SVN_ERR_ASSERT(
2918251881Speter                 db->edited                  /* Marked edited from merge_open_dir() */
2919251881Speter                 && ! merge_b->record_only /* Skip details from merge_open_dir() */
2920251881Speter                 );
2921251881Speter
2922251881Speter  if ((merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
2923251881Speter      && ( !db->parent_baton || !db->parent_baton->added))
2924251881Speter    {
2925251881Speter      /* Store the roots of added subtrees */
2926251881Speter      store_path(merge_b->added_abspaths, local_abspath);
2927251881Speter    }
2928251881Speter
2929251881Speter  if (merge_b->same_repos)
2930251881Speter    {
2931251881Speter      /* When the directory was added in merge_dir_added() we didn't update its
2932251881Speter         pristine properties. Instead we receive the property changes later and
2933251881Speter         apply them in this function.
2934251881Speter
2935251881Speter         If we would apply them as changes (such as before fixing issue #3405),
2936251881Speter         we would see the unmodified properties as local changes, and the
2937251881Speter         pristine properties would be out of sync with what the repository
2938251881Speter         expects for this directory.
2939251881Speter
2940251881Speter         Instead of doing that we now simply set the properties as the pristine
2941251881Speter         properties via a private libsvn_wc api.
2942251881Speter      */
2943251881Speter
2944251881Speter      const char *copyfrom_url;
2945251881Speter      svn_revnum_t copyfrom_rev;
2946251881Speter      const char *parent_abspath;
2947251881Speter      const char *child;
2948251881Speter
2949251881Speter      /* Creating a hash containing regular and entry props */
2950251881Speter      apr_hash_t *new_pristine_props = right_props;
2951251881Speter
2952251881Speter      parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
2953251881Speter      child = svn_dirent_is_child(merge_b->target->abspath, local_abspath, NULL);
2954251881Speter      SVN_ERR_ASSERT(child != NULL);
2955251881Speter
2956251881Speter      copyfrom_url = svn_path_url_add_component2(merge_b->merge_source.loc2->url,
2957251881Speter                                                 child, scratch_pool);
2958251881Speter      copyfrom_rev = right_source->revision;
2959251881Speter
2960251881Speter      SVN_ERR(check_repos_match(merge_b->target, parent_abspath, copyfrom_url,
2961251881Speter                                scratch_pool));
2962251881Speter
2963251881Speter      if (!merge_b->dry_run)
2964251881Speter        {
2965251881Speter          SVN_ERR(svn_wc__complete_directory_add(merge_b->ctx->wc_ctx,
2966251881Speter                                                local_abspath,
2967251881Speter                                                new_pristine_props,
2968251881Speter                                                copyfrom_url, copyfrom_rev,
2969251881Speter                                                scratch_pool));
2970251881Speter        }
2971251881Speter
2972251881Speter      if (svn_hash_gets(new_pristine_props, SVN_PROP_MERGEINFO))
2973251881Speter        {
2974251881Speter          alloc_and_store_path(&merge_b->paths_with_new_mergeinfo,
2975251881Speter                               local_abspath, merge_b->pool);
2976251881Speter        }
2977251881Speter    }
2978251881Speter  else
2979251881Speter    {
2980251881Speter      apr_array_header_t *regular_props;
2981251881Speter      apr_hash_t *new_props;
2982251881Speter      svn_wc_notify_state_t prop_state;
2983251881Speter
2984251881Speter      SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(right_props,
2985251881Speter                                                          scratch_pool),
2986251881Speter                                   NULL, NULL, &regular_props, scratch_pool));
2987251881Speter
2988251881Speter      new_props = svn_prop_array_to_hash(regular_props, scratch_pool);
2989251881Speter
2990251881Speter      svn_hash_sets(new_props, SVN_PROP_MERGEINFO, NULL);
2991251881Speter
2992251881Speter      /* ### What is the easiest way to set new_props on LOCAL_ABSPATH?
2993251881Speter
2994251881Speter         ### This doesn't need a merge as we just added the node
2995251881Speter         ### (or installed a tree conflict and skipped this node)*/
2996251881Speter
2997251881Speter      SVN_ERR(svn_wc_merge_props3(&prop_state, merge_b->ctx->wc_ctx,
2998251881Speter                                  local_abspath,
2999251881Speter                                  NULL, NULL,
3000251881Speter                                  apr_hash_make(scratch_pool),
3001251881Speter                                  svn_prop_hash_to_array(new_props,
3002251881Speter                                                         scratch_pool),
3003251881Speter                                  merge_b->dry_run,
3004251881Speter                                  NULL, NULL,
3005251881Speter                                  merge_b->ctx->cancel_func,
3006251881Speter                                  merge_b->ctx->cancel_baton,
3007251881Speter                                  scratch_pool));
3008251881Speter      if (prop_state == svn_wc_notify_state_conflicted)
3009251881Speter        {
3010251881Speter          alloc_and_store_path(&merge_b->conflicted_paths, local_abspath,
3011251881Speter                               merge_b->pool);
3012251881Speter        }
3013251881Speter    }
3014251881Speter
3015251881Speter  return SVN_NO_ERROR;
3016251881Speter}
3017251881Speter
3018251881Speter/* Helper for merge_dir_deleted. Implement svn_wc_status_func4_t */
3019251881Speterstatic svn_error_t *
3020251881Speterverify_touched_by_del_check(void *baton,
3021251881Speter                            const char *local_abspath,
3022251881Speter                            const svn_wc_status3_t *status,
3023251881Speter                            apr_pool_t *scratch_pool)
3024251881Speter{
3025251881Speter  struct dir_delete_baton_t *delb = baton;
3026251881Speter
3027251881Speter  if (svn_hash_gets(delb->compared_abspaths, local_abspath))
3028251881Speter    return SVN_NO_ERROR;
3029251881Speter
3030251881Speter  switch (status->node_status)
3031251881Speter    {
3032251881Speter      case svn_wc_status_deleted:
3033251881Speter      case svn_wc_status_ignored:
3034251881Speter      case svn_wc_status_none:
3035251881Speter        return SVN_NO_ERROR;
3036251881Speter
3037251881Speter      default:
3038251881Speter        delb->found_edit = TRUE;
3039251881Speter        return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
3040251881Speter    }
3041251881Speter}
3042251881Speter
3043251881Speter/* An svn_diff_tree_processor_t function.
3044251881Speter *
3045251881Speter * Called after merge_dir_opened() when a node existed only in the left source.
3046251881Speter *
3047251881Speter * After the merge_dir_opened() but before the call to this merge_dir_deleted()
3048251881Speter * function all descendants that existed in left_source will have been deleted.
3049251881Speter *
3050251881Speter * If this node is replaced, an _opened() followed by a matching _add() will
3051251881Speter * be invoked after this function.
3052251881Speter */
3053251881Speterstatic svn_error_t *
3054251881Spetermerge_dir_deleted(const char *relpath,
3055251881Speter                  const svn_diff_source_t *left_source,
3056251881Speter                  /*const*/ apr_hash_t *left_props,
3057251881Speter                  void *dir_baton,
3058251881Speter                  const struct svn_diff_tree_processor_t *processor,
3059251881Speter                  apr_pool_t *scratch_pool)
3060251881Speter{
3061251881Speter  merge_cmd_baton_t *merge_b = processor->baton;
3062251881Speter  struct merge_dir_baton_t *db = dir_baton;
3063251881Speter  const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
3064251881Speter                                              relpath, scratch_pool);
3065251881Speter  struct dir_delete_baton_t *delb;
3066251881Speter  svn_boolean_t same;
3067251881Speter  apr_hash_t *working_props;
3068251881Speter
3069251881Speter  SVN_ERR(handle_pending_notifications(merge_b, db, scratch_pool));
3070251881Speter  SVN_ERR(mark_dir_edited(merge_b, db, local_abspath, scratch_pool));
3071251881Speter
3072251881Speter  if (db->shadowed)
3073251881Speter    {
3074251881Speter      if (db->tree_conflict_reason == CONFLICT_REASON_NONE)
3075251881Speter        {
3076251881Speter          /* We haven't notified for this node yet: report a skip */
3077251881Speter          SVN_ERR(record_skip(merge_b, local_abspath, svn_node_dir,
3078251881Speter                              svn_wc_notify_update_shadowed_delete,
3079251881Speter                              db->skip_reason, scratch_pool));
3080251881Speter        }
3081251881Speter
3082251881Speter      return SVN_NO_ERROR;
3083251881Speter    }
3084251881Speter
3085251881Speter  /* Easy out: We are only applying mergeinfo differences. */
3086251881Speter  if (merge_b->record_only)
3087251881Speter    {
3088251881Speter      return SVN_NO_ERROR;
3089251881Speter    }
3090251881Speter
3091251881Speter  SVN_ERR(svn_wc_prop_list2(&working_props,
3092251881Speter                            merge_b->ctx->wc_ctx, local_abspath,
3093251881Speter                            scratch_pool, scratch_pool));
3094251881Speter
3095251881Speter  if (merge_b->force_delete)
3096251881Speter    same = TRUE;
3097251881Speter  else
3098251881Speter    {
3099251881Speter      /* Compare the properties */
3100251881Speter      SVN_ERR(properties_same_p(&same, left_props, working_props,
3101251881Speter                                scratch_pool));
3102251881Speter    }
3103251881Speter
3104251881Speter  delb = db->delete_state;
3105251881Speter  assert(delb != NULL);
3106251881Speter
3107251881Speter  if (! same)
3108251881Speter    {
3109251881Speter      delb->found_edit = TRUE;
3110251881Speter    }
3111251881Speter  else
3112251881Speter    {
3113251881Speter      store_path(delb->compared_abspaths, local_abspath);
3114251881Speter    }
3115251881Speter
3116251881Speter  if (delb->del_root != db)
3117251881Speter    return SVN_NO_ERROR;
3118251881Speter
3119251881Speter  if (delb->found_edit)
3120251881Speter    same = FALSE;
3121251881Speter  else if (merge_b->force_delete)
3122251881Speter    same = TRUE;
3123251881Speter  else
3124251881Speter    {
3125251881Speter      apr_array_header_t *ignores;
3126251881Speter      svn_error_t *err;
3127251881Speter      same = TRUE;
3128251881Speter
3129251881Speter      SVN_ERR(svn_wc_get_default_ignores(&ignores, merge_b->ctx->config,
3130251881Speter                                         scratch_pool));
3131251881Speter
3132251881Speter      /* None of the descendants was modified, but maybe there are
3133251881Speter         descendants we haven't walked?
3134251881Speter
3135251881Speter         Note that we aren't interested in changes, as we already verified
3136251881Speter         changes in the paths touched by the merge. And the existance of
3137251881Speter         other paths is enough to mark the directory edited */
3138251881Speter      err = svn_wc_walk_status(merge_b->ctx->wc_ctx, local_abspath,
3139251881Speter                               svn_depth_infinity, TRUE /* get-all */,
3140251881Speter                               FALSE /* no-ignore */,
3141251881Speter                               TRUE /* ignore-text-mods */, ignores,
3142251881Speter                               verify_touched_by_del_check, delb,
3143251881Speter                               merge_b->ctx->cancel_func,
3144251881Speter                               merge_b->ctx->cancel_baton,
3145251881Speter                               scratch_pool);
3146251881Speter
3147251881Speter      if (err)
3148251881Speter        {
3149251881Speter          if (err->apr_err != SVN_ERR_CEASE_INVOCATION)
3150251881Speter            return svn_error_trace(err);
3151251881Speter
3152251881Speter          svn_error_clear(err);
3153251881Speter        }
3154251881Speter
3155251881Speter      same = ! delb->found_edit;
3156251881Speter    }
3157251881Speter
3158251881Speter  if (same && !merge_b->dry_run)
3159251881Speter    {
3160251881Speter      svn_error_t *err;
3161251881Speter
3162251881Speter      err = svn_wc_delete4(merge_b->ctx->wc_ctx, local_abspath,
3163251881Speter                           FALSE /* keep_local */, FALSE /* unversioned */,
3164251881Speter                           merge_b->ctx->cancel_func,
3165251881Speter                           merge_b->ctx->cancel_baton,
3166251881Speter                           NULL, NULL /* no notify */,
3167251881Speter                           scratch_pool);
3168251881Speter
3169251881Speter      if (err)
3170251881Speter        {
3171251881Speter          if (err->apr_err != SVN_ERR_WC_LEFT_LOCAL_MOD)
3172251881Speter            return svn_error_trace(err);
3173251881Speter
3174251881Speter          svn_error_clear(err);
3175251881Speter          same = FALSE;
3176251881Speter        }
3177251881Speter    }
3178251881Speter
3179251881Speter  if (! same)
3180251881Speter    {
3181251881Speter      /* If the attempt to delete an existing directory failed,
3182251881Speter       * the directory has local modifications (e.g. locally added
3183251881Speter       * files, or property changes). Flag a tree conflict. */
3184251881Speter
3185251881Speter      /* This handles use case 5 described in the paper attached to issue
3186251881Speter       * #2282.  See also notes/tree-conflicts/detection.txt
3187251881Speter       */
3188251881Speter      SVN_ERR(record_tree_conflict(merge_b, local_abspath, db->parent_baton,
3189251881Speter                                   svn_node_dir,
3190251881Speter                                   svn_wc_conflict_action_delete,
3191251881Speter                                   svn_wc_conflict_reason_edited,
3192251881Speter                                   NULL, TRUE,
3193251881Speter                                   scratch_pool));
3194251881Speter    }
3195251881Speter  else
3196251881Speter    {
3197251881Speter      /* Record that we might have deleted mergeinfo */
3198251881Speter      if (working_props
3199251881Speter          && svn_hash_gets(working_props, SVN_PROP_MERGEINFO))
3200251881Speter        {
3201251881Speter          alloc_and_store_path(&merge_b->paths_with_deleted_mergeinfo,
3202251881Speter                               local_abspath, merge_b->pool);
3203251881Speter        }
3204251881Speter
3205251881Speter      SVN_ERR(record_update_delete(merge_b, db->parent_baton, local_abspath,
3206251881Speter                                   svn_node_dir, scratch_pool));
3207251881Speter    }
3208251881Speter
3209251881Speter  return SVN_NO_ERROR;
3210251881Speter}
3211251881Speter
3212251881Speter/* An svn_diff_tree_processor_t function.
3213251881Speter *
3214251881Speter * Called after merge_dir_opened() when a node itself didn't change between
3215251881Speter * the left and right source.
3216251881Speter *
3217251881Speter * After the merge_dir_opened() but before the call to this merge_dir_closed()
3218251881Speter * function all descendants will have been processed.
3219251881Speter */
3220251881Speterstatic svn_error_t *
3221251881Spetermerge_dir_closed(const char *relpath,
3222251881Speter                 const svn_diff_source_t *left_source,
3223251881Speter                 const svn_diff_source_t *right_source,
3224251881Speter                 void *dir_baton,
3225251881Speter                 const struct svn_diff_tree_processor_t *processor,
3226251881Speter                 apr_pool_t *scratch_pool)
3227251881Speter{
3228251881Speter  merge_cmd_baton_t *merge_b = processor->baton;
3229251881Speter  struct merge_dir_baton_t *db = dir_baton;
3230251881Speter
3231251881Speter  SVN_ERR(handle_pending_notifications(merge_b, db, scratch_pool));
3232251881Speter
3233251881Speter  return SVN_NO_ERROR;
3234251881Speter}
3235251881Speter
3236251881Speter/* An svn_diff_tree_processor_t function.
3237251881Speter
3238251881Speter   Called when the diff driver wants to report an absent path.
3239251881Speter
3240251881Speter   In case of merges this happens when the diff encounters a server-excluded
3241251881Speter   path.
3242251881Speter
3243251881Speter   We register a skipped path, which will make parent mergeinfo non-
3244251881Speter   inheritable. This ensures that a future merge might see these skipped
3245251881Speter   changes as eligable for merging.
3246251881Speter
3247251881Speter   For legacy reasons we also notify the path as skipped.
3248251881Speter */
3249251881Speterstatic svn_error_t *
3250251881Spetermerge_node_absent(const char *relpath,
3251251881Speter                  void *dir_baton,
3252251881Speter                  const svn_diff_tree_processor_t *processor,
3253251881Speter                  apr_pool_t *scratch_pool)
3254251881Speter{
3255251881Speter  merge_cmd_baton_t *merge_b = processor->baton;
3256251881Speter
3257251881Speter  const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
3258251881Speter                                              relpath, scratch_pool);
3259251881Speter
3260251881Speter  SVN_ERR(record_skip(merge_b, local_abspath, svn_node_unknown,
3261251881Speter                      svn_wc_notify_skip, svn_wc_notify_state_missing,
3262251881Speter                      scratch_pool));
3263251881Speter
3264251881Speter  return SVN_NO_ERROR;
3265251881Speter}
3266251881Speter
3267251881Speter/*-----------------------------------------------------------------------*/
3268251881Speter
3269251881Speter/*** Merge Notification ***/
3270251881Speter
3271251881Speter
3272251881Speter/* Finds a nearest ancestor in CHILDREN_WITH_MERGEINFO for LOCAL_ABSPATH. If
3273251881Speter   PATH_IS_OWN_ANCESTOR is TRUE then a child in CHILDREN_WITH_MERGEINFO
3274251881Speter   where child->abspath == PATH is considered PATH's ancestor.  If FALSE,
3275251881Speter   then child->abspath must be a proper ancestor of PATH.
3276251881Speter
3277251881Speter   CHILDREN_WITH_MERGEINFO is expected to be sorted in Depth first
3278251881Speter   order of path. */
3279251881Speterstatic svn_client__merge_path_t *
3280251881Speterfind_nearest_ancestor(const apr_array_header_t *children_with_mergeinfo,
3281251881Speter                      svn_boolean_t path_is_own_ancestor,
3282251881Speter                      const char *local_abspath)
3283251881Speter{
3284251881Speter  int i;
3285251881Speter
3286251881Speter  SVN_ERR_ASSERT_NO_RETURN(children_with_mergeinfo != NULL);
3287251881Speter
3288251881Speter  for (i = children_with_mergeinfo->nelts - 1; i >= 0 ; i--)
3289251881Speter    {
3290251881Speter      svn_client__merge_path_t *child =
3291251881Speter        APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
3292251881Speter
3293251881Speter      if (svn_dirent_is_ancestor(child->abspath, local_abspath)
3294251881Speter          && (path_is_own_ancestor
3295251881Speter              || strcmp(child->abspath, local_abspath) != 0))
3296251881Speter        return child;
3297251881Speter    }
3298251881Speter  return NULL;
3299251881Speter}
3300251881Speter
3301251881Speter/* Find the highest level path in a merge target (possibly the merge target
3302251881Speter   itself) to use in a merge notification header.
3303251881Speter
3304251881Speter   Return the svn_client__merge_path_t * representing the most distant
3305251881Speter   ancestor in CHILDREN_WITH_MERGEINFO of LOCAL_ABSPATH where said
3306251881Speter   ancestor's first remaining ranges element (per the REMAINING_RANGES
3307251881Speter   member of the ancestor) intersect with the first remaining ranges element
3308251881Speter   for every intermediate ancestor svn_client__merge_path_t * of
3309251881Speter   LOCAL_ABSPATH.  If no such ancestor is found return NULL.
3310251881Speter
3311251881Speter   If the remaining ranges of the elements in CHILDREN_WITH_MERGEINFO
3312251881Speter   represent a forward merge, then set *START to the oldest revision found
3313251881Speter   in any of the intersecting ancestors and *END to the youngest revision
3314251881Speter   found.  If the remaining ranges of the elements in CHILDREN_WITH_MERGEINFO
3315251881Speter   represent a reverse merge, then set *START to the youngest revision
3316251881Speter   found and *END to the oldest revision found.  If no ancestors are found
3317251881Speter   then set *START and *END to SVN_INVALID_REVNUM.
3318251881Speter
3319251881Speter   If PATH_IS_OWN_ANCESTOR is TRUE then a child in CHILDREN_WITH_MERGEINFO
3320251881Speter   where child->abspath == PATH is considered PATH's ancestor.  If FALSE,
3321251881Speter   then child->abspath must be a proper ancestor of PATH.
3322251881Speter
3323251881Speter   See the CHILDREN_WITH_MERGEINFO ARRAY global comment for more
3324251881Speter   information. */
3325251881Speterstatic svn_client__merge_path_t *
3326251881Speterfind_nearest_ancestor_with_intersecting_ranges(
3327251881Speter  svn_revnum_t *start,
3328251881Speter  svn_revnum_t *end,
3329251881Speter  const apr_array_header_t *children_with_mergeinfo,
3330251881Speter  svn_boolean_t path_is_own_ancestor,
3331251881Speter  const char *local_abspath)
3332251881Speter{
3333251881Speter  int i;
3334251881Speter  svn_client__merge_path_t *nearest_ancestor = NULL;
3335251881Speter
3336251881Speter  *start = SVN_INVALID_REVNUM;
3337251881Speter  *end = SVN_INVALID_REVNUM;
3338251881Speter
3339251881Speter  SVN_ERR_ASSERT_NO_RETURN(children_with_mergeinfo != NULL);
3340251881Speter
3341251881Speter  for (i = children_with_mergeinfo->nelts - 1; i >= 0 ; i--)
3342251881Speter    {
3343251881Speter      svn_client__merge_path_t *child =
3344251881Speter        APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
3345251881Speter
3346251881Speter      if (svn_dirent_is_ancestor(child->abspath, local_abspath)
3347251881Speter          && (path_is_own_ancestor
3348251881Speter              || strcmp(child->abspath, local_abspath) != 0))
3349251881Speter        {
3350251881Speter          if (nearest_ancestor == NULL)
3351251881Speter            {
3352251881Speter              /* Found an ancestor. */
3353251881Speter              nearest_ancestor = child;
3354251881Speter
3355251881Speter              if (child->remaining_ranges)
3356251881Speter                {
3357251881Speter                  svn_merge_range_t *r1 = APR_ARRAY_IDX(
3358251881Speter                    child->remaining_ranges, 0, svn_merge_range_t *);
3359251881Speter                  *start = r1->start;
3360251881Speter                  *end = r1->end;
3361251881Speter                }
3362251881Speter              else
3363251881Speter                {
3364251881Speter                  /* If CHILD->REMAINING_RANGES is null then LOCAL_ABSPATH
3365251881Speter                     is inside an absent subtree in the merge target. */
3366251881Speter                  *start = SVN_INVALID_REVNUM;
3367251881Speter                  *end = SVN_INVALID_REVNUM;
3368251881Speter                  break;
3369251881Speter                }
3370251881Speter            }
3371251881Speter          else
3372251881Speter            {
3373251881Speter              /* We'e found another ancestor for LOCAL_ABSPATH.  Do its
3374251881Speter                 first remaining range intersect with the previously
3375251881Speter                 found ancestor? */
3376251881Speter              svn_merge_range_t *r1 =
3377251881Speter                APR_ARRAY_IDX(nearest_ancestor->remaining_ranges, 0,
3378251881Speter                              svn_merge_range_t *);
3379251881Speter              svn_merge_range_t *r2 =
3380251881Speter                APR_ARRAY_IDX(child->remaining_ranges, 0,
3381251881Speter                              svn_merge_range_t *);
3382251881Speter
3383251881Speter              if (r1 && r2)
3384251881Speter                {
3385251881Speter                  svn_merge_range_t range1;
3386251881Speter                  svn_merge_range_t range2;
3387251881Speter                  svn_boolean_t reverse_merge = r1->start > r2->end;
3388251881Speter
3389251881Speter                  /* Flip endpoints if this is a reverse merge. */
3390251881Speter                  if (reverse_merge)
3391251881Speter                    {
3392251881Speter                      range1.start = r1->end;
3393251881Speter                      range1.end = r1->start;
3394251881Speter                      range2.start = r2->end;
3395251881Speter                      range2.end = r2->start;
3396251881Speter                    }
3397251881Speter                  else
3398251881Speter                    {
3399251881Speter                      range1.start = r1->start;
3400251881Speter                      range1.end = r1->end;
3401251881Speter                      range2.start = r2->start;
3402251881Speter                      range2.end = r2->end;
3403251881Speter                    }
3404251881Speter
3405251881Speter                  if (range1.start < range2.end && range2.start < range1.end)
3406251881Speter                    {
3407251881Speter                      *start = reverse_merge ?
3408251881Speter                        MAX(r1->start, r2->start) : MIN(r1->start, r2->start);
3409251881Speter                      *end = reverse_merge ?
3410251881Speter                        MIN(r1->end, r2->end) : MAX(r1->end, r2->end);
3411251881Speter                      nearest_ancestor = child;
3412251881Speter                    }
3413251881Speter                }
3414251881Speter            }
3415251881Speter        }
3416251881Speter    }
3417251881Speter  return nearest_ancestor;
3418251881Speter}
3419251881Speter
3420251881Speter/* Notify that we're starting to record mergeinfo for the merge of the
3421251881Speter * revision range RANGE into TARGET_ABSPATH.  RANGE should be null if the
3422251881Speter * merge sources are not from the same URL.
3423251881Speter *
3424251881Speter * This calls the client's notification receiver (as found in the client
3425251881Speter * context), with a WC abspath.
3426251881Speter */
3427251881Speterstatic void
3428251881Speternotify_mergeinfo_recording(const char *target_abspath,
3429251881Speter                           const svn_merge_range_t *range,
3430251881Speter                           svn_client_ctx_t *ctx,
3431251881Speter                           apr_pool_t *pool)
3432251881Speter{
3433251881Speter  if (ctx->notify_func2)
3434251881Speter    {
3435251881Speter      svn_wc_notify_t *n = svn_wc_create_notify(
3436251881Speter        target_abspath, svn_wc_notify_merge_record_info_begin, pool);
3437251881Speter
3438251881Speter      n->merge_range = range ? svn_merge_range_dup(range, pool) : NULL;
3439251881Speter      ctx->notify_func2(ctx->notify_baton2, n, pool);
3440251881Speter    }
3441251881Speter}
3442251881Speter
3443251881Speter/* Notify that we're completing the merge into TARGET_ABSPATH.
3444251881Speter *
3445251881Speter * This calls the client's notification receiver (as found in the client
3446251881Speter * context), with a WC abspath.
3447251881Speter */
3448251881Speterstatic void
3449251881Speternotify_merge_completed(const char *target_abspath,
3450251881Speter                       svn_client_ctx_t *ctx,
3451251881Speter                       apr_pool_t *pool)
3452251881Speter{
3453251881Speter  if (ctx->notify_func2)
3454251881Speter    {
3455251881Speter      svn_wc_notify_t *n
3456251881Speter        = svn_wc_create_notify(target_abspath, svn_wc_notify_merge_completed,
3457251881Speter                               pool);
3458251881Speter      ctx->notify_func2(ctx->notify_baton2, n, pool);
3459251881Speter    }
3460251881Speter}
3461251881Speter
3462251881Speter/* Is the notification the result of a real operative merge? */
3463251881Speter#define IS_OPERATIVE_NOTIFICATION(notify)  \
3464251881Speter                    (notify->content_state == svn_wc_notify_state_conflicted \
3465251881Speter                     || notify->content_state == svn_wc_notify_state_merged  \
3466251881Speter                     || notify->content_state == svn_wc_notify_state_changed \
3467251881Speter                     || notify->prop_state == svn_wc_notify_state_conflicted \
3468251881Speter                     || notify->prop_state == svn_wc_notify_state_merged     \
3469251881Speter                     || notify->prop_state == svn_wc_notify_state_changed    \
3470251881Speter                     || notify->action == svn_wc_notify_update_add \
3471251881Speter                     || notify->action == svn_wc_notify_tree_conflict)
3472251881Speter
3473251881Speter
3474251881Speter/* Remove merge source gaps from range used for merge notifications.
3475251881Speter   See http://subversion.tigris.org/issues/show_bug.cgi?id=4138
3476251881Speter
3477251881Speter   If IMPLICIT_SRC_GAP is not NULL then it is a rangelist containing a
3478251881Speter   single range (see the implicit_src_gap member of merge_cmd_baton_t).
3479251881Speter   RANGE describes a (possibly reverse) merge.
3480251881Speter
3481251881Speter   If IMPLICIT_SRC_GAP is not NULL and it's sole range intersects with
3482251881Speter   the older revision in *RANGE, then remove IMPLICIT_SRC_GAP's range
3483251881Speter   from *RANGE. */
3484251881Speterstatic void
3485251881Speterremove_source_gap(svn_merge_range_t *range,
3486251881Speter                  apr_array_header_t *implicit_src_gap)
3487251881Speter{
3488251881Speter  if (implicit_src_gap)
3489251881Speter    {
3490251881Speter      svn_merge_range_t *gap_range =
3491251881Speter        APR_ARRAY_IDX(implicit_src_gap, 0, svn_merge_range_t *);
3492251881Speter      if (range->start < range->end)
3493251881Speter        {
3494251881Speter          if (gap_range->start == range->start)
3495251881Speter            range->start = gap_range->end;
3496251881Speter        }
3497251881Speter      else /* Reverse merge */
3498251881Speter        {
3499251881Speter          if (gap_range->start == range->end)
3500251881Speter            range->end = gap_range->end;
3501251881Speter        }
3502251881Speter    }
3503251881Speter}
3504251881Speter
3505251881Speter/* Notify that we're starting a merge
3506251881Speter *
3507251881Speter * This calls the client's notification receiver (as found in the client
3508251881Speter * context), with a WC abspath.
3509251881Speter */
3510251881Speterstatic svn_error_t *
3511251881Speternotify_merge_begin(merge_cmd_baton_t *merge_b,
3512251881Speter                   const char *local_abspath,
3513251881Speter                   svn_boolean_t delete_action,
3514251881Speter                   apr_pool_t *scratch_pool)
3515251881Speter{
3516251881Speter  svn_wc_notify_t *notify;
3517251881Speter  svn_merge_range_t n_range =
3518251881Speter    {SVN_INVALID_REVNUM, SVN_INVALID_REVNUM, TRUE};
3519251881Speter  const char *notify_abspath;
3520251881Speter
3521251881Speter  if (! merge_b->ctx->notify_func2)
3522251881Speter    return SVN_NO_ERROR;
3523251881Speter
3524251881Speter  /* If our merge sources are ancestors of one another... */
3525251881Speter  if (merge_b->merge_source.ancestral)
3526251881Speter    {
3527251881Speter      const svn_client__merge_path_t *child;
3528251881Speter      /* Find NOTIFY->PATH's nearest ancestor in
3529251881Speter         NOTIFY->CHILDREN_WITH_MERGEINFO.  Normally we consider a child in
3530251881Speter         NOTIFY->CHILDREN_WITH_MERGEINFO representing PATH to be an
3531251881Speter         ancestor of PATH, but if this is a deletion of PATH then the
3532251881Speter         notification must be for a proper ancestor of PATH.  This ensures
3533251881Speter         we don't get notifications like:
3534251881Speter
3535251881Speter           --- Merging rX into 'PARENT/CHILD'
3536251881Speter           D    PARENT/CHILD
3537251881Speter
3538251881Speter         But rather:
3539251881Speter
3540251881Speter           --- Merging rX into 'PARENT'
3541251881Speter           D    PARENT/CHILD
3542251881Speter      */
3543251881Speter
3544251881Speter      child = find_nearest_ancestor_with_intersecting_ranges(
3545251881Speter        &(n_range.start), &(n_range.end),
3546251881Speter        merge_b->notify_begin.nodes_with_mergeinfo,
3547251881Speter        ! delete_action, local_abspath);
3548251881Speter
3549251881Speter      if (!child && delete_action)
3550251881Speter        {
3551251881Speter          /* Triggered by file replace in single-file-merge */
3552251881Speter          child = find_nearest_ancestor(merge_b->notify_begin.nodes_with_mergeinfo,
3553251881Speter                                        TRUE, local_abspath);
3554251881Speter        }
3555251881Speter
3556251881Speter      assert(child != NULL); /* Should always find the merge anchor */
3557251881Speter
3558251881Speter      if (! child)
3559251881Speter        return SVN_NO_ERROR;
3560251881Speter
3561251881Speter      if (merge_b->notify_begin.last_abspath != NULL
3562251881Speter          && strcmp(child->abspath, merge_b->notify_begin.last_abspath) == 0)
3563251881Speter        {
3564251881Speter          /* Don't notify the same merge again */
3565251881Speter          return SVN_NO_ERROR;
3566251881Speter        }
3567251881Speter
3568251881Speter      merge_b->notify_begin.last_abspath = child->abspath;
3569251881Speter
3570251881Speter      if (child->absent || child->remaining_ranges->nelts == 0
3571251881Speter          || !SVN_IS_VALID_REVNUM(n_range.start))
3572251881Speter        {
3573251881Speter          /* No valid information for an header */
3574251881Speter          return SVN_NO_ERROR;
3575251881Speter        }
3576251881Speter
3577251881Speter      notify_abspath = child->abspath;
3578251881Speter    }
3579251881Speter  else
3580251881Speter    {
3581251881Speter      if (merge_b->notify_begin.last_abspath)
3582251881Speter        return SVN_NO_ERROR; /* already notified */
3583251881Speter
3584251881Speter      notify_abspath = merge_b->target->abspath;
3585251881Speter      /* Store something in last_abspath. Any value would do */
3586251881Speter      merge_b->notify_begin.last_abspath = merge_b->target->abspath;
3587251881Speter    }
3588251881Speter
3589251881Speter  notify = svn_wc_create_notify(notify_abspath,
3590251881Speter                                merge_b->same_repos
3591251881Speter                                      ? svn_wc_notify_merge_begin
3592251881Speter                                      : svn_wc_notify_foreign_merge_begin,
3593251881Speter                                scratch_pool);
3594251881Speter
3595251881Speter  if (SVN_IS_VALID_REVNUM(n_range.start))
3596251881Speter    {
3597251881Speter      /* If the merge source has a gap, then don't mention
3598251881Speter         those gap revisions in the notification. */
3599251881Speter      remove_source_gap(&n_range, merge_b->implicit_src_gap);
3600251881Speter      notify->merge_range = &n_range;
3601251881Speter    }
3602251881Speter  else
3603251881Speter    {
3604251881Speter      notify->merge_range = NULL;
3605251881Speter    }
3606251881Speter
3607251881Speter  (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify,
3608251881Speter                                scratch_pool);
3609251881Speter
3610251881Speter  return SVN_NO_ERROR;
3611251881Speter}
3612251881Speter
3613251881Speter/* Set *OUT_RANGELIST to the intersection of IN_RANGELIST with the simple
3614251881Speter * (inheritable) revision range REV1:REV2, according to CONSIDER_INHERITANCE.
3615251881Speter * If REV1 is equal to REV2, the result is an empty rangelist, otherwise
3616251881Speter * REV1 must be less than REV2.
3617251881Speter *
3618251881Speter * Note: If CONSIDER_INHERITANCE is FALSE, the effect is to treat any non-
3619251881Speter * inheritable input ranges as if they were inheritable.  If it is TRUE, the
3620251881Speter * effect is to discard any non-inheritable input ranges.  Therefore the
3621251881Speter * ranges in *OUT_RANGELIST will always be inheritable. */
3622251881Speterstatic svn_error_t *
3623251881Speterrangelist_intersect_range(svn_rangelist_t **out_rangelist,
3624251881Speter                          const svn_rangelist_t *in_rangelist,
3625251881Speter                          svn_revnum_t rev1,
3626251881Speter                          svn_revnum_t rev2,
3627251881Speter                          svn_boolean_t consider_inheritance,
3628251881Speter                          apr_pool_t *result_pool,
3629251881Speter                          apr_pool_t *scratch_pool)
3630251881Speter{
3631251881Speter  SVN_ERR_ASSERT(rev1 <= rev2);
3632251881Speter
3633251881Speter  if (rev1 < rev2)
3634251881Speter    {
3635251881Speter      svn_rangelist_t *simple_rangelist =
3636251881Speter        svn_rangelist__initialize(rev1, rev2, TRUE, scratch_pool);
3637251881Speter
3638251881Speter      SVN_ERR(svn_rangelist_intersect(out_rangelist,
3639251881Speter                                      simple_rangelist, in_rangelist,
3640251881Speter                                      consider_inheritance, result_pool));
3641251881Speter    }
3642251881Speter  else
3643251881Speter    {
3644251881Speter      *out_rangelist = apr_array_make(result_pool, 0,
3645251881Speter                                      sizeof(svn_merge_range_t *));
3646251881Speter    }
3647251881Speter  return SVN_NO_ERROR;
3648251881Speter}
3649251881Speter
3650251881Speter/* Helper for fix_deleted_subtree_ranges().  Like fix_deleted_subtree_ranges()
3651251881Speter   this function should only be called when honoring mergeinfo.
3652251881Speter
3653251881Speter   CHILD, PARENT, REVISION1, REVISION2, and RA_SESSION are all cascaded from
3654251881Speter   fix_deleted_subtree_ranges() -- see that function for more information on
3655251881Speter   each.
3656251881Speter
3657251881Speter   If PARENT is not the merge target then PARENT must have already have been
3658251881Speter   processed by this function as a child.  Specifically, this means that
3659251881Speter   PARENT->REMAINING_RANGES must already be populated -- it can be an empty
3660251881Speter   rangelist but cannot be NULL.
3661251881Speter
3662251881Speter   PRIMARY_URL is the merge source url of CHILD at the younger of REVISION1
3663251881Speter   and REVISION2.
3664251881Speter
3665251881Speter   Since this function is only invoked for subtrees of the merge target, the
3666251881Speter   guarantees afforded by normalize_merge_sources() don't apply - see the
3667251881Speter   'MERGEINFO MERGE SOURCE NORMALIZATION' comment at the top of this file.
3668251881Speter   Therefore it is possible that PRIMARY_URL@REVISION1 and
3669251881Speter   PRIMARY_URL@REVISION2 don't describe the endpoints of an unbroken line of
3670251881Speter   history.  The purpose of this helper is to identify these cases of broken
3671251881Speter   history and adjust CHILD->REMAINING_RANGES in such a way we don't later try
3672251881Speter   to describe nonexistent path/revisions to the merge report editor -- see
3673251881Speter   drive_merge_report_editor().
3674251881Speter
3675251881Speter   If PRIMARY_URL@REVISION1 and PRIMARY_URL@REVISION2 describe an unbroken
3676251881Speter   line of history then do nothing and leave CHILD->REMAINING_RANGES as-is.
3677251881Speter
3678251881Speter   If neither PRIMARY_URL@REVISION1 nor PRIMARY_URL@REVISION2 exist then
3679251881Speter   there is nothing to merge to CHILD->ABSPATH so set CHILD->REMAINING_RANGES
3680251881Speter   equal to PARENT->REMAINING_RANGES.  This will cause the subtree to
3681251881Speter   effectively ignore CHILD -- see 'Note: If the first svn_merge_range_t...'
3682251881Speter   in drive_merge_report_editor()'s doc string.
3683251881Speter
3684251881Speter   If PRIMARY_URL@REVISION1 *xor* PRIMARY_URL@REVISION2 exist then we take the
3685251881Speter   subset of REVISION1:REVISION2 in CHILD->REMAINING_RANGES at which
3686251881Speter   PRIMARY_URL doesn't exist and set that subset equal to
3687251881Speter   PARENT->REMAINING_RANGES' intersection with that non-existent range.  Why?
3688251881Speter   Because this causes CHILD->REMAINING_RANGES to be identical to
3689251881Speter   PARENT->REMAINING_RANGES for revisions between REVISION1 and REVISION2 at
3690251881Speter   which PRIMARY_URL doesn't exist.  As mentioned above this means that
3691251881Speter   drive_merge_report_editor() won't attempt to describe these non-existent
3692251881Speter   subtree path/ranges to the reporter (which would break the merge).
3693251881Speter
3694251881Speter   If the preceding paragraph wasn't terribly clear then what follows spells
3695251881Speter   out this function's behavior a bit more explicitly:
3696251881Speter
3697251881Speter   For forward merges (REVISION1 < REVISION2)
3698251881Speter
3699251881Speter     If PRIMARY_URL@REVISION1 exists but PRIMARY_URL@REVISION2 doesn't, then
3700251881Speter     find the revision 'N' in which PRIMARY_URL@REVISION1 was deleted.  Leave
3701251881Speter     the subset of CHILD->REMAINING_RANGES that intersects with
3702251881Speter     REVISION1:(N - 1) as-is and set the subset of CHILD->REMAINING_RANGES
3703251881Speter     that intersects with (N - 1):REVISION2 equal to PARENT->REMAINING_RANGES'
3704251881Speter     intersection with (N - 1):REVISION2.
3705251881Speter
3706251881Speter     If PRIMARY_URL@REVISION1 doesn't exist but PRIMARY_URL@REVISION2 does,
3707251881Speter     then find the revision 'M' in which PRIMARY_URL@REVISION2 came into
3708251881Speter     existence.  Leave the subset of CHILD->REMAINING_RANGES that intersects with
3709251881Speter     (M - 1):REVISION2 as-is and set the subset of CHILD->REMAINING_RANGES
3710251881Speter     that intersects with REVISION1:(M - 1) equal to PARENT->REMAINING_RANGES'
3711251881Speter     intersection with REVISION1:(M - 1).
3712251881Speter
3713251881Speter   For reverse merges (REVISION1 > REVISION2)
3714251881Speter
3715251881Speter     If PRIMARY_URL@REVISION1 exists but PRIMARY_URL@REVISION2 doesn't, then
3716251881Speter     find the revision 'N' in which PRIMARY_URL@REVISION1 came into existence.
3717251881Speter     Leave the subset of CHILD->REMAINING_RANGES that intersects with
3718251881Speter     REVISION2:(N - 1) as-is and set the subset of CHILD->REMAINING_RANGES
3719251881Speter     that intersects with (N - 1):REVISION1 equal to PARENT->REMAINING_RANGES'
3720251881Speter     intersection with (N - 1):REVISION1.
3721251881Speter
3722251881Speter     If PRIMARY_URL@REVISION1 doesn't exist but PRIMARY_URL@REVISION2 does,
3723251881Speter     then find the revision 'M' in which PRIMARY_URL@REVISION2 came into
3724251881Speter     existence.  Leave the subset of CHILD->REMAINING_RANGES that intersects with
3725251881Speter     REVISION2:(M - 1) as-is and set the subset of CHILD->REMAINING_RANGES
3726251881Speter     that intersects with (M - 1):REVISION1 equal to PARENT->REMAINING_RANGES'
3727251881Speter     intersection with REVISION1:(M - 1).
3728251881Speter
3729251881Speter   SCRATCH_POOL is used for all temporary allocations.  Changes to CHILD are
3730251881Speter   allocated in RESULT_POOL. */
3731251881Speterstatic svn_error_t *
3732251881Speteradjust_deleted_subtree_ranges(svn_client__merge_path_t *child,
3733251881Speter                              svn_client__merge_path_t *parent,
3734251881Speter                              svn_revnum_t revision1,
3735251881Speter                              svn_revnum_t revision2,
3736251881Speter                              const char *primary_url,
3737251881Speter                              svn_ra_session_t *ra_session,
3738251881Speter                              svn_client_ctx_t *ctx,
3739251881Speter                              apr_pool_t *result_pool,
3740251881Speter                              apr_pool_t *scratch_pool)
3741251881Speter{
3742251881Speter  svn_boolean_t is_rollback = revision2 < revision1;
3743251881Speter  svn_revnum_t younger_rev = is_rollback ? revision1 : revision2;
3744251881Speter  svn_revnum_t peg_rev = younger_rev;
3745251881Speter  svn_revnum_t older_rev = is_rollback ? revision2 : revision1;
3746251881Speter  apr_array_header_t *segments;
3747251881Speter  svn_error_t *err;
3748251881Speter
3749251881Speter  SVN_ERR_ASSERT(parent->remaining_ranges);
3750251881Speter
3751251881Speter  err = svn_client__repos_location_segments(&segments, ra_session,
3752251881Speter                                            primary_url, peg_rev,
3753251881Speter                                            younger_rev, older_rev, ctx,
3754251881Speter                                            scratch_pool);
3755251881Speter
3756251881Speter  /* If PRIMARY_URL@peg_rev doesn't exist then
3757251881Speter      svn_client__repos_location_segments() typically returns an
3758251881Speter      SVN_ERR_FS_NOT_FOUND error, but if it doesn't exist for a
3759251881Speter      forward merge over ra_neon then we get SVN_ERR_RA_DAV_REQUEST_FAILED.
3760251881Speter      http://subversion.tigris.org/issues/show_bug.cgi?id=3137 fixed some of
3761251881Speter      the cases where different RA layers returned different error codes to
3762251881Speter      signal the "path not found"...but it looks like there is more to do.
3763251881Speter
3764251881Speter      ### Do we still need to special case for ra_neon (since it no longer
3765251881Speter          exists)? */
3766251881Speter  if (err)
3767251881Speter    {
3768251881Speter      if (err->apr_err == SVN_ERR_FS_NOT_FOUND
3769251881Speter          || err->apr_err == SVN_ERR_RA_DAV_REQUEST_FAILED)
3770251881Speter        {
3771251881Speter          /* PRIMARY_URL@peg_rev doesn't exist.  Check if PRIMARY_URL@older_rev
3772251881Speter             exists, if neither exist then the editor can simply ignore this
3773251881Speter             subtree. */
3774251881Speter          const char *rel_source_path;  /* PRIMARY_URL relative to RA_SESSION */
3775251881Speter          svn_node_kind_t kind;
3776251881Speter
3777251881Speter          svn_error_clear(err);
3778251881Speter          err = NULL;
3779251881Speter
3780251881Speter          SVN_ERR(svn_ra_get_path_relative_to_session(
3781251881Speter                    ra_session, &rel_source_path, primary_url, scratch_pool));
3782251881Speter
3783251881Speter          SVN_ERR(svn_ra_check_path(ra_session, rel_source_path,
3784251881Speter                                    older_rev, &kind, scratch_pool));
3785251881Speter          if (kind == svn_node_none)
3786251881Speter            {
3787251881Speter              /* Neither PRIMARY_URL@peg_rev nor PRIMARY_URL@older_rev exist,
3788251881Speter                 so there is nothing to merge.  Set CHILD->REMAINING_RANGES
3789251881Speter                 identical to PARENT's. */
3790251881Speter              child->remaining_ranges =
3791251881Speter                svn_rangelist_dup(parent->remaining_ranges, scratch_pool);
3792251881Speter            }
3793251881Speter          else
3794251881Speter            {
3795251881Speter              svn_rangelist_t *deleted_rangelist;
3796251881Speter              svn_revnum_t rev_primary_url_deleted;
3797251881Speter
3798251881Speter              /* PRIMARY_URL@older_rev exists, so it was deleted at some
3799251881Speter                 revision prior to peg_rev, find that revision. */
3800251881Speter              SVN_ERR(svn_ra_get_deleted_rev(ra_session, rel_source_path,
3801251881Speter                                             older_rev, younger_rev,
3802251881Speter                                             &rev_primary_url_deleted,
3803251881Speter                                             scratch_pool));
3804251881Speter
3805251881Speter              /* PRIMARY_URL@older_rev exists and PRIMARY_URL@peg_rev doesn't,
3806251881Speter                 so svn_ra_get_deleted_rev() should always find the revision
3807251881Speter                 PRIMARY_URL@older_rev was deleted. */
3808251881Speter              SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev_primary_url_deleted));
3809251881Speter
3810251881Speter              /* If this is a reverse merge reorder CHILD->REMAINING_RANGES and
3811251881Speter                 PARENT->REMAINING_RANGES so both will work with the
3812251881Speter                 svn_rangelist_* APIs below. */
3813251881Speter              if (is_rollback)
3814251881Speter                {
3815251881Speter                  /* svn_rangelist_reverse operates in place so it's safe
3816251881Speter                     to use our scratch_pool. */
3817251881Speter                  SVN_ERR(svn_rangelist_reverse(child->remaining_ranges,
3818251881Speter                                                scratch_pool));
3819251881Speter                  SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges,
3820251881Speter                                                scratch_pool));
3821251881Speter                }
3822251881Speter
3823251881Speter              /* Find the intersection of CHILD->REMAINING_RANGES with the
3824251881Speter                 range over which PRIMARY_URL@older_rev exists (ending at
3825251881Speter                 the youngest revision at which it still exists). */
3826251881Speter              SVN_ERR(rangelist_intersect_range(&child->remaining_ranges,
3827251881Speter                                                child->remaining_ranges,
3828251881Speter                                                older_rev,
3829251881Speter                                                rev_primary_url_deleted - 1,
3830251881Speter                                                FALSE,
3831251881Speter                                                scratch_pool, scratch_pool));
3832251881Speter
3833251881Speter              /* Merge into CHILD->REMAINING_RANGES the intersection of
3834251881Speter                 PARENT->REMAINING_RANGES with the range beginning when
3835251881Speter                 PRIMARY_URL@older_rev was deleted until younger_rev. */
3836251881Speter              SVN_ERR(rangelist_intersect_range(&deleted_rangelist,
3837251881Speter                                                parent->remaining_ranges,
3838251881Speter                                                rev_primary_url_deleted - 1,
3839251881Speter                                                peg_rev,
3840251881Speter                                                FALSE,
3841251881Speter                                                scratch_pool, scratch_pool));
3842251881Speter              SVN_ERR(svn_rangelist_merge2(child->remaining_ranges,
3843251881Speter                                           deleted_rangelist, scratch_pool,
3844251881Speter                                           scratch_pool));
3845251881Speter
3846251881Speter              /* Return CHILD->REMAINING_RANGES and PARENT->REMAINING_RANGES
3847251881Speter                 to reverse order if necessary. */
3848251881Speter              if (is_rollback)
3849251881Speter                {
3850251881Speter                  SVN_ERR(svn_rangelist_reverse(child->remaining_ranges,
3851251881Speter                                                scratch_pool));
3852251881Speter                  SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges,
3853251881Speter                                                scratch_pool));
3854251881Speter                }
3855251881Speter            }
3856251881Speter        }
3857251881Speter      else
3858251881Speter        {
3859251881Speter          return svn_error_trace(err);
3860251881Speter        }
3861251881Speter    }
3862251881Speter  else /* PRIMARY_URL@peg_rev exists. */
3863251881Speter    {
3864251881Speter      svn_rangelist_t *non_existent_rangelist;
3865251881Speter      svn_location_segment_t *segment =
3866251881Speter        APR_ARRAY_IDX(segments, (segments->nelts - 1),
3867251881Speter                      svn_location_segment_t *);
3868251881Speter
3869251881Speter      /* We know PRIMARY_URL@peg_rev exists as the call to
3870251881Speter         svn_client__repos_location_segments() succeeded.  If there is only
3871251881Speter         one segment that starts at oldest_rev then we know that
3872251881Speter         PRIMARY_URL@oldest_rev:PRIMARY_URL@peg_rev describes an unbroken
3873251881Speter         line of history, so there is nothing more to adjust in
3874251881Speter         CHILD->REMAINING_RANGES. */
3875251881Speter      if (segment->range_start == older_rev)
3876251881Speter        {
3877251881Speter          return SVN_NO_ERROR;
3878251881Speter        }
3879251881Speter
3880251881Speter      /* If this is a reverse merge reorder CHILD->REMAINING_RANGES and
3881251881Speter         PARENT->REMAINING_RANGES so both will work with the
3882251881Speter         svn_rangelist_* APIs below. */
3883251881Speter      if (is_rollback)
3884251881Speter        {
3885251881Speter          SVN_ERR(svn_rangelist_reverse(child->remaining_ranges,
3886251881Speter                                        scratch_pool));
3887251881Speter          SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges,
3888251881Speter                                        scratch_pool));
3889251881Speter        }
3890251881Speter
3891251881Speter      /* Intersect CHILD->REMAINING_RANGES with the range where PRIMARY_URL
3892251881Speter         exists.  Since segment doesn't span older_rev:peg_rev we know
3893251881Speter         PRIMARY_URL@peg_rev didn't come into existence until
3894251881Speter         segment->range_start + 1. */
3895251881Speter      SVN_ERR(rangelist_intersect_range(&child->remaining_ranges,
3896251881Speter                                        child->remaining_ranges,
3897251881Speter                                        segment->range_start, peg_rev,
3898251881Speter                                        FALSE, scratch_pool, scratch_pool));
3899251881Speter
3900251881Speter      /* Merge into CHILD->REMAINING_RANGES the intersection of
3901251881Speter         PARENT->REMAINING_RANGES with the range before PRIMARY_URL@peg_rev
3902251881Speter         came into existence. */
3903251881Speter      SVN_ERR(rangelist_intersect_range(&non_existent_rangelist,
3904251881Speter                                        parent->remaining_ranges,
3905251881Speter                                        older_rev, segment->range_start,
3906251881Speter                                        FALSE, scratch_pool, scratch_pool));
3907251881Speter      SVN_ERR(svn_rangelist_merge2(child->remaining_ranges,
3908251881Speter                                   non_existent_rangelist, scratch_pool,
3909251881Speter                                   scratch_pool));
3910251881Speter
3911251881Speter      /* Return CHILD->REMAINING_RANGES and PARENT->REMAINING_RANGES
3912251881Speter         to reverse order if necessary. */
3913251881Speter      if (is_rollback)
3914251881Speter        {
3915251881Speter          SVN_ERR(svn_rangelist_reverse(child->remaining_ranges,
3916251881Speter                                        scratch_pool));
3917251881Speter          SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges,
3918251881Speter                                        scratch_pool));
3919251881Speter        }
3920251881Speter    }
3921251881Speter
3922251881Speter  /* Make a lasting copy of CHILD->REMAINING_RANGES using POOL. */
3923251881Speter  child->remaining_ranges = svn_rangelist_dup(child->remaining_ranges,
3924251881Speter                                              result_pool);
3925251881Speter  return SVN_NO_ERROR;
3926251881Speter}
3927251881Speter
3928251881Speter/* Helper for do_directory_merge().
3929251881Speter
3930251881Speter   SOURCE is cascaded from the argument of the same name in
3931251881Speter   do_directory_merge().  TARGET is the merge target.  RA_SESSION is the
3932251881Speter   session for the younger of SOURCE->loc1 and SOURCE->loc2.
3933251881Speter
3934251881Speter   Adjust the subtrees in CHILDREN_WITH_MERGEINFO so that we don't
3935251881Speter   later try to describe invalid paths in drive_merge_report_editor().
3936251881Speter   This function is just a thin wrapper around
3937251881Speter   adjust_deleted_subtree_ranges(), which see for further details.
3938251881Speter
3939251881Speter   SCRATCH_POOL is used for all temporary allocations.  Changes to
3940251881Speter   CHILDREN_WITH_MERGEINFO are allocated in RESULT_POOL.
3941251881Speter*/
3942251881Speterstatic svn_error_t *
3943251881Speterfix_deleted_subtree_ranges(const merge_source_t *source,
3944251881Speter                           const merge_target_t *target,
3945251881Speter                           svn_ra_session_t *ra_session,
3946251881Speter                           apr_array_header_t *children_with_mergeinfo,
3947251881Speter                           svn_client_ctx_t *ctx,
3948251881Speter                           apr_pool_t *result_pool,
3949251881Speter                           apr_pool_t *scratch_pool)
3950251881Speter{
3951251881Speter  int i;
3952251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
3953251881Speter  svn_boolean_t is_rollback = source->loc2->rev < source->loc1->rev;
3954251881Speter
3955251881Speter  assert(session_url_is(ra_session,
3956251881Speter                        (is_rollback ? source->loc1 : source->loc2)->url,
3957251881Speter                        scratch_pool));
3958251881Speter
3959251881Speter  /* CHILDREN_WITH_MERGEINFO is sorted in depth-first order, so
3960251881Speter     start at index 1 to examine only subtrees. */
3961251881Speter  for (i = 1; i < children_with_mergeinfo->nelts; i++)
3962251881Speter    {
3963251881Speter      svn_client__merge_path_t *child =
3964251881Speter        APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
3965251881Speter      svn_client__merge_path_t *parent;
3966251881Speter      svn_rangelist_t *deleted_rangelist, *added_rangelist;
3967251881Speter
3968251881Speter      SVN_ERR_ASSERT(child);
3969251881Speter      if (child->absent)
3970251881Speter        continue;
3971251881Speter
3972251881Speter      svn_pool_clear(iterpool);
3973251881Speter
3974251881Speter      /* Find CHILD's parent. */
3975251881Speter      parent = find_nearest_ancestor(children_with_mergeinfo,
3976251881Speter                                     FALSE, child->abspath);
3977251881Speter
3978251881Speter      /* Since CHILD is a subtree then its parent must be in
3979251881Speter         CHILDREN_WITH_MERGEINFO, see the global comment
3980251881Speter         'THE CHILDREN_WITH_MERGEINFO ARRAY'. */
3981251881Speter      SVN_ERR_ASSERT(parent);
3982251881Speter
3983251881Speter      /* If this is a reverse merge reorder CHILD->REMAINING_RANGES
3984251881Speter         so it will work with the svn_rangelist_diff API. */
3985251881Speter      if (is_rollback)
3986251881Speter        {
3987251881Speter          SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool));
3988251881Speter          SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges, iterpool));
3989251881Speter        }
3990251881Speter
3991251881Speter      SVN_ERR(svn_rangelist_diff(&deleted_rangelist, &added_rangelist,
3992251881Speter                                 child->remaining_ranges,
3993251881Speter                                 parent->remaining_ranges,
3994251881Speter                                 TRUE, iterpool));
3995251881Speter
3996251881Speter      if (is_rollback)
3997251881Speter        {
3998251881Speter          SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool));
3999251881Speter          SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges, iterpool));
4000251881Speter        }
4001251881Speter
4002251881Speter      /* If CHILD is the merge target we then know that SOURCE is provided
4003251881Speter         by normalize_merge_sources() -- see 'MERGEINFO MERGE SOURCE
4004251881Speter         NORMALIZATION'.  Due to this normalization we know that SOURCE
4005251881Speter         describes an unbroken line of history such that the entire range
4006251881Speter         described by SOURCE can potentially be merged to CHILD.
4007251881Speter
4008251881Speter         But if CHILD is a subtree we don't have the same guarantees about
4009251881Speter         SOURCE as we do for the merge target.  SOURCE->loc1 and/or
4010251881Speter         SOURCE->loc2 might not exist.
4011251881Speter
4012251881Speter         If one or both doesn't exist, then adjust CHILD->REMAINING_RANGES
4013251881Speter         such that we don't later try to describe invalid subtrees in
4014251881Speter         drive_merge_report_editor(), as that will break the merge.
4015251881Speter         If CHILD has the same remaining ranges as PARENT however, then
4016251881Speter         there is no need to make these adjustments, since
4017251881Speter         drive_merge_report_editor() won't attempt to describe CHILD in this
4018251881Speter         case, see the 'Note' in drive_merge_report_editor's docstring. */
4019251881Speter      if (deleted_rangelist->nelts || added_rangelist->nelts)
4020251881Speter        {
4021251881Speter          const char *child_primary_source_url;
4022251881Speter          const char *child_repos_src_path =
4023251881Speter            svn_dirent_is_child(target->abspath, child->abspath, iterpool);
4024251881Speter
4025251881Speter          /* This loop is only processing subtrees, so CHILD->ABSPATH
4026251881Speter             better be a proper child of the merge target. */
4027251881Speter          SVN_ERR_ASSERT(child_repos_src_path);
4028251881Speter
4029251881Speter          child_primary_source_url =
4030251881Speter            svn_path_url_add_component2((source->loc1->rev < source->loc2->rev)
4031251881Speter                                        ? source->loc2->url : source->loc1->url,
4032251881Speter                                        child_repos_src_path, iterpool);
4033251881Speter
4034251881Speter          SVN_ERR(adjust_deleted_subtree_ranges(child, parent,
4035251881Speter                                                source->loc1->rev,
4036251881Speter                                                source->loc2->rev,
4037251881Speter                                                child_primary_source_url,
4038251881Speter                                                ra_session,
4039251881Speter                                                ctx, result_pool, iterpool));
4040251881Speter        }
4041251881Speter    }
4042251881Speter
4043251881Speter  svn_pool_destroy(iterpool);
4044251881Speter  return SVN_NO_ERROR;
4045251881Speter}
4046251881Speter
4047251881Speter/*-----------------------------------------------------------------------*/
4048251881Speter
4049251881Speter/*** Determining What Remains To Be Merged ***/
4050251881Speter
4051251881Speter/* Get explicit and/or implicit mergeinfo for the working copy path
4052251881Speter   TARGET_ABSPATH.
4053251881Speter
4054251881Speter   If RECORDED_MERGEINFO is not NULL then set *RECORDED_MERGEINFO
4055251881Speter   to TARGET_ABSPATH's explicit or inherited mergeinfo as dictated by
4056251881Speter   INHERIT.
4057251881Speter
4058251881Speter   If IMPLICIT_MERGEINFO is not NULL then set *IMPLICIT_MERGEINFO
4059251881Speter   to TARGET_ABSPATH's implicit mergeinfo (a.k.a. natural history).
4060251881Speter
4061251881Speter   If both RECORDED_MERGEINFO and IMPLICIT_MERGEINFO are not NULL and
4062251881Speter   *RECORDED_MERGEINFO is inherited, then *IMPLICIT_MERGEINFO will be
4063251881Speter   removed from *RECORDED_MERGEINFO.
4064251881Speter
4065251881Speter   If INHERITED is not NULL set *INHERITED to TRUE if *RECORDED_MERGEINFO
4066251881Speter   is inherited rather than explicit.  If RECORDED_MERGEINFO is NULL then
4067251881Speter   INHERITED is ignored.
4068251881Speter
4069251881Speter
4070251881Speter   If IMPLICIT_MERGEINFO is not NULL then START and END are limits on
4071251881Speter   the natural history sought, must both be valid revision numbers, and
4072251881Speter   START must be greater than END.  If TARGET_ABSPATH's base revision
4073251881Speter   is older than START, then the base revision is used as the younger
4074251881Speter   bound in place of START.
4075251881Speter
4076251881Speter   RA_SESSION is an RA session open to the repository in which TARGET_ABSPATH
4077251881Speter   lives.  It may be temporarily reparented as needed by this function.
4078251881Speter
4079251881Speter   Allocate *RECORDED_MERGEINFO and *IMPLICIT_MERGEINFO in RESULT_POOL.
4080251881Speter   Use SCRATCH_POOL for any temporary allocations. */
4081251881Speterstatic svn_error_t *
4082251881Speterget_full_mergeinfo(svn_mergeinfo_t *recorded_mergeinfo,
4083251881Speter                   svn_mergeinfo_t *implicit_mergeinfo,
4084251881Speter                   svn_boolean_t *inherited,
4085251881Speter                   svn_mergeinfo_inheritance_t inherit,
4086251881Speter                   svn_ra_session_t *ra_session,
4087251881Speter                   const char *target_abspath,
4088251881Speter                   svn_revnum_t start,
4089251881Speter                   svn_revnum_t end,
4090251881Speter                   svn_client_ctx_t *ctx,
4091251881Speter                   apr_pool_t *result_pool,
4092251881Speter                   apr_pool_t *scratch_pool)
4093251881Speter{
4094251881Speter  /* First, we get the real mergeinfo. */
4095251881Speter  if (recorded_mergeinfo)
4096251881Speter    {
4097251881Speter      SVN_ERR(svn_client__get_wc_or_repos_mergeinfo(recorded_mergeinfo,
4098251881Speter                                                    inherited,
4099251881Speter                                                    NULL /* from_repos */,
4100251881Speter                                                    FALSE,
4101251881Speter                                                    inherit, ra_session,
4102251881Speter                                                    target_abspath,
4103251881Speter                                                    ctx, result_pool));
4104251881Speter    }
4105251881Speter
4106251881Speter  if (implicit_mergeinfo)
4107251881Speter    {
4108251881Speter      svn_client__pathrev_t *target;
4109251881Speter
4110251881Speter      /* Assert that we have sane input. */
4111251881Speter      SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(start) && SVN_IS_VALID_REVNUM(end)
4112251881Speter                     && (start > end));
4113251881Speter
4114251881Speter      /* Retrieve the origin (original_*) of the node, or just the
4115251881Speter         url if the node was not copied. */
4116251881Speter      SVN_ERR(svn_client__wc_node_get_origin(&target, target_abspath, ctx,
4117251881Speter                                             scratch_pool, scratch_pool));
4118251881Speter
4119251881Speter      if (! target)
4120251881Speter        {
4121251881Speter          /* We've been asked to operate on a locally added target, so its
4122251881Speter           * implicit mergeinfo is empty. */
4123251881Speter          *implicit_mergeinfo = apr_hash_make(result_pool);
4124251881Speter        }
4125251881Speter      else if (target->rev <= end)
4126251881Speter        {
4127251881Speter          /* We're asking about a range outside our natural history
4128251881Speter             altogether.  That means our implicit mergeinfo is empty. */
4129251881Speter          *implicit_mergeinfo = apr_hash_make(result_pool);
4130251881Speter        }
4131251881Speter      else
4132251881Speter        {
4133251881Speter          /* Fetch so-called "implicit mergeinfo" (that is, natural
4134251881Speter             history). */
4135251881Speter
4136251881Speter          /* Do not ask for implicit mergeinfo from TARGET_ABSPATH's future.
4137251881Speter             TARGET_ABSPATH might not even exist, and even if it does the
4138251881Speter             working copy is *at* TARGET_REV so its implicit history ends
4139251881Speter             at TARGET_REV! */
4140251881Speter          if (target->rev < start)
4141251881Speter            start = target->rev;
4142251881Speter
4143251881Speter          /* Fetch the implicit mergeinfo. */
4144251881Speter          SVN_ERR(svn_client__get_history_as_mergeinfo(implicit_mergeinfo,
4145251881Speter                                                       NULL,
4146251881Speter                                                       target, start, end,
4147251881Speter                                                       ra_session, ctx,
4148251881Speter                                                       result_pool));
4149251881Speter        }
4150251881Speter    } /*if (implicit_mergeinfo) */
4151251881Speter
4152251881Speter  return SVN_NO_ERROR;
4153251881Speter}
4154251881Speter
4155251881Speter/* Helper for ensure_implicit_mergeinfo().
4156251881Speter
4157251881Speter   PARENT, CHILD, REVISION1, REVISION2 and CTX
4158251881Speter   are all cascaded from the arguments of the same names in
4159251881Speter   ensure_implicit_mergeinfo().  PARENT and CHILD must both exist, i.e.
4160251881Speter   this function should never be called where CHILD is the merge target.
4161251881Speter
4162251881Speter   If PARENT->IMPLICIT_MERGEINFO is NULL, obtain it from the server.
4163251881Speter
4164251881Speter   Set CHILD->IMPLICIT_MERGEINFO to the mergeinfo inherited from
4165251881Speter   PARENT->IMPLICIT_MERGEINFO.  CHILD->IMPLICIT_MERGEINFO is allocated
4166251881Speter   in RESULT_POOL.
4167251881Speter
4168251881Speter   RA_SESSION is an RA session open to the repository that contains CHILD.
4169251881Speter   It may be temporarily reparented by this function.
4170251881Speter   */
4171251881Speterstatic svn_error_t *
4172251881Speterinherit_implicit_mergeinfo_from_parent(svn_client__merge_path_t *parent,
4173251881Speter                                       svn_client__merge_path_t *child,
4174251881Speter                                       svn_revnum_t revision1,
4175251881Speter                                       svn_revnum_t revision2,
4176251881Speter                                       svn_ra_session_t *ra_session,
4177251881Speter                                       svn_client_ctx_t *ctx,
4178251881Speter                                       apr_pool_t *result_pool,
4179251881Speter                                       apr_pool_t *scratch_pool)
4180251881Speter{
4181251881Speter  const char *path_diff;
4182251881Speter
4183251881Speter  /* This only works on subtrees! */
4184251881Speter  SVN_ERR_ASSERT(parent);
4185251881Speter  SVN_ERR_ASSERT(child);
4186251881Speter
4187251881Speter  /* While PARENT must exist, it is possible we've deferred
4188251881Speter     getting its implicit mergeinfo.  If so get it now. */
4189251881Speter  if (!parent->implicit_mergeinfo)
4190251881Speter    SVN_ERR(get_full_mergeinfo(NULL, &(parent->implicit_mergeinfo),
4191251881Speter                               NULL, svn_mergeinfo_inherited,
4192251881Speter                               ra_session, child->abspath,
4193251881Speter                               MAX(revision1, revision2),
4194251881Speter                               MIN(revision1, revision2),
4195251881Speter                               ctx, result_pool, scratch_pool));
4196251881Speter
4197251881Speter  /* Let CHILD inherit PARENT's implicit mergeinfo. */
4198251881Speter
4199251881Speter  path_diff = svn_dirent_is_child(parent->abspath, child->abspath,
4200251881Speter                                  scratch_pool);
4201251881Speter  /* PARENT->PATH better be an ancestor of CHILD->ABSPATH! */
4202251881Speter  SVN_ERR_ASSERT(path_diff);
4203251881Speter
4204251881Speter  SVN_ERR(svn_mergeinfo__add_suffix_to_mergeinfo(
4205251881Speter            &child->implicit_mergeinfo, parent->implicit_mergeinfo,
4206251881Speter            path_diff, result_pool, scratch_pool));
4207251881Speter  child->implicit_mergeinfo = svn_mergeinfo_dup(child->implicit_mergeinfo,
4208251881Speter                                                result_pool);
4209251881Speter  return SVN_NO_ERROR;
4210251881Speter}
4211251881Speter
4212251881Speter/* Helper of filter_merged_revisions().
4213251881Speter
4214251881Speter   If we have deferred obtaining CHILD->IMPLICIT_MERGEINFO, then get
4215251881Speter   it now, allocating it in RESULT_POOL.  If CHILD_INHERITS_PARENT is true
4216251881Speter   then set CHILD->IMPLICIT_MERGEINFO to the mergeinfo inherited from
4217251881Speter   PARENT->IMPLICIT_MERGEINFO, otherwise contact the repository.  Use
4218251881Speter   SCRATCH_POOL for all temporary allocations.
4219251881Speter
4220251881Speter   RA_SESSION is an RA session open to the repository that contains CHILD.
4221251881Speter   It may be temporarily reparented by this function.
4222251881Speter
4223251881Speter   PARENT, CHILD, REVISION1, REVISION2 and
4224251881Speter   CTX are all cascaded from the arguments of the same name in
4225251881Speter   filter_merged_revisions() and the same conditions for that function
4226251881Speter   hold here. */
4227251881Speterstatic svn_error_t *
4228251881Speterensure_implicit_mergeinfo(svn_client__merge_path_t *parent,
4229251881Speter                          svn_client__merge_path_t *child,
4230251881Speter                          svn_boolean_t child_inherits_parent,
4231251881Speter                          svn_revnum_t revision1,
4232251881Speter                          svn_revnum_t revision2,
4233251881Speter                          svn_ra_session_t *ra_session,
4234251881Speter                          svn_client_ctx_t *ctx,
4235251881Speter                          apr_pool_t *result_pool,
4236251881Speter                          apr_pool_t *scratch_pool)
4237251881Speter{
4238251881Speter  /* If we haven't already found CHILD->IMPLICIT_MERGEINFO then
4239251881Speter     contact the server to get it. */
4240251881Speter
4241251881Speter  if (child->implicit_mergeinfo)
4242251881Speter    return SVN_NO_ERROR;
4243251881Speter
4244251881Speter  if (child_inherits_parent)
4245251881Speter    SVN_ERR(inherit_implicit_mergeinfo_from_parent(parent,
4246251881Speter                                                   child,
4247251881Speter                                                   revision1,
4248251881Speter                                                   revision2,
4249251881Speter                                                   ra_session,
4250251881Speter                                                   ctx,
4251251881Speter                                                   result_pool,
4252251881Speter                                                   scratch_pool));
4253251881Speter  else
4254251881Speter    SVN_ERR(get_full_mergeinfo(NULL,
4255251881Speter                               &(child->implicit_mergeinfo),
4256251881Speter                               NULL, svn_mergeinfo_inherited,
4257251881Speter                               ra_session, child->abspath,
4258251881Speter                               MAX(revision1, revision2),
4259251881Speter                               MIN(revision1, revision2),
4260251881Speter                               ctx, result_pool, scratch_pool));
4261251881Speter
4262251881Speter  return SVN_NO_ERROR;
4263251881Speter}
4264251881Speter
4265251881Speter/* Helper for calculate_remaining_ranges().
4266251881Speter
4267251881Speter   Initialize CHILD->REMAINING_RANGES to a rangelist representing the
4268251881Speter   requested merge of REVISION1:REVISION2 from MERGEINFO_PATH to
4269251881Speter   CHILD->ABSPATH.
4270251881Speter
4271251881Speter   For forward merges remove any ranges from CHILD->REMAINING_RANGES that
4272251881Speter   have already been merged to CHILD->ABSPATH per TARGET_MERGEINFO or
4273251881Speter   CHILD->IMPLICIT_MERGEINFO.  For reverse merges remove any ranges from
4274251881Speter   CHILD->REMAINING_RANGES that have not already been merged to CHILD->ABSPATH
4275251881Speter   per TARGET_MERGEINFO or CHILD->IMPLICIT_MERGEINFO.  If we have deferred
4276251881Speter   obtaining CHILD->IMPLICIT_MERGEINFO and it is necessary to use it for
4277251881Speter   these calculations, then get it from the server, allocating it in
4278251881Speter   RESULT_POOL.
4279251881Speter
4280251881Speter   CHILD represents a working copy path which is the merge target or one of
4281251881Speter   the target's subtrees.  If not NULL, PARENT is CHILD's nearest path-wise
4282251881Speter   ancestor - see 'THE CHILDREN_WITH_MERGEINFO ARRAY'.
4283251881Speter
4284251881Speter   If the function needs to consider CHILD->IMPLICIT_MERGEINFO and
4285251881Speter   CHILD_INHERITS_IMPLICIT is true, then set CHILD->IMPLICIT_MERGEINFO to the
4286251881Speter   mergeinfo inherited from PARENT->IMPLICIT_MERGEINFO.  Otherwise contact
4287251881Speter   the repository for CHILD->IMPLICIT_MERGEINFO.
4288251881Speter
4289251881Speter   NOTE: If PARENT is present then this function must have previously been
4290251881Speter   called for PARENT, i.e. if populate_remaining_ranges() is calling this
4291251881Speter   function for a set of svn_client__merge_path_t* the calls must be made
4292251881Speter   in depth-first order.
4293251881Speter
4294251881Speter   MERGEINFO_PATH is the merge source relative to the repository root.
4295251881Speter
4296251881Speter   REVISION1 and REVISION2 describe the merge range requested from
4297251881Speter   MERGEINFO_PATH.
4298251881Speter
4299251881Speter   TARGET_RANGELIST is the portion of CHILD->ABSPATH's explicit or inherited
4300251881Speter   mergeinfo that intersects with the merge history described by
4301251881Speter   MERGEINFO_PATH@REVISION1:MERGEINFO_PATH@REVISION2.  TARGET_RANGELIST
4302251881Speter   should be NULL if there is no explicit or inherited mergeinfo on
4303251881Speter   CHILD->ABSPATH or an empty list if CHILD->ABSPATH has empty mergeinfo or
4304251881Speter   explicit mergeinfo that exclusively describes non-intersecting history
4305251881Speter   with MERGEINFO_PATH@REVISION1:MERGEINFO_PATH@REVISION2.
4306251881Speter
4307251881Speter   SCRATCH_POOL is used for all temporary allocations.
4308251881Speter
4309251881Speter   NOTE: This should only be called when honoring mergeinfo.
4310251881Speter
4311251881Speter   NOTE: Like calculate_remaining_ranges() if PARENT is present then this
4312251881Speter   function must have previously been called for PARENT.
4313251881Speter*/
4314251881Speterstatic svn_error_t *
4315251881Speterfilter_merged_revisions(svn_client__merge_path_t *parent,
4316251881Speter                        svn_client__merge_path_t *child,
4317251881Speter                        const char *mergeinfo_path,
4318251881Speter                        svn_rangelist_t *target_rangelist,
4319251881Speter                        svn_revnum_t revision1,
4320251881Speter                        svn_revnum_t revision2,
4321251881Speter                        svn_boolean_t child_inherits_implicit,
4322251881Speter                        svn_ra_session_t *ra_session,
4323251881Speter                        svn_client_ctx_t *ctx,
4324251881Speter                        apr_pool_t *result_pool,
4325251881Speter                        apr_pool_t *scratch_pool)
4326251881Speter{
4327251881Speter  svn_rangelist_t *requested_rangelist,
4328251881Speter    *target_implicit_rangelist, *explicit_rangelist;
4329251881Speter
4330251881Speter  /* Convert REVISION1 and REVISION2 to a rangelist.
4331251881Speter
4332251881Speter     Note: Talking about a requested merge range's inheritability
4333251881Speter     doesn't make much sense, but as we are using svn_merge_range_t
4334251881Speter     to describe it we need to pick *something*.  Since all the
4335251881Speter     rangelist manipulations in this function either don't consider
4336251881Speter     inheritance by default or we are requesting that they don't (i.e.
4337251881Speter     svn_rangelist_remove and svn_rangelist_intersect) then we could
4338251881Speter     set the inheritability as FALSE, it won't matter either way. */
4339251881Speter  requested_rangelist = svn_rangelist__initialize(revision1, revision2,
4340251881Speter                                                  TRUE, scratch_pool);
4341251881Speter
4342251881Speter  /* Now filter out revisions that have already been merged to CHILD. */
4343251881Speter
4344251881Speter  if (revision1 > revision2) /* This is a reverse merge. */
4345251881Speter    {
4346251881Speter      svn_rangelist_t *added_rangelist, *deleted_rangelist;
4347251881Speter
4348251881Speter      /* The revert range and will need to be reversed for
4349251881Speter         our svn_rangelist_* APIs to work properly. */
4350251881Speter      SVN_ERR(svn_rangelist_reverse(requested_rangelist, scratch_pool));
4351251881Speter
4352251881Speter      /* Set EXPLICIT_RANGELIST to the list of source-range revs that are
4353251881Speter         already recorded as merged to target. */
4354251881Speter      if (target_rangelist)
4355251881Speter        {
4356251881Speter          /* Return the intersection of the revs which are both already
4357251881Speter             represented by CHILD's explicit or inherited mergeinfo.
4358251881Speter
4359251881Speter             We don't consider inheritance when determining intersecting
4360251881Speter             ranges.  If we *did* consider inheritance, then our calculation
4361251881Speter             would be wrong.  For example, if the CHILD->REMAINING_RANGES is
4362251881Speter             5:3 and TARGET_RANGELIST is r5* (non-inheritable) then the
4363251881Speter             intersection would be r4.  And that would be wrong as we clearly
4364251881Speter             want to reverse merge both r4 and r5 in this case.  Ignoring the
4365251881Speter             ranges' inheritance results in an intersection of r4-5.
4366251881Speter
4367251881Speter             You might be wondering about CHILD's children, doesn't the above
4368251881Speter             imply that we will reverse merge r4-5 from them?  Nope, this is
4369251881Speter             safe to do because any path whose parent has non-inheritable
4370251881Speter             ranges is always considered a subtree with differing mergeinfo
4371251881Speter             even if that path has no explicit mergeinfo prior to the
4372251881Speter             merge -- See condition 3 in the doc string for
4373251881Speter             merge.c:get_mergeinfo_paths(). */
4374251881Speter          SVN_ERR(svn_rangelist_intersect(&explicit_rangelist,
4375251881Speter                                          target_rangelist,
4376251881Speter                                          requested_rangelist,
4377251881Speter                                          FALSE, scratch_pool));
4378251881Speter        }
4379251881Speter      else
4380251881Speter        {
4381251881Speter          explicit_rangelist =
4382251881Speter            apr_array_make(result_pool, 0, sizeof(svn_merge_range_t *));
4383251881Speter        }
4384251881Speter
4385251881Speter      /* Was any part of the requested reverse merge not accounted for in
4386251881Speter         CHILD's explicit or inherited mergeinfo? */
4387251881Speter      SVN_ERR(svn_rangelist_diff(&deleted_rangelist, &added_rangelist,
4388251881Speter                                 requested_rangelist, explicit_rangelist,
4389251881Speter                                 FALSE, scratch_pool));
4390251881Speter
4391251881Speter      if (deleted_rangelist->nelts == 0)
4392251881Speter        {
4393251881Speter          /* The whole of REVISION1:REVISION2 was represented in CHILD's
4394251881Speter             explicit/inherited mergeinfo, allocate CHILD's remaining
4395251881Speter             ranges in POOL and then we are done. */
4396251881Speter          SVN_ERR(svn_rangelist_reverse(requested_rangelist, scratch_pool));
4397251881Speter          child->remaining_ranges = svn_rangelist_dup(requested_rangelist,
4398251881Speter                                                      result_pool);
4399251881Speter        }
4400251881Speter      else /* We need to check CHILD's implicit mergeinfo. */
4401251881Speter        {
4402251881Speter          svn_rangelist_t *implicit_rangelist;
4403251881Speter
4404251881Speter          SVN_ERR(ensure_implicit_mergeinfo(parent,
4405251881Speter                                            child,
4406251881Speter                                            child_inherits_implicit,
4407251881Speter                                            revision1,
4408251881Speter                                            revision2,
4409251881Speter                                            ra_session,
4410251881Speter                                            ctx,
4411251881Speter                                            result_pool,
4412251881Speter                                            scratch_pool));
4413251881Speter
4414251881Speter          target_implicit_rangelist = svn_hash_gets(child->implicit_mergeinfo,
4415251881Speter                                                    mergeinfo_path);
4416251881Speter
4417251881Speter          if (target_implicit_rangelist)
4418251881Speter            SVN_ERR(svn_rangelist_intersect(&implicit_rangelist,
4419251881Speter                                            target_implicit_rangelist,
4420251881Speter                                            requested_rangelist,
4421251881Speter                                            FALSE, scratch_pool));
4422251881Speter          else
4423251881Speter            implicit_rangelist = apr_array_make(scratch_pool, 0,
4424251881Speter                                                sizeof(svn_merge_range_t *));
4425251881Speter
4426251881Speter          SVN_ERR(svn_rangelist_merge2(implicit_rangelist,
4427251881Speter                                       explicit_rangelist, scratch_pool,
4428251881Speter                                       scratch_pool));
4429251881Speter          SVN_ERR(svn_rangelist_reverse(implicit_rangelist, scratch_pool));
4430251881Speter          child->remaining_ranges = svn_rangelist_dup(implicit_rangelist,
4431251881Speter                                                      result_pool);
4432251881Speter        }
4433251881Speter    }
4434251881Speter  else /* This is a forward merge */
4435251881Speter    {
4436251881Speter      /* Set EXPLICIT_RANGELIST to the list of source-range revs that are
4437251881Speter         NOT already recorded as merged to target. */
4438251881Speter      if (target_rangelist)
4439251881Speter        {
4440251881Speter          /* See earlier comment preceding svn_rangelist_intersect() for
4441251881Speter             why we don't consider inheritance here. */
4442251881Speter          SVN_ERR(svn_rangelist_remove(&explicit_rangelist,
4443251881Speter                                       target_rangelist,
4444251881Speter                                       requested_rangelist, FALSE,
4445251881Speter                                       scratch_pool));
4446251881Speter        }
4447251881Speter      else
4448251881Speter        {
4449251881Speter          explicit_rangelist = svn_rangelist_dup(requested_rangelist,
4450251881Speter                                                 scratch_pool);
4451251881Speter        }
4452251881Speter
4453251881Speter      if (explicit_rangelist->nelts == 0)
4454251881Speter        {
4455251881Speter          child->remaining_ranges =
4456251881Speter            apr_array_make(result_pool, 0, sizeof(svn_merge_range_t *));
4457251881Speter        }
4458251881Speter      else
4459251881Speter/* ### TODO:  Which evil shall we choose?
4460251881Speter   ###
4461251881Speter   ### If we allow all forward-merges not already found in recorded
4462251881Speter   ### mergeinfo, we destroy the ability to, say, merge the whole of a
4463251881Speter   ### branch to the trunk while automatically ignoring the revisions
4464251881Speter   ### common to both.  That's bad.
4465251881Speter   ###
4466251881Speter   ### If we allow only forward-merges not found in either recorded
4467251881Speter   ### mergeinfo or implicit mergeinfo (natural history), then the
4468251881Speter   ### previous scenario works great, but we can't reverse-merge a
4469251881Speter   ### previous change made to our line of history and then remake it
4470251881Speter   ### (because the reverse-merge will leave no mergeinfo trace, and
4471251881Speter   ### the remake-it attempt will still find the original change in
4472251881Speter   ### natural mergeinfo.  But you know, that we happen to use 'merge'
4473251881Speter   ### for revision undoing is somewhat unnatural anyway, so I'm
4474251881Speter   ### finding myself having little interest in caring too much about
4475251881Speter   ### this.  That said, if we had a way of storing reverse merge
4476251881Speter   ### ranges, we'd be in good shape either way.
4477251881Speter*/
4478251881Speter#ifdef SVN_MERGE__ALLOW_ALL_FORWARD_MERGES_FROM_SELF
4479251881Speter        {
4480251881Speter          /* ### Don't consider implicit mergeinfo. */
4481251881Speter          child->remaining_ranges = svn_rangelist_dup(explicit_rangelist,
4482251881Speter                                                      pool);
4483251881Speter        }
4484251881Speter#else
4485251881Speter        {
4486251881Speter          /* Based on CHILD's TARGET_MERGEINFO there are ranges to merge.
4487251881Speter             Check CHILD's implicit mergeinfo to see if these remaining
4488251881Speter             ranges are represented there. */
4489251881Speter          SVN_ERR(ensure_implicit_mergeinfo(parent,
4490251881Speter                                            child,
4491251881Speter                                            child_inherits_implicit,
4492251881Speter                                            revision1,
4493251881Speter                                            revision2,
4494251881Speter                                            ra_session,
4495251881Speter                                            ctx,
4496251881Speter                                            result_pool,
4497251881Speter                                            scratch_pool));
4498251881Speter
4499251881Speter          target_implicit_rangelist = svn_hash_gets(child->implicit_mergeinfo,
4500251881Speter                                                    mergeinfo_path);
4501251881Speter          if (target_implicit_rangelist)
4502251881Speter            SVN_ERR(svn_rangelist_remove(&(child->remaining_ranges),
4503251881Speter                                         target_implicit_rangelist,
4504251881Speter                                         explicit_rangelist,
4505251881Speter                                         FALSE, result_pool));
4506251881Speter          else
4507251881Speter            child->remaining_ranges = svn_rangelist_dup(explicit_rangelist,
4508251881Speter                                                        result_pool);
4509251881Speter        }
4510251881Speter#endif
4511251881Speter    }
4512251881Speter
4513251881Speter  return SVN_NO_ERROR;
4514251881Speter}
4515251881Speter
4516251881Speter/* Helper for do_file_merge and do_directory_merge (by way of
4517251881Speter   populate_remaining_ranges() for the latter).
4518251881Speter
4519251881Speter   Determine what portions of SOURCE have already
4520251881Speter   been merged to CHILD->ABSPATH and populate CHILD->REMAINING_RANGES with
4521251881Speter   the ranges that still need merging.
4522251881Speter
4523251881Speter   SOURCE and CTX are all cascaded from the caller's arguments of the same
4524251881Speter   names.  Note that this means SOURCE adheres to the requirements noted in
4525251881Speter   `MERGEINFO MERGE SOURCE NORMALIZATION'.
4526251881Speter
4527251881Speter   CHILD represents a working copy path which is the merge target or one of
4528251881Speter   the target's subtrees.  If not NULL, PARENT is CHILD's nearest path-wise
4529251881Speter   ancestor - see 'THE CHILDREN_WITH_MERGEINFO ARRAY'.  TARGET_MERGEINFO is
4530251881Speter   the working mergeinfo on CHILD.
4531251881Speter
4532251881Speter   RA_SESSION is the session for the younger of SOURCE->loc1 and
4533251881Speter   SOURCE->loc2.
4534251881Speter
4535251881Speter   If the function needs to consider CHILD->IMPLICIT_MERGEINFO and
4536251881Speter   CHILD_INHERITS_IMPLICIT is true, then set CHILD->IMPLICIT_MERGEINFO to the
4537251881Speter   mergeinfo inherited from PARENT->IMPLICIT_MERGEINFO.  Otherwise contact
4538251881Speter   the repository for CHILD->IMPLICIT_MERGEINFO.
4539251881Speter
4540251881Speter   If not null, IMPLICIT_SRC_GAP is the gap, if any, in the natural history
4541251881Speter   of SOURCE, see merge_cmd_baton_t.implicit_src_gap.
4542251881Speter
4543251881Speter   SCRATCH_POOL is used for all temporary allocations.  Changes to CHILD and
4544251881Speter   PARENT are made in RESULT_POOL.
4545251881Speter
4546251881Speter   NOTE: This should only be called when honoring mergeinfo.
4547251881Speter
4548251881Speter   NOTE: If PARENT is present then this function must have previously been
4549251881Speter   called for PARENT, i.e. if populate_remaining_ranges() is calling this
4550251881Speter   function for a set of svn_client__merge_path_t* the calls must be made
4551251881Speter   in depth-first order.
4552251881Speter
4553251881Speter   NOTE: When performing reverse merges, return
4554251881Speter   SVN_ERR_CLIENT_NOT_READY_TO_MERGE if both locations in SOURCE and
4555251881Speter   CHILD->ABSPATH are all on the same line of history but CHILD->ABSPATH's
4556251881Speter   base revision is older than the SOURCE->rev1:rev2 range, see comment re
4557251881Speter   issue #2973 below.
4558251881Speter*/
4559251881Speterstatic svn_error_t *
4560251881Spetercalculate_remaining_ranges(svn_client__merge_path_t *parent,
4561251881Speter                           svn_client__merge_path_t *child,
4562251881Speter                           const merge_source_t *source,
4563251881Speter                           svn_mergeinfo_t target_mergeinfo,
4564251881Speter                           const apr_array_header_t *implicit_src_gap,
4565251881Speter                           svn_boolean_t child_inherits_implicit,
4566251881Speter                           svn_ra_session_t *ra_session,
4567251881Speter                           svn_client_ctx_t *ctx,
4568251881Speter                           apr_pool_t *result_pool,
4569251881Speter                           apr_pool_t *scratch_pool)
4570251881Speter{
4571251881Speter  const svn_client__pathrev_t *primary_src
4572251881Speter    = (source->loc1->rev < source->loc2->rev) ? source->loc2 : source->loc1;
4573251881Speter  const char *mergeinfo_path = svn_client__pathrev_fspath(primary_src,
4574251881Speter                                                          scratch_pool);
4575251881Speter  /* Intersection of TARGET_MERGEINFO and the merge history
4576251881Speter     described by SOURCE. */
4577251881Speter  svn_rangelist_t *target_rangelist;
4578251881Speter  svn_revnum_t child_base_revision;
4579251881Speter
4580251881Speter  /* Since this function should only be called when honoring mergeinfo and
4581251881Speter   * SOURCE adheres to the requirements noted in 'MERGEINFO MERGE SOURCE
4582251881Speter   * NORMALIZATION', SOURCE must be 'ancestral'. */
4583251881Speter  SVN_ERR_ASSERT(source->ancestral);
4584251881Speter
4585251881Speter  /* Determine which of the requested ranges to consider merging... */
4586251881Speter
4587251881Speter  /* Set TARGET_RANGELIST to the portion of TARGET_MERGEINFO that refers
4588251881Speter     to SOURCE (excluding any gap in SOURCE): first get all ranges from
4589251881Speter     TARGET_MERGEINFO that refer to the path of SOURCE, and then prune
4590251881Speter     any ranges that lie in the gap in SOURCE.
4591251881Speter
4592251881Speter     ### [JAF] In fact, that may still leave some ranges that lie entirely
4593251881Speter     outside the range of SOURCE; it seems we don't care about that.  */
4594251881Speter  if (target_mergeinfo)
4595251881Speter    target_rangelist = svn_hash_gets(target_mergeinfo, mergeinfo_path);
4596251881Speter  else
4597251881Speter    target_rangelist = NULL;
4598251881Speter  if (implicit_src_gap && target_rangelist)
4599251881Speter    {
4600251881Speter      /* Remove any mergeinfo referring to the 'gap' in SOURCE, as that
4601251881Speter         mergeinfo doesn't really refer to SOURCE at all but instead
4602251881Speter         refers to locations that are non-existent or on a different
4603251881Speter         line of history.  (Issue #3242.) */
4604251881Speter      SVN_ERR(svn_rangelist_remove(&target_rangelist,
4605251881Speter                                   implicit_src_gap, target_rangelist,
4606251881Speter                                   FALSE, result_pool));
4607251881Speter    }
4608251881Speter
4609251881Speter  /* Initialize CHILD->REMAINING_RANGES and filter out revisions already
4610251881Speter     merged (or, in the case of reverse merges, ranges not yet merged). */
4611251881Speter  SVN_ERR(filter_merged_revisions(parent, child, mergeinfo_path,
4612251881Speter                                  target_rangelist,
4613251881Speter                                  source->loc1->rev, source->loc2->rev,
4614251881Speter                                  child_inherits_implicit,
4615251881Speter                                  ra_session, ctx, result_pool,
4616251881Speter                                  scratch_pool));
4617251881Speter
4618251881Speter  /* Issue #2973 -- from the continuing series of "Why, since the advent of
4619251881Speter     merge tracking, allowing merges into mixed rev and locally modified
4620251881Speter     working copies isn't simple and could be considered downright evil".
4621251881Speter
4622251881Speter     If reverse merging a range to the WC path represented by CHILD, from
4623251881Speter     that path's own history, where the path inherits no locally modified
4624251881Speter     mergeinfo from its WC parents (i.e. there is no uncommitted merge to
4625251881Speter     the WC), and the path's base revision is older than the range, then
4626251881Speter     the merge will always be a no-op.  This is because we only allow reverse
4627251881Speter     merges of ranges in the path's explicit or natural mergeinfo and a
4628251881Speter     reverse merge from the path's future history obviously isn't going to be
4629251881Speter     in either, hence the no-op.
4630251881Speter
4631251881Speter     The problem is two-fold.  First, in a mixed rev WC, the change we
4632251881Speter     want to revert might actually be to some child of the target path
4633251881Speter     which is at a younger base revision.  Sure, we can merge directly
4634251881Speter     to that child or update the WC or even use --ignore-ancestry and then
4635251881Speter     successfully run the reverse merge, but that gets to the second
4636251881Speter     problem: Those courses of action are not very obvious.  Before 1.5 if
4637251881Speter     a user committed a change that didn't touch the commit target, then
4638251881Speter     immediately decided to revert that change via a reverse merge it would
4639251881Speter     just DTRT.  But with the advent of merge tracking the user gets a no-op.
4640251881Speter
4641251881Speter     So in the name of user friendliness, return an error suggesting a helpful
4642251881Speter     course of action.
4643251881Speter  */
4644251881Speter  SVN_ERR(svn_wc__node_get_base(NULL, &child_base_revision,
4645251881Speter                                NULL, NULL, NULL, NULL,
4646251881Speter                                ctx->wc_ctx, child->abspath,
4647251881Speter                                TRUE /* ignore_enoent */,
4648251881Speter                                FALSE /* show_hidden */,
4649251881Speter                                scratch_pool, scratch_pool));
4650251881Speter  /* If CHILD has no base revision then it hasn't been committed yet, so it
4651251881Speter     can't have any "future" history. */
4652251881Speter  if (SVN_IS_VALID_REVNUM(child_base_revision)
4653251881Speter      && ((child->remaining_ranges)->nelts == 0) /* Inoperative merge */
4654251881Speter      && (source->loc2->rev < source->loc1->rev)     /* Reverse merge */
4655251881Speter      && (child_base_revision <= source->loc2->rev))  /* From CHILD's future */
4656251881Speter    {
4657251881Speter      /* Hmmm, an inoperative reverse merge from the "future".  If it is
4658251881Speter         from our own future return a helpful error. */
4659251881Speter      svn_error_t *err;
4660251881Speter      svn_client__pathrev_t *start_loc;
4661251881Speter
4662251881Speter      err = svn_client__repos_location(&start_loc,
4663251881Speter                                       ra_session,
4664251881Speter                                       source->loc1,
4665251881Speter                                       child_base_revision,
4666251881Speter                                       ctx, scratch_pool, scratch_pool);
4667251881Speter      if (err)
4668251881Speter        {
4669251881Speter          if (err->apr_err == SVN_ERR_FS_NOT_FOUND
4670251881Speter              || err->apr_err == SVN_ERR_CLIENT_UNRELATED_RESOURCES)
4671251881Speter            svn_error_clear(err);
4672251881Speter          else
4673251881Speter            return svn_error_trace(err);
4674251881Speter        }
4675251881Speter      else
4676251881Speter        {
4677251881Speter          const char *url;
4678251881Speter
4679251881Speter          SVN_ERR(svn_wc__node_get_url(&url, ctx->wc_ctx, child->abspath,
4680251881Speter                                       scratch_pool, scratch_pool));
4681251881Speter          if (strcmp(start_loc->url, url) == 0)
4682251881Speter            return svn_error_create(SVN_ERR_CLIENT_MERGE_UPDATE_REQUIRED, NULL,
4683251881Speter                                    _("Cannot reverse-merge a range from a "
4684251881Speter                                      "path's own future history; try "
4685251881Speter                                      "updating first"));
4686251881Speter        }
4687251881Speter    }
4688251881Speter
4689251881Speter  return SVN_NO_ERROR;
4690251881Speter}
4691251881Speter
4692251881Speter/* Helper for populate_remaining_ranges().
4693251881Speter
4694251881Speter   SOURCE is cascaded from the arguments of the same name in
4695251881Speter   populate_remaining_ranges().
4696251881Speter
4697251881Speter   Note: The following comments assume a forward merge, i.e.
4698251881Speter   SOURCE->loc1->rev < SOURCE->loc2->rev.  If this is a reverse merge then
4699251881Speter   all the following comments still apply, but with SOURCE->loc1 switched
4700251881Speter   with SOURCE->loc2.
4701251881Speter
4702251881Speter   Like populate_remaining_ranges(), SOURCE must adhere to the restrictions
4703251881Speter   documented in 'MERGEINFO MERGE SOURCE NORMALIZATION'.  These restrictions
4704251881Speter   allow for a *single* gap in SOURCE, GAP_REV1:GAP_REV2 exclusive:inclusive
4705251881Speter   (where SOURCE->loc1->rev == GAP_REV1 <= GAP_REV2 < SOURCE->loc2->rev),
4706251881Speter   if SOURCE->loc2->url@(GAP_REV2+1) was copied from SOURCE->loc1.  If such
4707251881Speter   a gap exists, set *GAP_START and *GAP_END to the starting and ending
4708251881Speter   revisions of the gap.  Otherwise set both to SVN_INVALID_REVNUM.
4709251881Speter
4710251881Speter   For example, if the natural history of URL@2:URL@9 is 'trunk/:2,7-9' this
4711251881Speter   would indicate that trunk@7 was copied from trunk@2.  This function would
4712251881Speter   return GAP_START:GAP_END of 2:6 in this case.  Note that a path 'trunk'
4713251881Speter   might exist at r3-6, but it would not be on the same line of history as
4714251881Speter   trunk@9.
4715251881Speter
4716251881Speter   ### GAP_START is basically redundant, as (if there is a gap at all) it is
4717251881Speter   necessarily the older revision of SOURCE.
4718251881Speter
4719251881Speter   RA_SESSION is an open RA session to the repository in which SOURCE lives.
4720251881Speter*/
4721251881Speterstatic svn_error_t *
4722251881Speterfind_gaps_in_merge_source_history(svn_revnum_t *gap_start,
4723251881Speter                                  svn_revnum_t *gap_end,
4724251881Speter                                  const merge_source_t *source,
4725251881Speter                                  svn_ra_session_t *ra_session,
4726251881Speter                                  svn_client_ctx_t *ctx,
4727251881Speter                                  apr_pool_t *scratch_pool)
4728251881Speter{
4729251881Speter  svn_mergeinfo_t implicit_src_mergeinfo;
4730251881Speter  svn_revnum_t old_rev = MIN(source->loc1->rev, source->loc2->rev);
4731251881Speter  const svn_client__pathrev_t *primary_src
4732251881Speter    = (source->loc1->rev < source->loc2->rev) ? source->loc2 : source->loc1;
4733251881Speter  const char *merge_src_fspath = svn_client__pathrev_fspath(primary_src,
4734251881Speter                                                            scratch_pool);
4735251881Speter  svn_rangelist_t *rangelist;
4736251881Speter
4737251881Speter  SVN_ERR_ASSERT(source->ancestral);
4738251881Speter
4739251881Speter  /* Start by assuming there is no gap. */
4740251881Speter  *gap_start = *gap_end = SVN_INVALID_REVNUM;
4741251881Speter
4742251881Speter  /* Easy out: There can't be a gap between adjacent revisions. */
4743251881Speter  if (abs(source->loc1->rev - source->loc2->rev) == 1)
4744251881Speter    return SVN_NO_ERROR;
4745251881Speter
4746251881Speter  /* Get SOURCE as mergeinfo. */
4747251881Speter  SVN_ERR(svn_client__get_history_as_mergeinfo(&implicit_src_mergeinfo, NULL,
4748251881Speter                                               primary_src,
4749251881Speter                                               primary_src->rev, old_rev,
4750251881Speter                                               ra_session,
4751251881Speter                                               ctx, scratch_pool));
4752251881Speter
4753251881Speter  rangelist = svn_hash_gets(implicit_src_mergeinfo, merge_src_fspath);
4754251881Speter
4755251881Speter  if (!rangelist) /* ### Can we ever not find a rangelist? */
4756251881Speter    return SVN_NO_ERROR;
4757251881Speter
4758251881Speter  /* A gap in natural history can result from either a copy or
4759251881Speter     a rename.  If from a copy then history as mergeinfo will look
4760251881Speter     something like this:
4761251881Speter
4762251881Speter       '/trunk:X,Y-Z'
4763251881Speter
4764251881Speter     If from a rename it will look like this:
4765251881Speter
4766251881Speter       '/trunk_old_name:X'
4767251881Speter       '/trunk_new_name:Y-Z'
4768251881Speter
4769251881Speter    In both cases the gap, if it exists, is M-N, where M = X + 1 and
4770251881Speter    N = Y - 1.
4771251881Speter
4772251881Speter    Note that per the rules of 'MERGEINFO MERGE SOURCE NORMALIZATION' we
4773251881Speter    should never have multiple gaps, e.g. if we see anything like the
4774251881Speter    following then something is quite wrong:
4775251881Speter
4776251881Speter        '/trunk_old_name:A,B-C'
4777251881Speter        '/trunk_new_name:D-E'
4778251881Speter  */
4779251881Speter
4780251881Speter  if (rangelist->nelts > 1) /* Copy */
4781251881Speter    {
4782251881Speter      const svn_merge_range_t *gap;
4783251881Speter      /* As mentioned above, multiple gaps *shouldn't* be possible. */
4784251881Speter      SVN_ERR_ASSERT(apr_hash_count(implicit_src_mergeinfo) == 1);
4785251881Speter
4786251881Speter      gap = APR_ARRAY_IDX(rangelist, rangelist->nelts - 1,
4787251881Speter                          const svn_merge_range_t *);
4788251881Speter
4789251881Speter      *gap_start = MIN(source->loc1->rev, source->loc2->rev);
4790251881Speter      *gap_end = gap->start;
4791251881Speter
4792251881Speter      /* ### Issue #4132:
4793251881Speter         ### This assertion triggers in merge_tests.py svnmucc_abuse_1()
4794251881Speter         ### when a node is replaced by an older copy of itself.
4795251881Speter
4796251881Speter         BH: I think we should review this and the 'rename' case to find
4797251881Speter             out which behavior we really want, and if we can really
4798251881Speter             determine what happened this way. */
4799251881Speter      SVN_ERR_ASSERT(*gap_start < *gap_end);
4800251881Speter    }
4801251881Speter  else if (apr_hash_count(implicit_src_mergeinfo) > 1) /* Rename */
4802251881Speter    {
4803251881Speter      svn_rangelist_t *requested_rangelist =
4804251881Speter        svn_rangelist__initialize(MIN(source->loc1->rev, source->loc2->rev),
4805251881Speter                                  MAX(source->loc1->rev, source->loc2->rev),
4806251881Speter                                  TRUE, scratch_pool);
4807251881Speter      svn_rangelist_t *implicit_rangelist =
4808251881Speter        apr_array_make(scratch_pool, 2, sizeof(svn_merge_range_t *));
4809251881Speter      svn_rangelist_t *gap_rangelist;
4810251881Speter
4811251881Speter      SVN_ERR(svn_rangelist__merge_many(implicit_rangelist,
4812251881Speter                                        implicit_src_mergeinfo,
4813251881Speter                                        scratch_pool, scratch_pool));
4814251881Speter      SVN_ERR(svn_rangelist_remove(&gap_rangelist, implicit_rangelist,
4815251881Speter                                   requested_rangelist, FALSE,
4816251881Speter                                   scratch_pool));
4817251881Speter
4818251881Speter      /* If there is anything left it is the gap. */
4819251881Speter      if (gap_rangelist->nelts)
4820251881Speter        {
4821251881Speter          svn_merge_range_t *gap_range =
4822251881Speter            APR_ARRAY_IDX(gap_rangelist, 0, svn_merge_range_t *);
4823251881Speter
4824251881Speter          *gap_start = gap_range->start;
4825251881Speter          *gap_end = gap_range->end;
4826251881Speter        }
4827251881Speter    }
4828251881Speter
4829251881Speter  SVN_ERR_ASSERT(*gap_start == MIN(source->loc1->rev, source->loc2->rev)
4830251881Speter                 || (*gap_start == SVN_INVALID_REVNUM
4831251881Speter                     && *gap_end == SVN_INVALID_REVNUM));
4832251881Speter  return SVN_NO_ERROR;
4833251881Speter}
4834251881Speter
4835251881Speter/* Helper for do_directory_merge().
4836251881Speter
4837251881Speter   For each (svn_client__merge_path_t *) child in CHILDREN_WITH_MERGEINFO,
4838251881Speter   populate that child's 'remaining_ranges' list with (### ... what?),
4839251881Speter   and populate that child's 'implicit_mergeinfo' with its implicit
4840251881Speter   mergeinfo (natural history).  CHILDREN_WITH_MERGEINFO is expected
4841251881Speter   to be sorted in depth first order and each child must be processed in
4842251881Speter   that order.  The inheritability of all calculated ranges is TRUE.
4843251881Speter
4844251881Speter   If mergeinfo is being honored (based on MERGE_B -- see HONOR_MERGEINFO()
4845251881Speter   for how this is determined), this function will actually try to be
4846251881Speter   intelligent about populating remaining_ranges list.  Otherwise, it
4847251881Speter   will claim that each child has a single remaining range, from
4848251881Speter   SOURCE->rev1, to SOURCE->rev2.
4849251881Speter   ### We also take the short-cut if doing record-only.  Why?
4850251881Speter
4851251881Speter   SCRATCH_POOL is used for all temporary allocations.  Changes to
4852251881Speter   CHILDREN_WITH_MERGEINFO are made in RESULT_POOL.
4853251881Speter
4854251881Speter   Note that if SOURCE->rev1 > SOURCE->rev2, then each child's remaining_ranges
4855251881Speter   member does not adhere to the API rules for rangelists described in
4856251881Speter   svn_mergeinfo.h -- See svn_client__merge_path_t.
4857251881Speter
4858251881Speter   See `MERGEINFO MERGE SOURCE NORMALIZATION' for more requirements
4859251881Speter   around SOURCE.
4860251881Speter*/
4861251881Speterstatic svn_error_t *
4862251881Speterpopulate_remaining_ranges(apr_array_header_t *children_with_mergeinfo,
4863251881Speter                          const merge_source_t *source,
4864251881Speter                          svn_ra_session_t *ra_session,
4865251881Speter                          merge_cmd_baton_t *merge_b,
4866251881Speter                          apr_pool_t *result_pool,
4867251881Speter                          apr_pool_t *scratch_pool)
4868251881Speter{
4869251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
4870251881Speter  int i;
4871251881Speter  svn_revnum_t gap_start, gap_end;
4872251881Speter
4873251881Speter  /* If we aren't honoring mergeinfo or this is a --record-only merge,
4874251881Speter     we'll make quick work of this by simply adding dummy SOURCE->rev1:rev2
4875251881Speter     ranges for all children. */
4876251881Speter  if (! HONOR_MERGEINFO(merge_b) || merge_b->record_only)
4877251881Speter    {
4878251881Speter      for (i = 0; i < children_with_mergeinfo->nelts; i++)
4879251881Speter        {
4880251881Speter          svn_client__merge_path_t *child =
4881251881Speter            APR_ARRAY_IDX(children_with_mergeinfo, i,
4882251881Speter                          svn_client__merge_path_t *);
4883251881Speter
4884251881Speter          svn_pool_clear(iterpool);
4885251881Speter
4886251881Speter          /* Issue #3646 'record-only merges create self-referential
4887251881Speter             mergeinfo'.  Get the merge target's implicit mergeinfo (natural
4888251881Speter             history).  We'll use it later to avoid setting self-referential
4889251881Speter             mergeinfo -- see filter_natural_history_from_mergeinfo(). */
4890251881Speter          if (i == 0) /* First item is always the merge target. */
4891251881Speter            {
4892251881Speter              SVN_ERR(get_full_mergeinfo(NULL, /* child->pre_merge_mergeinfo */
4893251881Speter                                         &(child->implicit_mergeinfo),
4894251881Speter                                         NULL, /* child->inherited_mergeinfo */
4895251881Speter                                         svn_mergeinfo_inherited, ra_session,
4896251881Speter                                         child->abspath,
4897251881Speter                                         MAX(source->loc1->rev,
4898251881Speter                                             source->loc2->rev),
4899251881Speter                                         MIN(source->loc1->rev,
4900251881Speter                                             source->loc2->rev),
4901251881Speter                                         merge_b->ctx, result_pool,
4902251881Speter                                         iterpool));
4903251881Speter            }
4904251881Speter          else
4905251881Speter            {
4906251881Speter              /* Issue #3443 - Subtrees of the merge target can inherit
4907251881Speter                 their parent's implicit mergeinfo in most cases. */
4908251881Speter              svn_client__merge_path_t *parent
4909251881Speter                = find_nearest_ancestor(children_with_mergeinfo,
4910251881Speter                                        FALSE, child->abspath);
4911251881Speter              svn_boolean_t child_inherits_implicit;
4912251881Speter
4913251881Speter              /* If CHILD is a subtree then its parent must be in
4914251881Speter                 CHILDREN_WITH_MERGEINFO, see the global comment
4915251881Speter                 'THE CHILDREN_WITH_MERGEINFO ARRAY'. */
4916251881Speter              SVN_ERR_ASSERT(parent);
4917251881Speter
4918251881Speter              child_inherits_implicit = (parent && !child->switched);
4919251881Speter              SVN_ERR(ensure_implicit_mergeinfo(parent, child,
4920251881Speter                                                child_inherits_implicit,
4921251881Speter                                                source->loc1->rev,
4922251881Speter                                                source->loc2->rev,
4923251881Speter                                                ra_session, merge_b->ctx,
4924251881Speter                                                result_pool, iterpool));
4925251881Speter            }
4926251881Speter
4927251881Speter          child->remaining_ranges = svn_rangelist__initialize(source->loc1->rev,
4928251881Speter                                                              source->loc2->rev,
4929251881Speter                                                              TRUE,
4930251881Speter                                                              result_pool);
4931251881Speter        }
4932251881Speter      svn_pool_destroy(iterpool);
4933251881Speter      return SVN_NO_ERROR;
4934251881Speter    }
4935251881Speter
4936251881Speter  /* If, in the merge source's history, there was a copy from an older
4937251881Speter     revision, then SOURCE->loc2->url won't exist at some range M:N, where
4938251881Speter     SOURCE->loc1->rev < M < N < SOURCE->loc2->rev. The rules of 'MERGEINFO
4939251881Speter     MERGE SOURCE NORMALIZATION' allow this, but we must ignore these gaps
4940251881Speter     when calculating what ranges remain to be merged from SOURCE. If we
4941251881Speter     don't and try to merge any part of SOURCE->loc2->url@M:N we would
4942251881Speter     break the editor since no part of that actually exists.  See
4943251881Speter     http://svn.haxx.se/dev/archive-2008-11/0618.shtml.
4944251881Speter
4945251881Speter     Find the gaps in the merge target's history, if any.  Eventually
4946251881Speter     we will adjust CHILD->REMAINING_RANGES such that we don't describe
4947251881Speter     non-existent paths to the editor. */
4948251881Speter  SVN_ERR(find_gaps_in_merge_source_history(&gap_start, &gap_end,
4949251881Speter                                            source,
4950251881Speter                                            ra_session, merge_b->ctx,
4951251881Speter                                            iterpool));
4952251881Speter
4953251881Speter  /* Stash any gap in the merge command baton, we'll need it later when
4954251881Speter     recording mergeinfo describing this merge. */
4955251881Speter  if (SVN_IS_VALID_REVNUM(gap_start) && SVN_IS_VALID_REVNUM(gap_end))
4956251881Speter    merge_b->implicit_src_gap = svn_rangelist__initialize(gap_start, gap_end,
4957251881Speter                                                          TRUE, result_pool);
4958251881Speter
4959251881Speter  for (i = 0; i < children_with_mergeinfo->nelts; i++)
4960251881Speter    {
4961251881Speter      svn_client__merge_path_t *child =
4962251881Speter        APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
4963251881Speter      const char *child_repos_path
4964251881Speter        = svn_dirent_skip_ancestor(merge_b->target->abspath, child->abspath);
4965251881Speter      merge_source_t child_source;
4966251881Speter      svn_client__merge_path_t *parent = NULL;
4967251881Speter      svn_boolean_t child_inherits_implicit;
4968251881Speter
4969251881Speter      svn_pool_clear(iterpool);
4970251881Speter
4971251881Speter      /* If the path is absent don't do subtree merge either. */
4972251881Speter      SVN_ERR_ASSERT(child);
4973251881Speter      if (child->absent)
4974251881Speter        continue;
4975251881Speter
4976251881Speter      SVN_ERR_ASSERT(child_repos_path != NULL);
4977251881Speter      child_source.loc1 = svn_client__pathrev_join_relpath(
4978251881Speter                            source->loc1, child_repos_path, iterpool);
4979251881Speter      child_source.loc2 = svn_client__pathrev_join_relpath(
4980251881Speter                            source->loc2, child_repos_path, iterpool);
4981251881Speter      /* ### Is the child 'ancestral' over the same revision range?  It's
4982251881Speter       * not necessarily true that a child is 'ancestral' if the parent is,
4983251881Speter       * nor that it's not if the parent is not.  However, here we claim
4984251881Speter       * that it is.  Before we had this 'ancestral' field that we need to
4985251881Speter       * set explicitly, the claim was implicit.  Either way, the impact is
4986251881Speter       * that we might pass calculate_remaining_ranges() a source that is
4987251881Speter       * not in fact 'ancestral' (despite its 'ancestral' field being true),
4988251881Speter       * contrary to its doc-string. */
4989251881Speter      child_source.ancestral = source->ancestral;
4990251881Speter
4991251881Speter      /* Get the explicit/inherited mergeinfo for CHILD.  If CHILD is the
4992251881Speter         merge target then also get its implicit mergeinfo.  Otherwise defer
4993251881Speter         this until we know it is absolutely necessary, since it requires an
4994251881Speter         expensive round trip communication with the server. */
4995251881Speter      SVN_ERR(get_full_mergeinfo(
4996251881Speter        child->pre_merge_mergeinfo ? NULL : &(child->pre_merge_mergeinfo),
4997251881Speter        /* Get implicit only for merge target. */
4998251881Speter        (i == 0) ? &(child->implicit_mergeinfo) : NULL,
4999251881Speter        &(child->inherited_mergeinfo),
5000251881Speter        svn_mergeinfo_inherited, ra_session,
5001251881Speter        child->abspath,
5002251881Speter        MAX(source->loc1->rev, source->loc2->rev),
5003251881Speter        MIN(source->loc1->rev, source->loc2->rev),
5004251881Speter        merge_b->ctx, result_pool, iterpool));
5005251881Speter
5006251881Speter      /* If CHILD isn't the merge target find its parent. */
5007251881Speter      if (i > 0)
5008251881Speter        {
5009251881Speter          parent = find_nearest_ancestor(children_with_mergeinfo,
5010251881Speter                                         FALSE, child->abspath);
5011251881Speter          /* If CHILD is a subtree then its parent must be in
5012251881Speter             CHILDREN_WITH_MERGEINFO, see the global comment
5013251881Speter             'THE CHILDREN_WITH_MERGEINFO ARRAY'. */
5014251881Speter          SVN_ERR_ASSERT(parent);
5015251881Speter        }
5016251881Speter
5017251881Speter      /* Issue #3443 - Can CHILD inherit PARENT's implicit mergeinfo, saving
5018251881Speter         us from having to ask the repos?  The only time we can't do this is if
5019251881Speter         CHILD is the merge target and so there is no PARENT to inherit from
5020251881Speter         or if CHILD is the root of a switched subtree, in which case PARENT
5021251881Speter         exists but is not CHILD's repository parent. */
5022251881Speter      child_inherits_implicit = (parent && !child->switched);
5023251881Speter
5024251881Speter      SVN_ERR(calculate_remaining_ranges(parent, child,
5025251881Speter                                         &child_source,
5026251881Speter                                         child->pre_merge_mergeinfo,
5027251881Speter                                         merge_b->implicit_src_gap,
5028251881Speter                                         child_inherits_implicit,
5029251881Speter                                         ra_session,
5030251881Speter                                         merge_b->ctx, result_pool,
5031251881Speter                                         iterpool));
5032251881Speter
5033251881Speter      /* Deal with any gap in SOURCE's natural history.
5034251881Speter
5035251881Speter         If the gap is a proper subset of CHILD->REMAINING_RANGES then we can
5036251881Speter         safely ignore it since we won't describe this path/rev pair.
5037251881Speter
5038251881Speter         If the gap exactly matches or is a superset of a range in
5039251881Speter         CHILD->REMAINING_RANGES then we must remove that range so we don't
5040251881Speter         attempt to describe non-existent paths via the reporter, this will
5041251881Speter         break the editor and our merge.
5042251881Speter
5043251881Speter         If the gap adjoins or overlaps a range in CHILD->REMAINING_RANGES
5044251881Speter         then we must *add* the gap so we span the missing revisions. */
5045251881Speter      if (child->remaining_ranges->nelts
5046251881Speter          && merge_b->implicit_src_gap)
5047251881Speter        {
5048251881Speter          int j;
5049251881Speter          svn_boolean_t proper_subset = FALSE;
5050251881Speter          svn_boolean_t overlaps_or_adjoins = FALSE;
5051251881Speter
5052251881Speter          /* If this is a reverse merge reorder CHILD->REMAINING_RANGES
5053251881Speter              so it will work with the svn_rangelist_* APIs below. */
5054251881Speter          if (source->loc1->rev > source->loc2->rev)
5055251881Speter            SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool));
5056251881Speter
5057251881Speter          for (j = 0; j < child->remaining_ranges->nelts; j++)
5058251881Speter            {
5059251881Speter              svn_merge_range_t *range
5060251881Speter                = APR_ARRAY_IDX(child->remaining_ranges, j, svn_merge_range_t *);
5061251881Speter
5062251881Speter              if ((range->start <= gap_start && gap_end < range->end)
5063251881Speter                  || (range->start < gap_start && gap_end <= range->end))
5064251881Speter                {
5065251881Speter                  proper_subset = TRUE;
5066251881Speter                  break;
5067251881Speter                }
5068251881Speter              else if ((gap_start == range->start) && (range->end == gap_end))
5069251881Speter                {
5070251881Speter                  break;
5071251881Speter                }
5072251881Speter              else if (gap_start <= range->end && range->start <= gap_end)
5073251881Speter                /* intersect */
5074251881Speter                {
5075251881Speter                  overlaps_or_adjoins = TRUE;
5076251881Speter                  break;
5077251881Speter                }
5078251881Speter            }
5079251881Speter
5080251881Speter          if (!proper_subset)
5081251881Speter            {
5082251881Speter              /* We need to make adjustments.  Remove from, or add the gap
5083251881Speter                 to, CHILD->REMAINING_RANGES as appropriate. */
5084251881Speter
5085251881Speter              if (overlaps_or_adjoins)
5086251881Speter                SVN_ERR(svn_rangelist_merge2(child->remaining_ranges,
5087251881Speter                                             merge_b->implicit_src_gap,
5088251881Speter                                             result_pool, iterpool));
5089251881Speter              else /* equals == TRUE */
5090251881Speter                SVN_ERR(svn_rangelist_remove(&(child->remaining_ranges),
5091251881Speter                                             merge_b->implicit_src_gap,
5092251881Speter                                             child->remaining_ranges, FALSE,
5093251881Speter                                             result_pool));
5094251881Speter            }
5095251881Speter
5096251881Speter          if (source->loc1->rev > source->loc2->rev) /* Reverse merge */
5097251881Speter            SVN_ERR(svn_rangelist_reverse(child->remaining_ranges, iterpool));
5098251881Speter        }
5099251881Speter    }
5100251881Speter
5101251881Speter  svn_pool_destroy(iterpool);
5102251881Speter  return SVN_NO_ERROR;
5103251881Speter}
5104251881Speter
5105251881Speter
5106251881Speter/*-----------------------------------------------------------------------*/
5107251881Speter
5108251881Speter/*** Other Helper Functions ***/
5109251881Speter
5110251881Speter/* Calculate the new mergeinfo for the target tree rooted at TARGET_ABSPATH
5111251881Speter   based on MERGES (a mapping of absolute WC paths to rangelists representing
5112251881Speter   a merge from the source SOURCE_FSPATH).
5113251881Speter
5114251881Speter   If RESULT_CATALOG is NULL, then record the new mergeinfo in the WC (at,
5115251881Speter   and possibly below, TARGET_ABSPATH).
5116251881Speter
5117251881Speter   If RESULT_CATALOG is not NULL, then don't record the new mergeinfo on the
5118251881Speter   WC, but instead record it in RESULT_CATALOG, where the keys are absolute
5119251881Speter   working copy paths and the values are the new mergeinfos for each.
5120251881Speter   Allocate additions to RESULT_CATALOG in pool which RESULT_CATALOG was
5121251881Speter   created in. */
5122251881Speterstatic svn_error_t *
5123251881Speterupdate_wc_mergeinfo(svn_mergeinfo_catalog_t result_catalog,
5124251881Speter                    const char *target_abspath,
5125251881Speter                    const char *source_fspath,
5126251881Speter                    apr_hash_t *merges,
5127251881Speter                    svn_boolean_t is_rollback,
5128251881Speter                    svn_client_ctx_t *ctx,
5129251881Speter                    apr_pool_t *scratch_pool)
5130251881Speter{
5131251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
5132251881Speter  apr_hash_index_t *hi;
5133251881Speter
5134251881Speter  /* Combine the mergeinfo for the revision range just merged into
5135251881Speter     the WC with its on-disk mergeinfo. */
5136251881Speter  for (hi = apr_hash_first(scratch_pool, merges); hi; hi = apr_hash_next(hi))
5137251881Speter    {
5138251881Speter      const char *local_abspath = svn__apr_hash_index_key(hi);
5139251881Speter      svn_rangelist_t *ranges = svn__apr_hash_index_val(hi);
5140251881Speter      svn_rangelist_t *rangelist;
5141251881Speter      svn_error_t *err;
5142251881Speter      const char *local_abspath_rel_to_target;
5143251881Speter      const char *fspath;
5144251881Speter      svn_mergeinfo_t mergeinfo;
5145251881Speter
5146251881Speter      svn_pool_clear(iterpool);
5147251881Speter
5148251881Speter      /* As some of the merges may've changed the WC's mergeinfo, get
5149251881Speter         a fresh copy before using it to update the WC's mergeinfo. */
5150251881Speter      err = svn_client__parse_mergeinfo(&mergeinfo, ctx->wc_ctx,
5151251881Speter                                        local_abspath, iterpool, iterpool);
5152251881Speter
5153251881Speter      /* If a directory PATH was skipped because it is missing or was
5154251881Speter         obstructed by an unversioned item then there's nothing we can
5155251881Speter         do with that, so skip it. */
5156251881Speter      if (err)
5157251881Speter        {
5158251881Speter          if (err->apr_err == SVN_ERR_WC_NOT_LOCKED
5159251881Speter              || err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
5160251881Speter            {
5161251881Speter              svn_error_clear(err);
5162251881Speter              continue;
5163251881Speter            }
5164251881Speter          else
5165251881Speter            {
5166251881Speter              return svn_error_trace(err);
5167251881Speter            }
5168251881Speter        }
5169251881Speter
5170251881Speter      /* If we are attempting to set empty revision range override mergeinfo
5171251881Speter         on a path with no explicit mergeinfo, we first need the
5172251881Speter         mergeinfo that path inherits. */
5173251881Speter      if (mergeinfo == NULL && ranges->nelts == 0)
5174251881Speter        {
5175251881Speter          SVN_ERR(svn_client__get_wc_mergeinfo(&mergeinfo, NULL,
5176251881Speter                                               svn_mergeinfo_nearest_ancestor,
5177251881Speter                                               local_abspath, NULL, NULL,
5178251881Speter                                               FALSE, ctx, iterpool, iterpool));
5179251881Speter        }
5180251881Speter
5181251881Speter      if (mergeinfo == NULL)
5182251881Speter        mergeinfo = apr_hash_make(iterpool);
5183251881Speter
5184251881Speter      local_abspath_rel_to_target = svn_dirent_skip_ancestor(target_abspath,
5185251881Speter                                                             local_abspath);
5186251881Speter      SVN_ERR_ASSERT(local_abspath_rel_to_target != NULL);
5187251881Speter      fspath = svn_fspath__join(source_fspath,
5188251881Speter                                local_abspath_rel_to_target,
5189251881Speter                                iterpool);
5190251881Speter      rangelist = svn_hash_gets(mergeinfo, fspath);
5191251881Speter      if (rangelist == NULL)
5192251881Speter        rangelist = apr_array_make(iterpool, 0, sizeof(svn_merge_range_t *));
5193251881Speter
5194251881Speter      if (is_rollback)
5195251881Speter        {
5196251881Speter          ranges = svn_rangelist_dup(ranges, iterpool);
5197251881Speter          SVN_ERR(svn_rangelist_reverse(ranges, iterpool));
5198251881Speter          SVN_ERR(svn_rangelist_remove(&rangelist, ranges, rangelist,
5199251881Speter                                       FALSE,
5200251881Speter                                       iterpool));
5201251881Speter        }
5202251881Speter      else
5203251881Speter        {
5204251881Speter          SVN_ERR(svn_rangelist_merge2(rangelist, ranges, iterpool, iterpool));
5205251881Speter        }
5206251881Speter      /* Update the mergeinfo by adjusting the path's rangelist. */
5207251881Speter      svn_hash_sets(mergeinfo, fspath, rangelist);
5208251881Speter
5209251881Speter      if (is_rollback && apr_hash_count(mergeinfo) == 0)
5210251881Speter        mergeinfo = NULL;
5211251881Speter
5212251881Speter      svn_mergeinfo__remove_empty_rangelists(mergeinfo, scratch_pool);
5213251881Speter
5214251881Speter      if (result_catalog)
5215251881Speter        {
5216251881Speter          svn_mergeinfo_t existing_mergeinfo =
5217251881Speter            svn_hash_gets(result_catalog, local_abspath);
5218251881Speter          apr_pool_t *result_catalog_pool = apr_hash_pool_get(result_catalog);
5219251881Speter
5220251881Speter          if (existing_mergeinfo)
5221251881Speter            SVN_ERR(svn_mergeinfo_merge2(mergeinfo, existing_mergeinfo,
5222251881Speter                                         result_catalog_pool, scratch_pool));
5223251881Speter          svn_hash_sets(result_catalog,
5224251881Speter                        apr_pstrdup(result_catalog_pool, local_abspath),
5225251881Speter                        svn_mergeinfo_dup(mergeinfo, result_catalog_pool));
5226251881Speter        }
5227251881Speter      else
5228251881Speter        {
5229251881Speter          err = svn_client__record_wc_mergeinfo(local_abspath, mergeinfo,
5230251881Speter                                                TRUE, ctx, iterpool);
5231251881Speter
5232251881Speter          if (err && err->apr_err == SVN_ERR_ENTRY_NOT_FOUND)
5233251881Speter            {
5234251881Speter              /* PATH isn't just missing, it's not even versioned as far
5235251881Speter                 as this working copy knows.  But it was included in
5236251881Speter                 MERGES, which means that the server knows about it.
5237251881Speter                 Likely we don't have access to the source due to authz
5238251881Speter                 restrictions.  For now just clear the error and
5239251881Speter                 continue...
5240251881Speter
5241251881Speter                 ### TODO:  Set non-inheritable mergeinfo on PATH's immediate
5242251881Speter                 ### parent and normal mergeinfo on PATH's siblings which we
5243251881Speter                 ### do have access to. */
5244251881Speter              svn_error_clear(err);
5245251881Speter            }
5246251881Speter          else
5247251881Speter            SVN_ERR(err);
5248251881Speter        }
5249251881Speter    }
5250251881Speter
5251251881Speter  svn_pool_destroy(iterpool);
5252251881Speter  return SVN_NO_ERROR;
5253251881Speter}
5254251881Speter
5255251881Speter/* Helper for record_mergeinfo_for_dir_merge().
5256251881Speter
5257251881Speter   Record override mergeinfo on any paths skipped during a merge.
5258251881Speter
5259251881Speter   Set empty mergeinfo on each path in MERGE_B->SKIPPED_ABSPATHS so the path
5260251881Speter   does not incorrectly inherit mergeinfo that will later be describing
5261251881Speter   the merge.
5262251881Speter
5263251881Speter   MERGEINFO_PATH and MERGE_B are cascaded from
5264251881Speter   arguments of the same name in the caller.
5265251881Speter
5266251881Speter   IS_ROLLBACK is true if the caller is recording a reverse merge and false
5267251881Speter   otherwise.  RANGELIST is the set of revisions being merged from
5268251881Speter   MERGEINFO_PATH to MERGE_B->target. */
5269251881Speterstatic svn_error_t *
5270251881Speterrecord_skips_in_mergeinfo(const char *mergeinfo_path,
5271251881Speter                          const svn_rangelist_t *rangelist,
5272251881Speter                          svn_boolean_t is_rollback,
5273251881Speter                          merge_cmd_baton_t *merge_b,
5274251881Speter                          apr_pool_t *scratch_pool)
5275251881Speter{
5276251881Speter  apr_hash_index_t *hi;
5277251881Speter  apr_hash_t *merges;
5278251881Speter  apr_size_t nbr_skips = apr_hash_count(merge_b->skipped_abspaths);
5279251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
5280251881Speter
5281251881Speter  if (nbr_skips == 0)
5282251881Speter    return SVN_NO_ERROR;
5283251881Speter
5284251881Speter  merges = apr_hash_make(scratch_pool);
5285251881Speter
5286251881Speter  /* Override the mergeinfo for child paths which weren't actually merged. */
5287251881Speter  for (hi = apr_hash_first(scratch_pool, merge_b->skipped_abspaths); hi;
5288251881Speter       hi = apr_hash_next(hi))
5289251881Speter    {
5290251881Speter      const char *skipped_abspath = svn__apr_hash_index_key(hi);
5291251881Speter      svn_wc_notify_state_t obstruction_state;
5292251881Speter
5293251881Speter      svn_pool_clear(iterpool);
5294251881Speter
5295251881Speter      /* Before we override, make sure this is a versioned path, it might
5296251881Speter         be an external or missing from disk due to authz restrictions. */
5297251881Speter      SVN_ERR(perform_obstruction_check(&obstruction_state, NULL, NULL,
5298251881Speter                                        NULL, NULL,
5299251881Speter                                        merge_b, skipped_abspath,
5300251881Speter                                        iterpool));
5301251881Speter      if (obstruction_state == svn_wc_notify_state_obstructed
5302251881Speter          || obstruction_state == svn_wc_notify_state_missing)
5303251881Speter        continue;
5304251881Speter
5305251881Speter      /* Add an empty range list for this path.
5306251881Speter
5307251881Speter         ### TODO: This works fine for a file path skipped because it is
5308251881Speter         ### missing as long as the file's parent directory is present.
5309251881Speter         ### But missing directory paths skipped are not handled yet,
5310251881Speter         ### see issue #2915.
5311251881Speter
5312251881Speter         ### TODO: An empty range is fine if the skipped path doesn't
5313251881Speter         ### inherit any mergeinfo from a parent, but if it does
5314251881Speter         ### we need to account for that.  See issue #3440
5315251881Speter         ### http://subversion.tigris.org/issues/show_bug.cgi?id=3440. */
5316251881Speter      svn_hash_sets(merges, skipped_abspath,
5317251881Speter                    apr_array_make(scratch_pool, 0,
5318251881Speter                                   sizeof(svn_merge_range_t *)));
5319251881Speter
5320251881Speter      /* if (nbr_skips < notify_b->nbr_notifications)
5321251881Speter           ### Use RANGELIST as the mergeinfo for all children of
5322251881Speter           ### this path which were not also explicitly
5323251881Speter           ### skipped? */
5324251881Speter    }
5325251881Speter  SVN_ERR(update_wc_mergeinfo(NULL, merge_b->target->abspath,
5326251881Speter                              mergeinfo_path, merges,
5327251881Speter                              is_rollback, merge_b->ctx, iterpool));
5328251881Speter  svn_pool_destroy(iterpool);
5329251881Speter  return SVN_NO_ERROR;
5330251881Speter}
5331251881Speter
5332251881Speter/* Data for reporting when a merge aborted because of raising conflicts.
5333251881Speter */
5334251881Spetertypedef struct single_range_conflict_report_t
5335251881Speter{
5336251881Speter  /* What sub-range of the requested source raised conflicts?
5337251881Speter   * The 'inheritable' flag is ignored. */
5338251881Speter  merge_source_t *conflicted_range;
5339251881Speter  /* What sub-range of the requested source remains to be merged?
5340251881Speter   * NULL if no more.  The 'inheritable' flag is ignored. */
5341251881Speter  merge_source_t *remaining_source;
5342251881Speter
5343251881Speter} single_range_conflict_report_t;
5344251881Speter
5345251881Speter/* Create a single_range_conflict_report_t, containing deep copies of
5346251881Speter * CONFLICTED_RANGE and REMAINING_SOURCE, allocated in RESULT_POOL. */
5347251881Speterstatic single_range_conflict_report_t *
5348251881Spetersingle_range_conflict_report_create(const merge_source_t *conflicted_range,
5349251881Speter                                    const merge_source_t *remaining_source,
5350251881Speter                                    apr_pool_t *result_pool)
5351251881Speter{
5352251881Speter  single_range_conflict_report_t *report
5353251881Speter    = apr_palloc(result_pool, sizeof(*report));
5354251881Speter
5355251881Speter  assert(conflicted_range != NULL);
5356251881Speter
5357251881Speter  report->conflicted_range = merge_source_dup(conflicted_range, result_pool);
5358251881Speter  report->remaining_source
5359251881Speter    = remaining_source ? merge_source_dup(remaining_source, result_pool)
5360251881Speter                       : NULL;
5361251881Speter  return report;
5362251881Speter}
5363251881Speter
5364251881Speter/* Data for reporting when a merge aborted because of raising conflicts.
5365251881Speter *
5366251881Speter * ### TODO: More info, including the ranges (or other parameters) the user
5367251881Speter *     needs to complete the merge.
5368251881Speter */
5369251881Spetertypedef struct conflict_report_t
5370251881Speter{
5371251881Speter  const char *target_abspath;
5372251881Speter  /* The revision range during which conflicts were raised */
5373251881Speter  const merge_source_t *conflicted_range;
5374251881Speter  /* Was the conflicted range the last range in the whole requested merge? */
5375251881Speter  svn_boolean_t was_last_range;
5376251881Speter} conflict_report_t;
5377251881Speter
5378251881Speter/* Return a new conflict_report_t containing deep copies of the parameters,
5379251881Speter * allocated in RESULT_POOL. */
5380251881Speterstatic conflict_report_t *
5381251881Speterconflict_report_create(const char *target_abspath,
5382251881Speter                       const merge_source_t *conflicted_range,
5383251881Speter                       svn_boolean_t was_last_range,
5384251881Speter                       apr_pool_t *result_pool)
5385251881Speter{
5386251881Speter  conflict_report_t *report = apr_palloc(result_pool, sizeof(*report));
5387251881Speter
5388251881Speter  report->target_abspath = apr_pstrdup(result_pool, target_abspath);
5389251881Speter  report->conflicted_range = merge_source_dup(conflicted_range, result_pool);
5390251881Speter  report->was_last_range = was_last_range;
5391251881Speter  return report;
5392251881Speter}
5393251881Speter
5394251881Speter/* Return a deep copy of REPORT, allocated in RESULT_POOL. */
5395251881Speterstatic conflict_report_t *
5396251881Speterconflict_report_dup(const conflict_report_t *report,
5397251881Speter                    apr_pool_t *result_pool)
5398251881Speter{
5399251881Speter  conflict_report_t *new = apr_pmemdup(result_pool, report, sizeof(*new));
5400251881Speter
5401251881Speter  new->target_abspath = apr_pstrdup(result_pool, report->target_abspath);
5402251881Speter  new->conflicted_range = merge_source_dup(report->conflicted_range,
5403251881Speter                                           result_pool);
5404251881Speter  return new;
5405251881Speter}
5406251881Speter
5407251881Speter/* Create and return an error structure appropriate for the unmerged
5408251881Speter   revisions range(s). */
5409251881Speterstatic APR_INLINE svn_error_t *
5410251881Spetermake_merge_conflict_error(conflict_report_t *report,
5411251881Speter                          apr_pool_t *scratch_pool)
5412251881Speter{
5413251881Speter  assert(!report || svn_dirent_is_absolute(report->target_abspath));
5414251881Speter
5415251881Speter  if (report && ! report->was_last_range)
5416251881Speter    {
5417251881Speter      svn_error_t *err = svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
5418251881Speter       _("One or more conflicts were produced while merging r%ld:%ld into\n"
5419251881Speter         "'%s' --\n"
5420251881Speter         "resolve all conflicts and rerun the merge to apply the remaining\n"
5421251881Speter         "unmerged revisions"),
5422251881Speter       report->conflicted_range->loc1->rev, report->conflicted_range->loc2->rev,
5423251881Speter       svn_dirent_local_style(report->target_abspath, scratch_pool));
5424251881Speter      assert(report->conflicted_range->loc1->rev != report->conflicted_range->loc2->rev); /* ### is a valid case in a 2-URL merge */
5425251881Speter      return err;
5426251881Speter    }
5427251881Speter  return SVN_NO_ERROR;
5428251881Speter}
5429251881Speter
5430251881Speter/* Helper for do_directory_merge().
5431251881Speter
5432251881Speter   TARGET_WCPATH is a directory and CHILDREN_WITH_MERGEINFO is filled
5433251881Speter   with paths (svn_client__merge_path_t *) arranged in depth first order,
5434251881Speter   which have mergeinfo set on them or meet one of the other criteria
5435251881Speter   defined in get_mergeinfo_paths().  Remove any paths absent from disk
5436251881Speter   or scheduled for deletion from CHILDREN_WITH_MERGEINFO which are equal to
5437251881Speter   or are descendants of TARGET_WCPATH by setting those children to NULL. */
5438251881Speterstatic void
5439251881Speterremove_absent_children(const char *target_wcpath,
5440251881Speter                       apr_array_header_t *children_with_mergeinfo)
5441251881Speter{
5442251881Speter  /* Before we try to override mergeinfo for skipped paths, make sure
5443251881Speter     the path isn't absent due to authz restrictions, because there's
5444251881Speter     nothing we can do about those. */
5445251881Speter  int i;
5446251881Speter  for (i = 0; i < children_with_mergeinfo->nelts; i++)
5447251881Speter    {
5448251881Speter      svn_client__merge_path_t *child =
5449251881Speter        APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
5450251881Speter      if ((child->absent || child->scheduled_for_deletion)
5451251881Speter          && svn_dirent_is_ancestor(target_wcpath, child->abspath))
5452251881Speter        {
5453251881Speter          svn_sort__array_delete(children_with_mergeinfo, i--, 1);
5454251881Speter        }
5455251881Speter    }
5456251881Speter}
5457251881Speter
5458251881Speter/* Helper for do_directory_merge() to handle the case where a merge editor
5459251881Speter   drive removes explicit mergeinfo from a subtree of the merge target.
5460251881Speter
5461251881Speter   MERGE_B is cascaded from the argument of the same name in
5462251881Speter   do_directory_merge().  For each path (if any) in
5463251881Speter   MERGE_B->PATHS_WITH_DELETED_MERGEINFO remove that path from
5464251881Speter   CHILDREN_WITH_MERGEINFO.
5465251881Speter
5466251881Speter   The one exception is for the merge target itself,
5467251881Speter   MERGE_B->target->abspath, this must always be present in
5468251881Speter   CHILDREN_WITH_MERGEINFO so this is never removed by this
5469251881Speter   function. */
5470251881Speterstatic void
5471251881Speterremove_children_with_deleted_mergeinfo(merge_cmd_baton_t *merge_b,
5472251881Speter                                       apr_array_header_t *children_with_mergeinfo)
5473251881Speter{
5474251881Speter  int i;
5475251881Speter
5476251881Speter  if (!merge_b->paths_with_deleted_mergeinfo)
5477251881Speter    return;
5478251881Speter
5479251881Speter  /* CHILDREN_WITH_MERGEINFO[0] is the always the merge target
5480251881Speter     so start at the first child. */
5481251881Speter  for (i = 1; i < children_with_mergeinfo->nelts; i++)
5482251881Speter    {
5483251881Speter      svn_client__merge_path_t *child =
5484251881Speter        APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
5485251881Speter
5486251881Speter      if (svn_hash_gets(merge_b->paths_with_deleted_mergeinfo, child->abspath))
5487251881Speter        {
5488251881Speter          svn_sort__array_delete(children_with_mergeinfo, i--, 1);
5489251881Speter        }
5490251881Speter    }
5491251881Speter}
5492251881Speter
5493251881Speter/* Helper for do_directory_merge().
5494251881Speter
5495251881Speter   Set up the diff editor report to merge the SOURCE diff
5496251881Speter   into TARGET_ABSPATH and drive it.
5497251881Speter
5498251881Speter   If mergeinfo is not being honored (based on MERGE_B -- see the doc
5499251881Speter   string for HONOR_MERGEINFO() for how this is determined), then ignore
5500251881Speter   CHILDREN_WITH_MERGEINFO and merge the SOURCE diff to TARGET_ABSPATH.
5501251881Speter
5502251881Speter   If mergeinfo is being honored then perform a history-aware merge,
5503251881Speter   describing TARGET_ABSPATH and its subtrees to the reporter in such as way
5504251881Speter   as to avoid repeating merges already performed per the mergeinfo and
5505251881Speter   natural history of TARGET_ABSPATH and its subtrees.
5506251881Speter
5507251881Speter   The ranges that still need to be merged to the TARGET_ABSPATH and its
5508251881Speter   subtrees are described in CHILDREN_WITH_MERGEINFO, an array of
5509251881Speter   svn_client__merge_path_t * -- see 'THE CHILDREN_WITH_MERGEINFO ARRAY'
5510251881Speter   comment at the top of this file for more info.  Note that it is possible
5511251881Speter   TARGET_ABSPATH and/or some of its subtrees need only a subset, or no part,
5512251881Speter   of SOURCE to be merged.  Though there is little point to
5513251881Speter   calling this function if TARGET_ABSPATH and all its subtrees have already
5514251881Speter   had SOURCE merged, this will work but is a no-op.
5515251881Speter
5516251881Speter   SOURCE->rev1 and SOURCE->rev2 must be bound by the set of remaining_ranges
5517251881Speter   fields in CHILDREN_WITH_MERGEINFO's elements, specifically:
5518251881Speter
5519251881Speter   For forward merges (SOURCE->rev1 < SOURCE->rev2):
5520251881Speter
5521251881Speter     1) The first svn_merge_range_t * element of each child's remaining_ranges
5522251881Speter        array must meet one of the following conditions:
5523251881Speter
5524251881Speter        a) The range's start field is greater than or equal to SOURCE->rev2.
5525251881Speter
5526251881Speter        b) The range's end field is SOURCE->rev2.
5527251881Speter
5528251881Speter     2) Among all the ranges that meet condition 'b' the oldest start
5529251881Speter        revision must equal SOURCE->rev1.
5530251881Speter
5531251881Speter   For reverse merges (SOURCE->rev1 > SOURCE->rev2):
5532251881Speter
5533251881Speter     1) The first svn_merge_range_t * element of each child's remaining_ranges
5534251881Speter        array must meet one of the following conditions:
5535251881Speter
5536251881Speter        a) The range's start field is less than or equal to SOURCE->rev2.
5537251881Speter
5538251881Speter        b) The range's end field is SOURCE->rev2.
5539251881Speter
5540251881Speter     2) Among all the ranges that meet condition 'b' the youngest start
5541251881Speter        revision must equal SOURCE->rev1.
5542251881Speter
5543251881Speter   Note: If the first svn_merge_range_t * element of some subtree child's
5544251881Speter   remaining_ranges array is the same as the first range of that child's
5545251881Speter   nearest path-wise ancestor, then the subtree child *will not* be described
5546251881Speter   to the reporter.
5547251881Speter
5548251881Speter   DEPTH, NOTIFY_B, and MERGE_B are cascaded from do_directory_merge(), see
5549251881Speter   that function for more info.
5550251881Speter
5551251881Speter   MERGE_B->ra_session1 and MERGE_B->ra_session2 are RA sessions open to any
5552251881Speter   URL in the repository of SOURCE; they may be temporarily reparented within
5553251881Speter   this function.
5554251881Speter
5555251881Speter   If SOURCE->ancestral is set, then SOURCE->loc1 must be a
5556251881Speter   historical ancestor of SOURCE->loc2, or vice-versa (see
5557251881Speter   `MERGEINFO MERGE SOURCE NORMALIZATION' for more requirements around
5558251881Speter   SOURCE in this case).
5559251881Speter*/
5560251881Speterstatic svn_error_t *
5561251881Speterdrive_merge_report_editor(const char *target_abspath,
5562251881Speter                          const merge_source_t *source,
5563251881Speter                          const apr_array_header_t *children_with_mergeinfo,
5564251881Speter                          const svn_diff_tree_processor_t *processor,
5565251881Speter                          svn_depth_t depth,
5566251881Speter                          merge_cmd_baton_t *merge_b,
5567251881Speter                          apr_pool_t *scratch_pool)
5568251881Speter{
5569251881Speter  const svn_ra_reporter3_t *reporter;
5570251881Speter  const svn_delta_editor_t *diff_editor;
5571251881Speter  void *diff_edit_baton;
5572251881Speter  void *report_baton;
5573251881Speter  svn_revnum_t target_start;
5574251881Speter  svn_boolean_t honor_mergeinfo = HONOR_MERGEINFO(merge_b);
5575251881Speter  const char *old_sess1_url, *old_sess2_url;
5576251881Speter  svn_boolean_t is_rollback = source->loc1->rev > source->loc2->rev;
5577251881Speter
5578251881Speter  /* Start with a safe default starting revision for the editor and the
5579251881Speter     merge target. */
5580251881Speter  target_start = source->loc1->rev;
5581251881Speter
5582251881Speter  /* If we are honoring mergeinfo the starting revision for the merge target
5583251881Speter     might not be SOURCE->rev1, in fact the merge target might not need *any*
5584251881Speter     part of SOURCE merged -- Instead some subtree of the target
5585251881Speter     needs SOURCE -- So get the right starting revision for the
5586251881Speter     target. */
5587251881Speter  if (honor_mergeinfo)
5588251881Speter    {
5589251881Speter      svn_client__merge_path_t *child;
5590251881Speter
5591251881Speter      /* CHILDREN_WITH_MERGEINFO must always exist if we are honoring
5592251881Speter         mergeinfo and must have at least one element (describing the
5593251881Speter         merge target). */
5594251881Speter      SVN_ERR_ASSERT(children_with_mergeinfo);
5595251881Speter      SVN_ERR_ASSERT(children_with_mergeinfo->nelts);
5596251881Speter
5597251881Speter      /* Get the merge target's svn_client__merge_path_t, which is always
5598251881Speter         the first in the array due to depth first sorting requirement,
5599251881Speter         see 'THE CHILDREN_WITH_MERGEINFO ARRAY'. */
5600251881Speter      child = APR_ARRAY_IDX(children_with_mergeinfo, 0,
5601251881Speter                            svn_client__merge_path_t *);
5602251881Speter      SVN_ERR_ASSERT(child);
5603251881Speter      if (child->remaining_ranges->nelts == 0)
5604251881Speter        {
5605251881Speter          /* The merge target doesn't need anything merged. */
5606251881Speter          target_start = source->loc2->rev;
5607251881Speter        }
5608251881Speter      else
5609251881Speter        {
5610251881Speter          /* The merge target has remaining revisions to merge.  These
5611251881Speter             ranges may fully or partially overlap the range described
5612251881Speter             by SOURCE->rev1:rev2 or may not intersect that range at
5613251881Speter             all. */
5614251881Speter          svn_merge_range_t *range =
5615251881Speter            APR_ARRAY_IDX(child->remaining_ranges, 0,
5616251881Speter                          svn_merge_range_t *);
5617251881Speter          if ((!is_rollback && range->start > source->loc2->rev)
5618251881Speter              || (is_rollback && range->start < source->loc2->rev))
5619251881Speter            {
5620251881Speter              /* Merge target's first remaining range doesn't intersect. */
5621251881Speter              target_start = source->loc2->rev;
5622251881Speter            }
5623251881Speter          else
5624251881Speter            {
5625251881Speter              /* Merge target's first remaining range partially or
5626251881Speter                 fully overlaps. */
5627251881Speter              target_start = range->start;
5628251881Speter            }
5629251881Speter        }
5630251881Speter    }
5631251881Speter
5632251881Speter  SVN_ERR(svn_client__ensure_ra_session_url(&old_sess1_url,
5633251881Speter                                            merge_b->ra_session1,
5634251881Speter                                            source->loc1->url, scratch_pool));
5635251881Speter  /* Temporarily point our second RA session to SOURCE->loc1->url, too.  We use
5636251881Speter     this to request individual file contents. */
5637251881Speter  SVN_ERR(svn_client__ensure_ra_session_url(&old_sess2_url,
5638251881Speter                                            merge_b->ra_session2,
5639251881Speter                                            source->loc1->url, scratch_pool));
5640251881Speter
5641251881Speter  /* Get the diff editor and a reporter with which to, ultimately,
5642251881Speter     drive it. */
5643251881Speter  SVN_ERR(svn_client__get_diff_editor2(&diff_editor, &diff_edit_baton,
5644251881Speter                                       merge_b->ra_session2,
5645251881Speter                                       depth,
5646251881Speter                                       source->loc1->rev,
5647251881Speter                                       TRUE /* text_deltas */,
5648251881Speter                                       processor,
5649251881Speter                                       merge_b->ctx->cancel_func,
5650251881Speter                                       merge_b->ctx->cancel_baton,
5651251881Speter                                       scratch_pool));
5652251881Speter  SVN_ERR(svn_ra_do_diff3(merge_b->ra_session1,
5653251881Speter                          &reporter, &report_baton, source->loc2->rev,
5654251881Speter                          "", depth, merge_b->diff_ignore_ancestry,
5655251881Speter                          TRUE,  /* text_deltas */
5656251881Speter                          source->loc2->url, diff_editor, diff_edit_baton,
5657251881Speter                          scratch_pool));
5658251881Speter
5659251881Speter  /* Drive the reporter. */
5660251881Speter  SVN_ERR(reporter->set_path(report_baton, "", target_start, depth,
5661251881Speter                             FALSE, NULL, scratch_pool));
5662251881Speter  if (honor_mergeinfo && children_with_mergeinfo)
5663251881Speter    {
5664251881Speter      /* Describe children with mergeinfo overlapping this merge
5665251881Speter         operation such that no repeated diff is retrieved for them from
5666251881Speter         the repository. */
5667251881Speter      int i;
5668251881Speter      apr_pool_t *iterpool = svn_pool_create(scratch_pool);
5669251881Speter
5670251881Speter      /* Start with CHILDREN_WITH_MERGEINFO[1], CHILDREN_WITH_MERGEINFO[0]
5671251881Speter         is always the merge target (TARGET_ABSPATH). */
5672251881Speter      for (i = 1; i < children_with_mergeinfo->nelts; i++)
5673251881Speter        {
5674251881Speter          svn_merge_range_t *range;
5675251881Speter          const char *child_repos_path;
5676251881Speter          const svn_client__merge_path_t *parent;
5677251881Speter          const svn_client__merge_path_t *child =
5678251881Speter            APR_ARRAY_IDX(children_with_mergeinfo, i,
5679251881Speter                          svn_client__merge_path_t *);
5680251881Speter
5681251881Speter          SVN_ERR_ASSERT(child);
5682251881Speter          if (child->absent)
5683251881Speter            continue;
5684251881Speter
5685251881Speter          svn_pool_clear(iterpool);
5686251881Speter
5687251881Speter          /* Find this child's nearest wc ancestor with mergeinfo. */
5688251881Speter          parent = find_nearest_ancestor(children_with_mergeinfo,
5689251881Speter                                         FALSE, child->abspath);
5690251881Speter
5691251881Speter          /* If a subtree needs the same range applied as its nearest parent
5692251881Speter             with mergeinfo or neither the subtree nor this parent need
5693251881Speter             SOURCE->rev1:rev2 merged, then we don't need to describe the
5694251881Speter             subtree separately.  In the latter case this could break the
5695251881Speter             editor if child->abspath didn't exist at SOURCE->rev2 and we
5696251881Speter             attempt to describe it via a reporter set_path call. */
5697251881Speter          if (child->remaining_ranges->nelts)
5698251881Speter            {
5699251881Speter              range = APR_ARRAY_IDX(child->remaining_ranges, 0,
5700251881Speter                                    svn_merge_range_t *);
5701251881Speter              if ((!is_rollback && range->start > source->loc2->rev)
5702251881Speter                  || (is_rollback && range->start < source->loc2->rev))
5703251881Speter                {
5704251881Speter                  /* This child's first remaining range comes after the range
5705251881Speter                     we are currently merging, so skip it. We expect to get
5706251881Speter                     to it in a subsequent call to this function. */
5707251881Speter                  continue;
5708251881Speter                }
5709251881Speter              else if (parent->remaining_ranges->nelts)
5710251881Speter                {
5711251881Speter                   svn_merge_range_t *parent_range =
5712251881Speter                    APR_ARRAY_IDX(parent->remaining_ranges, 0,
5713251881Speter                                  svn_merge_range_t *);
5714251881Speter                   svn_merge_range_t *child_range =
5715251881Speter                    APR_ARRAY_IDX(child->remaining_ranges, 0,
5716251881Speter                                  svn_merge_range_t *);
5717251881Speter                  if (parent_range->start == child_range->start)
5718251881Speter                    continue; /* Subtree needs same range as parent. */
5719251881Speter                }
5720251881Speter            }
5721251881Speter          else /* child->remaining_ranges->nelts == 0*/
5722251881Speter            {
5723251881Speter              /* If both the subtree and its parent need no ranges applied
5724251881Speter                 consider that as the "same ranges" and don't describe
5725251881Speter                 the subtree. */
5726251881Speter              if (parent->remaining_ranges->nelts == 0)
5727251881Speter                continue;
5728251881Speter            }
5729251881Speter
5730251881Speter          /* Ok, we really need to describe this subtree as it needs different
5731251881Speter             ranges applied than its nearest working copy parent. */
5732251881Speter          child_repos_path = svn_dirent_is_child(target_abspath,
5733251881Speter                                                 child->abspath,
5734251881Speter                                                 iterpool);
5735251881Speter          /* This loop is only processing subtrees, so CHILD->ABSPATH
5736251881Speter             better be a proper child of the merge target. */
5737251881Speter          SVN_ERR_ASSERT(child_repos_path);
5738251881Speter
5739251881Speter          if ((child->remaining_ranges->nelts == 0)
5740251881Speter              || (is_rollback && (range->start < source->loc2->rev))
5741251881Speter              || (!is_rollback && (range->start > source->loc2->rev)))
5742251881Speter            {
5743251881Speter              /* Nothing to merge to this child.  We'll claim we have
5744251881Speter                 it up to date so the server doesn't send us
5745251881Speter                 anything. */
5746251881Speter              SVN_ERR(reporter->set_path(report_baton, child_repos_path,
5747251881Speter                                         source->loc2->rev, depth, FALSE,
5748251881Speter                                         NULL, iterpool));
5749251881Speter            }
5750251881Speter          else
5751251881Speter            {
5752251881Speter              SVN_ERR(reporter->set_path(report_baton, child_repos_path,
5753251881Speter                                         range->start, depth, FALSE,
5754251881Speter                                         NULL, iterpool));
5755251881Speter            }
5756251881Speter        }
5757251881Speter      svn_pool_destroy(iterpool);
5758251881Speter    }
5759251881Speter  SVN_ERR(reporter->finish_report(report_baton, scratch_pool));
5760251881Speter
5761251881Speter  /* Point the merge baton's RA sessions back where they were. */
5762251881Speter  SVN_ERR(svn_ra_reparent(merge_b->ra_session1, old_sess1_url, scratch_pool));
5763251881Speter  SVN_ERR(svn_ra_reparent(merge_b->ra_session2, old_sess2_url, scratch_pool));
5764251881Speter
5765251881Speter  return SVN_NO_ERROR;
5766251881Speter}
5767251881Speter
5768251881Speter/* Iterate over each svn_client__merge_path_t * element in
5769251881Speter   CHILDREN_WITH_MERGEINFO and, if START_REV is true, find the most inclusive
5770251881Speter   start revision among those element's first remaining_ranges element.  If
5771251881Speter   START_REV is false, then look for the most inclusive end revision.
5772251881Speter
5773251881Speter   If IS_ROLLBACK is true the youngest start or end (as per START_REV)
5774251881Speter   revision is considered the "most inclusive" otherwise the oldest revision
5775251881Speter   is.
5776251881Speter
5777251881Speter   If none of CHILDREN_WITH_MERGEINFO's elements have any remaining ranges
5778251881Speter   return SVN_INVALID_REVNUM. */
5779251881Speterstatic svn_revnum_t
5780251881Speterget_most_inclusive_rev(const apr_array_header_t *children_with_mergeinfo,
5781251881Speter                       svn_boolean_t is_rollback,
5782251881Speter                       svn_boolean_t start_rev)
5783251881Speter{
5784251881Speter  int i;
5785251881Speter  svn_revnum_t most_inclusive_rev = SVN_INVALID_REVNUM;
5786251881Speter
5787251881Speter  for (i = 0; i < children_with_mergeinfo->nelts; i++)
5788251881Speter    {
5789251881Speter      svn_client__merge_path_t *child =
5790251881Speter        APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
5791251881Speter
5792251881Speter      if ((! child) || child->absent)
5793251881Speter        continue;
5794251881Speter      if (child->remaining_ranges->nelts > 0)
5795251881Speter        {
5796251881Speter          svn_merge_range_t *range =
5797251881Speter            APR_ARRAY_IDX(child->remaining_ranges, 0, svn_merge_range_t *);
5798251881Speter
5799251881Speter          /* Are we looking for the most inclusive start or end rev? */
5800251881Speter          svn_revnum_t rev = start_rev ? range->start : range->end;
5801251881Speter
5802251881Speter          if ((most_inclusive_rev == SVN_INVALID_REVNUM)
5803251881Speter              || (is_rollback && (rev > most_inclusive_rev))
5804251881Speter              || ((! is_rollback) && (rev < most_inclusive_rev)))
5805251881Speter            most_inclusive_rev = rev;
5806251881Speter        }
5807251881Speter    }
5808251881Speter  return most_inclusive_rev;
5809251881Speter}
5810251881Speter
5811251881Speter
5812251881Speter/* If first item in each child of CHILDREN_WITH_MERGEINFO's
5813251881Speter   remaining_ranges is inclusive of END_REV, Slice the first range in
5814251881Speter   to two at END_REV. All the allocations are persistent and allocated
5815251881Speter   from POOL. */
5816251881Speterstatic void
5817251881Speterslice_remaining_ranges(apr_array_header_t *children_with_mergeinfo,
5818251881Speter                       svn_boolean_t is_rollback, svn_revnum_t end_rev,
5819251881Speter                       apr_pool_t *pool)
5820251881Speter{
5821251881Speter  int i;
5822251881Speter  for (i = 0; i < children_with_mergeinfo->nelts; i++)
5823251881Speter    {
5824251881Speter      svn_client__merge_path_t *child =
5825251881Speter                                     APR_ARRAY_IDX(children_with_mergeinfo, i,
5826251881Speter                                                   svn_client__merge_path_t *);
5827251881Speter      if (!child || child->absent)
5828251881Speter        continue;
5829251881Speter      if (child->remaining_ranges->nelts > 0)
5830251881Speter        {
5831251881Speter          svn_merge_range_t *range = APR_ARRAY_IDX(child->remaining_ranges, 0,
5832251881Speter                                                   svn_merge_range_t *);
5833251881Speter          if ((is_rollback && (range->start > end_rev)
5834251881Speter               && (range->end < end_rev))
5835251881Speter              || (!is_rollback && (range->start < end_rev)
5836251881Speter                  && (range->end > end_rev)))
5837251881Speter            {
5838251881Speter              svn_merge_range_t *split_range1, *split_range2;
5839251881Speter
5840251881Speter              split_range1 = svn_merge_range_dup(range, pool);
5841251881Speter              split_range2 = svn_merge_range_dup(range, pool);
5842251881Speter              split_range1->end = end_rev;
5843251881Speter              split_range2->start = end_rev;
5844251881Speter              APR_ARRAY_IDX(child->remaining_ranges, 0,
5845251881Speter                            svn_merge_range_t *) = split_range1;
5846251881Speter              svn_sort__array_insert(&split_range2, child->remaining_ranges, 1);
5847251881Speter            }
5848251881Speter        }
5849251881Speter    }
5850251881Speter}
5851251881Speter
5852251881Speter/* Helper for do_directory_merge().
5853251881Speter
5854251881Speter   For each child in CHILDREN_WITH_MERGEINFO remove the first remaining_ranges
5855251881Speter   svn_merge_range_t *element of the child if that range has an end revision
5856251881Speter   equal to REVISION.
5857251881Speter
5858251881Speter   If a range is removed from a child's remaining_ranges array, allocate the
5859251881Speter   new remaining_ranges array in POOL.
5860251881Speter */
5861251881Speterstatic void
5862251881Speterremove_first_range_from_remaining_ranges(svn_revnum_t revision,
5863251881Speter                                         apr_array_header_t
5864251881Speter                                           *children_with_mergeinfo,
5865251881Speter                                         apr_pool_t *pool)
5866251881Speter{
5867251881Speter  int i;
5868251881Speter
5869251881Speter  for (i = 0; i < children_with_mergeinfo->nelts; i++)
5870251881Speter    {
5871251881Speter      svn_client__merge_path_t *child =
5872251881Speter                                APR_ARRAY_IDX(children_with_mergeinfo, i,
5873251881Speter                                              svn_client__merge_path_t *);
5874251881Speter      if (!child || child->absent)
5875251881Speter        continue;
5876251881Speter      if (child->remaining_ranges->nelts > 0)
5877251881Speter        {
5878251881Speter          svn_merge_range_t *first_range =
5879251881Speter            APR_ARRAY_IDX(child->remaining_ranges, 0, svn_merge_range_t *);
5880251881Speter          if (first_range->end == revision)
5881251881Speter            {
5882251881Speter              svn_sort__array_delete(child->remaining_ranges, 0, 1);
5883251881Speter            }
5884251881Speter        }
5885251881Speter    }
5886251881Speter}
5887251881Speter
5888251881Speter/* Get a file's content and properties from the repository.
5889251881Speter   Set *FILENAME to the local path to a new temporary file holding its text,
5890251881Speter   and set *PROPS to a new hash of its properties.
5891251881Speter
5892251881Speter   RA_SESSION is a session open to the correct repository, which will be
5893251881Speter   temporarily reparented to the URL of the file itself.  LOCATION is the
5894251881Speter   repository location of the file.
5895251881Speter
5896251881Speter   The resulting file and the return values live as long as RESULT_POOL, all
5897251881Speter   other allocations occur in SCRATCH_POOL.
5898251881Speter*/
5899251881Speterstatic svn_error_t *
5900251881Spetersingle_file_merge_get_file(const char **filename,
5901251881Speter                           apr_hash_t **props,
5902251881Speter                           svn_ra_session_t *ra_session,
5903251881Speter                           const svn_client__pathrev_t *location,
5904251881Speter                           const char *wc_target,
5905251881Speter                           apr_pool_t *result_pool,
5906251881Speter                           apr_pool_t *scratch_pool)
5907251881Speter{
5908251881Speter  svn_stream_t *stream;
5909251881Speter  const char *old_sess_url;
5910251881Speter  svn_error_t *err;
5911251881Speter
5912251881Speter  SVN_ERR(svn_stream_open_unique(&stream, filename, NULL,
5913251881Speter                                 svn_io_file_del_on_pool_cleanup,
5914251881Speter                                 result_pool, scratch_pool));
5915251881Speter
5916251881Speter  SVN_ERR(svn_client__ensure_ra_session_url(&old_sess_url, ra_session, location->url,
5917251881Speter                                            scratch_pool));
5918251881Speter  err = svn_ra_get_file(ra_session, "", location->rev,
5919251881Speter                        stream, NULL, props, scratch_pool);
5920251881Speter  SVN_ERR(svn_error_compose_create(
5921251881Speter            err, svn_ra_reparent(ra_session, old_sess_url, scratch_pool)));
5922251881Speter
5923251881Speter  return svn_error_trace(svn_stream_close(stream));
5924251881Speter}
5925251881Speter
5926251881Speter/* Compare two svn_client__merge_path_t elements **A and **B, given the
5927251881Speter   addresses of pointers to them. Return an integer less than, equal to, or
5928251881Speter   greater than zero if A sorts before, the same as, or after B, respectively.
5929251881Speter   This is a helper for qsort() and bsearch() on an array of such elements. */
5930251881Speterstatic int
5931251881Spetercompare_merge_path_t_as_paths(const void *a,
5932251881Speter                              const void *b)
5933251881Speter{
5934251881Speter  const svn_client__merge_path_t *child1
5935251881Speter    = *((const svn_client__merge_path_t * const *) a);
5936251881Speter  const svn_client__merge_path_t *child2
5937251881Speter    = *((const svn_client__merge_path_t * const *) b);
5938251881Speter
5939251881Speter  return svn_path_compare_paths(child1->abspath, child2->abspath);
5940251881Speter}
5941251881Speter
5942251881Speter/* Return a pointer to the element of CHILDREN_WITH_MERGEINFO whose path
5943251881Speter * is PATH, or return NULL if there is no such element. */
5944251881Speterstatic svn_client__merge_path_t *
5945251881Speterget_child_with_mergeinfo(const apr_array_header_t *children_with_mergeinfo,
5946251881Speter                         const char *abspath)
5947251881Speter{
5948251881Speter  svn_client__merge_path_t merge_path;
5949251881Speter  svn_client__merge_path_t *key;
5950251881Speter  svn_client__merge_path_t **pchild;
5951251881Speter
5952251881Speter  merge_path.abspath = abspath;
5953251881Speter  key = &merge_path;
5954251881Speter  pchild = bsearch(&key, children_with_mergeinfo->elts,
5955251881Speter                   children_with_mergeinfo->nelts,
5956251881Speter                   children_with_mergeinfo->elt_size,
5957251881Speter                   compare_merge_path_t_as_paths);
5958251881Speter  return pchild ? *pchild : NULL;
5959251881Speter}
5960251881Speter
5961251881Speter/* Insert a deep copy of INSERT_ELEMENT into the CHILDREN_WITH_MERGEINFO
5962251881Speter   array at its correct position.  Allocate the new storage in POOL.
5963251881Speter   CHILDREN_WITH_MERGEINFO is a depth first sorted array of
5964251881Speter   (svn_client__merge_path_t *).
5965251881Speter
5966251881Speter   ### Most callers don't need this to deep-copy the new element.
5967251881Speter   ### It may be more efficient for some callers to insert a bunch of items
5968251881Speter       out of order and then sort afterwards. (One caller is doing a qsort
5969251881Speter       after calling this anyway.)
5970251881Speter */
5971251881Speterstatic void
5972251881Speterinsert_child_to_merge(apr_array_header_t *children_with_mergeinfo,
5973251881Speter                      const svn_client__merge_path_t *insert_element,
5974251881Speter                      apr_pool_t *pool)
5975251881Speter{
5976251881Speter  int insert_index;
5977251881Speter  const svn_client__merge_path_t *new_element;
5978251881Speter
5979251881Speter  /* Find where to insert the new element */
5980251881Speter  insert_index =
5981251881Speter    svn_sort__bsearch_lower_bound(&insert_element, children_with_mergeinfo,
5982251881Speter                                  compare_merge_path_t_as_paths);
5983251881Speter
5984251881Speter  new_element = svn_client__merge_path_dup(insert_element, pool);
5985251881Speter  svn_sort__array_insert(&new_element, children_with_mergeinfo, insert_index);
5986251881Speter}
5987251881Speter
5988251881Speter/* Helper for get_mergeinfo_paths().
5989251881Speter
5990251881Speter   CHILDREN_WITH_MERGEINFO, DEPTH, and POOL are
5991251881Speter   all cascaded from the arguments of the same name to get_mergeinfo_paths().
5992251881Speter
5993251881Speter   TARGET is the merge target.
5994251881Speter
5995251881Speter   *CHILD is the element in in CHILDREN_WITH_MERGEINFO that
5996251881Speter   get_mergeinfo_paths() is iterating over and *CURR_INDEX is index for
5997251881Speter   *CHILD.
5998251881Speter
5999251881Speter   If CHILD->ABSPATH is equal to MERGE_CMD_BATON->target->abspath do nothing.
6000251881Speter   Else if CHILD->ABSPATH is switched or absent then make sure its immediate
6001251881Speter   (as opposed to nearest) parent in CHILDREN_WITH_MERGEINFO is marked as
6002251881Speter   missing a child.  If the immediate parent does not exist in
6003251881Speter   CHILDREN_WITH_MERGEINFO then create it (and increment *CURR_INDEX so that
6004251881Speter   caller doesn't process the inserted element).  Also ensure that
6005251881Speter   CHILD->ABSPATH's siblings which are not already present in
6006251881Speter   CHILDREN_WITH_MERGEINFO are also added to the array, limited by DEPTH
6007251881Speter   (e.g. don't add directory siblings of a switched file).
6008251881Speter   Use POOL for temporary allocations only, any new CHILDREN_WITH_MERGEINFO
6009251881Speter   elements are allocated in POOL. */
6010251881Speterstatic svn_error_t *
6011251881Speterinsert_parent_and_sibs_of_sw_absent_del_subtree(
6012251881Speter                                   apr_array_header_t *children_with_mergeinfo,
6013251881Speter                                   const merge_target_t *target,
6014251881Speter                                   int *curr_index,
6015251881Speter                                   svn_client__merge_path_t *child,
6016251881Speter                                   svn_depth_t depth,
6017251881Speter                                   svn_client_ctx_t *ctx,
6018251881Speter                                   apr_pool_t *pool)
6019251881Speter{
6020251881Speter  svn_client__merge_path_t *parent;
6021251881Speter  const char *parent_abspath;
6022251881Speter  apr_pool_t *iterpool;
6023251881Speter  const apr_array_header_t *children;
6024251881Speter  int i;
6025251881Speter
6026251881Speter  if (!(child->absent
6027251881Speter          || (child->switched
6028251881Speter              && strcmp(target->abspath,
6029251881Speter                        child->abspath) != 0)))
6030251881Speter    return SVN_NO_ERROR;
6031251881Speter
6032251881Speter  parent_abspath = svn_dirent_dirname(child->abspath, pool);
6033251881Speter  parent = get_child_with_mergeinfo(children_with_mergeinfo, parent_abspath);
6034251881Speter  if (parent)
6035251881Speter    {
6036251881Speter      parent->missing_child = child->absent;
6037251881Speter      parent->switched_child = child->switched;
6038251881Speter    }
6039251881Speter  else
6040251881Speter    {
6041251881Speter      /* Create a new element to insert into CHILDREN_WITH_MERGEINFO. */
6042251881Speter      parent = svn_client__merge_path_create(parent_abspath, pool);
6043251881Speter      parent->missing_child = child->absent;
6044251881Speter      parent->switched_child = child->switched;
6045251881Speter      /* Insert PARENT into CHILDREN_WITH_MERGEINFO. */
6046251881Speter      insert_child_to_merge(children_with_mergeinfo, parent, pool);
6047251881Speter      /* Increment for loop index so we don't process the inserted element. */
6048251881Speter      (*curr_index)++;
6049251881Speter    } /*(parent == NULL) */
6050251881Speter
6051251881Speter  /* Add all of PARENT's non-missing children that are not already present.*/
6052251881Speter  SVN_ERR(svn_wc__node_get_children(&children, ctx->wc_ctx,
6053251881Speter                                    parent_abspath, FALSE, pool, pool));
6054251881Speter  iterpool = svn_pool_create(pool);
6055251881Speter  for (i = 0; i < children->nelts; i++)
6056251881Speter    {
6057251881Speter      const char *child_abspath = APR_ARRAY_IDX(children, i, const char *);
6058251881Speter      svn_client__merge_path_t *sibling_of_missing;
6059251881Speter
6060251881Speter      svn_pool_clear(iterpool);
6061251881Speter
6062251881Speter      /* Does this child already exist in CHILDREN_WITH_MERGEINFO? */
6063251881Speter      sibling_of_missing = get_child_with_mergeinfo(children_with_mergeinfo,
6064251881Speter                                                    child_abspath);
6065251881Speter      /* Create the missing child and insert it into CHILDREN_WITH_MERGEINFO.*/
6066251881Speter      if (!sibling_of_missing)
6067251881Speter        {
6068251881Speter          /* Don't add directory children if DEPTH is svn_depth_files. */
6069251881Speter          if (depth == svn_depth_files)
6070251881Speter            {
6071251881Speter              svn_node_kind_t child_kind;
6072251881Speter
6073251881Speter              SVN_ERR(svn_wc_read_kind2(&child_kind,
6074251881Speter                                        ctx->wc_ctx, child_abspath,
6075251881Speter                                        FALSE, FALSE, iterpool));
6076251881Speter              if (child_kind != svn_node_file)
6077251881Speter                continue;
6078251881Speter            }
6079251881Speter
6080251881Speter          sibling_of_missing = svn_client__merge_path_create(child_abspath,
6081251881Speter                                                             pool);
6082251881Speter          insert_child_to_merge(children_with_mergeinfo, sibling_of_missing,
6083251881Speter                                pool);
6084251881Speter        }
6085251881Speter    }
6086251881Speter
6087251881Speter  svn_pool_destroy(iterpool);
6088251881Speter
6089251881Speter  return SVN_NO_ERROR;
6090251881Speter}
6091251881Speter
6092251881Speter/* pre_merge_status_cb's baton */
6093251881Speterstruct pre_merge_status_baton_t
6094251881Speter{
6095251881Speter  svn_wc_context_t *wc_ctx;
6096251881Speter
6097251881Speter  /* const char *absolute_wc_path to svn_depth_t * mapping for depths
6098251881Speter     of empty, immediates, and files. */
6099251881Speter  apr_hash_t *shallow_subtrees;
6100251881Speter
6101251881Speter  /* const char *absolute_wc_path to the same, for all paths missing
6102251881Speter     from the working copy. */
6103251881Speter  apr_hash_t *missing_subtrees;
6104251881Speter
6105251881Speter  /* const char *absolute_wc_path const char * repos relative path, describing
6106251881Speter     the root of each switched subtree in the working copy and the repository
6107251881Speter     relative path it is switched to. */
6108251881Speter  apr_hash_t *switched_subtrees;
6109251881Speter
6110251881Speter  /* A pool to allocate additions to the above hashes in. */
6111251881Speter  apr_pool_t *pool;
6112251881Speter};
6113251881Speter
6114251881Speter/* A svn_wc_status_func4_t callback used by get_mergeinfo_paths to gather
6115251881Speter   all switched, depth filtered and missing subtrees under a merge target.
6116251881Speter
6117251881Speter   Note that this doesn't see server and user excluded trees. */
6118251881Speterstatic svn_error_t *
6119251881Speterpre_merge_status_cb(void *baton,
6120251881Speter                    const char *local_abspath,
6121251881Speter                    const svn_wc_status3_t *status,
6122251881Speter                    apr_pool_t *scratch_pool)
6123251881Speter{
6124251881Speter  struct pre_merge_status_baton_t *pmsb = baton;
6125251881Speter
6126251881Speter  if (status->switched && !status->file_external)
6127251881Speter    {
6128251881Speter      store_path(pmsb->switched_subtrees, local_abspath);
6129251881Speter    }
6130251881Speter
6131251881Speter  if (status->depth == svn_depth_empty
6132251881Speter      || status->depth == svn_depth_files)
6133251881Speter    {
6134251881Speter      const char *dup_abspath;
6135251881Speter      svn_depth_t *depth = apr_pmemdup(pmsb->pool, &status->depth,
6136251881Speter                                       sizeof *depth);
6137251881Speter
6138251881Speter      dup_abspath = apr_pstrdup(pmsb->pool, local_abspath);
6139251881Speter
6140251881Speter      svn_hash_sets(pmsb->shallow_subtrees, dup_abspath, depth);
6141251881Speter    }
6142251881Speter
6143251881Speter  if (status->node_status == svn_wc_status_missing)
6144251881Speter    {
6145251881Speter      svn_boolean_t new_missing_root = TRUE;
6146251881Speter      apr_hash_index_t *hi;
6147251881Speter
6148251881Speter      for (hi = apr_hash_first(scratch_pool, pmsb->missing_subtrees);
6149251881Speter           hi;
6150251881Speter           hi = apr_hash_next(hi))
6151251881Speter        {
6152251881Speter          const char *missing_root_path = svn__apr_hash_index_key(hi);
6153251881Speter
6154251881Speter          if (svn_dirent_is_ancestor(missing_root_path,
6155251881Speter                                     local_abspath))
6156251881Speter            {
6157251881Speter              new_missing_root = FALSE;
6158251881Speter              break;
6159251881Speter            }
6160251881Speter        }
6161251881Speter
6162251881Speter      if (new_missing_root)
6163251881Speter        store_path(pmsb->missing_subtrees, local_abspath);
6164251881Speter    }
6165251881Speter
6166251881Speter  return SVN_NO_ERROR;
6167251881Speter}
6168251881Speter
6169251881Speter/* Find all the subtrees in the working copy tree rooted at TARGET_ABSPATH
6170251881Speter * that have explicit mergeinfo.
6171251881Speter * Set *SUBTREES_WITH_MERGEINFO to a hash mapping (const char *) absolute
6172251881Speter * WC path to (svn_mergeinfo_t *) mergeinfo.
6173251881Speter *
6174251881Speter * ### Is this function equivalent to:
6175251881Speter *
6176251881Speter *   svn_client__get_wc_mergeinfo_catalog(
6177251881Speter *     subtrees_with_mergeinfo, inherited=NULL, include_descendants=TRUE,
6178251881Speter *     svn_mergeinfo_explicit, target_abspath, limit_path=NULL,
6179251881Speter *     walked_path=NULL, ignore_invalid_mergeinfo=FALSE, ...)
6180251881Speter *
6181251881Speter *   except for the catalog keys being abspaths instead of repo-relpaths?
6182251881Speter */
6183251881Speterstatic svn_error_t *
6184251881Speterget_wc_explicit_mergeinfo_catalog(apr_hash_t **subtrees_with_mergeinfo,
6185251881Speter                                  const char *target_abspath,
6186251881Speter                                  svn_depth_t depth,
6187251881Speter                                  svn_client_ctx_t *ctx,
6188251881Speter                                  apr_pool_t *result_pool,
6189251881Speter                                  apr_pool_t *scratch_pool)
6190251881Speter{
6191251881Speter  svn_opt_revision_t working_revision = { svn_opt_revision_working, { 0 } };
6192251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
6193251881Speter  apr_hash_index_t *hi;
6194251881Speter  apr_hash_t *externals;
6195251881Speter
6196251881Speter  SVN_ERR(svn_client_propget5(subtrees_with_mergeinfo, NULL,
6197251881Speter                              SVN_PROP_MERGEINFO, target_abspath,
6198251881Speter                              &working_revision, &working_revision, NULL,
6199251881Speter                              depth, NULL, ctx, result_pool, scratch_pool));
6200251881Speter
6201251881Speter  SVN_ERR(svn_wc__externals_defined_below(&externals, ctx->wc_ctx,
6202251881Speter                                          target_abspath, scratch_pool,
6203251881Speter                                          scratch_pool));
6204251881Speter
6205251881Speter  /* Convert property values to svn_mergeinfo_t. */
6206251881Speter  for (hi = apr_hash_first(scratch_pool, *subtrees_with_mergeinfo);
6207251881Speter       hi;
6208251881Speter       hi = apr_hash_next(hi))
6209251881Speter    {
6210251881Speter      const char *wc_path = svn__apr_hash_index_key(hi);
6211251881Speter      svn_string_t *mergeinfo_string = svn__apr_hash_index_val(hi);
6212251881Speter      svn_mergeinfo_t mergeinfo;
6213251881Speter      svn_error_t *err;
6214251881Speter
6215251881Speter      /* svn_client_propget5 picks up file externals with
6216251881Speter         mergeinfo, but we don't want those. */
6217251881Speter      if (svn_hash_gets(externals, wc_path))
6218251881Speter        {
6219251881Speter          svn_hash_sets(*subtrees_with_mergeinfo, wc_path, NULL);
6220251881Speter          continue;
6221251881Speter        }
6222251881Speter
6223251881Speter      svn_pool_clear(iterpool);
6224251881Speter
6225251881Speter      err = svn_mergeinfo_parse(&mergeinfo, mergeinfo_string->data,
6226251881Speter                                result_pool);
6227251881Speter      if (err)
6228251881Speter        {
6229251881Speter          if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
6230251881Speter            {
6231251881Speter              err = svn_error_createf(
6232251881Speter                SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING, err,
6233251881Speter                _("Invalid mergeinfo detected on '%s', "
6234251881Speter                  "merge tracking not possible"),
6235251881Speter                svn_dirent_local_style(wc_path, scratch_pool));
6236251881Speter            }
6237251881Speter          return svn_error_trace(err);
6238251881Speter        }
6239251881Speter      svn_hash_sets(*subtrees_with_mergeinfo, wc_path, mergeinfo);
6240251881Speter    }
6241251881Speter  svn_pool_destroy(iterpool);
6242251881Speter
6243251881Speter  return SVN_NO_ERROR;
6244251881Speter}
6245251881Speter
6246251881Speter/* Helper for do_directory_merge() when performing merge-tracking aware
6247251881Speter   merges.
6248251881Speter
6249251881Speter   Walk of the working copy tree rooted at TARGET->abspath to
6250251881Speter   depth DEPTH.  Create an svn_client__merge_path_t * for any path which meets
6251251881Speter   one or more of the following criteria:
6252251881Speter
6253251881Speter     1) Path has working svn:mergeinfo.
6254251881Speter     2) Path is switched.
6255251881Speter     3) Path is a subtree of the merge target (i.e. is not equal to
6256251881Speter        TARGET->abspath) and has no mergeinfo of its own but
6257251881Speter        its immediate parent has mergeinfo with non-inheritable ranges.  If
6258251881Speter        this isn't a dry-run and the merge is between differences in the same
6259251881Speter        repository, then this function will set working mergeinfo on the path
6260251881Speter        equal to the mergeinfo inheritable from its parent.
6261251881Speter     4) Path has an immediate child (or children) missing from the WC because
6262251881Speter        the child is switched or absent from the WC, or due to a sparse
6263251881Speter        checkout.
6264251881Speter     5) Path has a sibling (or siblings) missing from the WC because the
6265251881Speter        sibling is switched, absent, scheduled for deletion, or missing due to
6266251881Speter        a sparse checkout.
6267251881Speter     6) Path is absent from disk due to an authz restriction.
6268251881Speter     7) Path is equal to TARGET->abspath.
6269251881Speter     8) Path is an immediate *directory* child of
6270251881Speter        TARGET->abspath and DEPTH is svn_depth_immediates.
6271251881Speter     9) Path is an immediate *file* child of TARGET->abspath
6272251881Speter        and DEPTH is svn_depth_files.
6273251881Speter     10) Path is at a depth of 'empty' or 'files'.
6274251881Speter     11) Path is missing from disk (e.g. due to an OS-level deletion).
6275251881Speter
6276251881Speter   If subtrees within the requested DEPTH are unexpectedly missing disk,
6277251881Speter   then raise SVN_ERR_CLIENT_NOT_READY_TO_MERGE.
6278251881Speter
6279251881Speter   Store the svn_client__merge_path_t *'s in *CHILDREN_WITH_MERGEINFO in
6280251881Speter   depth-first order based on the svn_client__merge_path_t *s path member as
6281251881Speter   sorted by svn_path_compare_paths().  Set the remaining_ranges field of each
6282251881Speter   element to NULL.
6283251881Speter
6284251881Speter   Note: Since the walk is rooted at TARGET->abspath, the
6285251881Speter   latter is guaranteed to be in *CHILDREN_WITH_MERGEINFO and due to the
6286251881Speter   depth-first ordering it is guaranteed to be the first element in
6287251881Speter   *CHILDREN_WITH_MERGEINFO.
6288251881Speter
6289251881Speter   MERGE_CMD_BATON is cascaded from the argument of the same name in
6290251881Speter   do_directory_merge().
6291251881Speter*/
6292251881Speterstatic svn_error_t *
6293251881Speterget_mergeinfo_paths(apr_array_header_t *children_with_mergeinfo,
6294251881Speter                    const merge_target_t *target,
6295251881Speter                    svn_depth_t depth,
6296251881Speter                    svn_boolean_t dry_run,
6297251881Speter                    svn_boolean_t same_repos,
6298251881Speter                    svn_client_ctx_t *ctx,
6299251881Speter                    apr_pool_t *result_pool,
6300251881Speter                    apr_pool_t *scratch_pool)
6301251881Speter{
6302251881Speter  int i;
6303251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
6304251881Speter  apr_hash_t *subtrees_with_mergeinfo;
6305251881Speter  apr_hash_t *excluded_subtrees;
6306251881Speter  apr_hash_t *switched_subtrees;
6307251881Speter  apr_hash_t *shallow_subtrees;
6308251881Speter  apr_hash_t *missing_subtrees;
6309251881Speter  struct pre_merge_status_baton_t pre_merge_status_baton;
6310251881Speter
6311251881Speter  /* Case 1: Subtrees with explicit mergeinfo. */
6312251881Speter  SVN_ERR(get_wc_explicit_mergeinfo_catalog(&subtrees_with_mergeinfo,
6313251881Speter                                            target->abspath,
6314251881Speter                                            depth, ctx,
6315251881Speter                                            result_pool, scratch_pool));
6316251881Speter  if (subtrees_with_mergeinfo)
6317251881Speter    {
6318251881Speter      apr_hash_index_t *hi;
6319251881Speter
6320251881Speter      for (hi = apr_hash_first(scratch_pool, subtrees_with_mergeinfo);
6321251881Speter           hi;
6322251881Speter           hi = apr_hash_next(hi))
6323251881Speter        {
6324251881Speter          const char *wc_path = svn__apr_hash_index_key(hi);
6325251881Speter          svn_mergeinfo_t mergeinfo = svn__apr_hash_index_val(hi);
6326251881Speter          svn_client__merge_path_t *mergeinfo_child =
6327251881Speter            svn_client__merge_path_create(wc_path, result_pool);
6328251881Speter
6329251881Speter          svn_pool_clear(iterpool);
6330251881Speter
6331251881Speter          /* Stash this child's pre-existing mergeinfo. */
6332251881Speter          mergeinfo_child->pre_merge_mergeinfo = mergeinfo;
6333251881Speter
6334251881Speter          /* Note if this child has non-inheritable mergeinfo */
6335251881Speter          mergeinfo_child->has_noninheritable
6336251881Speter            = svn_mergeinfo__is_noninheritable(
6337251881Speter                mergeinfo_child->pre_merge_mergeinfo, iterpool);
6338251881Speter
6339251881Speter          /* Append it.  We'll sort below. */
6340251881Speter          APR_ARRAY_PUSH(children_with_mergeinfo, svn_client__merge_path_t *)
6341251881Speter            = svn_client__merge_path_dup(mergeinfo_child, result_pool);
6342251881Speter        }
6343251881Speter
6344251881Speter      /* Sort CHILDREN_WITH_MERGEINFO by each child's path (i.e. as per
6345251881Speter         compare_merge_path_t_as_paths).  Any subsequent insertions of new
6346251881Speter         children with insert_child_to_merge() require this ordering. */
6347251881Speter      qsort(children_with_mergeinfo->elts,
6348251881Speter            children_with_mergeinfo->nelts,
6349251881Speter            children_with_mergeinfo->elt_size,
6350251881Speter            compare_merge_path_t_as_paths);
6351251881Speter    }
6352251881Speter
6353251881Speter  /* Case 2: Switched subtrees
6354251881Speter     Case 10: Paths at depths of 'empty' or 'files'
6355251881Speter     Case 11: Paths missing from disk */
6356251881Speter  pre_merge_status_baton.wc_ctx = ctx->wc_ctx;
6357251881Speter  switched_subtrees = apr_hash_make(scratch_pool);
6358251881Speter  pre_merge_status_baton.switched_subtrees = switched_subtrees;
6359251881Speter  shallow_subtrees = apr_hash_make(scratch_pool);
6360251881Speter  pre_merge_status_baton.shallow_subtrees = shallow_subtrees;
6361251881Speter  missing_subtrees = apr_hash_make(scratch_pool);
6362251881Speter  pre_merge_status_baton.missing_subtrees = missing_subtrees;
6363251881Speter  pre_merge_status_baton.pool = scratch_pool;
6364251881Speter  SVN_ERR(svn_wc_walk_status(ctx->wc_ctx,
6365251881Speter                             target->abspath,
6366251881Speter                             depth,
6367251881Speter                             TRUE /* get_all */,
6368251881Speter                             FALSE /* no_ignore */,
6369251881Speter                             TRUE /* ignore_text_mods */,
6370251881Speter                             NULL /* ingore_patterns */,
6371251881Speter                             pre_merge_status_cb, &pre_merge_status_baton,
6372251881Speter                             ctx->cancel_func, ctx->cancel_baton,
6373251881Speter                             scratch_pool));
6374251881Speter
6375251881Speter  /* Issue #2915: Raise an error describing the roots of any missing
6376251881Speter     subtrees, i.e. those that the WC thinks are on disk but have been
6377251881Speter     removed outside of Subversion. */
6378251881Speter  if (apr_hash_count(missing_subtrees))
6379251881Speter    {
6380251881Speter      apr_hash_index_t *hi;
6381251881Speter      svn_stringbuf_t *missing_subtree_err_buf =
6382251881Speter        svn_stringbuf_create(_("Merge tracking not allowed with missing "
6383251881Speter                               "subtrees; try restoring these items "
6384251881Speter                               "first:\n"), scratch_pool);
6385251881Speter
6386251881Speter      for (hi = apr_hash_first(scratch_pool, missing_subtrees);
6387251881Speter           hi;
6388251881Speter           hi = apr_hash_next(hi))
6389251881Speter        {
6390251881Speter          svn_pool_clear(iterpool);
6391251881Speter          svn_stringbuf_appendcstr(missing_subtree_err_buf,
6392251881Speter                                   svn_dirent_local_style(
6393251881Speter                                     svn__apr_hash_index_key(hi), iterpool));
6394251881Speter          svn_stringbuf_appendcstr(missing_subtree_err_buf, "\n");
6395251881Speter        }
6396251881Speter
6397251881Speter      return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE,
6398251881Speter                              NULL, missing_subtree_err_buf->data);
6399251881Speter    }
6400251881Speter
6401251881Speter  if (apr_hash_count(switched_subtrees))
6402251881Speter    {
6403251881Speter      apr_hash_index_t *hi;
6404251881Speter
6405251881Speter      for (hi = apr_hash_first(scratch_pool, switched_subtrees);
6406251881Speter           hi;
6407251881Speter           hi = apr_hash_next(hi))
6408251881Speter        {
6409251881Speter           const char *wc_path = svn__apr_hash_index_key(hi);
6410251881Speter           svn_client__merge_path_t *child = get_child_with_mergeinfo(
6411251881Speter             children_with_mergeinfo, wc_path);
6412251881Speter
6413251881Speter           if (child)
6414251881Speter             {
6415251881Speter               child->switched = TRUE;
6416251881Speter             }
6417251881Speter           else
6418251881Speter             {
6419251881Speter               svn_client__merge_path_t *switched_child =
6420251881Speter                 svn_client__merge_path_create(wc_path, result_pool);
6421251881Speter               switched_child->switched = TRUE;
6422251881Speter               insert_child_to_merge(children_with_mergeinfo, switched_child,
6423251881Speter                                     result_pool);
6424251881Speter             }
6425251881Speter        }
6426251881Speter    }
6427251881Speter
6428251881Speter  if (apr_hash_count(shallow_subtrees))
6429251881Speter    {
6430251881Speter      apr_hash_index_t *hi;
6431251881Speter
6432251881Speter      for (hi = apr_hash_first(scratch_pool, shallow_subtrees);
6433251881Speter           hi;
6434251881Speter           hi = apr_hash_next(hi))
6435251881Speter        {
6436251881Speter           svn_boolean_t new_shallow_child = FALSE;
6437251881Speter           const char *wc_path = svn__apr_hash_index_key(hi);
6438251881Speter           svn_depth_t *child_depth = svn__apr_hash_index_val(hi);
6439251881Speter           svn_client__merge_path_t *shallow_child = get_child_with_mergeinfo(
6440251881Speter             children_with_mergeinfo, wc_path);
6441251881Speter
6442251881Speter           if (shallow_child)
6443251881Speter             {
6444251881Speter               if (*child_depth == svn_depth_empty
6445251881Speter                   || *child_depth == svn_depth_files)
6446251881Speter                 shallow_child->missing_child = TRUE;
6447251881Speter             }
6448251881Speter           else
6449251881Speter             {
6450251881Speter               shallow_child = svn_client__merge_path_create(wc_path,
6451251881Speter                                                             result_pool);
6452251881Speter               new_shallow_child = TRUE;
6453251881Speter
6454251881Speter               if (*child_depth == svn_depth_empty
6455251881Speter                   || *child_depth == svn_depth_files)
6456251881Speter                 shallow_child->missing_child = TRUE;
6457251881Speter             }
6458251881Speter
6459251881Speter          /* A little trickery: If PATH doesn't have any mergeinfo or has
6460251881Speter             only inheritable mergeinfo, we still describe it as having
6461251881Speter             non-inheritable mergeinfo if it is missing a child due to
6462251881Speter             a shallow depth.  Why? Because the mergeinfo we'll add to PATH
6463251881Speter             to describe the merge must be non-inheritable, so PATH's missing
6464251881Speter             children don't inherit it.  Marking these PATHs as non-
6465251881Speter             inheritable allows the logic for case 3 to properly account
6466251881Speter             for PATH's children. */
6467251881Speter          if (!shallow_child->has_noninheritable
6468251881Speter              && (*child_depth == svn_depth_empty
6469251881Speter                  || *child_depth == svn_depth_files))
6470251881Speter            {
6471251881Speter              shallow_child->has_noninheritable = TRUE;
6472251881Speter            }
6473251881Speter
6474251881Speter          if (new_shallow_child)
6475251881Speter            insert_child_to_merge(children_with_mergeinfo, shallow_child,
6476251881Speter                                  result_pool);
6477251881Speter       }
6478251881Speter    }
6479251881Speter
6480251881Speter  /* Case 6: Paths absent from disk due to server or user exclusion. */
6481251881Speter  SVN_ERR(svn_wc__get_excluded_subtrees(&excluded_subtrees,
6482251881Speter                                        ctx->wc_ctx, target->abspath,
6483251881Speter                                        result_pool, scratch_pool));
6484251881Speter  if (excluded_subtrees)
6485251881Speter    {
6486251881Speter      apr_hash_index_t *hi;
6487251881Speter
6488251881Speter      for (hi = apr_hash_first(scratch_pool, excluded_subtrees);
6489251881Speter           hi;
6490251881Speter           hi = apr_hash_next(hi))
6491251881Speter        {
6492251881Speter           const char *wc_path = svn__apr_hash_index_key(hi);
6493251881Speter           svn_client__merge_path_t *child = get_child_with_mergeinfo(
6494251881Speter             children_with_mergeinfo, wc_path);
6495251881Speter
6496251881Speter           if (child)
6497251881Speter             {
6498251881Speter               child->absent = TRUE;
6499251881Speter             }
6500251881Speter           else
6501251881Speter             {
6502251881Speter               svn_client__merge_path_t *absent_child =
6503251881Speter                 svn_client__merge_path_create(wc_path, result_pool);
6504251881Speter               absent_child->absent = TRUE;
6505251881Speter               insert_child_to_merge(children_with_mergeinfo, absent_child,
6506251881Speter                                     result_pool);
6507251881Speter             }
6508251881Speter        }
6509251881Speter    }
6510251881Speter
6511251881Speter  /* Case 7: The merge target MERGE_CMD_BATON->target->abspath is always
6512251881Speter     present. */
6513251881Speter  if (!get_child_with_mergeinfo(children_with_mergeinfo,
6514251881Speter                                target->abspath))
6515251881Speter    {
6516251881Speter      svn_client__merge_path_t *target_child =
6517251881Speter        svn_client__merge_path_create(target->abspath,
6518251881Speter                                      result_pool);
6519251881Speter      insert_child_to_merge(children_with_mergeinfo, target_child,
6520251881Speter                            result_pool);
6521251881Speter    }
6522251881Speter
6523251881Speter  /* Case 8: Path is an immediate *directory* child of
6524251881Speter     MERGE_CMD_BATON->target->abspath and DEPTH is svn_depth_immediates.
6525251881Speter
6526251881Speter     Case 9: Path is an immediate *file* child of
6527251881Speter     MERGE_CMD_BATON->target->abspath and DEPTH is svn_depth_files. */
6528251881Speter  if (depth == svn_depth_immediates || depth == svn_depth_files)
6529251881Speter    {
6530251881Speter      int j;
6531251881Speter      const apr_array_header_t *immediate_children;
6532251881Speter
6533251881Speter      SVN_ERR(svn_wc__node_get_children_of_working_node(
6534251881Speter        &immediate_children, ctx->wc_ctx,
6535251881Speter        target->abspath, FALSE, scratch_pool, scratch_pool));
6536251881Speter
6537251881Speter      for (j = 0; j < immediate_children->nelts; j++)
6538251881Speter        {
6539251881Speter          const char *immediate_child_abspath =
6540251881Speter            APR_ARRAY_IDX(immediate_children, j, const char *);
6541251881Speter          svn_node_kind_t immediate_child_kind;
6542251881Speter
6543251881Speter          svn_pool_clear(iterpool);
6544251881Speter          SVN_ERR(svn_wc_read_kind2(&immediate_child_kind,
6545251881Speter                                    ctx->wc_ctx, immediate_child_abspath,
6546251881Speter                                    FALSE, FALSE, iterpool));
6547251881Speter          if ((immediate_child_kind == svn_node_dir
6548251881Speter               && depth == svn_depth_immediates)
6549251881Speter              || (immediate_child_kind == svn_node_file
6550251881Speter                  && depth == svn_depth_files))
6551251881Speter            {
6552251881Speter              if (!get_child_with_mergeinfo(children_with_mergeinfo,
6553251881Speter                                            immediate_child_abspath))
6554251881Speter                {
6555251881Speter                  svn_client__merge_path_t *immediate_child =
6556251881Speter                    svn_client__merge_path_create(immediate_child_abspath,
6557251881Speter                                                  result_pool);
6558251881Speter
6559251881Speter                  if (immediate_child_kind == svn_node_dir
6560251881Speter                      && depth == svn_depth_immediates)
6561251881Speter                    immediate_child->immediate_child_dir = TRUE;
6562251881Speter
6563251881Speter                  insert_child_to_merge(children_with_mergeinfo,
6564251881Speter                                        immediate_child, result_pool);
6565251881Speter                }
6566251881Speter            }
6567251881Speter        }
6568251881Speter    }
6569251881Speter
6570251881Speter  /* If DEPTH isn't empty then cover cases 3), 4), and 5), possibly adding
6571251881Speter     elements to CHILDREN_WITH_MERGEINFO. */
6572251881Speter  if (depth <= svn_depth_empty)
6573251881Speter    return SVN_NO_ERROR;
6574251881Speter
6575251881Speter  for (i = 0; i < children_with_mergeinfo->nelts; i++)
6576251881Speter    {
6577251881Speter      svn_client__merge_path_t *child =
6578251881Speter        APR_ARRAY_IDX(children_with_mergeinfo, i,
6579251881Speter                      svn_client__merge_path_t *);
6580251881Speter      svn_pool_clear(iterpool);
6581251881Speter
6582251881Speter      /* Case 3) Where merging to a path with a switched child the path
6583251881Speter         gets non-inheritable mergeinfo for the merge range performed and
6584251881Speter         the child gets its own set of mergeinfo.  If the switched child
6585251881Speter         later "returns", e.g. a switched path is unswitched, the child
6586251881Speter         may not have any explicit mergeinfo.  If the initial merge is
6587251881Speter         repeated we don't want to repeat the merge for the path, but we
6588251881Speter         do want to repeat it for the previously switched child.  To
6589251881Speter         ensure this we check if all of CHILD's non-missing children have
6590251881Speter         explicit mergeinfo (they should already be present in
6591251881Speter         CHILDREN_WITH_MERGEINFO if they do).  If not,
6592251881Speter         add the children without mergeinfo to CHILDREN_WITH_MERGEINFO so
6593251881Speter         do_directory_merge() will merge them independently.
6594251881Speter
6595251881Speter         But that's not enough!  Since do_directory_merge() performs
6596251881Speter         the merges on the paths in CHILDREN_WITH_MERGEINFO in a depth
6597251881Speter         first manner it will merge the previously switched path's parent
6598251881Speter         first.  As part of this merge it will update the parent's
6599251881Speter         previously non-inheritable mergeinfo and make it inheritable
6600251881Speter         (since it notices the path has no missing children), then when
6601251881Speter         do_directory_merge() finally merges the previously missing
6602251881Speter         child it needs to get mergeinfo from the child's nearest
6603251881Speter         ancestor, but since do_directory_merge() already tweaked that
6604251881Speter         mergeinfo, removing the non-inheritable flag, it appears that the
6605251881Speter         child already has been merged to.  To prevent this we set
6606251881Speter         override mergeinfo on the child now, before any merging is done,
6607251881Speter         so it has explicit mergeinfo that reflects only CHILD's
6608251881Speter         inheritable mergeinfo. */
6609251881Speter
6610251881Speter      /* If depth is immediates or files then don't add new children if
6611251881Speter         CHILD is a subtree of the merge target; those children are below
6612251881Speter         the operational depth of the merge. */
6613251881Speter      if (child->has_noninheritable
6614251881Speter          && (i == 0 || depth == svn_depth_infinity))
6615251881Speter        {
6616251881Speter          const apr_array_header_t *children;
6617251881Speter          int j;
6618251881Speter
6619251881Speter          SVN_ERR(svn_wc__node_get_children(&children,
6620251881Speter                                            ctx->wc_ctx,
6621251881Speter                                            child->abspath, FALSE,
6622251881Speter                                            iterpool, iterpool));
6623251881Speter          for (j = 0; j < children->nelts; j++)
6624251881Speter            {
6625251881Speter              svn_client__merge_path_t *child_of_noninheritable;
6626251881Speter              const char *child_abspath = APR_ARRAY_IDX(children, j,
6627251881Speter                                                        const char*);
6628251881Speter
6629251881Speter              /* Does this child already exist in CHILDREN_WITH_MERGEINFO?
6630251881Speter                 If not, create it and insert it into
6631251881Speter                 CHILDREN_WITH_MERGEINFO and set override mergeinfo on
6632251881Speter                 it. */
6633251881Speter              child_of_noninheritable =
6634251881Speter                get_child_with_mergeinfo(children_with_mergeinfo,
6635251881Speter                                         child_abspath);
6636251881Speter              if (!child_of_noninheritable)
6637251881Speter                {
6638251881Speter                  /* Don't add directory children if DEPTH
6639251881Speter                     is svn_depth_files. */
6640251881Speter                  if (depth == svn_depth_files)
6641251881Speter                    {
6642251881Speter                      svn_node_kind_t child_kind;
6643251881Speter                      SVN_ERR(svn_wc_read_kind2(&child_kind,
6644251881Speter                                                ctx->wc_ctx, child_abspath,
6645251881Speter                                                FALSE, FALSE, iterpool));
6646251881Speter                      if (child_kind != svn_node_file)
6647251881Speter                        continue;
6648251881Speter                    }
6649251881Speter                  /* else DEPTH is infinity or immediates so we want both
6650251881Speter                     directory and file children. */
6651251881Speter
6652251881Speter                  child_of_noninheritable =
6653251881Speter                    svn_client__merge_path_create(child_abspath, result_pool);
6654251881Speter                  child_of_noninheritable->child_of_noninheritable = TRUE;
6655251881Speter                  insert_child_to_merge(children_with_mergeinfo,
6656251881Speter                                        child_of_noninheritable,
6657251881Speter                                        result_pool);
6658251881Speter                  if (!dry_run && same_repos)
6659251881Speter                    {
6660251881Speter                      svn_mergeinfo_t mergeinfo;
6661251881Speter
6662251881Speter                      SVN_ERR(svn_client__get_wc_mergeinfo(
6663251881Speter                        &mergeinfo, NULL,
6664251881Speter                        svn_mergeinfo_nearest_ancestor,
6665251881Speter                        child_of_noninheritable->abspath,
6666251881Speter                        target->abspath, NULL, FALSE,
6667251881Speter                        ctx, iterpool, iterpool));
6668251881Speter
6669251881Speter                      SVN_ERR(svn_client__record_wc_mergeinfo(
6670251881Speter                        child_of_noninheritable->abspath, mergeinfo,
6671251881Speter                        FALSE, ctx, iterpool));
6672251881Speter                    }
6673251881Speter                }
6674251881Speter            }
6675251881Speter        }
6676251881Speter      /* Case 4 and 5 are handled by the following function. */
6677251881Speter      SVN_ERR(insert_parent_and_sibs_of_sw_absent_del_subtree(
6678251881Speter        children_with_mergeinfo, target, &i, child,
6679251881Speter        depth, ctx, result_pool));
6680251881Speter    } /* i < children_with_mergeinfo->nelts */
6681251881Speter  svn_pool_destroy(iterpool);
6682251881Speter
6683251881Speter  return SVN_NO_ERROR;
6684251881Speter}
6685251881Speter
6686251881Speter
6687251881Speter/* Implements the svn_log_entry_receiver_t interface.
6688251881Speter *
6689251881Speter * BATON is an 'apr_array_header_t *' array of 'svn_revnum_t'.
6690251881Speter * Push a copy of LOG_ENTRY->revision onto BATON.  Thus, a
6691251881Speter * series of invocations of this callback accumulates the
6692251881Speter * corresponding set of revisions into BATON.
6693251881Speter */
6694251881Speterstatic svn_error_t *
6695251881Speterlog_changed_revs(void *baton,
6696251881Speter                 svn_log_entry_t *log_entry,
6697251881Speter                 apr_pool_t *pool)
6698251881Speter{
6699251881Speter  apr_array_header_t *revs = baton;
6700251881Speter
6701251881Speter  APR_ARRAY_PUSH(revs, svn_revnum_t) = log_entry->revision;
6702251881Speter  return SVN_NO_ERROR;
6703251881Speter}
6704251881Speter
6705251881Speter
6706251881Speter/* Set *MIN_REV_P to the oldest and *MAX_REV_P to the youngest start or end
6707251881Speter * revision occurring in RANGELIST, or to SVN_INVALID_REVNUM if RANGELIST
6708251881Speter * is empty. */
6709251881Speterstatic void
6710251881Spetermerge_range_find_extremes(svn_revnum_t *min_rev_p,
6711251881Speter                          svn_revnum_t *max_rev_p,
6712251881Speter                          const svn_rangelist_t *rangelist)
6713251881Speter{
6714251881Speter  int i;
6715251881Speter
6716251881Speter  *min_rev_p = SVN_INVALID_REVNUM;
6717251881Speter  *max_rev_p = SVN_INVALID_REVNUM;
6718251881Speter  for (i = 0; i < rangelist->nelts; i++)
6719251881Speter    {
6720251881Speter      svn_merge_range_t *range
6721251881Speter        = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
6722251881Speter      svn_revnum_t range_min = MIN(range->start, range->end);
6723251881Speter      svn_revnum_t range_max = MAX(range->start, range->end);
6724251881Speter
6725251881Speter      if ((! SVN_IS_VALID_REVNUM(*min_rev_p)) || (range_min < *min_rev_p))
6726251881Speter        *min_rev_p = range_min;
6727251881Speter      if ((! SVN_IS_VALID_REVNUM(*max_rev_p)) || (range_max > *max_rev_p))
6728251881Speter        *max_rev_p = range_max;
6729251881Speter    }
6730251881Speter}
6731251881Speter
6732251881Speter/* Wrapper around svn_ra_get_log2(). Invoke RECEIVER with RECEIVER_BATON
6733251881Speter * on each commit from YOUNGEST_REV to OLDEST_REV in which TARGET_RELPATH
6734251881Speter * changed.  TARGET_RELPATH is relative to RA_SESSION's URL.
6735251881Speter * Important: Revision properties are not retrieved by this function for
6736251881Speter * performance reasons.
6737251881Speter */
6738251881Speterstatic svn_error_t *
6739251881Speterget_log(svn_ra_session_t *ra_session,
6740251881Speter        const char *target_relpath,
6741251881Speter        svn_revnum_t youngest_rev,
6742251881Speter        svn_revnum_t oldest_rev,
6743251881Speter        svn_boolean_t discover_changed_paths,
6744251881Speter        svn_log_entry_receiver_t receiver,
6745251881Speter        void *receiver_baton,
6746251881Speter        apr_pool_t *pool)
6747251881Speter{
6748251881Speter  apr_array_header_t *log_targets;
6749251881Speter  apr_array_header_t *revprops;
6750251881Speter
6751251881Speter  log_targets = apr_array_make(pool, 1, sizeof(const char *));
6752251881Speter  APR_ARRAY_PUSH(log_targets, const char *) = target_relpath;
6753251881Speter
6754251881Speter  revprops = apr_array_make(pool, 0, sizeof(const char *));
6755251881Speter
6756251881Speter  SVN_ERR(svn_ra_get_log2(ra_session, log_targets, youngest_rev,
6757251881Speter                          oldest_rev, 0 /* limit */, discover_changed_paths,
6758251881Speter                          FALSE /* strict_node_history */,
6759251881Speter                          FALSE /* include_merged_revisions */,
6760251881Speter                          revprops, receiver, receiver_baton, pool));
6761251881Speter
6762251881Speter  return SVN_NO_ERROR;
6763251881Speter}
6764251881Speter
6765251881Speter/* Set *OPERATIVE_RANGES_P to an array of svn_merge_range_t * merge
6766251881Speter   range objects copied wholesale from RANGES which have the property
6767251881Speter   that in some revision within that range the object identified by
6768251881Speter   RA_SESSION was modified (if by "modified" we mean "'svn log' would
6769251881Speter   return that revision).  *OPERATIVE_RANGES_P is allocated from the
6770251881Speter   same pool as RANGES, and the ranges within it are shared with
6771251881Speter   RANGES, too.
6772251881Speter
6773251881Speter   *OPERATIVE_RANGES_P may be the same as RANGES (that is, the output
6774251881Speter   parameter is set only after the input is no longer used).
6775251881Speter
6776251881Speter   Use POOL for temporary allocations.  */
6777251881Speterstatic svn_error_t *
6778251881Speterremove_noop_merge_ranges(svn_rangelist_t **operative_ranges_p,
6779251881Speter                         svn_ra_session_t *ra_session,
6780251881Speter                         const svn_rangelist_t *ranges,
6781251881Speter                         apr_pool_t *pool)
6782251881Speter{
6783251881Speter  int i;
6784251881Speter  svn_revnum_t oldest_rev, youngest_rev;
6785251881Speter  apr_array_header_t *changed_revs =
6786251881Speter    apr_array_make(pool, ranges->nelts, sizeof(svn_revnum_t));
6787251881Speter  svn_rangelist_t *operative_ranges =
6788251881Speter    apr_array_make(ranges->pool, ranges->nelts, ranges->elt_size);
6789251881Speter
6790251881Speter  /* Find the revision extremes of the RANGES we have. */
6791251881Speter  merge_range_find_extremes(&oldest_rev, &youngest_rev, ranges);
6792251881Speter  if (SVN_IS_VALID_REVNUM(oldest_rev))
6793251881Speter    oldest_rev++;  /* make it inclusive */
6794251881Speter
6795251881Speter  /* Get logs across those ranges, recording which revisions hold
6796251881Speter     changes to our object's history. */
6797251881Speter  SVN_ERR(get_log(ra_session, "", youngest_rev, oldest_rev, FALSE,
6798251881Speter                  log_changed_revs, changed_revs, pool));
6799251881Speter
6800251881Speter  /* Are there *any* changes? */
6801251881Speter  if (changed_revs->nelts)
6802251881Speter    {
6803251881Speter      /* Our list of changed revisions should be in youngest-to-oldest
6804251881Speter         order. */
6805251881Speter      svn_revnum_t youngest_changed_rev
6806251881Speter        = APR_ARRAY_IDX(changed_revs, 0, svn_revnum_t);
6807251881Speter      svn_revnum_t oldest_changed_rev
6808251881Speter        = APR_ARRAY_IDX(changed_revs, changed_revs->nelts - 1, svn_revnum_t);
6809251881Speter
6810251881Speter      /* Now, copy from RANGES to *OPERATIVE_RANGES, filtering out ranges
6811251881Speter         that aren't operative (by virtue of not having any revisions
6812251881Speter         represented in the CHANGED_REVS array). */
6813251881Speter      for (i = 0; i < ranges->nelts; i++)
6814251881Speter        {
6815251881Speter          svn_merge_range_t *range = APR_ARRAY_IDX(ranges, i,
6816251881Speter                                                   svn_merge_range_t *);
6817251881Speter          svn_revnum_t range_min = MIN(range->start, range->end) + 1;
6818251881Speter          svn_revnum_t range_max = MAX(range->start, range->end);
6819251881Speter          int j;
6820251881Speter
6821251881Speter          /* If the merge range is entirely outside the range of changed
6822251881Speter             revisions, we've no use for it. */
6823251881Speter          if ((range_min > youngest_changed_rev)
6824251881Speter              || (range_max < oldest_changed_rev))
6825251881Speter            continue;
6826251881Speter
6827251881Speter          /* Walk through the changed_revs to see if any of them fall
6828251881Speter             inside our current range. */
6829251881Speter          for (j = 0; j < changed_revs->nelts; j++)
6830251881Speter            {
6831251881Speter              svn_revnum_t changed_rev
6832251881Speter                = APR_ARRAY_IDX(changed_revs, j, svn_revnum_t);
6833251881Speter              if ((changed_rev >= range_min) && (changed_rev <= range_max))
6834251881Speter                {
6835251881Speter                  APR_ARRAY_PUSH(operative_ranges, svn_merge_range_t *) =
6836251881Speter                    range;
6837251881Speter                  break;
6838251881Speter                }
6839251881Speter            }
6840251881Speter        }
6841251881Speter    }
6842251881Speter
6843251881Speter  *operative_ranges_p = operative_ranges;
6844251881Speter  return SVN_NO_ERROR;
6845251881Speter}
6846251881Speter
6847251881Speter
6848251881Speter/*-----------------------------------------------------------------------*/
6849251881Speter
6850251881Speter/*** Merge Source Normalization ***/
6851251881Speter
6852251881Speter/* qsort-compatible sort routine, rating merge_source_t * objects to
6853251881Speter   be in descending (youngest-to-oldest) order based on their ->loc1->rev
6854251881Speter   component. */
6855251881Speterstatic int
6856251881Spetercompare_merge_source_ts(const void *a,
6857251881Speter                        const void *b)
6858251881Speter{
6859251881Speter  svn_revnum_t a_rev = (*(const merge_source_t *const *)a)->loc1->rev;
6860251881Speter  svn_revnum_t b_rev = (*(const merge_source_t *const *)b)->loc1->rev;
6861251881Speter  if (a_rev == b_rev)
6862251881Speter    return 0;
6863251881Speter  return a_rev < b_rev ? 1 : -1;
6864251881Speter}
6865251881Speter
6866251881Speter/* Set *MERGE_SOURCE_TS_P to a list of merge sources generated by
6867251881Speter   slicing history location SEGMENTS with a given requested merge
6868251881Speter   RANGE.  Use SOURCE_LOC for full source URL calculation.
6869251881Speter
6870251881Speter   Order the merge sources in *MERGE_SOURCE_TS_P from oldest to
6871251881Speter   youngest. */
6872251881Speterstatic svn_error_t *
6873251881Spetercombine_range_with_segments(apr_array_header_t **merge_source_ts_p,
6874251881Speter                            const svn_merge_range_t *range,
6875251881Speter                            const apr_array_header_t *segments,
6876251881Speter                            const svn_client__pathrev_t *source_loc,
6877251881Speter                            apr_pool_t *pool)
6878251881Speter{
6879251881Speter  apr_array_header_t *merge_source_ts =
6880251881Speter    apr_array_make(pool, 1, sizeof(merge_source_t *));
6881251881Speter  svn_revnum_t minrev = MIN(range->start, range->end) + 1;
6882251881Speter  svn_revnum_t maxrev = MAX(range->start, range->end);
6883251881Speter  svn_boolean_t subtractive = (range->start > range->end);
6884251881Speter  int i;
6885251881Speter
6886251881Speter  for (i = 0; i < segments->nelts; i++)
6887251881Speter    {
6888251881Speter      svn_location_segment_t *segment =
6889251881Speter        APR_ARRAY_IDX(segments, i, svn_location_segment_t *);
6890251881Speter      svn_client__pathrev_t *loc1, *loc2;
6891251881Speter      merge_source_t *merge_source;
6892251881Speter      const char *path1 = NULL;
6893251881Speter      svn_revnum_t rev1;
6894251881Speter
6895251881Speter      /* If this segment doesn't overlap our range at all, or
6896251881Speter         represents a gap, ignore it. */
6897251881Speter      if ((segment->range_end < minrev)
6898251881Speter          || (segment->range_start > maxrev)
6899251881Speter          || (! segment->path))
6900251881Speter        continue;
6901251881Speter
6902251881Speter      /* If our range spans a segment boundary, we have to point our
6903251881Speter         merge_source_t's path1 to the path of the immediately older
6904251881Speter         segment, else it points to the same location as its path2.  */
6905251881Speter      rev1 = MAX(segment->range_start, minrev) - 1;
6906251881Speter      if (minrev <= segment->range_start)
6907251881Speter        {
6908251881Speter          if (i > 0)
6909251881Speter            {
6910251881Speter              path1 = (APR_ARRAY_IDX(segments, i - 1,
6911251881Speter                                     svn_location_segment_t *))->path;
6912251881Speter            }
6913251881Speter          /* If we've backed PATH1 up into a segment gap, let's back
6914251881Speter             it up further still to the segment before the gap.  We'll
6915251881Speter             have to adjust rev1, too. */
6916251881Speter          if ((! path1) && (i > 1))
6917251881Speter            {
6918251881Speter              path1 = (APR_ARRAY_IDX(segments, i - 2,
6919251881Speter                                     svn_location_segment_t *))->path;
6920251881Speter              rev1 = (APR_ARRAY_IDX(segments, i - 2,
6921251881Speter                                    svn_location_segment_t *))->range_end;
6922251881Speter            }
6923251881Speter        }
6924251881Speter      else
6925251881Speter        {
6926251881Speter          path1 = apr_pstrdup(pool, segment->path);
6927251881Speter        }
6928251881Speter
6929251881Speter      /* If we don't have two valid paths, we won't know what to do
6930251881Speter         when merging.  This could happen if someone requested a merge
6931251881Speter         where the source didn't exist in a particular revision or
6932251881Speter         something.  The merge code would probably bomb out anyway, so
6933251881Speter         we'll just *not* create a merge source in this case. */
6934251881Speter      if (! (path1 && segment->path))
6935251881Speter        continue;
6936251881Speter
6937251881Speter      /* Build our merge source structure. */
6938251881Speter      loc1 = svn_client__pathrev_create_with_relpath(
6939251881Speter               source_loc->repos_root_url, source_loc->repos_uuid,
6940251881Speter               rev1, path1, pool);
6941251881Speter      loc2 = svn_client__pathrev_create_with_relpath(
6942251881Speter               source_loc->repos_root_url, source_loc->repos_uuid,
6943251881Speter               MIN(segment->range_end, maxrev), segment->path, pool);
6944251881Speter      /* If this is subtractive, reverse the whole calculation. */
6945251881Speter      if (subtractive)
6946251881Speter        merge_source = merge_source_create(loc2, loc1, TRUE /* ancestral */,
6947251881Speter                                           pool);
6948251881Speter      else
6949251881Speter        merge_source = merge_source_create(loc1, loc2, TRUE /* ancestral */,
6950251881Speter                                           pool);
6951251881Speter
6952251881Speter      APR_ARRAY_PUSH(merge_source_ts, merge_source_t *) = merge_source;
6953251881Speter    }
6954251881Speter
6955251881Speter  /* If this was a subtractive merge, and we created more than one
6956251881Speter     merge source, we need to reverse the sort ordering of our sources. */
6957251881Speter  if (subtractive && (merge_source_ts->nelts > 1))
6958251881Speter    qsort(merge_source_ts->elts, merge_source_ts->nelts,
6959251881Speter          merge_source_ts->elt_size, compare_merge_source_ts);
6960251881Speter
6961251881Speter  *merge_source_ts_p = merge_source_ts;
6962251881Speter  return SVN_NO_ERROR;
6963251881Speter}
6964251881Speter
6965251881Speter/* Similar to normalize_merge_sources() except the input MERGE_RANGE_TS is a
6966251881Speter * rangelist.
6967251881Speter */
6968251881Speterstatic svn_error_t *
6969251881Speternormalize_merge_sources_internal(apr_array_header_t **merge_sources_p,
6970251881Speter                                 const svn_client__pathrev_t *source_loc,
6971251881Speter                                 const svn_rangelist_t *merge_range_ts,
6972251881Speter                                 svn_ra_session_t *ra_session,
6973251881Speter                                 svn_client_ctx_t *ctx,
6974251881Speter                                 apr_pool_t *result_pool,
6975251881Speter                                 apr_pool_t *scratch_pool)
6976251881Speter{
6977251881Speter  svn_revnum_t source_peg_revnum = source_loc->rev;
6978251881Speter  svn_revnum_t oldest_requested, youngest_requested;
6979251881Speter  svn_revnum_t trim_revision = SVN_INVALID_REVNUM;
6980251881Speter  apr_array_header_t *segments;
6981251881Speter  int i;
6982251881Speter
6983251881Speter  /* Initialize our return variable. */
6984251881Speter  *merge_sources_p = apr_array_make(result_pool, 1, sizeof(merge_source_t *));
6985251881Speter
6986251881Speter  /* No ranges to merge?  No problem. */
6987251881Speter  if (merge_range_ts->nelts == 0)
6988251881Speter    return SVN_NO_ERROR;
6989251881Speter
6990251881Speter  /* Find the extremes of the revisions across our set of ranges. */
6991251881Speter  merge_range_find_extremes(&oldest_requested, &youngest_requested,
6992251881Speter                            merge_range_ts);
6993251881Speter
6994251881Speter  /* ### FIXME:  Our underlying APIs can't yet handle the case where
6995251881Speter     the peg revision isn't the youngest of the three revisions.  So
6996251881Speter     we'll just verify that the source in the peg revision is related
6997251881Speter     to the source in the youngest requested revision (which is
6998251881Speter     all the underlying APIs would do in this case right now anyway). */
6999251881Speter  if (source_peg_revnum < youngest_requested)
7000251881Speter    {
7001251881Speter      svn_client__pathrev_t *start_loc;
7002251881Speter
7003251881Speter      SVN_ERR(svn_client__repos_location(&start_loc,
7004251881Speter                                         ra_session, source_loc,
7005251881Speter                                         youngest_requested,
7006251881Speter                                         ctx, scratch_pool, scratch_pool));
7007251881Speter      source_peg_revnum = youngest_requested;
7008251881Speter    }
7009251881Speter
7010251881Speter  /* Fetch the locations for our merge range span. */
7011251881Speter  SVN_ERR(svn_client__repos_location_segments(&segments,
7012251881Speter                                              ra_session, source_loc->url,
7013251881Speter                                              source_peg_revnum,
7014251881Speter                                              youngest_requested,
7015251881Speter                                              oldest_requested,
7016251881Speter                                              ctx, result_pool));
7017251881Speter
7018251881Speter  /* See if we fetched enough history to do the job.  "Surely we did,"
7019251881Speter     you say.  "After all, we covered the entire requested merge
7020251881Speter     range."  Yes, that's true, but if our first segment doesn't
7021251881Speter     extend back to the oldest request revision, we've got a special
7022251881Speter     case to deal with.  Or if the first segment represents a gap,
7023251881Speter     that's another special case.  */
7024251881Speter  trim_revision = SVN_INVALID_REVNUM;
7025251881Speter  if (segments->nelts)
7026251881Speter    {
7027251881Speter      svn_location_segment_t *first_segment =
7028251881Speter        APR_ARRAY_IDX(segments, 0, svn_location_segment_t *);
7029251881Speter
7030251881Speter      /* If the first segment doesn't start with the OLDEST_REQUESTED
7031251881Speter         revision, we'll need to pass a trim revision to our range
7032251881Speter         cruncher. */
7033251881Speter      if (first_segment->range_start != oldest_requested)
7034251881Speter        {
7035251881Speter          trim_revision = first_segment->range_start;
7036251881Speter        }
7037251881Speter
7038251881Speter      /* Else, if the first segment has no path (and therefore is a
7039251881Speter         gap), then we'll fetch the copy source revision from the
7040251881Speter         second segment (provided there is one, of course) and use it
7041251881Speter         to prepend an extra pathful segment to our list.
7042251881Speter
7043251881Speter         ### We could avoid this bit entirely if we'd passed
7044251881Speter         ### SVN_INVALID_REVNUM instead of OLDEST_REQUESTED to
7045251881Speter         ### svn_client__repos_location_segments(), but that would
7046251881Speter         ### really penalize clients hitting pre-1.5 repositories with
7047251881Speter         ### the typical small merge range request (because of the
7048251881Speter         ### lack of a node-origins cache in the repository).  */
7049251881Speter      else if (! first_segment->path)
7050251881Speter        {
7051251881Speter          if (segments->nelts > 1)
7052251881Speter            {
7053251881Speter              svn_location_segment_t *second_segment =
7054251881Speter                APR_ARRAY_IDX(segments, 1, svn_location_segment_t *);
7055251881Speter              const char *segment_url;
7056251881Speter              const char *original_repos_relpath;
7057251881Speter              svn_revnum_t original_revision;
7058251881Speter              svn_opt_revision_t range_start_rev;
7059251881Speter              range_start_rev.kind = svn_opt_revision_number;
7060251881Speter              range_start_rev.value.number = second_segment->range_start;
7061251881Speter
7062251881Speter              segment_url = svn_path_url_add_component2(
7063251881Speter                              source_loc->repos_root_url, second_segment->path,
7064251881Speter                              scratch_pool);
7065251881Speter              SVN_ERR(svn_client__get_copy_source(&original_repos_relpath,
7066251881Speter                                                  &original_revision,
7067251881Speter                                                  segment_url,
7068251881Speter                                                  &range_start_rev, ctx,
7069251881Speter                                                  result_pool, scratch_pool));
7070251881Speter              /* Got copyfrom data?  Fix up the first segment to cover
7071251881Speter                 back to COPYFROM_REV + 1, and then prepend a new
7072251881Speter                 segment covering just COPYFROM_REV. */
7073251881Speter              if (original_repos_relpath)
7074251881Speter                {
7075251881Speter                  svn_location_segment_t *new_segment =
7076251881Speter                    apr_pcalloc(result_pool, sizeof(*new_segment));
7077251881Speter
7078251881Speter                  new_segment->path = original_repos_relpath;
7079251881Speter                  new_segment->range_start = original_revision;
7080251881Speter                  new_segment->range_end = original_revision;
7081251881Speter                  svn_sort__array_insert(&new_segment, segments, 0);
7082251881Speter                }
7083251881Speter            }
7084251881Speter        }
7085251881Speter    }
7086251881Speter
7087251881Speter  /* For each range in our requested range set, try to determine the
7088251881Speter     path(s) associated with that range.  */
7089251881Speter  for (i = 0; i < merge_range_ts->nelts; i++)
7090251881Speter    {
7091251881Speter      svn_merge_range_t *range =
7092251881Speter        APR_ARRAY_IDX(merge_range_ts, i, svn_merge_range_t *);
7093251881Speter      apr_array_header_t *merge_sources;
7094251881Speter
7095251881Speter      if (SVN_IS_VALID_REVNUM(trim_revision))
7096251881Speter        {
7097251881Speter          /* If the range predates the trim revision, discard it. */
7098251881Speter          if (MAX(range->start, range->end) < trim_revision)
7099251881Speter            continue;
7100251881Speter
7101251881Speter          /* If the range overlaps the trim revision, trim it. */
7102251881Speter          if (range->start < trim_revision)
7103251881Speter            range->start = trim_revision;
7104251881Speter          if (range->end < trim_revision)
7105251881Speter            range->end = trim_revision;
7106251881Speter        }
7107251881Speter
7108251881Speter      /* Copy the resulting merge sources into master list thereof. */
7109251881Speter      SVN_ERR(combine_range_with_segments(&merge_sources, range,
7110251881Speter                                          segments, source_loc,
7111251881Speter                                          result_pool));
7112251881Speter      apr_array_cat(*merge_sources_p, merge_sources);
7113251881Speter    }
7114251881Speter
7115251881Speter  return SVN_NO_ERROR;
7116251881Speter}
7117251881Speter
7118251881Speter/* Determine the normalized ranges to merge from a given line of history.
7119251881Speter
7120251881Speter   Calculate the result by intersecting the list of location segments at
7121251881Speter   which SOURCE_LOC existed along its line of history with the requested
7122251881Speter   revision ranges in RANGES_TO_MERGE.  RANGES_TO_MERGE is an array of
7123251881Speter   (svn_opt_revision_range_t *) revision ranges.  Use SOURCE_PATH_OR_URL to
7124251881Speter   resolve any WC-relative revision specifiers (such as 'base') in
7125251881Speter   RANGES_TO_MERGE.
7126251881Speter
7127251881Speter   Set *MERGE_SOURCES_P to an array of merge_source_t * objects, each
7128251881Speter   describing a normalized range of revisions to be merged from the line
7129251881Speter   history of SOURCE_LOC.  Order the objects from oldest to youngest.
7130251881Speter
7131251881Speter   RA_SESSION is an RA session open to the repository of SOURCE_LOC; it may
7132251881Speter   be temporarily reparented within this function.  Use RA_SESSION to find
7133251881Speter   the location segments along the line of history of SOURCE_LOC.
7134251881Speter
7135251881Speter   Allocate MERGE_SOURCES_P and its contents in RESULT_POOL.
7136251881Speter
7137251881Speter   See `MERGEINFO MERGE SOURCE NORMALIZATION' for more on the
7138251881Speter   background of this function.
7139251881Speter*/
7140251881Speterstatic svn_error_t *
7141251881Speternormalize_merge_sources(apr_array_header_t **merge_sources_p,
7142251881Speter                        const char *source_path_or_url,
7143251881Speter                        const svn_client__pathrev_t *source_loc,
7144251881Speter                        const apr_array_header_t *ranges_to_merge,
7145251881Speter                        svn_ra_session_t *ra_session,
7146251881Speter                        svn_client_ctx_t *ctx,
7147251881Speter                        apr_pool_t *result_pool,
7148251881Speter                        apr_pool_t *scratch_pool)
7149251881Speter{
7150251881Speter  const char *source_abspath_or_url;
7151251881Speter  svn_revnum_t youngest_rev = SVN_INVALID_REVNUM;
7152251881Speter  svn_rangelist_t *merge_range_ts;
7153251881Speter  int i;
7154251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
7155251881Speter
7156251881Speter  if(!svn_path_is_url(source_path_or_url))
7157251881Speter    SVN_ERR(svn_dirent_get_absolute(&source_abspath_or_url, source_path_or_url,
7158251881Speter                                    scratch_pool));
7159251881Speter  else
7160251881Speter    source_abspath_or_url = source_path_or_url;
7161251881Speter
7162251881Speter  /* Create a list to hold svn_merge_range_t's. */
7163251881Speter  merge_range_ts = apr_array_make(scratch_pool, ranges_to_merge->nelts,
7164251881Speter                                  sizeof(svn_merge_range_t *));
7165251881Speter
7166251881Speter  for (i = 0; i < ranges_to_merge->nelts; i++)
7167251881Speter    {
7168251881Speter      svn_opt_revision_range_t *range
7169251881Speter        = APR_ARRAY_IDX(ranges_to_merge, i, svn_opt_revision_range_t *);
7170251881Speter      svn_merge_range_t mrange;
7171251881Speter
7172251881Speter      svn_pool_clear(iterpool);
7173251881Speter
7174251881Speter      /* Resolve revisions to real numbers, validating as we go. */
7175251881Speter      if ((range->start.kind == svn_opt_revision_unspecified)
7176251881Speter          || (range->end.kind == svn_opt_revision_unspecified))
7177251881Speter        return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL,
7178251881Speter                                _("Not all required revisions are specified"));
7179251881Speter
7180251881Speter      SVN_ERR(svn_client__get_revision_number(&mrange.start, &youngest_rev,
7181251881Speter                                              ctx->wc_ctx,
7182251881Speter                                              source_abspath_or_url,
7183251881Speter                                              ra_session, &range->start,
7184251881Speter                                              iterpool));
7185251881Speter      SVN_ERR(svn_client__get_revision_number(&mrange.end, &youngest_rev,
7186251881Speter                                              ctx->wc_ctx,
7187251881Speter                                              source_abspath_or_url,
7188251881Speter                                              ra_session, &range->end,
7189251881Speter                                              iterpool));
7190251881Speter
7191251881Speter      /* If this isn't a no-op range... */
7192251881Speter      if (mrange.start != mrange.end)
7193251881Speter        {
7194251881Speter          /* ...then add it to the list. */
7195251881Speter          mrange.inheritable = TRUE;
7196251881Speter          APR_ARRAY_PUSH(merge_range_ts, svn_merge_range_t *)
7197251881Speter            = svn_merge_range_dup(&mrange, scratch_pool);
7198251881Speter        }
7199251881Speter    }
7200251881Speter
7201251881Speter  SVN_ERR(normalize_merge_sources_internal(
7202251881Speter            merge_sources_p, source_loc,
7203251881Speter            merge_range_ts, ra_session, ctx, result_pool, scratch_pool));
7204251881Speter
7205251881Speter  svn_pool_destroy(iterpool);
7206251881Speter  return SVN_NO_ERROR;
7207251881Speter}
7208251881Speter
7209251881Speter
7210251881Speter/*-----------------------------------------------------------------------*/
7211251881Speter
7212251881Speter/*** Merge Workhorse Functions ***/
7213251881Speter
7214251881Speter/* Helper for do_directory_merge() and do_file_merge() which filters out a
7215251881Speter   path's own natural history from the mergeinfo describing a merge.
7216251881Speter
7217251881Speter   Given the natural history IMPLICIT_MERGEINFO of some wc merge target path,
7218251881Speter   the repository-relative merge source path SOURCE_REL_PATH, and the
7219251881Speter   requested merge range REQUESTED_RANGE from SOURCE_REL_PATH, remove any
7220251881Speter   portion of REQUESTED_RANGE which is already described in
7221251881Speter   IMPLICIT_MERGEINFO.  Store the result in *FILTERED_RANGELIST.
7222251881Speter
7223251881Speter   This function only filters natural history for mergeinfo that will be
7224251881Speter   *added* during a forward merge.  Removing natural history from explicit
7225251881Speter   mergeinfo is harmless.  If REQUESTED_RANGE describes a reverse merge,
7226251881Speter   then *FILTERED_RANGELIST is simply populated with one range described
7227251881Speter   by REQUESTED_RANGE.  *FILTERED_RANGELIST is never NULL.
7228251881Speter
7229251881Speter   Allocate *FILTERED_RANGELIST in POOL. */
7230251881Speterstatic svn_error_t *
7231251881Speterfilter_natural_history_from_mergeinfo(svn_rangelist_t **filtered_rangelist,
7232251881Speter                                      const char *source_rel_path,
7233251881Speter                                      svn_mergeinfo_t implicit_mergeinfo,
7234251881Speter                                      svn_merge_range_t *requested_range,
7235251881Speter                                      apr_pool_t *pool)
7236251881Speter{
7237251881Speter  /* Make the REQUESTED_RANGE into a rangelist. */
7238251881Speter  svn_rangelist_t *requested_rangelist =
7239251881Speter    svn_rangelist__initialize(requested_range->start, requested_range->end,
7240251881Speter                              requested_range->inheritable, pool);
7241251881Speter
7242251881Speter  *filtered_rangelist = NULL;
7243251881Speter
7244251881Speter  /* For forward merges: If the IMPLICIT_MERGEINFO already describes ranges
7245251881Speter     associated with SOURCE_REL_PATH then filter those ranges out. */
7246251881Speter  if (implicit_mergeinfo
7247251881Speter      && (requested_range->start < requested_range->end))
7248251881Speter    {
7249251881Speter      svn_rangelist_t *implied_rangelist =
7250251881Speter                        svn_hash_gets(implicit_mergeinfo, source_rel_path);
7251251881Speter
7252251881Speter      if (implied_rangelist)
7253251881Speter        SVN_ERR(svn_rangelist_remove(filtered_rangelist,
7254251881Speter                                     implied_rangelist,
7255251881Speter                                     requested_rangelist,
7256251881Speter                                     FALSE, pool));
7257251881Speter    }
7258251881Speter
7259251881Speter  /* If no filtering was performed the filtered rangelist is
7260251881Speter     simply the requested rangelist.*/
7261251881Speter  if (! (*filtered_rangelist))
7262251881Speter    *filtered_rangelist = requested_rangelist;
7263251881Speter
7264251881Speter  return SVN_NO_ERROR;
7265251881Speter}
7266251881Speter
7267251881Speter/* Return a merge source representing the sub-range from START_REV to
7268251881Speter   END_REV of SOURCE.  SOURCE obeys the rules described in the
7269251881Speter   'MERGEINFO MERGE SOURCE NORMALIZATION' comment at the top of this file.
7270251881Speter   The younger of START_REV and END_REV is inclusive while the older is
7271251881Speter   exclusive.
7272251881Speter
7273251881Speter   Allocate the result structure in POOL but leave the URLs in it as shallow
7274251881Speter   copies of the URLs in SOURCE.
7275251881Speter*/
7276251881Speterstatic merge_source_t *
7277251881Spetersubrange_source(const merge_source_t *source,
7278251881Speter                svn_revnum_t start_rev,
7279251881Speter                svn_revnum_t end_rev,
7280251881Speter                apr_pool_t *pool)
7281251881Speter{
7282251881Speter  svn_boolean_t is_rollback = (source->loc1->rev > source->loc2->rev);
7283251881Speter  svn_boolean_t same_urls = (strcmp(source->loc1->url, source->loc2->url) == 0);
7284251881Speter  svn_client__pathrev_t loc1 = *source->loc1;
7285251881Speter  svn_client__pathrev_t loc2 = *source->loc2;
7286251881Speter
7287251881Speter  /* For this function we require that the input source is 'ancestral'. */
7288251881Speter  SVN_ERR_ASSERT_NO_RETURN(source->ancestral);
7289251881Speter  SVN_ERR_ASSERT_NO_RETURN(start_rev != end_rev);
7290251881Speter
7291251881Speter  loc1.rev = start_rev;
7292251881Speter  loc2.rev = end_rev;
7293251881Speter  if (! same_urls)
7294251881Speter    {
7295251881Speter      if (is_rollback && (end_rev != source->loc2->rev))
7296251881Speter        {
7297251881Speter          loc2.url = source->loc1->url;
7298251881Speter        }
7299251881Speter      if ((! is_rollback) && (start_rev != source->loc1->rev))
7300251881Speter        {
7301251881Speter          loc1.url = source->loc2->url;
7302251881Speter        }
7303251881Speter    }
7304251881Speter  return merge_source_create(&loc1, &loc2, source->ancestral, pool);
7305251881Speter}
7306251881Speter
7307251881Speter/* The single-file, simplified version of do_directory_merge(), which see for
7308251881Speter   parameter descriptions.
7309251881Speter
7310251881Speter   Additional parameters:
7311251881Speter
7312251881Speter   If SOURCES_RELATED is set, the "left" and "right" sides of SOURCE are
7313251881Speter   historically related (ancestors, uncles, second
7314251881Speter   cousins thrice removed, etc...).  (This is used to simulate the
7315251881Speter   history checks that the repository logic does in the directory case.)
7316251881Speter
7317251881Speter   If mergeinfo is being recorded to describe this merge, and RESULT_CATALOG
7318251881Speter   is not NULL, then don't record the new mergeinfo on the TARGET_ABSPATH,
7319251881Speter   but instead record it in RESULT_CATALOG, where the key is TARGET_ABSPATH
7320251881Speter   and the value is the new mergeinfo for that path.  Allocate additions
7321251881Speter   to RESULT_CATALOG in pool which RESULT_CATALOG was created in.
7322251881Speter
7323251881Speter   CONFLICTED_RANGE is as documented for do_directory_merge().
7324251881Speter
7325251881Speter   Note: MERGE_B->RA_SESSION1 must be associated with SOURCE->loc1->url and
7326251881Speter   MERGE_B->RA_SESSION2 with SOURCE->loc2->url.
7327251881Speter*/
7328251881Speterstatic svn_error_t *
7329251881Speterdo_file_merge(svn_mergeinfo_catalog_t result_catalog,
7330251881Speter              single_range_conflict_report_t **conflict_report,
7331251881Speter              const merge_source_t *source,
7332251881Speter              const char *target_abspath,
7333251881Speter              const svn_diff_tree_processor_t *processor,
7334251881Speter              svn_boolean_t sources_related,
7335251881Speter              svn_boolean_t squelch_mergeinfo_notifications,
7336251881Speter              merge_cmd_baton_t *merge_b,
7337251881Speter              apr_pool_t *result_pool,
7338251881Speter              apr_pool_t *scratch_pool)
7339251881Speter{
7340251881Speter  svn_rangelist_t *remaining_ranges;
7341251881Speter  svn_client_ctx_t *ctx = merge_b->ctx;
7342251881Speter  svn_merge_range_t range;
7343251881Speter  svn_mergeinfo_t target_mergeinfo;
7344251881Speter  svn_boolean_t inherited = FALSE;
7345251881Speter  svn_boolean_t is_rollback = (source->loc1->rev > source->loc2->rev);
7346251881Speter  const svn_client__pathrev_t *primary_src
7347251881Speter    = is_rollback ? source->loc1 : source->loc2;
7348251881Speter  svn_boolean_t honor_mergeinfo = HONOR_MERGEINFO(merge_b);
7349251881Speter  svn_client__merge_path_t *merge_target = NULL;
7350251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
7351251881Speter
7352251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath));
7353251881Speter
7354251881Speter  *conflict_report = NULL;
7355251881Speter
7356251881Speter  /* Note that this is a single-file merge. */
7357251881Speter  range.start = source->loc1->rev;
7358251881Speter  range.end = source->loc2->rev;
7359251881Speter  range.inheritable = TRUE;
7360251881Speter
7361251881Speter  merge_target = svn_client__merge_path_create(target_abspath, scratch_pool);
7362251881Speter
7363251881Speter  if (honor_mergeinfo)
7364251881Speter    {
7365251881Speter      svn_error_t *err;
7366251881Speter
7367251881Speter      /* Fetch mergeinfo. */
7368251881Speter      err = get_full_mergeinfo(&target_mergeinfo,
7369251881Speter                               &(merge_target->implicit_mergeinfo),
7370251881Speter                               &inherited, svn_mergeinfo_inherited,
7371251881Speter                               merge_b->ra_session1, target_abspath,
7372251881Speter                               MAX(source->loc1->rev, source->loc2->rev),
7373251881Speter                               MIN(source->loc1->rev, source->loc2->rev),
7374251881Speter                               ctx, scratch_pool, iterpool);
7375251881Speter
7376251881Speter      if (err)
7377251881Speter        {
7378251881Speter          if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
7379251881Speter            {
7380251881Speter              err = svn_error_createf(
7381251881Speter                SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING, err,
7382251881Speter                _("Invalid mergeinfo detected on merge target '%s', "
7383251881Speter                  "merge tracking not possible"),
7384251881Speter                svn_dirent_local_style(target_abspath, scratch_pool));
7385251881Speter            }
7386251881Speter          return svn_error_trace(err);
7387251881Speter        }
7388251881Speter
7389251881Speter      /* Calculate remaining merges unless this is a record only merge.
7390251881Speter         In that case the remaining range is the whole range described
7391251881Speter         by SOURCE->rev1:rev2. */
7392251881Speter      if (!merge_b->record_only)
7393251881Speter        {
7394251881Speter          /* ### Bug?  calculate_remaining_ranges() needs 'source' to adhere
7395251881Speter           *   to the requirements of 'MERGEINFO MERGE SOURCE NORMALIZATION'
7396251881Speter           *   here, but it doesn't appear to be guaranteed so. */
7397251881Speter          SVN_ERR(calculate_remaining_ranges(NULL, merge_target,
7398251881Speter                                             source,
7399251881Speter                                             target_mergeinfo,
7400251881Speter                                             merge_b->implicit_src_gap, FALSE,
7401251881Speter                                             merge_b->ra_session1,
7402251881Speter                                             ctx, scratch_pool,
7403251881Speter                                             iterpool));
7404251881Speter          remaining_ranges = merge_target->remaining_ranges;
7405251881Speter
7406251881Speter          /* We are honoring mergeinfo and this is not a simple record only
7407251881Speter             merge which blindly records mergeinfo describing the merge of
7408251881Speter             SOURCE->LOC1->URL@SOURCE->LOC1->REV through
7409251881Speter             SOURCE->LOC2->URL@SOURCE->LOC2->REV.  This means that the oldest
7410251881Speter             and youngest revisions merged (as determined above by
7411251881Speter             calculate_remaining_ranges) might differ from those described
7412251881Speter             in SOURCE.  To keep the '--- Merging *' notifications consistent
7413251881Speter             with the '--- Recording mergeinfo *' notifications, we adjust
7414251881Speter             RANGE to account for such changes. */
7415251881Speter          if (remaining_ranges->nelts)
7416251881Speter            {
7417251881Speter              svn_merge_range_t *adj_start_range =
7418251881Speter                APR_ARRAY_IDX(remaining_ranges, 0, svn_merge_range_t *);
7419251881Speter              svn_merge_range_t *adj_end_range =
7420251881Speter                APR_ARRAY_IDX(remaining_ranges, remaining_ranges->nelts - 1,
7421251881Speter                              svn_merge_range_t *);
7422251881Speter              range.start = adj_start_range->start;
7423251881Speter              range.end = adj_end_range->end;
7424251881Speter            }
7425251881Speter        }
7426251881Speter    }
7427251881Speter
7428251881Speter  /* The simple cases where our remaining range is SOURCE->rev1:rev2. */
7429251881Speter  if (!honor_mergeinfo || merge_b->record_only)
7430251881Speter    {
7431251881Speter      remaining_ranges = apr_array_make(scratch_pool, 1, sizeof(&range));
7432251881Speter      APR_ARRAY_PUSH(remaining_ranges, svn_merge_range_t *) = &range;
7433251881Speter    }
7434251881Speter
7435251881Speter  if (!merge_b->record_only)
7436251881Speter    {
7437251881Speter      svn_rangelist_t *ranges_to_merge = apr_array_copy(scratch_pool,
7438251881Speter                                                        remaining_ranges);
7439251881Speter      const char *target_relpath = "";  /* relative to root of merge */
7440251881Speter
7441251881Speter      if (source->ancestral)
7442251881Speter        {
7443251881Speter          apr_array_header_t *child_with_mergeinfo;
7444251881Speter          svn_client__merge_path_t *target_info;
7445251881Speter
7446251881Speter          /* If we have ancestrally related sources and more than one
7447251881Speter             range to merge, eliminate no-op ranges before going through
7448251881Speter             the effort of downloading the many copies of the file
7449251881Speter             required to do these merges (two copies per range). */
7450251881Speter          if (remaining_ranges->nelts > 1)
7451251881Speter            {
7452251881Speter              const char *old_sess_url;
7453251881Speter              svn_error_t *err;
7454251881Speter
7455251881Speter              SVN_ERR(svn_client__ensure_ra_session_url(&old_sess_url,
7456251881Speter                                                        merge_b->ra_session1,
7457251881Speter                                                        primary_src->url,
7458251881Speter                                                        iterpool));
7459251881Speter              err = remove_noop_merge_ranges(&ranges_to_merge,
7460251881Speter                                             merge_b->ra_session1,
7461251881Speter                                             remaining_ranges, scratch_pool);
7462251881Speter              SVN_ERR(svn_error_compose_create(
7463251881Speter                        err, svn_ra_reparent(merge_b->ra_session1,
7464251881Speter                                             old_sess_url, iterpool)));
7465251881Speter            }
7466251881Speter
7467251881Speter          /* To support notify_merge_begin() initialize our
7468251881Speter             CHILD_WITH_MERGEINFO. See the comment
7469251881Speter             'THE CHILDREN_WITH_MERGEINFO ARRAY' at the start of this file. */
7470251881Speter
7471251881Speter          child_with_mergeinfo = apr_array_make(scratch_pool, 1,
7472251881Speter                                        sizeof(svn_client__merge_path_t *));
7473251881Speter
7474251881Speter          /* ### Create a fake copy of merge_target as we don't keep
7475251881Speter                 remaining_ranges in sync (yet). */
7476251881Speter          target_info = apr_pcalloc(scratch_pool, sizeof(*target_info));
7477251881Speter
7478251881Speter          target_info->abspath = merge_target->abspath;
7479251881Speter          target_info->remaining_ranges = ranges_to_merge;
7480251881Speter
7481251881Speter          APR_ARRAY_PUSH(child_with_mergeinfo, svn_client__merge_path_t *)
7482251881Speter                                    = target_info;
7483251881Speter
7484251881Speter          /* And store in baton to allow using it from notify_merge_begin() */
7485251881Speter          merge_b->notify_begin.nodes_with_mergeinfo = child_with_mergeinfo;
7486251881Speter        }
7487251881Speter
7488251881Speter      while (ranges_to_merge->nelts > 0)
7489251881Speter        {
7490251881Speter          svn_merge_range_t *r = APR_ARRAY_IDX(ranges_to_merge, 0,
7491251881Speter                                               svn_merge_range_t *);
7492251881Speter          const merge_source_t *real_source;
7493251881Speter          const char *left_file, *right_file;
7494251881Speter          apr_hash_t *left_props, *right_props;
7495251881Speter          const svn_diff_source_t *left_source;
7496251881Speter          const svn_diff_source_t *right_source;
7497251881Speter
7498251881Speter          svn_pool_clear(iterpool);
7499251881Speter
7500251881Speter          /* Ensure any subsequent drives gets their own notification. */
7501251881Speter          merge_b->notify_begin.last_abspath = NULL;
7502251881Speter
7503251881Speter          /* While we currently don't allow it, in theory we could be
7504251881Speter             fetching two fulltexts from two different repositories here. */
7505251881Speter          if (source->ancestral)
7506251881Speter            real_source = subrange_source(source, r->start, r->end, iterpool);
7507251881Speter          else
7508251881Speter            real_source = source;
7509251881Speter          SVN_ERR(single_file_merge_get_file(&left_file, &left_props,
7510251881Speter                                             merge_b->ra_session1,
7511251881Speter                                             real_source->loc1,
7512251881Speter                                             target_abspath,
7513251881Speter                                             iterpool, iterpool));
7514251881Speter          SVN_ERR(single_file_merge_get_file(&right_file, &right_props,
7515251881Speter                                             merge_b->ra_session2,
7516251881Speter                                             real_source->loc2,
7517251881Speter                                             target_abspath,
7518251881Speter                                             iterpool, iterpool));
7519251881Speter          /* Calculate sources for the diff processor */
7520251881Speter          left_source = svn_diff__source_create(r->start, iterpool);
7521251881Speter          right_source = svn_diff__source_create(r->end, iterpool);
7522251881Speter
7523251881Speter
7524251881Speter          /* If the sources are related or we're ignoring ancestry in diffs,
7525251881Speter             do a text-n-props merge; otherwise, do a delete-n-add merge. */
7526251881Speter          if (! (merge_b->diff_ignore_ancestry || sources_related))
7527251881Speter            {
7528251881Speter              struct merge_dir_baton_t dir_baton;
7529251881Speter              void *file_baton;
7530251881Speter              svn_boolean_t skip;
7531251881Speter
7532251881Speter              /* Initialize minimal dir baton to allow calculating 'R'eplace
7533251881Speter                 from 'D'elete + 'A'dd. */
7534251881Speter
7535251881Speter              memset(&dir_baton, 0, sizeof(dir_baton));
7536251881Speter              dir_baton.pool = iterpool;
7537251881Speter              dir_baton.tree_conflict_reason = CONFLICT_REASON_NONE;
7538251881Speter              dir_baton.tree_conflict_action = svn_wc_conflict_action_edit;
7539251881Speter              dir_baton.skip_reason = svn_wc_notify_state_unknown;
7540251881Speter
7541251881Speter              /* Delete... */
7542251881Speter              file_baton = NULL;
7543251881Speter              skip = FALSE;
7544251881Speter              SVN_ERR(processor->file_opened(&file_baton, &skip, target_relpath,
7545251881Speter                                             left_source,
7546251881Speter                                             NULL /* right_source */,
7547251881Speter                                             NULL /* copyfrom_source */,
7548251881Speter                                             &dir_baton,
7549251881Speter                                             processor,
7550251881Speter                                             iterpool, iterpool));
7551251881Speter              if (! skip)
7552251881Speter                SVN_ERR(processor->file_deleted(target_relpath,
7553251881Speter                                                left_source,
7554251881Speter                                                left_file,
7555251881Speter                                                left_props,
7556251881Speter                                                file_baton,
7557251881Speter                                                processor,
7558251881Speter                                                iterpool));
7559251881Speter
7560251881Speter              /* ...plus add... */
7561251881Speter              file_baton = NULL;
7562251881Speter              skip = FALSE;
7563251881Speter              SVN_ERR(processor->file_opened(&file_baton, &skip, target_relpath,
7564251881Speter                                             NULL /* left_source */,
7565251881Speter                                             right_source,
7566251881Speter                                             NULL /* copyfrom_source */,
7567251881Speter                                             &dir_baton,
7568251881Speter                                             processor,
7569251881Speter                                             iterpool, iterpool));
7570251881Speter              if (! skip)
7571251881Speter                SVN_ERR(processor->file_added(target_relpath,
7572251881Speter                                              NULL /* copyfrom_source */,
7573251881Speter                                              right_source,
7574251881Speter                                              NULL /* copyfrom_file */,
7575251881Speter                                              right_file,
7576251881Speter                                              NULL /* copyfrom_props */,
7577251881Speter                                              right_props,
7578251881Speter                                              file_baton,
7579251881Speter                                              processor,
7580251881Speter                                              iterpool));
7581251881Speter              /* ... equals replace. */
7582251881Speter            }
7583251881Speter          else
7584251881Speter            {
7585251881Speter              void *file_baton = NULL;
7586251881Speter              svn_boolean_t skip = FALSE;
7587251881Speter              apr_array_header_t *propchanges;
7588251881Speter
7589251881Speter
7590251881Speter              /* Deduce property diffs. */
7591251881Speter              SVN_ERR(svn_prop_diffs(&propchanges, right_props, left_props,
7592251881Speter                                     iterpool));
7593251881Speter
7594251881Speter              SVN_ERR(processor->file_opened(&file_baton, &skip, target_relpath,
7595251881Speter                                             left_source,
7596251881Speter                                             right_source,
7597251881Speter                                             NULL /* copyfrom_source */,
7598251881Speter                                             NULL /* dir_baton */,
7599251881Speter                                             processor,
7600251881Speter                                             iterpool, iterpool));
7601251881Speter              if (! skip)
7602251881Speter                SVN_ERR(processor->file_changed(target_relpath,
7603251881Speter                                              left_source,
7604251881Speter                                              right_source,
7605251881Speter                                              left_file,
7606251881Speter                                              right_file,
7607251881Speter                                              left_props,
7608251881Speter                                              right_props,
7609251881Speter                                              TRUE /* file changed */,
7610251881Speter                                              propchanges,
7611251881Speter                                              file_baton,
7612251881Speter                                              processor,
7613251881Speter                                              iterpool));
7614251881Speter            }
7615251881Speter
7616251881Speter          if (is_path_conflicted_by_merge(merge_b))
7617251881Speter            {
7618251881Speter              merge_source_t *remaining_range = NULL;
7619251881Speter
7620251881Speter              if (real_source->loc2->rev != source->loc2->rev)
7621251881Speter                remaining_range = subrange_source(source,
7622251881Speter                                                  real_source->loc2->rev,
7623251881Speter                                                  source->loc2->rev,
7624251881Speter                                                  scratch_pool);
7625251881Speter              *conflict_report = single_range_conflict_report_create(
7626251881Speter                                   real_source, remaining_range, result_pool);
7627251881Speter
7628251881Speter              /* Only record partial mergeinfo if only a partial merge was
7629251881Speter                 performed before a conflict was encountered. */
7630251881Speter              range.end = r->end;
7631251881Speter              break;
7632251881Speter            }
7633251881Speter
7634251881Speter          /* Now delete the just merged range from the hash
7635251881Speter             (This list is used from notify_merge_begin)
7636251881Speter
7637251881Speter            Directory merges use remove_first_range_from_remaining_ranges() */
7638251881Speter          svn_sort__array_delete(ranges_to_merge, 0, 1);
7639251881Speter        }
7640251881Speter      merge_b->notify_begin.last_abspath = NULL;
7641251881Speter    } /* !merge_b->record_only */
7642251881Speter
7643251881Speter  /* Record updated WC mergeinfo to account for our new merges, minus
7644251881Speter     any unresolved conflicts and skips.  We use the original
7645251881Speter     REMAINING_RANGES here because we want to record all the requested
7646251881Speter     merge ranges, include the noop ones.  */
7647251881Speter  if (RECORD_MERGEINFO(merge_b) && remaining_ranges->nelts)
7648251881Speter    {
7649251881Speter      const char *mergeinfo_path = svn_client__pathrev_fspath(primary_src,
7650251881Speter                                                              scratch_pool);
7651251881Speter      svn_rangelist_t *filtered_rangelist;
7652251881Speter
7653251881Speter      /* Filter any ranges from TARGET_WCPATH's own history, there is no
7654251881Speter         need to record this explicitly in mergeinfo, it is already part
7655251881Speter         of TARGET_WCPATH's natural history (implicit mergeinfo). */
7656251881Speter      SVN_ERR(filter_natural_history_from_mergeinfo(
7657251881Speter        &filtered_rangelist,
7658251881Speter        mergeinfo_path,
7659251881Speter        merge_target->implicit_mergeinfo,
7660251881Speter        &range,
7661251881Speter        iterpool));
7662251881Speter
7663251881Speter      /* Only record mergeinfo if there is something other than
7664251881Speter         self-referential mergeinfo, but don't record mergeinfo if
7665251881Speter         TARGET_WCPATH was skipped. */
7666251881Speter      if (filtered_rangelist->nelts
7667251881Speter          && (apr_hash_count(merge_b->skipped_abspaths) == 0))
7668251881Speter        {
7669251881Speter          apr_hash_t *merges = apr_hash_make(iterpool);
7670251881Speter
7671251881Speter          /* If merge target has inherited mergeinfo set it before
7672251881Speter             recording the first merge range. */
7673251881Speter          if (inherited)
7674251881Speter            SVN_ERR(svn_client__record_wc_mergeinfo(target_abspath,
7675251881Speter                                                    target_mergeinfo,
7676251881Speter                                                    FALSE, ctx,
7677251881Speter                                                    iterpool));
7678251881Speter
7679251881Speter          svn_hash_sets(merges, target_abspath, filtered_rangelist);
7680251881Speter
7681251881Speter          if (!squelch_mergeinfo_notifications)
7682251881Speter            {
7683251881Speter              /* Notify that we are recording mergeinfo describing a merge. */
7684251881Speter              svn_merge_range_t n_range;
7685251881Speter
7686251881Speter              SVN_ERR(svn_mergeinfo__get_range_endpoints(
7687251881Speter                        &n_range.end, &n_range.start, merges, iterpool));
7688251881Speter              n_range.inheritable = TRUE;
7689251881Speter              notify_mergeinfo_recording(target_abspath, &n_range,
7690251881Speter                                         merge_b->ctx, iterpool);
7691251881Speter            }
7692251881Speter
7693251881Speter          SVN_ERR(update_wc_mergeinfo(result_catalog, target_abspath,
7694251881Speter                                      mergeinfo_path, merges, is_rollback,
7695251881Speter                                      ctx, iterpool));
7696251881Speter        }
7697251881Speter    }
7698251881Speter
7699251881Speter  merge_b->notify_begin.nodes_with_mergeinfo = NULL;
7700251881Speter
7701251881Speter  svn_pool_destroy(iterpool);
7702251881Speter
7703251881Speter  return SVN_NO_ERROR;
7704251881Speter}
7705251881Speter
7706251881Speter/* Helper for do_directory_merge() to handle the case where a merge editor
7707251881Speter   drive adds explicit mergeinfo to a path which didn't have any explicit
7708251881Speter   mergeinfo previously.
7709251881Speter
7710251881Speter   MERGE_B is cascaded from the argument of the same
7711251881Speter   name in do_directory_merge().  Should be called only after
7712251881Speter   do_directory_merge() has called populate_remaining_ranges() and populated
7713251881Speter   the remaining_ranges field of each child in
7714251881Speter   CHILDREN_WITH_MERGEINFO (i.e. the remaining_ranges fields can be
7715251881Speter   empty but never NULL).
7716251881Speter
7717251881Speter   If MERGE_B->DRY_RUN is true do nothing, if it is false then
7718251881Speter   for each path (if any) in MERGE_B->PATHS_WITH_NEW_MERGEINFO merge that
7719251881Speter   path's inherited mergeinfo (if any) with its working explicit mergeinfo
7720251881Speter   and set that as the path's new explicit mergeinfo.  Then add an
7721251881Speter   svn_client__merge_path_t * element representing the path to
7722251881Speter   CHILDREN_WITH_MERGEINFO if it isn't already present.  All fields
7723251881Speter   in any elements added to CHILDREN_WITH_MERGEINFO are initialized
7724251881Speter   to FALSE/NULL with the exception of 'path' and 'remaining_ranges'.  The
7725251881Speter   latter is set to a rangelist equal to the remaining_ranges of the path's
7726251881Speter   nearest path-wise ancestor in CHILDREN_WITH_MERGEINFO.
7727251881Speter
7728251881Speter   Any elements added to CHILDREN_WITH_MERGEINFO are allocated
7729251881Speter   in POOL. */
7730251881Speterstatic svn_error_t *
7731251881Speterprocess_children_with_new_mergeinfo(merge_cmd_baton_t *merge_b,
7732251881Speter                                    apr_array_header_t *children_with_mergeinfo,
7733251881Speter                                    apr_pool_t *pool)
7734251881Speter{
7735251881Speter  apr_pool_t *iterpool;
7736251881Speter  apr_hash_index_t *hi;
7737251881Speter
7738251881Speter  if (!merge_b->paths_with_new_mergeinfo || merge_b->dry_run)
7739251881Speter    return SVN_NO_ERROR;
7740251881Speter
7741251881Speter  /* Iterate over each path with explicit mergeinfo added by the merge. */
7742251881Speter  iterpool = svn_pool_create(pool);
7743251881Speter  for (hi = apr_hash_first(pool, merge_b->paths_with_new_mergeinfo);
7744251881Speter       hi;
7745251881Speter       hi = apr_hash_next(hi))
7746251881Speter    {
7747251881Speter      const char *abspath_with_new_mergeinfo = svn__apr_hash_index_key(hi);
7748251881Speter      svn_mergeinfo_t path_inherited_mergeinfo;
7749251881Speter      svn_mergeinfo_t path_explicit_mergeinfo;
7750251881Speter      svn_client__merge_path_t *new_child;
7751251881Speter
7752251881Speter      svn_pool_clear(iterpool);
7753251881Speter
7754251881Speter      /* Note: We could skip recording inherited mergeinfo here if this path
7755251881Speter         was added (with preexisting mergeinfo) by the merge.  That's actually
7756251881Speter         more correct, since the inherited mergeinfo likely describes
7757251881Speter         non-existent or unrelated merge history, but it's not quite so simple
7758251881Speter         as that, see http://subversion.tigris.org/issues/show_bug.cgi?id=4309
7759251881Speter         */
7760251881Speter
7761251881Speter      /* Get the path's new explicit mergeinfo... */
7762251881Speter      SVN_ERR(svn_client__get_wc_mergeinfo(&path_explicit_mergeinfo, NULL,
7763251881Speter                                           svn_mergeinfo_explicit,
7764251881Speter                                           abspath_with_new_mergeinfo,
7765251881Speter                                           NULL, NULL, FALSE,
7766251881Speter                                           merge_b->ctx,
7767251881Speter                                           iterpool, iterpool));
7768251881Speter      /* ...there *should* always be explicit mergeinfo at this point
7769251881Speter         but you can't be too careful. */
7770251881Speter      if (path_explicit_mergeinfo)
7771251881Speter        {
7772251881Speter          /* Get the mergeinfo the path would have inherited before
7773251881Speter             the merge. */
7774251881Speter          SVN_ERR(svn_client__get_wc_or_repos_mergeinfo(
7775251881Speter            &path_inherited_mergeinfo,
7776251881Speter            NULL, NULL,
7777251881Speter            FALSE,
7778251881Speter            svn_mergeinfo_nearest_ancestor, /* We only want inherited MI */
7779251881Speter            merge_b->ra_session2,
7780251881Speter            abspath_with_new_mergeinfo,
7781251881Speter            merge_b->ctx,
7782251881Speter            iterpool));
7783251881Speter
7784251881Speter          /* If the path inherited any mergeinfo then merge that with the
7785251881Speter             explicit mergeinfo and record the result as the path's new
7786251881Speter             explicit mergeinfo. */
7787251881Speter          if (path_inherited_mergeinfo)
7788251881Speter            {
7789251881Speter              SVN_ERR(svn_mergeinfo_merge2(path_explicit_mergeinfo,
7790251881Speter                                           path_inherited_mergeinfo,
7791251881Speter                                           iterpool, iterpool));
7792251881Speter              SVN_ERR(svn_client__record_wc_mergeinfo(
7793251881Speter                                          abspath_with_new_mergeinfo,
7794251881Speter                                          path_explicit_mergeinfo,
7795251881Speter                                          FALSE, merge_b->ctx, iterpool));
7796251881Speter            }
7797251881Speter
7798251881Speter          /* If the path is not in CHILDREN_WITH_MERGEINFO then add it. */
7799251881Speter          new_child =
7800251881Speter            get_child_with_mergeinfo(children_with_mergeinfo,
7801251881Speter                                     abspath_with_new_mergeinfo);
7802251881Speter          if (!new_child)
7803251881Speter            {
7804251881Speter              const svn_client__merge_path_t *parent
7805251881Speter                = find_nearest_ancestor(children_with_mergeinfo,
7806251881Speter                                        FALSE, abspath_with_new_mergeinfo);
7807251881Speter              new_child
7808251881Speter                = svn_client__merge_path_create(abspath_with_new_mergeinfo,
7809251881Speter                                                pool);
7810251881Speter
7811251881Speter              /* If path_with_new_mergeinfo is the merge target itself
7812251881Speter                 then it should already be in
7813251881Speter                 CHILDREN_WITH_MERGEINFO per the criteria of
7814251881Speter                 get_mergeinfo_paths() and we shouldn't be in this block.
7815251881Speter                 If path_with_new_mergeinfo is a subtree then it must have
7816251881Speter                 a parent in CHILDREN_WITH_MERGEINFO if only
7817251881Speter                 the merge target itself...so if we don't find a parent
7818251881Speter                 the caller has done something quite wrong. */
7819251881Speter              SVN_ERR_ASSERT(parent);
7820251881Speter              SVN_ERR_ASSERT(parent->remaining_ranges);
7821251881Speter
7822251881Speter              /* Set the path's remaining_ranges equal to its parent's. */
7823251881Speter              new_child->remaining_ranges = svn_rangelist_dup(
7824251881Speter                 parent->remaining_ranges, pool);
7825251881Speter              insert_child_to_merge(children_with_mergeinfo, new_child, pool);
7826251881Speter            }
7827251881Speter        }
7828251881Speter    }
7829251881Speter  svn_pool_destroy(iterpool);
7830251881Speter
7831251881Speter  return SVN_NO_ERROR;
7832251881Speter}
7833251881Speter
7834251881Speter/* Return true if any path in SUBTREES is equal to, or is a subtree of,
7835251881Speter   LOCAL_ABSPATH.  Return false otherwise.  The keys of SUBTREES are
7836251881Speter   (const char *) absolute paths and its values are irrelevant.
7837251881Speter   If SUBTREES is NULL return false. */
7838251881Speterstatic svn_boolean_t
7839251881Speterpath_is_subtree(const char *local_abspath,
7840251881Speter                apr_hash_t *subtrees,
7841251881Speter                apr_pool_t *pool)
7842251881Speter{
7843251881Speter  if (subtrees)
7844251881Speter    {
7845251881Speter      apr_hash_index_t *hi;
7846251881Speter
7847251881Speter      for (hi = apr_hash_first(pool, subtrees);
7848251881Speter           hi; hi = apr_hash_next(hi))
7849251881Speter        {
7850251881Speter          const char *path_touched_by_merge = svn__apr_hash_index_key(hi);
7851251881Speter          if (svn_dirent_is_ancestor(local_abspath, path_touched_by_merge))
7852251881Speter            return TRUE;
7853251881Speter        }
7854251881Speter    }
7855251881Speter  return FALSE;
7856251881Speter}
7857251881Speter
7858251881Speter/* Return true if any merged, skipped, added or tree-conflicted path
7859251881Speter   recorded in MERGE_B is equal to, or is a subtree of LOCAL_ABSPATH.  Return
7860251881Speter   false otherwise.
7861251881Speter
7862251881Speter   ### Why not text- or prop-conflicted paths? Are such paths guaranteed
7863251881Speter       to be recorded as 'merged' or 'skipped' or 'added', perhaps?
7864251881Speter*/
7865251881Speterstatic svn_boolean_t
7866251881Spetersubtree_touched_by_merge(const char *local_abspath,
7867251881Speter                         merge_cmd_baton_t *merge_b,
7868251881Speter                         apr_pool_t *pool)
7869251881Speter{
7870251881Speter  return (path_is_subtree(local_abspath, merge_b->merged_abspaths, pool)
7871251881Speter          || path_is_subtree(local_abspath, merge_b->skipped_abspaths, pool)
7872251881Speter          || path_is_subtree(local_abspath, merge_b->added_abspaths, pool)
7873251881Speter          || path_is_subtree(local_abspath, merge_b->tree_conflicted_abspaths,
7874251881Speter                             pool));
7875251881Speter}
7876251881Speter
7877251881Speter/* Helper for do_directory_merge() when performing mergeinfo unaware merges.
7878251881Speter
7879251881Speter   Merge the SOURCE diff into TARGET_DIR_WCPATH.
7880251881Speter
7881251881Speter   SOURCE, DEPTH, NOTIFY_B, and MERGE_B
7882251881Speter   are all cascaded from do_directory_merge's arguments of the same names.
7883251881Speter
7884251881Speter   CONFLICT_REPORT is as documented for do_directory_merge().
7885251881Speter
7886251881Speter   NOTE: This is a very thin wrapper around drive_merge_report_editor() and
7887251881Speter   exists only to populate CHILDREN_WITH_MERGEINFO with the single element
7888251881Speter   expected during mergeinfo unaware merges.
7889251881Speter*/
7890251881Speterstatic svn_error_t *
7891251881Speterdo_mergeinfo_unaware_dir_merge(single_range_conflict_report_t **conflict_report,
7892251881Speter                               const merge_source_t *source,
7893251881Speter                               const char *target_dir_wcpath,
7894251881Speter                               apr_array_header_t *children_with_mergeinfo,
7895251881Speter                               const svn_diff_tree_processor_t *processor,
7896251881Speter                               svn_depth_t depth,
7897251881Speter                               merge_cmd_baton_t *merge_b,
7898251881Speter                               apr_pool_t *result_pool,
7899251881Speter                               apr_pool_t *scratch_pool)
7900251881Speter{
7901251881Speter  /* Initialize CHILDREN_WITH_MERGEINFO and populate it with
7902251881Speter     one element describing the merge of SOURCE->rev1:rev2 to
7903251881Speter     TARGET_DIR_WCPATH. */
7904251881Speter  svn_client__merge_path_t *item
7905251881Speter    = svn_client__merge_path_create(target_dir_wcpath, scratch_pool);
7906251881Speter
7907251881Speter  *conflict_report = NULL;
7908251881Speter  item->remaining_ranges = svn_rangelist__initialize(source->loc1->rev,
7909251881Speter                                                     source->loc2->rev,
7910251881Speter                                                     TRUE, scratch_pool);
7911251881Speter  APR_ARRAY_PUSH(children_with_mergeinfo,
7912251881Speter                 svn_client__merge_path_t *) = item;
7913251881Speter  SVN_ERR(drive_merge_report_editor(target_dir_wcpath,
7914251881Speter                                    source,
7915251881Speter                                    NULL, processor, depth,
7916251881Speter                                    merge_b, scratch_pool));
7917251881Speter  if (is_path_conflicted_by_merge(merge_b))
7918251881Speter    {
7919251881Speter      *conflict_report = single_range_conflict_report_create(
7920251881Speter                           source, NULL, result_pool);
7921251881Speter    }
7922251881Speter  return SVN_NO_ERROR;
7923251881Speter}
7924251881Speter
7925251881Speter/* A svn_log_entry_receiver_t baton for log_find_operative_subtree_revs(). */
7926251881Spetertypedef struct log_find_operative_subtree_baton_t
7927251881Speter{
7928251881Speter  /* Mapping of const char * absolute working copy paths to those
7929251881Speter     path's const char * repos absolute paths. */
7930251881Speter  apr_hash_t *operative_children;
7931251881Speter
7932251881Speter  /* As per the arguments of the same name to
7933251881Speter     get_operative_immediate_children(). */
7934251881Speter  const char *merge_source_fspath;
7935251881Speter  const char *merge_target_abspath;
7936251881Speter  svn_depth_t depth;
7937251881Speter  svn_wc_context_t *wc_ctx;
7938251881Speter
7939251881Speter  /* A pool to allocate additions to the hashes in. */
7940251881Speter  apr_pool_t *result_pool;
7941251881Speter} log_find_operative_subtree_baton_t;
7942251881Speter
7943251881Speter/* A svn_log_entry_receiver_t callback for
7944251881Speter   get_inoperative_immediate_children(). */
7945251881Speterstatic svn_error_t *
7946251881Speterlog_find_operative_subtree_revs(void *baton,
7947251881Speter                                svn_log_entry_t *log_entry,
7948251881Speter                                apr_pool_t *pool)
7949251881Speter{
7950251881Speter  log_find_operative_subtree_baton_t *log_baton = baton;
7951251881Speter  apr_hash_index_t *hi;
7952251881Speter  apr_pool_t *iterpool;
7953251881Speter
7954251881Speter  /* It's possible that authz restrictions on the merge source prevent us
7955251881Speter     from knowing about any of the changes for LOG_ENTRY->REVISION. */
7956251881Speter  if (!log_entry->changed_paths2)
7957251881Speter    return SVN_NO_ERROR;
7958251881Speter
7959251881Speter  iterpool = svn_pool_create(pool);
7960251881Speter
7961251881Speter  for (hi = apr_hash_first(pool, log_entry->changed_paths2);
7962251881Speter       hi;
7963251881Speter       hi = apr_hash_next(hi))
7964251881Speter    {
7965251881Speter      const char *path = svn__apr_hash_index_key(hi);
7966251881Speter      svn_log_changed_path2_t *change = svn__apr_hash_index_val(hi);
7967251881Speter
7968251881Speter        {
7969251881Speter          const char *child;
7970251881Speter          const char *potential_child;
7971251881Speter          const char *rel_path =
7972251881Speter            svn_fspath__skip_ancestor(log_baton->merge_source_fspath, path);
7973251881Speter
7974251881Speter          /* Some affected paths might be the root of the merge source or
7975251881Speter             entirely outside our subtree of interest. In either case they
7976251881Speter             are not operative *immediate* children. */
7977251881Speter          if (rel_path == NULL
7978251881Speter              || rel_path[0] == '\0')
7979251881Speter            continue;
7980251881Speter
7981251881Speter          svn_pool_clear(iterpool);
7982251881Speter
7983251881Speter          child = svn_relpath_dirname(rel_path, iterpool);
7984251881Speter          if (child[0] == '\0')
7985251881Speter            {
7986251881Speter              /* The svn_log_changed_path2_t.node_kind members in
7987251881Speter                 LOG_ENTRY->CHANGED_PATHS2 may be set to
7988251881Speter                 svn_node_unknown, see svn_log_changed_path2_t and
7989251881Speter                 svn_fs_paths_changed2.  In that case we check the
7990251881Speter                 type of the corresponding subtree in the merge
7991251881Speter                 target. */
7992251881Speter              svn_node_kind_t node_kind;
7993251881Speter
7994251881Speter              if (change->node_kind == svn_node_unknown)
7995251881Speter                {
7996251881Speter                  const char *wc_child_abspath =
7997251881Speter                    svn_dirent_join(log_baton->merge_target_abspath,
7998251881Speter                                    rel_path, iterpool);
7999251881Speter
8000251881Speter                  SVN_ERR(svn_wc_read_kind2(&node_kind, log_baton->wc_ctx,
8001251881Speter                                            wc_child_abspath, FALSE, FALSE,
8002251881Speter                                            iterpool));
8003251881Speter                }
8004251881Speter              else
8005251881Speter                {
8006251881Speter                  node_kind = change->node_kind;
8007251881Speter                }
8008251881Speter
8009251881Speter              /* We only care about immediate directory children if
8010251881Speter                 DEPTH is svn_depth_files. */
8011251881Speter              if (log_baton->depth == svn_depth_files
8012251881Speter                  && node_kind != svn_node_dir)
8013251881Speter                continue;
8014251881Speter
8015251881Speter              /* If depth is svn_depth_immediates, then we only care
8016251881Speter                 about changes to proper subtrees of PATH.  If the change
8017251881Speter                 is to PATH itself then PATH is within the operational
8018251881Speter                 depth of the merge. */
8019251881Speter              if (log_baton->depth == svn_depth_immediates)
8020251881Speter                continue;
8021251881Speter
8022251881Speter              child = rel_path;
8023251881Speter            }
8024251881Speter
8025251881Speter          potential_child = svn_dirent_join(log_baton->merge_target_abspath,
8026251881Speter                                            child, iterpool);
8027251881Speter
8028251881Speter          if (change->action == 'A'
8029251881Speter              || !svn_hash_gets(log_baton->operative_children,
8030251881Speter                                potential_child))
8031251881Speter            {
8032251881Speter              svn_hash_sets(log_baton->operative_children,
8033251881Speter                            apr_pstrdup(log_baton->result_pool,
8034251881Speter                                        potential_child),
8035251881Speter                            apr_pstrdup(log_baton->result_pool, path));
8036251881Speter            }
8037251881Speter        }
8038251881Speter    }
8039251881Speter  svn_pool_destroy(iterpool);
8040251881Speter  return SVN_NO_ERROR;
8041251881Speter}
8042251881Speter
8043251881Speter/* Find immediate subtrees of MERGE_TARGET_ABSPATH which would have
8044251881Speter   additional differences applied if record_mergeinfo_for_dir_merge() were
8045251881Speter   recording mergeinfo describing a merge at svn_depth_infinity, rather
8046251881Speter   than at DEPTH (which is assumed to be shallow; if
8047251881Speter   DEPTH == svn_depth_infinity then this function does nothing beyond
8048251881Speter   setting *OPERATIVE_CHILDREN to an empty hash).
8049251881Speter
8050251881Speter   MERGE_SOURCE_FSPATH is the absolute repository path of the merge
8051251881Speter   source.  OLDEST_REV and YOUNGEST_REV are the revisions merged from
8052251881Speter   MERGE_SOURCE_FSPATH to MERGE_TARGET_ABSPATH.
8053251881Speter
8054251881Speter   RA_SESSION points to MERGE_SOURCE_FSPATH.
8055251881Speter
8056251881Speter   Set *OPERATIVE_CHILDREN to a hash (mapping const char * absolute
8057251881Speter   working copy paths to those path's const char * repos absolute paths)
8058251881Speter   containing all the immediate subtrees of MERGE_TARGET_ABSPATH which would
8059251881Speter   have a different diff applied if MERGE_SOURCE_FSPATH
8060251881Speter   -r(OLDEST_REV - 1):YOUNGEST_REV were merged to MERGE_TARGET_ABSPATH at
8061251881Speter   svn_depth_infinity rather than DEPTH.
8062251881Speter
8063251881Speter   RESULT_POOL is used to allocate the contents of *OPERATIVE_CHILDREN.
8064251881Speter   SCRATCH_POOL is used for temporary allocations. */
8065251881Speterstatic svn_error_t *
8066251881Speterget_operative_immediate_children(apr_hash_t **operative_children,
8067251881Speter                                 const char *merge_source_fspath,
8068251881Speter                                 svn_revnum_t oldest_rev,
8069251881Speter                                 svn_revnum_t youngest_rev,
8070251881Speter                                 const char *merge_target_abspath,
8071251881Speter                                 svn_depth_t depth,
8072251881Speter                                 svn_wc_context_t *wc_ctx,
8073251881Speter                                 svn_ra_session_t *ra_session,
8074251881Speter                                 apr_pool_t *result_pool,
8075251881Speter                                 apr_pool_t *scratch_pool)
8076251881Speter{
8077251881Speter  log_find_operative_subtree_baton_t log_baton;
8078251881Speter
8079251881Speter  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(oldest_rev));
8080251881Speter  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
8081251881Speter  SVN_ERR_ASSERT(oldest_rev <= youngest_rev);
8082251881Speter
8083251881Speter  *operative_children = apr_hash_make(result_pool);
8084251881Speter
8085251881Speter  if (depth == svn_depth_infinity)
8086251881Speter    return SVN_NO_ERROR;
8087251881Speter
8088251881Speter  /* Now remove any paths from *OPERATIVE_CHILDREN that are inoperative when
8089251881Speter     merging MERGE_SOURCE_REPOS_PATH -r(OLDEST_REV - 1):YOUNGEST_REV to
8090251881Speter     MERGE_TARGET_ABSPATH at --depth infinity. */
8091251881Speter  log_baton.operative_children = *operative_children;
8092251881Speter  log_baton.merge_source_fspath = merge_source_fspath;
8093251881Speter  log_baton.merge_target_abspath = merge_target_abspath;
8094251881Speter  log_baton.depth = depth;
8095251881Speter  log_baton.wc_ctx = wc_ctx;
8096251881Speter  log_baton.result_pool = result_pool;
8097251881Speter
8098251881Speter  SVN_ERR(get_log(ra_session, "", youngest_rev, oldest_rev,
8099251881Speter                  TRUE, /* discover_changed_paths */
8100251881Speter                  log_find_operative_subtree_revs,
8101251881Speter                  &log_baton, scratch_pool));
8102251881Speter
8103251881Speter  return SVN_NO_ERROR;
8104251881Speter}
8105251881Speter
8106251881Speter/* Helper for record_mergeinfo_for_dir_merge(): Identify which elements of
8107251881Speter   CHILDREN_WITH_MERGEINFO need new mergeinfo set to accurately
8108251881Speter   describe a merge, what inheritance type such new mergeinfo should have,
8109251881Speter   and what subtrees can be ignored altogether.
8110251881Speter
8111251881Speter   For each svn_client__merge_path_t CHILD in CHILDREN_WITH_MERGEINFO,
8112251881Speter   set CHILD->RECORD_MERGEINFO and CHILD->RECORD_NONINHERITABLE to true
8113251881Speter   if the subtree needs mergeinfo to describe the merge and if that
8114251881Speter   mergeinfo should be non-inheritable respectively.
8115251881Speter
8116251881Speter   If OPERATIVE_MERGE is true, then the merge being described is operative
8117251881Speter   as per subtree_touched_by_merge().  OPERATIVE_MERGE is false otherwise.
8118251881Speter
8119251881Speter   MERGED_RANGE, MERGEINFO_FSPATH, DEPTH, NOTIFY_B, and MERGE_B are all
8120251881Speter   cascaded from record_mergeinfo_for_dir_merge's arguments of the same
8121251881Speter   names.
8122251881Speter
8123251881Speter   SCRATCH_POOL is used for temporary allocations.
8124251881Speter*/
8125251881Speterstatic svn_error_t *
8126251881Speterflag_subtrees_needing_mergeinfo(svn_boolean_t operative_merge,
8127251881Speter                                const svn_merge_range_t *merged_range,
8128251881Speter                                apr_array_header_t *children_with_mergeinfo,
8129251881Speter                                const char *mergeinfo_fspath,
8130251881Speter                                svn_depth_t depth,
8131251881Speter                                merge_cmd_baton_t *merge_b,
8132251881Speter                                apr_pool_t *scratch_pool)
8133251881Speter{
8134251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
8135251881Speter  int i;
8136251881Speter  apr_hash_t *operative_immediate_children = NULL;
8137251881Speter
8138251881Speter  assert(! merge_b->dry_run);
8139251881Speter
8140251881Speter  if (!merge_b->record_only
8141251881Speter      && merged_range->start <= merged_range->end
8142251881Speter      && (depth < svn_depth_infinity))
8143251881Speter    SVN_ERR(get_operative_immediate_children(
8144251881Speter      &operative_immediate_children,
8145251881Speter      mergeinfo_fspath, merged_range->start + 1, merged_range->end,
8146251881Speter      merge_b->target->abspath, depth, merge_b->ctx->wc_ctx,
8147251881Speter      merge_b->ra_session1, scratch_pool, iterpool));
8148251881Speter
8149251881Speter  /* Issue #4056: Walk NOTIFY_B->CHILDREN_WITH_MERGEINFO reverse depth-first
8150251881Speter     order.  This way each child knows if it has operative missing/switched
8151251881Speter     children which necessitates non-inheritable mergeinfo. */
8152251881Speter  for (i = children_with_mergeinfo->nelts - 1; i >= 0; i--)
8153251881Speter    {
8154251881Speter      svn_client__merge_path_t *child =
8155251881Speter                     APR_ARRAY_IDX(children_with_mergeinfo, i,
8156251881Speter                                   svn_client__merge_path_t *);
8157251881Speter
8158251881Speter      /* Can't record mergeinfo on something that isn't here. */
8159251881Speter      if (child->absent)
8160251881Speter        continue;
8161251881Speter
8162251881Speter      /* Verify that remove_children_with_deleted_mergeinfo() did its job */
8163251881Speter      assert((i == 0)
8164251881Speter             ||! merge_b->paths_with_deleted_mergeinfo
8165251881Speter             || !svn_hash_gets(merge_b->paths_with_deleted_mergeinfo,
8166251881Speter                               child->abspath));
8167251881Speter
8168251881Speter      /* Don't record mergeinfo on skipped paths. */
8169251881Speter      if (svn_hash_gets(merge_b->skipped_abspaths, child->abspath))
8170251881Speter        continue;
8171251881Speter
8172251881Speter      /* ### ptb: Yes, we could combine the following into a single
8173251881Speter         ### conditional, but clarity would suffer (even more than
8174251881Speter         ### it does now). */
8175251881Speter      if (i == 0)
8176251881Speter        {
8177251881Speter          /* Always record mergeinfo on the merge target. */
8178251881Speter          child->record_mergeinfo = TRUE;
8179251881Speter        }
8180251881Speter      else if (merge_b->record_only && !merge_b->reintegrate_merge)
8181251881Speter        {
8182251881Speter          /* Always record mergeinfo for --record-only merges. */
8183251881Speter          child->record_mergeinfo = TRUE;
8184251881Speter        }
8185251881Speter      else if (child->immediate_child_dir
8186251881Speter               && !child->pre_merge_mergeinfo
8187251881Speter               && operative_immediate_children
8188251881Speter               && svn_hash_gets(operative_immediate_children, child->abspath))
8189251881Speter        {
8190251881Speter          /* We must record mergeinfo on those issue #3642 children
8191251881Speter             that are operative at a greater depth. */
8192251881Speter          child->record_mergeinfo = TRUE;
8193251881Speter        }
8194251881Speter
8195251881Speter      if (operative_merge
8196251881Speter          && subtree_touched_by_merge(child->abspath, merge_b, iterpool))
8197251881Speter        {
8198251881Speter          svn_pool_clear(iterpool);
8199251881Speter
8200251881Speter          /* This subtree was affected by the merge. */
8201251881Speter          child->record_mergeinfo = TRUE;
8202251881Speter
8203251881Speter          /* Were any CHILD's missing children skipped by the merge?
8204251881Speter             If not, then CHILD's missing children don't need to be
8205251881Speter             considered when recording mergeinfo describing the merge. */
8206251881Speter          if (! merge_b->reintegrate_merge
8207251881Speter              && child->missing_child
8208251881Speter              && !path_is_subtree(child->abspath,
8209251881Speter                                  merge_b->skipped_abspaths,
8210251881Speter                                  iterpool))
8211251881Speter            {
8212251881Speter              child->missing_child = FALSE;
8213251881Speter            }
8214251881Speter
8215251881Speter          /* If CHILD has an immediate switched child or children and
8216251881Speter             none of these were touched by the merge, then we don't need
8217251881Speter             need to do any special handling of those switched subtrees
8218251881Speter             (e.g. record non-inheritable mergeinfo) when recording
8219251881Speter             mergeinfo describing the merge. */
8220251881Speter          if (child->switched_child)
8221251881Speter            {
8222251881Speter              int j;
8223251881Speter              svn_boolean_t operative_switched_child = FALSE;
8224251881Speter
8225251881Speter              for (j = i + 1;
8226251881Speter                   j < children_with_mergeinfo->nelts;
8227251881Speter                   j++)
8228251881Speter                {
8229251881Speter                  svn_client__merge_path_t *potential_child =
8230251881Speter                    APR_ARRAY_IDX(children_with_mergeinfo, j,
8231251881Speter                                  svn_client__merge_path_t *);
8232251881Speter                  if (!svn_dirent_is_ancestor(child->abspath,
8233251881Speter                                              potential_child->abspath))
8234251881Speter                    break;
8235251881Speter
8236251881Speter                  /* POTENTIAL_CHILD is a subtree of CHILD, but is it
8237251881Speter                     an immediate child? */
8238251881Speter                  if (strcmp(child->abspath,
8239251881Speter                             svn_dirent_dirname(potential_child->abspath,
8240251881Speter                                                iterpool)))
8241251881Speter                    continue;
8242251881Speter
8243251881Speter                  if (potential_child->switched
8244251881Speter                      && potential_child->record_mergeinfo)
8245251881Speter                    {
8246251881Speter                      operative_switched_child = TRUE;
8247251881Speter                      break;
8248251881Speter                    }
8249251881Speter                }
8250251881Speter
8251251881Speter              /* Can we treat CHILD as if it has no switched children? */
8252251881Speter              if (! operative_switched_child)
8253251881Speter                child->switched_child = FALSE;
8254251881Speter            }
8255251881Speter        }
8256251881Speter
8257251881Speter      if (child->record_mergeinfo)
8258251881Speter        {
8259251881Speter          /* We need to record mergeinfo, but should that mergeinfo be
8260251881Speter             non-inheritable? */
8261251881Speter          svn_node_kind_t path_kind;
8262251881Speter          SVN_ERR(svn_wc_read_kind2(&path_kind, merge_b->ctx->wc_ctx,
8263251881Speter                                    child->abspath, FALSE, FALSE, iterpool));
8264251881Speter
8265251881Speter          /* Only directories can have non-inheritable mergeinfo. */
8266251881Speter          if (path_kind == svn_node_dir)
8267251881Speter            {
8268251881Speter              /* There are two general cases where non-inheritable mergeinfo
8269251881Speter                 is required:
8270251881Speter
8271251881Speter                 1) There merge target has missing subtrees (due to authz
8272251881Speter                    restrictions, switched subtrees, or a shallow working
8273251881Speter                    copy).
8274251881Speter
8275251881Speter                 2) The operational depth of the merge itself is shallow. */
8276251881Speter
8277251881Speter              /* We've already determined the first case. */
8278251881Speter              child->record_noninheritable =
8279251881Speter                child->missing_child || child->switched_child;
8280251881Speter
8281251881Speter              /* The second case requires a bit more work. */
8282251881Speter              if (i == 0)
8283251881Speter                {
8284251881Speter                  /* If CHILD is the root of the merge target and the
8285251881Speter                     operational depth is empty or files, then the mere
8286251881Speter                     existence of operative immediate children means we
8287251881Speter                     must record non-inheritable mergeinfo.
8288251881Speter
8289251881Speter                     ### What about svn_depth_immediates?  In that case
8290251881Speter                     ### the merge target needs only normal inheritable
8291251881Speter                     ### mergeinfo and the target's immediate children will
8292251881Speter                     ### get non-inheritable mergeinfo, assuming they
8293251881Speter                     ### need even that. */
8294251881Speter                  if (depth < svn_depth_immediates
8295251881Speter                      && operative_immediate_children
8296251881Speter                      && apr_hash_count(operative_immediate_children))
8297251881Speter                    child->record_noninheritable = TRUE;
8298251881Speter                }
8299251881Speter              else if (depth == svn_depth_immediates)
8300251881Speter                {
8301251881Speter                  /* An immediate directory child of the merge target, which
8302251881Speter                      was affected by a --depth=immediates merge, needs
8303251881Speter                      non-inheritable mergeinfo. */
8304251881Speter                  if (svn_hash_gets(operative_immediate_children,
8305251881Speter                                    child->abspath))
8306251881Speter                    child->record_noninheritable = TRUE;
8307251881Speter                }
8308251881Speter            }
8309251881Speter        }
8310251881Speter      else /* child->record_mergeinfo */
8311251881Speter        {
8312251881Speter          /* If CHILD is in NOTIFY_B->CHILDREN_WITH_MERGEINFO simply
8313251881Speter             because it had no explicit mergeinfo of its own at the
8314251881Speter             start of the merge but is the child of of some path with
8315251881Speter             non-inheritable mergeinfo, then the explicit mergeinfo it
8316251881Speter             has *now* was set by get_mergeinfo_paths() -- see criteria
8317251881Speter             3 in that function's doc string.  So since CHILD->ABSPATH
8318251881Speter             was not touched by the merge we can remove the
8319251881Speter             mergeinfo. */
8320251881Speter          if (child->child_of_noninheritable)
8321251881Speter            SVN_ERR(svn_client__record_wc_mergeinfo(child->abspath,
8322251881Speter                                                    NULL, FALSE,
8323251881Speter                                                    merge_b->ctx,
8324251881Speter                                                    iterpool));
8325251881Speter        }
8326251881Speter    }
8327251881Speter
8328251881Speter  svn_pool_destroy(iterpool);
8329251881Speter  return SVN_NO_ERROR;
8330251881Speter}
8331251881Speter
8332251881Speter/* Helper for do_directory_merge().
8333251881Speter
8334251881Speter   If RESULT_CATALOG is NULL then record mergeinfo describing a merge of
8335251881Speter   MERGED_RANGE->START:MERGED_RANGE->END from the repository relative path
8336251881Speter   MERGEINFO_FSPATH to the merge target (and possibly its subtrees) described
8337251881Speter   by NOTIFY_B->CHILDREN_WITH_MERGEINFO -- see the global comment
8338251881Speter   'THE CHILDREN_WITH_MERGEINFO ARRAY'.  Obviously this should only
8339251881Speter   be called if recording mergeinfo -- see doc string for RECORD_MERGEINFO().
8340251881Speter
8341251881Speter   If RESULT_CATALOG is not NULL, then don't record the new mergeinfo on the
8342251881Speter   WC, but instead record it in RESULT_CATALOG, where the keys are absolute
8343251881Speter   working copy paths and the values are the new mergeinfos for each.
8344251881Speter   Allocate additions to RESULT_CATALOG in pool which RESULT_CATALOG was
8345251881Speter   created in.
8346251881Speter
8347251881Speter   DEPTH, NOTIFY_B, MERGE_B, and SQUELCH_MERGEINFO_NOTIFICATIONS are all
8348251881Speter   cascaded from do_directory_merge's arguments of the same names.
8349251881Speter
8350251881Speter   SCRATCH_POOL is used for temporary allocations.
8351251881Speter*/
8352251881Speterstatic svn_error_t *
8353251881Speterrecord_mergeinfo_for_dir_merge(svn_mergeinfo_catalog_t result_catalog,
8354251881Speter                               const svn_merge_range_t *merged_range,
8355251881Speter                               const char *mergeinfo_fspath,
8356251881Speter                               apr_array_header_t *children_with_mergeinfo,
8357251881Speter                               svn_depth_t depth,
8358251881Speter                               svn_boolean_t squelch_mergeinfo_notifications,
8359251881Speter                               merge_cmd_baton_t *merge_b,
8360251881Speter                               apr_pool_t *scratch_pool)
8361251881Speter{
8362251881Speter  int i;
8363251881Speter  svn_boolean_t is_rollback = (merged_range->start > merged_range->end);
8364251881Speter  svn_boolean_t operative_merge;
8365251881Speter
8366251881Speter  /* Update the WC mergeinfo here to account for our new
8367251881Speter     merges, minus any unresolved conflicts and skips. */
8368251881Speter
8369251881Speter  /* We need a scratch pool for iterations below. */
8370251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
8371251881Speter
8372251881Speter  svn_merge_range_t range = *merged_range;
8373251881Speter
8374251881Speter  assert(! merge_b->dry_run);
8375251881Speter
8376251881Speter  /* Regardless of what subtrees in MERGE_B->target->abspath might be missing
8377251881Speter     could this merge have been operative? */
8378251881Speter  operative_merge = subtree_touched_by_merge(merge_b->target->abspath,
8379251881Speter                                             merge_b, iterpool);
8380251881Speter
8381251881Speter  /* If this couldn't be an operative merge then don't bother with
8382251881Speter     the added complexity (and user confusion) of non-inheritable ranges.
8383251881Speter     There is no harm in subtrees inheriting inoperative mergeinfo. */
8384251881Speter  if (!operative_merge)
8385251881Speter    range.inheritable = TRUE;
8386251881Speter
8387251881Speter  /* Remove absent children at or under MERGE_B->target->abspath from
8388251881Speter     NOTIFY_B->CHILDREN_WITH_MERGEINFO
8389251881Speter     before we calculate the merges performed. */
8390251881Speter  remove_absent_children(merge_b->target->abspath,
8391251881Speter                         children_with_mergeinfo);
8392251881Speter
8393251881Speter  /* Determine which subtrees of interest need mergeinfo recorded... */
8394251881Speter  SVN_ERR(flag_subtrees_needing_mergeinfo(operative_merge, &range,
8395251881Speter                                          children_with_mergeinfo,
8396251881Speter                                          mergeinfo_fspath, depth,
8397251881Speter                                          merge_b, iterpool));
8398251881Speter
8399251881Speter  /* ...and then record it. */
8400251881Speter  for (i = 0; i < children_with_mergeinfo->nelts; i++)
8401251881Speter    {
8402251881Speter      const char *child_repos_path;
8403251881Speter      const char *child_merge_src_fspath;
8404251881Speter      svn_rangelist_t *child_merge_rangelist;
8405251881Speter      apr_hash_t *child_merges;
8406251881Speter      svn_client__merge_path_t *child =
8407251881Speter                     APR_ARRAY_IDX(children_with_mergeinfo, i,
8408251881Speter                                   svn_client__merge_path_t *);
8409251881Speter      SVN_ERR_ASSERT(child);
8410251881Speter
8411251881Speter      svn_pool_clear(iterpool);
8412251881Speter
8413251881Speter      if (child->record_mergeinfo)
8414251881Speter        {
8415251881Speter          child_repos_path = svn_dirent_skip_ancestor(merge_b->target->abspath,
8416251881Speter                                                      child->abspath);
8417251881Speter          SVN_ERR_ASSERT(child_repos_path != NULL);
8418251881Speter          child_merge_src_fspath = svn_fspath__join(mergeinfo_fspath,
8419251881Speter                                                    child_repos_path,
8420251881Speter                                                    iterpool);
8421251881Speter          /* Filter any ranges from each child's natural history before
8422251881Speter             setting mergeinfo describing the merge. */
8423251881Speter          SVN_ERR(filter_natural_history_from_mergeinfo(
8424251881Speter            &child_merge_rangelist, child_merge_src_fspath,
8425251881Speter            child->implicit_mergeinfo, &range, iterpool));
8426251881Speter
8427251881Speter          if (child_merge_rangelist->nelts == 0)
8428251881Speter            continue;
8429251881Speter
8430251881Speter          if (!squelch_mergeinfo_notifications)
8431251881Speter            {
8432251881Speter              /* If the merge source has a gap, then don't mention
8433251881Speter                 those gap revisions in the notification. */
8434251881Speter              remove_source_gap(&range, merge_b->implicit_src_gap);
8435251881Speter              notify_mergeinfo_recording(child->abspath, &range,
8436251881Speter                                         merge_b->ctx, iterpool);
8437251881Speter            }
8438251881Speter
8439251881Speter          /* If we are here we know we will be recording some mergeinfo, but
8440251881Speter             before we do, set override mergeinfo on skipped paths so they
8441251881Speter             don't incorrectly inherit the mergeinfo we are about to set. */
8442251881Speter          if (i == 0)
8443251881Speter            SVN_ERR(record_skips_in_mergeinfo(mergeinfo_fspath,
8444251881Speter                                              child_merge_rangelist,
8445251881Speter                                              is_rollback, merge_b, iterpool));
8446251881Speter
8447251881Speter          /* We may need to record non-inheritable mergeinfo that applies
8448251881Speter             only to CHILD->ABSPATH. */
8449251881Speter          if (child->record_noninheritable)
8450251881Speter            svn_rangelist__set_inheritance(child_merge_rangelist, FALSE);
8451251881Speter
8452251881Speter          /* If CHILD has inherited mergeinfo set it before
8453251881Speter             recording the first merge range. */
8454251881Speter          if (child->inherited_mergeinfo)
8455251881Speter            SVN_ERR(svn_client__record_wc_mergeinfo(
8456251881Speter              child->abspath,
8457251881Speter              child->pre_merge_mergeinfo,
8458251881Speter              FALSE, merge_b->ctx,
8459251881Speter              iterpool));
8460251881Speter          if (merge_b->implicit_src_gap)
8461251881Speter            {
8462251881Speter              /* If this is a reverse merge reorder CHILD->REMAINING_RANGES
8463251881Speter                 so it will work with the svn_rangelist_remove API. */
8464251881Speter              if (is_rollback)
8465251881Speter                SVN_ERR(svn_rangelist_reverse(child_merge_rangelist,
8466251881Speter                                              iterpool));
8467251881Speter
8468251881Speter              SVN_ERR(svn_rangelist_remove(&child_merge_rangelist,
8469251881Speter                                           merge_b->implicit_src_gap,
8470251881Speter                                           child_merge_rangelist, FALSE,
8471251881Speter                                           iterpool));
8472251881Speter              if (is_rollback)
8473251881Speter                SVN_ERR(svn_rangelist_reverse(child_merge_rangelist,
8474251881Speter                                              iterpool));
8475251881Speter            }
8476251881Speter
8477251881Speter          child_merges = apr_hash_make(iterpool);
8478251881Speter
8479251881Speter          /* The short story:
8480251881Speter
8481251881Speter             If we are describing a forward merge, then the naive mergeinfo
8482251881Speter             defined by MERGE_SOURCE_PATH:MERGED_RANGE->START:
8483251881Speter             MERGE_SOURCE_PATH:MERGED_RANGE->END may contain non-existent
8484251881Speter             path-revs or may describe other lines of history.  We must
8485251881Speter             remove these invalid portion(s) before recording mergeinfo
8486251881Speter             describing the merge.
8487251881Speter
8488251881Speter             The long story:
8489251881Speter
8490251881Speter             If CHILD is the merge target we know that
8491251881Speter             MERGE_SOURCE_PATH:MERGED_RANGE->END exists.  Further, if there
8492251881Speter             were no copies in MERGE_SOURCE_PATH's history going back to
8493251881Speter             RANGE->START then we know that
8494251881Speter             MERGE_SOURCE_PATH:MERGED_RANGE->START exists too and the two
8495251881Speter             describe an unbroken line of history, and thus
8496251881Speter             MERGE_SOURCE_PATH:MERGED_RANGE->START:
8497251881Speter             MERGE_SOURCE_PATH:MERGED_RANGE->END is a valid description of
8498251881Speter             the merge -- see normalize_merge_sources() and the global comment
8499251881Speter             'MERGEINFO MERGE SOURCE NORMALIZATION'.
8500251881Speter
8501251881Speter             However, if there *was* a copy, then
8502251881Speter             MERGE_SOURCE_PATH:MERGED_RANGE->START doesn't exist or is
8503251881Speter             unrelated to MERGE_SOURCE_PATH:MERGED_RANGE->END.  Also, we
8504251881Speter             don't know if (MERGE_SOURCE_PATH:MERGED_RANGE->START)+1 through
8505251881Speter             (MERGE_SOURCE_PATH:MERGED_RANGE->END)-1 actually exist.
8506251881Speter
8507251881Speter             If CHILD is a subtree of the merge target, then nothing is
8508251881Speter             guaranteed beyond the fact that MERGE_SOURCE_PATH exists at
8509251881Speter             MERGED_RANGE->END. */
8510251881Speter          if ((!merge_b->record_only || merge_b->reintegrate_merge)
8511251881Speter              && (!is_rollback))
8512251881Speter            {
8513251881Speter              svn_error_t *err;
8514251881Speter              svn_mergeinfo_t subtree_history_as_mergeinfo;
8515251881Speter              svn_rangelist_t *child_merge_src_rangelist;
8516251881Speter              svn_client__pathrev_t *subtree_mergeinfo_pathrev
8517251881Speter                = svn_client__pathrev_create_with_relpath(
8518251881Speter                    merge_b->target->loc.repos_root_url,
8519251881Speter                    merge_b->target->loc.repos_uuid,
8520251881Speter                    merged_range->end, child_merge_src_fspath + 1,
8521251881Speter                    iterpool);
8522251881Speter
8523251881Speter              /* Confirm that the naive mergeinfo we want to set on
8524251881Speter                 CHILD->ABSPATH both exists and is part of
8525251881Speter                 (MERGE_SOURCE_PATH+CHILD_REPOS_PATH)@MERGED_RANGE->END's
8526251881Speter                 history. */
8527251881Speter              /* We know MERGED_RANGE->END is younger than MERGE_RANGE->START
8528251881Speter                 because we only do this for forward merges. */
8529251881Speter              err = svn_client__get_history_as_mergeinfo(
8530251881Speter                &subtree_history_as_mergeinfo, NULL,
8531251881Speter                subtree_mergeinfo_pathrev,
8532251881Speter                merged_range->end, merged_range->start,
8533251881Speter                merge_b->ra_session2, merge_b->ctx, iterpool);
8534251881Speter
8535251881Speter              /* If CHILD is a subtree it may have been deleted prior to
8536251881Speter                 MERGED_RANGE->END so the above call to get its history
8537251881Speter                 will fail. */
8538251881Speter              if (err)
8539251881Speter                {
8540251881Speter                  if (err->apr_err != SVN_ERR_FS_NOT_FOUND)
8541251881Speter                      return svn_error_trace(err);
8542251881Speter                  svn_error_clear(err);
8543251881Speter                }
8544251881Speter              else
8545251881Speter                {
8546251881Speter                  child_merge_src_rangelist = svn_hash_gets(
8547251881Speter                                                subtree_history_as_mergeinfo,
8548251881Speter                                                child_merge_src_fspath);
8549251881Speter                  SVN_ERR(svn_rangelist_intersect(&child_merge_rangelist,
8550251881Speter                                                  child_merge_rangelist,
8551251881Speter                                                  child_merge_src_rangelist,
8552251881Speter                                                  FALSE, iterpool));
8553251881Speter                  if (child->record_noninheritable)
8554251881Speter                    svn_rangelist__set_inheritance(child_merge_rangelist,
8555251881Speter                                                   FALSE);
8556251881Speter                }
8557251881Speter            }
8558251881Speter
8559251881Speter          svn_hash_sets(child_merges, child->abspath, child_merge_rangelist);
8560251881Speter          SVN_ERR(update_wc_mergeinfo(result_catalog,
8561251881Speter                                      child->abspath,
8562251881Speter                                      child_merge_src_fspath,
8563251881Speter                                      child_merges, is_rollback,
8564251881Speter                                      merge_b->ctx, iterpool));
8565251881Speter
8566251881Speter          /* Once is enough: We don't need to record mergeinfo describing
8567251881Speter             the merge a second.  If CHILD->ABSPATH is in
8568251881Speter             MERGE_B->ADDED_ABSPATHS, we'll do just that, so remove the
8569251881Speter             former from the latter. */
8570251881Speter          svn_hash_sets(merge_b->added_abspaths, child->abspath, NULL);
8571251881Speter        }
8572251881Speter
8573251881Speter      /* Elide explicit subtree mergeinfo whether or not we updated it. */
8574251881Speter      if (i > 0)
8575251881Speter        {
8576251881Speter          svn_boolean_t in_switched_subtree = FALSE;
8577251881Speter
8578251881Speter          if (child->switched)
8579251881Speter            in_switched_subtree = TRUE;
8580251881Speter          else if (i > 1)
8581251881Speter            {
8582251881Speter              /* Check if CHILD is part of a switched subtree */
8583251881Speter              svn_client__merge_path_t *parent;
8584251881Speter              int j = i - 1;
8585251881Speter              for (; j > 0; j--)
8586251881Speter                {
8587251881Speter                  parent = APR_ARRAY_IDX(children_with_mergeinfo,
8588251881Speter                                         j, svn_client__merge_path_t *);
8589251881Speter                  if (parent
8590251881Speter                      && parent->switched
8591251881Speter                      && svn_dirent_is_ancestor(parent->abspath,
8592251881Speter                                                child->abspath))
8593251881Speter                    {
8594251881Speter                      in_switched_subtree = TRUE;
8595251881Speter                      break;
8596251881Speter                    }
8597251881Speter                }
8598251881Speter            }
8599251881Speter
8600251881Speter          /* Allow mergeinfo on switched subtrees to elide to the
8601251881Speter             repository. Otherwise limit elision to the merge target
8602251881Speter             for now.  do_directory_merge() will eventually try to
8603251881Speter             elide that when the merge is complete. */
8604251881Speter          SVN_ERR(svn_client__elide_mergeinfo(
8605251881Speter            child->abspath,
8606251881Speter            in_switched_subtree ? NULL : merge_b->target->abspath,
8607251881Speter            merge_b->ctx, iterpool));
8608251881Speter        }
8609251881Speter    } /* (i = 0; i < notify_b->children_with_mergeinfo->nelts; i++) */
8610251881Speter
8611251881Speter  svn_pool_destroy(iterpool);
8612251881Speter  return SVN_NO_ERROR;
8613251881Speter}
8614251881Speter
8615251881Speter/* Helper for do_directory_merge().
8616251881Speter
8617251881Speter   Record mergeinfo describing a merge of
8618251881Speter   MERGED_RANGE->START:MERGED_RANGE->END from the repository relative path
8619251881Speter   MERGEINFO_FSPATH to each path in ADDED_ABSPATHS which has explicit
8620251881Speter   mergeinfo or is the immediate child of a parent with explicit
8621251881Speter   non-inheritable mergeinfo.
8622251881Speter
8623251881Speter   DEPTH, MERGE_B, and SQUELCH_MERGEINFO_NOTIFICATIONS, are
8624251881Speter   cascaded from do_directory_merge's arguments of the same names.
8625251881Speter
8626251881Speter   Note: This is intended to support forward merges only, i.e.
8627251881Speter   MERGED_RANGE->START must be older than MERGED_RANGE->END.
8628251881Speter*/
8629251881Speterstatic svn_error_t *
8630251881Speterrecord_mergeinfo_for_added_subtrees(
8631251881Speter  svn_merge_range_t *merged_range,
8632251881Speter  const char *mergeinfo_fspath,
8633251881Speter  svn_depth_t depth,
8634251881Speter  svn_boolean_t squelch_mergeinfo_notifications,
8635251881Speter  apr_hash_t *added_abspaths,
8636251881Speter  merge_cmd_baton_t *merge_b,
8637251881Speter  apr_pool_t *pool)
8638251881Speter{
8639251881Speter  apr_pool_t *iterpool;
8640251881Speter  apr_hash_index_t *hi;
8641251881Speter
8642251881Speter  /* If no paths were added by the merge then we have nothing to do. */
8643251881Speter  if (!added_abspaths)
8644251881Speter    return SVN_NO_ERROR;
8645251881Speter
8646251881Speter  SVN_ERR_ASSERT(merged_range->start < merged_range->end);
8647251881Speter
8648251881Speter  iterpool = svn_pool_create(pool);
8649251881Speter  for (hi = apr_hash_first(pool, added_abspaths); hi; hi = apr_hash_next(hi))
8650251881Speter    {
8651251881Speter      const char *added_abspath = svn__apr_hash_index_key(hi);
8652251881Speter      const char *dir_abspath;
8653251881Speter      svn_mergeinfo_t parent_mergeinfo;
8654251881Speter      svn_mergeinfo_t added_path_mergeinfo;
8655251881Speter
8656251881Speter      svn_pool_clear(iterpool);
8657251881Speter      dir_abspath = svn_dirent_dirname(added_abspath, iterpool);
8658251881Speter
8659251881Speter      /* Grab the added path's explicit mergeinfo. */
8660251881Speter      SVN_ERR(svn_client__get_wc_mergeinfo(&added_path_mergeinfo, NULL,
8661251881Speter                                           svn_mergeinfo_explicit,
8662251881Speter                                           added_abspath, NULL, NULL, FALSE,
8663251881Speter                                           merge_b->ctx, iterpool, iterpool));
8664251881Speter
8665251881Speter      /* If the added path doesn't have explicit mergeinfo, does its immediate
8666251881Speter         parent have non-inheritable mergeinfo? */
8667251881Speter      if (!added_path_mergeinfo)
8668251881Speter        SVN_ERR(svn_client__get_wc_mergeinfo(&parent_mergeinfo, NULL,
8669251881Speter                                             svn_mergeinfo_explicit,
8670251881Speter                                             dir_abspath, NULL, NULL, FALSE,
8671251881Speter                                             merge_b->ctx,
8672251881Speter                                             iterpool, iterpool));
8673251881Speter
8674251881Speter      if (added_path_mergeinfo
8675251881Speter          || svn_mergeinfo__is_noninheritable(parent_mergeinfo, iterpool))
8676251881Speter        {
8677251881Speter          svn_node_kind_t added_path_kind;
8678251881Speter          svn_mergeinfo_t merge_mergeinfo;
8679251881Speter          svn_mergeinfo_t adds_history_as_mergeinfo;
8680251881Speter          svn_rangelist_t *rangelist;
8681251881Speter          const char *rel_added_path;
8682251881Speter          const char *added_path_mergeinfo_fspath;
8683251881Speter          svn_client__pathrev_t *added_path_pathrev;
8684251881Speter
8685251881Speter          SVN_ERR(svn_wc_read_kind2(&added_path_kind, merge_b->ctx->wc_ctx,
8686251881Speter                                    added_abspath, FALSE, FALSE, iterpool));
8687251881Speter
8688251881Speter          /* Calculate the naive mergeinfo describing the merge. */
8689251881Speter          merge_mergeinfo = apr_hash_make(iterpool);
8690251881Speter          rangelist = svn_rangelist__initialize(
8691251881Speter                        merged_range->start, merged_range->end,
8692251881Speter                        ((added_path_kind == svn_node_file)
8693251881Speter                         || (!(depth == svn_depth_infinity
8694251881Speter                               || depth == svn_depth_immediates))),
8695251881Speter                        iterpool);
8696251881Speter
8697251881Speter          /* Create the new mergeinfo path for added_path's mergeinfo.
8698251881Speter             (added_abspath had better be a child of MERGE_B->target->abspath
8699251881Speter             or something is *really* wrong.) */
8700251881Speter          rel_added_path = svn_dirent_is_child(merge_b->target->abspath,
8701251881Speter                                               added_abspath, iterpool);
8702251881Speter          SVN_ERR_ASSERT(rel_added_path);
8703251881Speter          added_path_mergeinfo_fspath = svn_fspath__join(mergeinfo_fspath,
8704251881Speter                                                         rel_added_path,
8705251881Speter                                                         iterpool);
8706251881Speter          svn_hash_sets(merge_mergeinfo, added_path_mergeinfo_fspath,
8707251881Speter                        rangelist);
8708251881Speter
8709251881Speter          /* Don't add new mergeinfo to describe the merge if that mergeinfo
8710251881Speter             contains non-existent merge sources.
8711251881Speter
8712251881Speter             We know that MERGEINFO_PATH/rel_added_path's history does not
8713251881Speter             span MERGED_RANGE->START:MERGED_RANGE->END but rather that it
8714251881Speter             was added at some revions greater than MERGED_RANGE->START
8715251881Speter             (assuming this is a forward merge).  It may have been added,
8716251881Speter             deleted, and re-added many times.  The point is that we cannot
8717251881Speter             blindly apply the naive mergeinfo calculated above because it
8718251881Speter             will describe non-existent merge sources. To avoid this we get
8719251881Speter             take the intersection of the naive mergeinfo with
8720251881Speter             MERGEINFO_PATH/rel_added_path's history. */
8721251881Speter          added_path_pathrev = svn_client__pathrev_create_with_relpath(
8722251881Speter                                 merge_b->target->loc.repos_root_url,
8723251881Speter                                 merge_b->target->loc.repos_uuid,
8724251881Speter                                 MAX(merged_range->start, merged_range->end),
8725251881Speter                                 added_path_mergeinfo_fspath + 1, iterpool);
8726251881Speter          SVN_ERR(svn_client__get_history_as_mergeinfo(
8727251881Speter            &adds_history_as_mergeinfo, NULL,
8728251881Speter            added_path_pathrev,
8729251881Speter            MAX(merged_range->start, merged_range->end),
8730251881Speter            MIN(merged_range->start, merged_range->end),
8731251881Speter            merge_b->ra_session2, merge_b->ctx, iterpool));
8732251881Speter
8733251881Speter          SVN_ERR(svn_mergeinfo_intersect2(&merge_mergeinfo,
8734251881Speter                                           merge_mergeinfo,
8735251881Speter                                           adds_history_as_mergeinfo,
8736251881Speter                                           FALSE, iterpool, iterpool));
8737251881Speter
8738251881Speter          /* Combine the explicit mergeinfo on the added path (if any)
8739251881Speter             with the mergeinfo describing this merge. */
8740251881Speter          if (added_path_mergeinfo)
8741251881Speter            SVN_ERR(svn_mergeinfo_merge2(merge_mergeinfo,
8742251881Speter                                         added_path_mergeinfo,
8743251881Speter                                         iterpool, iterpool));
8744251881Speter          SVN_ERR(svn_client__record_wc_mergeinfo(
8745251881Speter            added_abspath, merge_mergeinfo,
8746251881Speter            !squelch_mergeinfo_notifications, merge_b->ctx, iterpool));
8747251881Speter        }
8748251881Speter    }
8749251881Speter  svn_pool_destroy(iterpool);
8750251881Speter
8751251881Speter  return SVN_NO_ERROR;
8752251881Speter}
8753251881Speter/* Baton structure for log_noop_revs. */
8754251881Spetertypedef struct log_noop_baton_t
8755251881Speter{
8756251881Speter  /* See the comment 'THE CHILDREN_WITH_MERGEINFO ARRAY' at the start
8757251881Speter     of this file.*/
8758251881Speter  apr_array_header_t *children_with_mergeinfo;
8759251881Speter
8760251881Speter  /* Absolute repository path of younger of the two merge sources
8761251881Speter     being diffed. */
8762251881Speter  const char *source_fspath;
8763251881Speter
8764251881Speter  /* The merge target. */
8765251881Speter  const merge_target_t *target;
8766251881Speter
8767251881Speter  /* Initially empty rangelists allocated in POOL. The rangelists are
8768251881Speter   * populated across multiple invocations of log_noop_revs(). */
8769251881Speter  svn_rangelist_t *operative_ranges;
8770251881Speter  svn_rangelist_t *merged_ranges;
8771251881Speter
8772251881Speter  /* Pool to store the rangelists. */
8773251881Speter  apr_pool_t *pool;
8774251881Speter} log_noop_baton_t;
8775251881Speter
8776251881Speter/* Helper for log_noop_revs: Merge a svn_merge_range_t representation of
8777251881Speter   REVISION into RANGELIST. New elements added to rangelist are allocated
8778251881Speter   in RESULT_POOL.
8779251881Speter
8780251881Speter   This is *not* a general purpose rangelist merge but a special replacement
8781251881Speter   for svn_rangelist_merge when REVISION is guaranteed to be younger than any
8782251881Speter   element in RANGELIST.  svn_rangelist_merge is O(n) worst-case (i.e. when
8783251881Speter   all the ranges in output rangelist are older than the incoming changes).
8784251881Speter   This turns the special case of a single incoming younger range into O(1).
8785251881Speter   */
8786251881Speterstatic svn_error_t *
8787251881Speterrangelist_merge_revision(svn_rangelist_t *rangelist,
8788251881Speter                         svn_revnum_t revision,
8789251881Speter                         apr_pool_t *result_pool)
8790251881Speter{
8791251881Speter  svn_merge_range_t *new_range;
8792251881Speter  if (rangelist->nelts)
8793251881Speter    {
8794251881Speter      svn_merge_range_t *range = APR_ARRAY_IDX(rangelist, rangelist->nelts - 1,
8795251881Speter                                               svn_merge_range_t *);
8796251881Speter      if (range->end == revision - 1)
8797251881Speter        {
8798251881Speter          /* REVISION is adjacent to the youngest range in RANGELIST
8799251881Speter             so we can simply expand that range to encompass REVISION. */
8800251881Speter          range->end = revision;
8801251881Speter          return SVN_NO_ERROR;
8802251881Speter        }
8803251881Speter    }
8804251881Speter  new_range = apr_palloc(result_pool, sizeof(*new_range));
8805251881Speter  new_range->start = revision - 1;
8806251881Speter  new_range->end = revision;
8807251881Speter  new_range->inheritable = TRUE;
8808251881Speter
8809251881Speter  APR_ARRAY_PUSH(rangelist, svn_merge_range_t *) = new_range;
8810251881Speter
8811251881Speter  return SVN_NO_ERROR;
8812251881Speter}
8813251881Speter
8814251881Speter/* Implements the svn_log_entry_receiver_t interface.
8815251881Speter
8816251881Speter   BATON is an log_noop_baton_t *.
8817251881Speter
8818251881Speter   Add LOG_ENTRY->REVISION to BATON->OPERATIVE_RANGES.
8819251881Speter
8820251881Speter   If LOG_ENTRY->REVISION has already been fully merged to
8821251881Speter   BATON->target->abspath per the mergeinfo in BATON->CHILDREN_WITH_MERGEINFO,
8822251881Speter   then add LOG_ENTRY->REVISION to BATON->MERGED_RANGES.
8823251881Speter
8824251881Speter   Use SCRATCH_POOL for temporary allocations.  Allocate additions to
8825251881Speter   BATON->MERGED_RANGES and BATON->OPERATIVE_RANGES in BATON->POOL.
8826251881Speter
8827251881Speter   Note: This callback must be invoked from oldest LOG_ENTRY->REVISION
8828251881Speter   to youngest LOG_ENTRY->REVISION -- see rangelist_merge_revision().
8829251881Speter*/
8830251881Speterstatic svn_error_t *
8831251881Speterlog_noop_revs(void *baton,
8832251881Speter              svn_log_entry_t *log_entry,
8833251881Speter              apr_pool_t *scratch_pool)
8834251881Speter{
8835251881Speter  log_noop_baton_t *log_gap_baton = baton;
8836251881Speter  apr_hash_index_t *hi;
8837251881Speter  svn_revnum_t revision;
8838251881Speter  svn_boolean_t log_entry_rev_required = FALSE;
8839251881Speter
8840251881Speter  revision = log_entry->revision;
8841251881Speter
8842251881Speter  /* It's possible that authz restrictions on the merge source prevent us
8843251881Speter     from knowing about any of the changes for LOG_ENTRY->REVISION. */
8844251881Speter  if (!log_entry->changed_paths2)
8845251881Speter    return SVN_NO_ERROR;
8846251881Speter
8847251881Speter  /* Unconditionally add LOG_ENTRY->REVISION to BATON->OPERATIVE_MERGES. */
8848251881Speter  SVN_ERR(rangelist_merge_revision(log_gap_baton->operative_ranges,
8849251881Speter                                   revision,
8850251881Speter                                   log_gap_baton->pool));
8851251881Speter
8852251881Speter  /* Examine each path affected by LOG_ENTRY->REVISION.  If the explicit or
8853251881Speter     inherited mergeinfo for *all* of the corresponding paths under
8854251881Speter     BATON->target->abspath reflects that LOG_ENTRY->REVISION has been
8855251881Speter     merged, then add LOG_ENTRY->REVISION to BATON->MERGED_RANGES. */
8856251881Speter  for (hi = apr_hash_first(scratch_pool, log_entry->changed_paths2);
8857251881Speter       hi;
8858251881Speter       hi = apr_hash_next(hi))
8859251881Speter    {
8860251881Speter      const char *fspath = svn__apr_hash_index_key(hi);
8861251881Speter      const char *rel_path;
8862251881Speter      const char *cwmi_abspath;
8863251881Speter      svn_rangelist_t *paths_explicit_rangelist = NULL;
8864251881Speter      svn_boolean_t mergeinfo_inherited = FALSE;
8865251881Speter
8866251881Speter      /* Adjust REL_PATH so it is relative to the merge source then use it to
8867251881Speter         calculate what path in the merge target would be affected by this
8868251881Speter         revision. */
8869251881Speter      rel_path = svn_fspath__skip_ancestor(log_gap_baton->source_fspath,
8870251881Speter                                           fspath);
8871251881Speter      /* Is PATH even within the merge target?  If it isn't we
8872251881Speter         can disregard it altogether. */
8873251881Speter      if (rel_path == NULL)
8874251881Speter        continue;
8875251881Speter      cwmi_abspath = svn_dirent_join(log_gap_baton->target->abspath,
8876251881Speter                                     rel_path, scratch_pool);
8877251881Speter
8878251881Speter      /* Find any explicit or inherited mergeinfo for PATH. */
8879251881Speter      while (!log_entry_rev_required)
8880251881Speter        {
8881251881Speter          svn_client__merge_path_t *child = get_child_with_mergeinfo(
8882251881Speter            log_gap_baton->children_with_mergeinfo, cwmi_abspath);
8883251881Speter
8884251881Speter          if (child && child->pre_merge_mergeinfo)
8885251881Speter            {
8886251881Speter              /* Found some explicit mergeinfo, grab any ranges
8887251881Speter                 for PATH. */
8888251881Speter              paths_explicit_rangelist =
8889251881Speter                            svn_hash_gets(child->pre_merge_mergeinfo, fspath);
8890251881Speter              break;
8891251881Speter            }
8892251881Speter
8893251881Speter          if (cwmi_abspath[0] == '\0'
8894251881Speter              || svn_dirent_is_root(cwmi_abspath, strlen(cwmi_abspath))
8895251881Speter              || strcmp(log_gap_baton->target->abspath, cwmi_abspath) == 0)
8896251881Speter            {
8897251881Speter              /* Can't crawl any higher. */
8898251881Speter              break;
8899251881Speter            }
8900251881Speter
8901251881Speter          /* Didn't find anything so crawl up to the parent. */
8902251881Speter          cwmi_abspath = svn_dirent_dirname(cwmi_abspath, scratch_pool);
8903251881Speter          fspath = svn_fspath__dirname(fspath, scratch_pool);
8904251881Speter
8905251881Speter          /* At this point *if* we find mergeinfo it will be inherited. */
8906251881Speter          mergeinfo_inherited = TRUE;
8907251881Speter        }
8908251881Speter
8909251881Speter      if (paths_explicit_rangelist)
8910251881Speter        {
8911251881Speter          svn_rangelist_t *intersecting_range;
8912251881Speter          svn_rangelist_t *rangelist;
8913251881Speter
8914251881Speter          rangelist = svn_rangelist__initialize(revision - 1, revision, TRUE,
8915251881Speter                                                scratch_pool);
8916251881Speter
8917251881Speter          /* If PATH inherited mergeinfo we must consider inheritance in the
8918251881Speter             event the inherited mergeinfo is actually non-inheritable. */
8919251881Speter          SVN_ERR(svn_rangelist_intersect(&intersecting_range,
8920251881Speter                                          paths_explicit_rangelist,
8921251881Speter                                          rangelist,
8922251881Speter                                          mergeinfo_inherited, scratch_pool));
8923251881Speter
8924251881Speter          if (intersecting_range->nelts == 0)
8925251881Speter            log_entry_rev_required = TRUE;
8926251881Speter        }
8927251881Speter      else
8928251881Speter        {
8929251881Speter          log_entry_rev_required = TRUE;
8930251881Speter        }
8931251881Speter    }
8932251881Speter
8933251881Speter  if (!log_entry_rev_required)
8934251881Speter    SVN_ERR(rangelist_merge_revision(log_gap_baton->merged_ranges,
8935251881Speter                                     revision,
8936251881Speter                                     log_gap_baton->pool));
8937251881Speter
8938251881Speter  return SVN_NO_ERROR;
8939251881Speter}
8940251881Speter
8941251881Speter/* Helper for do_directory_merge().
8942251881Speter
8943251881Speter   SOURCE is cascaded from the argument of the same name in
8944251881Speter   do_directory_merge().  TARGET is the merge target.  RA_SESSION is the
8945251881Speter   session for SOURCE->loc2.
8946251881Speter
8947251881Speter   Find all the ranges required by subtrees in
8948251881Speter   CHILDREN_WITH_MERGEINFO that are *not* required by
8949251881Speter   TARGET->abspath (i.e. CHILDREN_WITH_MERGEINFO[0]).  If such
8950251881Speter   ranges exist, then find any subset of ranges which, if merged, would be
8951251881Speter   inoperative.  Finally, if any inoperative ranges are found then remove
8952251881Speter   these ranges from all of the subtree's REMAINING_RANGES.
8953251881Speter
8954251881Speter   This function should only be called when honoring mergeinfo during
8955251881Speter   forward merges (i.e. SOURCE->rev1 < SOURCE->rev2).
8956251881Speter*/
8957251881Speterstatic svn_error_t *
8958251881Speterremove_noop_subtree_ranges(const merge_source_t *source,
8959251881Speter                           const merge_target_t *target,
8960251881Speter                           svn_ra_session_t *ra_session,
8961251881Speter                           apr_array_header_t *children_with_mergeinfo,
8962251881Speter                           apr_pool_t *result_pool,
8963251881Speter                           apr_pool_t *scratch_pool)
8964251881Speter{
8965251881Speter  /* ### Do we need to check that we are at a uniform working revision? */
8966251881Speter  int i;
8967251881Speter  svn_client__merge_path_t *root_child =
8968251881Speter    APR_ARRAY_IDX(children_with_mergeinfo, 0, svn_client__merge_path_t *);
8969251881Speter  svn_rangelist_t *requested_ranges;
8970251881Speter  svn_rangelist_t *subtree_gap_ranges;
8971251881Speter  svn_rangelist_t *subtree_remaining_ranges;
8972251881Speter  log_noop_baton_t log_gap_baton;
8973251881Speter  svn_merge_range_t *oldest_gap_rev;
8974251881Speter  svn_merge_range_t *youngest_gap_rev;
8975251881Speter  svn_rangelist_t *inoperative_ranges;
8976251881Speter  apr_pool_t *iterpool;
8977251881Speter  const char *longest_common_subtree_ancestor = NULL;
8978251881Speter  svn_error_t *err;
8979251881Speter
8980251881Speter  assert(session_url_is(ra_session, source->loc2->url, scratch_pool));
8981251881Speter
8982251881Speter  /* This function is only intended to work with forward merges. */
8983251881Speter  if (source->loc1->rev > source->loc2->rev)
8984251881Speter    return SVN_NO_ERROR;
8985251881Speter
8986251881Speter  /* Another easy out: There are no subtrees. */
8987251881Speter  if (children_with_mergeinfo->nelts < 2)
8988251881Speter    return SVN_NO_ERROR;
8989251881Speter
8990251881Speter  subtree_remaining_ranges = apr_array_make(scratch_pool, 1,
8991251881Speter                                            sizeof(svn_merge_range_t *));
8992251881Speter
8993251881Speter  /* Given the requested merge of SOURCE->rev1:rev2 might there be any
8994251881Speter     part of this range required for subtrees but not for the target? */
8995251881Speter  requested_ranges = svn_rangelist__initialize(MIN(source->loc1->rev,
8996251881Speter                                                   source->loc2->rev),
8997251881Speter                                               MAX(source->loc1->rev,
8998251881Speter                                                   source->loc2->rev),
8999251881Speter                                               TRUE, scratch_pool);
9000251881Speter  SVN_ERR(svn_rangelist_remove(&subtree_gap_ranges,
9001251881Speter                               root_child->remaining_ranges,
9002251881Speter                               requested_ranges, FALSE, scratch_pool));
9003251881Speter
9004251881Speter  /* Early out, nothing to operate on */
9005251881Speter  if (!subtree_gap_ranges->nelts)
9006251881Speter    return SVN_NO_ERROR;
9007251881Speter
9008251881Speter  /* Create a rangelist describing every range required across all subtrees. */
9009251881Speter  iterpool = svn_pool_create(scratch_pool);
9010251881Speter  for (i = 1; i < children_with_mergeinfo->nelts; i++)
9011251881Speter    {
9012251881Speter      svn_client__merge_path_t *child =
9013251881Speter        APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
9014251881Speter
9015251881Speter      svn_pool_clear(iterpool);
9016251881Speter
9017251881Speter      /* Issue #4269: Keep track of the longest common ancestor of all the
9018251881Speter         subtrees which require merges.  This may be a child of
9019251881Speter         TARGET->ABSPATH, which will allow us to narrow the log request
9020251881Speter         below. */
9021251881Speter      if (child->remaining_ranges && child->remaining_ranges->nelts)
9022251881Speter        {
9023251881Speter          if (longest_common_subtree_ancestor)
9024251881Speter            longest_common_subtree_ancestor = svn_dirent_get_longest_ancestor(
9025251881Speter              longest_common_subtree_ancestor, child->abspath, scratch_pool);
9026251881Speter          else
9027251881Speter            longest_common_subtree_ancestor = child->abspath;
9028251881Speter        }
9029251881Speter
9030251881Speter      /* CHILD->REMAINING_RANGES will be NULL if child is absent. */
9031251881Speter      if (child->remaining_ranges && child->remaining_ranges->nelts)
9032251881Speter        SVN_ERR(svn_rangelist_merge2(subtree_remaining_ranges,
9033251881Speter                                     child->remaining_ranges,
9034251881Speter                                     scratch_pool, iterpool));
9035251881Speter    }
9036251881Speter  svn_pool_destroy(iterpool);
9037251881Speter
9038251881Speter  /* It's possible that none of the subtrees had any remaining ranges. */
9039251881Speter  if (!subtree_remaining_ranges->nelts)
9040251881Speter    return SVN_NO_ERROR;
9041251881Speter
9042251881Speter  /* Ok, *finally* we can answer what part(s) of SOURCE->rev1:rev2 are
9043251881Speter     required for the subtrees but not the target. */
9044251881Speter  SVN_ERR(svn_rangelist_intersect(&subtree_gap_ranges,
9045251881Speter                                  subtree_gap_ranges,
9046251881Speter                                  subtree_remaining_ranges, FALSE,
9047251881Speter                                  scratch_pool));
9048251881Speter
9049251881Speter  /* Another early out */
9050251881Speter  if (!subtree_gap_ranges->nelts)
9051251881Speter    return SVN_NO_ERROR;
9052251881Speter
9053251881Speter  /* One or more subtrees need some revisions that the target doesn't need.
9054251881Speter     Use log to determine if any of these revisions are inoperative. */
9055251881Speter  oldest_gap_rev = APR_ARRAY_IDX(subtree_gap_ranges, 0, svn_merge_range_t *);
9056251881Speter  youngest_gap_rev = APR_ARRAY_IDX(subtree_gap_ranges,
9057251881Speter                         subtree_gap_ranges->nelts - 1, svn_merge_range_t *);
9058251881Speter
9059251881Speter  /* Set up the log baton. */
9060251881Speter  log_gap_baton.children_with_mergeinfo = children_with_mergeinfo;
9061251881Speter  log_gap_baton.source_fspath
9062251881Speter    = svn_client__pathrev_fspath(source->loc2, result_pool);
9063251881Speter  log_gap_baton.target = target;
9064251881Speter  log_gap_baton.merged_ranges = apr_array_make(scratch_pool, 0,
9065251881Speter                                               sizeof(svn_revnum_t *));
9066251881Speter  log_gap_baton.operative_ranges = apr_array_make(scratch_pool, 0,
9067251881Speter                                                  sizeof(svn_revnum_t *));
9068251881Speter  log_gap_baton.pool = svn_pool_create(scratch_pool);
9069251881Speter
9070251881Speter  /* Find the longest common ancestor of all subtrees relative to
9071251881Speter     RA_SESSION's URL. */
9072251881Speter  if (longest_common_subtree_ancestor)
9073251881Speter    longest_common_subtree_ancestor =
9074251881Speter      svn_dirent_skip_ancestor(target->abspath,
9075251881Speter                               longest_common_subtree_ancestor);
9076251881Speter  else
9077251881Speter    longest_common_subtree_ancestor = "";
9078251881Speter
9079251881Speter  /* Invoke the svn_log_entry_receiver_t receiver log_noop_revs() from
9080251881Speter     oldest to youngest.  The receiver is optimized to add ranges to
9081251881Speter     log_gap_baton.merged_ranges and log_gap_baton.operative_ranges, but
9082251881Speter     requires that the revs arrive oldest to youngest -- see log_noop_revs()
9083251881Speter     and rangelist_merge_revision(). */
9084251881Speter  err = get_log(ra_session, longest_common_subtree_ancestor,
9085251881Speter                oldest_gap_rev->start + 1, youngest_gap_rev->end, TRUE,
9086251881Speter                log_noop_revs, &log_gap_baton, scratch_pool);
9087251881Speter
9088251881Speter  /* It's possible that the only subtrees with mergeinfo in TARGET don't have
9089251881Speter     any corresponding subtree in SOURCE between SOURCE->REV1 < SOURCE->REV2.
9090251881Speter     So it's also possible that we may ask for the logs of non-existent paths.
9091251881Speter     If we do, then assume that no subtree requires any ranges that are not
9092251881Speter     already required by the TARGET. */
9093251881Speter  if (err)
9094251881Speter    {
9095251881Speter      if (err->apr_err != SVN_ERR_FS_NOT_FOUND
9096251881Speter          && longest_common_subtree_ancestor[0] != '\0')
9097251881Speter        return svn_error_trace(err);
9098251881Speter
9099251881Speter      /* Asked about a non-existent subtree in SOURCE. */
9100251881Speter      svn_error_clear(err);
9101251881Speter      log_gap_baton.merged_ranges =
9102251881Speter        svn_rangelist__initialize(oldest_gap_rev->start,
9103251881Speter                                  youngest_gap_rev->end,
9104251881Speter                                  TRUE, scratch_pool);
9105251881Speter    }
9106251881Speter  else
9107251881Speter    {
9108251881Speter      inoperative_ranges = svn_rangelist__initialize(oldest_gap_rev->start,
9109251881Speter                                                     youngest_gap_rev->end,
9110251881Speter                                                     TRUE, scratch_pool);
9111251881Speter      SVN_ERR(svn_rangelist_remove(&(inoperative_ranges),
9112251881Speter                                   log_gap_baton.operative_ranges,
9113251881Speter                                   inoperative_ranges, FALSE, scratch_pool));
9114251881Speter      SVN_ERR(svn_rangelist_merge2(log_gap_baton.merged_ranges, inoperative_ranges,
9115251881Speter                                   scratch_pool, scratch_pool));
9116251881Speter    }
9117251881Speter
9118251881Speter  for (i = 1; i < children_with_mergeinfo->nelts; i++)
9119251881Speter    {
9120251881Speter      svn_client__merge_path_t *child =
9121251881Speter        APR_ARRAY_IDX(children_with_mergeinfo, i, svn_client__merge_path_t *);
9122251881Speter
9123251881Speter      /* CHILD->REMAINING_RANGES will be NULL if child is absent. */
9124251881Speter      if (child->remaining_ranges && child->remaining_ranges->nelts)
9125251881Speter        {
9126251881Speter          /* Remove inoperative ranges from all children so we don't perform
9127251881Speter             inoperative editor drives. */
9128251881Speter          SVN_ERR(svn_rangelist_remove(&(child->remaining_ranges),
9129251881Speter                                       log_gap_baton.merged_ranges,
9130251881Speter                                       child->remaining_ranges,
9131251881Speter                                       FALSE, result_pool));
9132251881Speter        }
9133251881Speter    }
9134251881Speter
9135251881Speter  svn_pool_destroy(log_gap_baton.pool);
9136251881Speter
9137251881Speter  return SVN_NO_ERROR;
9138251881Speter}
9139251881Speter
9140251881Speter/* Perform a merge of changes in SOURCE to the working copy path
9141251881Speter   TARGET_ABSPATH. Both URLs in SOURCE, and TARGET_ABSPATH all represent
9142251881Speter   directories -- for the single file case, the caller should use
9143251881Speter   do_file_merge().
9144251881Speter
9145251881Speter   CHILDREN_WITH_MERGEINFO and MERGE_B describe the merge being performed
9146251881Speter   As this function is for a mergeinfo-aware merge, SOURCE->ancestral
9147251881Speter   should be TRUE, and SOURCE->loc1 must be a historical ancestor of
9148251881Speter   SOURCE->loc2, or vice-versa (see `MERGEINFO MERGE SOURCE NORMALIZATION'
9149251881Speter   for more requirements around SOURCE).
9150251881Speter
9151251881Speter   Mergeinfo changes will be recorded unless MERGE_B->dry_run is true.
9152251881Speter
9153251881Speter   If mergeinfo is being recorded, SQUELCH_MERGEINFO_NOTIFICATIONS is FALSE,
9154251881Speter   and MERGE_B->CTX->NOTIFY_FUNC2 is not NULL, then call
9155251881Speter   MERGE_B->CTX->NOTIFY_FUNC2 with MERGE_B->CTX->NOTIFY_BATON2 and a
9156251881Speter   svn_wc_notify_merge_record_info_begin notification before any mergeinfo
9157251881Speter   changes are made to describe the merge performed.
9158251881Speter
9159251881Speter   If mergeinfo is being recorded to describe this merge, and RESULT_CATALOG
9160251881Speter   is not NULL, then don't record the new mergeinfo on the WC, but instead
9161251881Speter   record it in RESULT_CATALOG, where the keys are absolute working copy
9162251881Speter   paths and the values are the new mergeinfos for each.  Allocate additions
9163251881Speter   to RESULT_CATALOG in pool which RESULT_CATALOG was created in.
9164251881Speter
9165251881Speter   Handle DEPTH as documented for svn_client_merge5().
9166251881Speter
9167251881Speter   CONFLICT_REPORT is as documented for do_directory_merge().
9168251881Speter
9169251881Speter   Perform any temporary allocations in SCRATCH_POOL.
9170251881Speter
9171251881Speter   NOTE: This is a wrapper around drive_merge_report_editor() which
9172251881Speter   handles the complexities inherent to situations where a given
9173251881Speter   directory's children may have intersecting merges (because they
9174251881Speter   meet one or more of the criteria described in get_mergeinfo_paths()).
9175251881Speter*/
9176251881Speterstatic svn_error_t *
9177251881Speterdo_mergeinfo_aware_dir_merge(svn_mergeinfo_catalog_t result_catalog,
9178251881Speter                             single_range_conflict_report_t **conflict_report,
9179251881Speter                             const merge_source_t *source,
9180251881Speter                             const char *target_abspath,
9181251881Speter                             apr_array_header_t *children_with_mergeinfo,
9182251881Speter                             const svn_diff_tree_processor_t *processor,
9183251881Speter                             svn_depth_t depth,
9184251881Speter                             svn_boolean_t squelch_mergeinfo_notifications,
9185251881Speter                             merge_cmd_baton_t *merge_b,
9186251881Speter                             apr_pool_t *result_pool,
9187251881Speter                             apr_pool_t *scratch_pool)
9188251881Speter{
9189251881Speter  /* The range defining the mergeinfo we will record to describe the merge
9190251881Speter     (assuming we are recording mergeinfo
9191251881Speter
9192251881Speter     Note: This may be a subset of SOURCE->rev1:rev2 if
9193251881Speter     populate_remaining_ranges() determines that some part of
9194251881Speter     SOURCE->rev1:rev2 has already been wholly merged to TARGET_ABSPATH.
9195251881Speter     Also, the actual editor drive(s) may be a subset of RANGE, if
9196251881Speter     remove_noop_subtree_ranges() and/or fix_deleted_subtree_ranges()
9197251881Speter     further tweak things. */
9198251881Speter  svn_merge_range_t range;
9199251881Speter
9200251881Speter  svn_ra_session_t *ra_session;
9201251881Speter  svn_client__merge_path_t *target_merge_path;
9202251881Speter  svn_boolean_t is_rollback = (source->loc1->rev > source->loc2->rev);
9203251881Speter
9204251881Speter  SVN_ERR_ASSERT(source->ancestral);
9205251881Speter
9206251881Speter  /*** If we get here, we're dealing with related sources from the
9207251881Speter       same repository as the target -- merge tracking might be
9208251881Speter       happenin'! ***/
9209251881Speter
9210251881Speter  *conflict_report = NULL;
9211251881Speter
9212251881Speter  /* Point our RA_SESSION to the URL of our youngest merge source side. */
9213251881Speter  ra_session = is_rollback ? merge_b->ra_session1 : merge_b->ra_session2;
9214251881Speter
9215251881Speter  /* Fill NOTIFY_B->CHILDREN_WITH_MERGEINFO with child paths (const
9216251881Speter     svn_client__merge_path_t *) which might have intersecting merges
9217251881Speter     because they meet one or more of the criteria described in
9218251881Speter     get_mergeinfo_paths(). Here the paths are arranged in a depth
9219251881Speter     first order. */
9220251881Speter  SVN_ERR(get_mergeinfo_paths(children_with_mergeinfo,
9221251881Speter                              merge_b->target, depth,
9222251881Speter                              merge_b->dry_run, merge_b->same_repos,
9223251881Speter                              merge_b->ctx, scratch_pool, scratch_pool));
9224251881Speter
9225251881Speter  /* The first item from the NOTIFY_B->CHILDREN_WITH_MERGEINFO is always
9226251881Speter     the target thanks to depth-first ordering. */
9227251881Speter  target_merge_path = APR_ARRAY_IDX(children_with_mergeinfo, 0,
9228251881Speter                                    svn_client__merge_path_t *);
9229251881Speter
9230251881Speter  /* If we are honoring mergeinfo, then for each item in
9231251881Speter     NOTIFY_B->CHILDREN_WITH_MERGEINFO, we need to calculate what needs to be
9232251881Speter     merged, and then merge it.  Otherwise, we just merge what we were asked
9233251881Speter     to merge across the whole tree.  */
9234251881Speter  SVN_ERR(populate_remaining_ranges(children_with_mergeinfo,
9235251881Speter                                    source, ra_session,
9236251881Speter                                    merge_b, scratch_pool, scratch_pool));
9237251881Speter
9238251881Speter  /* Always start with a range which describes the most inclusive merge
9239251881Speter     possible, i.e. SOURCE->rev1:rev2. */
9240251881Speter  range.start = source->loc1->rev;
9241251881Speter  range.end = source->loc2->rev;
9242251881Speter  range.inheritable = TRUE;
9243251881Speter
9244251881Speter  if (!merge_b->reintegrate_merge)
9245251881Speter    {
9246251881Speter      svn_revnum_t new_range_start, start_rev;
9247251881Speter      apr_pool_t *iterpool = svn_pool_create(scratch_pool);
9248251881Speter
9249251881Speter      /* The merge target TARGET_ABSPATH and/or its subtrees may not need all
9250251881Speter         of SOURCE->rev1:rev2 applied.  So examine
9251251881Speter         NOTIFY_B->CHILDREN_WITH_MERGEINFO to find the oldest starting
9252251881Speter         revision that actually needs to be merged (for reverse merges this is
9253251881Speter         the youngest starting revision).
9254251881Speter
9255251881Speter         We'll do this twice, right now for the start of the mergeinfo we will
9256251881Speter         ultimately record to describe this merge and then later for the
9257251881Speter         start of the actual editor drive. */
9258251881Speter      new_range_start = get_most_inclusive_rev(
9259251881Speter        children_with_mergeinfo, is_rollback, TRUE);
9260251881Speter      if (SVN_IS_VALID_REVNUM(new_range_start))
9261251881Speter        range.start = new_range_start;
9262251881Speter
9263251881Speter      /* Remove inoperative ranges from any subtrees' remaining_ranges
9264251881Speter         to spare the expense of noop editor drives. */
9265251881Speter      if (!is_rollback)
9266251881Speter        SVN_ERR(remove_noop_subtree_ranges(source, merge_b->target,
9267251881Speter                                           ra_session,
9268251881Speter                                           children_with_mergeinfo,
9269251881Speter                                           scratch_pool, iterpool));
9270251881Speter
9271251881Speter      /* Adjust subtrees' remaining_ranges to deal with issue #3067:
9272251881Speter       * "subtrees that don't exist at the start or end of a merge range
9273251881Speter       * shouldn't break the merge". */
9274251881Speter      SVN_ERR(fix_deleted_subtree_ranges(source, merge_b->target,
9275251881Speter                                         ra_session,
9276251881Speter                                         children_with_mergeinfo,
9277251881Speter                                         merge_b->ctx, scratch_pool, iterpool));
9278251881Speter
9279251881Speter      /* remove_noop_subtree_ranges() and/or fix_deleted_subtree_range()
9280251881Speter         may have further refined the starting revision for our editor
9281251881Speter         drive. */
9282251881Speter      start_rev =
9283251881Speter        get_most_inclusive_rev(children_with_mergeinfo,
9284251881Speter                               is_rollback, TRUE);
9285251881Speter
9286251881Speter      /* Is there anything to merge? */
9287251881Speter      if (SVN_IS_VALID_REVNUM(start_rev))
9288251881Speter        {
9289251881Speter          /* Now examine NOTIFY_B->CHILDREN_WITH_MERGEINFO to find the oldest
9290251881Speter             ending revision that actually needs to be merged (for reverse
9291251881Speter             merges this is the youngest ending revision). */
9292251881Speter           svn_revnum_t end_rev =
9293251881Speter             get_most_inclusive_rev(children_with_mergeinfo,
9294251881Speter                                    is_rollback, FALSE);
9295251881Speter
9296251881Speter          /* While END_REV is valid, do the following:
9297251881Speter
9298251881Speter             1. Tweak each NOTIFY_B->CHILDREN_WITH_MERGEINFO element so that
9299251881Speter                the element's remaining_ranges member has as its first element
9300251881Speter                a range that ends with end_rev.
9301251881Speter
9302251881Speter             2. Starting with start_rev, call drive_merge_report_editor()
9303251881Speter                on MERGE_B->target->abspath for start_rev:end_rev.
9304251881Speter
9305251881Speter             3. Remove the first element from each
9306251881Speter                NOTIFY_B->CHILDREN_WITH_MERGEINFO element's remaining_ranges
9307251881Speter                member.
9308251881Speter
9309251881Speter             4. Again examine NOTIFY_B->CHILDREN_WITH_MERGEINFO to find the most
9310251881Speter                inclusive starting revision that actually needs to be merged and
9311251881Speter                update start_rev.  This prevents us from needlessly contacting the
9312251881Speter                repository and doing a diff where we describe the entire target
9313251881Speter                tree as *not* needing any of the requested range.  This can happen
9314251881Speter                whenever we have mergeinfo with gaps in it for the merge source.
9315251881Speter
9316251881Speter             5. Again examine NOTIFY_B->CHILDREN_WITH_MERGEINFO to find the most
9317251881Speter                inclusive ending revision that actually needs to be merged and
9318251881Speter                update end_rev.
9319251881Speter
9320251881Speter             6. Lather, rinse, repeat.
9321251881Speter          */
9322251881Speter
9323251881Speter          while (end_rev != SVN_INVALID_REVNUM)
9324251881Speter            {
9325251881Speter              merge_source_t *real_source;
9326251881Speter              svn_merge_range_t *first_target_range
9327251881Speter                = (target_merge_path->remaining_ranges->nelts == 0 ? NULL
9328251881Speter                   : APR_ARRAY_IDX(target_merge_path->remaining_ranges, 0,
9329251881Speter                                   svn_merge_range_t *));
9330251881Speter
9331251881Speter              /* Issue #3324: Stop editor abuse!  Don't call
9332251881Speter                 drive_merge_report_editor() in such a way that we request an
9333251881Speter                 editor with svn_client__get_diff_editor() for some rev X,
9334251881Speter                 then call svn_ra_do_diff3() for some revision Y, and then
9335251881Speter                 call reporter->set_path(PATH=="") to set the root revision
9336251881Speter                 for the editor drive to revision Z where
9337251881Speter                 (X != Z && X < Z < Y).  This is bogus because the server will
9338251881Speter                 send us the diff between X:Y but the client is expecting the
9339251881Speter                 diff between Y:Z.  See issue #3324 for full details on the
9340251881Speter                 problems this can cause. */
9341251881Speter              if (first_target_range
9342251881Speter                  && start_rev != first_target_range->start)
9343251881Speter                {
9344251881Speter                  if (is_rollback)
9345251881Speter                    {
9346251881Speter                      if (end_rev < first_target_range->start)
9347251881Speter                        end_rev = first_target_range->start;
9348251881Speter                    }
9349251881Speter                  else
9350251881Speter                    {
9351251881Speter                      if (end_rev > first_target_range->start)
9352251881Speter                        end_rev = first_target_range->start;
9353251881Speter                    }
9354251881Speter                }
9355251881Speter
9356251881Speter              svn_pool_clear(iterpool);
9357251881Speter
9358251881Speter              slice_remaining_ranges(children_with_mergeinfo,
9359251881Speter                                     is_rollback, end_rev, scratch_pool);
9360251881Speter
9361251881Speter              /* Reset variables that must be reset for every drive */
9362251881Speter              merge_b->notify_begin.last_abspath = NULL;
9363251881Speter
9364251881Speter              real_source = subrange_source(source, start_rev, end_rev, iterpool);
9365251881Speter              SVN_ERR(drive_merge_report_editor(
9366251881Speter                merge_b->target->abspath,
9367251881Speter                real_source,
9368251881Speter                children_with_mergeinfo,
9369251881Speter                processor,
9370251881Speter                depth,
9371251881Speter                merge_b,
9372251881Speter                iterpool));
9373251881Speter
9374251881Speter              /* If any paths picked up explicit mergeinfo as a result of
9375251881Speter                 the merge we need to make sure any mergeinfo those paths
9376251881Speter                 inherited is recorded and then add these paths to
9377251881Speter                 NOTIFY_B->CHILDREN_WITH_MERGEINFO.*/
9378251881Speter              SVN_ERR(process_children_with_new_mergeinfo(
9379251881Speter                        merge_b, children_with_mergeinfo,
9380251881Speter                        scratch_pool));
9381251881Speter
9382251881Speter              /* If any subtrees had their explicit mergeinfo deleted as a
9383251881Speter                 result of the merge then remove these paths from
9384251881Speter                 NOTIFY_B->CHILDREN_WITH_MERGEINFO since there is no need
9385251881Speter                 to consider these subtrees for subsequent editor drives
9386251881Speter                 nor do we want to record mergeinfo on them describing
9387251881Speter                 the merge itself. */
9388251881Speter              remove_children_with_deleted_mergeinfo(
9389251881Speter                merge_b, children_with_mergeinfo);
9390251881Speter
9391251881Speter              /* Prepare for the next iteration (if any). */
9392251881Speter              remove_first_range_from_remaining_ranges(
9393251881Speter                end_rev, children_with_mergeinfo, scratch_pool);
9394251881Speter
9395251881Speter              /* If we raised any conflicts, break out and report how much
9396251881Speter                 we have merged. */
9397251881Speter              if (is_path_conflicted_by_merge(merge_b))
9398251881Speter                {
9399251881Speter                  merge_source_t *remaining_range = NULL;
9400251881Speter
9401251881Speter                  if (real_source->loc2->rev != source->loc2->rev)
9402251881Speter                    remaining_range = subrange_source(source,
9403251881Speter                                                      real_source->loc2->rev,
9404251881Speter                                                      source->loc2->rev,
9405251881Speter                                                      scratch_pool);
9406251881Speter                  *conflict_report = single_range_conflict_report_create(
9407251881Speter                                       real_source, remaining_range,
9408251881Speter                                       result_pool);
9409251881Speter
9410251881Speter                  range.end = end_rev;
9411251881Speter                  break;
9412251881Speter                }
9413251881Speter
9414251881Speter              start_rev =
9415251881Speter                get_most_inclusive_rev(children_with_mergeinfo,
9416251881Speter                                       is_rollback, TRUE);
9417251881Speter              end_rev =
9418251881Speter                get_most_inclusive_rev(children_with_mergeinfo,
9419251881Speter                                       is_rollback, FALSE);
9420251881Speter            }
9421251881Speter        }
9422251881Speter      svn_pool_destroy(iterpool);
9423251881Speter    }
9424251881Speter  else
9425251881Speter    {
9426251881Speter      if (!merge_b->record_only)
9427251881Speter        {
9428251881Speter          /* Reset cur_ancestor_abspath to null so that subsequent cherry
9429251881Speter             picked revision ranges will be notified upon subsequent
9430251881Speter             operative merge. */
9431251881Speter          merge_b->notify_begin.last_abspath = NULL;
9432251881Speter
9433251881Speter          SVN_ERR(drive_merge_report_editor(merge_b->target->abspath,
9434251881Speter                                            source,
9435251881Speter                                            NULL,
9436251881Speter                                            processor,
9437251881Speter                                            depth,
9438251881Speter                                            merge_b,
9439251881Speter                                            scratch_pool));
9440251881Speter        }
9441251881Speter    }
9442251881Speter
9443251881Speter  /* Record mergeinfo where appropriate.*/
9444251881Speter  if (RECORD_MERGEINFO(merge_b))
9445251881Speter    {
9446251881Speter      const svn_client__pathrev_t *primary_src
9447251881Speter        = is_rollback ? source->loc1 : source->loc2;
9448251881Speter      const char *mergeinfo_path
9449251881Speter        = svn_client__pathrev_fspath(primary_src, scratch_pool);
9450251881Speter
9451251881Speter      SVN_ERR(record_mergeinfo_for_dir_merge(result_catalog,
9452251881Speter                                             &range,
9453251881Speter                                             mergeinfo_path,
9454251881Speter                                             children_with_mergeinfo,
9455251881Speter                                             depth,
9456251881Speter                                             squelch_mergeinfo_notifications,
9457251881Speter                                             merge_b,
9458251881Speter                                             scratch_pool));
9459251881Speter
9460251881Speter      /* If a path has an immediate parent with non-inheritable mergeinfo at
9461251881Speter         this point, then it meets criteria 3 or 5 described in
9462251881Speter         get_mergeinfo_paths' doc string.  For paths which exist prior to a
9463251881Speter         merge explicit mergeinfo has already been set.  But for paths added
9464251881Speter         during the merge this is not the case.  The path might have explicit
9465251881Speter         mergeinfo from the merge source, but no mergeinfo yet exists
9466251881Speter         describing *this* merge.  So the added path has either incomplete
9467251881Speter         explicit mergeinfo or inherits incomplete mergeinfo from its
9468251881Speter         immediate parent (if any, the parent might have only non-inheritable
9469251881Speter         ranges in which case the path simply inherits empty mergeinfo).
9470251881Speter
9471251881Speter         So here we look at the root path of each subtree added during the
9472251881Speter         merge and set explicit mergeinfo on it if it meets the aforementioned
9473251881Speter         conditions. */
9474251881Speter      if (range.start < range.end) /* Nothing to record on added subtrees
9475251881Speter                                      resulting from reverse merges. */
9476251881Speter        {
9477251881Speter          SVN_ERR(record_mergeinfo_for_added_subtrees(
9478251881Speter                    &range, mergeinfo_path, depth,
9479251881Speter                    squelch_mergeinfo_notifications,
9480251881Speter                    merge_b->added_abspaths, merge_b, scratch_pool));
9481251881Speter        }
9482251881Speter    }
9483251881Speter
9484251881Speter  return SVN_NO_ERROR;
9485251881Speter}
9486251881Speter
9487251881Speter/* Helper for do_merge() when the merge target is a directory.
9488251881Speter *
9489251881Speter * If any conflict is raised during the merge, set *CONFLICTED_RANGE to
9490251881Speter * the revision sub-range that raised the conflict.  In this case, the
9491251881Speter * merge will have ended at revision CONFLICTED_RANGE and mergeinfo will
9492251881Speter * have been recorded for all revision sub-ranges up to and including
9493251881Speter * CONFLICTED_RANGE.  Otherwise, set *CONFLICTED_RANGE to NULL.
9494251881Speter */
9495251881Speterstatic svn_error_t *
9496251881Speterdo_directory_merge(svn_mergeinfo_catalog_t result_catalog,
9497251881Speter                   single_range_conflict_report_t **conflict_report,
9498251881Speter                   const merge_source_t *source,
9499251881Speter                   const char *target_abspath,
9500251881Speter                   const svn_diff_tree_processor_t *processor,
9501251881Speter                   svn_depth_t depth,
9502251881Speter                   svn_boolean_t squelch_mergeinfo_notifications,
9503251881Speter                   merge_cmd_baton_t *merge_b,
9504251881Speter                   apr_pool_t *result_pool,
9505251881Speter                   apr_pool_t *scratch_pool)
9506251881Speter{
9507251881Speter  apr_array_header_t *children_with_mergeinfo;
9508251881Speter
9509251881Speter  /* Initialize CHILDREN_WITH_MERGEINFO. See the comment
9510251881Speter     'THE CHILDREN_WITH_MERGEINFO ARRAY' at the start of this file. */
9511251881Speter  children_with_mergeinfo =
9512251881Speter    apr_array_make(scratch_pool, 16, sizeof(svn_client__merge_path_t *));
9513251881Speter
9514251881Speter  /* And make it read-only accessible from the baton */
9515251881Speter  merge_b->notify_begin.nodes_with_mergeinfo = children_with_mergeinfo;
9516251881Speter
9517251881Speter  /* If we are not honoring mergeinfo we can skip right to the
9518251881Speter     business of merging changes! */
9519251881Speter  if (HONOR_MERGEINFO(merge_b))
9520251881Speter    SVN_ERR(do_mergeinfo_aware_dir_merge(result_catalog, conflict_report,
9521251881Speter                                         source, target_abspath,
9522251881Speter                                         children_with_mergeinfo,
9523251881Speter                                         processor, depth,
9524251881Speter                                         squelch_mergeinfo_notifications,
9525251881Speter                                         merge_b, result_pool, scratch_pool));
9526251881Speter  else
9527251881Speter    SVN_ERR(do_mergeinfo_unaware_dir_merge(conflict_report,
9528251881Speter                                           source, target_abspath,
9529251881Speter                                           children_with_mergeinfo,
9530251881Speter                                           processor, depth,
9531251881Speter                                           merge_b, result_pool, scratch_pool));
9532251881Speter
9533251881Speter  merge_b->notify_begin.nodes_with_mergeinfo = NULL;
9534251881Speter
9535251881Speter  return SVN_NO_ERROR;
9536251881Speter}
9537251881Speter
9538251881Speter/** Ensure that *RA_SESSION is opened to URL, either by reusing
9539251881Speter * *RA_SESSION if it is non-null and already opened to URL's
9540251881Speter * repository, or by allocating a new *RA_SESSION in POOL.
9541251881Speter * (RA_SESSION itself cannot be null, of course.)
9542251881Speter *
9543251881Speter * CTX is used as for svn_client_open_ra_session().
9544251881Speter */
9545251881Speterstatic svn_error_t *
9546251881Speterensure_ra_session_url(svn_ra_session_t **ra_session,
9547251881Speter                      const char *url,
9548251881Speter                      const char *wri_abspath,
9549251881Speter                      svn_client_ctx_t *ctx,
9550251881Speter                      apr_pool_t *pool)
9551251881Speter{
9552251881Speter  svn_error_t *err = SVN_NO_ERROR;
9553251881Speter
9554251881Speter  if (*ra_session)
9555251881Speter    {
9556251881Speter      err = svn_ra_reparent(*ra_session, url, pool);
9557251881Speter    }
9558251881Speter
9559251881Speter  /* SVN_ERR_RA_ILLEGAL_URL is raised when url doesn't point to the same
9560251881Speter     repository as ra_session. */
9561251881Speter  if (! *ra_session || (err && err->apr_err == SVN_ERR_RA_ILLEGAL_URL))
9562251881Speter    {
9563251881Speter      svn_error_clear(err);
9564251881Speter      err = svn_client_open_ra_session2(ra_session, url, wri_abspath,
9565251881Speter                                        ctx, pool, pool);
9566251881Speter    }
9567251881Speter  SVN_ERR(err);
9568251881Speter
9569251881Speter  return SVN_NO_ERROR;
9570251881Speter}
9571251881Speter
9572251881Speter/* Drive a merge of MERGE_SOURCES into working copy node TARGET
9573251881Speter   and possibly record mergeinfo describing the merge -- see
9574251881Speter   RECORD_MERGEINFO().
9575251881Speter
9576251881Speter   If MODIFIED_SUBTREES is not NULL and all the MERGE_SOURCES are 'ancestral'
9577251881Speter   or REINTEGRATE_MERGE is true, then replace *MODIFIED_SUBTREES with a new
9578251881Speter   hash containing all the paths that *MODIFIED_SUBTREES contained before,
9579251881Speter   and also every path modified, skipped, added, or tree-conflicted
9580251881Speter   by the merge.  Keys and values of the hash are both (const char *)
9581251881Speter   absolute paths.  The contents of the hash are allocated in RESULT_POOL.
9582251881Speter
9583251881Speter   If the merge raises any conflicts while merging a revision range, return
9584251881Speter   early and set *CONFLICT_REPORT to describe the details.  (In this case,
9585251881Speter   notify that the merge is complete if and only if this was the last
9586251881Speter   revision range of the merge.)  If there are no conflicts, set
9587251881Speter   *CONFLICT_REPORT to NULL.  A revision range here can be one specified
9588251881Speter   in MERGE_SOURCES or an internally generated sub-range of one of those
9589251881Speter   when merge tracking is in use.
9590251881Speter
9591251881Speter   For every (const merge_source_t *) merge source in MERGE_SOURCES, if
9592251881Speter   SOURCE->ANCESTRAL is set, then the "left" and "right" side are
9593251881Speter   ancestrally related.  (See 'MERGEINFO MERGE SOURCE NORMALIZATION'
9594251881Speter   for more on what that means and how it matters.)
9595251881Speter
9596251881Speter   If SOURCES_RELATED is set, the "left" and "right" sides of the
9597251881Speter   merge source are historically related (ancestors, uncles, second
9598251881Speter   cousins thrice removed, etc...).  (This is passed through to
9599251881Speter   do_file_merge() to simulate the history checks that the repository
9600251881Speter   logic does in the directory case.)
9601251881Speter
9602251881Speter   SAME_REPOS is TRUE iff the merge sources live in the same
9603251881Speter   repository as the one from which the target working copy has been
9604251881Speter   checked out.
9605251881Speter
9606251881Speter   If mergeinfo is being recorded, SQUELCH_MERGEINFO_NOTIFICATIONS is FALSE,
9607251881Speter   and CTX->NOTIFY_FUNC2 is not NULL, then call CTX->NOTIFY_FUNC2 with
9608251881Speter   CTX->NOTIFY_BATON2 and a svn_wc_notify_merge_record_info_begin
9609251881Speter   notification before any mergeinfo changes are made to describe the merge
9610251881Speter   performed.
9611251881Speter
9612251881Speter   If mergeinfo is being recorded to describe this merge, and RESULT_CATALOG
9613251881Speter   is not NULL, then don't record the new mergeinfo on the WC, but instead
9614251881Speter   record it in RESULT_CATALOG, where the keys are absolute working copy
9615251881Speter   paths and the values are the new mergeinfos for each.  Allocate additions
9616251881Speter   to RESULT_CATALOG in pool which RESULT_CATALOG was created in.
9617251881Speter
9618251881Speter   FORCE_DELETE, DRY_RUN, RECORD_ONLY, DEPTH, MERGE_OPTIONS,
9619251881Speter   and CTX are as described in the docstring for svn_client_merge_peg3().
9620251881Speter
9621251881Speter   If IGNORE_MERGEINFO is true, disable merge tracking, by treating the two
9622251881Speter   sources as unrelated even if they actually have a common ancestor.  See
9623251881Speter   the macro HONOR_MERGEINFO().
9624251881Speter
9625251881Speter   If DIFF_IGNORE_ANCESTRY is true, diff the 'left' and 'right' versions
9626251881Speter   of a node (if they are the same kind) as if they were related, even if
9627251881Speter   they are not related.  Otherwise, diff unrelated items as a deletion
9628251881Speter   of one thing and the addition of another.
9629251881Speter
9630251881Speter   If not NULL, RECORD_ONLY_PATHS is a hash of (const char *) paths mapped
9631251881Speter   to the same.  If RECORD_ONLY is true and RECORD_ONLY_PATHS is not NULL,
9632251881Speter   then record mergeinfo describing the merge only on subtrees which contain
9633251881Speter   items from RECORD_ONLY_PATHS.  If RECORD_ONLY is true and RECORD_ONLY_PATHS
9634251881Speter   is NULL, then record mergeinfo on every subtree with mergeinfo in
9635251881Speter   TARGET.
9636251881Speter
9637251881Speter   REINTEGRATE_MERGE is TRUE if this is a reintegrate merge.
9638251881Speter
9639251881Speter   *USE_SLEEP will be set TRUE if a sleep is required to ensure timestamp
9640251881Speter   integrity, *USE_SLEEP will be unchanged if no sleep is required.
9641251881Speter
9642251881Speter   SCRATCH_POOL is used for all temporary allocations.
9643251881Speter*/
9644251881Speterstatic svn_error_t *
9645251881Speterdo_merge(apr_hash_t **modified_subtrees,
9646251881Speter         svn_mergeinfo_catalog_t result_catalog,
9647251881Speter         conflict_report_t **conflict_report,
9648251881Speter         svn_boolean_t *use_sleep,
9649251881Speter         const apr_array_header_t *merge_sources,
9650251881Speter         const merge_target_t *target,
9651251881Speter         svn_ra_session_t *src_session,
9652251881Speter         svn_boolean_t sources_related,
9653251881Speter         svn_boolean_t same_repos,
9654251881Speter         svn_boolean_t ignore_mergeinfo,
9655251881Speter         svn_boolean_t diff_ignore_ancestry,
9656251881Speter         svn_boolean_t force_delete,
9657251881Speter         svn_boolean_t dry_run,
9658251881Speter         svn_boolean_t record_only,
9659251881Speter         apr_hash_t *record_only_paths,
9660251881Speter         svn_boolean_t reintegrate_merge,
9661251881Speter         svn_boolean_t squelch_mergeinfo_notifications,
9662251881Speter         svn_depth_t depth,
9663251881Speter         const apr_array_header_t *merge_options,
9664251881Speter         svn_client_ctx_t *ctx,
9665251881Speter         apr_pool_t *result_pool,
9666251881Speter         apr_pool_t *scratch_pool)
9667251881Speter{
9668251881Speter  merge_cmd_baton_t merge_cmd_baton = { 0 };
9669251881Speter  svn_config_t *cfg;
9670251881Speter  const char *diff3_cmd;
9671251881Speter  int i;
9672251881Speter  svn_boolean_t checked_mergeinfo_capability = FALSE;
9673251881Speter  svn_ra_session_t *ra_session1 = NULL, *ra_session2 = NULL;
9674251881Speter  const char *old_src_session_url = NULL;
9675251881Speter  apr_pool_t *iterpool;
9676251881Speter  const svn_diff_tree_processor_t *processor;
9677251881Speter
9678251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(target->abspath));
9679251881Speter
9680251881Speter  *conflict_report = NULL;
9681251881Speter
9682251881Speter  /* Check from some special conditions when in record-only mode
9683251881Speter     (which is a merge-tracking thing). */
9684251881Speter  if (record_only)
9685251881Speter    {
9686251881Speter      svn_boolean_t sources_ancestral = TRUE;
9687251881Speter      int j;
9688251881Speter
9689251881Speter      /* Find out whether all of the sources are 'ancestral'. */
9690251881Speter      for (j = 0; j < merge_sources->nelts; j++)
9691251881Speter        if (! APR_ARRAY_IDX(merge_sources, j, merge_source_t *)->ancestral)
9692251881Speter          {
9693251881Speter            sources_ancestral = FALSE;
9694251881Speter            break;
9695251881Speter          }
9696251881Speter
9697251881Speter      /* We can't do a record-only merge if the sources aren't related. */
9698251881Speter      if (! sources_ancestral)
9699251881Speter        return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
9700251881Speter                                _("Use of two URLs is not compatible with "
9701251881Speter                                  "mergeinfo modification"));
9702251881Speter
9703251881Speter      /* We can't do a record-only merge if the sources aren't from
9704251881Speter         the same repository as the target. */
9705251881Speter      if (! same_repos)
9706251881Speter        return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
9707251881Speter                                _("Merge from foreign repository is not "
9708251881Speter                                  "compatible with mergeinfo modification"));
9709251881Speter
9710251881Speter      /* If this is a dry-run record-only merge, there's nothing to do. */
9711251881Speter      if (dry_run)
9712251881Speter        return SVN_NO_ERROR;
9713251881Speter    }
9714251881Speter
9715251881Speter  iterpool = svn_pool_create(scratch_pool);
9716251881Speter
9717251881Speter  /* Ensure a known depth. */
9718251881Speter  if (depth == svn_depth_unknown)
9719251881Speter    depth = svn_depth_infinity;
9720251881Speter
9721251881Speter  /* Set up the diff3 command, so various callers don't have to. */
9722251881Speter  cfg = ctx->config
9723251881Speter        ? svn_hash_gets(ctx->config, SVN_CONFIG_CATEGORY_CONFIG)
9724251881Speter        : NULL;
9725251881Speter  svn_config_get(cfg, &diff3_cmd, SVN_CONFIG_SECTION_HELPERS,
9726251881Speter                 SVN_CONFIG_OPTION_DIFF3_CMD, NULL);
9727251881Speter
9728251881Speter  if (diff3_cmd != NULL)
9729251881Speter    SVN_ERR(svn_path_cstring_to_utf8(&diff3_cmd, diff3_cmd, scratch_pool));
9730251881Speter
9731251881Speter  /* Build the merge context baton (or at least the parts of it that
9732251881Speter     don't need to be reset for each merge source).  */
9733251881Speter  merge_cmd_baton.force_delete = force_delete;
9734251881Speter  merge_cmd_baton.dry_run = dry_run;
9735251881Speter  merge_cmd_baton.record_only = record_only;
9736251881Speter  merge_cmd_baton.ignore_mergeinfo = ignore_mergeinfo;
9737251881Speter  merge_cmd_baton.diff_ignore_ancestry = diff_ignore_ancestry;
9738251881Speter  merge_cmd_baton.same_repos = same_repos;
9739251881Speter  merge_cmd_baton.mergeinfo_capable = FALSE;
9740251881Speter  merge_cmd_baton.ctx = ctx;
9741251881Speter  merge_cmd_baton.reintegrate_merge = reintegrate_merge;
9742251881Speter  merge_cmd_baton.target = target;
9743251881Speter  merge_cmd_baton.pool = iterpool;
9744251881Speter  merge_cmd_baton.merge_options = merge_options;
9745251881Speter  merge_cmd_baton.diff3_cmd = diff3_cmd;
9746251881Speter  merge_cmd_baton.use_sleep = use_sleep;
9747251881Speter
9748251881Speter  /* Do we already know the specific subtrees with mergeinfo we want
9749251881Speter     to record-only mergeinfo on? */
9750251881Speter  if (record_only && record_only_paths)
9751251881Speter    merge_cmd_baton.merged_abspaths = record_only_paths;
9752251881Speter  else
9753251881Speter    merge_cmd_baton.merged_abspaths = apr_hash_make(result_pool);
9754251881Speter
9755251881Speter  merge_cmd_baton.skipped_abspaths = apr_hash_make(result_pool);
9756251881Speter  merge_cmd_baton.added_abspaths = apr_hash_make(result_pool);
9757251881Speter  merge_cmd_baton.tree_conflicted_abspaths = apr_hash_make(result_pool);
9758251881Speter
9759251881Speter  {
9760251881Speter    svn_diff_tree_processor_t *merge_processor;
9761251881Speter
9762251881Speter    merge_processor = svn_diff__tree_processor_create(&merge_cmd_baton,
9763251881Speter                                                      scratch_pool);
9764251881Speter
9765251881Speter    merge_processor->dir_opened   = merge_dir_opened;
9766251881Speter    merge_processor->dir_changed  = merge_dir_changed;
9767251881Speter    merge_processor->dir_added    = merge_dir_added;
9768251881Speter    merge_processor->dir_deleted  = merge_dir_deleted;
9769251881Speter    merge_processor->dir_closed   = merge_dir_closed;
9770251881Speter
9771251881Speter    merge_processor->file_opened  = merge_file_opened;
9772251881Speter    merge_processor->file_changed = merge_file_changed;
9773251881Speter    merge_processor->file_added   = merge_file_added;
9774251881Speter    merge_processor->file_deleted = merge_file_deleted;
9775251881Speter    /* Not interested in file_closed() */
9776251881Speter
9777251881Speter    merge_processor->node_absent = merge_node_absent;
9778251881Speter
9779251881Speter    processor = merge_processor;
9780251881Speter  }
9781251881Speter
9782251881Speter  if (src_session)
9783251881Speter    {
9784251881Speter      SVN_ERR(svn_ra_get_session_url(src_session, &old_src_session_url,
9785251881Speter                                     scratch_pool));
9786251881Speter      ra_session1 = src_session;
9787251881Speter    }
9788251881Speter
9789251881Speter  for (i = 0; i < merge_sources->nelts; i++)
9790251881Speter    {
9791251881Speter      svn_node_kind_t src1_kind;
9792251881Speter      merge_source_t *source =
9793251881Speter        APR_ARRAY_IDX(merge_sources, i, merge_source_t *);
9794251881Speter      single_range_conflict_report_t *conflicted_range_report;
9795251881Speter
9796251881Speter      svn_pool_clear(iterpool);
9797251881Speter
9798251881Speter      /* Sanity check:  if our left- and right-side merge sources are
9799251881Speter         the same, there's nothing to here. */
9800251881Speter      if ((strcmp(source->loc1->url, source->loc2->url) == 0)
9801251881Speter          && (source->loc1->rev == source->loc2->rev))
9802251881Speter        continue;
9803251881Speter
9804251881Speter      /* Establish RA sessions to our URLs, reuse where possible. */
9805251881Speter      SVN_ERR(ensure_ra_session_url(&ra_session1, source->loc1->url,
9806251881Speter                                    target->abspath, ctx, scratch_pool));
9807251881Speter      SVN_ERR(ensure_ra_session_url(&ra_session2, source->loc2->url,
9808251881Speter                                    target->abspath, ctx, scratch_pool));
9809251881Speter
9810251881Speter      /* Populate the portions of the merge context baton that need to
9811251881Speter         be reset for each merge source iteration. */
9812251881Speter      merge_cmd_baton.merge_source = *source;
9813251881Speter      merge_cmd_baton.implicit_src_gap = NULL;
9814251881Speter      merge_cmd_baton.conflicted_paths = NULL;
9815251881Speter      merge_cmd_baton.paths_with_new_mergeinfo = NULL;
9816251881Speter      merge_cmd_baton.paths_with_deleted_mergeinfo = NULL;
9817251881Speter      merge_cmd_baton.ra_session1 = ra_session1;
9818251881Speter      merge_cmd_baton.ra_session2 = ra_session2;
9819251881Speter
9820251881Speter      merge_cmd_baton.notify_begin.last_abspath = NULL;
9821251881Speter
9822251881Speter      /* Populate the portions of the merge context baton that require
9823251881Speter         an RA session to set, but shouldn't be reset for each iteration. */
9824251881Speter      if (! checked_mergeinfo_capability)
9825251881Speter        {
9826251881Speter          SVN_ERR(svn_ra_has_capability(ra_session1,
9827251881Speter                                        &merge_cmd_baton.mergeinfo_capable,
9828251881Speter                                        SVN_RA_CAPABILITY_MERGEINFO,
9829251881Speter                                        iterpool));
9830251881Speter          checked_mergeinfo_capability = TRUE;
9831251881Speter        }
9832251881Speter
9833251881Speter      SVN_ERR(svn_ra_check_path(ra_session1, "", source->loc1->rev,
9834251881Speter                                &src1_kind, iterpool));
9835251881Speter
9836251881Speter      /* Run the merge; if there are conflicts, allow the callback to
9837251881Speter       * resolve them, and if it resolves all of them, then run the
9838251881Speter       * merge again with the remaining revision range, until it is all
9839251881Speter       * done. */
9840251881Speter      do
9841251881Speter        {
9842251881Speter          /* Merge as far as possible without resolving any conflicts */
9843251881Speter          if (src1_kind != svn_node_dir)
9844251881Speter            {
9845251881Speter              SVN_ERR(do_file_merge(result_catalog, &conflicted_range_report,
9846251881Speter                                    source, target->abspath,
9847251881Speter                                    processor,
9848251881Speter                                    sources_related,
9849251881Speter                                    squelch_mergeinfo_notifications,
9850251881Speter                                    &merge_cmd_baton, iterpool, iterpool));
9851251881Speter            }
9852251881Speter          else /* Directory */
9853251881Speter            {
9854251881Speter              SVN_ERR(do_directory_merge(result_catalog, &conflicted_range_report,
9855251881Speter                                         source, target->abspath,
9856251881Speter                                         processor,
9857251881Speter                                         depth, squelch_mergeinfo_notifications,
9858251881Speter                                         &merge_cmd_baton, iterpool, iterpool));
9859251881Speter            }
9860251881Speter
9861251881Speter          /* Give the conflict resolver callback the opportunity to
9862251881Speter           * resolve any conflicts that were raised.  If it resolves all
9863251881Speter           * of them, go around again to merge the next sub-range (if any). */
9864251881Speter          if (conflicted_range_report && ctx->conflict_func2 && ! dry_run)
9865251881Speter            {
9866251881Speter              svn_boolean_t conflicts_remain;
9867251881Speter
9868251881Speter              SVN_ERR(svn_client__resolve_conflicts(
9869251881Speter                        &conflicts_remain, merge_cmd_baton.conflicted_paths,
9870251881Speter                        ctx, iterpool));
9871251881Speter              if (conflicts_remain)
9872251881Speter                break;
9873251881Speter
9874251881Speter              merge_cmd_baton.conflicted_paths = NULL;
9875251881Speter              /* Caution: this source is in iterpool */
9876251881Speter              source = conflicted_range_report->remaining_source;
9877251881Speter              conflicted_range_report = NULL;
9878251881Speter            }
9879251881Speter          else
9880251881Speter            break;
9881251881Speter        }
9882251881Speter      while (source);
9883251881Speter
9884251881Speter      /* The final mergeinfo on TARGET_WCPATH may itself elide. */
9885251881Speter      if (! dry_run)
9886251881Speter        SVN_ERR(svn_client__elide_mergeinfo(target->abspath, NULL,
9887251881Speter                                            ctx, iterpool));
9888251881Speter
9889251881Speter      /* If conflicts occurred while merging any but the very last
9890251881Speter       * range of a multi-pass merge, we raise an error that aborts
9891251881Speter       * the merge. The user will be asked to resolve conflicts
9892251881Speter       * before merging subsequent revision ranges. */
9893251881Speter      if (conflicted_range_report)
9894251881Speter        {
9895251881Speter          *conflict_report = conflict_report_create(
9896251881Speter                               target->abspath, conflicted_range_report->conflicted_range,
9897251881Speter                               (i == merge_sources->nelts - 1
9898251881Speter                                && ! conflicted_range_report->remaining_source),
9899251881Speter                               result_pool);
9900251881Speter          break;
9901251881Speter        }
9902251881Speter    }
9903251881Speter
9904251881Speter  if (! *conflict_report || (*conflict_report)->was_last_range)
9905251881Speter    {
9906251881Speter      /* Let everyone know we're finished here. */
9907251881Speter      notify_merge_completed(target->abspath, ctx, iterpool);
9908251881Speter    }
9909251881Speter
9910251881Speter  /* Does the caller want to know what the merge has done? */
9911251881Speter  if (modified_subtrees)
9912251881Speter    {
9913251881Speter      *modified_subtrees =
9914251881Speter          apr_hash_overlay(result_pool, *modified_subtrees,
9915251881Speter                           merge_cmd_baton.merged_abspaths);
9916251881Speter      *modified_subtrees =
9917251881Speter          apr_hash_overlay(result_pool, *modified_subtrees,
9918251881Speter                           merge_cmd_baton.added_abspaths);
9919251881Speter      *modified_subtrees =
9920251881Speter          apr_hash_overlay(result_pool, *modified_subtrees,
9921251881Speter                           merge_cmd_baton.skipped_abspaths);
9922251881Speter      *modified_subtrees =
9923251881Speter          apr_hash_overlay(result_pool, *modified_subtrees,
9924251881Speter                           merge_cmd_baton.tree_conflicted_abspaths);
9925251881Speter    }
9926251881Speter
9927251881Speter  if (src_session)
9928251881Speter    SVN_ERR(svn_ra_reparent(src_session, old_src_session_url, iterpool));
9929251881Speter
9930251881Speter  svn_pool_destroy(iterpool);
9931251881Speter  return SVN_NO_ERROR;
9932251881Speter}
9933251881Speter
9934251881Speter/* Perform a two-URL merge between URLs which are related, but neither
9935251881Speter   is a direct ancestor of the other.  This first does a real two-URL
9936251881Speter   merge (unless this is record-only), followed by record-only merges
9937251881Speter   to represent the changed mergeinfo.
9938251881Speter
9939251881Speter   Set *CONFLICT_REPORT to indicate if there were any conflicts, as in
9940251881Speter   do_merge().
9941251881Speter
9942251881Speter   The diff to be merged is between SOURCE->loc1 (in URL1_RA_SESSION1)
9943251881Speter   and SOURCE->loc2 (in URL2_RA_SESSION2); YCA is their youngest
9944251881Speter   common ancestor.
9945251881Speter
9946251881Speter   SAME_REPOS must be true if and only if the source URLs are in the same
9947251881Speter   repository as the target working copy.
9948251881Speter
9949251881Speter   DIFF_IGNORE_ANCESTRY is as in do_merge().
9950251881Speter
9951251881Speter   Other arguments are as in all of the public merge APIs.
9952251881Speter
9953251881Speter   *USE_SLEEP will be set TRUE if a sleep is required to ensure timestamp
9954251881Speter   integrity, *USE_SLEEP will be unchanged if no sleep is required.
9955251881Speter
9956251881Speter   SCRATCH_POOL is used for all temporary allocations.
9957251881Speter */
9958251881Speterstatic svn_error_t *
9959251881Spetermerge_cousins_and_supplement_mergeinfo(conflict_report_t **conflict_report,
9960251881Speter                                       svn_boolean_t *use_sleep,
9961251881Speter                                       const merge_target_t *target,
9962251881Speter                                       svn_ra_session_t *URL1_ra_session,
9963251881Speter                                       svn_ra_session_t *URL2_ra_session,
9964251881Speter                                       const merge_source_t *source,
9965251881Speter                                       const svn_client__pathrev_t *yca,
9966251881Speter                                       svn_boolean_t same_repos,
9967251881Speter                                       svn_depth_t depth,
9968251881Speter                                       svn_boolean_t diff_ignore_ancestry,
9969251881Speter                                       svn_boolean_t force_delete,
9970251881Speter                                       svn_boolean_t record_only,
9971251881Speter                                       svn_boolean_t dry_run,
9972251881Speter                                       const apr_array_header_t *merge_options,
9973251881Speter                                       svn_client_ctx_t *ctx,
9974251881Speter                                       apr_pool_t *result_pool,
9975251881Speter                                       apr_pool_t *scratch_pool)
9976251881Speter{
9977251881Speter  apr_array_header_t *remove_sources, *add_sources;
9978251881Speter  apr_hash_t *modified_subtrees = NULL;
9979251881Speter
9980251881Speter  /* Sure we could use SCRATCH_POOL throughout this function, but since this
9981251881Speter     is a wrapper around three separate merges we'll create a subpool we can
9982251881Speter     clear between each of the three.  If the merge target has a lot of
9983251881Speter     subtree mergeinfo, then this will help keep memory use in check. */
9984251881Speter  apr_pool_t *subpool = svn_pool_create(scratch_pool);
9985251881Speter
9986251881Speter  assert(session_url_is(URL1_ra_session, source->loc1->url, scratch_pool));
9987251881Speter  assert(session_url_is(URL2_ra_session, source->loc2->url, scratch_pool));
9988251881Speter
9989251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(target->abspath));
9990251881Speter  SVN_ERR_ASSERT(! source->ancestral);
9991251881Speter
9992251881Speter  SVN_ERR(normalize_merge_sources_internal(
9993251881Speter            &remove_sources, source->loc1,
9994251881Speter            svn_rangelist__initialize(source->loc1->rev, yca->rev, TRUE,
9995251881Speter                                      scratch_pool),
9996251881Speter            URL1_ra_session, ctx, scratch_pool, subpool));
9997251881Speter
9998251881Speter  SVN_ERR(normalize_merge_sources_internal(
9999251881Speter            &add_sources, source->loc2,
10000251881Speter            svn_rangelist__initialize(yca->rev, source->loc2->rev, TRUE,
10001251881Speter                                      scratch_pool),
10002251881Speter            URL2_ra_session, ctx, scratch_pool, subpool));
10003251881Speter
10004251881Speter  *conflict_report = NULL;
10005251881Speter
10006251881Speter  /* If this isn't a record-only merge, we'll first do a stupid
10007251881Speter     point-to-point merge... */
10008251881Speter  if (! record_only)
10009251881Speter    {
10010251881Speter      apr_array_header_t *faux_sources =
10011251881Speter        apr_array_make(scratch_pool, 1, sizeof(merge_source_t *));
10012251881Speter
10013251881Speter      modified_subtrees = apr_hash_make(scratch_pool);
10014251881Speter      APR_ARRAY_PUSH(faux_sources, const merge_source_t *) = source;
10015251881Speter      SVN_ERR(do_merge(&modified_subtrees, NULL, conflict_report, use_sleep,
10016251881Speter                       faux_sources, target,
10017251881Speter                       URL1_ra_session, TRUE, same_repos,
10018251881Speter                       FALSE /*ignore_mergeinfo*/, diff_ignore_ancestry,
10019251881Speter                       force_delete, dry_run, FALSE, NULL, TRUE,
10020251881Speter                       FALSE, depth, merge_options, ctx,
10021251881Speter                       scratch_pool, subpool));
10022251881Speter      if (*conflict_report)
10023251881Speter        {
10024251881Speter          *conflict_report = conflict_report_dup(*conflict_report, result_pool);
10025251881Speter          if (! (*conflict_report)->was_last_range)
10026251881Speter            return SVN_NO_ERROR;
10027251881Speter        }
10028251881Speter    }
10029251881Speter  else if (! same_repos)
10030251881Speter    {
10031251881Speter      return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
10032251881Speter                              _("Merge from foreign repository is not "
10033251881Speter                                "compatible with mergeinfo modification"));
10034251881Speter    }
10035251881Speter
10036251881Speter  /* ... and now, if we're doing the mergeinfo thang, we execute a
10037251881Speter     pair of record-only merges using the real sources we've
10038251881Speter     calculated.
10039251881Speter
10040251881Speter     Issue #3648: We don't actually perform these two record-only merges
10041251881Speter     on the WC at first, but rather see what each would do and store that
10042251881Speter     in two mergeinfo catalogs.  We then merge the catalogs together and
10043251881Speter     then record the result in the WC.  This prevents the second record
10044251881Speter     only merge from removing legitimate mergeinfo history, from the same
10045251881Speter     source, that was made in prior merges. */
10046251881Speter  if (same_repos && !dry_run)
10047251881Speter    {
10048251881Speter      svn_mergeinfo_catalog_t add_result_catalog =
10049251881Speter        apr_hash_make(scratch_pool);
10050251881Speter      svn_mergeinfo_catalog_t remove_result_catalog =
10051251881Speter        apr_hash_make(scratch_pool);
10052251881Speter
10053251881Speter      notify_mergeinfo_recording(target->abspath, NULL, ctx, scratch_pool);
10054251881Speter      svn_pool_clear(subpool);
10055251881Speter      SVN_ERR(do_merge(NULL, add_result_catalog, conflict_report, use_sleep,
10056251881Speter                       add_sources, target,
10057251881Speter                       URL1_ra_session, TRUE, same_repos,
10058251881Speter                       FALSE /*ignore_mergeinfo*/, diff_ignore_ancestry,
10059251881Speter                       force_delete, dry_run, TRUE,
10060251881Speter                       modified_subtrees, TRUE,
10061251881Speter                       TRUE, depth, merge_options, ctx,
10062251881Speter                       scratch_pool, subpool));
10063251881Speter      if (*conflict_report)
10064251881Speter        {
10065251881Speter          *conflict_report = conflict_report_dup(*conflict_report, result_pool);
10066251881Speter          if (! (*conflict_report)->was_last_range)
10067251881Speter            return SVN_NO_ERROR;
10068251881Speter        }
10069251881Speter      svn_pool_clear(subpool);
10070251881Speter      SVN_ERR(do_merge(NULL, remove_result_catalog, conflict_report, use_sleep,
10071251881Speter                       remove_sources, target,
10072251881Speter                       URL1_ra_session, TRUE, same_repos,
10073251881Speter                       FALSE /*ignore_mergeinfo*/, diff_ignore_ancestry,
10074251881Speter                       force_delete, dry_run, TRUE,
10075251881Speter                       modified_subtrees, TRUE,
10076251881Speter                       TRUE, depth, merge_options, ctx,
10077251881Speter                       scratch_pool, subpool));
10078251881Speter      if (*conflict_report)
10079251881Speter        {
10080251881Speter          *conflict_report = conflict_report_dup(*conflict_report, result_pool);
10081251881Speter          if (! (*conflict_report)->was_last_range)
10082251881Speter            return SVN_NO_ERROR;
10083251881Speter        }
10084251881Speter      SVN_ERR(svn_mergeinfo_catalog_merge(add_result_catalog,
10085251881Speter                                          remove_result_catalog,
10086251881Speter                                          scratch_pool, scratch_pool));
10087251881Speter      SVN_ERR(svn_client__record_wc_mergeinfo_catalog(add_result_catalog,
10088251881Speter                                                      ctx, scratch_pool));
10089251881Speter    }
10090251881Speter
10091251881Speter  svn_pool_destroy(subpool);
10092251881Speter  return SVN_NO_ERROR;
10093251881Speter}
10094251881Speter
10095251881Speter/* Perform checks to determine whether the working copy at TARGET_ABSPATH
10096251881Speter * can safely be used as a merge target. Checks are performed according to
10097251881Speter * the ALLOW_MIXED_REV, ALLOW_LOCAL_MODS, and ALLOW_SWITCHED_SUBTREES
10098251881Speter * parameters. If any checks fail, raise SVN_ERR_CLIENT_NOT_READY_TO_MERGE.
10099251881Speter *
10100251881Speter * E.g. if all the ALLOW_* parameters are FALSE, TARGET_ABSPATH must
10101251881Speter * be a single-revision, pristine, unswitched working copy.
10102251881Speter * In other words, it must reflect a subtree of the repository as found
10103251881Speter * at single revision -- although sparse checkouts are permitted. */
10104251881Speterstatic svn_error_t *
10105251881Speterensure_wc_is_suitable_merge_target(const char *target_abspath,
10106251881Speter                                   svn_client_ctx_t *ctx,
10107251881Speter                                   svn_boolean_t allow_mixed_rev,
10108251881Speter                                   svn_boolean_t allow_local_mods,
10109251881Speter                                   svn_boolean_t allow_switched_subtrees,
10110251881Speter                                   apr_pool_t *scratch_pool)
10111251881Speter{
10112251881Speter  svn_node_kind_t target_kind;
10113251881Speter
10114251881Speter  /* Check the target exists. */
10115251881Speter  SVN_ERR(svn_io_check_path(target_abspath, &target_kind, scratch_pool));
10116251881Speter  if (target_kind == svn_node_none)
10117251881Speter    return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
10118251881Speter                             _("Path '%s' does not exist"),
10119251881Speter                             svn_dirent_local_style(target_abspath,
10120251881Speter                                                    scratch_pool));
10121251881Speter  SVN_ERR(svn_wc_read_kind2(&target_kind, ctx->wc_ctx, target_abspath,
10122251881Speter                            FALSE, FALSE, scratch_pool));
10123251881Speter  if (target_kind != svn_node_dir && target_kind != svn_node_file)
10124251881Speter    return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
10125251881Speter                             _("Merge target '%s' does not exist in the "
10126251881Speter                               "working copy"), target_abspath);
10127251881Speter
10128251881Speter  /* Perform the mixed-revision check first because it's the cheapest one. */
10129251881Speter  if (! allow_mixed_rev)
10130251881Speter    {
10131251881Speter      svn_revnum_t min_rev;
10132251881Speter      svn_revnum_t max_rev;
10133251881Speter
10134251881Speter      SVN_ERR(svn_client_min_max_revisions(&min_rev, &max_rev, target_abspath,
10135251881Speter                                           FALSE, ctx, scratch_pool));
10136251881Speter
10137251881Speter      if (!(SVN_IS_VALID_REVNUM(min_rev) && SVN_IS_VALID_REVNUM(max_rev)))
10138251881Speter        {
10139251881Speter          svn_boolean_t is_added;
10140251881Speter
10141251881Speter          /* Allow merge into added nodes. */
10142251881Speter          SVN_ERR(svn_wc__node_is_added(&is_added, ctx->wc_ctx, target_abspath,
10143251881Speter                                        scratch_pool));
10144251881Speter          if (is_added)
10145251881Speter            return SVN_NO_ERROR;
10146251881Speter          else
10147251881Speter            return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
10148251881Speter                                    _("Cannot determine revision of working "
10149251881Speter                                      "copy"));
10150251881Speter        }
10151251881Speter
10152251881Speter      if (min_rev != max_rev)
10153251881Speter        return svn_error_createf(SVN_ERR_CLIENT_MERGE_UPDATE_REQUIRED, NULL,
10154251881Speter                                 _("Cannot merge into mixed-revision working "
10155251881Speter                                   "copy [%ld:%ld]; try updating first"),
10156251881Speter                                   min_rev, max_rev);
10157251881Speter    }
10158251881Speter
10159251881Speter  /* Next, check for switched subtrees. */
10160251881Speter  if (! allow_switched_subtrees)
10161251881Speter    {
10162251881Speter      svn_boolean_t is_switched;
10163251881Speter
10164251881Speter      SVN_ERR(svn_wc__has_switched_subtrees(&is_switched, ctx->wc_ctx,
10165251881Speter                                            target_abspath, NULL,
10166251881Speter                                            scratch_pool));
10167251881Speter      if (is_switched)
10168251881Speter        return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
10169251881Speter                                _("Cannot merge into a working copy "
10170251881Speter                                  "with a switched subtree"));
10171251881Speter    }
10172251881Speter
10173251881Speter  /* This is the most expensive check, so it is performed last.*/
10174251881Speter  if (! allow_local_mods)
10175251881Speter    {
10176251881Speter      svn_boolean_t is_modified;
10177251881Speter
10178251881Speter      SVN_ERR(svn_wc__has_local_mods(&is_modified, ctx->wc_ctx,
10179251881Speter                                     target_abspath,
10180251881Speter                                     ctx->cancel_func,
10181251881Speter                                     ctx->cancel_baton,
10182251881Speter                                     scratch_pool));
10183251881Speter      if (is_modified)
10184251881Speter        return svn_error_create(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
10185251881Speter                                _("Cannot merge into a working copy "
10186251881Speter                                  "that has local modifications"));
10187251881Speter    }
10188251881Speter
10189251881Speter  return SVN_NO_ERROR;
10190251881Speter}
10191251881Speter
10192251881Speter/* Throw an error if PATH_OR_URL is a path and REVISION isn't a repository
10193251881Speter * revision. */
10194251881Speterstatic svn_error_t *
10195251881Speterensure_wc_path_has_repo_revision(const char *path_or_url,
10196251881Speter                                 const svn_opt_revision_t *revision,
10197251881Speter                                 apr_pool_t *scratch_pool)
10198251881Speter{
10199251881Speter  if (revision->kind != svn_opt_revision_number
10200251881Speter      && revision->kind != svn_opt_revision_date
10201251881Speter      && revision->kind != svn_opt_revision_head
10202251881Speter      && ! svn_path_is_url(path_or_url))
10203251881Speter    return svn_error_createf(
10204251881Speter      SVN_ERR_CLIENT_BAD_REVISION, NULL,
10205251881Speter      _("Invalid merge source '%s'; a working copy path can only be "
10206251881Speter        "used with a repository revision (a number, a date, or head)"),
10207251881Speter      svn_dirent_local_style(path_or_url, scratch_pool));
10208251881Speter  return SVN_NO_ERROR;
10209251881Speter}
10210251881Speter
10211251881Speter/* "Open" the target WC for a merge.  That means:
10212251881Speter *   - find out its exact repository location
10213251881Speter *   - check the WC for suitability (throw an error if unsuitable)
10214251881Speter *
10215251881Speter * Set *TARGET_P to a new, fully initialized, target description structure.
10216251881Speter *
10217251881Speter * ALLOW_MIXED_REV, ALLOW_LOCAL_MODS, ALLOW_SWITCHED_SUBTREES determine
10218251881Speter * whether the WC is deemed suitable; see ensure_wc_is_suitable_merge_target()
10219251881Speter * for details.
10220251881Speter *
10221251881Speter * If the node is locally added, the rev and URL will be null/invalid. Some
10222251881Speter * kinds of merge can use such a target; others can't.
10223251881Speter */
10224251881Speterstatic svn_error_t *
10225251881Speteropen_target_wc(merge_target_t **target_p,
10226251881Speter               const char *wc_abspath,
10227251881Speter               svn_boolean_t allow_mixed_rev,
10228251881Speter               svn_boolean_t allow_local_mods,
10229251881Speter               svn_boolean_t allow_switched_subtrees,
10230251881Speter               svn_client_ctx_t *ctx,
10231251881Speter               apr_pool_t *result_pool,
10232251881Speter               apr_pool_t *scratch_pool)
10233251881Speter{
10234251881Speter  merge_target_t *target = apr_palloc(result_pool, sizeof(*target));
10235251881Speter  svn_client__pathrev_t *origin;
10236251881Speter
10237251881Speter  target->abspath = apr_pstrdup(result_pool, wc_abspath);
10238251881Speter
10239251881Speter  SVN_ERR(svn_client__wc_node_get_origin(&origin, wc_abspath, ctx,
10240251881Speter                                         result_pool, scratch_pool));
10241251881Speter  if (origin)
10242251881Speter    {
10243251881Speter      target->loc = *origin;
10244251881Speter    }
10245251881Speter  else
10246251881Speter    {
10247251881Speter      svn_error_t *err;
10248251881Speter      /* The node has no location in the repository. It's unversioned or
10249251881Speter       * locally added or locally deleted.
10250251881Speter       *
10251251881Speter       * If it's locally added or deleted, find the repository root
10252251881Speter       * URL and UUID anyway, and leave the node URL and revision as NULL
10253251881Speter       * and INVALID.  If it's unversioned, this will throw an error. */
10254251881Speter      err = svn_wc__node_get_repos_info(NULL, NULL,
10255251881Speter                                        &target->loc.repos_root_url,
10256251881Speter                                        &target->loc.repos_uuid,
10257251881Speter                                        ctx->wc_ctx, wc_abspath,
10258251881Speter                                        result_pool, scratch_pool);
10259251881Speter
10260251881Speter      if (err)
10261251881Speter        {
10262251881Speter          if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND
10263251881Speter              && err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY
10264251881Speter              && err->apr_err != SVN_ERR_WC_UPGRADE_REQUIRED)
10265251881Speter            return svn_error_trace(err);
10266251881Speter
10267251881Speter          return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, err,
10268251881Speter                                   _("Merge target '%s' does not exist in the "
10269251881Speter                                     "working copy"),
10270251881Speter                                   svn_dirent_local_style(wc_abspath,
10271251881Speter                                                          scratch_pool));
10272251881Speter        }
10273251881Speter
10274251881Speter      target->loc.rev = SVN_INVALID_REVNUM;
10275251881Speter      target->loc.url = NULL;
10276251881Speter    }
10277251881Speter
10278251881Speter  SVN_ERR(ensure_wc_is_suitable_merge_target(
10279251881Speter            wc_abspath, ctx,
10280251881Speter            allow_mixed_rev, allow_local_mods, allow_switched_subtrees,
10281251881Speter            scratch_pool));
10282251881Speter
10283251881Speter  *target_p = target;
10284251881Speter  return SVN_NO_ERROR;
10285251881Speter}
10286251881Speter
10287251881Speter/*-----------------------------------------------------------------------*/
10288251881Speter
10289251881Speter/*** Public APIs ***/
10290251881Speter
10291251881Speter/* The body of svn_client_merge5(), which see for details.
10292251881Speter *
10293251881Speter * If SOURCE1 @ REVISION1 is related to SOURCE2 @ REVISION2 then use merge
10294251881Speter * tracking (subject to other constraints -- see HONOR_MERGEINFO());
10295251881Speter * otherwise disable merge tracking.
10296251881Speter *
10297251881Speter * IGNORE_MERGEINFO and DIFF_IGNORE_ANCESTRY are as in do_merge().
10298251881Speter */
10299251881Speterstatic svn_error_t *
10300251881Spetermerge_locked(conflict_report_t **conflict_report,
10301251881Speter             const char *source1,
10302251881Speter             const svn_opt_revision_t *revision1,
10303251881Speter             const char *source2,
10304251881Speter             const svn_opt_revision_t *revision2,
10305251881Speter             const char *target_abspath,
10306251881Speter             svn_depth_t depth,
10307251881Speter             svn_boolean_t ignore_mergeinfo,
10308251881Speter             svn_boolean_t diff_ignore_ancestry,
10309251881Speter             svn_boolean_t force_delete,
10310251881Speter             svn_boolean_t record_only,
10311251881Speter             svn_boolean_t dry_run,
10312251881Speter             svn_boolean_t allow_mixed_rev,
10313251881Speter             const apr_array_header_t *merge_options,
10314251881Speter             svn_client_ctx_t *ctx,
10315251881Speter             apr_pool_t *result_pool,
10316251881Speter             apr_pool_t *scratch_pool)
10317251881Speter{
10318251881Speter  merge_target_t *target;
10319251881Speter  svn_client__pathrev_t *source1_loc, *source2_loc;
10320251881Speter  svn_boolean_t sources_related = FALSE;
10321251881Speter  svn_ra_session_t *ra_session1, *ra_session2;
10322251881Speter  apr_array_header_t *merge_sources;
10323251881Speter  svn_error_t *err;
10324251881Speter  svn_boolean_t use_sleep = FALSE;
10325251881Speter  svn_client__pathrev_t *yca = NULL;
10326251881Speter  apr_pool_t *sesspool;
10327251881Speter  svn_boolean_t same_repos;
10328251881Speter
10329251881Speter  /* ### FIXME: This function really ought to do a history check on
10330251881Speter     the left and right sides of the merge source, and -- if one is an
10331251881Speter     ancestor of the other -- just call svn_client_merge_peg3() with
10332251881Speter     the appropriate args. */
10333251881Speter
10334251881Speter  SVN_ERR(open_target_wc(&target, target_abspath,
10335251881Speter                         allow_mixed_rev, TRUE, TRUE,
10336251881Speter                         ctx, scratch_pool, scratch_pool));
10337251881Speter
10338251881Speter  /* Open RA sessions to both sides of our merge source, and resolve URLs
10339251881Speter   * and revisions. */
10340251881Speter  sesspool = svn_pool_create(scratch_pool);
10341251881Speter  SVN_ERR(svn_client__ra_session_from_path2(
10342251881Speter            &ra_session1, &source1_loc,
10343251881Speter            source1, NULL, revision1, revision1, ctx, sesspool));
10344251881Speter  SVN_ERR(svn_client__ra_session_from_path2(
10345251881Speter            &ra_session2, &source2_loc,
10346251881Speter            source2, NULL, revision2, revision2, ctx, sesspool));
10347251881Speter
10348251881Speter  /* We can't do a diff between different repositories. */
10349251881Speter  /* ### We should also insist that the root URLs of the two sources match,
10350251881Speter   *     as we are only carrying around a single source-repos-root from now
10351251881Speter   *     on, and URL calculations will go wrong if they differ.
10352251881Speter   *     Alternatively, teach the code to cope with differing root URLs. */
10353251881Speter  SVN_ERR(check_same_repos(source1_loc, source1_loc->url,
10354251881Speter                           source2_loc, source2_loc->url,
10355251881Speter                           FALSE /* strict_urls */, scratch_pool));
10356251881Speter
10357251881Speter  /* Do our working copy and sources come from the same repository? */
10358251881Speter  same_repos = is_same_repos(&target->loc, source1_loc, TRUE /* strict_urls */);
10359251881Speter
10360251881Speter  /* Unless we're ignoring ancestry, see if the two sources are related.  */
10361251881Speter  if (! ignore_mergeinfo)
10362251881Speter    SVN_ERR(svn_client__get_youngest_common_ancestor(
10363251881Speter                    &yca, source1_loc, source2_loc, ra_session1, ctx,
10364251881Speter                    scratch_pool, scratch_pool));
10365251881Speter
10366251881Speter  /* Check for a youngest common ancestor.  If we have one, we'll be
10367251881Speter     doing merge tracking.
10368251881Speter
10369251881Speter     So, given a requested merge of the differences between A and
10370251881Speter     B, and a common ancestor of C, we will find ourselves in one of
10371251881Speter     four positions, and four different approaches:
10372251881Speter
10373251881Speter        A == B == C   there's nothing to merge
10374251881Speter
10375251881Speter        A == C != B   we merge the changes between A (or C) and B
10376251881Speter
10377251881Speter        B == C != A   we merge the changes between B (or C) and A
10378251881Speter
10379251881Speter        A != B != C   we merge the changes between A and B without
10380251881Speter                      merge recording, then record-only two merges:
10381251881Speter                      from A to C, and from C to B
10382251881Speter  */
10383251881Speter  if (yca)
10384251881Speter    {
10385251881Speter      /* Note that our merge sources are related. */
10386251881Speter      sources_related = TRUE;
10387251881Speter
10388251881Speter      /* If the common ancestor matches the right side of our merge,
10389251881Speter         then we only need to reverse-merge the left side. */
10390251881Speter      if ((strcmp(yca->url, source2_loc->url) == 0)
10391251881Speter          && (yca->rev == source2_loc->rev))
10392251881Speter        {
10393251881Speter          SVN_ERR(normalize_merge_sources_internal(
10394251881Speter                    &merge_sources, source1_loc,
10395251881Speter                    svn_rangelist__initialize(source1_loc->rev, yca->rev, TRUE,
10396251881Speter                                              scratch_pool),
10397251881Speter                    ra_session1, ctx, scratch_pool, scratch_pool));
10398251881Speter        }
10399251881Speter      /* If the common ancestor matches the left side of our merge,
10400251881Speter         then we only need to merge the right side. */
10401251881Speter      else if ((strcmp(yca->url, source1_loc->url) == 0)
10402251881Speter               && (yca->rev == source1_loc->rev))
10403251881Speter        {
10404251881Speter          SVN_ERR(normalize_merge_sources_internal(
10405251881Speter                    &merge_sources, source2_loc,
10406251881Speter                    svn_rangelist__initialize(yca->rev, source2_loc->rev, TRUE,
10407251881Speter                                              scratch_pool),
10408251881Speter                    ra_session2, ctx, scratch_pool, scratch_pool));
10409251881Speter        }
10410251881Speter      /* And otherwise, we need to do both: reverse merge the left
10411251881Speter         side, and merge the right. */
10412251881Speter      else
10413251881Speter        {
10414251881Speter          merge_source_t source;
10415251881Speter
10416251881Speter          source.loc1 = source1_loc;
10417251881Speter          source.loc2 = source2_loc;
10418251881Speter          source.ancestral = FALSE;
10419251881Speter
10420251881Speter          err = merge_cousins_and_supplement_mergeinfo(conflict_report,
10421251881Speter                                                       &use_sleep,
10422251881Speter                                                       target,
10423251881Speter                                                       ra_session1,
10424251881Speter                                                       ra_session2,
10425251881Speter                                                       &source,
10426251881Speter                                                       yca,
10427251881Speter                                                       same_repos,
10428251881Speter                                                       depth,
10429251881Speter                                                       diff_ignore_ancestry,
10430251881Speter                                                       force_delete,
10431251881Speter                                                       record_only, dry_run,
10432251881Speter                                                       merge_options,
10433251881Speter                                                       ctx,
10434251881Speter                                                       result_pool,
10435251881Speter                                                       scratch_pool);
10436251881Speter          /* Close our temporary RA sessions (this could've happened
10437251881Speter             after the second call to normalize_merge_sources() inside
10438251881Speter             the merge_cousins_and_supplement_mergeinfo() routine). */
10439251881Speter          svn_pool_destroy(sesspool);
10440251881Speter
10441251881Speter          if (use_sleep)
10442251881Speter            svn_io_sleep_for_timestamps(target->abspath, scratch_pool);
10443251881Speter
10444251881Speter          SVN_ERR(err);
10445251881Speter          return SVN_NO_ERROR;
10446251881Speter        }
10447251881Speter    }
10448251881Speter  else
10449251881Speter    {
10450251881Speter      merge_source_t source;
10451251881Speter
10452251881Speter      source.loc1 = source1_loc;
10453251881Speter      source.loc2 = source2_loc;
10454251881Speter      source.ancestral = FALSE;
10455251881Speter
10456251881Speter      /* Build a single-item merge_source_t array. */
10457251881Speter      merge_sources = apr_array_make(scratch_pool, 1, sizeof(merge_source_t *));
10458251881Speter      APR_ARRAY_PUSH(merge_sources, merge_source_t *) = &source;
10459251881Speter    }
10460251881Speter
10461251881Speter  err = do_merge(NULL, NULL, conflict_report, &use_sleep,
10462251881Speter                 merge_sources, target,
10463251881Speter                 ra_session1, sources_related, same_repos,
10464251881Speter                 ignore_mergeinfo, diff_ignore_ancestry, force_delete, dry_run,
10465251881Speter                 record_only, NULL, FALSE, FALSE, depth, merge_options,
10466251881Speter                 ctx, result_pool, scratch_pool);
10467251881Speter
10468251881Speter  /* Close our temporary RA sessions. */
10469251881Speter  svn_pool_destroy(sesspool);
10470251881Speter
10471251881Speter  if (use_sleep)
10472251881Speter    svn_io_sleep_for_timestamps(target->abspath, scratch_pool);
10473251881Speter
10474251881Speter  SVN_ERR(err);
10475251881Speter  return SVN_NO_ERROR;
10476251881Speter}
10477251881Speter
10478251881Speter/* Set *TARGET_ABSPATH to the absolute path of, and *LOCK_ABSPATH to
10479251881Speter the absolute path to lock for, TARGET_WCPATH. */
10480251881Speterstatic svn_error_t *
10481251881Speterget_target_and_lock_abspath(const char **target_abspath,
10482251881Speter                            const char **lock_abspath,
10483251881Speter                            const char *target_wcpath,
10484251881Speter                            svn_client_ctx_t *ctx,
10485251881Speter                            apr_pool_t *result_pool)
10486251881Speter{
10487251881Speter  svn_node_kind_t kind;
10488251881Speter  SVN_ERR(svn_dirent_get_absolute(target_abspath, target_wcpath,
10489251881Speter                                  result_pool));
10490251881Speter  SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, *target_abspath,
10491251881Speter                            FALSE, FALSE, result_pool));
10492251881Speter  if (kind == svn_node_dir)
10493251881Speter    *lock_abspath = *target_abspath;
10494251881Speter  else
10495251881Speter    *lock_abspath = svn_dirent_dirname(*target_abspath, result_pool);
10496251881Speter
10497251881Speter  return SVN_NO_ERROR;
10498251881Speter}
10499251881Speter
10500251881Spetersvn_error_t *
10501251881Spetersvn_client_merge5(const char *source1,
10502251881Speter                  const svn_opt_revision_t *revision1,
10503251881Speter                  const char *source2,
10504251881Speter                  const svn_opt_revision_t *revision2,
10505251881Speter                  const char *target_wcpath,
10506251881Speter                  svn_depth_t depth,
10507251881Speter                  svn_boolean_t ignore_mergeinfo,
10508251881Speter                  svn_boolean_t diff_ignore_ancestry,
10509251881Speter                  svn_boolean_t force_delete,
10510251881Speter                  svn_boolean_t record_only,
10511251881Speter                  svn_boolean_t dry_run,
10512251881Speter                  svn_boolean_t allow_mixed_rev,
10513251881Speter                  const apr_array_header_t *merge_options,
10514251881Speter                  svn_client_ctx_t *ctx,
10515251881Speter                  apr_pool_t *pool)
10516251881Speter{
10517251881Speter  const char *target_abspath, *lock_abspath;
10518251881Speter  conflict_report_t *conflict_report;
10519251881Speter
10520251881Speter  /* Sanity check our input -- we require specified revisions,
10521251881Speter   * and either 2 paths or 2 URLs. */
10522251881Speter  if ((revision1->kind == svn_opt_revision_unspecified)
10523251881Speter      || (revision2->kind == svn_opt_revision_unspecified))
10524251881Speter    return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL,
10525251881Speter                            _("Not all required revisions are specified"));
10526251881Speter  if (svn_path_is_url(source1) != svn_path_is_url(source2))
10527251881Speter    return svn_error_create(SVN_ERR_ILLEGAL_TARGET, NULL,
10528251881Speter                            _("Merge sources must both be "
10529251881Speter                              "either paths or URLs"));
10530251881Speter  /* A WC path must be used with a repository revision, as we can't
10531251881Speter   * (currently) use the WC itself as a source, we can only read the URL
10532251881Speter   * from it and use that. */
10533251881Speter  SVN_ERR(ensure_wc_path_has_repo_revision(source1, revision1, pool));
10534251881Speter  SVN_ERR(ensure_wc_path_has_repo_revision(source2, revision2, pool));
10535251881Speter
10536251881Speter  SVN_ERR(get_target_and_lock_abspath(&target_abspath, &lock_abspath,
10537251881Speter                                      target_wcpath, ctx, pool));
10538251881Speter
10539251881Speter  if (!dry_run)
10540251881Speter    SVN_WC__CALL_WITH_WRITE_LOCK(
10541251881Speter      merge_locked(&conflict_report,
10542251881Speter                   source1, revision1, source2, revision2,
10543251881Speter                   target_abspath, depth, ignore_mergeinfo,
10544251881Speter                   diff_ignore_ancestry,
10545251881Speter                   force_delete, record_only, dry_run,
10546251881Speter                   allow_mixed_rev, merge_options, ctx, pool, pool),
10547251881Speter      ctx->wc_ctx, lock_abspath, FALSE /* lock_anchor */, pool);
10548251881Speter  else
10549251881Speter    SVN_ERR(merge_locked(&conflict_report,
10550251881Speter                   source1, revision1, source2, revision2,
10551251881Speter                   target_abspath, depth, ignore_mergeinfo,
10552251881Speter                   diff_ignore_ancestry,
10553251881Speter                   force_delete, record_only, dry_run,
10554251881Speter                   allow_mixed_rev, merge_options, ctx, pool, pool));
10555251881Speter
10556251881Speter  SVN_ERR(make_merge_conflict_error(conflict_report, pool));
10557251881Speter  return SVN_NO_ERROR;
10558251881Speter}
10559251881Speter
10560251881Speter
10561251881Speter/* Check if mergeinfo for a given path is described explicitly or via
10562251881Speter   inheritance in a mergeinfo catalog.
10563251881Speter
10564251881Speter   If REPOS_REL_PATH exists in CATALOG and has mergeinfo containing
10565251881Speter   MERGEINFO, then set *IN_CATALOG to TRUE.  If REPOS_REL_PATH does
10566251881Speter   not exist in CATALOG, then find its nearest parent which does exist.
10567251881Speter   If the mergeinfo REPOS_REL_PATH would inherit from that parent
10568251881Speter   contains MERGEINFO then set *IN_CATALOG to TRUE.  Set *IN_CATALOG
10569251881Speter   to FALSE in all other cases.
10570251881Speter
10571251881Speter   Set *CAT_KEY_PATH to the key path in CATALOG for REPOS_REL_PATH's
10572251881Speter   explicit or inherited mergeinfo.  If no explicit or inherited mergeinfo
10573251881Speter   is found for REPOS_REL_PATH then set *CAT_KEY_PATH to NULL.
10574251881Speter
10575251881Speter   User RESULT_POOL to allocate *CAT_KEY_PATH.  Use SCRATCH_POOL for
10576251881Speter   temporary allocations. */
10577251881Speterstatic svn_error_t *
10578251881Spetermergeinfo_in_catalog(svn_boolean_t *in_catalog,
10579251881Speter                     const char **cat_key_path,
10580251881Speter                     const char *repos_rel_path,
10581251881Speter                     svn_mergeinfo_t mergeinfo,
10582251881Speter                     svn_mergeinfo_catalog_t catalog,
10583251881Speter                     apr_pool_t *result_pool,
10584251881Speter                     apr_pool_t *scratch_pool)
10585251881Speter{
10586251881Speter  const char *walk_path = NULL;
10587251881Speter
10588251881Speter  *in_catalog = FALSE;
10589251881Speter  *cat_key_path = NULL;
10590251881Speter
10591251881Speter  if (mergeinfo && catalog && apr_hash_count(catalog))
10592251881Speter    {
10593251881Speter      const char *path = repos_rel_path;
10594251881Speter
10595251881Speter      /* Start with the assumption there is no explicit or inherited
10596251881Speter         mergeinfo for REPOS_REL_PATH in CATALOG. */
10597251881Speter      svn_mergeinfo_t mergeinfo_in_cat = NULL;
10598251881Speter
10599251881Speter      while (1)
10600251881Speter        {
10601251881Speter          mergeinfo_in_cat = svn_hash_gets(catalog, path);
10602251881Speter
10603251881Speter          if (mergeinfo_in_cat) /* Found it! */
10604251881Speter            {
10605251881Speter              *cat_key_path = apr_pstrdup(result_pool, path);
10606251881Speter              break;
10607251881Speter            }
10608251881Speter          else /* Look for inherited mergeinfo. */
10609251881Speter            {
10610251881Speter              walk_path = svn_relpath_join(svn_relpath_basename(path,
10611251881Speter                                                                scratch_pool),
10612251881Speter                                           walk_path ? walk_path : "",
10613251881Speter                                           scratch_pool);
10614251881Speter              path = svn_relpath_dirname(path, scratch_pool);
10615251881Speter
10616251881Speter              if (path[0] == '\0') /* No mergeinfo to inherit. */
10617251881Speter                break;
10618251881Speter            }
10619251881Speter        }
10620251881Speter
10621251881Speter      if (mergeinfo_in_cat)
10622251881Speter        {
10623251881Speter          if (walk_path)
10624251881Speter            SVN_ERR(svn_mergeinfo__add_suffix_to_mergeinfo(&mergeinfo_in_cat,
10625251881Speter                                                           mergeinfo_in_cat,
10626251881Speter                                                           walk_path,
10627251881Speter                                                           scratch_pool,
10628251881Speter                                                           scratch_pool));
10629251881Speter          SVN_ERR(svn_mergeinfo_intersect2(&mergeinfo_in_cat,
10630251881Speter                                           mergeinfo_in_cat, mergeinfo,
10631251881Speter                                           TRUE,
10632251881Speter                                           scratch_pool, scratch_pool));
10633251881Speter          SVN_ERR(svn_mergeinfo__equals(in_catalog, mergeinfo_in_cat,
10634251881Speter                                        mergeinfo, TRUE, scratch_pool));
10635251881Speter        }
10636251881Speter    }
10637251881Speter
10638251881Speter  return SVN_NO_ERROR;
10639251881Speter}
10640251881Speter
10641251881Speter/* A svn_log_entry_receiver_t baton for log_find_operative_revs(). */
10642251881Spetertypedef struct log_find_operative_baton_t
10643251881Speter{
10644251881Speter  /* The catalog of explicit mergeinfo on a reintegrate source. */
10645251881Speter  svn_mergeinfo_catalog_t merged_catalog;
10646251881Speter
10647251881Speter  /* The catalog of unmerged history from the reintegrate target to
10648251881Speter     the source which we will create.  Allocated in RESULT_POOL. */
10649251881Speter  svn_mergeinfo_catalog_t unmerged_catalog;
10650251881Speter
10651251881Speter  /* The repository absolute path of the reintegrate target. */
10652251881Speter  const char *target_fspath;
10653251881Speter
10654251881Speter  /* The path of the reintegrate source relative to the repository root. */
10655251881Speter  const char *source_repos_rel_path;
10656251881Speter
10657251881Speter  apr_pool_t *result_pool;
10658251881Speter} log_find_operative_baton_t;
10659251881Speter
10660251881Speter/* A svn_log_entry_receiver_t callback for find_unsynced_ranges(). */
10661251881Speterstatic svn_error_t *
10662251881Speterlog_find_operative_revs(void *baton,
10663251881Speter                        svn_log_entry_t *log_entry,
10664251881Speter                        apr_pool_t *pool)
10665251881Speter{
10666251881Speter  log_find_operative_baton_t *log_baton = baton;
10667251881Speter  apr_hash_index_t *hi;
10668251881Speter  svn_revnum_t revision;
10669251881Speter
10670251881Speter  /* It's possible that authz restrictions on the merge source prevent us
10671251881Speter     from knowing about any of the changes for LOG_ENTRY->REVISION. */
10672251881Speter  if (!log_entry->changed_paths2)
10673251881Speter    return SVN_NO_ERROR;
10674251881Speter
10675251881Speter  revision = log_entry->revision;
10676251881Speter
10677251881Speter  for (hi = apr_hash_first(pool, log_entry->changed_paths2);
10678251881Speter       hi;
10679251881Speter       hi = apr_hash_next(hi))
10680251881Speter    {
10681251881Speter      const char *subtree_missing_this_rev;
10682251881Speter      const char *path = svn__apr_hash_index_key(hi);
10683251881Speter      const char *rel_path;
10684251881Speter      const char *source_rel_path;
10685251881Speter      svn_boolean_t in_catalog;
10686251881Speter      svn_mergeinfo_t log_entry_as_mergeinfo;
10687251881Speter
10688251881Speter      rel_path = svn_fspath__skip_ancestor(log_baton->target_fspath, path);
10689251881Speter      /* Easy out: The path is not within the tree of interest. */
10690251881Speter      if (rel_path == NULL)
10691251881Speter        continue;
10692251881Speter
10693251881Speter      source_rel_path = svn_relpath_join(log_baton->source_repos_rel_path,
10694251881Speter                                         rel_path, pool);
10695251881Speter
10696251881Speter      SVN_ERR(svn_mergeinfo_parse(&log_entry_as_mergeinfo,
10697251881Speter                                  apr_psprintf(pool, "%s:%ld",
10698251881Speter                                               path, revision),
10699251881Speter                                  pool));
10700251881Speter
10701251881Speter      SVN_ERR(mergeinfo_in_catalog(&in_catalog, &subtree_missing_this_rev,
10702251881Speter                                   source_rel_path, log_entry_as_mergeinfo,
10703251881Speter                                   log_baton->merged_catalog,
10704251881Speter                                   pool, pool));
10705251881Speter
10706251881Speter      if (!in_catalog)
10707251881Speter        {
10708251881Speter          svn_mergeinfo_t unmerged_for_key;
10709251881Speter          const char *suffix, *missing_path;
10710251881Speter
10711251881Speter          /* If there is no mergeinfo on the source tree we'll say
10712251881Speter             the "subtree" missing this revision is the root of the
10713251881Speter             source. */
10714251881Speter          if (!subtree_missing_this_rev)
10715251881Speter            subtree_missing_this_rev = log_baton->source_repos_rel_path;
10716251881Speter
10717251881Speter          suffix = svn_relpath_skip_ancestor(subtree_missing_this_rev,
10718251881Speter                                             source_rel_path);
10719251881Speter          if (suffix)
10720251881Speter            {
10721251881Speter              missing_path = apr_pstrmemdup(pool, path,
10722251881Speter                                            strlen(path) - strlen(suffix) - 1);
10723251881Speter            }
10724251881Speter          else
10725251881Speter            {
10726251881Speter              missing_path = path;
10727251881Speter            }
10728251881Speter
10729251881Speter          SVN_ERR(svn_mergeinfo_parse(&log_entry_as_mergeinfo,
10730251881Speter                                      apr_psprintf(pool, "%s:%ld",
10731251881Speter                                                   missing_path, revision),
10732251881Speter                                      log_baton->result_pool));
10733251881Speter          unmerged_for_key = svn_hash_gets(log_baton->unmerged_catalog,
10734251881Speter                                           subtree_missing_this_rev);
10735251881Speter
10736251881Speter          if (unmerged_for_key)
10737251881Speter            {
10738251881Speter              SVN_ERR(svn_mergeinfo_merge2(unmerged_for_key,
10739251881Speter                                           log_entry_as_mergeinfo,
10740251881Speter                                           log_baton->result_pool,
10741251881Speter                                           pool));
10742251881Speter            }
10743251881Speter          else
10744251881Speter            {
10745251881Speter              svn_hash_sets(log_baton->unmerged_catalog,
10746251881Speter                            apr_pstrdup(log_baton->result_pool,
10747251881Speter                                        subtree_missing_this_rev),
10748251881Speter                            log_entry_as_mergeinfo);
10749251881Speter            }
10750251881Speter
10751251881Speter        }
10752251881Speter    }
10753251881Speter  return SVN_NO_ERROR;
10754251881Speter}
10755251881Speter
10756251881Speter/* Determine if the mergeinfo on a reintegrate source SOURCE_LOC,
10757251881Speter   reflects that the source is fully synced with the reintegrate target
10758251881Speter   TARGET_LOC, even if a naive interpretation of the source's
10759251881Speter   mergeinfo says otherwise -- See issue #3577.
10760251881Speter
10761251881Speter   UNMERGED_CATALOG represents the history (as mergeinfo) from
10762251881Speter   TARGET_LOC that is not represented in SOURCE_LOC's
10763251881Speter   explicit/inherited mergeinfo as represented by MERGED_CATALOG.
10764251881Speter   MERGEINFO_CATALOG may be empty if the source has no explicit or inherited
10765251881Speter   mergeinfo.
10766251881Speter
10767251881Speter   Check that all of the unmerged revisions in UNMERGED_CATALOG's
10768251881Speter   mergeinfos are "phantoms", that is, one of the following conditions holds:
10769251881Speter
10770251881Speter     1) The revision affects no corresponding paths in SOURCE_LOC.
10771251881Speter
10772251881Speter     2) The revision affects corresponding paths in SOURCE_LOC,
10773251881Speter        but based on the mergeinfo in MERGED_CATALOG, the change was
10774251881Speter        previously merged.
10775251881Speter
10776251881Speter   Make a deep copy, allocated in RESULT_POOL, of any portions of
10777251881Speter   UNMERGED_CATALOG that are not phantoms, to TRUE_UNMERGED_CATALOG.
10778251881Speter
10779251881Speter   Note: The keys in all mergeinfo catalogs used here are relative to the
10780251881Speter   root of the repository.
10781251881Speter
10782251881Speter   RA_SESSION is an RA session open to the repository of TARGET_LOC; it may
10783251881Speter   be temporarily reparented within this function.
10784251881Speter
10785251881Speter   Use SCRATCH_POOL for all temporary allocations. */
10786251881Speterstatic svn_error_t *
10787251881Speterfind_unsynced_ranges(const svn_client__pathrev_t *source_loc,
10788251881Speter                     const svn_client__pathrev_t *target_loc,
10789251881Speter                     svn_mergeinfo_catalog_t unmerged_catalog,
10790251881Speter                     svn_mergeinfo_catalog_t merged_catalog,
10791251881Speter                     svn_mergeinfo_catalog_t true_unmerged_catalog,
10792251881Speter                     svn_ra_session_t *ra_session,
10793251881Speter                     apr_pool_t *result_pool,
10794251881Speter                     apr_pool_t *scratch_pool)
10795251881Speter{
10796251881Speter  svn_rangelist_t *potentially_unmerged_ranges = NULL;
10797251881Speter
10798251881Speter  /* Convert all the unmerged history to a rangelist. */
10799251881Speter  if (apr_hash_count(unmerged_catalog))
10800251881Speter    {
10801251881Speter      apr_hash_index_t *hi_catalog;
10802251881Speter
10803251881Speter      potentially_unmerged_ranges =
10804251881Speter        apr_array_make(scratch_pool, 1, sizeof(svn_merge_range_t *));
10805251881Speter
10806251881Speter      for (hi_catalog = apr_hash_first(scratch_pool, unmerged_catalog);
10807251881Speter           hi_catalog;
10808251881Speter           hi_catalog = apr_hash_next(hi_catalog))
10809251881Speter        {
10810251881Speter          svn_mergeinfo_t mergeinfo = svn__apr_hash_index_val(hi_catalog);
10811251881Speter
10812251881Speter          SVN_ERR(svn_rangelist__merge_many(potentially_unmerged_ranges,
10813251881Speter                                            mergeinfo,
10814251881Speter                                            scratch_pool, scratch_pool));
10815251881Speter        }
10816251881Speter    }
10817251881Speter
10818251881Speter  /* Find any unmerged revisions which both affect the source and
10819251881Speter     are not yet merged to it. */
10820251881Speter  if (potentially_unmerged_ranges)
10821251881Speter    {
10822251881Speter      svn_revnum_t oldest_rev =
10823251881Speter        (APR_ARRAY_IDX(potentially_unmerged_ranges,
10824251881Speter                       0,
10825251881Speter                       svn_merge_range_t *))->start + 1;
10826251881Speter      svn_revnum_t youngest_rev =
10827251881Speter        (APR_ARRAY_IDX(potentially_unmerged_ranges,
10828251881Speter                       potentially_unmerged_ranges->nelts - 1,
10829251881Speter                       svn_merge_range_t *))->end;
10830251881Speter      log_find_operative_baton_t log_baton;
10831251881Speter      const char *old_session_url;
10832251881Speter      svn_error_t *err;
10833251881Speter
10834251881Speter      log_baton.merged_catalog = merged_catalog;
10835251881Speter      log_baton.unmerged_catalog = true_unmerged_catalog;
10836251881Speter      log_baton.source_repos_rel_path
10837251881Speter        = svn_client__pathrev_relpath(source_loc, scratch_pool);
10838251881Speter      log_baton.target_fspath
10839251881Speter        = svn_client__pathrev_fspath(target_loc, scratch_pool);
10840251881Speter      log_baton.result_pool = result_pool;
10841251881Speter
10842251881Speter      SVN_ERR(svn_client__ensure_ra_session_url(
10843251881Speter                &old_session_url, ra_session, target_loc->url, scratch_pool));
10844251881Speter      err = get_log(ra_session, "", youngest_rev, oldest_rev,
10845251881Speter                    TRUE, /* discover_changed_paths */
10846251881Speter                    log_find_operative_revs, &log_baton,
10847251881Speter                    scratch_pool);
10848251881Speter      SVN_ERR(svn_error_compose_create(
10849251881Speter                err, svn_ra_reparent(ra_session, old_session_url, scratch_pool)));
10850251881Speter    }
10851251881Speter
10852251881Speter  return SVN_NO_ERROR;
10853251881Speter}
10854251881Speter
10855251881Speter
10856251881Speter/* Find the youngest revision that has been merged from target to source.
10857251881Speter *
10858251881Speter * If any location in TARGET_HISTORY_AS_MERGEINFO is mentioned in
10859251881Speter * SOURCE_MERGEINFO, then we know that at least one merge was done from the
10860251881Speter * target to the source.  In that case, set *YOUNGEST_MERGED_REV to the
10861251881Speter * youngest revision of that intersection (unless *YOUNGEST_MERGED_REV is
10862251881Speter * already younger than that).  Otherwise, leave *YOUNGEST_MERGED_REV alone.
10863251881Speter */
10864251881Speterstatic svn_error_t *
10865251881Speterfind_youngest_merged_rev(svn_revnum_t *youngest_merged_rev,
10866251881Speter                         svn_mergeinfo_t target_history_as_mergeinfo,
10867251881Speter                         svn_mergeinfo_t source_mergeinfo,
10868251881Speter                         apr_pool_t *scratch_pool)
10869251881Speter{
10870251881Speter  svn_mergeinfo_t explicit_source_target_history_intersection;
10871251881Speter
10872251881Speter  SVN_ERR(svn_mergeinfo_intersect2(
10873251881Speter            &explicit_source_target_history_intersection,
10874251881Speter            source_mergeinfo, target_history_as_mergeinfo, TRUE,
10875251881Speter            scratch_pool, scratch_pool));
10876251881Speter  if (apr_hash_count(explicit_source_target_history_intersection))
10877251881Speter    {
10878251881Speter      svn_revnum_t old_rev, young_rev;
10879251881Speter
10880251881Speter      /* Keep track of the youngest revision merged from target to source. */
10881251881Speter      SVN_ERR(svn_mergeinfo__get_range_endpoints(
10882251881Speter                &young_rev, &old_rev,
10883251881Speter                explicit_source_target_history_intersection, scratch_pool));
10884251881Speter      if (!SVN_IS_VALID_REVNUM(*youngest_merged_rev)
10885251881Speter          || (young_rev > *youngest_merged_rev))
10886251881Speter        *youngest_merged_rev = young_rev;
10887251881Speter    }
10888251881Speter
10889251881Speter  return SVN_NO_ERROR;
10890251881Speter}
10891251881Speter
10892251881Speter/* Set *FILTERED_MERGEINFO_P to the parts of TARGET_HISTORY_AS_MERGEINFO
10893251881Speter * that are not present in the source branch.
10894251881Speter *
10895251881Speter * SOURCE_MERGEINFO is the explicit or inherited mergeinfo of the source
10896251881Speter * branch SOURCE_PATHREV.  Extend SOURCE_MERGEINFO, modifying it in
10897251881Speter * place, to include the natural history (implicit mergeinfo) of
10898251881Speter * SOURCE_PATHREV.  ### But make these additions in SCRATCH_POOL.
10899251881Speter *
10900251881Speter * SOURCE_RA_SESSION is an RA session open to the repository containing
10901251881Speter * SOURCE_PATHREV; it may be temporarily reparented within this function.
10902251881Speter *
10903251881Speter * ### [JAF] This function is named '..._subroutine' simply because I
10904251881Speter *     factored it out based on code similarity, without knowing what it's
10905251881Speter *     purpose is.  We should clarify its purpose and choose a better name.
10906251881Speter */
10907251881Speterstatic svn_error_t *
10908251881Speterfind_unmerged_mergeinfo_subroutine(svn_mergeinfo_t *filtered_mergeinfo_p,
10909251881Speter                                   svn_mergeinfo_t target_history_as_mergeinfo,
10910251881Speter                                   svn_mergeinfo_t source_mergeinfo,
10911251881Speter                                   const svn_client__pathrev_t *source_pathrev,
10912251881Speter                                   svn_ra_session_t *source_ra_session,
10913251881Speter                                   svn_client_ctx_t *ctx,
10914251881Speter                                   apr_pool_t *result_pool,
10915251881Speter                                   apr_pool_t *scratch_pool)
10916251881Speter{
10917251881Speter  svn_mergeinfo_t source_history_as_mergeinfo;
10918251881Speter
10919251881Speter  /* Get the source path's natural history and merge it into source
10920251881Speter     path's explicit or inherited mergeinfo. */
10921251881Speter  SVN_ERR(svn_client__get_history_as_mergeinfo(
10922251881Speter            &source_history_as_mergeinfo, NULL /* has_rev_zero_history */,
10923251881Speter            source_pathrev, source_pathrev->rev, SVN_INVALID_REVNUM,
10924251881Speter            source_ra_session, ctx, scratch_pool));
10925251881Speter  SVN_ERR(svn_mergeinfo_merge2(source_mergeinfo,
10926251881Speter                               source_history_as_mergeinfo,
10927251881Speter                               scratch_pool, scratch_pool));
10928251881Speter
10929251881Speter  /* Now source_mergeinfo represents everything we know about
10930251881Speter     source_path's history.  Now we need to know what part, if any, of the
10931251881Speter     corresponding target's history is *not* part of source_path's total
10932251881Speter     history; because it is neither shared history nor was it ever merged
10933251881Speter     from the target to the source. */
10934251881Speter  SVN_ERR(svn_mergeinfo_remove2(filtered_mergeinfo_p,
10935251881Speter                                source_mergeinfo,
10936251881Speter                                target_history_as_mergeinfo, TRUE,
10937251881Speter                                result_pool, scratch_pool));
10938251881Speter  return SVN_NO_ERROR;
10939251881Speter}
10940251881Speter
10941251881Speter/* Helper for calculate_left_hand_side() which produces a mergeinfo catalog
10942251881Speter   describing what parts of of the reintegrate target have not previously been
10943251881Speter   merged to the reintegrate source.
10944251881Speter
10945251881Speter   SOURCE_CATALOG is the collection of explicit mergeinfo on SOURCE_LOC and
10946251881Speter   all its children, i.e. the mergeinfo catalog for the reintegrate source.
10947251881Speter
10948251881Speter   TARGET_HISTORY_HASH is a hash of (const char *) paths mapped to
10949251881Speter   svn_mergeinfo_t representing the location history.  Each of these
10950251881Speter   path keys represent a path in the reintegrate target, relative to the
10951251881Speter   repository root, which has explicit mergeinfo and/or is the reintegrate
10952251881Speter   target itself.  The svn_mergeinfo_t's contain the natural history of each
10953251881Speter   path@TARGET_REV.  Effectively this is the mergeinfo catalog on the
10954251881Speter   reintegrate target.
10955251881Speter
10956251881Speter   YC_ANCESTOR_REV is the revision of the youngest common ancestor of the
10957251881Speter   reintegrate source and the reintegrate target.
10958251881Speter
10959251881Speter   SOURCE_LOC is the reintegrate source.
10960251881Speter
10961251881Speter   SOURCE_RA_SESSION is a session opened to the URL of SOURCE_LOC
10962251881Speter   and TARGET_RA_SESSION is open to TARGET->loc.url.
10963251881Speter
10964251881Speter   For each entry in TARGET_HISTORY_HASH check that the history it
10965251881Speter   represents is contained in either the explicit mergeinfo for the
10966251881Speter   corresponding path in SOURCE_CATALOG, the corresponding path's inherited
10967251881Speter   mergeinfo (if no explicit mergeinfo for the path is found in
10968251881Speter   SOURCE_CATALOG), or the corresponding path's natural history.  Populate
10969251881Speter   *UNMERGED_TO_SOURCE_CATALOG with the corresponding source paths mapped to
10970251881Speter   the mergeinfo from the target's natural history which is *not* found.  Also
10971251881Speter   include any mergeinfo from SOURCE_CATALOG which explicitly describes the
10972251881Speter   target's history but for which *no* entry was found in
10973251881Speter   TARGET_HISTORY_HASH.
10974251881Speter
10975251881Speter   If no part of TARGET_HISTORY_HASH is found in SOURCE_CATALOG set
10976251881Speter   *YOUNGEST_MERGED_REV to SVN_INVALID_REVNUM; otherwise set it to the youngest
10977251881Speter   revision previously merged from the target to the source, and filter
10978251881Speter   *UNMERGED_TO_SOURCE_CATALOG so that it contains no ranges greater than
10979251881Speter   *YOUNGEST_MERGED_REV.
10980251881Speter
10981251881Speter   *UNMERGED_TO_SOURCE_CATALOG is (deeply) allocated in RESULT_POOL.
10982251881Speter   SCRATCH_POOL is used for all temporary allocations.  */
10983251881Speterstatic svn_error_t *
10984251881Speterfind_unmerged_mergeinfo(svn_mergeinfo_catalog_t *unmerged_to_source_catalog,
10985251881Speter                        svn_revnum_t *youngest_merged_rev,
10986251881Speter                        svn_revnum_t yc_ancestor_rev,
10987251881Speter                        svn_mergeinfo_catalog_t source_catalog,
10988251881Speter                        apr_hash_t *target_history_hash,
10989251881Speter                        const svn_client__pathrev_t *source_loc,
10990251881Speter                        const merge_target_t *target,
10991251881Speter                        svn_ra_session_t *source_ra_session,
10992251881Speter                        svn_ra_session_t *target_ra_session,
10993251881Speter                        svn_client_ctx_t *ctx,
10994251881Speter                        apr_pool_t *result_pool,
10995251881Speter                        apr_pool_t *scratch_pool)
10996251881Speter{
10997251881Speter  const char *source_repos_rel_path
10998251881Speter    = svn_client__pathrev_relpath(source_loc, scratch_pool);
10999251881Speter  const char *target_repos_rel_path
11000251881Speter    = svn_client__pathrev_relpath(&target->loc, scratch_pool);
11001251881Speter  apr_hash_index_t *hi;
11002251881Speter  svn_mergeinfo_catalog_t new_catalog = apr_hash_make(result_pool);
11003251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
11004251881Speter
11005251881Speter  assert(session_url_is(source_ra_session, source_loc->url, scratch_pool));
11006251881Speter  assert(session_url_is(target_ra_session, target->loc.url, scratch_pool));
11007251881Speter
11008251881Speter  *youngest_merged_rev = SVN_INVALID_REVNUM;
11009251881Speter
11010251881Speter  /* Examine the natural history of each path in the reintegrate target
11011251881Speter     with explicit mergeinfo. */
11012251881Speter  for (hi = apr_hash_first(scratch_pool, target_history_hash);
11013251881Speter       hi;
11014251881Speter       hi = apr_hash_next(hi))
11015251881Speter    {
11016251881Speter      const char *target_path = svn__apr_hash_index_key(hi);
11017251881Speter      svn_mergeinfo_t target_history_as_mergeinfo = svn__apr_hash_index_val(hi);
11018251881Speter      const char *path_rel_to_session
11019251881Speter        = svn_relpath_skip_ancestor(target_repos_rel_path, target_path);
11020251881Speter      const char *source_path;
11021251881Speter      svn_client__pathrev_t *source_pathrev;
11022251881Speter      svn_mergeinfo_t source_mergeinfo, filtered_mergeinfo;
11023251881Speter
11024251881Speter      svn_pool_clear(iterpool);
11025251881Speter
11026251881Speter      source_path = svn_relpath_join(source_repos_rel_path,
11027251881Speter                                     path_rel_to_session, iterpool);
11028251881Speter      source_pathrev = svn_client__pathrev_join_relpath(
11029251881Speter                         source_loc, path_rel_to_session, iterpool);
11030251881Speter
11031251881Speter      /* Remove any target history that is also part of the source's history,
11032251881Speter         i.e. their common ancestry.  By definition this has already been
11033251881Speter         "merged" from the target to the source.  If the source has explicit
11034251881Speter         self referential mergeinfo it would intersect with the target's
11035251881Speter         history below, making it appear that some merges had been done from
11036251881Speter         the target to the source, when this might not actually be the case. */
11037251881Speter      SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(
11038251881Speter        &target_history_as_mergeinfo, target_history_as_mergeinfo,
11039251881Speter        source_loc->rev, yc_ancestor_rev, TRUE, iterpool, iterpool));
11040251881Speter
11041251881Speter      /* Look for any explicit mergeinfo on the source path corresponding to
11042251881Speter         the target path.  If we find any remove that from SOURCE_CATALOG.
11043251881Speter         When this iteration over TARGET_HISTORY_HASH is complete all that
11044251881Speter         should be left in SOURCE_CATALOG are subtrees that have explicit
11045251881Speter         mergeinfo on the reintegrate source where there is no corresponding
11046251881Speter         explicit mergeinfo on the reintegrate target. */
11047251881Speter      source_mergeinfo = svn_hash_gets(source_catalog, source_path);
11048251881Speter      if (source_mergeinfo)
11049251881Speter        {
11050251881Speter          svn_hash_sets(source_catalog, source_path, NULL);
11051251881Speter
11052251881Speter          SVN_ERR(find_youngest_merged_rev(youngest_merged_rev,
11053251881Speter                                           target_history_as_mergeinfo,
11054251881Speter                                           source_mergeinfo,
11055251881Speter                                           iterpool));
11056251881Speter        }
11057251881Speter      else
11058251881Speter        {
11059251881Speter          /* There is no mergeinfo on source_path *or* source_path doesn't
11060251881Speter             exist at all.  If simply doesn't exist we can ignore it
11061251881Speter             altogether. */
11062251881Speter          svn_node_kind_t kind;
11063251881Speter
11064251881Speter          SVN_ERR(svn_ra_check_path(source_ra_session,
11065251881Speter                                    path_rel_to_session,
11066251881Speter                                    source_loc->rev, &kind, iterpool));
11067251881Speter          if (kind == svn_node_none)
11068251881Speter              continue;
11069251881Speter          /* Else source_path does exist though it has no explicit mergeinfo.
11070251881Speter             Find its inherited mergeinfo.  If it doesn't have any then simply
11071251881Speter             set source_mergeinfo to an empty hash. */
11072251881Speter          SVN_ERR(svn_client__get_repos_mergeinfo(
11073251881Speter                    &source_mergeinfo, source_ra_session,
11074251881Speter                    source_pathrev->url, source_pathrev->rev,
11075251881Speter                    svn_mergeinfo_inherited, FALSE /*squelch_incapable*/,
11076251881Speter                    iterpool));
11077251881Speter          if (!source_mergeinfo)
11078251881Speter            source_mergeinfo = apr_hash_make(iterpool);
11079251881Speter        }
11080251881Speter
11081251881Speter      /* Use scratch_pool rather than iterpool because filtered_mergeinfo
11082251881Speter         is going into new_catalog below and needs to last to the end of
11083251881Speter         this function. */
11084251881Speter      SVN_ERR(find_unmerged_mergeinfo_subroutine(
11085251881Speter                &filtered_mergeinfo, target_history_as_mergeinfo,
11086251881Speter                source_mergeinfo, source_pathrev,
11087251881Speter                source_ra_session, ctx, scratch_pool, iterpool));
11088251881Speter      svn_hash_sets(new_catalog, apr_pstrdup(scratch_pool, source_path),
11089251881Speter                    filtered_mergeinfo);
11090251881Speter    }
11091251881Speter
11092251881Speter  /* Are there any subtrees with explicit mergeinfo still left in the merge
11093251881Speter     source where there was no explicit mergeinfo for the corresponding path
11094251881Speter     in the merge target?  If so, add the intersection of those path's
11095251881Speter     mergeinfo and the corresponding target path's mergeinfo to
11096251881Speter     new_catalog. */
11097251881Speter  for (hi = apr_hash_first(scratch_pool, source_catalog);
11098251881Speter       hi;
11099251881Speter       hi = apr_hash_next(hi))
11100251881Speter    {
11101251881Speter      const char *source_path = svn__apr_hash_index_key(hi);
11102251881Speter      const char *path_rel_to_session =
11103251881Speter        svn_relpath_skip_ancestor(source_repos_rel_path, source_path);
11104251881Speter      const char *source_url;
11105251881Speter      svn_mergeinfo_t source_mergeinfo = svn__apr_hash_index_val(hi);
11106251881Speter      svn_mergeinfo_t filtered_mergeinfo;
11107251881Speter      svn_client__pathrev_t *target_pathrev;
11108251881Speter      svn_mergeinfo_t target_history_as_mergeinfo;
11109251881Speter      svn_error_t *err;
11110251881Speter
11111251881Speter      svn_pool_clear(iterpool);
11112251881Speter
11113251881Speter      source_url = svn_path_url_add_component2(source_loc->url,
11114251881Speter                                               path_rel_to_session, iterpool);
11115251881Speter      target_pathrev = svn_client__pathrev_join_relpath(
11116251881Speter                         &target->loc, path_rel_to_session, iterpool);
11117251881Speter      err = svn_client__get_history_as_mergeinfo(&target_history_as_mergeinfo,
11118251881Speter                                                 NULL /* has_rev_zero_history */,
11119251881Speter                                                 target_pathrev,
11120251881Speter                                                 target->loc.rev,
11121251881Speter                                                 SVN_INVALID_REVNUM,
11122251881Speter                                                 target_ra_session,
11123251881Speter                                                 ctx, iterpool);
11124251881Speter      if (err)
11125251881Speter        {
11126251881Speter          if (err->apr_err == SVN_ERR_FS_NOT_FOUND
11127251881Speter              || err->apr_err == SVN_ERR_RA_DAV_REQUEST_FAILED)
11128251881Speter            {
11129251881Speter              /* This path with explicit mergeinfo in the source doesn't
11130251881Speter                 exist on the target. */
11131251881Speter              svn_error_clear(err);
11132251881Speter              err = NULL;
11133251881Speter            }
11134251881Speter          else
11135251881Speter            {
11136251881Speter              return svn_error_trace(err);
11137251881Speter            }
11138251881Speter        }
11139251881Speter      else
11140251881Speter        {
11141251881Speter          svn_client__pathrev_t *pathrev;
11142251881Speter
11143251881Speter          SVN_ERR(find_youngest_merged_rev(youngest_merged_rev,
11144251881Speter                                           target_history_as_mergeinfo,
11145251881Speter                                           source_mergeinfo,
11146251881Speter                                           iterpool));
11147251881Speter
11148251881Speter          /* Use scratch_pool rather than iterpool because filtered_mergeinfo
11149251881Speter             is going into new_catalog below and needs to last to the end of
11150251881Speter             this function. */
11151251881Speter          /* ### Why looking at SOURCE_url at TARGET_rev? */
11152251881Speter          SVN_ERR(svn_client__pathrev_create_with_session(
11153251881Speter                    &pathrev, source_ra_session, target->loc.rev, source_url,
11154251881Speter                    iterpool));
11155251881Speter          SVN_ERR(find_unmerged_mergeinfo_subroutine(
11156251881Speter                    &filtered_mergeinfo, target_history_as_mergeinfo,
11157251881Speter                    source_mergeinfo, pathrev,
11158251881Speter                    source_ra_session, ctx, scratch_pool, iterpool));
11159251881Speter          if (apr_hash_count(filtered_mergeinfo))
11160251881Speter            svn_hash_sets(new_catalog,
11161251881Speter                          apr_pstrdup(scratch_pool, source_path),
11162251881Speter                          filtered_mergeinfo);
11163251881Speter        }
11164251881Speter    }
11165251881Speter
11166251881Speter  /* Limit new_catalog to the youngest revisions previously merged from
11167251881Speter     the target to the source. */
11168251881Speter  if (SVN_IS_VALID_REVNUM(*youngest_merged_rev))
11169251881Speter    SVN_ERR(svn_mergeinfo__filter_catalog_by_ranges(&new_catalog,
11170251881Speter                                                    new_catalog,
11171251881Speter                                                    *youngest_merged_rev,
11172251881Speter                                                    0, /* No oldest bound. */
11173251881Speter                                                    TRUE,
11174251881Speter                                                    scratch_pool,
11175251881Speter                                                    scratch_pool));
11176251881Speter
11177251881Speter  /* Make a shiny new copy before blowing away all the temporary pools. */
11178251881Speter  *unmerged_to_source_catalog = svn_mergeinfo_catalog_dup(new_catalog,
11179251881Speter                                                          result_pool);
11180251881Speter  svn_pool_destroy(iterpool);
11181251881Speter  return SVN_NO_ERROR;
11182251881Speter}
11183251881Speter
11184251881Speter/* Helper for svn_client_merge_reintegrate() which calculates the
11185251881Speter   'left hand side' of the underlying two-URL merge that a --reintegrate
11186251881Speter   merge actually performs.  If no merge should be performed, set
11187251881Speter   *LEFT_P to NULL.
11188251881Speter
11189251881Speter   TARGET->abspath is the absolute working copy path of the reintegrate
11190251881Speter   merge.
11191251881Speter
11192251881Speter   SOURCE_LOC is the reintegrate source.
11193251881Speter
11194251881Speter   SUBTREES_WITH_MERGEINFO is a hash of (const char *) absolute paths mapped
11195251881Speter   to (svn_mergeinfo_t *) mergeinfo values for each working copy path with
11196251881Speter   explicit mergeinfo in TARGET->abspath.  Actually we only need to know the
11197251881Speter   paths, not the mergeinfo.
11198251881Speter
11199251881Speter   TARGET->loc.rev is the working revision the entire WC tree rooted at
11200251881Speter   TARGET is at.
11201251881Speter
11202251881Speter   Populate *UNMERGED_TO_SOURCE_CATALOG with the mergeinfo describing what
11203251881Speter   parts of TARGET->loc have not been merged to SOURCE_LOC, up to the
11204251881Speter   youngest revision ever merged from the TARGET->abspath to the source if
11205251881Speter   such exists, see doc string for find_unmerged_mergeinfo().
11206251881Speter
11207251881Speter   SOURCE_RA_SESSION is a session opened to the SOURCE_LOC
11208251881Speter   and TARGET_RA_SESSION is open to TARGET->loc.url.
11209251881Speter
11210251881Speter   *LEFT_P, *MERGED_TO_SOURCE_CATALOG , and *UNMERGED_TO_SOURCE_CATALOG are
11211251881Speter   allocated in RESULT_POOL.  SCRATCH_POOL is used for all temporary
11212251881Speter   allocations. */
11213251881Speterstatic svn_error_t *
11214251881Spetercalculate_left_hand_side(svn_client__pathrev_t **left_p,
11215251881Speter                         svn_mergeinfo_catalog_t *merged_to_source_catalog,
11216251881Speter                         svn_mergeinfo_catalog_t *unmerged_to_source_catalog,
11217251881Speter                         const merge_target_t *target,
11218251881Speter                         apr_hash_t *subtrees_with_mergeinfo,
11219251881Speter                         const svn_client__pathrev_t *source_loc,
11220251881Speter                         svn_ra_session_t *source_ra_session,
11221251881Speter                         svn_ra_session_t *target_ra_session,
11222251881Speter                         svn_client_ctx_t *ctx,
11223251881Speter                         apr_pool_t *result_pool,
11224251881Speter                         apr_pool_t *scratch_pool)
11225251881Speter{
11226251881Speter  svn_mergeinfo_catalog_t mergeinfo_catalog, unmerged_catalog;
11227251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
11228251881Speter  apr_hash_index_t *hi;
11229251881Speter  /* hash of paths mapped to arrays of svn_mergeinfo_t. */
11230251881Speter  apr_hash_t *target_history_hash = apr_hash_make(scratch_pool);
11231251881Speter  svn_revnum_t youngest_merged_rev;
11232251881Speter  svn_client__pathrev_t *yc_ancestor;
11233251881Speter
11234251881Speter  assert(session_url_is(source_ra_session, source_loc->url, scratch_pool));
11235251881Speter  assert(session_url_is(target_ra_session, target->loc.url, scratch_pool));
11236251881Speter
11237251881Speter  /* Initialize our return variables. */
11238251881Speter  *left_p = NULL;
11239251881Speter
11240251881Speter  /* TARGET->abspath may not have explicit mergeinfo and thus may not be
11241251881Speter     contained within SUBTREES_WITH_MERGEINFO.  If this is the case then
11242251881Speter     add a dummy item for TARGET->abspath so we get its history (i.e. implicit
11243251881Speter     mergeinfo) below.  */
11244251881Speter  if (!svn_hash_gets(subtrees_with_mergeinfo, target->abspath))
11245251881Speter    svn_hash_sets(subtrees_with_mergeinfo, target->abspath,
11246251881Speter                  apr_hash_make(result_pool));
11247251881Speter
11248251881Speter  /* Get the history segments (as mergeinfo) for TARGET->abspath and any of
11249251881Speter     its subtrees with explicit mergeinfo. */
11250251881Speter  for (hi = apr_hash_first(scratch_pool, subtrees_with_mergeinfo);
11251251881Speter       hi;
11252251881Speter       hi = apr_hash_next(hi))
11253251881Speter    {
11254251881Speter      const char *local_abspath = svn__apr_hash_index_key(hi);
11255251881Speter      svn_client__pathrev_t *target_child;
11256251881Speter      const char *repos_relpath;
11257251881Speter      svn_mergeinfo_t target_history_as_mergeinfo;
11258251881Speter
11259251881Speter      svn_pool_clear(iterpool);
11260251881Speter
11261251881Speter      /* Convert the absolute path with mergeinfo on it to a path relative
11262251881Speter         to the session root. */
11263251881Speter      SVN_ERR(svn_wc__node_get_repos_info(NULL, &repos_relpath, NULL, NULL,
11264251881Speter                                          ctx->wc_ctx, local_abspath,
11265251881Speter                                          scratch_pool, iterpool));
11266251881Speter      target_child = svn_client__pathrev_create_with_relpath(
11267251881Speter                       target->loc.repos_root_url, target->loc.repos_uuid,
11268251881Speter                       target->loc.rev, repos_relpath, iterpool);
11269251881Speter      SVN_ERR(svn_client__get_history_as_mergeinfo(&target_history_as_mergeinfo,
11270251881Speter                                                   NULL /* has_rev_zero_hist */,
11271251881Speter                                                   target_child,
11272251881Speter                                                   target->loc.rev,
11273251881Speter                                                   SVN_INVALID_REVNUM,
11274251881Speter                                                   target_ra_session,
11275251881Speter                                                   ctx, scratch_pool));
11276251881Speter
11277251881Speter      svn_hash_sets(target_history_hash, repos_relpath,
11278251881Speter                    target_history_as_mergeinfo);
11279251881Speter    }
11280251881Speter
11281251881Speter  /* Check that SOURCE_LOC and TARGET->loc are
11282251881Speter     actually related, we can't reintegrate if they are not.  Also
11283251881Speter     get an initial value for the YCA revision number. */
11284251881Speter  SVN_ERR(svn_client__get_youngest_common_ancestor(
11285251881Speter              &yc_ancestor, source_loc, &target->loc, target_ra_session, ctx,
11286251881Speter              iterpool, iterpool));
11287251881Speter  if (! yc_ancestor)
11288251881Speter    return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
11289251881Speter                             _("'%s@%ld' must be ancestrally related to "
11290251881Speter                               "'%s@%ld'"), source_loc->url, source_loc->rev,
11291251881Speter                             target->loc.url, target->loc.rev);
11292251881Speter
11293251881Speter  /* If the source revision is the same as the youngest common
11294251881Speter     revision, then there can't possibly be any unmerged revisions
11295251881Speter     that we need to apply to target. */
11296251881Speter  if (source_loc->rev == yc_ancestor->rev)
11297251881Speter    {
11298251881Speter      svn_pool_destroy(iterpool);
11299251881Speter      return SVN_NO_ERROR;
11300251881Speter    }
11301251881Speter
11302251881Speter  /* Get the mergeinfo from the source, including its descendants
11303251881Speter     with differing explicit mergeinfo. */
11304251881Speter  SVN_ERR(svn_client__get_repos_mergeinfo_catalog(
11305251881Speter            &mergeinfo_catalog, source_ra_session,
11306251881Speter            source_loc->url, source_loc->rev,
11307251881Speter            svn_mergeinfo_inherited, FALSE /* squelch_incapable */,
11308251881Speter            TRUE /* include_descendants */, iterpool, iterpool));
11309251881Speter
11310251881Speter  if (!mergeinfo_catalog)
11311251881Speter    mergeinfo_catalog = apr_hash_make(iterpool);
11312251881Speter
11313251881Speter  *merged_to_source_catalog = svn_mergeinfo_catalog_dup(mergeinfo_catalog,
11314251881Speter                                                        result_pool);
11315251881Speter
11316251881Speter  /* Filter the source's mergeinfo catalog so that we are left with
11317251881Speter     mergeinfo that describes what has *not* previously been merged from
11318251881Speter     TARGET->loc to SOURCE_LOC. */
11319251881Speter  SVN_ERR(find_unmerged_mergeinfo(&unmerged_catalog,
11320251881Speter                                  &youngest_merged_rev,
11321251881Speter                                  yc_ancestor->rev,
11322251881Speter                                  mergeinfo_catalog,
11323251881Speter                                  target_history_hash,
11324251881Speter                                  source_loc,
11325251881Speter                                  target,
11326251881Speter                                  source_ra_session,
11327251881Speter                                  target_ra_session,
11328251881Speter                                  ctx,
11329251881Speter                                  iterpool, iterpool));
11330251881Speter
11331251881Speter  /* Simplify unmerged_catalog through elision then make a copy in POOL. */
11332251881Speter  SVN_ERR(svn_client__elide_mergeinfo_catalog(unmerged_catalog,
11333251881Speter                                              iterpool));
11334251881Speter  *unmerged_to_source_catalog = svn_mergeinfo_catalog_dup(unmerged_catalog,
11335251881Speter                                                          result_pool);
11336251881Speter
11337251881Speter  if (youngest_merged_rev == SVN_INVALID_REVNUM)
11338251881Speter    {
11339251881Speter      /* We never merged to the source.  Just return the branch point. */
11340251881Speter      *left_p = svn_client__pathrev_dup(yc_ancestor, result_pool);
11341251881Speter    }
11342251881Speter  else
11343251881Speter    {
11344251881Speter      /* We've previously merged some or all of the target, up to
11345251881Speter         youngest_merged_rev, to the source.  Set
11346251881Speter         *LEFT_P to cover the youngest part of this range. */
11347251881Speter      SVN_ERR(svn_client__repos_location(left_p, target_ra_session,
11348251881Speter                                         &target->loc, youngest_merged_rev,
11349251881Speter                                         ctx, result_pool, iterpool));
11350251881Speter    }
11351251881Speter
11352251881Speter  svn_pool_destroy(iterpool);
11353251881Speter  return SVN_NO_ERROR;
11354251881Speter}
11355251881Speter
11356251881Speter/* Determine the URLs and revisions needed to perform a reintegrate merge
11357251881Speter * from SOURCE_LOC into the working copy at TARGET.
11358251881Speter *
11359251881Speter * SOURCE_RA_SESSION and TARGET_RA_SESSION are RA sessions opened to the
11360251881Speter * URLs of SOURCE_LOC and TARGET->loc respectively.
11361251881Speter *
11362251881Speter * Set *SOURCE_P to
11363251881Speter * the source-left and source-right locations of the required merge.  Set
11364251881Speter * *YC_ANCESTOR_P to the location of the youngest ancestor.
11365251881Speter * Any of these output pointers may be NULL if not wanted.
11366251881Speter *
11367251881Speter * See svn_client_find_reintegrate_merge() for other details.
11368251881Speter */
11369251881Speterstatic svn_error_t *
11370251881Speterfind_reintegrate_merge(merge_source_t **source_p,
11371251881Speter                       svn_client__pathrev_t **yc_ancestor_p,
11372251881Speter                       svn_ra_session_t *source_ra_session,
11373251881Speter                       const svn_client__pathrev_t *source_loc,
11374251881Speter                       svn_ra_session_t *target_ra_session,
11375251881Speter                       const merge_target_t *target,
11376251881Speter                       svn_client_ctx_t *ctx,
11377251881Speter                       apr_pool_t *result_pool,
11378251881Speter                       apr_pool_t *scratch_pool)
11379251881Speter{
11380251881Speter  svn_client__pathrev_t *yc_ancestor;
11381251881Speter  svn_client__pathrev_t *loc1;
11382251881Speter  merge_source_t source;
11383251881Speter  svn_mergeinfo_catalog_t unmerged_to_source_mergeinfo_catalog;
11384251881Speter  svn_mergeinfo_catalog_t merged_to_source_mergeinfo_catalog;
11385251881Speter  svn_error_t *err;
11386251881Speter  apr_hash_t *subtrees_with_mergeinfo;
11387251881Speter
11388251881Speter  assert(session_url_is(source_ra_session, source_loc->url, scratch_pool));
11389251881Speter  assert(session_url_is(target_ra_session, target->loc.url, scratch_pool));
11390251881Speter
11391251881Speter  /* As the WC tree is "pure", use its last-updated-to revision as
11392251881Speter     the default revision for the left side of our merge, since that's
11393251881Speter     what the repository sub-tree is required to be up to date with
11394251881Speter     (with regard to the WC). */
11395251881Speter  /* ### Bogus/obsolete comment? */
11396251881Speter
11397251881Speter  /* Can't reintegrate to or from the root of the repository. */
11398251881Speter  if (strcmp(source_loc->url, source_loc->repos_root_url) == 0
11399251881Speter      || strcmp(target->loc.url, target->loc.repos_root_url) == 0)
11400251881Speter    return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
11401251881Speter                             _("Neither the reintegrate source nor target "
11402251881Speter                               "can be the root of the repository"));
11403251881Speter
11404251881Speter  /* Find all the subtrees in TARGET_WCPATH that have explicit mergeinfo. */
11405251881Speter  err = get_wc_explicit_mergeinfo_catalog(&subtrees_with_mergeinfo,
11406251881Speter                                          target->abspath, svn_depth_infinity,
11407251881Speter                                          ctx, scratch_pool, scratch_pool);
11408251881Speter  /* Issue #3896: If invalid mergeinfo in the reintegrate target
11409251881Speter     prevents us from proceeding, then raise the best error possible. */
11410251881Speter  if (err && err->apr_err == SVN_ERR_CLIENT_INVALID_MERGEINFO_NO_MERGETRACKING)
11411251881Speter    err = svn_error_quick_wrap(err, _("Reintegrate merge not possible"));
11412251881Speter  SVN_ERR(err);
11413251881Speter
11414251881Speter  SVN_ERR(calculate_left_hand_side(&loc1,
11415251881Speter                                   &merged_to_source_mergeinfo_catalog,
11416251881Speter                                   &unmerged_to_source_mergeinfo_catalog,
11417251881Speter                                   target,
11418251881Speter                                   subtrees_with_mergeinfo,
11419251881Speter                                   source_loc,
11420251881Speter                                   source_ra_session,
11421251881Speter                                   target_ra_session,
11422251881Speter                                   ctx,
11423251881Speter                                   scratch_pool, scratch_pool));
11424251881Speter
11425251881Speter  /* Did calculate_left_hand_side() decide that there was no merge to
11426251881Speter     be performed here?  */
11427251881Speter  if (! loc1)
11428251881Speter    {
11429251881Speter      if (source_p)
11430251881Speter        *source_p = NULL;
11431251881Speter      if (yc_ancestor_p)
11432251881Speter        *yc_ancestor_p = NULL;
11433251881Speter      return SVN_NO_ERROR;
11434251881Speter    }
11435251881Speter
11436251881Speter  source.loc1 = loc1;
11437251881Speter  source.loc2 = source_loc;
11438251881Speter
11439251881Speter  /* If the target was moved after the source was branched from it,
11440251881Speter     it is possible that the left URL differs from the target's current
11441251881Speter     URL.  If so, then adjust TARGET_RA_SESSION to point to the old URL. */
11442251881Speter  if (strcmp(source.loc1->url, target->loc.url))
11443251881Speter    SVN_ERR(svn_ra_reparent(target_ra_session, source.loc1->url, scratch_pool));
11444251881Speter
11445251881Speter  SVN_ERR(svn_client__get_youngest_common_ancestor(
11446251881Speter            &yc_ancestor, source.loc2, source.loc1, target_ra_session,
11447251881Speter            ctx, scratch_pool, scratch_pool));
11448251881Speter
11449251881Speter  if (! yc_ancestor)
11450251881Speter    return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
11451251881Speter                             _("'%s@%ld' must be ancestrally related to "
11452251881Speter                               "'%s@%ld'"),
11453251881Speter                             source.loc1->url, source.loc1->rev,
11454251881Speter                             source.loc2->url, source.loc2->rev);
11455251881Speter
11456251881Speter  /* The source side of a reintegrate merge is not 'ancestral', except in
11457251881Speter   * the degenerate case where source == YCA. */
11458251881Speter  source.ancestral = (loc1->rev == yc_ancestor->rev);
11459251881Speter
11460251881Speter  if (source.loc1->rev > yc_ancestor->rev)
11461251881Speter    {
11462251881Speter      /* Have we actually merged anything to the source from the
11463251881Speter         target?  If so, make sure we've merged a contiguous
11464251881Speter         prefix. */
11465251881Speter      svn_mergeinfo_catalog_t final_unmerged_catalog = apr_hash_make(scratch_pool);
11466251881Speter
11467251881Speter      SVN_ERR(find_unsynced_ranges(source_loc, yc_ancestor,
11468251881Speter                                   unmerged_to_source_mergeinfo_catalog,
11469251881Speter                                   merged_to_source_mergeinfo_catalog,
11470251881Speter                                   final_unmerged_catalog,
11471251881Speter                                   target_ra_session, scratch_pool,
11472251881Speter                                   scratch_pool));
11473251881Speter
11474251881Speter      if (apr_hash_count(final_unmerged_catalog))
11475251881Speter        {
11476251881Speter          svn_string_t *source_mergeinfo_cat_string;
11477251881Speter
11478251881Speter          SVN_ERR(svn_mergeinfo__catalog_to_formatted_string(
11479251881Speter            &source_mergeinfo_cat_string,
11480251881Speter            final_unmerged_catalog,
11481251881Speter            "  ", "    Missing ranges: ", scratch_pool));
11482251881Speter          return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE,
11483251881Speter                                   NULL,
11484251881Speter                                   _("Reintegrate can only be used if "
11485251881Speter                                     "revisions %ld through %ld were "
11486251881Speter                                     "previously merged from %s to the "
11487251881Speter                                     "reintegrate source, but this is "
11488251881Speter                                     "not the case:\n%s"),
11489251881Speter                                   yc_ancestor->rev + 1, source.loc2->rev,
11490251881Speter                                   target->loc.url,
11491251881Speter                                   source_mergeinfo_cat_string->data);
11492251881Speter        }
11493251881Speter    }
11494251881Speter
11495251881Speter  /* Left side: trunk@youngest-trunk-rev-merged-to-branch-at-specified-peg-rev
11496251881Speter   * Right side: branch@specified-peg-revision */
11497251881Speter  if (source_p)
11498251881Speter    *source_p = merge_source_dup(&source, result_pool);
11499251881Speter
11500251881Speter  if (yc_ancestor_p)
11501251881Speter    *yc_ancestor_p = svn_client__pathrev_dup(yc_ancestor, result_pool);
11502251881Speter  return SVN_NO_ERROR;
11503251881Speter}
11504251881Speter
11505251881Speter/* Resolve the source and target locations and open RA sessions to them, and
11506251881Speter * perform some checks appropriate for a reintegrate merge.
11507251881Speter *
11508251881Speter * Set *SOURCE_RA_SESSION_P and *SOURCE_LOC_P to a new session and the
11509251881Speter * repository location of SOURCE_PATH_OR_URL at SOURCE_PEG_REVISION.  Set
11510251881Speter * *TARGET_RA_SESSION_P and *TARGET_P to a new session and the repository
11511251881Speter * location of the WC at TARGET_ABSPATH.
11512251881Speter *
11513251881Speter * Throw a SVN_ERR_CLIENT_UNRELATED_RESOURCES error if the target WC node is
11514251881Speter * a locally added node or if the source and target are not in the same
11515251881Speter * repository.  Throw a SVN_ERR_CLIENT_NOT_READY_TO_MERGE error if the
11516251881Speter * target WC is not at a single revision without switched subtrees and
11517251881Speter * without local mods.
11518251881Speter *
11519251881Speter * Allocate all the outputs in RESULT_POOL.
11520251881Speter */
11521251881Speterstatic svn_error_t *
11522251881Speteropen_reintegrate_source_and_target(svn_ra_session_t **source_ra_session_p,
11523251881Speter                                   svn_client__pathrev_t **source_loc_p,
11524251881Speter                                   svn_ra_session_t **target_ra_session_p,
11525251881Speter                                   merge_target_t **target_p,
11526251881Speter                                   const char *source_path_or_url,
11527251881Speter                                   const svn_opt_revision_t *source_peg_revision,
11528251881Speter                                   const char *target_abspath,
11529251881Speter                                   svn_client_ctx_t *ctx,
11530251881Speter                                   apr_pool_t *result_pool,
11531251881Speter                                   apr_pool_t *scratch_pool)
11532251881Speter{
11533251881Speter  svn_client__pathrev_t *source_loc;
11534251881Speter  merge_target_t *target;
11535251881Speter
11536251881Speter  /* Open the target WC.  A reintegrate merge requires the merge target to
11537251881Speter   * reflect a subtree of the repository as found at a single revision. */
11538251881Speter  SVN_ERR(open_target_wc(&target, target_abspath,
11539251881Speter                         FALSE, FALSE, FALSE,
11540251881Speter                         ctx, scratch_pool, scratch_pool));
11541251881Speter  SVN_ERR(svn_client_open_ra_session2(target_ra_session_p,
11542251881Speter                                      target->loc.url, target->abspath,
11543251881Speter                                      ctx, result_pool, scratch_pool));
11544251881Speter  if (! target->loc.url)
11545251881Speter    return svn_error_createf(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
11546251881Speter                             _("Can't reintegrate into '%s' because it is "
11547251881Speter                               "locally added and therefore not related to "
11548251881Speter                               "the merge source"),
11549251881Speter                             svn_dirent_local_style(target->abspath,
11550251881Speter                                                    scratch_pool));
11551251881Speter
11552251881Speter  SVN_ERR(svn_client__ra_session_from_path2(
11553251881Speter            source_ra_session_p, &source_loc,
11554251881Speter            source_path_or_url, NULL, source_peg_revision, source_peg_revision,
11555251881Speter            ctx, result_pool));
11556251881Speter
11557251881Speter  /* source_loc and target->loc are required to be in the same repository,
11558251881Speter     as mergeinfo doesn't come into play for cross-repository merging. */
11559251881Speter  SVN_ERR(check_same_repos(source_loc,
11560251881Speter                           svn_dirent_local_style(source_path_or_url,
11561251881Speter                                                  scratch_pool),
11562251881Speter                           &target->loc,
11563251881Speter                           svn_dirent_local_style(target->abspath,
11564251881Speter                                                  scratch_pool),
11565251881Speter                           TRUE /* strict_urls */, scratch_pool));
11566251881Speter
11567251881Speter  *source_loc_p = source_loc;
11568251881Speter  *target_p = target;
11569251881Speter  return SVN_NO_ERROR;
11570251881Speter}
11571251881Speter
11572251881Speter/* The body of svn_client_merge_reintegrate(), which see for details. */
11573251881Speterstatic svn_error_t *
11574251881Spetermerge_reintegrate_locked(conflict_report_t **conflict_report,
11575251881Speter                         const char *source_path_or_url,
11576251881Speter                         const svn_opt_revision_t *source_peg_revision,
11577251881Speter                         const char *target_abspath,
11578251881Speter                         svn_boolean_t diff_ignore_ancestry,
11579251881Speter                         svn_boolean_t dry_run,
11580251881Speter                         const apr_array_header_t *merge_options,
11581251881Speter                         svn_client_ctx_t *ctx,
11582251881Speter                         apr_pool_t *result_pool,
11583251881Speter                         apr_pool_t *scratch_pool)
11584251881Speter{
11585251881Speter  svn_ra_session_t *target_ra_session, *source_ra_session;
11586251881Speter  merge_target_t *target;
11587251881Speter  svn_client__pathrev_t *source_loc;
11588251881Speter  merge_source_t *source;
11589251881Speter  svn_client__pathrev_t *yc_ancestor;
11590251881Speter  svn_boolean_t use_sleep = FALSE;
11591251881Speter  svn_error_t *err;
11592251881Speter
11593251881Speter  SVN_ERR(open_reintegrate_source_and_target(
11594251881Speter            &source_ra_session, &source_loc, &target_ra_session, &target,
11595251881Speter            source_path_or_url, source_peg_revision, target_abspath,
11596251881Speter            ctx, scratch_pool, scratch_pool));
11597251881Speter
11598251881Speter  SVN_ERR(find_reintegrate_merge(&source, &yc_ancestor,
11599251881Speter                                 source_ra_session, source_loc,
11600251881Speter                                 target_ra_session, target,
11601251881Speter                                 ctx, scratch_pool, scratch_pool));
11602251881Speter
11603251881Speter  if (! source)
11604251881Speter    {
11605251881Speter      return SVN_NO_ERROR;
11606251881Speter    }
11607251881Speter
11608251881Speter  /* Do the real merge! */
11609251881Speter  /* ### TODO(reint): Make sure that one isn't the same line ancestor
11610251881Speter     ### of the other (what's erroneously referred to as "ancestrally
11611251881Speter     ### related" in this source file).  For now, we just say the source
11612251881Speter     ### isn't "ancestral" even if it is (in the degenerate case where
11613251881Speter     ### source-left equals YCA). */
11614251881Speter  source->ancestral = FALSE;
11615251881Speter  err = merge_cousins_and_supplement_mergeinfo(conflict_report,
11616251881Speter                                               &use_sleep,
11617251881Speter                                               target,
11618251881Speter                                               target_ra_session,
11619251881Speter                                               source_ra_session,
11620251881Speter                                               source, yc_ancestor,
11621251881Speter                                               TRUE /* same_repos */,
11622251881Speter                                               svn_depth_infinity,
11623251881Speter                                               diff_ignore_ancestry,
11624251881Speter                                               FALSE /* force_delete */,
11625251881Speter                                               FALSE /* record_only */,
11626251881Speter                                               dry_run,
11627251881Speter                                               merge_options,
11628251881Speter                                               ctx,
11629251881Speter                                               result_pool, scratch_pool);
11630251881Speter
11631251881Speter  if (use_sleep)
11632251881Speter    svn_io_sleep_for_timestamps(target_abspath, scratch_pool);
11633251881Speter
11634251881Speter  SVN_ERR(err);
11635251881Speter  return SVN_NO_ERROR;
11636251881Speter}
11637251881Speter
11638251881Spetersvn_error_t *
11639251881Spetersvn_client_merge_reintegrate(const char *source_path_or_url,
11640251881Speter                             const svn_opt_revision_t *source_peg_revision,
11641251881Speter                             const char *target_wcpath,
11642251881Speter                             svn_boolean_t dry_run,
11643251881Speter                             const apr_array_header_t *merge_options,
11644251881Speter                             svn_client_ctx_t *ctx,
11645251881Speter                             apr_pool_t *pool)
11646251881Speter{
11647251881Speter  const char *target_abspath, *lock_abspath;
11648251881Speter  conflict_report_t *conflict_report;
11649251881Speter
11650251881Speter  SVN_ERR(get_target_and_lock_abspath(&target_abspath, &lock_abspath,
11651251881Speter                                      target_wcpath, ctx, pool));
11652251881Speter
11653251881Speter  if (!dry_run)
11654251881Speter    SVN_WC__CALL_WITH_WRITE_LOCK(
11655251881Speter      merge_reintegrate_locked(&conflict_report,
11656251881Speter                               source_path_or_url, source_peg_revision,
11657251881Speter                               target_abspath,
11658251881Speter                               FALSE /*diff_ignore_ancestry*/,
11659251881Speter                               dry_run, merge_options, ctx, pool, pool),
11660251881Speter      ctx->wc_ctx, lock_abspath, FALSE /* lock_anchor */, pool);
11661251881Speter  else
11662251881Speter    SVN_ERR(merge_reintegrate_locked(&conflict_report,
11663251881Speter                                     source_path_or_url, source_peg_revision,
11664251881Speter                                     target_abspath,
11665251881Speter                                     FALSE /*diff_ignore_ancestry*/,
11666251881Speter                                     dry_run, merge_options, ctx, pool, pool));
11667251881Speter
11668251881Speter  SVN_ERR(make_merge_conflict_error(conflict_report, pool));
11669251881Speter  return SVN_NO_ERROR;
11670251881Speter}
11671251881Speter
11672251881Speter
11673251881Speter/* The body of svn_client_merge_peg5(), which see for details.
11674251881Speter *
11675251881Speter * IGNORE_MERGEINFO and DIFF_IGNORE_ANCESTRY are as in do_merge().
11676251881Speter */
11677251881Speterstatic svn_error_t *
11678251881Spetermerge_peg_locked(conflict_report_t **conflict_report,
11679251881Speter                 const char *source_path_or_url,
11680251881Speter                 const svn_opt_revision_t *source_peg_revision,
11681251881Speter                 const svn_rangelist_t *ranges_to_merge,
11682251881Speter                 const char *target_abspath,
11683251881Speter                 svn_depth_t depth,
11684251881Speter                 svn_boolean_t ignore_mergeinfo,
11685251881Speter                 svn_boolean_t diff_ignore_ancestry,
11686251881Speter                 svn_boolean_t force_delete,
11687251881Speter                 svn_boolean_t record_only,
11688251881Speter                 svn_boolean_t dry_run,
11689251881Speter                 svn_boolean_t allow_mixed_rev,
11690251881Speter                 const apr_array_header_t *merge_options,
11691251881Speter                 svn_client_ctx_t *ctx,
11692251881Speter                 apr_pool_t *result_pool,
11693251881Speter                 apr_pool_t *scratch_pool)
11694251881Speter{
11695251881Speter  merge_target_t *target;
11696251881Speter  svn_client__pathrev_t *source_loc;
11697251881Speter  apr_array_header_t *merge_sources;
11698251881Speter  svn_ra_session_t *ra_session;
11699251881Speter  apr_pool_t *sesspool;
11700251881Speter  svn_boolean_t use_sleep = FALSE;
11701251881Speter  svn_error_t *err;
11702251881Speter  svn_boolean_t same_repos;
11703251881Speter
11704251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath));
11705251881Speter
11706251881Speter  SVN_ERR(open_target_wc(&target, target_abspath,
11707251881Speter                         allow_mixed_rev, TRUE, TRUE,
11708251881Speter                         ctx, scratch_pool, scratch_pool));
11709251881Speter
11710251881Speter  /* Create a short lived session pool */
11711251881Speter  sesspool = svn_pool_create(scratch_pool);
11712251881Speter
11713251881Speter  /* Open an RA session to our source URL, and determine its root URL. */
11714251881Speter  SVN_ERR(svn_client__ra_session_from_path2(
11715251881Speter            &ra_session, &source_loc,
11716251881Speter            source_path_or_url, NULL, source_peg_revision, source_peg_revision,
11717251881Speter            ctx, sesspool));
11718251881Speter
11719251881Speter  /* Normalize our merge sources. */
11720251881Speter  SVN_ERR(normalize_merge_sources(&merge_sources, source_path_or_url,
11721251881Speter                                  source_loc,
11722251881Speter                                  ranges_to_merge, ra_session, ctx,
11723251881Speter                                  scratch_pool, scratch_pool));
11724251881Speter
11725251881Speter  /* Check for same_repos. */
11726251881Speter  same_repos = is_same_repos(&target->loc, source_loc, TRUE /* strict_urls */);
11727251881Speter
11728251881Speter  /* Do the real merge!  (We say with confidence that our merge
11729251881Speter     sources are both ancestral and related.) */
11730251881Speter  err = do_merge(NULL, NULL, conflict_report, &use_sleep,
11731251881Speter                 merge_sources, target, ra_session,
11732251881Speter                 TRUE /*sources_related*/, same_repos, ignore_mergeinfo,
11733251881Speter                 diff_ignore_ancestry, force_delete, dry_run,
11734251881Speter                 record_only, NULL, FALSE, FALSE, depth, merge_options,
11735251881Speter                 ctx, result_pool, scratch_pool);
11736251881Speter
11737251881Speter  /* We're done with our RA session. */
11738251881Speter  svn_pool_destroy(sesspool);
11739251881Speter
11740251881Speter  if (use_sleep)
11741251881Speter    svn_io_sleep_for_timestamps(target_abspath, scratch_pool);
11742251881Speter
11743251881Speter  SVN_ERR(err);
11744251881Speter  return SVN_NO_ERROR;
11745251881Speter}
11746251881Speter
11747251881Speter/* Details of an automatic merge. */
11748251881Spetertypedef struct automatic_merge_t
11749251881Speter{
11750251881Speter  svn_client__pathrev_t *yca, *base, *right, *target;
11751251881Speter  svn_boolean_t is_reintegrate_like;
11752251881Speter  svn_boolean_t allow_mixed_rev, allow_local_mods, allow_switched_subtrees;
11753251881Speter} automatic_merge_t;
11754251881Speter
11755251881Speterstatic svn_error_t *
11756251881Speterclient_find_automatic_merge(automatic_merge_t **merge_p,
11757251881Speter                            const char *source_path_or_url,
11758251881Speter                            const svn_opt_revision_t *source_revision,
11759251881Speter                            const char *target_abspath,
11760251881Speter                            svn_boolean_t allow_mixed_rev,
11761251881Speter                            svn_boolean_t allow_local_mods,
11762251881Speter                            svn_boolean_t allow_switched_subtrees,
11763251881Speter                            svn_client_ctx_t *ctx,
11764251881Speter                            apr_pool_t *result_pool,
11765251881Speter                            apr_pool_t *scratch_pool);
11766251881Speter
11767251881Speterstatic svn_error_t *
11768251881Speterdo_automatic_merge_locked(conflict_report_t **conflict_report,
11769251881Speter                          const automatic_merge_t *merge,
11770251881Speter                          const char *target_abspath,
11771251881Speter                          svn_depth_t depth,
11772251881Speter                          svn_boolean_t diff_ignore_ancestry,
11773251881Speter                          svn_boolean_t force_delete,
11774251881Speter                          svn_boolean_t record_only,
11775251881Speter                          svn_boolean_t dry_run,
11776251881Speter                          const apr_array_header_t *merge_options,
11777251881Speter                          svn_client_ctx_t *ctx,
11778251881Speter                          apr_pool_t *result_pool,
11779251881Speter                          apr_pool_t *scratch_pool);
11780251881Speter
11781251881Spetersvn_error_t *
11782251881Spetersvn_client_merge_peg5(const char *source_path_or_url,
11783251881Speter                      const apr_array_header_t *ranges_to_merge,
11784251881Speter                      const svn_opt_revision_t *source_peg_revision,
11785251881Speter                      const char *target_wcpath,
11786251881Speter                      svn_depth_t depth,
11787251881Speter                      svn_boolean_t ignore_mergeinfo,
11788251881Speter                      svn_boolean_t diff_ignore_ancestry,
11789251881Speter                      svn_boolean_t force_delete,
11790251881Speter                      svn_boolean_t record_only,
11791251881Speter                      svn_boolean_t dry_run,
11792251881Speter                      svn_boolean_t allow_mixed_rev,
11793251881Speter                      const apr_array_header_t *merge_options,
11794251881Speter                      svn_client_ctx_t *ctx,
11795251881Speter                      apr_pool_t *pool)
11796251881Speter{
11797251881Speter  const char *target_abspath, *lock_abspath;
11798251881Speter  conflict_report_t *conflict_report;
11799251881Speter
11800251881Speter  /* No ranges to merge?  No problem. */
11801251881Speter  if (ranges_to_merge != NULL && ranges_to_merge->nelts == 0)
11802251881Speter    return SVN_NO_ERROR;
11803251881Speter
11804251881Speter  SVN_ERR(get_target_and_lock_abspath(&target_abspath, &lock_abspath,
11805251881Speter                                      target_wcpath, ctx, pool));
11806251881Speter
11807251881Speter  /* Do an automatic merge if no revision ranges are specified. */
11808251881Speter  if (ranges_to_merge == NULL)
11809251881Speter    {
11810251881Speter      automatic_merge_t *merge;
11811251881Speter
11812251881Speter      if (ignore_mergeinfo)
11813251881Speter        return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
11814251881Speter                                _("Cannot merge automatically while "
11815251881Speter                                  "ignoring mergeinfo"));
11816251881Speter
11817251881Speter      /* Find the details of the merge needed. */
11818251881Speter      SVN_ERR(client_find_automatic_merge(
11819251881Speter                                    &merge,
11820251881Speter                                    source_path_or_url, source_peg_revision,
11821251881Speter                                    target_abspath,
11822251881Speter                                    allow_mixed_rev,
11823251881Speter                                    TRUE /*allow_local_mods*/,
11824251881Speter                                    TRUE /*allow_switched_subtrees*/,
11825251881Speter                                    ctx, pool, pool));
11826251881Speter
11827251881Speter      if (!dry_run)
11828251881Speter        SVN_WC__CALL_WITH_WRITE_LOCK(
11829251881Speter          do_automatic_merge_locked(&conflict_report,
11830251881Speter                                    merge,
11831251881Speter                                    target_abspath, depth,
11832251881Speter                                    diff_ignore_ancestry,
11833251881Speter                                    force_delete, record_only, dry_run,
11834251881Speter                                    merge_options, ctx, pool, pool),
11835251881Speter          ctx->wc_ctx, lock_abspath, FALSE /* lock_anchor */, pool);
11836251881Speter      else
11837251881Speter        SVN_ERR(do_automatic_merge_locked(&conflict_report,
11838251881Speter                                    merge,
11839251881Speter                                    target_abspath, depth,
11840251881Speter                                    diff_ignore_ancestry,
11841251881Speter                                    force_delete, record_only, dry_run,
11842251881Speter                                    merge_options, ctx, pool, pool));
11843251881Speter    }
11844251881Speter  else if (!dry_run)
11845251881Speter    SVN_WC__CALL_WITH_WRITE_LOCK(
11846251881Speter      merge_peg_locked(&conflict_report,
11847251881Speter                       source_path_or_url, source_peg_revision,
11848251881Speter                       ranges_to_merge,
11849251881Speter                       target_abspath, depth, ignore_mergeinfo,
11850251881Speter                       diff_ignore_ancestry,
11851251881Speter                       force_delete, record_only, dry_run,
11852251881Speter                       allow_mixed_rev, merge_options, ctx, pool, pool),
11853251881Speter      ctx->wc_ctx, lock_abspath, FALSE /* lock_anchor */, pool);
11854251881Speter  else
11855251881Speter    SVN_ERR(merge_peg_locked(&conflict_report,
11856251881Speter                       source_path_or_url, source_peg_revision,
11857251881Speter                       ranges_to_merge,
11858251881Speter                       target_abspath, depth, ignore_mergeinfo,
11859251881Speter                       diff_ignore_ancestry,
11860251881Speter                       force_delete, record_only, dry_run,
11861251881Speter                       allow_mixed_rev, merge_options, ctx, pool, pool));
11862251881Speter
11863251881Speter  SVN_ERR(make_merge_conflict_error(conflict_report, pool));
11864251881Speter  return SVN_NO_ERROR;
11865251881Speter}
11866251881Speter
11867251881Speter
11868251881Speter/* The location-history of a branch.
11869251881Speter *
11870251881Speter * This structure holds the set of path-revisions occupied by a branch,
11871251881Speter * from an externally chosen 'tip' location back to its origin.  The
11872251881Speter * 'tip' location is the youngest location that we are considering on
11873251881Speter * the branch. */
11874251881Spetertypedef struct branch_history_t
11875251881Speter{
11876251881Speter  /* The tip location of the branch.  That is, the youngest location that's
11877251881Speter   * in the repository and that we're considering.  If we're considering a
11878251881Speter   * target branch right up to an uncommitted WC, then this is the WC base
11879251881Speter   * (pristine) location. */
11880251881Speter  svn_client__pathrev_t *tip;
11881251881Speter  /* The location-segment history, as mergeinfo. */
11882251881Speter  svn_mergeinfo_t history;
11883251881Speter  /* Whether the location-segment history reached as far as (necessarily
11884251881Speter     the root path in) revision 0 -- a fact that can't be represented as
11885251881Speter     mergeinfo. */
11886251881Speter  svn_boolean_t has_r0_history;
11887251881Speter} branch_history_t;
11888251881Speter
11889251881Speter/* Return the location on BRANCH_HISTORY at revision REV, or NULL if none. */
11890251881Speterstatic svn_client__pathrev_t *
11891251881Speterlocation_on_branch_at_rev(const branch_history_t *branch_history,
11892251881Speter                          svn_revnum_t rev,
11893251881Speter                          apr_pool_t *result_pool,
11894251881Speter                          apr_pool_t *scratch_pool)
11895251881Speter{
11896251881Speter  apr_hash_index_t *hi;
11897251881Speter
11898251881Speter  for (hi = apr_hash_first(scratch_pool, branch_history->history); hi;
11899251881Speter       hi = apr_hash_next(hi))
11900251881Speter    {
11901251881Speter      const char *fspath = svn__apr_hash_index_key(hi);
11902251881Speter      svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
11903251881Speter      int i;
11904251881Speter
11905251881Speter      for (i = 0; i < rangelist->nelts; i++)
11906251881Speter        {
11907251881Speter          svn_merge_range_t *r = APR_ARRAY_IDX(rangelist, i, svn_merge_range_t *);
11908251881Speter          if (r->start < rev && rev <= r->end)
11909251881Speter            {
11910251881Speter              return svn_client__pathrev_create_with_relpath(
11911251881Speter                       branch_history->tip->repos_root_url,
11912251881Speter                       branch_history->tip->repos_uuid,
11913251881Speter                       rev, fspath + 1, result_pool);
11914251881Speter            }
11915251881Speter        }
11916251881Speter    }
11917251881Speter  return NULL;
11918251881Speter}
11919251881Speter
11920251881Speter/* */
11921251881Spetertypedef struct source_and_target_t
11922251881Speter{
11923251881Speter  svn_client__pathrev_t *source;
11924251881Speter  svn_ra_session_t *source_ra_session;
11925251881Speter  branch_history_t source_branch;
11926251881Speter
11927251881Speter  merge_target_t *target;
11928251881Speter  svn_ra_session_t *target_ra_session;
11929251881Speter  branch_history_t target_branch;
11930251881Speter
11931251881Speter  /* Repos location of the youngest common ancestor of SOURCE and TARGET. */
11932251881Speter  svn_client__pathrev_t *yca;
11933251881Speter} source_and_target_t;
11934251881Speter
11935251881Speter/* Set *INTERSECTION_P to the intersection of BRANCH_HISTORY with the
11936251881Speter * revision range OLDEST_REV to YOUNGEST_REV (inclusive).
11937251881Speter *
11938251881Speter * If the intersection is empty, the result will be a branch history object
11939251881Speter * containing an empty (not null) history.
11940251881Speter *
11941251881Speter * ### The 'tip' of the result is currently unchanged.
11942251881Speter */
11943251881Speterstatic svn_error_t *
11944251881Speterbranch_history_intersect_range(branch_history_t **intersection_p,
11945251881Speter                               const branch_history_t *branch_history,
11946251881Speter                               svn_revnum_t oldest_rev,
11947251881Speter                               svn_revnum_t youngest_rev,
11948251881Speter                               apr_pool_t *result_pool,
11949251881Speter                               apr_pool_t *scratch_pool)
11950251881Speter{
11951251881Speter  branch_history_t *result = apr_palloc(result_pool, sizeof(*result));
11952251881Speter
11953251881Speter  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(oldest_rev));
11954251881Speter  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(youngest_rev));
11955251881Speter  SVN_ERR_ASSERT(oldest_rev >= 1);
11956251881Speter  /* Allow a just-empty range (oldest = youngest + 1) but not an
11957251881Speter   * arbitrary reverse range (such as oldest = youngest + 2). */
11958251881Speter  SVN_ERR_ASSERT(oldest_rev <= youngest_rev + 1);
11959251881Speter
11960251881Speter  if (oldest_rev <= youngest_rev)
11961251881Speter    {
11962251881Speter      SVN_ERR(svn_mergeinfo__filter_mergeinfo_by_ranges(
11963251881Speter                &result->history, branch_history->history,
11964251881Speter                youngest_rev, oldest_rev - 1, TRUE /* include_range */,
11965251881Speter                result_pool, scratch_pool));
11966251881Speter      result->history = svn_mergeinfo_dup(result->history, result_pool);
11967251881Speter    }
11968251881Speter  else
11969251881Speter    {
11970251881Speter      result->history = apr_hash_make(result_pool);
11971251881Speter    }
11972251881Speter  result->has_r0_history = FALSE;
11973251881Speter
11974251881Speter  /* ### TODO: Set RESULT->tip to the tip of the intersection. */
11975251881Speter  result->tip = svn_client__pathrev_dup(branch_history->tip, result_pool);
11976251881Speter
11977251881Speter  *intersection_p = result;
11978251881Speter  return SVN_NO_ERROR;
11979251881Speter}
11980251881Speter
11981251881Speter/* Set *OLDEST_P and *YOUNGEST_P to the oldest and youngest locations
11982251881Speter * (inclusive) along BRANCH.  OLDEST_P and/or YOUNGEST_P may be NULL if not
11983251881Speter * wanted.
11984251881Speter */
11985251881Speterstatic svn_error_t *
11986251881Speterbranch_history_get_endpoints(svn_client__pathrev_t **oldest_p,
11987251881Speter                             svn_client__pathrev_t **youngest_p,
11988251881Speter                             const branch_history_t *branch,
11989251881Speter                             apr_pool_t *result_pool,
11990251881Speter                             apr_pool_t *scratch_pool)
11991251881Speter{
11992251881Speter  svn_revnum_t youngest_rev, oldest_rev;
11993251881Speter
11994251881Speter  SVN_ERR(svn_mergeinfo__get_range_endpoints(
11995251881Speter            &youngest_rev, &oldest_rev,
11996251881Speter            branch->history, scratch_pool));
11997251881Speter  if (oldest_p)
11998251881Speter    *oldest_p = location_on_branch_at_rev(
11999251881Speter                  branch, oldest_rev + 1, result_pool, scratch_pool);
12000251881Speter  if (youngest_p)
12001251881Speter    *youngest_p = location_on_branch_at_rev(
12002251881Speter                    branch, youngest_rev, result_pool, scratch_pool);
12003251881Speter  return SVN_NO_ERROR;
12004251881Speter}
12005251881Speter
12006251881Speter/* Implements the svn_log_entry_receiver_t interface.
12007251881Speter
12008251881Speter  Set *BATON to LOG_ENTRY->revision and return SVN_ERR_CEASE_INVOCATION. */
12009251881Speterstatic svn_error_t *
12010251881Speteroperative_rev_receiver(void *baton,
12011251881Speter                       svn_log_entry_t *log_entry,
12012251881Speter                       apr_pool_t *pool)
12013251881Speter{
12014251881Speter  svn_revnum_t *operative_rev = baton;
12015251881Speter
12016251881Speter  *operative_rev = log_entry->revision;
12017251881Speter
12018251881Speter  /* We've found the youngest merged or oldest eligible revision, so
12019251881Speter     we're done...
12020251881Speter
12021251881Speter     ...but wait, shouldn't we care if LOG_ENTRY->NON_INHERITABLE is
12022251881Speter     true?  Because if it is, then LOG_ENTRY->REVISION is only
12023251881Speter     partially merged/elgibile!  And our only caller,
12024251881Speter     find_last_merged_location (via short_circuit_mergeinfo_log) is
12025251881Speter     interested in *fully* merged revisions.  That's all true, but if
12026251881Speter     find_last_merged_location() finds the youngest merged revision it
12027251881Speter     will also check for the oldest eligible revision.  So in the case
12028251881Speter     the youngest merged rev is non-inheritable, the *same* non-inheritable
12029251881Speter     rev will be found as the oldest eligible rev -- and
12030251881Speter     find_last_merged_location() handles that situation. */
12031251881Speter  return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
12032251881Speter}
12033251881Speter
12034253734Speter/* Wrapper around svn_client__mergeinfo_log. All arguments are as per
12035253734Speter   that private API.  The discover_changed_paths, depth, and revprops args to
12036253734Speter   svn_client__mergeinfo_log are always TRUE, svn_depth_infinity_t,
12037253734Speter   and empty array respectively.
12038251881Speter
12039251881Speter   If RECEIVER raises a SVN_ERR_CEASE_INVOCATION error, but still sets
12040251881Speter   *REVISION to a valid revnum, then clear the error.  Otherwise return
12041251881Speter   any error. */
12042251881Speterstatic svn_error_t*
12043253734Spetershort_circuit_mergeinfo_log(svn_mergeinfo_catalog_t *target_mergeinfo_cat,
12044253734Speter                            svn_boolean_t finding_merged,
12045251881Speter                            const char *target_path_or_url,
12046251881Speter                            const svn_opt_revision_t *target_peg_revision,
12047251881Speter                            const char *source_path_or_url,
12048251881Speter                            const svn_opt_revision_t *source_peg_revision,
12049251881Speter                            const svn_opt_revision_t *source_start_revision,
12050251881Speter                            const svn_opt_revision_t *source_end_revision,
12051251881Speter                            svn_log_entry_receiver_t receiver,
12052251881Speter                            svn_revnum_t *revision,
12053251881Speter                            svn_client_ctx_t *ctx,
12054253734Speter                            apr_pool_t *result_pool,
12055251881Speter                            apr_pool_t *scratch_pool)
12056251881Speter{
12057253734Speter  apr_array_header_t *revprops;
12058253734Speter  svn_error_t *err;
12059251881Speter
12060253734Speter  revprops = apr_array_make(scratch_pool, 0, sizeof(const char *));
12061253734Speter  err = svn_client__mergeinfo_log(finding_merged,
12062253734Speter                                  target_path_or_url,
12063253734Speter                                  target_peg_revision,
12064253734Speter                                  target_mergeinfo_cat,
12065253734Speter                                  source_path_or_url,
12066253734Speter                                  source_peg_revision,
12067253734Speter                                  source_start_revision,
12068253734Speter                                  source_end_revision,
12069253734Speter                                  receiver, revision,
12070253734Speter                                  TRUE, svn_depth_infinity,
12071253734Speter                                  revprops, ctx, result_pool,
12072253734Speter                                  scratch_pool);
12073253734Speter
12074251881Speter  if (err)
12075251881Speter    {
12076251881Speter      /* We expect RECEIVER to short-circuit the (potentially expensive) log
12077251881Speter         by raising an SVN_ERR_CEASE_INVOCATION -- see operative_rev_receiver.
12078251881Speter         So we can ignore that error, but only as long as we actually found a
12079251881Speter         valid revision. */
12080251881Speter      if (SVN_IS_VALID_REVNUM(*revision)
12081251881Speter          && err->apr_err == SVN_ERR_CEASE_INVOCATION)
12082251881Speter        {
12083251881Speter          svn_error_clear(err);
12084251881Speter          err = NULL;
12085251881Speter        }
12086251881Speter      else
12087251881Speter        {
12088251881Speter          return svn_error_trace(err);
12089251881Speter        }
12090251881Speter    }
12091251881Speter  return SVN_NO_ERROR;
12092251881Speter}
12093251881Speter
12094251881Speter/* Set *BASE_P to the last location on SOURCE_BRANCH such that all changes
12095251881Speter * on SOURCE_BRANCH after YCA up to and including *BASE_P have already
12096251881Speter * been fully merged into TARGET.
12097251881Speter *
12098251881Speter *               *BASE_P       TIP
12099251881Speter *          o-------o-----------o--- SOURCE_BRANCH
12100251881Speter *         /         \
12101251881Speter *   -----o     prev. \
12102251881Speter *     YCA \    merges \
12103251881Speter *          o-----------o----------- TARGET branch
12104251881Speter *
12105251881Speter * In terms of mergeinfo:
12106251881Speter *
12107251881Speter *     Source     a--...                     o=change, -=no-op revision
12108251881Speter *       branch  /   \
12109251881Speter *     YCA -->  o     a---o---o---o---o---   d=delete, a=add-as-a-copy
12110251881Speter *
12111251881Speter *     Eligible -.eee.eeeeeeeeeeeeeeeeeeee   .=not a source branch location
12112251881Speter *
12113251881Speter *     Tgt-mi   -.mmm.mm-mm-------m-------   m=merged to root of TARGET or
12114251881Speter *                                           subtree of TARGET with no
12115251881Speter *                                           operative changes outside of that
12116251881Speter *                                           subtree, -=not merged
12117251881Speter *
12118251881Speter *     Eligible -.---.--e--eeeeeee-eeeeeee
12119251881Speter *
12120251881Speter *     Next     --------^-----------------   BASE is just before here.
12121251881Speter *
12122251881Speter *             /         \
12123251881Speter *       -----o     prev. \
12124251881Speter *         YCA \    merges \
12125251881Speter *              o-----------o-------------
12126251881Speter *
12127251881Speter * If no revisions from SOURCE_BRANCH have been completely merged to TARGET,
12128251881Speter * then set *BASE_P to the YCA.
12129251881Speter */
12130251881Speterstatic svn_error_t *
12131251881Speterfind_last_merged_location(svn_client__pathrev_t **base_p,
12132251881Speter                          svn_client__pathrev_t *yca,
12133251881Speter                          const branch_history_t *source_branch,
12134251881Speter                          svn_client__pathrev_t *target,
12135251881Speter                          svn_client_ctx_t *ctx,
12136251881Speter                          apr_pool_t *result_pool,
12137251881Speter                          apr_pool_t *scratch_pool)
12138251881Speter{
12139251881Speter  svn_opt_revision_t source_peg_rev, source_start_rev, source_end_rev,
12140251881Speter    target_opt_rev;
12141251881Speter  svn_revnum_t youngest_merged_rev = SVN_INVALID_REVNUM;
12142253734Speter  svn_mergeinfo_catalog_t target_mergeinfo_cat = NULL;
12143251881Speter
12144251881Speter  source_peg_rev.kind = svn_opt_revision_number;
12145251881Speter  source_peg_rev.value.number = source_branch->tip->rev;
12146251881Speter  source_start_rev.kind = svn_opt_revision_number;
12147251881Speter  source_start_rev.value.number = yca->rev;
12148251881Speter  source_end_rev.kind = svn_opt_revision_number;
12149251881Speter  source_end_rev.value.number = source_branch->tip->rev;
12150251881Speter  target_opt_rev.kind = svn_opt_revision_number;
12151251881Speter  target_opt_rev.value.number = target->rev;
12152251881Speter
12153251881Speter  /* Find the youngest revision fully merged from SOURCE_BRANCH to TARGET,
12154251881Speter     if such a revision exists. */
12155253734Speter  SVN_ERR(short_circuit_mergeinfo_log(&target_mergeinfo_cat,
12156253734Speter                                      TRUE, /* Find merged */
12157251881Speter                                      target->url, &target_opt_rev,
12158251881Speter                                      source_branch->tip->url,
12159251881Speter                                      &source_peg_rev,
12160251881Speter                                      &source_end_rev, &source_start_rev,
12161251881Speter                                      operative_rev_receiver,
12162251881Speter                                      &youngest_merged_rev,
12163253734Speter                                      ctx, result_pool, scratch_pool));
12164251881Speter
12165251881Speter  if (!SVN_IS_VALID_REVNUM(youngest_merged_rev))
12166251881Speter    {
12167251881Speter      /* No revisions have been completely merged from SOURCE_BRANCH to
12168251881Speter         TARGET so the base for the next merge is the YCA. */
12169251881Speter      *base_p = yca;
12170251881Speter    }
12171251881Speter  else
12172251881Speter    {
12173251881Speter      /* One or more revisions have already been completely merged from
12174251881Speter         SOURCE_BRANCH to TARGET, now find the oldest revision, older
12175251881Speter         than the youngest merged revision, which is still eligible to
12176251881Speter         be merged, if such exists. */
12177251881Speter      branch_history_t *contiguous_source;
12178251881Speter      svn_revnum_t base_rev;
12179251881Speter      svn_revnum_t oldest_eligible_rev = SVN_INVALID_REVNUM;
12180251881Speter
12181251881Speter      /* If the only revisions eligible are younger than the youngest merged
12182251881Speter         revision we can simply assume that the youngest eligible revision
12183251881Speter         is the youngest merged revision.  Obviously this may not be true!
12184251881Speter         The revisions between the youngest merged revision and the tip of
12185251881Speter         the branch may have several inoperative revisions -- they may *all*
12186251881Speter         be inoperative revisions!  But for the purpose of this function
12187251881Speter         (i.e. finding the youngest revision after the YCA where all revs have
12188251881Speter         been merged) that doesn't matter. */
12189251881Speter      source_end_rev.value.number = youngest_merged_rev;
12190253734Speter      SVN_ERR(short_circuit_mergeinfo_log(&target_mergeinfo_cat,
12191253734Speter                                          FALSE, /* Find eligible */
12192251881Speter                                          target->url, &target_opt_rev,
12193251881Speter                                          source_branch->tip->url,
12194251881Speter                                          &source_peg_rev,
12195251881Speter                                          &source_start_rev, &source_end_rev,
12196251881Speter                                          operative_rev_receiver,
12197251881Speter                                          &oldest_eligible_rev,
12198253734Speter                                          ctx, scratch_pool, scratch_pool));
12199251881Speter
12200251881Speter      /* If there are revisions eligible for merging, use the oldest one
12201251881Speter         to calculate the base.  Otherwise there are no operative revisions
12202251881Speter         to merge and we can simple set the base to the youngest revision
12203251881Speter         already merged. */
12204251881Speter      if (SVN_IS_VALID_REVNUM(oldest_eligible_rev))
12205251881Speter        base_rev = oldest_eligible_rev - 1;
12206251881Speter      else
12207251881Speter        base_rev = youngest_merged_rev;
12208251881Speter
12209251881Speter      /* Find the branch location just before the oldest eligible rev.
12210251881Speter         (We can't just use the base revs calculated above because the branch
12211251881Speter         might have a gap there.) */
12212251881Speter      SVN_ERR(branch_history_intersect_range(&contiguous_source,
12213251881Speter                                             source_branch, yca->rev,
12214251881Speter                                             base_rev,
12215251881Speter                                             scratch_pool, scratch_pool));
12216251881Speter      SVN_ERR(branch_history_get_endpoints(NULL, base_p, contiguous_source,
12217251881Speter                                           result_pool, scratch_pool));
12218251881Speter    }
12219251881Speter
12220251881Speter  return SVN_NO_ERROR;
12221251881Speter}
12222251881Speter
12223251881Speter/* Find a merge base location on the target branch, like in a sync
12224251881Speter * merge.
12225251881Speter *
12226251881Speter *                BASE          S_T->source
12227251881Speter *          o-------o-----------o---
12228251881Speter *         /         \           \
12229251881Speter *   -----o     prev. \           \  this
12230251881Speter *     YCA \    merge  \           \ merge
12231251881Speter *          o-----------o-----------o
12232251881Speter *                                  S_T->target
12233251881Speter *
12234251881Speter * Set *BASE_P to BASE, the youngest location in the history of S_T->source
12235251881Speter * (at or after the YCA) at which all revisions up to BASE are effectively
12236251881Speter * merged into S_T->target.
12237251881Speter *
12238251881Speter * If no locations on the history of S_T->source are effectively merged to
12239251881Speter * S_T->target, set *BASE_P to the YCA.
12240251881Speter */
12241251881Speterstatic svn_error_t *
12242251881Speterfind_base_on_source(svn_client__pathrev_t **base_p,
12243251881Speter                    source_and_target_t *s_t,
12244251881Speter                    svn_client_ctx_t *ctx,
12245251881Speter                    apr_pool_t *result_pool,
12246251881Speter                    apr_pool_t *scratch_pool)
12247251881Speter{
12248251881Speter  SVN_ERR(find_last_merged_location(base_p,
12249251881Speter                                    s_t->yca,
12250251881Speter                                    &s_t->source_branch,
12251251881Speter                                    s_t->target_branch.tip,
12252251881Speter                                    ctx, result_pool, scratch_pool));
12253251881Speter  return SVN_NO_ERROR;
12254251881Speter}
12255251881Speter
12256251881Speter/* Find a merge base location on the target branch, like in a reintegrate
12257251881Speter * merge.
12258251881Speter *
12259251881Speter *                              S_T->source
12260251881Speter *          o-----------o-------o---
12261251881Speter *         /    prev.  /         \
12262251881Speter *   -----o     merge /           \  this
12263251881Speter *     YCA \         /             \ merge
12264251881Speter *          o-------o---------------o
12265251881Speter *                BASE              S_T->target
12266251881Speter *
12267251881Speter * Set *BASE_P to BASE, the youngest location in the history of S_T->target
12268251881Speter * (at or after the YCA) at which all revisions up to BASE are effectively
12269251881Speter * merged into S_T->source.
12270251881Speter *
12271251881Speter * If no locations on the history of S_T->target are effectively merged to
12272251881Speter * S_T->source, set *BASE_P to the YCA.
12273251881Speter */
12274251881Speterstatic svn_error_t *
12275251881Speterfind_base_on_target(svn_client__pathrev_t **base_p,
12276251881Speter                    source_and_target_t *s_t,
12277251881Speter                    svn_client_ctx_t *ctx,
12278251881Speter                    apr_pool_t *result_pool,
12279251881Speter                    apr_pool_t *scratch_pool)
12280251881Speter{
12281251881Speter  SVN_ERR(find_last_merged_location(base_p,
12282251881Speter                                    s_t->yca,
12283251881Speter                                    &s_t->target_branch,
12284251881Speter                                    s_t->source,
12285251881Speter                                    ctx, result_pool, scratch_pool));
12286251881Speter
12287251881Speter  return SVN_NO_ERROR;
12288251881Speter}
12289251881Speter
12290251881Speter/* The body of client_find_automatic_merge(), which see.
12291251881Speter */
12292251881Speterstatic svn_error_t *
12293251881Speterfind_automatic_merge(svn_client__pathrev_t **base_p,
12294251881Speter                     svn_boolean_t *is_reintegrate_like,
12295251881Speter                     source_and_target_t *s_t,
12296251881Speter                     svn_client_ctx_t *ctx,
12297251881Speter                     apr_pool_t *result_pool,
12298251881Speter                     apr_pool_t *scratch_pool)
12299251881Speter{
12300251881Speter  svn_client__pathrev_t *base_on_source, *base_on_target;
12301251881Speter
12302251881Speter  /* Get the location-history of each branch. */
12303251881Speter  s_t->source_branch.tip = s_t->source;
12304251881Speter  SVN_ERR(svn_client__get_history_as_mergeinfo(
12305251881Speter            &s_t->source_branch.history, &s_t->source_branch.has_r0_history,
12306251881Speter            s_t->source, SVN_INVALID_REVNUM, SVN_INVALID_REVNUM,
12307251881Speter            s_t->source_ra_session, ctx, scratch_pool));
12308251881Speter  s_t->target_branch.tip = &s_t->target->loc;
12309251881Speter  SVN_ERR(svn_client__get_history_as_mergeinfo(
12310251881Speter            &s_t->target_branch.history, &s_t->target_branch.has_r0_history,
12311251881Speter            &s_t->target->loc, SVN_INVALID_REVNUM, SVN_INVALID_REVNUM,
12312251881Speter            s_t->target_ra_session, ctx, scratch_pool));
12313251881Speter
12314253734Speter  SVN_ERR(svn_client__calc_youngest_common_ancestor(
12315253734Speter            &s_t->yca, s_t->source, s_t->source_branch.history,
12316253734Speter            s_t->source_branch.has_r0_history,
12317253734Speter            &s_t->target->loc, s_t->target_branch.history,
12318253734Speter            s_t->target_branch.has_r0_history,
12319253734Speter            result_pool, scratch_pool));
12320253734Speter
12321251881Speter  if (! s_t->yca)
12322251881Speter    return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE, NULL,
12323251881Speter                             _("'%s@%ld' must be ancestrally related to "
12324251881Speter                               "'%s@%ld'"),
12325251881Speter                             s_t->source->url, s_t->source->rev,
12326251881Speter                             s_t->target->loc.url, s_t->target->loc.rev);
12327251881Speter
12328251881Speter  /* Find the latest revision of A synced to B and the latest
12329251881Speter   * revision of B synced to A.
12330251881Speter   *
12331251881Speter   *   base_on_source = youngest_complete_synced_point(source, target)
12332251881Speter   *   base_on_target = youngest_complete_synced_point(target, source)
12333251881Speter   */
12334251881Speter  SVN_ERR(find_base_on_source(&base_on_source, s_t,
12335251881Speter                              ctx, scratch_pool, scratch_pool));
12336251881Speter  SVN_ERR(find_base_on_target(&base_on_target, s_t,
12337251881Speter                              ctx, scratch_pool, scratch_pool));
12338251881Speter
12339251881Speter  /* Choose a base. */
12340251881Speter  if (base_on_source->rev >= base_on_target->rev)
12341251881Speter    {
12342251881Speter      *base_p = base_on_source;
12343251881Speter      *is_reintegrate_like = FALSE;
12344251881Speter    }
12345251881Speter  else
12346251881Speter    {
12347251881Speter      *base_p = base_on_target;
12348251881Speter      *is_reintegrate_like = TRUE;
12349251881Speter    }
12350251881Speter
12351251881Speter  return SVN_NO_ERROR;
12352251881Speter}
12353251881Speter
12354251881Speter/** Find out what kind of automatic merge would be needed, when the target
12355251881Speter * is only known as a repository location rather than a WC.
12356251881Speter *
12357251881Speter * Like find_automatic_merge() except that the target is
12358251881Speter * specified by @a target_path_or_url at @a target_revision, which must
12359251881Speter * refer to a repository location, instead of by a WC path argument.
12360251881Speter */
12361251881Speterstatic svn_error_t *
12362251881Speterfind_automatic_merge_no_wc(automatic_merge_t **merge_p,
12363251881Speter                           const char *source_path_or_url,
12364251881Speter                           const svn_opt_revision_t *source_revision,
12365251881Speter                           const char *target_path_or_url,
12366251881Speter                           const svn_opt_revision_t *target_revision,
12367251881Speter                           svn_client_ctx_t *ctx,
12368251881Speter                           apr_pool_t *result_pool,
12369251881Speter                           apr_pool_t *scratch_pool)
12370251881Speter{
12371251881Speter  source_and_target_t *s_t = apr_palloc(scratch_pool, sizeof(*s_t));
12372251881Speter  svn_client__pathrev_t *target_loc;
12373251881Speter  automatic_merge_t *merge = apr_palloc(result_pool, sizeof(*merge));
12374251881Speter
12375251881Speter  /* Source */
12376251881Speter  SVN_ERR(svn_client__ra_session_from_path2(
12377251881Speter            &s_t->source_ra_session, &s_t->source,
12378251881Speter            source_path_or_url, NULL, source_revision, source_revision,
12379251881Speter            ctx, result_pool));
12380251881Speter
12381251881Speter  /* Target */
12382251881Speter  SVN_ERR(svn_client__ra_session_from_path2(
12383251881Speter            &s_t->target_ra_session, &target_loc,
12384251881Speter            target_path_or_url, NULL, target_revision, target_revision,
12385251881Speter            ctx, result_pool));
12386251881Speter  s_t->target = apr_palloc(scratch_pool, sizeof(*s_t->target));
12387251881Speter  s_t->target->abspath = NULL;  /* indicate the target is not a WC */
12388251881Speter  s_t->target->loc = *target_loc;
12389251881Speter
12390251881Speter  SVN_ERR(find_automatic_merge(&merge->base, &merge->is_reintegrate_like, s_t,
12391251881Speter                               ctx, result_pool, scratch_pool));
12392251881Speter
12393251881Speter  merge->right = s_t->source;
12394251881Speter  merge->target = &s_t->target->loc;
12395251881Speter  merge->yca = s_t->yca;
12396251881Speter  *merge_p = merge;
12397251881Speter
12398251881Speter  return SVN_NO_ERROR;
12399251881Speter}
12400251881Speter
12401251881Speter/* Find the information needed to merge all unmerged changes from a source
12402251881Speter * branch into a target branch.
12403251881Speter *
12404251881Speter * Set @a *merge_p to the information needed to merge all unmerged changes
12405251881Speter * (up to @a source_revision) from the source branch @a source_path_or_url
12406251881Speter * at @a source_revision into the target WC at @a target_abspath.
12407251881Speter *
12408251881Speter * The flags @a allow_mixed_rev, @a allow_local_mods and
12409251881Speter * @a allow_switched_subtrees enable merging into a WC that is in any or all
12410251881Speter * of the states described by their names, but only if this function decides
12411251881Speter * that the merge will be in the same direction as the last automatic merge.
12412251881Speter * If, on the other hand, the last automatic merge was in the opposite
12413251881Speter * direction, then such states of the WC are not allowed regardless
12414251881Speter * of these flags.  This function merely records these flags in the
12415251881Speter * @a *merge_p structure; do_automatic_merge_locked() checks the WC
12416251881Speter * state for compliance.
12417251881Speter *
12418251881Speter * Allocate the @a *merge_p structure in @a result_pool.
12419251881Speter */
12420251881Speterstatic svn_error_t *
12421251881Speterclient_find_automatic_merge(automatic_merge_t **merge_p,
12422251881Speter                            const char *source_path_or_url,
12423251881Speter                            const svn_opt_revision_t *source_revision,
12424251881Speter                            const char *target_abspath,
12425251881Speter                            svn_boolean_t allow_mixed_rev,
12426251881Speter                            svn_boolean_t allow_local_mods,
12427251881Speter                            svn_boolean_t allow_switched_subtrees,
12428251881Speter                            svn_client_ctx_t *ctx,
12429251881Speter                            apr_pool_t *result_pool,
12430251881Speter                            apr_pool_t *scratch_pool)
12431251881Speter{
12432251881Speter  source_and_target_t *s_t = apr_palloc(result_pool, sizeof(*s_t));
12433251881Speter  automatic_merge_t *merge = apr_palloc(result_pool, sizeof(*merge));
12434251881Speter
12435251881Speter  /* "Open" the target WC.  Check the target WC for mixed-rev, local mods and
12436251881Speter   * switched subtrees yet to faster exit and notify user before contacting
12437251881Speter   * with server.  After we find out what kind of merge is required, then if a
12438251881Speter   * reintegrate-like merge is required we'll do the stricter checks, in
12439251881Speter   * do_automatic_merge_locked(). */
12440251881Speter  SVN_ERR(open_target_wc(&s_t->target, target_abspath,
12441251881Speter                         allow_mixed_rev,
12442251881Speter                         allow_local_mods,
12443251881Speter                         allow_switched_subtrees,
12444251881Speter                         ctx, result_pool, scratch_pool));
12445251881Speter
12446251881Speter  /* Open RA sessions to the source and target trees. */
12447251881Speter  SVN_ERR(svn_client_open_ra_session2(&s_t->target_ra_session,
12448251881Speter                                      s_t->target->loc.url,
12449251881Speter                                      s_t->target->abspath,
12450251881Speter                                      ctx, result_pool, scratch_pool));
12451251881Speter  /* ### check for null URL (i.e. added path) here, like in reintegrate? */
12452251881Speter  SVN_ERR(svn_client__ra_session_from_path2(
12453251881Speter            &s_t->source_ra_session, &s_t->source,
12454251881Speter            source_path_or_url, NULL, source_revision, source_revision,
12455251881Speter            ctx, result_pool));
12456251881Speter
12457251881Speter  /* Check source is in same repos as target. */
12458251881Speter  SVN_ERR(check_same_repos(s_t->source, source_path_or_url,
12459251881Speter                           &s_t->target->loc, target_abspath,
12460251881Speter                           TRUE /* strict_urls */, scratch_pool));
12461251881Speter
12462251881Speter  SVN_ERR(find_automatic_merge(&merge->base, &merge->is_reintegrate_like, s_t,
12463251881Speter                               ctx, result_pool, scratch_pool));
12464251881Speter  merge->yca = s_t->yca;
12465251881Speter  merge->right = s_t->source;
12466251881Speter  merge->allow_mixed_rev = allow_mixed_rev;
12467251881Speter  merge->allow_local_mods = allow_local_mods;
12468251881Speter  merge->allow_switched_subtrees = allow_switched_subtrees;
12469251881Speter
12470251881Speter  *merge_p = merge;
12471251881Speter
12472251881Speter  /* TODO: Close the source and target sessions here? */
12473251881Speter
12474251881Speter  return SVN_NO_ERROR;
12475251881Speter}
12476251881Speter
12477251881Speter/* Perform an automatic merge, given the information in MERGE which
12478251881Speter * must have come from calling client_find_automatic_merge().
12479251881Speter *
12480251881Speter * Four locations are inputs: YCA, BASE, RIGHT, TARGET, as shown
12481251881Speter * depending on whether the base is on the source branch or the target
12482251881Speter * branch of this merge.
12483251881Speter *
12484251881Speter *                            RIGHT     (is_reintegrate_like)
12485251881Speter *          o-----------o-------o---
12486251881Speter *         /    prev.  /         \
12487251881Speter *   -----o     merge /           \  this
12488251881Speter *     YCA \         /             \ merge
12489251881Speter *          o-------o---------------o
12490251881Speter *                BASE            TARGET
12491251881Speter *
12492251881Speter * or
12493251881Speter *
12494251881Speter *                BASE        RIGHT      (! is_reintegrate_like)
12495251881Speter *          o-------o-----------o---
12496251881Speter *         /         \           \
12497251881Speter *   -----o     prev. \           \  this
12498251881Speter *     YCA \    merge  \           \ merge
12499251881Speter *          o-----------o-----------o
12500251881Speter *                                TARGET
12501251881Speter *
12502251881Speter * ### TODO: The reintegrate-like code path does not yet
12503251881Speter * eliminate already-cherry-picked revisions from the source.
12504251881Speter */
12505251881Speterstatic svn_error_t *
12506251881Speterdo_automatic_merge_locked(conflict_report_t **conflict_report,
12507251881Speter                          const automatic_merge_t *merge,
12508251881Speter                          const char *target_abspath,
12509251881Speter                          svn_depth_t depth,
12510251881Speter                          svn_boolean_t diff_ignore_ancestry,
12511251881Speter                          svn_boolean_t force_delete,
12512251881Speter                          svn_boolean_t record_only,
12513251881Speter                          svn_boolean_t dry_run,
12514251881Speter                          const apr_array_header_t *merge_options,
12515251881Speter                          svn_client_ctx_t *ctx,
12516251881Speter                          apr_pool_t *result_pool,
12517251881Speter                          apr_pool_t *scratch_pool)
12518251881Speter{
12519251881Speter  merge_target_t *target;
12520251881Speter  svn_boolean_t reintegrate_like = merge->is_reintegrate_like;
12521251881Speter  svn_boolean_t use_sleep = FALSE;
12522251881Speter  svn_error_t *err;
12523251881Speter
12524251881Speter  SVN_ERR(open_target_wc(&target, target_abspath,
12525251881Speter                         merge->allow_mixed_rev && ! reintegrate_like,
12526251881Speter                         merge->allow_local_mods && ! reintegrate_like,
12527251881Speter                         merge->allow_switched_subtrees && ! reintegrate_like,
12528251881Speter                         ctx, scratch_pool, scratch_pool));
12529251881Speter
12530251881Speter  if (reintegrate_like)
12531251881Speter    {
12532251881Speter      merge_source_t source;
12533251881Speter      svn_ra_session_t *base_ra_session = NULL;
12534251881Speter      svn_ra_session_t *right_ra_session = NULL;
12535251881Speter      svn_ra_session_t *target_ra_session = NULL;
12536251881Speter
12537251881Speter      if (record_only)
12538251881Speter        return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
12539251881Speter                                _("The required merge is reintegrate-like, "
12540251881Speter                                  "and the record-only option "
12541251881Speter                                  "cannot be used with this kind of merge"));
12542251881Speter
12543251881Speter      if (depth != svn_depth_unknown)
12544251881Speter        return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
12545251881Speter                                _("The required merge is reintegrate-like, "
12546251881Speter                                  "and the depth option "
12547251881Speter                                  "cannot be used with this kind of merge"));
12548251881Speter
12549251881Speter      if (force_delete)
12550251881Speter        return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
12551251881Speter                                _("The required merge is reintegrate-like, "
12552251881Speter                                  "and the force_delete option "
12553251881Speter                                  "cannot be used with this kind of merge"));
12554251881Speter
12555251881Speter      SVN_ERR(ensure_ra_session_url(&base_ra_session, merge->base->url,
12556251881Speter                                    target->abspath, ctx, scratch_pool));
12557251881Speter      SVN_ERR(ensure_ra_session_url(&right_ra_session, merge->right->url,
12558251881Speter                                    target->abspath, ctx, scratch_pool));
12559251881Speter      SVN_ERR(ensure_ra_session_url(&target_ra_session, target->loc.url,
12560251881Speter                                    target->abspath, ctx, scratch_pool));
12561251881Speter
12562251881Speter      /* Check for and reject any abnormalities -- such as revisions that
12563251881Speter       * have not yet been merged in the opposite direction -- that a
12564251881Speter       * 'reintegrate' merge would have rejected. */
12565251881Speter      {
12566251881Speter        merge_source_t *source2;
12567251881Speter
12568251881Speter        SVN_ERR(find_reintegrate_merge(&source2, NULL,
12569251881Speter                                       right_ra_session, merge->right,
12570251881Speter                                       target_ra_session, target,
12571251881Speter                                       ctx, scratch_pool, scratch_pool));
12572251881Speter      }
12573251881Speter
12574251881Speter      source.loc1 = merge->base;
12575251881Speter      source.loc2 = merge->right;
12576251881Speter      source.ancestral = ! merge->is_reintegrate_like;
12577251881Speter
12578251881Speter      err = merge_cousins_and_supplement_mergeinfo(conflict_report,
12579251881Speter                                                   &use_sleep,
12580251881Speter                                                   target,
12581251881Speter                                                   base_ra_session,
12582251881Speter                                                   right_ra_session,
12583251881Speter                                                   &source, merge->yca,
12584251881Speter                                                   TRUE /* same_repos */,
12585251881Speter                                                   depth,
12586251881Speter                                                   FALSE /*diff_ignore_ancestry*/,
12587251881Speter                                                   force_delete, record_only,
12588251881Speter                                                   dry_run,
12589251881Speter                                                   merge_options,
12590251881Speter                                                   ctx,
12591251881Speter                                                   result_pool, scratch_pool);
12592251881Speter    }
12593251881Speter  else /* ! merge->is_reintegrate_like */
12594251881Speter    {
12595251881Speter      /* Ignoring the base that we found, we pass the YCA instead and let
12596251881Speter         do_merge() work out which subtrees need which revision ranges to
12597251881Speter         be merged.  This enables do_merge() to fill in revision-range
12598251881Speter         gaps that are older than the base that we calculated (which is
12599251881Speter         for the root path of the merge).
12600251881Speter
12601251881Speter         An improvement would be to change find_automatic_merge() to
12602251881Speter         find the base for each sutree, and then here use the oldest base
12603251881Speter         among all subtrees. */
12604251881Speter      apr_array_header_t *merge_sources;
12605251881Speter      svn_ra_session_t *ra_session = NULL;
12606251881Speter
12607251881Speter      /* Normalize our merge sources, do_merge() requires this.  See the
12608251881Speter         'MERGEINFO MERGE SOURCE NORMALIZATION' global comment. */
12609251881Speter      SVN_ERR(ensure_ra_session_url(&ra_session, merge->right->url,
12610251881Speter                                    target->abspath, ctx, scratch_pool));
12611251881Speter      SVN_ERR(normalize_merge_sources_internal(
12612251881Speter        &merge_sources, merge->right,
12613251881Speter        svn_rangelist__initialize(merge->yca->rev, merge->right->rev, TRUE,
12614251881Speter                                  scratch_pool),
12615251881Speter        ra_session, ctx, scratch_pool, scratch_pool));
12616251881Speter
12617251881Speter      err = do_merge(NULL, NULL, conflict_report, &use_sleep,
12618251881Speter                     merge_sources, target, ra_session,
12619251881Speter                     TRUE /*related*/, TRUE /*same_repos*/,
12620251881Speter                     FALSE /*ignore_mergeinfo*/, diff_ignore_ancestry,
12621251881Speter                     force_delete, dry_run,
12622251881Speter                     record_only, NULL, FALSE, FALSE, depth, merge_options,
12623251881Speter                     ctx, result_pool, scratch_pool);
12624251881Speter    }
12625251881Speter
12626251881Speter  if (use_sleep)
12627251881Speter    svn_io_sleep_for_timestamps(target_abspath, scratch_pool);
12628251881Speter
12629251881Speter  SVN_ERR(err);
12630251881Speter
12631251881Speter  return SVN_NO_ERROR;
12632251881Speter}
12633251881Speter
12634251881Spetersvn_error_t *
12635251881Spetersvn_client_get_merging_summary(svn_boolean_t *needs_reintegration,
12636251881Speter                               const char **yca_url, svn_revnum_t *yca_rev,
12637251881Speter                               const char **base_url, svn_revnum_t *base_rev,
12638251881Speter                               const char **right_url, svn_revnum_t *right_rev,
12639251881Speter                               const char **target_url, svn_revnum_t *target_rev,
12640251881Speter                               const char **repos_root_url,
12641251881Speter                               const char *source_path_or_url,
12642251881Speter                               const svn_opt_revision_t *source_revision,
12643251881Speter                               const char *target_path_or_url,
12644251881Speter                               const svn_opt_revision_t *target_revision,
12645251881Speter                               svn_client_ctx_t *ctx,
12646251881Speter                               apr_pool_t *result_pool,
12647251881Speter                               apr_pool_t *scratch_pool)
12648251881Speter{
12649251881Speter  svn_boolean_t target_is_wc;
12650251881Speter  automatic_merge_t *merge;
12651251881Speter
12652251881Speter  target_is_wc = (! svn_path_is_url(target_path_or_url))
12653251881Speter                 && (target_revision->kind == svn_opt_revision_unspecified
12654251881Speter                     || target_revision->kind == svn_opt_revision_working);
12655251881Speter  if (target_is_wc)
12656251881Speter    SVN_ERR(client_find_automatic_merge(
12657251881Speter              &merge,
12658251881Speter              source_path_or_url, source_revision,
12659251881Speter              target_path_or_url,
12660251881Speter              TRUE, TRUE, TRUE,  /* allow_* */
12661251881Speter              ctx, scratch_pool, scratch_pool));
12662251881Speter  else
12663251881Speter    SVN_ERR(find_automatic_merge_no_wc(
12664251881Speter              &merge,
12665251881Speter              source_path_or_url, source_revision,
12666251881Speter              target_path_or_url, target_revision,
12667251881Speter              ctx, scratch_pool, scratch_pool));
12668251881Speter
12669251881Speter  if (needs_reintegration)
12670251881Speter    *needs_reintegration = merge->is_reintegrate_like;
12671251881Speter  if (yca_url)
12672251881Speter    *yca_url = apr_pstrdup(result_pool, merge->yca->url);
12673251881Speter  if (yca_rev)
12674251881Speter    *yca_rev = merge->yca->rev;
12675251881Speter  if (base_url)
12676251881Speter    *base_url = apr_pstrdup(result_pool, merge->base->url);
12677251881Speter  if (base_rev)
12678251881Speter    *base_rev = merge->base->rev;
12679251881Speter  if (right_url)
12680251881Speter    *right_url = apr_pstrdup(result_pool, merge->right->url);
12681251881Speter  if (right_rev)
12682251881Speter    *right_rev = merge->right->rev;
12683251881Speter  if (target_url)
12684251881Speter    *target_url = apr_pstrdup(result_pool, merge->target->url);
12685251881Speter  if (target_rev)
12686251881Speter    *target_rev = merge->target->rev;
12687251881Speter  if (repos_root_url)
12688251881Speter    *repos_root_url = apr_pstrdup(result_pool, merge->yca->repos_root_url);
12689251881Speter
12690251881Speter  return SVN_NO_ERROR;
12691251881Speter}
12692