summaryrefslogtreecommitdiff
path: root/subversion/libsvn_wc/adm_ops.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_wc/adm_ops.c')
-rw-r--r--subversion/libsvn_wc/adm_ops.c1517
1 files changed, 206 insertions, 1311 deletions
diff --git a/subversion/libsvn_wc/adm_ops.c b/subversion/libsvn_wc/adm_ops.c
index ff72d43..a0f8061 100644
--- a/subversion/libsvn_wc/adm_ops.c
+++ b/subversion/libsvn_wc/adm_ops.c
@@ -32,9 +32,7 @@
#include <stdlib.h>
#include <apr_pools.h>
-#include <apr_tables.h>
#include <apr_hash.h>
-#include <apr_file_io.h>
#include <apr_time.h>
#include <apr_errno.h>
@@ -52,14 +50,12 @@
#include "wc.h"
#include "adm_files.h"
-#include "props.h"
-#include "translate.h"
+#include "conflicts.h"
#include "workqueue.h"
#include "svn_private_config.h"
-#include "private/svn_io_private.h"
-#include "private/svn_wc_private.h"
-
+#include "private/svn_subr_private.h"
+#include "private/svn_dep_compat.h"
struct svn_wc_committed_queue_t
@@ -108,11 +104,19 @@ svn_wc__get_committed_queue_pool(const struct svn_wc_committed_queue_t *queue)
* - queue deletion of the old pristine texts by the remembered checksums.
*
* CHECKSUM is the checksum of the new text base for LOCAL_ABSPATH, and must
- * be provided if there is one, else NULL. */
+ * be provided if there is one, else NULL.
+ *
+ * STATUS, KIND, PROP_MODS and OLD_CHECKSUM are the current in-db values of
+ * the node LOCAL_ABSPATH.
+ */
static svn_error_t *
process_committed_leaf(svn_wc__db_t *db,
const char *local_abspath,
svn_boolean_t via_recurse,
+ svn_wc__db_status_t status,
+ svn_node_kind_t kind,
+ svn_boolean_t prop_mods,
+ const svn_checksum_t *old_checksum,
svn_revnum_t new_revnum,
apr_time_t new_changed_date,
const char *new_changed_author,
@@ -122,30 +126,15 @@ process_committed_leaf(svn_wc__db_t *db,
const svn_checksum_t *checksum,
apr_pool_t *scratch_pool)
{
- svn_wc__db_status_t status;
- svn_wc__db_kind_t kind;
- const svn_checksum_t *copied_checksum;
svn_revnum_t new_changed_rev = new_revnum;
- svn_boolean_t have_base;
- svn_boolean_t have_work;
- svn_boolean_t had_props;
- svn_boolean_t prop_mods;
svn_skel_t *work_item = NULL;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
- SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, &copied_checksum,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, &had_props, &prop_mods,
- &have_base, NULL, &have_work,
- db, local_abspath,
- scratch_pool, scratch_pool));
-
{
const char *adm_abspath;
- if (kind == svn_wc__db_kind_dir)
+ if (kind == svn_node_dir)
adm_abspath = local_abspath;
else
adm_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
@@ -155,11 +144,14 @@ process_committed_leaf(svn_wc__db_t *db,
if (status == svn_wc__db_status_deleted)
{
return svn_error_trace(
- svn_wc__db_op_remove_node(
+ svn_wc__db_base_remove(
db, local_abspath,
- (have_base && !via_recurse)
+ FALSE /* keep_as_working */,
+ FALSE /* queue_deletes */,
+ TRUE /* remove_locks */,
+ (! via_recurse)
? new_revnum : SVN_INVALID_REVNUM,
- kind,
+ NULL, NULL,
scratch_pool));
}
else if (status == svn_wc__db_status_not_present)
@@ -177,7 +169,7 @@ process_committed_leaf(svn_wc__db_t *db,
|| status == svn_wc__db_status_incomplete
|| status == svn_wc__db_status_added);
- if (kind != svn_wc__db_kind_dir)
+ if (kind != svn_node_dir)
{
/* If we sent a delta (meaning: post-copy modification),
then this file will appear in the queue and so we should have
@@ -186,9 +178,9 @@ process_committed_leaf(svn_wc__db_t *db,
{
/* It was copied and not modified. We must have a text
base for it. And the node should have a checksum. */
- SVN_ERR_ASSERT(copied_checksum != NULL);
+ SVN_ERR_ASSERT(old_checksum != NULL);
- checksum = copied_checksum;
+ checksum = old_checksum;
/* Is the node completely unmodified and are we recursing? */
if (via_recurse && !prop_mods)
@@ -250,23 +242,41 @@ svn_wc__process_committed_internal(svn_wc__db_t *db,
const svn_wc_committed_queue_t *queue,
apr_pool_t *scratch_pool)
{
- svn_wc__db_kind_t kind;
+ svn_wc__db_status_t status;
+ svn_node_kind_t kind;
+ const svn_checksum_t *old_checksum;
+ svn_boolean_t prop_mods;
+
+ SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, &old_checksum, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, &prop_mods, NULL, NULL, NULL,
+ db, local_abspath,
+ scratch_pool, scratch_pool));
/* NOTE: be wary of making crazy semantic changes in this function, since
svn_wc_process_committed4() calls this. */
SVN_ERR(process_committed_leaf(db, local_abspath, !top_of_recurse,
+ status, kind, prop_mods, old_checksum,
new_revnum, new_date, rev_author,
new_dav_cache,
no_unlock, keep_changelist,
sha1_checksum,
scratch_pool));
- /* Only check kind after processing the node itself. The node might
- have been deleted */
- SVN_ERR(svn_wc__db_read_kind(&kind, db, local_abspath, TRUE, scratch_pool));
+ /* Only check for recursion on nodes that have children */
+ if (kind != svn_node_file
+ || status == svn_wc__db_status_not_present
+ || status == svn_wc__db_status_excluded
+ || status == svn_wc__db_status_server_excluded
+ /* Node deleted -> then no longer a directory */
+ || status == svn_wc__db_status_deleted)
+ {
+ return SVN_NO_ERROR;
+ }
- if (recurse && kind == svn_wc__db_kind_dir)
+ if (recurse)
{
const apr_array_header_t *children;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
@@ -281,38 +291,17 @@ svn_wc__process_committed_internal(svn_wc__db_t *db,
{
const char *name = APR_ARRAY_IDX(children, i, const char *);
const char *this_abspath;
- svn_wc__db_status_t status;
+ const committed_queue_item_t *cqi;
svn_pool_clear(iterpool);
this_abspath = svn_dirent_join(local_abspath, name, iterpool);
- SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL,
- db, this_abspath,
- iterpool, iterpool));
-
- /* We come to this branch since we have committed a copied tree.
- svn_depth_exclude is possible in this situation. So check and
- skip */
- if (status == svn_wc__db_status_excluded)
- continue;
-
sha1_checksum = NULL;
- if (kind != svn_wc__db_kind_dir && queue != NULL)
- {
- const committed_queue_item_t *cqi;
+ cqi = svn_hash_gets(queue->queue, this_abspath);
- cqi = apr_hash_get(queue->queue, this_abspath,
- APR_HASH_KEY_STRING);
- if (cqi != NULL)
- {
- sha1_checksum = cqi->sha1_checksum;
- }
- }
+ if (cqi != NULL)
+ sha1_checksum = cqi->sha1_checksum;
/* Recurse. Pass NULL for NEW_DAV_CACHE, because the
ones present in the current call are only applicable to
@@ -354,7 +343,7 @@ svn_wc__prop_array_to_hash(const apr_array_header_t *props,
{
const svn_prop_t *prop = APR_ARRAY_IDX(props, i, const svn_prop_t *);
if (prop->value != NULL)
- apr_hash_set(prophash, prop->name, APR_HASH_KEY_STRING, prop->value);
+ svn_hash_sets(prophash, prop->name, prop->value);
}
return prophash;
@@ -405,7 +394,7 @@ svn_wc_queue_committed3(svn_wc_committed_queue_t *queue,
cqi->sha1_checksum = sha1_checksum;
cqi->new_dav_cache = svn_wc__prop_array_to_hash(wcprop_changes, queue->pool);
- apr_hash_set(queue->queue, local_abspath, APR_HASH_KEY_STRING, cqi);
+ svn_hash_sets(queue->queue, local_abspath, cqi);
return SVN_NO_ERROR;
}
@@ -500,16 +489,15 @@ svn_wc_process_committed_queue2(svn_wc_committed_queue_t *queue,
wc_ctx->db, cqi->local_abspath,
iterpool, iterpool));
- if (! apr_hash_get(run_wqs, wcroot_abspath, APR_HASH_KEY_STRING))
+ if (! svn_hash_gets(run_wqs, wcroot_abspath))
{
wcroot_abspath = apr_pstrdup(scratch_pool, wcroot_abspath);
- apr_hash_set(run_wqs, wcroot_abspath, APR_HASH_KEY_STRING,
- wcroot_abspath);
+ svn_hash_sets(run_wqs, wcroot_abspath, wcroot_abspath);
}
}
/* Make sure nothing happens if this function is called again. */
- SVN_ERR(svn_hash__clear(queue->queue, iterpool));
+ apr_hash_clear(queue->queue);
/* Ok; everything is committed now. Now we can start calling callbacks */
@@ -534,218 +522,42 @@ svn_wc_process_committed_queue2(svn_wc_committed_queue_t *queue,
return SVN_NO_ERROR;
}
-
-/* Remove/erase PATH from the working copy. This involves deleting PATH
- * from the physical filesystem. PATH is assumed to be an unversioned file
- * or directory.
+/* Schedule the single node at LOCAL_ABSPATH, of kind KIND, for addition in
+ * its parent directory in the WC. It will have the regular properties
+ * provided in PROPS, or none if that is NULL.
*
- * If ignore_enoent is TRUE, ignore missing targets.
+ * If the node is a file, set its on-disk executable and read-only bits to
+ * match its properties and lock state,
+ * ### only if it has an svn:executable or svn:needs-lock property.
+ * ### This is to match the previous behaviour of setting its props
+ * afterwards by calling svn_wc_prop_set4(), but is not very clean.
*
- * If CANCEL_FUNC is non-null, invoke it with CANCEL_BATON at various
- * points, return any error immediately.
+ * Sync the on-disk executable and read-only bits accordingly.
*/
static svn_error_t *
-erase_unversioned_from_wc(const char *path,
- svn_boolean_t ignore_enoent,
- svn_cancel_func_t cancel_func,
- void *cancel_baton,
- apr_pool_t *scratch_pool)
-{
- svn_error_t *err;
-
- /* Optimize the common case: try to delete the file */
- err = svn_io_remove_file2(path, ignore_enoent, scratch_pool);
- if (err)
- {
- /* Then maybe it was a directory? */
- svn_error_clear(err);
-
- err = svn_io_remove_dir2(path, ignore_enoent, cancel_func, cancel_baton,
- scratch_pool);
-
- if (err)
- {
- /* We're unlikely to end up here. But we need this fallback
- to make sure we report the right error *and* try the
- correct deletion at least once. */
- svn_node_kind_t kind;
-
- svn_error_clear(err);
- SVN_ERR(svn_io_check_path(path, &kind, scratch_pool));
- if (kind == svn_node_file)
- SVN_ERR(svn_io_remove_file2(path, ignore_enoent, scratch_pool));
- else if (kind == svn_node_dir)
- SVN_ERR(svn_io_remove_dir2(path, ignore_enoent,
- cancel_func, cancel_baton,
- scratch_pool));
- else if (kind == svn_node_none)
- return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL,
- _("'%s' does not exist"),
- svn_dirent_local_style(path,
- scratch_pool));
- else
- return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
- _("Unsupported node kind for path '%s'"),
- svn_dirent_local_style(path,
- scratch_pool));
-
- }
- }
-
- return SVN_NO_ERROR;
-}
-
-
-svn_error_t *
-svn_wc_delete4(svn_wc_context_t *wc_ctx,
- const char *local_abspath,
- svn_boolean_t keep_local,
- svn_boolean_t delete_unversioned_target,
- svn_cancel_func_t cancel_func,
- void *cancel_baton,
- svn_wc_notify_func2_t notify_func,
- void *notify_baton,
- apr_pool_t *scratch_pool)
-{
- apr_pool_t *pool = scratch_pool;
- svn_wc__db_t *db = wc_ctx->db;
- svn_error_t *err;
- svn_wc__db_status_t status;
- svn_wc__db_kind_t kind;
- svn_boolean_t conflicted;
- const apr_array_header_t *conflicts;
-
- err = svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, &conflicted,
- NULL, NULL, NULL, NULL, NULL, NULL,
- db, local_abspath, pool, pool);
-
- if (delete_unversioned_target &&
- err != NULL && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
- {
- svn_error_clear(err);
-
- if (!keep_local)
- SVN_ERR(erase_unversioned_from_wc(local_abspath, FALSE,
- cancel_func, cancel_baton,
- pool));
- return SVN_NO_ERROR;
- }
- else
- SVN_ERR(err);
-
- switch (status)
- {
- /* svn_wc__db_status_server_excluded handled by svn_wc__db_op_delete */
- case svn_wc__db_status_excluded:
- case svn_wc__db_status_not_present:
- return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
- _("'%s' cannot be deleted"),
- svn_dirent_local_style(local_abspath, pool));
-
- /* Explicitly ignore other statii */
- default:
- break;
- }
-
- if (status == svn_wc__db_status_normal
- && kind == svn_wc__db_kind_dir)
- {
- svn_boolean_t is_wcroot;
- SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, db, local_abspath, pool));
-
- if (is_wcroot)
- return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
- _("'%s' is the root of a working copy and "
- "cannot be deleted"),
- svn_dirent_local_style(local_abspath, pool));
- }
-
- /* Verify if we have a write lock on the parent of this node as we might
- be changing the childlist of that directory. */
- SVN_ERR(svn_wc__write_check(db, svn_dirent_dirname(local_abspath, pool),
- pool));
-
- /* Read conflicts, to allow deleting the markers after updating the DB */
- if (!keep_local && conflicted)
- SVN_ERR(svn_wc__db_read_conflicts(&conflicts, db, local_abspath,
- scratch_pool, scratch_pool));
-
- SVN_ERR(svn_wc__db_op_delete(db, local_abspath,
- notify_func, notify_baton,
- cancel_func, cancel_baton,
- pool));
-
- if (!keep_local && conflicted && conflicts != NULL)
- {
- int i;
-
- /* Do we have conflict markers that should be removed? */
- for (i = 0; i < conflicts->nelts; i++)
- {
- const svn_wc_conflict_description2_t *desc;
-
- desc = APR_ARRAY_IDX(conflicts, i,
- const svn_wc_conflict_description2_t*);
-
- if (desc->kind == svn_wc_conflict_kind_text)
- {
- if (desc->base_abspath != NULL)
- {
- SVN_ERR(svn_io_remove_file2(desc->base_abspath, TRUE,
- scratch_pool));
- }
- if (desc->their_abspath != NULL)
- {
- SVN_ERR(svn_io_remove_file2(desc->their_abspath, TRUE,
- scratch_pool));
- }
- if (desc->my_abspath != NULL)
- {
- SVN_ERR(svn_io_remove_file2(desc->my_abspath, TRUE,
- scratch_pool));
- }
- }
- else if (desc->kind == svn_wc_conflict_kind_property
- && desc->their_abspath != NULL)
- {
- SVN_ERR(svn_io_remove_file2(desc->their_abspath, TRUE,
- scratch_pool));
- }
- }
- }
-
- /* By the time we get here, the db knows that everything that is still at
- LOCAL_ABSPATH is unversioned. */
- if (!keep_local)
- {
- SVN_ERR(erase_unversioned_from_wc(local_abspath, TRUE,
- cancel_func, cancel_baton,
- pool));
- }
-
- return SVN_NO_ERROR;
-}
-
-
-/* Schedule the single node at LOCAL_ABSPATH, of kind KIND, for addition in
- * its parent directory in the WC. It will have no properties. */
-static svn_error_t *
add_from_disk(svn_wc__db_t *db,
const char *local_abspath,
svn_node_kind_t kind,
- svn_wc_notify_func2_t notify_func,
- void *notify_baton,
+ const apr_hash_t *props,
apr_pool_t *scratch_pool)
{
if (kind == svn_node_file)
{
- SVN_ERR(svn_wc__db_op_add_file(db, local_abspath, NULL, scratch_pool));
+ svn_skel_t *work_item = NULL;
+
+ if (props && (svn_prop_get_value(props, SVN_PROP_EXECUTABLE)
+ || svn_prop_get_value(props, SVN_PROP_NEEDS_LOCK)))
+ SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, db, local_abspath,
+ scratch_pool, scratch_pool));
+
+ SVN_ERR(svn_wc__db_op_add_file(db, local_abspath, props, work_item,
+ scratch_pool));
+ if (work_item)
+ SVN_ERR(svn_wc__wq_run(db, local_abspath, NULL, NULL, scratch_pool));
}
else
{
- SVN_ERR(svn_wc__db_op_add_directory(db, local_abspath, NULL,
+ SVN_ERR(svn_wc__db_op_add_directory(db, local_abspath, props, NULL,
scratch_pool));
}
@@ -768,7 +580,7 @@ check_can_add_to_parent(const char **repos_root_url,
{
const char *parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
svn_wc__db_status_t parent_status;
- svn_wc__db_kind_t parent_kind;
+ svn_node_kind_t parent_kind;
svn_error_t *err;
SVN_ERR(svn_wc__write_check(db, parent_abspath, scratch_pool));
@@ -801,7 +613,7 @@ check_can_add_to_parent(const char **repos_root_url,
svn_dirent_local_style(local_abspath,
scratch_pool));
}
- else if (parent_kind != svn_wc__db_kind_dir)
+ else if (parent_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"),
@@ -851,6 +663,7 @@ check_can_add_node(svn_node_kind_t *kind_p,
const char *base_name = svn_dirent_basename(local_abspath, scratch_pool);
svn_boolean_t is_wc_root;
svn_node_kind_t kind;
+ svn_boolean_t is_special;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR_ASSERT(!copyfrom_url || (svn_uri_is_canonical(copyfrom_url,
@@ -867,7 +680,8 @@ check_can_add_node(svn_node_kind_t *kind_p,
SVN_ERR(svn_path_check_valid(local_abspath, scratch_pool));
/* Make sure something's there; set KIND and *KIND_P. */
- SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool));
+ SVN_ERR(svn_io_check_special_path(local_abspath, &kind, &is_special,
+ scratch_pool));
if (kind == svn_node_none)
return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
_("'%s' not found"),
@@ -929,14 +743,20 @@ check_can_add_node(svn_node_kind_t *kind_p,
SVN_ERR_ASSERT(!is_wc_root);
break;
case svn_wc__db_status_normal:
- if (copyfrom_url)
- {
- SVN_ERR(svn_wc__check_wc_root(&is_wc_root, NULL, NULL,
- db, local_abspath,
- scratch_pool));
+ SVN_ERR(svn_wc__db_is_wcroot(&is_wc_root, db, local_abspath,
+ scratch_pool));
- if (is_wc_root)
- break;
+ if (is_wc_root && copyfrom_url)
+ {
+ /* Integrate a sub working copy in a parent working copy
+ (legacy behavior) */
+ break;
+ }
+ else if (is_wc_root && is_special)
+ {
+ /* Adding a symlink to a working copy root.
+ (special_tests.py 23: externals as symlink targets) */
+ break;
}
/* else: Fall through in default error */
@@ -1099,7 +919,7 @@ svn_wc_add4(svn_wc_context_t *wc_ctx,
if (!copyfrom_url) /* Case 2a: It's a simple add */
{
- SVN_ERR(add_from_disk(db, local_abspath, kind, notify_func, notify_baton,
+ SVN_ERR(add_from_disk(db, local_abspath, kind, NULL,
scratch_pool));
if (kind == svn_node_dir && !db_row_exists)
{
@@ -1146,6 +966,7 @@ svn_wc_add4(svn_wc_context_t *wc_ctx,
repos_root_url, repos_uuid,
copyfrom_rev,
NULL /* children */, depth,
+ FALSE /* is_move */,
NULL /* conflicts */,
NULL /* work items */,
scratch_pool));
@@ -1172,11 +993,12 @@ svn_wc_add4(svn_wc_context_t *wc_ctx,
svn_error_t *
-svn_wc_add_from_disk(svn_wc_context_t *wc_ctx,
- const char *local_abspath,
- svn_wc_notify_func2_t notify_func,
- void *notify_baton,
- apr_pool_t *scratch_pool)
+svn_wc_add_from_disk2(svn_wc_context_t *wc_ctx,
+ const char *local_abspath,
+ const apr_hash_t *props,
+ svn_wc_notify_func2_t notify_func,
+ void *notify_baton,
+ apr_pool_t *scratch_pool)
{
svn_node_kind_t kind;
@@ -1184,8 +1006,21 @@ svn_wc_add_from_disk(svn_wc_context_t *wc_ctx,
NULL, SVN_INVALID_REVNUM, scratch_pool));
SVN_ERR(check_can_add_to_parent(NULL, NULL, wc_ctx->db, local_abspath,
scratch_pool, scratch_pool));
- SVN_ERR(add_from_disk(wc_ctx->db, local_abspath, kind,
- notify_func, notify_baton,
+
+ /* Canonicalize and check the props */
+ if (props)
+ {
+ apr_hash_t *new_props;
+
+ SVN_ERR(svn_wc__canonicalize_props(
+ &new_props,
+ local_abspath, kind, props, FALSE /* skip_some_checks */,
+ scratch_pool, scratch_pool));
+ props = new_props;
+ }
+
+ /* Add to the DB and maybe update on-disk executable read-only bits */
+ SVN_ERR(add_from_disk(wc_ctx->db, local_abspath, kind, props,
scratch_pool));
/* Report the addition to the caller. */
@@ -1195,817 +1030,13 @@ svn_wc_add_from_disk(svn_wc_context_t *wc_ctx,
svn_wc_notify_add,
scratch_pool);
notify->kind = kind;
+ notify->mime_type = svn_prop_get_value(props, SVN_PROP_MIME_TYPE);
(*notify_func)(notify_baton, notify, scratch_pool);
}
return SVN_NO_ERROR;
}
-/* Thoughts on Reversion.
-
- What does is mean to revert a given PATH in a tree? We'll
- consider things by their modifications.
-
- Adds
-
- - For files, svn_wc_remove_from_revision_control(), baby.
-
- - Added directories may contain nothing but added children, and
- reverting the addition of a directory necessarily means reverting
- the addition of all the directory's children. Again,
- svn_wc_remove_from_revision_control() should do the trick.
-
- Deletes
-
- - Restore properties to their unmodified state.
-
- - For files, restore the pristine contents, and reset the schedule
- to 'normal'.
-
- - For directories, reset the schedule to 'normal'. All children
- of a directory marked for deletion must also be marked for
- deletion, but it's okay for those children to remain deleted even
- if their parent directory is restored. That's what the
- recursive flag is for.
-
- Replaces
-
- - Restore properties to their unmodified state.
-
- - For files, restore the pristine contents, and reset the schedule
- to 'normal'.
-
- - For directories, reset the schedule to normal. A replaced
- directory can have deleted children (left over from the initial
- deletion), replaced children (children of the initial deletion
- now re-added), and added children (new entries under the
- replaced directory). Since this is technically an addition, it
- necessitates recursion.
-
- Modifications
-
- - Restore properties and, for files, contents to their unmodified
- state.
-
-*/
-
-
-/* Remove conflict file CONFLICT_ABSPATH, which may not exist, and set
- * *NOTIFY_REQUIRED to TRUE if the file was present and removed. */
-static svn_error_t *
-remove_conflict_file(svn_boolean_t *notify_required,
- const char *conflict_abspath,
- const char *local_abspath,
- apr_pool_t *scratch_pool)
-{
- if (conflict_abspath)
- {
- svn_error_t *err = svn_io_remove_file2(conflict_abspath, FALSE,
- scratch_pool);
- if (err)
- svn_error_clear(err);
- else
- *notify_required = TRUE;
- }
-
- return SVN_NO_ERROR;
-}
-
-
-/* Sort copied children obtained from the revert list based on
- * their paths in descending order (longest paths first). */
-static int
-compare_revert_list_copied_children(const void *a, const void *b)
-{
- const svn_wc__db_revert_list_copied_child_info_t * const *ca = a;
- const svn_wc__db_revert_list_copied_child_info_t * const *cb = b;
- int i;
-
- i = svn_path_compare_paths(ca[0]->abspath, cb[0]->abspath);
-
- /* Reverse the result of svn_path_compare_paths() to achieve
- * descending order. */
- return -i;
-}
-
-
-/* Remove all reverted copied children from the directory at LOCAL_ABSPATH.
- * If REMOVE_SELF is TRUE, try to remove LOCAL_ABSPATH itself (REMOVE_SELF
- * should be set if LOCAL_ABSPATH is itself a reverted copy).
- *
- * If REMOVED_SELF is not NULL, indicate in *REMOVED_SELF whether
- * LOCAL_ABSPATH itself was removed.
- *
- * All reverted copied file children are removed from disk. Reverted copied
- * directories left empty as a result are also removed from disk.
- */
-static svn_error_t *
-revert_restore_handle_copied_dirs(svn_boolean_t *removed_self,
- svn_wc__db_t *db,
- const char *local_abspath,
- svn_boolean_t remove_self,
- svn_cancel_func_t cancel_func,
- void *cancel_baton,
- apr_pool_t *scratch_pool)
-{
- const apr_array_header_t *copied_children;
- svn_wc__db_revert_list_copied_child_info_t *child_info;
- int i;
- svn_node_kind_t on_disk;
- apr_pool_t *iterpool;
- svn_error_t *err;
-
- if (removed_self)
- *removed_self = FALSE;
-
- SVN_ERR(svn_wc__db_revert_list_read_copied_children(&copied_children,
- db, local_abspath,
- scratch_pool,
- scratch_pool));
- iterpool = svn_pool_create(scratch_pool);
-
- /* Remove all copied file children. */
- for (i = 0; i < copied_children->nelts; i++)
- {
- child_info = APR_ARRAY_IDX(
- copied_children, i,
- svn_wc__db_revert_list_copied_child_info_t *);
-
- if (cancel_func)
- SVN_ERR(cancel_func(cancel_baton));
-
- if (child_info->kind != svn_wc__db_kind_file)
- continue;
-
- svn_pool_clear(iterpool);
-
- /* Make sure what we delete from disk is really a file. */
- SVN_ERR(svn_io_check_path(child_info->abspath, &on_disk, iterpool));
- if (on_disk != svn_node_file)
- continue;
-
- SVN_ERR(svn_io_remove_file2(child_info->abspath, TRUE, iterpool));
- }
-
- /* Delete every empty child directory.
- * We cannot delete children recursively since we want to keep any files
- * that still exist on disk (e.g. unversioned files within the copied tree).
- * So sort the children list such that longest paths come first and try to
- * remove each child directory in order. */
- qsort(copied_children->elts, copied_children->nelts,
- sizeof(svn_wc__db_revert_list_copied_child_info_t *),
- compare_revert_list_copied_children);
- for (i = 0; i < copied_children->nelts; i++)
- {
- child_info = APR_ARRAY_IDX(
- copied_children, i,
- svn_wc__db_revert_list_copied_child_info_t *);
-
- if (cancel_func)
- SVN_ERR(cancel_func(cancel_baton));
-
- if (child_info->kind != svn_wc__db_kind_dir)
- continue;
-
- svn_pool_clear(iterpool);
-
- err = svn_io_dir_remove_nonrecursive(child_info->abspath, iterpool);
- if (err)
- {
- if (APR_STATUS_IS_ENOENT(err->apr_err) ||
- SVN__APR_STATUS_IS_ENOTDIR(err->apr_err) ||
- APR_STATUS_IS_ENOTEMPTY(err->apr_err))
- svn_error_clear(err);
- else
- return svn_error_trace(err);
- }
- }
-
- if (remove_self)
- {
- /* Delete LOCAL_ABSPATH itself if no children are left. */
- err = svn_io_dir_remove_nonrecursive(local_abspath, iterpool);
- if (err)
- {
- if (APR_STATUS_IS_ENOTEMPTY(err->apr_err))
- svn_error_clear(err);
- else
- return svn_error_trace(err);
- }
- else if (removed_self)
- *removed_self = TRUE;
- }
-
- svn_pool_destroy(iterpool);
-
- return SVN_NO_ERROR;
-}
-
-
-/* Make the working tree under LOCAL_ABSPATH to depth DEPTH match the
- versioned tree. This function is called after svn_wc__db_op_revert
- has done the database revert and created the revert list. Notifies
- for all paths equal to or below LOCAL_ABSPATH that are reverted. */
-static svn_error_t *
-revert_restore(svn_wc__db_t *db,
- const char *local_abspath,
- svn_depth_t depth,
- svn_boolean_t use_commit_times,
- svn_cancel_func_t cancel_func,
- void *cancel_baton,
- svn_wc_notify_func2_t notify_func,
- void *notify_baton,
- apr_pool_t *scratch_pool)
-{
- svn_error_t *err;
- svn_wc__db_status_t status;
- svn_wc__db_kind_t kind;
- svn_node_kind_t on_disk;
- svn_boolean_t notify_required;
- const char *conflict_old;
- const char *conflict_new;
- const char *conflict_working;
- const char *prop_reject;
- svn_filesize_t recorded_size;
- apr_time_t recorded_mod_time;
- apr_finfo_t finfo;
-#ifdef HAVE_SYMLINK
- svn_boolean_t special;
-#endif
- svn_boolean_t copied_here;
- svn_wc__db_kind_t reverted_kind;
-
- if (cancel_func)
- SVN_ERR(cancel_func(cancel_baton));
-
- SVN_ERR(svn_wc__db_revert_list_read(&notify_required,
- &conflict_old, &conflict_new,
- &conflict_working, &prop_reject,
- &copied_here, &reverted_kind,
- db, local_abspath,
- scratch_pool, scratch_pool));
-
- err = svn_wc__db_read_info(&status, &kind,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- &recorded_size, &recorded_mod_time, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- db, local_abspath, scratch_pool, scratch_pool);
-
- if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
- {
- svn_error_clear(err);
-
- if (!copied_here)
- {
- if (notify_func && notify_required)
- notify_func(notify_baton,
- svn_wc_create_notify(local_abspath,
- svn_wc_notify_revert,
- scratch_pool),
- scratch_pool);
-
- if (notify_func)
- SVN_ERR(svn_wc__db_revert_list_notify(notify_func, notify_baton,
- db, local_abspath,
- scratch_pool));
- return SVN_NO_ERROR;
- }
- else
- {
- /* ### Initialise to values which prevent the code below from
- * ### trying to restore anything to disk.
- * ### 'status' should be status_unknown but that doesn't exist. */
- status = svn_wc__db_status_normal;
- kind = svn_wc__db_kind_unknown;
- recorded_size = SVN_INVALID_FILESIZE;
- recorded_mod_time = 0;
- }
- }
- else if (err)
- return svn_error_trace(err);
-
- err = svn_io_stat(&finfo, local_abspath,
- APR_FINFO_TYPE | APR_FINFO_LINK
- | APR_FINFO_SIZE | APR_FINFO_MTIME
- | SVN__APR_FINFO_EXECUTABLE
- | SVN__APR_FINFO_READONLY,
- scratch_pool);
-
- if (err && (APR_STATUS_IS_ENOENT(err->apr_err)
- || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)))
- {
- svn_error_clear(err);
- on_disk = svn_node_none;
-#ifdef HAVE_SYMLINK
- special = FALSE;
-#endif
- }
- else
- {
- if (finfo.filetype == APR_REG || finfo.filetype == APR_LNK)
- on_disk = svn_node_file;
- else if (finfo.filetype == APR_DIR)
- on_disk = svn_node_dir;
- else
- on_disk = svn_node_unknown;
-
-#ifdef HAVE_SYMLINK
- special = (finfo.filetype == APR_LNK);
-#endif
- }
-
- if (copied_here)
- {
- /* The revert target itself is the op-root of a copy. */
- if (reverted_kind == svn_wc__db_kind_file && on_disk == svn_node_file)
- {
- SVN_ERR(svn_io_remove_file2(local_abspath, TRUE, scratch_pool));
- on_disk = svn_node_none;
- }
- else if (reverted_kind == svn_wc__db_kind_dir && on_disk == svn_node_dir)
- {
- svn_boolean_t removed;
-
- SVN_ERR(revert_restore_handle_copied_dirs(&removed, db,
- local_abspath, TRUE,
- cancel_func, cancel_baton,
- scratch_pool));
- if (removed)
- on_disk = svn_node_none;
- }
- }
-
- /* If we expect a versioned item to be present then check that any
- item on disk matches the versioned item, if it doesn't match then
- fix it or delete it. */
- if (on_disk != svn_node_none
- && status != svn_wc__db_status_server_excluded
- && status != svn_wc__db_status_deleted
- && status != svn_wc__db_status_excluded
- && status != svn_wc__db_status_not_present)
- {
- if (on_disk == svn_node_dir && kind != svn_wc__db_kind_dir)
- {
- SVN_ERR(svn_io_remove_dir2(local_abspath, FALSE,
- cancel_func, cancel_baton, scratch_pool));
- on_disk = svn_node_none;
- }
- else if (on_disk == svn_node_file && kind != svn_wc__db_kind_file)
- {
-#ifdef HAVE_SYMLINK
- /* Preserve symlinks pointing at directories. Changes on the
- * directory node have been reverted. The symlink should remain. */
- if (!(special && kind == svn_wc__db_kind_dir))
-#endif
- {
- SVN_ERR(svn_io_remove_file2(local_abspath, FALSE, scratch_pool));
- on_disk = svn_node_none;
- }
- }
- else if (on_disk == svn_node_file)
- {
- svn_boolean_t modified;
- apr_hash_t *props;
-#ifdef HAVE_SYMLINK
- svn_string_t *special_prop;
-#endif
-
- SVN_ERR(svn_wc__db_read_pristine_props(&props, db, local_abspath,
- scratch_pool, scratch_pool));
-
-#ifdef HAVE_SYMLINK
- special_prop = apr_hash_get(props, SVN_PROP_SPECIAL,
- APR_HASH_KEY_STRING);
-
- if ((special_prop != NULL) != special)
- {
- /* File/symlink mismatch. */
- SVN_ERR(svn_io_remove_file2(local_abspath, FALSE, scratch_pool));
- on_disk = svn_node_none;
- }
- else
-#endif
- {
- /* Issue #1663 asserts that we should compare a file in its
- working copy format here, but before r1101473 we would only
- do that if the file was already unequal to its recorded
- information.
-
- r1101473 removes the option of asking for a working format
- compare but *also* check the recorded information first, as
- that combination doesn't guarantee a stable behavior.
- (See the revert_test.py: revert_reexpand_keyword)
-
- But to have the same issue #1663 behavior for revert as we
- had in <=1.6 we only have to check the recorded information
- ourselves. And we already have everything we need, because
- we called stat ourselves. */
- if (recorded_size != SVN_INVALID_FILESIZE
- && recorded_mod_time != 0
- && recorded_size == finfo.size
- && recorded_mod_time == finfo.mtime)
- {
- modified = FALSE;
- }
- else
- SVN_ERR(svn_wc__internal_file_modified_p(&modified,
- db, local_abspath,
- TRUE, scratch_pool));
-
- if (modified)
- {
- SVN_ERR(svn_io_remove_file2(local_abspath, FALSE,
- scratch_pool));
- on_disk = svn_node_none;
- }
- else
- {
- svn_boolean_t read_only;
- svn_string_t *needs_lock_prop;
-
- SVN_ERR(svn_io__is_finfo_read_only(&read_only, &finfo,
- scratch_pool));
-
- needs_lock_prop = apr_hash_get(props, SVN_PROP_NEEDS_LOCK,
- APR_HASH_KEY_STRING);
- if (needs_lock_prop && !read_only)
- {
- SVN_ERR(svn_io_set_file_read_only(local_abspath,
- FALSE, scratch_pool));
- notify_required = TRUE;
- }
- else if (!needs_lock_prop && read_only)
- {
- SVN_ERR(svn_io_set_file_read_write(local_abspath,
- FALSE, scratch_pool));
- notify_required = TRUE;
- }
-
-#if !defined(WIN32) && !defined(__OS2__)
-#ifdef HAVE_SYMLINK
- if (!special)
-#endif
- {
- svn_boolean_t executable;
- svn_string_t *executable_prop;
-
- SVN_ERR(svn_io__is_finfo_executable(&executable, &finfo,
- scratch_pool));
- executable_prop = apr_hash_get(props, SVN_PROP_EXECUTABLE,
- APR_HASH_KEY_STRING);
- if (executable_prop && !executable)
- {
- SVN_ERR(svn_io_set_file_executable(local_abspath,
- TRUE, FALSE,
- scratch_pool));
- notify_required = TRUE;
- }
- else if (!executable_prop && executable)
- {
- SVN_ERR(svn_io_set_file_executable(local_abspath,
- FALSE, FALSE,
- scratch_pool));
- notify_required = TRUE;
- }
- }
-#endif
- }
- }
- }
- }
-
- /* If we expect a versioned item to be present and there is nothing
- on disk then recreate it. */
- if (on_disk == svn_node_none
- && status != svn_wc__db_status_server_excluded
- && status != svn_wc__db_status_deleted
- && status != svn_wc__db_status_excluded
- && status != svn_wc__db_status_not_present)
- {
- if (kind == svn_wc__db_kind_dir)
- SVN_ERR(svn_io_dir_make(local_abspath, APR_OS_DEFAULT, scratch_pool));
-
- if (kind == svn_wc__db_kind_file)
- {
- svn_skel_t *work_item;
-
- /* ### Get the checksum from read_info above and pass in here? */
- SVN_ERR(svn_wc__wq_build_file_install(&work_item, db, local_abspath,
- NULL, use_commit_times, TRUE,
- scratch_pool, scratch_pool));
- SVN_ERR(svn_wc__db_wq_add(db, local_abspath, work_item,
- scratch_pool));
- SVN_ERR(svn_wc__wq_run(db, local_abspath, cancel_func, cancel_baton,
- scratch_pool));
- }
- notify_required = TRUE;
- }
-
- SVN_ERR(remove_conflict_file(&notify_required, conflict_old,
- local_abspath, scratch_pool));
- SVN_ERR(remove_conflict_file(&notify_required, conflict_new,
- local_abspath, scratch_pool));
- SVN_ERR(remove_conflict_file(&notify_required, conflict_working,
- local_abspath, scratch_pool));
- SVN_ERR(remove_conflict_file(&notify_required, prop_reject,
- local_abspath, scratch_pool));
-
- if (notify_func && notify_required)
- notify_func(notify_baton,
- svn_wc_create_notify(local_abspath, svn_wc_notify_revert,
- scratch_pool),
- scratch_pool);
-
- if (depth == svn_depth_infinity && kind == svn_wc__db_kind_dir)
- {
- apr_pool_t *iterpool = svn_pool_create(scratch_pool);
- const apr_array_header_t *children;
- int i;
-
- SVN_ERR(revert_restore_handle_copied_dirs(NULL, db, local_abspath, FALSE,
- cancel_func, cancel_baton,
- iterpool));
-
- SVN_ERR(svn_wc__db_read_children_of_working_node(&children, db,
- local_abspath,
- scratch_pool,
- iterpool));
- for (i = 0; i < children->nelts; ++i)
- {
- const char *child_abspath;
-
- svn_pool_clear(iterpool);
-
- child_abspath = svn_dirent_join(local_abspath,
- APR_ARRAY_IDX(children, i,
- const char *),
- iterpool);
-
- SVN_ERR(revert_restore(db, child_abspath, depth,
- use_commit_times,
- cancel_func, cancel_baton,
- notify_func, notify_baton,
- iterpool));
- }
-
- svn_pool_destroy(iterpool);
- }
-
- if (notify_func)
- SVN_ERR(svn_wc__db_revert_list_notify(notify_func, notify_baton,
- db, local_abspath, scratch_pool));
- return SVN_NO_ERROR;
-}
-
-
-/* Revert tree LOCAL_ABSPATH to depth DEPTH and notify for all
- reverts. */
-static svn_error_t *
-new_revert_internal(svn_wc__db_t *db,
- const char *local_abspath,
- svn_depth_t depth,
- svn_boolean_t use_commit_times,
- svn_cancel_func_t cancel_func,
- void *cancel_baton,
- svn_wc_notify_func2_t notify_func,
- void *notify_baton,
- apr_pool_t *scratch_pool)
-{
- svn_error_t *err;
-
- SVN_ERR_ASSERT(depth == svn_depth_empty || depth == svn_depth_infinity);
-
- /* We should have a write lock on the parent of local_abspath, except
- when local_abspath is the working copy root. */
- {
- const char *dir_abspath;
-
- SVN_ERR(svn_wc__db_get_wcroot(&dir_abspath, db, local_abspath,
- scratch_pool, scratch_pool));
-
- if (svn_dirent_is_child(dir_abspath, local_abspath, NULL))
- dir_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
-
- SVN_ERR(svn_wc__write_check(db, dir_abspath, scratch_pool));
- }
-
- err = svn_wc__db_op_revert(db, local_abspath, depth,
- scratch_pool, scratch_pool);
-
- if (!err)
- err = revert_restore(db, local_abspath, depth,
- use_commit_times,
- cancel_func, cancel_baton,
- notify_func, notify_baton,
- scratch_pool);
-
- err = svn_error_compose_create(err,
- svn_wc__db_revert_list_done(db,
- local_abspath,
- scratch_pool));
-
- return err;
-}
-
-
-/* Revert files in LOCAL_ABSPATH to depth DEPTH that match
- CHANGELIST_HASH and notify for all reverts. */
-static svn_error_t *
-new_revert_changelist(svn_wc__db_t *db,
- const char *local_abspath,
- svn_depth_t depth,
- svn_boolean_t use_commit_times,
- apr_hash_t *changelist_hash,
- svn_cancel_func_t cancel_func,
- void *cancel_baton,
- svn_wc_notify_func2_t notify_func,
- void *notify_baton,
- apr_pool_t *scratch_pool)
-{
- apr_pool_t *iterpool;
- const apr_array_header_t *children;
- int i;
-
- if (cancel_func)
- SVN_ERR(cancel_func(cancel_baton));
-
- /* Revert this node (depth=empty) if it matches one of the changelists. */
- if (svn_wc__internal_changelist_match(db, local_abspath, changelist_hash,
- scratch_pool))
- SVN_ERR(new_revert_internal(db, local_abspath,
- svn_depth_empty, use_commit_times,
- cancel_func, cancel_baton,
- notify_func, notify_baton,
- scratch_pool));
-
- if (depth == svn_depth_empty)
- return SVN_NO_ERROR;
-
- iterpool = svn_pool_create(scratch_pool);
-
- /* We can handle both depth=files and depth=immediates by setting
- depth=empty here. We don't need to distinguish files and
- directories when making the recursive call because directories
- can never match a changelist, so making the recursive call for
- directories when asked for depth=files is a no-op. */
- if (depth == svn_depth_files || depth == svn_depth_immediates)
- depth = svn_depth_empty;
-
- SVN_ERR(svn_wc__db_read_children_of_working_node(&children, db,
- local_abspath,
- scratch_pool,
- iterpool));
- for (i = 0; i < children->nelts; ++i)
- {
- const char *child_abspath;
-
- svn_pool_clear(iterpool);
-
- child_abspath = svn_dirent_join(local_abspath,
- APR_ARRAY_IDX(children, i,
- const char *),
- iterpool);
-
- SVN_ERR(new_revert_changelist(db, child_abspath, depth,
- use_commit_times, changelist_hash,
- cancel_func, cancel_baton,
- notify_func, notify_baton,
- iterpool));
- }
-
- svn_pool_destroy(iterpool);
-
- return SVN_NO_ERROR;
-}
-
-
-/* Does a partially recursive revert of LOCAL_ABSPATH to depth DEPTH
- (which must be either svn_depth_files or svn_depth_immediates) by
- doing a non-recursive revert on each permissible path. Notifies
- all reverted paths.
-
- ### This won't revert a copied dir with one level of children since
- ### the non-recursive revert on the dir will fail. Not sure how a
- ### partially recursive revert should handle actual-only nodes. */
-static svn_error_t *
-new_revert_partial(svn_wc__db_t *db,
- const char *local_abspath,
- svn_depth_t depth,
- svn_boolean_t use_commit_times,
- svn_cancel_func_t cancel_func,
- void *cancel_baton,
- svn_wc_notify_func2_t notify_func,
- void *notify_baton,
- apr_pool_t *scratch_pool)
-{
- apr_pool_t *iterpool;
- const apr_array_header_t *children;
- int i;
-
- SVN_ERR_ASSERT(depth == svn_depth_files || depth == svn_depth_immediates);
-
- if (cancel_func)
- SVN_ERR(cancel_func(cancel_baton));
-
- iterpool = svn_pool_create(scratch_pool);
-
- /* Revert the root node itself (depth=empty), then move on to the
- children. */
- SVN_ERR(new_revert_internal(db, local_abspath, svn_depth_empty,
- use_commit_times, cancel_func, cancel_baton,
- notify_func, notify_baton, iterpool));
-
- SVN_ERR(svn_wc__db_read_children_of_working_node(&children, db,
- local_abspath,
- scratch_pool,
- iterpool));
- for (i = 0; i < children->nelts; ++i)
- {
- const char *child_abspath;
-
- svn_pool_clear(iterpool);
-
- child_abspath = svn_dirent_join(local_abspath,
- APR_ARRAY_IDX(children, i, const char *),
- iterpool);
-
- /* For svn_depth_files: don't revert non-files. */
- if (depth == svn_depth_files)
- {
- svn_wc__db_kind_t kind;
-
- SVN_ERR(svn_wc__db_read_kind(&kind, db, child_abspath, TRUE,
- iterpool));
- if (kind != svn_wc__db_kind_file)
- continue;
- }
-
- /* Revert just this node (depth=empty). */
- SVN_ERR(new_revert_internal(db, child_abspath,
- svn_depth_empty, use_commit_times,
- cancel_func, cancel_baton,
- notify_func, notify_baton,
- iterpool));
- }
-
- svn_pool_destroy(iterpool);
-
- return SVN_NO_ERROR;
-}
-
-
-svn_error_t *
-svn_wc_revert4(svn_wc_context_t *wc_ctx,
- const char *local_abspath,
- svn_depth_t depth,
- svn_boolean_t use_commit_times,
- const apr_array_header_t *changelist_filter,
- svn_cancel_func_t cancel_func,
- void *cancel_baton,
- svn_wc_notify_func2_t notify_func,
- void *notify_baton,
- apr_pool_t *scratch_pool)
-{
- if (changelist_filter && changelist_filter->nelts)
- {
- apr_hash_t *changelist_hash;
-
- SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelist_filter,
- scratch_pool));
- return svn_error_trace(new_revert_changelist(wc_ctx->db, local_abspath,
- depth, use_commit_times,
- changelist_hash,
- cancel_func, cancel_baton,
- notify_func, notify_baton,
- scratch_pool));
- }
-
- if (depth == svn_depth_empty || depth == svn_depth_infinity)
- return svn_error_trace(new_revert_internal(wc_ctx->db, local_abspath,
- depth, use_commit_times,
- cancel_func, cancel_baton,
- notify_func, notify_baton,
- scratch_pool));
-
- /* The user may expect svn_depth_files/svn_depth_immediates to work
- on copied dirs with one level of children. It doesn't, the user
- will get an error and will need to invoke an infinite revert. If
- we identified those cases where svn_depth_infinity would not
- revert too much we could invoke the recursive call above. */
-
- if (depth == svn_depth_files || depth == svn_depth_immediates)
- return svn_error_trace(new_revert_partial(wc_ctx->db, local_abspath,
- depth, use_commit_times,
- cancel_func, cancel_baton,
- notify_func, notify_baton,
- scratch_pool));
-
- /* Bogus depth. Tell the caller. */
- return svn_error_create(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL, NULL);
-}
-
-
/* Return a path where nothing exists on disk, within the admin directory
belonging to the WCROOT_ABSPATH directory. */
static const char *
@@ -2027,7 +1058,7 @@ svn_wc_get_pristine_copy_path(const char *path,
SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
- SVN_ERR(svn_wc__db_open(&db, NULL, TRUE, TRUE, pool, pool));
+ SVN_ERR(svn_wc__db_open(&db, NULL, FALSE, TRUE, pool, pool));
/* DB is now open. This is seemingly a "light" function that a caller
may use repeatedly despite error return values. The rest of this
function should aggressively close DB, even in the error case. */
@@ -2067,238 +1098,72 @@ svn_wc_get_pristine_contents2(svn_stream_t **contents,
}
-svn_error_t *
-svn_wc__internal_remove_from_revision_control(svn_wc__db_t *db,
- const char *local_abspath,
- svn_boolean_t destroy_wf,
- svn_boolean_t instant_error,
- svn_cancel_func_t cancel_func,
- void *cancel_baton,
- apr_pool_t *scratch_pool)
+typedef struct get_pristine_lazyopen_baton_t
{
- svn_error_t *err;
- svn_boolean_t left_something = FALSE;
- svn_wc__db_status_t status;
- svn_wc__db_kind_t kind;
-
- /* ### This whole function should be rewritten to run inside a transaction,
- ### to allow a stable cancel behavior.
- ###
- ### Subversion < 1.7 marked the directory as incomplete to allow updating
- ### it from a canceled state. But this would not work because update
- ### doesn't retrieve deleted items.
- ###
- ### WC-NG doesn't support a delete+incomplete state, but we can't build
- ### transactions over multiple databases yet. */
+ svn_wc_context_t *wc_ctx;
+ const char *wri_abspath;
+ const svn_checksum_t *checksum;
- SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
+} get_pristine_lazyopen_baton_t;
- /* Check cancellation here, so recursive calls get checked early. */
- if (cancel_func)
- SVN_ERR(cancel_func(cancel_baton));
- SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL,
- db, local_abspath, scratch_pool, scratch_pool));
-
- if (kind == svn_wc__db_kind_file || kind == svn_wc__db_kind_symlink)
- {
- svn_boolean_t text_modified_p = FALSE;
-
- if (instant_error || destroy_wf)
- {
- svn_node_kind_t on_disk;
- SVN_ERR(svn_io_check_path(local_abspath, &on_disk, scratch_pool));
- if (on_disk == svn_node_file)
- {
- /* Check for local mods. before removing entry */
- SVN_ERR(svn_wc__internal_file_modified_p(&text_modified_p, db,
- local_abspath, FALSE,
- scratch_pool));
- if (text_modified_p && instant_error)
- return svn_error_createf(SVN_ERR_WC_LEFT_LOCAL_MOD, NULL,
- _("File '%s' has local modifications"),
- svn_dirent_local_style(local_abspath, scratch_pool));
- }
- }
-
- /* Remove NAME from DB */
- SVN_ERR(svn_wc__db_op_remove_node(db, local_abspath,
- SVN_INVALID_REVNUM,
- svn_wc__db_kind_unknown,
- scratch_pool));
-
- /* If we were asked to destroy the working file, do so unless
- it has local mods. */
- if (destroy_wf)
- {
- /* Don't kill local mods. */
- if (text_modified_p)
- return svn_error_create(SVN_ERR_WC_LEFT_LOCAL_MOD, NULL, NULL);
- else /* The working file is still present; remove it. */
- SVN_ERR(svn_io_remove_file2(local_abspath, TRUE, scratch_pool));
- }
-
- } /* done with file case */
- else /* looking at THIS_DIR */
- {
- apr_pool_t *iterpool = svn_pool_create(scratch_pool);
- const apr_array_header_t *children;
- int i;
-
- /* ### sanity check: check 2 places for DELETED flag? */
-
- /* Walk over every entry. */
- SVN_ERR(svn_wc__db_read_children(&children, db, local_abspath,
- scratch_pool, iterpool));
-
- for (i = 0; i < children->nelts; i++)
- {
- const char *node_name = APR_ARRAY_IDX(children, i, const char*);
- const char *node_abspath;
- svn_wc__db_status_t node_status;
- svn_wc__db_kind_t node_kind;
-
- svn_pool_clear(iterpool);
-
- node_abspath = svn_dirent_join(local_abspath, node_name, iterpool);
-
- SVN_ERR(svn_wc__db_read_info(&node_status, &node_kind, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL,
- db, node_abspath,
- iterpool, iterpool));
-
- if (node_status == svn_wc__db_status_normal
- && node_kind == svn_wc__db_kind_dir)
- {
- svn_boolean_t is_root;
-
- SVN_ERR(svn_wc__check_wc_root(&is_root, NULL, NULL,
- db, node_abspath, iterpool));
-
- if (is_root)
- continue; /* Just skip working copies as obstruction */
- }
-
- if (node_status != svn_wc__db_status_normal
- && node_status != svn_wc__db_status_added
- && node_status != svn_wc__db_status_incomplete)
- {
- /* The node is already 'deleted', so nothing to do on
- versioned nodes */
- SVN_ERR(svn_wc__db_op_remove_node(db, node_abspath,
- SVN_INVALID_REVNUM,
- svn_wc__db_kind_unknown,
- iterpool));
-
- continue;
- }
-
- err = svn_wc__internal_remove_from_revision_control(
- db, node_abspath,
- destroy_wf, instant_error,
- cancel_func, cancel_baton,
- iterpool);
-
- if (err && (err->apr_err == SVN_ERR_WC_LEFT_LOCAL_MOD))
- {
- if (instant_error)
- return svn_error_trace(err);
- else
- {
- svn_error_clear(err);
- left_something = TRUE;
- }
- }
- else if (err)
- return svn_error_trace(err);
- }
+/* Implements svn_stream_lazyopen_func_t */
+static svn_error_t *
+get_pristine_lazyopen_func(svn_stream_t **stream,
+ void *baton,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ get_pristine_lazyopen_baton_t *b = baton;
+ const svn_checksum_t *sha1_checksum;
- /* At this point, every directory below this one has been
- removed from revision control. */
+ /* svn_wc__db_pristine_read() wants a SHA1, so if we have an MD5,
+ we'll use it to lookup the SHA1. */
+ if (b->checksum->kind == svn_checksum_sha1)
+ sha1_checksum = b->checksum;
+ else
+ SVN_ERR(svn_wc__db_pristine_get_sha1(&sha1_checksum, b->wc_ctx->db,
+ b->wri_abspath, b->checksum,
+ scratch_pool, scratch_pool));
- /* Remove self from parent's entries file, but only if parent is
- a working copy. If it's not, that's fine, we just move on. */
- {
- svn_boolean_t is_root;
+ SVN_ERR(svn_wc__db_pristine_read(stream, NULL, b->wc_ctx->db,
+ b->wri_abspath, sha1_checksum,
+ result_pool, scratch_pool));
+ return SVN_NO_ERROR;
+}
- SVN_ERR(svn_wc__check_wc_root(&is_root, NULL, NULL,
- db, local_abspath, iterpool));
+svn_error_t *
+svn_wc__get_pristine_contents_by_checksum(svn_stream_t **contents,
+ svn_wc_context_t *wc_ctx,
+ const char *wri_abspath,
+ const svn_checksum_t *checksum,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_boolean_t present;
- /* If full_path is not the top of a wc, then its parent
- directory is also a working copy and has an entry for
- full_path. We need to remove that entry: */
- if (! is_root)
- {
- SVN_ERR(svn_wc__db_op_remove_node(db, local_abspath,
- SVN_INVALID_REVNUM,
- svn_wc__db_kind_unknown,
- iterpool));
- }
- else
- {
- /* Remove the entire administrative .svn area, thereby removing
- _this_ dir from revision control too. */
- SVN_ERR(svn_wc__adm_destroy(db, local_abspath,
- cancel_func, cancel_baton, iterpool));
- }
- }
+ *contents = NULL;
- /* If caller wants us to recursively nuke everything on disk, go
- ahead, provided that there are no dangling local-mod files
- below */
- if (destroy_wf && (! left_something))
- {
- /* If the dir is *truly* empty (i.e. has no unversioned
- resources, all versioned files are gone, all .svn dirs are
- gone, and contains nothing but empty dirs), then a
- *non*-recursive dir_remove should work. If it doesn't,
- no big deal. Just assume there are unversioned items in
- there and set "left_something" */
- err = svn_io_dir_remove_nonrecursive(local_abspath, iterpool);
- if (err)
- {
- if (!APR_STATUS_IS_ENOENT(err->apr_err))
- left_something = TRUE;
- svn_error_clear(err);
- }
- }
+ SVN_ERR(svn_wc__db_pristine_check(&present, wc_ctx->db, wri_abspath,
+ checksum, scratch_pool));
- svn_pool_destroy(iterpool);
+ if (present)
+ {
+ get_pristine_lazyopen_baton_t *gpl_baton;
- } /* end of directory case */
+ gpl_baton = apr_pcalloc(result_pool, sizeof(*gpl_baton));
+ gpl_baton->wc_ctx = wc_ctx;
+ gpl_baton->wri_abspath = wri_abspath;
+ gpl_baton->checksum = checksum;
- if (left_something)
- return svn_error_create(SVN_ERR_WC_LEFT_LOCAL_MOD, NULL, NULL);
+ *contents = svn_stream_lazyopen_create(get_pristine_lazyopen_func,
+ gpl_baton, FALSE, result_pool);
+ }
return SVN_NO_ERROR;
}
-svn_error_t *
-svn_wc_remove_from_revision_control2(svn_wc_context_t *wc_ctx,
- const char *local_abspath,
- svn_boolean_t destroy_wf,
- svn_boolean_t instant_error,
- svn_cancel_func_t cancel_func,
- void *cancel_baton,
- apr_pool_t *scratch_pool)
-{
- return svn_error_trace(
- svn_wc__internal_remove_from_revision_control(wc_ctx->db,
- local_abspath,
- destroy_wf,
- instant_error,
- cancel_func,
- cancel_baton,
- scratch_pool));
-}
-
svn_error_t *
svn_wc_add_lock2(svn_wc_context_t *wc_ctx,
@@ -2312,6 +1177,11 @@ svn_wc_add_lock2(svn_wc_context_t *wc_ctx,
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
+ /* ### Enable after fixing callers */
+ /*SVN_ERR(svn_wc__write_check(wc_ctx->db,
+ svn_dirent_dirname(local_abspath, scratch_pool),
+ scratch_pool));*/
+
db_lock.token = lock->token;
db_lock.owner = lock->owner;
db_lock.comment = lock->comment;
@@ -2331,9 +1201,19 @@ svn_wc_add_lock2(svn_wc_context_t *wc_ctx,
}
/* if svn:needs-lock is present, then make the file read-write. */
- SVN_ERR(svn_wc__internal_propget(&needs_lock, wc_ctx->db, local_abspath,
- SVN_PROP_NEEDS_LOCK, scratch_pool,
- scratch_pool));
+ err = svn_wc__internal_propget(&needs_lock, wc_ctx->db, local_abspath,
+ SVN_PROP_NEEDS_LOCK, scratch_pool,
+ scratch_pool);
+
+ if (err && err->apr_err == SVN_ERR_WC_PATH_UNEXPECTED_STATUS)
+ {
+ /* The node has non wc representation (e.g. deleted), so
+ we don't want to touch the in-wc file */
+ svn_error_clear(err);
+ return SVN_NO_ERROR;
+ }
+ SVN_ERR(err);
+
if (needs_lock)
SVN_ERR(svn_io_set_file_read_write(local_abspath, FALSE, scratch_pool));
@@ -2351,6 +1231,11 @@ svn_wc_remove_lock2(svn_wc_context_t *wc_ctx,
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
+ /* ### Enable after fixing callers */
+ /*SVN_ERR(svn_wc__write_check(wc_ctx->db,
+ svn_dirent_dirname(local_abspath, scratch_pool),
+ scratch_pool));*/
+
err = svn_wc__db_lock_remove(wc_ctx->db, local_abspath, scratch_pool);
if (err)
{
@@ -2366,9 +1251,19 @@ svn_wc_remove_lock2(svn_wc_context_t *wc_ctx,
}
/* if svn:needs-lock is present, then make the file read-only. */
- SVN_ERR(svn_wc__internal_propget(&needs_lock, wc_ctx->db, local_abspath,
- SVN_PROP_NEEDS_LOCK, scratch_pool,
- scratch_pool));
+ err = svn_wc__internal_propget(&needs_lock, wc_ctx->db, local_abspath,
+ SVN_PROP_NEEDS_LOCK, scratch_pool,
+ scratch_pool);
+ if (err)
+ {
+ if (err->apr_err != SVN_ERR_WC_PATH_UNEXPECTED_STATUS)
+ return svn_error_trace(err);
+
+ svn_error_clear(err);
+ return SVN_NO_ERROR; /* Node is shadowed and/or deleted,
+ so we shouldn't apply its lock */
+ }
+
if (needs_lock)
SVN_ERR(svn_io_set_file_read_only(local_abspath, FALSE, scratch_pool));
@@ -2448,6 +1343,7 @@ svn_wc_get_changelists(svn_wc_context_t *wc_ctx,
apr_pool_t *scratch_pool)
{
struct get_cl_fn_baton gnb;
+
gnb.db = wc_ctx->db;
gnb.clhash = NULL;
gnb.callback_func = callback_func;
@@ -2491,8 +1387,7 @@ svn_wc__internal_changelist_match(svn_wc__db_t *db,
}
return (changelist
- && apr_hash_get((apr_hash_t *)clhash, changelist,
- APR_HASH_KEY_STRING) != NULL);
+ && svn_hash_gets((apr_hash_t *)clhash, changelist) != NULL);
}