1251881Speter/* 2251881Speter * status.c: construct a status structure from an entry structure 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 25251881Speter 26251881Speter#include <assert.h> 27251881Speter#include <string.h> 28251881Speter 29251881Speter#include <apr_pools.h> 30251881Speter#include <apr_file_io.h> 31251881Speter#include <apr_hash.h> 32251881Speter 33251881Speter#include "svn_pools.h" 34251881Speter#include "svn_types.h" 35251881Speter#include "svn_delta.h" 36251881Speter#include "svn_string.h" 37251881Speter#include "svn_error.h" 38251881Speter#include "svn_dirent_uri.h" 39251881Speter#include "svn_path.h" 40251881Speter#include "svn_io.h" 41251881Speter#include "svn_config.h" 42251881Speter#include "svn_time.h" 43251881Speter#include "svn_hash.h" 44251881Speter#include "svn_sorts.h" 45251881Speter 46251881Speter#include "svn_private_config.h" 47251881Speter 48251881Speter#include "wc.h" 49251881Speter#include "props.h" 50251881Speter#include "entries.h" 51251881Speter#include "translate.h" 52251881Speter#include "tree_conflicts.h" 53251881Speter 54251881Speter#include "private/svn_wc_private.h" 55251881Speter#include "private/svn_fspath.h" 56251881Speter#include "private/svn_editor.h" 57251881Speter 58251881Speter 59251881Speter 60251881Speter/*** Baton used for walking the local status */ 61251881Speterstruct walk_status_baton 62251881Speter{ 63251881Speter /* The DB handle for managing the working copy state. */ 64251881Speter svn_wc__db_t *db; 65251881Speter 66251881Speter /*** External handling ***/ 67251881Speter /* Target of the status */ 68251881Speter const char *target_abspath; 69251881Speter 70251881Speter /* Should we ignore text modifications? */ 71251881Speter svn_boolean_t ignore_text_mods; 72251881Speter 73251881Speter /* Externals info harvested during the status run. */ 74251881Speter apr_hash_t *externals; 75251881Speter 76251881Speter /*** Repository lock handling ***/ 77251881Speter /* The repository root URL, if set. */ 78251881Speter const char *repos_root; 79251881Speter 80251881Speter /* Repository locks, if set. */ 81251881Speter apr_hash_t *repos_locks; 82251881Speter}; 83251881Speter 84251881Speter/*** Editor batons ***/ 85251881Speter 86251881Speterstruct edit_baton 87251881Speter{ 88251881Speter /* For status, the "destination" of the edit. */ 89251881Speter const char *anchor_abspath; 90251881Speter const char *target_abspath; 91251881Speter const char *target_basename; 92251881Speter 93251881Speter /* The DB handle for managing the working copy state. */ 94251881Speter svn_wc__db_t *db; 95251881Speter svn_wc_context_t *wc_ctx; 96251881Speter 97251881Speter /* The overall depth of this edit (a dir baton may override this). 98251881Speter * 99251881Speter * If this is svn_depth_unknown, the depths found in the working 100251881Speter * copy will govern the edit; or if the edit depth indicates a 101251881Speter * descent deeper than the found depths are capable of, the found 102251881Speter * depths also govern, of course (there's no point descending into 103251881Speter * something that's not there). 104251881Speter */ 105251881Speter svn_depth_t default_depth; 106251881Speter 107251881Speter /* Do we want all statuses (instead of just the interesting ones) ? */ 108251881Speter svn_boolean_t get_all; 109251881Speter 110251881Speter /* Ignore the svn:ignores. */ 111251881Speter svn_boolean_t no_ignore; 112251881Speter 113251881Speter /* The comparison revision in the repository. This is a reference 114251881Speter because this editor returns this rev to the driver directly, as 115251881Speter well as in each statushash entry. */ 116251881Speter svn_revnum_t *target_revision; 117251881Speter 118251881Speter /* Status function/baton. */ 119251881Speter svn_wc_status_func4_t status_func; 120251881Speter void *status_baton; 121251881Speter 122251881Speter /* Cancellation function/baton. */ 123251881Speter svn_cancel_func_t cancel_func; 124251881Speter void *cancel_baton; 125251881Speter 126251881Speter /* The configured set of default ignores. */ 127251881Speter const apr_array_header_t *ignores; 128251881Speter 129251881Speter /* Status item for the path represented by the anchor of the edit. */ 130251881Speter svn_wc_status3_t *anchor_status; 131251881Speter 132251881Speter /* Was open_root() called for this edit drive? */ 133251881Speter svn_boolean_t root_opened; 134251881Speter 135251881Speter /* The local status baton */ 136251881Speter struct walk_status_baton wb; 137251881Speter}; 138251881Speter 139251881Speter 140251881Speterstruct dir_baton 141251881Speter{ 142251881Speter /* The path to this directory. */ 143251881Speter const char *local_abspath; 144251881Speter 145251881Speter /* Basename of this directory. */ 146251881Speter const char *name; 147251881Speter 148251881Speter /* The global edit baton. */ 149251881Speter struct edit_baton *edit_baton; 150251881Speter 151251881Speter /* Baton for this directory's parent, or NULL if this is the root 152251881Speter directory. */ 153251881Speter struct dir_baton *parent_baton; 154251881Speter 155251881Speter /* The ambient requested depth below this point in the edit. This 156251881Speter can differ from the parent baton's depth (with the edit baton 157251881Speter considered the ultimate parent baton). For example, if the 158251881Speter parent baton has svn_depth_immediates, then here we should have 159251881Speter svn_depth_empty, because there would be no further recursion, not 160251881Speter even to file children. */ 161251881Speter svn_depth_t depth; 162251881Speter 163251881Speter /* Is this directory filtered out due to depth? (Note that if this 164251881Speter is TRUE, the depth field is undefined.) */ 165251881Speter svn_boolean_t excluded; 166251881Speter 167251881Speter /* 'svn status' shouldn't print status lines for things that are 168251881Speter added; we're only interest in asking if objects that the user 169251881Speter *already* has are up-to-date or not. Thus if this flag is set, 170251881Speter the next two will be ignored. :-) */ 171251881Speter svn_boolean_t added; 172251881Speter 173251881Speter /* Gets set iff there's a change to this directory's properties, to 174251881Speter guide us when syncing adm files later. */ 175251881Speter svn_boolean_t prop_changed; 176251881Speter 177251881Speter /* This means (in terms of 'svn status') that some child was deleted 178251881Speter or added to the directory */ 179251881Speter svn_boolean_t text_changed; 180251881Speter 181251881Speter /* Working copy status structures for children of this directory. 182251881Speter This hash maps const char * abspaths to svn_wc_status3_t * 183251881Speter status items. */ 184251881Speter apr_hash_t *statii; 185251881Speter 186251881Speter /* The pool in which this baton itself is allocated. */ 187251881Speter apr_pool_t *pool; 188251881Speter 189251881Speter /* The repository root relative path to this item in the repository. */ 190251881Speter const char *repos_relpath; 191251881Speter 192251881Speter /* out-of-date info corresponding to ood_* fields in svn_wc_status3_t. */ 193251881Speter svn_node_kind_t ood_kind; 194251881Speter svn_revnum_t ood_changed_rev; 195251881Speter apr_time_t ood_changed_date; 196251881Speter const char *ood_changed_author; 197251881Speter}; 198251881Speter 199251881Speter 200251881Speterstruct file_baton 201251881Speter{ 202251881Speter/* Absolute local path to this file */ 203251881Speter const char *local_abspath; 204251881Speter 205251881Speter /* The global edit baton. */ 206251881Speter struct edit_baton *edit_baton; 207251881Speter 208251881Speter /* Baton for this file's parent directory. */ 209251881Speter struct dir_baton *dir_baton; 210251881Speter 211251881Speter /* Pool specific to this file_baton. */ 212251881Speter apr_pool_t *pool; 213251881Speter 214251881Speter /* Basename of this file */ 215251881Speter const char *name; 216251881Speter 217251881Speter /* 'svn status' shouldn't print status lines for things that are 218251881Speter added; we're only interest in asking if objects that the user 219251881Speter *already* has are up-to-date or not. Thus if this flag is set, 220251881Speter the next two will be ignored. :-) */ 221251881Speter svn_boolean_t added; 222251881Speter 223251881Speter /* This gets set if the file underwent a text change, which guides 224251881Speter the code that syncs up the adm dir and working copy. */ 225251881Speter svn_boolean_t text_changed; 226251881Speter 227251881Speter /* This gets set if the file underwent a prop change, which guides 228251881Speter the code that syncs up the adm dir and working copy. */ 229251881Speter svn_boolean_t prop_changed; 230251881Speter 231251881Speter /* The repository root relative path to this item in the repository. */ 232251881Speter const char *repos_relpath; 233251881Speter 234251881Speter /* out-of-date info corresponding to ood_* fields in svn_wc_status3_t. */ 235251881Speter svn_node_kind_t ood_kind; 236251881Speter svn_revnum_t ood_changed_rev; 237251881Speter apr_time_t ood_changed_date; 238251881Speter 239251881Speter const char *ood_changed_author; 240251881Speter}; 241251881Speter 242251881Speter 243251881Speter/** Code **/ 244251881Speter 245251881Speter 246251881Speter 247251881Speter/* Return *REPOS_RELPATH and *REPOS_ROOT_URL for LOCAL_ABSPATH using 248251881Speter information in INFO if available, falling back on 249251881Speter PARENT_REPOS_RELPATH and PARENT_REPOS_ROOT_URL if available, and 250251881Speter finally falling back on querying DB. */ 251251881Speterstatic svn_error_t * 252251881Speterget_repos_root_url_relpath(const char **repos_relpath, 253251881Speter const char **repos_root_url, 254251881Speter const char **repos_uuid, 255251881Speter const struct svn_wc__db_info_t *info, 256251881Speter const char *parent_repos_relpath, 257251881Speter const char *parent_repos_root_url, 258251881Speter const char *parent_repos_uuid, 259251881Speter svn_wc__db_t *db, 260251881Speter const char *local_abspath, 261251881Speter apr_pool_t *result_pool, 262251881Speter apr_pool_t *scratch_pool) 263251881Speter{ 264251881Speter if (info->repos_relpath && info->repos_root_url) 265251881Speter { 266251881Speter *repos_relpath = apr_pstrdup(result_pool, info->repos_relpath); 267251881Speter *repos_root_url = apr_pstrdup(result_pool, info->repos_root_url); 268251881Speter *repos_uuid = apr_pstrdup(result_pool, info->repos_uuid); 269251881Speter } 270251881Speter else if (parent_repos_relpath && parent_repos_root_url) 271251881Speter { 272251881Speter *repos_relpath = svn_relpath_join(parent_repos_relpath, 273251881Speter svn_dirent_basename(local_abspath, 274251881Speter NULL), 275251881Speter result_pool); 276251881Speter *repos_root_url = apr_pstrdup(result_pool, parent_repos_root_url); 277251881Speter *repos_uuid = apr_pstrdup(result_pool, parent_repos_uuid); 278251881Speter } 279251881Speter else if (info->status == svn_wc__db_status_added) 280251881Speter { 281251881Speter SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, 282251881Speter repos_relpath, repos_root_url, 283251881Speter repos_uuid, NULL, NULL, NULL, NULL, 284251881Speter db, local_abspath, 285251881Speter result_pool, scratch_pool)); 286251881Speter } 287269847Speter else if (info->status == svn_wc__db_status_deleted 288269847Speter && !info->have_more_work 289269847Speter && info->have_base) 290251881Speter { 291251881Speter SVN_ERR(svn_wc__db_scan_base_repos(repos_relpath, repos_root_url, 292251881Speter repos_uuid, 293251881Speter db, local_abspath, 294251881Speter result_pool, scratch_pool)); 295251881Speter } 296269847Speter else if (info->status == svn_wc__db_status_deleted) 297269847Speter { 298269847Speter const char *work_del_abspath; 299269847Speter const char *add_abspath; 300269847Speter 301269847Speter /* Handles working DELETE and the special case where there is just 302269847Speter svn_wc__db_status_not_present in WORKING */ 303269847Speter 304269847Speter SVN_ERR(svn_wc__db_scan_deletion(NULL, NULL, &work_del_abspath, NULL, 305269847Speter db, local_abspath, 306269847Speter scratch_pool, scratch_pool)); 307269847Speter 308269847Speter /* The parent of what has been deleted must be added */ 309269847Speter add_abspath = svn_dirent_dirname(work_del_abspath, scratch_pool); 310269847Speter 311269847Speter SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, repos_relpath, 312269847Speter repos_root_url, repos_uuid, NULL, 313269847Speter NULL, NULL, NULL, 314269847Speter db, add_abspath, 315269847Speter result_pool, scratch_pool)); 316269847Speter 317269847Speter *repos_relpath = svn_relpath_join(*repos_relpath, 318269847Speter svn_dirent_skip_ancestor( 319269847Speter add_abspath, 320269847Speter local_abspath), 321269847Speter result_pool); 322269847Speter } 323251881Speter else 324251881Speter { 325251881Speter *repos_relpath = NULL; 326251881Speter *repos_root_url = NULL; 327251881Speter *repos_uuid = NULL; 328251881Speter } 329251881Speter return SVN_NO_ERROR; 330251881Speter} 331251881Speter 332251881Speterstatic svn_error_t * 333251881Speterinternal_status(svn_wc_status3_t **status, 334251881Speter svn_wc__db_t *db, 335251881Speter const char *local_abspath, 336251881Speter apr_pool_t *result_pool, 337251881Speter apr_pool_t *scratch_pool); 338251881Speter 339251881Speter/* Fill in *STATUS for LOCAL_ABSPATH, using DB. Allocate *STATUS in 340251881Speter RESULT_POOL and use SCRATCH_POOL for temporary allocations. 341251881Speter 342251881Speter PARENT_REPOS_ROOT_URL and PARENT_REPOS_RELPATH are the repository root 343251881Speter and repository relative path of the parent of LOCAL_ABSPATH or NULL if 344251881Speter LOCAL_ABSPATH doesn't have a versioned parent directory. 345251881Speter 346251881Speter DIRENT is the local representation of LOCAL_ABSPATH in the working copy or 347251881Speter NULL if the node does not exist on disk. 348251881Speter 349251881Speter If GET_ALL is FALSE, and LOCAL_ABSPATH is not locally modified, then 350251881Speter *STATUS will be set to NULL. If GET_ALL is non-zero, then *STATUS will be 351251881Speter allocated and returned no matter what. If IGNORE_TEXT_MODS is TRUE then 352251881Speter don't check for text mods, assume there are none and set and *STATUS 353251881Speter returned to reflect that assumption. 354251881Speter 355251881Speter The status struct's repos_lock field will be set to REPOS_LOCK. 356251881Speter*/ 357251881Speterstatic svn_error_t * 358251881Speterassemble_status(svn_wc_status3_t **status, 359251881Speter svn_wc__db_t *db, 360251881Speter const char *local_abspath, 361251881Speter const char *parent_repos_root_url, 362251881Speter const char *parent_repos_relpath, 363251881Speter const char *parent_repos_uuid, 364251881Speter const struct svn_wc__db_info_t *info, 365251881Speter const svn_io_dirent2_t *dirent, 366251881Speter svn_boolean_t get_all, 367251881Speter svn_boolean_t ignore_text_mods, 368251881Speter const svn_lock_t *repos_lock, 369251881Speter apr_pool_t *result_pool, 370251881Speter apr_pool_t *scratch_pool) 371251881Speter{ 372251881Speter svn_wc_status3_t *stat; 373251881Speter svn_boolean_t switched_p = FALSE; 374251881Speter svn_boolean_t copied = FALSE; 375251881Speter svn_boolean_t conflicted; 376251881Speter const char *moved_from_abspath = NULL; 377251881Speter svn_filesize_t filesize = (dirent && (dirent->kind == svn_node_file)) 378251881Speter ? dirent->filesize 379251881Speter : SVN_INVALID_FILESIZE; 380251881Speter 381251881Speter /* Defaults for two main variables. */ 382251881Speter enum svn_wc_status_kind node_status = svn_wc_status_normal; 383251881Speter enum svn_wc_status_kind text_status = svn_wc_status_normal; 384251881Speter enum svn_wc_status_kind prop_status = svn_wc_status_none; 385251881Speter 386251881Speter 387251881Speter if (!info) 388269847Speter SVN_ERR(svn_wc__db_read_single_info(&info, db, local_abspath, 389269847Speter result_pool, scratch_pool)); 390251881Speter 391251881Speter if (!info->repos_relpath || !parent_repos_relpath) 392251881Speter switched_p = FALSE; 393251881Speter else 394251881Speter { 395251881Speter /* A node is switched if it doesn't have the implied repos_relpath */ 396251881Speter const char *name = svn_relpath_skip_ancestor(parent_repos_relpath, 397251881Speter info->repos_relpath); 398251881Speter switched_p = !name || (strcmp(name, 399251881Speter svn_dirent_basename(local_abspath, NULL)) 400251881Speter != 0); 401251881Speter } 402251881Speter 403251881Speter if (info->status == svn_wc__db_status_incomplete || info->incomplete) 404251881Speter { 405251881Speter /* Highest precedence. */ 406251881Speter node_status = svn_wc_status_incomplete; 407251881Speter } 408251881Speter else if (info->status == svn_wc__db_status_deleted) 409251881Speter { 410251881Speter node_status = svn_wc_status_deleted; 411251881Speter 412251881Speter if (!info->have_base || info->have_more_work || info->copied) 413251881Speter copied = TRUE; 414251881Speter else if (!info->have_more_work && info->have_base) 415251881Speter copied = FALSE; 416251881Speter else 417251881Speter { 418251881Speter const char *work_del_abspath; 419251881Speter 420251881Speter /* Find out details of our deletion. */ 421251881Speter SVN_ERR(svn_wc__db_scan_deletion(NULL, NULL, 422251881Speter &work_del_abspath, NULL, 423251881Speter db, local_abspath, 424251881Speter scratch_pool, scratch_pool)); 425251881Speter if (work_del_abspath) 426251881Speter copied = TRUE; /* Working deletion */ 427251881Speter } 428251881Speter } 429251881Speter else 430251881Speter { 431251881Speter /* Examine whether our target is missing or obstructed. To detect 432251881Speter * obstructions, we have to look at the on-disk status in DIRENT. */ 433251881Speter svn_node_kind_t expected_kind = (info->kind == svn_node_dir) 434251881Speter ? svn_node_dir 435251881Speter : svn_node_file; 436251881Speter 437251881Speter if (!dirent || dirent->kind != expected_kind) 438251881Speter { 439251881Speter /* A present or added node should be on disk, so it is 440251881Speter reported missing or obstructed. */ 441251881Speter if (!dirent || dirent->kind == svn_node_none) 442251881Speter node_status = svn_wc_status_missing; 443251881Speter else 444251881Speter node_status = svn_wc_status_obstructed; 445251881Speter } 446251881Speter } 447251881Speter 448251881Speter /* Does the node have props? */ 449251881Speter if (info->status != svn_wc__db_status_deleted) 450251881Speter { 451251881Speter if (info->props_mod) 452251881Speter prop_status = svn_wc_status_modified; 453251881Speter else if (info->had_props) 454251881Speter prop_status = svn_wc_status_normal; 455251881Speter } 456251881Speter 457251881Speter /* If NODE_STATUS is still normal, after the above checks, then 458251881Speter we should proceed to refine the status. 459251881Speter 460251881Speter If it was changed, then the subdir is incomplete or missing/obstructed. 461251881Speter */ 462251881Speter if (info->kind != svn_node_dir 463251881Speter && node_status == svn_wc_status_normal) 464251881Speter { 465251881Speter svn_boolean_t text_modified_p = FALSE; 466251881Speter 467251881Speter /* Implement predecence rules: */ 468251881Speter 469251881Speter /* 1. Set the two main variables to "discovered" values first (M, C). 470251881Speter Together, these two stati are of lowest precedence, and C has 471251881Speter precedence over M. */ 472251881Speter 473251881Speter /* If the entry is a file, check for textual modifications */ 474251881Speter if ((info->kind == svn_node_file 475251881Speter || info->kind == svn_node_symlink) 476251881Speter#ifdef HAVE_SYMLINK 477251881Speter && (info->special == (dirent && dirent->special)) 478251881Speter#endif /* HAVE_SYMLINK */ 479251881Speter ) 480251881Speter { 481251881Speter /* If the on-disk dirent exactly matches the expected state 482251881Speter skip all operations in svn_wc__internal_text_modified_p() 483251881Speter to avoid an extra filestat for every file, which can be 484251881Speter expensive on network drives as a filestat usually can't 485251881Speter be cached there */ 486251881Speter if (!info->has_checksum) 487251881Speter text_modified_p = TRUE; /* Local addition -> Modified */ 488251881Speter else if (ignore_text_mods 489251881Speter ||(dirent 490251881Speter && info->recorded_size != SVN_INVALID_FILESIZE 491251881Speter && info->recorded_time != 0 492251881Speter && info->recorded_size == dirent->filesize 493251881Speter && info->recorded_time == dirent->mtime)) 494251881Speter text_modified_p = FALSE; 495251881Speter else 496251881Speter { 497251881Speter svn_error_t *err; 498251881Speter err = svn_wc__internal_file_modified_p(&text_modified_p, 499251881Speter db, local_abspath, 500251881Speter FALSE, scratch_pool); 501251881Speter 502251881Speter if (err) 503251881Speter { 504251881Speter if (err->apr_err != SVN_ERR_WC_PATH_ACCESS_DENIED) 505251881Speter return svn_error_trace(err); 506251881Speter 507251881Speter /* An access denied is very common on Windows when another 508251881Speter application has the file open. Previously we ignored 509251881Speter this error in svn_wc__text_modified_internal_p, where it 510251881Speter should have really errored. */ 511251881Speter svn_error_clear(err); 512251881Speter text_modified_p = TRUE; 513251881Speter } 514251881Speter } 515251881Speter } 516251881Speter#ifdef HAVE_SYMLINK 517251881Speter else if (info->special != (dirent && dirent->special)) 518251881Speter node_status = svn_wc_status_obstructed; 519251881Speter#endif /* HAVE_SYMLINK */ 520251881Speter 521251881Speter if (text_modified_p) 522251881Speter text_status = svn_wc_status_modified; 523251881Speter } 524251881Speter 525251881Speter conflicted = info->conflicted; 526251881Speter if (conflicted) 527251881Speter { 528251881Speter svn_boolean_t text_conflicted, prop_conflicted, tree_conflicted; 529251881Speter 530251881Speter /* ### Check if the conflict was resolved by removing the marker files. 531251881Speter ### This should really be moved to the users of this API */ 532251881Speter SVN_ERR(svn_wc__internal_conflicted_p(&text_conflicted, &prop_conflicted, 533251881Speter &tree_conflicted, 534251881Speter db, local_abspath, scratch_pool)); 535251881Speter 536251881Speter if (!text_conflicted && !prop_conflicted && !tree_conflicted) 537251881Speter conflicted = FALSE; 538251881Speter } 539251881Speter 540251881Speter if (node_status == svn_wc_status_normal) 541251881Speter { 542251881Speter /* 2. Possibly overwrite the text_status variable with "scheduled" 543251881Speter states from the entry (A, D, R). As a group, these states are 544251881Speter of medium precedence. They also override any C or M that may 545251881Speter be in the prop_status field at this point, although they do not 546251881Speter override a C text status.*/ 547251881Speter if (info->status == svn_wc__db_status_added) 548251881Speter { 549251881Speter copied = info->copied; 550251881Speter if (!info->op_root) 551251881Speter { /* Keep status normal */ } 552251881Speter else if (!info->have_base && !info->have_more_work) 553251881Speter { 554251881Speter /* Simple addition or copy, no replacement */ 555251881Speter node_status = svn_wc_status_added; 556251881Speter } 557251881Speter else 558251881Speter { 559251881Speter svn_wc__db_status_t below_working; 560251881Speter svn_boolean_t have_base, have_work; 561251881Speter 562251881Speter SVN_ERR(svn_wc__db_info_below_working(&have_base, &have_work, 563251881Speter &below_working, 564251881Speter db, local_abspath, 565251881Speter scratch_pool)); 566251881Speter 567251881Speter /* If the node is not present or deleted (read: not present 568251881Speter in working), then the node is not a replacement */ 569251881Speter if (below_working != svn_wc__db_status_not_present 570251881Speter && below_working != svn_wc__db_status_deleted) 571251881Speter { 572251881Speter node_status = svn_wc_status_replaced; 573251881Speter } 574251881Speter else 575251881Speter node_status = svn_wc_status_added; 576251881Speter } 577251881Speter 578251881Speter /* Get moved-from info (only for potential op-roots of a move). */ 579251881Speter if (info->moved_here && info->op_root) 580251881Speter { 581251881Speter svn_error_t *err; 582251881Speter err = svn_wc__db_scan_moved(&moved_from_abspath, NULL, NULL, NULL, 583251881Speter db, local_abspath, 584251881Speter result_pool, scratch_pool); 585251881Speter 586251881Speter if (err) 587251881Speter { 588251881Speter if (err->apr_err != SVN_ERR_WC_PATH_UNEXPECTED_STATUS) 589251881Speter return svn_error_trace(err); 590251881Speter 591251881Speter svn_error_clear(err); 592251881Speter /* We are no longer moved... So most likely we are somehow 593251881Speter changing the db for things like resolving conflicts. */ 594251881Speter 595251881Speter moved_from_abspath = NULL; 596251881Speter } 597251881Speter } 598251881Speter } 599251881Speter } 600251881Speter 601251881Speter 602251881Speter if (node_status == svn_wc_status_normal) 603251881Speter node_status = text_status; 604251881Speter 605251881Speter if (node_status == svn_wc_status_normal 606251881Speter && prop_status != svn_wc_status_none) 607251881Speter node_status = prop_status; 608251881Speter 609251881Speter /* 5. Easy out: unless we're fetching -every- entry, don't bother 610251881Speter to allocate a struct for an uninteresting entry. */ 611251881Speter 612251881Speter if (! get_all) 613251881Speter if (((node_status == svn_wc_status_none) 614251881Speter || (node_status == svn_wc_status_normal)) 615251881Speter 616251881Speter && (! switched_p) 617251881Speter && (! info->locked ) 618251881Speter && (! info->lock) 619251881Speter && (! repos_lock) 620251881Speter && (! info->changelist) 621251881Speter && (! conflicted)) 622251881Speter { 623251881Speter *status = NULL; 624251881Speter return SVN_NO_ERROR; 625251881Speter } 626251881Speter 627251881Speter /* 6. Build and return a status structure. */ 628251881Speter 629251881Speter stat = apr_pcalloc(result_pool, sizeof(**status)); 630251881Speter 631251881Speter switch (info->kind) 632251881Speter { 633251881Speter case svn_node_dir: 634251881Speter stat->kind = svn_node_dir; 635251881Speter break; 636251881Speter case svn_node_file: 637251881Speter case svn_node_symlink: 638251881Speter stat->kind = svn_node_file; 639251881Speter break; 640251881Speter case svn_node_unknown: 641251881Speter default: 642251881Speter stat->kind = svn_node_unknown; 643251881Speter } 644251881Speter stat->depth = info->depth; 645251881Speter stat->filesize = filesize; 646251881Speter stat->node_status = node_status; 647251881Speter stat->text_status = text_status; 648251881Speter stat->prop_status = prop_status; 649251881Speter stat->repos_node_status = svn_wc_status_none; /* default */ 650251881Speter stat->repos_text_status = svn_wc_status_none; /* default */ 651251881Speter stat->repos_prop_status = svn_wc_status_none; /* default */ 652251881Speter stat->switched = switched_p; 653251881Speter stat->copied = copied; 654251881Speter stat->repos_lock = repos_lock; 655251881Speter stat->revision = info->revnum; 656251881Speter stat->changed_rev = info->changed_rev; 657251881Speter if (info->changed_author) 658251881Speter stat->changed_author = apr_pstrdup(result_pool, info->changed_author); 659251881Speter stat->changed_date = info->changed_date; 660251881Speter 661251881Speter stat->ood_kind = svn_node_none; 662251881Speter stat->ood_changed_rev = SVN_INVALID_REVNUM; 663251881Speter stat->ood_changed_date = 0; 664251881Speter stat->ood_changed_author = NULL; 665251881Speter 666251881Speter SVN_ERR(get_repos_root_url_relpath(&stat->repos_relpath, 667251881Speter &stat->repos_root_url, 668251881Speter &stat->repos_uuid, info, 669251881Speter parent_repos_relpath, 670251881Speter parent_repos_root_url, 671251881Speter parent_repos_uuid, 672251881Speter db, local_abspath, 673251881Speter result_pool, scratch_pool)); 674251881Speter 675251881Speter if (info->lock) 676251881Speter { 677251881Speter svn_lock_t *lck = svn_lock_create(result_pool); 678251881Speter lck->path = stat->repos_relpath; 679251881Speter lck->token = info->lock->token; 680251881Speter lck->owner = info->lock->owner; 681251881Speter lck->comment = info->lock->comment; 682251881Speter lck->creation_date = info->lock->date; 683251881Speter stat->lock = lck; 684251881Speter } 685251881Speter else 686251881Speter stat->lock = NULL; 687251881Speter 688251881Speter stat->locked = info->locked; 689251881Speter stat->conflicted = conflicted; 690251881Speter stat->versioned = TRUE; 691251881Speter if (info->changelist) 692251881Speter stat->changelist = apr_pstrdup(result_pool, info->changelist); 693251881Speter 694251881Speter stat->moved_from_abspath = moved_from_abspath; 695251881Speter 696269847Speter /* ### TODO: Handle multiple moved_to values properly */ 697269847Speter if (info->moved_to) 698269847Speter stat->moved_to_abspath = apr_pstrdup(result_pool, 699269847Speter info->moved_to->moved_to_abspath); 700269847Speter 701251881Speter stat->file_external = info->file_external; 702251881Speter 703251881Speter *status = stat; 704251881Speter 705251881Speter return SVN_NO_ERROR; 706251881Speter} 707251881Speter 708251881Speter/* Fill in *STATUS for the unversioned path LOCAL_ABSPATH, using data 709251881Speter available in DB. Allocate *STATUS in POOL. Use SCRATCH_POOL for 710251881Speter temporary allocations. 711251881Speter 712251881Speter If IS_IGNORED is non-zero and this is a non-versioned entity, set 713251881Speter the node_status to svn_wc_status_none. Otherwise set the 714251881Speter node_status to svn_wc_status_unversioned. 715251881Speter */ 716251881Speterstatic svn_error_t * 717251881Speterassemble_unversioned(svn_wc_status3_t **status, 718251881Speter svn_wc__db_t *db, 719251881Speter const char *local_abspath, 720251881Speter const svn_io_dirent2_t *dirent, 721251881Speter svn_boolean_t tree_conflicted, 722251881Speter svn_boolean_t is_ignored, 723251881Speter apr_pool_t *result_pool, 724251881Speter apr_pool_t *scratch_pool) 725251881Speter{ 726251881Speter svn_wc_status3_t *stat; 727251881Speter 728251881Speter /* return a fairly blank structure. */ 729251881Speter stat = apr_pcalloc(result_pool, sizeof(*stat)); 730251881Speter 731251881Speter /*stat->versioned = FALSE;*/ 732251881Speter stat->kind = svn_node_unknown; /* not versioned */ 733251881Speter stat->depth = svn_depth_unknown; 734251881Speter stat->filesize = (dirent && dirent->kind == svn_node_file) 735251881Speter ? dirent->filesize 736251881Speter : SVN_INVALID_FILESIZE; 737251881Speter stat->node_status = svn_wc_status_none; 738251881Speter stat->text_status = svn_wc_status_none; 739251881Speter stat->prop_status = svn_wc_status_none; 740251881Speter stat->repos_node_status = svn_wc_status_none; 741251881Speter stat->repos_text_status = svn_wc_status_none; 742251881Speter stat->repos_prop_status = svn_wc_status_none; 743251881Speter 744251881Speter /* If this path has no entry, but IS present on disk, it's 745251881Speter unversioned. If this file is being explicitly ignored (due 746251881Speter to matching an ignore-pattern), the node_status is set to 747251881Speter svn_wc_status_ignored. Otherwise the node_status is set to 748251881Speter svn_wc_status_unversioned. */ 749251881Speter if (dirent && dirent->kind != svn_node_none) 750251881Speter { 751251881Speter if (is_ignored) 752251881Speter stat->node_status = svn_wc_status_ignored; 753251881Speter else 754251881Speter stat->node_status = svn_wc_status_unversioned; 755251881Speter } 756251881Speter else if (tree_conflicted) 757251881Speter { 758251881Speter /* If this path has no entry, is NOT present on disk, and IS a 759251881Speter tree conflict victim, report it as conflicted. */ 760251881Speter stat->node_status = svn_wc_status_conflicted; 761251881Speter } 762251881Speter 763251881Speter stat->revision = SVN_INVALID_REVNUM; 764251881Speter stat->changed_rev = SVN_INVALID_REVNUM; 765251881Speter stat->ood_changed_rev = SVN_INVALID_REVNUM; 766251881Speter stat->ood_kind = svn_node_none; 767251881Speter 768251881Speter /* For the case of an incoming delete to a locally deleted path during 769251881Speter an update, we get a tree conflict. */ 770251881Speter stat->conflicted = tree_conflicted; 771251881Speter stat->changelist = NULL; 772251881Speter 773251881Speter *status = stat; 774251881Speter return SVN_NO_ERROR; 775251881Speter} 776251881Speter 777251881Speter 778251881Speter/* Given an ENTRY object representing PATH, build a status structure 779251881Speter and pass it off to the STATUS_FUNC/STATUS_BATON. All other 780251881Speter arguments are the same as those passed to assemble_status(). */ 781251881Speterstatic svn_error_t * 782251881Spetersend_status_structure(const struct walk_status_baton *wb, 783251881Speter const char *local_abspath, 784251881Speter const char *parent_repos_root_url, 785251881Speter const char *parent_repos_relpath, 786251881Speter const char *parent_repos_uuid, 787251881Speter const struct svn_wc__db_info_t *info, 788251881Speter const svn_io_dirent2_t *dirent, 789251881Speter svn_boolean_t get_all, 790251881Speter svn_wc_status_func4_t status_func, 791251881Speter void *status_baton, 792251881Speter apr_pool_t *scratch_pool) 793251881Speter{ 794251881Speter svn_wc_status3_t *statstruct; 795251881Speter const svn_lock_t *repos_lock = NULL; 796251881Speter 797251881Speter /* Check for a repository lock. */ 798251881Speter if (wb->repos_locks) 799251881Speter { 800251881Speter const char *repos_relpath, *repos_root_url, *repos_uuid; 801251881Speter 802251881Speter SVN_ERR(get_repos_root_url_relpath(&repos_relpath, &repos_root_url, 803251881Speter &repos_uuid, 804251881Speter info, parent_repos_relpath, 805251881Speter parent_repos_root_url, 806251881Speter parent_repos_uuid, 807251881Speter wb->db, local_abspath, 808251881Speter scratch_pool, scratch_pool)); 809251881Speter if (repos_relpath) 810251881Speter { 811251881Speter /* repos_lock still uses the deprecated filesystem absolute path 812251881Speter format */ 813251881Speter repos_lock = svn_hash_gets(wb->repos_locks, 814251881Speter svn_fspath__join("/", repos_relpath, 815251881Speter scratch_pool)); 816251881Speter } 817251881Speter } 818251881Speter 819251881Speter SVN_ERR(assemble_status(&statstruct, wb->db, local_abspath, 820251881Speter parent_repos_root_url, parent_repos_relpath, 821251881Speter parent_repos_uuid, 822251881Speter info, dirent, get_all, wb->ignore_text_mods, 823251881Speter repos_lock, scratch_pool, scratch_pool)); 824251881Speter 825251881Speter if (statstruct && status_func) 826251881Speter return svn_error_trace((*status_func)(status_baton, local_abspath, 827251881Speter statstruct, scratch_pool)); 828251881Speter 829251881Speter return SVN_NO_ERROR; 830251881Speter} 831251881Speter 832251881Speter 833251881Speter/* Store in *PATTERNS a list of ignores collected from svn:ignore properties 834251881Speter on LOCAL_ABSPATH and svn:global-ignores on LOCAL_ABSPATH and its 835251881Speter repository ancestors (as cached in the working copy), including the default 836251881Speter ignores passed in as IGNORES. 837251881Speter 838251881Speter Upon return, *PATTERNS will contain zero or more (const char *) 839251881Speter patterns from the value of the SVN_PROP_IGNORE property set on 840251881Speter the working directory path. 841251881Speter 842251881Speter IGNORES is a list of patterns to include; typically this will 843251881Speter be the default ignores as, for example, specified in a config file. 844251881Speter 845251881Speter DB, LOCAL_ABSPATH is used to access the working copy. 846251881Speter 847251881Speter Allocate results in RESULT_POOL, temporary stuffs in SCRATCH_POOL. 848251881Speter 849251881Speter None of the arguments may be NULL. 850251881Speter*/ 851251881Speterstatic svn_error_t * 852251881Spetercollect_ignore_patterns(apr_array_header_t **patterns, 853251881Speter svn_wc__db_t *db, 854251881Speter const char *local_abspath, 855251881Speter const apr_array_header_t *ignores, 856251881Speter apr_pool_t *result_pool, 857251881Speter apr_pool_t *scratch_pool) 858251881Speter{ 859251881Speter int i; 860251881Speter apr_hash_t *props; 861251881Speter apr_array_header_t *inherited_props; 862251881Speter svn_error_t *err; 863251881Speter 864251881Speter /* ### assert we are passed a directory? */ 865251881Speter 866251881Speter *patterns = apr_array_make(result_pool, 1, sizeof(const char *)); 867251881Speter 868251881Speter /* Copy default ignores into the local PATTERNS array. */ 869251881Speter for (i = 0; i < ignores->nelts; i++) 870251881Speter { 871251881Speter const char *ignore = APR_ARRAY_IDX(ignores, i, const char *); 872251881Speter APR_ARRAY_PUSH(*patterns, const char *) = apr_pstrdup(result_pool, 873251881Speter ignore); 874251881Speter } 875251881Speter 876251881Speter err = svn_wc__db_read_inherited_props(&inherited_props, &props, 877251881Speter db, local_abspath, 878251881Speter SVN_PROP_INHERITABLE_IGNORES, 879251881Speter scratch_pool, scratch_pool); 880251881Speter 881251881Speter if (err) 882251881Speter { 883251881Speter if (err->apr_err != SVN_ERR_WC_PATH_UNEXPECTED_STATUS) 884251881Speter return svn_error_trace(err); 885251881Speter 886251881Speter svn_error_clear(err); 887251881Speter return SVN_NO_ERROR; 888251881Speter } 889251881Speter 890251881Speter if (props) 891251881Speter { 892251881Speter const svn_string_t *value; 893251881Speter 894251881Speter value = svn_hash_gets(props, SVN_PROP_IGNORE); 895251881Speter if (value) 896251881Speter svn_cstring_split_append(*patterns, value->data, "\n\r", FALSE, 897251881Speter result_pool); 898251881Speter 899251881Speter value = svn_hash_gets(props, SVN_PROP_INHERITABLE_IGNORES); 900251881Speter if (value) 901251881Speter svn_cstring_split_append(*patterns, value->data, "\n\r", FALSE, 902251881Speter result_pool); 903251881Speter } 904251881Speter 905251881Speter for (i = 0; i < inherited_props->nelts; i++) 906251881Speter { 907251881Speter svn_prop_inherited_item_t *elt = APR_ARRAY_IDX( 908251881Speter inherited_props, i, svn_prop_inherited_item_t *); 909251881Speter const svn_string_t *value; 910251881Speter 911251881Speter value = svn_hash_gets(elt->prop_hash, SVN_PROP_INHERITABLE_IGNORES); 912251881Speter 913251881Speter if (value) 914251881Speter svn_cstring_split_append(*patterns, value->data, 915251881Speter "\n\r", FALSE, result_pool); 916251881Speter } 917251881Speter 918251881Speter return SVN_NO_ERROR; 919251881Speter} 920251881Speter 921251881Speter 922251881Speter/* Compare LOCAL_ABSPATH with items in the EXTERNALS hash to see if 923251881Speter LOCAL_ABSPATH is the drop location for, or an intermediate directory 924251881Speter of the drop location for, an externals definition. Use SCRATCH_POOL 925251881Speter for scratchwork. */ 926251881Speterstatic svn_boolean_t 927251881Speteris_external_path(apr_hash_t *externals, 928251881Speter const char *local_abspath, 929251881Speter apr_pool_t *scratch_pool) 930251881Speter{ 931251881Speter apr_hash_index_t *hi; 932251881Speter 933251881Speter /* First try: does the path exist as a key in the hash? */ 934251881Speter if (svn_hash_gets(externals, local_abspath)) 935251881Speter return TRUE; 936251881Speter 937251881Speter /* Failing that, we need to check if any external is a child of 938251881Speter LOCAL_ABSPATH. */ 939251881Speter for (hi = apr_hash_first(scratch_pool, externals); 940251881Speter hi; 941251881Speter hi = apr_hash_next(hi)) 942251881Speter { 943251881Speter const char *external_abspath = svn__apr_hash_index_key(hi); 944251881Speter 945251881Speter if (svn_dirent_is_child(local_abspath, external_abspath, NULL)) 946251881Speter return TRUE; 947251881Speter } 948251881Speter 949251881Speter return FALSE; 950251881Speter} 951251881Speter 952251881Speter 953251881Speter/* Assuming that LOCAL_ABSPATH is unversioned, send a status structure 954251881Speter for it through STATUS_FUNC/STATUS_BATON unless this path is being 955251881Speter ignored. This function should never be called on a versioned entry. 956251881Speter 957251881Speter LOCAL_ABSPATH is the path to the unversioned file whose status is being 958251881Speter requested. PATH_KIND is the node kind of NAME as determined by the 959251881Speter caller. PATH_SPECIAL is the special status of the path, also determined 960251881Speter by the caller. 961251881Speter PATTERNS points to a list of filename patterns which are marked as ignored. 962251881Speter None of these parameter may be NULL. 963251881Speter 964251881Speter If NO_IGNORE is TRUE, the item will be added regardless of 965251881Speter whether it is ignored; otherwise we will only add the item if it 966251881Speter does not match any of the patterns in PATTERN or INHERITED_IGNORES. 967251881Speter 968251881Speter Allocate everything in POOL. 969251881Speter*/ 970251881Speterstatic svn_error_t * 971251881Spetersend_unversioned_item(const struct walk_status_baton *wb, 972251881Speter const char *local_abspath, 973251881Speter const svn_io_dirent2_t *dirent, 974251881Speter svn_boolean_t tree_conflicted, 975251881Speter const apr_array_header_t *patterns, 976251881Speter svn_boolean_t no_ignore, 977251881Speter svn_wc_status_func4_t status_func, 978251881Speter void *status_baton, 979251881Speter apr_pool_t *scratch_pool) 980251881Speter{ 981251881Speter svn_boolean_t is_ignored; 982251881Speter svn_boolean_t is_external; 983251881Speter svn_wc_status3_t *status; 984251881Speter const char *base_name = svn_dirent_basename(local_abspath, NULL); 985251881Speter 986251881Speter is_ignored = svn_wc_match_ignore_list(base_name, patterns, scratch_pool); 987251881Speter SVN_ERR(assemble_unversioned(&status, 988251881Speter wb->db, local_abspath, 989251881Speter dirent, tree_conflicted, 990251881Speter is_ignored, 991251881Speter scratch_pool, scratch_pool)); 992251881Speter 993251881Speter is_external = is_external_path(wb->externals, local_abspath, scratch_pool); 994251881Speter if (is_external) 995251881Speter status->node_status = svn_wc_status_external; 996251881Speter 997251881Speter /* We can have a tree conflict on an unversioned path, i.e. an incoming 998251881Speter * delete on a locally deleted path during an update. Don't ever ignore 999251881Speter * those! */ 1000251881Speter if (status->conflicted) 1001251881Speter is_ignored = FALSE; 1002251881Speter 1003251881Speter /* If we aren't ignoring it, or if it's an externals path, pass this 1004251881Speter entry to the status func. */ 1005251881Speter if (no_ignore 1006251881Speter || !is_ignored 1007251881Speter || is_external) 1008251881Speter return svn_error_trace((*status_func)(status_baton, local_abspath, 1009251881Speter status, scratch_pool)); 1010251881Speter 1011251881Speter return SVN_NO_ERROR; 1012251881Speter} 1013251881Speter 1014251881Speterstatic svn_error_t * 1015251881Speterget_dir_status(const struct walk_status_baton *wb, 1016251881Speter const char *local_abspath, 1017251881Speter svn_boolean_t skip_this_dir, 1018251881Speter const char *parent_repos_root_url, 1019251881Speter const char *parent_repos_relpath, 1020251881Speter const char *parent_repos_uuid, 1021251881Speter const struct svn_wc__db_info_t *dir_info, 1022251881Speter const svn_io_dirent2_t *dirent, 1023251881Speter const apr_array_header_t *ignore_patterns, 1024251881Speter svn_depth_t depth, 1025251881Speter svn_boolean_t get_all, 1026251881Speter svn_boolean_t no_ignore, 1027251881Speter svn_wc_status_func4_t status_func, 1028251881Speter void *status_baton, 1029251881Speter svn_cancel_func_t cancel_func, 1030251881Speter void *cancel_baton, 1031251881Speter apr_pool_t *scratch_pool); 1032251881Speter 1033251881Speter/* Send out a status structure according to the information gathered on one 1034251881Speter * child node. (Basically this function is the guts of the loop in 1035251881Speter * get_dir_status() and of get_child_status().) 1036251881Speter * 1037251881Speter * Send a status structure of LOCAL_ABSPATH. PARENT_ABSPATH must be the 1038251881Speter * dirname of LOCAL_ABSPATH. 1039251881Speter * 1040251881Speter * INFO should reflect the information on LOCAL_ABSPATH; LOCAL_ABSPATH must 1041251881Speter * be an unversioned file or dir, or a versioned file. For versioned 1042251881Speter * directories use get_dir_status() instead. 1043251881Speter * 1044251881Speter * INFO may be NULL for an unversioned node. If such node has a tree conflict, 1045251881Speter * UNVERSIONED_TREE_CONFLICTED may be set to TRUE. If INFO is non-NULL, 1046251881Speter * UNVERSIONED_TREE_CONFLICTED is ignored. 1047251881Speter * 1048251881Speter * DIRENT should reflect LOCAL_ABSPATH's dirent information. 1049251881Speter * 1050251881Speter * DIR_REPOS_* should reflect LOCAL_ABSPATH's parent URL, i.e. LOCAL_ABSPATH's 1051251881Speter * URL treated with svn_uri_dirname(). ### TODO verify this (externals) 1052251881Speter * 1053251881Speter * If *COLLECTED_IGNORE_PATTERNS is NULL and ignore patterns are needed in this 1054251881Speter * call, then *COLLECTED_IGNORE_PATTERNS will be set to an apr_array_header_t* 1055251881Speter * containing all ignore patterns, as returned by collect_ignore_patterns() on 1056251881Speter * PARENT_ABSPATH and IGNORE_PATTERNS. If *COLLECTED_IGNORE_PATTERNS is passed 1057251881Speter * non-NULL, it is assumed it already holds those results. 1058251881Speter * This speeds up repeated calls with the same PARENT_ABSPATH. 1059251881Speter * 1060251881Speter * *COLLECTED_IGNORE_PATTERNS will be allocated in RESULT_POOL. All other 1061251881Speter * allocations are made in SCRATCH_POOL. 1062251881Speter * 1063251881Speter * The remaining parameters correspond to get_dir_status(). */ 1064251881Speterstatic svn_error_t * 1065251881Speterone_child_status(const struct walk_status_baton *wb, 1066251881Speter const char *local_abspath, 1067251881Speter const char *parent_abspath, 1068251881Speter const struct svn_wc__db_info_t *info, 1069251881Speter const svn_io_dirent2_t *dirent, 1070251881Speter const char *dir_repos_root_url, 1071251881Speter const char *dir_repos_relpath, 1072251881Speter const char *dir_repos_uuid, 1073251881Speter svn_boolean_t unversioned_tree_conflicted, 1074251881Speter apr_array_header_t **collected_ignore_patterns, 1075251881Speter const apr_array_header_t *ignore_patterns, 1076251881Speter svn_depth_t depth, 1077251881Speter svn_boolean_t get_all, 1078251881Speter svn_boolean_t no_ignore, 1079251881Speter svn_wc_status_func4_t status_func, 1080251881Speter void *status_baton, 1081251881Speter svn_cancel_func_t cancel_func, 1082251881Speter void *cancel_baton, 1083251881Speter apr_pool_t *result_pool, 1084251881Speter apr_pool_t *scratch_pool) 1085251881Speter{ 1086251881Speter svn_boolean_t conflicted = info ? info->conflicted 1087251881Speter : unversioned_tree_conflicted; 1088251881Speter 1089251881Speter if (info 1090251881Speter && info->status != svn_wc__db_status_not_present 1091251881Speter && info->status != svn_wc__db_status_excluded 1092251881Speter && info->status != svn_wc__db_status_server_excluded 1093251881Speter && !(info->kind == svn_node_unknown 1094251881Speter && info->status == svn_wc__db_status_normal)) 1095251881Speter { 1096251881Speter if (depth == svn_depth_files 1097251881Speter && info->kind == svn_node_dir) 1098251881Speter { 1099251881Speter return SVN_NO_ERROR; 1100251881Speter } 1101251881Speter 1102251881Speter SVN_ERR(send_status_structure(wb, local_abspath, 1103251881Speter dir_repos_root_url, 1104251881Speter dir_repos_relpath, 1105251881Speter dir_repos_uuid, 1106251881Speter info, dirent, get_all, 1107251881Speter status_func, status_baton, 1108251881Speter scratch_pool)); 1109251881Speter 1110251881Speter /* Descend in subdirectories. */ 1111251881Speter if (depth == svn_depth_infinity 1112251881Speter && info->kind == svn_node_dir) 1113251881Speter { 1114251881Speter SVN_ERR(get_dir_status(wb, local_abspath, TRUE, 1115251881Speter dir_repos_root_url, dir_repos_relpath, 1116251881Speter dir_repos_uuid, info, 1117251881Speter dirent, ignore_patterns, 1118251881Speter svn_depth_infinity, get_all, 1119251881Speter no_ignore, 1120251881Speter status_func, status_baton, 1121251881Speter cancel_func, cancel_baton, 1122251881Speter scratch_pool)); 1123251881Speter } 1124251881Speter 1125251881Speter return SVN_NO_ERROR; 1126251881Speter } 1127251881Speter 1128251881Speter /* If conflicted, fall right through to unversioned. 1129251881Speter * With depth_files, show all conflicts, even if their report is only 1130251881Speter * about directories. A tree conflict may actually report two different 1131251881Speter * kinds, so it's not so easy to define what depth=files means. We could go 1132251881Speter * look up the kinds in the conflict ... just show all. */ 1133251881Speter if (! conflicted) 1134251881Speter { 1135251881Speter /* Selected node, but not found */ 1136251881Speter if (dirent == NULL) 1137251881Speter return SVN_NO_ERROR; 1138251881Speter 1139251881Speter if (depth == svn_depth_files && dirent->kind == svn_node_dir) 1140251881Speter return SVN_NO_ERROR; 1141251881Speter 1142251881Speter if (svn_wc_is_adm_dir(svn_dirent_basename(local_abspath, NULL), 1143251881Speter scratch_pool)) 1144251881Speter return SVN_NO_ERROR; 1145251881Speter } 1146251881Speter 1147251881Speter /* The node exists on disk but there is no versioned information about it, 1148251881Speter * or it doesn't exist but is a tree conflicted path or should be 1149251881Speter * reported not-present. */ 1150251881Speter 1151251881Speter /* Why pass ignore patterns on a tree conflicted node, even if it should 1152251881Speter * always show up in clients' status reports anyway? Because the calling 1153251881Speter * client decides whether to ignore, and thus this flag needs to be 1154251881Speter * determined. For example, in 'svn status', plain unversioned nodes show 1155251881Speter * as '? C', where ignored ones show as 'I C'. */ 1156251881Speter 1157251881Speter if (ignore_patterns && ! *collected_ignore_patterns) 1158251881Speter SVN_ERR(collect_ignore_patterns(collected_ignore_patterns, 1159251881Speter wb->db, parent_abspath, ignore_patterns, 1160251881Speter result_pool, scratch_pool)); 1161251881Speter 1162251881Speter SVN_ERR(send_unversioned_item(wb, 1163251881Speter local_abspath, 1164251881Speter dirent, 1165251881Speter conflicted, 1166251881Speter *collected_ignore_patterns, 1167251881Speter no_ignore, 1168251881Speter status_func, status_baton, 1169251881Speter scratch_pool)); 1170251881Speter 1171251881Speter return SVN_NO_ERROR; 1172251881Speter} 1173251881Speter 1174251881Speter/* Send svn_wc_status3_t * structures for the directory LOCAL_ABSPATH and 1175251881Speter for all its child nodes (according to DEPTH) through STATUS_FUNC / 1176251881Speter STATUS_BATON. 1177251881Speter 1178251881Speter If SKIP_THIS_DIR is TRUE, the directory's own status will not be reported. 1179251881Speter All subdirs reached by recursion will be reported regardless of this 1180251881Speter parameter's value. 1181251881Speter 1182251881Speter PARENT_REPOS_* parameters can be set to refer to LOCAL_ABSPATH's parent's 1183251881Speter URL, i.e. the URL the WC reflects at the dirname of LOCAL_ABSPATH, to avoid 1184251881Speter retrieving them again. Otherwise they must be NULL. 1185251881Speter 1186251881Speter DIR_INFO can be set to the information of LOCAL_ABSPATH, to avoid retrieving 1187251881Speter it again. Otherwise it must be NULL. 1188251881Speter 1189251881Speter DIRENT is LOCAL_ABSPATH's own dirent and is only needed if it is reported, 1190251881Speter so if SKIP_THIS_DIR is TRUE, DIRENT can be left NULL. 1191251881Speter 1192251881Speter Other arguments are the same as those passed to 1193251881Speter svn_wc_get_status_editor5(). */ 1194251881Speterstatic svn_error_t * 1195251881Speterget_dir_status(const struct walk_status_baton *wb, 1196251881Speter const char *local_abspath, 1197251881Speter svn_boolean_t skip_this_dir, 1198251881Speter const char *parent_repos_root_url, 1199251881Speter const char *parent_repos_relpath, 1200251881Speter const char *parent_repos_uuid, 1201251881Speter const struct svn_wc__db_info_t *dir_info, 1202251881Speter const svn_io_dirent2_t *dirent, 1203251881Speter const apr_array_header_t *ignore_patterns, 1204251881Speter svn_depth_t depth, 1205251881Speter svn_boolean_t get_all, 1206251881Speter svn_boolean_t no_ignore, 1207251881Speter svn_wc_status_func4_t status_func, 1208251881Speter void *status_baton, 1209251881Speter svn_cancel_func_t cancel_func, 1210251881Speter void *cancel_baton, 1211251881Speter apr_pool_t *scratch_pool) 1212251881Speter{ 1213251881Speter const char *dir_repos_root_url; 1214251881Speter const char *dir_repos_relpath; 1215251881Speter const char *dir_repos_uuid; 1216251881Speter apr_hash_t *dirents, *nodes, *conflicts, *all_children; 1217251881Speter apr_array_header_t *sorted_children; 1218251881Speter apr_array_header_t *collected_ignore_patterns = NULL; 1219251881Speter apr_pool_t *iterpool; 1220251881Speter svn_error_t *err; 1221251881Speter int i; 1222251881Speter 1223251881Speter if (cancel_func) 1224251881Speter SVN_ERR(cancel_func(cancel_baton)); 1225251881Speter 1226251881Speter if (depth == svn_depth_unknown) 1227251881Speter depth = svn_depth_infinity; 1228251881Speter 1229251881Speter iterpool = svn_pool_create(scratch_pool); 1230251881Speter 1231251881Speter err = svn_io_get_dirents3(&dirents, local_abspath, FALSE, scratch_pool, 1232251881Speter iterpool); 1233251881Speter if (err 1234251881Speter && (APR_STATUS_IS_ENOENT(err->apr_err) 1235251881Speter || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err))) 1236251881Speter { 1237251881Speter svn_error_clear(err); 1238251881Speter dirents = apr_hash_make(scratch_pool); 1239251881Speter } 1240251881Speter else 1241251881Speter SVN_ERR(err); 1242251881Speter 1243251881Speter if (!dir_info) 1244269847Speter SVN_ERR(svn_wc__db_read_single_info(&dir_info, wb->db, local_abspath, 1245269847Speter scratch_pool, iterpool)); 1246251881Speter 1247251881Speter SVN_ERR(get_repos_root_url_relpath(&dir_repos_relpath, &dir_repos_root_url, 1248251881Speter &dir_repos_uuid, dir_info, 1249251881Speter parent_repos_relpath, 1250251881Speter parent_repos_root_url, parent_repos_uuid, 1251251881Speter wb->db, local_abspath, 1252251881Speter scratch_pool, iterpool)); 1253251881Speter 1254251881Speter /* Create a hash containing all children. The source hashes 1255251881Speter don't all map the same types, but only the keys of the result 1256251881Speter hash are subsequently used. */ 1257251881Speter SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts, 1258251881Speter wb->db, local_abspath, 1259251881Speter scratch_pool, iterpool)); 1260251881Speter 1261251881Speter all_children = apr_hash_overlay(scratch_pool, nodes, dirents); 1262251881Speter if (apr_hash_count(conflicts) > 0) 1263251881Speter all_children = apr_hash_overlay(scratch_pool, conflicts, all_children); 1264251881Speter 1265251881Speter /* Handle "this-dir" first. */ 1266251881Speter if (! skip_this_dir) 1267251881Speter { 1268251881Speter /* This code is not conditional on HAVE_SYMLINK as some systems that do 1269251881Speter not allow creating symlinks (!HAVE_SYMLINK) can still encounter 1270251881Speter symlinks (or in case of Windows also 'Junctions') created by other 1271251881Speter methods. 1272251881Speter 1273251881Speter Without this block a working copy in the root of a junction is 1274251881Speter reported as an obstruction, because the junction itself is reported as 1275251881Speter special. 1276251881Speter 1277251881Speter Systems that have no symlink support at all, would always see 1278251881Speter dirent->special as FALSE, so even there enabling this code shouldn't 1279251881Speter produce problems. 1280251881Speter */ 1281251881Speter if (dirent->special) 1282251881Speter { 1283251881Speter svn_io_dirent2_t *this_dirent = svn_io_dirent2_dup(dirent, iterpool); 1284251881Speter 1285251881Speter /* We're being pointed to "this-dir" via a symlink. 1286251881Speter * Get the real node kind and pretend the path is not a symlink. 1287251881Speter * This prevents send_status_structure() from treating this-dir 1288251881Speter * as a directory obstructed by a file. */ 1289251881Speter SVN_ERR(svn_io_check_resolved_path(local_abspath, 1290251881Speter &this_dirent->kind, iterpool)); 1291251881Speter this_dirent->special = FALSE; 1292251881Speter SVN_ERR(send_status_structure(wb, local_abspath, 1293251881Speter parent_repos_root_url, 1294251881Speter parent_repos_relpath, 1295251881Speter parent_repos_uuid, 1296251881Speter dir_info, this_dirent, get_all, 1297251881Speter status_func, status_baton, 1298251881Speter iterpool)); 1299251881Speter } 1300251881Speter else 1301251881Speter SVN_ERR(send_status_structure(wb, local_abspath, 1302251881Speter parent_repos_root_url, 1303251881Speter parent_repos_relpath, 1304251881Speter parent_repos_uuid, 1305251881Speter dir_info, dirent, get_all, 1306251881Speter status_func, status_baton, 1307251881Speter iterpool)); 1308251881Speter } 1309251881Speter 1310251881Speter /* If the requested depth is empty, we only need status on this-dir. */ 1311251881Speter if (depth == svn_depth_empty) 1312251881Speter return SVN_NO_ERROR; 1313251881Speter 1314251881Speter /* Walk all the children of this directory. */ 1315251881Speter sorted_children = svn_sort__hash(all_children, 1316251881Speter svn_sort_compare_items_lexically, 1317251881Speter scratch_pool); 1318251881Speter for (i = 0; i < sorted_children->nelts; i++) 1319251881Speter { 1320251881Speter const void *key; 1321251881Speter apr_ssize_t klen; 1322251881Speter svn_sort__item_t item; 1323251881Speter const char *child_abspath; 1324251881Speter svn_io_dirent2_t *child_dirent; 1325251881Speter const struct svn_wc__db_info_t *child_info; 1326251881Speter 1327251881Speter svn_pool_clear(iterpool); 1328251881Speter 1329251881Speter item = APR_ARRAY_IDX(sorted_children, i, svn_sort__item_t); 1330251881Speter key = item.key; 1331251881Speter klen = item.klen; 1332251881Speter 1333251881Speter child_abspath = svn_dirent_join(local_abspath, key, iterpool); 1334251881Speter child_dirent = apr_hash_get(dirents, key, klen); 1335251881Speter child_info = apr_hash_get(nodes, key, klen); 1336251881Speter 1337251881Speter SVN_ERR(one_child_status(wb, 1338251881Speter child_abspath, 1339251881Speter local_abspath, 1340251881Speter child_info, 1341251881Speter child_dirent, 1342251881Speter dir_repos_root_url, 1343251881Speter dir_repos_relpath, 1344251881Speter dir_repos_uuid, 1345251881Speter apr_hash_get(conflicts, key, klen) != NULL, 1346251881Speter &collected_ignore_patterns, 1347251881Speter ignore_patterns, 1348251881Speter depth, 1349251881Speter get_all, 1350251881Speter no_ignore, 1351251881Speter status_func, 1352251881Speter status_baton, 1353251881Speter cancel_func, 1354251881Speter cancel_baton, 1355251881Speter scratch_pool, 1356251881Speter iterpool)); 1357251881Speter } 1358251881Speter 1359251881Speter /* Destroy our subpools. */ 1360251881Speter svn_pool_destroy(iterpool); 1361251881Speter 1362251881Speter return SVN_NO_ERROR; 1363251881Speter} 1364251881Speter 1365251881Speter/* Send an svn_wc_status3_t * structure for the versioned file, or for the 1366251881Speter * unversioned file or directory, LOCAL_ABSPATH, which is not ignored (an 1367251881Speter * explicit target). Does not recurse. 1368251881Speter * 1369251881Speter * INFO should reflect LOCAL_ABSPATH's information, but should be NULL for 1370251881Speter * unversioned nodes. An unversioned and tree-conflicted node however should 1371251881Speter * pass a non-NULL INFO as returned by read_info() (INFO->CONFLICTED = TRUE). 1372251881Speter * 1373251881Speter * DIRENT should reflect LOCAL_ABSPATH. 1374251881Speter * 1375251881Speter * All allocations made in SCRATCH_POOL. 1376251881Speter * 1377251881Speter * The remaining parameters correspond to get_dir_status(). */ 1378251881Speterstatic svn_error_t * 1379251881Speterget_child_status(const struct walk_status_baton *wb, 1380251881Speter const char *local_abspath, 1381251881Speter const struct svn_wc__db_info_t *info, 1382251881Speter const svn_io_dirent2_t *dirent, 1383251881Speter const apr_array_header_t *ignore_patterns, 1384251881Speter svn_boolean_t get_all, 1385251881Speter svn_wc_status_func4_t status_func, 1386251881Speter void *status_baton, 1387251881Speter svn_cancel_func_t cancel_func, 1388251881Speter void *cancel_baton, 1389251881Speter apr_pool_t *scratch_pool) 1390251881Speter{ 1391251881Speter const char *dir_repos_root_url; 1392251881Speter const char *dir_repos_relpath; 1393251881Speter const char *dir_repos_uuid; 1394251881Speter const struct svn_wc__db_info_t *dir_info; 1395251881Speter apr_array_header_t *collected_ignore_patterns = NULL; 1396251881Speter const char *parent_abspath = svn_dirent_dirname(local_abspath, 1397251881Speter scratch_pool); 1398251881Speter 1399251881Speter if (cancel_func) 1400251881Speter SVN_ERR(cancel_func(cancel_baton)); 1401251881Speter 1402251881Speter if (dirent->kind == svn_node_none) 1403251881Speter dirent = NULL; 1404251881Speter 1405269847Speter SVN_ERR(svn_wc__db_read_single_info(&dir_info, 1406269847Speter wb->db, parent_abspath, 1407269847Speter scratch_pool, scratch_pool)); 1408251881Speter 1409251881Speter SVN_ERR(get_repos_root_url_relpath(&dir_repos_relpath, &dir_repos_root_url, 1410251881Speter &dir_repos_uuid, dir_info, 1411251881Speter NULL, NULL, NULL, 1412251881Speter wb->db, parent_abspath, 1413251881Speter scratch_pool, scratch_pool)); 1414251881Speter 1415251881Speter /* An unversioned node with a tree conflict will see an INFO != NULL here, 1416251881Speter * in which case the FALSE passed for UNVERSIONED_TREE_CONFLICTED has no 1417251881Speter * effect and INFO->CONFLICTED counts. 1418251881Speter * ### Maybe svn_wc__db_read_children_info() and read_info() should be more 1419251881Speter * ### alike? */ 1420251881Speter SVN_ERR(one_child_status(wb, 1421251881Speter local_abspath, 1422251881Speter parent_abspath, 1423251881Speter info, 1424251881Speter dirent, 1425251881Speter dir_repos_root_url, 1426251881Speter dir_repos_relpath, 1427251881Speter dir_repos_uuid, 1428251881Speter FALSE, /* unversioned_tree_conflicted */ 1429251881Speter &collected_ignore_patterns, 1430251881Speter ignore_patterns, 1431251881Speter svn_depth_empty, 1432251881Speter get_all, 1433251881Speter TRUE, /* no_ignore. This is an explicit target. */ 1434251881Speter status_func, 1435251881Speter status_baton, 1436251881Speter cancel_func, 1437251881Speter cancel_baton, 1438251881Speter scratch_pool, 1439251881Speter scratch_pool)); 1440251881Speter return SVN_NO_ERROR; 1441251881Speter} 1442251881Speter 1443251881Speter 1444251881Speter 1445251881Speter/*** Helpers ***/ 1446251881Speter 1447251881Speter/* A faux status callback function for stashing STATUS item in an hash 1448251881Speter (which is the BATON), keyed on PATH. This implements the 1449251881Speter svn_wc_status_func4_t interface. */ 1450251881Speterstatic svn_error_t * 1451251881Speterhash_stash(void *baton, 1452251881Speter const char *path, 1453251881Speter const svn_wc_status3_t *status, 1454251881Speter apr_pool_t *scratch_pool) 1455251881Speter{ 1456251881Speter apr_hash_t *stat_hash = baton; 1457251881Speter apr_pool_t *hash_pool = apr_hash_pool_get(stat_hash); 1458251881Speter assert(! svn_hash_gets(stat_hash, path)); 1459251881Speter svn_hash_sets(stat_hash, apr_pstrdup(hash_pool, path), 1460251881Speter svn_wc_dup_status3(status, hash_pool)); 1461251881Speter 1462251881Speter return SVN_NO_ERROR; 1463251881Speter} 1464251881Speter 1465251881Speter 1466251881Speter/* Look up the key PATH in BATON->STATII. IS_DIR_BATON indicates whether 1467251881Speter baton is a struct *dir_baton or struct *file_baton. If the value doesn't 1468251881Speter yet exist, and the REPOS_NODE_STATUS indicates that this is an addition, 1469251881Speter create a new status struct using the hash's pool. 1470251881Speter 1471251881Speter If IS_DIR_BATON is true, THIS_DIR_BATON is a *dir_baton cotaining the out 1472251881Speter of date (ood) information we want to set in BATON. This is necessary 1473251881Speter because this function tweaks the status of out-of-date directories 1474251881Speter (BATON == THIS_DIR_BATON) and out-of-date directories' parents 1475251881Speter (BATON == THIS_DIR_BATON->parent_baton). In the latter case THIS_DIR_BATON 1476251881Speter contains the ood info we want to bubble up to ancestor directories so these 1477251881Speter accurately reflect the fact they have an ood descendant. 1478251881Speter 1479251881Speter Merge REPOS_NODE_STATUS, REPOS_TEXT_STATUS and REPOS_PROP_STATUS into the 1480251881Speter status structure's "network" fields. 1481251881Speter 1482251881Speter Iff IS_DIR_BATON is true, DELETED_REV is used as follows, otherwise it 1483251881Speter is ignored: 1484251881Speter 1485251881Speter If REPOS_NODE_STATUS is svn_wc_status_deleted then DELETED_REV is 1486251881Speter optionally the revision path was deleted, in all other cases it must 1487251881Speter be set to SVN_INVALID_REVNUM. If DELETED_REV is not 1488251881Speter SVN_INVALID_REVNUM and REPOS_TEXT_STATUS is svn_wc_status_deleted, 1489251881Speter then use DELETED_REV to set PATH's ood_last_cmt_rev field in BATON. 1490251881Speter If DELETED_REV is SVN_INVALID_REVNUM and REPOS_NODE_STATUS is 1491251881Speter svn_wc_status_deleted, set PATH's ood_last_cmt_rev to its parent's 1492251881Speter ood_last_cmt_rev value - see comment below. 1493251881Speter 1494251881Speter If a new struct was added, set the repos_lock to REPOS_LOCK. */ 1495251881Speterstatic svn_error_t * 1496251881Spetertweak_statushash(void *baton, 1497251881Speter void *this_dir_baton, 1498251881Speter svn_boolean_t is_dir_baton, 1499251881Speter svn_wc__db_t *db, 1500251881Speter const char *local_abspath, 1501251881Speter enum svn_wc_status_kind repos_node_status, 1502251881Speter enum svn_wc_status_kind repos_text_status, 1503251881Speter enum svn_wc_status_kind repos_prop_status, 1504251881Speter svn_revnum_t deleted_rev, 1505251881Speter const svn_lock_t *repos_lock, 1506251881Speter apr_pool_t *scratch_pool) 1507251881Speter{ 1508251881Speter svn_wc_status3_t *statstruct; 1509251881Speter apr_pool_t *pool; 1510251881Speter apr_hash_t *statushash; 1511251881Speter 1512251881Speter if (is_dir_baton) 1513251881Speter statushash = ((struct dir_baton *) baton)->statii; 1514251881Speter else 1515251881Speter statushash = ((struct file_baton *) baton)->dir_baton->statii; 1516251881Speter pool = apr_hash_pool_get(statushash); 1517251881Speter 1518251881Speter /* Is PATH already a hash-key? */ 1519251881Speter statstruct = svn_hash_gets(statushash, local_abspath); 1520251881Speter 1521251881Speter /* If not, make it so. */ 1522251881Speter if (! statstruct) 1523251881Speter { 1524251881Speter /* If this item isn't being added, then we're most likely 1525251881Speter dealing with a non-recursive (or at least partially 1526251881Speter non-recursive) working copy. Due to bugs in how the client 1527251881Speter reports the state of non-recursive working copies, the 1528251881Speter repository can send back responses about paths that don't 1529251881Speter even exist locally. Our best course here is just to ignore 1530251881Speter those responses. After all, if the client had reported 1531251881Speter correctly in the first, that path would either be mentioned 1532251881Speter as an 'add' or not mentioned at all, depending on how we 1533251881Speter eventually fix the bugs in non-recursivity. See issue 1534251881Speter #2122 for details. */ 1535251881Speter if (repos_node_status != svn_wc_status_added) 1536251881Speter return SVN_NO_ERROR; 1537251881Speter 1538251881Speter /* Use the public API to get a statstruct, and put it into the hash. */ 1539251881Speter SVN_ERR(internal_status(&statstruct, db, local_abspath, pool, 1540251881Speter scratch_pool)); 1541251881Speter statstruct->repos_lock = repos_lock; 1542251881Speter svn_hash_sets(statushash, apr_pstrdup(pool, local_abspath), statstruct); 1543251881Speter } 1544251881Speter 1545251881Speter /* Merge a repos "delete" + "add" into a single "replace". */ 1546251881Speter if ((repos_node_status == svn_wc_status_added) 1547251881Speter && (statstruct->repos_node_status == svn_wc_status_deleted)) 1548251881Speter repos_node_status = svn_wc_status_replaced; 1549251881Speter 1550251881Speter /* Tweak the structure's repos fields. */ 1551251881Speter if (repos_node_status) 1552251881Speter statstruct->repos_node_status = repos_node_status; 1553251881Speter if (repos_text_status) 1554251881Speter statstruct->repos_text_status = repos_text_status; 1555251881Speter if (repos_prop_status) 1556251881Speter statstruct->repos_prop_status = repos_prop_status; 1557251881Speter 1558251881Speter /* Copy out-of-date info. */ 1559251881Speter if (is_dir_baton) 1560251881Speter { 1561251881Speter struct dir_baton *b = this_dir_baton; 1562251881Speter 1563251881Speter if (!statstruct->repos_relpath && b->repos_relpath) 1564251881Speter { 1565251881Speter if (statstruct->repos_node_status == svn_wc_status_deleted) 1566251881Speter { 1567251881Speter /* When deleting PATH, BATON is for PATH's parent, 1568251881Speter so we must construct PATH's real statstruct->url. */ 1569251881Speter statstruct->repos_relpath = 1570251881Speter svn_relpath_join(b->repos_relpath, 1571251881Speter svn_dirent_basename(local_abspath, 1572251881Speter NULL), 1573251881Speter pool); 1574251881Speter } 1575251881Speter else 1576251881Speter statstruct->repos_relpath = apr_pstrdup(pool, b->repos_relpath); 1577251881Speter 1578251881Speter statstruct->repos_root_url = 1579251881Speter b->edit_baton->anchor_status->repos_root_url; 1580251881Speter statstruct->repos_uuid = 1581251881Speter b->edit_baton->anchor_status->repos_uuid; 1582251881Speter } 1583251881Speter 1584251881Speter /* The last committed date, and author for deleted items 1585251881Speter isn't available. */ 1586251881Speter if (statstruct->repos_node_status == svn_wc_status_deleted) 1587251881Speter { 1588251881Speter statstruct->ood_kind = statstruct->kind; 1589251881Speter 1590251881Speter /* Pre 1.5 servers don't provide the revision a path was deleted. 1591251881Speter So we punt and use the last committed revision of the path's 1592251881Speter parent, which has some chance of being correct. At worse it 1593251881Speter is a higher revision than the path was deleted, but this is 1594251881Speter better than nothing... */ 1595251881Speter if (deleted_rev == SVN_INVALID_REVNUM) 1596251881Speter statstruct->ood_changed_rev = 1597251881Speter ((struct dir_baton *) baton)->ood_changed_rev; 1598251881Speter else 1599251881Speter statstruct->ood_changed_rev = deleted_rev; 1600251881Speter } 1601251881Speter else 1602251881Speter { 1603251881Speter statstruct->ood_kind = b->ood_kind; 1604251881Speter statstruct->ood_changed_rev = b->ood_changed_rev; 1605251881Speter statstruct->ood_changed_date = b->ood_changed_date; 1606251881Speter if (b->ood_changed_author) 1607251881Speter statstruct->ood_changed_author = 1608251881Speter apr_pstrdup(pool, b->ood_changed_author); 1609251881Speter } 1610251881Speter 1611251881Speter } 1612251881Speter else 1613251881Speter { 1614251881Speter struct file_baton *b = baton; 1615251881Speter statstruct->ood_changed_rev = b->ood_changed_rev; 1616251881Speter statstruct->ood_changed_date = b->ood_changed_date; 1617251881Speter if (!statstruct->repos_relpath && b->repos_relpath) 1618251881Speter { 1619251881Speter statstruct->repos_relpath = apr_pstrdup(pool, b->repos_relpath); 1620251881Speter statstruct->repos_root_url = 1621251881Speter b->edit_baton->anchor_status->repos_root_url; 1622251881Speter statstruct->repos_uuid = 1623251881Speter b->edit_baton->anchor_status->repos_uuid; 1624251881Speter } 1625251881Speter statstruct->ood_kind = b->ood_kind; 1626251881Speter if (b->ood_changed_author) 1627251881Speter statstruct->ood_changed_author = 1628251881Speter apr_pstrdup(pool, b->ood_changed_author); 1629251881Speter } 1630251881Speter return SVN_NO_ERROR; 1631251881Speter} 1632251881Speter 1633251881Speter/* Returns the URL for DB */ 1634251881Speterstatic const char * 1635251881Speterfind_dir_repos_relpath(const struct dir_baton *db, apr_pool_t *pool) 1636251881Speter{ 1637251881Speter /* If we have no name, we're the root, return the anchor URL. */ 1638251881Speter if (! db->name) 1639251881Speter return db->edit_baton->anchor_status->repos_relpath; 1640251881Speter else 1641251881Speter { 1642251881Speter const char *repos_relpath; 1643251881Speter struct dir_baton *pb = db->parent_baton; 1644251881Speter const svn_wc_status3_t *status = svn_hash_gets(pb->statii, 1645251881Speter db->local_abspath); 1646251881Speter /* Note that status->repos_relpath could be NULL in the case of a missing 1647251881Speter * directory, which means we need to recurse up another level to get 1648251881Speter * a useful relpath. */ 1649251881Speter if (status && status->repos_relpath) 1650251881Speter return status->repos_relpath; 1651251881Speter 1652251881Speter repos_relpath = find_dir_repos_relpath(pb, pool); 1653251881Speter return svn_relpath_join(repos_relpath, db->name, pool); 1654251881Speter } 1655251881Speter} 1656251881Speter 1657251881Speter 1658251881Speter 1659251881Speter/* Create a new dir_baton for subdir PATH. */ 1660251881Speterstatic svn_error_t * 1661251881Spetermake_dir_baton(void **dir_baton, 1662251881Speter const char *path, 1663251881Speter struct edit_baton *edit_baton, 1664251881Speter struct dir_baton *parent_baton, 1665251881Speter apr_pool_t *result_pool) 1666251881Speter{ 1667251881Speter struct dir_baton *pb = parent_baton; 1668251881Speter struct edit_baton *eb = edit_baton; 1669251881Speter struct dir_baton *d; 1670251881Speter const char *local_abspath; 1671251881Speter const svn_wc_status3_t *status_in_parent; 1672251881Speter apr_pool_t *dir_pool; 1673251881Speter 1674251881Speter if (parent_baton) 1675251881Speter dir_pool = svn_pool_create(parent_baton->pool); 1676251881Speter else 1677251881Speter dir_pool = svn_pool_create(result_pool); 1678251881Speter 1679251881Speter d = apr_pcalloc(dir_pool, sizeof(*d)); 1680251881Speter 1681251881Speter SVN_ERR_ASSERT(path || (! pb)); 1682251881Speter 1683251881Speter /* Construct the absolute path of this directory. */ 1684251881Speter if (pb) 1685251881Speter local_abspath = svn_dirent_join(eb->anchor_abspath, path, dir_pool); 1686251881Speter else 1687251881Speter local_abspath = eb->anchor_abspath; 1688251881Speter 1689251881Speter /* Finish populating the baton members. */ 1690251881Speter d->pool = dir_pool; 1691251881Speter d->local_abspath = local_abspath; 1692251881Speter d->name = path ? svn_dirent_basename(path, dir_pool) : NULL; 1693251881Speter d->edit_baton = edit_baton; 1694251881Speter d->parent_baton = parent_baton; 1695251881Speter d->statii = apr_hash_make(dir_pool); 1696251881Speter d->ood_changed_rev = SVN_INVALID_REVNUM; 1697251881Speter d->ood_changed_date = 0; 1698251881Speter d->repos_relpath = find_dir_repos_relpath(d, dir_pool); 1699251881Speter d->ood_kind = svn_node_dir; 1700251881Speter d->ood_changed_author = NULL; 1701251881Speter 1702251881Speter if (pb) 1703251881Speter { 1704251881Speter if (pb->excluded) 1705251881Speter d->excluded = TRUE; 1706251881Speter else if (pb->depth == svn_depth_immediates) 1707251881Speter d->depth = svn_depth_empty; 1708251881Speter else if (pb->depth == svn_depth_files || pb->depth == svn_depth_empty) 1709251881Speter d->excluded = TRUE; 1710251881Speter else if (pb->depth == svn_depth_unknown) 1711251881Speter /* This is only tentative, it can be overridden from d's entry 1712251881Speter later. */ 1713251881Speter d->depth = svn_depth_unknown; 1714251881Speter else 1715251881Speter d->depth = svn_depth_infinity; 1716251881Speter } 1717251881Speter else 1718251881Speter { 1719251881Speter d->depth = eb->default_depth; 1720251881Speter } 1721251881Speter 1722251881Speter /* Get the status for this path's children. Of course, we only want 1723251881Speter to do this if the path is versioned as a directory. */ 1724251881Speter if (pb) 1725251881Speter status_in_parent = svn_hash_gets(pb->statii, d->local_abspath); 1726251881Speter else 1727251881Speter status_in_parent = eb->anchor_status; 1728251881Speter 1729251881Speter if (status_in_parent 1730251881Speter && status_in_parent->versioned 1731251881Speter && (status_in_parent->kind == svn_node_dir) 1732251881Speter && (! d->excluded) 1733251881Speter && (d->depth == svn_depth_unknown 1734251881Speter || d->depth == svn_depth_infinity 1735251881Speter || d->depth == svn_depth_files 1736251881Speter || d->depth == svn_depth_immediates) 1737251881Speter ) 1738251881Speter { 1739251881Speter const svn_wc_status3_t *this_dir_status; 1740251881Speter const apr_array_header_t *ignores = eb->ignores; 1741251881Speter 1742251881Speter SVN_ERR(get_dir_status(&eb->wb, local_abspath, TRUE, 1743251881Speter status_in_parent->repos_root_url, 1744251881Speter NULL /*parent_repos_relpath*/, 1745251881Speter status_in_parent->repos_uuid, 1746251881Speter NULL, 1747251881Speter NULL /* dirent */, ignores, 1748251881Speter d->depth == svn_depth_files 1749251881Speter ? svn_depth_files 1750251881Speter : svn_depth_immediates, 1751251881Speter TRUE, TRUE, 1752251881Speter hash_stash, d->statii, 1753251881Speter eb->cancel_func, eb->cancel_baton, 1754251881Speter dir_pool)); 1755251881Speter 1756251881Speter /* If we found a depth here, it should govern. */ 1757251881Speter this_dir_status = svn_hash_gets(d->statii, d->local_abspath); 1758251881Speter if (this_dir_status && this_dir_status->versioned 1759251881Speter && (d->depth == svn_depth_unknown 1760251881Speter || d->depth > status_in_parent->depth)) 1761251881Speter { 1762251881Speter d->depth = this_dir_status->depth; 1763251881Speter } 1764251881Speter } 1765251881Speter 1766251881Speter *dir_baton = d; 1767251881Speter return SVN_NO_ERROR; 1768251881Speter} 1769251881Speter 1770251881Speter 1771251881Speter/* Make a file baton, using a new subpool of PARENT_DIR_BATON's pool. 1772251881Speter NAME is just one component, not a path. */ 1773251881Speterstatic struct file_baton * 1774251881Spetermake_file_baton(struct dir_baton *parent_dir_baton, 1775251881Speter const char *path, 1776251881Speter apr_pool_t *pool) 1777251881Speter{ 1778251881Speter struct dir_baton *pb = parent_dir_baton; 1779251881Speter struct edit_baton *eb = pb->edit_baton; 1780251881Speter struct file_baton *f = apr_pcalloc(pool, sizeof(*f)); 1781251881Speter 1782251881Speter /* Finish populating the baton members. */ 1783251881Speter f->local_abspath = svn_dirent_join(eb->anchor_abspath, path, pool); 1784251881Speter f->name = svn_dirent_basename(f->local_abspath, NULL); 1785251881Speter f->pool = pool; 1786251881Speter f->dir_baton = pb; 1787251881Speter f->edit_baton = eb; 1788251881Speter f->ood_changed_rev = SVN_INVALID_REVNUM; 1789251881Speter f->ood_changed_date = 0; 1790251881Speter f->repos_relpath = svn_relpath_join(find_dir_repos_relpath(pb, pool), 1791251881Speter f->name, pool); 1792251881Speter f->ood_kind = svn_node_file; 1793251881Speter f->ood_changed_author = NULL; 1794251881Speter return f; 1795251881Speter} 1796251881Speter 1797251881Speter 1798251881Speter/** 1799251881Speter * Return a boolean answer to the question "Is @a status something that 1800251881Speter * should be reported?". @a no_ignore and @a get_all are the same as 1801251881Speter * svn_wc_get_status_editor4(). 1802251881Speter */ 1803251881Speterstatic svn_boolean_t 1804251881Speteris_sendable_status(const svn_wc_status3_t *status, 1805251881Speter svn_boolean_t no_ignore, 1806251881Speter svn_boolean_t get_all) 1807251881Speter{ 1808251881Speter /* If the repository status was touched at all, it's interesting. */ 1809251881Speter if (status->repos_node_status != svn_wc_status_none) 1810251881Speter return TRUE; 1811251881Speter 1812251881Speter /* If there is a lock in the repository, send it. */ 1813251881Speter if (status->repos_lock) 1814251881Speter return TRUE; 1815251881Speter 1816251881Speter if (status->conflicted) 1817251881Speter return TRUE; 1818251881Speter 1819251881Speter /* If the item is ignored, and we don't want ignores, skip it. */ 1820251881Speter if ((status->node_status == svn_wc_status_ignored) && (! no_ignore)) 1821251881Speter return FALSE; 1822251881Speter 1823251881Speter /* If we want everything, we obviously want this single-item subset 1824251881Speter of everything. */ 1825251881Speter if (get_all) 1826251881Speter return TRUE; 1827251881Speter 1828251881Speter /* If the item is unversioned, display it. */ 1829251881Speter if (status->node_status == svn_wc_status_unversioned) 1830251881Speter return TRUE; 1831251881Speter 1832251881Speter /* If the text, property or tree state is interesting, send it. */ 1833251881Speter if ((status->node_status != svn_wc_status_none 1834251881Speter && (status->node_status != svn_wc_status_normal))) 1835251881Speter return TRUE; 1836251881Speter 1837251881Speter /* If it's switched, send it. */ 1838251881Speter if (status->switched) 1839251881Speter return TRUE; 1840251881Speter 1841251881Speter /* If there is a lock token, send it. */ 1842251881Speter if (status->versioned && status->lock) 1843251881Speter return TRUE; 1844251881Speter 1845251881Speter /* If the entry is associated with a changelist, send it. */ 1846251881Speter if (status->changelist) 1847251881Speter return TRUE; 1848251881Speter 1849251881Speter /* Otherwise, don't send it. */ 1850251881Speter return FALSE; 1851251881Speter} 1852251881Speter 1853251881Speter 1854251881Speter/* Baton for mark_status. */ 1855251881Speterstruct status_baton 1856251881Speter{ 1857251881Speter svn_wc_status_func4_t real_status_func; /* real status function */ 1858251881Speter void *real_status_baton; /* real status baton */ 1859251881Speter}; 1860251881Speter 1861251881Speter/* A status callback function which wraps the *real* status 1862251881Speter function/baton. It simply sets the "repos_node_status" field of the 1863251881Speter STATUS to svn_wc_status_deleted and passes it off to the real 1864251881Speter status func/baton. Implements svn_wc_status_func4_t */ 1865251881Speterstatic svn_error_t * 1866251881Spetermark_deleted(void *baton, 1867251881Speter const char *local_abspath, 1868251881Speter const svn_wc_status3_t *status, 1869251881Speter apr_pool_t *scratch_pool) 1870251881Speter{ 1871251881Speter struct status_baton *sb = baton; 1872251881Speter svn_wc_status3_t *new_status = svn_wc_dup_status3(status, scratch_pool); 1873251881Speter new_status->repos_node_status = svn_wc_status_deleted; 1874251881Speter return sb->real_status_func(sb->real_status_baton, local_abspath, 1875251881Speter new_status, scratch_pool); 1876251881Speter} 1877251881Speter 1878251881Speter 1879251881Speter/* Handle a directory's STATII hash. EB is the edit baton. DIR_PATH 1880251881Speter and DIR_ENTRY are the on-disk path and entry, respectively, for the 1881251881Speter directory itself. Descend into subdirectories according to DEPTH. 1882251881Speter Also, if DIR_WAS_DELETED is set, each status that is reported 1883251881Speter through this function will have its repos_text_status field showing 1884251881Speter a deletion. Use POOL for all allocations. */ 1885251881Speterstatic svn_error_t * 1886251881Speterhandle_statii(struct edit_baton *eb, 1887251881Speter const char *dir_repos_root_url, 1888251881Speter const char *dir_repos_relpath, 1889251881Speter const char *dir_repos_uuid, 1890251881Speter apr_hash_t *statii, 1891251881Speter svn_boolean_t dir_was_deleted, 1892251881Speter svn_depth_t depth, 1893251881Speter apr_pool_t *pool) 1894251881Speter{ 1895251881Speter const apr_array_header_t *ignores = eb->ignores; 1896251881Speter apr_hash_index_t *hi; 1897251881Speter apr_pool_t *iterpool = svn_pool_create(pool); 1898251881Speter svn_wc_status_func4_t status_func = eb->status_func; 1899251881Speter void *status_baton = eb->status_baton; 1900251881Speter struct status_baton sb; 1901251881Speter 1902251881Speter if (dir_was_deleted) 1903251881Speter { 1904251881Speter sb.real_status_func = eb->status_func; 1905251881Speter sb.real_status_baton = eb->status_baton; 1906251881Speter status_func = mark_deleted; 1907251881Speter status_baton = &sb; 1908251881Speter } 1909251881Speter 1910251881Speter /* Loop over all the statii still in our hash, handling each one. */ 1911251881Speter for (hi = apr_hash_first(pool, statii); hi; hi = apr_hash_next(hi)) 1912251881Speter { 1913251881Speter const char *local_abspath = svn__apr_hash_index_key(hi); 1914251881Speter svn_wc_status3_t *status = svn__apr_hash_index_val(hi); 1915251881Speter 1916251881Speter /* Clear the subpool. */ 1917251881Speter svn_pool_clear(iterpool); 1918251881Speter 1919251881Speter /* Now, handle the status. We don't recurse for svn_depth_immediates 1920251881Speter because we already have the subdirectories' statii. */ 1921251881Speter if (status->versioned && status->kind == svn_node_dir 1922251881Speter && (depth == svn_depth_unknown 1923251881Speter || depth == svn_depth_infinity)) 1924251881Speter { 1925251881Speter SVN_ERR(get_dir_status(&eb->wb, 1926251881Speter local_abspath, TRUE, 1927251881Speter dir_repos_root_url, dir_repos_relpath, 1928251881Speter dir_repos_uuid, 1929251881Speter NULL, 1930251881Speter NULL /* dirent */, 1931251881Speter ignores, depth, eb->get_all, eb->no_ignore, 1932251881Speter status_func, status_baton, 1933251881Speter eb->cancel_func, eb->cancel_baton, 1934251881Speter iterpool)); 1935251881Speter } 1936251881Speter if (dir_was_deleted) 1937251881Speter status->repos_node_status = svn_wc_status_deleted; 1938251881Speter if (is_sendable_status(status, eb->no_ignore, eb->get_all)) 1939251881Speter SVN_ERR((eb->status_func)(eb->status_baton, local_abspath, status, 1940251881Speter iterpool)); 1941251881Speter } 1942251881Speter 1943251881Speter /* Destroy the subpool. */ 1944251881Speter svn_pool_destroy(iterpool); 1945251881Speter 1946251881Speter return SVN_NO_ERROR; 1947251881Speter} 1948251881Speter 1949251881Speter 1950251881Speter/*----------------------------------------------------------------------*/ 1951251881Speter 1952251881Speter/*** The callbacks we'll plug into an svn_delta_editor_t structure. ***/ 1953251881Speter 1954251881Speter/* An svn_delta_editor_t function. */ 1955251881Speterstatic svn_error_t * 1956251881Speterset_target_revision(void *edit_baton, 1957251881Speter svn_revnum_t target_revision, 1958251881Speter apr_pool_t *pool) 1959251881Speter{ 1960251881Speter struct edit_baton *eb = edit_baton; 1961251881Speter *(eb->target_revision) = target_revision; 1962251881Speter return SVN_NO_ERROR; 1963251881Speter} 1964251881Speter 1965251881Speter 1966251881Speter/* An svn_delta_editor_t function. */ 1967251881Speterstatic svn_error_t * 1968251881Speteropen_root(void *edit_baton, 1969251881Speter svn_revnum_t base_revision, 1970251881Speter apr_pool_t *pool, 1971251881Speter void **dir_baton) 1972251881Speter{ 1973251881Speter struct edit_baton *eb = edit_baton; 1974251881Speter eb->root_opened = TRUE; 1975251881Speter return make_dir_baton(dir_baton, NULL, eb, NULL, pool); 1976251881Speter} 1977251881Speter 1978251881Speter 1979251881Speter/* An svn_delta_editor_t function. */ 1980251881Speterstatic svn_error_t * 1981251881Speterdelete_entry(const char *path, 1982251881Speter svn_revnum_t revision, 1983251881Speter void *parent_baton, 1984251881Speter apr_pool_t *pool) 1985251881Speter{ 1986251881Speter struct dir_baton *db = parent_baton; 1987251881Speter struct edit_baton *eb = db->edit_baton; 1988251881Speter const char *local_abspath = svn_dirent_join(eb->anchor_abspath, path, pool); 1989251881Speter 1990251881Speter /* Note: when something is deleted, it's okay to tweak the 1991251881Speter statushash immediately. No need to wait until close_file or 1992251881Speter close_dir, because there's no risk of having to honor the 'added' 1993251881Speter flag. We already know this item exists in the working copy. */ 1994251881Speter SVN_ERR(tweak_statushash(db, db, TRUE, eb->db, 1995251881Speter local_abspath, 1996251881Speter svn_wc_status_deleted, 0, 0, revision, NULL, pool)); 1997251881Speter 1998251881Speter /* Mark the parent dir -- it lost an entry (unless that parent dir 1999251881Speter is the root node and we're not supposed to report on the root 2000251881Speter node). */ 2001251881Speter if (db->parent_baton && (! *eb->target_basename)) 2002251881Speter SVN_ERR(tweak_statushash(db->parent_baton, db, TRUE,eb->db, 2003251881Speter db->local_abspath, 2004251881Speter svn_wc_status_modified, svn_wc_status_modified, 2005251881Speter 0, SVN_INVALID_REVNUM, NULL, pool)); 2006251881Speter 2007251881Speter return SVN_NO_ERROR; 2008251881Speter} 2009251881Speter 2010251881Speter 2011251881Speter/* An svn_delta_editor_t function. */ 2012251881Speterstatic svn_error_t * 2013251881Speteradd_directory(const char *path, 2014251881Speter void *parent_baton, 2015251881Speter const char *copyfrom_path, 2016251881Speter svn_revnum_t copyfrom_revision, 2017251881Speter apr_pool_t *pool, 2018251881Speter void **child_baton) 2019251881Speter{ 2020251881Speter struct dir_baton *pb = parent_baton; 2021251881Speter struct edit_baton *eb = pb->edit_baton; 2022251881Speter struct dir_baton *new_db; 2023251881Speter 2024251881Speter SVN_ERR(make_dir_baton(child_baton, path, eb, pb, pool)); 2025251881Speter 2026251881Speter /* Make this dir as added. */ 2027251881Speter new_db = *child_baton; 2028251881Speter new_db->added = TRUE; 2029251881Speter 2030251881Speter /* Mark the parent as changed; it gained an entry. */ 2031251881Speter pb->text_changed = TRUE; 2032251881Speter 2033251881Speter return SVN_NO_ERROR; 2034251881Speter} 2035251881Speter 2036251881Speter 2037251881Speter/* An svn_delta_editor_t function. */ 2038251881Speterstatic svn_error_t * 2039251881Speteropen_directory(const char *path, 2040251881Speter void *parent_baton, 2041251881Speter svn_revnum_t base_revision, 2042251881Speter apr_pool_t *pool, 2043251881Speter void **child_baton) 2044251881Speter{ 2045251881Speter struct dir_baton *pb = parent_baton; 2046251881Speter return make_dir_baton(child_baton, path, pb->edit_baton, pb, pool); 2047251881Speter} 2048251881Speter 2049251881Speter 2050251881Speter/* An svn_delta_editor_t function. */ 2051251881Speterstatic svn_error_t * 2052251881Speterchange_dir_prop(void *dir_baton, 2053251881Speter const char *name, 2054251881Speter const svn_string_t *value, 2055251881Speter apr_pool_t *pool) 2056251881Speter{ 2057251881Speter struct dir_baton *db = dir_baton; 2058251881Speter if (svn_wc_is_normal_prop(name)) 2059251881Speter db->prop_changed = TRUE; 2060251881Speter 2061251881Speter /* Note any changes to the repository. */ 2062251881Speter if (value != NULL) 2063251881Speter { 2064251881Speter if (strcmp(name, SVN_PROP_ENTRY_COMMITTED_REV) == 0) 2065251881Speter db->ood_changed_rev = SVN_STR_TO_REV(value->data); 2066251881Speter else if (strcmp(name, SVN_PROP_ENTRY_LAST_AUTHOR) == 0) 2067251881Speter db->ood_changed_author = apr_pstrdup(db->pool, value->data); 2068251881Speter else if (strcmp(name, SVN_PROP_ENTRY_COMMITTED_DATE) == 0) 2069251881Speter { 2070251881Speter apr_time_t tm; 2071251881Speter SVN_ERR(svn_time_from_cstring(&tm, value->data, db->pool)); 2072251881Speter db->ood_changed_date = tm; 2073251881Speter } 2074251881Speter } 2075251881Speter 2076251881Speter return SVN_NO_ERROR; 2077251881Speter} 2078251881Speter 2079251881Speter 2080251881Speter 2081251881Speter/* An svn_delta_editor_t function. */ 2082251881Speterstatic svn_error_t * 2083251881Speterclose_directory(void *dir_baton, 2084251881Speter apr_pool_t *pool) 2085251881Speter{ 2086251881Speter struct dir_baton *db = dir_baton; 2087251881Speter struct dir_baton *pb = db->parent_baton; 2088251881Speter struct edit_baton *eb = db->edit_baton; 2089251881Speter apr_pool_t *scratch_pool = db->pool; 2090251881Speter 2091251881Speter /* If nothing has changed and directory has no out of 2092251881Speter date descendants, return. */ 2093251881Speter if (db->added || db->prop_changed || db->text_changed 2094251881Speter || db->ood_changed_rev != SVN_INVALID_REVNUM) 2095251881Speter { 2096251881Speter enum svn_wc_status_kind repos_node_status; 2097251881Speter enum svn_wc_status_kind repos_text_status; 2098251881Speter enum svn_wc_status_kind repos_prop_status; 2099251881Speter 2100251881Speter /* If this is a new directory, add it to the statushash. */ 2101251881Speter if (db->added) 2102251881Speter { 2103251881Speter repos_node_status = svn_wc_status_added; 2104251881Speter repos_text_status = svn_wc_status_none; 2105251881Speter repos_prop_status = db->prop_changed ? svn_wc_status_added 2106251881Speter : svn_wc_status_none; 2107251881Speter } 2108251881Speter else 2109251881Speter { 2110251881Speter repos_node_status = (db->text_changed || db->prop_changed) 2111251881Speter ? svn_wc_status_modified 2112251881Speter : svn_wc_status_none; 2113251881Speter repos_text_status = db->text_changed ? svn_wc_status_modified 2114251881Speter : svn_wc_status_none; 2115251881Speter repos_prop_status = db->prop_changed ? svn_wc_status_modified 2116251881Speter : svn_wc_status_none; 2117251881Speter } 2118251881Speter 2119251881Speter /* Maybe add this directory to its parent's status hash. Note 2120251881Speter that tweak_statushash won't do anything if repos_text_status 2121251881Speter is not svn_wc_status_added. */ 2122251881Speter if (pb) 2123251881Speter { 2124251881Speter /* ### When we add directory locking, we need to find a 2125251881Speter ### directory lock here. */ 2126251881Speter SVN_ERR(tweak_statushash(pb, db, TRUE, eb->db, db->local_abspath, 2127251881Speter repos_node_status, repos_text_status, 2128251881Speter repos_prop_status, SVN_INVALID_REVNUM, NULL, 2129251881Speter scratch_pool)); 2130251881Speter } 2131251881Speter else 2132251881Speter { 2133251881Speter /* We're editing the root dir of the WC. As its repos 2134251881Speter status info isn't otherwise set, set it directly to 2135251881Speter trigger invocation of the status callback below. */ 2136251881Speter eb->anchor_status->repos_node_status = repos_node_status; 2137251881Speter eb->anchor_status->repos_prop_status = repos_prop_status; 2138251881Speter eb->anchor_status->repos_text_status = repos_text_status; 2139251881Speter 2140251881Speter /* If the root dir is out of date set the ood info directly too. */ 2141251881Speter if (db->ood_changed_rev != eb->anchor_status->revision) 2142251881Speter { 2143251881Speter eb->anchor_status->ood_changed_rev = db->ood_changed_rev; 2144251881Speter eb->anchor_status->ood_changed_date = db->ood_changed_date; 2145251881Speter eb->anchor_status->ood_kind = db->ood_kind; 2146251881Speter eb->anchor_status->ood_changed_author = 2147251881Speter apr_pstrdup(pool, db->ood_changed_author); 2148251881Speter } 2149251881Speter } 2150251881Speter } 2151251881Speter 2152251881Speter /* Handle this directory's statuses, and then note in the parent 2153251881Speter that this has been done. */ 2154251881Speter if (pb && ! db->excluded) 2155251881Speter { 2156251881Speter svn_boolean_t was_deleted = FALSE; 2157251881Speter const svn_wc_status3_t *dir_status; 2158251881Speter 2159251881Speter /* See if the directory was deleted or replaced. */ 2160251881Speter dir_status = svn_hash_gets(pb->statii, db->local_abspath); 2161251881Speter if (dir_status && 2162251881Speter ((dir_status->repos_node_status == svn_wc_status_deleted) 2163251881Speter || (dir_status->repos_node_status == svn_wc_status_replaced))) 2164251881Speter was_deleted = TRUE; 2165251881Speter 2166251881Speter /* Now do the status reporting. */ 2167251881Speter SVN_ERR(handle_statii(eb, 2168251881Speter dir_status ? dir_status->repos_root_url : NULL, 2169251881Speter dir_status ? dir_status->repos_relpath : NULL, 2170251881Speter dir_status ? dir_status->repos_uuid : NULL, 2171251881Speter db->statii, was_deleted, db->depth, scratch_pool)); 2172251881Speter if (dir_status && is_sendable_status(dir_status, eb->no_ignore, 2173251881Speter eb->get_all)) 2174251881Speter SVN_ERR((eb->status_func)(eb->status_baton, db->local_abspath, 2175251881Speter dir_status, scratch_pool)); 2176251881Speter svn_hash_sets(pb->statii, db->local_abspath, NULL); 2177251881Speter } 2178251881Speter else if (! pb) 2179251881Speter { 2180251881Speter /* If this is the top-most directory, and the operation had a 2181251881Speter target, we should only report the target. */ 2182251881Speter if (*eb->target_basename) 2183251881Speter { 2184251881Speter const svn_wc_status3_t *tgt_status; 2185251881Speter 2186251881Speter tgt_status = svn_hash_gets(db->statii, eb->target_abspath); 2187251881Speter if (tgt_status) 2188251881Speter { 2189251881Speter if (tgt_status->versioned 2190251881Speter && tgt_status->kind == svn_node_dir) 2191251881Speter { 2192251881Speter SVN_ERR(get_dir_status(&eb->wb, 2193251881Speter eb->target_abspath, TRUE, 2194251881Speter NULL, NULL, NULL, NULL, 2195251881Speter NULL /* dirent */, 2196251881Speter eb->ignores, 2197251881Speter eb->default_depth, 2198251881Speter eb->get_all, eb->no_ignore, 2199251881Speter eb->status_func, eb->status_baton, 2200251881Speter eb->cancel_func, eb->cancel_baton, 2201251881Speter scratch_pool)); 2202251881Speter } 2203251881Speter if (is_sendable_status(tgt_status, eb->no_ignore, eb->get_all)) 2204251881Speter SVN_ERR((eb->status_func)(eb->status_baton, eb->target_abspath, 2205251881Speter tgt_status, scratch_pool)); 2206251881Speter } 2207251881Speter } 2208251881Speter else 2209251881Speter { 2210251881Speter /* Otherwise, we report on all our children and ourself. 2211251881Speter Note that our directory couldn't have been deleted, 2212251881Speter because it is the root of the edit drive. */ 2213251881Speter SVN_ERR(handle_statii(eb, 2214251881Speter eb->anchor_status->repos_root_url, 2215251881Speter eb->anchor_status->repos_relpath, 2216251881Speter eb->anchor_status->repos_uuid, 2217251881Speter db->statii, FALSE, eb->default_depth, 2218251881Speter scratch_pool)); 2219251881Speter if (is_sendable_status(eb->anchor_status, eb->no_ignore, 2220251881Speter eb->get_all)) 2221251881Speter SVN_ERR((eb->status_func)(eb->status_baton, db->local_abspath, 2222251881Speter eb->anchor_status, scratch_pool)); 2223251881Speter eb->anchor_status = NULL; 2224251881Speter } 2225251881Speter } 2226251881Speter 2227251881Speter svn_pool_clear(scratch_pool); /* Clear baton and its pool */ 2228251881Speter 2229251881Speter return SVN_NO_ERROR; 2230251881Speter} 2231251881Speter 2232251881Speter 2233251881Speter 2234251881Speter/* An svn_delta_editor_t function. */ 2235251881Speterstatic svn_error_t * 2236251881Speteradd_file(const char *path, 2237251881Speter void *parent_baton, 2238251881Speter const char *copyfrom_path, 2239251881Speter svn_revnum_t copyfrom_revision, 2240251881Speter apr_pool_t *pool, 2241251881Speter void **file_baton) 2242251881Speter{ 2243251881Speter struct dir_baton *pb = parent_baton; 2244251881Speter struct file_baton *new_fb = make_file_baton(pb, path, pool); 2245251881Speter 2246251881Speter /* Mark parent dir as changed */ 2247251881Speter pb->text_changed = TRUE; 2248251881Speter 2249251881Speter /* Make this file as added. */ 2250251881Speter new_fb->added = TRUE; 2251251881Speter 2252251881Speter *file_baton = new_fb; 2253251881Speter return SVN_NO_ERROR; 2254251881Speter} 2255251881Speter 2256251881Speter 2257251881Speter/* An svn_delta_editor_t function. */ 2258251881Speterstatic svn_error_t * 2259251881Speteropen_file(const char *path, 2260251881Speter void *parent_baton, 2261251881Speter svn_revnum_t base_revision, 2262251881Speter apr_pool_t *pool, 2263251881Speter void **file_baton) 2264251881Speter{ 2265251881Speter struct dir_baton *pb = parent_baton; 2266251881Speter struct file_baton *new_fb = make_file_baton(pb, path, pool); 2267251881Speter 2268251881Speter *file_baton = new_fb; 2269251881Speter return SVN_NO_ERROR; 2270251881Speter} 2271251881Speter 2272251881Speter 2273251881Speter/* An svn_delta_editor_t function. */ 2274251881Speterstatic svn_error_t * 2275251881Speterapply_textdelta(void *file_baton, 2276251881Speter const char *base_checksum, 2277251881Speter apr_pool_t *pool, 2278251881Speter svn_txdelta_window_handler_t *handler, 2279251881Speter void **handler_baton) 2280251881Speter{ 2281251881Speter struct file_baton *fb = file_baton; 2282251881Speter 2283251881Speter /* Mark file as having textual mods. */ 2284251881Speter fb->text_changed = TRUE; 2285251881Speter 2286251881Speter /* Send back a NULL window handler -- we don't need the actual diffs. */ 2287251881Speter *handler_baton = NULL; 2288251881Speter *handler = svn_delta_noop_window_handler; 2289251881Speter 2290251881Speter return SVN_NO_ERROR; 2291251881Speter} 2292251881Speter 2293251881Speter 2294251881Speter/* An svn_delta_editor_t function. */ 2295251881Speterstatic svn_error_t * 2296251881Speterchange_file_prop(void *file_baton, 2297251881Speter const char *name, 2298251881Speter const svn_string_t *value, 2299251881Speter apr_pool_t *pool) 2300251881Speter{ 2301251881Speter struct file_baton *fb = file_baton; 2302251881Speter if (svn_wc_is_normal_prop(name)) 2303251881Speter fb->prop_changed = TRUE; 2304251881Speter 2305251881Speter /* Note any changes to the repository. */ 2306251881Speter if (value != NULL) 2307251881Speter { 2308251881Speter if (strcmp(name, SVN_PROP_ENTRY_COMMITTED_REV) == 0) 2309251881Speter fb->ood_changed_rev = SVN_STR_TO_REV(value->data); 2310251881Speter else if (strcmp(name, SVN_PROP_ENTRY_LAST_AUTHOR) == 0) 2311251881Speter fb->ood_changed_author = apr_pstrdup(fb->dir_baton->pool, 2312251881Speter value->data); 2313251881Speter else if (strcmp(name, SVN_PROP_ENTRY_COMMITTED_DATE) == 0) 2314251881Speter { 2315251881Speter apr_time_t tm; 2316251881Speter SVN_ERR(svn_time_from_cstring(&tm, value->data, 2317251881Speter fb->dir_baton->pool)); 2318251881Speter fb->ood_changed_date = tm; 2319251881Speter } 2320251881Speter } 2321251881Speter 2322251881Speter return SVN_NO_ERROR; 2323251881Speter} 2324251881Speter 2325251881Speter 2326251881Speter/* An svn_delta_editor_t function. */ 2327251881Speterstatic svn_error_t * 2328251881Speterclose_file(void *file_baton, 2329251881Speter const char *text_checksum, /* ignored, as we receive no data */ 2330251881Speter apr_pool_t *pool) 2331251881Speter{ 2332251881Speter struct file_baton *fb = file_baton; 2333251881Speter enum svn_wc_status_kind repos_node_status; 2334251881Speter enum svn_wc_status_kind repos_text_status; 2335251881Speter enum svn_wc_status_kind repos_prop_status; 2336251881Speter const svn_lock_t *repos_lock = NULL; 2337251881Speter 2338251881Speter /* If nothing has changed, return. */ 2339251881Speter if (! (fb->added || fb->prop_changed || fb->text_changed)) 2340251881Speter return SVN_NO_ERROR; 2341251881Speter 2342251881Speter /* If this is a new file, add it to the statushash. */ 2343251881Speter if (fb->added) 2344251881Speter { 2345251881Speter repos_node_status = svn_wc_status_added; 2346251881Speter repos_text_status = fb->text_changed ? svn_wc_status_modified 2347251881Speter : 0 /* don't tweak */; 2348251881Speter repos_prop_status = fb->prop_changed ? svn_wc_status_modified 2349251881Speter : 0 /* don't tweak */; 2350251881Speter 2351251881Speter if (fb->edit_baton->wb.repos_locks) 2352251881Speter { 2353251881Speter const char *dir_repos_relpath = find_dir_repos_relpath(fb->dir_baton, 2354251881Speter pool); 2355251881Speter 2356251881Speter /* repos_lock still uses the deprecated filesystem absolute path 2357251881Speter format */ 2358251881Speter const char *repos_relpath = svn_relpath_join(dir_repos_relpath, 2359251881Speter fb->name, pool); 2360251881Speter 2361251881Speter repos_lock = svn_hash_gets(fb->edit_baton->wb.repos_locks, 2362251881Speter svn_fspath__join("/", repos_relpath, 2363251881Speter pool)); 2364251881Speter } 2365251881Speter } 2366251881Speter else 2367251881Speter { 2368251881Speter repos_node_status = (fb->text_changed || fb->prop_changed) 2369251881Speter ? svn_wc_status_modified 2370251881Speter : 0 /* don't tweak */; 2371251881Speter repos_text_status = fb->text_changed ? svn_wc_status_modified 2372251881Speter : 0 /* don't tweak */; 2373251881Speter repos_prop_status = fb->prop_changed ? svn_wc_status_modified 2374251881Speter : 0 /* don't tweak */; 2375251881Speter } 2376251881Speter 2377251881Speter return tweak_statushash(fb, NULL, FALSE, fb->edit_baton->db, 2378251881Speter fb->local_abspath, repos_node_status, 2379251881Speter repos_text_status, repos_prop_status, 2380251881Speter SVN_INVALID_REVNUM, repos_lock, pool); 2381251881Speter} 2382251881Speter 2383251881Speter/* An svn_delta_editor_t function. */ 2384251881Speterstatic svn_error_t * 2385251881Speterclose_edit(void *edit_baton, 2386251881Speter apr_pool_t *pool) 2387251881Speter{ 2388251881Speter struct edit_baton *eb = edit_baton; 2389251881Speter 2390251881Speter /* If we get here and the root was not opened as part of the edit, 2391251881Speter we need to transmit statuses for everything. Otherwise, we 2392251881Speter should be done. */ 2393251881Speter if (eb->root_opened) 2394251881Speter return SVN_NO_ERROR; 2395251881Speter 2396251881Speter SVN_ERR(svn_wc_walk_status(eb->wc_ctx, 2397251881Speter eb->target_abspath, 2398251881Speter eb->default_depth, 2399251881Speter eb->get_all, 2400251881Speter eb->no_ignore, 2401251881Speter FALSE, 2402251881Speter eb->ignores, 2403251881Speter eb->status_func, 2404251881Speter eb->status_baton, 2405251881Speter eb->cancel_func, 2406251881Speter eb->cancel_baton, 2407251881Speter pool)); 2408251881Speter 2409251881Speter return SVN_NO_ERROR; 2410251881Speter} 2411251881Speter 2412251881Speter 2413251881Speter 2414251881Speter/*** Public API ***/ 2415251881Speter 2416251881Spetersvn_error_t * 2417251881Spetersvn_wc__get_status_editor(const svn_delta_editor_t **editor, 2418251881Speter void **edit_baton, 2419251881Speter void **set_locks_baton, 2420251881Speter svn_revnum_t *edit_revision, 2421251881Speter svn_wc_context_t *wc_ctx, 2422251881Speter const char *anchor_abspath, 2423251881Speter const char *target_basename, 2424251881Speter svn_depth_t depth, 2425251881Speter svn_boolean_t get_all, 2426251881Speter svn_boolean_t no_ignore, 2427251881Speter svn_boolean_t depth_as_sticky, 2428251881Speter svn_boolean_t server_performs_filtering, 2429251881Speter const apr_array_header_t *ignore_patterns, 2430251881Speter svn_wc_status_func4_t status_func, 2431251881Speter void *status_baton, 2432251881Speter svn_cancel_func_t cancel_func, 2433251881Speter void *cancel_baton, 2434251881Speter apr_pool_t *result_pool, 2435251881Speter apr_pool_t *scratch_pool) 2436251881Speter{ 2437251881Speter struct edit_baton *eb; 2438251881Speter svn_delta_editor_t *tree_editor = svn_delta_default_editor(result_pool); 2439251881Speter void *inner_baton; 2440251881Speter struct svn_wc__shim_fetch_baton_t *sfb; 2441251881Speter const svn_delta_editor_t *inner_editor; 2442251881Speter svn_delta_shim_callbacks_t *shim_callbacks = 2443251881Speter svn_delta_shim_callbacks_default(result_pool); 2444251881Speter 2445251881Speter /* Construct an edit baton. */ 2446251881Speter eb = apr_pcalloc(result_pool, sizeof(*eb)); 2447251881Speter eb->default_depth = depth; 2448251881Speter eb->target_revision = edit_revision; 2449251881Speter eb->db = wc_ctx->db; 2450251881Speter eb->wc_ctx = wc_ctx; 2451251881Speter eb->get_all = get_all; 2452251881Speter eb->no_ignore = no_ignore; 2453251881Speter eb->status_func = status_func; 2454251881Speter eb->status_baton = status_baton; 2455251881Speter eb->cancel_func = cancel_func; 2456251881Speter eb->cancel_baton = cancel_baton; 2457251881Speter eb->anchor_abspath = apr_pstrdup(result_pool, anchor_abspath); 2458251881Speter eb->target_abspath = svn_dirent_join(anchor_abspath, target_basename, 2459251881Speter result_pool); 2460251881Speter 2461251881Speter eb->target_basename = apr_pstrdup(result_pool, target_basename); 2462251881Speter eb->root_opened = FALSE; 2463251881Speter 2464251881Speter eb->wb.db = wc_ctx->db; 2465251881Speter eb->wb.target_abspath = eb->target_abspath; 2466251881Speter eb->wb.ignore_text_mods = FALSE; 2467251881Speter eb->wb.repos_locks = NULL; 2468251881Speter eb->wb.repos_root = NULL; 2469251881Speter 2470251881Speter SVN_ERR(svn_wc__db_externals_defined_below(&eb->wb.externals, 2471251881Speter wc_ctx->db, eb->target_abspath, 2472251881Speter result_pool, scratch_pool)); 2473251881Speter 2474251881Speter /* Use the caller-provided ignore patterns if provided; the build-time 2475251881Speter configured defaults otherwise. */ 2476251881Speter if (ignore_patterns) 2477251881Speter { 2478251881Speter eb->ignores = ignore_patterns; 2479251881Speter } 2480251881Speter else 2481251881Speter { 2482251881Speter apr_array_header_t *ignores; 2483251881Speter 2484251881Speter SVN_ERR(svn_wc_get_default_ignores(&ignores, NULL, result_pool)); 2485251881Speter eb->ignores = ignores; 2486251881Speter } 2487251881Speter 2488251881Speter /* The edit baton's status structure maps to PATH, and the editor 2489251881Speter have to be aware of whether that is the anchor or the target. */ 2490251881Speter SVN_ERR(internal_status(&(eb->anchor_status), wc_ctx->db, anchor_abspath, 2491251881Speter result_pool, scratch_pool)); 2492251881Speter 2493251881Speter /* Construct an editor. */ 2494251881Speter tree_editor->set_target_revision = set_target_revision; 2495251881Speter tree_editor->open_root = open_root; 2496251881Speter tree_editor->delete_entry = delete_entry; 2497251881Speter tree_editor->add_directory = add_directory; 2498251881Speter tree_editor->open_directory = open_directory; 2499251881Speter tree_editor->change_dir_prop = change_dir_prop; 2500251881Speter tree_editor->close_directory = close_directory; 2501251881Speter tree_editor->add_file = add_file; 2502251881Speter tree_editor->open_file = open_file; 2503251881Speter tree_editor->apply_textdelta = apply_textdelta; 2504251881Speter tree_editor->change_file_prop = change_file_prop; 2505251881Speter tree_editor->close_file = close_file; 2506251881Speter tree_editor->close_edit = close_edit; 2507251881Speter 2508251881Speter inner_editor = tree_editor; 2509251881Speter inner_baton = eb; 2510251881Speter 2511251881Speter if (!server_performs_filtering 2512251881Speter && !depth_as_sticky) 2513251881Speter SVN_ERR(svn_wc__ambient_depth_filter_editor(&inner_editor, 2514251881Speter &inner_baton, 2515251881Speter wc_ctx->db, 2516251881Speter anchor_abspath, 2517251881Speter target_basename, 2518251881Speter inner_editor, 2519251881Speter inner_baton, 2520251881Speter result_pool)); 2521251881Speter 2522251881Speter /* Conjoin a cancellation editor with our status editor. */ 2523251881Speter SVN_ERR(svn_delta_get_cancellation_editor(cancel_func, cancel_baton, 2524251881Speter inner_editor, inner_baton, 2525251881Speter editor, edit_baton, 2526251881Speter result_pool)); 2527251881Speter 2528251881Speter if (set_locks_baton) 2529251881Speter *set_locks_baton = eb; 2530251881Speter 2531251881Speter sfb = apr_palloc(result_pool, sizeof(*sfb)); 2532251881Speter sfb->db = wc_ctx->db; 2533251881Speter sfb->base_abspath = eb->anchor_abspath; 2534251881Speter sfb->fetch_base = FALSE; 2535251881Speter 2536251881Speter shim_callbacks->fetch_kind_func = svn_wc__fetch_kind_func; 2537251881Speter shim_callbacks->fetch_props_func = svn_wc__fetch_props_func; 2538251881Speter shim_callbacks->fetch_base_func = svn_wc__fetch_base_func; 2539251881Speter shim_callbacks->fetch_baton = sfb; 2540251881Speter 2541251881Speter SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton, 2542251881Speter NULL, NULL, shim_callbacks, 2543251881Speter result_pool, scratch_pool)); 2544251881Speter 2545251881Speter return SVN_NO_ERROR; 2546251881Speter} 2547251881Speter 2548251881Speter/* Like svn_io_stat_dirent, but works case sensitive inside working 2549251881Speter copies. Before 1.8 we handled this with a selection filter inside 2550251881Speter a directory */ 2551251881Speterstatic svn_error_t * 2552251881Speterstat_wc_dirent_case_sensitive(const svn_io_dirent2_t **dirent, 2553251881Speter svn_wc__db_t *db, 2554251881Speter const char *local_abspath, 2555251881Speter apr_pool_t *result_pool, 2556251881Speter apr_pool_t *scratch_pool) 2557251881Speter{ 2558251881Speter svn_boolean_t is_wcroot; 2559251881Speter 2560251881Speter /* The wcroot is "" inside the wc; handle it as not in the wc, as 2561251881Speter the case of the root is indifferent to us. */ 2562251881Speter 2563251881Speter /* Note that for performance this is really just a few hashtable lookups, 2564251881Speter as we just used local_abspath for a db call in both our callers */ 2565251881Speter SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, db, local_abspath, 2566251881Speter scratch_pool)); 2567251881Speter 2568251881Speter return svn_error_trace( 2569251881Speter svn_io_stat_dirent2(dirent, local_abspath, 2570251881Speter ! is_wcroot /* verify_truename */, 2571251881Speter TRUE /* ignore_enoent */, 2572251881Speter result_pool, scratch_pool)); 2573251881Speter} 2574251881Speter 2575251881Spetersvn_error_t * 2576251881Spetersvn_wc__internal_walk_status(svn_wc__db_t *db, 2577251881Speter const char *local_abspath, 2578251881Speter svn_depth_t depth, 2579251881Speter svn_boolean_t get_all, 2580251881Speter svn_boolean_t no_ignore, 2581251881Speter svn_boolean_t ignore_text_mods, 2582251881Speter const apr_array_header_t *ignore_patterns, 2583251881Speter svn_wc_status_func4_t status_func, 2584251881Speter void *status_baton, 2585251881Speter svn_cancel_func_t cancel_func, 2586251881Speter void *cancel_baton, 2587251881Speter apr_pool_t *scratch_pool) 2588251881Speter{ 2589251881Speter struct walk_status_baton wb; 2590251881Speter const svn_io_dirent2_t *dirent; 2591251881Speter const struct svn_wc__db_info_t *info; 2592251881Speter svn_error_t *err; 2593251881Speter 2594251881Speter wb.db = db; 2595251881Speter wb.target_abspath = local_abspath; 2596251881Speter wb.ignore_text_mods = ignore_text_mods; 2597251881Speter wb.repos_root = NULL; 2598251881Speter wb.repos_locks = NULL; 2599251881Speter 2600251881Speter /* Use the caller-provided ignore patterns if provided; the build-time 2601251881Speter configured defaults otherwise. */ 2602251881Speter if (!ignore_patterns) 2603251881Speter { 2604251881Speter apr_array_header_t *ignores; 2605251881Speter 2606251881Speter SVN_ERR(svn_wc_get_default_ignores(&ignores, NULL, scratch_pool)); 2607251881Speter ignore_patterns = ignores; 2608251881Speter } 2609251881Speter 2610269847Speter err = svn_wc__db_read_single_info(&info, db, local_abspath, 2611269847Speter scratch_pool, scratch_pool); 2612251881Speter 2613251881Speter if (err) 2614251881Speter { 2615251881Speter if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) 2616251881Speter { 2617251881Speter svn_error_clear(err); 2618251881Speter info = NULL; 2619251881Speter } 2620251881Speter else 2621251881Speter return svn_error_trace(err); 2622251881Speter 2623251881Speter wb.externals = apr_hash_make(scratch_pool); 2624251881Speter 2625251881Speter SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath, FALSE, TRUE, 2626251881Speter scratch_pool, scratch_pool)); 2627251881Speter } 2628251881Speter else 2629251881Speter { 2630251881Speter SVN_ERR(svn_wc__db_externals_defined_below(&wb.externals, 2631251881Speter db, local_abspath, 2632251881Speter scratch_pool, scratch_pool)); 2633251881Speter 2634251881Speter SVN_ERR(stat_wc_dirent_case_sensitive(&dirent, db, local_abspath, 2635251881Speter scratch_pool, scratch_pool)); 2636251881Speter } 2637251881Speter 2638251881Speter if (info 2639251881Speter && info->kind == svn_node_dir 2640251881Speter && info->status != svn_wc__db_status_not_present 2641251881Speter && info->status != svn_wc__db_status_excluded 2642251881Speter && info->status != svn_wc__db_status_server_excluded) 2643251881Speter { 2644251881Speter SVN_ERR(get_dir_status(&wb, 2645251881Speter local_abspath, 2646251881Speter FALSE /* skip_root */, 2647251881Speter NULL, NULL, NULL, 2648251881Speter info, 2649251881Speter dirent, 2650251881Speter ignore_patterns, 2651251881Speter depth, 2652251881Speter get_all, 2653251881Speter no_ignore, 2654251881Speter status_func, status_baton, 2655251881Speter cancel_func, cancel_baton, 2656251881Speter scratch_pool)); 2657251881Speter } 2658251881Speter else 2659251881Speter { 2660251881Speter /* It may be a file or an unversioned item. And this is an explicit 2661251881Speter * target, so no ignoring. An unversioned item (file or dir) shows a 2662251881Speter * status like '?', and can yield a tree conflicted path. */ 2663251881Speter err = get_child_status(&wb, 2664251881Speter local_abspath, 2665251881Speter info, 2666251881Speter dirent, 2667251881Speter ignore_patterns, 2668251881Speter get_all, 2669251881Speter status_func, status_baton, 2670251881Speter cancel_func, cancel_baton, 2671251881Speter scratch_pool); 2672251881Speter 2673251881Speter if (!info && err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND) 2674251881Speter { 2675251881Speter /* The parent is also not versioned, but it is not nice to show 2676251881Speter an error about a path a user didn't intend to touch. */ 2677251881Speter svn_error_clear(err); 2678251881Speter return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, 2679251881Speter _("The node '%s' was not found."), 2680251881Speter svn_dirent_local_style(local_abspath, 2681251881Speter scratch_pool)); 2682251881Speter } 2683251881Speter SVN_ERR(err); 2684251881Speter } 2685251881Speter 2686251881Speter return SVN_NO_ERROR; 2687251881Speter} 2688251881Speter 2689251881Spetersvn_error_t * 2690251881Spetersvn_wc_walk_status(svn_wc_context_t *wc_ctx, 2691251881Speter const char *local_abspath, 2692251881Speter svn_depth_t depth, 2693251881Speter svn_boolean_t get_all, 2694251881Speter svn_boolean_t no_ignore, 2695251881Speter svn_boolean_t ignore_text_mods, 2696251881Speter const apr_array_header_t *ignore_patterns, 2697251881Speter svn_wc_status_func4_t status_func, 2698251881Speter void *status_baton, 2699251881Speter svn_cancel_func_t cancel_func, 2700251881Speter void *cancel_baton, 2701251881Speter apr_pool_t *scratch_pool) 2702251881Speter{ 2703251881Speter return svn_error_trace( 2704251881Speter svn_wc__internal_walk_status(wc_ctx->db, 2705251881Speter local_abspath, 2706251881Speter depth, 2707251881Speter get_all, 2708251881Speter no_ignore, 2709251881Speter ignore_text_mods, 2710251881Speter ignore_patterns, 2711251881Speter status_func, 2712251881Speter status_baton, 2713251881Speter cancel_func, 2714251881Speter cancel_baton, 2715251881Speter scratch_pool)); 2716251881Speter} 2717251881Speter 2718251881Speter 2719251881Spetersvn_error_t * 2720251881Spetersvn_wc_status_set_repos_locks(void *edit_baton, 2721251881Speter apr_hash_t *locks, 2722251881Speter const char *repos_root, 2723251881Speter apr_pool_t *pool) 2724251881Speter{ 2725251881Speter struct edit_baton *eb = edit_baton; 2726251881Speter 2727251881Speter eb->wb.repos_locks = locks; 2728251881Speter eb->wb.repos_root = apr_pstrdup(pool, repos_root); 2729251881Speter 2730251881Speter return SVN_NO_ERROR; 2731251881Speter} 2732251881Speter 2733251881Speter 2734251881Spetersvn_error_t * 2735251881Spetersvn_wc_get_default_ignores(apr_array_header_t **patterns, 2736251881Speter apr_hash_t *config, 2737251881Speter apr_pool_t *pool) 2738251881Speter{ 2739251881Speter svn_config_t *cfg = config 2740251881Speter ? svn_hash_gets(config, SVN_CONFIG_CATEGORY_CONFIG) 2741251881Speter : NULL; 2742251881Speter const char *val; 2743251881Speter 2744251881Speter /* Check the Subversion run-time configuration for global ignores. 2745251881Speter If no configuration value exists, we fall back to our defaults. */ 2746251881Speter svn_config_get(cfg, &val, SVN_CONFIG_SECTION_MISCELLANY, 2747251881Speter SVN_CONFIG_OPTION_GLOBAL_IGNORES, 2748251881Speter SVN_CONFIG_DEFAULT_GLOBAL_IGNORES); 2749251881Speter *patterns = apr_array_make(pool, 16, sizeof(const char *)); 2750251881Speter 2751251881Speter /* Split the patterns on whitespace, and stuff them into *PATTERNS. */ 2752251881Speter svn_cstring_split_append(*patterns, val, "\n\r\t\v ", FALSE, pool); 2753251881Speter return SVN_NO_ERROR; 2754251881Speter} 2755251881Speter 2756251881Speter 2757251881Speter/* */ 2758251881Speterstatic svn_error_t * 2759251881Speterinternal_status(svn_wc_status3_t **status, 2760251881Speter svn_wc__db_t *db, 2761251881Speter const char *local_abspath, 2762251881Speter apr_pool_t *result_pool, 2763251881Speter apr_pool_t *scratch_pool) 2764251881Speter{ 2765251881Speter const svn_io_dirent2_t *dirent; 2766251881Speter svn_node_kind_t node_kind; 2767251881Speter const char *parent_repos_relpath; 2768251881Speter const char *parent_repos_root_url; 2769251881Speter const char *parent_repos_uuid; 2770251881Speter svn_wc__db_status_t node_status; 2771251881Speter svn_boolean_t conflicted; 2772251881Speter svn_boolean_t is_root = FALSE; 2773251881Speter svn_error_t *err; 2774251881Speter 2775251881Speter SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); 2776251881Speter 2777251881Speter err = svn_wc__db_read_info(&node_status, &node_kind, NULL, NULL, NULL, NULL, 2778251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 2779251881Speter NULL, NULL, NULL, NULL, NULL, NULL, &conflicted, 2780251881Speter NULL, NULL, NULL, NULL, NULL, NULL, 2781251881Speter db, local_abspath, 2782251881Speter scratch_pool, scratch_pool); 2783251881Speter 2784251881Speter if (err) 2785251881Speter { 2786251881Speter if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 2787251881Speter return svn_error_trace(err); 2788251881Speter 2789251881Speter svn_error_clear(err); 2790251881Speter node_kind = svn_node_unknown; 2791251881Speter /* Ensure conflicted is always set, but don't hide tree conflicts 2792251881Speter on 'hidden' nodes. */ 2793251881Speter conflicted = FALSE; 2794251881Speter 2795251881Speter SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath, FALSE, TRUE, 2796251881Speter scratch_pool, scratch_pool)); 2797251881Speter } 2798251881Speter else 2799251881Speter SVN_ERR(stat_wc_dirent_case_sensitive(&dirent, db, local_abspath, 2800251881Speter scratch_pool, scratch_pool)); 2801251881Speter 2802251881Speter if (node_kind != svn_node_unknown 2803251881Speter && (node_status == svn_wc__db_status_not_present 2804251881Speter || node_status == svn_wc__db_status_server_excluded 2805251881Speter || node_status == svn_wc__db_status_excluded)) 2806251881Speter { 2807251881Speter node_kind = svn_node_unknown; 2808251881Speter } 2809251881Speter 2810251881Speter if (node_kind == svn_node_unknown) 2811251881Speter return svn_error_trace(assemble_unversioned(status, 2812251881Speter db, local_abspath, 2813251881Speter dirent, conflicted, 2814251881Speter FALSE /* is_ignored */, 2815251881Speter result_pool, scratch_pool)); 2816251881Speter 2817251881Speter if (svn_dirent_is_root(local_abspath, strlen(local_abspath))) 2818251881Speter is_root = TRUE; 2819251881Speter else 2820251881Speter SVN_ERR(svn_wc__db_is_wcroot(&is_root, db, local_abspath, scratch_pool)); 2821251881Speter 2822251881Speter if (!is_root) 2823251881Speter { 2824251881Speter svn_wc__db_status_t parent_status; 2825251881Speter const char *parent_abspath = svn_dirent_dirname(local_abspath, 2826251881Speter scratch_pool); 2827251881Speter 2828251881Speter err = svn_wc__db_read_info(&parent_status, NULL, NULL, 2829251881Speter &parent_repos_relpath, &parent_repos_root_url, 2830251881Speter &parent_repos_uuid, NULL, NULL, NULL, 2831251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, 2832251881Speter NULL, NULL, NULL, NULL, NULL, NULL, NULL, 2833251881Speter NULL, NULL, NULL, NULL, 2834251881Speter db, parent_abspath, 2835251881Speter result_pool, scratch_pool); 2836251881Speter 2837251881Speter if (err && (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND 2838251881Speter || SVN_WC__ERR_IS_NOT_CURRENT_WC(err))) 2839251881Speter { 2840251881Speter svn_error_clear(err); 2841251881Speter parent_repos_root_url = NULL; 2842251881Speter parent_repos_relpath = NULL; 2843251881Speter parent_repos_uuid = NULL; 2844251881Speter } 2845251881Speter else SVN_ERR(err); 2846251881Speter } 2847251881Speter else 2848251881Speter { 2849251881Speter parent_repos_root_url = NULL; 2850251881Speter parent_repos_relpath = NULL; 2851251881Speter parent_repos_uuid = NULL; 2852251881Speter } 2853251881Speter 2854251881Speter return svn_error_trace(assemble_status(status, db, local_abspath, 2855251881Speter parent_repos_root_url, 2856251881Speter parent_repos_relpath, 2857251881Speter parent_repos_uuid, 2858251881Speter NULL, 2859251881Speter dirent, 2860251881Speter TRUE /* get_all */, 2861251881Speter FALSE, 2862251881Speter NULL /* repos_lock */, 2863251881Speter result_pool, scratch_pool)); 2864251881Speter} 2865251881Speter 2866251881Speter 2867251881Spetersvn_error_t * 2868251881Spetersvn_wc_status3(svn_wc_status3_t **status, 2869251881Speter svn_wc_context_t *wc_ctx, 2870251881Speter const char *local_abspath, 2871251881Speter apr_pool_t *result_pool, 2872251881Speter apr_pool_t *scratch_pool) 2873251881Speter{ 2874251881Speter return svn_error_trace( 2875251881Speter internal_status(status, wc_ctx->db, local_abspath, result_pool, 2876251881Speter scratch_pool)); 2877251881Speter} 2878251881Speter 2879251881Spetersvn_wc_status3_t * 2880251881Spetersvn_wc_dup_status3(const svn_wc_status3_t *orig_stat, 2881251881Speter apr_pool_t *pool) 2882251881Speter{ 2883251881Speter svn_wc_status3_t *new_stat = apr_palloc(pool, sizeof(*new_stat)); 2884251881Speter 2885251881Speter /* Shallow copy all members. */ 2886251881Speter *new_stat = *orig_stat; 2887251881Speter 2888251881Speter /* Now go back and dup the deep items into this pool. */ 2889251881Speter if (orig_stat->repos_lock) 2890251881Speter new_stat->repos_lock = svn_lock_dup(orig_stat->repos_lock, pool); 2891251881Speter 2892251881Speter if (orig_stat->changed_author) 2893251881Speter new_stat->changed_author = apr_pstrdup(pool, orig_stat->changed_author); 2894251881Speter 2895251881Speter if (orig_stat->ood_changed_author) 2896251881Speter new_stat->ood_changed_author 2897251881Speter = apr_pstrdup(pool, orig_stat->ood_changed_author); 2898251881Speter 2899251881Speter if (orig_stat->lock) 2900251881Speter new_stat->lock = svn_lock_dup(orig_stat->lock, pool); 2901251881Speter 2902251881Speter if (orig_stat->changelist) 2903251881Speter new_stat->changelist 2904251881Speter = apr_pstrdup(pool, orig_stat->changelist); 2905251881Speter 2906251881Speter if (orig_stat->repos_root_url) 2907251881Speter new_stat->repos_root_url 2908251881Speter = apr_pstrdup(pool, orig_stat->repos_root_url); 2909251881Speter 2910251881Speter if (orig_stat->repos_relpath) 2911251881Speter new_stat->repos_relpath 2912251881Speter = apr_pstrdup(pool, orig_stat->repos_relpath); 2913251881Speter 2914251881Speter if (orig_stat->repos_uuid) 2915251881Speter new_stat->repos_uuid 2916251881Speter = apr_pstrdup(pool, orig_stat->repos_uuid); 2917251881Speter 2918251881Speter if (orig_stat->moved_from_abspath) 2919251881Speter new_stat->moved_from_abspath 2920251881Speter = apr_pstrdup(pool, orig_stat->moved_from_abspath); 2921251881Speter 2922251881Speter if (orig_stat->moved_to_abspath) 2923251881Speter new_stat->moved_to_abspath 2924251881Speter = apr_pstrdup(pool, orig_stat->moved_to_abspath); 2925251881Speter 2926251881Speter /* Return the new hotness. */ 2927251881Speter return new_stat; 2928251881Speter} 2929251881Speter 2930251881Spetersvn_error_t * 2931251881Spetersvn_wc_get_ignores2(apr_array_header_t **patterns, 2932251881Speter svn_wc_context_t *wc_ctx, 2933251881Speter const char *local_abspath, 2934251881Speter apr_hash_t *config, 2935251881Speter apr_pool_t *result_pool, 2936251881Speter apr_pool_t *scratch_pool) 2937251881Speter{ 2938251881Speter apr_array_header_t *default_ignores; 2939251881Speter 2940251881Speter SVN_ERR(svn_wc_get_default_ignores(&default_ignores, config, scratch_pool)); 2941251881Speter return svn_error_trace(collect_ignore_patterns(patterns, wc_ctx->db, 2942251881Speter local_abspath, 2943251881Speter default_ignores, 2944251881Speter result_pool, scratch_pool)); 2945251881Speter} 2946