2 * entries.c : manipulating the administrative `entries' file.
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
13 * http://www.apache.org/licenses/LICENSE-2.0
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
21 * ====================================================================
27 #include <apr_strings.h>
29 #include "svn_error.h"
30 #include "svn_types.h"
32 #include "svn_pools.h"
33 #include "svn_dirent_uri.h"
35 #include "svn_ctype.h"
36 #include "svn_string.h"
40 #include "adm_files.h"
41 #include "conflicts.h"
44 #include "tree_conflicts.h"
46 #include "wc-queries.h" /* for STMT_* */
48 #include "svn_private_config.h"
49 #include "private/svn_wc_private.h"
50 #include "private/svn_sqlite.h"
52 #define MAYBE_ALLOC(x,p) ((x) ? (x) : apr_pcalloc((p), sizeof(*(x))))
55 /* Temporary structures which mirror the tables in wc-metadata.sql.
56 For detailed descriptions of each field, see that file. */
57 typedef struct db_node_t {
59 const char *local_relpath;
62 const char *repos_relpath;
63 const char *parent_relpath;
64 svn_wc__db_status_t presence;
65 svn_revnum_t revision;
67 svn_checksum_t *checksum;
68 svn_filesize_t recorded_size;
69 svn_revnum_t changed_rev;
70 apr_time_t changed_date;
71 const char *changed_author;
73 apr_time_t recorded_time;
74 apr_hash_t *properties;
75 svn_boolean_t file_external;
76 apr_array_header_t *inherited_props;
79 typedef struct db_actual_node_t {
81 const char *local_relpath;
82 const char *parent_relpath;
83 apr_hash_t *properties;
84 const char *conflict_old;
85 const char *conflict_new;
86 const char *conflict_working;
87 const char *prop_reject;
88 const char *changelist;
89 /* ### enum for text_mod */
90 const char *tree_conflict_data;
95 /*** reading and writing the entries file ***/
99 static svn_wc_entry_t *
100 alloc_entry(apr_pool_t *pool)
102 svn_wc_entry_t *entry = apr_pcalloc(pool, sizeof(*entry));
103 entry->revision = SVN_INVALID_REVNUM;
104 entry->copyfrom_rev = SVN_INVALID_REVNUM;
105 entry->cmt_rev = SVN_INVALID_REVNUM;
106 entry->kind = svn_node_none;
107 entry->working_size = SVN_WC_ENTRY_WORKING_SIZE_UNKNOWN;
108 entry->depth = svn_depth_infinity;
109 entry->file_external_peg_rev.kind = svn_opt_revision_unspecified;
110 entry->file_external_rev.kind = svn_opt_revision_unspecified;
115 /* Is the entry in a 'hidden' state in the sense of the 'show_hidden'
116 * switches on svn_wc_entries_read(), svn_wc_walk_entries*(), etc.? */
118 svn_wc__entry_is_hidden(svn_boolean_t *hidden, const svn_wc_entry_t *entry)
120 /* In English, the condition is: "the entry is not present, and I haven't
121 scheduled something over the top of it." */
124 || entry->depth == svn_depth_exclude)
126 /* These kinds of nodes cannot be marked for deletion (which also
127 means no "replace" either). */
128 SVN_ERR_ASSERT(entry->schedule == svn_wc_schedule_add
129 || entry->schedule == svn_wc_schedule_normal);
131 /* Hidden if something hasn't been added over it.
133 ### is this even possible with absent or excluded nodes? */
134 *hidden = entry->schedule != svn_wc_schedule_add;
143 /* Hit the database to check the file external information for the given
144 entry. The entry will be modified in place. */
146 check_file_external(svn_wc_entry_t *entry,
148 const char *local_abspath,
149 const char *wri_abspath,
150 apr_pool_t *result_pool,
151 apr_pool_t *scratch_pool)
153 svn_wc__db_status_t status;
154 svn_node_kind_t kind;
155 const char *repos_relpath;
156 svn_revnum_t peg_revision;
157 svn_revnum_t revision;
160 err = svn_wc__db_external_read(&status, &kind, NULL, NULL, NULL,
161 &repos_relpath, &peg_revision, &revision,
162 db, local_abspath, wri_abspath,
163 result_pool, scratch_pool);
167 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
168 return svn_error_trace(err);
170 svn_error_clear(err);
174 if (status == svn_wc__db_status_normal
175 && kind == svn_node_file)
177 entry->file_external_path = repos_relpath;
178 if (SVN_IS_VALID_REVNUM(peg_revision))
180 entry->file_external_peg_rev.kind = svn_opt_revision_number;
181 entry->file_external_peg_rev.value.number = peg_revision;
182 entry->file_external_rev = entry->file_external_peg_rev;
184 if (SVN_IS_VALID_REVNUM(revision))
186 entry->file_external_rev.kind = svn_opt_revision_number;
187 entry->file_external_rev.value.number = revision;
195 /* Fill in the following fields of ENTRY:
206 Return: KIND, REPOS_RELPATH, CHECKSUM
209 get_info_for_deleted(svn_wc_entry_t *entry,
210 svn_node_kind_t *kind,
211 const char **repos_relpath,
212 const svn_checksum_t **checksum,
213 svn_wc__db_lock_t **lock,
215 const char *entry_abspath,
216 const svn_wc_entry_t *parent_entry,
217 svn_boolean_t have_base,
218 svn_boolean_t have_more_work,
219 apr_pool_t *result_pool,
220 apr_pool_t *scratch_pool)
222 if (have_base && !have_more_work)
224 /* This is the delete of a BASE node */
225 SVN_ERR(svn_wc__db_base_get_info(NULL, kind,
237 &entry->has_props, NULL,
246 const char *work_del_abspath;
247 const char *parent_repos_relpath;
248 const char *parent_abspath;
250 /* This is a deleted child of a copy/move-here,
251 so we need to scan up the WORKING tree to find the root of
252 the deletion. Then examine its parent to discover its
253 future location in the repository. */
254 SVN_ERR(svn_wc__db_read_pristine_info(NULL, kind,
261 &entry->has_props, NULL,
266 /* working_size and text_time unavailable */
268 SVN_ERR(svn_wc__db_scan_deletion(NULL,
270 &work_del_abspath, NULL,
272 scratch_pool, scratch_pool));
274 SVN_ERR_ASSERT(work_del_abspath != NULL);
275 parent_abspath = svn_dirent_dirname(work_del_abspath, scratch_pool);
277 /* The parent directory of the delete root must be added, so we
278 can find the required information there */
279 SVN_ERR(svn_wc__db_scan_addition(NULL, NULL,
280 &parent_repos_relpath,
283 NULL, NULL, NULL, NULL,
285 result_pool, scratch_pool));
287 /* Now glue it all together */
288 *repos_relpath = svn_relpath_join(parent_repos_relpath,
289 svn_dirent_is_child(parent_abspath,
295 /* Even though this is the delete of a WORKING node, there might still
296 be a BASE node somewhere below with an interesting revision */
299 svn_wc__db_status_t status;
300 SVN_ERR(svn_wc__db_base_get_info(&status, NULL, &entry->revision,
301 NULL, NULL, NULL, NULL, NULL, NULL,
302 NULL, NULL, NULL, lock, NULL, NULL,
305 result_pool, scratch_pool));
307 if (status == svn_wc__db_status_not_present)
308 entry->deleted = TRUE;
312 /* Do some extra work for the child nodes. */
313 if (!SVN_IS_VALID_REVNUM(entry->revision) && parent_entry != NULL)
315 /* For child nodes without a revision, pick up the parent's
317 entry->revision = parent_entry->revision;
325 * Encode tree conflict descriptions into a single string.
327 * Set *CONFLICT_DATA to a string, allocated in POOL, that encodes the tree
328 * conflicts in CONFLICTS in a form suitable for storage in a single string
329 * field in a WC entry. CONFLICTS is a hash of zero or more pointers to
330 * svn_wc_conflict_description2_t objects, index by their basenames. All of the
331 * conflict victim paths must be siblings.
333 * Do all allocations in POOL.
335 * @see svn_wc__read_tree_conflicts()
338 write_tree_conflicts(const char **conflict_data,
339 apr_hash_t *conflicts,
342 svn_skel_t *skel = svn_skel__make_empty_list(pool);
343 apr_hash_index_t *hi;
345 for (hi = apr_hash_first(pool, conflicts); hi; hi = apr_hash_next(hi))
349 SVN_ERR(svn_wc__serialize_conflict(&c_skel, svn__apr_hash_index_val(hi),
351 svn_skel__prepend(c_skel, skel);
354 *conflict_data = svn_skel__unparse(skel, pool)->data;
360 /* Read one entry from wc_db. It will be allocated in RESULT_POOL and
361 returned in *NEW_ENTRY.
363 DIR_ABSPATH is the name of the directory to read this entry from, and
364 it will be named NAME (use "" for "this dir").
366 DB specifies the wc_db database, and WC_ID specifies which working copy
367 this information is being read from.
369 If this node is "this dir", then PARENT_ENTRY should be NULL. Otherwise,
370 it should refer to the entry for the child's parent directory.
372 Temporary allocations are made in SCRATCH_POOL. */
374 read_one_entry(const svn_wc_entry_t **new_entry,
377 const char *dir_abspath,
379 const svn_wc_entry_t *parent_entry,
380 apr_pool_t *result_pool,
381 apr_pool_t *scratch_pool)
383 svn_node_kind_t kind;
384 svn_wc__db_status_t status;
385 svn_wc__db_lock_t *lock;
386 const char *repos_relpath;
387 const svn_checksum_t *checksum;
388 svn_filesize_t translated_size;
389 svn_wc_entry_t *entry = alloc_entry(result_pool);
390 const char *entry_abspath;
391 const char *original_repos_relpath;
392 const char *original_root_url;
393 svn_boolean_t conflicted;
394 svn_boolean_t have_base;
395 svn_boolean_t have_more_work;
399 entry_abspath = svn_dirent_join(dir_abspath, entry->name, scratch_pool);
401 SVN_ERR(svn_wc__db_read_info(
414 &original_repos_relpath,
417 &entry->copyfrom_rev,
424 &entry->has_props /* have_props */,
425 &entry->has_prop_mods /* props_mod */,
428 NULL /* have_work */,
434 if (entry->has_prop_mods)
435 entry->has_props = TRUE;
437 if (strcmp(entry->name, SVN_WC_ENTRY_THIS_DIR) == 0)
439 /* get the tree conflict data. */
440 apr_hash_t *tree_conflicts = NULL;
441 const apr_array_header_t *conflict_victims;
444 SVN_ERR(svn_wc__db_read_conflict_victims(&conflict_victims, db,
449 for (k = 0; k < conflict_victims->nelts; k++)
452 const apr_array_header_t *child_conflicts;
453 const char *child_name;
454 const char *child_abspath;
456 child_name = APR_ARRAY_IDX(conflict_victims, k, const char *);
457 child_abspath = svn_dirent_join(dir_abspath, child_name,
460 SVN_ERR(svn_wc__read_conflicts(&child_conflicts,
462 FALSE /* create tempfiles */,
463 scratch_pool, scratch_pool));
465 for (j = 0; j < child_conflicts->nelts; j++)
467 const svn_wc_conflict_description2_t *conflict =
468 APR_ARRAY_IDX(child_conflicts, j,
469 svn_wc_conflict_description2_t *);
471 if (conflict->kind == svn_wc_conflict_kind_tree)
474 tree_conflicts = apr_hash_make(scratch_pool);
475 svn_hash_sets(tree_conflicts, child_name, conflict);
482 SVN_ERR(write_tree_conflicts(&entry->tree_conflict_data,
483 tree_conflicts, result_pool));
487 if (status == svn_wc__db_status_normal
488 || status == svn_wc__db_status_incomplete)
490 /* Plain old BASE node. */
491 entry->schedule = svn_wc_schedule_normal;
493 /* Grab inherited repository information, if necessary. */
494 if (repos_relpath == NULL)
496 SVN_ERR(svn_wc__db_scan_base_repos(&repos_relpath,
505 entry->incomplete = (status == svn_wc__db_status_incomplete);
507 else if (status == svn_wc__db_status_deleted)
509 svn_node_kind_t path_kind;
511 /* ### we don't have to worry about moves, so this is a delete. */
512 entry->schedule = svn_wc_schedule_delete;
514 /* If there are multiple working layers or no BASE layer, then
515 this is a WORKING delete or WORKING not-present. */
516 if (have_more_work || !have_base)
517 entry->copied = TRUE;
518 else if (have_base && !have_more_work)
519 entry->copied = FALSE;
522 const char *work_del_abspath;
523 SVN_ERR(svn_wc__db_scan_deletion(NULL, NULL,
524 &work_del_abspath, NULL,
526 scratch_pool, scratch_pool));
528 if (work_del_abspath)
529 entry->copied = TRUE;
532 /* If there is still a directory on-disk we keep it, if not it is
533 already deleted. Simple, isn't it?
535 Before single-db we had to keep the administative area alive until
536 after the commit really deletes it. Setting keep alive stopped the
537 commit processing from deleting the directory. We don't delete it
538 any more, so all we have to do is provide some 'sane' value.
540 SVN_ERR(svn_io_check_path(entry_abspath, &path_kind, scratch_pool));
541 entry->keep_local = (path_kind == svn_node_dir);
543 else if (status == svn_wc__db_status_added)
545 svn_wc__db_status_t work_status;
546 const char *op_root_abspath;
547 const char *scanned_original_relpath;
548 svn_revnum_t original_revision;
550 /* For child nodes, pick up the parent's revision. */
551 if (*entry->name != '\0')
553 assert(parent_entry != NULL);
554 assert(entry->revision == SVN_INVALID_REVNUM);
556 entry->revision = parent_entry->revision;
561 svn_wc__db_status_t base_status;
563 /* ENTRY->REVISION is overloaded. When a node is schedule-add
564 or -replace, then REVISION refers to the BASE node's revision
565 that is being overwritten. We need to fetch it now. */
566 SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL,
571 NULL, NULL, NULL, NULL,
576 if (base_status == svn_wc__db_status_not_present)
578 /* The underlying node is DELETED in this revision. */
579 entry->deleted = TRUE;
581 /* This is an add since there isn't a node to replace. */
582 entry->schedule = svn_wc_schedule_add;
585 entry->schedule = svn_wc_schedule_replace;
589 /* There is NO 'not-present' BASE_NODE for this node.
590 Therefore, we are looking at some kind of add/copy
591 rather than a replace. */
593 /* ### if this looks like a plain old add, then rev=0. */
594 if (!SVN_IS_VALID_REVNUM(entry->copyfrom_rev)
595 && !SVN_IS_VALID_REVNUM(entry->cmt_rev))
598 entry->schedule = svn_wc_schedule_add;
601 /* If we don't have "real" data from the entry (obstruction),
602 then we cannot begin a scan for data. The original node may
603 have important data. Set up stuff to kill that idea off,
604 and finish up this entry. */
606 SVN_ERR(svn_wc__db_scan_addition(&work_status,
611 &scanned_original_relpath,
612 NULL, NULL, /* original_root|uuid */
616 result_pool, scratch_pool));
618 /* In wc.db we want to keep the valid revision of the not-present
619 BASE_REV, but when we used entries we set the revision to 0
620 when adding a new node over a not present base node. */
621 if (work_status == svn_wc__db_status_added
626 if (!SVN_IS_VALID_REVNUM(entry->cmt_rev)
627 && scanned_original_relpath == NULL)
629 /* There is NOT a last-changed revision (last-changed date and
630 author may be unknown, but we can always check the rev).
631 The absence of a revision implies this node was added WITHOUT
632 any history. Avoid the COPIED checks in the else block. */
633 /* ### scan_addition may need to be updated to avoid returning
634 ### status_copied in this case. */
636 /* For backwards-compatiblity purposes we treat moves just like
638 else if (work_status == svn_wc__db_status_copied ||
639 work_status == svn_wc__db_status_moved_here)
641 entry->copied = TRUE;
643 /* If this is a child of a copied subtree, then it should be
645 if (original_repos_relpath == NULL)
647 /* ### what if there is a BASE node under there? */
648 entry->schedule = svn_wc_schedule_normal;
651 /* Copied nodes need to mirror their copyfrom_rev, if they
652 don't have a revision of their own already. */
653 if (!SVN_IS_VALID_REVNUM(entry->revision)
654 || entry->revision == 0 /* added */)
655 entry->revision = original_revision;
658 /* Does this node have copyfrom_* information? */
659 if (scanned_original_relpath != NULL)
661 svn_boolean_t is_copied_child;
662 svn_boolean_t is_mixed_rev = FALSE;
664 SVN_ERR_ASSERT(work_status == svn_wc__db_status_copied ||
665 work_status == svn_wc__db_status_moved_here);
667 /* If this node inherits copyfrom information from an
668 ancestor node, then it must be a copied child. */
669 is_copied_child = (original_repos_relpath == NULL);
671 /* If this node has copyfrom information on it, then it may
672 be an actual copy-root, or it could be participating in
673 a mixed-revision copied tree. So if we don't already know
674 this is a copied child, then we need to look for this
675 mixed-revision situation. */
676 if (!is_copied_child)
678 const char *parent_abspath;
680 const char *parent_repos_relpath;
681 const char *parent_root_url;
683 /* When we insert entries into the database, we will
684 construct additional copyfrom records for mixed-revision
685 copies. The old entries would simply record the different
686 revision in the entry->revision field. That is not
687 available within wc-ng, so additional copies are made
688 (see the logic inside write_entry()). However, when
689 reading these back *out* of the database, the additional
690 copies look like new "Added" nodes rather than a simple
691 mixed-rev working copy.
693 That would be a behavior change if we did not compensate.
694 If there is copyfrom information for this node, then the
695 code below looks at the parent to detect if it *also* has
696 copyfrom information, and if the copyfrom_url would align
697 properly. If it *does*, then we omit storing copyfrom_url
698 and copyfrom_rev (ie. inherit the copyfrom info like a
699 normal child), and update entry->revision with the
700 copyfrom_rev in order to (re)create the mixed-rev copied
701 subtree that was originally presented for storage. */
703 /* Get the copyfrom information from our parent.
705 Note that the parent could be added/copied/moved-here.
706 There is no way for it to be deleted/moved-away and
707 have *this* node appear as copied. */
708 parent_abspath = svn_dirent_dirname(entry_abspath,
710 err = svn_wc__db_scan_addition(NULL,
713 &parent_repos_relpath,
721 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
722 return svn_error_trace(err);
723 svn_error_clear(err);
725 else if (parent_root_url != NULL
726 && strcmp(original_root_url, parent_root_url) == 0)
728 const char *relpath_to_entry = svn_dirent_is_child(
729 op_root_abspath, entry_abspath, NULL);
730 const char *entry_repos_relpath = svn_relpath_join(
731 parent_repos_relpath, relpath_to_entry, scratch_pool);
733 /* The copyfrom repos roots matched.
735 Now we look to see if the copyfrom path of the parent
736 would align with our own path. If so, then it means
737 this copyfrom was spontaneously created and inserted
738 for mixed-rev purposes and can be eliminated without
739 changing the semantics of a mixed-rev copied subtree.
741 See notes/api-errata/wc003.txt for some additional
742 detail, and potential issues. */
743 if (strcmp(entry_repos_relpath,
744 original_repos_relpath) == 0)
746 is_copied_child = TRUE;
754 /* We won't be settig the copyfrom_url, yet need to
755 clear out the copyfrom_rev. Thus, this node becomes a
756 child of a copied subtree (rather than its own root). */
757 entry->copyfrom_rev = SVN_INVALID_REVNUM;
759 /* Children in a copied subtree are schedule normal
760 since we don't plan to actually *do* anything with
761 them. Their operation is implied by ancestors. */
762 entry->schedule = svn_wc_schedule_normal;
764 /* And *finally* we turn this entry into the mixed
765 revision node that it was intended to be. This
766 node's revision is taken from the copyfrom record
767 that we spontaneously constructed. */
769 entry->revision = original_revision;
771 else if (original_repos_relpath != NULL)
773 entry->copyfrom_url =
774 svn_path_url_add_component2(original_root_url,
775 original_repos_relpath,
780 /* NOTE: if original_repos_relpath == NULL, then the
781 second call to scan_addition() will not have occurred.
782 Thus, this use of OP_ROOT_ABSPATH still contains the
783 original value where we fetched a value for
784 SCANNED_REPOS_RELPATH. */
785 const char *relpath_to_entry = svn_dirent_is_child(
786 op_root_abspath, entry_abspath, NULL);
787 const char *entry_repos_relpath = svn_relpath_join(
788 scanned_original_relpath, relpath_to_entry, scratch_pool);
790 entry->copyfrom_url =
791 svn_path_url_add_component2(original_root_url,
797 else if (status == svn_wc__db_status_not_present)
799 /* ### buh. 'deleted' nodes are actually supposed to be
800 ### schedule "normal" since we aren't going to actually *do*
801 ### anything to this node at commit time. */
802 entry->schedule = svn_wc_schedule_normal;
803 entry->deleted = TRUE;
805 else if (status == svn_wc__db_status_server_excluded)
807 entry->absent = TRUE;
809 else if (status == svn_wc__db_status_excluded)
811 entry->schedule = svn_wc_schedule_normal;
812 entry->depth = svn_depth_exclude;
816 /* ### we should have handled all possible status values. */
817 SVN_ERR_MALFUNCTION();
820 /* ### higher levels want repos information about deleted nodes, even
821 ### tho they are not "part of" a repository any more. */
822 if (entry->schedule == svn_wc_schedule_delete)
824 SVN_ERR(get_info_for_deleted(entry,
831 have_base, have_more_work,
832 result_pool, scratch_pool));
835 /* ### default to the infinite depth if we don't know it. */
836 if (entry->depth == svn_depth_unknown)
837 entry->depth = svn_depth_infinity;
839 if (kind == svn_node_dir)
840 entry->kind = svn_node_dir;
841 else if (kind == svn_node_file)
842 entry->kind = svn_node_file;
843 else if (kind == svn_node_symlink)
844 entry->kind = svn_node_file; /* ### no symlink kind */
846 entry->kind = svn_node_unknown;
848 /* We should always have a REPOS_RELPATH, except for:
850 - certain obstructed nodes
855 ### the last three should probably have an "implied" REPOS_RELPATH
857 SVN_ERR_ASSERT(repos_relpath != NULL
858 || entry->schedule == svn_wc_schedule_delete
859 || status == svn_wc__db_status_not_present
860 || status == svn_wc__db_status_server_excluded
861 || status == svn_wc__db_status_excluded);
863 entry->url = svn_path_url_add_component2(entry->repos,
869 /* We got a SHA-1, get the corresponding MD-5. */
870 if (checksum->kind != svn_checksum_md5)
871 SVN_ERR(svn_wc__db_pristine_get_md5(&checksum, db,
872 entry_abspath, checksum,
873 scratch_pool, scratch_pool));
875 SVN_ERR_ASSERT(checksum->kind == svn_checksum_md5);
876 entry->checksum = svn_checksum_to_cstring(checksum, result_pool);
881 svn_skel_t *conflict;
882 svn_boolean_t text_conflicted;
883 svn_boolean_t prop_conflicted;
884 SVN_ERR(svn_wc__db_read_conflict(&conflict, db, entry_abspath,
885 scratch_pool, scratch_pool));
887 SVN_ERR(svn_wc__conflict_read_info(NULL, NULL, &text_conflicted,
888 &prop_conflicted, NULL,
889 db, dir_abspath, conflict,
890 scratch_pool, scratch_pool));
894 const char *my_abspath;
895 const char *their_old_abspath;
896 const char *their_abspath;
897 SVN_ERR(svn_wc__conflict_read_text_conflict(&my_abspath,
901 conflict, scratch_pool,
905 entry->conflict_wrk = svn_dirent_basename(my_abspath, result_pool);
907 if (their_old_abspath)
908 entry->conflict_old = svn_dirent_basename(their_old_abspath,
912 entry->conflict_new = svn_dirent_basename(their_abspath,
918 const char *prej_abspath;
920 SVN_ERR(svn_wc__conflict_read_prop_conflict(&prej_abspath, NULL,
923 conflict, scratch_pool,
927 entry->prejfile = svn_dirent_basename(prej_abspath, result_pool);
933 entry->lock_token = lock->token;
934 entry->lock_owner = lock->owner;
935 entry->lock_comment = lock->comment;
936 entry->lock_creation_date = lock->date;
939 /* Let's check for a file external. ugh. */
940 if (status == svn_wc__db_status_normal
941 && kind == svn_node_file)
942 SVN_ERR(check_file_external(entry, db, entry_abspath, dir_abspath,
943 result_pool, scratch_pool));
945 entry->working_size = translated_size;
952 /* Read entries for PATH/LOCAL_ABSPATH from DB. The entries
953 will be allocated in RESULT_POOL, with temporary allocations in
954 SCRATCH_POOL. The entries are returned in RESULT_ENTRIES. */
956 read_entries_new(apr_hash_t **result_entries,
958 const char *local_abspath,
959 apr_pool_t *result_pool,
960 apr_pool_t *scratch_pool)
963 const apr_array_header_t *children;
964 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
966 const svn_wc_entry_t *parent_entry;
967 apr_int64_t wc_id = 1; /* ### hacky. should remove. */
969 entries = apr_hash_make(result_pool);
971 SVN_ERR(read_one_entry(&parent_entry, db, wc_id, local_abspath,
973 NULL /* parent_entry */,
974 result_pool, iterpool));
975 svn_hash_sets(entries, "", parent_entry);
977 /* Use result_pool so that the child names (used by reference, rather
978 than copied) appear in result_pool. */
979 SVN_ERR(svn_wc__db_read_children(&children, db,
981 result_pool, iterpool));
982 for (i = children->nelts; i--; )
984 const char *name = APR_ARRAY_IDX(children, i, const char *);
985 const svn_wc_entry_t *entry;
987 svn_pool_clear(iterpool);
989 SVN_ERR(read_one_entry(&entry,
990 db, wc_id, local_abspath, name, parent_entry,
991 result_pool, iterpool));
992 svn_hash_sets(entries, entry->name, entry);
995 svn_pool_destroy(iterpool);
997 *result_entries = entries;
1003 /* Read a pair of entries from wc_db in the directory DIR_ABSPATH. Return
1004 the directory's entry in *PARENT_ENTRY and NAME's entry in *ENTRY. The
1005 two returned pointers will be the same if NAME=="" ("this dir").
1007 The parent entry must exist.
1009 The requested entry MAY exist. If it does not, then NULL will be returned.
1011 The resulting entries are allocated in RESULT_POOL, and all temporary
1012 allocations are made in SCRATCH_POOL. */
1013 static svn_error_t *
1014 read_entry_pair(const svn_wc_entry_t **parent_entry,
1015 const svn_wc_entry_t **entry,
1017 const char *dir_abspath,
1019 apr_pool_t *result_pool,
1020 apr_pool_t *scratch_pool)
1022 apr_int64_t wc_id = 1; /* ### hacky. should remove. */
1024 SVN_ERR(read_one_entry(parent_entry, db, wc_id, dir_abspath,
1026 NULL /* parent_entry */,
1027 result_pool, scratch_pool));
1029 /* If we need the entry for "this dir", then return the parent_entry
1030 in both outputs. Otherwise, read the child node. */
1033 /* If the retrieved node is a FILE, then we have a problem. We asked
1034 for a directory. This implies there is an obstructing, unversioned
1035 directory where a FILE should be. We navigated from the obstructing
1036 subdir up to the parent dir, then returned the FILE found there.
1038 Let's return WC_MISSING cuz the caller thought we had a dir, but
1039 that (versioned subdir) isn't there. */
1040 if ((*parent_entry)->kind == svn_node_file)
1042 *parent_entry = NULL;
1043 return svn_error_createf(SVN_ERR_WC_MISSING, NULL,
1044 _("'%s' is not a versioned working copy"),
1045 svn_dirent_local_style(dir_abspath,
1049 *entry = *parent_entry;
1053 const apr_array_header_t *children;
1056 /* Default to not finding the child. */
1059 /* Determine whether the parent KNOWS about this child. If it does
1060 not, then we should not attempt to look for it.
1062 For example: the parent doesn't "know" about the child, but the
1063 versioned directory *does* exist on disk. We don't want to look
1064 into that subdir. */
1065 SVN_ERR(svn_wc__db_read_children(&children, db, dir_abspath,
1066 scratch_pool, scratch_pool));
1067 for (i = children->nelts; i--; )
1069 const char *child = APR_ARRAY_IDX(children, i, const char *);
1071 if (strcmp(child, name) == 0)
1075 err = read_one_entry(entry,
1076 db, wc_id, dir_abspath, name, *parent_entry,
1077 result_pool, scratch_pool);
1080 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
1081 return svn_error_trace(err);
1083 /* No problem. Clear the error and leave the default value
1085 svn_error_clear(err);
1088 /* Found it. No need to keep searching. */
1092 /* if the loop ends without finding a child, then we have the default
1093 ENTRY value of NULL. */
1096 return SVN_NO_ERROR;
1101 static svn_error_t *
1102 read_entries(apr_hash_t **entries,
1104 const char *wcroot_abspath,
1105 apr_pool_t *result_pool,
1106 apr_pool_t *scratch_pool)
1110 SVN_ERR(svn_wc__db_temp_get_format(&wc_format, db, wcroot_abspath,
1113 if (wc_format < SVN_WC__WC_NG_VERSION)
1114 return svn_error_trace(svn_wc__read_entries_old(entries,
1119 return svn_error_trace(read_entries_new(entries, db, wcroot_abspath,
1120 result_pool, scratch_pool));
1124 /* For a given LOCAL_ABSPATH, using DB, set *ADM_ABSPATH to the directory in
1125 which the entry information is located, and *ENTRY_NAME to the entry name
1126 to access that entry.
1128 KIND is as in svn_wc__get_entry().
1130 Return the results in RESULT_POOL and use SCRATCH_POOL for temporary
1132 static svn_error_t *
1133 get_entry_access_info(const char **adm_abspath,
1134 const char **entry_name,
1136 const char *local_abspath,
1137 svn_node_kind_t kind,
1138 apr_pool_t *result_pool,
1139 apr_pool_t *scratch_pool)
1141 svn_wc_adm_access_t *adm_access;
1142 svn_boolean_t read_from_subdir = FALSE;
1144 /* If the caller didn't know the node kind, then stat the path. Maybe
1145 it is really there, and we can speed up the steps below. */
1146 if (kind == svn_node_unknown)
1148 svn_node_kind_t on_disk;
1150 /* Do we already have an access baton for LOCAL_ABSPATH? */
1151 adm_access = svn_wc__adm_retrieve_internal2(db, local_abspath,
1155 /* Sweet. The node is a directory. */
1156 on_disk = svn_node_dir;
1160 svn_boolean_t special;
1162 /* What's on disk? */
1163 SVN_ERR(svn_io_check_special_path(local_abspath, &on_disk, &special,
1167 if (on_disk != svn_node_dir)
1169 /* If this is *anything* besides a directory (FILE, NONE, or
1170 UNKNOWN), then we cannot treat it as a versioned directory
1171 containing entries to read. Leave READ_FROM_SUBDIR as FALSE,
1172 so that the parent will be examined.
1174 For NONE and UNKNOWN, it may be that metadata exists for the
1175 node, even though on-disk is unhelpful.
1177 If NEED_PARENT_STUB is TRUE, and the entry is not a DIRECTORY,
1180 If NEED_PARENT_STUB if FALSE, and we successfully read a stub,
1181 then this on-disk node is obstructing the read. */
1185 /* We found a directory for this UNKNOWN node. Determine whether
1186 we need to read inside it. */
1187 read_from_subdir = TRUE;
1190 else if (kind == svn_node_dir)
1192 read_from_subdir = TRUE;
1195 if (read_from_subdir)
1197 /* KIND must be a DIR or UNKNOWN (and we found a subdir). We want
1198 the "real" data, so treat LOCAL_ABSPATH as a versioned directory. */
1199 *adm_abspath = apr_pstrdup(result_pool, local_abspath);
1204 /* FILE node needs to read the parent directory. Or a DIR node
1205 needs to read from the parent to get at the stub entry. Or this
1206 is an UNKNOWN node, and we need to examine the parent. */
1207 svn_dirent_split(adm_abspath, entry_name, local_abspath, result_pool);
1210 return SVN_NO_ERROR;
1215 svn_wc__get_entry(const svn_wc_entry_t **entry,
1217 const char *local_abspath,
1218 svn_boolean_t allow_unversioned,
1219 svn_node_kind_t kind,
1220 apr_pool_t *result_pool,
1221 apr_pool_t *scratch_pool)
1223 const char *dir_abspath;
1224 const char *entry_name;
1226 SVN_ERR(get_entry_access_info(&dir_abspath, &entry_name, db, local_abspath,
1227 kind, scratch_pool, scratch_pool));
1230 const svn_wc_entry_t *parent_entry;
1233 /* NOTE: if KIND is UNKNOWN and we decided to examine the *parent*
1234 directory, then it is possible we moved out of the working copy.
1235 If the on-disk node is a DIR, and we asked for a stub, then we
1236 obviously can't provide that (parent has no info). If the on-disk
1237 node is a FILE/NONE/UNKNOWN, then it is obstructing the real
1238 LOCAL_ABSPATH (or it was never a versioned item). In all these
1239 cases, the read_entries() will (properly) throw an error.
1241 NOTE: if KIND is a DIR and we asked for the real data, but it is
1242 obstructed on-disk by some other node kind (NONE, FILE, UNKNOWN),
1243 then this will throw an error. */
1245 err = read_entry_pair(&parent_entry, entry,
1246 db, dir_abspath, entry_name,
1247 result_pool, scratch_pool);
1250 if (err->apr_err != SVN_ERR_WC_MISSING || kind != svn_node_unknown
1251 || *entry_name != '\0')
1252 return svn_error_trace(err);
1253 svn_error_clear(err);
1255 /* The caller didn't know the node type, we saw a directory there,
1256 we attempted to read IN that directory, and then wc_db reports
1257 that it is NOT a working copy directory. It is possible that
1258 one of two things has happened:
1260 1) a directory is obstructing a file in the parent
1261 2) the (versioned) directory's contents have been removed
1263 Let's assume situation (1); if that is true, then we can just
1264 return the newly-found data.
1266 If we assumed (2), then a valid result still won't help us
1267 since the caller asked for the actual contents, not the stub
1268 (which is why we read *into* the directory). However, if we
1269 assume (1) and get back a stub, then we have verified a
1270 missing, versioned directory, and can return an error
1273 Redo the fetch, but "insist" we are trying to find a file.
1274 This will read from the parent directory of the "file". */
1275 err = svn_wc__get_entry(entry, db, local_abspath, allow_unversioned,
1276 svn_node_file, result_pool, scratch_pool);
1277 if (err == SVN_NO_ERROR)
1278 return SVN_NO_ERROR;
1279 if (err->apr_err != SVN_ERR_NODE_UNEXPECTED_KIND)
1280 return svn_error_trace(err);
1281 svn_error_clear(err);
1283 /* We asked for a FILE, but the node found is a DIR. Thus, we
1284 are looking at a stub. Originally, we tried to read into the
1285 subdir because NEED_PARENT_STUB is FALSE. The stub we just
1286 read is not going to work for the caller, so inform them of
1287 the missing subdirectory. */
1288 SVN_ERR_ASSERT(*entry != NULL && (*entry)->kind == svn_node_dir);
1289 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
1290 _("Admin area of '%s' is missing"),
1291 svn_dirent_local_style(local_abspath,
1298 if (allow_unversioned)
1299 return SVN_NO_ERROR;
1300 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
1301 _("'%s' is not under version control"),
1302 svn_dirent_local_style(local_abspath,
1306 /* The caller had the wrong information. */
1307 if ((kind == svn_node_file && (*entry)->kind != svn_node_file)
1308 || (kind == svn_node_dir && (*entry)->kind != svn_node_dir))
1309 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
1310 _("'%s' is not of the right kind"),
1311 svn_dirent_local_style(local_abspath,
1314 return SVN_NO_ERROR;
1317 /* TODO ### Rewrite doc string to mention ENTRIES_ALL; not ADM_ACCESS.
1319 Prune the deleted entries from the cached entries in ADM_ACCESS, and
1320 return that collection in *ENTRIES_PRUNED. SCRATCH_POOL is used for local,
1321 short term, memory allocation, RESULT_POOL for permanent stuff. */
1322 static svn_error_t *
1323 prune_deleted(apr_hash_t **entries_pruned,
1324 apr_hash_t *entries_all,
1325 apr_pool_t *result_pool,
1326 apr_pool_t *scratch_pool)
1328 apr_hash_index_t *hi;
1332 *entries_pruned = NULL;
1333 return SVN_NO_ERROR;
1336 /* I think it will be common for there to be no deleted entries, so
1337 it is worth checking for that case as we can optimise it. */
1338 for (hi = apr_hash_first(scratch_pool, entries_all);
1340 hi = apr_hash_next(hi))
1342 svn_boolean_t hidden;
1344 SVN_ERR(svn_wc__entry_is_hidden(&hidden,
1345 svn__apr_hash_index_val(hi)));
1352 /* There are no deleted entries, so we can use the full hash */
1353 *entries_pruned = entries_all;
1354 return SVN_NO_ERROR;
1357 /* Construct pruned hash without deleted entries */
1358 *entries_pruned = apr_hash_make(result_pool);
1359 for (hi = apr_hash_first(scratch_pool, entries_all);
1361 hi = apr_hash_next(hi))
1363 const void *key = svn__apr_hash_index_key(hi);
1364 const svn_wc_entry_t *entry = svn__apr_hash_index_val(hi);
1365 svn_boolean_t hidden;
1367 SVN_ERR(svn_wc__entry_is_hidden(&hidden, entry));
1369 svn_hash_sets(*entries_pruned, key, entry);
1372 return SVN_NO_ERROR;
1375 struct entries_read_baton_t
1377 apr_hash_t **new_entries;
1379 const char *local_abspath;
1380 apr_pool_t *result_pool;
1383 static svn_error_t *
1384 entries_read_txn(void *baton, svn_sqlite__db_t *db, apr_pool_t *scratch_pool)
1386 struct entries_read_baton_t *erb = baton;
1388 SVN_ERR(read_entries(erb->new_entries, erb->db, erb->local_abspath,
1389 erb->result_pool, scratch_pool));
1395 svn_wc__entries_read_internal(apr_hash_t **entries,
1396 svn_wc_adm_access_t *adm_access,
1397 svn_boolean_t show_hidden,
1400 apr_hash_t *new_entries;
1402 new_entries = svn_wc__adm_access_entries(adm_access);
1405 svn_wc__db_t *db = svn_wc__adm_get_db(adm_access);
1406 const char *local_abspath = svn_wc__adm_access_abspath(adm_access);
1407 apr_pool_t *result_pool = svn_wc__adm_access_pool_internal(adm_access);
1408 svn_sqlite__db_t *sdb;
1409 struct entries_read_baton_t erb;
1411 /* ### Use the borrow DB api to handle all calls in a single read
1412 ### transaction. This api is used extensively in our test suite
1413 ### via the entries-read application. */
1415 SVN_ERR(svn_wc__db_temp_borrow_sdb(&sdb, db, local_abspath, pool));
1418 erb.local_abspath = local_abspath;
1419 erb.new_entries = &new_entries;
1420 erb.result_pool = result_pool;
1422 SVN_ERR(svn_sqlite__with_lock(sdb, entries_read_txn, &erb, pool));
1424 svn_wc__adm_access_set_entries(adm_access, new_entries);
1428 *entries = new_entries;
1430 SVN_ERR(prune_deleted(entries, new_entries,
1431 svn_wc__adm_access_pool_internal(adm_access),
1434 return SVN_NO_ERROR;
1438 svn_wc_entries_read(apr_hash_t **entries,
1439 svn_wc_adm_access_t *adm_access,
1440 svn_boolean_t show_hidden,
1443 return svn_error_trace(svn_wc__entries_read_internal(entries, adm_access,
1444 show_hidden, pool));
1447 /* No transaction required: called from write_entry which is itself
1448 transaction-wrapped. */
1449 static svn_error_t *
1450 insert_node(svn_sqlite__db_t *sdb,
1451 const db_node_t *node,
1452 apr_pool_t *scratch_pool)
1454 svn_sqlite__stmt_t *stmt;
1456 SVN_ERR_ASSERT(node->op_depth > 0 || node->repos_relpath);
1458 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_NODE));
1459 SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnnnsnrisnnni",
1461 node->local_relpath,
1463 node->parent_relpath,
1464 /* Setting depth for files? */
1465 svn_depth_to_word(node->depth),
1468 node->changed_author,
1469 node->recorded_time));
1471 if (node->repos_relpath)
1473 SVN_ERR(svn_sqlite__bind_int64(stmt, 5,
1475 SVN_ERR(svn_sqlite__bind_text(stmt, 6,
1476 node->repos_relpath));
1477 SVN_ERR(svn_sqlite__bind_revnum(stmt, 7, node->revision));
1480 if (node->presence == svn_wc__db_status_normal)
1481 SVN_ERR(svn_sqlite__bind_text(stmt, 8, "normal"));
1482 else if (node->presence == svn_wc__db_status_not_present)
1483 SVN_ERR(svn_sqlite__bind_text(stmt, 8, "not-present"));
1484 else if (node->presence == svn_wc__db_status_base_deleted)
1485 SVN_ERR(svn_sqlite__bind_text(stmt, 8, "base-deleted"));
1486 else if (node->presence == svn_wc__db_status_incomplete)
1487 SVN_ERR(svn_sqlite__bind_text(stmt, 8, "incomplete"));
1488 else if (node->presence == svn_wc__db_status_excluded)
1489 SVN_ERR(svn_sqlite__bind_text(stmt, 8, "excluded"));
1490 else if (node->presence == svn_wc__db_status_server_excluded)
1491 SVN_ERR(svn_sqlite__bind_text(stmt, 8, "server-excluded"));
1493 if (node->kind == svn_node_none)
1494 SVN_ERR(svn_sqlite__bind_text(stmt, 10, "unknown"));
1496 SVN_ERR(svn_sqlite__bind_text(stmt, 10,
1497 svn_node_kind_to_word(node->kind)));
1499 if (node->kind == svn_node_file)
1502 && node->op_depth == 0
1503 && node->presence != svn_wc__db_status_not_present
1504 && node->presence != svn_wc__db_status_excluded
1505 && node->presence != svn_wc__db_status_server_excluded)
1506 return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
1507 _("The file '%s' has no checksum"),
1508 svn_dirent_local_style(node->local_relpath,
1511 SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, node->checksum,
1515 if (node->properties) /* ### Never set, props done later */
1516 SVN_ERR(svn_sqlite__bind_properties(stmt, 15, node->properties,
1519 if (node->recorded_size != SVN_INVALID_FILESIZE)
1520 SVN_ERR(svn_sqlite__bind_int64(stmt, 16, node->recorded_size));
1522 if (node->file_external)
1523 SVN_ERR(svn_sqlite__bind_int(stmt, 20, 1));
1525 if (node->inherited_props)
1526 SVN_ERR(svn_sqlite__bind_iprops(stmt, 23, node->inherited_props,
1529 SVN_ERR(svn_sqlite__insert(NULL, stmt));
1531 return SVN_NO_ERROR;
1536 static svn_error_t *
1537 insert_actual_node(svn_sqlite__db_t *sdb,
1539 const char *wri_abspath,
1540 const db_actual_node_t *actual_node,
1541 apr_pool_t *scratch_pool)
1543 svn_sqlite__stmt_t *stmt;
1544 svn_skel_t *conflict_data = NULL;
1546 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_ACTUAL_NODE));
1548 SVN_ERR(svn_sqlite__bind_int64(stmt, 1, actual_node->wc_id));
1549 SVN_ERR(svn_sqlite__bind_text(stmt, 2, actual_node->local_relpath));
1550 SVN_ERR(svn_sqlite__bind_text(stmt, 3, actual_node->parent_relpath));
1552 if (actual_node->properties)
1553 SVN_ERR(svn_sqlite__bind_properties(stmt, 4, actual_node->properties,
1556 if (actual_node->changelist)
1557 SVN_ERR(svn_sqlite__bind_text(stmt, 5, actual_node->changelist));
1559 SVN_ERR(svn_wc__upgrade_conflict_skel_from_raw(
1562 actual_node->local_relpath,
1563 actual_node->conflict_old,
1564 actual_node->conflict_working,
1565 actual_node->conflict_new,
1566 actual_node->prop_reject,
1567 actual_node->tree_conflict_data,
1568 actual_node->tree_conflict_data
1569 ? strlen(actual_node->tree_conflict_data)
1571 scratch_pool, scratch_pool));
1575 svn_stringbuf_t *data = svn_skel__unparse(conflict_data, scratch_pool);
1577 SVN_ERR(svn_sqlite__bind_blob(stmt, 6, data->data, data->len));
1580 /* Execute and reset the insert clause. */
1581 return svn_error_trace(svn_sqlite__insert(NULL, stmt));
1584 static svn_boolean_t
1585 is_switched(db_node_t *parent,
1587 apr_pool_t *scratch_pool)
1589 if (parent && child)
1591 if (parent->repos_id != child->repos_id)
1594 if (parent->repos_relpath && child->repos_relpath)
1596 const char *unswitched
1597 = svn_relpath_join(parent->repos_relpath,
1598 svn_relpath_basename(child->local_relpath,
1601 if (strcmp(unswitched, child->repos_relpath))
1609 struct write_baton {
1612 db_node_t *below_work;
1613 apr_hash_t *tree_conflicts;
1616 #define WRITE_ENTRY_ASSERT(expr) \
1618 return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL, \
1619 _("Unable to upgrade '%s' at line %d"), \
1620 svn_dirent_local_style( \
1621 svn_dirent_join(root_abspath, \
1624 scratch_pool), __LINE__)
1626 /* Write the information for ENTRY to WC_DB. The WC_ID, REPOS_ID and
1627 REPOS_ROOT will all be used for writing ENTRY.
1628 ### transitioning from straight sql to using the wc_db APIs. For the
1629 ### time being, we'll need both parameters. */
1630 static svn_error_t *
1631 write_entry(struct write_baton **entry_node,
1632 const struct write_baton *parent_node,
1634 svn_sqlite__db_t *sdb,
1636 apr_int64_t repos_id,
1637 const svn_wc_entry_t *entry,
1638 const svn_wc__text_base_info_t *text_base_info,
1639 const char *local_relpath,
1640 const char *tmp_entry_abspath,
1641 const char *root_abspath,
1642 const svn_wc_entry_t *this_dir,
1643 svn_boolean_t create_locks,
1644 apr_pool_t *result_pool,
1645 apr_pool_t *scratch_pool)
1647 db_node_t *base_node = NULL;
1648 db_node_t *working_node = NULL, *below_working_node = NULL;
1649 db_actual_node_t *actual_node = NULL;
1650 const char *parent_relpath;
1651 apr_hash_t *tree_conflicts;
1653 if (*local_relpath == '\0')
1654 parent_relpath = NULL;
1656 parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
1658 /* This is how it should work, it doesn't work like this yet because
1659 we need proper op_depth to layer the working nodes.
1661 Using "svn add", "svn rm", "svn cp" only files can be replaced
1662 pre-wcng; directories can only be normal, deleted or added.
1663 Files cannot be replaced within a deleted directory, so replaced
1664 files can only exist in a normal directory, or a directory that
1665 is added+copied. In a normal directory a replaced file needs a
1666 base node and a working node, in an added+copied directory a
1667 replaced file needs two working nodes at different op-depths.
1669 With just the above operations the conversion for files and
1670 directories is straightforward:
1673 parent child parent child
1675 normal normal base base
1676 add+copied normal+copied work work
1677 normal+copied normal+copied work work
1678 normal delete base base+work
1679 delete delete base+work base+work
1680 add+copied delete work work
1681 normal add base work
1683 add+copied add work work
1684 normal add+copied base work
1685 add add+copied work work
1686 add+copied add+copied work work
1687 normal replace base base+work
1688 add+copied replace work work+work
1689 normal replace+copied base base+work
1690 add+copied replace+copied work work+work
1692 However "svn merge" make this more complicated. The pre-wcng
1693 "svn merge" is capable of replacing a directory, that is it can
1694 mark the whole tree deleted, and then copy another tree on top.
1695 The entries then represent the replacing tree overlayed on the
1698 original replace schedule in
1699 tree tree combined tree
1703 A/g A/g replace+copied
1705 A/B A/B replace+copied
1707 A/B/g A/B/g replace+copied
1714 The original tree could be normal tree, or an add+copied tree.
1715 Committing such a merge generally worked, but making further tree
1716 modifications before commit sometimes failed.
1718 The root of the replace is handled like the file replace:
1721 parent child parent child
1723 normal replace+copied base base+work
1724 add+copied replace+copied work work+work
1726 although obviously the node is a directory rather then a file.
1727 There are then more conversion states where the parent is
1731 parent child parent child
1733 replace+copied add [base|work]+work work
1734 replace+copied add+copied [base|work]+work work
1735 replace+copied delete+copied [base|work]+work [base|work]+work
1736 delete+copied delete+copied [base|work]+work [base|work]+work
1737 replace+copied replace+copied [base|work]+work [base|work]+work
1740 WRITE_ENTRY_ASSERT(parent_node || entry->schedule == svn_wc_schedule_normal);
1742 WRITE_ENTRY_ASSERT(!parent_node || parent_node->base
1743 || parent_node->below_work || parent_node->work);
1745 switch (entry->schedule)
1747 case svn_wc_schedule_normal:
1748 if (entry->copied ||
1749 (entry->depth == svn_depth_exclude
1750 && parent_node && !parent_node->base && parent_node->work))
1751 working_node = MAYBE_ALLOC(working_node, result_pool);
1753 base_node = MAYBE_ALLOC(base_node, result_pool);
1756 case svn_wc_schedule_add:
1757 working_node = MAYBE_ALLOC(working_node, result_pool);
1760 if (parent_node->base)
1761 base_node = MAYBE_ALLOC(base_node, result_pool);
1763 below_working_node = MAYBE_ALLOC(below_working_node, result_pool);
1767 case svn_wc_schedule_delete:
1768 working_node = MAYBE_ALLOC(working_node, result_pool);
1769 if (parent_node->base)
1770 base_node = MAYBE_ALLOC(base_node, result_pool);
1771 if (parent_node->work)
1772 below_working_node = MAYBE_ALLOC(below_working_node, result_pool);
1775 case svn_wc_schedule_replace:
1776 working_node = MAYBE_ALLOC(working_node, result_pool);
1777 if (parent_node->base)
1778 base_node = MAYBE_ALLOC(base_node, result_pool);
1780 below_working_node = MAYBE_ALLOC(below_working_node, result_pool);
1784 /* Something deleted in this revision means there should always be a
1785 BASE node to indicate the not-present node. */
1788 WRITE_ENTRY_ASSERT(base_node || below_working_node);
1789 WRITE_ENTRY_ASSERT(!entry->incomplete);
1791 base_node->presence = svn_wc__db_status_not_present;
1793 below_working_node->presence = svn_wc__db_status_not_present;
1795 else if (entry->absent)
1797 WRITE_ENTRY_ASSERT(base_node && !working_node && !below_working_node);
1798 WRITE_ENTRY_ASSERT(!entry->incomplete);
1799 base_node->presence = svn_wc__db_status_server_excluded;
1804 if (entry->copyfrom_url)
1806 working_node->repos_id = repos_id;
1807 working_node->repos_relpath = svn_uri_skip_ancestor(
1808 this_dir->repos, entry->copyfrom_url,
1810 working_node->revision = entry->copyfrom_rev;
1811 working_node->op_depth
1812 = svn_wc__db_op_depth_for_upgrade(local_relpath);
1814 else if (parent_node->work && parent_node->work->repos_relpath)
1816 working_node->repos_id = repos_id;
1817 working_node->repos_relpath
1818 = svn_relpath_join(parent_node->work->repos_relpath,
1819 svn_relpath_basename(local_relpath, NULL),
1821 working_node->revision = parent_node->work->revision;
1822 working_node->op_depth = parent_node->work->op_depth;
1824 else if (parent_node->below_work
1825 && parent_node->below_work->repos_relpath)
1827 working_node->repos_id = repos_id;
1828 working_node->repos_relpath
1829 = svn_relpath_join(parent_node->below_work->repos_relpath,
1830 svn_relpath_basename(local_relpath, NULL),
1832 working_node->revision = parent_node->below_work->revision;
1833 working_node->op_depth = parent_node->below_work->op_depth;
1836 return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL,
1837 _("No copyfrom URL for '%s'"),
1838 svn_dirent_local_style(local_relpath,
1842 if (entry->conflict_old)
1844 actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
1845 if (parent_relpath && entry->conflict_old)
1846 actual_node->conflict_old = svn_relpath_join(parent_relpath,
1847 entry->conflict_old,
1850 actual_node->conflict_old = entry->conflict_old;
1851 if (parent_relpath && entry->conflict_new)
1852 actual_node->conflict_new = svn_relpath_join(parent_relpath,
1853 entry->conflict_new,
1856 actual_node->conflict_new = entry->conflict_new;
1857 if (parent_relpath && entry->conflict_wrk)
1858 actual_node->conflict_working = svn_relpath_join(parent_relpath,
1859 entry->conflict_wrk,
1862 actual_node->conflict_working = entry->conflict_wrk;
1865 if (entry->prejfile)
1867 actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
1868 actual_node->prop_reject = svn_relpath_join((entry->kind == svn_node_dir
1875 if (entry->changelist)
1877 actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
1878 actual_node->changelist = entry->changelist;
1881 /* ### set the text_mod value? */
1883 if (entry_node && entry->tree_conflict_data)
1885 /* Issues #3840/#3916: 1.6 stores multiple tree conflicts on the
1886 parent node, 1.7 stores them directly on the conflited nodes.
1887 So "((skel1) (skel2))" becomes "(skel1)" and "(skel2)" */
1890 skel = svn_skel__parse(entry->tree_conflict_data,
1891 strlen(entry->tree_conflict_data),
1893 tree_conflicts = apr_hash_make(result_pool);
1894 skel = skel->children;
1897 svn_wc_conflict_description2_t *conflict;
1898 svn_skel_t *new_skel;
1901 /* *CONFLICT is allocated so it is safe to use a non-const pointer */
1902 SVN_ERR(svn_wc__deserialize_conflict(
1903 (const svn_wc_conflict_description2_t**)&conflict,
1905 svn_dirent_join(root_abspath,
1908 scratch_pool, scratch_pool));
1910 WRITE_ENTRY_ASSERT(conflict->kind == svn_wc_conflict_kind_tree);
1912 /* Fix dubious data stored by old clients, local adds don't have
1913 a repository URL. */
1914 if (conflict->reason == svn_wc_conflict_reason_added)
1915 conflict->src_left_version = NULL;
1917 SVN_ERR(svn_wc__serialize_conflict(&new_skel, conflict,
1918 scratch_pool, scratch_pool));
1920 /* Store in hash to be retrieved when writing the child
1922 key = svn_dirent_skip_ancestor(root_abspath, conflict->local_abspath);
1923 svn_hash_sets(tree_conflicts, apr_pstrdup(result_pool, key),
1924 svn_skel__unparse(new_skel, result_pool)->data);
1929 tree_conflicts = NULL;
1931 if (parent_node && parent_node->tree_conflicts)
1933 const char *tree_conflict_data =
1934 svn_hash_gets(parent_node->tree_conflicts, local_relpath);
1935 if (tree_conflict_data)
1937 actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
1938 actual_node->tree_conflict_data = tree_conflict_data;
1941 /* Reset hash so that we don't write the row again when writing
1942 actual-only nodes */
1943 svn_hash_sets(parent_node->tree_conflicts, local_relpath, NULL);
1946 if (entry->file_external_path != NULL)
1948 base_node = MAYBE_ALLOC(base_node, result_pool);
1952 /* Insert the base node. */
1955 base_node->wc_id = wc_id;
1956 base_node->local_relpath = local_relpath;
1957 base_node->op_depth = 0;
1958 base_node->parent_relpath = parent_relpath;
1959 base_node->revision = entry->revision;
1960 base_node->recorded_time = entry->text_time;
1961 base_node->recorded_size = entry->working_size;
1963 if (entry->depth != svn_depth_exclude)
1964 base_node->depth = entry->depth;
1967 base_node->presence = svn_wc__db_status_excluded;
1968 base_node->depth = svn_depth_infinity;
1973 WRITE_ENTRY_ASSERT(base_node->presence
1974 == svn_wc__db_status_not_present);
1975 /* ### should be svn_node_unknown, but let's store what we have. */
1976 base_node->kind = entry->kind;
1978 else if (entry->absent)
1980 WRITE_ENTRY_ASSERT(base_node->presence
1981 == svn_wc__db_status_server_excluded);
1982 /* ### should be svn_node_unknown, but let's store what we have. */
1983 base_node->kind = entry->kind;
1985 /* Store the most likely revision in the node to avoid
1986 base nodes without a valid revision. Of course
1987 we remember that the data is still incomplete. */
1988 if (!SVN_IS_VALID_REVNUM(base_node->revision) && parent_node->base)
1989 base_node->revision = parent_node->base->revision;
1993 base_node->kind = entry->kind;
1995 if (base_node->presence != svn_wc__db_status_excluded)
1997 /* All subdirs are initially incomplete, they stop being
1998 incomplete when the entries file in the subdir is
1999 upgraded and remain incomplete if that doesn't happen. */
2000 if (entry->kind == svn_node_dir
2001 && strcmp(entry->name, SVN_WC_ENTRY_THIS_DIR))
2003 base_node->presence = svn_wc__db_status_incomplete;
2005 /* Store the most likely revision in the node to avoid
2006 base nodes without a valid revision. Of course
2007 we remember that the data is still incomplete. */
2008 if (parent_node->base)
2009 base_node->revision = parent_node->base->revision;
2011 else if (entry->incomplete)
2013 /* ### nobody should have set the presence. */
2014 WRITE_ENTRY_ASSERT(base_node->presence
2015 == svn_wc__db_status_normal);
2016 base_node->presence = svn_wc__db_status_incomplete;
2021 if (entry->kind == svn_node_dir)
2022 base_node->checksum = NULL;
2025 if (text_base_info && text_base_info->revert_base.sha1_checksum)
2026 base_node->checksum = text_base_info->revert_base.sha1_checksum;
2027 else if (text_base_info && text_base_info->normal_base.sha1_checksum)
2028 base_node->checksum = text_base_info->normal_base.sha1_checksum;
2030 base_node->checksum = NULL;
2032 /* The base MD5 checksum is available in the entry, unless there
2033 * is a copied WORKING node. If possible, verify that the entry
2034 * checksum matches the base file that we found. */
2035 if (! (working_node && entry->copied))
2037 svn_checksum_t *entry_md5_checksum, *found_md5_checksum;
2038 SVN_ERR(svn_checksum_parse_hex(&entry_md5_checksum,
2040 entry->checksum, scratch_pool));
2041 if (text_base_info && text_base_info->revert_base.md5_checksum)
2042 found_md5_checksum = text_base_info->revert_base.md5_checksum;
2043 else if (text_base_info
2044 && text_base_info->normal_base.md5_checksum)
2045 found_md5_checksum = text_base_info->normal_base.md5_checksum;
2047 found_md5_checksum = NULL;
2048 if (entry_md5_checksum && found_md5_checksum &&
2049 !svn_checksum_match(entry_md5_checksum, found_md5_checksum))
2050 return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
2051 _("Bad base MD5 checksum for '%s'; "
2052 "expected: '%s'; found '%s'; "),
2053 svn_dirent_local_style(
2054 svn_dirent_join(root_abspath,
2058 svn_checksum_to_cstring_display(
2059 entry_md5_checksum, scratch_pool),
2060 svn_checksum_to_cstring_display(
2061 found_md5_checksum, scratch_pool));
2064 /* ### Not sure what conditions this should cover. */
2065 /* SVN_ERR_ASSERT(entry->deleted || ...); */
2070 if (this_dir->repos)
2072 base_node->repos_id = repos_id;
2074 if (entry->url != NULL)
2076 base_node->repos_relpath = svn_uri_skip_ancestor(
2077 this_dir->repos, entry->url,
2082 const char *relpath = svn_uri_skip_ancestor(this_dir->repos,
2085 if (relpath == NULL || *relpath == '\0')
2086 base_node->repos_relpath = entry->name;
2088 base_node->repos_relpath =
2089 svn_dirent_join(relpath, entry->name, result_pool);
2093 /* TODO: These values should always be present, if they are missing
2094 during an upgrade, set a flag, and then ask the user to talk to the
2097 Note: cmt_rev is the distinguishing value. The others may be 0 or
2098 NULL if the corresponding revprop has been deleted. */
2099 base_node->changed_rev = entry->cmt_rev;
2100 base_node->changed_date = entry->cmt_date;
2101 base_node->changed_author = entry->cmt_author;
2103 if (entry->file_external_path)
2104 base_node->file_external = TRUE;
2106 /* Switched nodes get an empty iprops cache. */
2108 && is_switched(parent_node->base, base_node, scratch_pool))
2109 base_node->inherited_props
2110 = apr_array_make(scratch_pool, 0, sizeof(svn_prop_inherited_item_t*));
2112 SVN_ERR(insert_node(sdb, base_node, scratch_pool));
2114 /* We have to insert the lock after the base node, because the node
2115 must exist to lookup various bits of repos related information for
2117 if (entry->lock_token && create_locks)
2119 svn_wc__db_lock_t lock;
2121 lock.token = entry->lock_token;
2122 lock.owner = entry->lock_owner;
2123 lock.comment = entry->lock_comment;
2124 lock.date = entry->lock_creation_date;
2126 SVN_ERR(svn_wc__db_lock_add(db, tmp_entry_abspath, &lock,
2131 if (below_working_node)
2134 = parent_node->below_work ? parent_node->below_work : parent_node->work;
2136 below_working_node->wc_id = wc_id;
2137 below_working_node->local_relpath = local_relpath;
2138 below_working_node->op_depth = work->op_depth;
2139 below_working_node->parent_relpath = parent_relpath;
2140 below_working_node->presence = svn_wc__db_status_normal;
2141 below_working_node->kind = entry->kind;
2142 below_working_node->repos_id = work->repos_id;
2144 /* This is just guessing. If the node below would have been switched
2145 or if it was updated to a different version, the guess would
2146 fail. But we don't have better information pre wc-ng :( */
2147 if (work->repos_relpath)
2148 below_working_node->repos_relpath
2149 = svn_relpath_join(work->repos_relpath, entry->name,
2152 below_working_node->repos_relpath = NULL;
2153 below_working_node->revision = parent_node->work->revision;
2155 /* The revert_base checksum isn't available in the entry structure,
2156 so the caller provides it. */
2158 /* text_base_info is NULL for files scheduled to be added. */
2159 below_working_node->checksum = NULL;
2162 if (entry->schedule == svn_wc_schedule_delete)
2163 below_working_node->checksum =
2164 text_base_info->normal_base.sha1_checksum;
2166 below_working_node->checksum =
2167 text_base_info->revert_base.sha1_checksum;
2169 below_working_node->recorded_size = 0;
2170 below_working_node->changed_rev = SVN_INVALID_REVNUM;
2171 below_working_node->changed_date = 0;
2172 below_working_node->changed_author = NULL;
2173 below_working_node->depth = svn_depth_infinity;
2174 below_working_node->recorded_time = 0;
2175 below_working_node->properties = NULL;
2178 && entry->schedule == svn_wc_schedule_delete
2179 && working_node->repos_relpath)
2181 /* We are lucky, our guesses above are not necessary. The known
2182 correct information is in working. But our op_depth design
2183 expects more information here */
2184 below_working_node->repos_relpath = working_node->repos_relpath;
2185 below_working_node->repos_id = working_node->repos_id;
2186 below_working_node->revision = working_node->revision;
2188 /* Nice for 'svn status' */
2189 below_working_node->changed_rev = entry->cmt_rev;
2190 below_working_node->changed_date = entry->cmt_date;
2191 below_working_node->changed_author = entry->cmt_author;
2193 /* And now remove it from WORKING, because in wc-ng code
2194 should read it from the lower layer */
2195 working_node->repos_relpath = NULL;
2196 working_node->repos_id = 0;
2197 working_node->revision = SVN_INVALID_REVNUM;
2200 SVN_ERR(insert_node(sdb, below_working_node, scratch_pool));
2203 /* Insert the working node. */
2206 working_node->wc_id = wc_id;
2207 working_node->local_relpath = local_relpath;
2208 working_node->parent_relpath = parent_relpath;
2209 working_node->changed_rev = SVN_INVALID_REVNUM;
2210 working_node->recorded_time = entry->text_time;
2211 working_node->recorded_size = entry->working_size;
2213 if (entry->depth != svn_depth_exclude)
2214 working_node->depth = entry->depth;
2217 working_node->presence = svn_wc__db_status_excluded;
2218 working_node->depth = svn_depth_infinity;
2221 if (entry->kind == svn_node_dir)
2222 working_node->checksum = NULL;
2225 working_node->checksum = NULL;
2226 /* text_base_info is NULL for files scheduled to be added. */
2228 working_node->checksum = text_base_info->normal_base.sha1_checksum;
2231 /* If an MD5 checksum is present in the entry, we can verify that
2232 * it matches the MD5 of the base file we found earlier. */
2234 if (entry->checksum && text_base_info)
2236 svn_checksum_t *md5_checksum;
2237 SVN_ERR(svn_checksum_parse_hex(&md5_checksum, svn_checksum_md5,
2238 entry->checksum, result_pool));
2240 md5_checksum && text_base_info->normal_base.md5_checksum);
2241 SVN_ERR_ASSERT(svn_checksum_match(
2242 md5_checksum, text_base_info->normal_base.md5_checksum));
2247 working_node->kind = entry->kind;
2248 if (working_node->presence != svn_wc__db_status_excluded)
2250 /* All subdirs start of incomplete, and stop being incomplete
2251 when the entries file in the subdir is upgraded. */
2252 if (entry->kind == svn_node_dir
2253 && strcmp(entry->name, SVN_WC_ENTRY_THIS_DIR))
2255 working_node->presence = svn_wc__db_status_incomplete;
2256 working_node->kind = svn_node_dir;
2258 else if (entry->schedule == svn_wc_schedule_delete)
2260 working_node->presence = svn_wc__db_status_base_deleted;
2261 working_node->kind = entry->kind;
2265 /* presence == normal */
2266 working_node->kind = entry->kind;
2268 if (entry->incomplete)
2270 /* We shouldn't be overwriting another status. */
2271 WRITE_ENTRY_ASSERT(working_node->presence
2272 == svn_wc__db_status_normal);
2273 working_node->presence = svn_wc__db_status_incomplete;
2278 /* These should generally be unset for added and deleted files,
2279 and contain whatever information we have for copied files. Let's
2280 just store whatever we have.
2282 Note: cmt_rev is the distinguishing value. The others may be 0 or
2283 NULL if the corresponding revprop has been deleted. */
2284 if (working_node->presence != svn_wc__db_status_base_deleted)
2286 working_node->changed_rev = entry->cmt_rev;
2287 working_node->changed_date = entry->cmt_date;
2288 working_node->changed_author = entry->cmt_author;
2291 if (entry->schedule == svn_wc_schedule_delete
2292 && parent_node->work
2293 && parent_node->work->presence == svn_wc__db_status_base_deleted)
2295 working_node->op_depth = parent_node->work->op_depth;
2297 else if (!entry->copied)
2299 working_node->op_depth
2300 = svn_wc__db_op_depth_for_upgrade(local_relpath);
2303 SVN_ERR(insert_node(sdb, working_node, scratch_pool));
2306 /* Insert the actual node. */
2309 actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
2311 actual_node->wc_id = wc_id;
2312 actual_node->local_relpath = local_relpath;
2313 actual_node->parent_relpath = parent_relpath;
2315 SVN_ERR(insert_actual_node(sdb, db, tmp_entry_abspath,
2316 actual_node, scratch_pool));
2321 *entry_node = apr_palloc(result_pool, sizeof(**entry_node));
2322 (*entry_node)->base = base_node;
2323 (*entry_node)->work = working_node;
2324 (*entry_node)->below_work = below_working_node;
2325 (*entry_node)->tree_conflicts = tree_conflicts;
2328 if (entry->file_external_path)
2330 /* TODO: Maybe add a file external registration inside EXTERNALS here,
2331 to allow removing file externals that aren't referenced from
2334 The svn:externals values are processed anyway after everything is
2338 return SVN_NO_ERROR;
2341 static svn_error_t *
2342 write_actual_only_entries(apr_hash_t *tree_conflicts,
2343 svn_sqlite__db_t *sdb,
2345 const char *wri_abspath,
2347 const char *parent_relpath,
2348 apr_pool_t *scratch_pool)
2350 apr_hash_index_t *hi;
2352 for (hi = apr_hash_first(scratch_pool, tree_conflicts);
2354 hi = apr_hash_next(hi))
2356 db_actual_node_t *actual_node = NULL;
2358 actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
2359 actual_node->wc_id = wc_id;
2360 actual_node->local_relpath = svn__apr_hash_index_key(hi);
2361 actual_node->parent_relpath = parent_relpath;
2362 actual_node->tree_conflict_data = svn__apr_hash_index_val(hi);
2364 SVN_ERR(insert_actual_node(sdb, db, wri_abspath, actual_node,
2368 return SVN_NO_ERROR;
2372 svn_wc__write_upgraded_entries(void **dir_baton,
2375 svn_sqlite__db_t *sdb,
2376 apr_int64_t repos_id,
2378 const char *dir_abspath,
2379 const char *new_root_abspath,
2380 apr_hash_t *entries,
2381 apr_hash_t *text_bases_info,
2382 apr_pool_t *result_pool,
2383 apr_pool_t *scratch_pool)
2385 const svn_wc_entry_t *this_dir;
2386 apr_hash_index_t *hi;
2387 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2388 const char *old_root_abspath, *dir_relpath;
2389 struct write_baton *parent_node = parent_baton;
2390 struct write_baton *dir_node;
2392 /* Get a copy of the "this dir" entry for comparison purposes. */
2393 this_dir = svn_hash_gets(entries, SVN_WC_ENTRY_THIS_DIR);
2395 /* If there is no "this dir" entry, something is wrong. */
2397 return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL,
2398 _("No default entry in directory '%s'"),
2399 svn_dirent_local_style(dir_abspath,
2401 old_root_abspath = svn_dirent_get_longest_ancestor(dir_abspath,
2405 SVN_ERR_ASSERT(old_root_abspath[0]);
2407 dir_relpath = svn_dirent_skip_ancestor(old_root_abspath, dir_abspath);
2409 /* Write out "this dir" */
2410 SVN_ERR(write_entry(&dir_node, parent_node, db, sdb,
2411 wc_id, repos_id, this_dir, NULL, dir_relpath,
2412 svn_dirent_join(new_root_abspath, dir_relpath,
2415 this_dir, FALSE, result_pool, iterpool));
2417 for (hi = apr_hash_first(scratch_pool, entries); hi;
2418 hi = apr_hash_next(hi))
2420 const char *name = svn__apr_hash_index_key(hi);
2421 const svn_wc_entry_t *this_entry = svn__apr_hash_index_val(hi);
2422 const char *child_abspath, *child_relpath;
2423 svn_wc__text_base_info_t *text_base_info
2424 = svn_hash_gets(text_bases_info, name);
2426 svn_pool_clear(iterpool);
2428 /* Don't rewrite the "this dir" entry! */
2429 if (strcmp(name, SVN_WC_ENTRY_THIS_DIR) == 0)
2432 /* Write the entry. Pass TRUE for create locks, because we still
2433 use this function for upgrading old working copies. */
2434 child_abspath = svn_dirent_join(dir_abspath, name, iterpool);
2435 child_relpath = svn_dirent_skip_ancestor(old_root_abspath, child_abspath);
2436 SVN_ERR(write_entry(NULL, dir_node, db, sdb,
2438 this_entry, text_base_info, child_relpath,
2439 svn_dirent_join(new_root_abspath, child_relpath,
2442 this_dir, TRUE, iterpool, iterpool));
2445 if (dir_node->tree_conflicts)
2446 SVN_ERR(write_actual_only_entries(dir_node->tree_conflicts, sdb, db,
2447 new_root_abspath, wc_id, dir_relpath,
2450 *dir_baton = dir_node;
2451 svn_pool_destroy(iterpool);
2452 return SVN_NO_ERROR;
2457 svn_wc_entry_dup(const svn_wc_entry_t *entry, apr_pool_t *pool)
2459 svn_wc_entry_t *dupentry = apr_palloc(pool, sizeof(*dupentry));
2461 /* Perform a trivial copy ... */
2464 /* ...and then re-copy stuff that needs to be duped into our pool. */
2466 dupentry->name = apr_pstrdup(pool, entry->name);
2468 dupentry->url = apr_pstrdup(pool, entry->url);
2470 dupentry->repos = apr_pstrdup(pool, entry->repos);
2472 dupentry->uuid = apr_pstrdup(pool, entry->uuid);
2473 if (entry->copyfrom_url)
2474 dupentry->copyfrom_url = apr_pstrdup(pool, entry->copyfrom_url);
2475 if (entry->conflict_old)
2476 dupentry->conflict_old = apr_pstrdup(pool, entry->conflict_old);
2477 if (entry->conflict_new)
2478 dupentry->conflict_new = apr_pstrdup(pool, entry->conflict_new);
2479 if (entry->conflict_wrk)
2480 dupentry->conflict_wrk = apr_pstrdup(pool, entry->conflict_wrk);
2481 if (entry->prejfile)
2482 dupentry->prejfile = apr_pstrdup(pool, entry->prejfile);
2483 if (entry->checksum)
2484 dupentry->checksum = apr_pstrdup(pool, entry->checksum);
2485 if (entry->cmt_author)
2486 dupentry->cmt_author = apr_pstrdup(pool, entry->cmt_author);
2487 if (entry->lock_token)
2488 dupentry->lock_token = apr_pstrdup(pool, entry->lock_token);
2489 if (entry->lock_owner)
2490 dupentry->lock_owner = apr_pstrdup(pool, entry->lock_owner);
2491 if (entry->lock_comment)
2492 dupentry->lock_comment = apr_pstrdup(pool, entry->lock_comment);
2493 if (entry->changelist)
2494 dupentry->changelist = apr_pstrdup(pool, entry->changelist);
2496 /* NOTE: we do not dup cachable_props or present_props since they
2497 are deprecated. Use "" to indicate "nothing cachable or cached". */
2498 dupentry->cachable_props = "";
2499 dupentry->present_props = "";
2501 if (entry->tree_conflict_data)
2502 dupentry->tree_conflict_data = apr_pstrdup(pool,
2503 entry->tree_conflict_data);
2504 if (entry->file_external_path)
2505 dupentry->file_external_path = apr_pstrdup(pool,
2506 entry->file_external_path);
2511 /*** Generic Entry Walker */
2513 /* A recursive entry-walker, helper for svn_wc_walk_entries3().
2515 * For this directory (DIRPATH, ADM_ACCESS), call the "found_entry" callback
2516 * in WALK_CALLBACKS, passing WALK_BATON to it. Then, for each versioned
2517 * entry in this directory, call the "found entry" callback and then recurse
2518 * (if it is a directory and if DEPTH allows).
2520 * If SHOW_HIDDEN is true, include entries that are in a 'deleted' or
2521 * 'absent' state (and not scheduled for re-addition), else skip them.
2523 * Call CANCEL_FUNC with CANCEL_BATON to allow cancellation.
2525 static svn_error_t *
2526 walker_helper(const char *dirpath,
2527 svn_wc_adm_access_t *adm_access,
2528 const svn_wc_entry_callbacks2_t *walk_callbacks,
2531 svn_boolean_t show_hidden,
2532 svn_cancel_func_t cancel_func,
2536 apr_pool_t *subpool = svn_pool_create(pool);
2537 apr_hash_t *entries;
2538 apr_hash_index_t *hi;
2539 svn_wc_entry_t *dot_entry;
2541 svn_wc__db_t *db = svn_wc__adm_get_db(adm_access);
2543 err = svn_wc__entries_read_internal(&entries, adm_access, show_hidden,
2547 SVN_ERR(walk_callbacks->handle_error(dirpath, err, walk_baton, pool));
2549 /* As promised, always return the '.' entry first. */
2550 dot_entry = svn_hash_gets(entries, SVN_WC_ENTRY_THIS_DIR);
2552 return walk_callbacks->handle_error
2553 (dirpath, svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL,
2554 _("Directory '%s' has no THIS_DIR entry"),
2555 svn_dirent_local_style(dirpath, pool)),
2558 /* Call the "found entry" callback for this directory as a "this dir"
2559 * entry. Note that if this directory has been reached by recursion, this
2560 * is the second visit as it will already have been visited once as a
2561 * child entry of its parent. */
2563 err = walk_callbacks->found_entry(dirpath, dot_entry, walk_baton, subpool);
2567 SVN_ERR(walk_callbacks->handle_error(dirpath, err, walk_baton, pool));
2569 if (depth == svn_depth_empty)
2570 return SVN_NO_ERROR;
2572 /* Loop over each of the other entries. */
2573 for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
2575 const char *name = svn__apr_hash_index_key(hi);
2576 const svn_wc_entry_t *current_entry = svn__apr_hash_index_val(hi);
2577 const char *entrypath;
2578 const char *entry_abspath;
2579 svn_boolean_t hidden;
2581 svn_pool_clear(subpool);
2583 /* See if someone wants to cancel this operation. */
2585 SVN_ERR(cancel_func(cancel_baton));
2587 /* Skip the "this dir" entry. */
2588 if (strcmp(current_entry->name, SVN_WC_ENTRY_THIS_DIR) == 0)
2591 entrypath = svn_dirent_join(dirpath, name, subpool);
2592 SVN_ERR(svn_wc__entry_is_hidden(&hidden, current_entry));
2593 SVN_ERR(svn_dirent_get_absolute(&entry_abspath, entrypath, subpool));
2595 /* Call the "found entry" callback for this entry. (For a directory,
2596 * this is the first visit: as a child.) */
2597 if (current_entry->kind == svn_node_file
2598 || depth >= svn_depth_immediates)
2600 err = walk_callbacks->found_entry(entrypath, current_entry,
2601 walk_baton, subpool);
2604 SVN_ERR(walk_callbacks->handle_error(entrypath, err,
2608 /* Recurse into this entry if appropriate. */
2609 if (current_entry->kind == svn_node_dir
2611 && depth >= svn_depth_immediates)
2613 svn_wc_adm_access_t *entry_access;
2614 svn_depth_t depth_below_here = depth;
2616 if (depth == svn_depth_immediates)
2617 depth_below_here = svn_depth_empty;
2619 entry_access = svn_wc__adm_retrieve_internal2(db, entry_abspath,
2623 SVN_ERR(walker_helper(entrypath, entry_access,
2624 walk_callbacks, walk_baton,
2625 depth_below_here, show_hidden,
2626 cancel_func, cancel_baton,
2631 svn_pool_destroy(subpool);
2632 return SVN_NO_ERROR;
2636 svn_wc__walker_default_error_handler(const char *path,
2641 /* Note: don't trace this. We don't want to insert a false "stack frame"
2642 onto an error generated elsewhere. */
2643 return svn_error_trace(err);
2647 /* The public API. */
2649 svn_wc_walk_entries3(const char *path,
2650 svn_wc_adm_access_t *adm_access,
2651 const svn_wc_entry_callbacks2_t *walk_callbacks,
2653 svn_depth_t walk_depth,
2654 svn_boolean_t show_hidden,
2655 svn_cancel_func_t cancel_func,
2659 const char *local_abspath;
2660 svn_wc__db_t *db = svn_wc__adm_get_db(adm_access);
2662 svn_node_kind_t kind;
2665 SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
2666 err = svn_wc__db_read_info(NULL, &kind, NULL, NULL, NULL, NULL,
2667 NULL, NULL, NULL, &depth, NULL, NULL,
2668 NULL, NULL, NULL, NULL, NULL, NULL,
2669 NULL, NULL, NULL, NULL, NULL, NULL,
2675 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
2676 return svn_error_trace(err);
2677 /* Remap into SVN_ERR_UNVERSIONED_RESOURCE. */
2678 svn_error_clear(err);
2679 return walk_callbacks->handle_error(
2680 path, svn_error_createf(SVN_ERR_UNVERSIONED_RESOURCE, NULL,
2681 _("'%s' is not under version control"),
2682 svn_dirent_local_style(local_abspath, pool)),
2686 if (kind == svn_node_file || depth == svn_depth_exclude)
2688 const svn_wc_entry_t *entry;
2690 /* ### we should stop passing out entry structures.
2692 ### we should not call handle_error for an error the *callback*
2693 ### gave us. let it deal with the problem before returning. */
2697 svn_boolean_t hidden;
2698 SVN_ERR(svn_wc__db_node_hidden(&hidden, db, local_abspath, pool));
2702 /* The fool asked to walk a "hidden" node. Report the node as
2705 ### this is incorrect behavior. see depth_test 36. the walk
2706 ### API will be revamped to avoid entry structures. we should
2707 ### be able to solve the problem with the new API. (since we
2708 ### shouldn't return a hidden entry here) */
2709 return walk_callbacks->handle_error(
2710 path, svn_error_createf(
2711 SVN_ERR_UNVERSIONED_RESOURCE, NULL,
2712 _("'%s' is not under version control"),
2713 svn_dirent_local_style(local_abspath, pool)),
2718 SVN_ERR(svn_wc__get_entry(&entry, db, local_abspath, FALSE,
2719 svn_node_file, pool, pool));
2721 err = walk_callbacks->found_entry(path, entry, walk_baton, pool);
2723 return walk_callbacks->handle_error(path, err, walk_baton, pool);
2725 return SVN_NO_ERROR;
2728 if (kind == svn_node_dir)
2729 return walker_helper(path, adm_access, walk_callbacks, walk_baton,
2730 walk_depth, show_hidden, cancel_func, cancel_baton,
2733 return walk_callbacks->handle_error(
2734 path, svn_error_createf(SVN_ERR_NODE_UNKNOWN_KIND, NULL,
2735 _("'%s' has an unrecognized node kind"),
2736 svn_dirent_local_style(local_abspath, pool)),