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
145 svn_error_t *
svn_wc_check_root(svn_boolean_t * is_wcroot,svn_boolean_t * is_switched,svn_node_kind_t * kind,svn_wc_context_t * wc_ctx,const char * local_abspath,apr_pool_t * scratch_pool)146 svn_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
160 svn_error_t *
svn_wc__is_wcroot(svn_boolean_t * is_wcroot,svn_wc_context_t * wc_ctx,const char * local_abspath,apr_pool_t * scratch_pool)161 svn_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
173 svn_error_t *
svn_wc__get_wcroot(const char ** wcroot_abspath,svn_wc_context_t * wc_ctx,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)174 svn_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
185 svn_error_t *
svn_wc__get_experimental_dir(char ** dir,svn_wc_context_t * wc_ctx,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)186 svn_wc__get_experimental_dir(char **dir,
187 svn_wc_context_t *wc_ctx,
188 const char *local_abspath,
189 apr_pool_t *result_pool,
190 apr_pool_t *scratch_pool)
191 {
192 const char *wcroot_abspath;
193
194 SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath, wc_ctx, local_abspath,
195 scratch_pool, scratch_pool));
196 *dir = svn_dirent_join(wcroot_abspath,
197 SVN_WC_ADM_DIR_NAME "/" SVN_WC__ADM_EXPERIMENTAL,
198 result_pool);
199
200 /* Ensure the directory exists. (Other versions of svn don't create it.) */
201 SVN_ERR(svn_io_make_dir_recursively(*dir, scratch_pool));
202
203 return SVN_NO_ERROR;
204 }
205
206
207 svn_error_t *
svn_wc_get_actual_target2(const char ** anchor,const char ** target,svn_wc_context_t * wc_ctx,const char * path,apr_pool_t * result_pool,apr_pool_t * scratch_pool)208 svn_wc_get_actual_target2(const char **anchor,
209 const char **target,
210 svn_wc_context_t *wc_ctx,
211 const char *path,
212 apr_pool_t *result_pool,
213 apr_pool_t *scratch_pool)
214 {
215 svn_boolean_t is_wc_root, is_switched;
216 svn_node_kind_t kind;
217 const char *local_abspath;
218 svn_error_t *err;
219
220 SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool));
221
222 err = svn_wc__db_is_switched(&is_wc_root, &is_switched, &kind,
223 wc_ctx->db, local_abspath,
224 scratch_pool);
225
226 if (err)
227 {
228 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND &&
229 err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY)
230 return svn_error_trace(err);
231
232 svn_error_clear(err);
233 is_wc_root = FALSE;
234 is_switched = FALSE;
235 }
236
237 /* If PATH is not a WC root, or if it is a file, lop off a basename. */
238 if (!(is_wc_root || is_switched) || (kind != svn_node_dir))
239 {
240 svn_dirent_split(anchor, target, path, result_pool);
241 }
242 else
243 {
244 *anchor = apr_pstrdup(result_pool, path);
245 *target = "";
246 }
247
248 return SVN_NO_ERROR;
249 }
250