1251881Speter/* 2251881Speter * entries.c : manipulating the administrative `entries' file. 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter#include <string.h> 25251881Speter#include <assert.h> 26251881Speter 27251881Speter#include <apr_strings.h> 28251881Speter 29251881Speter#include "svn_error.h" 30251881Speter#include "svn_types.h" 31251881Speter#include "svn_time.h" 32251881Speter#include "svn_pools.h" 33251881Speter#include "svn_dirent_uri.h" 34251881Speter#include "svn_path.h" 35251881Speter#include "svn_ctype.h" 36251881Speter#include "svn_string.h" 37251881Speter#include "svn_hash.h" 38251881Speter 39251881Speter#include "wc.h" 40251881Speter#include "adm_files.h" 41251881Speter#include "conflicts.h" 42251881Speter#include "entries.h" 43251881Speter#include "lock.h" 44251881Speter#include "tree_conflicts.h" 45251881Speter#include "wc_db.h" 46251881Speter#include "wc-queries.h" /* for STMT_* */ 47251881Speter 48251881Speter#include "svn_private_config.h" 49251881Speter#include "private/svn_wc_private.h" 50251881Speter#include "private/svn_sqlite.h" 51251881Speter 52251881Speter#define MAYBE_ALLOC(x,p) ((x) ? (x) : apr_pcalloc((p), sizeof(*(x)))) 53251881Speter 54251881Speter 55251881Speter/* Temporary structures which mirror the tables in wc-metadata.sql. 56251881Speter For detailed descriptions of each field, see that file. */ 57251881Spetertypedef struct db_node_t { 58251881Speter apr_int64_t wc_id; 59251881Speter const char *local_relpath; 60251881Speter int op_depth; 61251881Speter apr_int64_t repos_id; 62251881Speter const char *repos_relpath; 63251881Speter const char *parent_relpath; 64251881Speter svn_wc__db_status_t presence; 65251881Speter svn_revnum_t revision; 66251881Speter svn_node_kind_t kind; 67251881Speter svn_checksum_t *checksum; 68251881Speter svn_filesize_t recorded_size; 69251881Speter svn_revnum_t changed_rev; 70251881Speter apr_time_t changed_date; 71251881Speter const char *changed_author; 72251881Speter svn_depth_t depth; 73251881Speter apr_time_t recorded_time; 74251881Speter apr_hash_t *properties; 75251881Speter svn_boolean_t file_external; 76251881Speter apr_array_header_t *inherited_props; 77251881Speter} db_node_t; 78251881Speter 79251881Spetertypedef struct db_actual_node_t { 80251881Speter apr_int64_t wc_id; 81251881Speter const char *local_relpath; 82251881Speter const char *parent_relpath; 83251881Speter apr_hash_t *properties; 84251881Speter const char *conflict_old; 85251881Speter const char *conflict_new; 86251881Speter const char *conflict_working; 87251881Speter const char *prop_reject; 88251881Speter const char *changelist; 89251881Speter /* ### enum for text_mod */ 90251881Speter const char *tree_conflict_data; 91251881Speter} db_actual_node_t; 92251881Speter 93251881Speter 94251881Speter 95251881Speter/*** reading and writing the entries file ***/ 96251881Speter 97251881Speter 98251881Speter/* */ 99251881Speterstatic svn_wc_entry_t * 100251881Speteralloc_entry(apr_pool_t *pool) 101251881Speter{ 102251881Speter svn_wc_entry_t *entry = apr_pcalloc(pool, sizeof(*entry)); 103251881Speter entry->revision = SVN_INVALID_REVNUM; 104251881Speter entry->copyfrom_rev = SVN_INVALID_REVNUM; 105251881Speter entry->cmt_rev = SVN_INVALID_REVNUM; 106251881Speter entry->kind = svn_node_none; 107251881Speter entry->working_size = SVN_WC_ENTRY_WORKING_SIZE_UNKNOWN; 108251881Speter entry->depth = svn_depth_infinity; 109251881Speter entry->file_external_peg_rev.kind = svn_opt_revision_unspecified; 110251881Speter entry->file_external_rev.kind = svn_opt_revision_unspecified; 111251881Speter return entry; 112251881Speter} 113251881Speter 114251881Speter 115251881Speter/* Is the entry in a 'hidden' state in the sense of the 'show_hidden' 116251881Speter * switches on svn_wc_entries_read(), svn_wc_walk_entries*(), etc.? */ 117251881Spetersvn_error_t * 118251881Spetersvn_wc__entry_is_hidden(svn_boolean_t *hidden, const svn_wc_entry_t *entry) 119251881Speter{ 120251881Speter /* In English, the condition is: "the entry is not present, and I haven't 121251881Speter scheduled something over the top of it." */ 122251881Speter if (entry->deleted 123251881Speter || entry->absent 124251881Speter || entry->depth == svn_depth_exclude) 125251881Speter { 126251881Speter /* These kinds of nodes cannot be marked for deletion (which also 127251881Speter means no "replace" either). */ 128251881Speter SVN_ERR_ASSERT(entry->schedule == svn_wc_schedule_add 129251881Speter || entry->schedule == svn_wc_schedule_normal); 130251881Speter 131251881Speter /* Hidden if something hasn't been added over it. 132251881Speter 133251881Speter ### is this even possible with absent or excluded nodes? */ 134251881Speter *hidden = entry->schedule != svn_wc_schedule_add; 135251881Speter } 136251881Speter else 137251881Speter *hidden = FALSE; 138251881Speter 139251881Speter return SVN_NO_ERROR; 140251881Speter} 141251881Speter 142251881Speter 143251881Speter/* Hit the database to check the file external information for the given 144251881Speter entry. The entry will be modified in place. */ 145251881Speterstatic svn_error_t * 146251881Spetercheck_file_external(svn_wc_entry_t *entry, 147251881Speter svn_wc__db_t *db, 148251881Speter const char *local_abspath, 149251881Speter const char *wri_abspath, 150251881Speter apr_pool_t *result_pool, 151251881Speter apr_pool_t *scratch_pool) 152251881Speter{ 153251881Speter svn_wc__db_status_t status; 154251881Speter svn_node_kind_t kind; 155251881Speter const char *repos_relpath; 156251881Speter svn_revnum_t peg_revision; 157251881Speter svn_revnum_t revision; 158251881Speter svn_error_t *err; 159251881Speter 160251881Speter err = svn_wc__db_external_read(&status, &kind, NULL, NULL, NULL, 161251881Speter &repos_relpath, &peg_revision, &revision, 162251881Speter db, local_abspath, wri_abspath, 163251881Speter result_pool, scratch_pool); 164251881Speter 165251881Speter if (err) 166251881Speter { 167251881Speter if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 168251881Speter return svn_error_trace(err); 169251881Speter 170251881Speter svn_error_clear(err); 171251881Speter return SVN_NO_ERROR; 172251881Speter } 173251881Speter 174251881Speter if (status == svn_wc__db_status_normal 175251881Speter && kind == svn_node_file) 176251881Speter { 177251881Speter entry->file_external_path = repos_relpath; 178251881Speter if (SVN_IS_VALID_REVNUM(peg_revision)) 179251881Speter { 180251881Speter entry->file_external_peg_rev.kind = svn_opt_revision_number; 181251881Speter entry->file_external_peg_rev.value.number = peg_revision; 182251881Speter entry->file_external_rev = entry->file_external_peg_rev; 183251881Speter } 184251881Speter if (SVN_IS_VALID_REVNUM(revision)) 185251881Speter { 186251881Speter entry->file_external_rev.kind = svn_opt_revision_number; 187251881Speter entry->file_external_rev.value.number = revision; 188251881Speter } 189251881Speter } 190251881Speter 191251881Speter return SVN_NO_ERROR; 192251881Speter} 193251881Speter 194251881Speter 195251881Speter/* Fill in the following fields of ENTRY: 196251881Speter 197251881Speter REVISION 198251881Speter REPOS 199251881Speter UUID 200251881Speter CMT_REV 201251881Speter CMT_DATE 202251881Speter CMT_AUTHOR 203251881Speter DEPTH 204251881Speter DELETED 205251881Speter 206251881Speter Return: KIND, REPOS_RELPATH, CHECKSUM 207251881Speter*/ 208251881Speterstatic svn_error_t * 209251881Speterget_info_for_deleted(svn_wc_entry_t *entry, 210251881Speter svn_node_kind_t *kind, 211251881Speter const char **repos_relpath, 212251881Speter const svn_checksum_t **checksum, 213251881Speter svn_wc__db_lock_t **lock, 214251881Speter svn_wc__db_t *db, 215251881Speter const char *entry_abspath, 216251881Speter const svn_wc_entry_t *parent_entry, 217251881Speter svn_boolean_t have_base, 218251881Speter svn_boolean_t have_more_work, 219251881Speter apr_pool_t *result_pool, 220251881Speter apr_pool_t *scratch_pool) 221251881Speter{ 222251881Speter if (have_base && !have_more_work) 223251881Speter { 224251881Speter /* This is the delete of a BASE node */ 225251881Speter SVN_ERR(svn_wc__db_base_get_info(NULL, kind, 226251881Speter &entry->revision, 227251881Speter repos_relpath, 228251881Speter &entry->repos, 229251881Speter &entry->uuid, 230251881Speter &entry->cmt_rev, 231251881Speter &entry->cmt_date, 232251881Speter &entry->cmt_author, 233251881Speter &entry->depth, 234251881Speter checksum, 235251881Speter NULL, 236251881Speter lock, 237251881Speter &entry->has_props, NULL, 238251881Speter NULL, 239251881Speter db, 240251881Speter entry_abspath, 241251881Speter result_pool, 242251881Speter scratch_pool)); 243251881Speter } 244251881Speter else 245251881Speter { 246251881Speter const char *work_del_abspath; 247251881Speter const char *parent_repos_relpath; 248251881Speter const char *parent_abspath; 249251881Speter 250251881Speter /* This is a deleted child of a copy/move-here, 251251881Speter so we need to scan up the WORKING tree to find the root of 252251881Speter the deletion. Then examine its parent to discover its 253251881Speter future location in the repository. */ 254251881Speter SVN_ERR(svn_wc__db_read_pristine_info(NULL, kind, 255251881Speter &entry->cmt_rev, 256251881Speter &entry->cmt_date, 257251881Speter &entry->cmt_author, 258251881Speter &entry->depth, 259251881Speter checksum, 260251881Speter NULL, 261251881Speter &entry->has_props, NULL, 262251881Speter db, 263251881Speter entry_abspath, 264251881Speter result_pool, 265251881Speter scratch_pool)); 266251881Speter /* working_size and text_time unavailable */ 267251881Speter 268251881Speter SVN_ERR(svn_wc__db_scan_deletion(NULL, 269251881Speter NULL, 270251881Speter &work_del_abspath, NULL, 271251881Speter db, entry_abspath, 272251881Speter scratch_pool, scratch_pool)); 273251881Speter 274251881Speter SVN_ERR_ASSERT(work_del_abspath != NULL); 275251881Speter parent_abspath = svn_dirent_dirname(work_del_abspath, scratch_pool); 276251881Speter 277251881Speter /* The parent directory of the delete root must be added, so we 278251881Speter can find the required information there */ 279251881Speter SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, 280251881Speter &parent_repos_relpath, 281251881Speter &entry->repos, 282251881Speter &entry->uuid, 283251881Speter NULL, NULL, NULL, NULL, 284251881Speter db, parent_abspath, 285251881Speter result_pool, scratch_pool)); 286251881Speter 287251881Speter /* Now glue it all together */ 288251881Speter *repos_relpath = svn_relpath_join(parent_repos_relpath, 289251881Speter svn_dirent_is_child(parent_abspath, 290251881Speter entry_abspath, 291251881Speter NULL), 292251881Speter result_pool); 293251881Speter 294251881Speter 295251881Speter /* Even though this is the delete of a WORKING node, there might still 296251881Speter be a BASE node somewhere below with an interesting revision */ 297251881Speter if (have_base) 298251881Speter { 299251881Speter svn_wc__db_status_t status; 300251881Speter SVN_ERR(svn_wc__db_base_get_info(&status, NULL, &entry->revision, 301251881Speter NULL, NULL, NULL, NULL, NULL, NULL, 302251881Speter NULL, NULL, NULL, lock, NULL, NULL, 303251881Speter NULL, 304251881Speter db, entry_abspath, 305251881Speter result_pool, scratch_pool)); 306251881Speter 307251881Speter if (status == svn_wc__db_status_not_present) 308251881Speter entry->deleted = TRUE; 309251881Speter } 310251881Speter } 311251881Speter 312251881Speter /* Do some extra work for the child nodes. */ 313251881Speter if (!SVN_IS_VALID_REVNUM(entry->revision) && parent_entry != NULL) 314251881Speter { 315251881Speter /* For child nodes without a revision, pick up the parent's 316251881Speter revision. */ 317251881Speter entry->revision = parent_entry->revision; 318251881Speter } 319251881Speter 320251881Speter return SVN_NO_ERROR; 321251881Speter} 322251881Speter 323251881Speter 324251881Speter/* 325251881Speter * Encode tree conflict descriptions into a single string. 326251881Speter * 327251881Speter * Set *CONFLICT_DATA to a string, allocated in POOL, that encodes the tree 328251881Speter * conflicts in CONFLICTS in a form suitable for storage in a single string 329251881Speter * field in a WC entry. CONFLICTS is a hash of zero or more pointers to 330251881Speter * svn_wc_conflict_description2_t objects, index by their basenames. All of the 331251881Speter * conflict victim paths must be siblings. 332251881Speter * 333251881Speter * Do all allocations in POOL. 334251881Speter * 335251881Speter * @see svn_wc__read_tree_conflicts() 336251881Speter */ 337251881Speterstatic svn_error_t * 338251881Speterwrite_tree_conflicts(const char **conflict_data, 339251881Speter apr_hash_t *conflicts, 340251881Speter apr_pool_t *pool) 341251881Speter{ 342251881Speter svn_skel_t *skel = svn_skel__make_empty_list(pool); 343251881Speter apr_hash_index_t *hi; 344251881Speter 345251881Speter for (hi = apr_hash_first(pool, conflicts); hi; hi = apr_hash_next(hi)) 346251881Speter { 347251881Speter svn_skel_t *c_skel; 348251881Speter 349251881Speter SVN_ERR(svn_wc__serialize_conflict(&c_skel, svn__apr_hash_index_val(hi), 350251881Speter pool, pool)); 351251881Speter svn_skel__prepend(c_skel, skel); 352251881Speter } 353251881Speter 354251881Speter *conflict_data = svn_skel__unparse(skel, pool)->data; 355251881Speter 356251881Speter return SVN_NO_ERROR; 357251881Speter} 358251881Speter 359251881Speter 360251881Speter/* Read one entry from wc_db. It will be allocated in RESULT_POOL and 361251881Speter returned in *NEW_ENTRY. 362251881Speter 363251881Speter DIR_ABSPATH is the name of the directory to read this entry from, and 364251881Speter it will be named NAME (use "" for "this dir"). 365251881Speter 366251881Speter DB specifies the wc_db database, and WC_ID specifies which working copy 367251881Speter this information is being read from. 368251881Speter 369251881Speter If this node is "this dir", then PARENT_ENTRY should be NULL. Otherwise, 370251881Speter it should refer to the entry for the child's parent directory. 371251881Speter 372251881Speter Temporary allocations are made in SCRATCH_POOL. */ 373251881Speterstatic svn_error_t * 374251881Speterread_one_entry(const svn_wc_entry_t **new_entry, 375251881Speter svn_wc__db_t *db, 376251881Speter apr_int64_t wc_id, 377251881Speter const char *dir_abspath, 378251881Speter const char *name, 379251881Speter const svn_wc_entry_t *parent_entry, 380251881Speter apr_pool_t *result_pool, 381251881Speter apr_pool_t *scratch_pool) 382251881Speter{ 383251881Speter svn_node_kind_t kind; 384251881Speter svn_wc__db_status_t status; 385251881Speter svn_wc__db_lock_t *lock; 386251881Speter const char *repos_relpath; 387251881Speter const svn_checksum_t *checksum; 388251881Speter svn_filesize_t translated_size; 389251881Speter svn_wc_entry_t *entry = alloc_entry(result_pool); 390251881Speter const char *entry_abspath; 391251881Speter const char *original_repos_relpath; 392251881Speter const char *original_root_url; 393251881Speter svn_boolean_t conflicted; 394251881Speter svn_boolean_t have_base; 395251881Speter svn_boolean_t have_more_work; 396251881Speter 397251881Speter entry->name = name; 398251881Speter 399251881Speter entry_abspath = svn_dirent_join(dir_abspath, entry->name, scratch_pool); 400251881Speter 401251881Speter SVN_ERR(svn_wc__db_read_info( 402251881Speter &status, 403251881Speter &kind, 404251881Speter &entry->revision, 405251881Speter &repos_relpath, 406251881Speter &entry->repos, 407251881Speter &entry->uuid, 408251881Speter &entry->cmt_rev, 409251881Speter &entry->cmt_date, 410251881Speter &entry->cmt_author, 411251881Speter &entry->depth, 412251881Speter &checksum, 413251881Speter NULL, 414251881Speter &original_repos_relpath, 415251881Speter &original_root_url, 416251881Speter NULL, 417251881Speter &entry->copyfrom_rev, 418251881Speter &lock, 419251881Speter &translated_size, 420251881Speter &entry->text_time, 421251881Speter &entry->changelist, 422251881Speter &conflicted, 423251881Speter NULL /* op_root */, 424251881Speter &entry->has_props /* have_props */, 425251881Speter &entry->has_prop_mods /* props_mod */, 426251881Speter &have_base, 427251881Speter &have_more_work, 428251881Speter NULL /* have_work */, 429251881Speter db, 430251881Speter entry_abspath, 431251881Speter result_pool, 432251881Speter scratch_pool)); 433251881Speter 434251881Speter if (entry->has_prop_mods) 435251881Speter entry->has_props = TRUE; 436251881Speter 437251881Speter if (strcmp(entry->name, SVN_WC_ENTRY_THIS_DIR) == 0) 438251881Speter { 439251881Speter /* get the tree conflict data. */ 440251881Speter apr_hash_t *tree_conflicts = NULL; 441251881Speter const apr_array_header_t *conflict_victims; 442251881Speter int k; 443251881Speter 444251881Speter SVN_ERR(svn_wc__db_read_conflict_victims(&conflict_victims, db, 445251881Speter dir_abspath, 446251881Speter scratch_pool, 447251881Speter scratch_pool)); 448251881Speter 449251881Speter for (k = 0; k < conflict_victims->nelts; k++) 450251881Speter { 451251881Speter int j; 452251881Speter const apr_array_header_t *child_conflicts; 453251881Speter const char *child_name; 454251881Speter const char *child_abspath; 455251881Speter 456251881Speter child_name = APR_ARRAY_IDX(conflict_victims, k, const char *); 457251881Speter child_abspath = svn_dirent_join(dir_abspath, child_name, 458251881Speter scratch_pool); 459251881Speter 460251881Speter SVN_ERR(svn_wc__read_conflicts(&child_conflicts, 461251881Speter db, child_abspath, 462251881Speter FALSE /* create tempfiles */, 463251881Speter scratch_pool, scratch_pool)); 464251881Speter 465251881Speter for (j = 0; j < child_conflicts->nelts; j++) 466251881Speter { 467251881Speter const svn_wc_conflict_description2_t *conflict = 468251881Speter APR_ARRAY_IDX(child_conflicts, j, 469251881Speter svn_wc_conflict_description2_t *); 470251881Speter 471251881Speter if (conflict->kind == svn_wc_conflict_kind_tree) 472251881Speter { 473251881Speter if (!tree_conflicts) 474251881Speter tree_conflicts = apr_hash_make(scratch_pool); 475251881Speter svn_hash_sets(tree_conflicts, child_name, conflict); 476251881Speter } 477251881Speter } 478251881Speter } 479251881Speter 480251881Speter if (tree_conflicts) 481251881Speter { 482251881Speter SVN_ERR(write_tree_conflicts(&entry->tree_conflict_data, 483251881Speter tree_conflicts, result_pool)); 484251881Speter } 485251881Speter } 486251881Speter 487251881Speter if (status == svn_wc__db_status_normal 488251881Speter || status == svn_wc__db_status_incomplete) 489251881Speter { 490251881Speter /* Plain old BASE node. */ 491251881Speter entry->schedule = svn_wc_schedule_normal; 492251881Speter 493251881Speter /* Grab inherited repository information, if necessary. */ 494251881Speter if (repos_relpath == NULL) 495251881Speter { 496251881Speter SVN_ERR(svn_wc__db_scan_base_repos(&repos_relpath, 497251881Speter &entry->repos, 498251881Speter &entry->uuid, 499251881Speter db, 500251881Speter entry_abspath, 501251881Speter result_pool, 502251881Speter scratch_pool)); 503251881Speter } 504251881Speter 505251881Speter entry->incomplete = (status == svn_wc__db_status_incomplete); 506251881Speter } 507251881Speter else if (status == svn_wc__db_status_deleted) 508251881Speter { 509251881Speter svn_node_kind_t path_kind; 510251881Speter 511251881Speter /* ### we don't have to worry about moves, so this is a delete. */ 512251881Speter entry->schedule = svn_wc_schedule_delete; 513251881Speter 514251881Speter /* If there are multiple working layers or no BASE layer, then 515251881Speter this is a WORKING delete or WORKING not-present. */ 516251881Speter if (have_more_work || !have_base) 517251881Speter entry->copied = TRUE; 518251881Speter else if (have_base && !have_more_work) 519251881Speter entry->copied = FALSE; 520251881Speter else 521251881Speter { 522251881Speter const char *work_del_abspath; 523251881Speter SVN_ERR(svn_wc__db_scan_deletion(NULL, NULL, 524251881Speter &work_del_abspath, NULL, 525251881Speter db, entry_abspath, 526251881Speter scratch_pool, scratch_pool)); 527251881Speter 528251881Speter if (work_del_abspath) 529251881Speter entry->copied = TRUE; 530251881Speter } 531251881Speter 532251881Speter /* If there is still a directory on-disk we keep it, if not it is 533251881Speter already deleted. Simple, isn't it? 534251881Speter 535251881Speter Before single-db we had to keep the administative area alive until 536251881Speter after the commit really deletes it. Setting keep alive stopped the 537251881Speter commit processing from deleting the directory. We don't delete it 538251881Speter any more, so all we have to do is provide some 'sane' value. 539251881Speter */ 540251881Speter SVN_ERR(svn_io_check_path(entry_abspath, &path_kind, scratch_pool)); 541251881Speter entry->keep_local = (path_kind == svn_node_dir); 542251881Speter } 543251881Speter else if (status == svn_wc__db_status_added) 544251881Speter { 545251881Speter svn_wc__db_status_t work_status; 546251881Speter const char *op_root_abspath; 547251881Speter const char *scanned_original_relpath; 548251881Speter svn_revnum_t original_revision; 549251881Speter 550251881Speter /* For child nodes, pick up the parent's revision. */ 551251881Speter if (*entry->name != '\0') 552251881Speter { 553251881Speter assert(parent_entry != NULL); 554251881Speter assert(entry->revision == SVN_INVALID_REVNUM); 555251881Speter 556251881Speter entry->revision = parent_entry->revision; 557251881Speter } 558251881Speter 559251881Speter if (have_base) 560251881Speter { 561251881Speter svn_wc__db_status_t base_status; 562251881Speter 563251881Speter /* ENTRY->REVISION is overloaded. When a node is schedule-add 564251881Speter or -replace, then REVISION refers to the BASE node's revision 565251881Speter that is being overwritten. We need to fetch it now. */ 566251881Speter SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, 567251881Speter &entry->revision, 568251881Speter NULL, NULL, NULL, 569251881Speter NULL, NULL, NULL, 570251881Speter NULL, NULL, NULL, 571251881Speter NULL, NULL, NULL, NULL, 572251881Speter db, entry_abspath, 573251881Speter scratch_pool, 574251881Speter scratch_pool)); 575251881Speter 576251881Speter if (base_status == svn_wc__db_status_not_present) 577251881Speter { 578251881Speter /* The underlying node is DELETED in this revision. */ 579251881Speter entry->deleted = TRUE; 580251881Speter 581251881Speter /* This is an add since there isn't a node to replace. */ 582251881Speter entry->schedule = svn_wc_schedule_add; 583251881Speter } 584251881Speter else 585251881Speter entry->schedule = svn_wc_schedule_replace; 586251881Speter } 587251881Speter else 588251881Speter { 589251881Speter /* There is NO 'not-present' BASE_NODE for this node. 590251881Speter Therefore, we are looking at some kind of add/copy 591251881Speter rather than a replace. */ 592251881Speter 593251881Speter /* ### if this looks like a plain old add, then rev=0. */ 594251881Speter if (!SVN_IS_VALID_REVNUM(entry->copyfrom_rev) 595251881Speter && !SVN_IS_VALID_REVNUM(entry->cmt_rev)) 596251881Speter entry->revision = 0; 597251881Speter 598251881Speter entry->schedule = svn_wc_schedule_add; 599251881Speter } 600251881Speter 601251881Speter /* If we don't have "real" data from the entry (obstruction), 602251881Speter then we cannot begin a scan for data. The original node may 603251881Speter have important data. Set up stuff to kill that idea off, 604251881Speter and finish up this entry. */ 605251881Speter { 606251881Speter SVN_ERR(svn_wc__db_scan_addition(&work_status, 607251881Speter &op_root_abspath, 608251881Speter &repos_relpath, 609251881Speter &entry->repos, 610251881Speter &entry->uuid, 611251881Speter &scanned_original_relpath, 612251881Speter NULL, NULL, /* original_root|uuid */ 613251881Speter &original_revision, 614251881Speter db, 615251881Speter entry_abspath, 616251881Speter result_pool, scratch_pool)); 617251881Speter 618251881Speter /* In wc.db we want to keep the valid revision of the not-present 619251881Speter BASE_REV, but when we used entries we set the revision to 0 620251881Speter when adding a new node over a not present base node. */ 621251881Speter if (work_status == svn_wc__db_status_added 622251881Speter && entry->deleted) 623251881Speter entry->revision = 0; 624251881Speter } 625251881Speter 626251881Speter if (!SVN_IS_VALID_REVNUM(entry->cmt_rev) 627251881Speter && scanned_original_relpath == NULL) 628251881Speter { 629251881Speter /* There is NOT a last-changed revision (last-changed date and 630251881Speter author may be unknown, but we can always check the rev). 631251881Speter The absence of a revision implies this node was added WITHOUT 632251881Speter any history. Avoid the COPIED checks in the else block. */ 633251881Speter /* ### scan_addition may need to be updated to avoid returning 634251881Speter ### status_copied in this case. */ 635251881Speter } 636251881Speter /* For backwards-compatiblity purposes we treat moves just like 637251881Speter * regular copies. */ 638251881Speter else if (work_status == svn_wc__db_status_copied || 639251881Speter work_status == svn_wc__db_status_moved_here) 640251881Speter { 641251881Speter entry->copied = TRUE; 642251881Speter 643251881Speter /* If this is a child of a copied subtree, then it should be 644251881Speter schedule_normal. */ 645251881Speter if (original_repos_relpath == NULL) 646251881Speter { 647251881Speter /* ### what if there is a BASE node under there? */ 648251881Speter entry->schedule = svn_wc_schedule_normal; 649251881Speter } 650251881Speter 651251881Speter /* Copied nodes need to mirror their copyfrom_rev, if they 652251881Speter don't have a revision of their own already. */ 653251881Speter if (!SVN_IS_VALID_REVNUM(entry->revision) 654251881Speter || entry->revision == 0 /* added */) 655251881Speter entry->revision = original_revision; 656251881Speter } 657251881Speter 658251881Speter /* Does this node have copyfrom_* information? */ 659251881Speter if (scanned_original_relpath != NULL) 660251881Speter { 661251881Speter svn_boolean_t is_copied_child; 662251881Speter svn_boolean_t is_mixed_rev = FALSE; 663251881Speter 664251881Speter SVN_ERR_ASSERT(work_status == svn_wc__db_status_copied || 665251881Speter work_status == svn_wc__db_status_moved_here); 666251881Speter 667251881Speter /* If this node inherits copyfrom information from an 668251881Speter ancestor node, then it must be a copied child. */ 669251881Speter is_copied_child = (original_repos_relpath == NULL); 670251881Speter 671251881Speter /* If this node has copyfrom information on it, then it may 672251881Speter be an actual copy-root, or it could be participating in 673251881Speter a mixed-revision copied tree. So if we don't already know 674251881Speter this is a copied child, then we need to look for this 675251881Speter mixed-revision situation. */ 676251881Speter if (!is_copied_child) 677251881Speter { 678251881Speter const char *parent_abspath; 679251881Speter svn_error_t *err; 680251881Speter const char *parent_repos_relpath; 681251881Speter const char *parent_root_url; 682251881Speter 683251881Speter /* When we insert entries into the database, we will 684251881Speter construct additional copyfrom records for mixed-revision 685251881Speter copies. The old entries would simply record the different 686251881Speter revision in the entry->revision field. That is not 687251881Speter available within wc-ng, so additional copies are made 688251881Speter (see the logic inside write_entry()). However, when 689251881Speter reading these back *out* of the database, the additional 690251881Speter copies look like new "Added" nodes rather than a simple 691251881Speter mixed-rev working copy. 692251881Speter 693251881Speter That would be a behavior change if we did not compensate. 694251881Speter If there is copyfrom information for this node, then the 695251881Speter code below looks at the parent to detect if it *also* has 696251881Speter copyfrom information, and if the copyfrom_url would align 697251881Speter properly. If it *does*, then we omit storing copyfrom_url 698251881Speter and copyfrom_rev (ie. inherit the copyfrom info like a 699251881Speter normal child), and update entry->revision with the 700251881Speter copyfrom_rev in order to (re)create the mixed-rev copied 701251881Speter subtree that was originally presented for storage. */ 702251881Speter 703251881Speter /* Get the copyfrom information from our parent. 704251881Speter 705251881Speter Note that the parent could be added/copied/moved-here. 706251881Speter There is no way for it to be deleted/moved-away and 707251881Speter have *this* node appear as copied. */ 708251881Speter parent_abspath = svn_dirent_dirname(entry_abspath, 709251881Speter scratch_pool); 710251881Speter err = svn_wc__db_scan_addition(NULL, 711251881Speter &op_root_abspath, 712251881Speter NULL, NULL, NULL, 713251881Speter &parent_repos_relpath, 714251881Speter &parent_root_url, 715251881Speter NULL, NULL, 716251881Speter db, parent_abspath, 717251881Speter scratch_pool, 718251881Speter scratch_pool); 719251881Speter if (err) 720251881Speter { 721251881Speter if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 722251881Speter return svn_error_trace(err); 723251881Speter svn_error_clear(err); 724251881Speter } 725251881Speter else if (parent_root_url != NULL 726251881Speter && strcmp(original_root_url, parent_root_url) == 0) 727251881Speter { 728251881Speter const char *relpath_to_entry = svn_dirent_is_child( 729251881Speter op_root_abspath, entry_abspath, NULL); 730251881Speter const char *entry_repos_relpath = svn_relpath_join( 731251881Speter parent_repos_relpath, relpath_to_entry, scratch_pool); 732251881Speter 733251881Speter /* The copyfrom repos roots matched. 734251881Speter 735251881Speter Now we look to see if the copyfrom path of the parent 736251881Speter would align with our own path. If so, then it means 737251881Speter this copyfrom was spontaneously created and inserted 738251881Speter for mixed-rev purposes and can be eliminated without 739251881Speter changing the semantics of a mixed-rev copied subtree. 740251881Speter 741251881Speter See notes/api-errata/wc003.txt for some additional 742251881Speter detail, and potential issues. */ 743251881Speter if (strcmp(entry_repos_relpath, 744251881Speter original_repos_relpath) == 0) 745251881Speter { 746251881Speter is_copied_child = TRUE; 747251881Speter is_mixed_rev = TRUE; 748251881Speter } 749251881Speter } 750251881Speter } 751251881Speter 752251881Speter if (is_copied_child) 753251881Speter { 754251881Speter /* We won't be settig the copyfrom_url, yet need to 755251881Speter clear out the copyfrom_rev. Thus, this node becomes a 756251881Speter child of a copied subtree (rather than its own root). */ 757251881Speter entry->copyfrom_rev = SVN_INVALID_REVNUM; 758251881Speter 759251881Speter /* Children in a copied subtree are schedule normal 760251881Speter since we don't plan to actually *do* anything with 761251881Speter them. Their operation is implied by ancestors. */ 762251881Speter entry->schedule = svn_wc_schedule_normal; 763251881Speter 764251881Speter /* And *finally* we turn this entry into the mixed 765251881Speter revision node that it was intended to be. This 766251881Speter node's revision is taken from the copyfrom record 767251881Speter that we spontaneously constructed. */ 768251881Speter if (is_mixed_rev) 769251881Speter entry->revision = original_revision; 770251881Speter } 771251881Speter else if (original_repos_relpath != NULL) 772251881Speter { 773251881Speter entry->copyfrom_url = 774251881Speter svn_path_url_add_component2(original_root_url, 775251881Speter original_repos_relpath, 776251881Speter result_pool); 777251881Speter } 778251881Speter else 779251881Speter { 780251881Speter /* NOTE: if original_repos_relpath == NULL, then the 781251881Speter second call to scan_addition() will not have occurred. 782251881Speter Thus, this use of OP_ROOT_ABSPATH still contains the 783251881Speter original value where we fetched a value for 784251881Speter SCANNED_REPOS_RELPATH. */ 785251881Speter const char *relpath_to_entry = svn_dirent_is_child( 786251881Speter op_root_abspath, entry_abspath, NULL); 787251881Speter const char *entry_repos_relpath = svn_relpath_join( 788251881Speter scanned_original_relpath, relpath_to_entry, scratch_pool); 789251881Speter 790251881Speter entry->copyfrom_url = 791251881Speter svn_path_url_add_component2(original_root_url, 792251881Speter entry_repos_relpath, 793251881Speter result_pool); 794251881Speter } 795251881Speter } 796251881Speter } 797251881Speter else if (status == svn_wc__db_status_not_present) 798251881Speter { 799251881Speter /* ### buh. 'deleted' nodes are actually supposed to be 800251881Speter ### schedule "normal" since we aren't going to actually *do* 801251881Speter ### anything to this node at commit time. */ 802251881Speter entry->schedule = svn_wc_schedule_normal; 803251881Speter entry->deleted = TRUE; 804251881Speter } 805251881Speter else if (status == svn_wc__db_status_server_excluded) 806251881Speter { 807251881Speter entry->absent = TRUE; 808251881Speter } 809251881Speter else if (status == svn_wc__db_status_excluded) 810251881Speter { 811251881Speter entry->schedule = svn_wc_schedule_normal; 812251881Speter entry->depth = svn_depth_exclude; 813251881Speter } 814251881Speter else 815251881Speter { 816251881Speter /* ### we should have handled all possible status values. */ 817251881Speter SVN_ERR_MALFUNCTION(); 818251881Speter } 819251881Speter 820251881Speter /* ### higher levels want repos information about deleted nodes, even 821251881Speter ### tho they are not "part of" a repository any more. */ 822251881Speter if (entry->schedule == svn_wc_schedule_delete) 823251881Speter { 824251881Speter SVN_ERR(get_info_for_deleted(entry, 825251881Speter &kind, 826251881Speter &repos_relpath, 827251881Speter &checksum, 828251881Speter &lock, 829251881Speter db, entry_abspath, 830251881Speter parent_entry, 831251881Speter have_base, have_more_work, 832251881Speter result_pool, scratch_pool)); 833251881Speter } 834251881Speter 835251881Speter /* ### default to the infinite depth if we don't know it. */ 836251881Speter if (entry->depth == svn_depth_unknown) 837251881Speter entry->depth = svn_depth_infinity; 838251881Speter 839251881Speter if (kind == svn_node_dir) 840251881Speter entry->kind = svn_node_dir; 841251881Speter else if (kind == svn_node_file) 842251881Speter entry->kind = svn_node_file; 843251881Speter else if (kind == svn_node_symlink) 844251881Speter entry->kind = svn_node_file; /* ### no symlink kind */ 845251881Speter else 846251881Speter entry->kind = svn_node_unknown; 847251881Speter 848251881Speter /* We should always have a REPOS_RELPATH, except for: 849251881Speter - deleted nodes 850251881Speter - certain obstructed nodes 851251881Speter - not-present nodes 852251881Speter - absent nodes 853251881Speter - excluded nodes 854251881Speter 855251881Speter ### the last three should probably have an "implied" REPOS_RELPATH 856251881Speter */ 857251881Speter SVN_ERR_ASSERT(repos_relpath != NULL 858251881Speter || entry->schedule == svn_wc_schedule_delete 859251881Speter || status == svn_wc__db_status_not_present 860251881Speter || status == svn_wc__db_status_server_excluded 861251881Speter || status == svn_wc__db_status_excluded); 862251881Speter if (repos_relpath) 863251881Speter entry->url = svn_path_url_add_component2(entry->repos, 864251881Speter repos_relpath, 865251881Speter result_pool); 866251881Speter 867251881Speter if (checksum) 868251881Speter { 869251881Speter /* We got a SHA-1, get the corresponding MD-5. */ 870251881Speter if (checksum->kind != svn_checksum_md5) 871251881Speter SVN_ERR(svn_wc__db_pristine_get_md5(&checksum, db, 872251881Speter entry_abspath, checksum, 873251881Speter scratch_pool, scratch_pool)); 874251881Speter 875251881Speter SVN_ERR_ASSERT(checksum->kind == svn_checksum_md5); 876251881Speter entry->checksum = svn_checksum_to_cstring(checksum, result_pool); 877251881Speter } 878251881Speter 879251881Speter if (conflicted) 880251881Speter { 881251881Speter svn_skel_t *conflict; 882251881Speter svn_boolean_t text_conflicted; 883251881Speter svn_boolean_t prop_conflicted; 884251881Speter SVN_ERR(svn_wc__db_read_conflict(&conflict, db, entry_abspath, 885251881Speter scratch_pool, scratch_pool)); 886251881Speter 887251881Speter SVN_ERR(svn_wc__conflict_read_info(NULL, NULL, &text_conflicted, 888251881Speter &prop_conflicted, NULL, 889251881Speter db, dir_abspath, conflict, 890251881Speter scratch_pool, scratch_pool)); 891251881Speter 892251881Speter if (text_conflicted) 893251881Speter { 894251881Speter const char *my_abspath; 895251881Speter const char *their_old_abspath; 896251881Speter const char *their_abspath; 897251881Speter SVN_ERR(svn_wc__conflict_read_text_conflict(&my_abspath, 898251881Speter &their_old_abspath, 899251881Speter &their_abspath, 900251881Speter db, dir_abspath, 901251881Speter conflict, scratch_pool, 902251881Speter scratch_pool)); 903251881Speter 904251881Speter if (my_abspath) 905251881Speter entry->conflict_wrk = svn_dirent_basename(my_abspath, result_pool); 906251881Speter 907251881Speter if (their_old_abspath) 908251881Speter entry->conflict_old = svn_dirent_basename(their_old_abspath, 909251881Speter result_pool); 910251881Speter 911251881Speter if (their_abspath) 912251881Speter entry->conflict_new = svn_dirent_basename(their_abspath, 913251881Speter result_pool); 914251881Speter } 915251881Speter 916251881Speter if (prop_conflicted) 917251881Speter { 918251881Speter const char *prej_abspath; 919251881Speter 920251881Speter SVN_ERR(svn_wc__conflict_read_prop_conflict(&prej_abspath, NULL, 921251881Speter NULL, NULL, NULL, 922251881Speter db, dir_abspath, 923251881Speter conflict, scratch_pool, 924251881Speter scratch_pool)); 925251881Speter 926251881Speter if (prej_abspath) 927251881Speter entry->prejfile = svn_dirent_basename(prej_abspath, result_pool); 928251881Speter } 929251881Speter } 930251881Speter 931251881Speter if (lock) 932251881Speter { 933251881Speter entry->lock_token = lock->token; 934251881Speter entry->lock_owner = lock->owner; 935251881Speter entry->lock_comment = lock->comment; 936251881Speter entry->lock_creation_date = lock->date; 937251881Speter } 938251881Speter 939251881Speter /* Let's check for a file external. ugh. */ 940251881Speter if (status == svn_wc__db_status_normal 941251881Speter && kind == svn_node_file) 942251881Speter SVN_ERR(check_file_external(entry, db, entry_abspath, dir_abspath, 943251881Speter result_pool, scratch_pool)); 944251881Speter 945251881Speter entry->working_size = translated_size; 946251881Speter 947251881Speter *new_entry = entry; 948251881Speter 949251881Speter return SVN_NO_ERROR; 950251881Speter} 951251881Speter 952251881Speter/* Read entries for PATH/LOCAL_ABSPATH from DB. The entries 953251881Speter will be allocated in RESULT_POOL, with temporary allocations in 954251881Speter SCRATCH_POOL. The entries are returned in RESULT_ENTRIES. */ 955251881Speterstatic svn_error_t * 956251881Speterread_entries_new(apr_hash_t **result_entries, 957251881Speter svn_wc__db_t *db, 958251881Speter const char *local_abspath, 959251881Speter apr_pool_t *result_pool, 960251881Speter apr_pool_t *scratch_pool) 961251881Speter{ 962251881Speter apr_hash_t *entries; 963251881Speter const apr_array_header_t *children; 964251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 965251881Speter int i; 966251881Speter const svn_wc_entry_t *parent_entry; 967251881Speter apr_int64_t wc_id = 1; /* ### hacky. should remove. */ 968251881Speter 969251881Speter entries = apr_hash_make(result_pool); 970251881Speter 971251881Speter SVN_ERR(read_one_entry(&parent_entry, db, wc_id, local_abspath, 972251881Speter "" /* name */, 973251881Speter NULL /* parent_entry */, 974251881Speter result_pool, iterpool)); 975251881Speter svn_hash_sets(entries, "", parent_entry); 976251881Speter 977251881Speter /* Use result_pool so that the child names (used by reference, rather 978251881Speter than copied) appear in result_pool. */ 979251881Speter SVN_ERR(svn_wc__db_read_children(&children, db, 980251881Speter local_abspath, 981251881Speter result_pool, iterpool)); 982251881Speter for (i = children->nelts; i--; ) 983251881Speter { 984251881Speter const char *name = APR_ARRAY_IDX(children, i, const char *); 985251881Speter const svn_wc_entry_t *entry; 986251881Speter 987251881Speter svn_pool_clear(iterpool); 988251881Speter 989251881Speter SVN_ERR(read_one_entry(&entry, 990251881Speter db, wc_id, local_abspath, name, parent_entry, 991251881Speter result_pool, iterpool)); 992251881Speter svn_hash_sets(entries, entry->name, entry); 993251881Speter } 994251881Speter 995251881Speter svn_pool_destroy(iterpool); 996251881Speter 997251881Speter *result_entries = entries; 998251881Speter 999251881Speter return SVN_NO_ERROR; 1000251881Speter} 1001251881Speter 1002251881Speter 1003251881Speter/* Read a pair of entries from wc_db in the directory DIR_ABSPATH. Return 1004251881Speter the directory's entry in *PARENT_ENTRY and NAME's entry in *ENTRY. The 1005251881Speter two returned pointers will be the same if NAME=="" ("this dir"). 1006251881Speter 1007251881Speter The parent entry must exist. 1008251881Speter 1009251881Speter The requested entry MAY exist. If it does not, then NULL will be returned. 1010251881Speter 1011251881Speter The resulting entries are allocated in RESULT_POOL, and all temporary 1012251881Speter allocations are made in SCRATCH_POOL. */ 1013251881Speterstatic svn_error_t * 1014251881Speterread_entry_pair(const svn_wc_entry_t **parent_entry, 1015251881Speter const svn_wc_entry_t **entry, 1016251881Speter svn_wc__db_t *db, 1017251881Speter const char *dir_abspath, 1018251881Speter const char *name, 1019251881Speter apr_pool_t *result_pool, 1020251881Speter apr_pool_t *scratch_pool) 1021251881Speter{ 1022251881Speter apr_int64_t wc_id = 1; /* ### hacky. should remove. */ 1023251881Speter 1024251881Speter SVN_ERR(read_one_entry(parent_entry, db, wc_id, dir_abspath, 1025251881Speter "" /* name */, 1026251881Speter NULL /* parent_entry */, 1027251881Speter result_pool, scratch_pool)); 1028251881Speter 1029251881Speter /* If we need the entry for "this dir", then return the parent_entry 1030251881Speter in both outputs. Otherwise, read the child node. */ 1031251881Speter if (*name == '\0') 1032251881Speter { 1033251881Speter /* If the retrieved node is a FILE, then we have a problem. We asked 1034251881Speter for a directory. This implies there is an obstructing, unversioned 1035251881Speter directory where a FILE should be. We navigated from the obstructing 1036251881Speter subdir up to the parent dir, then returned the FILE found there. 1037251881Speter 1038251881Speter Let's return WC_MISSING cuz the caller thought we had a dir, but 1039251881Speter that (versioned subdir) isn't there. */ 1040251881Speter if ((*parent_entry)->kind == svn_node_file) 1041251881Speter { 1042251881Speter *parent_entry = NULL; 1043251881Speter return svn_error_createf(SVN_ERR_WC_MISSING, NULL, 1044251881Speter _("'%s' is not a versioned working copy"), 1045251881Speter svn_dirent_local_style(dir_abspath, 1046251881Speter scratch_pool)); 1047251881Speter } 1048251881Speter 1049251881Speter *entry = *parent_entry; 1050251881Speter } 1051251881Speter else 1052251881Speter { 1053251881Speter const apr_array_header_t *children; 1054251881Speter int i; 1055251881Speter 1056251881Speter /* Default to not finding the child. */ 1057251881Speter *entry = NULL; 1058251881Speter 1059251881Speter /* Determine whether the parent KNOWS about this child. If it does 1060251881Speter not, then we should not attempt to look for it. 1061251881Speter 1062251881Speter For example: the parent doesn't "know" about the child, but the 1063251881Speter versioned directory *does* exist on disk. We don't want to look 1064251881Speter into that subdir. */ 1065251881Speter SVN_ERR(svn_wc__db_read_children(&children, db, dir_abspath, 1066251881Speter scratch_pool, scratch_pool)); 1067251881Speter for (i = children->nelts; i--; ) 1068251881Speter { 1069251881Speter const char *child = APR_ARRAY_IDX(children, i, const char *); 1070251881Speter 1071251881Speter if (strcmp(child, name) == 0) 1072251881Speter { 1073251881Speter svn_error_t *err; 1074251881Speter 1075251881Speter err = read_one_entry(entry, 1076251881Speter db, wc_id, dir_abspath, name, *parent_entry, 1077251881Speter result_pool, scratch_pool); 1078251881Speter if (err) 1079251881Speter { 1080251881Speter if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 1081251881Speter return svn_error_trace(err); 1082251881Speter 1083251881Speter /* No problem. Clear the error and leave the default value 1084251881Speter of "missing". */ 1085251881Speter svn_error_clear(err); 1086251881Speter } 1087251881Speter 1088251881Speter /* Found it. No need to keep searching. */ 1089251881Speter break; 1090251881Speter } 1091251881Speter } 1092251881Speter /* if the loop ends without finding a child, then we have the default 1093251881Speter ENTRY value of NULL. */ 1094251881Speter } 1095251881Speter 1096251881Speter return SVN_NO_ERROR; 1097251881Speter} 1098251881Speter 1099251881Speter 1100251881Speter/* */ 1101251881Speterstatic svn_error_t * 1102251881Speterread_entries(apr_hash_t **entries, 1103251881Speter svn_wc__db_t *db, 1104251881Speter const char *wcroot_abspath, 1105251881Speter apr_pool_t *result_pool, 1106251881Speter apr_pool_t *scratch_pool) 1107251881Speter{ 1108251881Speter int wc_format; 1109251881Speter 1110251881Speter SVN_ERR(svn_wc__db_temp_get_format(&wc_format, db, wcroot_abspath, 1111251881Speter scratch_pool)); 1112251881Speter 1113251881Speter if (wc_format < SVN_WC__WC_NG_VERSION) 1114251881Speter return svn_error_trace(svn_wc__read_entries_old(entries, 1115251881Speter wcroot_abspath, 1116251881Speter result_pool, 1117251881Speter scratch_pool)); 1118251881Speter 1119251881Speter return svn_error_trace(read_entries_new(entries, db, wcroot_abspath, 1120251881Speter result_pool, scratch_pool)); 1121251881Speter} 1122251881Speter 1123251881Speter 1124251881Speter/* For a given LOCAL_ABSPATH, using DB, set *ADM_ABSPATH to the directory in 1125251881Speter which the entry information is located, and *ENTRY_NAME to the entry name 1126251881Speter to access that entry. 1127251881Speter 1128251881Speter KIND is as in svn_wc__get_entry(). 1129251881Speter 1130251881Speter Return the results in RESULT_POOL and use SCRATCH_POOL for temporary 1131251881Speter allocations. */ 1132251881Speterstatic svn_error_t * 1133251881Speterget_entry_access_info(const char **adm_abspath, 1134251881Speter const char **entry_name, 1135251881Speter svn_wc__db_t *db, 1136251881Speter const char *local_abspath, 1137251881Speter svn_node_kind_t kind, 1138251881Speter apr_pool_t *result_pool, 1139251881Speter apr_pool_t *scratch_pool) 1140251881Speter{ 1141251881Speter svn_wc_adm_access_t *adm_access; 1142251881Speter svn_boolean_t read_from_subdir = FALSE; 1143251881Speter 1144251881Speter /* If the caller didn't know the node kind, then stat the path. Maybe 1145251881Speter it is really there, and we can speed up the steps below. */ 1146251881Speter if (kind == svn_node_unknown) 1147251881Speter { 1148251881Speter svn_node_kind_t on_disk; 1149251881Speter 1150251881Speter /* Do we already have an access baton for LOCAL_ABSPATH? */ 1151251881Speter adm_access = svn_wc__adm_retrieve_internal2(db, local_abspath, 1152251881Speter scratch_pool); 1153251881Speter if (adm_access) 1154251881Speter { 1155251881Speter /* Sweet. The node is a directory. */ 1156251881Speter on_disk = svn_node_dir; 1157251881Speter } 1158251881Speter else 1159251881Speter { 1160251881Speter svn_boolean_t special; 1161251881Speter 1162251881Speter /* What's on disk? */ 1163251881Speter SVN_ERR(svn_io_check_special_path(local_abspath, &on_disk, &special, 1164251881Speter scratch_pool)); 1165251881Speter } 1166251881Speter 1167251881Speter if (on_disk != svn_node_dir) 1168251881Speter { 1169251881Speter /* If this is *anything* besides a directory (FILE, NONE, or 1170251881Speter UNKNOWN), then we cannot treat it as a versioned directory 1171251881Speter containing entries to read. Leave READ_FROM_SUBDIR as FALSE, 1172251881Speter so that the parent will be examined. 1173251881Speter 1174251881Speter For NONE and UNKNOWN, it may be that metadata exists for the 1175251881Speter node, even though on-disk is unhelpful. 1176251881Speter 1177251881Speter If NEED_PARENT_STUB is TRUE, and the entry is not a DIRECTORY, 1178251881Speter then we'll error. 1179251881Speter 1180251881Speter If NEED_PARENT_STUB if FALSE, and we successfully read a stub, 1181251881Speter then this on-disk node is obstructing the read. */ 1182251881Speter } 1183251881Speter else 1184251881Speter { 1185251881Speter /* We found a directory for this UNKNOWN node. Determine whether 1186251881Speter we need to read inside it. */ 1187251881Speter read_from_subdir = TRUE; 1188251881Speter } 1189251881Speter } 1190251881Speter else if (kind == svn_node_dir) 1191251881Speter { 1192251881Speter read_from_subdir = TRUE; 1193251881Speter } 1194251881Speter 1195251881Speter if (read_from_subdir) 1196251881Speter { 1197251881Speter /* KIND must be a DIR or UNKNOWN (and we found a subdir). We want 1198251881Speter the "real" data, so treat LOCAL_ABSPATH as a versioned directory. */ 1199251881Speter *adm_abspath = apr_pstrdup(result_pool, local_abspath); 1200251881Speter *entry_name = ""; 1201251881Speter } 1202251881Speter else 1203251881Speter { 1204251881Speter /* FILE node needs to read the parent directory. Or a DIR node 1205251881Speter needs to read from the parent to get at the stub entry. Or this 1206251881Speter is an UNKNOWN node, and we need to examine the parent. */ 1207251881Speter svn_dirent_split(adm_abspath, entry_name, local_abspath, result_pool); 1208251881Speter } 1209251881Speter 1210251881Speter return SVN_NO_ERROR; 1211251881Speter} 1212251881Speter 1213251881Speter 1214251881Spetersvn_error_t * 1215251881Spetersvn_wc__get_entry(const svn_wc_entry_t **entry, 1216251881Speter svn_wc__db_t *db, 1217251881Speter const char *local_abspath, 1218251881Speter svn_boolean_t allow_unversioned, 1219251881Speter svn_node_kind_t kind, 1220251881Speter apr_pool_t *result_pool, 1221251881Speter apr_pool_t *scratch_pool) 1222251881Speter{ 1223251881Speter const char *dir_abspath; 1224251881Speter const char *entry_name; 1225251881Speter 1226251881Speter SVN_ERR(get_entry_access_info(&dir_abspath, &entry_name, db, local_abspath, 1227251881Speter kind, scratch_pool, scratch_pool)); 1228251881Speter 1229251881Speter { 1230251881Speter const svn_wc_entry_t *parent_entry; 1231251881Speter svn_error_t *err; 1232251881Speter 1233251881Speter /* NOTE: if KIND is UNKNOWN and we decided to examine the *parent* 1234251881Speter directory, then it is possible we moved out of the working copy. 1235251881Speter If the on-disk node is a DIR, and we asked for a stub, then we 1236251881Speter obviously can't provide that (parent has no info). If the on-disk 1237251881Speter node is a FILE/NONE/UNKNOWN, then it is obstructing the real 1238251881Speter LOCAL_ABSPATH (or it was never a versioned item). In all these 1239251881Speter cases, the read_entries() will (properly) throw an error. 1240251881Speter 1241251881Speter NOTE: if KIND is a DIR and we asked for the real data, but it is 1242251881Speter obstructed on-disk by some other node kind (NONE, FILE, UNKNOWN), 1243251881Speter then this will throw an error. */ 1244251881Speter 1245251881Speter err = read_entry_pair(&parent_entry, entry, 1246251881Speter db, dir_abspath, entry_name, 1247251881Speter result_pool, scratch_pool); 1248251881Speter if (err) 1249251881Speter { 1250251881Speter if (err->apr_err != SVN_ERR_WC_MISSING || kind != svn_node_unknown 1251251881Speter || *entry_name != '\0') 1252251881Speter return svn_error_trace(err); 1253251881Speter svn_error_clear(err); 1254251881Speter 1255251881Speter /* The caller didn't know the node type, we saw a directory there, 1256251881Speter we attempted to read IN that directory, and then wc_db reports 1257251881Speter that it is NOT a working copy directory. It is possible that 1258251881Speter one of two things has happened: 1259251881Speter 1260251881Speter 1) a directory is obstructing a file in the parent 1261251881Speter 2) the (versioned) directory's contents have been removed 1262251881Speter 1263251881Speter Let's assume situation (1); if that is true, then we can just 1264251881Speter return the newly-found data. 1265251881Speter 1266251881Speter If we assumed (2), then a valid result still won't help us 1267251881Speter since the caller asked for the actual contents, not the stub 1268251881Speter (which is why we read *into* the directory). However, if we 1269251881Speter assume (1) and get back a stub, then we have verified a 1270251881Speter missing, versioned directory, and can return an error 1271251881Speter describing that. 1272251881Speter 1273251881Speter Redo the fetch, but "insist" we are trying to find a file. 1274251881Speter This will read from the parent directory of the "file". */ 1275251881Speter err = svn_wc__get_entry(entry, db, local_abspath, allow_unversioned, 1276251881Speter svn_node_file, result_pool, scratch_pool); 1277251881Speter if (err == SVN_NO_ERROR) 1278251881Speter return SVN_NO_ERROR; 1279251881Speter if (err->apr_err != SVN_ERR_NODE_UNEXPECTED_KIND) 1280251881Speter return svn_error_trace(err); 1281251881Speter svn_error_clear(err); 1282251881Speter 1283251881Speter /* We asked for a FILE, but the node found is a DIR. Thus, we 1284251881Speter are looking at a stub. Originally, we tried to read into the 1285251881Speter subdir because NEED_PARENT_STUB is FALSE. The stub we just 1286251881Speter read is not going to work for the caller, so inform them of 1287251881Speter the missing subdirectory. */ 1288251881Speter SVN_ERR_ASSERT(*entry != NULL && (*entry)->kind == svn_node_dir); 1289251881Speter return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, 1290251881Speter _("Admin area of '%s' is missing"), 1291251881Speter svn_dirent_local_style(local_abspath, 1292251881Speter scratch_pool)); 1293251881Speter } 1294251881Speter } 1295251881Speter 1296251881Speter if (*entry == NULL) 1297251881Speter { 1298251881Speter if (allow_unversioned) 1299251881Speter return SVN_NO_ERROR; 1300251881Speter return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, 1301251881Speter _("'%s' is not under version control"), 1302251881Speter svn_dirent_local_style(local_abspath, 1303251881Speter scratch_pool)); 1304251881Speter } 1305251881Speter 1306251881Speter /* The caller had the wrong information. */ 1307251881Speter if ((kind == svn_node_file && (*entry)->kind != svn_node_file) 1308251881Speter || (kind == svn_node_dir && (*entry)->kind != svn_node_dir)) 1309251881Speter return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL, 1310251881Speter _("'%s' is not of the right kind"), 1311251881Speter svn_dirent_local_style(local_abspath, 1312251881Speter scratch_pool)); 1313251881Speter 1314251881Speter return SVN_NO_ERROR; 1315251881Speter} 1316251881Speter 1317251881Speter/* TODO ### Rewrite doc string to mention ENTRIES_ALL; not ADM_ACCESS. 1318251881Speter 1319251881Speter Prune the deleted entries from the cached entries in ADM_ACCESS, and 1320251881Speter return that collection in *ENTRIES_PRUNED. SCRATCH_POOL is used for local, 1321251881Speter short term, memory allocation, RESULT_POOL for permanent stuff. */ 1322251881Speterstatic svn_error_t * 1323251881Speterprune_deleted(apr_hash_t **entries_pruned, 1324251881Speter apr_hash_t *entries_all, 1325251881Speter apr_pool_t *result_pool, 1326251881Speter apr_pool_t *scratch_pool) 1327251881Speter{ 1328251881Speter apr_hash_index_t *hi; 1329251881Speter 1330251881Speter if (!entries_all) 1331251881Speter { 1332251881Speter *entries_pruned = NULL; 1333251881Speter return SVN_NO_ERROR; 1334251881Speter } 1335251881Speter 1336251881Speter /* I think it will be common for there to be no deleted entries, so 1337251881Speter it is worth checking for that case as we can optimise it. */ 1338251881Speter for (hi = apr_hash_first(scratch_pool, entries_all); 1339251881Speter hi; 1340251881Speter hi = apr_hash_next(hi)) 1341251881Speter { 1342251881Speter svn_boolean_t hidden; 1343251881Speter 1344251881Speter SVN_ERR(svn_wc__entry_is_hidden(&hidden, 1345251881Speter svn__apr_hash_index_val(hi))); 1346251881Speter if (hidden) 1347251881Speter break; 1348251881Speter } 1349251881Speter 1350251881Speter if (! hi) 1351251881Speter { 1352251881Speter /* There are no deleted entries, so we can use the full hash */ 1353251881Speter *entries_pruned = entries_all; 1354251881Speter return SVN_NO_ERROR; 1355251881Speter } 1356251881Speter 1357251881Speter /* Construct pruned hash without deleted entries */ 1358251881Speter *entries_pruned = apr_hash_make(result_pool); 1359251881Speter for (hi = apr_hash_first(scratch_pool, entries_all); 1360251881Speter hi; 1361251881Speter hi = apr_hash_next(hi)) 1362251881Speter { 1363251881Speter const void *key = svn__apr_hash_index_key(hi); 1364251881Speter const svn_wc_entry_t *entry = svn__apr_hash_index_val(hi); 1365251881Speter svn_boolean_t hidden; 1366251881Speter 1367251881Speter SVN_ERR(svn_wc__entry_is_hidden(&hidden, entry)); 1368251881Speter if (!hidden) 1369251881Speter svn_hash_sets(*entries_pruned, key, entry); 1370251881Speter } 1371251881Speter 1372251881Speter return SVN_NO_ERROR; 1373251881Speter} 1374251881Speter 1375251881Speterstruct entries_read_baton_t 1376251881Speter{ 1377251881Speter apr_hash_t **new_entries; 1378251881Speter svn_wc__db_t *db; 1379251881Speter const char *local_abspath; 1380251881Speter apr_pool_t *result_pool; 1381251881Speter}; 1382251881Speter 1383251881Speterstatic svn_error_t * 1384251881Speterentries_read_txn(void *baton, svn_sqlite__db_t *db, apr_pool_t *scratch_pool) 1385251881Speter{ 1386251881Speter struct entries_read_baton_t *erb = baton; 1387251881Speter 1388251881Speter SVN_ERR(read_entries(erb->new_entries, erb->db, erb->local_abspath, 1389251881Speter erb->result_pool, scratch_pool)); 1390251881Speter 1391251881Speter return NULL; 1392251881Speter} 1393251881Speter 1394251881Spetersvn_error_t * 1395251881Spetersvn_wc__entries_read_internal(apr_hash_t **entries, 1396251881Speter svn_wc_adm_access_t *adm_access, 1397251881Speter svn_boolean_t show_hidden, 1398251881Speter apr_pool_t *pool) 1399251881Speter{ 1400251881Speter apr_hash_t *new_entries; 1401251881Speter 1402251881Speter new_entries = svn_wc__adm_access_entries(adm_access); 1403251881Speter if (! new_entries) 1404251881Speter { 1405251881Speter svn_wc__db_t *db = svn_wc__adm_get_db(adm_access); 1406251881Speter const char *local_abspath = svn_wc__adm_access_abspath(adm_access); 1407251881Speter apr_pool_t *result_pool = svn_wc__adm_access_pool_internal(adm_access); 1408251881Speter svn_sqlite__db_t *sdb; 1409251881Speter struct entries_read_baton_t erb; 1410251881Speter 1411251881Speter /* ### Use the borrow DB api to handle all calls in a single read 1412251881Speter ### transaction. This api is used extensively in our test suite 1413251881Speter ### via the entries-read application. */ 1414251881Speter 1415251881Speter SVN_ERR(svn_wc__db_temp_borrow_sdb(&sdb, db, local_abspath, pool)); 1416251881Speter 1417251881Speter erb.db = db; 1418251881Speter erb.local_abspath = local_abspath; 1419251881Speter erb.new_entries = &new_entries; 1420251881Speter erb.result_pool = result_pool; 1421251881Speter 1422251881Speter SVN_ERR(svn_sqlite__with_lock(sdb, entries_read_txn, &erb, pool)); 1423251881Speter 1424251881Speter svn_wc__adm_access_set_entries(adm_access, new_entries); 1425251881Speter } 1426251881Speter 1427251881Speter if (show_hidden) 1428251881Speter *entries = new_entries; 1429251881Speter else 1430251881Speter SVN_ERR(prune_deleted(entries, new_entries, 1431251881Speter svn_wc__adm_access_pool_internal(adm_access), 1432251881Speter pool)); 1433251881Speter 1434251881Speter return SVN_NO_ERROR; 1435251881Speter} 1436251881Speter 1437251881Spetersvn_error_t * 1438251881Spetersvn_wc_entries_read(apr_hash_t **entries, 1439251881Speter svn_wc_adm_access_t *adm_access, 1440251881Speter svn_boolean_t show_hidden, 1441251881Speter apr_pool_t *pool) 1442251881Speter{ 1443251881Speter return svn_error_trace(svn_wc__entries_read_internal(entries, adm_access, 1444251881Speter show_hidden, pool)); 1445251881Speter} 1446251881Speter 1447251881Speter/* No transaction required: called from write_entry which is itself 1448251881Speter transaction-wrapped. */ 1449251881Speterstatic svn_error_t * 1450251881Speterinsert_node(svn_sqlite__db_t *sdb, 1451251881Speter const db_node_t *node, 1452251881Speter apr_pool_t *scratch_pool) 1453251881Speter{ 1454251881Speter svn_sqlite__stmt_t *stmt; 1455251881Speter 1456251881Speter SVN_ERR_ASSERT(node->op_depth > 0 || node->repos_relpath); 1457251881Speter 1458251881Speter SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_NODE)); 1459251881Speter SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnnnsnrisnnni", 1460251881Speter node->wc_id, 1461251881Speter node->local_relpath, 1462251881Speter node->op_depth, 1463251881Speter node->parent_relpath, 1464251881Speter /* Setting depth for files? */ 1465251881Speter svn_depth_to_word(node->depth), 1466251881Speter node->changed_rev, 1467251881Speter node->changed_date, 1468251881Speter node->changed_author, 1469251881Speter node->recorded_time)); 1470251881Speter 1471251881Speter if (node->repos_relpath) 1472251881Speter { 1473251881Speter SVN_ERR(svn_sqlite__bind_int64(stmt, 5, 1474251881Speter node->repos_id)); 1475251881Speter SVN_ERR(svn_sqlite__bind_text(stmt, 6, 1476251881Speter node->repos_relpath)); 1477251881Speter SVN_ERR(svn_sqlite__bind_revnum(stmt, 7, node->revision)); 1478251881Speter } 1479251881Speter 1480251881Speter if (node->presence == svn_wc__db_status_normal) 1481251881Speter SVN_ERR(svn_sqlite__bind_text(stmt, 8, "normal")); 1482251881Speter else if (node->presence == svn_wc__db_status_not_present) 1483251881Speter SVN_ERR(svn_sqlite__bind_text(stmt, 8, "not-present")); 1484251881Speter else if (node->presence == svn_wc__db_status_base_deleted) 1485251881Speter SVN_ERR(svn_sqlite__bind_text(stmt, 8, "base-deleted")); 1486251881Speter else if (node->presence == svn_wc__db_status_incomplete) 1487251881Speter SVN_ERR(svn_sqlite__bind_text(stmt, 8, "incomplete")); 1488251881Speter else if (node->presence == svn_wc__db_status_excluded) 1489251881Speter SVN_ERR(svn_sqlite__bind_text(stmt, 8, "excluded")); 1490251881Speter else if (node->presence == svn_wc__db_status_server_excluded) 1491251881Speter SVN_ERR(svn_sqlite__bind_text(stmt, 8, "server-excluded")); 1492251881Speter 1493251881Speter if (node->kind == svn_node_none) 1494251881Speter SVN_ERR(svn_sqlite__bind_text(stmt, 10, "unknown")); 1495251881Speter else 1496251881Speter SVN_ERR(svn_sqlite__bind_text(stmt, 10, 1497251881Speter svn_node_kind_to_word(node->kind))); 1498251881Speter 1499251881Speter if (node->kind == svn_node_file) 1500251881Speter { 1501251881Speter if (!node->checksum 1502251881Speter && node->op_depth == 0 1503251881Speter && node->presence != svn_wc__db_status_not_present 1504251881Speter && node->presence != svn_wc__db_status_excluded 1505251881Speter && node->presence != svn_wc__db_status_server_excluded) 1506251881Speter return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL, 1507251881Speter _("The file '%s' has no checksum"), 1508251881Speter svn_dirent_local_style(node->local_relpath, 1509251881Speter scratch_pool)); 1510251881Speter 1511251881Speter SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, node->checksum, 1512251881Speter scratch_pool)); 1513251881Speter } 1514251881Speter 1515251881Speter if (node->properties) /* ### Never set, props done later */ 1516251881Speter SVN_ERR(svn_sqlite__bind_properties(stmt, 15, node->properties, 1517251881Speter scratch_pool)); 1518251881Speter 1519251881Speter if (node->recorded_size != SVN_INVALID_FILESIZE) 1520251881Speter SVN_ERR(svn_sqlite__bind_int64(stmt, 16, node->recorded_size)); 1521251881Speter 1522251881Speter if (node->file_external) 1523251881Speter SVN_ERR(svn_sqlite__bind_int(stmt, 20, 1)); 1524251881Speter 1525251881Speter if (node->inherited_props) 1526251881Speter SVN_ERR(svn_sqlite__bind_iprops(stmt, 23, node->inherited_props, 1527251881Speter scratch_pool)); 1528251881Speter 1529251881Speter SVN_ERR(svn_sqlite__insert(NULL, stmt)); 1530251881Speter 1531251881Speter return SVN_NO_ERROR; 1532251881Speter} 1533251881Speter 1534251881Speter 1535251881Speter/* */ 1536251881Speterstatic svn_error_t * 1537251881Speterinsert_actual_node(svn_sqlite__db_t *sdb, 1538251881Speter svn_wc__db_t *db, 1539251881Speter const char *wri_abspath, 1540251881Speter const db_actual_node_t *actual_node, 1541251881Speter apr_pool_t *scratch_pool) 1542251881Speter{ 1543251881Speter svn_sqlite__stmt_t *stmt; 1544251881Speter svn_skel_t *conflict_data = NULL; 1545251881Speter 1546251881Speter SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_ACTUAL_NODE)); 1547251881Speter 1548251881Speter SVN_ERR(svn_sqlite__bind_int64(stmt, 1, actual_node->wc_id)); 1549251881Speter SVN_ERR(svn_sqlite__bind_text(stmt, 2, actual_node->local_relpath)); 1550251881Speter SVN_ERR(svn_sqlite__bind_text(stmt, 3, actual_node->parent_relpath)); 1551251881Speter 1552251881Speter if (actual_node->properties) 1553251881Speter SVN_ERR(svn_sqlite__bind_properties(stmt, 4, actual_node->properties, 1554251881Speter scratch_pool)); 1555251881Speter 1556251881Speter if (actual_node->changelist) 1557251881Speter SVN_ERR(svn_sqlite__bind_text(stmt, 5, actual_node->changelist)); 1558251881Speter 1559251881Speter SVN_ERR(svn_wc__upgrade_conflict_skel_from_raw( 1560251881Speter &conflict_data, 1561251881Speter db, wri_abspath, 1562251881Speter actual_node->local_relpath, 1563251881Speter actual_node->conflict_old, 1564251881Speter actual_node->conflict_working, 1565251881Speter actual_node->conflict_new, 1566251881Speter actual_node->prop_reject, 1567251881Speter actual_node->tree_conflict_data, 1568251881Speter actual_node->tree_conflict_data 1569251881Speter ? strlen(actual_node->tree_conflict_data) 1570251881Speter : 0, 1571251881Speter scratch_pool, scratch_pool)); 1572251881Speter 1573251881Speter if (conflict_data) 1574251881Speter { 1575251881Speter svn_stringbuf_t *data = svn_skel__unparse(conflict_data, scratch_pool); 1576251881Speter 1577251881Speter SVN_ERR(svn_sqlite__bind_blob(stmt, 6, data->data, data->len)); 1578251881Speter } 1579251881Speter 1580251881Speter /* Execute and reset the insert clause. */ 1581251881Speter return svn_error_trace(svn_sqlite__insert(NULL, stmt)); 1582251881Speter} 1583251881Speter 1584251881Speterstatic svn_boolean_t 1585251881Speteris_switched(db_node_t *parent, 1586251881Speter db_node_t *child, 1587251881Speter apr_pool_t *scratch_pool) 1588251881Speter{ 1589251881Speter if (parent && child) 1590251881Speter { 1591251881Speter if (parent->repos_id != child->repos_id) 1592251881Speter return TRUE; 1593251881Speter 1594251881Speter if (parent->repos_relpath && child->repos_relpath) 1595251881Speter { 1596251881Speter const char *unswitched 1597251881Speter = svn_relpath_join(parent->repos_relpath, 1598251881Speter svn_relpath_basename(child->local_relpath, 1599251881Speter scratch_pool), 1600251881Speter scratch_pool); 1601251881Speter if (strcmp(unswitched, child->repos_relpath)) 1602251881Speter return TRUE; 1603251881Speter } 1604251881Speter } 1605251881Speter 1606251881Speter return FALSE; 1607251881Speter} 1608251881Speter 1609251881Speterstruct write_baton { 1610251881Speter db_node_t *base; 1611251881Speter db_node_t *work; 1612251881Speter db_node_t *below_work; 1613251881Speter apr_hash_t *tree_conflicts; 1614251881Speter}; 1615251881Speter 1616251881Speter#define WRITE_ENTRY_ASSERT(expr) \ 1617251881Speter if (!(expr)) \ 1618251881Speter return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL, \ 1619251881Speter _("Unable to upgrade '%s' at line %d"), \ 1620251881Speter svn_dirent_local_style( \ 1621251881Speter svn_dirent_join(root_abspath, \ 1622251881Speter local_relpath, \ 1623251881Speter scratch_pool), \ 1624251881Speter scratch_pool), __LINE__) 1625251881Speter 1626251881Speter/* Write the information for ENTRY to WC_DB. The WC_ID, REPOS_ID and 1627251881Speter REPOS_ROOT will all be used for writing ENTRY. 1628251881Speter ### transitioning from straight sql to using the wc_db APIs. For the 1629251881Speter ### time being, we'll need both parameters. */ 1630251881Speterstatic svn_error_t * 1631251881Speterwrite_entry(struct write_baton **entry_node, 1632251881Speter const struct write_baton *parent_node, 1633251881Speter svn_wc__db_t *db, 1634251881Speter svn_sqlite__db_t *sdb, 1635251881Speter apr_int64_t wc_id, 1636251881Speter apr_int64_t repos_id, 1637251881Speter const svn_wc_entry_t *entry, 1638251881Speter const svn_wc__text_base_info_t *text_base_info, 1639251881Speter const char *local_relpath, 1640251881Speter const char *tmp_entry_abspath, 1641251881Speter const char *root_abspath, 1642251881Speter const svn_wc_entry_t *this_dir, 1643251881Speter svn_boolean_t create_locks, 1644251881Speter apr_pool_t *result_pool, 1645251881Speter apr_pool_t *scratch_pool) 1646251881Speter{ 1647251881Speter db_node_t *base_node = NULL; 1648251881Speter db_node_t *working_node = NULL, *below_working_node = NULL; 1649251881Speter db_actual_node_t *actual_node = NULL; 1650251881Speter const char *parent_relpath; 1651251881Speter apr_hash_t *tree_conflicts; 1652251881Speter 1653251881Speter if (*local_relpath == '\0') 1654251881Speter parent_relpath = NULL; 1655251881Speter else 1656251881Speter parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool); 1657251881Speter 1658251881Speter /* This is how it should work, it doesn't work like this yet because 1659251881Speter we need proper op_depth to layer the working nodes. 1660251881Speter 1661251881Speter Using "svn add", "svn rm", "svn cp" only files can be replaced 1662251881Speter pre-wcng; directories can only be normal, deleted or added. 1663251881Speter Files cannot be replaced within a deleted directory, so replaced 1664251881Speter files can only exist in a normal directory, or a directory that 1665251881Speter is added+copied. In a normal directory a replaced file needs a 1666251881Speter base node and a working node, in an added+copied directory a 1667251881Speter replaced file needs two working nodes at different op-depths. 1668251881Speter 1669251881Speter With just the above operations the conversion for files and 1670251881Speter directories is straightforward: 1671251881Speter 1672251881Speter pre-wcng wcng 1673251881Speter parent child parent child 1674251881Speter 1675251881Speter normal normal base base 1676251881Speter add+copied normal+copied work work 1677251881Speter normal+copied normal+copied work work 1678251881Speter normal delete base base+work 1679251881Speter delete delete base+work base+work 1680251881Speter add+copied delete work work 1681251881Speter normal add base work 1682251881Speter add add work work 1683251881Speter add+copied add work work 1684251881Speter normal add+copied base work 1685251881Speter add add+copied work work 1686251881Speter add+copied add+copied work work 1687251881Speter normal replace base base+work 1688251881Speter add+copied replace work work+work 1689251881Speter normal replace+copied base base+work 1690251881Speter add+copied replace+copied work work+work 1691251881Speter 1692251881Speter However "svn merge" make this more complicated. The pre-wcng 1693251881Speter "svn merge" is capable of replacing a directory, that is it can 1694251881Speter mark the whole tree deleted, and then copy another tree on top. 1695251881Speter The entries then represent the replacing tree overlayed on the 1696251881Speter deleted tree. 1697251881Speter 1698251881Speter original replace schedule in 1699251881Speter tree tree combined tree 1700251881Speter 1701251881Speter A A replace+copied 1702251881Speter A/f delete+copied 1703251881Speter A/g A/g replace+copied 1704251881Speter A/h add+copied 1705251881Speter A/B A/B replace+copied 1706251881Speter A/B/f delete+copied 1707251881Speter A/B/g A/B/g replace+copied 1708251881Speter A/B/h add+copied 1709251881Speter A/C delete+copied 1710251881Speter A/C/f delete+copied 1711251881Speter A/D add+copied 1712251881Speter A/D/f add+copied 1713251881Speter 1714251881Speter The original tree could be normal tree, or an add+copied tree. 1715251881Speter Committing such a merge generally worked, but making further tree 1716251881Speter modifications before commit sometimes failed. 1717251881Speter 1718251881Speter The root of the replace is handled like the file replace: 1719251881Speter 1720251881Speter pre-wcng wcng 1721251881Speter parent child parent child 1722251881Speter 1723251881Speter normal replace+copied base base+work 1724251881Speter add+copied replace+copied work work+work 1725251881Speter 1726251881Speter although obviously the node is a directory rather then a file. 1727251881Speter There are then more conversion states where the parent is 1728251881Speter replaced. 1729251881Speter 1730251881Speter pre-wcng wcng 1731251881Speter parent child parent child 1732251881Speter 1733251881Speter replace+copied add [base|work]+work work 1734251881Speter replace+copied add+copied [base|work]+work work 1735251881Speter replace+copied delete+copied [base|work]+work [base|work]+work 1736251881Speter delete+copied delete+copied [base|work]+work [base|work]+work 1737251881Speter replace+copied replace+copied [base|work]+work [base|work]+work 1738251881Speter */ 1739251881Speter 1740251881Speter WRITE_ENTRY_ASSERT(parent_node || entry->schedule == svn_wc_schedule_normal); 1741251881Speter 1742251881Speter WRITE_ENTRY_ASSERT(!parent_node || parent_node->base 1743251881Speter || parent_node->below_work || parent_node->work); 1744251881Speter 1745251881Speter switch (entry->schedule) 1746251881Speter { 1747251881Speter case svn_wc_schedule_normal: 1748251881Speter if (entry->copied || 1749251881Speter (entry->depth == svn_depth_exclude 1750251881Speter && parent_node && !parent_node->base && parent_node->work)) 1751251881Speter working_node = MAYBE_ALLOC(working_node, result_pool); 1752251881Speter else 1753251881Speter base_node = MAYBE_ALLOC(base_node, result_pool); 1754251881Speter break; 1755251881Speter 1756251881Speter case svn_wc_schedule_add: 1757251881Speter working_node = MAYBE_ALLOC(working_node, result_pool); 1758251881Speter if (entry->deleted) 1759251881Speter { 1760251881Speter if (parent_node->base) 1761251881Speter base_node = MAYBE_ALLOC(base_node, result_pool); 1762251881Speter else 1763251881Speter below_working_node = MAYBE_ALLOC(below_working_node, result_pool); 1764251881Speter } 1765251881Speter break; 1766251881Speter 1767251881Speter case svn_wc_schedule_delete: 1768251881Speter working_node = MAYBE_ALLOC(working_node, result_pool); 1769251881Speter if (parent_node->base) 1770251881Speter base_node = MAYBE_ALLOC(base_node, result_pool); 1771251881Speter if (parent_node->work) 1772251881Speter below_working_node = MAYBE_ALLOC(below_working_node, result_pool); 1773251881Speter break; 1774251881Speter 1775251881Speter case svn_wc_schedule_replace: 1776251881Speter working_node = MAYBE_ALLOC(working_node, result_pool); 1777251881Speter if (parent_node->base) 1778251881Speter base_node = MAYBE_ALLOC(base_node, result_pool); 1779251881Speter else 1780251881Speter below_working_node = MAYBE_ALLOC(below_working_node, result_pool); 1781251881Speter break; 1782251881Speter } 1783251881Speter 1784251881Speter /* Something deleted in this revision means there should always be a 1785251881Speter BASE node to indicate the not-present node. */ 1786251881Speter if (entry->deleted) 1787251881Speter { 1788251881Speter WRITE_ENTRY_ASSERT(base_node || below_working_node); 1789251881Speter WRITE_ENTRY_ASSERT(!entry->incomplete); 1790251881Speter if (base_node) 1791251881Speter base_node->presence = svn_wc__db_status_not_present; 1792251881Speter else 1793251881Speter below_working_node->presence = svn_wc__db_status_not_present; 1794251881Speter } 1795251881Speter else if (entry->absent) 1796251881Speter { 1797251881Speter WRITE_ENTRY_ASSERT(base_node && !working_node && !below_working_node); 1798251881Speter WRITE_ENTRY_ASSERT(!entry->incomplete); 1799251881Speter base_node->presence = svn_wc__db_status_server_excluded; 1800251881Speter } 1801251881Speter 1802251881Speter if (entry->copied) 1803251881Speter { 1804251881Speter if (entry->copyfrom_url) 1805251881Speter { 1806251881Speter working_node->repos_id = repos_id; 1807251881Speter working_node->repos_relpath = svn_uri_skip_ancestor( 1808251881Speter this_dir->repos, entry->copyfrom_url, 1809251881Speter result_pool); 1810251881Speter working_node->revision = entry->copyfrom_rev; 1811251881Speter working_node->op_depth 1812251881Speter = svn_wc__db_op_depth_for_upgrade(local_relpath); 1813251881Speter } 1814251881Speter else if (parent_node->work && parent_node->work->repos_relpath) 1815251881Speter { 1816251881Speter working_node->repos_id = repos_id; 1817251881Speter working_node->repos_relpath 1818251881Speter = svn_relpath_join(parent_node->work->repos_relpath, 1819251881Speter svn_relpath_basename(local_relpath, NULL), 1820251881Speter result_pool); 1821251881Speter working_node->revision = parent_node->work->revision; 1822251881Speter working_node->op_depth = parent_node->work->op_depth; 1823251881Speter } 1824251881Speter else if (parent_node->below_work 1825251881Speter && parent_node->below_work->repos_relpath) 1826251881Speter { 1827251881Speter working_node->repos_id = repos_id; 1828251881Speter working_node->repos_relpath 1829251881Speter = svn_relpath_join(parent_node->below_work->repos_relpath, 1830251881Speter svn_relpath_basename(local_relpath, NULL), 1831251881Speter result_pool); 1832251881Speter working_node->revision = parent_node->below_work->revision; 1833251881Speter working_node->op_depth = parent_node->below_work->op_depth; 1834251881Speter } 1835251881Speter else 1836251881Speter return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL, 1837251881Speter _("No copyfrom URL for '%s'"), 1838251881Speter svn_dirent_local_style(local_relpath, 1839251881Speter scratch_pool)); 1840251881Speter } 1841251881Speter 1842251881Speter if (entry->conflict_old) 1843251881Speter { 1844251881Speter actual_node = MAYBE_ALLOC(actual_node, scratch_pool); 1845251881Speter if (parent_relpath && entry->conflict_old) 1846251881Speter actual_node->conflict_old = svn_relpath_join(parent_relpath, 1847251881Speter entry->conflict_old, 1848251881Speter scratch_pool); 1849251881Speter else 1850251881Speter actual_node->conflict_old = entry->conflict_old; 1851251881Speter if (parent_relpath && entry->conflict_new) 1852251881Speter actual_node->conflict_new = svn_relpath_join(parent_relpath, 1853251881Speter entry->conflict_new, 1854251881Speter scratch_pool); 1855251881Speter else 1856251881Speter actual_node->conflict_new = entry->conflict_new; 1857251881Speter if (parent_relpath && entry->conflict_wrk) 1858251881Speter actual_node->conflict_working = svn_relpath_join(parent_relpath, 1859251881Speter entry->conflict_wrk, 1860251881Speter scratch_pool); 1861251881Speter else 1862251881Speter actual_node->conflict_working = entry->conflict_wrk; 1863251881Speter } 1864251881Speter 1865251881Speter if (entry->prejfile) 1866251881Speter { 1867251881Speter actual_node = MAYBE_ALLOC(actual_node, scratch_pool); 1868251881Speter actual_node->prop_reject = svn_relpath_join((entry->kind == svn_node_dir 1869251881Speter ? local_relpath 1870251881Speter : parent_relpath), 1871251881Speter entry->prejfile, 1872251881Speter scratch_pool); 1873251881Speter } 1874251881Speter 1875251881Speter if (entry->changelist) 1876251881Speter { 1877251881Speter actual_node = MAYBE_ALLOC(actual_node, scratch_pool); 1878251881Speter actual_node->changelist = entry->changelist; 1879251881Speter } 1880251881Speter 1881251881Speter /* ### set the text_mod value? */ 1882251881Speter 1883251881Speter if (entry_node && entry->tree_conflict_data) 1884251881Speter { 1885251881Speter /* Issues #3840/#3916: 1.6 stores multiple tree conflicts on the 1886251881Speter parent node, 1.7 stores them directly on the conflited nodes. 1887251881Speter So "((skel1) (skel2))" becomes "(skel1)" and "(skel2)" */ 1888251881Speter svn_skel_t *skel; 1889251881Speter 1890251881Speter skel = svn_skel__parse(entry->tree_conflict_data, 1891251881Speter strlen(entry->tree_conflict_data), 1892251881Speter scratch_pool); 1893251881Speter tree_conflicts = apr_hash_make(result_pool); 1894251881Speter skel = skel->children; 1895251881Speter while(skel) 1896251881Speter { 1897251881Speter svn_wc_conflict_description2_t *conflict; 1898251881Speter svn_skel_t *new_skel; 1899251881Speter const char *key; 1900251881Speter 1901251881Speter /* *CONFLICT is allocated so it is safe to use a non-const pointer */ 1902251881Speter SVN_ERR(svn_wc__deserialize_conflict( 1903251881Speter (const svn_wc_conflict_description2_t**)&conflict, 1904251881Speter skel, 1905251881Speter svn_dirent_join(root_abspath, 1906251881Speter local_relpath, 1907251881Speter scratch_pool), 1908251881Speter scratch_pool, scratch_pool)); 1909251881Speter 1910251881Speter WRITE_ENTRY_ASSERT(conflict->kind == svn_wc_conflict_kind_tree); 1911251881Speter 1912251881Speter /* Fix dubious data stored by old clients, local adds don't have 1913251881Speter a repository URL. */ 1914251881Speter if (conflict->reason == svn_wc_conflict_reason_added) 1915251881Speter conflict->src_left_version = NULL; 1916251881Speter 1917251881Speter SVN_ERR(svn_wc__serialize_conflict(&new_skel, conflict, 1918251881Speter scratch_pool, scratch_pool)); 1919251881Speter 1920251881Speter /* Store in hash to be retrieved when writing the child 1921251881Speter row. */ 1922251881Speter key = svn_dirent_skip_ancestor(root_abspath, conflict->local_abspath); 1923251881Speter svn_hash_sets(tree_conflicts, apr_pstrdup(result_pool, key), 1924251881Speter svn_skel__unparse(new_skel, result_pool)->data); 1925251881Speter skel = skel->next; 1926251881Speter } 1927251881Speter } 1928251881Speter else 1929251881Speter tree_conflicts = NULL; 1930251881Speter 1931251881Speter if (parent_node && parent_node->tree_conflicts) 1932251881Speter { 1933251881Speter const char *tree_conflict_data = 1934251881Speter svn_hash_gets(parent_node->tree_conflicts, local_relpath); 1935251881Speter if (tree_conflict_data) 1936251881Speter { 1937251881Speter actual_node = MAYBE_ALLOC(actual_node, scratch_pool); 1938251881Speter actual_node->tree_conflict_data = tree_conflict_data; 1939251881Speter } 1940251881Speter 1941251881Speter /* Reset hash so that we don't write the row again when writing 1942251881Speter actual-only nodes */ 1943251881Speter svn_hash_sets(parent_node->tree_conflicts, local_relpath, NULL); 1944251881Speter } 1945251881Speter 1946251881Speter if (entry->file_external_path != NULL) 1947251881Speter { 1948251881Speter base_node = MAYBE_ALLOC(base_node, result_pool); 1949251881Speter } 1950251881Speter 1951251881Speter 1952251881Speter /* Insert the base node. */ 1953251881Speter if (base_node) 1954251881Speter { 1955251881Speter base_node->wc_id = wc_id; 1956251881Speter base_node->local_relpath = local_relpath; 1957251881Speter base_node->op_depth = 0; 1958251881Speter base_node->parent_relpath = parent_relpath; 1959251881Speter base_node->revision = entry->revision; 1960251881Speter base_node->recorded_time = entry->text_time; 1961251881Speter base_node->recorded_size = entry->working_size; 1962251881Speter 1963251881Speter if (entry->depth != svn_depth_exclude) 1964251881Speter base_node->depth = entry->depth; 1965251881Speter else 1966251881Speter { 1967251881Speter base_node->presence = svn_wc__db_status_excluded; 1968251881Speter base_node->depth = svn_depth_infinity; 1969251881Speter } 1970251881Speter 1971251881Speter if (entry->deleted) 1972251881Speter { 1973251881Speter WRITE_ENTRY_ASSERT(base_node->presence 1974251881Speter == svn_wc__db_status_not_present); 1975251881Speter /* ### should be svn_node_unknown, but let's store what we have. */ 1976251881Speter base_node->kind = entry->kind; 1977251881Speter } 1978251881Speter else if (entry->absent) 1979251881Speter { 1980251881Speter WRITE_ENTRY_ASSERT(base_node->presence 1981251881Speter == svn_wc__db_status_server_excluded); 1982251881Speter /* ### should be svn_node_unknown, but let's store what we have. */ 1983251881Speter base_node->kind = entry->kind; 1984251881Speter 1985251881Speter /* Store the most likely revision in the node to avoid 1986251881Speter base nodes without a valid revision. Of course 1987251881Speter we remember that the data is still incomplete. */ 1988251881Speter if (!SVN_IS_VALID_REVNUM(base_node->revision) && parent_node->base) 1989251881Speter base_node->revision = parent_node->base->revision; 1990251881Speter } 1991251881Speter else 1992251881Speter { 1993251881Speter base_node->kind = entry->kind; 1994251881Speter 1995251881Speter if (base_node->presence != svn_wc__db_status_excluded) 1996251881Speter { 1997251881Speter /* All subdirs are initially incomplete, they stop being 1998251881Speter incomplete when the entries file in the subdir is 1999251881Speter upgraded and remain incomplete if that doesn't happen. */ 2000251881Speter if (entry->kind == svn_node_dir 2001251881Speter && strcmp(entry->name, SVN_WC_ENTRY_THIS_DIR)) 2002251881Speter { 2003251881Speter base_node->presence = svn_wc__db_status_incomplete; 2004251881Speter 2005251881Speter /* Store the most likely revision in the node to avoid 2006251881Speter base nodes without a valid revision. Of course 2007251881Speter we remember that the data is still incomplete. */ 2008251881Speter if (parent_node->base) 2009251881Speter base_node->revision = parent_node->base->revision; 2010251881Speter } 2011251881Speter else if (entry->incomplete) 2012251881Speter { 2013251881Speter /* ### nobody should have set the presence. */ 2014251881Speter WRITE_ENTRY_ASSERT(base_node->presence 2015251881Speter == svn_wc__db_status_normal); 2016251881Speter base_node->presence = svn_wc__db_status_incomplete; 2017251881Speter } 2018251881Speter } 2019251881Speter } 2020251881Speter 2021251881Speter if (entry->kind == svn_node_dir) 2022251881Speter base_node->checksum = NULL; 2023251881Speter else 2024251881Speter { 2025251881Speter if (text_base_info && text_base_info->revert_base.sha1_checksum) 2026251881Speter base_node->checksum = text_base_info->revert_base.sha1_checksum; 2027251881Speter else if (text_base_info && text_base_info->normal_base.sha1_checksum) 2028251881Speter base_node->checksum = text_base_info->normal_base.sha1_checksum; 2029251881Speter else 2030251881Speter base_node->checksum = NULL; 2031251881Speter 2032251881Speter /* The base MD5 checksum is available in the entry, unless there 2033251881Speter * is a copied WORKING node. If possible, verify that the entry 2034251881Speter * checksum matches the base file that we found. */ 2035251881Speter if (! (working_node && entry->copied)) 2036251881Speter { 2037251881Speter svn_checksum_t *entry_md5_checksum, *found_md5_checksum; 2038251881Speter SVN_ERR(svn_checksum_parse_hex(&entry_md5_checksum, 2039251881Speter svn_checksum_md5, 2040251881Speter entry->checksum, scratch_pool)); 2041251881Speter if (text_base_info && text_base_info->revert_base.md5_checksum) 2042251881Speter found_md5_checksum = text_base_info->revert_base.md5_checksum; 2043251881Speter else if (text_base_info 2044251881Speter && text_base_info->normal_base.md5_checksum) 2045251881Speter found_md5_checksum = text_base_info->normal_base.md5_checksum; 2046251881Speter else 2047251881Speter found_md5_checksum = NULL; 2048251881Speter if (entry_md5_checksum && found_md5_checksum && 2049251881Speter !svn_checksum_match(entry_md5_checksum, found_md5_checksum)) 2050251881Speter return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL, 2051251881Speter _("Bad base MD5 checksum for '%s'; " 2052251881Speter "expected: '%s'; found '%s'; "), 2053251881Speter svn_dirent_local_style( 2054251881Speter svn_dirent_join(root_abspath, 2055251881Speter local_relpath, 2056251881Speter scratch_pool), 2057251881Speter scratch_pool), 2058251881Speter svn_checksum_to_cstring_display( 2059251881Speter entry_md5_checksum, scratch_pool), 2060251881Speter svn_checksum_to_cstring_display( 2061251881Speter found_md5_checksum, scratch_pool)); 2062251881Speter else 2063251881Speter { 2064251881Speter /* ### Not sure what conditions this should cover. */ 2065251881Speter /* SVN_ERR_ASSERT(entry->deleted || ...); */ 2066251881Speter } 2067251881Speter } 2068251881Speter } 2069251881Speter 2070251881Speter if (this_dir->repos) 2071251881Speter { 2072251881Speter base_node->repos_id = repos_id; 2073251881Speter 2074251881Speter if (entry->url != NULL) 2075251881Speter { 2076251881Speter base_node->repos_relpath = svn_uri_skip_ancestor( 2077251881Speter this_dir->repos, entry->url, 2078251881Speter result_pool); 2079251881Speter } 2080251881Speter else 2081251881Speter { 2082251881Speter const char *relpath = svn_uri_skip_ancestor(this_dir->repos, 2083251881Speter this_dir->url, 2084251881Speter scratch_pool); 2085251881Speter if (relpath == NULL || *relpath == '\0') 2086251881Speter base_node->repos_relpath = entry->name; 2087251881Speter else 2088251881Speter base_node->repos_relpath = 2089251881Speter svn_dirent_join(relpath, entry->name, result_pool); 2090251881Speter } 2091251881Speter } 2092251881Speter 2093251881Speter /* TODO: These values should always be present, if they are missing 2094251881Speter during an upgrade, set a flag, and then ask the user to talk to the 2095251881Speter server. 2096251881Speter 2097251881Speter Note: cmt_rev is the distinguishing value. The others may be 0 or 2098251881Speter NULL if the corresponding revprop has been deleted. */ 2099251881Speter base_node->changed_rev = entry->cmt_rev; 2100251881Speter base_node->changed_date = entry->cmt_date; 2101251881Speter base_node->changed_author = entry->cmt_author; 2102251881Speter 2103251881Speter if (entry->file_external_path) 2104251881Speter base_node->file_external = TRUE; 2105251881Speter 2106251881Speter /* Switched nodes get an empty iprops cache. */ 2107251881Speter if (parent_node 2108251881Speter && is_switched(parent_node->base, base_node, scratch_pool)) 2109251881Speter base_node->inherited_props 2110251881Speter = apr_array_make(scratch_pool, 0, sizeof(svn_prop_inherited_item_t*)); 2111251881Speter 2112251881Speter SVN_ERR(insert_node(sdb, base_node, scratch_pool)); 2113251881Speter 2114251881Speter /* We have to insert the lock after the base node, because the node 2115251881Speter must exist to lookup various bits of repos related information for 2116251881Speter the abs path. */ 2117251881Speter if (entry->lock_token && create_locks) 2118251881Speter { 2119251881Speter svn_wc__db_lock_t lock; 2120251881Speter 2121251881Speter lock.token = entry->lock_token; 2122251881Speter lock.owner = entry->lock_owner; 2123251881Speter lock.comment = entry->lock_comment; 2124251881Speter lock.date = entry->lock_creation_date; 2125251881Speter 2126251881Speter SVN_ERR(svn_wc__db_lock_add(db, tmp_entry_abspath, &lock, 2127251881Speter scratch_pool)); 2128251881Speter } 2129251881Speter } 2130251881Speter 2131251881Speter if (below_working_node) 2132251881Speter { 2133251881Speter db_node_t *work 2134251881Speter = parent_node->below_work ? parent_node->below_work : parent_node->work; 2135251881Speter 2136251881Speter below_working_node->wc_id = wc_id; 2137251881Speter below_working_node->local_relpath = local_relpath; 2138251881Speter below_working_node->op_depth = work->op_depth; 2139251881Speter below_working_node->parent_relpath = parent_relpath; 2140251881Speter below_working_node->presence = svn_wc__db_status_normal; 2141251881Speter below_working_node->kind = entry->kind; 2142251881Speter below_working_node->repos_id = work->repos_id; 2143251881Speter 2144251881Speter /* This is just guessing. If the node below would have been switched 2145251881Speter or if it was updated to a different version, the guess would 2146251881Speter fail. But we don't have better information pre wc-ng :( */ 2147251881Speter if (work->repos_relpath) 2148251881Speter below_working_node->repos_relpath 2149251881Speter = svn_relpath_join(work->repos_relpath, entry->name, 2150251881Speter result_pool); 2151251881Speter else 2152251881Speter below_working_node->repos_relpath = NULL; 2153251881Speter below_working_node->revision = parent_node->work->revision; 2154251881Speter 2155251881Speter /* The revert_base checksum isn't available in the entry structure, 2156251881Speter so the caller provides it. */ 2157251881Speter 2158251881Speter /* text_base_info is NULL for files scheduled to be added. */ 2159251881Speter below_working_node->checksum = NULL; 2160251881Speter if (text_base_info) 2161251881Speter { 2162251881Speter if (entry->schedule == svn_wc_schedule_delete) 2163251881Speter below_working_node->checksum = 2164251881Speter text_base_info->normal_base.sha1_checksum; 2165251881Speter else 2166251881Speter below_working_node->checksum = 2167251881Speter text_base_info->revert_base.sha1_checksum; 2168251881Speter } 2169251881Speter below_working_node->recorded_size = 0; 2170251881Speter below_working_node->changed_rev = SVN_INVALID_REVNUM; 2171251881Speter below_working_node->changed_date = 0; 2172251881Speter below_working_node->changed_author = NULL; 2173251881Speter below_working_node->depth = svn_depth_infinity; 2174251881Speter below_working_node->recorded_time = 0; 2175251881Speter below_working_node->properties = NULL; 2176251881Speter 2177251881Speter if (working_node 2178251881Speter && entry->schedule == svn_wc_schedule_delete 2179251881Speter && working_node->repos_relpath) 2180251881Speter { 2181251881Speter /* We are lucky, our guesses above are not necessary. The known 2182251881Speter correct information is in working. But our op_depth design 2183251881Speter expects more information here */ 2184251881Speter below_working_node->repos_relpath = working_node->repos_relpath; 2185251881Speter below_working_node->repos_id = working_node->repos_id; 2186251881Speter below_working_node->revision = working_node->revision; 2187251881Speter 2188251881Speter /* Nice for 'svn status' */ 2189251881Speter below_working_node->changed_rev = entry->cmt_rev; 2190251881Speter below_working_node->changed_date = entry->cmt_date; 2191251881Speter below_working_node->changed_author = entry->cmt_author; 2192251881Speter 2193251881Speter /* And now remove it from WORKING, because in wc-ng code 2194251881Speter should read it from the lower layer */ 2195251881Speter working_node->repos_relpath = NULL; 2196251881Speter working_node->repos_id = 0; 2197251881Speter working_node->revision = SVN_INVALID_REVNUM; 2198251881Speter } 2199251881Speter 2200251881Speter SVN_ERR(insert_node(sdb, below_working_node, scratch_pool)); 2201251881Speter } 2202251881Speter 2203251881Speter /* Insert the working node. */ 2204251881Speter if (working_node) 2205251881Speter { 2206251881Speter working_node->wc_id = wc_id; 2207251881Speter working_node->local_relpath = local_relpath; 2208251881Speter working_node->parent_relpath = parent_relpath; 2209251881Speter working_node->changed_rev = SVN_INVALID_REVNUM; 2210251881Speter working_node->recorded_time = entry->text_time; 2211251881Speter working_node->recorded_size = entry->working_size; 2212251881Speter 2213251881Speter if (entry->depth != svn_depth_exclude) 2214251881Speter working_node->depth = entry->depth; 2215251881Speter else 2216251881Speter { 2217251881Speter working_node->presence = svn_wc__db_status_excluded; 2218251881Speter working_node->depth = svn_depth_infinity; 2219251881Speter } 2220251881Speter 2221251881Speter if (entry->kind == svn_node_dir) 2222251881Speter working_node->checksum = NULL; 2223251881Speter else 2224251881Speter { 2225251881Speter working_node->checksum = NULL; 2226251881Speter /* text_base_info is NULL for files scheduled to be added. */ 2227251881Speter if (text_base_info) 2228251881Speter working_node->checksum = text_base_info->normal_base.sha1_checksum; 2229251881Speter 2230251881Speter 2231251881Speter /* If an MD5 checksum is present in the entry, we can verify that 2232251881Speter * it matches the MD5 of the base file we found earlier. */ 2233251881Speter#ifdef SVN_DEBUG 2234251881Speter if (entry->checksum && text_base_info) 2235251881Speter { 2236251881Speter svn_checksum_t *md5_checksum; 2237251881Speter SVN_ERR(svn_checksum_parse_hex(&md5_checksum, svn_checksum_md5, 2238251881Speter entry->checksum, result_pool)); 2239251881Speter SVN_ERR_ASSERT( 2240251881Speter md5_checksum && text_base_info->normal_base.md5_checksum); 2241251881Speter SVN_ERR_ASSERT(svn_checksum_match( 2242251881Speter md5_checksum, text_base_info->normal_base.md5_checksum)); 2243251881Speter } 2244251881Speter#endif 2245251881Speter } 2246251881Speter 2247251881Speter working_node->kind = entry->kind; 2248251881Speter if (working_node->presence != svn_wc__db_status_excluded) 2249251881Speter { 2250251881Speter /* All subdirs start of incomplete, and stop being incomplete 2251251881Speter when the entries file in the subdir is upgraded. */ 2252251881Speter if (entry->kind == svn_node_dir 2253251881Speter && strcmp(entry->name, SVN_WC_ENTRY_THIS_DIR)) 2254251881Speter { 2255251881Speter working_node->presence = svn_wc__db_status_incomplete; 2256251881Speter working_node->kind = svn_node_dir; 2257251881Speter } 2258251881Speter else if (entry->schedule == svn_wc_schedule_delete) 2259251881Speter { 2260251881Speter working_node->presence = svn_wc__db_status_base_deleted; 2261251881Speter working_node->kind = entry->kind; 2262251881Speter } 2263251881Speter else 2264251881Speter { 2265251881Speter /* presence == normal */ 2266251881Speter working_node->kind = entry->kind; 2267251881Speter 2268251881Speter if (entry->incomplete) 2269251881Speter { 2270251881Speter /* We shouldn't be overwriting another status. */ 2271251881Speter WRITE_ENTRY_ASSERT(working_node->presence 2272251881Speter == svn_wc__db_status_normal); 2273251881Speter working_node->presence = svn_wc__db_status_incomplete; 2274251881Speter } 2275251881Speter } 2276251881Speter } 2277251881Speter 2278251881Speter /* These should generally be unset for added and deleted files, 2279251881Speter and contain whatever information we have for copied files. Let's 2280251881Speter just store whatever we have. 2281251881Speter 2282251881Speter Note: cmt_rev is the distinguishing value. The others may be 0 or 2283251881Speter NULL if the corresponding revprop has been deleted. */ 2284251881Speter if (working_node->presence != svn_wc__db_status_base_deleted) 2285251881Speter { 2286251881Speter working_node->changed_rev = entry->cmt_rev; 2287251881Speter working_node->changed_date = entry->cmt_date; 2288251881Speter working_node->changed_author = entry->cmt_author; 2289251881Speter } 2290251881Speter 2291251881Speter if (entry->schedule == svn_wc_schedule_delete 2292251881Speter && parent_node->work 2293251881Speter && parent_node->work->presence == svn_wc__db_status_base_deleted) 2294251881Speter { 2295251881Speter working_node->op_depth = parent_node->work->op_depth; 2296251881Speter } 2297251881Speter else if (!entry->copied) 2298251881Speter { 2299251881Speter working_node->op_depth 2300251881Speter = svn_wc__db_op_depth_for_upgrade(local_relpath); 2301251881Speter } 2302251881Speter 2303251881Speter SVN_ERR(insert_node(sdb, working_node, scratch_pool)); 2304251881Speter } 2305251881Speter 2306251881Speter /* Insert the actual node. */ 2307251881Speter if (actual_node) 2308251881Speter { 2309251881Speter actual_node = MAYBE_ALLOC(actual_node, scratch_pool); 2310251881Speter 2311251881Speter actual_node->wc_id = wc_id; 2312251881Speter actual_node->local_relpath = local_relpath; 2313251881Speter actual_node->parent_relpath = parent_relpath; 2314251881Speter 2315251881Speter SVN_ERR(insert_actual_node(sdb, db, tmp_entry_abspath, 2316251881Speter actual_node, scratch_pool)); 2317251881Speter } 2318251881Speter 2319251881Speter if (entry_node) 2320251881Speter { 2321251881Speter *entry_node = apr_palloc(result_pool, sizeof(**entry_node)); 2322251881Speter (*entry_node)->base = base_node; 2323251881Speter (*entry_node)->work = working_node; 2324251881Speter (*entry_node)->below_work = below_working_node; 2325251881Speter (*entry_node)->tree_conflicts = tree_conflicts; 2326251881Speter } 2327251881Speter 2328251881Speter if (entry->file_external_path) 2329251881Speter { 2330251881Speter /* TODO: Maybe add a file external registration inside EXTERNALS here, 2331251881Speter to allow removing file externals that aren't referenced from 2332251881Speter svn:externals. 2333251881Speter 2334251881Speter The svn:externals values are processed anyway after everything is 2335251881Speter upgraded */ 2336251881Speter } 2337251881Speter 2338251881Speter return SVN_NO_ERROR; 2339251881Speter} 2340251881Speter 2341251881Speterstatic svn_error_t * 2342251881Speterwrite_actual_only_entries(apr_hash_t *tree_conflicts, 2343251881Speter svn_sqlite__db_t *sdb, 2344251881Speter svn_wc__db_t *db, 2345251881Speter const char *wri_abspath, 2346251881Speter apr_int64_t wc_id, 2347251881Speter const char *parent_relpath, 2348251881Speter apr_pool_t *scratch_pool) 2349251881Speter{ 2350251881Speter apr_hash_index_t *hi; 2351251881Speter 2352251881Speter for (hi = apr_hash_first(scratch_pool, tree_conflicts); 2353251881Speter hi; 2354251881Speter hi = apr_hash_next(hi)) 2355251881Speter { 2356251881Speter db_actual_node_t *actual_node = NULL; 2357251881Speter 2358251881Speter actual_node = MAYBE_ALLOC(actual_node, scratch_pool); 2359251881Speter actual_node->wc_id = wc_id; 2360251881Speter actual_node->local_relpath = svn__apr_hash_index_key(hi); 2361251881Speter actual_node->parent_relpath = parent_relpath; 2362251881Speter actual_node->tree_conflict_data = svn__apr_hash_index_val(hi); 2363251881Speter 2364251881Speter SVN_ERR(insert_actual_node(sdb, db, wri_abspath, actual_node, 2365251881Speter scratch_pool)); 2366251881Speter } 2367251881Speter 2368251881Speter return SVN_NO_ERROR; 2369251881Speter} 2370251881Speter 2371251881Spetersvn_error_t * 2372251881Spetersvn_wc__write_upgraded_entries(void **dir_baton, 2373251881Speter void *parent_baton, 2374251881Speter svn_wc__db_t *db, 2375251881Speter svn_sqlite__db_t *sdb, 2376251881Speter apr_int64_t repos_id, 2377251881Speter apr_int64_t wc_id, 2378251881Speter const char *dir_abspath, 2379251881Speter const char *new_root_abspath, 2380251881Speter apr_hash_t *entries, 2381251881Speter apr_hash_t *text_bases_info, 2382251881Speter apr_pool_t *result_pool, 2383251881Speter apr_pool_t *scratch_pool) 2384251881Speter{ 2385251881Speter const svn_wc_entry_t *this_dir; 2386251881Speter apr_hash_index_t *hi; 2387251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 2388251881Speter const char *old_root_abspath, *dir_relpath; 2389251881Speter struct write_baton *parent_node = parent_baton; 2390251881Speter struct write_baton *dir_node; 2391251881Speter 2392251881Speter /* Get a copy of the "this dir" entry for comparison purposes. */ 2393251881Speter this_dir = svn_hash_gets(entries, SVN_WC_ENTRY_THIS_DIR); 2394251881Speter 2395251881Speter /* If there is no "this dir" entry, something is wrong. */ 2396251881Speter if (! this_dir) 2397251881Speter return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL, 2398251881Speter _("No default entry in directory '%s'"), 2399251881Speter svn_dirent_local_style(dir_abspath, 2400251881Speter iterpool)); 2401251881Speter old_root_abspath = svn_dirent_get_longest_ancestor(dir_abspath, 2402251881Speter new_root_abspath, 2403251881Speter scratch_pool); 2404251881Speter 2405251881Speter SVN_ERR_ASSERT(old_root_abspath[0]); 2406251881Speter 2407251881Speter dir_relpath = svn_dirent_skip_ancestor(old_root_abspath, dir_abspath); 2408251881Speter 2409251881Speter /* Write out "this dir" */ 2410251881Speter SVN_ERR(write_entry(&dir_node, parent_node, db, sdb, 2411251881Speter wc_id, repos_id, this_dir, NULL, dir_relpath, 2412251881Speter svn_dirent_join(new_root_abspath, dir_relpath, 2413251881Speter iterpool), 2414251881Speter old_root_abspath, 2415251881Speter this_dir, FALSE, result_pool, iterpool)); 2416251881Speter 2417251881Speter for (hi = apr_hash_first(scratch_pool, entries); hi; 2418251881Speter hi = apr_hash_next(hi)) 2419251881Speter { 2420251881Speter const char *name = svn__apr_hash_index_key(hi); 2421251881Speter const svn_wc_entry_t *this_entry = svn__apr_hash_index_val(hi); 2422251881Speter const char *child_abspath, *child_relpath; 2423251881Speter svn_wc__text_base_info_t *text_base_info 2424251881Speter = svn_hash_gets(text_bases_info, name); 2425251881Speter 2426251881Speter svn_pool_clear(iterpool); 2427251881Speter 2428251881Speter /* Don't rewrite the "this dir" entry! */ 2429251881Speter if (strcmp(name, SVN_WC_ENTRY_THIS_DIR) == 0) 2430251881Speter continue; 2431251881Speter 2432251881Speter /* Write the entry. Pass TRUE for create locks, because we still 2433251881Speter use this function for upgrading old working copies. */ 2434251881Speter child_abspath = svn_dirent_join(dir_abspath, name, iterpool); 2435251881Speter child_relpath = svn_dirent_skip_ancestor(old_root_abspath, child_abspath); 2436251881Speter SVN_ERR(write_entry(NULL, dir_node, db, sdb, 2437251881Speter wc_id, repos_id, 2438251881Speter this_entry, text_base_info, child_relpath, 2439251881Speter svn_dirent_join(new_root_abspath, child_relpath, 2440251881Speter iterpool), 2441251881Speter old_root_abspath, 2442251881Speter this_dir, TRUE, iterpool, iterpool)); 2443251881Speter } 2444251881Speter 2445251881Speter if (dir_node->tree_conflicts) 2446251881Speter SVN_ERR(write_actual_only_entries(dir_node->tree_conflicts, sdb, db, 2447251881Speter new_root_abspath, wc_id, dir_relpath, 2448251881Speter iterpool)); 2449251881Speter 2450251881Speter *dir_baton = dir_node; 2451251881Speter svn_pool_destroy(iterpool); 2452251881Speter return SVN_NO_ERROR; 2453251881Speter} 2454251881Speter 2455251881Speter 2456251881Spetersvn_wc_entry_t * 2457251881Spetersvn_wc_entry_dup(const svn_wc_entry_t *entry, apr_pool_t *pool) 2458251881Speter{ 2459251881Speter svn_wc_entry_t *dupentry = apr_palloc(pool, sizeof(*dupentry)); 2460251881Speter 2461251881Speter /* Perform a trivial copy ... */ 2462251881Speter *dupentry = *entry; 2463251881Speter 2464251881Speter /* ...and then re-copy stuff that needs to be duped into our pool. */ 2465251881Speter if (entry->name) 2466251881Speter dupentry->name = apr_pstrdup(pool, entry->name); 2467251881Speter if (entry->url) 2468251881Speter dupentry->url = apr_pstrdup(pool, entry->url); 2469251881Speter if (entry->repos) 2470251881Speter dupentry->repos = apr_pstrdup(pool, entry->repos); 2471251881Speter if (entry->uuid) 2472251881Speter dupentry->uuid = apr_pstrdup(pool, entry->uuid); 2473251881Speter if (entry->copyfrom_url) 2474251881Speter dupentry->copyfrom_url = apr_pstrdup(pool, entry->copyfrom_url); 2475251881Speter if (entry->conflict_old) 2476251881Speter dupentry->conflict_old = apr_pstrdup(pool, entry->conflict_old); 2477251881Speter if (entry->conflict_new) 2478251881Speter dupentry->conflict_new = apr_pstrdup(pool, entry->conflict_new); 2479251881Speter if (entry->conflict_wrk) 2480251881Speter dupentry->conflict_wrk = apr_pstrdup(pool, entry->conflict_wrk); 2481251881Speter if (entry->prejfile) 2482251881Speter dupentry->prejfile = apr_pstrdup(pool, entry->prejfile); 2483251881Speter if (entry->checksum) 2484251881Speter dupentry->checksum = apr_pstrdup(pool, entry->checksum); 2485251881Speter if (entry->cmt_author) 2486251881Speter dupentry->cmt_author = apr_pstrdup(pool, entry->cmt_author); 2487251881Speter if (entry->lock_token) 2488251881Speter dupentry->lock_token = apr_pstrdup(pool, entry->lock_token); 2489251881Speter if (entry->lock_owner) 2490251881Speter dupentry->lock_owner = apr_pstrdup(pool, entry->lock_owner); 2491251881Speter if (entry->lock_comment) 2492251881Speter dupentry->lock_comment = apr_pstrdup(pool, entry->lock_comment); 2493251881Speter if (entry->changelist) 2494251881Speter dupentry->changelist = apr_pstrdup(pool, entry->changelist); 2495251881Speter 2496251881Speter /* NOTE: we do not dup cachable_props or present_props since they 2497251881Speter are deprecated. Use "" to indicate "nothing cachable or cached". */ 2498251881Speter dupentry->cachable_props = ""; 2499251881Speter dupentry->present_props = ""; 2500251881Speter 2501251881Speter if (entry->tree_conflict_data) 2502251881Speter dupentry->tree_conflict_data = apr_pstrdup(pool, 2503251881Speter entry->tree_conflict_data); 2504251881Speter if (entry->file_external_path) 2505251881Speter dupentry->file_external_path = apr_pstrdup(pool, 2506251881Speter entry->file_external_path); 2507251881Speter return dupentry; 2508251881Speter} 2509251881Speter 2510251881Speter 2511251881Speter/*** Generic Entry Walker */ 2512251881Speter 2513251881Speter/* A recursive entry-walker, helper for svn_wc_walk_entries3(). 2514251881Speter * 2515251881Speter * For this directory (DIRPATH, ADM_ACCESS), call the "found_entry" callback 2516251881Speter * in WALK_CALLBACKS, passing WALK_BATON to it. Then, for each versioned 2517251881Speter * entry in this directory, call the "found entry" callback and then recurse 2518251881Speter * (if it is a directory and if DEPTH allows). 2519251881Speter * 2520251881Speter * If SHOW_HIDDEN is true, include entries that are in a 'deleted' or 2521251881Speter * 'absent' state (and not scheduled for re-addition), else skip them. 2522251881Speter * 2523251881Speter * Call CANCEL_FUNC with CANCEL_BATON to allow cancellation. 2524251881Speter */ 2525251881Speterstatic svn_error_t * 2526251881Speterwalker_helper(const char *dirpath, 2527251881Speter svn_wc_adm_access_t *adm_access, 2528251881Speter const svn_wc_entry_callbacks2_t *walk_callbacks, 2529251881Speter void *walk_baton, 2530251881Speter svn_depth_t depth, 2531251881Speter svn_boolean_t show_hidden, 2532251881Speter svn_cancel_func_t cancel_func, 2533251881Speter void *cancel_baton, 2534251881Speter apr_pool_t *pool) 2535251881Speter{ 2536251881Speter apr_pool_t *subpool = svn_pool_create(pool); 2537251881Speter apr_hash_t *entries; 2538251881Speter apr_hash_index_t *hi; 2539251881Speter svn_wc_entry_t *dot_entry; 2540251881Speter svn_error_t *err; 2541251881Speter svn_wc__db_t *db = svn_wc__adm_get_db(adm_access); 2542251881Speter 2543251881Speter err = svn_wc__entries_read_internal(&entries, adm_access, show_hidden, 2544251881Speter pool); 2545251881Speter 2546251881Speter if (err) 2547251881Speter SVN_ERR(walk_callbacks->handle_error(dirpath, err, walk_baton, pool)); 2548251881Speter 2549251881Speter /* As promised, always return the '.' entry first. */ 2550251881Speter dot_entry = svn_hash_gets(entries, SVN_WC_ENTRY_THIS_DIR); 2551251881Speter if (! dot_entry) 2552251881Speter return walk_callbacks->handle_error 2553251881Speter (dirpath, svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL, 2554251881Speter _("Directory '%s' has no THIS_DIR entry"), 2555251881Speter svn_dirent_local_style(dirpath, pool)), 2556251881Speter walk_baton, pool); 2557251881Speter 2558251881Speter /* Call the "found entry" callback for this directory as a "this dir" 2559251881Speter * entry. Note that if this directory has been reached by recursion, this 2560251881Speter * is the second visit as it will already have been visited once as a 2561251881Speter * child entry of its parent. */ 2562251881Speter 2563251881Speter err = walk_callbacks->found_entry(dirpath, dot_entry, walk_baton, subpool); 2564251881Speter 2565251881Speter 2566251881Speter if(err) 2567251881Speter SVN_ERR(walk_callbacks->handle_error(dirpath, err, walk_baton, pool)); 2568251881Speter 2569251881Speter if (depth == svn_depth_empty) 2570251881Speter return SVN_NO_ERROR; 2571251881Speter 2572251881Speter /* Loop over each of the other entries. */ 2573251881Speter for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi)) 2574251881Speter { 2575251881Speter const char *name = svn__apr_hash_index_key(hi); 2576251881Speter const svn_wc_entry_t *current_entry = svn__apr_hash_index_val(hi); 2577251881Speter const char *entrypath; 2578251881Speter const char *entry_abspath; 2579251881Speter svn_boolean_t hidden; 2580251881Speter 2581251881Speter svn_pool_clear(subpool); 2582251881Speter 2583251881Speter /* See if someone wants to cancel this operation. */ 2584251881Speter if (cancel_func) 2585251881Speter SVN_ERR(cancel_func(cancel_baton)); 2586251881Speter 2587251881Speter /* Skip the "this dir" entry. */ 2588251881Speter if (strcmp(current_entry->name, SVN_WC_ENTRY_THIS_DIR) == 0) 2589251881Speter continue; 2590251881Speter 2591251881Speter entrypath = svn_dirent_join(dirpath, name, subpool); 2592251881Speter SVN_ERR(svn_wc__entry_is_hidden(&hidden, current_entry)); 2593251881Speter SVN_ERR(svn_dirent_get_absolute(&entry_abspath, entrypath, subpool)); 2594251881Speter 2595251881Speter /* Call the "found entry" callback for this entry. (For a directory, 2596251881Speter * this is the first visit: as a child.) */ 2597251881Speter if (current_entry->kind == svn_node_file 2598251881Speter || depth >= svn_depth_immediates) 2599251881Speter { 2600251881Speter err = walk_callbacks->found_entry(entrypath, current_entry, 2601251881Speter walk_baton, subpool); 2602251881Speter 2603251881Speter if (err) 2604251881Speter SVN_ERR(walk_callbacks->handle_error(entrypath, err, 2605251881Speter walk_baton, pool)); 2606251881Speter } 2607251881Speter 2608251881Speter /* Recurse into this entry if appropriate. */ 2609251881Speter if (current_entry->kind == svn_node_dir 2610251881Speter && !hidden 2611251881Speter && depth >= svn_depth_immediates) 2612251881Speter { 2613251881Speter svn_wc_adm_access_t *entry_access; 2614251881Speter svn_depth_t depth_below_here = depth; 2615251881Speter 2616251881Speter if (depth == svn_depth_immediates) 2617251881Speter depth_below_here = svn_depth_empty; 2618251881Speter 2619251881Speter entry_access = svn_wc__adm_retrieve_internal2(db, entry_abspath, 2620251881Speter subpool); 2621251881Speter 2622251881Speter if (entry_access) 2623251881Speter SVN_ERR(walker_helper(entrypath, entry_access, 2624251881Speter walk_callbacks, walk_baton, 2625251881Speter depth_below_here, show_hidden, 2626251881Speter cancel_func, cancel_baton, 2627251881Speter subpool)); 2628251881Speter } 2629251881Speter } 2630251881Speter 2631251881Speter svn_pool_destroy(subpool); 2632251881Speter return SVN_NO_ERROR; 2633251881Speter} 2634251881Speter 2635251881Spetersvn_error_t * 2636251881Spetersvn_wc__walker_default_error_handler(const char *path, 2637251881Speter svn_error_t *err, 2638251881Speter void *walk_baton, 2639251881Speter apr_pool_t *pool) 2640251881Speter{ 2641251881Speter /* Note: don't trace this. We don't want to insert a false "stack frame" 2642251881Speter onto an error generated elsewhere. */ 2643251881Speter return svn_error_trace(err); 2644251881Speter} 2645251881Speter 2646251881Speter 2647251881Speter/* The public API. */ 2648251881Spetersvn_error_t * 2649251881Spetersvn_wc_walk_entries3(const char *path, 2650251881Speter svn_wc_adm_access_t *adm_access, 2651251881Speter const svn_wc_entry_callbacks2_t *walk_callbacks, 2652251881Speter void *walk_baton, 2653251881Speter svn_depth_t walk_depth, 2654251881Speter svn_boolean_t show_hidden, 2655251881Speter svn_cancel_func_t cancel_func, 2656251881Speter void *cancel_baton, 2657251881Speter apr_pool_t *pool) 2658251881Speter{ 2659251881Speter const char *local_abspath; 2660251881Speter svn_wc__db_t *db = svn_wc__adm_get_db(adm_access); 2661251881Speter svn_error_t *err; 2662251881Speter svn_node_kind_t kind; 2663251881Speter svn_depth_t depth; 2664251881Speter 2665251881Speter SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool)); 2666251881Speter err = svn_wc__db_read_info(NULL, &kind, NULL, NULL, NULL, NULL, 2667251881Speter NULL, NULL, NULL, &depth, NULL, NULL, 2668251881Speter NULL, NULL, NULL, NULL, NULL, NULL, 2669251881Speter NULL, NULL, NULL, NULL, NULL, NULL, 2670251881Speter NULL, NULL, NULL, 2671251881Speter db, local_abspath, 2672251881Speter pool, pool); 2673251881Speter if (err) 2674251881Speter { 2675251881Speter if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 2676251881Speter return svn_error_trace(err); 2677251881Speter /* Remap into SVN_ERR_UNVERSIONED_RESOURCE. */ 2678251881Speter svn_error_clear(err); 2679251881Speter return walk_callbacks->handle_error( 2680251881Speter path, svn_error_createf(SVN_ERR_UNVERSIONED_RESOURCE, NULL, 2681251881Speter _("'%s' is not under version control"), 2682251881Speter svn_dirent_local_style(local_abspath, pool)), 2683251881Speter walk_baton, pool); 2684251881Speter } 2685251881Speter 2686251881Speter if (kind == svn_node_file || depth == svn_depth_exclude) 2687251881Speter { 2688251881Speter const svn_wc_entry_t *entry; 2689251881Speter 2690251881Speter /* ### we should stop passing out entry structures. 2691251881Speter ### 2692251881Speter ### we should not call handle_error for an error the *callback* 2693251881Speter ### gave us. let it deal with the problem before returning. */ 2694251881Speter 2695251881Speter if (!show_hidden) 2696251881Speter { 2697251881Speter svn_boolean_t hidden; 2698251881Speter SVN_ERR(svn_wc__db_node_hidden(&hidden, db, local_abspath, pool)); 2699251881Speter 2700251881Speter if (hidden) 2701251881Speter { 2702251881Speter /* The fool asked to walk a "hidden" node. Report the node as 2703251881Speter unversioned. 2704251881Speter 2705251881Speter ### this is incorrect behavior. see depth_test 36. the walk 2706251881Speter ### API will be revamped to avoid entry structures. we should 2707251881Speter ### be able to solve the problem with the new API. (since we 2708251881Speter ### shouldn't return a hidden entry here) */ 2709251881Speter return walk_callbacks->handle_error( 2710251881Speter path, svn_error_createf( 2711251881Speter SVN_ERR_UNVERSIONED_RESOURCE, NULL, 2712251881Speter _("'%s' is not under version control"), 2713251881Speter svn_dirent_local_style(local_abspath, pool)), 2714251881Speter walk_baton, pool); 2715251881Speter } 2716251881Speter } 2717251881Speter 2718251881Speter SVN_ERR(svn_wc__get_entry(&entry, db, local_abspath, FALSE, 2719251881Speter svn_node_file, pool, pool)); 2720251881Speter 2721251881Speter err = walk_callbacks->found_entry(path, entry, walk_baton, pool); 2722251881Speter if (err) 2723251881Speter return walk_callbacks->handle_error(path, err, walk_baton, pool); 2724251881Speter 2725251881Speter return SVN_NO_ERROR; 2726251881Speter } 2727251881Speter 2728251881Speter if (kind == svn_node_dir) 2729251881Speter return walker_helper(path, adm_access, walk_callbacks, walk_baton, 2730251881Speter walk_depth, show_hidden, cancel_func, cancel_baton, 2731251881Speter pool); 2732251881Speter 2733251881Speter return walk_callbacks->handle_error( 2734251881Speter path, svn_error_createf(SVN_ERR_NODE_UNKNOWN_KIND, NULL, 2735251881Speter _("'%s' has an unrecognized node kind"), 2736251881Speter svn_dirent_local_style(local_abspath, pool)), 2737251881Speter walk_baton, pool); 2738251881Speter} 2739