xref: /freebsd-11-stable/contrib/subversion/subversion/libsvn_wc/wcroot_anchor.c (revision 3c9339f7792540596bf97077a8f403e944af7f39)
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