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