1251881Speter/*
2251881Speter * status.c:  return the status of a working copy dirent
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter/* ==================================================================== */
25251881Speter
26251881Speter
27251881Speter
28251881Speter/*** Includes. ***/
29251881Speter#include <apr_strings.h>
30251881Speter#include <apr_pools.h>
31251881Speter
32299742Sdim#include "svn_private_config.h"
33251881Speter#include "svn_pools.h"
34299742Sdim#include "svn_sorts.h"
35251881Speter#include "client.h"
36251881Speter
37251881Speter#include "svn_path.h"
38251881Speter#include "svn_dirent_uri.h"
39251881Speter#include "svn_delta.h"
40251881Speter#include "svn_client.h"
41251881Speter#include "svn_error.h"
42251881Speter#include "svn_hash.h"
43251881Speter
44299742Sdim#include "private/svn_client_private.h"
45299742Sdim#include "private/svn_sorts_private.h"
46251881Speter#include "private/svn_wc_private.h"
47251881Speter
48251881Speter
49251881Speter/*** Getting update information ***/
50251881Speter
51251881Speter/* Baton for tweak_status.  It wraps a bit of extra functionality
52251881Speter   around the received status func/baton, so we can remember if the
53251881Speter   target was deleted in HEAD and tweak incoming status structures
54251881Speter   accordingly. */
55251881Speterstruct status_baton
56251881Speter{
57251881Speter  svn_boolean_t deleted_in_repos;             /* target is deleted in repos */
58251881Speter  apr_hash_t *changelist_hash;                /* keys are changelist names */
59251881Speter  svn_client_status_func_t real_status_func;  /* real status function */
60251881Speter  void *real_status_baton;                    /* real status baton */
61251881Speter  const char *anchor_abspath;                 /* Absolute path of anchor */
62251881Speter  const char *anchor_relpath;                 /* Relative path of anchor */
63251881Speter  svn_wc_context_t *wc_ctx;                   /* A working copy context. */
64251881Speter};
65251881Speter
66251881Speter/* A status callback function which wraps the *real* status
67251881Speter   function/baton.   This sucker takes care of any status tweaks we
68251881Speter   need to make (such as noting that the target of the status is
69251881Speter   missing from HEAD in the repository).
70251881Speter
71251881Speter   This implements the 'svn_wc_status_func4_t' function type.  */
72251881Speterstatic svn_error_t *
73251881Spetertweak_status(void *baton,
74251881Speter             const char *local_abspath,
75251881Speter             const svn_wc_status3_t *status,
76251881Speter             apr_pool_t *scratch_pool)
77251881Speter{
78251881Speter  struct status_baton *sb = baton;
79251881Speter  const char *path = local_abspath;
80251881Speter  svn_client_status_t *cst;
81251881Speter
82251881Speter  if (sb->anchor_abspath)
83251881Speter    path = svn_dirent_join(sb->anchor_relpath,
84251881Speter                           svn_dirent_skip_ancestor(sb->anchor_abspath, path),
85251881Speter                           scratch_pool);
86251881Speter
87251881Speter  /* If the status item has an entry, but doesn't belong to one of the
88251881Speter     changelists our caller is interested in, we filter out this status
89251881Speter     transmission.  */
90251881Speter  if (sb->changelist_hash
91251881Speter      && (! status->changelist
92251881Speter          || ! svn_hash_gets(sb->changelist_hash, status->changelist)))
93251881Speter    {
94251881Speter      return SVN_NO_ERROR;
95251881Speter    }
96251881Speter
97251881Speter  /* If we know that the target was deleted in HEAD of the repository,
98251881Speter     we need to note that fact in all the status structures that come
99251881Speter     through here. */
100251881Speter  if (sb->deleted_in_repos)
101251881Speter    {
102251881Speter      svn_wc_status3_t *new_status = svn_wc_dup_status3(status, scratch_pool);
103251881Speter      new_status->repos_node_status = svn_wc_status_deleted;
104251881Speter      status = new_status;
105251881Speter    }
106251881Speter
107251881Speter  SVN_ERR(svn_client__create_status(&cst, sb->wc_ctx, local_abspath, status,
108251881Speter                                    scratch_pool, scratch_pool));
109251881Speter
110251881Speter  /* Call the real status function/baton. */
111251881Speter  return sb->real_status_func(sb->real_status_baton, path, cst,
112251881Speter                              scratch_pool);
113251881Speter}
114251881Speter
115251881Speter/* A baton for our reporter that is used to collect locks. */
116251881Spetertypedef struct report_baton_t {
117251881Speter  const svn_ra_reporter3_t* wrapped_reporter;
118251881Speter  void *wrapped_report_baton;
119251881Speter  /* The common ancestor URL of all paths included in the report. */
120251881Speter  char *ancestor;
121251881Speter  void *set_locks_baton;
122251881Speter  svn_depth_t depth;
123251881Speter  svn_client_ctx_t *ctx;
124251881Speter  /* Pool to store locks in. */
125251881Speter  apr_pool_t *pool;
126251881Speter} report_baton_t;
127251881Speter
128251881Speter/* Implements svn_ra_reporter3_t->set_path. */
129251881Speterstatic svn_error_t *
130251881Speterreporter_set_path(void *report_baton, const char *path,
131251881Speter                  svn_revnum_t revision, svn_depth_t depth,
132251881Speter                  svn_boolean_t start_empty, const char *lock_token,
133251881Speter                  apr_pool_t *pool)
134251881Speter{
135251881Speter  report_baton_t *rb = report_baton;
136251881Speter
137251881Speter  return rb->wrapped_reporter->set_path(rb->wrapped_report_baton, path,
138251881Speter                                        revision, depth, start_empty,
139251881Speter                                        lock_token, pool);
140251881Speter}
141251881Speter
142251881Speter/* Implements svn_ra_reporter3_t->delete_path. */
143251881Speterstatic svn_error_t *
144251881Speterreporter_delete_path(void *report_baton, const char *path, apr_pool_t *pool)
145251881Speter{
146251881Speter  report_baton_t *rb = report_baton;
147251881Speter
148251881Speter  return rb->wrapped_reporter->delete_path(rb->wrapped_report_baton, path,
149251881Speter                                           pool);
150251881Speter}
151251881Speter
152251881Speter/* Implements svn_ra_reporter3_t->link_path. */
153251881Speterstatic svn_error_t *
154251881Speterreporter_link_path(void *report_baton, const char *path, const char *url,
155251881Speter                   svn_revnum_t revision, svn_depth_t depth,
156251881Speter                   svn_boolean_t start_empty,
157251881Speter                   const char *lock_token, apr_pool_t *pool)
158251881Speter{
159251881Speter  report_baton_t *rb = report_baton;
160251881Speter
161251881Speter  if (!svn_uri__is_ancestor(rb->ancestor, url))
162251881Speter    {
163251881Speter      const char *ancestor;
164251881Speter
165251881Speter      ancestor = svn_uri_get_longest_ancestor(url, rb->ancestor, pool);
166251881Speter
167251881Speter      /* If we got a shorter ancestor, truncate our current ancestor.
168251881Speter         Note that svn_uri_get_longest_ancestor will allocate its return
169251881Speter         value even if it identical to one of its arguments. */
170251881Speter
171251881Speter      rb->ancestor[strlen(ancestor)] = '\0';
172251881Speter      rb->depth = svn_depth_infinity;
173251881Speter    }
174251881Speter
175251881Speter  return rb->wrapped_reporter->link_path(rb->wrapped_report_baton, path, url,
176251881Speter                                         revision, depth, start_empty,
177251881Speter                                         lock_token, pool);
178251881Speter}
179251881Speter
180251881Speter/* Implements svn_ra_reporter3_t->finish_report. */
181251881Speterstatic svn_error_t *
182251881Speterreporter_finish_report(void *report_baton, apr_pool_t *pool)
183251881Speter{
184251881Speter  report_baton_t *rb = report_baton;
185251881Speter  svn_ra_session_t *ras;
186251881Speter  apr_hash_t *locks;
187251881Speter  const char *repos_root;
188251881Speter  apr_pool_t *subpool = svn_pool_create(pool);
189251881Speter  svn_error_t *err = SVN_NO_ERROR;
190251881Speter
191251881Speter  /* Open an RA session to our common ancestor and grab the locks under it.
192251881Speter   */
193251881Speter  SVN_ERR(svn_client_open_ra_session2(&ras, rb->ancestor, NULL,
194251881Speter                                      rb->ctx, subpool, subpool));
195251881Speter
196251881Speter  /* The locks need to live throughout the edit.  Note that if the
197251881Speter     server doesn't support lock discovery, we'll just not do locky
198251881Speter     stuff. */
199251881Speter  err = svn_ra_get_locks2(ras, &locks, "", rb->depth, rb->pool);
200299742Sdim  if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)
201251881Speter    {
202251881Speter      svn_error_clear(err);
203251881Speter      err = SVN_NO_ERROR;
204251881Speter      locks = apr_hash_make(rb->pool);
205251881Speter    }
206251881Speter  SVN_ERR(err);
207251881Speter
208251881Speter  SVN_ERR(svn_ra_get_repos_root2(ras, &repos_root, rb->pool));
209251881Speter
210251881Speter  /* Close the RA session. */
211251881Speter  svn_pool_destroy(subpool);
212251881Speter
213251881Speter  SVN_ERR(svn_wc_status_set_repos_locks(rb->set_locks_baton, locks,
214251881Speter                                        repos_root, rb->pool));
215251881Speter
216251881Speter  return rb->wrapped_reporter->finish_report(rb->wrapped_report_baton, pool);
217251881Speter}
218251881Speter
219251881Speter/* Implements svn_ra_reporter3_t->abort_report. */
220251881Speterstatic svn_error_t *
221251881Speterreporter_abort_report(void *report_baton, apr_pool_t *pool)
222251881Speter{
223251881Speter  report_baton_t *rb = report_baton;
224251881Speter
225251881Speter  return rb->wrapped_reporter->abort_report(rb->wrapped_report_baton, pool);
226251881Speter}
227251881Speter
228251881Speter/* A reporter that keeps track of the common URL ancestor of all paths in
229251881Speter   the WC and fetches repository locks for all paths under this ancestor. */
230251881Speterstatic svn_ra_reporter3_t lock_fetch_reporter = {
231251881Speter  reporter_set_path,
232251881Speter  reporter_delete_path,
233251881Speter  reporter_link_path,
234251881Speter  reporter_finish_report,
235251881Speter  reporter_abort_report
236251881Speter};
237251881Speter
238251881Speter/* Perform status operations on each external in EXTERNAL_MAP, a const char *
239251881Speter   local_abspath of all externals mapping to the const char* defining_abspath.
240251881Speter   All other options are the same as those passed to svn_client_status().
241251881Speter
242251881Speter   If ANCHOR_ABSPATH and ANCHOR-RELPATH are not null, use them to provide
243251881Speter   properly formatted relative paths */
244251881Speterstatic svn_error_t *
245251881Speterdo_external_status(svn_client_ctx_t *ctx,
246251881Speter                   apr_hash_t *external_map,
247251881Speter                   svn_depth_t depth,
248251881Speter                   svn_boolean_t get_all,
249299742Sdim                   svn_boolean_t check_out_of_date,
250299742Sdim                   svn_boolean_t check_working_copy,
251251881Speter                   svn_boolean_t no_ignore,
252299742Sdim                   const apr_array_header_t *changelists,
253251881Speter                   const char *anchor_abspath,
254251881Speter                   const char *anchor_relpath,
255251881Speter                   svn_client_status_func_t status_func,
256251881Speter                   void *status_baton,
257251881Speter                   apr_pool_t *scratch_pool)
258251881Speter{
259251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
260299742Sdim  apr_array_header_t *externals;
261299742Sdim  int i;
262251881Speter
263299742Sdim  externals = svn_sort__hash(external_map, svn_sort_compare_items_lexically,
264299742Sdim                             scratch_pool);
265299742Sdim
266251881Speter  /* Loop over the hash of new values (we don't care about the old
267251881Speter     ones).  This is a mapping of versioned directories to property
268251881Speter     values. */
269299742Sdim  for (i = 0; i < externals->nelts; i++)
270251881Speter    {
271251881Speter      svn_node_kind_t external_kind;
272299742Sdim      svn_sort__item_t item = APR_ARRAY_IDX(externals, i, svn_sort__item_t);
273299742Sdim      const char *local_abspath = item.key;
274299742Sdim      const char *defining_abspath = item.value;
275251881Speter      svn_node_kind_t kind;
276251881Speter      svn_opt_revision_t opt_rev;
277251881Speter      const char *status_path;
278251881Speter
279251881Speter      svn_pool_clear(iterpool);
280251881Speter
281251881Speter      /* Obtain information on the expected external. */
282251881Speter      SVN_ERR(svn_wc__read_external_info(&external_kind, NULL, NULL, NULL,
283251881Speter                                         &opt_rev.value.number,
284251881Speter                                         ctx->wc_ctx, defining_abspath,
285251881Speter                                         local_abspath, FALSE,
286251881Speter                                         iterpool, iterpool));
287251881Speter
288251881Speter      if (external_kind != svn_node_dir)
289251881Speter        continue;
290251881Speter
291251881Speter      SVN_ERR(svn_io_check_path(local_abspath, &kind, iterpool));
292251881Speter      if (kind != svn_node_dir)
293251881Speter        continue;
294251881Speter
295251881Speter      if (SVN_IS_VALID_REVNUM(opt_rev.value.number))
296251881Speter        opt_rev.kind = svn_opt_revision_number;
297251881Speter      else
298251881Speter        opt_rev.kind = svn_opt_revision_unspecified;
299251881Speter
300251881Speter      /* Tell the client we're starting an external status set. */
301251881Speter      if (ctx->notify_func2)
302251881Speter        ctx->notify_func2(
303251881Speter               ctx->notify_baton2,
304251881Speter               svn_wc_create_notify(local_abspath,
305251881Speter                                    svn_wc_notify_status_external,
306251881Speter                                    iterpool), iterpool);
307251881Speter
308251881Speter      status_path = local_abspath;
309251881Speter      if (anchor_abspath)
310251881Speter        {
311251881Speter          status_path = svn_dirent_join(anchor_relpath,
312251881Speter                           svn_dirent_skip_ancestor(anchor_abspath,
313251881Speter                                                    status_path),
314251881Speter                           iterpool);
315251881Speter        }
316251881Speter
317251881Speter      /* And then do the status. */
318299742Sdim      SVN_ERR(svn_client_status6(NULL, ctx, status_path, &opt_rev, depth,
319299742Sdim                                 get_all, check_out_of_date,
320299742Sdim                                 check_working_copy, no_ignore,
321299742Sdim                                 FALSE /* ignore_exernals */,
322299742Sdim                                 FALSE /* depth_as_sticky */,
323299742Sdim                                 changelists, status_func, status_baton,
324251881Speter                                 iterpool));
325251881Speter    }
326251881Speter
327251881Speter  /* Destroy SUBPOOL and (implicitly) ITERPOOL. */
328251881Speter  svn_pool_destroy(iterpool);
329251881Speter
330251881Speter  return SVN_NO_ERROR;
331251881Speter}
332251881Speter
333251881Speter/*** Public Interface. ***/
334251881Speter
335251881Speter
336251881Spetersvn_error_t *
337299742Sdimsvn_client_status6(svn_revnum_t *result_rev,
338251881Speter                   svn_client_ctx_t *ctx,
339251881Speter                   const char *path,
340251881Speter                   const svn_opt_revision_t *revision,
341251881Speter                   svn_depth_t depth,
342251881Speter                   svn_boolean_t get_all,
343299742Sdim                   svn_boolean_t check_out_of_date,
344299742Sdim                   svn_boolean_t check_working_copy,
345251881Speter                   svn_boolean_t no_ignore,
346251881Speter                   svn_boolean_t ignore_externals,
347251881Speter                   svn_boolean_t depth_as_sticky,
348251881Speter                   const apr_array_header_t *changelists,
349251881Speter                   svn_client_status_func_t status_func,
350251881Speter                   void *status_baton,
351251881Speter                   apr_pool_t *pool)  /* ### aka scratch_pool */
352251881Speter{
353251881Speter  struct status_baton sb;
354251881Speter  const char *dir, *dir_abspath;
355251881Speter  const char *target_abspath;
356251881Speter  const char *target_basename;
357251881Speter  apr_array_header_t *ignores;
358251881Speter  svn_error_t *err;
359251881Speter  apr_hash_t *changelist_hash = NULL;
360251881Speter
361299742Sdim  /* Override invalid combinations of the check_out_of_date and
362299742Sdim     check_working_copy flags. */
363299742Sdim  if (!check_out_of_date)
364299742Sdim    check_working_copy = TRUE;
365299742Sdim
366251881Speter  if (svn_path_is_url(path))
367251881Speter    return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
368251881Speter                             _("'%s' is not a local path"), path);
369251881Speter
370251881Speter  if (changelists && changelists->nelts)
371251881Speter    SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelists, pool));
372251881Speter
373251881Speter  if (result_rev)
374251881Speter    *result_rev = SVN_INVALID_REVNUM;
375251881Speter
376251881Speter  sb.real_status_func = status_func;
377251881Speter  sb.real_status_baton = status_baton;
378251881Speter  sb.deleted_in_repos = FALSE;
379251881Speter  sb.changelist_hash = changelist_hash;
380251881Speter  sb.wc_ctx = ctx->wc_ctx;
381251881Speter
382251881Speter  SVN_ERR(svn_dirent_get_absolute(&target_abspath, path, pool));
383251881Speter
384299742Sdim  if (check_out_of_date)
385251881Speter    {
386251881Speter      /* The status editor only works on directories, so get the ancestor
387251881Speter         if necessary */
388251881Speter
389251881Speter      svn_node_kind_t kind;
390251881Speter
391251881Speter      SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, target_abspath,
392251881Speter                                TRUE, FALSE, pool));
393251881Speter
394251881Speter      /* Dir must be a working copy directory or the status editor fails */
395251881Speter      if (kind == svn_node_dir)
396251881Speter        {
397251881Speter          dir_abspath = target_abspath;
398251881Speter          target_basename = "";
399251881Speter          dir = path;
400251881Speter        }
401251881Speter      else
402251881Speter        {
403251881Speter          dir_abspath = svn_dirent_dirname(target_abspath, pool);
404251881Speter          target_basename = svn_dirent_basename(target_abspath, NULL);
405251881Speter          dir = svn_dirent_dirname(path, pool);
406251881Speter
407251881Speter          if (kind == svn_node_file)
408251881Speter            {
409251881Speter              if (depth == svn_depth_empty)
410251881Speter                depth = svn_depth_files;
411251881Speter            }
412251881Speter          else
413251881Speter            {
414251881Speter              err = svn_wc_read_kind2(&kind, ctx->wc_ctx, dir_abspath,
415251881Speter                                      FALSE, FALSE, pool);
416251881Speter
417251881Speter              svn_error_clear(err);
418251881Speter
419251881Speter              if (err || kind != svn_node_dir)
420251881Speter                {
421251881Speter                  return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
422251881Speter                                           _("'%s' is not a working copy"),
423251881Speter                                           svn_dirent_local_style(path, pool));
424251881Speter                }
425251881Speter            }
426251881Speter        }
427251881Speter    }
428251881Speter  else
429251881Speter    {
430251881Speter      dir = path;
431251881Speter      dir_abspath = target_abspath;
432251881Speter    }
433251881Speter
434251881Speter  if (svn_dirent_is_absolute(dir))
435251881Speter    {
436251881Speter      sb.anchor_abspath = NULL;
437251881Speter      sb.anchor_relpath = NULL;
438251881Speter    }
439251881Speter  else
440251881Speter    {
441251881Speter      sb.anchor_abspath = dir_abspath;
442251881Speter      sb.anchor_relpath = dir;
443251881Speter    }
444251881Speter
445251881Speter  /* Get the status edit, and use our wrapping status function/baton
446251881Speter     as the callback pair. */
447251881Speter  SVN_ERR(svn_wc_get_default_ignores(&ignores, ctx->config, pool));
448251881Speter
449251881Speter  /* If we want to know about out-of-dateness, we crawl the working copy and
450251881Speter     let the RA layer drive the editor for real.  Otherwise, we just close the
451251881Speter     edit.  :-) */
452299742Sdim  if (check_out_of_date)
453251881Speter    {
454251881Speter      svn_ra_session_t *ra_session;
455251881Speter      const char *URL;
456251881Speter      svn_node_kind_t kind;
457251881Speter      svn_boolean_t server_supports_depth;
458251881Speter      const svn_delta_editor_t *editor;
459251881Speter      void *edit_baton, *set_locks_baton;
460251881Speter      svn_revnum_t edit_revision = SVN_INVALID_REVNUM;
461251881Speter
462251881Speter      /* Get full URL from the ANCHOR. */
463251881Speter      SVN_ERR(svn_client_url_from_path2(&URL, dir_abspath, ctx,
464251881Speter                                        pool, pool));
465251881Speter
466251881Speter      if (!URL)
467251881Speter        return svn_error_createf
468251881Speter          (SVN_ERR_ENTRY_MISSING_URL, NULL,
469251881Speter           _("Entry '%s' has no URL"),
470251881Speter           svn_dirent_local_style(dir, pool));
471251881Speter
472251881Speter      /* Open a repository session to the URL. */
473251881Speter      SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, URL,
474251881Speter                                                   dir_abspath, NULL,
475251881Speter                                                   FALSE, TRUE,
476251881Speter                                                   ctx, pool, pool));
477251881Speter
478251881Speter      SVN_ERR(svn_ra_has_capability(ra_session, &server_supports_depth,
479251881Speter                                    SVN_RA_CAPABILITY_DEPTH, pool));
480251881Speter
481251881Speter      SVN_ERR(svn_wc__get_status_editor(&editor, &edit_baton, &set_locks_baton,
482299742Sdim                                        &edit_revision, ctx->wc_ctx,
483299742Sdim                                        dir_abspath, target_basename,
484299742Sdim                                        depth, get_all, check_working_copy,
485299742Sdim                                        no_ignore, depth_as_sticky,
486299742Sdim                                        server_supports_depth,
487299742Sdim                                        ignores, tweak_status, &sb,
488299742Sdim                                        ctx->cancel_func, ctx->cancel_baton,
489299742Sdim                                        pool, pool));
490251881Speter
491251881Speter
492251881Speter      /* Verify that URL exists in HEAD.  If it doesn't, this can save
493251881Speter         us a whole lot of hassle; if it does, the cost of this
494251881Speter         request should be minimal compared to the size of getting
495251881Speter         back the average amount of "out-of-date" information. */
496251881Speter      SVN_ERR(svn_ra_check_path(ra_session, "", SVN_INVALID_REVNUM,
497251881Speter                                &kind, pool));
498251881Speter      if (kind == svn_node_none)
499251881Speter        {
500251881Speter          svn_boolean_t added;
501251881Speter
502251881Speter          /* Our status target does not exist in HEAD.  If we've got
503251881Speter             it locally added, that's okay.  But if it was previously
504251881Speter             versioned, then it must have since been deleted from the
505251881Speter             repository.  (Note that "locally replaced" doesn't count
506251881Speter             as "added" in this case.)  */
507251881Speter          SVN_ERR(svn_wc__node_is_added(&added, ctx->wc_ctx,
508251881Speter                                        dir_abspath, pool));
509251881Speter          if (! added)
510251881Speter            sb.deleted_in_repos = TRUE;
511251881Speter
512251881Speter          /* And now close the edit. */
513251881Speter          SVN_ERR(editor->close_edit(edit_baton, pool));
514251881Speter        }
515251881Speter      else
516251881Speter        {
517251881Speter          svn_revnum_t revnum;
518251881Speter          report_baton_t rb;
519251881Speter          svn_depth_t status_depth;
520251881Speter
521251881Speter          if (revision->kind == svn_opt_revision_head)
522251881Speter            {
523251881Speter              /* Cause the revision number to be omitted from the request,
524251881Speter                 which implies HEAD. */
525251881Speter              revnum = SVN_INVALID_REVNUM;
526251881Speter            }
527251881Speter          else
528251881Speter            {
529251881Speter              /* Get a revision number for our status operation. */
530251881Speter              SVN_ERR(svn_client__get_revision_number(&revnum, NULL,
531251881Speter                                                      ctx->wc_ctx,
532251881Speter                                                      target_abspath,
533251881Speter                                                      ra_session, revision,
534251881Speter                                                      pool));
535251881Speter            }
536251881Speter
537251881Speter          if (depth_as_sticky || !server_supports_depth)
538251881Speter            status_depth = depth;
539251881Speter          else
540251881Speter            status_depth = svn_depth_unknown; /* Use depth from WC */
541251881Speter
542251881Speter          /* Do the deed.  Let the RA layer drive the status editor. */
543251881Speter          SVN_ERR(svn_ra_do_status2(ra_session, &rb.wrapped_reporter,
544251881Speter                                    &rb.wrapped_report_baton,
545251881Speter                                    target_basename, revnum, status_depth,
546251881Speter                                    editor, edit_baton, pool));
547251881Speter
548251881Speter          /* Init the report baton. */
549251881Speter          rb.ancestor = apr_pstrdup(pool, URL); /* Edited later */
550251881Speter          rb.set_locks_baton = set_locks_baton;
551251881Speter          rb.ctx = ctx;
552251881Speter          rb.pool = pool;
553251881Speter
554251881Speter          if (depth == svn_depth_unknown)
555251881Speter            rb.depth = svn_depth_infinity;
556251881Speter          else
557251881Speter            rb.depth = depth;
558251881Speter
559251881Speter          /* Drive the reporter structure, describing the revisions
560251881Speter             within PATH.  When we call reporter->finish_report,
561251881Speter             EDITOR will be driven to describe differences between our
562251881Speter             working copy and HEAD. */
563251881Speter          SVN_ERR(svn_wc_crawl_revisions5(ctx->wc_ctx,
564251881Speter                                          target_abspath,
565251881Speter                                          &lock_fetch_reporter, &rb,
566251881Speter                                          FALSE /* restore_files */,
567251881Speter                                          depth, (! depth_as_sticky),
568251881Speter                                          (! server_supports_depth),
569251881Speter                                          FALSE /* use_commit_times */,
570251881Speter                                          ctx->cancel_func, ctx->cancel_baton,
571251881Speter                                          NULL, NULL, pool));
572251881Speter        }
573251881Speter
574251881Speter      if (ctx->notify_func2)
575251881Speter        {
576251881Speter          svn_wc_notify_t *notify
577251881Speter            = svn_wc_create_notify(target_abspath,
578251881Speter                                   svn_wc_notify_status_completed, pool);
579251881Speter          notify->revision = edit_revision;
580299742Sdim          ctx->notify_func2(ctx->notify_baton2, notify, pool);
581251881Speter        }
582251881Speter
583251881Speter      /* If the caller wants the result revision, give it to them. */
584251881Speter      if (result_rev)
585251881Speter        *result_rev = edit_revision;
586251881Speter    }
587251881Speter  else
588251881Speter    {
589251881Speter      err = svn_wc_walk_status(ctx->wc_ctx, target_abspath,
590251881Speter                               depth, get_all, no_ignore, FALSE, ignores,
591251881Speter                               tweak_status, &sb,
592251881Speter                               ctx->cancel_func, ctx->cancel_baton,
593251881Speter                               pool);
594251881Speter
595251881Speter      if (err && err->apr_err == SVN_ERR_WC_MISSING)
596251881Speter        {
597251881Speter          /* This error code is checked for in svn to continue after
598251881Speter             this error */
599251881Speter          svn_error_clear(err);
600251881Speter          return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
601251881Speter                               _("'%s' is not a working copy"),
602251881Speter                               svn_dirent_local_style(path, pool));
603251881Speter        }
604251881Speter
605251881Speter      SVN_ERR(err);
606251881Speter    }
607251881Speter
608299742Sdim  /* We only descend into an external if depth is svn_depth_infinity or
609251881Speter     svn_depth_unknown.  However, there are conceivable behaviors that
610251881Speter     would involve descending under other circumstances; thus, we pass
611251881Speter     depth anyway, so the code will DTRT if we change the conditional
612251881Speter     in the future.
613251881Speter  */
614251881Speter  if (SVN_DEPTH_IS_RECURSIVE(depth) && (! ignore_externals))
615251881Speter    {
616251881Speter      apr_hash_t *external_map;
617251881Speter      SVN_ERR(svn_wc__externals_defined_below(&external_map,
618251881Speter                                              ctx->wc_ctx, target_abspath,
619251881Speter                                              pool, pool));
620251881Speter
621251881Speter
622251881Speter      SVN_ERR(do_external_status(ctx, external_map,
623251881Speter                                 depth, get_all,
624299742Sdim                                 check_out_of_date, check_working_copy,
625299742Sdim                                 no_ignore, changelists,
626251881Speter                                 sb.anchor_abspath, sb.anchor_relpath,
627251881Speter                                 status_func, status_baton, pool));
628251881Speter    }
629251881Speter
630251881Speter  return SVN_NO_ERROR;
631251881Speter}
632251881Speter
633251881Spetersvn_client_status_t *
634251881Spetersvn_client_status_dup(const svn_client_status_t *status,
635251881Speter                      apr_pool_t *result_pool)
636251881Speter{
637251881Speter  svn_client_status_t *st = apr_palloc(result_pool, sizeof(*st));
638251881Speter
639251881Speter  *st = *status;
640251881Speter
641251881Speter  if (status->local_abspath)
642251881Speter    st->local_abspath = apr_pstrdup(result_pool, status->local_abspath);
643251881Speter
644251881Speter  if (status->repos_root_url)
645251881Speter    st->repos_root_url = apr_pstrdup(result_pool, status->repos_root_url);
646251881Speter
647251881Speter  if (status->repos_uuid)
648251881Speter    st->repos_uuid = apr_pstrdup(result_pool, status->repos_uuid);
649251881Speter
650251881Speter  if (status->repos_relpath)
651251881Speter    st->repos_relpath = apr_pstrdup(result_pool, status->repos_relpath);
652251881Speter
653251881Speter  if (status->changed_author)
654251881Speter    st->changed_author = apr_pstrdup(result_pool, status->changed_author);
655251881Speter
656251881Speter  if (status->lock)
657251881Speter    st->lock = svn_lock_dup(status->lock, result_pool);
658251881Speter
659251881Speter  if (status->changelist)
660251881Speter    st->changelist = apr_pstrdup(result_pool, status->changelist);
661251881Speter
662251881Speter  if (status->ood_changed_author)
663251881Speter    st->ood_changed_author = apr_pstrdup(result_pool, status->ood_changed_author);
664251881Speter
665251881Speter  if (status->repos_lock)
666251881Speter    st->repos_lock = svn_lock_dup(status->repos_lock, result_pool);
667251881Speter
668251881Speter  if (status->backwards_compatibility_baton)
669251881Speter    {
670251881Speter      const svn_wc_status3_t *wc_st = status->backwards_compatibility_baton;
671251881Speter
672251881Speter      st->backwards_compatibility_baton = svn_wc_dup_status3(wc_st,
673251881Speter                                                             result_pool);
674251881Speter    }
675251881Speter
676251881Speter  if (status->moved_from_abspath)
677251881Speter    st->moved_from_abspath =
678251881Speter      apr_pstrdup(result_pool, status->moved_from_abspath);
679251881Speter
680251881Speter  if (status->moved_to_abspath)
681251881Speter    st->moved_to_abspath = apr_pstrdup(result_pool, status->moved_to_abspath);
682251881Speter
683251881Speter  return st;
684251881Speter}
685251881Speter
686251881Spetersvn_error_t *
687251881Spetersvn_client__create_status(svn_client_status_t **cst,
688251881Speter                          svn_wc_context_t *wc_ctx,
689251881Speter                          const char *local_abspath,
690251881Speter                          const svn_wc_status3_t *status,
691251881Speter                          apr_pool_t *result_pool,
692251881Speter                          apr_pool_t *scratch_pool)
693251881Speter{
694251881Speter  *cst = apr_pcalloc(result_pool, sizeof(**cst));
695251881Speter
696251881Speter  (*cst)->kind = status->kind;
697251881Speter  (*cst)->local_abspath = local_abspath;
698251881Speter  (*cst)->filesize = status->filesize;
699251881Speter  (*cst)->versioned = status->versioned;
700251881Speter
701251881Speter  (*cst)->conflicted = status->conflicted;
702251881Speter
703251881Speter  (*cst)->node_status = status->node_status;
704251881Speter  (*cst)->text_status = status->text_status;
705251881Speter  (*cst)->prop_status = status->prop_status;
706251881Speter
707251881Speter  if (status->kind == svn_node_dir)
708251881Speter    (*cst)->wc_is_locked = status->locked;
709251881Speter
710251881Speter  (*cst)->copied = status->copied;
711251881Speter  (*cst)->revision = status->revision;
712251881Speter
713251881Speter  (*cst)->changed_rev = status->changed_rev;
714251881Speter  (*cst)->changed_date = status->changed_date;
715251881Speter  (*cst)->changed_author = status->changed_author;
716251881Speter
717251881Speter  (*cst)->repos_root_url = status->repos_root_url;
718251881Speter  (*cst)->repos_uuid = status->repos_uuid;
719251881Speter  (*cst)->repos_relpath = status->repos_relpath;
720251881Speter
721251881Speter  (*cst)->switched = status->switched;
722251881Speter
723251881Speter  (*cst)->file_external = status->file_external;
724251881Speter  if (status->file_external)
725251881Speter    {
726251881Speter      (*cst)->switched = FALSE;
727251881Speter    }
728251881Speter
729251881Speter  (*cst)->lock = status->lock;
730251881Speter
731251881Speter  (*cst)->changelist = status->changelist;
732251881Speter  (*cst)->depth = status->depth;
733251881Speter
734251881Speter  /* Out of date information */
735251881Speter  (*cst)->ood_kind = status->ood_kind;
736251881Speter  (*cst)->repos_node_status = status->repos_node_status;
737251881Speter  (*cst)->repos_text_status = status->repos_text_status;
738251881Speter  (*cst)->repos_prop_status = status->repos_prop_status;
739251881Speter  (*cst)->repos_lock = status->repos_lock;
740251881Speter
741251881Speter  (*cst)->ood_changed_rev = status->ood_changed_rev;
742251881Speter  (*cst)->ood_changed_date = status->ood_changed_date;
743251881Speter  (*cst)->ood_changed_author = status->ood_changed_author;
744251881Speter
745251881Speter  /* When changing the value of backwards_compatibility_baton, also
746251881Speter     change its use in status4_wrapper_func in deprecated.c */
747251881Speter  (*cst)->backwards_compatibility_baton = status;
748251881Speter
749251881Speter  if (status->versioned && status->conflicted)
750251881Speter    {
751251881Speter      svn_boolean_t text_conflicted, prop_conflicted, tree_conflicted;
752251881Speter
753251881Speter      /* Note: This checks the on disk markers to automatically hide
754251881Speter               text/property conflicts that are hidden by removing their
755251881Speter               markers */
756251881Speter      SVN_ERR(svn_wc_conflicted_p3(&text_conflicted, &prop_conflicted,
757251881Speter                                   &tree_conflicted, wc_ctx, local_abspath,
758251881Speter                                   scratch_pool));
759251881Speter
760251881Speter      if (text_conflicted)
761251881Speter        (*cst)->text_status = svn_wc_status_conflicted;
762251881Speter
763251881Speter      if (prop_conflicted)
764251881Speter        (*cst)->prop_status = svn_wc_status_conflicted;
765251881Speter
766251881Speter      /* ### Also set this for tree_conflicts? */
767251881Speter      if (text_conflicted || prop_conflicted)
768251881Speter        (*cst)->node_status = svn_wc_status_conflicted;
769251881Speter    }
770251881Speter
771251881Speter  (*cst)->moved_from_abspath = status->moved_from_abspath;
772251881Speter  (*cst)->moved_to_abspath = status->moved_to_abspath;
773251881Speter
774251881Speter  return SVN_NO_ERROR;
775251881Speter}
776251881Speter
777