aboutsummaryrefslogtreecommitdiffstats
path: root/subversion/svn
diff options
context:
space:
mode:
authorPeter Wemm <peter@FreeBSD.org>2018-05-08 03:44:38 +0000
committerPeter Wemm <peter@FreeBSD.org>2018-05-08 03:44:38 +0000
commit3faf8d6bffc5d0fb2525ba37bb504c53366caf9d (patch)
tree7e47911263e75034b767fe34b2f8d3d17e91f66d /subversion/svn
parenta55fb3c0d5eca7d887798125d5b95942b1f01d4b (diff)
downloadsrc-3faf8d6bffc5d0fb2525ba37bb504c53366caf9d.tar.gz
src-3faf8d6bffc5d0fb2525ba37bb504c53366caf9d.zip
Import Subversion-1.10.0vendor/subversion/subversion-1.10.0
Notes
Notes: svn path=/vendor/subversion/dist/; revision=333347 svn path=/vendor/subversion/subversion-1.10.0/; revision=333348; tag=vendor/subversion/subversion-1.10.0
Diffstat (limited to 'subversion/svn')
-rw-r--r--subversion/svn/cl-conflicts.c308
-rw-r--r--subversion/svn/cl-conflicts.h7
-rw-r--r--subversion/svn/cl-log.h5
-rw-r--r--subversion/svn/cl.h96
-rw-r--r--subversion/svn/cleanup-cmd.c5
-rw-r--r--subversion/svn/conflict-callbacks.c2103
-rw-r--r--subversion/svn/diff-cmd.c37
-rw-r--r--subversion/svn/help-cmd.c34
-rw-r--r--subversion/svn/info-cmd.c181
-rw-r--r--subversion/svn/list-cmd.c31
-rw-r--r--subversion/svn/log-cmd.c42
-rw-r--r--subversion/svn/merge-cmd.c123
-rw-r--r--subversion/svn/notify.c134
-rw-r--r--subversion/svn/propdel-cmd.c8
-rw-r--r--subversion/svn/propedit-cmd.c46
-rw-r--r--subversion/svn/propget-cmd.c43
-rw-r--r--subversion/svn/propset-cmd.c20
-rw-r--r--subversion/svn/resolve-cmd.c193
-rw-r--r--subversion/svn/shelve-cmd.c369
-rw-r--r--subversion/svn/status.c7
-rw-r--r--subversion/svn/svn.c499
-rw-r--r--subversion/svn/switch-cmd.c10
-rw-r--r--subversion/svn/update-cmd.c13
-rw-r--r--subversion/svn/util.c22
24 files changed, 3192 insertions, 1144 deletions
diff --git a/subversion/svn/cl-conflicts.c b/subversion/svn/cl-conflicts.c
index 8507e8c3b309..0dc0abf29114 100644
--- a/subversion/svn/cl-conflicts.c
+++ b/subversion/svn/cl-conflicts.c
@@ -58,14 +58,6 @@ static const svn_token_map_t map_conflict_reason_xml[] =
{ NULL, 0 }
};
-static const svn_token_map_t map_conflict_kind_xml[] =
-{
- { "text", svn_wc_conflict_kind_text },
- { "property", svn_wc_conflict_kind_property },
- { "tree", svn_wc_conflict_kind_tree },
- { NULL, 0 }
-};
-
/* Return a localised string representation of the local part of a conflict;
NULL for non-localised odd cases. */
static const char *
@@ -229,14 +221,14 @@ operation_str(svn_wc_operation_t operation)
svn_error_t *
svn_cl__get_human_readable_prop_conflict_description(
const char **desc,
- const svn_wc_conflict_description2_t *conflict,
+ svn_client_conflict_t *conflict,
apr_pool_t *pool)
{
const char *reason_str, *action_str;
/* We provide separately translatable strings for the values that we
* know about, and a fall-back in case any other values occur. */
- switch (conflict->reason)
+ switch (svn_client_conflict_get_local_change(conflict))
{
case svn_wc_conflict_reason_edited:
reason_str = _("local edit");
@@ -251,12 +243,14 @@ svn_cl__get_human_readable_prop_conflict_description(
reason_str = _("local obstruction");
break;
default:
- reason_str = apr_psprintf(pool, _("local %s"),
- svn_token__to_word(map_conflict_reason_xml,
- conflict->reason));
+ reason_str = apr_psprintf(
+ pool, _("local %s"),
+ svn_token__to_word(
+ map_conflict_reason_xml,
+ svn_client_conflict_get_local_change(conflict)));
break;
}
- switch (conflict->action)
+ switch (svn_client_conflict_get_incoming_change(conflict))
{
case svn_wc_conflict_action_edit:
action_str = _("incoming edit");
@@ -268,51 +262,63 @@ svn_cl__get_human_readable_prop_conflict_description(
action_str = _("incoming delete");
break;
default:
- action_str = apr_psprintf(pool, _("incoming %s"),
- svn_token__to_word(map_conflict_action_xml,
- conflict->action));
+ action_str = apr_psprintf(
+ pool, _("incoming %s"),
+ svn_token__to_word(
+ map_conflict_action_xml,
+ svn_client_conflict_get_incoming_change(conflict)));
break;
}
SVN_ERR_ASSERT(reason_str && action_str);
*desc = apr_psprintf(pool, _("%s, %s %s"),
reason_str, action_str,
- operation_str(conflict->operation));
+ operation_str(
+ svn_client_conflict_get_operation(conflict)));
return SVN_NO_ERROR;
}
svn_error_t *
svn_cl__get_human_readable_tree_conflict_description(
const char **desc,
- const svn_wc_conflict_description2_t *conflict,
+ svn_client_conflict_t *conflict,
apr_pool_t *pool)
{
const char *action, *reason, *operation;
svn_node_kind_t incoming_kind;
+ svn_wc_conflict_action_t conflict_action;
+ svn_wc_conflict_reason_t conflict_reason;
+ svn_wc_operation_t conflict_operation;
+ svn_node_kind_t conflict_node_kind;
+
+ conflict_action = svn_client_conflict_get_incoming_change(conflict);
+ conflict_reason = svn_client_conflict_get_local_change(conflict);
+ conflict_operation = svn_client_conflict_get_operation(conflict);
+ conflict_node_kind = svn_client_conflict_tree_get_victim_node_kind(conflict);
/* Determine the node kind of the incoming change. */
incoming_kind = svn_node_unknown;
- if (conflict->action == svn_wc_conflict_action_edit ||
- conflict->action == svn_wc_conflict_action_delete)
+ if (conflict_action == svn_wc_conflict_action_edit ||
+ conflict_action == svn_wc_conflict_action_delete)
{
/* Change is acting on 'src_left' version of the node. */
- if (conflict->src_left_version)
- incoming_kind = conflict->src_left_version->node_kind;
+ SVN_ERR(svn_client_conflict_get_incoming_old_repos_location(
+ NULL, NULL, &incoming_kind, conflict, pool, pool));
}
- else if (conflict->action == svn_wc_conflict_action_add ||
- conflict->action == svn_wc_conflict_action_replace)
+ else if (conflict_action == svn_wc_conflict_action_add ||
+ conflict_action == svn_wc_conflict_action_replace)
{
/* Change is acting on 'src_right' version of the node.
*
* ### For 'replace', the node kind is ambiguous. However, src_left
* ### is NULL for replace, so we must use src_right. */
- if (conflict->src_right_version)
- incoming_kind = conflict->src_right_version->node_kind;
+ SVN_ERR(svn_client_conflict_get_incoming_new_repos_location(
+ NULL, NULL, &incoming_kind, conflict, pool, pool));
}
- reason = local_reason_str(conflict->node_kind, conflict->reason,
- conflict->operation);
- action = incoming_action_str(incoming_kind, conflict->action);
- operation = operation_str(conflict->operation);
+ reason = local_reason_str(conflict_node_kind, conflict_reason,
+ conflict_operation);
+ action = incoming_action_str(incoming_kind, conflict_action);
+ operation = operation_str(conflict_operation);
SVN_ERR_ASSERT(operation);
if (action && reason)
@@ -326,12 +332,12 @@ svn_cl__get_human_readable_tree_conflict_description(
It will not be pretty, but is closer to an internal error than
an ordinary user-facing string. */
*desc = apr_psprintf(pool, _("local: %s %s incoming: %s %s %s"),
- svn_node_kind_to_word(conflict->node_kind),
+ svn_node_kind_to_word(conflict_node_kind),
svn_token__to_word(map_conflict_reason_xml,
- conflict->reason),
+ conflict_reason),
svn_node_kind_to_word(incoming_kind),
svn_token__to_word(map_conflict_action_xml,
- conflict->action),
+ conflict_action),
operation);
}
return SVN_NO_ERROR;
@@ -360,13 +366,16 @@ svn_cl__get_human_readable_action_description(
/* Helper for svn_cl__append_tree_conflict_info_xml().
- * Appends the attributes of the given VERSION to ATT_HASH.
+ * Appends the repository location of a conflicted node to ATT_HASH.
* SIDE is the content of the version tag's side="..." attribute,
* currently one of "source-left" or "source-right".*/
static svn_error_t *
add_conflict_version_xml(svn_stringbuf_t **pstr,
const char *side,
- const svn_wc_conflict_version_t *version,
+ const char *repos_root_url,
+ const char *repos_relpath,
+ svn_revnum_t peg_rev,
+ svn_node_kind_t node_kind,
apr_pool_t *pool)
{
apr_hash_t *att_hash = apr_hash_make(pool);
@@ -374,18 +383,17 @@ add_conflict_version_xml(svn_stringbuf_t **pstr,
svn_hash_sets(att_hash, "side", side);
- if (version->repos_url)
- svn_hash_sets(att_hash, "repos-url", version->repos_url);
+ if (repos_root_url)
+ svn_hash_sets(att_hash, "repos-url", repos_root_url);
- if (version->path_in_repos)
- svn_hash_sets(att_hash, "path-in-repos", version->path_in_repos);
+ if (repos_relpath)
+ svn_hash_sets(att_hash, "path-in-repos", repos_relpath);
- if (SVN_IS_VALID_REVNUM(version->peg_rev))
- svn_hash_sets(att_hash, "revision", apr_ltoa(pool, version->peg_rev));
+ if (SVN_IS_VALID_REVNUM(peg_rev))
+ svn_hash_sets(att_hash, "revision", apr_ltoa(pool, peg_rev));
- if (version->node_kind != svn_node_unknown)
- svn_hash_sets(att_hash, "kind",
- svn_cl__node_kind_str_xml(version->node_kind));
+ if (node_kind != svn_node_unknown)
+ svn_hash_sets(att_hash, "kind", svn_cl__node_kind_str_xml(node_kind));
svn_xml_make_open_tag_hash(pstr, pool, svn_xml_self_closing,
"version", att_hash);
@@ -395,25 +403,34 @@ add_conflict_version_xml(svn_stringbuf_t **pstr,
static svn_error_t *
append_tree_conflict_info_xml(svn_stringbuf_t *str,
- const svn_wc_conflict_description2_t *conflict,
+ svn_client_conflict_t *conflict,
apr_pool_t *pool)
{
apr_hash_t *att_hash = apr_hash_make(pool);
const char *tmp;
+ const char *repos_root_url;
+ const char *repos_relpath;
+ svn_revnum_t peg_rev;
+ svn_node_kind_t node_kind;
svn_hash_sets(att_hash, "victim",
- svn_dirent_basename(conflict->local_abspath, pool));
+ svn_dirent_basename(
+ svn_client_conflict_get_local_abspath(conflict), pool));
svn_hash_sets(att_hash, "kind",
- svn_cl__node_kind_str_xml(conflict->node_kind));
+ svn_cl__node_kind_str_xml(
+ svn_client_conflict_tree_get_victim_node_kind(conflict)));
svn_hash_sets(att_hash, "operation",
- svn_cl__operation_str_xml(conflict->operation, pool));
+ svn_cl__operation_str_xml(
+ svn_client_conflict_get_operation(conflict), pool));
- tmp = svn_token__to_word(map_conflict_action_xml, conflict->action);
+ tmp = svn_token__to_word(map_conflict_action_xml,
+ svn_client_conflict_get_incoming_change(conflict));
svn_hash_sets(att_hash, "action", tmp);
- tmp = svn_token__to_word(map_conflict_reason_xml, conflict->reason);
+ tmp = svn_token__to_word(map_conflict_reason_xml,
+ svn_client_conflict_get_local_change(conflict));
svn_hash_sets(att_hash, "reason", tmp);
/* Open the tree-conflict tag. */
@@ -422,17 +439,30 @@ append_tree_conflict_info_xml(svn_stringbuf_t *str,
/* Add child tags for OLDER_VERSION and THEIR_VERSION. */
- if (conflict->src_left_version)
- SVN_ERR(add_conflict_version_xml(&str,
- "source-left",
- conflict->src_left_version,
- pool));
-
- if (conflict->src_right_version)
+ SVN_ERR(svn_client_conflict_get_repos_info(&repos_root_url, NULL, conflict,
+ pool, pool));
+ SVN_ERR(svn_client_conflict_get_incoming_old_repos_location(&repos_relpath,
+ &peg_rev,
+ &node_kind,
+ conflict,
+ pool,
+ pool));
+ if (repos_root_url && repos_relpath)
+ SVN_ERR(add_conflict_version_xml(&str, "source-left",
+ repos_root_url, repos_relpath, peg_rev,
+ node_kind, pool));
+
+ SVN_ERR(svn_client_conflict_get_incoming_old_repos_location(&repos_relpath,
+ &peg_rev,
+ &node_kind,
+ conflict,
+ pool,
+ pool));
+ if (repos_root_url && repos_relpath)
SVN_ERR(add_conflict_version_xml(&str,
"source-right",
- conflict->src_right_version,
- pool));
+ repos_root_url, repos_relpath, peg_rev,
+ node_kind, pool));
svn_xml_make_close_tag(&str, pool, "tree-conflict");
@@ -441,78 +471,128 @@ append_tree_conflict_info_xml(svn_stringbuf_t *str,
svn_error_t *
svn_cl__append_conflict_info_xml(svn_stringbuf_t *str,
- const svn_wc_conflict_description2_t *conflict,
+ svn_client_conflict_t *conflict,
apr_pool_t *scratch_pool)
{
apr_hash_t *att_hash;
- const char *kind;
- if (conflict->kind == svn_wc_conflict_kind_tree)
+ svn_boolean_t text_conflicted;
+ apr_array_header_t *props_conflicted;
+ svn_boolean_t tree_conflicted;
+ svn_wc_operation_t conflict_operation;
+ const char *repos_root_url;
+ const char *repos_relpath;
+ svn_revnum_t peg_rev;
+ svn_node_kind_t node_kind;
+
+ conflict_operation = svn_client_conflict_get_operation(conflict);
+
+ SVN_ERR(svn_client_conflict_get_conflicted(&text_conflicted,
+ &props_conflicted,
+ &tree_conflicted,
+ conflict,
+ scratch_pool, scratch_pool));
+ if (tree_conflicted)
{
/* Uses other element type */
return svn_error_trace(
append_tree_conflict_info_xml(str, conflict, scratch_pool));
}
+ SVN_ERR(svn_client_conflict_get_repos_info(&repos_root_url, NULL,
+ conflict,
+ scratch_pool, scratch_pool));
att_hash = apr_hash_make(scratch_pool);
svn_hash_sets(att_hash, "operation",
- svn_cl__operation_str_xml(conflict->operation, scratch_pool));
-
-
- kind = svn_token__to_word(map_conflict_kind_xml, conflict->kind);
- svn_hash_sets(att_hash, "type", kind);
+ svn_cl__operation_str_xml(conflict_operation, scratch_pool));
svn_hash_sets(att_hash, "operation",
- svn_cl__operation_str_xml(conflict->operation, scratch_pool));
-
-
- /* "<conflict>" */
- svn_xml_make_open_tag_hash(&str, scratch_pool,
- svn_xml_normal, "conflict", att_hash);
+ svn_cl__operation_str_xml(conflict_operation, scratch_pool));
- if (conflict->src_left_version)
- SVN_ERR(add_conflict_version_xml(&str,
- "source-left",
- conflict->src_left_version,
- scratch_pool));
-
- if (conflict->src_right_version)
- SVN_ERR(add_conflict_version_xml(&str,
- "source-right",
- conflict->src_right_version,
- scratch_pool));
-
- switch (conflict->kind)
+ if (text_conflicted)
{
- case svn_wc_conflict_kind_text:
- /* "<prev-base-file> xx </prev-base-file>" */
- svn_cl__xml_tagged_cdata(&str, scratch_pool, "prev-base-file",
- conflict->base_abspath);
-
- /* "<prev-wc-file> xx </prev-wc-file>" */
- svn_cl__xml_tagged_cdata(&str, scratch_pool, "prev-wc-file",
- conflict->my_abspath);
-
- /* "<cur-base-file> xx </cur-base-file>" */
- svn_cl__xml_tagged_cdata(&str, scratch_pool, "cur-base-file",
- conflict->their_abspath);
-
- break;
-
- case svn_wc_conflict_kind_property:
- /* "<prop-file> xx </prop-file>" */
- svn_cl__xml_tagged_cdata(&str, scratch_pool, "prop-file",
- conflict->their_abspath);
- break;
-
- default:
- case svn_wc_conflict_kind_tree:
- SVN_ERR_MALFUNCTION(); /* Handled separately */
- break;
+ const char *base_abspath;
+ const char *my_abspath;
+ const char *their_abspath;
+
+ svn_hash_sets(att_hash, "type", "text");
+
+ /* "<conflict>" */
+ svn_xml_make_open_tag_hash(&str, scratch_pool,
+ svn_xml_normal, "conflict", att_hash);
+
+ SVN_ERR(svn_client_conflict_get_incoming_old_repos_location(
+ &repos_relpath, &peg_rev, &node_kind, conflict,
+ scratch_pool, scratch_pool));
+ if (repos_root_url && repos_relpath)
+ SVN_ERR(add_conflict_version_xml(&str, "source-left",
+ repos_root_url, repos_relpath, peg_rev,
+ node_kind, scratch_pool));
+
+ SVN_ERR(svn_client_conflict_get_incoming_old_repos_location(
+ &repos_relpath, &peg_rev, &node_kind, conflict,
+ scratch_pool, scratch_pool));
+ if (repos_root_url && repos_relpath)
+ SVN_ERR(add_conflict_version_xml(&str, "source-right",
+ repos_root_url, repos_relpath, peg_rev,
+ node_kind, scratch_pool));
+
+ SVN_ERR(svn_client_conflict_text_get_contents(NULL, &my_abspath,
+ &base_abspath,
+ &their_abspath,
+ conflict, scratch_pool,
+ scratch_pool));
+ /* "<prev-base-file> xx </prev-base-file>" */
+ svn_cl__xml_tagged_cdata(
+ &str, scratch_pool, "prev-base-file", base_abspath);
+
+ /* "<prev-wc-file> xx </prev-wc-file>" */
+ svn_cl__xml_tagged_cdata(
+ &str, scratch_pool, "prev-wc-file", my_abspath);
+
+ /* "<cur-base-file> xx </cur-base-file>" */
+ svn_cl__xml_tagged_cdata(
+ &str, scratch_pool, "cur-base-file", their_abspath);
+
+ /* "</conflict>" */
+ svn_xml_make_close_tag(&str, scratch_pool, "conflict");
}
- /* "</conflict>" */
- svn_xml_make_close_tag(&str, scratch_pool, "conflict");
+ if (props_conflicted->nelts > 0)
+ {
+ const char *reject_abspath;
+
+ svn_hash_sets(att_hash, "type", "property");
+
+ /* "<conflict>" */
+ svn_xml_make_open_tag_hash(&str, scratch_pool,
+ svn_xml_normal, "conflict", att_hash);
+
+ SVN_ERR(svn_client_conflict_get_incoming_old_repos_location(
+ &repos_relpath, &peg_rev, &node_kind, conflict,
+ scratch_pool, scratch_pool));
+ if (repos_root_url && repos_relpath)
+ SVN_ERR(add_conflict_version_xml(&str, "source-left",
+ repos_root_url, repos_relpath, peg_rev,
+ node_kind, scratch_pool));
+
+ SVN_ERR(svn_client_conflict_get_incoming_old_repos_location(
+ &repos_relpath, &peg_rev, &node_kind, conflict,
+ scratch_pool, scratch_pool));
+ if (repos_root_url && repos_relpath)
+ SVN_ERR(add_conflict_version_xml(&str, "source-right",
+ repos_root_url, repos_relpath, peg_rev,
+ node_kind, scratch_pool));
+
+ /* "<prop-file> xx </prop-file>" */
+ reject_abspath =
+ svn_client_conflict_prop_get_reject_abspath(conflict);
+ svn_cl__xml_tagged_cdata(
+ &str, scratch_pool, "prop-file", reject_abspath);
+
+ /* "</conflict>" */
+ svn_xml_make_close_tag(&str, scratch_pool, "conflict");
+ }
return SVN_NO_ERROR;
}
diff --git a/subversion/svn/cl-conflicts.h b/subversion/svn/cl-conflicts.h
index d4880748cd9c..bcc6c1b520f5 100644
--- a/subversion/svn/cl-conflicts.h
+++ b/subversion/svn/cl-conflicts.h
@@ -31,6 +31,7 @@
#include "svn_types.h"
#include "svn_string.h"
+#include "svn_client.h"
#include "svn_wc.h"
#ifdef __cplusplus
@@ -48,7 +49,7 @@ extern "C" {
svn_error_t *
svn_cl__get_human_readable_prop_conflict_description(
const char **desc,
- const svn_wc_conflict_description2_t *conflict,
+ svn_client_conflict_t *conflict,
apr_pool_t *pool);
/**
@@ -60,7 +61,7 @@ svn_cl__get_human_readable_prop_conflict_description(
svn_error_t *
svn_cl__get_human_readable_tree_conflict_description(
const char **desc,
- const svn_wc_conflict_description2_t *conflict,
+ svn_client_conflict_t *conflict,
apr_pool_t *pool);
/* Like svn_cl__get_human_readable_tree_conflict_description but
@@ -80,7 +81,7 @@ svn_cl__get_human_readable_action_description(
svn_error_t *
svn_cl__append_conflict_info_xml(
svn_stringbuf_t *str,
- const svn_wc_conflict_description2_t *conflict,
+ svn_client_conflict_t *conflict,
apr_pool_t *pool);
#ifdef __cplusplus
diff --git a/subversion/svn/cl-log.h b/subversion/svn/cl-log.h
index 5b4c7aa7a68b..e2d5646351a6 100644
--- a/subversion/svn/cl-log.h
+++ b/subversion/svn/cl-log.h
@@ -31,6 +31,8 @@
#include "svn_types.h"
+#include "private/svn_string_private.h"
+
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
@@ -70,6 +72,9 @@ typedef struct svn_cl__log_receiver_baton
* the log message, or a changed path matches one of these patterns. */
apr_array_header_t *search_patterns;
+ /* Buffer for Unicode normalization and case folding. */
+ svn_membuf_t buffer;
+
/* Pool for persistent allocations. */
apr_pool_t *pool;
} svn_cl__log_receiver_baton;
diff --git a/subversion/svn/cl.h b/subversion/svn/cl.h
index 42e770e075c5..a5b1d9b948ae 100644
--- a/subversion/svn/cl.h
+++ b/subversion/svn/cl.h
@@ -83,7 +83,10 @@ typedef enum svn_cl__accept_t
svn_cl__accept_edit,
/* Launch user's resolver and resolve conflict with edited file. */
- svn_cl__accept_launch
+ svn_cl__accept_launch,
+
+ /* Use recommended resolution if available, else leave the conflict alone. */
+ svn_cl__accept_recommended
} svn_cl__accept_t;
@@ -97,6 +100,7 @@ typedef enum svn_cl__accept_t
#define SVN_CL__ACCEPT_THEIRS_FULL "theirs-full"
#define SVN_CL__ACCEPT_EDIT "edit"
#define SVN_CL__ACCEPT_LAUNCH "launch"
+#define SVN_CL__ACCEPT_RECOMMENDED "recommended"
/* Return the svn_cl__accept_t value corresponding to WORD, using exact
* case-sensitive string comparison. Return svn_cl__accept_invalid if WORD
@@ -174,6 +178,7 @@ typedef struct svn_cl__opt_state_t
svn_boolean_t help; /* print usage message */
const char *auth_username; /* auth username */
const char *auth_password; /* auth password */
+ svn_boolean_t auth_password_from_stdin; /* read password from stdin */
const char *extensions; /* subprocess extension args */
apr_array_header_t *targets; /* target list from file */
svn_boolean_t xml; /* output in xml, e.g., "svn log --xml" */
@@ -249,12 +254,18 @@ typedef struct svn_cl__opt_state_t
svn_boolean_t show_passwords; /* show cached passwords */
svn_boolean_t pin_externals; /* pin externals to last-changed revisions */
const char *show_item; /* print only the given item */
+ svn_boolean_t adds_as_modification; /* update 'add vs add' no tree conflict */
+ svn_boolean_t vacuum_pristines; /* remove unreferenced pristines */
+ svn_boolean_t list;
} svn_cl__opt_state_t;
+/* Conflict stats for operations such as update and merge. */
+typedef struct svn_cl__conflict_stats_t svn_cl__conflict_stats_t;
typedef struct svn_cl__cmd_baton_t
{
svn_cl__opt_state_t *opt_state;
+ svn_cl__conflict_stats_t *conflict_stats;
svn_client_ctx_t *ctx;
} svn_cl__cmd_baton_t;
@@ -293,6 +304,9 @@ svn_opt_subcommand_t
svn_cl__revert,
svn_cl__resolve,
svn_cl__resolved,
+ svn_cl__shelve,
+ svn_cl__unshelve,
+ svn_cl__shelves,
svn_cl__status,
svn_cl__switch,
svn_cl__unlock,
@@ -335,8 +349,7 @@ svn_cl__try(svn_error_t *err,
/* Our cancellation callback. */
-svn_error_t *
-svn_cl__check_cancel(void *baton);
+extern svn_cancel_func_t svn_cl__check_cancel;
@@ -346,9 +359,6 @@ svn_cl__check_cancel(void *baton);
typedef struct svn_cl__interactive_conflict_baton_t
svn_cl__interactive_conflict_baton_t;
-/* Conflict stats for operations such as update and merge. */
-typedef struct svn_cl__conflict_stats_t svn_cl__conflict_stats_t;
-
/* Return a new, initialized, conflict stats structure, allocated in
* POOL. */
svn_cl__conflict_stats_t *
@@ -362,6 +372,14 @@ svn_cl__conflict_stats_resolved(svn_cl__conflict_stats_t *conflict_stats,
const char *path_local,
svn_wc_conflict_kind_t conflict_kind);
+/* Set *CONFLICTED_PATHS to the conflicted paths contained in CONFLICT_STATS.
+ * If no conflicted path exists, set *CONFLICTED_PATHS to NULL. */
+svn_error_t *
+svn_cl__conflict_stats_get_paths(apr_array_header_t **conflicted_paths,
+ svn_cl__conflict_stats_t *conflict_stats,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool);
+
/* Print the conflict stats accumulated in CONFLICT_STATS.
*
* Return any error encountered during printing.
@@ -371,36 +389,33 @@ svn_error_t *
svn_cl__print_conflict_stats(svn_cl__conflict_stats_t *conflict_stats,
apr_pool_t *scratch_pool);
-/* Create and return an baton for use with svn_cl__conflict_func_interactive
- * in *B, allocated from RESULT_POOL, and initialised with the values
- * ACCEPT_WHICH, CONFIG, EDITOR_CMD, CANCEL_FUNC and CANCEL_BATON. */
-svn_error_t *
-svn_cl__get_conflict_func_interactive_baton(
- svn_cl__interactive_conflict_baton_t **b,
- svn_cl__accept_t accept_which,
- apr_hash_t *config,
- const char *editor_cmd,
- svn_cl__conflict_stats_t *conflict_stats,
- svn_cancel_func_t cancel_func,
- void *cancel_baton,
- apr_pool_t *result_pool);
-
-/* A callback capable of doing interactive conflict resolution.
-
- The BATON must come from svn_cl__get_conflict_func_interactive_baton().
- Resolves based on the --accept option if one was given to that function,
- otherwise prompts the user to choose one of the three fulltexts, edit
- the merged file on the spot, or just skip the conflict (to be resolved
- later), among other options.
-
- Implements svn_wc_conflict_resolver_func2_t.
+/*
+ * Interactively resolve the conflict a @a CONFLICT.
+ * TODO: more docs
+ */
+svn_error_t *
+svn_cl__resolve_conflict(svn_boolean_t *quit,
+ svn_boolean_t *external_failed,
+ svn_boolean_t *printed_summary,
+ svn_client_conflict_t *conflict,
+ svn_cl__accept_t accept_which,
+ const char *editor_cmd,
+ const char *path_prefix,
+ svn_cmdline_prompt_baton_t *pb,
+ svn_cl__conflict_stats_t *conflict_stats,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool);
+
+/*
+ * Interactively resolve conflicts for all TARGETS.
+ * TODO: more docs
*/
svn_error_t *
-svn_cl__conflict_func_interactive(svn_wc_conflict_result_t **result,
- const svn_wc_conflict_description2_t *desc,
- void *baton,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool);
+svn_cl__walk_conflicts(apr_array_header_t *targets,
+ svn_cl__conflict_stats_t *conflict_stats,
+ svn_cl__opt_state_t *opt_state,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool);
/*** Command-line output functions -- printing to the user. ***/
@@ -777,15 +792,18 @@ svn_cl__args_to_target_array_print_reserved(apr_array_header_t **targets_p,
svn_boolean_t keep_dest_origpath_on_truepath_collision,
apr_pool_t *pool);
-/* Return a string showing NODE's kind, URL and revision, to the extent that
- * that information is available in NODE. If NODE itself is NULL, this prints
- * just a 'none' node kind.
+/* Return a string showing a conflicted node's kind, URL and revision,
+ * to the extent that that information is available. If REPOS_ROOT_URL or
+ * REPOS_RELPATH are NULL, this prints just a 'none' node kind.
* WC_REPOS_ROOT_URL should reflect the target working copy's repository
- * root URL. If NODE is from that same URL, the printed URL is abbreviated
+ * root URL. If the node is from that same URL, the printed URL is abbreviated
* to caret notation (^/). WC_REPOS_ROOT_URL may be NULL, in which case
* this function tries to print the conflicted node's complete URL. */
const char *
-svn_cl__node_description(const svn_wc_conflict_version_t *node,
+svn_cl__node_description(const char *repos_root_url,
+ const char *repos_relpath,
+ svn_revnum_t peg_rev,
+ svn_node_kind_t node_kind,
const char *wc_repos_root_URL,
apr_pool_t *pool);
diff --git a/subversion/svn/cleanup-cmd.c b/subversion/svn/cleanup-cmd.c
index 6b0b62efe930..516b933f68f4 100644
--- a/subversion/svn/cleanup-cmd.c
+++ b/subversion/svn/cleanup-cmd.c
@@ -72,13 +72,14 @@ svn_cl__cleanup(apr_getopt_t *os,
SVN_ERR(svn_dirent_get_absolute(&target_abspath, target, iterpool));
- if (opt_state->remove_unversioned || opt_state->remove_ignored)
+ if (opt_state->remove_unversioned || opt_state->remove_ignored ||
+ opt_state->vacuum_pristines)
{
svn_error_t *err = svn_client_vacuum(target_abspath,
opt_state->remove_unversioned,
opt_state->remove_ignored,
TRUE /* fix_timestamps */,
- FALSE /* vacuum_pristines */,
+ opt_state->vacuum_pristines,
opt_state->include_externals,
ctx, iterpool);
diff --git a/subversion/svn/conflict-callbacks.c b/subversion/svn/conflict-callbacks.c
index a9cb39a2ade1..278fffcdb882 100644
--- a/subversion/svn/conflict-callbacks.c
+++ b/subversion/svn/conflict-callbacks.c
@@ -44,53 +44,9 @@
#include "svn_private_config.h"
#define ARRAY_LEN(ary) ((sizeof (ary)) / (sizeof ((ary)[0])))
-#define MAX_ARRAY_LEN(aryx, aryz) \
- (ARRAY_LEN((aryx)) > ARRAY_LEN((aryz)) \
- ? ARRAY_LEN((aryx)) : ARRAY_LEN((aryz)))
-struct svn_cl__interactive_conflict_baton_t {
- svn_cl__accept_t accept_which;
- apr_hash_t *config;
- const char *editor_cmd;
- svn_boolean_t external_failed;
- svn_cmdline_prompt_baton_t *pb;
- const char *path_prefix;
- svn_boolean_t quit;
- svn_cl__conflict_stats_t *conflict_stats;
- svn_boolean_t printed_summary;
-};
-
-svn_error_t *
-svn_cl__get_conflict_func_interactive_baton(
- svn_cl__interactive_conflict_baton_t **b,
- svn_cl__accept_t accept_which,
- apr_hash_t *config,
- const char *editor_cmd,
- svn_cl__conflict_stats_t *conflict_stats,
- svn_cancel_func_t cancel_func,
- void *cancel_baton,
- apr_pool_t *result_pool)
-{
- svn_cmdline_prompt_baton_t *pb = apr_palloc(result_pool, sizeof(*pb));
- pb->cancel_func = cancel_func;
- pb->cancel_baton = cancel_baton;
-
- *b = apr_palloc(result_pool, sizeof(**b));
- (*b)->accept_which = accept_which;
- (*b)->config = config;
- (*b)->editor_cmd = editor_cmd;
- (*b)->external_failed = FALSE;
- (*b)->pb = pb;
- SVN_ERR(svn_dirent_get_absolute(&(*b)->path_prefix, "", result_pool));
- (*b)->quit = FALSE;
- (*b)->conflict_stats = conflict_stats;
- (*b)->printed_summary = FALSE;
-
- return SVN_NO_ERROR;
-}
-
svn_cl__accept_t
svn_cl__accept_from_word(const char *word)
{
@@ -122,15 +78,19 @@ svn_cl__accept_from_word(const char *word)
if (strcmp(word, SVN_CL__ACCEPT_LAUNCH) == 0
|| strcmp(word, "l") == 0 || strcmp(word, ":-l") == 0)
return svn_cl__accept_launch;
+ if (strcmp(word, SVN_CL__ACCEPT_RECOMMENDED) == 0
+ || strcmp(word, "r") == 0)
+ return svn_cl__accept_recommended;
/* word is an invalid action. */
return svn_cl__accept_invalid;
}
/* Print on stdout a diff that shows incoming conflicting changes
- * corresponding to the conflict described in DESC. */
+ * corresponding to the conflict described in CONFLICT. */
static svn_error_t *
-show_diff(const svn_wc_conflict_description2_t *desc,
+show_diff(svn_client_conflict_t *conflict,
+ const char *merged_abspath,
const char *path_prefix,
svn_cancel_func_t cancel_func,
void *cancel_baton,
@@ -141,8 +101,13 @@ show_diff(const svn_wc_conflict_description2_t *desc,
svn_diff_t *diff;
svn_stream_t *output;
svn_diff_file_options_t *options;
+ const char *my_abspath;
+ const char *their_abspath;
- if (desc->merged_file)
+ SVN_ERR(svn_client_conflict_text_get_contents(NULL, &my_abspath, NULL,
+ &their_abspath,
+ conflict, pool, pool));
+ if (merged_abspath)
{
/* For conflicts recorded by the 'merge' operation, show a diff between
* 'mine' (the working version of the file as it appeared before the
@@ -150,32 +115,32 @@ show_diff(const svn_wc_conflict_description2_t *desc,
* as it appears after the merge operation).
*
* For conflicts recorded by the 'update' and 'switch' operations,
- * show a diff beween 'theirs' (the new pristine version of the
+ * show a diff between 'theirs' (the new pristine version of the
* file) and 'merged' (the version of the file as it appears with
* local changes merged with the new pristine version).
*
* This way, the diff is always minimal and clearly identifies changes
* brought into the working copy by the update/switch/merge operation. */
- if (desc->operation == svn_wc_operation_merge)
+ if (svn_client_conflict_get_operation(conflict) == svn_wc_operation_merge)
{
- path1 = desc->my_abspath;
+ path1 = my_abspath;
label1 = _("MINE");
}
else
{
- path1 = desc->their_abspath;
+ path1 = their_abspath;
label1 = _("THEIRS");
}
- path2 = desc->merged_file;
+ path2 = merged_abspath;
label2 = _("MERGED");
}
else
{
/* There's no merged file, but we can show the
difference between mine and theirs. */
- path1 = desc->their_abspath;
+ path1 = their_abspath;
label1 = _("THEIRS");
- path2 = desc->my_abspath;
+ path2 = my_abspath;
label2 = _("MINE");
}
@@ -204,9 +169,9 @@ show_diff(const svn_wc_conflict_description2_t *desc,
/* Print on stdout just the conflict hunks of a diff among the 'base', 'their'
- * and 'my' files of DESC. */
+ * and 'my' files of CONFLICT. */
static svn_error_t *
-show_conflicts(const svn_wc_conflict_description2_t *desc,
+show_conflicts(svn_client_conflict_t *conflict,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
@@ -214,89 +179,78 @@ show_conflicts(const svn_wc_conflict_description2_t *desc,
svn_diff_t *diff;
svn_stream_t *output;
svn_diff_file_options_t *options;
+ const char *base_abspath;
+ const char *my_abspath;
+ const char *their_abspath;
+ SVN_ERR(svn_client_conflict_text_get_contents(NULL, &my_abspath,
+ &base_abspath, &their_abspath,
+ conflict, pool, pool));
options = svn_diff_file_options_create(pool);
options->ignore_eol_style = TRUE;
SVN_ERR(svn_stream_for_stdout(&output, pool));
- SVN_ERR(svn_diff_file_diff3_2(&diff,
- desc->base_abspath,
- desc->my_abspath,
- desc->their_abspath,
+ SVN_ERR(svn_diff_file_diff3_2(&diff, base_abspath, my_abspath, their_abspath,
options, pool));
/* ### Consider putting the markers/labels from
### svn_wc__merge_internal in the conflict description. */
- return svn_diff_file_output_merge3(output, diff,
- desc->base_abspath,
- desc->my_abspath,
- desc->their_abspath,
- _("||||||| ORIGINAL"),
- _("<<<<<<< MINE (select with 'mc')"),
- _(">>>>>>> THEIRS (select with 'tc')"),
- "=======",
- svn_diff_conflict_display_only_conflicts,
- cancel_func,
- cancel_baton,
- pool);
+ return svn_diff_file_output_merge3(
+ output, diff, base_abspath, my_abspath, their_abspath,
+ _("||||||| ORIGINAL"),
+ _("<<<<<<< MINE (select with 'mc')"),
+ _(">>>>>>> THEIRS (select with 'tc')"),
+ "=======",
+ svn_diff_conflict_display_only_conflicts,
+ cancel_func,
+ cancel_baton,
+ pool);
}
/* Perform a 3-way merge of the conflicting values of a property,
* and write the result to the OUTPUT stream.
*
- * If MERGED_ABSPATH is non-NULL, use it as 'my' version instead of
- * DESC->MY_ABSPATH.
+ * If MERGED_PROPVAL is non-NULL, use it as 'my' version instead of
+ * MY_ABSPATH.
*
* Assume the values are printable UTF-8 text.
*/
static svn_error_t *
merge_prop_conflict(svn_stream_t *output,
- const svn_wc_conflict_description2_t *desc,
- const char *merged_abspath,
+ const svn_string_t *base_propval,
+ const svn_string_t *my_propval,
+ const svn_string_t *their_propval,
+ const svn_string_t *merged_propval,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
{
- const char *base_abspath = desc->base_abspath;
- const char *my_abspath = desc->my_abspath;
- const char *their_abspath = desc->their_abspath;
svn_diff_file_options_t *options = svn_diff_file_options_create(pool);
svn_diff_t *diff;
- /* If any of the property values is missing, use an empty file instead
+ /* If any of the property values is missing, use an empty value instead
* for the purpose of showing a diff. */
- if (! base_abspath || ! my_abspath || ! their_abspath)
- {
- const char *empty_file;
-
- SVN_ERR(svn_io_open_unique_file3(NULL, &empty_file,
- NULL, svn_io_file_del_on_pool_cleanup,
- pool, pool));
- if (! base_abspath)
- base_abspath = empty_file;
- if (! my_abspath)
- my_abspath = empty_file;
- if (! their_abspath)
- their_abspath = empty_file;
- }
-
+ if (base_propval == NULL)
+ base_propval = svn_string_create_empty(pool);
+ if (my_propval == NULL)
+ my_propval = svn_string_create_empty(pool);
+ if (their_propval == NULL)
+ their_propval = svn_string_create_empty(pool);
+
options->ignore_eol_style = TRUE;
- SVN_ERR(svn_diff_file_diff3_2(&diff,
- base_abspath,
- merged_abspath ? merged_abspath : my_abspath,
- their_abspath,
- options, pool));
- SVN_ERR(svn_diff_file_output_merge3(output, diff,
- base_abspath,
- merged_abspath ? merged_abspath
- : my_abspath,
- their_abspath,
- _("||||||| ORIGINAL"),
- _("<<<<<<< MINE"),
- _(">>>>>>> THEIRS"),
- "=======",
- svn_diff_conflict_display_modified_original_latest,
- cancel_func,
- cancel_baton,
- pool));
+ SVN_ERR(svn_diff_mem_string_diff3(&diff, base_propval,
+ merged_propval ?
+ merged_propval : my_propval,
+ their_propval, options, pool));
+ SVN_ERR(svn_diff_mem_string_output_merge3(
+ output, diff, base_propval,
+ merged_propval ? merged_propval : my_propval, their_propval,
+ _("||||||| ORIGINAL"),
+ _("<<<<<<< MINE"),
+ _(">>>>>>> THEIRS"),
+ "=======",
+ svn_diff_conflict_display_modified_original_latest,
+ cancel_func,
+ cancel_baton,
+ pool));
return SVN_NO_ERROR;
}
@@ -309,8 +263,10 @@ merge_prop_conflict(svn_stream_t *output,
* Assume the values are printable UTF-8 text.
*/
static svn_error_t *
-show_prop_conflict(const svn_wc_conflict_description2_t *desc,
- const char *merged_abspath,
+show_prop_conflict(const svn_string_t *base_propval,
+ const svn_string_t *my_propval,
+ const svn_string_t *their_propval,
+ const svn_string_t *merged_propval,
svn_cancel_func_t cancel_func,
void *cancel_baton,
apr_pool_t *pool)
@@ -318,13 +274,13 @@ show_prop_conflict(const svn_wc_conflict_description2_t *desc,
svn_stream_t *output;
SVN_ERR(svn_stream_for_stdout(&output, pool));
- SVN_ERR(merge_prop_conflict(output, desc, merged_abspath,
- cancel_func, cancel_baton, pool));
+ SVN_ERR(merge_prop_conflict(output, base_propval, my_propval, their_propval,
+ merged_propval, cancel_func, cancel_baton, pool));
return SVN_NO_ERROR;
}
-/* Run an external editor, passing it the MERGED_FILE, or, if the
+/* Run an external editor, passing it the MERGED_ABSPATH, or, if the
* 'merged' file is null, return an error. The tool to use is determined by
* B->editor_cmd, B->config and environment variables; see
* svn_cl__edit_file_externally() for details.
@@ -335,16 +291,17 @@ show_prop_conflict(const svn_wc_conflict_description2_t *desc,
* return that error. */
static svn_error_t *
open_editor(svn_boolean_t *performed_edit,
- const char *merged_file,
- svn_cl__interactive_conflict_baton_t *b,
+ const char *merged_abspath,
+ const char *editor_cmd,
+ apr_hash_t *config,
apr_pool_t *pool)
{
svn_error_t *err;
- if (merged_file)
+ if (merged_abspath)
{
- err = svn_cmdline__edit_file_externally(merged_file, b->editor_cmd,
- b->config, pool);
+ err = svn_cmdline__edit_file_externally(merged_abspath, editor_cmd,
+ config, pool);
if (err && (err->apr_err == SVN_ERR_CL_NO_EXTERNAL_EDITOR ||
err->apr_err == SVN_ERR_EXTERNAL_PROGRAM))
{
@@ -368,34 +325,44 @@ open_editor(svn_boolean_t *performed_edit,
return SVN_NO_ERROR;
}
-/* Run an external editor, passing it the 'merged' property in DESC.
+/* Run an external editor on the merged property value with conflict markers.
+ * Return the edited result in *MERGED_PROPVAL.
+ * If the edit is aborted, set *MERGED_ABSPATH and *MERGED_PROPVAL to NULL.
* The tool to use is determined by B->editor_cmd, B->config and
* environment variables; see svn_cl__edit_file_externally() for details. */
static svn_error_t *
-edit_prop_conflict(const char **merged_file_path,
- const svn_wc_conflict_description2_t *desc,
- svn_cl__interactive_conflict_baton_t *b,
+edit_prop_conflict(const svn_string_t **merged_propval,
+ const svn_string_t *base_propval,
+ const svn_string_t *my_propval,
+ const svn_string_t *their_propval,
+ const char *editor_cmd,
+ apr_hash_t *config,
+ svn_cmdline_prompt_baton_t *pb,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
- apr_file_t *file;
const char *file_path;
svn_boolean_t performed_edit = FALSE;
svn_stream_t *merged_prop;
- SVN_ERR(svn_io_open_unique_file3(&file, &file_path, NULL,
- svn_io_file_del_on_pool_cleanup,
- result_pool, scratch_pool));
- merged_prop = svn_stream_from_aprfile2(file, TRUE /* disown */,
- scratch_pool);
- SVN_ERR(merge_prop_conflict(merged_prop, desc, NULL,
- b->pb->cancel_func,
- b->pb->cancel_baton,
+ SVN_ERR(svn_stream_open_unique(&merged_prop, &file_path, NULL,
+ svn_io_file_del_on_pool_cleanup,
+ scratch_pool, scratch_pool));
+ SVN_ERR(merge_prop_conflict(merged_prop, base_propval, my_propval,
+ their_propval, NULL,
+ pb->cancel_func,
+ pb->cancel_baton,
scratch_pool));
SVN_ERR(svn_stream_close(merged_prop));
- SVN_ERR(svn_io_file_flush(file, scratch_pool));
- SVN_ERR(open_editor(&performed_edit, file_path, b, scratch_pool));
- *merged_file_path = (performed_edit ? file_path : NULL);
+ SVN_ERR(open_editor(&performed_edit, file_path, editor_cmd,
+ config, scratch_pool));
+ if (performed_edit && merged_propval)
+ {
+ svn_stringbuf_t *buf;
+
+ SVN_ERR(svn_stringbuf_from_file2(&buf, file_path, scratch_pool));
+ *merged_propval = svn_string_create_from_buf(buf, result_pool);
+ }
return SVN_NO_ERROR;
}
@@ -403,169 +370,166 @@ edit_prop_conflict(const char **merged_file_path,
/* Maximum line length for the prompt string. */
#define MAX_PROMPT_WIDTH 70
-/* Description of a resolver option */
+/* Description of a resolver option.
+ * Resolver options are used to build the resolver's conflict prompt.
+ * The user types a code to select the corresponding conflict resolution option.
+ * Some resolver options have a corresponding --accept argument. */
typedef struct resolver_option_t
{
const char *code; /* one or two characters */
- const char *short_desc; /* label in prompt (localized) */
- const char *long_desc; /* longer description (localized) */
- svn_wc_conflict_choice_t choice;
- /* or ..._undefined if not a simple choice */
+ svn_client_conflict_option_id_t choice;
+ /* or ..._undefined if not from libsvn_client */
+ const char *accept_arg; /* --accept option argument (NOT localized) */
} resolver_option_t;
-/* Resolver options for a text conflict */
-/* (opt->code == "" causes a blank line break in help_string()) */
-static const resolver_option_t text_conflict_options[] =
+typedef struct client_option_t
{
- /* Translators: keep long_desc below 70 characters (wrap with a left
- margin of 9 spaces if needed); don't translate the words within square
- brackets. */
- { "e", N_("edit file"), N_("change merged file in an editor"
- " [edit]"),
- svn_wc_conflict_choose_undefined },
- { "df", N_("show diff"), N_("show all changes made to merged file"),
- svn_wc_conflict_choose_undefined },
- { "r", N_("mark resolved"), N_("accept merged version of file [working]"),
- svn_wc_conflict_choose_merged },
- { "", "", "", svn_wc_conflict_choose_unspecified },
- { "dc", N_("display conflict"), N_("show all conflicts "
- "(ignoring merged version)"),
- svn_wc_conflict_choose_undefined },
- { "mc", N_("my side of conflict"), N_("accept my version for all conflicts "
- "(same) [mine-conflict]"),
- svn_wc_conflict_choose_mine_conflict },
- { "tc", N_("their side of conflict"), N_("accept their version for all "
- "conflicts (same)"
- " [theirs-conflict]"),
- svn_wc_conflict_choose_theirs_conflict },
- { "", "", "", svn_wc_conflict_choose_unspecified },
- { "mf", N_("my version"), N_("accept my version of entire file (even "
- "non-conflicts) [mine-full]"),
- svn_wc_conflict_choose_mine_full },
- { "tf", N_("their version"), N_("accept their version of entire file "
- "(same) [theirs-full]"),
- svn_wc_conflict_choose_theirs_full },
- { "", "", "", svn_wc_conflict_choose_unspecified },
- { "m", N_("merge"), N_("use merge tool to resolve conflict"),
- svn_wc_conflict_choose_undefined },
- { "l", N_("launch tool"), N_("launch external merge tool to resolve "
- "conflict [launch]"),
- svn_wc_conflict_choose_undefined },
- { "i", N_("internal merge tool"), N_("use built-in merge tool to "
- "resolve conflict"),
- svn_wc_conflict_choose_undefined },
- { "p", N_("postpone"), N_("mark the conflict to be resolved later"
- " [postpone]"),
- svn_wc_conflict_choose_postpone },
- { "q", N_("quit resolution"), N_("postpone all remaining conflicts"),
- svn_wc_conflict_choose_postpone },
- { "s", N_("show all options"), N_("show this list (also 'h', '?')"),
- svn_wc_conflict_choose_undefined },
+ const char *code; /* one or two characters */
+ const char *label; /* label in prompt (localized) */
+ const char *long_desc; /* longer description (localized) */
+ svn_client_conflict_option_id_t choice;
+ /* or ..._undefined if not from libsvn_client */
+ const char *accept_arg; /* --accept option argument (NOT localized) */
+ svn_boolean_t is_recommended; /* if TRUE, try this option before prompting */
+} client_option_t;
+
+/* Resolver options for conflict options offered by libsvn_client. */
+static const resolver_option_t builtin_resolver_options[] =
+{
+ { "r", svn_client_conflict_option_merged_text,
+ SVN_CL__ACCEPT_WORKING },
+ { "mc", svn_client_conflict_option_working_text_where_conflicted,
+ SVN_CL__ACCEPT_MINE_CONFLICT },
+ { "tc", svn_client_conflict_option_incoming_text_where_conflicted,
+ SVN_CL__ACCEPT_THEIRS_CONFLICT },
+ { "mf", svn_client_conflict_option_working_text,
+ SVN_CL__ACCEPT_MINE_FULL},
+ { "tf", svn_client_conflict_option_incoming_text,
+ SVN_CL__ACCEPT_THEIRS_FULL },
+ { "p", svn_client_conflict_option_postpone,
+ SVN_CL__ACCEPT_POSTPONE },
+
+ /* This option resolves a tree conflict to the current working copy state. */
+ { "r", svn_client_conflict_option_accept_current_wc_state,
+ SVN_CL__ACCEPT_WORKING },
+
+ /* These options use the same code since they only occur in
+ * distinct conflict scenarios. */
+ { "u", svn_client_conflict_option_update_move_destination },
+ { "u", svn_client_conflict_option_update_any_moved_away_children },
+
+ /* Options for incoming add vs local add. */
+ { "i", svn_client_conflict_option_incoming_add_ignore },
+
+ /* Options for incoming file add vs local file add upon merge. */
+ { "m", svn_client_conflict_option_incoming_added_file_text_merge },
+ { "M", svn_client_conflict_option_incoming_added_file_replace_and_merge },
+
+ /* Options for incoming dir add vs local dir add upon merge. */
+ { "m", svn_client_conflict_option_incoming_added_dir_merge },
+ { "R", svn_client_conflict_option_incoming_added_dir_replace },
+ { "M", svn_client_conflict_option_incoming_added_dir_replace_and_merge },
+
+ /* Options for incoming delete vs any. */
+ { "i", svn_client_conflict_option_incoming_delete_ignore },
+ { "a", svn_client_conflict_option_incoming_delete_accept },
+
+ /* Options for incoming move vs local edit. */
+ { "m", svn_client_conflict_option_incoming_move_file_text_merge },
+ { "m", svn_client_conflict_option_incoming_move_dir_merge },
+
+ /* Options for local move vs incoming edit. */
+ { "m", svn_client_conflict_option_local_move_file_text_merge },
+
{ NULL }
};
-/* Resolver options for a binary file conflict. */
-static const resolver_option_t binary_conflict_options[] =
+/* Extra resolver options offered by 'svn' for any conflict. */
+static const client_option_t extra_resolver_options[] =
{
/* Translators: keep long_desc below 70 characters (wrap with a left
- margin of 9 spaces if needed); don't translate the words within square
- brackets. */
- { "r", N_("mark resolved"), N_("accept the working copy version of file "
- " [working]"),
- svn_wc_conflict_choose_merged },
- { "tf", N_("their version"), N_("accept the incoming version of file "
- " [theirs-full]"),
- svn_wc_conflict_choose_theirs_full },
- { "p", N_("postpone"), N_("mark the conflict to be resolved later "
- " [postpone]"),
- svn_wc_conflict_choose_postpone },
- { "q", N_("quit resolution"), N_("postpone all remaining conflicts"),
- svn_wc_conflict_choose_postpone },
- { "s", N_("show all options"), N_("show this list (also 'h', '?')"),
- svn_wc_conflict_choose_undefined },
+ margin of 9 spaces if needed) */
+ { "q", N_("Quit resolution"), N_("postpone all remaining conflicts"),
+ svn_client_conflict_option_postpone },
{ NULL }
};
-/* Resolver options for a property conflict */
-static const resolver_option_t prop_conflict_options[] =
-{
- { "mf", N_("my version"), N_("accept my version of entire property (even "
- "non-conflicts) [mine-full]"),
- svn_wc_conflict_choose_mine_full },
- { "tf", N_("their version"), N_("accept their version of entire property "
- "(same) [theirs-full]"),
- svn_wc_conflict_choose_theirs_full },
- { "dc", N_("display conflict"), N_("show conflicts in this property"),
- svn_wc_conflict_choose_undefined },
- { "e", N_("edit property"), N_("change merged property value in an editor"
- " [edit]"),
- svn_wc_conflict_choose_undefined },
- { "r", N_("mark resolved"), N_("accept edited version of property"),
- svn_wc_conflict_choose_merged },
- { "p", N_("postpone"), N_("mark the conflict to be resolved later"
- " [postpone]"),
- svn_wc_conflict_choose_postpone },
- { "q", N_("quit resolution"), N_("postpone all remaining conflicts"),
- svn_wc_conflict_choose_postpone },
- { "h", N_("help"), N_("show this help (also '?')"),
- svn_wc_conflict_choose_undefined },
- { NULL }
-};
-/* Resolver options for a tree conflict */
-static const resolver_option_t tree_conflict_options[] =
+/* Additional resolver options offered by 'svn' for a text conflict. */
+static const client_option_t extra_resolver_options_text[] =
{
- { "r", N_("mark resolved"), N_("accept current working copy state"),
- svn_wc_conflict_choose_merged },
- { "p", N_("postpone"), N_("resolve the conflict later [postpone]"),
- svn_wc_conflict_choose_postpone },
- { "q", N_("quit resolution"), N_("postpone all remaining conflicts"),
- svn_wc_conflict_choose_postpone },
- { "h", N_("help"), N_("show this help (also '?')"),
- svn_wc_conflict_choose_undefined },
+ /* Translators: keep long_desc below 70 characters (wrap with a left
+ margin of 9 spaces if needed) */
+ { "e", N_("Edit file"), N_("change merged file in an editor"),
+ svn_client_conflict_option_undefined,
+ SVN_CL__ACCEPT_EDIT },
+ { "df", N_("Show diff"), N_("show all changes made to merged file"),
+ svn_client_conflict_option_undefined},
+ { "dc", N_("Display conflict"), N_("show all conflicts "
+ "(ignoring merged version)"),
+ svn_client_conflict_option_undefined },
+ { "m", N_("Merge"), N_("use merge tool to resolve conflict"),
+ svn_client_conflict_option_undefined },
+ { "l", N_("Launch tool"), N_("launch external merge tool to resolve "
+ "conflict"),
+ svn_client_conflict_option_undefined,
+ SVN_CL__ACCEPT_LAUNCH },
+ { "i", N_("Internal merge tool"), N_("use built-in merge tool to "
+ "resolve conflict"),
+ svn_client_conflict_option_undefined },
+ { "s", N_("Show all options"), N_("show this list (also 'h', '?')"),
+ svn_client_conflict_option_undefined },
{ NULL }
};
-static const resolver_option_t tree_conflict_options_update_moved_away[] =
+/* Additional resolver options offered by 'svn' for a property conflict. */
+static const client_option_t extra_resolver_options_prop[] =
{
- { "mc", N_("apply update to move destination (recommended)"),
- N_("apply incoming update to move destination"
- " [mine-conflict]"),
- svn_wc_conflict_choose_mine_conflict },
- { "p", N_("postpone"), N_("resolve the conflict later [postpone]"),
- svn_wc_conflict_choose_postpone },
- { "q", N_("quit resolution"), N_("postpone all remaining conflicts"),
- svn_wc_conflict_choose_postpone },
- { "h", N_("help"), N_("show this help (also '?')"),
- svn_wc_conflict_choose_undefined },
+ /* Translators: keep long_desc below 70 characters (wrap with a left
+ margin of 9 spaces if needed) */
+ { "dc", N_("Display conflict"), N_("show conflicts in this property"),
+ svn_client_conflict_option_undefined },
+ { "e", N_("Edit property"), N_("change merged property value in an "
+ "editor"),
+ svn_client_conflict_option_undefined,
+ SVN_CL__ACCEPT_EDIT },
+ { "h", N_("Help"), N_("show this help (also '?')"),
+ svn_client_conflict_option_undefined },
{ NULL }
};
-static const resolver_option_t tree_conflict_options_update_edit_deleted_dir[] =
+/* Additional resolver options offered by 'svn' for a tree conflict. */
+static const client_option_t extra_resolver_options_tree[] =
{
- { "mc", N_("prepare for updating moved-away children, if any (recommended)"),
- N_("allow updating moved-away children "
- "with 'svn resolve' [mine-conflict]"),
- svn_wc_conflict_choose_mine_conflict },
- { "p", N_("postpone"), N_("resolve the conflict later [postpone]"),
- svn_wc_conflict_choose_postpone },
- { "q", N_("quit resolution"), N_("postpone all remaining conflicts"),
- svn_wc_conflict_choose_postpone },
- { "h", N_("help"), N_("show this help (also '?')"),
- svn_wc_conflict_choose_undefined },
+ /* Translators: keep long_desc below 70 characters (wrap with a left
+ margin of 9 spaces if needed) */
+ { "d", N_("Set repository move destination path"),
+ N_("pick repository move target from list of possible targets"),
+ svn_client_conflict_option_undefined },
+
+ { "w", N_("Set working copy move destination path"),
+ N_("pick working copy move target from list of possible targets"),
+ svn_client_conflict_option_undefined },
+
+ { "h", N_("Help"), N_("show this help (also '?')"),
+ svn_client_conflict_option_undefined },
+
{ NULL }
};
+
/* Return a pointer to the option description in OPTIONS matching the
* one- or two-character OPTION_CODE. Return NULL if not found. */
-static const resolver_option_t *
-find_option(const resolver_option_t *options,
+static const client_option_t *
+find_option(const apr_array_header_t *options,
const char *option_code)
{
- const resolver_option_t *opt;
+ int i;
- for (opt = options; opt->code; opt++)
+ for (i = 0; i < options->nelts; i++)
{
+ const client_option_t *opt = APR_ARRAY_IDX(options, i, client_option_t *);
+
/* Ignore code "" (blank lines) which is not a valid answer. */
if (opt->code[0] && strcmp(opt->code, option_code) == 0)
return opt;
@@ -573,10 +537,76 @@ find_option(const resolver_option_t *options,
return NULL;
}
+/* Find the first recommended option in OPTIONS. */
+static const client_option_t *
+find_recommended_option(const apr_array_header_t *options)
+{
+ int i;
+
+ for (i = 0; i < options->nelts; i++)
+ {
+ const client_option_t *opt = APR_ARRAY_IDX(options, i, client_option_t *);
+
+ /* Ignore code "" (blank lines) which is not a valid answer. */
+ if (opt->code[0] && opt->is_recommended)
+ return opt;
+ }
+ return NULL;
+}
+
+/* Return a pointer to the client_option_t in OPTIONS matching the ID of
+ * conflict option BUILTIN_OPTION. @a out will be set to NULL if the
+ * option was not found. */
+static svn_error_t *
+find_option_by_builtin(client_option_t **out,
+ svn_client_conflict_t *conflict,
+ const resolver_option_t *options,
+ svn_client_conflict_option_t *builtin_option,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ const resolver_option_t *opt;
+ svn_client_conflict_option_id_t id;
+ svn_client_conflict_option_id_t recommended_id;
+
+ id = svn_client_conflict_option_get_id(builtin_option);
+ recommended_id = svn_client_conflict_get_recommended_option_id(conflict);
+
+ for (opt = options; opt->code; opt++)
+ {
+ if (opt->choice == id)
+ {
+ client_option_t *client_opt;
+
+ client_opt = apr_pcalloc(result_pool, sizeof(*client_opt));
+ client_opt->choice = id;
+ client_opt->code = opt->code;
+ client_opt->label = svn_client_conflict_option_get_label(
+ builtin_option,
+ result_pool);
+ client_opt->long_desc = svn_client_conflict_option_get_description(
+ builtin_option,
+ result_pool);
+ client_opt->accept_arg = opt->accept_arg;
+ client_opt->is_recommended =
+ (recommended_id != svn_client_conflict_option_unspecified &&
+ id == recommended_id);
+
+ *out = client_opt;
+
+ return SVN_NO_ERROR;
+ }
+ }
+
+ *out = NULL;
+
+ return SVN_NO_ERROR;
+}
+
/* Return a prompt string listing the options OPTIONS. If OPTION_CODES is
* non-null, select only the options whose codes are mentioned in it. */
static const char *
-prompt_string(const resolver_option_t *options,
+prompt_string(const apr_array_header_t *options,
const char *const *option_codes,
apr_pool_t *pool)
{
@@ -585,10 +615,11 @@ prompt_string(const resolver_option_t *options,
const char *line_sep = apr_psprintf(pool, "\n%*s", left_margin, "");
int this_line_len = left_margin;
svn_boolean_t first = TRUE;
+ int i = 0;
while (1)
{
- const resolver_option_t *opt;
+ const client_option_t *opt;
const char *s;
int slen;
@@ -597,18 +628,21 @@ prompt_string(const resolver_option_t *options,
if (! *option_codes)
break;
opt = find_option(options, *option_codes++);
+ if (opt == NULL)
+ continue;
}
else
{
- opt = options++;
- if (! opt->code)
+ if (i >= options->nelts)
break;
+ opt = APR_ARRAY_IDX(options, i, client_option_t *);
+ i++;
}
if (! first)
result = apr_pstrcat(pool, result, ",", SVN_VA_NULL);
- s = apr_psprintf(pool, _(" (%s) %s"),
- opt->code, _(opt->short_desc));
+ s = apr_psprintf(pool, " (%s) %s", opt->code,
+ opt->label ? opt->label : opt->long_desc);
slen = svn_utf_cstring_utf8_width(s);
/* Break the line if adding the next option would make it too long */
if (this_line_len + slen > MAX_PROMPT_WIDTH)
@@ -624,33 +658,48 @@ prompt_string(const resolver_option_t *options,
}
/* Return a help string listing the OPTIONS. */
-static const char *
-help_string(const resolver_option_t *options,
+static svn_error_t *
+help_string(const char **result,
+ const apr_array_header_t *options,
apr_pool_t *pool)
{
- const char *result = "";
- const resolver_option_t *opt;
+ apr_pool_t *iterpool;
+ int i;
- for (opt = options; opt->code; opt++)
+ *result = "";
+ iterpool = svn_pool_create(pool);
+ for (i = 0; i < options->nelts; i++)
{
+ const client_option_t *opt;
+ svn_pool_clear(iterpool);
+
+ opt = APR_ARRAY_IDX(options, i,
+ client_option_t *);
+
/* Append a line describing OPT, or a blank line if its code is "". */
if (opt->code[0])
{
const char *s = apr_psprintf(pool, " (%s)", opt->code);
- result = apr_psprintf(pool, "%s%-6s - %s\n",
- result, s, _(opt->long_desc));
+ if (opt->accept_arg)
+ *result = apr_psprintf(pool, "%s%-6s - %s [%s]\n",
+ *result, s, opt->long_desc,
+ opt->accept_arg);
+ else
+ *result = apr_psprintf(pool, "%s%-6s - %s\n", *result, s,
+ opt->long_desc);
}
else
{
- result = apr_pstrcat(pool, result, "\n", SVN_VA_NULL);
+ *result = apr_pstrcat(pool, *result, "\n", SVN_VA_NULL);
}
}
- result = apr_pstrcat(pool, result,
+ svn_pool_destroy(iterpool);
+ *result = apr_pstrcat(pool, *result,
_("Words in square brackets are the corresponding "
"--accept option arguments.\n"),
SVN_VA_NULL);
- return result;
+ return SVN_NO_ERROR;
}
/* Prompt the user with CONFLICT_OPTIONS, restricted to the options listed
@@ -659,12 +708,14 @@ help_string(const resolver_option_t *options,
* NULL if the answer was not one of them.
*
* If the answer is the (globally recognized) 'help' option, then display
- * the help (on stderr) and return with *OPT == NULL.
+ * CONFLICT_DESCRIPTION (if not NULL) and help (on stderr) and return with
+ * *OPT == NULL.
*/
static svn_error_t *
-prompt_user(const resolver_option_t **opt,
- const resolver_option_t *conflict_options,
+prompt_user(const client_option_t **opt,
+ const apr_array_header_t *conflict_options,
const char *const *options_to_show,
+ const char *conflict_description,
void *prompt_baton,
apr_pool_t *scratch_pool)
{
@@ -675,9 +726,13 @@ prompt_user(const resolver_option_t **opt,
SVN_ERR(svn_cmdline_prompt_user2(&answer, prompt, prompt_baton, scratch_pool));
if (strcmp(answer, "h") == 0 || strcmp(answer, "?") == 0)
{
- SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "\n%s\n",
- help_string(conflict_options,
- scratch_pool)));
+ const char *helpstr;
+
+ if (conflict_description)
+ SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "\n%s\n",
+ conflict_description));
+ SVN_ERR(help_string(&helpstr, conflict_options, scratch_pool));
+ SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "\n%s\n", helpstr));
*opt = NULL;
}
else
@@ -692,41 +747,174 @@ prompt_user(const resolver_option_t **opt,
return SVN_NO_ERROR;
}
-/* Ask the user what to do about the text conflict described by DESC.
- * Return the answer in RESULT. B is the conflict baton for this
- * conflict resolution session.
+/* Set *OPTIONS to an array of resolution options for CONFLICT. */
+static svn_error_t *
+build_text_conflict_options(apr_array_header_t **options,
+ svn_client_conflict_t *conflict,
+ svn_client_ctx_t *ctx,
+ svn_boolean_t is_binary,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ const client_option_t *o;
+ apr_array_header_t *builtin_options;
+ int nopt;
+ int i;
+ apr_pool_t *iterpool;
+
+ SVN_ERR(svn_client_conflict_text_get_resolution_options(&builtin_options,
+ conflict, ctx,
+ scratch_pool,
+ scratch_pool));
+ nopt = builtin_options->nelts + ARRAY_LEN(extra_resolver_options);
+ if (!is_binary)
+ nopt += ARRAY_LEN(extra_resolver_options_text);
+ *options = apr_array_make(result_pool, nopt, sizeof(client_option_t *));
+
+ iterpool = svn_pool_create(scratch_pool);
+ for (i = 0; i < builtin_options->nelts; i++)
+ {
+ client_option_t *opt;
+ svn_client_conflict_option_t *builtin_option;
+
+ svn_pool_clear(iterpool);
+ builtin_option = APR_ARRAY_IDX(builtin_options, i,
+ svn_client_conflict_option_t *);
+ SVN_ERR(find_option_by_builtin(&opt, conflict,
+ builtin_resolver_options,
+ builtin_option,
+ result_pool,
+ iterpool));
+ if (opt == NULL)
+ continue; /* ### unknown option -- assign a code dynamically? */
+
+ APR_ARRAY_PUSH(*options, client_option_t *) = opt;
+ }
+
+ for (o = extra_resolver_options; o->code; o++)
+ APR_ARRAY_PUSH(*options, const client_option_t *) = o;
+ if (!is_binary)
+ {
+ for (o = extra_resolver_options_text; o->code; o++)
+ APR_ARRAY_PUSH(*options, const client_option_t *) = o;
+ }
+
+ svn_pool_destroy(iterpool);
+
+ return SVN_NO_ERROR;
+}
+
+/* Mark CONFLICT as resolved to resolution option with ID OPTION_ID.
+ * If TEXT_CONFLICTED is true, resolve text conflicts described by CONFLICT.
+ * IF PROPNAME is not NULL, mark the conflict in the specified property as
+ * resolved. If PROPNAME is "", mark all property conflicts described by
+ * CONFLICT as resolved.
+ * If TREE_CONFLICTED is true, resolve tree conflicts described by CONFLICT.
+ * Adjust CONFLICT_STATS as necessary (PATH_PREFIX is needed for this step). */
+static svn_error_t *
+mark_conflict_resolved(svn_client_conflict_t *conflict,
+ svn_client_conflict_option_id_t option_id,
+ svn_boolean_t text_conflicted,
+ const char *propname,
+ svn_boolean_t tree_conflicted,
+ const char *path_prefix,
+ svn_cl__conflict_stats_t *conflict_stats,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ const char *local_relpath
+ = svn_cl__local_style_skip_ancestor(
+ path_prefix, svn_client_conflict_get_local_abspath(conflict),
+ scratch_pool);
+
+ if (text_conflicted)
+ {
+ SVN_ERR(svn_client_conflict_text_resolve_by_id(conflict, option_id,
+ ctx, scratch_pool));
+ svn_cl__conflict_stats_resolved(conflict_stats, local_relpath,
+ svn_wc_conflict_kind_text);
+ }
+
+ if (propname)
+ {
+ SVN_ERR(svn_client_conflict_prop_resolve_by_id(conflict, propname,
+ option_id, ctx,
+ scratch_pool));
+ svn_cl__conflict_stats_resolved(conflict_stats, local_relpath,
+ svn_wc_conflict_kind_property);
+ }
+
+ if (tree_conflicted)
+ {
+ SVN_ERR(svn_client_conflict_tree_resolve_by_id(conflict, option_id,
+ ctx, scratch_pool));
+ svn_cl__conflict_stats_resolved(conflict_stats, local_relpath,
+ svn_wc_conflict_kind_tree);
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Ask the user what to do about the text conflict described by CONFLICT
+ * and either resolve the conflict accordingly or postpone resolution.
* SCRATCH_POOL is used for temporary allocations. */
static svn_error_t *
-handle_text_conflict(svn_wc_conflict_result_t *result,
- const svn_wc_conflict_description2_t *desc,
- svn_cl__interactive_conflict_baton_t *b,
+handle_text_conflict(svn_boolean_t *resolved,
+ svn_boolean_t *postponed,
+ svn_boolean_t *quit,
+ svn_boolean_t *printed_description,
+ svn_client_conflict_t *conflict,
+ const char *path_prefix,
+ svn_cmdline_prompt_baton_t *pb,
+ const char *editor_cmd,
+ apr_hash_t *config,
+ svn_cl__conflict_stats_t *conflict_stats,
+ svn_client_ctx_t *ctx,
apr_pool_t *scratch_pool)
{
apr_pool_t *iterpool = svn_pool_create(scratch_pool);
svn_boolean_t diff_allowed = FALSE;
- /* Have they done something that might have affected the merged
- file (so that we need to save a .edited copy by setting the
- result->save_merge flag)? */
+ /* Have they done something that might have affected the merged file? */
svn_boolean_t performed_edit = FALSE;
/* Have they done *something* (edit, look at diff, etc) to
give them a rational basis for choosing (r)esolved? */
svn_boolean_t knows_something = FALSE;
const char *local_relpath;
-
- SVN_ERR_ASSERT(desc->kind == svn_wc_conflict_kind_text);
-
- local_relpath = svn_cl__local_style_skip_ancestor(b->path_prefix,
- desc->local_abspath,
- scratch_pool);;
-
- if (desc->is_binary)
- SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool,
- _("Conflict discovered in binary file '%s'.\n"),
- local_relpath));
- else
- SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool,
- _("Conflict discovered in file '%s'.\n"),
- local_relpath));
+ const char *local_abspath = svn_client_conflict_get_local_abspath(conflict);
+ const char *mime_type = svn_client_conflict_text_get_mime_type(conflict);
+ svn_boolean_t is_binary = mime_type ? svn_mime_type_is_binary(mime_type)
+ : FALSE;
+ const char *base_abspath;
+ const char *my_abspath;
+ const char *their_abspath;
+ const char *merged_abspath = svn_client_conflict_get_local_abspath(conflict);
+ apr_array_header_t *text_conflict_options;
+ svn_client_conflict_option_id_t option_id;
+
+ option_id = svn_client_conflict_option_unspecified;
+
+ SVN_ERR(svn_client_conflict_text_get_contents(NULL, &my_abspath,
+ &base_abspath, &their_abspath,
+ conflict, scratch_pool,
+ scratch_pool));
+
+ local_relpath = svn_cl__local_style_skip_ancestor(path_prefix,
+ local_abspath,
+ scratch_pool);
+
+ if (!*printed_description)
+ {
+ if (is_binary)
+ SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool,
+ _("Merge conflict discovered in binary "
+ "file '%s'.\n"),
+ local_relpath));
+ else
+ SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool,
+ _("Merge conflict discovered in file '%s'.\n"),
+ local_relpath));
+ *printed_description = TRUE;
+ }
/* ### TODO This whole feature availability check is grossly outdated.
DIFF_ALLOWED needs either to be redefined or to go away.
@@ -736,21 +924,18 @@ handle_text_conflict(svn_wc_conflict_result_t *result,
markers to the user (this is the typical 3-way merge
scenario), or if no base is available, we can show a diff
between mine and theirs. */
- if (!desc->is_binary &&
- ((desc->merged_file && desc->base_abspath)
- || (!desc->base_abspath && desc->my_abspath && desc->their_abspath)))
+ if (!is_binary &&
+ ((merged_abspath && base_abspath)
+ || (!base_abspath && my_abspath && their_abspath)))
diff_allowed = TRUE;
+ SVN_ERR(build_text_conflict_options(&text_conflict_options, conflict, ctx,
+ is_binary, scratch_pool, scratch_pool));
while (TRUE)
{
- const char *options[1 + MAX_ARRAY_LEN(binary_conflict_options,
- text_conflict_options)];
-
- const resolver_option_t *conflict_options = desc->is_binary
- ? binary_conflict_options
- : text_conflict_options;
- const char **next_option = options;
- const resolver_option_t *opt;
+ const char *suggested_options[9]; /* filled statically below */
+ const char **next_option = suggested_options;
+ const client_option_t *opt;
svn_pool_clear(iterpool);
@@ -758,30 +943,26 @@ handle_text_conflict(svn_wc_conflict_result_t *result,
if (diff_allowed)
{
/* We need one more path for this feature. */
- if (desc->my_abspath)
+ if (my_abspath)
*next_option++ = "df";
*next_option++ = "e";
/* We need one more path for this feature. */
- if (desc->my_abspath)
+ if (my_abspath)
*next_option++ = "m";
if (knows_something)
*next_option++ = "r";
-
- *next_option++ = "mc";
- *next_option++ = "tc";
}
else
{
- if (knows_something || desc->is_binary)
+ if (knows_something || is_binary)
*next_option++ = "r";
- /* The 'mine-full' option selects the ".mine" file so only offer
- * it if that file exists. It does not exist for binary files,
- * for example (questionable historical behaviour since 1.0). */
- if (desc->my_abspath)
+ /* The 'mine-full' option selects the ".mine" file for texts or
+ * the current working directory file for binary files. */
+ if (my_abspath || is_binary)
*next_option++ = "mf";
*next_option++ = "tf";
@@ -789,26 +970,28 @@ handle_text_conflict(svn_wc_conflict_result_t *result,
*next_option++ = "s";
*next_option++ = NULL;
- SVN_ERR(prompt_user(&opt, conflict_options, options, b->pb, iterpool));
+ SVN_ERR(prompt_user(&opt, text_conflict_options, suggested_options,
+ NULL, pb, iterpool));
if (! opt)
continue;
if (strcmp(opt->code, "q") == 0)
{
- result->choice = opt->choice;
- b->accept_which = svn_cl__accept_postpone;
- b->quit = TRUE;
+ option_id = opt->choice;
+ *quit = TRUE;
break;
}
else if (strcmp(opt->code, "s") == 0)
{
+ const char *helpstr;
+
+ SVN_ERR(help_string(&helpstr, text_conflict_options, iterpool));
SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "\n%s\n",
- help_string(conflict_options,
- iterpool)));
+ helpstr));
}
else if (strcmp(opt->code, "dc") == 0)
{
- if (desc->is_binary)
+ if (is_binary)
{
SVN_ERR(svn_cmdline_fprintf(stderr, iterpool,
_("Invalid option; cannot "
@@ -816,24 +999,23 @@ handle_text_conflict(svn_wc_conflict_result_t *result,
"binary file.\n\n")));
continue;
}
- else if (! (desc->my_abspath && desc->base_abspath &&
- desc->their_abspath))
+ else if (! (my_abspath && base_abspath && their_abspath))
{
SVN_ERR(svn_cmdline_fprintf(stderr, iterpool,
_("Invalid option; original "
"files not available.\n\n")));
continue;
}
- SVN_ERR(show_conflicts(desc,
- b->pb->cancel_func,
- b->pb->cancel_baton,
+ SVN_ERR(show_conflicts(conflict,
+ pb->cancel_func,
+ pb->cancel_baton,
iterpool));
knows_something = TRUE;
}
else if (strcmp(opt->code, "df") == 0)
{
/* Re-check preconditions. */
- if (! diff_allowed || ! desc->my_abspath)
+ if (! diff_allowed || ! my_abspath)
{
SVN_ERR(svn_cmdline_fprintf(stderr, iterpool,
_("Invalid option; there's no "
@@ -841,14 +1023,15 @@ handle_text_conflict(svn_wc_conflict_result_t *result,
continue;
}
- SVN_ERR(show_diff(desc, b->path_prefix,
- b->pb->cancel_func, b->pb->cancel_baton,
+ SVN_ERR(show_diff(conflict, merged_abspath, path_prefix,
+ pb->cancel_func, pb->cancel_baton,
iterpool));
knows_something = TRUE;
}
else if (strcmp(opt->code, "e") == 0 || strcmp(opt->code, ":-E") == 0)
{
- SVN_ERR(open_editor(&performed_edit, desc->merged_file, b, iterpool));
+ SVN_ERR(open_editor(&performed_edit, merged_abspath, editor_cmd,
+ config, iterpool));
if (performed_edit)
knows_something = TRUE;
}
@@ -858,7 +1041,7 @@ handle_text_conflict(svn_wc_conflict_result_t *result,
svn_error_t *err;
/* Re-check preconditions. */
- if (! desc->my_abspath)
+ if (! my_abspath)
{
SVN_ERR(svn_cmdline_fprintf(stderr, iterpool,
_("Invalid option; there's no "
@@ -866,11 +1049,11 @@ handle_text_conflict(svn_wc_conflict_result_t *result,
continue;
}
- err = svn_cl__merge_file_externally(desc->base_abspath,
- desc->their_abspath,
- desc->my_abspath,
- desc->merged_file,
- desc->local_abspath, b->config,
+ err = svn_cl__merge_file_externally(base_abspath,
+ their_abspath,
+ my_abspath,
+ merged_abspath,
+ local_abspath, config,
NULL, iterpool);
if (err)
{
@@ -881,16 +1064,16 @@ handle_text_conflict(svn_wc_conflict_result_t *result,
/* Try the internal merge tool. */
svn_error_clear(err);
SVN_ERR(svn_cl__merge_file(&remains_in_conflict,
- desc->base_abspath,
- desc->their_abspath,
- desc->my_abspath,
- desc->merged_file,
- desc->local_abspath,
- b->path_prefix,
- b->editor_cmd,
- b->config,
- b->pb->cancel_func,
- b->pb->cancel_baton,
+ base_abspath,
+ their_abspath,
+ my_abspath,
+ merged_abspath,
+ local_abspath,
+ path_prefix,
+ editor_cmd,
+ config,
+ pb->cancel_func,
+ pb->cancel_baton,
iterpool));
knows_something = !remains_in_conflict;
}
@@ -922,21 +1105,20 @@ handle_text_conflict(svn_wc_conflict_result_t *result,
{
/* ### This check should be earlier as it's nasty to offer an option
* and then when the user chooses it say 'Invalid option'. */
- /* ### 'merged_file' shouldn't be necessary *before* we launch the
+ /* ### 'merged_abspath' shouldn't be necessary *before* we launch the
* resolver: it should be the *result* of doing so. */
- if (desc->base_abspath && desc->their_abspath &&
- desc->my_abspath && desc->merged_file)
+ if (base_abspath && their_abspath && my_abspath && merged_abspath)
{
svn_error_t *err;
char buf[1024];
const char *message;
- err = svn_cl__merge_file_externally(desc->base_abspath,
- desc->their_abspath,
- desc->my_abspath,
- desc->merged_file,
- desc->local_abspath,
- b->config, NULL, iterpool);
+ err = svn_cl__merge_file_externally(base_abspath,
+ their_abspath,
+ my_abspath,
+ merged_abspath,
+ local_abspath,
+ config, NULL, iterpool);
if (err && (err->apr_err == SVN_ERR_CL_NO_EXTERNAL_MERGE_TOOL ||
err->apr_err == SVN_ERR_EXTERNAL_PROGRAM))
{
@@ -962,26 +1144,26 @@ handle_text_conflict(svn_wc_conflict_result_t *result,
svn_boolean_t remains_in_conflict = TRUE;
SVN_ERR(svn_cl__merge_file(&remains_in_conflict,
- desc->base_abspath,
- desc->their_abspath,
- desc->my_abspath,
- desc->merged_file,
- desc->local_abspath,
- b->path_prefix,
- b->editor_cmd,
- b->config,
- b->pb->cancel_func,
- b->pb->cancel_baton,
+ base_abspath,
+ their_abspath,
+ my_abspath,
+ merged_abspath,
+ local_abspath,
+ path_prefix,
+ editor_cmd,
+ config,
+ pb->cancel_func,
+ pb->cancel_baton,
iterpool));
if (!remains_in_conflict)
knows_something = TRUE;
}
- else if (opt->choice != svn_wc_conflict_choose_undefined)
+ else if (opt->choice != svn_client_conflict_option_undefined)
{
- if ((opt->choice == svn_wc_conflict_choose_mine_conflict
- || opt->choice == svn_wc_conflict_choose_theirs_conflict)
- && desc->is_binary)
+ if ((opt->choice == svn_client_conflict_option_working_text_where_conflicted
+ || opt->choice == svn_client_conflict_option_incoming_text_where_conflicted)
+ && is_binary)
{
SVN_ERR(svn_cmdline_fprintf(stderr, iterpool,
_("Invalid option; cannot choose "
@@ -993,7 +1175,7 @@ handle_text_conflict(svn_wc_conflict_result_t *result,
/* We only allow the user accept the merged version of
the file if they've edited it, or at least looked at
the diff. */
- if (opt->choice == svn_wc_conflict_choose_merged
+ if (opt->choice == svn_client_conflict_option_merged_text
&& ! knows_something && diff_allowed)
{
SVN_ERR(svn_cmdline_fprintf(
@@ -1003,59 +1185,137 @@ handle_text_conflict(svn_wc_conflict_result_t *result,
continue;
}
- result->choice = opt->choice;
- if (performed_edit)
- result->save_merged = TRUE;
+ option_id = opt->choice;
break;
}
}
svn_pool_destroy(iterpool);
+ if (option_id != svn_client_conflict_option_unspecified &&
+ option_id != svn_client_conflict_option_postpone)
+ {
+ SVN_ERR(mark_conflict_resolved(conflict, option_id,
+ TRUE, NULL, FALSE,
+ path_prefix, conflict_stats,
+ ctx, scratch_pool));
+ *resolved = TRUE;
+ }
+ else
+ {
+ *resolved = FALSE;
+ *postponed = (option_id == svn_client_conflict_option_postpone);
+ }
+
return SVN_NO_ERROR;
}
-/* Ask the user what to do about the property conflict described by DESC.
- * Return the answer in RESULT. B is the conflict baton for this
- * conflict resolution session.
+/* Set *OPTIONS to an array of resolution options for CONFLICT. */
+static svn_error_t *
+build_prop_conflict_options(apr_array_header_t **options,
+ svn_client_conflict_t *conflict,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ const client_option_t *o;
+ apr_array_header_t *builtin_options;
+ int nopt;
+ int i;
+ apr_pool_t *iterpool;
+
+ SVN_ERR(svn_client_conflict_prop_get_resolution_options(&builtin_options,
+ conflict, ctx,
+ scratch_pool,
+ scratch_pool));
+ nopt = builtin_options->nelts + ARRAY_LEN(extra_resolver_options) +
+ ARRAY_LEN(extra_resolver_options_prop);
+ *options = apr_array_make(result_pool, nopt, sizeof(client_option_t *));
+
+ iterpool = svn_pool_create(scratch_pool);
+ for (i = 0; i < builtin_options->nelts; i++)
+ {
+ client_option_t *opt;
+ svn_client_conflict_option_t *builtin_option;
+
+ svn_pool_clear(iterpool);
+ builtin_option = APR_ARRAY_IDX(builtin_options, i,
+ svn_client_conflict_option_t *);
+ SVN_ERR(find_option_by_builtin(&opt, conflict,
+ builtin_resolver_options,
+ builtin_option,
+ result_pool,
+ iterpool));
+ if (opt == NULL)
+ continue; /* ### unknown option -- assign a code dynamically? */
+
+ APR_ARRAY_PUSH(*options, client_option_t *) = opt;
+ }
+
+ svn_pool_destroy(iterpool);
+
+ for (o = extra_resolver_options; o->code; o++)
+ APR_ARRAY_PUSH(*options, const client_option_t *) = o;
+ for (o = extra_resolver_options_prop; o->code; o++)
+ APR_ARRAY_PUSH(*options, const client_option_t *) = o;
+
+ return SVN_NO_ERROR;
+}
+
+/* Ask the user what to do about the conflicted property PROPNAME described
+ * by CONFLICT and return the corresponding resolution option in *OPTION.
* SCRATCH_POOL is used for temporary allocations. */
static svn_error_t *
-handle_prop_conflict(svn_wc_conflict_result_t *result,
- const svn_wc_conflict_description2_t *desc,
- svn_cl__interactive_conflict_baton_t *b,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
+handle_one_prop_conflict(svn_client_conflict_option_t **option,
+ svn_boolean_t *quit,
+ const char *path_prefix,
+ svn_cmdline_prompt_baton_t *pb,
+ const char *editor_cmd,
+ apr_hash_t *config,
+ svn_client_conflict_t *conflict,
+ const char *propname,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
apr_pool_t *iterpool;
- const char *message;
- const char *merged_file_path = NULL;
+ const char *description;
+ const svn_string_t *merged_propval = NULL;
svn_boolean_t resolved_allowed = FALSE;
+ const svn_string_t *base_propval;
+ const svn_string_t *my_propval;
+ const svn_string_t *their_propval;
+ apr_array_header_t *resolution_options;
+ apr_array_header_t *prop_conflict_options;
- /* ### Work around a historical bug in the provider: the path to the
- * conflict description file was put in the 'theirs' field, and
- * 'theirs' was put in the 'merged' field. */
- ((svn_wc_conflict_description2_t *)desc)->their_abspath = desc->merged_file;
- ((svn_wc_conflict_description2_t *)desc)->merged_file = NULL;
-
- SVN_ERR_ASSERT(desc->kind == svn_wc_conflict_kind_property);
+ SVN_ERR(svn_client_conflict_prop_get_propvals(NULL, &my_propval,
+ &base_propval, &their_propval,
+ conflict, propname,
+ scratch_pool));
SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool,
_("Conflict for property '%s' discovered"
" on '%s'.\n"),
- desc->property_name,
+ propname,
svn_cl__local_style_skip_ancestor(
- b->path_prefix, desc->local_abspath,
+ path_prefix,
+ svn_client_conflict_get_local_abspath(conflict),
scratch_pool)));
-
- SVN_ERR(svn_cl__get_human_readable_prop_conflict_description(&message, desc,
- scratch_pool));
- SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "%s\n", message));
-
+ SVN_ERR(svn_client_conflict_prop_get_description(&description, conflict,
+ scratch_pool, scratch_pool));
+ SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "%s\n", description));
+
+ SVN_ERR(svn_client_conflict_prop_get_resolution_options(&resolution_options,
+ conflict, ctx,
+ result_pool,
+ scratch_pool));
+ SVN_ERR(build_prop_conflict_options(&prop_conflict_options, conflict, ctx,
+ scratch_pool, scratch_pool));
iterpool = svn_pool_create(scratch_pool);
while (TRUE)
{
- const resolver_option_t *opt;
- const char *options[ARRAY_LEN(prop_conflict_options)];
- const char **next_option = options;
+ const client_option_t *opt;
+ const char *suggested_options[9]; /* filled statically below */
+ const char **next_option = suggested_options;
*next_option++ = "p";
*next_option++ = "mf";
@@ -1070,29 +1330,32 @@ handle_prop_conflict(svn_wc_conflict_result_t *result,
svn_pool_clear(iterpool);
- SVN_ERR(prompt_user(&opt, prop_conflict_options, options, b->pb,
- iterpool));
+ SVN_ERR(prompt_user(&opt, prop_conflict_options, suggested_options,
+ NULL, pb, iterpool));
if (! opt)
continue;
if (strcmp(opt->code, "q") == 0)
{
- result->choice = opt->choice;
- b->accept_which = svn_cl__accept_postpone;
- b->quit = TRUE;
+ *option = svn_client_conflict_option_find_by_id(resolution_options,
+ opt->choice);
+ *quit = TRUE;
break;
}
else if (strcmp(opt->code, "dc") == 0)
{
- SVN_ERR(show_prop_conflict(desc, merged_file_path,
- b->pb->cancel_func, b->pb->cancel_baton,
+ SVN_ERR(show_prop_conflict(base_propval, my_propval, their_propval,
+ merged_propval,
+ pb->cancel_func, pb->cancel_baton,
scratch_pool));
}
else if (strcmp(opt->code, "e") == 0)
{
- SVN_ERR(edit_prop_conflict(&merged_file_path, desc, b,
+ SVN_ERR(edit_prop_conflict(&merged_propval,
+ base_propval, my_propval, their_propval,
+ editor_cmd, config, pb,
result_pool, scratch_pool));
- resolved_allowed = (merged_file_path != NULL);
+ resolved_allowed = (merged_propval != NULL);
}
else if (strcmp(opt->code, "r") == 0)
{
@@ -1104,13 +1367,17 @@ handle_prop_conflict(svn_wc_conflict_result_t *result,
continue;
}
- result->merged_file = merged_file_path;
- result->choice = svn_wc_conflict_choose_merged;
+ *option = svn_client_conflict_option_find_by_id(
+ resolution_options,
+ svn_client_conflict_option_merged_text);
+ svn_client_conflict_option_set_merged_propval(*option,
+ merged_propval);
break;
}
- else if (opt->choice != svn_wc_conflict_choose_undefined)
+ else if (opt->choice != svn_client_conflict_option_undefined)
{
- result->choice = opt->choice;
+ *option = svn_client_conflict_option_find_by_id(resolution_options,
+ opt->choice);
break;
}
}
@@ -1119,252 +1386,854 @@ handle_prop_conflict(svn_wc_conflict_result_t *result,
return SVN_NO_ERROR;
}
-/* Ask the user what to do about the tree conflict described by DESC.
- * Return the answer in RESULT. B is the conflict baton for this
- * conflict resolution session.
+/* Ask the user what to do about the property conflicts described by CONFLICT
+ * and either resolve them accordingly or postpone resolution.
* SCRATCH_POOL is used for temporary allocations. */
static svn_error_t *
-handle_tree_conflict(svn_wc_conflict_result_t *result,
- const svn_wc_conflict_description2_t *desc,
- svn_cl__interactive_conflict_baton_t *b,
- apr_pool_t *scratch_pool)
+handle_prop_conflicts(svn_boolean_t *resolved,
+ svn_boolean_t *postponed,
+ svn_boolean_t *quit,
+ const svn_string_t **merged_value,
+ const char *path_prefix,
+ svn_cmdline_prompt_baton_t *pb,
+ const char *editor_cmd,
+ apr_hash_t *config,
+ svn_client_conflict_t *conflict,
+ svn_cl__conflict_stats_t *conflict_stats,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
{
- const char *readable_desc;
+ apr_array_header_t *props_conflicted;
apr_pool_t *iterpool;
+ int i;
+ int nresolved = 0;
- SVN_ERR(svn_cl__get_human_readable_tree_conflict_description(
- &readable_desc, desc, scratch_pool));
- SVN_ERR(svn_cmdline_fprintf(
- stderr, scratch_pool,
- _("Tree conflict on '%s'\n > %s\n"),
- svn_cl__local_style_skip_ancestor(b->path_prefix,
- desc->local_abspath,
- scratch_pool),
- readable_desc));
+ SVN_ERR(svn_client_conflict_get_conflicted(NULL, &props_conflicted, NULL,
+ conflict, scratch_pool,
+ scratch_pool));
iterpool = svn_pool_create(scratch_pool);
- while (1)
+ for (i = 0; i < props_conflicted->nelts; i++)
{
- const resolver_option_t *opt;
- const resolver_option_t *tc_opts;
+ const char *propname = APR_ARRAY_IDX(props_conflicted, i, const char *);
+ svn_client_conflict_option_t *option;
+ svn_client_conflict_option_id_t option_id;
svn_pool_clear(iterpool);
- tc_opts = tree_conflict_options;
+ SVN_ERR(handle_one_prop_conflict(&option, quit, path_prefix, pb,
+ editor_cmd, config, conflict, propname,
+ ctx,
+ iterpool, iterpool));
+ option_id = svn_client_conflict_option_get_id(option);
- if (desc->operation == svn_wc_operation_update ||
- desc->operation == svn_wc_operation_switch)
+ if (option_id != svn_client_conflict_option_unspecified &&
+ option_id != svn_client_conflict_option_postpone)
{
- if (desc->reason == svn_wc_conflict_reason_moved_away)
- {
- tc_opts = tree_conflict_options_update_moved_away;
- }
- else if (desc->reason == svn_wc_conflict_reason_deleted ||
- desc->reason == svn_wc_conflict_reason_replaced)
- {
- if (desc->action == svn_wc_conflict_action_edit &&
- desc->node_kind == svn_node_dir)
- tc_opts = tree_conflict_options_update_edit_deleted_dir;
- }
+ const char *local_relpath =
+ svn_cl__local_style_skip_ancestor(
+ path_prefix, svn_client_conflict_get_local_abspath(conflict),
+ iterpool);
+
+ SVN_ERR(svn_client_conflict_prop_resolve(conflict, propname, option,
+ ctx, iterpool));
+ svn_cl__conflict_stats_resolved(conflict_stats, local_relpath,
+ svn_wc_conflict_kind_property);
+ nresolved++;
+ *postponed = FALSE;
}
+ else
+ *postponed = (option_id == svn_client_conflict_option_postpone);
- SVN_ERR(prompt_user(&opt, tc_opts, NULL, b->pb, iterpool));
- if (! opt)
- continue;
+ if (*quit)
+ break;
+ }
+ svn_pool_destroy(iterpool);
- if (strcmp(opt->code, "q") == 0)
+ /* Indicate success if no property conflicts remain. */
+ *resolved = (nresolved == props_conflicted->nelts);
+
+ return SVN_NO_ERROR;
+}
+
+/* Set *OPTIONS to an array of resolution options for CONFLICT. */
+static svn_error_t *
+build_tree_conflict_options(
+ apr_array_header_t **options,
+ apr_array_header_t **possible_moved_to_repos_relpaths,
+ apr_array_header_t **possible_moved_to_abspaths,
+ svn_boolean_t *all_options_are_dumb,
+ svn_client_conflict_t *conflict,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ const client_option_t *o;
+ apr_array_header_t *builtin_options;
+ int nopt;
+ int i;
+ int next_unknown_option_code = 1;
+ apr_pool_t *iterpool;
+
+ if (all_options_are_dumb != NULL)
+ *all_options_are_dumb = TRUE;
+
+ SVN_ERR(svn_client_conflict_tree_get_resolution_options(&builtin_options,
+ conflict, ctx,
+ scratch_pool,
+ scratch_pool));
+ nopt = builtin_options->nelts + ARRAY_LEN(extra_resolver_options_tree) +
+ ARRAY_LEN(extra_resolver_options);
+ *options = apr_array_make(result_pool, nopt, sizeof(client_option_t *));
+ *possible_moved_to_abspaths = NULL;
+ *possible_moved_to_repos_relpaths = NULL;
+
+ iterpool = svn_pool_create(scratch_pool);
+ for (i = 0; i < builtin_options->nelts; i++)
+ {
+ client_option_t *opt;
+ svn_client_conflict_option_t *builtin_option;
+ svn_client_conflict_option_id_t id;
+
+ svn_pool_clear(iterpool);
+ builtin_option = APR_ARRAY_IDX(builtin_options, i,
+ svn_client_conflict_option_t *);
+ SVN_ERR(find_option_by_builtin(&opt, conflict,
+ builtin_resolver_options,
+ builtin_option,
+ result_pool,
+ iterpool));
+ if (opt == NULL)
{
- result->choice = opt->choice;
- b->accept_which = svn_cl__accept_postpone;
- b->quit = TRUE;
- break;
+ /* Unkown option. Assign a dynamic option code. */
+ opt = apr_pcalloc(result_pool, sizeof(*opt));
+ opt->code = apr_psprintf(result_pool, "%d", next_unknown_option_code);
+ next_unknown_option_code++;
+ opt->label = svn_client_conflict_option_get_label(builtin_option,
+ result_pool);
+ opt->long_desc = svn_client_conflict_option_get_description(
+ builtin_option, result_pool);
+ opt->choice = svn_client_conflict_option_get_id(builtin_option);
+ opt->accept_arg = NULL;
}
- else if (opt->choice != svn_wc_conflict_choose_undefined)
+
+ APR_ARRAY_PUSH(*options, client_option_t *) = opt;
+
+ id = svn_client_conflict_option_get_id(builtin_option);
+
+ /* Check if we got a "smart" tree conflict option. */
+ if (all_options_are_dumb != NULL &&
+ *all_options_are_dumb &&
+ id != svn_client_conflict_option_postpone &&
+ id != svn_client_conflict_option_accept_current_wc_state)
+ *all_options_are_dumb = FALSE;
+
+ if (id == svn_client_conflict_option_incoming_move_file_text_merge ||
+ id == svn_client_conflict_option_incoming_move_dir_merge)
{
- result->choice = opt->choice;
- break;
+ SVN_ERR(
+ svn_client_conflict_option_get_moved_to_repos_relpath_candidates(
+ possible_moved_to_repos_relpaths, builtin_option,
+ result_pool, iterpool));
+ SVN_ERR(svn_client_conflict_option_get_moved_to_abspath_candidates(
+ possible_moved_to_abspaths, builtin_option,
+ result_pool, iterpool));
}
}
+
svn_pool_destroy(iterpool);
+ for (o = extra_resolver_options_tree; o->code; o++)
+ {
+ /* Add move target choice options only if there are multiple
+ * move targets to choose from. */
+ if (strcmp(o->code, "d") == 0 &&
+ (*possible_moved_to_repos_relpaths == NULL ||
+ (*possible_moved_to_repos_relpaths)->nelts <= 1))
+ continue;
+ if (strcmp(o->code, "w") == 0 &&
+ (*possible_moved_to_abspaths == NULL ||
+ (*possible_moved_to_abspaths)->nelts <= 1))
+ continue;
+
+ APR_ARRAY_PUSH(*options, const client_option_t *) = o;
+ }
+ for (o = extra_resolver_options; o->code; o++)
+ APR_ARRAY_PUSH(*options, const client_option_t *) = o;
+
return SVN_NO_ERROR;
}
-/* The body of svn_cl__conflict_func_interactive(). */
+/* Make the user select a move target path for the moved-away VICTIM_ABSPATH. */
static svn_error_t *
-conflict_func_interactive(svn_wc_conflict_result_t **result,
- const svn_wc_conflict_description2_t *desc,
- void *baton,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
+prompt_move_target_path(int *preferred_move_target_idx,
+ apr_array_header_t *possible_moved_to_paths,
+ svn_boolean_t paths_are_local,
+ svn_cmdline_prompt_baton_t *pb,
+ const char *victim_abspath,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
{
- svn_cl__interactive_conflict_baton_t *b = baton;
- svn_error_t *err;
+ const char *move_targets_prompt = "";
+ const char *move_targets_list = "";
+ const char *wcroot_abspath;
+ const char *victim_relpath;
+ int i;
+ apr_int64_t idx;
+ apr_pool_t *iterpool;
+
+ SVN_ERR(svn_client_get_wc_root(&wcroot_abspath, victim_abspath,
+ ctx, scratch_pool, scratch_pool));
+ victim_relpath = svn_cl__local_style_skip_ancestor(wcroot_abspath,
+ victim_abspath,
+ scratch_pool),
+ iterpool = svn_pool_create(scratch_pool);
+
+ /* Build the prompt. */
+ for (i = 0; i < possible_moved_to_paths->nelts; i++)
+ {
+ svn_pool_clear(iterpool);
- /* Start out assuming we're going to postpone the conflict. */
- *result = svn_wc_create_conflict_result(svn_wc_conflict_choose_postpone,
- NULL, result_pool);
+ if (paths_are_local)
+ {
+ const char *moved_to_abspath;
+ const char *moved_to_relpath;
+
+ moved_to_abspath = APR_ARRAY_IDX(possible_moved_to_paths, i,
+ const char *);
+ moved_to_relpath = svn_cl__local_style_skip_ancestor(
+ wcroot_abspath, moved_to_abspath, iterpool),
+ move_targets_list = apr_psprintf(scratch_pool, "%s (%d): '%s'\n",
+ move_targets_list, i + 1,
+ moved_to_relpath);
+ }
+ else
+ {
+ const char *moved_to_repos_relpath;
- switch (b->accept_which)
+ moved_to_repos_relpath = APR_ARRAY_IDX(possible_moved_to_paths, i,
+ const char *);
+ move_targets_list = apr_psprintf(scratch_pool, "%s (%d): '^/%s'\n",
+ move_targets_list, i + 1,
+ moved_to_repos_relpath);
+ }
+ }
+ if (paths_are_local)
+ move_targets_prompt =
+ apr_psprintf(scratch_pool,
+ _("Possible working copy destinations for moved-away '%s' "
+ "are:\n%s"
+ "Only one destination can be a move; the others are "
+ "copies.\n"
+ "Specify the correct move target path by number: "),
+ victim_relpath, move_targets_list);
+ else
+ move_targets_prompt =
+ apr_psprintf(scratch_pool,
+ _("Possible repository destinations for moved-away '%s' "
+ "are:\n%s"
+ "Only one destination can be a move; the others are "
+ "copies.\n"
+ "Specify the correct move target path by number: "),
+ victim_relpath, move_targets_list);
+
+ /* Keep asking the user until we got a valid choice. */
+ while (1)
{
- case svn_cl__accept_invalid:
- case svn_cl__accept_unspecified:
- /* No (or no valid) --accept option, fall through to prompting. */
+ const char *answer;
+ svn_error_t *err;
+
+ svn_pool_clear(iterpool);
+
+ SVN_ERR(svn_cmdline_prompt_user2(&answer, move_targets_prompt,
+ pb, iterpool));
+ err = svn_cstring_strtoi64(&idx, answer, 1,
+ possible_moved_to_paths->nelts, 10);
+ if (err)
+ {
+ char buf[1024];
+
+ svn_cmdline_fprintf(stderr, iterpool, "%s\n",
+ svn_err_best_message(err, buf, sizeof(buf)));
+ svn_error_clear(err);
+ continue;
+ }
+
break;
- case svn_cl__accept_postpone:
- (*result)->choice = svn_wc_conflict_choose_postpone;
- return SVN_NO_ERROR;
- case svn_cl__accept_base:
- (*result)->choice = svn_wc_conflict_choose_base;
- return SVN_NO_ERROR;
- case svn_cl__accept_working:
- /* If the caller didn't merge the property values, then I guess
- * 'choose working' means 'choose mine'... */
- if (! desc->merged_file)
- (*result)->merged_file = desc->my_abspath;
- (*result)->choice = svn_wc_conflict_choose_merged;
- return SVN_NO_ERROR;
- case svn_cl__accept_mine_conflict:
- (*result)->choice = svn_wc_conflict_choose_mine_conflict;
- return SVN_NO_ERROR;
- case svn_cl__accept_theirs_conflict:
- (*result)->choice = svn_wc_conflict_choose_theirs_conflict;
- return SVN_NO_ERROR;
- case svn_cl__accept_mine_full:
- (*result)->choice = svn_wc_conflict_choose_mine_full;
- return SVN_NO_ERROR;
- case svn_cl__accept_theirs_full:
- (*result)->choice = svn_wc_conflict_choose_theirs_full;
- return SVN_NO_ERROR;
- case svn_cl__accept_edit:
- if (desc->merged_file)
+ }
+
+ svn_pool_destroy(iterpool);
+
+ SVN_ERR_ASSERT((idx - 1) == (int)(idx - 1));
+ *preferred_move_target_idx = (int)(idx - 1);
+ return SVN_NO_ERROR;
+}
+
+/* Ask the user what to do about the tree conflict described by CONFLICT
+ * and either resolve the conflict accordingly or postpone resolution.
+ * SCRATCH_POOL is used for temporary allocations. */
+static svn_error_t *
+handle_tree_conflict(svn_boolean_t *resolved,
+ svn_boolean_t *postponed,
+ svn_boolean_t *quit,
+ svn_boolean_t *printed_description,
+ svn_client_conflict_t *conflict,
+ const char *path_prefix,
+ svn_cmdline_prompt_baton_t *pb,
+ svn_cl__conflict_stats_t *conflict_stats,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ apr_pool_t *iterpool;
+ apr_array_header_t *tree_conflict_options;
+ svn_client_conflict_option_id_t option_id;
+ const char *local_abspath;
+ const char *conflict_description;
+ const char *local_change_description;
+ const char *incoming_change_description;
+ apr_array_header_t *possible_moved_to_repos_relpaths;
+ apr_array_header_t *possible_moved_to_abspaths;
+ svn_boolean_t all_options_are_dumb;
+ const struct client_option_t *recommended_option;
+ svn_boolean_t repos_move_target_chosen = FALSE;
+ svn_boolean_t wc_move_target_chosen = FALSE;
+
+ option_id = svn_client_conflict_option_unspecified;
+ local_abspath = svn_client_conflict_get_local_abspath(conflict);
+
+ /* Always show the best possible conflict description and options. */
+ SVN_ERR(svn_client_conflict_tree_get_details(conflict, ctx, scratch_pool));
+
+ SVN_ERR(svn_client_conflict_tree_get_description(
+ &incoming_change_description, &local_change_description,
+ conflict, ctx, scratch_pool, scratch_pool));
+ conflict_description = apr_psprintf(scratch_pool, "%s\n%s",
+ incoming_change_description,
+ local_change_description);
+ if (!*printed_description)
+ SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool,
+ _("Tree conflict on '%s':\n%s\n"),
+ svn_cl__local_style_skip_ancestor(
+ path_prefix, local_abspath, scratch_pool),
+ conflict_description));
+
+ SVN_ERR(build_tree_conflict_options(&tree_conflict_options,
+ &possible_moved_to_repos_relpaths,
+ &possible_moved_to_abspaths,
+ &all_options_are_dumb,
+ conflict, ctx,
+ scratch_pool, scratch_pool));
+
+ /* Try a recommended resolution option before prompting. */
+ recommended_option = find_recommended_option(tree_conflict_options);
+ if (recommended_option)
+ {
+ svn_error_t *err;
+ apr_status_t root_cause;
+
+ SVN_ERR(svn_cmdline_printf(scratch_pool,
+ _("Applying recommended resolution '%s':\n"),
+ recommended_option->label));
+
+ err = mark_conflict_resolved(conflict, recommended_option->choice,
+ FALSE, NULL, TRUE,
+ path_prefix, conflict_stats,
+ ctx, scratch_pool);
+ if (!err)
+ {
+ *resolved = TRUE;
+ return SVN_NO_ERROR;
+ }
+
+ root_cause = svn_error_root_cause(err)->apr_err;
+ if (root_cause != SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE &&
+ root_cause != SVN_ERR_WC_OBSTRUCTED_UPDATE &&
+ root_cause != SVN_ERR_WC_FOUND_CONFLICT)
+ return svn_error_trace(err);
+
+ /* Fall back to interactive prompting. */
+ svn_error_clear(err);
+ }
+
+ if (all_options_are_dumb)
+ SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool,
+ _("\nSubversion is not smart enough to resolve "
+ "this tree conflict automatically!\nSee 'svn "
+ "help resolve' for more information.\n\n")));
+
+ iterpool = svn_pool_create(scratch_pool);
+ while (1)
+ {
+ const client_option_t *opt;
+
+ svn_pool_clear(iterpool);
+
+ if (!repos_move_target_chosen &&
+ possible_moved_to_repos_relpaths &&
+ possible_moved_to_repos_relpaths->nelts > 1)
+ SVN_ERR(svn_cmdline_printf(scratch_pool,
+ _("Ambiguous move destinations exist in the repository; "
+ "try the 'd' option\n")));
+ if (!wc_move_target_chosen && possible_moved_to_abspaths &&
+ possible_moved_to_abspaths->nelts > 1)
+ SVN_ERR(svn_cmdline_printf(scratch_pool,
+ _("Ambiguous move destinations exist in the working copy; "
+ "try the 'w' option\n")));
+
+ SVN_ERR(prompt_user(&opt, tree_conflict_options, NULL,
+ conflict_description, pb, iterpool));
+ *printed_description = TRUE;
+ if (! opt)
+ continue;
+
+ if (strcmp(opt->code, "q") == 0)
+ {
+ option_id = opt->choice;
+ *quit = TRUE;
+ break;
+ }
+ else if (strcmp(opt->code, "d") == 0)
{
- if (b->external_failed)
+ int preferred_move_target_idx;
+ apr_array_header_t *options;
+ svn_client_conflict_option_t *conflict_option;
+
+ SVN_ERR(prompt_move_target_path(&preferred_move_target_idx,
+ possible_moved_to_repos_relpaths,
+ FALSE,
+ pb, local_abspath, ctx, iterpool));
+
+ /* Update preferred move target path. */
+ SVN_ERR(svn_client_conflict_tree_get_resolution_options(&options,
+ conflict,
+ ctx,
+ iterpool,
+ iterpool));
+ conflict_option =
+ svn_client_conflict_option_find_by_id(
+ options,
+ svn_client_conflict_option_incoming_move_file_text_merge);
+ if (conflict_option == NULL)
{
- (*result)->choice = svn_wc_conflict_choose_postpone;
- return SVN_NO_ERROR;
+ conflict_option =
+ svn_client_conflict_option_find_by_id(
+ options, svn_client_conflict_option_incoming_move_dir_merge);
}
- err = svn_cmdline__edit_file_externally(desc->merged_file,
- b->editor_cmd, b->config,
- scratch_pool);
- if (err && (err->apr_err == SVN_ERR_CL_NO_EXTERNAL_EDITOR ||
- err->apr_err == SVN_ERR_EXTERNAL_PROGRAM))
+ if (conflict_option)
{
- char buf[1024];
- const char *message;
-
- message = svn_err_best_message(err, buf, sizeof(buf));
- SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "%s\n",
- message));
- svn_error_clear(err);
- b->external_failed = TRUE;
+ SVN_ERR(svn_client_conflict_option_set_moved_to_repos_relpath(
+ conflict_option, preferred_move_target_idx,
+ ctx, iterpool));
+ repos_move_target_chosen = TRUE;
+ wc_move_target_chosen = FALSE;
+
+ /* Update option description. */
+ SVN_ERR(build_tree_conflict_options(
+ &tree_conflict_options,
+ &possible_moved_to_repos_relpaths,
+ &possible_moved_to_abspaths,
+ NULL, conflict, ctx,
+ scratch_pool, scratch_pool));
+
+ /* Update conflict description. */
+ SVN_ERR(svn_client_conflict_tree_get_description(
+ &incoming_change_description, &local_change_description,
+ conflict, ctx, scratch_pool, scratch_pool));
+ conflict_description = apr_psprintf(scratch_pool, "%s\n%s",
+ incoming_change_description,
+ local_change_description);
}
- else if (err)
- return svn_error_trace(err);
- (*result)->choice = svn_wc_conflict_choose_merged;
- return SVN_NO_ERROR;
+ continue;
}
- /* else, fall through to prompting. */
- break;
- case svn_cl__accept_launch:
- if (desc->base_abspath && desc->their_abspath
- && desc->my_abspath && desc->merged_file)
+ else if (strcmp(opt->code, "w") == 0)
{
- svn_boolean_t remains_in_conflict;
-
- if (b->external_failed)
+ int preferred_move_target_idx;
+ apr_array_header_t *options;
+ svn_client_conflict_option_t *conflict_option;
+
+ SVN_ERR(prompt_move_target_path(&preferred_move_target_idx,
+ possible_moved_to_abspaths, TRUE,
+ pb, local_abspath, ctx, iterpool));
+
+ /* Update preferred move target path. */
+ SVN_ERR(svn_client_conflict_tree_get_resolution_options(&options,
+ conflict,
+ ctx,
+ iterpool,
+ iterpool));
+ conflict_option =
+ svn_client_conflict_option_find_by_id(
+ options,
+ svn_client_conflict_option_incoming_move_file_text_merge);
+ if (conflict_option == NULL)
{
- (*result)->choice = svn_wc_conflict_choose_postpone;
- return SVN_NO_ERROR;
+ conflict_option =
+ svn_client_conflict_option_find_by_id(
+ options, svn_client_conflict_option_incoming_move_dir_merge);
}
- err = svn_cl__merge_file_externally(desc->base_abspath,
- desc->their_abspath,
- desc->my_abspath,
- desc->merged_file,
- desc->local_abspath,
- b->config,
- &remains_in_conflict,
- scratch_pool);
- if (err && (err->apr_err == SVN_ERR_CL_NO_EXTERNAL_MERGE_TOOL ||
- err->apr_err == SVN_ERR_EXTERNAL_PROGRAM))
+ if (conflict_option)
{
- char buf[1024];
- const char *message;
-
- message = svn_err_best_message(err, buf, sizeof(buf));
- SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "%s\n",
- message));
- b->external_failed = TRUE;
- return svn_error_trace(err);
+ SVN_ERR(svn_client_conflict_option_set_moved_to_abspath(
+ conflict_option, preferred_move_target_idx, ctx,
+ iterpool));
+ wc_move_target_chosen = TRUE;
+
+ /* Update option description. */
+ SVN_ERR(build_tree_conflict_options(
+ &tree_conflict_options,
+ &possible_moved_to_repos_relpaths,
+ &possible_moved_to_abspaths,
+ NULL, conflict, ctx,
+ scratch_pool, scratch_pool));
}
- else if (err)
- return svn_error_trace(err);
-
- if (remains_in_conflict)
- (*result)->choice = svn_wc_conflict_choose_postpone;
- else
- (*result)->choice = svn_wc_conflict_choose_merged;
- return SVN_NO_ERROR;
+ continue;
+ }
+ else if (opt->choice != svn_client_conflict_option_undefined)
+ {
+ option_id = opt->choice;
+ break;
}
- /* else, fall through to prompting. */
- break;
}
-
- /* Print a summary of conflicts before starting interactive resolution */
- if (! b->printed_summary)
+ svn_pool_destroy(iterpool);
+ if (option_id != svn_client_conflict_option_unspecified &&
+ option_id != svn_client_conflict_option_postpone)
{
- SVN_ERR(svn_cl__print_conflict_stats(b->conflict_stats, scratch_pool));
- b->printed_summary = TRUE;
+ SVN_ERR(mark_conflict_resolved(conflict, option_id,
+ FALSE, NULL, TRUE,
+ path_prefix, conflict_stats,
+ ctx, scratch_pool));
+ *resolved = TRUE;
+ }
+ else
+ {
+ *resolved = FALSE;
+ *postponed = (option_id == svn_client_conflict_option_postpone);
}
- /* We're in interactive mode and either the user gave no --accept
- option or the option did not apply; let's prompt. */
-
- /* Handle the most common cases, which is either:
+ return SVN_NO_ERROR;
+}
- Conflicting edits on a file's text, or
- Conflicting edits on a property.
- */
- if (((desc->kind == svn_wc_conflict_kind_text)
- && (desc->action == svn_wc_conflict_action_edit)
- && (desc->reason == svn_wc_conflict_reason_edited)))
- SVN_ERR(handle_text_conflict(*result, desc, b, scratch_pool));
- else if (desc->kind == svn_wc_conflict_kind_property)
- SVN_ERR(handle_prop_conflict(*result, desc, b, result_pool, scratch_pool));
- else if (desc->kind == svn_wc_conflict_kind_tree)
- SVN_ERR(handle_tree_conflict(*result, desc, b, scratch_pool));
+static svn_error_t *
+resolve_conflict_interactively(svn_boolean_t *resolved,
+ svn_boolean_t *postponed,
+ svn_boolean_t *quit,
+ svn_boolean_t *external_failed,
+ svn_boolean_t *printed_summary,
+ svn_boolean_t *printed_description,
+ svn_client_conflict_t *conflict,
+ const char *editor_cmd,
+ apr_hash_t *config,
+ const char *path_prefix,
+ svn_cmdline_prompt_baton_t *pb,
+ svn_cl__conflict_stats_t *conflict_stats,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ svn_boolean_t text_conflicted;
+ apr_array_header_t *props_conflicted;
+ svn_boolean_t tree_conflicted;
+ const svn_string_t *merged_propval;
+
+ SVN_ERR(svn_client_conflict_get_conflicted(&text_conflicted,
+ &props_conflicted,
+ &tree_conflicted,
+ conflict,
+ scratch_pool,
+ scratch_pool));
- else /* other types of conflicts -- do nothing about them. */
+ /* Print a summary of conflicts before starting interactive resolution */
+ if (! *printed_summary)
{
- (*result)->choice = svn_wc_conflict_choose_postpone;
+ SVN_ERR(svn_cl__print_conflict_stats(conflict_stats, scratch_pool));
+ *printed_summary = TRUE;
}
+ *resolved = FALSE;
+ if (text_conflicted
+ && (svn_client_conflict_get_incoming_change(conflict) ==
+ svn_wc_conflict_action_edit)
+ && (svn_client_conflict_get_local_change(conflict) ==
+ svn_wc_conflict_reason_edited))
+ SVN_ERR(handle_text_conflict(resolved, postponed, quit, printed_description,
+ conflict, path_prefix, pb, editor_cmd, config,
+ conflict_stats, ctx, scratch_pool));
+ if (props_conflicted->nelts > 0)
+ SVN_ERR(handle_prop_conflicts(resolved, postponed, quit, &merged_propval,
+ path_prefix, pb, editor_cmd, config, conflict,
+ conflict_stats, ctx, result_pool, scratch_pool));
+ if (tree_conflicted)
+ SVN_ERR(handle_tree_conflict(resolved, postponed, quit, printed_description,
+ conflict, path_prefix, pb, conflict_stats, ctx,
+ scratch_pool));
+
return SVN_NO_ERROR;
}
svn_error_t *
-svn_cl__conflict_func_interactive(svn_wc_conflict_result_t **result,
- const svn_wc_conflict_description2_t *desc,
- void *baton,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
+svn_cl__resolve_conflict(svn_boolean_t *quit,
+ svn_boolean_t *external_failed,
+ svn_boolean_t *printed_summary,
+ svn_client_conflict_t *conflict,
+ svn_cl__accept_t accept_which,
+ const char *editor_cmd,
+ const char *path_prefix,
+ svn_cmdline_prompt_baton_t *pb,
+ svn_cl__conflict_stats_t *conflict_stats,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
{
- svn_cl__interactive_conflict_baton_t *b = baton;
+ svn_boolean_t text_conflicted;
+ apr_array_header_t *props_conflicted;
+ svn_boolean_t tree_conflicted;
+ const char *local_abspath;
+ svn_client_conflict_option_id_t option_id;
+
+ SVN_ERR(svn_client_conflict_get_conflicted(&text_conflicted,
+ &props_conflicted,
+ &tree_conflicted,
+ conflict,
+ scratch_pool,
+ scratch_pool));
+ local_abspath = svn_client_conflict_get_local_abspath(conflict);
+
+ if (accept_which == svn_cl__accept_unspecified)
+ {
+ option_id = svn_client_conflict_option_unspecified;
+ }
+ else if (accept_which == svn_cl__accept_postpone)
+ {
+ option_id = svn_client_conflict_option_postpone;
+ }
+ else if (accept_which == svn_cl__accept_base)
+ {
+ option_id = svn_client_conflict_option_base_text;
+ }
+ else if (accept_which == svn_cl__accept_working)
+ {
+ option_id = svn_client_conflict_option_merged_text;
- SVN_ERR(conflict_func_interactive(result, desc, baton,
- result_pool, scratch_pool));
+ if (text_conflicted)
+ {
+ const char *mime_type =
+ svn_client_conflict_text_get_mime_type(conflict);
- /* If we are resolving a conflict, adjust the summary of conflicts. */
- if ((*result)->choice != svn_wc_conflict_choose_postpone)
+ /* There is no merged text for binary conflicts, behave as
+ * if 'mine-full' was chosen. */
+ if (mime_type && svn_mime_type_is_binary(mime_type))
+ option_id = svn_client_conflict_option_working_text;
+ }
+ else if (tree_conflicted)
+ {
+ /* For tree conflicts, map 'working' to 'accept current working
+ * copy state'. */
+ option_id = svn_client_conflict_option_accept_current_wc_state;
+ }
+ }
+ else if (accept_which == svn_cl__accept_theirs_conflict)
+ {
+ option_id = svn_client_conflict_option_incoming_text_where_conflicted;
+ }
+ else if (accept_which == svn_cl__accept_mine_conflict)
{
- const char *local_path
- = svn_cl__local_style_skip_ancestor(
- b->path_prefix, desc->local_abspath, scratch_pool);
+ option_id = svn_client_conflict_option_working_text_where_conflicted;
+
+ if (tree_conflicted)
+ {
+ svn_wc_operation_t operation;
+
+ operation = svn_client_conflict_get_operation(conflict);
+ if (operation == svn_wc_operation_update ||
+ operation == svn_wc_operation_switch)
+ {
+ svn_wc_conflict_reason_t reason;
- svn_cl__conflict_stats_resolved(b->conflict_stats, local_path,
- desc->kind);
+ reason = svn_client_conflict_get_local_change(conflict);
+ if (reason == svn_wc_conflict_reason_moved_away)
+ {
+ /* Map 'mine-conflict' to 'update move destination'. */
+ option_id =
+ svn_client_conflict_option_update_move_destination;
+ }
+ else if (reason == svn_wc_conflict_reason_deleted ||
+ reason == svn_wc_conflict_reason_replaced)
+ {
+ svn_wc_conflict_action_t action;
+ svn_node_kind_t node_kind;
+
+ action = svn_client_conflict_get_incoming_change(conflict);
+ node_kind =
+ svn_client_conflict_tree_get_victim_node_kind(conflict);
+
+ if (action == svn_wc_conflict_action_edit &&
+ node_kind == svn_node_dir)
+ {
+ /* Map 'mine-conflict' to 'update any moved away
+ * children'. */
+ option_id =
+ svn_client_conflict_option_update_any_moved_away_children;
+ }
+ }
+ }
+ }
+ }
+ else if (accept_which == svn_cl__accept_theirs_full)
+ {
+ option_id = svn_client_conflict_option_incoming_text;
}
+ else if (accept_which == svn_cl__accept_mine_full)
+ {
+ option_id = svn_client_conflict_option_working_text;
+ }
+ else if (accept_which == svn_cl__accept_edit)
+ {
+ option_id = svn_client_conflict_option_unspecified;
+
+ if (local_abspath)
+ {
+ if (*external_failed)
+ {
+ option_id = svn_client_conflict_option_postpone;
+ }
+ else
+ {
+ svn_error_t *err;
+
+ err = svn_cmdline__edit_file_externally(local_abspath,
+ editor_cmd,
+ ctx->config,
+ scratch_pool);
+ if (err && (err->apr_err == SVN_ERR_CL_NO_EXTERNAL_EDITOR ||
+ err->apr_err == SVN_ERR_EXTERNAL_PROGRAM))
+ {
+ char buf[1024];
+ const char *message;
+
+ message = svn_err_best_message(err, buf, sizeof(buf));
+ SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "%s\n",
+ message));
+ svn_error_clear(err);
+ *external_failed = TRUE;
+ }
+ else if (err)
+ return svn_error_trace(err);
+ option_id = svn_client_conflict_option_merged_text;
+ }
+ }
+ }
+ else if (accept_which == svn_cl__accept_launch)
+ {
+ const char *base_abspath = NULL;
+ const char *my_abspath = NULL;
+ const char *their_abspath = NULL;
+
+ option_id = svn_client_conflict_option_unspecified;
+
+ if (text_conflicted)
+ SVN_ERR(svn_client_conflict_text_get_contents(NULL, &my_abspath,
+ &base_abspath,
+ &their_abspath,
+ conflict, scratch_pool,
+ scratch_pool));
+
+ if (base_abspath && their_abspath && my_abspath && local_abspath)
+ {
+ if (*external_failed)
+ {
+ option_id = svn_client_conflict_option_postpone;
+ }
+ else
+ {
+ svn_boolean_t remains_in_conflict;
+ svn_error_t *err;
+
+ err = svn_cl__merge_file_externally(base_abspath, their_abspath,
+ my_abspath, local_abspath,
+ local_abspath, ctx->config,
+ &remains_in_conflict,
+ scratch_pool);
+ if (err && (err->apr_err == SVN_ERR_CL_NO_EXTERNAL_MERGE_TOOL ||
+ err->apr_err == SVN_ERR_EXTERNAL_PROGRAM))
+ {
+ char buf[1024];
+ const char *message;
+
+ message = svn_err_best_message(err, buf, sizeof(buf));
+ SVN_ERR(svn_cmdline_fprintf(stderr, scratch_pool, "%s\n",
+ message));
+ *external_failed = TRUE;
+ return svn_error_trace(err);
+ }
+ else if (err)
+ return svn_error_trace(err);
+
+ if (remains_in_conflict)
+ option_id = svn_client_conflict_option_postpone;
+ else
+ option_id = svn_client_conflict_option_merged_text;
+ }
+ }
+ }
+ else if (accept_which == svn_cl__accept_recommended)
+ {
+ svn_client_conflict_option_id_t recommended_id;
+
+ if (tree_conflicted)
+ SVN_ERR(svn_client_conflict_tree_get_details(conflict, ctx,
+ scratch_pool));
+ recommended_id = svn_client_conflict_get_recommended_option_id(conflict);
+ if (recommended_id != svn_client_conflict_option_unspecified)
+ option_id = recommended_id;
+ else
+ option_id = svn_client_conflict_option_postpone;
+ }
+ else
+ SVN_ERR_MALFUNCTION();
+
+ /* If we are in interactive mode and either the user gave no --accept
+ * option or the option did not apply, then prompt. */
+ if (option_id == svn_client_conflict_option_unspecified)
+ {
+ svn_boolean_t resolved = FALSE;
+ svn_boolean_t postponed = FALSE;
+ svn_boolean_t printed_description = FALSE;
+ svn_error_t *err;
+
+ *quit = FALSE;
+
+ while (!resolved && !postponed && !*quit)
+ {
+ err = resolve_conflict_interactively(&resolved, &postponed, quit,
+ external_failed,
+ printed_summary,
+ &printed_description,
+ conflict,
+ editor_cmd, ctx->config,
+ path_prefix, pb,
+ conflict_stats, ctx,
+ scratch_pool, scratch_pool);
+ if (err && err->apr_err == SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE)
+ {
+ /* Conflict resolution has failed. Let the user try again.
+ * It is always possible to break out of this loop with
+ * the 'quit' or 'postpone' options. */
+ svn_handle_warning2(stderr, err, "svn: ");
+ svn_error_clear(err);
+ err = SVN_NO_ERROR;
+ }
+ SVN_ERR(err);
+ }
+ }
+ else if (option_id != svn_client_conflict_option_postpone)
+ SVN_ERR(mark_conflict_resolved(conflict, option_id,
+ text_conflicted,
+ props_conflicted->nelts > 0 ? "" : NULL,
+ tree_conflicted,
+ path_prefix, conflict_stats,
+ ctx, scratch_pool));
+
return SVN_NO_ERROR;
}
diff --git a/subversion/svn/diff-cmd.c b/subversion/svn/diff-cmd.c
index 71853c7f2776..9e389ec856a6 100644
--- a/subversion/svn/diff-cmd.c
+++ b/subversion/svn/diff-cmd.c
@@ -233,6 +233,43 @@ svn_cl__diff(apr_getopt_t *os,
svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "paths", SVN_VA_NULL);
SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout));
}
+ if (opt_state->diff.summarize)
+ {
+ if (opt_state->diff.use_git_diff_format)
+ return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'%s' not valid with '--summarize' option"),
+ "--git");
+ if (opt_state->diff.patch_compatible)
+ return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'%s' not valid with '--summarize' option"),
+ "--patch-compatible");
+ if (opt_state->diff.show_copies_as_adds)
+ return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'%s' not valid with '--summarize' option"),
+ "--show-copies-as-adds");
+ if (opt_state->diff.internal_diff)
+ return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'%s' not valid with '--summarize' option"),
+ "--internal-diff");
+ if (opt_state->diff.diff_cmd)
+ return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'%s' not valid with '--summarize' option"),
+ "--diff-cmd");
+ if (opt_state->diff.no_diff_added)
+ return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'%s' not valid with '--summarize' option"),
+ "--no-diff-added");
+ if (opt_state->diff.no_diff_deleted)
+ return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'%s' not valid with '--summarize' option"),
+ "--no-diff-deleted");
+ if (opt_state->force)
+ return svn_error_createf(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'%s' not valid with '--summarize' option"),
+ "--force");
+ /* Not handling ignore-properties, and properties-only as there should
+ be a patch adding support for these being applied soon */
+ }
SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
opt_state->targets,
diff --git a/subversion/svn/help-cmd.c b/subversion/svn/help-cmd.c
index b095d30fd1c2..fc3558683033 100644
--- a/subversion/svn/help-cmd.c
+++ b/subversion/svn/help-cmd.c
@@ -53,8 +53,9 @@ svn_cl__help(apr_getopt_t *os,
N_("usage: svn <subcommand> [options] [args]\n"
"Subversion command-line client.\n"
"Type 'svn help <subcommand>' for help on a specific subcommand.\n"
- "Type 'svn --version' to see the program version and RA modules\n"
- " or 'svn --version --quiet' to see just the version number.\n"
+ "Type 'svn --version' to see the program version and RA modules,\n"
+ " 'svn --version --verbose' to see dependency versions as well,\n"
+ " 'svn --version --quiet' to see just the version number.\n"
"\n"
"Most subcommands take file and/or directory arguments, recursing\n"
"on the directories. If no arguments are supplied to such a\n"
@@ -147,21 +148,24 @@ svn_cl__help(apr_getopt_t *os,
_("\nThe following authentication credential caches are available:\n\n"));
/*### There is no API to query available providers at run time. */
+ if (config_path)
+ {
#if (defined(WIN32) && !defined(__MINGW32__))
- version_footer =
- svn_stringbuf_create(apr_psprintf(pool, _("%s* Wincrypt cache in %s\n"),
- version_footer->data,
- svn_dirent_local_style(config_path,
- pool)),
- pool);
+ version_footer =
+ svn_stringbuf_create(apr_psprintf(pool, _("%s* Wincrypt cache in %s\n"),
+ version_footer->data,
+ svn_dirent_local_style(config_path,
+ pool)),
+ pool);
#elif !defined(SVN_DISABLE_PLAINTEXT_PASSWORD_STORAGE)
- version_footer =
- svn_stringbuf_create(apr_psprintf(pool, _("%s* Plaintext cache in %s\n"),
- version_footer->data,
- svn_dirent_local_style(config_path,
- pool)),
- pool);
+ version_footer =
+ svn_stringbuf_create(apr_psprintf(pool, _("%s* Plaintext cache in %s\n"),
+ version_footer->data,
+ svn_dirent_local_style(config_path,
+ pool)),
+ pool);
#endif
+ }
#ifdef SVN_HAVE_GNOME_KEYRING
svn_stringbuf_appendcstr(version_footer, "* Gnome Keyring\n");
#endif
@@ -181,7 +185,7 @@ svn_cl__help(apr_getopt_t *os,
opt_state ? opt_state->quiet : FALSE,
opt_state ? opt_state->verbose : FALSE,
version_footer->data,
- help_header, /* already gettext()'d */
+ _(help_header),
svn_cl__cmd_table,
svn_cl__options,
svn_cl__global_options,
diff --git a/subversion/svn/info-cmd.c b/subversion/svn/info-cmd.c
index fafc398c5f6c..e0c0041b468a 100644
--- a/subversion/svn/info-cmd.c
+++ b/subversion/svn/info-cmd.c
@@ -162,6 +162,9 @@ typedef struct print_info_baton_t
/* Did we already print a line of output? */
svn_boolean_t start_new_line;
+
+ /* The client context. */
+ svn_client_ctx_t *ctx;
} print_info_baton_t;
@@ -391,15 +394,24 @@ print_info_xml(void *baton,
if (info->wc_info && info->wc_info->conflicts)
{
int i;
+ apr_pool_t *iterpool;
+ iterpool = svn_pool_create(pool);
for (i = 0; i < info->wc_info->conflicts->nelts; i++)
{
- const svn_wc_conflict_description2_t *conflict =
+ const svn_wc_conflict_description2_t *desc =
APR_ARRAY_IDX(info->wc_info->conflicts, i,
const svn_wc_conflict_description2_t *);
+ svn_client_conflict_t *conflict;
+
+ svn_pool_clear(iterpool);
- SVN_ERR(svn_cl__append_conflict_info_xml(sb, conflict, pool));
+ SVN_ERR(svn_client_conflict_get(&conflict, desc->local_abspath,
+ receiver_baton->ctx,
+ iterpool, iterpool));
+ SVN_ERR(svn_cl__append_conflict_info_xml(sb, conflict, iterpool));
}
+ svn_pool_destroy(iterpool);
}
if (info->lock)
@@ -581,68 +593,93 @@ print_info(void *baton,
if (info->wc_info->conflicts)
{
- svn_boolean_t printed_prop_conflict_file = FALSE;
svn_boolean_t printed_tc = FALSE;
- int i;
+ svn_stringbuf_t *conflicted_props = NULL;
+ svn_client_conflict_t *conflict;
+ svn_boolean_t text_conflicted;
+ apr_array_header_t *props_conflicted;
+ svn_boolean_t tree_conflicted;
+ const svn_wc_conflict_description2_t *desc2 =
+ APR_ARRAY_IDX(info->wc_info->conflicts, 0,
+ const svn_wc_conflict_description2_t *);
+
+ SVN_ERR(svn_client_conflict_get(&conflict, desc2->local_abspath,
+ receiver_baton->ctx, pool, pool));
+ SVN_ERR(svn_client_conflict_get_conflicted(&text_conflicted,
+ &props_conflicted,
+ &tree_conflicted,
+ conflict, pool, pool));
+ if (text_conflicted)
+ {
+ const char *base_abspath = NULL;
+ const char *my_abspath = NULL;
+ const char *their_abspath = NULL;
+
+ SVN_ERR(svn_client_conflict_text_get_contents(
+ NULL, &my_abspath, &base_abspath, &their_abspath,
+ conflict, pool, pool));
+
+ if (base_abspath)
+ SVN_ERR(svn_cmdline_printf(pool,
+ _("Conflict Previous Base File: %s\n"),
+ svn_cl__local_style_skip_ancestor(
+ receiver_baton->path_prefix,
+ base_abspath,
+ pool)));
+
+ if (my_abspath)
+ SVN_ERR(svn_cmdline_printf(pool,
+ _("Conflict Previous Working File: %s\n"),
+ svn_cl__local_style_skip_ancestor(
+ receiver_baton->path_prefix,
+ my_abspath,
+ pool)));
+
+ if (their_abspath)
+ SVN_ERR(svn_cmdline_printf(pool,
+ _("Conflict Current Base File: %s\n"),
+ svn_cl__local_style_skip_ancestor(
+ receiver_baton->path_prefix,
+ their_abspath,
+ pool)));
+ }
- for (i = 0; i < info->wc_info->conflicts->nelts; i++)
+ if (props_conflicted)
{
- const svn_wc_conflict_description2_t *conflict =
- APR_ARRAY_IDX(info->wc_info->conflicts, i,
- const svn_wc_conflict_description2_t *);
- const char *desc;
+ int i;
- switch (conflict->kind)
+ for (i = 0; i < props_conflicted->nelts; i++)
{
- case svn_wc_conflict_kind_text:
- if (conflict->base_abspath)
- SVN_ERR(svn_cmdline_printf(pool,
- _("Conflict Previous Base File: %s\n"),
- svn_cl__local_style_skip_ancestor(
- receiver_baton->path_prefix,
- conflict->base_abspath,
- pool)));
-
- if (conflict->my_abspath)
- SVN_ERR(svn_cmdline_printf(pool,
- _("Conflict Previous Working File: %s\n"),
- svn_cl__local_style_skip_ancestor(
- receiver_baton->path_prefix,
- conflict->my_abspath,
- pool)));
-
- if (conflict->their_abspath)
- SVN_ERR(svn_cmdline_printf(pool,
- _("Conflict Current Base File: %s\n"),
- svn_cl__local_style_skip_ancestor(
- receiver_baton->path_prefix,
- conflict->their_abspath,
- pool)));
- break;
-
- case svn_wc_conflict_kind_property:
- if (! printed_prop_conflict_file)
- SVN_ERR(svn_cmdline_printf(pool,
- _("Conflict Properties File: %s\n"),
- svn_cl__local_style_skip_ancestor(
- receiver_baton->path_prefix,
- conflict->prop_reject_abspath,
- pool)));
- printed_prop_conflict_file = TRUE;
- break;
-
- case svn_wc_conflict_kind_tree:
- printed_tc = TRUE;
- SVN_ERR(
- svn_cl__get_human_readable_tree_conflict_description(
- &desc, conflict, pool));
-
- SVN_ERR(svn_cmdline_printf(pool, "%s: %s\n",
- _("Tree conflict"), desc));
- break;
+ const char *name;
+
+ name = APR_ARRAY_IDX(props_conflicted, i, const char *);
+ if (conflicted_props == NULL)
+ conflicted_props = svn_stringbuf_create(name, pool);
+ else
+ {
+ svn_stringbuf_appendbyte(conflicted_props, ' ');
+ svn_stringbuf_appendcstr(conflicted_props, name);
+ }
}
}
+ if (tree_conflicted)
+ {
+ const char *desc;
+
+ printed_tc = TRUE;
+ SVN_ERR(
+ svn_cl__get_human_readable_tree_conflict_description(
+ &desc, conflict, pool));
+
+ SVN_ERR(svn_cmdline_printf(pool, "%s: %s\n",
+ _("Tree conflict"), desc));
+ }
+
+ if (conflicted_props)
+ SVN_ERR(svn_cmdline_printf(pool, _("Conflicted Properties: %s\n"),
+ conflicted_props->data));
+
/* We only store one left and right version for all conflicts, which is
referenced from all conflicts.
Print it after the conflicts to match the 1.6/1.7 output where it is
@@ -650,30 +687,40 @@ print_info(void *baton,
{
const char *src_left_version;
const char *src_right_version;
- const svn_wc_conflict_description2_t *conflict =
- APR_ARRAY_IDX(info->wc_info->conflicts, 0,
- const svn_wc_conflict_description2_t *);
+ const char *repos_root_url;
+ const char *repos_relpath;
+ svn_revnum_t peg_rev;
+ svn_node_kind_t node_kind;
if (!printed_tc)
{
const char *desc;
SVN_ERR(svn_cl__get_human_readable_action_description(&desc,
- svn_wc_conflict_action_edit,
- conflict->operation,
- conflict->node_kind, pool));
+ svn_wc_conflict_action_edit,
+ svn_client_conflict_get_operation(conflict),
+ info->kind,
+ pool));
SVN_ERR(svn_cmdline_printf(pool, "%s: %s\n",
_("Conflict Details"), desc));
}
+ SVN_ERR(svn_client_conflict_get_repos_info(&repos_root_url, NULL,
+ conflict, pool, pool));
+ SVN_ERR(svn_client_conflict_get_incoming_old_repos_location(
+ &repos_relpath, &peg_rev, &node_kind, conflict,
+ pool, pool));
src_left_version =
- svn_cl__node_description(conflict->src_left_version,
- info->repos_root_URL, pool);
+ svn_cl__node_description(repos_root_url, repos_relpath,
+ peg_rev, node_kind, info->repos_root_URL, pool);
+ SVN_ERR(svn_client_conflict_get_incoming_new_repos_location(
+ &repos_relpath, &peg_rev, &node_kind, conflict,
+ pool, pool));
src_right_version =
- svn_cl__node_description(conflict->src_right_version,
- info->repos_root_URL, pool);
+ svn_cl__node_description(repos_root_url, repos_relpath,
+ peg_rev, node_kind, info->repos_root_URL, pool);
if (src_left_version)
SVN_ERR(svn_cmdline_printf(pool, " %s: %s\n",
@@ -874,6 +921,8 @@ svn_cl__info(apr_getopt_t *os,
/* Add "." if user passed 0 arguments. */
svn_opt_push_implicit_dot_target(targets, pool);
+ receiver_baton.ctx = ctx;
+
if (opt_state->xml)
{
receiver = print_info_xml;
diff --git a/subversion/svn/list-cmd.c b/subversion/svn/list-cmd.c
index 5ea140fc93d1..da18252243d9 100644
--- a/subversion/svn/list-cmd.c
+++ b/subversion/svn/list-cmd.c
@@ -351,6 +351,8 @@ svn_cl__list(apr_getopt_t *os,
const char *target = APR_ARRAY_IDX(targets, i, const char *);
const char *truepath;
svn_opt_revision_t peg_revision;
+ apr_array_header_t *patterns = NULL;
+ int k;
/* Initialize the following variables for
every list target. */
@@ -375,8 +377,33 @@ svn_cl__list(apr_getopt_t *os,
SVN_ERR(svn_cl__error_checked_fputs(sb->data, stdout));
}
- err = svn_client_list3(truepath, &peg_revision,
- &(opt_state->start_revision),
+ if (opt_state->search_patterns)
+ {
+ patterns = apr_array_make(subpool, 4, sizeof(const char *));
+ for (k = 0; k < opt_state->search_patterns->nelts; ++k)
+ {
+ apr_array_header_t *pattern_group
+ = APR_ARRAY_IDX(opt_state->search_patterns, k,
+ apr_array_header_t *);
+ const char *pattern;
+
+ /* Should never fail but ... */
+ if (pattern_group->nelts != 1)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'search-and' option is not supported"));
+
+ pattern = APR_ARRAY_IDX(pattern_group, 0, const char *);
+#if defined(WIN32)
+ /* As we currently can't pass glob patterns via the Windows
+ CLI, fall back to sub-string search. */
+ pattern = apr_psprintf(subpool, "*%s*", pattern);
+#endif
+ APR_ARRAY_PUSH(patterns, const char *) = pattern;
+ }
+ }
+
+ err = svn_client_list4(truepath, &peg_revision,
+ &(opt_state->start_revision), patterns,
opt_state->depth,
dirent_fields,
(opt_state->xml || opt_state->verbose),
diff --git a/subversion/svn/log-cmd.c b/subversion/svn/log-cmd.c
index 44f8a4cff292..57f841506914 100644
--- a/subversion/svn/log-cmd.c
+++ b/subversion/svn/log-cmd.c
@@ -38,6 +38,7 @@
#include "private/svn_cmdline_private.h"
#include "private/svn_sorts_private.h"
+#include "private/svn_utf_private.h"
#include "cl.h"
#include "cl-log.h"
@@ -110,6 +111,24 @@ display_diff(const svn_log_entry_t *log_entry,
return SVN_NO_ERROR;
}
+/* Return TRUE if STR matches PATTERN. Else, return FALSE. Assumes that
+ * PATTERN is a UTF-8 string prepared for case- and accent-insensitive
+ * comparison via svn_utf__xfrm(). */
+static svn_boolean_t
+match(const char *pattern, const char *str, svn_membuf_t *buf)
+{
+ svn_error_t *err;
+
+ err = svn_utf__xfrm(&str, str, strlen(str), TRUE, TRUE, buf);
+ if (err)
+ {
+ /* Can't match invalid data. */
+ svn_error_clear(err);
+ return FALSE;
+ }
+
+ return apr_fnmatch(pattern, str, 0) == APR_SUCCESS;
+}
/* Return TRUE if SEARCH_PATTERN matches the AUTHOR, DATE, LOG_MESSAGE,
* or a path in the set of keys of the CHANGED_PATHS hash. Else, return FALSE.
@@ -120,22 +139,22 @@ match_search_pattern(const char *search_pattern,
const char *date,
const char *log_message,
apr_hash_t *changed_paths,
+ svn_membuf_t *buf,
apr_pool_t *pool)
{
/* Match any substring containing the pattern, like UNIX 'grep' does. */
const char *pattern = apr_psprintf(pool, "*%s*", search_pattern);
- int flags = 0;
/* Does the author match the search pattern? */
- if (author && apr_fnmatch(pattern, author, flags) == APR_SUCCESS)
+ if (author && match(pattern, author, buf))
return TRUE;
/* Does the date the search pattern? */
- if (date && apr_fnmatch(pattern, date, flags) == APR_SUCCESS)
+ if (date && match(pattern, date, buf))
return TRUE;
/* Does the log message the search pattern? */
- if (log_message && apr_fnmatch(pattern, log_message, flags) == APR_SUCCESS)
+ if (log_message && match(pattern, log_message, buf))
return TRUE;
if (changed_paths)
@@ -150,15 +169,14 @@ match_search_pattern(const char *search_pattern,
const char *path = apr_hash_this_key(hi);
svn_log_changed_path2_t *log_item;
- if (apr_fnmatch(pattern, path, flags) == APR_SUCCESS)
+ if (match(pattern, path, buf))
return TRUE;
/* Match copy-from paths, too. */
log_item = apr_hash_this_val(hi);
if (log_item->copyfrom_path
&& SVN_IS_VALID_REVNUM(log_item->copyfrom_rev)
- && apr_fnmatch(pattern,
- log_item->copyfrom_path, flags) == APR_SUCCESS)
+ && match(pattern, log_item->copyfrom_path, buf))
return TRUE;
}
}
@@ -168,13 +186,14 @@ match_search_pattern(const char *search_pattern,
/* Match all search patterns in SEARCH_PATTERNS against AUTHOR, DATE, MESSAGE,
* and CHANGED_PATHS. Return TRUE if any pattern matches, else FALSE.
- * SCRACH_POOL is used for temporary allocations. */
+ * BUF and SCRATCH_POOL are used for temporary allocations. */
static svn_boolean_t
match_search_patterns(apr_array_header_t *search_patterns,
const char *author,
const char *date,
const char *message,
apr_hash_t *changed_paths,
+ svn_membuf_t *buf,
apr_pool_t *scratch_pool)
{
int i;
@@ -197,7 +216,7 @@ match_search_patterns(apr_array_header_t *search_patterns,
pattern = APR_ARRAY_IDX(pattern_group, j, const char *);
match = match_search_pattern(pattern, author, date, message,
- changed_paths, iterpool);
+ changed_paths, buf, iterpool);
if (!match)
break;
}
@@ -331,7 +350,7 @@ svn_cl__log_entry_receiver(void *baton,
if (lb->search_patterns &&
! match_search_patterns(lb->search_patterns, author, date, message,
- log_entry->changed_paths2, pool))
+ log_entry->changed_paths2, &lb->buffer, pool))
{
if (log_entry->has_children)
{
@@ -535,7 +554,7 @@ svn_cl__log_entry_receiver_xml(void *baton,
/* Match search pattern before XML-escaping. */
if (lb->search_patterns &&
! match_search_patterns(lb->search_patterns, author, date, message,
- log_entry->changed_paths2, pool))
+ log_entry->changed_paths2, &lb->buffer, pool))
{
if (log_entry->has_children)
{
@@ -795,6 +814,7 @@ svn_cl__log(apr_getopt_t *os,
lb.diff_extensions = opt_state->extensions;
lb.merge_stack = NULL;
lb.search_patterns = opt_state->search_patterns;
+ svn_membuf__create(&lb.buffer, 0, pool);
lb.pool = pool;
if (opt_state->xml)
diff --git a/subversion/svn/merge-cmd.c b/subversion/svn/merge-cmd.c
index cbc818b89f7c..f5c19198a5b2 100644
--- a/subversion/svn/merge-cmd.c
+++ b/subversion/svn/merge-cmd.c
@@ -150,6 +150,85 @@ run_merge(svn_boolean_t two_sources_specified,
return merge_err;
}
+/* Baton type for conflict_func_merge_cmd(). */
+struct conflict_func_merge_cmd_baton {
+ svn_cl__accept_t accept_which;
+ const char *path_prefix;
+ svn_cl__conflict_stats_t *conflict_stats;
+};
+
+/* This implements the `svn_wc_conflict_resolver_func2_t ' interface.
+ *
+ * The merge subcommand needs to install this legacy conflict callback
+ * in case the user passed an --accept option to 'svn merge'.
+ * Otherwise, merges involving multiple editor drives might encounter a
+ * conflict during one of the editor drives and abort with an error,
+ * rather than resolving conflicts as per the --accept option and
+ * continuing with the next editor drive.
+ * ### TODO add an svn_client_merge API that makes this callback unnecessary
+ */
+static svn_error_t *
+conflict_func_merge_cmd(svn_wc_conflict_result_t **result,
+ const svn_wc_conflict_description2_t *desc,
+ void *baton,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ struct conflict_func_merge_cmd_baton *b = baton;
+ svn_wc_conflict_choice_t choice;
+
+ switch (b->accept_which)
+ {
+ case svn_cl__accept_postpone:
+ case svn_cl__accept_invalid:
+ case svn_cl__accept_unspecified:
+ case svn_cl__accept_recommended:
+ /* Postpone or no valid --accept option, postpone the conflict. */
+ choice = svn_wc_conflict_choose_postpone;
+ break;
+ case svn_cl__accept_base:
+ choice = svn_wc_conflict_choose_base;
+ break;
+ case svn_cl__accept_working:
+ choice = svn_wc_conflict_choose_merged;
+ break;
+ case svn_cl__accept_mine_conflict:
+ choice = svn_wc_conflict_choose_mine_conflict;
+ break;
+ case svn_cl__accept_theirs_conflict:
+ choice = svn_wc_conflict_choose_theirs_conflict;
+ break;
+ case svn_cl__accept_mine_full:
+ choice = svn_wc_conflict_choose_mine_full;
+ break;
+ case svn_cl__accept_theirs_full:
+ choice = svn_wc_conflict_choose_theirs_full;
+ break;
+ case svn_cl__accept_edit:
+ case svn_cl__accept_launch:
+ /* The 'edit' and 'launch' options used to be valid in Subversion 1.9 but
+ * we can't support these options for the purposes of this callback. */
+ choice = svn_wc_conflict_choose_postpone;
+ break;
+ }
+
+ *result = svn_wc_create_conflict_result(choice, NULL, result_pool);
+
+ /* If we are resolving a conflict, adjust the summary of conflicts. */
+ if (choice != svn_wc_conflict_choose_postpone)
+ {
+ const char *local_path;
+
+ local_path = svn_cl__local_style_skip_ancestor(b->path_prefix,
+ desc->local_abspath,
+ scratch_pool);
+ svn_cl__conflict_stats_resolved(b->conflict_stats, local_path,
+ desc->kind);
+ }
+
+ return SVN_NO_ERROR;
+}
+
/* This implements the `svn_opt_subcommand_t' interface. */
svn_error_t *
svn_cl__merge(apr_getopt_t *os,
@@ -157,6 +236,8 @@ svn_cl__merge(apr_getopt_t *os,
apr_pool_t *pool)
{
svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
+ svn_cl__conflict_stats_t *conflict_stats =
+ ((svn_cl__cmd_baton_t *) baton)->conflict_stats;
svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
apr_array_header_t *targets;
const char *sourcepath1 = NULL, *sourcepath2 = NULL, *targetpath = "";
@@ -165,6 +246,7 @@ svn_cl__merge(apr_getopt_t *os,
svn_opt_revision_t first_range_start, first_range_end, peg_revision1,
peg_revision2;
apr_array_header_t *options, *ranges_to_merge = opt_state->revision_ranges;
+ apr_array_header_t *conflicted_paths;
svn_boolean_t has_explicit_target = FALSE;
/* Merge doesn't support specifying a revision or revision range
@@ -432,6 +514,22 @@ svn_cl__merge(apr_getopt_t *os,
"with --reintegrate"));
}
+ /* Install a legacy conflict handler if the --accept option was given.
+ * Else, svn_client_merge5() may abort the merge in an undesirable way.
+ * See the docstring at conflict_func_merge_cmd() for details */
+ if (opt_state->accept_which != svn_cl__accept_unspecified)
+ {
+ struct conflict_func_merge_cmd_baton *b = apr_pcalloc(pool, sizeof(*b));
+
+ b->accept_which = opt_state->accept_which;
+ SVN_ERR(svn_dirent_get_absolute(&b->path_prefix, "", pool));
+ b->conflict_stats = conflict_stats;
+
+ ctx->conflict_func2 = conflict_func_merge_cmd;
+ ctx->conflict_baton2 = b;
+ }
+
+retry:
merge_err = run_merge(two_sources_specified,
sourcepath1, peg_revision1,
sourcepath2,
@@ -447,6 +545,31 @@ svn_cl__merge(apr_getopt_t *os,
"fix invalid mergeinfo in target with 'svn propset'"));
}
+ /* Run the interactive resolver if conflicts were raised. */
+ SVN_ERR(svn_cl__conflict_stats_get_paths(&conflicted_paths, conflict_stats,
+ pool, pool));
+ if (conflicted_paths)
+ {
+ SVN_ERR(svn_cl__walk_conflicts(conflicted_paths, conflict_stats,
+ opt_state, ctx, pool));
+ if (merge_err &&
+ svn_error_root_cause(merge_err)->apr_err == SVN_ERR_WC_FOUND_CONFLICT)
+ {
+ svn_error_t *err;
+
+ /* Check if all conflicts were resolved just now. */
+ err = svn_cl__conflict_stats_get_paths(&conflicted_paths,
+ conflict_stats, pool, pool);
+ if (err)
+ merge_err = svn_error_compose_create(merge_err, err);
+ else if (conflicted_paths == NULL)
+ {
+ svn_error_clear(merge_err);
+ goto retry; /* ### conflicts resolved; continue merging */
+ }
+ }
+ }
+
if (!opt_state->quiet)
{
svn_error_t *err = svn_cl__notifier_print_conflict_stats(
diff --git a/subversion/svn/notify.c b/subversion/svn/notify.c
index c530694c7adf..c15301e32b8b 100644
--- a/subversion/svn/notify.c
+++ b/subversion/svn/notify.c
@@ -39,6 +39,7 @@
#include "svn_hash.h"
#include "cl.h"
#include "private/svn_subr_private.h"
+#include "private/svn_sorts_private.h"
#include "private/svn_dep_compat.h"
#include "svn_private_config.h"
@@ -53,6 +54,7 @@ struct notify_baton
svn_boolean_t is_wc_to_repos_copy;
svn_boolean_t sent_first_txdelta;
int in_external;
+ svn_revnum_t progress_revision;
svn_boolean_t had_print_error; /* Used to not keep printing error messages
when we've already had one print error. */
@@ -145,6 +147,76 @@ resolved_str(apr_pool_t *pool, int n_resolved)
}
svn_error_t *
+svn_cl__conflict_stats_get_paths(apr_array_header_t **conflicted_paths,
+ svn_cl__conflict_stats_t *conflict_stats,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+
+ int n_text = apr_hash_count(conflict_stats->text_conflicts);
+ int n_prop = apr_hash_count(conflict_stats->prop_conflicts);
+ int n_tree = apr_hash_count(conflict_stats->tree_conflicts);
+ apr_hash_t *all_conflicts;
+
+ *conflicted_paths = NULL;
+ if (n_text == 0 && n_prop == 0 && n_tree == 0)
+ return SVN_NO_ERROR;
+
+ /* Use a hash table to ensure paths with multiple conflicts are
+ * returned just once. */
+ all_conflicts = apr_hash_make(result_pool);
+ if (n_text > 0)
+ {
+ apr_array_header_t *k_text;
+ int i;
+
+ SVN_ERR(svn_hash_keys(&k_text, conflict_stats->text_conflicts,
+ scratch_pool));
+ for (i = 0; i < k_text->nelts; i++)
+ {
+ const char *path = APR_ARRAY_IDX(k_text, i, const char *);
+
+ svn_hash_sets(all_conflicts, path, "");
+ }
+ }
+
+ if (n_prop > 0)
+ {
+ apr_array_header_t *k_prop;
+ int i;
+
+ SVN_ERR(svn_hash_keys(&k_prop, conflict_stats->prop_conflicts,
+ scratch_pool));
+ for (i = 0; i < k_prop->nelts; i++)
+ {
+ const char *path = APR_ARRAY_IDX(k_prop, i, const char *);
+
+ svn_hash_sets(all_conflicts, path, "");
+ }
+ }
+
+ if (n_tree > 0)
+ {
+ apr_array_header_t *k_tree;
+ int i;
+
+ SVN_ERR(svn_hash_keys(&k_tree, conflict_stats->tree_conflicts,
+ scratch_pool));
+ for (i = 0; i < k_tree->nelts; i++)
+ {
+ const char *path = APR_ARRAY_IDX(k_tree, i, const char *);
+
+ svn_hash_sets(all_conflicts, path, "");
+ }
+ }
+
+ svn_hash_keys(conflicted_paths, all_conflicts, result_pool);
+ svn_sort__array(*conflicted_paths, svn_sort_compare_paths);
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
svn_cl__print_conflict_stats(svn_cl__conflict_stats_t *conflict_stats,
apr_pool_t *scratch_pool)
{
@@ -253,6 +325,13 @@ notify_body(struct notify_baton *nb,
_("Skipped target: '%s' -- copy-source is missing\n"),
path_local));
}
+ else if (n->content_state == svn_wc_notify_state_obstructed)
+ {
+ SVN_ERR(svn_cmdline_printf(
+ pool,
+ _("Skipped '%s' -- obstructed by unversioned node\n"),
+ path_local));
+ }
else
{
SVN_ERR(svn_cmdline_printf(pool, _("Skipped '%s'\n"), path_local));
@@ -372,6 +451,56 @@ notify_body(struct notify_baton *nb,
path_local));
break;
+ case svn_wc_notify_resolved_text:
+ SVN_ERR(svn_cmdline_printf(pool,
+ _("Merge conflicts in '%s' marked as "
+ "resolved.\n"),
+ path_local));
+ break;
+
+ case svn_wc_notify_resolved_prop:
+ SVN_ERR_ASSERT(n->prop_name && strlen(n->prop_name) > 0);
+ SVN_ERR(svn_cmdline_printf(pool,
+ _("Conflict in property '%s' at '%s' marked "
+ "as resolved.\n"),
+ n->prop_name, path_local));
+ break;
+
+ case svn_wc_notify_resolved_tree:
+ SVN_ERR(svn_cmdline_printf(pool,
+ _("Tree conflict at '%s' marked as "
+ "resolved.\n"),
+ path_local));
+ break;
+
+ case svn_wc_notify_begin_search_tree_conflict_details:
+ SVN_ERR(svn_cmdline_printf(pool,
+ _("Searching tree conflict details for '%s' "
+ "in repository:\n"),
+ path_local));
+ nb->progress_revision = 0;
+ break;
+
+ case svn_wc_notify_tree_conflict_details_progress:
+ /* First printf is to obliterate any previous progress printf,
+ assuming no more than 10 digit revisions. Avoid i18n so the
+ text length is known. We only need to do this if the new
+ revision is 4 digits less than the previous revision but that
+ requires counting digits. Dividing by 1000 works well
+ enough: it triggers when needed, it sometimes triggers when
+ not needed, but in typical cases it doesn't trigger as the
+ revisions don't vary much. */
+ if (n->revision < nb->progress_revision / 1000)
+ SVN_ERR(svn_cmdline_printf(pool, "\rChecking r "));
+ SVN_ERR(svn_cmdline_printf(pool, "\rChecking r%ld...", n->revision));
+ nb->progress_revision = n->revision;
+ break;
+
+ case svn_wc_notify_end_search_tree_conflict_details:
+ SVN_ERR(svn_cmdline_printf(pool, _(" done\n")));
+ nb->progress_revision = 0;
+ break;
+
case svn_wc_notify_add:
/* We *should* only get the MIME_TYPE if PATH is a file. If we
do get it, and the mime-type is not textual, note that this
@@ -415,8 +544,10 @@ notify_body(struct notify_baton *nb,
store_path(nb, nb->conflict_stats->prop_conflicts, path_local);
statchar_buf[1] = 'C';
}
+ else if (n->prop_state == svn_wc_notify_state_merged)
+ statchar_buf[1] = 'G';
else if (n->prop_state == svn_wc_notify_state_changed)
- statchar_buf[1] = 'U';
+ statchar_buf[1] = 'U';
if (statchar_buf[0] != ' ' || statchar_buf[1] != ' ')
{
@@ -1118,6 +1249,7 @@ svn_cl__get_notifier(svn_wc_notify_func2_t *notify_func_p,
nb->is_export = FALSE;
nb->is_wc_to_repos_copy = FALSE;
nb->in_external = 0;
+ nb->progress_revision = 0;
nb->had_print_error = FALSE;
nb->conflict_stats = conflict_stats;
SVN_ERR(svn_dirent_get_absolute(&nb->path_prefix, "", pool));
diff --git a/subversion/svn/propdel-cmd.c b/subversion/svn/propdel-cmd.c
index 28c95970b41d..b396b5dc3cf6 100644
--- a/subversion/svn/propdel-cmd.c
+++ b/subversion/svn/propdel-cmd.c
@@ -49,13 +49,13 @@ svn_cl__propdel(apr_getopt_t *os,
{
svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
- const char *pname, *pname_utf8;
+ const char *pname;
apr_array_header_t *args, *targets;
/* Get the property's name (and a UTF-8 version of that name). */
SVN_ERR(svn_opt_parse_num_args(&args, os, 1, pool));
pname = APR_ARRAY_IDX(args, 0, const char *);
- SVN_ERR(svn_utf_cstring_to_utf8(&pname_utf8, pname, pool));
+ SVN_ERR(svn_utf_cstring_to_utf8(&pname, pname, pool));
/* No need to check svn_prop_name_is_valid for *deleting*
properties, and it may even be useful to allow, in case invalid
properties sneaked through somehow. */
@@ -78,7 +78,7 @@ svn_cl__propdel(apr_getopt_t *os,
&URL, ctx, pool));
/* Let libsvn_client do the real work. */
- SVN_ERR(svn_client_revprop_set2(pname_utf8, NULL, NULL,
+ SVN_ERR(svn_client_revprop_set2(pname, NULL, NULL,
URL, &(opt_state->start_revision),
&rev, FALSE, ctx, pool));
}
@@ -94,7 +94,7 @@ svn_cl__propdel(apr_getopt_t *os,
opt_state->depth = svn_depth_empty;
/* For each target, remove the property PNAME. */
- SVN_ERR(svn_client_propset_local(pname_utf8, NULL, targets,
+ SVN_ERR(svn_client_propset_local(pname, NULL, targets,
opt_state->depth, FALSE,
opt_state->changelists, ctx, pool));
}
diff --git a/subversion/svn/propedit-cmd.c b/subversion/svn/propedit-cmd.c
index 520fe6c00c87..59ef24bda93b 100644
--- a/subversion/svn/propedit-cmd.c
+++ b/subversion/svn/propedit-cmd.c
@@ -47,7 +47,7 @@
/*** Code. ***/
struct commit_info_baton
{
- const char *pname_utf8;
+ const char *pname;
const char *target_local;
};
@@ -60,7 +60,7 @@ commit_info_handler(const svn_commit_info_t *commit_info,
SVN_ERR(svn_cmdline_printf(pool,
_("Set new value for property '%s' on '%s'\n"),
- cib->pname_utf8, cib->target_local));
+ cib->pname, cib->target_local));
SVN_ERR(svn_cl__print_commit_info(commit_info, NULL, pool));
return SVN_NO_ERROR;
@@ -74,23 +74,23 @@ svn_cl__propedit(apr_getopt_t *os,
{
svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
- const char *pname, *pname_utf8;
+ const char *pname;
apr_array_header_t *args, *targets;
/* Validate the input and get the property's name (and a UTF-8
version of that name). */
SVN_ERR(svn_opt_parse_num_args(&args, os, 1, pool));
pname = APR_ARRAY_IDX(args, 0, const char *);
- SVN_ERR(svn_utf_cstring_to_utf8(&pname_utf8, pname, pool));
- if (! svn_prop_name_is_valid(pname_utf8))
+ SVN_ERR(svn_utf_cstring_to_utf8(&pname, pname, pool));
+ if (! svn_prop_name_is_valid(pname))
return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
_("'%s' is not a valid Subversion property name"),
- pname_utf8);
+ pname);
if (!opt_state->force)
- SVN_ERR(svn_cl__check_svn_prop_name(pname_utf8, opt_state->revprop,
+ SVN_ERR(svn_cl__check_svn_prop_name(pname, opt_state->revprop,
svn_cl__prop_use_edit, pool));
- if (opt_state->encoding && !svn_prop_needs_translation(pname_utf8))
+ if (opt_state->encoding && !svn_prop_needs_translation(pname))
return svn_error_create
(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
_("--encoding option applies only to textual"
@@ -120,7 +120,7 @@ svn_cl__propedit(apr_getopt_t *os,
&URL, ctx, pool));
/* Fetch the current property. */
- SVN_ERR(svn_client_revprop_get(pname_utf8, &propval,
+ SVN_ERR(svn_client_revprop_get(pname, &propval,
URL, &(opt_state->start_revision),
&rev, ctx, pool));
@@ -145,13 +145,13 @@ svn_cl__propedit(apr_getopt_t *os,
opt_state->editor_cmd, temp_dir,
propval, "svn-prop",
ctx->config,
- svn_prop_needs_translation(pname_utf8),
+ svn_prop_needs_translation(pname),
opt_state->encoding, pool));
/* ...and re-set the property's value accordingly. */
if (propval)
{
- SVN_ERR(svn_client_revprop_set2(pname_utf8,
+ SVN_ERR(svn_client_revprop_set2(pname,
propval, &original_propval,
URL, &(opt_state->start_revision),
&rev, opt_state->force, ctx, pool));
@@ -160,13 +160,13 @@ svn_cl__propedit(apr_getopt_t *os,
(svn_cmdline_printf
(pool,
_("Set new value for property '%s' on revision %ld\n"),
- pname_utf8, rev));
+ pname, rev));
}
else
{
SVN_ERR(svn_cmdline_printf
(pool, _("No changes to property '%s' on revision %ld\n"),
- pname_utf8, rev));
+ pname, rev));
}
}
else if (opt_state->start_revision.kind != svn_opt_revision_unspecified)
@@ -174,7 +174,7 @@ svn_cl__propedit(apr_getopt_t *os,
return svn_error_createf
(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
_("Cannot specify revision for editing versioned property '%s'"),
- pname_utf8);
+ pname);
}
else /* operate on a normal, versioned property (not a revprop) */
{
@@ -206,7 +206,7 @@ svn_cl__propedit(apr_getopt_t *os,
SVN_ERR(svn_cl__eat_peg_revisions(&targets, targets, pool));
- cib.pname_utf8 = pname_utf8;
+ cib.pname = pname;
/* For each target, edit the property PNAME. */
for (i = 0; i < targets->nelts; i++)
@@ -234,7 +234,7 @@ svn_cl__propedit(apr_getopt_t *os,
peg_revision.kind = svn_opt_revision_unspecified;
/* Fetch the current property. */
- SVN_ERR(svn_client_propget5(&props, NULL, pname_utf8, abspath_or_url,
+ SVN_ERR(svn_client_propget5(&props, NULL, pname, abspath_or_url,
&peg_revision,
&(opt_state->start_revision),
&base_rev, svn_depth_empty,
@@ -282,7 +282,7 @@ svn_cl__propedit(apr_getopt_t *os,
"svn-prop",
ctx->config,
svn_prop_needs_translation
- (pname_utf8),
+ (pname),
opt_state->encoding,
subpool));
@@ -295,7 +295,7 @@ svn_cl__propedit(apr_getopt_t *os,
{
svn_error_t *err = SVN_NO_ERROR;
- svn_cl__check_boolean_prop_val(pname_utf8, edited_propval->data,
+ svn_cl__check_boolean_prop_val(pname, edited_propval->data,
subpool);
if (ctx->log_msg_func3)
@@ -304,7 +304,7 @@ svn_cl__propedit(apr_getopt_t *os,
subpool));
if (svn_path_is_url(target))
{
- err = svn_client_propset_remote(pname_utf8, edited_propval,
+ err = svn_client_propset_remote(pname, edited_propval,
target, opt_state->force,
base_rev,
opt_state->revprop_table,
@@ -319,9 +319,9 @@ svn_cl__propedit(apr_getopt_t *os,
APR_ARRAY_PUSH(targs, const char *) = target;
SVN_ERR(svn_cl__propset_print_binary_mime_type_warning(
- targs, pname_utf8, propval, subpool));
+ targs, pname, propval, subpool));
- err = svn_client_propset_local(pname_utf8, edited_propval,
+ err = svn_client_propset_local(pname, edited_propval,
targs, svn_depth_empty,
opt_state->force, NULL,
ctx, subpool);
@@ -339,14 +339,14 @@ svn_cl__propedit(apr_getopt_t *os,
if (!svn_path_is_url(target))
SVN_ERR(svn_cmdline_printf(
subpool, _("Set new value for property '%s' on '%s'\n"),
- pname_utf8, target_local));
+ pname, target_local));
}
else
{
SVN_ERR
(svn_cmdline_printf
(subpool, _("No changes to property '%s' on '%s'\n"),
- pname_utf8, target_local));
+ pname, target_local));
}
}
svn_pool_destroy(subpool);
diff --git a/subversion/svn/propget-cmd.c b/subversion/svn/propget-cmd.c
index fa04c202ce3d..fe2d1bd3fba1 100644
--- a/subversion/svn/propget-cmd.c
+++ b/subversion/svn/propget-cmd.c
@@ -137,7 +137,7 @@ print_properties_xml(const char *pname,
return SVN_NO_ERROR;
}
-/* Print the property PNAME_UTF with the value PROPVAL set on ABSPATH_OR_URL
+/* Print the property PNAME with the value PROPVAL set on ABSPATH_OR_URL
to the stream OUT.
If INHERITED_PROPERTY is true then the property described is inherited,
@@ -153,7 +153,7 @@ print_single_prop(svn_string_t *propval,
const char *abspath_or_URL,
const char *wc_path_prefix,
svn_stream_t *out,
- const char *pname_utf8,
+ const char *pname,
svn_boolean_t print_filenames,
svn_boolean_t omit_newline,
svn_boolean_t like_proplist,
@@ -211,14 +211,14 @@ print_single_prop(svn_string_t *propval,
/* Print the property name and value just as "proplist -v" does */
apr_hash_t *hash = apr_hash_make(scratch_pool);
- svn_hash_sets(hash, pname_utf8, propval);
+ svn_hash_sets(hash, pname, propval);
SVN_ERR(svn_cmdline__print_prop_hash(out, hash, FALSE, scratch_pool));
}
else
{
/* If this is a special Subversion property, it is stored as
UTF8, so convert to the native format. */
- if (svn_prop_needs_translation(pname_utf8))
+ if (svn_prop_needs_translation(pname))
SVN_ERR(svn_subst_detranslate_string(&propval, propval,
TRUE, scratch_pool));
@@ -244,7 +244,7 @@ print_single_prop(svn_string_t *propval,
If IS_URL is true, all paths in PROPS are URLs, else all paths are local
paths.
- PNAME_UTF8 is the property name of all the properties.
+ PNAME is the property name of all the properties.
If PRINT_FILENAMES is true, print the item's path before each property.
@@ -255,7 +255,7 @@ print_single_prop(svn_string_t *propval,
static svn_error_t *
print_properties(svn_stream_t *out,
const char *target_abspath_or_url,
- const char *pname_utf8,
+ const char *pname,
apr_hash_t *props,
apr_array_header_t *inherited_props,
svn_boolean_t print_filenames,
@@ -282,7 +282,7 @@ print_properties(svn_stream_t *out,
iprop->prop_hash));
SVN_ERR(print_single_prop(propval, target_abspath_or_url,
iprop->path_or_url,
- path_prefix, out, pname_utf8,
+ path_prefix, out, pname,
print_filenames, omit_newline,
like_proplist, TRUE, iterpool));
}
@@ -298,7 +298,7 @@ print_properties(svn_stream_t *out,
svn_pool_clear(iterpool);
SVN_ERR(print_single_prop(propval, target_abspath_or_url, filename,
- path_prefix, out, pname_utf8, print_filenames,
+ path_prefix, out, pname, print_filenames,
omit_newline, like_proplist, FALSE,
iterpool));
}
@@ -317,7 +317,7 @@ svn_cl__propget(apr_getopt_t *os,
{
svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
- const char *pname, *pname_utf8;
+ const char *pname;
apr_array_header_t *args, *targets;
svn_stream_t *out;
svn_boolean_t warned = FALSE;
@@ -328,15 +328,14 @@ svn_cl__propget(apr_getopt_t *os,
_("--verbose cannot be used with --revprop or "
"--no-newline or --xml"));
- /* PNAME is first argument (and PNAME_UTF8 will be a UTF-8 version
- thereof) */
+ /* PNAME is first argument */
SVN_ERR(svn_opt_parse_num_args(&args, os, 1, pool));
pname = APR_ARRAY_IDX(args, 0, const char *);
- SVN_ERR(svn_utf_cstring_to_utf8(&pname_utf8, pname, pool));
- if (! svn_prop_name_is_valid(pname_utf8))
+ SVN_ERR(svn_utf_cstring_to_utf8(&pname, pname, pool));
+ if (! svn_prop_name_is_valid(pname))
return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
_("'%s' is not a valid Subversion property name"),
- pname_utf8);
+ pname);
SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
opt_state->targets,
@@ -363,7 +362,7 @@ svn_cl__propget(apr_getopt_t *os,
&URL, ctx, pool));
/* Let libsvn_client do the real work. */
- SVN_ERR(svn_client_revprop_get(pname_utf8, &propval,
+ SVN_ERR(svn_client_revprop_get(pname, &propval,
URL, &(opt_state->start_revision),
&rev, ctx, pool));
@@ -372,7 +371,7 @@ svn_cl__propget(apr_getopt_t *os,
return svn_error_createf(SVN_ERR_PROPERTY_NOT_FOUND, NULL,
_("Property '%s' not found on "
"revision %s"),
- pname_utf8,
+ pname,
svn_opt__revision_to_string(
&opt_state->start_revision,
pool));
@@ -390,7 +389,7 @@ svn_cl__propget(apr_getopt_t *os,
"revprops",
"rev", revstr, SVN_VA_NULL);
- svn_cmdline__print_xml_prop(&sb, pname_utf8, propval, FALSE,
+ svn_cmdline__print_xml_prop(&sb, pname, propval, FALSE,
pool);
svn_xml_make_close_tag(&sb, pool, "revprops");
@@ -405,7 +404,7 @@ svn_cl__propget(apr_getopt_t *os,
/* If this is a special Subversion property, it is stored as
UTF8 and LF, so convert to the native locale and eol-style. */
- if (svn_prop_needs_translation(pname_utf8))
+ if (svn_prop_needs_translation(pname))
SVN_ERR(svn_subst_detranslate_string(&printable_val, propval,
TRUE, pool));
@@ -462,7 +461,7 @@ svn_cl__propget(apr_getopt_t *os,
SVN_ERR(svn_client_propget5(
&props,
opt_state->show_inherited_props ? &inherited_props : NULL,
- pname_utf8, truepath,
+ pname, truepath,
&peg_revision,
&(opt_state->start_revision),
NULL, opt_state->depth,
@@ -491,7 +490,7 @@ svn_cl__propget(apr_getopt_t *os,
svn_error_t *err;
err = svn_error_createf(SVN_ERR_PROPERTY_NOT_FOUND, NULL,
_("Property '%s' not found on '%s'"),
- pname_utf8, target);
+ pname, target);
svn_handle_warning2(stderr, err, "svn: ");
svn_error_clear(err);
warned = TRUE;
@@ -499,12 +498,12 @@ svn_cl__propget(apr_getopt_t *os,
if (opt_state->xml)
SVN_ERR(print_properties_xml(
- pname_utf8, props,
+ pname, props,
opt_state->show_inherited_props ? inherited_props : NULL,
subpool));
else
SVN_ERR(print_properties(
- out, truepath, pname_utf8,
+ out, truepath, pname,
props,
opt_state->show_inherited_props ? inherited_props : NULL,
print_filenames,
diff --git a/subversion/svn/propset-cmd.c b/subversion/svn/propset-cmd.c
index 07b9bbdb57a4..05087069005a 100644
--- a/subversion/svn/propset-cmd.c
+++ b/subversion/svn/propset-cmd.c
@@ -51,7 +51,7 @@ svn_cl__propset(apr_getopt_t *os,
{
svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
- const char *pname, *pname_utf8;
+ const char *pname;
svn_string_t *propval = NULL;
svn_boolean_t propval_came_from_cmdline;
apr_array_header_t *args, *targets;
@@ -62,13 +62,13 @@ svn_cl__propset(apr_getopt_t *os,
SVN_ERR(svn_opt_parse_num_args(&args, os,
opt_state->filedata ? 1 : 2, scratch_pool));
pname = APR_ARRAY_IDX(args, 0, const char *);
- SVN_ERR(svn_utf_cstring_to_utf8(&pname_utf8, pname, scratch_pool));
- if (! svn_prop_name_is_valid(pname_utf8))
+ SVN_ERR(svn_utf_cstring_to_utf8(&pname, pname, scratch_pool));
+ if (! svn_prop_name_is_valid(pname))
return svn_error_createf(SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
_("'%s' is not a valid Subversion property name"),
- pname_utf8);
+ pname);
if (!opt_state->force)
- SVN_ERR(svn_cl__check_svn_prop_name(pname_utf8, opt_state->revprop,
+ SVN_ERR(svn_cl__check_svn_prop_name(pname, opt_state->revprop,
svn_cl__prop_use_set, scratch_pool));
/* Get the PROPVAL from either an external file, or from the command
@@ -87,7 +87,7 @@ svn_cl__propset(apr_getopt_t *os,
/* We only want special Subversion property values to be in UTF-8
and LF line endings. All other propvals are taken literally. */
- if (svn_prop_needs_translation(pname_utf8))
+ if (svn_prop_needs_translation(pname))
SVN_ERR(svn_subst_translate_string2(&propval, NULL, NULL, propval,
opt_state->encoding, FALSE,
scratch_pool, scratch_pool));
@@ -120,7 +120,7 @@ svn_cl__propset(apr_getopt_t *os,
&URL, ctx, scratch_pool));
/* Let libsvn_client do the real work. */
- SVN_ERR(svn_client_revprop_set2(pname_utf8, propval, NULL,
+ SVN_ERR(svn_client_revprop_set2(pname, propval, NULL,
URL, &(opt_state->start_revision),
&rev, opt_state->force, ctx,
scratch_pool));
@@ -174,17 +174,17 @@ svn_cl__propset(apr_getopt_t *os,
}
SVN_ERR(svn_cl__propset_print_binary_mime_type_warning(targets,
- pname_utf8,
+ pname,
propval,
scratch_pool));
- SVN_ERR(svn_client_propset_local(pname_utf8, propval, targets,
+ SVN_ERR(svn_client_propset_local(pname, propval, targets,
opt_state->depth, opt_state->force,
opt_state->changelists, ctx,
scratch_pool));
if (! opt_state->quiet)
- svn_cl__check_boolean_prop_val(pname_utf8, propval->data, scratch_pool);
+ svn_cl__check_boolean_prop_val(pname, propval->data, scratch_pool);
}
return SVN_NO_ERROR;
diff --git a/subversion/svn/resolve-cmd.c b/subversion/svn/resolve-cmd.c
index 035bf29aff5a..bbf9ff548ef5 100644
--- a/subversion/svn/resolve-cmd.c
+++ b/subversion/svn/resolve-cmd.c
@@ -30,6 +30,7 @@
#include "svn_client.h"
#include "svn_error.h"
#include "svn_pools.h"
+#include "svn_hash.h"
#include "cl.h"
#include "svn_private_config.h"
@@ -38,6 +39,127 @@
/*** Code. ***/
+struct conflict_walker_baton
+{
+ svn_client_ctx_t *ctx;
+ svn_cl__accept_t accept_which;
+ svn_boolean_t quit;
+ svn_boolean_t external_failed;
+ svn_boolean_t printed_summary;
+ const char *editor_cmd;
+ const char *path_prefix;
+ svn_cmdline_prompt_baton_t *pb;
+ svn_cl__conflict_stats_t *conflict_stats;
+};
+
+/* Implements svn_client_conflict_walk_func_t. */
+static svn_error_t *
+conflict_walker(void *baton, svn_client_conflict_t *conflict,
+ apr_pool_t *scratch_pool)
+{
+ struct conflict_walker_baton *cwb = baton;
+
+ SVN_ERR(svn_cl__resolve_conflict(&cwb->quit, &cwb->external_failed,
+ &cwb->printed_summary, conflict,
+ cwb->accept_which, cwb->editor_cmd,
+ cwb->path_prefix, cwb->pb,
+ cwb->conflict_stats,
+ cwb->ctx, scratch_pool));
+ if (cwb->quit)
+ return svn_error_create(SVN_ERR_CANCELLED, NULL, NULL);
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_cl__walk_conflicts(apr_array_header_t *targets,
+ svn_cl__conflict_stats_t *conflict_stats,
+ svn_cl__opt_state_t *opt_state,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ svn_boolean_t had_error = FALSE;
+ svn_cmdline_prompt_baton_t *pb = apr_palloc(scratch_pool, sizeof(*pb));
+ struct conflict_walker_baton cwb = { 0 };
+ const char *path_prefix;
+ svn_error_t *err;
+ int i;
+ apr_pool_t *iterpool;
+
+ SVN_ERR(svn_dirent_get_absolute(&path_prefix, "", scratch_pool));
+
+ pb->cancel_func = ctx->cancel_func;
+ pb->cancel_baton = ctx->cancel_baton;
+
+ cwb.ctx = ctx;
+ cwb.accept_which = opt_state->accept_which;
+ cwb.quit = FALSE;
+ cwb.external_failed = FALSE;
+ cwb.printed_summary = FALSE;
+ cwb.editor_cmd = opt_state->editor_cmd;
+ cwb.path_prefix = path_prefix;
+ cwb.pb = pb;
+ cwb.conflict_stats = conflict_stats;
+
+ iterpool = svn_pool_create(scratch_pool);
+ for (i = 0; i < targets->nelts; i++)
+ {
+ const char *target = APR_ARRAY_IDX(targets, i, const char *);
+ const char *local_abspath;
+ svn_client_conflict_t *conflict;
+
+ svn_pool_clear(iterpool);
+
+ SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton));
+
+ SVN_ERR(svn_dirent_get_absolute(&local_abspath, target, iterpool));
+
+ if (opt_state->depth == svn_depth_empty)
+ {
+ SVN_ERR(svn_client_conflict_get(&conflict, local_abspath, ctx,
+ iterpool, iterpool));
+ err = svn_cl__resolve_conflict(&cwb.quit, &cwb.external_failed,
+ &cwb.printed_summary,
+ conflict, opt_state->accept_which,
+ opt_state->editor_cmd,
+ path_prefix, pb, conflict_stats,
+ ctx, iterpool);
+ }
+ else
+ err = svn_client_conflict_walk(local_abspath, opt_state->depth,
+ conflict_walker, &cwb, ctx, iterpool);
+
+ if (err)
+ {
+ svn_error_t *root = svn_error_root_cause(err);
+
+ if (root->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
+ {
+ /* ### Ignore. These errors can happen due to the working copy
+ * ### being re-arranged during tree conflict resolution. */
+ svn_error_clear(err);
+ continue;
+ }
+ else if (root->apr_err == SVN_ERR_CANCELLED)
+ {
+ svn_error_clear(err);
+ break;
+ }
+
+ svn_handle_warning2(stderr, svn_error_root_cause(err), "svn: ");
+ svn_error_clear(err);
+ had_error = TRUE;
+ }
+ }
+ svn_pool_destroy(iterpool);
+
+ if (had_error)
+ return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
+ _("Failure occurred resolving one or more "
+ "conflicts"));
+ return SVN_NO_ERROR;
+}
+
/* This implements the `svn_opt_subcommand_t' interface. */
svn_error_t *
svn_cl__resolve(apr_getopt_t *os,
@@ -45,44 +167,10 @@ svn_cl__resolve(apr_getopt_t *os,
apr_pool_t *scratch_pool)
{
svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
+ svn_cl__conflict_stats_t *conflict_stats =
+ ((svn_cl__cmd_baton_t *) baton)->conflict_stats;
svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
- svn_wc_conflict_choice_t conflict_choice;
- svn_error_t *err;
apr_array_header_t *targets;
- int i;
- apr_pool_t *iterpool;
- svn_boolean_t had_error = FALSE;
-
- switch (opt_state->accept_which)
- {
- case svn_cl__accept_working:
- conflict_choice = svn_wc_conflict_choose_merged;
- break;
- case svn_cl__accept_base:
- conflict_choice = svn_wc_conflict_choose_base;
- break;
- case svn_cl__accept_theirs_conflict:
- conflict_choice = svn_wc_conflict_choose_theirs_conflict;
- break;
- case svn_cl__accept_mine_conflict:
- conflict_choice = svn_wc_conflict_choose_mine_conflict;
- break;
- case svn_cl__accept_theirs_full:
- conflict_choice = svn_wc_conflict_choose_theirs_full;
- break;
- case svn_cl__accept_mine_full:
- conflict_choice = svn_wc_conflict_choose_mine_full;
- break;
- case svn_cl__accept_unspecified:
- if (opt_state->non_interactive)
- return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
- _("missing --accept option"));
- conflict_choice = svn_wc_conflict_choose_unspecified;
- break;
- default:
- return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
- _("invalid 'accept' ARG"));
- }
SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
opt_state->targets,
@@ -103,29 +191,22 @@ svn_cl__resolve(apr_getopt_t *os,
SVN_ERR(svn_cl__check_targets_are_local_paths(targets));
- iterpool = svn_pool_create(scratch_pool);
- for (i = 0; i < targets->nelts; i++)
+ if (opt_state->accept_which == svn_cl__accept_unspecified &&
+ opt_state->non_interactive)
{
- const char *target = APR_ARRAY_IDX(targets, i, const char *);
- svn_pool_clear(iterpool);
- SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton));
- err = svn_client_resolve(target,
- opt_state->depth, conflict_choice,
- ctx,
- iterpool);
- if (err)
- {
- svn_handle_warning2(stderr, err, "svn: ");
- svn_error_clear(err);
- had_error = TRUE;
- }
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("missing --accept option"));
+ }
+ else if (opt_state->accept_which == svn_cl__accept_postpone ||
+ opt_state->accept_which == svn_cl__accept_edit ||
+ opt_state->accept_which == svn_cl__accept_launch)
+ {
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("invalid 'accept' ARG"));
}
- svn_pool_destroy(iterpool);
- if (had_error)
- return svn_error_create(SVN_ERR_WC_CONFLICT_RESOLVER_FAILURE, NULL,
- _("Failure occurred resolving one or more "
- "conflicts"));
+ SVN_ERR(svn_cl__walk_conflicts(targets, conflict_stats,
+ opt_state, ctx, scratch_pool));
return SVN_NO_ERROR;
}
diff --git a/subversion/svn/shelve-cmd.c b/subversion/svn/shelve-cmd.c
new file mode 100644
index 000000000000..df88a651d9c2
--- /dev/null
+++ b/subversion/svn/shelve-cmd.c
@@ -0,0 +1,369 @@
+/*
+ * shelve-cmd.c -- Shelve commands.
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ */
+
+/* We define this here to remove any further warnings about the usage of
+ experimental functions in this file. */
+#define SVN_EXPERIMENTAL
+
+#include "svn_client.h"
+#include "svn_error_codes.h"
+#include "svn_error.h"
+#include "svn_path.h"
+#include "svn_utf.h"
+
+#include "cl.h"
+
+#include "svn_private_config.h"
+#include "private/svn_sorts_private.h"
+
+
+/* First argument should be the name of a shelved change. */
+static svn_error_t *
+get_name(const char **name,
+ apr_getopt_t *os,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ apr_array_header_t *args;
+
+ SVN_ERR(svn_opt_parse_num_args(&args, os, 1, scratch_pool));
+ SVN_ERR(svn_utf_cstring_to_utf8(name,
+ APR_ARRAY_IDX(args, 0, const char *),
+ result_pool));
+ return SVN_NO_ERROR;
+}
+
+/* A comparison function for svn_sort__hash(), comparing the mtime of two
+ svn_client_shelved_patch_info_t's. */
+static int
+compare_shelved_patch_infos_by_mtime(const svn_sort__item_t *a,
+ const svn_sort__item_t *b)
+{
+ svn_client_shelved_patch_info_t *a_val = a->value;
+ svn_client_shelved_patch_info_t *b_val = b->value;
+
+ return (a_val->dirent->mtime < b_val->dirent->mtime)
+ ? -1 : (a_val->dirent->mtime > b_val->dirent->mtime) ? 1 : 0;
+}
+
+/* Return a list of shelved changes sorted by patch file mtime, oldest first.
+ */
+static svn_error_t *
+list_sorted_by_date(apr_array_header_t **list,
+ const char *local_abspath,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ apr_hash_t *shelved_patch_infos;
+
+ SVN_ERR(svn_client_shelves_list(&shelved_patch_infos, local_abspath,
+ ctx, scratch_pool, scratch_pool));
+ *list = svn_sort__hash(shelved_patch_infos,
+ compare_shelved_patch_infos_by_mtime,
+ scratch_pool);
+ return SVN_NO_ERROR;
+}
+
+#ifndef WIN32
+/* Run CMD with ARGS.
+ * Send its stdout to the parent's stdout. Disconnect its stdin and stderr.
+ */
+static svn_error_t *
+run_cmd(const char *cmd,
+ const char *const *args,
+ apr_pool_t *scratch_pool)
+{
+ apr_status_t apr_err;
+ apr_file_t *outfile;
+ svn_error_t *err;
+ int exitcode;
+
+ apr_err = apr_file_open_stdout(&outfile, scratch_pool);
+ if (apr_err)
+ return svn_error_wrap_apr(apr_err, "Can't open stdout");
+
+ err = svn_io_run_cmd(NULL /*path*/, cmd, args,
+ &exitcode, NULL /*exitwhy*/,
+ TRUE /*inherit*/,
+ NULL /*infile*/, outfile, NULL /*errfile*/,
+ scratch_pool);
+ if (err || exitcode)
+ return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, err,
+ _("Could not run external command '%s'"), cmd);
+ return SVN_NO_ERROR;
+}
+#endif
+
+/* Display a list of shelved changes */
+static svn_error_t *
+shelves_list(const char *local_abspath,
+ svn_boolean_t diffstat,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *scratch_pool)
+{
+ apr_array_header_t *list;
+ int i;
+
+ SVN_ERR(list_sorted_by_date(&list,
+ local_abspath, ctx, scratch_pool));
+
+ for (i = 0; i < list->nelts; i++)
+ {
+ const svn_sort__item_t *item = &APR_ARRAY_IDX(list, i, svn_sort__item_t);
+ const char *name = item->key;
+ svn_client_shelved_patch_info_t *info = item->value;
+ int age = (int)((apr_time_now() - info->mtime) / 1000000 / 60);
+ apr_hash_t *paths;
+
+ SVN_ERR(svn_client_shelf_get_paths(&paths,
+ name, local_abspath, ctx,
+ scratch_pool, scratch_pool));
+
+ SVN_ERR(svn_cmdline_printf(scratch_pool,
+ _("%-30s %6d mins old %10ld bytes %4d paths changed\n"),
+ name, age, (long)info->dirent->filesize,
+ apr_hash_count(paths)));
+ SVN_ERR(svn_cmdline_printf(scratch_pool,
+ _(" %.50s\n"),
+ info->message));
+
+ if (diffstat)
+ {
+#ifndef WIN32
+ const char *args[4];
+ svn_error_t *err;
+
+ args[0] = "diffstat";
+ args[1] = "-p0";
+ args[2] = info->patch_path;
+ args[3] = NULL;
+ err = run_cmd("diffstat", args, scratch_pool);
+ if (err)
+ svn_error_clear(err);
+ else
+ SVN_ERR(svn_cmdline_printf(scratch_pool,
+ "\n"));
+#endif
+ }
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Find the name of the youngest shelved change.
+ */
+static svn_error_t *
+name_of_youngest(const char **name_p,
+ const char *local_abspath,
+ svn_client_ctx_t *ctx,
+ apr_pool_t *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ apr_array_header_t *list;
+ const svn_sort__item_t *youngest_item;
+
+ SVN_ERR(list_sorted_by_date(&list,
+ local_abspath, ctx, scratch_pool));
+ if (list->nelts == 0)
+ return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, NULL,
+ _("No shelved changes found"));
+
+ youngest_item = &APR_ARRAY_IDX(list, list->nelts - 1, svn_sort__item_t);
+ *name_p = youngest_item->key;
+ return SVN_NO_ERROR;
+}
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__shelve(apr_getopt_t *os,
+ void *baton,
+ apr_pool_t *pool)
+{
+ svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
+ svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
+ const char *local_abspath;
+ const char *name;
+ apr_array_header_t *targets;
+ svn_boolean_t has_changes;
+
+ if (opt_state->quiet)
+ ctx->notify_func2 = NULL; /* Easy out: avoid unneeded work */
+
+ SVN_ERR(svn_dirent_get_absolute(&local_abspath, "", pool));
+
+ if (opt_state->list)
+ {
+ if (os->ind < os->argc)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, NULL);
+
+ SVN_ERR(shelves_list(local_abspath,
+ ! opt_state->quiet /*diffstat*/,
+ ctx, pool));
+ return SVN_NO_ERROR;
+ }
+
+ SVN_ERR(get_name(&name, os, pool, pool));
+
+ if (opt_state->remove)
+ {
+ if (os->ind < os->argc)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, NULL);
+
+ SVN_ERR(svn_client_shelves_delete(name, local_abspath,
+ opt_state->dry_run,
+ ctx, pool));
+ if (! opt_state->quiet)
+ SVN_ERR(svn_cmdline_printf(pool, "deleted '%s'\n", name));
+ return SVN_NO_ERROR;
+ }
+
+ /* Parse the remaining arguments as paths. */
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE, pool));
+ svn_opt_push_implicit_dot_target(targets, pool);
+
+ {
+ svn_depth_t depth = opt_state->depth;
+ svn_error_t *err;
+
+ /* shelve has no implicit dot-target `.', so don't you put that
+ code here! */
+ if (!targets->nelts)
+ return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);
+
+ SVN_ERR(svn_cl__check_targets_are_local_paths(targets));
+
+ if (depth == svn_depth_unknown)
+ depth = svn_depth_infinity;
+
+ SVN_ERR(svn_cl__eat_peg_revisions(&targets, targets, pool));
+
+ if (ctx->log_msg_func3)
+ SVN_ERR(svn_cl__make_log_msg_baton(&ctx->log_msg_baton3,
+ opt_state, NULL, ctx->config,
+ pool));
+ err = svn_client_shelve(name,
+ targets, depth, opt_state->changelists,
+ opt_state->keep_local, opt_state->dry_run,
+ ctx, pool);
+ if (ctx->log_msg_func3)
+ SVN_ERR(svn_cl__cleanup_log_msg(ctx->log_msg_baton3,
+ err, pool));
+ else
+ SVN_ERR(err);
+ }
+
+ /* If no modifications were shelved, throw an error. */
+ SVN_ERR(svn_client_shelf_has_changes(&has_changes,
+ name, local_abspath, ctx, pool));
+ if (! has_changes)
+ {
+ SVN_ERR(svn_client_shelves_delete(name, local_abspath,
+ opt_state->dry_run, ctx, pool));
+ return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
+ _("No changes were shelved"));
+ }
+
+ if (! opt_state->quiet)
+ SVN_ERR(svn_cmdline_printf(pool, "shelved '%s'\n", name));
+
+ return SVN_NO_ERROR;
+}
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__unshelve(apr_getopt_t *os,
+ void *baton,
+ apr_pool_t *pool)
+{
+ svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
+ svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
+ const char *local_abspath;
+ const char *name;
+ apr_array_header_t *targets;
+
+ SVN_ERR(svn_dirent_get_absolute(&local_abspath, "", pool));
+
+ if (opt_state->list)
+ {
+ if (os->ind < os->argc)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, NULL);
+
+ SVN_ERR(shelves_list(local_abspath,
+ ! opt_state->quiet /*diffstat*/,
+ ctx, pool));
+ return SVN_NO_ERROR;
+ }
+
+ if (os->ind < os->argc)
+ {
+ SVN_ERR(get_name(&name, os, pool, pool));
+ }
+ else
+ {
+ SVN_ERR(name_of_youngest(&name, local_abspath, ctx, pool, pool));
+ SVN_ERR(svn_cmdline_printf(pool,
+ _("unshelving the youngest change, '%s'\n"),
+ name));
+ }
+
+ /* There should be no remaining arguments. */
+ SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
+ opt_state->targets,
+ ctx, FALSE, pool));
+ if (targets->nelts)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, NULL);
+
+ if (opt_state->quiet)
+ ctx->notify_func2 = NULL; /* Easy out: avoid unneeded work */
+
+ SVN_ERR(svn_client_unshelve(name, local_abspath,
+ opt_state->keep_local, opt_state->dry_run,
+ ctx, pool));
+ if (! opt_state->quiet)
+ SVN_ERR(svn_cmdline_printf(pool, "unshelved '%s'\n", name));
+
+ return SVN_NO_ERROR;
+}
+
+/* This implements the `svn_opt_subcommand_t' interface. */
+svn_error_t *
+svn_cl__shelves(apr_getopt_t *os,
+ void *baton,
+ apr_pool_t *pool)
+{
+ svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
+ svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
+ const char *local_abspath;
+
+ /* There should be no remaining arguments. */
+ if (os->ind < os->argc)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, NULL);
+
+ SVN_ERR(svn_dirent_get_absolute(&local_abspath, "", pool));
+ SVN_ERR(shelves_list(local_abspath, ! opt_state->quiet /*diffstat*/,
+ ctx, pool));
+
+ return SVN_NO_ERROR;
+}
diff --git a/subversion/svn/status.c b/subversion/svn/status.c
index 9ab9c5926c55..409540267b4f 100644
--- a/subversion/svn/status.c
+++ b/subversion/svn/status.c
@@ -282,11 +282,10 @@ print_status(const char *target_abspath,
if (tree_conflicted)
{
- const svn_wc_conflict_description2_t *tree_conflict;
- SVN_ERR(svn_wc__get_tree_conflict(&tree_conflict, ctx->wc_ctx,
- local_abspath, pool, pool));
- SVN_ERR_ASSERT(tree_conflict != NULL);
+ svn_client_conflict_t *tree_conflict;
+ SVN_ERR(svn_client_conflict_get(&tree_conflict, local_abspath,
+ ctx, pool, pool));
tree_status_code = 'C';
SVN_ERR(svn_cl__get_human_readable_tree_conflict_description(
&desc, tree_conflict, pool));
diff --git a/subversion/svn/svn.c b/subversion/svn/svn.c
index 058be707d074..48b894f49983 100644
--- a/subversion/svn/svn.c
+++ b/subversion/svn/svn.c
@@ -33,7 +33,6 @@
#include <apr_strings.h>
#include <apr_tables.h>
#include <apr_general.h>
-#include <apr_signal.h>
#include "svn_cmdline.h"
#include "svn_pools.h"
@@ -57,6 +56,7 @@
#include "private/svn_opt_private.h"
#include "private/svn_cmdline_private.h"
#include "private/svn_subr_private.h"
+#include "private/svn_utf_private.h"
#include "svn_private_config.h"
@@ -68,6 +68,7 @@
use the short option letter as identifier. */
typedef enum svn_cl__longopt_t {
opt_auth_password = SVN_OPT_FIRST_LONGOPT_ID,
+ opt_auth_password_from_stdin,
opt_auth_username,
opt_autoprops,
opt_changelist,
@@ -143,6 +144,11 @@ typedef enum svn_cl__longopt_t {
opt_show_passwords,
opt_pin_externals,
opt_show_item,
+ opt_adds_as_modification,
+ opt_vacuum_pristines,
+ opt_delete,
+ opt_keep_shelved,
+ opt_list
} svn_cl__longopt_t;
@@ -198,6 +204,9 @@ const apr_getopt_option_t svn_cl__options[] =
N_("specify a password ARG (caution: on many operating\n"
" "
"systems, other users will be able to see this)")},
+ {"password-from-stdin",
+ opt_auth_password_from_stdin, 0,
+ N_("read password from stdin")},
{"extensions", 'x', 1,
N_("Specify differencing options for external diff or\n"
" "
@@ -327,9 +336,9 @@ const apr_getopt_option_t svn_cl__options[] =
" "
"'theirs-conflict', 'mine-full', 'theirs-full',\n"
" "
- "'edit', 'launch')\n"
+ "'edit', 'launch', 'recommended') (shorthand:\n"
" "
- "(shorthand: 'p', 'mc', 'tc', 'mf', 'tf', 'e', 'l')"
+ "'p', 'mc', 'tc', 'mf', 'tf', 'e', 'l', 'r')"
)},
{"show-revs", opt_show_revs, 1,
N_("specify which collection of revisions to display\n"
@@ -400,7 +409,11 @@ const apr_getopt_option_t svn_cl__options[] =
{"show-inherited-props", opt_show_inherited_props, 0,
N_("retrieve properties set on parents of the target")},
{"search", opt_search, 1,
- N_("use ARG as search pattern (glob syntax)")},
+ N_("use ARG as search pattern (glob syntax, case-\n"
+ " "
+ "and accent-insensitive, may require quotation marks\n"
+ " "
+ "to prevent shell expansion)")},
{"search-and", opt_search_and, 1,
N_("combine ARG with the previous search pattern")},
{"log", opt_mergeinfo_log, 0,
@@ -415,15 +428,55 @@ const apr_getopt_option_t svn_cl__options[] =
" "
"current revision (recommended when tagging)")},
{"show-item", opt_show_item, 1,
- N_("print only the item identified by ARG ('kind',\n"
- " "
- "'url', 'relative-url', 'repos-root-url',\n"
+ N_("print only the item identified by ARG:\n"
+ " "
+ " 'kind' node kind of TARGET\n"
+ " "
+ " 'url' URL of TARGET in the repository\n"
+ " "
+ " 'relative-url'\n"
+ " "
+ " repository-relative URL of TARGET\n"
+ " "
+ " 'repos-root-url'\n"
+ " "
+ " root URL of repository\n"
+ " "
+ " 'repos-uuid' UUID of repository\n"
+ " "
+ " 'revision' specified or implied revision\n"
+ " "
+ " 'last-changed-revision'\n"
+ " "
+ " last change of TARGET at or before\n"
+ " "
+ " 'revision'\n"
+ " "
+ " 'last-changed-date'\n"
+ " "
+ " date of 'last-changed-revision'\n"
+ " "
+ " 'last-changed-author'\n"
+ " "
+ " author of 'last-changed-revision'\n"
+ " "
+ " 'wc-root' root of TARGET's working copy")},
+
+ {"adds-as-modification", opt_adds_as_modification, 0,
+ N_("Local additions are merged with incoming additions\n"
" "
- "'repos-uuid', 'revision', 'last-changed-revision',\n"
+ "instead of causing a tree conflict. Use of this\n"
" "
- "'last-changed-date', 'last-changed-author',\n"
+ "option is not recommended! Use 'svn resolve' to\n"
" "
- "'wc-root')")},
+ "resolve tree conflicts instead.")},
+
+ {"vacuum-pristines", opt_vacuum_pristines, 0,
+ N_("remove unreferenced pristines from .svn directory")},
+
+ {"list", opt_list, 0, N_("list shelved patches")},
+ {"keep-shelved", opt_keep_shelved, 0, N_("do not delete the shelved patch")},
+ {"delete", opt_delete, 0, N_("delete the shelved patch")},
/* Long-opt Aliases
*
@@ -455,7 +508,8 @@ const apr_getopt_option_t svn_cl__options[] =
command to take these arguments allows scripts to just pass them
willy-nilly to every invocation of 'svn') . */
const int svn_cl__global_options[] =
-{ opt_auth_username, opt_auth_password, opt_no_auth_cache, opt_non_interactive,
+{ opt_auth_username, opt_auth_password, opt_auth_password_from_stdin,
+ opt_no_auth_cache, opt_non_interactive,
opt_force_interactive, opt_trust_server_cert,
opt_trust_server_cert_failures,
opt_config_dir, opt_config_options, 0
@@ -491,7 +545,7 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
" or more patterns. With the --remove option, remove cached authentication\n"
" credentials matching one or more patterns.\n"
"\n"
- " If more than one pattern is specified credentials are considered only they\n"
+ " If more than one pattern is specified credentials are considered only if they\n"
" match all specified patterns. Patterns are matched case-sensitively and may\n"
" contain glob wildcards:\n"
" ? matches any single character\n"
@@ -571,38 +625,45 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
{'r', 'q', 'N', opt_depth, opt_force, opt_ignore_externals} },
{ "cleanup", svn_cl__cleanup, {0}, N_
- ("Recursively clean up the working copy, removing write locks, resuming\n"
- "unfinished operations, etc.\n"
- "usage: cleanup [WCPATH...]\n"
- "\n"
- " By default, finish any unfinished business in the working copy at WCPATH,\n"
- " and remove write locks (shown as 'L' by the 'svn status' command) from\n"
- " the working copy. Usually, this is only necessary if a Subversion client\n"
- " has crashed while using the working copy, leaving it in an unusable state.\n"
- "\n"
- " WARNING: There is no mechanism that will protect write locks still\n"
- " being used by other Subversion clients. Running this command\n"
- " while another client is using the working copy can corrupt\n"
- " the working copy beyond repair!\n"
- "\n"
- " If the --remove-unversioned option or the --remove-ignored option\n"
- " is given, remove any unversioned or ignored items within WCPATH.\n"
- " To prevent accidental working copy corruption, unversioned or ignored\n"
- " items can only be removed if the working copy is not already locked\n"
- " for writing by another Subversion client.\n"
- " Note that the 'svn status' command shows unversioned items as '?',\n"
- " and ignored items as 'I' if the --no-ignore option is given to it.\n"),
- {opt_merge_cmd, opt_remove_unversioned, opt_remove_ignored,
- opt_include_externals, 'q'} },
-
+ ("Either recover from an interrupted operation that left the working copy locked,\n"
+ "or remove unwanted files.\n"
+ "usage: 1. cleanup [WCPATH...]\n"
+ " 2. cleanup --remove-unversioned [WCPATH...]\n"
+ " cleanup --remove-ignored [WCPATH...]\n"
+ " 3. cleanup --vacuum-pristines [WCPATH...]\n"
+ "\n"
+ " 1. When none of the options --remove-unversioned, --remove-ignored, and\n"
+ " --vacuum-pristines is specified, remove all write locks (shown as 'L' by\n"
+ " the 'svn status' command) from the working copy. Usually, this is only\n"
+ " necessary if a Subversion client has crashed while using the working copy,\n"
+ " leaving it in an unusable state.\n"
+ "\n"
+ " WARNING: There is no mechanism that will protect write locks still\n"
+ " being used by other Subversion clients. Running this command\n"
+ " without any options while another client is using the working\n"
+ " copy can corrupt the working copy beyond repair!\n"
+ "\n"
+ " 2. If the --remove-unversioned option or the --remove-ignored option\n"
+ " is given, remove any unversioned or ignored items within WCPATH.\n"
+ " Note that the 'svn status' command shows unversioned items as '?',\n"
+ " and ignored items as 'I' if the --no-ignore option is given to it.\n"
+ "\n"
+ " 3. If the --vacuum-pristines option is given, remove pristine copies of\n"
+ " files which are stored inside the .svn directory and which are no longer\n"
+ " referenced by any file in the working copy.\n"),
+ { opt_remove_unversioned, opt_remove_ignored, opt_vacuum_pristines,
+ opt_include_externals, 'q', opt_merge_cmd },
+ { { opt_merge_cmd, N_("deprecated and ignored") } } },
+
{ "commit", svn_cl__commit, {"ci"},
N_("Send changes from your working copy to the repository.\n"
"usage: commit [PATH...]\n"
"\n"
" A log message must be provided, but it can be empty. If it is not\n"
" given by a --message or --file option, an editor will be started.\n"
+ "\n"
" If any targets are (or contain) locked items, those will be\n"
- " unlocked after a successful commit.\n"
+ " unlocked after a successful commit, unless --no-unlock is given.\n"
"\n"
" If --include-externals is given, also commit file and directory\n"
" externals reached by recursion. Do not commit externals with a\n"
@@ -619,8 +680,9 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
" WC -> URL: immediately commit a copy of WC to URL\n"
" URL -> WC: check out URL into WC, schedule for addition\n"
" URL -> URL: complete server-side copy; used to branch and tag\n"
- " All the SRCs must be of the same type. When copying multiple sources,\n"
- " they will be added as children of DST, which must be a directory.\n"
+ " All the SRCs must be of the same type. If DST is an existing directory,\n"
+ " the sources will be added as children of DST. When copying multiple\n"
+ " sources, DST must be an existing directory.\n"
"\n"
" WARNING: For compatibility with previous versions of Subversion,\n"
" copies performed using two working copy paths (WC -> WC) will not\n"
@@ -734,28 +796,44 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
"usage: info [TARGET[@REV]...]\n"
"\n"
" Print information about each TARGET (default: '.').\n"
- " TARGET may be either a working-copy path or URL. If specified, REV\n"
- " determines in which revision the target is first looked up.\n"
+ " TARGET may be either a working-copy path or a URL. If specified, REV\n"
+ " determines in which revision the target is first looked up; the default\n"
+ " is HEAD for a URL or BASE for a WC path.\n"
"\n"
" With --show-item, print only the value of one item of information\n"
- " about TARGET. One of the following items can be selected:\n"
- " kind the kind of TARGET\n"
- " url the URL of TARGET in the repository\n"
- " relative-url the repository-relative URL\n"
- " repos-root-url the repository root URL\n"
- " repos-uuid the repository UUID\n"
- " revision the revision of TARGET (defaults to BASE\n"
- " for working copy paths and HEAD for URLs)\n"
- " last-changed-revision the most recent revision in which TARGET\n"
- " was changed\n"
- " last-changed-date the date of the last-changed revision\n"
- " last-changed-author the author of the last-changed revision\n"
- " wc-root the root of TARGET's working copy\n"),
+ " about TARGET.\n"),
{'r', 'R', opt_depth, opt_targets, opt_incremental, opt_xml,
opt_changelist, opt_include_externals, opt_show_item, opt_no_newline}
},
- { "list", svn_cl__list, {"ls"}, N_
+ { "list", svn_cl__list, {"ls"},
+#if defined(WIN32)
+ N_
+ ("List directory entries in the repository.\n"
+ "usage: list [TARGET[@REV]...]\n"
+ "\n"
+ " List each TARGET file and the contents of each TARGET directory as\n"
+ " they exist in the repository. If TARGET is a working copy path, the\n"
+ " corresponding repository URL will be used. If specified, REV determines\n"
+ " in which revision the target is first looked up.\n"
+ "\n"
+ " The default TARGET is '.', meaning the repository URL of the current\n"
+ " working directory.\n"
+ "\n"
+ " Multiple --search patterns may be specified and the output will be\n"
+ " reduced to those paths whose last segment - i.e. the file or directory\n"
+ " name - contains a sub-string matching at least one of these patterns\n"
+ " (Windows only).\n"
+ "\n"
+ " With --verbose, the following fields will be shown for each item:\n"
+ "\n"
+ " Revision number of the last commit\n"
+ " Author of the last commit\n"
+ " If locked, the letter 'O'. (Use 'svn info URL' to see details)\n"
+ " Size (in bytes)\n"
+ " Date and time of the last commit\n"),
+#else
+ N_
("List directory entries in the repository.\n"
"usage: list [TARGET[@REV]...]\n"
"\n"
@@ -767,6 +845,10 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
" The default TARGET is '.', meaning the repository URL of the current\n"
" working directory.\n"
"\n"
+ " Multiple --search patterns may be specified and the output will be\n"
+ " reduced to those paths whose last segment - i.e. the file or directory\n"
+ " name - matches at least one of these patterns.\n"
+ "\n"
" With --verbose, the following fields will be shown for each item:\n"
"\n"
" Revision number of the last commit\n"
@@ -774,19 +856,21 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
" If locked, the letter 'O'. (Use 'svn info URL' to see details)\n"
" Size (in bytes)\n"
" Date and time of the last commit\n"),
+#endif
{'r', 'v', 'R', opt_depth, opt_incremental, opt_xml,
- opt_include_externals}, },
+ opt_include_externals, opt_search}, },
{ "lock", svn_cl__lock, {0}, N_
("Lock working copy paths or URLs in the repository, so that\n"
"no other user can commit changes to them.\n"
"usage: lock TARGET...\n"
"\n"
- " Use --force to steal the lock from another user or working copy.\n"),
- { opt_targets, 'm', 'F', opt_force_log, opt_encoding, opt_force },
+ " Use --force to steal a lock from another user or working copy.\n"),
+ { opt_targets, 'm', 'F', opt_force_log, opt_encoding, opt_force, 'q' },
{{'F', N_("read lock comment from file ARG")},
{'m', N_("specify lock comment ARG")},
- {opt_force_log, N_("force validity of lock comment source")}} },
+ {opt_force_log, N_("force validity of lock comment source")},
+ {opt_force, N_("steal locks")}} },
{ "log", svn_cl__log, {0}, N_
("Show the log messages for a set of revision(s) and/or path(s).\n"
@@ -810,6 +894,17 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
" reverse ranges is allowed.\n"
"\n"
" With -v, also print all affected paths with each log message.\n"
+ " Each changed path is preceded with a symbol describing the change:\n"
+ " A: The path was added or copied.\n"
+ " D: The path was deleted.\n"
+ " R: The path was replaced (deleted and re-added in the same revision).\n"
+ " M: The path's file and/or property content was modified.\n"
+ " If an added or replaced path was copied from somewhere else, the copy\n"
+ " source path and revision are shown in parentheses.\n"
+ " If a file or directory was moved from one path to another with 'svn move'\n"
+ " the old path will be listed as deleted and the new path will be listed\n"
+ " as copied from the old path at a prior revision.\n"
+ "\n"
" With -q, don't print the log message body itself (note that this is\n"
" compatible with -v).\n"
"\n"
@@ -862,7 +957,12 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
"\n"
" Show the log message for the revision in which /branches/foo\n"
" was created:\n"
- " svn log --stop-on-copy --limit 1 -r0:HEAD ^/branches/foo\n"),
+ " svn log --stop-on-copy --limit 1 -r0:HEAD ^/branches/foo\n"
+ "\n"
+ " If ^/trunk/foo.c was moved to ^/trunk/bar.c' in revision 22, 'svn log -v'\n"
+ " shows a deletion and a copy in its changed paths list, such as:\n"
+ " D /trunk/foo.c\n"
+ " A /trunk/bar.c (from /trunk/foo.c:21)\n"),
{'r', 'c', 'q', 'v', 'g', opt_targets, opt_stop_on_copy, opt_incremental,
opt_xml, 'l', opt_with_all_revprops, opt_with_no_revprops,
opt_with_revprop, opt_depth, opt_diff, opt_diff_cmd,
@@ -1267,18 +1367,18 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
" be committed later (with or without further changes)\n"
" URL -> URL: move an item in the repository directly, immediately\n"
" creating a new revision in the repository\n"
- " All the SRCs must be of the same type. When moving multiple sources,\n"
- " they will be added as children of DST, which must be a directory.\n"
+ " All the SRCs must be of the same type. If DST is an existing directory,\n"
+ " the sources will be added as children of DST. When moving multiple\n"
+ " sources, DST must be an existing directory.\n"
"\n"
" SRC and DST of WC -> WC moves must be committed in the same revision.\n"
" Furthermore, WC -> WC moves will refuse to move a mixed-revision subtree.\n"
" To avoid unnecessary conflicts, it is recommended to run 'svn update'\n"
" to update the subtree to a single revision before moving it.\n"
- " The --allow-mixed-revisions option is provided for backward compatibility.\n"
- "\n"
- " The --revision option has no use and is deprecated.\n"),
- {'r', 'q', opt_force, opt_parents, opt_allow_mixed_revisions,
- SVN_CL__LOG_MSG_OPTIONS} },
+ " The --allow-mixed-revisions option is provided for backward compatibility.\n"),
+ {'q', opt_force, opt_parents, opt_allow_mixed_revisions,
+ SVN_CL__LOG_MSG_OPTIONS, 'r'},
+ {{'r', "deprecated and ignored"}} },
{ "patch", svn_cl__patch, {0}, N_
("Apply a patch to a working copy.\n"
@@ -1519,7 +1619,48 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
"\n"
" The --accept=ARG option prevents interactive prompting and forces\n"
" conflicts on PATH to be resolved in the manner specified by ARG.\n"
- " In this mode, the command is not recursive by default (depth 'empty').\n"),
+ " In this mode, the command is not recursive by default (depth 'empty').\n"
+ "\n"
+ " A conflicted path cannot be committed with 'svn commit' until it\n"
+ " has been marked as resolved with 'svn resolve'.\n"
+ "\n"
+ " Subversion knows three types of conflicts:\n"
+ " Text conflicts, Property conflicts, and Tree conflicts.\n"
+ "\n"
+ " Text conflicts occur when overlapping changes to file contents were\n"
+ " made. Text conflicts are usually resolved by editing the conflicted\n"
+ " file or by using a merge tool (which may be an external program).\n"
+ " 'svn resolve' provides options which can be used to automatically\n"
+ " edit files (such as 'mine-full' or 'theirs-conflict'), but these are\n"
+ " only useful in situations where it is acceptable to discard local or\n"
+ " incoming changes altogether.\n"
+ "\n"
+ " Property conflicts are usually resolved by editing the value of the\n"
+ " conflicted property (either from the interactive prompt, or with\n"
+ " 'svn propedit'). As with text conflicts, options exist to edit a\n"
+ " property automatically, discarding some changes in favour of others.\n"
+ "\n"
+ " Tree conflicts occur when a change to the directory structure was\n"
+ " made, and when this change cannot be applied to the working copy\n"
+ " without affecting other changes (text changes, property changes,\n"
+ " or other changes to the directory structure). Brief information about\n"
+ " tree conflicts is shown by the 'svn status' and 'svn info' commands.\n"
+ " In interactive mode, 'svn resolve' will attempt to describe tree conflicts\n"
+ " in detail, and may offer options to resolve the conflict automatically.\n"
+ " It is recommended to use these automatic options whenever possible,\n"
+ " rather than attempting manual tree conflict resolution.\n"
+ "\n"
+ " If a tree conflict cannot be resolved automatically, it is recommended\n"
+ " to figure out why the conflict occurred before attempting to resolve it.\n"
+ " The 'svn log -v' command can be used to inspect structural changes\n"
+ " made in past revisions, and perhaps even on other branches.\n"
+ " 'svn help log' describes how these structural changes are presented.\n"
+ " Once the conflicting \"incoming\" change has been identified with 'svn log'\n"
+ " the current \"local\" working copy state should be examined and adjusted\n"
+ " in a way such that the conflict is resolved. This may involve editing\n"
+ " files manually or with 'svn merge'. It may be necessary to discard some\n"
+ " local changes with 'svn revert'. Files or directories might have to be\n"
+ " copied, deleted, or moved.\n"),
{opt_targets, 'R', opt_depth, 'q', opt_accept},
{{opt_accept, N_("specify automatic conflict resolution source\n"
" "
@@ -1685,20 +1826,23 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
" svn switch --relocate http:// svn://\n"
" svn switch --relocate http://www.example.com/repo/project \\\n"
" svn://svn.example.com/repo/project\n"),
- { 'r', 'N', opt_depth, opt_set_depth, 'q', opt_merge_cmd, opt_relocate,
- opt_ignore_externals, opt_ignore_ancestry, opt_force, opt_accept},
+ { 'r', 'N', opt_depth, opt_set_depth, 'q', opt_merge_cmd,
+ opt_ignore_externals, opt_ignore_ancestry, opt_force, opt_accept,
+ opt_relocate },
{{opt_ignore_ancestry,
N_("allow switching to a node with no common ancestor")},
{opt_force,
- N_("handle unversioned obstructions as changes")}}
+ N_("handle unversioned obstructions as changes")},
+ {opt_relocate,N_("deprecated; use 'svn relocate'")}}
},
{ "unlock", svn_cl__unlock, {0}, N_
("Unlock working copy paths or URLs.\n"
"usage: unlock TARGET...\n"
"\n"
- " Use --force to break the lock.\n"),
- { opt_targets, opt_force } },
+ " Use --force to break a lock held by another user or working copy.\n"),
+ { opt_targets, opt_force, 'q' },
+ {{opt_force, N_("break locks")}} },
{ "update", svn_cl__update, {"up"}, N_
("Bring changes from the repository into the working copy.\n"
@@ -1748,7 +1892,7 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
" targets of this operation.\n"),
{'r', 'N', opt_depth, opt_set_depth, 'q', opt_merge_cmd, opt_force,
opt_ignore_externals, opt_changelist, opt_editor_cmd, opt_accept,
- opt_parents},
+ opt_parents, opt_adds_as_modification},
{ {opt_force,
N_("handle unversioned obstructions as changes")} } },
@@ -1759,6 +1903,74 @@ const svn_opt_subcommand_desc2_t svn_cl__cmd_table[] =
" Local modifications are preserved.\n"),
{ 'q' } },
+ { "x-shelve", svn_cl__shelve, {"shelve"}, N_
+ ("Put a local change aside, as if putting it on a shelf.\n"
+ "usage: 1. x-shelve [--keep-local] NAME [PATH...]\n"
+ " 2. x-shelve --delete NAME\n"
+ " 3. x-shelve --list\n"
+ "\n"
+ " 1. Save the local change in the given PATHs to a patch file, and\n"
+ " revert that change from the WC unless '--keep-local' is given.\n"
+ " If a log message is given with '-m' or '-F', include it at the\n"
+ " beginning of the patch file.\n"
+ "\n"
+ " 2. Delete the shelved change NAME.\n"
+ " (A backup is kept, named with a '.bak' extension.)\n"
+ "\n"
+ " 3. List shelved changes. Include the first line of any log message\n"
+ " and some details about the contents of the change, unless '-q' is\n"
+ " given.\n"
+ "\n"
+ " The kinds of change you can shelve are those supported by 'svn diff'\n"
+ " and 'svn patch'. The following are currently NOT supported:\n"
+ " mergeinfo changes, copies, moves, mkdir, rmdir,\n"
+ " 'binary' content, uncommittable states\n"
+ "\n"
+ " To bring back a shelved change, use 'svn x-unshelve NAME'.\n"
+ "\n"
+ " Shelved changes are stored in <WC>/.svn/shelves/\n"
+ "\n"
+ " The shelving feature is EXPERIMENTAL. This command is likely to change\n"
+ " in the next release, and there is no promise of backward compatibility.\n"
+ ),
+ {opt_delete, opt_list, 'q', opt_dry_run, opt_keep_local,
+ opt_depth, opt_targets, opt_changelist,
+ /* almost SVN_CL__LOG_MSG_OPTIONS but not currently opt_with_revprop: */
+ 'm', 'F', opt_force_log, opt_editor_cmd, opt_encoding,
+ } },
+
+ { "x-unshelve", svn_cl__unshelve, {"unshelve"}, N_
+ ("Bring a shelved change back to a local change in the WC.\n"
+ "usage: 1. x-unshelve [--keep-shelved] [NAME]\n"
+ " 2. x-unshelve --list\n"
+ "\n"
+ " 1. Apply the shelved change NAME to the working copy.\n"
+ " Delete the patch unless the '--keep-shelved' option is given.\n"
+ " (A backup is kept, named with a '.bak' extension.)\n"
+ " NAME defaults to the most recent shelved change.\n"
+ "\n"
+ " 2. List shelved changes. Include the first line of any log message\n"
+ " and some details about the contents of the change, unless '-q' is\n"
+ " given.\n"
+ "\n"
+ " Any conflict between the change being unshelved and a change\n"
+ " already in the WC is handled the same way as by 'svn patch',\n"
+ " creating a 'reject' file.\n"
+ "\n"
+ " The shelving feature is EXPERIMENTAL. This command is likely to change\n"
+ " in the next release, and there is no promise of backward compatibility.\n"
+ ),
+ {opt_keep_shelved, opt_list, 'q', opt_dry_run} },
+
+ { "x-shelves", svn_cl__shelves, {"shelves"}, N_
+ ("List shelved changes.\n"
+ "usage: x-shelves\n"
+ "\n"
+ " The shelving feature is EXPERIMENTAL. This command is likely to change\n"
+ " in the next release, and there is no promise of backward compatibility.\n"
+ ),
+ {'q'} },
+
{ NULL, NULL, {0}, NULL, {0} }
};
@@ -1782,29 +1994,8 @@ check_lib_versions(void)
return svn_ver_check_list2(&my_version, checklist, svn_ver_equal);
}
-
-/* A flag to see if we've been cancelled by the client or not. */
-static volatile sig_atomic_t cancelled = FALSE;
-
-/* A signal handler to support cancellation. */
-static void
-signal_handler(int signum)
-{
- apr_signal(signum, SIG_IGN);
- cancelled = TRUE;
-}
-
-/* Our cancellation callback. */
-svn_error_t *
-svn_cl__check_cancel(void *baton)
-{
- /* Cancel baton should be always NULL in command line client. */
- SVN_ERR_ASSERT(baton == NULL);
- if (cancelled)
- return svn_error_create(SVN_ERR_CANCELLED, NULL, _("Caught signal"));
- else
- return SVN_NO_ERROR;
-}
+/* The cancelation handler setup by the cmdline library. */
+svn_cancel_func_t svn_cl__check_cancel = NULL;
/* Add a --search argument to OPT_STATE.
* These options start a new search pattern group. */
@@ -1877,6 +2068,8 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
svn_boolean_t reading_file_from_stdin = FALSE;
apr_hash_t *changelists;
apr_hash_t *cfg_hash;
+ svn_membuf_t buf;
+ svn_boolean_t read_pass_from_stdin = FALSE;
received_opts = apr_array_make(pool, SVN_OPT_MAX_OPTIONS, sizeof(int));
@@ -1897,6 +2090,9 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
/* Init our changelists hash. */
changelists = apr_hash_make(pool);
+ /* Init the temporary buffer. */
+ svn_membuf__create(&buf, 0, pool);
+
/* Begin processing arguments. */
opt_state.start_revision.kind = svn_opt_revision_unspecified;
opt_state.end_revision.kind = svn_opt_revision_unspecified;
@@ -2112,6 +2308,9 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
case opt_dry_run:
opt_state.dry_run = TRUE;
break;
+ case opt_list:
+ opt_state.list = TRUE;
+ break;
case opt_revprop:
opt_state.revprop = TRUE;
break;
@@ -2166,6 +2365,9 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
SVN_ERR(svn_utf_cstring_to_utf8(&opt_state.auth_password,
opt_arg, pool));
break;
+ case opt_auth_password_from_stdin:
+ read_pass_from_stdin = TRUE;
+ break;
case opt_encoding:
opt_state.encoding = apr_pstrdup(pool, opt_arg);
break;
@@ -2292,6 +2494,7 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
opt_state.diff.summarize = TRUE;
break;
case opt_remove:
+ case opt_delete:
opt_state.remove = TRUE;
break;
case opt_changelist:
@@ -2307,6 +2510,7 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
opt_state.keep_changelists = TRUE;
break;
case opt_keep_local:
+ case opt_keep_shelved:
opt_state.keep_local = TRUE;
break;
case opt_with_all_revprops:
@@ -2401,11 +2605,20 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
break;
case opt_search:
SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool));
- add_search_pattern_group(&opt_state, utf8_opt_arg, pool);
+ SVN_ERR(svn_utf__xfrm(&utf8_opt_arg, utf8_opt_arg,
+ strlen(utf8_opt_arg), TRUE, TRUE, &buf));
+ add_search_pattern_group(&opt_state,
+ apr_pstrdup(pool, utf8_opt_arg),
+ pool);
break;
case opt_search_and:
SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool));
- add_search_pattern_to_latest_group(&opt_state, utf8_opt_arg, pool);
+ SVN_ERR(svn_utf__xfrm(&utf8_opt_arg, utf8_opt_arg,
+ strlen(utf8_opt_arg), TRUE, TRUE, &buf));
+ add_search_pattern_to_latest_group(&opt_state,
+ apr_pstrdup(pool, utf8_opt_arg),
+ pool);
+ break;
case opt_remove_unversioned:
opt_state.remove_unversioned = TRUE;
break;
@@ -2426,6 +2639,12 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool));
opt_state.show_item = utf8_opt_arg;
break;
+ case opt_adds_as_modification:
+ opt_state.adds_as_modification = TRUE;
+ break;
+ case opt_vacuum_pristines:
+ opt_state.vacuum_pristines = TRUE;
+ break;
default:
/* Hmmm. Perhaps this would be a good place to squirrel away
opts that commands like svn diff might need. Hmmm indeed. */
@@ -2499,22 +2718,22 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
}
else
{
- const char *first_arg = os->argv[os->ind++];
+ const char *first_arg;
+
+ SVN_ERR(svn_utf_cstring_to_utf8(&first_arg, os->argv[os->ind++],
+ pool));
subcommand = svn_opt_get_canonical_subcommand2(svn_cl__cmd_table,
first_arg);
if (subcommand == NULL)
{
- const char *first_arg_utf8;
- SVN_ERR(svn_utf_cstring_to_utf8(&first_arg_utf8,
- first_arg, pool));
svn_error_clear
(svn_cmdline_fprintf(stderr, pool,
_("Unknown subcommand: '%s'\n"),
- first_arg_utf8));
+ first_arg));
svn_error_clear(svn_cl__help(NULL, NULL, pool));
/* Be kind to people who try 'svn undo'. */
- if (strcmp(first_arg_utf8, "undo") == 0)
+ if (strcmp(first_arg, "undo") == 0)
{
svn_error_clear
(svn_cmdline_fprintf(stderr, pool,
@@ -2645,6 +2864,14 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
"--non-interactive"));
}
+ /* --password-from-stdin can only be used with --non-interactive */
+ if (read_pass_from_stdin && !opt_state.non_interactive)
+ {
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("--password-from-stdin requires "
+ "--non-interactive"));
+ }
+
/* Disallow simultaneous use of both --diff-cmd and
--internal-diff. */
if (opt_state.diff.diff_cmd && opt_state.diff.internal_diff)
@@ -2780,6 +3007,7 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
/* Create a client context object. */
command_baton.opt_state = &opt_state;
+ command_baton.conflict_stats = conflict_stats;
SVN_ERR(svn_client_create_context2(&ctx, cfg_hash, pool));
command_baton.ctx = ctx;
@@ -2795,7 +3023,8 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
|| subcommand->cmd_func == svn_cl__mkdir
|| subcommand->cmd_func == svn_cl__move
|| subcommand->cmd_func == svn_cl__lock
- || subcommand->cmd_func == svn_cl__propedit))
+ || subcommand->cmd_func == svn_cl__propedit
+ || subcommand->cmd_func == svn_cl__shelve))
{
/* If the -F argument is a file that's under revision control,
that's probably not what the user intended. */
@@ -2803,9 +3032,9 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
{
svn_node_kind_t kind;
const char *local_abspath;
- const char *fname_utf8 = svn_dirent_internal_style(dash_F_arg, pool);
+ const char *fname = svn_dirent_internal_style(dash_F_arg, pool);
- err = svn_dirent_get_absolute(&local_abspath, fname_utf8, pool);
+ err = svn_dirent_get_absolute(&local_abspath, fname, pool);
if (!err)
{
@@ -2933,31 +3162,15 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
conflict_stats, pool));
}
+ /* Get password from stdin if necessary */
+ if (read_pass_from_stdin)
+ {
+ SVN_ERR(svn_cmdline__stdin_readline(&opt_state.auth_password, pool, pool));
+ }
+
/* Set up our cancellation support. */
+ svn_cl__check_cancel = svn_cmdline__setup_cancellation_handler();
ctx->cancel_func = svn_cl__check_cancel;
- apr_signal(SIGINT, signal_handler);
-#ifdef SIGBREAK
- /* SIGBREAK is a Win32 specific signal generated by ctrl-break. */
- apr_signal(SIGBREAK, signal_handler);
-#endif
-#ifdef SIGHUP
- apr_signal(SIGHUP, signal_handler);
-#endif
-#ifdef SIGTERM
- apr_signal(SIGTERM, signal_handler);
-#endif
-
-#ifdef SIGPIPE
- /* Disable SIGPIPE generation for the platforms that have it. */
- apr_signal(SIGPIPE, SIG_IGN);
-#endif
-
-#ifdef SIGXFSZ
- /* Disable SIGXFSZ generation for the platforms that have it, otherwise
- * working with large files when compiled against an APR that doesn't have
- * large file support will crash the program, which is uncool. */
- apr_signal(SIGXFSZ, SIG_IGN);
-#endif
/* Set up Authentication stuff. */
SVN_ERR(svn_cmdline_create_auth_baton2(
@@ -2996,10 +3209,11 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
SVN_CL__ACCEPT_LAUNCH);
}
- /* The default action when we're non-interactive is to postpone
- * conflict resolution. */
+ /* The default action when we're non-interactive is to use the
+ * recommended conflict resolution (this will postpone conflicts
+ * for which no recommended resolution is available). */
if (opt_state.accept_which == svn_cl__accept_unspecified)
- opt_state.accept_which = svn_cl__accept_postpone;
+ opt_state.accept_which = svn_cl__accept_recommended;
}
/* Check whether interactive conflict resolution is disabled by
@@ -3021,20 +3235,12 @@ sub_main(int *exit_code, int argc, const char *argv[], apr_pool_t *pool)
opt_state.accept_which = svn_cl__accept_postpone;
}
- /* Install the default conflict handler. */
+ /* We don't use legacy libsvn_wc conflict handlers by default. */
{
- svn_cl__interactive_conflict_baton_t *b;
-
ctx->conflict_func = NULL;
ctx->conflict_baton = NULL;
-
- ctx->conflict_func2 = svn_cl__conflict_func_interactive;
- SVN_ERR(svn_cl__get_conflict_func_interactive_baton(
- &b,
- opt_state.accept_which,
- ctx->config, opt_state.editor_cmd, conflict_stats,
- ctx->cancel_func, ctx->cancel_baton, pool));
- ctx->conflict_baton2 = b;
+ ctx->conflict_func2 = NULL;
+ ctx->conflict_baton2 = NULL;
}
/* And now we finally run the subcommand. */
@@ -3136,5 +3342,8 @@ main(int argc, const char *argv[])
}
svn_pool_destroy(pool);
+
+ svn_cmdline__cancellation_exit();
+
return exit_code;
}
diff --git a/subversion/svn/switch-cmd.c b/subversion/svn/switch-cmd.c
index aaef2b56ae8b..7cab8d980a59 100644
--- a/subversion/svn/switch-cmd.c
+++ b/subversion/svn/switch-cmd.c
@@ -96,8 +96,11 @@ svn_cl__switch(apr_getopt_t *os,
svn_error_t *err = SVN_NO_ERROR;
svn_error_t *externals_err = SVN_NO_ERROR;
svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
+ svn_cl__conflict_stats_t *conflict_stats =
+ ((svn_cl__cmd_baton_t *) baton)->conflict_stats;
svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
apr_array_header_t *targets;
+ apr_array_header_t *conflicted_paths;
const char *target, *switch_url;
svn_opt_revision_t peg_revision;
svn_depth_t depth;
@@ -188,6 +191,13 @@ svn_cl__switch(apr_getopt_t *os,
_("Failure occurred processing one or "
"more externals definitions"));
+ /* Run the interactive resolver if conflicts were raised. */
+ SVN_ERR(svn_cl__conflict_stats_get_paths(&conflicted_paths, conflict_stats,
+ scratch_pool, scratch_pool));
+ if (conflicted_paths)
+ SVN_ERR(svn_cl__walk_conflicts(conflicted_paths, conflict_stats,
+ opt_state, ctx, scratch_pool));
+
if (! opt_state->quiet)
{
err = svn_cl__notifier_print_conflict_stats(nwb.wrapped_baton, scratch_pool);
diff --git a/subversion/svn/update-cmd.c b/subversion/svn/update-cmd.c
index 77c28f97ca40..2820615ea8c2 100644
--- a/subversion/svn/update-cmd.c
+++ b/subversion/svn/update-cmd.c
@@ -111,12 +111,15 @@ svn_cl__update(apr_getopt_t *os,
apr_pool_t *scratch_pool)
{
svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
+ svn_cl__conflict_stats_t *conflict_stats =
+ ((svn_cl__cmd_baton_t *) baton)->conflict_stats;
svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
apr_array_header_t *targets;
svn_depth_t depth;
svn_boolean_t depth_is_sticky;
struct svn_cl__check_externals_failed_notify_baton nwb;
apr_array_header_t *result_revs;
+ apr_array_header_t *conflicted_paths;
svn_error_t *err = SVN_NO_ERROR;
svn_error_t *externals_err = SVN_NO_ERROR;
@@ -167,7 +170,8 @@ svn_cl__update(apr_getopt_t *os,
&(opt_state->start_revision),
depth, depth_is_sticky,
opt_state->ignore_externals,
- opt_state->force, TRUE /* adds_as_modification */,
+ opt_state->force,
+ opt_state->adds_as_modification,
opt_state->parents,
ctx, scratch_pool));
@@ -177,6 +181,13 @@ svn_cl__update(apr_getopt_t *os,
_("Failure occurred processing one or "
"more externals definitions"));
+ /* Run the interactive resolver if conflicts were raised. */
+ SVN_ERR(svn_cl__conflict_stats_get_paths(&conflicted_paths, conflict_stats,
+ scratch_pool, scratch_pool));
+ if (conflicted_paths)
+ SVN_ERR(svn_cl__walk_conflicts(conflicted_paths, conflict_stats,
+ opt_state, ctx, scratch_pool));
+
if (! opt_state->quiet)
{
err = print_update_summary(targets, result_revs, scratch_pool);
diff --git a/subversion/svn/util.c b/subversion/svn/util.c
index 88ae27b14824..a18e5f37c9f7 100644
--- a/subversion/svn/util.c
+++ b/subversion/svn/util.c
@@ -614,6 +614,7 @@ svn_cl__try(svn_error_t *err,
if (! quiet)
svn_handle_warning2(stderr, err, "svn: ");
svn_error_clear(err);
+ va_end(ap);
return SVN_NO_ERROR;
}
}
@@ -907,14 +908,17 @@ svn_cl__time_cstring_to_human_cstring(const char **human_cstring,
}
const char *
-svn_cl__node_description(const svn_wc_conflict_version_t *node,
+svn_cl__node_description(const char *repos_root_url,
+ const char *repos_relpath,
+ svn_revnum_t peg_rev,
+ svn_node_kind_t node_kind,
const char *wc_repos_root_URL,
apr_pool_t *pool)
{
const char *root_str = "^";
const char *path_str = "...";
- if (!node)
+ if (!repos_root_url || !repos_relpath || !SVN_IS_VALID_REVNUM(peg_rev))
/* Printing "(none)" the harder way to ensure conformity (mostly with
* translations). */
return apr_psprintf(pool, "(%s)",
@@ -923,18 +927,18 @@ svn_cl__node_description(const svn_wc_conflict_version_t *node,
/* Construct a "caret notation" ^/URL if NODE matches WC_REPOS_ROOT_URL.
* Otherwise show the complete URL, and if we can't, show dots. */
- if (node->repos_url &&
+ if (repos_root_url &&
(wc_repos_root_URL == NULL ||
- strcmp(node->repos_url, wc_repos_root_URL) != 0))
- root_str = node->repos_url;
+ strcmp(repos_root_url, wc_repos_root_URL) != 0))
+ root_str = repos_root_url;
- if (node->path_in_repos)
- path_str = node->path_in_repos;
+ if (repos_relpath)
+ path_str = repos_relpath;
return apr_psprintf(pool, "(%s) %s@%ld",
- svn_cl__node_kind_str_human_readable(node->node_kind),
+ svn_cl__node_kind_str_human_readable(node_kind),
svn_path_url_add_component2(root_str, path_str, pool),
- node->peg_rev);
+ peg_rev);
}
svn_error_t *