3 * ====================================================================
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
20 * ====================================================================
24 #include "svn_dirent_uri.h"
27 #include "svn_pools.h"
32 #include "svn_private_config.h"
33 #include "private/svn_wc_private.h"
38 svn_wc_info_dup(const svn_wc_info_t *info,
41 svn_wc_info_t *new_info = apr_pmemdup(pool, info, sizeof(*new_info));
44 new_info->changelist = apr_pstrdup(pool, info->changelist);
45 new_info->checksum = svn_checksum_dup(info->checksum, pool);
50 apr_array_header_t *new_conflicts
51 = apr_array_make(pool, info->conflicts->nelts, info->conflicts->elt_size);
52 for (i = 0; i < info->conflicts->nelts; i++)
54 APR_ARRAY_PUSH(new_conflicts, svn_wc_conflict_description2_t *)
55 = svn_wc_conflict_description2_dup(
56 APR_ARRAY_IDX(info->conflicts, i,
57 const svn_wc_conflict_description2_t *),
60 new_info->conflicts = new_conflicts;
62 if (info->copyfrom_url)
63 new_info->copyfrom_url = apr_pstrdup(pool, info->copyfrom_url);
64 if (info->wcroot_abspath)
65 new_info->wcroot_abspath = apr_pstrdup(pool, info->wcroot_abspath);
66 if (info->moved_from_abspath)
67 new_info->moved_from_abspath = apr_pstrdup(pool, info->moved_from_abspath);
68 if (info->moved_to_abspath)
69 new_info->moved_to_abspath = apr_pstrdup(pool, info->moved_to_abspath);
75 /* Set *INFO to a new struct, allocated in RESULT_POOL, built from the WC
76 metadata of LOCAL_ABSPATH. Pointer fields are copied by reference, not
79 build_info_for_node(svn_wc__info2_t **info,
81 const char *local_abspath,
83 apr_pool_t *result_pool,
84 apr_pool_t *scratch_pool)
86 svn_wc__info2_t *tmpinfo;
87 const char *repos_relpath;
88 svn_wc__db_status_t status;
89 svn_node_kind_t db_kind;
90 const char *original_repos_relpath;
91 const char *original_repos_root_url;
92 const char *original_uuid;
93 svn_revnum_t original_revision;
94 svn_wc__db_lock_t *lock;
95 svn_boolean_t conflicted;
96 svn_boolean_t op_root;
97 svn_boolean_t have_base;
98 svn_boolean_t have_more_work;
99 svn_wc_info_t *wc_info;
101 tmpinfo = apr_pcalloc(result_pool, sizeof(*tmpinfo));
102 tmpinfo->kind = kind;
104 wc_info = apr_pcalloc(result_pool, sizeof(*wc_info));
105 tmpinfo->wc_info = wc_info;
107 wc_info->copyfrom_rev = SVN_INVALID_REVNUM;
109 SVN_ERR(svn_wc__db_read_info(&status, &db_kind, &tmpinfo->rev,
111 &tmpinfo->repos_root_URL, &tmpinfo->repos_UUID,
112 &tmpinfo->last_changed_rev,
113 &tmpinfo->last_changed_date,
114 &tmpinfo->last_changed_author,
115 &wc_info->depth, &wc_info->checksum, NULL,
116 &original_repos_relpath,
117 &original_repos_root_url, &original_uuid,
118 &original_revision, &lock,
119 &wc_info->recorded_size,
120 &wc_info->recorded_time,
121 &wc_info->changelist,
122 &conflicted, &op_root, NULL, NULL,
123 &have_base, &have_more_work, NULL,
125 result_pool, scratch_pool));
127 if (original_repos_root_url != NULL)
129 tmpinfo->repos_root_URL = original_repos_root_url;
130 tmpinfo->repos_UUID = original_uuid;
133 if (status == svn_wc__db_status_added)
135 /* ### We should also just be fetching the true BASE revision
136 ### here, which means copied items would also not have a
137 ### revision to display. But WC-1 wants to show the revision of
138 ### copy targets as the copyfrom-rev. *sigh* */
140 if (original_repos_relpath)
142 /* Root or child of copy */
143 tmpinfo->rev = original_revision;
148 wc_info->copyfrom_url =
149 svn_path_url_add_component2(tmpinfo->repos_root_URL,
150 original_repos_relpath,
153 wc_info->copyfrom_rev = original_revision;
155 err = svn_wc__db_scan_moved(&wc_info->moved_from_abspath,
158 result_pool, scratch_pool);
162 if (err->apr_err != SVN_ERR_WC_PATH_UNEXPECTED_STATUS)
163 return svn_error_trace(err);
164 svn_error_clear(err);
165 wc_info->moved_from_abspath = NULL;
170 /* ### We should be able to avoid both these calls with the information
171 from read_info() in most cases */
173 wc_info->schedule = svn_wc_schedule_normal;
174 else if (! have_more_work && ! have_base)
175 wc_info->schedule = svn_wc_schedule_add;
178 svn_wc__db_status_t below_working;
179 svn_boolean_t have_work;
181 SVN_ERR(svn_wc__db_info_below_working(&have_base, &have_work,
186 /* If the node is not present or deleted (read: not present
187 in working), then the node is not a replacement */
188 if (below_working != svn_wc__db_status_not_present
189 && below_working != svn_wc__db_status_deleted)
191 wc_info->schedule = svn_wc_schedule_replace;
194 wc_info->schedule = svn_wc_schedule_add;
196 SVN_ERR(svn_wc__db_read_repos_info(NULL, &repos_relpath,
197 &tmpinfo->repos_root_URL,
198 &tmpinfo->repos_UUID,
200 result_pool, scratch_pool));
202 tmpinfo->URL = svn_path_url_add_component2(tmpinfo->repos_root_URL,
203 repos_relpath, result_pool);
205 else if (status == svn_wc__db_status_deleted)
207 svn_wc__db_status_t w_status;
209 SVN_ERR(svn_wc__db_read_pristine_info(&w_status, &tmpinfo->kind,
210 &tmpinfo->last_changed_rev,
211 &tmpinfo->last_changed_date,
212 &tmpinfo->last_changed_author,
217 result_pool, scratch_pool));
219 if (w_status == svn_wc__db_status_deleted)
221 /* We have a working not-present status. We don't know anything
222 about this node, but it *is visible* in STATUS.
224 Let's tell that it is excluded */
226 wc_info->depth = svn_depth_exclude;
227 tmpinfo->kind = svn_node_unknown;
230 /* And now fetch the url and revision of what will be deleted */
231 SVN_ERR(svn_wc__db_scan_deletion(NULL, &wc_info->moved_to_abspath,
234 scratch_pool, scratch_pool));
236 SVN_ERR(svn_wc__db_read_repos_info(&tmpinfo->rev, &repos_relpath,
237 &tmpinfo->repos_root_URL,
238 &tmpinfo->repos_UUID,
240 result_pool, scratch_pool));
242 wc_info->schedule = svn_wc_schedule_delete;
243 tmpinfo->URL = svn_path_url_add_component2(tmpinfo->repos_root_URL,
244 repos_relpath, result_pool);
246 else if (status == svn_wc__db_status_not_present
247 || status == svn_wc__db_status_server_excluded)
252 else if (status == svn_wc__db_status_excluded && !repos_relpath)
254 /* We have a WORKING exclude. Avoid segfault on no repos info */
256 SVN_ERR(svn_wc__db_read_repos_info(NULL, &repos_relpath,
257 &tmpinfo->repos_root_URL,
258 &tmpinfo->repos_UUID,
260 result_pool, scratch_pool));
262 wc_info->schedule = svn_wc_schedule_normal;
263 tmpinfo->URL = svn_path_url_add_component2(tmpinfo->repos_root_URL,
264 repos_relpath, result_pool);
265 tmpinfo->wc_info->depth = svn_depth_exclude;
269 /* Just a BASE node. We have all the info we need */
270 tmpinfo->URL = svn_path_url_add_component2(tmpinfo->repos_root_URL,
273 wc_info->schedule = svn_wc_schedule_normal;
275 if (status == svn_wc__db_status_excluded)
276 wc_info->depth = svn_depth_exclude;
280 tmpinfo->size = SVN_INVALID_FILESIZE;
282 SVN_ERR(svn_wc__db_get_wcroot(&tmpinfo->wc_info->wcroot_abspath, db,
283 local_abspath, result_pool, scratch_pool));
286 SVN_ERR(svn_wc__read_conflicts(&wc_info->conflicts, NULL,
288 FALSE /* create tempfiles */,
289 FALSE /* only tree conflicts */,
290 result_pool, scratch_pool));
292 wc_info->conflicts = NULL;
297 tmpinfo->lock = apr_pcalloc(result_pool, sizeof(*(tmpinfo->lock)));
298 tmpinfo->lock->token = lock->token;
299 tmpinfo->lock->owner = lock->owner;
300 tmpinfo->lock->comment = lock->comment;
301 tmpinfo->lock->creation_date = lock->date;
309 /* Set *INFO to a new struct with minimal content, to be
310 used in reporting info for unversioned tree conflict victims. */
311 /* ### Some fields we could fill out based on the parent dir's entry
312 or by looking at an obstructing item. */
314 build_info_for_unversioned(svn_wc__info2_t **info,
317 svn_wc__info2_t *tmpinfo = apr_pcalloc(pool, sizeof(*tmpinfo));
318 svn_wc_info_t *wc_info = apr_pcalloc(pool, sizeof (*wc_info));
321 tmpinfo->repos_UUID = NULL;
322 tmpinfo->repos_root_URL = NULL;
323 tmpinfo->rev = SVN_INVALID_REVNUM;
324 tmpinfo->kind = svn_node_none;
325 tmpinfo->size = SVN_INVALID_FILESIZE;
326 tmpinfo->last_changed_rev = SVN_INVALID_REVNUM;
327 tmpinfo->last_changed_date = 0;
328 tmpinfo->last_changed_author = NULL;
329 tmpinfo->lock = NULL;
331 tmpinfo->wc_info = wc_info;
333 wc_info->copyfrom_rev = SVN_INVALID_REVNUM;
334 wc_info->depth = svn_depth_unknown;
335 wc_info->recorded_size = SVN_INVALID_FILESIZE;
341 /* Callback and baton for crawl_entries() walk over entries files. */
342 struct found_entry_baton
344 svn_wc__info_receiver2_t receiver;
345 void *receiver_baton;
347 svn_boolean_t actual_only;
349 /* The set of tree conflicts that have been found but not (yet) visited by
350 * the tree walker. Map of abspath -> empty string. */
351 apr_hash_t *tree_conflicts;
355 /* Call WALK_BATON->receiver with WALK_BATON->receiver_baton, passing to it
356 * info about the path LOCAL_ABSPATH.
357 * An svn_wc__node_found_func_t callback function. */
359 info_found_node_callback(const char *local_abspath,
360 svn_node_kind_t kind,
362 apr_pool_t *scratch_pool)
364 struct found_entry_baton *fe_baton = walk_baton;
365 svn_wc__info2_t *info;
367 SVN_ERR(build_info_for_node(&info, fe_baton->db, local_abspath,
368 kind, scratch_pool, scratch_pool));
372 if (!fe_baton->first)
373 return SVN_NO_ERROR; /* not present or server excluded descendant */
375 /* If the info root is not found, that is an error */
376 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
377 _("The node '%s' was not found."),
378 svn_dirent_local_style(local_abspath,
382 fe_baton->first = FALSE;
384 SVN_ERR_ASSERT(info->wc_info != NULL);
385 SVN_ERR(fe_baton->receiver(fe_baton->receiver_baton, local_abspath,
386 info, scratch_pool));
388 /* If this node is a versioned directory, make a note of any tree conflicts
389 * on all immediate children. Some of these may be visited later in this
390 * walk, at which point they will be removed from the list, while any that
391 * are not visited will remain in the list. */
392 if (fe_baton->actual_only && kind == svn_node_dir)
394 const apr_array_header_t *victims;
397 SVN_ERR(svn_wc__db_read_conflict_victims(&victims,
398 fe_baton->db, local_abspath,
399 scratch_pool, scratch_pool));
401 for (i = 0; i < victims->nelts; i++)
403 const char *this_basename = APR_ARRAY_IDX(victims, i, const char *);
405 svn_hash_sets(fe_baton->tree_conflicts,
406 svn_dirent_join(local_abspath, this_basename,
412 /* Delete this path which we are currently visiting from the list of tree
413 * conflicts. This relies on the walker visiting a directory before visiting
415 svn_hash_sets(fe_baton->tree_conflicts, local_abspath, NULL);
421 /* Return TRUE iff the subtree at ROOT_ABSPATH, restricted to depth DEPTH,
422 * would include the path CHILD_ABSPATH of kind CHILD_KIND. */
424 depth_includes(const char *root_abspath,
426 const char *child_abspath,
427 svn_node_kind_t child_kind,
428 apr_pool_t *scratch_pool)
430 const char *parent_abspath = svn_dirent_dirname(child_abspath, scratch_pool);
432 return (depth == svn_depth_infinity
433 || ((depth == svn_depth_immediates
434 || (depth == svn_depth_files && child_kind == svn_node_file))
435 && strcmp(root_abspath, parent_abspath) == 0)
436 || strcmp(root_abspath, child_abspath) == 0);
441 svn_wc__get_info(svn_wc_context_t *wc_ctx,
442 const char *local_abspath,
444 svn_boolean_t fetch_excluded,
445 svn_boolean_t fetch_actual_only,
446 const apr_array_header_t *changelist_filter,
447 svn_wc__info_receiver2_t receiver,
448 void *receiver_baton,
449 svn_cancel_func_t cancel_func,
451 apr_pool_t *scratch_pool)
453 struct found_entry_baton fe_baton;
455 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
456 apr_hash_index_t *hi;
457 const char *repos_root_url = NULL;
458 const char *repos_uuid = NULL;
460 fe_baton.receiver = receiver;
461 fe_baton.receiver_baton = receiver_baton;
462 fe_baton.db = wc_ctx->db;
463 fe_baton.actual_only = fetch_actual_only;
464 fe_baton.first = TRUE;
465 fe_baton.tree_conflicts = apr_hash_make(scratch_pool);
466 fe_baton.pool = scratch_pool;
468 err = svn_wc__internal_walk_children(wc_ctx->db, local_abspath,
471 info_found_node_callback,
473 cancel_func, cancel_baton,
476 /* If the target root node is not present, svn_wc__internal_walk_children()
477 returns a PATH_NOT_FOUND error and doesn't call the callback. If there
478 is a tree conflict on this node, that is not an error. */
479 if (fe_baton.first /* not visited by walk_children */
481 && err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
483 svn_boolean_t tree_conflicted;
486 err2 = svn_wc__internal_conflicted_p(NULL, NULL, &tree_conflicted,
487 wc_ctx->db, local_abspath,
490 if ((err2 && err2->apr_err == SVN_ERR_WC_PATH_NOT_FOUND))
492 svn_error_clear(err2);
493 return svn_error_trace(err);
495 else if (err2 || !tree_conflicted)
496 return svn_error_compose_create(err, err2);
498 svn_error_clear(err);
500 svn_hash_sets(fe_baton.tree_conflicts, local_abspath, "");
505 /* If there are any tree conflicts that we have found but have not reported,
506 * send a minimal info struct for each one now. */
507 for (hi = apr_hash_first(scratch_pool, fe_baton.tree_conflicts); hi;
508 hi = apr_hash_next(hi))
510 const char *this_abspath = apr_hash_this_key(hi);
511 const svn_wc_conflict_description2_t *tree_conflict;
512 svn_wc__info2_t *info;
513 const apr_array_header_t *conflicts;
515 svn_pool_clear(iterpool);
517 SVN_ERR(build_info_for_unversioned(&info, iterpool));
521 SVN_ERR(svn_wc__db_read_repos_info(NULL, NULL,
528 scratch_pool, iterpool));
531 info->repos_root_URL = repos_root_url;
532 info->repos_UUID = repos_uuid;
534 SVN_ERR(svn_wc__read_conflicts(&conflicts, NULL,
535 wc_ctx->db, this_abspath,
536 FALSE /* create tempfiles */,
537 FALSE /* only tree conflicts */,
538 iterpool, iterpool));
539 if (! conflicts || ! conflicts->nelts)
542 tree_conflict = APR_ARRAY_IDX(conflicts, 0,
543 const svn_wc_conflict_description2_t *);
545 if (!depth_includes(local_abspath, depth, tree_conflict->local_abspath,
546 tree_conflict->node_kind, iterpool))
549 info->wc_info->conflicts = conflicts;
550 SVN_ERR(receiver(receiver_baton, this_abspath, info, iterpool));
552 svn_pool_destroy(iterpool);