summaryrefslogtreecommitdiff
path: root/subversion/libsvn_client
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_client')
-rw-r--r--subversion/libsvn_client/commit_util.c118
-rw-r--r--subversion/libsvn_client/export.c5
-rw-r--r--subversion/libsvn_client/merge.c159
-rw-r--r--subversion/libsvn_client/prop_commands.c18
4 files changed, 196 insertions, 104 deletions
diff --git a/subversion/libsvn_client/commit_util.c b/subversion/libsvn_client/commit_util.c
index 1e2c50cd9e617..a32ec5d3ef807 100644
--- a/subversion/libsvn_client/commit_util.c
+++ b/subversion/libsvn_client/commit_util.c
@@ -892,7 +892,7 @@ harvest_status_callback(void *status_baton,
if (matches_changelists
&& (is_harvest_root || baton->changelists)
&& state_flags
- && is_added
+ && (is_added || (is_deleted && is_op_root && status->copied))
&& baton->danglers)
{
/* If a node is added, its parent must exist in the repository at the
@@ -966,17 +966,19 @@ struct handle_descendants_baton
void *cancel_baton;
svn_client__check_url_kind_t check_url_func;
void *check_url_baton;
+ svn_client__committables_t *committables;
};
/* Helper for the commit harvesters */
static svn_error_t *
handle_descendants(void *baton,
- const void *key, apr_ssize_t klen, void *val,
- apr_pool_t *pool)
+ const void *key, apr_ssize_t klen, void *val,
+ apr_pool_t *pool)
{
struct handle_descendants_baton *hdb = baton;
apr_array_header_t *commit_items = val;
apr_pool_t *iterpool = svn_pool_create(pool);
+ const char *repos_root_url = key;
int i;
for (i = 0; i < commit_items->nelts; i++)
@@ -1002,32 +1004,64 @@ handle_descendants(void *baton,
for (j = 0; j < absent_descendants->nelts; j++)
{
- int k;
- svn_boolean_t found_item = FALSE;
svn_node_kind_t kind;
+ svn_client_commit_item3_t *desc_item;
const char *relpath = APR_ARRAY_IDX(absent_descendants, j,
const char *);
const char *local_abspath = svn_dirent_join(item->path, relpath,
iterpool);
- /* If the path has a commit operation, we do nothing.
- (It will be deleted by the operation) */
- for (k = 0; k < commit_items->nelts; k++)
+ /* ### Need a sub-iterpool? */
+
+
+ /* We found a 'not present' descendant during a copy (at op_depth>0),
+ this is most commonly caused by copying some mixed revision tree.
+
+ In this case not present can imply that the node does not exist
+ in the parent revision, or that the node does. But we want to copy
+ the working copy state in which it does not exist, but might be
+ replaced. */
+
+ desc_item = svn_hash_gets(hdb->committables->by_path, local_abspath);
+
+ /* If the path has a commit operation (possibly at an higher
+ op_depth, we might want to turn an add in a replace. */
+ if (desc_item)
{
- svn_client_commit_item3_t *cmt_item =
- APR_ARRAY_IDX(commit_items, k, svn_client_commit_item3_t *);
+ const char *dir;
+ svn_boolean_t found_intermediate = FALSE;
+
+ if (desc_item->state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE)
+ continue; /* We already have a delete or replace */
+ else if (!(desc_item->state_flags & SVN_CLIENT_COMMIT_ITEM_ADD))
+ continue; /* Not a copy/add, just a modification */
- if (! strcmp(cmt_item->path, local_abspath))
+ dir = svn_dirent_dirname(local_abspath, iterpool);
+
+ while (strcmp(dir, item->path))
{
- found_item = TRUE;
- break;
+ svn_client_commit_item3_t *i_item;
+
+ i_item = svn_hash_gets(hdb->committables->by_path, dir);
+
+ if (i_item)
+ {
+ if ((i_item->state_flags & SVN_CLIENT_COMMIT_ITEM_DELETE)
+ || (i_item->state_flags & SVN_CLIENT_COMMIT_ITEM_ADD))
+ {
+ found_intermediate = TRUE;
+ break;
+ }
+ }
+ dir = svn_dirent_dirname(dir, iterpool);
}
- }
- if (found_item)
- continue; /* We have an explicit delete or replace for this path */
+ if (found_intermediate)
+ continue; /* Some intermediate ancestor is an add or delete */
- /* ### Need a sub-iterpool? */
+ /* Fall through to detect if we need to turn the add in a
+ replace. */
+ }
if (hdb->check_url_func)
{
@@ -1045,25 +1079,35 @@ handle_descendants(void *baton,
else
kind = svn_node_unknown; /* 'Ok' for a delete of something */
- {
- /* Add a new commit item that describes the delete */
- apr_pool_t *result_pool = commit_items->pool;
- svn_client_commit_item3_t *new_item
- = svn_client_commit_item3_create(result_pool);
-
- new_item->path = svn_dirent_join(item->path, relpath,
- result_pool);
- new_item->kind = kind;
- new_item->url = svn_path_url_add_component2(item->url, relpath,
- result_pool);
- new_item->revision = SVN_INVALID_REVNUM;
- new_item->state_flags = SVN_CLIENT_COMMIT_ITEM_DELETE;
- new_item->incoming_prop_changes = apr_array_make(result_pool, 1,
- sizeof(svn_prop_t *));
-
- APR_ARRAY_PUSH(commit_items, svn_client_commit_item3_t *)
- = new_item;
- }
+ if (desc_item)
+ {
+ /* Extend the existing add/copy item to create a replace */
+ desc_item->state_flags |= SVN_CLIENT_COMMIT_ITEM_DELETE;
+ continue;
+ }
+
+ /* Add a new commit item that describes the delete */
+
+ SVN_ERR(add_committable(hdb->committables,
+ svn_dirent_join(item->path, relpath,
+ iterpool),
+ kind,
+ repos_root_url,
+ svn_uri_skip_ancestor(
+ repos_root_url,
+ svn_path_url_add_component2(item->url,
+ relpath,
+ iterpool),
+ iterpool),
+ SVN_INVALID_REVNUM,
+ NULL /* copyfrom_relpath */,
+ SVN_INVALID_REVNUM,
+ NULL /* moved_from_abspath */,
+ SVN_CLIENT_COMMIT_ITEM_DELETE,
+ NULL /* lock tokens */,
+ NULL /* lock */,
+ commit_items->pool,
+ iterpool));
}
}
@@ -1181,6 +1225,7 @@ svn_client__harvest_committables(svn_client__committables_t **committables,
hdb.cancel_baton = ctx->cancel_baton;
hdb.check_url_func = check_url_func;
hdb.check_url_baton = check_url_baton;
+ hdb.committables = *committables;
SVN_ERR(svn_iter_apr_hash(NULL, (*committables)->by_repository,
handle_descendants, &hdb, iterpool));
@@ -1274,6 +1319,7 @@ harvest_copy_committables(void *baton, void *item, apr_pool_t *pool)
hdb.cancel_baton = btn->ctx->cancel_baton;
hdb.check_url_func = btn->check_url_func;
hdb.check_url_baton = btn->check_url_baton;
+ hdb.committables = btn->committables;
SVN_ERR(svn_iter_apr_hash(NULL, btn->committables->by_repository,
handle_descendants, &hdb, pool));
diff --git a/subversion/libsvn_client/export.c b/subversion/libsvn_client/export.c
index d6022ed68f291..c14a5f0bc0178 100644
--- a/subversion/libsvn_client/export.c
+++ b/subversion/libsvn_client/export.c
@@ -267,7 +267,9 @@ export_node(void *baton,
scratch_pool));
}
- if (status->file_external)
+ /* Skip file externals if they are a descendant of the export,
+ BUT NOT if we are explictly exporting the file external. */
+ if (status->file_external && strcmp(eib->origin_abspath, local_abspath) != 0)
return SVN_NO_ERROR;
/* Produce overwrite errors for the export root */
@@ -1587,3 +1589,4 @@ svn_client_export5(svn_revnum_t *result_rev,
return SVN_NO_ERROR;
}
+
diff --git a/subversion/libsvn_client/merge.c b/subversion/libsvn_client/merge.c
index cb07f8303c2b3..b314e8409b2a3 100644
--- a/subversion/libsvn_client/merge.c
+++ b/subversion/libsvn_client/merge.c
@@ -322,6 +322,10 @@ typedef struct merge_cmd_baton_t {
const char *diff3_cmd;
const apr_array_header_t *merge_options;
+ /* Array of file extension patterns to preserve as extensions in
+ generated conflict files. */
+ const apr_array_header_t *ext_patterns;
+
/* RA sessions used throughout a merge operation. Opened/re-parented
as needed.
@@ -2023,17 +2027,36 @@ merge_file_changed(const char *relpath,
{
svn_boolean_t has_local_mods;
enum svn_wc_merge_outcome_t content_outcome;
+ const char *target_label;
+ const char *left_label;
+ const char *right_label;
+ const char *path_ext = "";
+
+ if (merge_b->ext_patterns && merge_b->ext_patterns->nelts)
+ {
+ svn_path_splitext(NULL, &path_ext, local_abspath, scratch_pool);
+ if (! (*path_ext
+ && svn_cstring_match_glob_list(path_ext,
+ merge_b->ext_patterns)))
+ {
+ path_ext = "";
+ }
+ }
/* xgettext: the '.working', '.merge-left.r%ld' and
'.merge-right.r%ld' strings are used to tag onto a file
name in case of a merge conflict */
- const char *target_label = _(".working");
- const char *left_label = apr_psprintf(scratch_pool,
- _(".merge-left.r%ld"),
- left_source->revision);
- const char *right_label = apr_psprintf(scratch_pool,
- _(".merge-right.r%ld"),
- right_source->revision);
+
+ target_label = apr_psprintf(scratch_pool, _(".working%s%s"),
+ *path_ext ? "." : "", path_ext);
+ left_label = apr_psprintf(scratch_pool,
+ _(".merge-left.r%ld%s%s"),
+ left_source->revision,
+ *path_ext ? "." : "", path_ext);
+ right_label = apr_psprintf(scratch_pool,
+ _(".merge-right.r%ld%s%s"),
+ right_source->revision,
+ *path_ext ? "." : "", path_ext);
SVN_ERR(svn_wc_text_modified_p2(&has_local_mods, ctx->wc_ctx,
local_abspath, FALSE, scratch_pool));
@@ -3062,7 +3085,6 @@ merge_dir_deleted(const char *relpath,
struct merge_dir_baton_t *db = dir_baton;
const char *local_abspath = svn_dirent_join(merge_b->target->abspath,
relpath, scratch_pool);
- struct dir_delete_baton_t *delb;
svn_boolean_t same;
apr_hash_t *working_props;
@@ -3093,66 +3115,69 @@ merge_dir_deleted(const char *relpath,
scratch_pool, scratch_pool));
if (merge_b->force_delete)
- same = TRUE;
+ {
+ /* In this legacy mode we just assume that a directory delete
+ matches any directory. db->delete_state is NULL */
+ same = TRUE;
+ }
else
{
+ struct dir_delete_baton_t *delb;
+
/* Compare the properties */
SVN_ERR(properties_same_p(&same, left_props, working_props,
scratch_pool));
- }
+ delb = db->delete_state;
+ assert(delb != NULL);
- delb = db->delete_state;
- assert(delb != NULL);
-
- if (! same)
- {
- delb->found_edit = TRUE;
- }
- else
- {
- store_path(delb->compared_abspaths, local_abspath);
- }
+ if (! same)
+ {
+ delb->found_edit = TRUE;
+ }
+ else
+ {
+ store_path(delb->compared_abspaths, local_abspath);
+ }
- if (delb->del_root != db)
- return SVN_NO_ERROR;
+ if (delb->del_root != db)
+ return SVN_NO_ERROR;
- if (delb->found_edit)
- same = FALSE;
- else if (merge_b->force_delete)
- same = TRUE;
- else
- {
- apr_array_header_t *ignores;
- svn_error_t *err;
- same = TRUE;
+ if (delb->found_edit)
+ same = FALSE;
+ else
+ {
+ apr_array_header_t *ignores;
+ svn_error_t *err;
+ same = TRUE;
- SVN_ERR(svn_wc_get_default_ignores(&ignores, merge_b->ctx->config,
- scratch_pool));
+ SVN_ERR(svn_wc_get_default_ignores(&ignores, merge_b->ctx->config,
+ scratch_pool));
- /* None of the descendants was modified, but maybe there are
- descendants we haven't walked?
-
- Note that we aren't interested in changes, as we already verified
- changes in the paths touched by the merge. And the existance of
- other paths is enough to mark the directory edited */
- err = svn_wc_walk_status(merge_b->ctx->wc_ctx, local_abspath,
- svn_depth_infinity, TRUE /* get-all */,
- FALSE /* no-ignore */,
- TRUE /* ignore-text-mods */, ignores,
- verify_touched_by_del_check, delb,
- merge_b->ctx->cancel_func,
- merge_b->ctx->cancel_baton,
- scratch_pool);
+ /* None of the descendants was modified, but maybe there are
+ descendants we haven't walked?
+
+ Note that we aren't interested in changes, as we already verified
+ changes in the paths touched by the merge. And the existence of
+ other paths is enough to mark the directory edited */
+ err = svn_wc_walk_status(merge_b->ctx->wc_ctx, local_abspath,
+ svn_depth_infinity, TRUE /* get-all */,
+ FALSE /* no-ignore */,
+ TRUE /* ignore-text-mods */, ignores,
+ verify_touched_by_del_check, delb,
+ merge_b->ctx->cancel_func,
+ merge_b->ctx->cancel_baton,
+ scratch_pool);
+
+ if (err)
+ {
+ if (err->apr_err != SVN_ERR_CEASE_INVOCATION)
+ return svn_error_trace(err);
- if (err)
- {
- if (err->apr_err != SVN_ERR_CEASE_INVOCATION)
- return svn_error_trace(err);
+ svn_error_clear(err);
+ }
- svn_error_clear(err);
+ same = ! delb->found_edit;
}
-
- same = ! delb->found_edit;
}
if (same && !merge_b->dry_run)
@@ -9668,6 +9693,7 @@ do_merge(apr_hash_t **modified_subtrees,
merge_cmd_baton_t merge_cmd_baton = { 0 };
svn_config_t *cfg;
const char *diff3_cmd;
+ const char *preserved_exts_str;
int i;
svn_boolean_t checked_mergeinfo_capability = FALSE;
svn_ra_session_t *ra_session1 = NULL, *ra_session2 = NULL;
@@ -9728,6 +9754,11 @@ do_merge(apr_hash_t **modified_subtrees,
if (diff3_cmd != NULL)
SVN_ERR(svn_path_cstring_to_utf8(&diff3_cmd, diff3_cmd, scratch_pool));
+ /* See which files the user wants to preserve the extension of when
+ conflict files are made. */
+ svn_config_get(cfg, &preserved_exts_str, SVN_CONFIG_SECTION_MISCELLANY,
+ SVN_CONFIG_OPTION_PRESERVED_CF_EXTS, "");
+
/* Build the merge context baton (or at least the parts of it that
don't need to be reset for each merge source). */
merge_cmd_baton.force_delete = force_delete;
@@ -9743,6 +9774,11 @@ do_merge(apr_hash_t **modified_subtrees,
merge_cmd_baton.pool = iterpool;
merge_cmd_baton.merge_options = merge_options;
merge_cmd_baton.diff3_cmd = diff3_cmd;
+ merge_cmd_baton.ext_patterns = *preserved_exts_str
+ ? svn_cstring_split(preserved_exts_str, "\n\r\t\v ",
+ FALSE, scratch_pool)
+ : NULL;
+
merge_cmd_baton.use_sleep = use_sleep;
/* Do we already know the specific subtrees with mergeinfo we want
@@ -10447,15 +10483,10 @@ merge_locked(conflict_report_t **conflict_report,
}
else
{
- merge_source_t source;
-
- source.loc1 = source1_loc;
- source.loc2 = source2_loc;
- source.ancestral = FALSE;
-
/* Build a single-item merge_source_t array. */
merge_sources = apr_array_make(scratch_pool, 1, sizeof(merge_source_t *));
- APR_ARRAY_PUSH(merge_sources, merge_source_t *) = &source;
+ APR_ARRAY_PUSH(merge_sources, merge_source_t *)
+ = merge_source_create(source1_loc, source2_loc, FALSE, scratch_pool);
}
err = do_merge(NULL, NULL, conflict_report, &use_sleep,
@@ -10761,7 +10792,7 @@ log_find_operative_revs(void *baton,
UNMERGED_CATALOG represents the history (as mergeinfo) from
TARGET_LOC that is not represented in SOURCE_LOC's
explicit/inherited mergeinfo as represented by MERGED_CATALOG.
- MERGEINFO_CATALOG may be empty if the source has no explicit or inherited
+ MERGED_CATALOG may be empty if the source has no explicit or inherited
mergeinfo.
Check that all of the unmerged revisions in UNMERGED_CATALOG's
@@ -11464,7 +11495,7 @@ find_reintegrate_merge(merge_source_t **source_p,
prefix. */
svn_mergeinfo_catalog_t final_unmerged_catalog = apr_hash_make(scratch_pool);
- SVN_ERR(find_unsynced_ranges(source_loc, yc_ancestor,
+ SVN_ERR(find_unsynced_ranges(source_loc, &target->loc,
unmerged_to_source_mergeinfo_catalog,
merged_to_source_mergeinfo_catalog,
final_unmerged_catalog,
diff --git a/subversion/libsvn_client/prop_commands.c b/subversion/libsvn_client/prop_commands.c
index a3e59c866813d..06c4d21dc1e06 100644
--- a/subversion/libsvn_client/prop_commands.c
+++ b/subversion/libsvn_client/prop_commands.c
@@ -890,8 +890,14 @@ svn_client_propget5(apr_hash_t **props,
const char *copy_root_abspath;
svn_boolean_t is_copy;
- SVN_ERR(svn_dirent_get_absolute(&local_abspath, target,
- scratch_pool));
+ /* Avoid assertion on the next line when somebody accidentally asks for
+ a working copy revision on a URL */
+ if (svn_path_is_url(target))
+ return svn_error_create(SVN_ERR_CLIENT_VERSIONED_PATH_REQUIRED,
+ NULL, NULL);
+
+ SVN_ERR_ASSERT(svn_dirent_is_absolute(target));
+ local_abspath = target;
if (SVN_CLIENT__REVKIND_NEEDS_WC(peg_revision->kind))
{
@@ -1232,7 +1238,7 @@ recursive_proplist_receiver(void *baton,
Report iprops anyway */
SVN_ERR(b->wrapped_receiver(b->wrapped_receiver_baton,
- b->anchor ? b->anchor : local_abspath,
+ b->anchor ? b->anchor : b->anchor_abspath,
NULL /* prop_hash */,
b->iprops,
scratch_pool));
@@ -1293,6 +1299,12 @@ get_remote_props(const char *path_or_url,
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 */
+ if (svn_path_is_url(path_or_url))
+ return svn_error_create(SVN_ERR_CLIENT_VERSIONED_PATH_REQUIRED,
+ NULL, NULL);
+
SVN_ERR(svn_dirent_get_absolute(&local_abspath, path_or_url,
scratch_pool));