2 * status.c: construct a status structure from an entry structure
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 * ====================================================================
23 #include <apr_pools.h>
24 #include <apr_file_io.h>
26 #include "svn_pools.h"
27 #include "svn_types.h"
28 #include "svn_delta.h"
29 #include "svn_string.h"
30 #include "svn_error.h"
33 #include "svn_config.h"
35 #include "svn_private_config.h"
40 #include "translate.h"
42 #include "private/svn_wc_private.h"
45 /*** Editor batons ***/
49 /* For status, the "destination" of the edit. */
52 svn_wc_adm_access_t
*adm_access
;
54 /* The overall depth of this edit (a dir baton may override this).
56 * If this is svn_depth_unknown, the depths found in the working
57 * copy will govern the edit; or if the edit depth indicates a
58 * descent deeper than the found depths are capable of, the found
59 * depths also govern, of course (there's no point descending into
60 * something that's not there).
62 svn_depth_t default_depth
;
64 /* Do we want all statuses (instead of just the interesting ones) ? */
65 svn_boolean_t get_all
;
67 /* Ignore the svn:ignores. */
68 svn_boolean_t no_ignore
;
70 /* The comparison revision in the repository. This is a reference
71 because this editor returns this rev to the driver directly, as
72 well as in each statushash entry. */
73 svn_revnum_t
*target_revision
;
75 /* Status function/baton. */
76 svn_wc_status_func2_t status_func
;
79 /* Cancellation function/baton. */
80 svn_cancel_func_t cancel_func
;
83 /* The configured set of default ignores. */
84 apr_array_header_t
*ignores
;
86 /* Externals info harvested during the status run. */
87 svn_wc_traversal_info_t
*traversal_info
;
88 apr_hash_t
*externals
;
90 /* Status item for the path represented by the anchor of the edit. */
91 svn_wc_status2_t
*anchor_status
;
93 /* Was open_root() called for this edit drive? */
94 svn_boolean_t root_opened
;
96 /* The repository root URL, if set. */
97 const char *repos_root
;
99 /* Repository locks, if set. */
100 apr_hash_t
*repos_locks
;
106 /* The path to this directory. */
109 /* Basename of this directory. */
112 /* The global edit baton. */
113 struct edit_baton
*edit_baton
;
115 /* Baton for this directory's parent, or NULL if this is the root
117 struct dir_baton
*parent_baton
;
119 /* The ambient requested depth below this point in the edit. This
120 can differ from the parent baton's depth (with the edit baton
121 considered the ultimate parent baton). For example, if the
122 parent baton has svn_depth_immediates, then here we should have
123 svn_depth_empty, because there would be no further recursion, not
124 even to file children. */
127 /* Is this directory filtered out due to depth? (Note that if this
128 is TRUE, the depth field is undefined.) */
129 svn_boolean_t excluded
;
131 /* 'svn status' shouldn't print status lines for things that are
132 added; we're only interest in asking if objects that the user
133 *already* has are up-to-date or not. Thus if this flag is set,
134 the next two will be ignored. :-) */
137 /* Gets set iff there's a change to this directory's properties, to
138 guide us when syncing adm files later. */
139 svn_boolean_t prop_changed
;
141 /* This means (in terms of 'svn status') that some child was deleted
142 or added to the directory */
143 svn_boolean_t text_changed
;
145 /* Working copy status structures for children of this directory.
146 This hash maps const char * paths (relative to the root of the
147 edit) to svn_wc_status2_t * status items. */
150 /* The pool in which this baton itself is allocated. */
153 /* The URI to this item in the repository. */
156 /* out-of-date info corresponding to ood_* fields in svn_wc_status2_t. */
157 svn_revnum_t ood_last_cmt_rev
;
158 apr_time_t ood_last_cmt_date
;
159 svn_node_kind_t ood_kind
;
160 const char *ood_last_cmt_author
;
166 /* The global edit baton. */
167 struct edit_baton
*edit_baton
;
169 /* Baton for this file's parent directory. */
170 struct dir_baton
*dir_baton
;
172 /* Pool specific to this file_baton. */
175 /* Name of this file (its entry in the directory). */
178 /* Path to this file, either abs or relative to the change-root. */
181 /* 'svn status' shouldn't print status lines for things that are
182 added; we're only interest in asking if objects that the user
183 *already* has are up-to-date or not. Thus if this flag is set,
184 the next two will be ignored. :-) */
187 /* This gets set if the file underwent a text change, which guides
188 the code that syncs up the adm dir and working copy. */
189 svn_boolean_t text_changed
;
191 /* This gets set if the file underwent a prop change, which guides
192 the code that syncs up the adm dir and working copy. */
193 svn_boolean_t prop_changed
;
195 /* The URI to this item in the repository. */
198 /* out-of-date info corresponding to ood_* fields in svn_wc_status2_t. */
199 svn_revnum_t ood_last_cmt_rev
;
200 apr_time_t ood_last_cmt_date
;
201 svn_node_kind_t ood_kind
;
202 const char *ood_last_cmt_author
;
208 /* Fill in *STATUS for PATH, whose entry data is in ENTRY. Allocate
211 ENTRY may be null, for non-versioned entities. In this case, we
212 will assemble a special status structure item which implies a
215 PARENT_ENTRY is the entry for the parent directory of PATH, it may be
216 NULL if ENTRY is NULL or if PATH is a working copy root. The lifetime
217 of PARENT_ENTRY's pool is not important.
219 PATH_KIND is the node kind of PATH as determined by the caller.
220 NOTE: this may be svn_node_unknown if the caller has made no such
223 If PATH_KIND is not svn_node_unknown, PATH_SPECIAL indicates whether
224 the entry is a special file.
226 If GET_ALL is zero, and ENTRY is not locally modified, then *STATUS
227 will be set to NULL. If GET_ALL is non-zero, then *STATUS will be
228 allocated and returned no matter what.
230 If IS_IGNORED is non-zero and this is a non-versioned entity, set
231 the text_status to svn_wc_status_none. Otherwise set the
232 text_status to svn_wc_status_unversioned.
234 If non-NULL, look up a repository lock in REPOS_LOCKS and set the repos_lock
235 field of the status struct to that lock if it exists. If REPOS_LOCKS is
236 non-NULL, REPOS_ROOT must contain the repository root URL of the entry.
239 assemble_status(svn_wc_status2_t
**status
,
241 svn_wc_adm_access_t
*adm_access
,
242 const svn_wc_entry_t
*entry
,
243 const svn_wc_entry_t
*parent_entry
,
244 svn_node_kind_t path_kind
, svn_boolean_t path_special
,
245 svn_boolean_t get_all
,
246 svn_boolean_t is_ignored
,
247 apr_hash_t
*repos_locks
,
248 const char *repos_root
,
251 svn_wc_status2_t
*stat
;
252 svn_boolean_t has_props
;
253 svn_boolean_t text_modified_p
= FALSE
;
254 svn_boolean_t prop_modified_p
= FALSE
;
255 svn_boolean_t locked_p
= FALSE
;
256 svn_boolean_t switched_p
= FALSE
;
258 svn_boolean_t wc_special
;
259 #endif /* HAVE_SYMLINK */
261 /* Defaults for two main variables. */
262 enum svn_wc_status_kind final_text_status
= svn_wc_status_normal
;
263 enum svn_wc_status_kind final_prop_status
= svn_wc_status_none
;
265 svn_lock_t
*repos_lock
= NULL
;
267 /* Check for a repository lock. */
270 const char *abs_path
;
272 if (entry
&& entry
->url
)
273 abs_path
= entry
->url
+ strlen(repos_root
);
274 else if (parent_entry
&& parent_entry
->url
)
275 abs_path
= svn_path_join(parent_entry
->url
+ strlen(repos_root
),
276 svn_path_basename(path
, pool
), pool
);
281 repos_lock
= apr_hash_get(repos_locks
,
282 svn_path_uri_decode(abs_path
, pool
),
283 APR_HASH_KEY_STRING
);
286 /* Check the path kind for PATH. */
287 if (path_kind
== svn_node_unknown
)
288 SVN_ERR(svn_io_check_special_path(path
, &path_kind
, &path_special
,
293 /* return a blank structure. */
294 stat
= apr_pcalloc(pool
, sizeof(*stat
));
296 stat
->text_status
= svn_wc_status_none
;
297 stat
->prop_status
= svn_wc_status_none
;
298 stat
->repos_text_status
= svn_wc_status_none
;
299 stat
->repos_prop_status
= svn_wc_status_none
;
300 stat
->locked
= FALSE
;
301 stat
->copied
= FALSE
;
302 stat
->switched
= FALSE
;
304 /* If this path has no entry, but IS present on disk, it's
305 unversioned. If this file is being explicitly ignored (due
306 to matching an ignore-pattern), the text_status is set to
307 svn_wc_status_ignored. Otherwise the text_status is set to
308 svn_wc_status_unversioned. */
309 if (path_kind
!= svn_node_none
)
312 stat
->text_status
= svn_wc_status_ignored
;
314 stat
->text_status
= svn_wc_status_unversioned
;
317 stat
->repos_lock
= repos_lock
;
319 stat
->ood_last_cmt_rev
= SVN_INVALID_REVNUM
;
320 stat
->ood_last_cmt_date
= 0;
321 stat
->ood_kind
= svn_node_none
;
322 stat
->ood_last_cmt_author
= NULL
;
328 /* Someone either deleted the administrative directory in the versioned
329 subdir, or deleted the directory altogether and created a new one.
330 In any case, what is currently there is in the way.
332 if (entry
->kind
== svn_node_dir
)
334 if (path_kind
== svn_node_dir
)
336 if (svn_wc__adm_missing(adm_access
, path
))
337 final_text_status
= svn_wc_status_obstructed
;
339 else if (path_kind
!= svn_node_none
)
340 final_text_status
= svn_wc_status_obstructed
;
343 /* Is this item switched? Well, to be switched it must have both an URL
344 and a parent with an URL, at the very least.
345 If this is the root folder on the (virtual) disk, entry and parent_entry
347 if (entry
->url
&& parent_entry
&& parent_entry
->url
&&
348 entry
!= parent_entry
)
350 /* An item is switched if its working copy basename differs from the
351 basename of its URL. */
352 if (strcmp(svn_path_uri_encode(svn_path_basename(path
, pool
), pool
),
353 svn_path_basename(entry
->url
, pool
)))
356 /* An item is switched if its URL, without the basename, does not
357 equal its parent's URL. */
359 && strcmp(svn_path_dirname(entry
->url
, pool
),
364 if (final_text_status
!= svn_wc_status_obstructed
)
366 /* Implement predecence rules: */
368 /* 1. Set the two main variables to "discovered" values first (M, C).
369 Together, these two stati are of lowest precedence, and C has
370 precedence over M. */
372 /* Does the entry have props? */
373 SVN_ERR(svn_wc__has_props(&has_props
, path
, adm_access
, pool
));
375 final_prop_status
= svn_wc_status_normal
;
377 /* If the entry has a property file, see if it has local changes. */
378 SVN_ERR(svn_wc_props_modified_p(&prop_modified_p
, path
, adm_access
,
383 SVN_ERR(svn_wc__get_special(&wc_special
, path
, adm_access
, pool
));
386 #endif /* HAVE_SYMLINK */
388 /* If the entry is a file, check for textual modifications */
389 if ((entry
->kind
== svn_node_file
)
391 && (wc_special
== path_special
)
392 #endif /* HAVE_SYMLINK */
394 SVN_ERR(svn_wc_text_modified_p(&text_modified_p
, path
, FALSE
,
398 final_text_status
= svn_wc_status_modified
;
401 final_prop_status
= svn_wc_status_modified
;
403 if (entry
->prejfile
|| entry
->conflict_old
||
404 entry
->conflict_new
|| entry
->conflict_wrk
)
406 svn_boolean_t text_conflict_p
, prop_conflict_p
;
407 const char *parent_dir
;
409 if (entry
->kind
== svn_node_dir
)
411 else /* non-directory, that's all we need to know */
412 parent_dir
= svn_path_dirname(path
, pool
);
414 SVN_ERR(svn_wc_conflicted_p(&text_conflict_p
, &prop_conflict_p
,
415 parent_dir
, entry
, pool
));
418 final_text_status
= svn_wc_status_conflicted
;
420 final_prop_status
= svn_wc_status_conflicted
;
423 /* 2. Possibly overwrite the text_status variable with "scheduled"
424 states from the entry (A, D, R). As a group, these states are
425 of medium precedence. They also override any C or M that may
426 be in the prop_status field at this point, although they do not
427 override a C text status.*/
429 if (entry
->schedule
== svn_wc_schedule_add
430 && final_text_status
!= svn_wc_status_conflicted
)
432 final_text_status
= svn_wc_status_added
;
433 final_prop_status
= svn_wc_status_none
;
436 else if (entry
->schedule
== svn_wc_schedule_replace
437 && final_text_status
!= svn_wc_status_conflicted
)
439 final_text_status
= svn_wc_status_replaced
;
440 final_prop_status
= svn_wc_status_none
;
443 else if (entry
->schedule
== svn_wc_schedule_delete
444 && final_text_status
!= svn_wc_status_conflicted
)
446 final_text_status
= svn_wc_status_deleted
;
447 final_prop_status
= svn_wc_status_none
;
451 /* 3. Highest precedence:
453 a. check to see if file or dir is just missing, or
454 incomplete. This overrides every possible state
455 *except* deletion. (If something is deleted or
456 scheduled for it, we don't care if the working file
459 b. check to see if the file or dir is present in the
460 file system as the same kind it was versioned as.
462 4. Check for locked directory (only for directories). */
464 if (entry
->incomplete
465 && (final_text_status
!= svn_wc_status_deleted
)
466 && (final_text_status
!= svn_wc_status_added
))
468 final_text_status
= svn_wc_status_incomplete
;
470 else if (path_kind
== svn_node_none
)
472 if (final_text_status
!= svn_wc_status_deleted
)
473 final_text_status
= svn_wc_status_missing
;
475 else if (path_kind
!= entry
->kind
)
476 final_text_status
= svn_wc_status_obstructed
;
478 else if (((! wc_special
) && (path_special
))
479 || (wc_special
&& (! path_special
))
481 final_text_status
= svn_wc_status_obstructed
;
482 #endif /* HAVE_SYMLINK */
484 if (path_kind
== svn_node_dir
&& entry
->kind
== svn_node_dir
)
485 SVN_ERR(svn_wc_locked(&locked_p
, path
, pool
));
488 /* 5. Easy out: unless we're fetching -every- entry, don't bother
489 to allocate a struct for an uninteresting entry. */
492 if (((final_text_status
== svn_wc_status_none
)
493 || (final_text_status
== svn_wc_status_normal
))
494 && ((final_prop_status
== svn_wc_status_none
)
495 || (final_prop_status
== svn_wc_status_normal
))
496 && (! locked_p
) && (! switched_p
) && (! entry
->lock_token
)
497 && (! repos_lock
) && (! entry
->changelist
))
504 /* 6. Build and return a status structure. */
506 stat
= apr_pcalloc(pool
, sizeof(**status
));
507 stat
->entry
= svn_wc_entry_dup(entry
, pool
);
508 stat
->text_status
= final_text_status
;
509 stat
->prop_status
= final_prop_status
;
510 stat
->repos_text_status
= svn_wc_status_none
; /* default */
511 stat
->repos_prop_status
= svn_wc_status_none
; /* default */
512 stat
->locked
= locked_p
;
513 stat
->switched
= switched_p
;
514 stat
->copied
= entry
->copied
;
515 stat
->repos_lock
= repos_lock
;
516 stat
->url
= (entry
->url
? entry
->url
: NULL
);
517 stat
->ood_last_cmt_rev
= SVN_INVALID_REVNUM
;
518 stat
->ood_last_cmt_date
= 0;
519 stat
->ood_kind
= svn_node_none
;
520 stat
->ood_last_cmt_author
= NULL
;
530 /* Given an ENTRY object representing PATH, build a status structure
531 and pass it off to the STATUS_FUNC/STATUS_BATON. All other
532 arguments are the same as those passed to assemble_status(). */
534 send_status_structure(const char *path
,
535 svn_wc_adm_access_t
*adm_access
,
536 const svn_wc_entry_t
*entry
,
537 const svn_wc_entry_t
*parent_entry
,
538 svn_node_kind_t path_kind
,
539 svn_boolean_t path_special
,
540 svn_boolean_t get_all
,
541 svn_boolean_t is_ignored
,
542 apr_hash_t
*repos_locks
,
543 const char *repos_root
,
544 svn_wc_status_func2_t status_func
,
548 svn_wc_status2_t
*statstruct
;
550 SVN_ERR(assemble_status(&statstruct
, path
, adm_access
, entry
, parent_entry
,
551 path_kind
, path_special
, get_all
, is_ignored
,
552 repos_locks
, repos_root
, pool
));
553 if (statstruct
&& (status_func
))
554 (*status_func
)(status_baton
, path
, statstruct
);
560 /* Store in PATTERNS a list of all svn:ignore properties from
561 the working copy directory, including the default ignores
562 passed in as IGNORES.
564 Upon return, *PATTERNS will contain zero or more (const char *)
565 patterns from the value of the SVN_PROP_IGNORE property set on
566 the working directory path.
568 IGNORES is a list of patterns to include; typically this will
569 be the default ignores as, for example, specified in a config file.
571 ADM_ACCESS is an access baton for the working copy path.
573 Allocate everything in POOL.
575 None of the arguments may be NULL.
578 collect_ignore_patterns(apr_array_header_t
**patterns
,
579 apr_array_header_t
*ignores
,
580 svn_wc_adm_access_t
*adm_access
,
584 const svn_string_t
*value
;
586 *patterns
= apr_array_make(pool
, 1, sizeof(const char *));
588 /* Copy default ignores into the local PATTERNS array. */
589 for (i
= 0; i
< ignores
->nelts
; i
++)
591 const char *ignore
= APR_ARRAY_IDX(ignores
, i
, const char *);
592 APR_ARRAY_PUSH(*patterns
, const char *) = ignore
;
595 /* Then add any svn:ignore globs to the PATTERNS array. */
596 SVN_ERR(svn_wc_prop_get(&value
, SVN_PROP_IGNORE
,
597 svn_wc_adm_access_path(adm_access
), adm_access
,
600 svn_cstring_split_append(*patterns
, value
->data
, "\n\r", FALSE
, pool
);
606 /* Compare PATH with items in the EXTERNALS hash to see if PATH is the
607 drop location for, or an intermediate directory of the drop
608 location for, an externals definition. Use POOL for
611 is_external_path(apr_hash_t
*externals
,
615 apr_hash_index_t
*hi
;
617 /* First try: does the path exist as a key in the hash? */
618 if (apr_hash_get(externals
, path
, APR_HASH_KEY_STRING
))
621 /* Failing that, we need to check if any external is a child of
623 for (hi
= apr_hash_first(pool
, externals
); hi
; hi
= apr_hash_next(hi
))
626 apr_hash_this(hi
, &key
, NULL
, NULL
);
627 if (svn_path_is_child(path
, key
, pool
))
635 /* Assuming that NAME is unversioned, send a status structure
636 for it through STATUS_FUNC/STATUS_BATON unless this path is being
637 ignored. This function should never be called on a versioned entry.
639 NAME is the basename of the unversioned file whose status is being
640 requested. PATH_KIND is the node kind of NAME as determined by the
641 caller. PATH_SPECIAL is the special status of the path, also determined
642 by the caller. ADM_ACCESS is an access baton for the working copy path.
643 PATTERNS points to a list of filename patterns which are marked as
644 ignored. None of these parameter may be NULL. EXTERNALS is a hash
645 of known externals definitions for this status run.
647 If NO_IGNORE is non-zero, the item will be added regardless of
648 whether it is ignored; otherwise we will only add the item if it
649 does not match any of the patterns in PATTERNS.
651 Allocate everything in POOL.
654 send_unversioned_item(const char *name
,
655 svn_node_kind_t path_kind
, svn_boolean_t path_special
,
656 svn_wc_adm_access_t
*adm_access
,
657 apr_array_header_t
*patterns
,
658 apr_hash_t
*externals
,
659 svn_boolean_t no_ignore
,
660 apr_hash_t
*repos_locks
,
661 const char *repos_root
,
662 svn_wc_status_func2_t status_func
,
666 int ignore_me
= svn_wc_match_ignore_list(name
, patterns
, pool
);
667 const char *path
= svn_path_join(svn_wc_adm_access_path(adm_access
),
669 int is_external
= is_external_path(externals
, path
, pool
);
670 svn_wc_status2_t
*status
;
672 SVN_ERR(assemble_status(&status
, path
, adm_access
, NULL
, NULL
,
673 path_kind
, path_special
, FALSE
, ignore_me
,
674 repos_locks
, repos_root
, pool
));
677 status
->text_status
= svn_wc_status_external
;
679 /* If we aren't ignoring it, or if it's an externals path, or it has a lock
680 in the repository, pass this entry to the status func. */
681 if (no_ignore
|| (! ignore_me
) || is_external
|| status
->repos_lock
)
682 (status_func
)(status_baton
, path
, status
);
688 /* Prototype for untangling a tango-ing two-some. */
689 static svn_error_t
*get_dir_status(struct edit_baton
*eb
,
690 const svn_wc_entry_t
*parent_entry
,
691 svn_wc_adm_access_t
*adm_access
,
693 apr_array_header_t
*ignores
,
695 svn_boolean_t get_all
,
696 svn_boolean_t no_ignore
,
697 svn_boolean_t skip_this_dir
,
698 svn_wc_status_func2_t status_func
,
700 svn_cancel_func_t cancel_func
,
704 /* Handle NAME (whose entry is ENTRY) as a directory entry of the
705 directory represented by ADM_ACCESS (and whose entry is
706 DIR_ENTRY). All other arguments are the same as those passed to
707 get_dir_status(), the function for which this one is a helper. */
709 handle_dir_entry(struct edit_baton
*eb
,
710 svn_wc_adm_access_t
*adm_access
,
712 const svn_wc_entry_t
*dir_entry
,
713 const svn_wc_entry_t
*entry
,
714 svn_node_kind_t kind
,
715 svn_boolean_t special
,
716 apr_array_header_t
*ignores
,
718 svn_boolean_t get_all
,
719 svn_boolean_t no_ignore
,
720 svn_wc_status_func2_t status_func
,
722 svn_cancel_func_t cancel_func
,
726 const char *dirname
= svn_wc_adm_access_path(adm_access
);
727 const char *path
= svn_path_join(dirname
, name
, pool
);
729 if (kind
== svn_node_dir
)
731 /* Directory entries are incomplete. We must get their full
732 entry from their own THIS_DIR entry. svn_wc_entry does this
735 Of course, if there has been a kind-changing replacement (for
736 example, there is an entry for a file 'foo', but 'foo' exists
737 as a *directory* on disk), we don't want to reach down into
738 that subdir to try to flesh out a "complete entry". */
739 const svn_wc_entry_t
*full_entry
= entry
;
741 if (entry
->kind
== kind
)
742 SVN_ERR(svn_wc__entry_versioned(&full_entry
, path
, adm_access
, FALSE
,
745 /* Descend only if the subdirectory is a working copy directory
746 (and DEPTH permits it, of course) */
747 if (full_entry
!= entry
748 && (depth
== svn_depth_unknown
749 || depth
== svn_depth_immediates
750 || depth
== svn_depth_infinity
))
752 svn_wc_adm_access_t
*dir_access
;
753 SVN_ERR(svn_wc_adm_retrieve(&dir_access
, adm_access
, path
, pool
));
754 SVN_ERR(get_dir_status(eb
, dir_entry
, dir_access
, NULL
, ignores
,
755 depth
, get_all
, no_ignore
, FALSE
,
756 status_func
, status_baton
, cancel_func
,
757 cancel_baton
, pool
));
761 SVN_ERR(send_status_structure(path
, adm_access
, full_entry
,
762 dir_entry
, kind
, special
, get_all
,
763 FALSE
, eb
->repos_locks
,
765 status_func
, status_baton
, pool
));
770 /* File entries are ... just fine! */
771 SVN_ERR(send_status_structure(path
, adm_access
, entry
, dir_entry
,
772 kind
, special
, get_all
, FALSE
,
773 eb
->repos_locks
, eb
->repos_root
,
774 status_func
, status_baton
, pool
));
780 /* Send svn_wc_status2_t * structures for the directory ADM_ACCESS and
781 for all its entries through STATUS_FUNC/STATUS_BATON, or, if ENTRY
782 is non-NULL, only for that directory entry.
784 PARENT_ENTRY is the entry for the parent of the directory or NULL
785 if that directory is a working copy root.
787 If SKIP_THIS_DIR is TRUE (and ENTRY is NULL), the directory's own
788 status will not be reported. However, upon recursing, all subdirs
789 *will* be reported, regardless of this parameter's value.
791 Other arguments are the same as those passed to
792 svn_wc_get_status_editor3(). */
794 get_dir_status(struct edit_baton
*eb
,
795 const svn_wc_entry_t
*parent_entry
,
796 svn_wc_adm_access_t
*adm_access
,
798 apr_array_header_t
*ignore_patterns
,
800 svn_boolean_t get_all
,
801 svn_boolean_t no_ignore
,
802 svn_boolean_t skip_this_dir
,
803 svn_wc_status_func2_t status_func
,
805 svn_cancel_func_t cancel_func
,
810 apr_hash_index_t
*hi
;
811 const svn_wc_entry_t
*dir_entry
;
812 const char *path
= svn_wc_adm_access_path(adm_access
);
814 apr_array_header_t
*patterns
= NULL
;
815 apr_pool_t
*iterpool
, *subpool
= svn_pool_create(pool
);
817 /* See if someone wants to cancel this operation. */
819 SVN_ERR(cancel_func(cancel_baton
));
821 if (depth
== svn_depth_unknown
)
822 depth
= svn_depth_infinity
;
824 /* Load entries file for the directory into the requested pool. */
825 SVN_ERR(svn_wc_entries_read(&entries
, adm_access
, FALSE
, subpool
));
827 /* Read PATH's dirents. */
828 SVN_ERR(svn_io_get_dirents2(&dirents
, path
, subpool
));
830 /* Get this directory's entry. */
831 SVN_ERR(svn_wc_entry(&dir_entry
, path
, adm_access
, FALSE
, subpool
));
833 /* If "this dir" has "svn:externals" property set on it, store the
834 name and value in traversal_info, along with this directory's depth.
835 (Also, we want to track the externals internally so we can report
836 status more accurately.) */
838 const svn_string_t
*prop_val
;
839 SVN_ERR(svn_wc_prop_get(&prop_val
, SVN_PROP_EXTERNALS
, path
,
840 adm_access
, subpool
));
843 apr_array_header_t
*ext_items
;
846 if (eb
->traversal_info
)
848 apr_pool_t
*dup_pool
= eb
->traversal_info
->pool
;
849 const char *dup_path
= apr_pstrdup(dup_pool
, path
);
850 const char *dup_val
= apr_pstrmemdup(dup_pool
, prop_val
->data
,
853 /* First things first -- we put the externals information
854 into the "global" traversal info structure. */
855 apr_hash_set(eb
->traversal_info
->externals_old
,
856 dup_path
, APR_HASH_KEY_STRING
, dup_val
);
857 apr_hash_set(eb
->traversal_info
->externals_new
,
858 dup_path
, APR_HASH_KEY_STRING
, dup_val
);
859 apr_hash_set(eb
->traversal_info
->depths
,
860 dup_path
, APR_HASH_KEY_STRING
,
861 svn_depth_to_word(dir_entry
->depth
));
864 /* Now, parse the thing, and copy the parsed results into
865 our "global" externals hash. */
866 SVN_ERR(svn_wc_parse_externals_description3(&ext_items
, path
,
867 prop_val
->data
, FALSE
,
869 for (i
= 0; ext_items
&& i
< ext_items
->nelts
; i
++)
871 svn_wc_external_item2_t
*item
;
873 item
= APR_ARRAY_IDX(ext_items
, i
, svn_wc_external_item2_t
*);
874 apr_hash_set(eb
->externals
, svn_path_join(path
,
877 APR_HASH_KEY_STRING
, item
);
882 /* Early out -- our caller only cares about a single ENTRY in this
886 const svn_wc_entry_t
*entry_entry
;
887 svn_io_dirent_t
* dirent_p
= apr_hash_get(dirents
, entry
,
888 APR_HASH_KEY_STRING
);
889 entry_entry
= apr_hash_get(entries
, entry
, APR_HASH_KEY_STRING
);
891 /* If ENTRY is versioned, send its versioned status. */
894 SVN_ERR(handle_dir_entry(eb
, adm_access
, entry
, dir_entry
,
896 dirent_p
? dirent_p
->kind
: svn_node_none
,
897 dirent_p
? dirent_p
->special
: FALSE
,
898 ignore_patterns
, depth
, get_all
,
899 no_ignore
, status_func
, status_baton
,
900 cancel_func
, cancel_baton
, subpool
));
902 /* Otherwise, if it exists, send its unversioned status. */
905 if (ignore_patterns
&& ! patterns
)
906 SVN_ERR(collect_ignore_patterns(&patterns
, ignore_patterns
,
907 adm_access
, subpool
));
908 SVN_ERR(send_unversioned_item(entry
, dirent_p
->kind
,
909 dirent_p
->special
, adm_access
,
910 patterns
, eb
->externals
, no_ignore
,
911 eb
->repos_locks
, eb
->repos_root
,
912 status_func
, status_baton
, subpool
));
915 /* Regardless, we're done here. Let's go home. */
919 /** If we get here, ENTRY is NULL and we are handling all the
920 directory entries (depending on specified depth). */
922 /* Handle "this-dir" first. */
924 SVN_ERR(send_status_structure(path
, adm_access
, dir_entry
,
925 parent_entry
, svn_node_dir
, FALSE
,
926 get_all
, FALSE
, eb
->repos_locks
,
927 eb
->repos_root
, status_func
, status_baton
,
930 /* If the requested depth is empty, we only need status on this-dir. */
931 if (depth
== svn_depth_empty
)
934 /* Make our iteration pool. */
935 iterpool
= svn_pool_create(subpool
);
937 /* Add empty status structures for each of the unversioned things.
938 This also catches externals; not sure whether that's good or bad,
939 but it's what's happening right now. */
940 for (hi
= apr_hash_first(subpool
, dirents
); hi
; hi
= apr_hash_next(hi
))
945 svn_io_dirent_t
*dirent_p
;
947 svn_pool_clear(iterpool
);
949 apr_hash_this(hi
, &key
, &klen
, &val
);
951 /* Skip versioned, non-external things, and skip the
952 administrative directory. */
953 if (apr_hash_get(entries
, key
, klen
)
954 || svn_wc_is_adm_dir(key
, iterpool
))
959 if (depth
== svn_depth_files
&& dirent_p
->kind
== svn_node_dir
)
962 if (ignore_patterns
&& ! patterns
)
963 SVN_ERR(collect_ignore_patterns(&patterns
, ignore_patterns
,
964 adm_access
, subpool
));
966 SVN_ERR(send_unversioned_item(key
, dirent_p
->kind
, dirent_p
->special
,
968 patterns
, eb
->externals
, no_ignore
,
969 eb
->repos_locks
, eb
->repos_root
,
970 status_func
, status_baton
, iterpool
));
973 /* Loop over entries hash */
974 for (hi
= apr_hash_first(pool
, entries
); hi
; hi
= apr_hash_next(hi
))
978 svn_io_dirent_t
*dirent_p
;
980 /* Get the next entry */
981 apr_hash_this(hi
, &key
, NULL
, &val
);
983 dirent_p
= apr_hash_get(dirents
, key
, APR_HASH_KEY_STRING
);
985 /* ### todo: What if the subdir is from another repository? */
987 /* Skip "this-dir". */
988 if (strcmp(key
, SVN_WC_ENTRY_THIS_DIR
) == 0)
991 /* Skip directories if user is only interested in files */
992 if (depth
== svn_depth_files
993 && dirent_p
&& dirent_p
->kind
== svn_node_dir
)
996 /* Clear the iteration subpool. */
997 svn_pool_clear(iterpool
);
999 /* Handle this directory entry (possibly recursing). */
1000 SVN_ERR(handle_dir_entry(eb
, adm_access
, key
, dir_entry
, val
,
1001 dirent_p
? dirent_p
->kind
: svn_node_none
,
1002 dirent_p
? dirent_p
->special
: FALSE
,
1004 depth
== svn_depth_infinity
? depth
1007 status_func
, status_baton
, cancel_func
,
1008 cancel_baton
, iterpool
));
1011 /* Destroy our subpools. */
1012 svn_pool_destroy(subpool
);
1014 return SVN_NO_ERROR
;
1021 /* A faux status callback function for stashing STATUS item in an hash
1022 (which is the BATON), keyed on PATH. This implements the
1023 svn_wc_status_func2_t interface. */
1025 hash_stash(void *baton
,
1027 svn_wc_status2_t
*status
)
1029 apr_hash_t
*stat_hash
= baton
;
1030 apr_pool_t
*hash_pool
= apr_hash_pool_get(stat_hash
);
1031 assert(! apr_hash_get(stat_hash
, path
, APR_HASH_KEY_STRING
));
1032 apr_hash_set(stat_hash
, apr_pstrdup(hash_pool
, path
),
1033 APR_HASH_KEY_STRING
, svn_wc_dup_status2(status
, hash_pool
));
1037 /* Look up the key PATH in BATON->STATII. IS_DIR_BATON indicates whether
1038 baton is a struct *dir_baton or struct *file_baton. If the value doesn't
1039 yet exist, and the REPOS_TEXT_STATUS indicates that this is an
1040 addition, create a new status struct using the hash's pool.
1042 If IS_DIR_BATON is true, THIS_DIR_BATON is a *dir_baton cotaining the out
1043 of date (ood) information we want to set in BATON. This is necessary
1044 because this function tweaks the status of out-of-date directories
1045 (BATON == THIS_DIR_BATON) and out-of-date directories' parents
1046 (BATON == THIS_DIR_BATON->parent_baton). In the latter case THIS_DIR_BATON
1047 contains the ood info we want to bubble up to ancestor directories so these
1048 accurately reflect the fact they have an ood descendant.
1050 Merge REPOS_TEXT_STATUS and REPOS_PROP_STATUS into the status structure's
1053 Iff IS_DIR_BATON is true, DELETED_REV is used as follows, otherwise it
1056 If REPOS_TEXT_STATUS is svn_wc_status_deleted then DELETED_REV is
1057 optionally the revision path was deleted, in all other cases it must
1058 be set to SVN_INVALID_REVNUM. If DELETED_REV is not
1059 SVN_INVALID_REVNUM and REPOS_TEXT_STATUS is svn_wc_status_deleted,
1060 then use DELETED_REV to set PATH's ood_last_cmt_rev field in BATON.
1061 If DELETED_REV is SVN_INVALID_REVNUM and REPOS_TEXT_STATUS is
1062 svn_wc_status_deleted, set PATH's ood_last_cmt_rev to its parent's
1063 ood_last_cmt_rev value - see comment below.
1065 If a new struct was added, set the repos_lock to REPOS_LOCK. */
1066 static svn_error_t
*
1067 tweak_statushash(void *baton
,
1068 void *this_dir_baton
,
1069 svn_boolean_t is_dir_baton
,
1070 svn_wc_adm_access_t
*adm_access
,
1072 svn_boolean_t is_dir
,
1073 enum svn_wc_status_kind repos_text_status
,
1074 enum svn_wc_status_kind repos_prop_status
,
1075 svn_revnum_t deleted_rev
,
1076 svn_lock_t
*repos_lock
)
1078 svn_wc_status2_t
*statstruct
;
1080 apr_hash_t
*statushash
;
1083 statushash
= ((struct dir_baton
*) baton
)->statii
;
1085 statushash
= ((struct file_baton
*) baton
)->dir_baton
->statii
;
1086 pool
= apr_hash_pool_get(statushash
);
1088 /* Is PATH already a hash-key? */
1089 statstruct
= apr_hash_get(statushash
, path
, APR_HASH_KEY_STRING
);
1091 /* If not, make it so. */
1094 /* If this item isn't being added, then we're most likely
1095 dealing with a non-recursive (or at least partially
1096 non-recursive) working copy. Due to bugs in how the client
1097 reports the state of non-recursive working copies, the
1098 repository can send back responses about paths that don't
1099 even exist locally. Our best course here is just to ignore
1100 those responses. After all, if the client had reported
1101 correctly in the first, that path would either be mentioned
1102 as an 'add' or not mentioned at all, depending on how we
1103 eventually fix the bugs in non-recursivity. See issue
1104 #2122 for details. */
1105 if (repos_text_status
!= svn_wc_status_added
)
1106 return SVN_NO_ERROR
;
1108 /* Use the public API to get a statstruct, and put it into the hash. */
1109 SVN_ERR(svn_wc_status2(&statstruct
, path
, adm_access
, pool
));
1110 statstruct
->repos_lock
= repos_lock
;
1111 apr_hash_set(statushash
, apr_pstrdup(pool
, path
),
1112 APR_HASH_KEY_STRING
, statstruct
);
1115 /* Merge a repos "delete" + "add" into a single "replace". */
1116 if ((repos_text_status
== svn_wc_status_added
)
1117 && (statstruct
->repos_text_status
== svn_wc_status_deleted
))
1118 repos_text_status
= svn_wc_status_replaced
;
1120 /* Tweak the structure's repos fields. */
1121 if (repos_text_status
)
1122 statstruct
->repos_text_status
= repos_text_status
;
1123 if (repos_prop_status
)
1124 statstruct
->repos_prop_status
= repos_prop_status
;
1126 /* Copy out-of-date info. */
1129 struct dir_baton
*b
= this_dir_baton
;
1133 if (statstruct
->repos_text_status
== svn_wc_status_deleted
)
1135 /* When deleting PATH, BATON is for PATH's parent,
1136 so we must construct PATH's real statstruct->url. */
1138 svn_path_url_add_component(b
->url
,
1139 svn_path_basename(path
, pool
),
1143 statstruct
->url
= apr_pstrdup(pool
, b
->url
);
1146 /* The last committed date, and author for deleted items
1148 if (statstruct
->repos_text_status
== svn_wc_status_deleted
)
1150 statstruct
->ood_kind
= is_dir
? svn_node_dir
: svn_node_file
;
1152 /* Pre 1.5 servers don't provide the revision a path was deleted.
1153 So we punt and use the last committed revision of the path's
1154 parent, which has some chance of being correct. At worse it
1155 is a higher revision than the path was deleted, but this is
1156 better than nothing... */
1157 if (deleted_rev
== SVN_INVALID_REVNUM
)
1158 statstruct
->ood_last_cmt_rev
=
1159 ((struct dir_baton
*) baton
)->ood_last_cmt_rev
;
1161 statstruct
->ood_last_cmt_rev
= deleted_rev
;
1165 statstruct
->ood_kind
= b
->ood_kind
;
1166 statstruct
->ood_last_cmt_rev
= b
->ood_last_cmt_rev
;
1167 statstruct
->ood_last_cmt_date
= b
->ood_last_cmt_date
;
1168 if (b
->ood_last_cmt_author
)
1169 statstruct
->ood_last_cmt_author
=
1170 apr_pstrdup(pool
, b
->ood_last_cmt_author
);
1176 struct file_baton
*b
= baton
;
1178 statstruct
->url
= apr_pstrdup(pool
, b
->url
);
1179 statstruct
->ood_last_cmt_rev
= b
->ood_last_cmt_rev
;
1180 statstruct
->ood_last_cmt_date
= b
->ood_last_cmt_date
;
1181 statstruct
->ood_kind
= b
->ood_kind
;
1182 if (b
->ood_last_cmt_author
)
1183 statstruct
->ood_last_cmt_author
=
1184 apr_pstrdup(pool
, b
->ood_last_cmt_author
);
1186 return SVN_NO_ERROR
;
1189 /* Returns the URL for DB, or NULL: */
1191 find_dir_url(const struct dir_baton
*db
, apr_pool_t
*pool
)
1193 /* If we have no name, we're the root, return the anchor URL. */
1195 return db
->edit_baton
->anchor_status
->entry
->url
;
1199 struct dir_baton
*pb
= db
->parent_baton
;
1200 svn_wc_status2_t
*status
= apr_hash_get(pb
->statii
, db
->name
,
1201 APR_HASH_KEY_STRING
);
1202 /* Note that status->entry->url is NULL in the case of a missing
1203 * directory, which means we need to recurse up another level to
1204 * get a useful URL. */
1205 if (status
&& status
->entry
&& status
->entry
->url
)
1206 return status
->entry
->url
;
1208 url
= find_dir_url(pb
, pool
);
1210 return svn_path_url_add_component(url
, db
->name
, pool
);
1218 /* Create a new dir_baton for subdir PATH. */
1219 static svn_error_t
*
1220 make_dir_baton(void **dir_baton
,
1222 struct edit_baton
*edit_baton
,
1223 struct dir_baton
*parent_baton
,
1226 struct dir_baton
*pb
= parent_baton
;
1227 struct edit_baton
*eb
= edit_baton
;
1228 struct dir_baton
*d
= apr_pcalloc(pool
, sizeof(*d
));
1229 const char *full_path
;
1230 svn_wc_status2_t
*status_in_parent
;
1232 /* Don't do this. Just do NOT do this to me. */
1236 /* Construct the full path of this directory. */
1238 full_path
= svn_path_join(eb
->anchor
, path
, pool
);
1240 full_path
= apr_pstrdup(pool
, eb
->anchor
);
1242 /* Finish populating the baton members. */
1243 d
->path
= full_path
;
1244 d
->name
= path
? (svn_path_basename(path
, pool
)) : NULL
;
1245 d
->edit_baton
= edit_baton
;
1246 d
->parent_baton
= parent_baton
;
1248 d
->statii
= apr_hash_make(pool
);
1249 d
->url
= apr_pstrdup(pool
, find_dir_url(d
, pool
));
1250 d
->ood_last_cmt_rev
= SVN_INVALID_REVNUM
;
1251 d
->ood_last_cmt_date
= 0;
1252 d
->ood_kind
= svn_node_dir
;
1253 d
->ood_last_cmt_author
= NULL
;
1259 else if (pb
->depth
== svn_depth_immediates
)
1260 d
->depth
= svn_depth_empty
;
1261 else if (pb
->depth
== svn_depth_files
|| pb
->depth
== svn_depth_empty
)
1263 else if (pb
->depth
== svn_depth_unknown
)
1264 /* This is only tentative, it can be overridden from d's entry
1266 d
->depth
= svn_depth_unknown
;
1268 d
->depth
= svn_depth_infinity
;
1272 d
->depth
= eb
->default_depth
;
1275 /* Get the status for this path's children. Of course, we only want
1276 to do this if the path is versioned as a directory. */
1278 status_in_parent
= apr_hash_get(pb
->statii
, d
->path
, APR_HASH_KEY_STRING
);
1280 status_in_parent
= eb
->anchor_status
;
1282 /* Order is important here. We can't depend on status_in_parent->entry
1283 being non-NULL until after we've checked all the conditions that
1284 might indicate that the parent is unversioned ("unversioned" for
1285 our purposes includes being an external or ignored item). */
1286 if (status_in_parent
1287 && (status_in_parent
->text_status
!= svn_wc_status_unversioned
)
1288 && (status_in_parent
->text_status
!= svn_wc_status_missing
)
1289 && (status_in_parent
->text_status
!= svn_wc_status_obstructed
)
1290 && (status_in_parent
->text_status
!= svn_wc_status_external
)
1291 && (status_in_parent
->text_status
!= svn_wc_status_ignored
)
1292 && (status_in_parent
->entry
->kind
== svn_node_dir
)
1294 && (d
->depth
== svn_depth_unknown
1295 || d
->depth
== svn_depth_infinity
1296 || d
->depth
== svn_depth_files
1297 || d
->depth
== svn_depth_immediates
)
1300 svn_wc_adm_access_t
*dir_access
;
1301 svn_wc_status2_t
*this_dir_status
;
1302 apr_array_header_t
*ignores
= eb
->ignores
;
1303 SVN_ERR(svn_wc_adm_retrieve(&dir_access
, eb
->adm_access
,
1305 SVN_ERR(get_dir_status(eb
, status_in_parent
->entry
, dir_access
, NULL
,
1306 ignores
, d
->depth
== svn_depth_files
?
1307 svn_depth_files
: svn_depth_immediates
,
1308 TRUE
, TRUE
, TRUE
, hash_stash
, d
->statii
, NULL
,
1311 /* If we found a depth here, it should govern. */
1312 this_dir_status
= apr_hash_get(d
->statii
, d
->path
, APR_HASH_KEY_STRING
);
1313 if (this_dir_status
&& this_dir_status
->entry
1314 && (d
->depth
== svn_depth_unknown
1315 || d
->depth
> status_in_parent
->entry
->depth
))
1317 d
->depth
= this_dir_status
->entry
->depth
;
1322 return SVN_NO_ERROR
;
1326 /* Make a file baton, using a new subpool of PARENT_DIR_BATON's pool.
1327 NAME is just one component, not a path. */
1328 static struct file_baton
*
1329 make_file_baton(struct dir_baton
*parent_dir_baton
,
1333 struct dir_baton
*pb
= parent_dir_baton
;
1334 struct edit_baton
*eb
= pb
->edit_baton
;
1335 struct file_baton
*f
= apr_pcalloc(pool
, sizeof(*f
));
1336 const char *full_path
;
1338 /* Construct the full path of this file. */
1339 full_path
= svn_path_join(eb
->anchor
, path
, pool
);
1341 /* Finish populating the baton members. */
1342 f
->path
= full_path
;
1343 f
->name
= svn_path_basename(path
, pool
);
1347 f
->url
= svn_path_url_add_component(find_dir_url(pb
, pool
),
1348 svn_path_basename(full_path
, pool
),
1350 f
->ood_last_cmt_rev
= SVN_INVALID_REVNUM
;
1351 f
->ood_last_cmt_date
= 0;
1352 f
->ood_kind
= svn_node_file
;
1353 f
->ood_last_cmt_author
= NULL
;
1357 /* Return a boolean answer to the question "Is STATUS something that
1358 should be reported?". EB is the edit baton. */
1359 static svn_boolean_t
1360 is_sendable_status(svn_wc_status2_t
*status
,
1361 struct edit_baton
*eb
)
1363 /* If the repository status was touched at all, it's interesting. */
1364 if (status
->repos_text_status
!= svn_wc_status_none
)
1366 if (status
->repos_prop_status
!= svn_wc_status_none
)
1369 /* If there is a lock in the repository, send it. */
1370 if (status
->repos_lock
)
1373 /* If the item is ignored, and we don't want ignores, skip it. */
1374 if ((status
->text_status
== svn_wc_status_ignored
) && (! eb
->no_ignore
))
1377 /* If we want everything, we obviously want this single-item subset
1382 /* If the item is unversioned, display it. */
1383 if (status
->text_status
== svn_wc_status_unversioned
)
1386 /* If the text or property states are interesting, send it. */
1387 if ((status
->text_status
!= svn_wc_status_none
)
1388 && (status
->text_status
!= svn_wc_status_normal
))
1390 if ((status
->prop_status
!= svn_wc_status_none
)
1391 && (status
->prop_status
!= svn_wc_status_normal
))
1394 /* If it's locked or switched, send it. */
1397 if (status
->switched
)
1400 /* If there is a lock token, send it. */
1401 if (status
->entry
&& status
->entry
->lock_token
)
1404 /* If the entry is associated with a changelist, send it. */
1405 if (status
->entry
&& status
->entry
->changelist
)
1408 /* Otherwise, don't send it. */
1413 /* Baton for mark_status. */
1416 svn_wc_status_func2_t real_status_func
; /* real status function */
1417 void *real_status_baton
; /* real status baton */
1420 /* A status callback function which wraps the *real* status
1421 function/baton. It simply sets the "repos_text_status" field of the
1422 STATUS to svn_wc_status_deleted and passes it off to the real
1423 status func/baton. */
1425 mark_deleted(void *baton
,
1427 svn_wc_status2_t
*status
)
1429 struct status_baton
*sb
= baton
;
1430 status
->repos_text_status
= svn_wc_status_deleted
;
1431 sb
->real_status_func(sb
->real_status_baton
, path
, status
);
1435 /* Handle a directory's STATII hash. EB is the edit baton. DIR_PATH
1436 and DIR_ENTRY are the on-disk path and entry, respectively, for the
1437 directory itself. Descend into subdirectories according to DEPTH.
1438 Also, if DIR_WAS_DELETED is set, each status that is reported
1439 through this function will have its repos_text_status field showing
1440 a deletion. Use POOL for all allocations. */
1441 static svn_error_t
*
1442 handle_statii(struct edit_baton
*eb
,
1443 svn_wc_entry_t
*dir_entry
,
1444 const char *dir_path
,
1446 svn_boolean_t dir_was_deleted
,
1450 apr_array_header_t
*ignores
= eb
->ignores
;
1451 apr_hash_index_t
*hi
;
1452 apr_pool_t
*subpool
= svn_pool_create(pool
);
1453 svn_wc_status_func2_t status_func
= eb
->status_func
;
1454 void *status_baton
= eb
->status_baton
;
1455 struct status_baton sb
;
1457 if (dir_was_deleted
)
1459 sb
.real_status_func
= eb
->status_func
;
1460 sb
.real_status_baton
= eb
->status_baton
;
1461 status_func
= mark_deleted
;
1465 /* Loop over all the statuses still in our hash, handling each one. */
1466 for (hi
= apr_hash_first(pool
, statii
); hi
; hi
= apr_hash_next(hi
))
1470 svn_wc_status2_t
*status
;
1472 apr_hash_this(hi
, &key
, NULL
, &val
);
1475 /* Clear the subpool. */
1476 svn_pool_clear(subpool
);
1478 /* Now, handle the status. We don't recurse for svn_depth_immediates
1479 because we already have the subdirectories' statii. */
1480 if (status
->text_status
!= svn_wc_status_obstructed
1481 && status
->text_status
!= svn_wc_status_missing
1482 && status
->entry
&& status
->entry
->kind
== svn_node_dir
1483 && (depth
== svn_depth_unknown
1484 || depth
== svn_depth_infinity
))
1486 svn_wc_adm_access_t
*dir_access
;
1488 SVN_ERR(svn_wc_adm_retrieve(&dir_access
, eb
->adm_access
,
1491 SVN_ERR(get_dir_status(eb
, dir_entry
, dir_access
, NULL
,
1492 ignores
, depth
, eb
->get_all
,
1493 eb
->no_ignore
, TRUE
, status_func
,
1494 status_baton
, eb
->cancel_func
,
1495 eb
->cancel_baton
, subpool
));
1497 if (dir_was_deleted
)
1498 status
->repos_text_status
= svn_wc_status_deleted
;
1499 if (is_sendable_status(status
, eb
))
1500 (eb
->status_func
)(eb
->status_baton
, key
, status
);
1503 /* Destroy the subpool. */
1504 svn_pool_destroy(subpool
);
1506 return SVN_NO_ERROR
;
1510 /*----------------------------------------------------------------------*/
1512 /*** The callbacks we'll plug into an svn_delta_editor_t structure. ***/
1514 static svn_error_t
*
1515 set_target_revision(void *edit_baton
,
1516 svn_revnum_t target_revision
,
1519 struct edit_baton
*eb
= edit_baton
;
1520 *(eb
->target_revision
) = target_revision
;
1521 return SVN_NO_ERROR
;
1525 static svn_error_t
*
1526 open_root(void *edit_baton
,
1527 svn_revnum_t base_revision
,
1531 struct edit_baton
*eb
= edit_baton
;
1532 eb
->root_opened
= TRUE
;
1533 return make_dir_baton(dir_baton
, NULL
, eb
, NULL
, pool
);
1537 static svn_error_t
*
1538 delete_entry(const char *path
,
1539 svn_revnum_t revision
,
1543 struct dir_baton
*db
= parent_baton
;
1544 struct edit_baton
*eb
= db
->edit_baton
;
1545 apr_hash_t
*entries
;
1546 const char *name
= svn_path_basename(path
, pool
);
1547 const char *full_path
= svn_path_join(eb
->anchor
, path
, pool
);
1548 const char *dir_path
;
1549 svn_node_kind_t kind
;
1550 svn_wc_adm_access_t
*adm_access
;
1551 const char *hash_key
;
1552 const svn_wc_entry_t
*entry
;
1555 /* Note: when something is deleted, it's okay to tweak the
1556 statushash immediately. No need to wait until close_file or
1557 close_dir, because there's no risk of having to honor the 'added'
1558 flag. We already know this item exists in the working copy. */
1560 /* Read the parent's entries file. If the deleted thing is not
1561 versioned in this working copy, it was probably deleted via this
1562 working copy. No need to report such a thing. */
1563 /* ### use svn_wc_entry() instead? */
1564 SVN_ERR(svn_wc__entry_versioned(&entry
, full_path
, eb
->adm_access
,
1566 if (entry
->kind
== svn_node_dir
)
1568 dir_path
= full_path
;
1569 hash_key
= SVN_WC_ENTRY_THIS_DIR
;
1573 dir_path
= svn_path_dirname(full_path
, pool
);
1577 err
= svn_wc_adm_retrieve(&adm_access
, eb
->adm_access
, dir_path
, pool
);
1580 SVN_ERR(svn_io_check_path(full_path
, &kind
, pool
));
1581 if ((kind
== svn_node_none
) && (err
->apr_err
== SVN_ERR_WC_NOT_LOCKED
))
1583 /* We're probably dealing with a non-recursive, (or
1584 partially non-recursive, working copy. Due to deep bugs
1585 in how the client reports the state of non-recursive
1586 working copies, the repository can report that a path is
1587 deleted in an area where we not only don't have the path
1588 in question, we don't even have its parent(s). A
1589 complete fix would require a serious revamp of how
1590 non-recursive working copies store and report themselves,
1591 plus some thinking about the UI behavior we want when
1592 someone runs 'svn st -u' in a [partially] non-recursive
1595 For now, we just do our best to detect the condition and
1596 not report an error if it holds. See issue #2122. */
1597 svn_error_clear(err
);
1598 return SVN_NO_ERROR
;
1604 SVN_ERR(svn_wc_entries_read(&entries
, adm_access
, FALSE
, pool
));
1605 if (apr_hash_get(entries
, hash_key
, APR_HASH_KEY_STRING
))
1606 SVN_ERR(tweak_statushash(db
, db
, TRUE
, eb
->adm_access
,
1607 full_path
, entry
->kind
== svn_node_dir
,
1608 svn_wc_status_deleted
, 0, revision
, NULL
));
1610 /* Mark the parent dir -- it lost an entry (unless that parent dir
1611 is the root node and we're not supposed to report on the root
1613 if (db
->parent_baton
&& (! *eb
->target
))
1614 SVN_ERR(tweak_statushash(db
->parent_baton
, db
, TRUE
, eb
->adm_access
,
1615 db
->path
, entry
->kind
== svn_node_dir
,
1616 svn_wc_status_modified
, 0, SVN_INVALID_REVNUM
,
1619 return SVN_NO_ERROR
;
1623 static svn_error_t
*
1624 add_directory(const char *path
,
1626 const char *copyfrom_path
,
1627 svn_revnum_t copyfrom_revision
,
1631 struct dir_baton
*pb
= parent_baton
;
1632 struct edit_baton
*eb
= pb
->edit_baton
;
1633 struct dir_baton
*new_db
;
1635 SVN_ERR(make_dir_baton(child_baton
, path
, eb
, pb
, pool
));
1637 /* Make this dir as added. */
1638 new_db
= *child_baton
;
1639 new_db
->added
= TRUE
;
1641 /* Mark the parent as changed; it gained an entry. */
1642 pb
->text_changed
= TRUE
;
1644 return SVN_NO_ERROR
;
1648 static svn_error_t
*
1649 open_directory(const char *path
,
1651 svn_revnum_t base_revision
,
1655 struct dir_baton
*pb
= parent_baton
;
1656 return make_dir_baton(child_baton
, path
, pb
->edit_baton
, pb
, pool
);
1660 static svn_error_t
*
1661 change_dir_prop(void *dir_baton
,
1663 const svn_string_t
*value
,
1666 struct dir_baton
*db
= dir_baton
;
1667 if (svn_wc_is_normal_prop(name
))
1668 db
->prop_changed
= TRUE
;
1670 /* Note any changes to the repository. */
1673 if (strcmp(name
, SVN_PROP_ENTRY_COMMITTED_REV
) == 0)
1674 db
->ood_last_cmt_rev
= SVN_STR_TO_REV(value
->data
);
1675 else if (strcmp(name
, SVN_PROP_ENTRY_LAST_AUTHOR
) == 0)
1676 db
->ood_last_cmt_author
= apr_pstrdup(db
->pool
, value
->data
);
1677 else if (strcmp(name
, SVN_PROP_ENTRY_COMMITTED_DATE
) == 0)
1680 SVN_ERR(svn_time_from_cstring(&tm
, value
->data
, db
->pool
));
1681 db
->ood_last_cmt_date
= tm
;
1685 return SVN_NO_ERROR
;
1690 static svn_error_t
*
1691 close_directory(void *dir_baton
,
1694 struct dir_baton
*db
= dir_baton
;
1695 struct dir_baton
*pb
= db
->parent_baton
;
1696 struct edit_baton
*eb
= db
->edit_baton
;
1697 svn_wc_status2_t
*dir_status
= NULL
;
1699 /* If nothing has changed and directory has no out of
1700 date descendants, return. */
1701 if (db
->added
|| db
->prop_changed
|| db
->text_changed
1702 || db
->ood_last_cmt_rev
!= SVN_INVALID_REVNUM
)
1704 enum svn_wc_status_kind repos_text_status
;
1705 enum svn_wc_status_kind repos_prop_status
;
1707 /* If this is a new directory, add it to the statushash. */
1710 repos_text_status
= svn_wc_status_added
;
1711 repos_prop_status
= db
->prop_changed
? svn_wc_status_added
1712 : svn_wc_status_none
;
1716 repos_text_status
= db
->text_changed
? svn_wc_status_modified
1717 : svn_wc_status_none
;
1718 repos_prop_status
= db
->prop_changed
? svn_wc_status_modified
1719 : svn_wc_status_none
;
1722 /* Maybe add this directory to its parent's status hash. Note
1723 that tweak_statushash won't do anything if repos_text_status
1724 is not svn_wc_status_added. */
1727 /* ### When we add directory locking, we need to find a
1728 ### directory lock here. */
1729 SVN_ERR(tweak_statushash(pb
, db
, TRUE
,
1733 repos_prop_status
, SVN_INVALID_REVNUM
,
1738 /* We're editing the root dir of the WC. As its repos
1739 status info isn't otherwise set, set it directly to
1740 trigger invocation of the status callback below. */
1741 eb
->anchor_status
->repos_prop_status
= repos_prop_status
;
1742 eb
->anchor_status
->repos_text_status
= repos_text_status
;
1744 /* If the root dir is out of date set the ood info directly too. */
1745 if (db
->ood_last_cmt_rev
!= eb
->anchor_status
->entry
->revision
)
1747 eb
->anchor_status
->ood_last_cmt_rev
= db
->ood_last_cmt_rev
;
1748 eb
->anchor_status
->ood_last_cmt_date
= db
->ood_last_cmt_date
;
1749 eb
->anchor_status
->ood_kind
= db
->ood_kind
;
1750 eb
->anchor_status
->ood_last_cmt_author
=
1751 apr_pstrdup(pool
, db
->ood_last_cmt_author
);
1756 /* Handle this directory's statuses, and then note in the parent
1757 that this has been done. */
1758 if (pb
&& ! db
->excluded
)
1760 svn_boolean_t was_deleted
= FALSE
;
1762 /* See if the directory was deleted or replaced. */
1763 dir_status
= apr_hash_get(pb
->statii
, db
->path
, APR_HASH_KEY_STRING
);
1765 ((dir_status
->repos_text_status
== svn_wc_status_deleted
)
1766 || (dir_status
->repos_text_status
== svn_wc_status_replaced
)))
1769 /* Now do the status reporting. */
1770 SVN_ERR(handle_statii(eb
, dir_status
? dir_status
->entry
: NULL
,
1771 db
->path
, db
->statii
, was_deleted
, db
->depth
,
1773 if (dir_status
&& is_sendable_status(dir_status
, eb
))
1774 (eb
->status_func
)(eb
->status_baton
, db
->path
, dir_status
);
1775 apr_hash_set(pb
->statii
, db
->path
, APR_HASH_KEY_STRING
, NULL
);
1779 /* If this is the top-most directory, and the operation had a
1780 target, we should only report the target. */
1783 svn_wc_status2_t
*tgt_status
;
1784 const char *path
= svn_path_join(eb
->anchor
, eb
->target
, pool
);
1785 dir_status
= eb
->anchor_status
;
1786 tgt_status
= apr_hash_get(db
->statii
, path
, APR_HASH_KEY_STRING
);
1789 if (tgt_status
->entry
1790 && tgt_status
->entry
->kind
== svn_node_dir
)
1792 svn_wc_adm_access_t
*dir_access
;
1793 SVN_ERR(svn_wc_adm_retrieve(&dir_access
, eb
->adm_access
,
1795 SVN_ERR(get_dir_status
1796 (eb
, tgt_status
->entry
, dir_access
, NULL
,
1797 eb
->ignores
, eb
->default_depth
, eb
->get_all
,
1798 eb
->no_ignore
, TRUE
,
1799 eb
->status_func
, eb
->status_baton
,
1800 eb
->cancel_func
, eb
->cancel_baton
, pool
));
1802 if (is_sendable_status(tgt_status
, eb
))
1803 (eb
->status_func
)(eb
->status_baton
, path
, tgt_status
);
1808 /* Otherwise, we report on all our children and ourself.
1809 Note that our directory couldn't have been deleted,
1810 because it is the root of the edit drive. */
1811 SVN_ERR(handle_statii(eb
, eb
->anchor_status
->entry
, db
->path
,
1812 db
->statii
, FALSE
, eb
->default_depth
, pool
));
1813 if (is_sendable_status(eb
->anchor_status
, eb
))
1814 (eb
->status_func
)(eb
->status_baton
, db
->path
, eb
->anchor_status
);
1815 eb
->anchor_status
= NULL
;
1818 return SVN_NO_ERROR
;
1823 static svn_error_t
*
1824 add_file(const char *path
,
1826 const char *copyfrom_path
,
1827 svn_revnum_t copyfrom_revision
,
1831 struct dir_baton
*pb
= parent_baton
;
1832 struct file_baton
*new_fb
= make_file_baton(pb
, path
, pool
);
1834 /* Mark parent dir as changed */
1835 pb
->text_changed
= TRUE
;
1837 /* Make this file as added. */
1838 new_fb
->added
= TRUE
;
1840 *file_baton
= new_fb
;
1841 return SVN_NO_ERROR
;
1845 static svn_error_t
*
1846 open_file(const char *path
,
1848 svn_revnum_t base_revision
,
1852 struct dir_baton
*pb
= parent_baton
;
1853 struct file_baton
*new_fb
= make_file_baton(pb
, path
, pool
);
1855 *file_baton
= new_fb
;
1856 return SVN_NO_ERROR
;
1860 static svn_error_t
*
1861 apply_textdelta(void *file_baton
,
1862 const char *base_checksum
,
1864 svn_txdelta_window_handler_t
*handler
,
1865 void **handler_baton
)
1867 struct file_baton
*fb
= file_baton
;
1869 /* Mark file as having textual mods. */
1870 fb
->text_changed
= TRUE
;
1872 /* Send back a NULL window handler -- we don't need the actual diffs. */
1873 *handler_baton
= NULL
;
1874 *handler
= svn_delta_noop_window_handler
;
1876 return SVN_NO_ERROR
;
1880 static svn_error_t
*
1881 change_file_prop(void *file_baton
,
1883 const svn_string_t
*value
,
1886 struct file_baton
*fb
= file_baton
;
1887 if (svn_wc_is_normal_prop(name
))
1888 fb
->prop_changed
= TRUE
;
1890 /* Note any changes to the repository. */
1893 if (strcmp(name
, SVN_PROP_ENTRY_COMMITTED_REV
) == 0)
1894 fb
->ood_last_cmt_rev
= SVN_STR_TO_REV(value
->data
);
1895 else if (strcmp(name
, SVN_PROP_ENTRY_LAST_AUTHOR
) == 0)
1896 fb
->ood_last_cmt_author
= apr_pstrdup(fb
->dir_baton
->pool
,
1898 else if (strcmp(name
, SVN_PROP_ENTRY_COMMITTED_DATE
) == 0)
1901 SVN_ERR(svn_time_from_cstring(&tm
, value
->data
,
1902 fb
->dir_baton
->pool
));
1903 fb
->ood_last_cmt_date
= tm
;
1907 return SVN_NO_ERROR
;
1911 static svn_error_t
*
1912 close_file(void *file_baton
,
1913 const char *text_checksum
, /* ignored, as we receive no data */
1916 struct file_baton
*fb
= file_baton
;
1917 enum svn_wc_status_kind repos_text_status
;
1918 enum svn_wc_status_kind repos_prop_status
;
1919 svn_lock_t
*repos_lock
= NULL
;
1921 /* If nothing has changed, return. */
1922 if (! (fb
->added
|| fb
->prop_changed
|| fb
->text_changed
))
1923 return SVN_NO_ERROR
;
1925 /* If this is a new file, add it to the statushash. */
1929 repos_text_status
= svn_wc_status_added
;
1930 repos_prop_status
= fb
->prop_changed
? svn_wc_status_added
: 0;
1932 if (fb
->edit_baton
->repos_locks
)
1934 url
= find_dir_url(fb
->dir_baton
, pool
);
1937 url
= svn_path_url_add_component(url
, fb
->name
, pool
);
1938 repos_lock
= apr_hash_get
1939 (fb
->edit_baton
->repos_locks
,
1940 svn_path_uri_decode(url
+
1941 strlen(fb
->edit_baton
->repos_root
),
1942 pool
), APR_HASH_KEY_STRING
);
1948 repos_text_status
= fb
->text_changed
? svn_wc_status_modified
: 0;
1949 repos_prop_status
= fb
->prop_changed
? svn_wc_status_modified
: 0;
1952 SVN_ERR(tweak_statushash(fb
, NULL
, FALSE
,
1953 fb
->edit_baton
->adm_access
,
1956 repos_prop_status
, SVN_INVALID_REVNUM
,
1959 return SVN_NO_ERROR
;
1963 static svn_error_t
*
1964 close_edit(void *edit_baton
,
1967 struct edit_baton
*eb
= edit_baton
;
1968 apr_array_header_t
*ignores
= eb
->ignores
;
1969 svn_error_t
*err
= NULL
;
1971 /* If we get here and the root was not opened as part of the edit,
1972 we need to transmit statuses for everything. Otherwise, we
1974 if (eb
->root_opened
)
1977 /* If we have a target, that's the thing we're sending, otherwise
1978 we're sending the anchor. */
1982 svn_node_kind_t kind
;
1983 const char *full_path
= svn_path_join(eb
->anchor
, eb
->target
, pool
);
1985 err
= svn_io_check_path(full_path
, &kind
, pool
);
1986 if (err
) goto cleanup
;
1988 if (kind
== svn_node_dir
)
1990 svn_wc_adm_access_t
*tgt_access
;
1991 const svn_wc_entry_t
*tgt_entry
;
1993 err
= svn_wc_entry(&tgt_entry
, full_path
, eb
->adm_access
,
1995 if (err
) goto cleanup
;
1999 err
= get_dir_status(eb
, NULL
, eb
->adm_access
, eb
->target
,
2000 ignores
, svn_depth_empty
, eb
->get_all
,
2002 eb
->status_func
, eb
->status_baton
,
2003 eb
->cancel_func
, eb
->cancel_baton
,
2005 if (err
) goto cleanup
;
2009 err
= svn_wc_adm_retrieve(&tgt_access
, eb
->adm_access
,
2011 if (err
) goto cleanup
;
2013 err
= get_dir_status(eb
, NULL
, tgt_access
, NULL
, ignores
,
2014 eb
->default_depth
, eb
->get_all
,
2015 eb
->no_ignore
, FALSE
,
2016 eb
->status_func
, eb
->status_baton
,
2017 eb
->cancel_func
, eb
->cancel_baton
,
2019 if (err
) goto cleanup
;
2024 err
= get_dir_status(eb
, NULL
, eb
->adm_access
, eb
->target
,
2025 ignores
, svn_depth_empty
, eb
->get_all
,
2026 TRUE
, TRUE
, eb
->status_func
, eb
->status_baton
,
2027 eb
->cancel_func
, eb
->cancel_baton
, pool
);
2028 if (err
) goto cleanup
;
2033 err
= get_dir_status(eb
, NULL
, eb
->adm_access
, NULL
, ignores
,
2034 eb
->default_depth
, eb
->get_all
, eb
->no_ignore
,
2035 FALSE
, eb
->status_func
, eb
->status_baton
,
2036 eb
->cancel_func
, eb
->cancel_baton
, pool
);
2037 if (err
) goto cleanup
;
2041 /* Let's make sure that we didn't harvest any traversal info for the
2042 anchor if we had a target. */
2043 if (eb
->traversal_info
&& *eb
->target
)
2045 apr_hash_set(eb
->traversal_info
->externals_old
,
2046 eb
->anchor
, APR_HASH_KEY_STRING
, NULL
);
2047 apr_hash_set(eb
->traversal_info
->externals_new
,
2048 eb
->anchor
, APR_HASH_KEY_STRING
, NULL
);
2049 apr_hash_set(eb
->traversal_info
->depths
,
2050 eb
->anchor
, APR_HASH_KEY_STRING
, NULL
);
2058 /*** Public API ***/
2061 svn_wc_get_status_editor3(const svn_delta_editor_t
**editor
,
2063 void **set_locks_baton
,
2064 svn_revnum_t
*edit_revision
,
2065 svn_wc_adm_access_t
*anchor
,
2068 svn_boolean_t get_all
,
2069 svn_boolean_t no_ignore
,
2070 apr_array_header_t
*ignore_patterns
,
2071 svn_wc_status_func2_t status_func
,
2073 svn_cancel_func_t cancel_func
,
2075 svn_wc_traversal_info_t
*traversal_info
,
2078 struct edit_baton
*eb
;
2079 svn_delta_editor_t
*tree_editor
= svn_delta_default_editor(pool
);
2081 /* Construct an edit baton. */
2082 eb
= apr_palloc(pool
, sizeof(*eb
));
2083 eb
->default_depth
= depth
;
2084 eb
->target_revision
= edit_revision
;
2085 eb
->adm_access
= anchor
;
2086 eb
->get_all
= get_all
;
2087 eb
->no_ignore
= no_ignore
;
2088 eb
->status_func
= status_func
;
2089 eb
->status_baton
= status_baton
;
2090 eb
->cancel_func
= cancel_func
;
2091 eb
->cancel_baton
= cancel_baton
;
2092 eb
->traversal_info
= traversal_info
;
2093 eb
->externals
= apr_hash_make(pool
);
2094 eb
->anchor
= svn_wc_adm_access_path(anchor
);
2095 eb
->target
= target
;
2096 eb
->root_opened
= FALSE
;
2097 eb
->repos_locks
= NULL
;
2098 eb
->repos_root
= NULL
;
2100 /* Use the caller-provided ignore patterns if provided; the build-time
2101 configured defaults otherwise. */
2102 if (ignore_patterns
)
2104 eb
->ignores
= ignore_patterns
;
2108 eb
->ignores
= apr_array_make(pool
, 16, sizeof(const char *));
2109 svn_cstring_split_append(eb
->ignores
, SVN_CONFIG_DEFAULT_GLOBAL_IGNORES
,
2110 "\n\r\t\v ", FALSE
, pool
);
2113 /* The edit baton's status structure maps to PATH, and the editor
2114 have to be aware of whether that is the anchor or the target. */
2115 SVN_ERR(svn_wc_status2(&(eb
->anchor_status
), eb
->anchor
, anchor
, pool
));
2117 /* Construct an editor. */
2118 tree_editor
->set_target_revision
= set_target_revision
;
2119 tree_editor
->open_root
= open_root
;
2120 tree_editor
->delete_entry
= delete_entry
;
2121 tree_editor
->add_directory
= add_directory
;
2122 tree_editor
->open_directory
= open_directory
;
2123 tree_editor
->change_dir_prop
= change_dir_prop
;
2124 tree_editor
->close_directory
= close_directory
;
2125 tree_editor
->add_file
= add_file
;
2126 tree_editor
->open_file
= open_file
;
2127 tree_editor
->apply_textdelta
= apply_textdelta
;
2128 tree_editor
->change_file_prop
= change_file_prop
;
2129 tree_editor
->close_file
= close_file
;
2130 tree_editor
->close_edit
= close_edit
;
2132 /* Conjoin a cancellation editor with our status editor. */
2133 SVN_ERR(svn_delta_get_cancellation_editor(cancel_func
, cancel_baton
,
2134 tree_editor
, eb
, editor
,
2137 if (set_locks_baton
)
2138 *set_locks_baton
= eb
;
2140 return SVN_NO_ERROR
;
2145 svn_wc_get_status_editor2(const svn_delta_editor_t
**editor
,
2147 void **set_locks_baton
,
2148 svn_revnum_t
*edit_revision
,
2149 svn_wc_adm_access_t
*anchor
,
2152 svn_boolean_t recurse
,
2153 svn_boolean_t get_all
,
2154 svn_boolean_t no_ignore
,
2155 svn_wc_status_func2_t status_func
,
2157 svn_cancel_func_t cancel_func
,
2159 svn_wc_traversal_info_t
*traversal_info
,
2162 apr_array_header_t
*ignores
;
2163 SVN_ERR(svn_wc_get_default_ignores(&ignores
, config
, pool
));
2164 return svn_wc_get_status_editor3(editor
,
2170 SVN_DEPTH_INFINITY_OR_IMMEDIATES(recurse
),
2183 /* Helpers for deprecated svn_wc_status_editor(), of type
2184 svn_wc_status_func2_t. */
2185 struct old_status_func_cb_baton
2187 svn_wc_status_func_t original_func
;
2188 void *original_baton
;
2191 static void old_status_func_cb(void *baton
,
2193 svn_wc_status2_t
*status
)
2195 struct old_status_func_cb_baton
*b
= baton
;
2196 svn_wc_status_t
*stat
= (svn_wc_status_t
*) status
;
2198 b
->original_func(b
->original_baton
, path
, stat
);
2202 svn_wc_get_status_editor(const svn_delta_editor_t
**editor
,
2204 svn_revnum_t
*edit_revision
,
2205 svn_wc_adm_access_t
*anchor
,
2208 svn_boolean_t recurse
,
2209 svn_boolean_t get_all
,
2210 svn_boolean_t no_ignore
,
2211 svn_wc_status_func_t status_func
,
2213 svn_cancel_func_t cancel_func
,
2215 svn_wc_traversal_info_t
*traversal_info
,
2218 struct old_status_func_cb_baton
*b
= apr_pcalloc(pool
, sizeof(*b
));
2219 apr_array_header_t
*ignores
;
2220 b
->original_func
= status_func
;
2221 b
->original_baton
= status_baton
;
2222 SVN_ERR(svn_wc_get_default_ignores(&ignores
, config
, pool
));
2223 return svn_wc_get_status_editor3(editor
, edit_baton
, NULL
, edit_revision
,
2225 SVN_DEPTH_INFINITY_OR_IMMEDIATES(recurse
),
2226 get_all
, no_ignore
, ignores
,
2227 old_status_func_cb
, b
,
2228 cancel_func
, cancel_baton
,
2229 traversal_info
, pool
);
2234 svn_wc_status_set_repos_locks(void *edit_baton
,
2236 const char *repos_root
,
2239 struct edit_baton
*eb
= edit_baton
;
2241 eb
->repos_locks
= locks
;
2242 eb
->repos_root
= apr_pstrdup(pool
, repos_root
);
2244 return SVN_NO_ERROR
;
2249 svn_wc_get_default_ignores(apr_array_header_t
**patterns
,
2253 svn_config_t
*cfg
= config
? apr_hash_get(config
,
2254 SVN_CONFIG_CATEGORY_CONFIG
,
2255 APR_HASH_KEY_STRING
) : NULL
;
2258 /* Check the Subversion run-time configuration for global ignores.
2259 If no configuration value exists, we fall back to our defaults. */
2260 svn_config_get(cfg
, &val
, SVN_CONFIG_SECTION_MISCELLANY
,
2261 SVN_CONFIG_OPTION_GLOBAL_IGNORES
,
2262 SVN_CONFIG_DEFAULT_GLOBAL_IGNORES
);
2263 *patterns
= apr_array_make(pool
, 16, sizeof(const char *));
2265 /* Split the patterns on whitespace, and stuff them into *PATTERNS. */
2266 svn_cstring_split_append(*patterns
, val
, "\n\r\t\v ", FALSE
, pool
);
2267 return SVN_NO_ERROR
;
2272 svn_wc_status2(svn_wc_status2_t
**status
,
2274 svn_wc_adm_access_t
*adm_access
,
2277 const svn_wc_entry_t
*entry
= NULL
;
2278 const svn_wc_entry_t
*parent_entry
= NULL
;
2281 SVN_ERR(svn_wc_entry(&entry
, path
, adm_access
, FALSE
, pool
));
2283 if (entry
&& ! svn_path_is_empty(path
))
2285 const char *parent_path
= svn_path_dirname(path
, pool
);
2286 svn_wc_adm_access_t
*parent_access
;
2287 SVN_ERR(svn_wc__adm_retrieve_internal(&parent_access
, adm_access
,
2288 parent_path
, pool
));
2290 SVN_ERR(svn_wc_entry(&parent_entry
, parent_path
, parent_access
,
2294 SVN_ERR(assemble_status(status
, path
, adm_access
, entry
, parent_entry
,
2295 svn_node_unknown
, FALSE
, /* bogus */
2296 TRUE
, FALSE
, NULL
, NULL
, pool
));
2297 return SVN_NO_ERROR
;
2302 svn_wc_status(svn_wc_status_t
**status
,
2304 svn_wc_adm_access_t
*adm_access
,
2307 svn_wc_status2_t
*stat2
;
2309 SVN_ERR(svn_wc_status2(&stat2
, path
, adm_access
, pool
));
2310 *status
= (svn_wc_status_t
*) stat2
;
2311 return SVN_NO_ERROR
;
2317 svn_wc_dup_status2(svn_wc_status2_t
*orig_stat
,
2320 svn_wc_status2_t
*new_stat
= apr_palloc(pool
, sizeof(*new_stat
));
2322 /* Shallow copy all members. */
2323 *new_stat
= *orig_stat
;
2325 /* No go back and dup the deep item. */
2326 if (orig_stat
->entry
)
2327 new_stat
->entry
= svn_wc_entry_dup(orig_stat
->entry
, pool
);
2329 if (orig_stat
->repos_lock
)
2330 new_stat
->repos_lock
= svn_lock_dup(orig_stat
->repos_lock
, pool
);
2333 new_stat
->url
= apr_pstrdup(pool
, orig_stat
->url
);
2335 if (orig_stat
->ood_last_cmt_author
)
2336 new_stat
->ood_last_cmt_author
2337 = apr_pstrdup(pool
, orig_stat
->ood_last_cmt_author
);
2339 /* Return the new hotness. */
2345 svn_wc_dup_status(svn_wc_status_t
*orig_stat
,
2348 svn_wc_status_t
*new_stat
= apr_palloc(pool
, sizeof(*new_stat
));
2350 /* Shallow copy all members. */
2351 *new_stat
= *orig_stat
;
2353 /* No go back and dup the deep item. */
2354 if (orig_stat
->entry
)
2355 new_stat
->entry
= svn_wc_entry_dup(orig_stat
->entry
, pool
);
2357 /* Return the new hotness. */
2362 svn_wc_get_ignores(apr_array_header_t
**patterns
,
2364 svn_wc_adm_access_t
*adm_access
,
2367 apr_array_header_t
*default_ignores
;
2369 SVN_ERR(svn_wc_get_default_ignores(&default_ignores
, config
, pool
));
2370 SVN_ERR(collect_ignore_patterns(patterns
, default_ignores
, adm_access
,
2373 return SVN_NO_ERROR
;