2
* status.c: return the status of a working copy dirent
4
* ====================================================================
5
* Copyright (c) 2000-2004 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 <apr_strings.h>
26
#include <apr_pools.h>
28
#include "svn_pools.h"
33
#include "svn_delta.h"
34
#include "svn_client.h"
35
#include "svn_error.h"
37
#include "svn_private_config.h"
40
/*** Getting update information ***/
44
svn_boolean_t deleted_in_repos; /* target is deleted in repos */
45
svn_wc_status_func2_t real_status_func; /* real status function */
46
void *real_status_baton; /* real status baton */
49
/* A status callback function which wraps the *real* status
50
function/baton. This sucker takes care of any status tweaks we
51
need to make (such as noting that the target of the status is
52
missing from HEAD in the repository). */
54
tweak_status (void *baton,
56
svn_wc_status2_t *status)
58
struct status_baton *sb = baton;
60
/* If we know that the target was deleted in HEAD of the repository,
61
we need to note that fact in all the status structures that come
63
if (sb->deleted_in_repos)
64
status->repos_text_status = svn_wc_status_deleted;
66
/* Call the real status function/baton. */
67
sb->real_status_func (sb->real_status_baton, path, status);
70
/* A baton for our reporter that is used to collect locks. */
71
typedef struct report_baton_t {
72
const svn_ra_reporter2_t* wrapped_reporter;
73
void *wrapped_report_baton;
74
/* The common ancestor URL of all paths included in the report. */
76
void *set_locks_baton;
77
svn_client_ctx_t *ctx;
78
/* Pool to store locks in. */
82
/* Implements svn_ra_reporter2_t->set_path. */
84
reporter_set_path (void *report_baton, const char *path, svn_revnum_t revision,
85
svn_boolean_t start_empty, const char *lock_token,
88
report_baton_t *rb = report_baton;
90
return rb->wrapped_reporter->set_path (rb->wrapped_report_baton, path,
91
revision, start_empty, lock_token,
95
/* Implements svn_ra_reporter2_t->delete_path. */
97
reporter_delete_path (void *report_baton, const char *path, apr_pool_t *pool)
99
report_baton_t *rb = report_baton;
101
return rb->wrapped_reporter->delete_path (rb->wrapped_report_baton, path,
105
/* Implements svn_ra_reporter2_t->link_path. */
107
reporter_link_path (void *report_baton, const char *path, const char *url,
108
svn_revnum_t revision, svn_boolean_t start_empty,
109
const char *lock_token, apr_pool_t *pool)
111
report_baton_t *rb = report_baton;
112
const char *ancestor;
115
ancestor = svn_path_get_longest_ancestor (url, rb->ancestor, pool);
117
/* If we got a shorter ancestor, truncate our current ancestor.
118
Note that svn_path_get_longest_ancestor will allocate its return
119
value even if it identical to one of its arguments. */
120
len = strlen (ancestor);
121
if (len < strlen (rb->ancestor))
122
rb->ancestor[len] = '\0';
124
return rb->wrapped_reporter->link_path (rb->wrapped_report_baton, path, url,
125
revision, start_empty, lock_token,
129
/* Implements svn_ra_reporter2_t->finish_report. */
131
reporter_finish_report (void *report_baton, apr_pool_t *pool)
133
report_baton_t *rb = report_baton;
134
svn_ra_session_t *ras;
136
const char *repos_root;
137
apr_pool_t *subpool = svn_pool_create (pool);
138
svn_error_t *err = SVN_NO_ERROR;
140
/* Open an RA session to our common ancestor and grab the locks under it.
142
SVN_ERR (svn_client__open_ra_session (&ras, rb->ancestor, NULL, NULL, NULL,
143
FALSE, TRUE, rb->ctx, subpool));
145
/* The locks need to live throughout the edit. Note that if the
146
server doesn't support lock discovery, we'll just not do locky
148
err = svn_ra_get_locks (ras, &locks, "", rb->pool);
149
if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)
151
svn_error_clear (err);
153
locks = apr_hash_make (rb->pool);
157
SVN_ERR (svn_ra_get_repos_root (ras, &repos_root, subpool));
158
repos_root = apr_pstrdup (rb->pool, repos_root);
160
/* Close the RA session. */
161
svn_pool_destroy (subpool);
163
SVN_ERR (svn_wc_status_set_repos_locks (rb->set_locks_baton, locks,
164
repos_root, rb->pool));
166
return rb->wrapped_reporter->finish_report (rb->wrapped_report_baton, pool);
169
/* Implements svn_ra_reporter2_t->abort_report. */
171
reporter_abort_report (void *report_baton, apr_pool_t *pool)
173
report_baton_t *rb = report_baton;
175
return rb->wrapped_reporter->abort_report (rb->wrapped_report_baton, pool);
178
/* A reporter that keeps track of the common URL ancestor of all paths in
179
the WC and fetches repository locks for all paths under this ancestor. */
180
static svn_ra_reporter2_t lock_fetch_reporter = {
182
reporter_delete_path,
184
reporter_finish_report,
185
reporter_abort_report
189
/*** Public Interface. ***/
193
svn_client_status2 (svn_revnum_t *result_rev,
195
const svn_opt_revision_t *revision,
196
svn_wc_status_func2_t status_func,
198
svn_boolean_t recurse,
199
svn_boolean_t get_all,
200
svn_boolean_t update,
201
svn_boolean_t no_ignore,
202
svn_boolean_t ignore_externals,
203
svn_client_ctx_t *ctx,
206
svn_wc_adm_access_t *anchor_access, *target_access;
207
svn_wc_traversal_info_t *traversal_info = svn_wc_init_traversal_info (pool);
208
const char *anchor, *target;
209
const svn_delta_editor_t *editor;
210
void *edit_baton, *set_locks_baton;
211
const svn_wc_entry_t *entry;
212
struct status_baton sb;
213
svn_revnum_t edit_revision = SVN_INVALID_REVNUM;
215
sb.real_status_func = status_func;
216
sb.real_status_baton = status_baton;
217
sb.deleted_in_repos = FALSE;
219
SVN_ERR (svn_wc_adm_open_anchor (&anchor_access, &target_access, &target,
220
path, FALSE, recurse ? -1 : 1,
221
ctx->cancel_func, ctx->cancel_baton,
223
anchor = svn_wc_adm_access_path (anchor_access);
225
/* Get the status edit, and use our wrapping status function/baton
226
as the callback pair. */
227
SVN_ERR (svn_wc_get_status_editor2 (&editor, &edit_baton, &set_locks_baton,
228
&edit_revision, anchor_access, target,
229
ctx->config, recurse, get_all, no_ignore,
230
tweak_status, &sb, ctx->cancel_func,
231
ctx->cancel_baton, traversal_info,
234
/* If we want to know about out-of-dateness, we crawl the working copy and
235
let the RA layer drive the editor for real. Otherwise, we just close the
239
svn_ra_session_t *ra_session;
241
svn_node_kind_t kind;
243
/* Get full URL from the ANCHOR. */
244
SVN_ERR (svn_wc_entry (&entry, anchor, anchor_access, FALSE, pool));
246
return svn_error_createf
247
(SVN_ERR_UNVERSIONED_RESOURCE, NULL,
248
_("'%s' is not under version control"),
249
svn_path_local_style (anchor, pool));
251
return svn_error_createf
252
(SVN_ERR_ENTRY_MISSING_URL, NULL,
253
_("Entry '%s' has no URL"),
254
svn_path_local_style (anchor, pool));
255
URL = apr_pstrdup (pool, entry->url);
257
/* Open a repository session to the URL. */
258
SVN_ERR (svn_client__open_ra_session (&ra_session, URL, anchor,
259
anchor_access, NULL, TRUE, TRUE,
262
/* Verify that URL exists in HEAD. If it doesn't, this can save
263
us a whole lot of hassle; if it does, the cost of this
264
request should be minimal compared to the size of getting
265
back the average amount of "out-of-date" information. */
266
SVN_ERR (svn_ra_check_path (ra_session, "", SVN_INVALID_REVNUM,
268
if (kind == svn_node_none)
270
/* Our status target does not exist in HEAD of the
271
repository. If we're just adding this thing, that's
272
fine. But if it was previously versioned, then it must
273
have been deleted from the repository. */
274
if (entry->schedule != svn_wc_schedule_add)
275
sb.deleted_in_repos = TRUE;
277
/* And now close the edit. */
278
SVN_ERR (editor->close_edit (edit_baton, pool));
285
if (revision->kind == svn_opt_revision_head)
287
/* Cause the revision number to be omitted from the request,
288
which implies HEAD. */
289
revnum = SVN_INVALID_REVNUM;
293
/* Get a revision number for our status operation. */
294
SVN_ERR (svn_client__get_revision_number
295
(&revnum, ra_session, revision, target, pool));
298
/* Do the deed. Let the RA layer drive the status editor. */
299
SVN_ERR (svn_ra_do_status (ra_session, &rb.wrapped_reporter,
300
&rb.wrapped_report_baton,
301
target, revnum, recurse, editor,
304
/* Init the report baton. */
305
rb.ancestor = apr_pstrdup (pool, URL);
306
rb.set_locks_baton = set_locks_baton;
310
/* Drive the reporter structure, describing the revisions
311
within PATH. When we call reporter->finish_report,
312
EDITOR will be driven to describe differences between our
313
working copy and HEAD. */
314
SVN_ERR (svn_wc_crawl_revisions2 (path, target_access,
315
&lock_fetch_reporter, &rb, FALSE,
316
recurse, FALSE, NULL, NULL, NULL,
322
SVN_ERR (editor->close_edit (edit_baton, pool));
325
if (ctx->notify_func2 && update)
327
svn_wc_notify_t *notify
328
= svn_wc_create_notify (path, svn_wc_notify_status_completed, pool);
329
notify->revision = edit_revision;
330
(ctx->notify_func2) (ctx->notify_baton2, notify, pool);
333
/* If the caller wants the result revision, give it to them. */
335
*result_rev = edit_revision;
337
/* Close the access baton here, as svn_client__do_external_status()
338
calls back into this function and thus will be re-opening the
340
SVN_ERR (svn_wc_adm_close (anchor_access));
342
/* If there are svn:externals set, we don't want those to show up as
343
unversioned or unrecognized, so patch up the hash. If caller wants
344
all the statuses, we will change unversioned status items that
345
are interesting to an svn:externals property to
346
svn_wc_status_unversioned, otherwise we'll just remove the status
348
if (recurse && (! ignore_externals))
349
SVN_ERR (svn_client__do_external_status (traversal_info, status_func,
350
status_baton, get_all, update,
351
no_ignore, ctx, pool));
357
/* Helpers for deprecated svn_wc_status_editor(), of type
358
svn_wc_status_func2_t. */
359
struct old_status_func_cb_baton
361
svn_wc_status_func_t original_func;
362
void *original_baton;
365
static void old_status_func_cb (void *baton,
367
svn_wc_status2_t *status)
369
struct old_status_func_cb_baton *b = baton;
370
svn_wc_status_t *stat = (svn_wc_status_t *) status;
372
b->original_func (b->original_baton, path, stat);
377
svn_client_status (svn_revnum_t *result_rev,
379
svn_opt_revision_t *revision,
380
svn_wc_status_func_t status_func,
382
svn_boolean_t recurse,
383
svn_boolean_t get_all,
384
svn_boolean_t update,
385
svn_boolean_t no_ignore,
386
svn_client_ctx_t *ctx,
389
struct old_status_func_cb_baton *b = apr_pcalloc(pool, sizeof(*b));
390
b->original_func = status_func;
391
b->original_baton = status_baton;
393
return svn_client_status2 (result_rev, path, revision,
394
old_status_func_cb, b,
395
recurse, get_all, update, no_ignore, FALSE,