Return-Path: X-Original-To: apmail-subversion-commits-archive@minotaur.apache.org Delivered-To: apmail-subversion-commits-archive@minotaur.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 2F19D17BBA for ; Tue, 24 Feb 2015 11:59:25 +0000 (UTC) Received: (qmail 49245 invoked by uid 500); 24 Feb 2015 11:59:25 -0000 Delivered-To: apmail-subversion-commits-archive@subversion.apache.org Received: (qmail 49208 invoked by uid 500); 24 Feb 2015 11:59:25 -0000 Mailing-List: contact commits-help@subversion.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@subversion.apache.org Delivered-To: mailing list commits@subversion.apache.org Received: (qmail 49198 invoked by uid 99); 24 Feb 2015 11:59:25 -0000 Received: from eris.apache.org (HELO hades.apache.org) (140.211.11.105) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 24 Feb 2015 11:59:25 +0000 Received: from hades.apache.org (localhost [127.0.0.1]) by hades.apache.org (ASF Mail Server at hades.apache.org) with ESMTP id E7BE3AC0051 for ; Tue, 24 Feb 2015 11:59:24 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1661901 - in /subversion/branches/svn-info-detail: BRANCH-README subversion/svn/cl.h subversion/svn/info-cmd.c subversion/svn/svn.c Date: Tue, 24 Feb 2015 11:59:24 -0000 To: commits@subversion.apache.org From: brane@apache.org X-Mailer: svnmailer-1.0.9 Message-Id: <20150224115924.E7BE3AC0051@hades.apache.org> 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, /* "" */ 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. */