entries.c revision 289166
154359Sroberto/* 254359Sroberto * entries.c : manipulating the administrative `entries' file. 354359Sroberto * 454359Sroberto * ==================================================================== 554359Sroberto * Licensed to the Apache Software Foundation (ASF) under one 654359Sroberto * or more contributor license agreements. See the NOTICE file 754359Sroberto * distributed with this work for additional information 854359Sroberto * regarding copyright ownership. The ASF licenses this file 954359Sroberto * to you under the Apache License, Version 2.0 (the 1054359Sroberto * "License"); you may not use this file except in compliance 1154359Sroberto * with the License. You may obtain a copy of the License at 1254359Sroberto * 1354359Sroberto * http://www.apache.org/licenses/LICENSE-2.0 1454359Sroberto * 1554359Sroberto * Unless required by applicable law or agreed to in writing, 1654359Sroberto * software distributed under the License is distributed on an 1754359Sroberto * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 1854359Sroberto * KIND, either express or implied. See the License for the 1954359Sroberto * specific language governing permissions and limitations 2054359Sroberto * under the License. 2154359Sroberto * ==================================================================== 2254359Sroberto */ 2354359Sroberto 2454359Sroberto#include <string.h> 2554359Sroberto#include <assert.h> 2654359Sroberto 2754359Sroberto#include <apr_strings.h> 2854359Sroberto 2954359Sroberto#include "svn_error.h" 3054359Sroberto#include "svn_types.h" 3154359Sroberto#include "svn_time.h" 3254359Sroberto#include "svn_pools.h" 3354359Sroberto#include "svn_dirent_uri.h" 3454359Sroberto#include "svn_path.h" 3554359Sroberto#include "svn_ctype.h" 3654359Sroberto#include "svn_string.h" 3754359Sroberto#include "svn_hash.h" 3854359Sroberto 3954359Sroberto#include "wc.h" 4054359Sroberto#include "adm_files.h" 4154359Sroberto#include "conflicts.h" 4254359Sroberto#include "entries.h" 4354359Sroberto#include "lock.h" 4454359Sroberto#include "tree_conflicts.h" 4554359Sroberto#include "wc_db.h" 4654359Sroberto#include "wc-queries.h" /* for STMT_* */ 4754359Sroberto 4854359Sroberto#include "svn_private_config.h" 4954359Sroberto#include "private/svn_wc_private.h" 50285612Sdelphij#include "private/svn_sqlite.h" 51285612Sdelphij 52285612Sdelphij#define MAYBE_ALLOC(x,p) ((x) ? (x) : apr_pcalloc((p), sizeof(*(x)))) 5354359Sroberto 5454359Sroberto 5554359Sroberto/* Temporary structures which mirror the tables in wc-metadata.sql. 5654359Sroberto For detailed descriptions of each field, see that file. */ 5754359Srobertotypedef struct db_node_t { 5854359Sroberto apr_int64_t wc_id; 5954359Sroberto const char *local_relpath; 6054359Sroberto int op_depth; 6154359Sroberto apr_int64_t repos_id; 6254359Sroberto const char *repos_relpath; 6354359Sroberto const char *parent_relpath; 6454359Sroberto svn_wc__db_status_t presence; 6554359Sroberto svn_revnum_t revision; 6654359Sroberto svn_node_kind_t kind; 6754359Sroberto svn_checksum_t *checksum; 6854359Sroberto svn_filesize_t recorded_size; 6954359Sroberto svn_revnum_t changed_rev; 7054359Sroberto apr_time_t changed_date; 7154359Sroberto const char *changed_author; 7254359Sroberto svn_depth_t depth; 7354359Sroberto apr_time_t recorded_time; 7454359Sroberto apr_hash_t *properties; 7554359Sroberto svn_boolean_t file_external; 7654359Sroberto apr_array_header_t *inherited_props; 7754359Sroberto} db_node_t; 7854359Sroberto 7954359Srobertotypedef struct db_actual_node_t { 8054359Sroberto apr_int64_t wc_id; 8154359Sroberto const char *local_relpath; 8254359Sroberto const char *parent_relpath; 8354359Sroberto apr_hash_t *properties; 8454359Sroberto const char *conflict_old; 8554359Sroberto const char *conflict_new; 8654359Sroberto const char *conflict_working; 8754359Sroberto const char *prop_reject; 8854359Sroberto const char *changelist; 8954359Sroberto /* ### enum for text_mod */ 9054359Sroberto const char *tree_conflict_data; 9154359Sroberto} db_actual_node_t; 9254359Sroberto 9354359Sroberto 9454359Sroberto 9554359Sroberto/*** reading and writing the entries file ***/ 9654359Sroberto 9754359Sroberto 9854359Sroberto/* */ 9954359Srobertostatic svn_wc_entry_t * 10054359Srobertoalloc_entry(apr_pool_t *pool) 10154359Sroberto{ 10254359Sroberto svn_wc_entry_t *entry = apr_pcalloc(pool, sizeof(*entry)); 10354359Sroberto entry->revision = SVN_INVALID_REVNUM; 10454359Sroberto entry->copyfrom_rev = SVN_INVALID_REVNUM; 10554359Sroberto entry->cmt_rev = SVN_INVALID_REVNUM; 10654359Sroberto entry->kind = svn_node_none; 10754359Sroberto entry->working_size = SVN_WC_ENTRY_WORKING_SIZE_UNKNOWN; 10854359Sroberto entry->depth = svn_depth_infinity; 10954359Sroberto entry->file_external_peg_rev.kind = svn_opt_revision_unspecified; 11054359Sroberto entry->file_external_rev.kind = svn_opt_revision_unspecified; 11154359Sroberto return entry; 11254359Sroberto} 11354359Sroberto 11454359Sroberto 11554359Sroberto/* Is the entry in a 'hidden' state in the sense of the 'show_hidden' 11654359Sroberto * switches on svn_wc_entries_read(), svn_wc_walk_entries*(), etc.? */ 11754359Srobertosvn_error_t * 11854359Srobertosvn_wc__entry_is_hidden(svn_boolean_t *hidden, const svn_wc_entry_t *entry) 11954359Sroberto{ 120289997Sglebius /* In English, the condition is: "the entry is not present, and I haven't 12154359Sroberto scheduled something over the top of it." */ 12254359Sroberto if (entry->deleted 12354359Sroberto || entry->absent 12454359Sroberto || entry->depth == svn_depth_exclude) 12554359Sroberto { 12654359Sroberto /* These kinds of nodes cannot be marked for deletion (which also 12754359Sroberto means no "replace" either). */ 12854359Sroberto SVN_ERR_ASSERT(entry->schedule == svn_wc_schedule_add 12954359Sroberto || entry->schedule == svn_wc_schedule_normal); 13054359Sroberto 13154359Sroberto /* Hidden if something hasn't been added over it. 13254359Sroberto 13354359Sroberto ### is this even possible with absent or excluded nodes? */ 13454359Sroberto *hidden = entry->schedule != svn_wc_schedule_add; 13554359Sroberto } 13654359Sroberto else 13754359Sroberto *hidden = FALSE; 13854359Sroberto 13954359Sroberto return SVN_NO_ERROR; 14054359Sroberto} 14154359Sroberto 14254359Sroberto 14354359Sroberto/* Hit the database to check the file external information for the given 14454359Sroberto entry. The entry will be modified in place. */ 14554359Srobertostatic svn_error_t * 14654359Srobertocheck_file_external(svn_wc_entry_t *entry, 147285612Sdelphij svn_wc__db_t *db, 148285612Sdelphij const char *local_abspath, 14954359Sroberto const char *wri_abspath, 15054359Sroberto apr_pool_t *result_pool, 15154359Sroberto apr_pool_t *scratch_pool) 15254359Sroberto{ 15354359Sroberto svn_wc__db_status_t status; 15454359Sroberto svn_node_kind_t kind; 15554359Sroberto const char *repos_relpath; 15654359Sroberto svn_revnum_t peg_revision; 15754359Sroberto svn_revnum_t revision; 15854359Sroberto svn_error_t *err; 15954359Sroberto 16054359Sroberto err = svn_wc__db_external_read(&status, &kind, NULL, NULL, NULL, 16154359Sroberto &repos_relpath, &peg_revision, &revision, 16254359Sroberto db, local_abspath, wri_abspath, 16354359Sroberto result_pool, scratch_pool); 16454359Sroberto 16554359Sroberto if (err) 16654359Sroberto { 16754359Sroberto if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 16854359Sroberto return svn_error_trace(err); 16954359Sroberto 17054359Sroberto svn_error_clear(err); 17154359Sroberto return SVN_NO_ERROR; 17254359Sroberto } 17354359Sroberto 17454359Sroberto if (status == svn_wc__db_status_normal 17554359Sroberto && kind == svn_node_file) 17654359Sroberto { 17754359Sroberto entry->file_external_path = repos_relpath; 17854359Sroberto if (SVN_IS_VALID_REVNUM(peg_revision)) 17954359Sroberto { 18054359Sroberto entry->file_external_peg_rev.kind = svn_opt_revision_number; 18154359Sroberto entry->file_external_peg_rev.value.number = peg_revision; 18254359Sroberto entry->file_external_rev = entry->file_external_peg_rev; 18354359Sroberto } 18454359Sroberto if (SVN_IS_VALID_REVNUM(revision)) 18554359Sroberto { 18654359Sroberto entry->file_external_rev.kind = svn_opt_revision_number; 18754359Sroberto entry->file_external_rev.value.number = revision; 18854359Sroberto } 18954359Sroberto } 19054359Sroberto 19154359Sroberto return SVN_NO_ERROR; 19254359Sroberto} 19354359Sroberto 19454359Sroberto 19554359Sroberto/* Fill in the following fields of ENTRY: 19654359Sroberto 19754359Sroberto REVISION 19854359Sroberto REPOS 19954359Sroberto UUID 20054359Sroberto CMT_REV 20154359Sroberto CMT_DATE 20254359Sroberto CMT_AUTHOR 20354359Sroberto DEPTH 20454359Sroberto DELETED 20554359Sroberto 20654359Sroberto Return: KIND, REPOS_RELPATH, CHECKSUM 20754359Sroberto*/ 20854359Srobertostatic svn_error_t * 20954359Srobertoget_info_for_deleted(svn_wc_entry_t *entry, 21054359Sroberto svn_node_kind_t *kind, 21154359Sroberto const char **repos_relpath, 21254359Sroberto const svn_checksum_t **checksum, 21354359Sroberto svn_wc__db_lock_t **lock, 21454359Sroberto svn_wc__db_t *db, 21554359Sroberto const char *entry_abspath, 21654359Sroberto const svn_wc_entry_t *parent_entry, 21754359Sroberto svn_boolean_t have_base, 21854359Sroberto svn_boolean_t have_more_work, 21954359Sroberto apr_pool_t *result_pool, 22054359Sroberto apr_pool_t *scratch_pool) 22154359Sroberto{ 22254359Sroberto if (have_base && !have_more_work) 22354359Sroberto { 22454359Sroberto /* This is the delete of a BASE node */ 22554359Sroberto SVN_ERR(svn_wc__db_base_get_info(NULL, kind, 22654359Sroberto &entry->revision, 22754359Sroberto repos_relpath, 22854359Sroberto &entry->repos, 22954359Sroberto &entry->uuid, 23054359Sroberto &entry->cmt_rev, 23154359Sroberto &entry->cmt_date, 23254359Sroberto &entry->cmt_author, 23354359Sroberto &entry->depth, 23454359Sroberto checksum, 23554359Sroberto NULL, 23654359Sroberto lock, 23754359Sroberto &entry->has_props, NULL, 23854359Sroberto NULL, 23954359Sroberto db, 24054359Sroberto entry_abspath, 24154359Sroberto result_pool, 24254359Sroberto scratch_pool)); 24354359Sroberto } 24454359Sroberto else 24554359Sroberto { 24654359Sroberto const char *work_del_abspath; 24754359Sroberto const char *parent_repos_relpath; 24854359Sroberto const char *parent_abspath; 24954359Sroberto 25054359Sroberto /* This is a deleted child of a copy/move-here, 25154359Sroberto so we need to scan up the WORKING tree to find the root of 25254359Sroberto the deletion. Then examine its parent to discover its 25354359Sroberto future location in the repository. */ 25454359Sroberto SVN_ERR(svn_wc__db_read_pristine_info(NULL, kind, 25554359Sroberto &entry->cmt_rev, 25654359Sroberto &entry->cmt_date, 25754359Sroberto &entry->cmt_author, 25854359Sroberto &entry->depth, 25954359Sroberto checksum, 26054359Sroberto NULL, 26154359Sroberto &entry->has_props, NULL, 26254359Sroberto db, 26354359Sroberto entry_abspath, 26454359Sroberto result_pool, 26554359Sroberto scratch_pool)); 26654359Sroberto /* working_size and text_time unavailable */ 26754359Sroberto 26854359Sroberto SVN_ERR(svn_wc__db_scan_deletion(NULL, 26954359Sroberto NULL, 27054359Sroberto &work_del_abspath, NULL, 27154359Sroberto db, entry_abspath, 27254359Sroberto scratch_pool, scratch_pool)); 27354359Sroberto 27454359Sroberto SVN_ERR_ASSERT(work_del_abspath != NULL); 27554359Sroberto parent_abspath = svn_dirent_dirname(work_del_abspath, scratch_pool); 27654359Sroberto 27754359Sroberto /* The parent directory of the delete root must be added, so we 27854359Sroberto can find the required information there */ 27954359Sroberto SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, 28054359Sroberto &parent_repos_relpath, 28154359Sroberto &entry->repos, 28254359Sroberto &entry->uuid, 28354359Sroberto NULL, NULL, NULL, NULL, 28454359Sroberto db, parent_abspath, 28554359Sroberto result_pool, scratch_pool)); 28654359Sroberto 28754359Sroberto /* Now glue it all together */ 28854359Sroberto *repos_relpath = svn_relpath_join(parent_repos_relpath, 28954359Sroberto svn_dirent_is_child(parent_abspath, 29054359Sroberto entry_abspath, 29154359Sroberto NULL), 29254359Sroberto result_pool); 29354359Sroberto 29454359Sroberto 29554359Sroberto /* Even though this is the delete of a WORKING node, there might still 29654359Sroberto be a BASE node somewhere below with an interesting revision */ 29754359Sroberto if (have_base) 29854359Sroberto { 29954359Sroberto svn_wc__db_status_t status; 30054359Sroberto SVN_ERR(svn_wc__db_base_get_info(&status, NULL, &entry->revision, 30154359Sroberto NULL, NULL, NULL, NULL, NULL, NULL, 30254359Sroberto NULL, NULL, NULL, lock, NULL, NULL, 30354359Sroberto NULL, 30454359Sroberto db, entry_abspath, 305182007Sroberto result_pool, scratch_pool)); 30654359Sroberto 30754359Sroberto if (status == svn_wc__db_status_not_present) 30854359Sroberto entry->deleted = TRUE; 30954359Sroberto } 31054359Sroberto } 31154359Sroberto 31254359Sroberto /* Do some extra work for the child nodes. */ 31354359Sroberto if (!SVN_IS_VALID_REVNUM(entry->revision) && parent_entry != NULL) 31454359Sroberto { 31554359Sroberto /* For child nodes without a revision, pick up the parent's 31654359Sroberto revision. */ 31754359Sroberto entry->revision = parent_entry->revision; 31854359Sroberto } 31954359Sroberto 32054359Sroberto return SVN_NO_ERROR; 32154359Sroberto} 32254359Sroberto 32354359Sroberto 32454359Sroberto/* 32554359Sroberto * Encode tree conflict descriptions into a single string. 32654359Sroberto * 32754359Sroberto * Set *CONFLICT_DATA to a string, allocated in POOL, that encodes the tree 32854359Sroberto * conflicts in CONFLICTS in a form suitable for storage in a single string 32954359Sroberto * field in a WC entry. CONFLICTS is a hash of zero or more pointers to 33054359Sroberto * svn_wc_conflict_description2_t objects, index by their basenames. All of the 33154359Sroberto * conflict victim paths must be siblings. 33254359Sroberto * 33354359Sroberto * Do all allocations in POOL. 33454359Sroberto * 33554359Sroberto * @see svn_wc__read_tree_conflicts() 33654359Sroberto */ 33754359Srobertostatic svn_error_t * 33854359Srobertowrite_tree_conflicts(const char **conflict_data, 33954359Sroberto apr_hash_t *conflicts, 34054359Sroberto apr_pool_t *pool) 34154359Sroberto{ 34254359Sroberto svn_skel_t *skel = svn_skel__make_empty_list(pool); 34354359Sroberto apr_hash_index_t *hi; 34454359Sroberto 34554359Sroberto for (hi = apr_hash_first(pool, conflicts); hi; hi = apr_hash_next(hi)) 34654359Sroberto { 347182007Sroberto svn_skel_t *c_skel; 34854359Sroberto 34954359Sroberto SVN_ERR(svn_wc__serialize_conflict(&c_skel, svn__apr_hash_index_val(hi), 35054359Sroberto pool, pool)); 35154359Sroberto svn_skel__prepend(c_skel, skel); 35254359Sroberto } 35354359Sroberto 35454359Sroberto *conflict_data = svn_skel__unparse(skel, pool)->data; 355182007Sroberto 356182007Sroberto return SVN_NO_ERROR; 35754359Sroberto} 35854359Sroberto 359182007Sroberto 36054359Sroberto/* Read one entry from wc_db. It will be allocated in RESULT_POOL and 36154359Sroberto returned in *NEW_ENTRY. 36254359Sroberto 36354359Sroberto DIR_ABSPATH is the name of the directory to read this entry from, and 36454359Sroberto it will be named NAME (use "" for "this dir"). 36554359Sroberto 36654359Sroberto DB specifies the wc_db database, and WC_ID specifies which working copy 36754359Sroberto this information is being read from. 36854359Sroberto 36954359Sroberto If this node is "this dir", then PARENT_ENTRY should be NULL. Otherwise, 37054359Sroberto it should refer to the entry for the child's parent directory. 37154359Sroberto 37254359Sroberto Temporary allocations are made in SCRATCH_POOL. */ 37354359Srobertostatic svn_error_t * 37454359Srobertoread_one_entry(const svn_wc_entry_t **new_entry, 37554359Sroberto svn_wc__db_t *db, 37654359Sroberto apr_int64_t wc_id, 37754359Sroberto const char *dir_abspath, 37854359Sroberto const char *name, 37954359Sroberto const svn_wc_entry_t *parent_entry, 38054359Sroberto apr_pool_t *result_pool, 38154359Sroberto apr_pool_t *scratch_pool) 38254359Sroberto{ 38354359Sroberto svn_node_kind_t kind; 38454359Sroberto svn_wc__db_status_t status; 38554359Sroberto svn_wc__db_lock_t *lock; 38654359Sroberto const char *repos_relpath; 38754359Sroberto const svn_checksum_t *checksum; 38854359Sroberto svn_filesize_t translated_size; 38954359Sroberto svn_wc_entry_t *entry = alloc_entry(result_pool); 39054359Sroberto const char *entry_abspath; 39154359Sroberto const char *original_repos_relpath; 39254359Sroberto const char *original_root_url; 39354359Sroberto svn_boolean_t conflicted; 39454359Sroberto svn_boolean_t have_base; 39554359Sroberto svn_boolean_t have_more_work; 39654359Sroberto 39754359Sroberto entry->name = name; 39854359Sroberto 39954359Sroberto entry_abspath = svn_dirent_join(dir_abspath, entry->name, scratch_pool); 40054359Sroberto 40154359Sroberto SVN_ERR(svn_wc__db_read_info( 40254359Sroberto &status, 40354359Sroberto &kind, 40454359Sroberto &entry->revision, 40554359Sroberto &repos_relpath, 40654359Sroberto &entry->repos, 40754359Sroberto &entry->uuid, 40854359Sroberto &entry->cmt_rev, 40954359Sroberto &entry->cmt_date, 41054359Sroberto &entry->cmt_author, 41154359Sroberto &entry->depth, 41254359Sroberto &checksum, 41354359Sroberto NULL, 41454359Sroberto &original_repos_relpath, 41554359Sroberto &original_root_url, 41654359Sroberto NULL, 41754359Sroberto &entry->copyfrom_rev, 41854359Sroberto &lock, 41954359Sroberto &translated_size, 42054359Sroberto &entry->text_time, 42154359Sroberto &entry->changelist, 42254359Sroberto &conflicted, 42354359Sroberto NULL /* op_root */, 42454359Sroberto &entry->has_props /* have_props */, 42554359Sroberto &entry->has_prop_mods /* props_mod */, 42654359Sroberto &have_base, 42754359Sroberto &have_more_work, 42854359Sroberto NULL /* have_work */, 42954359Sroberto db, 43054359Sroberto entry_abspath, 43154359Sroberto result_pool, 43254359Sroberto scratch_pool)); 43354359Sroberto 43454359Sroberto if (entry->has_prop_mods) 43554359Sroberto entry->has_props = TRUE; 43654359Sroberto 43754359Sroberto if (strcmp(entry->name, SVN_WC_ENTRY_THIS_DIR) == 0) 43854359Sroberto { 43954359Sroberto /* get the tree conflict data. */ 44054359Sroberto apr_hash_t *tree_conflicts = NULL; 44154359Sroberto const apr_array_header_t *conflict_victims; 44254359Sroberto int k; 44354359Sroberto 44454359Sroberto SVN_ERR(svn_wc__db_read_conflict_victims(&conflict_victims, db, 44554359Sroberto dir_abspath, 44654359Sroberto scratch_pool, 44754359Sroberto scratch_pool)); 44854359Sroberto 44954359Sroberto for (k = 0; k < conflict_victims->nelts; k++) 45054359Sroberto { 45154359Sroberto int j; 45254359Sroberto const apr_array_header_t *child_conflicts; 45354359Sroberto const char *child_name; 45454359Sroberto const char *child_abspath; 45554359Sroberto 45654359Sroberto child_name = APR_ARRAY_IDX(conflict_victims, k, const char *); 45754359Sroberto child_abspath = svn_dirent_join(dir_abspath, child_name, 45854359Sroberto scratch_pool); 45954359Sroberto 46054359Sroberto SVN_ERR(svn_wc__read_conflicts(&child_conflicts, 46154359Sroberto db, child_abspath, 46254359Sroberto FALSE /* create tempfiles */, 46354359Sroberto scratch_pool, scratch_pool)); 46454359Sroberto 46554359Sroberto for (j = 0; j < child_conflicts->nelts; j++) 46654359Sroberto { 46754359Sroberto const svn_wc_conflict_description2_t *conflict = 46854359Sroberto APR_ARRAY_IDX(child_conflicts, j, 46954359Sroberto svn_wc_conflict_description2_t *); 47054359Sroberto 47154359Sroberto if (conflict->kind == svn_wc_conflict_kind_tree) 47254359Sroberto { 47354359Sroberto if (!tree_conflicts) 47454359Sroberto tree_conflicts = apr_hash_make(scratch_pool); 47554359Sroberto svn_hash_sets(tree_conflicts, child_name, conflict); 47654359Sroberto } 47754359Sroberto } 47854359Sroberto } 47954359Sroberto 48054359Sroberto if (tree_conflicts) 48154359Sroberto { 48254359Sroberto SVN_ERR(write_tree_conflicts(&entry->tree_conflict_data, 48354359Sroberto tree_conflicts, result_pool)); 48454359Sroberto } 48554359Sroberto } 48654359Sroberto 48754359Sroberto if (status == svn_wc__db_status_normal 48854359Sroberto || status == svn_wc__db_status_incomplete) 48954359Sroberto { 49054359Sroberto /* Plain old BASE node. */ 49154359Sroberto entry->schedule = svn_wc_schedule_normal; 49254359Sroberto 49354359Sroberto /* Grab inherited repository information, if necessary. */ 49454359Sroberto if (repos_relpath == NULL) 49554359Sroberto { 49654359Sroberto SVN_ERR(svn_wc__db_scan_base_repos(&repos_relpath, 49754359Sroberto &entry->repos, 49854359Sroberto &entry->uuid, 49954359Sroberto db, 50054359Sroberto entry_abspath, 50154359Sroberto result_pool, 50254359Sroberto scratch_pool)); 50354359Sroberto } 50454359Sroberto 50554359Sroberto entry->incomplete = (status == svn_wc__db_status_incomplete); 50654359Sroberto } 50754359Sroberto else if (status == svn_wc__db_status_deleted) 50854359Sroberto { 50954359Sroberto svn_node_kind_t path_kind; 51054359Sroberto 51154359Sroberto /* ### we don't have to worry about moves, so this is a delete. */ 51254359Sroberto entry->schedule = svn_wc_schedule_delete; 51354359Sroberto 51454359Sroberto /* If there are multiple working layers or no BASE layer, then 51554359Sroberto this is a WORKING delete or WORKING not-present. */ 51654359Sroberto if (have_more_work || !have_base) 51754359Sroberto entry->copied = TRUE; 51854359Sroberto else if (have_base && !have_more_work) 51954359Sroberto entry->copied = FALSE; 52054359Sroberto else 52154359Sroberto { 52254359Sroberto const char *work_del_abspath; 52354359Sroberto SVN_ERR(svn_wc__db_scan_deletion(NULL, NULL, 52454359Sroberto &work_del_abspath, NULL, 52554359Sroberto db, entry_abspath, 52654359Sroberto scratch_pool, scratch_pool)); 52754359Sroberto 52854359Sroberto if (work_del_abspath) 52954359Sroberto entry->copied = TRUE; 53054359Sroberto } 53154359Sroberto 53254359Sroberto /* If there is still a directory on-disk we keep it, if not it is 53354359Sroberto already deleted. Simple, isn't it? 53454359Sroberto 53554359Sroberto Before single-db we had to keep the administative area alive until 53654359Sroberto after the commit really deletes it. Setting keep alive stopped the 53754359Sroberto commit processing from deleting the directory. We don't delete it 53854359Sroberto any more, so all we have to do is provide some 'sane' value. 53954359Sroberto */ 54054359Sroberto SVN_ERR(svn_io_check_path(entry_abspath, &path_kind, scratch_pool)); 54154359Sroberto entry->keep_local = (path_kind == svn_node_dir); 54254359Sroberto } 54354359Sroberto else if (status == svn_wc__db_status_added) 54454359Sroberto { 54554359Sroberto svn_wc__db_status_t work_status; 54654359Sroberto const char *op_root_abspath; 54754359Sroberto const char *scanned_original_relpath; 54854359Sroberto svn_revnum_t original_revision; 549 550 /* For child nodes, pick up the parent's revision. */ 551 if (*entry->name != '\0') 552 { 553 assert(parent_entry != NULL); 554 assert(entry->revision == SVN_INVALID_REVNUM); 555 556 entry->revision = parent_entry->revision; 557 } 558 559 if (have_base) 560 { 561 svn_wc__db_status_t base_status; 562 563 /* ENTRY->REVISION is overloaded. When a node is schedule-add 564 or -replace, then REVISION refers to the BASE node's revision 565 that is being overwritten. We need to fetch it now. */ 566 SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, 567 &entry->revision, 568 NULL, NULL, NULL, 569 NULL, NULL, NULL, 570 NULL, NULL, NULL, 571 NULL, NULL, NULL, NULL, 572 db, entry_abspath, 573 scratch_pool, 574 scratch_pool)); 575 576 if (base_status == svn_wc__db_status_not_present) 577 { 578 /* The underlying node is DELETED in this revision. */ 579 entry->deleted = TRUE; 580 581 /* This is an add since there isn't a node to replace. */ 582 entry->schedule = svn_wc_schedule_add; 583 } 584 else 585 entry->schedule = svn_wc_schedule_replace; 586 } 587 else 588 { 589 /* There is NO 'not-present' BASE_NODE for this node. 590 Therefore, we are looking at some kind of add/copy 591 rather than a replace. */ 592 593 /* ### if this looks like a plain old add, then rev=0. */ 594 if (!SVN_IS_VALID_REVNUM(entry->copyfrom_rev) 595 && !SVN_IS_VALID_REVNUM(entry->cmt_rev)) 596 entry->revision = 0; 597 598 entry->schedule = svn_wc_schedule_add; 599 } 600 601 /* If we don't have "real" data from the entry (obstruction), 602 then we cannot begin a scan for data. The original node may 603 have important data. Set up stuff to kill that idea off, 604 and finish up this entry. */ 605 { 606 SVN_ERR(svn_wc__db_scan_addition(&work_status, 607 &op_root_abspath, 608 &repos_relpath, 609 &entry->repos, 610 &entry->uuid, 611 &scanned_original_relpath, 612 NULL, NULL, /* original_root|uuid */ 613 &original_revision, 614 db, 615 entry_abspath, 616 result_pool, scratch_pool)); 617 618 /* In wc.db we want to keep the valid revision of the not-present 619 BASE_REV, but when we used entries we set the revision to 0 620 when adding a new node over a not present base node. */ 621 if (work_status == svn_wc__db_status_added 622 && entry->deleted) 623 entry->revision = 0; 624 } 625 626 if (!SVN_IS_VALID_REVNUM(entry->cmt_rev) 627 && scanned_original_relpath == NULL) 628 { 629 /* There is NOT a last-changed revision (last-changed date and 630 author may be unknown, but we can always check the rev). 631 The absence of a revision implies this node was added WITHOUT 632 any history. Avoid the COPIED checks in the else block. */ 633 /* ### scan_addition may need to be updated to avoid returning 634 ### status_copied in this case. */ 635 } 636 /* For backwards-compatiblity purposes we treat moves just like 637 * regular copies. */ 638 else if (work_status == svn_wc__db_status_copied || 639 work_status == svn_wc__db_status_moved_here) 640 { 641 entry->copied = TRUE; 642 643 /* If this is a child of a copied subtree, then it should be 644 schedule_normal. */ 645 if (original_repos_relpath == NULL) 646 { 647 /* ### what if there is a BASE node under there? */ 648 entry->schedule = svn_wc_schedule_normal; 649 } 650 651 /* Copied nodes need to mirror their copyfrom_rev, if they 652 don't have a revision of their own already. */ 653 if (!SVN_IS_VALID_REVNUM(entry->revision) 654 || entry->revision == 0 /* added */) 655 entry->revision = original_revision; 656 } 657 658 /* Does this node have copyfrom_* information? */ 659 if (scanned_original_relpath != NULL) 660 { 661 svn_boolean_t is_copied_child; 662 svn_boolean_t is_mixed_rev = FALSE; 663 664 SVN_ERR_ASSERT(work_status == svn_wc__db_status_copied || 665 work_status == svn_wc__db_status_moved_here); 666 667 /* If this node inherits copyfrom information from an 668 ancestor node, then it must be a copied child. */ 669 is_copied_child = (original_repos_relpath == NULL); 670 671 /* If this node has copyfrom information on it, then it may 672 be an actual copy-root, or it could be participating in 673 a mixed-revision copied tree. So if we don't already know 674 this is a copied child, then we need to look for this 675 mixed-revision situation. */ 676 if (!is_copied_child) 677 { 678 const char *parent_abspath; 679 svn_error_t *err; 680 const char *parent_repos_relpath; 681 const char *parent_root_url; 682 683 /* When we insert entries into the database, we will 684 construct additional copyfrom records for mixed-revision 685 copies. The old entries would simply record the different 686 revision in the entry->revision field. That is not 687 available within wc-ng, so additional copies are made 688 (see the logic inside write_entry()). However, when 689 reading these back *out* of the database, the additional 690 copies look like new "Added" nodes rather than a simple 691 mixed-rev working copy. 692 693 That would be a behavior change if we did not compensate. 694 If there is copyfrom information for this node, then the 695 code below looks at the parent to detect if it *also* has 696 copyfrom information, and if the copyfrom_url would align 697 properly. If it *does*, then we omit storing copyfrom_url 698 and copyfrom_rev (ie. inherit the copyfrom info like a 699 normal child), and update entry->revision with the 700 copyfrom_rev in order to (re)create the mixed-rev copied 701 subtree that was originally presented for storage. */ 702 703 /* Get the copyfrom information from our parent. 704 705 Note that the parent could be added/copied/moved-here. 706 There is no way for it to be deleted/moved-away and 707 have *this* node appear as copied. */ 708 parent_abspath = svn_dirent_dirname(entry_abspath, 709 scratch_pool); 710 err = svn_wc__db_scan_addition(NULL, 711 &op_root_abspath, 712 NULL, NULL, NULL, 713 &parent_repos_relpath, 714 &parent_root_url, 715 NULL, NULL, 716 db, parent_abspath, 717 scratch_pool, 718 scratch_pool); 719 if (err) 720 { 721 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 722 return svn_error_trace(err); 723 svn_error_clear(err); 724 } 725 else if (parent_root_url != NULL 726 && strcmp(original_root_url, parent_root_url) == 0) 727 { 728 const char *relpath_to_entry = svn_dirent_is_child( 729 op_root_abspath, entry_abspath, NULL); 730 const char *entry_repos_relpath = svn_relpath_join( 731 parent_repos_relpath, relpath_to_entry, scratch_pool); 732 733 /* The copyfrom repos roots matched. 734 735 Now we look to see if the copyfrom path of the parent 736 would align with our own path. If so, then it means 737 this copyfrom was spontaneously created and inserted 738 for mixed-rev purposes and can be eliminated without 739 changing the semantics of a mixed-rev copied subtree. 740 741 See notes/api-errata/wc003.txt for some additional 742 detail, and potential issues. */ 743 if (strcmp(entry_repos_relpath, 744 original_repos_relpath) == 0) 745 { 746 is_copied_child = TRUE; 747 is_mixed_rev = TRUE; 748 } 749 } 750 } 751 752 if (is_copied_child) 753 { 754 /* We won't be settig the copyfrom_url, yet need to 755 clear out the copyfrom_rev. Thus, this node becomes a 756 child of a copied subtree (rather than its own root). */ 757 entry->copyfrom_rev = SVN_INVALID_REVNUM; 758 759 /* Children in a copied subtree are schedule normal 760 since we don't plan to actually *do* anything with 761 them. Their operation is implied by ancestors. */ 762 entry->schedule = svn_wc_schedule_normal; 763 764 /* And *finally* we turn this entry into the mixed 765 revision node that it was intended to be. This 766 node's revision is taken from the copyfrom record 767 that we spontaneously constructed. */ 768 if (is_mixed_rev) 769 entry->revision = original_revision; 770 } 771 else if (original_repos_relpath != NULL) 772 { 773 entry->copyfrom_url = 774 svn_path_url_add_component2(original_root_url, 775 original_repos_relpath, 776 result_pool); 777 } 778 else 779 { 780 /* NOTE: if original_repos_relpath == NULL, then the 781 second call to scan_addition() will not have occurred. 782 Thus, this use of OP_ROOT_ABSPATH still contains the 783 original value where we fetched a value for 784 SCANNED_REPOS_RELPATH. */ 785 const char *relpath_to_entry = svn_dirent_is_child( 786 op_root_abspath, entry_abspath, NULL); 787 const char *entry_repos_relpath = svn_relpath_join( 788 scanned_original_relpath, relpath_to_entry, scratch_pool); 789 790 entry->copyfrom_url = 791 svn_path_url_add_component2(original_root_url, 792 entry_repos_relpath, 793 result_pool); 794 } 795 } 796 } 797 else if (status == svn_wc__db_status_not_present) 798 { 799 /* ### buh. 'deleted' nodes are actually supposed to be 800 ### schedule "normal" since we aren't going to actually *do* 801 ### anything to this node at commit time. */ 802 entry->schedule = svn_wc_schedule_normal; 803 entry->deleted = TRUE; 804 } 805 else if (status == svn_wc__db_status_server_excluded) 806 { 807 entry->absent = TRUE; 808 } 809 else if (status == svn_wc__db_status_excluded) 810 { 811 entry->schedule = svn_wc_schedule_normal; 812 entry->depth = svn_depth_exclude; 813 } 814 else 815 { 816 /* ### we should have handled all possible status values. */ 817 SVN_ERR_MALFUNCTION(); 818 } 819 820 /* ### higher levels want repos information about deleted nodes, even 821 ### tho they are not "part of" a repository any more. */ 822 if (entry->schedule == svn_wc_schedule_delete) 823 { 824 SVN_ERR(get_info_for_deleted(entry, 825 &kind, 826 &repos_relpath, 827 &checksum, 828 &lock, 829 db, entry_abspath, 830 parent_entry, 831 have_base, have_more_work, 832 result_pool, scratch_pool)); 833 } 834 835 /* ### default to the infinite depth if we don't know it. */ 836 if (entry->depth == svn_depth_unknown) 837 entry->depth = svn_depth_infinity; 838 839 if (kind == svn_node_dir) 840 entry->kind = svn_node_dir; 841 else if (kind == svn_node_file) 842 entry->kind = svn_node_file; 843 else if (kind == svn_node_symlink) 844 entry->kind = svn_node_file; /* ### no symlink kind */ 845 else 846 entry->kind = svn_node_unknown; 847 848 /* We should always have a REPOS_RELPATH, except for: 849 - deleted nodes 850 - certain obstructed nodes 851 - not-present nodes 852 - absent nodes 853 - excluded nodes 854 855 ### the last three should probably have an "implied" REPOS_RELPATH 856 */ 857 SVN_ERR_ASSERT(repos_relpath != NULL 858 || entry->schedule == svn_wc_schedule_delete 859 || status == svn_wc__db_status_not_present 860 || status == svn_wc__db_status_server_excluded 861 || status == svn_wc__db_status_excluded); 862 if (repos_relpath) 863 entry->url = svn_path_url_add_component2(entry->repos, 864 repos_relpath, 865 result_pool); 866 867 if (checksum) 868 { 869 /* We got a SHA-1, get the corresponding MD-5. */ 870 if (checksum->kind != svn_checksum_md5) 871 SVN_ERR(svn_wc__db_pristine_get_md5(&checksum, db, 872 entry_abspath, checksum, 873 scratch_pool, scratch_pool)); 874 875 SVN_ERR_ASSERT(checksum->kind == svn_checksum_md5); 876 entry->checksum = svn_checksum_to_cstring(checksum, result_pool); 877 } 878 879 if (conflicted) 880 { 881 svn_skel_t *conflict; 882 svn_boolean_t text_conflicted; 883 svn_boolean_t prop_conflicted; 884 SVN_ERR(svn_wc__db_read_conflict(&conflict, db, entry_abspath, 885 scratch_pool, scratch_pool)); 886 887 SVN_ERR(svn_wc__conflict_read_info(NULL, NULL, &text_conflicted, 888 &prop_conflicted, NULL, 889 db, dir_abspath, conflict, 890 scratch_pool, scratch_pool)); 891 892 if (text_conflicted) 893 { 894 const char *my_abspath; 895 const char *their_old_abspath; 896 const char *their_abspath; 897 SVN_ERR(svn_wc__conflict_read_text_conflict(&my_abspath, 898 &their_old_abspath, 899 &their_abspath, 900 db, dir_abspath, 901 conflict, scratch_pool, 902 scratch_pool)); 903 904 if (my_abspath) 905 entry->conflict_wrk = svn_dirent_basename(my_abspath, result_pool); 906 907 if (their_old_abspath) 908 entry->conflict_old = svn_dirent_basename(their_old_abspath, 909 result_pool); 910 911 if (their_abspath) 912 entry->conflict_new = svn_dirent_basename(their_abspath, 913 result_pool); 914 } 915 916 if (prop_conflicted) 917 { 918 const char *prej_abspath; 919 920 SVN_ERR(svn_wc__conflict_read_prop_conflict(&prej_abspath, NULL, 921 NULL, NULL, NULL, 922 db, dir_abspath, 923 conflict, scratch_pool, 924 scratch_pool)); 925 926 if (prej_abspath) 927 entry->prejfile = svn_dirent_basename(prej_abspath, result_pool); 928 } 929 } 930 931 if (lock) 932 { 933 entry->lock_token = lock->token; 934 entry->lock_owner = lock->owner; 935 entry->lock_comment = lock->comment; 936 entry->lock_creation_date = lock->date; 937 } 938 939 /* Let's check for a file external. ugh. */ 940 if (status == svn_wc__db_status_normal 941 && kind == svn_node_file) 942 SVN_ERR(check_file_external(entry, db, entry_abspath, dir_abspath, 943 result_pool, scratch_pool)); 944 945 entry->working_size = translated_size; 946 947 *new_entry = entry; 948 949 return SVN_NO_ERROR; 950} 951 952/* Read entries for PATH/LOCAL_ABSPATH from DB. The entries 953 will be allocated in RESULT_POOL, with temporary allocations in 954 SCRATCH_POOL. The entries are returned in RESULT_ENTRIES. */ 955static svn_error_t * 956read_entries_new(apr_hash_t **result_entries, 957 svn_wc__db_t *db, 958 const char *local_abspath, 959 apr_pool_t *result_pool, 960 apr_pool_t *scratch_pool) 961{ 962 apr_hash_t *entries; 963 const apr_array_header_t *children; 964 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 965 int i; 966 const svn_wc_entry_t *parent_entry; 967 apr_int64_t wc_id = 1; /* ### hacky. should remove. */ 968 969 entries = apr_hash_make(result_pool); 970 971 SVN_ERR(read_one_entry(&parent_entry, db, wc_id, local_abspath, 972 "" /* name */, 973 NULL /* parent_entry */, 974 result_pool, iterpool)); 975 svn_hash_sets(entries, "", parent_entry); 976 977 /* Use result_pool so that the child names (used by reference, rather 978 than copied) appear in result_pool. */ 979 SVN_ERR(svn_wc__db_read_children(&children, db, 980 local_abspath, 981 result_pool, iterpool)); 982 for (i = children->nelts; i--; ) 983 { 984 const char *name = APR_ARRAY_IDX(children, i, const char *); 985 const svn_wc_entry_t *entry; 986 987 svn_pool_clear(iterpool); 988 989 SVN_ERR(read_one_entry(&entry, 990 db, wc_id, local_abspath, name, parent_entry, 991 result_pool, iterpool)); 992 svn_hash_sets(entries, entry->name, entry); 993 } 994 995 svn_pool_destroy(iterpool); 996 997 *result_entries = entries; 998 999 return SVN_NO_ERROR; 1000} 1001 1002 1003/* Read a pair of entries from wc_db in the directory DIR_ABSPATH. Return 1004 the directory's entry in *PARENT_ENTRY and NAME's entry in *ENTRY. The 1005 two returned pointers will be the same if NAME=="" ("this dir"). 1006 1007 The parent entry must exist. 1008 1009 The requested entry MAY exist. If it does not, then NULL will be returned. 1010 1011 The resulting entries are allocated in RESULT_POOL, and all temporary 1012 allocations are made in SCRATCH_POOL. */ 1013static svn_error_t * 1014read_entry_pair(const svn_wc_entry_t **parent_entry, 1015 const svn_wc_entry_t **entry, 1016 svn_wc__db_t *db, 1017 const char *dir_abspath, 1018 const char *name, 1019 apr_pool_t *result_pool, 1020 apr_pool_t *scratch_pool) 1021{ 1022 apr_int64_t wc_id = 1; /* ### hacky. should remove. */ 1023 1024 SVN_ERR(read_one_entry(parent_entry, db, wc_id, dir_abspath, 1025 "" /* name */, 1026 NULL /* parent_entry */, 1027 result_pool, scratch_pool)); 1028 1029 /* If we need the entry for "this dir", then return the parent_entry 1030 in both outputs. Otherwise, read the child node. */ 1031 if (*name == '\0') 1032 { 1033 /* If the retrieved node is a FILE, then we have a problem. We asked 1034 for a directory. This implies there is an obstructing, unversioned 1035 directory where a FILE should be. We navigated from the obstructing 1036 subdir up to the parent dir, then returned the FILE found there. 1037 1038 Let's return WC_MISSING cuz the caller thought we had a dir, but 1039 that (versioned subdir) isn't there. */ 1040 if ((*parent_entry)->kind == svn_node_file) 1041 { 1042 *parent_entry = NULL; 1043 return svn_error_createf(SVN_ERR_WC_MISSING, NULL, 1044 _("'%s' is not a versioned working copy"), 1045 svn_dirent_local_style(dir_abspath, 1046 scratch_pool)); 1047 } 1048 1049 *entry = *parent_entry; 1050 } 1051 else 1052 { 1053 const apr_array_header_t *children; 1054 int i; 1055 1056 /* Default to not finding the child. */ 1057 *entry = NULL; 1058 1059 /* Determine whether the parent KNOWS about this child. If it does 1060 not, then we should not attempt to look for it. 1061 1062 For example: the parent doesn't "know" about the child, but the 1063 versioned directory *does* exist on disk. We don't want to look 1064 into that subdir. */ 1065 SVN_ERR(svn_wc__db_read_children(&children, db, dir_abspath, 1066 scratch_pool, scratch_pool)); 1067 for (i = children->nelts; i--; ) 1068 { 1069 const char *child = APR_ARRAY_IDX(children, i, const char *); 1070 1071 if (strcmp(child, name) == 0) 1072 { 1073 svn_error_t *err; 1074 1075 err = read_one_entry(entry, 1076 db, wc_id, dir_abspath, name, *parent_entry, 1077 result_pool, scratch_pool); 1078 if (err) 1079 { 1080 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 1081 return svn_error_trace(err); 1082 1083 /* No problem. Clear the error and leave the default value 1084 of "missing". */ 1085 svn_error_clear(err); 1086 } 1087 1088 /* Found it. No need to keep searching. */ 1089 break; 1090 } 1091 } 1092 /* if the loop ends without finding a child, then we have the default 1093 ENTRY value of NULL. */ 1094 } 1095 1096 return SVN_NO_ERROR; 1097} 1098 1099 1100/* */ 1101static svn_error_t * 1102read_entries(apr_hash_t **entries, 1103 svn_wc__db_t *db, 1104 const char *wcroot_abspath, 1105 apr_pool_t *result_pool, 1106 apr_pool_t *scratch_pool) 1107{ 1108 int wc_format; 1109 1110 SVN_ERR(svn_wc__db_temp_get_format(&wc_format, db, wcroot_abspath, 1111 scratch_pool)); 1112 1113 if (wc_format < SVN_WC__WC_NG_VERSION) 1114 return svn_error_trace(svn_wc__read_entries_old(entries, 1115 wcroot_abspath, 1116 result_pool, 1117 scratch_pool)); 1118 1119 return svn_error_trace(read_entries_new(entries, db, wcroot_abspath, 1120 result_pool, scratch_pool)); 1121} 1122 1123 1124/* For a given LOCAL_ABSPATH, using DB, set *ADM_ABSPATH to the directory in 1125 which the entry information is located, and *ENTRY_NAME to the entry name 1126 to access that entry. 1127 1128 KIND is as in svn_wc__get_entry(). 1129 1130 Return the results in RESULT_POOL and use SCRATCH_POOL for temporary 1131 allocations. */ 1132static svn_error_t * 1133get_entry_access_info(const char **adm_abspath, 1134 const char **entry_name, 1135 svn_wc__db_t *db, 1136 const char *local_abspath, 1137 svn_node_kind_t kind, 1138 apr_pool_t *result_pool, 1139 apr_pool_t *scratch_pool) 1140{ 1141 svn_wc_adm_access_t *adm_access; 1142 svn_boolean_t read_from_subdir = FALSE; 1143 1144 /* If the caller didn't know the node kind, then stat the path. Maybe 1145 it is really there, and we can speed up the steps below. */ 1146 if (kind == svn_node_unknown) 1147 { 1148 svn_node_kind_t on_disk; 1149 1150 /* Do we already have an access baton for LOCAL_ABSPATH? */ 1151 adm_access = svn_wc__adm_retrieve_internal2(db, local_abspath, 1152 scratch_pool); 1153 if (adm_access) 1154 { 1155 /* Sweet. The node is a directory. */ 1156 on_disk = svn_node_dir; 1157 } 1158 else 1159 { 1160 svn_boolean_t special; 1161 1162 /* What's on disk? */ 1163 SVN_ERR(svn_io_check_special_path(local_abspath, &on_disk, &special, 1164 scratch_pool)); 1165 } 1166 1167 if (on_disk != svn_node_dir) 1168 { 1169 /* If this is *anything* besides a directory (FILE, NONE, or 1170 UNKNOWN), then we cannot treat it as a versioned directory 1171 containing entries to read. Leave READ_FROM_SUBDIR as FALSE, 1172 so that the parent will be examined. 1173 1174 For NONE and UNKNOWN, it may be that metadata exists for the 1175 node, even though on-disk is unhelpful. 1176 1177 If NEED_PARENT_STUB is TRUE, and the entry is not a DIRECTORY, 1178 then we'll error. 1179 1180 If NEED_PARENT_STUB if FALSE, and we successfully read a stub, 1181 then this on-disk node is obstructing the read. */ 1182 } 1183 else 1184 { 1185 /* We found a directory for this UNKNOWN node. Determine whether 1186 we need to read inside it. */ 1187 read_from_subdir = TRUE; 1188 } 1189 } 1190 else if (kind == svn_node_dir) 1191 { 1192 read_from_subdir = TRUE; 1193 } 1194 1195 if (read_from_subdir) 1196 { 1197 /* KIND must be a DIR or UNKNOWN (and we found a subdir). We want 1198 the "real" data, so treat LOCAL_ABSPATH as a versioned directory. */ 1199 *adm_abspath = apr_pstrdup(result_pool, local_abspath); 1200 *entry_name = ""; 1201 } 1202 else 1203 { 1204 /* FILE node needs to read the parent directory. Or a DIR node 1205 needs to read from the parent to get at the stub entry. Or this 1206 is an UNKNOWN node, and we need to examine the parent. */ 1207 svn_dirent_split(adm_abspath, entry_name, local_abspath, result_pool); 1208 } 1209 1210 return SVN_NO_ERROR; 1211} 1212 1213 1214svn_error_t * 1215svn_wc__get_entry(const svn_wc_entry_t **entry, 1216 svn_wc__db_t *db, 1217 const char *local_abspath, 1218 svn_boolean_t allow_unversioned, 1219 svn_node_kind_t kind, 1220 apr_pool_t *result_pool, 1221 apr_pool_t *scratch_pool) 1222{ 1223 const char *dir_abspath; 1224 const char *entry_name; 1225 1226 SVN_ERR(get_entry_access_info(&dir_abspath, &entry_name, db, local_abspath, 1227 kind, scratch_pool, scratch_pool)); 1228 1229 { 1230 const svn_wc_entry_t *parent_entry; 1231 svn_error_t *err; 1232 1233 /* NOTE: if KIND is UNKNOWN and we decided to examine the *parent* 1234 directory, then it is possible we moved out of the working copy. 1235 If the on-disk node is a DIR, and we asked for a stub, then we 1236 obviously can't provide that (parent has no info). If the on-disk 1237 node is a FILE/NONE/UNKNOWN, then it is obstructing the real 1238 LOCAL_ABSPATH (or it was never a versioned item). In all these 1239 cases, the read_entries() will (properly) throw an error. 1240 1241 NOTE: if KIND is a DIR and we asked for the real data, but it is 1242 obstructed on-disk by some other node kind (NONE, FILE, UNKNOWN), 1243 then this will throw an error. */ 1244 1245 err = read_entry_pair(&parent_entry, entry, 1246 db, dir_abspath, entry_name, 1247 result_pool, scratch_pool); 1248 if (err) 1249 { 1250 if (err->apr_err != SVN_ERR_WC_MISSING || kind != svn_node_unknown 1251 || *entry_name != '\0') 1252 return svn_error_trace(err); 1253 svn_error_clear(err); 1254 1255 /* The caller didn't know the node type, we saw a directory there, 1256 we attempted to read IN that directory, and then wc_db reports 1257 that it is NOT a working copy directory. It is possible that 1258 one of two things has happened: 1259 1260 1) a directory is obstructing a file in the parent 1261 2) the (versioned) directory's contents have been removed 1262 1263 Let's assume situation (1); if that is true, then we can just 1264 return the newly-found data. 1265 1266 If we assumed (2), then a valid result still won't help us 1267 since the caller asked for the actual contents, not the stub 1268 (which is why we read *into* the directory). However, if we 1269 assume (1) and get back a stub, then we have verified a 1270 missing, versioned directory, and can return an error 1271 describing that. 1272 1273 Redo the fetch, but "insist" we are trying to find a file. 1274 This will read from the parent directory of the "file". */ 1275 err = svn_wc__get_entry(entry, db, local_abspath, allow_unversioned, 1276 svn_node_file, result_pool, scratch_pool); 1277 if (err == SVN_NO_ERROR) 1278 return SVN_NO_ERROR; 1279 if (err->apr_err != SVN_ERR_NODE_UNEXPECTED_KIND) 1280 return svn_error_trace(err); 1281 svn_error_clear(err); 1282 1283 /* We asked for a FILE, but the node found is a DIR. Thus, we 1284 are looking at a stub. Originally, we tried to read into the 1285 subdir because NEED_PARENT_STUB is FALSE. The stub we just 1286 read is not going to work for the caller, so inform them of 1287 the missing subdirectory. */ 1288 SVN_ERR_ASSERT(*entry != NULL && (*entry)->kind == svn_node_dir); 1289 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, 1290 _("Admin area of '%s' is missing"), 1291 svn_dirent_local_style(local_abspath, 1292 scratch_pool)); 1293 } 1294 } 1295 1296 if (*entry == NULL) 1297 { 1298 if (allow_unversioned) 1299 return SVN_NO_ERROR; 1300 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL, 1301 _("'%s' is not under version control"), 1302 svn_dirent_local_style(local_abspath, 1303 scratch_pool)); 1304 } 1305 1306 /* The caller had the wrong information. */ 1307 if ((kind == svn_node_file && (*entry)->kind != svn_node_file) 1308 || (kind == svn_node_dir && (*entry)->kind != svn_node_dir)) 1309 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL, 1310 _("'%s' is not of the right kind"), 1311 svn_dirent_local_style(local_abspath, 1312 scratch_pool)); 1313 1314 return SVN_NO_ERROR; 1315} 1316 1317/* TODO ### Rewrite doc string to mention ENTRIES_ALL; not ADM_ACCESS. 1318 1319 Prune the deleted entries from the cached entries in ADM_ACCESS, and 1320 return that collection in *ENTRIES_PRUNED. SCRATCH_POOL is used for local, 1321 short term, memory allocation, RESULT_POOL for permanent stuff. */ 1322static svn_error_t * 1323prune_deleted(apr_hash_t **entries_pruned, 1324 apr_hash_t *entries_all, 1325 apr_pool_t *result_pool, 1326 apr_pool_t *scratch_pool) 1327{ 1328 apr_hash_index_t *hi; 1329 1330 if (!entries_all) 1331 { 1332 *entries_pruned = NULL; 1333 return SVN_NO_ERROR; 1334 } 1335 1336 /* I think it will be common for there to be no deleted entries, so 1337 it is worth checking for that case as we can optimise it. */ 1338 for (hi = apr_hash_first(scratch_pool, entries_all); 1339 hi; 1340 hi = apr_hash_next(hi)) 1341 { 1342 svn_boolean_t hidden; 1343 1344 SVN_ERR(svn_wc__entry_is_hidden(&hidden, 1345 svn__apr_hash_index_val(hi))); 1346 if (hidden) 1347 break; 1348 } 1349 1350 if (! hi) 1351 { 1352 /* There are no deleted entries, so we can use the full hash */ 1353 *entries_pruned = entries_all; 1354 return SVN_NO_ERROR; 1355 } 1356 1357 /* Construct pruned hash without deleted entries */ 1358 *entries_pruned = apr_hash_make(result_pool); 1359 for (hi = apr_hash_first(scratch_pool, entries_all); 1360 hi; 1361 hi = apr_hash_next(hi)) 1362 { 1363 const void *key = svn__apr_hash_index_key(hi); 1364 const svn_wc_entry_t *entry = svn__apr_hash_index_val(hi); 1365 svn_boolean_t hidden; 1366 1367 SVN_ERR(svn_wc__entry_is_hidden(&hidden, entry)); 1368 if (!hidden) 1369 svn_hash_sets(*entries_pruned, key, entry); 1370 } 1371 1372 return SVN_NO_ERROR; 1373} 1374 1375struct entries_read_baton_t 1376{ 1377 apr_hash_t **new_entries; 1378 svn_wc__db_t *db; 1379 const char *local_abspath; 1380 apr_pool_t *result_pool; 1381}; 1382 1383static svn_error_t * 1384entries_read_txn(void *baton, svn_sqlite__db_t *db, apr_pool_t *scratch_pool) 1385{ 1386 struct entries_read_baton_t *erb = baton; 1387 1388 SVN_ERR(read_entries(erb->new_entries, erb->db, erb->local_abspath, 1389 erb->result_pool, scratch_pool)); 1390 1391 return NULL; 1392} 1393 1394svn_error_t * 1395svn_wc__entries_read_internal(apr_hash_t **entries, 1396 svn_wc_adm_access_t *adm_access, 1397 svn_boolean_t show_hidden, 1398 apr_pool_t *pool) 1399{ 1400 apr_hash_t *new_entries; 1401 1402 new_entries = svn_wc__adm_access_entries(adm_access); 1403 if (! new_entries) 1404 { 1405 svn_wc__db_t *db = svn_wc__adm_get_db(adm_access); 1406 const char *local_abspath = svn_wc__adm_access_abspath(adm_access); 1407 apr_pool_t *result_pool = svn_wc__adm_access_pool_internal(adm_access); 1408 svn_sqlite__db_t *sdb; 1409 struct entries_read_baton_t erb; 1410 1411 /* ### Use the borrow DB api to handle all calls in a single read 1412 ### transaction. This api is used extensively in our test suite 1413 ### via the entries-read application. */ 1414 1415 SVN_ERR(svn_wc__db_temp_borrow_sdb(&sdb, db, local_abspath, pool)); 1416 1417 erb.db = db; 1418 erb.local_abspath = local_abspath; 1419 erb.new_entries = &new_entries; 1420 erb.result_pool = result_pool; 1421 1422 SVN_ERR(svn_sqlite__with_lock(sdb, entries_read_txn, &erb, pool)); 1423 1424 svn_wc__adm_access_set_entries(adm_access, new_entries); 1425 } 1426 1427 if (show_hidden) 1428 *entries = new_entries; 1429 else 1430 SVN_ERR(prune_deleted(entries, new_entries, 1431 svn_wc__adm_access_pool_internal(adm_access), 1432 pool)); 1433 1434 return SVN_NO_ERROR; 1435} 1436 1437svn_error_t * 1438svn_wc_entries_read(apr_hash_t **entries, 1439 svn_wc_adm_access_t *adm_access, 1440 svn_boolean_t show_hidden, 1441 apr_pool_t *pool) 1442{ 1443 return svn_error_trace(svn_wc__entries_read_internal(entries, adm_access, 1444 show_hidden, pool)); 1445} 1446 1447/* No transaction required: called from write_entry which is itself 1448 transaction-wrapped. */ 1449static svn_error_t * 1450insert_node(svn_sqlite__db_t *sdb, 1451 const db_node_t *node, 1452 apr_pool_t *scratch_pool) 1453{ 1454 svn_sqlite__stmt_t *stmt; 1455 1456 SVN_ERR_ASSERT(node->op_depth > 0 || node->repos_relpath); 1457 1458 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_NODE)); 1459 SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnnnsnrisnnni", 1460 node->wc_id, 1461 node->local_relpath, 1462 node->op_depth, 1463 node->parent_relpath, 1464 /* Setting depth for files? */ 1465 svn_depth_to_word(node->depth), 1466 node->changed_rev, 1467 node->changed_date, 1468 node->changed_author, 1469 node->recorded_time)); 1470 1471 if (node->repos_relpath) 1472 { 1473 SVN_ERR(svn_sqlite__bind_int64(stmt, 5, 1474 node->repos_id)); 1475 SVN_ERR(svn_sqlite__bind_text(stmt, 6, 1476 node->repos_relpath)); 1477 SVN_ERR(svn_sqlite__bind_revnum(stmt, 7, node->revision)); 1478 } 1479 1480 if (node->presence == svn_wc__db_status_normal) 1481 SVN_ERR(svn_sqlite__bind_text(stmt, 8, "normal")); 1482 else if (node->presence == svn_wc__db_status_not_present) 1483 SVN_ERR(svn_sqlite__bind_text(stmt, 8, "not-present")); 1484 else if (node->presence == svn_wc__db_status_base_deleted) 1485 SVN_ERR(svn_sqlite__bind_text(stmt, 8, "base-deleted")); 1486 else if (node->presence == svn_wc__db_status_incomplete) 1487 SVN_ERR(svn_sqlite__bind_text(stmt, 8, "incomplete")); 1488 else if (node->presence == svn_wc__db_status_excluded) 1489 SVN_ERR(svn_sqlite__bind_text(stmt, 8, "excluded")); 1490 else if (node->presence == svn_wc__db_status_server_excluded) 1491 SVN_ERR(svn_sqlite__bind_text(stmt, 8, "server-excluded")); 1492 1493 if (node->kind == svn_node_none) 1494 SVN_ERR(svn_sqlite__bind_text(stmt, 10, "unknown")); 1495 else 1496 SVN_ERR(svn_sqlite__bind_text(stmt, 10, 1497 svn_node_kind_to_word(node->kind))); 1498 1499 if (node->kind == svn_node_file) 1500 { 1501 if (!node->checksum 1502 && node->op_depth == 0 1503 && node->presence != svn_wc__db_status_not_present 1504 && node->presence != svn_wc__db_status_excluded 1505 && node->presence != svn_wc__db_status_server_excluded) 1506 return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL, 1507 _("The file '%s' has no checksum"), 1508 svn_dirent_local_style(node->local_relpath, 1509 scratch_pool)); 1510 1511 SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, node->checksum, 1512 scratch_pool)); 1513 } 1514 1515 if (node->properties) /* ### Never set, props done later */ 1516 SVN_ERR(svn_sqlite__bind_properties(stmt, 15, node->properties, 1517 scratch_pool)); 1518 1519 if (node->recorded_size != SVN_INVALID_FILESIZE) 1520 SVN_ERR(svn_sqlite__bind_int64(stmt, 16, node->recorded_size)); 1521 1522 if (node->file_external) 1523 SVN_ERR(svn_sqlite__bind_int(stmt, 20, 1)); 1524 1525 if (node->inherited_props) 1526 SVN_ERR(svn_sqlite__bind_iprops(stmt, 23, node->inherited_props, 1527 scratch_pool)); 1528 1529 SVN_ERR(svn_sqlite__insert(NULL, stmt)); 1530 1531 return SVN_NO_ERROR; 1532} 1533 1534 1535/* */ 1536static svn_error_t * 1537insert_actual_node(svn_sqlite__db_t *sdb, 1538 svn_wc__db_t *db, 1539 const char *wri_abspath, 1540 const db_actual_node_t *actual_node, 1541 apr_pool_t *scratch_pool) 1542{ 1543 svn_sqlite__stmt_t *stmt; 1544 svn_skel_t *conflict_data = NULL; 1545 1546 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_ACTUAL_NODE)); 1547 1548 SVN_ERR(svn_sqlite__bind_int64(stmt, 1, actual_node->wc_id)); 1549 SVN_ERR(svn_sqlite__bind_text(stmt, 2, actual_node->local_relpath)); 1550 SVN_ERR(svn_sqlite__bind_text(stmt, 3, actual_node->parent_relpath)); 1551 1552 if (actual_node->properties) 1553 SVN_ERR(svn_sqlite__bind_properties(stmt, 4, actual_node->properties, 1554 scratch_pool)); 1555 1556 if (actual_node->changelist) 1557 SVN_ERR(svn_sqlite__bind_text(stmt, 5, actual_node->changelist)); 1558 1559 SVN_ERR(svn_wc__upgrade_conflict_skel_from_raw( 1560 &conflict_data, 1561 db, wri_abspath, 1562 actual_node->local_relpath, 1563 actual_node->conflict_old, 1564 actual_node->conflict_working, 1565 actual_node->conflict_new, 1566 actual_node->prop_reject, 1567 actual_node->tree_conflict_data, 1568 actual_node->tree_conflict_data 1569 ? strlen(actual_node->tree_conflict_data) 1570 : 0, 1571 scratch_pool, scratch_pool)); 1572 1573 if (conflict_data) 1574 { 1575 svn_stringbuf_t *data = svn_skel__unparse(conflict_data, scratch_pool); 1576 1577 SVN_ERR(svn_sqlite__bind_blob(stmt, 6, data->data, data->len)); 1578 } 1579 1580 /* Execute and reset the insert clause. */ 1581 return svn_error_trace(svn_sqlite__insert(NULL, stmt)); 1582} 1583 1584static svn_boolean_t 1585is_switched(db_node_t *parent, 1586 db_node_t *child, 1587 apr_pool_t *scratch_pool) 1588{ 1589 if (parent && child) 1590 { 1591 if (parent->repos_id != child->repos_id) 1592 return TRUE; 1593 1594 if (parent->repos_relpath && child->repos_relpath) 1595 { 1596 const char *unswitched 1597 = svn_relpath_join(parent->repos_relpath, 1598 svn_relpath_basename(child->local_relpath, 1599 scratch_pool), 1600 scratch_pool); 1601 if (strcmp(unswitched, child->repos_relpath)) 1602 return TRUE; 1603 } 1604 } 1605 1606 return FALSE; 1607} 1608 1609struct write_baton { 1610 db_node_t *base; 1611 db_node_t *work; 1612 db_node_t *below_work; 1613 apr_hash_t *tree_conflicts; 1614}; 1615 1616#define WRITE_ENTRY_ASSERT(expr) \ 1617 if (!(expr)) \ 1618 return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL, \ 1619 _("Unable to upgrade '%s' at line %d"), \ 1620 svn_dirent_local_style( \ 1621 svn_dirent_join(root_abspath, \ 1622 local_relpath, \ 1623 scratch_pool), \ 1624 scratch_pool), __LINE__) 1625 1626/* Write the information for ENTRY to WC_DB. The WC_ID, REPOS_ID and 1627 REPOS_ROOT will all be used for writing ENTRY. 1628 ### transitioning from straight sql to using the wc_db APIs. For the 1629 ### time being, we'll need both parameters. */ 1630static svn_error_t * 1631write_entry(struct write_baton **entry_node, 1632 const struct write_baton *parent_node, 1633 svn_wc__db_t *db, 1634 svn_sqlite__db_t *sdb, 1635 apr_int64_t wc_id, 1636 apr_int64_t repos_id, 1637 const svn_wc_entry_t *entry, 1638 const svn_wc__text_base_info_t *text_base_info, 1639 const char *local_relpath, 1640 const char *tmp_entry_abspath, 1641 const char *root_abspath, 1642 const svn_wc_entry_t *this_dir, 1643 svn_boolean_t create_locks, 1644 apr_pool_t *result_pool, 1645 apr_pool_t *scratch_pool) 1646{ 1647 db_node_t *base_node = NULL; 1648 db_node_t *working_node = NULL, *below_working_node = NULL; 1649 db_actual_node_t *actual_node = NULL; 1650 const char *parent_relpath; 1651 apr_hash_t *tree_conflicts; 1652 1653 if (*local_relpath == '\0') 1654 parent_relpath = NULL; 1655 else 1656 parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool); 1657 1658 /* This is how it should work, it doesn't work like this yet because 1659 we need proper op_depth to layer the working nodes. 1660 1661 Using "svn add", "svn rm", "svn cp" only files can be replaced 1662 pre-wcng; directories can only be normal, deleted or added. 1663 Files cannot be replaced within a deleted directory, so replaced 1664 files can only exist in a normal directory, or a directory that 1665 is added+copied. In a normal directory a replaced file needs a 1666 base node and a working node, in an added+copied directory a 1667 replaced file needs two working nodes at different op-depths. 1668 1669 With just the above operations the conversion for files and 1670 directories is straightforward: 1671 1672 pre-wcng wcng 1673 parent child parent child 1674 1675 normal normal base base 1676 add+copied normal+copied work work 1677 normal+copied normal+copied work work 1678 normal delete base base+work 1679 delete delete base+work base+work 1680 add+copied delete work work 1681 normal add base work 1682 add add work work 1683 add+copied add work work 1684 normal add+copied base work 1685 add add+copied work work 1686 add+copied add+copied work work 1687 normal replace base base+work 1688 add+copied replace work work+work 1689 normal replace+copied base base+work 1690 add+copied replace+copied work work+work 1691 1692 However "svn merge" make this more complicated. The pre-wcng 1693 "svn merge" is capable of replacing a directory, that is it can 1694 mark the whole tree deleted, and then copy another tree on top. 1695 The entries then represent the replacing tree overlayed on the 1696 deleted tree. 1697 1698 original replace schedule in 1699 tree tree combined tree 1700 1701 A A replace+copied 1702 A/f delete+copied 1703 A/g A/g replace+copied 1704 A/h add+copied 1705 A/B A/B replace+copied 1706 A/B/f delete+copied 1707 A/B/g A/B/g replace+copied 1708 A/B/h add+copied 1709 A/C delete+copied 1710 A/C/f delete+copied 1711 A/D add+copied 1712 A/D/f add+copied 1713 1714 The original tree could be normal tree, or an add+copied tree. 1715 Committing such a merge generally worked, but making further tree 1716 modifications before commit sometimes failed. 1717 1718 The root of the replace is handled like the file replace: 1719 1720 pre-wcng wcng 1721 parent child parent child 1722 1723 normal replace+copied base base+work 1724 add+copied replace+copied work work+work 1725 1726 although obviously the node is a directory rather then a file. 1727 There are then more conversion states where the parent is 1728 replaced. 1729 1730 pre-wcng wcng 1731 parent child parent child 1732 1733 replace+copied add [base|work]+work work 1734 replace+copied add+copied [base|work]+work work 1735 replace+copied delete+copied [base|work]+work [base|work]+work 1736 delete+copied delete+copied [base|work]+work [base|work]+work 1737 replace+copied replace+copied [base|work]+work [base|work]+work 1738 */ 1739 1740 WRITE_ENTRY_ASSERT(parent_node || entry->schedule == svn_wc_schedule_normal); 1741 1742 WRITE_ENTRY_ASSERT(!parent_node || parent_node->base 1743 || parent_node->below_work || parent_node->work); 1744 1745 switch (entry->schedule) 1746 { 1747 case svn_wc_schedule_normal: 1748 if (entry->copied || 1749 (entry->depth == svn_depth_exclude 1750 && parent_node && !parent_node->base && parent_node->work)) 1751 working_node = MAYBE_ALLOC(working_node, result_pool); 1752 else 1753 base_node = MAYBE_ALLOC(base_node, result_pool); 1754 break; 1755 1756 case svn_wc_schedule_add: 1757 working_node = MAYBE_ALLOC(working_node, result_pool); 1758 if (entry->deleted) 1759 { 1760 if (parent_node->base) 1761 base_node = MAYBE_ALLOC(base_node, result_pool); 1762 else 1763 below_working_node = MAYBE_ALLOC(below_working_node, result_pool); 1764 } 1765 break; 1766 1767 case svn_wc_schedule_delete: 1768 working_node = MAYBE_ALLOC(working_node, result_pool); 1769 if (parent_node->base) 1770 base_node = MAYBE_ALLOC(base_node, result_pool); 1771 if (parent_node->work) 1772 below_working_node = MAYBE_ALLOC(below_working_node, result_pool); 1773 break; 1774 1775 case svn_wc_schedule_replace: 1776 working_node = MAYBE_ALLOC(working_node, result_pool); 1777 if (parent_node->base) 1778 base_node = MAYBE_ALLOC(base_node, result_pool); 1779 else 1780 below_working_node = MAYBE_ALLOC(below_working_node, result_pool); 1781 break; 1782 } 1783 1784 /* Something deleted in this revision means there should always be a 1785 BASE node to indicate the not-present node. */ 1786 if (entry->deleted) 1787 { 1788 WRITE_ENTRY_ASSERT(base_node || below_working_node); 1789 WRITE_ENTRY_ASSERT(!entry->incomplete); 1790 if (base_node) 1791 base_node->presence = svn_wc__db_status_not_present; 1792 else 1793 below_working_node->presence = svn_wc__db_status_not_present; 1794 } 1795 else if (entry->absent) 1796 { 1797 WRITE_ENTRY_ASSERT(base_node && !working_node && !below_working_node); 1798 WRITE_ENTRY_ASSERT(!entry->incomplete); 1799 base_node->presence = svn_wc__db_status_server_excluded; 1800 } 1801 1802 if (entry->copied) 1803 { 1804 if (entry->copyfrom_url) 1805 { 1806 working_node->repos_id = repos_id; 1807 working_node->repos_relpath = svn_uri_skip_ancestor( 1808 this_dir->repos, entry->copyfrom_url, 1809 result_pool); 1810 working_node->revision = entry->copyfrom_rev; 1811 working_node->op_depth 1812 = svn_wc__db_op_depth_for_upgrade(local_relpath); 1813 } 1814 else if (parent_node->work && parent_node->work->repos_relpath) 1815 { 1816 working_node->repos_id = repos_id; 1817 working_node->repos_relpath 1818 = svn_relpath_join(parent_node->work->repos_relpath, 1819 svn_relpath_basename(local_relpath, NULL), 1820 result_pool); 1821 working_node->revision = parent_node->work->revision; 1822 working_node->op_depth = parent_node->work->op_depth; 1823 } 1824 else if (parent_node->below_work 1825 && parent_node->below_work->repos_relpath) 1826 { 1827 working_node->repos_id = repos_id; 1828 working_node->repos_relpath 1829 = svn_relpath_join(parent_node->below_work->repos_relpath, 1830 svn_relpath_basename(local_relpath, NULL), 1831 result_pool); 1832 working_node->revision = parent_node->below_work->revision; 1833 working_node->op_depth = parent_node->below_work->op_depth; 1834 } 1835 else 1836 return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL, 1837 _("No copyfrom URL for '%s'"), 1838 svn_dirent_local_style(local_relpath, 1839 scratch_pool)); 1840 } 1841 1842 if (entry->conflict_old) 1843 { 1844 actual_node = MAYBE_ALLOC(actual_node, scratch_pool); 1845 if (parent_relpath && entry->conflict_old) 1846 actual_node->conflict_old = svn_relpath_join(parent_relpath, 1847 entry->conflict_old, 1848 scratch_pool); 1849 else 1850 actual_node->conflict_old = entry->conflict_old; 1851 if (parent_relpath && entry->conflict_new) 1852 actual_node->conflict_new = svn_relpath_join(parent_relpath, 1853 entry->conflict_new, 1854 scratch_pool); 1855 else 1856 actual_node->conflict_new = entry->conflict_new; 1857 if (parent_relpath && entry->conflict_wrk) 1858 actual_node->conflict_working = svn_relpath_join(parent_relpath, 1859 entry->conflict_wrk, 1860 scratch_pool); 1861 else 1862 actual_node->conflict_working = entry->conflict_wrk; 1863 } 1864 1865 if (entry->prejfile) 1866 { 1867 actual_node = MAYBE_ALLOC(actual_node, scratch_pool); 1868 actual_node->prop_reject = svn_relpath_join((entry->kind == svn_node_dir 1869 ? local_relpath 1870 : parent_relpath), 1871 entry->prejfile, 1872 scratch_pool); 1873 } 1874 1875 if (entry->changelist) 1876 { 1877 actual_node = MAYBE_ALLOC(actual_node, scratch_pool); 1878 actual_node->changelist = entry->changelist; 1879 } 1880 1881 /* ### set the text_mod value? */ 1882 1883 if (entry_node && entry->tree_conflict_data) 1884 { 1885 /* Issues #3840/#3916: 1.6 stores multiple tree conflicts on the 1886 parent node, 1.7 stores them directly on the conflited nodes. 1887 So "((skel1) (skel2))" becomes "(skel1)" and "(skel2)" */ 1888 svn_skel_t *skel; 1889 1890 skel = svn_skel__parse(entry->tree_conflict_data, 1891 strlen(entry->tree_conflict_data), 1892 scratch_pool); 1893 tree_conflicts = apr_hash_make(result_pool); 1894 skel = skel->children; 1895 while(skel) 1896 { 1897 svn_wc_conflict_description2_t *conflict; 1898 svn_skel_t *new_skel; 1899 const char *key; 1900 1901 /* *CONFLICT is allocated so it is safe to use a non-const pointer */ 1902 SVN_ERR(svn_wc__deserialize_conflict( 1903 (const svn_wc_conflict_description2_t**)&conflict, 1904 skel, 1905 svn_dirent_join(root_abspath, 1906 local_relpath, 1907 scratch_pool), 1908 scratch_pool, scratch_pool)); 1909 1910 WRITE_ENTRY_ASSERT(conflict->kind == svn_wc_conflict_kind_tree); 1911 1912 /* Fix dubious data stored by old clients, local adds don't have 1913 a repository URL. */ 1914 if (conflict->reason == svn_wc_conflict_reason_added) 1915 conflict->src_left_version = NULL; 1916 1917 SVN_ERR(svn_wc__serialize_conflict(&new_skel, conflict, 1918 scratch_pool, scratch_pool)); 1919 1920 /* Store in hash to be retrieved when writing the child 1921 row. */ 1922 key = svn_dirent_skip_ancestor(root_abspath, conflict->local_abspath); 1923 svn_hash_sets(tree_conflicts, apr_pstrdup(result_pool, key), 1924 svn_skel__unparse(new_skel, result_pool)->data); 1925 skel = skel->next; 1926 } 1927 } 1928 else 1929 tree_conflicts = NULL; 1930 1931 if (parent_node && parent_node->tree_conflicts) 1932 { 1933 const char *tree_conflict_data = 1934 svn_hash_gets(parent_node->tree_conflicts, local_relpath); 1935 if (tree_conflict_data) 1936 { 1937 actual_node = MAYBE_ALLOC(actual_node, scratch_pool); 1938 actual_node->tree_conflict_data = tree_conflict_data; 1939 } 1940 1941 /* Reset hash so that we don't write the row again when writing 1942 actual-only nodes */ 1943 svn_hash_sets(parent_node->tree_conflicts, local_relpath, NULL); 1944 } 1945 1946 if (entry->file_external_path != NULL) 1947 { 1948 base_node = MAYBE_ALLOC(base_node, result_pool); 1949 } 1950 1951 1952 /* Insert the base node. */ 1953 if (base_node) 1954 { 1955 base_node->wc_id = wc_id; 1956 base_node->local_relpath = local_relpath; 1957 base_node->op_depth = 0; 1958 base_node->parent_relpath = parent_relpath; 1959 base_node->revision = entry->revision; 1960 base_node->recorded_time = entry->text_time; 1961 base_node->recorded_size = entry->working_size; 1962 1963 if (entry->depth != svn_depth_exclude) 1964 base_node->depth = entry->depth; 1965 else 1966 { 1967 base_node->presence = svn_wc__db_status_excluded; 1968 base_node->depth = svn_depth_infinity; 1969 } 1970 1971 if (entry->deleted) 1972 { 1973 WRITE_ENTRY_ASSERT(base_node->presence 1974 == svn_wc__db_status_not_present); 1975 /* ### should be svn_node_unknown, but let's store what we have. */ 1976 base_node->kind = entry->kind; 1977 } 1978 else if (entry->absent) 1979 { 1980 WRITE_ENTRY_ASSERT(base_node->presence 1981 == svn_wc__db_status_server_excluded); 1982 /* ### should be svn_node_unknown, but let's store what we have. */ 1983 base_node->kind = entry->kind; 1984 1985 /* Store the most likely revision in the node to avoid 1986 base nodes without a valid revision. Of course 1987 we remember that the data is still incomplete. */ 1988 if (!SVN_IS_VALID_REVNUM(base_node->revision) && parent_node->base) 1989 base_node->revision = parent_node->base->revision; 1990 } 1991 else 1992 { 1993 base_node->kind = entry->kind; 1994 1995 if (base_node->presence != svn_wc__db_status_excluded) 1996 { 1997 /* All subdirs are initially incomplete, they stop being 1998 incomplete when the entries file in the subdir is 1999 upgraded and remain incomplete if that doesn't happen. */ 2000 if (entry->kind == svn_node_dir 2001 && strcmp(entry->name, SVN_WC_ENTRY_THIS_DIR)) 2002 { 2003 base_node->presence = svn_wc__db_status_incomplete; 2004 2005 /* Store the most likely revision in the node to avoid 2006 base nodes without a valid revision. Of course 2007 we remember that the data is still incomplete. */ 2008 if (parent_node->base) 2009 base_node->revision = parent_node->base->revision; 2010 } 2011 else if (entry->incomplete) 2012 { 2013 /* ### nobody should have set the presence. */ 2014 WRITE_ENTRY_ASSERT(base_node->presence 2015 == svn_wc__db_status_normal); 2016 base_node->presence = svn_wc__db_status_incomplete; 2017 } 2018 } 2019 } 2020 2021 if (entry->kind == svn_node_dir) 2022 base_node->checksum = NULL; 2023 else 2024 { 2025 if (text_base_info && text_base_info->revert_base.sha1_checksum) 2026 base_node->checksum = text_base_info->revert_base.sha1_checksum; 2027 else if (text_base_info && text_base_info->normal_base.sha1_checksum) 2028 base_node->checksum = text_base_info->normal_base.sha1_checksum; 2029 else 2030 base_node->checksum = NULL; 2031 2032 /* The base MD5 checksum is available in the entry, unless there 2033 * is a copied WORKING node. If possible, verify that the entry 2034 * checksum matches the base file that we found. */ 2035 if (! (working_node && entry->copied)) 2036 { 2037 svn_checksum_t *entry_md5_checksum, *found_md5_checksum; 2038 SVN_ERR(svn_checksum_parse_hex(&entry_md5_checksum, 2039 svn_checksum_md5, 2040 entry->checksum, scratch_pool)); 2041 if (text_base_info && text_base_info->revert_base.md5_checksum) 2042 found_md5_checksum = text_base_info->revert_base.md5_checksum; 2043 else if (text_base_info 2044 && text_base_info->normal_base.md5_checksum) 2045 found_md5_checksum = text_base_info->normal_base.md5_checksum; 2046 else 2047 found_md5_checksum = NULL; 2048 if (entry_md5_checksum && found_md5_checksum && 2049 !svn_checksum_match(entry_md5_checksum, found_md5_checksum)) 2050 return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL, 2051 _("Bad base MD5 checksum for '%s'; " 2052 "expected: '%s'; found '%s'; "), 2053 svn_dirent_local_style( 2054 svn_dirent_join(root_abspath, 2055 local_relpath, 2056 scratch_pool), 2057 scratch_pool), 2058 svn_checksum_to_cstring_display( 2059 entry_md5_checksum, scratch_pool), 2060 svn_checksum_to_cstring_display( 2061 found_md5_checksum, scratch_pool)); 2062 else 2063 { 2064 /* ### Not sure what conditions this should cover. */ 2065 /* SVN_ERR_ASSERT(entry->deleted || ...); */ 2066 } 2067 } 2068 } 2069 2070 if (this_dir->repos) 2071 { 2072 base_node->repos_id = repos_id; 2073 2074 if (entry->url != NULL) 2075 { 2076 base_node->repos_relpath = svn_uri_skip_ancestor( 2077 this_dir->repos, entry->url, 2078 result_pool); 2079 } 2080 else 2081 { 2082 const char *relpath = svn_uri_skip_ancestor(this_dir->repos, 2083 this_dir->url, 2084 scratch_pool); 2085 if (relpath == NULL || *relpath == '\0') 2086 base_node->repos_relpath = entry->name; 2087 else 2088 base_node->repos_relpath = 2089 svn_dirent_join(relpath, entry->name, result_pool); 2090 } 2091 } 2092 2093 /* TODO: These values should always be present, if they are missing 2094 during an upgrade, set a flag, and then ask the user to talk to the 2095 server. 2096 2097 Note: cmt_rev is the distinguishing value. The others may be 0 or 2098 NULL if the corresponding revprop has been deleted. */ 2099 base_node->changed_rev = entry->cmt_rev; 2100 base_node->changed_date = entry->cmt_date; 2101 base_node->changed_author = entry->cmt_author; 2102 2103 if (entry->file_external_path) 2104 base_node->file_external = TRUE; 2105 2106 /* Switched nodes get an empty iprops cache. */ 2107 if (parent_node 2108 && is_switched(parent_node->base, base_node, scratch_pool)) 2109 base_node->inherited_props 2110 = apr_array_make(scratch_pool, 0, sizeof(svn_prop_inherited_item_t*)); 2111 2112 SVN_ERR(insert_node(sdb, base_node, scratch_pool)); 2113 2114 /* We have to insert the lock after the base node, because the node 2115 must exist to lookup various bits of repos related information for 2116 the abs path. */ 2117 if (entry->lock_token && create_locks) 2118 { 2119 svn_wc__db_lock_t lock; 2120 2121 lock.token = entry->lock_token; 2122 lock.owner = entry->lock_owner; 2123 lock.comment = entry->lock_comment; 2124 lock.date = entry->lock_creation_date; 2125 2126 SVN_ERR(svn_wc__db_lock_add(db, tmp_entry_abspath, &lock, 2127 scratch_pool)); 2128 } 2129 } 2130 2131 if (below_working_node) 2132 { 2133 db_node_t *work 2134 = parent_node->below_work ? parent_node->below_work : parent_node->work; 2135 2136 below_working_node->wc_id = wc_id; 2137 below_working_node->local_relpath = local_relpath; 2138 below_working_node->op_depth = work->op_depth; 2139 below_working_node->parent_relpath = parent_relpath; 2140 below_working_node->presence = svn_wc__db_status_normal; 2141 below_working_node->kind = entry->kind; 2142 below_working_node->repos_id = work->repos_id; 2143 below_working_node->revision = work->revision; 2144 2145 /* This is just guessing. If the node below would have been switched 2146 or if it was updated to a different version, the guess would 2147 fail. But we don't have better information pre wc-ng :( */ 2148 if (work->repos_relpath) 2149 below_working_node->repos_relpath 2150 = svn_relpath_join(work->repos_relpath, 2151 svn_relpath_basename(local_relpath, NULL), 2152 result_pool); 2153 else 2154 below_working_node->repos_relpath = NULL; 2155 2156 /* The revert_base checksum isn't available in the entry structure, 2157 so the caller provides it. */ 2158 2159 /* text_base_info is NULL for files scheduled to be added. */ 2160 below_working_node->checksum = NULL; 2161 if (text_base_info) 2162 { 2163 if (entry->schedule == svn_wc_schedule_delete) 2164 below_working_node->checksum = 2165 text_base_info->normal_base.sha1_checksum; 2166 else 2167 below_working_node->checksum = 2168 text_base_info->revert_base.sha1_checksum; 2169 } 2170 below_working_node->recorded_size = 0; 2171 below_working_node->changed_rev = SVN_INVALID_REVNUM; 2172 below_working_node->changed_date = 0; 2173 below_working_node->changed_author = NULL; 2174 below_working_node->depth = svn_depth_infinity; 2175 below_working_node->recorded_time = 0; 2176 below_working_node->properties = NULL; 2177 2178 if (working_node 2179 && entry->schedule == svn_wc_schedule_delete 2180 && working_node->repos_relpath) 2181 { 2182 /* We are lucky, our guesses above are not necessary. The known 2183 correct information is in working. But our op_depth design 2184 expects more information here */ 2185 below_working_node->repos_relpath = working_node->repos_relpath; 2186 below_working_node->repos_id = working_node->repos_id; 2187 below_working_node->revision = working_node->revision; 2188 2189 /* Nice for 'svn status' */ 2190 below_working_node->changed_rev = entry->cmt_rev; 2191 below_working_node->changed_date = entry->cmt_date; 2192 below_working_node->changed_author = entry->cmt_author; 2193 2194 /* And now remove it from WORKING, because in wc-ng code 2195 should read it from the lower layer */ 2196 working_node->repos_relpath = NULL; 2197 working_node->repos_id = 0; 2198 working_node->revision = SVN_INVALID_REVNUM; 2199 } 2200 2201 SVN_ERR(insert_node(sdb, below_working_node, scratch_pool)); 2202 } 2203 2204 /* Insert the working node. */ 2205 if (working_node) 2206 { 2207 working_node->wc_id = wc_id; 2208 working_node->local_relpath = local_relpath; 2209 working_node->parent_relpath = parent_relpath; 2210 working_node->changed_rev = SVN_INVALID_REVNUM; 2211 working_node->recorded_time = entry->text_time; 2212 working_node->recorded_size = entry->working_size; 2213 2214 if (entry->depth != svn_depth_exclude) 2215 working_node->depth = entry->depth; 2216 else 2217 { 2218 working_node->presence = svn_wc__db_status_excluded; 2219 working_node->depth = svn_depth_infinity; 2220 } 2221 2222 if (entry->kind == svn_node_dir) 2223 working_node->checksum = NULL; 2224 else 2225 { 2226 working_node->checksum = NULL; 2227 /* text_base_info is NULL for files scheduled to be added. */ 2228 if (text_base_info) 2229 working_node->checksum = text_base_info->normal_base.sha1_checksum; 2230 2231 2232 /* If an MD5 checksum is present in the entry, we can verify that 2233 * it matches the MD5 of the base file we found earlier. */ 2234#ifdef SVN_DEBUG 2235 if (entry->checksum && text_base_info) 2236 { 2237 svn_checksum_t *md5_checksum; 2238 SVN_ERR(svn_checksum_parse_hex(&md5_checksum, svn_checksum_md5, 2239 entry->checksum, result_pool)); 2240 SVN_ERR_ASSERT( 2241 md5_checksum && text_base_info->normal_base.md5_checksum); 2242 SVN_ERR_ASSERT(svn_checksum_match( 2243 md5_checksum, text_base_info->normal_base.md5_checksum)); 2244 } 2245#endif 2246 } 2247 2248 working_node->kind = entry->kind; 2249 if (working_node->presence != svn_wc__db_status_excluded) 2250 { 2251 /* All subdirs start of incomplete, and stop being incomplete 2252 when the entries file in the subdir is upgraded. */ 2253 if (entry->kind == svn_node_dir 2254 && strcmp(entry->name, SVN_WC_ENTRY_THIS_DIR)) 2255 { 2256 working_node->presence = svn_wc__db_status_incomplete; 2257 working_node->kind = svn_node_dir; 2258 } 2259 else if (entry->schedule == svn_wc_schedule_delete) 2260 { 2261 working_node->presence = svn_wc__db_status_base_deleted; 2262 working_node->kind = entry->kind; 2263 } 2264 else 2265 { 2266 /* presence == normal */ 2267 working_node->kind = entry->kind; 2268 2269 if (entry->incomplete) 2270 { 2271 /* We shouldn't be overwriting another status. */ 2272 WRITE_ENTRY_ASSERT(working_node->presence 2273 == svn_wc__db_status_normal); 2274 working_node->presence = svn_wc__db_status_incomplete; 2275 } 2276 } 2277 } 2278 2279 /* These should generally be unset for added and deleted files, 2280 and contain whatever information we have for copied files. Let's 2281 just store whatever we have. 2282 2283 Note: cmt_rev is the distinguishing value. The others may be 0 or 2284 NULL if the corresponding revprop has been deleted. */ 2285 if (working_node->presence != svn_wc__db_status_base_deleted) 2286 { 2287 working_node->changed_rev = entry->cmt_rev; 2288 working_node->changed_date = entry->cmt_date; 2289 working_node->changed_author = entry->cmt_author; 2290 } 2291 2292 if (entry->schedule == svn_wc_schedule_delete 2293 && parent_node->work 2294 && parent_node->work->presence == svn_wc__db_status_base_deleted) 2295 { 2296 working_node->op_depth = parent_node->work->op_depth; 2297 } 2298 else if (!entry->copied) 2299 { 2300 working_node->op_depth 2301 = svn_wc__db_op_depth_for_upgrade(local_relpath); 2302 } 2303 2304 SVN_ERR(insert_node(sdb, working_node, scratch_pool)); 2305 } 2306 2307 /* Insert the actual node. */ 2308 if (actual_node) 2309 { 2310 actual_node = MAYBE_ALLOC(actual_node, scratch_pool); 2311 2312 actual_node->wc_id = wc_id; 2313 actual_node->local_relpath = local_relpath; 2314 actual_node->parent_relpath = parent_relpath; 2315 2316 SVN_ERR(insert_actual_node(sdb, db, tmp_entry_abspath, 2317 actual_node, scratch_pool)); 2318 } 2319 2320 if (entry_node) 2321 { 2322 *entry_node = apr_palloc(result_pool, sizeof(**entry_node)); 2323 (*entry_node)->base = base_node; 2324 (*entry_node)->work = working_node; 2325 (*entry_node)->below_work = below_working_node; 2326 (*entry_node)->tree_conflicts = tree_conflicts; 2327 } 2328 2329 if (entry->file_external_path) 2330 { 2331 /* TODO: Maybe add a file external registration inside EXTERNALS here, 2332 to allow removing file externals that aren't referenced from 2333 svn:externals. 2334 2335 The svn:externals values are processed anyway after everything is 2336 upgraded */ 2337 } 2338 2339 return SVN_NO_ERROR; 2340} 2341 2342static svn_error_t * 2343write_actual_only_entries(apr_hash_t *tree_conflicts, 2344 svn_sqlite__db_t *sdb, 2345 svn_wc__db_t *db, 2346 const char *wri_abspath, 2347 apr_int64_t wc_id, 2348 const char *parent_relpath, 2349 apr_pool_t *scratch_pool) 2350{ 2351 apr_hash_index_t *hi; 2352 2353 for (hi = apr_hash_first(scratch_pool, tree_conflicts); 2354 hi; 2355 hi = apr_hash_next(hi)) 2356 { 2357 db_actual_node_t *actual_node = NULL; 2358 2359 actual_node = MAYBE_ALLOC(actual_node, scratch_pool); 2360 actual_node->wc_id = wc_id; 2361 actual_node->local_relpath = svn__apr_hash_index_key(hi); 2362 actual_node->parent_relpath = parent_relpath; 2363 actual_node->tree_conflict_data = svn__apr_hash_index_val(hi); 2364 2365 SVN_ERR(insert_actual_node(sdb, db, wri_abspath, actual_node, 2366 scratch_pool)); 2367 } 2368 2369 return SVN_NO_ERROR; 2370} 2371 2372svn_error_t * 2373svn_wc__write_upgraded_entries(void **dir_baton, 2374 void *parent_baton, 2375 svn_wc__db_t *db, 2376 svn_sqlite__db_t *sdb, 2377 apr_int64_t repos_id, 2378 apr_int64_t wc_id, 2379 const char *dir_abspath, 2380 const char *new_root_abspath, 2381 apr_hash_t *entries, 2382 apr_hash_t *text_bases_info, 2383 apr_pool_t *result_pool, 2384 apr_pool_t *scratch_pool) 2385{ 2386 const svn_wc_entry_t *this_dir; 2387 apr_hash_index_t *hi; 2388 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 2389 const char *old_root_abspath, *dir_relpath; 2390 struct write_baton *parent_node = parent_baton; 2391 struct write_baton *dir_node; 2392 2393 /* Get a copy of the "this dir" entry for comparison purposes. */ 2394 this_dir = svn_hash_gets(entries, SVN_WC_ENTRY_THIS_DIR); 2395 2396 /* If there is no "this dir" entry, something is wrong. */ 2397 if (! this_dir) 2398 return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL, 2399 _("No default entry in directory '%s'"), 2400 svn_dirent_local_style(dir_abspath, 2401 iterpool)); 2402 old_root_abspath = svn_dirent_get_longest_ancestor(dir_abspath, 2403 new_root_abspath, 2404 scratch_pool); 2405 2406 SVN_ERR_ASSERT(old_root_abspath[0]); 2407 2408 dir_relpath = svn_dirent_skip_ancestor(old_root_abspath, dir_abspath); 2409 2410 /* Write out "this dir" */ 2411 SVN_ERR(write_entry(&dir_node, parent_node, db, sdb, 2412 wc_id, repos_id, this_dir, NULL, dir_relpath, 2413 svn_dirent_join(new_root_abspath, dir_relpath, 2414 iterpool), 2415 old_root_abspath, 2416 this_dir, FALSE, result_pool, iterpool)); 2417 2418 for (hi = apr_hash_first(scratch_pool, entries); hi; 2419 hi = apr_hash_next(hi)) 2420 { 2421 const char *name = svn__apr_hash_index_key(hi); 2422 const svn_wc_entry_t *this_entry = svn__apr_hash_index_val(hi); 2423 const char *child_abspath, *child_relpath; 2424 svn_wc__text_base_info_t *text_base_info 2425 = svn_hash_gets(text_bases_info, name); 2426 2427 svn_pool_clear(iterpool); 2428 2429 /* Don't rewrite the "this dir" entry! */ 2430 if (strcmp(name, SVN_WC_ENTRY_THIS_DIR) == 0) 2431 continue; 2432 2433 /* Write the entry. Pass TRUE for create locks, because we still 2434 use this function for upgrading old working copies. */ 2435 child_abspath = svn_dirent_join(dir_abspath, name, iterpool); 2436 child_relpath = svn_dirent_skip_ancestor(old_root_abspath, child_abspath); 2437 SVN_ERR(write_entry(NULL, dir_node, db, sdb, 2438 wc_id, repos_id, 2439 this_entry, text_base_info, child_relpath, 2440 svn_dirent_join(new_root_abspath, child_relpath, 2441 iterpool), 2442 old_root_abspath, 2443 this_dir, TRUE, iterpool, iterpool)); 2444 } 2445 2446 if (dir_node->tree_conflicts) 2447 SVN_ERR(write_actual_only_entries(dir_node->tree_conflicts, sdb, db, 2448 new_root_abspath, wc_id, dir_relpath, 2449 iterpool)); 2450 2451 *dir_baton = dir_node; 2452 svn_pool_destroy(iterpool); 2453 return SVN_NO_ERROR; 2454} 2455 2456 2457svn_wc_entry_t * 2458svn_wc_entry_dup(const svn_wc_entry_t *entry, apr_pool_t *pool) 2459{ 2460 svn_wc_entry_t *dupentry = apr_palloc(pool, sizeof(*dupentry)); 2461 2462 /* Perform a trivial copy ... */ 2463 *dupentry = *entry; 2464 2465 /* ...and then re-copy stuff that needs to be duped into our pool. */ 2466 if (entry->name) 2467 dupentry->name = apr_pstrdup(pool, entry->name); 2468 if (entry->url) 2469 dupentry->url = apr_pstrdup(pool, entry->url); 2470 if (entry->repos) 2471 dupentry->repos = apr_pstrdup(pool, entry->repos); 2472 if (entry->uuid) 2473 dupentry->uuid = apr_pstrdup(pool, entry->uuid); 2474 if (entry->copyfrom_url) 2475 dupentry->copyfrom_url = apr_pstrdup(pool, entry->copyfrom_url); 2476 if (entry->conflict_old) 2477 dupentry->conflict_old = apr_pstrdup(pool, entry->conflict_old); 2478 if (entry->conflict_new) 2479 dupentry->conflict_new = apr_pstrdup(pool, entry->conflict_new); 2480 if (entry->conflict_wrk) 2481 dupentry->conflict_wrk = apr_pstrdup(pool, entry->conflict_wrk); 2482 if (entry->prejfile) 2483 dupentry->prejfile = apr_pstrdup(pool, entry->prejfile); 2484 if (entry->checksum) 2485 dupentry->checksum = apr_pstrdup(pool, entry->checksum); 2486 if (entry->cmt_author) 2487 dupentry->cmt_author = apr_pstrdup(pool, entry->cmt_author); 2488 if (entry->lock_token) 2489 dupentry->lock_token = apr_pstrdup(pool, entry->lock_token); 2490 if (entry->lock_owner) 2491 dupentry->lock_owner = apr_pstrdup(pool, entry->lock_owner); 2492 if (entry->lock_comment) 2493 dupentry->lock_comment = apr_pstrdup(pool, entry->lock_comment); 2494 if (entry->changelist) 2495 dupentry->changelist = apr_pstrdup(pool, entry->changelist); 2496 2497 /* NOTE: we do not dup cachable_props or present_props since they 2498 are deprecated. Use "" to indicate "nothing cachable or cached". */ 2499 dupentry->cachable_props = ""; 2500 dupentry->present_props = ""; 2501 2502 if (entry->tree_conflict_data) 2503 dupentry->tree_conflict_data = apr_pstrdup(pool, 2504 entry->tree_conflict_data); 2505 if (entry->file_external_path) 2506 dupentry->file_external_path = apr_pstrdup(pool, 2507 entry->file_external_path); 2508 return dupentry; 2509} 2510 2511 2512/*** Generic Entry Walker */ 2513 2514/* A recursive entry-walker, helper for svn_wc_walk_entries3(). 2515 * 2516 * For this directory (DIRPATH, ADM_ACCESS), call the "found_entry" callback 2517 * in WALK_CALLBACKS, passing WALK_BATON to it. Then, for each versioned 2518 * entry in this directory, call the "found entry" callback and then recurse 2519 * (if it is a directory and if DEPTH allows). 2520 * 2521 * If SHOW_HIDDEN is true, include entries that are in a 'deleted' or 2522 * 'absent' state (and not scheduled for re-addition), else skip them. 2523 * 2524 * Call CANCEL_FUNC with CANCEL_BATON to allow cancellation. 2525 */ 2526static svn_error_t * 2527walker_helper(const char *dirpath, 2528 svn_wc_adm_access_t *adm_access, 2529 const svn_wc_entry_callbacks2_t *walk_callbacks, 2530 void *walk_baton, 2531 svn_depth_t depth, 2532 svn_boolean_t show_hidden, 2533 svn_cancel_func_t cancel_func, 2534 void *cancel_baton, 2535 apr_pool_t *pool) 2536{ 2537 apr_pool_t *subpool = svn_pool_create(pool); 2538 apr_hash_t *entries; 2539 apr_hash_index_t *hi; 2540 svn_wc_entry_t *dot_entry; 2541 svn_error_t *err; 2542 svn_wc__db_t *db = svn_wc__adm_get_db(adm_access); 2543 2544 err = svn_wc__entries_read_internal(&entries, adm_access, show_hidden, 2545 pool); 2546 2547 if (err) 2548 SVN_ERR(walk_callbacks->handle_error(dirpath, err, walk_baton, pool)); 2549 2550 /* As promised, always return the '.' entry first. */ 2551 dot_entry = svn_hash_gets(entries, SVN_WC_ENTRY_THIS_DIR); 2552 if (! dot_entry) 2553 return walk_callbacks->handle_error 2554 (dirpath, svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, NULL, 2555 _("Directory '%s' has no THIS_DIR entry"), 2556 svn_dirent_local_style(dirpath, pool)), 2557 walk_baton, pool); 2558 2559 /* Call the "found entry" callback for this directory as a "this dir" 2560 * entry. Note that if this directory has been reached by recursion, this 2561 * is the second visit as it will already have been visited once as a 2562 * child entry of its parent. */ 2563 2564 err = walk_callbacks->found_entry(dirpath, dot_entry, walk_baton, subpool); 2565 2566 2567 if(err) 2568 SVN_ERR(walk_callbacks->handle_error(dirpath, err, walk_baton, pool)); 2569 2570 if (depth == svn_depth_empty) 2571 return SVN_NO_ERROR; 2572 2573 /* Loop over each of the other entries. */ 2574 for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi)) 2575 { 2576 const char *name = svn__apr_hash_index_key(hi); 2577 const svn_wc_entry_t *current_entry = svn__apr_hash_index_val(hi); 2578 const char *entrypath; 2579 const char *entry_abspath; 2580 svn_boolean_t hidden; 2581 2582 svn_pool_clear(subpool); 2583 2584 /* See if someone wants to cancel this operation. */ 2585 if (cancel_func) 2586 SVN_ERR(cancel_func(cancel_baton)); 2587 2588 /* Skip the "this dir" entry. */ 2589 if (strcmp(current_entry->name, SVN_WC_ENTRY_THIS_DIR) == 0) 2590 continue; 2591 2592 entrypath = svn_dirent_join(dirpath, name, subpool); 2593 SVN_ERR(svn_wc__entry_is_hidden(&hidden, current_entry)); 2594 SVN_ERR(svn_dirent_get_absolute(&entry_abspath, entrypath, subpool)); 2595 2596 /* Call the "found entry" callback for this entry. (For a directory, 2597 * this is the first visit: as a child.) */ 2598 if (current_entry->kind == svn_node_file 2599 || depth >= svn_depth_immediates) 2600 { 2601 err = walk_callbacks->found_entry(entrypath, current_entry, 2602 walk_baton, subpool); 2603 2604 if (err) 2605 SVN_ERR(walk_callbacks->handle_error(entrypath, err, 2606 walk_baton, pool)); 2607 } 2608 2609 /* Recurse into this entry if appropriate. */ 2610 if (current_entry->kind == svn_node_dir 2611 && !hidden 2612 && depth >= svn_depth_immediates) 2613 { 2614 svn_wc_adm_access_t *entry_access; 2615 svn_depth_t depth_below_here = depth; 2616 2617 if (depth == svn_depth_immediates) 2618 depth_below_here = svn_depth_empty; 2619 2620 entry_access = svn_wc__adm_retrieve_internal2(db, entry_abspath, 2621 subpool); 2622 2623 if (entry_access) 2624 SVN_ERR(walker_helper(entrypath, entry_access, 2625 walk_callbacks, walk_baton, 2626 depth_below_here, show_hidden, 2627 cancel_func, cancel_baton, 2628 subpool)); 2629 } 2630 } 2631 2632 svn_pool_destroy(subpool); 2633 return SVN_NO_ERROR; 2634} 2635 2636svn_error_t * 2637svn_wc__walker_default_error_handler(const char *path, 2638 svn_error_t *err, 2639 void *walk_baton, 2640 apr_pool_t *pool) 2641{ 2642 /* Note: don't trace this. We don't want to insert a false "stack frame" 2643 onto an error generated elsewhere. */ 2644 return svn_error_trace(err); 2645} 2646 2647 2648/* The public API. */ 2649svn_error_t * 2650svn_wc_walk_entries3(const char *path, 2651 svn_wc_adm_access_t *adm_access, 2652 const svn_wc_entry_callbacks2_t *walk_callbacks, 2653 void *walk_baton, 2654 svn_depth_t walk_depth, 2655 svn_boolean_t show_hidden, 2656 svn_cancel_func_t cancel_func, 2657 void *cancel_baton, 2658 apr_pool_t *pool) 2659{ 2660 const char *local_abspath; 2661 svn_wc__db_t *db = svn_wc__adm_get_db(adm_access); 2662 svn_error_t *err; 2663 svn_node_kind_t kind; 2664 svn_depth_t depth; 2665 2666 SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool)); 2667 err = svn_wc__db_read_info(NULL, &kind, NULL, NULL, NULL, NULL, 2668 NULL, NULL, NULL, &depth, NULL, NULL, 2669 NULL, NULL, NULL, NULL, NULL, NULL, 2670 NULL, NULL, NULL, NULL, NULL, NULL, 2671 NULL, NULL, NULL, 2672 db, local_abspath, 2673 pool, pool); 2674 if (err) 2675 { 2676 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND) 2677 return svn_error_trace(err); 2678 /* Remap into SVN_ERR_UNVERSIONED_RESOURCE. */ 2679 svn_error_clear(err); 2680 return walk_callbacks->handle_error( 2681 path, svn_error_createf(SVN_ERR_UNVERSIONED_RESOURCE, NULL, 2682 _("'%s' is not under version control"), 2683 svn_dirent_local_style(local_abspath, pool)), 2684 walk_baton, pool); 2685 } 2686 2687 if (kind == svn_node_file || depth == svn_depth_exclude) 2688 { 2689 const svn_wc_entry_t *entry; 2690 2691 /* ### we should stop passing out entry structures. 2692 ### 2693 ### we should not call handle_error for an error the *callback* 2694 ### gave us. let it deal with the problem before returning. */ 2695 2696 if (!show_hidden) 2697 { 2698 svn_boolean_t hidden; 2699 SVN_ERR(svn_wc__db_node_hidden(&hidden, db, local_abspath, pool)); 2700 2701 if (hidden) 2702 { 2703 /* The fool asked to walk a "hidden" node. Report the node as 2704 unversioned. 2705 2706 ### this is incorrect behavior. see depth_test 36. the walk 2707 ### API will be revamped to avoid entry structures. we should 2708 ### be able to solve the problem with the new API. (since we 2709 ### shouldn't return a hidden entry here) */ 2710 return walk_callbacks->handle_error( 2711 path, svn_error_createf( 2712 SVN_ERR_UNVERSIONED_RESOURCE, NULL, 2713 _("'%s' is not under version control"), 2714 svn_dirent_local_style(local_abspath, pool)), 2715 walk_baton, pool); 2716 } 2717 } 2718 2719 SVN_ERR(svn_wc__get_entry(&entry, db, local_abspath, FALSE, 2720 svn_node_file, pool, pool)); 2721 2722 err = walk_callbacks->found_entry(path, entry, walk_baton, pool); 2723 if (err) 2724 return walk_callbacks->handle_error(path, err, walk_baton, pool); 2725 2726 return SVN_NO_ERROR; 2727 } 2728 2729 if (kind == svn_node_dir) 2730 return walker_helper(path, adm_access, walk_callbacks, walk_baton, 2731 walk_depth, show_hidden, cancel_func, cancel_baton, 2732 pool); 2733 2734 return walk_callbacks->handle_error( 2735 path, svn_error_createf(SVN_ERR_NODE_UNKNOWN_KIND, NULL, 2736 _("'%s' has an unrecognized node kind"), 2737 svn_dirent_local_style(local_abspath, pool)), 2738 walk_baton, pool); 2739} 2740