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 #define SVN_WC__I_AM_WC_DB
50 #include "svn_private_config.h"
51 #include "private/svn_wc_private.h"
52 #include "private/svn_sqlite.h"
53 #include "token-map.h"
55 #include "wc_db_private.h"
57 #define MAYBE_ALLOC(x,p) ((x) ? (x) : apr_pcalloc((p), sizeof(*(x))))
60 /* Temporary structures which mirror the tables in wc-metadata.sql.
61 For detailed descriptions of each field, see that file. */
62 typedef struct db_node_t {
64 const char *local_relpath;
67 const char *repos_relpath;
68 const char *parent_relpath;
69 svn_wc__db_status_t presence;
70 svn_revnum_t revision;
72 svn_checksum_t *checksum;
73 svn_filesize_t recorded_size;
74 svn_revnum_t changed_rev;
75 apr_time_t changed_date;
76 const char *changed_author;
78 apr_time_t recorded_time;
79 apr_hash_t *properties;
80 svn_boolean_t file_external;
81 apr_array_header_t *inherited_props;
84 typedef struct db_actual_node_t {
86 const char *local_relpath;
87 const char *parent_relpath;
88 apr_hash_t *properties;
89 const char *conflict_old;
90 const char *conflict_new;
91 const char *conflict_working;
92 const char *prop_reject;
93 const char *changelist;
94 /* ### enum for text_mod */
95 const char *tree_conflict_data;
100 /*** reading and writing the entries file ***/
104 static svn_wc_entry_t *
105 alloc_entry(apr_pool_t *pool)
107 svn_wc_entry_t *entry = apr_pcalloc(pool, sizeof(*entry));
108 entry->revision = SVN_INVALID_REVNUM;
109 entry->copyfrom_rev = SVN_INVALID_REVNUM;
110 entry->cmt_rev = SVN_INVALID_REVNUM;
111 entry->kind = svn_node_none;
112 entry->working_size = SVN_WC_ENTRY_WORKING_SIZE_UNKNOWN;
113 entry->depth = svn_depth_infinity;
114 entry->file_external_peg_rev.kind = svn_opt_revision_unspecified;
115 entry->file_external_rev.kind = svn_opt_revision_unspecified;
120 /* Is the entry in a 'hidden' state in the sense of the 'show_hidden'
121 * switches on svn_wc_entries_read(), svn_wc_walk_entries*(), etc.? */
123 svn_wc__entry_is_hidden(svn_boolean_t *hidden, const svn_wc_entry_t *entry)
125 /* In English, the condition is: "the entry is not present, and I haven't
126 scheduled something over the top of it." */
129 || entry->depth == svn_depth_exclude)
131 /* These kinds of nodes cannot be marked for deletion (which also
132 means no "replace" either). */
133 SVN_ERR_ASSERT(entry->schedule == svn_wc_schedule_add
134 || entry->schedule == svn_wc_schedule_normal);
136 /* Hidden if something hasn't been added over it.
138 ### is this even possible with absent or excluded nodes? */
139 *hidden = entry->schedule != svn_wc_schedule_add;
148 /* Hit the database to check the file external information for the given
149 entry. The entry will be modified in place. */
151 check_file_external(svn_wc_entry_t *entry,
153 const char *local_abspath,
154 const char *wri_abspath,
155 apr_pool_t *result_pool,
156 apr_pool_t *scratch_pool)
158 svn_wc__db_status_t status;
159 svn_node_kind_t kind;
160 const char *repos_relpath;
161 svn_revnum_t peg_revision;
162 svn_revnum_t revision;
165 err = svn_wc__db_external_read(&status, &kind, NULL, NULL, NULL,
166 &repos_relpath, &peg_revision, &revision,
167 db, local_abspath, wri_abspath,
168 result_pool, scratch_pool);
172 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
173 return svn_error_trace(err);
175 svn_error_clear(err);
179 if (status == svn_wc__db_status_normal
180 && kind == svn_node_file)
182 entry->file_external_path = repos_relpath;
183 if (SVN_IS_VALID_REVNUM(peg_revision))
185 entry->file_external_peg_rev.kind = svn_opt_revision_number;
186 entry->file_external_peg_rev.value.number = peg_revision;
187 entry->file_external_rev = entry->file_external_peg_rev;
189 if (SVN_IS_VALID_REVNUM(revision))
191 entry->file_external_rev.kind = svn_opt_revision_number;
192 entry->file_external_rev.value.number = revision;
200 /* Fill in the following fields of ENTRY:
211 Return: KIND, REPOS_RELPATH, CHECKSUM
214 get_info_for_deleted(svn_wc_entry_t *entry,
215 svn_node_kind_t *kind,
216 const char **repos_relpath,
217 const svn_checksum_t **checksum,
218 svn_wc__db_lock_t **lock,
220 const char *entry_abspath,
221 svn_wc__db_wcroot_t *wcroot,
222 const char *entry_relpath,
223 const svn_wc_entry_t *parent_entry,
224 svn_boolean_t have_base,
225 svn_boolean_t have_more_work,
226 apr_pool_t *result_pool,
227 apr_pool_t *scratch_pool)
229 if (have_base && !have_more_work)
231 apr_int64_t repos_id;
232 /* This is the delete of a BASE node */
233 SVN_ERR(svn_wc__db_base_get_info_internal(
245 &entry->has_props, NULL,
247 wcroot, entry_relpath,
250 SVN_ERR(svn_wc__db_fetch_repos_info(&entry->repos, &entry->uuid,
251 wcroot, repos_id, result_pool));
255 const char *work_del_relpath;
256 const char *parent_repos_relpath;
257 const char *parent_relpath;
258 apr_int64_t repos_id;
260 /* This is a deleted child of a copy/move-here,
261 so we need to scan up the WORKING tree to find the root of
262 the deletion. Then examine its parent to discover its
263 future location in the repository. */
264 SVN_ERR(svn_wc__db_read_pristine_info(NULL, kind,
271 &entry->has_props, NULL,
276 /* working_size and text_time unavailable */
278 SVN_ERR(svn_wc__db_scan_deletion_internal(
281 &work_del_relpath, NULL,
282 wcroot, entry_relpath,
283 scratch_pool, scratch_pool));
285 SVN_ERR_ASSERT(work_del_relpath != NULL);
286 parent_relpath = svn_relpath_dirname(work_del_relpath, scratch_pool);
288 /* The parent directory of the delete root must be added, so we
289 can find the required information there */
290 SVN_ERR(svn_wc__db_scan_addition_internal(
292 &parent_repos_relpath,
295 wcroot, parent_relpath,
296 result_pool, scratch_pool));
297 SVN_ERR(svn_wc__db_fetch_repos_info(&entry->repos, &entry->uuid,
298 wcroot, repos_id, result_pool));
300 /* Now glue it all together */
301 *repos_relpath = svn_relpath_join(parent_repos_relpath,
302 svn_relpath_skip_ancestor(
308 /* Even though this is the delete of a WORKING node, there might still
309 be a BASE node somewhere below with an interesting revision */
312 svn_wc__db_status_t status;
313 SVN_ERR(svn_wc__db_base_get_info_internal(
314 &status, NULL, &entry->revision,
315 NULL, NULL, NULL, NULL, NULL, NULL,
316 NULL, NULL, lock, NULL, NULL,
318 wcroot, entry_relpath,
319 result_pool, scratch_pool));
321 if (status == svn_wc__db_status_not_present)
322 entry->deleted = TRUE;
326 /* Do some extra work for the child nodes. */
327 if (!SVN_IS_VALID_REVNUM(entry->revision) && parent_entry != NULL)
329 /* For child nodes without a revision, pick up the parent's
331 entry->revision = parent_entry->revision;
339 * Encode tree conflict descriptions into a single string.
341 * Set *CONFLICT_DATA to a string, allocated in POOL, that encodes the tree
342 * conflicts in CONFLICTS in a form suitable for storage in a single string
343 * field in a WC entry. CONFLICTS is a hash of zero or more pointers to
344 * svn_wc_conflict_description2_t objects, index by their basenames. All of the
345 * conflict victim paths must be siblings.
347 * Do all allocations in POOL.
349 * @see svn_wc__read_tree_conflicts()
352 write_tree_conflicts(const char **conflict_data,
353 apr_hash_t *conflicts,
356 svn_skel_t *skel = svn_skel__make_empty_list(pool);
357 apr_hash_index_t *hi;
359 for (hi = apr_hash_first(pool, conflicts); hi; hi = apr_hash_next(hi))
363 SVN_ERR(svn_wc__serialize_conflict(&c_skel, apr_hash_this_val(hi),
365 svn_skel__prepend(c_skel, skel);
368 *conflict_data = svn_skel__unparse(skel, pool)->data;
374 /* Read one entry from wc_db. It will be allocated in RESULT_POOL and
375 returned in *NEW_ENTRY.
377 DIR_ABSPATH is the name of the directory to read this entry from, and
378 it will be named NAME (use "" for "this dir").
380 DB specifies the wc_db database, and WC_ID specifies which working copy
381 this information is being read from.
383 If this node is "this dir", then PARENT_ENTRY should be NULL. Otherwise,
384 it should refer to the entry for the child's parent directory.
386 ### All database read operations should really use wcroot, dir_relpath,
387 as that restores obstruction compatibility with <= 1.6.0
388 but that has been the case since the introduction of WC-NG in 1.7.0
390 Temporary allocations are made in SCRATCH_POOL. */
392 read_one_entry(const svn_wc_entry_t **new_entry,
394 const char *dir_abspath,
395 svn_wc__db_wcroot_t *wcroot,
396 const char *dir_relpath,
398 const svn_wc_entry_t *parent_entry,
399 apr_pool_t *result_pool,
400 apr_pool_t *scratch_pool)
402 svn_node_kind_t kind;
403 svn_wc__db_status_t status;
404 svn_wc__db_lock_t *lock;
405 const char *repos_relpath;
406 const svn_checksum_t *checksum;
407 svn_filesize_t translated_size;
408 svn_wc_entry_t *entry = alloc_entry(result_pool);
409 const char *entry_relpath;
410 const char *entry_abspath;
411 apr_int64_t repos_id;
412 apr_int64_t original_repos_id;
413 const char *original_repos_relpath;
414 const char *original_root_url;
415 svn_boolean_t conflicted;
416 svn_boolean_t have_base;
417 svn_boolean_t have_more_work;
418 svn_boolean_t op_root;
420 entry->name = apr_pstrdup(result_pool, name);
422 entry_relpath = svn_relpath_join(dir_relpath, entry->name, scratch_pool);
423 entry_abspath = svn_dirent_join(dir_abspath, entry->name, scratch_pool);
425 SVN_ERR(svn_wc__db_read_info_internal(
437 &original_repos_relpath,
439 &entry->copyfrom_rev,
446 &entry->has_props /* have_props */,
447 &entry->has_prop_mods /* props_mod */,
450 NULL /* have_work */,
451 wcroot, entry_relpath,
452 result_pool, scratch_pool));
454 SVN_ERR(svn_wc__db_fetch_repos_info(&entry->repos, &entry->uuid,
455 wcroot, repos_id, result_pool));
456 SVN_ERR(svn_wc__db_fetch_repos_info(&original_root_url, NULL,
457 wcroot, original_repos_id,
460 if (entry->has_prop_mods)
461 entry->has_props = TRUE;
463 if (strcmp(entry->name, SVN_WC_ENTRY_THIS_DIR) == 0)
465 /* get the tree conflict data. */
466 apr_hash_t *tree_conflicts = NULL;
467 const apr_array_header_t *conflict_victims;
470 SVN_ERR(svn_wc__db_read_conflict_victims(&conflict_victims, db,
475 for (k = 0; k < conflict_victims->nelts; k++)
478 const apr_array_header_t *child_conflicts;
479 const char *child_name;
480 const char *child_abspath;
482 child_name = APR_ARRAY_IDX(conflict_victims, k, const char *);
483 child_abspath = svn_dirent_join(dir_abspath, child_name,
486 SVN_ERR(svn_wc__read_conflicts(&child_conflicts, NULL,
488 FALSE /* create tempfiles */,
489 TRUE /* tree_conflicts_only */,
490 scratch_pool, scratch_pool));
492 for (j = 0; j < child_conflicts->nelts; j++)
494 const svn_wc_conflict_description2_t *conflict =
495 APR_ARRAY_IDX(child_conflicts, j,
496 svn_wc_conflict_description2_t *);
498 if (conflict->kind == svn_wc_conflict_kind_tree)
501 tree_conflicts = apr_hash_make(scratch_pool);
502 svn_hash_sets(tree_conflicts, child_name, conflict);
509 SVN_ERR(write_tree_conflicts(&entry->tree_conflict_data,
510 tree_conflicts, result_pool));
514 if (status == svn_wc__db_status_normal
515 || status == svn_wc__db_status_incomplete)
517 /* Plain old BASE node. */
518 entry->schedule = svn_wc_schedule_normal;
520 /* Grab inherited repository information, if necessary. */
521 if (repos_relpath == NULL)
523 SVN_ERR(svn_wc__db_base_get_info_internal(
524 NULL, NULL, NULL, &repos_relpath,
525 &repos_id, NULL, NULL, NULL,
526 NULL, NULL, NULL, NULL, NULL, NULL,
528 wcroot, entry_relpath,
529 result_pool, scratch_pool));
530 SVN_ERR(svn_wc__db_fetch_repos_info(&entry->repos, &entry->uuid,
531 wcroot, repos_id, result_pool));
534 entry->incomplete = (status == svn_wc__db_status_incomplete);
536 else if (status == svn_wc__db_status_deleted)
538 svn_node_kind_t path_kind;
540 /* ### we don't have to worry about moves, so this is a delete. */
541 entry->schedule = svn_wc_schedule_delete;
543 /* If there are multiple working layers or no BASE layer, then
544 this is a WORKING delete or WORKING not-present. */
545 if (have_more_work || !have_base)
546 entry->copied = TRUE;
547 else if (have_base && !have_more_work)
548 entry->copied = FALSE;
551 const char *work_del_relpath;
552 SVN_ERR(svn_wc__db_scan_deletion_internal(
554 &work_del_relpath, NULL,
555 wcroot, entry_relpath,
556 scratch_pool, scratch_pool));
558 if (work_del_relpath)
559 entry->copied = TRUE;
562 /* If there is still a directory on-disk we keep it, if not it is
563 already deleted. Simple, isn't it?
565 Before single-db we had to keep the administative area alive until
566 after the commit really deletes it. Setting keep alive stopped the
567 commit processing from deleting the directory. We don't delete it
568 any more, so all we have to do is provide some 'sane' value.
570 SVN_ERR(svn_io_check_path(entry_abspath, &path_kind, scratch_pool));
571 entry->keep_local = (path_kind == svn_node_dir);
573 else if (status == svn_wc__db_status_added)
575 svn_wc__db_status_t work_status;
576 const char *op_root_abspath;
577 const char *scanned_original_relpath;
578 svn_revnum_t original_revision;
580 /* For child nodes, pick up the parent's revision. */
581 if (*entry->name != '\0')
583 assert(parent_entry != NULL);
584 assert(entry->revision == SVN_INVALID_REVNUM);
586 entry->revision = parent_entry->revision;
591 svn_wc__db_status_t base_status;
593 /* ENTRY->REVISION is overloaded. When a node is schedule-add
594 or -replace, then REVISION refers to the BASE node's revision
595 that is being overwritten. We need to fetch it now. */
596 SVN_ERR(svn_wc__db_base_get_info_internal(
603 wcroot, entry_relpath,
607 if (base_status == svn_wc__db_status_not_present)
609 /* The underlying node is DELETED in this revision. */
610 entry->deleted = TRUE;
612 /* This is an add since there isn't a node to replace. */
613 entry->schedule = svn_wc_schedule_add;
616 entry->schedule = svn_wc_schedule_replace;
620 /* There is NO 'not-present' BASE_NODE for this node.
621 Therefore, we are looking at some kind of add/copy
622 rather than a replace. */
624 /* ### if this looks like a plain old add, then rev=0. */
625 if (!SVN_IS_VALID_REVNUM(entry->copyfrom_rev)
626 && !SVN_IS_VALID_REVNUM(entry->cmt_rev))
629 entry->schedule = svn_wc_schedule_add;
632 /* If we don't have "real" data from the entry (obstruction),
633 then we cannot begin a scan for data. The original node may
634 have important data. Set up stuff to kill that idea off,
635 and finish up this entry. */
637 const char *op_root_relpath;
638 SVN_ERR(svn_wc__db_scan_addition_internal(
643 &scanned_original_relpath,
644 NULL /* original_repos_id */,
646 wcroot, entry_relpath,
647 result_pool, scratch_pool));
649 SVN_ERR(svn_wc__db_fetch_repos_info(&entry->repos, &entry->uuid,
650 wcroot, repos_id, result_pool));
652 if (!op_root_relpath)
653 op_root_abspath = NULL;
655 op_root_abspath = svn_dirent_join(wcroot->abspath, op_root_relpath,
658 /* In wc.db we want to keep the valid revision of the not-present
659 BASE_REV, but when we used entries we set the revision to 0
660 when adding a new node over a not present base node. */
661 if (work_status == svn_wc__db_status_added
666 if (!SVN_IS_VALID_REVNUM(entry->cmt_rev)
667 && scanned_original_relpath == NULL)
669 /* There is NOT a last-changed revision (last-changed date and
670 author may be unknown, but we can always check the rev).
671 The absence of a revision implies this node was added WITHOUT
672 any history. Avoid the COPIED checks in the else block. */
673 /* ### scan_addition may need to be updated to avoid returning
674 ### status_copied in this case. */
676 /* For backwards-compatibility purposes we treat moves just like
678 else if (work_status == svn_wc__db_status_copied ||
679 work_status == svn_wc__db_status_moved_here)
681 entry->copied = TRUE;
683 /* If this is a child of a copied subtree, then it should be
685 if (original_repos_relpath == NULL)
687 /* ### what if there is a BASE node under there? */
688 entry->schedule = svn_wc_schedule_normal;
691 /* Copied nodes need to mirror their copyfrom_rev, if they
692 don't have a revision of their own already. */
693 if (!SVN_IS_VALID_REVNUM(entry->revision)
694 || entry->revision == 0 /* added */)
695 entry->revision = original_revision;
698 /* Does this node have copyfrom_* information? */
699 if (scanned_original_relpath != NULL)
701 svn_boolean_t is_copied_child;
702 svn_boolean_t is_mixed_rev = FALSE;
704 SVN_ERR_ASSERT(work_status == svn_wc__db_status_copied ||
705 work_status == svn_wc__db_status_moved_here);
707 /* If this node inherits copyfrom information from an
708 ancestor node, then it must be a copied child. */
709 is_copied_child = (original_repos_relpath == NULL);
711 /* If this node has copyfrom information on it, then it may
712 be an actual copy-root, or it could be participating in
713 a mixed-revision copied tree. So if we don't already know
714 this is a copied child, then we need to look for this
715 mixed-revision situation. */
716 if (!is_copied_child)
718 const char *parent_relpath;
720 const char *parent_repos_relpath;
721 const char *parent_root_url;
722 apr_int64_t parent_repos_id;
723 const char *op_root_relpath;
725 /* When we insert entries into the database, we will
726 construct additional copyfrom records for mixed-revision
727 copies. The old entries would simply record the different
728 revision in the entry->revision field. That is not
729 available within wc-ng, so additional copies are made
730 (see the logic inside write_entry()). However, when
731 reading these back *out* of the database, the additional
732 copies look like new "Added" nodes rather than a simple
733 mixed-rev working copy.
735 That would be a behavior change if we did not compensate.
736 If there is copyfrom information for this node, then the
737 code below looks at the parent to detect if it *also* has
738 copyfrom information, and if the copyfrom_url would align
739 properly. If it *does*, then we omit storing copyfrom_url
740 and copyfrom_rev (ie. inherit the copyfrom info like a
741 normal child), and update entry->revision with the
742 copyfrom_rev in order to (re)create the mixed-rev copied
743 subtree that was originally presented for storage. */
745 /* Get the copyfrom information from our parent.
747 Note that the parent could be added/copied/moved-here.
748 There is no way for it to be deleted/moved-away and
749 have *this* node appear as copied. */
750 parent_relpath = svn_relpath_dirname(entry_relpath,
752 err = svn_wc__db_scan_addition_internal(
756 &parent_repos_relpath,
759 wcroot, parent_relpath,
764 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
765 return svn_error_trace(err);
766 svn_error_clear(err);
767 op_root_abspath = NULL;
768 parent_repos_relpath = NULL;
769 parent_root_url = NULL;
773 SVN_ERR(svn_wc__db_fetch_repos_info(&parent_root_url, NULL,
774 wcroot, parent_repos_id,
776 op_root_abspath = svn_dirent_join(wcroot->abspath,
781 if (parent_root_url != NULL
782 && strcmp(original_root_url, parent_root_url) == 0)
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 parent_repos_relpath, relpath_to_entry, scratch_pool);
790 /* The copyfrom repos roots matched.
792 Now we look to see if the copyfrom path of the parent
793 would align with our own path. If so, then it means
794 this copyfrom was spontaneously created and inserted
795 for mixed-rev purposes and can be eliminated without
796 changing the semantics of a mixed-rev copied subtree.
798 See notes/api-errata/wc003.txt for some additional
799 detail, and potential issues. */
800 if (strcmp(entry_repos_relpath,
801 original_repos_relpath) == 0)
803 is_copied_child = TRUE;
811 /* We won't be settig the copyfrom_url, yet need to
812 clear out the copyfrom_rev. Thus, this node becomes a
813 child of a copied subtree (rather than its own root). */
814 entry->copyfrom_rev = SVN_INVALID_REVNUM;
816 /* Children in a copied subtree are schedule normal
817 since we don't plan to actually *do* anything with
818 them. Their operation is implied by ancestors. */
819 entry->schedule = svn_wc_schedule_normal;
821 /* And *finally* we turn this entry into the mixed
822 revision node that it was intended to be. This
823 node's revision is taken from the copyfrom record
824 that we spontaneously constructed. */
826 entry->revision = original_revision;
828 else if (original_repos_relpath != NULL)
830 entry->copyfrom_url =
831 svn_path_url_add_component2(original_root_url,
832 original_repos_relpath,
837 /* NOTE: if original_repos_relpath == NULL, then the
838 second call to scan_addition() will not have occurred.
839 Thus, this use of OP_ROOT_ABSPATH still contains the
840 original value where we fetched a value for
841 SCANNED_REPOS_RELPATH. */
842 const char *relpath_to_entry = svn_dirent_is_child(
843 op_root_abspath, entry_abspath, NULL);
844 const char *entry_repos_relpath = svn_relpath_join(
845 scanned_original_relpath, relpath_to_entry, scratch_pool);
847 entry->copyfrom_url =
848 svn_path_url_add_component2(original_root_url,
854 else if (status == svn_wc__db_status_not_present)
856 /* ### buh. 'deleted' nodes are actually supposed to be
857 ### schedule "normal" since we aren't going to actually *do*
858 ### anything to this node at commit time. */
859 entry->schedule = svn_wc_schedule_normal;
860 entry->deleted = TRUE;
862 else if (status == svn_wc__db_status_server_excluded)
864 entry->absent = TRUE;
866 else if (status == svn_wc__db_status_excluded)
868 entry->schedule = svn_wc_schedule_normal;
869 entry->depth = svn_depth_exclude;
873 /* ### we should have handled all possible status values. */
874 SVN_ERR_MALFUNCTION();
877 /* ### higher levels want repos information about deleted nodes, even
878 ### tho they are not "part of" a repository any more. */
879 if (entry->schedule == svn_wc_schedule_delete)
881 SVN_ERR(get_info_for_deleted(entry,
887 wcroot, entry_relpath,
889 have_base, have_more_work,
890 result_pool, scratch_pool));
893 /* ### default to the infinite depth if we don't know it. */
894 if (entry->depth == svn_depth_unknown)
895 entry->depth = svn_depth_infinity;
897 if (kind == svn_node_dir)
898 entry->kind = svn_node_dir;
899 else if (kind == svn_node_file)
900 entry->kind = svn_node_file;
901 else if (kind == svn_node_symlink)
902 entry->kind = svn_node_file; /* ### no symlink kind */
904 entry->kind = svn_node_unknown;
906 /* We should always have a REPOS_RELPATH, except for:
908 - certain obstructed nodes
913 ### the last three should probably have an "implied" REPOS_RELPATH
915 SVN_ERR_ASSERT(repos_relpath != NULL
916 || entry->schedule == svn_wc_schedule_delete
917 || status == svn_wc__db_status_not_present
918 || status == svn_wc__db_status_server_excluded
919 || status == svn_wc__db_status_excluded);
921 entry->url = svn_path_url_add_component2(entry->repos,
927 /* We got a SHA-1, get the corresponding MD-5. */
928 if (checksum->kind != svn_checksum_md5)
929 SVN_ERR(svn_wc__db_pristine_get_md5(&checksum, db,
930 dir_abspath, checksum,
931 scratch_pool, scratch_pool));
933 SVN_ERR_ASSERT(checksum->kind == svn_checksum_md5);
934 entry->checksum = svn_checksum_to_cstring(checksum, result_pool);
939 svn_skel_t *conflict;
940 svn_boolean_t text_conflicted;
941 svn_boolean_t prop_conflicted;
942 SVN_ERR(svn_wc__db_read_conflict_internal(&conflict, NULL, NULL,
943 wcroot, entry_relpath,
944 scratch_pool, scratch_pool));
946 SVN_ERR(svn_wc__conflict_read_info(NULL, NULL, &text_conflicted,
947 &prop_conflicted, NULL,
948 db, dir_abspath, conflict,
949 scratch_pool, scratch_pool));
953 const char *my_abspath;
954 const char *their_old_abspath;
955 const char *their_abspath;
956 SVN_ERR(svn_wc__conflict_read_text_conflict(&my_abspath,
960 conflict, scratch_pool,
964 entry->conflict_wrk = svn_dirent_basename(my_abspath, result_pool);
966 if (their_old_abspath)
967 entry->conflict_old = svn_dirent_basename(their_old_abspath,
971 entry->conflict_new = svn_dirent_basename(their_abspath,
977 const char *prej_abspath;
979 SVN_ERR(svn_wc__conflict_read_prop_conflict(&prej_abspath, NULL,
982 conflict, scratch_pool,
986 entry->prejfile = svn_dirent_basename(prej_abspath, result_pool);
992 entry->lock_token = lock->token;
993 entry->lock_owner = lock->owner;
994 entry->lock_comment = lock->comment;
995 entry->lock_creation_date = lock->date;
998 /* Let's check for a file external. ugh. */
999 if (status == svn_wc__db_status_normal
1000 && kind == svn_node_file)
1001 SVN_ERR(check_file_external(entry, db, entry_abspath, dir_abspath,
1002 result_pool, scratch_pool));
1004 entry->working_size = translated_size;
1008 return SVN_NO_ERROR;
1011 /* Read entries for PATH/LOCAL_ABSPATH from DB. The entries
1012 will be allocated in RESULT_POOL, with temporary allocations in
1013 SCRATCH_POOL. The entries are returned in RESULT_ENTRIES. */
1014 static svn_error_t *
1015 read_entries_new(apr_hash_t **result_entries,
1017 const char *dir_abspath,
1018 svn_wc__db_wcroot_t *wcroot,
1019 const char *dir_relpath,
1020 apr_pool_t *result_pool,
1021 apr_pool_t *scratch_pool)
1023 apr_hash_t *entries;
1024 const apr_array_header_t *children;
1025 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1027 const svn_wc_entry_t *parent_entry;
1029 entries = apr_hash_make(result_pool);
1031 SVN_ERR(read_one_entry(&parent_entry,
1033 wcroot, dir_relpath,
1035 NULL /* parent_entry */,
1036 result_pool, iterpool));
1037 svn_hash_sets(entries, "", parent_entry);
1039 /* Use result_pool so that the child names (used by reference, rather
1040 than copied) appear in result_pool. */
1041 SVN_ERR(svn_wc__db_read_children(&children, db,
1043 scratch_pool, iterpool));
1044 for (i = children->nelts; i--; )
1046 const char *name = APR_ARRAY_IDX(children, i, const char *);
1047 const svn_wc_entry_t *entry;
1049 svn_pool_clear(iterpool);
1051 SVN_ERR(read_one_entry(&entry,
1053 wcroot, dir_relpath,
1055 result_pool, iterpool));
1056 svn_hash_sets(entries, entry->name, entry);
1059 svn_pool_destroy(iterpool);
1061 *result_entries = entries;
1063 return SVN_NO_ERROR;
1067 static svn_error_t *
1068 read_entry_pair_txn(const svn_wc_entry_t **parent_entry,
1069 const svn_wc_entry_t **entry,
1071 const char *dir_abspath,
1072 svn_wc__db_wcroot_t *wcroot,
1073 const char *dir_relpath,
1075 apr_pool_t *result_pool,
1076 apr_pool_t *scratch_pool)
1078 SVN_ERR(read_one_entry(parent_entry,
1080 wcroot, dir_relpath,
1082 NULL /* parent_entry */,
1083 result_pool, scratch_pool));
1085 /* If we need the entry for "this dir", then return the parent_entry
1086 in both outputs. Otherwise, read the child node. */
1089 /* If the retrieved node is a FILE, then we have a problem. We asked
1090 for a directory. This implies there is an obstructing, unversioned
1091 directory where a FILE should be. We navigated from the obstructing
1092 subdir up to the parent dir, then returned the FILE found there.
1094 Let's return WC_MISSING cuz the caller thought we had a dir, but
1095 that (versioned subdir) isn't there. */
1096 if ((*parent_entry)->kind == svn_node_file)
1098 *parent_entry = NULL;
1099 return svn_error_createf(SVN_ERR_WC_MISSING, NULL,
1100 _("'%s' is not a versioned working copy"),
1101 svn_dirent_local_style(dir_abspath,
1105 *entry = *parent_entry;
1109 const apr_array_header_t *children;
1112 /* Default to not finding the child. */
1115 /* Determine whether the parent KNOWS about this child. If it does
1116 not, then we should not attempt to look for it.
1118 For example: the parent doesn't "know" about the child, but the
1119 versioned directory *does* exist on disk. We don't want to look
1120 into that subdir. */
1121 SVN_ERR(svn_wc__db_read_children(&children, db, dir_abspath,
1122 scratch_pool, scratch_pool));
1123 for (i = children->nelts; i--; )
1125 const char *child = APR_ARRAY_IDX(children, i, const char *);
1127 if (strcmp(child, name) == 0)
1131 err = read_one_entry(entry,
1133 wcroot, dir_relpath,
1134 name, *parent_entry,
1135 result_pool, scratch_pool);
1138 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
1139 return svn_error_trace(err);
1141 /* No problem. Clear the error and leave the default value
1143 svn_error_clear(err);
1146 /* Found it. No need to keep searching. */
1150 /* if the loop ends without finding a child, then we have the default
1151 ENTRY value of NULL. */
1154 return SVN_NO_ERROR;
1157 /* Read a pair of entries from wc_db in the directory DIR_ABSPATH. Return
1158 the directory's entry in *PARENT_ENTRY and NAME's entry in *ENTRY. The
1159 two returned pointers will be the same if NAME=="" ("this dir").
1161 The parent entry must exist.
1163 The requested entry MAY exist. If it does not, then NULL will be returned.
1165 The resulting entries are allocated in RESULT_POOL, and all temporary
1166 allocations are made in SCRATCH_POOL. */
1167 static svn_error_t *
1168 read_entry_pair(const svn_wc_entry_t **parent_entry,
1169 const svn_wc_entry_t **entry,
1171 const char *dir_abspath,
1173 apr_pool_t *result_pool,
1174 apr_pool_t *scratch_pool)
1176 svn_wc__db_wcroot_t *wcroot;
1177 const char *dir_relpath;
1179 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &dir_relpath,
1181 scratch_pool, scratch_pool));
1182 VERIFY_USABLE_WCROOT(wcroot);
1184 SVN_WC__DB_WITH_TXN(read_entry_pair_txn(parent_entry, entry,
1186 wcroot, dir_relpath,
1188 result_pool, scratch_pool),
1191 return SVN_NO_ERROR;
1195 static svn_error_t *
1196 read_entries(apr_hash_t **entries,
1198 const char *dir_abspath,
1199 apr_pool_t *result_pool,
1200 apr_pool_t *scratch_pool)
1202 svn_wc__db_wcroot_t *wcroot;
1203 const char *dir_relpath;
1206 SVN_ERR(svn_wc__db_temp_get_format(&wc_format, db, dir_abspath,
1209 if (wc_format < SVN_WC__WC_NG_VERSION)
1210 return svn_error_trace(svn_wc__read_entries_old(entries,
1215 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &dir_relpath,
1217 scratch_pool, scratch_pool));
1218 VERIFY_USABLE_WCROOT(wcroot);
1220 SVN_WC__DB_WITH_TXN(read_entries_new(entries,
1222 wcroot, dir_relpath,
1223 result_pool, scratch_pool),
1226 return SVN_NO_ERROR;
1230 /* For a given LOCAL_ABSPATH, using DB, set *ADM_ABSPATH to the directory in
1231 which the entry information is located, and *ENTRY_NAME to the entry name
1232 to access that entry.
1234 KIND is as in svn_wc__get_entry().
1236 Return the results in RESULT_POOL and use SCRATCH_POOL for temporary
1238 static svn_error_t *
1239 get_entry_access_info(const char **adm_abspath,
1240 const char **entry_name,
1242 const char *local_abspath,
1243 svn_node_kind_t kind,
1244 apr_pool_t *result_pool,
1245 apr_pool_t *scratch_pool)
1247 svn_wc_adm_access_t *adm_access;
1248 svn_boolean_t read_from_subdir = FALSE;
1250 /* If the caller didn't know the node kind, then stat the path. Maybe
1251 it is really there, and we can speed up the steps below. */
1252 if (kind == svn_node_unknown)
1254 svn_node_kind_t on_disk;
1256 /* Do we already have an access baton for LOCAL_ABSPATH? */
1257 adm_access = svn_wc__adm_retrieve_internal2(db, local_abspath,
1261 /* Sweet. The node is a directory. */
1262 on_disk = svn_node_dir;
1266 svn_boolean_t special;
1268 /* What's on disk? */
1269 SVN_ERR(svn_io_check_special_path(local_abspath, &on_disk, &special,
1273 if (on_disk != svn_node_dir)
1275 /* If this is *anything* besides a directory (FILE, NONE, or
1276 UNKNOWN), then we cannot treat it as a versioned directory
1277 containing entries to read. Leave READ_FROM_SUBDIR as FALSE,
1278 so that the parent will be examined.
1280 For NONE and UNKNOWN, it may be that metadata exists for the
1281 node, even though on-disk is unhelpful.
1283 If NEED_PARENT_STUB is TRUE, and the entry is not a DIRECTORY,
1286 If NEED_PARENT_STUB if FALSE, and we successfully read a stub,
1287 then this on-disk node is obstructing the read. */
1291 /* We found a directory for this UNKNOWN node. Determine whether
1292 we need to read inside it. */
1293 read_from_subdir = TRUE;
1296 else if (kind == svn_node_dir)
1298 read_from_subdir = TRUE;
1301 if (read_from_subdir)
1303 /* KIND must be a DIR or UNKNOWN (and we found a subdir). We want
1304 the "real" data, so treat LOCAL_ABSPATH as a versioned directory. */
1305 *adm_abspath = apr_pstrdup(result_pool, local_abspath);
1310 /* FILE node needs to read the parent directory. Or a DIR node
1311 needs to read from the parent to get at the stub entry. Or this
1312 is an UNKNOWN node, and we need to examine the parent. */
1313 svn_dirent_split(adm_abspath, entry_name, local_abspath, result_pool);
1316 return SVN_NO_ERROR;
1321 svn_wc__get_entry(const svn_wc_entry_t **entry,
1323 const char *local_abspath,
1324 svn_boolean_t allow_unversioned,
1325 svn_node_kind_t kind,
1326 apr_pool_t *result_pool,
1327 apr_pool_t *scratch_pool)
1329 const char *dir_abspath;
1330 const char *entry_name;
1332 SVN_ERR(get_entry_access_info(&dir_abspath, &entry_name, db, local_abspath,
1333 kind, scratch_pool, scratch_pool));
1336 const svn_wc_entry_t *parent_entry;
1339 /* NOTE: if KIND is UNKNOWN and we decided to examine the *parent*
1340 directory, then it is possible we moved out of the working copy.
1341 If the on-disk node is a DIR, and we asked for a stub, then we
1342 obviously can't provide that (parent has no info). If the on-disk
1343 node is a FILE/NONE/UNKNOWN, then it is obstructing the real
1344 LOCAL_ABSPATH (or it was never a versioned item). In all these
1345 cases, the read_entries() will (properly) throw an error.
1347 NOTE: if KIND is a DIR and we asked for the real data, but it is
1348 obstructed on-disk by some other node kind (NONE, FILE, UNKNOWN),
1349 then this will throw an error. */
1351 err = read_entry_pair(&parent_entry, entry,
1352 db, dir_abspath, entry_name,
1353 result_pool, scratch_pool);
1356 if (err->apr_err != SVN_ERR_WC_MISSING || kind != svn_node_unknown
1357 || *entry_name != '\0')
1358 return svn_error_trace(err);
1359 svn_error_clear(err);
1361 /* The caller didn't know the node type, we saw a directory there,
1362 we attempted to read IN that directory, and then wc_db reports
1363 that it is NOT a working copy directory. It is possible that
1364 one of two things has happened:
1366 1) a directory is obstructing a file in the parent
1367 2) the (versioned) directory's contents have been removed
1369 Let's assume situation (1); if that is true, then we can just
1370 return the newly-found data.
1372 If we assumed (2), then a valid result still won't help us
1373 since the caller asked for the actual contents, not the stub
1374 (which is why we read *into* the directory). However, if we
1375 assume (1) and get back a stub, then we have verified a
1376 missing, versioned directory, and can return an error
1379 Redo the fetch, but "insist" we are trying to find a file.
1380 This will read from the parent directory of the "file". */
1381 err = svn_wc__get_entry(entry, db, local_abspath, allow_unversioned,
1382 svn_node_file, result_pool, scratch_pool);
1383 if (err == SVN_NO_ERROR)
1384 return SVN_NO_ERROR;
1385 if (err->apr_err != SVN_ERR_NODE_UNEXPECTED_KIND)
1386 return svn_error_trace(err);
1387 svn_error_clear(err);
1389 /* We asked for a FILE, but the node found is a DIR. Thus, we
1390 are looking at a stub. Originally, we tried to read into the
1391 subdir because NEED_PARENT_STUB is FALSE. The stub we just
1392 read is not going to work for the caller, so inform them of
1393 the missing subdirectory. */
1394 SVN_ERR_ASSERT(*entry != NULL && (*entry)->kind == svn_node_dir);
1395 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
1396 _("Admin area of '%s' is missing"),
1397 svn_dirent_local_style(local_abspath,
1404 if (allow_unversioned)
1405 return SVN_NO_ERROR;
1406 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
1407 _("'%s' is not under version control"),
1408 svn_dirent_local_style(local_abspath,
1412 /* The caller had the wrong information. */
1413 if ((kind == svn_node_file && (*entry)->kind != svn_node_file)
1414 || (kind == svn_node_dir && (*entry)->kind != svn_node_dir))
1415 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
1416 _("'%s' is not of the right kind"),
1417 svn_dirent_local_style(local_abspath,
1420 return SVN_NO_ERROR;
1423 /* TODO ### Rewrite doc string to mention ENTRIES_ALL; not ADM_ACCESS.
1425 Prune the deleted entries from the cached entries in ADM_ACCESS, and
1426 return that collection in *ENTRIES_PRUNED. SCRATCH_POOL is used for local,
1427 short term, memory allocation, RESULT_POOL for permanent stuff. */
1428 static svn_error_t *
1429 prune_deleted(apr_hash_t **entries_pruned,
1430 apr_hash_t *entries_all,
1431 apr_pool_t *result_pool,
1432 apr_pool_t *scratch_pool)
1434 apr_hash_index_t *hi;
1438 *entries_pruned = NULL;
1439 return SVN_NO_ERROR;
1442 /* I think it will be common for there to be no deleted entries, so
1443 it is worth checking for that case as we can optimise it. */
1444 for (hi = apr_hash_first(scratch_pool, entries_all);
1446 hi = apr_hash_next(hi))
1448 svn_boolean_t hidden;
1450 SVN_ERR(svn_wc__entry_is_hidden(&hidden,
1451 apr_hash_this_val(hi)));
1458 /* There are no deleted entries, so we can use the full hash */
1459 *entries_pruned = entries_all;
1460 return SVN_NO_ERROR;
1463 /* Construct pruned hash without deleted entries */
1464 *entries_pruned = apr_hash_make(result_pool);
1465 for (hi = apr_hash_first(scratch_pool, entries_all);
1467 hi = apr_hash_next(hi))
1469 const void *key = apr_hash_this_key(hi);
1470 const svn_wc_entry_t *entry = apr_hash_this_val(hi);
1471 svn_boolean_t hidden;
1473 SVN_ERR(svn_wc__entry_is_hidden(&hidden, entry));
1475 svn_hash_sets(*entries_pruned, key, entry);
1478 return SVN_NO_ERROR;
1482 svn_wc__entries_read_internal(apr_hash_t **entries,
1483 svn_wc_adm_access_t *adm_access,
1484 svn_boolean_t show_hidden,
1487 apr_hash_t *new_entries;
1489 new_entries = svn_wc__adm_access_entries(adm_access);
1492 svn_wc__db_t *db = svn_wc__adm_get_db(adm_access);
1493 const char *local_abspath = svn_wc__adm_access_abspath(adm_access);
1494 apr_pool_t *result_pool = svn_wc__adm_access_pool_internal(adm_access);
1496 SVN_ERR(read_entries(&new_entries, db, local_abspath,
1497 result_pool, pool));
1499 svn_wc__adm_access_set_entries(adm_access, new_entries);
1503 *entries = new_entries;
1505 SVN_ERR(prune_deleted(entries, new_entries,
1506 svn_wc__adm_access_pool_internal(adm_access),
1509 return SVN_NO_ERROR;
1513 svn_wc_entries_read(apr_hash_t **entries,
1514 svn_wc_adm_access_t *adm_access,
1515 svn_boolean_t show_hidden,
1518 return svn_error_trace(svn_wc__entries_read_internal(entries, adm_access,
1519 show_hidden, pool));
1522 /* No transaction required: called from write_entry which is itself
1523 transaction-wrapped. */
1524 static svn_error_t *
1525 insert_node(svn_sqlite__db_t *sdb,
1526 const db_node_t *node,
1527 apr_pool_t *scratch_pool)
1529 svn_sqlite__stmt_t *stmt;
1530 svn_boolean_t present = (node->presence == svn_wc__db_status_normal
1531 || node->presence == svn_wc__db_status_incomplete);
1533 SVN_ERR_ASSERT(node->op_depth > 0 || node->repos_relpath);
1535 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_NODE));
1536 SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnnnsn",
1538 node->local_relpath,
1540 node->parent_relpath,
1541 /* Setting depth for files? */
1542 (node->kind == svn_node_dir && present)
1543 ? svn_depth_to_word(node->depth)
1546 if (present && node->repos_relpath)
1548 SVN_ERR(svn_sqlite__bind_revnum(stmt, 11, node->changed_rev));
1549 SVN_ERR(svn_sqlite__bind_int64(stmt, 12, node->changed_date));
1550 SVN_ERR(svn_sqlite__bind_text(stmt, 13, node->changed_author));
1553 if (node->repos_relpath
1554 && node->presence != svn_wc__db_status_base_deleted)
1556 SVN_ERR(svn_sqlite__bind_int64(stmt, 5,
1558 SVN_ERR(svn_sqlite__bind_text(stmt, 6,
1559 node->repos_relpath));
1560 SVN_ERR(svn_sqlite__bind_revnum(stmt, 7, node->revision));
1563 SVN_ERR(svn_sqlite__bind_token(stmt, 8, presence_map, node->presence));
1565 if (node->kind == svn_node_none)
1566 SVN_ERR(svn_sqlite__bind_text(stmt, 10, "unknown"));
1568 SVN_ERR(svn_sqlite__bind_token(stmt, 10, kind_map, node->kind));
1570 if (node->kind == svn_node_file && present)
1573 && node->op_depth == 0
1574 && node->presence != svn_wc__db_status_not_present
1575 && node->presence != svn_wc__db_status_excluded
1576 && node->presence != svn_wc__db_status_server_excluded)
1577 return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
1578 _("The file '%s' has no checksum"),
1579 svn_dirent_local_style(node->local_relpath,
1582 SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, node->checksum,
1585 if (node->repos_relpath)
1587 if (node->recorded_size != SVN_INVALID_FILESIZE)
1588 SVN_ERR(svn_sqlite__bind_int64(stmt, 16, node->recorded_size));
1590 SVN_ERR(svn_sqlite__bind_int64(stmt, 17, node->recorded_time));
1594 /* ### Never set, props done later */
1595 if (node->properties && present && node->repos_relpath)
1596 SVN_ERR(svn_sqlite__bind_properties(stmt, 15, node->properties,
1599 if (node->file_external)
1600 SVN_ERR(svn_sqlite__bind_int(stmt, 20, 1));
1602 if (node->inherited_props && present)
1603 SVN_ERR(svn_sqlite__bind_iprops(stmt, 23, node->inherited_props,
1606 SVN_ERR(svn_sqlite__insert(NULL, stmt));
1608 return SVN_NO_ERROR;
1613 static svn_error_t *
1614 insert_actual_node(svn_sqlite__db_t *sdb,
1616 const char *wri_abspath,
1617 const db_actual_node_t *actual_node,
1618 apr_pool_t *scratch_pool)
1620 svn_sqlite__stmt_t *stmt;
1621 svn_skel_t *conflict_data = NULL;
1623 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_ACTUAL_NODE));
1625 SVN_ERR(svn_sqlite__bind_int64(stmt, 1, actual_node->wc_id));
1626 SVN_ERR(svn_sqlite__bind_text(stmt, 2, actual_node->local_relpath));
1627 SVN_ERR(svn_sqlite__bind_text(stmt, 3, actual_node->parent_relpath));
1629 if (actual_node->properties)
1630 SVN_ERR(svn_sqlite__bind_properties(stmt, 4, actual_node->properties,
1633 if (actual_node->changelist)
1634 SVN_ERR(svn_sqlite__bind_text(stmt, 5, actual_node->changelist));
1636 SVN_ERR(svn_wc__upgrade_conflict_skel_from_raw(
1639 actual_node->local_relpath,
1640 actual_node->conflict_old,
1641 actual_node->conflict_working,
1642 actual_node->conflict_new,
1643 actual_node->prop_reject,
1644 actual_node->tree_conflict_data,
1645 actual_node->tree_conflict_data
1646 ? strlen(actual_node->tree_conflict_data)
1648 scratch_pool, scratch_pool));
1652 svn_stringbuf_t *data = svn_skel__unparse(conflict_data, scratch_pool);
1654 SVN_ERR(svn_sqlite__bind_blob(stmt, 6, data->data, data->len));
1657 /* Execute and reset the insert clause. */
1658 return svn_error_trace(svn_sqlite__insert(NULL, stmt));
1661 static svn_boolean_t
1662 is_switched(db_node_t *parent,
1664 apr_pool_t *scratch_pool)
1666 if (parent && child)
1668 if (parent->repos_id != child->repos_id)
1671 if (parent->repos_relpath && child->repos_relpath)
1673 const char *unswitched
1674 = svn_relpath_join(parent->repos_relpath,
1675 svn_relpath_basename(child->local_relpath,
1678 if (strcmp(unswitched, child->repos_relpath))
1686 struct write_baton {
1689 db_node_t *below_work;
1690 apr_hash_t *tree_conflicts;
1693 #define WRITE_ENTRY_ASSERT(expr) \
1695 return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL, \
1696 _("Unable to upgrade '%s' at line %d"), \
1697 svn_dirent_local_style( \
1698 svn_dirent_join(root_abspath, \
1701 scratch_pool), __LINE__)
1703 /* Write the information for ENTRY to WC_DB. The WC_ID, REPOS_ID and
1704 REPOS_ROOT will all be used for writing ENTRY.
1705 ### transitioning from straight sql to using the wc_db APIs. For the
1706 ### time being, we'll need both parameters. */
1707 static svn_error_t *
1708 write_entry(struct write_baton **entry_node,
1709 const struct write_baton *parent_node,
1711 svn_sqlite__db_t *sdb,
1713 apr_int64_t repos_id,
1714 const svn_wc_entry_t *entry,
1715 const svn_wc__text_base_info_t *text_base_info,
1716 const char *local_relpath,
1717 const char *tmp_entry_abspath,
1718 const char *root_abspath,
1719 const svn_wc_entry_t *this_dir,
1720 svn_boolean_t create_locks,
1721 apr_pool_t *result_pool,
1722 apr_pool_t *scratch_pool)
1724 db_node_t *base_node = NULL;
1725 db_node_t *working_node = NULL, *below_working_node = NULL;
1726 db_actual_node_t *actual_node = NULL;
1727 const char *parent_relpath;
1728 apr_hash_t *tree_conflicts;
1730 if (*local_relpath == '\0')
1731 parent_relpath = NULL;
1733 parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
1735 /* This is how it should work, it doesn't work like this yet because
1736 we need proper op_depth to layer the working nodes.
1738 Using "svn add", "svn rm", "svn cp" only files can be replaced
1739 pre-wcng; directories can only be normal, deleted or added.
1740 Files cannot be replaced within a deleted directory, so replaced
1741 files can only exist in a normal directory, or a directory that
1742 is added+copied. In a normal directory a replaced file needs a
1743 base node and a working node, in an added+copied directory a
1744 replaced file needs two working nodes at different op-depths.
1746 With just the above operations the conversion for files and
1747 directories is straightforward:
1750 parent child parent child
1752 normal normal base base
1753 add+copied normal+copied work work
1754 normal+copied normal+copied work work
1755 normal delete base base+work
1756 delete delete base+work base+work
1757 add+copied delete work work
1758 normal add base work
1760 add+copied add work work
1761 normal add+copied base work
1762 add add+copied work work
1763 add+copied add+copied work work
1764 normal replace base base+work
1765 add+copied replace work work+work
1766 normal replace+copied base base+work
1767 add+copied replace+copied work work+work
1769 However "svn merge" make this more complicated. The pre-wcng
1770 "svn merge" is capable of replacing a directory, that is it can
1771 mark the whole tree deleted, and then copy another tree on top.
1772 The entries then represent the replacing tree overlayed on the
1775 original replace schedule in
1776 tree tree combined tree
1780 A/g A/g replace+copied
1782 A/B A/B replace+copied
1784 A/B/g A/B/g replace+copied
1791 The original tree could be normal tree, or an add+copied tree.
1792 Committing such a merge generally worked, but making further tree
1793 modifications before commit sometimes failed.
1795 The root of the replace is handled like the file replace:
1798 parent child parent child
1800 normal replace+copied base base+work
1801 add+copied replace+copied work work+work
1803 although obviously the node is a directory rather then a file.
1804 There are then more conversion states where the parent is
1808 parent child parent child
1810 replace+copied add [base|work]+work work
1811 replace+copied add+copied [base|work]+work work
1812 replace+copied delete+copied [base|work]+work [base|work]+work
1813 delete+copied delete+copied [base|work]+work [base|work]+work
1814 replace+copied replace+copied [base|work]+work [base|work]+work
1817 WRITE_ENTRY_ASSERT(parent_node || entry->schedule == svn_wc_schedule_normal);
1819 WRITE_ENTRY_ASSERT(!parent_node || parent_node->base
1820 || parent_node->below_work || parent_node->work);
1822 switch (entry->schedule)
1824 case svn_wc_schedule_normal:
1825 if (entry->copied ||
1826 (entry->depth == svn_depth_exclude
1827 && parent_node && !parent_node->base && parent_node->work))
1828 working_node = MAYBE_ALLOC(working_node, result_pool);
1830 base_node = MAYBE_ALLOC(base_node, result_pool);
1833 case svn_wc_schedule_add:
1834 working_node = MAYBE_ALLOC(working_node, result_pool);
1837 if (parent_node->base)
1838 base_node = MAYBE_ALLOC(base_node, result_pool);
1840 below_working_node = MAYBE_ALLOC(below_working_node, result_pool);
1844 case svn_wc_schedule_delete:
1845 working_node = MAYBE_ALLOC(working_node, result_pool);
1846 if (parent_node->base)
1847 base_node = MAYBE_ALLOC(base_node, result_pool);
1848 if (parent_node->work)
1849 below_working_node = MAYBE_ALLOC(below_working_node, result_pool);
1852 case svn_wc_schedule_replace:
1853 working_node = MAYBE_ALLOC(working_node, result_pool);
1854 if (parent_node->base)
1855 base_node = MAYBE_ALLOC(base_node, result_pool);
1857 below_working_node = MAYBE_ALLOC(below_working_node, result_pool);
1861 /* Something deleted in this revision means there should always be a
1862 BASE node to indicate the not-present node. */
1865 WRITE_ENTRY_ASSERT(base_node || below_working_node);
1866 WRITE_ENTRY_ASSERT(!entry->incomplete);
1868 base_node->presence = svn_wc__db_status_not_present;
1870 below_working_node->presence = svn_wc__db_status_not_present;
1872 else if (entry->absent)
1874 WRITE_ENTRY_ASSERT(base_node && !working_node && !below_working_node);
1875 WRITE_ENTRY_ASSERT(!entry->incomplete);
1876 base_node->presence = svn_wc__db_status_server_excluded;
1881 db_node_t *work = parent_node->work
1883 : parent_node->below_work;
1885 if (entry->copyfrom_url)
1887 working_node->repos_id = repos_id;
1888 working_node->repos_relpath = svn_uri_skip_ancestor(
1889 this_dir->repos, entry->copyfrom_url,
1891 working_node->revision = entry->copyfrom_rev;
1892 working_node->op_depth
1893 = svn_wc__db_op_depth_for_upgrade(local_relpath);
1895 if (work && work->repos_relpath
1896 && work->repos_id == repos_id
1897 && work->revision == entry->copyfrom_rev)
1901 name = svn_relpath_skip_ancestor(work->repos_relpath,
1902 working_node->repos_relpath);
1905 && !strcmp(name, svn_relpath_basename(local_relpath, NULL)))
1907 working_node->op_depth = work->op_depth;
1911 else if (work && work->repos_relpath)
1913 working_node->repos_id = repos_id;
1914 working_node->repos_relpath
1915 = svn_relpath_join(work->repos_relpath,
1916 svn_relpath_basename(local_relpath, NULL),
1918 working_node->revision = work->revision;
1919 working_node->op_depth = work->op_depth;
1921 else if (parent_node->below_work
1922 && parent_node->below_work->repos_relpath)
1924 /* Parent deleted, this not-present or similar */
1925 working_node->repos_id = repos_id;
1926 working_node->repos_relpath
1927 = svn_relpath_join(parent_node->below_work->repos_relpath,
1928 svn_relpath_basename(local_relpath, NULL),
1930 working_node->revision = parent_node->below_work->revision;
1931 working_node->op_depth = parent_node->below_work->op_depth;
1934 return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL,
1935 _("No copyfrom URL for '%s'"),
1936 svn_dirent_local_style(local_relpath,
1939 if (work && work->op_depth != working_node->op_depth
1940 && work->repos_relpath
1941 && work->repos_id == working_node->repos_id
1942 && work->presence == svn_wc__db_status_normal
1943 && !below_working_node)
1945 /* Introduce a not-present node! */
1946 below_working_node = MAYBE_ALLOC(below_working_node, scratch_pool);
1948 below_working_node->wc_id = wc_id;
1949 below_working_node->op_depth = work->op_depth;
1950 below_working_node->local_relpath = local_relpath;
1951 below_working_node->parent_relpath = parent_relpath;
1953 below_working_node->presence = svn_wc__db_status_not_present;
1954 below_working_node->repos_id = repos_id;
1955 below_working_node->repos_relpath = working_node->local_relpath;
1957 SVN_ERR(insert_node(sdb, below_working_node, scratch_pool));
1959 below_working_node = NULL; /* Don't write a present intermediate! */
1963 if (entry->conflict_old)
1965 actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
1966 if (parent_relpath && entry->conflict_old)
1967 actual_node->conflict_old = svn_relpath_join(parent_relpath,
1968 entry->conflict_old,
1971 actual_node->conflict_old = entry->conflict_old;
1972 if (parent_relpath && entry->conflict_new)
1973 actual_node->conflict_new = svn_relpath_join(parent_relpath,
1974 entry->conflict_new,
1977 actual_node->conflict_new = entry->conflict_new;
1978 if (parent_relpath && entry->conflict_wrk)
1979 actual_node->conflict_working = svn_relpath_join(parent_relpath,
1980 entry->conflict_wrk,
1983 actual_node->conflict_working = entry->conflict_wrk;
1986 if (entry->prejfile)
1988 actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
1989 actual_node->prop_reject = svn_relpath_join((entry->kind == svn_node_dir
1996 if (entry->changelist)
1998 actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
1999 actual_node->changelist = entry->changelist;
2002 /* ### set the text_mod value? */
2004 if (entry_node && entry->tree_conflict_data)
2006 /* Issues #3840/#3916: 1.6 stores multiple tree conflicts on the
2007 parent node, 1.7 stores them directly on the conflited nodes.
2008 So "((skel1) (skel2))" becomes "(skel1)" and "(skel2)" */
2011 skel = svn_skel__parse(entry->tree_conflict_data,
2012 strlen(entry->tree_conflict_data),
2014 tree_conflicts = apr_hash_make(result_pool);
2015 skel = skel->children;
2018 svn_wc_conflict_description2_t *conflict;
2019 svn_skel_t *new_skel;
2022 /* *CONFLICT is allocated so it is safe to use a non-const pointer */
2023 SVN_ERR(svn_wc__deserialize_conflict(
2024 (const svn_wc_conflict_description2_t**)&conflict,
2026 svn_dirent_join(root_abspath,
2029 scratch_pool, scratch_pool));
2031 WRITE_ENTRY_ASSERT(conflict->kind == svn_wc_conflict_kind_tree);
2033 SVN_ERR(svn_wc__serialize_conflict(&new_skel, conflict,
2034 scratch_pool, scratch_pool));
2036 /* Store in hash to be retrieved when writing the child
2038 key = svn_dirent_skip_ancestor(root_abspath, conflict->local_abspath);
2039 svn_hash_sets(tree_conflicts, apr_pstrdup(result_pool, key),
2040 svn_skel__unparse(new_skel, result_pool)->data);
2045 tree_conflicts = NULL;
2047 if (parent_node && parent_node->tree_conflicts)
2049 const char *tree_conflict_data =
2050 svn_hash_gets(parent_node->tree_conflicts, local_relpath);
2051 if (tree_conflict_data)
2053 actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
2054 actual_node->tree_conflict_data = tree_conflict_data;
2057 /* Reset hash so that we don't write the row again when writing
2058 actual-only nodes */
2059 svn_hash_sets(parent_node->tree_conflicts, local_relpath, NULL);
2062 if (entry->file_external_path != NULL)
2064 base_node = MAYBE_ALLOC(base_node, result_pool);
2068 /* Insert the base node. */
2071 base_node->wc_id = wc_id;
2072 base_node->local_relpath = local_relpath;
2073 base_node->op_depth = 0;
2074 base_node->parent_relpath = parent_relpath;
2075 base_node->revision = entry->revision;
2076 base_node->recorded_time = entry->text_time;
2077 base_node->recorded_size = entry->working_size;
2079 if (entry->depth != svn_depth_exclude)
2080 base_node->depth = entry->depth;
2083 base_node->presence = svn_wc__db_status_excluded;
2084 base_node->depth = svn_depth_infinity;
2089 WRITE_ENTRY_ASSERT(base_node->presence
2090 == svn_wc__db_status_not_present);
2091 /* ### should be svn_node_unknown, but let's store what we have. */
2092 base_node->kind = entry->kind;
2094 else if (entry->absent)
2096 WRITE_ENTRY_ASSERT(base_node->presence
2097 == svn_wc__db_status_server_excluded);
2098 /* ### should be svn_node_unknown, but let's store what we have. */
2099 base_node->kind = entry->kind;
2101 /* Store the most likely revision in the node to avoid
2102 base nodes without a valid revision. Of course
2103 we remember that the data is still incomplete. */
2104 if (!SVN_IS_VALID_REVNUM(base_node->revision) && parent_node->base)
2105 base_node->revision = parent_node->base->revision;
2109 base_node->kind = entry->kind;
2111 if (base_node->presence != svn_wc__db_status_excluded)
2113 /* All subdirs are initially incomplete, they stop being
2114 incomplete when the entries file in the subdir is
2115 upgraded and remain incomplete if that doesn't happen. */
2116 if (entry->kind == svn_node_dir
2117 && strcmp(entry->name, SVN_WC_ENTRY_THIS_DIR))
2119 base_node->presence = svn_wc__db_status_incomplete;
2121 /* Store the most likely revision in the node to avoid
2122 base nodes without a valid revision. Of course
2123 we remember that the data is still incomplete. */
2124 if (parent_node->base)
2125 base_node->revision = parent_node->base->revision;
2127 else if (entry->incomplete)
2129 /* ### nobody should have set the presence. */
2130 WRITE_ENTRY_ASSERT(base_node->presence
2131 == svn_wc__db_status_normal);
2132 base_node->presence = svn_wc__db_status_incomplete;
2137 if (entry->kind == svn_node_dir)
2138 base_node->checksum = NULL;
2141 if (text_base_info && text_base_info->revert_base.sha1_checksum)
2142 base_node->checksum = text_base_info->revert_base.sha1_checksum;
2143 else if (text_base_info && text_base_info->normal_base.sha1_checksum)
2144 base_node->checksum = text_base_info->normal_base.sha1_checksum;
2146 base_node->checksum = NULL;
2148 /* The base MD5 checksum is available in the entry, unless there
2149 * is a copied WORKING node. If possible, verify that the entry
2150 * checksum matches the base file that we found. */
2151 if (! (working_node && entry->copied))
2153 svn_checksum_t *entry_md5_checksum, *found_md5_checksum;
2154 SVN_ERR(svn_checksum_parse_hex(&entry_md5_checksum,
2156 entry->checksum, scratch_pool));
2157 if (text_base_info && text_base_info->revert_base.md5_checksum)
2158 found_md5_checksum = text_base_info->revert_base.md5_checksum;
2159 else if (text_base_info
2160 && text_base_info->normal_base.md5_checksum)
2161 found_md5_checksum = text_base_info->normal_base.md5_checksum;
2163 found_md5_checksum = NULL;
2164 if (entry_md5_checksum && found_md5_checksum &&
2165 !svn_checksum_match(entry_md5_checksum, found_md5_checksum))
2166 return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
2167 _("Bad base MD5 checksum for '%s'; "
2168 "expected: '%s'; found '%s'; "),
2169 svn_dirent_local_style(
2170 svn_dirent_join(root_abspath,
2174 svn_checksum_to_cstring_display(
2175 entry_md5_checksum, scratch_pool),
2176 svn_checksum_to_cstring_display(
2177 found_md5_checksum, scratch_pool));
2180 /* ### Not sure what conditions this should cover. */
2181 /* SVN_ERR_ASSERT(entry->deleted || ...); */
2186 if (this_dir->repos)
2188 base_node->repos_id = repos_id;
2190 if (entry->url != NULL)
2192 base_node->repos_relpath = svn_uri_skip_ancestor(
2193 this_dir->repos, entry->url,
2198 const char *relpath = svn_uri_skip_ancestor(this_dir->repos,
2201 if (relpath == NULL || *relpath == '\0')
2202 base_node->repos_relpath = entry->name;
2204 base_node->repos_relpath =
2205 svn_dirent_join(relpath, entry->name, result_pool);
2209 /* TODO: These values should always be present, if they are missing
2210 during an upgrade, set a flag, and then ask the user to talk to the
2213 Note: cmt_rev is the distinguishing value. The others may be 0 or
2214 NULL if the corresponding revprop has been deleted. */
2215 base_node->changed_rev = entry->cmt_rev;
2216 base_node->changed_date = entry->cmt_date;
2217 base_node->changed_author = entry->cmt_author;
2219 if (entry->file_external_path)
2220 base_node->file_external = TRUE;
2222 /* Switched nodes get an empty iprops cache. */
2224 && is_switched(parent_node->base, base_node, scratch_pool))
2225 base_node->inherited_props
2226 = apr_array_make(scratch_pool, 0, sizeof(svn_prop_inherited_item_t*));
2228 SVN_ERR(insert_node(sdb, base_node, scratch_pool));
2230 /* We have to insert the lock after the base node, because the node
2231 must exist to lookup various bits of repos related information for
2233 if (entry->lock_token && create_locks)
2235 svn_wc__db_lock_t lock;
2237 lock.token = entry->lock_token;
2238 lock.owner = entry->lock_owner;
2239 lock.comment = entry->lock_comment;
2240 lock.date = entry->lock_creation_date;
2242 SVN_ERR(svn_wc__db_lock_add(db, tmp_entry_abspath, &lock,
2247 if (below_working_node)
2250 = parent_node->below_work ? parent_node->below_work : parent_node->work;
2252 below_working_node->wc_id = wc_id;
2253 below_working_node->local_relpath = local_relpath;
2254 below_working_node->op_depth = work->op_depth;
2255 below_working_node->parent_relpath = parent_relpath;
2256 below_working_node->presence = svn_wc__db_status_normal;
2257 below_working_node->kind = entry->kind;
2258 below_working_node->repos_id = work->repos_id;
2259 below_working_node->revision = work->revision;
2261 /* This is just guessing. If the node below would have been switched
2262 or if it was updated to a different version, the guess would
2263 fail. But we don't have better information pre wc-ng :( */
2264 if (work->repos_relpath)
2265 below_working_node->repos_relpath
2266 = svn_relpath_join(work->repos_relpath,
2267 svn_relpath_basename(local_relpath, NULL),
2270 below_working_node->repos_relpath = NULL;
2272 /* The revert_base checksum isn't available in the entry structure,
2273 so the caller provides it. */
2275 /* text_base_info is NULL for files scheduled to be added. */
2276 below_working_node->checksum = NULL;
2279 if (entry->schedule == svn_wc_schedule_delete)
2280 below_working_node->checksum =
2281 text_base_info->normal_base.sha1_checksum;
2283 below_working_node->checksum =
2284 text_base_info->revert_base.sha1_checksum;
2286 below_working_node->recorded_size = 0;
2287 below_working_node->changed_rev = SVN_INVALID_REVNUM;
2288 below_working_node->changed_date = 0;
2289 below_working_node->changed_author = NULL;
2290 below_working_node->depth = svn_depth_infinity;
2291 below_working_node->recorded_time = 0;
2292 below_working_node->properties = NULL;
2295 && entry->schedule == svn_wc_schedule_delete
2296 && working_node->repos_relpath)
2298 /* We are lucky, our guesses above are not necessary. The known
2299 correct information is in working. But our op_depth design
2300 expects more information here */
2301 below_working_node->repos_relpath = working_node->repos_relpath;
2302 below_working_node->repos_id = working_node->repos_id;
2303 below_working_node->revision = working_node->revision;
2305 /* Nice for 'svn status' */
2306 below_working_node->changed_rev = entry->cmt_rev;
2307 below_working_node->changed_date = entry->cmt_date;
2308 below_working_node->changed_author = entry->cmt_author;
2310 /* And now remove it from WORKING, because in wc-ng code
2311 should read it from the lower layer */
2312 working_node->repos_relpath = NULL;
2313 working_node->repos_id = 0;
2314 working_node->revision = SVN_INVALID_REVNUM;
2317 SVN_ERR(insert_node(sdb, below_working_node, scratch_pool));
2320 /* Insert the working node. */
2323 working_node->wc_id = wc_id;
2324 working_node->local_relpath = local_relpath;
2325 working_node->parent_relpath = parent_relpath;
2326 working_node->changed_rev = SVN_INVALID_REVNUM;
2327 working_node->recorded_time = entry->text_time;
2328 working_node->recorded_size = entry->working_size;
2330 if (entry->depth != svn_depth_exclude)
2331 working_node->depth = entry->depth;
2334 working_node->presence = svn_wc__db_status_excluded;
2335 working_node->depth = svn_depth_infinity;
2338 if (entry->kind == svn_node_dir)
2339 working_node->checksum = NULL;
2342 working_node->checksum = NULL;
2343 /* text_base_info is NULL for files scheduled to be added. */
2345 working_node->checksum = text_base_info->normal_base.sha1_checksum;
2348 /* If an MD5 checksum is present in the entry, we can verify that
2349 * it matches the MD5 of the base file we found earlier. */
2351 if (entry->checksum && text_base_info)
2353 svn_checksum_t *md5_checksum;
2354 SVN_ERR(svn_checksum_parse_hex(&md5_checksum, svn_checksum_md5,
2355 entry->checksum, result_pool));
2357 md5_checksum && text_base_info->normal_base.md5_checksum);
2358 SVN_ERR_ASSERT(svn_checksum_match(
2359 md5_checksum, text_base_info->normal_base.md5_checksum));
2364 working_node->kind = entry->kind;
2365 if (working_node->presence != svn_wc__db_status_excluded)
2367 /* All subdirs start of incomplete, and stop being incomplete
2368 when the entries file in the subdir is upgraded. */
2369 if (entry->kind == svn_node_dir
2370 && strcmp(entry->name, SVN_WC_ENTRY_THIS_DIR))
2372 working_node->presence = svn_wc__db_status_incomplete;
2373 working_node->kind = svn_node_dir;
2375 else if (entry->schedule == svn_wc_schedule_delete)
2377 working_node->presence = svn_wc__db_status_base_deleted;
2378 working_node->kind = entry->kind;
2382 /* presence == normal */
2383 working_node->kind = entry->kind;
2385 if (entry->incomplete)
2387 /* We shouldn't be overwriting another status. */
2388 WRITE_ENTRY_ASSERT(working_node->presence
2389 == svn_wc__db_status_normal);
2390 working_node->presence = svn_wc__db_status_incomplete;
2395 /* These should generally be unset for added and deleted files,
2396 and contain whatever information we have for copied files. Let's
2397 just store whatever we have.
2399 Note: cmt_rev is the distinguishing value. The others may be 0 or
2400 NULL if the corresponding revprop has been deleted. */
2401 if (working_node->presence != svn_wc__db_status_base_deleted)
2403 working_node->changed_rev = entry->cmt_rev;
2404 working_node->changed_date = entry->cmt_date;
2405 working_node->changed_author = entry->cmt_author;
2408 if (entry->schedule == svn_wc_schedule_delete
2409 && parent_node->work
2410 && parent_node->work->presence == svn_wc__db_status_base_deleted)
2412 working_node->op_depth = parent_node->work->op_depth;
2414 else if (working_node->presence == svn_wc__db_status_excluded
2415 && parent_node->work)
2417 working_node->op_depth = parent_node->work->op_depth;
2419 else if (!entry->copied)
2421 working_node->op_depth
2422 = svn_wc__db_op_depth_for_upgrade(local_relpath);
2425 SVN_ERR(insert_node(sdb, working_node, scratch_pool));
2428 /* Insert the actual node. */
2431 actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
2433 actual_node->wc_id = wc_id;
2434 actual_node->local_relpath = local_relpath;
2435 actual_node->parent_relpath = parent_relpath;
2437 SVN_ERR(insert_actual_node(sdb, db, tmp_entry_abspath,
2438 actual_node, scratch_pool));
2443 *entry_node = apr_palloc(result_pool, sizeof(**entry_node));
2444 (*entry_node)->base = base_node;
2445 (*entry_node)->work = working_node;
2446 (*entry_node)->below_work = below_working_node;
2447 (*entry_node)->tree_conflicts = tree_conflicts;
2450 if (entry->file_external_path)
2452 /* TODO: Maybe add a file external registration inside EXTERNALS here,
2453 to allow removing file externals that aren't referenced from
2456 The svn:externals values are processed anyway after everything is
2460 return SVN_NO_ERROR;
2463 static svn_error_t *
2464 write_actual_only_entries(apr_hash_t *tree_conflicts,
2465 svn_sqlite__db_t *sdb,
2467 const char *wri_abspath,
2469 const char *parent_relpath,
2470 apr_pool_t *scratch_pool)
2472 apr_hash_index_t *hi;
2474 for (hi = apr_hash_first(scratch_pool, tree_conflicts);
2476 hi = apr_hash_next(hi))
2478 db_actual_node_t *actual_node = NULL;
2480 actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
2481 actual_node->wc_id = wc_id;
2482 actual_node->local_relpath = apr_hash_this_key(hi);
2483 actual_node->parent_relpath = parent_relpath;
2484 actual_node->tree_conflict_data = apr_hash_this_val(hi);
2486 SVN_ERR(insert_actual_node(sdb, db, wri_abspath, actual_node,
2490 return SVN_NO_ERROR;
2494 svn_wc__write_upgraded_entries(void **dir_baton,
2497 svn_sqlite__db_t *sdb,
2498 apr_int64_t repos_id,
2500 const char *dir_abspath,
2501 const char *new_root_abspath,
2502 apr_hash_t *entries,
2503 apr_hash_t *text_bases_info,
2504 apr_pool_t *result_pool,
2505 apr_pool_t *scratch_pool)
2507 const svn_wc_entry_t *this_dir;
2508 apr_hash_index_t *hi;
2509 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2510 const char *old_root_abspath, *dir_relpath;
2511 struct write_baton *parent_node = parent_baton;
2512 struct write_baton *dir_node;
2514 /* Get a copy of the "this dir" entry for comparison purposes. */
2515 this_dir = svn_hash_gets(entries, SVN_WC_ENTRY_THIS_DIR);
2517 /* If there is no "this dir" entry, something is wrong. */
2519 return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL,
2520 _("No default entry in directory '%s'"),
2521 svn_dirent_local_style(dir_abspath,
2523 old_root_abspath = svn_dirent_get_longest_ancestor(dir_abspath,
2527 SVN_ERR_ASSERT(old_root_abspath[0]);
2529 dir_relpath = svn_dirent_skip_ancestor(old_root_abspath, dir_abspath);
2531 /* Write out "this dir" */
2532 SVN_ERR(write_entry(&dir_node, parent_node, db, sdb,
2533 wc_id, repos_id, this_dir, NULL, dir_relpath,
2534 svn_dirent_join(new_root_abspath, dir_relpath,
2537 this_dir, FALSE, result_pool, iterpool));
2539 for (hi = apr_hash_first(scratch_pool, entries); hi;
2540 hi = apr_hash_next(hi))
2542 const char *name = apr_hash_this_key(hi);
2543 const svn_wc_entry_t *this_entry = apr_hash_this_val(hi);
2544 const char *child_abspath, *child_relpath;
2545 svn_wc__text_base_info_t *text_base_info
2546 = svn_hash_gets(text_bases_info, name);
2548 svn_pool_clear(iterpool);
2550 /* Don't rewrite the "this dir" entry! */
2551 if (strcmp(name, SVN_WC_ENTRY_THIS_DIR) == 0)
2554 /* Write the entry. Pass TRUE for create locks, because we still
2555 use this function for upgrading old working copies. */
2556 child_abspath = svn_dirent_join(dir_abspath, name, iterpool);
2557 child_relpath = svn_dirent_skip_ancestor(old_root_abspath, child_abspath);
2558 SVN_ERR(write_entry(NULL, dir_node, db, sdb,
2560 this_entry, text_base_info, child_relpath,
2561 svn_dirent_join(new_root_abspath, child_relpath,
2564 this_dir, TRUE, iterpool, iterpool));
2567 if (dir_node->tree_conflicts)
2568 SVN_ERR(write_actual_only_entries(dir_node->tree_conflicts, sdb, db,
2569 new_root_abspath, wc_id, dir_relpath,
2572 *dir_baton = dir_node;
2573 svn_pool_destroy(iterpool);
2574 return SVN_NO_ERROR;
2579 svn_wc_entry_dup(const svn_wc_entry_t *entry, apr_pool_t *pool)
2581 svn_wc_entry_t *dupentry = apr_palloc(pool, sizeof(*dupentry));
2583 /* Perform a trivial copy ... */
2586 /* ...and then re-copy stuff that needs to be duped into our pool. */
2588 dupentry->name = apr_pstrdup(pool, entry->name);
2590 dupentry->url = apr_pstrdup(pool, entry->url);
2592 dupentry->repos = apr_pstrdup(pool, entry->repos);
2594 dupentry->uuid = apr_pstrdup(pool, entry->uuid);
2595 if (entry->copyfrom_url)
2596 dupentry->copyfrom_url = apr_pstrdup(pool, entry->copyfrom_url);
2597 if (entry->conflict_old)
2598 dupentry->conflict_old = apr_pstrdup(pool, entry->conflict_old);
2599 if (entry->conflict_new)
2600 dupentry->conflict_new = apr_pstrdup(pool, entry->conflict_new);
2601 if (entry->conflict_wrk)
2602 dupentry->conflict_wrk = apr_pstrdup(pool, entry->conflict_wrk);
2603 if (entry->prejfile)
2604 dupentry->prejfile = apr_pstrdup(pool, entry->prejfile);
2605 if (entry->checksum)
2606 dupentry->checksum = apr_pstrdup(pool, entry->checksum);
2607 if (entry->cmt_author)
2608 dupentry->cmt_author = apr_pstrdup(pool, entry->cmt_author);
2609 if (entry->lock_token)
2610 dupentry->lock_token = apr_pstrdup(pool, entry->lock_token);
2611 if (entry->lock_owner)
2612 dupentry->lock_owner = apr_pstrdup(pool, entry->lock_owner);
2613 if (entry->lock_comment)
2614 dupentry->lock_comment = apr_pstrdup(pool, entry->lock_comment);
2615 if (entry->changelist)
2616 dupentry->changelist = apr_pstrdup(pool, entry->changelist);
2618 /* NOTE: we do not dup cachable_props or present_props since they
2619 are deprecated. Use "" to indicate "nothing cachable or cached". */
2620 dupentry->cachable_props = "";
2621 dupentry->present_props = "";
2623 if (entry->tree_conflict_data)
2624 dupentry->tree_conflict_data = apr_pstrdup(pool,
2625 entry->tree_conflict_data);
2626 if (entry->file_external_path)
2627 dupentry->file_external_path = apr_pstrdup(pool,
2628 entry->file_external_path);
2633 /*** Generic Entry Walker */
2635 /* A recursive entry-walker, helper for svn_wc_walk_entries3().
2637 * For this directory (DIRPATH, ADM_ACCESS), call the "found_entry" callback
2638 * in WALK_CALLBACKS, passing WALK_BATON to it. Then, for each versioned
2639 * entry in this directory, call the "found entry" callback and then recurse
2640 * (if it is a directory and if DEPTH allows).
2642 * If SHOW_HIDDEN is true, include entries that are in a 'deleted' or
2643 * 'absent' state (and not scheduled for re-addition), else skip them.
2645 * Call CANCEL_FUNC with CANCEL_BATON to allow cancellation.
2647 static svn_error_t *
2648 walker_helper(const char *dirpath,
2649 svn_wc_adm_access_t *adm_access,
2650 const svn_wc_entry_callbacks2_t *walk_callbacks,
2653 svn_boolean_t show_hidden,
2654 svn_cancel_func_t cancel_func,
2658 apr_pool_t *subpool = svn_pool_create(pool);
2659 apr_hash_t *entries;
2660 apr_hash_index_t *hi;
2661 svn_wc_entry_t *dot_entry;
2663 svn_wc__db_t *db = svn_wc__adm_get_db(adm_access);
2665 err = svn_wc__entries_read_internal(&entries, adm_access, show_hidden,
2669 SVN_ERR(walk_callbacks->handle_error(dirpath, err, walk_baton, pool));
2671 /* As promised, always return the '.' entry first. */
2672 dot_entry = svn_hash_gets(entries, SVN_WC_ENTRY_THIS_DIR);
2674 return walk_callbacks->handle_error
2675 (dirpath, svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL,
2676 _("Directory '%s' has no THIS_DIR entry"),
2677 svn_dirent_local_style(dirpath, pool)),
2680 /* Call the "found entry" callback for this directory as a "this dir"
2681 * entry. Note that if this directory has been reached by recursion, this
2682 * is the second visit as it will already have been visited once as a
2683 * child entry of its parent. */
2685 err = walk_callbacks->found_entry(dirpath, dot_entry, walk_baton, subpool);
2689 SVN_ERR(walk_callbacks->handle_error(dirpath, err, walk_baton, pool));
2691 if (depth == svn_depth_empty)
2692 return SVN_NO_ERROR;
2694 /* Loop over each of the other entries. */
2695 for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
2697 const char *name = apr_hash_this_key(hi);
2698 const svn_wc_entry_t *current_entry = apr_hash_this_val(hi);
2699 const char *entrypath;
2700 const char *entry_abspath;
2701 svn_boolean_t hidden;
2703 svn_pool_clear(subpool);
2705 /* See if someone wants to cancel this operation. */
2707 SVN_ERR(cancel_func(cancel_baton));
2709 /* Skip the "this dir" entry. */
2710 if (strcmp(current_entry->name, SVN_WC_ENTRY_THIS_DIR) == 0)
2713 entrypath = svn_dirent_join(dirpath, name, subpool);
2714 SVN_ERR(svn_wc__entry_is_hidden(&hidden, current_entry));
2715 SVN_ERR(svn_dirent_get_absolute(&entry_abspath, entrypath, subpool));
2717 /* Call the "found entry" callback for this entry. (For a directory,
2718 * this is the first visit: as a child.) */
2719 if (current_entry->kind == svn_node_file
2720 || depth >= svn_depth_immediates)
2722 err = walk_callbacks->found_entry(entrypath, current_entry,
2723 walk_baton, subpool);
2726 SVN_ERR(walk_callbacks->handle_error(entrypath, err,
2730 /* Recurse into this entry if appropriate. */
2731 if (current_entry->kind == svn_node_dir
2733 && depth >= svn_depth_immediates)
2735 svn_wc_adm_access_t *entry_access;
2736 svn_depth_t depth_below_here = depth;
2738 if (depth == svn_depth_immediates)
2739 depth_below_here = svn_depth_empty;
2741 entry_access = svn_wc__adm_retrieve_internal2(db, entry_abspath,
2745 SVN_ERR(walker_helper(entrypath, entry_access,
2746 walk_callbacks, walk_baton,
2747 depth_below_here, show_hidden,
2748 cancel_func, cancel_baton,
2753 svn_pool_destroy(subpool);
2754 return SVN_NO_ERROR;
2758 svn_wc__walker_default_error_handler(const char *path,
2763 /* Note: don't trace this. We don't want to insert a false "stack frame"
2764 onto an error generated elsewhere. */
2765 return svn_error_trace(err);
2769 /* The public API. */
2771 svn_wc_walk_entries3(const char *path,
2772 svn_wc_adm_access_t *adm_access,
2773 const svn_wc_entry_callbacks2_t *walk_callbacks,
2775 svn_depth_t walk_depth,
2776 svn_boolean_t show_hidden,
2777 svn_cancel_func_t cancel_func,
2781 const char *local_abspath;
2782 svn_wc__db_t *db = svn_wc__adm_get_db(adm_access);
2784 svn_node_kind_t kind;
2785 svn_wc__db_status_t status;
2787 SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
2788 err = svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL,
2789 NULL, NULL, NULL, NULL, NULL, NULL,
2790 NULL, NULL, NULL, NULL, NULL, NULL,
2791 NULL, NULL, NULL, NULL, NULL, NULL,
2797 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
2798 return svn_error_trace(err);
2799 /* Remap into SVN_ERR_UNVERSIONED_RESOURCE. */
2800 svn_error_clear(err);
2801 return walk_callbacks->handle_error(
2802 path, svn_error_createf(SVN_ERR_UNVERSIONED_RESOURCE, NULL,
2803 _("'%s' is not under version control"),
2804 svn_dirent_local_style(local_abspath, pool)),
2808 if (kind == svn_node_file
2809 || status == svn_wc__db_status_excluded
2810 || status == svn_wc__db_status_server_excluded)
2812 const svn_wc_entry_t *entry;
2814 /* ### we should stop passing out entry structures.
2816 ### we should not call handle_error for an error the *callback*
2817 ### gave us. let it deal with the problem before returning. */
2820 && (status == svn_wc__db_status_not_present
2821 || status == svn_wc__db_status_excluded
2822 || status == svn_wc__db_status_server_excluded))
2824 /* The fool asked to walk a "hidden" node. Report the node as
2827 ### this is incorrect behavior. see depth_test 36. the walk
2828 ### API will be revamped to avoid entry structures. we should
2829 ### be able to solve the problem with the new API. (since we
2830 ### shouldn't return a hidden entry here) */
2831 return walk_callbacks->handle_error(
2832 path, svn_error_createf(
2833 SVN_ERR_UNVERSIONED_RESOURCE, NULL,
2834 _("'%s' is not under version control"),
2835 svn_dirent_local_style(local_abspath, pool)),
2839 SVN_ERR(svn_wc__get_entry(&entry, db, local_abspath, FALSE,
2840 svn_node_file, pool, pool));
2842 err = walk_callbacks->found_entry(path, entry, walk_baton, pool);
2844 return walk_callbacks->handle_error(path, err, walk_baton, pool);
2846 return SVN_NO_ERROR;
2849 if (kind == svn_node_dir)
2850 return walker_helper(path, adm_access, walk_callbacks, walk_baton,
2851 walk_depth, show_hidden, cancel_func, cancel_baton,
2854 return walk_callbacks->handle_error(
2855 path, svn_error_createf(SVN_ERR_NODE_UNKNOWN_KIND, NULL,
2856 _("'%s' has an unrecognized node kind"),
2857 svn_dirent_local_style(local_abspath, pool)),