Author: brane
Date: Tue Feb 24 11:59:24 2015
New Revision: 1661901
URL: http://svn.apache.org/r1661901
Log:
On the svn-info-detail branch: Implement 'svn info --show-item [--no-newline]'.
* BRANCH-README: Update the branch description and status.
* subversion/svn/cl.h
(svn_cl__opt_state_t): New option show_item.
* subversion/svn/svn.c
(svn_cl__longopt_t): New option opt_show_item.
(svn_cl__options): Describe it.
(svn_cl__cmd_table): Add opt_show_item and opt_no_newline to the
list of available options for 'svn info', and add a detailed description
of the 'svn info --show-item' invocation.
(sub_main): Parse the --show-item option argument.
* subversion/svn/info-cmd.c
(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): 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 option, including checking valid option combinatinos.
Modified:
subversion/branches/svn-info-detail/BRANCH-README
subversion/branches/svn-info-detail/subversion/svn/cl.h
subversion/branches/svn-info-detail/subversion/svn/info-cmd.c
subversion/branches/svn-info-detail/subversion/svn/svn.c
Modified: subversion/branches/svn-info-detail/BRANCH-README
URL:
http://svn.apache.org/viewvc/subversion/branches/svn-info-detail/BRANCH-README?rev=1661901&r1=1661900&r2=1661901&view=diff
==============================================================================
--- subversion/branches/svn-info-detail/BRANCH-README (original)
+++ subversion/branches/svn-info-detail/BRANCH-README Tue Feb 24 11:59:24 2015
@@ -4,20 +4,47 @@ the problem described in issue #4299 in
Instead of 'svn youngest', we'll implement an extension to 'svn info',
as described in option 1. of issue #4556, as follows:
- - Add a --detail=FIELD option to 'svn info' that will cause it to
+ - The 'svn youngest' command will be removed.
+
+ - Add a --show-item=FIELD option to 'svn info' that will cause it to
display only the value of FIELD and nothing else; for example:
- $ svn info ^/subversion/trunk --detail=revision
+ $ svn info ^/subversion/trunk --show-item=revision
1660035
- $ svn info ^/subversion/trunk --detail=last-changed-rev
+ $ svn info ^/subversion/trunk --show-item=last-changed-rev
1660014
and so on.
- - The --detail option is incompatible with the --xml option
+ When the 'svn info' command is invoked on multiple targets or
+ in recursive mode, the output will be modified to print the
+ requested item and the path or URL of the target:
+
+ $ svn info ^/subversion/trunk -R --show-item=last-changed-rev
+ 1660014 http://svn.apache.org/repos/asf/subversion/trunk
+ 915036 https://svn.apache.org/repos/asf/subversion/trunk/BUGS
+ ...
+
+ - The --show-item option is incompatible with the --xml option
+
+ - As was the case with the now defunct 'svn youngest' subcommand,
+ it's possible to use --no-newline with --show-item.
- Initially, only a few field values will be supported with
- --detail; revision, last-changed-rev, url, relative-url,
+ --show-item; revision, last-changed-rev, url, relative-url,
repository-root; maybe a few more.
- - The 'svn youngest' command will be removed.
+
+Branch Status
+=============
+
+TODO:
+ - Add tests for 'svn info --show-item'.
+ - Modify --show-item output with multiple targets and/or recursive
+ mode to display both the requested item and the (relative) path
+ and/or URL it applies to.
+
+DONE:
+ - Removed 'svn youngest' (already merged to trunk before 1.9.x branch).
+ - Implemented 'svn info --show-item [--no-newline]' with output suitable
+ for single targets.
Modified: subversion/branches/svn-info-detail/subversion/svn/cl.h
URL:
http://svn.apache.org/viewvc/subversion/branches/svn-info-detail/subversion/svn/cl.h?rev=1661901&r1=1661900&r2=1661901&view=diff
==============================================================================
--- subversion/branches/svn-info-detail/subversion/svn/cl.h (original)
+++ subversion/branches/svn-info-detail/subversion/svn/cl.h Tue Feb 24 11:59:24
2015
@@ -249,6 +249,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;
Modified: subversion/branches/svn-info-detail/subversion/svn/info-cmd.c
URL:
http://svn.apache.org/viewvc/subversion/branches/svn-info-detail/subversion/svn/info-cmd.c?rev=1661901&r1=1661900&r2=1661901&view=diff
==============================================================================
--- subversion/branches/svn-info-detail/subversion/svn/info-cmd.c (original)
+++ subversion/branches/svn-info-detail/subversion/svn/info-cmd.c Tue Feb 24
11:59:24 2015
@@ -89,6 +89,150 @@ relative_url(const svn_client_info2_t *i
}
+/* 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 print the trailing newline? */
+ svn_boolean_t print_newline;
+
+} 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 */
static svn_error_t *
@@ -99,7 +243,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);
@@ -109,7 +253,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);
@@ -269,11 +413,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. */
@@ -394,14 +538,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)));
}
@@ -449,21 +593,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;
@@ -472,7 +619,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;
@@ -580,6 +727,96 @@ print_info(void *baton,
}
+/* 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;
+
+ switch (receiver_baton->print_what)
+ {
+ case info_item_kind:
+ switch (info->kind)
+ {
+ case svn_node_file:
+ SVN_ERR(svn_cmdline_fputs("file", stdout, pool));
+ break;
+
+ case svn_node_dir:
+ SVN_ERR(svn_cmdline_fputs("directory", stdout, pool));
+ break;
+
+ case svn_node_none:
+ SVN_ERR(svn_cmdline_fputs("none", stdout, pool));
+ break;
+
+ case svn_node_unknown:
+ default:
+ SVN_ERR(svn_cmdline_fputs("unknown", stdout, pool));
+ break;
+ }
+
+ case info_item_url:
+ SVN_ERR(svn_cmdline_fputs(info->URL, stdout, pool));
+ break;
+
+ case info_item_relative_url:
+ SVN_ERR(svn_cmdline_fputs(relative_url(info, pool), stdout, pool));
+ break;
+
+ case info_item_repos_root_url:
+ SVN_ERR(svn_cmdline_fputs(info->repos_root_URL, stdout, pool));
+ break;
+
+ case info_item_repos_uuid:
+ SVN_ERR(svn_cmdline_fputs(info->repos_UUID, stdout, pool));
+ break;
+
+ case info_item_revision:
+ SVN_ERR(svn_cmdline_printf(pool, "%ld", info->rev));
+ break;
+
+ case info_item_last_changed_rev:
+ SVN_ERR(svn_cmdline_printf(pool, "%ld", info->last_changed_rev));
+ break;
+
+ case info_item_last_changed_date:
+ SVN_ERR(svn_cmdline_fputs(
+ svn_time_to_cstring(info->last_changed_date, pool),
+ stdout, pool));
+ break;
+
+ case info_item_last_changed_author:
+ SVN_ERR(svn_cmdline_fputs(info->last_changed_author, stdout, pool));
+ break;
+
+ case info_item_wc_root:
+ /* FIXME: Consider just printing nothing if wc-root is not available. */
+ if (info->wc_info && info->wc_info->wcroot_abspath)
+ SVN_ERR(svn_cmdline_fputs(svn_dirent_local_style(
+ info->wc_info->wcroot_abspath,
+ pool),
+ stdout, pool));
+ else
+ return svn_error_create(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
+ _("Can't print the working copy root of"
+ " something that is not in a working copy"));
+ break;
+
+ default:
+ SVN_ERR_MALFUNCTION();
+ }
+
+ if (receiver_baton->print_newline)
+ SVN_ERR(svn_cmdline_fputs("\n", stdout, pool));
+
+ return SVN_NO_ERROR;
+}
+
+
/* This implements the `svn_opt_subcommand_t' interface. */
svn_error_t *
svn_cl__info(apr_getopt_t *os,
@@ -595,7 +832,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,
@@ -608,26 +845,52 @@ 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,
+ _("the 'show-item' option is not valid"
+ " in XML mode"));
+ if (opt_state->no_newline)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("the 'no-newline' option is only valid"
+ " with the 'show-item' option"));
+
/* 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,
+ _("the 'incremental' option is only valid"
+ " in XML mode"));
+
+ SVN_ERR(find_print_what(opt_state->show_item,
+ &receiver_baton, pool));
+ receiver_baton.print_newline = !opt_state->no_newline;
+ }
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"));
+ _("the 'incremental' option is only valid"
+ " in XML mode"));
+ if (opt_state->no_newline)
+ return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
+ _("the 'no-newline' option is only valid"
+ " with the 'show-item' option"));
}
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++)
{
@@ -658,7 +921,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)
Modified: subversion/branches/svn-info-detail/subversion/svn/svn.c
URL:
http://svn.apache.org/viewvc/subversion/branches/svn-info-detail/subversion/svn/svn.c?rev=1661901&r1=1661900&r2=1661901&view=diff
==============================================================================
--- subversion/branches/svn-info-detail/subversion/svn/svn.c (original)
+++ subversion/branches/svn-info-detail/subversion/svn/svn.c Tue Feb 24
11:59:24 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
*
@@ -721,9 +724,25 @@ 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 repositor-relative URL\n"
+ " repos-root-url the repository root URL\n"
+ " repos-uuid the repository UUID\n"
+ " revision if TARGET is a local path, its working\n"
+ " copy revision; otherwise, the HEAD revision\n"
+ " of the repository\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_
@@ -2391,6 +2410,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. */