1251881Speter/*
2251881Speter * wcroot_anchor.c :  wcroot and anchor functions
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter
25251881Speter
26251881Speter#include <stdlib.h>
27251881Speter#include <string.h>
28251881Speter
29251881Speter#include "svn_types.h"
30251881Speter#include "svn_pools.h"
31251881Speter#include "svn_string.h"
32251881Speter#include "svn_dirent_uri.h"
33251881Speter#include "svn_error.h"
34251881Speter#include "svn_io.h"
35251881Speter#include "svn_private_config.h"
36251881Speter
37251881Speter#include "wc.h"
38251881Speter
39251881Speter#include "private/svn_wc_private.h"
40251881Speter
41251881Speter/* ABOUT ANCHOR AND TARGET, AND svn_wc_get_actual_target2()
42251881Speter
43251881Speter   THE GOAL
44251881Speter
45251881Speter   Note the following actions, where X is the thing we wish to update,
46251881Speter   P is a directory whose repository URL is the parent of
47251881Speter   X's repository URL, N is directory whose repository URL is *not*
48251881Speter   the parent directory of X (including the case where N is not a
49251881Speter   versioned resource at all):
50251881Speter
51251881Speter      1.  `svn up .' from inside X.
52251881Speter      2.  `svn up ...P/X' from anywhere.
53251881Speter      3.  `svn up ...N/X' from anywhere.
54251881Speter
55251881Speter   For the purposes of the discussion, in the '...N/X' situation, X is
56251881Speter   said to be a "working copy (WC) root" directory.
57251881Speter
58251881Speter   Now consider the four cases for X's type (file/dir) in the working
59251881Speter   copy vs. the repository:
60251881Speter
61251881Speter      A.  dir in working copy, dir in repos.
62251881Speter      B.  dir in working copy, file in repos.
63251881Speter      C.  file in working copy, dir in repos.
64251881Speter      D.  file in working copy, file in repos.
65251881Speter
66251881Speter   Here are the results we expect for each combination of the above:
67251881Speter
68251881Speter      1A. Successfully update X.
69251881Speter      1B. Error (you don't want to remove your current working
70251881Speter          directory out from underneath the application).
71251881Speter      1C. N/A (you can't be "inside X" if X is a file).
72251881Speter      1D. N/A (you can't be "inside X" if X is a file).
73251881Speter
74251881Speter      2A. Successfully update X.
75251881Speter      2B. Successfully update X.
76251881Speter      2C. Successfully update X.
77251881Speter      2D. Successfully update X.
78251881Speter
79251881Speter      3A. Successfully update X.
80251881Speter      3B. Error (you can't create a versioned file X inside a
81251881Speter          non-versioned directory).
82251881Speter      3C. N/A (you can't have a versioned file X in directory that is
83251881Speter          not its repository parent).
84251881Speter      3D. N/A (you can't have a versioned file X in directory that is
85251881Speter          not its repository parent).
86251881Speter
87251881Speter   To summarize, case 2 always succeeds, and cases 1 and 3 always fail
88251881Speter   (or can't occur) *except* when the target is a dir that remains a
89251881Speter   dir after the update.
90251881Speter
91251881Speter   ACCOMPLISHING THE GOAL
92251881Speter
93251881Speter   Updates are accomplished by driving an editor, and an editor is
94251881Speter   "rooted" on a directory.  So, in order to update a file, we need to
95251881Speter   break off the basename of the file, rooting the editor in that
96251881Speter   file's parent directory, and then updating only that file, not the
97251881Speter   other stuff in its parent directory.
98251881Speter
99251881Speter   Secondly, we look at the case where we wish to update a directory.
100251881Speter   This is typically trivial.  However, one problematic case, exists
101251881Speter   when we wish to update a directory that has been removed from the
102251881Speter   repository and replaced with a file of the same name.  If we root
103251881Speter   our edit at the initial directory, there is no editor mechanism for
104251881Speter   deleting that directory and replacing it with a file (this would be
105251881Speter   like having an editor now anchored on a file, which is disallowed).
106251881Speter
107251881Speter   All that remains is to have a function with the knowledge required
108251881Speter   to properly decide where to root our editor, and what to act upon
109251881Speter   with that now-rooted editor.  Given a path to be updated, this
110251881Speter   function should conditionally split that path into an "anchor" and
111251881Speter   a "target", where the "anchor" is the directory at which the update
112251881Speter   editor is rooted (meaning, editor->open_root() is called with
113251881Speter   this directory in mind), and the "target" is the actual intended
114251881Speter   subject of the update.
115251881Speter
116251881Speter   svn_wc_get_actual_target2() is that function.
117251881Speter
118251881Speter   So, what are the conditions?
119251881Speter
120251881Speter   Case I: Any time X is '.' (implying it is a directory), we won't
121251881Speter   lop off a basename.  So we'll root our editor at X, and update all
122251881Speter   of X.
123251881Speter
124251881Speter   Cases II & III: Any time we are trying to update some path ...N/X,
125251881Speter   we again will not lop off a basename.  We can't root an editor at
126251881Speter   ...N with X as a target, either because ...N isn't a versioned
127251881Speter   resource at all (Case II) or because X is X is not a child of ...N
128251881Speter   in the repository (Case III).  We root at X, and update X.
129251881Speter
130251881Speter   Cases IV-???: We lop off a basename when we are updating a
131251881Speter   path ...P/X, rooting our editor at ...P and updating X, or when X
132251881Speter   is missing from disk.
133251881Speter
134251881Speter   These conditions apply whether X is a file or directory.
135251881Speter
136251881Speter   ---
137251881Speter
138251881Speter   As it turns out, commits need to have a similar check in place,
139251881Speter   too, specifically for the case where a single directory is being
140251881Speter   committed (we have to anchor at that directory's parent in case the
141251881Speter   directory itself needs to be modified).
142251881Speter*/
143251881Speter
144251881Speter
145251881Spetersvn_error_t *
146251881Spetersvn_wc_check_root(svn_boolean_t *is_wcroot,
147251881Speter                  svn_boolean_t *is_switched,
148251881Speter                  svn_node_kind_t *kind,
149251881Speter                  svn_wc_context_t *wc_ctx,
150251881Speter                  const char *local_abspath,
151251881Speter                  apr_pool_t *scratch_pool)
152251881Speter{
153251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
154251881Speter
155251881Speter  return svn_error_trace(svn_wc__db_is_switched(is_wcroot,is_switched, kind,
156251881Speter                                                wc_ctx->db, local_abspath,
157251881Speter                                                scratch_pool));
158251881Speter}
159251881Speter
160251881Spetersvn_error_t *
161251881Spetersvn_wc__is_wcroot(svn_boolean_t *is_wcroot,
162251881Speter                  svn_wc_context_t *wc_ctx,
163251881Speter                  const char *local_abspath,
164251881Speter                  apr_pool_t *scratch_pool)
165251881Speter{
166251881Speter  return svn_error_trace(svn_wc__db_is_wcroot(is_wcroot,
167251881Speter                                              wc_ctx->db,
168251881Speter                                              local_abspath,
169251881Speter                                              scratch_pool));
170251881Speter}
171251881Speter
172251881Speter
173251881Spetersvn_error_t *
174251881Spetersvn_wc__get_wcroot(const char **wcroot_abspath,
175251881Speter                   svn_wc_context_t *wc_ctx,
176251881Speter                   const char *local_abspath,
177251881Speter                   apr_pool_t *result_pool,
178251881Speter                   apr_pool_t *scratch_pool)
179251881Speter{
180251881Speter  return svn_wc__db_get_wcroot(wcroot_abspath, wc_ctx->db,
181251881Speter                               local_abspath, result_pool, scratch_pool);
182251881Speter}
183251881Speter
184251881Speter
185251881Spetersvn_error_t *
186251881Spetersvn_wc_get_actual_target2(const char **anchor,
187251881Speter                          const char **target,
188251881Speter                          svn_wc_context_t *wc_ctx,
189251881Speter                          const char *path,
190251881Speter                          apr_pool_t *result_pool,
191251881Speter                          apr_pool_t *scratch_pool)
192251881Speter{
193251881Speter  svn_boolean_t is_wc_root, is_switched;
194251881Speter  svn_node_kind_t kind;
195251881Speter  const char *local_abspath;
196251881Speter  svn_error_t *err;
197251881Speter
198251881Speter  SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool));
199251881Speter
200251881Speter  err = svn_wc__db_is_switched(&is_wc_root, &is_switched, &kind,
201251881Speter                               wc_ctx->db, local_abspath,
202251881Speter                               scratch_pool);
203251881Speter
204251881Speter  if (err)
205251881Speter    {
206251881Speter      if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND &&
207251881Speter          err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY)
208251881Speter        return svn_error_trace(err);
209251881Speter
210251881Speter      svn_error_clear(err);
211251881Speter      is_wc_root = FALSE;
212251881Speter      is_switched = FALSE;
213251881Speter    }
214251881Speter
215251881Speter  /* If PATH is not a WC root, or if it is a file, lop off a basename. */
216251881Speter  if (!(is_wc_root || is_switched) || (kind != svn_node_dir))
217251881Speter    {
218251881Speter      svn_dirent_split(anchor, target, path, result_pool);
219251881Speter    }
220251881Speter  else
221251881Speter    {
222251881Speter      *anchor = apr_pstrdup(result_pool, path);
223251881Speter      *target = "";
224251881Speter    }
225251881Speter
226251881Speter  return SVN_NO_ERROR;
227251881Speter}
228