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