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