]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/subversion/subversion/svn/mergeinfo-cmd.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / subversion / subversion / svn / mergeinfo-cmd.c
1 /*
2  * mergeinfo-cmd.c -- Query merge-relative info.
3  *
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
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
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
20  *    under the License.
21  * ====================================================================
22  */
23
24 /* ==================================================================== */
25
26
27 \f
28 /*** Includes. ***/
29
30 #include "svn_pools.h"
31 #include "svn_client.h"
32 #include "svn_cmdline.h"
33 #include "svn_path.h"
34 #include "svn_error.h"
35 #include "svn_error_codes.h"
36 #include "svn_types.h"
37 #include "cl.h"
38
39 #include "svn_private_config.h"
40
41 \f
42 /*** Code. ***/
43
44 /* Implements the svn_log_entry_receiver_t interface. */
45 static svn_error_t *
46 print_log_rev(void *baton,
47               svn_log_entry_t *log_entry,
48               apr_pool_t *pool)
49 {
50   if (log_entry->non_inheritable)
51     SVN_ERR(svn_cmdline_printf(pool, "r%ld*\n", log_entry->revision));
52   else
53     SVN_ERR(svn_cmdline_printf(pool, "r%ld\n", log_entry->revision));
54
55   return SVN_NO_ERROR;
56 }
57
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. */
61 static svn_error_t *
62 mergeinfo_diagram(const char *yca_url,
63                   const char *base_url,
64                   const char *right_url,
65                   const char *target_url,
66                   svn_revnum_t yca_rev,
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,
73                   apr_pool_t *pool)
74 {
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. */
81 #define ROWS 8
82 #define COLS 4
83   const char *g[ROWS][COLS] = {{0}};
84   int col_width[COLS];
85   int row, col;
86
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);
90   g[1][0] =     "  |         ";
91   if (strcmp(yca_url, right_url) == 0)
92     {
93       g[2][0] = "-------| |--";
94       g[3][0] = "   \\        ";
95       g[4][0] = "    \\       ";
96       g[5][0] = "     --| |--";
97     }
98   else if (strcmp(yca_url, target_url) == 0)
99     {
100       g[2][0] = "     --| |--";
101       g[3][0] = "    /       ";
102       g[4][0] = "   /        ";
103       g[5][0] = "-------| |--";
104     }
105   else
106     {
107       g[2][0] = "     --| |--";
108       g[3][0] = "... /       ";
109       g[4][0] = "    \\       ";
110       g[5][0] = "     --| |--";
111     }
112
113   /* The last full merge */
114   if ((base_rev > yca_rev) && reintegrate_like)
115     {
116       g[2][2] = "---------";
117       g[3][2] = "  /      ";
118       g[4][2] = " /       ";
119       g[5][2] = "---------";
120       g[6][2] = "|        ";
121       g[7][2] = apr_psprintf(pool, "%-8ld ", base_rev);
122     }
123   else if (base_rev > yca_rev)
124     {
125       g[0][2] = apr_psprintf(pool, "%-8ld ", base_rev);
126       g[1][2] = "|        ";
127       g[2][2] = "---------";
128       g[3][2] = " \\       ";
129       g[4][2] = "  \\      ";
130       g[5][2] = "---------";
131     }
132   else
133     {
134       g[2][2] = "---------";
135       g[3][2] = "         ";
136       g[4][2] = "         ";
137       g[5][2] = "---------";
138     }
139
140   /* The tips of the branches */
141     {
142       g[0][3] = apr_psprintf(pool, "%-8ld", right_rev);
143       g[1][3] = "|       ";
144       g[2][3] = "-       ";
145       g[3][3] = "        ";
146       g[4][3] = "        ";
147       g[5][3] = "-       ";
148       g[6][3] = "|       ";
149       g[7][3] = target_is_wc ? "WC      "
150                              : apr_psprintf(pool, "%-8ld", target_rev);
151     }
152
153   /* Find the width of each column, so we know how to print blank cells */
154   for (col = 0; col < COLS; col++)
155     {
156       col_width[col] = 0;
157       for (row = 0; row < ROWS; row++)
158         {
159           if (g[row][col] && ((int)strlen(g[row][col]) > col_width[col]))
160             col_width[col] = (int)strlen(g[row][col]);
161         }
162     }
163
164   /* Column headings */
165   SVN_ERR(svn_cmdline_printf(pool,
166             "    %s\n"
167             "    |         %s\n"
168             "    |         |        %s\n"
169             "    |         |        |         %s\n"
170             "\n",
171             _("youngest common ancestor"), _("last full merge"),
172             _("tip of branch"), _("repository path")));
173
174   /* Print the diagram, row by row */
175   for (row = 0; row < ROWS; row++)
176     {
177       SVN_ERR(svn_cmdline_fputs("  ", stdout, pool));
178       for (col = 0; col < COLS; col++)
179         {
180           if (g[row][col])
181             {
182               SVN_ERR(svn_cmdline_fputs(g[row][col], stdout, pool));
183             }
184           else
185             {
186               /* Print <column-width> spaces */
187               SVN_ERR(svn_cmdline_printf(pool, "%*s", col_width[col], ""));
188             }
189         }
190       if (row == 2)
191         SVN_ERR(svn_cmdline_printf(pool, "  %s",
192                 svn_uri_skip_ancestor(repos_root_url, right_url, pool)));
193       if (row == 5)
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));
197     }
198
199   return SVN_NO_ERROR;
200 }
201
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. */
205 static svn_error_t *
206 mergeinfo_summary(
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,
212                   apr_pool_t *pool)
213 {
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;
218
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(
223             &is_reintegration,
224             &yca_url, &yca_rev,
225             &base_url, &base_rev,
226             &right_url, &right_rev,
227             &target_url, &target_rev,
228             &repos_root_url,
229             source_path_or_url, source_revision,
230             target_path_or_url, target_revision,
231             ctx, pool, pool));
232
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,
236                             pool));
237
238   return SVN_NO_ERROR;
239 }
240
241 /* This implements the `svn_opt_subcommand_t' interface. */
242 svn_error_t *
243 svn_cl__mergeinfo(apr_getopt_t *os,
244                   void *baton,
245                   apr_pool_t *pool)
246 {
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;
256
257   SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
258                                                       opt_state->targets,
259                                                       ctx, FALSE, pool));
260
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)
271     {
272       SVN_ERR(svn_opt_parse_path(&tgt_peg_revision, &target,
273                                  APR_ARRAY_IDX(targets, 1, const char *),
274                                  pool));
275     }
276   else
277     {
278       target = "";
279       tgt_peg_revision.kind = svn_opt_revision_unspecified;
280     }
281
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;
287
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)
293     {
294       if (svn_path_is_url(target))
295         tgt_peg_revision.kind = svn_opt_revision_head;
296       else
297         tgt_peg_revision.kind = svn_opt_revision_base;
298     }
299
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;
303   else
304     src_end_revision = &(opt_state->end_revision);
305
306   /* Do the real work, depending on the requested data flavor. */
307   if (opt_state->show_revs == svn_cl__show_revs_merged)
308     {
309       apr_array_header_t *revprops;
310
311       /* We need only revisions number, not revision properties. */
312       revprops = apr_array_make(pool, 0, sizeof(const char *));
313
314       SVN_ERR(svn_client_mergeinfo_log2(TRUE, target, &tgt_peg_revision,
315                                         source, &src_peg_revision,
316                                         src_start_revision,
317                                         src_end_revision,
318                                         print_log_rev, NULL,
319                                         TRUE, depth, revprops, ctx,
320                                         pool));
321     }
322   else if (opt_state->show_revs == svn_cl__show_revs_eligible)
323     {
324       apr_array_header_t *revprops;
325
326       /* We need only revisions number, not revision properties. */
327       revprops = apr_array_make(pool, 0, sizeof(const char *));
328
329       SVN_ERR(svn_client_mergeinfo_log2(FALSE, target, &tgt_peg_revision,
330                                         source, &src_peg_revision,
331                                         src_start_revision,
332                                         src_end_revision,
333                                         print_log_rev, NULL,
334                                         TRUE, depth, revprops, ctx,
335                                         pool));
336     }
337   else
338     {
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"));
348
349       SVN_ERR(mergeinfo_summary(source, &src_peg_revision,
350                                 target, &tgt_peg_revision,
351                                 ctx, pool));
352     }
353   return SVN_NO_ERROR;
354 }