You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by hw...@apache.org on 2010/09/15 21:32:38 UTC
svn commit: r997472 [25/41] - in /subversion/branches/py-tests-as-modules:
./ build/ build/ac-macros/ build/generator/ build/generator/templates/
contrib/server-side/ notes/ notes/tree-conflicts/ notes/wc-ng/
subversion/bindings/javahl/native/ subversi...
Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_wc/update_editor.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_wc/update_editor.c?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_wc/update_editor.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_wc/update_editor.c Wed Sep 15 19:32:26 2010
@@ -30,7 +30,6 @@
#include <apr_hash.h>
#include <apr_md5.h>
#include <apr_tables.h>
-#include <apr_file_io.h>
#include <apr_strings.h>
#include "svn_types.h"
@@ -39,27 +38,20 @@
#include "svn_string.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
-#include "svn_xml.h"
#include "svn_error.h"
#include "svn_io.h"
#include "svn_private_config.h"
#include "svn_time.h"
-#include "svn_config.h"
#include "svn_iter.h"
#include "wc.h"
-#include "log.h"
#include "adm_files.h"
-#include "adm_ops.h"
#include "entries.h"
-#include "lock.h"
-#include "props.h"
#include "translate.h"
#include "tree_conflicts.h"
#include "workqueue.h"
#include "private/svn_wc_private.h"
-
/* Checks whether a svn_wc__db_status_t indicates whether a node is
present in a working copy. Used by the editor implementation */
#define IS_NODE_PRESENT(status) \
@@ -198,11 +190,6 @@ struct edit_baton
/* Allow unversioned obstructions when adding a path. */
svn_boolean_t allow_unver_obstructions;
- /* The close_edit method destroys the edit pool and so runs the
- cleanup_dir_baton cleanup handlers. This flag is set to indicate
- that the edit was completed successfully. */
- svn_boolean_t close_edit_complete;
-
/* If this is a 'switch' operation, the new relpath of target_abspath,
else NULL. */
const char *switch_relpath;
@@ -474,16 +461,14 @@ node_get_relpath_ignore_errors(svn_wc__d
if (relpath)
return relpath;
- if (status == svn_wc__db_status_added ||
- status == svn_wc__db_status_obstructed_add)
+ if (status == svn_wc__db_status_added)
{
svn_error_clear(svn_wc__db_scan_addition(NULL, NULL, &relpath, NULL,
NULL, NULL, NULL, NULL, NULL,
db, local_abspath,
result_pool, scratch_pool));
}
- else if (status != svn_wc__db_status_deleted &&
- status != svn_wc__db_status_obstructed_delete)
+ else if (status != svn_wc__db_status_deleted)
{
svn_error_clear(svn_wc__db_scan_base_repos(&relpath, NULL, NULL,
db, local_abspath,
@@ -505,20 +490,9 @@ cleanup_dir_baton(void *dir_baton)
apr_pool_t *pool = apr_pool_parent_get(db->pool);
err = svn_wc__wq_run(eb->db, db->local_abspath,
- eb->cancel_func, eb->cancel_baton,
+ NULL /* cancel_func */, NULL /* cancel_baton */,
pool);
- /* If the editor aborts for some sort of error, the command line
- client relies on pool cleanup to run outstanding work queues and
- remove locks. This avoids leaving the working copy locked in
- many cases, but plays havoc with operations that do multiple
- updates (think externals). So we flag updates that complete
- successfully and avoid removing locks. In 1.6 locks were
- associated with a pool distinct from the edit pool and so were
- removed separately. */
- if (!err && !eb->close_edit_complete)
- err = svn_wc__release_write_lock(eb->wc_ctx, db->local_abspath, pool);
-
if (err)
{
apr_status_t apr_err = err->apr_err;
@@ -726,15 +700,10 @@ complete_directory(struct edit_baton *eb
int i;
apr_pool_t *iterpool;
- /* If this is the root directory and there is a target, we can't
+ /* If this is the root directory and there is a target, we don't have to
mark this directory complete. */
if (is_root_dir && *eb->target_basename != '\0')
{
- /* ### obsolete comment?
- Before we can finish, we may need to clear the exclude flag for
- target. Also give a chance to the target that is explicitly pulled
- in. */
- svn_wc__db_kind_t kind;
svn_wc__db_status_t status;
svn_error_t *err;
@@ -749,38 +718,26 @@ complete_directory(struct edit_baton *eb
If there is no BASE node for the target, then we certainly don't
have to worry about removing it. */
- err = svn_wc__db_base_get_info(&status, &kind, NULL,
- NULL, NULL, NULL,
- NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL,
- eb->db, eb->target_abspath,
- pool, pool);
+ err = svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL,
+ eb->db, eb->target_abspath, pool, pool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
return svn_error_return(err);
svn_error_clear(err);
- return SVN_NO_ERROR;
}
- if (status == svn_wc__db_status_excluded)
+ if (!err && status == svn_wc__db_status_excluded)
{
- /* ### obsolete comment?
- There is a small chance that the target is gone in the
- repository. If so, we should get rid of the entry now. */
+ /* There is a small chance that the explicit target of an update/
+ switch is gone in the repository, in that specific case the node
+ hasn't been re-added to the BASE tree by this update. If so, we
+ should get rid of this excluded node now. */
- if (kind == svn_wc__db_kind_dir &&
- svn_wc__adm_missing(eb->db, eb->target_abspath, pool))
- {
- /* ### obsolete comment?
- * Still passing NULL for THEIR_URL. A case where THEIR_URL
- * is needed in this call is rare or even non-existant.
- * ### TODO: Construct a proper THEIR_URL anyway. See also
- * NULL handling code in do_entry_deletion(). */
- SVN_ERR(do_entry_deletion(eb, eb->target_abspath,
- NULL, FALSE, pool));
- }
+ SVN_ERR(do_entry_deletion(eb, eb->target_abspath, NULL, FALSE, pool));
}
return SVN_NO_ERROR;
@@ -836,46 +793,17 @@ complete_directory(struct edit_baton *eb
svn_wc__db_status_t status;
svn_wc__db_kind_t kind;
svn_revnum_t revnum;
- svn_error_t *err;
svn_pool_clear(iterpool);
node_abspath = svn_dirent_join(local_abspath, name, iterpool);
- /* ### there is an edge case that we can run into right now: this
- ### dir can have a "subdir" node in the BASE_NODE, but the
- ### actual subdir does NOT have a record.
- ###
- ### in particular, copy_tests 21 and schedule_tests 10 can create
- ### this situation. in short: the subdir is rm'd on the disk, and
- ### a deletion of that directory is committed. a local-add then
- ### reintroduces the directory and metadata (within WORKING).
- ### before or after an update, the parent dir has the "subdir"
- ### BASE_NODE and it is missing in the child. asking for the BASE
- ### won't return status_obstructed since there is a true subdir
- ### there now.
- ###
- ### at some point in the control flow, we should have removed
- ### the "subdir" record. maybe there is a good place to remove
- ### that record (or wait for single-dir). for now, we can correct
- ### it when we detect it. */
- err = svn_wc__db_base_get_info(&status, &kind, &revnum,
- NULL, NULL, NULL,
- NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL,
- eb->db, node_abspath,
- iterpool, iterpool);
- if (err)
- {
- if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
- return svn_error_return(err);
-
- svn_error_clear(err);
-
- SVN_ERR(svn_wc__db_temp_remove_subdir_record(eb->db, node_abspath,
- iterpool));
- continue;
- }
+ SVN_ERR(svn_wc__db_base_get_info(&status, &kind, &revnum,
+ NULL, NULL, NULL,
+ NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL,
+ eb->db, node_abspath,
+ iterpool, iterpool));
/* ### obsolete comment?
Any entry still marked as deleted (and not schedule add) can now
@@ -888,24 +816,32 @@ complete_directory(struct edit_baton *eb
number different from the target revision of the update means the
update never mentioned the item, so the entry should be
removed. */
- if (status == svn_wc__db_status_not_present
- || (status == svn_wc__db_status_absent
- && revnum != *eb->target_revision))
+ if (status == svn_wc__db_status_not_present)
{
- SVN_ERR(svn_wc__db_base_remove(eb->db, node_abspath, iterpool));
+ /* "Usually", not_present nodes indicate that an 'svn delete' was
+ * committed and its parent has not been updated yet. We have just
+ * updated the parent and so the not_present BASE_NODE should go
+ * away.
+ * However, not_present can also mean that 'update' wanted to add a
+ * node and found an unversioned obstruction at that path. We don't
+ * want to remove such not_present state, so check if there is a
+ * tree conflict flagged against an unversioned node and leave the
+ * BASE_NODE alone if so.
+ * Note that add_file() automatically fixes such an
+ * added-not_present node when it finds the obstruction gone. */
+ const svn_wc_conflict_description2_t *tree_conflict;
+ SVN_ERR(svn_wc__db_op_read_tree_conflict(&tree_conflict,
+ eb->db,
+ node_abspath,
+ iterpool, iterpool));
+ if (!tree_conflict
+ || tree_conflict->reason != svn_wc_conflict_reason_unversioned)
+ SVN_ERR(svn_wc__db_base_remove(eb->db, node_abspath, iterpool));
}
- else if (kind == svn_wc__db_kind_dir
- && svn_wc__adm_missing(eb->db, node_abspath, iterpool)
- && status != svn_wc__db_status_absent)
- {
- SVN_ERR(svn_wc__db_temp_op_remove_entry(eb->db, node_abspath,
- iterpool));
-
- do_notification(eb, node_abspath, svn_wc_notify_update_delete,
- (kind == svn_wc__db_kind_dir)
- ? svn_node_dir
- : svn_node_file,
- iterpool);
+ else if (status == svn_wc__db_status_absent
+ && revnum != *eb->target_revision)
+ {
+ SVN_ERR(svn_wc__db_base_remove(eb->db, node_abspath, iterpool));
}
}
@@ -993,27 +929,14 @@ struct file_baton
within a locally deleted tree. */
svn_boolean_t deleted;
- /* The path to the incoming text base (that is, to a text-base-file-
- in-progress in the tmp area). This gets set if there are file
- content changes. */
- const char *new_text_base_tmp_abspath;
-
- /* The MD5 checksum of the incoming text base (pristine text). */
+ /* If there are file content changes, these are the checksums of the
+ resulting new text base, which is in the pristine store, else NULL. */
svn_checksum_t *new_text_base_md5_checksum;
-
- /* The SHA1 checksum of the incoming text base (pristine text). */
svn_checksum_t *new_text_base_sha1_checksum;
- /* If this file was added with history, this is the path to a copy
- of the text base of the copyfrom file (in the temporary area). */
- const char *copied_text_base_abspath;
-
- /* If this file was added with history, this is the MD5 checksum of the
- text base (see copied_text_base_abspath). May be NULL if unknown. */
+ /* If this file was added with history, these are the checksums of the
+ copy-from text base, which is in the pristine store, else NULL. */
svn_checksum_t *copied_text_base_md5_checksum;
-
- /* If this file was added with history, this is the SHA-1 checksum of the
- text base (see copied_text_base_abspath). May be NULL if unknown. */
svn_checksum_t *copied_text_base_sha1_checksum;
/* If this file was added with history, and the copyfrom had local
@@ -1039,11 +962,18 @@ struct file_baton
/* The last-changed-date of the file. This is actually a property
that comes through as an 'entry prop', and will be used to set
- the working file's timestamp if it's added. */
+ the working file's timestamp if it's added.
+
+ Will be NULL unless eb->use_commit_times is TRUE. */
const char *last_changed_date;
/* Bump information for the directory this file lives in */
struct bump_dir_info *bump_info;
+
+ /* This is set when there is an incoming add of a file/symlink node onto a
+ * locally added node of different identity (add-vs-add tree conflict). */
+ svn_boolean_t adding_base_under_local_add;
+
};
@@ -1099,12 +1029,15 @@ make_file_baton(struct file_baton **f_p,
}
-/* */
+/* Handle the next delta window of the file described by BATON. If it is
+ * the end (WINDOW == NULL), then check the checksum, store the text in the
+ * pristine store and write its details into BATON->fb->new_text_base_*. */
static svn_error_t *
window_handler(svn_txdelta_window_t *window, void *baton)
{
struct handler_baton *hb = baton;
struct file_baton *fb = hb->fb;
+ svn_wc__db_t *db = fb->edit_baton->db;
svn_error_t *err;
/* Apply this window. We may be done at that point. */
@@ -1142,16 +1075,20 @@ window_handler(svn_txdelta_window_t *win
}
else
{
- /* Tell the file baton about the new text base. */
- fb->new_text_base_tmp_abspath = apr_pstrdup(fb->pool,
- hb->new_text_base_tmp_abspath);
-
- /* ... and its checksums. */
+ /* Tell the file baton about the new text base's checksums. */
fb->new_text_base_md5_checksum =
svn_checksum__from_digest(hb->new_text_base_md5_digest,
svn_checksum_md5, fb->pool);
fb->new_text_base_sha1_checksum =
svn_checksum_dup(hb->new_text_base_sha1_checksum, fb->pool);
+
+ /* Store the new pristine text in the pristine store now. Later, in a
+ single transaction we will update the BASE_NODE to include a
+ reference to this pristine text's checksum. */
+ SVN_ERR(svn_wc__db_pristine_install(db, hb->new_text_base_tmp_abspath,
+ fb->new_text_base_sha1_checksum,
+ fb->new_text_base_md5_checksum,
+ hb->pool));
}
svn_pool_destroy(hb->pool);
@@ -1172,37 +1109,13 @@ prep_directory(struct dir_baton *db,
svn_revnum_t ancestor_revision,
apr_pool_t *pool)
{
- const char *repos_root;
const char *dir_abspath;
- svn_boolean_t locked_here;
dir_abspath = db->local_abspath;
/* Make sure the directory exists. */
SVN_ERR(svn_wc__ensure_directory(dir_abspath, pool));
- /* Use the repository root of the anchor, but only if it actually is an
- ancestor of the URL of this directory. */
- if (svn_uri_is_ancestor(db->edit_baton->repos_root, ancestor_url))
- repos_root = db->edit_baton->repos_root;
- else
- repos_root = NULL;
-
- /* Make sure it's the right working copy, either by creating it so,
- or by checking that it is so already. */
- SVN_ERR(svn_wc__internal_ensure_adm(db->edit_baton->db, dir_abspath,
- ancestor_url, repos_root,
- db->edit_baton->repos_uuid,
- ancestor_revision,
- db->ambient_depth, pool));
-
- SVN_ERR(svn_wc_locked2(&locked_here, NULL, db->edit_baton->wc_ctx,
- dir_abspath, pool));
- if (!locked_here)
- /* Recursive lock release on parent will release this lock. */
- SVN_ERR(svn_wc__acquire_write_lock(NULL, db->edit_baton->wc_ctx,
- dir_abspath, pool, pool));
-
return SVN_NO_ERROR;
}
@@ -1273,9 +1186,12 @@ check_path_under_root(const char *base_p
const char *add_path,
apr_pool_t *pool)
{
- char *full_path;
+ const char *full_path;
+ svn_boolean_t under_root;
- if (! svn_dirent_is_under_root(&full_path, base_path, add_path, pool))
+ SVN_ERR(svn_dirent_is_under_root(&under_root, &full_path, base_path, add_path, pool));
+
+ if (! under_root)
{
return svn_error_createf(
SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
@@ -1386,36 +1302,6 @@ open_root(void *edit_baton,
}
-/* Helper for delete_entry() and do_entry_deletion().
-
- If the error chain ERR contains evidence that a local mod was left
- (an SVN_ERR_WC_LEFT_LOCAL_MOD error), clear ERR. Otherwise, return ERR.
-*/
-static svn_error_t *
-leftmod_error_chain(svn_error_t *err)
-{
- svn_error_t *tmp_err;
-
- if (! err)
- return SVN_NO_ERROR;
-
- /* Advance TMP_ERR to the part of the error chain that reveals that
- a local mod was left, or to the NULL end of the chain. */
- for (tmp_err = err; tmp_err; tmp_err = tmp_err->child)
- if (tmp_err->apr_err == SVN_ERR_WC_LEFT_LOCAL_MOD)
- {
- /* We just found a "left a local mod" error, so tolerate it
- and clear the whole error. In that case we continue with
- modified files left on the disk. */
- svn_error_clear(err);
- return SVN_NO_ERROR;
- }
-
- /* Otherwise, we just return our top-most error. */
- return err;
-}
-
-
/* ===================================================================== */
/* Checking for local modifications. */
@@ -1481,7 +1367,9 @@ modcheck_found_node(const char *local_ab
if (status != svn_wc__db_status_normal)
modified = TRUE;
- else
+ /* No need to check if we already have at least one non-delete
+ modification */
+ else if (!baton->found_mod || baton->all_edits_are_deletes)
SVN_ERR(entry_has_local_mods(&modified, baton->db, local_abspath,
kind, scratch_pool));
@@ -1533,6 +1421,199 @@ tree_has_local_mods(svn_boolean_t *modif
/* Indicates an unset svn_wc_conflict_reason_t. */
#define SVN_WC_CONFLICT_REASON_NONE (svn_wc_conflict_reason_t)(-1)
+/* Create a tree conflict struct.
+ *
+ * The REASON is stored directly in the tree conflict info.
+ *
+ * All temporary allocactions are be made in SCRATCH_POOL, while allocations
+ * needed for the returned conflict struct are made in RESULT_POOL.
+ *
+ * All other parameters are identical to and described by
+ * check_tree_conflict(), with the slight modification that this function
+ * relies on the reason passed in REASON instead of actively looking for one. */
+static svn_error_t *
+create_tree_conflict(svn_wc_conflict_description2_t **pconflict,
+ struct edit_baton *eb,
+ const char *local_abspath,
+ svn_wc_conflict_reason_t reason,
+ svn_wc_conflict_action_t action,
+ svn_node_kind_t their_node_kind,
+ const char *their_relpath,
+ apr_pool_t *result_pool, apr_pool_t *scratch_pool)
+{
+ const char *repos_root_url = NULL;
+ const char *left_repos_relpath;
+ svn_revnum_t left_revision;
+ svn_node_kind_t left_kind;
+ const char *right_repos_relpath;
+ const char *added_repos_relpath = NULL;
+ svn_node_kind_t conflict_node_kind;
+ svn_wc_conflict_version_t *src_left_version;
+ svn_wc_conflict_version_t *src_right_version;
+
+ *pconflict = NULL;
+
+ SVN_ERR_ASSERT(reason != SVN_WC_CONFLICT_REASON_NONE);
+
+ /* Get the source-left information, i.e. the local state of the node
+ * before any changes were made to the working copy, i.e. the state the
+ * node would have if it was reverted. */
+ if (reason == svn_wc_conflict_reason_added)
+ {
+ svn_wc__db_status_t added_status;
+
+ /* ###TODO: It would be nice to tell the user at which URL and
+ * ### revision source-left was empty, which could be quite difficult
+ * ### to code, and is a slight theoretical leap of the svn mind.
+ * ### Update should show
+ * ### URL: svn_wc__db_scan_addition( &repos_relpath )
+ * ### REV: The base revision of the parent of before this update
+ * ### started
+ * ### ### BUT what if parent was updated/switched away with
+ * ### ### depth=empty after this node was added?
+ * ### Switch should show
+ * ### URL: scan_addition URL of before this switch started
+ * ### REV: same as above */
+
+ /* In case of a local addition, source-left is non-existent / empty. */
+ left_kind = svn_node_none;
+ left_revision = SVN_INVALID_REVNUM;
+ left_repos_relpath = NULL;
+
+ /* Still get the repository root needed by both 'update' and 'switch',
+ * and the would-be repos_relpath needed to construct the source-right
+ * in case of an 'update'. Check sanity while we're at it. */
+ SVN_ERR(svn_wc__db_scan_addition(&added_status, NULL,
+ &added_repos_relpath,
+ &repos_root_url,
+ NULL, NULL, NULL, NULL, NULL,
+ eb->db, local_abspath,
+ result_pool, scratch_pool));
+
+ /* This better really be an added status. */
+ SVN_ERR_ASSERT(added_status == svn_wc__db_status_added
+ || added_status == svn_wc__db_status_copied
+ || added_status == svn_wc__db_status_moved_here);
+ }
+ else if (reason == svn_wc_conflict_reason_unversioned)
+ {
+ /* Obstructed by an unversioned node. Source-left is
+ * non-existent/empty. */
+ left_kind = svn_node_none;
+ left_revision = SVN_INVALID_REVNUM;
+ left_repos_relpath = NULL;
+ repos_root_url = eb->repos_root;
+ }
+ else
+ {
+ /* A BASE node should exist. */
+ svn_wc__db_kind_t base_kind;
+
+ /* If anything else shows up, then this assertion is probably naive
+ * and that other case should also be handled. */
+ SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_edited
+ || reason == svn_wc_conflict_reason_deleted
+ || reason == svn_wc_conflict_reason_replaced
+ || reason == svn_wc_conflict_reason_obstructed);
+
+ SVN_ERR(svn_wc__db_base_get_info(NULL, &base_kind,
+ &left_revision,
+ &left_repos_relpath,
+ &repos_root_url,
+ NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ eb->db,
+ local_abspath,
+ result_pool,
+ scratch_pool));
+ /* Translate the node kind. */
+ if (base_kind == svn_wc__db_kind_file
+ || base_kind == svn_wc__db_kind_symlink)
+ left_kind = svn_node_file;
+ else if (base_kind == svn_wc__db_kind_dir)
+ left_kind = svn_node_dir;
+ else
+ SVN_ERR_MALFUNCTION();
+ }
+
+ SVN_ERR_ASSERT(strcmp(repos_root_url, eb->repos_root) == 0);
+
+ /* Find the source-right information, i.e. the state in the repository
+ * to which we would like to update. */
+ if (eb->switch_relpath)
+ {
+ /* If this is a 'switch' operation, try to construct the switch
+ * target's REPOS_RELPATH. */
+ if (their_relpath != NULL)
+ right_repos_relpath = their_relpath;
+ else
+ {
+ /* The complete source-right URL is not available, but it
+ * is somewhere below the SWITCH_URL. For now, just go
+ * without it.
+ * ### TODO: Construct a proper THEIR_URL in some of the
+ * delete cases that still pass NULL for THEIR_URL when
+ * calling this function. Do that on the caller's side. */
+ right_repos_relpath = eb->switch_relpath;
+ right_repos_relpath = apr_pstrcat(result_pool, right_repos_relpath,
+ "_THIS_IS_INCOMPLETE", NULL);
+ }
+ }
+ else
+ {
+ /* This is an 'update', so REPOS_RELPATH would be the same as for
+ * source-left. However, we don't have a source-left for locally
+ * added files. */
+ right_repos_relpath = (reason == svn_wc_conflict_reason_added ?
+ added_repos_relpath : left_repos_relpath);
+ if (! right_repos_relpath)
+ right_repos_relpath = their_relpath;
+ }
+
+ SVN_ERR_ASSERT(right_repos_relpath != NULL);
+
+ /* Determine PCONFLICT's overall node kind, which is not allowed to be
+ * svn_node_none. We give it the source-right revision (THEIR_NODE_KIND)
+ * -- unless source-right is deleted and hence == svn_node_none, in which
+ * case we take it from source-left, which has to be the node kind that
+ * was deleted. */
+ conflict_node_kind = (action == svn_wc_conflict_action_delete ?
+ left_kind : their_node_kind);
+ SVN_ERR_ASSERT(conflict_node_kind == svn_node_file
+ || conflict_node_kind == svn_node_dir);
+
+
+ /* Construct the tree conflict info structs. */
+
+ if (left_repos_relpath == NULL)
+ /* A locally added or unversioned path in conflict with an incoming add.
+ * Send an 'empty' left revision. */
+ src_left_version = NULL;
+ else
+ src_left_version = svn_wc_conflict_version_create(repos_root_url,
+ left_repos_relpath,
+ left_revision,
+ left_kind,
+ result_pool);
+
+ src_right_version = svn_wc_conflict_version_create(repos_root_url,
+ right_repos_relpath,
+ *eb->target_revision,
+ their_node_kind,
+ result_pool);
+
+ *pconflict = svn_wc_conflict_description_create_tree2(
+ local_abspath, conflict_node_kind,
+ eb->switch_relpath ?
+ svn_wc_operation_switch : svn_wc_operation_update,
+ src_left_version, src_right_version, result_pool);
+ (*pconflict)->action = action;
+ (*pconflict)->reason = reason;
+
+ return SVN_NO_ERROR;
+}
+
+
/* Check whether the incoming change ACTION on FULL_PATH would conflict with
* LOCAL_ABSPATH's scheduled change. If so, then raise a tree conflict with
* LOCAL_ABSPATH as the victim.
@@ -1540,24 +1621,20 @@ tree_has_local_mods(svn_boolean_t *modif
* The edit baton EB gives information including whether the operation is
* an update or a switch.
*
- * If PCONFLICT is not null, set *PCONFLICT to the conflict description if
- * there is one or else to null.
+ * If a tree conflict reason was found for the incoming action, the resulting
+ * tree conflict info is returned in *PCONFLICT. PCONFLICT must be non-NULL,
+ * while *PCONFLICT is always overwritten.
*
- * THEIR_NODE_KIND is the node kind reflected by the incoming edit
+ * THEIR_NODE_KIND should be the node kind reflected by the incoming edit
* function. E.g. dir_opened() should pass svn_node_dir, etc.
* In some cases of delete, svn_node_none may be used here.
*
- * THEIR_RELPATH is the involved node's repository relative path on the
- * source-right side, the side that the target should become after the
- * update. Simply put, that's the URL obtained from the node's
- * dir_baton->new_relpath or file_baton->new_relpath (but it's more
- * complex for a delete).
- *
- * ACCEPT_DELETED is true if one of the ancestors got tree conflicted, but
- * the operation continued updating a deleted base tree.
+ * THEIR_RELPATH should be the involved node's repository-relative path on the
+ * source-right side, the side that the target should become after the update.
+ * Simply put, that's the URL obtained from the node's dir_baton->new_relpath
+ * or file_baton->new_relpath (but it's more complex for a delete).
*
- * Tree conflict use cases are described in issue #2282 and in
- * notes/tree-conflicts/detection.txt.
+ * All allocations are made in POOL.
*/
static svn_error_t *
check_tree_conflict(svn_wc_conflict_description2_t **pconflict,
@@ -1595,7 +1672,6 @@ check_tree_conflict(svn_wc_conflict_desc
switch (status)
{
case svn_wc__db_status_added:
- case svn_wc__db_status_obstructed_add:
case svn_wc__db_status_moved_here:
case svn_wc__db_status_copied:
/* Is it a replace? */
@@ -1636,7 +1712,6 @@ check_tree_conflict(svn_wc_conflict_desc
case svn_wc__db_status_deleted:
- case svn_wc__db_status_obstructed_delete:
/* The node is locally deleted. */
reason = svn_wc_conflict_reason_deleted;
break;
@@ -1648,10 +1723,6 @@ check_tree_conflict(svn_wc_conflict_desc
* So the node exists and is essentially 'normal'. We still need to
* check prop and text mods, and those checks will retrieve the
* missing information (hopefully). */
- case svn_wc__db_status_obstructed:
- /* Tree-conflicts during update are only concerned with local
- * modifications. We can safely update BASE, disregarding the
- * obstruction. So let's treat this as normal. */
case svn_wc__db_status_normal:
if (action == svn_wc_conflict_action_edit)
/* An edit onto a local edit or onto *no* local changes is no
@@ -1675,11 +1746,10 @@ check_tree_conflict(svn_wc_conflict_desc
* but the update editor will not visit the subdirectories
* of a directory that it wants to delete. Therefore, we
* need to start a separate crawl here. */
- if (!svn_wc__adm_missing(eb->db, local_abspath, pool))
- SVN_ERR(tree_has_local_mods(&modified, &all_mods_are_deletes,
- eb->db, local_abspath,
- eb->cancel_func, eb->cancel_baton,
- pool));
+ SVN_ERR(tree_has_local_mods(&modified, &all_mods_are_deletes,
+ eb->db, local_abspath,
+ eb->cancel_func, eb->cancel_baton,
+ pool));
break;
default:
@@ -1739,162 +1809,12 @@ check_tree_conflict(svn_wc_conflict_desc
/* A conflict was detected. Append log commands to the log accumulator
* to record it. */
- {
- const char *repos_root_url = NULL;
- const char *left_repos_relpath;
- svn_revnum_t left_revision;
- svn_node_kind_t left_kind;
- const char *right_repos_relpath;
- const char *added_repos_relpath = NULL;
- svn_node_kind_t conflict_node_kind;
- svn_wc_conflict_version_t *src_left_version;
- svn_wc_conflict_version_t *src_right_version;
-
- /* Get the source-left information, i.e. the local state of the node
- * before any changes were made to the working copy, i.e. the state the
- * node would have if it was reverted. */
- if (reason == svn_wc_conflict_reason_added)
- {
- svn_wc__db_status_t added_status;
-
- /* ###TODO: It would be nice to tell the user at which URL and
- * ### revision source-left was empty, which could be quite difficult
- * ### to code, and is a slight theoretical leap of the svn mind.
- * ### Update should show
- * ### URL: svn_wc__db_scan_addition( &repos_relpath )
- * ### REV: The base revision of the parent of before this update
- * ### started
- * ### ### BUT what if parent was updated/switched away with
- * ### ### depth=empty after this node was added?
- * ### Switch should show
- * ### URL: scan_addition URL of before this switch started
- * ### REV: same as above */
-
- /* In case of a local addition, source-left is non-existent / empty. */
- left_kind = svn_node_none;
- left_revision = SVN_INVALID_REVNUM;
- left_repos_relpath = NULL;
-
- /* Still get the repository root needed by both 'update' and 'switch',
- * and the would-be repos_relpath needed to construct the source-right
- * in case of an 'update'. Check sanity while we're at it. */
- SVN_ERR(svn_wc__db_scan_addition(&added_status, NULL,
- &added_repos_relpath,
- &repos_root_url,
- NULL, NULL, NULL, NULL, NULL,
- eb->db, local_abspath, pool, pool));
-
- /* This better really be an added status. */
- SVN_ERR_ASSERT(added_status == svn_wc__db_status_added
- || added_status == svn_wc__db_status_obstructed_add
- || added_status == svn_wc__db_status_copied
- || added_status == svn_wc__db_status_moved_here);
- }
- else
- {
- /* A BASE node should exist. */
- svn_wc__db_kind_t base_kind;
-
- /* If anything else shows up, then this assertion is probably naive
- * and that other case should also be handled. */
- SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_edited
- || reason == svn_wc_conflict_reason_deleted
- || reason == svn_wc_conflict_reason_replaced
- || reason == svn_wc_conflict_reason_obstructed);
-
- SVN_ERR(svn_wc__db_base_get_info(NULL, &base_kind,
- &left_revision,
- &left_repos_relpath,
- &repos_root_url,
- NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL,
- eb->db,
- local_abspath,
- pool,
- pool));
- /* Translate the node kind. */
- if (base_kind == svn_wc__db_kind_file
- || base_kind == svn_wc__db_kind_symlink)
- left_kind = svn_node_file;
- else if (base_kind == svn_wc__db_kind_dir)
- left_kind = svn_node_dir;
- else
- SVN_ERR_MALFUNCTION();
- }
-
-
- /* Find the source-right information, i.e. the state in the repository
- * to which we would like to update. */
- if (eb->switch_relpath)
- {
- /* If this is a 'switch' operation, try to construct the switch
- * target's REPOS_RELPATH. */
- if (their_relpath != NULL)
- right_repos_relpath = their_relpath;
- else
- {
- /* The complete source-right URL is not available, but it
- * is somewhere below the SWITCH_URL. For now, just go
- * without it.
- * ### TODO: Construct a proper THEIR_URL in some of the
- * delete cases that still pass NULL for THEIR_URL when
- * calling this function. Do that on the caller's side. */
- right_repos_relpath = eb->switch_relpath;
- right_repos_relpath = apr_pstrcat(pool, right_repos_relpath,
- "_THIS_IS_INCOMPLETE", NULL);
- }
- }
- else
- {
- /* This is an 'update', so REPOS_RELPATH would be the same as for
- * source-left. However, we don't have a source-left for locally
- * added files. */
- right_repos_relpath = (reason == svn_wc_conflict_reason_added ?
- added_repos_relpath : left_repos_relpath);
- }
-
- /* Determine PCONFLICT's overall node kind, which is not allowed to be
- * svn_node_none. We give it the source-right revision (THEIR_NODE_KIND)
- * -- unless source-right is deleted and hence == svn_node_none, in which
- * case we take it from source-left, which has to be the node kind that
- * was deleted. */
- conflict_node_kind = (action == svn_wc_conflict_action_delete ?
- left_kind : their_node_kind);
- SVN_ERR_ASSERT(conflict_node_kind == svn_node_file
- || conflict_node_kind == svn_node_dir);
-
-
- /* Construct the tree conflict info structs. */
-
- if (left_repos_relpath == NULL)
- /* A locally added path in conflict with an incoming add.
- * Send an 'empty' left revision. */
- src_left_version = NULL;
- else
- src_left_version = svn_wc_conflict_version_create(repos_root_url,
- left_repos_relpath,
- left_revision,
- left_kind,
- pool);
-
- src_right_version = svn_wc_conflict_version_create(repos_root_url,
- right_repos_relpath,
- *eb->target_revision,
- their_node_kind,
- pool);
-
- *pconflict = svn_wc_conflict_description_create_tree2(
- local_abspath, conflict_node_kind,
- eb->switch_relpath ?
- svn_wc_operation_switch : svn_wc_operation_update,
- src_left_version, src_right_version, pool);
- (*pconflict)->action = action;
- (*pconflict)->reason = reason;
- }
-
- return SVN_NO_ERROR;
+ return svn_error_return(create_tree_conflict(pconflict, eb, local_abspath,
+ reason, action, their_node_kind,
+ their_relpath, pool, pool));
}
+
/* If LOCAL_ABSPATH is inside a conflicted tree, set *CONFLICTED to TRUE,
* Otherwise set *CONFLICTED to FALSE. Use SCRATCH_POOL for temporary
* allocations.
@@ -1923,58 +1843,55 @@ already_in_a_tree_conflict(svn_boolean_t
while (TRUE)
{
- svn_wc__db_kind_t kind;
- svn_boolean_t hidden;
- svn_boolean_t is_wc_root;
+ svn_wc__db_status_t status;
+ svn_boolean_t is_wc_root, has_conflict;
svn_error_t *err;
const svn_wc_conflict_description2_t *conflict;
svn_pool_clear(iterpool);
- err = svn_wc__db_read_kind(&kind, db, ancestor_abspath, TRUE,
- iterpool);
+ err = svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, &has_conflict, NULL,
+ db, ancestor_abspath, iterpool, iterpool);
if (err)
{
- if (! SVN_WC__ERR_IS_NOT_CURRENT_WC(err))
+ if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND
+ && !SVN_WC__ERR_IS_NOT_CURRENT_WC(err))
return svn_error_return(err);
svn_error_clear(err);
break;
}
- if (kind == svn_wc__db_kind_unknown)
- break;
-
- SVN_ERR(svn_wc__db_node_hidden(&hidden, db, ancestor_abspath, iterpool));
-
- if (hidden)
+ if (status == svn_wc__db_status_not_present
+ || status == svn_wc__db_status_absent
+ || status == svn_wc__db_status_excluded)
break;
- SVN_ERR(svn_wc__db_op_read_tree_conflict(&conflict, db, ancestor_abspath,
- iterpool, iterpool));
-
- if (conflict != NULL)
+ if (has_conflict)
{
- *conflicted = TRUE;
- break;
+ SVN_ERR(svn_wc__db_op_read_tree_conflict(&conflict, db,
+ ancestor_abspath,
+ iterpool, iterpool));
+
+ if (conflict != NULL)
+ {
+ *conflicted = TRUE;
+ break;
+ }
}
if (svn_dirent_is_root(ancestor_abspath, strlen(ancestor_abspath)))
break;
- err = svn_wc__check_wc_root(&is_wc_root, NULL, NULL,
- db, ancestor_abspath, iterpool);
+ SVN_ERR(svn_wc__db_is_wcroot(&is_wc_root, db, ancestor_abspath,
+ iterpool));
- if (err
- && (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND
- || err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY))
- {
- svn_error_clear(err);
- return SVN_NO_ERROR;
- }
- else
- SVN_ERR(err);
+ if (is_wc_root)
+ break;
ancestor_abspath = svn_dirent_dirname(ancestor_abspath, scratch_pool);
}
@@ -2047,19 +1964,25 @@ do_entry_deletion(struct edit_baton *eb,
svn_boolean_t in_deleted_and_tree_conflicted_subtree,
apr_pool_t *pool)
{
+ svn_wc__db_status_t status;
svn_wc__db_kind_t kind;
- svn_boolean_t already_conflicted;
+ svn_boolean_t conflicted;
svn_wc_conflict_description2_t *tree_conflict = NULL;
const char *dir_abspath = svn_dirent_dirname(local_abspath, pool);
svn_boolean_t hidden;
svn_skel_t *work_item;
- SVN_ERR(svn_wc__db_read_kind(&kind, eb->db, local_abspath, FALSE, pool));
+ SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ &conflicted, NULL,
+ eb->db, local_abspath, pool, pool));
/* Is this path a conflict victim? */
- SVN_ERR(node_already_conflicted(&already_conflicted, eb->db,
- local_abspath, pool));
- if (already_conflicted)
+ if (conflicted)
+ SVN_ERR(node_already_conflicted(&conflicted, eb->db,
+ local_abspath, pool));
+ if (conflicted)
{
SVN_ERR(remember_skipped_tree(eb, local_abspath));
@@ -2072,10 +1995,11 @@ do_entry_deletion(struct edit_baton *eb,
/* Receive the remote removal of excluded/absent/not present node.
Do not notify. */
- SVN_ERR(svn_wc__db_node_hidden(&hidden, eb->db, local_abspath, pool));
- if (hidden)
+ if (status == svn_wc__db_status_not_present
+ || status == svn_wc__db_status_excluded
+ || status == svn_wc__db_status_absent)
{
- SVN_ERR(svn_wc__db_temp_op_remove_entry(eb->db, local_abspath, pool));
+ SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath, pool));
if (strcmp(local_abspath, eb->target_abspath) == 0)
eb->target_deleted = TRUE;
@@ -2097,12 +2021,19 @@ do_entry_deletion(struct edit_baton *eb,
if (tree_conflict != NULL)
{
- /* When we raise a tree conflict on a directory, we want to avoid
+ /* When we raise a tree conflict on a node, we want to avoid
* making any changes inside it. (Will an update ever try to make
- * further changes to or inside a directory it's just deleted?) */
- SVN_ERR(svn_wc__loggy_add_tree_conflict(&work_item, eb->db, dir_abspath,
- tree_conflict, pool));
- SVN_ERR(svn_wc__db_wq_add(eb->db, dir_abspath, work_item, pool));
+ * further changes to or inside a directory it's just deleted?)
+ *
+ * ### BH: Only if a new node is added in it's place.
+ * See svn_delta.h for further details. */
+
+ /* ### Note that we still add this on the parent of a node, so it is
+ ### safe to add it now, while we remove the node later */
+ SVN_ERR(svn_wc__db_op_set_tree_conflict(eb->db,
+ tree_conflict->local_abspath,
+ tree_conflict,
+ pool));
SVN_ERR(remember_skipped_tree(eb, local_abspath));
@@ -2113,23 +2044,16 @@ do_entry_deletion(struct edit_baton *eb,
{
/* The item exists locally and has some sort of local mod.
* It no longer exists in the repository at its target URL@REV.
- * (### If its WC parent was not updated similarly, then it needs to
- * be marked 'deleted' in its WC parent.)
+ *
* To prepare the "accept mine" resolution for the tree conflict,
* we must schedule the existing content for re-addition as a copy
* of what it was, but with its local modifications preserved. */
- /* Run the log in the parent dir, to record the tree conflict.
- * Do this before schedule_existing_item_for_re_add(), in case
- * that needs to modify the same entries. */
- SVN_ERR(svn_wc__wq_run(eb->db, dir_abspath,
- eb->cancel_func, eb->cancel_baton,
- pool));
-
- SVN_ERR(svn_wc__db_temp_op_make_copy(eb->db, local_abspath, TRUE,
+ SVN_ERR(svn_wc__db_temp_op_make_copy(eb->db, local_abspath, FALSE,
pool));
- return SVN_NO_ERROR;
+ /* Fall through to remove the BASE_NODEs properly, with potentially
+ keeping a not-present marker */
}
else if (tree_conflict->reason == svn_wc_conflict_reason_deleted)
{
@@ -2143,31 +2067,19 @@ do_entry_deletion(struct edit_baton *eb,
else if (tree_conflict->reason == svn_wc_conflict_reason_replaced)
{
/* The item was locally replaced with something else. We should
- * keep the existing item schedule-replace, but we also need to
- * update the BASE rev of the item to the revision we are updating
- * to. Otherwise, the replace cannot be committed because the item
- * is considered out-of-date, and it cannot be updated either because
- * we're here to do just that. */
-
- /* Run the log in the parent dir, to record the tree conflict.
- * Do this before schedule_existing_item_for_re_add(), in case
- * that needs to modify the same entries. */
- SVN_ERR(svn_wc__wq_run(eb->db, dir_abspath,
- eb->cancel_func, eb->cancel_baton,
- pool));
-
- SVN_ERR(svn_wc__db_temp_op_make_copy(eb->db, local_abspath, TRUE,
- pool));
-
- return SVN_NO_ERROR;
+ * remove the BASE node below the new working node, which turns
+ * the replacement in an addition. */
+
+ /* Fall through to the normal "delete" code path. */
}
else
SVN_ERR_MALFUNCTION(); /* other reasons are not expected here */
}
- /* Issue a loggy command to delete the entry from version control and to
- delete it from disk if unmodified, but leave any modified files on disk
- unversioned.
+ /* Issue a wq operation to delete the BASE_NODE data and to delete actual
+ nodes based on that from disk, but leave any WORKING_NODEs on disk.
+
+ Local modifications are already turned into copies at this point.
If the thing being deleted is the *target* of this update, then
we need to recreate a 'deleted' entry, so that the parent can give
@@ -2175,58 +2087,21 @@ do_entry_deletion(struct edit_baton *eb,
if (strcmp(local_abspath, eb->target_abspath) != 0)
{
/* Delete, and do not leave a not-present node. */
- SVN_ERR(svn_wc__loggy_delete_entry(&work_item,
- eb->db, dir_abspath, local_abspath,
- SVN_INVALID_REVNUM,
- svn_wc__db_kind_unknown,
- pool));
+ SVN_ERR(svn_wc__wq_build_base_remove(&work_item,
+ eb->db, local_abspath, FALSE,
+ pool, pool));
SVN_ERR(svn_wc__db_wq_add(eb->db, dir_abspath, work_item, pool));
}
else
{
/* Delete, leaving a not-present node. */
- SVN_ERR(svn_wc__loggy_delete_entry(&work_item,
- eb->db, dir_abspath, local_abspath,
- *eb->target_revision,
- kind,
- pool));
+ SVN_ERR(svn_wc__wq_build_base_remove(&work_item,
+ eb->db, local_abspath, TRUE,
+ pool, pool));
SVN_ERR(svn_wc__db_wq_add(eb->db, dir_abspath, work_item, pool));
eb->target_deleted = TRUE;
}
- if (eb->switch_relpath)
- {
- /* The SVN_WC__LOG_DELETE_ENTRY log item will cause
- * svn_wc_remove_from_revision_control() to be run. But that
- * function checks whether the deletion target's URL is child of
- * its parent directory's URL, and if it's not, then the entry
- * in parent won't be deleted (because presumably the child
- * represents a disjoint working copy, i.e., it is a wc_root).
- *
- * However, during a switch this works against us, because by
- * the time we get here, the parent's URL has already been
- * changed. So we manually remove the child from revision
- * control after the delete-entry item has been written in the
- * parent's log, but before it is run, so the only work left for
- * the log item is to remove the entry in the parent directory.
- */
-
- if (kind == svn_wc__db_kind_dir)
- {
- SVN_ERR(leftmod_error_chain(
- svn_wc__internal_remove_from_revision_control(
- eb->db,
- local_abspath,
- TRUE, /* destroy */
- FALSE, /* instant error */
- eb->cancel_func,
- eb->cancel_baton,
- pool)));
- }
- }
-
- /* Note: these two lines are duplicated in the tree-conflicts bail out
- * above. */
SVN_ERR(svn_wc__wq_run(eb->db, dir_abspath,
eb->cancel_func, eb->cancel_baton,
pool));
@@ -2288,7 +2163,9 @@ add_directory(const char *path,
svn_node_kind_t kind;
svn_wc__db_status_t status;
svn_wc__db_kind_t wc_kind;
- svn_boolean_t already_conflicted;
+ svn_boolean_t conflicted;
+ svn_boolean_t versioned_locally_and_present;
+ svn_wc_conflict_description2_t *tree_conflict = NULL;
svn_error_t *err;
/* Semantic check. Either both "copyfrom" args are valid, or they're
@@ -2364,25 +2241,6 @@ add_directory(const char *path,
db->ambient_depth = svn_depth_infinity;
}
- /* Is this path a conflict victim? */
- SVN_ERR(node_already_conflicted(&already_conflicted, eb->db,
- db->local_abspath, pool));
- if (already_conflicted)
- {
- /* Record this conflict so that its descendants are skipped silently. */
- SVN_ERR(remember_skipped_tree(eb, db->local_abspath));
-
- db->skip_this = TRUE;
- db->skip_descendants = TRUE;
- db->already_notified = TRUE;
-
- /* ### TODO: Also print victim_path in the skip msg. */
- do_notification(eb, db->local_abspath, svn_node_unknown,
- svn_wc_notify_skip, pool);
-
- return SVN_NO_ERROR;
- }
-
/* It may not be named the same as the administrative directory. */
if (svn_wc_is_adm_dir(db->name, pool))
return svn_error_createf(
@@ -2395,8 +2253,8 @@ add_directory(const char *path,
err = svn_wc__db_read_info(&status, &wc_kind, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ &conflicted, NULL,
eb->db, db->local_abspath, db->pool, db->pool);
if (err)
{
@@ -2406,48 +2264,98 @@ add_directory(const char *path,
svn_error_clear(err);
wc_kind = svn_wc__db_kind_unknown;
status = svn_wc__db_status_normal;
+ conflicted = TRUE; /* TRUE here causes us to check for a conflict */
+
+ versioned_locally_and_present = FALSE;
+ }
+ else
+ versioned_locally_and_present = IS_NODE_PRESENT(status);
+
+ /* Is this path a conflict victim? */
+ if (conflicted)
+ SVN_ERR(node_already_conflicted(&conflicted, eb->db,
+ db->local_abspath, pool));
+ if (conflicted
+ && status == svn_wc__db_status_not_present
+ && kind == svn_node_none)
+ {
+ /* A conflict is flagged. Now let's do some user convenience.
+ * When we flagged a tree conflict for a local unversioned node
+ * vs. an incoming add, and we find that this unversioned node is
+ * no longer in the way, automatically pull in the versioned node
+ * and remove the conflict marker.
+ * Right, the node status matches (not_present) and there is no
+ * unversioned obstruction in the file system (anymore?). If it
+ * has a tree conflict with reason 'unversioned', remove that. */
+ const svn_wc_conflict_description2_t *previous_tc;
+ SVN_ERR(svn_wc__get_tree_conflict(&previous_tc,
+ eb->wc_ctx,
+ db->local_abspath,
+ pool, pool));
+ if (previous_tc
+ && previous_tc->reason == svn_wc_conflict_reason_unversioned)
+ {
+ /* Remove tree conflict. */
+ SVN_ERR(svn_wc__db_op_set_tree_conflict(eb->db,
+ db->local_abspath,
+ NULL, pool));
+ /* Don't skip this path after all. */
+ conflicted = FALSE;
+ }
}
- /* The path can exist, but it must be a directory. */
- if (kind == svn_node_file || kind == svn_node_unknown ||
- (wc_kind != svn_wc__db_kind_unknown &&
- wc_kind != svn_wc__db_kind_dir && IS_NODE_PRESENT(status)))
+ /* Now the "usual" behaviour if already conflicted. Skip it. */
+ if (conflicted)
{
+ /* Record this conflict so that its descendants are skipped silently. */
+ SVN_ERR(remember_skipped_tree(eb, db->local_abspath));
+
+ db->skip_this = TRUE;
+ db->skip_descendants = TRUE;
db->already_notified = TRUE;
- do_notification(eb, db->local_abspath, svn_node_dir,
- svn_wc_notify_update_obstruction, pool);
- return svn_error_createf(
- SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
- _("Failed to add directory '%s': a non-directory object of the "
- "same name already exists"),
- svn_dirent_local_style(db->local_abspath, pool));
+ /* ### TODO: Also print victim_path in the skip msg. */
+ do_notification(eb, db->local_abspath, svn_node_unknown,
+ svn_wc_notify_skip, pool);
+ return SVN_NO_ERROR;
}
- if (kind == svn_node_dir &&
- (wc_kind == svn_wc__db_kind_unknown || !IS_NODE_PRESENT(status)))
+
+ if (versioned_locally_and_present)
{
- /* Found an unversioned directory */
- db->obstruction_found = TRUE;
+ /* What to do with a versioned or schedule-add dir:
- if (!eb->allow_unver_obstructions)
- {
- db->already_notified = TRUE;
- do_notification(eb, db->local_abspath, svn_node_dir,
- svn_wc_notify_update_obstruction, pool);
+ A dir already added without history is OK. Set add_existed
+ so that user notification is delayed until after any prop
+ conflicts have been found.
- return svn_error_createf(
- SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
- _("Failed to add directory '%s': an unversioned "
- "directory of the same name already exists"),
- svn_dirent_local_style(db->local_abspath, pool));
- }
- }
- else if (wc_kind == svn_wc__db_kind_dir)
- {
- /* Directory exists */
- if (IS_NODE_PRESENT(status) &&
- status != svn_wc__db_status_deleted)
+ An existing versioned dir is an error. In the future we may
+ relax this restriction and simply update such dirs.
+
+ A dir added with history is a tree conflict. */
+
+ svn_boolean_t local_is_dir;
+ svn_boolean_t local_is_non_dir;
+ const char *local_is_copy = NULL;
+
+ /* Is the local add a copy? */
+ if (status == svn_wc__db_status_added)
+ SVN_ERR(svn_wc__node_get_copyfrom_info(&local_is_copy,
+ NULL, NULL, NULL, NULL,
+ eb->wc_ctx,
+ db->local_abspath,
+ pool, pool));
+
+
+ /* Is there something that is a file? */
+ local_is_dir = (wc_kind == svn_wc__db_kind_dir
+ && status != svn_wc__db_status_deleted);
+
+ /* Is there *something* that is not a dir? */
+ local_is_non_dir = (wc_kind != svn_wc__db_kind_dir
+ && status != svn_wc__db_status_deleted);
+
+ if (local_is_dir)
{
svn_boolean_t wc_root;
svn_boolean_t switched;
@@ -2489,129 +2397,126 @@ add_directory(const char *path,
}
}
- /* What to do with a versioned or schedule-add dir:
+ /* We can't properly handle add vs. add with mismatching
+ * node kinds before single db. */
+ if (local_is_non_dir)
+ {
+ db->already_notified = TRUE;
+ do_notification(eb, db->local_abspath, svn_node_dir,
+ svn_wc_notify_update_obstruction, pool);
+ return svn_error_createf(
+ SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
+ _("Failed to add directory '%s': a non-directory object "
+ "of the same name already exists"),
+ svn_dirent_local_style(db->local_abspath,
+ pool));
+ }
- A dir already added without history is OK. Set add_existed
- so that user notification is delayed until after any prop
- conflicts have been found.
-
- An existing versioned dir is an error. In the future we may
- relax this restriction and simply update such dirs.
-
- A dir added with history is a tree conflict. */
-
- if (status == svn_wc__db_status_added)
- {
- /* Specialize the added case to added, copied, moved */
- SVN_ERR(svn_wc__db_scan_addition(&status, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, eb->db,
- db->local_abspath, pool, pool));
+ /* Do tree conflict checking if
+ * - if there is a local copy.
+ * - if this is a switch operation
+ * - the node kinds mismatch (when single db is here)
+ *
+ * During switch, local adds at the same path as incoming adds get
+ * "lost" in that switching back to the original will no longer have the
+ * local add. So switch always alerts the user with a tree conflict.
+ *
+ * Allow pulling absent/exluded/not_present nodes back in.
+ *
+ * ### This code is already gearing up to single db with respect to
+ * add-vs.-add conflicts with mismatching node kinds. But before single
+ * db, we cannot deal with mismatching node kinds properly.
+ *
+ * ### We would also like to be checking copyfrom infos to not flag tree
+ * conflicts on two copies with identical history. But at the time of
+ * writing, add_directory() does not get any copyfrom information. */
+ if (! pb->in_deleted_and_tree_conflicted_subtree
+ && (eb->switch_relpath != NULL
+ || local_is_non_dir
+ || local_is_copy
+ )
+ )
+ {
+ SVN_ERR(check_tree_conflict(&tree_conflict, eb,
+ db->local_abspath,
+ svn_wc_conflict_action_add,
+ svn_node_dir, db->new_relpath, pool));
}
- switch (status)
- {
- case svn_wc__db_status_absent:
- case svn_wc__db_status_excluded:
- /* Ignore these hidden states. Allow pulling them (back) in. */
- break;
- case svn_wc__db_status_not_present:
- break;
- case svn_wc__db_status_obstructed:
- case svn_wc__db_status_obstructed_add:
- case svn_wc__db_status_obstructed_delete:
- /* ### In 1.6 we ignored these cases in this block as
- the behavior was just pulling them back in.
-
- Explicitly handle them as not raising a tree conflict
- now. Will never occur once we have a central DB. */
- break;
- case svn_wc__db_status_added:
- /* ### BH: I think this case should be conditional with something
- like allow_unver_obstructions, as this changes the
- base of locally added files.
- ### BH: Always generate tree conflict? */
- db->add_existed = TRUE;
- break;
- default:
- {
- svn_wc_conflict_description2_t *tree_conflict = NULL;
-
- /* Check for conflicts only when we haven't already recorded
- * a tree-conflict on a parent node. */
- if (!db->in_deleted_and_tree_conflicted_subtree)
- SVN_ERR(check_tree_conflict(&tree_conflict, eb,
- db->local_abspath,
- svn_wc_conflict_action_add,
- svn_node_dir, db->new_relpath, pool));
- if (tree_conflict != NULL)
- {
- svn_skel_t *work_item;
-
- /* Queue this conflict in the parent so that its descendants
- are skipped silently. */
- SVN_ERR(svn_wc__loggy_add_tree_conflict(&work_item,
- eb->db,
- pb->local_abspath,
- tree_conflict,
- pool));
- SVN_ERR(svn_wc__db_wq_add(eb->db, pb->local_abspath,
- work_item, pool));
-
- SVN_ERR(remember_skipped_tree(eb, db->local_abspath));
-
- db->skip_this = TRUE;
- db->skip_descendants = TRUE;
- db->already_notified = TRUE;
-
- do_notification(eb, db->local_abspath, svn_node_unknown,
- svn_wc_notify_tree_conflict, pool);
-
- return SVN_NO_ERROR;
- }
- }
- break;
+ if (tree_conflict == NULL)
+ {
+ /* We have a node in WORKING and we've decided not to flag a
+ * conflict, so merge it with the incoming add. */
+ db->add_existed = TRUE;
}
}
-
+ else if (kind != svn_node_none)
{
- /* Immediately create an entry for the new directory in the parent.
- Note that the parent must already be either added or opened, and
- thus it's in an 'incomplete' state just like the new dir.
- The entry may already exist if the new directory is already
- scheduled for addition without history, in that case set
- its schedule to normal. */
- SVN_ERR(svn_wc__db_temp_set_parent_stub_to_normal(eb->db,
- db->local_abspath,
- db->add_existed,
- pool));
+ /* There's an unversioned node at this path. */
+ db->obstruction_found = TRUE;
- if (db->add_existed)
+ /* Unversioned, obstructing dirs are handled by prop merge/conflict,
+ * if unversioned obstructions are allowed. */
+ if (! (kind == svn_node_dir && eb->allow_unver_obstructions))
{
- /* Immediately tweak the schedule for "this dir" so it too
- is no longer scheduled for addition. Change rev from 0
- to the target revision allowing prep_directory() to do
- its thing without error.
-
- ### In the future this should probably become a proper
- ### tree conflict and just handled by putting a base
- ### directory below the existing working node.
- */
- SVN_ERR(svn_wc__db_temp_op_set_new_dir_to_incomplete(
- eb->db,
- db->local_abspath,
+ /* ### Instead of skipping, this should bring in the BASE node
+ * and mark some sort of obstruction-conflict. Come, o single-db! */
+ db->skip_this = TRUE;
+
+ /* If we are skipping an add, we need to tell the WC that
+ * there's a node supposed to be here which we don't have. */
+ SVN_ERR(svn_wc__db_base_add_absent_node(eb->db, db->local_abspath,
db->new_relpath,
eb->repos_root,
eb->repos_uuid,
- *eb->target_revision,
- pool));
+ (eb->target_revision?
+ *eb->target_revision
+ : SVN_INVALID_REVNUM),
+ svn_wc__db_kind_dir,
+ svn_wc__db_status_not_present,
+ NULL, NULL, pool));
+ SVN_ERR(remember_skipped_tree(eb, db->local_abspath));
- SVN_ERR(svn_wc__db_temp_set_parent_stub_to_normal(eb->db,
- db->local_abspath,
- TRUE, pool));
+ /* Mark a conflict */
+ SVN_ERR(create_tree_conflict(&tree_conflict, eb,
+ db->local_abspath,
+ svn_wc_conflict_reason_unversioned,
+ svn_wc_conflict_action_add,
+ svn_node_dir,
+ db->new_relpath, pool, pool));
+ SVN_ERR_ASSERT(tree_conflict != NULL);
}
}
+ if (tree_conflict != NULL)
+ {
+ /* Queue this conflict in the parent so that its descendants
+ are skipped silently. */
+ SVN_ERR(svn_wc__db_op_set_tree_conflict(eb->db, db->local_abspath,
+ tree_conflict, pool));
+
+ SVN_ERR(remember_skipped_tree(eb, db->local_abspath));
+
+ db->skip_this = TRUE;
+ db->skip_descendants = TRUE;
+ db->already_notified = TRUE;
+
+ do_notification(eb, db->local_abspath, svn_node_unknown,
+ svn_wc_notify_tree_conflict, pool);
+ return SVN_NO_ERROR;
+ }
+
+
+ SVN_ERR(svn_wc__db_temp_op_set_new_dir_to_incomplete(eb->db,
+ db->local_abspath,
+ db->new_relpath,
+ eb->repos_root,
+ eb->repos_uuid,
+ *eb->target_revision,
+ db->ambient_depth,
+ pool));
+
SVN_ERR(prep_directory(db,
svn_path_url_add_component2(eb->repos_root,
db->new_relpath, pool),
@@ -2663,7 +2568,7 @@ open_directory(const char *path,
struct dir_baton *db, *pb = parent_baton;
struct edit_baton *eb = pb->edit_baton;
svn_boolean_t have_work;
- svn_boolean_t already_conflicted;
+ svn_boolean_t conflicted;
svn_wc_conflict_description2_t *tree_conflict = NULL;
svn_wc__db_status_t status, base_status;
@@ -2693,7 +2598,7 @@ open_directory(const char *path,
NULL, NULL, NULL, NULL, NULL,
&db->ambient_depth, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL,
- NULL, &have_work, NULL, NULL,
+ NULL, &have_work, &conflicted, NULL,
eb->db, db->local_abspath, pool, pool));
if (!have_work)
@@ -2708,9 +2613,10 @@ open_directory(const char *path,
db->was_incomplete = (base_status == svn_wc__db_status_incomplete);
/* Is this path a conflict victim? */
- SVN_ERR(node_already_conflicted(&already_conflicted, eb->db,
- db->local_abspath, pool));
- if (already_conflicted)
+ if (conflicted)
+ SVN_ERR(node_already_conflicted(&conflicted, eb->db,
+ db->local_abspath, pool));
+ if (conflicted)
{
SVN_ERR(remember_skipped_tree(eb, db->local_abspath));
@@ -2737,13 +2643,9 @@ open_directory(const char *path,
/* Remember the roots of any locally deleted trees. */
if (tree_conflict != NULL)
{
- svn_skel_t *work_item;
-
/* Place a tree conflict into the parent work queue. */
- SVN_ERR(svn_wc__loggy_add_tree_conflict(&work_item,
- eb->db, pb->local_abspath,
+ SVN_ERR(svn_wc__db_op_set_tree_conflict(eb->db, db->local_abspath,
tree_conflict, pool));
- SVN_ERR(svn_wc__db_wq_add(eb->db, pb->local_abspath, work_item, pool));
do_notification(eb, db->local_abspath, svn_node_dir,
svn_wc_notify_tree_conflict, pool);
@@ -2832,54 +2734,6 @@ prop_hash_from_array(const apr_array_hea
return prop_hash;
}
-
-/* Set *WORK_ITEM to a new work item to write an old-style propsfile for the
- specified BASE properties, BASE_PROPS.
-
- ### breaks the props encapsulation by using svn_wc__prop_path, but this
- ### function will only last until we move props into the database. */
-static svn_error_t *
-build_write_base_props(svn_skel_t **work_item,
- const char *local_abspath,
- svn_wc__db_kind_t kind,
- apr_hash_t *base_props,
- apr_pool_t *scratch_pool)
-{
- const char *props_abspath;
-
- SVN_ERR(svn_wc__prop_path(&props_abspath, local_abspath, kind,
- svn_wc__props_base, scratch_pool));
- return svn_error_return(svn_wc__wq_build_write_old_props(work_item,
- props_abspath,
- base_props,
- scratch_pool));
-}
-
-
-/* Set *WORK_ITEM to a new work item to write an old-style propsfile for the
- specified ACTUAL properties. If ACTUAL_PROPS is NULL, then the old-style
- propsfile will be removed, indicating "no change" from the pristines.
-
- ### breaks the props encapsulation by using svn_wc__prop_path, but this
- ### function will only last until we move props into the database. */
-static svn_error_t *
-build_write_actual_props(svn_skel_t **work_item,
- const char *local_abspath,
- svn_wc__db_kind_t kind,
- apr_hash_t *actual_props,
- apr_pool_t *scratch_pool)
-{
- const char *props_abspath;
-
- SVN_ERR(svn_wc__prop_path(&props_abspath, local_abspath, kind,
- svn_wc__props_working, scratch_pool));
- return svn_error_return(svn_wc__wq_build_write_old_props(work_item,
- props_abspath,
- actual_props,
- scratch_pool));
-}
-
-
/* An svn_delta_editor_t function. */
static svn_error_t *
close_directory(void *dir_baton,
@@ -2888,7 +2742,7 @@ close_directory(void *dir_baton,
struct dir_baton *db = dir_baton;
struct edit_baton *eb = db->edit_baton;
svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
- apr_array_header_t *entry_props, *wc_props, *regular_props;
+ apr_array_header_t *entry_props, *dav_props, *regular_props;
apr_hash_t *base_props;
apr_hash_t *actual_props;
apr_hash_t *new_base_props = NULL, *new_actual_props = NULL;
@@ -2911,7 +2765,7 @@ close_directory(void *dir_baton,
return SVN_NO_ERROR;
}
- SVN_ERR(svn_categorize_props(db->propchanges, &entry_props, &wc_props,
+ SVN_ERR(svn_categorize_props(db->propchanges, &entry_props, &dav_props,
®ular_props, pool));
/* Fetch the existing properties. */
@@ -2966,7 +2820,7 @@ close_directory(void *dir_baton,
/* If this directory has property changes stored up, now is the time
to deal with them. */
- if (regular_props->nelts || entry_props->nelts || wc_props->nelts)
+ if (regular_props->nelts || entry_props->nelts || dav_props->nelts)
{
if (regular_props->nelts)
{
@@ -3056,7 +2910,6 @@ close_directory(void *dir_baton,
apr_time_t changed_date;
const char *changed_author;
apr_hash_t *props;
- svn_skel_t *work_items;
/* ### we know a base node already exists. it was created in
### open_directory or add_directory. let's just preserve the
@@ -3078,22 +2931,17 @@ close_directory(void *dir_baton,
if (new_changed_author != NULL)
changed_author = new_changed_author;
+ /* If no depth is set yet, set to infinity. */
+ if (depth == svn_depth_unknown)
+ depth = svn_depth_infinity;
+
/* Do we have new properties to install? Or shall we simply retain
the prior set of properties? If we're installing new properties,
then we also want to write them to an old-style props file. */
props = new_base_props;
if (props == NULL)
- {
- SVN_ERR(svn_wc__db_base_get_props(&props, eb->db, db->local_abspath,
- pool, pool));
- work_items = NULL;
- }
- else
- {
- SVN_ERR(build_write_base_props(&work_items, db->local_abspath,
- svn_wc__db_kind_dir, new_base_props,
- pool));
- }
+ SVN_ERR(svn_wc__db_base_get_props(&props, eb->db, db->local_abspath,
+ pool, pool));
/* ### NOTE: from this point onwards, we make TWO changes to the
### database in a non-transactional way. some kind of revamp
@@ -3109,8 +2957,11 @@ close_directory(void *dir_baton,
changed_rev, changed_date, changed_author,
NULL /* children */,
depth,
+ (dav_props && dav_props->nelts > 0)
+ ? prop_hash_from_array(dav_props, pool)
+ : NULL,
NULL /* conflict */,
- work_items,
+ NULL /* work_items */,
pool));
/* If we updated the BASE properties, then we also have ACTUAL
@@ -3132,25 +2983,12 @@ close_directory(void *dir_baton,
if (prop_diffs->nelts == 0)
props = NULL;
- SVN_ERR(build_write_actual_props(&work_items, db->local_abspath,
- svn_wc__db_kind_dir,
- props,
- pool));
SVN_ERR(svn_wc__db_op_set_props(eb->db, db->local_abspath,
props,
NULL /* conflict */,
- work_items,
+ NULL /* work_items */,
pool));
}
-
- /* Handle the wcprops. */
- if (wc_props && wc_props->nelts > 0)
- {
- SVN_ERR(svn_wc__db_base_set_dav_cache(eb->db, db->local_abspath,
- prop_hash_from_array(wc_props,
- pool),
- pool));
- }
}
/* Process all of the queued work items for this directory. */
@@ -3275,15 +3113,35 @@ absent_directory(const char *path,
}
-/* Beginning at DIR_ABSPATH (from repository with uuid DIR_REPOS_UUID and
- with repos_relpath DIR_REPOS_RELPATH) within a working copy, search the
- working copy for a pre-existing versioned file which is exactly equal
- to COPYFROM_PATH@COPYFROM_REV.
+/* Beginning at DIR_ABSPATH within a working copy, search the working copy
+ copy for a pre-existing versioned file which is exactly equal to
+ COPYFROM_PATH@COPYFROM_REV.
+
+ The current implementation does this by taking the repos_relpath of
+ dir_abspath and copyfrom_relpath to calculate where in the working copy
+ repos_relpath would be and then tries to confirm its guess.
+
+ 1) When it finds a copied file there, it looks for its origin to see
+ if the origin matches the copied file good enough to use it as new base
+ contents and properties. If that is the case set NEW_BASE_CONTENTS
+ and NEW_BASE_PROPS to the found restult.
+
+ If the new base information is found check if the node is tree-conflicted,
+ and when that is the case use its in-wc contents and actual properties
+ to set NEW_CONTENTS and NEW_PROPS.
+
+ (If new base info is found, return)
+
+ 2) If the node's BASE information matches the expected origin matches the the
+ copy origin good enough use it as NEW_BASE_CONTENTS and NEW_BASE_PROPS.
- If the file isn't found, set *RETURN_ABSPATH to NULL.
+ If the new base information is found and the db_status of the node is normal,
+ then set NEW_CONTENTS and NEW_PROPS with the found values.
- If the file is found, return the absolute path to it in
- *RETURN_ABSPATH.
+ If data is not found, its values will be set to NULL.
+
+ Allocate the return values in RESULT_POOL, but perform all temporary allocations
+ in SCRATCH_POOL.
### With a centralized datastore this becomes much easier. For now we
### keep the old algorithm because the result is also used for copying
@@ -3291,32 +3149,40 @@ absent_directory(const char *path,
### local file moves.
*/
static svn_error_t *
-locate_copyfrom(svn_wc__db_t *db,
- const char *copyfrom_path,
- svn_revnum_t copyfrom_rev,
+locate_copyfrom(svn_stream_t **new_base_contents,
+ svn_stream_t **new_contents,
+ apr_hash_t **new_base_props,
+ apr_hash_t **new_props,
+ svn_wc__db_t *db,
const char *dir_abspath,
- const char *dir_repos_uuid,
- const char *dir_repos_relpath,
- const char **return_abspath,
+ const char *copyfrom_relpath,
+ svn_revnum_t copyfrom_rev,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *ancestor_abspath, *ancestor_relpath;
- const char *copyfrom_relpath;
+ const char *dir_repos_relpath, *dir_repos_root_url, *dir_repos_uuid;
+ const char *repos_relpath, *repos_root_url, *repos_uuid;
+ const char *local_abspath;
+
apr_size_t levels_up;
- const char *file_abspath;
svn_error_t *err;
- SVN_ERR_ASSERT(dir_repos_relpath && dir_repos_uuid);
- SVN_ERR_ASSERT(copyfrom_path[0] == '/');
+ SVN_ERR_ASSERT(copyfrom_relpath[0] != '/');
+
+ SVN_ERR(svn_wc__db_scan_base_repos(&dir_repos_relpath, &dir_repos_root_url,
+ &dir_repos_uuid,
+ db, dir_abspath,
+ scratch_pool, scratch_pool));
/* Be pessimistic. This function is basically a series of tests
that gives dozens of ways to fail our search, returning
SVN_NO_ERROR in each case. If we make it all the way to the
bottom, we have a real discovery to return. */
- *return_abspath = NULL;
-
- copyfrom_relpath = copyfrom_path+1; /* Skip the initial '/' */
+ *new_base_contents = NULL;
+ *new_contents = NULL;
+ *new_base_props = NULL;
+ *new_props = NULL;
/* Subtract the dest_dir's URL from the repository "root" URL to get
the absolute FS path represented by dest_dir. */
@@ -3336,118 +3202,229 @@ locate_copyfrom(svn_wc__db_t *db,
ancestor_abspath = svn_dirent_dirname(ancestor_abspath, scratch_pool);
/* Verify hypothetical ancestor */
- {
- const char *repos_relpath, *repos_uuid;
-
- err = svn_wc__db_scan_base_repos(&repos_relpath, NULL, &repos_uuid,
- db, ancestor_abspath,
- scratch_pool, scratch_pool);
-
- if (err && ((err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY) ||
- (err->apr_err == SVN_ERR_WC_PATH_FOUND)))
- {
- svn_error_clear(err);
- return SVN_NO_ERROR;
- }
- else
- SVN_ERR(err);
+ err = svn_wc__db_scan_base_repos(&repos_relpath, &repos_root_url,
+ &repos_uuid,
+ db, ancestor_abspath,
+ scratch_pool, scratch_pool);
- /* If we got this far, we know that the ancestor dir exists, and
- that it's a working copy too. But is it from the same
- repository? And does it represent the URL we expect it to? */
- if (strcmp(dir_repos_uuid, repos_uuid) != 0)
+ if (err && ((err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY) ||
+ (err->apr_err == SVN_ERR_WC_PATH_FOUND)))
+ {
+ svn_error_clear(err);
return SVN_NO_ERROR;
+ }
+ else
+ SVN_ERR(err);
- if (strcmp(ancestor_relpath, repos_relpath) != 0)
- return SVN_NO_ERROR;
- }
+ /* If we got this far, we know that the ancestor dir exists, and
+ that it's a working copy too. But is it from the same
+ repository? And does it represent the URL we expect it to? */
+ if ((strcmp(dir_repos_uuid, repos_uuid) != 0)
+ || (strcmp(dir_repos_root_url, repos_root_url) != 0)
+ || (strcmp(ancestor_relpath, repos_relpath) != 0))
+ return SVN_NO_ERROR;
/* Add the remaining components to cwd, then add the remaining relpath to
where we hope the copyfrom_relpath file exists. */
- file_abspath = svn_dirent_join(ancestor_abspath,
+ local_abspath = svn_dirent_join(ancestor_abspath,
svn_dirent_skip_ancestor(ancestor_relpath,
copyfrom_relpath),
scratch_pool);
/* Verify file in expected location */
{
- svn_node_kind_t kind;
- svn_revnum_t addition_rev = SVN_INVALID_REVNUM;
- const char *repos_relpath, *repos_uuid;
-
- /* First: does the proposed file path even exist? */
- SVN_ERR(svn_io_check_path(file_abspath, &kind, scratch_pool));
- if (kind != svn_node_file)
- return SVN_NO_ERROR;
+ svn_revnum_t rev, changed_rev;
+ svn_wc__db_status_t status, base_status;
+ svn_boolean_t conflicted, have_base;
+ const svn_checksum_t *checksum;
- /* Next: is the file under version control? */
- err = svn_wc__db_scan_base_repos(&repos_relpath, NULL, &repos_uuid,
- db, file_abspath,
- scratch_pool, scratch_pool);
+ err = svn_wc__db_read_info(&status, NULL, &rev, &repos_relpath,
+ &repos_root_url, &repos_uuid, &changed_rev,
+ NULL, NULL, NULL, NULL, &checksum, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, &have_base,
+ NULL, &conflicted, NULL,
+ db, local_abspath, scratch_pool, scratch_pool);
- if (err && ((err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY) ||
- (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)))
+ if (err && ((err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY ||
+ (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND))))
{
svn_error_clear(err);
+ return SVN_NO_ERROR;
+ }
+ else
+ SVN_ERR(err);
- /* ### Our entries handling made us handle the following
- scenario: An older version of a file was copied at
- exactly the expected location. Reproduced this behavior
- until we can really querry the entire workingcopy. */
-
- err = svn_wc__db_scan_addition(NULL, NULL, NULL, NULL, NULL,
- &repos_relpath, NULL, &repos_uuid,
- &addition_rev, db, file_abspath,
- scratch_pool, scratch_pool);
+ /* Check if we have an added node with the right copyfrom information, as
+ this is what you would see on a file move. */
+
+ if (status == svn_wc__db_status_added)
+ {
+ const char *op_root_abspath;
+ const char *original_repos_relpath, *original_root_url;
+ const char *original_uuid;
+ svn_revnum_t original_rev;
+
+ SVN_ERR(svn_wc__db_scan_addition(&status, &op_root_abspath,
+ &repos_relpath, &repos_root_url,
+ &repos_uuid, &original_repos_relpath,
+ &original_root_url, &original_uuid,
+ &original_rev,
+ db, local_abspath,
+ scratch_pool, scratch_pool));
- if (err && ((err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY ||
- (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND))))
+ if (status == svn_wc__db_status_copied
+ || status == svn_wc__db_status_moved_here)
{
- svn_error_clear(err);
+ original_repos_relpath = svn_relpath_join(
+ original_repos_relpath,
+ svn_dirent_skip_ancestor(op_root_abspath,
+ local_abspath),
+ scratch_pool);
- return SVN_NO_ERROR;
+ /* If the repository location matches our exact guess and
[... 2379 lines stripped ...]