1251881Speter/*
2251881Speter * ra.c :  routines for interacting with the RA layer
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#include <apr_pools.h>
27251881Speter
28251881Speter#include "svn_error.h"
29251881Speter#include "svn_hash.h"
30251881Speter#include "svn_pools.h"
31251881Speter#include "svn_string.h"
32251881Speter#include "svn_sorts.h"
33251881Speter#include "svn_ra.h"
34251881Speter#include "svn_client.h"
35251881Speter#include "svn_dirent_uri.h"
36251881Speter#include "svn_path.h"
37251881Speter#include "svn_props.h"
38251881Speter#include "svn_mergeinfo.h"
39251881Speter#include "client.h"
40251881Speter#include "mergeinfo.h"
41251881Speter
42251881Speter#include "svn_private_config.h"
43251881Speter#include "private/svn_wc_private.h"
44251881Speter#include "private/svn_client_private.h"
45299742Sdim#include "private/svn_sorts_private.h"
46251881Speter
47251881Speter
48251881Speter/* This is the baton that we pass svn_ra_open3(), and is associated with
49251881Speter   the callback table we provide to RA. */
50251881Spetertypedef struct callback_baton_t
51251881Speter{
52251881Speter  /* Holds the directory that corresponds to the REPOS_URL at svn_ra_open3()
53251881Speter     time. When callbacks specify a relative path, they are joined with
54251881Speter     this base directory. */
55251881Speter  const char *base_dir_abspath;
56251881Speter
57251881Speter  /* TEMPORARY: Is 'base_dir_abspath' a versioned path?  cmpilato
58251881Speter     suspects that the commit-to-multiple-disjoint-working-copies
59251881Speter     code is getting this all wrong, sometimes passing an unversioned
60251881Speter     (or versioned in a foreign wc) path here which sorta kinda
61251881Speter     happens to work most of the time but is ultimately incorrect.  */
62251881Speter  svn_boolean_t base_dir_isversioned;
63251881Speter
64251881Speter  /* Used as wri_abspath for obtaining access to the pristine store */
65251881Speter  const char *wcroot_abspath;
66251881Speter
67251881Speter  /* An array of svn_client_commit_item3_t * structures, present only
68251881Speter     during working copy commits. */
69251881Speter  const apr_array_header_t *commit_items;
70251881Speter
71251881Speter  /* A client context. */
72251881Speter  svn_client_ctx_t *ctx;
73251881Speter
74299742Sdim  /* Last progress reported by progress callback. */
75299742Sdim  apr_off_t last_progress;
76251881Speter} callback_baton_t;
77251881Speter
78251881Speter
79251881Speter
80251881Speterstatic svn_error_t *
81251881Speteropen_tmp_file(apr_file_t **fp,
82251881Speter              void *callback_baton,
83251881Speter              apr_pool_t *pool)
84251881Speter{
85251881Speter  return svn_error_trace(svn_io_open_unique_file3(fp, NULL, NULL,
86251881Speter                                  svn_io_file_del_on_pool_cleanup,
87251881Speter                                  pool, pool));
88251881Speter}
89251881Speter
90251881Speter
91251881Speter/* This implements the 'svn_ra_get_wc_prop_func_t' interface. */
92251881Speterstatic svn_error_t *
93251881Speterget_wc_prop(void *baton,
94251881Speter            const char *relpath,
95251881Speter            const char *name,
96251881Speter            const svn_string_t **value,
97251881Speter            apr_pool_t *pool)
98251881Speter{
99251881Speter  callback_baton_t *cb = baton;
100251881Speter  const char *local_abspath = NULL;
101251881Speter  svn_error_t *err;
102251881Speter
103251881Speter  *value = NULL;
104251881Speter
105251881Speter  /* If we have a list of commit_items, search through that for a
106251881Speter     match for this relative URL. */
107251881Speter  if (cb->commit_items)
108251881Speter    {
109251881Speter      int i;
110251881Speter      for (i = 0; i < cb->commit_items->nelts; i++)
111251881Speter        {
112251881Speter          svn_client_commit_item3_t *item
113251881Speter            = APR_ARRAY_IDX(cb->commit_items, i, svn_client_commit_item3_t *);
114251881Speter
115251881Speter          if (! strcmp(relpath, item->session_relpath))
116251881Speter            {
117251881Speter              SVN_ERR_ASSERT(svn_dirent_is_absolute(item->path));
118251881Speter              local_abspath = item->path;
119251881Speter              break;
120251881Speter            }
121251881Speter        }
122251881Speter
123251881Speter      /* Commits can only query relpaths in the commit_items list
124251881Speter         since the commit driver traverses paths as they are, or will
125251881Speter         be, in the repository.  Non-commits query relpaths in the
126251881Speter         working copy. */
127251881Speter      if (! local_abspath)
128251881Speter        return SVN_NO_ERROR;
129251881Speter    }
130251881Speter
131251881Speter  /* If we don't have a base directory, then there are no properties. */
132251881Speter  else if (cb->base_dir_abspath == NULL)
133251881Speter    return SVN_NO_ERROR;
134251881Speter
135251881Speter  else
136251881Speter    local_abspath = svn_dirent_join(cb->base_dir_abspath, relpath, pool);
137251881Speter
138251881Speter  err = svn_wc_prop_get2(value, cb->ctx->wc_ctx, local_abspath, name,
139251881Speter                         pool, pool);
140251881Speter  if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
141251881Speter    {
142251881Speter      svn_error_clear(err);
143251881Speter      err = NULL;
144251881Speter    }
145251881Speter  return svn_error_trace(err);
146251881Speter}
147251881Speter
148251881Speter/* This implements the 'svn_ra_push_wc_prop_func_t' interface. */
149251881Speterstatic svn_error_t *
150251881Speterpush_wc_prop(void *baton,
151251881Speter             const char *relpath,
152251881Speter             const char *name,
153251881Speter             const svn_string_t *value,
154251881Speter             apr_pool_t *pool)
155251881Speter{
156251881Speter  callback_baton_t *cb = baton;
157251881Speter  int i;
158251881Speter
159251881Speter  /* If we're committing, search through the commit_items list for a
160251881Speter     match for this relative URL. */
161251881Speter  if (! cb->commit_items)
162251881Speter    return svn_error_createf
163251881Speter      (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
164251881Speter       _("Attempt to set wcprop '%s' on '%s' in a non-commit operation"),
165251881Speter       name, svn_dirent_local_style(relpath, pool));
166251881Speter
167251881Speter  for (i = 0; i < cb->commit_items->nelts; i++)
168251881Speter    {
169251881Speter      svn_client_commit_item3_t *item
170251881Speter        = APR_ARRAY_IDX(cb->commit_items, i, svn_client_commit_item3_t *);
171251881Speter
172251881Speter      if (strcmp(relpath, item->session_relpath) == 0)
173251881Speter        {
174251881Speter          apr_pool_t *changes_pool = item->incoming_prop_changes->pool;
175251881Speter          svn_prop_t *prop = apr_palloc(changes_pool, sizeof(*prop));
176251881Speter
177251881Speter          prop->name = apr_pstrdup(changes_pool, name);
178251881Speter          if (value)
179251881Speter            prop->value = svn_string_dup(value, changes_pool);
180251881Speter          else
181251881Speter            prop->value = NULL;
182251881Speter
183251881Speter          /* Buffer the propchange to take effect during the
184251881Speter             post-commit process. */
185251881Speter          APR_ARRAY_PUSH(item->incoming_prop_changes, svn_prop_t *) = prop;
186251881Speter          return SVN_NO_ERROR;
187251881Speter        }
188251881Speter    }
189251881Speter
190251881Speter  return SVN_NO_ERROR;
191251881Speter}
192251881Speter
193251881Speter
194251881Speter/* This implements the 'svn_ra_set_wc_prop_func_t' interface. */
195251881Speterstatic svn_error_t *
196251881Speterset_wc_prop(void *baton,
197251881Speter            const char *path,
198251881Speter            const char *name,
199251881Speter            const svn_string_t *value,
200251881Speter            apr_pool_t *pool)
201251881Speter{
202251881Speter  callback_baton_t *cb = baton;
203251881Speter  const char *local_abspath;
204251881Speter
205251881Speter  local_abspath = svn_dirent_join(cb->base_dir_abspath, path, pool);
206251881Speter
207251881Speter  /* We pass 1 for the 'force' parameter here.  Since the property is
208251881Speter     coming from the repository, we definitely want to accept it.
209251881Speter     Ideally, we'd raise a conflict if, say, the received property is
210251881Speter     svn:eol-style yet the file has a locally added svn:mime-type
211251881Speter     claiming that it's binary.  Probably the repository is still
212251881Speter     right, but the conflict would remind the user to make sure.
213251881Speter     Unfortunately, we don't have a clean mechanism for doing that
214251881Speter     here, so we just set the property and hope for the best. */
215251881Speter  return svn_error_trace(svn_wc_prop_set4(cb->ctx->wc_ctx, local_abspath,
216251881Speter                                          name,
217251881Speter                                          value, svn_depth_empty,
218251881Speter                                          TRUE /* skip_checks */,
219251881Speter                                          NULL /* changelist_filter */,
220251881Speter                                          NULL, NULL /* cancellation */,
221251881Speter                                          NULL, NULL /* notification */,
222251881Speter                                          pool));
223251881Speter}
224251881Speter
225251881Speter
226251881Speter/* This implements the `svn_ra_invalidate_wc_props_func_t' interface. */
227251881Speterstatic svn_error_t *
228251881Speterinvalidate_wc_props(void *baton,
229251881Speter                    const char *path,
230251881Speter                    const char *prop_name,
231251881Speter                    apr_pool_t *pool)
232251881Speter{
233251881Speter  callback_baton_t *cb = baton;
234251881Speter  const char *local_abspath;
235251881Speter
236251881Speter  local_abspath = svn_dirent_join(cb->base_dir_abspath, path, pool);
237251881Speter
238251881Speter  /* It's easier just to clear the whole dav_cache than to remove
239251881Speter     individual items from it recursively like this.  And since we
240251881Speter     know that the RA providers that ship with Subversion only
241251881Speter     invalidate the one property they use the most from this cache,
242251881Speter     and that we're intentionally trying to get away from the use of
243251881Speter     the cache altogether anyway, there's little to lose in wiping the
244251881Speter     whole cache.  Is it the most well-behaved approach to take?  Not
245251881Speter     so much.  We choose not to care.  */
246251881Speter  return svn_error_trace(svn_wc__node_clear_dav_cache_recursive(
247251881Speter                              cb->ctx->wc_ctx, local_abspath, pool));
248251881Speter}
249251881Speter
250251881Speter
251251881Speter/* This implements the `svn_ra_get_wc_contents_func_t' interface. */
252251881Speterstatic svn_error_t *
253251881Speterget_wc_contents(void *baton,
254251881Speter                svn_stream_t **contents,
255251881Speter                const svn_checksum_t *checksum,
256251881Speter                apr_pool_t *pool)
257251881Speter{
258251881Speter  callback_baton_t *cb = baton;
259251881Speter
260251881Speter  if (! cb->wcroot_abspath)
261251881Speter    {
262251881Speter      *contents = NULL;
263251881Speter      return SVN_NO_ERROR;
264251881Speter    }
265251881Speter
266251881Speter  return svn_error_trace(
267251881Speter             svn_wc__get_pristine_contents_by_checksum(contents,
268251881Speter                                                       cb->ctx->wc_ctx,
269251881Speter                                                       cb->wcroot_abspath,
270251881Speter                                                       checksum,
271251881Speter                                                       pool, pool));
272251881Speter}
273251881Speter
274251881Speter
275251881Speterstatic svn_error_t *
276251881Spetercancel_callback(void *baton)
277251881Speter{
278251881Speter  callback_baton_t *b = baton;
279251881Speter  return svn_error_trace((b->ctx->cancel_func)(b->ctx->cancel_baton));
280251881Speter}
281251881Speter
282251881Speter
283251881Speterstatic svn_error_t *
284251881Speterget_client_string(void *baton,
285251881Speter                  const char **name,
286251881Speter                  apr_pool_t *pool)
287251881Speter{
288251881Speter  callback_baton_t *b = baton;
289251881Speter  *name = apr_pstrdup(pool, b->ctx->client_name);
290251881Speter  return SVN_NO_ERROR;
291251881Speter}
292251881Speter
293299742Sdim/* Implements svn_ra_progress_notify_func_t. Accumulates progress information
294299742Sdim * for different RA sessions and reports total progress to caller. */
295299742Sdimstatic void
296299742Sdimprogress_func(apr_off_t progress,
297299742Sdim              apr_off_t total,
298299742Sdim              void *baton,
299299742Sdim              apr_pool_t *pool)
300299742Sdim{
301299742Sdim  callback_baton_t *b = baton;
302299742Sdim  svn_client_ctx_t *public_ctx = b->ctx;
303299742Sdim  svn_client__private_ctx_t *private_ctx =
304299742Sdim    svn_client__get_private_ctx(public_ctx);
305251881Speter
306299742Sdim  private_ctx->total_progress += (progress - b->last_progress);
307299742Sdim  b->last_progress = progress;
308299742Sdim
309299742Sdim  if (public_ctx->progress_func)
310299742Sdim    {
311299742Sdim      /* All RA implementations currently provide -1 for total. So it doesn't
312299742Sdim         make sense to develop some complex logic to combine total across all
313299742Sdim         RA sessions. */
314299742Sdim      public_ctx->progress_func(private_ctx->total_progress, -1,
315299742Sdim                                public_ctx->progress_baton, pool);
316299742Sdim    }
317299742Sdim}
318299742Sdim
319251881Speter#define SVN_CLIENT__MAX_REDIRECT_ATTEMPTS 3 /* ### TODO:  Make configurable. */
320251881Speter
321251881Spetersvn_error_t *
322251881Spetersvn_client__open_ra_session_internal(svn_ra_session_t **ra_session,
323251881Speter                                     const char **corrected_url,
324251881Speter                                     const char *base_url,
325251881Speter                                     const char *base_dir_abspath,
326251881Speter                                     const apr_array_header_t *commit_items,
327251881Speter                                     svn_boolean_t write_dav_props,
328251881Speter                                     svn_boolean_t read_dav_props,
329251881Speter                                     svn_client_ctx_t *ctx,
330251881Speter                                     apr_pool_t *result_pool,
331251881Speter                                     apr_pool_t *scratch_pool)
332251881Speter{
333251881Speter  svn_ra_callbacks2_t *cbtable;
334251881Speter  callback_baton_t *cb = apr_pcalloc(result_pool, sizeof(*cb));
335251881Speter  const char *uuid = NULL;
336251881Speter
337251881Speter  SVN_ERR_ASSERT(!write_dav_props || read_dav_props);
338251881Speter  SVN_ERR_ASSERT(!read_dav_props || base_dir_abspath != NULL);
339251881Speter  SVN_ERR_ASSERT(base_dir_abspath == NULL
340251881Speter                        || svn_dirent_is_absolute(base_dir_abspath));
341251881Speter
342251881Speter  SVN_ERR(svn_ra_create_callbacks(&cbtable, result_pool));
343251881Speter  cbtable->open_tmp_file = open_tmp_file;
344251881Speter  cbtable->get_wc_prop = read_dav_props ? get_wc_prop : NULL;
345251881Speter  cbtable->set_wc_prop = (write_dav_props && read_dav_props)
346251881Speter                          ? set_wc_prop : NULL;
347251881Speter  cbtable->push_wc_prop = commit_items ? push_wc_prop : NULL;
348251881Speter  cbtable->invalidate_wc_props = (write_dav_props && read_dav_props)
349251881Speter                                  ? invalidate_wc_props : NULL;
350251881Speter  cbtable->auth_baton = ctx->auth_baton; /* new-style */
351299742Sdim  cbtable->progress_func = progress_func;
352299742Sdim  cbtable->progress_baton = cb;
353251881Speter  cbtable->cancel_func = ctx->cancel_func ? cancel_callback : NULL;
354251881Speter  cbtable->get_client_string = get_client_string;
355251881Speter  if (base_dir_abspath)
356251881Speter    cbtable->get_wc_contents = get_wc_contents;
357299742Sdim  cbtable->check_tunnel_func = ctx->check_tunnel_func;
358299742Sdim  cbtable->open_tunnel_func = ctx->open_tunnel_func;
359299742Sdim  cbtable->tunnel_baton = ctx->tunnel_baton;
360251881Speter
361251881Speter  cb->commit_items = commit_items;
362251881Speter  cb->ctx = ctx;
363251881Speter
364251881Speter  if (base_dir_abspath && (read_dav_props || write_dav_props))
365251881Speter    {
366251881Speter      svn_error_t *err = svn_wc__node_get_repos_info(NULL, NULL, NULL, &uuid,
367251881Speter                                                     ctx->wc_ctx,
368251881Speter                                                     base_dir_abspath,
369251881Speter                                                     result_pool,
370251881Speter                                                     scratch_pool);
371251881Speter
372251881Speter      if (err && (err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY
373251881Speter                  || err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND
374251881Speter                  || err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED))
375251881Speter        {
376251881Speter          svn_error_clear(err);
377251881Speter          uuid = NULL;
378251881Speter        }
379251881Speter      else
380251881Speter        {
381251881Speter          SVN_ERR(err);
382251881Speter          cb->base_dir_isversioned = TRUE;
383251881Speter        }
384251881Speter      cb->base_dir_abspath = apr_pstrdup(result_pool, base_dir_abspath);
385251881Speter    }
386251881Speter
387251881Speter  if (base_dir_abspath)
388251881Speter    {
389251881Speter      svn_error_t *err = svn_wc__get_wcroot(&cb->wcroot_abspath,
390251881Speter                                            ctx->wc_ctx, base_dir_abspath,
391251881Speter                                            result_pool, scratch_pool);
392251881Speter
393251881Speter      if (err)
394251881Speter        {
395251881Speter          if (err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY
396251881Speter              && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND
397251881Speter              && err->apr_err != SVN_ERR_WC_UPGRADE_REQUIRED)
398251881Speter            return svn_error_trace(err);
399251881Speter
400251881Speter          svn_error_clear(err);
401251881Speter          cb->wcroot_abspath = NULL;
402251881Speter        }
403251881Speter    }
404251881Speter
405251881Speter  /* If the caller allows for auto-following redirections, and the
406251881Speter     RA->open() call above reveals a CORRECTED_URL, try the new URL.
407251881Speter     We'll do this in a loop up to some maximum number follow-and-retry
408251881Speter     attempts.  */
409251881Speter  if (corrected_url)
410251881Speter    {
411251881Speter      apr_hash_t *attempted = apr_hash_make(scratch_pool);
412251881Speter      int attempts_left = SVN_CLIENT__MAX_REDIRECT_ATTEMPTS;
413251881Speter
414251881Speter      *corrected_url = NULL;
415251881Speter      while (attempts_left--)
416251881Speter        {
417251881Speter          const char *corrected = NULL;
418251881Speter
419251881Speter          /* Try to open the RA session.  If this is our last attempt,
420251881Speter             don't accept corrected URLs from the RA provider. */
421251881Speter          SVN_ERR(svn_ra_open4(ra_session,
422251881Speter                               attempts_left == 0 ? NULL : &corrected,
423251881Speter                               base_url, uuid, cbtable, cb, ctx->config,
424251881Speter                               result_pool));
425251881Speter
426251881Speter          /* No error and no corrected URL?  We're done here. */
427251881Speter          if (! corrected)
428251881Speter            break;
429251881Speter
430251881Speter          /* Notify the user that a redirect is being followed. */
431251881Speter          if (ctx->notify_func2 != NULL)
432251881Speter            {
433251881Speter              svn_wc_notify_t *notify =
434251881Speter                svn_wc_create_notify_url(corrected,
435251881Speter                                         svn_wc_notify_url_redirect,
436251881Speter                                         scratch_pool);
437299742Sdim              ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool);
438251881Speter            }
439251881Speter
440251881Speter          /* Our caller will want to know what our final corrected URL was. */
441251881Speter          *corrected_url = corrected;
442251881Speter
443251881Speter          /* Make sure we've not attempted this URL before. */
444251881Speter          if (svn_hash_gets(attempted, corrected))
445251881Speter            return svn_error_createf(SVN_ERR_CLIENT_CYCLE_DETECTED, NULL,
446251881Speter                                     _("Redirect cycle detected for URL '%s'"),
447251881Speter                                     corrected);
448251881Speter
449251881Speter          /* Remember this CORRECTED_URL so we don't wind up in a loop. */
450251881Speter          svn_hash_sets(attempted, corrected, (void *)1);
451251881Speter          base_url = corrected;
452251881Speter        }
453251881Speter    }
454251881Speter  else
455251881Speter    {
456251881Speter      SVN_ERR(svn_ra_open4(ra_session, NULL, base_url,
457251881Speter                           uuid, cbtable, cb, ctx->config, result_pool));
458251881Speter    }
459251881Speter
460251881Speter  return SVN_NO_ERROR;
461251881Speter}
462251881Speter#undef SVN_CLIENT__MAX_REDIRECT_ATTEMPTS
463251881Speter
464251881Speter
465251881Spetersvn_error_t *
466251881Spetersvn_client_open_ra_session2(svn_ra_session_t **session,
467251881Speter                            const char *url,
468251881Speter                            const char *wri_abspath,
469251881Speter                            svn_client_ctx_t *ctx,
470251881Speter                            apr_pool_t *result_pool,
471251881Speter                            apr_pool_t *scratch_pool)
472251881Speter{
473251881Speter  return svn_error_trace(
474251881Speter             svn_client__open_ra_session_internal(session, NULL, url,
475251881Speter                                                  wri_abspath, NULL,
476251881Speter                                                  FALSE, FALSE,
477251881Speter                                                  ctx, result_pool,
478251881Speter                                                  scratch_pool));
479251881Speter}
480251881Speter
481251881Spetersvn_error_t *
482251881Spetersvn_client__resolve_rev_and_url(svn_client__pathrev_t **resolved_loc_p,
483251881Speter                                svn_ra_session_t *ra_session,
484251881Speter                                const char *path_or_url,
485251881Speter                                const svn_opt_revision_t *peg_revision,
486251881Speter                                const svn_opt_revision_t *revision,
487251881Speter                                svn_client_ctx_t *ctx,
488251881Speter                                apr_pool_t *pool)
489251881Speter{
490251881Speter  svn_opt_revision_t peg_rev = *peg_revision;
491251881Speter  svn_opt_revision_t start_rev = *revision;
492251881Speter  const char *url;
493251881Speter  svn_revnum_t rev;
494251881Speter
495251881Speter  /* Default revisions: peg -> working or head; operative -> peg. */
496251881Speter  SVN_ERR(svn_opt_resolve_revisions(&peg_rev, &start_rev,
497251881Speter                                    svn_path_is_url(path_or_url),
498251881Speter                                    TRUE /* notice_local_mods */,
499251881Speter                                    pool));
500251881Speter
501251881Speter  /* Run the history function to get the object's (possibly
502251881Speter     different) url in REVISION. */
503251881Speter  SVN_ERR(svn_client__repos_locations(&url, &rev, NULL, NULL,
504251881Speter                                      ra_session, path_or_url, &peg_rev,
505251881Speter                                      &start_rev, NULL, ctx, pool));
506251881Speter
507251881Speter  SVN_ERR(svn_client__pathrev_create_with_session(resolved_loc_p,
508251881Speter                                                  ra_session, rev, url, pool));
509251881Speter  return SVN_NO_ERROR;
510251881Speter}
511251881Speter
512251881Spetersvn_error_t *
513251881Spetersvn_client__ra_session_from_path2(svn_ra_session_t **ra_session_p,
514251881Speter                                  svn_client__pathrev_t **resolved_loc_p,
515251881Speter                                  const char *path_or_url,
516251881Speter                                  const char *base_dir_abspath,
517251881Speter                                  const svn_opt_revision_t *peg_revision,
518251881Speter                                  const svn_opt_revision_t *revision,
519251881Speter                                  svn_client_ctx_t *ctx,
520251881Speter                                  apr_pool_t *pool)
521251881Speter{
522251881Speter  svn_ra_session_t *ra_session;
523251881Speter  const char *initial_url;
524251881Speter  const char *corrected_url;
525251881Speter  svn_client__pathrev_t *resolved_loc;
526251881Speter  const char *wri_abspath;
527251881Speter
528251881Speter  SVN_ERR(svn_client_url_from_path2(&initial_url, path_or_url, ctx, pool,
529251881Speter                                    pool));
530251881Speter  if (! initial_url)
531251881Speter    return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL,
532251881Speter                             _("'%s' has no URL"), path_or_url);
533251881Speter
534251881Speter  if (base_dir_abspath)
535251881Speter    wri_abspath = base_dir_abspath;
536251881Speter  else if (!svn_path_is_url(path_or_url))
537251881Speter    SVN_ERR(svn_dirent_get_absolute(&wri_abspath, path_or_url, pool));
538251881Speter  else
539251881Speter    wri_abspath = NULL;
540251881Speter
541251881Speter  SVN_ERR(svn_client__open_ra_session_internal(&ra_session, &corrected_url,
542251881Speter                                               initial_url,
543251881Speter                                               wri_abspath,
544251881Speter                                               NULL /* commit_items */,
545251881Speter                                               base_dir_abspath != NULL,
546251881Speter                                               base_dir_abspath != NULL,
547251881Speter                                               ctx, pool, pool));
548251881Speter
549251881Speter  /* If we got a CORRECTED_URL, we'll want to refer to that as the
550251881Speter     URL-ized form of PATH_OR_URL from now on. */
551251881Speter  if (corrected_url && svn_path_is_url(path_or_url))
552251881Speter    path_or_url = corrected_url;
553251881Speter
554251881Speter  SVN_ERR(svn_client__resolve_rev_and_url(&resolved_loc, ra_session,
555251881Speter                                          path_or_url, peg_revision, revision,
556251881Speter                                          ctx, pool));
557251881Speter
558251881Speter  /* Make the session point to the real URL. */
559251881Speter  SVN_ERR(svn_ra_reparent(ra_session, resolved_loc->url, pool));
560251881Speter
561251881Speter  *ra_session_p = ra_session;
562251881Speter  if (resolved_loc_p)
563251881Speter    *resolved_loc_p = resolved_loc;
564251881Speter
565251881Speter  return SVN_NO_ERROR;
566251881Speter}
567251881Speter
568251881Speter
569251881Spetersvn_error_t *
570251881Spetersvn_client__ensure_ra_session_url(const char **old_session_url,
571251881Speter                                  svn_ra_session_t *ra_session,
572251881Speter                                  const char *session_url,
573251881Speter                                  apr_pool_t *pool)
574251881Speter{
575251881Speter  SVN_ERR(svn_ra_get_session_url(ra_session, old_session_url, pool));
576251881Speter  if (! session_url)
577251881Speter    SVN_ERR(svn_ra_get_repos_root2(ra_session, &session_url, pool));
578251881Speter  if (strcmp(*old_session_url, session_url) != 0)
579251881Speter    SVN_ERR(svn_ra_reparent(ra_session, session_url, pool));
580251881Speter  return SVN_NO_ERROR;
581251881Speter}
582251881Speter
583251881Speter
584251881Speter
585251881Speter/*** Repository Locations ***/
586251881Speter
587251881Speterstruct gls_receiver_baton_t
588251881Speter{
589251881Speter  apr_array_header_t *segments;
590251881Speter  svn_client_ctx_t *ctx;
591251881Speter  apr_pool_t *pool;
592251881Speter};
593251881Speter
594251881Speterstatic svn_error_t *
595251881Spetergls_receiver(svn_location_segment_t *segment,
596251881Speter             void *baton,
597251881Speter             apr_pool_t *pool)
598251881Speter{
599251881Speter  struct gls_receiver_baton_t *b = baton;
600251881Speter  APR_ARRAY_PUSH(b->segments, svn_location_segment_t *) =
601251881Speter    svn_location_segment_dup(segment, b->pool);
602251881Speter  if (b->ctx->cancel_func)
603251881Speter    SVN_ERR((b->ctx->cancel_func)(b->ctx->cancel_baton));
604251881Speter  return SVN_NO_ERROR;
605251881Speter}
606251881Speter
607251881Speter/* A qsort-compatible function which sorts svn_location_segment_t's
608251881Speter   based on their revision range covering, resulting in ascending
609251881Speter   (oldest-to-youngest) ordering. */
610251881Speterstatic int
611251881Spetercompare_segments(const void *a, const void *b)
612251881Speter{
613251881Speter  const svn_location_segment_t *a_seg
614251881Speter    = *((const svn_location_segment_t * const *) a);
615251881Speter  const svn_location_segment_t *b_seg
616251881Speter    = *((const svn_location_segment_t * const *) b);
617251881Speter  if (a_seg->range_start == b_seg->range_start)
618251881Speter    return 0;
619251881Speter  return (a_seg->range_start < b_seg->range_start) ? -1 : 1;
620251881Speter}
621251881Speter
622251881Spetersvn_error_t *
623251881Spetersvn_client__repos_location_segments(apr_array_header_t **segments,
624251881Speter                                    svn_ra_session_t *ra_session,
625251881Speter                                    const char *url,
626251881Speter                                    svn_revnum_t peg_revision,
627251881Speter                                    svn_revnum_t start_revision,
628251881Speter                                    svn_revnum_t end_revision,
629251881Speter                                    svn_client_ctx_t *ctx,
630251881Speter                                    apr_pool_t *pool)
631251881Speter{
632251881Speter  struct gls_receiver_baton_t gls_receiver_baton;
633251881Speter  const char *old_session_url;
634251881Speter  svn_error_t *err;
635251881Speter
636251881Speter  *segments = apr_array_make(pool, 8, sizeof(svn_location_segment_t *));
637251881Speter  gls_receiver_baton.segments = *segments;
638251881Speter  gls_receiver_baton.ctx = ctx;
639251881Speter  gls_receiver_baton.pool = pool;
640251881Speter  SVN_ERR(svn_client__ensure_ra_session_url(&old_session_url, ra_session,
641251881Speter                                            url, pool));
642251881Speter  err = svn_ra_get_location_segments(ra_session, "", peg_revision,
643251881Speter                                     start_revision, end_revision,
644251881Speter                                     gls_receiver, &gls_receiver_baton,
645251881Speter                                     pool);
646251881Speter  SVN_ERR(svn_error_compose_create(
647251881Speter            err, svn_ra_reparent(ra_session, old_session_url, pool)));
648299742Sdim  svn_sort__array(*segments, compare_segments);
649251881Speter  return SVN_NO_ERROR;
650251881Speter}
651251881Speter
652251881Speter/* Set *START_URL and *END_URL to the URLs that the object URL@PEG_REVNUM
653251881Speter * had in revisions START_REVNUM and END_REVNUM.  Return an error if the
654251881Speter * node cannot be traced back to one of the requested revisions.
655251881Speter *
656251881Speter * START_URL and/or END_URL may be NULL if not wanted.  START_REVNUM and
657251881Speter * END_REVNUM must be valid revision numbers except that END_REVNUM may
658251881Speter * be SVN_INVALID_REVNUM if END_URL is NULL.
659251881Speter *
660299742Sdim * YOUNGEST_REV is the already retrieved youngest revision of the ra session,
661299742Sdim * but can be SVN_INVALID_REVNUM if the value is not already retrieved.
662299742Sdim *
663251881Speter * RA_SESSION is an open RA session parented at URL.
664251881Speter */
665251881Speterstatic svn_error_t *
666251881Speterrepos_locations(const char **start_url,
667251881Speter                const char **end_url,
668251881Speter                svn_ra_session_t *ra_session,
669251881Speter                const char *url,
670251881Speter                svn_revnum_t peg_revnum,
671251881Speter                svn_revnum_t start_revnum,
672251881Speter                svn_revnum_t end_revnum,
673299742Sdim                svn_revnum_t youngest_rev,
674251881Speter                apr_pool_t *result_pool,
675251881Speter                apr_pool_t *scratch_pool)
676251881Speter{
677251881Speter  const char *repos_url, *start_path, *end_path;
678251881Speter  apr_array_header_t *revs;
679251881Speter  apr_hash_t *rev_locs;
680251881Speter
681299742Sdim  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(peg_revnum));
682299742Sdim  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(start_revnum));
683299742Sdim  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(end_revnum) || end_url == NULL);
684251881Speter
685251881Speter  /* Avoid a network request in the common easy case. */
686251881Speter  if (start_revnum == peg_revnum
687251881Speter      && (end_revnum == peg_revnum || end_revnum == SVN_INVALID_REVNUM))
688251881Speter    {
689251881Speter      if (start_url)
690251881Speter        *start_url = apr_pstrdup(result_pool, url);
691251881Speter      if (end_url)
692251881Speter        *end_url = apr_pstrdup(result_pool, url);
693251881Speter      return SVN_NO_ERROR;
694251881Speter    }
695251881Speter
696251881Speter  SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_url, scratch_pool));
697251881Speter
698299742Sdim  /* Handle another common case: The repository root can't move */
699299742Sdim  if (! strcmp(repos_url, url))
700299742Sdim    {
701299742Sdim      if (! SVN_IS_VALID_REVNUM(youngest_rev))
702299742Sdim        SVN_ERR(svn_ra_get_latest_revnum(ra_session, &youngest_rev,
703299742Sdim                                         scratch_pool));
704299742Sdim
705299742Sdim      if (start_revnum > youngest_rev
706299742Sdim          || (SVN_IS_VALID_REVNUM(end_revnum) && (end_revnum > youngest_rev)))
707299742Sdim        return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
708299742Sdim                                 _("No such revision %ld"),
709299742Sdim                                 (start_revnum > youngest_rev)
710299742Sdim                                        ? start_revnum : end_revnum);
711299742Sdim
712299742Sdim      if (start_url)
713299742Sdim        *start_url = apr_pstrdup(result_pool, repos_url);
714299742Sdim      if (end_url)
715299742Sdim        *end_url = apr_pstrdup(result_pool, repos_url);
716299742Sdim      return SVN_NO_ERROR;
717299742Sdim    }
718299742Sdim
719251881Speter  revs = apr_array_make(scratch_pool, 2, sizeof(svn_revnum_t));
720251881Speter  APR_ARRAY_PUSH(revs, svn_revnum_t) = start_revnum;
721251881Speter  if (end_revnum != start_revnum && end_revnum != SVN_INVALID_REVNUM)
722251881Speter    APR_ARRAY_PUSH(revs, svn_revnum_t) = end_revnum;
723251881Speter
724251881Speter  SVN_ERR(svn_ra_get_locations(ra_session, &rev_locs, "", peg_revnum,
725251881Speter                               revs, scratch_pool));
726251881Speter
727251881Speter  /* We'd better have all the paths we were looking for! */
728251881Speter  if (start_url)
729251881Speter    {
730251881Speter      start_path = apr_hash_get(rev_locs, &start_revnum, sizeof(svn_revnum_t));
731251881Speter      if (! start_path)
732251881Speter        return svn_error_createf
733251881Speter          (SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
734251881Speter           _("Unable to find repository location for '%s' in revision %ld"),
735251881Speter           url, start_revnum);
736251881Speter      *start_url = svn_path_url_add_component2(repos_url, start_path + 1,
737251881Speter                                               result_pool);
738251881Speter    }
739251881Speter
740251881Speter  if (end_url)
741251881Speter    {
742251881Speter      end_path = apr_hash_get(rev_locs, &end_revnum, sizeof(svn_revnum_t));
743251881Speter      if (! end_path)
744251881Speter        return svn_error_createf
745251881Speter          (SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
746251881Speter           _("The location for '%s' for revision %ld does not exist in the "
747251881Speter             "repository or refers to an unrelated object"),
748251881Speter           url, end_revnum);
749251881Speter
750251881Speter      *end_url = svn_path_url_add_component2(repos_url, end_path + 1,
751251881Speter                                             result_pool);
752251881Speter    }
753251881Speter
754251881Speter  return SVN_NO_ERROR;
755251881Speter}
756251881Speter
757251881Spetersvn_error_t *
758251881Spetersvn_client__repos_location(svn_client__pathrev_t **op_loc_p,
759251881Speter                           svn_ra_session_t *ra_session,
760251881Speter                           const svn_client__pathrev_t *peg_loc,
761251881Speter                           svn_revnum_t op_revnum,
762251881Speter                           svn_client_ctx_t *ctx,
763251881Speter                           apr_pool_t *result_pool,
764251881Speter                           apr_pool_t *scratch_pool)
765251881Speter{
766251881Speter  const char *old_session_url;
767251881Speter  const char *op_url;
768251881Speter  svn_error_t *err;
769251881Speter
770251881Speter  SVN_ERR(svn_client__ensure_ra_session_url(&old_session_url, ra_session,
771251881Speter                                            peg_loc->url, scratch_pool));
772251881Speter  err = repos_locations(&op_url, NULL, ra_session,
773251881Speter                        peg_loc->url, peg_loc->rev,
774299742Sdim                        op_revnum, SVN_INVALID_REVNUM, SVN_INVALID_REVNUM,
775251881Speter                        result_pool, scratch_pool);
776251881Speter  SVN_ERR(svn_error_compose_create(
777251881Speter            err, svn_ra_reparent(ra_session, old_session_url, scratch_pool)));
778251881Speter
779251881Speter  *op_loc_p = svn_client__pathrev_create(peg_loc->repos_root_url,
780251881Speter                                         peg_loc->repos_uuid,
781251881Speter                                         op_revnum, op_url, result_pool);
782251881Speter  return SVN_NO_ERROR;
783251881Speter}
784251881Speter
785251881Spetersvn_error_t *
786251881Spetersvn_client__repos_locations(const char **start_url,
787251881Speter                            svn_revnum_t *start_revision,
788251881Speter                            const char **end_url,
789251881Speter                            svn_revnum_t *end_revision,
790251881Speter                            svn_ra_session_t *ra_session,
791251881Speter                            const char *path,
792251881Speter                            const svn_opt_revision_t *revision,
793251881Speter                            const svn_opt_revision_t *start,
794251881Speter                            const svn_opt_revision_t *end,
795251881Speter                            svn_client_ctx_t *ctx,
796251881Speter                            apr_pool_t *pool)
797251881Speter{
798251881Speter  const char *url;
799251881Speter  const char *local_abspath_or_url;
800251881Speter  svn_revnum_t peg_revnum = SVN_INVALID_REVNUM;
801251881Speter  svn_revnum_t start_revnum, end_revnum;
802251881Speter  svn_revnum_t youngest_rev = SVN_INVALID_REVNUM;
803251881Speter  apr_pool_t *subpool = svn_pool_create(pool);
804251881Speter
805251881Speter  /* Ensure that we are given some real revision data to work with.
806251881Speter     (It's okay if the END is unspecified -- in that case, we'll just
807251881Speter     set it to the same thing as START.)  */
808251881Speter  if (revision->kind == svn_opt_revision_unspecified
809251881Speter      || start->kind == svn_opt_revision_unspecified)
810251881Speter    return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL);
811251881Speter
812251881Speter  if (end == NULL)
813251881Speter    {
814251881Speter      static const svn_opt_revision_t unspecified_rev
815251881Speter        = { svn_opt_revision_unspecified, { 0 } };
816251881Speter
817251881Speter      end = &unspecified_rev;
818251881Speter    }
819251881Speter
820251881Speter  /* Determine LOCAL_ABSPATH_OR_URL, URL, and possibly PEG_REVNUM.
821251881Speter     If we are looking at the working version of a WC path that is scheduled
822251881Speter     as a copy, then we need to use the copy-from URL and peg revision. */
823251881Speter  if (! svn_path_is_url(path))
824251881Speter    {
825251881Speter      SVN_ERR(svn_dirent_get_absolute(&local_abspath_or_url, path, subpool));
826251881Speter
827251881Speter      if (revision->kind == svn_opt_revision_working)
828251881Speter        {
829251881Speter          const char *repos_root_url;
830251881Speter          const char *repos_relpath;
831251881Speter          svn_boolean_t is_copy;
832251881Speter
833251881Speter          SVN_ERR(svn_wc__node_get_origin(&is_copy, &peg_revnum, &repos_relpath,
834299742Sdim                                          &repos_root_url, NULL, NULL, NULL,
835251881Speter                                          ctx->wc_ctx, local_abspath_or_url,
836251881Speter                                          FALSE, subpool, subpool));
837251881Speter
838251881Speter          if (repos_relpath)
839251881Speter            url = svn_path_url_add_component2(repos_root_url, repos_relpath,
840251881Speter                                              pool);
841251881Speter          else
842251881Speter            url = NULL;
843251881Speter
844251881Speter          if (url && is_copy && ra_session)
845251881Speter            {
846251881Speter              const char *session_url;
847251881Speter              SVN_ERR(svn_ra_get_session_url(ra_session, &session_url,
848251881Speter                                             subpool));
849251881Speter
850251881Speter              if (strcmp(session_url, url) != 0)
851251881Speter                {
852251881Speter                  /* We can't use the caller provided RA session now :( */
853251881Speter                  ra_session = NULL;
854251881Speter                }
855251881Speter            }
856251881Speter        }
857251881Speter      else
858251881Speter        url = NULL;
859251881Speter
860251881Speter      if (! url)
861251881Speter        SVN_ERR(svn_wc__node_get_url(&url, ctx->wc_ctx,
862251881Speter                                     local_abspath_or_url, pool, subpool));
863251881Speter
864251881Speter      if (!url)
865251881Speter        {
866251881Speter          return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL,
867251881Speter                                   _("'%s' has no URL"),
868251881Speter                                   svn_dirent_local_style(path, pool));
869251881Speter        }
870251881Speter    }
871251881Speter  else
872251881Speter    {
873251881Speter      local_abspath_or_url = path;
874251881Speter      url = path;
875251881Speter    }
876251881Speter
877251881Speter  /* ### We should be smarter here.  If the callers just asks for BASE and
878251881Speter     WORKING revisions, we should already have the correct URLs, so we
879251881Speter     don't need to do anything more here in that case. */
880251881Speter
881251881Speter  /* Open a RA session to this URL if we don't have one already. */
882251881Speter  if (! ra_session)
883251881Speter    SVN_ERR(svn_client_open_ra_session2(&ra_session, url, NULL,
884251881Speter                                        ctx, subpool, subpool));
885251881Speter
886251881Speter  /* Resolve the opt_revision_ts. */
887251881Speter  if (peg_revnum == SVN_INVALID_REVNUM)
888251881Speter    SVN_ERR(svn_client__get_revision_number(&peg_revnum, &youngest_rev,
889251881Speter                                            ctx->wc_ctx, local_abspath_or_url,
890251881Speter                                            ra_session, revision, pool));
891251881Speter
892251881Speter  SVN_ERR(svn_client__get_revision_number(&start_revnum, &youngest_rev,
893251881Speter                                          ctx->wc_ctx, local_abspath_or_url,
894251881Speter                                          ra_session, start, pool));
895251881Speter  if (end->kind == svn_opt_revision_unspecified)
896251881Speter    end_revnum = start_revnum;
897251881Speter  else
898251881Speter    SVN_ERR(svn_client__get_revision_number(&end_revnum, &youngest_rev,
899251881Speter                                            ctx->wc_ctx, local_abspath_or_url,
900251881Speter                                            ra_session, end, pool));
901251881Speter
902251881Speter  /* Set the output revision variables. */
903251881Speter  if (start_revision)
904251881Speter    {
905251881Speter      *start_revision = start_revnum;
906251881Speter    }
907251881Speter  if (end_revision && end->kind != svn_opt_revision_unspecified)
908251881Speter    {
909251881Speter      *end_revision = end_revnum;
910251881Speter    }
911251881Speter
912251881Speter  SVN_ERR(repos_locations(start_url, end_url,
913251881Speter                          ra_session, url, peg_revnum,
914299742Sdim                          start_revnum, end_revnum, youngest_rev,
915251881Speter                          pool, subpool));
916251881Speter  svn_pool_destroy(subpool);
917251881Speter  return SVN_NO_ERROR;
918251881Speter}
919251881Speter
920251881Spetersvn_error_t *
921253734Spetersvn_client__calc_youngest_common_ancestor(svn_client__pathrev_t **ancestor_p,
922253734Speter                                          const svn_client__pathrev_t *loc1,
923253734Speter                                          apr_hash_t *history1,
924253734Speter                                          svn_boolean_t has_rev_zero_history1,
925253734Speter                                          const svn_client__pathrev_t *loc2,
926253734Speter                                          apr_hash_t *history2,
927253734Speter                                          svn_boolean_t has_rev_zero_history2,
928253734Speter                                          apr_pool_t *result_pool,
929253734Speter                                          apr_pool_t *scratch_pool)
930251881Speter{
931251881Speter  apr_hash_index_t *hi;
932251881Speter  svn_revnum_t yc_revision = SVN_INVALID_REVNUM;
933251881Speter  const char *yc_relpath = NULL;
934251881Speter
935251881Speter  if (strcmp(loc1->repos_root_url, loc2->repos_root_url) != 0)
936251881Speter    {
937251881Speter      *ancestor_p = NULL;
938251881Speter      return SVN_NO_ERROR;
939251881Speter    }
940251881Speter
941251881Speter  /* Loop through the first location's history, check for overlapping
942251881Speter     paths and ranges in the second location's history, and
943251881Speter     remembering the youngest matching location. */
944251881Speter  for (hi = apr_hash_first(scratch_pool, history1); hi; hi = apr_hash_next(hi))
945251881Speter    {
946299742Sdim      const char *path = apr_hash_this_key(hi);
947299742Sdim      apr_ssize_t path_len = apr_hash_this_key_len(hi);
948299742Sdim      svn_rangelist_t *ranges1 = apr_hash_this_val(hi);
949251881Speter      svn_rangelist_t *ranges2, *common;
950251881Speter
951251881Speter      ranges2 = apr_hash_get(history2, path, path_len);
952251881Speter      if (ranges2)
953251881Speter        {
954251881Speter          /* We have a path match.  Now, did our two histories share
955251881Speter             any revisions at that path? */
956251881Speter          SVN_ERR(svn_rangelist_intersect(&common, ranges1, ranges2,
957251881Speter                                          TRUE, scratch_pool));
958251881Speter          if (common->nelts)
959251881Speter            {
960251881Speter              svn_merge_range_t *yc_range =
961251881Speter                APR_ARRAY_IDX(common, common->nelts - 1, svn_merge_range_t *);
962251881Speter              if ((! SVN_IS_VALID_REVNUM(yc_revision))
963251881Speter                  || (yc_range->end > yc_revision))
964251881Speter                {
965251881Speter                  yc_revision = yc_range->end;
966251881Speter                  yc_relpath = path + 1;
967251881Speter                }
968251881Speter            }
969251881Speter        }
970251881Speter    }
971251881Speter
972251881Speter  /* It's possible that PATH_OR_URL1 and PATH_OR_URL2's only common
973251881Speter     history is revision 0. */
974251881Speter  if (!yc_relpath && has_rev_zero_history1 && has_rev_zero_history2)
975251881Speter    {
976251881Speter      yc_relpath = "";
977251881Speter      yc_revision = 0;
978251881Speter    }
979251881Speter
980251881Speter  if (yc_relpath)
981251881Speter    {
982251881Speter      *ancestor_p = svn_client__pathrev_create_with_relpath(
983251881Speter                      loc1->repos_root_url, loc1->repos_uuid,
984251881Speter                      yc_revision, yc_relpath, result_pool);
985251881Speter    }
986251881Speter  else
987251881Speter    {
988251881Speter      *ancestor_p = NULL;
989251881Speter    }
990251881Speter  return SVN_NO_ERROR;
991251881Speter}
992251881Speter
993251881Spetersvn_error_t *
994253734Spetersvn_client__get_youngest_common_ancestor(svn_client__pathrev_t **ancestor_p,
995253734Speter                                         const svn_client__pathrev_t *loc1,
996253734Speter                                         const svn_client__pathrev_t *loc2,
997253734Speter                                         svn_ra_session_t *session,
998253734Speter                                         svn_client_ctx_t *ctx,
999253734Speter                                         apr_pool_t *result_pool,
1000253734Speter                                         apr_pool_t *scratch_pool)
1001253734Speter{
1002253734Speter  apr_pool_t *sesspool = NULL;
1003253734Speter  apr_hash_t *history1, *history2;
1004253734Speter  svn_boolean_t has_rev_zero_history1;
1005253734Speter  svn_boolean_t has_rev_zero_history2;
1006253734Speter
1007253734Speter  if (strcmp(loc1->repos_root_url, loc2->repos_root_url) != 0)
1008253734Speter    {
1009253734Speter      *ancestor_p = NULL;
1010253734Speter      return SVN_NO_ERROR;
1011253734Speter    }
1012253734Speter
1013253734Speter  /* Open an RA session for the two locations. */
1014253734Speter  if (session == NULL)
1015253734Speter    {
1016253734Speter      sesspool = svn_pool_create(scratch_pool);
1017253734Speter      SVN_ERR(svn_client_open_ra_session2(&session, loc1->url, NULL, ctx,
1018253734Speter                                          sesspool, sesspool));
1019253734Speter    }
1020253734Speter
1021253734Speter  /* We're going to cheat and use history-as-mergeinfo because it
1022253734Speter     saves us a bunch of annoying custom data comparisons and such. */
1023253734Speter  SVN_ERR(svn_client__get_history_as_mergeinfo(&history1,
1024253734Speter                                               &has_rev_zero_history1,
1025253734Speter                                               loc1,
1026253734Speter                                               SVN_INVALID_REVNUM,
1027253734Speter                                               SVN_INVALID_REVNUM,
1028253734Speter                                               session, ctx, scratch_pool));
1029253734Speter  SVN_ERR(svn_client__get_history_as_mergeinfo(&history2,
1030253734Speter                                               &has_rev_zero_history2,
1031253734Speter                                               loc2,
1032253734Speter                                               SVN_INVALID_REVNUM,
1033253734Speter                                               SVN_INVALID_REVNUM,
1034253734Speter                                               session, ctx, scratch_pool));
1035253734Speter  /* Close the ra session if we opened one. */
1036253734Speter  if (sesspool)
1037253734Speter    svn_pool_destroy(sesspool);
1038253734Speter
1039253734Speter  SVN_ERR(svn_client__calc_youngest_common_ancestor(ancestor_p,
1040253734Speter                                                    loc1, history1,
1041253734Speter                                                    has_rev_zero_history1,
1042253734Speter                                                    loc2, history2,
1043253734Speter                                                    has_rev_zero_history2,
1044253734Speter                                                    result_pool,
1045253734Speter                                                    scratch_pool));
1046253734Speter
1047253734Speter  return SVN_NO_ERROR;
1048253734Speter}
1049253734Speter
1050251881Speterstruct ra_ev2_baton {
1051251881Speter  /* The working copy context, from the client context.  */
1052251881Speter  svn_wc_context_t *wc_ctx;
1053251881Speter
1054251881Speter  /* For a given REPOS_RELPATH, provide a LOCAL_ABSPATH that represents
1055251881Speter     that repository node.  */
1056251881Speter  apr_hash_t *relpath_map;
1057251881Speter};
1058251881Speter
1059251881Speter
1060251881Spetersvn_error_t *
1061251881Spetersvn_client__ra_provide_base(svn_stream_t **contents,
1062251881Speter                            svn_revnum_t *revision,
1063251881Speter                            void *baton,
1064251881Speter                            const char *repos_relpath,
1065251881Speter                            apr_pool_t *result_pool,
1066251881Speter                            apr_pool_t *scratch_pool)
1067251881Speter{
1068251881Speter  struct ra_ev2_baton *reb = baton;
1069251881Speter  const char *local_abspath;
1070251881Speter  svn_error_t *err;
1071251881Speter
1072251881Speter  local_abspath = svn_hash_gets(reb->relpath_map, repos_relpath);
1073251881Speter  if (!local_abspath)
1074251881Speter    {
1075251881Speter      *contents = NULL;
1076251881Speter      return SVN_NO_ERROR;
1077251881Speter    }
1078251881Speter
1079251881Speter  err = svn_wc_get_pristine_contents2(contents, reb->wc_ctx, local_abspath,
1080251881Speter                                      result_pool, scratch_pool);
1081251881Speter  if (err)
1082251881Speter    {
1083251881Speter      if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
1084251881Speter        return svn_error_trace(err);
1085251881Speter
1086251881Speter      svn_error_clear(err);
1087251881Speter      *contents = NULL;
1088251881Speter      return SVN_NO_ERROR;
1089251881Speter    }
1090251881Speter
1091251881Speter  if (*contents != NULL)
1092251881Speter    {
1093251881Speter      /* The pristine contents refer to the BASE, or to the pristine of
1094251881Speter         a copy/move to this location. Fetch the correct revision.  */
1095251881Speter      SVN_ERR(svn_wc__node_get_origin(NULL, revision, NULL, NULL, NULL, NULL,
1096299742Sdim                                      NULL,
1097251881Speter                                      reb->wc_ctx, local_abspath, FALSE,
1098251881Speter                                      scratch_pool, scratch_pool));
1099251881Speter    }
1100251881Speter
1101251881Speter  return SVN_NO_ERROR;
1102251881Speter}
1103251881Speter
1104251881Speter
1105251881Spetersvn_error_t *
1106251881Spetersvn_client__ra_provide_props(apr_hash_t **props,
1107251881Speter                             svn_revnum_t *revision,
1108251881Speter                             void *baton,
1109251881Speter                             const char *repos_relpath,
1110251881Speter                             apr_pool_t *result_pool,
1111251881Speter                             apr_pool_t *scratch_pool)
1112251881Speter{
1113251881Speter  struct ra_ev2_baton *reb = baton;
1114251881Speter  const char *local_abspath;
1115251881Speter  svn_error_t *err;
1116251881Speter
1117251881Speter  local_abspath = svn_hash_gets(reb->relpath_map, repos_relpath);
1118251881Speter  if (!local_abspath)
1119251881Speter    {
1120251881Speter      *props = NULL;
1121251881Speter      return SVN_NO_ERROR;
1122251881Speter    }
1123251881Speter
1124251881Speter  err = svn_wc_get_pristine_props(props, reb->wc_ctx, local_abspath,
1125251881Speter                                  result_pool, scratch_pool);
1126251881Speter  if (err)
1127251881Speter    {
1128251881Speter      if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
1129251881Speter        return svn_error_trace(err);
1130251881Speter
1131251881Speter      svn_error_clear(err);
1132251881Speter      *props = NULL;
1133251881Speter      return SVN_NO_ERROR;
1134251881Speter    }
1135251881Speter
1136251881Speter  if (*props != NULL)
1137251881Speter    {
1138251881Speter      /* The pristine props refer to the BASE, or to the pristine props of
1139251881Speter         a copy/move to this location. Fetch the correct revision.  */
1140251881Speter      SVN_ERR(svn_wc__node_get_origin(NULL, revision, NULL, NULL, NULL, NULL,
1141299742Sdim                                      NULL,
1142251881Speter                                      reb->wc_ctx, local_abspath, FALSE,
1143251881Speter                                      scratch_pool, scratch_pool));
1144251881Speter    }
1145251881Speter
1146251881Speter  return SVN_NO_ERROR;
1147251881Speter}
1148251881Speter
1149251881Speter
1150251881Spetersvn_error_t *
1151251881Spetersvn_client__ra_get_copysrc_kind(svn_node_kind_t *kind,
1152251881Speter                                void *baton,
1153251881Speter                                const char *repos_relpath,
1154251881Speter                                svn_revnum_t src_revision,
1155251881Speter                                apr_pool_t *scratch_pool)
1156251881Speter{
1157251881Speter  struct ra_ev2_baton *reb = baton;
1158251881Speter  const char *local_abspath;
1159251881Speter
1160251881Speter  local_abspath = svn_hash_gets(reb->relpath_map, repos_relpath);
1161251881Speter  if (!local_abspath)
1162251881Speter    {
1163251881Speter      *kind = svn_node_unknown;
1164251881Speter      return SVN_NO_ERROR;
1165251881Speter    }
1166251881Speter
1167251881Speter  /* ### what to do with SRC_REVISION?  */
1168251881Speter
1169251881Speter  SVN_ERR(svn_wc_read_kind2(kind, reb->wc_ctx, local_abspath,
1170251881Speter                            FALSE, FALSE, scratch_pool));
1171251881Speter
1172251881Speter  return SVN_NO_ERROR;
1173251881Speter}
1174251881Speter
1175251881Speter
1176251881Spetervoid *
1177251881Spetersvn_client__ra_make_cb_baton(svn_wc_context_t *wc_ctx,
1178251881Speter                             apr_hash_t *relpath_map,
1179251881Speter                             apr_pool_t *result_pool)
1180251881Speter{
1181251881Speter  struct ra_ev2_baton *reb = apr_palloc(result_pool, sizeof(*reb));
1182251881Speter
1183251881Speter  SVN_ERR_ASSERT_NO_RETURN(wc_ctx != NULL);
1184251881Speter  SVN_ERR_ASSERT_NO_RETURN(relpath_map != NULL);
1185251881Speter
1186251881Speter  reb->wc_ctx = wc_ctx;
1187251881Speter  reb->relpath_map = relpath_map;
1188251881Speter
1189251881Speter  return reb;
1190251881Speter}
1191