~svn/ubuntu/raring/subversion/ppa

« back to all changes in this revision

Viewing changes to subversion/libsvn_client/status.c

  • Committer: Bazaar Package Importer
  • Author(s): Adam Conrad
  • Date: 2005-12-05 01:26:14 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20051205012614-qom4xfypgtsqc2xq
Tags: 1.2.3dfsg1-3ubuntu1
Merge with the final Debian release of 1.2.3dfsg1-3, bringing in
fixes to the clean target, better documentation of the libdb4.3
upgrade and build fixes to work with swig1.3_1.3.27.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * status.c:  return the status of a working copy dirent
 
3
 *
 
4
 * ====================================================================
 
5
 * Copyright (c) 2000-2004 CollabNet.  All rights reserved.
 
6
 *
 
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.
 
12
 *
 
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
 * ====================================================================
 
17
 */
 
18
 
 
19
/* ==================================================================== */
 
20
 
 
21
 
 
22
 
 
23
/*** Includes. ***/
 
24
#include <assert.h>
 
25
#include <apr_strings.h>
 
26
#include <apr_pools.h>
 
27
 
 
28
#include "svn_pools.h"
 
29
#include "client.h"
 
30
 
 
31
#include "svn_wc.h"
 
32
#include "svn_path.h"
 
33
#include "svn_delta.h"
 
34
#include "svn_client.h"
 
35
#include "svn_error.h"
 
36
 
 
37
#include "svn_private_config.h"
 
38
 
 
39
 
 
40
/*** Getting update information ***/
 
41
 
 
42
struct status_baton
 
43
{
 
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 */
 
47
};
 
48
 
 
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).  */
 
53
static void
 
54
tweak_status (void *baton,
 
55
              const char *path,
 
56
              svn_wc_status2_t *status)
 
57
{
 
58
  struct status_baton *sb = baton;
 
59
 
 
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
 
62
     through here. */
 
63
  if (sb->deleted_in_repos)
 
64
    status->repos_text_status = svn_wc_status_deleted;
 
65
 
 
66
  /* Call the real status function/baton. */
 
67
  sb->real_status_func (sb->real_status_baton, path, status);
 
68
}
 
69
 
 
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. */
 
75
  char *ancestor;
 
76
  void *set_locks_baton;
 
77
  svn_client_ctx_t *ctx;
 
78
  /* Pool to store locks in. */
 
79
  apr_pool_t *pool;
 
80
} report_baton_t;
 
81
 
 
82
/* Implements svn_ra_reporter2_t->set_path. */
 
83
static svn_error_t *
 
84
reporter_set_path (void *report_baton, const char *path, svn_revnum_t revision,
 
85
                   svn_boolean_t start_empty, const char *lock_token,
 
86
                   apr_pool_t *pool)
 
87
{
 
88
  report_baton_t *rb = report_baton;
 
89
 
 
90
  return rb->wrapped_reporter->set_path (rb->wrapped_report_baton, path,
 
91
                                         revision, start_empty, lock_token,
 
92
                                         pool);
 
93
}
 
94
 
 
95
/* Implements svn_ra_reporter2_t->delete_path. */
 
96
static svn_error_t *
 
97
reporter_delete_path (void *report_baton, const char *path, apr_pool_t *pool)
 
98
{
 
99
  report_baton_t *rb = report_baton;
 
100
 
 
101
  return rb->wrapped_reporter->delete_path (rb->wrapped_report_baton, path,
 
102
                                            pool);
 
103
}
 
104
 
 
105
/* Implements svn_ra_reporter2_t->link_path. */
 
106
static svn_error_t *
 
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)
 
110
{
 
111
  report_baton_t *rb = report_baton;
 
112
  const char *ancestor;
 
113
  apr_size_t len;
 
114
 
 
115
  ancestor = svn_path_get_longest_ancestor (url, rb->ancestor, pool);
 
116
 
 
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';
 
123
 
 
124
  return rb->wrapped_reporter->link_path (rb->wrapped_report_baton, path, url,
 
125
                                          revision, start_empty, lock_token,
 
126
                                          pool);
 
127
}
 
128
 
 
129
/* Implements svn_ra_reporter2_t->finish_report. */
 
130
static svn_error_t *
 
131
reporter_finish_report (void *report_baton, apr_pool_t *pool)
 
132
{
 
133
  report_baton_t *rb = report_baton;
 
134
  svn_ra_session_t *ras;
 
135
  apr_hash_t *locks;
 
136
  const char *repos_root;
 
137
  apr_pool_t *subpool = svn_pool_create (pool);
 
138
  svn_error_t *err = SVN_NO_ERROR;
 
139
 
 
140
  /* Open an RA session to our common ancestor and grab the locks under it.
 
141
   */
 
142
  SVN_ERR (svn_client__open_ra_session (&ras, rb->ancestor, NULL, NULL, NULL,
 
143
                                        FALSE, TRUE, rb->ctx, subpool));
 
144
 
 
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
 
147
     stuff. */
 
148
  err = svn_ra_get_locks (ras, &locks, "", rb->pool);
 
149
  if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)
 
150
    {
 
151
      svn_error_clear (err);
 
152
      err = SVN_NO_ERROR;
 
153
      locks = apr_hash_make (rb->pool);
 
154
    }
 
155
  SVN_ERR (err);
 
156
 
 
157
  SVN_ERR (svn_ra_get_repos_root (ras, &repos_root, subpool));
 
158
  repos_root = apr_pstrdup (rb->pool, repos_root);
 
159
 
 
160
  /* Close the RA session. */
 
161
  svn_pool_destroy (subpool);
 
