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/08/11 18:43:31 UTC
svn commit: r984468 [14/25] - in /subversion/branches/ignore-mergeinfo: ./
build/ build/generator/ build/generator/templates/ notes/
notes/tree-conflicts/ notes/wc-ng/ subversion/bindings/javahl/native/
subversion/bindings/javahl/src/org/apache/subvers...
Modified: subversion/branches/ignore-mergeinfo/subversion/libsvn_wc/update_editor.c
URL: http://svn.apache.org/viewvc/subversion/branches/ignore-mergeinfo/subversion/libsvn_wc/update_editor.c?rev=984468&r1=984467&r2=984468&view=diff
==============================================================================
--- subversion/branches/ignore-mergeinfo/subversion/libsvn_wc/update_editor.c (original)
+++ subversion/branches/ignore-mergeinfo/subversion/libsvn_wc/update_editor.c Wed Aug 11 16:43:22 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,25 +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 "entries.h"
-#include "lock.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) \
@@ -196,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;
@@ -503,20 +492,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;
@@ -724,15 +702,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;
@@ -747,38 +720,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;
@@ -840,6 +801,7 @@ complete_directory(struct edit_baton *eb
node_abspath = svn_dirent_join(local_abspath, name, iterpool);
+#ifndef SVN_WC__SINGLE_DB
/* ### 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.
@@ -857,12 +819,16 @@ complete_directory(struct edit_baton *eb
### 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. */
+#endif
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);
+#ifdef SVN_WC__SINGLE_DB
+ SVN_ERR(err);
+#else
if (err)
{
if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
@@ -874,6 +840,7 @@ complete_directory(struct edit_baton *eb
iterpool));
continue;
}
+#endif
/* ### obsolete comment?
Any entry still marked as deleted (and not schedule add) can now
@@ -913,9 +880,16 @@ complete_directory(struct edit_baton *eb
{
SVN_ERR(svn_wc__db_base_remove(eb->db, node_abspath, iterpool));
}
+#ifndef SVN_WC__SINGLE_DB
+ /* In Single-DB mode, administrative data is never reported as missing
+ by the adm crawler, and we should always remove nodes using normal
+ update handling.
+ In !Single-DB mode the nodes should have been re-added by now,
+ so we can assume that the repository doesn't know about them. */
else if (kind == svn_wc__db_kind_dir
- && svn_wc__adm_missing(eb->db, node_abspath, iterpool)
- && status != svn_wc__db_status_absent)
+ && (status == svn_wc__db_status_obstructed
+ || status == svn_wc__db_status_obstructed_delete
+ || status == svn_wc__db_status_obstructed_add))
{
SVN_ERR(svn_wc__db_temp_op_remove_entry(eb->db, node_abspath,
iterpool));
@@ -926,6 +900,7 @@ complete_directory(struct edit_baton *eb
: svn_node_file,
iterpool);
}
+#endif
}
svn_pool_destroy(iterpool);
@@ -1012,27 +987,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
@@ -1058,7 +1020,9 @@ 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 */
@@ -1123,15 +1087,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;
-#ifdef SVN_EXPERIMENTAL_PRISTINE
svn_wc__db_t *db = fb->edit_baton->db;
-#endif
svn_error_t *err;
/* Apply this window. We may be done at that point. */
@@ -1169,30 +1133,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);
-#ifdef SVN_EXPERIMENTAL_PRISTINE
/* 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, fb->new_text_base_tmp_abspath,
+ 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_ERR(svn_wc__db_pristine_get_path(&fb->new_text_base_tmp_abspath,
- db, fb->local_abspath,
- fb->new_text_base_sha1_checksum,
- fb->pool, hb->pool));
-#endif
}
svn_pool_destroy(hb->pool);
@@ -1213,15 +1167,18 @@ prep_directory(struct dir_baton *db,
svn_revnum_t ancestor_revision,
apr_pool_t *pool)
{
- const char *repos_root;
const char *dir_abspath;
+#ifndef SINGLE_DB
+ const char *repos_root;
svn_boolean_t locked_here;
+#endif
dir_abspath = db->local_abspath;
/* Make sure the directory exists. */
SVN_ERR(svn_wc__ensure_directory(dir_abspath, pool));
+#ifndef SINGLE_DB
/* 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))
@@ -1242,7 +1199,8 @@ prep_directory(struct dir_baton *db,
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));
+ dir_abspath, FALSE, pool, pool));
+#endif
return SVN_NO_ERROR;
}
@@ -1314,9 +1272,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;
+
+ SVN_ERR(svn_dirent_is_under_root(&under_root, &full_path, base_path, add_path, pool));
- if (! svn_dirent_is_under_root(&full_path, base_path, add_path, pool))
+ if (! under_root)
{
return svn_error_createf(
SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
@@ -1427,36 +1388,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. */
@@ -1522,7 +1453,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));
@@ -1906,7 +1839,7 @@ 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))
+ if (status != svn_wc__db_status_obstructed)
SVN_ERR(tree_has_local_mods(&modified, &all_mods_are_deletes,
eb->db, local_abspath,
eb->cancel_func, eb->cancel_baton,
@@ -2004,58 +1937,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);
}
@@ -2129,18 +2059,23 @@ do_entry_deletion(struct edit_baton *eb,
apr_pool_t *pool)
{
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(NULL, &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));
@@ -2178,12 +2113,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));
@@ -2194,23 +2136,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)
{
@@ -2224,31 +2159,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
@@ -2256,58 +2179,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));
@@ -2369,7 +2255,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
@@ -2445,25 +2333,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(
@@ -2476,8 +2345,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)
{
@@ -2487,48 +2356,97 @@ add_directory(const char *path,
svn_error_clear(err);
wc_kind = svn_wc__db_kind_unknown;
status = svn_wc__db_status_normal;
+
+ versioned_locally_and_present = FALSE;
}
+ else
+ versioned_locally_and_present = IS_NODE_PRESENT(status);
- /* 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)))
+ /* 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)
{
- 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 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;
+ }
}
- if (kind == svn_node_dir &&
- (wc_kind == svn_wc__db_kind_unknown || !IS_NODE_PRESENT(status)))
+ /* Now the "usual" behaviour if already conflicted. Skip it. */
+ if (conflicted)
{
- /* Found an unversioned directory */
- db->obstruction_found = TRUE;
+ /* Record this conflict so that its descendants are skipped silently. */
+ SVN_ERR(remember_skipped_tree(eb, db->local_abspath));
- if (!eb->allow_unver_obstructions)
- {
- db->already_notified = TRUE;
- do_notification(eb, db->local_abspath, svn_node_dir,
- svn_wc_notify_update_obstruction, pool);
+ db->skip_this = TRUE;
+ db->skip_descendants = TRUE;
+ db->already_notified = TRUE;
- 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));
- }
+ /* ### 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;
}
- else if (wc_kind == svn_wc__db_kind_dir)
+
+
+ if (versioned_locally_and_present)
{
- /* Directory exists */
- if (IS_NODE_PRESENT(status) &&
- status != svn_wc__db_status_deleted)
+ /* What to do with a versioned or schedule-add dir:
+
+ 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. */
+
+ 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;
@@ -2570,91 +2488,144 @@ add_directory(const char *path,
}
}
- /* What to do with a versioned or schedule-add dir:
-
- 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.
+ /* 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));
+ }
- An existing versioned dir is an error. In the future we may
- relax this restriction and simply update such dirs.
+ /* 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));
+ }
- A dir added with history is a tree conflict. */
- if (status == svn_wc__db_status_added)
+ if (tree_conflict == NULL)
{
- /* 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));
+ /* 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;
+
+ /* Pre-single-db, a dir that was OS-deleted from the working copy
+ * along with its .svn folder is seen 'obstructed' in this code
+ * path. The particular situation however better matches the word
+ * 'missing'. We do add_existed to avoid spurious errors where other
+ * code relies on add_existed to be TRUE when there is a node
+ * record (schedule_tests delete_redelete_fudgery used to XFail).
+ * Still, let's notify 'A' as the old client did. Ultimately, this
+ * should probably say 'Restored' instead of 'A', like with file. */
+ if (status == svn_wc__db_status_obstructed
+ || status == svn_wc__db_status_obstructed_add
+ || status == svn_wc__db_status_obstructed_delete)
+ {
+ db->already_notified = TRUE;
+ do_notification(eb, db->local_abspath, svn_node_dir,
+ svn_wc_notify_add, pool);
+ }
}
+ }
+ else if (kind != svn_node_none)
+ {
+ /* There's an unversioned node at this path. */
+ db->obstruction_found = TRUE;
- switch (status)
+ /* Unversioned, obstructing dirs are handled by prop merge/conflict,
+ * if unversioned obstructions are allowed. */
+ if (! (kind == svn_node_dir && eb->allow_unver_obstructions))
{
- 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.
+ /* ### 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;
- 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;
+ /* 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?
+ *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));
- /* 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));
+ /* 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)
- {
- svn_skel_t *work_item;
+ 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));
- /* 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;
+ SVN_ERR(remember_skipped_tree(eb, db->local_abspath));
- do_notification(eb, db->local_abspath, svn_node_unknown,
- svn_wc_notify_tree_conflict, pool);
+ db->skip_this = TRUE;
+ db->skip_descendants = TRUE;
+ db->already_notified = TRUE;
- return SVN_NO_ERROR;
- }
- }
- break;
- }
+ do_notification(eb, db->local_abspath, svn_node_unknown,
+ svn_wc_notify_tree_conflict, pool);
+ return SVN_NO_ERROR;
}
+
+#ifdef SINGLE_DB
+ 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));
+#else
{
/* Immediately create an entry for the new directory in the parent.
Note that the parent must already be either added or opened, and
@@ -2685,6 +2656,7 @@ add_directory(const char *path,
eb->repos_root,
eb->repos_uuid,
*eb->target_revision,
+ db->ambient_depth,
pool));
SVN_ERR(svn_wc__db_temp_set_parent_stub_to_normal(eb->db,
@@ -2692,6 +2664,7 @@ add_directory(const char *path,
TRUE, pool));
}
}
+#endif
SVN_ERR(prep_directory(db,
svn_path_url_add_component2(eb->repos_root,
@@ -2744,7 +2717,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;
@@ -2774,7 +2747,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)
@@ -2789,9 +2762,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));
@@ -2818,13 +2792,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);
@@ -2913,71 +2883,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));
-}
-
-static svn_error_t *
-build_write_revert_base_props(svn_skel_t **work_item,
- const char *local_abspath,
- svn_wc__db_kind_t kind,
- apr_hash_t *revert_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_revert, scratch_pool));
- return svn_error_return(svn_wc__wq_build_write_old_props(work_item,
- props_abspath,
- revert_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,
@@ -2986,7 +2891,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;
@@ -3009,7 +2914,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. */
@@ -3064,7 +2969,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)
{
@@ -3154,7 +3059,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
@@ -3176,22 +3080,19 @@ close_directory(void *dir_baton,
if (new_changed_author != NULL)
changed_author = new_changed_author;
+#ifdef SVN_WC__SINGLE_DB
+ /* If no depth is set yet, set to infinity. */
+ if (depth == svn_depth_unknown)
+ depth = svn_depth_infinity;
+#endif
+
/* 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
@@ -3207,8 +3108,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
@@ -3230,25 +3134,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. */
@@ -3373,15 +3264,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 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 isn't found, set *RETURN_ABSPATH to NULL.
+ If data is not found, its values will be set to NULL.
- If the file is found, return the absolute path to it in
- *RETURN_ABSPATH.
+ 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
@@ -3389,32 +3300,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. */
@@ -3434,118 +3353,227 @@ 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, &repos_root_url,
+ &repos_uuid,
+ db, ancestor_abspath,
+ scratch_pool, scratch_pool);
- 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);
-
- /* 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;
+ }
- /* ### 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);
+
+ /* If the repository location matches our exact guess and
+ the file's recorded revisions tell us that the file had the
+ same contents at the copyfrom_revision, we can use this
+ data as new_base */
+ if (strcmp(original_repos_relpath, copyfrom_relpath) == 0
+ && strcmp(original_root_url, dir_repos_root_url) == 0
+ && strcmp(original_uuid, dir_repos_uuid) == 0
+ && strcmp(repos_relpath, copyfrom_relpath) == 0
+ && strcmp(repos_root_url, dir_repos_root_url) == 0
+ && strcmp(repos_uuid, dir_repos_uuid) == 0
+
+ && SVN_IS_VALID_REVNUM(changed_rev)
+ && changed_rev <= copyfrom_rev
+ && copyfrom_rev <= original_rev)
+ {
+ svn_node_kind_t kind;
+ svn_boolean_t text_changed;
+
+ /* WORKING_NODE has the right new-BASE information,
+ so we have at least a partial result. */
+ SVN_ERR(svn_wc__db_pristine_read(new_base_contents,
+ db, local_abspath, checksum,
+ result_pool, scratch_pool));
+ SVN_ERR(svn_wc__get_pristine_props(new_base_props,
+ db, local_abspath,
+ result_pool, scratch_pool));
+
+ /* If the node is conflicted, that might have happened because
+ the node was deleted. Which might indicate that we have
+ a file move. In this case we like the real file data */
+ if (!conflicted
+ && status == svn_wc__db_status_copied)
+ return SVN_NO_ERROR; /* A local copy is no local modification
+ that we should keep */
+
+ /* ### TODO: Add verification to check that the conflict
+ tells us that this is the right thing to do.
+
+ ### Pre 1.7 we just assumed that it is ok without checking for
+ conflicts, so this is not a regression */
+
+ SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool));
- return SVN_NO_ERROR;
+ if (kind != svn_node_file)
+ return SVN_NO_ERROR; /* Nothing to copy */
+
+ SVN_ERR(svn_wc__internal_text_modified_p(&text_changed, db,
+ local_abspath, FALSE,
+ TRUE, scratch_pool));
+
+ if (!text_changed)
+ return SVN_NO_ERROR; /* Take the easy route */
+
+ SVN_ERR(svn_stream_open_readonly(new_contents, local_abspath,
+ result_pool, scratch_pool));
+
+ SVN_ERR(svn_wc__get_actual_props(new_props, db, local_abspath,
+ result_pool, scratch_pool));
+
+ return SVN_NO_ERROR;
+ }
}
- else
- SVN_ERR(err);
}
- else
- SVN_ERR(err);
- if (strcmp(dir_repos_uuid, repos_uuid))
+ if (!have_base)
return SVN_NO_ERROR;
- if (strcmp(copyfrom_relpath, repos_relpath))
+ base_status = status;
+
+ if (status != svn_wc__db_status_normal)
+ SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &rev,
+ &repos_relpath, &repos_root_url,
+ &repos_uuid, &changed_rev, NULL,
+ NULL, NULL, NULL, &checksum, NULL,
+ NULL, NULL,
+ db, local_abspath,
+ scratch_pool, scratch_pool));
+
+ if (base_status != svn_wc__db_status_normal)
+ return SVN_NO_ERROR; /* No interesting BASE_NODE */
+
+ if (!repos_relpath || !repos_root_url || !repos_uuid)
+ SVN_ERR(svn_wc__db_scan_base_repos(&repos_relpath, &repos_root_url,
+ &repos_uuid,
+ db, local_abspath,
+ scratch_pool, scratch_pool));
+
+ /* Is it from the same repository */
+ if ((strcmp(dir_repos_uuid, repos_uuid) != 0)
+ || (strcmp(dir_repos_root_url, repos_root_url) != 0)
+ || (strcmp(copyfrom_relpath, repos_relpath) != 0))
return SVN_NO_ERROR;
- if (SVN_IS_VALID_REVNUM(addition_rev) && addition_rev == copyfrom_rev)
+ /* Ok, we know that we look at the right node, but do we have the
+ right revision?
+
+ To be sure that the base node has the right properties and text,
+ the node must be the same in copyfrom_rev and changed_rev, which
+ is only true within this specific range
+ */
+ if (!(SVN_IS_VALID_REVNUM(changed_rev)
+ && changed_rev <= copyfrom_rev
+ && copyfrom_rev <= rev))
{
- /* We found the right file as copy source */
- *return_abspath = apr_pstrdup(result_pool, file_abspath);
return SVN_NO_ERROR;
}
- }
- /* Do we actually have valid revisions for the file? (See Issue
- #2977.) */
- {
- svn_revnum_t wc_rev, change_rev;
+ /* BASE_NODE has the right new-BASE information,
+ so we have at least a partial result. */
+ SVN_ERR(svn_wc__db_pristine_read(new_base_contents,
+ db, local_abspath, checksum,
+ result_pool, scratch_pool));
- SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &wc_rev, NULL, NULL, NULL,
- &change_rev, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, db, file_abspath,
- scratch_pool, scratch_pool));
+ SVN_ERR(svn_wc__db_base_get_props(new_base_props,
+ db, local_abspath, result_pool,
+ scratch_pool));
- if (!SVN_IS_VALID_REVNUM(wc_rev) || !SVN_IS_VALID_REVNUM(change_rev))
- return SVN_NO_ERROR;
+ /* If the node is in status normal, the user probably intended to make
+ a copy of this in-wc node, so copy its local changes over to
+ the new file. */
+ if (status == svn_wc__db_status_normal)
+ {
+ svn_node_kind_t kind;
+ svn_boolean_t text_changed;
- /* Do we have the the right *version* of the file? */
- if (! ((change_rev <= copyfrom_rev) && (copyfrom_rev <= wc_rev)))
- return SVN_NO_ERROR;
- }
+ SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool));
+
+ if (kind != svn_node_file)
+ return SVN_NO_ERROR; /* Nothing to copy */
+
+ SVN_ERR(svn_wc__internal_text_modified_p(&text_changed, db,
+ local_abspath, FALSE,
+ TRUE, scratch_pool));
- /* Success! We found the exact file we wanted! */
- *return_abspath = apr_pstrdup(result_pool, file_abspath);
+ if (!text_changed)
+ return SVN_NO_ERROR; /* Take the easy route */
+ SVN_ERR(svn_stream_open_readonly(new_contents, local_abspath,
+ result_pool, scratch_pool));
+
+ SVN_ERR(svn_wc__get_actual_props(new_props, db, local_abspath,
+ result_pool, scratch_pool));
+ }
+ }
return SVN_NO_ERROR;
}
@@ -3595,94 +3623,50 @@ add_file_with_history(struct dir_baton *
const char *copyfrom_path,
svn_revnum_t copyfrom_rev,
struct file_baton *tfb,
- apr_pool_t *pool)
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
struct edit_baton *eb = pb->edit_baton;
- apr_hash_t *base_props, *working_props;
- svn_error_t *err;
svn_stream_t *copied_stream;
- const char *src_local_abspath;
+ const char *copied_text_base_tmp_abspath;
svn_wc__db_t *db = eb->db;
- const char *dir_repos_relpath, *dir_repos_root, *dir_repos_uuid;
+ svn_stream_t *new_base_contents, *new_contents;
+ apr_hash_t *new_base_props, *new_props;
- /* The file_pool can stick around for a *long* time, so we want to
- use a subpool for any temporary allocations. */
- apr_pool_t *subpool = svn_pool_create(pool);
+ SVN_ERR_ASSERT(copyfrom_path[0] == '/');
tfb->added_with_history = TRUE;
/* Attempt to locate the copyfrom_path in the working copy first. */
-
- SVN_ERR(svn_wc__db_scan_base_repos(&dir_repos_relpath, &dir_repos_root,
- &dir_repos_uuid, db, pb->local_abspath,
- subpool, subpool));
-
- err = locate_copyfrom(eb->db, copyfrom_path, copyfrom_rev, pb->local_abspath,
- dir_repos_uuid, dir_repos_relpath,
- &src_local_abspath, subpool, subpool);
-
- if (err && err->apr_err == SVN_ERR_WC_COPYFROM_PATH_NOT_FOUND)
- svn_error_clear(err);
- else
- SVN_ERR(err);
+ SVN_ERR(locate_copyfrom(&new_base_contents, &new_contents,
+ &new_base_props, &new_props,
+ db, pb->local_abspath,
+ copyfrom_path+1, /* Create repos_relpath */
+ copyfrom_rev, result_pool, scratch_pool));
/* Open the text base for writing (this will get us a temporary file). */
SVN_ERR(svn_wc__open_writable_base(&copied_stream,
- &tfb->copied_text_base_abspath,
+ &copied_text_base_tmp_abspath,
/* Compute an MD5 checksum for the stream as we write stuff into it.
### this is temporary. in many cases, we already *know* the checksum
### since it is a copy. */
&tfb->copied_text_base_md5_checksum,
&tfb->copied_text_base_sha1_checksum,
db, pb->local_abspath,
- pool, pool));
+ result_pool, scratch_pool));
- if (src_local_abspath != NULL) /* Found a file to copy */
- {
- /* Copy the existing file's text-base over to the (temporary)
- new text-base, where the file baton expects it to be. Get
- the text base and props from the usual place or from the
- revert place, depending on scheduling. */
- svn_stream_t *source_text_base;
- const svn_wc_entry_t *src_entry;
-
- SVN_ERR(svn_wc__get_entry(&src_entry, db, src_local_abspath, FALSE,
- svn_node_file, FALSE, subpool, subpool));
-
- if (src_entry->schedule == svn_wc_schedule_add)
- SVN_ERR(svn_wc__get_pristine_contents(&source_text_base,
- db, src_local_abspath,
- subpool, subpool));
- else
- SVN_ERR(svn_wc__get_ultimate_base_contents(&source_text_base,
- db, src_local_abspath,
- subpool, subpool));
-
- /* If this has no base, should we use an empty stream?
- * This assert wants to verify that there are no such callers. */
- SVN_ERR_ASSERT(source_text_base != NULL);
-
- if (src_entry->schedule == svn_wc_schedule_replace
- && src_entry->copyfrom_url)
- {
- SVN_ERR(svn_wc__get_revert_props(&base_props, db,
- src_local_abspath, pool, subpool));
- /* The old working props are lost, just like the old
- working file text is. Just use the base props. */
- working_props = base_props;
- }
- else
- {
- SVN_ERR(svn_wc__get_pristine_props(&base_props,
- db, src_local_abspath,
- pool, subpool));
- SVN_ERR(svn_wc__get_actual_props(&working_props,
- db, src_local_abspath,
- pool, subpool));
- }
+ if (new_base_contents && new_base_props)
+ {
+ /* Copy the existing file's text-base over to the (temporary)
+ new text-base, where the file baton expects it to be. Get
+ the text base and props from the usual place or from the
+ revert place, depending on scheduling. */
+ SVN_ERR(svn_stream_copy3(new_base_contents, copied_stream,
+ eb->cancel_func, eb->cancel_baton,
+ scratch_pool));
- SVN_ERR(svn_stream_copy3(source_text_base, copied_stream,
- eb->cancel_func, eb->cancel_baton, pool));
+ if (!new_props)
+ new_props = new_base_props;
}
else /* Couldn't find a file to copy */
{
@@ -3699,55 +3683,49 @@ add_file_with_history(struct dir_baton *
to the root of the repository so skip the first '/'. */
SVN_ERR(eb->fetch_func(eb->fetch_baton, copyfrom_path + 1, copyfrom_rev,
copied_stream,
- NULL, &base_props, pool));
+ NULL, &new_base_props, scratch_pool));
SVN_ERR(svn_stream_close(copied_stream));
- working_props = base_props;
+
+ /* Filter out wc-props */
+ /* ### Do we get new values as modification or should these really
+ be installed? */
+ new_base_props = svn_prop_hash_dup(copy_regular_props(new_base_props,
+ scratch_pool),
+ result_pool);
+
+ new_props = new_base_props;
}
-#ifdef SVN_EXPERIMENTAL_PRISTINE
- /* The Pristine Store way: copied_text_base_abspath points to the
- * installed pristine text. */
- SVN_ERR(svn_wc__db_pristine_install(db, tfb->copied_text_base_abspath,
+ SVN_ERR(svn_wc__db_pristine_install(db, copied_text_base_tmp_abspath,
tfb->copied_text_base_sha1_checksum,
tfb->copied_text_base_md5_checksum,
- subpool));
- /* Update the reference to the path where we can read the file, now
- * that it's moved into the pristine store. */
- SVN_ERR(svn_wc__db_pristine_get_path(&tfb->copied_text_base_abspath,
- db, tfb->local_abspath,
- tfb->copied_text_base_sha1_checksum,
- tfb->pool, subpool));
-#endif
+ scratch_pool));
- /* Loop over whatever props we have in memory, and add all
- regular props to hashes in the baton. Skip entry and wc
- properties, these are only valid for the original file. */
- tfb->copied_base_props = copy_regular_props(base_props, pool);
- tfb->copied_working_props = copy_regular_props(working_props, pool);
+ tfb->copied_base_props = new_base_props;
- if (src_local_abspath != NULL)
+ if (new_contents)
{
/* If we copied an existing file over, we need to copy its
working text too, to preserve any local mods. (We already
read its working *props* into tfb->copied_working_props.) */
- svn_boolean_t text_changed;
+ const char *temp_dir_abspath;
+ svn_stream_t *tmp_contents;
- SVN_ERR(svn_wc__internal_text_modified_p(&text_changed, eb->db,
- src_local_abspath, FALSE,
- TRUE, subpool));
+ /* Make a unique file name for the copied working text. */
+ SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath,
+ db, pb->local_abspath,
+ scratch_pool, scratch_pool));
- if (text_changed)
- {
- /* Make a unique file name for the copied working text. */
- SVN_ERR(get_empty_tmp_file(&tfb->copied_working_text, eb->db,
- pb->local_abspath, pool, pool));
+ SVN_ERR(svn_stream_open_unique(&tmp_contents, &tfb->copied_working_text,
+ temp_dir_abspath, svn_io_file_del_none,
+ result_pool, scratch_pool));
- SVN_ERR(svn_io_copy_file(src_local_abspath, tfb->copied_working_text,
- TRUE, subpool));
- }
- }
+ SVN_ERR(svn_stream_copy3(new_contents, tmp_contents, eb->cancel_func,
+ eb->cancel_baton,
+ scratch_pool));
- svn_pool_destroy(subpool);
+ tfb->copied_working_props = new_props;
+ }
return SVN_NO_ERROR;
}
@@ -3769,15 +3747,11 @@ add_file(const char *path,
svn_wc__db_kind_t wc_kind;
svn_wc__db_status_t status;
apr_pool_t *subpool;
- svn_boolean_t already_conflicted;
+ svn_boolean_t conflicted;
svn_boolean_t versioned_locally_and_present;
svn_error_t *err;
svn_wc_conflict_description2_t *tree_conflict = NULL;
- /* Skip the initial '/' */
- const char *copyfrom_relpath = (copyfrom_path && copyfrom_path[0] ?
- copyfrom_path+1 : copyfrom_path);
-
/* Semantic check. Either both "copyfrom" args are valid, or they're
NULL and SVN_INVALID_REVNUM. A mixture is illegal semantics. */
SVN_ERR_ASSERT((copyfrom_path && SVN_IS_VALID_REVNUM(copyfrom_rev))
@@ -3819,8 +3793,8 @@ add_file(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, fb->local_abspath, subpool, subpool);
if (err)
@@ -3839,9 +3813,10 @@ add_file(const char *path,
/* Is this path a conflict victim? */
- SVN_ERR(node_already_conflicted(&already_conflicted, eb->db,
- fb->local_abspath, subpool));
- if (already_conflicted)
+ if (conflicted)
+ SVN_ERR(node_already_conflicted(&conflicted, eb->db,
+ fb->local_abspath, subpool));
+ if (conflicted)
{
svn_boolean_t do_skip = TRUE;
@@ -3897,9 +3872,8 @@ add_file(const char *path,
If the UUID doesn't match the parent's, or the URL isn't a child of
the parent dir's URL, it's an error.
- A file with matching history is OK. Set add_existed so that
- user notification is delayed until after any text or prop conflicts
- have been found.
+ Set add_existed so that user notification is delayed until after any
+ text or prop conflicts have been found.
Whether the incoming add is a symlink or a file will only be known in
close_file(), when the props are known. So with a locally added file
@@ -3908,32 +3882,32 @@ add_file(const char *path,
We will never see missing files here, because these would be
re-added during the crawler phase. */
svn_boolean_t local_is_file;
- svn_boolean_t local_is_non_file;
svn_boolean_t is_file_external;
- const char *local_copyfrom_repos_relpath = NULL;
- svn_revnum_t local_copyfrom_rev = SVN_INVALID_REVNUM;
- /* Is the local add a copy, and where from? */
+ /* Is the local node a copy or move */
if (status == svn_wc__db_status_added)
- SVN_ERR(svn_wc__node_get_copyfrom_info(NULL,
- &local_copyfrom_repos_relpath,
- NULL,
- &local_copyfrom_rev,
- NULL,
- eb->wc_ctx,
- fb->local_abspath,
- subpool, subpool));
-
+ SVN_ERR(svn_wc__db_scan_addition(&status, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL,
+ eb->db, fb->local_abspath,
+ subpool, subpool));
/* Is there something that is a file? */
- local_is_file = ((wc_kind == svn_wc__db_kind_file
- || wc_kind == svn_wc__db_kind_symlink)
- && status != svn_wc__db_status_deleted);
+ local_is_file = (wc_kind == svn_wc__db_kind_file
+ || wc_kind == svn_wc__db_kind_symlink);
+#ifndef SVN_WC__SINGLE_DB
/* Is there *something* that is not a file? */
- local_is_non_file = ((wc_kind == svn_wc__db_kind_dir
- || wc_kind == svn_wc__db_kind_unknown)
- && status != svn_wc__db_status_deleted);
+ if (status != svn_wc__db_status_deleted
+ && wc_kind == svn_wc__db_kind_dir)
+ {
+ return svn_error_createf(
+ SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
+ _("Failed to add file '%s': a non-file object "
+ "of the same name already exists"),
+ svn_dirent_local_style(fb->local_abspath,
+ pool));
+ }
+#endif
if (local_is_file)
{
@@ -3944,16 +3918,6 @@ add_file(const char *path,
err = NULL;
- if (wc_root)
- {
- err = svn_error_createf(
- SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
- _("Failed to add file '%s': a file "
- "from another repository with the same name "
- "already exists"),
- svn_dirent_local_style(fb->local_abspath, pool));
- }
-
if (switched && !eb->switch_relpath)
{
err = svn_error_createf(
@@ -3975,18 +3939,6 @@ add_file(const char *path,
}
}
- /* We can't properly handle add vs. add with mismatching
- * node kinds before single db. */
- if (local_is_non_file)
- {
- return svn_error_createf(
- SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
- _("Failed to add file '%s': a non-file object "
- "of the same name already exists"),
- svn_dirent_local_style(fb->local_abspath,
- pool));
- }
-
/* Find out if this is a file external, because we want to allow pulling
* in a file external onto an existing node -- because that's how
* externals are currently implemented. :( */
@@ -4000,47 +3952,16 @@ add_file(const char *path,
else
SVN_ERR(err);
- /* Do tree conflict checking if
- * - if a copy is involved on either side, except if both are copies
- * from the same URL and revnum.
- * - if this is a switch operation
[... 1174 lines stripped ...]