]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/subversion/subversion/svn/status-cmd.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / contrib / subversion / subversion / svn / status-cmd.c
1 /*
2  * status-cmd.c -- Display status information in current directory
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_hash.h"
31 #include "svn_string.h"
32 #include "svn_wc.h"
33 #include "svn_client.h"
34 #include "svn_error_codes.h"
35 #include "svn_error.h"
36 #include "svn_pools.h"
37 #include "svn_xml.h"
38 #include "svn_dirent_uri.h"
39 #include "svn_path.h"
40 #include "svn_cmdline.h"
41 #include "cl.h"
42
43 #include "svn_private_config.h"
44 #include "private/svn_wc_private.h"
45
46
47 \f
48 /*** Code. ***/
49
50 struct status_baton
51 {
52   /* These fields all correspond to the ones in the
53      svn_cl__print_status() interface. */
54   const char *target_abspath;
55   const char *target_path;
56   svn_boolean_t suppress_externals_placeholders;
57   svn_boolean_t detailed;
58   svn_boolean_t show_last_committed;
59   svn_boolean_t skip_unrecognized;
60   svn_boolean_t repos_locks;
61
62   apr_hash_t *cached_changelists;
63   apr_pool_t *cl_pool;          /* where cached changelists are allocated */
64
65   svn_boolean_t had_print_error;  /* To avoid printing lots of errors if we get
66                                      errors while printing to stdout */
67   svn_boolean_t xml_mode;
68
69   /* Conflict stats. */
70   unsigned int text_conflicts;
71   unsigned int prop_conflicts;
72   unsigned int tree_conflicts;
73
74   svn_client_ctx_t *ctx;
75 };
76
77
78 struct status_cache
79 {
80   const char *path;
81   const char *target_abspath;
82   const char *target_path;
83   svn_client_status_t *status;
84 };
85
86 /* Print conflict stats accumulated in status baton SB.
87  * Do temporary allocations in POOL. */
88 static svn_error_t *
89 print_conflict_stats(struct status_baton *sb, apr_pool_t *pool)
90 {
91   if (sb->text_conflicts > 0 || sb->prop_conflicts > 0 ||
92       sb->tree_conflicts > 0)
93       SVN_ERR(svn_cmdline_printf(pool, "%s", _("Summary of conflicts:\n")));
94
95   if (sb->text_conflicts > 0)
96     SVN_ERR(svn_cmdline_printf
97       (pool, _("  Text conflicts: %u\n"), sb->text_conflicts));
98
99   if (sb->prop_conflicts > 0)
100     SVN_ERR(svn_cmdline_printf
101       (pool, _("  Property conflicts: %u\n"), sb->prop_conflicts));
102
103   if (sb->tree_conflicts > 0)
104     SVN_ERR(svn_cmdline_printf
105       (pool, _("  Tree conflicts: %u\n"), sb->tree_conflicts));
106
107   return SVN_NO_ERROR;
108 }
109
110 /* Prints XML target element with path attribute TARGET, using POOL for
111    temporary allocations. */
112 static svn_error_t *
113 print_start_target_xml(const char *target, apr_pool_t *pool)
114 {
115   svn_stringbuf_t *sb = svn_stringbuf_create_empty(pool);
116
117   svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "target",
118                         "path", target, NULL);
119
120   return svn_cl__error_checked_fputs(sb->data, stdout);
121 }
122
123
124 /* Finish a target element by optionally printing an against element if
125  * REPOS_REV is a valid revision number, and then printing an target end tag.
126  * Use POOL for temporary allocations. */
127 static svn_error_t *
128 print_finish_target_xml(svn_revnum_t repos_rev,
129                         apr_pool_t *pool)
130 {
131   svn_stringbuf_t *sb = svn_stringbuf_create_empty(pool);
132
133   if (SVN_IS_VALID_REVNUM(repos_rev))
134     {
135       const char *repos_rev_str;
136       repos_rev_str = apr_psprintf(pool, "%ld", repos_rev);
137       svn_xml_make_open_tag(&sb, pool, svn_xml_self_closing, "against",
138                             "revision", repos_rev_str, NULL);
139     }
140
141   svn_xml_make_close_tag(&sb, pool, "target");
142
143   return svn_cl__error_checked_fputs(sb->data, stdout);
144 }
145
146
147 /* Function which *actually* causes a status structure to be output to
148    the user.  Called by both print_status() and svn_cl__status(). */
149 static svn_error_t *
150 print_status_normal_or_xml(void *baton,
151                            const char *path,
152                            const svn_client_status_t *status,
153                            apr_pool_t *pool)
154 {
155   struct status_baton *sb = baton;
156
157   if (sb->xml_mode)
158     return svn_cl__print_status_xml(sb->target_abspath, sb->target_path,
159                                     path, status, sb->ctx, pool);
160   else
161     return svn_cl__print_status(sb->target_abspath, sb->target_path,
162                                 path, status,
163                                 sb->suppress_externals_placeholders,
164                                 sb->detailed,
165                                 sb->show_last_committed,
166                                 sb->skip_unrecognized,
167                                 sb->repos_locks,
168                                 &sb->text_conflicts,
169                                 &sb->prop_conflicts,
170                                 &sb->tree_conflicts,
171                                 sb->ctx,
172                                 pool);
173 }
174
175
176 /* A status callback function for printing STATUS for PATH. */
177 static svn_error_t *
178 print_status(void *baton,
179              const char *path,
180              const svn_client_status_t *status,
181              apr_pool_t *pool)
182 {
183   struct status_baton *sb = baton;
184   const char *local_abspath = status->local_abspath;
185
186   /* ### The revision information with associates are based on what
187    * ### _read_info() returns. The svn_wc_status_func4_t callback is
188    * ### suppposed to handle the gathering of additional information from the
189    * ### WORKING nodes on its own. Until we've agreed on how the CLI should
190    * ### handle the revision information, we use this appproach to stay compat
191    * ### with our testsuite. */
192   if (status->versioned
193       && !SVN_IS_VALID_REVNUM(status->revision)
194       && !status->copied
195       && (status->node_status == svn_wc_status_deleted
196           || status->node_status == svn_wc_status_replaced))
197     {
198       svn_client_status_t *twks = svn_client_status_dup(status, sb->cl_pool);
199
200       /* Copied is FALSE, so either we have a local addition, or we have
201          a delete that directly shadows a BASE node */
202
203       switch(status->node_status)
204         {
205           case svn_wc_status_replaced:
206             /* Just retrieve the revision below the replacement.
207                The other fields are filled by a copy.
208                (With ! copied, we know we have a BASE node)
209
210                ### Is this really what we want to provide? */
211             SVN_ERR(svn_wc__node_get_pre_ng_status_data(&twks->revision,
212                                                         NULL, NULL, NULL,
213                                                         sb->ctx->wc_ctx,
214                                                         local_abspath,
215                                                         sb->cl_pool, pool));
216             break;
217           case svn_wc_status_deleted:
218             /* Retrieve some data from the original version below the delete */
219             SVN_ERR(svn_wc__node_get_pre_ng_status_data(&twks->revision,
220                                                         &twks->changed_rev,
221                                                         &twks->changed_date,
222                                                         &twks->changed_author,
223                                                         sb->ctx->wc_ctx,
224                                                         local_abspath,
225                                                         sb->cl_pool, pool));
226             break;
227
228           default:
229             /* This space intentionally left blank. */
230             break;
231         }
232
233       status = twks;
234     }
235
236   /* If the path is part of a changelist, then we don't print
237      the item, but instead dup & cache the status structure for later. */
238   if (status->changelist)
239     {
240       /* The hash maps a changelist name to an array of status_cache
241          structures. */
242       apr_array_header_t *path_array;
243       const char *cl_key = apr_pstrdup(sb->cl_pool, status->changelist);
244       struct status_cache *scache = apr_pcalloc(sb->cl_pool, sizeof(*scache));
245       scache->path = apr_pstrdup(sb->cl_pool, path);
246       scache->target_abspath = apr_pstrdup(sb->cl_pool, sb->target_abspath);
247       scache->target_path = apr_pstrdup(sb->cl_pool, sb->target_path);
248       scache->status = svn_client_status_dup(status, sb->cl_pool);
249
250       path_array =
251         svn_hash_gets(sb->cached_changelists, cl_key);
252       if (path_array == NULL)
253         {
254           path_array = apr_array_make(sb->cl_pool, 1,
255                                       sizeof(struct status_cache *));
256           svn_hash_sets(sb->cached_changelists, cl_key, path_array);
257         }
258
259       APR_ARRAY_PUSH(path_array, struct status_cache *) = scache;
260       return SVN_NO_ERROR;
261     }
262
263   return print_status_normal_or_xml(baton, path, status, pool);
264 }
265
266 /* This implements the `svn_opt_subcommand_t' interface. */
267 svn_error_t *
268 svn_cl__status(apr_getopt_t *os,
269                void *baton,
270                apr_pool_t *scratch_pool)
271 {
272   svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
273   svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
274   apr_array_header_t *targets;
275   apr_pool_t *iterpool;
276   apr_hash_t *master_cl_hash = apr_hash_make(scratch_pool);
277   int i;
278   svn_opt_revision_t rev;
279   struct status_baton sb;
280
281   SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
282                                                       opt_state->targets,
283                                                       ctx, FALSE,
284                                                       scratch_pool));
285
286   /* Add "." if user passed 0 arguments */
287   svn_opt_push_implicit_dot_target(targets, scratch_pool);
288
289   SVN_ERR(svn_cl__check_targets_are_local_paths(targets));
290
291   /* We want our -u statuses to be against HEAD. */
292   rev.kind = svn_opt_revision_head;
293
294   sb.had_print_error = FALSE;
295
296   if (opt_state->xml)
297     {
298       /* If output is not incremental, output the XML header and wrap
299          everything in a top-level element. This makes the output in
300          its entirety a well-formed XML document. */
301       if (! opt_state->incremental)
302         SVN_ERR(svn_cl__xml_print_header("status", scratch_pool));
303     }
304   else
305     {
306       if (opt_state->incremental)
307         return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
308                                 _("'incremental' option only valid in XML "
309                                   "mode"));
310     }
311
312   sb.suppress_externals_placeholders = (opt_state->quiet
313                                         && (! opt_state->verbose));
314   sb.detailed = (opt_state->verbose || opt_state->update);
315   sb.show_last_committed = opt_state->verbose;
316   sb.skip_unrecognized = opt_state->quiet;
317   sb.repos_locks = opt_state->update;
318   sb.xml_mode = opt_state->xml;
319   sb.cached_changelists = master_cl_hash;
320   sb.cl_pool = scratch_pool;
321   sb.text_conflicts = 0;
322   sb.prop_conflicts = 0;
323   sb.tree_conflicts = 0;
324   sb.ctx = ctx;
325
326   SVN_ERR(svn_cl__eat_peg_revisions(&targets, targets, scratch_pool));
327
328   iterpool = svn_pool_create(scratch_pool);
329   for (i = 0; i < targets->nelts; i++)
330     {
331       const char *target = APR_ARRAY_IDX(targets, i, const char *);
332       svn_revnum_t repos_rev = SVN_INVALID_REVNUM;
333
334       svn_pool_clear(iterpool);
335
336       SVN_ERR(svn_dirent_get_absolute(&(sb.target_abspath), target,
337                                       scratch_pool));
338       sb.target_path = target;
339
340       SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton));
341
342       if (opt_state->xml)
343         SVN_ERR(print_start_target_xml(svn_dirent_local_style(target, iterpool),
344                                        iterpool));
345
346       /* Retrieve a hash of status structures with the information
347          requested by the user. */
348       SVN_ERR(svn_cl__try(svn_client_status5(&repos_rev, ctx, target, &rev,
349                                              opt_state->depth,
350                                              opt_state->verbose,
351                                              opt_state->update,
352                                              opt_state->no_ignore,
353                                              opt_state->ignore_externals,
354                                              FALSE /* depth_as_sticky */,
355                                              opt_state->changelists,
356                                              print_status, &sb,
357                                              iterpool),
358                           NULL, opt_state->quiet,
359                           /* not versioned: */
360                           SVN_ERR_WC_NOT_WORKING_COPY,
361                           SVN_ERR_WC_PATH_NOT_FOUND,
362                           SVN_NO_ERROR));
363
364       if (opt_state->xml)
365         SVN_ERR(print_finish_target_xml(repos_rev, iterpool));
366     }
367
368   /* If any paths were cached because they were associatied with
369      changelists, we can now display them as grouped changelists. */
370   if (apr_hash_count(master_cl_hash) > 0)
371     {
372       apr_hash_index_t *hi;
373       svn_stringbuf_t *buf;
374
375       if (opt_state->xml)
376         buf = svn_stringbuf_create_empty(scratch_pool);
377
378       for (hi = apr_hash_first(scratch_pool, master_cl_hash); hi;
379            hi = apr_hash_next(hi))
380         {
381           const char *changelist_name = svn__apr_hash_index_key(hi);
382           apr_array_header_t *path_array = svn__apr_hash_index_val(hi);
383           int j;
384
385           /* ### TODO: For non-XML output, we shouldn't print the
386              ### leading \n on the first changelist if there were no
387              ### non-changelist entries. */
388           if (opt_state->xml)
389             {
390               svn_stringbuf_setempty(buf);
391               svn_xml_make_open_tag(&buf, scratch_pool, svn_xml_normal,
392                                     "changelist", "name", changelist_name,
393                                     NULL);
394               SVN_ERR(svn_cl__error_checked_fputs(buf->data, stdout));
395             }
396           else
397             SVN_ERR(svn_cmdline_printf(scratch_pool,
398                                        _("\n--- Changelist '%s':\n"),
399                                        changelist_name));
400
401           for (j = 0; j < path_array->nelts; j++)
402             {
403               struct status_cache *scache =
404                 APR_ARRAY_IDX(path_array, j, struct status_cache *);
405               sb.target_abspath = scache->target_abspath;
406               sb.target_path = scache->target_path;
407               SVN_ERR(print_status_normal_or_xml(&sb, scache->path,
408                                                  scache->status, scratch_pool));
409             }
410
411           if (opt_state->xml)
412             {
413               svn_stringbuf_setempty(buf);
414               svn_xml_make_close_tag(&buf, scratch_pool, "changelist");
415               SVN_ERR(svn_cl__error_checked_fputs(buf->data, stdout));
416             }
417         }
418     }
419   svn_pool_destroy(iterpool);
420
421   if (opt_state->xml && (! opt_state->incremental))
422     SVN_ERR(svn_cl__xml_print_footer("status", scratch_pool));
423
424   if (! opt_state->quiet && ! opt_state->xml)
425       SVN_ERR(print_conflict_stats(&sb, scratch_pool));
426
427   return SVN_NO_ERROR;
428 }