2 * mergeinfo-cmd.c -- Query merge-relative info.
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
21 * ====================================================================
24 /* ==================================================================== */
30 #include "svn_pools.h"
31 #include "svn_client.h"
32 #include "svn_cmdline.h"
34 #include "svn_error.h"
35 #include "svn_error_codes.h"
36 #include "svn_types.h"
39 #include "svn_private_config.h"
44 /* Implements the svn_log_entry_receiver_t interface. */
46 print_log_rev(void *baton,
47 svn_log_entry_t *log_entry,
50 if (log_entry->non_inheritable)
51 SVN_ERR(svn_cmdline_printf(pool, "r%ld*\n", log_entry->revision));
53 SVN_ERR(svn_cmdline_printf(pool, "r%ld\n", log_entry->revision));
58 /* Draw a diagram (by printing text to the console) summarizing the state
59 * of merging between two branches, given the merge description
60 * indicated by YCA, BASE, RIGHT, TARGET, REINTEGRATE_LIKE. */
62 mergeinfo_diagram(const char *yca_url,
64 const char *right_url,
65 const char *target_url,
67 svn_revnum_t base_rev,
68 svn_revnum_t right_rev,
69 svn_revnum_t target_rev,
70 const char *repos_root_url,
71 svn_boolean_t target_is_wc,
72 svn_boolean_t reintegrate_like,
75 /* The graph occupies 4 rows of text, and the annotations occupy
76 * another 2 rows above and 2 rows below. The graph is constructed
77 * from left to right in discrete sections ("columns"), each of which
78 * can have a different width (measured in characters). Each element in
79 * the array is either a text string of the appropriate width, or can
80 * be NULL to draw a blank cell. */
83 const char *g[ROWS][COLS] = {{0}};
87 /* The YCA (that is, the branching point). And an ellipsis, because we
88 * don't show information about earlier merges */
89 g[0][0] = apr_psprintf(pool, " %-8ld ", yca_rev);
91 if (strcmp(yca_url, right_url) == 0)
93 g[2][0] = "-------| |--";
98 else if (strcmp(yca_url, target_url) == 0)
100 g[2][0] = " --| |--";
103 g[5][0] = "-------| |--";
107 g[2][0] = " --| |--";
110 g[5][0] = " --| |--";
113 /* The last full merge */
114 if ((base_rev > yca_rev) && reintegrate_like)
116 g[2][2] = "---------";
119 g[5][2] = "---------";
121 g[7][2] = apr_psprintf(pool, "%-8ld ", base_rev);
123 else if (base_rev > yca_rev)
125 g[0][2] = apr_psprintf(pool, "%-8ld ", base_rev);
127 g[2][2] = "---------";
130 g[5][2] = "---------";
134 g[2][2] = "---------";
137 g[5][2] = "---------";
140 /* The tips of the branches */
142 g[0][3] = apr_psprintf(pool, "%-8ld", right_rev);
149 g[7][3] = target_is_wc ? "WC "
150 : apr_psprintf(pool, "%-8ld", target_rev);
153 /* Find the width of each column, so we know how to print blank cells */
154 for (col = 0; col < COLS; col++)
157 for (row = 0; row < ROWS; row++)
159 if (g[row][col] && ((int)strlen(g[row][col]) > col_width[col]))
160 col_width[col] = (int)strlen(g[row][col]);
164 /* Column headings */
165 SVN_ERR(svn_cmdline_printf(pool,
171 _("youngest common ancestor"), _("last full merge"),
172 _("tip of branch"), _("repository path")));
174 /* Print the diagram, row by row */
175 for (row = 0; row < ROWS; row++)
177 SVN_ERR(svn_cmdline_fputs(" ", stdout, pool));
178 for (col = 0; col < COLS; col++)
182 SVN_ERR(svn_cmdline_fputs(g[row][col], stdout, pool));
186 /* Print <column-width> spaces */
187 SVN_ERR(svn_cmdline_printf(pool, "%*s", col_width[col], ""));
191 SVN_ERR(svn_cmdline_printf(pool, " %s",
192 svn_uri_skip_ancestor(repos_root_url, right_url, pool)));
194 SVN_ERR(svn_cmdline_printf(pool, " %s",
195 svn_uri_skip_ancestor(repos_root_url, target_url, pool)));
196 SVN_ERR(svn_cmdline_fputs("\n", stdout, pool));
202 /* Display a summary of the state of merging between the two branches
203 * SOURCE_PATH_OR_URL@SOURCE_REVISION and
204 * TARGET_PATH_OR_URL@TARGET_REVISION. */
207 const char *source_path_or_url,
208 const svn_opt_revision_t *source_revision,
209 const char *target_path_or_url,
210 const svn_opt_revision_t *target_revision,
211 svn_client_ctx_t *ctx,
214 const char *yca_url, *base_url, *right_url, *target_url;
215 svn_revnum_t yca_rev, base_rev, right_rev, target_rev;
216 const char *repos_root_url;
217 svn_boolean_t target_is_wc, is_reintegration;
219 target_is_wc = (! svn_path_is_url(target_path_or_url))
220 && (target_revision->kind == svn_opt_revision_unspecified
221 || target_revision->kind == svn_opt_revision_working);
222 SVN_ERR(svn_client_get_merging_summary(
225 &base_url, &base_rev,
226 &right_url, &right_rev,
227 &target_url, &target_rev,
229 source_path_or_url, source_revision,
230 target_path_or_url, target_revision,
233 SVN_ERR(mergeinfo_diagram(yca_url, base_url, right_url, target_url,
234 yca_rev, base_rev, right_rev, target_rev,
235 repos_root_url, target_is_wc, is_reintegration,
241 /* This implements the `svn_opt_subcommand_t' interface. */
243 svn_cl__mergeinfo(apr_getopt_t *os,
247 svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
248 svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
249 apr_array_header_t *targets;
250 const char *source, *target;
251 svn_opt_revision_t src_peg_revision, tgt_peg_revision;
252 svn_opt_revision_t *src_start_revision, *src_end_revision;
253 /* Default to depth empty. */
254 svn_depth_t depth = (opt_state->depth == svn_depth_unknown)
255 ? svn_depth_empty : opt_state->depth;
257 SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
261 /* Parse the arguments: SOURCE[@REV] optionally followed by TARGET[@REV]. */
262 if (targets->nelts < 1)
263 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
264 _("Not enough arguments given"));
265 if (targets->nelts > 2)
266 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
267 _("Too many arguments given"));
268 SVN_ERR(svn_opt_parse_path(&src_peg_revision, &source,
269 APR_ARRAY_IDX(targets, 0, const char *), pool));
270 if (targets->nelts == 2)
272 SVN_ERR(svn_opt_parse_path(&tgt_peg_revision, &target,
273 APR_ARRAY_IDX(targets, 1, const char *),
279 tgt_peg_revision.kind = svn_opt_revision_unspecified;
282 /* If no peg-rev was attached to the source URL, assume HEAD. */
283 /* ### But what if SOURCE is a WC path not a URL -- shouldn't we then use
284 * BASE (but not WORKING: that would be inconsistent with 'svn merge')? */
285 if (src_peg_revision.kind == svn_opt_revision_unspecified)
286 src_peg_revision.kind = svn_opt_revision_head;
288 /* If no peg-rev was attached to a URL target, then assume HEAD; if
289 no peg-rev was attached to a non-URL target, then assume BASE. */
290 /* ### But we would like to be able to examine a working copy with an
291 uncommitted merge in it, so change this to use WORKING not BASE? */
292 if (tgt_peg_revision.kind == svn_opt_revision_unspecified)
294 if (svn_path_is_url(target))
295 tgt_peg_revision.kind = svn_opt_revision_head;
297 tgt_peg_revision.kind = svn_opt_revision_base;
300 src_start_revision = &(opt_state->start_revision);
301 if (opt_state->end_revision.kind == svn_opt_revision_unspecified)
302 src_end_revision = src_start_revision;
304 src_end_revision = &(opt_state->end_revision);
306 /* Do the real work, depending on the requested data flavor. */
307 if (opt_state->show_revs == svn_cl__show_revs_merged)
309 apr_array_header_t *revprops;
311 /* We need only revisions number, not revision properties. */
312 revprops = apr_array_make(pool, 0, sizeof(const char *));
314 SVN_ERR(svn_client_mergeinfo_log2(TRUE, target, &tgt_peg_revision,
315 source, &src_peg_revision,
319 TRUE, depth, revprops, ctx,
322 else if (opt_state->show_revs == svn_cl__show_revs_eligible)
324 apr_array_header_t *revprops;
326 /* We need only revisions number, not revision properties. */
327 revprops = apr_array_make(pool, 0, sizeof(const char *));
329 SVN_ERR(svn_client_mergeinfo_log2(FALSE, target, &tgt_peg_revision,
330 source, &src_peg_revision,
334 TRUE, depth, revprops, ctx,
339 if ((opt_state->start_revision.kind != svn_opt_revision_unspecified)
340 || (opt_state->end_revision.kind != svn_opt_revision_unspecified))
341 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
342 _("--revision (-r) option valid only with "
343 "--show-revs option"));
344 if (opt_state->depth != svn_depth_unknown)
345 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
346 _("Depth specification options valid only "
347 "with --show-revs option"));
349 SVN_ERR(mergeinfo_summary(source, &src_peg_revision,
350 target, &tgt_peg_revision,