162
 
 
163
  SVN_ERR (svn_wc_status_set_repos_locks (rb->set_locks_baton, locks,
 
164
                                          repos_root, rb->pool));
 
165
 
 
166
  return rb->wrapped_reporter->finish_report (rb->wrapped_report_baton, pool);
 
167
}
 
168
 
 
169
/* Implements svn_ra_reporter2_t->abort_report. */
 
170
static svn_error_t *
 
171
reporter_abort_report (void *report_baton, apr_pool_t *pool)
 
172
{
 
173
  report_baton_t *rb = report_baton;
 
174
 
 
175
  return rb->wrapped_reporter->abort_report (rb->wrapped_report_baton, pool);
 
176
}
 
177
 
 
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 = {
 
181
  reporter_set_path,
 
182
  reporter_delete_path,
 
183
  reporter_link_path,
 
184
  reporter_finish_report,
 
185
  reporter_abort_report
 
186
};
 
187
 
 
188
 
 
189
/*** Public Interface. ***/
 
190
 
 
191
 
 
192
svn_error_t *
 
193
svn_client_status2 (svn_revnum_t *result_rev,
 
194
                    const char *path,
 
195
                    const svn_opt_revision_t *revision,
 
196
                    svn_wc_status_func2_t status_func,
 
197
                    void *status_baton,
 
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,
 
204
                    apr_pool_t *pool)
 
205
{
 
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;
 
214
 
 
215
  sb.real_status_func = status_func;
 
216
  sb.real_status_baton = status_baton;
 
217
  sb.deleted_in_repos = FALSE;
 
218
 
 
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,
 
222
                                   pool));
 
223
  anchor = svn_wc_adm_access_path (anchor_access);
 
224
 
 
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,
 
232
                                      pool));
 
233
 
 
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
 
236
     edit.  :-) */ 
 
237
  if (update)
 
238
    {
 
239
      svn_ra_session_t *ra_session;
 
240
      const char *URL;
 
241
      svn_node_kind_t kind;
 
242
 
 
243
      /* Get full URL from the ANCHOR. */
 
244
      SVN_ERR (svn_wc_entry (&entry, anchor, anchor_access, FALSE, pool));
 
245
      if (! entry)
 
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));
 
250
      if (! entry->url)
 
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);
 
256
 
 
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, 
 
260
                                            ctx, pool));
 
261
 
 
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,
 
267
                                  &kind, pool));
 
268
      if (kind == svn_node_none)
 
269
        {
 
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;
 
276
 
 
277
          /* And now close the edit. */
 
278
          SVN_ERR (editor->close_edit (edit_baton, pool));
 
279
        }
 
280
      else
 
281
        {
 
282
          svn_revnum_t revnum;
 
283
          report_baton_t rb;
 
284
            
 
285
          if (revision->kind == svn_opt_revision_head)
 
286
            {
 
287
              /* Cause the revision number to be omitted from the request,
 
288
                 which implies HEAD. */
 
289
              revnum = SVN_INVALID_REVNUM;
 
290
            }
 
291
          else
 
292
            {
 
293
              /* Get a revision number for our status operation. */
 
294
              SVN_ERR (svn_client__get_revision_number
 
295
                       (&revnum, ra_session, revision, target, pool));
 
296
            }
 
297
 
 
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, 
 
302
                                     edit_baton, pool));
 
303
 
 
304
          /* Init the report baton. */
 
305
          rb.ancestor = apr_pstrdup (pool, URL);
 
306
          rb.set_locks_baton = set_locks_baton;
 
307
          rb.ctx = ctx;
 
308
          rb.pool = pool;
 
309
          
 
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,
 
317
                                            pool));
 
318
        }
 
319
    }
 
320
  else
 
321
    {
 
322
      SVN_ERR (editor->close_edit (edit_baton, pool));
 
323
    }
 
324
 
 
325
  if (ctx->notify_func2 && update)
 
326
    {
 
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);
 
331
    }
 
332
 
 
333
  /* If the caller wants the result revision, give it to them. */
 
334
  if (result_rev)
 
335
    *result_rev = edit_revision;
 
336
 
 
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
 
339
     working copy. */
 
340
  SVN_ERR (svn_wc_adm_close (anchor_access));
 
341
 
 
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
 
347
     item altogether. */
 
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));
 
352
 
 
353
  return SVN_NO_ERROR;
 
354
}
 
355
 
 
356
 
 
357
/* Helpers for deprecated svn_wc_status_editor(), of type
 
358
   svn_wc_status_func2_t. */
 
359
struct old_status_func_cb_baton
 
360
{
 
361
  svn_wc_status_func_t original_func;
 
362
  void *original_baton;
 
363
};
 
364
 
 
365
static void old_status_func_cb (void *baton,
 
366
                                const char *path,
 
367
                                svn_wc_status2_t *status)
 
368
{
 
369
  struct old_status_func_cb_baton *b = baton;
 
370
  svn_wc_status_t *stat = (svn_wc_status_t *) status;
 
371
  
 
372
  b->original_func (b->original_baton, path, stat);
 
373
}
 
374
 
 
375
 
 
376
svn_error_t *
 
377
svn_client_status (svn_revnum_t *result_rev,
 
378
                   const char *path,
 
379
                   svn_opt_revision_t *revision,
 
380
                   svn_wc_status_func_t status_func,
 
381
                   void *status_baton,
 
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,
 
387
                   apr_pool_t *pool)
 
388
{
 
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;
 
392
 
 
393
  return svn_client_status2 (result_rev, path, revision, 
 
394
                             old_status_func_cb, b,
 
395
                             recurse, get_all, update, no_ignore, FALSE,
 
396
                             ctx, pool);
 
397
}
 
398