]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/subversion/subversion/libsvn_wc/entries.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / contrib / subversion / subversion / libsvn_wc / entries.c
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 #include "svn_private_config.h"
49 #include "private/svn_wc_private.h"
50 #include "private/svn_sqlite.h"
51
52 #define MAYBE_ALLOC(x,p) ((x) ? (x) : apr_pcalloc((p), sizeof(*(x))))
53
54
55 /* Temporary structures which mirror the tables in wc-metadata.sql.
56    For detailed descriptions of each field, see that file. */
57 typedef struct db_node_t {
58   apr_int64_t wc_id;
59   const char *local_relpath;
60   int op_depth;
61   apr_int64_t repos_id;
62   const char *repos_relpath;
63   const char *parent_relpath;
64   svn_wc__db_status_t presence;
65   svn_revnum_t revision;
66   svn_node_kind_t kind;
67   svn_checksum_t *checksum;
68   svn_filesize_t recorded_size;
69   svn_revnum_t changed_rev;
70   apr_time_t changed_date;
71   const char *changed_author;
72   svn_depth_t depth;
73   apr_time_t recorded_time;
74   apr_hash_t *properties;
75   svn_boolean_t file_external;
76   apr_array_header_t *inherited_props;
77 } db_node_t;
78
79 typedef struct db_actual_node_t {
80   apr_int64_t wc_id;
81   const char *local_relpath;
82   const char *parent_relpath;
83   apr_hash_t *properties;
84   const char *conflict_old;
85   const char *conflict_new;
86   const char *conflict_working;
87   const char *prop_reject;
88   const char *changelist;
89   /* ### enum for text_mod */
90   const char *tree_conflict_data;
91 } db_actual_node_t;
92
93
94 \f
95 /*** reading and writing the entries file ***/
96
97
98 /* */
99 static svn_wc_entry_t *
100 alloc_entry(apr_pool_t *pool)
101 {
102   svn_wc_entry_t *entry = apr_pcalloc(pool, sizeof(*entry));
103   entry->revision = SVN_INVALID_REVNUM;
104   entry->copyfrom_rev = SVN_INVALID_REVNUM;
105   entry->cmt_rev = SVN_INVALID_REVNUM;
106   entry->kind = svn_node_none;
107   entry->working_size = SVN_WC_ENTRY_WORKING_SIZE_UNKNOWN;
108   entry->depth = svn_depth_infinity;
109   entry->file_external_peg_rev.kind = svn_opt_revision_unspecified;
110   entry->file_external_rev.kind = svn_opt_revision_unspecified;
111   return entry;
112 }
113
114
115 /* Is the entry in a 'hidden' state in the sense of the 'show_hidden'
116  * switches on svn_wc_entries_read(), svn_wc_walk_entries*(), etc.? */
117 svn_error_t *
118 svn_wc__entry_is_hidden(svn_boolean_t *hidden, const svn_wc_entry_t *entry)
119 {
120   /* In English, the condition is: "the entry is not present, and I haven't
121      scheduled something over the top of it."  */
122   if (entry->deleted
123       || entry->absent
124       || entry->depth == svn_depth_exclude)
125     {
126       /* These kinds of nodes cannot be marked for deletion (which also
127          means no "replace" either).  */
128       SVN_ERR_ASSERT(entry->schedule == svn_wc_schedule_add
129                      || entry->schedule == svn_wc_schedule_normal);
130
131       /* Hidden if something hasn't been added over it.
132
133          ### is this even possible with absent or excluded nodes?  */
134       *hidden = entry->schedule != svn_wc_schedule_add;
135     }
136   else
137     *hidden = FALSE;
138
139   return SVN_NO_ERROR;
140 }
141
142
143 /* Hit the database to check the file external information for the given
144    entry.  The entry will be modified in place. */
145 static svn_error_t *
146 check_file_external(svn_wc_entry_t *entry,
147                     svn_wc__db_t *db,
148                     const char *local_abspath,
149                     const char *wri_abspath,
150                     apr_pool_t *result_pool,
151                     apr_pool_t *scratch_pool)
152 {
153   svn_wc__db_status_t status;
154   svn_node_kind_t kind;
155   const char *repos_relpath;
156   svn_revnum_t peg_revision;
157   svn_revnum_t revision;
158   svn_error_t *err;
159
160   err = svn_wc__db_external_read(&status, &kind, NULL, NULL, NULL,
161                                  &repos_relpath, &peg_revision, &revision,
162                                  db, local_abspath, wri_abspath,
163                                  result_pool, scratch_pool);
164
165   if (err)
166     {
167       if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
168         return svn_error_trace(err);
169
170       svn_error_clear(err);
171       return SVN_NO_ERROR;
172     }
173
174   if (status == svn_wc__db_status_normal
175       && kind == svn_node_file)
176     {
177       entry->file_external_path = repos_relpath;
178       if (SVN_IS_VALID_REVNUM(peg_revision))
179         {
180           entry->file_external_peg_rev.kind = svn_opt_revision_number;
181           entry->file_external_peg_rev.value.number = peg_revision;
182           entry->file_external_rev = entry->file_external_peg_rev;
183         }
184       if (SVN_IS_VALID_REVNUM(revision))
185         {
186           entry->file_external_rev.kind = svn_opt_revision_number;
187           entry->file_external_rev.value.number = revision;
188         }
189     }
190
191   return SVN_NO_ERROR;
192 }
193
194
195 /* Fill in the following fields of ENTRY:
196
197      REVISION
198      REPOS
199      UUID
200      CMT_REV
201      CMT_DATE
202      CMT_AUTHOR
203      DEPTH
204      DELETED
205
206    Return: KIND, REPOS_RELPATH, CHECKSUM
207 */
208 static svn_error_t *
209 get_info_for_deleted(svn_wc_entry_t *entry,
210                      svn_node_kind_t *kind,
211                      const char **repos_relpath,
212                      const svn_checksum_t **checksum,
213                      svn_wc__db_lock_t **lock,
214                      svn_wc__db_t *db,
215                      const char *entry_abspath,
216                      const svn_wc_entry_t *parent_entry,
217                      svn_boolean_t have_base,
218                      svn_boolean_t have_more_work,
219                      apr_pool_t *result_pool,
220                      apr_pool_t *scratch_pool)
221 {
222   if (have_base && !have_more_work)
223     {
224       /* This is the delete of a BASE node */
225       SVN_ERR(svn_wc__db_base_get_info(NULL, kind,
226                                        &entry->revision,
227                                        repos_relpath,
228                                        &entry->repos,
229                                        &entry->uuid,
230                                        &entry->cmt_rev,
231                                        &entry->cmt_date,
232                                        &entry->cmt_author,
233                                        &entry->depth,
234                                        checksum,
235                                        NULL,
236                                        lock,
237                                        &entry->has_props, NULL,
238                                        NULL,
239                                        db,
240                                        entry_abspath,
241                                        result_pool,
242                                        scratch_pool));
243     }
244   else
245     {
246       const char *work_del_abspath;
247       const char *parent_repos_relpath;
248       const char *parent_abspath;
249
250       /* This is a deleted child of a copy/move-here,
251          so we need to scan up the WORKING tree to find the root of
252          the deletion. Then examine its parent to discover its
253          future location in the repository.  */
254       SVN_ERR(svn_wc__db_read_pristine_info(NULL, kind,
255                                             &entry->cmt_rev,
256                                             &entry->cmt_date,
257                                             &entry->cmt_author,
258                                             &entry->depth,
259                                             checksum,
260                                             NULL,
261                                             &entry->has_props, NULL,
262                                             db,
263                                             entry_abspath,
264                                             result_pool,
265                                             scratch_pool));
266       /* working_size and text_time unavailable */
267
268      SVN_ERR(svn_wc__db_scan_deletion(NULL,
269                                       NULL,
270                                       &work_del_abspath, NULL,
271                                       db, entry_abspath,
272                                       scratch_pool, scratch_pool));
273
274       SVN_ERR_ASSERT(work_del_abspath != NULL);
275       parent_abspath = svn_dirent_dirname(work_del_abspath, scratch_pool);
276
277       /* The parent directory of the delete root must be added, so we
278          can find the required information there */
279       SVN_ERR(svn_wc__db_scan_addition(NULL, NULL,
280                                        &parent_repos_relpath,
281                                        &entry->repos,
282                                        &entry->uuid,
283                                        NULL, NULL, NULL, NULL,
284                                        db, parent_abspath,
285                                        result_pool, scratch_pool));
286
287       /* Now glue it all together */
288       *repos_relpath = svn_relpath_join(parent_repos_relpath,
289                                         svn_dirent_is_child(parent_abspath,
290                                                             entry_abspath,
291                                                             NULL),
292                                         result_pool);
293
294
295       /* Even though this is the delete of a WORKING node, there might still
296          be a BASE node somewhere below with an interesting revision */
297       if (have_base)
298         {
299           svn_wc__db_status_t status;
300           SVN_ERR(svn_wc__db_base_get_info(&status, NULL, &entry->revision,
301                                            NULL, NULL, NULL, NULL, NULL, NULL,
302                                            NULL, NULL, NULL, lock, NULL, NULL,
303                                            NULL,
304                                            db, entry_abspath,
305                                            result_pool, scratch_pool));
306
307           if (status == svn_wc__db_status_not_present)
308             entry->deleted = TRUE;
309         }
310     }
311
312   /* Do some extra work for the child nodes.  */
313   if (!SVN_IS_VALID_REVNUM(entry->revision) && parent_entry != NULL)
314     {
315       /* For child nodes without a revision, pick up the parent's
316          revision.  */
317       entry->revision = parent_entry->revision;
318     }
319
320   return SVN_NO_ERROR;
321 }
322
323
324 /*
325  * Encode tree conflict descriptions into a single string.
326  *
327  * Set *CONFLICT_DATA to a string, allocated in POOL, that encodes the tree
328  * conflicts in CONFLICTS in a form suitable for storage in a single string
329  * field in a WC entry. CONFLICTS is a hash of zero or more pointers to
330  * svn_wc_conflict_description2_t objects, index by their basenames. All of the
331  * conflict victim paths must be siblings.
332  *
333  * Do all allocations in POOL.
334  *
335  * @see svn_wc__read_tree_conflicts()
336  */
337 static svn_error_t *
338 write_tree_conflicts(const char **conflict_data,
339                      apr_hash_t *conflicts,
340                      apr_pool_t *pool)
341 {
342   svn_skel_t *skel = svn_skel__make_empty_list(pool);
343   apr_hash_index_t *hi;
344
345   for (hi = apr_hash_first(pool, conflicts); hi; hi = apr_hash_next(hi))
346     {
347       svn_skel_t *c_skel;
348
349       SVN_ERR(svn_wc__serialize_conflict(&c_skel, svn__apr_hash_index_val(hi),
350                                          pool, pool));
351       svn_skel__prepend(c_skel, skel);
352     }
353
354   *conflict_data = svn_skel__unparse(skel, pool)->data;
355
356   return SVN_NO_ERROR;
357 }
358
359
360 /* Read one entry from wc_db. It will be allocated in RESULT_POOL and
361    returned in *NEW_ENTRY.
362
363    DIR_ABSPATH is the name of the directory to read this entry from, and
364    it will be named NAME (use "" for "this dir").
365
366    DB specifies the wc_db database, and WC_ID specifies which working copy
367    this information is being read from.
368
369    If this node is "this dir", then PARENT_ENTRY should be NULL. Otherwise,
370    it should refer to the entry for the child's parent directory.
371
372    Temporary allocations are made in SCRATCH_POOL.  */
373 static svn_error_t *
374 read_one_entry(const svn_wc_entry_t **new_entry,
375                svn_wc__db_t *db,
376                apr_int64_t wc_id,
377                const char *dir_abspath,
378                const char *name,
379                const svn_wc_entry_t *parent_entry,
380                apr_pool_t *result_pool,
381                apr_pool_t *scratch_pool)
382 {
383   svn_node_kind_t kind;
384   svn_wc__db_status_t status;
385   svn_wc__db_lock_t *lock;
386   const char *repos_relpath;
387   const svn_checksum_t *checksum;
388   svn_filesize_t translated_size;
389   svn_wc_entry_t *entry = alloc_entry(result_pool);
390   const char *entry_abspath;
391   const char *original_repos_relpath;
392   const char *original_root_url;
393   svn_boolean_t conflicted;
394   svn_boolean_t have_base;
395   svn_boolean_t have_more_work;
396
397   entry->name = name;
398
399   entry_abspath = svn_dirent_join(dir_abspath, entry->name, scratch_pool);
400
401   SVN_ERR(svn_wc__db_read_info(
402             &status,
403             &kind,
404             &entry->revision,
405             &repos_relpath,
406             &entry->repos,
407             &entry->uuid,
408             &entry->cmt_rev,
409             &entry->cmt_date,
410             &entry->cmt_author,
411             &entry->depth,
412             &checksum,
413             NULL,
414             &original_repos_relpath,
415             &original_root_url,
416             NULL,
417             &entry->copyfrom_rev,
418             &lock,
419             &translated_size,
420             &entry->text_time,
421             &entry->changelist,
422             &conflicted,
423             NULL /* op_root */,
424             &entry->has_props /* have_props */,
425             &entry->has_prop_mods /* props_mod */,
426             &have_base,
427             &have_more_work,
428             NULL /* have_work */,
429             db,
430             entry_abspath,
431             result_pool,
432             scratch_pool));
433
434   if (entry->has_prop_mods)
435     entry->has_props = TRUE;
436
437   if (strcmp(entry->name, SVN_WC_ENTRY_THIS_DIR) == 0)
438     {
439       /* get the tree conflict data. */
440       apr_hash_t *tree_conflicts = NULL;
441       const apr_array_header_t *conflict_victims;
442       int k;
443
444       SVN_ERR(svn_wc__db_read_conflict_victims(&conflict_victims, db,
445                                                dir_abspath,
446                                                scratch_pool,
447                                                scratch_pool));
448
449       for (k = 0; k < conflict_victims->nelts; k++)
450         {
451           int j;
452           const apr_array_header_t *child_conflicts;
453           const char *child_name;
454           const char *child_abspath;
455
456           child_name = APR_ARRAY_IDX(conflict_victims, k, const char *);
457           child_abspath = svn_dirent_join(dir_abspath, child_name,
458                                           scratch_pool);
459
460           SVN_ERR(svn_wc__read_conflicts(&child_conflicts,
461                                          db, child_abspath,
462                                          FALSE /* create tempfiles */,
463                                          scratch_pool, scratch_pool));
464
465           for (j = 0; j < child_conflicts->nelts; j++)
466             {
467               const svn_wc_conflict_description2_t *conflict =
468                 APR_ARRAY_IDX(child_conflicts, j,
469                               svn_wc_conflict_description2_t *);
470
471               if (conflict->kind == svn_wc_conflict_kind_tree)
472                 {
473                   if (!tree_conflicts)
474                     tree_conflicts = apr_hash_make(scratch_pool);
475                   svn_hash_sets(tree_conflicts, child_name, conflict);
476                 }
477             }
478         }
479
480       if (tree_conflicts)
481         {
482           SVN_ERR(write_tree_conflicts(&entry->tree_conflict_data,
483                                        tree_conflicts, result_pool));
484         }
485     }
486
487   if (status == svn_wc__db_status_normal
488       || status == svn_wc__db_status_incomplete)
489     {
490       /* Plain old BASE node.  */
491       entry->schedule = svn_wc_schedule_normal;
492
493       /* Grab inherited repository information, if necessary. */
494       if (repos_relpath == NULL)
495         {
496           SVN_ERR(svn_wc__db_scan_base_repos(&repos_relpath,
497                                              &entry->repos,
498                                              &entry->uuid,
499                                              db,
500                                              entry_abspath,
501                                              result_pool,
502                                              scratch_pool));
503         }
504
505       entry->incomplete = (status == svn_wc__db_status_incomplete);
506     }
507   else if (status == svn_wc__db_status_deleted)
508     {
509       svn_node_kind_t path_kind;
510
511       /* ### we don't have to worry about moves, so this is a delete. */
512       entry->schedule = svn_wc_schedule_delete;
513
514       /* If there are multiple working layers or no BASE layer, then
515          this is a WORKING delete or WORKING not-present. */
516       if (have_more_work || !have_base)
517         entry->copied = TRUE;
518       else if (have_base && !have_more_work)
519         entry->copied = FALSE;
520       else
521         {
522           const char *work_del_abspath;
523           SVN_ERR(svn_wc__db_scan_deletion(NULL, NULL,
524                                            &work_del_abspath, NULL,
525                                            db, entry_abspath,
526                                            scratch_pool, scratch_pool));
527
528           if (work_del_abspath)
529             entry->copied = TRUE;
530         }
531
532       /* If there is still a directory on-disk we keep it, if not it is
533          already deleted. Simple, isn't it?
534
535          Before single-db we had to keep the administative area alive until
536          after the commit really deletes it. Setting keep alive stopped the
537          commit processing from deleting the directory. We don't delete it
538          any more, so all we have to do is provide some 'sane' value.
539        */
540       SVN_ERR(svn_io_check_path(entry_abspath, &path_kind, scratch_pool));
541       entry->keep_local = (path_kind == svn_node_dir);
542     }
543   else if (status == svn_wc__db_status_added)
544     {
545       svn_wc__db_status_t work_status;
546       const char *op_root_abspath;
547       const char *scanned_original_relpath;
548       svn_revnum_t original_revision;
549
550       /* For child nodes, pick up the parent's revision.  */
551       if (*entry->name != '\0')
552         {
553           assert(parent_entry != NULL);
554           assert(entry->revision == SVN_INVALID_REVNUM);
555
556           entry->revision = parent_entry->revision;
557         }
558
559       if (have_base)
560         {
561           svn_wc__db_status_t base_status;
562
563           /* ENTRY->REVISION is overloaded. When a node is schedule-add
564              or -replace, then REVISION refers to the BASE node's revision
565              that is being overwritten. We need to fetch it now.  */
566           SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL,
567                                            &entry->revision,
568                                            NULL, NULL, NULL,
569                                            NULL, NULL, NULL,
570                                            NULL, NULL, NULL,
571                                            NULL, NULL, NULL, NULL,
572                                            db, entry_abspath,
573                                            scratch_pool,
574                                            scratch_pool));
575
576           if (base_status == svn_wc__db_status_not_present)
577             {
578               /* The underlying node is DELETED in this revision.  */
579               entry->deleted = TRUE;
580
581               /* This is an add since there isn't a node to replace.  */
582               entry->schedule = svn_wc_schedule_add;
583             }
584           else
585             entry->schedule = svn_wc_schedule_replace;
586         }
587       else
588         {
589           /* There is NO 'not-present' BASE_NODE for this node.
590              Therefore, we are looking at some kind of add/copy
591              rather than a replace.  */
592
593           /* ### if this looks like a plain old add, then rev=0.  */
594           if (!SVN_IS_VALID_REVNUM(entry->copyfrom_rev)
595               && !SVN_IS_VALID_REVNUM(entry->cmt_rev))
596             entry->revision = 0;
597
598           entry->schedule = svn_wc_schedule_add;
599         }
600
601       /* If we don't have "real" data from the entry (obstruction),
602          then we cannot begin a scan for data. The original node may
603          have important data. Set up stuff to kill that idea off,
604          and finish up this entry.  */
605         {
606           SVN_ERR(svn_wc__db_scan_addition(&work_status,
607                                            &op_root_abspath,
608                                            &repos_relpath,
609                                            &entry->repos,
610                                            &entry->uuid,
611                                            &scanned_original_relpath,
612                                            NULL, NULL, /* original_root|uuid */
613                                            &original_revision,
614                                            db,
615                                            entry_abspath,
616                                            result_pool, scratch_pool));
617
618           /* In wc.db we want to keep the valid revision of the not-present
619              BASE_REV, but when we used entries we set the revision to 0
620              when adding a new node over a not present base node. */
621           if (work_status == svn_wc__db_status_added
622               && entry->deleted)
623             entry->revision = 0;
624         }
625
626       if (!SVN_IS_VALID_REVNUM(entry->cmt_rev)
627           && scanned_original_relpath == NULL)
628         {
629           /* There is NOT a last-changed revision (last-changed date and
630              author may be unknown, but we can always check the rev).
631              The absence of a revision implies this node was added WITHOUT
632              any history. Avoid the COPIED checks in the else block.  */
633           /* ### scan_addition may need to be updated to avoid returning
634              ### status_copied in this case.  */
635         }
636       /* For backwards-compatiblity purposes we treat moves just like
637        * regular copies. */
638       else if (work_status == svn_wc__db_status_copied ||
639                work_status == svn_wc__db_status_moved_here)
640         {
641           entry->copied = TRUE;
642
643           /* If this is a child of a copied subtree, then it should be
644              schedule_normal.  */
645           if (original_repos_relpath == NULL)
646             {
647               /* ### what if there is a BASE node under there? */
648               entry->schedule = svn_wc_schedule_normal;
649             }
650
651           /* Copied nodes need to mirror their copyfrom_rev, if they
652              don't have a revision of their own already. */
653           if (!SVN_IS_VALID_REVNUM(entry->revision)
654               || entry->revision == 0 /* added */)
655             entry->revision = original_revision;
656         }
657
658       /* Does this node have copyfrom_* information?  */
659       if (scanned_original_relpath != NULL)
660         {
661           svn_boolean_t is_copied_child;
662           svn_boolean_t is_mixed_rev = FALSE;
663
664           SVN_ERR_ASSERT(work_status == svn_wc__db_status_copied ||
665                          work_status == svn_wc__db_status_moved_here);
666
667           /* If this node inherits copyfrom information from an
668              ancestor node, then it must be a copied child.  */
669           is_copied_child = (original_repos_relpath == NULL);
670
671           /* If this node has copyfrom information on it, then it may
672              be an actual copy-root, or it could be participating in
673              a mixed-revision copied tree. So if we don't already know
674              this is a copied child, then we need to look for this
675              mixed-revision situation.  */
676           if (!is_copied_child)
677             {
678               const char *parent_abspath;
679               svn_error_t *err;
680               const char *parent_repos_relpath;
681               const char *parent_root_url;
682
683               /* When we insert entries into the database, we will
684                  construct additional copyfrom records for mixed-revision
685                  copies. The old entries would simply record the different
686                  revision in the entry->revision field. That is not
687                  available within wc-ng, so additional copies are made
688                  (see the logic inside write_entry()). However, when
689                  reading these back *out* of the database, the additional
690                  copies look like new "Added" nodes rather than a simple
691                  mixed-rev working copy.
692
693                  That would be a behavior change if we did not compensate.
694                  If there is copyfrom information for this node, then the
695                  code below looks at the parent to detect if it *also* has
696                  copyfrom information, and if the copyfrom_url would align
697                  properly. If it *does*, then we omit storing copyfrom_url
698                  and copyfrom_rev (ie. inherit the copyfrom info like a
699                  normal child), and update entry->revision with the
700                  copyfrom_rev in order to (re)create the mixed-rev copied
701                  subtree that was originally presented for storage.  */
702
703               /* Get the copyfrom information from our parent.
704
705                  Note that the parent could be added/copied/moved-here.
706                  There is no way for it to be deleted/moved-away and
707                  have *this* node appear as copied.  */
708               parent_abspath = svn_dirent_dirname(entry_abspath,
709                                                   scratch_pool);
710               err = svn_wc__db_scan_addition(NULL,
711                                              &op_root_abspath,
712                                              NULL, NULL, NULL,
713                                              &parent_repos_relpath,
714                                              &parent_root_url,
715                                              NULL, NULL,
716                                              db, parent_abspath,
717                                              scratch_pool,
718                                              scratch_pool);
719               if (err)
720                 {
721                   if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
722                     return svn_error_trace(err);
723                   svn_error_clear(err);
724                 }
725               else if (parent_root_url != NULL
726                        && strcmp(original_root_url, parent_root_url) == 0)
727                 {
728                   const char *relpath_to_entry = svn_dirent_is_child(
729                     op_root_abspath, entry_abspath, NULL);
730                   const char *entry_repos_relpath = svn_relpath_join(
731                     parent_repos_relpath, relpath_to_entry, scratch_pool);
732
733                   /* The copyfrom repos roots matched.
734
735                      Now we look to see if the copyfrom path of the parent
736                      would align with our own path. If so, then it means
737                      this copyfrom was spontaneously created and inserted
738                      for mixed-rev purposes and can be eliminated without
739                      changing the semantics of a mixed-rev copied subtree.
740
741                      See notes/api-errata/wc003.txt for some additional
742                      detail, and potential issues.  */
743                   if (strcmp(entry_repos_relpath,
744                              original_repos_relpath) == 0)
745                     {
746                       is_copied_child = TRUE;
747                       is_mixed_rev = TRUE;
748                     }
749                 }
750             }
751
752           if (is_copied_child)
753             {
754               /* We won't be settig the  copyfrom_url, yet need to
755                  clear out the copyfrom_rev. Thus, this node becomes a
756                  child of a copied subtree (rather than its own root).  */
757               entry->copyfrom_rev = SVN_INVALID_REVNUM;
758
759               /* Children in a copied subtree are schedule normal
760                  since we don't plan to actually *do* anything with
761                  them. Their operation is implied by ancestors.  */
762               entry->schedule = svn_wc_schedule_normal;
763
764               /* And *finally* we turn this entry into the mixed
765                  revision node that it was intended to be. This
766                  node's revision is taken from the copyfrom record
767                  that we spontaneously constructed.  */
768               if (is_mixed_rev)
769                 entry->revision = original_revision;
770             }
771           else if (original_repos_relpath != NULL)
772             {
773               entry->copyfrom_url =
774                 svn_path_url_add_component2(original_root_url,
775                                             original_repos_relpath,
776                                             result_pool);
777             }
778           else
779             {
780               /* NOTE: if original_repos_relpath == NULL, then the
781                  second call to scan_addition() will not have occurred.
782                  Thus, this use of OP_ROOT_ABSPATH still contains the
783                  original value where we fetched a value for
784                  SCANNED_REPOS_RELPATH.  */
785               const char *relpath_to_entry = svn_dirent_is_child(
786                 op_root_abspath, entry_abspath, NULL);
787               const char *entry_repos_relpath = svn_relpath_join(
788                 scanned_original_relpath, relpath_to_entry, scratch_pool);
789
790               entry->copyfrom_url =
791                 svn_path_url_add_component2(original_root_url,
792                                             entry_repos_relpath,
793                                             result_pool);
794             }
795         }
796     }
797   else if (status == svn_wc__db_status_not_present)
798     {
799       /* ### buh. 'deleted' nodes are actually supposed to be
800          ### schedule "normal" since we aren't going to actually *do*
801          ### anything to this node at commit time.  */
802       entry->schedule = svn_wc_schedule_normal;
803       entry->deleted = TRUE;
804     }
805   else if (status == svn_wc__db_status_server_excluded)
806     {
807       entry->absent = TRUE;
808     }
809   else if (status == svn_wc__db_status_excluded)
810     {
811       entry->schedule = svn_wc_schedule_normal;
812       entry->depth = svn_depth_exclude;
813     }
814   else
815     {
816       /* ### we should have handled all possible status values.  */
817       SVN_ERR_MALFUNCTION();
818     }
819
820   /* ### higher levels want repos information about deleted nodes, even
821      ### tho they are not "part of" a repository any more.  */
822   if (entry->schedule == svn_wc_schedule_delete)
823     {
824       SVN_ERR(get_info_for_deleted(entry,
825                                    &kind,
826                                    &repos_relpath,
827                                    &checksum,
828                                    &lock,
829                                    db, entry_abspath,
830                                    parent_entry,
831                                    have_base, have_more_work,
832                                    result_pool, scratch_pool));
833     }
834
835   /* ### default to the infinite depth if we don't know it. */
836   if (entry->depth == svn_depth_unknown)
837     entry->depth = svn_depth_infinity;
838
839   if (kind == svn_node_dir)
840     entry->kind = svn_node_dir;
841   else if (kind == svn_node_file)
842     entry->kind = svn_node_file;
843   else if (kind == svn_node_symlink)
844     entry->kind = svn_node_file;  /* ### no symlink kind */
845   else
846     entry->kind = svn_node_unknown;
847
848   /* We should always have a REPOS_RELPATH, except for:
849      - deleted nodes
850      - certain obstructed nodes
851      - not-present nodes
852      - absent nodes
853      - excluded nodes
854
855      ### the last three should probably have an "implied" REPOS_RELPATH
856   */
857   SVN_ERR_ASSERT(repos_relpath != NULL
858                  || entry->schedule == svn_wc_schedule_delete
859                  || status == svn_wc__db_status_not_present
860                  || status == svn_wc__db_status_server_excluded
861                  || status == svn_wc__db_status_excluded);
862   if (repos_relpath)
863     entry->url = svn_path_url_add_component2(entry->repos,
864                                              repos_relpath,
865                                              result_pool);
866
867   if (checksum)
868     {
869       /* We got a SHA-1, get the corresponding MD-5. */
870       if (checksum->kind != svn_checksum_md5)
871         SVN_ERR(svn_wc__db_pristine_get_md5(&checksum, db,
872                                             entry_abspath, checksum,
873                                             scratch_pool, scratch_pool));
874
875       SVN_ERR_ASSERT(checksum->kind == svn_checksum_md5);
876       entry->checksum = svn_checksum_to_cstring(checksum, result_pool);
877     }
878
879   if (conflicted)
880     {
881       svn_skel_t *conflict;
882       svn_boolean_t text_conflicted;
883       svn_boolean_t prop_conflicted;
884       SVN_ERR(svn_wc__db_read_conflict(&conflict, db, entry_abspath,
885                                        scratch_pool, scratch_pool));
886
887       SVN_ERR(svn_wc__conflict_read_info(NULL, NULL, &text_conflicted,
888                                          &prop_conflicted, NULL,
889                                          db, dir_abspath, conflict,
890                                          scratch_pool, scratch_pool));
891
892       if (text_conflicted)
893         {
894           const char *my_abspath;
895           const char *their_old_abspath;
896           const char *their_abspath;
897           SVN_ERR(svn_wc__conflict_read_text_conflict(&my_abspath,
898                                                       &their_old_abspath,
899                                                       &their_abspath,
900                                                       db, dir_abspath,
901                                                       conflict, scratch_pool,
902                                                       scratch_pool));
903
904           if (my_abspath)
905             entry->conflict_wrk = svn_dirent_basename(my_abspath, result_pool);
906
907           if (their_old_abspath)
908             entry->conflict_old = svn_dirent_basename(their_old_abspath,
909                                                       result_pool);
910
911           if (their_abspath)
912             entry->conflict_new = svn_dirent_basename(their_abspath,
913                                                       result_pool);
914         }
915
916       if (prop_conflicted)
917         {
918           const char *prej_abspath;
919
920           SVN_ERR(svn_wc__conflict_read_prop_conflict(&prej_abspath, NULL,
921                                                       NULL, NULL, NULL,
922                                                       db, dir_abspath,
923                                                       conflict, scratch_pool,
924                                                       scratch_pool));
925
926           if (prej_abspath)
927             entry->prejfile = svn_dirent_basename(prej_abspath, result_pool);
928         }
929     }
930
931   if (lock)
932     {
933       entry->lock_token = lock->token;
934       entry->lock_owner = lock->owner;
935       entry->lock_comment = lock->comment;
936       entry->lock_creation_date = lock->date;
937     }
938
939   /* Let's check for a file external.  ugh.  */
940   if (status == svn_wc__db_status_normal
941       && kind == svn_node_file)
942     SVN_ERR(check_file_external(entry, db, entry_abspath, dir_abspath,
943                                 result_pool, scratch_pool));
944
945   entry->working_size = translated_size;
946
947   *new_entry = entry;
948
949   return SVN_NO_ERROR;
950 }
951
952 /* Read entries for PATH/LOCAL_ABSPATH from DB. The entries
953    will be allocated in RESULT_POOL, with temporary allocations in
954    SCRATCH_POOL. The entries are returned in RESULT_ENTRIES.  */
955 static svn_error_t *
956 read_entries_new(apr_hash_t **result_entries,
957                  svn_wc__db_t *db,
958                  const char *local_abspath,
959                  apr_pool_t *result_pool,
960                  apr_pool_t *scratch_pool)
961 {
962   apr_hash_t *entries;
963   const apr_array_header_t *children;
964   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
965   int i;
966   const svn_wc_entry_t *parent_entry;
967   apr_int64_t wc_id = 1;  /* ### hacky. should remove.  */
968
969   entries = apr_hash_make(result_pool);
970
971   SVN_ERR(read_one_entry(&parent_entry, db, wc_id, local_abspath,
972                          "" /* name */,
973                          NULL /* parent_entry */,
974                          result_pool, iterpool));
975   svn_hash_sets(entries, "", parent_entry);
976
977   /* Use result_pool so that the child names (used by reference, rather
978      than copied) appear in result_pool.  */
979   SVN_ERR(svn_wc__db_read_children(&children, db,
980                                    local_abspath,
981                                    result_pool, iterpool));
982   for (i = children->nelts; i--; )
983     {
984       const char *name = APR_ARRAY_IDX(children, i, const char *);
985       const svn_wc_entry_t *entry;
986
987       svn_pool_clear(iterpool);
988
989       SVN_ERR(read_one_entry(&entry,
990                              db, wc_id, local_abspath, name, parent_entry,
991                              result_pool, iterpool));
992       svn_hash_sets(entries, entry->name, entry);
993     }
994
995   svn_pool_destroy(iterpool);
996
997   *result_entries = entries;
998
999   return SVN_NO_ERROR;
1000 }
1001
1002
1003 /* Read a pair of entries from wc_db in the directory DIR_ABSPATH. Return
1004    the directory's entry in *PARENT_ENTRY and NAME's entry in *ENTRY. The
1005    two returned pointers will be the same if NAME=="" ("this dir").
1006
1007    The parent entry must exist.
1008
1009    The requested entry MAY exist. If it does not, then NULL will be returned.
1010
1011    The resulting entries are allocated in RESULT_POOL, and all temporary
1012    allocations are made in SCRATCH_POOL.  */
1013 static svn_error_t *
1014 read_entry_pair(const svn_wc_entry_t **parent_entry,
1015                 const svn_wc_entry_t **entry,
1016                 svn_wc__db_t *db,
1017                 const char *dir_abspath,
1018                 const char *name,
1019                 apr_pool_t *result_pool,
1020                 apr_pool_t *scratch_pool)
1021 {
1022   apr_int64_t wc_id = 1;  /* ### hacky. should remove.  */
1023
1024   SVN_ERR(read_one_entry(parent_entry, db, wc_id, dir_abspath,
1025                          "" /* name */,
1026                          NULL /* parent_entry */,
1027                          result_pool, scratch_pool));
1028
1029   /* If we need the entry for "this dir", then return the parent_entry
1030      in both outputs. Otherwise, read the child node.  */
1031   if (*name == '\0')
1032     {
1033       /* If the retrieved node is a FILE, then we have a problem. We asked
1034          for a directory. This implies there is an obstructing, unversioned
1035          directory where a FILE should be. We navigated from the obstructing
1036          subdir up to the parent dir, then returned the FILE found there.
1037
1038          Let's return WC_MISSING cuz the caller thought we had a dir, but
1039          that (versioned subdir) isn't there.  */
1040       if ((*parent_entry)->kind == svn_node_file)
1041         {
1042           *parent_entry = NULL;
1043           return svn_error_createf(SVN_ERR_WC_MISSING, NULL,
1044                                  _("'%s' is not a versioned working copy"),
1045                                  svn_dirent_local_style(dir_abspath,
1046                                                         scratch_pool));
1047         }
1048
1049       *entry = *parent_entry;
1050     }
1051   else
1052     {
1053       const apr_array_header_t *children;
1054       int i;
1055
1056       /* Default to not finding the child.  */
1057       *entry = NULL;
1058
1059       /* Determine whether the parent KNOWS about this child. If it does
1060          not, then we should not attempt to look for it.
1061
1062          For example: the parent doesn't "know" about the child, but the
1063          versioned directory *does* exist on disk. We don't want to look
1064          into that subdir.  */
1065       SVN_ERR(svn_wc__db_read_children(&children, db, dir_abspath,
1066                                        scratch_pool, scratch_pool));
1067       for (i = children->nelts; i--; )
1068         {
1069           const char *child = APR_ARRAY_IDX(children, i, const char *);
1070
1071           if (strcmp(child, name) == 0)
1072             {
1073               svn_error_t *err;
1074
1075               err = read_one_entry(entry,
1076                                    db, wc_id, dir_abspath, name, *parent_entry,
1077                                    result_pool, scratch_pool);
1078               if (err)
1079                 {
1080                   if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
1081                     return svn_error_trace(err);
1082
1083                   /* No problem. Clear the error and leave the default value
1084                      of "missing".  */
1085                   svn_error_clear(err);
1086                 }
1087
1088               /* Found it. No need to keep searching.  */
1089               break;
1090             }
1091         }
1092       /* if the loop ends without finding a child, then we have the default
1093          ENTRY value of NULL.  */
1094     }
1095
1096   return SVN_NO_ERROR;
1097 }
1098
1099
1100 /* */
1101 static svn_error_t *
1102 read_entries(apr_hash_t **entries,
1103              svn_wc__db_t *db,
1104              const char *wcroot_abspath,
1105              apr_pool_t *result_pool,
1106              apr_pool_t *scratch_pool)
1107 {
1108   int wc_format;
1109
1110   SVN_ERR(svn_wc__db_temp_get_format(&wc_format, db, wcroot_abspath,
1111                                      scratch_pool));
1112
1113   if (wc_format < SVN_WC__WC_NG_VERSION)
1114     return svn_error_trace(svn_wc__read_entries_old(entries,
1115                                                     wcroot_abspath,
1116                                                     result_pool,
1117                                                     scratch_pool));
1118
1119   return svn_error_trace(read_entries_new(entries, db, wcroot_abspath,
1120                                           result_pool, scratch_pool));
1121 }
1122
1123
1124 /* For a given LOCAL_ABSPATH, using DB, set *ADM_ABSPATH to the directory in
1125    which the entry information is located, and *ENTRY_NAME to the entry name
1126    to access that entry.
1127
1128    KIND is as in svn_wc__get_entry().
1129
1130    Return the results in RESULT_POOL and use SCRATCH_POOL for temporary
1131    allocations. */
1132 static svn_error_t *
1133 get_entry_access_info(const char **adm_abspath,
1134                       const char **entry_name,
1135                       svn_wc__db_t *db,
1136                       const char *local_abspath,
1137                       svn_node_kind_t kind,
1138                       apr_pool_t *result_pool,
1139                       apr_pool_t *scratch_pool)
1140 {
1141   svn_wc_adm_access_t *adm_access;
1142   svn_boolean_t read_from_subdir = FALSE;
1143
1144   /* If the caller didn't know the node kind, then stat the path. Maybe
1145      it is really there, and we can speed up the steps below.  */
1146   if (kind == svn_node_unknown)
1147     {
1148       svn_node_kind_t on_disk;
1149
1150       /* Do we already have an access baton for LOCAL_ABSPATH?  */
1151       adm_access = svn_wc__adm_retrieve_internal2(db, local_abspath,
1152                                                   scratch_pool);
1153       if (adm_access)
1154         {
1155           /* Sweet. The node is a directory.  */
1156           on_disk = svn_node_dir;
1157         }
1158       else
1159         {
1160           svn_boolean_t special;
1161
1162           /* What's on disk?  */
1163           SVN_ERR(svn_io_check_special_path(local_abspath, &on_disk, &special,
1164                                             scratch_pool));
1165         }
1166
1167       if (on_disk != svn_node_dir)
1168         {
1169           /* If this is *anything* besides a directory (FILE, NONE, or
1170              UNKNOWN), then we cannot treat it as a versioned directory
1171              containing entries to read. Leave READ_FROM_SUBDIR as FALSE,
1172              so that the parent will be examined.
1173
1174              For NONE and UNKNOWN, it may be that metadata exists for the
1175              node, even though on-disk is unhelpful.
1176
1177              If NEED_PARENT_STUB is TRUE, and the entry is not a DIRECTORY,
1178              then we'll error.
1179
1180              If NEED_PARENT_STUB if FALSE, and we successfully read a stub,
1181              then this on-disk node is obstructing the read.  */
1182         }
1183       else
1184         {
1185           /* We found a directory for this UNKNOWN node. Determine whether
1186              we need to read inside it.  */
1187           read_from_subdir = TRUE;
1188         }
1189     }
1190   else if (kind == svn_node_dir)
1191     {
1192       read_from_subdir = TRUE;
1193     }
1194
1195   if (read_from_subdir)
1196     {
1197       /* KIND must be a DIR or UNKNOWN (and we found a subdir). We want
1198          the "real" data, so treat LOCAL_ABSPATH as a versioned directory.  */
1199       *adm_abspath = apr_pstrdup(result_pool, local_abspath);
1200       *entry_name = "";
1201     }
1202   else
1203     {
1204       /* FILE node needs to read the parent directory. Or a DIR node
1205          needs to read from the parent to get at the stub entry. Or this
1206          is an UNKNOWN node, and we need to examine the parent.  */
1207       svn_dirent_split(adm_abspath, entry_name, local_abspath, result_pool);
1208     }
1209
1210   return SVN_NO_ERROR;
1211 }
1212
1213
1214 svn_error_t *
1215 svn_wc__get_entry(const svn_wc_entry_t **entry,
1216                   svn_wc__db_t *db,
1217                   const char *local_abspath,
1218                   svn_boolean_t allow_unversioned,
1219                   svn_node_kind_t kind,
1220                   apr_pool_t *result_pool,
1221                   apr_pool_t *scratch_pool)
1222 {
1223   const char *dir_abspath;
1224   const char *entry_name;
1225
1226   SVN_ERR(get_entry_access_info(&dir_abspath, &entry_name, db, local_abspath,
1227                                 kind, scratch_pool, scratch_pool));
1228
1229     {
1230       const svn_wc_entry_t *parent_entry;
1231       svn_error_t *err;
1232
1233       /* NOTE: if KIND is UNKNOWN and we decided to examine the *parent*
1234          directory, then it is possible we moved out of the working copy.
1235          If the on-disk node is a DIR, and we asked for a stub, then we
1236          obviously can't provide that (parent has no info). If the on-disk
1237          node is a FILE/NONE/UNKNOWN, then it is obstructing the real
1238          LOCAL_ABSPATH (or it was never a versioned item). In all these
1239          cases, the read_entries() will (properly) throw an error.
1240
1241          NOTE: if KIND is a DIR and we asked for the real data, but it is
1242          obstructed on-disk by some other node kind (NONE, FILE, UNKNOWN),
1243          then this will throw an error.  */
1244
1245       err = read_entry_pair(&parent_entry, entry,
1246                             db, dir_abspath, entry_name,
1247                             result_pool, scratch_pool);
1248       if (err)
1249         {
1250           if (err->apr_err != SVN_ERR_WC_MISSING || kind != svn_node_unknown
1251               || *entry_name != '\0')
1252             return svn_error_trace(err);
1253           svn_error_clear(err);
1254
1255           /* The caller didn't know the node type, we saw a directory there,
1256              we attempted to read IN that directory, and then wc_db reports
1257              that it is NOT a working copy directory. It is possible that
1258              one of two things has happened:
1259
1260              1) a directory is obstructing a file in the parent
1261              2) the (versioned) directory's contents have been removed
1262
1263              Let's assume situation (1); if that is true, then we can just
1264              return the newly-found data.
1265
1266              If we assumed (2), then a valid result still won't help us
1267              since the caller asked for the actual contents, not the stub
1268              (which is why we read *into* the directory). However, if we
1269              assume (1) and get back a stub, then we have verified a
1270              missing, versioned directory, and can return an error
1271              describing that.
1272
1273              Redo the fetch, but "insist" we are trying to find a file.
1274              This will read from the parent directory of the "file".  */
1275           err = svn_wc__get_entry(entry, db, local_abspath, allow_unversioned,
1276                                   svn_node_file, result_pool, scratch_pool);
1277           if (err == SVN_NO_ERROR)
1278             return SVN_NO_ERROR;
1279           if (err->apr_err != SVN_ERR_NODE_UNEXPECTED_KIND)
1280             return svn_error_trace(err);
1281           svn_error_clear(err);
1282
1283           /* We asked for a FILE, but the node found is a DIR. Thus, we
1284              are looking at a stub. Originally, we tried to read into the
1285              subdir because NEED_PARENT_STUB is FALSE. The stub we just
1286              read is not going to work for the caller, so inform them of
1287              the missing subdirectory.  */
1288           SVN_ERR_ASSERT(*entry != NULL && (*entry)->kind == svn_node_dir);
1289           return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
1290                                  _("Admin area of '%s' is missing"),
1291                                  svn_dirent_local_style(local_abspath,
1292                                                         scratch_pool));
1293         }
1294     }
1295
1296   if (*entry == NULL)
1297     {
1298       if (allow_unversioned)
1299         return SVN_NO_ERROR;
1300       return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
1301                                _("'%s' is not under version control"),
1302                                svn_dirent_local_style(local_abspath,
1303                                                       scratch_pool));
1304     }
1305
1306   /* The caller had the wrong information.  */
1307   if ((kind == svn_node_file && (*entry)->kind != svn_node_file)
1308       || (kind == svn_node_dir && (*entry)->kind != svn_node_dir))
1309     return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
1310                              _("'%s' is not of the right kind"),
1311                              svn_dirent_local_style(local_abspath,
1312                                                     scratch_pool));
1313
1314   return SVN_NO_ERROR;
1315 }
1316
1317 /* TODO ### Rewrite doc string to mention ENTRIES_ALL; not ADM_ACCESS.
1318
1319    Prune the deleted entries from the cached entries in ADM_ACCESS, and
1320    return that collection in *ENTRIES_PRUNED.  SCRATCH_POOL is used for local,
1321    short term, memory allocation, RESULT_POOL for permanent stuff.  */
1322 static svn_error_t *
1323 prune_deleted(apr_hash_t **entries_pruned,
1324               apr_hash_t *entries_all,
1325               apr_pool_t *result_pool,
1326               apr_pool_t *scratch_pool)
1327 {
1328   apr_hash_index_t *hi;
1329
1330   if (!entries_all)
1331     {
1332       *entries_pruned = NULL;
1333       return SVN_NO_ERROR;
1334     }
1335
1336   /* I think it will be common for there to be no deleted entries, so
1337      it is worth checking for that case as we can optimise it. */
1338   for (hi = apr_hash_first(scratch_pool, entries_all);
1339        hi;
1340        hi = apr_hash_next(hi))
1341     {
1342       svn_boolean_t hidden;
1343
1344       SVN_ERR(svn_wc__entry_is_hidden(&hidden,
1345                                       svn__apr_hash_index_val(hi)));
1346       if (hidden)
1347         break;
1348     }
1349
1350   if (! hi)
1351     {
1352       /* There are no deleted entries, so we can use the full hash */
1353       *entries_pruned = entries_all;
1354       return SVN_NO_ERROR;
1355     }
1356
1357   /* Construct pruned hash without deleted entries */
1358   *entries_pruned = apr_hash_make(result_pool);
1359   for (hi = apr_hash_first(scratch_pool, entries_all);
1360        hi;
1361        hi = apr_hash_next(hi))
1362     {
1363       const void *key = svn__apr_hash_index_key(hi);
1364       const svn_wc_entry_t *entry = svn__apr_hash_index_val(hi);
1365       svn_boolean_t hidden;
1366
1367       SVN_ERR(svn_wc__entry_is_hidden(&hidden, entry));
1368       if (!hidden)
1369         svn_hash_sets(*entries_pruned, key, entry);
1370     }
1371
1372   return SVN_NO_ERROR;
1373 }
1374
1375 struct entries_read_baton_t
1376 {
1377   apr_hash_t **new_entries;
1378   svn_wc__db_t *db;
1379   const char *local_abspath;
1380   apr_pool_t *result_pool;
1381 };
1382
1383 static svn_error_t *
1384 entries_read_txn(void *baton, svn_sqlite__db_t *db, apr_pool_t *scratch_pool)
1385 {
1386   struct entries_read_baton_t *erb = baton;
1387
1388   SVN_ERR(read_entries(erb->new_entries, erb->db, erb->local_abspath,
1389                        erb->result_pool, scratch_pool));
1390
1391   return NULL;
1392 }
1393
1394 svn_error_t *
1395 svn_wc__entries_read_internal(apr_hash_t **entries,
1396                               svn_wc_adm_access_t *adm_access,
1397                               svn_boolean_t show_hidden,
1398                               apr_pool_t *pool)
1399 {
1400   apr_hash_t *new_entries;
1401
1402   new_entries = svn_wc__adm_access_entries(adm_access);
1403   if (! new_entries)
1404     {
1405       svn_wc__db_t *db = svn_wc__adm_get_db(adm_access);
1406       const char *local_abspath = svn_wc__adm_access_abspath(adm_access);
1407       apr_pool_t *result_pool = svn_wc__adm_access_pool_internal(adm_access);
1408       svn_sqlite__db_t *sdb;
1409       struct entries_read_baton_t erb;
1410
1411       /* ### Use the borrow DB api to handle all calls in a single read
1412          ### transaction. This api is used extensively in our test suite
1413          ### via the entries-read application. */
1414
1415       SVN_ERR(svn_wc__db_temp_borrow_sdb(&sdb, db, local_abspath, pool));
1416
1417       erb.db = db;
1418       erb.local_abspath = local_abspath;
1419       erb.new_entries = &new_entries;
1420       erb.result_pool = result_pool;
1421
1422       SVN_ERR(svn_sqlite__with_lock(sdb, entries_read_txn, &erb, pool));
1423
1424       svn_wc__adm_access_set_entries(adm_access, new_entries);
1425     }
1426
1427   if (show_hidden)
1428     *entries = new_entries;
1429   else
1430     SVN_ERR(prune_deleted(entries, new_entries,
1431                           svn_wc__adm_access_pool_internal(adm_access),
1432                           pool));
1433
1434   return SVN_NO_ERROR;
1435 }
1436
1437 svn_error_t *
1438 svn_wc_entries_read(apr_hash_t **entries,
1439                     svn_wc_adm_access_t *adm_access,
1440                     svn_boolean_t show_hidden,
1441                     apr_pool_t *pool)
1442 {
1443   return svn_error_trace(svn_wc__entries_read_internal(entries, adm_access,
1444                                                        show_hidden, pool));
1445 }
1446
1447 /* No transaction required: called from write_entry which is itself
1448    transaction-wrapped. */
1449 static svn_error_t *
1450 insert_node(svn_sqlite__db_t *sdb,
1451             const db_node_t *node,
1452             apr_pool_t *scratch_pool)
1453 {
1454   svn_sqlite__stmt_t *stmt;
1455
1456   SVN_ERR_ASSERT(node->op_depth > 0 || node->repos_relpath);
1457
1458   SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_NODE));
1459   SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnnnsnrisnnni",
1460                             node->wc_id,
1461                             node->local_relpath,
1462                             node->op_depth,
1463                             node->parent_relpath,
1464                             /* Setting depth for files? */
1465                             svn_depth_to_word(node->depth),
1466                             node->changed_rev,
1467                             node->changed_date,
1468                             node->changed_author,
1469                             node->recorded_time));
1470
1471   if (node->repos_relpath)
1472     {
1473       SVN_ERR(svn_sqlite__bind_int64(stmt, 5,
1474                                      node->repos_id));
1475       SVN_ERR(svn_sqlite__bind_text(stmt, 6,
1476                                     node->repos_relpath));
1477       SVN_ERR(svn_sqlite__bind_revnum(stmt, 7, node->revision));
1478     }
1479
1480   if (node->presence == svn_wc__db_status_normal)
1481     SVN_ERR(svn_sqlite__bind_text(stmt, 8, "normal"));
1482   else if (node->presence == svn_wc__db_status_not_present)
1483     SVN_ERR(svn_sqlite__bind_text(stmt, 8, "not-present"));
1484   else if (node->presence == svn_wc__db_status_base_deleted)
1485     SVN_ERR(svn_sqlite__bind_text(stmt, 8, "base-deleted"));
1486   else if (node->presence == svn_wc__db_status_incomplete)
1487     SVN_ERR(svn_sqlite__bind_text(stmt, 8, "incomplete"));
1488   else if (node->presence == svn_wc__db_status_excluded)
1489     SVN_ERR(svn_sqlite__bind_text(stmt, 8, "excluded"));
1490   else if (node->presence == svn_wc__db_status_server_excluded)
1491     SVN_ERR(svn_sqlite__bind_text(stmt, 8, "server-excluded"));
1492
1493   if (node->kind == svn_node_none)
1494     SVN_ERR(svn_sqlite__bind_text(stmt, 10, "unknown"));
1495   else
1496     SVN_ERR(svn_sqlite__bind_text(stmt, 10,
1497                                   svn_node_kind_to_word(node->kind)));
1498
1499   if (node->kind == svn_node_file)
1500     {
1501       if (!node->checksum
1502           && node->op_depth == 0
1503           && node->presence != svn_wc__db_status_not_present
1504           && node->presence != svn_wc__db_status_excluded
1505           && node->presence != svn_wc__db_status_server_excluded)
1506         return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
1507                                  _("The file '%s' has no checksum"),
1508                                  svn_dirent_local_style(node->local_relpath,
1509                                                         scratch_pool));
1510
1511       SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, node->checksum,
1512                                         scratch_pool));
1513     }
1514
1515   if (node->properties) /* ### Never set, props done later */
1516     SVN_ERR(svn_sqlite__bind_properties(stmt, 15, node->properties,
1517                                         scratch_pool));
1518
1519   if (node->recorded_size != SVN_INVALID_FILESIZE)
1520     SVN_ERR(svn_sqlite__bind_int64(stmt, 16, node->recorded_size));
1521
1522   if (node->file_external)
1523     SVN_ERR(svn_sqlite__bind_int(stmt, 20, 1));
1524
1525   if (node->inherited_props)
1526     SVN_ERR(svn_sqlite__bind_iprops(stmt, 23, node->inherited_props,
1527                                     scratch_pool));
1528
1529   SVN_ERR(svn_sqlite__insert(NULL, stmt));
1530
1531   return SVN_NO_ERROR;
1532 }
1533
1534
1535 /* */
1536 static svn_error_t *
1537 insert_actual_node(svn_sqlite__db_t *sdb,
1538                    svn_wc__db_t *db,
1539                    const char *wri_abspath,
1540                    const db_actual_node_t *actual_node,
1541                    apr_pool_t *scratch_pool)
1542 {
1543   svn_sqlite__stmt_t *stmt;
1544   svn_skel_t *conflict_data = NULL;
1545
1546   SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_ACTUAL_NODE));
1547
1548   SVN_ERR(svn_sqlite__bind_int64(stmt, 1, actual_node->wc_id));
1549   SVN_ERR(svn_sqlite__bind_text(stmt, 2, actual_node->local_relpath));
1550   SVN_ERR(svn_sqlite__bind_text(stmt, 3, actual_node->parent_relpath));
1551
1552   if (actual_node->properties)
1553     SVN_ERR(svn_sqlite__bind_properties(stmt, 4, actual_node->properties,
1554                                         scratch_pool));
1555
1556   if (actual_node->changelist)
1557     SVN_ERR(svn_sqlite__bind_text(stmt, 5, actual_node->changelist));
1558
1559   SVN_ERR(svn_wc__upgrade_conflict_skel_from_raw(
1560                                 &conflict_data,
1561                                 db, wri_abspath,
1562                                 actual_node->local_relpath,
1563                                 actual_node->conflict_old,
1564                                 actual_node->conflict_working,
1565                                 actual_node->conflict_new,
1566                                 actual_node->prop_reject,
1567                                 actual_node->tree_conflict_data,
1568                                 actual_node->tree_conflict_data
1569                                     ? strlen(actual_node->tree_conflict_data)
1570                                     : 0,
1571                                 scratch_pool, scratch_pool));
1572
1573   if (conflict_data)
1574     {
1575       svn_stringbuf_t *data = svn_skel__unparse(conflict_data, scratch_pool);
1576
1577       SVN_ERR(svn_sqlite__bind_blob(stmt, 6, data->data, data->len));
1578     }
1579
1580   /* Execute and reset the insert clause. */
1581   return svn_error_trace(svn_sqlite__insert(NULL, stmt));
1582 }
1583
1584 static svn_boolean_t
1585 is_switched(db_node_t *parent,
1586             db_node_t *child,
1587             apr_pool_t *scratch_pool)
1588 {
1589   if (parent && child)
1590     {
1591       if (parent->repos_id != child->repos_id)
1592         return TRUE;
1593
1594       if (parent->repos_relpath && child->repos_relpath)
1595         {
1596           const char *unswitched
1597             = svn_relpath_join(parent->repos_relpath,
1598                                svn_relpath_basename(child->local_relpath,
1599                                                     scratch_pool),
1600                                scratch_pool);
1601           if (strcmp(unswitched, child->repos_relpath))
1602             return TRUE;
1603         }
1604     }
1605
1606   return FALSE;
1607 }
1608
1609 struct write_baton {
1610   db_node_t *base;
1611   db_node_t *work;
1612   db_node_t *below_work;
1613   apr_hash_t *tree_conflicts;
1614 };
1615
1616 #define WRITE_ENTRY_ASSERT(expr) \
1617   if (!(expr)) \
1618     return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL,  \
1619                              _("Unable to upgrade '%s' at line %d"),    \
1620                              svn_dirent_local_style( \
1621                                svn_dirent_join(root_abspath, \
1622                                                local_relpath,           \
1623                                                scratch_pool),           \
1624                                scratch_pool), __LINE__)
1625
1626 /* Write the information for ENTRY to WC_DB.  The WC_ID, REPOS_ID and
1627    REPOS_ROOT will all be used for writing ENTRY.
1628    ### transitioning from straight sql to using the wc_db APIs.  For the
1629    ### time being, we'll need both parameters. */
1630 static svn_error_t *
1631 write_entry(struct write_baton **entry_node,
1632             const struct write_baton *parent_node,
1633             svn_wc__db_t *db,
1634             svn_sqlite__db_t *sdb,
1635             apr_int64_t wc_id,
1636             apr_int64_t repos_id,
1637             const svn_wc_entry_t *entry,
1638             const svn_wc__text_base_info_t *text_base_info,
1639             const char *local_relpath,
1640             const char *tmp_entry_abspath,
1641             const char *root_abspath,
1642             const svn_wc_entry_t *this_dir,
1643             svn_boolean_t create_locks,
1644             apr_pool_t *result_pool,
1645             apr_pool_t *scratch_pool)
1646 {
1647   db_node_t *base_node = NULL;
1648   db_node_t *working_node = NULL, *below_working_node = NULL;
1649   db_actual_node_t *actual_node = NULL;
1650   const char *parent_relpath;
1651   apr_hash_t *tree_conflicts;
1652
1653   if (*local_relpath == '\0')
1654     parent_relpath = NULL;
1655   else
1656     parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
1657
1658   /* This is how it should work, it doesn't work like this yet because
1659      we need proper op_depth to layer the working nodes.
1660
1661      Using "svn add", "svn rm", "svn cp" only files can be replaced
1662      pre-wcng; directories can only be normal, deleted or added.
1663      Files cannot be replaced within a deleted directory, so replaced
1664      files can only exist in a normal directory, or a directory that
1665      is added+copied.  In a normal directory a replaced file needs a
1666      base node and a working node, in an added+copied directory a
1667      replaced file needs two working nodes at different op-depths.
1668
1669      With just the above operations the conversion for files and
1670      directories is straightforward:
1671
1672            pre-wcng                             wcng
1673      parent         child                 parent     child
1674
1675      normal         normal                base       base
1676      add+copied     normal+copied         work       work
1677      normal+copied  normal+copied         work       work
1678      normal         delete                base       base+work
1679      delete         delete                base+work  base+work
1680      add+copied     delete                work       work
1681      normal         add                   base       work
1682      add            add                   work       work
1683      add+copied     add                   work       work
1684      normal         add+copied            base       work
1685      add            add+copied            work       work
1686      add+copied     add+copied            work       work
1687      normal         replace               base       base+work
1688      add+copied     replace               work       work+work
1689      normal         replace+copied        base       base+work
1690      add+copied     replace+copied        work       work+work
1691
1692      However "svn merge" make this more complicated.  The pre-wcng
1693      "svn merge" is capable of replacing a directory, that is it can
1694      mark the whole tree deleted, and then copy another tree on top.
1695      The entries then represent the replacing tree overlayed on the
1696      deleted tree.
1697
1698        original       replace          schedule in
1699        tree           tree             combined tree
1700
1701        A              A                replace+copied
1702        A/f                             delete+copied
1703        A/g            A/g              replace+copied
1704                       A/h              add+copied
1705        A/B            A/B              replace+copied
1706        A/B/f                           delete+copied
1707        A/B/g          A/B/g            replace+copied
1708                       A/B/h            add+copied
1709        A/C                             delete+copied
1710        A/C/f                           delete+copied
1711                       A/D              add+copied
1712                       A/D/f            add+copied
1713
1714      The original tree could be normal tree, or an add+copied tree.
1715      Committing such a merge generally worked, but making further tree
1716      modifications before commit sometimes failed.
1717
1718      The root of the replace is handled like the file replace:
1719
1720            pre-wcng                             wcng
1721      parent         child                 parent     child
1722
1723      normal         replace+copied        base       base+work
1724      add+copied     replace+copied        work       work+work
1725
1726      although obviously the node is a directory rather then a file.
1727      There are then more conversion states where the parent is
1728      replaced.
1729
1730            pre-wcng                                wcng
1731      parent           child              parent            child
1732
1733      replace+copied   add                [base|work]+work  work
1734      replace+copied   add+copied         [base|work]+work  work
1735      replace+copied   delete+copied      [base|work]+work  [base|work]+work
1736      delete+copied    delete+copied      [base|work]+work  [base|work]+work
1737      replace+copied   replace+copied     [base|work]+work  [base|work]+work
1738   */
1739
1740   WRITE_ENTRY_ASSERT(parent_node || entry->schedule == svn_wc_schedule_normal);
1741
1742   WRITE_ENTRY_ASSERT(!parent_node || parent_node->base
1743                      || parent_node->below_work || parent_node->work);
1744
1745   switch (entry->schedule)
1746     {
1747       case svn_wc_schedule_normal:
1748         if (entry->copied ||
1749             (entry->depth == svn_depth_exclude
1750              && parent_node && !parent_node->base && parent_node->work))
1751           working_node = MAYBE_ALLOC(working_node, result_pool);
1752         else
1753           base_node = MAYBE_ALLOC(base_node, result_pool);
1754         break;
1755
1756       case svn_wc_schedule_add:
1757         working_node = MAYBE_ALLOC(working_node, result_pool);
1758         if (entry->deleted)
1759           {
1760             if (parent_node->base)
1761               base_node = MAYBE_ALLOC(base_node, result_pool);
1762             else
1763               below_working_node = MAYBE_ALLOC(below_working_node, result_pool);
1764           }
1765         break;
1766
1767       case svn_wc_schedule_delete:
1768         working_node = MAYBE_ALLOC(working_node, result_pool);
1769         if (parent_node->base)
1770           base_node = MAYBE_ALLOC(base_node, result_pool);
1771         if (parent_node->work)
1772           below_working_node = MAYBE_ALLOC(below_working_node, result_pool);
1773         break;
1774
1775       case svn_wc_schedule_replace:
1776         working_node = MAYBE_ALLOC(working_node, result_pool);
1777         if (parent_node->base)
1778           base_node = MAYBE_ALLOC(base_node, result_pool);
1779         else
1780           below_working_node = MAYBE_ALLOC(below_working_node, result_pool);
1781         break;
1782     }
1783
1784   /* Something deleted in this revision means there should always be a
1785      BASE node to indicate the not-present node.  */
1786   if (entry->deleted)
1787     {
1788       WRITE_ENTRY_ASSERT(base_node || below_working_node);
1789       WRITE_ENTRY_ASSERT(!entry->incomplete);
1790       if (base_node)
1791         base_node->presence = svn_wc__db_status_not_present;
1792       else
1793         below_working_node->presence = svn_wc__db_status_not_present;
1794     }
1795   else if (entry->absent)
1796     {
1797       WRITE_ENTRY_ASSERT(base_node && !working_node && !below_working_node);
1798       WRITE_ENTRY_ASSERT(!entry->incomplete);
1799       base_node->presence = svn_wc__db_status_server_excluded;
1800     }
1801
1802   if (entry->copied)
1803     {
1804       if (entry->copyfrom_url)
1805         {
1806           working_node->repos_id = repos_id;
1807           working_node->repos_relpath = svn_uri_skip_ancestor(
1808                                           this_dir->repos, entry->copyfrom_url,
1809                                           result_pool);
1810           working_node->revision = entry->copyfrom_rev;
1811           working_node->op_depth
1812             = svn_wc__db_op_depth_for_upgrade(local_relpath);
1813         }
1814       else if (parent_node->work && parent_node->work->repos_relpath)
1815         {
1816           working_node->repos_id = repos_id;
1817           working_node->repos_relpath
1818             = svn_relpath_join(parent_node->work->repos_relpath,
1819                                svn_relpath_basename(local_relpath, NULL),
1820                                result_pool);
1821           working_node->revision = parent_node->work->revision;
1822           working_node->op_depth = parent_node->work->op_depth;
1823         }
1824       else if (parent_node->below_work
1825                 && parent_node->below_work->repos_relpath)
1826         {
1827           working_node->repos_id = repos_id;
1828           working_node->repos_relpath
1829             = svn_relpath_join(parent_node->below_work->repos_relpath,
1830                                svn_relpath_basename(local_relpath, NULL),
1831                                result_pool);
1832           working_node->revision = parent_node->below_work->revision;
1833           working_node->op_depth = parent_node->below_work->op_depth;
1834         }
1835       else
1836         return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL,
1837                                  _("No copyfrom URL for '%s'"),
1838                                  svn_dirent_local_style(local_relpath,
1839                                                         scratch_pool));
1840     }
1841
1842   if (entry->conflict_old)
1843     {
1844       actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
1845       if (parent_relpath && entry->conflict_old)
1846         actual_node->conflict_old = svn_relpath_join(parent_relpath,
1847                                                      entry->conflict_old,
1848                                                      scratch_pool);
1849       else
1850         actual_node->conflict_old = entry->conflict_old;
1851       if (parent_relpath && entry->conflict_new)
1852         actual_node->conflict_new = svn_relpath_join(parent_relpath,
1853                                                      entry->conflict_new,
1854                                                      scratch_pool);
1855       else
1856         actual_node->conflict_new = entry->conflict_new;
1857       if (parent_relpath && entry->conflict_wrk)
1858         actual_node->conflict_working = svn_relpath_join(parent_relpath,
1859                                                          entry->conflict_wrk,
1860                                                          scratch_pool);
1861       else
1862         actual_node->conflict_working = entry->conflict_wrk;
1863     }
1864
1865   if (entry->prejfile)
1866     {
1867       actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
1868       actual_node->prop_reject = svn_relpath_join((entry->kind == svn_node_dir
1869                                                    ? local_relpath
1870                                                    : parent_relpath),
1871                                                   entry->prejfile,
1872                                                   scratch_pool);
1873     }
1874
1875   if (entry->changelist)
1876     {
1877       actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
1878       actual_node->changelist = entry->changelist;
1879     }
1880
1881   /* ### set the text_mod value? */
1882
1883   if (entry_node && entry->tree_conflict_data)
1884     {
1885       /* Issues #3840/#3916: 1.6 stores multiple tree conflicts on the
1886          parent node, 1.7 stores them directly on the conflited nodes.
1887          So "((skel1) (skel2))" becomes "(skel1)" and "(skel2)" */
1888       svn_skel_t *skel;
1889
1890       skel = svn_skel__parse(entry->tree_conflict_data,
1891                              strlen(entry->tree_conflict_data),
1892                              scratch_pool);
1893       tree_conflicts = apr_hash_make(result_pool);
1894       skel = skel->children;
1895       while(skel)
1896         {
1897           svn_wc_conflict_description2_t *conflict;
1898           svn_skel_t *new_skel;
1899           const char *key;
1900
1901           /* *CONFLICT is allocated so it is safe to use a non-const pointer */
1902           SVN_ERR(svn_wc__deserialize_conflict(
1903                              (const svn_wc_conflict_description2_t**)&conflict,
1904                                                skel,
1905                                                svn_dirent_join(root_abspath,
1906                                                                local_relpath,
1907                                                                scratch_pool),
1908                                                scratch_pool, scratch_pool));
1909
1910           WRITE_ENTRY_ASSERT(conflict->kind == svn_wc_conflict_kind_tree);
1911
1912           /* Fix dubious data stored by old clients, local adds don't have
1913              a repository URL. */
1914           if (conflict->reason == svn_wc_conflict_reason_added)
1915             conflict->src_left_version = NULL;
1916
1917           SVN_ERR(svn_wc__serialize_conflict(&new_skel, conflict,
1918                                              scratch_pool, scratch_pool));
1919
1920           /* Store in hash to be retrieved when writing the child
1921              row. */
1922           key = svn_dirent_skip_ancestor(root_abspath, conflict->local_abspath);
1923           svn_hash_sets(tree_conflicts, apr_pstrdup(result_pool, key),
1924                         svn_skel__unparse(new_skel, result_pool)->data);
1925           skel = skel->next;
1926         }
1927     }
1928   else
1929     tree_conflicts = NULL;
1930
1931   if (parent_node && parent_node->tree_conflicts)
1932     {
1933       const char *tree_conflict_data =
1934           svn_hash_gets(parent_node->tree_conflicts, local_relpath);
1935       if (tree_conflict_data)
1936         {
1937           actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
1938           actual_node->tree_conflict_data = tree_conflict_data;
1939         }
1940
1941       /* Reset hash so that we don't write the row again when writing
1942          actual-only nodes */
1943       svn_hash_sets(parent_node->tree_conflicts, local_relpath, NULL);
1944     }
1945
1946   if (entry->file_external_path != NULL)
1947     {
1948       base_node = MAYBE_ALLOC(base_node, result_pool);
1949     }
1950
1951
1952   /* Insert the base node. */
1953   if (base_node)
1954     {
1955       base_node->wc_id = wc_id;
1956       base_node->local_relpath = local_relpath;
1957       base_node->op_depth = 0;
1958       base_node->parent_relpath = parent_relpath;
1959       base_node->revision = entry->revision;
1960       base_node->recorded_time = entry->text_time;
1961       base_node->recorded_size = entry->working_size;
1962
1963       if (entry->depth != svn_depth_exclude)
1964         base_node->depth = entry->depth;
1965       else
1966         {
1967           base_node->presence = svn_wc__db_status_excluded;
1968           base_node->depth = svn_depth_infinity;
1969         }
1970
1971       if (entry->deleted)
1972         {
1973           WRITE_ENTRY_ASSERT(base_node->presence
1974                              == svn_wc__db_status_not_present);
1975           /* ### should be svn_node_unknown, but let's store what we have. */
1976           base_node->kind = entry->kind;
1977         }
1978       else if (entry->absent)
1979         {
1980           WRITE_ENTRY_ASSERT(base_node->presence
1981                              == svn_wc__db_status_server_excluded);
1982           /* ### should be svn_node_unknown, but let's store what we have. */
1983           base_node->kind = entry->kind;
1984
1985           /* Store the most likely revision in the node to avoid
1986              base nodes without a valid revision. Of course
1987              we remember that the data is still incomplete. */
1988           if (!SVN_IS_VALID_REVNUM(base_node->revision) && parent_node->base)
1989             base_node->revision = parent_node->base->revision;
1990         }
1991       else
1992         {
1993           base_node->kind = entry->kind;
1994
1995           if (base_node->presence != svn_wc__db_status_excluded)
1996             {
1997               /* All subdirs are initially incomplete, they stop being
1998                  incomplete when the entries file in the subdir is
1999                  upgraded and remain incomplete if that doesn't happen. */
2000               if (entry->kind == svn_node_dir
2001                   && strcmp(entry->name, SVN_WC_ENTRY_THIS_DIR))
2002                 {
2003                   base_node->presence = svn_wc__db_status_incomplete;
2004
2005                   /* Store the most likely revision in the node to avoid
2006                      base nodes without a valid revision. Of course
2007                      we remember that the data is still incomplete. */
2008                   if (parent_node->base)
2009                     base_node->revision = parent_node->base->revision;
2010                 }
2011               else if (entry->incomplete)
2012                 {
2013                   /* ### nobody should have set the presence.  */
2014                   WRITE_ENTRY_ASSERT(base_node->presence
2015                                      == svn_wc__db_status_normal);
2016                   base_node->presence = svn_wc__db_status_incomplete;
2017                 }
2018             }
2019         }
2020
2021       if (entry->kind == svn_node_dir)
2022         base_node->checksum = NULL;
2023       else
2024         {
2025           if (text_base_info && text_base_info->revert_base.sha1_checksum)
2026             base_node->checksum = text_base_info->revert_base.sha1_checksum;
2027           else if (text_base_info && text_base_info->normal_base.sha1_checksum)
2028             base_node->checksum = text_base_info->normal_base.sha1_checksum;
2029           else
2030             base_node->checksum = NULL;
2031
2032           /* The base MD5 checksum is available in the entry, unless there
2033            * is a copied WORKING node.  If possible, verify that the entry
2034            * checksum matches the base file that we found. */
2035           if (! (working_node && entry->copied))
2036             {
2037               svn_checksum_t *entry_md5_checksum, *found_md5_checksum;
2038               SVN_ERR(svn_checksum_parse_hex(&entry_md5_checksum,
2039                                              svn_checksum_md5,
2040                                              entry->checksum, scratch_pool));
2041               if (text_base_info && text_base_info->revert_base.md5_checksum)
2042                 found_md5_checksum = text_base_info->revert_base.md5_checksum;
2043               else if (text_base_info
2044                        && text_base_info->normal_base.md5_checksum)
2045                 found_md5_checksum = text_base_info->normal_base.md5_checksum;
2046               else
2047                 found_md5_checksum = NULL;
2048               if (entry_md5_checksum && found_md5_checksum &&
2049                   !svn_checksum_match(entry_md5_checksum, found_md5_checksum))
2050                 return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
2051                                          _("Bad base MD5 checksum for '%s'; "
2052                                            "expected: '%s'; found '%s'; "),
2053                                        svn_dirent_local_style(
2054                                          svn_dirent_join(root_abspath,
2055                                                          local_relpath,
2056                                                          scratch_pool),
2057                                          scratch_pool),
2058                                        svn_checksum_to_cstring_display(
2059                                          entry_md5_checksum, scratch_pool),
2060                                        svn_checksum_to_cstring_display(
2061                                          found_md5_checksum, scratch_pool));
2062               else
2063                 {
2064                   /* ### Not sure what conditions this should cover. */
2065                   /* SVN_ERR_ASSERT(entry->deleted || ...); */
2066                 }
2067             }
2068         }
2069
2070       if (this_dir->repos)
2071         {
2072           base_node->repos_id = repos_id;
2073
2074           if (entry->url != NULL)
2075             {
2076               base_node->repos_relpath = svn_uri_skip_ancestor(
2077                                            this_dir->repos, entry->url,
2078                                            result_pool);
2079             }
2080           else
2081             {
2082               const char *relpath = svn_uri_skip_ancestor(this_dir->repos,
2083                                                           this_dir->url,
2084                                                           scratch_pool);
2085               if (relpath == NULL || *relpath == '\0')
2086                 base_node->repos_relpath = entry->name;
2087               else
2088                 base_node->repos_relpath =
2089                   svn_dirent_join(relpath, entry->name, result_pool);
2090             }
2091         }
2092
2093       /* TODO: These values should always be present, if they are missing
2094          during an upgrade, set a flag, and then ask the user to talk to the
2095          server.
2096
2097          Note: cmt_rev is the distinguishing value. The others may be 0 or
2098          NULL if the corresponding revprop has been deleted.  */
2099       base_node->changed_rev = entry->cmt_rev;
2100       base_node->changed_date = entry->cmt_date;
2101       base_node->changed_author = entry->cmt_author;
2102
2103       if (entry->file_external_path)
2104         base_node->file_external = TRUE;
2105
2106       /* Switched nodes get an empty iprops cache. */
2107       if (parent_node
2108           && is_switched(parent_node->base, base_node, scratch_pool))
2109         base_node->inherited_props
2110           = apr_array_make(scratch_pool, 0, sizeof(svn_prop_inherited_item_t*));
2111
2112       SVN_ERR(insert_node(sdb, base_node, scratch_pool));
2113
2114       /* We have to insert the lock after the base node, because the node
2115          must exist to lookup various bits of repos related information for
2116          the abs path. */
2117       if (entry->lock_token && create_locks)
2118         {
2119           svn_wc__db_lock_t lock;
2120
2121           lock.token = entry->lock_token;
2122           lock.owner = entry->lock_owner;
2123           lock.comment = entry->lock_comment;
2124           lock.date = entry->lock_creation_date;
2125
2126           SVN_ERR(svn_wc__db_lock_add(db, tmp_entry_abspath, &lock,
2127                                       scratch_pool));
2128         }
2129     }
2130
2131   if (below_working_node)
2132     {
2133       db_node_t *work
2134         = parent_node->below_work ? parent_node->below_work : parent_node->work;
2135
2136       below_working_node->wc_id = wc_id;
2137       below_working_node->local_relpath = local_relpath;
2138       below_working_node->op_depth = work->op_depth;
2139       below_working_node->parent_relpath = parent_relpath;
2140       below_working_node->presence = svn_wc__db_status_normal;
2141       below_working_node->kind = entry->kind;
2142       below_working_node->repos_id = work->repos_id;
2143
2144       /* This is just guessing. If the node below would have been switched
2145          or if it was updated to a different version, the guess would
2146          fail. But we don't have better information pre wc-ng :( */
2147       if (work->repos_relpath)
2148         below_working_node->repos_relpath
2149           = svn_relpath_join(work->repos_relpath, entry->name,
2150                              result_pool);
2151       else
2152         below_working_node->repos_relpath = NULL;
2153       below_working_node->revision = parent_node->work->revision;
2154
2155       /* The revert_base checksum isn't available in the entry structure,
2156          so the caller provides it. */
2157
2158       /* text_base_info is NULL for files scheduled to be added. */
2159       below_working_node->checksum = NULL;
2160       if (text_base_info)
2161         {
2162           if (entry->schedule == svn_wc_schedule_delete)
2163             below_working_node->checksum =
2164               text_base_info->normal_base.sha1_checksum;
2165           else
2166             below_working_node->checksum =
2167               text_base_info->revert_base.sha1_checksum;
2168         }
2169       below_working_node->recorded_size = 0;
2170       below_working_node->changed_rev = SVN_INVALID_REVNUM;
2171       below_working_node->changed_date = 0;
2172       below_working_node->changed_author = NULL;
2173       below_working_node->depth = svn_depth_infinity;
2174       below_working_node->recorded_time = 0;
2175       below_working_node->properties = NULL;
2176
2177       if (working_node
2178           && entry->schedule == svn_wc_schedule_delete
2179           && working_node->repos_relpath)
2180         {
2181           /* We are lucky, our guesses above are not necessary. The known
2182              correct information is in working. But our op_depth design
2183              expects more information here */
2184           below_working_node->repos_relpath = working_node->repos_relpath;
2185           below_working_node->repos_id = working_node->repos_id;
2186           below_working_node->revision = working_node->revision;
2187
2188           /* Nice for 'svn status' */
2189           below_working_node->changed_rev = entry->cmt_rev;
2190           below_working_node->changed_date = entry->cmt_date;
2191           below_working_node->changed_author = entry->cmt_author;
2192
2193           /* And now remove it from WORKING, because in wc-ng code
2194              should read it from the lower layer */
2195           working_node->repos_relpath = NULL;
2196           working_node->repos_id = 0;
2197           working_node->revision = SVN_INVALID_REVNUM;
2198         }
2199
2200       SVN_ERR(insert_node(sdb, below_working_node, scratch_pool));
2201     }
2202
2203   /* Insert the working node. */
2204   if (working_node)
2205     {
2206       working_node->wc_id = wc_id;
2207       working_node->local_relpath = local_relpath;
2208       working_node->parent_relpath = parent_relpath;
2209       working_node->changed_rev = SVN_INVALID_REVNUM;
2210       working_node->recorded_time = entry->text_time;
2211       working_node->recorded_size = entry->working_size;
2212
2213       if (entry->depth != svn_depth_exclude)
2214         working_node->depth = entry->depth;
2215       else
2216         {
2217           working_node->presence = svn_wc__db_status_excluded;
2218           working_node->depth = svn_depth_infinity;
2219         }
2220
2221       if (entry->kind == svn_node_dir)
2222         working_node->checksum = NULL;
2223       else
2224         {
2225           working_node->checksum = NULL;
2226           /* text_base_info is NULL for files scheduled to be added. */
2227           if (text_base_info)
2228             working_node->checksum = text_base_info->normal_base.sha1_checksum;
2229
2230
2231           /* If an MD5 checksum is present in the entry, we can verify that
2232            * it matches the MD5 of the base file we found earlier. */
2233 #ifdef SVN_DEBUG
2234           if (entry->checksum && text_base_info)
2235           {
2236             svn_checksum_t *md5_checksum;
2237             SVN_ERR(svn_checksum_parse_hex(&md5_checksum, svn_checksum_md5,
2238                                            entry->checksum, result_pool));
2239             SVN_ERR_ASSERT(
2240               md5_checksum && text_base_info->normal_base.md5_checksum);
2241             SVN_ERR_ASSERT(svn_checksum_match(
2242               md5_checksum, text_base_info->normal_base.md5_checksum));
2243           }
2244 #endif
2245         }
2246
2247       working_node->kind = entry->kind;
2248       if (working_node->presence != svn_wc__db_status_excluded)
2249         {
2250           /* All subdirs start of incomplete, and stop being incomplete
2251              when the entries file in the subdir is upgraded. */
2252           if (entry->kind == svn_node_dir
2253               && strcmp(entry->name, SVN_WC_ENTRY_THIS_DIR))
2254             {
2255               working_node->presence = svn_wc__db_status_incomplete;
2256               working_node->kind = svn_node_dir;
2257             }
2258           else if (entry->schedule == svn_wc_schedule_delete)
2259             {
2260               working_node->presence = svn_wc__db_status_base_deleted;
2261               working_node->kind = entry->kind;
2262             }
2263           else
2264             {
2265               /* presence == normal  */
2266               working_node->kind = entry->kind;
2267
2268               if (entry->incomplete)
2269                 {
2270                   /* We shouldn't be overwriting another status.  */
2271                   WRITE_ENTRY_ASSERT(working_node->presence
2272                                      == svn_wc__db_status_normal);
2273                   working_node->presence = svn_wc__db_status_incomplete;
2274                 }
2275             }
2276         }
2277
2278       /* These should generally be unset for added and deleted files,
2279          and contain whatever information we have for copied files. Let's
2280          just store whatever we have.
2281
2282          Note: cmt_rev is the distinguishing value. The others may be 0 or
2283          NULL if the corresponding revprop has been deleted.  */
2284       if (working_node->presence != svn_wc__db_status_base_deleted)
2285         {
2286           working_node->changed_rev = entry->cmt_rev;
2287           working_node->changed_date = entry->cmt_date;
2288           working_node->changed_author = entry->cmt_author;
2289         }
2290
2291       if (entry->schedule == svn_wc_schedule_delete
2292           && parent_node->work
2293           && parent_node->work->presence == svn_wc__db_status_base_deleted)
2294         {
2295           working_node->op_depth = parent_node->work->op_depth;
2296         }
2297       else if (!entry->copied)
2298         {
2299           working_node->op_depth
2300             = svn_wc__db_op_depth_for_upgrade(local_relpath);
2301         }
2302
2303       SVN_ERR(insert_node(sdb, working_node, scratch_pool));
2304     }
2305
2306   /* Insert the actual node. */
2307   if (actual_node)
2308     {
2309       actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
2310
2311       actual_node->wc_id = wc_id;
2312       actual_node->local_relpath = local_relpath;
2313       actual_node->parent_relpath = parent_relpath;
2314
2315       SVN_ERR(insert_actual_node(sdb, db, tmp_entry_abspath,
2316                                  actual_node, scratch_pool));
2317     }
2318
2319   if (entry_node)
2320     {
2321       *entry_node = apr_palloc(result_pool, sizeof(**entry_node));
2322       (*entry_node)->base = base_node;
2323       (*entry_node)->work = working_node;
2324       (*entry_node)->below_work = below_working_node;
2325       (*entry_node)->tree_conflicts = tree_conflicts;
2326     }
2327
2328   if (entry->file_external_path)
2329     {
2330       /* TODO: Maybe add a file external registration inside EXTERNALS here,
2331                to allow removing file externals that aren't referenced from
2332                svn:externals.
2333
2334          The svn:externals values are processed anyway after everything is
2335          upgraded */
2336     }
2337
2338   return SVN_NO_ERROR;
2339 }
2340
2341 static svn_error_t *
2342 write_actual_only_entries(apr_hash_t *tree_conflicts,
2343                           svn_sqlite__db_t *sdb,
2344                           svn_wc__db_t *db,
2345                           const char *wri_abspath,
2346                           apr_int64_t wc_id,
2347                           const char *parent_relpath,
2348                           apr_pool_t *scratch_pool)
2349 {
2350   apr_hash_index_t *hi;
2351
2352   for (hi = apr_hash_first(scratch_pool, tree_conflicts);
2353        hi;
2354        hi = apr_hash_next(hi))
2355     {
2356       db_actual_node_t *actual_node = NULL;
2357
2358       actual_node = MAYBE_ALLOC(actual_node, scratch_pool);
2359       actual_node->wc_id = wc_id;
2360       actual_node->local_relpath = svn__apr_hash_index_key(hi);
2361       actual_node->parent_relpath = parent_relpath;
2362       actual_node->tree_conflict_data = svn__apr_hash_index_val(hi);
2363
2364       SVN_ERR(insert_actual_node(sdb, db, wri_abspath, actual_node,
2365                                  scratch_pool));
2366     }
2367
2368   return SVN_NO_ERROR;
2369 }
2370
2371 svn_error_t *
2372 svn_wc__write_upgraded_entries(void **dir_baton,
2373                                void *parent_baton,
2374                                svn_wc__db_t *db,
2375                                svn_sqlite__db_t *sdb,
2376                                apr_int64_t repos_id,
2377                                apr_int64_t wc_id,
2378                                const char *dir_abspath,
2379                                const char *new_root_abspath,
2380                                apr_hash_t *entries,
2381                                apr_hash_t *text_bases_info,
2382                                apr_pool_t *result_pool,
2383                                apr_pool_t *scratch_pool)
2384 {
2385   const svn_wc_entry_t *this_dir;
2386   apr_hash_index_t *hi;
2387   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2388   const char *old_root_abspath, *dir_relpath;
2389   struct write_baton *parent_node = parent_baton;
2390   struct write_baton *dir_node;
2391
2392   /* Get a copy of the "this dir" entry for comparison purposes. */
2393   this_dir = svn_hash_gets(entries, SVN_WC_ENTRY_THIS_DIR);
2394
2395   /* If there is no "this dir" entry, something is wrong. */
2396   if (! this_dir)
2397     return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL,
2398                              _("No default entry in directory '%s'"),
2399                              svn_dirent_local_style(dir_abspath,
2400                                                     iterpool));
2401   old_root_abspath = svn_dirent_get_longest_ancestor(dir_abspath,
2402                                                      new_root_abspath,
2403                                                      scratch_pool);
2404
2405   SVN_ERR_ASSERT(old_root_abspath[0]);
2406
2407   dir_relpath = svn_dirent_skip_ancestor(old_root_abspath, dir_abspath);
2408
2409   /* Write out "this dir" */
2410   SVN_ERR(write_entry(&dir_node, parent_node, db, sdb,
2411                       wc_id, repos_id, this_dir, NULL, dir_relpath,
2412                       svn_dirent_join(new_root_abspath, dir_relpath,
2413                                       iterpool),
2414                       old_root_abspath,
2415                       this_dir, FALSE, result_pool, iterpool));
2416
2417   for (hi = apr_hash_first(scratch_pool, entries); hi;
2418        hi = apr_hash_next(hi))
2419     {
2420       const char *name = svn__apr_hash_index_key(hi);
2421       const svn_wc_entry_t *this_entry = svn__apr_hash_index_val(hi);
2422       const char *child_abspath, *child_relpath;
2423       svn_wc__text_base_info_t *text_base_info
2424         = svn_hash_gets(text_bases_info, name);
2425
2426       svn_pool_clear(iterpool);
2427
2428       /* Don't rewrite the "this dir" entry! */
2429       if (strcmp(name, SVN_WC_ENTRY_THIS_DIR) == 0)
2430         continue;
2431
2432       /* Write the entry. Pass TRUE for create locks, because we still
2433          use this function for upgrading old working copies. */
2434       child_abspath = svn_dirent_join(dir_abspath, name, iterpool);
2435       child_relpath = svn_dirent_skip_ancestor(old_root_abspath, child_abspath);
2436       SVN_ERR(write_entry(NULL, dir_node, db, sdb,
2437                           wc_id, repos_id,
2438                           this_entry, text_base_info, child_relpath,
2439                           svn_dirent_join(new_root_abspath, child_relpath,
2440                                           iterpool),
2441                           old_root_abspath,
2442                           this_dir, TRUE, iterpool, iterpool));
2443     }
2444
2445   if (dir_node->tree_conflicts)
2446     SVN_ERR(write_actual_only_entries(dir_node->tree_conflicts, sdb, db,
2447                                       new_root_abspath, wc_id, dir_relpath,
2448                                       iterpool));
2449
2450   *dir_baton = dir_node;
2451   svn_pool_destroy(iterpool);
2452   return SVN_NO_ERROR;
2453 }
2454
2455
2456 svn_wc_entry_t *
2457 svn_wc_entry_dup(const svn_wc_entry_t *entry, apr_pool_t *pool)
2458 {
2459   svn_wc_entry_t *dupentry = apr_palloc(pool, sizeof(*dupentry));
2460
2461   /* Perform a trivial copy ... */
2462   *dupentry = *entry;
2463
2464   /* ...and then re-copy stuff that needs to be duped into our pool. */
2465   if (entry->name)
2466     dupentry->name = apr_pstrdup(pool, entry->name);
2467   if (entry->url)
2468     dupentry->url = apr_pstrdup(pool, entry->url);
2469   if (entry->repos)
2470     dupentry->repos = apr_pstrdup(pool, entry->repos);
2471   if (entry->uuid)
2472     dupentry->uuid = apr_pstrdup(pool, entry->uuid);
2473   if (entry->copyfrom_url)
2474     dupentry->copyfrom_url = apr_pstrdup(pool, entry->copyfrom_url);
2475   if (entry->conflict_old)
2476     dupentry->conflict_old = apr_pstrdup(pool, entry->conflict_old);
2477   if (entry->conflict_new)
2478     dupentry->conflict_new = apr_pstrdup(pool, entry->conflict_new);
2479   if (entry->conflict_wrk)
2480     dupentry->conflict_wrk = apr_pstrdup(pool, entry->conflict_wrk);
2481   if (entry->prejfile)
2482     dupentry->prejfile = apr_pstrdup(pool, entry->prejfile);
2483   if (entry->checksum)
2484     dupentry->checksum = apr_pstrdup(pool, entry->checksum);
2485   if (entry->cmt_author)
2486     dupentry->cmt_author = apr_pstrdup(pool, entry->cmt_author);
2487   if (entry->lock_token)
2488     dupentry->lock_token = apr_pstrdup(pool, entry->lock_token);
2489   if (entry->lock_owner)
2490     dupentry->lock_owner = apr_pstrdup(pool, entry->lock_owner);
2491   if (entry->lock_comment)
2492     dupentry->lock_comment = apr_pstrdup(pool, entry->lock_comment);
2493   if (entry->changelist)
2494     dupentry->changelist = apr_pstrdup(pool, entry->changelist);
2495
2496   /* NOTE: we do not dup cachable_props or present_props since they
2497      are deprecated. Use "" to indicate "nothing cachable or cached". */
2498   dupentry->cachable_props = "";
2499   dupentry->present_props = "";
2500
2501   if (entry->tree_conflict_data)
2502     dupentry->tree_conflict_data = apr_pstrdup(pool,
2503                                                entry->tree_conflict_data);
2504   if (entry->file_external_path)
2505     dupentry->file_external_path = apr_pstrdup(pool,
2506                                                entry->file_external_path);
2507   return dupentry;
2508 }
2509
2510
2511 /*** Generic Entry Walker */
2512
2513 /* A recursive entry-walker, helper for svn_wc_walk_entries3().
2514  *
2515  * For this directory (DIRPATH, ADM_ACCESS), call the "found_entry" callback
2516  * in WALK_CALLBACKS, passing WALK_BATON to it. Then, for each versioned
2517  * entry in this directory, call the "found entry" callback and then recurse
2518  * (if it is a directory and if DEPTH allows).
2519  *
2520  * If SHOW_HIDDEN is true, include entries that are in a 'deleted' or
2521  * 'absent' state (and not scheduled for re-addition), else skip them.
2522  *
2523  * Call CANCEL_FUNC with CANCEL_BATON to allow cancellation.
2524  */
2525 static svn_error_t *
2526 walker_helper(const char *dirpath,
2527               svn_wc_adm_access_t *adm_access,
2528               const svn_wc_entry_callbacks2_t *walk_callbacks,
2529               void *walk_baton,
2530               svn_depth_t depth,
2531               svn_boolean_t show_hidden,
2532               svn_cancel_func_t cancel_func,
2533               void *cancel_baton,
2534               apr_pool_t *pool)
2535 {
2536   apr_pool_t *subpool = svn_pool_create(pool);
2537   apr_hash_t *entries;
2538   apr_hash_index_t *hi;
2539   svn_wc_entry_t *dot_entry;
2540   svn_error_t *err;
2541   svn_wc__db_t *db = svn_wc__adm_get_db(adm_access);
2542
2543   err = svn_wc__entries_read_internal(&entries, adm_access, show_hidden,
2544                                       pool);
2545
2546   if (err)
2547     SVN_ERR(walk_callbacks->handle_error(dirpath, err, walk_baton, pool));
2548
2549   /* As promised, always return the '.' entry first. */
2550   dot_entry = svn_hash_gets(entries, SVN_WC_ENTRY_THIS_DIR);
2551   if (! dot_entry)
2552     return walk_callbacks->handle_error
2553       (dirpath, svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL,
2554                                   _("Directory '%s' has no THIS_DIR entry"),
2555                                   svn_dirent_local_style(dirpath, pool)),
2556        walk_baton, pool);
2557
2558   /* Call the "found entry" callback for this directory as a "this dir"
2559    * entry. Note that if this directory has been reached by recursion, this
2560    * is the second visit as it will already have been visited once as a
2561    * child entry of its parent. */
2562
2563   err = walk_callbacks->found_entry(dirpath, dot_entry, walk_baton, subpool);
2564
2565
2566   if(err)
2567     SVN_ERR(walk_callbacks->handle_error(dirpath, err, walk_baton, pool));
2568
2569   if (depth == svn_depth_empty)
2570     return SVN_NO_ERROR;
2571
2572   /* Loop over each of the other entries. */
2573   for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
2574     {
2575       const char *name = svn__apr_hash_index_key(hi);
2576       const svn_wc_entry_t *current_entry = svn__apr_hash_index_val(hi);
2577       const char *entrypath;
2578       const char *entry_abspath;
2579       svn_boolean_t hidden;
2580
2581       svn_pool_clear(subpool);
2582
2583       /* See if someone wants to cancel this operation. */
2584       if (cancel_func)
2585         SVN_ERR(cancel_func(cancel_baton));
2586
2587       /* Skip the "this dir" entry. */
2588       if (strcmp(current_entry->name, SVN_WC_ENTRY_THIS_DIR) == 0)
2589         continue;
2590
2591       entrypath = svn_dirent_join(dirpath, name, subpool);
2592       SVN_ERR(svn_wc__entry_is_hidden(&hidden, current_entry));
2593       SVN_ERR(svn_dirent_get_absolute(&entry_abspath, entrypath, subpool));
2594
2595       /* Call the "found entry" callback for this entry. (For a directory,
2596        * this is the first visit: as a child.) */
2597       if (current_entry->kind == svn_node_file
2598           || depth >= svn_depth_immediates)
2599         {
2600           err = walk_callbacks->found_entry(entrypath, current_entry,
2601                                             walk_baton, subpool);
2602
2603           if (err)
2604             SVN_ERR(walk_callbacks->handle_error(entrypath, err,
2605                                                  walk_baton, pool));
2606         }
2607
2608       /* Recurse into this entry if appropriate. */
2609       if (current_entry->kind == svn_node_dir
2610           && !hidden
2611           && depth >= svn_depth_immediates)
2612         {
2613           svn_wc_adm_access_t *entry_access;
2614           svn_depth_t depth_below_here = depth;
2615
2616           if (depth == svn_depth_immediates)
2617             depth_below_here = svn_depth_empty;
2618
2619           entry_access = svn_wc__adm_retrieve_internal2(db, entry_abspath,
2620                                                         subpool);
2621
2622           if (entry_access)
2623             SVN_ERR(walker_helper(entrypath, entry_access,
2624                                   walk_callbacks, walk_baton,
2625                                   depth_below_here, show_hidden,
2626                                   cancel_func, cancel_baton,
2627                                   subpool));
2628         }
2629     }
2630
2631   svn_pool_destroy(subpool);
2632   return SVN_NO_ERROR;
2633 }
2634
2635 svn_error_t *
2636 svn_wc__walker_default_error_handler(const char *path,
2637                                      svn_error_t *err,
2638                                      void *walk_baton,
2639                                      apr_pool_t *pool)
2640 {
2641   /* Note: don't trace this. We don't want to insert a false "stack frame"
2642      onto an error generated elsewhere.  */
2643   return svn_error_trace(err);
2644 }
2645
2646
2647 /* The public API. */
2648 svn_error_t *
2649 svn_wc_walk_entries3(const char *path,
2650                      svn_wc_adm_access_t *adm_access,
2651                      const svn_wc_entry_callbacks2_t *walk_callbacks,
2652                      void *walk_baton,
2653                      svn_depth_t walk_depth,
2654                      svn_boolean_t show_hidden,
2655                      svn_cancel_func_t cancel_func,
2656                      void *cancel_baton,
2657                      apr_pool_t *pool)
2658 {
2659   const char *local_abspath;
2660   svn_wc__db_t *db = svn_wc__adm_get_db(adm_access);
2661   svn_error_t *err;
2662   svn_node_kind_t kind;
2663   svn_depth_t depth;
2664
2665   SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
2666   err = svn_wc__db_read_info(NULL, &kind, NULL, NULL, NULL, NULL,
2667                              NULL, NULL, NULL, &depth, NULL, NULL,
2668                              NULL, NULL, NULL, NULL, NULL, NULL,
2669                              NULL, NULL, NULL, NULL, NULL, NULL,
2670                              NULL, NULL, NULL,
2671                              db, local_abspath,
2672                              pool, pool);
2673   if (err)
2674     {
2675       if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
2676         return svn_error_trace(err);
2677       /* Remap into SVN_ERR_UNVERSIONED_RESOURCE.  */
2678       svn_error_clear(err);
2679       return walk_callbacks->handle_error(
2680         path, svn_error_createf(SVN_ERR_UNVERSIONED_RESOURCE, NULL,
2681                                 _("'%s' is not under version control"),
2682                                 svn_dirent_local_style(local_abspath, pool)),
2683         walk_baton, pool);
2684     }
2685
2686   if (kind == svn_node_file || depth == svn_depth_exclude)
2687     {
2688       const svn_wc_entry_t *entry;
2689
2690       /* ### we should stop passing out entry structures.
2691          ###
2692          ### we should not call handle_error for an error the *callback*
2693          ###   gave us. let it deal with the problem before returning.  */
2694
2695       if (!show_hidden)
2696         {
2697           svn_boolean_t hidden;
2698           SVN_ERR(svn_wc__db_node_hidden(&hidden, db, local_abspath, pool));
2699
2700           if (hidden)
2701             {
2702               /* The fool asked to walk a "hidden" node. Report the node as
2703                  unversioned.
2704
2705                  ### this is incorrect behavior. see depth_test 36. the walk
2706                  ### API will be revamped to avoid entry structures. we should
2707                  ### be able to solve the problem with the new API. (since we
2708                  ### shouldn't return a hidden entry here)  */
2709               return walk_callbacks->handle_error(
2710                                path, svn_error_createf(
2711                                   SVN_ERR_UNVERSIONED_RESOURCE, NULL,
2712                                   _("'%s' is not under version control"),
2713                                   svn_dirent_local_style(local_abspath, pool)),
2714                                walk_baton, pool);
2715             }
2716         }
2717
2718       SVN_ERR(svn_wc__get_entry(&entry, db, local_abspath, FALSE,
2719                                 svn_node_file, pool, pool));
2720
2721       err = walk_callbacks->found_entry(path, entry, walk_baton, pool);
2722       if (err)
2723         return walk_callbacks->handle_error(path, err, walk_baton, pool);
2724
2725       return SVN_NO_ERROR;
2726     }
2727
2728   if (kind == svn_node_dir)
2729     return walker_helper(path, adm_access, walk_callbacks, walk_baton,
2730                          walk_depth, show_hidden, cancel_func, cancel_baton,
2731                          pool);
2732
2733   return walk_callbacks->handle_error(
2734        path, svn_error_createf(SVN_ERR_NODE_UNKNOWN_KIND, NULL,
2735                                _("'%s' has an unrecognized node kind"),
2736                                svn_dirent_local_style(local_abspath, pool)),
2737        walk_baton, pool);
2738 }