2 * entries.c : manipulating the administrative `entries' file.
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_strings.h>
26 #include "svn_error.h"
27 #include "svn_types.h"
29 #include "svn_pools.h"
31 #include "svn_ctype.h"
34 #include "adm_files.h"
39 #include "svn_private_config.h"
40 #include "private/svn_wc_private.h"
45 /* The administrative `entries' file tracks information about files
46 and subdirs within a particular directory.
48 See the section on the `entries' file in libsvn_wc/README, for
49 concrete information about the XML format.
53 /*--------------------------------------------------------------- */
56 /*** reading and writing the entries file ***/
59 static svn_wc_entry_t
*
60 alloc_entry(apr_pool_t
*pool
)
62 svn_wc_entry_t
*entry
= apr_pcalloc(pool
, sizeof(*entry
));
63 entry
->revision
= SVN_INVALID_REVNUM
;
64 entry
->copyfrom_rev
= SVN_INVALID_REVNUM
;
65 entry
->cmt_rev
= SVN_INVALID_REVNUM
;
66 entry
->kind
= svn_node_none
;
67 entry
->working_size
= SVN_WC_ENTRY_WORKING_SIZE_UNKNOWN
;
68 entry
->depth
= svn_depth_infinity
;
73 /* If attribute ATTR_NAME appears in hash ATTS, set *ENTRY_FLAG to its
74 * boolean value and add MODIFY_FLAG into *MODIFY_FLAGS, else set *ENTRY_FLAG
75 * false. ENTRY_NAME is the name of the WC-entry. */
77 do_bool_attr(svn_boolean_t
*entry_flag
,
78 apr_uint64_t
*modify_flags
, apr_uint64_t modify_flag
,
79 apr_hash_t
*atts
, const char *attr_name
,
80 const char *entry_name
)
82 const char *str
= apr_hash_get(atts
, attr_name
, APR_HASH_KEY_STRING
);
87 if (strcmp(str
, "true") == 0)
89 else if (strcmp(str
, "false") == 0 || strcmp(str
, "") == 0)
92 return svn_error_createf
93 (SVN_ERR_ENTRY_ATTRIBUTE_INVALID
, NULL
,
94 _("Entry '%s' has invalid '%s' value"),
95 (entry_name
? entry_name
: SVN_WC_ENTRY_THIS_DIR
), attr_name
);
97 *modify_flags
|= modify_flag
;
102 /* Read an escaped byte on the form 'xHH' from [*BUF, END), placing
103 the byte in *RESULT. Advance *BUF to point after the escape
106 read_escaped(char *result
, char **buf
, const char *end
)
111 if (end
- *buf
< 3 || **buf
!= 'x' || ! svn_ctype_isxdigit((*buf
)[1])
112 || ! svn_ctype_isxdigit((*buf
)[2]))
113 return svn_error_create(SVN_ERR_WC_CORRUPT
, NULL
,
114 _("Invalid escape sequence"));
116 digits
[0] = *((*buf
)++);
117 digits
[1] = *((*buf
)++);
119 if ((val
= apr_strtoi64(digits
, NULL
, 16)) == 0)
120 return svn_error_create(SVN_ERR_WC_CORRUPT
, NULL
,
121 _("Invalid escaped character"));
122 *result
= (char) val
;
126 /* Read a field, possibly with escaped bytes, from [*BUF, END),
127 stopping at the terminator. Place the read string in *RESULT, or set
128 *RESULT to NULL if it is the empty string. Allocate the returned string
129 in POOL. Advance *BUF to point after the terminator. */
131 read_str(const char **result
,
132 char **buf
, const char *end
,
135 svn_stringbuf_t
*s
= NULL
;
138 return svn_error_create(SVN_ERR_WC_CORRUPT
, NULL
,
139 _("Unexpected end of entry"));
148 while (*buf
!= end
&& **buf
!= '\n')
154 s
= svn_stringbuf_ncreate(start
, *buf
- start
, pool
);
156 svn_stringbuf_appendbytes(s
, start
, *buf
- start
);
158 SVN_ERR(read_escaped(&c
, buf
, end
));
159 svn_stringbuf_appendbytes(s
, &c
, 1);
167 return svn_error_create(SVN_ERR_WC_CORRUPT
, NULL
,
168 _("Unexpected end of entry"));
172 svn_stringbuf_appendbytes(s
, start
, *buf
- start
);
176 *result
= apr_pstrndup(pool
, start
, *buf
- start
);
181 /* This is wrapper around read_str() (which see for details); it
182 simply asks svn_path_is_canonical() of the string it reads,
183 returning an error if the test fails. */
185 read_path(const char **result
,
186 char **buf
, const char *end
,
189 SVN_ERR(read_str(result
, buf
, end
, pool
));
190 if (*result
&& **result
&& (! svn_path_is_canonical(*result
, pool
)))
191 return svn_error_createf(SVN_ERR_WC_CORRUPT
, NULL
,
192 _("Entry contains non-canonical path '%s'"),
197 /* Read a field from [*BUF, END), terminated by a newline character.
198 The field may not contain escape sequences. The field is not
199 copyed and the buffer is modified in place, by replacing the
200 terminator with a NUL byte. Make *BUF point after the original
203 read_val(const char **result
,
204 char **buf
, const char *end
)
206 const char *start
= *buf
;
209 return svn_error_create(SVN_ERR_WC_CORRUPT
, NULL
,
210 _("Unexpected end of entry"));
218 while (*buf
!= end
&& **buf
!= '\n')
221 return svn_error_create(SVN_ERR_WC_CORRUPT
, NULL
,
222 _("Unexpected end of entry"));
229 /* Read a boolean field from [*BUF, END), placing the result in
230 *RESULT. If there is no boolean value (just a terminator), it
231 defaults to false. Else, the value must match FIELD_NAME, in which
232 case *RESULT will be set to true. Advance *BUF to point after the
235 read_bool(svn_boolean_t
*result
, const char *field_name
,
236 char **buf
, const char *end
)
239 SVN_ERR(read_val(&val
, buf
, end
));
242 if (strcmp(val
, field_name
) != 0)
243 return svn_error_createf(SVN_ERR_WC_CORRUPT
, NULL
,
244 _("Invalid value for field '%s'"),
253 /* Read a revision number from [*BUF, END) stopping at the
254 terminator. Set *RESULT to the revision number, or
255 SVN_INVALID_REVNUM if there is none. Use POOL for temporary
256 allocations. Make *BUF point after the terminator. */
258 read_revnum(svn_revnum_t
*result
,
265 SVN_ERR(read_val(&val
, buf
, end
));
268 *result
= SVN_STR_TO_REV(val
);
270 *result
= SVN_INVALID_REVNUM
;
275 /* Read a timestamp from [*BUF, END) stopping at the terminator.
276 Set *RESULT to the resulting timestamp, or 0 if there is none. Use
277 POOL for temporary allocations. Make *BUF point after the
280 read_time(apr_time_t
*result
,
281 char **buf
, const char *end
,
286 SVN_ERR(read_val(&val
, buf
, end
));
288 SVN_ERR(svn_time_from_cstring(result
, val
, pool
));
295 /* Allocate an entry from POOL and read it from [*BUF, END). The
296 buffer may be modified in place while parsing. Return the new
297 entry in *NEW_ENTRY. Advance *BUF to point at the end of the entry
300 read_entry(svn_wc_entry_t
**new_entry
,
301 char **buf
, const char *end
,
304 svn_wc_entry_t
*entry
= alloc_entry(pool
);
307 #define MAYBE_DONE if (**buf == '\f') goto done
309 /* Find the name and set up the entry under that name. */
310 SVN_ERR(read_path(&name
, buf
, end
, pool
));
311 entry
->name
= name
? name
: SVN_WC_ENTRY_THIS_DIR
;
316 SVN_ERR(read_val(&kindstr
, buf
, end
));
319 if (! strcmp(kindstr
, SVN_WC__ENTRIES_ATTR_FILE_STR
))
320 entry
->kind
= svn_node_file
;
321 else if (! strcmp(kindstr
, SVN_WC__ENTRIES_ATTR_DIR_STR
))
322 entry
->kind
= svn_node_dir
;
324 return svn_error_createf
325 (SVN_ERR_NODE_UNKNOWN_KIND
, NULL
,
326 _("Entry '%s' has invalid node kind"),
327 (name
? name
: SVN_WC_ENTRY_THIS_DIR
));
330 entry
->kind
= svn_node_none
;
334 /* Attempt to set revision (resolve_to_defaults may do it later, too) */
335 SVN_ERR(read_revnum(&entry
->revision
, buf
, end
, pool
));
338 /* Attempt to set up url path (again, see resolve_to_defaults). */
339 SVN_ERR(read_path(&entry
->url
, buf
, end
, pool
));
342 /* Set up repository root. Make sure it is a prefix of url. */
343 SVN_ERR(read_path(&entry
->repos
, buf
, end
, pool
));
344 if (entry
->repos
&& entry
->url
345 && ! svn_path_is_ancestor(entry
->repos
, entry
->url
))
346 return svn_error_createf(SVN_ERR_WC_CORRUPT
, NULL
,
347 _("Entry for '%s' has invalid repository "
349 name
? name
: SVN_WC_ENTRY_THIS_DIR
);
352 /* Look for a schedule attribute on this entry. */
354 const char *schedulestr
;
355 SVN_ERR(read_val(&schedulestr
, buf
, end
));
356 entry
->schedule
= svn_wc_schedule_normal
;
359 if (! strcmp(schedulestr
, SVN_WC__ENTRY_VALUE_ADD
))
360 entry
->schedule
= svn_wc_schedule_add
;
361 else if (! strcmp(schedulestr
, SVN_WC__ENTRY_VALUE_DELETE
))
362 entry
->schedule
= svn_wc_schedule_delete
;
363 else if (! strcmp(schedulestr
, SVN_WC__ENTRY_VALUE_REPLACE
))
364 entry
->schedule
= svn_wc_schedule_replace
;
366 return svn_error_createf
367 (SVN_ERR_ENTRY_ATTRIBUTE_INVALID
, NULL
,
368 _("Entry '%s' has invalid '%s' value"),
369 (name
? name
: SVN_WC_ENTRY_THIS_DIR
),
370 SVN_WC__ENTRY_ATTR_SCHEDULE
);
375 /* Attempt to set up text timestamp. */
376 SVN_ERR(read_time(&entry
->text_time
, buf
, end
, pool
));
380 SVN_ERR(read_str(&entry
->checksum
, buf
, end
, pool
));
383 /* Setup last-committed values. */
384 SVN_ERR(read_time(&entry
->cmt_date
, buf
, end
, pool
));
387 SVN_ERR(read_revnum(&entry
->cmt_rev
, buf
, end
, pool
));
390 SVN_ERR(read_str(&entry
->cmt_author
, buf
, end
, pool
));
393 /* has-props flag. */
394 SVN_ERR(read_bool(&entry
->has_props
, SVN_WC__ENTRY_ATTR_HAS_PROPS
,
398 /* has-prop-mods flag. */
399 SVN_ERR(read_bool(&entry
->has_prop_mods
, SVN_WC__ENTRY_ATTR_HAS_PROP_MODS
,
403 /* cachable-props string. */
404 SVN_ERR(read_val(&entry
->cachable_props
, buf
, end
));
405 if (entry
->cachable_props
)
406 entry
->cachable_props
= apr_pstrdup(pool
, entry
->cachable_props
);
409 /* present-props string. */
410 SVN_ERR(read_val(&entry
->present_props
, buf
, end
));
411 if (entry
->present_props
)
412 entry
->present_props
= apr_pstrdup(pool
, entry
->present_props
);
415 /* Is this entry in a state of mental torment (conflict)? */
417 SVN_ERR(read_path(&entry
->prejfile
, buf
, end
, pool
));
419 SVN_ERR(read_path(&entry
->conflict_old
, buf
, end
, pool
));
421 SVN_ERR(read_path(&entry
->conflict_new
, buf
, end
, pool
));
423 SVN_ERR(read_path(&entry
->conflict_wrk
, buf
, end
, pool
));
427 /* Is this entry copied? */
428 SVN_ERR(read_bool(&entry
->copied
, SVN_WC__ENTRY_ATTR_COPIED
, buf
, end
));
431 SVN_ERR(read_path(&entry
->copyfrom_url
, buf
, end
, pool
));
433 SVN_ERR(read_revnum(&entry
->copyfrom_rev
, buf
, end
, pool
));
436 /* Is this entry deleted? */
437 SVN_ERR(read_bool(&entry
->deleted
, SVN_WC__ENTRY_ATTR_DELETED
, buf
, end
));
440 /* Is this entry absent? */
441 SVN_ERR(read_bool(&entry
->absent
, SVN_WC__ENTRY_ATTR_ABSENT
, buf
, end
));
444 /* Is this entry incomplete? */
445 SVN_ERR(read_bool(&entry
->incomplete
, SVN_WC__ENTRY_ATTR_INCOMPLETE
,
450 SVN_ERR(read_str(&entry
->uuid
, buf
, end
, pool
));
454 SVN_ERR(read_str(&entry
->lock_token
, buf
, end
, pool
));
458 SVN_ERR(read_str(&entry
->lock_owner
, buf
, end
, pool
));
462 SVN_ERR(read_str(&entry
->lock_comment
, buf
, end
, pool
));
465 /* Lock creation date. */
466 SVN_ERR(read_time(&entry
->lock_creation_date
, buf
, end
, pool
));
470 SVN_ERR(read_str(&entry
->changelist
, buf
, end
, pool
));
473 /* Keep entry in working copy after deletion? */
474 SVN_ERR(read_bool(&entry
->keep_local
, SVN_WC__ENTRY_ATTR_KEEP_LOCAL
,
478 /* Translated size */
482 /* read_val() returns NULL on an empty (e.g. default) entry line,
483 and entry has already been initialized accordingly already */
484 SVN_ERR(read_val(&val
, buf
, end
));
486 entry
->working_size
= (apr_off_t
)apr_strtoi64(val
, NULL
, 0);
493 SVN_ERR(read_val(&result
, buf
, end
));
495 entry
->depth
= svn_depth_from_word(result
);
497 entry
->depth
= svn_depth_infinity
;
508 svn_wc__atts_to_entry(svn_wc_entry_t
**new_entry
,
509 apr_uint64_t
*modify_flags
,
513 svn_wc_entry_t
*entry
= alloc_entry(pool
);
518 /* Find the name and set up the entry under that name. */
519 name
= apr_hash_get(atts
, SVN_WC__ENTRY_ATTR_NAME
, APR_HASH_KEY_STRING
);
520 entry
->name
= name
? apr_pstrdup(pool
, name
) : SVN_WC_ENTRY_THIS_DIR
;
522 /* Attempt to set revision (resolve_to_defaults may do it later, too) */
524 const char *revision_str
525 = apr_hash_get(atts
, SVN_WC__ENTRY_ATTR_REVISION
, APR_HASH_KEY_STRING
);
529 entry
->revision
= SVN_STR_TO_REV(revision_str
);
530 *modify_flags
|= SVN_WC__ENTRY_MODIFY_REVISION
;
533 entry
->revision
= SVN_INVALID_REVNUM
;
536 /* Attempt to set up url path (again, see resolve_to_defaults). */
539 = apr_hash_get(atts
, SVN_WC__ENTRY_ATTR_URL
, APR_HASH_KEY_STRING
);
543 *modify_flags
|= SVN_WC__ENTRY_MODIFY_URL
;
544 entry
->url
= apr_pstrdup(pool
, entry
->url
);
548 /* Set up repository root. Make sure it is a prefix of url. */
550 entry
->repos
= apr_hash_get(atts
, SVN_WC__ENTRY_ATTR_REPOS
,
551 APR_HASH_KEY_STRING
);
554 if (entry
->url
&& ! svn_path_is_ancestor(entry
->repos
, entry
->url
))
555 return svn_error_createf(SVN_ERR_WC_CORRUPT
, NULL
,
556 _("Entry for '%s' has invalid repository "
558 name
? name
: SVN_WC_ENTRY_THIS_DIR
);
559 *modify_flags
|= SVN_WC__ENTRY_MODIFY_REPOS
;
560 entry
->repos
= apr_pstrdup(pool
, entry
->repos
);
567 = apr_hash_get(atts
, SVN_WC__ENTRY_ATTR_KIND
, APR_HASH_KEY_STRING
);
569 entry
->kind
= svn_node_none
;
572 if (! strcmp(kindstr
, SVN_WC__ENTRIES_ATTR_FILE_STR
))
573 entry
->kind
= svn_node_file
;
574 else if (! strcmp(kindstr
, SVN_WC__ENTRIES_ATTR_DIR_STR
))
575 entry
->kind
= svn_node_dir
;
577 return svn_error_createf
578 (SVN_ERR_NODE_UNKNOWN_KIND
, NULL
,
579 _("Entry '%s' has invalid node kind"),
580 (name
? name
: SVN_WC_ENTRY_THIS_DIR
));
581 *modify_flags
|= SVN_WC__ENTRY_MODIFY_KIND
;
585 /* Look for a schedule attribute on this entry. */
587 const char *schedulestr
588 = apr_hash_get(atts
, SVN_WC__ENTRY_ATTR_SCHEDULE
, APR_HASH_KEY_STRING
);
590 entry
->schedule
= svn_wc_schedule_normal
;
593 if (! strcmp(schedulestr
, SVN_WC__ENTRY_VALUE_ADD
))
594 entry
->schedule
= svn_wc_schedule_add
;
595 else if (! strcmp(schedulestr
, SVN_WC__ENTRY_VALUE_DELETE
))
596 entry
->schedule
= svn_wc_schedule_delete
;
597 else if (! strcmp(schedulestr
, SVN_WC__ENTRY_VALUE_REPLACE
))
598 entry
->schedule
= svn_wc_schedule_replace
;
599 else if (! strcmp(schedulestr
, ""))
600 entry
->schedule
= svn_wc_schedule_normal
;
602 return svn_error_createf
603 (SVN_ERR_ENTRY_ATTRIBUTE_INVALID
, NULL
,
604 _("Entry '%s' has invalid '%s' value"),
605 (name
? name
: SVN_WC_ENTRY_THIS_DIR
),
606 SVN_WC__ENTRY_ATTR_SCHEDULE
);
608 *modify_flags
|= SVN_WC__ENTRY_MODIFY_SCHEDULE
;
612 /* Is this entry in a state of mental torment (conflict)? */
615 = apr_hash_get(atts
, SVN_WC__ENTRY_ATTR_PREJFILE
,
616 APR_HASH_KEY_STRING
)))
618 *modify_flags
|= SVN_WC__ENTRY_MODIFY_PREJFILE
;
619 /* Normalize "" (used by the log runner) to NULL */
620 entry
->prejfile
= *(entry
->prejfile
)
621 ? apr_pstrdup(pool
, entry
->prejfile
) : NULL
;
624 if ((entry
->conflict_old
625 = apr_hash_get(atts
, SVN_WC__ENTRY_ATTR_CONFLICT_OLD
,
626 APR_HASH_KEY_STRING
)))
628 *modify_flags
|= SVN_WC__ENTRY_MODIFY_CONFLICT_OLD
;
629 /* Normalize "" (used by the log runner) to NULL */
630 entry
->conflict_old
=
631 *(entry
->conflict_old
)
632 ? apr_pstrdup(pool
, entry
->conflict_old
) : NULL
;
635 if ((entry
->conflict_new
636 = apr_hash_get(atts
, SVN_WC__ENTRY_ATTR_CONFLICT_NEW
,
637 APR_HASH_KEY_STRING
)))
639 *modify_flags
|= SVN_WC__ENTRY_MODIFY_CONFLICT_NEW
;
640 /* Normalize "" (used by the log runner) to NULL */
641 entry
->conflict_new
=
642 *(entry
->conflict_new
)
643 ? apr_pstrdup(pool
, entry
->conflict_new
) : NULL
;
646 if ((entry
->conflict_wrk
647 = apr_hash_get(atts
, SVN_WC__ENTRY_ATTR_CONFLICT_WRK
,
648 APR_HASH_KEY_STRING
)))
650 *modify_flags
|= SVN_WC__ENTRY_MODIFY_CONFLICT_WRK
;
651 /* Normalize "" (used by the log runner) to NULL */
652 entry
->conflict_wrk
=
653 *(entry
->conflict_wrk
)
654 ? apr_pstrdup(pool
, entry
->conflict_wrk
) : NULL
;
658 /* Is this entry copied? */
659 SVN_ERR(do_bool_attr(&entry
->copied
,
660 modify_flags
, SVN_WC__ENTRY_MODIFY_COPIED
,
661 atts
, SVN_WC__ENTRY_ATTR_COPIED
, name
));
665 entry
->copyfrom_url
= apr_hash_get(atts
, SVN_WC__ENTRY_ATTR_COPYFROM_URL
,
666 APR_HASH_KEY_STRING
);
667 if (entry
->copyfrom_url
)
669 *modify_flags
|= SVN_WC__ENTRY_MODIFY_COPYFROM_URL
;
670 entry
->copyfrom_url
= apr_pstrdup(pool
, entry
->copyfrom_url
);
673 revstr
= apr_hash_get(atts
, SVN_WC__ENTRY_ATTR_COPYFROM_REV
,
674 APR_HASH_KEY_STRING
);
677 entry
->copyfrom_rev
= SVN_STR_TO_REV(revstr
);
678 *modify_flags
|= SVN_WC__ENTRY_MODIFY_COPYFROM_REV
;
682 /* Is this entry deleted? */
683 SVN_ERR(do_bool_attr(&entry
->deleted
,
684 modify_flags
, SVN_WC__ENTRY_MODIFY_DELETED
,
685 atts
, SVN_WC__ENTRY_ATTR_DELETED
, name
));
687 /* Is this entry absent? */
688 SVN_ERR(do_bool_attr(&entry
->absent
,
689 modify_flags
, SVN_WC__ENTRY_MODIFY_ABSENT
,
690 atts
, SVN_WC__ENTRY_ATTR_ABSENT
, name
));
692 /* Is this entry incomplete? */
693 SVN_ERR(do_bool_attr(&entry
->incomplete
,
694 modify_flags
, SVN_WC__ENTRY_MODIFY_INCOMPLETE
,
695 atts
, SVN_WC__ENTRY_ATTR_INCOMPLETE
, name
));
697 /* Should this item be kept in the working copy after deletion? */
698 SVN_ERR(do_bool_attr(&entry
->keep_local
,
699 modify_flags
, SVN_WC__ENTRY_MODIFY_KEEP_LOCAL
,
700 atts
, SVN_WC__ENTRY_ATTR_KEEP_LOCAL
, name
));
702 /* Attempt to set up timestamps. */
704 const char *text_timestr
, *prop_timestr
;
706 text_timestr
= apr_hash_get(atts
, SVN_WC__ENTRY_ATTR_TEXT_TIME
,
707 APR_HASH_KEY_STRING
);
710 if (! strcmp(text_timestr
, SVN_WC__TIMESTAMP_WC
))
712 /* Special case: a magic string that means 'get this value
713 from the working copy' -- we ignore it here, trusting
714 that the caller of this function know what to do about
718 SVN_ERR(svn_time_from_cstring(&entry
->text_time
, text_timestr
,
721 *modify_flags
|= SVN_WC__ENTRY_MODIFY_TEXT_TIME
;
724 prop_timestr
= apr_hash_get(atts
, SVN_WC__ENTRY_ATTR_PROP_TIME
,
725 APR_HASH_KEY_STRING
);
728 if (! strcmp(prop_timestr
, SVN_WC__TIMESTAMP_WC
))
730 /* Special case: a magic string that means 'get this value
731 from the working copy' -- we ignore it here, trusting
732 that the caller of this function know what to do about
736 SVN_ERR(svn_time_from_cstring(&entry
->prop_time
, prop_timestr
,
739 *modify_flags
|= SVN_WC__ENTRY_MODIFY_PROP_TIME
;
745 entry
->checksum
= apr_hash_get(atts
, SVN_WC__ENTRY_ATTR_CHECKSUM
,
746 APR_HASH_KEY_STRING
);
749 *modify_flags
|= SVN_WC__ENTRY_MODIFY_CHECKSUM
;
750 entry
->checksum
= apr_pstrdup(pool
, entry
->checksum
);
756 entry
->uuid
= apr_hash_get(atts
, SVN_WC__ENTRY_ATTR_UUID
,
757 APR_HASH_KEY_STRING
);
760 *modify_flags
|= SVN_WC__ENTRY_MODIFY_UUID
;
761 entry
->uuid
= apr_pstrdup(pool
, entry
->uuid
);
765 /* Setup last-committed values. */
767 const char *cmt_datestr
, *cmt_revstr
;
769 cmt_datestr
= apr_hash_get(atts
, SVN_WC__ENTRY_ATTR_CMT_DATE
,
770 APR_HASH_KEY_STRING
);
773 SVN_ERR(svn_time_from_cstring(&entry
->cmt_date
, cmt_datestr
, pool
));
774 *modify_flags
|= SVN_WC__ENTRY_MODIFY_CMT_DATE
;
779 cmt_revstr
= apr_hash_get(atts
, SVN_WC__ENTRY_ATTR_CMT_REV
,
780 APR_HASH_KEY_STRING
);
783 entry
->cmt_rev
= SVN_STR_TO_REV(cmt_revstr
);
784 *modify_flags
|= SVN_WC__ENTRY_MODIFY_CMT_REV
;
787 entry
->cmt_rev
= SVN_INVALID_REVNUM
;
789 entry
->cmt_author
= apr_hash_get(atts
, SVN_WC__ENTRY_ATTR_CMT_AUTHOR
,
790 APR_HASH_KEY_STRING
);
791 if (entry
->cmt_author
)
793 *modify_flags
|= SVN_WC__ENTRY_MODIFY_CMT_AUTHOR
;
794 entry
->cmt_author
= apr_pstrdup(pool
, entry
->cmt_author
);
799 entry
->lock_token
= apr_hash_get(atts
, SVN_WC__ENTRY_ATTR_LOCK_TOKEN
,
800 APR_HASH_KEY_STRING
);
801 if (entry
->lock_token
)
803 *modify_flags
|= SVN_WC__ENTRY_MODIFY_LOCK_TOKEN
;
804 entry
->lock_token
= apr_pstrdup(pool
, entry
->lock_token
);
808 entry
->lock_owner
= apr_hash_get(atts
, SVN_WC__ENTRY_ATTR_LOCK_OWNER
,
809 APR_HASH_KEY_STRING
);
810 if (entry
->lock_owner
)
812 *modify_flags
|= SVN_WC__ENTRY_MODIFY_LOCK_OWNER
;
813 entry
->lock_owner
= apr_pstrdup(pool
, entry
->lock_owner
);
817 entry
->lock_comment
= apr_hash_get(atts
, SVN_WC__ENTRY_ATTR_LOCK_COMMENT
,
818 APR_HASH_KEY_STRING
);
819 if (entry
->lock_comment
)
821 *modify_flags
|= SVN_WC__ENTRY_MODIFY_LOCK_COMMENT
;
822 entry
->lock_comment
= apr_pstrdup(pool
, entry
->lock_comment
);
825 /* lock creation date. */
827 const char *cdate_str
=
828 apr_hash_get(atts
, SVN_WC__ENTRY_ATTR_LOCK_CREATION_DATE
,
829 APR_HASH_KEY_STRING
);
832 SVN_ERR(svn_time_from_cstring(&entry
->lock_creation_date
,
834 *modify_flags
|= SVN_WC__ENTRY_MODIFY_LOCK_CREATION_DATE
;
839 entry
->changelist
= apr_hash_get(atts
, SVN_WC__ENTRY_ATTR_CHANGELIST
,
840 APR_HASH_KEY_STRING
);
841 if (entry
->changelist
)
843 *modify_flags
|= SVN_WC__ENTRY_MODIFY_CHANGELIST
;
844 entry
->changelist
= apr_pstrdup(pool
, entry
->changelist
);
847 /* has-props flag. */
848 SVN_ERR(do_bool_attr(&entry
->has_props
,
849 modify_flags
, SVN_WC__ENTRY_MODIFY_HAS_PROPS
,
850 atts
, SVN_WC__ENTRY_ATTR_HAS_PROPS
, name
));
852 /* has-prop-mods flag. */
854 const char *has_prop_mods_str
855 = apr_hash_get(atts
, SVN_WC__ENTRY_ATTR_HAS_PROP_MODS
,
856 APR_HASH_KEY_STRING
);
858 if (has_prop_mods_str
)
860 if (strcmp(has_prop_mods_str
, "true") == 0)
861 entry
->has_prop_mods
= TRUE
;
862 else if (strcmp(has_prop_mods_str
, "false") != 0)
863 return svn_error_createf
864 (SVN_ERR_ENTRY_ATTRIBUTE_INVALID
, NULL
,
865 _("Entry '%s' has invalid '%s' value"),
866 (name
? name
: SVN_WC_ENTRY_THIS_DIR
),
867 SVN_WC__ENTRY_ATTR_HAS_PROP_MODS
);
869 *modify_flags
|= SVN_WC__ENTRY_MODIFY_HAS_PROP_MODS
;
873 /* cachable-props string. */
874 entry
->cachable_props
= apr_hash_get(atts
,
875 SVN_WC__ENTRY_ATTR_CACHABLE_PROPS
,
876 APR_HASH_KEY_STRING
);
877 if (entry
->cachable_props
)
879 *modify_flags
|= SVN_WC__ENTRY_MODIFY_CACHABLE_PROPS
;
880 entry
->cachable_props
= apr_pstrdup(pool
, entry
->cachable_props
);
883 /* present-props string. */
884 entry
->present_props
= apr_hash_get(atts
,
885 SVN_WC__ENTRY_ATTR_PRESENT_PROPS
,
886 APR_HASH_KEY_STRING
);
887 if (entry
->present_props
)
889 *modify_flags
|= SVN_WC__ENTRY_MODIFY_PRESENT_PROPS
;
890 entry
->present_props
= apr_pstrdup(pool
, entry
->present_props
);
893 /* Translated size */
897 SVN_WC__ENTRY_ATTR_WORKING_SIZE
,
898 APR_HASH_KEY_STRING
);
901 if (! strcmp(val
, SVN_WC__WORKING_SIZE_WC
))
903 /* Special case (same as the timestamps); ignore here
904 these will be handled elsewhere */
907 /* Cast to off_t; it's safe: we put in an off_t to start with... */
908 entry
->working_size
= (apr_off_t
)apr_strtoi64(val
, NULL
, 0);
910 *modify_flags
|= SVN_WC__ENTRY_MODIFY_WORKING_SIZE
;
918 /* Used when reading an entries file in XML format. */
919 struct entries_accumulator
921 /* Keys are entry names, vals are (struct svn_wc_entry_t *)'s. */
924 /* The parser that's parsing it, for signal_expat_bailout(). */
925 svn_xml_parser_t
*parser
;
927 /* Should we include 'deleted' entries in the hash? */
928 svn_boolean_t show_hidden
;
930 /* Don't leave home without one. */
933 /* Cleared before handling each entry. */
934 apr_pool_t
*scratch_pool
;
938 /* Called whenever we find an <open> tag of some kind. */
940 handle_start_tag(void *userData
, const char *tagname
, const char **atts
)
942 struct entries_accumulator
*accum
= userData
;
943 apr_hash_t
*attributes
;
944 svn_wc_entry_t
*entry
;
946 apr_uint64_t modify_flags
= 0;
948 /* We only care about the `entry' tag; all other tags, such as `xml'
949 and `wc-entries', are ignored. */
950 if (strcmp(tagname
, SVN_WC__ENTRIES_ENTRY
))
953 svn_pool_clear(accum
->scratch_pool
);
954 /* Make an entry from the attributes. */
955 attributes
= svn_xml_make_att_hash(atts
, accum
->scratch_pool
);
956 err
= svn_wc__atts_to_entry(&entry
, &modify_flags
, attributes
, accum
->pool
);
959 svn_xml_signal_bailout(err
, accum
->parser
);
963 /* Find the name and set up the entry under that name. This
964 should *NOT* be NULL, since svn_wc__atts_to_entry() should
965 have made it into SVN_WC_ENTRY_THIS_DIR. (Note that technically,
966 an entry can't be both absent and scheduled for addition, but we
967 don't need a sanity check for that here.) */
968 if ((entry
->deleted
|| entry
->absent
)
969 && (entry
->schedule
!= svn_wc_schedule_add
)
970 && (entry
->schedule
!= svn_wc_schedule_replace
)
971 && (! accum
->show_hidden
))
974 apr_hash_set(accum
->entries
, entry
->name
, APR_HASH_KEY_STRING
, entry
);
977 /* Parse BUF of size SIZE as an entries file in XML format, storing the parsed
978 entries in ENTRIES. Use pool for temporary allocations and the pool of
979 ADM_ACCESS for the returned entries. */
981 parse_entries_xml(svn_wc_adm_access_t
*adm_access
,
983 svn_boolean_t show_hidden
,
988 svn_xml_parser_t
*svn_parser
;
989 struct entries_accumulator accum
;
991 /* Set up userData for the XML parser. */
992 accum
.entries
= entries
;
993 accum
.show_hidden
= show_hidden
;
994 accum
.pool
= svn_wc_adm_access_pool(adm_access
);
995 accum
.scratch_pool
= svn_pool_create(pool
);
997 /* Create the XML parser */
998 svn_parser
= svn_xml_make_parser(&accum
,
1004 /* Store parser in its own userdata, so callbacks can call
1005 svn_xml_signal_bailout() */
1006 accum
.parser
= svn_parser
;
1009 SVN_ERR_W(svn_xml_parse(svn_parser
, buf
, size
, TRUE
),
1011 _("XML parser failed in '%s'"),
1012 svn_path_local_style
1013 (svn_wc_adm_access_path(adm_access
), pool
)));
1015 svn_pool_destroy(accum
.scratch_pool
);
1017 /* Clean up the XML parser */
1018 svn_xml_free_parser(svn_parser
);
1020 return SVN_NO_ERROR
;
1025 /* Use entry SRC to fill in blank portions of entry DST. SRC itself
1026 may not have any blanks, of course.
1027 Typically, SRC is a parent directory's own entry, and DST is some
1028 child in that directory. */
1030 take_from_entry(svn_wc_entry_t
*src
, svn_wc_entry_t
*dst
, apr_pool_t
*pool
)
1032 /* Inherits parent's revision if doesn't have a revision of one's
1033 own, unless this is a subdirectory. */
1034 if ((dst
->revision
== SVN_INVALID_REVNUM
) && (dst
->kind
!= svn_node_dir
))
1035 dst
->revision
= src
->revision
;
1037 /* Inherits parent's url if doesn't have a url of one's own. */
1039 dst
->url
= svn_path_url_add_component(src
->url
, dst
->name
, pool
);
1042 dst
->repos
= src
->repos
;
1045 && (! ((dst
->schedule
== svn_wc_schedule_add
)
1046 || (dst
->schedule
== svn_wc_schedule_replace
))))
1048 dst
->uuid
= src
->uuid
;
1051 if (! dst
->cachable_props
)
1052 dst
->cachable_props
= src
->cachable_props
;
1056 /* Resolve any missing information in ENTRIES by deducing from the
1057 directory's own entry (which must already be present in ENTRIES). */
1058 static svn_error_t
*
1059 resolve_to_defaults(apr_hash_t
*entries
,
1062 apr_hash_index_t
*hi
;
1063 svn_wc_entry_t
*default_entry
1064 = apr_hash_get(entries
, SVN_WC_ENTRY_THIS_DIR
, APR_HASH_KEY_STRING
);
1066 /* First check the dir's own entry for consistency. */
1067 if (! default_entry
)
1068 return svn_error_create(SVN_ERR_ENTRY_NOT_FOUND
,
1070 _("Missing default entry"));
1072 if (default_entry
->revision
== SVN_INVALID_REVNUM
)
1073 return svn_error_create(SVN_ERR_ENTRY_MISSING_REVISION
,
1075 _("Default entry has no revision number"));
1077 if (! default_entry
->url
)
1078 return svn_error_create(SVN_ERR_ENTRY_MISSING_URL
,
1080 _("Default entry is missing URL"));
1083 /* Then use it to fill in missing information in other entries. */
1084 for (hi
= apr_hash_first(pool
, entries
); hi
; hi
= apr_hash_next(hi
))
1087 svn_wc_entry_t
*this_entry
;
1089 apr_hash_this(hi
, NULL
, NULL
, &val
);
1092 if (this_entry
== default_entry
)
1093 /* THIS_DIR already has all the information it can possibly
1097 if (this_entry
->kind
== svn_node_dir
)
1098 /* Entries that are directories have everything but their
1099 name, kind, and state stored in the THIS_DIR entry of the
1100 directory itself. However, we are disallowing the perusing
1101 of any entries outside of the current entries file. If a
1102 caller wants more info about a directory, it should look in
1103 the entries file in the directory. */
1106 if (this_entry
->kind
== svn_node_file
)
1107 /* For file nodes that do not explicitly have their ancestry
1108 stated, this can be derived from the default entry of the
1109 directory in which those files reside. */
1110 take_from_entry(default_entry
, this_entry
, pool
);
1113 return SVN_NO_ERROR
;
1118 /* Fill the entries cache in ADM_ACCESS. Either the full hash cache will be
1119 populated, if SHOW_HIDDEN is TRUE, or the truncated hash cache will be
1120 populated if SHOW_HIDDEN is FALSE. POOL is used for local memory
1121 allocation, the access baton pool is used for the cache. */
1122 static svn_error_t
*
1123 read_entries(svn_wc_adm_access_t
*adm_access
,
1124 svn_boolean_t show_hidden
,
1127 const char *path
= svn_wc_adm_access_path(adm_access
);
1128 apr_file_t
*infile
= NULL
;
1129 svn_stringbuf_t
*buf
= svn_stringbuf_create("", pool
);
1130 apr_hash_t
*entries
= apr_hash_make(svn_wc_adm_access_pool(adm_access
));
1132 svn_wc_entry_t
*entry
;
1135 /* Open the entries file. */
1136 SVN_ERR(svn_wc__open_adm_file(&infile
, path
,
1137 SVN_WC__ADM_ENTRIES
, APR_READ
, pool
));
1139 SVN_ERR(svn_stringbuf_from_aprfile(&buf
, infile
, pool
));
1142 endp
= buf
->data
+ buf
->len
;
1144 /* If the first byte of the file is not a digit, then it is probably in XML
1146 if (curp
!= endp
&& !svn_ctype_isdigit(*curp
))
1147 SVN_ERR(parse_entries_xml(adm_access
, entries
, show_hidden
,
1148 buf
->data
, buf
->len
, pool
));
1151 /* Skip format line. */
1152 /* ### Could read it here and report it to caller if it wants it. */
1153 curp
= memchr(curp
, '\n', buf
->len
);
1155 return svn_error_createf(SVN_ERR_WC_CORRUPT
, NULL
,
1156 _("Invalid version line in entries file "
1158 svn_path_local_style(path
, pool
));
1162 while (curp
!= endp
)
1164 svn_error_t
*err
= read_entry(&entry
, &curp
, endp
,
1165 svn_wc_adm_access_pool(adm_access
));
1168 /* We allow extra fields at the end of the line, for
1170 curp
= memchr(curp
, '\f', endp
- curp
);
1172 err
= svn_error_create(SVN_ERR_WC_CORRUPT
, NULL
,
1173 _("Missing entry terminator"));
1174 if (! err
&& (curp
== endp
|| *(++curp
) != '\n'))
1175 err
= svn_error_create(SVN_ERR_WC_CORRUPT
, NULL
,
1176 _("Invalid entry terminator"));
1179 return svn_error_createf(err
->apr_err
, err
,
1180 _("Error at entry %d in entries file for "
1182 entryno
, svn_path_local_style(path
, pool
));
1186 if ((entry
->deleted
|| entry
->absent
)
1187 && (entry
->schedule
!= svn_wc_schedule_add
)
1188 && (entry
->schedule
!= svn_wc_schedule_replace
)
1192 apr_hash_set(entries
, entry
->name
, APR_HASH_KEY_STRING
, entry
);
1196 /* Close the entries file. */
1197 SVN_ERR(svn_wc__close_adm_file(infile
, svn_wc_adm_access_path(adm_access
),
1198 SVN_WC__ADM_ENTRIES
, 0, pool
));
1200 /* Fill in any implied fields. */
1201 SVN_ERR(resolve_to_defaults(entries
, svn_wc_adm_access_pool(adm_access
)));
1203 svn_wc__adm_access_set_entries(adm_access
, show_hidden
, entries
);
1205 return SVN_NO_ERROR
;
1208 /* For non-directory PATHs full entry information is obtained by reading
1209 * the entries for the parent directory of PATH and then extracting PATH's
1210 * entry. If PATH is a directory then only abrieviated information is
1211 * available in the parent directory, more complete information is
1212 * available by reading the entries for PATH itself.
1214 * Note: There is one bit of information about directories that is only
1215 * available in the parent directory, that is the "deleted" state. If PATH
1216 * is a versioned directory then the "deleted" state information will not
1217 * be returned in ENTRY. This means some bits of the code (e.g. revert)
1218 * need to obtain it by directly extracting the directory entry from the
1219 * parent directory's entries. I wonder if this function should handle
1223 svn_wc_entry(const svn_wc_entry_t
**entry
,
1225 svn_wc_adm_access_t
*adm_access
,
1226 svn_boolean_t show_hidden
,
1229 const char *entry_name
;
1230 svn_wc_adm_access_t
*dir_access
;
1232 SVN_ERR(svn_wc__adm_retrieve_internal(&dir_access
, adm_access
, path
, pool
));
1235 const char *dir_path
, *base_name
;
1236 svn_path_split(path
, &dir_path
, &base_name
, pool
);
1237 SVN_ERR(svn_wc__adm_retrieve_internal(&dir_access
, adm_access
, dir_path
,
1239 entry_name
= base_name
;
1242 entry_name
= SVN_WC_ENTRY_THIS_DIR
;
1246 apr_hash_t
*entries
;
1247 SVN_ERR(svn_wc_entries_read(&entries
, dir_access
, show_hidden
, pool
));
1248 *entry
= apr_hash_get(entries
, entry_name
, APR_HASH_KEY_STRING
);
1253 return SVN_NO_ERROR
;
1258 svn_wc__entry_versioned_internal(const svn_wc_entry_t
**entry
,
1260 svn_wc_adm_access_t
*adm_access
,
1261 svn_boolean_t show_hidden
,
1262 const char *caller_filename
,
1266 SVN_ERR(svn_wc_entry(entry
, path
, adm_access
, show_hidden
, pool
));
1271 = svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND
, NULL
,
1272 _("'%s' is not under version control"),
1273 svn_path_local_style(path
, pool
));
1275 err
->file
= caller_filename
;
1276 err
->line
= caller_lineno
;
1280 return SVN_NO_ERROR
;
1285 /* This is #if 0'd out until I decide where to use it. --cmpilato */
1287 /* Run a simple validity check on the ENTRIES (the list of entries
1288 associated with the directory PATH). */
1289 static svn_error_t
*
1290 check_entries(apr_hash_t
*entries
,
1294 svn_wc_entry_t
*default_entry
;
1295 apr_hash_index_t
*hi
;
1297 default_entry
= apr_hash_get(entries
,
1298 SVN_WC_ENTRY_THIS_DIR
,
1299 APR_HASH_KEY_STRING
);
1300 if (! default_entry
)
1301 return svn_error_createf
1302 (SVN_ERR_WC_CORRUPT
, NULL
,
1303 _("Corrupt working copy: '%s' has no default entry"),
1304 svn_path_local_style(path
, pool
));
1306 /* Validate DEFAULT_ENTRY's current schedule. */
1307 switch (default_entry
->schedule
)
1309 case svn_wc_schedule_normal
:
1310 case svn_wc_schedule_add
:
1311 case svn_wc_schedule_delete
:
1312 case svn_wc_schedule_replace
:
1313 /* These are all valid states */
1317 /* This is an invalid state */
1318 return svn_error_createf
1319 (SVN_ERR_WC_CORRUPT
, NULL
,
1320 _("Corrupt working copy: directory '%s' has an invalid schedule"),
1321 svn_path_local_style(path
, pool
));
1324 for (hi
= apr_hash_first(pool
, entries
); hi
; hi
= apr_hash_next(hi
))
1329 svn_wc_entry_t
*this_entry
;
1332 apr_hash_this(hi
, &key
, NULL
, &val
);
1336 /* We've already checked the "this dir" entry */
1337 if (! strcmp(name
, SVN_WC_ENTRY_THIS_DIR
))
1340 /* Validate THIS_ENTRY's current schedule. */
1341 switch (this_entry
->schedule
)
1343 case svn_wc_schedule_normal
:
1344 case svn_wc_schedule_add
:
1345 case svn_wc_schedule_delete
:
1346 case svn_wc_schedule_replace
:
1347 /* These are all valid states */
1351 /* This is an invalid state */
1352 return svn_error_createf
1353 (SVN_ERR_WC_CORRUPT
, NULL
,
1354 _("Corrupt working copy: "
1355 "'%s' in directory '%s' has an invalid schedule"),
1356 name
, svn_path_local_style(path
, pool
));
1359 if ((default_entry
->schedule
== svn_wc_schedule_add
)
1360 && (this_entry
->schedule
!= svn_wc_schedule_add
))
1361 return svn_error_createf
1362 (SVN_ERR_WC_CORRUPT
, NULL
,
1363 _("Corrupt working copy: '%s' in directory '%s' (which is "
1364 "scheduled for addition) is not itself scheduled for addition"),
1365 name
, svn_path_local_style(path
, pool
));
1367 if ((default_entry
->schedule
== svn_wc_schedule_delete
)
1368 && (this_entry
->schedule
!= svn_wc_schedule_delete
))
1369 return svn_error_createf
1370 (SVN_ERR_WC_CORRUPT
, NULL
,
1371 _("Corrupt working copy: '%s' in directory '%s' (which is "
1372 "scheduled for deletion) is not itself scheduled for deletion"),
1373 name
, svn_path_local_style(path
, pool
));
1375 if ((default_entry
->schedule
== svn_wc_schedule_replace
)
1376 && (this_entry
->schedule
== svn_wc_schedule_normal
))
1377 return svn_error_createf
1378 (SVN_ERR_WC_CORRUPT
, NULL
,
1379 _("Corrupt working copy: '%s' in directory '%s' (which is "
1380 "scheduled for replacement) has an invalid schedule"),
1381 name
, svn_path_local_style(path
, pool
));
1384 return SVN_NO_ERROR
;
1390 svn_wc_entries_read(apr_hash_t
**entries
,
1391 svn_wc_adm_access_t
*adm_access
,
1392 svn_boolean_t show_hidden
,
1395 apr_hash_t
*new_entries
;
1397 new_entries
= svn_wc__adm_access_entries(adm_access
, show_hidden
, pool
);
1400 /* Ask for the deleted entries because most operations request them
1401 at some stage, getting them now avoids a second file parse. */
1402 SVN_ERR(read_entries(adm_access
, TRUE
, pool
));
1404 new_entries
= svn_wc__adm_access_entries(adm_access
, show_hidden
, pool
);
1407 *entries
= new_entries
;
1408 return SVN_NO_ERROR
;
1411 /* If STR is non-null, append STR to BUF, terminating it with a
1412 newline, escaping bytes that needs escaping, using POOL for
1413 temporary allocations. Else if STR is null, just append the
1414 terminating newline. */
1416 write_str(svn_stringbuf_t
*buf
, const char *str
, apr_pool_t
*pool
)
1418 const char *start
= str
;
1423 /* Escape control characters and | and \. */
1424 if (svn_ctype_iscntrl(*str
) || *str
== '\\')
1426 svn_stringbuf_appendbytes(buf
, start
, str
- start
);
1427 svn_stringbuf_appendcstr(buf
,
1428 apr_psprintf(pool
, "\\x%02x", *str
));
1433 svn_stringbuf_appendbytes(buf
, start
, str
- start
);
1435 svn_stringbuf_appendbytes(buf
, "\n", 1);
1438 /* Append the string VAL of length LEN to BUF, without escaping any
1439 bytes, followed by a terminator. If VAL is NULL, ignore LEN and
1440 append just the terminator. */
1442 write_val(svn_stringbuf_t
*buf
, const char *val
, apr_size_t len
)
1445 svn_stringbuf_appendbytes(buf
, val
, len
);
1446 svn_stringbuf_appendbytes(buf
, "\n", 1);
1449 /* If VAL is true, append FIELD_NAME followed by a terminator to BUF.
1450 Else, just append the terminator. */
1452 write_bool(svn_stringbuf_t
*buf
, const char *field_name
, svn_boolean_t val
)
1454 write_val(buf
, val
? field_name
: NULL
, val
? strlen(field_name
) : 0);
1457 /* If REVNUM is valid, append the representation of REVNUM to BUF
1458 followed by a terminator, using POOL for temporary allocations.
1459 Otherwise, just append the terminator. */
1461 write_revnum(svn_stringbuf_t
*buf
, svn_revnum_t revnum
, apr_pool_t
*pool
)
1463 if (SVN_IS_VALID_REVNUM(revnum
))
1464 svn_stringbuf_appendcstr(buf
, apr_ltoa(pool
, revnum
));
1465 svn_stringbuf_appendbytes(buf
, "\n", 1);
1468 /* Append the timestamp VAL to BUF (or the empty string if VAL is 0),
1469 followed by a terminator. Use POOL for temporary allocations. */
1471 write_time(svn_stringbuf_t
*buf
, apr_time_t val
, apr_pool_t
*pool
)
1474 svn_stringbuf_appendcstr(buf
, svn_time_to_cstring(val
, pool
));
1475 svn_stringbuf_appendbytes(buf
, "\n", 1);
1478 /* Append a single entry ENTRY to the string OUTPUT, using the
1479 entry for "this dir" THIS_DIR for comparison/optimization.
1480 Allocations are done in POOL. */
1482 write_entry(svn_stringbuf_t
*buf
,
1483 svn_wc_entry_t
*entry
,
1485 svn_wc_entry_t
*this_dir
,
1488 const char *valuestr
;
1489 svn_revnum_t valuerev
;
1490 svn_boolean_t is_this_dir
= strcmp(name
, SVN_WC_ENTRY_THIS_DIR
) == 0;
1491 svn_boolean_t is_subdir
= ! is_this_dir
&& (entry
->kind
== svn_node_dir
);
1496 write_str(buf
, name
, pool
);
1499 switch (entry
->kind
)
1502 write_val(buf
, SVN_WC__ENTRIES_ATTR_DIR_STR
,
1503 sizeof(SVN_WC__ENTRIES_ATTR_DIR_STR
) - 1);
1507 write_val(buf
, NULL
, 0);
1511 case svn_node_unknown
:
1513 write_val(buf
, SVN_WC__ENTRIES_ATTR_FILE_STR
,
1514 sizeof(SVN_WC__ENTRIES_ATTR_FILE_STR
) - 1);
1519 if (is_this_dir
|| (! is_subdir
&& entry
->revision
!= this_dir
->revision
))
1520 valuerev
= entry
->revision
;
1522 valuerev
= SVN_INVALID_REVNUM
;
1523 write_revnum(buf
, valuerev
, pool
);
1527 (! is_subdir
&& strcmp(svn_path_url_add_component(this_dir
->url
, name
,
1530 valuestr
= entry
->url
;
1533 write_str(buf
, valuestr
, pool
);
1535 /* Repository root. */
1538 || (this_dir
->repos
== NULL
1540 && strcmp(this_dir
->repos
, entry
->repos
) != 0))))
1541 valuestr
= entry
->repos
;
1544 write_str(buf
, valuestr
, pool
);
1547 switch (entry
->schedule
)
1549 case svn_wc_schedule_add
:
1550 write_val(buf
, SVN_WC__ENTRY_VALUE_ADD
,
1551 sizeof(SVN_WC__ENTRY_VALUE_ADD
) - 1);
1554 case svn_wc_schedule_delete
:
1555 write_val(buf
, SVN_WC__ENTRY_VALUE_DELETE
,
1556 sizeof(SVN_WC__ENTRY_VALUE_DELETE
) - 1);
1559 case svn_wc_schedule_replace
:
1560 write_val(buf
, SVN_WC__ENTRY_VALUE_REPLACE
,
1561 sizeof(SVN_WC__ENTRY_VALUE_REPLACE
) - 1);
1564 case svn_wc_schedule_normal
:
1566 write_val(buf
, NULL
, 0);
1571 write_time(buf
, entry
->text_time
, pool
);
1574 write_val(buf
, entry
->checksum
,
1575 entry
->checksum
? strlen(entry
->checksum
) : 0);
1577 /* Last-commit stuff */
1578 write_time(buf
, entry
->cmt_date
, pool
);
1579 write_revnum(buf
, entry
->cmt_rev
, pool
);
1580 write_str(buf
, entry
->cmt_author
, pool
);
1582 /* has-props flag. */
1583 write_bool(buf
, SVN_WC__ENTRY_ATTR_HAS_PROPS
, entry
->has_props
);
1585 /* has-prop-mods flag. */
1586 write_bool(buf
, SVN_WC__ENTRY_ATTR_HAS_PROP_MODS
, entry
->has_prop_mods
);
1588 /* cachable-props string. */
1590 || ! this_dir
->cachable_props
|| ! entry
->cachable_props
1591 || strcmp(this_dir
->cachable_props
, entry
->cachable_props
) != 0)
1592 valuestr
= entry
->cachable_props
;
1595 write_val(buf
, valuestr
, valuestr
? strlen(valuestr
) : 0);
1597 /* present-props string. */
1598 write_val(buf
, entry
->present_props
,
1599 entry
->present_props
? strlen(entry
->present_props
) : 0);
1602 write_str(buf
, entry
->prejfile
, pool
);
1603 write_str(buf
, entry
->conflict_old
, pool
);
1604 write_str(buf
, entry
->conflict_new
, pool
);
1605 write_str(buf
, entry
->conflict_wrk
, pool
);
1607 write_bool(buf
, SVN_WC__ENTRY_ATTR_COPIED
, entry
->copied
);
1609 /* Copy-related Stuff */
1610 write_str(buf
, entry
->copyfrom_url
, pool
);
1611 write_revnum(buf
, entry
->copyfrom_rev
, pool
);
1614 write_bool(buf
, SVN_WC__ENTRY_ATTR_DELETED
, entry
->deleted
);
1617 write_bool(buf
, SVN_WC__ENTRY_ATTR_ABSENT
, entry
->absent
);
1619 /* Incomplete state */
1620 write_bool(buf
, SVN_WC__ENTRY_ATTR_INCOMPLETE
, entry
->incomplete
);
1623 if (is_this_dir
|| ! this_dir
->uuid
|| ! entry
->uuid
1624 || strcmp(this_dir
->uuid
, entry
->uuid
) != 0)
1625 valuestr
= entry
->uuid
;
1628 write_val(buf
, valuestr
, valuestr
? strlen(valuestr
) : 0);
1631 write_str(buf
, entry
->lock_token
, pool
);
1634 write_str(buf
, entry
->lock_owner
, pool
);
1637 write_str(buf
, entry
->lock_comment
, pool
);
1639 /* Lock creation date. */
1640 write_time(buf
, entry
->lock_creation_date
, pool
);
1643 write_str(buf
, entry
->changelist
, pool
);
1645 /* Keep in working copy flag. */
1646 write_bool(buf
, SVN_WC__ENTRY_ATTR_KEEP_LOCAL
, entry
->keep_local
);
1648 /* Translated size */
1651 = (entry
->working_size
!= SVN_WC_ENTRY_WORKING_SIZE_UNKNOWN
)
1652 ? apr_off_t_toa(pool
, entry
->working_size
) : "";
1653 write_val(buf
, val
, strlen(val
));
1657 if (is_subdir
|| entry
->depth
== svn_depth_infinity
)
1659 write_val(buf
, NULL
, 0);
1663 const char *val
= svn_depth_to_word(entry
->depth
);
1664 write_val(buf
, val
, strlen(val
));
1667 /* Remove redundant separators at the end of the entry. */
1668 while (buf
->len
> 1 && buf
->data
[buf
->len
- 2] == '\n')
1671 svn_stringbuf_appendbytes(buf
, "\f\n", 2);
1674 /* Append a single entry ENTRY as an XML element to the string OUTPUT,
1675 using the entry for "this dir" THIS_DIR for
1676 comparison/optimization. Allocations are done in POOL. */
1678 write_entry_xml(svn_stringbuf_t
**output
,
1679 svn_wc_entry_t
*entry
,
1681 svn_wc_entry_t
*this_dir
,
1684 apr_hash_t
*atts
= apr_hash_make(pool
);
1685 const char *valuestr
;
1687 /*** Create a hash that represents an entry. ***/
1692 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_NAME
, APR_HASH_KEY_STRING
,
1696 if (SVN_IS_VALID_REVNUM(entry
->revision
))
1697 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_REVISION
, APR_HASH_KEY_STRING
,
1698 apr_psprintf(pool
, "%ld", entry
->revision
));
1702 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_URL
, APR_HASH_KEY_STRING
,
1705 /* Repository root */
1707 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_REPOS
, APR_HASH_KEY_STRING
,
1711 switch (entry
->kind
)
1714 valuestr
= SVN_WC__ENTRIES_ATTR_DIR_STR
;
1722 case svn_node_unknown
:
1724 valuestr
= SVN_WC__ENTRIES_ATTR_FILE_STR
;
1727 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_KIND
, APR_HASH_KEY_STRING
, valuestr
);
1730 switch (entry
->schedule
)
1732 case svn_wc_schedule_add
:
1733 valuestr
= SVN_WC__ENTRY_VALUE_ADD
;
1736 case svn_wc_schedule_delete
:
1737 valuestr
= SVN_WC__ENTRY_VALUE_DELETE
;
1740 case svn_wc_schedule_replace
:
1741 valuestr
= SVN_WC__ENTRY_VALUE_REPLACE
;
1744 case svn_wc_schedule_normal
:
1749 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_SCHEDULE
, APR_HASH_KEY_STRING
,
1753 if (entry
->conflict_old
)
1754 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_CONFLICT_OLD
, APR_HASH_KEY_STRING
,
1755 entry
->conflict_old
);
1757 if (entry
->conflict_new
)
1758 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_CONFLICT_NEW
, APR_HASH_KEY_STRING
,
1759 entry
->conflict_new
);
1761 if (entry
->conflict_wrk
)
1762 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_CONFLICT_WRK
, APR_HASH_KEY_STRING
,
1763 entry
->conflict_wrk
);
1765 if (entry
->prejfile
)
1766 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_PREJFILE
, APR_HASH_KEY_STRING
,
1769 /* Copy-related Stuff */
1770 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_COPIED
, APR_HASH_KEY_STRING
,
1771 (entry
->copied
? "true" : NULL
));
1773 if (SVN_IS_VALID_REVNUM(entry
->copyfrom_rev
))
1774 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_COPYFROM_REV
, APR_HASH_KEY_STRING
,
1775 apr_psprintf(pool
, "%ld",
1776 entry
->copyfrom_rev
));
1778 if (entry
->copyfrom_url
)
1779 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_COPYFROM_URL
, APR_HASH_KEY_STRING
,
1780 entry
->copyfrom_url
);
1783 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_DELETED
, APR_HASH_KEY_STRING
,
1784 (entry
->deleted
? "true" : NULL
));
1787 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_ABSENT
, APR_HASH_KEY_STRING
,
1788 (entry
->absent
? "true" : NULL
));
1790 /* Incomplete state */
1791 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_INCOMPLETE
, APR_HASH_KEY_STRING
,
1792 (entry
->incomplete
? "true" : NULL
));
1795 if (entry
->text_time
)
1797 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_TEXT_TIME
, APR_HASH_KEY_STRING
,
1798 svn_time_to_cstring(entry
->text_time
, pool
));
1800 if (entry
->prop_time
)
1802 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_PROP_TIME
, APR_HASH_KEY_STRING
,
1803 svn_time_to_cstring(entry
->prop_time
, pool
));
1807 if (entry
->checksum
)
1808 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_CHECKSUM
, APR_HASH_KEY_STRING
,
1811 /* Last-commit stuff */
1812 if (SVN_IS_VALID_REVNUM(entry
->cmt_rev
))
1813 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_CMT_REV
, APR_HASH_KEY_STRING
,
1814 apr_psprintf(pool
, "%ld", entry
->cmt_rev
));
1816 if (entry
->cmt_author
)
1817 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_CMT_AUTHOR
, APR_HASH_KEY_STRING
,
1821 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_UUID
, APR_HASH_KEY_STRING
,
1824 if (entry
->cmt_date
)
1826 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_CMT_DATE
, APR_HASH_KEY_STRING
,
1827 svn_time_to_cstring(entry
->cmt_date
, pool
));
1831 if (entry
->lock_token
)
1832 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_LOCK_TOKEN
, APR_HASH_KEY_STRING
,
1836 if (entry
->lock_owner
)
1837 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_LOCK_OWNER
, APR_HASH_KEY_STRING
,
1841 if (entry
->lock_comment
)
1842 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_LOCK_COMMENT
, APR_HASH_KEY_STRING
,
1843 entry
->lock_comment
);
1845 /* Lock creation date */
1846 if (entry
->lock_creation_date
)
1847 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_LOCK_CREATION_DATE
,
1848 APR_HASH_KEY_STRING
,
1849 svn_time_to_cstring(entry
->lock_creation_date
, pool
));
1851 /* Has-props flag. */
1852 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_HAS_PROPS
, APR_HASH_KEY_STRING
,
1853 (entry
->has_props
? "true" : NULL
));
1856 if (entry
->has_prop_mods
)
1857 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_HAS_PROP_MODS
,
1858 APR_HASH_KEY_STRING
, "true");
1860 /* Cachable props. */
1861 if (entry
->cachable_props
&& *entry
->cachable_props
)
1862 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_CACHABLE_PROPS
,
1863 APR_HASH_KEY_STRING
, entry
->cachable_props
);
1865 /* Present props. */
1866 if (entry
->present_props
1867 && *entry
->present_props
)
1868 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_PRESENT_PROPS
,
1869 APR_HASH_KEY_STRING
, entry
->present_props
);
1871 /*** Now, remove stuff that can be derived through inheritance rules. ***/
1873 /* We only want to write out 'revision' and 'url' for the
1875 1. the current directory's "this dir" entry.
1876 2. non-directory entries:
1877 a. which are marked for addition (and consequently should
1878 have an invalid revnum)
1879 b. whose revision or url is valid and different than
1880 that of the "this dir" entry.
1882 if (strcmp(name
, SVN_WC_ENTRY_THIS_DIR
))
1884 /* This is NOT the "this dir" entry */
1885 if (! strcmp(name
, "."))
1887 /* By golly, if this isn't recognized as the "this dir"
1888 entry, and it looks like '.', we're just asking for an
1889 infinite recursion to happen. Abort! */
1893 if (entry
->kind
== svn_node_dir
)
1895 /* We don't write url, revision, repository root or uuid for subdir
1897 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_REVISION
, APR_HASH_KEY_STRING
,
1899 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_URL
, APR_HASH_KEY_STRING
,
1901 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_REPOS
, APR_HASH_KEY_STRING
,
1903 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_UUID
, APR_HASH_KEY_STRING
,
1908 /* If this is not the "this dir" entry, and the revision is
1909 the same as that of the "this dir" entry, don't write out
1911 if (entry
->revision
== this_dir
->revision
)
1912 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_REVISION
,
1913 APR_HASH_KEY_STRING
, NULL
);
1915 /* If this is not the "this dir" entry, and the uuid is
1916 the same as that of the "this dir" entry, don't write out
1918 if (entry
->uuid
&& this_dir
->uuid
)
1920 if (strcmp(entry
->uuid
, this_dir
->uuid
) == 0)
1921 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_UUID
,
1922 APR_HASH_KEY_STRING
, NULL
);
1925 /* If this is not the "this dir" entry, and the url is
1926 trivially calculable from that of the "this dir" entry,
1927 don't write out the url */
1930 if (strcmp(entry
->url
,
1931 svn_path_url_add_component(this_dir
->url
,
1933 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_URL
,
1934 APR_HASH_KEY_STRING
, NULL
);
1937 /* Avoid writing repository root if that's the same as this_dir. */
1938 if (entry
->repos
&& this_dir
->repos
1939 && strcmp(entry
->repos
, this_dir
->repos
) == 0)
1940 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_REPOS
, APR_HASH_KEY_STRING
,
1943 /* Cachable props are also inherited. */
1944 if (entry
->cachable_props
&& this_dir
->cachable_props
1945 && strcmp(entry
->cachable_props
, this_dir
->cachable_props
) == 0)
1946 apr_hash_set(atts
, SVN_WC__ENTRY_ATTR_CACHABLE_PROPS
,
1947 APR_HASH_KEY_STRING
, NULL
);
1951 /* Append the entry onto the accumulating string. */
1952 svn_xml_make_open_tag_hash(output
,
1954 svn_xml_self_closing
,
1955 SVN_WC__ENTRIES_ENTRY
,
1959 /* Construct an entries file from the ENTRIES hash in XML format in a
1960 newly allocated stringbuf and return it in *OUTPUT. Allocate the
1961 result in POOL. THIS_DIR is the this_dir entry in ENTRIES. */
1963 write_entries_xml(svn_stringbuf_t
**output
,
1964 apr_hash_t
*entries
,
1965 svn_wc_entry_t
*this_dir
,
1968 apr_hash_index_t
*hi
;
1969 apr_pool_t
*subpool
= svn_pool_create(pool
);
1971 svn_xml_make_header(output
, pool
);
1972 svn_xml_make_open_tag(output
, pool
, svn_xml_normal
,
1973 SVN_WC__ENTRIES_TOPLEVEL
,
1978 /* Write out "this dir" */
1979 write_entry_xml(output
, this_dir
, SVN_WC_ENTRY_THIS_DIR
, this_dir
, pool
);
1981 for (hi
= apr_hash_first(pool
, entries
); hi
; hi
= apr_hash_next(hi
))
1985 svn_wc_entry_t
*this_entry
;
1987 svn_pool_clear(subpool
);
1989 /* Get the entry and make sure its attributes are up-to-date. */
1990 apr_hash_this(hi
, &key
, NULL
, &val
);
1993 /* Don't rewrite the "this dir" entry! */
1994 if (! strcmp(key
, SVN_WC_ENTRY_THIS_DIR
))
1997 /* Append the entry to output */
1998 write_entry_xml(output
, this_entry
, key
, this_dir
, subpool
);
2001 svn_xml_make_close_tag(output
, pool
, SVN_WC__ENTRIES_TOPLEVEL
);
2003 svn_pool_destroy(subpool
);
2007 svn_wc__entries_write(apr_hash_t
*entries
,
2008 svn_wc_adm_access_t
*adm_access
,
2011 svn_error_t
*err
= SVN_NO_ERROR
;
2012 svn_stringbuf_t
*bigstr
= NULL
;
2013 apr_file_t
*outfile
= NULL
;
2014 apr_hash_index_t
*hi
;
2015 svn_wc_entry_t
*this_dir
;
2017 SVN_ERR(svn_wc__adm_write_check(adm_access
));
2019 /* Get a copy of the "this dir" entry for comparison purposes. */
2020 this_dir
= apr_hash_get(entries
, SVN_WC_ENTRY_THIS_DIR
,
2021 APR_HASH_KEY_STRING
);
2023 /* If there is no "this dir" entry, something is wrong. */
2025 return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND
, NULL
,
2026 _("No default entry in directory '%s'"),
2027 svn_path_local_style
2028 (svn_wc_adm_access_path(adm_access
), pool
));
2030 /* Open entries file for writing. It's important we don't use APR_EXCL
2031 * here. Consider what happens if a log file is interrupted, it may
2032 * leave a .svn/tmp/entries file behind. Then when cleanup reruns the
2033 * log file, and it attempts to modify the entries file, APR_EXCL would
2034 * cause an error that prevents cleanup running. We don't use log file
2035 * tags such as SVN_WC__LOG_MV to move entries files so any existing file
2036 * is not "valuable".
2038 SVN_ERR(svn_wc__open_adm_file(&outfile
,
2039 svn_wc_adm_access_path(adm_access
),
2040 SVN_WC__ADM_ENTRIES
,
2041 (APR_WRITE
| APR_CREATE
),
2044 if (svn_wc__adm_wc_format(adm_access
) > SVN_WC__XML_ENTRIES_VERSION
)
2046 apr_pool_t
*subpool
= svn_pool_create(pool
);
2047 bigstr
= svn_stringbuf_createf(pool
, "%d\n",
2048 svn_wc__adm_wc_format(adm_access
));
2049 /* Write out "this dir" */
2050 write_entry(bigstr
, this_dir
, SVN_WC_ENTRY_THIS_DIR
, this_dir
, pool
);
2052 for (hi
= apr_hash_first(pool
, entries
); hi
; hi
= apr_hash_next(hi
))
2056 svn_wc_entry_t
*this_entry
;
2058 svn_pool_clear(subpool
);
2060 /* Get the entry and make sure its attributes are up-to-date. */
2061 apr_hash_this(hi
, &key
, NULL
, &val
);
2064 /* Don't rewrite the "this dir" entry! */
2065 if (! strcmp(key
, SVN_WC_ENTRY_THIS_DIR
))
2068 /* Append the entry to BIGSTR */
2069 write_entry(bigstr
, this_entry
, key
, this_dir
, subpool
);
2072 svn_pool_destroy(subpool
);
2075 /* This is needed during cleanup of a not yet upgraded WC. */
2076 write_entries_xml(&bigstr
, entries
, this_dir
, pool
);
2078 SVN_ERR_W(svn_io_file_write_full(outfile
, bigstr
->data
,
2079 bigstr
->len
, NULL
, pool
),
2081 _("Error writing to '%s'"),
2082 svn_path_local_style
2083 (svn_wc_adm_access_path(adm_access
), pool
)));
2085 err
= svn_wc__close_adm_file(outfile
,
2086 svn_wc_adm_access_path(adm_access
),
2087 SVN_WC__ADM_ENTRIES
, 1, pool
);
2089 svn_wc__adm_access_set_entries(adm_access
, TRUE
, entries
);
2090 svn_wc__adm_access_set_entries(adm_access
, FALSE
, NULL
);
2096 /* Update an entry NAME in ENTRIES, according to the combination of
2097 entry data found in ENTRY and masked by MODIFY_FLAGS. If the entry
2098 already exists, the requested changes will be folded (merged) into
2099 the entry's existing state. If the entry doesn't exist, the entry
2100 will be created with exactly those properties described by the set
2101 of changes. Also cleanups meaningless fields combinations.
2103 POOL may be used to allocate memory referenced by ENTRIES.
2106 fold_entry(apr_hash_t
*entries
,
2108 apr_uint64_t modify_flags
,
2109 svn_wc_entry_t
*entry
,
2112 svn_wc_entry_t
*cur_entry
2113 = apr_hash_get(entries
, name
, APR_HASH_KEY_STRING
);
2115 assert(name
!= NULL
);
2118 cur_entry
= alloc_entry(pool
);
2120 /* Name (just a safeguard here, really) */
2121 if (! cur_entry
->name
)
2122 cur_entry
->name
= apr_pstrdup(pool
, name
);
2125 if (modify_flags
& SVN_WC__ENTRY_MODIFY_REVISION
)
2126 cur_entry
->revision
= entry
->revision
;
2128 /* Ancestral URL in repository */
2129 if (modify_flags
& SVN_WC__ENTRY_MODIFY_URL
)
2130 cur_entry
->url
= entry
->url
? apr_pstrdup(pool
, entry
->url
) : NULL
;
2132 /* Repository root */
2133 if (modify_flags
& SVN_WC__ENTRY_MODIFY_REPOS
)
2134 cur_entry
->repos
= entry
->repos
? apr_pstrdup(pool
, entry
->repos
) : NULL
;
2137 if (modify_flags
& SVN_WC__ENTRY_MODIFY_KIND
)
2138 cur_entry
->kind
= entry
->kind
;
2141 if (modify_flags
& SVN_WC__ENTRY_MODIFY_SCHEDULE
)
2142 cur_entry
->schedule
= entry
->schedule
;
2145 if (modify_flags
& SVN_WC__ENTRY_MODIFY_CHECKSUM
)
2146 cur_entry
->checksum
= entry
->checksum
2147 ? apr_pstrdup(pool
, entry
->checksum
)
2150 /* Copy-related stuff */
2151 if (modify_flags
& SVN_WC__ENTRY_MODIFY_COPIED
)
2152 cur_entry
->copied
= entry
->copied
;
2154 if (modify_flags
& SVN_WC__ENTRY_MODIFY_COPYFROM_URL
)
2155 cur_entry
->copyfrom_url
= entry
->copyfrom_url
2156 ? apr_pstrdup(pool
, entry
->copyfrom_url
)
2159 if (modify_flags
& SVN_WC__ENTRY_MODIFY_COPYFROM_REV
)
2160 cur_entry
->copyfrom_rev
= entry
->copyfrom_rev
;
2163 if (modify_flags
& SVN_WC__ENTRY_MODIFY_DELETED
)
2164 cur_entry
->deleted
= entry
->deleted
;
2167 if (modify_flags
& SVN_WC__ENTRY_MODIFY_ABSENT
)
2168 cur_entry
->absent
= entry
->absent
;
2170 /* Incomplete state */
2171 if (modify_flags
& SVN_WC__ENTRY_MODIFY_INCOMPLETE
)
2172 cur_entry
->incomplete
= entry
->incomplete
;
2174 /* Text/prop modification times */
2175 if (modify_flags
& SVN_WC__ENTRY_MODIFY_TEXT_TIME
)
2176 cur_entry
->text_time
= entry
->text_time
;
2178 if (modify_flags
& SVN_WC__ENTRY_MODIFY_PROP_TIME
)
2179 cur_entry
->prop_time
= entry
->prop_time
;
2181 /* Conflict stuff */
2182 if (modify_flags
& SVN_WC__ENTRY_MODIFY_CONFLICT_OLD
)
2183 cur_entry
->conflict_old
= entry
->conflict_old
2184 ? apr_pstrdup(pool
, entry
->conflict_old
)
2187 if (modify_flags
& SVN_WC__ENTRY_MODIFY_CONFLICT_NEW
)
2188 cur_entry
->conflict_new
= entry
->conflict_new
2189 ? apr_pstrdup(pool
, entry
->conflict_new
)
2192 if (modify_flags
& SVN_WC__ENTRY_MODIFY_CONFLICT_WRK
)
2193 cur_entry
->conflict_wrk
= entry
->conflict_wrk
2194 ? apr_pstrdup(pool
, entry
->conflict_wrk
)
2197 if (modify_flags
& SVN_WC__ENTRY_MODIFY_PREJFILE
)
2198 cur_entry
->prejfile
= entry
->prejfile
2199 ? apr_pstrdup(pool
, entry
->prejfile
)
2202 /* Last-commit stuff */
2203 if (modify_flags
& SVN_WC__ENTRY_MODIFY_CMT_REV
)
2204 cur_entry
->cmt_rev
= entry
->cmt_rev
;
2206 if (modify_flags
& SVN_WC__ENTRY_MODIFY_CMT_DATE
)
2207 cur_entry
->cmt_date
= entry
->cmt_date
;
2209 if (modify_flags
& SVN_WC__ENTRY_MODIFY_CMT_AUTHOR
)
2210 cur_entry
->cmt_author
= entry
->cmt_author
2211 ? apr_pstrdup(pool
, entry
->cmt_author
)
2214 if (modify_flags
& SVN_WC__ENTRY_MODIFY_UUID
)
2215 cur_entry
->uuid
= entry
->uuid
2216 ? apr_pstrdup(pool
, entry
->uuid
)
2220 if (modify_flags
& SVN_WC__ENTRY_MODIFY_LOCK_TOKEN
)
2221 cur_entry
->lock_token
= (entry
->lock_token
2222 ? apr_pstrdup(pool
, entry
->lock_token
)
2226 if (modify_flags
& SVN_WC__ENTRY_MODIFY_LOCK_OWNER
)
2227 cur_entry
->lock_owner
= (entry
->lock_owner
2228 ? apr_pstrdup(pool
, entry
->lock_owner
)
2232 if (modify_flags
& SVN_WC__ENTRY_MODIFY_LOCK_COMMENT
)
2233 cur_entry
->lock_comment
= (entry
->lock_comment
2234 ? apr_pstrdup(pool
, entry
->lock_comment
)
2237 /* Lock creation date */
2238 if (modify_flags
& SVN_WC__ENTRY_MODIFY_LOCK_CREATION_DATE
)
2239 cur_entry
->lock_creation_date
= entry
->lock_creation_date
;
2242 if (modify_flags
& SVN_WC__ENTRY_MODIFY_CHANGELIST
)
2243 cur_entry
->changelist
= (entry
->changelist
2244 ? apr_pstrdup(pool
, entry
->changelist
)
2247 /* has-props flag */
2248 if (modify_flags
& SVN_WC__ENTRY_MODIFY_HAS_PROPS
)
2249 cur_entry
->has_props
= entry
->has_props
;
2251 /* prop-mods flag */
2252 if (modify_flags
& SVN_WC__ENTRY_MODIFY_HAS_PROP_MODS
)
2253 cur_entry
->has_prop_mods
= entry
->has_prop_mods
;
2255 /* Cachable props. */
2256 if (modify_flags
& SVN_WC__ENTRY_MODIFY_CACHABLE_PROPS
)
2257 cur_entry
->cachable_props
= (entry
->cachable_props
2258 ? apr_pstrdup(pool
, entry
->cachable_props
)
2261 /* Property existence */
2262 if (modify_flags
& SVN_WC__ENTRY_MODIFY_PRESENT_PROPS
)
2263 cur_entry
->present_props
= (entry
->present_props
2264 ? apr_pstrdup(pool
, entry
->present_props
)
2267 if (modify_flags
& SVN_WC__ENTRY_MODIFY_KEEP_LOCAL
)
2268 cur_entry
->keep_local
= entry
->keep_local
;
2270 /* Note that we don't bother to fold entry->depth, because it is
2271 only meaningful on the this-dir entry anyway. */
2273 /* Absorb defaults from the parent dir, if any, unless this is a
2275 if (cur_entry
->kind
!= svn_node_dir
)
2277 svn_wc_entry_t
*default_entry
2278 = apr_hash_get(entries
, SVN_WC_ENTRY_THIS_DIR
, APR_HASH_KEY_STRING
);
2280 take_from_entry(default_entry
, cur_entry
, pool
);
2283 /* Cleanup meaningless fields */
2285 /* ### svn_wc_schedule_delete is the minimal value. We need it because it's
2286 impossible to NULLify copyfrom_url with log-instructions.
2288 Note that I tried to find the smallest collection not to clear these
2289 fields for, but this condition still fails the test suite:
2291 !(entry->schedule == svn_wc_schedule_add
2292 || entry->schedule == svn_wc_schedule_replace
2293 || (entry->schedule == svn_wc_schedule_normal && entry->copied)))
2296 if (modify_flags
& SVN_WC__ENTRY_MODIFY_SCHEDULE
2297 && entry
->schedule
== svn_wc_schedule_delete
)
2299 cur_entry
->copied
= FALSE
;
2300 cur_entry
->copyfrom_rev
= SVN_INVALID_REVNUM
;
2301 cur_entry
->copyfrom_url
= NULL
;
2304 if (modify_flags
& SVN_WC__ENTRY_MODIFY_WORKING_SIZE
)
2305 cur_entry
->working_size
= entry
->working_size
;
2307 /* keep_local makes sense only when we are going to delete directory. */
2308 if (modify_flags
& SVN_WC__ENTRY_MODIFY_SCHEDULE
2309 && entry
->schedule
!= svn_wc_schedule_delete
)
2311 cur_entry
->keep_local
= FALSE
;
2314 /* Make sure the entry exists in the entries hash. Possibly it
2315 already did, in which case this could have been skipped, but what
2317 apr_hash_set(entries
, cur_entry
->name
, APR_HASH_KEY_STRING
, cur_entry
);
2322 svn_wc__entry_remove(apr_hash_t
*entries
, const char *name
)
2324 apr_hash_set(entries
, name
, APR_HASH_KEY_STRING
, NULL
);
2328 /* Our general purpose intelligence module for handling scheduling
2329 changes to a single entry.
2331 Given an entryname NAME in ENTRIES, examine the caller's requested
2332 change in *SCHEDULE and the current state of the entry. Possibly
2333 modify *SCHEDULE and *MODIFY_FLAGS so that when merged, it will
2334 reflect the caller's original intent.
2336 POOL is used for local allocations only, calling this function does not
2337 use POOL to allocate any memory referenced by ENTRIES.
2339 static svn_error_t
*
2340 fold_scheduling(apr_hash_t
*entries
,
2342 apr_uint64_t
*modify_flags
,
2343 svn_wc_schedule_t
*schedule
,
2346 svn_wc_entry_t
*entry
, *this_dir_entry
;
2348 /* If we're not supposed to be bothering with this anyway...return. */
2349 if (! (*modify_flags
& SVN_WC__ENTRY_MODIFY_SCHEDULE
))
2350 return SVN_NO_ERROR
;
2352 /* Get the current entry */
2353 entry
= apr_hash_get(entries
, name
, APR_HASH_KEY_STRING
);
2355 /* If we're not merging in changes, only the _add, _delete, _replace
2356 and _normal schedules are allowed. */
2357 if (*modify_flags
& SVN_WC__ENTRY_MODIFY_FORCE
)
2361 case svn_wc_schedule_add
:
2362 case svn_wc_schedule_delete
:
2363 case svn_wc_schedule_replace
:
2364 case svn_wc_schedule_normal
:
2365 /* Since we aren't merging in a change, not only are these
2366 schedules legal, but they are final. */
2367 return SVN_NO_ERROR
;
2370 return svn_error_create(SVN_ERR_WC_SCHEDULE_CONFLICT
, NULL
, NULL
);
2374 /* The only operation valid on an item not already in revision
2375 control is addition. */
2378 if (*schedule
== svn_wc_schedule_add
)
2379 return SVN_NO_ERROR
;
2382 svn_error_createf(SVN_ERR_WC_SCHEDULE_CONFLICT
, NULL
,
2383 _("'%s' is not under version control"),
2387 /* Get the default entry */
2388 this_dir_entry
= apr_hash_get(entries
, SVN_WC_ENTRY_THIS_DIR
,
2389 APR_HASH_KEY_STRING
);
2391 /* At this point, we know the following things:
2393 1. There is already an entry for this item in the entries file
2394 whose existence is either _normal or _added (or about to
2395 become such), which for our purposes mean the same thing.
2397 2. We have been asked to merge in a state change, not to
2398 explicitly set the state. */
2400 /* Here are some cases that are parent-directory sensitive.
2401 Basically, we make sure that we are not allowing versioned
2402 resources to just sorta dangle below directories marked for
2404 if ((entry
!= this_dir_entry
)
2405 && (this_dir_entry
->schedule
== svn_wc_schedule_delete
))
2407 if (*schedule
== svn_wc_schedule_add
)
2409 svn_error_createf(SVN_ERR_WC_SCHEDULE_CONFLICT
, NULL
,
2410 _("Can't add '%s' to deleted directory; "
2411 "try undeleting its parent directory first"),
2413 if (*schedule
== svn_wc_schedule_replace
)
2415 svn_error_createf(SVN_ERR_WC_SCHEDULE_CONFLICT
, NULL
,
2416 _("Can't replace '%s' in deleted directory; "
2417 "try undeleting its parent directory first"),
2421 if (entry
->absent
&& (*schedule
== svn_wc_schedule_add
))
2423 return svn_error_createf
2424 (SVN_ERR_WC_SCHEDULE_CONFLICT
, NULL
,
2425 _("'%s' is marked as absent, so it cannot be scheduled for addition"),
2429 switch (entry
->schedule
)
2431 case svn_wc_schedule_normal
:
2434 case svn_wc_schedule_normal
:
2435 /* Normal is a trivial no-op case. Reset the
2436 schedule modification bit and move along. */
2437 *modify_flags
&= ~SVN_WC__ENTRY_MODIFY_SCHEDULE
;
2438 return SVN_NO_ERROR
;
2441 case svn_wc_schedule_delete
:
2442 case svn_wc_schedule_replace
:
2443 /* These are all good. */
2444 return SVN_NO_ERROR
;
2447 case svn_wc_schedule_add
:
2448 /* You can't add something that's already been added to
2449 revision control... unless it's got a 'deleted' state */
2450 if (! entry
->deleted
)
2453 (SVN_ERR_WC_SCHEDULE_CONFLICT
, NULL
,
2454 _("Entry '%s' is already under version control"), name
);
2458 case svn_wc_schedule_add
:
2461 case svn_wc_schedule_normal
:
2462 case svn_wc_schedule_add
:
2463 case svn_wc_schedule_replace
:
2464 /* These are all no-op cases. Normal is obvious, as is add.
2465 Replace on an entry marked for addition breaks down to
2466 (add + (delete + add)), which resolves to just (add), and
2467 since this entry is already marked with (add), this too
2469 *modify_flags
&= ~SVN_WC__ENTRY_MODIFY_SCHEDULE
;
2470 return SVN_NO_ERROR
;
2473 case svn_wc_schedule_delete
:
2474 /* Not-yet-versioned item being deleted. If the original
2475 entry was not marked as "deleted", then remove the entry.
2476 Else, return the entry to a 'normal' state, preserving
2477 the "deleted" flag. Check that we are not trying to
2478 remove the SVN_WC_ENTRY_THIS_DIR entry as that would
2479 leave the entries file in an invalid state. */
2480 assert(entry
!= this_dir_entry
);
2481 if (! entry
->deleted
)
2482 apr_hash_set(entries
, name
, APR_HASH_KEY_STRING
, NULL
);
2484 *schedule
= svn_wc_schedule_normal
;
2485 return SVN_NO_ERROR
;
2489 case svn_wc_schedule_delete
:
2492 case svn_wc_schedule_normal
:
2493 /* Reverting a delete results in normal */
2494 return SVN_NO_ERROR
;
2496 case svn_wc_schedule_delete
:
2497 /* These are no-op cases. */
2498 *modify_flags
&= ~SVN_WC__ENTRY_MODIFY_SCHEDULE
;
2499 return SVN_NO_ERROR
;
2502 case svn_wc_schedule_add
:
2503 /* Re-adding an entry marked for deletion? This is really a
2504 replace operation. */
2505 *schedule
= svn_wc_schedule_replace
;
2506 return SVN_NO_ERROR
;
2509 case svn_wc_schedule_replace
:
2510 /* Replacing an item marked for deletion breaks down to
2511 (delete + (delete + add)), which might deserve a warning,
2513 return SVN_NO_ERROR
;
2518 case svn_wc_schedule_replace
:
2521 case svn_wc_schedule_normal
:
2522 /* Reverting replacements results normal. */
2523 return SVN_NO_ERROR
;
2525 case svn_wc_schedule_add
:
2526 /* Adding a to-be-replaced entry breaks down to ((delete +
2527 add) + add) which might deserve a warning, but we'll just
2529 case svn_wc_schedule_replace
:
2530 /* Replacing a to-be-replaced entry breaks down to ((delete
2531 + add) + (delete + add)), which is insane! Make up your
2532 friggin' mind, dude! :-) Well, we'll no-op this one,
2534 *modify_flags
&= ~SVN_WC__ENTRY_MODIFY_SCHEDULE
;
2535 return SVN_NO_ERROR
;
2538 case svn_wc_schedule_delete
:
2539 /* Deleting a to-be-replaced entry breaks down to ((delete +
2540 add) + delete) which resolves to a flat deletion. */
2541 *schedule
= svn_wc_schedule_delete
;
2542 return SVN_NO_ERROR
;
2550 (SVN_ERR_WC_SCHEDULE_CONFLICT
, NULL
,
2551 _("Entry '%s' has illegal schedule"), name
);
2553 return SVN_NO_ERROR
;
2559 svn_wc__entry_modify(svn_wc_adm_access_t
*adm_access
,
2561 svn_wc_entry_t
*entry
,
2562 apr_uint64_t modify_flags
,
2563 svn_boolean_t do_sync
,
2566 apr_hash_t
*entries
, *entries_nohidden
;
2567 svn_boolean_t entry_was_deleted_p
= FALSE
;
2569 /* ENTRY is rather necessary! */
2572 /* Load ADM_ACCESS's whole entries file. */
2573 SVN_ERR(svn_wc_entries_read(&entries
, adm_access
, TRUE
, pool
));
2574 SVN_ERR(svn_wc_entries_read(&entries_nohidden
, adm_access
, FALSE
, pool
));
2576 /* Ensure that NAME is valid. */
2578 name
= SVN_WC_ENTRY_THIS_DIR
;
2580 if (modify_flags
& SVN_WC__ENTRY_MODIFY_SCHEDULE
)
2582 svn_wc_entry_t
*entry_before
, *entry_after
;
2583 apr_uint64_t orig_modify_flags
= modify_flags
;
2584 svn_wc_schedule_t orig_schedule
= entry
->schedule
;
2586 /* Keep a copy of the unmodified entry on hand. */
2587 entry_before
= apr_hash_get(entries
, name
, APR_HASH_KEY_STRING
);
2589 /* If scheduling changes were made, we have a special routine to
2590 manage those modifications. */
2591 SVN_ERR(fold_scheduling(entries
, name
, &modify_flags
,
2592 &entry
->schedule
, pool
));
2594 if (entries
!= entries_nohidden
)
2596 SVN_ERR(fold_scheduling(entries_nohidden
, name
, &orig_modify_flags
,
2597 &orig_schedule
, pool
));
2599 /* Make certain that both folding operations had the same
2601 assert(orig_modify_flags
== modify_flags
);
2602 assert(orig_schedule
== entry
->schedule
);
2605 /* Special case: fold_state_changes() may have actually REMOVED
2606 the entry in question! If so, don't try to fold_entry, as
2607 this will just recreate the entry again. */
2608 entry_after
= apr_hash_get(entries
, name
, APR_HASH_KEY_STRING
);
2610 /* Note if this entry was deleted above so we don't accidentally
2611 re-add it in the following steps. */
2612 if (entry_before
&& (! entry_after
))
2613 entry_was_deleted_p
= TRUE
;
2616 /* If the entry wasn't just removed from the entries hash, fold the
2617 changes into the entry. */
2618 if (! entry_was_deleted_p
)
2620 fold_entry(entries
, name
, modify_flags
, entry
,
2621 svn_wc_adm_access_pool(adm_access
));
2622 if (entries
!= entries_nohidden
)
2623 fold_entry(entries_nohidden
, name
, modify_flags
, entry
,
2624 svn_wc_adm_access_pool(adm_access
));
2627 /* Sync changes to disk. */
2629 SVN_ERR(svn_wc__entries_write(entries
, adm_access
, pool
));
2631 return SVN_NO_ERROR
;
2636 svn_wc_entry_dup(const svn_wc_entry_t
*entry
, apr_pool_t
*pool
)
2638 svn_wc_entry_t
*dupentry
= apr_palloc(pool
, sizeof(*dupentry
));
2640 /* Perform a trivial copy ... */
2643 /* ...and then re-copy stuff that needs to be duped into our pool. */
2645 dupentry
->name
= apr_pstrdup(pool
, entry
->name
);
2647 dupentry
->url
= apr_pstrdup(pool
, entry
->url
);
2649 dupentry
->repos
= apr_pstrdup(pool
, entry
->repos
);
2651 dupentry
->uuid
= apr_pstrdup(pool
, entry
->uuid
);
2652 if (entry
->copyfrom_url
)
2653 dupentry
->copyfrom_url
= apr_pstrdup(pool
, entry
->copyfrom_url
);
2654 if (entry
->conflict_old
)
2655 dupentry
->conflict_old
= apr_pstrdup(pool
, entry
->conflict_old
);
2656 if (entry
->conflict_new
)
2657 dupentry
->conflict_new
= apr_pstrdup(pool
, entry
->conflict_new
);
2658 if (entry
->conflict_wrk
)
2659 dupentry
->conflict_wrk
= apr_pstrdup(pool
, entry
->conflict_wrk
);
2660 if (entry
->prejfile
)
2661 dupentry
->prejfile
= apr_pstrdup(pool
, entry
->prejfile
);
2662 if (entry
->checksum
)
2663 dupentry
->checksum
= apr_pstrdup(pool
, entry
->checksum
);
2664 if (entry
->cmt_author
)
2665 dupentry
->cmt_author
= apr_pstrdup(pool
, entry
->cmt_author
);
2666 if (entry
->lock_token
)
2667 dupentry
->lock_token
= apr_pstrdup(pool
, entry
->lock_token
);
2668 if (entry
->lock_owner
)
2669 dupentry
->lock_owner
= apr_pstrdup(pool
, entry
->lock_owner
);
2670 if (entry
->lock_comment
)
2671 dupentry
->lock_comment
= apr_pstrdup(pool
, entry
->lock_comment
);
2672 if (entry
->changelist
)
2673 dupentry
->changelist
= apr_pstrdup(pool
, entry
->changelist
);
2674 if (entry
->cachable_props
)
2675 dupentry
->cachable_props
= apr_pstrdup(pool
, entry
->cachable_props
);
2676 if (entry
->present_props
)
2677 dupentry
->present_props
= apr_pstrdup(pool
, entry
->present_props
);
2683 svn_wc__tweak_entry(apr_hash_t
*entries
,
2685 const char *new_url
,
2687 svn_revnum_t new_rev
,
2688 svn_boolean_t allow_removal
,
2689 svn_boolean_t
*write_required
,
2692 svn_wc_entry_t
*entry
;
2694 entry
= apr_hash_get(entries
, name
, APR_HASH_KEY_STRING
);
2696 return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND
, NULL
,
2697 _("No such entry: '%s'"), name
);
2700 && (! entry
->url
|| strcmp(new_url
, entry
->url
)))
2702 *write_required
= TRUE
;
2703 entry
->url
= apr_pstrdup(pool
, new_url
);
2707 && (! entry
->repos
|| strcmp(repos
, entry
->repos
))
2709 && svn_path_is_ancestor(repos
, entry
->url
))
2711 svn_boolean_t set_repos
= TRUE
;
2713 /* Setting the repository root on THIS_DIR will make files in this
2714 directory inherit that property. So to not make the WC corrupt,
2715 we have to make sure that the repos root is valid for such entries as
2716 well. Note that this shouldn't happen in normal circumstances. */
2717 if (strcmp(entry
->name
, SVN_WC_ENTRY_THIS_DIR
) == 0)
2719 apr_hash_index_t
*hi
;
2720 for (hi
= apr_hash_first(pool
, entries
); hi
;
2721 hi
= apr_hash_next(hi
))
2724 const svn_wc_entry_t
*child_entry
;
2726 apr_hash_this(hi
, NULL
, NULL
, &value
);
2727 child_entry
= value
;
2729 if (! child_entry
->repos
&& child_entry
->url
2730 && ! svn_path_is_ancestor(repos
, child_entry
->url
))
2740 *write_required
= TRUE
;
2741 entry
->repos
= apr_pstrdup(pool
, repos
);
2745 if ((SVN_IS_VALID_REVNUM(new_rev
))
2746 && (entry
->schedule
!= svn_wc_schedule_add
)
2747 && (entry
->schedule
!= svn_wc_schedule_replace
)
2748 && (entry
->copied
!= TRUE
)
2749 && (entry
->revision
!= new_rev
))
2751 *write_required
= TRUE
;
2752 entry
->revision
= new_rev
;
2755 /* As long as this function is only called as a helper to
2756 svn_wc__do_update_cleanup, then it's okay to remove any entry
2757 under certain circumstances:
2759 If the entry is still marked 'deleted', then the server did not
2760 re-add it. So it's really gone in this revision, thus we remove
2763 If the entry is still marked 'absent' and yet is not the same
2764 revision as new_rev, then the server did not re-add it, nor
2765 re-absent it, so we can remove the entry.
2767 ### This function cannot always determine whether removal is
2768 ### appropriate, hence the ALLOW_REMOVAL flag. It's all a bit of a
2771 && (entry
->deleted
|| (entry
->absent
&& entry
->revision
!= new_rev
)))
2773 *write_required
= TRUE
;
2774 apr_hash_set(entries
, name
, APR_HASH_KEY_STRING
, NULL
);
2777 return SVN_NO_ERROR
;
2783 /*** Initialization of the entries file. ***/
2786 svn_wc__entries_init(const char *path
,
2790 svn_revnum_t initial_rev
,
2794 apr_file_t
*f
= NULL
;
2795 svn_stringbuf_t
*accum
= svn_stringbuf_createf(pool
, "%d\n",
2797 svn_wc_entry_t
*entry
= alloc_entry(pool
);
2799 /* Sanity checks. */
2800 assert(! repos
|| svn_path_is_ancestor(repos
, url
));
2801 assert(depth
== svn_depth_empty
2802 || depth
== svn_depth_files
2803 || depth
== svn_depth_immediates
2804 || depth
== svn_depth_infinity
);
2806 /* Create the entries file, which must not exist prior to this. */
2807 SVN_ERR(svn_wc__open_adm_file(&f
, path
, SVN_WC__ADM_ENTRIES
,
2808 (APR_WRITE
| APR_CREATE
| APR_EXCL
), pool
));
2810 /* Add an entry for the dir itself. The directory has no name. It
2811 might have a UUID, but otherwise only the revision and default
2812 ancestry are present as XML attributes, and possibly an
2813 'incomplete' flag if the revnum is > 0. */
2815 entry
->kind
= svn_node_dir
;
2817 entry
->revision
= initial_rev
;
2819 entry
->repos
= repos
;
2820 entry
->depth
= depth
;
2821 if (initial_rev
> 0)
2822 entry
->incomplete
= TRUE
;
2823 /* Add cachable-props here so that it can be inherited by other entries.
2825 entry
->cachable_props
= SVN_WC__CACHABLE_PROPS
;
2827 write_entry(accum
, entry
, SVN_WC_ENTRY_THIS_DIR
, entry
, pool
);
2829 SVN_ERR_W(svn_io_file_write_full(f
, accum
->data
, accum
->len
, NULL
, pool
),
2831 _("Error writing entries file for '%s'"),
2832 svn_path_local_style(path
, pool
)));
2834 /* Now we have a `entries' file with exactly one entry, an entry
2835 for this dir. Close the file and sync it up. */
2836 SVN_ERR(svn_wc__close_adm_file(f
, path
, SVN_WC__ADM_ENTRIES
, 1, pool
));
2838 return SVN_NO_ERROR
;
2842 /*--------------------------------------------------------------- */
2844 /*** Generic Entry Walker */
2847 /* A recursive entry-walker, helper for svn_wc_walk_entries2 */
2848 static svn_error_t
*
2849 walker_helper(const char *dirpath
,
2850 svn_wc_adm_access_t
*adm_access
,
2851 const svn_wc_entry_callbacks2_t
*walk_callbacks
,
2854 svn_boolean_t show_hidden
,
2855 svn_cancel_func_t cancel_func
,
2859 apr_pool_t
*subpool
= svn_pool_create(pool
);
2860 apr_hash_t
*entries
;
2861 apr_hash_index_t
*hi
;
2862 svn_wc_entry_t
*dot_entry
;
2864 SVN_ERR(walk_callbacks
->handle_error
2865 (dirpath
, svn_wc_entries_read(&entries
, adm_access
, show_hidden
,
2866 pool
), walk_baton
, pool
));
2868 /* As promised, always return the '.' entry first. */
2869 dot_entry
= apr_hash_get(entries
, SVN_WC_ENTRY_THIS_DIR
,
2870 APR_HASH_KEY_STRING
);
2872 return walk_callbacks
->handle_error
2873 (dirpath
, svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND
, NULL
,
2874 _("Directory '%s' has no THIS_DIR entry"),
2875 svn_path_local_style(dirpath
, pool
)),
2878 SVN_ERR(walk_callbacks
->handle_error
2880 walk_callbacks
->found_entry(dirpath
, dot_entry
, walk_baton
, pool
),
2883 if (depth
== svn_depth_empty
)
2884 return SVN_NO_ERROR
;
2886 /* Loop over each of the other entries. */
2887 for (hi
= apr_hash_first(pool
, entries
); hi
; hi
= apr_hash_next(hi
))
2891 const svn_wc_entry_t
*current_entry
;
2892 const char *entrypath
;
2894 svn_pool_clear(subpool
);
2896 /* See if someone wants to cancel this operation. */
2898 SVN_ERR(cancel_func(cancel_baton
));
2900 apr_hash_this(hi
, &key
, NULL
, &val
);
2901 current_entry
= val
;
2903 if (strcmp(current_entry
->name
, SVN_WC_ENTRY_THIS_DIR
) == 0)
2906 entrypath
= svn_path_join(dirpath
, key
, subpool
);
2908 if (current_entry
->kind
== svn_node_file
2909 || depth
>= svn_depth_immediates
)
2911 SVN_ERR(walk_callbacks
->handle_error
2913 walk_callbacks
->found_entry(entrypath
, current_entry
,
2914 walk_baton
, subpool
),
2918 if (current_entry
->kind
== svn_node_dir
2919 && depth
>= svn_depth_immediates
)
2921 svn_wc_adm_access_t
*entry_access
;
2922 svn_depth_t depth_below_here
= depth
;
2924 if (depth
== svn_depth_immediates
)
2925 depth_below_here
= svn_depth_empty
;
2927 SVN_ERR(walk_callbacks
->handle_error
2929 svn_wc_adm_retrieve(&entry_access
, adm_access
, entrypath
,
2934 SVN_ERR(walker_helper(entrypath
, entry_access
,
2935 walk_callbacks
, walk_baton
,
2936 depth_below_here
, show_hidden
,
2937 cancel_func
, cancel_baton
,
2942 svn_pool_destroy(subpool
);
2943 return SVN_NO_ERROR
;
2948 svn_wc_walk_entries(const char *path
,
2949 svn_wc_adm_access_t
*adm_access
,
2950 const svn_wc_entry_callbacks_t
*walk_callbacks
,
2952 svn_boolean_t show_hidden
,
2955 return svn_wc_walk_entries2(path
, adm_access
, walk_callbacks
,
2956 walk_baton
, show_hidden
, NULL
, NULL
,
2961 svn_wc__walker_default_error_handler(const char *path
,
2970 svn_wc_walk_entries2(const char *path
,
2971 svn_wc_adm_access_t
*adm_access
,
2972 const svn_wc_entry_callbacks_t
*walk_callbacks
,
2974 svn_boolean_t show_hidden
,
2975 svn_cancel_func_t cancel_func
,
2979 svn_wc_entry_callbacks2_t walk_cb2
= { walk_callbacks
->found_entry
,
2980 svn_wc__walker_default_error_handler
};
2981 return svn_wc_walk_entries3(path
, adm_access
,
2982 &walk_cb2
, walk_baton
, svn_depth_infinity
,
2983 show_hidden
, cancel_func
, cancel_baton
, pool
);
2986 /* The public API. */
2988 svn_wc_walk_entries3(const char *path
,
2989 svn_wc_adm_access_t
*adm_access
,
2990 const svn_wc_entry_callbacks2_t
*walk_callbacks
,
2993 svn_boolean_t show_hidden
,
2994 svn_cancel_func_t cancel_func
,
2998 const svn_wc_entry_t
*entry
;
3000 SVN_ERR(svn_wc_entry(&entry
, path
, adm_access
, show_hidden
, pool
));
3003 return walk_callbacks
->handle_error
3004 (path
, svn_error_createf(SVN_ERR_UNVERSIONED_RESOURCE
, NULL
,
3005 _("'%s' is not under version control"),
3006 svn_path_local_style(path
, pool
)),
3009 if (entry
->kind
== svn_node_file
)
3010 return walk_callbacks
->handle_error
3011 (path
, walk_callbacks
->found_entry(path
, entry
, walk_baton
, pool
),
3014 else if (entry
->kind
== svn_node_dir
)
3015 return walker_helper(path
, adm_access
, walk_callbacks
, walk_baton
,
3016 depth
, show_hidden
, cancel_func
, cancel_baton
, pool
);
3019 return walk_callbacks
->handle_error
3020 (path
, svn_error_createf(SVN_ERR_NODE_UNKNOWN_KIND
, NULL
,
3021 _("'%s' has an unrecognized node kind"),
3022 svn_path_local_style(path
, pool
)),
3028 svn_wc_mark_missing_deleted(const char *path
,
3029 svn_wc_adm_access_t
*parent
,
3032 svn_node_kind_t pkind
;
3034 SVN_ERR(svn_io_check_path(path
, &pkind
, pool
));
3036 if (pkind
== svn_node_none
)
3038 const char *parent_path
, *bname
;
3039 svn_wc_adm_access_t
*adm_access
;
3040 svn_wc_entry_t newent
;
3042 newent
.deleted
= TRUE
;
3043 newent
.schedule
= svn_wc_schedule_normal
;
3045 svn_path_split(path
, &parent_path
, &bname
, pool
);
3047 SVN_ERR(svn_wc_adm_retrieve(&adm_access
, parent
, parent_path
, pool
));
3048 SVN_ERR(svn_wc__entry_modify(adm_access
, bname
, &newent
,
3049 (SVN_WC__ENTRY_MODIFY_DELETED
3050 | SVN_WC__ENTRY_MODIFY_SCHEDULE
3051 | SVN_WC__ENTRY_MODIFY_FORCE
),
3052 TRUE
, /* sync right away */ pool
));
3054 return SVN_NO_ERROR
;
3057 return svn_error_createf(SVN_ERR_WC_PATH_FOUND
, NULL
,
3058 _("Unexpectedly found '%s': "
3059 "path is marked 'missing'"),
3060 svn_path_local_style(path
, pool
));