Author: brane
Date: Fri Feb 27 02:19:41 2015
New Revision: 1662620
URL: http://svn.apache.org/r1662620
Log:
Merged the svn-info-detail branch to trunk.
This implements the --show-detail option for the 'svn info' command.
* subversion/svn/cl.h
(svn_cl__opt_state_t): New option show_item.
(svn_cl__simcheck_context_t): New forward-declared struct.
(svn_cl__simcheck_t): New structure.
(svn_cl__similarity_check): New client-local function.
* subversion/svn/info-cmd.c
(relative_url): New; constructs a relative URL.
(info_item_t): New enumeration.
(info_item_map_t): New struct.
(info_item_map): Static array of known keywords for --show-item.
(info_item_map_len): New.
(print_info_baton_t): New; baton for the various print_info functions.
Replaces the former use of just the path_prefix string as the baton.
(find_print_what): New; converts the --show-item argument to its
equivalent info_item_t enumeration value.
(print_info_xml, print_info): Call relative_url instead of
maintaining the same logic in two places.
Adjust to match the new baton type.
(print_info_item): New handler for 'svn info --show-item'.
(svn_cl__info): Initialize the receiver baton and handle the
--show-item and --no-newline options, including checking valid
option combinatinos. Change the texts of the parsing-error messages
to show the complete offending option names.
* subversion/include/private/svn_string_private.h
(SVN_STRING__SIM_RANGE_MAX): New; parametrized the similarity range.
(svn_cstring__similarity): Update docstring and adjust return type.
(svn_string__similarity): Adjust return type.
* subversion/libsvn_subr/string.c
(svn_cstring__similarity): Adjust return type.
(svn_string__similarity): Adjust return type. Change the result range.
* subversion/svn/props.c
(simprop_context_t, simprop_t): Removed in favour of the new
svn_cl__simcheck_context_t and svn_cl__simcheck_t.
(simprop_key_diff, simprop_compare): Moved to similarity.c.
(svn_cl__check_svn_prop_name): Use the new similarity check API.
* subversion/svn/similarity.c: New file.
(svn_cl__simcheck_context_t): Defined here.
(simcheck_key_diff): Replaces simprop_key_diff from props.c.
(simcheck_compare): Replaces simprop_compare from props.c.
(svn_cl__similarity_check): Implement.
* subversion/tests/libsvn_subr/string-test.c
(test_string_similarity): Adjust test case to account for the
changed range of the result of svn_string__similarity.
* subversion/tests/cmdline/info_tests.py
(info_item_simple,
info_item_simple_multiple,
info_item_url,
info_item_uncommmitted,
info_item_failures): New test cases.
(test_list): Updated.
Added:
subversion/trunk/subversion/svn/similarity.c
- copied unchanged from r1662618,
subversion/branches/svn-info-detail/subversion/svn/similarity.c
Modified:
subversion/trunk/ (props changed)
subversion/trunk/subversion/include/private/svn_string_private.h
subversion/trunk/subversion/libsvn_subr/string.c
subversion/trunk/subversion/svn/cl.h
subversion/trunk/subversion/svn/info-cmd.c
subversion/trunk/subversion/svn/props.c
subversion/trunk/subversion/svn/svn.c
subversion/trunk/subversion/tests/cmdline/info_tests.py
subversion/trunk/subversion/tests/libsvn_subr/string-test.c
Propchange: subversion/trunk/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Fri Feb 27 02:19:41 2015
@@ -70,7 +70,7 @@
/subversion/branches/revprop-packing:1143907,1143971,1143997,1144017,1144499,1144568,1146145
/subversion/branches/subtree-mergeinfo:876734-878766
/subversion/branches/svn-auth-x509:1603509-1655900
-/subversion/branches/svn-info-detail:1660035-1660413
+/subversion/branches/svn-info-detail:1660035-1662618
/subversion/branches/svn-mergeinfo-enhancements:870119-870195,870197-870288
/subversion/branches/svn-patch-improvements:918519-934609
/subversion/branches/svn_mutex:1141683-1182099
Modified: subversion/trunk/subversion/include/private/svn_string_private.h
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/include/private/svn_string_private.h?rev=1662620&r1=1662619&r2=1662620&view=diff
==============================================================================
--- subversion/trunk/subversion/include/private/svn_string_private.h (original)
+++ subversion/trunk/subversion/include/private/svn_string_private.h Fri Feb 27
02:19:41 2015
@@ -197,10 +197,17 @@ apr_uint64_t
svn__base36toui64(const char **next, const char *source);
/**
+ * The upper limit of the similarity range returned by
+ * svn_cstring__similarity() and svn_string__similarity().
+ */
+#define SVN_STRING__SIM_RANGE_MAX 1000000
+
+/**
* Computes the similarity score of STRA and STRB. Returns the ratio
* of the length of their longest common subsequence and the average
- * length of the strings, normalized to the range [0..1000].
- * The result is equivalent to Python's
+ * length of the strings, normalized to the range
+ * [0..SVN_STRING__SIM_RANGE_MAX]. The result is equivalent to
+ * Python's
*
* difflib.SequenceMatcher.ratio
*
@@ -225,7 +232,7 @@ svn__base36toui64(const char **next, con
* has O(strlen(STRA) * strlen(STRB)) worst-case performance,
* so do keep a rein on your enthusiasm.
*/
-unsigned int
+apr_size_t
svn_cstring__similarity(const char *stra, const char *strb,
svn_membuf_t *buffer, apr_size_t *rlcs);
@@ -233,7 +240,7 @@ svn_cstring__similarity(const char *stra
* Like svn_cstring__similarity, but accepts svn_string_t's instead
* of NUL-terminated character strings.
*/
-unsigned int
+apr_size_t
svn_string__similarity(const svn_string_t *stringa,
const svn_string_t *stringb,
svn_membuf_t *buffer, apr_size_t *rlcs);
Modified: subversion/trunk/subversion/libsvn_subr/string.c
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_subr/string.c?rev=1662620&r1=1662619&r2=1662620&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_subr/string.c (original)
+++ subversion/trunk/subversion/libsvn_subr/string.c Fri Feb 27 02:19:41 2015
@@ -1300,7 +1300,7 @@ svn__base36toui64(const char **next, con
}
-unsigned int
+apr_size_t
svn_cstring__similarity(const char *stra, const char *strb,
svn_membuf_t *buffer, apr_size_t *rlcs)
{
@@ -1312,7 +1312,7 @@ svn_cstring__similarity(const char *stra
return svn_string__similarity(&stringa, &stringb, buffer, rlcs);
}
-unsigned int
+apr_size_t
svn_string__similarity(const svn_string_t *stringa,
const svn_string_t *stringb,
svn_membuf_t *buffer, apr_size_t *rlcs)
@@ -1401,9 +1401,9 @@ svn_string__similarity(const svn_string_
/* Return similarity ratio rounded to 4 significant digits */
if (total)
- return(unsigned int)((2000 * lcs + total/2) / total);
+ return ((2 * SVN_STRING__SIM_RANGE_MAX * lcs + total/2) / total);
else
- return 1000;
+ return SVN_STRING__SIM_RANGE_MAX;
}
apr_size_t
Modified: subversion/trunk/subversion/svn/cl.h
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/cl.h?rev=1662620&r1=1662619&r2=1662620&view=diff
==============================================================================
--- subversion/trunk/subversion/svn/cl.h (original)
+++ subversion/trunk/subversion/svn/cl.h Fri Feb 27 02:19:41 2015
@@ -248,6 +248,7 @@ typedef struct svn_cl__opt_state_t
svn_boolean_t no_newline; /* do not output the trailing newline */
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_cl__opt_state_t;
@@ -854,6 +855,48 @@ svn_cl__deprecated_merge_reintegrate(con
svn_client_ctx_t *ctx,
apr_pool_t *pool);
+
+/* Forward declaration of the similarity check context. */
+typedef struct svn_cl__simcheck_context_t svn_cl__simcheck_context_t;
+
+/* Token definition for the similarity check. */
+typedef struct svn_cl__simcheck_t
+{
+ /* The token we're checking for similarity. */
+ svn_string_t token;
+
+ /* User data associated with this token. */
+ const void *data;
+
+ /*
+ * The following fields are populated by svn_cl__similarity_check.
+ */
+
+ /* Similarity score [0..SVN_STRING__SIM_RANGE_MAX] */
+ apr_size_t score;
+
+ /* Number of characters of difference from the key. */
+ apr_size_t diff;
+
+ /* Similarity check context (private) */
+ svn_cl__simcheck_context_t *context;
+} svn_cl__simcheck_t;
+
+/* Find the entries in TOKENS that are most similar to KEY.
+ * TOKEN_COUNT is the number of entries in the (mutable) TOKENS array.
+ * Use SCRATCH_POOL for temporary allocations.
+ *
+ * On return, the TOKENS array will be sorted according to similarity
+ * to KEY, in descending order. The return value will be zero if the
+ * first token is an exact match; otherwise, it will be one more than
+ * the number of tokens that are at least two-thirds similar to KEY.
+ */
+apr_size_t
+svn_cl__similarity_check(const char *key,
+ svn_cl__simcheck_t **tokens,
+ apr_size_t token_count,
+ apr_pool_t *scratch_pool);
+
#ifdef __cplusplus
}
#endif /* __cplusplus */
Modified: subversion/trunk/subversion/svn/info-cmd.c
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/info-cmd.c?rev=1662620&r1=1662619&r2=1662620&view=diff
==============================================================================
--- subversion/trunk/subversion/svn/info-cmd.c (original)
+++ subversion/trunk/subversion/svn/info-cmd.c Fri Feb 27 02:19:41 2015
@@ -76,6 +76,167 @@ schedule_str(svn_wc_schedule_t schedule)
}
}
+/* Return a relative URL from information in INFO using POOL for
+ temporary allocation. */
+static const char*
+relative_url(const svn_client_info2_t *info, apr_pool_t *pool)
+{
+ return apr_pstrcat(pool, "^/",
+ svn_path_uri_encode(
+ svn_uri_skip_ancestor(info->repos_root_URL,
+ info->URL, pool),
+ pool), SVN_VA_NULL);
+}
+
+
+/* The kinds of items for print_info_item(). */
+typedef enum
+{
+ /* Entry kind */
+ info_item_kind,
+
+ /* Repository location. */
+ info_item_url,
+ info_item_relative_url,
+ info_item_repos_root_url,
+ info_item_repos_uuid,
+
+ /* Working copy revision or repository HEAD revision */
+ info_item_revision,
+
+ /* Commit details. */
+ info_item_last_changed_rev,
+ info_item_last_changed_date,
+ info_item_last_changed_author,
+
+ /* Working copy information */
+ info_item_wc_root
+} info_item_t;
+
+/* Mapping between option keywords and info_item_t. */
+typedef struct info_item_map_t
+{
+ const svn_string_t keyword;
+ const info_item_t print_what;
+} info_item_map_t;
+
+#define MAKE_STRING(x) { x, sizeof(x) - 1 }
+static const info_item_map_t info_item_map[] =
+ {
+ { MAKE_STRING("kind"), info_item_kind },
+ { MAKE_STRING("url"), info_item_url },
+ { MAKE_STRING("relative-url"), info_item_relative_url },
+ { MAKE_STRING("repos-root-url"), info_item_repos_root_url },
+ { MAKE_STRING("repos-uuid"), info_item_repos_uuid },
+ { MAKE_STRING("revision"), info_item_revision },
+ { MAKE_STRING("last-changed-rev"), info_item_last_changed_rev },
+ { MAKE_STRING("last-changed-date"), info_item_last_changed_date },
+ { MAKE_STRING("last-changed-author"), info_item_last_changed_author },
+ { MAKE_STRING("wc-root"), info_item_wc_root }
+ };
+#undef MAKE_STRING
+
+static const apr_size_t info_item_map_len =
+ (sizeof(info_item_map) / sizeof(info_item_map[0]));
+
+
+/* The baton type used by the info receiver functions. */
+typedef struct print_info_baton_t
+{
+ /* The path prefix that output paths should be normalized to. */
+ const char *path_prefix;
+
+ /*
+ * The following fields are used by print_info_item().
+ */
+
+ /* Which item to print. */
+ info_item_t print_what;
+
+ /* Do we expect to show info for multiple targets? */
+ svn_boolean_t multiple_targets;
+
+ /* TRUE iff the current is a local path. */
+ svn_boolean_t target_is_path;
+
+ /* Did we already print a line of output? */
+ svn_boolean_t start_new_line;
+} print_info_baton_t;
+
+
+/* Find the appropriate info_item_t for KEYWORD and initialize
+ * RECEIVER_BATON for print_info_item(). Use SCRATCH_POOL for
+ * temporary allocation.
+ */
+static svn_error_t *
+find_print_what(const char *keyword,
+ print_info_baton_t *receiver_baton,
+ apr_pool_t *scratch_pool)
+{
+ svn_cl__simcheck_t **keywords = apr_palloc(
+ scratch_pool, info_item_map_len * sizeof(svn_cl__simcheck_t*));
+ svn_cl__simcheck_t *kwbuf = apr_palloc(
+ scratch_pool, info_item_map_len * sizeof(svn_cl__simcheck_t));
+ apr_size_t i;
+
+ for (i = 0; i < info_item_map_len; ++i)
+ {
+ keywords[i] = &kwbuf[i];
+ kwbuf[i].token.data = info_item_map[i].keyword.data;
+ kwbuf[i].token.len = info_item_map[i].keyword.len;
+ kwbuf[i].data = &info_item_map[i];
+ }
+
+ switch (svn_cl__similarity_check(keyword, keywords,
+ info_item_map_len, scratch_pool))
+ {
+ const info_item_map_t *kw0;
+ const info_item_map_t *kw1;
+ const info_item_map_t *kw2;
+
+ case 0: /* Exact match. */
+ kw0 = keywords[0]->data;
+ receiver_baton->print_what = kw0->print_what;
+ return SVN_NO_ERROR;
+
+ case 1:
+ /* The best alternative isn't good enough */
+ return svn_error_createf(
+ SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'%s' is not a valid value for the 'show-item' option."),
+ keyword);
+
+ case 2:
+ /* There is only one good candidate */
+ kw0 = keywords[0]->data;
+ return svn_error_createf(
+ SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'%s' is not a valid value for the 'show-item' option;"
+ " did you mean '%s'?"),
+ keyword, kw0->keyword.data);
+
+ case 3:
+ /* Suggest a list of the most likely candidates */
+ kw0 = keywords[0]->data;
+ kw1 = keywords[1]->data;
+ return svn_error_createf(
+ SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'%s' is not a valid value for the 'show-item' option;"
+ " did you mean '%s' or '%s'?"),
+ keyword, kw0->keyword.data, kw1->keyword.data);
+
+ default:
+ /* Never suggest more than three candidates */
+ kw0 = keywords[0]->data;
+ kw1 = keywords[1]->data;
+ kw2 = keywords[2]->data;
+ return svn_error_createf(
+ SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("'%s' is not a valid value for the 'show-item' option;"
+ " did you mean '%s', '%s' or '%s'?"),
+ keyword, kw0->keyword.data, kw1->keyword.data, kw2->keyword.data);
+ }
+}
/* A callback of type svn_client_info_receiver2_t.
Prints svn info in xml mode to standard out */
@@ -87,7 +248,7 @@ print_info_xml(void *baton,
{
svn_stringbuf_t *sb = svn_stringbuf_create_empty(pool);
const char *rev_str;
- const char *path_prefix = baton;
+ print_info_baton_t *const receiver_baton = baton;
if (SVN_IS_VALID_REVNUM(info->rev))
rev_str = apr_psprintf(pool, "%ld", info->rev);
@@ -97,7 +258,7 @@ print_info_xml(void *baton,
/* "<entry ...>" */
svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "entry",
"path", svn_cl__local_style_skip_ancestor(
- path_prefix, target, pool),
+ receiver_baton->path_prefix, target, pool),
"kind", svn_cl__node_kind_str_xml(info->kind),
"revision", rev_str,
SVN_VA_NULL);
@@ -109,13 +270,7 @@ print_info_xml(void *baton,
{
/* "<relative-url> xx </relative-url>" */
svn_cl__xml_tagged_cdata(&sb, pool, "relative-url",
- apr_pstrcat(pool, "^/",
- svn_path_uri_encode(
- svn_uri_skip_ancestor(
- info->repos_root_URL,
- info->URL, pool),
- pool),
- SVN_VA_NULL));
+ relative_url(info, pool));
}
if (info->repos_root_URL || info->repos_UUID)
@@ -263,11 +418,11 @@ print_info(void *baton,
const svn_client_info2_t *info,
apr_pool_t *pool)
{
- const char *path_prefix = baton;
+ print_info_baton_t *const receiver_baton = baton;
SVN_ERR(svn_cmdline_printf(pool, _("Path: %s\n"),
svn_cl__local_style_skip_ancestor(
- path_prefix, target, pool)));
+ receiver_baton->path_prefix, target, pool)));
/* ### remove this someday: it's only here for cmdline output
compatibility with svn 1.1 and older. */
@@ -285,11 +440,8 @@ print_info(void *baton,
SVN_ERR(svn_cmdline_printf(pool, _("URL: %s\n"), info->URL));
if (info->URL && info->repos_root_URL)
- SVN_ERR(svn_cmdline_printf(pool, _("Relative URL: ^/%s\n"),
- svn_path_uri_encode(
- svn_uri_skip_ancestor(info->repos_root_URL,
- info->URL, pool),
- pool)));
+ SVN_ERR(svn_cmdline_printf(pool, _("Relative URL: %s\n"),
+ relative_url(info, pool)));
if (info->repos_root_URL)
SVN_ERR(svn_cmdline_printf(pool, _("Repository Root: %s\n"),
@@ -391,14 +543,14 @@ print_info(void *baton,
if (info->wc_info->moved_from_abspath)
SVN_ERR(svn_cmdline_printf(pool, _("Moved From: %s\n"),
svn_cl__local_style_skip_ancestor(
- path_prefix,
+ receiver_baton->path_prefix,
info->wc_info->moved_from_abspath,
pool)));
if (info->wc_info->moved_to_abspath)
SVN_ERR(svn_cmdline_printf(pool, _("Moved To: %s\n"),
svn_cl__local_style_skip_ancestor(
- path_prefix,
+ receiver_baton->path_prefix,
info->wc_info->moved_to_abspath,
pool)));
}
@@ -446,21 +598,24 @@ print_info(void *baton,
SVN_ERR(svn_cmdline_printf(pool,
_("Conflict Previous Base File: %s\n"),
svn_cl__local_style_skip_ancestor(
- path_prefix, conflict->base_abspath,
+ 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(
- path_prefix, conflict->my_abspath,
+ 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(
- path_prefix, conflict->their_abspath,
+ receiver_baton->path_prefix,
+ conflict->their_abspath,
pool)));
break;
@@ -469,7 +624,7 @@ print_info(void *baton,
SVN_ERR(svn_cmdline_printf(pool,
_("Conflict Properties File: %s\n"),
svn_cl__local_style_skip_ancestor(
- path_prefix,
+ receiver_baton->path_prefix,
conflict->prop_reject_abspath,
pool)));
printed_prop_conflict_file = TRUE;
@@ -577,6 +732,123 @@ print_info(void *baton,
}
+/* Helper for print_info_item(): Print the value TEXT for TARGET_PATH,
+ either of which may be NULL. Use POOL for temporary allocation. */
+static svn_error_t *
+print_info_item_string(const char *text, const char *target_path,
+ apr_pool_t *pool)
+{
+ if (text)
+ {
+ if (target_path)
+ SVN_ERR(svn_cmdline_printf(pool, "%-10s %s", text, target_path));
+ else
+ SVN_ERR(svn_cmdline_fputs(text, stdout, pool));
+ }
+ else if (target_path)
+ SVN_ERR(svn_cmdline_printf(pool, "%-10s %s", "", target_path));
+
+ return SVN_NO_ERROR;
+}
+
+/* Helper for print_info_item(): Print the revision number REV, which
+ may be SVN_INVALID_REVNUM, for TARGET_PATH, which may be NULL. Use
+ POOL for temporary allocation. */
+static svn_error_t *
+print_info_item_revision(svn_revnum_t rev, const char *target_path,
+ apr_pool_t *pool)
+{
+ if (SVN_IS_VALID_REVNUM(rev))
+ {
+ if (target_path)
+ SVN_ERR(svn_cmdline_printf(pool, "%-10ld %s", rev, target_path));
+ else
+ SVN_ERR(svn_cmdline_printf(pool, "%-10ld", rev));
+ }
+ else if (target_path)
+ SVN_ERR(svn_cmdline_printf(pool, "%-10s %s", "", target_path));
+
+ return SVN_NO_ERROR;
+}
+
+/* A callback of type svn_client_info_receiver2_t. */
+static svn_error_t *
+print_info_item(void *baton,
+ const char *target,
+ const svn_client_info2_t *info,
+ apr_pool_t *pool)
+{
+ print_info_baton_t *const receiver_baton = baton;
+ const char *const target_path =
+ (!receiver_baton->multiple_targets ? NULL
+ : (!receiver_baton->target_is_path ? info->URL
+ : svn_cl__local_style_skip_ancestor(
+ receiver_baton->path_prefix, target, pool)));
+
+ if (receiver_baton->start_new_line)
+ SVN_ERR(svn_cmdline_fputs("\n", stdout, pool));
+
+ switch (receiver_baton->print_what)
+ {
+ case info_item_kind:
+ SVN_ERR(print_info_item_string(svn_node_kind_to_word(info->kind),
+ target_path, pool));
+ break;
+
+ case info_item_url:
+ SVN_ERR(print_info_item_string(info->URL, target_path, pool));
+ break;
+
+ case info_item_relative_url:
+ SVN_ERR(print_info_item_string(relative_url(info, pool),
+ target_path, pool));
+ break;
+
+ case info_item_repos_root_url:
+ SVN_ERR(print_info_item_string(info->repos_root_URL, target_path, pool));
+ break;
+
+ case info_item_repos_uuid:
+ SVN_ERR(print_info_item_string(info->repos_UUID, target_path, pool));
+ break;
+
+ case info_item_revision:
+ SVN_ERR(print_info_item_revision(info->rev, target_path, pool));
+ break;
+
+ case info_item_last_changed_rev:
+ SVN_ERR(print_info_item_revision(info->last_changed_rev,
+ target_path, pool));
+ break;
+
+ case info_item_last_changed_date:
+ SVN_ERR(print_info_item_string(
+ (!info->last_changed_date ? NULL
+ : svn_time_to_cstring(info->last_changed_date, pool)),
+ target_path, pool));
+ break;
+
+ case info_item_last_changed_author:
+ SVN_ERR(print_info_item_string(info->last_changed_author,
+ target_path, pool));
+ break;
+
+ case info_item_wc_root:
+ SVN_ERR(print_info_item_string(
+ (info->wc_info && info->wc_info->wcroot_abspath
+ ? info->wc_info->wcroot_abspath : NULL),
+ target_path, pool));
+ break;
+
+ default:
+ SVN_ERR_MALFUNCTION();
+ }
+
+ receiver_baton->start_new_line = TRUE;
+ return SVN_NO_ERROR;
+}
+
+
/* This implements the `svn_opt_subcommand_t' interface. */
svn_error_t *
svn_cl__info(apr_getopt_t *os,
@@ -592,7 +864,7 @@ svn_cl__info(apr_getopt_t *os,
svn_boolean_t seen_nonexistent_target = FALSE;
svn_opt_revision_t peg_revision;
svn_client_info_receiver2_t receiver;
- const char *path_prefix;
+ print_info_baton_t receiver_baton = { 0 };
SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
opt_state->targets,
@@ -605,26 +877,59 @@ svn_cl__info(apr_getopt_t *os,
{
receiver = print_info_xml;
+ if (opt_state->show_item)
+ return svn_error_create(
+ SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("--show-item is not valid in --xml mode"));
+ if (opt_state->no_newline)
+ return svn_error_create(
+ SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("--no-newline' is not valid in --xml mode"));
+
/* If output is not incremental, output the XML header and wrap
everything in a top-level element. This makes the output in
its entirety a well-formed XML document. */
if (! opt_state->incremental)
SVN_ERR(svn_cl__xml_print_header("info", pool));
}
+ else if (opt_state->show_item)
+ {
+ receiver = print_info_item;
+
+ if (opt_state->incremental)
+ return svn_error_create(
+ SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("--incremental is only valid in --xml mode"));
+
+ receiver_baton.multiple_targets = (opt_state->depth > svn_depth_empty
+ || targets->nelts > 1);
+ if (receiver_baton.multiple_targets && opt_state->no_newline)
+ return svn_error_create(
+ SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("--no-newline is only available for single-target,"
+ " non-recursive info operations"));
+
+ SVN_ERR(find_print_what(opt_state->show_item, &receiver_baton, pool));
+ receiver_baton.start_new_line = FALSE;
+ }
else
{
receiver = print_info;
if (opt_state->incremental)
- return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
- _("'incremental' option only valid in XML "
- "mode"));
+ return svn_error_create(
+ SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("--incremental is only valid in --xml mode"));
+ if (opt_state->no_newline)
+ return svn_error_create(
+ SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("--no-newline' is only valid with --show-item"));
}
if (opt_state->depth == svn_depth_unknown)
opt_state->depth = svn_depth_empty;
- SVN_ERR(svn_dirent_get_absolute(&path_prefix, "", pool));
+ SVN_ERR(svn_dirent_get_absolute(&receiver_baton.path_prefix, "", pool));
for (i = 0; i < targets->nelts; i++)
{
@@ -642,10 +947,12 @@ svn_cl__info(apr_getopt_t *os,
{
if (peg_revision.kind == svn_opt_revision_unspecified)
peg_revision.kind = svn_opt_revision_head;
+ receiver_baton.target_is_path = FALSE;
}
else
{
SVN_ERR(svn_dirent_get_absolute(&truepath, truepath, subpool));
+ receiver_baton.target_is_path = TRUE;
}
err = svn_client_info4(truepath,
@@ -655,7 +962,7 @@ svn_cl__info(apr_getopt_t *os,
TRUE /* fetch_actual_only */,
opt_state->include_externals,
opt_state->changelists,
- receiver, (void *) path_prefix,
+ receiver, &receiver_baton,
ctx, subpool);
if (err)
@@ -682,6 +989,9 @@ svn_cl__info(apr_getopt_t *os,
if (opt_state->xml && (! opt_state->incremental))
SVN_ERR(svn_cl__xml_print_footer("info", pool));
+ else if (opt_state->show_item && !opt_state->no_newline
+ && receiver_baton.start_new_line)
+ SVN_ERR(svn_cmdline_fputs("\n", stdout, pool));
if (seen_nonexistent_target)
return svn_error_create(
Modified: subversion/trunk/subversion/svn/props.c
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/props.c?rev=1662620&r1=1662619&r2=1662620&view=diff
==============================================================================
--- subversion/trunk/subversion/svn/props.c (original)
+++ subversion/trunk/subversion/svn/props.c Fri Feb 27 02:19:41 2015
@@ -112,59 +112,6 @@ svn_cl__check_boolean_prop_val(const cha
}
}
-
-/* Context for sorting property names */
-struct simprop_context_t
-{
- svn_string_t name; /* The name of the property we're comparing with */
- svn_membuf_t buffer; /* Buffer for similarity testing */
-};
-
-struct simprop_t
-{
- const char *propname; /* The original svn: property name */
- svn_string_t name; /* The property name without the svn: prefix */
- unsigned int score; /* The similarity score */
- apr_size_t diff; /* Number of chars different from context.name */
- struct simprop_context_t *context; /* Sorting context for qsort() */
-};
-
-/* Similarity test between two property names */
-static APR_INLINE unsigned int
-simprop_key_diff(const svn_string_t *key, const svn_string_t *ctx,
- svn_membuf_t *buffer, apr_size_t *diff)
-{
- apr_size_t lcs;
- const unsigned int score = svn_string__similarity(key, ctx, buffer, &lcs);
- if (key->len > ctx->len)
- *diff = key->len - lcs;
- else
- *diff = ctx->len - lcs;
- return score;
-}
-
-/* Key comparator for qsort for simprop_t */
-static int
-simprop_compare(const void *pkeya, const void *pkeyb)
-{
- struct simprop_t *const keya = *(struct simprop_t *const *)pkeya;
- struct simprop_t *const keyb = *(struct simprop_t *const *)pkeyb;
- struct simprop_context_t *const context = keya->context;
-
- if (keya->score == -1)
- keya->score = simprop_key_diff(&keya->name, &context->name,
- &context->buffer, &keya->diff);
- if (keyb->score == -1)
- keyb->score = simprop_key_diff(&keyb->name, &context->name,
- &context->buffer, &keyb->diff);
-
- return (keya->score < keyb->score ? 1
- : (keya->score > keyb->score ? -1
- : (keya->diff > keyb->diff ? 1
- : (keya->diff < keyb->diff ? -1 : 0))));
-}
-
-
static const char*
force_prop_option_message(svn_cl__prop_use_t prop_use, const char *prop_name,
apr_pool_t *scratch_pool)
@@ -239,33 +186,34 @@ svn_cl__check_svn_prop_name(const char *
const char *const *const proplist = (revprop ? revprops : nodeprops);
const apr_size_t numprops = (revprop ? revprops_len : nodeprops_len);
- struct simprop_t **propkeys;
- struct simprop_t *propbuf;
+ svn_cl__simcheck_t **propkeys;
+ svn_cl__simcheck_t *propbuf;
apr_size_t i;
- struct simprop_context_t context;
+ svn_string_t propstring;
svn_string_t prefix;
+ svn_membuf_t buffer;
- context.name.data = propname;
- context.name.len = strlen(propname);
+ propstring.data = propname;
+ propstring.len = strlen(propname);
prefix.data = SVN_PROP_PREFIX;
prefix.len = strlen(SVN_PROP_PREFIX);
- svn_membuf__create(&context.buffer, 0, scratch_pool);
+ svn_membuf__create(&buffer, 0, scratch_pool);
/* First, check if the name is even close to being in the svn: namespace.
It must contain a colon in the right place, and we only allow
one-char typos or a single transposition. */
- if (context.name.len < prefix.len
- || context.name.data[prefix.len - 1] != prefix.data[prefix.len - 1])
+ if (propstring.len < prefix.len
+ || propstring.data[prefix.len - 1] != prefix.data[prefix.len - 1])
return SVN_NO_ERROR; /* Wrong prefix, ignore */
else
{
apr_size_t lcs;
- const apr_size_t name_len = context.name.len;
- context.name.len = prefix.len; /* Only check up to the prefix length */
- svn_string__similarity(&context.name, &prefix, &context.buffer, &lcs);
- context.name.len = name_len; /* Restore the original propname length */
+ const apr_size_t name_len = propstring.len;
+ propstring.len = prefix.len; /* Only check up to the prefix length */
+ svn_string__similarity(&propstring, &prefix, &buffer, &lcs);
+ propstring.len = name_len; /* Restore the original propname length */
if (lcs < prefix.len - 1)
return SVN_NO_ERROR; /* Wrong prefix, ignore */
@@ -292,55 +240,47 @@ svn_cl__check_svn_prop_name(const char *
we already know that it's the same and looking at it would only
skew the results. */
propkeys = apr_palloc(scratch_pool,
- numprops * sizeof(struct simprop_t*));
+ numprops * sizeof(svn_cl__simcheck_t*));
propbuf = apr_palloc(scratch_pool,
- numprops * sizeof(struct simprop_t));
- context.name.data += prefix.len;
- context.name.len -= prefix.len;
+ numprops * sizeof(svn_cl__simcheck_t));
+ propstring.data += prefix.len;
+ propstring.len -= prefix.len;
for (i = 0; i < numprops; ++i)
{
propkeys[i] = &propbuf[i];
- propbuf[i].propname = proplist[i];
- propbuf[i].name.data = proplist[i] + prefix.len;
- propbuf[i].name.len = strlen(propbuf[i].name.data);
- propbuf[i].score = (unsigned int)-1;
- propbuf[i].context = &context;
+ propbuf[i].token.data = proplist[i] + prefix.len;
+ propbuf[i].token.len = strlen(propbuf[i].token.data);
+ propbuf[i].data = proplist[i];
}
- qsort(propkeys, numprops, sizeof(*propkeys), simprop_compare);
-
- if (0 == propkeys[0]->diff)
- return SVN_NO_ERROR; /* We found an exact match. */
-
- /* See if we can suggest a sane alternative spelling */
- for (i = 0; i < numprops; ++i)
- if (propkeys[i]->score < 666) /* 2/3 similarity required */
- break;
-
- switch (i)
+ switch (svn_cl__similarity_check(
+ propstring.data, propkeys, numprops, scratch_pool))
{
case 0:
+ return SVN_NO_ERROR; /* We found an exact match. */
+
+ case 1:
/* The best alternative isn't good enough */
return svn_error_create(
SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
wrong_prop_error_message(prop_use, propname, scratch_pool));
- case 1:
+ case 2:
/* There is only one good candidate */
return svn_error_createf(
SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
_("'%s' is not a valid %s property name; did you mean '%s'?\n%s"),
- propname, SVN_PROP_PREFIX, propkeys[0]->propname,
+ propname, SVN_PROP_PREFIX, propkeys[0]->data,
force_prop_option_message(prop_use, propname, scratch_pool));
- case 2:
+ case 3:
/* Suggest a list of the most likely candidates */
return svn_error_createf(
SVN_ERR_CLIENT_PROPERTY_NAME, NULL,
_("'%s' is not a valid %s property name\n"
"Did you mean '%s' or '%s'?\n%s"),
propname, SVN_PROP_PREFIX,
- propkeys[0]->propname, propkeys[1]->propname,
+ propkeys[0]->data, propkeys[1]->data,
force_prop_option_message(prop_use, propname, scratch_pool));
default:
@@ -350,7 +290,7 @@ svn_cl__check_svn_prop_name(const char *
_("'%s' is not a valid %s property name\n"
"Did you mean '%s', '%s' or '%s'?\n%s"),
propname, SVN_PROP_PREFIX,
- propkeys[0]->propname, propkeys[1]->propname, propkeys[2]->propname,
+ propkeys[0]->data, propkeys[1]->data, propkeys[2]->data,
force_prop_option_message(prop_use, propname, scratch_pool));
}
}
Modified: subversion/trunk/subversion/svn/svn.c
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/svn.c?rev=1662620&r1=1662619&r2=1662620&view=diff
==============================================================================
--- subversion/trunk/subversion/svn/svn.c (original)
+++ subversion/trunk/subversion/svn/svn.c Fri Feb 27 02:19:41 2015
@@ -146,6 +146,7 @@ typedef enum svn_cl__longopt_t {
opt_no_newline,
opt_show_passwords,
opt_pin_externals,
+ opt_show_item,
} svn_cl__longopt_t;
@@ -423,6 +424,8 @@ const apr_getopt_option_t svn_cl__option
N_("pin externals with no explicit revision to their\n"
" "
"current revision (recommended when tagging)")},
+ {"show-item", opt_show_item, 1,
+ N_("print only the item identified by ARG")},
/* Long-opt Aliases
*
@@ -736,9 +739,24 @@ const svn_opt_subcommand_desc2_t svn_cl_
"\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"),
+ " determines in which revision the target is first looked up.\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-rev 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"),
{'r', 'R', opt_depth, opt_targets, opt_incremental, opt_xml,
- opt_changelist, opt_include_externals}
+ opt_changelist, opt_include_externals, opt_show_item, opt_no_newline}
},
{ "list", svn_cl__list, {"ls"}, N_
@@ -2404,6 +2422,10 @@ sub_main(int *exit_code, int argc, const
case opt_pin_externals:
opt_state.pin_externals = TRUE;
break;
+ case opt_show_item:
+ SVN_ERR(svn_utf_cstring_to_utf8(&utf8_opt_arg, opt_arg, pool));
+ opt_state.show_item = utf8_opt_arg;
+ break;
default:
/* Hmmm. Perhaps this would be a good place to squirrel away
opts that commands like svn diff might need. Hmmm indeed. */
Modified: subversion/trunk/subversion/tests/cmdline/info_tests.py
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/cmdline/info_tests.py?rev=1662620&r1=1662619&r2=1662620&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/cmdline/info_tests.py (original)
+++ subversion/trunk/subversion/tests/cmdline/info_tests.py Fri Feb 27 02:19:41
2015
@@ -636,6 +636,117 @@ def node_hidden_info(sbox):
sbox.ospath('E/beta'))
+def info_item_simple(sbox):
+ "show one info item"
+
+ sbox.build(read_only=True)
+ svntest.actions.run_and_verify_svn(
+ '1', [],
+ 'info', '--show-item=revision', '--no-newline',
+ sbox.ospath(''))
+
+
+def info_item_simple_multiple(sbox):
+ "show one info item with multiple targets"
+
+ sbox.build(read_only=True)
+
+ svntest.actions.run_and_verify_svn(
+ r'^jrandom\s+\S+/info_tests-\d+(/[^/]+)?$', [],
+ 'info', '--show-item=last-changed-author',
+ '--depth=immediates', sbox.ospath(''))
+
+ svntest.actions.run_and_verify_svn(
+ r'^1\s+\S+/info_tests-\d+/[^/]+$', [],
+ 'info', '--show-item=last-changed-rev',
+ sbox.ospath('A'), sbox.ospath('iota'))
+
+
+def info_item_url(sbox):
+ "show one info item with URL targets"
+
+ sbox.build(create_wc=False, read_only=True)
+
+ svntest.actions.run_and_verify_svn(
+ '1', [],
+ 'info', '--show-item=last-changed-rev',
+ sbox.repo_url)
+
+
+ svntest.actions.run_and_verify_svn(
+ r'^1\s+[^/:]+://.+/repos/[^/]+$', [],
+ 'info', '--show-item=last-changed-rev',
+ sbox.repo_url + '/A', sbox.repo_url + '/iota')
+
+
+ # Empty working copy root on URL targets
+ svntest.actions.run_and_verify_svn(
+ '', [],
+ 'info', '--show-item=wc-root',
+ sbox.repo_url)
+
+
+def info_item_uncommmitted(sbox):
+ "show one info item on uncommitted targets"
+
+ sbox.build()
+
+ svntest.main.file_write(sbox.ospath('newfile'), 'newfile')
+ sbox.simple_add('newfile')
+ sbox.simple_mkdir('newdir')
+
+ svntest.actions.run_and_verify_svn(
+ '', [],
+ 'info', '--show-item=last-changed-rev',
+ sbox.ospath('newfile'))
+
+ svntest.actions.run_and_verify_svn(
+ '', [],
+ 'info', '--show-item=last-changed-author',
+ sbox.ospath('newdir'))
+
+ svntest.actions.run_and_verify_svn(
+ r'\s+\S+/new(file|dir)', [],
+ 'info', '--show-item=last-changed-date',
+ sbox.ospath('newfile'), sbox.ospath('newdir'))
+
+ svntest.actions.run_and_verify_svn(
+ r'\^/new(file|dir)\s+\S+/new(file|dir)', [],
+ 'info', '--show-item=relative-url',
+ sbox.ospath('newfile'), sbox.ospath('newdir'))
+
+
+def info_item_failures(sbox):
+ "failure modes of 'svn info --show-item'"
+
+ sbox.build(read_only=True)
+
+ svntest.actions.run_and_verify_svn(
+ None, r'.*E200009:.*',
+ 'info', '--show-item=revision',
+ sbox.ospath('not-there'))
+
+ svntest.actions.run_and_verify_svn(
+ None, r".*E205000: .*; did you mean 'wc-root'\?",
+ 'info', '--show-item=root',
+ sbox.ospath(''))
+
+ svntest.actions.run_and_verify_svn(
+ None, (r".*E205000: --show-item is not valid in --xml mode"),
+ 'info', '--show-item=revision', '--xml',
+ sbox.ospath(''))
+
+ svntest.actions.run_and_verify_svn(
+ None, (r".*E205000: --incremental is only valid in --xml mode"),
+ 'info', '--show-item=revision', '--incremental',
+ sbox.ospath(''))
+
+ svntest.actions.run_and_verify_svn(
+ None, (r".*E205000: --no-newline is only available.*"),
+ 'info', '--show-item=revision', '--no-newline',
+ sbox.ospath('A'), sbox.ospath('iota'))
+
+
########################################################################
# Run the tests
@@ -652,6 +763,11 @@ test_list = [ None,
binary_tree_conflict,
relpath_escaping,
node_hidden_info,
+ info_item_simple,
+ info_item_simple_multiple,
+ info_item_url,
+ info_item_uncommmitted,
+ info_item_failures,
]
if __name__ == '__main__':
Modified: subversion/trunk/subversion/tests/libsvn_subr/string-test.c
URL:
http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/libsvn_subr/string-test.c?rev=1662620&r1=1662619&r2=1662620&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/libsvn_subr/string-test.c (original)
+++ subversion/trunk/subversion/tests/libsvn_subr/string-test.c Fri Feb 27
02:19:41 2015
@@ -686,10 +686,11 @@ test_string_similarity(apr_pool_t *pool)
unsigned int score;
} tests[] =
{
-#define SCORE(lcs, len) ((2000 * (lcs) + (len)/2) / (len))
+#define SCORE(lcs, len) \
+ ((2 * SVN_STRING__SIM_RANGE_MAX * (lcs) + (len)/2) / (len))
/* Equality */
- {"", "", 0, 1000},
+ {"", "", 0, SVN_STRING__SIM_RANGE_MAX},
{"quoth", "quoth", 5, SCORE(5, 5+5)},
/* Deletion at start */
@@ -743,17 +744,20 @@ test_string_similarity(apr_pool_t *pool)
for (t = tests; t->stra; ++t)
{
apr_size_t lcs;
- const unsigned int score =
+ const apr_size_t score =
svn_cstring__similarity(t->stra, t->strb, &buffer, &lcs);
/*
fprintf(stderr,
- "lcs %s ~ %s score %.3f (%"APR_SIZE_T_FMT
- ") expected %.3f (%"APR_SIZE_T_FMT"))\n",
- t->stra, t->strb, score/1000.0, lcs, t->score/1000.0, t->lcs);
+ "lcs %s ~ %s score %.6f (%"APR_SIZE_T_FMT
+ ") expected %.6f (%"APR_SIZE_T_FMT"))\n",
+ t->stra, t->strb, score/1.0/SVN_STRING__SIM_RANGE_MAX,
+ lcs, t->score/1.0/SVN_STRING__SIM_RANGE_MAX, t->lcs);
*/
if (score != t->score)
- return fail(pool, "%s ~ %s score %.3f <> expected %.3f",
- t->stra, t->strb, score/1000.0, t->score/1000.0);
+ return fail(pool, "%s ~ %s score %.6f <> expected %.6f",
+ t->stra, t->strb,
+ score/1.0/SVN_STRING__SIM_RANGE_MAX,
+ t->score/1.0/SVN_STRING__SIM_RANGE_MAX);
if (lcs != t->lcs)
return fail(pool,
@@ -766,7 +770,8 @@ test_string_similarity(apr_pool_t *pool)
{
const svn_string_t foo = {"svn:foo", 4};
const svn_string_t bar = {"svn:bar", 4};
- if (1000 != svn_string__similarity(&foo, &bar, &buffer, NULL))
+ if (SVN_STRING__SIM_RANGE_MAX
+ != svn_string__similarity(&foo, &bar, &buffer, NULL))
return fail(pool, "'%s'[:4] ~ '%s'[:4] found different",
foo.data, bar.data);
}