aboutsummaryrefslogtreecommitdiffstats
path: root/subversion/libsvn_client
diff options
context:
space:
mode:
authorPeter Wemm <peter@FreeBSD.org>2015-10-12 08:54:49 +0000
committerPeter Wemm <peter@FreeBSD.org>2015-10-12 08:54:49 +0000
commitdc5d469d6574e9fb03bdd793658bb371315b306a (patch)
tree013c2e6845398e5a9ca4901dcc077769c7520e1d /subversion/libsvn_client
parent58218291fa73a17020ef0447398e9e8a78f9e8c7 (diff)
downloadsrc-dc5d469d6574e9fb03bdd793658bb371315b306a.tar.gz
src-dc5d469d6574e9fb03bdd793658bb371315b306a.zip
Vendor import of subversion-1.9.2vendor/subversion/subversion-1.9.2
Notes
Notes: svn path=/vendor/subversion/dist/; revision=289177 svn path=/vendor/subversion/subversion-1.9.2/; revision=289178; tag=vendor/subversion/subversion-1.9.2
Diffstat (limited to 'subversion/libsvn_client')
-rw-r--r--subversion/libsvn_client/add.c143
-rw-r--r--subversion/libsvn_client/blame.c307
-rw-r--r--subversion/libsvn_client/cat.c67
-rw-r--r--subversion/libsvn_client/checkout.c81
-rw-r--r--subversion/libsvn_client/cleanup.c228
-rw-r--r--subversion/libsvn_client/client.h210
-rw-r--r--subversion/libsvn_client/cmdline.c15
-rw-r--r--subversion/libsvn_client/commit.c266
-rw-r--r--subversion/libsvn_client/commit_util.c87
-rw-r--r--subversion/libsvn_client/compat_providers.c4
-rw-r--r--subversion/libsvn_client/copy.c1035
-rw-r--r--subversion/libsvn_client/copy_foreign.c20
-rw-r--r--subversion/libsvn_client/ctx.c39
-rw-r--r--subversion/libsvn_client/delete.c21
-rw-r--r--subversion/libsvn_client/deprecated.c135
-rw-r--r--subversion/libsvn_client/diff.c2066
-rw-r--r--subversion/libsvn_client/diff_local.c1077
-rw-r--r--subversion/libsvn_client/diff_summarize.c338
-rw-r--r--subversion/libsvn_client/export.c8
-rw-r--r--subversion/libsvn_client/externals.c138
-rw-r--r--subversion/libsvn_client/import.c191
-rw-r--r--subversion/libsvn_client/info.c120
-rw-r--r--subversion/libsvn_client/iprops.c6
-rw-r--r--subversion/libsvn_client/libsvn_client.pc.in12
-rw-r--r--subversion/libsvn_client/list.c120
-rw-r--r--subversion/libsvn_client/locking_commands.c333
-rw-r--r--subversion/libsvn_client/log.c58
-rw-r--r--subversion/libsvn_client/merge.c554
-rw-r--r--subversion/libsvn_client/mergeinfo.c85
-rw-r--r--subversion/libsvn_client/mergeinfo.h6
-rw-r--r--subversion/libsvn_client/mtcc.c1429
-rw-r--r--subversion/libsvn_client/patch.c287
-rw-r--r--subversion/libsvn_client/prop_commands.c127
-rw-r--r--subversion/libsvn_client/ra.c127
-rw-r--r--subversion/libsvn_client/relocate.c125
-rw-r--r--subversion/libsvn_client/repos_diff.c15
-rw-r--r--subversion/libsvn_client/resolved.c8
-rw-r--r--subversion/libsvn_client/revert.c46
-rw-r--r--subversion/libsvn_client/revisions.c1
-rw-r--r--subversion/libsvn_client/status.c81
-rw-r--r--subversion/libsvn_client/switch.c10
-rw-r--r--subversion/libsvn_client/update.c168
-rw-r--r--subversion/libsvn_client/upgrade.c16
-rw-r--r--subversion/libsvn_client/util.c12
44 files changed, 6769 insertions, 3453 deletions
diff --git a/subversion/libsvn_client/add.c b/subversion/libsvn_client/add.c
index f121bc87d17f..ce7891afb27c 100644
--- a/subversion/libsvn_client/add.c
+++ b/subversion/libsvn_client/add.c
@@ -48,6 +48,7 @@
#include "private/svn_client_private.h"
#include "private/svn_wc_private.h"
#include "private/svn_ra_private.h"
+#include "private/svn_sorts_private.h"
#include "private/svn_magic.h"
#include "svn_private_config.h"
@@ -168,8 +169,8 @@ get_auto_props_for_pattern(apr_hash_t *properties,
hi != NULL;
hi = apr_hash_next(hi))
{
- const char *propname = svn__apr_hash_index_key(hi);
- const char *propval = svn__apr_hash_index_val(hi);
+ const char *propname = apr_hash_this_key(hi);
+ const char *propval = apr_hash_this_val(hi);
svn_string_t *propval_str =
svn_string_create_empty(apr_hash_pool_get(properties));
@@ -206,8 +207,8 @@ svn_client__get_paths_auto_props(apr_hash_t **properties,
hi != NULL;
hi = apr_hash_next(hi))
{
- const char *pattern = svn__apr_hash_index_key(hi);
- apr_hash_t *propvals = svn__apr_hash_index_val(hi);
+ const char *pattern = apr_hash_this_key(hi);
+ apr_hash_t *propvals = apr_hash_this_val(hi);
get_auto_props_for_pattern(*properties, mimetype, &have_executable,
svn_dirent_basename(path, scratch_pool),
@@ -316,7 +317,8 @@ add_file(const char *local_abspath,
}
/* Add the file */
- SVN_ERR(svn_wc_add_from_disk2(ctx->wc_ctx, local_abspath, properties,
+ SVN_ERR(svn_wc_add_from_disk3(ctx->wc_ctx, local_abspath, properties,
+ FALSE /* skip checks */,
ctx->notify_func2, ctx->notify_baton2, pool));
return SVN_NO_ERROR;
@@ -378,7 +380,8 @@ add_dir_recursive(const char *dir_abspath,
iterpool = svn_pool_create(scratch_pool);
/* Add this directory to revision control. */
- err = svn_wc_add_from_disk2(ctx->wc_ctx, dir_abspath, NULL /*props*/,
+ err = svn_wc_add_from_disk3(ctx->wc_ctx, dir_abspath, NULL /*props*/,
+ FALSE /* skip checks */,
ctx->notify_func2, ctx->notify_baton2,
iterpool);
if (err)
@@ -424,8 +427,8 @@ add_dir_recursive(const char *dir_abspath,
version control. */
for (hi = apr_hash_first(scratch_pool, dirents); hi; hi = apr_hash_next(hi))
{
- const char *name = svn__apr_hash_index_key(hi);
- svn_io_dirent2_t *dirent = svn__apr_hash_index_val(hi);
+ const char *name = apr_hash_this_key(hi);
+ svn_io_dirent2_t *dirent = apr_hash_this_val(hi);
const char *abspath;
svn_pool_clear(iterpool);
@@ -704,15 +707,12 @@ svn_client__get_all_auto_props(apr_hash_t **autoprops,
for (i = 0; i < inherited_config_auto_props->nelts; i++)
{
- apr_hash_index_t *hi;
svn_prop_inherited_item_t *elt = APR_ARRAY_IDX(
inherited_config_auto_props, i, svn_prop_inherited_item_t *);
+ const svn_string_t *propval =
+ svn_hash_gets(elt->prop_hash, SVN_PROP_INHERITABLE_AUTO_PROPS);
- for (hi = apr_hash_first(scratch_pool, elt->prop_hash);
- hi;
- hi = apr_hash_next(hi))
{
- const svn_string_t *propval = svn__apr_hash_index_val(hi);
const char *ch = propval->data;
svn_stringbuf_t *config_auto_prop_pattern;
svn_stringbuf_t *config_auto_prop_val;
@@ -768,59 +768,6 @@ svn_client__get_all_auto_props(apr_hash_t **autoprops,
return SVN_NO_ERROR;
}
-svn_error_t *svn_client__get_inherited_ignores(apr_array_header_t **ignores,
- const char *path_or_url,
- svn_client_ctx_t *ctx,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
-{
- svn_opt_revision_t rev;
- apr_hash_t *explicit_ignores;
- apr_array_header_t *inherited_ignores;
- svn_boolean_t target_is_url = svn_path_is_url(path_or_url);
- svn_string_t *explicit_prop;
- int i;
-
- if (target_is_url)
- rev.kind = svn_opt_revision_head;
- else
- rev.kind = svn_opt_revision_working;
-
- SVN_ERR(svn_client_propget5(&explicit_ignores, &inherited_ignores,
- SVN_PROP_INHERITABLE_IGNORES, path_or_url,
- &rev, &rev, NULL, svn_depth_empty, NULL, ctx,
- scratch_pool, scratch_pool));
-
- explicit_prop = svn_hash_gets(explicit_ignores, path_or_url);
-
- if (explicit_prop)
- {
- svn_prop_inherited_item_t *new_iprop =
- apr_palloc(scratch_pool, sizeof(*new_iprop));
- new_iprop->path_or_url = path_or_url;
- new_iprop->prop_hash = apr_hash_make(scratch_pool);
- svn_hash_sets(new_iprop->prop_hash, SVN_PROP_INHERITABLE_IGNORES,
- explicit_prop);
- APR_ARRAY_PUSH(inherited_ignores,
- svn_prop_inherited_item_t *) = new_iprop;
- }
-
- *ignores = apr_array_make(result_pool, 16, sizeof(const char *));
-
- for (i = 0; i < inherited_ignores->nelts; i++)
- {
- svn_prop_inherited_item_t *elt = APR_ARRAY_IDX(
- inherited_ignores, i, svn_prop_inherited_item_t *);
- svn_string_t *ignore_val = svn_hash_gets(elt->prop_hash,
- SVN_PROP_INHERITABLE_IGNORES);
- if (ignore_val)
- svn_cstring_split_append(*ignores, ignore_val->data, "\n\r\t\v ",
- FALSE, result_pool);
- }
-
- return SVN_NO_ERROR;
-}
-
/* The main logic of the public svn_client_add5.
*
* EXISTING_PARENT_ABSPATH is the absolute path to the first existing
@@ -841,7 +788,7 @@ add(const char *local_abspath,
svn_magic__cookie_t *magic_cookie;
apr_array_header_t *ignores = NULL;
- svn_magic__init(&magic_cookie, scratch_pool);
+ SVN_ERR(svn_magic__init(&magic_cookie, ctx->config, scratch_pool));
if (existing_parent_abspath)
{
@@ -876,8 +823,9 @@ add(const char *local_abspath,
parent_abspath, local_abspath);
SVN_ERR(svn_io_make_dir_recursively(parent_abspath, scratch_pool));
- SVN_ERR(svn_wc_add_from_disk2(ctx->wc_ctx, parent_abspath,
+ SVN_ERR(svn_wc_add_from_disk3(ctx->wc_ctx, parent_abspath,
NULL /*props*/,
+ FALSE /* skip checks */,
ctx->notify_func2, ctx->notify_baton2,
scratch_pool));
}
@@ -1169,8 +1117,8 @@ mkdir_urls(const apr_array_header_t *urls,
}
}
}
- qsort(targets->elts, targets->nelts, targets->elt_size,
- svn_sort_compare_paths);
+
+ svn_sort__array(targets, svn_sort_compare_paths);
/* ### This reparent may be problematic in limited-authz-to-common-parent
### scenarios (compare issue #3242). See also issue #3649. */
@@ -1228,53 +1176,58 @@ mkdir_urls(const apr_array_header_t *urls,
pool));
/* Call the path-based editor driver. */
- err = svn_delta_path_driver2(editor, edit_baton, targets, TRUE,
- path_driver_cb_func, (void *)editor, pool);
+ err = svn_error_trace(
+ svn_delta_path_driver2(editor, edit_baton, targets, TRUE,
+ path_driver_cb_func, (void *)editor, pool));
if (err)
{
/* At least try to abort the edit (and fs txn) before throwing err. */
return svn_error_compose_create(
err,
- editor->abort_edit(edit_baton, pool));
+ svn_error_trace(editor->abort_edit(edit_baton, pool)));
+ }
+
+ if (ctx->notify_func2)
+ {
+ svn_wc_notify_t *notify;
+ notify = svn_wc_create_notify_url(common,
+ svn_wc_notify_commit_finalizing,
+ pool);
+ ctx->notify_func2(ctx->notify_baton2, notify, pool);
}
/* Close the edit. */
- return editor->close_edit(edit_baton, pool);
+ return svn_error_trace(editor->close_edit(edit_baton, pool));
}
svn_error_t *
-svn_client__make_local_parents(const char *path,
+svn_client__make_local_parents(const char *local_abspath,
svn_boolean_t make_parents,
svn_client_ctx_t *ctx,
- apr_pool_t *pool)
+ apr_pool_t *scratch_pool)
{
svn_error_t *err;
svn_node_kind_t orig_kind;
- SVN_ERR(svn_io_check_path(path, &orig_kind, pool));
+ SVN_ERR(svn_io_check_path(local_abspath, &orig_kind, scratch_pool));
if (make_parents)
- SVN_ERR(svn_io_make_dir_recursively(path, pool));
+ SVN_ERR(svn_io_make_dir_recursively(local_abspath, scratch_pool));
else
- SVN_ERR(svn_io_dir_make(path, APR_OS_DEFAULT, pool));
+ SVN_ERR(svn_io_dir_make(local_abspath, APR_OS_DEFAULT, scratch_pool));
- /* Should no longer use svn_depth_empty to indicate that only the directory
- itself is added, since it not only constraints the operation depth, but
- also defines the depth of the target directory now. Moreover, the new
- directory will have no children at all.*/
- err = svn_client_add5(path, svn_depth_infinity, FALSE, FALSE, FALSE,
- make_parents, ctx, pool);
+ err = svn_client_add5(local_abspath, svn_depth_empty, FALSE, FALSE, FALSE,
+ make_parents, ctx, scratch_pool);
/* If we created a new directory, but couldn't add it to version
control, then delete it. */
if (err && (orig_kind == svn_node_none))
{
- /* ### If this returns an error, should we link it onto
- err instead, so that the user is warned that we just
- created an unversioned directory? */
-
- svn_error_clear(svn_io_remove_dir2(path, FALSE, NULL, NULL, pool));
+ err = svn_error_compose_create(err,
+ svn_io_remove_dir2(local_abspath, FALSE,
+ NULL, NULL,
+ scratch_pool));
}
return svn_error_trace(err);
@@ -1303,23 +1256,25 @@ svn_client_mkdir4(const apr_array_header_t *paths,
else
{
/* This is a regular "mkdir" + "svn add" */
- apr_pool_t *subpool = svn_pool_create(pool);
+ apr_pool_t *iterpool = svn_pool_create(pool);
int i;
for (i = 0; i < paths->nelts; i++)
{
const char *path = APR_ARRAY_IDX(paths, i, const char *);
- svn_pool_clear(subpool);
+ svn_pool_clear(iterpool);
/* See if the user wants us to stop. */
if (ctx->cancel_func)
SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
+ SVN_ERR(svn_dirent_get_absolute(&path, path, iterpool));
+
SVN_ERR(svn_client__make_local_parents(path, make_parents, ctx,
- subpool));
+ iterpool));
}
- svn_pool_destroy(subpool);
+ svn_pool_destroy(iterpool);
}
return SVN_NO_ERROR;
diff --git a/subversion/libsvn_client/blame.c b/subversion/libsvn_client/blame.c
index 188fdd2bdc2a..b9363e20dd3e 100644
--- a/subversion/libsvn_client/blame.c
+++ b/subversion/libsvn_client/blame.c
@@ -34,6 +34,7 @@
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_props.h"
+#include "svn_hash.h"
#include "svn_sorts.h"
#include "private/svn_wc_private.h"
@@ -73,16 +74,16 @@ struct diff_baton {
const struct rev *rev;
};
-/* The baton used for a file revision. */
+/* The baton used for a file revision. Lives the entire operation */
struct file_rev_baton {
svn_revnum_t start_rev, end_rev;
+ svn_boolean_t backwards;
const char *target;
svn_client_ctx_t *ctx;
const svn_diff_file_options_t *diff_options;
/* name of file containing the previous revision of the file */
const char *last_filename;
- struct rev *rev; /* the rev for which blame is being assigned
- during a diff */
+ struct rev *last_rev; /* the rev of the last modification */
struct blame_chain *chain; /* the original blame chain. */
const char *repos_root_url; /* To construct a url */
apr_pool_t *mainpool; /* lives during the whole sequence of calls */
@@ -91,22 +92,32 @@ struct file_rev_baton {
/* These are used for tracking merged revisions. */
svn_boolean_t include_merged_revisions;
- svn_boolean_t merged_revision;
struct blame_chain *merged_chain; /* the merged blame chain. */
/* name of file containing the previous merged revision of the file */
const char *last_original_filename;
/* pools for files which may need to persist for more than one rev. */
apr_pool_t *filepool;
apr_pool_t *prevfilepool;
+
+ svn_boolean_t check_mime_type;
+
+ /* When blaming backwards we have to use the changes
+ on the *next* revision, as the interesting change
+ happens when we move to the previous revision */
+ svn_revnum_t last_revnum;
+ apr_hash_t *last_props;
};
-/* The baton used by the txdelta window handler. */
+/* The baton used by the txdelta window handler. Allocated per revision */
struct delta_baton {
/* Our underlying handler/baton that we wrap */
svn_txdelta_window_handler_t wrapped_handler;
void *wrapped_baton;
struct file_rev_baton *file_rev_baton;
+ svn_stream_t *source_stream; /* the delta source */
const char *filename;
+ svn_boolean_t is_merged_revision;
+ struct rev *rev; /* the rev struct for the current revision */
};
@@ -280,6 +291,8 @@ add_file_blame(const char *last_file,
struct blame_chain *chain,
struct rev *rev,
const svn_diff_file_options_t *diff_options,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
apr_pool_t *pool)
{
if (!last_file)
@@ -298,32 +311,28 @@ add_file_blame(const char *last_file,
/* We have a previous file. Get the diff and adjust blame info. */
SVN_ERR(svn_diff_file_diff_2(&diff, last_file, cur_file,
diff_options, pool));
- SVN_ERR(svn_diff_output(diff, &diff_baton, &output_fns));
+ SVN_ERR(svn_diff_output2(diff, &diff_baton, &output_fns,
+ cancel_func, cancel_baton));
}
return SVN_NO_ERROR;
}
-/* The delta window handler for the text delta between the previously seen
- * revision and the revision currently being handled.
- *
- * Record the blame information for this revision in BATON->file_rev_baton.
- *
- * Implements svn_txdelta_window_handler_t.
+/* Record the blame information for the revision in BATON->file_rev_baton.
*/
static svn_error_t *
-window_handler(svn_txdelta_window_t *window, void *baton)
+update_blame(void *baton)
{
struct delta_baton *dbaton = baton;
struct file_rev_baton *frb = dbaton->file_rev_baton;
struct blame_chain *chain;
- /* Call the wrapped handler first. */
- SVN_ERR(dbaton->wrapped_handler(window, dbaton->wrapped_baton));
-
- /* We patiently wait for the NULL window marking the end. */
- if (window)
- return SVN_NO_ERROR;
+ /* Close the source file used for the delta.
+ It is important to do this early, since otherwise, they will be deleted
+ before all handles are closed, which leads to failures on some platforms
+ when new tempfiles are to be created. */
+ if (dbaton->source_stream)
+ SVN_ERR(svn_stream_close(dbaton->source_stream));
/* If we are including merged revisions, we need to add each rev to the
merged chain. */
@@ -334,19 +343,23 @@ window_handler(svn_txdelta_window_t *window, void *baton)
/* Process this file. */
SVN_ERR(add_file_blame(frb->last_filename,
- dbaton->filename, chain, frb->rev,
- frb->diff_options, frb->currpool));
+ dbaton->filename, chain, dbaton->rev,
+ frb->diff_options,
+ frb->ctx->cancel_func, frb->ctx->cancel_baton,
+ frb->currpool));
/* If we are including merged revisions, and the current revision is not a
merged one, we need to add its blame info to the chain for the original
line of history. */
- if (frb->include_merged_revisions && ! frb->merged_revision)
+ if (frb->include_merged_revisions && ! dbaton->is_merged_revision)
{
apr_pool_t *tmppool;
SVN_ERR(add_file_blame(frb->last_original_filename,
- dbaton->filename, frb->chain, frb->rev,
- frb->diff_options, frb->currpool));
+ dbaton->filename, frb->chain, dbaton->rev,
+ frb->diff_options,
+ frb->ctx->cancel_func, frb->ctx->cancel_baton,
+ frb->currpool));
/* This filename could be around for a while, potentially, so
use the longer lifetime pool, and switch it with the previous one*/
@@ -374,6 +387,32 @@ window_handler(svn_txdelta_window_t *window, void *baton)
return SVN_NO_ERROR;
}
+/* The delta window handler for the text delta between the previously seen
+ * revision and the revision currently being handled.
+ *
+ * Record the blame information for this revision in BATON->file_rev_baton.
+ *
+ * Implements svn_txdelta_window_handler_t.
+ */
+static svn_error_t *
+window_handler(svn_txdelta_window_t *window, void *baton)
+{
+ struct delta_baton *dbaton = baton;
+
+ /* Call the wrapped handler first. */
+ if (dbaton->wrapped_handler)
+ SVN_ERR(dbaton->wrapped_handler(window, dbaton->wrapped_baton));
+
+ /* We patiently wait for the NULL window marking the end. */
+ if (window)
+ return SVN_NO_ERROR;
+
+ /* Diff and update blame info. */
+ SVN_ERR(update_blame(baton));
+
+ return SVN_NO_ERROR;
+}
+
/* Calculate and record blame information for one revision of the file,
* by comparing the file content against the previously seen revision.
@@ -402,6 +441,26 @@ file_rev_handler(void *baton, const char *path, svn_revnum_t revnum,
/* Clear the current pool. */
svn_pool_clear(frb->currpool);
+ if (frb->check_mime_type)
+ {
+ apr_hash_t *props = svn_prop_array_to_hash(prop_diffs, frb->currpool);
+ const char *value;
+
+ frb->check_mime_type = FALSE; /* Only check first */
+
+ value = svn_prop_get_value(props, SVN_PROP_MIME_TYPE);
+
+ if (value && svn_mime_type_is_binary(value))
+ {
+ return svn_error_createf(
+ SVN_ERR_CLIENT_IS_BINARY_FILE, NULL,
+ _("Cannot calculate blame information for binary file '%s'"),
+ (svn_path_is_url(frb->target)
+ ? frb->target
+ : svn_dirent_local_style(frb->target, pool)));
+ }
+ }
+
if (frb->ctx->notify_func2)
{
svn_wc_notify_t *notify
@@ -422,72 +481,112 @@ file_rev_handler(void *baton, const char *path, svn_revnum_t revnum,
if (frb->ctx->cancel_func)
SVN_ERR(frb->ctx->cancel_func(frb->ctx->cancel_baton));
- /* If there were no content changes, we couldn't care less about this
- revision now. Note that we checked the mime type above, so things
- work if the user just changes the mime type in a commit.
+ /* If there were no content changes and no (potential) merges, we couldn't
+ care less about this revision now. Note that we checked the mime type
+ above, so things work if the user just changes the mime type in a commit.
Also note that we don't switch the pools in this case. This is important,
since the tempfile will be removed by the pool and we need the tempfile
from the last revision with content changes. */
- if (!content_delta_handler)
+ if (!content_delta_handler
+ && (!frb->include_merged_revisions || merged_revision))
return SVN_NO_ERROR;
- frb->merged_revision = merged_revision;
-
/* Create delta baton. */
- delta_baton = apr_palloc(frb->currpool, sizeof(*delta_baton));
+ delta_baton = apr_pcalloc(frb->currpool, sizeof(*delta_baton));
/* Prepare the text delta window handler. */
if (frb->last_filename)
- SVN_ERR(svn_stream_open_readonly(&last_stream, frb->last_filename,
+ SVN_ERR(svn_stream_open_readonly(&delta_baton->source_stream, frb->last_filename,
frb->currpool, pool));
else
- last_stream = svn_stream_empty(frb->currpool);
+ /* Means empty stream below. */
+ delta_baton->source_stream = NULL;
+ last_stream = svn_stream_disown(delta_baton->source_stream, pool);
- if (frb->include_merged_revisions && !frb->merged_revision)
+ if (frb->include_merged_revisions && !merged_revision)
filepool = frb->filepool;
else
filepool = frb->currpool;
SVN_ERR(svn_stream_open_unique(&cur_stream, &delta_baton->filename, NULL,
svn_io_file_del_on_pool_cleanup,
- filepool, pool));
-
- /* Get window handler for applying delta. */
- svn_txdelta_apply(last_stream, cur_stream, NULL, NULL,
- frb->currpool,
- &delta_baton->wrapped_handler,
- &delta_baton->wrapped_baton);
+ filepool, filepool));
/* Wrap the window handler with our own. */
delta_baton->file_rev_baton = frb;
- *content_delta_handler = window_handler;
- *content_delta_baton = delta_baton;
+ delta_baton->is_merged_revision = merged_revision;
/* Create the rev structure. */
- frb->rev = apr_pcalloc(frb->mainpool, sizeof(struct rev));
+ delta_baton->rev = apr_pcalloc(frb->mainpool, sizeof(struct rev));
- if (revnum < frb->start_rev)
+ if (frb->backwards)
{
- /* We shouldn't get more than one revision before the starting
- revision (unless of including merged revisions). */
- SVN_ERR_ASSERT((frb->last_filename == NULL)
- || frb->include_merged_revisions);
+ /* Use from last round...
+ SVN_INVALID_REVNUM on first, which is exactly
+ what we want */
+ delta_baton->rev->revision = frb->last_revnum;
+ delta_baton->rev->rev_props = frb->last_props;
+
+ /* Store for next delta */
+ if (revnum >= MIN(frb->start_rev, frb->end_rev))
+ {
+ frb->last_revnum = revnum;
+ frb->last_props = svn_prop_hash_dup(rev_props, frb->mainpool);
+ }
+ /* Else: Not needed on last rev */
+ }
+ else if (merged_revision
+ || (revnum >= MIN(frb->start_rev, frb->end_rev)))
+ {
+ /* 1+ for the "youngest to oldest" blame */
+ SVN_ERR_ASSERT(revnum <= 1 + MAX(frb->end_rev, frb->start_rev));
- /* The file existed before start_rev; generate no blame info for
- lines from this revision (or before). */
- frb->rev->revision = SVN_INVALID_REVNUM;
+ /* Set values from revision props. */
+ delta_baton->rev->revision = revnum;
+ delta_baton->rev->rev_props = svn_prop_hash_dup(rev_props, frb->mainpool);
}
else
{
- SVN_ERR_ASSERT(revnum <= frb->end_rev);
+ /* We shouldn't get more than one revision outside the
+ specified range (unless we alsoe receive merged revisions) */
+ SVN_ERR_ASSERT((frb->last_filename == NULL)
+ || frb->include_merged_revisions);
- /* Set values from revision props. */
- frb->rev->revision = revnum;
- frb->rev->rev_props = svn_prop_hash_dup(rev_props, frb->mainpool);
+ /* The file existed before start_rev; generate no blame info for
+ lines from this revision (or before).
+
+ This revision specifies the state as it was at the start revision */
+
+ delta_baton->rev->revision = SVN_INVALID_REVNUM;
}
if (frb->include_merged_revisions)
- frb->rev->path = apr_pstrdup(frb->mainpool, path);
+ delta_baton->rev->path = apr_pstrdup(frb->mainpool, path);
+
+ /* Keep last revision for postprocessing after all changes */
+ frb->last_rev = delta_baton->rev;
+
+ /* Handle all delta - even if it is empty.
+ We must do the latter to "merge" blame info from other branches. */
+ if (content_delta_handler)
+ {
+ /* Proper delta - get window handler for applying delta.
+ svn_ra_get_file_revs2 will drive the delta editor. */
+ svn_txdelta_apply(last_stream, cur_stream, NULL, NULL,
+ frb->currpool,
+ &delta_baton->wrapped_handler,
+ &delta_baton->wrapped_baton);
+ *content_delta_handler = window_handler;
+ *content_delta_baton = delta_baton;
+ }
+ else
+ {
+ /* Apply an empty delta, i.e. simply copy the old contents.
+ We can't simply use the existing file due to the pool rotation logic.
+ Trigger the blame update magic. */
+ SVN_ERR(svn_stream_copy3(last_stream, cur_stream, NULL, NULL, pool));
+ SVN_ERR(update_blame(delta_baton));
+ }
return SVN_NO_ERROR;
}
@@ -572,7 +671,6 @@ svn_client_blame5(const char *target,
struct file_rev_baton frb;
svn_ra_session_t *ra_session;
svn_revnum_t start_revnum, end_revnum;
- svn_client__pathrev_t *end_loc;
struct blame *walk, *walk_merged = NULL;
apr_pool_t *iterpool;
svn_stream_t *last_stream;
@@ -590,44 +688,77 @@ svn_client_blame5(const char *target,
SVN_ERR(svn_dirent_get_absolute(&target_abspath_or_url, target, pool));
/* Get an RA plugin for this filesystem object. */
- SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &end_loc,
- target, NULL, peg_revision, end,
+ SVN_ERR(svn_client__ra_session_from_path2(&ra_session, NULL,
+ target, NULL, peg_revision,
+ peg_revision,
ctx, pool));
- end_revnum = end_loc->rev;
SVN_ERR(svn_client__get_revision_number(&start_revnum, NULL, ctx->wc_ctx,
target_abspath_or_url, ra_session,
start, pool));
- if (end_revnum < start_revnum)
- return svn_error_create
- (SVN_ERR_CLIENT_BAD_REVISION, NULL,
- _("Start revision must precede end revision"));
+ SVN_ERR(svn_client__get_revision_number(&end_revnum, NULL, ctx->wc_ctx,
+ target_abspath_or_url, ra_session,
+ end, pool));
+
+ {
+ svn_client__pathrev_t *loc;
+ svn_opt_revision_t younger_end;
+ younger_end.kind = svn_opt_revision_number;
+ younger_end.value.number = MAX(start_revnum, end_revnum);
+
+ SVN_ERR(svn_client__resolve_rev_and_url(&loc, ra_session,
+ target, peg_revision,
+ &younger_end,
+ ctx, pool));
+
+ /* Make the session point to the real URL. */
+ SVN_ERR(svn_ra_reparent(ra_session, loc->url, pool));
+ }
/* We check the mime-type of the yougest revision before getting all
the older revisions. */
- if (!ignore_mime_type)
+ if (!ignore_mime_type
+ && start_revnum < end_revnum)
{
apr_hash_t *props;
- apr_hash_index_t *hi;
+ const char *mime_type = NULL;
- SVN_ERR(svn_client_propget5(&props, NULL, SVN_PROP_MIME_TYPE,
- target_abspath_or_url, peg_revision,
- end, NULL, svn_depth_empty, NULL, ctx,
- pool, pool));
+ if (svn_path_is_url(target)
+ || start_revnum > end_revnum
+ || (end->kind != svn_opt_revision_working
+ && end->kind != svn_opt_revision_base))
+ {
+ SVN_ERR(svn_ra_get_file(ra_session, "", end_revnum, NULL, NULL,
+ &props, pool));
- /* props could be keyed on URLs or paths depending on the
- peg_revision and end values so avoid using the key. */
- hi = apr_hash_first(pool, props);
- if (hi)
+ mime_type = svn_prop_get_value(props, SVN_PROP_MIME_TYPE);
+ }
+ else
{
- svn_string_t *value;
+ const svn_string_t *value;
+
+ if (end->kind == svn_opt_revision_working)
+ SVN_ERR(svn_wc_prop_get2(&value, ctx->wc_ctx,
+ target_abspath_or_url,
+ SVN_PROP_MIME_TYPE,
+ pool, pool));
+ else
+ {
+ SVN_ERR(svn_wc_get_pristine_props(&props, ctx->wc_ctx,
+ target_abspath_or_url,
+ pool, pool));
- /* Should only be one value */
- SVN_ERR_ASSERT(apr_hash_count(props) == 1);
+ value = props ? svn_hash_gets(props, SVN_PROP_MIME_TYPE)
+ : NULL;
+ }
- value = svn__apr_hash_index_val(hi);
- if (value && svn_mime_type_is_binary(value->data))
+ mime_type = value ? value->data : NULL;
+ }
+
+ if (mime_type)
+ {
+ if (svn_mime_type_is_binary(mime_type))
return svn_error_createf
(SVN_ERR_CLIENT_IS_BINARY_FILE, 0,
_("Cannot calculate blame information for binary file '%s'"),
@@ -643,6 +774,7 @@ svn_client_blame5(const char *target,
frb.diff_options = diff_options;
frb.include_merged_revisions = include_merged_revisions;
frb.last_filename = NULL;
+ frb.last_rev = NULL;
frb.last_original_filename = NULL;
frb.chain = apr_palloc(pool, sizeof(*frb.chain));
frb.chain->blame = NULL;
@@ -655,6 +787,10 @@ svn_client_blame5(const char *target,
frb.merged_chain->avail = NULL;
frb.merged_chain->pool = pool;
}
+ frb.backwards = (frb.start_rev > frb.end_rev);
+ frb.last_revnum = SVN_INVALID_REVNUM;
+ frb.last_props = NULL;
+ frb.check_mime_type = (frb.backwards && !ignore_mime_type);
SVN_ERR(svn_ra_get_repos_root2(ra_session, &frb.repos_root_url, pool));
@@ -675,8 +811,10 @@ svn_client_blame5(const char *target,
if available so that we can know what was actually changed in the start
revision. */
SVN_ERR(svn_ra_get_file_revs2(ra_session, "",
- start_revnum - (start_revnum > 0 ? 1 : 0),
- end_revnum, include_merged_revisions,
+ frb.backwards ? start_revnum
+ : MAX(0, start_revnum-1),
+ end_revnum,
+ include_merged_revisions,
file_rev_handler, &frb, pool));
if (end->kind == svn_opt_revision_working)
@@ -732,7 +870,8 @@ svn_client_blame5(const char *target,
ctx->cancel_baton, pool));
SVN_ERR(add_file_blame(frb.last_filename, temppath, frb.chain, NULL,
- frb.diff_options, pool));
+ frb.diff_options,
+ ctx->cancel_func, ctx->cancel_baton, pool));
frb.last_filename = temppath;
}
@@ -762,7 +901,7 @@ svn_client_blame5(const char *target,
the most recently changed revision. ### Is this really what we want
to do here? Do the sematics of copy change? */
if (!frb.chain->blame)
- frb.chain->blame = blame_create(frb.chain, frb.rev, 0);
+ frb.chain->blame = blame_create(frb.chain, frb.last_rev, 0);
normalize_blames(frb.chain, frb.merged_chain, pool);
walk_merged = frb.merged_chain->blame;
diff --git a/subversion/libsvn_client/cat.c b/subversion/libsvn_client/cat.c
index 7c58f884880e..8c6aac85a591 100644
--- a/subversion/libsvn_client/cat.c
+++ b/subversion/libsvn_client/cat.c
@@ -176,18 +176,21 @@ svn_client__get_normalized_stream(svn_stream_t **normal_stream,
}
svn_error_t *
-svn_client_cat2(svn_stream_t *out,
+svn_client_cat3(apr_hash_t **returned_props,
+ svn_stream_t *out,
const char *path_or_url,
const svn_opt_revision_t *peg_revision,
const svn_opt_revision_t *revision,
+ svn_boolean_t expand_keywords,
svn_client_ctx_t *ctx,
- apr_pool_t *pool)
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
svn_ra_session_t *ra_session;
svn_client__pathrev_t *loc;
svn_string_t *eol_style;
svn_string_t *keywords;
- apr_hash_t *props;
+ apr_hash_t *props = NULL;
const char *repos_root_url;
svn_stream_t *output = out;
svn_error_t *err;
@@ -201,8 +204,6 @@ svn_client_cat2(svn_stream_t *out,
}
else
{
- peg_revision = svn_cl__rev_default_to_head_or_working(peg_revision,
- path_or_url);
revision = svn_cl__rev_default_to_peg(revision, peg_revision);
}
@@ -213,32 +214,39 @@ svn_client_cat2(svn_stream_t *out,
const char *local_abspath;
svn_stream_t *normal_stream;
- SVN_ERR(svn_dirent_get_absolute(&local_abspath, path_or_url, pool));
+ SVN_ERR(svn_dirent_get_absolute(&local_abspath, path_or_url,
+ scratch_pool));
SVN_ERR(svn_client__get_normalized_stream(&normal_stream, ctx->wc_ctx,
- local_abspath, revision, TRUE, FALSE,
+ local_abspath, revision,
+ expand_keywords, FALSE,
ctx->cancel_func, ctx->cancel_baton,
- pool, pool));
+ scratch_pool, scratch_pool));
/* We don't promise to close output, so disown it to ensure we don't. */
- output = svn_stream_disown(output, pool);
+ output = svn_stream_disown(output, scratch_pool);
+
+ if (returned_props)
+ SVN_ERR(svn_wc_prop_list2(returned_props, ctx->wc_ctx, local_abspath,
+ result_pool, scratch_pool));
return svn_error_trace(svn_stream_copy3(normal_stream, output,
ctx->cancel_func,
- ctx->cancel_baton, pool));
+ ctx->cancel_baton, scratch_pool));
}
/* Get an RA plugin for this filesystem object. */
SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &loc,
path_or_url, NULL,
peg_revision,
- revision, ctx, pool));
+ revision, ctx, scratch_pool));
/* Find the repos root URL */
- SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url, pool));
+ SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url, scratch_pool));
/* Grab some properties we need to know in order to figure out if anything
special needs to be done with this file. */
- err = svn_ra_get_file(ra_session, "", loc->rev, NULL, NULL, &props, pool);
+ err = svn_ra_get_file(ra_session, "", loc->rev, NULL, NULL, &props,
+ result_pool);
if (err)
{
if (err->apr_err == SVN_ERR_FS_NOT_FILE)
@@ -272,7 +280,7 @@ svn_client_cat2(svn_stream_t *out,
}
- if (keywords)
+ if (keywords && expand_keywords)
{
svn_string_t *cmt_rev, *cmt_date, *cmt_author;
apr_time_t when = 0;
@@ -281,24 +289,45 @@ svn_client_cat2(svn_stream_t *out,
cmt_date = svn_hash_gets(props, SVN_PROP_ENTRY_COMMITTED_DATE);
cmt_author = svn_hash_gets(props, SVN_PROP_ENTRY_LAST_AUTHOR);
if (cmt_date)
- SVN_ERR(svn_time_from_cstring(&when, cmt_date->data, pool));
+ SVN_ERR(svn_time_from_cstring(&when, cmt_date->data, scratch_pool));
SVN_ERR(svn_subst_build_keywords3(&kw, keywords->data,
cmt_rev->data, loc->url,
repos_root_url, when,
cmt_author ?
cmt_author->data : NULL,
- pool));
+ scratch_pool));
}
else
kw = NULL;
/* Interject a translating stream */
- output = svn_subst_stream_translated(svn_stream_disown(out, pool),
- eol_str, FALSE, kw, TRUE, pool);
+ output = svn_subst_stream_translated(svn_stream_disown(out,
+ scratch_pool),
+ eol_str, FALSE, kw, TRUE,
+ scratch_pool);
+ }
+
+ if (returned_props)
+ {
+ /* filter entry and WC props */
+ apr_hash_index_t *hi;
+ const void *key;
+ apr_ssize_t klen;
+
+ for (hi = apr_hash_first(scratch_pool, props);
+ hi; hi = apr_hash_next(hi))
+ {
+ apr_hash_this(hi, &key, &klen, NULL);
+ if (!svn_wc_is_normal_prop(key))
+ apr_hash_set(props, key, klen, NULL);
+ }
+
+ *returned_props = props;
}
- SVN_ERR(svn_ra_get_file(ra_session, "", loc->rev, output, NULL, NULL, pool));
+ SVN_ERR(svn_ra_get_file(ra_session, "", loc->rev, output, NULL, NULL,
+ scratch_pool));
if (out != output)
/* Close the interjected stream */
diff --git a/subversion/libsvn_client/checkout.c b/subversion/libsvn_client/checkout.c
index 41be776f16e2..0d20e24e117a 100644
--- a/subversion/libsvn_client/checkout.c
+++ b/subversion/libsvn_client/checkout.c
@@ -67,6 +67,7 @@ initialize_area(const char *local_abspath,
svn_error_t *
svn_client__checkout_internal(svn_revnum_t *result_rev,
+ svn_boolean_t *timestamp_sleep,
const char *url,
const char *local_abspath,
const svn_opt_revision_t *peg_revision,
@@ -74,18 +75,16 @@ svn_client__checkout_internal(svn_revnum_t *result_rev,
svn_depth_t depth,
svn_boolean_t ignore_externals,
svn_boolean_t allow_unver_obstructions,
- svn_boolean_t *timestamp_sleep,
+ svn_ra_session_t *ra_session,
svn_client_ctx_t *ctx,
- apr_pool_t *pool)
+ apr_pool_t *scratch_pool)
{
svn_node_kind_t kind;
- apr_pool_t *session_pool = svn_pool_create(pool);
- svn_ra_session_t *ra_session;
svn_client__pathrev_t *pathrev;
/* Sanity check. Without these, the checkout is meaningless. */
SVN_ERR_ASSERT(local_abspath != NULL);
- SVN_ERR_ASSERT(svn_uri_is_canonical(url, pool));
+ SVN_ERR_ASSERT(svn_uri_is_canonical(url, scratch_pool));
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
/* Fulfill the docstring promise of svn_client_checkout: */
@@ -94,15 +93,38 @@ svn_client__checkout_internal(svn_revnum_t *result_rev,
&& (revision->kind != svn_opt_revision_head))
return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL);
- /* Get the RA connection. */
- SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &pathrev,
- url, NULL, peg_revision, revision,
- ctx, session_pool));
+ /* Get the RA connection, if needed. */
+ if (ra_session)
+ {
+ svn_error_t *err = svn_ra_reparent(ra_session, url, scratch_pool);
+
+ if (err)
+ {
+ if (err->apr_err == SVN_ERR_RA_ILLEGAL_URL)
+ {
+ svn_error_clear(err);
+ ra_session = NULL;
+ }
+ else
+ return svn_error_trace(err);
+ }
+ else
+ {
+ SVN_ERR(svn_client__resolve_rev_and_url(&pathrev,
+ ra_session, url,
+ peg_revision, revision,
+ ctx, scratch_pool));
+ }
+ }
- pathrev = svn_client__pathrev_dup(pathrev, pool);
- SVN_ERR(svn_ra_check_path(ra_session, "", pathrev->rev, &kind, pool));
+ if (!ra_session)
+ {
+ SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &pathrev,
+ url, NULL, peg_revision,
+ revision, ctx, scratch_pool));
+ }
- svn_pool_destroy(session_pool);
+ SVN_ERR(svn_ra_check_path(ra_session, "", pathrev->rev, &kind, scratch_pool));
if (kind == svn_node_none)
return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
@@ -112,31 +134,35 @@ svn_client__checkout_internal(svn_revnum_t *result_rev,
(SVN_ERR_UNSUPPORTED_FEATURE , NULL,
_("URL '%s' refers to a file, not a directory"), pathrev->url);
- SVN_ERR(svn_io_check_path(local_abspath, &kind, pool));
+ SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool));
if (kind == svn_node_none)
{
/* Bootstrap: create an incomplete working-copy root dir. Its
entries file should only have an entry for THIS_DIR with a
URL, revnum, and an 'incomplete' flag. */
- SVN_ERR(svn_io_make_dir_recursively(local_abspath, pool));
- SVN_ERR(initialize_area(local_abspath, pathrev, depth, ctx, pool));
+ SVN_ERR(svn_io_make_dir_recursively(local_abspath, scratch_pool));
+ SVN_ERR(initialize_area(local_abspath, pathrev, depth, ctx,
+ scratch_pool));
}
else if (kind == svn_node_dir)
{
int wc_format;
const char *entry_url;
- SVN_ERR(svn_wc_check_wc2(&wc_format, ctx->wc_ctx, local_abspath, pool));
+ SVN_ERR(svn_wc_check_wc2(&wc_format, ctx->wc_ctx, local_abspath,
+ scratch_pool));
+
if (! wc_format)
{
- SVN_ERR(initialize_area(local_abspath, pathrev, depth, ctx, pool));
+ SVN_ERR(initialize_area(local_abspath, pathrev, depth, ctx,
+ scratch_pool));
}
else
{
/* Get PATH's URL. */
SVN_ERR(svn_wc__node_get_url(&entry_url, ctx->wc_ctx, local_abspath,
- pool, pool));
+ scratch_pool, scratch_pool));
/* If PATH's existing URL matches the incoming one, then
just update. This allows 'svn co' to restart an
@@ -146,24 +172,25 @@ svn_client__checkout_internal(svn_revnum_t *result_rev,
SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
_("'%s' is already a working copy for a"
" different URL"),
- svn_dirent_local_style(local_abspath, pool));
+ svn_dirent_local_style(local_abspath, scratch_pool));
}
}
else
{
return svn_error_createf(SVN_ERR_WC_NODE_KIND_CHANGE, NULL,
_("'%s' already exists and is not a directory"),
- svn_dirent_local_style(local_abspath, pool));
+ svn_dirent_local_style(local_abspath,
+ scratch_pool));
}
/* Have update fix the incompleteness. */
- SVN_ERR(svn_client__update_internal(result_rev, local_abspath,
- revision, depth, TRUE,
+ SVN_ERR(svn_client__update_internal(result_rev, timestamp_sleep,
+ local_abspath, revision, depth, TRUE,
ignore_externals,
allow_unver_obstructions,
TRUE /* adds_as_modification */,
- FALSE, FALSE,
- timestamp_sleep, ctx, pool));
+ FALSE, FALSE, ra_session,
+ ctx, scratch_pool));
return SVN_NO_ERROR;
}
@@ -186,10 +213,12 @@ svn_client_checkout3(svn_revnum_t *result_rev,
SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, pool));
- err = svn_client__checkout_internal(result_rev, URL, local_abspath,
+ err = svn_client__checkout_internal(result_rev, &sleep_here,
+ URL, local_abspath,
peg_revision, revision, depth,
ignore_externals,
- allow_unver_obstructions, &sleep_here,
+ allow_unver_obstructions,
+ NULL /* ra_session */,
ctx, pool);
if (sleep_here)
svn_io_sleep_for_timestamps(local_abspath, pool);
diff --git a/subversion/libsvn_client/cleanup.c b/subversion/libsvn_client/cleanup.c
index b15e824d3490..e3fffdcaf0db 100644
--- a/subversion/libsvn_client/cleanup.c
+++ b/subversion/libsvn_client/cleanup.c
@@ -32,32 +32,234 @@
#include "svn_client.h"
#include "svn_config.h"
#include "svn_dirent_uri.h"
+#include "svn_hash.h"
#include "svn_path.h"
#include "svn_pools.h"
#include "client.h"
#include "svn_props.h"
#include "svn_private_config.h"
+#include "private/svn_wc_private.h"
/*** Code. ***/
+struct cleanup_status_walk_baton
+{
+ svn_boolean_t break_locks;
+ svn_boolean_t fix_timestamps;
+ svn_boolean_t clear_dav_cache;
+ svn_boolean_t vacuum_pristines;
+ svn_boolean_t remove_unversioned_items;
+ svn_boolean_t remove_ignored_items;
+ svn_boolean_t include_externals;
+ svn_client_ctx_t *ctx;
+};
+
+/* Forward declararion. */
+static svn_error_t *
+cleanup_status_walk(void *baton,
+ const char *local_abspath,
+ const svn_wc_status3_t *status,
+ apr_pool_t *scratch_pool);
+
+static svn_error_t *
+do_cleanup(const char *local_abspath,
+ svn_boolean_t break_locks,
+ svn_boolean_t fix_timestamps,
+ svn_boolean_t clear_dav_cache,
+ svn_boolean_t vacuum_pristines,
+ svn_boolean_t remove_unversioned_items,
+ svn_boolean_t remove_ignored_items,
+ svn_boolean_t include_externals,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ SVN_ERR(svn_wc_cleanup4(ctx->wc_ctx,
+ local_abspath,
+ break_locks,
+ fix_timestamps,
+ clear_dav_cache,
+ vacuum_pristines,
+ ctx->cancel_func, ctx->cancel_baton,
+ ctx->notify_func2, ctx->notify_baton2,
+ scratch_pool));
+
+ if (fix_timestamps)
+ svn_io_sleep_for_timestamps(local_abspath, scratch_pool);
+
+ if (remove_unversioned_items || remove_ignored_items || include_externals)
+ {
+ struct cleanup_status_walk_baton b;
+ apr_array_header_t *ignores;
+
+ b.break_locks = break_locks;
+ b.fix_timestamps = fix_timestamps;
+ b.clear_dav_cache = clear_dav_cache;
+ b.vacuum_pristines = vacuum_pristines;
+ b.remove_unversioned_items = remove_unversioned_items;
+ b.remove_ignored_items = remove_ignored_items;
+ b.include_externals = include_externals;
+ b.ctx = ctx;
+
+ SVN_ERR(svn_wc_get_default_ignores(&ignores, ctx->config, scratch_pool));
+
+ SVN_WC__CALL_WITH_WRITE_LOCK(
+ svn_wc_walk_status(ctx->wc_ctx, local_abspath,
+ svn_depth_infinity,
+ TRUE, /* get all */
+ remove_ignored_items,
+ TRUE, /* ignore textmods */
+ ignores,
+ cleanup_status_walk, &b,
+ ctx->cancel_func,
+ ctx->cancel_baton,
+ scratch_pool),
+ ctx->wc_ctx,
+ local_abspath,
+ FALSE /* lock_anchor */,
+ scratch_pool);
+ }
+
+ return SVN_NO_ERROR;
+}
+
+
+/* An implementation of svn_wc_status_func4_t. */
+static svn_error_t *
+cleanup_status_walk(void *baton,
+ const char *local_abspath,
+ const svn_wc_status3_t *status,
+ apr_pool_t *scratch_pool)
+{
+ struct cleanup_status_walk_baton *b = baton;
+ svn_node_kind_t kind_on_disk;
+ svn_wc_notify_t *notify;
+
+ if (status->node_status == svn_wc_status_external && b->include_externals)
+ {
+ svn_error_t *err;
+
+ SVN_ERR(svn_io_check_path(local_abspath, &kind_on_disk, scratch_pool));
+ if (kind_on_disk == svn_node_dir)
+ {
+ if (b->ctx->notify_func2)
+ {
+ notify = svn_wc_create_notify(local_abspath,
+ svn_wc_notify_cleanup_external,
+ scratch_pool);
+ b->ctx->notify_func2(b->ctx->notify_baton2, notify,
+ scratch_pool);
+ }
+
+ err = do_cleanup(local_abspath,
+ b->break_locks,
+ b->fix_timestamps,
+ b->clear_dav_cache,
+ b->vacuum_pristines,
+ b->remove_unversioned_items,
+ b->remove_ignored_items,
+ TRUE /* include_externals */,
+ b->ctx, scratch_pool);
+ if (err && err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY)
+ {
+ svn_error_clear(err);
+ return SVN_NO_ERROR;
+ }
+ else
+ SVN_ERR(err);
+ }
+
+ return SVN_NO_ERROR;
+ }
+
+ if (status->node_status == svn_wc_status_ignored)
+ {
+ if (!b->remove_ignored_items)
+ return SVN_NO_ERROR;
+ }
+ else if (status->node_status == svn_wc_status_unversioned)
+ {
+ if (!b->remove_unversioned_items)
+ return SVN_NO_ERROR;
+ }
+ else
+ return SVN_NO_ERROR;
+
+ SVN_ERR(svn_io_check_path(local_abspath, &kind_on_disk, scratch_pool));
+ switch (kind_on_disk)
+ {
+ case svn_node_file:
+ case svn_node_symlink:
+ SVN_ERR(svn_io_remove_file2(local_abspath, FALSE, scratch_pool));
+ break;
+ case svn_node_dir:
+ SVN_ERR(svn_io_remove_dir2(local_abspath, FALSE,
+ b->ctx->cancel_func, b->ctx->cancel_baton,
+ scratch_pool));
+ break;
+ case svn_node_none:
+ default:
+ return SVN_NO_ERROR;
+ }
+
+ if (b->ctx->notify_func2)
+ {
+ notify = svn_wc_create_notify(local_abspath, svn_wc_notify_delete,
+ scratch_pool);
+ notify->kind = kind_on_disk;
+ b->ctx->notify_func2(b->ctx->notify_baton2, notify, scratch_pool);
+ }
+
+ return SVN_NO_ERROR;
+}
+
svn_error_t *
-svn_client_cleanup(const char *path,
- svn_client_ctx_t *ctx,
- apr_pool_t *scratch_pool)
+svn_client_cleanup2(const char *dir_abspath,
+ svn_boolean_t break_locks,
+ svn_boolean_t fix_recorded_timestamps,
+ svn_boolean_t clear_dav_cache,
+ svn_boolean_t vacuum_pristines,
+ svn_boolean_t include_externals,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
{
- const char *local_abspath;
- svn_error_t *err;
+ SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
- if (svn_path_is_url(path))
- return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
- _("'%s' is not a local path"), path);
+ SVN_ERR(do_cleanup(dir_abspath,
+ break_locks,
+ fix_recorded_timestamps,
+ clear_dav_cache,
+ vacuum_pristines,
+ FALSE /* remove_unversioned_items */,
+ FALSE /* remove_ignored_items */,
+ include_externals,
+ ctx, scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client_vacuum(const char *dir_abspath,
+ svn_boolean_t remove_unversioned_items,
+ svn_boolean_t remove_ignored_items,
+ svn_boolean_t fix_recorded_timestamps,
+ svn_boolean_t vacuum_pristines,
+ svn_boolean_t include_externals,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
- SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool));
+ SVN_ERR(do_cleanup(dir_abspath,
+ FALSE /* break_locks */,
+ fix_recorded_timestamps,
+ FALSE /* clear_dav_cache */,
+ vacuum_pristines,
+ remove_unversioned_items,
+ remove_ignored_items,
+ include_externals,
+ ctx, scratch_pool));
- err = svn_wc_cleanup3(ctx->wc_ctx, local_abspath, ctx->cancel_func,
- ctx->cancel_baton, scratch_pool);
- svn_io_sleep_for_timestamps(path, scratch_pool);
- return svn_error_trace(err);
+ return SVN_NO_ERROR;
}
diff --git a/subversion/libsvn_client/client.h b/subversion/libsvn_client/client.h
index f13688828744..58354cf9b2c4 100644
--- a/subversion/libsvn_client/client.h
+++ b/subversion/libsvn_client/client.h
@@ -45,60 +45,48 @@
extern "C" {
#endif /* __cplusplus */
-/* Set *REVNUM to the revision number identified by REVISION.
-
- If REVISION->kind is svn_opt_revision_number, just use
- REVISION->value.number, ignoring LOCAL_ABSPATH and RA_SESSION.
-
- Else if REVISION->kind is svn_opt_revision_committed,
- svn_opt_revision_previous, or svn_opt_revision_base, or
- svn_opt_revision_working, then the revision can be identified
- purely based on the working copy's administrative information for
- LOCAL_ABSPATH, so RA_SESSION is ignored. If LOCAL_ABSPATH is not
- under revision control, return SVN_ERR_UNVERSIONED_RESOURCE, or if
- LOCAL_ABSPATH is null, return SVN_ERR_CLIENT_VERSIONED_PATH_REQUIRED.
-
- Else if REVISION->kind is svn_opt_revision_date or
- svn_opt_revision_head, then RA_SESSION is used to retrieve the
- revision from the repository (using REVISION->value.date in the
- former case), and LOCAL_ABSPATH is ignored. If RA_SESSION is null,
- return SVN_ERR_CLIENT_RA_ACCESS_REQUIRED.
-
- Else if REVISION->kind is svn_opt_revision_unspecified, set
- *REVNUM to SVN_INVALID_REVNUM.
-
- If YOUNGEST_REV is non-NULL, it is an in/out parameter. If
- *YOUNGEST_REV is valid, use it as the youngest revision in the
- repository (regardless of reality) -- don't bother to lookup the
- true value for HEAD, and don't return any value in *REVNUM greater
- than *YOUNGEST_REV. If *YOUNGEST_REV is not valid, and a HEAD
- lookup is required to populate *REVNUM, then also populate
- *YOUNGEST_REV with the result. This is useful for making multiple
- serialized calls to this function with a basically static view of
- the repository, avoiding race conditions which could occur between
- multiple invocations with HEAD lookup requests.
-
- Else return SVN_ERR_CLIENT_BAD_REVISION.
-
- Use SCRATCH_POOL for any temporary allocation. */
-svn_error_t *
-svn_client__get_revision_number(svn_revnum_t *revnum,
- svn_revnum_t *youngest_rev,
- svn_wc_context_t *wc_ctx,
- const char *local_abspath,
- svn_ra_session_t *ra_session,
- const svn_opt_revision_t *revision,
- apr_pool_t *scratch_pool);
+
+/* Private client context.
+ *
+ * This is what is actually allocated by svn_client_create_context2(),
+ * which then returns the address of the public_ctx member. */
+typedef struct svn_client__private_ctx_t
+{
+ /* Reserved field, always zero, to detect misuse of the private
+ context as a public client context. */
+ apr_uint64_t magic_null;
+
+ /* Reserved field, always set to a known magic number, to identify
+ this struct as the private client context. */
+ apr_uint64_t magic_id;
+
+ /* Total number of bytes transferred over network across all RA sessions. */
+ apr_off_t total_progress;
+
+ /* The public context. */
+ svn_client_ctx_t public_ctx;
+} svn_client__private_ctx_t;
+
+
+/* Given a public client context CTX, return the private context
+ within which it is allocated. */
+svn_client__private_ctx_t *
+svn_client__get_private_ctx(svn_client_ctx_t *ctx);
/* Set *ORIGINAL_REPOS_RELPATH and *ORIGINAL_REVISION to the original location
that served as the source of the copy from which PATH_OR_URL at REVISION was
created, or NULL and SVN_INVALID_REVNUM (respectively) if PATH_OR_URL at
- REVISION was not the result of a copy operation. */
+ REVISION was not the result of a copy operation.
+
+ If RA_SESSION is not NULL it is an existing session to the repository that
+ might be reparented temporarily to obtain the information.
+ */
svn_error_t *
svn_client__get_copy_source(const char **original_repos_relpath,
svn_revnum_t *original_revision,
const char *path_or_url,
const svn_opt_revision_t *revision,
+ svn_ra_session_t *ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
@@ -212,7 +200,6 @@ svn_client__repos_location_segments(apr_array_header_t **segments,
See also svn_client__calc_youngest_common_ancestor() to find youngest
common ancestor for already fetched history-as-mergeinfo information.
- See also svn_client__youngest_common_ancestor().
*/
svn_error_t *
svn_client__get_youngest_common_ancestor(svn_client__pathrev_t **ancestor_p,
@@ -422,17 +409,6 @@ svn_error_t *svn_client__get_all_auto_props(apr_hash_t **autoprops,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
-/* Get a list of ignore patterns defined by the svn:global-ignores
- properties set on, or inherited by, PATH_OR_URL. Store the collected
- patterns as const char * elements in the array *IGNORES. Allocate
- *IGNORES and its contents in RESULT_POOL. Use SCRATCH_POOL for
- temporary allocations. */
-svn_error_t *svn_client__get_inherited_ignores(apr_array_header_t **ignores,
- const char *path_or_url,
- svn_client_ctx_t *ctx,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool);
-
/* The main logic for client deletion from a working copy. Deletes PATH
from CTX->WC_CTX. If PATH (or any item below a directory PATH) is
modified the delete will fail and return an error unless FORCE or KEEP_LOCAL
@@ -467,10 +443,10 @@ svn_client__wc_delete_many(const apr_array_header_t *targets,
apr_pool_t *pool);
-/* Make PATH and add it to the working copy, optionally making all the
- intermediate parent directories if MAKE_PARENTS is TRUE. */
+/* Make LOCAL_ABSPATH and add it to the working copy, optionally making all
+ the intermediate parent directories if MAKE_PARENTS is TRUE. */
svn_error_t *
-svn_client__make_local_parents(const char *path,
+svn_client__make_local_parents(const char *local_abspath,
svn_boolean_t make_parents,
svn_client_ctx_t *ctx,
apr_pool_t *pool);
@@ -519,10 +495,14 @@ svn_client__make_local_parents(const char *path,
(with depth=empty) any parent directories of the requested update
target which are missing from the working copy.
+ If RA_SESSION is NOT NULL, it may be used to avoid creating a new
+ session. The session may point to a different URL after returning.
+
NOTE: You may not specify both INNERUPDATE and MAKE_PARENTS as true.
*/
svn_error_t *
svn_client__update_internal(svn_revnum_t *result_rev,
+ svn_boolean_t *timestamp_sleep,
const char *local_abspath,
const svn_opt_revision_t *revision,
svn_depth_t depth,
@@ -532,7 +512,7 @@ svn_client__update_internal(svn_revnum_t *result_rev,
svn_boolean_t adds_as_modification,
svn_boolean_t make_parents,
svn_boolean_t innerupdate,
- svn_boolean_t *timestamp_sleep,
+ svn_ra_session_t *ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *pool);
@@ -548,11 +528,6 @@ svn_client__update_internal(svn_revnum_t *result_rev,
DEPTH must be a definite depth, not (e.g.) svn_depth_unknown.
- RA_CACHE is a pointer to a cache of information for the URL at
- REVISION based on the PEG_REVISION. Any information not in
- *RA_CACHE is retrieved by a round-trip to the repository. RA_CACHE
- may be NULL which indicates that no cache information is available.
-
If IGNORE_EXTERNALS is true, do no externals processing.
Set *TIMESTAMP_SLEEP to TRUE if a sleep is required; otherwise do not
@@ -564,10 +539,12 @@ svn_client__update_internal(svn_revnum_t *result_rev,
the repos are tolerated; if FALSE, these obstructions cause the checkout
to fail.
- If INNERCHECKOUT is true, no anchor check is performed on the target.
+ If RA_SESSION is NOT NULL, it may be used to avoid creating a new
+ session. The session may point to a different URL after returning.
*/
svn_error_t *
svn_client__checkout_internal(svn_revnum_t *result_rev,
+ svn_boolean_t *timestamp_sleep,
const char *URL,
const char *local_abspath,
const svn_opt_revision_t *peg_revision,
@@ -575,7 +552,7 @@ svn_client__checkout_internal(svn_revnum_t *result_rev,
svn_depth_t depth,
svn_boolean_t ignore_externals,
svn_boolean_t allow_unver_obstructions,
- svn_boolean_t *timestamp_sleep,
+ svn_ra_session_t *ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *pool);
@@ -707,24 +684,28 @@ svn_client__get_diff_editor2(const svn_delta_editor_t **editor,
/*** Editor for diff summary ***/
-/* Set *CALLBACKS and *CALLBACK_BATON to a set of diff callbacks that will
- report a diff summary, i.e. only providing information about the changed
- items without the text deltas.
+/* Set *DIFF_PROCESSOR to a diff processor that will report a diff summary
+ to SUMMARIZE_FUNC.
+
+ P_ROOT_RELPATH will return a pointer to a string that must be set to
+ the root of the operation before the processor is called.
- TARGET is the target path, relative to the anchor, of the diff.
+ ORIGINAL_PATH specifies the original path and will be used with
+ **ANCHOR_PATH to create paths as the user originally provided them
+ to the diff function.
SUMMARIZE_FUNC is called with SUMMARIZE_BATON as parameter by the
created callbacks for each changed item.
*/
svn_error_t *
svn_client__get_diff_summarize_callbacks(
- svn_wc_diff_callbacks4_t **callbacks,
- void **callback_baton,
- const char *target,
- svn_boolean_t reversed,
+ const svn_diff_tree_processor_t **diff_processor,
+ const char ***p_root_relpath,
svn_client_diff_summarize_func_t summarize_func,
void *summarize_baton,
- apr_pool_t *pool);
+ const char *original_target,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
/* ---------------------------------------------------------------- */
@@ -921,11 +902,6 @@ svn_client__get_copy_committables(svn_client__committables_t **committables,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
-/* A qsort()-compatible sort routine for sorting an array of
- svn_client_commit_item_t *'s by their URL member. */
-int svn_client__sort_commit_item_urls(const void *a, const void *b);
-
-
/* Rewrite the COMMIT_ITEMS array to be sorted by URL. Also, discover
a common *BASE_URL for the items in the array, and rewrite those
items' URLs to be relative to that *BASE_URL.
@@ -939,18 +915,6 @@ svn_client__condense_commit_items(const char **base_url,
apr_array_header_t *commit_items,
apr_pool_t *pool);
-
-/* Like svn_ra_stat() on the ra session root, but with a compatibility
- hack for pre-1.2 svnserve that don't support this api. */
-svn_error_t *
-svn_client__ra_stat_compatible(svn_ra_session_t *ra_session,
- svn_revnum_t rev,
- svn_dirent_t **dirent_p,
- apr_uint32_t dirent_fields,
- svn_client_ctx_t *ctx,
- apr_pool_t *result_pool);
-
-
/* Commit the items in the COMMIT_ITEMS array using EDITOR/EDIT_BATON
to describe the committed local mods. Prior to this call,
COMMIT_ITEMS should have been run through (and BASE_URL generated
@@ -1016,6 +980,9 @@ svn_client__do_commit(const char *base_url,
change *TIMESTAMP_SLEEP. The output will be valid even if the function
returns an error.
+ If RA_SESSION is NOT NULL, it may be used to avoid creating a new
+ session. The session may point to a different URL after returning.
+
Use POOL for temporary allocation. */
svn_error_t *
svn_client__handle_externals(apr_hash_t *externals_new,
@@ -1024,6 +991,7 @@ svn_client__handle_externals(apr_hash_t *externals_new,
const char *target_abspath,
svn_depth_t requested_depth,
svn_boolean_t *timestamp_sleep,
+ svn_ra_session_t *ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *pool);
@@ -1157,7 +1125,61 @@ svn_client__resolve_conflicts(svn_boolean_t *conflicts_remain,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool);
+/* Produce a diff with depth DEPTH between two files or two directories at
+ * LEFT_ABSPATH1 and RIGHT_ABSPATH, using the provided diff callbacks to
+ * show changes in files. The files and directories involved may be part of
+ * a working copy or they may be unversioned. For versioned files, show
+ * property changes, too.
+ *
+ * If ANCHOR_ABSPATH is not null, set it to the anchor of the diff before
+ * the first processor call. (The anchor is LEFT_ABSPATH or an ancestor of it)
+ */
+svn_error_t *
+svn_client__arbitrary_nodes_diff(const char **root_relpath,
+ svn_boolean_t *root_is_dir,
+ const char *left_abspath,
+ const char *right_abspath,
+ svn_depth_t depth,
+ const svn_diff_tree_processor_t *diff_processor,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
+/* Helper for the remote case of svn_client_propget.
+ *
+ * If PROPS is not null, then get the value of property PROPNAME in
+ * REVNUM, using RA_SESSION. Store the value ('svn_string_t *') in
+ * PROPS, under the path key "TARGET_PREFIX/TARGET_RELATIVE"
+ * ('const char *').
+ *
+ * If INHERITED_PROPS is not null, then set *INHERITED_PROPS to a
+ * depth-first ordered array of svn_prop_inherited_item_t * structures
+ * representing the PROPNAME properties inherited by the target. If
+ * INHERITABLE_PROPS in not null and no inheritable properties are found,
+ * then set *INHERITED_PROPS to an empty array.
+ *
+ * Recurse according to DEPTH, similarly to svn_client_propget3().
+ *
+ * KIND is the kind of the node at "TARGET_PREFIX/TARGET_RELATIVE".
+ * Yes, caller passes this; it makes the recursion more efficient :-).
+ *
+ * Allocate PROPS and *INHERITED_PROPS in RESULT_POOL, but do all temporary
+ * work in SCRATCH_POOL. The two pools can be the same; recursive
+ * calls may use a different SCRATCH_POOL, however.
+ */
+svn_error_t *
+svn_client__remote_propget(apr_hash_t *props,
+ apr_array_header_t **inherited_props,
+ const char *propname,
+ const char *target_prefix,
+ const char *target_relative,
+ svn_node_kind_t kind,
+ svn_revnum_t revnum,
+ svn_ra_session_t *ra_session,
+ svn_depth_t depth,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
#ifdef __cplusplus
}
diff --git a/subversion/libsvn_client/cmdline.c b/subversion/libsvn_client/cmdline.c
index a17f8c4783e5..a850b4e68437 100644
--- a/subversion/libsvn_client/cmdline.c
+++ b/subversion/libsvn_client/cmdline.c
@@ -87,7 +87,7 @@ check_root_url_of_target(const char **root_url,
if ((err->apr_err == SVN_ERR_ENTRY_NOT_FOUND)
|| (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
|| (err->apr_err == SVN_ERR_WC_NOT_WORKING_COPY)
- || (err->apr_err == SVN_ERR_RA_LOCAL_REPOS_OPEN_FAILED)
+ || (err->apr_err == SVN_ERR_RA_CANNOT_CREATE_SESSION)
|| (err->apr_err == SVN_ERR_CLIENT_BAD_REVISION))
{
svn_error_clear(err);
@@ -200,6 +200,15 @@ svn_client_args_to_target_array2(apr_array_header_t **targets_p,
SVN_ERR(svn_opt__split_arg_at_peg_revision(&true_target, &peg_rev,
utf8_target, pool));
+ /* Reject the form "@abc", a peg specifier with no path. */
+ if (true_target[0] == '\0' && peg_rev[0] != '\0')
+ {
+ return svn_error_createf(SVN_ERR_BAD_FILENAME, NULL,
+ _("'%s' is just a peg revision. "
+ "Maybe try '%s@' instead?"),
+ utf8_target, utf8_target);
+ }
+
/* URLs and wc-paths get treated differently. */
if (svn_path_is_url(true_target))
{
@@ -278,7 +287,7 @@ svn_client_args_to_target_array2(apr_array_header_t **targets_p,
}
}
- target = apr_pstrcat(pool, true_target, peg_rev, (char *)NULL);
+ target = apr_pstrcat(pool, true_target, peg_rev, SVN_VA_NULL);
if (rel_url_found)
{
@@ -338,7 +347,7 @@ svn_client_args_to_target_array2(apr_array_header_t **targets_p,
SVN_ERR(svn_opt__arg_canonicalize_url(&true_target, abs_target,
pool));
- target = apr_pstrcat(pool, true_target, peg_rev, (char *)NULL);
+ target = apr_pstrcat(pool, true_target, peg_rev, SVN_VA_NULL);
}
APR_ARRAY_PUSH(*targets_p, const char *) = target;
diff --git a/subversion/libsvn_client/commit.c b/subversion/libsvn_client/commit.c
index 07fdce19cdd0..4a945c887aa9 100644
--- a/subversion/libsvn_client/commit.c
+++ b/subversion/libsvn_client/commit.c
@@ -45,6 +45,7 @@
#include "client.h"
#include "private/svn_wc_private.h"
#include "private/svn_ra_private.h"
+#include "private/svn_sorts_private.h"
#include "svn_private_config.h"
@@ -110,7 +111,8 @@ get_ra_editor(const svn_delta_editor_t **editor,
continue;
svn_pool_clear(iterpool);
- SVN_ERR(svn_wc__node_get_origin(NULL, NULL, &relpath, NULL, NULL, NULL,
+ SVN_ERR(svn_wc__node_get_origin(NULL, NULL, &relpath, NULL, NULL,
+ NULL, NULL,
ctx->wc_ctx, item->path, FALSE, pool,
iterpool));
if (relpath)
@@ -203,8 +205,8 @@ collect_lock_tokens(apr_hash_t **result,
for (hi = apr_hash_first(pool, all_tokens); hi; hi = apr_hash_next(hi))
{
- const char *url = svn__apr_hash_index_key(hi);
- const char *token = svn__apr_hash_index_val(hi);
+ const char *url = apr_hash_this_key(hi);
+ const char *token = apr_hash_this_val(hi);
const char *relpath = svn_uri_skip_ancestor(base_url, url, pool);
if (relpath)
@@ -238,91 +240,30 @@ post_process_commit_item(svn_wc_committed_queue_t *queue,
loop_recurse = TRUE;
remove_lock = (! keep_locks && (item->state_flags
- & SVN_CLIENT_COMMIT_ITEM_LOCK_TOKEN));
+ & (SVN_CLIENT_COMMIT_ITEM_LOCK_TOKEN
+ | SVN_CLIENT_COMMIT_ITEM_ADD
+ | SVN_CLIENT_COMMIT_ITEM_DELETE)));
- /* When the node was deleted (or replaced), we need to always remove the
- locks, as they're invalidated on the server. We cannot honor the
+ /* When the node was deleted (or replaced), we need to always remove the
+ locks, as they're invalidated on the server. We cannot honor the
SVN_CLIENT_COMMIT_ITEM_LOCK_TOKEN flag here because it does not tell
us whether we have locked children. */
if (item->state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE)
remove_lock = TRUE;
- return svn_wc_queue_committed3(queue, wc_ctx, item->path,
- loop_recurse, item->incoming_prop_changes,
+ return svn_error_trace(
+ svn_wc_queue_committed4(queue, wc_ctx, item->path,
+ loop_recurse,
+ 0 != (item->state_flags &
+ (SVN_CLIENT_COMMIT_ITEM_ADD
+ | SVN_CLIENT_COMMIT_ITEM_DELETE
+ | SVN_CLIENT_COMMIT_ITEM_TEXT_MODS
+ | SVN_CLIENT_COMMIT_ITEM_PROP_MODS)),
+ item->incoming_prop_changes,
remove_lock, !keep_changelists,
- sha1_checksum, scratch_pool);
-}
-
-
-static svn_error_t *
-check_nonrecursive_dir_delete(svn_wc_context_t *wc_ctx,
- const char *target_abspath,
- svn_depth_t depth,
- apr_pool_t *scratch_pool)
-{
- svn_node_kind_t kind;
-
- SVN_ERR_ASSERT(depth != svn_depth_infinity);
-
- SVN_ERR(svn_wc_read_kind2(&kind, wc_ctx, target_abspath,
- TRUE, FALSE, scratch_pool));
-
-
- /* ### TODO(sd): This check is slightly too strict. It should be
- ### possible to:
- ###
- ### * delete a directory containing only files when
- ### depth==svn_depth_files;
- ###
- ### * delete a directory containing only files and empty
- ### subdirs when depth==svn_depth_immediates.
- ###
- ### But for now, we insist on svn_depth_infinity if you're
- ### going to delete a directory, because we're lazy and
- ### trying to get depthy commits working in the first place.
- ###
- ### This would be fairly easy to fix, though: just, well,
- ### check the above conditions!
- ###
- ### GJS: I think there may be some confusion here. there is
- ### the depth of the commit, and the depth of a checked-out
- ### directory in the working copy. Delete, by its nature, will
- ### always delete all of its children, so it seems a bit
- ### strange to worry about what is in the working copy.
- */
- if (kind == svn_node_dir)
- {
- svn_wc_schedule_t schedule;
-
- /* ### Looking at schedule is probably enough, no need for
- pristine compare etc. */
- SVN_ERR(svn_wc__node_get_schedule(&schedule, NULL,
- wc_ctx, target_abspath,
- scratch_pool));
-
- if (schedule == svn_wc_schedule_delete
- || schedule == svn_wc_schedule_replace)
- {
- const apr_array_header_t *children;
-
- SVN_ERR(svn_wc__node_get_children(&children, wc_ctx,
- target_abspath, TRUE,
- scratch_pool, scratch_pool));
-
- if (children->nelts > 0)
- return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
- _("Cannot delete the directory '%s' "
- "in a non-recursive commit "
- "because it has children"),
- svn_dirent_local_style(target_abspath,
- scratch_pool));
- }
- }
-
- return SVN_NO_ERROR;
+ sha1_checksum, scratch_pool));
}
-
/* Given a list of committables described by their common base abspath
BASE_ABSPATH and a list of relative dirents TARGET_RELPATHS determine
which absolute paths must be locked to commit all these targets and
@@ -393,8 +334,8 @@ determine_lock_targets(apr_array_header_t **lock_targets,
hi = apr_hash_next(hi))
{
const char *common;
- const char *wcroot_abspath = svn__apr_hash_index_key(hi);
- apr_array_header_t *wc_targets = svn__apr_hash_index_val(hi);
+ const char *wcroot_abspath = apr_hash_this_key(hi);
+ apr_array_header_t *wc_targets = apr_hash_this_val(hi);
svn_pool_clear(iterpool);
@@ -420,8 +361,7 @@ determine_lock_targets(apr_array_header_t **lock_targets,
SVN_ERR(svn_dirent_condense_targets(&common, &wc_targets, wc_targets,
FALSE, iterpool, iterpool));
- qsort(wc_targets->elts, wc_targets->nelts, wc_targets->elt_size,
- svn_sort_compare_paths);
+ svn_sort__array(wc_targets, svn_sort_compare_paths);
if (wc_targets->nelts == 0
|| !svn_path_is_empty(APR_ARRAY_IDX(wc_targets, 0, const char*))
@@ -599,6 +539,7 @@ svn_client_commit6(const apr_array_header_t *targets,
const char *current_abspath;
const char *notify_prefix;
int depth_empty_after = -1;
+ apr_hash_t *move_youngest = NULL;
int i;
SVN_ERR_ASSERT(depth != svn_depth_unknown && depth != svn_depth_exclude);
@@ -673,26 +614,6 @@ svn_client_commit6(const apr_array_header_t *targets,
base_abspath,
pool);
- /* If a non-recursive commit is desired, do not allow a deleted directory
- as one of the targets. */
- if (depth != svn_depth_infinity && ! commit_as_operations)
- for (i = 0; i < rel_targets->nelts; i++)
- {
- const char *relpath = APR_ARRAY_IDX(rel_targets, i, const char *);
- const char *target_abspath;
-
- svn_pool_clear(iterpool);
-
- target_abspath = svn_dirent_join(base_abspath, relpath, iterpool);
-
- cmt_err = svn_error_trace(
- check_nonrecursive_dir_delete(ctx->wc_ctx, target_abspath,
- depth, iterpool));
-
- if (cmt_err)
- goto cleanup;
- }
-
/* Crawl the working copy for commit items. */
{
struct check_url_kind_baton cukb;
@@ -741,7 +662,7 @@ svn_client_commit6(const apr_array_header_t *targets,
apr_hash_index_t *hi = apr_hash_first(iterpool,
committables->by_repository);
- commit_items = svn__apr_hash_index_val(hi);
+ commit_items = apr_hash_this_val(hi);
}
/* If our array of targets contains only locks (and no actual file
@@ -789,62 +710,12 @@ svn_client_commit6(const apr_array_header_t *targets,
if (cmt_err)
goto cleanup;
- if (moved_from_abspath && delete_op_root_abspath &&
- strcmp(moved_from_abspath, delete_op_root_abspath) == 0)
-
+ if (moved_from_abspath && delete_op_root_abspath)
{
- svn_boolean_t found_delete_half =
- (svn_hash_gets(committables->by_path, delete_op_root_abspath)
- != NULL);
-
- if (!found_delete_half)
- {
- const char *delete_half_parent_abspath;
-
- /* The delete-half isn't in the commit target list.
- * However, it might itself be the child of a deleted node,
- * either because of another move or a deletion.
- *
- * For example, consider: mv A/B B; mv B/C C; commit;
- * C's moved-from A/B/C is a child of the deleted A/B.
- * A/B/C does not appear in the commit target list, but
- * A/B does appear.
- * (Note that moved-from information is always stored
- * relative to the BASE tree, so we have 'C moved-from
- * A/B/C', not 'C moved-from B/C'.)
- *
- * An example involving a move and a delete would be:
- * mv A/B C; rm A; commit;
- * Now C is moved-from A/B which does not appear in the
- * commit target list, but A does appear.
- */
-
- /* Scan upwards for a deletion op-root from the
- * delete-half's parent directory. */
- delete_half_parent_abspath =
- svn_dirent_dirname(delete_op_root_abspath, iterpool);
- if (strcmp(delete_op_root_abspath,
- delete_half_parent_abspath) != 0)
- {
- const char *parent_delete_op_root_abspath;
-
- cmt_err = svn_error_trace(
- svn_wc__node_get_deleted_ancestor(
- &parent_delete_op_root_abspath,
- ctx->wc_ctx, delete_half_parent_abspath,
- iterpool, iterpool));
- if (cmt_err)
- goto cleanup;
-
- if (parent_delete_op_root_abspath)
- found_delete_half =
- (svn_hash_gets(committables->by_path,
- parent_delete_op_root_abspath)
- != NULL);
- }
- }
+ svn_client_commit_item3_t *delete_half =
+ svn_hash_gets(committables->by_path, delete_op_root_abspath);
- if (!found_delete_half)
+ if (!delete_half)
{
cmt_err = svn_error_createf(
SVN_ERR_ILLEGAL_TARGET, NULL,
@@ -854,8 +725,32 @@ svn_client_commit6(const apr_array_header_t *targets,
svn_dirent_local_style(item->path, iterpool),
svn_dirent_local_style(delete_op_root_abspath,
iterpool));
+
+ if (ctx->notify_func2)
+ {
+ svn_wc_notify_t *notify;
+ notify = svn_wc_create_notify(
+ delete_op_root_abspath,
+ svn_wc_notify_failed_requires_target,
+ iterpool);
+ notify->err = cmt_err;
+
+ ctx->notify_func2(ctx->notify_baton2, notify, iterpool);
+ }
+
goto cleanup;
}
+ else if (delete_half->revision == item->copyfrom_rev)
+ {
+ /* Ok, now we know that we perform an out-of-date check
+ on the copyfrom location. Remember this for a fixup
+ round right before committing. */
+
+ if (!move_youngest)
+ move_youngest = apr_hash_make(pool);
+
+ svn_hash_sets(move_youngest, item->path, item);
+ }
}
}
@@ -885,6 +780,19 @@ svn_client_commit6(const apr_array_header_t *targets,
svn_dirent_local_style(item->path, iterpool),
svn_dirent_local_style(copy_op_root_abspath,
iterpool));
+
+ if (ctx->notify_func2)
+ {
+ svn_wc_notify_t *notify;
+ notify = svn_wc_create_notify(
+ copy_op_root_abspath,
+ svn_wc_notify_failed_requires_target,
+ iterpool);
+ notify->err = cmt_err;
+
+ ctx->notify_func2(ctx->notify_baton2, notify, iterpool);
+ }
+
goto cleanup;
}
}
@@ -941,6 +849,37 @@ svn_client_commit6(const apr_array_header_t *targets,
if (cmt_err)
goto cleanup;
+ if (move_youngest != NULL)
+ {
+ apr_hash_index_t *hi;
+ svn_revnum_t youngest;
+
+ SVN_ERR(svn_ra_get_latest_revnum(ra_session, &youngest, pool));
+
+ for (hi = apr_hash_first(iterpool, move_youngest);
+ hi;
+ hi = apr_hash_next(hi))
+ {
+ svn_client_commit_item3_t *item = apr_hash_this_val(hi);
+
+ /* We delete the original side with its original revision and will
+ receive an out-of-date error if that node changed since that
+ revision.
+
+ The copy is of that same revision and we know that this revision
+ didn't change between this revision and youngest. So we can just
+ as well commit a copy from youngest.
+
+ Note that it is still possible to see gaps between the delete and
+ copy revisions as the repository might handle multiple commits
+ at the same time (or when an out of date proxy is involved), but
+ in general it should decrease the number of gaps. */
+
+ if (item->copyfrom_rev < youngest)
+ item->copyfrom_rev = youngest;
+ }
+ }
+
cmt_err = svn_error_trace(
get_ra_editor(&editor, &edit_baton, ra_session, ctx,
log_msg, commit_items, revprop_table,
@@ -996,6 +935,9 @@ svn_client_commit6(const apr_array_header_t *targets,
commit_info->author,
ctx->cancel_func, ctx->cancel_baton,
iterpool);
+
+ if (bump_err)
+ goto cleanup;
}
cleanup:
@@ -1004,16 +946,16 @@ svn_client_commit6(const apr_array_header_t *targets,
working copies. */
if (timestamp_sleep)
{
- const char *wcroot_abspath;
- svn_error_t *err = svn_wc__get_wcroot(&wcroot_abspath, ctx->wc_ctx,
+ const char *sleep_abspath;
+ svn_error_t *err = svn_wc__get_wcroot(&sleep_abspath, ctx->wc_ctx,
base_abspath, pool, pool);
if (err)
{
svn_error_clear(err);
- wcroot_abspath = NULL;
+ sleep_abspath = base_abspath;
}
- svn_io_sleep_for_timestamps(wcroot_abspath, pool);
+ svn_io_sleep_for_timestamps(sleep_abspath, pool);
}
/* Abort the commit if it is still in progress. */
diff --git a/subversion/libsvn_client/commit_util.c b/subversion/libsvn_client/commit_util.c
index a32ec5d3ef80..1f3d87783436 100644
--- a/subversion/libsvn_client/commit_util.c
+++ b/subversion/libsvn_client/commit_util.c
@@ -40,11 +40,11 @@
#include "svn_hash.h"
#include <assert.h>
-#include <stdlib.h> /* for qsort() */
#include "svn_private_config.h"
#include "private/svn_wc_private.h"
#include "private/svn_client_private.h"
+#include "private/svn_sorts_private.h"
/*** Uncomment this to turn on commit driver debugging. ***/
/*
@@ -62,10 +62,12 @@ fixup_commit_error(const char *local_abspath,
apr_pool_t *scratch_pool)
{
if (err->apr_err == SVN_ERR_FS_NOT_FOUND
+ || err->apr_err == SVN_ERR_FS_CONFLICT
|| err->apr_err == SVN_ERR_FS_ALREADY_EXISTS
|| err->apr_err == SVN_ERR_FS_TXN_OUT_OF_DATE
|| err->apr_err == SVN_ERR_RA_DAV_PATH_NOT_FOUND
|| err->apr_err == SVN_ERR_RA_DAV_ALREADY_EXISTS
+ || err->apr_err == SVN_ERR_RA_DAV_PRECONDITION_FAILED
|| svn_error_find_cause(err, SVN_ERR_RA_OUT_OF_DATE))
{
if (ctx->notify_func2)
@@ -102,6 +104,7 @@ fixup_commit_error(const char *local_abspath,
}
else if (svn_error_find_cause(err, SVN_ERR_FS_NO_LOCK_TOKEN)
|| err->apr_err == SVN_ERR_FS_LOCK_OWNER_MISMATCH
+ || err->apr_err == SVN_ERR_FS_BAD_LOCK_TOKEN
|| err->apr_err == SVN_ERR_RA_NOT_LOCKED)
{
if (ctx->notify_func2)
@@ -464,10 +467,12 @@ harvest_not_present_for_copy(svn_wc_context_t *wc_ctx,
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
int i;
+ SVN_ERR_ASSERT(commit_relpath != NULL);
+
/* A function to retrieve not present children would be nice to have */
- SVN_ERR(svn_wc__node_get_children_of_working_node(
- &children, wc_ctx, local_abspath, TRUE,
- scratch_pool, iterpool));
+ SVN_ERR(svn_wc__node_get_not_present_children(&children, wc_ctx,
+ local_abspath,
+ scratch_pool, iterpool));
for (i = 0; i < children->nelts; i++)
{
@@ -483,13 +488,10 @@ harvest_not_present_for_copy(svn_wc_context_t *wc_ctx,
this_abspath, FALSE, scratch_pool));
if (!not_present)
- continue;
+ continue; /* Node is replaced */
- if (commit_relpath == NULL)
- this_commit_relpath = NULL;
- else
- this_commit_relpath = svn_relpath_join(commit_relpath, name,
- iterpool);
+ this_commit_relpath = svn_relpath_join(commit_relpath, name,
+ iterpool);
/* We should check if we should really add a delete operation */
if (check_url_func)
@@ -502,7 +504,7 @@ harvest_not_present_for_copy(svn_wc_context_t *wc_ctx,
/* Determine from what parent we would be the deleted child */
SVN_ERR(svn_wc__node_get_origin(
NULL, &parent_rev, &parent_repos_relpath,
- &parent_repos_root_url, NULL, NULL,
+ &parent_repos_root_url, NULL, NULL, NULL,
wc_ctx,
svn_dirent_dirname(this_abspath,
scratch_pool),
@@ -768,13 +770,14 @@ harvest_status_callback(void *status_baton,
&& !(state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE))
{
svn_revnum_t dir_rev = SVN_INVALID_REVNUM;
+ const char *dir_repos_relpath = NULL;
- if (!copy_mode_root && !status->switched && !is_added)
- SVN_ERR(svn_wc__node_get_base(NULL, &dir_rev, NULL, NULL, NULL, NULL,
+ if (!copy_mode_root && !is_added)
+ SVN_ERR(svn_wc__node_get_base(NULL, &dir_rev, &dir_repos_relpath, NULL,
+ NULL, NULL,
wc_ctx, svn_dirent_dirname(local_abspath,
scratch_pool),
FALSE /* ignore_enoent */,
- FALSE /* show_hidden */,
scratch_pool, scratch_pool));
if (copy_mode_root || status->switched || node_rev != dir_rev)
@@ -794,6 +797,25 @@ harvest_status_callback(void *status_baton,
cf_rev = status->revision;
cf_relpath = status->repos_relpath;
}
+
+ if (!copy_mode_root && !is_added && baton->check_url_func
+ && dir_repos_relpath)
+ {
+ svn_node_kind_t me_kind;
+ /* Maybe we need to issue an delete (mixed rev/switched) */
+
+ SVN_ERR(baton->check_url_func(
+ baton->check_url_baton, &me_kind,
+ svn_path_url_add_component2(repos_root_url,
+ svn_relpath_join(dir_repos_relpath,
+ svn_dirent_basename(local_abspath,
+ NULL),
+ scratch_pool),
+ scratch_pool),
+ dir_rev, scratch_pool));
+ if (me_kind != svn_node_none)
+ state_flags |= SVN_CLIENT_COMMIT_ITEM_DELETE;
+ }
}
}
@@ -919,7 +941,7 @@ harvest_status_callback(void *status_baton,
* directory. In either case, we require the op-root of the parent
* to be part of the commit. See issue #4059. */
SVN_ERR(svn_wc__node_get_origin(&parent_is_copy, NULL, NULL, NULL,
- NULL, &copy_root_abspath,
+ NULL, NULL, &copy_root_abspath,
wc_ctx, parent_abspath,
FALSE, scratch_pool, scratch_pool));
@@ -1233,13 +1255,13 @@ svn_client__harvest_committables(svn_client__committables_t **committables,
/* Make sure that every path in danglers is part of the commit. */
for (hi = apr_hash_first(scratch_pool, danglers); hi; hi = apr_hash_next(hi))
{
- const char *dangling_parent = svn__apr_hash_index_key(hi);
+ const char *dangling_parent = apr_hash_this_key(hi);
svn_pool_clear(iterpool);
if (! look_up_committable(*committables, dangling_parent, iterpool))
{
- const char *dangling_child = svn__apr_hash_index_val(hi);
+ const char *dangling_child = apr_hash_this_val(hi);
if (ctx->notify_func2 != NULL)
{
@@ -1357,7 +1379,10 @@ svn_client__get_copy_committables(svn_client__committables_t **committables,
}
-int svn_client__sort_commit_item_urls(const void *a, const void *b)
+/* A svn_sort__array()/qsort()-compatible sort routine for sorting
+ an array of svn_client_commit_item_t *'s by their URL member. */
+static int
+sort_commit_item_urls(const void *a, const void *b)
{
const svn_client_commit_item3_t *item1
= *((const svn_client_commit_item3_t * const *) a);
@@ -1381,8 +1406,7 @@ svn_client__condense_commit_items(const char **base_url,
SVN_ERR_ASSERT(ci && ci->nelts);
/* Sort our commit items by their URLs. */
- qsort(ci->elts, ci->nelts,
- ci->elt_size, svn_client__sort_commit_item_urls);
+ svn_sort__array(ci, sort_commit_item_urls);
/* Loop through the URLs, finding the longest usable ancestor common
to all of them, and making sure there are no duplicate URLs. */
@@ -1470,6 +1494,7 @@ struct file_mod_t
{
const svn_client_commit_item3_t *item;
void *file_baton;
+ apr_pool_t *file_pool;
};
@@ -1535,6 +1560,9 @@ do_item_commit(void **dir_baton,
else
file_pool = pool;
+ /* Subpools are cheap, but memory isn't */
+ file_pool = svn_pool_create(file_pool);
+
/* Call the cancellation function. */
if (ctx->cancel_func)
SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
@@ -1618,11 +1646,12 @@ do_item_commit(void **dir_baton,
else
notify = NULL;
+
if (notify)
{
notify->kind = item->kind;
notify->path_prefix = icb->notify_path_prefix;
- (*ctx->notify_func2)(ctx->notify_baton2, notify, pool);
+ ctx->notify_func2(ctx->notify_baton2, notify, pool);
}
}
@@ -1783,6 +1812,7 @@ do_item_commit(void **dir_baton,
/* Add this file mod to the FILE_MODS hash. */
mod->item = item;
mod->file_baton = file_baton;
+ mod->file_pool = file_pool;
svn_hash_sets(file_mods, item->session_relpath, mod);
}
else if (file_baton)
@@ -1790,7 +1820,7 @@ do_item_commit(void **dir_baton,
/* Close any outstanding file batons that didn't get caught by
the "has local mods" conditional above. */
err = editor->close_file(file_baton, NULL, file_pool);
-
+ svn_pool_destroy(file_pool);
if (err)
goto fixup_error;
}
@@ -1858,7 +1888,7 @@ svn_client__do_commit(const char *base_url,
hi;
hi = apr_hash_next(hi))
{
- struct file_mod_t *mod = svn__apr_hash_index_val(hi);
+ struct file_mod_t *mod = apr_hash_this_val(hi);
const svn_client_commit_item3_t *item = mod->item;
const svn_checksum_t *new_text_base_md5_checksum;
const svn_checksum_t *new_text_base_sha1_checksum;
@@ -1905,6 +1935,17 @@ svn_client__do_commit(const char *base_url,
if (sha1_checksums)
svn_hash_sets(*sha1_checksums, item->path, new_text_base_sha1_checksum);
+
+ svn_pool_destroy(mod->file_pool);
+ }
+
+ if (ctx->notify_func2)
+ {
+ svn_wc_notify_t *notify;
+ notify = svn_wc_create_notify_url(base_url,
+ svn_wc_notify_commit_finalizing,
+ iterpool);
+ ctx->notify_func2(ctx->notify_baton2, notify, iterpool);
}
svn_pool_destroy(iterpool);
diff --git a/subversion/libsvn_client/compat_providers.c b/subversion/libsvn_client/compat_providers.c
index ae53a15cdea8..864d158e341b 100644
--- a/subversion/libsvn_client/compat_providers.c
+++ b/subversion/libsvn_client/compat_providers.c
@@ -27,6 +27,10 @@
/*** Includes. ***/
+/* We define this here to remove any further warnings about the usage of
+ deprecated functions in this file. */
+#define SVN_DEPRECATED
+
#include "svn_auth.h"
#include "svn_client.h"
diff --git a/subversion/libsvn_client/copy.c b/subversion/libsvn_client/copy.c
index 1a48b67155b3..12bbfe6cf687 100644
--- a/subversion/libsvn_client/copy.c
+++ b/subversion/libsvn_client/copy.c
@@ -177,12 +177,513 @@ get_copy_pair_ancestors(const apr_array_header_t *copy_pairs,
return SVN_NO_ERROR;
}
+/* Quote a string if it would be handled as multiple or different tokens
+ during externals parsing */
+static const char *
+maybe_quote(const char *value,
+ apr_pool_t *result_pool)
+{
+ apr_status_t status;
+ char **argv;
+
+ status = apr_tokenize_to_argv(value, &argv, result_pool);
+
+ if (!status && argv[0] && !argv[1] && strcmp(argv[0], value) == 0)
+ return apr_pstrdup(result_pool, value);
+
+ {
+ svn_stringbuf_t *sb = svn_stringbuf_create_empty(result_pool);
+ const char *c;
+
+ svn_stringbuf_appendbyte(sb, '\"');
+
+ for (c = value; *c; c++)
+ {
+ if (*c == '\\' || *c == '\"' || *c == '\'')
+ svn_stringbuf_appendbyte(sb, '\\');
+
+ svn_stringbuf_appendbyte(sb, *c);
+ }
+
+ svn_stringbuf_appendbyte(sb, '\"');
+
+#ifdef SVN_DEBUG
+ status = apr_tokenize_to_argv(sb->data, &argv, result_pool);
+
+ SVN_ERR_ASSERT_NO_RETURN(!status && argv[0] && !argv[1]
+ && !strcmp(argv[0], value));
+#endif
+
+ return sb->data;
+ }
+}
+
+/* In *NEW_EXTERNALS_DESCRIPTION, return a new external description for
+ * use as a line in an svn:externals property, based on the external item
+ * ITEM and the additional parser information in INFO. Pin the external
+ * to EXTERNAL_PEGREV. Use POOL for all allocations. */
+static svn_error_t *
+make_external_description(const char **new_external_description,
+ const char *local_abspath_or_url,
+ svn_wc_external_item2_t *item,
+ svn_wc__externals_parser_info_t *info,
+ svn_opt_revision_t external_pegrev,
+ apr_pool_t *pool)
+{
+ const char *rev_str;
+ const char *peg_rev_str;
+
+ switch (info->format)
+ {
+ case svn_wc__external_description_format_1:
+ if (external_pegrev.kind == svn_opt_revision_unspecified)
+ {
+ /* If info->rev_str is NULL, this yields an empty string. */
+ rev_str = apr_pstrcat(pool, info->rev_str, " ", SVN_VA_NULL);
+ }
+ else if (info->rev_str && item->revision.kind != svn_opt_revision_head)
+ rev_str = apr_psprintf(pool, "%s ", info->rev_str);
+ else
+ {
+ /* ### can't handle svn_opt_revision_date without info->rev_str */
+ SVN_ERR_ASSERT(external_pegrev.kind == svn_opt_revision_number);
+ rev_str = apr_psprintf(pool, "-r%ld ",
+ external_pegrev.value.number);
+ }
+
+ *new_external_description =
+ apr_psprintf(pool, "%s %s%s\n", maybe_quote(item->target_dir, pool),
+ rev_str,
+ maybe_quote(item->url, pool));
+ break;
+
+ case svn_wc__external_description_format_2:
+ if (external_pegrev.kind == svn_opt_revision_unspecified)
+ {
+ /* If info->rev_str is NULL, this yields an empty string. */
+ rev_str = apr_pstrcat(pool, info->rev_str, " ", SVN_VA_NULL);
+ }
+ else if (info->rev_str && item->revision.kind != svn_opt_revision_head)
+ rev_str = apr_psprintf(pool, "%s ", info->rev_str);
+ else
+ rev_str = "";
+
+ if (external_pegrev.kind == svn_opt_revision_unspecified)
+ peg_rev_str = info->peg_rev_str ? info->peg_rev_str : "";
+ else if (info->peg_rev_str &&
+ item->peg_revision.kind != svn_opt_revision_head)
+ peg_rev_str = info->peg_rev_str;
+ else
+ {
+ /* ### can't handle svn_opt_revision_date without info->rev_str */
+ SVN_ERR_ASSERT(external_pegrev.kind == svn_opt_revision_number);
+ peg_rev_str = apr_psprintf(pool, "@%ld",
+ external_pegrev.value.number);
+ }
+
+ *new_external_description =
+ apr_psprintf(pool, "%s%s %s\n", rev_str,
+ maybe_quote(apr_psprintf(pool, "%s%s", item->url,
+ peg_rev_str),
+ pool),
+ maybe_quote(item->target_dir, pool));
+ break;
+
+ default:
+ return svn_error_createf(
+ SVN_ERR_CLIENT_INVALID_EXTERNALS_DESCRIPTION, NULL,
+ _("%s property defined at '%s' is using an unsupported "
+ "syntax"), SVN_PROP_EXTERNALS,
+ svn_dirent_local_style(local_abspath_or_url, pool));
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Pin all externals listed in EXTERNALS_PROP_VAL to their
+ * last-changed revision. Set *PINNED_EXTERNALS to a new property
+ * value allocated in RESULT_POOL, or to NULL if none of the externals
+ * in EXTERNALS_PROP_VAL were changed. LOCAL_ABSPATH_OR_URL is the
+ * path or URL defining the svn:externals property. Use SCRATCH_POOL
+ * for temporary allocations.
+ */
+static svn_error_t *
+pin_externals_prop(svn_string_t **pinned_externals,
+ svn_string_t *externals_prop_val,
+ const apr_hash_t *externals_to_pin,
+ const char *repos_root_url,
+ const char *local_abspath_or_url,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_stringbuf_t *buf;
+ apr_array_header_t *external_items;
+ apr_array_header_t *parser_infos;
+ apr_array_header_t *items_to_pin;
+ int pinned_items;
+ int i;
+ apr_pool_t *iterpool;
+
+ SVN_ERR(svn_wc__parse_externals_description(&external_items,
+ &parser_infos,
+ local_abspath_or_url,
+ externals_prop_val->data,
+ FALSE /* canonicalize_url */,
+ scratch_pool));
+
+ if (externals_to_pin)
+ {
+ items_to_pin = svn_hash_gets((apr_hash_t *)externals_to_pin,
+ local_abspath_or_url);
+ if (!items_to_pin)
+ {
+ /* No pinning at all for this path. */
+ *pinned_externals = NULL;
+ return SVN_NO_ERROR;
+ }
+ }
+ else
+ items_to_pin = NULL;
+
+ buf = svn_stringbuf_create_empty(scratch_pool);
+ iterpool = svn_pool_create(scratch_pool);
+ pinned_items = 0;
+ for (i = 0; i < external_items->nelts; i++)
+ {
+ svn_wc_external_item2_t *item;
+ svn_wc__externals_parser_info_t *info;
+ svn_opt_revision_t external_pegrev;
+ const char *pinned_desc;
+
+ svn_pool_clear(iterpool);
+
+ item = APR_ARRAY_IDX(external_items, i, svn_wc_external_item2_t *);
+ info = APR_ARRAY_IDX(parser_infos, i, svn_wc__externals_parser_info_t *);
+
+ if (items_to_pin)
+ {
+ int j;
+ svn_wc_external_item2_t *item_to_pin = NULL;
+
+ for (j = 0; j < items_to_pin->nelts; j++)
+ {
+ svn_wc_external_item2_t *const current =
+ APR_ARRAY_IDX(items_to_pin, j, svn_wc_external_item2_t *);
+
+
+ if (current
+ && 0 == strcmp(item->url, current->url)
+ && 0 == strcmp(item->target_dir, current->target_dir))
+ {
+ item_to_pin = current;
+ break;
+ }
+ }
+
+ /* If this item is not in our list of external items to pin then
+ * simply keep the external at its original value. */
+ if (!item_to_pin)
+ {
+ const char *desc;
+
+ external_pegrev.kind = svn_opt_revision_unspecified;
+ SVN_ERR(make_external_description(&desc, local_abspath_or_url,
+ item, info, external_pegrev,
+ iterpool));
+ svn_stringbuf_appendcstr(buf, desc);
+ continue;
+ }
+ }
+
+ if (item->peg_revision.kind == svn_opt_revision_date)
+ {
+ /* Already pinned ... copy the peg date. */
+ external_pegrev.kind = svn_opt_revision_date;
+ external_pegrev.value.date = item->peg_revision.value.date;
+ }
+ else if (item->peg_revision.kind == svn_opt_revision_number)
+ {
+ /* Already pinned ... copy the peg revision number. */
+ external_pegrev.kind = svn_opt_revision_number;
+ external_pegrev.value.number = item->peg_revision.value.number;
+ }
+ else
+ {
+ SVN_ERR_ASSERT(
+ item->peg_revision.kind == svn_opt_revision_head ||
+ item->peg_revision.kind == svn_opt_revision_unspecified);
+
+ /* We're actually going to change the peg revision. */
+ ++pinned_items;
+
+ if (svn_path_is_url(local_abspath_or_url))
+ {
+ const char *resolved_url;
+ svn_ra_session_t *external_ra_session;
+ svn_revnum_t latest_revnum;
+
+ SVN_ERR(svn_wc__resolve_relative_external_url(
+ &resolved_url, item, repos_root_url,
+ local_abspath_or_url, iterpool, iterpool));
+ SVN_ERR(svn_client__open_ra_session_internal(&external_ra_session,
+ NULL, resolved_url,
+ NULL, NULL, FALSE,
+ FALSE, ctx,
+ iterpool,
+ iterpool));
+ SVN_ERR(svn_ra_get_latest_revnum(external_ra_session,
+ &latest_revnum,
+ iterpool));
+
+ external_pegrev.kind = svn_opt_revision_number;
+ external_pegrev.value.number = latest_revnum;
+ }
+ else
+ {
+ const char *external_abspath;
+ svn_node_kind_t external_kind;
+ svn_revnum_t external_checked_out_rev;
+
+ external_abspath = svn_dirent_join(local_abspath_or_url,
+ item->target_dir,
+ iterpool);
+ SVN_ERR(svn_wc__read_external_info(&external_kind, NULL, NULL,
+ NULL, NULL, ctx->wc_ctx,
+ local_abspath_or_url,
+ external_abspath, TRUE,
+ iterpool,
+ iterpool));
+ if (external_kind == svn_node_none)
+ return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
+ NULL,
+ _("Cannot pin external '%s' defined "
+ "in %s at '%s' because it is not "
+ "checked out in the working copy "
+ "at '%s'"),
+ item->url, SVN_PROP_EXTERNALS,
+ svn_dirent_local_style(
+ local_abspath_or_url, iterpool),
+ svn_dirent_local_style(
+ external_abspath, iterpool));
+ else if (external_kind == svn_node_dir)
+ {
+ svn_boolean_t is_switched;
+ svn_boolean_t is_modified;
+ svn_revnum_t min_rev;
+ svn_revnum_t max_rev;
+
+ /* Perform some sanity checks on the checked-out external. */
+
+ SVN_ERR(svn_wc__has_switched_subtrees(&is_switched,
+ ctx->wc_ctx,
+ external_abspath, NULL,
+ iterpool));
+ if (is_switched)
+ return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
+ NULL,
+ _("Cannot pin external '%s' defined "
+ "in %s at '%s' because '%s' has "
+ "switched subtrees (switches "
+ "cannot be represented in %s)"),
+ item->url, SVN_PROP_EXTERNALS,
+ svn_dirent_local_style(
+ local_abspath_or_url, iterpool),
+ svn_dirent_local_style(
+ external_abspath, iterpool),
+ SVN_PROP_EXTERNALS);
+
+ SVN_ERR(svn_wc__has_local_mods(&is_modified, ctx->wc_ctx,
+ external_abspath, TRUE,
+ ctx->cancel_func,
+ ctx->cancel_baton,
+ iterpool));
+ if (is_modified)
+ return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
+ NULL,
+ _("Cannot pin external '%s' defined "
+ "in %s at '%s' because '%s' has "
+ "local modifications (local "
+ "modifications cannot be "
+ "represented in %s)"),
+ item->url, SVN_PROP_EXTERNALS,
+ svn_dirent_local_style(
+ local_abspath_or_url, iterpool),
+ svn_dirent_local_style(
+ external_abspath, iterpool),
+ SVN_PROP_EXTERNALS);
+
+ SVN_ERR(svn_wc__min_max_revisions(&min_rev, &max_rev, ctx->wc_ctx,
+ external_abspath, FALSE,
+ iterpool));
+ if (min_rev != max_rev)
+ return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
+ NULL,
+ _("Cannot pin external '%s' defined "
+ "in %s at '%s' because '%s' is a "
+ "mixed-revision working copy "
+ "(mixed-revisions cannot be "
+ "represented in %s)"),
+ item->url, SVN_PROP_EXTERNALS,
+ svn_dirent_local_style(
+ local_abspath_or_url, iterpool),
+ svn_dirent_local_style(
+ external_abspath, iterpool),
+ SVN_PROP_EXTERNALS);
+ external_checked_out_rev = min_rev;
+ }
+ else
+ {
+ SVN_ERR_ASSERT(external_kind == svn_node_file);
+ SVN_ERR(svn_wc__node_get_repos_info(&external_checked_out_rev,
+ NULL, NULL, NULL,
+ ctx->wc_ctx, external_abspath,
+ iterpool, iterpool));
+ }
+
+ external_pegrev.kind = svn_opt_revision_number;
+ external_pegrev.value.number = external_checked_out_rev;
+ }
+ }
+
+ SVN_ERR_ASSERT(external_pegrev.kind == svn_opt_revision_date ||
+ external_pegrev.kind == svn_opt_revision_number);
+
+ SVN_ERR(make_external_description(&pinned_desc, local_abspath_or_url,
+ item, info, external_pegrev, iterpool));
+
+ svn_stringbuf_appendcstr(buf, pinned_desc);
+ }
+ svn_pool_destroy(iterpool);
+
+ if (pinned_items > 0)
+ *pinned_externals = svn_string_create_from_buf(buf, result_pool);
+ else
+ *pinned_externals = NULL;
+
+ return SVN_NO_ERROR;
+}
+
+/* Return, in *PINNED_EXTERNALS, a new hash mapping URLs or local abspaths
+ * to svn:externals property values (as const char *), where some or all
+ * external references have been pinned.
+ * If EXTERNALS_TO_PIN is NULL, pin all externals, else pin the externals
+ * mentioned in EXTERNALS_TO_PIN.
+ * The pinning operation takes place as part of the copy operation for
+ * the source/destination pair PAIR. Use RA_SESSION and REPOS_ROOT_URL
+ * to contact the repository containing the externals definition, if neccesary.
+ * Use CX to fopen additional RA sessions to external repositories, if
+ * neccessary. Allocate *NEW_EXTERNALS in RESULT_POOL.
+ * Use SCRATCH_POOL for temporary allocations. */
+static svn_error_t *
+resolve_pinned_externals(apr_hash_t **pinned_externals,
+ const apr_hash_t *externals_to_pin,
+ svn_client__copy_pair_t *pair,
+ svn_ra_session_t *ra_session,
+ const char *repos_root_url,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ const char *old_url = NULL;
+ apr_hash_t *externals_props;
+ apr_hash_index_t *hi;
+ apr_pool_t *iterpool;
+
+ *pinned_externals = apr_hash_make(result_pool);
+
+ if (svn_path_is_url(pair->src_abspath_or_url))
+ {
+ SVN_ERR(svn_client__ensure_ra_session_url(&old_url, ra_session,
+ pair->src_abspath_or_url,
+ scratch_pool));
+ externals_props = apr_hash_make(scratch_pool);
+ SVN_ERR(svn_client__remote_propget(externals_props, NULL,
+ SVN_PROP_EXTERNALS,
+ pair->src_abspath_or_url, "",
+ svn_node_dir,
+ pair->src_revnum,
+ ra_session,
+ svn_depth_infinity,
+ scratch_pool,
+ scratch_pool));
+ }
+ else
+ {
+ SVN_ERR(svn_wc__externals_gather_definitions(&externals_props, NULL,
+ ctx->wc_ctx,
+ pair->src_abspath_or_url,
+ svn_depth_infinity,
+ scratch_pool, scratch_pool));
+
+ /* ### gather_definitions returns propvals as const char * */
+ for (hi = apr_hash_first(scratch_pool, externals_props);
+ hi;
+ hi = apr_hash_next(hi))
+ {
+ const char *local_abspath_or_url = apr_hash_this_key(hi);
+ const char *propval = apr_hash_this_val(hi);
+ svn_string_t *new_propval = svn_string_create(propval, scratch_pool);
+
+ svn_hash_sets(externals_props, local_abspath_or_url, new_propval);
+ }
+ }
+
+ if (apr_hash_count(externals_props) == 0)
+ {
+ if (old_url)
+ SVN_ERR(svn_ra_reparent(ra_session, old_url, scratch_pool));
+ return SVN_NO_ERROR;
+ }
+
+ iterpool = svn_pool_create(scratch_pool);
+ for (hi = apr_hash_first(scratch_pool, externals_props);
+ hi;
+ hi = apr_hash_next(hi))
+ {
+ const char *local_abspath_or_url = apr_hash_this_key(hi);
+ svn_string_t *externals_propval = apr_hash_this_val(hi);
+ const char *relpath;
+ svn_string_t *new_propval;
+
+ svn_pool_clear(iterpool);
+
+ SVN_ERR(pin_externals_prop(&new_propval, externals_propval,
+ externals_to_pin,
+ repos_root_url, local_abspath_or_url, ctx,
+ result_pool, iterpool));
+ if (new_propval)
+ {
+ if (svn_path_is_url(pair->src_abspath_or_url))
+ relpath = svn_uri_skip_ancestor(pair->src_abspath_or_url,
+ local_abspath_or_url,
+ result_pool);
+ else
+ relpath = svn_dirent_skip_ancestor(pair->src_abspath_or_url,
+ local_abspath_or_url);
+ SVN_ERR_ASSERT(relpath);
+
+ svn_hash_sets(*pinned_externals, relpath, new_propval);
+ }
+ }
+ svn_pool_destroy(iterpool);
+
+ if (old_url)
+ SVN_ERR(svn_ra_reparent(ra_session, old_url, scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+
/* The guts of do_wc_to_wc_copies */
static svn_error_t *
do_wc_to_wc_copies_with_write_lock(svn_boolean_t *timestamp_sleep,
const apr_array_header_t *copy_pairs,
const char *dst_parent,
+ svn_boolean_t metadata_only,
+ svn_boolean_t pin_externals,
+ const apr_hash_t *externals_to_pin,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
@@ -195,22 +696,63 @@ do_wc_to_wc_copies_with_write_lock(svn_boolean_t *timestamp_sleep,
const char *dst_abspath;
svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
svn_client__copy_pair_t *);
+ apr_hash_t *pinned_externals = NULL;
+
svn_pool_clear(iterpool);
/* Check for cancellation */
if (ctx->cancel_func)
SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
+ if (pin_externals)
+ {
+ const char *repos_root_url;
+
+ SVN_ERR(svn_wc__node_get_origin(NULL, NULL, NULL, &repos_root_url,
+ NULL, NULL, NULL, ctx->wc_ctx,
+ pair->src_abspath_or_url, FALSE,
+ scratch_pool, iterpool));
+ SVN_ERR(resolve_pinned_externals(&pinned_externals,
+ externals_to_pin, pair, NULL,
+ repos_root_url, ctx,
+ iterpool, iterpool));
+ }
+
/* Perform the copy */
dst_abspath = svn_dirent_join(pair->dst_parent_abspath, pair->base_name,
iterpool);
*timestamp_sleep = TRUE;
err = svn_wc_copy3(ctx->wc_ctx, pair->src_abspath_or_url, dst_abspath,
- FALSE /* metadata_only */,
+ metadata_only,
ctx->cancel_func, ctx->cancel_baton,
ctx->notify_func2, ctx->notify_baton2, iterpool);
if (err)
break;
+
+ if (pinned_externals)
+ {
+ apr_hash_index_t *hi;
+
+ for (hi = apr_hash_first(iterpool, pinned_externals);
+ hi;
+ hi = apr_hash_next(hi))
+ {
+ const char *dst_relpath = apr_hash_this_key(hi);
+ svn_string_t *externals_propval = apr_hash_this_val(hi);
+ const char *local_abspath;
+
+ local_abspath = svn_dirent_join(pair->dst_abspath_or_url,
+ dst_relpath, iterpool);
+ /* ### use a work queue? */
+ SVN_ERR(svn_wc_prop_set4(ctx->wc_ctx, local_abspath,
+ SVN_PROP_EXTERNALS, externals_propval,
+ svn_depth_empty, TRUE /* skip_checks */,
+ NULL /* changelist_filter */,
+ ctx->cancel_func, ctx->cancel_baton,
+ NULL, NULL, /* no extra notification */
+ iterpool));
+ }
+ }
}
svn_pool_destroy(iterpool);
@@ -223,6 +765,9 @@ do_wc_to_wc_copies_with_write_lock(svn_boolean_t *timestamp_sleep,
static svn_error_t *
do_wc_to_wc_copies(svn_boolean_t *timestamp_sleep,
const apr_array_header_t *copy_pairs,
+ svn_boolean_t metadata_only,
+ svn_boolean_t pin_externals,
+ const apr_hash_t *externals_to_pin,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
@@ -236,7 +781,8 @@ do_wc_to_wc_copies(svn_boolean_t *timestamp_sleep,
SVN_WC__CALL_WITH_WRITE_LOCK(
do_wc_to_wc_copies_with_write_lock(timestamp_sleep, copy_pairs, dst_parent,
- ctx, pool),
+ metadata_only, pin_externals,
+ externals_to_pin, ctx, pool),
ctx->wc_ctx, dst_parent_abspath, FALSE, pool);
return SVN_NO_ERROR;
@@ -594,6 +1140,8 @@ typedef struct path_driver_info_t
svn_boolean_t resurrection;
svn_boolean_t dir_add;
svn_string_t *mergeinfo; /* the new mergeinfo for the target */
+ svn_string_t *externals; /* new externals definitions for the target */
+ svn_boolean_t only_pin_externals;
} path_driver_info_t;
@@ -631,7 +1179,7 @@ path_driver_cb_func(void **dir_baton,
with such, the code is just plain wrong. */
SVN_ERR_ASSERT(! svn_path_is_empty(path));
- /* Check to see if we need to add the path as a directory. */
+ /* Check to see if we need to add the path as a parent directory. */
if (path_info->dir_add)
{
return cb_baton->editor->add_directory(path, parent_baton, NULL,
@@ -662,7 +1210,7 @@ path_driver_cb_func(void **dir_baton,
/* Not a move? This must just be the copy addition. */
else
{
- do_add = TRUE;
+ do_add = !path_info->only_pin_externals;
}
}
@@ -702,6 +1250,18 @@ path_driver_cb_func(void **dir_baton,
pool));
}
}
+
+ if (path_info->externals)
+ {
+ if (*dir_baton == NULL)
+ SVN_ERR(cb_baton->editor->open_directory(path, parent_baton,
+ SVN_INVALID_REVNUM,
+ pool, dir_baton));
+
+ SVN_ERR(cb_baton->editor->change_dir_prop(*dir_baton, SVN_PROP_EXTERNALS,
+ path_info->externals, pool));
+ }
+
return SVN_NO_ERROR;
}
@@ -786,6 +1346,79 @@ find_absent_parents2(svn_ra_session_t *ra_session,
return SVN_NO_ERROR;
}
+/* Queue property changes for pinning svn:externals properties set on
+ * descendants of the path corresponding to PARENT_INFO. PINNED_EXTERNALS
+ * is keyed by the relative path of each descendant which should have some
+ * or all of its externals pinned, with the corresponding pinned svn:externals
+ * properties as values. Property changes are queued in a new list of path
+ * infos *NEW_PATH_INFOS, or in an existing item of the PATH_INFOS list if an
+ * existing item is found for the descendant. Allocate results in RESULT_POOL.
+ * Use SCRATCH_POOL for temporary allocations. */
+static svn_error_t *
+queue_externals_change_path_infos(apr_array_header_t *new_path_infos,
+ apr_array_header_t *path_infos,
+ apr_hash_t *pinned_externals,
+ path_driver_info_t *parent_info,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+ apr_hash_index_t *hi;
+
+ for (hi = apr_hash_first(scratch_pool, pinned_externals);
+ hi;
+ hi = apr_hash_next(hi))
+ {
+ const char *dst_relpath = apr_hash_this_key(hi);
+ svn_string_t *externals_prop = apr_hash_this_val(hi);
+ const char *src_url;
+ path_driver_info_t *info;
+ int i;
+
+ svn_pool_clear(iterpool);
+
+ src_url = svn_path_url_add_component2(parent_info->src_url,
+ dst_relpath, iterpool);
+
+ /* Try to find a path info the external change can be applied to. */
+ info = NULL;
+ for (i = 0; i < path_infos->nelts; i++)
+ {
+ path_driver_info_t *existing_info;
+
+ existing_info = APR_ARRAY_IDX(path_infos, i, path_driver_info_t *);
+ if (strcmp(src_url, existing_info->src_url) == 0)
+ {
+ info = existing_info;
+ break;
+ }
+ }
+
+ if (info == NULL)
+ {
+ /* A copied-along child needs its externals pinned.
+ Create a new path info for this property change. */
+ info = apr_pcalloc(result_pool, sizeof(*info));
+ info->src_url = svn_path_url_add_component2(
+ parent_info->src_url, dst_relpath,
+ result_pool);
+ info->src_path = NULL; /* Only needed on copied dirs */
+ info->dst_path = svn_relpath_join(parent_info->dst_path,
+ dst_relpath,
+ result_pool);
+ info->src_kind = svn_node_dir;
+ info->only_pin_externals = TRUE;
+ APR_ARRAY_PUSH(new_path_infos, path_driver_info_t *) = info;
+ }
+
+ info->externals = externals_prop;
+ }
+
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
+
static svn_error_t *
repos_to_repos_copy(const apr_array_header_t *copy_pairs,
svn_boolean_t make_parents,
@@ -794,6 +1427,8 @@ repos_to_repos_copy(const apr_array_header_t *copy_pairs,
void *commit_baton,
svn_client_ctx_t *ctx,
svn_boolean_t is_move,
+ svn_boolean_t pin_externals,
+ const apr_hash_t *externals_to_pin,
apr_pool_t *pool)
{
svn_error_t *err;
@@ -809,6 +1444,7 @@ repos_to_repos_copy(const apr_array_header_t *copy_pairs,
struct path_driver_cb_baton cb_baton;
apr_array_header_t *new_dirs = NULL;
apr_hash_t *commit_revprops;
+ apr_array_header_t *pin_externals_only_infos = NULL;
int i;
svn_client__copy_pair_t *first_pair =
APR_ARRAY_IDX(copy_pairs, 0, svn_client__copy_pair_t *);
@@ -1061,16 +1697,40 @@ repos_to_repos_copy(const apr_array_header_t *copy_pairs,
SVN_ERR(svn_ra_check_path(ra_session, dst_rel, SVN_INVALID_REVNUM,
&dst_kind, pool));
if (dst_kind != svn_node_none)
- return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL,
- _("Path '%s' already exists"), dst_rel);
+ {
+ const char *path = svn_uri_skip_ancestor(repos_root,
+ pair->dst_abspath_or_url,
+ pool);
+ return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL,
+ _("Path '/%s' already exists"), path);
+ }
/* More info for our INFO structure. */
- info->src_path = src_rel;
+ info->src_path = src_rel; /* May be NULL, if outside RA session scope */
info->dst_path = dst_rel;
svn_hash_sets(action_hash, info->dst_path, info);
if (is_move && (! info->resurrection))
svn_hash_sets(action_hash, info->src_path, info);
+
+ if (pin_externals)
+ {
+ apr_hash_t *pinned_externals;
+
+ SVN_ERR(resolve_pinned_externals(&pinned_externals,
+ externals_to_pin, pair,
+ ra_session, repos_root,
+ ctx, pool, pool));
+ if (pin_externals_only_infos == NULL)
+ {
+ pin_externals_only_infos =
+ apr_array_make(pool, 0, sizeof(path_driver_info_t *));
+ }
+ SVN_ERR(queue_externals_change_path_infos(pin_externals_only_infos,
+ path_infos,
+ pinned_externals,
+ info, pool, pool));
+ }
}
if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx))
@@ -1091,6 +1751,7 @@ repos_to_repos_copy(const apr_array_header_t *copy_pairs,
item = svn_client_commit_item3_create(pool);
item->url = svn_path_url_add_component2(top_url, relpath, pool);
+ item->kind = svn_node_dir;
item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD;
APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
}
@@ -1104,14 +1765,19 @@ repos_to_repos_copy(const apr_array_header_t *copy_pairs,
item = svn_client_commit_item3_create(pool);
item->url = svn_path_url_add_component2(top_url, info->dst_path,
pool);
- item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD;
+ item->kind = info->src_kind;
+ item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD
+ | SVN_CLIENT_COMMIT_ITEM_IS_COPY;
+ item->copyfrom_url = info->src_url;
+ item->copyfrom_rev = info->src_revnum;
APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
if (is_move && (! info->resurrection))
{
- item = apr_pcalloc(pool, sizeof(*item));
+ item = svn_client_commit_item3_create(pool);
item->url = svn_path_url_add_component2(top_url, info->src_path,
pool);
+ item->kind = info->src_kind;
item->state_flags = SVN_CLIENT_COMMIT_ITEM_DELETE;
APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
}
@@ -1153,6 +1819,19 @@ repos_to_repos_copy(const apr_array_header_t *copy_pairs,
APR_ARRAY_PUSH(paths, const char *) = info->src_path;
}
+ /* Add any items which only need their externals pinned. */
+ if (pin_externals_only_infos)
+ {
+ for (i = 0; i < pin_externals_only_infos->nelts; i++)
+ {
+ path_driver_info_t *info;
+
+ info = APR_ARRAY_IDX(pin_externals_only_infos, i, path_driver_info_t *);
+ APR_ARRAY_PUSH(paths, const char *) = info->dst_path;
+ svn_hash_sets(action_hash, info->dst_path, info);
+ }
+ }
+
SVN_ERR(svn_client__ensure_revprop_table(&commit_revprops, revprop_table,
message, ctx, pool));
@@ -1184,6 +1863,15 @@ repos_to_repos_copy(const apr_array_header_t *copy_pairs,
editor->abort_edit(edit_baton, pool));
}
+ if (ctx->notify_func2)
+ {
+ svn_wc_notify_t *notify;
+ notify = svn_wc_create_notify_url(top_url,
+ svn_wc_notify_commit_finalizing,
+ pool);
+ ctx->notify_func2(ctx->notify_baton2, notify, pool);
+ }
+
/* Close the edit. */
return svn_error_trace(editor->close_edit(edit_baton, pool));
}
@@ -1222,6 +1910,65 @@ check_url_kind(void *baton,
return SVN_NO_ERROR;
}
+/* Queue a property change on a copy of LOCAL_ABSPATH to COMMIT_URL
+ * in the COMMIT_ITEMS list.
+ * If the list does not already have a commit item for COMMIT_URL
+ * add a new commit item for the property change.
+ * Allocate results in RESULT_POOL.
+ * Use SCRATCH_POOL for temporary allocations. */
+static svn_error_t *
+queue_prop_change_commit_items(const char *local_abspath,
+ const char *commit_url,
+ apr_array_header_t *commit_items,
+ const char *propname,
+ svn_string_t *propval,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_client_commit_item3_t *item = NULL;
+ svn_prop_t *prop;
+ int i;
+
+ for (i = 0; i < commit_items->nelts; i++)
+ {
+ svn_client_commit_item3_t *existing_item;
+
+ existing_item = APR_ARRAY_IDX(commit_items, i,
+ svn_client_commit_item3_t *);
+ if (strcmp(existing_item->url, commit_url) == 0)
+ {
+ item = existing_item;
+ break;
+ }
+ }
+
+ if (item == NULL)
+ {
+ item = svn_client_commit_item3_create(result_pool);
+ item->path = local_abspath;
+ item->url = commit_url;
+ item->kind = svn_node_dir;
+ item->state_flags = SVN_CLIENT_COMMIT_ITEM_PROP_MODS;
+
+ item->incoming_prop_changes = apr_array_make(result_pool, 1,
+ sizeof(svn_prop_t *));
+ APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
+ }
+ else
+ item->state_flags |= SVN_CLIENT_COMMIT_ITEM_PROP_MODS;
+
+ if (item->outgoing_prop_changes == NULL)
+ item->outgoing_prop_changes = apr_array_make(result_pool, 1,
+ sizeof(svn_prop_t *));
+
+ prop = apr_palloc(result_pool, sizeof(*prop));
+ prop->name = propname;
+ prop->value = propval;
+ APR_ARRAY_PUSH(item->outgoing_prop_changes, svn_prop_t *) = prop;
+
+ return SVN_NO_ERROR;
+}
+
/* ### Copy ...
* COMMIT_INFO_P is ...
* COPY_PAIRS is ... such that each 'src_abspath_or_url' is a local abspath
@@ -1235,6 +1982,8 @@ wc_to_repos_copy(const apr_array_header_t *copy_pairs,
const apr_hash_t *revprop_table,
svn_commit_callback2_t commit_callback,
void *commit_baton,
+ svn_boolean_t pin_externals,
+ const apr_hash_t *externals_to_pin,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
@@ -1244,7 +1993,9 @@ wc_to_repos_copy(const apr_array_header_t *copy_pairs,
const char *top_src_abspath;
svn_ra_session_t *ra_session;
const svn_delta_editor_t *editor;
+#ifdef ENABLE_EV2_SHIMS
apr_hash_t *relpath_map = NULL;
+#endif
void *edit_baton;
svn_client__committables_t *committables;
apr_array_header_t *commit_items;
@@ -1253,6 +2004,7 @@ wc_to_repos_copy(const apr_array_header_t *copy_pairs,
apr_hash_t *commit_revprops;
svn_client__copy_pair_t *first_pair;
apr_pool_t *session_pool = svn_pool_create(scratch_pool);
+ apr_array_header_t *commit_items_for_dav;
int i;
/* Find the common root of all the source paths */
@@ -1287,9 +2039,13 @@ wc_to_repos_copy(const apr_array_header_t *copy_pairs,
SVN_ERR(svn_dirent_get_absolute(&top_src_abspath, top_src_path, scratch_pool));
+ commit_items_for_dav = apr_array_make(session_pool, 0,
+ sizeof(svn_client_commit_item3_t*));
+
/* Open a session to help while determining the exact targets */
SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, top_dst_url,
- top_src_abspath, NULL,
+ top_src_abspath,
+ commit_items_for_dav,
FALSE /* write_dav_props */,
TRUE /* read_dav_props */,
ctx,
@@ -1326,52 +2082,6 @@ wc_to_repos_copy(const apr_array_header_t *copy_pairs,
}
}
- if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx))
- {
- /* Produce a list of new paths to add, and provide it to the
- mechanism used to acquire a log message. */
- svn_client_commit_item3_t *item;
- const char *tmp_file;
- commit_items = apr_array_make(scratch_pool, copy_pairs->nelts,
- sizeof(item));
-
- /* Add any intermediate directories to the message */
- if (make_parents)
- {
- for (i = 0; i < new_dirs->nelts; i++)
- {
- const char *url = APR_ARRAY_IDX(new_dirs, i, const char *);
-
- item = svn_client_commit_item3_create(scratch_pool);
- item->url = url;
- item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD;
- APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
- }
- }
-
- for (i = 0; i < copy_pairs->nelts; i++)
- {
- svn_client__copy_pair_t *pair = APR_ARRAY_IDX(copy_pairs, i,
- svn_client__copy_pair_t *);
-
- item = svn_client_commit_item3_create(scratch_pool);
- item->url = pair->dst_abspath_or_url;
- item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD;
- APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
- }
-
- SVN_ERR(svn_client__get_log_msg(&message, &tmp_file, commit_items,
- ctx, scratch_pool));
- if (! message)
- {
- svn_pool_destroy(iterpool);
- svn_pool_destroy(session_pool);
- return SVN_NO_ERROR;
- }
- }
- else
- message = "";
-
cukb.session = ra_session;
SVN_ERR(svn_ra_get_repos_root2(ra_session, &cukb.repos_root_url, session_pool));
cukb.should_reparent = FALSE;
@@ -1402,6 +2112,7 @@ wc_to_repos_copy(const apr_array_header_t *copy_pairs,
item = svn_client_commit_item3_create(scratch_pool);
item->url = url;
+ item->kind = svn_node_dir;
item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD;
item->incoming_prop_changes = apr_array_make(scratch_pool, 1,
sizeof(svn_prop_t *));
@@ -1429,8 +2140,6 @@ wc_to_repos_copy(const apr_array_header_t *copy_pairs,
/* Set the mergeinfo for the destination to the combined merge
info known to the WC and the repository. */
- item->outgoing_prop_changes = apr_array_make(scratch_pool, 1,
- sizeof(svn_prop_t *));
/* Repository mergeinfo (or NULL if it's locally added)... */
if (src_origin)
SVN_ERR(svn_client__get_repos_mergeinfo(
@@ -1447,34 +2156,97 @@ wc_to_repos_copy(const apr_array_header_t *copy_pairs,
iterpool));
else if (! mergeinfo)
mergeinfo = wc_mergeinfo;
+
if (mergeinfo)
{
/* Push a mergeinfo prop representing MERGEINFO onto the
* OUTGOING_PROP_CHANGES array. */
svn_prop_t *mergeinfo_prop
- = apr_palloc(item->outgoing_prop_changes->pool,
- sizeof(svn_prop_t));
+ = apr_palloc(scratch_pool, sizeof(*mergeinfo_prop));
svn_string_t *prop_value;
SVN_ERR(svn_mergeinfo_to_string(&prop_value, mergeinfo,
- item->outgoing_prop_changes->pool));
+ scratch_pool));
+
+ if (!item->outgoing_prop_changes)
+ {
+ item->outgoing_prop_changes = apr_array_make(scratch_pool, 1,
+ sizeof(svn_prop_t *));
+ }
mergeinfo_prop->name = SVN_PROP_MERGEINFO;
mergeinfo_prop->value = prop_value;
APR_ARRAY_PUSH(item->outgoing_prop_changes, svn_prop_t *)
= mergeinfo_prop;
}
+
+ if (pin_externals)
+ {
+ apr_hash_t *pinned_externals;
+ apr_hash_index_t *hi;
+
+ SVN_ERR(resolve_pinned_externals(&pinned_externals,
+ externals_to_pin, pair,
+ ra_session, cukb.repos_root_url,
+ ctx, scratch_pool, iterpool));
+ for (hi = apr_hash_first(scratch_pool, pinned_externals);
+ hi;
+ hi = apr_hash_next(hi))
+ {
+ const char *dst_relpath = apr_hash_this_key(hi);
+ svn_string_t *externals_propval = apr_hash_this_val(hi);
+ const char *dst_url;
+ const char *commit_url;
+ const char *src_abspath;
+
+ if (svn_path_is_url(pair->dst_abspath_or_url))
+ dst_url = pair->dst_abspath_or_url;
+ else
+ SVN_ERR(svn_wc__node_get_url(&dst_url, ctx->wc_ctx,
+ pair->dst_abspath_or_url,
+ scratch_pool, iterpool));
+ commit_url = svn_path_url_add_component2(dst_url, dst_relpath,
+ scratch_pool);
+ src_abspath = svn_dirent_join(pair->src_abspath_or_url,
+ dst_relpath, iterpool);
+ SVN_ERR(queue_prop_change_commit_items(src_abspath,
+ commit_url, commit_items,
+ SVN_PROP_EXTERNALS,
+ externals_propval,
+ scratch_pool, iterpool));
+ }
+ }
+ }
+
+ if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx))
+ {
+ const char *tmp_file;
+
+ SVN_ERR(svn_client__get_log_msg(&message, &tmp_file, commit_items,
+ ctx, scratch_pool));
+ if (! message)
+ {
+ svn_pool_destroy(iterpool);
+ svn_pool_destroy(session_pool);
+ return SVN_NO_ERROR;
+ }
}
+ else
+ message = "";
/* Sort and condense our COMMIT_ITEMS. */
SVN_ERR(svn_client__condense_commit_items(&top_dst_url,
commit_items, scratch_pool));
+ /* Add the commit items to the DAV commit item list to provide access
+ to dav properties (for pre http-v2 DAV) */
+ apr_array_cat(commit_items_for_dav, commit_items);
+
#ifdef ENABLE_EV2_SHIMS
if (commit_items)
{
- relpath_map = apr_hash_make(pool);
+ relpath_map = apr_hash_make(scratch_pool);
for (i = 0; i < commit_items->nelts; i++)
{
svn_client_commit_item3_t *item = APR_ARRAY_IDX(commit_items, i,
@@ -1485,7 +2257,8 @@ wc_to_repos_copy(const apr_array_header_t *copy_pairs,
continue;
svn_pool_clear(iterpool);
- SVN_ERR(svn_wc__node_get_origin(NULL, NULL, &relpath, NULL, NULL, NULL,
+ SVN_ERR(svn_wc__node_get_origin(NULL, NULL, &relpath, NULL, NULL,
+ NULL, NULL,
ctx->wc_ctx, item->path, FALSE,
scratch_pool, iterpool));
if (relpath)
@@ -1494,22 +2267,17 @@ wc_to_repos_copy(const apr_array_header_t *copy_pairs,
}
#endif
- /* Close the initial session, to reopen a new session with commit handling */
- svn_pool_clear(session_pool);
-
- /* Open a new RA session to DST_URL. */
- SVN_ERR(svn_client__open_ra_session_internal(&ra_session, NULL, top_dst_url,
- NULL, commit_items,
- FALSE, FALSE, ctx,
- session_pool, session_pool));
+ SVN_ERR(svn_ra_reparent(ra_session, top_dst_url, session_pool));
SVN_ERR(svn_client__ensure_revprop_table(&commit_revprops, revprop_table,
message, ctx, session_pool));
/* Fetch RA commit editor. */
+#ifdef ENABLE_EV2_SHIMS
SVN_ERR(svn_ra__register_editor_shim_callbacks(ra_session,
svn_client__get_shim_callbacks(ctx->wc_ctx, relpath_map,
session_pool)));
+#endif
SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
commit_revprops,
commit_callback,
@@ -1520,7 +2288,7 @@ wc_to_repos_copy(const apr_array_header_t *copy_pairs,
/* Perform the commit. */
SVN_ERR_W(svn_client__do_commit(top_dst_url, commit_items,
editor, edit_baton,
- 0, /* ### any notify_path_offset needed? */
+ NULL /* notify_path_prefix */,
NULL, ctx, session_pool, session_pool),
_("Commit failed (details follow):"));
@@ -1568,6 +2336,8 @@ repos_to_wc_copy_single(svn_boolean_t *timestamp_sleep,
svn_client__copy_pair_t *pair,
svn_boolean_t same_repositories,
svn_boolean_t ignore_externals,
+ svn_boolean_t pin_externals,
+ const apr_hash_t *externals_to_pin,
svn_ra_session_t *ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
@@ -1596,7 +2366,6 @@ repos_to_wc_copy_single(svn_boolean_t *timestamp_sleep,
{
if (same_repositories)
{
- svn_boolean_t sleep_needed = FALSE;
const char *tmpdir_abspath, *tmp_abspath;
/* Find a temporary location in which to check out the copy source. */
@@ -1625,14 +2394,22 @@ repos_to_wc_copy_single(svn_boolean_t *timestamp_sleep,
ctx->notify_func2 = notification_adjust_func;
ctx->notify_baton2 = &nb;
- err = svn_client__checkout_internal(&pair->src_revnum,
+ /* Avoid a chicken-and-egg problem:
+ * If pinning externals we'll need to adjust externals
+ * properties before checking out any externals.
+ * But copy needs to happen before pinning because else there
+ * are no svn:externals properties to pin. */
+ if (pin_externals)
+ ignore_externals = TRUE;
+
+ err = svn_client__checkout_internal(&pair->src_revnum, timestamp_sleep,
pair->src_original,
tmp_abspath,
&pair->src_peg_revision,
&pair->src_op_revision,
svn_depth_infinity,
ignore_externals, FALSE,
- &sleep_needed, ctx, pool);
+ ra_session, ctx, pool);
ctx->notify_func2 = old_notify_func2;
ctx->notify_baton2 = old_notify_baton2;
@@ -1677,6 +2454,61 @@ repos_to_wc_copy_single(svn_boolean_t *timestamp_sleep,
return SVN_NO_ERROR;
}
+
+ if (pin_externals)
+ {
+ apr_hash_t *pinned_externals;
+ apr_hash_index_t *hi;
+ apr_pool_t *iterpool;
+ const char *repos_root_url;
+ apr_hash_t *new_externals;
+ apr_hash_t *new_depths;
+
+ SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url, pool));
+ SVN_ERR(resolve_pinned_externals(&pinned_externals,
+ externals_to_pin, pair,
+ ra_session, repos_root_url,
+ ctx, pool, pool));
+
+ iterpool = svn_pool_create(pool);
+ for (hi = apr_hash_first(pool, pinned_externals);
+ hi;
+ hi = apr_hash_next(hi))
+ {
+ const char *dst_relpath = apr_hash_this_key(hi);
+ svn_string_t *externals_propval = apr_hash_this_val(hi);
+ const char *local_abspath;
+
+ svn_pool_clear(iterpool);
+
+ local_abspath = svn_dirent_join(pair->dst_abspath_or_url,
+ dst_relpath, iterpool);
+ /* ### use a work queue? */
+ SVN_ERR(svn_wc_prop_set4(ctx->wc_ctx, local_abspath,
+ SVN_PROP_EXTERNALS, externals_propval,
+ svn_depth_empty, TRUE /* skip_checks */,
+ NULL /* changelist_filter */,
+ ctx->cancel_func, ctx->cancel_baton,
+ NULL, NULL, /* no extra notification */
+ iterpool));
+ }
+
+ /* Now update all externals in the newly created copy. */
+ SVN_ERR(svn_wc__externals_gather_definitions(&new_externals,
+ &new_depths,
+ ctx->wc_ctx,
+ dst_abspath,
+ svn_depth_infinity,
+ iterpool, iterpool));
+ SVN_ERR(svn_client__handle_externals(new_externals,
+ new_depths,
+ repos_root_url, dst_abspath,
+ svn_depth_infinity,
+ timestamp_sleep,
+ ra_session,
+ ctx, iterpool));
+ svn_pool_destroy(iterpool);
+ }
} /* end directory case */
else if (pair->src_kind == svn_node_file)
@@ -1725,7 +2557,7 @@ repos_to_wc_copy_single(svn_boolean_t *timestamp_sleep,
svn_wc_notify_t *notify = svn_wc_create_notify(
dst_abspath, svn_wc_notify_add, pool);
notify->kind = pair->src_kind;
- (*ctx->notify_func2)(ctx->notify_baton2, notify, pool);
+ ctx->notify_func2(ctx->notify_baton2, notify, pool);
}
return SVN_NO_ERROR;
@@ -1736,6 +2568,8 @@ repos_to_wc_copy_locked(svn_boolean_t *timestamp_sleep,
const apr_array_header_t *copy_pairs,
const char *top_dst_path,
svn_boolean_t ignore_externals,
+ svn_boolean_t pin_externals,
+ const apr_hash_t *externals_to_pin,
svn_ra_session_t *ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
@@ -1801,6 +2635,7 @@ repos_to_wc_copy_locked(svn_boolean_t *timestamp_sleep,
svn_client__copy_pair_t *),
same_repositories,
ignore_externals,
+ pin_externals, externals_to_pin,
ra_session, ctx, iterpool));
}
svn_pool_destroy(iterpool);
@@ -1813,6 +2648,8 @@ repos_to_wc_copy(svn_boolean_t *timestamp_sleep,
const apr_array_header_t *copy_pairs,
svn_boolean_t make_parents,
svn_boolean_t ignore_externals,
+ svn_boolean_t pin_externals,
+ const apr_hash_t *externals_to_pin,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
@@ -1921,6 +2758,7 @@ repos_to_wc_copy(svn_boolean_t *timestamp_sleep,
SVN_WC__CALL_WITH_WRITE_LOCK(
repos_to_wc_copy_locked(timestamp_sleep,
copy_pairs, top_dst_path, ignore_externals,
+ pin_externals, externals_to_pin,
ra_session, ctx, pool),
ctx->wc_ctx, lock_abspath, FALSE, pool);
return SVN_NO_ERROR;
@@ -1947,6 +2785,8 @@ try_copy(svn_boolean_t *timestamp_sleep,
svn_boolean_t metadata_only,
svn_boolean_t make_parents,
svn_boolean_t ignore_externals,
+ svn_boolean_t pin_externals,
+ const apr_hash_t *externals_to_pin,
const apr_hash_t *revprop_table,
svn_commit_callback2_t commit_callback,
void *commit_baton,
@@ -1959,6 +2799,9 @@ try_copy(svn_boolean_t *timestamp_sleep,
svn_boolean_t srcs_are_urls, dst_is_url;
int i;
+ /* Assert instead of crashing if the sources list is empty. */
+ SVN_ERR_ASSERT(sources->nelts > 0);
+
/* Are either of our paths URLs? Just check the first src_path. If
there are more than one, we'll check for homogeneity among them
down below. */
@@ -1979,7 +2822,7 @@ try_copy(svn_boolean_t *timestamp_sleep,
{
svn_client_copy_source_t *source = APR_ARRAY_IDX(sources, i,
svn_client_copy_source_t *);
- svn_client__copy_pair_t *pair = apr_palloc(pool, sizeof(*pair));
+ svn_client__copy_pair_t *pair = apr_pcalloc(pool, sizeof(*pair));
const char *src_basename;
svn_boolean_t src_is_url = svn_path_is_url(source->path);
@@ -2001,6 +2844,7 @@ try_copy(svn_boolean_t *timestamp_sleep,
pair->src_op_revision = *source->revision;
pair->src_peg_revision = *source->peg_revision;
+ pair->src_kind = svn_node_unknown;
SVN_ERR(svn_opt_resolve_revisions(&pair->src_peg_revision,
&pair->src_op_revision,
@@ -2029,7 +2873,7 @@ try_copy(svn_boolean_t *timestamp_sleep,
else
{
/* Only one source path. */
- svn_client__copy_pair_t *pair = apr_palloc(pool, sizeof(*pair));
+ svn_client__copy_pair_t *pair = apr_pcalloc(pool, sizeof(*pair));
svn_client_copy_source_t *source =
APR_ARRAY_IDX(sources, 0, svn_client_copy_source_t *);
svn_boolean_t src_is_url = svn_path_is_url(source->path);
@@ -2041,6 +2885,7 @@ try_copy(svn_boolean_t *timestamp_sleep,
source->path, pool));
pair->src_op_revision = *source->revision;
pair->src_peg_revision = *source->peg_revision;
+ pair->src_kind = svn_node_unknown;
SVN_ERR(svn_opt_resolve_revisions(&pair->src_peg_revision,
&pair->src_op_revision,
@@ -2187,7 +3032,7 @@ try_copy(svn_boolean_t *timestamp_sleep,
SVN_ERR(svn_wc__node_get_origin(NULL, &copyfrom_rev,
&copyfrom_repos_relpath,
&copyfrom_repos_root_url,
- NULL, NULL,
+ NULL, NULL, NULL,
ctx->wc_ctx,
pair->src_abspath_or_url,
TRUE, iterpool, iterpool));
@@ -2242,30 +3087,35 @@ try_copy(svn_boolean_t *timestamp_sleep,
else
{
/* We ignore these values, so assert the default value */
- SVN_ERR_ASSERT(allow_mixed_revisions && !metadata_only);
+ SVN_ERR_ASSERT(allow_mixed_revisions);
return svn_error_trace(do_wc_to_wc_copies(timestamp_sleep,
- copy_pairs, ctx, pool));
+ copy_pairs,
+ metadata_only,
+ pin_externals,
+ externals_to_pin,
+ ctx, pool));
}
}
else if ((! srcs_are_urls) && (dst_is_url))
{
return svn_error_trace(
wc_to_repos_copy(copy_pairs, make_parents, revprop_table,
- commit_callback, commit_baton, ctx, pool));
+ commit_callback, commit_baton,
+ pin_externals, externals_to_pin, ctx, pool));
}
else if ((srcs_are_urls) && (! dst_is_url))
{
return svn_error_trace(
repos_to_wc_copy(timestamp_sleep,
copy_pairs, make_parents, ignore_externals,
- ctx, pool));
+ pin_externals, externals_to_pin, ctx, pool));
}
else
{
return svn_error_trace(
repos_to_repos_copy(copy_pairs, make_parents, revprop_table,
commit_callback, commit_baton, ctx, is_move,
- pool));
+ pin_externals, externals_to_pin, pool));
}
}
@@ -2273,11 +3123,14 @@ try_copy(svn_boolean_t *timestamp_sleep,
/* Public Interfaces */
svn_error_t *
-svn_client_copy6(const apr_array_header_t *sources,
+svn_client_copy7(const apr_array_header_t *sources,
const char *dst_path,
svn_boolean_t copy_as_child,
svn_boolean_t make_parents,
svn_boolean_t ignore_externals,
+ svn_boolean_t metadata_only,
+ svn_boolean_t pin_externals,
+ const apr_hash_t *externals_to_pin,
const apr_hash_t *revprop_table,
svn_commit_callback2_t commit_callback,
void *commit_baton,
@@ -2296,9 +3149,11 @@ svn_client_copy6(const apr_array_header_t *sources,
sources, dst_path,
FALSE /* is_move */,
TRUE /* allow_mixed_revisions */,
- FALSE /* metadata_only */,
+ metadata_only,
make_parents,
ignore_externals,
+ pin_externals,
+ externals_to_pin,
revprop_table,
commit_callback, commit_baton,
ctx,
@@ -2330,9 +3185,11 @@ svn_client_copy6(const apr_array_header_t *sources,
sources, dst_path,
FALSE /* is_move */,
TRUE /* allow_mixed_revisions */,
- FALSE /* metadata_only */,
+ metadata_only,
make_parents,
ignore_externals,
+ pin_externals,
+ externals_to_pin,
revprop_table,
commit_callback, commit_baton,
ctx,
@@ -2394,6 +3251,8 @@ svn_client_move7(const apr_array_header_t *src_paths,
metadata_only,
make_parents,
FALSE /* ignore_externals */,
+ FALSE /* pin_externals */,
+ NULL /* externals_to_pin */,
revprop_table,
commit_callback, commit_baton,
ctx,
@@ -2427,6 +3286,8 @@ svn_client_move7(const apr_array_header_t *src_paths,
metadata_only,
make_parents,
FALSE /* ignore_externals */,
+ FALSE /* pin_externals */,
+ NULL /* externals_to_pin */,
revprop_table,
commit_callback, commit_baton,
ctx,
diff --git a/subversion/libsvn_client/copy_foreign.c b/subversion/libsvn_client/copy_foreign.c
index 8de8a5d6a4b4..cfe6aea05846 100644
--- a/subversion/libsvn_client/copy_foreign.c
+++ b/subversion/libsvn_client/copy_foreign.c
@@ -160,7 +160,7 @@ dir_change_prop(void *dir_baton,
if (! db->created)
{
/* We can still store them in the hash for immediate addition
- with the svn_wc_add_from_disk2() call */
+ with the svn_wc_add_from_disk3() call */
if (! db->properties)
db->properties = apr_hash_make(db->pool);
@@ -173,7 +173,7 @@ dir_change_prop(void *dir_baton,
/* We have already notified for this directory, so don't do that again */
SVN_ERR(svn_wc_prop_set4(eb->wc_ctx, db->local_abspath, name, value,
svn_depth_empty, FALSE, NULL,
- NULL, NULL, /* Cancelation */
+ NULL, NULL, /* Cancellation */
NULL, NULL, /* Notification */
scratch_pool));
}
@@ -213,9 +213,10 @@ ensure_added(struct dir_baton_t *db,
db->created = TRUE;
/* Add the directory with all the already collected properties */
- SVN_ERR(svn_wc_add_from_disk2(db->eb->wc_ctx,
+ SVN_ERR(svn_wc_add_from_disk3(db->eb->wc_ctx,
db->local_abspath,
db->properties,
+ TRUE /* skip checks */,
db->eb->notify_func,
db->eb->notify_baton,
scratch_pool));
@@ -306,7 +307,7 @@ file_change_prop(void *file_baton,
}
/* We store all properties in the hash for immediate addition
- with the svn_wc_add_from_disk2() call */
+ with the svn_wc_add_from_disk3() call */
if (! fb->properties)
fb->properties = apr_hash_make(fb->pool);
@@ -375,7 +376,8 @@ file_close(void *file_baton,
fb->pool)));
}
- SVN_ERR(svn_wc_add_from_disk2(eb->wc_ctx, fb->local_abspath, fb->properties,
+ SVN_ERR(svn_wc_add_from_disk3(eb->wc_ctx, fb->local_abspath, fb->properties,
+ TRUE /* skip checks */,
eb->notify_func, eb->notify_baton,
fb->pool));
@@ -526,7 +528,7 @@ svn_client__copy_foreign(const char *url,
for (hi = apr_hash_first(scratch_pool, props); hi;
hi = apr_hash_next(hi))
{
- const char *name = svn__apr_hash_index_key(hi);
+ const char *name = apr_hash_this_key(hi);
if (svn_property_kind2(name) != svn_prop_regular_kind
|| ! strcmp(name, SVN_PROP_MERGEINFO))
@@ -538,12 +540,14 @@ svn_client__copy_foreign(const char *url,
if (!already_locked)
SVN_WC__CALL_WITH_WRITE_LOCK(
- svn_wc_add_from_disk2(ctx->wc_ctx, dst_abspath, props,
+ svn_wc_add_from_disk3(ctx->wc_ctx, dst_abspath, props,
+ TRUE /* skip checks */,
ctx->notify_func2, ctx->notify_baton2,
scratch_pool),
ctx->wc_ctx, dir_abspath, FALSE, scratch_pool);
else
- SVN_ERR(svn_wc_add_from_disk2(ctx->wc_ctx, dst_abspath, props,
+ SVN_ERR(svn_wc_add_from_disk3(ctx->wc_ctx, dst_abspath, props,
+ TRUE /* skip checks */,
ctx->notify_func2, ctx->notify_baton2,
scratch_pool));
}
diff --git a/subversion/libsvn_client/ctx.c b/subversion/libsvn_client/ctx.c
index 185b91e8c6e3..05a5eec2df09 100644
--- a/subversion/libsvn_client/ctx.c
+++ b/subversion/libsvn_client/ctx.c
@@ -27,6 +27,7 @@
/*** Includes. ***/
+#include <stddef.h>
#include <apr_pools.h>
#include "svn_hash.h"
#include "svn_client.h"
@@ -34,6 +35,8 @@
#include "private/svn_wc_private.h"
+#include "client.h"
+
/*** Code. ***/
@@ -76,6 +79,20 @@ call_conflict_func(svn_wc_conflict_result_t **result,
return SVN_NO_ERROR;
}
+/* The magic number in client_ctx_t.magic_id. */
+#define CLIENT_CTX_MAGIC APR_UINT64_C(0xDEADBEEF600DF00D)
+
+svn_client__private_ctx_t *
+svn_client__get_private_ctx(svn_client_ctx_t *ctx)
+{
+ svn_client__private_ctx_t *const private_ctx =
+ (void*)((char *)ctx - offsetof(svn_client__private_ctx_t, public_ctx));
+ SVN_ERR_ASSERT_NO_RETURN(&private_ctx->public_ctx == ctx);
+ SVN_ERR_ASSERT_NO_RETURN(0 == private_ctx->magic_null);
+ SVN_ERR_ASSERT_NO_RETURN(CLIENT_CTX_MAGIC == private_ctx->magic_id);
+ return private_ctx;
+}
+
svn_error_t *
svn_client_create_context2(svn_client_ctx_t **ctx,
apr_hash_t *cfg_hash,
@@ -83,23 +100,29 @@ svn_client_create_context2(svn_client_ctx_t **ctx,
{
svn_config_t *cfg_config;
- *ctx = apr_pcalloc(pool, sizeof(svn_client_ctx_t));
+ svn_client__private_ctx_t *const private_ctx =
+ apr_pcalloc(pool, sizeof(*private_ctx));
+ svn_client_ctx_t *const public_ctx = &private_ctx->public_ctx;
+
+ private_ctx->magic_null = 0;
+ private_ctx->magic_id = CLIENT_CTX_MAGIC;
- (*ctx)->notify_func2 = call_notify_func;
- (*ctx)->notify_baton2 = *ctx;
+ public_ctx->notify_func2 = call_notify_func;
+ public_ctx->notify_baton2 = public_ctx;
- (*ctx)->conflict_func2 = call_conflict_func;
- (*ctx)->conflict_baton2 = *ctx;
+ public_ctx->conflict_func2 = call_conflict_func;
+ public_ctx->conflict_baton2 = public_ctx;
- (*ctx)->config = cfg_hash;
+ public_ctx->config = cfg_hash;
if (cfg_hash)
cfg_config = svn_hash_gets(cfg_hash, SVN_CONFIG_CATEGORY_CONFIG);
else
cfg_config = NULL;
- SVN_ERR(svn_wc_context_create(&(*ctx)->wc_ctx, cfg_config, pool,
- pool));
+ SVN_ERR(svn_wc_context_create(&public_ctx->wc_ctx, cfg_config,
+ pool, pool));
+ *ctx = public_ctx;
return SVN_NO_ERROR;
}
diff --git a/subversion/libsvn_client/delete.c b/subversion/libsvn_client/delete.c
index 803b70c1fb23..943cdd9a4496 100644
--- a/subversion/libsvn_client/delete.c
+++ b/subversion/libsvn_client/delete.c
@@ -258,6 +258,15 @@ single_repos_delete(svn_ra_session_t *ra_session,
editor->abort_edit(edit_baton, pool)));
}
+ if (ctx->notify_func2)
+ {
+ svn_wc_notify_t *notify;
+ notify = svn_wc_create_notify_url(base_uri,
+ svn_wc_notify_commit_finalizing,
+ pool);
+ ctx->notify_func2(ctx->notify_baton2, notify, pool);
+ }
+
/* Close the edit. */
return svn_error_trace(editor->close_edit(edit_baton, pool));
}
@@ -296,7 +305,7 @@ delete_urls_multi_repos(const apr_array_header_t *uris,
for (hi = apr_hash_first(pool, deletables); hi; hi = apr_hash_next(hi))
{
- const char *repos_root = svn__apr_hash_index_key(hi);
+ const char *repos_root = apr_hash_this_key(hi);
repos_relpath = svn_uri_skip_ancestor(repos_root, uri, pool);
if (repos_relpath)
@@ -304,7 +313,7 @@ delete_urls_multi_repos(const apr_array_header_t *uris,
/* Great! We've found another URI underneath this
session. We'll pick out the related RA session for
use later, store the new target, and move on. */
- repos_deletables = svn__apr_hash_index_val(hi);
+ repos_deletables = apr_hash_this_val(hi);
APR_ARRAY_PUSH(repos_deletables->target_uris, const char *) =
apr_pstrdup(pool, uri);
break;
@@ -346,14 +355,14 @@ delete_urls_multi_repos(const apr_array_header_t *uris,
RA error code otherwise for 1.6 compatibility.) */
if (!repos_relpath || !*repos_relpath)
return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
- "URL '%s' not within a repository", uri);
+ _("URL '%s' not within a repository"), uri);
/* Now, test to see if the thing actually exists in HEAD. */
SVN_ERR(svn_ra_check_path(repos_deletables->ra_session, repos_relpath,
SVN_INVALID_REVNUM, &kind, pool));
if (kind == svn_node_none)
return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
- "URL '%s' does not exist", uri);
+ _("URL '%s' does not exist"), uri);
}
/* Now we iterate over the DELETABLES hash, issuing a commit for
@@ -361,7 +370,7 @@ delete_urls_multi_repos(const apr_array_header_t *uris,
iterpool = svn_pool_create(pool);
for (hi = apr_hash_first(pool, deletables); hi; hi = apr_hash_next(hi))
{
- struct repos_deletables_t *repos_deletables = svn__apr_hash_index_val(hi);
+ struct repos_deletables_t *repos_deletables = apr_hash_this_val(hi);
const char *base_uri;
apr_array_header_t *target_relpaths;
@@ -573,7 +582,7 @@ svn_client_delete4(const apr_array_header_t *paths,
for (hi = apr_hash_first(pool, wcroots); hi; hi = apr_hash_next(hi))
{
const char *root_abspath;
- const apr_array_header_t *targets = svn__apr_hash_index_val(hi);
+ const apr_array_header_t *targets = apr_hash_this_val(hi);
svn_pool_clear(iterpool);
diff --git a/subversion/libsvn_client/deprecated.c b/subversion/libsvn_client/deprecated.c
index a67a69bb948a..b1760a4e3738 100644
--- a/subversion/libsvn_client/deprecated.c
+++ b/subversion/libsvn_client/deprecated.c
@@ -627,6 +627,28 @@ svn_client_commit(svn_client_commit_info_t **commit_info_p,
/*** From copy.c ***/
svn_error_t *
+svn_client_copy6(const apr_array_header_t *sources,
+ const char *dst_path,
+ svn_boolean_t copy_as_child,
+ svn_boolean_t make_parents,
+ svn_boolean_t ignore_externals,
+ const apr_hash_t *revprop_table,
+ svn_commit_callback2_t commit_callback,
+ void *commit_baton,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *pool)
+{
+ return svn_error_trace(svn_client_copy7(sources, dst_path, copy_as_child,
+ make_parents, ignore_externals,
+ FALSE /* metadata_only */,
+ FALSE /* pin_externals */,
+ NULL /* externals_to_pin */,
+ revprop_table,
+ commit_callback, commit_baton,
+ ctx, pool));
+}
+
+svn_error_t *
svn_client_copy5(svn_commit_info_t **commit_info_p,
const apr_array_header_t *sources,
const char *dst_path,
@@ -1498,6 +1520,7 @@ svn_client_ls(apr_hash_t **dirents,
}
/*** From log.c ***/
+
svn_error_t *
svn_client_log4(const apr_array_header_t *targets,
const svn_opt_revision_t *peg_revision,
@@ -1970,8 +1993,8 @@ svn_client_propget3(apr_hash_t **props,
for (hi = apr_hash_first(pool, temp_props); hi;
hi = apr_hash_next(hi))
{
- const char *abspath = svn__apr_hash_index_key(hi);
- svn_string_t *value = svn__apr_hash_index_val(hi);
+ const char *abspath = apr_hash_this_key(hi);
+ svn_string_t *value = apr_hash_this_val(hi);
const char *relpath = svn_dirent_join(path_or_url,
svn_dirent_skip_ancestor(target, abspath),
pool);
@@ -2031,9 +2054,9 @@ string_hash_dup(apr_hash_t *hash, apr_pool_t *pool)
for (hi = apr_hash_first(pool, hash); hi; hi = apr_hash_next(hi))
{
- const char *key = apr_pstrdup(pool, svn__apr_hash_index_key(hi));
- apr_ssize_t klen = svn__apr_hash_index_klen(hi);
- svn_string_t *val = svn_string_dup(svn__apr_hash_index_val(hi), pool);
+ const char *key = apr_pstrdup(pool, apr_hash_this_key(hi));
+ apr_ssize_t klen = apr_hash_this_key_len(hi);
+ svn_string_t *val = svn_string_dup(apr_hash_this_val(hi), pool);
apr_hash_set(new_hash, key, klen, val);
}
@@ -2185,6 +2208,28 @@ svn_client_proplist(apr_array_header_t **props,
/*** From status.c ***/
+svn_error_t *
+svn_client_status5(svn_revnum_t *result_rev,
+ svn_client_ctx_t *ctx,
+ const char *path,
+ const svn_opt_revision_t *revision,
+ svn_depth_t depth,
+ svn_boolean_t get_all,
+ svn_boolean_t update,
+ svn_boolean_t no_ignore,
+ svn_boolean_t ignore_externals,
+ svn_boolean_t depth_as_sticky,
+ const apr_array_header_t *changelists,
+ svn_client_status_func_t status_func,
+ void *status_baton,
+ apr_pool_t *scratch_pool)
+{
+ return svn_client_status6(result_rev, ctx, path, revision, depth,
+ get_all, update, TRUE, no_ignore,
+ ignore_externals, depth_as_sticky, changelists,
+ status_func, status_baton, scratch_pool);
+}
+
struct status4_wrapper_baton
{
svn_wc_context_t *wc_ctx;
@@ -2439,6 +2484,21 @@ svn_client_switch(svn_revnum_t *result_rev,
/*** From cat.c ***/
svn_error_t *
+svn_client_cat2(svn_stream_t *out,
+ const char *path_or_url,
+ const svn_opt_revision_t *peg_revision,
+ const svn_opt_revision_t *revision,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *pool)
+{
+ return svn_client_cat3(NULL /* props */,
+ out, path_or_url, peg_revision, revision,
+ TRUE /* expand_keywords */,
+ ctx, pool, pool);
+}
+
+
+svn_error_t *
svn_client_cat(svn_stream_t *out,
const char *path_or_url,
const svn_opt_revision_t *revision,
@@ -2487,6 +2547,32 @@ svn_client_checkout(svn_revnum_t *result_rev,
/*** From info.c ***/
+svn_error_t *
+svn_client_info3(const char *abspath_or_url,
+ const svn_opt_revision_t *peg_revision,
+ const svn_opt_revision_t *revision,
+ svn_depth_t depth,
+ svn_boolean_t fetch_excluded,
+ svn_boolean_t fetch_actual_only,
+ const apr_array_header_t *changelists,
+ svn_client_info_receiver2_t receiver,
+ void *receiver_baton,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *pool)
+{
+ return svn_error_trace(
+ svn_client_info4(abspath_or_url,
+ peg_revision,
+ revision,
+ depth,
+ fetch_excluded,
+ fetch_actual_only,
+ FALSE /* include_externals */,
+ changelists,
+ receiver, receiver_baton,
+ ctx, pool));
+}
+
svn_info_t *
svn_info_dup(const svn_info_t *info, apr_pool_t *pool)
{
@@ -2747,6 +2833,22 @@ svn_client_resolved(const char *path,
}
/*** From revert.c ***/
svn_error_t *
+svn_client_revert2(const apr_array_header_t *paths,
+ svn_depth_t depth,
+ const apr_array_header_t *changelists,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *pool)
+{
+ return svn_error_trace(svn_client_revert3(paths,
+ depth,
+ changelists,
+ FALSE /* clear_changelists */,
+ FALSE /* metadata_only */,
+ ctx,
+ pool));
+}
+
+svn_error_t *
svn_client_revert(const apr_array_header_t *paths,
svn_boolean_t recursive,
svn_client_ctx_t *ctx,
@@ -2783,7 +2885,7 @@ svn_client_uuid_from_url(const char **uuid,
/* destroy the RA session */
svn_pool_destroy(subpool);
- return svn_error_trace(err);;
+ return svn_error_trace(err);
}
svn_error_t *
@@ -2964,3 +3066,24 @@ svn_client_commit_item2_dup(const svn_client_commit_item2_t *item,
return new_item;
}
+svn_error_t *
+svn_client_cleanup(const char *path,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ const char *local_abspath;
+
+ if (svn_path_is_url(path))
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("'%s' is not a local path"), path);
+
+ SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool));
+
+ return svn_error_trace(svn_client_cleanup2(local_abspath,
+ TRUE /* break_locks */,
+ TRUE /* fix_recorded_timestamps */,
+ TRUE /* clear_dav_cache */,
+ TRUE /* vacuum_pristines */,
+ FALSE /* include_externals */,
+ ctx, scratch_pool));
+}
diff --git a/subversion/libsvn_client/diff.c b/subversion/libsvn_client/diff.c
index 26890aec66b5..4817ffdf1016 100644
--- a/subversion/libsvn_client/diff.c
+++ b/subversion/libsvn_client/diff.c
@@ -52,20 +52,22 @@
#include "private/svn_diff_private.h"
#include "private/svn_subr_private.h"
#include "private/svn_io_private.h"
+#include "private/svn_ra_private.h"
#include "svn_private_config.h"
/* Utilities */
+#define DIFF_REVNUM_NONEXISTENT ((svn_revnum_t) -100)
#define MAKE_ERR_BAD_RELATIVE_PATH(path, relative_to_dir) \
svn_error_createf(SVN_ERR_BAD_RELATIVE_PATH, NULL, \
_("Path '%s' must be an immediate child of " \
"the directory '%s'"), path, relative_to_dir)
-/* Calculate the repository relative path of DIFF_RELPATH, using RA_SESSION
- * and WC_CTX, and return the result in *REPOS_RELPATH.
+/* Calculate the repository relative path of DIFF_RELPATH, using
+ * SESSION_RELPATH and WC_CTX, and return the result in *REPOS_RELPATH.
* ORIG_TARGET is the related original target passed to the diff command,
* and may be used to derive leading path components missing from PATH.
* ANCHOR is the local path where the diff editor is anchored.
@@ -74,16 +76,15 @@ static svn_error_t *
make_repos_relpath(const char **repos_relpath,
const char *diff_relpath,
const char *orig_target,
- svn_ra_session_t *ra_session,
+ const char *session_relpath,
svn_wc_context_t *wc_ctx,
const char *anchor,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *local_abspath;
- const char *orig_repos_relpath = NULL;
- if (! ra_session
+ if (! session_relpath
|| (anchor && !svn_path_is_url(orig_target)))
{
svn_error_t *err;
@@ -98,7 +99,7 @@ make_repos_relpath(const char **repos_relpath,
wc_ctx, local_abspath,
result_pool, scratch_pool);
- if (!ra_session
+ if (!session_relpath
|| ! err
|| (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND))
{
@@ -113,23 +114,8 @@ make_repos_relpath(const char **repos_relpath,
svn_error_clear(err);
}
- {
- const char *url;
- const char *repos_root_url;
-
- /* Would be nice if the RA layer could just provide the parent
- repos_relpath of the ra session */
- SVN_ERR(svn_ra_get_session_url(ra_session, &url, scratch_pool));
-
- SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url,
- scratch_pool));
-
- orig_repos_relpath = svn_uri_skip_ancestor(repos_root_url, url,
- scratch_pool);
-
- *repos_relpath = svn_relpath_join(orig_repos_relpath, diff_relpath,
- result_pool);
- }
+ *repos_relpath = svn_relpath_join(session_relpath, diff_relpath,
+ result_pool);
return SVN_NO_ERROR;
}
@@ -169,9 +155,6 @@ adjust_paths_for_diff_labels(const char **index_path,
new_path = ".";
else
return MAKE_ERR_BAD_RELATIVE_PATH(new_path, relative_to_dir);
-
- child_path = svn_dirent_is_child(relative_to_dir, new_path1,
- result_pool);
}
{
@@ -179,7 +162,7 @@ adjust_paths_for_diff_labels(const char **index_path,
svn_boolean_t is_url1;
svn_boolean_t is_url2;
/* ### Holy cow. Due to anchor/target weirdness, we can't
- simply join diff_cmd_baton->orig_path_1 with path, ditto for
+ simply join dwi->orig_path_1 with path, ditto for
orig_path_2. That will work when they're directory URLs, but
not for file URLs. Nor can we just use anchor1 and anchor2
from do_diff(), at least not without some more logic here.
@@ -230,7 +213,7 @@ adjust_paths_for_diff_labels(const char **index_path,
if (new_path2[0] == '\0')
new_path2 = new_path;
else if (svn_path_is_url(new_path2))
- new_path1 = apr_psprintf(result_pool, "%s\t(%s)", new_path, new_path2);
+ new_path2 = apr_psprintf(result_pool, "%s\t(%s)", new_path, new_path2);
else if (new_path2[0] == '/')
new_path2 = apr_psprintf(result_pool, "%s\t(...%s)", new_path, new_path2);
else
@@ -254,9 +237,11 @@ diff_label(const char *path,
apr_pool_t *pool)
{
const char *label;
- if (revnum != SVN_INVALID_REVNUM)
+ if (revnum >= 0)
label = apr_psprintf(pool, _("%s\t(revision %ld)"), path, revnum);
- else
+ else if (revnum == DIFF_REVNUM_NONEXISTENT)
+ label = apr_psprintf(pool, _("%s\t(nonexistent)"), path);
+ else /* SVN_INVALID_REVNUM */
label = apr_psprintf(pool, _("%s\t(working copy)"), path);
return label;
@@ -452,7 +437,9 @@ display_prop_diffs(const apr_array_header_t *propchanges,
const char *relative_to_dir,
svn_boolean_t show_diff_header,
svn_boolean_t use_git_diff_format,
- svn_ra_session_t *ra_session,
+ const char *ra_session_relpath,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
svn_wc_context_t *wc_ctx,
apr_pool_t *scratch_pool)
{
@@ -465,10 +452,10 @@ display_prop_diffs(const apr_array_header_t *propchanges,
if (use_git_diff_format)
{
SVN_ERR(make_repos_relpath(&repos_relpath1, diff_relpath, orig_path1,
- ra_session, wc_ctx, anchor,
+ ra_session_relpath, wc_ctx, anchor,
scratch_pool, scratch_pool));
SVN_ERR(make_repos_relpath(&repos_relpath2, diff_relpath, orig_path2,
- ra_session, wc_ctx, anchor,
+ ra_session_relpath, wc_ctx, anchor,
scratch_pool, scratch_pool));
}
@@ -521,7 +508,9 @@ display_prop_diffs(const apr_array_header_t *propchanges,
SVN_ERR(svn_diff__display_prop_diffs(
outstream, encoding, propchanges, original_props,
- TRUE /* pretty_print_mergeinfo */, scratch_pool));
+ TRUE /* pretty_print_mergeinfo */,
+ -1 /* context_size */,
+ cancel_func, cancel_baton, scratch_pool));
return SVN_NO_ERROR;
}
@@ -530,9 +519,27 @@ display_prop_diffs(const apr_array_header_t *propchanges,
/*** Callbacks for 'svn diff', invoked by the repos-diff editor. ***/
+/* State provided by the diff drivers; used by the diff writer */
+typedef struct diff_driver_info_t
+{
+ /* The anchor to prefix before wc paths */
+ const char *anchor;
-struct diff_cmd_baton {
+ /* Relative path of ra session from repos_root_url */
+ const char *session_relpath;
+ /* The original targets passed to the diff command. We may need
+ these to construct distinctive diff labels when comparing the
+ same relative path in the same revision, under different anchors
+ (for example, when comparing a trunk against a branch). */
+ const char *orig_path_1;
+ const char *orig_path_2;
+} diff_driver_info_t;
+
+
+/* Diff writer state */
+typedef struct diff_writer_info_t
+{
/* If non-null, the external diff command to invoke. */
const char *diff_cmd;
@@ -556,24 +563,6 @@ struct diff_cmd_baton {
const char *header_encoding;
- /* The original targets passed to the diff command. We may need
- these to construct distinctive diff labels when comparing the
- same relative path in the same revision, under different anchors
- (for example, when comparing a trunk against a branch). */
- const char *orig_path_1;
- const char *orig_path_2;
-
- /* These are the numeric representations of the revisions passed to
- svn_client_diff6(), either may be SVN_INVALID_REVNUM. We need these
- because some of the svn_wc_diff_callbacks4_t don't get revision
- arguments.
-
- ### Perhaps we should change the callback signatures and eliminate
- ### these?
- */
- svn_revnum_t revnum1;
- svn_revnum_t revnum2;
-
/* Set this if you want diff output even for binary files. */
svn_boolean_t force_binary;
@@ -597,22 +586,18 @@ struct diff_cmd_baton {
svn_boolean_t no_diff_deleted;
/* Whether to ignore copyfrom information when showing adds */
- svn_boolean_t no_copyfrom_on_add;
+ svn_boolean_t show_copies_as_adds;
/* Empty files for creating diffs or NULL if not used yet */
const char *empty_file;
svn_wc_context_t *wc_ctx;
- /* The RA session used during diffs involving the repository. */
- svn_ra_session_t *ra_session;
-
- /* The anchor to prefix before wc paths */
- const char *anchor;
+ svn_cancel_func_t cancel_func;
+ void *cancel_baton;
- /* Whether the local diff target of a repos->wc diff is a copy. */
- svn_boolean_t repos_wc_diff_target_is_copy;
-};
+ struct diff_driver_info_t ddi;
+} diff_writer_info_t;
/* An helper for diff_dir_props_changed, diff_file_changed and diff_file_added
*/
@@ -620,17 +605,16 @@ static svn_error_t *
diff_props_changed(const char *diff_relpath,
svn_revnum_t rev1,
svn_revnum_t rev2,
- svn_boolean_t dir_was_added,
const apr_array_header_t *propchanges,
apr_hash_t *original_props,
svn_boolean_t show_diff_header,
- struct diff_cmd_baton *diff_cmd_baton,
+ diff_writer_info_t *dwi,
apr_pool_t *scratch_pool)
{
apr_array_header_t *props;
/* If property differences are ignored, there's nothing to do. */
- if (diff_cmd_baton->ignore_properties)
+ if (dwi->ignore_properties)
return SVN_NO_ERROR;
SVN_ERR(svn_categorize_props(propchanges, NULL, NULL, &props,
@@ -638,58 +622,31 @@ diff_props_changed(const char *diff_relpath,
if (props->nelts > 0)
{
- /* We're using the revnums from the diff_cmd_baton since there's
+ /* We're using the revnums from the dwi since there's
* no revision argument to the svn_wc_diff_callback_t
* dir_props_changed(). */
SVN_ERR(display_prop_diffs(props, original_props,
diff_relpath,
- diff_cmd_baton->anchor,
- diff_cmd_baton->orig_path_1,
- diff_cmd_baton->orig_path_2,
+ dwi->ddi.anchor,
+ dwi->ddi.orig_path_1,
+ dwi->ddi.orig_path_2,
rev1,
rev2,
- diff_cmd_baton->header_encoding,
- diff_cmd_baton->outstream,
- diff_cmd_baton->relative_to_dir,
+ dwi->header_encoding,
+ dwi->outstream,
+ dwi->relative_to_dir,
show_diff_header,
- diff_cmd_baton->use_git_diff_format,
- diff_cmd_baton->ra_session,
- diff_cmd_baton->wc_ctx,
+ dwi->use_git_diff_format,
+ dwi->ddi.session_relpath,
+ dwi->cancel_func,
+ dwi->cancel_baton,
+ dwi->wc_ctx,
scratch_pool));
}
return SVN_NO_ERROR;
}
-/* An svn_wc_diff_callbacks4_t function. */
-static svn_error_t *
-diff_dir_props_changed(svn_wc_notify_state_t *state,
- svn_boolean_t *tree_conflicted,
- const char *diff_relpath,
- svn_boolean_t dir_was_added,
- const apr_array_header_t *propchanges,
- apr_hash_t *original_props,
- void *diff_baton,
- apr_pool_t *scratch_pool)
-{
- struct diff_cmd_baton *diff_cmd_baton = diff_baton;
-
- return svn_error_trace(diff_props_changed(diff_relpath,
- /* ### These revs be filled
- * ### with per node info */
- dir_was_added
- ? 0 /* Magic legacy value */
- : diff_cmd_baton->revnum1,
- diff_cmd_baton->revnum2,
- dir_was_added,
- propchanges,
- original_props,
- TRUE /* show_diff_header */,
- diff_cmd_baton,
- scratch_pool));
-}
-
-
/* Show differences between TMPFILE1 and TMPFILE2. DIFF_RELPATH, REV1, and
REV2 are used in the headers to indicate the file and revisions. If either
MIMETYPE1 or MIMETYPE2 indicate binary content, don't show a diff,
@@ -711,26 +668,24 @@ diff_content_changed(svn_boolean_t *wrote_header,
svn_boolean_t force_diff,
const char *copyfrom_path,
svn_revnum_t copyfrom_rev,
- struct diff_cmd_baton *diff_cmd_baton,
+ diff_writer_info_t *dwi,
apr_pool_t *scratch_pool)
{
- int exitcode;
- const char *rel_to_dir = diff_cmd_baton->relative_to_dir;
- svn_stream_t *errstream = diff_cmd_baton->errstream;
- svn_stream_t *outstream = diff_cmd_baton->outstream;
+ const char *rel_to_dir = dwi->relative_to_dir;
+ svn_stream_t *outstream = dwi->outstream;
const char *label1, *label2;
svn_boolean_t mt1_binary = FALSE, mt2_binary = FALSE;
const char *index_path = diff_relpath;
- const char *path1 = diff_cmd_baton->orig_path_1;
- const char *path2 = diff_cmd_baton->orig_path_2;
+ const char *path1 = dwi->ddi.orig_path_1;
+ const char *path2 = dwi->ddi.orig_path_2;
/* If only property differences are shown, there's nothing to do. */
- if (diff_cmd_baton->properties_only)
+ if (dwi->properties_only)
return SVN_NO_ERROR;
/* Generate the diff headers. */
SVN_ERR(adjust_paths_for_diff_labels(&index_path, &path1, &path2,
- rel_to_dir, diff_cmd_baton->anchor,
+ rel_to_dir, dwi->ddi.anchor,
scratch_pool, scratch_pool));
label1 = diff_label(path1, rev1, scratch_pool);
@@ -744,42 +699,82 @@ diff_content_changed(svn_boolean_t *wrote_header,
if (mimetype2)
mt2_binary = svn_mime_type_is_binary(mimetype2);
- if (! diff_cmd_baton->force_binary && (mt1_binary || mt2_binary))
+ if (! dwi->force_binary && (mt1_binary || mt2_binary))
{
/* Print out the diff header. */
SVN_ERR(svn_stream_printf_from_utf8(outstream,
- diff_cmd_baton->header_encoding, scratch_pool,
+ dwi->header_encoding, scratch_pool,
"Index: %s" APR_EOL_STR
SVN_DIFF__EQUAL_STRING APR_EOL_STR,
index_path));
/* ### Print git diff headers. */
- SVN_ERR(svn_stream_printf_from_utf8(outstream,
- diff_cmd_baton->header_encoding, scratch_pool,
- _("Cannot display: file marked as a binary type.%s"),
- APR_EOL_STR));
-
- if (mt1_binary && !mt2_binary)
- SVN_ERR(svn_stream_printf_from_utf8(outstream,
- diff_cmd_baton->header_encoding, scratch_pool,
- "svn:mime-type = %s" APR_EOL_STR, mimetype1));
- else if (mt2_binary && !mt1_binary)
- SVN_ERR(svn_stream_printf_from_utf8(outstream,
- diff_cmd_baton->header_encoding, scratch_pool,
- "svn:mime-type = %s" APR_EOL_STR, mimetype2));
- else if (mt1_binary && mt2_binary)
+ if (dwi->use_git_diff_format)
+ {
+ svn_stream_t *left_stream;
+ svn_stream_t *right_stream;
+ const char *repos_relpath1;
+ const char *repos_relpath2;
+
+ SVN_ERR(make_repos_relpath(&repos_relpath1, diff_relpath,
+ dwi->ddi.orig_path_1,
+ dwi->ddi.session_relpath,
+ dwi->wc_ctx,
+ dwi->ddi.anchor,
+ scratch_pool, scratch_pool));
+ SVN_ERR(make_repos_relpath(&repos_relpath2, diff_relpath,
+ dwi->ddi.orig_path_2,
+ dwi->ddi.session_relpath,
+ dwi->wc_ctx,
+ dwi->ddi.anchor,
+ scratch_pool, scratch_pool));
+ SVN_ERR(print_git_diff_header(outstream, &label1, &label2,
+ operation,
+ repos_relpath1, repos_relpath2,
+ rev1, rev2,
+ copyfrom_path,
+ copyfrom_rev,
+ dwi->header_encoding,
+ scratch_pool));
+
+ SVN_ERR(svn_stream_open_readonly(&left_stream, tmpfile1,
+ scratch_pool, scratch_pool));
+ SVN_ERR(svn_stream_open_readonly(&right_stream, tmpfile2,
+ scratch_pool, scratch_pool));
+ SVN_ERR(svn_diff_output_binary(outstream,
+ left_stream, right_stream,
+ dwi->cancel_func, dwi->cancel_baton,
+ scratch_pool));
+ }
+ else
{
- if (strcmp(mimetype1, mimetype2) == 0)
+ SVN_ERR(svn_stream_printf_from_utf8(outstream,
+ dwi->header_encoding, scratch_pool,
+ _("Cannot display: file marked as a binary type.%s"),
+ APR_EOL_STR));
+
+ if (mt1_binary && !mt2_binary)
SVN_ERR(svn_stream_printf_from_utf8(outstream,
- diff_cmd_baton->header_encoding, scratch_pool,
- "svn:mime-type = %s" APR_EOL_STR,
- mimetype1));
- else
+ dwi->header_encoding, scratch_pool,
+ "svn:mime-type = %s" APR_EOL_STR, mimetype1));
+ else if (mt2_binary && !mt1_binary)
SVN_ERR(svn_stream_printf_from_utf8(outstream,
- diff_cmd_baton->header_encoding, scratch_pool,
- "svn:mime-type = (%s, %s)" APR_EOL_STR,
- mimetype1, mimetype2));
+ dwi->header_encoding, scratch_pool,
+ "svn:mime-type = %s" APR_EOL_STR, mimetype2));
+ else if (mt1_binary && mt2_binary)
+ {
+ if (strcmp(mimetype1, mimetype2) == 0)
+ SVN_ERR(svn_stream_printf_from_utf8(outstream,
+ dwi->header_encoding, scratch_pool,
+ "svn:mime-type = %s" APR_EOL_STR,
+ mimetype1));
+ else
+ SVN_ERR(svn_stream_printf_from_utf8(outstream,
+ dwi->header_encoding, scratch_pool,
+ "svn:mime-type = (%s, %s)" APR_EOL_STR,
+ mimetype1, mimetype2));
+ }
}
/* Exit early. */
@@ -787,17 +782,19 @@ diff_content_changed(svn_boolean_t *wrote_header,
}
- if (diff_cmd_baton->diff_cmd)
+ if (dwi->diff_cmd)
{
+ svn_stream_t *errstream = dwi->errstream;
apr_file_t *outfile;
apr_file_t *errfile;
const char *outfilename;
const char *errfilename;
svn_stream_t *stream;
+ int exitcode;
/* Print out the diff header. */
SVN_ERR(svn_stream_printf_from_utf8(outstream,
- diff_cmd_baton->header_encoding, scratch_pool,
+ dwi->header_encoding, scratch_pool,
"Index: %s" APR_EOL_STR
SVN_DIFF__EQUAL_STRING APR_EOL_STR,
index_path));
@@ -827,12 +824,12 @@ diff_content_changed(svn_boolean_t *wrote_header,
scratch_pool, scratch_pool));
SVN_ERR(svn_io_run_diff2(".",
- diff_cmd_baton->options.for_external.argv,
- diff_cmd_baton->options.for_external.argc,
+ dwi->options.for_external.argv,
+ dwi->options.for_external.argc,
label1, label2,
tmpfile1, tmpfile2,
&exitcode, outfile, errfile,
- diff_cmd_baton->diff_cmd, scratch_pool));
+ dwi->diff_cmd, scratch_pool));
/* Now, open and copy our files to our output streams. */
if (outfilename)
@@ -854,43 +851,44 @@ diff_content_changed(svn_boolean_t *wrote_header,
NULL, NULL, scratch_pool));
}
- /* We have a printed a diff for this path, mark it as visited. */
- *wrote_header = TRUE;
+ /* If we have printed a diff for this path, mark it as visited. */
+ if (exitcode == 1)
+ *wrote_header = TRUE;
}
else /* use libsvn_diff to generate the diff */
{
svn_diff_t *diff;
SVN_ERR(svn_diff_file_diff_2(&diff, tmpfile1, tmpfile2,
- diff_cmd_baton->options.for_internal,
+ dwi->options.for_internal,
scratch_pool));
if (force_diff
- || diff_cmd_baton->use_git_diff_format
+ || dwi->use_git_diff_format
|| svn_diff_contains_diffs(diff))
{
/* Print out the diff header. */
SVN_ERR(svn_stream_printf_from_utf8(outstream,
- diff_cmd_baton->header_encoding, scratch_pool,
+ dwi->header_encoding, scratch_pool,
"Index: %s" APR_EOL_STR
SVN_DIFF__EQUAL_STRING APR_EOL_STR,
index_path));
- if (diff_cmd_baton->use_git_diff_format)
+ if (dwi->use_git_diff_format)
{
const char *repos_relpath1;
const char *repos_relpath2;
SVN_ERR(make_repos_relpath(&repos_relpath1, diff_relpath,
- diff_cmd_baton->orig_path_1,
- diff_cmd_baton->ra_session,
- diff_cmd_baton->wc_ctx,
- diff_cmd_baton->anchor,
+ dwi->ddi.orig_path_1,
+ dwi->ddi.session_relpath,
+ dwi->wc_ctx,
+ dwi->ddi.anchor,
scratch_pool, scratch_pool));
SVN_ERR(make_repos_relpath(&repos_relpath2, diff_relpath,
- diff_cmd_baton->orig_path_2,
- diff_cmd_baton->ra_session,
- diff_cmd_baton->wc_ctx,
- diff_cmd_baton->anchor,
+ dwi->ddi.orig_path_2,
+ dwi->ddi.session_relpath,
+ dwi->wc_ctx,
+ dwi->ddi.anchor,
scratch_pool, scratch_pool));
SVN_ERR(print_git_diff_header(outstream, &label1, &label2,
operation,
@@ -898,20 +896,23 @@ diff_content_changed(svn_boolean_t *wrote_header,
rev1, rev2,
copyfrom_path,
copyfrom_rev,
- diff_cmd_baton->header_encoding,
+ dwi->header_encoding,
scratch_pool));
}
/* Output the actual diff */
if (force_diff || svn_diff_contains_diffs(diff))
- SVN_ERR(svn_diff_file_output_unified3(outstream, diff,
+ SVN_ERR(svn_diff_file_output_unified4(outstream, diff,
tmpfile1, tmpfile2, label1, label2,
- diff_cmd_baton->header_encoding, rel_to_dir,
- diff_cmd_baton->options.for_internal->show_c_function,
+ dwi->header_encoding, rel_to_dir,
+ dwi->options.for_internal->show_c_function,
+ dwi->options.for_internal->context_size,
+ dwi->cancel_func, dwi->cancel_baton,
scratch_pool));
- /* We have a printed a diff for this path, mark it as visited. */
- *wrote_header = TRUE;
+ /* If we have printed a diff for this path, mark it as visited. */
+ if (dwi->use_git_diff_format || svn_diff_contains_diffs(diff))
+ *wrote_header = TRUE;
}
}
@@ -922,62 +923,43 @@ diff_content_changed(svn_boolean_t *wrote_header,
return SVN_NO_ERROR;
}
+/* An svn_diff_tree_processor_t callback. */
static svn_error_t *
-diff_file_opened(svn_boolean_t *tree_conflicted,
- svn_boolean_t *skip,
- const char *diff_relpath,
- svn_revnum_t rev,
- void *diff_baton,
- apr_pool_t *scratch_pool)
-{
- return SVN_NO_ERROR;
-}
-
-/* An svn_wc_diff_callbacks4_t function. */
-static svn_error_t *
-diff_file_changed(svn_wc_notify_state_t *content_state,
- svn_wc_notify_state_t *prop_state,
- svn_boolean_t *tree_conflicted,
- const char *diff_relpath,
- const char *tmpfile1,
- const char *tmpfile2,
- svn_revnum_t rev1,
- svn_revnum_t rev2,
- const char *mimetype1,
- const char *mimetype2,
+diff_file_changed(const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ const char *left_file,
+ const char *right_file,
+ /*const*/ apr_hash_t *left_props,
+ /*const*/ apr_hash_t *right_props,
+ svn_boolean_t file_modified,
const apr_array_header_t *prop_changes,
- apr_hash_t *original_props,
- void *diff_baton,
+ void *file_baton,
+ const struct svn_diff_tree_processor_t *processor,
apr_pool_t *scratch_pool)
{
- struct diff_cmd_baton *diff_cmd_baton = diff_baton;
+ diff_writer_info_t *dwi = processor->baton;
svn_boolean_t wrote_header = FALSE;
- /* During repos->wc diff of a copy revision numbers obtained
- * from the working copy are always SVN_INVALID_REVNUM. */
- if (diff_cmd_baton->repos_wc_diff_target_is_copy)
- {
- if (rev1 == SVN_INVALID_REVNUM &&
- diff_cmd_baton->revnum1 != SVN_INVALID_REVNUM)
- rev1 = diff_cmd_baton->revnum1;
-
- if (rev2 == SVN_INVALID_REVNUM &&
- diff_cmd_baton->revnum2 != SVN_INVALID_REVNUM)
- rev2 = diff_cmd_baton->revnum2;
- }
-
- if (tmpfile1)
- SVN_ERR(diff_content_changed(&wrote_header, diff_relpath,
- tmpfile1, tmpfile2, rev1, rev2,
- mimetype1, mimetype2,
+ if (file_modified)
+ SVN_ERR(diff_content_changed(&wrote_header, relpath,
+ left_file, right_file,
+ left_source->revision,
+ right_source->revision,
+ svn_prop_get_value(left_props,
+ SVN_PROP_MIME_TYPE),
+ svn_prop_get_value(right_props,
+ SVN_PROP_MIME_TYPE),
svn_diff_op_modified, FALSE,
NULL,
- SVN_INVALID_REVNUM, diff_cmd_baton,
+ SVN_INVALID_REVNUM, dwi,
scratch_pool));
if (prop_changes->nelts > 0)
- SVN_ERR(diff_props_changed(diff_relpath, rev1, rev2, FALSE, prop_changes,
- original_props, !wrote_header,
- diff_cmd_baton, scratch_pool));
+ SVN_ERR(diff_props_changed(relpath,
+ left_source->revision,
+ right_source->revision, prop_changes,
+ left_props, !wrote_header,
+ dwi, scratch_pool));
return SVN_NO_ERROR;
}
@@ -985,132 +967,125 @@ diff_file_changed(svn_wc_notify_state_t *content_state,
each of these next two functions, they can be dumb wrappers around
the main workhorse routine. */
-/* An svn_wc_diff_callbacks4_t function. */
+/* An svn_diff_tree_processor_t callback. */
static svn_error_t *
-diff_file_added(svn_wc_notify_state_t *content_state,
- svn_wc_notify_state_t *prop_state,
- svn_boolean_t *tree_conflicted,
- const char *diff_relpath,
- const char *tmpfile1,
- const char *tmpfile2,
- svn_revnum_t rev1,
- svn_revnum_t rev2,
- const char *mimetype1,
- const char *mimetype2,
- const char *copyfrom_path,
- svn_revnum_t copyfrom_revision,
- const apr_array_header_t *prop_changes,
- apr_hash_t *original_props,
- void *diff_baton,
+diff_file_added(const char *relpath,
+ const svn_diff_source_t *copyfrom_source,
+ const svn_diff_source_t *right_source,
+ const char *copyfrom_file,
+ const char *right_file,
+ /*const*/ apr_hash_t *copyfrom_props,
+ /*const*/ apr_hash_t *right_props,
+ void *file_baton,
+ const struct svn_diff_tree_processor_t *processor,
apr_pool_t *scratch_pool)
{
- struct diff_cmd_baton *diff_cmd_baton = diff_baton;
+ diff_writer_info_t *dwi = processor->baton;
svn_boolean_t wrote_header = FALSE;
+ const char *left_file;
+ apr_hash_t *left_props;
+ apr_array_header_t *prop_changes;
/* During repos->wc diff of a copy revision numbers obtained
* from the working copy are always SVN_INVALID_REVNUM. */
- if (diff_cmd_baton->repos_wc_diff_target_is_copy)
+ if (copyfrom_source && !dwi->show_copies_as_adds)
{
- if (rev1 == SVN_INVALID_REVNUM &&
- diff_cmd_baton->revnum1 != SVN_INVALID_REVNUM)
- rev1 = diff_cmd_baton->revnum1;
-
- if (rev2 == SVN_INVALID_REVNUM &&
- diff_cmd_baton->revnum2 != SVN_INVALID_REVNUM)
- rev2 = diff_cmd_baton->revnum2;
+ left_file = copyfrom_file;
+ left_props = copyfrom_props ? copyfrom_props : apr_hash_make(scratch_pool);
}
-
- if (diff_cmd_baton->no_copyfrom_on_add
- && (copyfrom_path || SVN_IS_VALID_REVNUM(copyfrom_revision)))
+ else
{
- apr_hash_t *empty_hash = apr_hash_make(scratch_pool);
- apr_array_header_t *new_changes;
-
- /* Rebase changes on having no left source. */
- if (!diff_cmd_baton->empty_file)
- SVN_ERR(svn_io_open_unique_file3(NULL, &diff_cmd_baton->empty_file,
+ if (!dwi->empty_file)
+ SVN_ERR(svn_io_open_unique_file3(NULL, &dwi->empty_file,
NULL, svn_io_file_del_on_pool_cleanup,
- diff_cmd_baton->pool, scratch_pool));
+ dwi->pool, scratch_pool));
- SVN_ERR(svn_prop_diffs(&new_changes,
- svn_prop__patch(original_props, prop_changes,
- scratch_pool),
- empty_hash,
- scratch_pool));
+ left_file = dwi->empty_file;
+ left_props = apr_hash_make(scratch_pool);
- tmpfile1 = diff_cmd_baton->empty_file;
- prop_changes = new_changes;
- original_props = empty_hash;
- copyfrom_revision = SVN_INVALID_REVNUM;
+ copyfrom_source = NULL;
+ copyfrom_file = NULL;
}
- if (diff_cmd_baton->no_diff_added)
+ SVN_ERR(svn_prop_diffs(&prop_changes, right_props, left_props, scratch_pool));
+
+ if (dwi->no_diff_added)
{
- const char *index_path = diff_relpath;
+ const char *index_path = relpath;
- if (diff_cmd_baton->anchor)
- index_path = svn_dirent_join(diff_cmd_baton->anchor, diff_relpath,
+ if (dwi->ddi.anchor)
+ index_path = svn_dirent_join(dwi->ddi.anchor, relpath,
scratch_pool);
- SVN_ERR(svn_stream_printf_from_utf8(diff_cmd_baton->outstream,
- diff_cmd_baton->header_encoding, scratch_pool,
+ SVN_ERR(svn_stream_printf_from_utf8(dwi->outstream,
+ dwi->header_encoding, scratch_pool,
"Index: %s (added)" APR_EOL_STR
SVN_DIFF__EQUAL_STRING APR_EOL_STR,
index_path));
wrote_header = TRUE;
}
- else if (tmpfile1 && copyfrom_path)
- SVN_ERR(diff_content_changed(&wrote_header, diff_relpath,
- tmpfile1, tmpfile2, rev1, rev2,
- mimetype1, mimetype2,
+ else if (copyfrom_source && right_file)
+ SVN_ERR(diff_content_changed(&wrote_header, relpath,
+ left_file, right_file,
+ copyfrom_source->revision,
+ right_source->revision,
+ svn_prop_get_value(left_props,
+ SVN_PROP_MIME_TYPE),
+ svn_prop_get_value(right_props,
+ SVN_PROP_MIME_TYPE),
svn_diff_op_copied,
TRUE /* force diff output */,
- copyfrom_path,
- copyfrom_revision, diff_cmd_baton,
- scratch_pool));
- else if (tmpfile1)
- SVN_ERR(diff_content_changed(&wrote_header, diff_relpath,
- tmpfile1, tmpfile2, rev1, rev2,
- mimetype1, mimetype2,
+ copyfrom_source->repos_relpath,
+ copyfrom_source->revision,
+ dwi, scratch_pool));
+ else if (right_file)
+ SVN_ERR(diff_content_changed(&wrote_header, relpath,
+ left_file, right_file,
+ DIFF_REVNUM_NONEXISTENT,
+ right_source->revision,
+ svn_prop_get_value(left_props,
+ SVN_PROP_MIME_TYPE),
+ svn_prop_get_value(right_props,
+ SVN_PROP_MIME_TYPE),
svn_diff_op_added,
TRUE /* force diff output */,
NULL, SVN_INVALID_REVNUM,
- diff_cmd_baton, scratch_pool));
+ dwi, scratch_pool));
if (prop_changes->nelts > 0)
- SVN_ERR(diff_props_changed(diff_relpath, rev1, rev2,
- FALSE, prop_changes,
- original_props, ! wrote_header,
- diff_cmd_baton, scratch_pool));
+ SVN_ERR(diff_props_changed(relpath,
+ copyfrom_source ? copyfrom_source->revision
+ : DIFF_REVNUM_NONEXISTENT,
+ right_source->revision,
+ prop_changes,
+ left_props, ! wrote_header,
+ dwi, scratch_pool));
return SVN_NO_ERROR;
}
-/* An svn_wc_diff_callbacks4_t function. */
+/* An svn_diff_tree_processor_t callback. */
static svn_error_t *
-diff_file_deleted(svn_wc_notify_state_t *state,
- svn_boolean_t *tree_conflicted,
- const char *diff_relpath,
- const char *tmpfile1,
- const char *tmpfile2,
- const char *mimetype1,
- const char *mimetype2,
- apr_hash_t *original_props,
- void *diff_baton,
+diff_file_deleted(const char *relpath,
+ const svn_diff_source_t *left_source,
+ const char *left_file,
+ /*const*/ apr_hash_t *left_props,
+ void *file_baton,
+ const struct svn_diff_tree_processor_t *processor,
apr_pool_t *scratch_pool)
{
- struct diff_cmd_baton *diff_cmd_baton = diff_baton;
+ diff_writer_info_t *dwi = processor->baton;
- if (diff_cmd_baton->no_diff_deleted)
+ if (dwi->no_diff_deleted)
{
- const char *index_path = diff_relpath;
+ const char *index_path = relpath;
- if (diff_cmd_baton->anchor)
- index_path = svn_dirent_join(diff_cmd_baton->anchor, diff_relpath,
+ if (dwi->ddi.anchor)
+ index_path = svn_dirent_join(dwi->ddi.anchor, relpath,
scratch_pool);
- SVN_ERR(svn_stream_printf_from_utf8(diff_cmd_baton->outstream,
- diff_cmd_baton->header_encoding, scratch_pool,
+ SVN_ERR(svn_stream_printf_from_utf8(dwi->outstream,
+ dwi->header_encoding, scratch_pool,
"Index: %s (deleted)" APR_EOL_STR
SVN_DIFF__EQUAL_STRING APR_EOL_STR,
index_path));
@@ -1118,99 +1093,144 @@ diff_file_deleted(svn_wc_notify_state_t *state,
else
{
svn_boolean_t wrote_header = FALSE;
- if (tmpfile1)
- SVN_ERR(diff_content_changed(&wrote_header, diff_relpath,
- tmpfile1, tmpfile2,
- diff_cmd_baton->revnum1,
- diff_cmd_baton->revnum2,
- mimetype1, mimetype2,
+
+ if (!dwi->empty_file)
+ SVN_ERR(svn_io_open_unique_file3(NULL, &dwi->empty_file,
+ NULL, svn_io_file_del_on_pool_cleanup,
+ dwi->pool, scratch_pool));
+
+ if (left_file)
+ SVN_ERR(diff_content_changed(&wrote_header, relpath,
+ left_file, dwi->empty_file,
+ left_source->revision,
+ DIFF_REVNUM_NONEXISTENT,
+ svn_prop_get_value(left_props,
+ SVN_PROP_MIME_TYPE),
+ NULL,
svn_diff_op_deleted, FALSE,
NULL, SVN_INVALID_REVNUM,
- diff_cmd_baton,
+ dwi,
scratch_pool));
- /* Should we also report the properties as deleted? */
- }
+ if (left_props && apr_hash_count(left_props))
+ {
+ apr_array_header_t *prop_changes;
- /* We don't list all the deleted properties. */
+ SVN_ERR(svn_prop_diffs(&prop_changes, apr_hash_make(scratch_pool),
+ left_props, scratch_pool));
- return SVN_NO_ERROR;
-}
-
-/* An svn_wc_diff_callbacks4_t function. */
-static svn_error_t *
-diff_dir_added(svn_wc_notify_state_t *state,
- svn_boolean_t *tree_conflicted,
- svn_boolean_t *skip,
- svn_boolean_t *skip_children,
- const char *diff_relpath,
- svn_revnum_t rev,
- const char *copyfrom_path,
- svn_revnum_t copyfrom_revision,
- void *diff_baton,
- apr_pool_t *scratch_pool)
-{
- /* Do nothing. */
+ SVN_ERR(diff_props_changed(relpath,
+ left_source->revision,
+ DIFF_REVNUM_NONEXISTENT,
+ prop_changes,
+ left_props, ! wrote_header,
+ dwi, scratch_pool));
+ }
+ }
return SVN_NO_ERROR;
}
/* An svn_wc_diff_callbacks4_t function. */
static svn_error_t *
-diff_dir_deleted(svn_wc_notify_state_t *state,
- svn_boolean_t *tree_conflicted,
- const char *diff_relpath,
- void *diff_baton,
+diff_dir_changed(const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ /*const*/ apr_hash_t *left_props,
+ /*const*/ apr_hash_t *right_props,
+ const apr_array_header_t *prop_changes,
+ void *dir_baton,
+ const struct svn_diff_tree_processor_t *processor,
apr_pool_t *scratch_pool)
{
- /* Do nothing. */
+ diff_writer_info_t *dwi = processor->baton;
+
+ SVN_ERR(diff_props_changed(relpath,
+ left_source->revision,
+ right_source->revision,
+ prop_changes,
+ left_props,
+ TRUE /* show_diff_header */,
+ dwi,
+ scratch_pool));
return SVN_NO_ERROR;
}
-/* An svn_wc_diff_callbacks4_t function. */
+/* An svn_diff_tree_processor_t callback. */
static svn_error_t *
-diff_dir_opened(svn_boolean_t *tree_conflicted,
- svn_boolean_t *skip,
- svn_boolean_t *skip_children,
- const char *diff_relpath,
- svn_revnum_t rev,
- void *diff_baton,
- apr_pool_t *scratch_pool)
+diff_dir_added(const char *relpath,
+ const svn_diff_source_t *copyfrom_source,
+ const svn_diff_source_t *right_source,
+ /*const*/ apr_hash_t *copyfrom_props,
+ /*const*/ apr_hash_t *right_props,
+ void *dir_baton,
+ const struct svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
{
- /* Do nothing. */
+ diff_writer_info_t *dwi = processor->baton;
+ apr_hash_t *left_props;
+ apr_array_header_t *prop_changes;
- return SVN_NO_ERROR;
+ if (dwi->no_diff_added)
+ return SVN_NO_ERROR;
+
+ if (copyfrom_source && !dwi->show_copies_as_adds)
+ {
+ left_props = copyfrom_props ? copyfrom_props
+ : apr_hash_make(scratch_pool);
+ }
+ else
+ {
+ left_props = apr_hash_make(scratch_pool);
+ copyfrom_source = NULL;
+ }
+
+ SVN_ERR(svn_prop_diffs(&prop_changes, right_props, left_props,
+ scratch_pool));
+
+ return svn_error_trace(diff_props_changed(relpath,
+ copyfrom_source ? copyfrom_source->revision
+ : DIFF_REVNUM_NONEXISTENT,
+ right_source->revision,
+ prop_changes,
+ left_props,
+ TRUE /* show_diff_header */,
+ dwi,
+ scratch_pool));
}
-/* An svn_wc_diff_callbacks4_t function. */
+/* An svn_diff_tree_processor_t callback. */
static svn_error_t *
-diff_dir_closed(svn_wc_notify_state_t *contentstate,
- svn_wc_notify_state_t *propstate,
- svn_boolean_t *tree_conflicted,
- const char *diff_relpath,
- svn_boolean_t dir_was_added,
- void *diff_baton,
- apr_pool_t *scratch_pool)
+diff_dir_deleted(const char *relpath,
+ const svn_diff_source_t *left_source,
+ /*const*/ apr_hash_t *left_props,
+ void *dir_baton,
+ const struct svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
{
- /* Do nothing. */
+ diff_writer_info_t *dwi = processor->baton;
+ apr_array_header_t *prop_changes;
+
+ if (dwi->no_diff_deleted)
+ return SVN_NO_ERROR;
+
+
+ SVN_ERR(svn_prop_diffs(&prop_changes, apr_hash_make(scratch_pool),
+ left_props, scratch_pool));
+
+ SVN_ERR(diff_props_changed(relpath,
+ left_source->revision,
+ DIFF_REVNUM_NONEXISTENT,
+ prop_changes,
+ left_props,
+ TRUE /* show_diff_header */,
+ dwi,
+ scratch_pool));
return SVN_NO_ERROR;
}
-static const svn_wc_diff_callbacks4_t diff_callbacks =
-{
- diff_file_opened,
- diff_file_changed,
- diff_file_added,
- diff_file_deleted,
- diff_dir_deleted,
- diff_dir_opened,
- diff_dir_added,
- diff_dir_props_changed,
- diff_dir_closed
-};
-
/*-----------------------------------------------------------------*/
/** The logic behind 'svn diff' and 'svn merge'. */
@@ -1329,45 +1349,6 @@ check_diff_target_exists(const char *url,
return SVN_NO_ERROR;
}
-
-/* Return in *RESOLVED_URL the URL which PATH_OR_URL@PEG_REVISION has in
- * REVISION. If the object has no location in REVISION, set *RESOLVED_URL
- * to NULL. */
-static svn_error_t *
-resolve_pegged_diff_target_url(const char **resolved_url,
- svn_ra_session_t *ra_session,
- const char *path_or_url,
- const svn_opt_revision_t *peg_revision,
- const svn_opt_revision_t *revision,
- svn_client_ctx_t *ctx,
- apr_pool_t *scratch_pool)
-{
- svn_error_t *err;
-
- /* Check if the PATH_OR_URL exists at REVISION. */
- err = svn_client__repos_locations(resolved_url, NULL,
- NULL, NULL,
- ra_session,
- path_or_url,
- peg_revision,
- revision,
- NULL,
- ctx, scratch_pool);
- if (err)
- {
- if (err->apr_err == SVN_ERR_CLIENT_UNRELATED_RESOURCES ||
- err->apr_err == SVN_ERR_FS_NOT_FOUND)
- {
- svn_error_clear(err);
- *resolved_url = NULL;
- }
- else
- return svn_error_trace(err);
- }
-
- return SVN_NO_ERROR;
-}
-
/** Prepare a repos repos diff between PATH_OR_URL1 and
* PATH_OR_URL2@PEG_REVISION, in the revision range REVISION1:REVISION2.
* Return URLs and peg revisions in *URL1, *REV1 and in *URL2, *REV2.
@@ -1375,13 +1356,10 @@ resolve_pegged_diff_target_url(const char **resolved_url,
* *TARGET1 and *TARGET2, based on *URL1 and *URL2.
* Indicate the corresponding node kinds in *KIND1 and *KIND2, and verify
* that at least one of the diff targets exists.
- * Set *BASE_PATH corresponding to the URL opened in the new *RA_SESSION
- * which is pointing at *ANCHOR1.
* Use client context CTX. Do all allocations in POOL. */
static svn_error_t *
diff_prepare_repos_repos(const char **url1,
const char **url2,
- const char **base_path,
svn_revnum_t *rev1,
svn_revnum_t *rev2,
const char **anchor1,
@@ -1399,92 +1377,135 @@ diff_prepare_repos_repos(const char **url1,
const svn_opt_revision_t *peg_revision,
apr_pool_t *pool)
{
- const char *abspath_or_url2;
- const char *abspath_or_url1;
+ const char *local_abspath1 = NULL;
+ const char *local_abspath2 = NULL;
const char *repos_root_url;
const char *wri_abspath = NULL;
+ svn_client__pathrev_t *resolved1;
+ svn_client__pathrev_t *resolved2 = NULL;
+ enum svn_opt_revision_kind peg_kind = peg_revision->kind;
if (!svn_path_is_url(path_or_url2))
{
- SVN_ERR(svn_dirent_get_absolute(&abspath_or_url2, path_or_url2, pool));
- SVN_ERR(svn_wc__node_get_url(url2, ctx->wc_ctx, abspath_or_url2,
+ SVN_ERR(svn_dirent_get_absolute(&local_abspath2, path_or_url2, pool));
+ SVN_ERR(svn_wc__node_get_url(url2, ctx->wc_ctx, local_abspath2,
pool, pool));
- wri_abspath = abspath_or_url2;
+ wri_abspath = local_abspath2;
}
else
- *url2 = abspath_or_url2 = apr_pstrdup(pool, path_or_url2);
+ *url2 = apr_pstrdup(pool, path_or_url2);
if (!svn_path_is_url(path_or_url1))
{
- SVN_ERR(svn_dirent_get_absolute(&abspath_or_url1, path_or_url1, pool));
- SVN_ERR(svn_wc__node_get_url(url1, ctx->wc_ctx, abspath_or_url1,
- pool, pool));
- wri_abspath = abspath_or_url1;
+ SVN_ERR(svn_dirent_get_absolute(&local_abspath1, path_or_url1, pool));
+ wri_abspath = local_abspath1;
}
- else
- *url1 = abspath_or_url1 = apr_pstrdup(pool, path_or_url1);
-
- /* We need exactly one BASE_PATH, so we'll let the BASE_PATH
- calculated for PATH_OR_URL2 override the one for PATH_OR_URL1
- (since the diff will be "applied" to URL2 anyway). */
- *base_path = NULL;
- if (strcmp(*url1, path_or_url1) != 0)
- *base_path = path_or_url1;
- if (strcmp(*url2, path_or_url2) != 0)
- *base_path = path_or_url2;
SVN_ERR(svn_client_open_ra_session2(ra_session, *url2, wri_abspath,
ctx, pool, pool));
/* If we are performing a pegged diff, we need to find out what our
actual URLs will be. */
- if (peg_revision->kind != svn_opt_revision_unspecified)
+ if (peg_kind != svn_opt_revision_unspecified
+ || path_or_url1 == path_or_url2
+ || local_abspath2)
{
- const char *resolved_url1;
- const char *resolved_url2;
-
- SVN_ERR(resolve_pegged_diff_target_url(&resolved_url2, *ra_session,
- path_or_url2, peg_revision,
- revision2, ctx, pool));
-
- SVN_ERR(svn_ra_reparent(*ra_session, *url1, pool));
- SVN_ERR(resolve_pegged_diff_target_url(&resolved_url1, *ra_session,
- path_or_url1, peg_revision,
- revision1, ctx, pool));
-
- /* Either or both URLs might have changed as a result of resolving
- * the PATH_OR_URL@PEG_REVISION's history. If only one of the URLs
- * could be resolved, use the same URL for URL1 and URL2, so we can
- * show a diff that adds or removes the object (see issue #4153). */
- if (resolved_url2)
+ svn_error_t *err;
+
+ err = svn_client__resolve_rev_and_url(&resolved2,
+ *ra_session, path_or_url2,
+ peg_revision, revision2,
+ ctx, pool);
+ if (err)
{
- *url2 = resolved_url2;
- if (!resolved_url1)
- *url1 = resolved_url2;
+ if (err->apr_err != SVN_ERR_CLIENT_UNRELATED_RESOURCES
+ && err->apr_err != SVN_ERR_FS_NOT_FOUND)
+ return svn_error_trace(err);
+
+ svn_error_clear(err);
+ resolved2 = NULL;
}
- if (resolved_url1)
+ }
+ else
+ resolved2 = NULL;
+
+ if (peg_kind != svn_opt_revision_unspecified
+ || path_or_url1 == path_or_url2
+ || local_abspath1)
+ {
+ svn_error_t *err;
+
+ err = svn_client__resolve_rev_and_url(&resolved1,
+ *ra_session, path_or_url1,
+ peg_revision, revision1,
+ ctx, pool);
+ if (err)
{
- *url1 = resolved_url1;
- if (!resolved_url2)
- *url2 = resolved_url1;
+ if (err->apr_err != SVN_ERR_CLIENT_UNRELATED_RESOURCES
+ && err->apr_err != SVN_ERR_FS_NOT_FOUND)
+ return svn_error_trace(err);
+
+ svn_error_clear(err);
+ resolved1 = NULL;
}
+ }
+ else
+ resolved1 = NULL;
+
+ if (resolved1)
+ {
+ *url1 = resolved1->url;
+ *rev1 = resolved1->rev;
+ }
+ else
+ {
+ /* It would be nice if we could just return an error when resolving a
+ location fails... But in many such cases we prefer diffing against
+ an not existing location to show adds od removes (see issue #4153) */
+
+ if (resolved2
+ && (peg_kind != svn_opt_revision_unspecified
+ || path_or_url1 == path_or_url2))
+ *url1 = resolved2->url;
+ else if (! local_abspath1)
+ *url1 = path_or_url1;
+ else
+ SVN_ERR(svn_wc__node_get_url(url1, ctx->wc_ctx, local_abspath1,
+ pool, pool));
+
+ SVN_ERR(svn_client__get_revision_number(rev1, NULL, ctx->wc_ctx,
+ local_abspath1 /* may be NULL */,
+ *ra_session, revision1, pool));
+ }
- /* Reparent the session, since *URL2 might have changed as a result
- the above call. */
- SVN_ERR(svn_ra_reparent(*ra_session, *url2, pool));
+ if (resolved2)
+ {
+ *url2 = resolved2->url;
+ *rev2 = resolved2->rev;
+ }
+ else
+ {
+ /* It would be nice if we could just return an error when resolving a
+ location fails... But in many such cases we prefer diffing against
+ an not existing location to show adds od removes (see issue #4153) */
+
+ if (resolved1
+ && (peg_kind != svn_opt_revision_unspecified
+ || path_or_url1 == path_or_url2))
+ *url2 = resolved1->url;
+ /* else keep url2 */
+
+ SVN_ERR(svn_client__get_revision_number(rev2, NULL, ctx->wc_ctx,
+ local_abspath2 /* may be NULL */,
+ *ra_session, revision2, pool));
}
/* Resolve revision and get path kind for the second target. */
- SVN_ERR(svn_client__get_revision_number(rev2, NULL, ctx->wc_ctx,
- (path_or_url2 == *url2) ? NULL : abspath_or_url2,
- *ra_session, revision2, pool));
+ SVN_ERR(svn_ra_reparent(*ra_session, *url2, pool));
SVN_ERR(svn_ra_check_path(*ra_session, "", *rev2, kind2, pool));
/* Do the same for the first target. */
SVN_ERR(svn_ra_reparent(*ra_session, *url1, pool));
- SVN_ERR(svn_client__get_revision_number(rev1, NULL, ctx->wc_ctx,
- (strcmp(path_or_url1, *url1) == 0) ? NULL : abspath_or_url1,
- *ra_session, revision1, pool));
SVN_ERR(svn_ra_check_path(*ra_session, "", *rev1, kind1, pool));
/* Either both URLs must exist at their respective revisions,
@@ -1521,12 +1542,37 @@ diff_prepare_repos_repos(const char **url1,
if (strcmp(*url1, repos_root_url) != 0
&& strcmp(*url2, repos_root_url) != 0)
{
+ svn_node_kind_t ignored_kind;
+ svn_error_t *err;
+
svn_uri_split(anchor1, target1, *url1, pool);
svn_uri_split(anchor2, target2, *url2, pool);
- if (*base_path
- && (*kind1 == svn_node_file || *kind2 == svn_node_file))
- *base_path = svn_dirent_dirname(*base_path, pool);
+
SVN_ERR(svn_ra_reparent(*ra_session, *anchor1, pool));
+
+ /* We might not have the necessary rights to read the root now.
+ (It is ok to pass a revision here where the node doesn't exist) */
+ err = svn_ra_check_path(*ra_session, "", *rev1, &ignored_kind, pool);
+
+ if (err && (err->apr_err == SVN_ERR_RA_DAV_FORBIDDEN
+ || err->apr_err == SVN_ERR_RA_NOT_AUTHORIZED))
+ {
+ svn_error_clear(err);
+
+ /* Ok, lets undo the reparent...
+
+ We can't report replacements this way, but at least we can
+ report changes on the descendants */
+
+ *anchor1 = svn_path_url_add_component2(*anchor1, *target1, pool);
+ *anchor2 = svn_path_url_add_component2(*anchor2, *target2, pool);
+ *target1 = "";
+ *target2 = "";
+
+ SVN_ERR(svn_ra_reparent(*ra_session, *anchor1, pool));
+ }
+ else
+ SVN_ERR(err);
}
return SVN_NO_ERROR;
@@ -1553,7 +1599,7 @@ diff_prepare_repos_repos(const char **url1,
Since Subversion 1.8 we also have a variant of svn_wc_diff called
svn_client__arbitrary_nodes_diff, that allows handling WORKING-WORKING
- comparisions between nodes in the working copy.
+ comparisons between nodes in the working copy.
So the truth of the matter is, if the caller's arguments can't be
pigeonholed into one of these use-cases, we currently bail with a
@@ -1583,28 +1629,27 @@ unsupported_diff_error(svn_error_t *child_err)
All other options are the same as those passed to svn_client_diff6(). */
static svn_error_t *
-diff_wc_wc(const char *path1,
+diff_wc_wc(const char **root_relpath,
+ svn_boolean_t *root_is_dir,
+ struct diff_driver_info_t *ddi,
+ const char *path1,
const svn_opt_revision_t *revision1,
const char *path2,
const svn_opt_revision_t *revision2,
svn_depth_t depth,
svn_boolean_t ignore_ancestry,
- svn_boolean_t show_copies_as_adds,
- svn_boolean_t use_git_diff_format,
const apr_array_header_t *changelists,
- const svn_wc_diff_callbacks4_t *callbacks,
- struct diff_cmd_baton *callback_baton,
+ const svn_diff_tree_processor_t *diff_processor,
svn_client_ctx_t *ctx,
- apr_pool_t *pool)
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
const char *abspath1;
- svn_error_t *err;
- svn_node_kind_t kind;
SVN_ERR_ASSERT(! svn_path_is_url(path1));
SVN_ERR_ASSERT(! svn_path_is_url(path2));
- SVN_ERR(svn_dirent_get_absolute(&abspath1, path1, pool));
+ SVN_ERR(svn_dirent_get_absolute(&abspath1, path1, scratch_pool));
/* Currently we support only the case where path1 and path2 are the
same path. */
@@ -1617,42 +1662,25 @@ diff_wc_wc(const char *path1,
"and its working files are supported at this time"
)));
-
- /* Resolve named revisions to real numbers. */
- err = svn_client__get_revision_number(&callback_baton->revnum1, NULL,
- ctx->wc_ctx, abspath1, NULL,
- revision1, pool);
-
- /* In case of an added node, we have no base rev, and we show a revision
- * number of 0. Note that this code is currently always asking for
- * svn_opt_revision_base.
- * ### TODO: get rid of this 0 for added nodes. */
- if (err && (err->apr_err == SVN_ERR_CLIENT_BAD_REVISION))
+ if (ddi)
{
- svn_error_clear(err);
- callback_baton->revnum1 = 0;
- }
- else
- SVN_ERR(err);
+ svn_node_kind_t kind;
- callback_baton->revnum2 = SVN_INVALID_REVNUM; /* WC */
+ SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, abspath1,
+ TRUE, FALSE, scratch_pool));
- SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, abspath1,
- TRUE, FALSE, pool));
+ if (kind != svn_node_dir)
+ ddi->anchor = svn_dirent_dirname(path1, scratch_pool);
+ else
+ ddi->anchor = path1;
+ }
- if (kind != svn_node_dir)
- callback_baton->anchor = svn_dirent_dirname(path1, pool);
- else
- callback_baton->anchor = path1;
-
- SVN_ERR(svn_wc_diff6(ctx->wc_ctx,
- abspath1,
- callbacks, callback_baton,
- depth,
- ignore_ancestry, show_copies_as_adds,
- use_git_diff_format, changelists,
- ctx->cancel_func, ctx->cancel_baton,
- pool));
+ SVN_ERR(svn_wc__diff7(root_relpath, root_is_dir,
+ ctx->wc_ctx, abspath1, depth,
+ ignore_ancestry, changelists,
+ diff_processor,
+ ctx->cancel_func, ctx->cancel_baton,
+ result_pool, scratch_pool));
return SVN_NO_ERROR;
}
@@ -1666,9 +1694,9 @@ diff_wc_wc(const char *path1,
All other options are the same as those passed to svn_client_diff6(). */
static svn_error_t *
-diff_repos_repos(const svn_wc_diff_callbacks4_t *callbacks,
- struct diff_cmd_baton *callback_baton,
- svn_client_ctx_t *ctx,
+diff_repos_repos(const char **root_relpath,
+ svn_boolean_t *root_is_dir,
+ struct diff_driver_info_t *ddi,
const char *path_or_url1,
const char *path_or_url2,
const svn_opt_revision_t *revision1,
@@ -1676,7 +1704,11 @@ diff_repos_repos(const svn_wc_diff_callbacks4_t *callbacks,
const svn_opt_revision_t *peg_revision,
svn_depth_t depth,
svn_boolean_t ignore_ancestry,
- apr_pool_t *pool)
+ svn_boolean_t text_deltas,
+ const svn_diff_tree_processor_t *diff_processor,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
svn_ra_session_t *extra_ra_session;
@@ -1686,11 +1718,8 @@ diff_repos_repos(const svn_wc_diff_callbacks4_t *callbacks,
const svn_delta_editor_t *diff_editor;
void *diff_edit_baton;
- const svn_diff_tree_processor_t *diff_processor;
-
const char *url1;
const char *url2;
- const char *base_path;
svn_revnum_t rev1;
svn_revnum_t rev2;
svn_node_kind_t kind1;
@@ -1700,40 +1729,40 @@ diff_repos_repos(const svn_wc_diff_callbacks4_t *callbacks,
const char *target1;
const char *target2;
svn_ra_session_t *ra_session;
- const char *wri_abspath = NULL;
/* Prepare info for the repos repos diff. */
- SVN_ERR(diff_prepare_repos_repos(&url1, &url2, &base_path, &rev1, &rev2,
+ SVN_ERR(diff_prepare_repos_repos(&url1, &url2, &rev1, &rev2,
&anchor1, &anchor2, &target1, &target2,
&kind1, &kind2, &ra_session,
ctx, path_or_url1, path_or_url2,
revision1, revision2, peg_revision,
- pool));
-
- /* Find a WC path for the ra session */
- if (!svn_path_is_url(path_or_url1))
- SVN_ERR(svn_dirent_get_absolute(&wri_abspath, path_or_url1, pool));
- else if (!svn_path_is_url(path_or_url2))
- SVN_ERR(svn_dirent_get_absolute(&wri_abspath, path_or_url2, pool));
+ scratch_pool));
/* Set up the repos_diff editor on BASE_PATH, if available.
Otherwise, we just use "". */
- SVN_ERR(svn_wc__wrap_diff_callbacks(&diff_processor,
- callbacks, callback_baton,
- TRUE /* walk_deleted_dirs */,
- pool, pool));
-
- /* Get actual URLs. */
- callback_baton->orig_path_1 = url1;
- callback_baton->orig_path_2 = url2;
-
- /* Get numeric revisions. */
- callback_baton->revnum1 = rev1;
- callback_baton->revnum2 = rev2;
+ if (ddi)
+ {
+ /* Get actual URLs. */
+ ddi->orig_path_1 = url1;
+ ddi->orig_path_2 = url2;
+
+ /* This should be moved to the diff writer
+ - path_or_url are provided by the caller
+ - target1 is available as *root_relpath
+ - (kind1 != svn_node_dir || kind2 != svn_node_dir) = !*root_is_dir */
+
+ if (!svn_path_is_url(path_or_url2))
+ ddi->anchor = path_or_url2;
+ else if (!svn_path_is_url(path_or_url1))
+ ddi->anchor = path_or_url1;
+ else
+ ddi->anchor = NULL;
- callback_baton->ra_session = ra_session;
- callback_baton->anchor = base_path;
+ if (*target1 && ddi->anchor
+ && (kind1 != svn_node_dir || kind2 != svn_node_dir))
+ ddi->anchor = svn_dirent_dirname(ddi->anchor, result_pool);
+ }
/* The repository can bring in a new working copy, but not delete
everything. Luckily our new diff handler can just be reversed. */
@@ -1759,45 +1788,65 @@ diff_repos_repos(const svn_wc_diff_callbacks4_t *callbacks,
target1 = str_tmp;
diff_processor = svn_diff__tree_processor_reverse_create(diff_processor,
- NULL, pool);
+ NULL,
+ scratch_pool);
}
/* Filter the first path component using a filter processor, until we fixed
the diff processing to handle this directly */
- if ((kind1 != svn_node_file && kind2 != svn_node_file) && target1[0] != '\0')
- {
- diff_processor = svn_diff__tree_processor_filter_create(diff_processor,
- target1, pool);
- }
+ if (root_relpath)
+ *root_relpath = apr_pstrdup(result_pool, target1);
+ else if ((kind1 != svn_node_file && kind2 != svn_node_file)
+ && target1[0] != '\0')
+ {
+ diff_processor = svn_diff__tree_processor_filter_create(
+ diff_processor, target1, scratch_pool);
+ }
/* Now, we open an extra RA session to the correct anchor
location for URL1. This is used during the editor calls to fetch file
contents. */
- SVN_ERR(svn_client_open_ra_session2(&extra_ra_session, anchor1, wri_abspath,
- ctx, pool, pool));
+ SVN_ERR(svn_ra__dup_session(&extra_ra_session, ra_session, anchor1,
+ scratch_pool, scratch_pool));
+
+ if (ddi)
+ {
+ const char *repos_root_url;
+ const char *session_url;
+
+ SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url,
+ scratch_pool));
+ SVN_ERR(svn_ra_get_session_url(ra_session, &session_url,
+ scratch_pool));
+
+ ddi->session_relpath = svn_uri_skip_ancestor(repos_root_url,
+ session_url,
+ result_pool);
+ }
SVN_ERR(svn_client__get_diff_editor2(
&diff_editor, &diff_edit_baton,
extra_ra_session, depth,
rev1,
- TRUE /* text_deltas */,
+ text_deltas,
diff_processor,
ctx->cancel_func, ctx->cancel_baton,
- pool));
+ scratch_pool));
/* We want to switch our txn into URL2 */
SVN_ERR(svn_ra_do_diff3(ra_session, &reporter, &reporter_baton,
rev2, target1,
- depth, ignore_ancestry, TRUE /* text_deltas */,
- url2, diff_editor, diff_edit_baton, pool));
+ depth, ignore_ancestry, text_deltas,
+ url2, diff_editor, diff_edit_baton, scratch_pool));
/* Drive the reporter; do the diff. */
SVN_ERR(reporter->set_path(reporter_baton, "", rev1,
svn_depth_infinity,
FALSE, NULL,
- pool));
+ scratch_pool));
- return svn_error_trace(reporter->finish_report(reporter_baton, pool));
+ return svn_error_trace(
+ reporter->finish_report(reporter_baton, scratch_pool));
}
/* Perform a diff between a repository path and a working-copy path.
@@ -1811,7 +1860,10 @@ diff_repos_repos(const svn_wc_diff_callbacks4_t *callbacks,
All other options are the same as those passed to svn_client_diff6(). */
static svn_error_t *
-diff_repos_wc(const char *path_or_url1,
+diff_repos_wc(const char **root_relpath,
+ svn_boolean_t *root_is_dir,
+ struct diff_driver_info_t *ddi,
+ const char *path_or_url1,
const svn_opt_revision_t *revision1,
const svn_opt_revision_t *peg_revision,
const char *path2,
@@ -1819,18 +1871,13 @@ diff_repos_wc(const char *path_or_url1,
svn_boolean_t reverse,
svn_depth_t depth,
svn_boolean_t ignore_ancestry,
- svn_boolean_t show_copies_as_adds,
- svn_boolean_t use_git_diff_format,
const apr_array_header_t *changelists,
- const svn_wc_diff_callbacks4_t *callbacks,
- void *callback_baton,
- struct diff_cmd_baton *cmd_baton,
+ const svn_diff_tree_processor_t *diff_processor,
svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
- apr_pool_t *pool = scratch_pool;
- const char *url1, *anchor, *anchor_url, *target;
- svn_revnum_t rev;
+ const char *anchor, *anchor_url, *target;
svn_ra_session_t *ra_session;
svn_depth_t diff_depth;
const svn_ra_reporter3_t *reporter;
@@ -1842,197 +1889,236 @@ diff_repos_wc(const char *path_or_url1,
const char *abspath_or_url1;
const char *abspath2;
const char *anchor_abspath;
- svn_node_kind_t kind1;
- svn_node_kind_t kind2;
svn_boolean_t is_copy;
svn_revnum_t cf_revision;
const char *cf_repos_relpath;
const char *cf_repos_root_url;
+ svn_depth_t cf_depth;
+ const char *copy_root_abspath;
+ const char *target_url;
+ svn_client__pathrev_t *loc1;
SVN_ERR_ASSERT(! svn_path_is_url(path2));
if (!svn_path_is_url(path_or_url1))
{
- SVN_ERR(svn_dirent_get_absolute(&abspath_or_url1, path_or_url1, pool));
- SVN_ERR(svn_wc__node_get_url(&url1, ctx->wc_ctx, abspath_or_url1,
- pool, pool));
+ SVN_ERR(svn_dirent_get_absolute(&abspath_or_url1, path_or_url1,
+ scratch_pool));
}
else
{
- url1 = path_or_url1;
abspath_or_url1 = path_or_url1;
}
- SVN_ERR(svn_dirent_get_absolute(&abspath2, path2, pool));
+ SVN_ERR(svn_dirent_get_absolute(&abspath2, path2, scratch_pool));
- /* Convert path_or_url1 to a URL to feed to do_diff. */
- SVN_ERR(svn_wc_get_actual_target2(&anchor, &target,
- ctx->wc_ctx, path2,
- pool, pool));
+ /* Check if our diff target is a copied node. */
+ SVN_ERR(svn_wc__node_get_origin(&is_copy,
+ &cf_revision,
+ &cf_repos_relpath,
+ &cf_repos_root_url,
+ NULL, &cf_depth,
+ &copy_root_abspath,
+ ctx->wc_ctx, abspath2,
+ FALSE, scratch_pool, scratch_pool));
- /* Fetch the URL of the anchor directory. */
- SVN_ERR(svn_dirent_get_absolute(&anchor_abspath, anchor, pool));
- SVN_ERR(svn_wc__node_get_url(&anchor_url, ctx->wc_ctx, anchor_abspath,
- pool, pool));
- SVN_ERR_ASSERT(anchor_url != NULL);
+ SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &loc1,
+ path_or_url1, abspath2,
+ peg_revision, revision1,
+ ctx, scratch_pool));
- /* If we are performing a pegged diff, we need to find out what our
- actual URLs will be. */
- if (peg_revision->kind != svn_opt_revision_unspecified)
+ if (revision2->kind == svn_opt_revision_base || !is_copy)
{
- SVN_ERR(svn_client__repos_locations(&url1, NULL, NULL, NULL,
- NULL,
- path_or_url1,
- peg_revision,
- revision1, NULL,
- ctx, pool));
- if (!reverse)
+ /* Convert path_or_url1 to a URL to feed to do_diff. */
+ SVN_ERR(svn_wc_get_actual_target2(&anchor, &target, ctx->wc_ctx, path2,
+ scratch_pool, scratch_pool));
+
+ /* Handle the ugly case where target is ".." */
+ if (*target && !svn_path_is_single_path_component(target))
+ {
+ anchor = svn_dirent_join(anchor, target, scratch_pool);
+ target = "";
+ }
+
+ if (root_relpath)
+ *root_relpath = apr_pstrdup(result_pool, target);
+ if (root_is_dir)
+ *root_is_dir = (*target == '\0');
+
+ /* Fetch the URL of the anchor directory. */
+ SVN_ERR(svn_dirent_get_absolute(&anchor_abspath, anchor, scratch_pool));
+ SVN_ERR(svn_wc__node_get_url(&anchor_url, ctx->wc_ctx, anchor_abspath,
+ scratch_pool, scratch_pool));
+ SVN_ERR_ASSERT(anchor_url != NULL);
+
+ target_url = NULL;
+ }
+ else /* is_copy && revision2->kind == svn_opt_revision_base */
+ {
+#if 0
+ svn_node_kind_t kind;
+#endif
+ /* ### Ugly hack ahead ###
+ *
+ * We're diffing a locally copied/moved node.
+ * Describe the copy source to the reporter instead of the copy itself.
+ * Doing the latter would generate a single add_directory() call to the
+ * diff editor which results in an unexpected diff (the copy would
+ * be shown as deleted).
+ *
+ * ### But if we will receive any real changes from the repositor we
+ * will most likely fail to apply them as the wc diff editor assumes
+ * that we have the data to which the change applies in BASE...
+ */
+
+ target_url = svn_path_url_add_component2(cf_repos_root_url,
+ cf_repos_relpath,
+ scratch_pool);
+
+#if 0
+ /*SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, abspath2, FALSE, FALSE,
+ scratch_pool));
+
+ if (kind != svn_node_dir
+ || strcmp(copy_root_abspath, abspath2) != 0) */
+#endif
{
- cmd_baton->orig_path_1 = url1;
- cmd_baton->orig_path_2 =
- svn_path_url_add_component2(anchor_url, target, pool);
+ /* We are looking at a subdirectory of the repository,
+ We can describe the parent directory as the anchor..
+
+ ### This 'appears to work', but that is really dumb luck
+ ### for the simple cases in the test suite */
+ anchor_abspath = svn_dirent_dirname(abspath2, scratch_pool);
+ anchor_url = svn_path_url_add_component2(cf_repos_root_url,
+ svn_relpath_dirname(
+ cf_repos_relpath,
+ scratch_pool),
+ scratch_pool);
+ target = svn_dirent_basename(abspath2, NULL);
+ anchor = svn_dirent_dirname(path2, scratch_pool);
}
+#if 0
else
{
- cmd_baton->orig_path_1 =
- svn_path_url_add_component2(anchor_url, target, pool);
- cmd_baton->orig_path_2 = url1;
+ /* This code, while ok can't be enabled without causing test
+ * failures. The repository will send some changes against
+ * BASE for nodes that don't have BASE...
+ */
+ anchor_abspath = abspath2;
+ anchor_url = svn_path_url_add_component2(cf_repos_root_url,
+ cf_repos_relpath,
+ scratch_pool);
+ anchor = path2;
+ target = "";
}
+#endif
}
- /* Open an RA session to URL1 to figure out its node kind. */
- SVN_ERR(svn_client_open_ra_session2(&ra_session, url1, abspath2,
- ctx, pool, pool));
- /* Resolve the revision to use for URL1. */
- SVN_ERR(svn_client__get_revision_number(&rev, NULL, ctx->wc_ctx,
- (strcmp(path_or_url1, url1) == 0)
- ? NULL : abspath_or_url1,
- ra_session, revision1, pool));
- SVN_ERR(svn_ra_check_path(ra_session, "", rev, &kind1, pool));
-
- /* Figure out the node kind of the local target. */
- SVN_ERR(svn_wc_read_kind2(&kind2, ctx->wc_ctx, abspath2,
- TRUE, FALSE, pool));
-
- cmd_baton->ra_session = ra_session;
- cmd_baton->anchor = anchor;
-
- if (!reverse)
- cmd_baton->revnum1 = rev;
- else
- cmd_baton->revnum2 = rev;
+ SVN_ERR(svn_ra_reparent(ra_session, anchor_url, scratch_pool));
- /* Check if our diff target is a copied node. */
- SVN_ERR(svn_wc__node_get_origin(&is_copy,
- &cf_revision,
- &cf_repos_relpath,
- &cf_repos_root_url,
- NULL, NULL,
- ctx->wc_ctx, abspath2,
- FALSE, pool, pool));
+ if (ddi)
+ {
+ const char *repos_root_url;
+
+ ddi->anchor = anchor;
+
+ if (!reverse)
+ {
+ ddi->orig_path_1 = apr_pstrdup(result_pool, loc1->url);
+ ddi->orig_path_2 =
+ svn_path_url_add_component2(anchor_url, target, result_pool);
+ }
+ else
+ {
+ ddi->orig_path_1 =
+ svn_path_url_add_component2(anchor_url, target, result_pool);
+ ddi->orig_path_2 = apr_pstrdup(result_pool, loc1->url);
+ }
+
+ SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url,
+ scratch_pool));
+
+ ddi->session_relpath = svn_uri_skip_ancestor(repos_root_url,
+ anchor_url,
+ result_pool);
+ }
+
+ if (reverse)
+ diff_processor = svn_diff__tree_processor_reverse_create(
+ diff_processor, NULL, scratch_pool);
/* Use the diff editor to generate the diff. */
SVN_ERR(svn_ra_has_capability(ra_session, &server_supports_depth,
- SVN_RA_CAPABILITY_DEPTH, pool));
+ SVN_RA_CAPABILITY_DEPTH, scratch_pool));
SVN_ERR(svn_wc__get_diff_editor(&diff_editor, &diff_edit_baton,
ctx->wc_ctx,
anchor_abspath,
target,
depth,
- ignore_ancestry || is_copy,
- show_copies_as_adds,
- use_git_diff_format,
+ ignore_ancestry,
rev2_is_base,
reverse,
server_supports_depth,
changelists,
- callbacks, callback_baton,
+ diff_processor,
ctx->cancel_func, ctx->cancel_baton,
- pool, pool));
- SVN_ERR(svn_ra_reparent(ra_session, anchor_url, pool));
+ scratch_pool, scratch_pool));
if (depth != svn_depth_infinity)
diff_depth = depth;
else
diff_depth = svn_depth_unknown;
- if (is_copy)
- {
- const char *copyfrom_parent_url;
- const char *copyfrom_basename;
- svn_depth_t copy_depth;
-
- cmd_baton->repos_wc_diff_target_is_copy = TRUE;
-
- /* We're diffing a locally copied/moved node.
- * Describe the copy source to the reporter instead of the copy itself.
- * Doing the latter would generate a single add_directory() call to the
- * diff editor which results in an unexpected diff (the copy would
- * be shown as deleted). */
-
- if (cf_repos_relpath[0] == '\0')
- {
- copyfrom_parent_url = cf_repos_root_url;
- copyfrom_basename = "";
- }
- else
- {
- const char *parent_relpath;
- svn_relpath_split(&parent_relpath, &copyfrom_basename,
- cf_repos_relpath, scratch_pool);
- copyfrom_parent_url = svn_path_url_add_component2(cf_repos_root_url,
- parent_relpath,
- scratch_pool);
- }
- SVN_ERR(svn_ra_reparent(ra_session, copyfrom_parent_url, pool));
+ if (is_copy && revision2->kind != svn_opt_revision_base)
+ {
/* Tell the RA layer we want a delta to change our txn to URL1 */
SVN_ERR(svn_ra_do_diff3(ra_session,
&reporter, &reporter_baton,
- rev,
+ loc1->rev,
target,
diff_depth,
ignore_ancestry,
TRUE, /* text_deltas */
- url1,
- diff_editor, diff_edit_baton, pool));
+ loc1->url,
+ diff_editor, diff_edit_baton,
+ scratch_pool));
/* Report the copy source. */
- SVN_ERR(svn_wc__node_get_depth(&copy_depth, ctx->wc_ctx, abspath2,
- pool));
+ if (cf_depth == svn_depth_unknown)
+ cf_depth = svn_depth_infinity;
- if (copy_depth == svn_depth_unknown)
- copy_depth = svn_depth_infinity;
+ /* Reporting the in-wc revision as r0, makes the repository send
+ everything as added, which avoids using BASE for pristine information,
+ which is not there (or unrelated) for a copy */
SVN_ERR(reporter->set_path(reporter_baton, "",
- cf_revision,
- copy_depth, FALSE, NULL, scratch_pool));
+ ignore_ancestry ? 0 : cf_revision,
+ cf_depth, FALSE, NULL, scratch_pool));
- if (strcmp(target, copyfrom_basename) != 0)
+ if (*target)
SVN_ERR(reporter->link_path(reporter_baton, target,
- svn_path_url_add_component2(
- cf_repos_root_url,
- cf_repos_relpath,
- scratch_pool),
- cf_revision,
- copy_depth, FALSE, NULL, scratch_pool));
+ target_url,
+ ignore_ancestry ? 0 : cf_revision,
+ cf_depth, FALSE, NULL, scratch_pool));
/* Finish the report to generate the diff. */
- SVN_ERR(reporter->finish_report(reporter_baton, pool));
+ SVN_ERR(reporter->finish_report(reporter_baton, scratch_pool));
}
else
{
/* Tell the RA layer we want a delta to change our txn to URL1 */
SVN_ERR(svn_ra_do_diff3(ra_session,
&reporter, &reporter_baton,
- rev,
+ loc1->rev,
target,
diff_depth,
ignore_ancestry,
TRUE, /* text_deltas */
- url1,
- diff_editor, diff_edit_baton, pool));
+ loc1->url,
+ diff_editor, diff_edit_baton,
+ scratch_pool));
/* Create a txn mirror of path2; the diff editor will print
diffs in reverse. :-) */
@@ -2043,18 +2129,18 @@ diff_repos_wc(const char *path_or_url1,
FALSE,
ctx->cancel_func, ctx->cancel_baton,
NULL, NULL, /* notification is N/A */
- pool));
+ scratch_pool));
}
return SVN_NO_ERROR;
}
-/* This is basically just the guts of svn_client_diff[_peg]6(). */
+/* This is basically just the guts of svn_client_diff[_summarize][_peg]6(). */
static svn_error_t *
-do_diff(const svn_wc_diff_callbacks4_t *callbacks,
- struct diff_cmd_baton *callback_baton,
- svn_client_ctx_t *ctx,
+do_diff(const char **root_relpath,
+ svn_boolean_t *root_is_dir,
+ diff_driver_info_t *ddi,
const char *path_or_url1,
const char *path_or_url2,
const svn_opt_revision_t *revision1,
@@ -2062,10 +2148,12 @@ do_diff(const svn_wc_diff_callbacks4_t *callbacks,
const svn_opt_revision_t *peg_revision,
svn_depth_t depth,
svn_boolean_t ignore_ancestry,
- svn_boolean_t show_copies_as_adds,
- svn_boolean_t use_git_diff_format,
const apr_array_header_t *changelists,
- apr_pool_t *pool)
+ svn_boolean_t text_deltas,
+ const svn_diff_tree_processor_t *diff_processor,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
svn_boolean_t is_repos1;
svn_boolean_t is_repos2;
@@ -2079,32 +2167,35 @@ do_diff(const svn_wc_diff_callbacks4_t *callbacks,
if (is_repos2)
{
/* ### Ignores 'show_copies_as_adds'. */
- SVN_ERR(diff_repos_repos(callbacks, callback_baton, ctx,
+ SVN_ERR(diff_repos_repos(root_relpath, root_is_dir,
+ ddi,
path_or_url1, path_or_url2,
revision1, revision2,
peg_revision, depth, ignore_ancestry,
- pool));
+ text_deltas,
+ diff_processor, ctx,
+ result_pool, scratch_pool));
}
else /* path_or_url2 is a working copy path */
{
- SVN_ERR(diff_repos_wc(path_or_url1, revision1, peg_revision,
+ SVN_ERR(diff_repos_wc(root_relpath, root_is_dir, ddi,
+ path_or_url1, revision1, peg_revision,
path_or_url2, revision2, FALSE, depth,
- ignore_ancestry, show_copies_as_adds,
- use_git_diff_format, changelists,
- callbacks, callback_baton, callback_baton,
- ctx, pool));
+ ignore_ancestry, changelists,
+ diff_processor, ctx,
+ result_pool, scratch_pool));
}
}
else /* path_or_url1 is a working copy path */
{
if (is_repos2)
{
- SVN_ERR(diff_repos_wc(path_or_url2, revision2, peg_revision,
+ SVN_ERR(diff_repos_wc(root_relpath, root_is_dir, ddi,
+ path_or_url2, revision2, peg_revision,
path_or_url1, revision1, TRUE, depth,
- ignore_ancestry, show_copies_as_adds,
- use_git_diff_format, changelists,
- callbacks, callback_baton, callback_baton,
- ctx, pool));
+ ignore_ancestry, changelists,
+ diff_processor, ctx,
+ result_pool, scratch_pool));
}
else /* path_or_url2 is a working copy path */
{
@@ -2114,352 +2205,44 @@ do_diff(const svn_wc_diff_callbacks4_t *callbacks,
const char *abspath1;
const char *abspath2;
- SVN_ERR(svn_dirent_get_absolute(&abspath1, path_or_url1, pool));
- SVN_ERR(svn_dirent_get_absolute(&abspath2, path_or_url2, pool));
+ SVN_ERR(svn_dirent_get_absolute(&abspath1, path_or_url1,
+ scratch_pool));
+ SVN_ERR(svn_dirent_get_absolute(&abspath2, path_or_url2,
+ scratch_pool));
- SVN_ERR(svn_client__arbitrary_nodes_diff(abspath1, abspath2,
+ /* ### What about ddi? */
+
+ SVN_ERR(svn_client__arbitrary_nodes_diff(root_relpath, root_is_dir,
+ abspath1, abspath2,
depth,
- callbacks,
- callback_baton,
- ctx, pool));
+ diff_processor,
+ ctx,
+ result_pool, scratch_pool));
}
else
- SVN_ERR(diff_wc_wc(path_or_url1, revision1,
- path_or_url2, revision2,
- depth, ignore_ancestry, show_copies_as_adds,
- use_git_diff_format, changelists,
- callbacks, callback_baton, ctx, pool));
+ {
+ SVN_ERR(diff_wc_wc(root_relpath, root_is_dir, ddi,
+ path_or_url1, revision1,
+ path_or_url2, revision2,
+ depth, ignore_ancestry, changelists,
+ diff_processor, ctx,
+ result_pool, scratch_pool));
+ }
}
}
return SVN_NO_ERROR;
}
-/* Perform a diff between a repository path and a working-copy path.
-
- PATH_OR_URL1 may be either a URL or a working copy path. PATH2 is a
- working copy path. REVISION1 and REVISION2 are their respective
- revisions. If REVERSE is TRUE, the diff will be done in reverse.
- If PEG_REVISION is specified, then PATH_OR_URL1 is the path in the peg
- revision, and the actual repository path to be compared is
- determined by following copy history.
-
- All other options are the same as those passed to svn_client_diff6(). */
-static svn_error_t *
-diff_summarize_repos_wc(svn_client_diff_summarize_func_t summarize_func,
- void *summarize_baton,
- const char *path_or_url1,
- const svn_opt_revision_t *revision1,
- const svn_opt_revision_t *peg_revision,
- const char *path2,
- const svn_opt_revision_t *revision2,
- svn_boolean_t reverse,
- svn_depth_t depth,
- svn_boolean_t ignore_ancestry,
- const apr_array_header_t *changelists,
- svn_client_ctx_t *ctx,
- apr_pool_t *pool)
-{
- const char *anchor, *target;
- svn_wc_diff_callbacks4_t *callbacks;
- void *callback_baton;
- struct diff_cmd_baton cmd_baton;
-
- SVN_ERR_ASSERT(! svn_path_is_url(path2));
-
- SVN_ERR(svn_wc_get_actual_target2(&anchor, &target,
- ctx->wc_ctx, path2,
- pool, pool));
-
- SVN_ERR(svn_client__get_diff_summarize_callbacks(
- &callbacks, &callback_baton, target, reverse,
- summarize_func, summarize_baton, pool));
-
- SVN_ERR(diff_repos_wc(path_or_url1, revision1, peg_revision,
- path2, revision2, reverse,
- depth, FALSE, TRUE, FALSE, changelists,
- callbacks, callback_baton, &cmd_baton,
- ctx, pool));
- return SVN_NO_ERROR;
-}
-
-/* Perform a summary diff between two working-copy paths.
-
- PATH1 and PATH2 are both working copy paths. REVISION1 and
- REVISION2 are their respective revisions.
-
- All other options are the same as those passed to svn_client_diff6(). */
-static svn_error_t *
-diff_summarize_wc_wc(svn_client_diff_summarize_func_t summarize_func,
- void *summarize_baton,
- const char *path1,
- const svn_opt_revision_t *revision1,
- const char *path2,
- const svn_opt_revision_t *revision2,
- svn_depth_t depth,
- svn_boolean_t ignore_ancestry,
- const apr_array_header_t *changelists,
- svn_client_ctx_t *ctx,
- apr_pool_t *pool)
-{
- svn_wc_diff_callbacks4_t *callbacks;
- void *callback_baton;
- const char *abspath1, *target1;
- svn_node_kind_t kind;
-
- SVN_ERR_ASSERT(! svn_path_is_url(path1));
- SVN_ERR_ASSERT(! svn_path_is_url(path2));
-
- /* Currently we support only the case where path1 and path2 are the
- same path. */
- if ((strcmp(path1, path2) != 0)
- || (! ((revision1->kind == svn_opt_revision_base)
- && (revision2->kind == svn_opt_revision_working))))
- return unsupported_diff_error
- (svn_error_create
- (SVN_ERR_INCORRECT_PARAMS, NULL,
- _("Summarized diffs are only supported between a path's text-base "
- "and its working files at this time")));
-
- /* Find the node kind of PATH1 so that we know whether the diff drive will
- be anchored at PATH1 or its parent dir. */
- SVN_ERR(svn_dirent_get_absolute(&abspath1, path1, pool));
- SVN_ERR(svn_wc_read_kind2(&kind, ctx->wc_ctx, abspath1,
- TRUE, FALSE, pool));
- target1 = (kind == svn_node_dir) ? "" : svn_dirent_basename(path1, pool);
- SVN_ERR(svn_client__get_diff_summarize_callbacks(
- &callbacks, &callback_baton, target1, FALSE,
- summarize_func, summarize_baton, pool));
-
- SVN_ERR(svn_wc_diff6(ctx->wc_ctx,
- abspath1,
- callbacks, callback_baton,
- depth,
- ignore_ancestry, FALSE /* show_copies_as_adds */,
- FALSE /* use_git_diff_format */, changelists,
- ctx->cancel_func, ctx->cancel_baton,
- pool));
- return SVN_NO_ERROR;
-}
-
-/* Perform a diff summary between two repository paths. */
-static svn_error_t *
-diff_summarize_repos_repos(svn_client_diff_summarize_func_t summarize_func,
- void *summarize_baton,
- svn_client_ctx_t *ctx,
- const char *path_or_url1,
- const char *path_or_url2,
- const svn_opt_revision_t *revision1,
- const svn_opt_revision_t *revision2,
- const svn_opt_revision_t *peg_revision,
- svn_depth_t depth,
- svn_boolean_t ignore_ancestry,
- apr_pool_t *pool)
-{
- svn_ra_session_t *extra_ra_session;
-
- const svn_ra_reporter3_t *reporter;
- void *reporter_baton;
-
- const svn_delta_editor_t *diff_editor;
- void *diff_edit_baton;
-
- const svn_diff_tree_processor_t *diff_processor;
-
- const char *url1;
- const char *url2;
- const char *base_path;
- svn_revnum_t rev1;
- svn_revnum_t rev2;
- svn_node_kind_t kind1;
- svn_node_kind_t kind2;
- const char *anchor1;
- const char *anchor2;
- const char *target1;
- const char *target2;
- svn_ra_session_t *ra_session;
- svn_wc_diff_callbacks4_t *callbacks;
- void *callback_baton;
-
- /* Prepare info for the repos repos diff. */
- SVN_ERR(diff_prepare_repos_repos(&url1, &url2, &base_path, &rev1, &rev2,
- &anchor1, &anchor2, &target1, &target2,
- &kind1, &kind2, &ra_session,
- ctx, path_or_url1, path_or_url2,
- revision1, revision2,
- peg_revision, pool));
-
- /* Set up the repos_diff editor. */
- SVN_ERR(svn_client__get_diff_summarize_callbacks(
- &callbacks, &callback_baton,
- target1, FALSE, summarize_func, summarize_baton, pool));
-
- SVN_ERR(svn_wc__wrap_diff_callbacks(&diff_processor,
- callbacks, callback_baton,
- TRUE /* walk_deleted_dirs */,
- pool, pool));
-
-
- /* The repository can bring in a new working copy, but not delete
- everything. Luckily our new diff handler can just be reversed. */
- if (kind2 == svn_node_none)
- {
- const char *str_tmp;
- svn_revnum_t rev_tmp;
-
- str_tmp = url2;
- url2 = url1;
- url1 = str_tmp;
-
- rev_tmp = rev2;
- rev2 = rev1;
- rev1 = rev_tmp;
-
- str_tmp = anchor2;
- anchor2 = anchor1;
- anchor1 = str_tmp;
-
- str_tmp = target2;
- target2 = target1;
- target1 = str_tmp;
-
- diff_processor = svn_diff__tree_processor_reverse_create(diff_processor,
- NULL, pool);
- }
-
- /* Now, we open an extra RA session to the correct anchor
- location for URL1. This is used to get deleted path information. */
- SVN_ERR(svn_client_open_ra_session2(&extra_ra_session, anchor1, NULL,
- ctx, pool, pool));
-
- SVN_ERR(svn_client__get_diff_editor2(&diff_editor, &diff_edit_baton,
- extra_ra_session,
- depth,
- rev1,
- FALSE /* text_deltas */,
- diff_processor,
- ctx->cancel_func, ctx->cancel_baton,
- pool));
-
- /* We want to switch our txn into URL2 */
- SVN_ERR(svn_ra_do_diff3
- (ra_session, &reporter, &reporter_baton, rev2, target1,
- depth, ignore_ancestry,
- FALSE /* do not create text delta */, url2, diff_editor,
- diff_edit_baton, pool));
-
- /* Drive the reporter; do the diff. */
- SVN_ERR(reporter->set_path(reporter_baton, "", rev1,
- svn_depth_infinity,
- FALSE, NULL, pool));
- return svn_error_trace(reporter->finish_report(reporter_baton, pool));
-}
-
-/* This is basically just the guts of svn_client_diff_summarize[_peg]2(). */
-static svn_error_t *
-do_diff_summarize(svn_client_diff_summarize_func_t summarize_func,
- void *summarize_baton,
- svn_client_ctx_t *ctx,
- const char *path_or_url1,
- const char *path_or_url2,
- const svn_opt_revision_t *revision1,
- const svn_opt_revision_t *revision2,
- const svn_opt_revision_t *peg_revision,
- svn_depth_t depth,
- svn_boolean_t ignore_ancestry,
- const apr_array_header_t *changelists,
- apr_pool_t *pool)
-{
- svn_boolean_t is_repos1;
- svn_boolean_t is_repos2;
-
- /* Check if paths/revisions are urls/local. */
- SVN_ERR(check_paths(&is_repos1, &is_repos2, path_or_url1, path_or_url2,
- revision1, revision2, peg_revision));
-
- if (is_repos1)
- {
- if (is_repos2)
- SVN_ERR(diff_summarize_repos_repos(summarize_func, summarize_baton, ctx,
- path_or_url1, path_or_url2,
- revision1, revision2,
- peg_revision, depth, ignore_ancestry,
- pool));
- else
- SVN_ERR(diff_summarize_repos_wc(summarize_func, summarize_baton,
- path_or_url1, revision1,
- peg_revision,
- path_or_url2, revision2,
- FALSE, depth,
- ignore_ancestry,
- changelists,
- ctx, pool));
- }
- else /* ! is_repos1 */
- {
- if (is_repos2)
- SVN_ERR(diff_summarize_repos_wc(summarize_func, summarize_baton,
- path_or_url2, revision2,
- peg_revision,
- path_or_url1, revision1,
- TRUE, depth,
- ignore_ancestry,
- changelists,
- ctx, pool));
- else
- {
- if (revision1->kind == svn_opt_revision_working
- && revision2->kind == svn_opt_revision_working)
- {
- const char *abspath1;
- const char *abspath2;
- svn_wc_diff_callbacks4_t *callbacks;
- void *callback_baton;
- const char *target;
- svn_node_kind_t kind;
-
- SVN_ERR(svn_dirent_get_absolute(&abspath1, path_or_url1, pool));
- SVN_ERR(svn_dirent_get_absolute(&abspath2, path_or_url2, pool));
-
- SVN_ERR(svn_io_check_resolved_path(abspath1, &kind, pool));
-
- if (kind == svn_node_dir)
- target = "";
- else
- target = svn_dirent_basename(path_or_url1, NULL);
-
- SVN_ERR(svn_client__get_diff_summarize_callbacks(
- &callbacks, &callback_baton, target, FALSE,
- summarize_func, summarize_baton, pool));
-
- SVN_ERR(svn_client__arbitrary_nodes_diff(abspath1, abspath2,
- depth,
- callbacks,
- callback_baton,
- ctx, pool));
- }
- else
- SVN_ERR(diff_summarize_wc_wc(summarize_func, summarize_baton,
- path_or_url1, revision1,
- path_or_url2, revision2,
- depth, ignore_ancestry,
- changelists, ctx, pool));
- }
- }
-
- return SVN_NO_ERROR;
-}
-
-
-/* Initialize DIFF_CMD_BATON.diff_cmd and DIFF_CMD_BATON.options,
+/* Initialize DWI.diff_cmd and DWI.options,
* according to OPTIONS and CONFIG. CONFIG and OPTIONS may be null.
- * Allocate the fields in POOL, which should be at least as long-lived
- * as the pool DIFF_CMD_BATON itself is allocated in.
+ * Allocate the fields in RESULT_POOL, which should be at least as long-lived
+ * as the pool DWI itself is allocated in.
*/
static svn_error_t *
-set_up_diff_cmd_and_options(struct diff_cmd_baton *diff_cmd_baton,
- const apr_array_header_t *options,
- apr_hash_t *config, apr_pool_t *pool)
+create_diff_writer_info(diff_writer_info_t *dwi,
+ const apr_array_header_t *options,
+ apr_hash_t *config, apr_pool_t *result_pool)
{
const char *diff_cmd = NULL;
@@ -2475,41 +2258,41 @@ set_up_diff_cmd_and_options(struct diff_cmd_baton *diff_cmd_baton,
svn_config_get(cfg, &diff_extensions, SVN_CONFIG_SECTION_HELPERS,
SVN_CONFIG_OPTION_DIFF_EXTENSIONS, NULL);
if (diff_extensions)
- options = svn_cstring_split(diff_extensions, " \t\n\r", TRUE, pool);
+ options = svn_cstring_split(diff_extensions, " \t\n\r", TRUE,
+ result_pool);
}
}
if (options == NULL)
- options = apr_array_make(pool, 0, sizeof(const char *));
+ options = apr_array_make(result_pool, 0, sizeof(const char *));
if (diff_cmd)
- SVN_ERR(svn_path_cstring_to_utf8(&diff_cmd_baton->diff_cmd, diff_cmd,
- pool));
+ SVN_ERR(svn_path_cstring_to_utf8(&dwi->diff_cmd, diff_cmd,
+ result_pool));
else
- diff_cmd_baton->diff_cmd = NULL;
+ dwi->diff_cmd = NULL;
/* If there was a command, arrange options to pass to it. */
- if (diff_cmd_baton->diff_cmd)
+ if (dwi->diff_cmd)
{
const char **argv = NULL;
int argc = options->nelts;
if (argc)
{
int i;
- argv = apr_palloc(pool, argc * sizeof(char *));
+ argv = apr_palloc(result_pool, argc * sizeof(char *));
for (i = 0; i < argc; i++)
SVN_ERR(svn_utf_cstring_to_utf8(&argv[i],
- APR_ARRAY_IDX(options, i, const char *), pool));
+ APR_ARRAY_IDX(options, i, const char *), result_pool));
}
- diff_cmd_baton->options.for_external.argv = argv;
- diff_cmd_baton->options.for_external.argc = argc;
+ dwi->options.for_external.argv = argv;
+ dwi->options.for_external.argc = argc;
}
else /* No command, so arrange options for internal invocation instead. */
{
- diff_cmd_baton->options.for_internal
- = svn_diff_file_options_create(pool);
- SVN_ERR(svn_diff_file_options_parse
- (diff_cmd_baton->options.for_internal, options, pool));
+ dwi->options.for_internal = svn_diff_file_options_create(result_pool);
+ SVN_ERR(svn_diff_file_options_parse(dwi->options.for_internal,
+ options, result_pool));
}
return SVN_NO_ERROR;
@@ -2574,8 +2357,10 @@ svn_client_diff6(const apr_array_header_t *options,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
- struct diff_cmd_baton diff_cmd_baton = { 0 };
+ diff_writer_info_t dwi = { 0 };
svn_opt_revision_t peg_revision;
+ const svn_diff_tree_processor_t *diff_processor;
+ svn_diff_tree_processor_t *processor;
if (ignore_properties && properties_only)
return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
@@ -2586,36 +2371,54 @@ svn_client_diff6(const apr_array_header_t *options,
peg_revision.kind = svn_opt_revision_unspecified;
/* setup callback and baton */
- diff_cmd_baton.orig_path_1 = path_or_url1;
- diff_cmd_baton.orig_path_2 = path_or_url2;
-
- SVN_ERR(set_up_diff_cmd_and_options(&diff_cmd_baton, options,
- ctx->config, pool));
- diff_cmd_baton.pool = pool;
- diff_cmd_baton.outstream = outstream;
- diff_cmd_baton.errstream = errstream;
- diff_cmd_baton.header_encoding = header_encoding;
- diff_cmd_baton.revnum1 = SVN_INVALID_REVNUM;
- diff_cmd_baton.revnum2 = SVN_INVALID_REVNUM;
-
- diff_cmd_baton.force_binary = ignore_content_type;
- diff_cmd_baton.ignore_properties = ignore_properties;
- diff_cmd_baton.properties_only = properties_only;
- diff_cmd_baton.relative_to_dir = relative_to_dir;
- diff_cmd_baton.use_git_diff_format = use_git_diff_format;
- diff_cmd_baton.no_diff_added = no_diff_added;
- diff_cmd_baton.no_diff_deleted = no_diff_deleted;
- diff_cmd_baton.no_copyfrom_on_add = show_copies_as_adds;
-
- diff_cmd_baton.wc_ctx = ctx->wc_ctx;
- diff_cmd_baton.ra_session = NULL;
- diff_cmd_baton.anchor = NULL;
-
- return do_diff(&diff_callbacks, &diff_cmd_baton, ctx,
- path_or_url1, path_or_url2, revision1, revision2,
- &peg_revision,
- depth, ignore_ancestry, show_copies_as_adds,
- use_git_diff_format, changelists, pool);
+ dwi.ddi.orig_path_1 = path_or_url1;
+ dwi.ddi.orig_path_2 = path_or_url2;
+
+ SVN_ERR(create_diff_writer_info(&dwi, options,
+ ctx->config, pool));
+ dwi.pool = pool;
+ dwi.outstream = outstream;
+ dwi.errstream = errstream;
+ dwi.header_encoding = header_encoding;
+
+ dwi.force_binary = ignore_content_type;
+ dwi.ignore_properties = ignore_properties;
+ dwi.properties_only = properties_only;
+ dwi.relative_to_dir = relative_to_dir;
+ dwi.use_git_diff_format = use_git_diff_format;
+ dwi.no_diff_added = no_diff_added;
+ dwi.no_diff_deleted = no_diff_deleted;
+ dwi.show_copies_as_adds = show_copies_as_adds;
+
+ dwi.cancel_func = ctx->cancel_func;
+ dwi.cancel_baton = ctx->cancel_baton;
+
+ dwi.wc_ctx = ctx->wc_ctx;
+ dwi.ddi.session_relpath = NULL;
+ dwi.ddi.anchor = NULL;
+
+ processor = svn_diff__tree_processor_create(&dwi, pool);
+
+ processor->dir_added = diff_dir_added;
+ processor->dir_changed = diff_dir_changed;
+ processor->dir_deleted = diff_dir_deleted;
+
+ processor->file_added = diff_file_added;
+ processor->file_changed = diff_file_changed;
+ processor->file_deleted = diff_file_deleted;
+
+ diff_processor = processor;
+
+ /* --show-copies-as-adds and --git imply --notice-ancestry */
+ if (show_copies_as_adds || use_git_diff_format)
+ ignore_ancestry = FALSE;
+
+ return svn_error_trace(do_diff(NULL, NULL, &dwi.ddi,
+ path_or_url1, path_or_url2,
+ revision1, revision2, &peg_revision,
+ depth, ignore_ancestry, changelists,
+ TRUE /* text_deltas */,
+ diff_processor, ctx, pool, pool));
}
svn_error_t *
@@ -2641,7 +2444,9 @@ svn_client_diff_peg6(const apr_array_header_t *options,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
- struct diff_cmd_baton diff_cmd_baton = { 0 };
+ diff_writer_info_t dwi = { 0 };
+ const svn_diff_tree_processor_t *diff_processor;
+ svn_diff_tree_processor_t *processor;
if (ignore_properties && properties_only)
return svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
@@ -2649,36 +2454,54 @@ svn_client_diff_peg6(const apr_array_header_t *options,
"properties at the same time"));
/* setup callback and baton */
- diff_cmd_baton.orig_path_1 = path_or_url;
- diff_cmd_baton.orig_path_2 = path_or_url;
-
- SVN_ERR(set_up_diff_cmd_and_options(&diff_cmd_baton, options,
- ctx->config, pool));
- diff_cmd_baton.pool = pool;
- diff_cmd_baton.outstream = outstream;
- diff_cmd_baton.errstream = errstream;
- diff_cmd_baton.header_encoding = header_encoding;
- diff_cmd_baton.revnum1 = SVN_INVALID_REVNUM;
- diff_cmd_baton.revnum2 = SVN_INVALID_REVNUM;
-
- diff_cmd_baton.force_binary = ignore_content_type;
- diff_cmd_baton.ignore_properties = ignore_properties;
- diff_cmd_baton.properties_only = properties_only;
- diff_cmd_baton.relative_to_dir = relative_to_dir;
- diff_cmd_baton.use_git_diff_format = use_git_diff_format;
- diff_cmd_baton.no_diff_added = no_diff_added;
- diff_cmd_baton.no_diff_deleted = no_diff_deleted;
- diff_cmd_baton.no_copyfrom_on_add = show_copies_as_adds;
-
- diff_cmd_baton.wc_ctx = ctx->wc_ctx;
- diff_cmd_baton.ra_session = NULL;
- diff_cmd_baton.anchor = NULL;
-
- return do_diff(&diff_callbacks, &diff_cmd_baton, ctx,
- path_or_url, path_or_url, start_revision, end_revision,
- peg_revision,
- depth, ignore_ancestry, show_copies_as_adds,
- use_git_diff_format, changelists, pool);
+ dwi.ddi.orig_path_1 = path_or_url;
+ dwi.ddi.orig_path_2 = path_or_url;
+
+ SVN_ERR(create_diff_writer_info(&dwi, options,
+ ctx->config, pool));
+ dwi.pool = pool;
+ dwi.outstream = outstream;
+ dwi.errstream = errstream;
+ dwi.header_encoding = header_encoding;
+
+ dwi.force_binary = ignore_content_type;
+ dwi.ignore_properties = ignore_properties;
+ dwi.properties_only = properties_only;
+ dwi.relative_to_dir = relative_to_dir;
+ dwi.use_git_diff_format = use_git_diff_format;
+ dwi.no_diff_added = no_diff_added;
+ dwi.no_diff_deleted = no_diff_deleted;
+ dwi.show_copies_as_adds = show_copies_as_adds;
+
+ dwi.cancel_func = ctx->cancel_func;
+ dwi.cancel_baton = ctx->cancel_baton;
+
+ dwi.wc_ctx = ctx->wc_ctx;
+ dwi.ddi.session_relpath = NULL;
+ dwi.ddi.anchor = NULL;
+
+ processor = svn_diff__tree_processor_create(&dwi, pool);
+
+ processor->dir_added = diff_dir_added;
+ processor->dir_changed = diff_dir_changed;
+ processor->dir_deleted = diff_dir_deleted;
+
+ processor->file_added = diff_file_added;
+ processor->file_changed = diff_file_changed;
+ processor->file_deleted = diff_file_deleted;
+
+ diff_processor = processor;
+
+ /* --show-copies-as-adds and --git imply --notice-ancestry */
+ if (show_copies_as_adds || use_git_diff_format)
+ ignore_ancestry = FALSE;
+
+ return svn_error_trace(do_diff(NULL, NULL, &dwi.ddi,
+ path_or_url, path_or_url,
+ start_revision, end_revision, peg_revision,
+ depth, ignore_ancestry, changelists,
+ TRUE /* text_deltas */,
+ diff_processor, ctx, pool, pool));
}
svn_error_t *
@@ -2694,14 +2517,24 @@ svn_client_diff_summarize2(const char *path_or_url1,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
- /* We will never do a pegged diff from here. */
+ const svn_diff_tree_processor_t *diff_processor;
svn_opt_revision_t peg_revision;
+ const char **p_root_relpath;
+
+ /* We will never do a pegged diff from here. */
peg_revision.kind = svn_opt_revision_unspecified;
- return do_diff_summarize(summarize_func, summarize_baton, ctx,
- path_or_url1, path_or_url2, revision1, revision2,
- &peg_revision,
- depth, ignore_ancestry, changelists, pool);
+ SVN_ERR(svn_client__get_diff_summarize_callbacks(
+ &diff_processor, &p_root_relpath,
+ summarize_func, summarize_baton,
+ path_or_url1, pool, pool));
+
+ return svn_error_trace(do_diff(p_root_relpath, NULL, NULL,
+ path_or_url1, path_or_url2,
+ revision1, revision2, &peg_revision,
+ depth, ignore_ancestry, changelists,
+ FALSE /* text_deltas */,
+ diff_processor, ctx, pool, pool));
}
svn_error_t *
@@ -2717,22 +2550,19 @@ svn_client_diff_summarize_peg2(const char *path_or_url,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
- return do_diff_summarize(summarize_func, summarize_baton, ctx,
- path_or_url, path_or_url,
- start_revision, end_revision, peg_revision,
- depth, ignore_ancestry, changelists, pool);
-}
-
-svn_client_diff_summarize_t *
-svn_client_diff_summarize_dup(const svn_client_diff_summarize_t *diff,
- apr_pool_t *pool)
-{
- svn_client_diff_summarize_t *dup_diff = apr_palloc(pool, sizeof(*dup_diff));
-
- *dup_diff = *diff;
-
- if (diff->path)
- dup_diff->path = apr_pstrdup(pool, diff->path);
+ const svn_diff_tree_processor_t *diff_processor;
+ const char **p_root_relpath;
- return dup_diff;
+ SVN_ERR(svn_client__get_diff_summarize_callbacks(
+ &diff_processor, &p_root_relpath,
+ summarize_func, summarize_baton,
+ path_or_url, pool, pool));
+
+ return svn_error_trace(do_diff(p_root_relpath, NULL, NULL,
+ path_or_url, path_or_url,
+ start_revision, end_revision, peg_revision,
+ depth, ignore_ancestry, changelists,
+ FALSE /* text_deltas */,
+ diff_processor, ctx, pool, pool));
}
+
diff --git a/subversion/libsvn_client/diff_local.c b/subversion/libsvn_client/diff_local.c
index 2dd8a1b67da0..df6bc0a8c009 100644
--- a/subversion/libsvn_client/diff_local.c
+++ b/subversion/libsvn_client/diff_local.c
@@ -45,7 +45,9 @@
#include "svn_subst.h"
#include "client.h"
+#include "private/svn_sorts_private.h"
#include "private/svn_wc_private.h"
+#include "private/svn_diff_tree.h"
#include "svn_private_config.h"
@@ -81,559 +83,718 @@ get_props(apr_hash_t **props,
return SVN_NO_ERROR;
}
-/* Produce a diff between two arbitrary files at LOCAL_ABSPATH1 and
- * LOCAL_ABSPATH2, using the diff callbacks from CALLBACKS.
- * Use PATH as the name passed to diff callbacks.
- * FILE1_IS_EMPTY and FILE2_IS_EMPTY are used as hints which diff callback
- * function to use to compare the files (added/deleted/changed).
+/* Forward declaration */
+static svn_error_t *
+do_file_diff(const char *left_abspath,
+ const char *right_abspath,
+ const char *left_root_abspath,
+ const char *right_root_abspath,
+ svn_boolean_t left_only,
+ svn_boolean_t right_only,
+ void *parent_baton,
+ const svn_diff_tree_processor_t *diff_processor,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool);
+
+/* Forward declaration */
+static svn_error_t *
+do_dir_diff(const char *left_abspath,
+ const char *right_abspath,
+ const char *left_root_abspath,
+ const char *right_root_abspath,
+ svn_boolean_t left_only,
+ svn_boolean_t right_only,
+ svn_boolean_t left_before_right,
+ svn_depth_t depth,
+ void *parent_baton,
+ const svn_diff_tree_processor_t *diff_processor,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool);
+
+/* Produce a diff of depth DEPTH between two arbitrary directories at
+ * LEFT_ABSPATH1 and RIGHT_ABSPATH2, using the provided diff callbacks
+ * to show file changes and, for versioned nodes, property changes.
+ *
+ * Report paths as relative from LEFT_ROOT_ABSPATH/RIGHT_ROOT_ABSPATH.
*
- * If ORIGINAL_PROPS_OVERRIDE is not NULL, use it as original properties
- * instead of reading properties from LOCAL_ABSPATH1. This is required when
- * a file replaces a directory, where LOCAL_ABSPATH1 is an empty file that
- * file content must be diffed against, but properties to diff against come
- * from the replaced directory. */
+ * If LEFT_ONLY is TRUE, only the left source exists (= everything will
+ * be reported as deleted). If RIGHT_ONLY is TRUE, only the right source
+ * exists (= everything will be reported as added).
+ *
+ * If LEFT_BEFORE_RIGHT is TRUE and left and right are unrelated, left is
+ * reported first. If false, right is reported first. (This is to allow
+ * producing a proper inverse diff).
+ *
+ * Walk the sources according to depth, and report with parent baton
+ * PARENT_BATON. */
static svn_error_t *
-do_arbitrary_files_diff(const char *local_abspath1,
- const char *local_abspath2,
- const char *path,
- svn_boolean_t file1_is_empty,
- svn_boolean_t file2_is_empty,
- apr_hash_t *original_props_override,
- const svn_wc_diff_callbacks4_t *callbacks,
- void *diff_baton,
- svn_client_ctx_t *ctx,
- apr_pool_t *scratch_pool)
+inner_dir_diff(const char *left_abspath,
+ const char *right_abspath,
+ const char *left_root_abspath,
+ const char *right_root_abspath,
+ svn_boolean_t left_only,
+ svn_boolean_t right_only,
+ svn_boolean_t left_before_right,
+ svn_depth_t depth,
+ void *parent_baton,
+ const svn_diff_tree_processor_t *diff_processor,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
{
- apr_hash_t *original_props;
- apr_hash_t *modified_props;
- apr_array_header_t *prop_changes;
- svn_string_t *original_mime_type = NULL;
- svn_string_t *modified_mime_type = NULL;
-
- if (ctx->cancel_func)
- SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
-
- /* Try to get properties from either file. It's OK if the files do not
- * have properties, or if they are unversioned. */
- if (original_props_override)
- original_props = original_props_override;
- else
- SVN_ERR(get_props(&original_props, local_abspath1, ctx->wc_ctx,
- scratch_pool, scratch_pool));
- SVN_ERR(get_props(&modified_props, local_abspath2, ctx->wc_ctx,
- scratch_pool, scratch_pool));
+ apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+ apr_hash_t *left_dirents;
+ apr_hash_t *right_dirents;
+ apr_array_header_t *sorted_dirents;
+ svn_error_t *err;
+ svn_depth_t depth_below_here;
+ int i;
- SVN_ERR(svn_prop_diffs(&prop_changes, modified_props, original_props,
- scratch_pool));
+ SVN_ERR_ASSERT(depth >= svn_depth_files && depth <= svn_depth_infinity);
- /* Try to determine the mime-type of each file. */
- original_mime_type = svn_hash_gets(original_props, SVN_PROP_MIME_TYPE);
- if (!file1_is_empty && !original_mime_type)
+ if (!right_only)
{
- const char *mime_type;
- SVN_ERR(svn_io_detect_mimetype2(&mime_type, local_abspath1,
- ctx->mimetypes_map, scratch_pool));
+ err = svn_io_get_dirents3(&left_dirents, left_abspath, FALSE,
+ scratch_pool, iterpool);
- if (mime_type)
- original_mime_type = svn_string_create(mime_type, scratch_pool);
+ if (err && (APR_STATUS_IS_ENOENT(err->apr_err)
+ || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)))
+ {
+ svn_error_clear(err);
+ left_dirents = apr_hash_make(scratch_pool);
+ right_only = TRUE;
+ }
+ else
+ SVN_ERR(err);
}
+ else
+ left_dirents = apr_hash_make(scratch_pool);
- modified_mime_type = svn_hash_gets(modified_props, SVN_PROP_MIME_TYPE);
- if (!file2_is_empty && !modified_mime_type)
+ if (!left_only)
{
- const char *mime_type;
- SVN_ERR(svn_io_detect_mimetype2(&mime_type, local_abspath1,
- ctx->mimetypes_map, scratch_pool));
+ err = svn_io_get_dirents3(&right_dirents, right_abspath, FALSE,
+ scratch_pool, iterpool);
- if (mime_type)
- modified_mime_type = svn_string_create(mime_type, scratch_pool);
+ if (err && (APR_STATUS_IS_ENOENT(err->apr_err)
+ || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)))
+ {
+ svn_error_clear(err);
+ right_dirents = apr_hash_make(scratch_pool);
+ right_only = TRUE;
+ }
+ else
+ SVN_ERR(err);
}
+ else
+ right_dirents = apr_hash_make(scratch_pool);
+
+ if (left_only && right_only)
+ return SVN_NO_ERROR; /* Somebody deleted the directory?? */
- /* Produce the diff. */
- if (file1_is_empty && !file2_is_empty)
- SVN_ERR(callbacks->file_added(NULL, NULL, NULL, path,
- local_abspath1, local_abspath2,
- /* ### TODO get real revision info
- * for versioned files? */
- SVN_INVALID_REVNUM, SVN_INVALID_REVNUM,
- original_mime_type ?
- original_mime_type->data : NULL,
- modified_mime_type ?
- modified_mime_type->data : NULL,
- /* ### TODO get copyfrom? */
- NULL, SVN_INVALID_REVNUM,
- prop_changes, original_props,
- diff_baton, scratch_pool));
- else if (!file1_is_empty && file2_is_empty)
- SVN_ERR(callbacks->file_deleted(NULL, NULL, path,
- local_abspath1, local_abspath2,
- original_mime_type ?
- original_mime_type->data : NULL,
- modified_mime_type ?
- modified_mime_type->data : NULL,
- original_props,
- diff_baton, scratch_pool));
+ if (depth != svn_depth_infinity)
+ depth_below_here = svn_depth_empty;
else
+ depth_below_here = svn_depth_infinity;
+
+ sorted_dirents = svn_sort__hash(apr_hash_merge(iterpool, left_dirents,
+ right_dirents, NULL, NULL),
+ svn_sort_compare_items_as_paths,
+ scratch_pool);
+
+ for (i = 0; i < sorted_dirents->nelts; i++)
{
- svn_stream_t *file1;
- svn_stream_t *file2;
- svn_boolean_t same;
- svn_string_t *val;
- /* We have two files, which may or may not be the same.
+ svn_sort__item_t* elt = &APR_ARRAY_IDX(sorted_dirents, i, svn_sort__item_t);
+ svn_io_dirent2_t *left_dirent;
+ svn_io_dirent2_t *right_dirent;
+ const char *child_left_abspath;
+ const char *child_right_abspath;
- ### Our caller assumes that we should ignore symlinks here and
- handle them as normal paths. Perhaps that should change?
- */
- SVN_ERR(svn_stream_open_readonly(&file1, local_abspath1, scratch_pool,
- scratch_pool));
+ svn_pool_clear(iterpool);
- SVN_ERR(svn_stream_open_readonly(&file2, local_abspath2, scratch_pool,
- scratch_pool));
+ if (ctx->cancel_func)
+ SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
- /* Wrap with normalization, etc. if necessary */
- if (original_props)
- {
- val = svn_hash_gets(original_props, SVN_PROP_EOL_STYLE);
+ if (svn_wc_is_adm_dir(elt->key, iterpool))
+ continue;
- if (val)
+ left_dirent = right_only ? NULL : svn_hash_gets(left_dirents, elt->key);
+ right_dirent = left_only ? NULL : svn_hash_gets(right_dirents, elt->key);
+
+ child_left_abspath = svn_dirent_join(left_abspath, elt->key, iterpool);
+ child_right_abspath = svn_dirent_join(right_abspath, elt->key, iterpool);
+
+ if (((left_dirent == NULL) != (right_dirent == NULL))
+ || (left_dirent->kind != right_dirent->kind))
+ {
+ /* Report delete and/or add */
+ if (left_dirent && left_before_right)
{
- svn_subst_eol_style_t style;
- const char *eol;
- svn_subst_eol_style_from_value(&style, &eol, val->data);
-
- /* ### Ignoring keywords */
- if (eol)
- file1 = svn_subst_stream_translated(file1, eol, TRUE,
- NULL, FALSE,
- scratch_pool);
+ if (left_dirent->kind == svn_node_file)
+ SVN_ERR(do_file_diff(child_left_abspath, child_right_abspath,
+ left_root_abspath, right_root_abspath,
+ TRUE, FALSE, parent_baton,
+ diff_processor, ctx, iterpool));
+ else if (depth >= svn_depth_immediates)
+ SVN_ERR(do_dir_diff(child_left_abspath, child_right_abspath,
+ left_root_abspath, right_root_abspath,
+ TRUE, FALSE, left_before_right,
+ depth_below_here, parent_baton,
+ diff_processor, ctx, iterpool));
}
- }
- if (modified_props)
- {
- val = svn_hash_gets(modified_props, SVN_PROP_EOL_STYLE);
+ if (right_dirent)
+ {
+ if (right_dirent->kind == svn_node_file)
+ SVN_ERR(do_file_diff(child_left_abspath, child_right_abspath,
+ left_root_abspath, right_root_abspath,
+ FALSE, TRUE, parent_baton,
+ diff_processor, ctx, iterpool));
+ else if (depth >= svn_depth_immediates)
+ SVN_ERR(do_dir_diff(child_left_abspath, child_right_abspath,
+ left_root_abspath, right_root_abspath,
+ FALSE, TRUE, left_before_right,
+ depth_below_here, parent_baton,
+ diff_processor, ctx, iterpool));
+ }
- if (val)
+ if (left_dirent && !left_before_right)
{
- svn_subst_eol_style_t style;
- const char *eol;
- svn_subst_eol_style_from_value(&style, &eol, val->data);
-
- /* ### Ignoring keywords */
- if (eol)
- file2 = svn_subst_stream_translated(file2, eol, TRUE,
- NULL, FALSE,
- scratch_pool);
+ if (left_dirent->kind == svn_node_file)
+ SVN_ERR(do_file_diff(child_left_abspath, child_right_abspath,
+ left_root_abspath, right_root_abspath,
+ TRUE, FALSE, parent_baton,
+ diff_processor, ctx, iterpool));
+ else if (depth >= svn_depth_immediates)
+ SVN_ERR(do_dir_diff(child_left_abspath, child_right_abspath,
+ left_root_abspath, right_root_abspath,
+ TRUE, FALSE, left_before_right,
+ depth_below_here, parent_baton,
+ diff_processor, ctx, iterpool));
}
}
-
- SVN_ERR(svn_stream_contents_same2(&same, file1, file2, scratch_pool));
-
- if (! same || prop_changes->nelts > 0)
+ else if (left_dirent->kind == svn_node_file)
{
- /* ### We should probably pass the normalized data we created using
- the subst streams as that is what diff users expect */
- SVN_ERR(callbacks->file_changed(NULL, NULL, NULL, path,
- same ? NULL : local_abspath1,
- same ? NULL : local_abspath2,
- /* ### TODO get real revision info
- * for versioned files? */
- SVN_INVALID_REVNUM /* rev1 */,
- SVN_INVALID_REVNUM /* rev2 */,
- original_mime_type ?
- original_mime_type->data : NULL,
- modified_mime_type ?
- modified_mime_type->data : NULL,
- prop_changes, original_props,
- diff_baton, scratch_pool));
+ /* Perform file-file diff */
+ SVN_ERR(do_file_diff(child_left_abspath, child_right_abspath,
+ left_root_abspath, right_root_abspath,
+ FALSE, FALSE, parent_baton,
+ diff_processor, ctx, iterpool));
+ }
+ else if (depth >= svn_depth_immediates)
+ {
+ /* Perform dir-dir diff */
+ SVN_ERR(do_dir_diff(child_left_abspath, child_right_abspath,
+ left_root_abspath, right_root_abspath,
+ FALSE, FALSE, left_before_right,
+ depth_below_here, parent_baton,
+ diff_processor, ctx, iterpool));
}
}
return SVN_NO_ERROR;
}
-struct arbitrary_diff_walker_baton {
- /* The root directories of the trees being compared. */
- const char *root1_abspath;
- const char *root2_abspath;
-
- /* TRUE if recursing within an added subtree of root2_abspath that
- * does not exist in root1_abspath. */
- svn_boolean_t recursing_within_added_subtree;
+/* Translates *LEFT_ABSPATH to a temporary file if PROPS specify that the
+ file needs translation. *LEFT_ABSPATH is updated to point to a file that
+ lives at least as long as RESULT_POOL when translation is necessary.
+ Otherwise the value is not updated */
+static svn_error_t *
+translate_if_necessary(const char **local_abspath,
+ apr_hash_t *props,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ const svn_string_t *eol_style_val;
+ const svn_string_t *keywords_val;
+ svn_subst_eol_style_t eol_style;
+ const char *eol;
+ apr_hash_t *keywords;
+ svn_stream_t *contents;
+ svn_stream_t *dst;
+
+ /* if (svn_hash_gets(props, SVN_PROP_SPECIAL))
+ ### TODO: Implement */
+
+ eol_style_val = svn_hash_gets(props, SVN_PROP_EOL_STYLE);
+ keywords_val = svn_hash_gets(props, SVN_PROP_KEYWORDS);
+
+ if (eol_style_val)
+ svn_subst_eol_style_from_value(&eol_style, &eol, eol_style_val->data);
+ else
+ {
+ eol = NULL;
+ eol_style = svn_subst_eol_style_none;
+ }
- /* TRUE if recursing within an administrative (.i.e. .svn) directory. */
- svn_boolean_t recursing_within_adm_dir;
+ if (keywords_val)
+ SVN_ERR(svn_subst_build_keywords3(&keywords, keywords_val->data,
+ APR_STRINGIFY(SVN_INVALID_REVNUM),
+ "", "", 0, "", scratch_pool));
+ else
+ keywords = NULL;
- /* The absolute path of the adm dir if RECURSING_WITHIN_ADM_DIR is TRUE.
- * Else this is NULL.*/
- const char *adm_dir_abspath;
+ if (!svn_subst_translation_required(eol_style, eol, keywords, FALSE, FALSE))
+ return SVN_NO_ERROR;
- /* A path to an empty file used for diffs that add/delete files. */
- const char *empty_file_abspath;
+ SVN_ERR(svn_stream_open_readonly(&contents, *local_abspath,
+ scratch_pool, scratch_pool));
- const svn_wc_diff_callbacks4_t *callbacks;
- void *diff_baton;
- svn_client_ctx_t *ctx;
- apr_pool_t *pool;
-} arbitrary_diff_walker_baton;
+ SVN_ERR(svn_stream_open_unique(&dst, local_abspath, NULL,
+ svn_io_file_del_on_pool_cleanup,
+ result_pool, scratch_pool));
-/* Forward declaration needed because this function has a cyclic
- * dependency with do_arbitrary_dirs_diff(). */
-static svn_error_t *
-arbitrary_diff_walker(void *baton, const char *local_abspath,
- const apr_finfo_t *finfo,
- apr_pool_t *scratch_pool);
+ dst = svn_subst_stream_translated(dst, eol, TRUE /* repair */,
+ keywords, FALSE /* expand */,
+ scratch_pool);
-/* Another forward declaration. */
-static svn_error_t *
-arbitrary_diff_this_dir(struct arbitrary_diff_walker_baton *b,
- const char *local_abspath,
- svn_depth_t depth,
- apr_pool_t *scratch_pool);
+ SVN_ERR(svn_stream_copy3(contents, dst, cancel_func, cancel_baton,
+ scratch_pool));
-/* Produce a diff of depth DEPTH between two arbitrary directories at
- * LOCAL_ABSPATH1 and LOCAL_ABSPATH2, using the provided diff callbacks
- * to show file changes and, for versioned nodes, property changes.
- *
- * If ROOT_ABSPATH1 and ROOT_ABSPATH2 are not NULL, show paths in diffs
- * relative to these roots, rather than relative to LOCAL_ABSPATH1 and
- * LOCAL_ABSPATH2. This is needed when crawling a subtree that exists
- * only within LOCAL_ABSPATH2. */
-static svn_error_t *
-do_arbitrary_dirs_diff(const char *local_abspath1,
- const char *local_abspath2,
- const char *root_abspath1,
- const char *root_abspath2,
- svn_depth_t depth,
- const svn_wc_diff_callbacks4_t *callbacks,
- void *diff_baton,
- svn_client_ctx_t *ctx,
- apr_pool_t *scratch_pool)
-{
- apr_file_t *empty_file;
- svn_node_kind_t kind1;
-
- struct arbitrary_diff_walker_baton b;
-
- /* If LOCAL_ABSPATH1 is not a directory, crawl LOCAL_ABSPATH2 instead
- * and compare it to LOCAL_ABSPATH1, showing only additions.
- * This case can only happen during recursion from arbitrary_diff_walker(),
- * because do_arbitrary_nodes_diff() prevents this from happening at
- * the root of the comparison. */
- SVN_ERR(svn_io_check_resolved_path(local_abspath1, &kind1, scratch_pool));
- b.recursing_within_added_subtree = (kind1 != svn_node_dir);
-
- b.root1_abspath = root_abspath1 ? root_abspath1 : local_abspath1;
- b.root2_abspath = root_abspath2 ? root_abspath2 : local_abspath2;
- b.recursing_within_adm_dir = FALSE;
- b.adm_dir_abspath = NULL;
- b.callbacks = callbacks;
- b.diff_baton = diff_baton;
- b.ctx = ctx;
- b.pool = scratch_pool;
-
- SVN_ERR(svn_io_open_unique_file3(&empty_file, &b.empty_file_abspath,
- NULL, svn_io_file_del_on_pool_cleanup,
- scratch_pool, scratch_pool));
-
- if (depth <= svn_depth_immediates)
- SVN_ERR(arbitrary_diff_this_dir(&b, local_abspath1, depth, scratch_pool));
- else if (depth == svn_depth_infinity)
- SVN_ERR(svn_io_dir_walk2(b.recursing_within_added_subtree ? local_abspath2
- : local_abspath1,
- 0, arbitrary_diff_walker, &b, scratch_pool));
return SVN_NO_ERROR;
}
-/* Produce a diff of depth DEPTH for the directory at LOCAL_ABSPATH,
- * using information from the arbitrary_diff_walker_baton B.
- * LOCAL_ABSPATH is the path being crawled and can be on either side
- * of the diff depending on baton->recursing_within_added_subtree. */
+/* Handles reporting of a file for inner_dir_diff */
static svn_error_t *
-arbitrary_diff_this_dir(struct arbitrary_diff_walker_baton *b,
- const char *local_abspath,
- svn_depth_t depth,
- apr_pool_t *scratch_pool)
+do_file_diff(const char *left_abspath,
+ const char *right_abspath,
+ const char *left_root_abspath,
+ const char *right_root_abspath,
+ svn_boolean_t left_only,
+ svn_boolean_t right_only,
+ void *parent_baton,
+ const svn_diff_tree_processor_t *diff_processor,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
{
- const char *local_abspath1;
- const char *local_abspath2;
- svn_node_kind_t kind1;
- svn_node_kind_t kind2;
- const char *child_relpath;
- apr_hash_t *dirents1;
- apr_hash_t *dirents2;
- apr_hash_t *merged_dirents;
- apr_array_header_t *sorted_dirents;
- int i;
- apr_pool_t *iterpool;
-
- if (b->recursing_within_adm_dir)
- {
- if (svn_dirent_skip_ancestor(b->adm_dir_abspath, local_abspath))
- return SVN_NO_ERROR;
- else
- {
- b->recursing_within_adm_dir = FALSE;
- b->adm_dir_abspath = NULL;
- }
- }
- else if (svn_wc_is_adm_dir(svn_dirent_basename(local_abspath, NULL),
- scratch_pool))
- {
- b->recursing_within_adm_dir = TRUE;
- b->adm_dir_abspath = apr_pstrdup(b->pool, local_abspath);
- return SVN_NO_ERROR;
- }
+ const char *relpath;
+ svn_diff_source_t *left_source;
+ svn_diff_source_t *right_source;
+ svn_boolean_t skip = FALSE;
+ apr_hash_t *left_props;
+ apr_hash_t *right_props;
+ void *file_baton;
+
+ relpath = svn_dirent_skip_ancestor(left_root_abspath, left_abspath);
+
+ if (! right_only)
+ left_source = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
+ else
+ left_source = NULL;
- if (b->recursing_within_added_subtree)
- child_relpath = svn_dirent_skip_ancestor(b->root2_abspath, local_abspath);
+ if (! left_only)
+ right_source = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
else
- child_relpath = svn_dirent_skip_ancestor(b->root1_abspath, local_abspath);
- if (!child_relpath)
+ right_source = NULL;
+
+ SVN_ERR(diff_processor->file_opened(&file_baton, &skip,
+ relpath,
+ left_source,
+ right_source,
+ NULL /* copyfrom_source */,
+ parent_baton,
+ diff_processor,
+ scratch_pool,
+ scratch_pool));
+
+ if (skip)
return SVN_NO_ERROR;
- local_abspath1 = svn_dirent_join(b->root1_abspath, child_relpath,
- scratch_pool);
- SVN_ERR(svn_io_check_resolved_path(local_abspath1, &kind1, scratch_pool));
+ if (! right_only)
+ {
+ SVN_ERR(get_props(&left_props, left_abspath, ctx->wc_ctx,
+ scratch_pool, scratch_pool));
- local_abspath2 = svn_dirent_join(b->root2_abspath, child_relpath,
- scratch_pool);
- SVN_ERR(svn_io_check_resolved_path(local_abspath2, &kind2, scratch_pool));
+ /* We perform a mimetype detection to avoid diffing binary files
+ for textual changes.*/
+ if (! svn_hash_gets(left_props, SVN_PROP_MIME_TYPE))
+ {
+ const char *mime_type;
- if (depth > svn_depth_empty)
- {
- if (kind1 == svn_node_dir)
- SVN_ERR(svn_io_get_dirents3(&dirents1, local_abspath1,
- TRUE, /* only_check_type */
- scratch_pool, scratch_pool));
- else
- dirents1 = apr_hash_make(scratch_pool);
+ /* ### Use libmagic magic? */
+ SVN_ERR(svn_io_detect_mimetype2(&mime_type, left_abspath,
+ ctx->mimetypes_map, scratch_pool));
+
+ if (mime_type)
+ svn_hash_sets(left_props, SVN_PROP_MIME_TYPE,
+ svn_string_create(mime_type, scratch_pool));
+ }
+
+ SVN_ERR(translate_if_necessary(&left_abspath, left_props,
+ ctx->cancel_func, ctx->cancel_baton,
+ scratch_pool, scratch_pool));
}
+ else
+ left_props = NULL;
- if (kind2 == svn_node_dir)
+ if (! left_only)
{
- apr_hash_t *original_props;
- apr_hash_t *modified_props;
- apr_array_header_t *prop_changes;
-
- /* Show any property changes for this directory. */
- SVN_ERR(get_props(&original_props, local_abspath1, b->ctx->wc_ctx,
- scratch_pool, scratch_pool));
- SVN_ERR(get_props(&modified_props, local_abspath2, b->ctx->wc_ctx,
+ SVN_ERR(get_props(&right_props, right_abspath, ctx->wc_ctx,
scratch_pool, scratch_pool));
- SVN_ERR(svn_prop_diffs(&prop_changes, modified_props, original_props,
- scratch_pool));
- if (prop_changes->nelts > 0)
- SVN_ERR(b->callbacks->dir_props_changed(NULL, NULL, child_relpath,
- FALSE /* was_added */,
- prop_changes, original_props,
- b->diff_baton,
- scratch_pool));
-
- if (depth > svn_depth_empty)
- {
- /* Read directory entries. */
- SVN_ERR(svn_io_get_dirents3(&dirents2, local_abspath2,
- TRUE, /* only_check_type */
- scratch_pool, scratch_pool));
- }
- }
- else if (depth > svn_depth_empty)
- dirents2 = apr_hash_make(scratch_pool);
- if (depth <= svn_depth_empty)
- return SVN_NO_ERROR;
+ /* We perform a mimetype detection to avoid diffing binary files
+ for textual changes.*/
+ if (! svn_hash_gets(right_props, SVN_PROP_MIME_TYPE))
+ {
+ const char *mime_type;
- /* Compare dirents1 to dirents2 and show added/deleted/changed files. */
- merged_dirents = apr_hash_merge(scratch_pool, dirents1, dirents2,
- NULL, NULL);
- sorted_dirents = svn_sort__hash(merged_dirents,
- svn_sort_compare_items_as_paths,
- scratch_pool);
- iterpool = svn_pool_create(scratch_pool);
- for (i = 0; i < sorted_dirents->nelts; i++)
- {
- svn_sort__item_t elt = APR_ARRAY_IDX(sorted_dirents, i, svn_sort__item_t);
- const char *name = elt.key;
- svn_io_dirent2_t *dirent1;
- svn_io_dirent2_t *dirent2;
- const char *child1_abspath;
- const char *child2_abspath;
+ /* ### Use libmagic magic? */
+ SVN_ERR(svn_io_detect_mimetype2(&mime_type, right_abspath,
+ ctx->mimetypes_map, scratch_pool));
- svn_pool_clear(iterpool);
+ if (mime_type)
+ svn_hash_sets(right_props, SVN_PROP_MIME_TYPE,
+ svn_string_create(mime_type, scratch_pool));
+ }
- if (b->ctx->cancel_func)
- SVN_ERR(b->ctx->cancel_func(b->ctx->cancel_baton));
+ SVN_ERR(translate_if_necessary(&right_abspath, right_props,
+ ctx->cancel_func, ctx->cancel_baton,
+ scratch_pool, scratch_pool));
- if (strcmp(name, SVN_WC_ADM_DIR_NAME) == 0)
- continue;
+ }
+ else
+ right_props = NULL;
- dirent1 = svn_hash_gets(dirents1, name);
- if (!dirent1)
- {
- dirent1 = svn_io_dirent2_create(iterpool);
- dirent1->kind = svn_node_none;
- }
- dirent2 = svn_hash_gets(dirents2, name);
- if (!dirent2)
- {
- dirent2 = svn_io_dirent2_create(iterpool);
- dirent2->kind = svn_node_none;
- }
+ if (left_only)
+ {
+ SVN_ERR(diff_processor->file_deleted(relpath,
+ left_source,
+ left_abspath,
+ left_props,
+ file_baton,
+ diff_processor,
+ scratch_pool));
+ }
+ else if (right_only)
+ {
+ SVN_ERR(diff_processor->file_added(relpath,
+ NULL /* copyfrom_source */,
+ right_source,
+ NULL /* copyfrom_file */,
+ right_abspath,
+ NULL /* copyfrom_props */,
+ right_props,
+ file_baton,
+ diff_processor,
+ scratch_pool));
+ }
+ else
+ {
+ /* ### Perform diff -> close/changed */
+ svn_boolean_t same;
+ apr_array_header_t *prop_changes;
- child1_abspath = svn_dirent_join(local_abspath1, name, iterpool);
- child2_abspath = svn_dirent_join(local_abspath2, name, iterpool);
+ SVN_ERR(svn_io_files_contents_same_p(&same, left_abspath, right_abspath,
+ scratch_pool));
- if (dirent1->special)
- SVN_ERR(svn_io_check_resolved_path(child1_abspath, &dirent1->kind,
- iterpool));
- if (dirent2->special)
- SVN_ERR(svn_io_check_resolved_path(child1_abspath, &dirent2->kind,
- iterpool));
+ SVN_ERR(svn_prop_diffs(&prop_changes, right_props, left_props,
+ scratch_pool));
- if (dirent1->kind == svn_node_dir &&
- dirent2->kind == svn_node_dir)
+ if (!same || prop_changes->nelts > 0)
{
- if (depth == svn_depth_immediates)
- {
- /* Not using the walker, so show property diffs on these dirs. */
- SVN_ERR(do_arbitrary_dirs_diff(child1_abspath, child2_abspath,
- b->root1_abspath, b->root2_abspath,
- svn_depth_empty,
- b->callbacks, b->diff_baton,
- b->ctx, iterpool));
- }
- else
- {
- /* Either the walker will visit these directories (with
- * depth=infinity) and they will be processed as 'this dir'
- * later, or we're showing file children only (depth=files). */
- continue;
- }
-
+ SVN_ERR(diff_processor->file_changed(relpath,
+ left_source,
+ right_source,
+ same ? NULL : left_abspath,
+ same ? NULL : right_abspath,
+ left_props,
+ right_props,
+ !same,
+ prop_changes,
+ file_baton,
+ diff_processor,
+ scratch_pool));
}
-
- /* Files that exist only in dirents1. */
- if (dirent1->kind == svn_node_file &&
- (dirent2->kind == svn_node_dir || dirent2->kind == svn_node_none))
- SVN_ERR(do_arbitrary_files_diff(child1_abspath, b->empty_file_abspath,
- svn_relpath_join(child_relpath, name,
- iterpool),
- FALSE, TRUE, NULL,
- b->callbacks, b->diff_baton,
- b->ctx, iterpool));
-
- /* Files that exist only in dirents2. */
- if (dirent2->kind == svn_node_file &&
- (dirent1->kind == svn_node_dir || dirent1->kind == svn_node_none))
+ else
{
- apr_hash_t *original_props;
-
- SVN_ERR(get_props(&original_props, child1_abspath, b->ctx->wc_ctx,
- scratch_pool, scratch_pool));
- SVN_ERR(do_arbitrary_files_diff(b->empty_file_abspath, child2_abspath,
- svn_relpath_join(child_relpath, name,
- iterpool),
- TRUE, FALSE, original_props,
- b->callbacks, b->diff_baton,
- b->ctx, iterpool));
+ SVN_ERR(diff_processor->file_closed(relpath,
+ left_source,
+ right_source,
+ file_baton,
+ diff_processor,
+ scratch_pool));
}
-
- /* Files that exist in dirents1 and dirents2. */
- if (dirent1->kind == svn_node_file && dirent2->kind == svn_node_file)
- SVN_ERR(do_arbitrary_files_diff(child1_abspath, child2_abspath,
- svn_relpath_join(child_relpath, name,
- iterpool),
- FALSE, FALSE, NULL,
- b->callbacks, b->diff_baton,
- b->ctx, scratch_pool));
-
- /* Directories that only exist in dirents2. These aren't crawled
- * by this walker so we have to crawl them separately. */
- if (depth > svn_depth_files &&
- dirent2->kind == svn_node_dir &&
- (dirent1->kind == svn_node_file || dirent1->kind == svn_node_none))
- SVN_ERR(do_arbitrary_dirs_diff(child1_abspath, child2_abspath,
- b->root1_abspath, b->root2_abspath,
- depth <= svn_depth_immediates
- ? svn_depth_empty
- : svn_depth_infinity ,
- b->callbacks, b->diff_baton,
- b->ctx, iterpool));
}
-
- svn_pool_destroy(iterpool);
-
return SVN_NO_ERROR;
}
-/* An implementation of svn_io_walk_func_t.
- * Note: LOCAL_ABSPATH is the path being crawled and can be on either side
- * of the diff depending on baton->recursing_within_added_subtree. */
+
+/* Handles reporting of a directory and its children for inner_dir_diff */
static svn_error_t *
-arbitrary_diff_walker(void *baton, const char *local_abspath,
- const apr_finfo_t *finfo,
- apr_pool_t *scratch_pool)
+do_dir_diff(const char *left_abspath,
+ const char *right_abspath,
+ const char *left_root_abspath,
+ const char *right_root_abspath,
+ svn_boolean_t left_only,
+ svn_boolean_t right_only,
+ svn_boolean_t left_before_right,
+ svn_depth_t depth,
+ void *parent_baton,
+ const svn_diff_tree_processor_t *diff_processor,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
{
- struct arbitrary_diff_walker_baton *b = baton;
+ const char *relpath;
+ svn_diff_source_t *left_source;
+ svn_diff_source_t *right_source;
+ svn_boolean_t skip = FALSE;
+ svn_boolean_t skip_children = FALSE;
+ void *dir_baton;
+ apr_hash_t *left_props;
+ apr_hash_t *right_props;
+
+ relpath = svn_dirent_skip_ancestor(left_root_abspath, left_abspath);
+
+ if (! right_only)
+ {
+ left_source = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
+ SVN_ERR(get_props(&left_props, left_abspath, ctx->wc_ctx,
+ scratch_pool, scratch_pool));
+ }
+ else
+ {
+ left_source = NULL;
+ left_props = NULL;
+ }
+
+ if (! left_only)
+ {
+ right_source = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
+ SVN_ERR(get_props(&right_props, right_abspath, ctx->wc_ctx,
+ scratch_pool, scratch_pool));
+ }
+ else
+ {
+ right_source = NULL;
+ right_props = NULL;
+ }
- if (b->ctx->cancel_func)
- SVN_ERR(b->ctx->cancel_func(b->ctx->cancel_baton));
+ SVN_ERR(diff_processor->dir_opened(&dir_baton, &skip, &skip_children,
+ relpath,
+ left_source,
+ right_source,
+ NULL /* copyfrom_source */,
+ parent_baton,
+ diff_processor,
+ scratch_pool, scratch_pool));
- if (finfo->filetype != APR_DIR)
+ if (!skip_children)
+ {
+ if (depth >= svn_depth_files)
+ SVN_ERR(inner_dir_diff(left_abspath, right_abspath,
+ left_root_abspath, right_root_abspath,
+ left_only, right_only,
+ left_before_right, depth,
+ dir_baton,
+ diff_processor, ctx, scratch_pool));
+ }
+ else if (skip)
return SVN_NO_ERROR;
- SVN_ERR(arbitrary_diff_this_dir(b, local_abspath, svn_depth_infinity,
- scratch_pool));
+ if (left_props && right_props)
+ {
+ apr_array_header_t *prop_diffs;
+
+ SVN_ERR(svn_prop_diffs(&prop_diffs, right_props, left_props,
+ scratch_pool));
+
+ if (prop_diffs->nelts)
+ {
+ SVN_ERR(diff_processor->dir_changed(relpath,
+ left_source,
+ right_source,
+ left_props,
+ right_props,
+ prop_diffs,
+ dir_baton,
+ diff_processor,
+ scratch_pool));
+ return SVN_NO_ERROR;
+ }
+ }
+
+ if (left_source && right_source)
+ {
+ SVN_ERR(diff_processor->dir_closed(relpath,
+ left_source,
+ right_source,
+ dir_baton,
+ diff_processor,
+ scratch_pool));
+ }
+ else if (left_source)
+ {
+ SVN_ERR(diff_processor->dir_deleted(relpath,
+ left_source,
+ left_props,
+ dir_baton,
+ diff_processor,
+ scratch_pool));
+ }
+ else
+ {
+ SVN_ERR(diff_processor->dir_added(relpath,
+ NULL /* copyfrom_source */,
+ right_source,
+ NULL /* copyfrom_props */,
+ right_props,
+ dir_baton,
+ diff_processor,
+ scratch_pool));
+ }
return SVN_NO_ERROR;
}
svn_error_t *
-svn_client__arbitrary_nodes_diff(const char *local_abspath1,
- const char *local_abspath2,
+svn_client__arbitrary_nodes_diff(const char **root_relpath,
+ svn_boolean_t *root_is_dir,
+ const char *left_abspath,
+ const char *right_abspath,
svn_depth_t depth,
- const svn_wc_diff_callbacks4_t *callbacks,
- void *diff_baton,
+ const svn_diff_tree_processor_t *diff_processor,
svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
- svn_node_kind_t kind1;
- svn_node_kind_t kind2;
+ svn_node_kind_t left_kind;
+ svn_node_kind_t right_kind;
+ const char *left_root_abspath;
+ const char *right_root_abspath;
+ svn_boolean_t left_before_right = TRUE; /* Future argument? */
- SVN_ERR(svn_io_check_resolved_path(local_abspath1, &kind1, scratch_pool));
- SVN_ERR(svn_io_check_resolved_path(local_abspath2, &kind2, scratch_pool));
+ if (depth == svn_depth_unknown)
+ depth = svn_depth_infinity;
- if (kind1 != kind2)
- return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
- _("'%s' is not the same node kind as '%s'"),
- svn_dirent_local_style(local_abspath1,
- scratch_pool),
- svn_dirent_local_style(local_abspath2,
- scratch_pool));
+ SVN_ERR(svn_io_check_resolved_path(left_abspath, &left_kind, scratch_pool));
+ SVN_ERR(svn_io_check_resolved_path(right_abspath, &right_kind, scratch_pool));
if (depth == svn_depth_unknown)
depth = svn_depth_infinity;
- if (kind1 == svn_node_file)
- SVN_ERR(do_arbitrary_files_diff(local_abspath1, local_abspath2,
- svn_dirent_basename(local_abspath1,
- scratch_pool),
- FALSE, FALSE, NULL,
- callbacks, diff_baton,
- ctx, scratch_pool));
- else if (kind1 == svn_node_dir)
- SVN_ERR(do_arbitrary_dirs_diff(local_abspath1, local_abspath2,
- NULL, NULL, depth,
- callbacks, diff_baton,
- ctx, scratch_pool));
+ if (left_kind == svn_node_dir && right_kind == svn_node_dir)
+ {
+ left_root_abspath = left_abspath;
+ right_root_abspath = right_abspath;
+
+ if (root_relpath)
+ *root_relpath = "";
+ if (root_is_dir)
+ *root_is_dir = TRUE;
+ }
+ else
+ {
+ svn_dirent_split(&left_root_abspath, root_relpath, left_abspath,
+ scratch_pool);
+ right_root_abspath = svn_dirent_dirname(right_abspath, scratch_pool);
+
+ if (root_relpath)
+ *root_relpath = apr_pstrdup(result_pool, *root_relpath);
+ if (root_is_dir)
+ *root_is_dir = FALSE;
+ }
+
+ if (left_kind == svn_node_dir && right_kind == svn_node_dir)
+ {
+ SVN_ERR(do_dir_diff(left_abspath, right_abspath,
+ left_root_abspath, right_root_abspath,
+ FALSE, FALSE, left_before_right,
+ depth, NULL /* parent_baton */,
+ diff_processor, ctx, scratch_pool));
+ }
+ else if (left_kind == svn_node_file && right_kind == svn_node_file)
+ {
+ SVN_ERR(do_file_diff(left_abspath, right_abspath,
+ left_root_abspath, right_root_abspath,
+ FALSE, FALSE,
+ NULL /* parent_baton */,
+ diff_processor, ctx, scratch_pool));
+ }
+ else if (left_kind == svn_node_file || left_kind == svn_node_dir
+ || right_kind == svn_node_file || right_kind == svn_node_dir)
+ {
+ void *dir_baton;
+ svn_boolean_t skip = FALSE;
+ svn_boolean_t skip_children = FALSE;
+ svn_diff_source_t *left_src;
+ svn_diff_source_t *right_src;
+
+ left_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
+ right_src = svn_diff__source_create(SVN_INVALID_REVNUM, scratch_pool);
+
+ /* The root is replaced... */
+ /* Report delete and/or add */
+
+ SVN_ERR(diff_processor->dir_opened(&dir_baton, &skip, &skip_children, "",
+ left_src,
+ right_src,
+ NULL /* copyfrom_src */,
+ NULL,
+ diff_processor,
+ scratch_pool, scratch_pool));
+
+ if (skip)
+ return SVN_NO_ERROR;
+ else if (!skip_children)
+ {
+ if (left_before_right)
+ {
+ if (left_kind == svn_node_file)
+ SVN_ERR(do_file_diff(left_abspath, right_abspath,
+ left_root_abspath, right_root_abspath,
+ TRUE, FALSE, NULL /* parent_baton */,
+ diff_processor, ctx, scratch_pool));
+ else if (left_kind == svn_node_dir)
+ SVN_ERR(do_dir_diff(left_abspath, right_abspath,
+ left_root_abspath, right_root_abspath,
+ TRUE, FALSE, left_before_right,
+ depth, NULL /* parent_baton */,
+ diff_processor, ctx, scratch_pool));
+ }
+
+ if (right_kind == svn_node_file)
+ SVN_ERR(do_file_diff(left_abspath, right_abspath,
+ left_root_abspath, right_root_abspath,
+ FALSE, TRUE, NULL /* parent_baton */,
+ diff_processor, ctx, scratch_pool));
+ else if (right_kind == svn_node_dir)
+ SVN_ERR(do_dir_diff(left_abspath, right_abspath,
+ left_root_abspath, right_root_abspath,
+ FALSE, TRUE, left_before_right,
+ depth, NULL /* parent_baton */,
+ diff_processor, ctx, scratch_pool));
+
+ if (! left_before_right)
+ {
+ if (left_kind == svn_node_file)
+ SVN_ERR(do_file_diff(left_abspath, right_abspath,
+ left_root_abspath, right_root_abspath,
+ TRUE, FALSE, NULL /* parent_baton */,
+ diff_processor, ctx, scratch_pool));
+ else if (left_kind == svn_node_dir)
+ SVN_ERR(do_dir_diff(left_abspath, right_abspath,
+ left_root_abspath, right_root_abspath,
+ TRUE, FALSE, left_before_right,
+ depth, NULL /* parent_baton */,
+ diff_processor, ctx, scratch_pool));
+ }
+ }
+
+ SVN_ERR(diff_processor->dir_closed("",
+ left_src,
+ right_src,
+ dir_baton,
+ diff_processor,
+ scratch_pool));
+ }
else
return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
_("'%s' is not a file or directory"),
- kind1 == svn_node_none
- ? svn_dirent_local_style(local_abspath1,
- scratch_pool)
- : svn_dirent_local_style(local_abspath2,
- scratch_pool));
+ svn_dirent_local_style(
+ (left_kind == svn_node_none)
+ ? left_abspath
+ : right_abspath,
+ scratch_pool));
+
return SVN_NO_ERROR;
}
diff --git a/subversion/libsvn_client/diff_summarize.c b/subversion/libsvn_client/diff_summarize.c
index df0911baf284..9e258cdfcbcb 100644
--- a/subversion/libsvn_client/diff_summarize.c
+++ b/subversion/libsvn_client/diff_summarize.c
@@ -25,32 +25,29 @@
#include "svn_dirent_uri.h"
#include "svn_hash.h"
+#include "svn_path.h"
#include "svn_props.h"
#include "svn_pools.h"
+#include "private/svn_wc_private.h"
+
#include "client.h"
/* Diff callbacks baton. */
struct summarize_baton_t {
+ apr_pool_t *baton_pool; /* For allocating skip_path */
+
/* The target path of the diff, relative to the anchor; "" if target == anchor. */
- const char *target;
+ const char *skip_relpath;
/* The summarize callback passed down from the API */
svn_client_diff_summarize_func_t summarize_func;
- /* Is the diff handling reversed? (add<->delete) */
- svn_boolean_t reversed;
-
/* The summarize callback baton */
void *summarize_func_baton;
-
- /* Which paths have a prop change. Key is a (const char *) path; the value
- * is any non-null pointer to indicate that this path has a prop change. */
- apr_hash_t *prop_changes;
};
-
/* Call B->summarize_func with B->summarize_func_baton, passing it a
* summary object composed from PATH (but made to be relative to the target
* of the diff), SUMMARIZE_KIND, PROP_CHANGED (or FALSE if the action is an
@@ -68,24 +65,9 @@ send_summary(struct summarize_baton_t *b,
SVN_ERR_ASSERT(summarize_kind != svn_client_diff_summarize_kind_normal
|| prop_changed);
- if (b->reversed)
- {
- switch(summarize_kind)
- {
- case svn_client_diff_summarize_kind_added:
- summarize_kind = svn_client_diff_summarize_kind_deleted;
- break;
- case svn_client_diff_summarize_kind_deleted:
- summarize_kind = svn_client_diff_summarize_kind_added;
- break;
- default:
- break;
- }
- }
-
/* PATH is relative to the anchor of the diff, but SUM->path needs to be
relative to the target of the diff. */
- sum->path = svn_relpath_skip_ancestor(b->target, path);
+ sum->path = svn_relpath_skip_ancestor(b->skip_relpath, path);
sum->summarize_kind = summarize_kind;
if (summarize_kind == svn_client_diff_summarize_kind_modified
|| summarize_kind == svn_client_diff_summarize_kind_normal)
@@ -96,6 +78,29 @@ send_summary(struct summarize_baton_t *b,
return SVN_NO_ERROR;
}
+/* Are there any changes to relevant (normal) props in PROPS? */
+static svn_boolean_t
+props_changed_hash(apr_hash_t *props,
+ apr_pool_t *scratch_pool)
+{
+ apr_hash_index_t *hi;
+
+ if (!props)
+ return FALSE;
+
+ for (hi = apr_hash_first(scratch_pool, props); hi; hi = apr_hash_next(hi))
+ {
+ const char *name = apr_hash_this_key(hi);
+
+ if (svn_property_kind2(name) == svn_prop_regular_kind)
+ {
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
/* Are there any changes to relevant (normal) props in PROPCHANGES? */
static svn_boolean_t
props_changed(const apr_array_header_t *propchanges,
@@ -108,210 +113,201 @@ props_changed(const apr_array_header_t *propchanges,
return (props->nelts != 0);
}
-
+/* svn_diff_tree_processor_t callback */
static svn_error_t *
-cb_dir_deleted(svn_wc_notify_state_t *state,
- svn_boolean_t *tree_conflicted,
- const char *path,
- void *diff_baton,
- apr_pool_t *scratch_pool)
+diff_dir_opened(void **new_dir_baton,
+ svn_boolean_t *skip,
+ svn_boolean_t *skip_children,
+ const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ const svn_diff_source_t *copyfrom_source,
+ void *parent_dir_baton,
+ const struct svn_diff_tree_processor_t *processor,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
- struct summarize_baton_t *b = diff_baton;
+ /* struct summarize_baton_t *b = processor->baton; */
- SVN_ERR(send_summary(b, path, svn_client_diff_summarize_kind_deleted,
- FALSE, svn_node_dir, scratch_pool));
+ /* ### Send here instead of from dir_added() ? */
+ /*if (!left_source)
+ {
+ SVN_ERR(send_summary(b, relpath, svn_client_diff_summarize_kind_added,
+ FALSE, svn_node_dir, scratch_pool));
+ }*/
return SVN_NO_ERROR;
}
+/* svn_diff_tree_processor_t callback */
static svn_error_t *
-cb_file_deleted(svn_wc_notify_state_t *state,
- svn_boolean_t *tree_conflicted,
- const char *path,
- const char *tmpfile1,
- const char *tmpfile2,
- const char *mimetype1,
- const char *mimetype2,
- apr_hash_t *originalprops,
- void *diff_baton,
- apr_pool_t *scratch_pool)
+diff_dir_changed(const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ /*const*/ apr_hash_t *left_props,
+ /*const*/ apr_hash_t *right_props,
+ const apr_array_header_t *prop_changes,
+ void *dir_baton,
+ const struct svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
{
- struct summarize_baton_t *b = diff_baton;
+ struct summarize_baton_t *b = processor->baton;
- SVN_ERR(send_summary(b, path, svn_client_diff_summarize_kind_deleted,
- FALSE, svn_node_file, scratch_pool));
+ SVN_ERR(send_summary(b, relpath, svn_client_diff_summarize_kind_normal,
+ TRUE, svn_node_dir, scratch_pool));
return SVN_NO_ERROR;
}
+/* svn_diff_tree_processor_t callback */
static svn_error_t *
-cb_dir_added(svn_wc_notify_state_t *state,
- svn_boolean_t *tree_conflicted,
- svn_boolean_t *skip,
- svn_boolean_t *skip_children,
- const char *path,
- svn_revnum_t rev,
- const char *copyfrom_path,
- svn_revnum_t copyfrom_revision,
- void *diff_baton,
- apr_pool_t *scratch_pool)
+diff_dir_added(const char *relpath,
+ const svn_diff_source_t *copyfrom_source,
+ const svn_diff_source_t *right_source,
+ /*const*/ apr_hash_t *copyfrom_props,
+ /*const*/ apr_hash_t *right_props,
+ void *dir_baton,
+ const struct svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
{
- return SVN_NO_ERROR;
-}
+ struct summarize_baton_t *b = processor->baton;
+
+ /* ### Send from dir_opened without prop info? */
+ SVN_ERR(send_summary(b, relpath, svn_client_diff_summarize_kind_added,
+ props_changed_hash(right_props, scratch_pool),
+ svn_node_dir, scratch_pool));
-static svn_error_t *
-cb_dir_opened(svn_boolean_t *tree_conflicted,
- svn_boolean_t *skip,
- svn_boolean_t *skip_children,
- const char *path,
- svn_revnum_t rev,
- void *diff_baton,
- apr_pool_t *scratch_pool)
-{
return SVN_NO_ERROR;
}
+/* svn_diff_tree_processor_t callback */
static svn_error_t *
-cb_dir_closed(svn_wc_notify_state_t *contentstate,
- svn_wc_notify_state_t *propstate,
- svn_boolean_t *tree_conflicted,
- const char *path,
- svn_boolean_t dir_was_added,
- void *diff_baton,
- apr_pool_t *scratch_pool)
+diff_dir_deleted(const char *relpath,
+ const svn_diff_source_t *left_source,
+ /*const*/ apr_hash_t *left_props,
+ void *dir_baton,
+ const struct svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
{
- struct summarize_baton_t *b = diff_baton;
- svn_boolean_t prop_change;
+ struct summarize_baton_t *b = processor->baton;
- if (! svn_relpath_skip_ancestor(b->target, path))
- return SVN_NO_ERROR;
-
- prop_change = svn_hash_gets(b->prop_changes, path) != NULL;
- if (dir_was_added || prop_change)
- SVN_ERR(send_summary(b, path,
- dir_was_added ? svn_client_diff_summarize_kind_added
- : svn_client_diff_summarize_kind_normal,
- prop_change, svn_node_dir, scratch_pool));
+ SVN_ERR(send_summary(b, relpath, svn_client_diff_summarize_kind_deleted,
+ FALSE, svn_node_dir, scratch_pool));
return SVN_NO_ERROR;
}
+/* svn_diff_tree_processor_t callback */
static svn_error_t *
-cb_file_added(svn_wc_notify_state_t *contentstate,
- svn_wc_notify_state_t *propstate,
- svn_boolean_t *tree_conflicted,
- const char *path,
- const char *tmpfile1,
- const char *tmpfile2,
- svn_revnum_t rev1,
- svn_revnum_t rev2,
- const char *mimetype1,
- const char *mimetype2,
- const char *copyfrom_path,
- svn_revnum_t copyfrom_revision,
- const apr_array_header_t *propchanges,
- apr_hash_t *originalprops,
- void *diff_baton,
- apr_pool_t *scratch_pool)
+diff_file_added(const char *relpath,
+ const svn_diff_source_t *copyfrom_source,
+ const svn_diff_source_t *right_source,
+ const char *copyfrom_file,
+ const char *right_file,
+ /*const*/ apr_hash_t *copyfrom_props,
+ /*const*/ apr_hash_t *right_props,
+ void *file_baton,
+ const struct svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
{
- struct summarize_baton_t *b = diff_baton;
+ struct summarize_baton_t *b = processor->baton;
- SVN_ERR(send_summary(b, path, svn_client_diff_summarize_kind_added,
- props_changed(propchanges, scratch_pool),
+ SVN_ERR(send_summary(b, relpath, svn_client_diff_summarize_kind_added,
+ props_changed_hash(right_props, scratch_pool),
svn_node_file, scratch_pool));
return SVN_NO_ERROR;
}
+/* svn_diff_tree_processor_t callback */
static svn_error_t *
-cb_file_opened(svn_boolean_t *tree_conflicted,
- svn_boolean_t *skip,
- const char *path,
- svn_revnum_t rev,
- void *diff_baton,
- apr_pool_t *scratch_pool)
+diff_file_changed(const char *relpath,
+ const svn_diff_source_t *left_source,
+ const svn_diff_source_t *right_source,
+ const char *left_file,
+ const char *right_file,
+ /*const*/ apr_hash_t *left_props,
+ /*const*/ apr_hash_t *right_props,
+ svn_boolean_t file_modified,
+ const apr_array_header_t *prop_changes,
+ void *file_baton,
+ const struct svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
{
- return SVN_NO_ERROR;
-}
+ struct summarize_baton_t *b = processor->baton;
-static svn_error_t *
-cb_file_changed(svn_wc_notify_state_t *contentstate,
- svn_wc_notify_state_t *propstate,
- svn_boolean_t *tree_conflicted,
- const char *path,
- const char *tmpfile1,
- const char *tmpfile2,
- svn_revnum_t rev1,
- svn_revnum_t rev2,
- const char *mimetype1,
- const char *mimetype2,
- const apr_array_header_t *propchanges,
- apr_hash_t *originalprops,
- void *diff_baton,
- apr_pool_t *scratch_pool)
-{
- struct summarize_baton_t *b = diff_baton;
- svn_boolean_t text_change = (tmpfile2 != NULL);
- svn_boolean_t prop_change = props_changed(propchanges, scratch_pool);
-
- if (text_change || prop_change)
- SVN_ERR(send_summary(b, path,
- text_change ? svn_client_diff_summarize_kind_modified
+ SVN_ERR(send_summary(b, relpath,
+ file_modified ? svn_client_diff_summarize_kind_modified
: svn_client_diff_summarize_kind_normal,
- prop_change, svn_node_file, scratch_pool));
+ props_changed(prop_changes, scratch_pool),
+ svn_node_file, scratch_pool));
return SVN_NO_ERROR;
}
+/* svn_diff_tree_processor_t callback */
static svn_error_t *
-cb_dir_props_changed(svn_wc_notify_state_t *propstate,
- svn_boolean_t *tree_conflicted,
- const char *path,
- svn_boolean_t dir_was_added,
- const apr_array_header_t *propchanges,
- apr_hash_t *original_props,
- void *diff_baton,
- apr_pool_t *scratch_pool)
+diff_file_deleted(const char *relpath,
+ const svn_diff_source_t *left_source,
+ const char *left_file,
+ /*const*/ apr_hash_t *left_props,
+ void *file_baton,
+ const struct svn_diff_tree_processor_t *processor,
+ apr_pool_t *scratch_pool)
{
- struct summarize_baton_t *b = diff_baton;
+ struct summarize_baton_t *b = processor->baton;
- if (props_changed(propchanges, scratch_pool))
- svn_hash_sets(b->prop_changes, path, path);
+ SVN_ERR(send_summary(b, relpath, svn_client_diff_summarize_kind_deleted,
+ FALSE, svn_node_file, scratch_pool));
return SVN_NO_ERROR;
}
svn_error_t *
svn_client__get_diff_summarize_callbacks(
- svn_wc_diff_callbacks4_t **callbacks,
- void **callback_baton,
- const char *target,
- svn_boolean_t reversed,
+ const svn_diff_tree_processor_t **diff_processor,
+ const char ***p_root_relpath,
svn_client_diff_summarize_func_t summarize_func,
void *summarize_baton,
- apr_pool_t *pool)
+ const char *original_target,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
- svn_wc_diff_callbacks4_t *cb = apr_palloc(pool, sizeof(*cb));
- struct summarize_baton_t *b = apr_palloc(pool, sizeof(*b));
+ svn_diff_tree_processor_t *dp;
+ struct summarize_baton_t *b = apr_pcalloc(result_pool, sizeof(*b));
- b->target = target;
+ b->baton_pool = result_pool;
b->summarize_func = summarize_func;
b->summarize_func_baton = summarize_baton;
- b->prop_changes = apr_hash_make(pool);
- b->reversed = reversed;
-
- cb->file_opened = cb_file_opened;
- cb->file_changed = cb_file_changed;
- cb->file_added = cb_file_added;
- cb->file_deleted = cb_file_deleted;
- cb->dir_deleted = cb_dir_deleted;
- cb->dir_opened = cb_dir_opened;
- cb->dir_added = cb_dir_added;
- cb->dir_props_changed = cb_dir_props_changed;
- cb->dir_closed = cb_dir_closed;
-
- *callbacks = cb;
- *callback_baton = b;
+
+ dp = svn_diff__tree_processor_create(b, result_pool);
+
+ /*dp->file_opened = diff_file_opened;*/
+ dp->file_added = diff_file_added;
+ dp->file_deleted = diff_file_deleted;
+ dp->file_changed = diff_file_changed;
+
+ dp->dir_opened = diff_dir_opened;
+ dp->dir_changed = diff_dir_changed;
+ dp->dir_deleted = diff_dir_deleted;
+ dp->dir_added = diff_dir_added;
+
+ *diff_processor = dp;
+ *p_root_relpath = &b->skip_relpath;
return SVN_NO_ERROR;
}
+
+svn_client_diff_summarize_t *
+svn_client_diff_summarize_dup(const svn_client_diff_summarize_t *diff,
+ apr_pool_t *pool)
+{
+ svn_client_diff_summarize_t *dup_diff = apr_palloc(pool, sizeof(*dup_diff));
+
+ *dup_diff = *diff;
+
+ if (diff->path)
+ dup_diff->path = apr_pstrdup(pool, diff->path);
+
+ return dup_diff;
+}
diff --git a/subversion/libsvn_client/export.c b/subversion/libsvn_client/export.c
index c14a5f0bc017..63cd87a921b0 100644
--- a/subversion/libsvn_client/export.c
+++ b/subversion/libsvn_client/export.c
@@ -1274,8 +1274,8 @@ export_file(const char *from_path_or_url,
* with information. */
for (hi = apr_hash_first(scratch_pool, props); hi; hi = apr_hash_next(hi))
{
- const char *propname = svn__apr_hash_index_key(hi);
- const svn_string_t *propval = svn__apr_hash_index_val(hi);
+ const char *propname = apr_hash_this_key(hi);
+ const svn_string_t *propval = apr_hash_this_val(hi);
SVN_ERR(change_file_prop(fb, propname, propval, scratch_pool));
}
@@ -1542,7 +1542,7 @@ svn_client_export5(svn_revnum_t *result_rev,
hi;
hi = apr_hash_next(hi))
{
- const char *external_abspath = svn__apr_hash_index_key(hi);
+ const char *external_abspath = apr_hash_this_key(hi);
const char *relpath;
const char *target_abspath;
@@ -1581,7 +1581,7 @@ svn_client_export5(svn_revnum_t *result_rev,
= svn_wc_create_notify(to_path,
svn_wc_notify_update_completed, pool);
notify->revision = edit_revision;
- (*ctx->notify_func2)(ctx->notify_baton2, notify, pool);
+ ctx->notify_func2(ctx->notify_baton2, notify, pool);
}
if (result_rev)
diff --git a/subversion/libsvn_client/externals.c b/subversion/libsvn_client/externals.c
index 52c236c60467..e50371543954 100644
--- a/subversion/libsvn_client/externals.c
+++ b/subversion/libsvn_client/externals.c
@@ -151,6 +151,7 @@ switch_dir_external(const char *local_abspath,
const svn_opt_revision_t *revision,
const char *defining_abspath,
svn_boolean_t *timestamp_sleep,
+ svn_ra_session_t *ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
@@ -170,7 +171,7 @@ switch_dir_external(const char *local_abspath,
if (revision->kind == svn_opt_revision_number)
external_rev = revision->value.number;
- /*
+ /*
* The code below assumes existing versioned paths are *not* part of
* the external's defining working copy.
* The working copy library does not support registering externals
@@ -186,7 +187,6 @@ switch_dir_external(const char *local_abspath,
&repos_root_url, &repos_uuid,
NULL, ctx->wc_ctx, local_abspath,
TRUE, /* ignore_enoent */
- TRUE, /* show hidden */
pool, pool));
if (kind != svn_node_unknown)
{
@@ -236,12 +236,12 @@ switch_dir_external(const char *local_abspath,
externals definition, perform an update. */
if (strcmp(node_url, url) == 0)
{
- SVN_ERR(svn_client__update_internal(NULL, local_abspath,
+ SVN_ERR(svn_client__update_internal(NULL, timestamp_sleep,
+ local_abspath,
revision, svn_depth_unknown,
FALSE, FALSE, FALSE, TRUE,
FALSE, TRUE,
- timestamp_sleep,
- ctx, subpool));
+ ra_session, ctx, subpool));
/* We just decided that this existing directory is an external,
so update the external registry with this information, like
@@ -372,9 +372,11 @@ switch_dir_external(const char *local_abspath,
}
/* ... Hello, new hotness. */
- SVN_ERR(svn_client__checkout_internal(NULL, url, local_abspath, peg_revision,
+ SVN_ERR(svn_client__checkout_internal(NULL, timestamp_sleep,
+ url, local_abspath, peg_revision,
revision, svn_depth_infinity,
- FALSE, FALSE, timestamp_sleep,
+ FALSE, FALSE,
+ ra_session,
ctx, pool));
SVN_ERR(svn_wc__node_get_repos_info(NULL, NULL,
@@ -401,14 +403,15 @@ switch_dir_external(const char *local_abspath,
return SVN_NO_ERROR;
}
-/* Try to update a file external at LOCAL_ABSPATH to URL at REVISION using a
- access baton that has a write lock. Use SCRATCH_POOL for temporary
+/* Try to update a file external at LOCAL_ABSPATH to SWITCH_LOC. This function
+ assumes caller has a write lock in CTX. Use SCRATCH_POOL for temporary
allocations, and use the client context CTX. */
static svn_error_t *
switch_file_external(const char *local_abspath,
- const char *url,
- const svn_opt_revision_t *peg_revision,
- const svn_opt_revision_t *revision,
+ const svn_client__pathrev_t *switch_loc,
+ const char *record_url,
+ const svn_opt_revision_t *record_peg_revision,
+ const svn_opt_revision_t *record_revision,
const char *def_dir_abspath,
svn_ra_session_t *ra_session,
svn_client_ctx_t *ctx,
@@ -485,7 +488,8 @@ switch_file_external(const char *local_abspath,
SVN_ERR_CLIENT_FILE_EXTERNAL_OVERWRITE_VERSIONED, 0,
_("The file external from '%s' cannot overwrite the existing "
"versioned item at '%s'"),
- url, svn_dirent_local_style(local_abspath, scratch_pool));
+ switch_loc->url,
+ svn_dirent_local_style(local_abspath, scratch_pool));
}
}
else
@@ -507,28 +511,17 @@ switch_file_external(const char *local_abspath,
void *report_baton;
const svn_delta_editor_t *switch_editor;
void *switch_baton;
- svn_client__pathrev_t *switch_loc;
svn_revnum_t revnum;
apr_array_header_t *inherited_props;
- const char *dir_abspath;
- const char *target;
-
- svn_dirent_split(&dir_abspath, &target, local_abspath, scratch_pool);
-
- /* ### Why do we open a new session? RA_SESSION is a valid
- ### session -- the caller used it to call svn_ra_check_path on
- ### this very URL, the caller also did the resolving and
- ### reparenting that is repeated here. */
- SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &switch_loc,
- url, dir_abspath,
- peg_revision, revision,
- ctx, scratch_pool));
+ const char *target = svn_dirent_basename(local_abspath, scratch_pool);
+
/* Get the external file's iprops. */
SVN_ERR(svn_ra_get_inherited_props(ra_session, &inherited_props, "",
switch_loc->rev,
scratch_pool, scratch_pool));
- SVN_ERR(svn_ra_reparent(ra_session, svn_uri_dirname(url, scratch_pool),
+ SVN_ERR(svn_ra_reparent(ra_session,
+ svn_uri_dirname(switch_loc->url, scratch_pool),
scratch_pool));
SVN_ERR(svn_wc__get_file_external_editor(&switch_editor, &switch_baton,
@@ -542,9 +535,9 @@ switch_file_external(const char *local_abspath,
use_commit_times,
diff3_cmd, preserved_exts,
def_dir_abspath,
- url, peg_revision, revision,
- ctx->conflict_func2,
- ctx->conflict_baton2,
+ record_url,
+ record_peg_revision,
+ record_revision,
ctx->cancel_func,
ctx->cancel_baton,
ctx->notify_func2,
@@ -578,7 +571,7 @@ switch_file_external(const char *local_abspath,
= svn_wc_notify_state_inapplicable;
notify->lock_state = svn_wc_notify_lock_state_inapplicable;
notify->revision = revnum;
- (*ctx->notify_func2)(ctx->notify_baton2, notify, scratch_pool);
+ ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool);
}
}
@@ -691,7 +684,7 @@ handle_external_item_removal(const svn_client_ctx_t *ctx,
notify->kind = kind;
notify->err = err;
- (ctx->notify_func2)(ctx->notify_baton2, notify, scratch_pool);
+ ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool);
if (err && err->apr_err == SVN_ERR_WC_LEFT_LOCAL_MOD)
{
@@ -701,7 +694,7 @@ handle_external_item_removal(const svn_client_ctx_t *ctx,
notify->kind = svn_node_dir;
notify->err = err;
- (ctx->notify_func2)(ctx->notify_baton2, notify, scratch_pool);
+ ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool);
}
}
@@ -722,10 +715,10 @@ handle_external_item_change(svn_client_ctx_t *ctx,
const char *local_abspath,
const char *old_defining_abspath,
const svn_wc_external_item2_t *new_item,
+ svn_ra_session_t *ra_session,
svn_boolean_t *timestamp_sleep,
apr_pool_t *scratch_pool)
{
- svn_ra_session_t *ra_session;
svn_client__pathrev_t *new_loc;
const char *new_url;
svn_node_kind_t ext_kind;
@@ -746,12 +739,39 @@ handle_external_item_change(svn_client_ctx_t *ctx,
scratch_pool, scratch_pool));
/* Determine if the external is a file or directory. */
- /* Get the RA connection. */
- SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &new_loc,
- new_url, NULL,
- &(new_item->peg_revision),
- &(new_item->revision), ctx,
- scratch_pool));
+ /* Get the RA connection, if needed. */
+ if (ra_session)
+ {
+ svn_error_t *err = svn_ra_reparent(ra_session, new_url, scratch_pool);
+
+ if (err)
+ {
+ if (err->apr_err == SVN_ERR_RA_ILLEGAL_URL)
+ {
+ svn_error_clear(err);
+ ra_session = NULL;
+ }
+ else
+ return svn_error_trace(err);
+ }
+ else
+ {
+ SVN_ERR(svn_client__resolve_rev_and_url(&new_loc,
+ ra_session, new_url,
+ &(new_item->peg_revision),
+ &(new_item->revision), ctx,
+ scratch_pool));
+
+ SVN_ERR(svn_ra_reparent(ra_session, new_loc->url, scratch_pool));
+ }
+ }
+
+ if (!ra_session)
+ SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &new_loc,
+ new_url, NULL,
+ &(new_item->peg_revision),
+ &(new_item->revision), ctx,
+ scratch_pool));
SVN_ERR(svn_ra_check_path(ra_session, "", new_loc->rev, &ext_kind,
scratch_pool));
@@ -775,7 +795,7 @@ handle_external_item_change(svn_client_ctx_t *ctx,
/* First notify that we're about to handle an external. */
if (ctx->notify_func2)
{
- (*ctx->notify_func2)(
+ ctx->notify_func2(
ctx->notify_baton2,
svn_wc_create_notify(local_abspath,
svn_wc_notify_update_external,
@@ -800,7 +820,7 @@ handle_external_item_change(svn_client_ctx_t *ctx,
&(new_item->peg_revision),
&(new_item->revision),
parent_dir_abspath,
- timestamp_sleep, ctx,
+ timestamp_sleep, ra_session, ctx,
scratch_pool));
break;
case svn_node_file:
@@ -858,6 +878,7 @@ handle_external_item_change(svn_client_ctx_t *ctx,
}
SVN_ERR(switch_file_external(local_abspath,
+ new_loc,
new_url,
&new_item->peg_revision,
&new_item->revision,
@@ -908,6 +929,7 @@ handle_externals_change(svn_client_ctx_t *ctx,
apr_hash_t *old_externals,
svn_depth_t ambient_depth,
svn_depth_t requested_depth,
+ svn_ra_session_t *ra_session,
apr_pool_t *scratch_pool)
{
apr_array_header_t *new_desc;
@@ -976,7 +998,7 @@ handle_externals_change(svn_client_ctx_t *ctx,
local_abspath, url,
target_abspath,
old_defining_abspath,
- new_item,
+ new_item, ra_session,
timestamp_sleep,
iterpool),
iterpool));
@@ -999,6 +1021,7 @@ svn_client__handle_externals(apr_hash_t *externals_new,
const char *target_abspath,
svn_depth_t requested_depth,
svn_boolean_t *timestamp_sleep,
+ svn_ra_session_t *ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
@@ -1018,8 +1041,8 @@ svn_client__handle_externals(apr_hash_t *externals_new,
hi;
hi = apr_hash_next(hi))
{
- const char *local_abspath = svn__apr_hash_index_key(hi);
- const char *desc_text = svn__apr_hash_index_val(hi);
+ const char *local_abspath = apr_hash_this_key(hi);
+ const char *desc_text = apr_hash_this_val(hi);
svn_depth_t ambient_depth = svn_depth_infinity;
svn_pool_clear(iterpool);
@@ -1029,7 +1052,7 @@ svn_client__handle_externals(apr_hash_t *externals_new,
const char *ambient_depth_w;
ambient_depth_w = apr_hash_get(ambient_depths, local_abspath,
- svn__apr_hash_index_klen(hi));
+ apr_hash_this_key_len(hi));
if (ambient_depth_w == NULL)
{
@@ -1048,7 +1071,7 @@ svn_client__handle_externals(apr_hash_t *externals_new,
local_abspath,
desc_text, old_external_defs,
ambient_depth, requested_depth,
- iterpool));
+ ra_session, iterpool));
}
/* Remove the remaining externals */
@@ -1056,8 +1079,8 @@ svn_client__handle_externals(apr_hash_t *externals_new,
hi;
hi = apr_hash_next(hi))
{
- const char *item_abspath = svn__apr_hash_index_key(hi);
- const char *defining_abspath = svn__apr_hash_index_val(hi);
+ const char *item_abspath = apr_hash_this_key(hi);
+ const char *defining_abspath = apr_hash_this_val(hi);
const char *parent_abspath;
svn_pool_clear(iterpool);
@@ -1131,8 +1154,8 @@ svn_client__export_externals(apr_hash_t *externals,
hi;
hi = apr_hash_next(hi))
{
- const char *local_abspath = svn__apr_hash_index_key(hi);
- const char *desc_text = svn__apr_hash_index_val(hi);
+ const char *local_abspath = apr_hash_this_key(hi);
+ const char *desc_text = apr_hash_this_val(hi);
const char *local_relpath;
const char *dir_url;
apr_array_header_t *items;
@@ -1188,6 +1211,17 @@ svn_client__export_externals(apr_hash_t *externals,
sub_iterpool),
sub_iterpool));
+ /* First notify that we're about to handle an external. */
+ if (ctx->notify_func2)
+ {
+ ctx->notify_func2(
+ ctx->notify_baton2,
+ svn_wc_create_notify(item_abspath,
+ svn_wc_notify_update_external,
+ sub_iterpool),
+ sub_iterpool);
+ }
+
SVN_ERR(wrap_external_error(
ctx, item_abspath,
svn_client_export5(NULL, new_url, item_abspath,
diff --git a/subversion/libsvn_client/import.c b/subversion/libsvn_client/import.c
index 43e0d79d202a..b5ee0776def7 100644
--- a/subversion/libsvn_client/import.c
+++ b/subversion/libsvn_client/import.c
@@ -47,25 +47,15 @@
#include "svn_props.h"
#include "client.h"
-#include "private/svn_subr_private.h"
#include "private/svn_ra_private.h"
+#include "private/svn_sorts_private.h"
+#include "private/svn_subr_private.h"
#include "private/svn_magic.h"
#include "svn_private_config.h"
-/* Import context baton.
-
- ### TODO: Add the following items to this baton:
- /` import editor/baton. `/
- const svn_delta_editor_t *editor;
- void *edit_baton;
+/* Import context baton. */
- /` Client context baton `/
- svn_client_ctx_t `ctx;
-
- /` Paths (keys) excluded from the import (values ignored) `/
- apr_hash_t *excludes;
-*/
typedef struct import_ctx_t
{
/* Whether any changes were made to the repository */
@@ -238,8 +228,8 @@ import_file(const svn_delta_editor_t *editor,
{
for (hi = apr_hash_first(pool, properties); hi; hi = apr_hash_next(hi))
{
- const char *pname = svn__apr_hash_index_key(hi);
- const svn_string_t *pval = svn__apr_hash_index_val(hi);
+ const char *pname = apr_hash_this_key(hi);
+ const svn_string_t *pval = apr_hash_this_val(hi);
SVN_ERR(editor->change_file_prop(file_baton, pname, pval, pool));
}
@@ -255,7 +245,7 @@ import_file(const svn_delta_editor_t *editor,
notify->content_state = notify->prop_state
= svn_wc_notify_state_inapplicable;
notify->lock_state = svn_wc_notify_lock_state_inapplicable;
- (*ctx->notify_func2)(ctx->notify_baton2, notify, pool);
+ ctx->notify_func2(ctx->notify_baton2, notify, pool);
}
/* If this is a special file, we need to set the svn:special
@@ -279,7 +269,7 @@ import_file(const svn_delta_editor_t *editor,
text_checksum =
svn_checksum_to_cstring(svn_checksum__from_digest_md5(digest, pool), pool);
- return editor->close_file(file_baton, text_checksum, pool);
+ return svn_error_trace(editor->close_file(file_baton, text_checksum, pool));
}
@@ -312,8 +302,8 @@ get_filtered_children(apr_hash_t **children,
for (hi = apr_hash_first(scratch_pool, dirents); hi; hi = apr_hash_next(hi))
{
- const char *base_name = svn__apr_hash_index_key(hi);
- const svn_io_dirent2_t *dirent = svn__apr_hash_index_val(hi);
+ const char *base_name = apr_hash_this_key(hi);
+ const svn_io_dirent2_t *dirent = apr_hash_this_val(hi);
const char *local_abspath;
svn_pool_clear(iterpool);
@@ -338,7 +328,7 @@ get_filtered_children(apr_hash_t **children,
notify->content_state = notify->prop_state
= svn_wc_notify_state_inapplicable;
notify->lock_state = svn_wc_notify_lock_state_inapplicable;
- (*ctx->notify_func2)(ctx->notify_baton2, notify, iterpool);
+ ctx->notify_func2(ctx->notify_baton2, notify, iterpool);
}
svn_hash_sets(dirents, base_name, NULL);
@@ -480,7 +470,7 @@ import_children(const char *dir_abspath,
notify->content_state = notify->prop_state
= svn_wc_notify_state_inapplicable;
notify->lock_state = svn_wc_notify_lock_state_inapplicable;
- (*ctx->notify_func2)(ctx->notify_baton2, notify, iterpool);
+ ctx->notify_func2(ctx->notify_baton2, notify, iterpool);
}
}
else
@@ -569,7 +559,7 @@ import_dir(const svn_delta_editor_t *editor,
notify->content_state = notify->prop_state
= svn_wc_notify_state_inapplicable;
notify->lock_state = svn_wc_notify_lock_state_inapplicable;
- (*ctx->notify_func2)(ctx->notify_baton2, notify, pool);
+ ctx->notify_func2(ctx->notify_baton2, notify, pool);
}
}
@@ -590,9 +580,15 @@ import_dir(const svn_delta_editor_t *editor,
/* Recursively import PATH to a repository using EDITOR and
* EDIT_BATON. PATH can be a file or directory.
*
+ * Sets *UPDATED_REPOSITORY to TRUE when the repository was modified by
+ * a successfull commit, otherwise to FALSE.
+ *
* DEPTH is the depth at which to import PATH; it behaves as for
* svn_client_import4().
*
+ * BASE_REV is the revision to use for the root of the commit. We
+ * checked the preconditions against this revision.
+ *
* NEW_ENTRIES is an ordered array of path components that must be
* created in the repository (where the ordering direction is
* parent-to-child). If PATH is a directory, NEW_ENTRIES may be empty
@@ -636,11 +632,14 @@ import_dir(const svn_delta_editor_t *editor,
* not necessarily the root.)
*/
static svn_error_t *
-import(const char *local_abspath,
+import(svn_boolean_t *updated_repository,
+ const char *local_abspath,
+ const char *url,
const apr_array_header_t *new_entries,
const svn_delta_editor_t *editor,
void *edit_baton,
svn_depth_t depth,
+ svn_revnum_t base_rev,
apr_hash_t *excludes,
apr_hash_t *autoprops,
apr_array_header_t *local_ignores,
@@ -656,17 +655,17 @@ import(const char *local_abspath,
void *root_baton;
apr_array_header_t *batons = NULL;
const char *edit_path = "";
- import_ctx_t *import_ctx = apr_pcalloc(pool, sizeof(*import_ctx));
+ import_ctx_t import_ctx = { FALSE };
const svn_io_dirent2_t *dirent;
- import_ctx->autoprops = autoprops;
- svn_magic__init(&import_ctx->magic_cookie, pool);
+ *updated_repository = FALSE;
- /* Get a root dir baton. We pass an invalid revnum to open_root
- to mean "base this on the youngest revision". Should we have an
- SVN_YOUNGEST_REVNUM defined for these purposes? */
- SVN_ERR(editor->open_root(edit_baton, SVN_INVALID_REVNUM,
- pool, &root_baton));
+ import_ctx.autoprops = autoprops;
+ SVN_ERR(svn_magic__init(&import_ctx.magic_cookie, ctx->config, pool));
+
+ /* Get a root dir baton. We pass the revnum we used for testing our
+ assumptions and obtaining inherited properties. */
+ SVN_ERR(editor->open_root(edit_baton, base_rev, pool, &root_baton));
/* Import a file or a directory tree. */
SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath, FALSE, FALSE,
@@ -697,7 +696,7 @@ import(const char *local_abspath,
pool, &root_baton));
/* Remember that the repository was modified */
- import_ctx->repos_changed = TRUE;
+ import_ctx.repos_changed = TRUE;
}
}
else if (dirent->kind == svn_node_file)
@@ -728,7 +727,7 @@ import(const char *local_abspath,
if (!ignores_match)
SVN_ERR(import_file(editor, root_baton, local_abspath, edit_path,
- dirent, import_ctx, ctx, pool));
+ dirent, &import_ctx, ctx, pool));
}
else if (dirent->kind == svn_node_dir)
{
@@ -748,7 +747,7 @@ import(const char *local_abspath,
root_baton, depth, excludes, global_ignores,
no_ignore, no_autoprops,
ignore_unknown_node_types, filter_callback,
- filter_baton, import_ctx, ctx, pool));
+ filter_baton, &import_ctx, ctx, pool));
}
else if (dirent->kind == svn_node_none
@@ -770,10 +769,23 @@ import(const char *local_abspath,
}
}
- if (import_ctx->repos_changed)
- return editor->close_edit(edit_baton, pool);
- else
- return editor->abort_edit(edit_baton, pool);
+ if (import_ctx.repos_changed)
+ {
+ if (ctx->notify_func2)
+ {
+ svn_wc_notify_t *notify;
+ notify = svn_wc_create_notify_url(url,
+ svn_wc_notify_commit_finalizing,
+ pool);
+ ctx->notify_func2(ctx->notify_baton2, notify, pool);
+ }
+
+ SVN_ERR(editor->close_edit(edit_baton, pool));
+
+ *updated_repository = TRUE;
+ }
+
+ return SVN_NO_ERROR;
}
@@ -809,6 +821,10 @@ svn_client_import5(const char *path,
apr_hash_t *autoprops = NULL;
apr_array_header_t *global_ignores;
apr_array_header_t *local_ignores_arr;
+ svn_revnum_t base_rev;
+ apr_array_header_t *inherited_props = NULL;
+ apr_hash_t *url_props = NULL;
+ svn_boolean_t updated_repository;
if (svn_path_is_url(path))
return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
@@ -816,6 +832,8 @@ svn_client_import5(const char *path,
SVN_ERR(svn_dirent_get_absolute(&local_abspath, path, scratch_pool));
+ SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool));
+
/* Create a new commit item and add it to the array. */
if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx))
{
@@ -828,7 +846,9 @@ svn_client_import5(const char *path,
= apr_array_make(scratch_pool, 1, sizeof(item));
item = svn_client_commit_item3_create(scratch_pool);
- item->path = apr_pstrdup(scratch_pool, path);
+ item->path = local_abspath;
+ item->url = url;
+ item->kind = kind;
item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD;
APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
@@ -844,15 +864,14 @@ svn_client_import5(const char *path,
}
}
- SVN_ERR(svn_io_check_path(local_abspath, &kind, scratch_pool));
-
SVN_ERR(svn_client_open_ra_session2(&ra_session, url, NULL,
ctx, scratch_pool, iterpool));
+ SVN_ERR(svn_ra_get_latest_revnum(ra_session, &base_rev, iterpool));
+
/* Figure out all the path components we need to create just to have
a place to stick our imported tree. */
- SVN_ERR(svn_ra_check_path(ra_session, "", SVN_INVALID_REVNUM, &kind,
- iterpool));
+ SVN_ERR(svn_ra_check_path(ra_session, "", base_rev, &kind, iterpool));
/* We can import into directories, but if a file already exists, that's
an error. */
@@ -871,8 +890,7 @@ svn_client_import5(const char *path,
APR_ARRAY_PUSH(new_entries, const char *) = dir;
SVN_ERR(svn_ra_reparent(ra_session, url, iterpool));
- SVN_ERR(svn_ra_check_path(ra_session, "", SVN_INVALID_REVNUM, &kind,
- iterpool));
+ SVN_ERR(svn_ra_check_path(ra_session, "", base_rev, &kind, iterpool));
}
/* Reverse the order of the components we added to our NEW_ENTRIES array. */
@@ -895,6 +913,17 @@ svn_client_import5(const char *path,
SVN_ERR(svn_client__ensure_revprop_table(&commit_revprops, revprop_table,
log_msg, ctx, scratch_pool));
+ /* Obtain properties before opening the commit editor, as at that point we are
+ not allowed to use the existing ra-session */
+ if (! no_ignore /*|| ! no_autoprops*/)
+ {
+ SVN_ERR(svn_ra_get_dir2(ra_session, NULL, NULL, &url_props, "",
+ base_rev, SVN_DIRENT_KIND, scratch_pool));
+
+ SVN_ERR(svn_ra_get_inherited_props(ra_session, &inherited_props, "", base_rev,
+ scratch_pool, iterpool));
+ }
+
/* Fetch RA commit editor. */
SVN_ERR(svn_ra__register_editor_shim_callbacks(ra_session,
svn_client__get_shim_callbacks(ctx->wc_ctx,
@@ -907,8 +936,13 @@ svn_client_import5(const char *path,
/* Get inherited svn:auto-props, svn:global-ignores, and
svn:ignores for the location we are importing to. */
if (!no_autoprops)
- SVN_ERR(svn_client__get_all_auto_props(&autoprops, url, ctx,
- scratch_pool, iterpool));
+ {
+ /* ### This should use inherited_props and url_props to avoid creating
+ another ra session to obtain the same values, but using a possibly
+ different HEAD revision */
+ SVN_ERR(svn_client__get_all_auto_props(&autoprops, url, ctx,
+ scratch_pool, iterpool));
+ }
if (no_ignore)
{
global_ignores = NULL;
@@ -916,49 +950,62 @@ svn_client_import5(const char *path,
}
else
{
- svn_opt_revision_t rev;
apr_array_header_t *config_ignores;
- apr_hash_t *local_ignores_hash;
+ svn_string_t *val;
+ int i;
+
+ global_ignores = apr_array_make(scratch_pool, 64, sizeof(const char *));
- SVN_ERR(svn_client__get_inherited_ignores(&global_ignores, url, ctx,
- scratch_pool, iterpool));
SVN_ERR(svn_wc_get_default_ignores(&config_ignores, ctx->config,
scratch_pool));
global_ignores = apr_array_append(scratch_pool, global_ignores,
config_ignores);
- rev.kind = svn_opt_revision_head;
- SVN_ERR(svn_client_propget5(&local_ignores_hash, NULL, SVN_PROP_IGNORE, url,
- &rev, &rev, NULL, svn_depth_empty, NULL, ctx,
- scratch_pool, scratch_pool));
+ val = svn_hash_gets(url_props, SVN_PROP_INHERITABLE_IGNORES);
+ if (val)
+ svn_cstring_split_append(global_ignores, val->data, "\n\r\t\v ",
+ FALSE, scratch_pool);
+
+ for (i = 0; i < inherited_props->nelts; i++)
+ {
+ svn_prop_inherited_item_t *elt = APR_ARRAY_IDX(
+ inherited_props, i, svn_prop_inherited_item_t *);
+
+ val = svn_hash_gets(elt->prop_hash, SVN_PROP_INHERITABLE_IGNORES);
+
+ if (val)
+ svn_cstring_split_append(global_ignores, val->data, "\n\r\t\v ",
+ FALSE, scratch_pool);
+ }
local_ignores_arr = apr_array_make(scratch_pool, 1, sizeof(const char *));
- if (apr_hash_count(local_ignores_hash))
+ val = svn_hash_gets(url_props, SVN_PROP_IGNORE);
+
+ if (val)
{
- svn_string_t *propval = svn_hash_gets(local_ignores_hash, url);
- if (propval)
- {
- svn_cstring_split_append(local_ignores_arr, propval->data,
- "\n\r\t\v ", FALSE, scratch_pool);
- }
+ svn_cstring_split_append(local_ignores_arr, val->data,
+ "\n\r\t\v ", FALSE, scratch_pool);
}
}
- /* If an error occurred during the commit, abort the edit and return
- the error. We don't even care if the abort itself fails. */
- if ((err = import(local_abspath, new_entries, editor, edit_baton,
- depth, excludes, autoprops, local_ignores_arr,
- global_ignores, no_ignore, no_autoprops,
- ignore_unknown_node_types, filter_callback,
- filter_baton, ctx, iterpool)))
+ /* If an error occurred during the commit, properly abort the edit. */
+ err = svn_error_trace(import(&updated_repository,
+ local_abspath, url, new_entries, editor,
+ edit_baton, depth, base_rev, excludes,
+ autoprops, local_ignores_arr, global_ignores,
+ no_ignore, no_autoprops,
+ ignore_unknown_node_types, filter_callback,
+ filter_baton, ctx, iterpool));
+
+ svn_pool_destroy(iterpool);
+
+ if (err || !updated_repository)
{
return svn_error_compose_create(
err,
- editor->abort_edit(edit_baton, iterpool));
+ editor->abort_edit(edit_baton, scratch_pool));
}
- svn_pool_destroy(iterpool);
-
return SVN_NO_ERROR;
}
diff --git a/subversion/libsvn_client/info.c b/subversion/libsvn_client/info.c
index f49f22e8ee3e..39b5eb112bab 100644
--- a/subversion/libsvn_client/info.c
+++ b/subversion/libsvn_client/info.c
@@ -27,14 +27,16 @@
#include "client.h"
#include "svn_client.h"
-#include "svn_pools.h"
#include "svn_dirent_uri.h"
-#include "svn_path.h"
#include "svn_hash.h"
+#include "svn_pools.h"
+#include "svn_sorts.h"
+
#include "svn_wc.h"
#include "svn_private_config.h"
#include "private/svn_fspath.h"
+#include "private/svn_sorts_private.h"
#include "private/svn_wc_private.h"
@@ -59,6 +61,78 @@ svn_client_info2_dup(const svn_client_info2_t *info,
return new_info;
}
+/* Handle externals for svn_client_info4() */
+
+static svn_error_t *
+do_external_info(apr_hash_t *external_map,
+ svn_depth_t depth,
+ svn_boolean_t fetch_excluded,
+ svn_boolean_t fetch_actual_only,
+ const apr_array_header_t *changelists,
+ svn_client_info_receiver2_t receiver,
+ void *receiver_baton,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+ apr_array_header_t *externals;
+ int i;
+
+ externals = svn_sort__hash(external_map, svn_sort_compare_items_lexically,
+ scratch_pool);
+
+ /* Loop over the hash of new values (we don't care about the old
+ ones). This is a mapping of versioned directories to property
+ values. */
+ for (i = 0; i < externals->nelts; i++)
+ {
+ svn_node_kind_t external_kind;
+ svn_sort__item_t item = APR_ARRAY_IDX(externals, i, svn_sort__item_t);
+ const char *local_abspath = item.key;
+ const char *defining_abspath = item.value;
+ svn_opt_revision_t opt_rev;
+ svn_node_kind_t kind;
+
+ svn_pool_clear(iterpool);
+
+ /* Obtain information on the expected external. */
+ SVN_ERR(svn_wc__read_external_info(&external_kind, NULL, NULL, NULL,
+ &opt_rev.value.number,
+ ctx->wc_ctx, defining_abspath,
+ local_abspath, FALSE,
+ iterpool, iterpool));
+
+ if (external_kind != svn_node_dir)
+ continue;
+
+ SVN_ERR(svn_io_check_path(local_abspath, &kind, iterpool));
+ if (kind != svn_node_dir)
+ continue;
+
+ /* Tell the client we're starting an external info. */
+ if (ctx->notify_func2)
+ ctx->notify_func2(
+ ctx->notify_baton2,
+ svn_wc_create_notify(local_abspath,
+ svn_wc_notify_info_external,
+ iterpool), iterpool);
+
+ SVN_ERR(svn_client_info4(local_abspath,
+ NULL /* peg_revision */,
+ NULL /* revision */,
+ depth,
+ fetch_excluded,
+ fetch_actual_only,
+ TRUE /* include_externals */,
+ changelists,
+ receiver, receiver_baton,
+ ctx, iterpool));
+ }
+
+ svn_pool_destroy(iterpool);
+ return SVN_NO_ERROR;
+}
+
/* Set *INFO to a new info struct built from DIRENT
and (possibly NULL) svn_lock_t LOCK, all allocated in POOL.
Pointer fields are copied by reference, not dup'd. */
@@ -129,8 +203,8 @@ push_dir_info(svn_ra_session_t *ra_session,
const char *path, *fs_path;
svn_lock_t *lock;
svn_client_info2_t *info;
- const char *name = svn__apr_hash_index_key(hi);
- svn_dirent_t *the_ent = svn__apr_hash_index_val(hi);
+ const char *name = apr_hash_this_key(hi);
+ svn_dirent_t *the_ent = apr_hash_this_val(hi);
svn_client__pathrev_t *child_pathrev;
svn_pool_clear(subpool);
@@ -249,12 +323,13 @@ wc_info_receiver(void *baton,
}
svn_error_t *
-svn_client_info3(const char *abspath_or_url,
+svn_client_info4(const char *abspath_or_url,
const svn_opt_revision_t *peg_revision,
const svn_opt_revision_t *revision,
svn_depth_t depth,
svn_boolean_t fetch_excluded,
svn_boolean_t fetch_actual_only,
+ svn_boolean_t include_externals,
const apr_array_header_t *changelists,
svn_client_info_receiver2_t receiver,
void *receiver_baton,
@@ -283,11 +358,26 @@ svn_client_info3(const char *abspath_or_url,
b.client_receiver_func = receiver;
b.client_receiver_baton = receiver_baton;
- return svn_error_trace(
- svn_wc__get_info(ctx->wc_ctx, abspath_or_url, depth,
- fetch_excluded, fetch_actual_only, changelists,
- wc_info_receiver, &b,
- ctx->cancel_func, ctx->cancel_baton, pool));
+ SVN_ERR(svn_wc__get_info(ctx->wc_ctx, abspath_or_url, depth,
+ fetch_excluded, fetch_actual_only, changelists,
+ wc_info_receiver, &b,
+ ctx->cancel_func, ctx->cancel_baton, pool));
+
+ if (include_externals && SVN_DEPTH_IS_RECURSIVE(depth))
+ {
+ apr_hash_t *external_map;
+
+ SVN_ERR(svn_wc__externals_defined_below(&external_map,
+ ctx->wc_ctx, abspath_or_url,
+ pool, pool));
+
+ SVN_ERR(do_external_info(external_map,
+ depth, fetch_excluded, fetch_actual_only,
+ changelists,
+ receiver, receiver_baton, ctx, pool));
+ }
+
+ return SVN_NO_ERROR;
}
/* Go repository digging instead. */
@@ -298,12 +388,10 @@ svn_client_info3(const char *abspath_or_url,
SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &pathrev,
abspath_or_url, NULL, peg_revision,
revision, ctx, pool));
-
- svn_uri_split(NULL, &base_name, pathrev->url, pool);
+ base_name = svn_uri_basename(pathrev->url, pool);
/* Get the dirent for the URL itself. */
- SVN_ERR(svn_client__ra_stat_compatible(ra_session, pathrev->rev, &the_ent,
- DIRENT_FIELDS, ctx, pool));
+ SVN_ERR(svn_ra_stat(ra_session, "", pathrev->rev, &the_ent, pool));
if (! the_ent)
return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
@@ -353,9 +441,7 @@ svn_client_info3(const char *abspath_or_url,
pool);
/* Catch specific errors thrown by old mod_dav_svn or svnserve. */
- if (err &&
- (err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED
- || err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE))
+ if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)
{
svn_error_clear(err);
locks = apr_hash_make(pool); /* use an empty hash */
diff --git a/subversion/libsvn_client/iprops.c b/subversion/libsvn_client/iprops.c
index 653ce8cfb99a..9d725bf42632 100644
--- a/subversion/libsvn_client/iprops.c
+++ b/subversion/libsvn_client/iprops.c
@@ -176,8 +176,8 @@ get_inheritable_props(apr_hash_t **wcroot_iprops,
hi;
hi = apr_hash_next(hi))
{
- const char *child_abspath = svn__apr_hash_index_key(hi);
- const char *child_repos_relpath = svn__apr_hash_index_val(hi);
+ const char *child_abspath = apr_hash_this_key(hi);
+ const char *child_repos_relpath = apr_hash_this_val(hi);
const char *url;
apr_array_header_t *inherited_props;
svn_error_t *err;
@@ -244,6 +244,8 @@ svn_client__get_inheritable_props(apr_hash_t **wcroot_iprops,
const char *old_session_url;
svn_error_t *err;
+ *wcroot_iprops = NULL;
+
if (!SVN_IS_VALID_REVNUM(revision))
return SVN_NO_ERROR;
diff --git a/subversion/libsvn_client/libsvn_client.pc.in b/subversion/libsvn_client/libsvn_client.pc.in
new file mode 100644
index 000000000000..7cc7865643a9
--- /dev/null
+++ b/subversion/libsvn_client/libsvn_client.pc.in
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libsvn_client
+Description: Subversion Client Library
+Version: @PACKAGE_VERSION@
+Requires: apr-@SVN_APR_MAJOR_VERSION@
+Requires.private: libsvn_wc libsvn_ra libsvn_delta libsvn_diff libsvn_subr
+Libs: -L${libdir} -lsvn_client
+Cflags: -I${includedir}
diff --git a/subversion/libsvn_client/list.c b/subversion/libsvn_client/list.c
index 4093893f0536..c29b8bd68dc4 100644
--- a/subversion/libsvn_client/list.c
+++ b/subversion/libsvn_client/list.c
@@ -34,6 +34,7 @@
#include "private/svn_fspath.h"
#include "private/svn_ra_private.h"
+#include "private/svn_sorts_private.h"
#include "private/svn_wc_private.h"
#include "svn_private_config.h"
@@ -128,6 +129,10 @@ get_dir_contents(apr_uint32_t dirent_fields,
}
SVN_ERR(err);
+ /* Locks will often be empty. Prevent pointless lookups in that case. */
+ if (locks && apr_hash_count(locks) == 0)
+ locks = NULL;
+
/* Filter out svn:externals from all properties hash. */
if (prop_hash)
prop_val = svn_hash_gets(prop_hash, SVN_PROP_EXTERNALS);
@@ -187,114 +192,6 @@ get_dir_contents(apr_uint32_t dirent_fields,
return SVN_NO_ERROR;
}
-/* Like svn_ra_stat() but with a compatibility hack for pre-1.2 svnserve. */
-/* ### Maybe we should move this behavior into the svn_ra_stat wrapper? */
-svn_error_t *
-svn_client__ra_stat_compatible(svn_ra_session_t *ra_session,
- svn_revnum_t rev,
- svn_dirent_t **dirent_p,
- apr_uint32_t dirent_fields,
- svn_client_ctx_t *ctx,
- apr_pool_t *pool)
-{
- svn_error_t *err;
-
- err = svn_ra_stat(ra_session, "", rev, dirent_p, pool);
-
- /* svnserve before 1.2 doesn't support the above, so fall back on
- a less efficient method. */
- if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)
- {
- const char *repos_root_url;
- const char *session_url;
- svn_node_kind_t kind;
- svn_dirent_t *dirent;
-
- svn_error_clear(err);
-
- SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url, pool));
- SVN_ERR(svn_ra_get_session_url(ra_session, &session_url, pool));
-
- SVN_ERR(svn_ra_check_path(ra_session, "", rev, &kind, pool));
-
- if (kind != svn_node_none)
- {
- if (strcmp(session_url, repos_root_url) != 0)
- {
- svn_ra_session_t *parent_session;
- apr_hash_t *parent_ents;
- const char *parent_url, *base_name;
- apr_pool_t *subpool = svn_pool_create(pool);
-
- /* Open another session to the path's parent. This server
- doesn't support svn_ra_reparent anyway, so don't try it. */
- svn_uri_split(&parent_url, &base_name, session_url, subpool);
-
- SVN_ERR(svn_client_open_ra_session2(&parent_session, parent_url,
- NULL, ctx,
- subpool, subpool));
-
- /* Get all parent's entries, no props. */
- SVN_ERR(svn_ra_get_dir2(parent_session, &parent_ents, NULL,
- NULL, "", rev, dirent_fields, subpool));
-
- /* Get the relevant entry. */
- dirent = svn_hash_gets(parent_ents, base_name);
-
- if (dirent)
- *dirent_p = svn_dirent_dup(dirent, pool);
- else
- *dirent_p = NULL;
-
- svn_pool_destroy(subpool); /* Close RA session */
- }
- else
- {
- /* We can't get the directory entry for the repository root,
- but we can still get the information we want.
- The created-rev of the repository root must, by definition,
- be rev. */
- dirent = apr_palloc(pool, sizeof(*dirent));
- dirent->kind = kind;
- dirent->size = SVN_INVALID_FILESIZE;
- if (dirent_fields & SVN_DIRENT_HAS_PROPS)
- {
- apr_hash_t *props;
- SVN_ERR(svn_ra_get_dir2(ra_session, NULL, NULL, &props,
- "", rev, 0 /* no dirent fields */,
- pool));
- dirent->has_props = (apr_hash_count(props) != 0);
- }
- dirent->created_rev = rev;
- if (dirent_fields & (SVN_DIRENT_TIME | SVN_DIRENT_LAST_AUTHOR))
- {
- apr_hash_t *props;
- svn_string_t *val;
-
- SVN_ERR(svn_ra_rev_proplist(ra_session, rev, &props,
- pool));
- val = svn_hash_gets(props, SVN_PROP_REVISION_DATE);
- if (val)
- SVN_ERR(svn_time_from_cstring(&dirent->time, val->data,
- pool));
- else
- dirent->time = 0;
-
- val = svn_hash_gets(props, SVN_PROP_REVISION_AUTHOR);
- dirent->last_author = val ? val->data : NULL;
- }
-
- *dirent_p = dirent;
- }
- }
- else
- *dirent_p = NULL;
- }
- else
- SVN_ERR(err);
-
- return SVN_NO_ERROR;
-}
/* List the file/directory entries for PATH_OR_URL at REVISION.
The actual node revision selected is determined by the path as
@@ -369,8 +266,7 @@ list_internal(const char *path_or_url,
fs_path = svn_client__pathrev_fspath(loc, pool);
- SVN_ERR(svn_client__ra_stat_compatible(ra_session, loc->rev, &dirent,
- dirent_fields, ctx, pool));
+ SVN_ERR(svn_ra_stat(ra_session, "", loc->rev, &dirent, pool));
if (! dirent)
return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
_("URL '%s' non-existent in revision %ld"),
@@ -530,8 +426,8 @@ list_externals(apr_hash_t *externals,
hi;
hi = apr_hash_next(hi))
{
- const char *externals_parent_url = svn__apr_hash_index_key(hi);
- svn_string_t *externals_desc = svn__apr_hash_index_val(hi);
+ const char *externals_parent_url = apr_hash_this_key(hi);
+ svn_string_t *externals_desc = apr_hash_this_val(hi);
apr_array_header_t *external_items;
svn_pool_clear(iterpool);
diff --git a/subversion/libsvn_client/locking_commands.c b/subversion/libsvn_client/locking_commands.c
index c768503ef968..2e0164fa7b47 100644
--- a/subversion/libsvn_client/locking_commands.c
+++ b/subversion/libsvn_client/locking_commands.c
@@ -46,7 +46,8 @@
struct lock_baton
{
const char *base_dir_abspath;
- apr_hash_t *urls_to_paths;
+ apr_hash_t *urls_to_paths; /* url -> abspath */
+ const char *base_url;
svn_client_ctx_t *ctx;
apr_pool_t *pool;
};
@@ -56,7 +57,7 @@ struct lock_baton
* BATON is a 'struct lock_baton *', PATH is the path being locked,
* and LOCK is the lock itself.
*
- * If BATON->base_dir_abspath is not null, then this function either
+ * If BATON->urls_to_paths is not null, then this function either
* stores the LOCK on REL_URL or removes any lock tokens from REL_URL
* (depending on whether DO_LOCK is true or false respectively), but
* only if RA_ERR is null, or (in the unlock case) is something other
@@ -73,9 +74,12 @@ store_locks_callback(void *baton,
{
struct lock_baton *lb = baton;
svn_wc_notify_t *notify;
+ const char *local_abspath = lb->urls_to_paths
+ ? svn_hash_gets(lb->urls_to_paths, rel_url)
+ : NULL;
/* Create the notify struct first, so we can tweak it below. */
- notify = svn_wc_create_notify(rel_url,
+ notify = svn_wc_create_notify(local_abspath ? local_abspath : rel_url,
do_lock
? (ra_err
? svn_wc_notify_failed_lock
@@ -87,20 +91,14 @@ store_locks_callback(void *baton,
notify->lock = lock;
notify->err = ra_err;
- if (lb->base_dir_abspath)
+ if (local_abspath)
{
- char *path = svn_hash_gets(lb->urls_to_paths, rel_url);
- const char *local_abspath;
-
- local_abspath = svn_dirent_join(lb->base_dir_abspath, path, pool);
-
/* Notify a valid working copy path */
- notify->path = local_abspath;
notify->path_prefix = lb->base_dir_abspath;
if (do_lock)
{
- if (!ra_err)
+ if (!ra_err && lock)
{
SVN_ERR(svn_wc_add_lock2(lb->ctx->wc_ctx, local_abspath, lock,
lb->pool));
@@ -112,12 +110,14 @@ store_locks_callback(void *baton,
else /* unlocking */
{
/* Remove our wc lock token either a) if we got no error, or b) if
- we got any error except for owner mismatch. Note that the only
- errors that are handed to this callback will be locking-related
- errors. */
+ we got any error except for owner mismatch or hook failure (the
+ hook would be pre-unlock rather than post-unlock). Note that the
+ only errors that are handed to this callback will be
+ locking-related errors. */
if (!ra_err ||
- (ra_err && (ra_err->apr_err != SVN_ERR_FS_LOCK_OWNER_MISMATCH)))
+ (ra_err && (ra_err->apr_err != SVN_ERR_FS_LOCK_OWNER_MISMATCH
+ && ra_err->apr_err != SVN_ERR_REPOS_HOOK_FAILURE)))
{
SVN_ERR(svn_wc_remove_lock2(lb->ctx->wc_ctx, local_abspath,
lb->pool));
@@ -128,7 +128,7 @@ store_locks_callback(void *baton,
}
}
else
- notify->url = rel_url; /* Notify that path is actually a url */
+ notify->url = svn_path_url_add_component2(lb->base_url, rel_url, pool);
if (lb->ctx->notify_func2)
lb->ctx->notify_func2(lb->ctx->notify_baton2, notify, pool);
@@ -195,9 +195,15 @@ struct wc_lock_item_t
{
svn_revnum_t revision;
const char *lock_token;
+ const char *url;
};
-/* Set *COMMON_PARENT_URL to the nearest common parent URL of all TARGETS.
+/*
+ * Sets LOCK_PATHS to an array of working copy paths that this function
+ * has obtained lock on. The caller is responsible to release the locks
+ * EVEN WHEN THIS FUNCTION RETURNS AN ERROR.
+ *
+ * Set *COMMON_PARENT_URL to the nearest common parent URL of all TARGETS.
* If TARGETS are local paths, then the entry for each path is examined
* and *COMMON_PARENT is set to the common parent URL for all the
* targets (as opposed to the common local path).
@@ -217,8 +223,7 @@ struct wc_lock_item_t
*
* If TARGETS is an array of urls, REL_FS_PATHS_P is set to NULL.
* Otherwise each key in REL_FS_PATHS_P is an repository path (relative to
- * COMMON_PARENT) mapped to the target path for TARGET (relative to
- * the common parent WC path). working copy targets that they "belong" to.
+ * COMMON_PARENT) mapped to the absolute path for TARGET.
*
* If *COMMON_PARENT is a URL, then the values are a pointer to
* SVN_INVALID_REVNUM (allocated in pool) if DO_LOCK, else "".
@@ -226,8 +231,9 @@ struct wc_lock_item_t
* TARGETS may not be empty.
*/
static svn_error_t *
-organize_lock_targets(const char **common_parent_url,
- const char **base_dir,
+organize_lock_targets(apr_array_header_t **lock_paths,
+ const char **common_parent_url,
+ const char **base_dir_abspath,
apr_hash_t **rel_targets_p,
apr_hash_t **rel_fs_paths_p,
const apr_array_header_t *targets,
@@ -238,13 +244,12 @@ organize_lock_targets(const char **common_parent_url,
apr_pool_t *scratch_pool)
{
const char *common_url = NULL;
- const char *common_dirent = NULL;
apr_hash_t *rel_targets_ret = apr_hash_make(result_pool);
apr_hash_t *rel_fs_paths = NULL;
- apr_array_header_t *rel_targets;
apr_hash_t *wc_info = apr_hash_make(scratch_pool);
svn_boolean_t url_mode;
- int i;
+
+ *lock_paths = NULL;
SVN_ERR_ASSERT(targets->nelts);
SVN_ERR(svn_client__assert_homogeneous_target_type(targets));
@@ -253,6 +258,8 @@ organize_lock_targets(const char **common_parent_url,
if (url_mode)
{
+ apr_array_header_t *rel_targets;
+ int i;
svn_revnum_t *invalid_revnum =
apr_palloc(result_pool, sizeof(*invalid_revnum));
@@ -281,58 +288,108 @@ organize_lock_targets(const char **common_parent_url,
else
{
apr_array_header_t *rel_urls, *target_urls;
+ apr_hash_t *wcroot_target = apr_hash_make(scratch_pool);
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+ apr_hash_index_t *hi;
+ int i;
- /* Get the common parent dirent and a bunch of relpaths, one per
- target. */
- SVN_ERR(condense_targets(&common_dirent, &rel_targets, targets,
- FALSE, TRUE, result_pool, scratch_pool));
- if (! (common_dirent && *common_dirent))
- return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
- _("No common parent found, unable to operate "
- "on disjoint arguments"));
+ *lock_paths = apr_array_make(result_pool, 1, sizeof(const char *));
- /* Get the URL for each target (which also serves to verify that
- the dirent targets are sane). */
- target_urls = apr_array_make(scratch_pool, rel_targets->nelts,
- sizeof(const char *));
- for (i = 0; i < rel_targets->nelts; i++)
+ for (i = 0; i < targets->nelts; i++)
{
- const char *rel_target;
- const char *repos_relpath;
- const char *repos_root_url;
- const char *target_url;
- struct wc_lock_item_t *wli;
- const char *local_abspath;
- svn_node_kind_t kind;
+ const char *target_abspath;
+ const char *wcroot_abspath;
+ apr_array_header_t *wc_targets;
svn_pool_clear(iterpool);
- rel_target = APR_ARRAY_IDX(rel_targets, i, const char *);
- local_abspath = svn_dirent_join(common_dirent, rel_target, scratch_pool);
- wli = apr_pcalloc(scratch_pool, sizeof(*wli));
+ SVN_ERR(svn_dirent_get_absolute(&target_abspath,
+ APR_ARRAY_IDX(targets, i, const char*),
+ result_pool));
- SVN_ERR(svn_wc__node_get_base(&kind, &wli->revision, &repos_relpath,
- &repos_root_url, NULL,
- &wli->lock_token,
- wc_ctx, local_abspath,
- FALSE /* ignore_enoent */,
- FALSE /* show_hidden */,
- result_pool, iterpool));
+ SVN_ERR(svn_wc__get_wcroot(&wcroot_abspath, wc_ctx, target_abspath,
+ iterpool, iterpool));
- if (kind != svn_node_file)
- return svn_error_createf(SVN_ERR_WC_NOT_FILE, NULL,
- _("The node '%s' is not a file"),
- svn_dirent_local_style(local_abspath,
- iterpool));
+ wc_targets = svn_hash_gets(wcroot_target, wcroot_abspath);
- svn_hash_sets(wc_info, local_abspath, wli);
+ if (!wc_targets)
+ {
+ wc_targets = apr_array_make(scratch_pool, 1, sizeof(const char*));
+ svn_hash_sets(wcroot_target, apr_pstrdup(scratch_pool, wcroot_abspath),
+ wc_targets);
+ }
- target_url = svn_path_url_add_component2(repos_root_url,
- repos_relpath,
- scratch_pool);
+ APR_ARRAY_PUSH(wc_targets, const char *) = target_abspath;
+ }
- APR_ARRAY_PUSH(target_urls, const char *) = target_url;
+ for (hi = apr_hash_first(scratch_pool, wcroot_target);
+ hi;
+ hi = apr_hash_next(hi))
+ {
+ const char *lock_abspath;
+ apr_array_header_t *paths = apr_hash_this_val(hi);
+
+ /* Use parent dir of a single file target */
+ if (paths->nelts == 1)
+ lock_abspath = svn_dirent_dirname(
+ APR_ARRAY_IDX(paths, 0, const char *),
+ result_pool);
+ else
+ SVN_ERR(svn_dirent_condense_targets(&lock_abspath, NULL, paths,
+ FALSE, result_pool,
+ scratch_pool));
+
+ SVN_ERR(svn_wc__acquire_write_lock(&lock_abspath,
+ wc_ctx, lock_abspath, FALSE,
+ result_pool, scratch_pool));
+
+ APR_ARRAY_PUSH(*lock_paths, const char *) = lock_abspath;
+ }
+
+ /* Get the URL for each target (which also serves to verify that
+ the dirent targets are sane). */
+ target_urls = apr_array_make(scratch_pool, targets->nelts,
+ sizeof(const char *));
+ for (hi = apr_hash_first(scratch_pool, wcroot_target);
+ hi;
+ hi = apr_hash_next(hi))
+ {
+ apr_array_header_t *wc_targets = apr_hash_this_val(hi);
+
+ for (i = 0; i < wc_targets->nelts; i++)
+ {
+ const char *repos_relpath;
+ const char *repos_root_url;
+ struct wc_lock_item_t *wli;
+ const char *local_abspath;
+ svn_node_kind_t kind;
+
+ svn_pool_clear(iterpool);
+
+ local_abspath = APR_ARRAY_IDX(wc_targets, i, const char *);
+ wli = apr_pcalloc(scratch_pool, sizeof(*wli));
+
+ SVN_ERR(svn_wc__node_get_base(&kind, &wli->revision,
+ &repos_relpath,
+ &repos_root_url, NULL,
+ &wli->lock_token,
+ wc_ctx, local_abspath,
+ FALSE /* ignore_enoent */,
+ result_pool, iterpool));
+
+ if (kind != svn_node_file)
+ return svn_error_createf(SVN_ERR_WC_NOT_FILE, NULL,
+ _("The node '%s' is not a file"),
+ svn_dirent_local_style(local_abspath,
+ iterpool));
+
+ wli->url = svn_path_url_add_component2(repos_root_url,
+ repos_relpath,
+ scratch_pool);
+ svn_hash_sets(wc_info, local_abspath, wli);
+
+ APR_ARRAY_PUSH(target_urls, const char *) = wli->url;
+ }
}
/* Now that we have a bunch of URLs for our dirent targets,
@@ -345,39 +402,29 @@ organize_lock_targets(const char **common_parent_url,
_("Unable to lock/unlock across multiple "
"repositories"));
- /* Now we need to create a couple of different hash mappings. */
+ /* Now we need to create our mapping. */
rel_fs_paths = apr_hash_make(result_pool);
- for (i = 0; i < rel_targets->nelts; i++)
+
+ for (hi = apr_hash_first(scratch_pool, wc_info);
+ hi;
+ hi = apr_hash_next(hi))
{
- const char *rel_target, *rel_url;
- const char *local_abspath;
+ const char *local_abspath = apr_hash_this_key(hi);
+ struct wc_lock_item_t *wli = apr_hash_this_val(hi);
+ const char *rel_url;
svn_pool_clear(iterpool);
- /* First, we need to map our REL_URL (which is relative to
- COMMON_URL) to our REL_TARGET (which is relative to
- COMMON_DIRENT). */
- rel_target = APR_ARRAY_IDX(rel_targets, i, const char *);
- rel_url = APR_ARRAY_IDX(rel_urls, i, const char *);
- svn_hash_sets(rel_fs_paths, rel_url,
- apr_pstrdup(result_pool, rel_target));
+ rel_url = svn_uri_skip_ancestor(common_url, wli->url, result_pool);
- /* Then, we map our REL_URL (again) to either the base
- revision of the dirent target with which it is associated
- (if our caller is locking) or to a (possible empty) lock
- token string (if the caller is unlocking). */
- local_abspath = svn_dirent_join(common_dirent, rel_target, iterpool);
+ svn_hash_sets(rel_fs_paths, rel_url,
+ apr_pstrdup(result_pool, local_abspath));
if (do_lock) /* Lock. */
{
svn_revnum_t *revnum;
- struct wc_lock_item_t *wli;
revnum = apr_palloc(result_pool, sizeof(* revnum));
- wli = svn_hash_gets(wc_info, local_abspath);
-
- SVN_ERR_ASSERT(wli != NULL);
-
*revnum = wli->revision;
svn_hash_sets(rel_targets_ret, rel_url, revnum);
@@ -385,15 +432,10 @@ organize_lock_targets(const char **common_parent_url,
else /* Unlock. */
{
const char *lock_token;
- struct wc_lock_item_t *wli;
/* If not forcing the unlock, get the lock token. */
if (! force)
{
- wli = svn_hash_gets(wc_info, local_abspath);
-
- SVN_ERR_ASSERT(wli != NULL);
-
if (! wli->lock_token)
return svn_error_createf(
SVN_ERR_CLIENT_MISSING_LOCK_TOKEN, NULL,
@@ -419,7 +461,10 @@ organize_lock_targets(const char **common_parent_url,
/* Set our return variables. */
*common_parent_url = common_url;
- *base_dir = common_dirent;
+ if (*lock_paths && (*lock_paths)->nelts == 1)
+ *base_dir_abspath = APR_ARRAY_IDX(*lock_paths, 0, const char*);
+ else
+ *base_dir_abspath = NULL;
*rel_targets_p = rel_targets_ret;
*rel_fs_paths_p = rel_fs_paths;
@@ -437,7 +482,7 @@ fetch_tokens(svn_ra_session_t *ra_session, apr_hash_t *path_tokens,
for (hi = apr_hash_first(pool, path_tokens); hi; hi = apr_hash_next(hi))
{
- const char *path = svn__apr_hash_index_key(hi);
+ const char *path = apr_hash_this_key(hi);
svn_lock_t *lock;
svn_pool_clear(iterpool);
@@ -464,12 +509,13 @@ svn_client_lock(const apr_array_header_t *targets,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
- const char *base_dir;
const char *base_dir_abspath = NULL;
const char *common_parent_url;
svn_ra_session_t *ra_session;
apr_hash_t *path_revs, *urls_to_paths;
struct lock_baton cb;
+ apr_array_header_t *lock_abspaths;
+ svn_error_t *err;
if (apr_is_empty_array(targets))
return SVN_NO_ERROR;
@@ -483,26 +529,49 @@ svn_client_lock(const apr_array_header_t *targets,
_("Lock comment contains illegal characters"));
}
- SVN_ERR(organize_lock_targets(&common_parent_url, &base_dir, &path_revs,
- &urls_to_paths, targets, TRUE, steal_lock,
- ctx->wc_ctx, pool, pool));
+ err = organize_lock_targets(&lock_abspaths, &common_parent_url,
+ &base_dir_abspath, &path_revs,
+ &urls_to_paths,
+ targets, TRUE, steal_lock,
+ ctx->wc_ctx, pool, pool);
+
+ if (err)
+ goto release_locks;
- /* Open an RA session to the common parent of TARGETS. */
- if (base_dir)
- SVN_ERR(svn_dirent_get_absolute(&base_dir_abspath, base_dir, pool));
- SVN_ERR(svn_client_open_ra_session2(&ra_session, common_parent_url,
- base_dir_abspath, ctx, pool, pool));
+ /* Open an RA session to the common parent URL of TARGETS. */
+ err = svn_client_open_ra_session2(&ra_session, common_parent_url,
+ base_dir_abspath, ctx, pool, pool);
+
+ if (err)
+ goto release_locks;
cb.base_dir_abspath = base_dir_abspath;
+ cb.base_url = common_parent_url;
cb.urls_to_paths = urls_to_paths;
cb.ctx = ctx;
cb.pool = pool;
/* Lock the paths. */
- SVN_ERR(svn_ra_lock(ra_session, path_revs, comment,
- steal_lock, store_locks_callback, &cb, pool));
+ err = svn_ra_lock(ra_session, path_revs, comment,
+ steal_lock, store_locks_callback, &cb, pool);
- return SVN_NO_ERROR;
+release_locks:
+ if (lock_abspaths)
+ {
+ int i;
+
+ for (i = 0; i < lock_abspaths->nelts; i++)
+ {
+ err = svn_error_compose_create(
+ err,
+ svn_wc__release_write_lock(ctx->wc_ctx,
+ APR_ARRAY_IDX(lock_abspaths, i,
+ const char *),
+ pool));
+ }
+ }
+
+ return svn_error_trace(err);
}
svn_error_t *
@@ -511,42 +580,70 @@ svn_client_unlock(const apr_array_header_t *targets,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
- const char *base_dir;
const char *base_dir_abspath = NULL;
const char *common_parent_url;
svn_ra_session_t *ra_session;
apr_hash_t *path_tokens, *urls_to_paths;
+ apr_array_header_t *lock_abspaths;
struct lock_baton cb;
+ svn_error_t *err;
if (apr_is_empty_array(targets))
return SVN_NO_ERROR;
- SVN_ERR(organize_lock_targets(&common_parent_url, &base_dir, &path_tokens,
- &urls_to_paths, targets, FALSE, break_lock,
- ctx->wc_ctx, pool, pool));
+ err = organize_lock_targets(&lock_abspaths, &common_parent_url,
+ &base_dir_abspath, &path_tokens, &urls_to_paths,
+ targets, FALSE, break_lock,
+ ctx->wc_ctx, pool, pool);
+
+ if (err)
+ goto release_locks;
- /* Open an RA session. */
- if (base_dir)
- SVN_ERR(svn_dirent_get_absolute(&base_dir_abspath, base_dir, pool));
- SVN_ERR(svn_client_open_ra_session2(&ra_session, common_parent_url,
- base_dir_abspath, ctx, pool, pool));
+ /* Open an RA session to the common parent URL of TARGETS. */
+ err = svn_client_open_ra_session2(&ra_session, common_parent_url,
+ base_dir_abspath, ctx, pool, pool);
+
+ if (err)
+ goto release_locks;
/* If break_lock is not set, lock tokens are required by the server.
If the targets were all URLs, ensure that we provide lock tokens,
so the repository will only check that the user owns the
locks. */
- if (! base_dir && !break_lock)
- SVN_ERR(fetch_tokens(ra_session, path_tokens, pool));
+ if (! lock_abspaths && !break_lock)
+ {
+ err = fetch_tokens(ra_session, path_tokens, pool);
+
+ if (err)
+ goto release_locks;
+ }
cb.base_dir_abspath = base_dir_abspath;
+ cb.base_url = common_parent_url;
cb.urls_to_paths = urls_to_paths;
cb.ctx = ctx;
cb.pool = pool;
/* Unlock the paths. */
- SVN_ERR(svn_ra_unlock(ra_session, path_tokens, break_lock,
- store_locks_callback, &cb, pool));
+ err = svn_ra_unlock(ra_session, path_tokens, break_lock,
+ store_locks_callback, &cb, pool);
- return SVN_NO_ERROR;
+release_locks:
+ if (lock_abspaths)
+ {
+ int i;
+
+ for (i = 0; i < lock_abspaths->nelts; i++)
+ {
+ err = svn_error_compose_create(
+ err,
+ svn_wc__release_write_lock(ctx->wc_ctx,
+ APR_ARRAY_IDX(lock_abspaths, i,
+ const char *),
+ pool));
+ }
+ }
+
+ return svn_error_trace(err);
}
diff --git a/subversion/libsvn_client/log.c b/subversion/libsvn_client/log.c
index 91961559d255..adaee6104177 100644
--- a/subversion/libsvn_client/log.c
+++ b/subversion/libsvn_client/log.c
@@ -96,6 +96,7 @@ svn_client__get_copy_source(const char **original_repos_relpath,
svn_revnum_t *original_revision,
const char *path_or_url,
const svn_opt_revision_t *revision,
+ svn_ra_session_t *ra_session,
svn_client_ctx_t *ctx,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
@@ -103,18 +104,50 @@ svn_client__get_copy_source(const char **original_repos_relpath,
svn_error_t *err;
copyfrom_info_t copyfrom_info = { 0 };
apr_pool_t *sesspool = svn_pool_create(scratch_pool);
- svn_ra_session_t *ra_session;
svn_client__pathrev_t *at_loc;
+ const char *old_session_url = NULL;
copyfrom_info.is_first = TRUE;
copyfrom_info.path = NULL;
copyfrom_info.rev = SVN_INVALID_REVNUM;
copyfrom_info.pool = result_pool;
- SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &at_loc,
- path_or_url, NULL,
- revision, revision,
- ctx, sesspool));
+ if (!ra_session)
+ {
+ SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &at_loc,
+ path_or_url, NULL,
+ revision, revision,
+ ctx, sesspool));
+ }
+ else
+ {
+ const char *url;
+ if (svn_path_is_url(path_or_url))
+ url = path_or_url;
+ else
+ {
+ SVN_ERR(svn_client_url_from_path2(&url, path_or_url, ctx, sesspool,
+ sesspool));
+
+ if (! url)
+ return svn_error_createf(SVN_ERR_ENTRY_MISSING_URL, NULL,
+ _("'%s' has no URL"), path_or_url);
+ }
+
+ SVN_ERR(svn_client__ensure_ra_session_url(&old_session_url, ra_session,
+ url, sesspool));
+
+ err = svn_client__resolve_rev_and_url(&at_loc, ra_session, path_or_url,
+ revision, revision, ctx,
+ sesspool);
+
+ /* On error reparent back (and return), otherwise reparent to new
+ location */
+ SVN_ERR(svn_error_compose_create(
+ err,
+ svn_ra_reparent(ra_session, err ? old_session_url
+ : at_loc->url, sesspool)));
+ }
/* Find the copy source. Walk the location segments to find the revision
at which this node was created (copied or added). */
@@ -124,6 +157,11 @@ svn_client__get_copy_source(const char **original_repos_relpath,
copyfrom_info_receiver, &copyfrom_info,
scratch_pool);
+ if (old_session_url)
+ err = svn_error_compose_create(
+ err,
+ svn_ra_reparent(ra_session, old_session_url, sesspool));
+
svn_pool_destroy(sesspool);
if (err)
@@ -265,7 +303,7 @@ limit_receiver(void *baton, svn_log_entry_t *log_entry, apr_pool_t *pool)
The limitations on TARGETS specified by svn_client_log5 are enforced here.
So TARGETS can only contain a single WC path or a URL and zero or more
- relative paths -- anything else will raise an error.
+ relative paths -- anything else will raise an error.
PEG_REVISION, TARGETS, and CTX are as per svn_client_log5.
@@ -604,7 +642,7 @@ run_ra_get_log(apr_array_header_t *revision_ranges,
apr_array_header_t *log_segments,
svn_client__pathrev_t *actual_loc,
svn_ra_session_t *ra_session,
- /* The following are as per svn_client_log5. */
+ /* The following are as per svn_client_log5. */
const apr_array_header_t *targets,
int limit,
svn_boolean_t discover_changed_paths,
@@ -723,7 +761,7 @@ run_ra_get_log(apr_array_header_t *revision_ranges,
So to be safe we handle that case. */
if (matching_segment == NULL)
continue;
-
+
/* A segment with a NULL path means there is gap in the history.
We'll just proceed and let svn_ra_get_log2 fail with a useful
error...*/
@@ -905,8 +943,8 @@ svn_client_log5(const apr_array_header_t *targets,
SVN_ERR(run_ra_get_log(revision_ranges, relative_targets, log_segments,
actual_loc, ra_session, targets, limit,
discover_changed_paths, strict_node_history,
- include_merged_revisions, revprops, real_receiver,
- real_receiver_baton, ctx, pool));
+ include_merged_revisions, revprops,
+ real_receiver, real_receiver_baton, ctx, pool));
return SVN_NO_ERROR;
}
diff --git a/subversion/libsvn_client/merge.c b/subversion/libsvn_client/merge.c
index f0ff9a233c84..e912682e3651 100644
--- a/subversion/libsvn_client/merge.c
+++ b/subversion/libsvn_client/merge.c
@@ -54,13 +54,12 @@
#include "client.h"
#include "mergeinfo.h"
-#include "private/svn_opt_private.h"
-#include "private/svn_wc_private.h"
-#include "private/svn_mergeinfo_private.h"
#include "private/svn_fspath.h"
-#include "private/svn_ra_private.h"
+#include "private/svn_mergeinfo_private.h"
#include "private/svn_client_private.h"
+#include "private/svn_sorts_private.h"
#include "private/svn_subr_private.h"
+#include "private/svn_wc_private.h"
#include "svn_private_config.h"
@@ -570,14 +569,16 @@ perform_obstruction_check(svn_wc_notify_state_t *obstruction_state,
}
/* Create *LEFT and *RIGHT conflict versions for conflict victim
- * at VICTIM_ABSPATH, with kind NODE_KIND, using information obtained
- * from MERGE_SOURCE and TARGET.
+ * at VICTIM_ABSPATH, with merge-left node kind MERGE_LEFT_NODE_KIND
+ * and merge-right node kind MERGE_RIGHT_NODE_KIND, using information
+ * obtained from MERGE_SOURCE and TARGET.
* Allocate returned conflict versions in RESULT_POOL. */
static svn_error_t *
make_conflict_versions(const svn_wc_conflict_version_t **left,
const svn_wc_conflict_version_t **right,
const char *victim_abspath,
- svn_node_kind_t node_kind,
+ svn_node_kind_t merge_left_node_kind,
+ svn_node_kind_t merge_right_node_kind,
const merge_source_t *merge_source,
const merge_target_t *target,
apr_pool_t *result_pool,
@@ -597,13 +598,15 @@ make_conflict_versions(const svn_wc_conflict_version_t **left,
merge_source->loc1->repos_root_url,
merge_source->loc1->repos_uuid,
svn_relpath_join(left_relpath, child, scratch_pool),
- merge_source->loc1->rev, node_kind, result_pool);
+ merge_source->loc1->rev,
+ merge_left_node_kind, result_pool);
*right = svn_wc_conflict_version_create2(
merge_source->loc2->repos_root_url,
merge_source->loc2->repos_uuid,
svn_relpath_join(right_relpath, child, scratch_pool),
- merge_source->loc2->rev, node_kind, result_pool);
+ merge_source->loc2->rev,
+ merge_right_node_kind, result_pool);
return SVN_NO_ERROR;
}
@@ -633,8 +636,8 @@ split_mergeinfo_on_revision(svn_mergeinfo_t *younger_mergeinfo,
for (hi = apr_hash_first(pool, *mergeinfo); hi; hi = apr_hash_next(hi))
{
int i;
- const char *merge_source_path = svn__apr_hash_index_key(hi);
- svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
+ const char *merge_source_path = apr_hash_this_key(hi);
+ svn_rangelist_t *rangelist = apr_hash_this_val(hi);
svn_pool_clear(iterpool);
@@ -764,7 +767,7 @@ filter_self_referential_mergeinfo(apr_array_header_t **props,
/* If PATH itself has been added there is no need to filter. */
SVN_ERR(svn_wc__node_get_origin(&is_copy, &target_base.rev, &repos_relpath,
&target_base.repos_root_url,
- &target_base.repos_uuid, NULL,
+ &target_base.repos_uuid, NULL, NULL,
ctx->wc_ctx, target_abspath, FALSE,
pool, pool));
@@ -863,8 +866,8 @@ filter_self_referential_mergeinfo(apr_array_header_t **props,
hi; hi = apr_hash_next(hi))
{
int j;
- const char *source_path = svn__apr_hash_index_key(hi);
- svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
+ const char *source_path = apr_hash_this_key(hi);
+ svn_rangelist_t *rangelist = apr_hash_this_val(hi);
const char *merge_source_url;
svn_rangelist_t *adjusted_rangelist =
apr_array_make(iterpool, 0, sizeof(svn_merge_range_t *));
@@ -1182,6 +1185,9 @@ struct merge_dir_baton_t
*/
svn_wc_conflict_reason_t tree_conflict_reason;
svn_wc_conflict_action_t tree_conflict_action;
+ svn_node_kind_t tree_conflict_local_node_kind;
+ svn_node_kind_t tree_conflict_merge_left_node_kind;
+ svn_node_kind_t tree_conflict_merge_right_node_kind;
/* When TREE_CONFLICT_REASON is CONFLICT_REASON_SKIP, the skip state to
add to the notification */
@@ -1233,6 +1239,9 @@ struct merge_file_baton_t
merge_tree_baton_t for an explanation. */
svn_wc_conflict_reason_t tree_conflict_reason;
svn_wc_conflict_action_t tree_conflict_action;
+ svn_node_kind_t tree_conflict_local_node_kind;
+ svn_node_kind_t tree_conflict_merge_left_node_kind;
+ svn_node_kind_t tree_conflict_merge_right_node_kind;
/* When TREE_CONFLICT_REASON is CONFLICT_REASON_SKIP, the skip state to
add to the notification */
@@ -1280,8 +1289,8 @@ record_skip(merge_cmd_baton_t *merge_b,
notify->kind = kind;
notify->content_state = notify->prop_state = state;
- (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify,
- scratch_pool);
+ merge_b->ctx->notify_func2(merge_b->ctx->notify_baton2, notify,
+ scratch_pool);
}
return SVN_NO_ERROR;
}
@@ -1294,8 +1303,6 @@ record_skip(merge_cmd_baton_t *merge_b,
* The tree conflict, with its victim specified by VICTIM_PATH, is
* assumed to have happened during a merge using merge baton MERGE_B.
*
- * NODE_KIND must be the node kind of "old" and "theirs" and "mine";
- * this function cannot cope with node kind clashes.
* ACTION and REASON correspond to the fields
* of the same names in svn_wc_tree_conflict_description_t.
*/
@@ -1303,7 +1310,9 @@ static svn_error_t *
record_tree_conflict(merge_cmd_baton_t *merge_b,
const char *local_abspath,
struct merge_dir_baton_t *parent_baton,
- svn_node_kind_t node_kind,
+ svn_node_kind_t local_node_kind,
+ svn_node_kind_t merge_left_node_kind,
+ svn_node_kind_t merge_right_node_kind,
svn_wc_conflict_action_t action,
svn_wc_conflict_reason_t reason,
const svn_wc_conflict_description2_t *existing_conflict,
@@ -1357,7 +1366,9 @@ record_tree_conflict(merge_cmd_baton_t *merge_b,
reason = svn_wc_conflict_reason_moved_here;
}
- SVN_ERR(make_conflict_versions(&left, &right, local_abspath, node_kind,
+ SVN_ERR(make_conflict_versions(&left, &right, local_abspath,
+ merge_left_node_kind,
+ merge_right_node_kind,
&merge_b->merge_source, merge_b->target,
result_pool, scratch_pool));
@@ -1366,7 +1377,8 @@ record_tree_conflict(merge_cmd_baton_t *merge_b,
left = existing_conflict->src_left_version;
conflict = svn_wc_conflict_description_create_tree2(
- local_abspath, node_kind, svn_wc_operation_merge,
+ local_abspath, local_node_kind,
+ svn_wc_operation_merge,
left, right, result_pool);
conflict->action = action;
@@ -1402,10 +1414,10 @@ record_tree_conflict(merge_cmd_baton_t *merge_b,
notify = svn_wc_create_notify(local_abspath, svn_wc_notify_tree_conflict,
scratch_pool);
- notify->kind = node_kind;
+ notify->kind = local_node_kind;
- (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify,
- scratch_pool);
+ merge_b->ctx->notify_func2(merge_b->ctx->notify_baton2, notify,
+ scratch_pool);
}
return SVN_NO_ERROR;
@@ -1439,8 +1451,8 @@ record_update_add(merge_cmd_baton_t *merge_b,
notify = svn_wc_create_notify(local_abspath, action, scratch_pool);
notify->kind = kind;
- (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify,
- scratch_pool);
+ merge_b->ctx->notify_func2(merge_b->ctx->notify_baton2, notify,
+ scratch_pool);
}
return SVN_NO_ERROR;
@@ -1473,8 +1485,8 @@ record_update_update(merge_cmd_baton_t *merge_b,
notify->content_state = content_state;
notify->prop_state = prop_state;
- (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify,
- scratch_pool);
+ merge_b->ctx->notify_func2(merge_b->ctx->notify_baton2, notify,
+ scratch_pool);
}
return SVN_NO_ERROR;
@@ -1531,17 +1543,17 @@ handle_pending_notifications(merge_cmd_baton_t *merge_b,
hi;
hi = apr_hash_next(hi))
{
- const char *del_abspath = svn__apr_hash_index_key(hi);
+ const char *del_abspath = apr_hash_this_key(hi);
svn_wc_notify_t *notify;
notify = svn_wc_create_notify(del_abspath,
svn_wc_notify_update_delete,
scratch_pool);
notify->kind = svn_node_kind_from_word(
- svn__apr_hash_index_val(hi));
+ apr_hash_this_val(hi));
- (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2,
- notify, scratch_pool);
+ merge_b->ctx->notify_func2(merge_b->ctx->notify_baton2,
+ notify, scratch_pool);
}
db->pending_deletes = NULL;
@@ -1607,9 +1619,9 @@ mark_dir_edited(merge_cmd_baton_t *merge_b,
notify->kind = svn_node_dir;
notify->content_state = notify->prop_state = db->skip_reason;
- (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2,
- notify,
- scratch_pool);
+ merge_b->ctx->notify_func2(merge_b->ctx->notify_baton2,
+ notify,
+ scratch_pool);
}
if (merge_b->merge_source.ancestral
@@ -1623,7 +1635,10 @@ mark_dir_edited(merge_cmd_baton_t *merge_b,
/* open_directory() decided that a tree conflict should be raised */
SVN_ERR(record_tree_conflict(merge_b, local_abspath, db->parent_baton,
- svn_node_dir, db->tree_conflict_action,
+ db->tree_conflict_local_node_kind,
+ db->tree_conflict_merge_left_node_kind,
+ db->tree_conflict_merge_right_node_kind,
+ db->tree_conflict_action,
db->tree_conflict_reason,
NULL, TRUE,
scratch_pool));
@@ -1686,9 +1701,9 @@ mark_file_edited(merge_cmd_baton_t *merge_b,
notify->kind = svn_node_file;
notify->content_state = notify->prop_state = fb->skip_reason;
- (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2,
- notify,
- scratch_pool);
+ merge_b->ctx->notify_func2(merge_b->ctx->notify_baton2,
+ notify,
+ scratch_pool);
}
if (merge_b->merge_source.ancestral
@@ -1702,7 +1717,10 @@ mark_file_edited(merge_cmd_baton_t *merge_b,
/* open_file() decided that a tree conflict should be raised */
SVN_ERR(record_tree_conflict(merge_b, local_abspath, fb->parent_baton,
- svn_node_file, fb->tree_conflict_action,
+ fb->tree_conflict_local_node_kind,
+ fb->tree_conflict_merge_left_node_kind,
+ fb->tree_conflict_merge_right_node_kind,
+ fb->tree_conflict_action,
fb->tree_conflict_reason,
NULL, TRUE,
scratch_pool));
@@ -1742,6 +1760,16 @@ merge_file_opened(void **new_file_baton,
fb->tree_conflict_action = svn_wc_conflict_action_edit;
fb->skip_reason = svn_wc_notify_state_unknown;
+ if (left_source)
+ fb->tree_conflict_merge_left_node_kind = svn_node_file;
+ else
+ fb->tree_conflict_merge_left_node_kind = svn_node_none;
+
+ if (right_source)
+ fb->tree_conflict_merge_right_node_kind = svn_node_file;
+ else
+ fb->tree_conflict_merge_right_node_kind = svn_node_none;
+
*new_file_baton = fb;
if (pdb)
@@ -1758,7 +1786,6 @@ merge_file_opened(void **new_file_baton,
else if (left_source != NULL)
{
/* Node is expected to be a file, which will be changed or deleted. */
- svn_node_kind_t kind;
svn_boolean_t is_deleted;
svn_boolean_t excluded;
svn_depth_t parent_depth;
@@ -1770,7 +1797,8 @@ merge_file_opened(void **new_file_baton,
svn_wc_notify_state_t obstr_state;
SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, &excluded,
- &kind, &parent_depth,
+ &fb->tree_conflict_local_node_kind,
+ &parent_depth,
merge_b, local_abspath,
scratch_pool));
@@ -1783,10 +1811,10 @@ merge_file_opened(void **new_file_baton,
}
if (is_deleted)
- kind = svn_node_none;
+ fb->tree_conflict_local_node_kind = svn_node_none;
}
- if (kind == svn_node_none)
+ if (fb->tree_conflict_local_node_kind == svn_node_none)
{
fb->shadowed = TRUE;
@@ -1820,11 +1848,16 @@ merge_file_opened(void **new_file_baton,
return SVN_NO_ERROR;
/* ### /Similar */
}
- else if (kind != svn_node_file)
+ else if (fb->tree_conflict_local_node_kind != svn_node_file)
{
+ svn_boolean_t added;
fb->shadowed = TRUE;
- fb->tree_conflict_reason = svn_wc_conflict_reason_obstructed;
+ SVN_ERR(svn_wc__node_is_added(&added, merge_b->ctx->wc_ctx,
+ local_abspath, scratch_pool));
+
+ fb->tree_conflict_reason = added ? svn_wc_conflict_reason_added
+ : svn_wc_conflict_reason_obstructed;
/* ### Similar to directory */
*skip = TRUE;
@@ -1879,6 +1912,8 @@ merge_file_opened(void **new_file_baton,
/* Update the tree conflict to store that this is a replace */
SVN_ERR(record_tree_conflict(merge_b, local_abspath, pdb,
+ old_tc->node_kind,
+ old_tc->src_left_version->node_kind,
svn_node_file,
fb->tree_conflict_action,
fb->tree_conflict_reason,
@@ -1905,12 +1940,11 @@ merge_file_opened(void **new_file_baton,
&& ((pdb && pdb->added) || fb->add_is_replace)))
{
svn_wc_notify_state_t obstr_state;
- svn_node_kind_t kind;
svn_boolean_t is_deleted;
SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, NULL,
- &kind, NULL,
- merge_b, local_abspath,
+ &fb->tree_conflict_local_node_kind,
+ NULL, merge_b, local_abspath,
scratch_pool));
if (obstr_state != svn_wc_notify_state_inapplicable)
@@ -1920,11 +1954,18 @@ merge_file_opened(void **new_file_baton,
fb->tree_conflict_reason = CONFLICT_REASON_SKIP;
fb->skip_reason = obstr_state;
}
- else if (kind != svn_node_none && !is_deleted)
+ else if (fb->tree_conflict_local_node_kind != svn_node_none
+ && !is_deleted)
{
/* Set a tree conflict */
+ svn_boolean_t added;
+
fb->shadowed = TRUE;
- fb->tree_conflict_reason = svn_wc_conflict_reason_obstructed;
+ SVN_ERR(svn_wc__node_is_added(&added, merge_b->ctx->wc_ctx,
+ local_abspath, scratch_pool));
+
+ fb->tree_conflict_reason = added ? svn_wc_conflict_reason_added
+ : svn_wc_conflict_reason_obstructed;
}
}
@@ -2000,7 +2041,8 @@ merge_file_changed(const char *relpath,
scratch_pool, scratch_pool));
SVN_ERR(make_conflict_versions(&left, &right, local_abspath,
- svn_node_file, &merge_b->merge_source, merge_b->target,
+ svn_node_file, svn_node_file,
+ &merge_b->merge_source, merge_b->target,
scratch_pool, scratch_pool));
/* Do property merge now, if we are not going to perform a text merge */
@@ -2427,6 +2469,8 @@ merge_file_deleted(const char *relpath,
*/
SVN_ERR(record_tree_conflict(merge_b, local_abspath, fb->parent_baton,
svn_node_file,
+ svn_node_file,
+ svn_node_none,
svn_wc_conflict_action_delete,
svn_wc_conflict_reason_edited,
NULL, TRUE,
@@ -2447,7 +2491,7 @@ merge_file_deleted(const char *relpath,
When *SKIP is TRUE, the diff driver avoids work on getting the details
for the closing callbacks.
- The SKIP and SKIP_DESCENDANTS work independantly.
+ The SKIP and SKIP_DESCENDANTS work independently.
*/
static svn_error_t *
merge_dir_opened(void **new_dir_baton,
@@ -2477,6 +2521,16 @@ merge_dir_opened(void **new_dir_baton,
*new_dir_baton = db;
+ if (left_source)
+ db->tree_conflict_merge_left_node_kind = svn_node_dir;
+ else
+ db->tree_conflict_merge_left_node_kind = svn_node_none;
+
+ if (right_source)
+ db->tree_conflict_merge_right_node_kind = svn_node_dir;
+ else
+ db->tree_conflict_merge_right_node_kind = svn_node_none;
+
if (pdb)
{
db->parent_baton = pdb;
@@ -2493,7 +2547,6 @@ merge_dir_opened(void **new_dir_baton,
else if (left_source != NULL)
{
/* Node is expected to be a directory. */
- svn_node_kind_t kind;
svn_boolean_t is_deleted;
svn_boolean_t excluded;
svn_depth_t parent_depth;
@@ -2505,9 +2558,9 @@ merge_dir_opened(void **new_dir_baton,
{
svn_wc_notify_state_t obstr_state;
SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, &excluded,
- &kind, &parent_depth,
- merge_b, local_abspath,
- scratch_pool));
+ &db->tree_conflict_local_node_kind,
+ &parent_depth, merge_b,
+ local_abspath, scratch_pool));
if (obstr_state != svn_wc_notify_state_inapplicable)
{
@@ -2542,10 +2595,10 @@ merge_dir_opened(void **new_dir_baton,
}
if (is_deleted)
- kind = svn_node_none;
+ db->tree_conflict_local_node_kind = svn_node_none;
}
- if (kind == svn_node_none)
+ if (db->tree_conflict_local_node_kind == svn_node_none)
{
db->shadowed = TRUE;
@@ -2581,11 +2634,16 @@ merge_dir_opened(void **new_dir_baton,
return SVN_NO_ERROR;
/* ### /avoid breaking tests */
}
- else if (kind != svn_node_dir)
+ else if (db->tree_conflict_local_node_kind != svn_node_dir)
{
+ svn_boolean_t added;
+
db->shadowed = TRUE;
+ SVN_ERR(svn_wc__node_is_added(&added, merge_b->ctx->wc_ctx,
+ local_abspath, scratch_pool));
- db->tree_conflict_reason = svn_wc_conflict_reason_obstructed;
+ db->tree_conflict_reason = added ? svn_wc_conflict_reason_added
+ : svn_wc_conflict_reason_obstructed;
/* ### To avoid breaking tests */
*skip = TRUE;
@@ -2672,6 +2730,8 @@ merge_dir_opened(void **new_dir_baton,
/* Update the tree conflict to store that this is a replace */
SVN_ERR(record_tree_conflict(merge_b, local_abspath, pdb,
+ old_tc->node_kind,
+ old_tc->src_left_version->node_kind,
svn_node_dir,
db->tree_conflict_action,
db->tree_conflict_reason,
@@ -2686,12 +2746,11 @@ merge_dir_opened(void **new_dir_baton,
&& ((pdb && pdb->added) || db->add_is_replace)))
{
svn_wc_notify_state_t obstr_state;
- svn_node_kind_t kind;
svn_boolean_t is_deleted;
SVN_ERR(perform_obstruction_check(&obstr_state, &is_deleted, NULL,
- &kind, NULL,
- merge_b, local_abspath,
+ &db->tree_conflict_local_node_kind,
+ NULL, merge_b, local_abspath,
scratch_pool));
/* In this case of adding a directory, we have an exception to the
@@ -2701,7 +2760,8 @@ merge_dir_opened(void **new_dir_baton,
* versioned but unexpectedly missing from disk, or is unversioned
* but obstructed by a node of the wrong kind. */
if (obstr_state == svn_wc_notify_state_obstructed
- && (is_deleted || kind == svn_node_none))
+ && (is_deleted ||
+ db->tree_conflict_local_node_kind == svn_node_none))
{
svn_node_kind_t disk_kind;
@@ -2722,17 +2782,18 @@ merge_dir_opened(void **new_dir_baton,
db->tree_conflict_reason = CONFLICT_REASON_SKIP;
db->skip_reason = obstr_state;
}
- else if (kind != svn_node_none && !is_deleted)
+ else if (db->tree_conflict_local_node_kind != svn_node_none
+ && !is_deleted)
{
/* Set a tree conflict */
+ svn_boolean_t added;
db->shadowed = TRUE;
- db->tree_conflict_reason = svn_wc_conflict_reason_obstructed;
- if ((merge_b->merge_source.ancestral || merge_b->reintegrate_merge)
- && !(pdb && pdb->shadowed))
- {
- store_path(merge_b->skipped_abspaths, local_abspath);
- }
+ SVN_ERR(svn_wc__node_is_added(&added, merge_b->ctx->wc_ctx,
+ local_abspath, scratch_pool));
+
+ db->tree_conflict_reason = added ? svn_wc_conflict_reason_added
+ : svn_wc_conflict_reason_obstructed;
}
}
@@ -2758,7 +2819,7 @@ merge_dir_opened(void **new_dir_baton,
if (old_tc)
{
- /* svn_wc_add4 and svn_wc_add_from_disk2 can't add a node
+ /* svn_wc_add4 and svn_wc_add_from_disk3 can't add a node
over an existing tree conflict */
/* ### These functions should take some tree conflict argument
@@ -2796,8 +2857,9 @@ merge_dir_opened(void **new_dir_baton,
}
else
{
- SVN_ERR(svn_wc_add_from_disk2(merge_b->ctx->wc_ctx, local_abspath,
+ SVN_ERR(svn_wc_add_from_disk3(merge_b->ctx->wc_ctx, local_abspath,
apr_hash_make(scratch_pool),
+ FALSE /* skip checks */,
NULL, NULL /* no notify! */,
scratch_pool));
}
@@ -2806,6 +2868,8 @@ merge_dir_opened(void **new_dir_baton,
{
/* ### Should be atomic with svn_wc_add(4|_from_disk2)() */
SVN_ERR(record_tree_conflict(merge_b, local_abspath, pdb,
+ old_tc->node_kind,
+ svn_node_none,
svn_node_dir,
db->tree_conflict_action,
db->tree_conflict_reason,
@@ -2875,7 +2939,8 @@ merge_dir_changed(const char *relpath,
svn_wc_notify_state_t prop_state;
SVN_ERR(make_conflict_versions(&left, &right, local_abspath,
- svn_node_dir, &merge_b->merge_source,
+ svn_node_dir, svn_node_dir,
+ &merge_b->merge_source,
merge_b->target,
scratch_pool, scratch_pool));
@@ -3225,6 +3290,8 @@ merge_dir_deleted(const char *relpath,
*/
SVN_ERR(record_tree_conflict(merge_b, local_abspath, db->parent_baton,
svn_node_dir,
+ svn_node_dir,
+ svn_node_none,
svn_wc_conflict_action_delete,
svn_wc_conflict_reason_edited,
NULL, TRUE,
@@ -3643,8 +3710,8 @@ notify_merge_begin(merge_cmd_baton_t *merge_b,
notify->merge_range = NULL;
}
- (*merge_b->ctx->notify_func2)(merge_b->ctx->notify_baton2, notify,
- scratch_pool);
+ merge_b->ctx->notify_func2(merge_b->ctx->notify_baton2, notify,
+ scratch_pool);
return SVN_NO_ERROR;
}
@@ -3792,111 +3859,96 @@ adjust_deleted_subtree_ranges(svn_client__merge_path_t *child,
younger_rev, older_rev, ctx,
scratch_pool);
- /* If PRIMARY_URL@peg_rev doesn't exist then
- svn_client__repos_location_segments() typically returns an
- SVN_ERR_FS_NOT_FOUND error, but if it doesn't exist for a
- forward merge over ra_neon then we get SVN_ERR_RA_DAV_REQUEST_FAILED.
- http://subversion.tigris.org/issues/show_bug.cgi?id=3137 fixed some of
- the cases where different RA layers returned different error codes to
- signal the "path not found"...but it looks like there is more to do.
-
- ### Do we still need to special case for ra_neon (since it no longer
- exists)? */
if (err)
{
- if (err->apr_err == SVN_ERR_FS_NOT_FOUND
- || err->apr_err == SVN_ERR_RA_DAV_REQUEST_FAILED)
+ const char *rel_source_path; /* PRIMARY_URL relative to RA_SESSION */
+ svn_node_kind_t kind;
+
+ if (err->apr_err != SVN_ERR_FS_NOT_FOUND)
+ return svn_error_trace(err);
+
+ svn_error_clear(err);
+
+ /* PRIMARY_URL@peg_rev doesn't exist. Check if PRIMARY_URL@older_rev
+ exists, if neither exist then the editor can simply ignore this
+ subtree. */
+
+ SVN_ERR(svn_ra_get_path_relative_to_session(
+ ra_session, &rel_source_path, primary_url, scratch_pool));
+
+ SVN_ERR(svn_ra_check_path(ra_session, rel_source_path,
+ older_rev, &kind, scratch_pool));
+ if (kind == svn_node_none)
{
- /* PRIMARY_URL@peg_rev doesn't exist. Check if PRIMARY_URL@older_rev
- exists, if neither exist then the editor can simply ignore this
- subtree. */
- const char *rel_source_path; /* PRIMARY_URL relative to RA_SESSION */
- svn_node_kind_t kind;
+ /* Neither PRIMARY_URL@peg_rev nor PRIMARY_URL@older_rev exist,
+ so there is nothing to merge. Set CHILD->REMAINING_RANGES
+ identical to PARENT's. */
+ child->remaining_ranges =
+ svn_rangelist_dup(parent->remaining_ranges, scratch_pool);
+ }
+ else
+ {
+ svn_rangelist_t *deleted_rangelist;
+ svn_revnum_t rev_primary_url_deleted;
- svn_error_clear(err);
- err = NULL;
+ /* PRIMARY_URL@older_rev exists, so it was deleted at some
+ revision prior to peg_rev, find that revision. */
+ SVN_ERR(svn_ra_get_deleted_rev(ra_session, rel_source_path,
+ older_rev, younger_rev,
+ &rev_primary_url_deleted,
+ scratch_pool));
- SVN_ERR(svn_ra_get_path_relative_to_session(
- ra_session, &rel_source_path, primary_url, scratch_pool));
+ /* PRIMARY_URL@older_rev exists and PRIMARY_URL@peg_rev doesn't,
+ so svn_ra_get_deleted_rev() should always find the revision
+ PRIMARY_URL@older_rev was deleted. */
+ SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev_primary_url_deleted));
- SVN_ERR(svn_ra_check_path(ra_session, rel_source_path,
- older_rev, &kind, scratch_pool));
- if (kind == svn_node_none)
+ /* If this is a reverse merge reorder CHILD->REMAINING_RANGES and
+ PARENT->REMAINING_RANGES so both will work with the
+ svn_rangelist_* APIs below. */
+ if (is_rollback)
{
- /* Neither PRIMARY_URL@peg_rev nor PRIMARY_URL@older_rev exist,
- so there is nothing to merge. Set CHILD->REMAINING_RANGES
- identical to PARENT's. */
- child->remaining_ranges =
- svn_rangelist_dup(parent->remaining_ranges, scratch_pool);
+ /* svn_rangelist_reverse operates in place so it's safe
+ to use our scratch_pool. */
+ SVN_ERR(svn_rangelist_reverse(child->remaining_ranges,
+ scratch_pool));
+ SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges,
+ scratch_pool));
}
- else
- {
- svn_rangelist_t *deleted_rangelist;
- svn_revnum_t rev_primary_url_deleted;
-
- /* PRIMARY_URL@older_rev exists, so it was deleted at some
- revision prior to peg_rev, find that revision. */
- SVN_ERR(svn_ra_get_deleted_rev(ra_session, rel_source_path,
- older_rev, younger_rev,
- &rev_primary_url_deleted,
- scratch_pool));
- /* PRIMARY_URL@older_rev exists and PRIMARY_URL@peg_rev doesn't,
- so svn_ra_get_deleted_rev() should always find the revision
- PRIMARY_URL@older_rev was deleted. */
- SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev_primary_url_deleted));
-
- /* If this is a reverse merge reorder CHILD->REMAINING_RANGES and
- PARENT->REMAINING_RANGES so both will work with the
- svn_rangelist_* APIs below. */
- if (is_rollback)
- {
- /* svn_rangelist_reverse operates in place so it's safe
- to use our scratch_pool. */
- SVN_ERR(svn_rangelist_reverse(child->remaining_ranges,
- scratch_pool));
- SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges,
- scratch_pool));
- }
+ /* Find the intersection of CHILD->REMAINING_RANGES with the
+ range over which PRIMARY_URL@older_rev exists (ending at
+ the youngest revision at which it still exists). */
+ SVN_ERR(rangelist_intersect_range(&child->remaining_ranges,
+ child->remaining_ranges,
+ older_rev,
+ rev_primary_url_deleted - 1,
+ FALSE,
+ scratch_pool, scratch_pool));
- /* Find the intersection of CHILD->REMAINING_RANGES with the
- range over which PRIMARY_URL@older_rev exists (ending at
- the youngest revision at which it still exists). */
- SVN_ERR(rangelist_intersect_range(&child->remaining_ranges,
- child->remaining_ranges,
- older_rev,
- rev_primary_url_deleted - 1,
- FALSE,
- scratch_pool, scratch_pool));
-
- /* Merge into CHILD->REMAINING_RANGES the intersection of
- PARENT->REMAINING_RANGES with the range beginning when
- PRIMARY_URL@older_rev was deleted until younger_rev. */
- SVN_ERR(rangelist_intersect_range(&deleted_rangelist,
- parent->remaining_ranges,
- rev_primary_url_deleted - 1,
- peg_rev,
- FALSE,
- scratch_pool, scratch_pool));
- SVN_ERR(svn_rangelist_merge2(child->remaining_ranges,
- deleted_rangelist, scratch_pool,
- scratch_pool));
+ /* Merge into CHILD->REMAINING_RANGES the intersection of
+ PARENT->REMAINING_RANGES with the range beginning when
+ PRIMARY_URL@older_rev was deleted until younger_rev. */
+ SVN_ERR(rangelist_intersect_range(&deleted_rangelist,
+ parent->remaining_ranges,
+ rev_primary_url_deleted - 1,
+ peg_rev,
+ FALSE,
+ scratch_pool, scratch_pool));
+ SVN_ERR(svn_rangelist_merge2(child->remaining_ranges,
+ deleted_rangelist, scratch_pool,
+ scratch_pool));
- /* Return CHILD->REMAINING_RANGES and PARENT->REMAINING_RANGES
- to reverse order if necessary. */
- if (is_rollback)
- {
- SVN_ERR(svn_rangelist_reverse(child->remaining_ranges,
- scratch_pool));
- SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges,
- scratch_pool));
- }
+ /* Return CHILD->REMAINING_RANGES and PARENT->REMAINING_RANGES
+ to reverse order if necessary. */
+ if (is_rollback)
+ {
+ SVN_ERR(svn_rangelist_reverse(child->remaining_ranges,
+ scratch_pool));
+ SVN_ERR(svn_rangelist_reverse(parent->remaining_ranges,
+ scratch_pool));
}
}
- else
- {
- return svn_error_trace(err);
- }
}
else /* PRIMARY_URL@peg_rev exists. */
{
@@ -4684,7 +4736,6 @@ calculate_remaining_ranges(svn_client__merge_path_t *parent,
NULL, NULL, NULL, NULL,
ctx->wc_ctx, child->abspath,
TRUE /* ignore_enoent */,
- FALSE /* show_hidden */,
scratch_pool, scratch_pool));
/* If CHILD has no base revision then it hasn't been committed yet, so it
can't have any "future" history. */
@@ -4779,7 +4830,7 @@ find_gaps_in_merge_source_history(svn_revnum_t *gap_start,
*gap_start = *gap_end = SVN_INVALID_REVNUM;
/* Easy out: There can't be a gap between adjacent revisions. */
- if (abs(source->loc1->rev - source->loc2->rev) == 1)
+ if (labs(source->loc1->rev - source->loc2->rev) == 1)
return SVN_NO_ERROR;
/* Get SOURCE as mergeinfo. */
@@ -5174,8 +5225,8 @@ update_wc_mergeinfo(svn_mergeinfo_catalog_t result_catalog,
the WC with its on-disk mergeinfo. */
for (hi = apr_hash_first(scratch_pool, merges); hi; hi = apr_hash_next(hi))
{
- const char *local_abspath = svn__apr_hash_index_key(hi);
- svn_rangelist_t *ranges = svn__apr_hash_index_val(hi);
+ const char *local_abspath = apr_hash_this_key(hi);
+ svn_rangelist_t *ranges = apr_hash_this_val(hi);
svn_rangelist_t *rangelist;
svn_error_t *err;
const char *local_abspath_rel_to_target;
@@ -5326,7 +5377,7 @@ record_skips_in_mergeinfo(const char *mergeinfo_path,
for (hi = apr_hash_first(scratch_pool, merge_b->skipped_abspaths); hi;
hi = apr_hash_next(hi))
{
- const char *skipped_abspath = svn__apr_hash_index_key(hi);
+ const char *skipped_abspath = apr_hash_this_key(hi);
svn_wc_notify_state_t obstruction_state;
svn_pool_clear(iterpool);
@@ -5882,7 +5933,7 @@ slice_remaining_ranges(apr_array_header_t *children_with_mergeinfo,
split_range2->start = end_rev;
APR_ARRAY_IDX(child->remaining_ranges, 0,
svn_merge_range_t *) = split_range1;
- svn_sort__array_insert(&split_range2, child->remaining_ranges, 1);
+ svn_sort__array_insert(child->remaining_ranges, &split_range2, 1);
}
}
}
@@ -6017,11 +6068,11 @@ insert_child_to_merge(apr_array_header_t *children_with_mergeinfo,
/* Find where to insert the new element */
insert_index =
- svn_sort__bsearch_lower_bound(&insert_element, children_with_mergeinfo,
+ svn_sort__bsearch_lower_bound(children_with_mergeinfo, &insert_element,
compare_merge_path_t_as_paths);
new_element = svn_client__merge_path_dup(insert_element, pool);
- svn_sort__array_insert(&new_element, children_with_mergeinfo, insert_index);
+ svn_sort__array_insert(children_with_mergeinfo, &new_element, insert_index);
}
/* Helper for get_mergeinfo_paths().
@@ -6088,8 +6139,9 @@ insert_parent_and_sibs_of_sw_absent_del_subtree(
} /*(parent == NULL) */
/* Add all of PARENT's non-missing children that are not already present.*/
- SVN_ERR(svn_wc__node_get_children(&children, ctx->wc_ctx,
- parent_abspath, FALSE, pool, pool));
+ SVN_ERR(svn_wc__node_get_children_of_working_node(&children, ctx->wc_ctx,
+ parent_abspath,
+ pool, pool));
iterpool = svn_pool_create(pool);
for (i = 0; i < children->nelts; i++)
{
@@ -6188,7 +6240,7 @@ pre_merge_status_cb(void *baton,
hi;
hi = apr_hash_next(hi))
{
- const char *missing_root_path = svn__apr_hash_index_key(hi);
+ const char *missing_root_path = apr_hash_this_key(hi);
if (svn_dirent_is_ancestor(missing_root_path,
local_abspath))
@@ -6246,8 +6298,8 @@ get_wc_explicit_mergeinfo_catalog(apr_hash_t **subtrees_with_mergeinfo,
hi;
hi = apr_hash_next(hi))
{
- const char *wc_path = svn__apr_hash_index_key(hi);
- svn_string_t *mergeinfo_string = svn__apr_hash_index_val(hi);
+ const char *wc_path = apr_hash_this_key(hi);
+ svn_string_t *mergeinfo_string = apr_hash_this_val(hi);
svn_mergeinfo_t mergeinfo;
svn_error_t *err;
@@ -6360,8 +6412,8 @@ get_mergeinfo_paths(apr_array_header_t *children_with_mergeinfo,
hi;
hi = apr_hash_next(hi))
{
- const char *wc_path = svn__apr_hash_index_key(hi);
- svn_mergeinfo_t mergeinfo = svn__apr_hash_index_val(hi);
+ const char *wc_path = apr_hash_this_key(hi);
+ svn_mergeinfo_t mergeinfo = apr_hash_this_val(hi);
svn_client__merge_path_t *mergeinfo_child =
svn_client__merge_path_create(wc_path, result_pool);
@@ -6429,7 +6481,7 @@ get_mergeinfo_paths(apr_array_header_t *children_with_mergeinfo,
svn_pool_clear(iterpool);
svn_stringbuf_appendcstr(missing_subtree_err_buf,
svn_dirent_local_style(
- svn__apr_hash_index_key(hi), iterpool));
+ apr_hash_this_key(hi), iterpool));
svn_stringbuf_appendcstr(missing_subtree_err_buf, "\n");
}
@@ -6445,7 +6497,7 @@ get_mergeinfo_paths(apr_array_header_t *children_with_mergeinfo,
hi;
hi = apr_hash_next(hi))
{
- const char *wc_path = svn__apr_hash_index_key(hi);
+ const char *wc_path = apr_hash_this_key(hi);
svn_client__merge_path_t *child = get_child_with_mergeinfo(
children_with_mergeinfo, wc_path);
@@ -6473,8 +6525,8 @@ get_mergeinfo_paths(apr_array_header_t *children_with_mergeinfo,
hi = apr_hash_next(hi))
{
svn_boolean_t new_shallow_child = FALSE;
- const char *wc_path = svn__apr_hash_index_key(hi);
- svn_depth_t *child_depth = svn__apr_hash_index_val(hi);
+ const char *wc_path = apr_hash_this_key(hi);
+ svn_depth_t *child_depth = apr_hash_this_val(hi);
svn_client__merge_path_t *shallow_child = get_child_with_mergeinfo(
children_with_mergeinfo, wc_path);
@@ -6528,7 +6580,7 @@ get_mergeinfo_paths(apr_array_header_t *children_with_mergeinfo,
hi;
hi = apr_hash_next(hi))
{
- const char *wc_path = svn__apr_hash_index_key(hi);
+ const char *wc_path = apr_hash_this_key(hi);
svn_client__merge_path_t *child = get_child_with_mergeinfo(
children_with_mergeinfo, wc_path);
@@ -6571,7 +6623,7 @@ get_mergeinfo_paths(apr_array_header_t *children_with_mergeinfo,
SVN_ERR(svn_wc__node_get_children_of_working_node(
&immediate_children, ctx->wc_ctx,
- target->abspath, FALSE, scratch_pool, scratch_pool));
+ target->abspath, scratch_pool, scratch_pool));
for (j = 0; j < immediate_children->nelts; j++)
{
@@ -6655,9 +6707,10 @@ get_mergeinfo_paths(apr_array_header_t *children_with_mergeinfo,
const apr_array_header_t *children;
int j;
- SVN_ERR(svn_wc__node_get_children(&children,
+ SVN_ERR(svn_wc__node_get_children_of_working_node(
+ &children,
ctx->wc_ctx,
- child->abspath, FALSE,
+ child->abspath,
iterpool, iterpool));
for (j = 0; j < children->nelts; j++)
{
@@ -7104,7 +7157,8 @@ normalize_merge_sources_internal(apr_array_header_t **merge_sources_p,
SVN_ERR(svn_client__get_copy_source(&original_repos_relpath,
&original_revision,
segment_url,
- &range_start_rev, ctx,
+ &range_start_rev,
+ ra_session, ctx,
result_pool, scratch_pool));
/* Got copyfrom data? Fix up the first segment to cover
back to COPYFROM_REV + 1, and then prepend a new
@@ -7117,7 +7171,7 @@ normalize_merge_sources_internal(apr_array_header_t **merge_sources_p,
new_segment->path = original_repos_relpath;
new_segment->range_start = original_revision;
new_segment->range_end = original_revision;
- svn_sort__array_insert(&new_segment, segments, 0);
+ svn_sort__array_insert(segments, &new_segment, 0);
}
}
}
@@ -7783,7 +7837,7 @@ process_children_with_new_mergeinfo(merge_cmd_baton_t *merge_b,
hi;
hi = apr_hash_next(hi))
{
- const char *abspath_with_new_mergeinfo = svn__apr_hash_index_key(hi);
+ const char *abspath_with_new_mergeinfo = apr_hash_this_key(hi);
svn_mergeinfo_t path_inherited_mergeinfo;
svn_mergeinfo_t path_explicit_mergeinfo;
svn_client__merge_path_t *new_child;
@@ -7886,7 +7940,7 @@ path_is_subtree(const char *local_abspath,
for (hi = apr_hash_first(pool, subtrees);
hi; hi = apr_hash_next(hi))
{
- const char *path_touched_by_merge = svn__apr_hash_index_key(hi);
+ const char *path_touched_by_merge = apr_hash_this_key(hi);
if (svn_dirent_is_ancestor(local_abspath, path_touched_by_merge))
return TRUE;
}
@@ -8001,8 +8055,8 @@ log_find_operative_subtree_revs(void *baton,
hi;
hi = apr_hash_next(hi))
{
- const char *path = svn__apr_hash_index_key(hi);
- svn_log_changed_path2_t *change = svn__apr_hash_index_val(hi);
+ const char *path = apr_hash_this_key(hi);
+ svn_log_changed_path2_t *change = apr_hash_this_val(hi);
{
const char *child;
@@ -8638,7 +8692,7 @@ record_mergeinfo_for_dir_merge(svn_mergeinfo_catalog_t result_catalog,
/* Allow mergeinfo on switched subtrees to elide to the
repository. Otherwise limit elision to the merge target
- for now. do_directory_merge() will eventually try to
+ for now. do_merge() will eventually try to
elide that when the merge is complete. */
SVN_ERR(svn_client__elide_mergeinfo(
child->abspath,
@@ -8687,7 +8741,7 @@ record_mergeinfo_for_added_subtrees(
iterpool = svn_pool_create(pool);
for (hi = apr_hash_first(pool, added_abspaths); hi; hi = apr_hash_next(hi))
{
- const char *added_abspath = svn__apr_hash_index_key(hi);
+ const char *added_abspath = apr_hash_this_key(hi);
const char *dir_abspath;
svn_mergeinfo_t parent_mergeinfo;
svn_mergeinfo_t added_path_mergeinfo;
@@ -8896,7 +8950,7 @@ log_noop_revs(void *baton,
hi;
hi = apr_hash_next(hi))
{
- const char *fspath = svn__apr_hash_index_key(hi);
+ const char *fspath = apr_hash_this_key(hi);
const char *rel_path;
const char *cwmi_abspath;
svn_rangelist_t *paths_explicit_rangelist = NULL;
@@ -9053,24 +9107,23 @@ remove_noop_subtree_ranges(const merge_source_t *source,
svn_pool_clear(iterpool);
- /* Issue #4269: Keep track of the longest common ancestor of all the
- subtrees which require merges. This may be a child of
- TARGET->ABSPATH, which will allow us to narrow the log request
- below. */
+ /* CHILD->REMAINING_RANGES will be NULL if child is absent. */
if (child->remaining_ranges && child->remaining_ranges->nelts)
{
+ /* Issue #4269: Keep track of the longest common ancestor of all the
+ subtrees which require merges. This may be a child of
+ TARGET->ABSPATH, which will allow us to narrow the log request
+ below. */
if (longest_common_subtree_ancestor)
longest_common_subtree_ancestor = svn_dirent_get_longest_ancestor(
longest_common_subtree_ancestor, child->abspath, scratch_pool);
else
longest_common_subtree_ancestor = child->abspath;
- }
- /* CHILD->REMAINING_RANGES will be NULL if child is absent. */
- if (child->remaining_ranges && child->remaining_ranges->nelts)
- SVN_ERR(svn_rangelist_merge2(subtree_remaining_ranges,
- child->remaining_ranges,
- scratch_pool, iterpool));
+ SVN_ERR(svn_rangelist_merge2(subtree_remaining_ranges,
+ child->remaining_ranges,
+ scratch_pool, iterpool));
+ }
}
svn_pool_destroy(iterpool);
@@ -9464,7 +9517,7 @@ do_mergeinfo_aware_dir_merge(svn_mergeinfo_catalog_t result_catalog,
{
if (!merge_b->record_only)
{
- /* Reset cur_ancestor_abspath to null so that subsequent cherry
+ /* Reset the last notification path so that subsequent cherry
picked revision ranges will be notified upon subsequent
operative merge. */
merge_b->notify_begin.last_abspath = NULL;
@@ -10226,7 +10279,7 @@ ensure_wc_is_suitable_merge_target(const char *target_abspath,
svn_boolean_t is_modified;
SVN_ERR(svn_wc__has_local_mods(&is_modified, ctx->wc_ctx,
- target_abspath,
+ target_abspath, TRUE,
ctx->cancel_func,
ctx->cancel_baton,
scratch_pool));
@@ -10724,7 +10777,7 @@ log_find_operative_revs(void *baton,
hi = apr_hash_next(hi))
{
const char *subtree_missing_this_rev;
- const char *path = svn__apr_hash_index_key(hi);
+ const char *path = apr_hash_this_key(hi);
const char *rel_path;
const char *source_rel_path;
svn_boolean_t in_catalog;
@@ -10852,7 +10905,7 @@ find_unsynced_ranges(const svn_client__pathrev_t *source_loc,
hi_catalog;
hi_catalog = apr_hash_next(hi_catalog))
{
- svn_mergeinfo_t mergeinfo = svn__apr_hash_index_val(hi_catalog);
+ svn_mergeinfo_t mergeinfo = apr_hash_this_val(hi_catalog);
SVN_ERR(svn_rangelist__merge_many(potentially_unmerged_ranges,
mergeinfo,
@@ -11058,8 +11111,8 @@ find_unmerged_mergeinfo(svn_mergeinfo_catalog_t *unmerged_to_source_catalog,
hi;
hi = apr_hash_next(hi))
{
- const char *target_path = svn__apr_hash_index_key(hi);
- svn_mergeinfo_t target_history_as_mergeinfo = svn__apr_hash_index_val(hi);
+ const char *target_path = apr_hash_this_key(hi);
+ svn_mergeinfo_t target_history_as_mergeinfo = apr_hash_this_val(hi);
const char *path_rel_to_session
= svn_relpath_skip_ancestor(target_repos_rel_path, target_path);
const char *source_path;
@@ -11143,11 +11196,11 @@ find_unmerged_mergeinfo(svn_mergeinfo_catalog_t *unmerged_to_source_catalog,
hi;
hi = apr_hash_next(hi))
{
- const char *source_path = svn__apr_hash_index_key(hi);
+ const char *source_path = apr_hash_this_key(hi);
const char *path_rel_to_session =
svn_relpath_skip_ancestor(source_repos_rel_path, source_path);
const char *source_url;
- svn_mergeinfo_t source_mergeinfo = svn__apr_hash_index_val(hi);
+ svn_mergeinfo_t source_mergeinfo = apr_hash_this_val(hi);
svn_mergeinfo_t filtered_mergeinfo;
svn_client__pathrev_t *target_pathrev;
svn_mergeinfo_t target_history_as_mergeinfo;
@@ -11296,7 +11349,7 @@ calculate_left_hand_side(svn_client__pathrev_t **left_p,
hi;
hi = apr_hash_next(hi))
{
- const char *local_abspath = svn__apr_hash_index_key(hi);
+ const char *local_abspath = apr_hash_this_key(hi);
svn_client__pathrev_t *target_child;
const char *repos_relpath;
svn_mergeinfo_t target_history_as_mergeinfo;
@@ -11523,7 +11576,7 @@ find_reintegrate_merge(merge_source_t **source_p,
SVN_ERR(svn_mergeinfo__catalog_to_formatted_string(
&source_mergeinfo_cat_string,
final_unmerged_catalog,
- " ", " Missing ranges: ", scratch_pool));
+ " ", _(" Missing ranges: "), scratch_pool));
return svn_error_createf(SVN_ERR_CLIENT_NOT_READY_TO_MERGE,
NULL,
_("Reintegrate can only be used if "
@@ -11583,9 +11636,6 @@ open_reintegrate_source_and_target(svn_ra_session_t **source_ra_session_p,
SVN_ERR(open_target_wc(&target, target_abspath,
FALSE, FALSE, FALSE,
ctx, scratch_pool, scratch_pool));
- SVN_ERR(svn_client_open_ra_session2(target_ra_session_p,
- target->loc.url, target->abspath,
- ctx, result_pool, scratch_pool));
if (! target->loc.url)
return svn_error_createf(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
_("Can't reintegrate into '%s' because it is "
@@ -11594,6 +11644,10 @@ open_reintegrate_source_and_target(svn_ra_session_t **source_ra_session_p,
svn_dirent_local_style(target->abspath,
scratch_pool));
+ SVN_ERR(svn_client_open_ra_session2(target_ra_session_p,
+ target->loc.url, target->abspath,
+ ctx, result_pool, scratch_pool));
+
SVN_ERR(svn_client__ra_session_from_path2(
source_ra_session_p, &source_loc,
source_path_or_url, NULL, source_peg_revision, source_peg_revision,
@@ -11647,6 +11701,7 @@ merge_reintegrate_locked(conflict_report_t **conflict_report,
if (! source)
{
+ *conflict_report = NULL;
return SVN_NO_ERROR;
}
@@ -11943,8 +11998,8 @@ location_on_branch_at_rev(const branch_history_t *branch_history,
for (hi = apr_hash_first(scratch_pool, branch_history->history); hi;
hi = apr_hash_next(hi))
{
- const char *fspath = svn__apr_hash_index_key(hi);
- svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi);
+ const char *fspath = apr_hash_this_key(hi);
+ svn_rangelist_t *rangelist = apr_hash_this_val(hi);
int i;
for (i = 0; i < rangelist->nelts; i++)
@@ -12347,7 +12402,16 @@ find_base_on_target(svn_client__pathrev_t **base_p,
return SVN_NO_ERROR;
}
-/* The body of client_find_automatic_merge(), which see.
+/* Find the last point at which the branch at S_T->source was completely
+ * merged to the branch at S_T->target or vice-versa.
+ *
+ * Fill in S_T->source_branch and S_T->target_branch and S_T->yca.
+ * Set *BASE_P to the merge base. Set *IS_REINTEGRATE_LIKE to true if
+ * an automatic merge from source to target would be a reintegration
+ * merge: that is, if the last automatic merge was in the opposite
+ * direction; or to false otherwise.
+ *
+ * If there is no youngest common ancestor, throw an error.
*/
static svn_error_t *
find_automatic_merge(svn_client__pathrev_t **base_p,
@@ -12417,6 +12481,9 @@ find_automatic_merge(svn_client__pathrev_t **base_p,
* Like find_automatic_merge() except that the target is
* specified by @a target_path_or_url at @a target_revision, which must
* refer to a repository location, instead of by a WC path argument.
+ *
+ * Set *MERGE_P to a new structure with all fields filled in except the
+ * 'allow_*' flags.
*/
static svn_error_t *
find_automatic_merge_no_wc(automatic_merge_t **merge_p,
@@ -12492,6 +12559,8 @@ client_find_automatic_merge(automatic_merge_t **merge_p,
source_and_target_t *s_t = apr_palloc(result_pool, sizeof(*s_t));
automatic_merge_t *merge = apr_palloc(result_pool, sizeof(*merge));
+ SVN_ERR_ASSERT(svn_dirent_is_absolute(target_abspath));
+
/* "Open" the target WC. Check the target WC for mixed-rev, local mods and
* switched subtrees yet to faster exit and notify user before contacting
* with server. After we find out what kind of merge is required, then if a
@@ -12503,12 +12572,19 @@ client_find_automatic_merge(automatic_merge_t **merge_p,
allow_switched_subtrees,
ctx, result_pool, scratch_pool));
+ if (!s_t->target->loc.url)
+ return svn_error_createf(SVN_ERR_CLIENT_UNRELATED_RESOURCES, NULL,
+ _("Can't perform automatic merge into '%s' "
+ "because it is locally added and therefore "
+ "not related to the merge source"),
+ svn_dirent_local_style(target_abspath,
+ scratch_pool));
+
/* Open RA sessions to the source and target trees. */
SVN_ERR(svn_client_open_ra_session2(&s_t->target_ra_session,
s_t->target->loc.url,
s_t->target->abspath,
ctx, result_pool, scratch_pool));
- /* ### check for null URL (i.e. added path) here, like in reintegrate? */
SVN_ERR(svn_client__ra_session_from_path2(
&s_t->source_ra_session, &s_t->source,
source_path_or_url, NULL, source_revision, source_revision,
@@ -12523,6 +12599,7 @@ client_find_automatic_merge(automatic_merge_t **merge_p,
ctx, result_pool, scratch_pool));
merge->yca = s_t->yca;
merge->right = s_t->source;
+ merge->target = &s_t->target->loc;
merge->allow_mixed_rev = allow_mixed_rev;
merge->allow_local_mods = allow_local_mods;
merge->allow_switched_subtrees = allow_switched_subtrees;
@@ -12711,14 +12788,21 @@ svn_client_get_merging_summary(svn_boolean_t *needs_reintegration,
target_is_wc = (! svn_path_is_url(target_path_or_url))
&& (target_revision->kind == svn_opt_revision_unspecified
- || target_revision->kind == svn_opt_revision_working);
+ || target_revision->kind == svn_opt_revision_working
+ || target_revision->kind == svn_opt_revision_base);
if (target_is_wc)
- SVN_ERR(client_find_automatic_merge(
- &merge,
- source_path_or_url, source_revision,
- target_path_or_url,
- TRUE, TRUE, TRUE, /* allow_* */
- ctx, scratch_pool, scratch_pool));
+ {
+ const char *target_abspath;
+
+ SVN_ERR(svn_dirent_get_absolute(&target_abspath, target_path_or_url,
+ scratch_pool));
+ SVN_ERR(client_find_automatic_merge(
+ &merge,
+ source_path_or_url, source_revision,
+ target_abspath,
+ TRUE, TRUE, TRUE, /* allow_* */
+ ctx, scratch_pool, scratch_pool));
+ }
else
SVN_ERR(find_automatic_merge_no_wc(
&merge,
diff --git a/subversion/libsvn_client/mergeinfo.c b/subversion/libsvn_client/mergeinfo.c
index 2d277f5f616e..622dbbca2261 100644
--- a/subversion/libsvn_client/mergeinfo.c
+++ b/subversion/libsvn_client/mergeinfo.c
@@ -38,12 +38,13 @@
#include "svn_client.h"
#include "svn_hash.h"
+#include "private/svn_client_private.h"
#include "private/svn_opt_private.h"
#include "private/svn_mergeinfo_private.h"
-#include "private/svn_wc_private.h"
#include "private/svn_ra_private.h"
+#include "private/svn_sorts_private.h"
+#include "private/svn_wc_private.h"
#include "private/svn_fspath.h"
-#include "private/svn_client_private.h"
#include "client.h"
#include "mergeinfo.h"
#include "svn_private_config.h"
@@ -223,7 +224,6 @@ svn_client__get_wc_mergeinfo(svn_mergeinfo_t *mergeinfo,
SVN_ERR(svn_wc__node_get_base(NULL, &base_revision, NULL, NULL, NULL, NULL,
ctx->wc_ctx, local_abspath,
TRUE /* ignore_enoent */,
- FALSE /* show_hidden */,
scratch_pool, scratch_pool));
iterpool = svn_pool_create(scratch_pool);
@@ -294,7 +294,7 @@ svn_client__get_wc_mergeinfo(svn_mergeinfo_t *mergeinfo,
SVN_ERR(svn_wc__node_get_base(NULL, &parent_base_rev, NULL, NULL,
NULL, NULL,
ctx->wc_ctx, local_abspath,
- TRUE, FALSE,
+ TRUE /* ignore_enoent */,
scratch_pool, scratch_pool));
/* ### This checks the WORKING changed_rev, so invalid on replacement
@@ -360,7 +360,7 @@ svn_client__get_wc_mergeinfo(svn_mergeinfo_t *mergeinfo,
SVN_ERR(svn_mergeinfo_inheritable2(mergeinfo, *mergeinfo, NULL,
SVN_INVALID_REVNUM, SVN_INVALID_REVNUM,
TRUE, result_pool, scratch_pool));
- svn_mergeinfo__remove_empty_rangelists(*mergeinfo, result_pool);
+ svn_mergeinfo__remove_empty_rangelists(*mergeinfo, scratch_pool);
}
if (inherited_p)
@@ -429,8 +429,8 @@ svn_client__get_wc_mergeinfo_catalog(svn_mergeinfo_catalog_t *mergeinfo_cat,
hi;
hi = apr_hash_next(hi))
{
- const char *node_abspath = svn__apr_hash_index_key(hi);
- svn_string_t *propval = svn__apr_hash_index_val(hi);
+ const char *node_abspath = apr_hash_this_key(hi);
+ svn_string_t *propval = apr_hash_this_val(hi);
svn_mergeinfo_t subtree_mergeinfo;
const char *repos_relpath;
@@ -482,7 +482,7 @@ svn_client__get_repos_mergeinfo(svn_mergeinfo_t *target_mergeinfo,
descendants. So if there is anything in the catalog it is the
mergeinfo for REL_PATH. */
*target_mergeinfo =
- svn__apr_hash_index_val(apr_hash_first(pool, tgt_mergeinfo_cat));
+ apr_hash_this_val(apr_hash_first(pool, tgt_mergeinfo_cat));
}
@@ -582,7 +582,7 @@ svn_client__get_wc_or_repos_mergeinfo(svn_mergeinfo_t *target_mergeinfo,
so we can peek into our catalog, but it ought to be the only
thing in the catalog, so we'll just fetch the first hash item. */
*target_mergeinfo =
- svn__apr_hash_index_val(apr_hash_first(pool, tgt_mergeinfo_cat));
+ apr_hash_this_val(apr_hash_first(pool, tgt_mergeinfo_cat));
}
@@ -623,7 +623,7 @@ svn_client__get_wc_or_repos_mergeinfo_catalog(
a URL and without that we cannot get accurate mergeinfo for
TARGET_WCPATH. */
SVN_ERR(svn_wc__node_get_origin(NULL, &target_rev, &repos_relpath,
- &repos_root, NULL, NULL,
+ &repos_root, NULL, NULL, NULL,
ctx->wc_ctx, local_abspath, FALSE,
scratch_pool, scratch_pool));
@@ -921,16 +921,13 @@ svn_client__elide_mergeinfo(const char *target_abspath,
{
svn_mergeinfo_t target_mergeinfo;
svn_mergeinfo_t mergeinfo = NULL;
- svn_boolean_t inherited;
- const char *walk_path;
svn_error_t *err;
/* Get the TARGET_WCPATH's explicit mergeinfo. */
- err = svn_client__get_wc_mergeinfo(&target_mergeinfo, &inherited,
- svn_mergeinfo_inherited,
+ err = svn_client__get_wc_mergeinfo(&target_mergeinfo, NULL,
+ svn_mergeinfo_explicit,
target_abspath,
- limit_abspath,
- &walk_path, FALSE,
+ NULL, NULL, FALSE,
ctx, pool, pool);
if (err)
{
@@ -950,7 +947,7 @@ svn_client__elide_mergeinfo(const char *target_abspath,
/* If TARGET_WCPATH has no explicit mergeinfo, there's nothing to
elide, we're done. */
- if (inherited || target_mergeinfo == NULL)
+ if (target_mergeinfo == NULL)
return SVN_NO_ERROR;
/* Get TARGET_WCPATH's inherited mergeinfo from the WC. */
@@ -958,7 +955,7 @@ svn_client__elide_mergeinfo(const char *target_abspath,
svn_mergeinfo_nearest_ancestor,
target_abspath,
limit_abspath,
- &walk_path, FALSE, ctx, pool, pool);
+ NULL, FALSE, ctx, pool, pool);
if (err)
{
if (err->apr_err == SVN_ERR_MERGEINFO_PARSE_ERROR)
@@ -1345,8 +1342,8 @@ filter_log_entry_with_rangelist(void *baton,
hi = apr_hash_next(hi))
{
int i;
- const char *path = svn__apr_hash_index_key(hi);
- svn_log_changed_path2_t *change = svn__apr_hash_index_val(hi);
+ const char *path = apr_hash_this_key(hi);
+ svn_log_changed_path2_t *change = apr_hash_this_val(hi);
const char *target_fspath_affected;
svn_mergeinfo_t nearest_ancestor_mergeinfo;
svn_boolean_t found_this_revision = FALSE;
@@ -1430,8 +1427,8 @@ filter_log_entry_with_rangelist(void *baton,
hi2;
hi2 = apr_hash_next(hi2))
{
- const char *mergeinfo_path = svn__apr_hash_index_key(hi2);
- svn_rangelist_t *rangelist = svn__apr_hash_index_val(hi2);
+ const char *mergeinfo_path = apr_hash_this_key(hi2);
+ svn_rangelist_t *rangelist = apr_hash_this_val(hi2);
/* Does the mergeinfo for PATH reflect if
LOG_ENTRY->REVISION was previously merged
@@ -1608,8 +1605,8 @@ mergeinfo_relpaths_to_urls(apr_hash_t **out_mergeinfo,
for (hi = apr_hash_first(scratch_pool, mergeinfo);
hi; hi = apr_hash_next(hi))
{
- const char *key = svn__apr_hash_index_key(hi);
- void *val = svn__apr_hash_index_val(hi);
+ const char *key = apr_hash_this_key(hi);
+ void *val = apr_hash_this_val(hi);
svn_hash_sets(full_path_mergeinfo,
svn_path_url_add_component2(repos_root_url, key + 1,
@@ -1927,14 +1924,14 @@ svn_client__mergeinfo_log(svn_boolean_t finding_merged,
hi_catalog;
hi_catalog = apr_hash_next(hi_catalog))
{
- svn_mergeinfo_t subtree_mergeinfo = svn__apr_hash_index_val(hi_catalog);
+ svn_mergeinfo_t subtree_mergeinfo = apr_hash_this_val(hi_catalog);
svn_mergeinfo_t subtree_history;
svn_mergeinfo_t subtree_source_history;
svn_mergeinfo_t subtree_inheritable_mergeinfo;
svn_mergeinfo_t subtree_noninheritable_mergeinfo;
svn_mergeinfo_t merged_noninheritable;
svn_mergeinfo_t merged;
- const char *subtree_path = svn__apr_hash_index_key(hi_catalog);
+ const char *subtree_path = apr_hash_this_key(hi_catalog);
svn_boolean_t is_subtree = strcmp(subtree_path,
target_repos_relpath) != 0;
svn_pool_clear(iterpool);
@@ -2057,8 +2054,7 @@ svn_client__mergeinfo_log(svn_boolean_t finding_merged,
{
svn_rangelist_t *deleted_rangelist;
svn_rangelist_t *added_rangelist;
- svn_rangelist_t *subtree_merged_rangelist =
- svn__apr_hash_index_val(hi);
+ svn_rangelist_t *subtree_merged_rangelist = apr_hash_this_val(hi);
svn_pool_clear(iterpool);
@@ -2134,15 +2130,14 @@ svn_client__mergeinfo_log(svn_boolean_t finding_merged,
svn_rangelist__initialize(youngest_range->end - 1,
youngest_range->end,
youngest_range->inheritable,
- scratch_pool);;
+ scratch_pool);
for (hi = apr_hash_first(scratch_pool, source_history);
hi;
hi = apr_hash_next(hi))
{
- const char *key = svn__apr_hash_index_key(hi);
- svn_rangelist_t *subtree_merged_rangelist =
- svn__apr_hash_index_val(hi);
+ const char *key = apr_hash_this_key(hi);
+ svn_rangelist_t *subtree_merged_rangelist = apr_hash_this_val(hi);
svn_rangelist_t *intersecting_rangelist;
svn_pool_clear(iterpool);
@@ -2230,6 +2225,8 @@ svn_client_suggest_merge_sources(apr_array_header_t **suggestions,
svn_mergeinfo_catalog_t mergeinfo_cat;
svn_mergeinfo_t mergeinfo;
apr_hash_index_t *hi;
+ apr_pool_t *session_pool = svn_pool_create(pool);
+ svn_ra_session_t *ra_session;
list = apr_array_make(pool, 1, sizeof(const char *));
@@ -2248,26 +2245,32 @@ svn_client_suggest_merge_sources(apr_array_header_t **suggestions,
1. The copyfrom source.
2. All remaining merge sources (unordered).
*/
+ SVN_ERR(svn_client__ra_session_from_path2(&ra_session, NULL, path_or_url,
+ NULL, peg_revision, peg_revision,
+ ctx, session_pool));
- /* ### TODO: Share ra_session batons to improve efficiency? */
SVN_ERR(get_mergeinfo(&mergeinfo_cat, &repos_root, path_or_url,
- peg_revision, FALSE, FALSE, ctx, NULL, pool, pool));
+ peg_revision, FALSE, FALSE,
+ ctx, ra_session, session_pool, session_pool));
if (mergeinfo_cat && apr_hash_count(mergeinfo_cat))
{
/* We asked only for the PATH_OR_URL's mergeinfo, not any of its
descendants. So if there is anything in the catalog it is the
mergeinfo for PATH_OR_URL. */
- mergeinfo = svn__apr_hash_index_val(apr_hash_first(pool, mergeinfo_cat));
+ mergeinfo = apr_hash_this_val(apr_hash_first(session_pool,
+ mergeinfo_cat));
}
else
{
mergeinfo = NULL;
}
+ /* ### Should we only add the last source or all copy sources back to
+ the origin? */
SVN_ERR(svn_client__get_copy_source(&copyfrom_path, &copyfrom_rev,
- path_or_url, peg_revision, ctx,
- pool, pool));
+ path_or_url, peg_revision, ra_session,
+ ctx, session_pool, session_pool));
if (copyfrom_path)
{
APR_ARRAY_PUSH(list, const char *) =
@@ -2276,9 +2279,11 @@ svn_client_suggest_merge_sources(apr_array_header_t **suggestions,
if (mergeinfo)
{
- for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
+ for (hi = apr_hash_first(session_pool, mergeinfo);
+ hi;
+ hi = apr_hash_next(hi))
{
- const char *rel_path = svn__apr_hash_index_key(hi);
+ const char *rel_path = apr_hash_this_key(hi);
if (copyfrom_path == NULL || strcmp(rel_path, copyfrom_path) != 0)
APR_ARRAY_PUSH(list, const char *) = \
@@ -2286,6 +2291,8 @@ svn_client_suggest_merge_sources(apr_array_header_t **suggestions,
}
}
+ svn_pool_destroy(session_pool);
+
*suggestions = list;
return SVN_NO_ERROR;
}
diff --git a/subversion/libsvn_client/mergeinfo.h b/subversion/libsvn_client/mergeinfo.h
index 0c4cf05d56e6..1d6d524f5dd2 100644
--- a/subversion/libsvn_client/mergeinfo.h
+++ b/subversion/libsvn_client/mergeinfo.h
@@ -316,7 +316,9 @@ svn_client__get_history_as_mergeinfo(svn_mergeinfo_t *mergeinfo_p,
/* Parse any explicit mergeinfo on LOCAL_ABSPATH and store it in
*MERGEINFO. If no record of any mergeinfo exists, set *MERGEINFO to NULL.
- Does not acount for inherited mergeinfo. */
+ Does not acount for inherited mergeinfo.
+
+ Allocate the result deeply in @a result_pool. */
svn_error_t *
svn_client__parse_mergeinfo(svn_mergeinfo_t *mergeinfo,
svn_wc_context_t *wc_ctx,
@@ -358,8 +360,6 @@ svn_client__record_wc_mergeinfo_catalog(apr_hash_t *result_catalog,
working copy or the nearest switched parent for an elision
destination, if none is found check the repository, otherwise check
as far as WC_ELISION_LIMIT_ABSPATH within the working copy.
- TARGET_WCPATH and WC_ELISION_LIMIT_ABSPATH, if it exists, must both be
- absolute or relative to the working directory.
Elision occurs if:
diff --git a/subversion/libsvn_client/mtcc.c b/subversion/libsvn_client/mtcc.c
new file mode 100644
index 000000000000..e0fc1e9441b0
--- /dev/null
+++ b/subversion/libsvn_client/mtcc.c
@@ -0,0 +1,1429 @@
+/*
+ * mtcc.c -- Multi Command Context implementation. This allows
+ * performing many operations without a working copy.
+ *
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ */
+
+#include "svn_dirent_uri.h"
+#include "svn_hash.h"
+#include "svn_path.h"
+#include "svn_props.h"
+#include "svn_pools.h"
+#include "svn_subst.h"
+
+#include "private/svn_client_mtcc.h"
+
+
+#include "svn_private_config.h"
+
+#include "client.h"
+
+#include <assert.h>
+
+#define SVN_PATH_IS_EMPTY(s) ((s)[0] == '\0')
+
+/* The kind of operation to perform in an mtcc_op_t */
+typedef enum mtcc_kind_t
+{
+ OP_OPEN_DIR,
+ OP_OPEN_FILE,
+ OP_ADD_DIR,
+ OP_ADD_FILE,
+ OP_DELETE
+} mtcc_kind_t;
+
+typedef struct mtcc_op_t
+{
+ const char *name; /* basename of operation */
+ mtcc_kind_t kind; /* editor operation */
+
+ apr_array_header_t *children; /* List of mtcc_op_t * */
+
+ const char *src_relpath; /* For ADD_DIR, ADD_FILE */
+ svn_revnum_t src_rev; /* For ADD_DIR, ADD_FILE */
+ svn_stream_t *src_stream; /* For ADD_FILE, OPEN_FILE */
+ svn_checksum_t *src_checksum; /* For ADD_FILE, OPEN_FILE */
+ svn_stream_t *base_stream; /* For ADD_FILE, OPEN_FILE */
+ const svn_checksum_t *base_checksum; /* For ADD_FILE, OPEN_FILE */
+
+ apr_array_header_t *prop_mods; /* For all except DELETE
+ List of svn_prop_t */
+
+ svn_boolean_t performed_stat; /* Verified kind with repository */
+} mtcc_op_t;
+
+/* Check if the mtcc doesn't contain any modifications yet */
+#define MTCC_UNMODIFIED(mtcc) \
+ ((mtcc->root_op->kind == OP_OPEN_DIR \
+ || mtcc->root_op->kind == OP_OPEN_FILE) \
+ && (mtcc->root_op->prop_mods == NULL \
+ || !mtcc->root_op->prop_mods->nelts) \
+ && (mtcc->root_op->children == NULL \
+ || !mtcc->root_op->children->nelts))
+
+struct svn_client__mtcc_t
+{
+ apr_pool_t *pool;
+ svn_revnum_t head_revision;
+ svn_revnum_t base_revision;
+
+ svn_ra_session_t *ra_session;
+ svn_client_ctx_t *ctx;
+
+ mtcc_op_t *root_op;
+};
+
+static mtcc_op_t *
+mtcc_op_create(const char *name,
+ svn_boolean_t add,
+ svn_boolean_t directory,
+ apr_pool_t *result_pool)
+{
+ mtcc_op_t *op;
+
+ op = apr_pcalloc(result_pool, sizeof(*op));
+ op->name = name ? apr_pstrdup(result_pool, name) : "";
+
+ if (add)
+ op->kind = directory ? OP_ADD_DIR : OP_ADD_FILE;
+ else
+ op->kind = directory ? OP_OPEN_DIR : OP_OPEN_FILE;
+
+ if (directory)
+ op->children = apr_array_make(result_pool, 4, sizeof(mtcc_op_t *));
+
+ op->src_rev = SVN_INVALID_REVNUM;
+
+ return op;
+}
+
+static svn_error_t *
+mtcc_op_find(mtcc_op_t **op,
+ svn_boolean_t *created,
+ const char *relpath,
+ mtcc_op_t *base_op,
+ svn_boolean_t find_existing,
+ svn_boolean_t find_deletes,
+ svn_boolean_t create_file,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ const char *name;
+ const char *child;
+ int i;
+
+ assert(svn_relpath_is_canonical(relpath));
+ if (created)
+ *created = FALSE;
+
+ if (SVN_PATH_IS_EMPTY(relpath))
+ {
+ if (find_existing)
+ *op = base_op;
+ else
+ *op = NULL;
+
+ return SVN_NO_ERROR;
+ }
+
+ child = strchr(relpath, '/');
+
+ if (child)
+ {
+ name = apr_pstrmemdup(scratch_pool, relpath, (child-relpath));
+ child++; /* Skip '/' */
+ }
+ else
+ name = relpath;
+
+ if (!base_op->children)
+ {
+ if (!created)
+ {
+ *op = NULL;
+ return SVN_NO_ERROR;
+ }
+ else
+ return svn_error_createf(SVN_ERR_FS_NOT_DIRECTORY, NULL,
+ _("Can't operate on '%s' because '%s' is not a "
+ "directory"),
+ name, base_op->name);
+ }
+
+ for (i = base_op->children->nelts-1; i >= 0 ; i--)
+ {
+ mtcc_op_t *cop;
+
+ cop = APR_ARRAY_IDX(base_op->children, i, mtcc_op_t *);
+
+ if (! strcmp(cop->name, name)
+ && (find_deletes || cop->kind != OP_DELETE))
+ {
+ return svn_error_trace(
+ mtcc_op_find(op, created, child ? child : "", cop,
+ find_existing, find_deletes, create_file,
+ result_pool, scratch_pool));
+ }
+ }
+
+ if (!created)
+ {
+ *op = NULL;
+ return SVN_NO_ERROR;
+ }
+
+ {
+ mtcc_op_t *cop;
+
+ cop = mtcc_op_create(name, FALSE, child || !create_file, result_pool);
+
+ APR_ARRAY_PUSH(base_op->children, mtcc_op_t *) = cop;
+
+ if (!child)
+ {
+ *op = cop;
+ *created = TRUE;
+ return SVN_NO_ERROR;
+ }
+
+ return svn_error_trace(
+ mtcc_op_find(op, created, child, cop, find_existing,
+ find_deletes, create_file,
+ result_pool, scratch_pool));
+ }
+}
+
+/* Gets the original repository location of RELPATH, checking things
+ like copies, moves, etc. */
+static svn_error_t *
+get_origin(svn_boolean_t *done,
+ const char **origin_relpath,
+ svn_revnum_t *rev,
+ mtcc_op_t *op,
+ const char *relpath,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ const char *child;
+ const char *name;
+ if (SVN_PATH_IS_EMPTY(relpath))
+ {
+ if (op->kind == OP_ADD_DIR || op->kind == OP_ADD_FILE)
+ *done = TRUE;
+ *origin_relpath = op->src_relpath
+ ? apr_pstrdup(result_pool, op->src_relpath)
+ : NULL;
+ *rev = op->src_rev;
+ return SVN_NO_ERROR;
+ }
+
+ child = strchr(relpath, '/');
+ if (child)
+ {
+ name = apr_pstrmemdup(scratch_pool, relpath, child-relpath);
+ child++; /* Skip '/' */
+ }
+ else
+ name = relpath;
+
+ if (op->children && op->children->nelts)
+ {
+ int i;
+
+ for (i = op->children->nelts-1; i >= 0; i--)
+ {
+ mtcc_op_t *cop;
+
+ cop = APR_ARRAY_IDX(op->children, i, mtcc_op_t *);
+
+ if (! strcmp(cop->name, name))
+ {
+ if (cop->kind == OP_DELETE)
+ {
+ *done = TRUE;
+ return SVN_NO_ERROR;
+ }
+
+ SVN_ERR(get_origin(done, origin_relpath, rev,
+ cop, child ? child : "",
+ result_pool, scratch_pool));
+
+ if (*origin_relpath || *done)
+ return SVN_NO_ERROR;
+
+ break;
+ }
+ }
+ }
+
+ if (op->kind == OP_ADD_DIR || op->kind == OP_ADD_FILE)
+ {
+ *done = TRUE;
+ if (op->src_relpath)
+ {
+ *origin_relpath = svn_relpath_join(op->src_relpath, relpath,
+ result_pool);
+ *rev = op->src_rev;
+ }
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Obtains the original repository location for an mtcc relpath as
+ *ORIGIN_RELPATH @ *REV, if it has one. If it has not and IGNORE_ENOENT
+ is TRUE report *ORIGIN_RELPATH as NULL, otherwise return an error */
+static svn_error_t *
+mtcc_get_origin(const char **origin_relpath,
+ svn_revnum_t *rev,
+ const char *relpath,
+ svn_boolean_t ignore_enoent,
+ svn_client__mtcc_t *mtcc,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_boolean_t done = FALSE;
+
+ *origin_relpath = NULL;
+ *rev = SVN_INVALID_REVNUM;
+
+ SVN_ERR(get_origin(&done, origin_relpath, rev, mtcc->root_op, relpath,
+ result_pool, scratch_pool));
+
+ if (!*origin_relpath && !done)
+ {
+ *origin_relpath = apr_pstrdup(result_pool, relpath);
+ *rev = mtcc->base_revision;
+ }
+ else if (!ignore_enoent)
+ {
+ return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
+ _("No origin found for node at '%s'"),
+ relpath);
+ }
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client__mtcc_create(svn_client__mtcc_t **mtcc,
+ const char *anchor_url,
+ svn_revnum_t base_revision,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ apr_pool_t *mtcc_pool;
+
+ mtcc_pool = svn_pool_create(result_pool);
+
+ *mtcc = apr_pcalloc(mtcc_pool, sizeof(**mtcc));
+ (*mtcc)->pool = mtcc_pool;
+
+ (*mtcc)->root_op = mtcc_op_create(NULL, FALSE, TRUE, mtcc_pool);
+
+ (*mtcc)->ctx = ctx;
+
+ SVN_ERR(svn_client_open_ra_session2(&(*mtcc)->ra_session, anchor_url,
+ NULL /* wri_abspath */, ctx,
+ mtcc_pool, scratch_pool));
+
+ SVN_ERR(svn_ra_get_latest_revnum((*mtcc)->ra_session, &(*mtcc)->head_revision,
+ scratch_pool));
+
+ if (SVN_IS_VALID_REVNUM(base_revision))
+ (*mtcc)->base_revision = base_revision;
+ else
+ (*mtcc)->base_revision = (*mtcc)->head_revision;
+
+ if ((*mtcc)->base_revision > (*mtcc)->head_revision)
+ return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
+ _("No such revision %ld (HEAD is %ld)"),
+ base_revision, (*mtcc)->head_revision);
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+update_copy_src(mtcc_op_t *op,
+ const char *add_relpath,
+ apr_pool_t *result_pool)
+{
+ int i;
+
+ if (op->src_relpath)
+ op->src_relpath = svn_relpath_join(add_relpath, op->src_relpath,
+ result_pool);
+
+ if (!op->children)
+ return SVN_NO_ERROR;
+
+ for (i = 0; i < op->children->nelts; i++)
+ {
+ mtcc_op_t *cop;
+
+ cop = APR_ARRAY_IDX(op->children, i, mtcc_op_t *);
+
+ SVN_ERR(update_copy_src(cop, add_relpath, result_pool));
+ }
+
+ return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+mtcc_reparent(const char *new_anchor_url,
+ svn_client__mtcc_t *mtcc,
+ apr_pool_t *scratch_pool)
+{
+ const char *session_url;
+ const char *up;
+
+ SVN_ERR(svn_ra_get_session_url(mtcc->ra_session, &session_url,
+ scratch_pool));
+
+ up = svn_uri_skip_ancestor(new_anchor_url, session_url, scratch_pool);
+
+ if (! up)
+ {
+ return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
+ _("'%s' is not an ancestor of '%s'"),
+ new_anchor_url, session_url);
+ }
+ else if (!*up)
+ {
+ return SVN_NO_ERROR; /* Same url */
+ }
+
+ /* Update copy origins recursively...:( */
+ SVN_ERR(update_copy_src(mtcc->root_op, up, mtcc->pool));
+
+ SVN_ERR(svn_ra_reparent(mtcc->ra_session, new_anchor_url, scratch_pool));
+
+ /* Create directory open operations for new ancestors */
+ while (*up)
+ {
+ mtcc_op_t *root_op;
+
+ mtcc->root_op->name = svn_relpath_basename(up, mtcc->pool);
+ up = svn_relpath_dirname(up, scratch_pool);
+
+ root_op = mtcc_op_create(NULL, FALSE, TRUE, mtcc->pool);
+
+ APR_ARRAY_PUSH(root_op->children, mtcc_op_t *) = mtcc->root_op;
+
+ mtcc->root_op = root_op;
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Check if it is safe to create a new node at NEW_RELPATH. Return a proper
+ error if it is not */
+static svn_error_t *
+mtcc_verify_create(svn_client__mtcc_t *mtcc,
+ const char *new_relpath,
+ apr_pool_t *scratch_pool)
+{
+ svn_node_kind_t kind;
+
+ if (*new_relpath || !MTCC_UNMODIFIED(mtcc))
+ {
+ mtcc_op_t *op;
+
+ SVN_ERR(mtcc_op_find(&op, NULL, new_relpath, mtcc->root_op, TRUE, FALSE,
+ FALSE, mtcc->pool, scratch_pool));
+
+ if (op)
+ return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL,
+ _("Path '%s' already exists"),
+ new_relpath);
+
+ SVN_ERR(mtcc_op_find(&op, NULL, new_relpath, mtcc->root_op, TRUE, TRUE,
+ FALSE, mtcc->pool, scratch_pool));
+
+ if (op)
+ return SVN_NO_ERROR; /* Node is explicitly deleted. We can replace */
+ }
+
+ /* mod_dav_svn used to allow overwriting existing directories. Let's hide
+ that for users of this api */
+ SVN_ERR(svn_client__mtcc_check_path(&kind, new_relpath, FALSE,
+ mtcc, scratch_pool));
+
+ if (kind != svn_node_none)
+ return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL,
+ _("Path '%s' already exists"),
+ new_relpath);
+
+ return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_client__mtcc_add_add_file(const char *relpath,
+ svn_stream_t *src_stream,
+ const svn_checksum_t *src_checksum,
+ svn_client__mtcc_t *mtcc,
+ apr_pool_t *scratch_pool)
+{
+ mtcc_op_t *op;
+ svn_boolean_t created;
+ SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath) && src_stream);
+
+ SVN_ERR(mtcc_verify_create(mtcc, relpath, scratch_pool));
+
+ if (SVN_PATH_IS_EMPTY(relpath) && MTCC_UNMODIFIED(mtcc))
+ {
+ /* Turn the root operation into a file addition */
+ op = mtcc->root_op;
+ }
+ else
+ {
+ SVN_ERR(mtcc_op_find(&op, &created, relpath, mtcc->root_op, FALSE, FALSE,
+ TRUE, mtcc->pool, scratch_pool));
+
+ if (!op || !created)
+ {
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("Can't add file at '%s'"),
+ relpath);
+ }
+ }
+
+ op->kind = OP_ADD_FILE;
+ op->src_stream = src_stream;
+ op->src_checksum = src_checksum ? svn_checksum_dup(src_checksum, mtcc->pool)
+ : NULL;
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client__mtcc_add_copy(const char *src_relpath,
+ svn_revnum_t revision,
+ const char *dst_relpath,
+ svn_client__mtcc_t *mtcc,
+ apr_pool_t *scratch_pool)
+{
+ mtcc_op_t *op;
+ svn_boolean_t created;
+ svn_node_kind_t kind;
+
+ SVN_ERR_ASSERT(svn_relpath_is_canonical(src_relpath)
+ && svn_relpath_is_canonical(dst_relpath));
+
+ if (! SVN_IS_VALID_REVNUM(revision))
+ revision = mtcc->head_revision;
+ else if (revision > mtcc->head_revision)
+ {
+ return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
+ _("No such revision %ld"), revision);
+ }
+
+ SVN_ERR(mtcc_verify_create(mtcc, dst_relpath, scratch_pool));
+
+ /* Subversion requires the kind of a copy */
+ SVN_ERR(svn_ra_check_path(mtcc->ra_session, src_relpath, revision, &kind,
+ scratch_pool));
+
+ if (kind != svn_node_dir && kind != svn_node_file)
+ {
+ return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
+ _("Path '%s' not found in revision %ld"),
+ src_relpath, revision);
+ }
+
+ SVN_ERR(mtcc_op_find(&op, &created, dst_relpath, mtcc->root_op, FALSE, FALSE,
+ (kind == svn_node_file), mtcc->pool, scratch_pool));
+
+ if (!op || !created)
+ {
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("Can't add node at '%s'"),
+ dst_relpath);
+ }
+
+ op->kind = (kind == svn_node_file) ? OP_ADD_FILE : OP_ADD_DIR;
+ op->src_relpath = apr_pstrdup(mtcc->pool, src_relpath);
+ op->src_rev = revision;
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client__mtcc_add_delete(const char *relpath,
+ svn_client__mtcc_t *mtcc,
+ apr_pool_t *scratch_pool)
+{
+ mtcc_op_t *op;
+ svn_boolean_t created;
+ svn_node_kind_t kind;
+
+ SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
+
+ SVN_ERR(svn_client__mtcc_check_path(&kind, relpath, FALSE,
+ mtcc, scratch_pool));
+
+ if (kind == svn_node_none)
+ return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
+ _("Can't delete node at '%s' as it "
+ "does not exist"),
+ relpath);
+
+ if (SVN_PATH_IS_EMPTY(relpath) && MTCC_UNMODIFIED(mtcc))
+ {
+ /* Turn root operation into delete */
+ op = mtcc->root_op;
+ }
+ else
+ {
+ SVN_ERR(mtcc_op_find(&op, &created, relpath, mtcc->root_op, FALSE, TRUE,
+ TRUE, mtcc->pool, scratch_pool));
+
+ if (!op || !created)
+ {
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("Can't delete node at '%s'"),
+ relpath);
+ }
+ }
+
+ op->kind = OP_DELETE;
+ op->children = NULL;
+ op->prop_mods = NULL;
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client__mtcc_add_mkdir(const char *relpath,
+ svn_client__mtcc_t *mtcc,
+ apr_pool_t *scratch_pool)
+{
+ mtcc_op_t *op;
+ svn_boolean_t created;
+ SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
+
+ SVN_ERR(mtcc_verify_create(mtcc, relpath, scratch_pool));
+
+ if (SVN_PATH_IS_EMPTY(relpath) && MTCC_UNMODIFIED(mtcc))
+ {
+ /* Turn the root of the operation in an MKDIR */
+ mtcc->root_op->kind = OP_ADD_DIR;
+
+ return SVN_NO_ERROR;
+ }
+
+ SVN_ERR(mtcc_op_find(&op, &created, relpath, mtcc->root_op, FALSE, FALSE,
+ FALSE, mtcc->pool, scratch_pool));
+
+ if (!op || !created)
+ {
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("Can't create directory at '%s'"),
+ relpath);
+ }
+
+ op->kind = OP_ADD_DIR;
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client__mtcc_add_move(const char *src_relpath,
+ const char *dst_relpath,
+ svn_client__mtcc_t *mtcc,
+ apr_pool_t *scratch_pool)
+{
+ const char *origin_relpath;
+ svn_revnum_t origin_rev;
+
+ SVN_ERR(mtcc_get_origin(&origin_relpath, &origin_rev,
+ src_relpath, FALSE, mtcc,
+ scratch_pool, scratch_pool));
+
+ SVN_ERR(svn_client__mtcc_add_copy(src_relpath, mtcc->base_revision,
+ dst_relpath, mtcc, scratch_pool));
+ SVN_ERR(svn_client__mtcc_add_delete(src_relpath, mtcc, scratch_pool));
+
+ return SVN_NO_ERROR;
+}
+
+/* Baton for mtcc_prop_getter */
+struct mtcc_prop_get_baton
+{
+ svn_client__mtcc_t *mtcc;
+ const char *relpath;
+ svn_cancel_func_t cancel_func;
+ void *cancel_baton;
+};
+
+/* Implements svn_wc_canonicalize_svn_prop_get_file_t */
+static svn_error_t *
+mtcc_prop_getter(const svn_string_t **mime_type,
+ svn_stream_t *stream,
+ void *baton,
+ apr_pool_t *pool)
+{
+ struct mtcc_prop_get_baton *mpgb = baton;
+ const char *origin_relpath;
+ svn_revnum_t origin_rev;
+ apr_hash_t *props = NULL;
+
+ mtcc_op_t *op;
+
+ if (mime_type)
+ *mime_type = NULL;
+
+ /* Check if we have the information locally */
+ SVN_ERR(mtcc_op_find(&op, NULL, mpgb->relpath, mpgb->mtcc->root_op, TRUE,
+ FALSE, FALSE, pool, pool));
+
+ if (op)
+ {
+ if (mime_type)
+ {
+ int i;
+
+ for (i = 0; op->prop_mods && i < op->prop_mods->nelts; i++)
+ {
+ const svn_prop_t *mod = &APR_ARRAY_IDX(op->prop_mods, i,
+ svn_prop_t);
+
+ if (! strcmp(mod->name, SVN_PROP_MIME_TYPE))
+ {
+ *mime_type = svn_string_dup(mod->value, pool);
+ mime_type = NULL;
+ }
+ }
+ }
+
+ if (stream && op->src_stream)
+ {
+ svn_stream_mark_t *mark;
+ svn_error_t *err;
+
+ /* Is the source stream capable of being read multiple times? */
+ err = svn_stream_mark(op->src_stream, &mark, pool);
+
+ if (err && err->apr_err != SVN_ERR_STREAM_SEEK_NOT_SUPPORTED)
+ return svn_error_trace(err);
+ svn_error_clear(err);
+
+ if (!err)
+ {
+ err = svn_stream_copy3(svn_stream_disown(op->src_stream, pool),
+ svn_stream_disown(stream, pool),
+ mpgb->cancel_func, mpgb->cancel_baton,
+ pool);
+
+ SVN_ERR(svn_error_compose_create(
+ err,
+ svn_stream_seek(op->src_stream, mark)));
+ }
+ /* else: ### Create tempfile? */
+
+ stream = NULL; /* Stream is handled */
+ }
+ }
+
+ if (!stream && !mime_type)
+ return SVN_NO_ERROR;
+
+ SVN_ERR(mtcc_get_origin(&origin_relpath, &origin_rev, mpgb->relpath, TRUE,
+ mpgb->mtcc, pool, pool));
+
+ if (!origin_relpath)
+ return SVN_NO_ERROR; /* Nothing to fetch at repository */
+
+ SVN_ERR(svn_ra_get_file(mpgb->mtcc->ra_session, origin_relpath, origin_rev,
+ stream, NULL, mime_type ? &props : NULL, pool));
+
+ if (mime_type && props)
+ *mime_type = svn_hash_gets(props, SVN_PROP_MIME_TYPE);
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client__mtcc_add_propset(const char *relpath,
+ const char *propname,
+ const svn_string_t *propval,
+ svn_boolean_t skip_checks,
+ svn_client__mtcc_t *mtcc,
+ apr_pool_t *scratch_pool)
+{
+ mtcc_op_t *op;
+ SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
+
+ if (! svn_prop_name_is_valid(propname))
+ return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
+ _("Bad property name: '%s'"), propname);
+
+ if (svn_prop_is_known_svn_rev_prop(propname))
+ return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
+ _("Revision property '%s' not allowed "
+ "in this context"), propname);
+
+ if (svn_property_kind2(propname) == svn_prop_wc_kind)
+ return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
+ _("'%s' is a wcprop, thus not accessible "
+ "to clients"), propname);
+
+ if (!skip_checks && svn_prop_needs_translation(propname))
+ {
+ svn_string_t *translated_value;
+ SVN_ERR_W(svn_subst_translate_string2(&translated_value, NULL,
+ NULL, propval,
+ NULL, FALSE,
+ scratch_pool, scratch_pool),
+ _("Error normalizing property value"));
+
+ propval = translated_value;
+ }
+
+ if (propval && svn_prop_is_svn_prop(propname))
+ {
+ struct mtcc_prop_get_baton mpbg;
+ svn_node_kind_t kind;
+ SVN_ERR(svn_client__mtcc_check_path(&kind, relpath, FALSE, mtcc,
+ scratch_pool));
+
+ mpbg.mtcc = mtcc;
+ mpbg.relpath = relpath;
+ mpbg.cancel_func = mtcc->ctx->cancel_func;
+ mpbg.cancel_baton = mtcc->ctx->cancel_baton;
+
+ SVN_ERR(svn_wc_canonicalize_svn_prop(&propval, propname, propval,
+ relpath, kind, skip_checks,
+ mtcc_prop_getter, &mpbg,
+ scratch_pool));
+ }
+
+ if (SVN_PATH_IS_EMPTY(relpath) && MTCC_UNMODIFIED(mtcc))
+ {
+ svn_node_kind_t kind;
+
+ /* Probing the node for an unmodified root will fix the node type to
+ a file if necessary */
+
+ SVN_ERR(svn_client__mtcc_check_path(&kind, relpath, FALSE,
+ mtcc, scratch_pool));
+
+ if (kind == svn_node_none)
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("Can't set properties at not existing '%s'"),
+ relpath);
+
+ op = mtcc->root_op;
+ }
+ else
+ {
+ SVN_ERR(mtcc_op_find(&op, NULL, relpath, mtcc->root_op, TRUE, FALSE,
+ FALSE, mtcc->pool, scratch_pool));
+
+ if (!op)
+ {
+ svn_node_kind_t kind;
+ svn_boolean_t created;
+
+ /* ### TODO: Check if this node is within a newly copied directory,
+ and update origin values accordingly */
+
+ SVN_ERR(svn_client__mtcc_check_path(&kind, relpath, FALSE,
+ mtcc, scratch_pool));
+
+ if (kind == svn_node_none)
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("Can't set properties at not existing '%s'"),
+ relpath);
+
+ SVN_ERR(mtcc_op_find(&op, &created, relpath, mtcc->root_op, TRUE, FALSE,
+ (kind != svn_node_dir),
+ mtcc->pool, scratch_pool));
+
+ SVN_ERR_ASSERT(op != NULL);
+ }
+ }
+
+ if (!op->prop_mods)
+ op->prop_mods = apr_array_make(mtcc->pool, 4, sizeof(svn_prop_t));
+
+ {
+ svn_prop_t propchange;
+ propchange.name = apr_pstrdup(mtcc->pool, propname);
+
+ if (propval)
+ propchange.value = svn_string_dup(propval, mtcc->pool);
+ else
+ propchange.value = NULL;
+
+ APR_ARRAY_PUSH(op->prop_mods, svn_prop_t) = propchange;
+ }
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client__mtcc_add_update_file(const char *relpath,
+ svn_stream_t *src_stream,
+ const svn_checksum_t *src_checksum,
+ svn_stream_t *base_stream,
+ const svn_checksum_t *base_checksum,
+ svn_client__mtcc_t *mtcc,
+ apr_pool_t *scratch_pool)
+{
+ mtcc_op_t *op;
+ svn_boolean_t created;
+ svn_node_kind_t kind;
+ SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath) && src_stream);
+
+ SVN_ERR(svn_client__mtcc_check_path(&kind, relpath, FALSE,
+ mtcc, scratch_pool));
+
+ if (kind != svn_node_file)
+ return svn_error_createf(SVN_ERR_FS_NOT_FILE, NULL,
+ _("Can't update '%s' because it is not a file"),
+ relpath);
+
+ SVN_ERR(mtcc_op_find(&op, &created, relpath, mtcc->root_op, TRUE, FALSE,
+ TRUE, mtcc->pool, scratch_pool));
+
+ if (!op
+ || (op->kind != OP_OPEN_FILE && op->kind != OP_ADD_FILE)
+ || (op->src_stream != NULL))
+ {
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("Can't update file at '%s'"), relpath);
+ }
+
+ op->src_stream = src_stream;
+ op->src_checksum = src_checksum ? svn_checksum_dup(src_checksum, mtcc->pool)
+ : NULL;
+
+ op->base_stream = base_stream;
+ op->base_checksum = base_checksum ? svn_checksum_dup(base_checksum,
+ mtcc->pool)
+ : NULL;
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client__mtcc_check_path(svn_node_kind_t *kind,
+ const char *relpath,
+ svn_boolean_t check_repository,
+ svn_client__mtcc_t *mtcc,
+ apr_pool_t *scratch_pool)
+{
+ const char *origin_relpath;
+ svn_revnum_t origin_rev;
+ mtcc_op_t *op;
+
+ SVN_ERR_ASSERT(svn_relpath_is_canonical(relpath));
+
+ if (SVN_PATH_IS_EMPTY(relpath) && MTCC_UNMODIFIED(mtcc)
+ && !mtcc->root_op->performed_stat)
+ {
+ /* We know nothing about the root. Perhaps it is a file? */
+ SVN_ERR(svn_ra_check_path(mtcc->ra_session, "", mtcc->base_revision,
+ kind, scratch_pool));
+
+ mtcc->root_op->performed_stat = TRUE;
+ if (*kind == svn_node_file)
+ {
+ mtcc->root_op->kind = OP_OPEN_FILE;
+ mtcc->root_op->children = NULL;
+ }
+ return SVN_NO_ERROR;
+ }
+
+ SVN_ERR(mtcc_op_find(&op, NULL, relpath, mtcc->root_op, TRUE, FALSE,
+ FALSE, mtcc->pool, scratch_pool));
+
+ if (!op || (check_repository && !op->performed_stat))
+ {
+ SVN_ERR(mtcc_get_origin(&origin_relpath, &origin_rev,
+ relpath, TRUE, mtcc,
+ scratch_pool, scratch_pool));
+
+ if (!origin_relpath)
+ *kind = svn_node_none;
+ else
+ SVN_ERR(svn_ra_check_path(mtcc->ra_session, origin_relpath,
+ origin_rev, kind, scratch_pool));
+
+ if (op && *kind == svn_node_dir)
+ {
+ if (op->kind == OP_OPEN_DIR || op->kind == OP_ADD_DIR)
+ op->performed_stat = TRUE;
+ else if (op->kind == OP_OPEN_FILE || op->kind == OP_ADD_FILE)
+ return svn_error_createf(SVN_ERR_FS_NOT_FILE, NULL,
+ _("Can't perform file operation "
+ "on '%s' as it is not a file"),
+ relpath);
+ }
+ else if (op && *kind == svn_node_file)
+ {
+ if (op->kind == OP_OPEN_FILE || op->kind == OP_ADD_FILE)
+ op->performed_stat = TRUE;
+ else if (op->kind == OP_OPEN_DIR || op->kind == OP_ADD_DIR)
+ return svn_error_createf(SVN_ERR_FS_NOT_DIRECTORY, NULL,
+ _("Can't perform directory operation "
+ "on '%s' as it is not a directory"),
+ relpath);
+ }
+ else if (op && (op->kind == OP_OPEN_DIR || op->kind == OP_OPEN_FILE))
+ {
+ return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
+ _("Can't open '%s' as it does not exist"),
+ relpath);
+ }
+
+ return SVN_NO_ERROR;
+ }
+
+ /* op != NULL */
+ if (op->kind == OP_OPEN_DIR || op->kind == OP_ADD_DIR)
+ {
+ *kind = svn_node_dir;
+ return SVN_NO_ERROR;
+ }
+ else if (op->kind == OP_OPEN_FILE || op->kind == OP_ADD_FILE)
+ {
+ *kind = svn_node_file;
+ return SVN_NO_ERROR;
+ }
+ SVN_ERR_MALFUNCTION(); /* No other kinds defined as delete is filtered */
+}
+
+static svn_error_t *
+commit_properties(const svn_delta_editor_t *editor,
+ const mtcc_op_t *op,
+ void *node_baton,
+ apr_pool_t *scratch_pool)
+{
+ int i;
+ apr_pool_t *iterpool;
+
+ if (!op->prop_mods || op->prop_mods->nelts == 0)
+ return SVN_NO_ERROR;
+
+ iterpool = svn_pool_create(scratch_pool);
+ for (i = 0; i < op->prop_mods->nelts; i++)
+ {
+ const svn_prop_t *mod = &APR_ARRAY_IDX(op->prop_mods, i, svn_prop_t);
+
+ svn_pool_clear(iterpool);
+
+ if (op->kind == OP_ADD_DIR || op->kind == OP_OPEN_DIR)
+ SVN_ERR(editor->change_dir_prop(node_baton, mod->name, mod->value,
+ iterpool));
+ else if (op->kind == OP_ADD_FILE || op->kind == OP_OPEN_FILE)
+ SVN_ERR(editor->change_file_prop(node_baton, mod->name, mod->value,
+ iterpool));
+ }
+
+ svn_pool_destroy(iterpool);
+ return SVN_NO_ERROR;
+}
+
+/* Handles updating a file to a delta editor and then closes it */
+static svn_error_t *
+commit_file(const svn_delta_editor_t *editor,
+ mtcc_op_t *op,
+ void *file_baton,
+ const char *session_url,
+ const char *relpath,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ const char *text_checksum = NULL;
+ svn_checksum_t *src_checksum = op->src_checksum;
+ SVN_ERR(commit_properties(editor, op, file_baton, scratch_pool));
+
+ if (op->src_stream)
+ {
+ const char *base_checksum = NULL;
+ apr_pool_t *txdelta_pool = scratch_pool;
+ svn_txdelta_window_handler_t window_handler;
+ void *handler_baton;
+ svn_stream_t *src_stream = op->src_stream;
+
+ if (op->base_checksum && op->base_checksum->kind == svn_checksum_md5)
+ base_checksum = svn_checksum_to_cstring(op->base_checksum, scratch_pool);
+
+ /* ### TODO: Future enhancement: Allocate in special pool and send
+ files after the true edit operation, like a wc commit */
+ SVN_ERR(editor->apply_textdelta(file_baton, base_checksum, txdelta_pool,
+ &window_handler, &handler_baton));
+
+ if (ctx->notify_func2)
+ {
+ svn_wc_notify_t *notify;
+
+ notify = svn_wc_create_notify_url(
+ svn_path_url_add_component2(session_url, relpath,
+ scratch_pool),
+ svn_wc_notify_commit_postfix_txdelta,
+ scratch_pool);
+
+ notify->path = relpath;
+ notify->kind = svn_node_file;
+
+ ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool);
+ }
+
+ if (window_handler != svn_delta_noop_window_handler)
+ {
+ if (!src_checksum || src_checksum->kind != svn_checksum_md5)
+ src_stream = svn_stream_checksummed2(src_stream, &src_checksum, NULL,
+ svn_checksum_md5,
+ TRUE, scratch_pool);
+
+ if (!op->base_stream)
+ SVN_ERR(svn_txdelta_send_stream(src_stream,
+ window_handler, handler_baton, NULL,
+ scratch_pool));
+ else
+ SVN_ERR(svn_txdelta_run(op->base_stream, src_stream,
+ window_handler, handler_baton,
+ svn_checksum_md5, NULL,
+ ctx->cancel_func, ctx->cancel_baton,
+ scratch_pool, scratch_pool));
+ }
+
+ SVN_ERR(svn_stream_close(src_stream));
+ if (op->base_stream)
+ SVN_ERR(svn_stream_close(op->base_stream));
+ }
+
+ if (src_checksum && src_checksum->kind == svn_checksum_md5)
+ text_checksum = svn_checksum_to_cstring(src_checksum, scratch_pool);
+
+ return svn_error_trace(editor->close_file(file_baton, text_checksum,
+ scratch_pool));
+}
+
+/* Handles updating a directory to a delta editor and then closes it */
+static svn_error_t *
+commit_directory(const svn_delta_editor_t *editor,
+ mtcc_op_t *op,
+ const char *relpath,
+ svn_revnum_t base_rev,
+ void *dir_baton,
+ const char *session_url,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ SVN_ERR(commit_properties(editor, op, dir_baton, scratch_pool));
+
+ if (op->children && op->children->nelts > 0)
+ {
+ apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+ int i;
+
+ for (i = 0; i < op->children->nelts; i++)
+ {
+ mtcc_op_t *cop;
+ const char * child_relpath;
+ void *child_baton;
+
+ cop = APR_ARRAY_IDX(op->children, i, mtcc_op_t *);
+
+ svn_pool_clear(iterpool);
+
+ if (ctx->cancel_func)
+ SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
+
+ child_relpath = svn_relpath_join(relpath, cop->name, iterpool);
+
+ switch (cop->kind)
+ {
+ case OP_DELETE:
+ SVN_ERR(editor->delete_entry(child_relpath, base_rev,
+ dir_baton, iterpool));
+ break;
+
+ case OP_ADD_DIR:
+ SVN_ERR(editor->add_directory(child_relpath, dir_baton,
+ cop->src_relpath
+ ? svn_path_url_add_component2(
+ session_url,
+ cop->src_relpath,
+ iterpool)
+ : NULL,
+ cop->src_rev,
+ iterpool, &child_baton));
+ SVN_ERR(commit_directory(editor, cop, child_relpath,
+ SVN_INVALID_REVNUM, child_baton,
+ session_url, ctx, iterpool));
+ break;
+ case OP_OPEN_DIR:
+ SVN_ERR(editor->open_directory(child_relpath, dir_baton,
+ base_rev, iterpool, &child_baton));
+ SVN_ERR(commit_directory(editor, cop, child_relpath,
+ base_rev, child_baton,
+ session_url, ctx, iterpool));
+ break;
+
+ case OP_ADD_FILE:
+ SVN_ERR(editor->add_file(child_relpath, dir_baton,
+ cop->src_relpath
+ ? svn_path_url_add_component2(
+ session_url,
+ cop->src_relpath,
+ iterpool)
+ : NULL,
+ cop->src_rev,
+ iterpool, &child_baton));
+ SVN_ERR(commit_file(editor, cop, child_baton,
+ session_url, child_relpath, ctx, iterpool));
+ break;
+ case OP_OPEN_FILE:
+ SVN_ERR(editor->open_file(child_relpath, dir_baton, base_rev,
+ iterpool, &child_baton));
+ SVN_ERR(commit_file(editor, cop, child_baton,
+ session_url, child_relpath, ctx, iterpool));
+ break;
+
+ default:
+ SVN_ERR_MALFUNCTION();
+ }
+ }
+ }
+
+ return svn_error_trace(editor->close_directory(dir_baton, scratch_pool));
+}
+
+
+/* Helper function to recursively create svn_client_commit_item3_t items
+ to provide to the log message callback */
+static svn_error_t *
+add_commit_items(mtcc_op_t *op,
+ const char *session_url,
+ const char *url,
+ apr_array_header_t *commit_items,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ if ((op->kind != OP_OPEN_DIR && op->kind != OP_OPEN_FILE)
+ || (op->prop_mods && op->prop_mods->nelts)
+ || (op->src_stream))
+ {
+ svn_client_commit_item3_t *item;
+
+ item = svn_client_commit_item3_create(result_pool);
+
+ item->path = NULL;
+ if (op->kind == OP_OPEN_DIR || op->kind == OP_ADD_DIR)
+ item->kind = svn_node_dir;
+ else if (op->kind == OP_OPEN_FILE || op->kind == OP_ADD_FILE)
+ item->kind = svn_node_file;
+ else
+ item->kind = svn_node_unknown;
+
+ item->url = apr_pstrdup(result_pool, url);
+ item->session_relpath = svn_uri_skip_ancestor(session_url, item->url,
+ result_pool);
+
+ if (op->src_relpath)
+ {
+ item->copyfrom_url = svn_path_url_add_component2(session_url,
+ op->src_relpath,
+ result_pool);
+ item->copyfrom_rev = op->src_rev;
+ item->state_flags |= SVN_CLIENT_COMMIT_ITEM_IS_COPY;
+ }
+ else
+ item->copyfrom_rev = SVN_INVALID_REVNUM;
+
+ if (op->kind == OP_ADD_DIR || op->kind == OP_ADD_FILE)
+ item->state_flags = SVN_CLIENT_COMMIT_ITEM_ADD;
+ else if (op->kind == OP_DELETE)
+ item->state_flags = SVN_CLIENT_COMMIT_ITEM_DELETE;
+ /* else item->state_flags = 0; */
+
+ if (op->prop_mods && op->prop_mods->nelts)
+ item->state_flags |= SVN_CLIENT_COMMIT_ITEM_PROP_MODS;
+
+ if (op->src_stream)
+ item->state_flags |= SVN_CLIENT_COMMIT_ITEM_TEXT_MODS;
+
+ APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
+ }
+
+ if (op->children && op->children->nelts)
+ {
+ int i;
+ apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+
+ for (i = 0; i < op->children->nelts; i++)
+ {
+ mtcc_op_t *cop;
+ const char * child_url;
+
+ cop = APR_ARRAY_IDX(op->children, i, mtcc_op_t *);
+
+ svn_pool_clear(iterpool);
+
+ child_url = svn_path_url_add_component2(url, cop->name, iterpool);
+
+ SVN_ERR(add_commit_items(cop, session_url, child_url, commit_items,
+ result_pool, iterpool));
+ }
+
+ svn_pool_destroy(iterpool);
+ }
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_client__mtcc_commit(apr_hash_t *revprop_table,
+ svn_commit_callback2_t commit_callback,
+ void *commit_baton,
+ svn_client__mtcc_t *mtcc,
+ apr_pool_t *scratch_pool)
+{
+ const svn_delta_editor_t *editor;
+ void *edit_baton;
+ void *root_baton;
+ apr_hash_t *commit_revprops;
+ svn_node_kind_t kind;
+ svn_error_t *err;
+ const char *session_url;
+ const char *log_msg;
+
+ if (MTCC_UNMODIFIED(mtcc))
+ {
+ /* No changes -> no revision. Easy out */
+ svn_pool_destroy(mtcc->pool);
+ return SVN_NO_ERROR;
+ }
+
+ SVN_ERR(svn_ra_get_session_url(mtcc->ra_session, &session_url, scratch_pool));
+
+ if (mtcc->root_op->kind != OP_OPEN_DIR)
+ {
+ const char *name;
+
+ svn_uri_split(&session_url, &name, session_url, scratch_pool);
+
+ if (*name)
+ {
+ SVN_ERR(mtcc_reparent(session_url, mtcc, scratch_pool));
+
+ SVN_ERR(svn_ra_reparent(mtcc->ra_session, session_url, scratch_pool));
+ }
+ }
+
+ /* Create new commit items and add them to the array. */
+ if (SVN_CLIENT__HAS_LOG_MSG_FUNC(mtcc->ctx))
+ {
+ svn_client_commit_item3_t *item;
+ const char *tmp_file;
+ apr_array_header_t *commit_items
+ = apr_array_make(scratch_pool, 32, sizeof(item));
+
+ SVN_ERR(add_commit_items(mtcc->root_op, session_url, session_url,
+ commit_items, scratch_pool, scratch_pool));
+
+ SVN_ERR(svn_client__get_log_msg(&log_msg, &tmp_file, commit_items,
+ mtcc->ctx, scratch_pool));
+
+ if (! log_msg)
+ return SVN_NO_ERROR;
+ }
+ else
+ log_msg = "";
+
+ SVN_ERR(svn_client__ensure_revprop_table(&commit_revprops, revprop_table,
+ log_msg, mtcc->ctx, scratch_pool));
+
+ /* Ugly corner case: The ra session might have died while we were waiting
+ for the callback */
+
+ err = svn_ra_check_path(mtcc->ra_session, "", mtcc->base_revision, &kind,
+ scratch_pool);
+
+ if (err)
+ {
+ svn_error_t *err2 = svn_client_open_ra_session2(&mtcc->ra_session,
+ session_url,
+ NULL, mtcc->ctx,
+ mtcc->pool,
+ scratch_pool);
+
+ if (err2)
+ {
+ svn_pool_destroy(mtcc->pool);
+ return svn_error_trace(svn_error_compose_create(err, err2));
+ }
+ svn_error_clear(err);
+
+ SVN_ERR(svn_ra_check_path(mtcc->ra_session, "",
+ mtcc->base_revision, &kind, scratch_pool));
+ }
+
+ if (kind != svn_node_dir)
+ return svn_error_createf(SVN_ERR_FS_NOT_DIRECTORY, NULL,
+ _("Can't commit to '%s' because it "
+ "is not a directory"),
+ session_url);
+
+ /* Beware that the editor object must not live longer than the MTCC.
+ Otherwise, txn objects etc. in EDITOR may live longer than their
+ respective FS objects. So, we can't use SCRATCH_POOL here. */
+ SVN_ERR(svn_ra_get_commit_editor3(mtcc->ra_session, &editor, &edit_baton,
+ commit_revprops,
+ commit_callback, commit_baton,
+ NULL /* lock_tokens */,
+ FALSE /* keep_locks */,
+ mtcc->pool));
+
+ err = editor->open_root(edit_baton, mtcc->base_revision, scratch_pool, &root_baton);
+
+ if (!err)
+ err = commit_directory(editor, mtcc->root_op, "", mtcc->base_revision,
+ root_baton, session_url, mtcc->ctx, scratch_pool);
+
+ if (!err)
+ {
+ if (mtcc->ctx->notify_func2)
+ {
+ svn_wc_notify_t *notify;
+ notify = svn_wc_create_notify_url(session_url,
+ svn_wc_notify_commit_finalizing,
+ scratch_pool);
+ mtcc->ctx->notify_func2(mtcc->ctx->notify_baton2, notify,
+ scratch_pool);
+ }
+ SVN_ERR(editor->close_edit(edit_baton, scratch_pool));
+ }
+ else
+ err = svn_error_compose_create(err,
+ editor->abort_edit(edit_baton, scratch_pool));
+
+ svn_pool_destroy(mtcc->pool);
+
+ return svn_error_trace(err);
+}
diff --git a/subversion/libsvn_client/patch.c b/subversion/libsvn_client/patch.c
index b7fbf06145aa..d70e83ccaf48 100644
--- a/subversion/libsvn_client/patch.c
+++ b/subversion/libsvn_client/patch.c
@@ -48,6 +48,7 @@
#include "private/svn_dep_compat.h"
#include "private/svn_string_private.h"
#include "private/svn_subr_private.h"
+#include "private/svn_sorts_private.h"
typedef struct hunk_info_t {
/* The hunk. */
@@ -232,6 +233,10 @@ typedef struct patch_target_t {
* (i.e. a new file was added on top locally deleted node). */
svn_boolean_t replaced;
+ /* Set if the target is supposed to be moved by the patch.
+ * This applies to --git diffs which carry "rename from/to" headers. */
+ const char *move_target_abspath;
+
/* True if the target has the executable bit set. */
svn_boolean_t executable;
@@ -321,7 +326,8 @@ obtain_eol_and_keywords_for_file(apr_hash_t **keywords,
const char *rev_str;
const char *author;
const char *url;
- const char *root_url;
+ const char *repos_root_url;
+ const char *repos_relpath;
SVN_ERR(svn_wc__node_get_changed_info(&changed_rev,
&changed_date,
@@ -330,15 +336,17 @@ obtain_eol_and_keywords_for_file(apr_hash_t **keywords,
scratch_pool,
scratch_pool));
rev_str = apr_psprintf(scratch_pool, "%ld", changed_rev);
- SVN_ERR(svn_wc__node_get_url(&url, wc_ctx,
- local_abspath,
- scratch_pool, scratch_pool));
- SVN_ERR(svn_wc__node_get_repos_info(NULL, NULL, &root_url, NULL,
+ SVN_ERR(svn_wc__node_get_repos_info(NULL, &repos_relpath, &repos_root_url,
+ NULL,
wc_ctx, local_abspath,
scratch_pool, scratch_pool));
+ url = svn_path_url_add_component2(repos_root_url, repos_relpath,
+ scratch_pool);
+
SVN_ERR(svn_subst_build_keywords3(keywords,
keywords_val->data,
- rev_str, url, root_url, changed_date,
+ rev_str, url, repos_root_url,
+ changed_date,
author, result_pool));
}
@@ -942,6 +950,12 @@ choose_target_filename(const svn_patch_t *patch)
if (strcmp(patch->new_filename, "/dev/null") == 0)
return patch->old_filename;
+ /* If the patch renames the target, use the old name while
+ * applying hunks. The target will be renamed to the new name
+ * after hunks have been applied. */
+ if (patch->operation == svn_diff_op_moved)
+ return patch->old_filename;
+
old = svn_path_component_count(patch->old_filename);
new = svn_path_component_count(patch->new_filename);
@@ -991,7 +1005,7 @@ init_patch_target(patch_target_t **patch_target,
hi;
hi = apr_hash_next(hi))
{
- svn_prop_patch_t *prop_patch = svn__apr_hash_index_val(hi);
+ svn_prop_patch_t *prop_patch = apr_hash_this_val(hi);
if (! has_prop_changes)
has_prop_changes = prop_patch->hunks->nelts > 0;
else
@@ -1021,6 +1035,7 @@ init_patch_target(patch_target_t **patch_target,
SVN_ERR(resolve_target_path(target, choose_target_filename(patch),
wcroot_abspath, strip_count, prop_changes_only,
wc_ctx, result_pool, scratch_pool));
+ *patch_target = target;
if (! target->skipped)
{
const char *diff_header;
@@ -1079,6 +1094,64 @@ init_patch_target(patch_target_t **patch_target,
target->added = TRUE;
else if (patch->operation == svn_diff_op_deleted)
target->deleted = TRUE;
+ else if (patch->operation == svn_diff_op_moved)
+ {
+ const char *move_target_path;
+ const char *move_target_relpath;
+ svn_boolean_t under_root;
+ svn_node_kind_t kind_on_disk;
+ svn_node_kind_t wc_kind;
+
+ move_target_path = svn_dirent_internal_style(patch->new_filename,
+ scratch_pool);
+
+ if (strip_count > 0)
+ SVN_ERR(strip_path(&move_target_path, move_target_path,
+ strip_count, scratch_pool, scratch_pool));
+
+ if (svn_dirent_is_absolute(move_target_path))
+ {
+ move_target_relpath = svn_dirent_is_child(wcroot_abspath,
+ move_target_path,
+ scratch_pool);
+ if (! move_target_relpath)
+ {
+ /* The move target path is either outside of the working
+ * copy or it is the working copy itself. Skip it. */
+ target->skipped = TRUE;
+ target->local_abspath = NULL;
+ return SVN_NO_ERROR;
+ }
+ }
+ else
+ move_target_relpath = move_target_path;
+
+ /* Make sure the move target path is secure to use. */
+ SVN_ERR(svn_dirent_is_under_root(&under_root,
+ &target->move_target_abspath,
+ wcroot_abspath,
+ move_target_relpath, result_pool));
+ if (! under_root)
+ {
+ /* The target path is outside of the working copy. Skip it. */
+ target->skipped = TRUE;
+ target->local_abspath = NULL;
+ return SVN_NO_ERROR;
+ }
+
+ SVN_ERR(svn_io_check_path(target->move_target_abspath,
+ &kind_on_disk, scratch_pool));
+ SVN_ERR(svn_wc_read_kind2(&wc_kind, wc_ctx,
+ target->move_target_abspath,
+ FALSE, FALSE, scratch_pool));
+ if (kind_on_disk != svn_node_none || wc_kind != svn_node_none)
+ {
+ /* The move target path already exists on disk. Skip target. */
+ target->skipped = TRUE;
+ target->move_target_abspath = NULL;
+ return SVN_NO_ERROR;
+ }
+ }
if (! target->is_symlink)
{
@@ -1136,8 +1209,8 @@ init_patch_target(patch_target_t **patch_target,
hi;
hi = apr_hash_next(hi))
{
- const char *prop_name = svn__apr_hash_index_key(hi);
- svn_prop_patch_t *prop_patch = svn__apr_hash_index_val(hi);
+ const char *prop_name = apr_hash_this_key(hi);
+ svn_prop_patch_t *prop_patch = apr_hash_this_val(hi);
prop_patch_target_t *prop_target;
SVN_ERR(init_prop_target(&prop_target,
@@ -1150,7 +1223,6 @@ init_patch_target(patch_target_t **patch_target,
}
}
- *patch_target = target;
return SVN_NO_ERROR;
}
@@ -1510,7 +1582,8 @@ match_existing_target(svn_boolean_t *match,
/* Determine the line at which a HUNK applies to CONTENT of the TARGET
* file, and return an appropriate hunk_info object in *HI, allocated from
* RESULT_POOL. Use fuzz factor FUZZ. Set HI->FUZZ to FUZZ. If no correct
- * line can be determined, set HI->REJECTED to TRUE.
+ * line can be determined, set HI->REJECTED to TRUE. PREVIOUS_OFFSET
+ * is the offset at which the previous matching hunk was applied, or zero.
* IGNORE_WHITESPACE tells whether whitespace should be considered when
* matching. IS_PROP_HUNK indicates whether the hunk patches file content
* or a property.
@@ -1522,6 +1595,7 @@ static svn_error_t *
get_hunk_info(hunk_info_t **hi, patch_target_t *target,
target_content_t *content,
svn_diff_hunk_t *hunk, svn_linenum_t fuzz,
+ svn_linenum_t previous_offset,
svn_boolean_t ignore_whitespace,
svn_boolean_t is_prop_hunk,
svn_cancel_func_t cancel_func, void *cancel_baton,
@@ -1531,7 +1605,7 @@ get_hunk_info(hunk_info_t **hi, patch_target_t *target,
svn_linenum_t original_start;
svn_boolean_t already_applied;
- original_start = svn_diff_hunk_get_original_start(hunk);
+ original_start = svn_diff_hunk_get_original_start(hunk) + previous_offset;
already_applied = FALSE;
/* An original offset of zero means that this hunk wants to create
@@ -1639,7 +1713,9 @@ get_hunk_info(hunk_info_t **hi, patch_target_t *target,
modified_start = svn_diff_hunk_get_modified_start(hunk);
if (modified_start == 0)
{
- /* Patch wants to delete the file. */
+ /* Patch wants to delete the file.
+
+ ### locally_deleted is always false here? */
already_applied = target->locally_deleted;
}
else
@@ -1660,27 +1736,85 @@ get_hunk_info(hunk_info_t **hi, patch_target_t *target,
if (! already_applied)
{
- /* Scan the whole file again from the start. */
- SVN_ERR(seek_to_line(content, 1, scratch_pool));
+ int i;
+ svn_linenum_t search_start = 1, search_end = 0;
+ svn_linenum_t matched_line2;
+
+ /* Search for closest match before or after original
+ start. We have no backward search so search forwards
+ from the previous match (or start of file) to the
+ original start looking for the last match. Then
+ search forwards from the original start looking for a
+ better match. Finally search forwards from the start
+ of file to the previous hunk if that could result in
+ a better match. */
+
+ for (i = content->hunks->nelts; i > 0; --i)
+ {
+ const hunk_info_t *prev
+ = APR_ARRAY_IDX(content->hunks, i - 1, const hunk_info_t *);
+ if (!prev->rejected)
+ {
+ svn_linenum_t length;
+
+ length = svn_diff_hunk_get_original_length(prev->hunk);
+ search_start = prev->matched_line + length;
+ break;
+ }
+ }
- /* Scan forward towards the hunk's line and look for a line
- * where the hunk matches. */
+ /* Search from the previous match, or start of file,
+ towards the original location. */
+ SVN_ERR(seek_to_line(content, search_start, scratch_pool));
SVN_ERR(scan_for_match(&matched_line, content, hunk, FALSE,
original_start, fuzz,
ignore_whitespace, FALSE,
cancel_func, cancel_baton,
scratch_pool));
- /* In tie-break situations, we arbitrarily prefer early matches
- * to save us from scanning the rest of the file. */
- if (matched_line == 0)
+ /* If a match we only need to search forwards for a
+ better match, otherwise to the end of the file. */
+ if (matched_line)
+ search_end = original_start + (original_start - matched_line);
+
+ /* Search from original location, towards the end. */
+ SVN_ERR(seek_to_line(content, original_start + 1, scratch_pool));
+ SVN_ERR(scan_for_match(&matched_line2, content, hunk,
+ TRUE, search_end, fuzz, ignore_whitespace,
+ FALSE, cancel_func, cancel_baton,
+ scratch_pool));
+
+ /* Chose the forward match if it is closer than the
+ backward match or if there is no backward match. */
+ if (matched_line2
+ && (!matched_line
+ || (matched_line2 - original_start
+ < original_start - matched_line)))
+ matched_line = matched_line2;
+
+ /* Search from before previous hunk if there could be a
+ better match. */
+ if (search_start > 1
+ && (!matched_line
+ || (matched_line > original_start
+ && (matched_line - original_start
+ > original_start - search_start))))
{
- /* Scan forward towards the end of the file and look
- * for a line where the hunk matches. */
- SVN_ERR(scan_for_match(&matched_line, content, hunk,
- TRUE, 0, fuzz, ignore_whitespace,
- FALSE, cancel_func, cancel_baton,
+ svn_linenum_t search_start2 = 1;
+
+ if (matched_line
+ && matched_line - original_start < original_start)
+ search_start2
+ = original_start - (matched_line - original_start) + 1;
+
+ SVN_ERR(seek_to_line(content, search_start2, scratch_pool));
+ SVN_ERR(scan_for_match(&matched_line2, content, hunk, FALSE,
+ search_start - 1, fuzz,
+ ignore_whitespace, FALSE,
+ cancel_func, cancel_baton,
scratch_pool));
+ if (matched_line2)
+ matched_line = matched_line2;
}
}
}
@@ -1725,7 +1859,7 @@ copy_lines_to_target(target_content_t *content, svn_linenum_t line,
SVN_ERR(readline(content, &target_line, iterpool, iterpool));
if (! content->eof)
target_line = apr_pstrcat(iterpool, target_line, content->eol_str,
- (char *)NULL);
+ SVN_VA_NULL);
len = strlen(target_line);
SVN_ERR(content->write(content->write_baton, target_line,
len, iterpool));
@@ -1949,7 +2083,7 @@ send_hunk_notification(const hunk_info_t *hi,
notify->hunk_fuzz = hi->fuzz;
notify->prop_name = prop_name;
- (*ctx->notify_func2)(ctx->notify_baton2, notify, pool);
+ ctx->notify_func2(ctx->notify_baton2, notify, pool);
return SVN_NO_ERROR;
}
@@ -1963,6 +2097,7 @@ send_patch_notification(const patch_target_t *target,
{
svn_wc_notify_t *notify;
svn_wc_notify_action_t action;
+ const char *notify_path;
if (! ctx->notify_func2)
return SVN_NO_ERROR;
@@ -1971,14 +2106,18 @@ send_patch_notification(const patch_target_t *target,
action = svn_wc_notify_skip;
else if (target->deleted)
action = svn_wc_notify_delete;
- else if (target->added || target->replaced)
+ else if (target->added || target->replaced || target->move_target_abspath)
action = svn_wc_notify_add;
else
action = svn_wc_notify_patch;
- notify = svn_wc_create_notify(target->local_abspath ? target->local_abspath
- : target->local_relpath,
- action, pool);
+ if (target->move_target_abspath)
+ notify_path = target->move_target_abspath;
+ else
+ notify_path = target->local_abspath ? target->local_abspath
+ : target->local_relpath;
+
+ notify = svn_wc_create_notify(notify_path, action, pool);
notify->kind = svn_node_file;
if (action == svn_wc_notify_skip)
@@ -2006,7 +2145,7 @@ send_patch_notification(const patch_target_t *target,
notify->prop_state = svn_wc_notify_state_changed;
}
- (*ctx->notify_func2)(ctx->notify_baton2, notify, pool);
+ ctx->notify_func2(ctx->notify_baton2, notify, pool);
if (action == svn_wc_notify_patch)
{
@@ -2033,7 +2172,7 @@ send_patch_notification(const patch_target_t *target,
{
prop_patch_target_t *prop_target;
- prop_target = svn__apr_hash_index_val(hash_index);
+ prop_target = apr_hash_this_val(hash_index);
for (i = 0; i < prop_target->content->hunks->nelts; i++)
{
@@ -2054,15 +2193,16 @@ send_patch_notification(const patch_target_t *target,
svn_pool_destroy(iterpool);
}
- return SVN_NO_ERROR;
-}
+ if (target->move_target_abspath)
+ {
+ /* Notify about deletion of move source. */
+ notify = svn_wc_create_notify(target->local_abspath,
+ svn_wc_notify_delete, pool);
+ notify->kind = svn_node_file;
+ ctx->notify_func2(ctx->notify_baton2, notify, pool);
+ }
-static void
-svn_sort__array(apr_array_header_t *array,
- int (*comparison_func)(const void *,
- const void *))
-{
- qsort(array->elts, array->nelts, array->elt_size, comparison_func);
+ return SVN_NO_ERROR;
}
/* Implements the callback for svn_sort__array. Puts hunks that match
@@ -2135,6 +2275,7 @@ apply_one_patch(patch_target_t **patch_target, svn_patch_t *patch,
int i;
static const svn_linenum_t MAX_FUZZ = 2;
apr_hash_index_t *hash_index;
+ svn_linenum_t previous_offset = 0;
SVN_ERR(init_patch_target(&target, patch, abs_wc_path, wc_ctx, strip_count,
remove_tempfiles, result_pool, scratch_pool));
@@ -2177,6 +2318,7 @@ apply_one_patch(patch_target_t **patch_target, svn_patch_t *patch,
do
{
SVN_ERR(get_hunk_info(&hi, target, target->content, hunk, fuzz,
+ previous_offset,
ignore_whitespace,
FALSE /* is_prop_hunk */,
cancel_func, cancel_baton,
@@ -2185,6 +2327,10 @@ apply_one_patch(patch_target_t **patch_target, svn_patch_t *patch,
}
while (hi->rejected && fuzz <= MAX_FUZZ && ! hi->already_applied);
+ if (hi->matched_line)
+ previous_offset
+ = hi->matched_line - svn_diff_hunk_get_original_start(hunk);
+
APR_ARRAY_PUSH(target->content->hunks, hunk_info_t *) = hi;
}
@@ -2236,8 +2382,8 @@ apply_one_patch(patch_target_t **patch_target, svn_patch_t *patch,
const char *prop_name;
prop_patch_target_t *prop_target;
- prop_name = svn__apr_hash_index_key(hash_index);
- prop_patch = svn__apr_hash_index_val(hash_index);
+ prop_name = apr_hash_this_key(hash_index);
+ prop_patch = apr_hash_this_val(hash_index);
if (! strcmp(prop_name, SVN_PROP_SPECIAL))
target->is_special = TRUE;
@@ -2263,7 +2409,7 @@ apply_one_patch(patch_target_t **patch_target, svn_patch_t *patch,
do
{
SVN_ERR(get_hunk_info(&hi, target, prop_target->content,
- hunk, fuzz,
+ hunk, fuzz, 0,
ignore_whitespace,
TRUE /* is_prop_hunk */,
cancel_func, cancel_baton,
@@ -2283,7 +2429,7 @@ apply_one_patch(patch_target_t **patch_target, svn_patch_t *patch,
{
prop_patch_target_t *prop_target;
- prop_target = svn__apr_hash_index_val(hash_index);
+ prop_target = apr_hash_this_val(hash_index);
for (i = 0; i < prop_target->content->hunks->nelts; i++)
{
@@ -2508,8 +2654,9 @@ create_missing_parents(patch_target_t *target,
if (ctx->cancel_func)
SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
- SVN_ERR(svn_wc_add_from_disk2(ctx->wc_ctx, local_abspath,
+ SVN_ERR(svn_wc_add_from_disk3(ctx->wc_ctx, local_abspath,
NULL /*props*/,
+ FALSE /* skip checks */,
ctx->notify_func2, ctx->notify_baton2,
iterpool));
}
@@ -2625,7 +2772,10 @@ install_patched_target(patch_target_t *target, const char *abs_wc_path,
svn_subst_eol_style_native);
SVN_ERR(svn_subst_copy_and_translate4(
- target->patched_path, target->local_abspath,
+ target->patched_path,
+ target->move_target_abspath
+ ? target->move_target_abspath
+ : target->local_abspath,
target->content->eol_str, repair_eol,
target->content->keywords,
TRUE /* expand */, FALSE /* special */,
@@ -2639,15 +2789,39 @@ install_patched_target(patch_target_t *target, const char *abs_wc_path,
* Suppress notification, we'll do that later (and also
* during dry-run). Don't allow cancellation because
* we'd rather notify about what we did before aborting. */
- SVN_ERR(svn_wc_add_from_disk2(ctx->wc_ctx, target->local_abspath,
+ SVN_ERR(svn_wc_add_from_disk3(ctx->wc_ctx, target->local_abspath,
NULL /*props*/,
+ FALSE /* skip checks */,
NULL, NULL, pool));
}
/* Restore the target's executable bit if necessary. */
- SVN_ERR(svn_io_set_file_executable(target->local_abspath,
+ SVN_ERR(svn_io_set_file_executable(target->move_target_abspath
+ ? target->move_target_abspath
+ : target->local_abspath,
target->executable,
FALSE, pool));
+
+ if (target->move_target_abspath)
+ {
+ /* ### Copying the patched content to the move target location,
+ * performing the move in meta-data, and removing the file at
+ * the move source should be one atomic operation. */
+
+ /* ### Create missing parents. */
+
+ /* Perform the move in meta-data. */
+ SVN_ERR(svn_wc__move2(ctx->wc_ctx,
+ target->local_abspath,
+ target->move_target_abspath,
+ TRUE, /* metadata_only */
+ FALSE, /* allow_mixed_revisions */
+ NULL, NULL, NULL, NULL,
+ pool));
+
+ /* Delete the patch target's old location from disk. */
+ SVN_ERR(svn_io_remove_file2(target->local_abspath, FALSE, pool));
+ }
}
}
@@ -2694,7 +2868,7 @@ install_patched_prop_targets(patch_target_t *target,
hi;
hi = apr_hash_next(hi))
{
- prop_patch_target_t *prop_target = svn__apr_hash_index_val(hi);
+ prop_patch_target_t *prop_target = apr_hash_this_val(hi);
const svn_string_t *prop_val;
svn_error_t *err;
@@ -2730,10 +2904,11 @@ install_patched_prop_targets(patch_target_t *target,
{
if (! dry_run)
{
- SVN_ERR(svn_io_file_create(target->local_abspath, "",
- scratch_pool));
- SVN_ERR(svn_wc_add_from_disk2(ctx->wc_ctx, target->local_abspath,
+ SVN_ERR(svn_io_file_create_empty(target->local_abspath,
+ scratch_pool));
+ SVN_ERR(svn_wc_add_from_disk3(ctx->wc_ctx, target->local_abspath,
NULL /*props*/,
+ FALSE /* skip checks */,
/* suppress notification */
NULL, NULL,
iterpool));
@@ -2869,10 +3044,13 @@ check_ancestor_delete(const char *deleted_target,
{
struct can_delete_baton_t cb;
svn_error_t *err;
+ apr_array_header_t *ignores;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
const char *dir_abspath = svn_dirent_dirname(deleted_target, scratch_pool);
+ SVN_ERR(svn_wc_get_default_ignores(&ignores, ctx->config, scratch_pool));
+
while (svn_dirent_is_child(apply_root, dir_abspath, iterpool))
{
svn_pool_clear(iterpool);
@@ -2882,7 +3060,7 @@ check_ancestor_delete(const char *deleted_target,
cb.targets_info = targets_info;
err = svn_wc_walk_status(ctx->wc_ctx, dir_abspath, svn_depth_infinity,
- TRUE, FALSE, FALSE, NULL,
+ TRUE, FALSE, FALSE, ignores,
can_delete_callback, &cb,
ctx->cancel_func, ctx->cancel_baton,
iterpool);
@@ -3009,6 +3187,7 @@ apply_patches(/* The path to the patch file. */
if (target->has_text_changes
|| target->added
+ || target->move_target_abspath
|| target->deleted)
SVN_ERR(install_patched_target(target, abs_wc_path,
ctx, dry_run, iterpool));
diff --git a/subversion/libsvn_client/prop_commands.c b/subversion/libsvn_client/prop_commands.c
index 06c4d21dc1e0..15629666c91b 100644
--- a/subversion/libsvn_client/prop_commands.c
+++ b/subversion/libsvn_client/prop_commands.c
@@ -206,6 +206,7 @@ propset_on_url(const char *propname,
item = svn_client_commit_item3_create(pool);
item->url = target;
+ item->kind = node_kind;
item->state_flags = SVN_CLIENT_COMMIT_ITEM_PROP_MODS;
APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *) = item;
SVN_ERR(svn_client__get_log_msg(&message, &tmp_file, commit_items,
@@ -240,6 +241,14 @@ propset_on_url(const char *propname,
return svn_error_trace(err);
}
+ if (ctx->notify_func2)
+ {
+ svn_wc_notify_t *notify;
+ notify = svn_wc_create_notify_url(target,
+ svn_wc_notify_commit_finalizing,
+ pool);
+ ctx->notify_func2(ctx->notify_baton2, notify, pool);
+ }
/* Close the edit. */
return editor->close_edit(edit_baton, pool);
}
@@ -485,11 +494,11 @@ svn_client_revprop_set2(const char *propname,
const svn_string_t *unset = NULL;
if (original_propval == NULL)
- old_value_p = NULL;
+ old_value_p = NULL;
else if (original_propval->data == NULL)
- old_value_p = &unset;
+ old_value_p = &unset;
else
- old_value_p = &original_propval;
+ old_value_p = &original_propval;
/* The actual RA call. */
SVN_ERR(svn_ra_change_rev_prop2(ra_session, *set_rev, propname,
@@ -512,45 +521,24 @@ svn_client_revprop_set2(const char *propname,
notify->prop_name = propname;
notify->revision = *set_rev;
- (*ctx->notify_func2)(ctx->notify_baton2, notify, pool);
+ ctx->notify_func2(ctx->notify_baton2, notify, pool);
}
return SVN_NO_ERROR;
}
-/* Helper for the remote case of svn_client_propget.
- *
- * If PROPS is not null, then get the value of property PROPNAME in REVNUM,
- using RA_LIB and SESSION. Store the value ('svn_string_t *') in PROPS,
- under the path key "TARGET_PREFIX/TARGET_RELATIVE" ('const char *').
- *
- * If INHERITED_PROPS is not null, then set *INHERITED_PROPS to a
- * depth-first ordered array of svn_prop_inherited_item_t * structures
- * representing the PROPNAME properties inherited by the target. If
- * INHERITABLE_PROPS in not null and no inheritable properties are found,
- * then set *INHERITED_PROPS to an empty array.
- *
- * Recurse according to DEPTH, similarly to svn_client_propget3().
- *
- * KIND is the kind of the node at "TARGET_PREFIX/TARGET_RELATIVE".
- * Yes, caller passes this; it makes the recursion more efficient :-).
- *
- * Allocate PROPS and *INHERITED_PROPS in RESULT_POOL, but do all temporary
- * work in SCRATCH_POOL. The two pools can be the same; recursive
- * calls may use a different SCRATCH_POOL, however.
- */
-static svn_error_t *
-remote_propget(apr_hash_t *props,
- apr_array_header_t **inherited_props,
- const char *propname,
- const char *target_prefix,
- const char *target_relative,
- svn_node_kind_t kind,
- svn_revnum_t revnum,
- svn_ra_session_t *ra_session,
- svn_depth_t depth,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
+svn_error_t *
+svn_client__remote_propget(apr_hash_t *props,
+ apr_array_header_t **inherited_props,
+ const char *propname,
+ const char *target_prefix,
+ const char *target_relative,
+ svn_node_kind_t kind,
+ svn_revnum_t revnum,
+ svn_ra_session_t *ra_session,
+ svn_depth_t depth,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
apr_hash_t *dirents;
apr_hash_t *prop_hash = NULL;
@@ -587,6 +575,9 @@ remote_propget(apr_hash_t *props,
if (inherited_props)
{
const char *repos_root_url;
+ int i;
+ apr_array_header_t *final_iprops =
+ apr_array_make(result_pool, 1, sizeof(svn_prop_inherited_item_t *));
/* We will filter out all but PROPNAME later, making a final copy
in RESULT_POOL, so pass SCRATCH_POOL for all pools. */
@@ -599,15 +590,8 @@ remote_propget(apr_hash_t *props,
repos_root_url,
scratch_pool,
scratch_pool));
- }
-
- /* Make a copy of any inherited PROPNAME properties in RESULT_POOL. */
- if (inherited_props)
- {
- int i;
- apr_array_header_t *final_iprops =
- apr_array_make(result_pool, 1, sizeof(svn_prop_inherited_item_t *));
+ /* Make a copy of any inherited PROPNAME properties in RESULT_POOL. */
for (i = 0; i < (*inherited_props)->nelts; i++)
{
svn_prop_inherited_item_t *iprop =
@@ -650,8 +634,8 @@ remote_propget(apr_hash_t *props,
hi;
hi = apr_hash_next(hi))
{
- const char *this_name = svn__apr_hash_index_key(hi);
- svn_dirent_t *this_ent = svn__apr_hash_index_val(hi);
+ const char *this_name = apr_hash_this_key(hi);
+ svn_dirent_t *this_ent = apr_hash_this_val(hi);
const char *new_target_relative;
svn_depth_t depth_below_here = depth;
@@ -666,15 +650,15 @@ remote_propget(apr_hash_t *props,
new_target_relative = svn_relpath_join(target_relative, this_name,
iterpool);
- SVN_ERR(remote_propget(props, NULL,
- propname,
- target_prefix,
- new_target_relative,
- this_ent->kind,
- revnum,
- ra_session,
- depth_below_here,
- result_pool, iterpool));
+ SVN_ERR(svn_client__remote_propget(props, NULL,
+ propname,
+ target_prefix,
+ new_target_relative,
+ this_ent->kind,
+ revnum,
+ ra_session,
+ depth_below_here,
+ result_pool, iterpool));
}
svn_pool_destroy(iterpool);
@@ -704,7 +688,7 @@ recursive_propget_receiver(void *baton,
{
apr_hash_index_t *hi = apr_hash_first(scratch_pool, props);
svn_hash_sets(b->props, apr_pstrdup(b->pool, local_abspath),
- svn_string_dup(svn__apr_hash_index_val(hi), b->pool));
+ svn_string_dup(apr_hash_this_val(hi), b->pool));
}
return SVN_NO_ERROR;
@@ -882,13 +866,9 @@ svn_client_propget5(apr_hash_t **props,
if (SVN_CLIENT__REVKIND_NEEDS_WC(peg_revision->kind)
|| SVN_CLIENT__REVKIND_NEEDS_WC(revision->kind))
{
- svn_revnum_t origin_rev;
const char *repos_relpath;
const char *repos_root_url;
- const char *repos_uuid;
const char *local_abspath;
- const char *copy_root_abspath;
- svn_boolean_t is_copy;
/* Avoid assertion on the next line when somebody accidentally asks for
a working copy revision on a URL */
@@ -901,12 +881,10 @@ svn_client_propget5(apr_hash_t **props,
if (SVN_CLIENT__REVKIND_NEEDS_WC(peg_revision->kind))
{
- SVN_ERR(svn_wc__node_get_origin(&is_copy,
- &origin_rev,
+ SVN_ERR(svn_wc__node_get_origin(NULL, NULL,
&repos_relpath,
&repos_root_url,
- &repos_uuid,
- &copy_root_abspath,
+ NULL, NULL, NULL,
ctx->wc_ctx,
local_abspath,
FALSE, /* scan_deleted */
@@ -970,7 +948,8 @@ svn_client_propget5(apr_hash_t **props,
if (!local_explicit_props)
*props = apr_hash_make(result_pool);
- SVN_ERR(remote_propget(!local_explicit_props ? *props : NULL,
+ SVN_ERR(svn_client__remote_propget(
+ !local_explicit_props ? *props : NULL,
!local_iprops ? inherited_props : NULL,
propname, loc->url, "",
kind, loc->rev, ra_session,
@@ -1134,8 +1113,8 @@ remote_proplist(const char *target_prefix,
hi;
hi = apr_hash_next(hi))
{
- const char *name = svn__apr_hash_index_key(hi);
- apr_ssize_t klen = svn__apr_hash_index_klen(hi);
+ const char *name = apr_hash_this_key(hi);
+ apr_ssize_t klen = apr_hash_this_key_len(hi);
svn_prop_kind_t prop_kind;
prop_kind = svn_property_kind2(name);
@@ -1160,8 +1139,8 @@ remote_proplist(const char *target_prefix,
hi;
hi = apr_hash_next(hi))
{
- const char *this_name = svn__apr_hash_index_key(hi);
- svn_dirent_t *this_ent = svn__apr_hash_index_val(hi);
+ const char *this_name = apr_hash_this_key(hi);
+ svn_dirent_t *this_ent = apr_hash_this_val(hi);
const char *new_target_relative;
if (cancel_func)
@@ -1291,12 +1270,9 @@ get_remote_props(const char *path_or_url,
if (SVN_CLIENT__REVKIND_NEEDS_WC(peg_revision->kind)
|| SVN_CLIENT__REVKIND_NEEDS_WC(revision->kind))
{
- svn_revnum_t origin_rev;
const char *repos_relpath;
const char *repos_root_url;
- const char *repos_uuid;
const char *local_abspath;
- const char *copy_root_abspath;
svn_boolean_t is_copy;
/* Avoid assertion on the next line when somebody accidentally asks for
@@ -1311,11 +1287,10 @@ get_remote_props(const char *path_or_url,
if (SVN_CLIENT__REVKIND_NEEDS_WC(peg_revision->kind))
{
SVN_ERR(svn_wc__node_get_origin(&is_copy,
- &origin_rev,
+ NULL,
&repos_relpath,
&repos_root_url,
- &repos_uuid,
- &copy_root_abspath,
+ NULL, NULL, NULL,
ctx->wc_ctx,
local_abspath,
FALSE, /* scan_deleted */
diff --git a/subversion/libsvn_client/ra.c b/subversion/libsvn_client/ra.c
index a0d4ceab5f40..f98b4b0708ee 100644
--- a/subversion/libsvn_client/ra.c
+++ b/subversion/libsvn_client/ra.c
@@ -42,6 +42,7 @@
#include "svn_private_config.h"
#include "private/svn_wc_private.h"
#include "private/svn_client_private.h"
+#include "private/svn_sorts_private.h"
/* This is the baton that we pass svn_ra_open3(), and is associated with
@@ -70,6 +71,8 @@ typedef struct callback_baton_t
/* A client context. */
svn_client_ctx_t *ctx;
+ /* Last progress reported by progress callback. */
+ apr_off_t last_progress;
} callback_baton_t;
@@ -287,6 +290,31 @@ get_client_string(void *baton,
return SVN_NO_ERROR;
}
+/* Implements svn_ra_progress_notify_func_t. Accumulates progress information
+ * for different RA sessions and reports total progress to caller. */
+static void
+progress_func(apr_off_t progress,
+ apr_off_t total,
+ void *baton,
+ apr_pool_t *pool)
+{
+ callback_baton_t *b = baton;
+ svn_client_ctx_t *public_ctx = b->ctx;
+ svn_client__private_ctx_t *private_ctx =
+ svn_client__get_private_ctx(public_ctx);
+
+ private_ctx->total_progress += (progress - b->last_progress);
+ b->last_progress = progress;
+
+ if (public_ctx->progress_func)
+ {
+ /* All RA implementations currently provide -1 for total. So it doesn't
+ make sense to develop some complex logic to combine total across all
+ RA sessions. */
+ public_ctx->progress_func(private_ctx->total_progress, -1,
+ public_ctx->progress_baton, pool);
+ }
+}
#define SVN_CLIENT__MAX_REDIRECT_ATTEMPTS 3 /* ### TODO: Make configurable. */
@@ -320,12 +348,15 @@ svn_client__open_ra_session_internal(svn_ra_session_t **ra_session,
cbtable->invalidate_wc_props = (write_dav_props && read_dav_props)
? invalidate_wc_props : NULL;
cbtable->auth_baton = ctx->auth_baton; /* new-style */
- cbtable->progress_func = ctx->progress_func;
- cbtable->progress_baton = ctx->progress_baton;
+ cbtable->progress_func = progress_func;
+ cbtable->progress_baton = cb;
cbtable->cancel_func = ctx->cancel_func ? cancel_callback : NULL;
cbtable->get_client_string = get_client_string;
if (base_dir_abspath)
cbtable->get_wc_contents = get_wc_contents;
+ cbtable->check_tunnel_func = ctx->check_tunnel_func;
+ cbtable->open_tunnel_func = ctx->open_tunnel_func;
+ cbtable->tunnel_baton = ctx->tunnel_baton;
cb->commit_items = commit_items;
cb->ctx = ctx;
@@ -403,7 +434,7 @@ svn_client__open_ra_session_internal(svn_ra_session_t **ra_session,
svn_wc_create_notify_url(corrected,
svn_wc_notify_url_redirect,
scratch_pool);
- (*ctx->notify_func2)(ctx->notify_baton2, notify, scratch_pool);
+ ctx->notify_func2(ctx->notify_baton2, notify, scratch_pool);
}
/* Our caller will want to know what our final corrected URL was. */
@@ -614,8 +645,7 @@ svn_client__repos_location_segments(apr_array_header_t **segments,
pool);
SVN_ERR(svn_error_compose_create(
err, svn_ra_reparent(ra_session, old_session_url, pool)));
- qsort((*segments)->elts, (*segments)->nelts,
- (*segments)->elt_size, compare_segments);
+ svn_sort__array(*segments, compare_segments);
return SVN_NO_ERROR;
}
@@ -627,6 +657,9 @@ svn_client__repos_location_segments(apr_array_header_t **segments,
* END_REVNUM must be valid revision numbers except that END_REVNUM may
* be SVN_INVALID_REVNUM if END_URL is NULL.
*
+ * YOUNGEST_REV is the already retrieved youngest revision of the ra session,
+ * but can be SVN_INVALID_REVNUM if the value is not already retrieved.
+ *
* RA_SESSION is an open RA session parented at URL.
*/
static svn_error_t *
@@ -637,6 +670,7 @@ repos_locations(const char **start_url,
svn_revnum_t peg_revnum,
svn_revnum_t start_revnum,
svn_revnum_t end_revnum,
+ svn_revnum_t youngest_rev,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
@@ -644,9 +678,9 @@ repos_locations(const char **start_url,
apr_array_header_t *revs;
apr_hash_t *rev_locs;
- SVN_ERR_ASSERT(peg_revnum != SVN_INVALID_REVNUM);
- SVN_ERR_ASSERT(start_revnum != SVN_INVALID_REVNUM);
- SVN_ERR_ASSERT(end_revnum != SVN_INVALID_REVNUM || end_url == NULL);
+ SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(peg_revnum));
+ SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(start_revnum));
+ SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(end_revnum) || end_url == NULL);
/* Avoid a network request in the common easy case. */
if (start_revnum == peg_revnum
@@ -661,6 +695,27 @@ repos_locations(const char **start_url,
SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_url, scratch_pool));
+ /* Handle another common case: The repository root can't move */
+ if (! strcmp(repos_url, url))
+ {
+ if (! SVN_IS_VALID_REVNUM(youngest_rev))
+ SVN_ERR(svn_ra_get_latest_revnum(ra_session, &youngest_rev,
+ scratch_pool));
+
+ if (start_revnum > youngest_rev
+ || (SVN_IS_VALID_REVNUM(end_revnum) && (end_revnum > youngest_rev)))
+ return svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, NULL,
+ _("No such revision %ld"),
+ (start_revnum > youngest_rev)
+ ? start_revnum : end_revnum);
+
+ if (start_url)
+ *start_url = apr_pstrdup(result_pool, repos_url);
+ if (end_url)
+ *end_url = apr_pstrdup(result_pool, repos_url);
+ return SVN_NO_ERROR;
+ }
+
revs = apr_array_make(scratch_pool, 2, sizeof(svn_revnum_t));
APR_ARRAY_PUSH(revs, svn_revnum_t) = start_revnum;
if (end_revnum != start_revnum && end_revnum != SVN_INVALID_REVNUM)
@@ -716,7 +771,7 @@ svn_client__repos_location(svn_client__pathrev_t **op_loc_p,
peg_loc->url, scratch_pool));
err = repos_locations(&op_url, NULL, ra_session,
peg_loc->url, peg_loc->rev,
- op_revnum, SVN_INVALID_REVNUM,
+ op_revnum, SVN_INVALID_REVNUM, SVN_INVALID_REVNUM,
result_pool, scratch_pool);
SVN_ERR(svn_error_compose_create(
err, svn_ra_reparent(ra_session, old_session_url, scratch_pool)));
@@ -776,7 +831,7 @@ svn_client__repos_locations(const char **start_url,
svn_boolean_t is_copy;
SVN_ERR(svn_wc__node_get_origin(&is_copy, &peg_revnum, &repos_relpath,
- &repos_root_url, NULL, NULL,
+ &repos_root_url, NULL, NULL, NULL,
ctx->wc_ctx, local_abspath_or_url,
FALSE, subpool, subpool));
@@ -856,7 +911,7 @@ svn_client__repos_locations(const char **start_url,
SVN_ERR(repos_locations(start_url, end_url,
ra_session, url, peg_revnum,
- start_revnum, end_revnum,
+ start_revnum, end_revnum, youngest_rev,
pool, subpool));
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
@@ -888,9 +943,9 @@ svn_client__calc_youngest_common_ancestor(svn_client__pathrev_t **ancestor_p,
remembering the youngest matching location. */
for (hi = apr_hash_first(scratch_pool, history1); hi; hi = apr_hash_next(hi))
{
- const char *path = svn__apr_hash_index_key(hi);
- apr_ssize_t path_len = svn__apr_hash_index_klen(hi);
- svn_rangelist_t *ranges1 = svn__apr_hash_index_val(hi);
+ const char *path = apr_hash_this_key(hi);
+ apr_ssize_t path_len = apr_hash_this_key_len(hi);
+ svn_rangelist_t *ranges1 = apr_hash_this_val(hi);
svn_rangelist_t *ranges2, *common;
ranges2 = apr_hash_get(history2, path, path_len);
@@ -992,48 +1047,6 @@ svn_client__get_youngest_common_ancestor(svn_client__pathrev_t **ancestor_p,
return SVN_NO_ERROR;
}
-svn_error_t *
-svn_client__youngest_common_ancestor(const char **ancestor_url,
- svn_revnum_t *ancestor_rev,
- const char *path_or_url1,
- const svn_opt_revision_t *revision1,
- const char *path_or_url2,
- const svn_opt_revision_t *revision2,
- svn_client_ctx_t *ctx,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
-{
- apr_pool_t *sesspool = svn_pool_create(scratch_pool);
- svn_ra_session_t *session;
- svn_client__pathrev_t *loc1, *loc2, *ancestor;
-
- /* Resolve the two locations */
- SVN_ERR(svn_client__ra_session_from_path2(&session, &loc1,
- path_or_url1, NULL,
- revision1, revision1,
- ctx, sesspool));
- SVN_ERR(svn_client__resolve_rev_and_url(&loc2, session,
- path_or_url2, revision2, revision2,
- ctx, scratch_pool));
-
- SVN_ERR(svn_client__get_youngest_common_ancestor(
- &ancestor, loc1, loc2, session, ctx, result_pool, scratch_pool));
-
- if (ancestor)
- {
- *ancestor_url = ancestor->url;
- *ancestor_rev = ancestor->rev;
- }
- else
- {
- *ancestor_url = NULL;
- *ancestor_rev = SVN_INVALID_REVNUM;
- }
- svn_pool_destroy(sesspool);
- return SVN_NO_ERROR;
-}
-
-
struct ra_ev2_baton {
/* The working copy context, from the client context. */
svn_wc_context_t *wc_ctx;
@@ -1080,6 +1093,7 @@ svn_client__ra_provide_base(svn_stream_t **contents,
/* The pristine contents refer to the BASE, or to the pristine of
a copy/move to this location. Fetch the correct revision. */
SVN_ERR(svn_wc__node_get_origin(NULL, revision, NULL, NULL, NULL, NULL,
+ NULL,
reb->wc_ctx, local_abspath, FALSE,
scratch_pool, scratch_pool));
}
@@ -1124,6 +1138,7 @@ svn_client__ra_provide_props(apr_hash_t **props,
/* The pristine props refer to the BASE, or to the pristine props of
a copy/move to this location. Fetch the correct revision. */
SVN_ERR(svn_wc__node_get_origin(NULL, revision, NULL, NULL, NULL, NULL,
+ NULL,
reb->wc_ctx, local_abspath, FALSE,
scratch_pool, scratch_pool));
}
diff --git a/subversion/libsvn_client/relocate.c b/subversion/libsvn_client/relocate.c
index ed8d09cc039b..dcd9017c75ca 100644
--- a/subversion/libsvn_client/relocate.c
+++ b/subversion/libsvn_client/relocate.c
@@ -127,85 +127,6 @@ validator_func(void *baton,
return SVN_NO_ERROR;
}
-
-/* Examing the array of svn_wc_external_item2_t's EXT_DESC (parsed
- from the svn:externals property set on LOCAL_ABSPATH) and determine
- if the external working copies described by such should be
- relocated as a side-effect of the relocation of their parent
- working copy (from OLD_PARENT_REPOS_ROOT_URL to
- NEW_PARENT_REPOS_ROOT_URL). If so, attempt said relocation. */
-static svn_error_t *
-relocate_externals(const char *local_abspath,
- apr_array_header_t *ext_desc,
- const char *old_parent_repos_root_url,
- const char *new_parent_repos_root_url,
- svn_client_ctx_t *ctx,
- apr_pool_t *scratch_pool)
-{
- apr_pool_t *iterpool;
- int i;
-
- /* Parse an externals definition into an array of external items. */
-
- iterpool = svn_pool_create(scratch_pool);
-
- for (i = 0; i < ext_desc->nelts; i++)
- {
- svn_wc_external_item2_t *ext_item =
- APR_ARRAY_IDX(ext_desc, i, svn_wc_external_item2_t *);
- const char *target_repos_root_url;
- const char *target_abspath;
- svn_error_t *err;
-
- svn_pool_clear(iterpool);
-
- /* If this external isn't pulled in via a relative URL, ignore
- it. There's no sense in relocating a working copy only to
- have the next 'svn update' try to point it back to another
- location. */
- if (! ((strncmp("../", ext_item->url, 3) == 0) ||
- (strncmp("^/", ext_item->url, 2) == 0)))
- continue;
-
- /* If the external working copy's not-yet-relocated repos root
- URL matches the primary working copy's pre-relocated
- repository root URL, try to relocate that external, too.
- You might wonder why this check is needed, given that we're
- already limiting ourselves to externals pulled via URLs
- relative to their primary working copy. Well, it's because
- you can use "../" to "crawl up" above one repository's URL
- space and down into another one. */
- SVN_ERR(svn_dirent_get_absolute(&target_abspath,
- svn_dirent_join(local_abspath,
- ext_item->target_dir,
- iterpool),
- iterpool));
- err = svn_client_get_repos_root(&target_repos_root_url, NULL /* uuid */,
- target_abspath, ctx, iterpool, iterpool);
-
- /* Ignore externals that aren't present in the working copy.
- * This can happen if an external is deleted from disk accidentally,
- * or if an external is configured on a locally added directory. */
- if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
- {
- svn_error_clear(err);
- continue;
- }
- else
- SVN_ERR(err);
-
- if (strcmp(target_repos_root_url, old_parent_repos_root_url) == 0)
- SVN_ERR(svn_client_relocate2(target_abspath,
- old_parent_repos_root_url,
- new_parent_repos_root_url,
- FALSE, ctx, iterpool));
- }
-
- svn_pool_destroy(iterpool);
-
- return SVN_NO_ERROR;
-}
-
svn_error_t *
svn_client_relocate2(const char *wcroot_dir,
const char *from_prefix,
@@ -256,10 +177,9 @@ svn_client_relocate2(const char *wcroot_dir,
/* Relocate externals, too (if any). */
- SVN_ERR(svn_wc__externals_gather_definitions(&externals_hash, NULL,
- ctx->wc_ctx, local_abspath,
- svn_depth_infinity,
- pool, pool));
+ SVN_ERR(svn_wc__externals_defined_below(&externals_hash,
+ ctx->wc_ctx, local_abspath,
+ pool, pool));
if (! apr_hash_count(externals_hash))
return SVN_NO_ERROR;
@@ -269,18 +189,39 @@ svn_client_relocate2(const char *wcroot_dir,
hi != NULL;
hi = apr_hash_next(hi))
{
- const char *this_abspath = svn__apr_hash_index_key(hi);
- const char *value = svn__apr_hash_index_val(hi);
- apr_array_header_t *ext_desc;
+ svn_node_kind_t kind;
+ const char *this_abspath = apr_hash_this_key(hi);
svn_pool_clear(iterpool);
- SVN_ERR(svn_wc_parse_externals_description3(&ext_desc, this_abspath,
- value, FALSE,
- iterpool));
- if (ext_desc->nelts)
- SVN_ERR(relocate_externals(this_abspath, ext_desc, old_repos_root_url,
- new_repos_root_url, ctx, iterpool));
+ SVN_ERR(svn_wc__read_external_info(&kind, NULL, NULL, NULL, NULL,
+ ctx->wc_ctx,
+ local_abspath, this_abspath,
+ FALSE, iterpool, iterpool));
+
+ if (kind == svn_node_dir)
+ {
+ const char *this_repos_root_url;
+ svn_error_t *err;
+
+ err = svn_client_get_repos_root(&this_repos_root_url, NULL /* uuid */,
+ this_abspath, ctx, iterpool, iterpool);
+
+ /* Ignore externals that aren't present in the working copy.
+ * This can happen if an external is deleted from disk accidentally,
+ * or if an external is configured on a locally added directory. */
+ if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
+ {
+ svn_error_clear(err);
+ continue;
+ }
+ SVN_ERR(err);
+
+ if (strcmp(old_repos_root_url, this_repos_root_url) == 0)
+ SVN_ERR(svn_client_relocate2(this_abspath, from_prefix, to_prefix,
+ FALSE /* ignore_externals */,
+ ctx, iterpool));
+ }
}
svn_pool_destroy(iterpool);
diff --git a/subversion/libsvn_client/repos_diff.c b/subversion/libsvn_client/repos_diff.c
index 6a7725f59ff1..58fe8aaa1dae 100644
--- a/subversion/libsvn_client/repos_diff.c
+++ b/subversion/libsvn_client/repos_diff.c
@@ -328,7 +328,7 @@ get_file_from_ra(struct file_baton *fb,
fb->pool, scratch_pool));
fstream = svn_stream_checksummed2(fstream, NULL, &fb->start_md5_checksum,
- svn_checksum_md5, TRUE, scratch_pool);
+ svn_checksum_md5, TRUE, fb->pool);
/* Retrieve the file and its properties */
SVN_ERR(svn_ra_get_file(fb->edit_baton->ra_session,
@@ -389,6 +389,10 @@ remove_non_prop_changes(apr_hash_t *pristine_props,
{
int i;
+ /* For added nodes, there is nothing to filter. */
+ if (apr_hash_count(pristine_props) == 0)
+ return;
+
for (i = 0; i < changes->nelts; i++)
{
svn_prop_t *change = &APR_ARRAY_IDX(changes, i, svn_prop_t);
@@ -581,8 +585,8 @@ diff_deleted_dir(const char *path,
hi = apr_hash_next(hi))
{
const char *child_path;
- const char *name = svn__apr_hash_index_key(hi);
- svn_dirent_t *dirent = svn__apr_hash_index_val(hi);
+ const char *name = apr_hash_this_key(hi);
+ svn_dirent_t *dirent = apr_hash_this_val(hi);
svn_pool_clear(iterpool);
@@ -1162,7 +1166,7 @@ change_file_prop(void *file_baton,
propchange = apr_array_push(fb->propchanges);
propchange->name = apr_pstrdup(fb->pool, name);
- propchange->value = value ? svn_string_dup(value, fb->pool) : NULL;
+ propchange->value = svn_string_dup(value, fb->pool);
return SVN_NO_ERROR;
}
@@ -1192,7 +1196,7 @@ change_dir_prop(void *dir_baton,
propchange = apr_array_push(db->propchanges);
propchange->name = apr_pstrdup(db->pool, name);
- propchange->value = value ? svn_string_dup(value, db->pool) : NULL;
+ propchange->value = svn_string_dup(value, db->pool);
return SVN_NO_ERROR;
}
@@ -1365,6 +1369,7 @@ svn_client__get_diff_editor2(const svn_delta_editor_t **editor,
eb->ra_session = ra_session;
eb->revision = revision;
+ eb->target_revision = SVN_INVALID_REVNUM;
eb->empty_file = NULL;
eb->empty_hash = apr_hash_make(eb->pool);
eb->text_deltas = text_deltas;
diff --git a/subversion/libsvn_client/resolved.c b/subversion/libsvn_client/resolved.c
index 049637109dbc..8b94707ec89b 100644
--- a/subversion/libsvn_client/resolved.c
+++ b/subversion/libsvn_client/resolved.c
@@ -27,8 +27,6 @@
/*** Includes. ***/
-#include <stdlib.h>
-
#include "svn_types.h"
#include "svn_wc.h"
#include "svn_client.h"
@@ -39,6 +37,7 @@
#include "svn_hash.h"
#include "svn_sorts.h"
#include "client.h"
+#include "private/svn_sorts_private.h"
#include "private/svn_wc_private.h"
#include "svn_private_config.h"
@@ -59,8 +58,7 @@ svn_client__resolve_conflicts(svn_boolean_t *conflicts_remain,
*conflicts_remain = FALSE;
SVN_ERR(svn_hash_keys(&array, conflicted_paths, scratch_pool));
- qsort(array->elts, array->nelts, array->elt_size,
- svn_sort_compare_paths);
+ svn_sort__array(array, svn_sort_compare_paths);
for (i = 0; i < array->nelts; i++)
{
@@ -79,7 +77,7 @@ svn_client__resolve_conflicts(svn_boolean_t *conflicts_remain,
ctx->notify_func2, ctx->notify_baton2,
iterpool));
- if (conflicts_remain)
+ if (conflicts_remain && !*conflicts_remain)
{
svn_error_t *err;
svn_boolean_t text_c, prop_c, tree_c;
diff --git a/subversion/libsvn_client/revert.c b/subversion/libsvn_client/revert.c
index 681e39c07676..d827014ebe1e 100644
--- a/subversion/libsvn_client/revert.c
+++ b/subversion/libsvn_client/revert.c
@@ -49,6 +49,8 @@ struct revert_with_write_lock_baton {
svn_depth_t depth;
svn_boolean_t use_commit_times;
const apr_array_header_t *changelists;
+ svn_boolean_t clear_changelists;
+ svn_boolean_t metadata_only;
svn_client_ctx_t *ctx;
};
@@ -78,11 +80,13 @@ revert(void *baton, apr_pool_t *result_pool, apr_pool_t *scratch_pool)
struct revert_with_write_lock_baton *b = baton;
svn_error_t *err;
- err = svn_wc_revert4(b->ctx->wc_ctx,
+ err = svn_wc_revert5(b->ctx->wc_ctx,
b->local_abspath,
b->depth,
b->use_commit_times,
b->changelists,
+ b->clear_changelists,
+ b->metadata_only,
b->ctx->cancel_func, b->ctx->cancel_baton,
b->ctx->notify_func2, b->ctx->notify_baton2,
scratch_pool);
@@ -96,11 +100,18 @@ revert(void *baton, apr_pool_t *result_pool, apr_pool_t *scratch_pool)
|| err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
{
if (b->ctx->notify_func2)
- (*b->ctx->notify_func2)(
- b->ctx->notify_baton2,
- svn_wc_create_notify(b->local_abspath, svn_wc_notify_skip,
- scratch_pool),
- scratch_pool);
+ {
+ svn_wc_notify_t *notify;
+
+ notify = svn_wc_create_notify(b->local_abspath,
+ svn_wc_notify_skip,
+ scratch_pool);
+
+ notify->err = err;
+
+ b->ctx->notify_func2(b->ctx->notify_baton2,
+ notify, scratch_pool);
+ }
svn_error_clear(err);
}
else
@@ -112,13 +123,15 @@ revert(void *baton, apr_pool_t *result_pool, apr_pool_t *scratch_pool)
svn_error_t *
-svn_client_revert2(const apr_array_header_t *paths,
+svn_client_revert3(const apr_array_header_t *paths,
svn_depth_t depth,
const apr_array_header_t *changelists,
+ svn_boolean_t clear_changelists,
+ svn_boolean_t metadata_only,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
- apr_pool_t *subpool;
+ apr_pool_t *iterpool;
svn_error_t *err = SVN_NO_ERROR;
int i;
svn_config_t *cfg;
@@ -145,7 +158,7 @@ svn_client_revert2(const apr_array_header_t *paths,
SVN_CONFIG_OPTION_USE_COMMIT_TIMES,
FALSE));
- subpool = svn_pool_create(pool);
+ iterpool = svn_pool_create(pool);
for (i = 0; i < paths->nelts; i++)
{
@@ -153,14 +166,14 @@ svn_client_revert2(const apr_array_header_t *paths,
const char *local_abspath, *lock_target;
svn_boolean_t wc_root;
- svn_pool_clear(subpool);
+ svn_pool_clear(iterpool);
/* See if we've been asked to cancel this operation. */
if ((ctx->cancel_func)
&& ((err = ctx->cancel_func(ctx->cancel_baton))))
goto errorful;
- err = svn_dirent_get_absolute(&local_abspath, path, pool);
+ err = svn_dirent_get_absolute(&local_abspath, path, iterpool);
if (err)
goto errorful;
@@ -168,15 +181,18 @@ svn_client_revert2(const apr_array_header_t *paths,
baton.depth = depth;
baton.use_commit_times = use_commit_times;
baton.changelists = changelists;
+ baton.clear_changelists = clear_changelists;
+ baton.metadata_only = metadata_only;
baton.ctx = ctx;
- err = svn_wc__is_wcroot(&wc_root, ctx->wc_ctx, local_abspath, pool);
+ err = svn_wc__is_wcroot(&wc_root, ctx->wc_ctx, local_abspath, iterpool);
if (err)
goto errorful;
lock_target = wc_root ? local_abspath
: svn_dirent_dirname(local_abspath, pool);
err = svn_wc__call_with_write_lock(revert, &baton, ctx->wc_ctx,
- lock_target, FALSE, pool, pool);
+ lock_target, FALSE,
+ iterpool, iterpool);
if (err)
goto errorful;
}
@@ -192,10 +208,10 @@ svn_client_revert2(const apr_array_header_t *paths,
if (paths->nelts == 1)
sleep_path = APR_ARRAY_IDX(paths, 0, const char *);
- svn_io_sleep_for_timestamps(sleep_path, subpool);
+ svn_io_sleep_for_timestamps(sleep_path, iterpool);
}
- svn_pool_destroy(subpool);
+ svn_pool_destroy(iterpool);
return svn_error_trace(err);
}
diff --git a/subversion/libsvn_client/revisions.c b/subversion/libsvn_client/revisions.c
index ec255c1e1ca4..e61e7d475f86 100644
--- a/subversion/libsvn_client/revisions.c
+++ b/subversion/libsvn_client/revisions.c
@@ -92,6 +92,7 @@ svn_client__get_revision_number(svn_revnum_t *revnum,
goto invalid_rev_arg;
err = svn_wc__node_get_origin(NULL, revnum, NULL, NULL, NULL, NULL,
+ NULL,
wc_ctx, local_abspath, TRUE,
scratch_pool, scratch_pool);
diff --git a/subversion/libsvn_client/status.c b/subversion/libsvn_client/status.c
index e581d37a41e4..a70165867140 100644
--- a/subversion/libsvn_client/status.c
+++ b/subversion/libsvn_client/status.c
@@ -29,7 +29,9 @@
#include <apr_strings.h>
#include <apr_pools.h>
+#include "svn_private_config.h"
#include "svn_pools.h"
+#include "svn_sorts.h"
#include "client.h"
#include "svn_path.h"
@@ -39,9 +41,9 @@
#include "svn_error.h"
#include "svn_hash.h"
-#include "svn_private_config.h"
-#include "private/svn_wc_private.h"
#include "private/svn_client_private.h"
+#include "private/svn_sorts_private.h"
+#include "private/svn_wc_private.h"
/*** Getting update information ***/
@@ -195,8 +197,7 @@ reporter_finish_report(void *report_baton, apr_pool_t *pool)
server doesn't support lock discovery, we'll just not do locky
stuff. */
err = svn_ra_get_locks2(ras, &locks, "", rb->depth, rb->pool);
- if (err && ((err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)
- || (err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE)))
+ if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)
{
svn_error_clear(err);
err = SVN_NO_ERROR;
@@ -245,27 +246,32 @@ do_external_status(svn_client_ctx_t *ctx,
apr_hash_t *external_map,
svn_depth_t depth,
svn_boolean_t get_all,
- svn_boolean_t update,
+ svn_boolean_t check_out_of_date,
+ svn_boolean_t check_working_copy,
svn_boolean_t no_ignore,
+ const apr_array_header_t *changelists,
const char *anchor_abspath,
const char *anchor_relpath,
svn_client_status_func_t status_func,
void *status_baton,
apr_pool_t *scratch_pool)
{
- apr_hash_index_t *hi;
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+ apr_array_header_t *externals;
+ int i;
+
+ externals = svn_sort__hash(external_map, svn_sort_compare_items_lexically,
+ scratch_pool);
/* Loop over the hash of new values (we don't care about the old
ones). This is a mapping of versioned directories to property
values. */
- for (hi = apr_hash_first(scratch_pool, external_map);
- hi;
- hi = apr_hash_next(hi))
+ for (i = 0; i < externals->nelts; i++)
{
svn_node_kind_t external_kind;
- const char *local_abspath = svn__apr_hash_index_key(hi);
- const char *defining_abspath = svn__apr_hash_index_val(hi);
+ svn_sort__item_t item = APR_ARRAY_IDX(externals, i, svn_sort__item_t);
+ const char *local_abspath = item.key;
+ const char *defining_abspath = item.value;
svn_node_kind_t kind;
svn_opt_revision_t opt_rev;
const char *status_path;
@@ -309,9 +315,12 @@ do_external_status(svn_client_ctx_t *ctx,
}
/* And then do the status. */
- SVN_ERR(svn_client_status5(NULL, ctx, status_path, &opt_rev, depth,
- get_all, update, no_ignore, FALSE, FALSE,
- NULL, status_func, status_baton,
+ SVN_ERR(svn_client_status6(NULL, ctx, status_path, &opt_rev, depth,
+ get_all, check_out_of_date,
+ check_working_copy, no_ignore,
+ FALSE /* ignore_exernals */,
+ FALSE /* depth_as_sticky */,
+ changelists, status_func, status_baton,
iterpool));
}
@@ -325,13 +334,14 @@ do_external_status(svn_client_ctx_t *ctx,
svn_error_t *
-svn_client_status5(svn_revnum_t *result_rev,
+svn_client_status6(svn_revnum_t *result_rev,
svn_client_ctx_t *ctx,
const char *path,
const svn_opt_revision_t *revision,
svn_depth_t depth,
svn_boolean_t get_all,
- svn_boolean_t update,
+ svn_boolean_t check_out_of_date,
+ svn_boolean_t check_working_copy,
svn_boolean_t no_ignore,
svn_boolean_t ignore_externals,
svn_boolean_t depth_as_sticky,
@@ -348,6 +358,11 @@ svn_client_status5(svn_revnum_t *result_rev,
svn_error_t *err;
apr_hash_t *changelist_hash = NULL;
+ /* Override invalid combinations of the check_out_of_date and
+ check_working_copy flags. */
+ if (!check_out_of_date)
+ check_working_copy = TRUE;
+
if (svn_path_is_url(path))
return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
_("'%s' is not a local path"), path);
@@ -366,7 +381,7 @@ svn_client_status5(svn_revnum_t *result_rev,
SVN_ERR(svn_dirent_get_absolute(&target_abspath, path, pool));
- if (update)
+ if (check_out_of_date)
{
/* The status editor only works on directories, so get the ancestor
if necessary */
@@ -434,7 +449,7 @@ svn_client_status5(svn_revnum_t *result_rev,
/* If we want to know about out-of-dateness, we crawl the working copy and
let the RA layer drive the editor for real. Otherwise, we just close the
edit. :-) */
- if (update)
+ if (check_out_of_date)
{
svn_ra_session_t *ra_session;
const char *URL;
@@ -464,14 +479,14 @@ svn_client_status5(svn_revnum_t *result_rev,
SVN_RA_CAPABILITY_DEPTH, pool));
SVN_ERR(svn_wc__get_status_editor(&editor, &edit_baton, &set_locks_baton,
- &edit_revision, ctx->wc_ctx,
- dir_abspath, target_basename,
- depth, get_all,
- no_ignore, depth_as_sticky,
- server_supports_depth,
- ignores, tweak_status, &sb,
- ctx->cancel_func, ctx->cancel_baton,
- pool, pool));
+ &edit_revision, ctx->wc_ctx,
+ dir_abspath, target_basename,
+ depth, get_all, check_working_copy,
+ no_ignore, depth_as_sticky,
+ server_supports_depth,
+ ignores, tweak_status, &sb,
+ ctx->cancel_func, ctx->cancel_baton,
+ pool, pool));
/* Verify that URL exists in HEAD. If it doesn't, this can save
@@ -562,7 +577,7 @@ svn_client_status5(svn_revnum_t *result_rev,
= svn_wc_create_notify(target_abspath,
svn_wc_notify_status_completed, pool);
notify->revision = edit_revision;
- (ctx->notify_func2)(ctx->notify_baton2, notify, pool);
+ ctx->notify_func2(ctx->notify_baton2, notify, pool);
}
/* If the caller wants the result revision, give it to them. */
@@ -590,14 +605,7 @@ svn_client_status5(svn_revnum_t *result_rev,
SVN_ERR(err);
}
- /* If there are svn:externals set, we don't want those to show up as
- unversioned or unrecognized, so patch up the hash. If caller wants
- all the statuses, we will change unversioned status items that
- are interesting to an svn:externals property to
- svn_wc_status_unversioned, otherwise we'll just remove the status
- item altogether.
-
- We only descend into an external if depth is svn_depth_infinity or
+ /* We only descend into an external if depth is svn_depth_infinity or
svn_depth_unknown. However, there are conceivable behaviors that
would involve descending under other circumstances; thus, we pass
depth anyway, so the code will DTRT if we change the conditional
@@ -613,7 +621,8 @@ svn_client_status5(svn_revnum_t *result_rev,
SVN_ERR(do_external_status(ctx, external_map,
depth, get_all,
- update, no_ignore,
+ check_out_of_date, check_working_copy,
+ no_ignore, changelists,
sb.anchor_abspath, sb.anchor_relpath,
status_func, status_baton, pool));
}
diff --git a/subversion/libsvn_client/switch.c b/subversion/libsvn_client/switch.c
index cd39cad936cf..37ab05c11b40 100644
--- a/subversion/libsvn_client/switch.c
+++ b/subversion/libsvn_client/switch.c
@@ -231,8 +231,6 @@ switch_internal(svn_revnum_t *result_rev,
yca = NULL; /* Not versioned */
else
{
- /* ### It would be nice if this function could reuse the existing
- ra session instead of opening two for its own use. */
SVN_ERR(svn_client__get_youngest_common_ancestor(
&yca, switch_loc, target_base_loc, ra_session, ctx,
pool, pool));
@@ -340,8 +338,8 @@ switch_internal(svn_revnum_t *result_rev,
*timestamp_sleep = TRUE;
/* Drive the reporter structure, describing the revisions within
- PATH. When we call reporter->finish_report, the update_editor
- will be driven by svn_repos_dir_delta2. */
+ LOCAL_ABSPATH. When this calls reporter->finish_report, the
+ reporter will drive the switch_editor. */
SVN_ERR(svn_wc_crawl_revisions5(ctx->wc_ctx, local_abspath, reporter,
report_baton, TRUE,
depth, (! depth_is_sticky),
@@ -367,7 +365,7 @@ switch_internal(svn_revnum_t *result_rev,
new_depths,
switch_loc->repos_root_url,
local_abspath,
- depth, timestamp_sleep,
+ depth, timestamp_sleep, ra_session,
ctx, pool));
}
@@ -382,7 +380,7 @@ switch_internal(svn_revnum_t *result_rev,
= svn_wc_notify_state_inapplicable;
notify->lock_state = svn_wc_notify_lock_state_inapplicable;
notify->revision = revnum;
- (*ctx->notify_func2)(ctx->notify_baton2, notify, pool);
+ ctx->notify_func2(ctx->notify_baton2, notify, pool);
}
/* If the caller wants the result revision, give it to them. */
diff --git a/subversion/libsvn_client/update.c b/subversion/libsvn_client/update.c
index 0b006cc33fbe..3895aa29d658 100644
--- a/subversion/libsvn_client/update.c
+++ b/subversion/libsvn_client/update.c
@@ -196,10 +196,16 @@ record_conflict(svn_wc_conflict_result_t **result,
Add the paths of any conflict victims to CONFLICTED_PATHS, if that
is not null.
+
+ Use RA_SESSION_P to run the update if it is not NULL. If it is then
+ open a new ra session and place it in RA_SESSION_P. This allows
+ repeated calls to update_internal to reuse the same session.
*/
static svn_error_t *
update_internal(svn_revnum_t *result_rev,
+ svn_boolean_t *timestamp_sleep,
apr_hash_t *conflicted_paths,
+ svn_ra_session_t **ra_session_p,
const char *local_abspath,
const char *anchor_abspath,
const svn_opt_revision_t *revision,
@@ -208,10 +214,10 @@ update_internal(svn_revnum_t *result_rev,
svn_boolean_t ignore_externals,
svn_boolean_t allow_unver_obstructions,
svn_boolean_t adds_as_modification,
- svn_boolean_t *timestamp_sleep,
svn_boolean_t notify_summary,
svn_client_ctx_t *ctx,
- apr_pool_t *pool)
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
const svn_delta_editor_t *update_editor;
void *update_edit_baton;
@@ -229,7 +235,7 @@ update_internal(svn_revnum_t *result_rev,
const char *diff3_cmd;
apr_hash_t *wcroot_iprops;
svn_opt_revision_t opt_rev;
- svn_ra_session_t *ra_session;
+ svn_ra_session_t *ra_session = *ra_session_p;
const char *preserved_exts_str;
apr_array_header_t *preserved_exts;
struct svn_client__dirent_fetcher_baton_t dfb;
@@ -248,7 +254,7 @@ update_internal(svn_revnum_t *result_rev,
depth_is_sticky = FALSE;
if (strcmp(local_abspath, anchor_abspath))
- target = svn_dirent_basename(local_abspath, pool);
+ target = svn_dirent_basename(local_abspath, scratch_pool);
else
target = "";