| /* |
| * wcroot_anchor.c : wcroot and anchor functions |
| * |
| * ==================================================================== |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * ==================================================================== |
| */ |
| |
| |
| |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include "svn_types.h" |
| #include "svn_pools.h" |
| #include "svn_string.h" |
| #include "svn_dirent_uri.h" |
| #include "svn_error.h" |
| #include "svn_io.h" |
| #include "svn_private_config.h" |
| |
| #include "wc.h" |
| |
| #include "private/svn_wc_private.h" |
| |
| /* ABOUT ANCHOR AND TARGET, AND svn_wc_get_actual_target2() |
| |
| THE GOAL |
| |
| Note the following actions, where X is the thing we wish to update, |
| P is a directory whose repository URL is the parent of |
| X's repository URL, N is directory whose repository URL is *not* |
| the parent directory of X (including the case where N is not a |
| versioned resource at all): |
| |
| 1. `svn up .' from inside X. |
| 2. `svn up ...P/X' from anywhere. |
| 3. `svn up ...N/X' from anywhere. |
| |
| For the purposes of the discussion, in the '...N/X' situation, X is |
| said to be a "working copy (WC) root" directory. |
| |
| Now consider the four cases for X's type (file/dir) in the working |
| copy vs. the repository: |
| |
| A. dir in working copy, dir in repos. |
| B. dir in working copy, file in repos. |
| C. file in working copy, dir in repos. |
| D. file in working copy, file in repos. |
| |
| Here are the results we expect for each combination of the above: |
| |
| 1A. Successfully update X. |
| 1B. Error (you don't want to remove your current working |
| directory out from underneath the application). |
| 1C. N/A (you can't be "inside X" if X is a file). |
| 1D. N/A (you can't be "inside X" if X is a file). |
| |
| 2A. Successfully update X. |
| 2B. Successfully update X. |
| 2C. Successfully update X. |
| 2D. Successfully update X. |
| |
| 3A. Successfully update X. |
| 3B. Error (you can't create a versioned file X inside a |
| non-versioned directory). |
| 3C. N/A (you can't have a versioned file X in directory that is |
| not its repository parent). |
| 3D. N/A (you can't have a versioned file X in directory that is |
| not its repository parent). |
| |
| To summarize, case 2 always succeeds, and cases 1 and 3 always fail |
| (or can't occur) *except* when the target is a dir that remains a |
| dir after the update. |
| |
| ACCOMPLISHING THE GOAL |
| |
| Updates are accomplished by driving an editor, and an editor is |
| "rooted" on a directory. So, in order to update a file, we need to |
| break off the basename of the file, rooting the editor in that |
| file's parent directory, and then updating only that file, not the |
| other stuff in its parent directory. |
| |
| Secondly, we look at the case where we wish to update a directory. |
| This is typically trivial. However, one problematic case, exists |
| when we wish to update a directory that has been removed from the |
| repository and replaced with a file of the same name. If we root |
| our edit at the initial directory, there is no editor mechanism for |
| deleting that directory and replacing it with a file (this would be |
| like having an editor now anchored on a file, which is disallowed). |
| |
| All that remains is to have a function with the knowledge required |
| to properly decide where to root our editor, and what to act upon |
| with that now-rooted editor. Given a path to be updated, this |
| function should conditionally split that path into an "anchor" and |
| a "target", where the "anchor" is the directory at which the update |
| editor is rooted (meaning, editor->open_root() is called with |
| this directory in mind), and the "target" is the actual intended |
| subject of the update. |
| |
| svn_wc_get_actual_target2() is that function. |
| |
| So, what are the conditions? |
| |
| Case I: Any time X is '.' (implying it is a directory), we won't |
| lop off a basename. So we'll root our editor at X, and update all |
| of X. |
| |
| Cases II & III: Any time we are trying to update some path ...N/X, |
| we again will not lop off a basename. We can't root an editor at |
| ...N with X as a target, either because ...N isn't a versioned |
| resource at all (Case II) or because X is X is not a child of ...N |
| in the repository (Case III). We root at X, and update X. |
| |
| Cases IV-???: We lop off a basename when we are updating a |
| path ...P/X, rooting our editor at ...P and updating X, or when X |
| is missing from disk. |
| |
| These conditions apply whether X is a file or directory. |
| |
| --- |
| |
| As it turns out, commits need to have a similar check in place, |
| too, specifically for the case where a single directory is being |
| committed (we have to anchor at that directory's parent in case the |
| directory itself needs to be modified). |
| */ |
| |
| |
| 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) |
| { |
| SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath)); |
| |
| return svn_error_trace(svn_wc__db_is_switched(is_wcroot,is_switched, kind, |
| wc_ctx->db, local_abspath, |
| scratch_pool)); |
| } |
| |
| 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) |
| { |
| return svn_error_trace(svn_wc__db_is_wcroot(is_wcroot, |
| wc_ctx->db, |
| local_abspath, |
| scratch_pool)); |
| } |
| |
| |
| 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) |
| { |
| return svn_wc__db_get_wcroot(wcroot_abspath, wc_ctx->db, |
| local_abspath, result_pool, scratch_pool); |
| } |
| |
| |
| 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) |
| { |
| svn_boolean_t is_wc_root, is_switched; |
| svn_node_kind_t kind; |
| const char *local_abspath; |
| svn_error_t *err; |
| |
| SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool)); |
| |
| err = svn_wc__db_is_switched(&is_wc_root, &is_switched, &kind, |
| wc_ctx->db, local_abspath, |
| scratch_pool); |
| |
| if (err) |
| { |
| if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND && |
| err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY) |
| return svn_error_trace(err); |
| |
| svn_error_clear(err); |
| is_wc_root = FALSE; |
| is_switched = FALSE; |
| } |
| |
| /* If PATH is not a WC root, or if it is a file, lop off a basename. */ |
| if (!(is_wc_root || is_switched) || (kind != svn_node_dir)) |
| { |
| svn_dirent_split(anchor, target, path, result_pool); |
| } |
| else |
| { |
| *anchor = apr_pstrdup(result_pool, path); |
| *target = ""; |
| } |
| |
| return SVN_NO_ERROR; |
| } |