2 * wcroot_anchor.c : wcroot and anchor functions
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
13 * http://www.apache.org/licenses/LICENSE-2.0
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
21 * ====================================================================
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"
35 #include "svn_private_config.h"
39 #include "private/svn_wc_private.h"
41 /* ABOUT ANCHOR AND TARGET, AND svn_wc_get_actual_target2()
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):
51 1. `svn up .' from inside X.
52 2. `svn up ...P/X' from anywhere.
53 3. `svn up ...N/X' from anywhere.
55 For the purposes of the discussion, in the '...N/X' situation, X is
56 said to be a "working copy (WC) root" directory.
58 Now consider the four cases for X's type (file/dir) in the working
59 copy vs. the repository:
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.
66 Here are the results we expect for each combination of the above:
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).
74 2A. Successfully update X.
75 2B. Successfully update X.
76 2C. Successfully update X.
77 2D. Successfully update X.
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).
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
91 ACCOMPLISHING THE GOAL
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.
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).
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.
116 svn_wc_get_actual_target2() is that function.
118 So, what are the conditions?
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
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.
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.
134 These conditions apply whether X is a file or directory.
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).
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)
153 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
155 return svn_error_trace(svn_wc__db_is_switched(is_wcroot,is_switched, kind,
156 wc_ctx->db, local_abspath,
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)
166 return svn_error_trace(svn_wc__db_is_wcroot(is_wcroot,
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)
180 return svn_wc__db_get_wcroot(wcroot_abspath, wc_ctx->db,
181 local_abspath, result_pool, scratch_pool);
186 svn_wc__get_shelves_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)
192 const char *wcroot_abspath;
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, ".svn/shelves", result_pool);
198 /* Ensure the directory exists. (Other versions of svn don't create it.) */
199 SVN_ERR(svn_io_make_dir_recursively(*dir, scratch_pool));
206 svn_wc_get_actual_target2(const char **anchor,
208 svn_wc_context_t *wc_ctx,
210 apr_pool_t *result_pool,
211 apr_pool_t *scratch_pool)
213 svn_boolean_t is_wc_root, is_switched;
214 svn_node_kind_t kind;
215 const char *local_abspath;
218 SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool));
220 err = svn_wc__db_is_switched(&is_wc_root, &is_switched, &kind,
221 wc_ctx->db, local_abspath,
226 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND &&
227 err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY)
228 return svn_error_trace(err);
230 svn_error_clear(err);
235 /* If PATH is not a WC root, or if it is a file, lop off a basename. */
236 if (!(is_wc_root || is_switched) || (kind != svn_node_dir))
238 svn_dirent_split(anchor, target, path, result_pool);
242 *anchor = apr_pstrdup(result_pool, path);