diff options
author | peter <peter@FreeBSD.org> | 2013-06-18 02:07:41 +0000 |
---|---|---|
committer | peter <peter@FreeBSD.org> | 2013-06-18 02:07:41 +0000 |
commit | d25dac7fcc6acc838b71bbda8916fd9665c709ab (patch) | |
tree | 135691142dc0e75a5e5d97b5074d03436435b8e0 /subversion/svn/mergeinfo-cmd.c | |
download | FreeBSD-src-d25dac7fcc6acc838b71bbda8916fd9665c709ab.zip FreeBSD-src-d25dac7fcc6acc838b71bbda8916fd9665c709ab.tar.gz |
Import trimmed svn-1.8.0-rc3
Diffstat (limited to 'subversion/svn/mergeinfo-cmd.c')
-rw-r--r-- | subversion/svn/mergeinfo-cmd.c | 349 |
1 files changed, 349 insertions, 0 deletions
diff --git a/subversion/svn/mergeinfo-cmd.c b/subversion/svn/mergeinfo-cmd.c new file mode 100644 index 0000000..a78c42a --- /dev/null +++ b/subversion/svn/mergeinfo-cmd.c @@ -0,0 +1,349 @@ +/* + * mergeinfo-cmd.c -- Query merge-relative info. + * + * ==================================================================== + * 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. + * ==================================================================== + */ + +/* ==================================================================== */ + + + +/*** Includes. ***/ + +#include "svn_pools.h" +#include "svn_client.h" +#include "svn_cmdline.h" +#include "svn_path.h" +#include "svn_error.h" +#include "svn_error_codes.h" +#include "svn_types.h" +#include "cl.h" + +#include "svn_private_config.h" + + +/*** Code. ***/ + +/* Implements the svn_log_entry_receiver_t interface. */ +static svn_error_t * +print_log_rev(void *baton, + svn_log_entry_t *log_entry, + apr_pool_t *pool) +{ + if (log_entry->non_inheritable) + SVN_ERR(svn_cmdline_printf(pool, "r%ld*\n", log_entry->revision)); + else + SVN_ERR(svn_cmdline_printf(pool, "r%ld\n", log_entry->revision)); + + return SVN_NO_ERROR; +} + +/* Draw a diagram (by printing text to the console) summarizing the state + * of merging between two branches, given the merge description + * indicated by YCA, BASE, RIGHT, TARGET, REINTEGRATE_LIKE. */ +static svn_error_t * +mergeinfo_diagram(const char *yca_url, + const char *base_url, + const char *right_url, + const char *target_url, + svn_revnum_t yca_rev, + svn_revnum_t base_rev, + svn_revnum_t right_rev, + svn_revnum_t target_rev, + const char *repos_root_url, + svn_boolean_t target_is_wc, + svn_boolean_t reintegrate_like, + apr_pool_t *pool) +{ + /* The graph occupies 4 rows of text, and the annotations occupy + * another 2 rows above and 2 rows below. The graph is constructed + * from left to right in discrete sections ("columns"), each of which + * can have a different width (measured in characters). Each element in + * the array is either a text string of the appropriate width, or can + * be NULL to draw a blank cell. */ +#define ROWS 8 +#define COLS 4 + const char *g[ROWS][COLS] = {{0}}; + int col_width[COLS]; + int row, col; + + /* The YCA (that is, the branching point). And an ellipsis, because we + * don't show information about earlier merges */ + g[0][0] = apr_psprintf(pool, " %-8ld ", yca_rev); + g[1][0] = " | "; + if (strcmp(yca_url, right_url) == 0) + { + g[2][0] = "-------| |--"; + g[3][0] = " \\ "; + g[4][0] = " \\ "; + g[5][0] = " --| |--"; + } + else if (strcmp(yca_url, target_url) == 0) + { + g[2][0] = " --| |--"; + g[3][0] = " / "; + g[4][0] = " / "; + g[5][0] = "-------| |--"; + } + else + { + g[2][0] = " --| |--"; + g[3][0] = "... / "; + g[4][0] = " \\ "; + g[5][0] = " --| |--"; + } + + /* The last full merge */ + if ((base_rev > yca_rev) && reintegrate_like) + { + g[2][2] = "---------"; + g[3][2] = " / "; + g[4][2] = " / "; + g[5][2] = "---------"; + g[6][2] = "| "; + g[7][2] = apr_psprintf(pool, "%-8ld ", base_rev); + } + else if (base_rev > yca_rev) + { + g[0][2] = apr_psprintf(pool, "%-8ld ", base_rev); + g[1][2] = "| "; + g[2][2] = "---------"; + g[3][2] = " \\ "; + g[4][2] = " \\ "; + g[5][2] = "---------"; + } + else + { + g[2][2] = "---------"; + g[3][2] = " "; + g[4][2] = " "; + g[5][2] = "---------"; + } + + /* The tips of the branches */ + { + g[0][3] = apr_psprintf(pool, "%-8ld", right_rev); + g[1][3] = "| "; + g[2][3] = "- "; + g[3][3] = " "; + g[4][3] = " "; + g[5][3] = "- "; + g[6][3] = "| "; + g[7][3] = target_is_wc ? "WC " + : apr_psprintf(pool, "%-8ld", target_rev); + } + + /* Find the width of each column, so we know how to print blank cells */ + for (col = 0; col < COLS; col++) + { + col_width[col] = 0; + for (row = 0; row < ROWS; row++) + { + if (g[row][col] && ((int)strlen(g[row][col]) > col_width[col])) + col_width[col] = (int)strlen(g[row][col]); + } + } + + /* Column headings */ + SVN_ERR(svn_cmdline_printf(pool, + " %s\n" + " | %s\n" + " | | %s\n" + " | | | %s\n" + "\n", + _("youngest common ancestor"), _("last full merge"), + _("tip of branch"), _("repository path"))); + + /* Print the diagram, row by row */ + for (row = 0; row < ROWS; row++) + { + SVN_ERR(svn_cmdline_fputs(" ", stdout, pool)); + for (col = 0; col < COLS; col++) + { + if (g[row][col]) + { + SVN_ERR(svn_cmdline_fputs(g[row][col], stdout, pool)); + } + else + { + /* Print <column-width> spaces */ + SVN_ERR(svn_cmdline_printf(pool, "%*s", col_width[col], "")); + } + } + if (row == 2) + SVN_ERR(svn_cmdline_printf(pool, " %s", + svn_uri_skip_ancestor(repos_root_url, right_url, pool))); + if (row == 5) + SVN_ERR(svn_cmdline_printf(pool, " %s", + svn_uri_skip_ancestor(repos_root_url, target_url, pool))); + SVN_ERR(svn_cmdline_fputs("\n", stdout, pool)); + } + + return SVN_NO_ERROR; +} + +/* Display a summary of the state of merging between the two branches + * SOURCE_PATH_OR_URL@SOURCE_REVISION and + * TARGET_PATH_OR_URL@TARGET_REVISION. */ +static svn_error_t * +mergeinfo_summary( + const char *source_path_or_url, + const svn_opt_revision_t *source_revision, + const char *target_path_or_url, + const svn_opt_revision_t *target_revision, + svn_client_ctx_t *ctx, + apr_pool_t *pool) +{ + const char *yca_url, *base_url, *right_url, *target_url; + svn_revnum_t yca_rev, base_rev, right_rev, target_rev; + const char *repos_root_url; + svn_boolean_t target_is_wc, is_reintegration; + + target_is_wc = (! svn_path_is_url(target_path_or_url)) + && (target_revision->kind == svn_opt_revision_unspecified + || target_revision->kind == svn_opt_revision_working); + SVN_ERR(svn_client_get_merging_summary( + &is_reintegration, + &yca_url, &yca_rev, + &base_url, &base_rev, + &right_url, &right_rev, + &target_url, &target_rev, + &repos_root_url, + source_path_or_url, source_revision, + target_path_or_url, target_revision, + ctx, pool, pool)); + + SVN_ERR(mergeinfo_diagram(yca_url, base_url, right_url, target_url, + yca_rev, base_rev, right_rev, target_rev, + repos_root_url, target_is_wc, is_reintegration, + pool)); + + return SVN_NO_ERROR; +} + +/* This implements the `svn_opt_subcommand_t' interface. */ +svn_error_t * +svn_cl__mergeinfo(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; + apr_array_header_t *targets; + const char *source, *target; + svn_opt_revision_t src_peg_revision, tgt_peg_revision; + svn_opt_revision_t *src_start_revision, *src_end_revision; + /* Default to depth empty. */ + svn_depth_t depth = (opt_state->depth == svn_depth_unknown) + ? svn_depth_empty : opt_state->depth; + + SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os, + opt_state->targets, + ctx, FALSE, pool)); + + /* Parse the arguments: SOURCE[@REV] optionally followed by TARGET[@REV]. */ + if (targets->nelts < 1) + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Not enough arguments given")); + if (targets->nelts > 2) + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Too many arguments given")); + SVN_ERR(svn_opt_parse_path(&src_peg_revision, &source, + APR_ARRAY_IDX(targets, 0, const char *), pool)); + if (targets->nelts == 2) + { + SVN_ERR(svn_opt_parse_path(&tgt_peg_revision, &target, + APR_ARRAY_IDX(targets, 1, const char *), + pool)); + } + else + { + target = ""; + tgt_peg_revision.kind = svn_opt_revision_unspecified; + } + + /* If no peg-rev was attached to the source URL, assume HEAD. */ + /* ### But what if SOURCE is a WC path not a URL -- shouldn't we then use + * BASE (but not WORKING: that would be inconsistent with 'svn merge')? */ + if (src_peg_revision.kind == svn_opt_revision_unspecified) + src_peg_revision.kind = svn_opt_revision_head; + + /* If no peg-rev was attached to a URL target, then assume HEAD; if + no peg-rev was attached to a non-URL target, then assume BASE. */ + /* ### But we would like to be able to examine a working copy with an + uncommitted merge in it, so change this to use WORKING not BASE? */ + if (tgt_peg_revision.kind == svn_opt_revision_unspecified) + { + if (svn_path_is_url(target)) + tgt_peg_revision.kind = svn_opt_revision_head; + else + tgt_peg_revision.kind = svn_opt_revision_base; + } + + SVN_ERR_W(svn_cl__check_related_source_and_target(source, &src_peg_revision, + target, &tgt_peg_revision, + ctx, pool), + _("Source and target must be different but related branches")); + + src_start_revision = &(opt_state->start_revision); + if (opt_state->end_revision.kind == svn_opt_revision_unspecified) + src_end_revision = src_start_revision; + else + src_end_revision = &(opt_state->end_revision); + + /* Do the real work, depending on the requested data flavor. */ + if (opt_state->show_revs == svn_cl__show_revs_merged) + { + SVN_ERR(svn_client_mergeinfo_log2(TRUE, target, &tgt_peg_revision, + source, &src_peg_revision, + src_start_revision, + src_end_revision, + print_log_rev, NULL, + TRUE, depth, NULL, ctx, + pool)); + } + else if (opt_state->show_revs == svn_cl__show_revs_eligible) + { + SVN_ERR(svn_client_mergeinfo_log2(FALSE, target, &tgt_peg_revision, + source, &src_peg_revision, + src_start_revision, + src_end_revision, + print_log_rev, NULL, + TRUE, depth, NULL, ctx, + pool)); + } + else + { + if ((opt_state->start_revision.kind != svn_opt_revision_unspecified) + || (opt_state->end_revision.kind != svn_opt_revision_unspecified)) + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("--revision (-r) option valid only with " + "--show-revs option")); + if (opt_state->depth != svn_depth_unknown) + return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, + _("Depth specification options valid only " + "with --show-revs option")); + + SVN_ERR(mergeinfo_summary(source, &src_peg_revision, + target, &tgt_peg_revision, + ctx, pool)); + } + return SVN_NO_ERROR; +} |