diff options
Diffstat (limited to 'subversion/svn/mergeinfo-cmd.c')
-rw-r--r-- | subversion/svn/mergeinfo-cmd.c | 255 |
1 files changed, 237 insertions, 18 deletions
diff --git a/subversion/svn/mergeinfo-cmd.c b/subversion/svn/mergeinfo-cmd.c index 5875e2d..41edcda 100644 --- a/subversion/svn/mergeinfo-cmd.c +++ b/subversion/svn/mergeinfo-cmd.c @@ -55,6 +55,189 @@ print_log_rev(void *baton, 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, @@ -66,28 +249,24 @@ svn_cl__mergeinfo(apr_getopt_t *os, 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_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)); - /* We expect a single source URL followed by a single target -- - nothing more, nothing less. */ + /* 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")); - - /* Parse the SOURCE-URL[@REV] argument. */ SVN_ERR(svn_opt_parse_path(&src_peg_revision, &source, APR_ARRAY_IDX(targets, 0, const char *), pool)); - - /* Parse the TARGET[@REV] argument (if provided). */ if (targets->nelts == 2) { SVN_ERR(svn_opt_parse_path(&tgt_peg_revision, &target, @@ -101,11 +280,15 @@ svn_cl__mergeinfo(apr_getopt_t *os, } /* 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)) @@ -114,22 +297,58 @@ svn_cl__mergeinfo(apr_getopt_t *os, tgt_peg_revision.kind = svn_opt_revision_base; } + 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_log(TRUE, target, &tgt_peg_revision, - source, &src_peg_revision, - print_log_rev, NULL, - TRUE, depth, NULL, ctx, - pool)); + apr_array_header_t *revprops; + + /* We need only revisions number, not revision properties. */ + revprops = apr_array_make(pool, 0, sizeof(const char *)); + + 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, revprops, ctx, + pool)); } else if (opt_state->show_revs == svn_cl__show_revs_eligible) { - SVN_ERR(svn_client_mergeinfo_log(FALSE, target, &tgt_peg_revision, - source, &src_peg_revision, - print_log_rev, NULL, - TRUE, depth, NULL, ctx, - pool)); + apr_array_header_t *revprops; + + /* We need only revisions number, not revision properties. */ + revprops = apr_array_make(pool, 0, sizeof(const char *)); + + 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, revprops, 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; } |