ra.c revision 253734
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"
45251881Speter
46251881Speter
47251881Speter/* This is the baton that we pass svn_ra_open3(), and is associated with
48251881Speter   the callback table we provide to RA. */
49251881Spetertypedef struct callback_baton_t
50251881Speter{
51251881Speter  /* Holds the directory that corresponds to the REPOS_URL at svn_ra_open3()
52251881Speter     time. When callbacks specify a relative path, they are joined with
53251881Speter     this base directory. */
54251881Speter  const char *base_dir_abspath;
55251881Speter
56251881Speter  /* TEMPORARY: Is 'base_dir_abspath' a versioned path?  cmpilato
57251881Speter     suspects that the commit-to-multiple-disjoint-working-copies
58251881Speter     code is getting this all wrong, sometimes passing an unversioned
59251881Speter     (or versioned in a foreign wc) path here which sorta kinda
60251881Speter     happens to work most of the time but is ultimately incorrect.  */
61251881Speter  svn_boolean_t base_dir_isversioned;
62251881Speter
63251881Speter  /* Used as wri_abspath for obtaining access to the pristine store */
64251881Speter  const char *wcroot_abspath;
65251881Speter
66251881Speter  /* An array of svn_client_commit_item3_t * structures, present only
67251881Speter     during working copy commits. */
68251881Speter  const apr_array_header_t *commit_items;
69251881Speter
70251881Speter  /* A client context. */
71251881Speter  svn_client_ctx_t *ctx;
72251881Speter
73251881Speter} callback_baton_t;
74251881Speter
75251881Speter
76251881Speter
77251881Speterstatic svn_error_t *
78251881Speteropen_tmp_file(apr_file_t **fp,
79251881Speter              void *callback_baton,
80251881Speter              apr_pool_t *pool)
81251881Speter{
82251881Speter  return svn_error_trace(svn_io_open_unique_file3(fp, NULL, NULL,
83251881Speter                                  svn_io_file_del_on_pool_cleanup,
84251881Speter                                  pool, pool));
85251881Speter}
86251881Speter
87251881Speter
88251881Speter/* This implements the 'svn_ra_get_wc_prop_func_t' interface. */
89251881Speterstatic svn_error_t *
90251881Speterget_wc_prop(void *baton,
91251881Speter            const char *relpath,
92251881Speter            const char *name,
93251881Speter            const svn_string_t **value,
94251881Speter            apr_pool_t *pool)
95251881Speter{
96251881Speter  callback_baton_t *cb = baton;
97251881Speter  const char *local_abspath = NULL;
98251881Speter  svn_error_t *err;
99251881Speter
100251881Speter  *value = NULL;
101251881Speter
102251881Speter  /* If we have a list of commit_items, search through that for a
103251881Speter     match for this relative URL. */
104251881Speter  if (cb->commit_items)
105251881Speter    {
106251881Speter      int i;
107251881Speter      for (i = 0; i < cb->commit_items->nelts; i++)
108251881Speter        {
109251881Speter          svn_client_commit_item3_t *item
110251881Speter            = APR_ARRAY_IDX(cb->commit_items, i, svn_client_commit_item3_t *);
111251881Speter
112251881Speter          if (! strcmp(relpath, item->session_relpath))
113251881Speter            {
114251881Speter              SVN_ERR_ASSERT(svn_dirent_is_absolute(item->path));
115251881Speter              local_abspath = item->path;
116251881Speter              break;
117251881Speter            }
118251881Speter        }
119251881Speter
120251881Speter      /* Commits can only query relpaths in the commit_items list
121251881Speter         since the commit driver traverses paths as they are, or will
122251881Speter         be, in the repository.  Non-commits query relpaths in the
123251881Speter         working copy. */
124251881Speter      if (! local_abspath)
125251881Speter        return SVN_NO_ERROR;
126251881Speter    }
127251881Speter
128251881Speter  /* If we don't have a base directory, then there are no properties. */
129251881Speter  else if (cb->base_dir_abspath == NULL)
130251881Speter    return SVN_NO_ERROR;
131251881Speter
132251881Speter  else
133251881Speter    local_abspath = svn_dirent_join(cb->base_dir_abspath, relpath, pool);
134251881Speter
135251881Speter  err = svn_wc_prop_get2(value, cb->ctx->wc_ctx, local_abspath, name,
136251881Speter                         pool, pool);
137251881Speter  if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
138251881Speter    {
139251881Speter      svn_error_clear(err);
140251881Speter      err = NULL;
141251881Speter    }
142251881Speter  return svn_error_trace(err);
143251881Speter}
144251881Speter
145251881Speter/* This implements the 'svn_ra_push_wc_prop_func_t' interface. */
146251881Speterstatic svn_error_t *
147251881Speterpush_wc_prop(void *baton,
148251881Speter             const char *relpath,
149251881Speter             const char *name,
150251881Speter             const svn_string_t *value,
151251881Speter             apr_pool_t *pool)
152251881Speter{
153251881Speter  callback_baton_t *cb = baton;
154251881Speter  int i;
155251881Speter
156251881Speter  /* If we're committing, search through the commit_items list for a
157251881Speter     match for this relative URL. */
158251881Speter  if (! cb->commit_items)
159251881Speter    return svn_error_createf
160251881Speter      (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
161251881Speter       _("Attempt to set wcprop '%s' on '%s' in a non-commit operation"),
162251881Speter       name, svn_dirent_local_style(relpath, pool));
163251881Speter
164251881Speter  for (i = 0; i < cb->commit_items->nelts; i++)
165251881Speter    {
166251881Speter      svn_client_commit_item3_t *item
167251881Speter        = APR_ARRAY_IDX(cb->commit_items, i, svn_client_commit_item3_t *);
168251881Speter
169251881Speter      if (strcmp(relpath, item->session_relpath) == 0)
170251881Speter        {
171251881Speter          apr_pool_t *changes_pool = item->incoming_prop_changes->pool;
172251881Speter          svn_prop_t *prop = apr_palloc(changes_pool, sizeof(*prop));
173251881Speter
174251881Speter          prop->name = apr_pstrdup(changes_pool, name);
175251881Speter          if (value)
176251881Speter            prop->value = svn_string_dup(value, changes_pool);
177251881Speter          else
178251881Speter            prop->value = NULL;
179251881Speter
180251881Speter          /* Buffer the propchange to take effect during the
181251881Speter             post-commit process. */
182251881Speter          APR_ARRAY_PUSH(item->incoming_prop_changes, svn_prop_t *) = prop;
183251881Speter          return SVN_NO_ERROR;
184251881Speter        }
185251881Speter    }
186251881Speter
187251881Speter  return SVN_NO_ERROR;
188251881Speter}
189251881Speter
190251881Speter
191251881Speter/* This implements the 'svn_ra_set_wc_prop_func_t' interface. */
192251881Speterstatic svn_error_t *
193251881Speterset_wc_prop(void *baton,
194251881Speter            const char *path,
195251881Speter            const char *name,
196251881Speter            const svn_string_t *value,
197251881Speter            apr_pool_t *pool)
198251881Speter{
199251881Speter  callback_baton_t *cb = baton;
200251881Speter  const char *local_abspath;
201251881Speter
202251881Speter  local_abspath = svn_dirent_join(cb->base_dir_abspath, path, pool);
203251881Speter
204251881Speter  /* We pass 1 for the 'force' parameter here.  Since the property is
205251881Speter     coming from the repository, we definitely want to accept it.
206251881Speter     Ideally, we'd raise a conflict if, say, the received property is
207251881Speter     svn:eol-style yet the file has a locally added svn:mime-type
208251881Speter     claiming that it's binary.  Probably the repository is still
209251881Speter     right, but the conflict would remind the user to make sure.
210251881Speter     Unfortunately, we don't have a clean mechanism for doing that
211251881Speter     here, so we just set the property and hope for the best. */
212251881Speter  return svn_error_trace(svn_wc_prop_set4(cb->ctx->wc_ctx, local_abspath,
213251881Speter                                          name,
214251881Speter                                          value, svn_depth_empty,
215251881Speter                                          TRUE /* skip_checks */,
216251881Speter                                          NULL /* changelist_filter */,
217251881Speter                                          NULL, NULL /* cancellation */,
218251881Speter                                          NULL, NULL /* notification */,
219251881Speter                                          pool));
220251881Speter}
221251881Speter
222251881Speter
223251881Speter/* This implements the `svn_ra_invalidate_wc_props_func_t' interface. */
224251881Speterstatic svn_error_t *
225251881Speterinvalidate_wc_props(void *baton,
226251881Speter                    const char *path,
227251881Speter                    const char *prop_name,
228251881Speter                    apr_pool_t *pool)
229251881Speter{
230251881Speter  callback_baton_t *cb = baton;
231251881Speter  const char *local_abspath;
232251881Speter
233251881Speter  local_abspath = svn_dirent_join(cb->base_dir_abspath, path, pool);
234251881Speter
235251881Speter  /* It's easier just to clear the whole dav_cache than to remove
236251881Speter     individual items from it recursively like this.  And since we
237251881Speter     know that the RA providers that ship with Subversion only
238251881Speter     invalidate the one property they use the most from this cache,
239251881Speter     and that we're intentionally trying to get away from the use of
240251881Speter     the cache altogether anyway, there's little to lose in wiping the
241251881Speter     whole cache.  Is it the most well-behaved approach to take?  Not
242251881Speter     so much.  We choose not to care.  */
243251881Speter  return svn_error_trace(svn_wc__node_clear_dav_cache_recursive(
244251881Speter                              cb->ctx->wc_ctx, local_abspath, pool));
245251881Speter}
246251881Speter
247251881Speter
248251881Speter/* This implements the `svn_ra_get_wc_contents_func_t' interface. */
249251881Speterstatic svn_error_t *
250251881Speterget_wc_contents(void *baton,
251251881Speter                svn_stream_t **contents,
252251881Speter                const svn_checksum_t *checksum,
253251881Speter                apr_pool_t *pool)
254251881Speter{
255251881Speter  callback_baton_t *cb = baton;
256251881Speter
257251881Speter  if (! cb->wcroot_abspath)
258251881Speter    {
259251881Speter      *contents = NULL;
260251881Speter      return SVN_NO_ERROR;
261251881Speter    }
262251881Speter
263251881Speter  return svn_error_trace(
264251881Speter             svn_wc__get_pristine_contents_by_checksum(contents,
265251881Speter                                                       cb->ctx->wc_ctx,
266251881Speter                                                       cb->wcroot_abspath,
267251881Speter                                                       checksum,
268251881Speter                                                       pool, pool));
269251881Speter}
270251881Speter
271251881Speter
272251881Speterstatic svn_error_t *
273251881Spetercancel_callback(void *baton)
274251881Speter{
275251881Speter  callback_baton_t *b = baton;
276251881Speter  return svn_error_trace((b->ctx->cancel_func)(b->ctx->cancel_baton));
277251881Speter}
278251881Speter
279251881Speter
280251881Speterstatic svn_error_t *
281251881Speterget_client_string(void *baton,
282251881Speter                  const char **name,
283251881Speter                  apr_pool_t *pool)
284251881Speter{
285251881Speter  callback_baton_t *b = baton;
286251881Speter  *name = apr_pstrdup(pool, b->ctx->client_name);
287251881Speter  return SVN_NO_ERROR;
288251881Speter}
289251881Speter
290251881Speter
291251881Speter#define SVN_CLIENT__MAX_REDIRECT_ATTEMPTS 3 /* ### TODO:  Make configurable. */
292251881Speter
293251881Spetersvn_error_t *
294251881Spetersvn_client__open_ra_session_internal(svn_ra_session_t **ra_session,
295251881Speter                                     const char **corrected_url,
296251881Speter                                     const char *base_url,
297251881Speter                                     const char *base_dir_abspath,
298251881Speter                                     const apr_array_header_t *commit_items,
299251881Speter                                     svn_boolean_t write_dav_props,
300251881Speter                                     svn_boolean_t read_dav_props,
301251881Speter                                     svn_client_ctx_t *ctx,
302251881Speter                                     apr_pool_t *result_pool,
303251881Speter                                     apr_pool_t *scratch_pool)
304251881Speter{
305251881Speter  svn_ra_callbacks2_t *cbtable;
306251881Speter  callback_baton_t *cb = apr_pcalloc(result_pool, sizeof(*cb));
307251881Speter  const char *uuid = NULL;
308251881Speter
309251881Speter  SVN_ERR_ASSERT(!write_dav_props || read_dav_props);
310251881Speter  SVN_ERR_ASSERT(!read_dav_props || base_dir_abspath != NULL);
311251881Speter  SVN_ERR_ASSERT(base_dir_abspath == NULL
312251881Speter                        || svn_dirent_is_absolute(base_dir_abspath));
313251881Speter
314251881Speter  SVN_ERR(svn_ra_create_callbacks(&cbtable, result_pool));
315251881Speter  cbtable->open_tmp_file = open_tmp_file;
316251881Speter  cbtable->get_wc_prop = read_dav_props ? get_wc_prop : NULL;
317251881Speter  cbtable->set_wc_prop = (write_dav_props && read_dav_props)
318251881Speter                          ? set_wc_prop : NULL;
319251881Speter  cbtable->push_wc_prop = commit_items ? push_wc_prop : NULL;
320251881Speter  cbtable->invalidate_wc_props = (write_dav_props && read_dav_props)
321251881Speter                                  ? invalidate_wc_props : NULL;
322251881Speter  cbtable->auth_baton = ctx->auth_baton; /* new-style */
323251881Speter  cbtable->progress_func = ctx->progress_func;
324251881Speter  cbtable->progress_baton = ctx->progress_baton;
325251881Speter  cbtable->cancel_func = ctx->cancel_func ? cancel_callback : NULL;
326251881Speter  cbtable->get_client_string = get_client_string;
327251881Speter  if (base_dir_abspath)
328251881Speter    cbtable->get_wc_contents = get_wc_contents;
329251881Speter
330251881Speter  cb->commit_items = commit_items;
331251881Speter  cb->ctx = ctx;
332251881Speter
333251881Speter  if (base_dir_abspath && (read_dav_props || write_dav_props))
334251881Speter    {
335251881Speter      svn_error_t *err = svn_wc__node_get_repos_info(NULL, NULL, NULL, &uuid,
336251881Speter                                                     ctx->wc_ctx,
337251881Speter                                                     base_dir_abspath,
338251881Speter                                                     result_pool,
339251881Speter                                                     scratch_pool);
340251881Speter
341251881Speter      if (err && (err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY
342251881Speter                  || err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND
343251881Speter                  || err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED))
344251881Speter        {
345251881Speter          svn_error_clear(err);
346251881Speter          uuid = NULL;
347251881Speter        }
348251881Speter      else
349251881Speter        {
350251881Speter          SVN_ERR(err);
351251881Speter          cb->base_dir_isversioned = TRUE;
352251881Speter        }
353251881Speter      cb->base_dir_abspath = apr_pstrdup(result_pool, base_dir_abspath);
354251881Speter    }
355251881Speter
356251881Speter  if (base_dir_abspath)
357251881Speter    {
358251881Speter      svn_error_t *err = svn_wc__get_wcroot(&cb->wcroot_abspath,
359251881Speter                                            ctx->wc_ctx, base_dir_abspath,
360251881Speter                                            result_pool, scratch_pool);
361251881Speter
362251881Speter      if (err)
363251881Speter        {
364251881Speter          if (err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY
365251881Speter              && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND
366251881Speter              && err->apr_err != SVN_ERR_WC_UPGRADE_REQUIRED)
367251881Speter            return svn_error_trace(err);
368251881Speter
369251881Speter          svn_error_clear(err);
370251881Speter          cb->wcroot_abspath = NULL;
371251881Speter        }
372251881Speter    }
373251881Speter
374251881Speter  /* If the caller allows for auto-following redirections, and the
375251881Speter     RA->open() call above reveals a CORRECTED_URL, try the new URL.
376251881Speter     We'll do this in a loop up to some maximum number follow-and-retry
377251881Speter     attempts.  */
378251881Speter  if (corrected_url)
379251881Speter    {
380251881Speter      apr_hash_t *attempted = apr_hash_make(scratch_pool);
381251881Speter      int attempts_left = SVN_CLIENT__MAX_REDIRECT_ATTEMPTS;
382251881Speter
383251881Speter      *corrected_url = NULL;
384251881Speter      while (attempts_left--)
385251881Speter        {
386251881Speter          const char *corrected = NULL;
387251881Speter
388251881Speter          /* Try to open the RA session.  If this is our last attempt,
389251881Speter             don't accept corrected URLs from the RA provider. */
390251881Speter          SVN_ERR(svn_ra_open4(ra_session,
391251881Speter                               attempts_left == 0 ? NULL : &corrected,
392251881Speter                               base_url, uuid, cbtable, cb, ctx->config,
393251881Speter                               result_pool));
394251881Speter
395251881Speter          /* No error and no corrected URL?  We're done here. */
396251881Speter          if (! corrected)
397251881Speter            break;
398251881Speter
399251881Speter          /* Notify the user that a redirect is being followed. */
400251881Speter          if (ctx->notify_func2 != NULL)
401251881Speter            {
402251881Speter              svn_wc_notify_t *notify =
403251881Speter                svn_wc_create_notify_url(corrected,
404251881Speter                                         svn_wc_notify_url_redirect,
405251881Speter                                         scratch_pool);
406251881Speter              (*ctx->notify_func2)(ctx->notify_baton2, notify, scratch_pool);
407251881Speter            }
408251881Speter
409251881Speter          /* Our caller will want to know what our final corrected URL was. */
410251881Speter          *corrected_url = corrected;
411251881Speter
412251881Speter          /* Make sure we've not attempted this URL before. */
413251881Speter          if (svn_hash_gets(attempted, corrected))
414251881Speter            return svn_error_createf(SVN_ERR_CLIENT_CYCLE_DETECTED, NULL,
415251881Speter                                     _("Redirect cycle detected for URL '%s'"),
416251881Speter                                     corrected);
417251881Speter
418251881Speter          /* Remember this CORRECTED_URL so we don't wind up in a loop. */
419251881Speter          svn_hash_sets(attempted, corrected, (void *)1);
420251881Speter          base_url = corrected;
421251881Speter        }
422251881Speter    }
423251881Speter  else
424251881Speter    {
425251881Speter      SVN_ERR(svn_ra_open4(ra_session, NULL, base_url,
426251881Speter                           uuid, cbtable, cb, ctx->config, result_pool));
427251881Speter    }
428251881Speter
429251881Speter  return SVN_NO_ERROR;
430251881Speter}
431251881Speter#undef SVN_CLIENT__MAX_REDIRECT_ATTEMPTS
432251881Speter
433251881Speter
434251881Spetersvn_error_t *
435251881Spetersvn_client_open_ra_session2(svn_ra_session_t **session,
436251881Speter                            const char *url,
437251881Speter                            const char *wri_abspath,
438251881Speter                            svn_client_ctx_t *ctx,
439251881Speter                            apr_pool_t *result_pool,
440251881Speter                            apr_pool_t *scratch_pool)
441251881Speter{
442251881Speter  return svn_error_trace(
443251881Speter             svn_client__open_ra_session_internal(session, NULL, url,
444251881Speter                                                  wri_abspath, NULL,
445251881Speter                                                  FALSE, FALSE,
446251881Speter                                                  ctx, result_pool,
447251881Speter                                                  scratch_pool));
448251881Speter}
449251881Speter
450251881Spetersvn_error_t *
451251881Spetersvn_client__resolve_rev_and_url(svn_client__pathrev_t **resolved_loc_p,
452251881Speter                                svn_ra_session_t *ra_session,
453251881Speter                                const char *path_or_url,
454251881Speter                                const svn_opt_revision_t *peg_revision,
455251881Speter                                const svn_opt_revision_t *revision,
456251881Speter                                svn_client_ctx_t *ctx,
457251881Speter                                apr_pool_t *pool)
458251881Speter{
459251881Speter  svn_opt_revision_t peg_rev = *peg_revision;
460251881Speter  svn_opt_revision_t start_rev = *revision;
461251881Speter  const char *url;
462251881Speter  svn_revnum_t rev;
463251881Speter
464251881Speter  /* Default revisions: peg -> working or head; operative -> peg. */
465251881Speter  SVN_ERR(svn_opt_resolve_revisions(&peg_rev, &start_rev,
466251881Speter                                    svn_path_is_url(path_or_url),
467251881Speter                                    TRUE /* notice_local_mods */,
468251881Speter                                    pool));
469251881Speter
470251881Speter  /* Run the history function to get the object's (possibly
471251881Speter     different) url in REVISION. */
472251881Speter  SVN_ERR(svn_client__repos_locations(&url, &rev, NULL, NULL,
473251881Speter                                      ra_session, path_or_url, &peg_rev,
474251881Speter                                      &start_rev, NULL, ctx, pool));
475251881Speter
476251881Speter  SVN_ERR(svn_client__pathrev_create_with_session(resolved_loc_p,
477251881Speter                                                  ra_session, rev, url, pool));
478251881Speter  return SVN_NO_ERROR;
479251881Speter}
480251881Speter
481251881Spetersvn_error_t *
482251881Spetersvn_client__ra_session_from_path2(svn_ra_session_t **ra_session_p,
483251881Speter                                  svn_client__pathrev_t **resolved_loc_p,
484251881Speter                                  const char *path_or_url,
485251881Speter                                  const char *base_dir_abspath,
486251881Speter                                  const svn_opt_revision_t *peg_revision,
487251881Speter                                  const svn_opt_revision_t *revision,
488251881Speter                                  svn_client_ctx_t *ctx,
489251881Speter                                  apr_pool_t *pool)
490251881Speter{
491251881Speter  svn_ra_session_t *ra_session;
492251881Speter  const char *initial_url;
493251881Speter  const char *corrected_url;
494251881Speter  svn_client__pathrev_t *resolved_loc;
495251881Speter  const char *wri_abspath;
496251881Speter
497251881Speter  SVN_ERR(svn_client_url_from_path2(&initial_url, path_or_url, ctx, pool,
498251881Speter                                    pool));
499251881Speter  if (! initial_url)
500251881Speter    return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL,
501251881Speter                             _("'%s' has no URL"), path_or_url);
502251881Speter
503251881Speter  if (base_dir_abspath)
504251881Speter    wri_abspath = base_dir_abspath;
505251881Speter  else if (!svn_path_is_url(path_or_url))
506251881Speter    SVN_ERR(svn_dirent_get_absolute(&wri_abspath, path_or_url, pool));
507251881Speter  else
508251881Speter    wri_abspath = NULL;
509251881Speter
510251881Speter  SVN_ERR(svn_client__open_ra_session_internal(&ra_session, &corrected_url,
511251881Speter                                               initial_url,
512251881Speter                                               wri_abspath,
513251881Speter                                               NULL /* commit_items */,
514251881Speter                                               base_dir_abspath != NULL,
515251881Speter                                               base_dir_abspath != NULL,
516251881Speter                                               ctx, pool, pool));
517251881Speter
518251881Speter  /* If we got a CORRECTED_URL, we'll want to refer to that as the
519251881Speter     URL-ized form of PATH_OR_URL from now on. */
520251881Speter  if (corrected_url && svn_path_is_url(path_or_url))
521251881Speter    path_or_url = corrected_url;
522251881Speter
523251881Speter  SVN_ERR(svn_client__resolve_rev_and_url(&resolved_loc, ra_session,
524251881Speter                                          path_or_url, peg_revision, revision,
525251881Speter                                          ctx, pool));
526251881Speter
527251881Speter  /* Make the session point to the real URL. */
528251881Speter  SVN_ERR(svn_ra_reparent(ra_session, resolved_loc->url, pool));
529251881Speter
530251881Speter  *ra_session_p = ra_session;
531251881Speter  if (resolved_loc_p)
532251881Speter    *resolved_loc_p = resolved_loc;
533251881Speter
534251881Speter  return SVN_NO_ERROR;
535251881Speter}
536251881Speter
537251881Speter
538251881Spetersvn_error_t *
539251881Spetersvn_client__ensure_ra_session_url(const char **old_session_url,
540251881Speter                                  svn_ra_session_t *ra_session,
541251881Speter                                  const char *session_url,
542251881Speter                                  apr_pool_t *pool)
543251881Speter{
544251881Speter  SVN_ERR(svn_ra_get_session_url(ra_session, old_session_url, pool));
545251881Speter  if (! session_url)
546251881Speter    SVN_ERR(svn_ra_get_repos_root2(ra_session, &session_url, pool));
547251881Speter  if (strcmp(*old_session_url, session_url) != 0)
548251881Speter    SVN_ERR(svn_ra_reparent(ra_session, session_url, pool));
549251881Speter  return SVN_NO_ERROR;
550251881Speter}
551251881Speter
552251881Speter
553251881Speter
554251881Speter/*** Repository Locations ***/
555251881Speter
556251881Speterstruct gls_receiver_baton_t
557251881Speter{
558251881Speter  apr_array_header_t *segments;
559251881Speter  svn_client_ctx_t *ctx;
560251881Speter  apr_pool_t *pool;
561251881Speter};
562251881Speter
563251881Speterstatic svn_error_t *
564251881Spetergls_receiver(svn_location_segment_t *segment,
565251881Speter             void *baton,
566251881Speter             apr_pool_t *pool)
567251881Speter{
568251881Speter  struct gls_receiver_baton_t *b = baton;
569251881Speter  APR_ARRAY_PUSH(b->segments, svn_location_segment_t *) =
570251881Speter    svn_location_segment_dup(segment, b->pool);
571251881Speter  if (b->ctx->cancel_func)
572251881Speter    SVN_ERR((b->ctx->cancel_func)(b->ctx->cancel_baton));
573251881Speter  return SVN_NO_ERROR;
574251881Speter}
575251881Speter
576251881Speter/* A qsort-compatible function which sorts svn_location_segment_t's
577251881Speter   based on their revision range covering, resulting in ascending
578251881Speter   (oldest-to-youngest) ordering. */
579251881Speterstatic int
580251881Spetercompare_segments(const void *a, const void *b)
581251881Speter{
582251881Speter  const svn_location_segment_t *a_seg
583251881Speter    = *((const svn_location_segment_t * const *) a);
584251881Speter  const svn_location_segment_t *b_seg
585251881Speter    = *((const svn_location_segment_t * const *) b);
586251881Speter  if (a_seg->range_start == b_seg->range_start)
587251881Speter    return 0;
588251881Speter  return (a_seg->range_start < b_seg->range_start) ? -1 : 1;
589251881Speter}
590251881Speter
591251881Spetersvn_error_t *
592251881Spetersvn_client__repos_location_segments(apr_array_header_t **segments,
593251881Speter                                    svn_ra_session_t *ra_session,
594251881Speter                                    const char *url,
595251881Speter                                    svn_revnum_t peg_revision,
596251881Speter                                    svn_revnum_t start_revision,
597251881Speter                                    svn_revnum_t end_revision,
598251881Speter                                    svn_client_ctx_t *ctx,
599251881Speter                                    apr_pool_t *pool)
600251881Speter{
601251881Speter  struct gls_receiver_baton_t gls_receiver_baton;
602251881Speter  const char *old_session_url;
603251881Speter  svn_error_t *err;
604251881Speter
605251881Speter  *segments = apr_array_make(pool, 8, sizeof(svn_location_segment_t *));
606251881Speter  gls_receiver_baton.segments = *segments;
607251881Speter  gls_receiver_baton.ctx = ctx;
608251881Speter  gls_receiver_baton.pool = pool;
609251881Speter  SVN_ERR(svn_client__ensure_ra_session_url(&old_session_url, ra_session,
610251881Speter                                            url, pool));
611251881Speter  err = svn_ra_get_location_segments(ra_session, "", peg_revision,
612251881Speter                                     start_revision, end_revision,
613251881Speter                                     gls_receiver, &gls_receiver_baton,
614251881Speter                                     pool);
615251881Speter  SVN_ERR(svn_error_compose_create(
616251881Speter            err, svn_ra_reparent(ra_session, old_session_url, pool)));
617251881Speter  qsort((*segments)->elts, (*segments)->nelts,
618251881Speter        (*segments)->elt_size, compare_segments);
619251881Speter  return SVN_NO_ERROR;
620251881Speter}
621251881Speter
622251881Speter/* Set *START_URL and *END_URL to the URLs that the object URL@PEG_REVNUM
623251881Speter * had in revisions START_REVNUM and END_REVNUM.  Return an error if the
624251881Speter * node cannot be traced back to one of the requested revisions.
625251881Speter *
626251881Speter * START_URL and/or END_URL may be NULL if not wanted.  START_REVNUM and
627251881Speter * END_REVNUM must be valid revision numbers except that END_REVNUM may
628251881Speter * be SVN_INVALID_REVNUM if END_URL is NULL.
629251881Speter *
630251881Speter * RA_SESSION is an open RA session parented at URL.
631251881Speter */
632251881Speterstatic svn_error_t *
633251881Speterrepos_locations(const char **start_url,
634251881Speter                const char **end_url,
635251881Speter                svn_ra_session_t *ra_session,
636251881Speter                const char *url,
637251881Speter                svn_revnum_t peg_revnum,
638251881Speter                svn_revnum_t start_revnum,
639251881Speter                svn_revnum_t end_revnum,
640251881Speter                apr_pool_t *result_pool,
641251881Speter                apr_pool_t *scratch_pool)
642251881Speter{
643251881Speter  const char *repos_url, *start_path, *end_path;
644251881Speter  apr_array_header_t *revs;
645251881Speter  apr_hash_t *rev_locs;
646251881Speter
647251881Speter  SVN_ERR_ASSERT(peg_revnum != SVN_INVALID_REVNUM);
648251881Speter  SVN_ERR_ASSERT(start_revnum != SVN_INVALID_REVNUM);
649251881Speter  SVN_ERR_ASSERT(end_revnum != SVN_INVALID_REVNUM || end_url == NULL);
650251881Speter
651251881Speter  /* Avoid a network request in the common easy case. */
652251881Speter  if (start_revnum == peg_revnum
653251881Speter      && (end_revnum == peg_revnum || end_revnum == SVN_INVALID_REVNUM))
654251881Speter    {
655251881Speter      if (start_url)
656251881Speter        *start_url = apr_pstrdup(result_pool, url);
657251881Speter      if (end_url)
658251881Speter        *end_url = apr_pstrdup(result_pool, url);
659251881Speter      return SVN_NO_ERROR;
660251881Speter    }
661251881Speter
662251881Speter  SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_url, scratch_pool));
663251881Speter
664251881Speter  revs = apr_array_make(scratch_pool, 2, sizeof(svn_revnum_t));
665251881Speter  APR_ARRAY_PUSH(revs, svn_revnum_t) = start_revnum;
666251881Speter  if (end_revnum != start_revnum && end_revnum != SVN_INVALID_REVNUM)
667251881Speter    APR_ARRAY_PUSH(revs, svn_revnum_t) = end_revnum;
668251881Speter
669251881Speter  SVN_ERR(svn_ra_get_locations(ra_session, &rev_locs, "", peg_revnum,
670251881Speter                               revs, scratch_pool));
671251881Speter
672251881Speter  /* We'd better have all the paths we were looking for! */
673251881Speter  if (start_url)
674251881Speter    {
675251881Speter      start_path = apr_hash_get(rev_locs, &start_revnum, sizeof(svn_revnum_t));
676251881Speter      if (! start_path)
677251881Speter        return svn_error_createf
678251881Speter          (SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
679251881Speter           _("Unable to find repository location for '%s' in revision %ld"),
680251881Speter           url, start_revnum);
681251881Speter      *start_url = svn_path_url_add_component2(repos_url, start_path + 1,
682251881Speter                                               result_pool);
683251881Speter    }
684251881Speter
685251881Speter  if (end_url)
686251881Speter    {
687251881Speter      end_path = apr_hash_get(rev_locs, &end_revnum, sizeof(svn_revnum_t));
688251881Speter      if (! end_path)
689251881Speter        return svn_error_createf
690251881Speter          (SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
691251881Speter           _("The location for '%s' for revision %ld does not exist in the "
692251881Speter             "repository or refers to an unrelated object"),
693251881Speter           url, end_revnum);
694251881Speter
695251881Speter      *end_url = svn_path_url_add_component2(repos_url, end_path + 1,
696251881Speter                                             result_pool);
697251881Speter    }
698251881Speter
699251881Speter  return SVN_NO_ERROR;
700251881Speter}
701251881Speter
702251881Spetersvn_error_t *
703251881Spetersvn_client__repos_location(svn_client__pathrev_t **op_loc_p,
704251881Speter                           svn_ra_session_t *ra_session,
705251881Speter                           const svn_client__pathrev_t *peg_loc,
706251881Speter                           svn_revnum_t op_revnum,
707251881Speter                           svn_client_ctx_t *ctx,
708251881Speter                           apr_pool_t *result_pool,
709251881Speter                           apr_pool_t *scratch_pool)
710251881Speter{
711251881Speter  const char *old_session_url;
712251881Speter  const char *op_url;
713251881Speter  svn_error_t *err;
714251881Speter
715251881Speter  SVN_ERR(svn_client__ensure_ra_session_url(&old_session_url, ra_session,
716251881Speter                                            peg_loc->url, scratch_pool));
717251881Speter  err = repos_locations(&op_url, NULL, ra_session,
718251881Speter                        peg_loc->url, peg_loc->rev,
719251881Speter                        op_revnum, SVN_INVALID_REVNUM,
720251881Speter                        result_pool, scratch_pool);
721251881Speter  SVN_ERR(svn_error_compose_create(
722251881Speter            err, svn_ra_reparent(ra_session, old_session_url, scratch_pool)));
723251881Speter
724251881Speter  *op_loc_p = svn_client__pathrev_create(peg_loc->repos_root_url,
725251881Speter                                         peg_loc->repos_uuid,
726251881Speter                                         op_revnum, op_url, result_pool);
727251881Speter  return SVN_NO_ERROR;
728251881Speter}
729251881Speter
730251881Spetersvn_error_t *
731251881Spetersvn_client__repos_locations(const char **start_url,
732251881Speter                            svn_revnum_t *start_revision,
733251881Speter                            const char **end_url,
734251881Speter                            svn_revnum_t *end_revision,
735251881Speter                            svn_ra_session_t *ra_session,
736251881Speter                            const char *path,
737251881Speter                            const svn_opt_revision_t *revision,
738251881Speter                            const svn_opt_revision_t *start,
739251881Speter                            const svn_opt_revision_t *end,
740251881Speter                            svn_client_ctx_t *ctx,
741251881Speter                            apr_pool_t *pool)
742251881Speter{
743251881Speter  const char *url;
744251881Speter  const char *local_abspath_or_url;
745251881Speter  svn_revnum_t peg_revnum = SVN_INVALID_REVNUM;
746251881Speter  svn_revnum_t start_revnum, end_revnum;
747251881Speter  svn_revnum_t youngest_rev = SVN_INVALID_REVNUM;
748251881Speter  apr_pool_t *subpool = svn_pool_create(pool);
749251881Speter
750251881Speter  /* Ensure that we are given some real revision data to work with.
751251881Speter     (It's okay if the END is unspecified -- in that case, we'll just
752251881Speter     set it to the same thing as START.)  */
753251881Speter  if (revision->kind == svn_opt_revision_unspecified
754251881Speter      || start->kind == svn_opt_revision_unspecified)
755251881Speter    return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL);
756251881Speter
757251881Speter  if (end == NULL)
758251881Speter    {
759251881Speter      static const svn_opt_revision_t unspecified_rev
760251881Speter        = { svn_opt_revision_unspecified, { 0 } };
761251881Speter
762251881Speter      end = &unspecified_rev;
763251881Speter    }
764251881Speter
765251881Speter  /* Determine LOCAL_ABSPATH_OR_URL, URL, and possibly PEG_REVNUM.
766251881Speter     If we are looking at the working version of a WC path that is scheduled
767251881Speter     as a copy, then we need to use the copy-from URL and peg revision. */
768251881Speter  if (! svn_path_is_url(path))
769251881Speter    {
770251881Speter      SVN_ERR(svn_dirent_get_absolute(&local_abspath_or_url, path, subpool));
771251881Speter
772251881Speter      if (revision->kind == svn_opt_revision_working)
773251881Speter        {
774251881Speter          const char *repos_root_url;
775251881Speter          const char *repos_relpath;
776251881Speter          svn_boolean_t is_copy;
777251881Speter
778251881Speter          SVN_ERR(svn_wc__node_get_origin(&is_copy, &peg_revnum, &repos_relpath,
779251881Speter                                          &repos_root_url, NULL, NULL,
780251881Speter                                          ctx->wc_ctx, local_abspath_or_url,
781251881Speter                                          FALSE, subpool, subpool));
782251881Speter
783251881Speter          if (repos_relpath)
784251881Speter            url = svn_path_url_add_component2(repos_root_url, repos_relpath,
785251881Speter                                              pool);
786251881Speter          else
787251881Speter            url = NULL;
788251881Speter
789251881Speter          if (url && is_copy && ra_session)
790251881Speter            {
791251881Speter              const char *session_url;
792251881Speter              SVN_ERR(svn_ra_get_session_url(ra_session, &session_url,
793251881Speter                                             subpool));
794251881Speter
795251881Speter              if (strcmp(session_url, url) != 0)
796251881Speter                {
797251881Speter                  /* We can't use the caller provided RA session now :( */
798251881Speter                  ra_session = NULL;
799251881Speter                }
800251881Speter            }
801251881Speter        }
802251881Speter      else
803251881Speter        url = NULL;
804251881Speter
805251881Speter      if (! url)
806251881Speter        SVN_ERR(svn_wc__node_get_url(&url, ctx->wc_ctx,
807251881Speter                                     local_abspath_or_url, pool, subpool));
808251881Speter
809251881Speter      if (!url)
810251881Speter        {
811251881Speter          return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL,
812251881Speter                                   _("'%s' has no URL"),
813251881Speter                                   svn_dirent_local_style(path, pool));
814251881Speter        }
815251881Speter    }
816251881Speter  else
817251881Speter    {
818251881Speter      local_abspath_or_url = path;
819251881Speter      url = path;
820251881Speter    }
821251881Speter
822251881Speter  /* ### We should be smarter here.  If the callers just asks for BASE and
823251881Speter     WORKING revisions, we should already have the correct URLs, so we
824251881Speter     don't need to do anything more here in that case. */
825251881Speter
826251881Speter  /* Open a RA session to this URL if we don't have one already. */
827251881Speter  if (! ra_session)
828251881Speter    SVN_ERR(svn_client_open_ra_session2(&ra_session, url, NULL,
829251881Speter                                        ctx, subpool, subpool));
830251881Speter
831251881Speter  /* Resolve the opt_revision_ts. */
832251881Speter  if (peg_revnum == SVN_INVALID_REVNUM)
833251881Speter    SVN_ERR(svn_client__get_revision_number(&peg_revnum, &youngest_rev,
834251881Speter                                            ctx->wc_ctx, local_abspath_or_url,
835251881Speter                                            ra_session, revision, pool));
836251881Speter
837251881Speter  SVN_ERR(svn_client__get_revision_number(&start_revnum, &youngest_rev,
838251881Speter                                          ctx->wc_ctx, local_abspath_or_url,
839251881Speter                                          ra_session, start, pool));
840251881Speter  if (end->kind == svn_opt_revision_unspecified)
841251881Speter    end_revnum = start_revnum;
842251881Speter  else
843251881Speter    SVN_ERR(svn_client__get_revision_number(&end_revnum, &youngest_rev,
844251881Speter                                            ctx->wc_ctx, local_abspath_or_url,
845251881Speter                                            ra_session, end, pool));
846251881Speter
847251881Speter  /* Set the output revision variables. */
848251881Speter  if (start_revision)
849251881Speter    {
850251881Speter      *start_revision = start_revnum;
851251881Speter    }
852251881Speter  if (end_revision && end->kind != svn_opt_revision_unspecified)
853251881Speter    {
854251881Speter      *end_revision = end_revnum;
855251881Speter    }
856251881Speter
857251881Speter  SVN_ERR(repos_locations(start_url, end_url,
858251881Speter                          ra_session, url, peg_revnum,
859251881Speter                          start_revnum, end_revnum,
860251881Speter                          pool, subpool));
861251881Speter  svn_pool_destroy(subpool);
862251881Speter  return SVN_NO_ERROR;
863251881Speter}
864251881Speter
865251881Spetersvn_error_t *
866253734Spetersvn_client__calc_youngest_common_ancestor(svn_client__pathrev_t **ancestor_p,
867253734Speter                                          const svn_client__pathrev_t *loc1,
868253734Speter                                          apr_hash_t *history1,
869253734Speter                                          svn_boolean_t has_rev_zero_history1,
870253734Speter                                          const svn_client__pathrev_t *loc2,
871253734Speter                                          apr_hash_t *history2,
872253734Speter                                          svn_boolean_t has_rev_zero_history2,
873253734Speter                                          apr_pool_t *result_pool,
874253734Speter                                          apr_pool_t *scratch_pool)
875251881Speter{
876251881Speter  apr_hash_index_t *hi;
877251881Speter  svn_revnum_t yc_revision = SVN_INVALID_REVNUM;
878251881Speter  const char *yc_relpath = NULL;
879251881Speter
880251881Speter  if (strcmp(loc1->repos_root_url, loc2->repos_root_url) != 0)
881251881Speter    {
882251881Speter      *ancestor_p = NULL;
883251881Speter      return SVN_NO_ERROR;
884251881Speter    }
885251881Speter
886251881Speter  /* Loop through the first location's history, check for overlapping
887251881Speter     paths and ranges in the second location's history, and
888251881Speter     remembering the youngest matching location. */
889251881Speter  for (hi = apr_hash_first(scratch_pool, history1); hi; hi = apr_hash_next(hi))
890251881Speter    {
891251881Speter      const char *path = svn__apr_hash_index_key(hi);
892251881Speter      apr_ssize_t path_len = svn__apr_hash_index_klen(hi);
893251881Speter      svn_rangelist_t *ranges1 = svn__apr_hash_index_val(hi);
894251881Speter      svn_rangelist_t *ranges2, *common;
895251881Speter
896251881Speter      ranges2 = apr_hash_get(history2, path, path_len);
897251881Speter      if (ranges2)
898251881Speter        {
899251881Speter          /* We have a path match.  Now, did our two histories share
900251881Speter             any revisions at that path? */
901251881Speter          SVN_ERR(svn_rangelist_intersect(&common, ranges1, ranges2,
902251881Speter                                          TRUE, scratch_pool));
903251881Speter          if (common->nelts)
904251881Speter            {
905251881Speter              svn_merge_range_t *yc_range =
906251881Speter                APR_ARRAY_IDX(common, common->nelts - 1, svn_merge_range_t *);
907251881Speter              if ((! SVN_IS_VALID_REVNUM(yc_revision))
908251881Speter                  || (yc_range->end > yc_revision))
909251881Speter                {
910251881Speter                  yc_revision = yc_range->end;
911251881Speter                  yc_relpath = path + 1;
912251881Speter                }
913251881Speter            }
914251881Speter        }
915251881Speter    }
916251881Speter
917251881Speter  /* It's possible that PATH_OR_URL1 and PATH_OR_URL2's only common
918251881Speter     history is revision 0. */
919251881Speter  if (!yc_relpath && has_rev_zero_history1 && has_rev_zero_history2)
920251881Speter    {
921251881Speter      yc_relpath = "";
922251881Speter      yc_revision = 0;
923251881Speter    }
924251881Speter
925251881Speter  if (yc_relpath)
926251881Speter    {
927251881Speter      *ancestor_p = svn_client__pathrev_create_with_relpath(
928251881Speter                      loc1->repos_root_url, loc1->repos_uuid,
929251881Speter                      yc_revision, yc_relpath, result_pool);
930251881Speter    }
931251881Speter  else
932251881Speter    {
933251881Speter      *ancestor_p = NULL;
934251881Speter    }
935251881Speter  return SVN_NO_ERROR;
936251881Speter}
937251881Speter
938251881Spetersvn_error_t *
939253734Spetersvn_client__get_youngest_common_ancestor(svn_client__pathrev_t **ancestor_p,
940253734Speter                                         const svn_client__pathrev_t *loc1,
941253734Speter                                         const svn_client__pathrev_t *loc2,
942253734Speter                                         svn_ra_session_t *session,
943253734Speter                                         svn_client_ctx_t *ctx,
944253734Speter                                         apr_pool_t *result_pool,
945253734Speter                                         apr_pool_t *scratch_pool)
946253734Speter{
947253734Speter  apr_pool_t *sesspool = NULL;
948253734Speter  apr_hash_t *history1, *history2;
949253734Speter  svn_boolean_t has_rev_zero_history1;
950253734Speter  svn_boolean_t has_rev_zero_history2;
951253734Speter
952253734Speter  if (strcmp(loc1->repos_root_url, loc2->repos_root_url) != 0)
953253734Speter    {
954253734Speter      *ancestor_p = NULL;
955253734Speter      return SVN_NO_ERROR;
956253734Speter    }
957253734Speter
958253734Speter  /* Open an RA session for the two locations. */
959253734Speter  if (session == NULL)
960253734Speter    {
961253734Speter      sesspool = svn_pool_create(scratch_pool);
962253734Speter      SVN_ERR(svn_client_open_ra_session2(&session, loc1->url, NULL, ctx,
963253734Speter                                          sesspool, sesspool));
964253734Speter    }
965253734Speter
966253734Speter  /* We're going to cheat and use history-as-mergeinfo because it
967253734Speter     saves us a bunch of annoying custom data comparisons and such. */
968253734Speter  SVN_ERR(svn_client__get_history_as_mergeinfo(&history1,
969253734Speter                                               &has_rev_zero_history1,
970253734Speter                                               loc1,
971253734Speter                                               SVN_INVALID_REVNUM,
972253734Speter                                               SVN_INVALID_REVNUM,
973253734Speter                                               session, ctx, scratch_pool));
974253734Speter  SVN_ERR(svn_client__get_history_as_mergeinfo(&history2,
975253734Speter                                               &has_rev_zero_history2,
976253734Speter                                               loc2,
977253734Speter                                               SVN_INVALID_REVNUM,
978253734Speter                                               SVN_INVALID_REVNUM,
979253734Speter                                               session, ctx, scratch_pool));
980253734Speter  /* Close the ra session if we opened one. */
981253734Speter  if (sesspool)
982253734Speter    svn_pool_destroy(sesspool);
983253734Speter
984253734Speter  SVN_ERR(svn_client__calc_youngest_common_ancestor(ancestor_p,
985253734Speter                                                    loc1, history1,
986253734Speter                                                    has_rev_zero_history1,
987253734Speter                                                    loc2, history2,
988253734Speter                                                    has_rev_zero_history2,
989253734Speter                                                    result_pool,
990253734Speter                                                    scratch_pool));
991253734Speter
992253734Speter  return SVN_NO_ERROR;
993253734Speter}
994253734Speter
995253734Spetersvn_error_t *
996251881Spetersvn_client__youngest_common_ancestor(const char **ancestor_url,
997251881Speter                                     svn_revnum_t *ancestor_rev,
998251881Speter                                     const char *path_or_url1,
999251881Speter                                     const svn_opt_revision_t *revision1,
1000251881Speter                                     const char *path_or_url2,
1001251881Speter                                     const svn_opt_revision_t *revision2,
1002251881Speter                                     svn_client_ctx_t *ctx,
1003251881Speter                                     apr_pool_t *result_pool,
1004251881Speter                                     apr_pool_t *scratch_pool)
1005251881Speter{
1006251881Speter  apr_pool_t *sesspool = svn_pool_create(scratch_pool);
1007251881Speter  svn_ra_session_t *session;
1008251881Speter  svn_client__pathrev_t *loc1, *loc2, *ancestor;
1009251881Speter
1010251881Speter  /* Resolve the two locations */
1011251881Speter  SVN_ERR(svn_client__ra_session_from_path2(&session, &loc1,
1012251881Speter                                            path_or_url1, NULL,
1013251881Speter                                            revision1, revision1,
1014251881Speter                                            ctx, sesspool));
1015251881Speter  SVN_ERR(svn_client__resolve_rev_and_url(&loc2, session,
1016251881Speter                                          path_or_url2, revision2, revision2,
1017251881Speter                                          ctx, scratch_pool));
1018251881Speter
1019251881Speter  SVN_ERR(svn_client__get_youngest_common_ancestor(
1020251881Speter            &ancestor, loc1, loc2, session, ctx, result_pool, scratch_pool));
1021251881Speter
1022251881Speter  if (ancestor)
1023251881Speter    {
1024251881Speter      *ancestor_url = ancestor->url;
1025251881Speter      *ancestor_rev = ancestor->rev;
1026251881Speter    }
1027251881Speter  else
1028251881Speter    {
1029251881Speter      *ancestor_url = NULL;
1030251881Speter      *ancestor_rev = SVN_INVALID_REVNUM;
1031251881Speter    }
1032251881Speter  svn_pool_destroy(sesspool);
1033251881Speter  return SVN_NO_ERROR;
1034251881Speter}
1035251881Speter
1036251881Speter
1037251881Speterstruct ra_ev2_baton {
1038251881Speter  /* The working copy context, from the client context.  */
1039251881Speter  svn_wc_context_t *wc_ctx;
1040251881Speter
1041251881Speter  /* For a given REPOS_RELPATH, provide a LOCAL_ABSPATH that represents
1042251881Speter     that repository node.  */
1043251881Speter  apr_hash_t *relpath_map;
1044251881Speter};
1045251881Speter
1046251881Speter
1047251881Spetersvn_error_t *
1048251881Spetersvn_client__ra_provide_base(svn_stream_t **contents,
1049251881Speter                            svn_revnum_t *revision,
1050251881Speter                            void *baton,
1051251881Speter                            const char *repos_relpath,
1052251881Speter                            apr_pool_t *result_pool,
1053251881Speter                            apr_pool_t *scratch_pool)
1054251881Speter{
1055251881Speter  struct ra_ev2_baton *reb = baton;
1056251881Speter  const char *local_abspath;
1057251881Speter  svn_error_t *err;
1058251881Speter
1059251881Speter  local_abspath = svn_hash_gets(reb->relpath_map, repos_relpath);
1060251881Speter  if (!local_abspath)
1061251881Speter    {
1062251881Speter      *contents = NULL;
1063251881Speter      return SVN_NO_ERROR;
1064251881Speter    }
1065251881Speter
1066251881Speter  err = svn_wc_get_pristine_contents2(contents, reb->wc_ctx, local_abspath,
1067251881Speter                                      result_pool, scratch_pool);
1068251881Speter  if (err)
1069251881Speter    {
1070251881Speter      if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
1071251881Speter        return svn_error_trace(err);
1072251881Speter
1073251881Speter      svn_error_clear(err);
1074251881Speter      *contents = NULL;
1075251881Speter      return SVN_NO_ERROR;
1076251881Speter    }
1077251881Speter
1078251881Speter  if (*contents != NULL)
1079251881Speter    {
1080251881Speter      /* The pristine contents refer to the BASE, or to the pristine of
1081251881Speter         a copy/move to this location. Fetch the correct revision.  */
1082251881Speter      SVN_ERR(svn_wc__node_get_origin(NULL, revision, NULL, NULL, NULL, NULL,
1083251881Speter                                      reb->wc_ctx, local_abspath, FALSE,
1084251881Speter                                      scratch_pool, scratch_pool));
1085251881Speter    }
1086251881Speter
1087251881Speter  return SVN_NO_ERROR;
1088251881Speter}
1089251881Speter
1090251881Speter
1091251881Spetersvn_error_t *
1092251881Spetersvn_client__ra_provide_props(apr_hash_t **props,
1093251881Speter                             svn_revnum_t *revision,
1094251881Speter                             void *baton,
1095251881Speter                             const char *repos_relpath,
1096251881Speter                             apr_pool_t *result_pool,
1097251881Speter                             apr_pool_t *scratch_pool)
1098251881Speter{
1099251881Speter  struct ra_ev2_baton *reb = baton;
1100251881Speter  const char *local_abspath;
1101251881Speter  svn_error_t *err;
1102251881Speter
1103251881Speter  local_abspath = svn_hash_gets(reb->relpath_map, repos_relpath);
1104251881Speter  if (!local_abspath)
1105251881Speter    {
1106251881Speter      *props = NULL;
1107251881Speter      return SVN_NO_ERROR;
1108251881Speter    }
1109251881Speter
1110251881Speter  err = svn_wc_get_pristine_props(props, reb->wc_ctx, local_abspath,
1111251881Speter                                  result_pool, scratch_pool);
1112251881Speter  if (err)
1113251881Speter    {
1114251881Speter      if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
1115251881Speter        return svn_error_trace(err);
1116251881Speter
1117251881Speter      svn_error_clear(err);
1118251881Speter      *props = NULL;
1119251881Speter      return SVN_NO_ERROR;
1120251881Speter    }
1121251881Speter
1122251881Speter  if (*props != NULL)
1123251881Speter    {
1124251881Speter      /* The pristine props refer to the BASE, or to the pristine props of
1125251881Speter         a copy/move to this location. Fetch the correct revision.  */
1126251881Speter      SVN_ERR(svn_wc__node_get_origin(NULL, revision, NULL, NULL, NULL, NULL,
1127251881Speter                                      reb->wc_ctx, local_abspath, FALSE,
1128251881Speter                                      scratch_pool, scratch_pool));
1129251881Speter    }
1130251881Speter
1131251881Speter  return SVN_NO_ERROR;
1132251881Speter}
1133251881Speter
1134251881Speter
1135251881Spetersvn_error_t *
1136251881Spetersvn_client__ra_get_copysrc_kind(svn_node_kind_t *kind,
1137251881Speter                                void *baton,
1138251881Speter                                const char *repos_relpath,
1139251881Speter                                svn_revnum_t src_revision,
1140251881Speter                                apr_pool_t *scratch_pool)
1141251881Speter{
1142251881Speter  struct ra_ev2_baton *reb = baton;
1143251881Speter  const char *local_abspath;
1144251881Speter
1145251881Speter  local_abspath = svn_hash_gets(reb->relpath_map, repos_relpath);
1146251881Speter  if (!local_abspath)
1147251881Speter    {
1148251881Speter      *kind = svn_node_unknown;
1149251881Speter      return SVN_NO_ERROR;
1150251881Speter    }
1151251881Speter
1152251881Speter  /* ### what to do with SRC_REVISION?  */
1153251881Speter
1154251881Speter  SVN_ERR(svn_wc_read_kind2(kind, reb->wc_ctx, local_abspath,
1155251881Speter                            FALSE, FALSE, scratch_pool));
1156251881Speter
1157251881Speter  return SVN_NO_ERROR;
1158251881Speter}
1159251881Speter
1160251881Speter
1161251881Spetervoid *
1162251881Spetersvn_client__ra_make_cb_baton(svn_wc_context_t *wc_ctx,
1163251881Speter                             apr_hash_t *relpath_map,
1164251881Speter                             apr_pool_t *result_pool)
1165251881Speter{
1166251881Speter  struct ra_ev2_baton *reb = apr_palloc(result_pool, sizeof(*reb));
1167251881Speter
1168251881Speter  SVN_ERR_ASSERT_NO_RETURN(wc_ctx != NULL);
1169251881Speter  SVN_ERR_ASSERT_NO_RETURN(relpath_map != NULL);
1170251881Speter
1171251881Speter  reb->wc_ctx = wc_ctx;
1172251881Speter  reb->relpath_map = relpath_map;
1173251881Speter
1174251881Speter  return reb;
1175251881Speter}
1176