Fix compiler warning due to missing function prototype.
[svn.git] / subversion / libsvn_client / status.c
blob1bbe2559f7ccc852fb401ecdf9953bffbc9b01e3
1 /*
2 * status.c: return the status of a working copy dirent
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 /* ==================================================================== */
23 /*** Includes. ***/
24 #include <assert.h>
25 #include <apr_strings.h>
26 #include <apr_pools.h>
28 #include "svn_pools.h"
29 #include "client.h"
31 #include "svn_path.h"
32 #include "svn_delta.h"
33 #include "svn_client.h"
34 #include "svn_error.h"
35 #include "svn_hash.h"
37 #include "svn_private_config.h"
38 #include "private/svn_wc_private.h"
41 /*** Getting update information ***/
43 /* Baton for tweak_status. It wraps a bit of extra functionality
44 around the received status func/baton, so we can remember if the
45 target was deleted in HEAD and tweak incoming status structures
46 accordingly. */
47 struct status_baton
49 svn_boolean_t deleted_in_repos; /* target is deleted in repos */
50 apr_hash_t *changelist_hash; /* keys are changelist names */
51 svn_wc_status_func2_t real_status_func; /* real status function */
52 void *real_status_baton; /* real status baton */
55 /* A status callback function which wraps the *real* status
56 function/baton. This sucker takes care of any status tweaks we
57 need to make (such as noting that the target of the status is
58 missing from HEAD in the repository).
60 This implements the 'svn_wc_status_func2_t' function type. */
61 static void
62 tweak_status(void *baton,
63 const char *path,
64 svn_wc_status2_t *status)
66 struct status_baton *sb = baton;
68 /* If we know that the target was deleted in HEAD of the repository,
69 we need to note that fact in all the status structures that come
70 through here. */
71 if (sb->deleted_in_repos)
72 status->repos_text_status = svn_wc_status_deleted;
74 /* If the status item has an entry, but doesn't belong to one of the
75 changelists our caller is interested in, we filter our this status
76 transmission. */
77 if (! SVN_WC__CL_MATCH(sb->changelist_hash, status->entry))
78 return;
80 /* Call the real status function/baton. */
81 sb->real_status_func(sb->real_status_baton, path, status);
84 /* A baton for our reporter that is used to collect locks. */
85 typedef struct report_baton_t {
86 const svn_ra_reporter3_t* wrapped_reporter;
87 void *wrapped_report_baton;
88 /* The common ancestor URL of all paths included in the report. */
89 char *ancestor;
90 void *set_locks_baton;
91 svn_client_ctx_t *ctx;
92 /* Pool to store locks in. */
93 apr_pool_t *pool;
94 } report_baton_t;
96 /* Implements svn_ra_reporter3_t->set_path. */
97 static svn_error_t *
98 reporter_set_path(void *report_baton, const char *path,
99 svn_revnum_t revision, svn_depth_t depth,
100 svn_boolean_t start_empty, const char *lock_token,
101 apr_pool_t *pool)
103 report_baton_t *rb = report_baton;
105 return rb->wrapped_reporter->set_path(rb->wrapped_report_baton, path,
106 revision, depth, start_empty,
107 lock_token, pool);
110 /* Implements svn_ra_reporter3_t->delete_path. */
111 static svn_error_t *
112 reporter_delete_path(void *report_baton, const char *path, apr_pool_t *pool)
114 report_baton_t *rb = report_baton;
116 return rb->wrapped_reporter->delete_path(rb->wrapped_report_baton, path,
117 pool);
120 /* Implements svn_ra_reporter3_t->link_path. */
121 static svn_error_t *
122 reporter_link_path(void *report_baton, const char *path, const char *url,
123 svn_revnum_t revision, svn_depth_t depth,
124 svn_boolean_t start_empty,
125 const char *lock_token, apr_pool_t *pool)
127 report_baton_t *rb = report_baton;
128 const char *ancestor;
129 apr_size_t len;
131 ancestor = svn_path_get_longest_ancestor(url, rb->ancestor, pool);
133 /* If we got a shorter ancestor, truncate our current ancestor.
134 Note that svn_path_get_longest_ancestor will allocate its return
135 value even if it identical to one of its arguments. */
136 len = strlen(ancestor);
137 if (len < strlen(rb->ancestor))
138 rb->ancestor[len] = '\0';
140 return rb->wrapped_reporter->link_path(rb->wrapped_report_baton, path, url,
141 revision, depth, start_empty,
142 lock_token, pool);
145 /* Implements svn_ra_reporter3_t->finish_report. */
146 static svn_error_t *
147 reporter_finish_report(void *report_baton, apr_pool_t *pool)
149 report_baton_t *rb = report_baton;
150 svn_ra_session_t *ras;
151 apr_hash_t *locks;
152 const char *repos_root;
153 apr_pool_t *subpool = svn_pool_create(pool);
154 svn_error_t *err = SVN_NO_ERROR;
156 /* Open an RA session to our common ancestor and grab the locks under it.
158 SVN_ERR(svn_client__open_ra_session_internal(&ras, rb->ancestor, NULL,
159 NULL, NULL, FALSE, TRUE,
160 rb->ctx, subpool));
162 /* The locks need to live throughout the edit. Note that if the
163 server doesn't support lock discovery, we'll just not do locky
164 stuff. */
165 err = svn_ra_get_locks(ras, &locks, "", rb->pool);
166 if (err && ((err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)
167 || (err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE)))
169 svn_error_clear(err);
170 err = SVN_NO_ERROR;
171 locks = apr_hash_make(rb->pool);
173 SVN_ERR(err);
175 SVN_ERR(svn_ra_get_repos_root2(ras, &repos_root, rb->pool));
177 /* Close the RA session. */
178 svn_pool_destroy(subpool);
180 SVN_ERR(svn_wc_status_set_repos_locks(rb->set_locks_baton, locks,
181 repos_root, rb->pool));
183 return rb->wrapped_reporter->finish_report(rb->wrapped_report_baton, pool);
186 /* Implements svn_ra_reporter3_t->abort_report. */
187 static svn_error_t *
188 reporter_abort_report(void *report_baton, apr_pool_t *pool)
190 report_baton_t *rb = report_baton;
192 return rb->wrapped_reporter->abort_report(rb->wrapped_report_baton, pool);
195 /* A reporter that keeps track of the common URL ancestor of all paths in
196 the WC and fetches repository locks for all paths under this ancestor. */
197 static svn_ra_reporter3_t lock_fetch_reporter = {
198 reporter_set_path,
199 reporter_delete_path,
200 reporter_link_path,
201 reporter_finish_report,
202 reporter_abort_report
206 /*** Public Interface. ***/
209 svn_error_t *
210 svn_client_status3(svn_revnum_t *result_rev,
211 const char *path,
212 const svn_opt_revision_t *revision,
213 svn_wc_status_func2_t status_func,
214 void *status_baton,
215 svn_depth_t depth,
216 svn_boolean_t get_all,
217 svn_boolean_t update,
218 svn_boolean_t no_ignore,
219 svn_boolean_t ignore_externals,
220 const apr_array_header_t *changelists,
221 svn_client_ctx_t *ctx,
222 apr_pool_t *pool)
224 svn_wc_adm_access_t *anchor_access, *target_access;
225 svn_wc_traversal_info_t *traversal_info = svn_wc_init_traversal_info(pool);
226 const char *anchor, *target;
227 const svn_delta_editor_t *editor;
228 void *edit_baton, *set_locks_baton;
229 const svn_wc_entry_t *entry = NULL;
230 struct status_baton sb;
231 apr_array_header_t *ignores;
232 svn_error_t *err;
233 apr_hash_t *changelist_hash = NULL;
234 svn_revnum_t edit_revision = SVN_INVALID_REVNUM;
236 if (changelists && changelists->nelts)
237 SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelists, pool));
239 sb.real_status_func = status_func;
240 sb.real_status_baton = status_baton;
241 sb.deleted_in_repos = FALSE;
242 sb.changelist_hash = changelist_hash;
244 /* Try to open the target directory. If the target is a file or an
245 unversioned directory, open the parent directory instead */
246 err = svn_wc_adm_open3(&anchor_access, NULL, path, FALSE,
247 SVN_DEPTH_IS_RECURSIVE(depth) ? -1 : 1,
248 ctx->cancel_func, ctx->cancel_baton,
249 pool);
250 if (err && err->apr_err == SVN_ERR_WC_NOT_DIRECTORY)
252 svn_error_clear(err);
253 SVN_ERR(svn_wc_adm_open_anchor(&anchor_access, &target_access, &target,
254 path, FALSE,
255 SVN_DEPTH_IS_RECURSIVE(depth) ? -1 : 1,
256 ctx->cancel_func, ctx->cancel_baton,
257 pool));
259 else if (!err)
261 target = "";
262 target_access = anchor_access;
264 else
265 return err;
267 anchor = svn_wc_adm_access_path(anchor_access);
269 /* Get the status edit, and use our wrapping status function/baton
270 as the callback pair. */
271 SVN_ERR(svn_wc_get_default_ignores(&ignores, ctx->config, pool));
272 SVN_ERR(svn_wc_get_status_editor3(&editor, &edit_baton, &set_locks_baton,
273 &edit_revision, anchor_access, target,
274 depth, get_all, no_ignore, ignores,
275 tweak_status, &sb, ctx->cancel_func,
276 ctx->cancel_baton, traversal_info,
277 pool));
279 /* If we want to know about out-of-dateness, we crawl the working copy and
280 let the RA layer drive the editor for real. Otherwise, we just close the
281 edit. :-) */
282 if (update)
284 svn_ra_session_t *ra_session;
285 const char *URL;
286 svn_node_kind_t kind;
287 svn_boolean_t server_supports_depth;
289 /* Get full URL from the ANCHOR. */
290 if (! entry)
291 SVN_ERR(svn_wc__entry_versioned(&entry, anchor, anchor_access, FALSE,
292 pool));
293 if (! entry->url)
294 return svn_error_createf
295 (SVN_ERR_ENTRY_MISSING_URL, NULL,
296 _("Entry '%s' has no URL"),
297 svn_path_local_style(anchor, pool));
298 URL = apr_pstrdup(pool, entry->url);
300 /* Open a repository session to the URL. */
301 SVN_ERR(svn_client__open_ra_session_internal(&ra_session, URL, anchor,
302 anchor_access, NULL,
303 FALSE, TRUE,
304 ctx, pool));
306 /* Verify that URL exists in HEAD. If it doesn't, this can save
307 us a whole lot of hassle; if it does, the cost of this
308 request should be minimal compared to the size of getting
309 back the average amount of "out-of-date" information. */
310 SVN_ERR(svn_ra_check_path(ra_session, "", SVN_INVALID_REVNUM,
311 &kind, pool));
312 if (kind == svn_node_none)
314 /* Our status target does not exist in HEAD of the
315 repository. If we're just adding this thing, that's
316 fine. But if it was previously versioned, then it must
317 have been deleted from the repository. */
318 if (entry->schedule != svn_wc_schedule_add)
319 sb.deleted_in_repos = TRUE;
321 /* And now close the edit. */
322 SVN_ERR(editor->close_edit(edit_baton, pool));
324 else
326 svn_revnum_t revnum;
327 report_baton_t rb;
329 if (revision->kind == svn_opt_revision_head)
331 /* Cause the revision number to be omitted from the request,
332 which implies HEAD. */
333 revnum = SVN_INVALID_REVNUM;
335 else
337 /* Get a revision number for our status operation. */
338 SVN_ERR(svn_client__get_revision_number
339 (&revnum, NULL, ra_session, revision, target, pool));
342 /* Do the deed. Let the RA layer drive the status editor. */
343 SVN_ERR(svn_ra_do_status2(ra_session, &rb.wrapped_reporter,
344 &rb.wrapped_report_baton,
345 target, revnum, depth, editor,
346 edit_baton, pool));
348 /* Init the report baton. */
349 rb.ancestor = apr_pstrdup(pool, URL);
350 rb.set_locks_baton = set_locks_baton;
351 rb.ctx = ctx;
352 rb.pool = pool;
354 SVN_ERR(svn_ra_has_capability(ra_session, &server_supports_depth,
355 SVN_RA_CAPABILITY_DEPTH, pool));
357 /* Drive the reporter structure, describing the revisions
358 within PATH. When we call reporter->finish_report,
359 EDITOR will be driven to describe differences between our
360 working copy and HEAD. */
361 SVN_ERR(svn_wc_crawl_revisions3(path, target_access,
362 &lock_fetch_reporter, &rb, FALSE,
363 depth, (! server_supports_depth),
364 FALSE, NULL, NULL, NULL, pool));
367 else
369 SVN_ERR(editor->close_edit(edit_baton, pool));
372 if (ctx->notify_func2 && update)
374 svn_wc_notify_t *notify
375 = svn_wc_create_notify(path, svn_wc_notify_status_completed, pool);
376 notify->revision = edit_revision;
377 (ctx->notify_func2)(ctx->notify_baton2, notify, pool);
380 /* If the caller wants the result revision, give it to them. */
381 if (result_rev)
382 *result_rev = edit_revision;
384 /* Close the access baton here, as svn_client__do_external_status()
385 calls back into this function and thus will be re-opening the
386 working copy. */
387 SVN_ERR(svn_wc_adm_close(anchor_access));
389 /* If there are svn:externals set, we don't want those to show up as
390 unversioned or unrecognized, so patch up the hash. If caller wants
391 all the statuses, we will change unversioned status items that
392 are interesting to an svn:externals property to
393 svn_wc_status_unversioned, otherwise we'll just remove the status
394 item altogether.
396 We only descend into an external if depth is svn_depth_infinity or
397 svn_depth_unknown. However, there are conceivable behaviors that
398 would involve descending under other circumstances; thus, we pass
399 depth anyway, so the code will DTRT if we change the conditional
400 in the future.
402 if (SVN_DEPTH_IS_RECURSIVE(depth) && (! ignore_externals))
403 SVN_ERR(svn_client__do_external_status(traversal_info, status_func,
404 status_baton, depth, get_all,
405 update, no_ignore, ctx, pool));
407 return SVN_NO_ERROR;
410 svn_error_t *
411 svn_client_status2(svn_revnum_t *result_rev,
412 const char *path,
413 const svn_opt_revision_t *revision,
414 svn_wc_status_func2_t status_func,
415 void *status_baton,
416 svn_boolean_t recurse,
417 svn_boolean_t get_all,
418 svn_boolean_t update,
419 svn_boolean_t no_ignore,
420 svn_boolean_t ignore_externals,
421 svn_client_ctx_t *ctx,
422 apr_pool_t *pool)
424 return svn_client_status3(result_rev, path, revision,
425 status_func, status_baton,
426 SVN_DEPTH_INFINITY_OR_IMMEDIATES(recurse),
427 get_all, update, no_ignore, ignore_externals, NULL,
428 ctx, pool);
432 /* Baton for old_status_func_cb; does what you think it does. */
433 struct old_status_func_cb_baton
435 svn_wc_status_func_t original_func;
436 void *original_baton;
439 /* Help svn_client_status() accept an old-style status func and baton,
440 by wrapping them before passing along to svn_client_status2().
442 This implements the 'svn_wc_status_func2_t' function type. */
443 static void old_status_func_cb(void *baton,
444 const char *path,
445 svn_wc_status2_t *status)
447 struct old_status_func_cb_baton *b = baton;
448 svn_wc_status_t *stat = (svn_wc_status_t *) status;
450 b->original_func(b->original_baton, path, stat);
454 svn_error_t *
455 svn_client_status(svn_revnum_t *result_rev,
456 const char *path,
457 svn_opt_revision_t *revision,
458 svn_wc_status_func_t status_func,
459 void *status_baton,
460 svn_boolean_t recurse,
461 svn_boolean_t get_all,
462 svn_boolean_t update,
463 svn_boolean_t no_ignore,
464 svn_client_ctx_t *ctx,
465 apr_pool_t *pool)
467 struct old_status_func_cb_baton *b = apr_pcalloc(pool, sizeof(*b));
468 b->original_func = status_func;
469 b->original_baton = status_baton;
471 return svn_client_status2(result_rev, path, revision,
472 old_status_func_cb, b,
473 recurse, get_all, update, no_ignore, FALSE,
474 ctx, pool);