You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by ju...@apache.org on 2012/09/21 22:42:09 UTC
svn commit: r1388673 - in /subversion/trunk/subversion:
include/private/svn_client_private.h libsvn_client/merge.c svn/main.c
svn/mergeinfo-cmd.c
Author: julianfoad
Date: Fri Sep 21 20:42:09 2012
New Revision: 1388673
URL: http://svn.apache.org/viewvc?rev=1388673&view=rev
Log:
Teach 'svn mergeinfo' to print an ASCII-art diagram summarizing the state of
merging between the two branches.
This mode will now be used by default, and the --show-revs option can still
be used to get a plain list of revision numbers. This is a start, and
concentrates on "full" merges; it is not yet very useful where
cherry-picking is the main form of merge, such as on a release branch.
* subversion/include/private/svn_client_private.h,
subversion/libsvn_client/merge.c
(svn_client__symmetric_merge_t): Add the 'target' location.
(svn_client__find_symmetric_merge_no_wc,
svn_client__symmetric_merge_get_locations): New functions.
(find_symmetric_merge): Accept a target branch that is not a WC.
* subversion/svn/main.c
(svn_cl__cmd_table): Update the help for 'svn mergeinfo'.
(sub_main): Change the default value of the show-revs option to 'invalid'
so we can distinguish when the option has not been given.
* subversion/svn/mergeinfo-cmd.c
(mergeinfo_diagram, mergeinfo_summary): New functions.
(svn_cl__mergeinfo): Call mergeinfo_summary() if no show-revs option was
given.
Modified:
subversion/trunk/subversion/include/private/svn_client_private.h
subversion/trunk/subversion/libsvn_client/merge.c
subversion/trunk/subversion/svn/main.c
subversion/trunk/subversion/svn/mergeinfo-cmd.c
Modified: subversion/trunk/subversion/include/private/svn_client_private.h
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/include/private/svn_client_private.h?rev=1388673&r1=1388672&r2=1388673&view=diff
==============================================================================
--- subversion/trunk/subversion/include/private/svn_client_private.h (original)
+++ subversion/trunk/subversion/include/private/svn_client_private.h Fri Sep 21 20:42:09 2012
@@ -202,6 +202,45 @@ svn_client__find_symmetric_merge(svn_cli
apr_pool_t *result_pool,
apr_pool_t *scratch_pool);
+/* Find out what kind of symmetric merge would be needed, when the target
+ * is only known as a repository location rather than a WC.
+ *
+ * Like svn_client__find_symmetric_merge() except that SOURCE_PATH_OR_URL @
+ * SOURCE_REVISION should refer to a repository location and not a WC.
+ *
+ * ### The result, *MERGE_P, may not be suitable for passing to
+ * svn_client__do_symmetric_merge(). The target WC state would not be
+ * checked (as in the ALLOW_* flags). We should resolve this problem:
+ * perhaps add the allow_* params here, or provide another way of setting
+ * them; and perhaps ensure __do_...() will accept the result iff given a
+ * WC that matches the stored target location.
+ */
+svn_error_t *
+svn_client__find_symmetric_merge_no_wc(
+ svn_client__symmetric_merge_t **merge_p,
+ 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 *result_pool,
+ apr_pool_t *scratch_pool);
+
+/* Set *YCA, *BASE, *RIGHT, *TARGET to the repository locations of the
+ * youngest common ancestor of the branches, the base chosen for 3-way
+ * merge, the right-hand side of the source diff, and the target WC.
+ *
+ * Any of the output pointers may be NULL if not wanted.
+ */
+svn_error_t *
+svn_client__symmetric_merge_get_locations(
+ svn_client__pathrev_t **yca,
+ svn_client__pathrev_t **base,
+ svn_client__pathrev_t **right,
+ svn_client__pathrev_t **target,
+ const svn_client__symmetric_merge_t *merge,
+ apr_pool_t *result_pool);
+
/* Perform a symmetric merge.
*
* Merge according to MERGE into the WC at TARGET_WCPATH.
Modified: subversion/trunk/subversion/libsvn_client/merge.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_client/merge.c?rev=1388673&r1=1388672&r2=1388673&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_client/merge.c (original)
+++ subversion/trunk/subversion/libsvn_client/merge.c Fri Sep 21 20:42:09 2012
@@ -11443,14 +11443,23 @@ find_symmetric_merge(svn_client__pathrev
svn_mergeinfo_inherited,
FALSE /* squelch_incapable */,
scratch_pool));
- SVN_ERR(svn_client__get_wc_or_repos_mergeinfo(&s_t->target_mergeinfo,
- NULL /* inherited */,
- NULL /* from_repos */,
- FALSE /* repos_only */,
- svn_mergeinfo_inherited,
- s_t->target_ra_session,
- s_t->target->abspath,
- ctx, scratch_pool));
+ if (! s_t->target->abspath)
+ SVN_ERR(svn_client__get_repos_mergeinfo(&s_t->target_mergeinfo,
+ s_t->target_ra_session,
+ s_t->target->loc.url,
+ s_t->target->loc.rev,
+ svn_mergeinfo_inherited,
+ FALSE /* squelch_incapable */,
+ scratch_pool));
+ else
+ SVN_ERR(svn_client__get_wc_or_repos_mergeinfo(&s_t->target_mergeinfo,
+ NULL /* inherited */,
+ NULL /* from_repos */,
+ FALSE /* repos_only */,
+ svn_mergeinfo_inherited,
+ s_t->target_ra_session,
+ s_t->target->abspath,
+ ctx, scratch_pool));
/* Get the location-history of each branch. */
s_t->source_branch.tip = s_t->source;
@@ -11497,11 +11506,53 @@ find_symmetric_merge(svn_client__pathrev
/* Details of a symmetric merge. */
struct svn_client__symmetric_merge_t
{
- svn_client__pathrev_t *yca, *base, *mid, *right;
+ svn_client__pathrev_t *yca, *base, *mid, *right, *target;
svn_boolean_t allow_mixed_rev, allow_local_mods, allow_switched_subtrees;
};
svn_error_t *
+svn_client__find_symmetric_merge_no_wc(
+ svn_client__symmetric_merge_t **merge_p,
+ 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 *result_pool,
+ apr_pool_t *scratch_pool)
+{
+ source_and_target_t *s_t = apr_palloc(scratch_pool, sizeof(*s_t));
+ svn_client__pathrev_t *target_loc;
+ svn_client__symmetric_merge_t *merge = apr_palloc(result_pool, sizeof(*merge));
+
+ /* Source */
+ SVN_ERR(svn_client__ra_session_from_path2(
+ &s_t->source_ra_session, &s_t->source,
+ source_path_or_url, NULL, source_revision, source_revision,
+ ctx, result_pool));
+
+ /* Target */
+ SVN_ERR(svn_client__ra_session_from_path2(
+ &s_t->target_ra_session, &target_loc,
+ target_path_or_url, NULL, target_revision, target_revision,
+ ctx, result_pool));
+ s_t->target = apr_palloc(scratch_pool, sizeof(*s_t->target));
+ s_t->target->kind = svn_node_none;
+ s_t->target->abspath = NULL; /* indicate the target is not a WC */
+ s_t->target->loc = *target_loc;
+
+ SVN_ERR(find_symmetric_merge(&merge->base, &merge->mid, s_t,
+ ctx, result_pool, scratch_pool));
+
+ merge->right = s_t->source;
+ merge->target = &s_t->target->loc;
+ merge->yca = s_t->yca;
+ *merge_p = merge;
+
+ return SVN_NO_ERROR;
+}
+
+svn_error_t *
svn_client__find_symmetric_merge(svn_client__symmetric_merge_t **merge_p,
const char *source_path_or_url,
const svn_opt_revision_t *source_revision,
@@ -11742,4 +11793,24 @@ svn_client__symmetric_merge_is_reintegra
const svn_client__symmetric_merge_t *merge)
{
return merge->mid != NULL;
+}
+
+svn_error_t *
+svn_client__symmetric_merge_get_locations(
+ svn_client__pathrev_t **yca,
+ svn_client__pathrev_t **base,
+ svn_client__pathrev_t **right,
+ svn_client__pathrev_t **target,
+ const svn_client__symmetric_merge_t *merge,
+ apr_pool_t *result_pool)
+{
+ if (yca)
+ *yca = svn_client__pathrev_dup(merge->yca, result_pool);
+ if (base)
+ *base = svn_client__pathrev_dup(merge->base, result_pool);
+ if (right)
+ *right = svn_client__pathrev_dup(merge->right, result_pool);
+ if (target)
+ *target = svn_client__pathrev_dup(merge->target, result_pool);
+ return SVN_NO_ERROR;
}
\ No newline at end of file
Modified: subversion/trunk/subversion/svn/main.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/main.c?rev=1388673&r1=1388672&r2=1388673&view=diff
==============================================================================
--- subversion/trunk/subversion/svn/main.c (original)
+++ subversion/trunk/subversion/svn/main.c Fri Sep 21 20:42:09 2012
@@ -1036,13 +1036,19 @@ const svn_opt_subcommand_desc2_t svn_cl_
{ "mergeinfo", svn_cl__mergeinfo, {0}, N_
("Display merge-related information.\n"
- "usage: mergeinfo SOURCE[@REV] [TARGET[@REV]]\n"
- "\n"
- " Display information related to merges (or potential merges) between\n"
- " SOURCE and TARGET (default: '.'). Display the type of information\n"
- " specified by the --show-revs option. If --show-revs isn't passed,\n"
- " it defaults to --show-revs='merged'.\n"
+ "usage: 1. mergeinfo SOURCE[@REV] [TARGET[@REV]]\n"
+ " 2. mergeinfo --show-revs=merged SOURCE[@REV] [TARGET[@REV]]\n"
+ " 3. mergeinfo --show-revs=eligible SOURCE[@REV] [TARGET[@REV]]\n"
+ "\n"
+ " 1. Display the following information about merges between SOURCE and\n"
+ " TARGET:\n"
+ " the youngest common ancestor;\n"
+ " the latest full merge in either direction, and thus the\n"
+ " base that will be used for the next full merge.\n"
+ " 2. Print the revision numbers on SOURCE that have been merged to TARGET.\n"
+ " 3. Print the revision numbers on SOURCE that have NOT been merged to TARGET.\n"
"\n"
+ " The default TARGET is the current working directory ('.').\n"
" If --revision (-r) is provided, filter the displayed information to\n"
" show only that which is associated with the revisions within the\n"
" specified range. Revision numbers, dates, and the 'HEAD' keyword are\n"
@@ -1670,7 +1676,7 @@ sub_main(int argc, const char *argv[], a
opt_state.depth = svn_depth_unknown;
opt_state.set_depth = svn_depth_unknown;
opt_state.accept_which = svn_cl__accept_unspecified;
- opt_state.show_revs = svn_cl__show_revs_merged;
+ opt_state.show_revs = svn_cl__show_revs_invalid;
/* No args? Show usage. */
if (argc <= 1)
Modified: subversion/trunk/subversion/svn/mergeinfo-cmd.c
URL: http://svn.apache.org/viewvc/subversion/trunk/subversion/svn/mergeinfo-cmd.c?rev=1388673&r1=1388672&r2=1388673&view=diff
==============================================================================
--- subversion/trunk/subversion/svn/mergeinfo-cmd.c (original)
+++ subversion/trunk/subversion/svn/mergeinfo-cmd.c Fri Sep 21 20:42:09 2012
@@ -37,6 +37,7 @@
#include "cl.h"
#include "svn_private_config.h"
+#include "private/svn_client_private.h"
/*** Code. ***/
@@ -55,6 +56,194 @@ 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(svn_client__pathrev_t *yca,
+ svn_client__pathrev_t *base,
+ svn_client__pathrev_t *right,
+ svn_client__pathrev_t *target,
+ 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) */
+ 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] = " -----";
+ }
+
+ /* An ellipsis, because we don't show information about earlier merges */
+ {
+ g[2][1] = "| ... |---";
+ g[3][1] = " ";
+ g[4][1] = " ";
+ g[5][1] = "| ... |---";
+ }
+
+ /* 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 ? apr_psprintf(pool, "%-8ld", target->rev)
+ : "WC ";
+ }
+
+ /* 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] && (strlen(g[row][col]) > col_width[col]))
+ col_width[col] = strlen(g[row][col]);
+ }
+ }
+
+ /* Column headings */
+ SVN_ERR(svn_cmdline_fputs(
+ _(" youngest last repos.\n"
+ " common full tip of path of\n"
+ " ancestor merge branch branch\n"
+ "\n"),
+ stdout, pool));
+
+ /* 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_client__pathrev_relpath(right, pool)));
+ if (row == 5)
+ SVN_ERR(svn_cmdline_printf(pool, " %s",
+ svn_client__pathrev_relpath(target, 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)
+{
+ svn_client__symmetric_merge_t *the_merge;
+ svn_client__pathrev_t *yca, *base, *right, *target;
+ svn_boolean_t target_is_wc, reintegrate_like;
+
+ 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);
+ if (target_is_wc)
+ SVN_ERR(svn_client__find_symmetric_merge(
+ &the_merge,
+ source_path_or_url, source_revision,
+ target_path_or_url,
+ TRUE, TRUE, TRUE, /* allow_* */
+ ctx, pool, pool));
+ else
+ SVN_ERR(svn_client__find_symmetric_merge_no_wc(
+ &the_merge,
+ source_path_or_url, source_revision,
+ target_path_or_url, target_revision,
+ ctx, pool, pool));
+
+ SVN_ERR(svn_client__symmetric_merge_get_locations(
+ &yca, &base, &right, &target, the_merge, pool));
+ reintegrate_like = svn_client__symmetric_merge_is_reintegrate_like(the_merge);
+
+ SVN_ERR(mergeinfo_diagram(yca, base, right, target,
+ target_is_wc, reintegrate_like,
+ pool));
+
+ return SVN_NO_ERROR;
+}
+
/* This implements the `svn_opt_subcommand_t' interface. */
svn_error_t *
svn_cl__mergeinfo(apr_getopt_t *os,
@@ -140,5 +329,11 @@ svn_cl__mergeinfo(apr_getopt_t *os,
TRUE, depth, NULL, ctx,
pool));
}
+ else
+ {
+ SVN_ERR(mergeinfo_summary(source, &src_peg_revision,
+ target, &tgt_peg_revision,
+ ctx, pool));
+ }
return SVN_NO_ERROR;
}