2 * status-cmd.c -- Display status information in current directory
4 * ====================================================================
5 * Copyright (c) 2000-2007 CollabNet. All rights reserved.
7 * This software is licensed as described in the file COPYING, which
8 * you should have received as part of this distribution. The terms
9 * are also available at http://subversion.tigris.org/license-1.html.
10 * If newer versions of this license are posted there, you may use a
11 * newer version instead, at your option.
13 * This software consists of voluntary contributions made by many
14 * individuals. For exact contribution history, see the revision
15 * history and logs, available at http://subversion.tigris.org/.
16 * ====================================================================
19 /* ==================================================================== */
25 #include "svn_string.h"
27 #include "svn_client.h"
28 #include "svn_error_codes.h"
29 #include "svn_error.h"
30 #include "svn_pools.h"
33 #include "svn_cmdline.h"
36 #include "svn_private_config.h"
44 /* These fields all correspond to the ones in the
45 svn_cl__print_status() interface. */
46 svn_boolean_t detailed
;
47 svn_boolean_t show_last_committed
;
48 svn_boolean_t skip_unrecognized
;
49 svn_boolean_t repos_locks
;
52 apr_hash_t
*cached_changelists
;
53 apr_pool_t
*cl_pool
; /* where cached changelists are allocated */
55 svn_boolean_t had_print_error
; /* To avoid printing lots of errors if we get
56 errors while printing to stdout */
57 svn_boolean_t xml_mode
;
64 svn_wc_status2_t
*status
;
68 /* Prints XML target element with path attribute TARGET, using POOL for
69 temporary allocations. */
71 print_start_target_xml(const char *target
, apr_pool_t
*pool
)
73 svn_stringbuf_t
*sb
= svn_stringbuf_create("", pool
);
75 svn_xml_make_open_tag(&sb
, pool
, svn_xml_normal
, "target",
76 "path", target
, NULL
);
78 return svn_cl__error_checked_fputs(sb
->data
, stdout
);
82 /* Finish a target element by optionally printing an against element if
83 * REPOS_REV is a valid revision number, and then printing an target end tag.
84 * Use POOL for temporary allocations. */
86 print_finish_target_xml(svn_revnum_t repos_rev
,
89 svn_stringbuf_t
*sb
= svn_stringbuf_create("", pool
);
91 if (SVN_IS_VALID_REVNUM(repos_rev
))
93 const char *repos_rev_str
;
94 repos_rev_str
= apr_psprintf(pool
, "%ld", repos_rev
);
95 svn_xml_make_open_tag(&sb
, pool
, svn_xml_self_closing
, "against",
96 "revision", repos_rev_str
, NULL
);
99 svn_xml_make_close_tag(&sb
, pool
, "target");
101 return svn_cl__error_checked_fputs(sb
->data
, stdout
);
105 /* Function which *actually* causes a status structure to be output to
106 the user. Called by both print_status() and svn_cl__status(). */
108 print_status_normal_or_xml(void *baton
,
110 svn_wc_status2_t
*status
)
112 struct status_baton
*sb
= baton
;
116 err
= svn_cl__print_status_xml(path
, status
, sb
->pool
);
118 err
= svn_cl__print_status(path
, status
, sb
->detailed
,
119 sb
->show_last_committed
,
120 sb
->skip_unrecognized
,
126 /* Print if it is the first error. */
127 if (!sb
->had_print_error
)
129 sb
->had_print_error
= TRUE
;
130 svn_handle_error2(err
, stderr
, FALSE
, "svn: ");
132 svn_error_clear(err
);
137 /* A status callback function for printing STATUS for PATH. */
139 print_status(void *baton
,
141 svn_wc_status2_t
*status
)
143 struct status_baton
*sb
= baton
;
145 /* If there's a changelist attached to the entry, then we don't print
146 the item, but instead dup & cache the status structure for later. */
147 if (status
->entry
&& status
->entry
->changelist
)
149 /* The hash maps a changelist name to an array of status_cache
151 apr_array_header_t
*path_array
;
152 const char *cl_key
= apr_pstrdup(sb
->cl_pool
, status
->entry
->changelist
);
153 struct status_cache
*scache
= apr_pcalloc(sb
->cl_pool
, sizeof(*scache
));
154 scache
->path
= apr_pstrdup(sb
->cl_pool
, path
);
155 scache
->status
= svn_wc_dup_status2(status
, sb
->cl_pool
);
157 path_array
= (apr_array_header_t
*)
158 apr_hash_get(sb
->cached_changelists
, cl_key
, APR_HASH_KEY_STRING
);
159 if (path_array
== NULL
)
161 path_array
= apr_array_make(sb
->cl_pool
, 1,
162 sizeof(struct status_cache
*));
163 apr_hash_set(sb
->cached_changelists
, cl_key
,
164 APR_HASH_KEY_STRING
, path_array
);
167 APR_ARRAY_PUSH(path_array
, struct status_cache
*) = scache
;
171 print_status_normal_or_xml(baton
, path
, status
);
174 /* Simpler helper to allow use of svn_cl__try. */
176 do_status(svn_cl__opt_state_t
*opt_state
,
178 const svn_opt_revision_t
*rev
,
180 svn_client_ctx_t
*ctx
,
183 svn_revnum_t repos_rev
= SVN_INVALID_REVNUM
;
186 SVN_ERR(print_start_target_xml(svn_path_local_style(target
, pool
), pool
));
188 SVN_ERR(svn_client_status3(&repos_rev
, target
, rev
,
189 print_status
, status_baton
,
193 opt_state
->no_ignore
,
194 opt_state
->ignore_externals
,
195 opt_state
->changelists
,
199 SVN_ERR(print_finish_target_xml(repos_rev
, pool
));
204 /* This implements the `svn_opt_subcommand_t' interface. */
206 svn_cl__status(apr_getopt_t
*os
,
210 svn_cl__opt_state_t
*opt_state
= ((svn_cl__cmd_baton_t
*) baton
)->opt_state
;
211 svn_client_ctx_t
*ctx
= ((svn_cl__cmd_baton_t
*) baton
)->ctx
;
212 apr_array_header_t
*targets
;
214 apr_hash_t
*master_cl_hash
= apr_hash_make(pool
);
216 svn_opt_revision_t rev
;
217 struct status_baton sb
;
219 SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets
, os
,
223 /* Add "." if user passed 0 arguments */
224 svn_opt_push_implicit_dot_target(targets
, pool
);
226 /* We want our -u statuses to be against HEAD. */
227 rev
.kind
= svn_opt_revision_head
;
229 /* The notification callback, leave the notifier as NULL in XML mode */
230 if (! opt_state
->xml
)
231 svn_cl__get_notifier(&ctx
->notify_func2
, &ctx
->notify_baton2
, FALSE
,
234 subpool
= svn_pool_create(pool
);
236 sb
.had_print_error
= FALSE
;
240 /* If output is not incremental, output the XML header and wrap
241 everything in a top-level element. This makes the output in
242 its entirety a well-formed XML document. */
243 if (! opt_state
->incremental
)
244 SVN_ERR(svn_cl__xml_print_header("status", pool
));
248 if (opt_state
->incremental
)
249 return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR
, NULL
,
250 _("'incremental' option only valid in XML "
254 sb
.detailed
= (opt_state
->verbose
|| opt_state
->update
);
255 sb
.show_last_committed
= opt_state
->verbose
;
256 sb
.skip_unrecognized
= opt_state
->quiet
;
257 sb
.repos_locks
= opt_state
->update
;
258 sb
.xml_mode
= opt_state
->xml
;
260 sb
.cached_changelists
= master_cl_hash
;
263 for (i
= 0; i
< targets
->nelts
; i
++)
265 const char *target
= APR_ARRAY_IDX(targets
, i
, const char *);
267 svn_pool_clear(subpool
);
269 SVN_ERR(svn_cl__check_cancel(ctx
->cancel_baton
));
271 /* Retrieve a hash of status structures with the information
272 requested by the user. */
273 SVN_ERR(svn_cl__try(do_status(opt_state
, target
, &rev
, &sb
, ctx
,
275 NULL
, opt_state
->quiet
,
276 SVN_ERR_WC_NOT_DIRECTORY
, /* not versioned */
280 /* If any paths were cached because they were associatied with
281 changelists, we can now display them as grouped changelists. */
282 if (apr_hash_count(master_cl_hash
) > 0)
284 apr_hash_index_t
*hi
;
285 svn_stringbuf_t
*buf
;
288 buf
= svn_stringbuf_create("", pool
);
290 for (hi
= apr_hash_first(pool
, master_cl_hash
); hi
;
291 hi
= apr_hash_next(hi
))
293 const char *changelist_name
;
294 apr_array_header_t
*path_array
;
299 apr_hash_this(hi
, &key
, NULL
, &val
);
300 changelist_name
= key
;
303 /* ### TODO: For non-XML output, we shouldn't print the
304 ### leading \n on the first changelist if there were no
305 ### non-changelist entries. */
308 svn_stringbuf_set(buf
, "");
309 svn_xml_make_open_tag(&buf
, pool
, svn_xml_normal
, "changelist",
310 "name", changelist_name
, NULL
);
311 SVN_ERR(svn_cl__error_checked_fputs(buf
->data
, stdout
));
314 SVN_ERR(svn_cmdline_printf(pool
, _("\n--- Changelist '%s':\n"),
317 for (j
= 0; j
< path_array
->nelts
; j
++)
319 struct status_cache
*scache
=
320 APR_ARRAY_IDX(path_array
, j
, struct status_cache
*);
321 print_status_normal_or_xml(&sb
, scache
->path
, scache
->status
);
326 svn_stringbuf_set(buf
, "");
327 svn_xml_make_close_tag(&buf
, pool
, "changelist");
328 SVN_ERR(svn_cl__error_checked_fputs(buf
->data
, stdout
));
333 svn_pool_destroy(subpool
);
334 if (opt_state
->xml
&& (! opt_state
->incremental
))
335 SVN_ERR(svn_cl__xml_print_footer("status", pool
));