1251881Speter/* 2251881Speter * mergeinfo-cmd.c -- Query merge-relative info. 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter/* ==================================================================== */ 25251881Speter 26251881Speter 27251881Speter 28251881Speter/*** Includes. ***/ 29251881Speter 30251881Speter#include "svn_pools.h" 31251881Speter#include "svn_client.h" 32251881Speter#include "svn_cmdline.h" 33251881Speter#include "svn_path.h" 34251881Speter#include "svn_error.h" 35251881Speter#include "svn_error_codes.h" 36251881Speter#include "svn_types.h" 37251881Speter#include "cl.h" 38251881Speter 39251881Speter#include "svn_private_config.h" 40251881Speter 41251881Speter 42251881Speter/*** Code. ***/ 43251881Speter 44251881Speter/* Implements the svn_log_entry_receiver_t interface. */ 45251881Speterstatic svn_error_t * 46251881Speterprint_log_rev(void *baton, 47251881Speter svn_log_entry_t *log_entry, 48251881Speter apr_pool_t *pool) 49251881Speter{ 50251881Speter if (log_entry->non_inheritable) 51251881Speter SVN_ERR(svn_cmdline_printf(pool, "r%ld*\n", log_entry->revision)); 52251881Speter else 53251881Speter SVN_ERR(svn_cmdline_printf(pool, "r%ld\n", log_entry->revision)); 54251881Speter 55251881Speter return SVN_NO_ERROR; 56251881Speter} 57251881Speter 58251881Speter/* Draw a diagram (by printing text to the console) summarizing the state 59251881Speter * of merging between two branches, given the merge description 60251881Speter * indicated by YCA, BASE, RIGHT, TARGET, REINTEGRATE_LIKE. */ 61251881Speterstatic svn_error_t * 62251881Spetermergeinfo_diagram(const char *yca_url, 63251881Speter const char *base_url, 64251881Speter const char *right_url, 65251881Speter const char *target_url, 66251881Speter svn_revnum_t yca_rev, 67251881Speter svn_revnum_t base_rev, 68251881Speter svn_revnum_t right_rev, 69251881Speter svn_revnum_t target_rev, 70251881Speter const char *repos_root_url, 71251881Speter svn_boolean_t target_is_wc, 72251881Speter svn_boolean_t reintegrate_like, 73251881Speter apr_pool_t *pool) 74251881Speter{ 75251881Speter /* The graph occupies 4 rows of text, and the annotations occupy 76251881Speter * another 2 rows above and 2 rows below. The graph is constructed 77251881Speter * from left to right in discrete sections ("columns"), each of which 78251881Speter * can have a different width (measured in characters). Each element in 79251881Speter * the array is either a text string of the appropriate width, or can 80251881Speter * be NULL to draw a blank cell. */ 81251881Speter#define ROWS 8 82251881Speter#define COLS 4 83251881Speter const char *g[ROWS][COLS] = {{0}}; 84251881Speter int col_width[COLS]; 85251881Speter int row, col; 86251881Speter 87251881Speter /* The YCA (that is, the branching point). And an ellipsis, because we 88251881Speter * don't show information about earlier merges */ 89251881Speter g[0][0] = apr_psprintf(pool, " %-8ld ", yca_rev); 90251881Speter g[1][0] = " | "; 91251881Speter if (strcmp(yca_url, right_url) == 0) 92251881Speter { 93251881Speter g[2][0] = "-------| |--"; 94251881Speter g[3][0] = " \\ "; 95251881Speter g[4][0] = " \\ "; 96251881Speter g[5][0] = " --| |--"; 97251881Speter } 98251881Speter else if (strcmp(yca_url, target_url) == 0) 99251881Speter { 100251881Speter g[2][0] = " --| |--"; 101251881Speter g[3][0] = " / "; 102251881Speter g[4][0] = " / "; 103251881Speter g[5][0] = "-------| |--"; 104251881Speter } 105251881Speter else 106251881Speter { 107251881Speter g[2][0] = " --| |--"; 108251881Speter g[3][0] = "... / "; 109251881Speter g[4][0] = " \\ "; 110251881Speter g[5][0] = " --| |--"; 111251881Speter } 112251881Speter 113251881Speter /* The last full merge */ 114251881Speter if ((base_rev > yca_rev) && reintegrate_like) 115251881Speter { 116251881Speter g[2][2] = "---------"; 117251881Speter g[3][2] = " / "; 118251881Speter g[4][2] = " / "; 119251881Speter g[5][2] = "---------"; 120251881Speter g[6][2] = "| "; 121251881Speter g[7][2] = apr_psprintf(pool, "%-8ld ", base_rev); 122251881Speter } 123251881Speter else if (base_rev > yca_rev) 124251881Speter { 125251881Speter g[0][2] = apr_psprintf(pool, "%-8ld ", base_rev); 126251881Speter g[1][2] = "| "; 127251881Speter g[2][2] = "---------"; 128251881Speter g[3][2] = " \\ "; 129251881Speter g[4][2] = " \\ "; 130251881Speter g[5][2] = "---------"; 131251881Speter } 132251881Speter else 133251881Speter { 134251881Speter g[2][2] = "---------"; 135251881Speter g[3][2] = " "; 136251881Speter g[4][2] = " "; 137251881Speter g[5][2] = "---------"; 138251881Speter } 139251881Speter 140251881Speter /* The tips of the branches */ 141251881Speter { 142251881Speter g[0][3] = apr_psprintf(pool, "%-8ld", right_rev); 143251881Speter g[1][3] = "| "; 144251881Speter g[2][3] = "- "; 145251881Speter g[3][3] = " "; 146251881Speter g[4][3] = " "; 147251881Speter g[5][3] = "- "; 148251881Speter g[6][3] = "| "; 149251881Speter g[7][3] = target_is_wc ? "WC " 150251881Speter : apr_psprintf(pool, "%-8ld", target_rev); 151251881Speter } 152251881Speter 153251881Speter /* Find the width of each column, so we know how to print blank cells */ 154251881Speter for (col = 0; col < COLS; col++) 155251881Speter { 156251881Speter col_width[col] = 0; 157251881Speter for (row = 0; row < ROWS; row++) 158251881Speter { 159251881Speter if (g[row][col] && ((int)strlen(g[row][col]) > col_width[col])) 160251881Speter col_width[col] = (int)strlen(g[row][col]); 161251881Speter } 162251881Speter } 163251881Speter 164251881Speter /* Column headings */ 165251881Speter SVN_ERR(svn_cmdline_printf(pool, 166251881Speter " %s\n" 167251881Speter " | %s\n" 168251881Speter " | | %s\n" 169251881Speter " | | | %s\n" 170251881Speter "\n", 171251881Speter _("youngest common ancestor"), _("last full merge"), 172251881Speter _("tip of branch"), _("repository path"))); 173251881Speter 174251881Speter /* Print the diagram, row by row */ 175251881Speter for (row = 0; row < ROWS; row++) 176251881Speter { 177251881Speter SVN_ERR(svn_cmdline_fputs(" ", stdout, pool)); 178251881Speter for (col = 0; col < COLS; col++) 179251881Speter { 180251881Speter if (g[row][col]) 181251881Speter { 182251881Speter SVN_ERR(svn_cmdline_fputs(g[row][col], stdout, pool)); 183251881Speter } 184251881Speter else 185251881Speter { 186251881Speter /* Print <column-width> spaces */ 187251881Speter SVN_ERR(svn_cmdline_printf(pool, "%*s", col_width[col], "")); 188251881Speter } 189251881Speter } 190251881Speter if (row == 2) 191251881Speter SVN_ERR(svn_cmdline_printf(pool, " %s", 192251881Speter svn_uri_skip_ancestor(repos_root_url, right_url, pool))); 193251881Speter if (row == 5) 194251881Speter SVN_ERR(svn_cmdline_printf(pool, " %s", 195251881Speter svn_uri_skip_ancestor(repos_root_url, target_url, pool))); 196251881Speter SVN_ERR(svn_cmdline_fputs("\n", stdout, pool)); 197251881Speter } 198251881Speter 199251881Speter return SVN_NO_ERROR; 200251881Speter} 201251881Speter 202251881Speter/* Display a summary of the state of merging between the two branches 203251881Speter * SOURCE_PATH_OR_URL@SOURCE_REVISION and 204251881Speter * TARGET_PATH_OR_URL@TARGET_REVISION. */ 205251881Speterstatic svn_error_t * 206251881Spetermergeinfo_summary( 207251881Speter const char *source_path_or_url, 208251881Speter const svn_opt_revision_t *source_revision, 209251881Speter const char *target_path_or_url, 210251881Speter const svn_opt_revision_t *target_revision, 211251881Speter svn_client_ctx_t *ctx, 212251881Speter apr_pool_t *pool) 213251881Speter{ 214251881Speter const char *yca_url, *base_url, *right_url, *target_url; 215251881Speter svn_revnum_t yca_rev, base_rev, right_rev, target_rev; 216251881Speter const char *repos_root_url; 217251881Speter svn_boolean_t target_is_wc, is_reintegration; 218251881Speter 219251881Speter target_is_wc = (! svn_path_is_url(target_path_or_url)) 220251881Speter && (target_revision->kind == svn_opt_revision_unspecified 221251881Speter || target_revision->kind == svn_opt_revision_working); 222251881Speter SVN_ERR(svn_client_get_merging_summary( 223251881Speter &is_reintegration, 224251881Speter &yca_url, &yca_rev, 225251881Speter &base_url, &base_rev, 226251881Speter &right_url, &right_rev, 227251881Speter &target_url, &target_rev, 228251881Speter &repos_root_url, 229251881Speter source_path_or_url, source_revision, 230251881Speter target_path_or_url, target_revision, 231251881Speter ctx, pool, pool)); 232251881Speter 233251881Speter SVN_ERR(mergeinfo_diagram(yca_url, base_url, right_url, target_url, 234251881Speter yca_rev, base_rev, right_rev, target_rev, 235251881Speter repos_root_url, target_is_wc, is_reintegration, 236251881Speter pool)); 237251881Speter 238251881Speter return SVN_NO_ERROR; 239251881Speter} 240251881Speter 241251881Speter/* This implements the `svn_opt_subcommand_t' interface. */ 242251881Spetersvn_error_t * 243251881Spetersvn_cl__mergeinfo(apr_getopt_t *os, 244251881Speter void *baton, 245251881Speter apr_pool_t *pool) 246251881Speter{ 247251881Speter svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state; 248251881Speter svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx; 249251881Speter apr_array_header_t *targets; 250251881Speter const char *source, *target; 251251881Speter svn_opt_revision_t src_peg_revision, tgt_peg_revision; 252251881Speter svn_opt_revision_t *src_start_revision, *src_end_revision; 253251881Speter /* Default to depth empty. */ 254251881Speter svn_depth_t depth = (opt_state->depth == svn_depth_unknown) 255251881Speter ? svn_depth_empty : opt_state->depth; 256251881Speter 257251881Speter SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os, 258251881Speter opt_state->targets, 259251881Speter ctx, FALSE, pool)); 260251881Speter 261251881Speter /* Parse the arguments: SOURCE[@REV] optionally followed by TARGET[@REV]. */ 262251881Speter if (targets->nelts < 1) 263251881Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 264251881Speter _("Not enough arguments given")); 265251881Speter if (targets->nelts > 2) 266251881Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 267251881Speter _("Too many arguments given")); 268251881Speter SVN_ERR(svn_opt_parse_path(&src_peg_revision, &source, 269251881Speter APR_ARRAY_IDX(targets, 0, const char *), pool)); 270251881Speter if (targets->nelts == 2) 271251881Speter { 272251881Speter SVN_ERR(svn_opt_parse_path(&tgt_peg_revision, &target, 273251881Speter APR_ARRAY_IDX(targets, 1, const char *), 274251881Speter pool)); 275251881Speter } 276251881Speter else 277251881Speter { 278251881Speter target = ""; 279251881Speter tgt_peg_revision.kind = svn_opt_revision_unspecified; 280251881Speter } 281251881Speter 282251881Speter /* If no peg-rev was attached to the source URL, assume HEAD. */ 283251881Speter /* ### But what if SOURCE is a WC path not a URL -- shouldn't we then use 284251881Speter * BASE (but not WORKING: that would be inconsistent with 'svn merge')? */ 285251881Speter if (src_peg_revision.kind == svn_opt_revision_unspecified) 286251881Speter src_peg_revision.kind = svn_opt_revision_head; 287251881Speter 288251881Speter /* If no peg-rev was attached to a URL target, then assume HEAD; if 289251881Speter no peg-rev was attached to a non-URL target, then assume BASE. */ 290251881Speter /* ### But we would like to be able to examine a working copy with an 291251881Speter uncommitted merge in it, so change this to use WORKING not BASE? */ 292251881Speter if (tgt_peg_revision.kind == svn_opt_revision_unspecified) 293251881Speter { 294251881Speter if (svn_path_is_url(target)) 295251881Speter tgt_peg_revision.kind = svn_opt_revision_head; 296251881Speter else 297251881Speter tgt_peg_revision.kind = svn_opt_revision_base; 298251881Speter } 299251881Speter 300251881Speter src_start_revision = &(opt_state->start_revision); 301251881Speter if (opt_state->end_revision.kind == svn_opt_revision_unspecified) 302251881Speter src_end_revision = src_start_revision; 303251881Speter else 304251881Speter src_end_revision = &(opt_state->end_revision); 305251881Speter 306251881Speter /* Do the real work, depending on the requested data flavor. */ 307251881Speter if (opt_state->show_revs == svn_cl__show_revs_merged) 308251881Speter { 309253734Speter apr_array_header_t *revprops; 310253734Speter 311253734Speter /* We need only revisions number, not revision properties. */ 312253734Speter revprops = apr_array_make(pool, 0, sizeof(const char *)); 313253734Speter 314251881Speter SVN_ERR(svn_client_mergeinfo_log2(TRUE, target, &tgt_peg_revision, 315251881Speter source, &src_peg_revision, 316251881Speter src_start_revision, 317251881Speter src_end_revision, 318251881Speter print_log_rev, NULL, 319253734Speter TRUE, depth, revprops, ctx, 320251881Speter pool)); 321251881Speter } 322251881Speter else if (opt_state->show_revs == svn_cl__show_revs_eligible) 323251881Speter { 324253734Speter apr_array_header_t *revprops; 325253734Speter 326253734Speter /* We need only revisions number, not revision properties. */ 327253734Speter revprops = apr_array_make(pool, 0, sizeof(const char *)); 328253734Speter 329251881Speter SVN_ERR(svn_client_mergeinfo_log2(FALSE, target, &tgt_peg_revision, 330251881Speter source, &src_peg_revision, 331251881Speter src_start_revision, 332251881Speter src_end_revision, 333251881Speter print_log_rev, NULL, 334253734Speter TRUE, depth, revprops, ctx, 335251881Speter pool)); 336251881Speter } 337251881Speter else 338251881Speter { 339251881Speter if ((opt_state->start_revision.kind != svn_opt_revision_unspecified) 340251881Speter || (opt_state->end_revision.kind != svn_opt_revision_unspecified)) 341251881Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 342251881Speter _("--revision (-r) option valid only with " 343251881Speter "--show-revs option")); 344251881Speter if (opt_state->depth != svn_depth_unknown) 345251881Speter return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL, 346251881Speter _("Depth specification options valid only " 347251881Speter "with --show-revs option")); 348251881Speter 349251881Speter SVN_ERR(mergeinfo_summary(source, &src_peg_revision, 350251881Speter target, &tgt_peg_revision, 351251881Speter ctx, pool)); 352251881Speter } 353251881Speter return SVN_NO_ERROR; 354251881Speter} 355