1/* 2 * wcroot_anchor.c : wcroot and anchor functions 3 * 4 * ==================================================================== 5 * Licensed to the Apache Software Foundation (ASF) under one 6 * or more contributor license agreements. See the NOTICE file 7 * distributed with this work for additional information 8 * regarding copyright ownership. The ASF licenses this file 9 * to you under the Apache License, Version 2.0 (the 10 * "License"); you may not use this file except in compliance 11 * with the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, 16 * software distributed under the License is distributed on an 17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 * KIND, either express or implied. See the License for the 19 * specific language governing permissions and limitations 20 * under the License. 21 * ==================================================================== 22 */ 23 24 25 26#include <stdlib.h> 27#include <string.h> 28 29#include "svn_types.h" 30#include "svn_pools.h" 31#include "svn_string.h" 32#include "svn_dirent_uri.h" 33#include "svn_error.h" 34#include "svn_io.h" 35#include "svn_private_config.h" 36 37#include "wc.h" 38 39#include "private/svn_wc_private.h" 40 41/* ABOUT ANCHOR AND TARGET, AND svn_wc_get_actual_target2() 42 43 THE GOAL 44 45 Note the following actions, where X is the thing we wish to update, 46 P is a directory whose repository URL is the parent of 47 X's repository URL, N is directory whose repository URL is *not* 48 the parent directory of X (including the case where N is not a 49 versioned resource at all): 50 51 1. `svn up .' from inside X. 52 2. `svn up ...P/X' from anywhere. 53 3. `svn up ...N/X' from anywhere. 54 55 For the purposes of the discussion, in the '...N/X' situation, X is 56 said to be a "working copy (WC) root" directory. 57 58 Now consider the four cases for X's type (file/dir) in the working 59 copy vs. the repository: 60 61 A. dir in working copy, dir in repos. 62 B. dir in working copy, file in repos. 63 C. file in working copy, dir in repos. 64 D. file in working copy, file in repos. 65 66 Here are the results we expect for each combination of the above: 67 68 1A. Successfully update X. 69 1B. Error (you don't want to remove your current working 70 directory out from underneath the application). 71 1C. N/A (you can't be "inside X" if X is a file). 72 1D. N/A (you can't be "inside X" if X is a file). 73 74 2A. Successfully update X. 75 2B. Successfully update X. 76 2C. Successfully update X. 77 2D. Successfully update X. 78 79 3A. Successfully update X. 80 3B. Error (you can't create a versioned file X inside a 81 non-versioned directory). 82 3C. N/A (you can't have a versioned file X in directory that is 83 not its repository parent). 84 3D. N/A (you can't have a versioned file X in directory that is 85 not its repository parent). 86 87 To summarize, case 2 always succeeds, and cases 1 and 3 always fail 88 (or can't occur) *except* when the target is a dir that remains a 89 dir after the update. 90 91 ACCOMPLISHING THE GOAL 92 93 Updates are accomplished by driving an editor, and an editor is 94 "rooted" on a directory. So, in order to update a file, we need to 95 break off the basename of the file, rooting the editor in that 96 file's parent directory, and then updating only that file, not the 97 other stuff in its parent directory. 98 99 Secondly, we look at the case where we wish to update a directory. 100 This is typically trivial. However, one problematic case, exists 101 when we wish to update a directory that has been removed from the 102 repository and replaced with a file of the same name. If we root 103 our edit at the initial directory, there is no editor mechanism for 104 deleting that directory and replacing it with a file (this would be 105 like having an editor now anchored on a file, which is disallowed). 106 107 All that remains is to have a function with the knowledge required 108 to properly decide where to root our editor, and what to act upon 109 with that now-rooted editor. Given a path to be updated, this 110 function should conditionally split that path into an "anchor" and 111 a "target", where the "anchor" is the directory at which the update 112 editor is rooted (meaning, editor->open_root() is called with 113 this directory in mind), and the "target" is the actual intended 114 subject of the update. 115 116 svn_wc_get_actual_target2() is that function. 117 118 So, what are the conditions? 119 120 Case I: Any time X is '.' (implying it is a directory), we won't 121 lop off a basename. So we'll root our editor at X, and update all 122 of X. 123 124 Cases II & III: Any time we are trying to update some path ...N/X, 125 we again will not lop off a basename. We can't root an editor at 126 ...N with X as a target, either because ...N isn't a versioned 127 resource at all (Case II) or because X is X is not a child of ...N 128 in the repository (Case III). We root at X, and update X. 129 130 Cases IV-???: We lop off a basename when we are updating a 131 path ...P/X, rooting our editor at ...P and updating X, or when X 132 is missing from disk. 133 134 These conditions apply whether X is a file or directory. 135 136 --- 137 138 As it turns out, commits need to have a similar check in place, 139 too, specifically for the case where a single directory is being 140 committed (we have to anchor at that directory's parent in case the 141 directory itself needs to be modified). 142*/ 143 144 145svn_error_t * 146svn_wc_check_root(svn_boolean_t *is_wcroot, 147 svn_boolean_t *is_switched, 148 svn_node_kind_t *kind, 149 svn_wc_context_t *wc_ctx, 150 const char *local_abspath, 151 apr_pool_t *scratch_pool) 152{ 153 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); 154 155 return svn_error_trace(svn_wc__db_is_switched(is_wcroot,is_switched, kind, 156 wc_ctx->db, local_abspath, 157 scratch_pool)); 158} 159 160svn_error_t * 161svn_wc__is_wcroot(svn_boolean_t *is_wcroot, 162 svn_wc_context_t *wc_ctx, 163 const char *local_abspath, 164 apr_pool_t *scratch_pool) 165{ 166 return svn_error_trace(svn_wc__db_is_wcroot(is_wcroot, 167 wc_ctx->db, 168 local_abspath, 169 scratch_pool)); 170} 171 172 173svn_error_t * 174svn_wc__get_wcroot(const char **wcroot_abspath, 175 svn_wc_context_t *wc_ctx, 176 const char *local_abspath, 177 apr_pool_t *result_pool, 178 apr_pool_t *scratch_pool) 179{ 180 return svn_wc__db_get_wcroot(wcroot_abspath, wc_ctx->db, 181 local_abspath, result_pool, scratch_pool); 182} 183 184 185svn_error_t * 186svn_wc_get_actual_target2(const char **anchor, 187 const char **target, 188 svn_wc_context_t *wc_ctx, 189 const char *path, 190 apr_pool_t *result_pool, 191 apr_pool_t *scratch_pool) 192{ 193 svn_boolean_t is_wc_root, is_switched; 194 svn_node_kind_t kind; 195 const char *local_abspath; 196 svn_error_t *err; 197 198 SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool)); 199 200 err = svn_wc__db_is_switched(&is_wc_root, &is_switched, &kind, 201 wc_ctx->db, local_abspath, 202 scratch_pool); 203 204 if (err) 205 { 206 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND && 207 err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY) 208 return svn_error_trace(err); 209 210 svn_error_clear(err); 211 is_wc_root = FALSE; 212 is_switched = FALSE; 213 } 214 215 /* If PATH is not a WC root, or if it is a file, lop off a basename. */ 216 if (!(is_wc_root || is_switched) || (kind != svn_node_dir)) 217 { 218 svn_dirent_split(anchor, target, path, result_pool); 219 } 220 else 221 { 222 *anchor = apr_pstrdup(result_pool, path); 223 *target = ""; 224 } 225 226 return SVN_NO_ERROR; 227} 228