]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/libsvn_client/status.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.git] / contrib / subversion / subversion / libsvn_client / status.c
1 /*
2  * status.c:  return the status of a working copy dirent
3  *
4  * ====================================================================
5  *    Licensed to the Apache Software Foundation (ASF) under one
6  *    or more contributor license agreements.  See the NOTICE file
7  *    distributed with this work for additional information
8  *    regarding copyright ownership.  The ASF licenses this file
9  *    to you under the Apache License, Version 2.0 (the
10  *    "License"); you may not use this file except in compliance
11  *    with the License.  You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  *    Unless required by applicable law or agreed to in writing,
16  *    software distributed under the License is distributed on an
17  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18  *    KIND, either express or implied.  See the License for the
19  *    specific language governing permissions and limitations
20  *    under the License.
21  * ====================================================================
22  */
23
24 /* ==================================================================== */
25
26
27 \f
28 /*** Includes. ***/
29 #include <apr_strings.h>
30 #include <apr_pools.h>
31
32 #include "svn_private_config.h"
33 #include "svn_pools.h"
34 #include "svn_sorts.h"
35 #include "client.h"
36
37 #include "svn_path.h"
38 #include "svn_dirent_uri.h"
39 #include "svn_delta.h"
40 #include "svn_client.h"
41 #include "svn_error.h"
42 #include "svn_hash.h"
43
44 #include "private/svn_client_private.h"
45 #include "private/svn_sorts_private.h"
46 #include "private/svn_wc_private.h"
47
48 \f
49 /*** Getting update information ***/
50
51 /* Baton for tweak_status.  It wraps a bit of extra functionality
52    around the received status func/baton, so we can remember if the
53    target was deleted in HEAD and tweak incoming status structures
54    accordingly. */
55 struct status_baton
56 {
57   svn_boolean_t deleted_in_repos;             /* target is deleted in repos */
58   apr_hash_t *changelist_hash;                /* keys are changelist names */
59   svn_client_status_func_t real_status_func;  /* real status function */
60   void *real_status_baton;                    /* real status baton */
61   const char *anchor_abspath;                 /* Absolute path of anchor */
62   const char *anchor_relpath;                 /* Relative path of anchor */
63   svn_wc_context_t *wc_ctx;                   /* A working copy context. */
64 };
65
66 /* A status callback function which wraps the *real* status
67    function/baton.   This sucker takes care of any status tweaks we
68    need to make (such as noting that the target of the status is
69    missing from HEAD in the repository).
70
71    This implements the 'svn_wc_status_func4_t' function type.  */
72 static svn_error_t *
73 tweak_status(void *baton,
74              const char *local_abspath,
75              const svn_wc_status3_t *status,
76              apr_pool_t *scratch_pool)
77 {
78   struct status_baton *sb = baton;
79   const char *path = local_abspath;
80   svn_client_status_t *cst;
81
82   if (sb->anchor_abspath)
83     path = svn_dirent_join(sb->anchor_relpath,
84                            svn_dirent_skip_ancestor(sb->anchor_abspath, path),
85                            scratch_pool);
86
87   /* If the status item has an entry, but doesn't belong to one of the
88      changelists our caller is interested in, we filter out this status
89      transmission.  */
90   if (sb->changelist_hash
91       && (! status->changelist
92           || ! svn_hash_gets(sb->changelist_hash, status->changelist)))
93     {
94       return SVN_NO_ERROR;
95     }
96
97   /* If we know that the target was deleted in HEAD of the repository,
98      we need to note that fact in all the status structures that come
99      through here. */
100   if (sb->deleted_in_repos)
101     {
102       svn_wc_status3_t *new_status = svn_wc_dup_status3(status, scratch_pool);
103       new_status->repos_node_status = svn_wc_status_deleted;
104       status = new_status;
105     }
106
107   SVN_ERR(svn_client__create_status(&cst, sb->wc_ctx, local_abspath, status,
108                                     scratch_pool, scratch_pool));
109
110   /* Call the real status function/baton. */
111   return sb->real_status_func(sb->real_status_baton, path, cst,
112                               scratch_pool);
113 }
114
115 /* A baton for our reporter that is used to collect locks. */
116 typedef struct report_baton_t {
117   const svn_ra_reporter3_t* wrapped_reporter;
118   void *wrapped_report_baton;
119   /* The common ancestor URL of all paths included in the report. */
120   char *ancestor;
121   void *set_locks_baton;
122   svn_depth_t depth;
123   svn_client_ctx_t *ctx;
124   /* Pool to store locks in. */
125   apr_pool_t *pool;
126 } report_baton_t;
127
128 /* Implements svn_ra_reporter3_t->set_path. */
129 static svn_error_t *
130 reporter_set_path(void *report_baton, const char *path,
131                   svn_revnum_t revision, svn_depth_t depth,
132                   svn_boolean_t start_empty, const char *lock_token,
133                   apr_pool_t *pool)
134 {
135   report_baton_t *rb = report_baton;
136
137   return rb->wrapped_reporter->set_path(rb->wrapped_report_baton, path,
138                                         revision, depth, start_empty,
139                                         lock_token, pool);
140 }
141
142 /* Implements svn_ra_reporter3_t->delete_path. */
143 static svn_error_t *
144 reporter_delete_path(void *report_baton, const char *path, apr_pool_t *pool)
145 {
146   report_baton_t *rb = report_baton;
147
148   return rb->wrapped_reporter->delete_path(rb->wrapped_report_baton, path,
149                                            pool);
150 }
151
152 /* Implements svn_ra_reporter3_t->link_path. */
153 static svn_error_t *
154 reporter_link_path(void *report_baton, const char *path, const char *url,
155                    svn_revnum_t revision, svn_depth_t depth,
156                    svn_boolean_t start_empty,
157                    const char *lock_token, apr_pool_t *pool)
158 {
159   report_baton_t *rb = report_baton;
160
161   if (!svn_uri__is_ancestor(rb->ancestor, url))
162     {
163       const char *ancestor;
164
165       ancestor = svn_uri_get_longest_ancestor(url, rb->ancestor, pool);
166
167       /* If we got a shorter ancestor, truncate our current ancestor.
168          Note that svn_uri_get_longest_ancestor will allocate its return
169          value even if it identical to one of its arguments. */
170
171       rb->ancestor[strlen(ancestor)] = '\0';
172       rb->depth = svn_depth_infinity;
173     }
174
175   return rb->wrapped_reporter->link_path(rb->wrapped_report_baton, path, url,
176                                          revision, depth, start_empty,
177                                          lock_token, pool);
178 }
179
180 /* Implements svn_ra_reporter3_t->finish_report. */
181 static svn_error_t *
182 reporter_finish_report(void *report_baton, apr_pool_t *pool)
183 {
184   report_baton_t *rb = report_baton;
185   svn_ra_session_t *ras;
186   apr_hash_t *locks;
187   const char *repos_root;
188   apr_pool_t *subpool = svn_pool_create(pool);
189   svn_error_t *err = SVN_NO_ERROR;
190
191   /* Open an RA session to our common ancestor and grab the locks under it.
192    */
193   SVN_ERR(svn_client_open_ra_session2(&ras, rb->ancestor, NULL,
194                                       rb->ctx, subpool, subpool));
195
196   /* The locks need to live throughout the edit.  Note that if the
197      server doesn't support lock discovery, we'll just not do locky
198      stuff. */
199   err = svn_ra_get_locks2(ras, &locks, "", rb->depth, rb->pool);
200   if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)
201     {
202       svn_error_clear(err);
203       err = SVN_NO_ERROR;
204       locks = apr_hash_make(rb->pool);
205     }
206   SVN_ERR(err);
207
208   SVN_ERR(svn_ra_get_repos_root2(ras, &repos_root, rb->pool));
209
210   /* Close the RA session. */
211   svn_pool_destroy(subpool);
212
213   SVN_ERR(svn_wc_status_set_repos_locks(rb->set_locks_baton, locks,
214                                         repos_root, rb->pool));
215
216   return rb->wrapped_reporter->finish_report(rb->wrapped_report_baton, pool);
217 }
218
219 /* Implements svn_ra_reporter3_t->abort_report. */
220 static svn_error_t *
221 reporter_abort_report(void *report_baton, apr_pool_t *pool)
222 {
223   report_baton_t *rb = report_baton;
224
225   return rb->wrapped_reporter->abort_report(rb->wrapped_report_baton, pool);
226 }
227
228 /* A reporter that keeps track of the common URL ancestor of all paths in
229    the WC and fetches repository locks for all paths under this ancestor. */
230 static svn_ra_reporter3_t lock_fetch_reporter = {
231   reporter_set_path,
232   reporter_delete_path,
233   reporter_link_path,
234   reporter_finish_report,
235   reporter_abort_report
236 };
237
238 /* Perform status operations on each external in EXTERNAL_MAP, a const char *
239    local_abspath of all externals mapping to the const char* defining_abspath.
240    All other options are the same as those passed to svn_client_status().
241
242    If ANCHOR_ABSPATH and ANCHOR-RELPATH are not null, use them to provide
243    properly formatted relative paths */
244 static svn_error_t *
245 do_external_status(svn_client_ctx_t *ctx,
246                    apr_hash_t *external_map,
247                    svn_depth_t depth,
248                    svn_boolean_t get_all,
249                    svn_boolean_t check_out_of_date,
250                    svn_boolean_t check_working_copy,
251                    svn_boolean_t no_ignore,
252                    const apr_array_header_t *changelists,
253                    const char *anchor_abspath,
254                    const char *anchor_relpath,
255                    svn_client_status_func_t status_func,
256                    void *status_baton,
257                    apr_pool_t *scratch_pool)
258 {
259   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
260   apr_array_header_t *externals;
261   int i;
262
263   externals = svn_sort__hash(external_map, svn_sort_compare_items_lexically,
264                              scratch_pool);
265
266   /* Loop over the hash of new values (we don't care about the old
267      ones).  This is a mapping of versioned directories to property
268      values. */
269   for (i = 0; i < externals->nelts; i++)
270     {
271       svn_node_kind_t external_kind;
272       svn_sort__item_t item = APR_ARRAY_IDX(externals, i, svn_sort__item_t);
273       const char *local_abspath = item.key;
274       const char *defining_abspath = item.value;
275       svn_node_kind_t kind;
276       svn_opt_revision_t opt_rev;
277       const char *status_path;
278
279       svn_pool_clear(iterpool);
280
281       /* Obtain information on the expected external. */
282       SVN_ERR(svn_wc__read_external_info(&external_kind, NULL, NULL, NULL,
283                                          &opt_rev.value.number,
284                                          ctx->wc_ctx, defining_abspath,
285                                          local_abspath, FALSE,
286                                          iterpool, iterpool));
287
288       if (external_kind != svn_node_dir)
289         continue;
290
291       SVN_ERR(svn_io_check_path(local_abspath, &kind, iterpool));
292       if (kind != svn_node_dir)
293         continue;
294
295       if (SVN_IS_VALID_REVNUM(opt_rev.value.number))
296         opt_rev.kind = svn_opt_revision_number;
297       else
298         opt_rev.kind = svn_opt_revision_unspecified;
299
300       /* Tell the client we're starting an external status set. */
301       if (ctx->notify_func2)
302         ctx->notify_func2(
303                ctx->notify_baton2,
304                svn_wc_create_notify(local_abspath,
305                                     svn_wc_notify_status_external,
306                                     iterpool), iterpool);
307
308       status_path = local_abspath;
309       if (anchor_abspath)
310         {
311           status_path = svn_dirent_join(anchor_relpath,
312                            svn_dirent_skip_ancestor(anchor_abspath,
313                                                     status_path),
314                            iterpool);
315         }
316
317       /* And then do the status. */
318       SVN_ERR(svn_client_status6(NULL, ctx, status_path, &opt_rev, depth,
319                                  get_all, check_out_of_date,
320                                  check_working_copy, no_ignore,
321                                  FALSE /* ignore_exernals */,
322                                  FALSE /* depth_as_sticky */,
323                                  changelists, status_func, status_baton,
324                                  iterpool));
325     }
326
327   /* Destroy SUBPOOL and (implicitly) ITERPOOL. */
328   svn_pool_destroy(iterpool);
329
330   return SVN_NO_ERROR;
331 }
332 \f
333 /*** Public Interface. ***/
334
335
336 svn_error_t *
337 svn_client_status6(svn_revnum_t *result_rev,
338                    svn_client_ctx_t *ctx,
339                    const char *path,
340                    const svn_opt_revision_t *revision,
341                    svn_depth_t depth,
342                    svn_boolean_t get_all,
343                    svn_boolean_t check_out_of_date,
344                    svn_boolean_t check_working_copy,
345                    svn_boolean_t no_ignore,
346                    svn_boolean_t ignore_externals,
347                    svn_boolean_t depth_as_sticky,
348                    const apr_array_header_t *changelists,
349                    svn_client_status_func_t status_func,
350                    void *status_baton,
351                    apr_pool_t *pool)  /* ### aka scratch_pool */
352 {
353   struct status_baton sb;
354   const char *dir, *dir_abspath;
355   const char *target_abspath;
356   const char *target_basename;
357   apr_array_header_t *ignores;
358   svn_error_t *err;
359   apr_hash_t *changelist_hash = NULL;
360
361   /* Override invalid combinations of the check_out_of_date and
362      check_working_copy flags. */
363   if (!check_out_of_date)
364     check_working_copy = TRUE;
365
366   if (svn_path_is_url(path))
367     return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
368                              _("'%s' is not a local path"), path);
369
370   if (changelists && changelists->nelts)
371     SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelists, pool));
372
373   if (result_rev)
374     *result_rev = SVN_INVALID_REVNUM;
375
376   sb.real_status_func = status_func;
377   sb.real_status_baton = status_baton;
378   sb.deleted_in_repos = FALSE;
379   sb.changelist_hash = changelist_hash;
380   sb.wc_ctx = ctx->wc_ctx;
381
382   SVN_ERR(svn_dirent_get_absolute(&target_abspath, path, pool));
383
384   if (check_out_of_date)
385     {
386       /* The status editor only works on directories, so get the ancestor
387          if necessary */
388
389       svn_node_kind_t kind;
390
391       SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, target_abspath,
392                                 TRUE, FALSE, pool));
393
394       /* Dir must be a working copy directory or the status editor fails */
395       if (kind == svn_node_dir)
396         {
397           dir_abspath = target_abspath;
398           target_basename = "";
399           dir = path;
400         }
401       else
402         {
403           dir_abspath = svn_dirent_dirname(target_abspath, pool);
404           target_basename = svn_dirent_basename(target_abspath, NULL);
405           dir = svn_dirent_dirname(path, pool);
406
407           if (kind == svn_node_file)
408             {
409               if (depth == svn_depth_empty)
410                 depth = svn_depth_files;
411             }
412           else
413             {
414               err = svn_wc_read_kind2(&kind, ctx->wc_ctx, dir_abspath,
415                                       FALSE, FALSE, pool);
416
417               svn_error_clear(err);
418
419               if (err || kind != svn_node_dir)
420                 {
421                   return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
422                                            _("'%s' is not a working copy"),
423                                            svn_dirent_local_style(path, pool));
424                 }
425             }
426         }
427     }
428   else
429     {
430       dir = path;
431       dir_abspath = target_abspath;
432     }
433
434   if (svn_dirent_is_absolute(dir))
435     {
436       sb.anchor_abspath = NULL;
437       sb.anchor_relpath = NULL;
438     }
439   else
440     {
441       sb.anchor_abspath = dir_abspath;
442       sb.anchor_relpath = dir;
443     }
444
445   /* Get the status edit, and use our wrapping status function/baton
446      as the callback pair. */
447   SVN_ERR(svn_wc_get_default_ignores(&ignores, ctx->config, pool));
448
449   /* If we want to know about out-of-dateness, we crawl the working copy and
450      let the RA layer drive the editor for real.  Otherwise, we just close the
451      edit.  :-) */
452   if (check_out_of_date)
453     {
454       svn_ra_session_t *ra_session;
455       const char *URL;
456       svn_node_kind_t kind;
457       svn_boolean_t server_supports_depth;
458       const svn_delta_editor_t *editor;
459       void *edit_baton, *set_locks_baton;
460       svn_revnum_t edit_revision = SVN_INVALID_REVNUM;
461
462       /* Get full URL from the ANCHOR. */
463       SVN_ERR(svn_client_url_from_path2(&URL, dir_abspath, ctx,
464                                         pool, pool));
465
466       if (!URL)
467         return svn_error_createf
468           (SVN_ERR_ENTRY_MISSING_URL, NULL,
469            _("Entry '%s' has no URL"),
470            svn_dirent_local_style(dir, pool));
471
472       /* Open a repository session to the URL. */
473       SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, URL,
474                                                    dir_abspath, NULL,
475                                                    FALSE, TRUE,
476                                                    ctx, pool, pool));
477
478       SVN_ERR(svn_ra_has_capability(ra_session, &server_supports_depth,
479                                     SVN_RA_CAPABILITY_DEPTH, pool));
480
481       SVN_ERR(svn_wc__get_status_editor(&editor, &edit_baton, &set_locks_baton,
482                                         &edit_revision, ctx->wc_ctx,
483                                         dir_abspath, target_basename,
484                                         depth, get_all, check_working_copy,
485                                         no_ignore, depth_as_sticky,
486                                         server_supports_depth,
487                                         ignores, tweak_status, &sb,
488                                         ctx->cancel_func, ctx->cancel_baton,
489                                         pool, pool));
490
491
492       /* Verify that URL exists in HEAD.  If it doesn't, this can save
493          us a whole lot of hassle; if it does, the cost of this
494          request should be minimal compared to the size of getting
495          back the average amount of "out-of-date" information. */
496       SVN_ERR(svn_ra_check_path(ra_session, "", SVN_INVALID_REVNUM,
497                                 &kind, pool));
498       if (kind == svn_node_none)
499         {
500           svn_boolean_t added;
501
502           /* Our status target does not exist in HEAD.  If we've got
503              it locally added, that's okay.  But if it was previously
504              versioned, then it must have since been deleted from the
505              repository.  (Note that "locally replaced" doesn't count
506              as "added" in this case.)  */
507           SVN_ERR(svn_wc__node_is_added(&added, ctx->wc_ctx,
508                                         dir_abspath, pool));
509           if (! added)
510             sb.deleted_in_repos = TRUE;
511
512           /* And now close the edit. */
513           SVN_ERR(editor->close_edit(edit_baton, pool));
514         }
515       else
516         {
517           svn_revnum_t revnum;
518           report_baton_t rb;
519           svn_depth_t status_depth;
520
521           if (revision->kind == svn_opt_revision_head)
522             {
523               /* Cause the revision number to be omitted from the request,
524                  which implies HEAD. */
525               revnum = SVN_INVALID_REVNUM;
526             }
527           else
528             {
529               /* Get a revision number for our status operation. */
530               SVN_ERR(svn_client__get_revision_number(&revnum, NULL,
531                                                       ctx->wc_ctx,
532                                                       target_abspath,
533                                                       ra_session, revision,
534                                                       pool));
535             }
536
537           if (depth_as_sticky || !server_supports_depth)
538             status_depth = depth;
539           else
540             status_depth = svn_depth_unknown; /* Use depth from WC */
541
542           /* Do the deed.  Let the RA layer drive the status editor. */
543           SVN_ERR(svn_ra_do_status2(ra_session, &rb.wrapped_reporter,
544                                     &rb.wrapped_report_baton,
545                                     target_basename, revnum, status_depth,
546                                     editor, edit_baton, pool));
547
548           /* Init the report baton. */
549           rb.ancestor = apr_pstrdup(pool, URL); /* Edited later */
550           rb.set_locks_baton = set_locks_baton;
551           rb.ctx = ctx;
552           rb.pool = pool;
553
554           if (depth == svn_depth_unknown)
555             rb.depth = svn_depth_infinity;
556           else
557             rb.depth = depth;
558
559           /* Drive the reporter structure, describing the revisions
560              within PATH.  When we call reporter->finish_report,
561              EDITOR will be driven to describe differences between our
562              working copy and HEAD. */
563           SVN_ERR(svn_wc_crawl_revisions5(ctx->wc_ctx,
564                                           target_abspath,
565                                           &lock_fetch_reporter, &rb,
566                                           FALSE /* restore_files */,
567                                           depth, (! depth_as_sticky),
568                                           (! server_supports_depth),
569                                           FALSE /* use_commit_times */,
570                                           ctx->cancel_func, ctx->cancel_baton,
571                                           NULL, NULL, pool));
572         }
573
574       if (ctx->notify_func2)
575         {
576           svn_wc_notify_t *notify
577             = svn_wc_create_notify(target_abspath,
578                                    svn_wc_notify_status_completed, pool);
579           notify->revision = edit_revision;
580           ctx->notify_func2(ctx->notify_baton2, notify, pool);
581         }
582
583       /* If the caller wants the result revision, give it to them. */
584       if (result_rev)
585         *result_rev = edit_revision;
586     }
587   else
588     {
589       err = svn_wc_walk_status(ctx->wc_ctx, target_abspath,
590                                depth, get_all, no_ignore, FALSE, ignores,
591                                tweak_status, &sb,
592                                ctx->cancel_func, ctx->cancel_baton,
593                                pool);
594
595       if (err && err->apr_err == SVN_ERR_WC_MISSING)
596         {
597           /* This error code is checked for in svn to continue after
598              this error */
599           svn_error_clear(err);
600           return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
601                                _("'%s' is not a working copy"),
602                                svn_dirent_local_style(path, pool));
603         }
604
605       SVN_ERR(err);
606     }
607
608   /* We only descend into an external if depth is svn_depth_infinity or
609      svn_depth_unknown.  However, there are conceivable behaviors that
610      would involve descending under other circumstances; thus, we pass
611      depth anyway, so the code will DTRT if we change the conditional
612      in the future.
613   */
614   if (SVN_DEPTH_IS_RECURSIVE(depth) && (! ignore_externals))
615     {
616       apr_hash_t *external_map;
617       SVN_ERR(svn_wc__externals_defined_below(&external_map,
618                                               ctx->wc_ctx, target_abspath,
619                                               pool, pool));
620
621
622       SVN_ERR(do_external_status(ctx, external_map,
623                                  depth, get_all,
624                                  check_out_of_date, check_working_copy,
625                                  no_ignore, changelists,
626                                  sb.anchor_abspath, sb.anchor_relpath,
627                                  status_func, status_baton, pool));
628     }
629
630   return SVN_NO_ERROR;
631 }
632
633 svn_client_status_t *
634 svn_client_status_dup(const svn_client_status_t *status,
635                       apr_pool_t *result_pool)
636 {
637   svn_client_status_t *st = apr_palloc(result_pool, sizeof(*st));
638
639   *st = *status;
640
641   if (status->local_abspath)
642     st->local_abspath = apr_pstrdup(result_pool, status->local_abspath);
643
644   if (status->repos_root_url)
645     st->repos_root_url = apr_pstrdup(result_pool, status->repos_root_url);
646
647   if (status->repos_uuid)
648     st->repos_uuid = apr_pstrdup(result_pool, status->repos_uuid);
649
650   if (status->repos_relpath)
651     st->repos_relpath = apr_pstrdup(result_pool, status->repos_relpath);
652
653   if (status->changed_author)
654     st->changed_author = apr_pstrdup(result_pool, status->changed_author);
655
656   if (status->lock)
657     st->lock = svn_lock_dup(status->lock, result_pool);
658
659   if (status->changelist)
660     st->changelist = apr_pstrdup(result_pool, status->changelist);
661
662   if (status->ood_changed_author)
663     st->ood_changed_author = apr_pstrdup(result_pool, status->ood_changed_author);
664
665   if (status->repos_lock)
666     st->repos_lock = svn_lock_dup(status->repos_lock, result_pool);
667
668   if (status->backwards_compatibility_baton)
669     {
670       const svn_wc_status3_t *wc_st = status->backwards_compatibility_baton;
671
672       st->backwards_compatibility_baton = svn_wc_dup_status3(wc_st,
673                                                              result_pool);
674     }
675
676   if (status->moved_from_abspath)
677     st->moved_from_abspath =
678       apr_pstrdup(result_pool, status->moved_from_abspath);
679
680   if (status->moved_to_abspath)
681     st->moved_to_abspath = apr_pstrdup(result_pool, status->moved_to_abspath);
682
683   return st;
684 }
685
686 svn_error_t *
687 svn_client__create_status(svn_client_status_t **cst,
688                           svn_wc_context_t *wc_ctx,
689                           const char *local_abspath,
690                           const svn_wc_status3_t *status,
691                           apr_pool_t *result_pool,
692                           apr_pool_t *scratch_pool)
693 {
694   *cst = apr_pcalloc(result_pool, sizeof(**cst));
695
696   (*cst)->kind = status->kind;
697   (*cst)->local_abspath = local_abspath;
698   (*cst)->filesize = status->filesize;
699   (*cst)->versioned = status->versioned;
700
701   (*cst)->conflicted = status->conflicted;
702
703   (*cst)->node_status = status->node_status;
704   (*cst)->text_status = status->text_status;
705   (*cst)->prop_status = status->prop_status;
706
707   if (status->kind == svn_node_dir)
708     (*cst)->wc_is_locked = status->locked;
709
710   (*cst)->copied = status->copied;
711   (*cst)->revision = status->revision;
712
713   (*cst)->changed_rev = status->changed_rev;
714   (*cst)->changed_date = status->changed_date;
715   (*cst)->changed_author = status->changed_author;
716
717   (*cst)->repos_root_url = status->repos_root_url;
718   (*cst)->repos_uuid = status->repos_uuid;
719   (*cst)->repos_relpath = status->repos_relpath;
720
721   (*cst)->switched = status->switched;
722
723   (*cst)->file_external = status->file_external;
724   if (status->file_external)
725     {
726       (*cst)->switched = FALSE;
727     }
728
729   (*cst)->lock = status->lock;
730
731   (*cst)->changelist = status->changelist;
732   (*cst)->depth = status->depth;
733
734   /* Out of date information */
735   (*cst)->ood_kind = status->ood_kind;
736   (*cst)->repos_node_status = status->repos_node_status;
737   (*cst)->repos_text_status = status->repos_text_status;
738   (*cst)->repos_prop_status = status->repos_prop_status;
739   (*cst)->repos_lock = status->repos_lock;
740
741   (*cst)->ood_changed_rev = status->ood_changed_rev;
742   (*cst)->ood_changed_date = status->ood_changed_date;
743   (*cst)->ood_changed_author = status->ood_changed_author;
744
745   /* When changing the value of backwards_compatibility_baton, also
746      change its use in status4_wrapper_func in deprecated.c */
747   (*cst)->backwards_compatibility_baton = status;
748
749   if (status->versioned && status->conflicted)
750     {
751       svn_boolean_t text_conflicted, prop_conflicted, tree_conflicted;
752
753       /* Note: This checks the on disk markers to automatically hide
754                text/property conflicts that are hidden by removing their
755                markers */
756       SVN_ERR(svn_wc_conflicted_p3(&text_conflicted, &prop_conflicted,
757                                    &tree_conflicted, wc_ctx, local_abspath,
758                                    scratch_pool));
759
760       if (text_conflicted)
761         (*cst)->text_status = svn_wc_status_conflicted;
762
763       if (prop_conflicted)
764         (*cst)->prop_status = svn_wc_status_conflicted;
765
766       /* ### Also set this for tree_conflicts? */
767       if (text_conflicted || prop_conflicted)
768         (*cst)->node_status = svn_wc_status_conflicted;
769     }
770
771   (*cst)->moved_from_abspath = status->moved_from_abspath;
772   (*cst)->moved_to_abspath = status->moved_to_abspath;
773
774   return SVN_NO_ERROR;
775 }
776