summaryrefslogtreecommitdiff
path: root/subversion/libsvn_wc/update_editor.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_wc/update_editor.c')
-rw-r--r--subversion/libsvn_wc/update_editor.c2565
1 files changed, 1338 insertions, 1227 deletions
diff --git a/subversion/libsvn_wc/update_editor.c b/subversion/libsvn_wc/update_editor.c
index 6dad817..fd3e9ca 100644
--- a/subversion/libsvn_wc/update_editor.c
+++ b/subversion/libsvn_wc/update_editor.c
@@ -34,7 +34,7 @@
#include "svn_types.h"
#include "svn_pools.h"
-#include "svn_delta.h"
+#include "svn_hash.h"
#include "svn_string.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
@@ -45,11 +45,14 @@
#include "wc.h"
#include "adm_files.h"
-#include "entries.h"
+#include "conflicts.h"
#include "translate.h"
#include "workqueue.h"
+#include "private/svn_subr_private.h"
#include "private/svn_wc_private.h"
+#include "private/svn_editor.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) \
@@ -167,6 +170,12 @@ struct edit_baton
generated conflict files. */
const apr_array_header_t *ext_patterns;
+ /* Hash mapping const char * absolute working copy paths to depth-first
+ ordered arrays of svn_prop_inherited_item_t * structures representing
+ the properties inherited by the base node at that working copy path.
+ May be NULL. */
+ apr_hash_t *wcroot_iprops;
+
/* The revision we're targeting...or something like that. This
starts off as a pointer to the revision to which we are updating,
or SVN_INVALID_REVNUM, but by the end of the edit, should be
@@ -266,15 +275,17 @@ remember_skipped_tree(struct edit_baton *eb,
{
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
- apr_hash_set(eb->skipped_trees,
- apr_pstrdup(eb->pool,
- svn_dirent_skip_ancestor(eb->wcroot_abspath,
- local_abspath)),
- APR_HASH_KEY_STRING, (void*)1);
+ svn_hash_sets(eb->skipped_trees,
+ apr_pstrdup(eb->pool,
+ svn_dirent_skip_ancestor(eb->wcroot_abspath,
+ local_abspath)),
+ (void *)1);
return SVN_NO_ERROR;
}
+/* Per directory baton. Lives in its own subpool of the parent directory
+ or of the edit baton if there is no parent directory */
struct dir_baton
{
/* Basename of this directory. */
@@ -289,6 +300,9 @@ struct dir_baton
/* The revision of the directory before updating */
svn_revnum_t old_revision;
+ /* The repos_relpath before updating/switching */
+ const char *old_repos_relpath;
+
/* The global edit baton. */
struct edit_baton *edit_baton;
@@ -310,13 +324,17 @@ struct dir_baton
marked as deleted. */
svn_boolean_t shadowed;
+ /* Set on a node when the existing node is obstructed, and the edit operation
+ continues as semi-shadowed update */
+ svn_boolean_t edit_obstructed;
+
/* The (new) changed_* information, cached to avoid retrieving it later */
svn_revnum_t changed_rev;
apr_time_t changed_date;
const char *changed_author;
/* If not NULL, contains a mapping of const char* basenames of children that
- have been deleted to their svn_wc_conflict_description2_t* tree conflicts.
+ have been deleted to their svn_skel_t* tree conflicts.
We store this hash to allow replacements to continue under a just
installed tree conflict.
@@ -346,7 +364,7 @@ struct dir_baton
svn_boolean_t edited;
/* The tree conflict to install once the node is really edited */
- svn_wc_conflict_description2_t *edit_conflict;
+ svn_skel_t *edit_conflict;
/* The bump information for this directory. */
struct bump_dir_info *bump_info;
@@ -365,26 +383,10 @@ struct dir_baton
/* The pool in which this baton itself is allocated. */
apr_pool_t *pool;
-};
-
-/* The bump information is tracked separately from the directory batons.
- This is a small structure kept in the edit pool, while the heavier
- directory baton is managed by the editor driver.
-
- In a postfix delta case, the directory batons are going to disappear.
- The files will refer to these structures, rather than the full
- directory baton. */
-struct bump_dir_info
-{
- /* ptr to the bump information for the parent directory */
- struct bump_dir_info *parent;
-
- /* how many entries are referring to this bump information? */
+ /* how many nodes are referring to baton? */
int ref_count;
- /* Pool that should be cleared after the dir is bumped */
- apr_pool_t *pool;
};
@@ -484,19 +486,6 @@ cleanup_edit_baton(void *edit_baton)
return APR_SUCCESS;
}
-/* An APR pool cleanup handler. This is a child handler, it removes
- the mail pool handler.
- <stsp> mail pool?
- <hwright> that's where the missing commit mails are going! */
-static apr_status_t
-cleanup_edit_baton_child(void *edit_baton)
-{
- struct edit_baton *eb = edit_baton;
- apr_pool_cleanup_kill(eb->pool, eb, cleanup_edit_baton);
- return APR_SUCCESS;
-}
-
-
/* Make a new dir baton in a subpool of PB->pool. PB is the parent baton.
If PATH and PB are NULL, this is the root directory of the edit; in this
case, make the new dir baton in a subpool of EB->pool.
@@ -511,7 +500,6 @@ make_dir_baton(struct dir_baton **d_p,
{
apr_pool_t *dir_pool;
struct dir_baton *d;
- struct bump_dir_info *bdi;
if (pb != NULL)
dir_pool = svn_pool_create(pb->pool);
@@ -596,23 +584,13 @@ make_dir_baton(struct dir_baton **d_p,
}
}
- /* the bump information lives in the edit pool */
- bdi = apr_pcalloc(dir_pool, sizeof(*bdi));
- bdi->parent = pb ? pb->bump_info : NULL;
- bdi->ref_count = 1;
- bdi->pool = dir_pool;
-
- /* the parent's bump info has one more referer */
- if (pb)
- ++bdi->parent->ref_count;
-
d->edit_baton = eb;
d->parent_baton = pb;
d->pool = dir_pool;
d->propchanges = apr_array_make(dir_pool, 1, sizeof(svn_prop_t));
d->obstruction_found = FALSE;
d->add_existed = FALSE;
- d->bump_info = bdi;
+ d->ref_count = 1;
d->old_revision = SVN_INVALID_REVNUM;
d->adding_dir = adding;
d->changed_rev = SVN_INVALID_REVNUM;
@@ -622,7 +600,10 @@ make_dir_baton(struct dir_baton **d_p,
if (pb)
{
d->skip_this = pb->skip_this;
- d->shadowed = pb->shadowed;
+ d->shadowed = pb->shadowed || pb->edit_obstructed;
+
+ /* the parent's bump info has one more referer */
+ pb->ref_count++;
}
/* The caller of this function needs to fill these in. */
@@ -637,6 +618,7 @@ make_dir_baton(struct dir_baton **d_p,
/* Forward declarations. */
static svn_error_t *
already_in_a_tree_conflict(svn_boolean_t *conflicted,
+ svn_boolean_t *ignored,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool);
@@ -660,34 +642,32 @@ do_notification(const struct edit_baton *eb,
(*eb->notify_func)(eb->notify_baton, notify, scratch_pool);
}
-/* Decrement the bump_dir_info's reference count. If it hits zero,
+/* Decrement the directory's reference count. If it hits zero,
then this directory is "done". This means it is safe to clear its pool.
- In addition, when the directory is "done", we loop onto the parent's
- bump information to possibly mark it as done, too.
+ In addition, when the directory is "done", we recurse to possible cleanup
+ the parent directory.
*/
static svn_error_t *
-maybe_release_dir_info(struct bump_dir_info *bdi)
+maybe_release_dir_info(struct dir_baton *db)
{
- /* Keep moving up the tree of directories until we run out of parents,
- or a directory is not yet "done". */
- while (bdi != NULL)
- {
- apr_pool_t *destroy_pool;
+ db->ref_count--;
- if (--bdi->ref_count > 0)
- break; /* directory isn't done yet */
+ if (!db->ref_count)
+ {
+ struct dir_baton *pb = db->parent_baton;
- destroy_pool = bdi->pool;
- bdi = bdi->parent;
+ svn_pool_destroy(db->pool);
- svn_pool_destroy(destroy_pool);
+ if (pb)
+ SVN_ERR(maybe_release_dir_info(pb));
}
- /* we exited the for loop because there are no more parents */
return SVN_NO_ERROR;
}
+/* Per file baton. Lives in its own subpool below the pool of the parent
+ directory */
struct file_baton
{
/* Pool specific to this file_baton. */
@@ -705,6 +685,9 @@ struct file_baton
/* The revision of the file before updating */
svn_revnum_t old_revision;
+ /* The repos_relpath before updating/switching */
+ const char *old_repos_relpath;
+
/* The global edit baton. */
struct edit_baton *edit_baton;
@@ -732,6 +715,10 @@ struct file_baton
in the working copy (replaced, deleted, etc.). */
svn_boolean_t shadowed;
+ /* Set on a node when the existing node is obstructed, and the edit operation
+ continues as semi-shadowed update */
+ svn_boolean_t edit_obstructed;
+
/* The (new) changed_* information, cached to avoid retrieving it later */
svn_revnum_t changed_rev;
apr_time_t changed_date;
@@ -750,6 +737,10 @@ struct file_baton
initialized, this is never NULL, but it may have zero elements. */
apr_array_header_t *propchanges;
+ /* For existing files, whether there are local modifications. FALSE for added
+ files */
+ svn_boolean_t local_prop_mods;
+
/* Bump information for the directory this file lives in */
struct bump_dir_info *bump_info;
@@ -759,7 +750,7 @@ struct file_baton
svn_boolean_t edited;
/* The tree conflict to install once the node is really edited */
- svn_wc_conflict_description2_t *edit_conflict;
+ svn_skel_t *edit_conflict;
};
@@ -775,7 +766,6 @@ make_file_baton(struct file_baton **f_p,
{
struct edit_baton *eb = pb->edit_baton;
apr_pool_t *file_pool = svn_pool_create(pb->pool);
-
struct file_baton *f = apr_pcalloc(file_pool, sizeof(*f));
SVN_ERR_ASSERT(path);
@@ -825,17 +815,84 @@ make_file_baton(struct file_baton **f_p,
f->obstruction_found = FALSE;
f->add_existed = FALSE;
f->skip_this = pb->skip_this;
- f->shadowed = pb->shadowed;
+ f->shadowed = pb->shadowed || pb->edit_obstructed;
f->dir_baton = pb;
f->changed_rev = SVN_INVALID_REVNUM;
- /* the directory's bump info has one more referer now */
- ++f->bump_info->ref_count;
+ /* the directory has one more referer now */
+ pb->ref_count++;
*f_p = f;
return SVN_NO_ERROR;
}
+/* Complete a conflict skel by describing the update.
+ *
+ * LOCAL_KIND is the node kind of the tree conflict victim in the
+ * working copy.
+ *
+ * All temporary allocations are be made in SCRATCH_POOL, while allocations
+ * needed for the returned conflict struct are made in RESULT_POOL.
+ */
+static svn_error_t *
+complete_conflict(svn_skel_t *conflict,
+ const struct edit_baton *eb,
+ const char *local_abspath,
+ const char *old_repos_relpath,
+ svn_revnum_t old_revision,
+ const char *new_repos_relpath,
+ svn_node_kind_t local_kind,
+ svn_node_kind_t target_kind,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_wc_conflict_version_t *original_version;
+ svn_wc_conflict_version_t *target_version;
+ svn_boolean_t is_complete;
+
+ if (!conflict)
+ return SVN_NO_ERROR; /* Not conflicted */
+
+ SVN_ERR(svn_wc__conflict_skel_is_complete(&is_complete, conflict));
+
+ if (is_complete)
+ return SVN_NO_ERROR; /* Already completed */
+
+ if (old_repos_relpath)
+ original_version = svn_wc_conflict_version_create2(eb->repos_root,
+ eb->repos_uuid,
+ old_repos_relpath,
+ old_revision,
+ local_kind,
+ result_pool);
+ else
+ original_version = NULL;
+
+ if (new_repos_relpath)
+ target_version = svn_wc_conflict_version_create2(eb->repos_root,
+ eb->repos_uuid,
+ new_repos_relpath,
+ *eb->target_revision,
+ target_kind,
+ result_pool);
+ else
+ target_version = NULL;
+
+ if (eb->switch_relpath)
+ SVN_ERR(svn_wc__conflict_skel_set_op_switch(conflict,
+ original_version,
+ target_version,
+ result_pool, scratch_pool));
+ else
+ SVN_ERR(svn_wc__conflict_skel_set_op_update(conflict,
+ original_version,
+ target_version,
+ result_pool, scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+
/* Called when a directory is really edited, to avoid marking a
tree conflict on a node for a no-change edit */
static svn_error_t *
@@ -852,14 +909,22 @@ mark_directory_edited(struct dir_baton *db, apr_pool_t *scratch_pool)
if (db->edit_conflict)
{
/* We have a (delayed) tree conflict to install */
- SVN_ERR(svn_wc__db_op_set_tree_conflict(db->edit_baton->db,
- db->local_abspath,
- db->edit_conflict,
- scratch_pool));
+
+ SVN_ERR(complete_conflict(db->edit_conflict, db->edit_baton,
+ db->local_abspath,
+ db->old_repos_relpath, db->old_revision,
+ db->new_relpath,
+ svn_node_dir, svn_node_dir,
+ db->pool, scratch_pool));
+ SVN_ERR(svn_wc__db_op_mark_conflict(db->edit_baton->db,
+ db->local_abspath,
+ db->edit_conflict, NULL,
+ scratch_pool));
do_notification(db->edit_baton, db->local_abspath, svn_node_dir,
svn_wc_notify_tree_conflict, scratch_pool);
db->already_notified = TRUE;
+
}
return SVN_NO_ERROR;
@@ -880,10 +945,17 @@ mark_file_edited(struct file_baton *fb, apr_pool_t *scratch_pool)
if (fb->edit_conflict)
{
/* We have a (delayed) tree conflict to install */
- SVN_ERR(svn_wc__db_op_set_tree_conflict(fb->edit_baton->db,
- fb->local_abspath,
- fb->edit_conflict,
- scratch_pool));
+
+ SVN_ERR(complete_conflict(fb->edit_conflict, fb->edit_baton,
+ fb->local_abspath, fb->old_repos_relpath,
+ fb->old_revision, fb->new_relpath,
+ svn_node_file, svn_node_file,
+ fb->pool, scratch_pool));
+
+ SVN_ERR(svn_wc__db_op_mark_conflict(fb->edit_baton->db,
+ fb->local_abspath,
+ fb->edit_conflict, NULL,
+ scratch_pool));
do_notification(fb->edit_baton, fb->local_abspath, svn_node_file,
svn_wc_notify_tree_conflict, scratch_pool);
@@ -940,16 +1012,19 @@ window_handler(svn_txdelta_window_t *window, void *baton)
if (err)
{
- /* We failed to apply the delta; clean up the temporary file. */
- svn_error_clear(svn_io_remove_file2(hb->new_text_base_tmp_abspath, TRUE,
- hb->pool));
+ /* We failed to apply the delta; clean up the temporary file if it
+ already created by lazy_open_target(). */
+ if (hb->new_text_base_tmp_abspath)
+ {
+ svn_error_clear(svn_io_remove_file2(hb->new_text_base_tmp_abspath,
+ TRUE, hb->pool));
+ }
}
else
{
/* 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);
+ svn_checksum__from_digest_md5(hb->new_text_base_md5_digest, fb->pool);
fb->new_text_base_sha1_checksum =
svn_checksum_dup(hb->new_text_base_sha1_checksum, fb->pool);
@@ -1051,6 +1126,16 @@ path_join_under_root(const char **result_path,
pool));
}
+ /* This catches issue #3288 */
+ if (strcmp(add_path, svn_dirent_basename(*result_path, NULL)) != 0)
+ {
+ return svn_error_createf(
+ SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
+ _("'%s' is not valid as filename in directory '%s'"),
+ svn_dirent_local_style(add_path, pool),
+ svn_dirent_local_style(base_path, pool));
+ }
+
return SVN_NO_ERROR;
}
@@ -1069,6 +1154,17 @@ set_target_revision(void *edit_baton,
return SVN_NO_ERROR;
}
+static svn_error_t *
+check_tree_conflict(svn_skel_t **pconflict,
+ struct edit_baton *eb,
+ const char *local_abspath,
+ svn_wc__db_status_t working_status,
+ svn_boolean_t exists_in_repos,
+ svn_node_kind_t expected_kind,
+ svn_wc_conflict_action_t action,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
/* An svn_delta_editor_t function. */
static svn_error_t *
open_root(void *edit_baton,
@@ -1078,8 +1174,12 @@ open_root(void *edit_baton,
{
struct edit_baton *eb = edit_baton;
struct dir_baton *db;
- svn_boolean_t already_conflicted;
+ svn_boolean_t already_conflicted, conflict_ignored;
svn_error_t *err;
+ svn_wc__db_status_t status;
+ svn_wc__db_status_t base_status;
+ svn_node_kind_t kind;
+ svn_boolean_t have_work;
/* Note that something interesting is actually happening in this
edit run. */
@@ -1088,8 +1188,8 @@ open_root(void *edit_baton,
SVN_ERR(make_dir_baton(&db, NULL, eb, NULL, FALSE, pool));
*dir_baton = db;
- err = already_in_a_tree_conflict(&already_conflicted, eb->db,
- db->local_abspath, pool);
+ err = already_in_a_tree_conflict(&already_conflicted, &conflict_ignored,
+ eb->db, db->local_abspath, pool);
if (err)
{
@@ -1097,10 +1197,15 @@ open_root(void *edit_baton,
return svn_error_trace(err);
svn_error_clear(err);
- already_conflicted = FALSE;
+ already_conflicted = conflict_ignored = FALSE;
}
else if (already_conflicted)
{
+ /* Record a skip of both the anchor and target in the skipped tree
+ as the anchor itself might not be updated */
+ SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
+ SVN_ERR(remember_skipped_tree(eb, eb->target_abspath, pool));
+
db->skip_this = TRUE;
db->already_notified = TRUE;
@@ -1112,19 +1217,94 @@ open_root(void *edit_baton,
return SVN_NO_ERROR;
}
+
+ SVN_ERR(svn_wc__db_read_info(&status, &kind, &db->old_revision,
+ &db->old_repos_relpath, NULL, NULL,
+ &db->changed_rev, &db->changed_date,
+ &db->changed_author, &db->ambient_depth,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, &have_work,
+ eb->db, db->local_abspath,
+ db->pool, pool));
+
+ if (conflict_ignored)
+ {
+ db->shadowed = TRUE;
+ }
+ else if (have_work)
+ {
+ const char *move_src_root_abspath;
+
+ SVN_ERR(svn_wc__db_base_moved_to(NULL, NULL, &move_src_root_abspath,
+ NULL, eb->db, db->local_abspath,
+ pool, pool));
+ if (move_src_root_abspath || *eb->target_basename == '\0')
+ SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL,
+ &db->old_revision,
+ &db->old_repos_relpath, NULL, NULL,
+ &db->changed_rev, &db->changed_date,
+ &db->changed_author,
+ &db->ambient_depth,
+ NULL, NULL, NULL, NULL, NULL, NULL,
+ eb->db, db->local_abspath,
+ db->pool, pool));
+
+ if (move_src_root_abspath)
+ {
+ /* This is an update anchored inside a move. We need to
+ raise a move-edit tree-conflict on the move root to
+ update the move destination. */
+ svn_skel_t *tree_conflict = svn_wc__conflict_skel_create(pool);
+
+ SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
+ tree_conflict, eb->db, move_src_root_abspath,
+ svn_wc_conflict_reason_moved_away,
+ svn_wc_conflict_action_edit,
+ move_src_root_abspath, pool, pool));
+
+ if (strcmp(db->local_abspath, move_src_root_abspath))
+ {
+ /* We are raising the tree-conflict on some parent of
+ the edit root, we won't be handling that path again
+ so raise the conflict now. */
+ SVN_ERR(complete_conflict(tree_conflict, eb,
+ move_src_root_abspath,
+ db->old_repos_relpath,
+ db->old_revision, db->new_relpath,
+ svn_node_dir, svn_node_dir,
+ pool, pool));
+ SVN_ERR(svn_wc__db_op_mark_conflict(eb->db,
+ move_src_root_abspath,
+ tree_conflict,
+ NULL, pool));
+ do_notification(eb, move_src_root_abspath, svn_node_dir,
+ svn_wc_notify_tree_conflict, pool);
+ }
+ else
+ db->edit_conflict = tree_conflict;
+ }
+
+ db->shadowed = TRUE; /* Needed for the close_directory() on the root, to
+ make sure it doesn't use the ACTUAL tree */
+ }
+ else
+ base_status = status;
+
if (*eb->target_basename == '\0')
{
/* For an update with a NULL target, this is equivalent to open_dir(): */
- svn_wc__db_status_t status;
- /* Read the depth from the entry. */
- SVN_ERR(svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL, NULL,
- &db->changed_rev, &db->changed_date,
- &db->changed_author, &db->ambient_depth,
- NULL, NULL, NULL, NULL, NULL,
- eb->db, db->local_abspath,
- db->pool, pool));
- db->was_incomplete = (status == svn_wc__db_status_incomplete);
+ db->was_incomplete = (base_status == svn_wc__db_status_incomplete);
+
+ /* ### TODO: Add some tree conflict and obstruction detection, etc. like
+ open_directory() does.
+ (or find a way to reuse that code here)
+
+ ### BH 2013: I don't think we need all of the detection here, as the
+ user explicitly asked to update this node. So we don't
+ have to tell that it is a local replacement/delete.
+ */
SVN_ERR(svn_wc__db_temp_op_start_directory_update(eb->db,
db->local_abspath,
@@ -1172,9 +1352,10 @@ modcheck_callback(void *baton,
case svn_wc_status_missing:
case svn_wc_status_obstructed:
- if (status->prop_status != svn_wc_status_modified)
- break;
- /* Fall through in the found modification case */
+ mb->found_mod = TRUE;
+ mb->found_not_delete = TRUE;
+ /* Exit from the status walker: We know what we want to know */
+ return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
default:
case svn_wc_status_added:
@@ -1195,14 +1376,14 @@ modcheck_callback(void *baton,
* is set to true and all the local modifications were deletes then set
* *ALL_EDITS_ARE_DELETES to true, set it to false otherwise. LOCAL_ABSPATH
* may be a file or a directory. */
-static svn_error_t *
-node_has_local_mods(svn_boolean_t *modified,
- svn_boolean_t *all_edits_are_deletes,
- svn_wc__db_t *db,
- const char *local_abspath,
- svn_cancel_func_t cancel_func,
- void *cancel_baton,
- apr_pool_t *scratch_pool)
+svn_error_t *
+svn_wc__node_has_local_mods(svn_boolean_t *modified,
+ svn_boolean_t *all_edits_are_deletes,
+ svn_wc__db_t *db,
+ const char *local_abspath,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *scratch_pool)
{
modcheck_baton_t modcheck_baton = { NULL, FALSE, FALSE };
svn_error_t *err;
@@ -1226,195 +1407,15 @@ node_has_local_mods(svn_boolean_t *modified,
SVN_ERR(err);
*modified = modcheck_baton.found_mod;
- *all_edits_are_deletes = !modcheck_baton.found_not_delete;
+ *all_edits_are_deletes = (modcheck_baton.found_mod
+ && !modcheck_baton.found_not_delete);
return SVN_NO_ERROR;
}
-
/* 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);
- SVN_ERR_ASSERT(their_relpath != NULL);
-
- /* 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)
- {
- /* This is a 'switch' operation. */
- right_repos_relpath = their_relpath;
- }
- 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.
@@ -1422,45 +1423,32 @@ create_tree_conflict(svn_wc_conflict_description2_t **pconflict,
* The edit baton EB gives information including whether the operation is
* an update or a switch.
*
- * WORKING_STATUS and WORKING_KIND are the current node status of LOCAL_ABSPATH
+ * WORKING_STATUS is the current node status of LOCAL_ABSPATH
* and EXISTS_IN_REPOS specifies whether a BASE_NODE representation for exists
- * for this node.
+ * for this node. In that case the on disk type is compared to EXPECTED_KIND.
*
* 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 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 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).
- *
* The tree conflict is allocated in RESULT_POOL. Temporary allocations use
- * SCRACTH_POOl.
+ * SCRATCH_POOL.
*/
static svn_error_t *
-check_tree_conflict(svn_wc_conflict_description2_t **pconflict,
+check_tree_conflict(svn_skel_t **pconflict,
struct edit_baton *eb,
const char *local_abspath,
svn_wc__db_status_t working_status,
- svn_wc__db_kind_t working_kind,
svn_boolean_t exists_in_repos,
+ svn_node_kind_t expected_kind,
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)
{
svn_wc_conflict_reason_t reason = SVN_WC_CONFLICT_REASON_NONE;
- svn_boolean_t locally_replaced = FALSE;
svn_boolean_t modified = FALSE;
svn_boolean_t all_mods_are_deletes = FALSE;
-
- SVN_ERR_ASSERT(their_relpath != NULL);
+ const char *move_src_op_root_abspath = NULL;
*pconflict = NULL;
@@ -1471,21 +1459,7 @@ check_tree_conflict(svn_wc_conflict_description2_t **pconflict,
case svn_wc__db_status_added:
case svn_wc__db_status_moved_here:
case svn_wc__db_status_copied:
- /* Is it a replace? */
- if (exists_in_repos)
- {
- svn_wc__db_status_t base_status;
- SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL,
- NULL, NULL,
- eb->db, local_abspath,
- scratch_pool, scratch_pool));
- if (base_status != svn_wc__db_status_not_present)
- locally_replaced = TRUE;
- }
-
- if (!locally_replaced)
+ if (!exists_in_repos)
{
/* The node is locally added, and it did not exist before. This
* is an 'update', so the local add can only conflict with an
@@ -1497,19 +1471,45 @@ check_tree_conflict(svn_wc_conflict_description2_t **pconflict,
* would not have been called in the first place. */
SVN_ERR_ASSERT(action == svn_wc_conflict_action_add);
- reason = svn_wc_conflict_reason_added;
+ /* Scan the addition in case our caller didn't. */
+ if (working_status == svn_wc__db_status_added)
+ SVN_ERR(svn_wc__db_scan_addition(&working_status, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL,
+ eb->db, local_abspath,
+ scratch_pool, scratch_pool));
+
+ if (working_status == svn_wc__db_status_moved_here)
+ reason = svn_wc_conflict_reason_moved_here;
+ else
+ reason = svn_wc_conflict_reason_added;
}
else
{
- /* The node is locally replaced. */
- reason = svn_wc_conflict_reason_replaced;
+ /* The node is locally replaced but could also be moved-away. */
+ SVN_ERR(svn_wc__db_base_moved_to(NULL, NULL, NULL,
+ &move_src_op_root_abspath,
+ eb->db, local_abspath,
+ scratch_pool, scratch_pool));
+ if (move_src_op_root_abspath)
+ reason = svn_wc_conflict_reason_moved_away;
+ else
+ reason = svn_wc_conflict_reason_replaced;
}
break;
case svn_wc__db_status_deleted:
- /* The node is locally deleted. */
- reason = svn_wc_conflict_reason_deleted;
+ {
+ SVN_ERR(svn_wc__db_base_moved_to(NULL, NULL, NULL,
+ &move_src_op_root_abspath,
+ eb->db, local_abspath,
+ scratch_pool, scratch_pool));
+ if (move_src_op_root_abspath)
+ reason = svn_wc_conflict_reason_moved_away;
+ else
+ reason = svn_wc_conflict_reason_deleted;
+ }
break;
case svn_wc__db_status_incomplete:
@@ -1521,10 +1521,33 @@ check_tree_conflict(svn_wc_conflict_description2_t **pconflict,
* missing information (hopefully). */
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
- * tree-conflict. (It's possibly a text- or prop-conflict,
- * but we don't handle those here.) */
- return SVN_NO_ERROR;
+ {
+ /* An edit onto a local edit or onto *no* local changes is no
+ * tree-conflict. (It's possibly a text- or prop-conflict,
+ * but we don't handle those here.)
+ *
+ * Except when there is a local obstruction
+ */
+ if (exists_in_repos)
+ {
+ svn_node_kind_t disk_kind;
+
+ SVN_ERR(svn_io_check_path(local_abspath, &disk_kind,
+ scratch_pool));
+
+ if (disk_kind != expected_kind && disk_kind != svn_node_none)
+ {
+ reason = svn_wc_conflict_reason_obstructed;
+ break;
+ }
+
+ }
+ return SVN_NO_ERROR;
+ }
+
+ /* Replace is handled as delete and then specifically in
+ add_directory() and add_file(), so we only expect deletes here */
+ SVN_ERR_ASSERT(action == svn_wc_conflict_action_delete);
/* Check if the update wants to delete or replace a locally
* modified node. */
@@ -1534,10 +1557,10 @@ check_tree_conflict(svn_wc_conflict_description2_t **pconflict,
* not visit the subdirectories of a directory that it wants to delete.
* Therefore, we need to start a separate crawl here. */
- SVN_ERR(node_has_local_mods(&modified, &all_mods_are_deletes,
- eb->db, local_abspath,
- eb->cancel_func, eb->cancel_baton,
- scratch_pool));
+ SVN_ERR(svn_wc__node_has_local_mods(&modified, &all_mods_are_deletes,
+ eb->db, local_abspath,
+ eb->cancel_func, eb->cancel_baton,
+ scratch_pool));
if (modified)
{
@@ -1573,35 +1596,57 @@ check_tree_conflict(svn_wc_conflict_description2_t **pconflict,
/* Sanity checks. Note that if there was no action on the node, this function
* would not have been called in the first place.*/
if (reason == svn_wc_conflict_reason_edited
+ || reason == svn_wc_conflict_reason_obstructed
|| reason == svn_wc_conflict_reason_deleted
+ || reason == svn_wc_conflict_reason_moved_away
|| reason == svn_wc_conflict_reason_replaced)
- /* When the node existed before (it was locally deleted, replaced or
- * edited), then 'update' cannot add it "again". So it can only send
- * _action_edit, _delete or _replace. */
- SVN_ERR_ASSERT(action == svn_wc_conflict_action_edit
- || action == svn_wc_conflict_action_delete
- || action == svn_wc_conflict_action_replace);
- else if (reason == svn_wc_conflict_reason_added)
- /* When the node did not exist before (it was locally added), then 'update'
- * cannot want to modify it in any way. It can only send _action_add. */
- SVN_ERR_ASSERT(action == svn_wc_conflict_action_add);
-
-
- /* A conflict was detected. Append log commands to the log accumulator
- * to record it. */
- return svn_error_trace(create_tree_conflict(pconflict, eb, local_abspath,
- reason, action, their_node_kind,
- their_relpath,
- result_pool, scratch_pool));
+ {
+ /* When the node existed before (it was locally deleted, replaced or
+ * edited), then 'update' cannot add it "again". So it can only send
+ * _action_edit, _delete or _replace. */
+ if (action != svn_wc_conflict_action_edit
+ && action != svn_wc_conflict_action_delete
+ && action != svn_wc_conflict_action_replace)
+ return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
+ _("Unexpected attempt to add a node at path '%s'"),
+ svn_dirent_local_style(local_abspath, scratch_pool));
+ }
+ else if (reason == svn_wc_conflict_reason_added ||
+ reason == svn_wc_conflict_reason_moved_here)
+ {
+ /* When the node did not exist before (it was locally added),
+ * then 'update' cannot want to modify it in any way.
+ * It can only send _action_add. */
+ if (action != svn_wc_conflict_action_add)
+ return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
+ _("Unexpected attempt to edit, delete, or replace "
+ "a node at path '%s'"),
+ svn_dirent_local_style(local_abspath, scratch_pool));
+
+ }
+
+
+ /* A conflict was detected. Create a conflict skel to record it. */
+ *pconflict = svn_wc__conflict_skel_create(result_pool);
+
+ SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(*pconflict,
+ eb->db, local_abspath,
+ reason,
+ action,
+ move_src_op_root_abspath,
+ result_pool, scratch_pool));
+
+ return SVN_NO_ERROR;
}
-/* If LOCAL_ABSPATH is inside a conflicted tree, set *CONFLICTED to TRUE,
- * Otherwise set *CONFLICTED to FALSE. Use SCRATCH_POOL for temporary
- * allocations.
+/* If LOCAL_ABSPATH is inside a conflicted tree and the conflict is
+ * not a moved-away-edit conflict, set *CONFLICTED to TRUE. Otherwise
+ * set *CONFLICTED to FALSE.
*/
static svn_error_t *
already_in_a_tree_conflict(svn_boolean_t *conflicted,
+ svn_boolean_t *ignored,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool)
@@ -1611,39 +1656,22 @@ already_in_a_tree_conflict(svn_boolean_t *conflicted,
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
- *conflicted = FALSE;
+ *conflicted = *ignored = FALSE;
while (TRUE)
{
- svn_boolean_t is_wc_root, has_conflict;
+ svn_boolean_t is_wc_root;
svn_pool_clear(iterpool);
- SVN_ERR(svn_wc__db_read_info(NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL,
- &has_conflict, NULL, NULL, NULL,
- NULL, NULL, NULL,
- db, ancestor_abspath, iterpool, iterpool));
-
- if (has_conflict)
- {
- const svn_wc_conflict_description2_t *conflict;
-
- SVN_ERR(svn_wc__db_op_read_tree_conflict(&conflict, db,
- ancestor_abspath,
- iterpool, iterpool));
-
- if (conflict != NULL)
- {
- *conflicted = TRUE;
- break;
- }
- }
+ SVN_ERR(svn_wc__conflicted_for_update_p(conflicted, ignored, db,
+ ancestor_abspath, TRUE,
+ scratch_pool));
+ if (*conflicted || *ignored)
+ break;
SVN_ERR(svn_wc__db_is_wcroot(&is_wc_root, db, ancestor_abspath,
iterpool));
-
if (is_wc_root)
break;
@@ -1658,19 +1686,15 @@ already_in_a_tree_conflict(svn_boolean_t *conflicted,
/* Temporary helper until the new conflict handling is in place */
static svn_error_t *
node_already_conflicted(svn_boolean_t *conflicted,
+ svn_boolean_t *conflict_ignored,
svn_wc__db_t *db,
const char *local_abspath,
apr_pool_t *scratch_pool)
{
- svn_boolean_t text_conflicted, prop_conflicted, tree_conflicted;
-
- SVN_ERR(svn_wc__internal_conflicted_p(&text_conflicted,
- &prop_conflicted,
- &tree_conflicted,
- db, local_abspath,
- scratch_pool));
+ SVN_ERR(svn_wc__conflicted_for_update_p(conflicted, conflict_ignored, db,
+ local_abspath, FALSE,
+ scratch_pool));
- *conflicted = (text_conflicted || prop_conflicted || tree_conflicted);
return SVN_NO_ERROR;
}
@@ -1687,16 +1711,18 @@ delete_entry(const char *path,
const char *base = svn_relpath_basename(path, NULL);
const char *local_abspath;
const char *repos_relpath;
- svn_wc__db_kind_t kind, base_kind;
+ svn_node_kind_t kind, base_kind;
+ svn_revnum_t old_revision;
svn_boolean_t conflicted;
- svn_boolean_t have_base;
svn_boolean_t have_work;
- svn_wc_conflict_description2_t *tree_conflict = NULL;
- svn_skel_t *work_item = NULL;
+ svn_skel_t *tree_conflict = NULL;
svn_wc__db_status_t status;
svn_wc__db_status_t base_status;
apr_pool_t *scratch_pool;
svn_boolean_t deleting_target;
+ svn_boolean_t deleting_switched;
+ svn_boolean_t keep_as_working = FALSE;
+ svn_boolean_t queue_deletes = TRUE;
if (pb->skip_this)
return SVN_NO_ERROR;
@@ -1720,7 +1746,7 @@ delete_entry(const char *path,
if (is_root)
{
/* Just skip this node; a future update will handle it */
- remember_skipped_tree(eb, local_abspath, pool);
+ SVN_ERR(remember_skipped_tree(eb, local_abspath, pool));
do_notification(eb, local_abspath, svn_node_unknown,
svn_wc_notify_update_skip_obstruction, scratch_pool);
@@ -1730,11 +1756,11 @@ delete_entry(const char *path,
}
}
- SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, &repos_relpath, NULL, NULL,
+ SVN_ERR(svn_wc__db_read_info(&status, &kind, &old_revision, &repos_relpath,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, &conflicted,
- NULL, NULL, NULL,
- &have_base, NULL, &have_work,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ &conflicted, NULL, NULL, NULL,
+ NULL, NULL, &have_work,
eb->db, local_abspath,
scratch_pool, scratch_pool));
@@ -1744,17 +1770,31 @@ delete_entry(const char *path,
base_kind = kind;
}
else
- SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL,
+ SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, &old_revision,
&repos_relpath,
NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
eb->db, local_abspath,
scratch_pool, scratch_pool));
+ if (pb->old_repos_relpath && repos_relpath)
+ {
+ const char *expected_name;
+
+ expected_name = svn_relpath_skip_ancestor(pb->old_repos_relpath,
+ repos_relpath);
+
+ deleting_switched = (!expected_name || strcmp(expected_name, base) != 0);
+ }
+ else
+ deleting_switched = FALSE;
+
/* Is this path a conflict victim? */
- if (conflicted)
- SVN_ERR(node_already_conflicted(&conflicted, eb->db, local_abspath,
- scratch_pool));
+ if (pb->shadowed)
+ conflicted = FALSE; /* Conflict applies to WORKING */
+ else if (conflicted)
+ SVN_ERR(node_already_conflicted(&conflicted, NULL,
+ eb->db, local_abspath, scratch_pool));
if (conflicted)
{
SVN_ERR(remember_skipped_tree(eb, local_abspath, scratch_pool));
@@ -1769,14 +1809,19 @@ delete_entry(const char *path,
}
-
- /* Receive the remote removal of excluded/absent/not present node.
- Do not notify, but perform the change even when the node is shadowed */
+ /* Receive the remote removal of excluded/server-excluded/not present node.
+ Do not notify, but perform the change even when the node is shadowed */
if (base_status == svn_wc__db_status_not_present
|| base_status == svn_wc__db_status_excluded
|| base_status == svn_wc__db_status_server_excluded)
{
- SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath, scratch_pool));
+ SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath,
+ FALSE /* keep_as_working */,
+ FALSE /* queue_deletes */,
+ FALSE /* remove_locks */,
+ SVN_INVALID_REVNUM /* not_present_rev */,
+ NULL, NULL,
+ scratch_pool));
if (deleting_target)
eb->target_deleted = TRUE;
@@ -1793,16 +1838,22 @@ delete_entry(const char *path,
/* Check for conflicts only when we haven't already recorded
* a tree-conflict on a parent node. */
- if (!pb->shadowed)
+ if (!pb->shadowed && !pb->edit_obstructed)
{
SVN_ERR(check_tree_conflict(&tree_conflict, eb, local_abspath,
- status, kind, TRUE,
- svn_wc_conflict_action_delete, svn_node_none,
- repos_relpath, pb->pool, scratch_pool));
+ status, TRUE,
+ (kind == svn_node_dir)
+ ? svn_node_dir
+ : svn_node_file,
+ svn_wc_conflict_action_delete,
+ pb->pool, scratch_pool));
}
+ else
+ queue_deletes = FALSE; /* There is no in-wc representation */
if (tree_conflict != NULL)
{
+ svn_wc_conflict_reason_t reason;
/* When we raise a tree conflict on a node, we don't want to mark the
* node as skipped, to allow a replacement to continue doing at least
* a bit of its work (possibly adding a not present node, for the
@@ -1810,18 +1861,16 @@ delete_entry(const char *path,
if (!pb->deletion_conflicts)
pb->deletion_conflicts = apr_hash_make(pb->pool);
- apr_hash_set(pb->deletion_conflicts, apr_pstrdup(pb->pool, base),
- APR_HASH_KEY_STRING, tree_conflict);
+ svn_hash_sets(pb->deletion_conflicts, apr_pstrdup(pb->pool, base),
+ tree_conflict);
- SVN_ERR(svn_wc__db_op_set_tree_conflict(eb->db,
- local_abspath,
- tree_conflict,
- scratch_pool));
-
- do_notification(eb, local_abspath, svn_node_unknown,
- svn_wc_notify_tree_conflict, scratch_pool);
+ SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
+ eb->db, local_abspath,
+ tree_conflict,
+ scratch_pool, scratch_pool));
- if (tree_conflict->reason == svn_wc_conflict_reason_edited)
+ if (reason == svn_wc_conflict_reason_edited
+ || reason == svn_wc_conflict_reason_obstructed)
{
/* The item exists locally and has some sort of local mod.
* It no longer exists in the repository at its target URL@REV.
@@ -1829,14 +1878,14 @@ delete_entry(const char *path,
* 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. */
- SVN_ERR(svn_wc__db_temp_op_make_copy(eb->db, local_abspath,
- scratch_pool));
+ keep_as_working = TRUE;
/* 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
- || tree_conflict->reason == svn_wc_conflict_reason_replaced)
+ else if (reason == svn_wc_conflict_reason_deleted
+ || reason == svn_wc_conflict_reason_moved_away
+ || reason == svn_wc_conflict_reason_replaced)
{
/* The item does not exist locally because it was already shadowed.
* We must complete the deletion, leaving the tree conflict info
@@ -1848,6 +1897,14 @@ delete_entry(const char *path,
SVN_ERR_MALFUNCTION(); /* other reasons are not expected here */
}
+ SVN_ERR(complete_conflict(tree_conflict, eb, local_abspath, repos_relpath,
+ old_revision, NULL,
+ (kind == svn_node_dir)
+ ? svn_node_dir
+ : svn_node_file,
+ svn_node_none,
+ pb->pool, scratch_pool));
+
/* 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.
@@ -1856,43 +1913,60 @@ delete_entry(const char *path,
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
accurate reports about itself in the future. */
- if (! deleting_target)
+ if (! deleting_target && ! deleting_switched)
{
/* Delete, and do not leave a not-present node. */
- SVN_ERR(svn_wc__wq_build_base_remove(&work_item,
- eb->db, local_abspath,
- SVN_INVALID_REVNUM,
- svn_wc__db_kind_unknown,
- scratch_pool, scratch_pool));
+ SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath,
+ keep_as_working, queue_deletes, FALSE,
+ SVN_INVALID_REVNUM /* not_present_rev */,
+ tree_conflict, NULL,
+ scratch_pool));
}
else
{
/* Delete, leaving a not-present node. */
- SVN_ERR(svn_wc__wq_build_base_remove(&work_item,
- eb->db, local_abspath,
- *eb->target_revision,
- base_kind,
- scratch_pool, scratch_pool));
- eb->target_deleted = TRUE;
+ SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath,
+ keep_as_working, queue_deletes, FALSE,
+ *eb->target_revision,
+ tree_conflict, NULL,
+ scratch_pool));
+ if (deleting_target)
+ eb->target_deleted = TRUE;
+ else
+ {
+ /* Don't remove the not-present marker at the final bump */
+ SVN_ERR(remember_skipped_tree(eb, local_abspath, pool));
+ }
}
- SVN_ERR(svn_wc__db_wq_add(eb->db, pb->local_abspath, work_item,
- scratch_pool));
-
SVN_ERR(svn_wc__wq_run(eb->db, pb->local_abspath,
eb->cancel_func, eb->cancel_baton,
scratch_pool));
- /* Notify. (If tree_conflict, we've already notified.) */
- if (tree_conflict == NULL)
+ /* Notify. */
+ if (tree_conflict)
+ {
+ if (eb->conflict_func)
+ SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, local_abspath,
+ tree_conflict,
+ NULL /* merge_options */,
+ eb->conflict_func,
+ eb->conflict_baton,
+ eb->cancel_func,
+ eb->cancel_baton,
+ scratch_pool));
+ do_notification(eb, local_abspath, svn_node_unknown,
+ svn_wc_notify_tree_conflict, scratch_pool);
+ }
+ else
{
svn_wc_notify_action_t action = svn_wc_notify_update_delete;
svn_node_kind_t node_kind;
- if (pb->shadowed)
+ if (pb->shadowed || pb->edit_obstructed)
action = svn_wc_notify_update_shadowed_delete;
- if (kind == svn_wc__db_kind_dir)
+ if (kind == svn_node_dir)
node_kind = svn_node_dir;
else
node_kind = svn_node_file;
@@ -1919,10 +1993,11 @@ add_directory(const char *path,
struct dir_baton *db;
svn_node_kind_t kind;
svn_wc__db_status_t status;
- svn_wc__db_kind_t wc_kind;
+ svn_node_kind_t wc_kind;
svn_boolean_t conflicted;
+ svn_boolean_t conflict_ignored = FALSE;
svn_boolean_t versioned_locally_and_present;
- svn_wc_conflict_description2_t *tree_conflict = NULL;
+ svn_skel_t *tree_conflict = NULL;
svn_error_t *err;
SVN_ERR_ASSERT(! (copyfrom_path || SVN_IS_VALID_REVNUM(copyfrom_rev)));
@@ -1975,13 +2050,13 @@ add_directory(const char *path,
return svn_error_trace(err);
svn_error_clear(err);
- wc_kind = svn_wc__db_kind_unknown;
+ wc_kind = svn_node_unknown;
status = svn_wc__db_status_normal;
conflicted = FALSE;
versioned_locally_and_present = FALSE;
}
- else if (wc_kind == svn_wc__db_kind_dir
+ else if (wc_kind == svn_node_dir
&& status == svn_wc__db_status_normal)
{
/* !! We found the root of a separate working copy obstructing the wc !!
@@ -1999,11 +2074,11 @@ add_directory(const char *path,
eb->repos_root,
eb->repos_uuid,
*eb->target_revision,
- svn_wc__db_kind_file,
+ svn_node_file,
NULL, NULL,
pool));
- remember_skipped_tree(eb, db->local_abspath, pool);
+ SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
db->skip_this = TRUE;
db->already_notified = TRUE;
@@ -2013,8 +2088,8 @@ add_directory(const char *path,
return SVN_NO_ERROR;
}
else if (status == svn_wc__db_status_normal
- && (wc_kind == svn_wc__db_kind_file
- || wc_kind == svn_wc__db_kind_symlink))
+ && (wc_kind == svn_node_file
+ || wc_kind == svn_node_symlink))
{
/* We found a file external occupating the place we need in BASE.
@@ -2027,7 +2102,7 @@ add_directory(const char *path,
file externals.
*/
- remember_skipped_tree(eb, db->local_abspath, pool);
+ SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
db->skip_this = TRUE;
db->already_notified = TRUE;
@@ -2036,7 +2111,7 @@ add_directory(const char *path,
return SVN_NO_ERROR;
}
- else if (wc_kind == svn_wc__db_kind_unknown)
+ else if (wc_kind == svn_node_unknown)
versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */
else
versioned_locally_and_present = IS_NODE_PRESENT(status);
@@ -2045,30 +2120,41 @@ add_directory(const char *path,
if (conflicted)
{
if (pb->deletion_conflicts)
- tree_conflict = apr_hash_get(pb->deletion_conflicts, db->name,
- APR_HASH_KEY_STRING);
+ tree_conflict = svn_hash_gets(pb->deletion_conflicts, db->name);
if (tree_conflict)
{
+ svn_wc_conflict_reason_t reason;
+ const char *move_src_op_root_abspath;
/* So this deletion wasn't just a deletion, it is actually a
- replacement. Luckily we still have the conflict so we can
- just update it. */
+ replacement. Let's install a better tree conflict. */
- /* ### What else should we update? */
- tree_conflict->action = svn_wc_conflict_action_replace;
+ SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL,
+ &move_src_op_root_abspath,
+ eb->db,
+ db->local_abspath,
+ tree_conflict,
+ db->pool, db->pool));
- SVN_ERR(svn_wc__db_op_set_tree_conflict(eb->db, db->local_abspath,
- tree_conflict, pool));
+ tree_conflict = svn_wc__conflict_skel_create(db->pool);
+
+ SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
+ tree_conflict,
+ eb->db, db->local_abspath,
+ reason, svn_wc_conflict_action_replace,
+ move_src_op_root_abspath,
+ db->pool, db->pool));
/* And now stop checking for conflicts here and just perform
a shadowed update */
+ db->edit_conflict = tree_conflict; /* Cache for close_directory */
tree_conflict = NULL; /* No direct notification */
db->shadowed = TRUE; /* Just continue */
conflicted = FALSE; /* No skip */
}
else
- SVN_ERR(node_already_conflicted(&conflicted, eb->db,
- db->local_abspath, pool));
+ SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
+ eb->db, db->local_abspath, pool));
}
/* Now the "usual" behaviour if already conflicted. Skip it. */
@@ -2096,7 +2182,7 @@ add_directory(const char *path,
eb->repos_root,
eb->repos_uuid,
*eb->target_revision,
- svn_wc__db_kind_dir,
+ svn_node_dir,
NULL, NULL,
pool));
@@ -2105,6 +2191,10 @@ add_directory(const char *path,
svn_wc_notify_skip_conflicted, pool);
return SVN_NO_ERROR;
}
+ else if (conflict_ignored)
+ {
+ db->shadowed = TRUE;
+ }
if (db->shadowed)
{
@@ -2135,7 +2225,7 @@ add_directory(const char *path,
/* Is there *something* that is not a dir? */
- local_is_non_dir = (wc_kind != svn_wc__db_kind_dir
+ local_is_non_dir = (wc_kind != svn_node_dir
&& status != svn_wc__db_status_deleted);
/* Do tree conflict checking if
@@ -2152,9 +2242,8 @@ add_directory(const char *path,
{
SVN_ERR(check_tree_conflict(&tree_conflict, eb,
db->local_abspath,
- status, wc_kind, FALSE,
+ status, FALSE, svn_node_none,
svn_wc_conflict_action_add,
- svn_node_dir, db->new_relpath,
pool, pool));
}
@@ -2176,60 +2265,64 @@ add_directory(const char *path,
db->shadowed = TRUE;
/* 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);
+ tree_conflict = svn_wc__conflict_skel_create(db->pool);
+
+ SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
+ tree_conflict,
+ eb->db, db->local_abspath,
+ svn_wc_conflict_reason_unversioned,
+ svn_wc_conflict_action_add, NULL,
+ db->pool, pool));
+ db->edit_conflict = tree_conflict;
}
}
- 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));
+ if (tree_conflict)
+ SVN_ERR(complete_conflict(tree_conflict, eb, db->local_abspath,
+ db->old_repos_relpath, db->old_revision,
+ db->new_relpath,
+ wc_kind,
+ svn_node_dir,
+ db->pool, pool));
+
+ SVN_ERR(svn_wc__db_base_add_incomplete_directory(
+ eb->db, db->local_abspath,
+ db->new_relpath,
+ eb->repos_root,
+ eb->repos_uuid,
+ *eb->target_revision,
+ db->ambient_depth,
+ (db->shadowed && db->obstruction_found),
+ (! db->shadowed
+ && status == svn_wc__db_status_added),
+ tree_conflict, NULL,
+ pool));
/* Make sure there is a real directory at LOCAL_ABSPATH, unless we are just
updating the DB */
if (!db->shadowed)
SVN_ERR(svn_wc__ensure_directory(db->local_abspath, pool));
- if (!db->shadowed && status == svn_wc__db_status_added)
- /* If there is no conflict we take over any added directory */
- SVN_ERR(svn_wc__db_temp_op_remove_working(eb->db, db->local_abspath, pool));
-
- /* ### We can't record an unversioned obstruction yet, so
- ### we record a delete instead, which will allow resolving the conflict
- ### to theirs with 'svn revert'. */
- if (db->shadowed && db->obstruction_found)
- {
- SVN_ERR(svn_wc__db_op_delete(eb->db, db->local_abspath,
- NULL, NULL /* notification */,
- eb->cancel_func, eb->cancel_baton,
- pool));
- }
-
if (tree_conflict != NULL)
{
- SVN_ERR(svn_wc__db_op_set_tree_conflict(eb->db, db->local_abspath,
- tree_conflict, pool));
+ if (eb->conflict_func)
+ SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, db->local_abspath,
+ tree_conflict,
+ NULL /* merge_options */,
+ eb->conflict_func,
+ eb->conflict_baton,
+ eb->cancel_func,
+ eb->cancel_baton,
+ pool));
db->already_notified = TRUE;
-
do_notification(eb, db->local_abspath, svn_node_dir,
svn_wc_notify_tree_conflict, pool);
}
-
/* If this add was obstructed by dir scheduled for addition without
- history let close_file() handle the notification because there
+ history let close_directory() handle the notification because there
might be properties to deal with. If PATH was added inside a locally
deleted tree, then suppress notification, a tree conflict was already
issued. */
@@ -2239,7 +2332,7 @@ add_directory(const char *path,
if (db->shadowed)
action = svn_wc_notify_update_shadowed_add;
- else if (db->obstruction_found)
+ else if (db->obstruction_found || db->add_existed)
action = svn_wc_notify_exists;
else
action = svn_wc_notify_update_add;
@@ -2264,9 +2357,10 @@ open_directory(const char *path,
struct edit_baton *eb = pb->edit_baton;
svn_boolean_t have_work;
svn_boolean_t conflicted;
- svn_wc_conflict_description2_t *tree_conflict = NULL;
+ svn_boolean_t conflict_ignored = FALSE;
+ svn_skel_t *tree_conflict = NULL;
svn_wc__db_status_t status, base_status;
- svn_wc__db_kind_t wc_kind;
+ svn_node_kind_t wc_kind;
SVN_ERR(make_dir_baton(&db, path, eb, pb, FALSE, pool));
*child_baton = db;
@@ -2284,7 +2378,7 @@ open_directory(const char *path,
if (is_root)
{
/* Just skip this node; a future update will handle it */
- remember_skipped_tree(eb, db->local_abspath, pool);
+ SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
db->skip_this = TRUE;
db->already_notified = TRUE;
@@ -2298,8 +2392,9 @@ open_directory(const char *path,
/* We should have a write lock on every directory touched. */
SVN_ERR(svn_wc__write_check(eb->db, db->local_abspath, pool));
- SVN_ERR(svn_wc__db_read_info(&status, &wc_kind, &db->old_revision, NULL,
- NULL, NULL, &db->changed_rev, &db->changed_date,
+ SVN_ERR(svn_wc__db_read_info(&status, &wc_kind, &db->old_revision,
+ &db->old_repos_relpath, NULL, NULL,
+ &db->changed_rev, &db->changed_date,
&db->changed_author, &db->ambient_depth,
NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
@@ -2312,19 +2407,21 @@ open_directory(const char *path,
base_status = status;
else
SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &db->old_revision,
- NULL, NULL, NULL, &db->changed_rev,
- &db->changed_date, &db->changed_author,
- &db->ambient_depth, NULL, NULL, NULL,
- NULL, NULL,
+ &db->old_repos_relpath, NULL, NULL,
+ &db->changed_rev, &db->changed_date,
+ &db->changed_author, &db->ambient_depth,
+ NULL, NULL, NULL, NULL, NULL, NULL,
eb->db, db->local_abspath,
db->pool, pool));
db->was_incomplete = (base_status == svn_wc__db_status_incomplete);
/* Is this path a conflict victim? */
- if (conflicted)
- SVN_ERR(node_already_conflicted(&conflicted, eb->db,
- db->local_abspath, pool));
+ if (db->shadowed)
+ conflicted = FALSE; /* Conflict applies to WORKING */
+ else if (conflicted)
+ SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
+ eb->db, db->local_abspath, pool));
if (conflicted)
{
SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
@@ -2337,6 +2434,10 @@ open_directory(const char *path,
return SVN_NO_ERROR;
}
+ else if (conflict_ignored)
+ {
+ db->shadowed = TRUE;
+ }
/* Is this path a fresh tree conflict victim? If so, skip the tree
with one notification. */
@@ -2345,21 +2446,31 @@ open_directory(const char *path,
* a tree-conflict on a parent node. */
if (!db->shadowed)
SVN_ERR(check_tree_conflict(&tree_conflict, eb, db->local_abspath,
- status, wc_kind, TRUE,
- svn_wc_conflict_action_edit, svn_node_dir,
- db->new_relpath, db->pool, pool));
+ status, TRUE, svn_node_dir,
+ svn_wc_conflict_action_edit,
+ db->pool, pool));
/* Remember the roots of any locally deleted trees. */
if (tree_conflict != NULL)
{
+ svn_wc_conflict_reason_t reason;
db->edit_conflict = tree_conflict;
/* Other modifications wouldn't be a tree conflict */
- SVN_ERR_ASSERT(
- tree_conflict->reason == svn_wc_conflict_reason_deleted ||
- tree_conflict->reason == svn_wc_conflict_reason_replaced);
+
+ SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
+ eb->db, db->local_abspath,
+ tree_conflict,
+ db->pool, db->pool));
+ SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_deleted
+ || reason == svn_wc_conflict_reason_moved_away
+ || reason == svn_wc_conflict_reason_replaced
+ || reason == svn_wc_conflict_reason_obstructed);
/* Continue updating BASE */
- db->shadowed = TRUE;
+ if (reason == svn_wc_conflict_reason_obstructed)
+ db->edit_obstructed = TRUE;
+ else
+ db->shadowed = TRUE;
}
/* Mark directory as being at target_revision and URL, but incomplete. */
@@ -2389,7 +2500,7 @@ change_dir_prop(void *dir_baton,
propchange->name = apr_pstrdup(db->pool, name);
propchange->value = value ? svn_string_dup(value, db->pool) : NULL;
- if (!db->edited && svn_property_kind(NULL, name) == svn_prop_regular_kind)
+ if (!db->edited && svn_property_kind2(name) == svn_prop_regular_kind)
SVN_ERR(mark_directory_edited(db, pool));
return SVN_NO_ERROR;
@@ -2436,16 +2547,20 @@ close_directory(void *dir_baton,
const char *new_changed_author = NULL;
apr_pool_t *scratch_pool = db->pool;
svn_skel_t *all_work_items = NULL;
+ svn_skel_t *conflict_skel = NULL;
/* Skip if we're in a conflicted tree. */
if (db->skip_this)
{
/* Allow the parent to complete its update. */
- SVN_ERR(maybe_release_dir_info(db->bump_info));
+ SVN_ERR(maybe_release_dir_info(db));
return SVN_NO_ERROR;
}
+ if (db->edited)
+ conflict_skel = db->edit_conflict;
+
SVN_ERR(svn_categorize_props(db->propchanges, &entry_prop_changes,
&dav_prop_changes, &regular_prop_changes, pool));
@@ -2463,9 +2578,9 @@ close_directory(void *dir_baton,
if (db->add_existed)
{
/* This node already exists. Grab the current pristine properties. */
- SVN_ERR(svn_wc__get_pristine_props(&base_props,
- eb->db, db->local_abspath,
- scratch_pool, scratch_pool));
+ SVN_ERR(svn_wc__db_read_pristine_props(&base_props,
+ eb->db, db->local_abspath,
+ scratch_pool, scratch_pool));
}
else if (!db->adding_dir)
{
@@ -2495,8 +2610,7 @@ close_directory(void *dir_baton,
{
const svn_prop_t *prop;
prop = &APR_ARRAY_IDX(regular_prop_changes, i, svn_prop_t);
- apr_hash_set(props_to_delete, prop->name,
- APR_HASH_KEY_STRING, NULL);
+ svn_hash_sets(props_to_delete, prop->name, NULL);
}
/* Add these props to the incoming propchanges (in
@@ -2518,8 +2632,6 @@ close_directory(void *dir_baton,
to deal with them. */
if (regular_prop_changes->nelts)
{
- svn_skel_t *work_item;
-
/* If recording traversal info, then see if the
SVN_PROP_EXTERNALS property on this directory changed,
and record before and after for the change. */
@@ -2533,8 +2645,7 @@ close_directory(void *dir_baton,
const svn_string_t *new_val_s = change->value;
const svn_string_t *old_val_s;
- old_val_s = apr_hash_get(base_props, SVN_PROP_EXTERNALS,
- APR_HASH_KEY_STRING);
+ old_val_s = svn_hash_gets(base_props, SVN_PROP_EXTERNALS);
if ((new_val_s == NULL) && (old_val_s == NULL))
; /* No value before, no value after... so do nothing. */
@@ -2565,34 +2676,23 @@ close_directory(void *dir_baton,
actual_props = base_props;
}
- /* Merge pending properties into temporary files (ignoring
- conflicts). */
- SVN_ERR_W(svn_wc__merge_props(&work_item,
+ /* Merge pending properties. */
+ new_base_props = svn_prop__patch(base_props, regular_prop_changes,
+ db->pool);
+ SVN_ERR_W(svn_wc__merge_props(&conflict_skel,
&prop_state,
- &new_base_props,
&new_actual_props,
eb->db,
db->local_abspath,
- svn_wc__db_kind_dir,
- NULL, /* left_version */
- NULL, /* right_version */
NULL /* use baseprops */,
base_props,
actual_props,
regular_prop_changes,
- TRUE /* base_merge */,
- FALSE /* dry_run */,
- eb->conflict_func,
- eb->conflict_baton,
- eb->cancel_func,
- eb->cancel_baton,
db->pool,
scratch_pool),
_("Couldn't do property merge"));
/* After a (not-dry-run) merge, we ALWAYS have props to save. */
SVN_ERR_ASSERT(new_base_props != NULL && new_actual_props != NULL);
- all_work_items = svn_wc__wq_merge(all_work_items, work_item,
- scratch_pool);
}
SVN_ERR(accumulate_last_change(&new_changed_rev, &new_changed_date,
@@ -2602,8 +2702,7 @@ close_directory(void *dir_baton,
/* Check if we should add some not-present markers before marking the
directory complete (Issue #3569) */
{
- apr_hash_t *new_children = apr_hash_get(eb->dir_dirents, db->new_relpath,
- APR_HASH_KEY_STRING);
+ apr_hash_t *new_children = svn_hash_gets(eb->dir_dirents, db->new_relpath);
if (new_children != NULL)
{
@@ -2619,7 +2718,7 @@ close_directory(void *dir_baton,
const char *child_relpath;
const svn_dirent_t *dirent;
svn_wc__db_status_t status;
- svn_wc__db_kind_t child_kind;
+ svn_node_kind_t child_kind;
svn_error_t *err;
svn_pool_clear(iterpool);
@@ -2630,17 +2729,17 @@ close_directory(void *dir_baton,
dirent = svn__apr_hash_index_val(hi);
child_kind = (dirent->kind == svn_node_dir)
- ? svn_wc__db_kind_dir
- : svn_wc__db_kind_file;
+ ? svn_node_dir
+ : svn_node_file;
if (db->ambient_depth < svn_depth_immediates
- && child_kind == svn_wc__db_kind_dir)
+ && child_kind == svn_node_dir)
continue; /* We don't need the subdirs */
/* ### We just check if there is some node in BASE at this path */
err = svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
eb->db, child_abspath,
iterpool, iterpool);
@@ -2705,7 +2804,7 @@ close_directory(void *dir_baton,
eb->repos_root,
eb->repos_uuid,
*eb->target_revision,
- svn_wc__db_kind_file,
+ svn_node_file,
NULL, NULL,
iterpool));
}
@@ -2725,6 +2824,7 @@ close_directory(void *dir_baton,
else
{
apr_hash_t *props;
+ apr_array_header_t *iprops = NULL;
/* ### we know a base node already exists. it was created in
### open_directory or add_directory. let's just preserve the
@@ -2763,6 +2863,42 @@ close_directory(void *dir_baton,
if (props == NULL)
props = base_props;
+ if (conflict_skel)
+ {
+ svn_skel_t *work_item;
+
+ SVN_ERR(complete_conflict(conflict_skel,
+ db->edit_baton,
+ db->local_abspath,
+ db->old_repos_relpath,
+ db->old_revision,
+ db->new_relpath,
+ svn_node_dir, svn_node_dir,
+ db->pool, scratch_pool));
+
+ SVN_ERR(svn_wc__conflict_create_markers(&work_item,
+ eb->db, db->local_abspath,
+ conflict_skel,
+ scratch_pool, scratch_pool));
+
+ all_work_items = svn_wc__wq_merge(all_work_items, work_item,
+ scratch_pool);
+ }
+
+ /* Any inherited props to be set set for this base node? */
+ if (eb->wcroot_iprops)
+ {
+ iprops = svn_hash_gets(eb->wcroot_iprops, db->local_abspath);
+
+ /* close_edit may also update iprops for switched nodes, catching
+ those for which close_directory is never called (e.g. a switch
+ with no changes). So as a minor optimization we remove any
+ iprops from the hash so as not to set them again in
+ close_edit. */
+ if (iprops)
+ svn_hash_sets(eb->wcroot_iprops, db->local_abspath, NULL);
+ }
+
/* Update the BASE data for the directory and mark the directory
complete */
SVN_ERR(svn_wc__db_base_add_directory(
@@ -2778,10 +2914,10 @@ close_directory(void *dir_baton,
(dav_prop_changes->nelts > 0)
? svn_prop_array_to_hash(dav_prop_changes, pool)
: NULL,
- NULL /* conflict */,
+ conflict_skel,
(! db->shadowed) && new_base_props != NULL,
new_actual_props,
- all_work_items,
+ iprops, all_work_items,
scratch_pool));
}
@@ -2790,6 +2926,16 @@ close_directory(void *dir_baton,
eb->cancel_func, eb->cancel_baton,
scratch_pool));
+ if (conflict_skel && eb->conflict_func)
+ SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, db->local_abspath,
+ conflict_skel,
+ NULL /* merge_options */,
+ eb->conflict_func,
+ eb->conflict_baton,
+ eb->cancel_func,
+ eb->cancel_baton,
+ scratch_pool));
+
/* Notify of any prop changes on this directory -- but do nothing if
it's an added or skipped directory, because notification has already
happened in that case - unless the add was obstructed by a dir
@@ -2800,7 +2946,7 @@ close_directory(void *dir_baton,
svn_wc_notify_t *notify;
svn_wc_notify_action_t action;
- if (db->shadowed)
+ if (db->shadowed || db->edit_obstructed)
action = svn_wc_notify_update_shadowed_update;
else if (db->obstruction_found || db->add_existed)
action = svn_wc_notify_exists;
@@ -2818,7 +2964,7 @@ close_directory(void *dir_baton,
/* We're done with this directory, so remove one reference from the
bump information. */
- SVN_ERR(maybe_release_dir_info(db->bump_info));
+ SVN_ERR(maybe_release_dir_info(db));
return SVN_NO_ERROR;
}
@@ -2827,7 +2973,7 @@ close_directory(void *dir_baton,
/* Common code for 'absent_file' and 'absent_directory'. */
static svn_error_t *
absent_node(const char *path,
- svn_wc__db_kind_t absent_kind,
+ svn_node_kind_t absent_kind,
void *parent_baton,
apr_pool_t *pool)
{
@@ -2838,7 +2984,7 @@ absent_node(const char *path,
const char *local_abspath;
svn_error_t *err;
svn_wc__db_status_t status;
- svn_wc__db_kind_t kind;
+ svn_node_kind_t kind;
if (pb->skip_this)
return SVN_NO_ERROR;
@@ -2863,21 +3009,58 @@ absent_node(const char *path,
svn_error_clear(err);
status = svn_wc__db_status_not_present;
- kind = svn_wc__db_kind_unknown;
+ kind = svn_node_unknown;
}
- if (status == svn_wc__db_status_normal
- && kind == svn_wc__db_kind_dir)
+ if (status == svn_wc__db_status_normal)
{
- /* We found an obstructing working copy!
+ svn_boolean_t wcroot;
+ /* We found an obstructing working copy or a file external! */
- We can do two things now:
- 1) notify the user, record a skip, etc.
- 2) Just record the absent node in BASE in the parent
- working copy.
+ SVN_ERR(svn_wc__db_is_wcroot(&wcroot, eb->db, local_abspath,
+ scratch_pool));
- As option 2 happens to be exactly what we do anyway, lets do that.
- */
+ if (wcroot)
+ {
+ /*
+ We have an obstructing working copy; possibly a directory external
+
+ We can do two things now:
+ 1) notify the user, record a skip, etc.
+ 2) Just record the absent node in BASE in the parent
+ working copy.
+
+ As option 2 happens to be exactly what we do anyway, fall through.
+ */
+ }
+ else
+ {
+ /* The server asks us to replace a file external
+ (Existing BASE node; not reported by the working copy crawler or
+ there would have been a delete_entry() call.
+
+ There is no way we can store this state in the working copy as
+ the BASE layer is already filled.
+
+ We could error out, but that is not helping anybody; the user is not
+ even seeing with what the file external would be replaced, so let's
+ report a skip and continue the update.
+ */
+
+ if (eb->notify_func)
+ {
+ svn_wc_notify_t *notify;
+ notify = svn_wc_create_notify(
+ local_abspath,
+ svn_wc_notify_update_skip_obstruction,
+ scratch_pool);
+
+ eb->notify_func(eb->notify_baton, notify, scratch_pool);
+ }
+
+ svn_pool_destroy(scratch_pool);
+ return SVN_NO_ERROR;
+ }
}
else if (status == svn_wc__db_status_not_present
|| status == svn_wc__db_status_server_excluded
@@ -2933,7 +3116,7 @@ absent_file(const char *path,
void *parent_baton,
apr_pool_t *pool)
{
- return absent_node(path, svn_wc__db_kind_file, parent_baton, pool);
+ return absent_node(path, svn_node_file, parent_baton, pool);
}
@@ -2943,7 +3126,7 @@ absent_directory(const char *path,
void *parent_baton,
apr_pool_t *pool)
{
- return absent_node(path, svn_wc__db_kind_dir, parent_baton, pool);
+ return absent_node(path, svn_node_dir, parent_baton, pool);
}
@@ -2960,12 +3143,13 @@ add_file(const char *path,
struct edit_baton *eb = pb->edit_baton;
struct file_baton *fb;
svn_node_kind_t kind = svn_node_none;
- svn_wc__db_kind_t wc_kind = svn_wc__db_kind_unknown;
+ svn_node_kind_t wc_kind = svn_node_unknown;
svn_wc__db_status_t status = svn_wc__db_status_normal;
apr_pool_t *scratch_pool;
svn_boolean_t conflicted = FALSE;
+ svn_boolean_t conflict_ignored = FALSE;
svn_boolean_t versioned_locally_and_present = FALSE;
- svn_wc_conflict_description2_t *tree_conflict = NULL;
+ svn_skel_t *tree_conflict = NULL;
svn_error_t *err = SVN_NO_ERROR;
SVN_ERR_ASSERT(! (copyfrom_path || SVN_IS_VALID_REVNUM(copyfrom_rev)));
@@ -3009,12 +3193,12 @@ add_file(const char *path,
return svn_error_trace(err);
svn_error_clear(err);
- wc_kind = svn_wc__db_kind_unknown;
+ wc_kind = svn_node_unknown;
conflicted = FALSE;
versioned_locally_and_present = FALSE;
}
- else if (wc_kind == svn_wc__db_kind_dir
+ else if (wc_kind == svn_node_dir
&& status == svn_wc__db_status_normal)
{
/* !! We found the root of a separate working copy obstructing the wc !!
@@ -3025,10 +3209,10 @@ add_file(const char *path,
The only thing we can do is add a not-present node, to allow
a future update to bring in the new files when the problem is
resolved. */
- apr_hash_set(pb->not_present_files, apr_pstrdup(pb->pool, fb->name),
- APR_HASH_KEY_STRING, (void*)1);
+ svn_hash_sets(pb->not_present_files, apr_pstrdup(pb->pool, fb->name),
+ (void *)1);
- remember_skipped_tree(eb, fb->local_abspath, pool);
+ SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
fb->skip_this = TRUE;
fb->already_notified = TRUE;
@@ -3040,8 +3224,8 @@ add_file(const char *path,
return SVN_NO_ERROR;
}
else if (status == svn_wc__db_status_normal
- && (wc_kind == svn_wc__db_kind_file
- || wc_kind == svn_wc__db_kind_symlink))
+ && (wc_kind == svn_node_file
+ || wc_kind == svn_node_symlink))
{
/* We found a file external occupating the place we need in BASE.
@@ -3053,7 +3237,7 @@ add_file(const char *path,
The reason we get here is that the adm crawler doesn't report
file externals.
*/
- remember_skipped_tree(eb, fb->local_abspath, pool);
+ SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
fb->skip_this = TRUE;
fb->already_notified = TRUE;
@@ -3064,41 +3248,53 @@ add_file(const char *path,
return SVN_NO_ERROR;
}
- else if (wc_kind == svn_wc__db_kind_unknown)
+ else if (wc_kind == svn_node_unknown)
versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */
else
versioned_locally_and_present = IS_NODE_PRESENT(status);
/* Is this path a conflict victim? */
- if (conflicted)
- if (conflicted)
+ if (fb->shadowed)
+ conflicted = FALSE; /* Conflict applies to WORKING */
+ else if (conflicted)
{
if (pb->deletion_conflicts)
- tree_conflict = apr_hash_get(pb->deletion_conflicts, fb->name,
- APR_HASH_KEY_STRING);
+ tree_conflict = svn_hash_gets(pb->deletion_conflicts, fb->name);
if (tree_conflict)
{
+ svn_wc_conflict_reason_t reason;
+ const char *move_src_op_root_abspath;
/* So this deletion wasn't just a deletion, it is actually a
- replacement. Luckily we still have the conflict so we can
- just update it. */
+ replacement. Let's install a better tree conflict. */
- /* ### What else should we update? */
- tree_conflict->action = svn_wc_conflict_action_replace;
+ SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL,
+ &move_src_op_root_abspath,
+ eb->db,
+ fb->local_abspath,
+ tree_conflict,
+ fb->pool, fb->pool));
- SVN_ERR(svn_wc__db_op_set_tree_conflict(eb->db, fb->local_abspath,
- tree_conflict, pool));
+ tree_conflict = svn_wc__conflict_skel_create(fb->pool);
+
+ SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
+ tree_conflict,
+ eb->db, fb->local_abspath,
+ reason, svn_wc_conflict_action_replace,
+ move_src_op_root_abspath,
+ fb->pool, fb->pool));
/* And now stop checking for conflicts here and just perform
a shadowed update */
+ fb->edit_conflict = tree_conflict; /* Cache for close_file */
tree_conflict = NULL; /* No direct notification */
fb->shadowed = TRUE; /* Just continue */
conflicted = FALSE; /* No skip */
}
else
- SVN_ERR(node_already_conflicted(&conflicted, eb->db,
- fb->local_abspath, pool));
+ SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
+ eb->db, fb->local_abspath, pool));
}
/* Now the usual conflict handling: skip. */
@@ -3120,8 +3316,8 @@ add_file(const char *path,
Note that we can safely assume that no present base node exists,
because then we would not have received an add_file.
*/
- apr_hash_set(pb->not_present_files, apr_pstrdup(pb->pool, fb->name),
- APR_HASH_KEY_STRING, (void*)1);
+ svn_hash_sets(pb->not_present_files, apr_pstrdup(pb->pool, fb->name),
+ (void *)1);
do_notification(eb, fb->local_abspath, svn_node_unknown,
svn_wc_notify_skip_conflicted, scratch_pool);
@@ -3130,6 +3326,10 @@ add_file(const char *path,
return SVN_NO_ERROR;
}
+ else if (conflict_ignored)
+ {
+ fb->shadowed = TRUE;
+ }
if (fb->shadowed)
{
@@ -3161,8 +3361,8 @@ add_file(const char *path,
scratch_pool, scratch_pool));
/* Is there something that is a file? */
- local_is_file = (wc_kind == svn_wc__db_kind_file
- || wc_kind == svn_wc__db_kind_symlink);
+ local_is_file = (wc_kind == svn_node_file
+ || wc_kind == svn_node_symlink);
/* Do tree conflict checking if
* - if there is a local copy.
@@ -3178,9 +3378,8 @@ add_file(const char *path,
{
SVN_ERR(check_tree_conflict(&tree_conflict, eb,
fb->local_abspath,
- status, wc_kind, FALSE,
+ status, FALSE, svn_node_none,
svn_wc_conflict_action_add,
- svn_node_file, fb->new_relpath,
scratch_pool, scratch_pool));
}
@@ -3203,14 +3402,15 @@ add_file(const char *path,
fb->shadowed = TRUE;
/* Mark a conflict */
- SVN_ERR(create_tree_conflict(&tree_conflict, eb,
- fb->local_abspath,
- svn_wc_conflict_reason_unversioned,
- svn_wc_conflict_action_add,
- svn_node_file,
- fb->new_relpath,
- scratch_pool, scratch_pool));
- SVN_ERR_ASSERT(tree_conflict != NULL);
+ tree_conflict = svn_wc__conflict_skel_create(fb->pool);
+
+ SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
+ tree_conflict,
+ eb->db, fb->local_abspath,
+ svn_wc_conflict_reason_unversioned,
+ svn_wc_conflict_action_add,
+ NULL,
+ fb->pool, scratch_pool));
}
}
@@ -3221,16 +3421,36 @@ add_file(const char *path,
|| *eb->target_basename == '\0'
|| (strcmp(fb->local_abspath, eb->target_abspath) != 0))
{
- apr_hash_set(pb->not_present_files, apr_pstrdup(pb->pool, fb->name),
- APR_HASH_KEY_STRING, (void*)1);
+ svn_hash_sets(pb->not_present_files, apr_pstrdup(pb->pool, fb->name),
+ (void *)1);
}
if (tree_conflict != NULL)
{
- SVN_ERR(svn_wc__db_op_set_tree_conflict(eb->db,
- fb->local_abspath,
- tree_conflict,
- scratch_pool));
+ SVN_ERR(complete_conflict(tree_conflict,
+ fb->edit_baton,
+ fb->local_abspath,
+ fb->old_repos_relpath,
+ fb->old_revision,
+ fb->new_relpath,
+ wc_kind,
+ svn_node_file,
+ fb->pool, scratch_pool));
+
+ SVN_ERR(svn_wc__db_op_mark_conflict(eb->db,
+ fb->local_abspath,
+ tree_conflict, NULL,
+ scratch_pool));
+
+ if (eb->conflict_func)
+ SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, fb->local_abspath,
+ tree_conflict,
+ NULL /* merge_options */,
+ eb->conflict_func,
+ eb->conflict_baton,
+ eb->cancel_func,
+ eb->cancel_baton,
+ scratch_pool));
fb->already_notified = TRUE;
do_notification(eb, fb->local_abspath, svn_node_file,
@@ -3255,10 +3475,11 @@ open_file(const char *path,
struct edit_baton *eb = pb->edit_baton;
struct file_baton *fb;
svn_boolean_t conflicted;
+ svn_boolean_t conflict_ignored = FALSE;
svn_boolean_t have_work;
svn_wc__db_status_t status;
- svn_wc__db_kind_t wc_kind;
- svn_wc_conflict_description2_t *tree_conflict = NULL;
+ svn_node_kind_t wc_kind;
+ svn_skel_t *tree_conflict = NULL;
/* the file_pool can stick around for a *long* time, so we want to use
a subpool for any temporary allocations. */
@@ -3280,7 +3501,7 @@ open_file(const char *path,
if (is_root)
{
/* Just skip this node; a future update will handle it */
- remember_skipped_tree(eb, fb->local_abspath, pool);
+ SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
fb->skip_this = TRUE;
fb->already_notified = TRUE;
@@ -3294,29 +3515,33 @@ open_file(const char *path,
/* Sanity check. */
/* If replacing, make sure the .svn entry already exists. */
- SVN_ERR(svn_wc__db_read_info(&status, &wc_kind, &fb->old_revision, NULL,
- NULL, NULL, &fb->changed_rev, &fb->changed_date,
+ SVN_ERR(svn_wc__db_read_info(&status, &wc_kind, &fb->old_revision,
+ &fb->old_repos_relpath, NULL, NULL,
+ &fb->changed_rev, &fb->changed_date,
&fb->changed_author, NULL,
&fb->original_checksum, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
- &conflicted, NULL, NULL, NULL,
+ &conflicted, NULL, NULL, &fb->local_prop_mods,
NULL, NULL, &have_work,
eb->db, fb->local_abspath,
fb->pool, scratch_pool));
if (have_work)
SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &fb->old_revision,
- NULL, NULL, NULL, &fb->changed_rev,
- &fb->changed_date, &fb->changed_author,
- NULL, &fb->original_checksum, NULL, NULL,
- NULL, NULL,
+ &fb->old_repos_relpath, NULL, NULL,
+ &fb->changed_rev, &fb->changed_date,
+ &fb->changed_author, NULL,
+ &fb->original_checksum, NULL, NULL,
+ NULL, NULL, NULL,
eb->db, fb->local_abspath,
fb->pool, scratch_pool));
/* Is this path a conflict victim? */
- if (conflicted)
- SVN_ERR(node_already_conflicted(&conflicted, eb->db,
- fb->local_abspath, pool));
+ if (fb->shadowed)
+ conflicted = FALSE; /* Conflict applies to WORKING */
+ else if (conflicted)
+ SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
+ eb->db, fb->local_abspath, pool));
if (conflicted)
{
SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
@@ -3331,29 +3556,40 @@ open_file(const char *path,
return SVN_NO_ERROR;
}
-
- fb->shadowed = pb->shadowed;
+ else if (conflict_ignored)
+ {
+ fb->shadowed = TRUE;
+ }
/* Check for conflicts only when we haven't already recorded
* a tree-conflict on a parent node. */
if (!fb->shadowed)
SVN_ERR(check_tree_conflict(&tree_conflict, eb, fb->local_abspath,
- status, wc_kind, TRUE,
- svn_wc_conflict_action_edit, svn_node_file,
- fb->new_relpath, fb->pool, scratch_pool));
+ status, TRUE, svn_node_file,
+ svn_wc_conflict_action_edit,
+ fb->pool, scratch_pool));
/* Is this path the victim of a newly-discovered tree conflict? */
if (tree_conflict != NULL)
{
+ svn_wc_conflict_reason_t reason;
fb->edit_conflict = tree_conflict;
-
/* Other modifications wouldn't be a tree conflict */
- SVN_ERR_ASSERT(
- tree_conflict->reason == svn_wc_conflict_reason_deleted ||
- tree_conflict->reason == svn_wc_conflict_reason_replaced);
+
+ SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
+ eb->db, fb->local_abspath,
+ tree_conflict,
+ scratch_pool, scratch_pool));
+ SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_deleted
+ || reason == svn_wc_conflict_reason_moved_away
+ || reason == svn_wc_conflict_reason_replaced
+ || reason == svn_wc_conflict_reason_obstructed);
/* Continue updating BASE */
- fb->shadowed = TRUE;
+ if (reason == svn_wc_conflict_reason_obstructed)
+ fb->edit_obstructed = TRUE;
+ else
+ fb->shadowed = TRUE;
}
svn_pool_destroy(scratch_pool);
@@ -3361,6 +3597,48 @@ open_file(const char *path,
return SVN_NO_ERROR;
}
+/* Implements svn_stream_lazyopen_func_t. */
+static svn_error_t *
+lazy_open_source(svn_stream_t **stream,
+ void *baton,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ struct file_baton *fb = baton;
+
+ SVN_ERR(svn_wc__db_pristine_read(stream, NULL, fb->edit_baton->db,
+ fb->local_abspath,
+ fb->original_checksum,
+ result_pool, scratch_pool));
+
+
+ return SVN_NO_ERROR;
+}
+
+struct lazy_target_baton {
+ struct file_baton *fb;
+ struct handler_baton *hb;
+ struct edit_baton *eb;
+};
+
+/* Implements svn_stream_lazyopen_func_t. */
+static svn_error_t *
+lazy_open_target(svn_stream_t **stream,
+ void *baton,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ struct lazy_target_baton *tb = baton;
+
+ SVN_ERR(svn_wc__open_writable_base(stream, &tb->hb->new_text_base_tmp_abspath,
+ NULL, &tb->hb->new_text_base_sha1_checksum,
+ tb->fb->edit_baton->db,
+ tb->eb->wcroot_abspath,
+ result_pool, scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
/* An svn_delta_editor_t function. */
static svn_error_t *
apply_textdelta(void *file_baton,
@@ -3373,10 +3651,10 @@ apply_textdelta(void *file_baton,
apr_pool_t *handler_pool = svn_pool_create(fb->pool);
struct handler_baton *hb = apr_pcalloc(handler_pool, sizeof(*hb));
struct edit_baton *eb = fb->edit_baton;
- svn_error_t *err;
const svn_checksum_t *recorded_base_checksum;
svn_checksum_t *expected_base_checksum;
svn_stream_t *source;
+ struct lazy_target_baton *tb;
svn_stream_t *target;
if (fb->skip_this)
@@ -3443,10 +3721,8 @@ apply_textdelta(void *file_baton,
SVN_ERR_ASSERT(!fb->original_checksum
|| fb->original_checksum->kind == svn_checksum_sha1);
- SVN_ERR(svn_wc__db_pristine_read(&source, NULL, fb->edit_baton->db,
- fb->local_abspath,
- fb->original_checksum,
- handler_pool, handler_pool));
+ source = svn_stream_lazyopen_create(lazy_open_source, fb, FALSE,
+ handler_pool);
}
else
{
@@ -3472,16 +3748,11 @@ apply_textdelta(void *file_baton,
hb->source_checksum_stream = source;
}
- /* Open the text base for writing (this will get us a temporary file). */
- err = svn_wc__open_writable_base(&target, &hb->new_text_base_tmp_abspath,
- NULL, &hb->new_text_base_sha1_checksum,
- fb->edit_baton->db, eb->wcroot_abspath,
- handler_pool, pool);
- if (err)
- {
- svn_pool_destroy(handler_pool);
- return svn_error_trace(err);
- }
+ tb = apr_palloc(handler_pool, sizeof(struct lazy_target_baton));
+ tb->hb = hb;
+ tb->fb = fb;
+ tb->eb = eb;
+ target = svn_stream_lazyopen_create(lazy_open_target, tb, TRUE, handler_pool);
/* Prepare to apply the delta. */
svn_txdelta_apply(source, target,
@@ -3519,9 +3790,89 @@ change_file_prop(void *file_baton,
propchange->name = apr_pstrdup(fb->pool, name);
propchange->value = value ? svn_string_dup(value, fb->pool) : NULL;
- if (!fb->edited && svn_property_kind(NULL, name) == svn_prop_regular_kind)
+ if (!fb->edited && svn_property_kind2(name) == svn_prop_regular_kind)
SVN_ERR(mark_file_edited(fb, scratch_pool));
+ if (! fb->shadowed
+ && strcmp(name, SVN_PROP_SPECIAL) == 0)
+ {
+ struct edit_baton *eb = fb->edit_baton;
+ svn_boolean_t modified = FALSE;
+ svn_boolean_t becomes_symlink;
+ svn_boolean_t was_symlink;
+
+ /* Let's see if we have a change as in some scenarios servers report
+ non-changes of properties. */
+ becomes_symlink = (value != NULL);
+
+ if (fb->adding_file)
+ was_symlink = becomes_symlink; /* No change */
+ else
+ {
+ apr_hash_t *props;
+
+ /* We read the server-props, not the ACTUAL props here as we just
+ want to see if this is really an incoming prop change. */
+ SVN_ERR(svn_wc__db_base_get_props(&props, eb->db,
+ fb->local_abspath,
+ scratch_pool, scratch_pool));
+
+ was_symlink = ((props
+ && svn_hash_gets(props, SVN_PROP_SPECIAL) != NULL)
+ ? svn_tristate_true
+ : svn_tristate_false);
+ }
+
+ if (was_symlink != becomes_symlink)
+ {
+ /* If the local node was not modified, we continue as usual, if
+ modified we want a tree conflict just like how we would handle
+ it when receiving a delete + add (aka "replace") */
+ if (fb->local_prop_mods)
+ modified = TRUE;
+ else
+ SVN_ERR(svn_wc__internal_file_modified_p(&modified, eb->db,
+ fb->local_abspath,
+ FALSE, scratch_pool));
+ }
+
+ if (modified)
+ {
+ if (!fb->edit_conflict)
+ fb->edit_conflict = svn_wc__conflict_skel_create(fb->pool);
+
+ SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
+ fb->edit_conflict,
+ eb->db, fb->local_abspath,
+ svn_wc_conflict_reason_edited,
+ svn_wc_conflict_action_replace,
+ NULL,
+ fb->pool, scratch_pool));
+
+ SVN_ERR(complete_conflict(fb->edit_conflict, fb->edit_baton,
+ fb->local_abspath, fb->old_repos_relpath,
+ fb->old_revision, fb->new_relpath,
+ svn_node_file, svn_node_file,
+ fb->pool, scratch_pool));
+
+ /* Create a copy of the existing (pre update) BASE node in WORKING,
+ mark a tree conflict and handle the rest of the update as
+ shadowed */
+ SVN_ERR(svn_wc__db_op_make_copy(eb->db, fb->local_abspath,
+ fb->edit_conflict, NULL,
+ scratch_pool));
+
+ do_notification(eb, fb->local_abspath, svn_node_file,
+ svn_wc_notify_tree_conflict, scratch_pool);
+
+ /* Ok, we introduced a replacement, so we can now handle the rest
+ as a normal shadowed update */
+ fb->shadowed = TRUE;
+ fb->add_existed = FALSE;
+ fb->already_notified = TRUE;
+ }
+ }
+
return SVN_NO_ERROR;
}
@@ -3533,37 +3884,37 @@ change_file_prop(void *file_baton,
identified by WRI_ABSPATH. Use OLD_REVISION and TARGET_REVISION for naming
the intermediate files.
- The rest of the arguments are passed to svn_wc__internal_merge.
+ The rest of the arguments are passed to svn_wc__internal_merge().
*/
svn_error_t *
svn_wc__perform_file_merge(svn_skel_t **work_items,
- enum svn_wc_merge_outcome_t *merge_outcome,
+ svn_skel_t **conflict_skel,
+ svn_boolean_t *found_conflict,
svn_wc__db_t *db,
const char *local_abspath,
const char *wri_abspath,
const svn_checksum_t *new_checksum,
const svn_checksum_t *original_checksum,
- apr_hash_t *actual_props,
+ apr_hash_t *old_actual_props,
const apr_array_header_t *ext_patterns,
svn_revnum_t old_revision,
svn_revnum_t target_revision,
const apr_array_header_t *propchanges,
const char *diff3_cmd,
- svn_wc_conflict_resolver_func2_t conflict_func,
- void *conflict_baton,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
/* Actual file exists and has local mods:
- Now we need to let loose svn_wc__merge_internal() to merge
+ Now we need to let loose svn_wc__internal_merge() to merge
the textual changes into the working file. */
const char *oldrev_str, *newrev_str, *mine_str;
const char *merge_left;
svn_boolean_t delete_left = FALSE;
const char *path_ext = "";
const char *new_text_base_tmp_abspath;
+ enum svn_wc_merge_outcome_t merge_outcome = svn_wc_merge_unchanged;
svn_skel_t *work_item;
*work_items = NULL;
@@ -3616,28 +3967,29 @@ svn_wc__perform_file_merge(svn_skel_t **work_items,
/* Merge the changes from the old textbase to the new
textbase into the file we're updating.
Remember that this function wants full paths! */
- /* ### TODO: Pass version info here. */
SVN_ERR(svn_wc__internal_merge(&work_item,
- merge_outcome,
+ conflict_skel,
+ &merge_outcome,
db,
- merge_left, NULL,
- new_text_base_tmp_abspath, NULL,
+ merge_left,
+ new_text_base_tmp_abspath,
local_abspath,
wri_abspath,
oldrev_str, newrev_str, mine_str,
- actual_props,
+ old_actual_props,
FALSE /* dry_run */,
diff3_cmd, NULL, propchanges,
- conflict_func, conflict_baton,
cancel_func, cancel_baton,
result_pool, scratch_pool));
*work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
+ *found_conflict = (merge_outcome == svn_wc_merge_conflict);
/* If we created a temporary left merge file, get rid of it. */
if (delete_left)
{
- SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, merge_left,
+ SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, wri_abspath,
+ merge_left,
result_pool, scratch_pool));
*work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
}
@@ -3670,6 +4022,7 @@ svn_wc__perform_file_merge(svn_skel_t **work_items,
*/
static svn_error_t *
merge_file(svn_skel_t **work_items,
+ svn_skel_t **conflict_skel,
svn_boolean_t *install_pristine,
const char **install_from,
svn_wc_notify_state_t *content_state,
@@ -3682,10 +4035,11 @@ merge_file(svn_skel_t **work_items,
struct edit_baton *eb = fb->edit_baton;
struct dir_baton *pb = fb->dir_baton;
svn_boolean_t is_locally_modified;
- enum svn_wc_merge_outcome_t merge_outcome = svn_wc_merge_unchanged;
- svn_skel_t *work_item;
+ svn_boolean_t found_text_conflict = FALSE;
- SVN_ERR_ASSERT(! fb->shadowed && !fb->obstruction_found);
+ SVN_ERR_ASSERT(! fb->shadowed
+ && ! fb->obstruction_found
+ && ! fb->edit_obstructed);
/*
When this function is called on file F, we assume the following
@@ -3770,7 +4124,8 @@ merge_file(svn_skel_t **work_items,
Now we need to let loose svn_wc__merge_internal() to merge
the textual changes into the working file. */
SVN_ERR(svn_wc__perform_file_merge(work_items,
- &merge_outcome,
+ conflict_skel,
+ &found_text_conflict,
eb->db,
fb->local_abspath,
pb->local_abspath,
@@ -3784,7 +4139,6 @@ merge_file(svn_skel_t **work_items,
*eb->target_revision,
fb->propchanges,
eb->diff3_cmd,
- eb->conflict_func, eb->conflict_baton,
eb->cancel_func, eb->cancel_baton,
result_pool, scratch_pool));
} /* end: working file exists and has mods */
@@ -3852,36 +4206,9 @@ merge_file(svn_skel_t **work_items,
}
}
- /* Installing from a pristine will handle timestamps and recording.
- However, if we are NOT creating a new working copy file, then create
- work items to handle the recording of the timestamp and working-size. */
- if (!*install_pristine
- && !is_locally_modified)
- {
- apr_time_t set_date = 0;
-
- if (eb->use_commit_times && last_changed_date != 0)
- {
- set_date = last_changed_date;
- }
-
- SVN_ERR(svn_wc__wq_build_record_fileinfo(&work_item,
- eb->db, fb->local_abspath,
- set_date,
- result_pool, scratch_pool));
- *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
- }
-
/* Set the returned content state. */
- /* This is kind of interesting. Even if no new text was
- installed (i.e., NEW_TEXT_BASE_ABSPATH was null), we could still
- report a pre-existing conflict state. Say a file, already
- in a state of textual conflict, receives prop mods during an
- update. Then we'll notify that it has text conflicts. This
- seems okay to me. I guess. I dunno. You? */
-
- if (merge_outcome == svn_wc_merge_conflict)
+ if (found_text_conflict)
*content_state = svn_wc_notify_state_conflicted;
else if (fb->new_text_base_sha1_checksum)
{
@@ -3905,6 +4232,7 @@ close_file(void *file_baton,
apr_pool_t *pool)
{
struct file_baton *fb = file_baton;
+ struct dir_baton *pdb = fb->dir_baton;
struct edit_baton *eb = fb->edit_baton;
svn_wc_notify_state_t content_state, prop_state;
svn_wc_notify_lock_state_t lock_state;
@@ -3918,17 +4246,23 @@ close_file(void *file_baton,
apr_hash_t *current_actual_props = NULL;
apr_hash_t *local_actual_props = NULL;
svn_skel_t *all_work_items = NULL;
+ svn_skel_t *conflict_skel = NULL;
svn_skel_t *work_item;
apr_pool_t *scratch_pool = fb->pool; /* Destroyed at function exit */
svn_boolean_t keep_recorded_info = FALSE;
+ const svn_checksum_t *new_checksum;
+ apr_array_header_t *iprops = NULL;
if (fb->skip_this)
{
- SVN_ERR(maybe_release_dir_info(fb->bump_info));
svn_pool_destroy(fb->pool);
+ SVN_ERR(maybe_release_dir_info(pdb));
return SVN_NO_ERROR;
}
+ if (fb->edited)
+ conflict_skel = fb->edit_conflict;
+
if (expected_md5_digest)
SVN_ERR(svn_checksum_parse_hex(&expected_md5_checksum, svn_checksum_md5,
expected_md5_digest, scratch_pool));
@@ -3936,10 +4270,13 @@ close_file(void *file_baton,
if (fb->new_text_base_md5_checksum && expected_md5_checksum
&& !svn_checksum_match(expected_md5_checksum,
fb->new_text_base_md5_checksum))
- return svn_checksum_mismatch_err(expected_md5_checksum,
- fb->new_text_base_md5_checksum, scratch_pool,
- _("Checksum mismatch for '%s'"),
- svn_dirent_local_style(fb->local_abspath, pool));
+ return svn_error_trace(
+ svn_checksum_mismatch_err(expected_md5_checksum,
+ fb->new_text_base_md5_checksum,
+ scratch_pool,
+ _("Checksum mismatch for '%s'"),
+ svn_dirent_local_style(
+ fb->local_abspath, pool)));
/* Gather the changes for each kind of property. */
SVN_ERR(svn_categorize_props(fb->propchanges, &entry_prop_changes,
@@ -3982,11 +4319,17 @@ close_file(void *file_baton,
and we should likewise remove our cached copy of it. */
if (! strcmp(prop->name, SVN_PROP_ENTRY_LOCK_TOKEN))
{
- SVN_ERR_ASSERT(prop->value == NULL);
- SVN_ERR(svn_wc__db_lock_remove(eb->db, fb->local_abspath,
- scratch_pool));
+ /* If we lose the lock, but not because we are switching to
+ another url, remove the state lock from the wc */
+ if (! eb->switch_relpath
+ || strcmp(fb->new_relpath, fb->old_repos_relpath) == 0)
+ {
+ SVN_ERR_ASSERT(prop->value == NULL);
+ SVN_ERR(svn_wc__db_lock_remove(eb->db, fb->local_abspath,
+ scratch_pool));
- lock_state = svn_wc_notify_lock_state_unlocked;
+ lock_state = svn_wc_notify_lock_state_unlocked;
+ }
break;
}
}
@@ -4016,9 +4359,9 @@ close_file(void *file_baton,
if (fb->add_existed)
{
/* This node already exists. Grab the current pristine properties. */
- SVN_ERR(svn_wc__get_pristine_props(&current_base_props,
- eb->db, fb->local_abspath,
- scratch_pool, scratch_pool));
+ SVN_ERR(svn_wc__db_read_pristine_props(&current_base_props,
+ eb->db, fb->local_abspath,
+ scratch_pool, scratch_pool));
current_actual_props = local_actual_props;
}
else if (!fb->adding_file)
@@ -4039,67 +4382,6 @@ close_file(void *file_baton,
if (current_actual_props == NULL)
current_actual_props = apr_hash_make(scratch_pool);
- /* Catch symlink-ness change.
- * add_file() doesn't know whether the incoming added node is a file or
- * a symlink, because symlink-ness is saved in a prop :(
- * So add_file() cannot notice when update wants to add a symlink where
- * locally there already is a file scheduled for addition, or vice versa.
- * It sees incoming symlinks as simple files and may wrongly try to offer
- * a text conflict. So flag a tree conflict here. */
- if (!fb->shadowed
- && (! fb->adding_file || fb->add_existed))
- {
- svn_boolean_t local_is_link;
- svn_boolean_t incoming_is_link;
- int i;
-
- local_is_link = apr_hash_get(local_actual_props,
- SVN_PROP_SPECIAL,
- APR_HASH_KEY_STRING) != NULL;
-
- incoming_is_link = local_is_link;
-
- /* Does an incoming propchange affect symlink-ness? */
- for (i = 0; i < regular_prop_changes->nelts; ++i)
- {
- const svn_prop_t *prop = &APR_ARRAY_IDX(regular_prop_changes, i,
- svn_prop_t);
-
- if (strcmp(prop->name, SVN_PROP_SPECIAL) == 0)
- {
- incoming_is_link = (prop->value != NULL);
- break;
- }
- }
-
- if (local_is_link != incoming_is_link)
- {
- svn_wc_conflict_description2_t *tree_conflict = NULL;
-
- fb->shadowed = TRUE;
- fb->obstruction_found = TRUE;
- fb->add_existed = FALSE;
-
- /* ### Performance: We should just create the conflict here, without
- ### verifying again */
- SVN_ERR(check_tree_conflict(&tree_conflict, eb, fb->local_abspath,
- svn_wc__db_status_added,
- svn_wc__db_kind_file, TRUE,
- svn_wc_conflict_action_add,
- svn_node_file, fb->new_relpath,
- scratch_pool, scratch_pool));
- SVN_ERR_ASSERT(tree_conflict != NULL);
- SVN_ERR(svn_wc__db_op_set_tree_conflict(eb->db,
- fb->local_abspath,
- tree_conflict,
- scratch_pool));
-
- fb->already_notified = TRUE;
- do_notification(eb, fb->local_abspath, svn_node_unknown,
- svn_wc_notify_tree_conflict, scratch_pool);
- }
- }
-
prop_state = svn_wc_notify_state_unknown;
if (! fb->shadowed)
@@ -4111,35 +4393,28 @@ close_file(void *file_baton,
/* This will merge the old and new props into a new prop db, and
write <cp> commands to the logfile to install the merged
props. */
- SVN_ERR(svn_wc__merge_props(&work_item,
+ new_base_props = svn_prop__patch(current_base_props, regular_prop_changes,
+ scratch_pool);
+ SVN_ERR(svn_wc__merge_props(&conflict_skel,
&prop_state,
- &new_base_props,
&new_actual_props,
eb->db,
fb->local_abspath,
- svn_wc__db_kind_file,
- NULL /* left_version */,
- NULL /* right_version */,
NULL /* server_baseprops (update, not merge) */,
current_base_props,
current_actual_props,
regular_prop_changes, /* propchanges */
- TRUE /* base_merge */,
- FALSE /* dry_run */,
- eb->conflict_func, eb->conflict_baton,
- eb->cancel_func, eb->cancel_baton,
scratch_pool,
scratch_pool));
/* We will ALWAYS have properties to save (after a not-dry-run merge). */
SVN_ERR_ASSERT(new_base_props != NULL && new_actual_props != NULL);
- all_work_items = svn_wc__wq_merge(all_work_items, work_item,
- scratch_pool);
/* Merge the text. This will queue some additional work. */
- if (!fb->obstruction_found)
+ if (!fb->obstruction_found && !fb->edit_obstructed)
{
svn_error_t *err;
- err = merge_file(&work_item, &install_pristine, &install_from,
+ err = merge_file(&work_item, &conflict_skel,
+ &install_pristine, &install_from,
&content_state, fb, current_actual_props,
fb->changed_date, scratch_pool, scratch_pool);
@@ -4163,8 +4438,8 @@ close_file(void *file_baton,
scratch_pool));
fb->skip_this = TRUE;
- SVN_ERR(maybe_release_dir_info(fb->bump_info));
svn_pool_destroy(fb->pool);
+ SVN_ERR(maybe_release_dir_info(pdb));
return SVN_NO_ERROR;
}
else
@@ -4232,7 +4507,7 @@ close_file(void *file_baton,
&& strcmp(install_from, fb->local_abspath) != 0)
{
SVN_ERR(svn_wc__wq_build_file_remove(&work_item, eb->db,
- install_from,
+ fb->local_abspath, install_from,
scratch_pool, scratch_pool));
all_work_items = svn_wc__wq_merge(all_work_items, work_item,
scratch_pool);
@@ -4250,29 +4525,20 @@ close_file(void *file_baton,
/* Store the incoming props (sent as propchanges) in new_base_props
and create a set of new actual props to use for notifications */
- SVN_ERR(svn_wc__merge_props(&work_item,
+ new_base_props = svn_prop__patch(current_base_props, regular_prop_changes,
+ scratch_pool);
+ SVN_ERR(svn_wc__merge_props(&conflict_skel,
&prop_state,
- &new_base_props,
&new_actual_props,
eb->db,
fb->local_abspath,
- svn_wc__db_kind_file,
- NULL /* left_version */,
- NULL /* right_version */,
NULL /* server_baseprops (not merging) */,
- current_base_props /* base_props */,
- fake_actual_props /* working_props */,
+ current_base_props /* pristine_props */,
+ fake_actual_props /* actual_props */,
regular_prop_changes, /* propchanges */
- TRUE /* base_merge */,
- FALSE /* dry_run */,
- NULL, NULL, /* No conflict handling */
- eb->cancel_func, eb->cancel_baton,
scratch_pool,
scratch_pool));
- all_work_items = svn_wc__wq_merge(all_work_items, work_item,
- scratch_pool);
-
if (fb->new_text_base_sha1_checksum)
content_state = svn_wc_notify_state_changed;
else
@@ -4280,54 +4546,89 @@ close_file(void *file_baton,
}
/* Insert/replace the BASE node with all of the new metadata. */
- {
- /* Set the 'checksum' column of the file's BASE_NODE row to
- * NEW_TEXT_BASE_SHA1_CHECKSUM. The pristine text identified by that
- * checksum is already in the pristine store. */
- const svn_checksum_t *new_checksum = fb->new_text_base_sha1_checksum;
-
- /* If we don't have a NEW checksum, then the base must not have changed.
- Just carry over the old checksum. */
- if (new_checksum == NULL)
- new_checksum = fb->original_checksum;
-
- SVN_ERR(svn_wc__db_base_add_file(eb->db, fb->local_abspath,
- eb->wcroot_abspath,
- fb->new_relpath,
- eb->repos_root, eb->repos_uuid,
- *eb->target_revision,
- new_base_props,
- fb->changed_rev,
- fb->changed_date,
- fb->changed_author,
- new_checksum,
- (dav_prop_changes->nelts > 0)
- ? svn_prop_array_to_hash(
- dav_prop_changes,
- scratch_pool)
- : NULL,
- NULL /* conflict */,
- (! fb->shadowed) && new_base_props,
- new_actual_props,
- keep_recorded_info,
- (fb->shadowed && fb->obstruction_found),
- all_work_items,
- scratch_pool));
- }
- /* Deal with the WORKING tree, based on updates to the BASE tree. */
+ /* Set the 'checksum' column of the file's BASE_NODE row to
+ * NEW_TEXT_BASE_SHA1_CHECKSUM. The pristine text identified by that
+ * checksum is already in the pristine store. */
+ new_checksum = fb->new_text_base_sha1_checksum;
+
+ /* If we don't have a NEW checksum, then the base must not have changed.
+ Just carry over the old checksum. */
+ if (new_checksum == NULL)
+ new_checksum = fb->original_checksum;
- /* If this file was locally-added and is now being added by the update, we
- can toss the local-add, turning this into a local-edit.
- If the local file is replaced, we don't want to touch ACTUAL. */
- if (fb->add_existed && fb->adding_file)
+ if (conflict_skel)
{
- SVN_ERR(svn_wc__db_temp_op_remove_working(eb->db, fb->local_abspath,
- scratch_pool));
+ SVN_ERR(complete_conflict(conflict_skel,
+ fb->edit_baton,
+ fb->local_abspath,
+ fb->old_repos_relpath,
+ fb->old_revision,
+ fb->new_relpath,
+ svn_node_file, svn_node_file,
+ fb->pool, scratch_pool));
+
+ SVN_ERR(svn_wc__conflict_create_markers(&work_item,
+ eb->db, fb->local_abspath,
+ conflict_skel,
+ scratch_pool, scratch_pool));
+
+ all_work_items = svn_wc__wq_merge(all_work_items, work_item,
+ scratch_pool);
+ }
+
+ /* Any inherited props to be set set for this base node? */
+ if (eb->wcroot_iprops)
+ {
+ iprops = svn_hash_gets(eb->wcroot_iprops, fb->local_abspath);
+
+ /* close_edit may also update iprops for switched nodes, catching
+ those for which close_directory is never called (e.g. a switch
+ with no changes). So as a minor optimization we remove any
+ iprops from the hash so as not to set them again in
+ close_edit. */
+ if (iprops)
+ svn_hash_sets(eb->wcroot_iprops, fb->local_abspath, NULL);
}
- apr_hash_set(fb->dir_baton->not_present_files, fb->name,
- APR_HASH_KEY_STRING, NULL);
+ SVN_ERR(svn_wc__db_base_add_file(eb->db, fb->local_abspath,
+ eb->wcroot_abspath,
+ fb->new_relpath,
+ eb->repos_root, eb->repos_uuid,
+ *eb->target_revision,
+ new_base_props,
+ fb->changed_rev,
+ fb->changed_date,
+ fb->changed_author,
+ new_checksum,
+ (dav_prop_changes->nelts > 0)
+ ? svn_prop_array_to_hash(
+ dav_prop_changes,
+ scratch_pool)
+ : NULL,
+ (fb->add_existed && fb->adding_file),
+ (! fb->shadowed) && new_base_props,
+ new_actual_props,
+ iprops,
+ keep_recorded_info,
+ (fb->shadowed && fb->obstruction_found),
+ conflict_skel,
+ all_work_items,
+ scratch_pool));
+
+ if (conflict_skel && eb->conflict_func)
+ SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, fb->local_abspath,
+ conflict_skel,
+ NULL /* merge_options */,
+ eb->conflict_func,
+ eb->conflict_baton,
+ eb->cancel_func,
+ eb->cancel_baton,
+ scratch_pool));
+
+ /* Deal with the WORKING tree, based on updates to the BASE tree. */
+
+ svn_hash_sets(fb->dir_baton->not_present_files, fb->name, NULL);
/* Send a notification to the callback function. (Skip notifications
about files which were already notified for another reason.) */
@@ -4339,7 +4640,7 @@ close_file(void *file_baton,
if (fb->edited)
{
- if (fb->shadowed)
+ if (fb->shadowed || fb->edit_obstructed)
action = fb->adding_file
? svn_wc_notify_update_shadowed_add
: svn_wc_notify_update_shadowed_update;
@@ -4353,7 +4654,15 @@ close_file(void *file_baton,
action = svn_wc_notify_update_add;
}
}
+ else
+ {
+ SVN_ERR_ASSERT(lock_state == svn_wc_notify_lock_state_unlocked);
+ action = svn_wc_notify_update_broken_lock;
+ }
+ /* If the file was moved-away, notify for the moved-away node.
+ * The original location only had its BASE info changed and
+ * we don't usually notify about such changes. */
notify = svn_wc_create_notify(fb->local_abspath, action, scratch_pool);
notify->kind = svn_node_file;
notify->content_state = content_state;
@@ -4369,11 +4678,11 @@ close_file(void *file_baton,
eb->notify_func(eb->notify_baton, notify, scratch_pool);
}
- /* We have one less referrer to the directory's bump information. */
- SVN_ERR(maybe_release_dir_info(fb->bump_info));
-
svn_pool_destroy(fb->pool); /* Destroy scratch_pool */
+ /* We have one less referrer to the directory */
+ SVN_ERR(maybe_release_dir_info(pdb));
+
return SVN_NO_ERROR;
}
@@ -4424,6 +4733,9 @@ close_edit(void *edit_baton,
eb->repos_uuid,
*(eb->target_revision),
eb->skipped_trees,
+ eb->wcroot_iprops,
+ eb->notify_func,
+ eb->notify_baton,
eb->pool));
if (*eb->target_basename != '\0')
@@ -4442,7 +4754,7 @@ close_edit(void *edit_baton,
have to worry about removing it. */
err = svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
eb->db, eb->target_abspath,
scratch_pool, scratch_pool);
if (err)
@@ -4461,7 +4773,11 @@ close_edit(void *edit_baton,
If so, we should get rid of this excluded node now. */
SVN_ERR(svn_wc__db_base_remove(eb->db, eb->target_abspath,
- scratch_pool));
+ FALSE /* keep_as_working */,
+ FALSE /* queue_deletes */,
+ FALSE /* remove_locks */,
+ SVN_INVALID_REVNUM,
+ NULL, NULL, scratch_pool));
}
}
}
@@ -4487,7 +4803,6 @@ close_edit(void *edit_baton,
return SVN_NO_ERROR;
}
-
/*** Returning editors. ***/
@@ -4497,6 +4812,7 @@ make_editor(svn_revnum_t *target_revision,
svn_wc__db_t *db,
const char *anchor_abspath,
const char *target_basename,
+ apr_hash_t *wcroot_iprops,
svn_boolean_t use_commit_times,
const char *switch_url,
svn_depth_t depth,
@@ -4528,6 +4844,9 @@ make_editor(svn_revnum_t *target_revision,
svn_delta_editor_t *tree_editor = svn_delta_default_editor(edit_pool);
const svn_delta_editor_t *inner_editor;
const char *repos_root, *repos_uuid;
+ struct svn_wc__shim_fetch_baton_t *sfb;
+ svn_delta_shim_callbacks_t *shim_callbacks =
+ svn_delta_shim_callbacks_default(edit_pool);
/* An unknown depth can't be sticky. */
if (depth == svn_depth_unknown)
@@ -4559,6 +4878,7 @@ make_editor(svn_revnum_t *target_revision,
eb->db = db;
eb->target_basename = target_basename;
eb->anchor_abspath = anchor_abspath;
+ eb->wcroot_iprops = wcroot_iprops;
SVN_ERR(svn_wc__db_get_wcroot(&eb->wcroot_abspath, db, anchor_abspath,
edit_pool, scratch_pool));
@@ -4594,7 +4914,7 @@ make_editor(svn_revnum_t *target_revision,
eb->ext_patterns = preserved_exts;
apr_pool_cleanup_register(edit_pool, eb, cleanup_edit_baton,
- cleanup_edit_baton_child);
+ apr_pool_cleanup_null);
/* Construct an editor. */
tree_editor->set_target_revision = set_target_revision;
@@ -4626,7 +4946,7 @@ make_editor(svn_revnum_t *target_revision,
depth. In this case the update won't describe additions that would
have been reported if we updated at the ambient depth. */
svn_error_t *err;
- svn_wc__db_kind_t dir_kind;
+ svn_node_kind_t dir_kind;
svn_wc__db_status_t dir_status;
const char *dir_repos_relpath;
svn_depth_t dir_depth;
@@ -4635,12 +4955,12 @@ make_editor(svn_revnum_t *target_revision,
err = svn_wc__db_base_get_info(&dir_status, &dir_kind, NULL,
&dir_repos_relpath, NULL, NULL, NULL,
NULL, NULL, &dir_depth, NULL, NULL, NULL,
- NULL, NULL,
+ NULL, NULL, NULL,
db, eb->target_abspath,
scratch_pool, scratch_pool);
if (!err
- && dir_kind == svn_wc__db_kind_dir
+ && dir_kind == svn_node_dir
&& dir_status == svn_wc__db_status_normal)
{
if (dir_depth > depth)
@@ -4656,10 +4976,9 @@ make_editor(svn_revnum_t *target_revision,
edit_pool, scratch_pool));
if (dirents != NULL && apr_hash_count(dirents))
- apr_hash_set(eb->dir_dirents,
- apr_pstrdup(edit_pool, dir_repos_relpath),
- APR_HASH_KEY_STRING,
- dirents);
+ svn_hash_sets(eb->dir_dirents,
+ apr_pstrdup(edit_pool, dir_repos_relpath),
+ dirents);
}
if (depth == svn_depth_immediates)
@@ -4691,12 +5010,12 @@ make_editor(svn_revnum_t *target_revision,
NULL, &dir_repos_relpath,
NULL, NULL, NULL, NULL,
NULL, &dir_depth, NULL,
- NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
NULL,
db, child_abspath,
iterpool, iterpool));
- if (dir_kind == svn_wc__db_kind_dir
+ if (dir_kind == svn_node_dir
&& dir_status == svn_wc__db_status_normal
&& dir_depth > svn_depth_empty)
{
@@ -4713,10 +5032,10 @@ make_editor(svn_revnum_t *target_revision,
edit_pool, iterpool));
if (dirents != NULL && apr_hash_count(dirents))
- apr_hash_set(eb->dir_dirents,
- apr_pstrdup(edit_pool, dir_repos_relpath),
- APR_HASH_KEY_STRING,
- dirents);
+ svn_hash_sets(eb->dir_dirents,
+ apr_pstrdup(edit_pool,
+ dir_repos_relpath),
+ dirents);
}
}
}
@@ -4747,23 +5066,40 @@ make_editor(svn_revnum_t *target_revision,
inner_baton,
result_pool));
- return svn_delta_get_cancellation_editor(cancel_func,
- cancel_baton,
- inner_editor,
- inner_baton,
- editor,
- edit_baton,
- result_pool);
+ SVN_ERR(svn_delta_get_cancellation_editor(cancel_func,
+ cancel_baton,
+ inner_editor,
+ inner_baton,
+ editor,
+ edit_baton,
+ result_pool));
+
+ sfb = apr_palloc(result_pool, sizeof(*sfb));
+ sfb->db = db;
+ sfb->base_abspath = eb->anchor_abspath;
+ sfb->fetch_base = TRUE;
+
+ shim_callbacks->fetch_kind_func = svn_wc__fetch_kind_func;
+ shim_callbacks->fetch_props_func = svn_wc__fetch_props_func;
+ shim_callbacks->fetch_base_func = svn_wc__fetch_base_func;
+ shim_callbacks->fetch_baton = sfb;
+
+ SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton,
+ NULL, NULL, shim_callbacks,
+ result_pool, scratch_pool));
+
+ return SVN_NO_ERROR;
}
svn_error_t *
-svn_wc_get_update_editor4(const svn_delta_editor_t **editor,
+svn_wc__get_update_editor(const svn_delta_editor_t **editor,
void **edit_baton,
svn_revnum_t *target_revision,
svn_wc_context_t *wc_ctx,
const char *anchor_abspath,
const char *target_basename,
+ apr_hash_t *wcroot_iprops,
svn_boolean_t use_commit_times,
svn_depth_t depth,
svn_boolean_t depth_is_sticky,
@@ -4787,7 +5123,7 @@ svn_wc_get_update_editor4(const svn_delta_editor_t **editor,
apr_pool_t *scratch_pool)
{
return make_editor(target_revision, wc_ctx->db, anchor_abspath,
- target_basename, use_commit_times,
+ target_basename, wcroot_iprops, use_commit_times,
NULL, depth, depth_is_sticky, allow_unver_obstructions,
adds_as_modification, server_performs_filtering,
clean_checkout,
@@ -4801,13 +5137,14 @@ svn_wc_get_update_editor4(const svn_delta_editor_t **editor,
}
svn_error_t *
-svn_wc_get_switch_editor4(const svn_delta_editor_t **editor,
+svn_wc__get_switch_editor(const svn_delta_editor_t **editor,
void **edit_baton,
svn_revnum_t *target_revision,
svn_wc_context_t *wc_ctx,
const char *anchor_abspath,
const char *target_basename,
const char *switch_url,
+ apr_hash_t *wcroot_iprops,
svn_boolean_t use_commit_times,
svn_depth_t depth,
svn_boolean_t depth_is_sticky,
@@ -4831,7 +5168,7 @@ svn_wc_get_switch_editor4(const svn_delta_editor_t **editor,
SVN_ERR_ASSERT(switch_url && svn_uri_is_canonical(switch_url, scratch_pool));
return make_editor(target_revision, wc_ctx->db, anchor_abspath,
- target_basename, use_commit_times,
+ target_basename, wcroot_iprops, use_commit_times,
switch_url,
depth, depth_is_sticky, allow_unver_obstructions,
FALSE /* adds_as_modification */,
@@ -4847,319 +5184,6 @@ svn_wc_get_switch_editor4(const svn_delta_editor_t **editor,
result_pool, scratch_pool);
}
-/* ABOUT ANCHOR AND TARGET, AND svn_wc_get_actual_target2()
-
- THE GOAL
-
- Note the following actions, where X is the thing we wish to update,
- P is a directory whose repository URL is the parent of
- X's repository URL, N is directory whose repository URL is *not*
- the parent directory of X (including the case where N is not a
- versioned resource at all):
-
- 1. `svn up .' from inside X.
- 2. `svn up ...P/X' from anywhere.
- 3. `svn up ...N/X' from anywhere.
-
- For the purposes of the discussion, in the '...N/X' situation, X is
- said to be a "working copy (WC) root" directory.
-
- Now consider the four cases for X's type (file/dir) in the working
- copy vs. the repository:
-
- A. dir in working copy, dir in repos.
- B. dir in working copy, file in repos.
- C. file in working copy, dir in repos.
- D. file in working copy, file in repos.
-
- Here are the results we expect for each combination of the above:
-
- 1A. Successfully update X.
- 1B. Error (you don't want to remove your current working
- directory out from underneath the application).
- 1C. N/A (you can't be "inside X" if X is a file).
- 1D. N/A (you can't be "inside X" if X is a file).
-
- 2A. Successfully update X.
- 2B. Successfully update X.
- 2C. Successfully update X.
- 2D. Successfully update X.
-
- 3A. Successfully update X.
- 3B. Error (you can't create a versioned file X inside a
- non-versioned directory).
- 3C. N/A (you can't have a versioned file X in directory that is
- not its repository parent).
- 3D. N/A (you can't have a versioned file X in directory that is
- not its repository parent).
-
- To summarize, case 2 always succeeds, and cases 1 and 3 always fail
- (or can't occur) *except* when the target is a dir that remains a
- dir after the update.
-
- ACCOMPLISHING THE GOAL
-
- Updates are accomplished by driving an editor, and an editor is
- "rooted" on a directory. So, in order to update a file, we need to
- break off the basename of the file, rooting the editor in that
- file's parent directory, and then updating only that file, not the
- other stuff in its parent directory.
-
- Secondly, we look at the case where we wish to update a directory.
- This is typically trivial. However, one problematic case, exists
- when we wish to update a directory that has been removed from the
- repository and replaced with a file of the same name. If we root
- our edit at the initial directory, there is no editor mechanism for
- deleting that directory and replacing it with a file (this would be
- like having an editor now anchored on a file, which is disallowed).
-
- All that remains is to have a function with the knowledge required
- to properly decide where to root our editor, and what to act upon
- with that now-rooted editor. Given a path to be updated, this
- function should conditionally split that path into an "anchor" and
- a "target", where the "anchor" is the directory at which the update
- editor is rooted (meaning, editor->open_root() is called with
- this directory in mind), and the "target" is the actual intended
- subject of the update.
-
- svn_wc_get_actual_target2() is that function.
-
- So, what are the conditions?
-
- Case I: Any time X is '.' (implying it is a directory), we won't
- lop off a basename. So we'll root our editor at X, and update all
- of X.
-
- Cases II & III: Any time we are trying to update some path ...N/X,
- we again will not lop off a basename. We can't root an editor at
- ...N with X as a target, either because ...N isn't a versioned
- resource at all (Case II) or because X is X is not a child of ...N
- in the repository (Case III). We root at X, and update X.
-
- Cases IV-???: We lop off a basename when we are updating a
- path ...P/X, rooting our editor at ...P and updating X, or when X
- is missing from disk.
-
- These conditions apply whether X is a file or directory.
-
- ---
-
- As it turns out, commits need to have a similar check in place,
- too, specifically for the case where a single directory is being
- committed (we have to anchor at that directory's parent in case the
- directory itself needs to be modified).
-*/
-
-
-svn_error_t *
-svn_wc__check_wc_root(svn_boolean_t *wc_root,
- svn_wc__db_kind_t *kind,
- svn_boolean_t *switched,
- svn_wc__db_t *db,
- const char *local_abspath,
- apr_pool_t *scratch_pool)
-{
- const char *parent_abspath, *name;
- const char *repos_relpath, *repos_root, *repos_uuid;
- svn_wc__db_status_t status;
- svn_wc__db_kind_t my_kind;
-
- if (!kind)
- kind = &my_kind;
-
- /* Initialize our return values to the most common (code-wise) values. */
- *wc_root = TRUE;
- if (switched)
- *switched = FALSE;
-
- SVN_ERR(svn_wc__db_read_info(&status, kind, NULL, &repos_relpath,
- &repos_root, &repos_uuid, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL,
- db, local_abspath,
- scratch_pool, scratch_pool));
-
- if (repos_relpath == NULL)
- {
- /* If we inherit our URL, then we can't be a root, nor switched. */
- *wc_root = FALSE;
- return SVN_NO_ERROR;
- }
- if (*kind != svn_wc__db_kind_dir)
- {
- /* File/symlinks cannot be a root. */
- *wc_root = FALSE;
- }
- else if (status == svn_wc__db_status_added
- || status == svn_wc__db_status_deleted)
- {
- *wc_root = FALSE;
- }
- else if (status == svn_wc__db_status_server_excluded
- || status == svn_wc__db_status_excluded
- || status == svn_wc__db_status_not_present)
- {
- return svn_error_createf(
- SVN_ERR_WC_PATH_NOT_FOUND, NULL,
- _("The node '%s' was not found."),
- svn_dirent_local_style(local_abspath, scratch_pool));
- }
- else if (svn_dirent_is_root(local_abspath, strlen(local_abspath)))
- return SVN_NO_ERROR;
-
- if (!*wc_root && switched == NULL )
- return SVN_NO_ERROR; /* No more info needed */
-
- svn_dirent_split(&parent_abspath, &name, local_abspath, scratch_pool);
-
- /* Check if the node is recorded in the parent */
- if (*wc_root)
- {
- svn_boolean_t is_root;
- SVN_ERR(svn_wc__db_is_wcroot(&is_root, db, local_abspath, scratch_pool));
-
- if (is_root)
- {
- /* We're not in the (versioned) parent directory's list of
- children, so we must be the root of a distinct working copy. */
- return SVN_NO_ERROR;
- }
- }
-
- {
- const char *parent_repos_root;
- const char *parent_repos_relpath;
- const char *parent_repos_uuid;
-
- SVN_ERR(svn_wc__db_scan_base_repos(&parent_repos_relpath,
- &parent_repos_root,
- &parent_repos_uuid,
- db, parent_abspath,
- scratch_pool, scratch_pool));
-
- if (strcmp(repos_root, parent_repos_root) != 0
- || strcmp(repos_uuid, parent_repos_uuid) != 0)
- {
- /* This should never happen (### until we get mixed-repos working
- copies). If we're in the parent, then we should be from the
- same repository. For this situation, just declare us the root
- of a separate, unswitched working copy. */
- return SVN_NO_ERROR;
- }
-
- *wc_root = FALSE;
-
- if (switched)
- {
- const char *expected_relpath = svn_relpath_join(parent_repos_relpath,
- name, scratch_pool);
-
- *switched = (strcmp(expected_relpath, repos_relpath) != 0);
- }
- }
-
- return SVN_NO_ERROR;
-}
-
-svn_error_t *
-svn_wc_is_wc_root2(svn_boolean_t *wc_root,
- svn_wc_context_t *wc_ctx,
- const char *local_abspath,
- apr_pool_t *scratch_pool)
-{
- svn_boolean_t is_root;
- svn_boolean_t is_switched;
- svn_wc__db_kind_t kind;
- svn_error_t *err;
- SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
-
- err = svn_wc__check_wc_root(&is_root, &kind, &is_switched,
- wc_ctx->db, local_abspath, scratch_pool);
-
- if (err)
- {
- if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND &&
- err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY)
- return svn_error_trace(err);
-
- return svn_error_create(SVN_ERR_ENTRY_NOT_FOUND, err, err->message);
- }
-
- *wc_root = is_root || (kind == svn_wc__db_kind_dir && is_switched);
-
- return SVN_NO_ERROR;
-}
-
-
-svn_error_t*
-svn_wc__strictly_is_wc_root(svn_boolean_t *wc_root,
- svn_wc_context_t *wc_ctx,
- const char *local_abspath,
- apr_pool_t *scratch_pool)
-{
- return svn_error_trace(svn_wc__db_is_wcroot(wc_root,
- wc_ctx->db,
- local_abspath,
- scratch_pool));
-}
-
-
-svn_error_t *
-svn_wc__get_wc_root(const char **wcroot_abspath,
- svn_wc_context_t *wc_ctx,
- const char *local_abspath,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
-{
- return svn_wc__db_get_wcroot(wcroot_abspath, wc_ctx->db,
- local_abspath, result_pool, scratch_pool);
-}
-
-
-svn_error_t *
-svn_wc_get_actual_target2(const char **anchor,
- const char **target,
- svn_wc_context_t *wc_ctx,
- const char *path,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
-{
- svn_boolean_t is_wc_root, is_switched;
- svn_wc__db_kind_t kind;
- const char *local_abspath;
- svn_error_t *err;
-
- SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool));
-
- err = svn_wc__check_wc_root(&is_wc_root, &kind, &is_switched,
- wc_ctx->db, local_abspath,
- scratch_pool);
-
- if (err)
- {
- if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND &&
- err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY)
- return svn_error_trace(err);
-
- svn_error_clear(err);
- is_wc_root = FALSE;
- is_switched = FALSE;
- }
-
- /* If PATH is not a WC root, or if it is a file, lop off a basename. */
- if (!(is_wc_root || is_switched) || (kind != svn_wc__db_kind_dir))
- {
- svn_dirent_split(anchor, target, path, result_pool);
- }
- else
- {
- *anchor = apr_pstrdup(result_pool, path);
- *target = "";
- }
-
- return SVN_NO_ERROR;
-}
/* ### Note that this function is completely different from the rest of the
@@ -5186,16 +5210,16 @@ svn_wc_add_repos_file4(svn_wc_context_t *wc_ctx,
svn_wc__db_t *db = wc_ctx->db;
const char *dir_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
svn_wc__db_status_t status;
- svn_wc__db_kind_t kind;
+ svn_node_kind_t kind;
const char *tmp_text_base_abspath;
svn_checksum_t *new_text_base_md5_checksum;
svn_checksum_t *new_text_base_sha1_checksum;
const char *source_abspath = NULL;
svn_skel_t *all_work_items = NULL;
svn_skel_t *work_item;
- const char *original_root_url;
+ const char *repos_root_url;
+ const char *repos_uuid;
const char *original_repos_relpath;
- const char *original_uuid;
svn_revnum_t changed_rev;
apr_time_t changed_date;
const char *changed_author;
@@ -5232,10 +5256,10 @@ svn_wc_add_repos_file4(svn_wc_context_t *wc_ctx,
scratch_pool));
}
- SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, &repos_root_url,
+ &repos_uuid, 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,
db, dir_abspath, scratch_pool, scratch_pool));
switch (status)
@@ -5257,7 +5281,7 @@ svn_wc_add_repos_file4(svn_wc_context_t *wc_ctx,
svn_dirent_local_style(local_abspath,
scratch_pool));
}
- if (kind != svn_wc__db_kind_dir)
+ if (kind != svn_node_dir)
return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
_("Can't schedule an addition of '%s'"
" below a not-directory node"),
@@ -5270,26 +5294,30 @@ svn_wc_add_repos_file4(svn_wc_context_t *wc_ctx,
{
/* Find the repository_root via the parent directory, which
is always versioned before this function is called */
- SVN_ERR(svn_wc__internal_get_repos_info(&original_root_url,
- &original_uuid,
- wc_ctx->db,
- dir_abspath,
- pool, pool));
- if (!svn_uri__is_ancestor(original_root_url, copyfrom_url))
+ if (!repos_root_url)
+ {
+ /* The parent is an addition, scan upwards to find the right info */
+ SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, NULL,
+ &repos_root_url, &repos_uuid,
+ NULL, NULL, NULL, NULL,
+ wc_ctx->db, dir_abspath,
+ scratch_pool, scratch_pool));
+ }
+ SVN_ERR_ASSERT(repos_root_url);
+
+ original_repos_relpath =
+ svn_uri_skip_ancestor(repos_root_url, copyfrom_url, scratch_pool);
+
+ if (!original_repos_relpath)
return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("Copyfrom-url '%s' has different repository"
" root than '%s'"),
- copyfrom_url, original_root_url);
-
- original_repos_relpath =
- svn_uri_skip_ancestor(original_root_url, copyfrom_url, pool);
+ copyfrom_url, repos_root_url);
}
else
{
- original_root_url = NULL;
original_repos_relpath = NULL;
- original_uuid = NULL;
copyfrom_rev = SVN_INVALID_REVNUM; /* Just to be sure. */
}
@@ -5390,7 +5418,7 @@ svn_wc_add_repos_file4(svn_wc_context_t *wc_ctx,
/* If new contents were provided, then we do NOT want to record the
file information. We assume the new contents do not match the
- "proper" values for TRANSLATED_SIZE and LAST_MOD_TIME. */
+ "proper" values for RECORDED_SIZE and RECORDED_TIME. */
record_fileinfo = (new_contents == NULL);
/* Install the working copy file (with appropriate translation) from
@@ -5410,8 +5438,8 @@ svn_wc_add_repos_file4(svn_wc_context_t *wc_ctx,
it is a temporary file, which needs to be removed. */
if (source_abspath != NULL)
{
- SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
- db, source_abspath,
+ SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, local_abspath,
+ source_abspath,
pool, pool));
all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool);
}
@@ -5426,18 +5454,14 @@ svn_wc_add_repos_file4(svn_wc_context_t *wc_ctx,
changed_date,
changed_author,
original_repos_relpath,
- original_root_url,
- original_uuid,
+ original_repos_relpath ? repos_root_url
+ : NULL,
+ original_repos_relpath ? repos_uuid : NULL,
copyfrom_rev,
new_text_base_sha1_checksum,
- NULL /* conflict */,
- NULL /* work_items */,
- pool));
-
- /* ### if below fails, then the above db change would remain :-( */
-
- SVN_ERR(svn_wc__db_op_set_props(db, local_abspath,
- new_props, FALSE,
+ TRUE,
+ new_props,
+ FALSE /* is_move */,
NULL /* conflict */,
all_work_items,
pool));
@@ -5446,3 +5470,90 @@ svn_wc_add_repos_file4(svn_wc_context_t *wc_ctx,
cancel_func, cancel_baton,
pool));
}
+
+svn_error_t *
+svn_wc__complete_directory_add(svn_wc_context_t *wc_ctx,
+ const char *local_abspath,
+ apr_hash_t *new_original_props,
+ const char *copyfrom_url,
+ svn_revnum_t copyfrom_rev,
+ apr_pool_t *scratch_pool)
+{
+ svn_wc__db_status_t status;
+ svn_node_kind_t kind;
+ const char *original_repos_relpath;
+ const char *original_root_url;
+ const char *original_uuid;
+ svn_boolean_t had_props;
+ svn_boolean_t props_mod;
+
+ svn_revnum_t original_revision;
+ svn_revnum_t changed_rev;
+ apr_time_t changed_date;
+ const char *changed_author;
+
+ SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ &original_repos_relpath, &original_root_url,
+ &original_uuid, &original_revision, NULL, NULL,
+ NULL, NULL, NULL, NULL, &had_props, &props_mod,
+ NULL, NULL, NULL,
+ wc_ctx->db, local_abspath,
+ scratch_pool, scratch_pool));
+
+ if (status != svn_wc__db_status_added
+ || kind != svn_node_dir
+ || had_props
+ || props_mod
+ || !original_repos_relpath)
+ {
+ return svn_error_createf(
+ SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
+ _("'%s' is not an unmodified copied directory"),
+ svn_dirent_local_style(local_abspath, scratch_pool));
+ }
+ if (original_revision != copyfrom_rev
+ || strcmp(copyfrom_url,
+ svn_path_url_add_component2(original_root_url,
+ original_repos_relpath,
+ scratch_pool)))
+ {
+ return svn_error_createf(
+ SVN_ERR_WC_COPYFROM_PATH_NOT_FOUND, NULL,
+ _("Copyfrom '%s' doesn't match original location of '%s'"),
+ copyfrom_url,
+ svn_dirent_local_style(local_abspath, scratch_pool));
+ }
+
+ {
+ apr_array_header_t *regular_props;
+ apr_array_header_t *entry_props;
+
+ SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(new_original_props,
+ scratch_pool),
+ &entry_props, NULL, &regular_props,
+ scratch_pool));
+
+ /* Put regular props back into a hash table. */
+ new_original_props = svn_prop_array_to_hash(regular_props, scratch_pool);
+
+ /* Get the change_* info from the entry props. */
+ SVN_ERR(accumulate_last_change(&changed_rev,
+ &changed_date,
+ &changed_author,
+ entry_props, scratch_pool, scratch_pool));
+ }
+
+ return svn_error_trace(
+ svn_wc__db_op_copy_dir(wc_ctx->db, local_abspath,
+ new_original_props,
+ changed_rev, changed_date, changed_author,
+ original_repos_relpath, original_root_url,
+ original_uuid, original_revision,
+ NULL /* children */,
+ svn_depth_infinity,
+ FALSE /* is_move */,
+ NULL /* conflict */,
+ NULL /* work_items */,
+ scratch_pool));
+}