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