2 * status-cmd.c -- Display status information in current directory
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 /* ==================================================================== */
31 #include "svn_string.h"
33 #include "svn_client.h"
34 #include "svn_error_codes.h"
35 #include "svn_error.h"
36 #include "svn_pools.h"
38 #include "svn_dirent_uri.h"
40 #include "svn_cmdline.h"
43 #include "svn_private_config.h"
44 #include "private/svn_wc_private.h"
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;
62 apr_hash_t *cached_changelists;
63 apr_pool_t *cl_pool; /* where cached changelists are allocated */
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;
70 unsigned int text_conflicts;
71 unsigned int prop_conflicts;
72 unsigned int tree_conflicts;
74 svn_client_ctx_t *ctx;
81 const char *target_abspath;
82 const char *target_path;
83 svn_client_status_t *status;
86 /* Print conflict stats accumulated in status baton SB.
87 * Do temporary allocations in POOL. */
89 print_conflict_stats(struct status_baton *sb, apr_pool_t *pool)
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")));
95 if (sb->text_conflicts > 0)
96 SVN_ERR(svn_cmdline_printf
97 (pool, _(" Text conflicts: %u\n"), sb->text_conflicts));
99 if (sb->prop_conflicts > 0)
100 SVN_ERR(svn_cmdline_printf
101 (pool, _(" Property conflicts: %u\n"), sb->prop_conflicts));
103 if (sb->tree_conflicts > 0)
104 SVN_ERR(svn_cmdline_printf
105 (pool, _(" Tree conflicts: %u\n"), sb->tree_conflicts));
110 /* Prints XML target element with path attribute TARGET, using POOL for
111 temporary allocations. */
113 print_start_target_xml(const char *target, apr_pool_t *pool)
115 svn_stringbuf_t *sb = svn_stringbuf_create_empty(pool);
117 svn_xml_make_open_tag(&sb, pool, svn_xml_normal, "target",
118 "path", target, NULL);
120 return svn_cl__error_checked_fputs(sb->data, stdout);
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. */
128 print_finish_target_xml(svn_revnum_t repos_rev,
131 svn_stringbuf_t *sb = svn_stringbuf_create_empty(pool);
133 if (SVN_IS_VALID_REVNUM(repos_rev))
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);
141 svn_xml_make_close_tag(&sb, pool, "target");
143 return svn_cl__error_checked_fputs(sb->data, stdout);
147 /* Function which *actually* causes a status structure to be output to
148 the user. Called by both print_status() and svn_cl__status(). */
150 print_status_normal_or_xml(void *baton,
152 const svn_client_status_t *status,
155 struct status_baton *sb = baton;
158 return svn_cl__print_status_xml(sb->target_abspath, sb->target_path,
159 path, status, sb->ctx, pool);
161 return svn_cl__print_status(sb->target_abspath, sb->target_path,
163 sb->suppress_externals_placeholders,
165 sb->show_last_committed,
166 sb->skip_unrecognized,
176 /* A status callback function for printing STATUS for PATH. */
178 print_status(void *baton,
180 const svn_client_status_t *status,
183 struct status_baton *sb = baton;
184 const char *local_abspath = status->local_abspath;
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)
195 && (status->node_status == svn_wc_status_deleted
196 || status->node_status == svn_wc_status_replaced))
198 svn_client_status_t *twks = svn_client_status_dup(status, sb->cl_pool);
200 /* Copied is FALSE, so either we have a local addition, or we have
201 a delete that directly shadows a BASE node */
203 switch(status->node_status)
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)
210 ### Is this really what we want to provide? */
211 SVN_ERR(svn_wc__node_get_pre_ng_status_data(&twks->revision,
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,
222 &twks->changed_author,
229 /* This space intentionally left blank. */
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)
240 /* The hash maps a changelist name to an array of status_cache
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);
251 svn_hash_gets(sb->cached_changelists, cl_key);
252 if (path_array == NULL)
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);
259 APR_ARRAY_PUSH(path_array, struct status_cache *) = scache;
263 return print_status_normal_or_xml(baton, path, status, pool);
266 /* This implements the `svn_opt_subcommand_t' interface. */
268 svn_cl__status(apr_getopt_t *os,
270 apr_pool_t *scratch_pool)
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);
278 svn_opt_revision_t rev;
279 struct status_baton sb;
281 SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
286 /* Add "." if user passed 0 arguments */
287 svn_opt_push_implicit_dot_target(targets, scratch_pool);
289 SVN_ERR(svn_cl__check_targets_are_local_paths(targets));
291 /* We want our -u statuses to be against HEAD. */
292 rev.kind = svn_opt_revision_head;
294 sb.had_print_error = FALSE;
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));
306 if (opt_state->incremental)
307 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
308 _("'incremental' option only valid in XML "
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;
326 SVN_ERR(svn_cl__eat_peg_revisions(&targets, targets, scratch_pool));
328 iterpool = svn_pool_create(scratch_pool);
329 for (i = 0; i < targets->nelts; i++)
331 const char *target = APR_ARRAY_IDX(targets, i, const char *);
332 svn_revnum_t repos_rev = SVN_INVALID_REVNUM;
334 svn_pool_clear(iterpool);
336 SVN_ERR(svn_dirent_get_absolute(&(sb.target_abspath), target,
338 sb.target_path = target;
340 SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton));
343 SVN_ERR(print_start_target_xml(svn_dirent_local_style(target, iterpool),
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,
352 opt_state->no_ignore,
353 opt_state->ignore_externals,
354 FALSE /* depth_as_sticky */,
355 opt_state->changelists,
358 NULL, opt_state->quiet,
360 SVN_ERR_WC_NOT_WORKING_COPY,
361 SVN_ERR_WC_PATH_NOT_FOUND,
365 SVN_ERR(print_finish_target_xml(repos_rev, iterpool));
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)
372 apr_hash_index_t *hi;
373 svn_stringbuf_t *buf;
376 buf = svn_stringbuf_create_empty(scratch_pool);
378 for (hi = apr_hash_first(scratch_pool, master_cl_hash); hi;
379 hi = apr_hash_next(hi))
381 const char *changelist_name = svn__apr_hash_index_key(hi);
382 apr_array_header_t *path_array = svn__apr_hash_index_val(hi);
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. */
390 svn_stringbuf_setempty(buf);
391 svn_xml_make_open_tag(&buf, scratch_pool, svn_xml_normal,
392 "changelist", "name", changelist_name,
394 SVN_ERR(svn_cl__error_checked_fputs(buf->data, stdout));
397 SVN_ERR(svn_cmdline_printf(scratch_pool,
398 _("\n--- Changelist '%s':\n"),
401 for (j = 0; j < path_array->nelts; j++)
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));
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));
419 svn_pool_destroy(iterpool);
421 if (opt_state->xml && (! opt_state->incremental))
422 SVN_ERR(svn_cl__xml_print_footer("status", scratch_pool));
424 if (! opt_state->quiet && ! opt_state->xml)
425 SVN_ERR(print_conflict_stats(&sb, scratch_pool));