1 /*
2 * entries.c : manipulating the administrative `entries' file.
3 *
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
20 * under the License.
21 * ====================================================================
22 */
23
24 #include <string.h>
25 #include <assert.h>
26
27 #include <apr_strings.h>
28
29 #include "svn_error.h"
30 #include "svn_types.h"
31 #include "svn_time.h"
32 #include "svn_pools.h"
33 #include "svn_dirent_uri.h"
34 #include "svn_path.h"
35 #include "svn_ctype.h"
36 #include "svn_string.h"
37 #include "svn_hash.h"
38
39 #include "wc.h"
40 #include "adm_files.h"
41 #include "conflicts.h"
42 #include "entries.h"
43 #include "lock.h"
44 #include "tree_conflicts.h"
45 #include "wc_db.h"
46 #include "wc-queries.h" /* for STMT_* */
47
48 #define SVN_WC__I_AM_WC_DB
49
50 #include "svn_private_config.h"
51 #include "private/svn_wc_private.h"
52 #include "private/svn_sqlite.h"
53 #include "token-map.h"
54
55 #include "wc_db_private.h"
56
57 #define MAYBE_ALLOC(x,p) ((x) ? (x) : apr_pcalloc((p), sizeof(*(x))))
58
59
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 {
63 apr_int64_t wc_id;
64 const char *local_relpath;
65 int op_depth;
66 apr_int64_t repos_id;
67 const char *repos_relpath;
68 const char *parent_relpath;
69 svn_wc__db_status_t presence;
70 svn_revnum_t revision;
71 svn_node_kind_t kind;
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;
77 svn_depth_t depth;
78 apr_time_t recorded_time;
79 apr_hash_t *properties;
80 svn_boolean_t file_external;
81 apr_array_header_t *inherited_props;
82 } db_node_t;
83
84 typedef struct db_actual_node_t {
85 apr_int64_t wc_id;
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;
96 } db_actual_node_t;
97
98
99
100 /*** reading and writing the entries file ***/
101
102
103 /* */
104 static svn_wc_entry_t *
alloc_entry(apr_pool_t * pool)105 alloc_entry(apr_pool_t *pool)
106 {
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;
116 return entry;
117 }
118
119
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.? */
122 svn_error_t *
svn_wc__entry_is_hidden(svn_boolean_t * hidden,const svn_wc_entry_t * entry)123 svn_wc__entry_is_hidden(svn_boolean_t *hidden, const svn_wc_entry_t *entry)
124 {
125 /* In English, the condition is: "the entry is not present, and I haven't
126 scheduled something over the top of it." */
127 if (entry->deleted
128 || entry->absent
129 || entry->depth == svn_depth_exclude)
130 {
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);
135
136 /* Hidden if something hasn't been added over it.
137
138 ### is this even possible with absent or excluded nodes? */
139 *hidden = entry->schedule != svn_wc_schedule_add;
140 }
141 else
142 *hidden = FALSE;
143
144 return SVN_NO_ERROR;
145 }
146
147
148 /* Hit the database to check the file external information for the given
149 entry. The entry will be modified in place. */
150 static svn_error_t *
check_file_external(svn_wc_entry_t * entry,svn_wc__db_t * db,const char * local_abspath,const char * wri_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)151 check_file_external(svn_wc_entry_t *entry,
152 svn_wc__db_t *db,
153 const char *local_abspath,
154 const char *wri_abspath,
155 apr_pool_t *result_pool,
156 apr_pool_t *scratch_pool)
157 {
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;
163 svn_error_t *err;
164
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);
169
170 if (err)
171 {
172 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
173 return svn_error_trace(err);
174
175 svn_error_clear(err);
176 return SVN_NO_ERROR;
177 }
178
179 if (status == svn_wc__db_status_normal
180 && kind == svn_node_file)
181 {
182 entry->file_external_path = repos_relpath;
183 if (SVN_IS_VALID_REVNUM(peg_revision))
184 {
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;
188 }
189 if (SVN_IS_VALID_REVNUM(revision))
190 {
191 entry->file_external_rev.kind = svn_opt_revision_number;
192 entry->file_external_rev.value.number = revision;
193 }
194 }
195
196 return SVN_NO_ERROR;
197 }
198
199
200 /* Fill in the following fields of ENTRY:
201
202 REVISION
203 REPOS
204 UUID
205 CMT_REV
206 CMT_DATE
207 CMT_AUTHOR
208 DEPTH
209 DELETED
210
211 Return: KIND, REPOS_RELPATH, CHECKSUM
212 */
213 static svn_error_t *
get_info_for_deleted(svn_wc_entry_t * entry,svn_node_kind_t * kind,const char ** repos_relpath,const svn_checksum_t ** checksum,svn_wc__db_lock_t ** lock,svn_wc__db_t * db,const char * entry_abspath,svn_wc__db_wcroot_t * wcroot,const char * entry_relpath,const svn_wc_entry_t * parent_entry,svn_boolean_t have_base,svn_boolean_t have_more_work,apr_pool_t * result_pool,apr_pool_t * scratch_pool)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,
219 svn_wc__db_t *db,
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)
228 {
229 if (have_base && !have_more_work)
230 {
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(
234 NULL, kind,
235 &entry->revision,
236 repos_relpath,
237 &repos_id,
238 &entry->cmt_rev,
239 &entry->cmt_date,
240 &entry->cmt_author,
241 &entry->depth,
242 checksum,
243 NULL,
244 lock,
245 &entry->has_props, NULL,
246 NULL,
247 wcroot, entry_relpath,
248 result_pool,
249 scratch_pool));
250 SVN_ERR(svn_wc__db_fetch_repos_info(&entry->repos, &entry->uuid,
251 wcroot, repos_id, result_pool));
252 }
253 else
254 {
255 const char *work_del_relpath;
256 const char *parent_repos_relpath;
257 const char *parent_relpath;
258 apr_int64_t repos_id;
259
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,
265 &entry->cmt_rev,
266 &entry->cmt_date,
267 &entry->cmt_author,
268 &entry->depth,
269 checksum,
270 NULL,
271 &entry->has_props, NULL,
272 db,
273 entry_abspath,
274 result_pool,
275 scratch_pool));
276 /* working_size and text_time unavailable */
277
278 SVN_ERR(svn_wc__db_scan_deletion_internal(
279 NULL,
280 NULL,
281 &work_del_relpath, NULL,
282 wcroot, entry_relpath,
283 scratch_pool, scratch_pool));
284
285 SVN_ERR_ASSERT(work_del_relpath != NULL);
286 parent_relpath = svn_relpath_dirname(work_del_relpath, scratch_pool);
287
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(
291 NULL, NULL,
292 &parent_repos_relpath,
293 &repos_id,
294 NULL, NULL, NULL,
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));
299
300 /* Now glue it all together */
301 *repos_relpath = svn_relpath_join(parent_repos_relpath,
302 svn_relpath_skip_ancestor(
303 parent_relpath,
304 entry_relpath),
305 result_pool);
306
307
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 */
310 if (have_base)
311 {
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,
317 NULL,
318 wcroot, entry_relpath,
319 result_pool, scratch_pool));
320
321 if (status == svn_wc__db_status_not_present)
322 entry->deleted = TRUE;
323 }
324 }
325
326 /* Do some extra work for the child nodes. */
327 if (!SVN_IS_VALID_REVNUM(entry->revision) && parent_entry != NULL)
328 {
329 /* For child nodes without a revision, pick up the parent's
330 revision. */
331 entry->revision = parent_entry->revision;
332 }
333
334 return SVN_NO_ERROR;
335 }
336
337
338 /*
339 * Encode tree conflict descriptions into a single string.
340 *
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.
346 *
347 * Do all allocations in POOL.
348 *
349 * @see svn_wc__read_tree_conflicts()
350 */
351 static svn_error_t *
write_tree_conflicts(const char ** conflict_data,apr_hash_t * conflicts,apr_pool_t * pool)352 write_tree_conflicts(const char **conflict_data,
353 apr_hash_t *conflicts,
354 apr_pool_t *pool)
355 {
356 svn_skel_t *skel = svn_skel__make_empty_list(pool);
357 apr_hash_index_t *hi;
358
359 for (hi = apr_hash_first(pool, conflicts); hi; hi = apr_hash_next(hi))
360 {
361 svn_skel_t *c_skel;
362
363 SVN_ERR(svn_wc__serialize_conflict(&c_skel, apr_hash_this_val(hi),
364 pool, pool));
365 svn_skel__prepend(c_skel, skel);
366 }
367
368 *conflict_data = svn_skel__unparse(skel, pool)->data;
369
370 return SVN_NO_ERROR;
371 }
372
373
374 /* Read one entry from wc_db. It will be allocated in RESULT_POOL and
375 returned in *NEW_ENTRY.
376
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").
379
380 DB specifies the wc_db database, and WC_ID specifies which working copy
381 this information is being read from.
382
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.
385
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
389
390 Temporary allocations are made in SCRATCH_POOL. */
391 static svn_error_t *
read_one_entry(const svn_wc_entry_t ** new_entry,svn_wc__db_t * db,const char * dir_abspath,svn_wc__db_wcroot_t * wcroot,const char * dir_relpath,const char * name,const svn_wc_entry_t * parent_entry,apr_pool_t * result_pool,apr_pool_t * scratch_pool)392 read_one_entry(const svn_wc_entry_t **new_entry,
393 svn_wc__db_t *db,
394 const char *dir_abspath,
395 svn_wc__db_wcroot_t *wcroot,
396 const char *dir_relpath,
397 const char *name,
398 const svn_wc_entry_t *parent_entry,
399 apr_pool_t *result_pool,
400 apr_pool_t *scratch_pool)
401 {
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;
419
420 entry->name = apr_pstrdup(result_pool, name);
421
422 entry_relpath = svn_relpath_join(dir_relpath, entry->name, scratch_pool);
423 entry_abspath = svn_dirent_join(dir_abspath, entry->name, scratch_pool);
424
425 SVN_ERR(svn_wc__db_read_info_internal(
426 &status,
427 &kind,
428 &entry->revision,
429 &repos_relpath,
430 &repos_id,
431 &entry->cmt_rev,
432 &entry->cmt_date,
433 &entry->cmt_author,
434 &entry->depth,
435 &checksum,
436 NULL,
437 &original_repos_relpath,
438 &original_repos_id,
439 &entry->copyfrom_rev,
440 &lock,
441 &translated_size,
442 &entry->text_time,
443 &entry->changelist,
444 &conflicted,
445 &op_root,
446 &entry->has_props /* have_props */,
447 &entry->has_prop_mods /* props_mod */,
448 &have_base,
449 &have_more_work,
450 NULL /* have_work */,
451 wcroot, entry_relpath,
452 result_pool, scratch_pool));
453
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,
458 result_pool));
459
460 if (entry->has_prop_mods)
461 entry->has_props = TRUE;
462
463 if (strcmp(entry->name, SVN_WC_ENTRY_THIS_DIR) == 0)
464 {
465 /* get the tree conflict data. */
466 apr_hash_t *tree_conflicts = NULL;
467 const apr_array_header_t *conflict_victims;
468 int k;
469
470 SVN_ERR(svn_wc__db_read_conflict_victims(&conflict_victims, db,
471 dir_abspath,
472 scratch_pool,
473 scratch_pool));
474
475 for (k = 0; k < conflict_victims->nelts; k++)
476 {
477 int j;
478 const apr_array_header_t *child_conflicts;
479 const char *child_name;
480 const char *child_abspath;
481
482 child_name = APR_ARRAY_IDX(conflict_victims, k, const char *);
483 child_abspath = svn_dirent_join(dir_abspath, child_name,
484 scratch_pool);
485
486 SVN_ERR(svn_wc__read_conflicts(&child_conflicts, NULL,
487 db, child_abspath,
488 FALSE /* create tempfiles */,
489 TRUE /* tree_conflicts_only */,
490 scratch_pool, scratch_pool));
491
492 for (j = 0; j < child_conflicts->nelts; j++)
493 {
494 const svn_wc_conflict_description2_t *conflict =
495 APR_ARRAY_IDX(child_conflicts, j,
496 svn_wc_conflict_description2_t *);
497
498 if (conflict->kind == svn_wc_conflict_kind_tree)
499 {
500 if (!tree_conflicts)
501 tree_conflicts = apr_hash_make(scratch_pool);
502 svn_hash_sets(tree_conflicts, child_name, conflict);
503 }
504 }
505 }
506
507 if (tree_conflicts)
508 {
509 SVN_ERR(write_tree_conflicts(&entry->tree_conflict_data,
510 tree_conflicts, result_pool));
511 }
512 }
513
514 if (status == svn_wc__db_status_normal
515 || status == svn_wc__db_status_incomplete)
516 {
517 /* Plain old BASE node. */
518 entry->schedule = svn_wc_schedule_normal;
519
520 /* Grab inherited repository information, if necessary. */
521 if (repos_relpath == NULL)
522 {
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,
527 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));
532 }
533
534 entry->incomplete = (status == svn_wc__db_status_incomplete);
535 }
536 else if (status == svn_wc__db_status_deleted)
537 {
538 svn_node_kind_t path_kind;
539
540 /* ### we don't have to worry about moves, so this is a delete. */
541 entry->schedule = svn_wc_schedule_delete;
542
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;
549 else
550 {
551 const char *work_del_relpath;
552 SVN_ERR(svn_wc__db_scan_deletion_internal(
553 NULL, NULL,
554 &work_del_relpath, NULL,
555 wcroot, entry_relpath,
556 scratch_pool, scratch_pool));
557
558 if (work_del_relpath)
559 entry->copied = TRUE;
560 }
561
562 /* If there is still a directory on-disk we keep it, if not it is
563 already deleted. Simple, isn't it?
564
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.
569 */
570 SVN_ERR(svn_io_check_path(entry_abspath, &path_kind, scratch_pool));
571 entry->keep_local = (path_kind == svn_node_dir);
572 }
573 else if (status == svn_wc__db_status_added)
574 {
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;
579
580 /* For child nodes, pick up the parent's revision. */
581 if (*entry->name != '\0')
582 {
583 assert(parent_entry != NULL);
584 assert(entry->revision == SVN_INVALID_REVNUM);
585
586 entry->revision = parent_entry->revision;
587 }
588
589 if (have_base)
590 {
591 svn_wc__db_status_t base_status;
592
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(
597 &base_status, NULL,
598 &entry->revision,
599 NULL, NULL, NULL,
600 NULL, NULL, NULL,
601 NULL, NULL, NULL,
602 NULL, NULL, NULL,
603 wcroot, entry_relpath,
604 scratch_pool,
605 scratch_pool));
606
607 if (base_status == svn_wc__db_status_not_present)
608 {
609 /* The underlying node is DELETED in this revision. */
610 entry->deleted = TRUE;
611
612 /* This is an add since there isn't a node to replace. */
613 entry->schedule = svn_wc_schedule_add;
614 }
615 else
616 entry->schedule = svn_wc_schedule_replace;
617 }
618 else
619 {
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. */
623
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))
627 entry->revision = 0;
628
629 entry->schedule = svn_wc_schedule_add;
630 }
631
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. */
636 {
637 const char *op_root_relpath;
638 SVN_ERR(svn_wc__db_scan_addition_internal(
639 &work_status,
640 &op_root_relpath,
641 &repos_relpath,
642 &repos_id,
643 &scanned_original_relpath,
644 NULL /* original_repos_id */,
645 &original_revision,
646 wcroot, entry_relpath,
647 result_pool, scratch_pool));
648
649 SVN_ERR(svn_wc__db_fetch_repos_info(&entry->repos, &entry->uuid,
650 wcroot, repos_id, result_pool));
651
652 if (!op_root_relpath)
653 op_root_abspath = NULL;
654 else
655 op_root_abspath = svn_dirent_join(wcroot->abspath, op_root_relpath,
656 scratch_pool);
657
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
662 && entry->deleted)
663 entry->revision = 0;
664 }
665
666 if (!SVN_IS_VALID_REVNUM(entry->cmt_rev)
667 && scanned_original_relpath == NULL)
668 {
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. */
675 }
676 /* For backwards-compatibility purposes we treat moves just like
677 * regular copies. */
678 else if (work_status == svn_wc__db_status_copied ||
679 work_status == svn_wc__db_status_moved_here)
680 {
681 entry->copied = TRUE;
682
683 /* If this is a child of a copied subtree, then it should be
684 schedule_normal. */
685 if (original_repos_relpath == NULL)
686 {
687 /* ### what if there is a BASE node under there? */
688 entry->schedule = svn_wc_schedule_normal;
689 }
690
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;
696 }
697
698 /* Does this node have copyfrom_* information? */
699 if (scanned_original_relpath != NULL)
700 {
701 svn_boolean_t is_copied_child;
702 svn_boolean_t is_mixed_rev = FALSE;
703
704 SVN_ERR_ASSERT(work_status == svn_wc__db_status_copied ||
705 work_status == svn_wc__db_status_moved_here);
706
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);
710
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)
717 {
718 const char *parent_relpath;
719 svn_error_t *err;
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;
724
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.
734
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. */
744
745 /* Get the copyfrom information from our parent.
746
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,
751 scratch_pool);
752 err = svn_wc__db_scan_addition_internal(
753 NULL,
754 &op_root_relpath,
755 NULL, NULL,
756 &parent_repos_relpath,
757 &parent_repos_id,
758 NULL,
759 wcroot, parent_relpath,
760 scratch_pool,
761 scratch_pool);
762 if (err)
763 {
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;
770 }
771 else
772 {
773 SVN_ERR(svn_wc__db_fetch_repos_info(&parent_root_url, NULL,
774 wcroot, parent_repos_id,
775 scratch_pool));
776 op_root_abspath = svn_dirent_join(wcroot->abspath,
777 op_root_relpath,
778 scratch_pool);
779 }
780
781 if (parent_root_url != NULL
782 && strcmp(original_root_url, parent_root_url) == 0)
783 {
784
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);
789
790 /* The copyfrom repos roots matched.
791
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.
797
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)
802 {
803 is_copied_child = TRUE;
804 is_mixed_rev = TRUE;
805 }
806 }
807 }
808
809 if (is_copied_child)
810 {
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;
815
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;
820
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. */
825 if (is_mixed_rev)
826 entry->revision = original_revision;
827 }
828 else if (original_repos_relpath != NULL)
829 {
830 entry->copyfrom_url =
831 svn_path_url_add_component2(original_root_url,
832 original_repos_relpath,
833 result_pool);
834 }
835 else
836 {
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);
846
847 entry->copyfrom_url =
848 svn_path_url_add_component2(original_root_url,
849 entry_repos_relpath,
850 result_pool);
851 }
852 }
853 }
854 else if (status == svn_wc__db_status_not_present)
855 {
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;
861 }
862 else if (status == svn_wc__db_status_server_excluded)
863 {
864 entry->absent = TRUE;
865 }
866 else if (status == svn_wc__db_status_excluded)
867 {
868 entry->schedule = svn_wc_schedule_normal;
869 entry->depth = svn_depth_exclude;
870 }
871 else
872 {
873 /* ### we should have handled all possible status values. */
874 SVN_ERR_MALFUNCTION();
875 }
876
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)
880 {
881 SVN_ERR(get_info_for_deleted(entry,
882 &kind,
883 &repos_relpath,
884 &checksum,
885 &lock,
886 db, entry_abspath,
887 wcroot, entry_relpath,
888 parent_entry,
889 have_base, have_more_work,
890 result_pool, scratch_pool));
891 }
892
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;
896
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 */
903 else
904 entry->kind = svn_node_unknown;
905
906 /* We should always have a REPOS_RELPATH, except for:
907 - deleted nodes
908 - certain obstructed nodes
909 - not-present nodes
910 - absent nodes
911 - excluded nodes
912
913 ### the last three should probably have an "implied" REPOS_RELPATH
914 */
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);
920 if (repos_relpath)
921 entry->url = svn_path_url_add_component2(entry->repos,
922 repos_relpath,
923 result_pool);
924
925 if (checksum)
926 {
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));
932
933 SVN_ERR_ASSERT(checksum->kind == svn_checksum_md5);
934 entry->checksum = svn_checksum_to_cstring(checksum, result_pool);
935 }
936
937 if (conflicted)
938 {
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));
945
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));
950
951 if (text_conflicted)
952 {
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,
957 &their_old_abspath,
958 &their_abspath,
959 db, dir_abspath,
960 conflict, scratch_pool,
961 scratch_pool));
962
963 if (my_abspath)
964 entry->conflict_wrk = svn_dirent_basename(my_abspath, result_pool);
965
966 if (their_old_abspath)
967 entry->conflict_old = svn_dirent_basename(their_old_abspath,
968 result_pool);
969
970 if (their_abspath)
971 entry->conflict_new = svn_dirent_basename(their_abspath,
972 result_pool);
973 }
974
975 if (prop_conflicted)
976 {
977 const char *prej_abspath;
978
979 SVN_ERR(svn_wc__conflict_read_prop_conflict(&prej_abspath, NULL,
980 NULL, NULL, NULL,
981 db, dir_abspath,
982 conflict, scratch_pool,
983 scratch_pool));
984
985 if (prej_abspath)
986 entry->prejfile = svn_dirent_basename(prej_abspath, result_pool);
987 }
988 }
989
990 if (lock)
991 {
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;
996 }
997
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));
1003
1004 entry->working_size = translated_size;
1005
1006 *new_entry = entry;
1007
1008 return SVN_NO_ERROR;
1009 }
1010
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 *
read_entries_new(apr_hash_t ** result_entries,svn_wc__db_t * db,const char * dir_abspath,svn_wc__db_wcroot_t * wcroot,const char * dir_relpath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1015 read_entries_new(apr_hash_t **result_entries,
1016 svn_wc__db_t *db,
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)
1022 {
1023 apr_hash_t *entries;
1024 const apr_array_header_t *children;
1025 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1026 int i;
1027 const svn_wc_entry_t *parent_entry;
1028
1029 entries = apr_hash_make(result_pool);
1030
1031 SVN_ERR(read_one_entry(&parent_entry,
1032 db, dir_abspath,
1033 wcroot, dir_relpath,
1034 "" /* name */,
1035 NULL /* parent_entry */,
1036 result_pool, iterpool));
1037 svn_hash_sets(entries, "", parent_entry);
1038
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,
1042 dir_abspath,
1043 scratch_pool, iterpool));
1044 for (i = children->nelts; i--; )
1045 {
1046 const char *name = APR_ARRAY_IDX(children, i, const char *);
1047 const svn_wc_entry_t *entry;
1048
1049 svn_pool_clear(iterpool);
1050
1051 SVN_ERR(read_one_entry(&entry,
1052 db, dir_abspath,
1053 wcroot, dir_relpath,
1054 name, parent_entry,
1055 result_pool, iterpool));
1056 svn_hash_sets(entries, entry->name, entry);
1057 }
1058
1059 svn_pool_destroy(iterpool);
1060
1061 *result_entries = entries;
1062
1063 return SVN_NO_ERROR;
1064 }
1065
1066
1067 static svn_error_t *
read_entry_pair_txn(const svn_wc_entry_t ** parent_entry,const svn_wc_entry_t ** entry,svn_wc__db_t * db,const char * dir_abspath,svn_wc__db_wcroot_t * wcroot,const char * dir_relpath,const char * name,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1068 read_entry_pair_txn(const svn_wc_entry_t **parent_entry,
1069 const svn_wc_entry_t **entry,
1070 svn_wc__db_t *db,
1071 const char *dir_abspath,
1072 svn_wc__db_wcroot_t *wcroot,
1073 const char *dir_relpath,
1074 const char *name,
1075 apr_pool_t *result_pool,
1076 apr_pool_t *scratch_pool)
1077 {
1078 SVN_ERR(read_one_entry(parent_entry,
1079 db, dir_abspath,
1080 wcroot, dir_relpath,
1081 "" /* name */,
1082 NULL /* parent_entry */,
1083 result_pool, scratch_pool));
1084
1085 /* If we need the entry for "this dir", then return the parent_entry
1086 in both outputs. Otherwise, read the child node. */
1087 if (*name == '\0')
1088 {
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.
1093
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)
1097 {
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,
1102 scratch_pool));
1103 }
1104
1105 *entry = *parent_entry;
1106 }
1107 else
1108 {
1109 const apr_array_header_t *children;
1110 int i;
1111
1112 /* Default to not finding the child. */
1113 *entry = NULL;
1114
1115 /* Determine whether the parent KNOWS about this child. If it does
1116 not, then we should not attempt to look for it.
1117
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--; )
1124 {
1125 const char *child = APR_ARRAY_IDX(children, i, const char *);
1126
1127 if (strcmp(child, name) == 0)
1128 {
1129 svn_error_t *err;
1130
1131 err = read_one_entry(entry,
1132 db, dir_abspath,
1133 wcroot, dir_relpath,
1134 name, *parent_entry,
1135 result_pool, scratch_pool);
1136 if (err)
1137 {
1138 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
1139 return svn_error_trace(err);
1140
1141 /* No problem. Clear the error and leave the default value
1142 of "missing". */
1143 svn_error_clear(err);
1144 }
1145
1146 /* Found it. No need to keep searching. */
1147 break;
1148 }
1149 }
1150 /* if the loop ends without finding a child, then we have the default
1151 ENTRY value of NULL. */
1152 }
1153
1154 return SVN_NO_ERROR;
1155 }
1156
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").
1160
1161 The parent entry must exist.
1162
1163 The requested entry MAY exist. If it does not, then NULL will be returned.
1164
1165 The resulting entries are allocated in RESULT_POOL, and all temporary
1166 allocations are made in SCRATCH_POOL. */
1167 static svn_error_t *
read_entry_pair(const svn_wc_entry_t ** parent_entry,const svn_wc_entry_t ** entry,svn_wc__db_t * db,const char * dir_abspath,const char * name,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1168 read_entry_pair(const svn_wc_entry_t **parent_entry,
1169 const svn_wc_entry_t **entry,
1170 svn_wc__db_t *db,
1171 const char *dir_abspath,
1172 const char *name,
1173 apr_pool_t *result_pool,
1174 apr_pool_t *scratch_pool)
1175 {
1176 svn_wc__db_wcroot_t *wcroot;
1177 const char *dir_relpath;
1178
1179 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &dir_relpath,
1180 db, dir_abspath,
1181 scratch_pool, scratch_pool));
1182 VERIFY_USABLE_WCROOT(wcroot);
1183
1184 SVN_WC__DB_WITH_TXN(read_entry_pair_txn(parent_entry, entry,
1185 db, dir_abspath,
1186 wcroot, dir_relpath,
1187 name,
1188 result_pool, scratch_pool),
1189 wcroot);
1190
1191 return SVN_NO_ERROR;
1192 }
1193
1194 /* */
1195 static svn_error_t *
read_entries(apr_hash_t ** entries,svn_wc__db_t * db,const char * dir_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1196 read_entries(apr_hash_t **entries,
1197 svn_wc__db_t *db,
1198 const char *dir_abspath,
1199 apr_pool_t *result_pool,
1200 apr_pool_t *scratch_pool)
1201 {
1202 svn_wc__db_wcroot_t *wcroot;
1203 const char *dir_relpath;
1204 int wc_format;
1205
1206 SVN_ERR(svn_wc__db_temp_get_format(&wc_format, db, dir_abspath,
1207 scratch_pool));
1208
1209 if (wc_format < SVN_WC__WC_NG_VERSION)
1210 return svn_error_trace(svn_wc__read_entries_old(entries,
1211 dir_abspath,
1212 result_pool,
1213 scratch_pool));
1214
1215 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &dir_relpath,
1216 db, dir_abspath,
1217 scratch_pool, scratch_pool));
1218 VERIFY_USABLE_WCROOT(wcroot);
1219
1220 SVN_WC__DB_WITH_TXN(read_entries_new(entries,
1221 db, dir_abspath,
1222 wcroot, dir_relpath,
1223 result_pool, scratch_pool),
1224 wcroot);
1225
1226 return SVN_NO_ERROR;
1227 }
1228
1229
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.
1233
1234 KIND is as in svn_wc__get_entry().
1235
1236 Return the results in RESULT_POOL and use SCRATCH_POOL for temporary
1237 allocations. */
1238 static svn_error_t *
get_entry_access_info(const char ** adm_abspath,const char ** entry_name,svn_wc__db_t * db,const char * local_abspath,svn_node_kind_t kind,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1239 get_entry_access_info(const char **adm_abspath,
1240 const char **entry_name,
1241 svn_wc__db_t *db,
1242 const char *local_abspath,
1243 svn_node_kind_t kind,
1244 apr_pool_t *result_pool,
1245 apr_pool_t *scratch_pool)
1246 {
1247 svn_wc_adm_access_t *adm_access;
1248 svn_boolean_t read_from_subdir = FALSE;
1249
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)
1253 {
1254 svn_node_kind_t on_disk;
1255
1256 /* Do we already have an access baton for LOCAL_ABSPATH? */
1257 adm_access = svn_wc__adm_retrieve_internal2(db, local_abspath,
1258 scratch_pool);
1259 if (adm_access)
1260 {
1261 /* Sweet. The node is a directory. */
1262 on_disk = svn_node_dir;
1263 }
1264 else
1265 {
1266 svn_boolean_t special;
1267
1268 /* What's on disk? */
1269 SVN_ERR(svn_io_check_special_path(local_abspath, &on_disk, &special,
1270 scratch_pool));
1271 }
1272
1273 if (on_disk != svn_node_dir)
1274 {
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.
1279
1280 For NONE and UNKNOWN, it may be that metadata exists for the
1281 node, even though on-disk is unhelpful.
1282
1283 If NEED_PARENT_STUB is TRUE, and the entry is not a DIRECTORY,
1284 then we'll error.
1285
1286 If NEED_PARENT_STUB if FALSE, and we successfully read a stub,
1287 then this on-disk node is obstructing the read. */
1288 }
1289 else
1290 {
1291 /* We found a directory for this UNKNOWN node. Determine whether
1292 we need to read inside it. */
1293 read_from_subdir = TRUE;
1294 }
1295 }
1296 else if (kind == svn_node_dir)
1297 {
1298 read_from_subdir = TRUE;
1299 }
1300
1301 if (read_from_subdir)
1302 {
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);
1306 *entry_name = "";
1307 }
1308 else
1309 {
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);
1314 }
1315
1316 return SVN_NO_ERROR;
1317 }
1318
1319
1320 svn_error_t *
svn_wc__get_entry(const svn_wc_entry_t ** entry,svn_wc__db_t * db,const char * local_abspath,svn_boolean_t allow_unversioned,svn_node_kind_t kind,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1321 svn_wc__get_entry(const svn_wc_entry_t **entry,
1322 svn_wc__db_t *db,
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)
1328 {
1329 const char *dir_abspath;
1330 const char *entry_name;
1331
1332 SVN_ERR(get_entry_access_info(&dir_abspath, &entry_name, db, local_abspath,
1333 kind, scratch_pool, scratch_pool));
1334
1335 {
1336 const svn_wc_entry_t *parent_entry;
1337 svn_error_t *err;
1338
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.
1346
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. */
1350
1351 err = read_entry_pair(&parent_entry, entry,
1352 db, dir_abspath, entry_name,
1353 result_pool, scratch_pool);
1354 if (err)
1355 {
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);
1360
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:
1365
1366 1) a directory is obstructing a file in the parent
1367 2) the (versioned) directory's contents have been removed
1368
1369 Let's assume situation (1); if that is true, then we can just
1370 return the newly-found data.
1371
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
1377 describing that.
1378
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);
1388
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,
1398 scratch_pool));
1399 }
1400 }
1401
1402 if (*entry == NULL)
1403 {
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,
1409 scratch_pool));
1410 }
1411
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,
1418 scratch_pool));
1419
1420 return SVN_NO_ERROR;
1421 }
1422
1423 /* TODO ### Rewrite doc string to mention ENTRIES_ALL; not ADM_ACCESS.
1424
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 *
prune_deleted(apr_hash_t ** entries_pruned,apr_hash_t * entries_all,apr_pool_t * result_pool,apr_pool_t * scratch_pool)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)
1433 {
1434 apr_hash_index_t *hi;
1435
1436 if (!entries_all)
1437 {
1438 *entries_pruned = NULL;
1439 return SVN_NO_ERROR;
1440 }
1441
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);
1445 hi;
1446 hi = apr_hash_next(hi))
1447 {
1448 svn_boolean_t hidden;
1449
1450 SVN_ERR(svn_wc__entry_is_hidden(&hidden,
1451 apr_hash_this_val(hi)));
1452 if (hidden)
1453 break;
1454 }
1455
1456 if (! hi)
1457 {
1458 /* There are no deleted entries, so we can use the full hash */
1459 *entries_pruned = entries_all;
1460 return SVN_NO_ERROR;
1461 }
1462
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);
1466 hi;
1467 hi = apr_hash_next(hi))
1468 {
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;
1472
1473 SVN_ERR(svn_wc__entry_is_hidden(&hidden, entry));
1474 if (!hidden)
1475 svn_hash_sets(*entries_pruned, key, entry);
1476 }
1477
1478 return SVN_NO_ERROR;
1479 }
1480
1481 svn_error_t *
svn_wc__entries_read_internal(apr_hash_t ** entries,svn_wc_adm_access_t * adm_access,svn_boolean_t show_hidden,apr_pool_t * pool)1482 svn_wc__entries_read_internal(apr_hash_t **entries,
1483 svn_wc_adm_access_t *adm_access,
1484 svn_boolean_t show_hidden,
1485 apr_pool_t *pool)
1486 {
1487 apr_hash_t *new_entries;
1488
1489 new_entries = svn_wc__adm_access_entries(adm_access);
1490 if (! new_entries)
1491 {
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);
1495
1496 SVN_ERR(read_entries(&new_entries, db, local_abspath,
1497 result_pool, pool));
1498
1499 svn_wc__adm_access_set_entries(adm_access, new_entries);
1500 }
1501
1502 if (show_hidden)
1503 *entries = new_entries;
1504 else
1505 SVN_ERR(prune_deleted(entries, new_entries,
1506 svn_wc__adm_access_pool_internal(adm_access),
1507 pool));
1508
1509 return SVN_NO_ERROR;
1510 }
1511
1512 svn_error_t *
svn_wc_entries_read(apr_hash_t ** entries,svn_wc_adm_access_t * adm_access,svn_boolean_t show_hidden,apr_pool_t * pool)1513 svn_wc_entries_read(apr_hash_t **entries,
1514 svn_wc_adm_access_t *adm_access,
1515 svn_boolean_t show_hidden,
1516 apr_pool_t *pool)
1517 {
1518 return svn_error_trace(svn_wc__entries_read_internal(entries, adm_access,
1519 show_hidden, pool));
1520 }
1521
1522 /* No transaction required: called from write_entry which is itself
1523 transaction-wrapped. */
1524 static svn_error_t *
insert_node(svn_sqlite__db_t * sdb,const db_node_t * node,apr_pool_t * scratch_pool)1525 insert_node(svn_sqlite__db_t *sdb,
1526 const db_node_t *node,
1527 apr_pool_t *scratch_pool)
1528 {
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);
1532
1533 SVN_ERR_ASSERT(node->op_depth > 0 || node->repos_relpath);
1534
1535 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_NODE));
1536 SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnnnsn",
1537 node->wc_id,
1538 node->local_relpath,
1539 node->op_depth,
1540 node->parent_relpath,
1541 /* Setting depth for files? */
1542 (node->kind == svn_node_dir && present)
1543 ? svn_depth_to_word(node->depth)
1544 : NULL));
1545
1546 if (present && node->repos_relpath)
1547 {
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));
1551 }
1552
1553 if (node->repos_relpath
1554 && node->presence != svn_wc__db_status_base_deleted)
1555 {
1556 SVN_ERR(svn_sqlite__bind_int64(stmt, 5,
1557 node->repos_id));
1558 SVN_ERR(svn_sqlite__bind_text(stmt, 6,
1559 node->repos_relpath));
1560 SVN_ERR(svn_sqlite__bind_revnum(stmt, 7, node->revision));
1561 }
1562
1563 SVN_ERR(svn_sqlite__bind_token(stmt, 8, presence_map, node->presence));
1564
1565 if (node->kind == svn_node_none)
1566 SVN_ERR(svn_sqlite__bind_text(stmt, 10, "unknown"));
1567 else
1568 SVN_ERR(svn_sqlite__bind_token(stmt, 10, kind_map, node->kind));
1569
1570 if (node->kind == svn_node_file && present)
1571 {
1572 if (!node->checksum
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,
1580 scratch_pool));
1581
1582 SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, node->checksum,
1583 scratch_pool));
1584
1585 if (node->repos_relpath)
1586 {
1587 if (node->recorded_size != SVN_INVALID_FILESIZE)
1588 SVN_ERR(svn_sqlite__bind_int64(stmt, 16, node->recorded_size));
1589
1590 SVN_ERR(svn_sqlite__bind_int64(stmt, 17, node->recorded_time));
1591 }
1592 }
1593
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,
1597 scratch_pool));
1598
1599 if (node->file_external)
1600 SVN_ERR(svn_sqlite__bind_int(stmt, 20, 1));
1601
1602 if (node->inherited_props && present)
1603 SVN_ERR(svn_sqlite__bind_iprops(stmt, 23, node->inherited_props,
1604 scratch_pool));
1605
1606 SVN_ERR(svn_sqlite__insert(NULL, stmt));
1607
1608 return SVN_NO_ERROR;
1609 }
1610
1611
1612 /* */
1613 static svn_error_t *
insert_actual_node(svn_sqlite__db_t * sdb,svn_wc__db_t * db,const char * wri_abspath,const db_actual_node_t * actual_node,apr_pool_t * scratch_pool)1614 insert_actual_node(svn_sqlite__db_t *sdb,
1615 svn_wc__db_t *db,
1616 const char *wri_abspath,
1617 const db_actual_node_t *actual_node,
1618 apr_pool_t *scratch_pool)
1619 {
1620 svn_sqlite__stmt_t *stmt;
1621 svn_skel_t *conflict_data = NULL;
1622
1623 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_ACTUAL_NODE));
1624
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));
1628
1629 if (actual_node->properties)
1630 SVN_ERR(svn_sqlite__bind_properties(stmt, 4, actual_node->properties,
1631 scratch_pool));
1632
1633 if (actual_node->changelist)
1634 SVN_ERR(svn_sqlite__bind_text(stmt, 5, actual_node->changelist));
1635
1636 SVN_ERR(svn_wc__upgrade_conflict_skel_from_raw(
1637 &conflict_data,
1638 db, wri_abspath,
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)
1647 : 0,
1648 scratch_pool, scratch_pool));
1649
1650 if (conflict_data)
1651 {
1652 svn_stringbuf_t *data = svn_skel__unparse(conflict_data, scratch_pool);
1653
1654 SVN_ERR(svn_sqlite__bind_blob(stmt, 6, data->data, data->len));
1655 }
1656
1657 /* Execute and reset the insert clause. */
1658 return svn_error_trace(svn_sqlite__insert(NULL, stmt));
1659 }
1660
1661 static svn_boolean_t
is_switched(db_node_t * parent,db_node_t * child,apr_pool_t * scratch_pool)1662 is_switched(db_node_t *parent,
1663 db_node_t *child,
1664 apr_pool_t *scratch_pool)
1665 {
1666 if (parent && child)
1667 {
1668 if (parent->repos_id != child->repos_id)
1669 return TRUE;
1670
1671 if (parent->repos_relpath && child->repos_relpath)
1672 {
1673 const char *unswitched
1674 = svn_relpath_join(parent->repos_relpath,
1675 svn_relpath_basename(child->local_relpath,
1676 scratch_pool),
1677 scratch_pool);
1678 if (strcmp(unswitched, child->repos_relpath))
1679 return TRUE;
1680 }
1681 }
1682
1683 return FALSE;
1684 }
1685
1686 struct write_baton {
1687 db_node_t *base;
1688 db_node_t *work;
1689 db_node_t *below_work;
1690 apr_hash_t *tree_conflicts;
1691 };
1692
1693 #define WRITE_ENTRY_ASSERT(expr) \
1694 if (!(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, \
1699 local_relpath, \
1700 scratch_pool), \
1701 scratch_pool), __LINE__)
1702
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 *
write_entry(struct write_baton ** entry_node,const struct write_baton * parent_node,svn_wc__db_t * db,svn_sqlite__db_t * sdb,apr_int64_t wc_id,apr_int64_t repos_id,const svn_wc_entry_t * entry,const svn_wc__text_base_info_t * text_base_info,const char * local_relpath,const char * tmp_entry_abspath,const char * root_abspath,const svn_wc_entry_t * this_dir,svn_boolean_t create_locks,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1708 write_entry(struct write_baton **entry_node,
1709 const struct write_baton *parent_node,
1710 svn_wc__db_t *db,
1711 svn_sqlite__db_t *sdb,
1712 apr_int64_t wc_id,
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)
1723 {
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;
1729
1730 if (*local_relpath == '\0')
1731 parent_relpath = NULL;
1732 else
1733 parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
1734
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.
1737
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.
1745
1746 With just the above operations the conversion for files and
1747 directories is straightforward:
1748
1749 pre-wcng wcng
1750 parent child parent child
1751
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
1759 add add work 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
1768
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
1773 deleted tree.
1774
1775 original replace schedule in
1776 tree tree combined tree
1777
1778 A A replace+copied
1779 A/f delete+copied
1780 A/g A/g replace+copied
1781 A/h add+copied
1782 A/B A/B replace+copied
1783 A/B/f delete+copied
1784 A/B/g A/B/g replace+copied
1785 A/B/h add+copied
1786 A/C delete+copied
1787 A/C/f delete+copied
1788 A/D add+copied
1789 A/D/f add+copied
1790
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.
1794
1795 The root of the replace is handled like the file replace:
1796
1797 pre-wcng wcng
1798 parent child parent child
1799
1800 normal replace+copied base base+work
1801 add+copied replace+copied work work+work
1802
1803 although obviously the node is a directory rather than a file.
1804 There are then more conversion states where the parent is
1805 replaced.
1806
1807 pre-wcng wcng
1808 parent child parent child
1809
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
1815 */
1816
1817 WRITE_ENTRY_ASSERT(parent_node || entry->schedule == svn_wc_schedule_normal);
1818
1819 WRITE_ENTRY_ASSERT(!parent_node || parent_node->base
1820 || parent_node->below_work || parent_node->work);
1821
1822 switch (entry->schedule)
1823 {
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);
1829 else
1830 base_node = MAYBE_ALLOC(base_node, result_pool);
1831 break;
1832
1833 case svn_wc_schedule_add:
1834 working_node = MAYBE_ALLOC(working_node, result_pool);
1835 if (entry->deleted)
1836 {
1837 if (parent_node->base)
1838 base_node = MAYBE_ALLOC(base_node, result_pool);
1839 else
1840 below_working_node = MAYBE_ALLOC(below_working_node, result_pool);
1841 }
1842 break;
1843
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);
1850 break;
1851
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);
1856 else
1857 below_working_node = MAYBE_ALLOC(below_working_node, result_pool);
1858 break;
1859 }
1860
1861 /* Something deleted in this revision means there should always be a
1862 BASE node to indicate the not-present node. */
1863 if (entry->deleted)
1864 {
1865 WRITE_ENTRY_ASSERT(base_node || below_working_node);
1866 WRITE_ENTRY_ASSERT(!entry->incomplete);
1867 if (base_node)
1868 base_node->presence = svn_wc__db_status_not_present;
1869 else
1870 below_working_node->presence = svn_wc__db_status_not_present;
1871 }
1872 else if (entry->absent)
1873 {
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;
1877 }
1878
1879 if (entry->copied)
1880 {
1881 db_node_t *work = parent_node->work
1882 ? parent_node->work
1883 : parent_node->below_work;
1884
1885 if (entry->copyfrom_url)
1886 {
1887 working_node->repos_id = repos_id;
1888 working_node->repos_relpath = svn_uri_skip_ancestor(
1889 this_dir->repos, entry->copyfrom_url,
1890 result_pool);
1891 working_node->revision = entry->copyfrom_rev;
1892 working_node->op_depth
1893 = svn_wc__db_op_depth_for_upgrade(local_relpath);
1894
1895 if (work && work->repos_relpath
1896 && work->repos_id == repos_id
1897 && work->revision == entry->copyfrom_rev)
1898 {
1899 const char *name;
1900
1901 name = svn_relpath_skip_ancestor(work->repos_relpath,
1902 working_node->repos_relpath);
1903
1904 if (name
1905 && !strcmp(name, svn_relpath_basename(local_relpath, NULL)))
1906 {
1907 working_node->op_depth = work->op_depth;
1908 }
1909 }
1910 }
1911 else if (work && work->repos_relpath)
1912 {
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),
1917 result_pool);
1918 working_node->revision = work->revision;
1919 working_node->op_depth = work->op_depth;
1920 }
1921 else if (parent_node->below_work
1922 && parent_node->below_work->repos_relpath)
1923 {
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),
1929 result_pool);
1930 working_node->revision = parent_node->below_work->revision;
1931 working_node->op_depth = parent_node->below_work->op_depth;
1932 }
1933 else
1934 return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL,
1935 _("No copyfrom URL for '%s'"),
1936 svn_dirent_local_style(local_relpath,
1937 scratch_pool));
1938
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)
1944 {
1945 /* Introduce a not-present node! */
1946 below_working_node = MAYBE_ALLOC(below_working_node, scratch_pool);
1947
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;
1952
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;
1956
1957 SVN_ERR(insert_node(sdb, below_working_node, scratch_pool));
1958
1959 below_working_node = NULL; /* Don't write a present intermediate! */
1960 }
1961 }
1962
1963 if (entry->conflict_old)
1964 {
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,
1969 scratch_pool);
1970 else
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,
1975 scratch_pool);
1976 else
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,
1981 scratch_pool);
1982 else
1983 actual_node->conflict_working = entry->conflict_wrk;
1984 }
1985
1986 if (entry->prejfile)
1987 {
1988 actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
1989 actual_node->prop_reject = svn_relpath_join((entry->kind == svn_node_dir
1990 ? local_relpath
1991 : parent_relpath),
1992 entry->prejfile,
1993 scratch_pool);
1994 }
1995
1996 if (entry->changelist)
1997 {
1998 actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
1999 actual_node->changelist = entry->changelist;
2000 }
2001
2002 /* ### set the text_mod value? */
2003
2004 if (entry_node && entry->tree_conflict_data)
2005 {
2006 /* Issues #3840/#3916: 1.6 stores multiple tree conflicts on the
2007 parent node, 1.7 stores them directly on the conflicted nodes.
2008 So "((skel1) (skel2))" becomes "(skel1)" and "(skel2)" */
2009 svn_skel_t *skel;
2010
2011 skel = svn_skel__parse(entry->tree_conflict_data,
2012 strlen(entry->tree_conflict_data),
2013 scratch_pool);
2014 tree_conflicts = apr_hash_make(result_pool);
2015 skel = skel->children;
2016 while (skel)
2017 {
2018 svn_wc_conflict_description2_t *conflict;
2019 svn_skel_t *new_skel;
2020 const char *key;
2021
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,
2025 skel,
2026 svn_dirent_join(root_abspath,
2027 local_relpath,
2028 scratch_pool),
2029 scratch_pool, scratch_pool));
2030
2031 WRITE_ENTRY_ASSERT(conflict->kind == svn_wc_conflict_kind_tree);
2032
2033 SVN_ERR(svn_wc__serialize_conflict(&new_skel, conflict,
2034 scratch_pool, scratch_pool));
2035
2036 /* Store in hash to be retrieved when writing the child
2037 row. */
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);
2041 skel = skel->next;
2042 }
2043 }
2044 else
2045 tree_conflicts = NULL;
2046
2047 if (parent_node && parent_node->tree_conflicts)
2048 {
2049 const char *tree_conflict_data =
2050 svn_hash_gets(parent_node->tree_conflicts, local_relpath);
2051 if (tree_conflict_data)
2052 {
2053 actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
2054 actual_node->tree_conflict_data = tree_conflict_data;
2055 }
2056
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);
2060 }
2061
2062 if (entry->file_external_path != NULL)
2063 {
2064 base_node = MAYBE_ALLOC(base_node, result_pool);
2065 }
2066
2067
2068 /* Insert the base node. */
2069 if (base_node)
2070 {
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;
2078
2079 if (entry->depth != svn_depth_exclude)
2080 base_node->depth = entry->depth;
2081 else
2082 {
2083 base_node->presence = svn_wc__db_status_excluded;
2084 base_node->depth = svn_depth_infinity;
2085 }
2086
2087 if (entry->deleted)
2088 {
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;
2093 }
2094 else if (entry->absent)
2095 {
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;
2100
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;
2106 }
2107 else
2108 {
2109 base_node->kind = entry->kind;
2110
2111 if (base_node->presence != svn_wc__db_status_excluded)
2112 {
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))
2118 {
2119 base_node->presence = svn_wc__db_status_incomplete;
2120
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;
2126 }
2127 else if (entry->incomplete)
2128 {
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;
2133 }
2134 }
2135 }
2136
2137 if (entry->kind == svn_node_dir)
2138 base_node->checksum = NULL;
2139 else
2140 {
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;
2145 else
2146 base_node->checksum = NULL;
2147
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))
2152 {
2153 svn_checksum_t *entry_md5_checksum, *found_md5_checksum;
2154 SVN_ERR(svn_checksum_parse_hex(&entry_md5_checksum,
2155 svn_checksum_md5,
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;
2162 else
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,
2171 local_relpath,
2172 scratch_pool),
2173 scratch_pool),
2174 svn_checksum_to_cstring_display(
2175 entry_md5_checksum, scratch_pool),
2176 svn_checksum_to_cstring_display(
2177 found_md5_checksum, scratch_pool));
2178 else
2179 {
2180 /* ### Not sure what conditions this should cover. */
2181 /* SVN_ERR_ASSERT(entry->deleted || ...); */
2182 }
2183 }
2184 }
2185
2186 if (this_dir->repos)
2187 {
2188 base_node->repos_id = repos_id;
2189
2190 if (entry->url != NULL)
2191 {
2192 base_node->repos_relpath = svn_uri_skip_ancestor(
2193 this_dir->repos, entry->url,
2194 result_pool);
2195 }
2196 else
2197 {
2198 const char *relpath = svn_uri_skip_ancestor(this_dir->repos,
2199 this_dir->url,
2200 scratch_pool);
2201 if (relpath == NULL || *relpath == '\0')
2202 base_node->repos_relpath = entry->name;
2203 else
2204 base_node->repos_relpath =
2205 svn_dirent_join(relpath, entry->name, result_pool);
2206 }
2207 }
2208
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
2211 server.
2212
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;
2218
2219 if (entry->file_external_path)
2220 base_node->file_external = TRUE;
2221
2222 /* Switched nodes get an empty iprops cache. */
2223 if (parent_node
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*));
2227
2228 SVN_ERR(insert_node(sdb, base_node, scratch_pool));
2229
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
2232 the abs path. */
2233 if (entry->lock_token && create_locks)
2234 {
2235 svn_wc__db_lock_t lock;
2236
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;
2241
2242 SVN_ERR(svn_wc__db_lock_add(db, tmp_entry_abspath, &lock,
2243 scratch_pool));
2244 }
2245 }
2246
2247 if (below_working_node)
2248 {
2249 db_node_t *work
2250 = parent_node->below_work ? parent_node->below_work : parent_node->work;
2251
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;
2260
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),
2268 result_pool);
2269 else
2270 below_working_node->repos_relpath = NULL;
2271
2272 /* The revert_base checksum isn't available in the entry structure,
2273 so the caller provides it. */
2274
2275 /* text_base_info is NULL for files scheduled to be added. */
2276 below_working_node->checksum = NULL;
2277 if (text_base_info)
2278 {
2279 if (entry->schedule == svn_wc_schedule_delete)
2280 below_working_node->checksum =
2281 text_base_info->normal_base.sha1_checksum;
2282 else
2283 below_working_node->checksum =
2284 text_base_info->revert_base.sha1_checksum;
2285 }
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;
2293
2294 if (working_node
2295 && entry->schedule == svn_wc_schedule_delete
2296 && working_node->repos_relpath)
2297 {
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;
2304
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;
2309
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;
2315 }
2316
2317 SVN_ERR(insert_node(sdb, below_working_node, scratch_pool));
2318 }
2319
2320 /* Insert the working node. */
2321 if (working_node)
2322 {
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;
2329
2330 if (entry->depth != svn_depth_exclude)
2331 working_node->depth = entry->depth;
2332 else
2333 {
2334 working_node->presence = svn_wc__db_status_excluded;
2335 working_node->depth = svn_depth_infinity;
2336 }
2337
2338 if (entry->kind == svn_node_dir)
2339 working_node->checksum = NULL;
2340 else
2341 {
2342 working_node->checksum = NULL;
2343 /* text_base_info is NULL for files scheduled to be added. */
2344 if (text_base_info)
2345 working_node->checksum = text_base_info->normal_base.sha1_checksum;
2346
2347
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. */
2350 #ifdef SVN_DEBUG
2351 if (entry->checksum && text_base_info)
2352 {
2353 svn_checksum_t *md5_checksum;
2354 SVN_ERR(svn_checksum_parse_hex(&md5_checksum, svn_checksum_md5,
2355 entry->checksum, result_pool));
2356 SVN_ERR_ASSERT(
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));
2360 }
2361 #endif
2362 }
2363
2364 working_node->kind = entry->kind;
2365 if (working_node->presence != svn_wc__db_status_excluded)
2366 {
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))
2371 {
2372 working_node->presence = svn_wc__db_status_incomplete;
2373 working_node->kind = svn_node_dir;
2374 }
2375 else if (entry->schedule == svn_wc_schedule_delete)
2376 {
2377 working_node->presence = svn_wc__db_status_base_deleted;
2378 working_node->kind = entry->kind;
2379 }
2380 else
2381 {
2382 /* presence == normal */
2383 working_node->kind = entry->kind;
2384
2385 if (entry->incomplete)
2386 {
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;
2391 }
2392 }
2393 }
2394
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.
2398
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)
2402 {
2403 working_node->changed_rev = entry->cmt_rev;
2404 working_node->changed_date = entry->cmt_date;
2405 working_node->changed_author = entry->cmt_author;
2406 }
2407
2408 if (entry->schedule == svn_wc_schedule_delete
2409 && parent_node->work
2410 && parent_node->work->presence == svn_wc__db_status_base_deleted)
2411 {
2412 working_node->op_depth = parent_node->work->op_depth;
2413 }
2414 else if (working_node->presence == svn_wc__db_status_excluded
2415 && parent_node->work)
2416 {
2417 working_node->op_depth = parent_node->work->op_depth;
2418 }
2419 else if (!entry->copied)
2420 {
2421 working_node->op_depth
2422 = svn_wc__db_op_depth_for_upgrade(local_relpath);
2423 }
2424
2425 SVN_ERR(insert_node(sdb, working_node, scratch_pool));
2426 }
2427
2428 /* Insert the actual node. */
2429 if (actual_node)
2430 {
2431 actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
2432
2433 actual_node->wc_id = wc_id;
2434 actual_node->local_relpath = local_relpath;
2435 actual_node->parent_relpath = parent_relpath;
2436
2437 SVN_ERR(insert_actual_node(sdb, db, tmp_entry_abspath,
2438 actual_node, scratch_pool));
2439 }
2440
2441 if (entry_node)
2442 {
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;
2448 }
2449
2450 if (entry->file_external_path)
2451 {
2452 /* TODO: Maybe add a file external registration inside EXTERNALS here,
2453 to allow removing file externals that aren't referenced from
2454 svn:externals.
2455
2456 The svn:externals values are processed anyway after everything is
2457 upgraded */
2458 }
2459
2460 return SVN_NO_ERROR;
2461 }
2462
2463 static svn_error_t *
write_actual_only_entries(apr_hash_t * tree_conflicts,svn_sqlite__db_t * sdb,svn_wc__db_t * db,const char * wri_abspath,apr_int64_t wc_id,const char * parent_relpath,apr_pool_t * scratch_pool)2464 write_actual_only_entries(apr_hash_t *tree_conflicts,
2465 svn_sqlite__db_t *sdb,
2466 svn_wc__db_t *db,
2467 const char *wri_abspath,
2468 apr_int64_t wc_id,
2469 const char *parent_relpath,
2470 apr_pool_t *scratch_pool)
2471 {
2472 apr_hash_index_t *hi;
2473
2474 for (hi = apr_hash_first(scratch_pool, tree_conflicts);
2475 hi;
2476 hi = apr_hash_next(hi))
2477 {
2478 db_actual_node_t *actual_node = NULL;
2479
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);
2485
2486 SVN_ERR(insert_actual_node(sdb, db, wri_abspath, actual_node,
2487 scratch_pool));
2488 }
2489
2490 return SVN_NO_ERROR;
2491 }
2492
2493 svn_error_t *
svn_wc__write_upgraded_entries(void ** dir_baton,void * parent_baton,svn_wc__db_t * db,svn_sqlite__db_t * sdb,apr_int64_t repos_id,apr_int64_t wc_id,const char * dir_abspath,const char * new_root_abspath,apr_hash_t * entries,apr_hash_t * text_bases_info,apr_pool_t * result_pool,apr_pool_t * scratch_pool)2494 svn_wc__write_upgraded_entries(void **dir_baton,
2495 void *parent_baton,
2496 svn_wc__db_t *db,
2497 svn_sqlite__db_t *sdb,
2498 apr_int64_t repos_id,
2499 apr_int64_t wc_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)
2506 {
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;
2513
2514 /* Get a copy of the "this dir" entry for comparison purposes. */
2515 this_dir = svn_hash_gets(entries, SVN_WC_ENTRY_THIS_DIR);
2516
2517 /* If there is no "this dir" entry, something is wrong. */
2518 if (! this_dir)
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,
2522 iterpool));
2523 old_root_abspath = svn_dirent_get_longest_ancestor(dir_abspath,
2524 new_root_abspath,
2525 scratch_pool);
2526
2527 SVN_ERR_ASSERT(old_root_abspath[0]);
2528
2529 dir_relpath = svn_dirent_skip_ancestor(old_root_abspath, dir_abspath);
2530
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,
2535 iterpool),
2536 old_root_abspath,
2537 this_dir, FALSE, result_pool, iterpool));
2538
2539 for (hi = apr_hash_first(scratch_pool, entries); hi;
2540 hi = apr_hash_next(hi))
2541 {
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);
2547
2548 svn_pool_clear(iterpool);
2549
2550 /* Don't rewrite the "this dir" entry! */
2551 if (strcmp(name, SVN_WC_ENTRY_THIS_DIR) == 0)
2552 continue;
2553
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,
2559 wc_id, repos_id,
2560 this_entry, text_base_info, child_relpath,
2561 svn_dirent_join(new_root_abspath, child_relpath,
2562 iterpool),
2563 old_root_abspath,
2564 this_dir, TRUE, iterpool, iterpool));
2565 }
2566
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,
2570 iterpool));
2571
2572 *dir_baton = dir_node;
2573 svn_pool_destroy(iterpool);
2574 return SVN_NO_ERROR;
2575 }
2576
2577
2578 svn_wc_entry_t *
svn_wc_entry_dup(const svn_wc_entry_t * entry,apr_pool_t * pool)2579 svn_wc_entry_dup(const svn_wc_entry_t *entry, apr_pool_t *pool)
2580 {
2581 svn_wc_entry_t *dupentry = apr_palloc(pool, sizeof(*dupentry));
2582
2583 /* Perform a trivial copy ... */
2584 *dupentry = *entry;
2585
2586 /* ...and then re-copy stuff that needs to be duped into our pool. */
2587 if (entry->name)
2588 dupentry->name = apr_pstrdup(pool, entry->name);
2589 if (entry->url)
2590 dupentry->url = apr_pstrdup(pool, entry->url);
2591 if (entry->repos)
2592 dupentry->repos = apr_pstrdup(pool, entry->repos);
2593 if (entry->uuid)
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);
2617
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 = "";
2622
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);
2629 return dupentry;
2630 }
2631
2632
2633 /*** Generic Entry Walker */
2634
2635 /* A recursive entry-walker, helper for svn_wc_walk_entries3().
2636 *
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).
2641 *
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.
2644 *
2645 * Call CANCEL_FUNC with CANCEL_BATON to allow cancellation.
2646 */
2647 static svn_error_t *
walker_helper(const char * dirpath,svn_wc_adm_access_t * adm_access,const svn_wc_entry_callbacks2_t * walk_callbacks,void * walk_baton,svn_depth_t depth,svn_boolean_t show_hidden,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * pool)2648 walker_helper(const char *dirpath,
2649 svn_wc_adm_access_t *adm_access,
2650 const svn_wc_entry_callbacks2_t *walk_callbacks,
2651 void *walk_baton,
2652 svn_depth_t depth,
2653 svn_boolean_t show_hidden,
2654 svn_cancel_func_t cancel_func,
2655 void *cancel_baton,
2656 apr_pool_t *pool)
2657 {
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;
2662 svn_error_t *err;
2663 svn_wc__db_t *db = svn_wc__adm_get_db(adm_access);
2664
2665 err = svn_wc__entries_read_internal(&entries, adm_access, show_hidden,
2666 pool);
2667
2668 if (err)
2669 SVN_ERR(walk_callbacks->handle_error(dirpath, err, walk_baton, pool));
2670
2671 /* As promised, always return the '.' entry first. */
2672 dot_entry = svn_hash_gets(entries, SVN_WC_ENTRY_THIS_DIR);
2673 if (! dot_entry)
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)),
2678 walk_baton, pool);
2679
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. */
2684
2685 err = walk_callbacks->found_entry(dirpath, dot_entry, walk_baton, subpool);
2686
2687
2688 if(err)
2689 SVN_ERR(walk_callbacks->handle_error(dirpath, err, walk_baton, pool));
2690
2691 if (depth == svn_depth_empty)
2692 return SVN_NO_ERROR;
2693
2694 /* Loop over each of the other entries. */
2695 for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
2696 {
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;
2702
2703 svn_pool_clear(subpool);
2704
2705 /* See if someone wants to cancel this operation. */
2706 if (cancel_func)
2707 SVN_ERR(cancel_func(cancel_baton));
2708
2709 /* Skip the "this dir" entry. */
2710 if (strcmp(current_entry->name, SVN_WC_ENTRY_THIS_DIR) == 0)
2711 continue;
2712
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));
2716
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)
2721 {
2722 err = walk_callbacks->found_entry(entrypath, current_entry,
2723 walk_baton, subpool);
2724
2725 if (err)
2726 SVN_ERR(walk_callbacks->handle_error(entrypath, err,
2727 walk_baton, pool));
2728 }
2729
2730 /* Recurse into this entry if appropriate. */
2731 if (current_entry->kind == svn_node_dir
2732 && !hidden
2733 && depth >= svn_depth_immediates)
2734 {
2735 svn_wc_adm_access_t *entry_access;
2736 svn_depth_t depth_below_here = depth;
2737
2738 if (depth == svn_depth_immediates)
2739 depth_below_here = svn_depth_empty;
2740
2741 entry_access = svn_wc__adm_retrieve_internal2(db, entry_abspath,
2742 subpool);
2743
2744 if (entry_access)
2745 SVN_ERR(walker_helper(entrypath, entry_access,
2746 walk_callbacks, walk_baton,
2747 depth_below_here, show_hidden,
2748 cancel_func, cancel_baton,
2749 subpool));
2750 }
2751 }
2752
2753 svn_pool_destroy(subpool);
2754 return SVN_NO_ERROR;
2755 }
2756
2757 svn_error_t *
svn_wc__walker_default_error_handler(const char * path,svn_error_t * err,void * walk_baton,apr_pool_t * pool)2758 svn_wc__walker_default_error_handler(const char *path,
2759 svn_error_t *err,
2760 void *walk_baton,
2761 apr_pool_t *pool)
2762 {
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);
2766 }
2767
2768
2769 /* The public API. */
2770 svn_error_t *
svn_wc_walk_entries3(const char * path,svn_wc_adm_access_t * adm_access,const svn_wc_entry_callbacks2_t * walk_callbacks,void * walk_baton,svn_depth_t walk_depth,svn_boolean_t show_hidden,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * pool)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,
2774 void *walk_baton,
2775 svn_depth_t walk_depth,
2776 svn_boolean_t show_hidden,
2777 svn_cancel_func_t cancel_func,
2778 void *cancel_baton,
2779 apr_pool_t *pool)
2780 {
2781 const char *local_abspath;
2782 svn_wc__db_t *db = svn_wc__adm_get_db(adm_access);
2783 svn_error_t *err;
2784 svn_node_kind_t kind;
2785 svn_wc__db_status_t status;
2786
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,
2792 NULL, NULL, NULL,
2793 db, local_abspath,
2794 pool, pool);
2795 if (err)
2796 {
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)),
2805 walk_baton, pool);
2806 }
2807
2808 if (kind == svn_node_file
2809 || status == svn_wc__db_status_excluded
2810 || status == svn_wc__db_status_server_excluded)
2811 {
2812 const svn_wc_entry_t *entry;
2813
2814 /* ### we should stop passing out entry structures.
2815 ###
2816 ### we should not call handle_error for an error the *callback*
2817 ### gave us. let it deal with the problem before returning. */
2818
2819 if (!show_hidden
2820 && (status == svn_wc__db_status_not_present
2821 || status == svn_wc__db_status_excluded
2822 || status == svn_wc__db_status_server_excluded))
2823 {
2824 /* The fool asked to walk a "hidden" node. Report the node as
2825 unversioned.
2826
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)),
2836 walk_baton, pool);
2837 }
2838
2839 SVN_ERR(svn_wc__get_entry(&entry, db, local_abspath, FALSE,
2840 svn_node_file, pool, pool));
2841
2842 err = walk_callbacks->found_entry(path, entry, walk_baton, pool);
2843 if (err)
2844 return walk_callbacks->handle_error(path, err, walk_baton, pool);
2845
2846 return SVN_NO_ERROR;
2847 }
2848
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,
2852 pool);
2853
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)),
2858 walk_baton, pool);
2859 }
2860