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