2 * update_editor.c : main editor for checkouts and updates
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
21 * ====================================================================
29 #include <apr_pools.h>
32 #include <apr_tables.h>
33 #include <apr_strings.h>
35 #include "svn_types.h"
36 #include "svn_pools.h"
38 #include "svn_string.h"
39 #include "svn_dirent_uri.h"
41 #include "svn_error.h"
43 #include "svn_private_config.h"
47 #include "adm_files.h"
48 #include "conflicts.h"
49 #include "translate.h"
50 #include "workqueue.h"
52 #include "private/svn_subr_private.h"
53 #include "private/svn_wc_private.h"
54 #include "private/svn_editor.h"
56 /* Checks whether a svn_wc__db_status_t indicates whether a node is
57 present in a working copy. Used by the editor implementation */
58 #define IS_NODE_PRESENT(status) \
59 ((status) != svn_wc__db_status_server_excluded &&\
60 (status) != svn_wc__db_status_excluded && \
61 (status) != svn_wc__db_status_not_present)
64 path_join_under_root(const char **result_path,
65 const char *base_path,
67 apr_pool_t *result_pool);
71 * This code handles "checkout" and "update" and "switch".
72 * A checkout is similar to an update that is only adding new items.
74 * The intended behaviour of "update" and "switch", focusing on the checks
75 * to be made before applying a change, is:
77 * For each incoming change:
78 * if target is already in conflict or obstructed:
81 * if this action will cause a tree conflict:
82 * record the tree conflict
89 * For each incoming change:
91 * 1. if # Incoming change is inside an item already in conflict:
92 * a. tree/text/prop change to node beneath tree-conflicted dir
93 * then # Skip all changes in this conflicted subtree [*1]:
94 * do not update the Base nor the Working
95 * notify "skipped because already in conflict" just once
96 * for the whole conflicted subtree
98 * if # Incoming change affects an item already in conflict:
99 * b. tree/text/prop change to tree-conflicted dir/file, or
100 * c. tree change to a text/prop-conflicted file/dir, or
101 * d. text/prop change to a text/prop-conflicted file/dir [*2], or
102 * e. tree change to a dir tree containing any conflicts,
103 * then # Skip this change [*1]:
104 * do not update the Base nor the Working
105 * notify "skipped because already in conflict"
107 * 2. if # Incoming change affects an item that's "obstructed":
108 * a. on-disk node kind doesn't match recorded Working node kind
109 * (including an absence/presence mis-match),
110 * then # Skip this change [*1]:
111 * do not update the Base nor the Working
112 * notify "skipped because obstructed"
114 * 3. if # Incoming change raises a tree conflict:
115 * a. tree/text/prop change to node beneath sched-delete dir, or
116 * b. tree/text/prop change to sched-delete dir/file, or
117 * c. text/prop change to tree-scheduled dir/file,
118 * then # Skip this change:
119 * do not update the Base nor the Working [*3]
120 * notify "tree conflict"
122 * 4. Apply the change:
124 * update the Working, possibly raising text/prop conflicts
129 * "Tree change" here refers to an add or delete of the target node,
130 * including the add or delete part of a copy or move or rename.
132 * [*1] We should skip changes to an entire node, as the base revision number
133 * applies to the entire node. Not sure how this affects attempts to
134 * handle text and prop changes separately.
136 * [*2] Details of which combinations of property and text changes conflict
137 * are not specified here.
139 * [*3] For now, we skip the update, and require the user to:
140 * - Modify the WC to be compatible with the incoming change;
141 * - Mark the conflict as resolved;
142 * - Repeat the update.
143 * Ideally, it would be possible to resolve any conflict without
144 * repeating the update. To achieve this, we would have to store the
145 * necessary data at conflict detection time, and delay the update of
146 * the Base until the time of resolving.
154 /* For updates, the "destination" of the edit is ANCHOR_ABSPATH, the
155 directory containing TARGET_ABSPATH. If ANCHOR_ABSPATH itself is the
156 target, the values are identical.
158 TARGET_BASENAME is the name of TARGET_ABSPATH in ANCHOR_ABSPATH, or "" if
159 ANCHOR_ABSPATH is the target */
160 const char *target_basename;
162 /* Absolute variants of ANCHOR and TARGET */
163 const char *anchor_abspath;
164 const char *target_abspath;
166 /* The DB handle for managing the working copy state. */
169 /* Array of file extension patterns to preserve as extensions in
170 generated conflict files. */
171 const apr_array_header_t *ext_patterns;
173 /* Hash mapping const char * absolute working copy paths to depth-first
174 ordered arrays of svn_prop_inherited_item_t * structures representing
175 the properties inherited by the base node at that working copy path.
177 apr_hash_t *wcroot_iprops;
179 /* The revision we're targeting...or something like that. This
180 starts off as a pointer to the revision to which we are updating,
181 or SVN_INVALID_REVNUM, but by the end of the edit, should be
182 pointing to the final revision. */
183 svn_revnum_t *target_revision;
185 /* The requested depth of this edit. */
186 svn_depth_t requested_depth;
188 /* Is the requested depth merely an operational limitation, or is
189 also the new sticky ambient depth of the update target? */
190 svn_boolean_t depth_is_sticky;
192 /* Need to know if the user wants us to overwrite the 'now' times on
193 edited/added files with the last-commit-time. */
194 svn_boolean_t use_commit_times;
196 /* Was the root actually opened (was this a non-empty edit)? */
197 svn_boolean_t root_opened;
199 /* Was the update-target deleted? This is a special situation. */
200 svn_boolean_t target_deleted;
202 /* Allow unversioned obstructions when adding a path. */
203 svn_boolean_t allow_unver_obstructions;
205 /* Handle local additions as modifications of new nodes */
206 svn_boolean_t adds_as_modification;
208 /* If set, we check out into an empty directory. This allows for a number
209 of conflict checks to be omitted. */
210 svn_boolean_t clean_checkout;
212 /* If this is a 'switch' operation, the new relpath of target_abspath,
214 const char *switch_relpath;
216 /* The URL to the root of the repository. */
217 const char *repos_root;
219 /* The UUID of the repos, or NULL. */
220 const char *repos_uuid;
222 /* External diff3 to use for merges (can be null, in which case
223 internal merge code is used). */
224 const char *diff3_cmd;
226 /* Externals handler */
227 svn_wc_external_update_t external_func;
228 void *external_baton;
230 /* This editor sends back notifications as it edits. */
231 svn_wc_notify_func2_t notify_func;
234 /* This editor is normally wrapped in a cancellation editor anyway,
235 so it doesn't bother to check for cancellation itself. However,
236 it needs a cancel_func and cancel_baton available to pass to
237 long-running functions. */
238 svn_cancel_func_t cancel_func;
241 /* This editor will invoke a interactive conflict-resolution
242 callback, if available. */
243 svn_wc_conflict_resolver_func2_t conflict_func;
244 void *conflict_baton;
246 /* Subtrees that were skipped during the edit, and therefore shouldn't
247 have their revision/url info updated at the end. If a path is a
248 directory, its descendants will also be skipped. The keys are paths
249 relative to the working copy root and the values unspecified. */
250 apr_hash_t *skipped_trees;
252 /* A mapping from const char * repos_relpaths to the apr_hash_t * instances
253 returned from fetch_dirents_func for that repos_relpath. These
254 are used to avoid issue #3569 in specific update scenarios where a
255 restricted depth is used. */
256 apr_hash_t *dir_dirents;
258 /* Absolute path of the working copy root or NULL if not initialized yet */
259 const char *wcroot_abspath;
265 /* Record in the edit baton EB that LOCAL_ABSPATH's base version is not being
268 * Add to EB->skipped_trees a copy (allocated in EB->pool) of the string
272 remember_skipped_tree(struct edit_baton *eb,
273 const char *local_abspath,
274 apr_pool_t *scratch_pool)
276 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
278 svn_hash_sets(eb->skipped_trees,
279 apr_pstrdup(eb->pool,
280 svn_dirent_skip_ancestor(eb->wcroot_abspath,
287 /* Per directory baton. Lives in its own subpool of the parent directory
288 or of the edit baton if there is no parent directory */
291 /* Basename of this directory. */
294 /* Absolute path of this directory */
295 const char *local_abspath;
297 /* The repository relative path this directory will correspond to. */
298 const char *new_relpath;
300 /* The revision of the directory before updating */
301 svn_revnum_t old_revision;
303 /* The repos_relpath before updating/switching */
304 const char *old_repos_relpath;
306 /* The global edit baton. */
307 struct edit_baton *edit_baton;
309 /* Baton for this directory's parent, or NULL if this is the root
311 struct dir_baton *parent_baton;
313 /* Set if updates to this directory are skipped */
314 svn_boolean_t skip_this;
316 /* Set if there was a previous notification for this directory */
317 svn_boolean_t already_notified;
319 /* Set if this directory is being added during this editor drive. */
320 svn_boolean_t adding_dir;
322 /* Set on a node and its descendants are not present in the working copy
323 but should still be updated (not skipped). These nodes should all be
324 marked as deleted. */
325 svn_boolean_t shadowed;
327 /* Set on a node when the existing node is obstructed, and the edit operation
328 continues as semi-shadowed update */
329 svn_boolean_t edit_obstructed;
331 /* The (new) changed_* information, cached to avoid retrieving it later */
332 svn_revnum_t changed_rev;
333 apr_time_t changed_date;
334 const char *changed_author;
336 /* If not NULL, contains a mapping of const char* basenames of children that
337 have been deleted to their svn_skel_t* tree conflicts.
338 We store this hash to allow replacements to continue under a just
339 installed tree conflict.
341 The add after the delete will then update the tree conflicts information
343 apr_hash_t *deletion_conflicts;
345 /* A hash of file names (only the hash key matters) seen by add_file
346 and not yet added to the database by close_file. */
347 apr_hash_t *not_present_files;
349 /* Set if an unversioned dir of the same name already existed in
351 svn_boolean_t obstruction_found;
353 /* Set if a dir of the same name already exists and is
354 scheduled for addition without history. */
355 svn_boolean_t add_existed;
357 /* An array of svn_prop_t structures, representing all the property
358 changes to be applied to this directory. */
359 apr_array_header_t *propchanges;
361 /* A boolean indicating whether this node or one of its children has
362 received any 'real' changes. Used to avoid tree conflicts for simple
363 entryprop changes, like lock management */
364 svn_boolean_t edited;
366 /* The tree conflict to install once the node is really edited */
367 svn_skel_t *edit_conflict;
369 /* The bump information for this directory. */
370 struct bump_dir_info *bump_info;
372 /* The depth of the directory in the wc (or inferred if added). Not
373 used for filtering; we have a separate wrapping editor for that. */
374 svn_depth_t ambient_depth;
376 /* Was the directory marked as incomplete before the update?
377 (In other words, are we resuming an interrupted update?)
379 If WAS_INCOMPLETE is set to TRUE we expect to receive all child nodes
380 and properties for/of the directory. If WAS_INCOMPLETE is FALSE then
381 we only receive the changes in/for children and properties.*/
382 svn_boolean_t was_incomplete;
384 /* The pool in which this baton itself is allocated. */
387 /* how many nodes are referring to baton? */
395 svn_txdelta_window_handler_t apply_handler;
398 struct file_baton *fb;
400 /* Where we are assembling the new file. */
401 const char *new_text_base_tmp_abspath;
403 /* The expected source checksum of the text source or NULL if no base
404 checksum is available (MD5 if the server provides a checksum, SHA1 if
405 the server doesn't) */
406 svn_checksum_t *expected_source_checksum;
408 /* Why two checksums?
409 The editor currently provides an md5 which we use to detect corruption
410 during transmission. We use the sha1 inside libsvn_wc both for pristine
411 handling and corruption detection. In the future, the editor will also
412 provide a sha1, so we may not have to calculate both, but for the time
413 being, that's the way it is. */
415 /* The calculated checksum of the text source or NULL if the acual
416 checksum is not being calculated. The checksum kind is identical to the
417 kind of expected_source_checksum. */
418 svn_checksum_t *actual_source_checksum;
420 /* The stream used to calculate the source checksums */
421 svn_stream_t *source_checksum_stream;
423 /* A calculated MD5 digest of NEW_TEXT_BASE_TMP_ABSPATH.
424 This is initialized to all zeroes when the baton is created, then
425 populated with the MD5 digest of the resultant fulltext after the
426 last window is handled by the handler returned from
427 apply_textdelta(). */
428 unsigned char new_text_base_md5_digest[APR_MD5_DIGESTSIZE];
430 /* A calculated SHA-1 of NEW_TEXT_BASE_TMP_ABSPATH, which we'll use for
431 eventually writing the pristine. */
432 svn_checksum_t * new_text_base_sha1_checksum;
436 /* Get an empty file in the temporary area for WRI_ABSPATH. The file will
437 not be set for automatic deletion, and the name will be returned in
440 This implementation creates a new empty file with a unique name.
442 ### This is inefficient for callers that just want an empty file to read
443 ### from. There could be (and there used to be) a permanent, shared
444 ### empty file for this purpose.
446 ### This is inefficient for callers that just want to reserve a unique
447 ### file name to create later. A better way may not be readily available.
450 get_empty_tmp_file(const char **tmp_filename,
452 const char *wri_abspath,
453 apr_pool_t *result_pool,
454 apr_pool_t *scratch_pool)
456 const char *temp_dir_abspath;
458 SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, db, wri_abspath,
459 scratch_pool, scratch_pool));
460 SVN_ERR(svn_io_open_unique_file3(NULL, tmp_filename, temp_dir_abspath,
461 svn_io_file_del_none,
462 scratch_pool, scratch_pool));
467 /* An APR pool cleanup handler. This runs the working queue for an
470 cleanup_edit_baton(void *edit_baton)
472 struct edit_baton *eb = edit_baton;
474 apr_pool_t *pool = apr_pool_parent_get(eb->pool);
476 err = svn_wc__wq_run(eb->db, eb->wcroot_abspath,
477 NULL /* cancel_func */, NULL /* cancel_baton */,
482 apr_status_t apr_err = err->apr_err;
483 svn_error_clear(err);
489 /* Make a new dir baton in a subpool of PB->pool. PB is the parent baton.
490 If PATH and PB are NULL, this is the root directory of the edit; in this
491 case, make the new dir baton in a subpool of EB->pool.
492 ADDING should be TRUE if we are adding this directory. */
494 make_dir_baton(struct dir_baton **d_p,
496 struct edit_baton *eb,
497 struct dir_baton *pb,
498 svn_boolean_t adding,
499 apr_pool_t *scratch_pool)
501 apr_pool_t *dir_pool;
505 dir_pool = svn_pool_create(pb->pool);
507 dir_pool = svn_pool_create(eb->pool);
509 SVN_ERR_ASSERT(path || (! pb));
511 /* Okay, no easy out, so allocate and initialize a dir baton. */
512 d = apr_pcalloc(dir_pool, sizeof(*d));
514 /* Construct the PATH and baseNAME of this directory. */
517 d->name = svn_dirent_basename(path, dir_pool);
518 SVN_ERR(path_join_under_root(&d->local_abspath,
519 pb->local_abspath, d->name, dir_pool));
523 /* This is the root baton. */
525 d->local_abspath = eb->anchor_abspath;
528 /* Figure out the new_relpath for this directory. */
529 if (eb->switch_relpath)
531 /* Handle switches... */
535 if (*eb->target_basename == '\0')
537 /* No parent baton and target_basename=="" means that we are
538 the target of the switch. Thus, our NEW_RELPATH will be
539 the SWITCH_RELPATH. */
540 d->new_relpath = eb->switch_relpath;
544 /* This node is NOT the target of the switch (one of our
545 children is the target); therefore, it must already exist.
546 Get its old REPOS_RELPATH, as it won't be changing. */
547 SVN_ERR(svn_wc__db_scan_base_repos(&d->new_relpath, NULL, NULL,
548 eb->db, d->local_abspath,
549 dir_pool, scratch_pool));
554 /* This directory is *not* the root (has a parent). If there is
555 no grandparent, then we may have anchored at the parent,
556 and self is the target. If we match the target, then set
557 NEW_RELPATH to the SWITCH_RELPATH.
559 Otherwise, we simply extend NEW_RELPATH from the parent. */
560 if (pb->parent_baton == NULL
561 && strcmp(eb->target_basename, d->name) == 0)
562 d->new_relpath = eb->switch_relpath;
564 d->new_relpath = svn_relpath_join(pb->new_relpath, d->name,
568 else /* must be an update */
570 /* If we are adding the node, then simply extend the parent's
571 relpath for our own. */
574 SVN_ERR_ASSERT(pb != NULL);
575 d->new_relpath = svn_relpath_join(pb->new_relpath, d->name,
580 SVN_ERR(svn_wc__db_scan_base_repos(&d->new_relpath, NULL, NULL,
581 eb->db, d->local_abspath,
582 dir_pool, scratch_pool));
583 SVN_ERR_ASSERT(d->new_relpath);
588 d->parent_baton = pb;
590 d->propchanges = apr_array_make(dir_pool, 1, sizeof(svn_prop_t));
591 d->obstruction_found = FALSE;
592 d->add_existed = FALSE;
594 d->old_revision = SVN_INVALID_REVNUM;
595 d->adding_dir = adding;
596 d->changed_rev = SVN_INVALID_REVNUM;
597 d->not_present_files = apr_hash_make(dir_pool);
599 /* Copy some flags from the parent baton */
602 d->skip_this = pb->skip_this;
603 d->shadowed = pb->shadowed || pb->edit_obstructed;
605 /* the parent's bump info has one more referer */
609 /* The caller of this function needs to fill these in. */
610 d->ambient_depth = svn_depth_unknown;
611 d->was_incomplete = FALSE;
618 /* Forward declarations. */
620 already_in_a_tree_conflict(svn_boolean_t *conflicted,
621 svn_boolean_t *ignored,
623 const char *local_abspath,
624 apr_pool_t *scratch_pool);
628 do_notification(const struct edit_baton *eb,
629 const char *local_abspath,
630 svn_node_kind_t kind,
631 svn_wc_notify_action_t action,
632 apr_pool_t *scratch_pool)
634 svn_wc_notify_t *notify;
636 if (eb->notify_func == NULL)
639 notify = svn_wc_create_notify(local_abspath, action, scratch_pool);
642 (*eb->notify_func)(eb->notify_baton, notify, scratch_pool);
645 /* Decrement the directory's reference count. If it hits zero,
646 then this directory is "done". This means it is safe to clear its pool.
648 In addition, when the directory is "done", we recurse to possible cleanup
649 the parent directory.
652 maybe_release_dir_info(struct dir_baton *db)
658 struct dir_baton *pb = db->parent_baton;
660 svn_pool_destroy(db->pool);
663 SVN_ERR(maybe_release_dir_info(pb));
669 /* Per file baton. Lives in its own subpool below the pool of the parent
673 /* Pool specific to this file_baton. */
676 /* Name of this file (its entry in the directory). */
679 /* Absolute path to this file */
680 const char *local_abspath;
682 /* The repository relative path this file will correspond to. */
683 const char *new_relpath;
685 /* The revision of the file before updating */
686 svn_revnum_t old_revision;
688 /* The repos_relpath before updating/switching */
689 const char *old_repos_relpath;
691 /* The global edit baton. */
692 struct edit_baton *edit_baton;
694 /* The parent directory of this file. */
695 struct dir_baton *dir_baton;
697 /* Set if updates to this directory are skipped */
698 svn_boolean_t skip_this;
700 /* Set if there was a previous notification */
701 svn_boolean_t already_notified;
703 /* Set if this file is new. */
704 svn_boolean_t adding_file;
706 /* Set if an unversioned file of the same name already existed in
708 svn_boolean_t obstruction_found;
710 /* Set if a file of the same name already exists and is
711 scheduled for addition without history. */
712 svn_boolean_t add_existed;
714 /* Set if this file is being added in the BASE layer, but is not-present
715 in the working copy (replaced, deleted, etc.). */
716 svn_boolean_t shadowed;
718 /* Set on a node when the existing node is obstructed, and the edit operation
719 continues as semi-shadowed update */
720 svn_boolean_t edit_obstructed;
722 /* The (new) changed_* information, cached to avoid retrieving it later */
723 svn_revnum_t changed_rev;
724 apr_time_t changed_date;
725 const char *changed_author;
727 /* If there are file content changes, these are the checksums of the
728 resulting new text base, which is in the pristine store, else NULL. */
729 const svn_checksum_t *new_text_base_md5_checksum;
730 const svn_checksum_t *new_text_base_sha1_checksum;
732 /* The checksum of the file before the update */
733 const svn_checksum_t *original_checksum;
735 /* An array of svn_prop_t structures, representing all the property
736 changes to be applied to this file. Once a file baton is
737 initialized, this is never NULL, but it may have zero elements. */
738 apr_array_header_t *propchanges;
740 /* For existing files, whether there are local modifications. FALSE for added
742 svn_boolean_t local_prop_mods;
744 /* Bump information for the directory this file lives in */
745 struct bump_dir_info *bump_info;
747 /* A boolean indicating whether this node or one of its children has
748 received any 'real' changes. Used to avoid tree conflicts for simple
749 entryprop changes, like lock management */
750 svn_boolean_t edited;
752 /* The tree conflict to install once the node is really edited */
753 svn_skel_t *edit_conflict;
757 /* Make a new file baton in a subpool of PB->pool. PB is the parent baton.
758 * PATH is relative to the root of the edit. ADDING tells whether this file
761 make_file_baton(struct file_baton **f_p,
762 struct dir_baton *pb,
764 svn_boolean_t adding,
765 apr_pool_t *scratch_pool)
767 struct edit_baton *eb = pb->edit_baton;
768 apr_pool_t *file_pool = svn_pool_create(pb->pool);
769 struct file_baton *f = apr_pcalloc(file_pool, sizeof(*f));
771 SVN_ERR_ASSERT(path);
773 /* Make the file's on-disk name. */
774 f->name = svn_dirent_basename(path, file_pool);
775 f->old_revision = SVN_INVALID_REVNUM;
776 SVN_ERR(path_join_under_root(&f->local_abspath,
777 pb->local_abspath, f->name, file_pool));
779 /* Figure out the new URL for this file. */
780 if (eb->switch_relpath)
782 /* Handle switches... */
784 /* This file has a parent directory. If there is
785 no grandparent, then we may have anchored at the parent,
786 and self is the target. If we match the target, then set
787 NEW_RELPATH to the SWITCH_RELPATH.
789 Otherwise, we simply extend NEW_RELPATH from the parent. */
790 if (pb->parent_baton == NULL
791 && strcmp(eb->target_basename, f->name) == 0)
792 f->new_relpath = eb->switch_relpath;
794 f->new_relpath = svn_relpath_join(pb->new_relpath, f->name,
797 else /* must be an update */
800 f->new_relpath = svn_relpath_join(pb->new_relpath, f->name, file_pool);
803 SVN_ERR(svn_wc__db_scan_base_repos(&f->new_relpath, NULL, NULL,
804 eb->db, f->local_abspath,
805 file_pool, scratch_pool));
806 SVN_ERR_ASSERT(f->new_relpath);
811 f->edit_baton = pb->edit_baton;
812 f->propchanges = apr_array_make(file_pool, 1, sizeof(svn_prop_t));
813 f->bump_info = pb->bump_info;
814 f->adding_file = adding;
815 f->obstruction_found = FALSE;
816 f->add_existed = FALSE;
817 f->skip_this = pb->skip_this;
818 f->shadowed = pb->shadowed || pb->edit_obstructed;
820 f->changed_rev = SVN_INVALID_REVNUM;
822 /* the directory has one more referer now */
829 /* Complete a conflict skel by describing the update.
831 * LOCAL_KIND is the node kind of the tree conflict victim in the
834 * All temporary allocations are be made in SCRATCH_POOL, while allocations
835 * needed for the returned conflict struct are made in RESULT_POOL.
838 complete_conflict(svn_skel_t *conflict,
839 const struct edit_baton *eb,
840 const char *local_abspath,
841 const char *old_repos_relpath,
842 svn_revnum_t old_revision,
843 const char *new_repos_relpath,
844 svn_node_kind_t local_kind,
845 svn_node_kind_t target_kind,
846 apr_pool_t *result_pool,
847 apr_pool_t *scratch_pool)
849 svn_wc_conflict_version_t *original_version;
850 svn_wc_conflict_version_t *target_version;
851 svn_boolean_t is_complete;
854 return SVN_NO_ERROR; /* Not conflicted */
856 SVN_ERR(svn_wc__conflict_skel_is_complete(&is_complete, conflict));
859 return SVN_NO_ERROR; /* Already completed */
861 if (old_repos_relpath)
862 original_version = svn_wc_conflict_version_create2(eb->repos_root,
869 original_version = NULL;
871 if (new_repos_relpath)
872 target_version = svn_wc_conflict_version_create2(eb->repos_root,
875 *eb->target_revision,
879 target_version = NULL;
881 if (eb->switch_relpath)
882 SVN_ERR(svn_wc__conflict_skel_set_op_switch(conflict,
885 result_pool, scratch_pool));
887 SVN_ERR(svn_wc__conflict_skel_set_op_update(conflict,
890 result_pool, scratch_pool));
896 /* Called when a directory is really edited, to avoid marking a
897 tree conflict on a node for a no-change edit */
899 mark_directory_edited(struct dir_baton *db, apr_pool_t *scratch_pool)
904 if (db->parent_baton)
905 SVN_ERR(mark_directory_edited(db->parent_baton, scratch_pool));
909 if (db->edit_conflict)
911 /* We have a (delayed) tree conflict to install */
913 SVN_ERR(complete_conflict(db->edit_conflict, db->edit_baton,
915 db->old_repos_relpath, db->old_revision,
917 svn_node_dir, svn_node_dir,
918 db->pool, scratch_pool));
919 SVN_ERR(svn_wc__db_op_mark_conflict(db->edit_baton->db,
921 db->edit_conflict, NULL,
924 do_notification(db->edit_baton, db->local_abspath, svn_node_dir,
925 svn_wc_notify_tree_conflict, scratch_pool);
926 db->already_notified = TRUE;
933 /* Called when a file is really edited, to avoid marking a
934 tree conflict on a node for a no-change edit */
936 mark_file_edited(struct file_baton *fb, apr_pool_t *scratch_pool)
941 SVN_ERR(mark_directory_edited(fb->dir_baton, scratch_pool));
945 if (fb->edit_conflict)
947 /* We have a (delayed) tree conflict to install */
949 SVN_ERR(complete_conflict(fb->edit_conflict, fb->edit_baton,
950 fb->local_abspath, fb->old_repos_relpath,
951 fb->old_revision, fb->new_relpath,
952 svn_node_file, svn_node_file,
953 fb->pool, scratch_pool));
955 SVN_ERR(svn_wc__db_op_mark_conflict(fb->edit_baton->db,
957 fb->edit_conflict, NULL,
960 do_notification(fb->edit_baton, fb->local_abspath, svn_node_file,
961 svn_wc_notify_tree_conflict, scratch_pool);
962 fb->already_notified = TRUE;
969 /* Handle the next delta window of the file described by BATON. If it is
970 * the end (WINDOW == NULL), then check the checksum, store the text in the
971 * pristine store and write its details into BATON->fb->new_text_base_*. */
973 window_handler(svn_txdelta_window_t *window, void *baton)
975 struct handler_baton *hb = baton;
976 struct file_baton *fb = hb->fb;
977 svn_wc__db_t *db = fb->edit_baton->db;
980 /* Apply this window. We may be done at that point. */
981 err = hb->apply_handler(window, hb->apply_baton);
982 if (window != NULL && !err)
985 if (hb->expected_source_checksum)
987 /* Close the stream to calculate HB->actual_source_md5_checksum. */
988 svn_error_t *err2 = svn_stream_close(hb->source_checksum_stream);
992 SVN_ERR_ASSERT(hb->expected_source_checksum->kind ==
993 hb->actual_source_checksum->kind);
995 if (!svn_checksum_match(hb->expected_source_checksum,
996 hb->actual_source_checksum))
998 err = svn_error_createf(SVN_ERR_WC_CORRUPT_TEXT_BASE, err,
999 _("Checksum mismatch while updating '%s':\n"
1002 svn_dirent_local_style(fb->local_abspath, hb->pool),
1003 svn_checksum_to_cstring(hb->expected_source_checksum,
1005 svn_checksum_to_cstring(hb->actual_source_checksum,
1010 err = svn_error_compose_create(err, err2);
1015 /* We failed to apply the delta; clean up the temporary file. */
1016 svn_error_clear(svn_io_remove_file2(hb->new_text_base_tmp_abspath, TRUE,
1021 /* Tell the file baton about the new text base's checksums. */
1022 fb->new_text_base_md5_checksum =
1023 svn_checksum__from_digest_md5(hb->new_text_base_md5_digest, fb->pool);
1024 fb->new_text_base_sha1_checksum =
1025 svn_checksum_dup(hb->new_text_base_sha1_checksum, fb->pool);
1027 /* Store the new pristine text in the pristine store now. Later, in a
1028 single transaction we will update the BASE_NODE to include a
1029 reference to this pristine text's checksum. */
1030 SVN_ERR(svn_wc__db_pristine_install(db, hb->new_text_base_tmp_abspath,
1031 fb->new_text_base_sha1_checksum,
1032 fb->new_text_base_md5_checksum,
1036 svn_pool_destroy(hb->pool);
1042 /* Find the last-change info within ENTRY_PROPS, and return then in the
1043 CHANGED_* parameters. Each parameter will be initialized to its "none"
1044 value, and will contain the relavent info if found.
1046 CHANGED_AUTHOR will be allocated in RESULT_POOL. SCRATCH_POOL will be
1047 used for some temporary allocations.
1049 static svn_error_t *
1050 accumulate_last_change(svn_revnum_t *changed_rev,
1051 apr_time_t *changed_date,
1052 const char **changed_author,
1053 const apr_array_header_t *entry_props,
1054 apr_pool_t *result_pool,
1055 apr_pool_t *scratch_pool)
1059 *changed_rev = SVN_INVALID_REVNUM;
1061 *changed_author = NULL;
1063 for (i = 0; i < entry_props->nelts; ++i)
1065 const svn_prop_t *prop = &APR_ARRAY_IDX(entry_props, i, svn_prop_t);
1067 /* A prop value of NULL means the information was not
1068 available. We don't remove this field from the entries
1069 file; we have convention just leave it empty. So let's
1070 just skip those entry props that have no values. */
1074 if (! strcmp(prop->name, SVN_PROP_ENTRY_LAST_AUTHOR))
1075 *changed_author = apr_pstrdup(result_pool, prop->value->data);
1076 else if (! strcmp(prop->name, SVN_PROP_ENTRY_COMMITTED_REV))
1079 SVN_ERR(svn_cstring_atoi64(&rev, prop->value->data));
1080 *changed_rev = (svn_revnum_t)rev;
1082 else if (! strcmp(prop->name, SVN_PROP_ENTRY_COMMITTED_DATE))
1083 SVN_ERR(svn_time_from_cstring(changed_date, prop->value->data,
1086 /* Starting with Subversion 1.7 we ignore the SVN_PROP_ENTRY_UUID
1090 return SVN_NO_ERROR;
1094 /* Join ADD_PATH to BASE_PATH. If ADD_PATH is absolute, or if any ".."
1095 * component of it resolves to a path above BASE_PATH, then return
1096 * SVN_ERR_WC_OBSTRUCTED_UPDATE.
1098 * This is to prevent the situation where the repository contains,
1099 * say, "..\nastyfile". Although that's perfectly legal on some
1100 * systems, when checked out onto Win32 it would cause "nastyfile" to
1101 * be created in the parent of the current edit directory.
1103 * (http://cve.mitre.org/cgi-bin/cvename.cgi?name=2007-3846)
1105 static svn_error_t *
1106 path_join_under_root(const char **result_path,
1107 const char *base_path,
1108 const char *add_path,
1111 svn_boolean_t under_root;
1113 SVN_ERR(svn_dirent_is_under_root(&under_root,
1114 result_path, base_path, add_path, pool));
1118 return svn_error_createf(
1119 SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
1120 _("Path '%s' is not in the working copy"),
1121 svn_dirent_local_style(svn_dirent_join(base_path, add_path, pool),
1125 /* This catches issue #3288 */
1126 if (strcmp(add_path, svn_dirent_basename(*result_path, NULL)) != 0)
1128 return svn_error_createf(
1129 SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
1130 _("'%s' is not valid as filename in directory '%s'"),
1131 svn_dirent_local_style(add_path, pool),
1132 svn_dirent_local_style(base_path, pool));
1135 return SVN_NO_ERROR;
1139 /*** The callbacks we'll plug into an svn_delta_editor_t structure. ***/
1141 /* An svn_delta_editor_t function. */
1142 static svn_error_t *
1143 set_target_revision(void *edit_baton,
1144 svn_revnum_t target_revision,
1147 struct edit_baton *eb = edit_baton;
1149 *(eb->target_revision) = target_revision;
1150 return SVN_NO_ERROR;
1153 static svn_error_t *
1154 check_tree_conflict(svn_skel_t **pconflict,
1155 struct edit_baton *eb,
1156 const char *local_abspath,
1157 svn_wc__db_status_t working_status,
1158 svn_boolean_t exists_in_repos,
1159 svn_node_kind_t expected_kind,
1160 svn_wc_conflict_action_t action,
1161 apr_pool_t *result_pool,
1162 apr_pool_t *scratch_pool);
1164 /* An svn_delta_editor_t function. */
1165 static svn_error_t *
1166 open_root(void *edit_baton,
1167 svn_revnum_t base_revision, /* This is ignored in co */
1171 struct edit_baton *eb = edit_baton;
1172 struct dir_baton *db;
1173 svn_boolean_t already_conflicted, conflict_ignored;
1175 svn_wc__db_status_t status;
1176 svn_wc__db_status_t base_status;
1177 svn_node_kind_t kind;
1178 svn_boolean_t have_work;
1180 /* Note that something interesting is actually happening in this
1182 eb->root_opened = TRUE;
1184 SVN_ERR(make_dir_baton(&db, NULL, eb, NULL, FALSE, pool));
1187 err = already_in_a_tree_conflict(&already_conflicted, &conflict_ignored,
1188 eb->db, db->local_abspath, pool);
1192 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
1193 return svn_error_trace(err);
1195 svn_error_clear(err);
1196 already_conflicted = conflict_ignored = FALSE;
1198 else if (already_conflicted)
1200 /* Record a skip of both the anchor and target in the skipped tree
1201 as the anchor itself might not be updated */
1202 SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
1203 SVN_ERR(remember_skipped_tree(eb, eb->target_abspath, pool));
1205 db->skip_this = TRUE;
1206 db->already_notified = TRUE;
1208 /* Notify that we skipped the target, while we actually skipped
1210 do_notification(eb, eb->target_abspath, svn_node_unknown,
1211 svn_wc_notify_skip_conflicted, pool);
1213 return SVN_NO_ERROR;
1217 SVN_ERR(svn_wc__db_read_info(&status, &kind, &db->old_revision,
1218 &db->old_repos_relpath, NULL, NULL,
1219 &db->changed_rev, &db->changed_date,
1220 &db->changed_author, &db->ambient_depth,
1221 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1222 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1223 NULL, NULL, &have_work,
1224 eb->db, db->local_abspath,
1227 if (conflict_ignored)
1229 db->shadowed = TRUE;
1233 const char *move_src_root_abspath;
1235 SVN_ERR(svn_wc__db_base_moved_to(NULL, NULL, &move_src_root_abspath,
1236 NULL, eb->db, db->local_abspath,
1238 if (move_src_root_abspath || *eb->target_basename == '\0')
1239 SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL,
1241 &db->old_repos_relpath, NULL, NULL,
1242 &db->changed_rev, &db->changed_date,
1243 &db->changed_author,
1245 NULL, NULL, NULL, NULL, NULL, NULL,
1246 eb->db, db->local_abspath,
1249 if (move_src_root_abspath)
1251 /* This is an update anchored inside a move. We need to
1252 raise a move-edit tree-conflict on the move root to
1253 update the move destination. */
1254 svn_skel_t *tree_conflict = svn_wc__conflict_skel_create(pool);
1256 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
1257 tree_conflict, eb->db, move_src_root_abspath,
1258 svn_wc_conflict_reason_moved_away,
1259 svn_wc_conflict_action_edit,
1260 move_src_root_abspath, pool, pool));
1262 if (strcmp(db->local_abspath, move_src_root_abspath))
1264 /* We are raising the tree-conflict on some parent of
1265 the edit root, we won't be handling that path again
1266 so raise the conflict now. */
1267 SVN_ERR(complete_conflict(tree_conflict, eb,
1268 move_src_root_abspath,
1269 db->old_repos_relpath,
1270 db->old_revision, db->new_relpath,
1271 svn_node_dir, svn_node_dir,
1273 SVN_ERR(svn_wc__db_op_mark_conflict(eb->db,
1274 move_src_root_abspath,
1277 do_notification(eb, move_src_root_abspath, svn_node_dir,
1278 svn_wc_notify_tree_conflict, pool);
1281 db->edit_conflict = tree_conflict;
1284 db->shadowed = TRUE; /* Needed for the close_directory() on the root, to
1285 make sure it doesn't use the ACTUAL tree */
1288 base_status = status;
1290 if (*eb->target_basename == '\0')
1292 /* For an update with a NULL target, this is equivalent to open_dir(): */
1294 db->was_incomplete = (base_status == svn_wc__db_status_incomplete);
1296 /* ### TODO: Add some tree conflict and obstruction detection, etc. like
1297 open_directory() does.
1298 (or find a way to reuse that code here)
1300 ### BH 2013: I don't think we need all of the detection here, as the
1301 user explicitly asked to update this node. So we don't
1302 have to tell that it is a local replacement/delete.
1305 SVN_ERR(svn_wc__db_temp_op_start_directory_update(eb->db,
1308 *eb->target_revision,
1312 return SVN_NO_ERROR;
1316 /* ===================================================================== */
1317 /* Checking for local modifications. */
1319 /* A baton for use with modcheck_found_entry(). */
1320 typedef struct modcheck_baton_t {
1321 svn_wc__db_t *db; /* wc_db to access nodes */
1322 svn_boolean_t found_mod; /* whether a modification has been found */
1323 svn_boolean_t found_not_delete; /* Found a not-delete modification */
1326 /* An implementation of svn_wc_status_func4_t. */
1327 static svn_error_t *
1328 modcheck_callback(void *baton,
1329 const char *local_abspath,
1330 const svn_wc_status3_t *status,
1331 apr_pool_t *scratch_pool)
1333 modcheck_baton_t *mb = baton;
1335 switch (status->node_status)
1337 case svn_wc_status_normal:
1338 case svn_wc_status_incomplete:
1339 case svn_wc_status_ignored:
1340 case svn_wc_status_none:
1341 case svn_wc_status_unversioned:
1342 case svn_wc_status_external:
1345 case svn_wc_status_deleted:
1346 mb->found_mod = TRUE;
1349 case svn_wc_status_missing:
1350 case svn_wc_status_obstructed:
1351 mb->found_mod = TRUE;
1352 mb->found_not_delete = TRUE;
1353 /* Exit from the status walker: We know what we want to know */
1354 return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
1357 case svn_wc_status_added:
1358 case svn_wc_status_replaced:
1359 case svn_wc_status_modified:
1360 mb->found_mod = TRUE;
1361 mb->found_not_delete = TRUE;
1362 /* Exit from the status walker: We know what we want to know */
1363 return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
1366 return SVN_NO_ERROR;
1370 /* Set *MODIFIED to true iff there are any local modifications within the
1371 * tree rooted at LOCAL_ABSPATH, using DB. If *MODIFIED
1372 * is set to true and all the local modifications were deletes then set
1373 * *ALL_EDITS_ARE_DELETES to true, set it to false otherwise. LOCAL_ABSPATH
1374 * may be a file or a directory. */
1376 svn_wc__node_has_local_mods(svn_boolean_t *modified,
1377 svn_boolean_t *all_edits_are_deletes,
1379 const char *local_abspath,
1380 svn_cancel_func_t cancel_func,
1382 apr_pool_t *scratch_pool)
1384 modcheck_baton_t modcheck_baton = { NULL, FALSE, FALSE };
1387 modcheck_baton.db = db;
1389 /* Walk the WC tree for status with depth infinity, looking for any local
1390 * modifications. If it's a "sparse" directory, that's OK: there can be
1391 * no local mods in the pieces that aren't present in the WC. */
1393 err = svn_wc__internal_walk_status(db, local_abspath,
1395 FALSE, FALSE, FALSE, NULL,
1396 modcheck_callback, &modcheck_baton,
1397 cancel_func, cancel_baton,
1400 if (err && err->apr_err == SVN_ERR_CEASE_INVOCATION)
1401 svn_error_clear(err);
1405 *modified = modcheck_baton.found_mod;
1406 *all_edits_are_deletes = (modcheck_baton.found_mod
1407 && !modcheck_baton.found_not_delete);
1409 return SVN_NO_ERROR;
1412 /* Indicates an unset svn_wc_conflict_reason_t. */
1413 #define SVN_WC_CONFLICT_REASON_NONE (svn_wc_conflict_reason_t)(-1)
1415 /* Check whether the incoming change ACTION on FULL_PATH would conflict with
1416 * LOCAL_ABSPATH's scheduled change. If so, then raise a tree conflict with
1417 * LOCAL_ABSPATH as the victim.
1419 * The edit baton EB gives information including whether the operation is
1420 * an update or a switch.
1422 * WORKING_STATUS is the current node status of LOCAL_ABSPATH
1423 * and EXISTS_IN_REPOS specifies whether a BASE_NODE representation for exists
1424 * for this node. In that case the on disk type is compared to EXPECTED_KIND.
1426 * If a tree conflict reason was found for the incoming action, the resulting
1427 * tree conflict info is returned in *PCONFLICT. PCONFLICT must be non-NULL,
1428 * while *PCONFLICT is always overwritten.
1430 * The tree conflict is allocated in RESULT_POOL. Temporary allocations use
1433 static svn_error_t *
1434 check_tree_conflict(svn_skel_t **pconflict,
1435 struct edit_baton *eb,
1436 const char *local_abspath,
1437 svn_wc__db_status_t working_status,
1438 svn_boolean_t exists_in_repos,
1439 svn_node_kind_t expected_kind,
1440 svn_wc_conflict_action_t action,
1441 apr_pool_t *result_pool,
1442 apr_pool_t *scratch_pool)
1444 svn_wc_conflict_reason_t reason = SVN_WC_CONFLICT_REASON_NONE;
1445 svn_boolean_t modified = FALSE;
1446 svn_boolean_t all_mods_are_deletes = FALSE;
1447 const char *move_src_op_root_abspath = NULL;
1451 /* Find out if there are any local changes to this node that may
1452 * be the "reason" of a tree-conflict with the incoming "action". */
1453 switch (working_status)
1455 case svn_wc__db_status_added:
1456 case svn_wc__db_status_moved_here:
1457 case svn_wc__db_status_copied:
1458 if (!exists_in_repos)
1460 /* The node is locally added, and it did not exist before. This
1461 * is an 'update', so the local add can only conflict with an
1462 * incoming 'add'. In fact, if we receive anything else than an
1463 * svn_wc_conflict_action_add (which includes 'added',
1464 * 'copied-here' and 'moved-here') during update on a node that
1465 * did not exist before, then something is very wrong.
1466 * Note that if there was no action on the node, this code
1467 * would not have been called in the first place. */
1468 SVN_ERR_ASSERT(action == svn_wc_conflict_action_add);
1470 /* Scan the addition in case our caller didn't. */
1471 if (working_status == svn_wc__db_status_added)
1472 SVN_ERR(svn_wc__db_scan_addition(&working_status, NULL, NULL,
1473 NULL, NULL, NULL, NULL,
1475 eb->db, local_abspath,
1476 scratch_pool, scratch_pool));
1478 if (working_status == svn_wc__db_status_moved_here)
1479 reason = svn_wc_conflict_reason_moved_here;
1481 reason = svn_wc_conflict_reason_added;
1485 /* The node is locally replaced but could also be moved-away. */
1486 SVN_ERR(svn_wc__db_base_moved_to(NULL, NULL, NULL,
1487 &move_src_op_root_abspath,
1488 eb->db, local_abspath,
1489 scratch_pool, scratch_pool));
1490 if (move_src_op_root_abspath)
1491 reason = svn_wc_conflict_reason_moved_away;
1493 reason = svn_wc_conflict_reason_replaced;
1498 case svn_wc__db_status_deleted:
1500 SVN_ERR(svn_wc__db_base_moved_to(NULL, NULL, NULL,
1501 &move_src_op_root_abspath,
1502 eb->db, local_abspath,
1503 scratch_pool, scratch_pool));
1504 if (move_src_op_root_abspath)
1505 reason = svn_wc_conflict_reason_moved_away;
1507 reason = svn_wc_conflict_reason_deleted;
1511 case svn_wc__db_status_incomplete:
1512 /* We used svn_wc__db_read_info(), so 'incomplete' means
1513 * - there is no node in the WORKING tree
1514 * - a BASE node is known to exist
1515 * So the node exists and is essentially 'normal'. We still need to
1516 * check prop and text mods, and those checks will retrieve the
1517 * missing information (hopefully). */
1518 case svn_wc__db_status_normal:
1519 if (action == svn_wc_conflict_action_edit)
1521 /* An edit onto a local edit or onto *no* local changes is no
1522 * tree-conflict. (It's possibly a text- or prop-conflict,
1523 * but we don't handle those here.)
1525 * Except when there is a local obstruction
1527 if (exists_in_repos)
1529 svn_node_kind_t disk_kind;
1531 SVN_ERR(svn_io_check_path(local_abspath, &disk_kind,
1534 if (disk_kind != expected_kind && disk_kind != svn_node_none)
1536 reason = svn_wc_conflict_reason_obstructed;
1541 return SVN_NO_ERROR;
1544 /* Replace is handled as delete and then specifically in
1545 add_directory() and add_file(), so we only expect deletes here */
1546 SVN_ERR_ASSERT(action == svn_wc_conflict_action_delete);
1548 /* Check if the update wants to delete or replace a locally
1552 /* Do a deep tree detection of local changes. The update editor will
1553 * not visit the subdirectories of a directory that it wants to delete.
1554 * Therefore, we need to start a separate crawl here. */
1556 SVN_ERR(svn_wc__node_has_local_mods(&modified, &all_mods_are_deletes,
1557 eb->db, local_abspath,
1558 eb->cancel_func, eb->cancel_baton,
1563 if (all_mods_are_deletes)
1564 reason = svn_wc_conflict_reason_deleted;
1566 reason = svn_wc_conflict_reason_edited;
1570 case svn_wc__db_status_server_excluded:
1571 /* Not allowed to view the node. Not allowed to report tree
1573 case svn_wc__db_status_excluded:
1574 /* Locally marked as excluded. No conflicts wanted. */
1575 case svn_wc__db_status_not_present:
1576 /* A committed delete (but parent not updated). The delete is
1577 committed, so no conflict possible during update. */
1578 return SVN_NO_ERROR;
1580 case svn_wc__db_status_base_deleted:
1581 /* An internal status. Should never show up here. */
1582 SVN_ERR_MALFUNCTION();
1587 if (reason == SVN_WC_CONFLICT_REASON_NONE)
1588 /* No conflict with the current action. */
1589 return SVN_NO_ERROR;
1592 /* Sanity checks. Note that if there was no action on the node, this function
1593 * would not have been called in the first place.*/
1594 if (reason == svn_wc_conflict_reason_edited
1595 || reason == svn_wc_conflict_reason_obstructed
1596 || reason == svn_wc_conflict_reason_deleted
1597 || reason == svn_wc_conflict_reason_moved_away
1598 || reason == svn_wc_conflict_reason_replaced)
1600 /* When the node existed before (it was locally deleted, replaced or
1601 * edited), then 'update' cannot add it "again". So it can only send
1602 * _action_edit, _delete or _replace. */
1603 if (action != svn_wc_conflict_action_edit
1604 && action != svn_wc_conflict_action_delete
1605 && action != svn_wc_conflict_action_replace)
1606 return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
1607 _("Unexpected attempt to add a node at path '%s'"),
1608 svn_dirent_local_style(local_abspath, scratch_pool));
1610 else if (reason == svn_wc_conflict_reason_added ||
1611 reason == svn_wc_conflict_reason_moved_here)
1613 /* When the node did not exist before (it was locally added),
1614 * then 'update' cannot want to modify it in any way.
1615 * It can only send _action_add. */
1616 if (action != svn_wc_conflict_action_add)
1617 return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
1618 _("Unexpected attempt to edit, delete, or replace "
1619 "a node at path '%s'"),
1620 svn_dirent_local_style(local_abspath, scratch_pool));
1625 /* A conflict was detected. Create a conflict skel to record it. */
1626 *pconflict = svn_wc__conflict_skel_create(result_pool);
1628 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(*pconflict,
1629 eb->db, local_abspath,
1632 move_src_op_root_abspath,
1633 result_pool, scratch_pool));
1635 return SVN_NO_ERROR;
1639 /* If LOCAL_ABSPATH is inside a conflicted tree and the conflict is
1640 * not a moved-away-edit conflict, set *CONFLICTED to TRUE. Otherwise
1641 * set *CONFLICTED to FALSE.
1643 static svn_error_t *
1644 already_in_a_tree_conflict(svn_boolean_t *conflicted,
1645 svn_boolean_t *ignored,
1647 const char *local_abspath,
1648 apr_pool_t *scratch_pool)
1650 const char *ancestor_abspath = local_abspath;
1651 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1653 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1655 *conflicted = *ignored = FALSE;
1659 svn_boolean_t is_wc_root;
1661 svn_pool_clear(iterpool);
1663 SVN_ERR(svn_wc__conflicted_for_update_p(conflicted, ignored, db,
1664 ancestor_abspath, TRUE,
1666 if (*conflicted || *ignored)
1669 SVN_ERR(svn_wc__db_is_wcroot(&is_wc_root, db, ancestor_abspath,
1674 ancestor_abspath = svn_dirent_dirname(ancestor_abspath, scratch_pool);
1677 svn_pool_destroy(iterpool);
1679 return SVN_NO_ERROR;
1682 /* Temporary helper until the new conflict handling is in place */
1683 static svn_error_t *
1684 node_already_conflicted(svn_boolean_t *conflicted,
1685 svn_boolean_t *conflict_ignored,
1687 const char *local_abspath,
1688 apr_pool_t *scratch_pool)
1690 SVN_ERR(svn_wc__conflicted_for_update_p(conflicted, conflict_ignored, db,
1691 local_abspath, FALSE,
1694 return SVN_NO_ERROR;
1698 /* An svn_delta_editor_t function. */
1699 static svn_error_t *
1700 delete_entry(const char *path,
1701 svn_revnum_t revision,
1705 struct dir_baton *pb = parent_baton;
1706 struct edit_baton *eb = pb->edit_baton;
1707 const char *base = svn_relpath_basename(path, NULL);
1708 const char *local_abspath;
1709 const char *repos_relpath;
1710 svn_node_kind_t kind, base_kind;
1711 svn_revnum_t old_revision;
1712 svn_boolean_t conflicted;
1713 svn_boolean_t have_work;
1714 svn_skel_t *tree_conflict = NULL;
1715 svn_wc__db_status_t status;
1716 svn_wc__db_status_t base_status;
1717 apr_pool_t *scratch_pool;
1718 svn_boolean_t deleting_target;
1719 svn_boolean_t deleting_switched;
1720 svn_boolean_t keep_as_working = FALSE;
1721 svn_boolean_t queue_deletes = TRUE;
1724 return SVN_NO_ERROR;
1726 scratch_pool = svn_pool_create(pb->pool);
1728 SVN_ERR(mark_directory_edited(pb, scratch_pool));
1730 SVN_ERR(path_join_under_root(&local_abspath, pb->local_abspath, base,
1733 deleting_target = (strcmp(local_abspath, eb->target_abspath) == 0);
1735 /* Detect obstructing working copies */
1737 svn_boolean_t is_root;
1739 SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, local_abspath,
1744 /* Just skip this node; a future update will handle it */
1745 SVN_ERR(remember_skipped_tree(eb, local_abspath, pool));
1746 do_notification(eb, local_abspath, svn_node_unknown,
1747 svn_wc_notify_update_skip_obstruction, scratch_pool);
1749 svn_pool_destroy(scratch_pool);
1751 return SVN_NO_ERROR;
1755 SVN_ERR(svn_wc__db_read_info(&status, &kind, &old_revision, &repos_relpath,
1756 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1757 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1758 &conflicted, NULL, NULL, NULL,
1759 NULL, NULL, &have_work,
1760 eb->db, local_abspath,
1761 scratch_pool, scratch_pool));
1765 base_status = status;
1769 SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, &old_revision,
1771 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1772 NULL, NULL, NULL, NULL, NULL,
1773 eb->db, local_abspath,
1774 scratch_pool, scratch_pool));
1776 if (pb->old_repos_relpath && repos_relpath)
1778 const char *expected_name;
1780 expected_name = svn_relpath_skip_ancestor(pb->old_repos_relpath,
1783 deleting_switched = (!expected_name || strcmp(expected_name, base) != 0);
1786 deleting_switched = FALSE;
1788 /* Is this path a conflict victim? */
1790 conflicted = FALSE; /* Conflict applies to WORKING */
1791 else if (conflicted)
1792 SVN_ERR(node_already_conflicted(&conflicted, NULL,
1793 eb->db, local_abspath, scratch_pool));
1796 SVN_ERR(remember_skipped_tree(eb, local_abspath, scratch_pool));
1798 do_notification(eb, local_abspath, svn_node_unknown,
1799 svn_wc_notify_skip_conflicted,
1802 svn_pool_destroy(scratch_pool);
1804 return SVN_NO_ERROR;
1808 /* Receive the remote removal of excluded/server-excluded/not present node.
1809 Do not notify, but perform the change even when the node is shadowed */
1810 if (base_status == svn_wc__db_status_not_present
1811 || base_status == svn_wc__db_status_excluded
1812 || base_status == svn_wc__db_status_server_excluded)
1814 SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath,
1815 FALSE /* keep_as_working */,
1816 FALSE /* queue_deletes */,
1817 FALSE /* remove_locks */,
1818 SVN_INVALID_REVNUM /* not_present_rev */,
1822 if (deleting_target)
1823 eb->target_deleted = TRUE;
1825 svn_pool_destroy(scratch_pool);
1827 return SVN_NO_ERROR;
1830 /* Is this path the victim of a newly-discovered tree conflict? If so,
1831 * remember it and notify the client. Then (if it was existing and
1832 * modified), re-schedule the node to be added back again, as a (modified)
1833 * copy of the previous base version. */
1835 /* Check for conflicts only when we haven't already recorded
1836 * a tree-conflict on a parent node. */
1837 if (!pb->shadowed && !pb->edit_obstructed)
1839 SVN_ERR(check_tree_conflict(&tree_conflict, eb, local_abspath,
1841 (kind == svn_node_dir)
1844 svn_wc_conflict_action_delete,
1845 pb->pool, scratch_pool));
1848 queue_deletes = FALSE; /* There is no in-wc representation */
1850 if (tree_conflict != NULL)
1852 svn_wc_conflict_reason_t reason;
1853 /* When we raise a tree conflict on a node, we don't want to mark the
1854 * node as skipped, to allow a replacement to continue doing at least
1855 * a bit of its work (possibly adding a not present node, for the
1857 if (!pb->deletion_conflicts)
1858 pb->deletion_conflicts = apr_hash_make(pb->pool);
1860 svn_hash_sets(pb->deletion_conflicts, apr_pstrdup(pb->pool, base),
1863 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
1864 eb->db, local_abspath,
1866 scratch_pool, scratch_pool));
1868 if (reason == svn_wc_conflict_reason_edited
1869 || reason == svn_wc_conflict_reason_obstructed)
1871 /* The item exists locally and has some sort of local mod.
1872 * It no longer exists in the repository at its target URL@REV.
1874 * To prepare the "accept mine" resolution for the tree conflict,
1875 * we must schedule the existing content for re-addition as a copy
1876 * of what it was, but with its local modifications preserved. */
1877 keep_as_working = TRUE;
1879 /* Fall through to remove the BASE_NODEs properly, with potentially
1880 keeping a not-present marker */
1882 else if (reason == svn_wc_conflict_reason_deleted
1883 || reason == svn_wc_conflict_reason_moved_away
1884 || reason == svn_wc_conflict_reason_replaced)
1886 /* The item does not exist locally because it was already shadowed.
1887 * We must complete the deletion, leaving the tree conflict info
1888 * as the only difference from a normal deletion. */
1890 /* Fall through to the normal "delete" code path. */
1893 SVN_ERR_MALFUNCTION(); /* other reasons are not expected here */
1896 SVN_ERR(complete_conflict(tree_conflict, eb, local_abspath, repos_relpath,
1898 (kind == svn_node_dir)
1902 pb->pool, scratch_pool));
1904 /* Issue a wq operation to delete the BASE_NODE data and to delete actual
1905 nodes based on that from disk, but leave any WORKING_NODEs on disk.
1907 Local modifications are already turned into copies at this point.
1909 If the thing being deleted is the *target* of this update, then
1910 we need to recreate a 'deleted' entry, so that the parent can give
1911 accurate reports about itself in the future. */
1912 if (! deleting_target && ! deleting_switched)
1914 /* Delete, and do not leave a not-present node. */
1915 SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath,
1916 keep_as_working, queue_deletes, FALSE,
1917 SVN_INVALID_REVNUM /* not_present_rev */,
1918 tree_conflict, NULL,
1923 /* Delete, leaving a not-present node. */
1924 SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath,
1925 keep_as_working, queue_deletes, FALSE,
1926 *eb->target_revision,
1927 tree_conflict, NULL,
1929 if (deleting_target)
1930 eb->target_deleted = TRUE;
1933 /* Don't remove the not-present marker at the final bump */
1934 SVN_ERR(remember_skipped_tree(eb, local_abspath, pool));
1938 SVN_ERR(svn_wc__wq_run(eb->db, pb->local_abspath,
1939 eb->cancel_func, eb->cancel_baton,
1945 if (eb->conflict_func)
1946 SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, local_abspath,
1948 NULL /* merge_options */,
1954 do_notification(eb, local_abspath, svn_node_unknown,
1955 svn_wc_notify_tree_conflict, scratch_pool);
1959 svn_wc_notify_action_t action = svn_wc_notify_update_delete;
1960 svn_node_kind_t node_kind;
1962 if (pb->shadowed || pb->edit_obstructed)
1963 action = svn_wc_notify_update_shadowed_delete;
1965 if (kind == svn_node_dir)
1966 node_kind = svn_node_dir;
1968 node_kind = svn_node_file;
1970 do_notification(eb, local_abspath, node_kind, action, scratch_pool);
1973 svn_pool_destroy(scratch_pool);
1975 return SVN_NO_ERROR;
1978 /* An svn_delta_editor_t function. */
1979 static svn_error_t *
1980 add_directory(const char *path,
1982 const char *copyfrom_path,
1983 svn_revnum_t copyfrom_rev,
1987 struct dir_baton *pb = parent_baton;
1988 struct edit_baton *eb = pb->edit_baton;
1989 struct dir_baton *db;
1990 svn_node_kind_t kind;
1991 svn_wc__db_status_t status;
1992 svn_node_kind_t wc_kind;
1993 svn_boolean_t conflicted;
1994 svn_boolean_t conflict_ignored = FALSE;
1995 svn_boolean_t versioned_locally_and_present;
1996 svn_skel_t *tree_conflict = NULL;
1999 SVN_ERR_ASSERT(! (copyfrom_path || SVN_IS_VALID_REVNUM(copyfrom_rev)));
2001 SVN_ERR(make_dir_baton(&db, path, eb, pb, TRUE, pool));
2005 return SVN_NO_ERROR;
2007 SVN_ERR(mark_directory_edited(db, pool));
2009 if (strcmp(eb->target_abspath, db->local_abspath) == 0)
2011 /* The target of the edit is being added, give it the requested
2012 depth of the edit (but convert svn_depth_unknown to
2013 svn_depth_infinity). */
2014 db->ambient_depth = (eb->requested_depth == svn_depth_unknown)
2015 ? svn_depth_infinity : eb->requested_depth;
2017 else if (eb->requested_depth == svn_depth_immediates
2018 || (eb->requested_depth == svn_depth_unknown
2019 && pb->ambient_depth == svn_depth_immediates))
2021 db->ambient_depth = svn_depth_empty;
2025 db->ambient_depth = svn_depth_infinity;
2028 /* It may not be named the same as the administrative directory. */
2029 if (svn_wc_is_adm_dir(db->name, pool))
2030 return svn_error_createf(
2031 SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
2032 _("Failed to add directory '%s': object of the same name as the "
2033 "administrative directory"),
2034 svn_dirent_local_style(db->local_abspath, pool));
2036 SVN_ERR(svn_io_check_path(db->local_abspath, &kind, db->pool));
2038 err = svn_wc__db_read_info(&status, &wc_kind, NULL, NULL, NULL, NULL, NULL,
2039 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
2040 NULL, NULL, NULL, NULL, NULL,
2041 &conflicted, NULL, NULL, NULL, NULL, NULL, NULL,
2042 eb->db, db->local_abspath, db->pool, db->pool);
2045 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
2046 return svn_error_trace(err);
2048 svn_error_clear(err);
2049 wc_kind = svn_node_unknown;
2050 status = svn_wc__db_status_normal;
2053 versioned_locally_and_present = FALSE;
2055 else if (wc_kind == svn_node_dir
2056 && status == svn_wc__db_status_normal)
2058 /* !! We found the root of a separate working copy obstructing the wc !!
2060 If the directory would be part of our own working copy then
2061 we wouldn't have been called as an add_directory().
2063 The only thing we can do is add a not-present node, to allow
2064 a future update to bring in the new files when the problem is
2065 resolved. Note that svn_wc__db_base_add_not_present_node()
2066 explicitly adds the node into the parent's node database. */
2068 SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db, db->local_abspath,
2072 *eb->target_revision,
2077 SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
2078 db->skip_this = TRUE;
2079 db->already_notified = TRUE;
2081 do_notification(eb, db->local_abspath, svn_node_dir,
2082 svn_wc_notify_update_skip_obstruction, pool);
2084 return SVN_NO_ERROR;
2086 else if (status == svn_wc__db_status_normal
2087 && (wc_kind == svn_node_file
2088 || wc_kind == svn_node_symlink))
2090 /* We found a file external occupating the place we need in BASE.
2092 We can't add a not-present node in this case as that would overwrite
2093 the file external. Luckily the file external itself stops us from
2094 forgetting a child of this parent directory like an obstructing
2097 The reason we get here is that the adm crawler doesn't report
2101 SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
2102 db->skip_this = TRUE;
2103 db->already_notified = TRUE;
2105 do_notification(eb, db->local_abspath, svn_node_file,
2106 svn_wc_notify_update_skip_obstruction, pool);
2108 return SVN_NO_ERROR;
2110 else if (wc_kind == svn_node_unknown)
2111 versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */
2113 versioned_locally_and_present = IS_NODE_PRESENT(status);
2115 /* Is this path a conflict victim? */
2118 if (pb->deletion_conflicts)
2119 tree_conflict = svn_hash_gets(pb->deletion_conflicts, db->name);
2123 svn_wc_conflict_reason_t reason;
2124 /* So this deletion wasn't just a deletion, it is actually a
2125 replacement. Let's install a better tree conflict. */
2127 /* ### Should store the conflict in DB to allow reinstalling
2128 ### with theoretically more data in close_directory() */
2130 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
2134 db->pool, db->pool));
2136 tree_conflict = svn_wc__conflict_skel_create(db->pool);
2138 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
2140 eb->db, db->local_abspath,
2141 reason, svn_wc_conflict_action_replace,
2143 db->pool, db->pool));
2145 /* And now stop checking for conflicts here and just perform
2146 a shadowed update */
2147 db->edit_conflict = tree_conflict; /* Cache for close_directory */
2148 tree_conflict = NULL; /* No direct notification */
2149 db->shadowed = TRUE; /* Just continue */
2150 conflicted = FALSE; /* No skip */
2153 SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
2154 eb->db, db->local_abspath, pool));
2157 /* Now the "usual" behaviour if already conflicted. Skip it. */
2160 /* Record this conflict so that its descendants are skipped silently. */
2161 SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
2163 db->skip_this = TRUE;
2164 db->already_notified = TRUE;
2166 /* We skip this node, but once the update completes the parent node will
2167 be updated to the new revision. So a future recursive update of the
2168 parent will not bring in this new node as the revision of the parent
2169 describes to the repository that all children are available.
2171 To resolve this problem, we add a not-present node to allow bringing
2172 the node in once this conflict is resolved.
2174 Note that we can safely assume that no present base node exists,
2175 because then we would not have received an add_directory.
2177 SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db, db->local_abspath,
2181 *eb->target_revision,
2186 /* ### TODO: Also print victim_path in the skip msg. */
2187 do_notification(eb, db->local_abspath, svn_node_dir,
2188 svn_wc_notify_skip_conflicted, pool);
2189 return SVN_NO_ERROR;
2191 else if (conflict_ignored)
2193 db->shadowed = TRUE;
2198 /* Nothing to check; does not and will not exist in working copy */
2200 else if (versioned_locally_and_present)
2202 /* What to do with a versioned or schedule-add dir:
2204 A dir already added without history is OK. Set add_existed
2205 so that user notification is delayed until after any prop
2206 conflicts have been found.
2208 An existing versioned dir is an error. In the future we may
2209 relax this restriction and simply update such dirs.
2211 A dir added with history is a tree conflict. */
2213 svn_boolean_t local_is_non_dir;
2214 svn_wc__db_status_t add_status = svn_wc__db_status_normal;
2216 /* Is the local add a copy? */
2217 if (status == svn_wc__db_status_added)
2218 SVN_ERR(svn_wc__db_scan_addition(&add_status, NULL, NULL, NULL, NULL,
2219 NULL, NULL, NULL, NULL,
2220 eb->db, db->local_abspath,
2224 /* Is there *something* that is not a dir? */
2225 local_is_non_dir = (wc_kind != svn_node_dir
2226 && status != svn_wc__db_status_deleted);
2228 /* Do tree conflict checking if
2229 * - if there is a local copy.
2230 * - if this is a switch operation
2231 * - the node kinds mismatch
2233 * During switch, local adds at the same path as incoming adds get
2234 * "lost" in that switching back to the original will no longer have the
2235 * local add. So switch always alerts the user with a tree conflict. */
2236 if (!eb->adds_as_modification
2238 || add_status != svn_wc__db_status_added)
2240 SVN_ERR(check_tree_conflict(&tree_conflict, eb,
2242 status, FALSE, svn_node_none,
2243 svn_wc_conflict_action_add,
2247 if (tree_conflict == NULL)
2248 db->add_existed = TRUE; /* Take over WORKING */
2250 db->shadowed = TRUE; /* Only update BASE */
2252 else if (kind != svn_node_none)
2254 /* There's an unversioned node at this path. */
2255 db->obstruction_found = TRUE;
2257 /* Unversioned, obstructing dirs are handled by prop merge/conflict,
2258 * if unversioned obstructions are allowed. */
2259 if (! (kind == svn_node_dir && eb->allow_unver_obstructions))
2261 /* Bring in the node as deleted */ /* ### Obstructed Conflict */
2262 db->shadowed = TRUE;
2264 /* Mark a conflict */
2265 tree_conflict = svn_wc__conflict_skel_create(db->pool);
2267 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
2269 eb->db, db->local_abspath,
2270 svn_wc_conflict_reason_unversioned,
2271 svn_wc_conflict_action_add, NULL,
2273 db->edit_conflict = tree_conflict;
2278 SVN_ERR(complete_conflict(tree_conflict, eb, db->local_abspath,
2279 db->old_repos_relpath, db->old_revision,
2285 SVN_ERR(svn_wc__db_base_add_incomplete_directory(
2286 eb->db, db->local_abspath,
2290 *eb->target_revision,
2292 (db->shadowed && db->obstruction_found),
2294 && status == svn_wc__db_status_added),
2295 tree_conflict, NULL,
2298 /* Make sure there is a real directory at LOCAL_ABSPATH, unless we are just
2301 SVN_ERR(svn_wc__ensure_directory(db->local_abspath, pool));
2303 if (tree_conflict != NULL)
2305 if (eb->conflict_func)
2306 SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, db->local_abspath,
2308 NULL /* merge_options */,
2315 db->already_notified = TRUE;
2316 do_notification(eb, db->local_abspath, svn_node_dir,
2317 svn_wc_notify_tree_conflict, pool);
2321 /* If this add was obstructed by dir scheduled for addition without
2322 history let close_directory() handle the notification because there
2323 might be properties to deal with. If PATH was added inside a locally
2324 deleted tree, then suppress notification, a tree conflict was already
2326 if (eb->notify_func && !db->already_notified && !db->add_existed)
2328 svn_wc_notify_action_t action;
2331 action = svn_wc_notify_update_shadowed_add;
2332 else if (db->obstruction_found || db->add_existed)
2333 action = svn_wc_notify_exists;
2335 action = svn_wc_notify_update_add;
2337 db->already_notified = TRUE;
2339 do_notification(eb, db->local_abspath, svn_node_dir, action, pool);
2342 return SVN_NO_ERROR;
2345 /* An svn_delta_editor_t function. */
2346 static svn_error_t *
2347 open_directory(const char *path,
2349 svn_revnum_t base_revision,
2353 struct dir_baton *db, *pb = parent_baton;
2354 struct edit_baton *eb = pb->edit_baton;
2355 svn_boolean_t have_work;
2356 svn_boolean_t conflicted;
2357 svn_boolean_t conflict_ignored = FALSE;
2358 svn_skel_t *tree_conflict = NULL;
2359 svn_wc__db_status_t status, base_status;
2360 svn_node_kind_t wc_kind;
2362 SVN_ERR(make_dir_baton(&db, path, eb, pb, FALSE, pool));
2366 return SVN_NO_ERROR;
2368 /* Detect obstructing working copies */
2370 svn_boolean_t is_root;
2372 SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, db->local_abspath,
2377 /* Just skip this node; a future update will handle it */
2378 SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
2379 db->skip_this = TRUE;
2380 db->already_notified = TRUE;
2382 do_notification(eb, db->local_abspath, svn_node_dir,
2383 svn_wc_notify_update_skip_obstruction, pool);
2385 return SVN_NO_ERROR;
2389 /* We should have a write lock on every directory touched. */
2390 SVN_ERR(svn_wc__write_check(eb->db, db->local_abspath, pool));
2392 SVN_ERR(svn_wc__db_read_info(&status, &wc_kind, &db->old_revision,
2393 &db->old_repos_relpath, NULL, NULL,
2394 &db->changed_rev, &db->changed_date,
2395 &db->changed_author, &db->ambient_depth,
2396 NULL, NULL, NULL, NULL,
2397 NULL, NULL, NULL, NULL, NULL, NULL,
2398 &conflicted, NULL, NULL, NULL,
2399 NULL, NULL, &have_work,
2400 eb->db, db->local_abspath,
2404 base_status = status;
2406 SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &db->old_revision,
2407 &db->old_repos_relpath, NULL, NULL,
2408 &db->changed_rev, &db->changed_date,
2409 &db->changed_author, &db->ambient_depth,
2410 NULL, NULL, NULL, NULL, NULL, NULL,
2411 eb->db, db->local_abspath,
2414 db->was_incomplete = (base_status == svn_wc__db_status_incomplete);
2416 /* Is this path a conflict victim? */
2418 conflicted = FALSE; /* Conflict applies to WORKING */
2419 else if (conflicted)
2420 SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
2421 eb->db, db->local_abspath, pool));
2424 SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
2426 db->skip_this = TRUE;
2427 db->already_notified = TRUE;
2429 do_notification(eb, db->local_abspath, svn_node_unknown,
2430 svn_wc_notify_skip_conflicted, pool);
2432 return SVN_NO_ERROR;
2434 else if (conflict_ignored)
2436 db->shadowed = TRUE;
2439 /* Is this path a fresh tree conflict victim? If so, skip the tree
2440 with one notification. */
2442 /* Check for conflicts only when we haven't already recorded
2443 * a tree-conflict on a parent node. */
2445 SVN_ERR(check_tree_conflict(&tree_conflict, eb, db->local_abspath,
2446 status, TRUE, svn_node_dir,
2447 svn_wc_conflict_action_edit,
2450 /* Remember the roots of any locally deleted trees. */
2451 if (tree_conflict != NULL)
2453 svn_wc_conflict_reason_t reason;
2454 db->edit_conflict = tree_conflict;
2455 /* Other modifications wouldn't be a tree conflict */
2457 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
2458 eb->db, db->local_abspath,
2460 db->pool, db->pool));
2461 SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_deleted
2462 || reason == svn_wc_conflict_reason_moved_away
2463 || reason == svn_wc_conflict_reason_replaced
2464 || reason == svn_wc_conflict_reason_obstructed);
2466 /* Continue updating BASE */
2467 if (reason == svn_wc_conflict_reason_obstructed)
2468 db->edit_obstructed = TRUE;
2470 db->shadowed = TRUE;
2473 /* Mark directory as being at target_revision and URL, but incomplete. */
2474 SVN_ERR(svn_wc__db_temp_op_start_directory_update(eb->db, db->local_abspath,
2476 *eb->target_revision,
2479 return SVN_NO_ERROR;
2483 /* An svn_delta_editor_t function. */
2484 static svn_error_t *
2485 change_dir_prop(void *dir_baton,
2487 const svn_string_t *value,
2490 svn_prop_t *propchange;
2491 struct dir_baton *db = dir_baton;
2494 return SVN_NO_ERROR;
2496 propchange = apr_array_push(db->propchanges);
2497 propchange->name = apr_pstrdup(db->pool, name);
2498 propchange->value = value ? svn_string_dup(value, db->pool) : NULL;
2500 if (!db->edited && svn_property_kind2(name) == svn_prop_regular_kind)
2501 SVN_ERR(mark_directory_edited(db, pool));
2503 return SVN_NO_ERROR;
2506 /* If any of the svn_prop_t objects in PROPCHANGES represents a change
2507 to the SVN_PROP_EXTERNALS property, return that change, else return
2508 null. If PROPCHANGES contains more than one such change, return
2510 static const svn_prop_t *
2511 externals_prop_changed(const apr_array_header_t *propchanges)
2515 for (i = 0; i < propchanges->nelts; i++)
2517 const svn_prop_t *p = &(APR_ARRAY_IDX(propchanges, i, svn_prop_t));
2518 if (strcmp(p->name, SVN_PROP_EXTERNALS) == 0)
2527 /* An svn_delta_editor_t function. */
2528 static svn_error_t *
2529 close_directory(void *dir_baton,
2532 struct dir_baton *db = dir_baton;
2533 struct edit_baton *eb = db->edit_baton;
2534 svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
2535 apr_array_header_t *entry_prop_changes;
2536 apr_array_header_t *dav_prop_changes;
2537 apr_array_header_t *regular_prop_changes;
2538 apr_hash_t *base_props;
2539 apr_hash_t *actual_props;
2540 apr_hash_t *new_base_props = NULL;
2541 apr_hash_t *new_actual_props = NULL;
2542 svn_revnum_t new_changed_rev = SVN_INVALID_REVNUM;
2543 apr_time_t new_changed_date = 0;
2544 const char *new_changed_author = NULL;
2545 apr_pool_t *scratch_pool = db->pool;
2546 svn_skel_t *all_work_items = NULL;
2547 svn_skel_t *conflict_skel = NULL;
2549 /* Skip if we're in a conflicted tree. */
2552 /* Allow the parent to complete its update. */
2553 SVN_ERR(maybe_release_dir_info(db));
2555 return SVN_NO_ERROR;
2559 conflict_skel = db->edit_conflict;
2561 SVN_ERR(svn_categorize_props(db->propchanges, &entry_prop_changes,
2562 &dav_prop_changes, ®ular_prop_changes, pool));
2564 /* Fetch the existing properties. */
2565 if ((!db->adding_dir || db->add_existed)
2568 SVN_ERR(svn_wc__get_actual_props(&actual_props,
2569 eb->db, db->local_abspath,
2570 scratch_pool, scratch_pool));
2573 actual_props = apr_hash_make(pool);
2575 if (db->add_existed)
2577 /* This node already exists. Grab the current pristine properties. */
2578 SVN_ERR(svn_wc__db_read_pristine_props(&base_props,
2579 eb->db, db->local_abspath,
2580 scratch_pool, scratch_pool));
2582 else if (!db->adding_dir)
2584 /* Get the BASE properties for proper merging. */
2585 SVN_ERR(svn_wc__db_base_get_props(&base_props,
2586 eb->db, db->local_abspath,
2587 scratch_pool, scratch_pool));
2590 base_props = apr_hash_make(pool);
2592 /* An incomplete directory might have props which were supposed to be
2593 deleted but weren't. Because the server sent us all the props we're
2594 supposed to have, any previous base props not in this list must be
2595 deleted (issue #1672). */
2596 if (db->was_incomplete)
2599 apr_hash_t *props_to_delete;
2600 apr_hash_index_t *hi;
2602 /* In a copy of the BASE props, remove every property that we see an
2603 incoming change for. The remaining unmentioned properties are those
2604 which need to be deleted. */
2605 props_to_delete = apr_hash_copy(pool, base_props);
2606 for (i = 0; i < regular_prop_changes->nelts; i++)
2608 const svn_prop_t *prop;
2609 prop = &APR_ARRAY_IDX(regular_prop_changes, i, svn_prop_t);
2610 svn_hash_sets(props_to_delete, prop->name, NULL);
2613 /* Add these props to the incoming propchanges (in
2614 * regular_prop_changes). */
2615 for (hi = apr_hash_first(pool, props_to_delete);
2617 hi = apr_hash_next(hi))
2619 const char *propname = svn__apr_hash_index_key(hi);
2620 svn_prop_t *prop = apr_array_push(regular_prop_changes);
2622 /* Record a deletion for PROPNAME. */
2623 prop->name = propname;
2628 /* If this directory has property changes stored up, now is the time
2629 to deal with them. */
2630 if (regular_prop_changes->nelts)
2632 /* If recording traversal info, then see if the
2633 SVN_PROP_EXTERNALS property on this directory changed,
2634 and record before and after for the change. */
2635 if (eb->external_func)
2637 const svn_prop_t *change
2638 = externals_prop_changed(regular_prop_changes);
2642 const svn_string_t *new_val_s = change->value;
2643 const svn_string_t *old_val_s;
2645 old_val_s = svn_hash_gets(base_props, SVN_PROP_EXTERNALS);
2647 if ((new_val_s == NULL) && (old_val_s == NULL))
2648 ; /* No value before, no value after... so do nothing. */
2649 else if (new_val_s && old_val_s
2650 && (svn_string_compare(old_val_s, new_val_s)))
2651 ; /* Value did not change... so do nothing. */
2652 else if (old_val_s || new_val_s)
2653 /* something changed, record the change */
2655 SVN_ERR((eb->external_func)(
2668 /* We don't have a relevant actual row, but we need actual properties
2669 to allow property merging without conflicts. */
2671 actual_props = apr_hash_make(scratch_pool);
2673 actual_props = base_props;
2676 /* Merge pending properties. */
2677 new_base_props = svn_prop__patch(base_props, regular_prop_changes,
2679 SVN_ERR_W(svn_wc__merge_props(&conflict_skel,
2684 NULL /* use baseprops */,
2687 regular_prop_changes,
2690 _("Couldn't do property merge"));
2691 /* After a (not-dry-run) merge, we ALWAYS have props to save. */
2692 SVN_ERR_ASSERT(new_base_props != NULL && new_actual_props != NULL);
2695 SVN_ERR(accumulate_last_change(&new_changed_rev, &new_changed_date,
2696 &new_changed_author, entry_prop_changes,
2697 scratch_pool, scratch_pool));
2699 /* Check if we should add some not-present markers before marking the
2700 directory complete (Issue #3569) */
2702 apr_hash_t *new_children = svn_hash_gets(eb->dir_dirents, db->new_relpath);
2704 if (new_children != NULL)
2706 apr_hash_index_t *hi;
2707 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2709 for (hi = apr_hash_first(scratch_pool, new_children);
2711 hi = apr_hash_next(hi))
2713 const char *child_name;
2714 const char *child_abspath;
2715 const char *child_relpath;
2716 const svn_dirent_t *dirent;
2717 svn_wc__db_status_t status;
2718 svn_node_kind_t child_kind;
2721 svn_pool_clear(iterpool);
2723 child_name = svn__apr_hash_index_key(hi);
2724 child_abspath = svn_dirent_join(db->local_abspath, child_name,
2727 dirent = svn__apr_hash_index_val(hi);
2728 child_kind = (dirent->kind == svn_node_dir)
2732 if (db->ambient_depth < svn_depth_immediates
2733 && child_kind == svn_node_dir)
2734 continue; /* We don't need the subdirs */
2736 /* ### We just check if there is some node in BASE at this path */
2737 err = svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL,
2738 NULL, NULL, NULL, NULL, NULL, NULL,
2739 NULL, NULL, NULL, NULL, NULL,
2740 eb->db, child_abspath,
2741 iterpool, iterpool);
2745 svn_boolean_t is_wcroot;
2746 SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, eb->db, child_abspath,
2750 continue; /* Everything ok... Nothing to do here */
2751 /* Fall through to allow recovering later */
2753 else if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
2754 return svn_error_trace(err);
2756 svn_error_clear(err);
2758 child_relpath = svn_relpath_join(db->new_relpath, child_name,
2761 SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db,
2766 *eb->target_revision,
2772 svn_pool_destroy(iterpool);
2776 if (apr_hash_count(db->not_present_files))
2778 apr_hash_index_t *hi;
2779 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2781 /* This should call some new function (which could also be used
2782 for new_children above) to add all the names in single
2783 transaction, but I can't even trigger it. I've tried
2784 ra_local, ra_svn, ra_neon, ra_serf and they all call
2785 close_file before close_dir. */
2786 for (hi = apr_hash_first(scratch_pool, db->not_present_files);
2788 hi = apr_hash_next(hi))
2790 const char *child = svn__apr_hash_index_key(hi);
2791 const char *child_abspath, *child_relpath;
2793 svn_pool_clear(iterpool);
2795 child_abspath = svn_dirent_join(db->local_abspath, child, iterpool);
2796 child_relpath = svn_dirent_join(db->new_relpath, child, iterpool);
2798 SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db,
2803 *eb->target_revision,
2808 svn_pool_destroy(iterpool);
2811 /* If this directory is merely an anchor for a targeted child, then we
2812 should not be updating the node at all. */
2813 if (db->parent_baton == NULL
2814 && *eb->target_basename != '\0')
2816 /* And we should not have received any changes! */
2817 SVN_ERR_ASSERT(db->propchanges->nelts == 0);
2818 /* ... which also implies NEW_CHANGED_* are not set,
2819 and NEW_BASE_PROPS == NULL. */
2824 apr_array_header_t *iprops = NULL;
2826 /* ### we know a base node already exists. it was created in
2827 ### open_directory or add_directory. let's just preserve the
2828 ### existing DEPTH value, and possibly CHANGED_*. */
2829 /* If we received any changed_* values, then use them. */
2830 if (SVN_IS_VALID_REVNUM(new_changed_rev))
2831 db->changed_rev = new_changed_rev;
2832 if (new_changed_date != 0)
2833 db->changed_date = new_changed_date;
2834 if (new_changed_author != NULL)
2835 db->changed_author = new_changed_author;
2837 /* If no depth is set yet, set to infinity. */
2838 if (db->ambient_depth == svn_depth_unknown)
2839 db->ambient_depth = svn_depth_infinity;
2841 if (eb->depth_is_sticky
2842 && db->ambient_depth != eb->requested_depth)
2844 /* After a depth upgrade the entry must reflect the new depth.
2845 Upgrading to infinity changes the depth of *all* directories,
2846 upgrading to something else only changes the target. */
2848 if (eb->requested_depth == svn_depth_infinity
2849 || (strcmp(db->local_abspath, eb->target_abspath) == 0
2850 && eb->requested_depth > db->ambient_depth))
2852 db->ambient_depth = eb->requested_depth;
2856 /* Do we have new properties to install? Or shall we simply retain
2857 the prior set of properties? If we're installing new properties,
2858 then we also want to write them to an old-style props file. */
2859 props = new_base_props;
2865 svn_skel_t *work_item;
2867 SVN_ERR(complete_conflict(conflict_skel,
2870 db->old_repos_relpath,
2873 svn_node_dir, svn_node_dir,
2874 db->pool, scratch_pool));
2876 SVN_ERR(svn_wc__conflict_create_markers(&work_item,
2877 eb->db, db->local_abspath,
2879 scratch_pool, scratch_pool));
2881 all_work_items = svn_wc__wq_merge(all_work_items, work_item,
2885 /* Any inherited props to be set set for this base node? */
2886 if (eb->wcroot_iprops)
2888 iprops = svn_hash_gets(eb->wcroot_iprops, db->local_abspath);
2890 /* close_edit may also update iprops for switched nodes, catching
2891 those for which close_directory is never called (e.g. a switch
2892 with no changes). So as a minor optimization we remove any
2893 iprops from the hash so as not to set them again in
2896 svn_hash_sets(eb->wcroot_iprops, db->local_abspath, NULL);
2899 /* Update the BASE data for the directory and mark the directory
2901 SVN_ERR(svn_wc__db_base_add_directory(
2902 eb->db, db->local_abspath,
2905 eb->repos_root, eb->repos_uuid,
2906 *eb->target_revision,
2908 db->changed_rev, db->changed_date, db->changed_author,
2909 NULL /* children */,
2911 (dav_prop_changes->nelts > 0)
2912 ? svn_prop_array_to_hash(dav_prop_changes, pool)
2915 (! db->shadowed) && new_base_props != NULL,
2917 iprops, all_work_items,
2921 /* Process all of the queued work items for this directory. */
2922 SVN_ERR(svn_wc__wq_run(eb->db, db->local_abspath,
2923 eb->cancel_func, eb->cancel_baton,
2926 if (conflict_skel && eb->conflict_func)
2927 SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, db->local_abspath,
2929 NULL /* merge_options */,
2936 /* Notify of any prop changes on this directory -- but do nothing if
2937 it's an added or skipped directory, because notification has already
2938 happened in that case - unless the add was obstructed by a dir
2939 scheduled for addition without history, in which case we handle
2940 notification here). */
2941 if (!db->already_notified && eb->notify_func && db->edited)
2943 svn_wc_notify_t *notify;
2944 svn_wc_notify_action_t action;
2946 if (db->shadowed || db->edit_obstructed)
2947 action = svn_wc_notify_update_shadowed_update;
2948 else if (db->obstruction_found || db->add_existed)
2949 action = svn_wc_notify_exists;
2951 action = svn_wc_notify_update_update;
2953 notify = svn_wc_create_notify(db->local_abspath, action, pool);
2954 notify->kind = svn_node_dir;
2955 notify->prop_state = prop_state;
2956 notify->revision = *eb->target_revision;
2957 notify->old_revision = db->old_revision;
2959 eb->notify_func(eb->notify_baton, notify, scratch_pool);
2962 /* We're done with this directory, so remove one reference from the
2963 bump information. */
2964 SVN_ERR(maybe_release_dir_info(db));
2966 return SVN_NO_ERROR;
2970 /* Common code for 'absent_file' and 'absent_directory'. */
2971 static svn_error_t *
2972 absent_node(const char *path,
2973 svn_node_kind_t absent_kind,
2977 struct dir_baton *pb = parent_baton;
2978 struct edit_baton *eb = pb->edit_baton;
2979 apr_pool_t *scratch_pool = svn_pool_create(pool);
2980 const char *name = svn_dirent_basename(path, NULL);
2981 const char *local_abspath;
2983 svn_wc__db_status_t status;
2984 svn_node_kind_t kind;
2987 return SVN_NO_ERROR;
2989 SVN_ERR(mark_directory_edited(pb, scratch_pool));
2991 local_abspath = svn_dirent_join(pb->local_abspath, name, scratch_pool);
2993 /* If an item by this name is scheduled for addition that's a
2994 genuine tree-conflict. */
2995 err = svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
2996 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
2997 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
2998 NULL, NULL, NULL, NULL,
2999 eb->db, local_abspath,
3000 scratch_pool, scratch_pool);
3004 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
3005 return svn_error_trace(err);
3007 svn_error_clear(err);
3008 status = svn_wc__db_status_not_present;
3009 kind = svn_node_unknown;
3012 if (status == svn_wc__db_status_normal
3013 && kind == svn_node_dir)
3015 /* We found an obstructing working copy!
3017 We can do two things now:
3018 1) notify the user, record a skip, etc.
3019 2) Just record the absent node in BASE in the parent
3022 As option 2 happens to be exactly what we do anyway, lets do that.
3025 else if (status == svn_wc__db_status_not_present
3026 || status == svn_wc__db_status_server_excluded
3027 || status == svn_wc__db_status_excluded)
3029 /* The BASE node is not actually there, so we can safely turn it into
3034 /* We have a local addition. If this would be a BASE node it would have
3035 been deleted before we get here. (Which might have turned it into
3038 ### This should be recorded as a tree conflict and the update
3039 ### can just continue, as we can just record the absent status
3042 SVN_ERR_ASSERT(status != svn_wc__db_status_normal);
3044 return svn_error_createf(
3045 SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
3046 _("Failed to mark '%s' absent: item of the same name is already "
3047 "scheduled for addition"),
3048 svn_dirent_local_style(local_abspath, pool));
3052 const char *repos_relpath;
3053 repos_relpath = svn_relpath_join(pb->new_relpath, name, scratch_pool);
3055 /* Insert an excluded node below the parent node to note that this child
3056 is absent. (This puts it in the parent db if the child is obstructed) */
3057 SVN_ERR(svn_wc__db_base_add_excluded_node(eb->db, local_abspath,
3058 repos_relpath, eb->repos_root,
3060 *(eb->target_revision),
3062 svn_wc__db_status_server_excluded,
3067 svn_pool_destroy(scratch_pool);
3069 return SVN_NO_ERROR;
3073 /* An svn_delta_editor_t function. */
3074 static svn_error_t *
3075 absent_file(const char *path,
3079 return absent_node(path, svn_node_file, parent_baton, pool);
3083 /* An svn_delta_editor_t function. */
3084 static svn_error_t *
3085 absent_directory(const char *path,
3089 return absent_node(path, svn_node_dir, parent_baton, pool);
3093 /* An svn_delta_editor_t function. */
3094 static svn_error_t *
3095 add_file(const char *path,
3097 const char *copyfrom_path,
3098 svn_revnum_t copyfrom_rev,
3102 struct dir_baton *pb = parent_baton;
3103 struct edit_baton *eb = pb->edit_baton;
3104 struct file_baton *fb;
3105 svn_node_kind_t kind = svn_node_none;
3106 svn_node_kind_t wc_kind = svn_node_unknown;
3107 svn_wc__db_status_t status = svn_wc__db_status_normal;
3108 apr_pool_t *scratch_pool;
3109 svn_boolean_t conflicted = FALSE;
3110 svn_boolean_t conflict_ignored = FALSE;
3111 svn_boolean_t versioned_locally_and_present = FALSE;
3112 svn_skel_t *tree_conflict = NULL;
3113 svn_error_t *err = SVN_NO_ERROR;
3115 SVN_ERR_ASSERT(! (copyfrom_path || SVN_IS_VALID_REVNUM(copyfrom_rev)));
3117 SVN_ERR(make_file_baton(&fb, pb, path, TRUE, pool));
3121 return SVN_NO_ERROR;
3123 SVN_ERR(mark_file_edited(fb, pool));
3125 /* The file_pool can stick around for a *long* time, so we want to
3126 use a subpool for any temporary allocations. */
3127 scratch_pool = svn_pool_create(pool);
3130 /* It may not be named the same as the administrative directory. */
3131 if (svn_wc_is_adm_dir(fb->name, pool))
3132 return svn_error_createf(
3133 SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
3134 _("Failed to add file '%s': object of the same name as the "
3135 "administrative directory"),
3136 svn_dirent_local_style(fb->local_abspath, pool));
3138 if (!eb->clean_checkout)
3140 SVN_ERR(svn_io_check_path(fb->local_abspath, &kind, scratch_pool));
3142 err = svn_wc__db_read_info(&status, &wc_kind, NULL, NULL, NULL, NULL, NULL,
3143 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3144 NULL, NULL, NULL, NULL, NULL,
3145 &conflicted, NULL, NULL, NULL, NULL, NULL, NULL,
3146 eb->db, fb->local_abspath,
3147 scratch_pool, scratch_pool);
3152 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
3153 return svn_error_trace(err);
3155 svn_error_clear(err);
3156 wc_kind = svn_node_unknown;
3159 versioned_locally_and_present = FALSE;
3161 else if (wc_kind == svn_node_dir
3162 && status == svn_wc__db_status_normal)
3164 /* !! We found the root of a separate working copy obstructing the wc !!
3166 If the directory would be part of our own working copy then
3167 we wouldn't have been called as an add_file().
3169 The only thing we can do is add a not-present node, to allow
3170 a future update to bring in the new files when the problem is
3172 svn_hash_sets(pb->not_present_files, apr_pstrdup(pb->pool, fb->name),
3175 SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3176 fb->skip_this = TRUE;
3177 fb->already_notified = TRUE;
3179 do_notification(eb, fb->local_abspath, svn_node_file,
3180 svn_wc_notify_update_skip_obstruction, scratch_pool);
3182 svn_pool_destroy(scratch_pool);
3184 return SVN_NO_ERROR;
3186 else if (status == svn_wc__db_status_normal
3187 && (wc_kind == svn_node_file
3188 || wc_kind == svn_node_symlink))
3190 /* We found a file external occupating the place we need in BASE.
3192 We can't add a not-present node in this case as that would overwrite
3193 the file external. Luckily the file external itself stops us from
3194 forgetting a child of this parent directory like an obstructing
3197 The reason we get here is that the adm crawler doesn't report
3200 SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3201 fb->skip_this = TRUE;
3202 fb->already_notified = TRUE;
3204 do_notification(eb, fb->local_abspath, svn_node_file,
3205 svn_wc_notify_update_skip_obstruction, scratch_pool);
3207 svn_pool_destroy(scratch_pool);
3209 return SVN_NO_ERROR;
3211 else if (wc_kind == svn_node_unknown)
3212 versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */
3214 versioned_locally_and_present = IS_NODE_PRESENT(status);
3217 /* Is this path a conflict victim? */
3219 conflicted = FALSE; /* Conflict applies to WORKING */
3220 else if (conflicted)
3222 if (pb->deletion_conflicts)
3223 tree_conflict = svn_hash_gets(pb->deletion_conflicts, fb->name);
3227 svn_wc_conflict_reason_t reason;
3228 /* So this deletion wasn't just a deletion, it is actually a
3229 replacement. Let's install a better tree conflict. */
3231 /* ### Should store the conflict in DB to allow reinstalling
3232 ### with theoretically more data in close_directory() */
3234 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
3238 fb->pool, fb->pool));
3240 tree_conflict = svn_wc__conflict_skel_create(fb->pool);
3242 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
3244 eb->db, fb->local_abspath,
3245 reason, svn_wc_conflict_action_replace,
3247 fb->pool, fb->pool));
3249 /* And now stop checking for conflicts here and just perform
3250 a shadowed update */
3251 fb->edit_conflict = tree_conflict; /* Cache for close_file */
3252 tree_conflict = NULL; /* No direct notification */
3253 fb->shadowed = TRUE; /* Just continue */
3254 conflicted = FALSE; /* No skip */
3257 SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
3258 eb->db, fb->local_abspath, pool));
3261 /* Now the usual conflict handling: skip. */
3264 SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3266 fb->skip_this = TRUE;
3267 fb->already_notified = TRUE;
3269 /* We skip this node, but once the update completes the parent node will
3270 be updated to the new revision. So a future recursive update of the
3271 parent will not bring in this new node as the revision of the parent
3272 describes to the repository that all children are available.
3274 To resolve this problem, we add a not-present node to allow bringing
3275 the node in once this conflict is resolved.
3277 Note that we can safely assume that no present base node exists,
3278 because then we would not have received an add_file.
3280 svn_hash_sets(pb->not_present_files, apr_pstrdup(pb->pool, fb->name),
3283 do_notification(eb, fb->local_abspath, svn_node_unknown,
3284 svn_wc_notify_skip_conflicted, scratch_pool);
3286 svn_pool_destroy(scratch_pool);
3288 return SVN_NO_ERROR;
3290 else if (conflict_ignored)
3292 fb->shadowed = TRUE;
3297 /* Nothing to check; does not and will not exist in working copy */
3299 else if (versioned_locally_and_present)
3301 /* What to do with a versioned or schedule-add file:
3303 If the UUID doesn't match the parent's, or the URL isn't a child of
3304 the parent dir's URL, it's an error.
3306 Set add_existed so that user notification is delayed until after any
3307 text or prop conflicts have been found.
3309 Whether the incoming add is a symlink or a file will only be known in
3310 close_file(), when the props are known. So with a locally added file
3311 or symlink, let close_file() check for a tree conflict.
3313 We will never see missing files here, because these would be
3314 re-added during the crawler phase. */
3315 svn_boolean_t local_is_file;
3317 /* Is the local node a copy or move */
3318 if (status == svn_wc__db_status_added)
3319 SVN_ERR(svn_wc__db_scan_addition(&status, NULL, NULL, NULL, NULL, NULL,
3321 eb->db, fb->local_abspath,
3322 scratch_pool, scratch_pool));
3324 /* Is there something that is a file? */
3325 local_is_file = (wc_kind == svn_node_file
3326 || wc_kind == svn_node_symlink);
3328 /* Do tree conflict checking if
3329 * - if there is a local copy.
3330 * - if this is a switch operation
3331 * - the node kinds mismatch
3333 * During switch, local adds at the same path as incoming adds get
3334 * "lost" in that switching back to the original will no longer have the
3335 * local add. So switch always alerts the user with a tree conflict. */
3336 if (!eb->adds_as_modification
3338 || status != svn_wc__db_status_added)
3340 SVN_ERR(check_tree_conflict(&tree_conflict, eb,
3342 status, FALSE, svn_node_none,
3343 svn_wc_conflict_action_add,
3344 scratch_pool, scratch_pool));
3347 if (tree_conflict == NULL)
3348 fb->add_existed = TRUE; /* Take over WORKING */
3350 fb->shadowed = TRUE; /* Only update BASE */
3353 else if (kind != svn_node_none)
3355 /* There's an unversioned node at this path. */
3356 fb->obstruction_found = TRUE;
3358 /* Unversioned, obstructing files are handled by text merge/conflict,
3359 * if unversioned obstructions are allowed. */
3360 if (! (kind == svn_node_file && eb->allow_unver_obstructions))
3362 /* Bring in the node as deleted */ /* ### Obstructed Conflict */
3363 fb->shadowed = TRUE;
3365 /* Mark a conflict */
3366 tree_conflict = svn_wc__conflict_skel_create(fb->pool);
3368 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
3370 eb->db, fb->local_abspath,
3371 svn_wc_conflict_reason_unversioned,
3372 svn_wc_conflict_action_add,
3374 fb->pool, scratch_pool));
3378 /* When this is not the update target add a not-present BASE node now,
3379 to allow marking the parent directory complete in its close_edit() call.
3380 This resolves issues when that occurs before the close_file(). */
3381 if (pb->parent_baton
3382 || *eb->target_basename == '\0'
3383 || (strcmp(fb->local_abspath, eb->target_abspath) != 0))
3385 svn_hash_sets(pb->not_present_files, apr_pstrdup(pb->pool, fb->name),
3389 if (tree_conflict != NULL)
3391 SVN_ERR(complete_conflict(tree_conflict,
3394 fb->old_repos_relpath,
3399 fb->pool, scratch_pool));
3401 SVN_ERR(svn_wc__db_op_mark_conflict(eb->db,
3403 tree_conflict, NULL,
3406 if (eb->conflict_func)
3407 SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, fb->local_abspath,
3409 NULL /* merge_options */,
3416 fb->already_notified = TRUE;
3417 do_notification(eb, fb->local_abspath, svn_node_file,
3418 svn_wc_notify_tree_conflict, scratch_pool);
3421 svn_pool_destroy(scratch_pool);
3423 return SVN_NO_ERROR;
3427 /* An svn_delta_editor_t function. */
3428 static svn_error_t *
3429 open_file(const char *path,
3431 svn_revnum_t base_revision,
3435 struct dir_baton *pb = parent_baton;
3436 struct edit_baton *eb = pb->edit_baton;
3437 struct file_baton *fb;
3438 svn_boolean_t conflicted;
3439 svn_boolean_t conflict_ignored = FALSE;
3440 svn_boolean_t have_work;
3441 svn_wc__db_status_t status;
3442 svn_node_kind_t wc_kind;
3443 svn_skel_t *tree_conflict = NULL;
3445 /* the file_pool can stick around for a *long* time, so we want to use
3446 a subpool for any temporary allocations. */
3447 apr_pool_t *scratch_pool = svn_pool_create(pool);
3449 SVN_ERR(make_file_baton(&fb, pb, path, FALSE, pool));
3453 return SVN_NO_ERROR;
3455 /* Detect obstructing working copies */
3457 svn_boolean_t is_root;
3459 SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, fb->local_abspath,
3464 /* Just skip this node; a future update will handle it */
3465 SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3466 fb->skip_this = TRUE;
3467 fb->already_notified = TRUE;
3469 do_notification(eb, fb->local_abspath, svn_node_file,
3470 svn_wc_notify_update_skip_obstruction, pool);
3472 return SVN_NO_ERROR;
3478 /* If replacing, make sure the .svn entry already exists. */
3479 SVN_ERR(svn_wc__db_read_info(&status, &wc_kind, &fb->old_revision,
3480 &fb->old_repos_relpath, NULL, NULL,
3481 &fb->changed_rev, &fb->changed_date,
3482 &fb->changed_author, NULL,
3483 &fb->original_checksum, NULL, NULL, NULL,
3484 NULL, NULL, NULL, NULL, NULL, NULL,
3485 &conflicted, NULL, NULL, &fb->local_prop_mods,
3486 NULL, NULL, &have_work,
3487 eb->db, fb->local_abspath,
3488 fb->pool, scratch_pool));
3491 SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &fb->old_revision,
3492 &fb->old_repos_relpath, NULL, NULL,
3493 &fb->changed_rev, &fb->changed_date,
3494 &fb->changed_author, NULL,
3495 &fb->original_checksum, NULL, NULL,
3497 eb->db, fb->local_abspath,
3498 fb->pool, scratch_pool));
3500 /* Is this path a conflict victim? */
3502 conflicted = FALSE; /* Conflict applies to WORKING */
3503 else if (conflicted)
3504 SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
3505 eb->db, fb->local_abspath, pool));
3508 SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3510 fb->skip_this = TRUE;
3511 fb->already_notified = TRUE;
3513 do_notification(eb, fb->local_abspath, svn_node_unknown,
3514 svn_wc_notify_skip_conflicted, scratch_pool);
3516 svn_pool_destroy(scratch_pool);
3518 return SVN_NO_ERROR;
3520 else if (conflict_ignored)
3522 fb->shadowed = TRUE;
3525 /* Check for conflicts only when we haven't already recorded
3526 * a tree-conflict on a parent node. */
3528 SVN_ERR(check_tree_conflict(&tree_conflict, eb, fb->local_abspath,
3529 status, TRUE, svn_node_file,
3530 svn_wc_conflict_action_edit,
3531 fb->pool, scratch_pool));
3533 /* Is this path the victim of a newly-discovered tree conflict? */
3534 if (tree_conflict != NULL)
3536 svn_wc_conflict_reason_t reason;
3537 fb->edit_conflict = tree_conflict;
3538 /* Other modifications wouldn't be a tree conflict */
3540 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
3541 eb->db, fb->local_abspath,
3543 scratch_pool, scratch_pool));
3544 SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_deleted
3545 || reason == svn_wc_conflict_reason_moved_away
3546 || reason == svn_wc_conflict_reason_replaced
3547 || reason == svn_wc_conflict_reason_obstructed);
3549 /* Continue updating BASE */
3550 if (reason == svn_wc_conflict_reason_obstructed)
3551 fb->edit_obstructed = TRUE;
3553 fb->shadowed = TRUE;
3556 svn_pool_destroy(scratch_pool);
3558 return SVN_NO_ERROR;
3561 /* Implements svn_stream_lazyopen_func_t. */
3562 static svn_error_t *
3563 lazy_open_source(svn_stream_t **stream,
3565 apr_pool_t *result_pool,
3566 apr_pool_t *scratch_pool)
3568 struct file_baton *fb = baton;
3570 SVN_ERR(svn_wc__db_pristine_read(stream, NULL, fb->edit_baton->db,
3572 fb->original_checksum,
3573 result_pool, scratch_pool));
3576 return SVN_NO_ERROR;
3579 struct lazy_target_baton {
3580 struct file_baton *fb;
3581 struct handler_baton *hb;
3582 struct edit_baton *eb;
3585 /* Implements svn_stream_lazyopen_func_t. */
3586 static svn_error_t *
3587 lazy_open_target(svn_stream_t **stream,
3589 apr_pool_t *result_pool,
3590 apr_pool_t *scratch_pool)
3592 struct lazy_target_baton *tb = baton;
3594 SVN_ERR(svn_wc__open_writable_base(stream, &tb->hb->new_text_base_tmp_abspath,
3595 NULL, &tb->hb->new_text_base_sha1_checksum,
3596 tb->fb->edit_baton->db,
3597 tb->eb->wcroot_abspath,
3598 result_pool, scratch_pool));
3600 return SVN_NO_ERROR;
3603 /* An svn_delta_editor_t function. */
3604 static svn_error_t *
3605 apply_textdelta(void *file_baton,
3606 const char *expected_checksum,
3608 svn_txdelta_window_handler_t *handler,
3609 void **handler_baton)
3611 struct file_baton *fb = file_baton;
3612 apr_pool_t *handler_pool = svn_pool_create(fb->pool);
3613 struct handler_baton *hb = apr_pcalloc(handler_pool, sizeof(*hb));
3614 struct edit_baton *eb = fb->edit_baton;
3615 const svn_checksum_t *recorded_base_checksum;
3616 svn_checksum_t *expected_base_checksum;
3617 svn_stream_t *source;
3618 struct lazy_target_baton *tb;
3619 svn_stream_t *target;
3623 *handler = svn_delta_noop_window_handler;
3624 *handler_baton = NULL;
3625 return SVN_NO_ERROR;
3628 SVN_ERR(mark_file_edited(fb, pool));
3630 /* Parse checksum or sets expected_base_checksum to NULL */
3631 SVN_ERR(svn_checksum_parse_hex(&expected_base_checksum, svn_checksum_md5,
3632 expected_checksum, pool));
3634 /* Before applying incoming svndiff data to text base, make sure
3635 text base hasn't been corrupted, and that its checksum
3636 matches the expected base checksum. */
3638 /* The incoming delta is targeted against EXPECTED_BASE_CHECKSUM. Find and
3639 check our RECORDED_BASE_CHECKSUM. (In WC-1, we could not do this test
3640 for replaced nodes because we didn't store the checksum of the "revert
3641 base". In WC-NG, we do and we can.) */
3642 recorded_base_checksum = fb->original_checksum;
3644 /* If we have a checksum that we want to compare to a MD5 checksum,
3645 ensure that it is a MD5 checksum */
3646 if (recorded_base_checksum
3647 && expected_base_checksum
3648 && recorded_base_checksum->kind != svn_checksum_md5)
3649 SVN_ERR(svn_wc__db_pristine_get_md5(&recorded_base_checksum,
3650 eb->db, eb->wcroot_abspath,
3651 recorded_base_checksum, pool, pool));
3654 if (!svn_checksum_match(expected_base_checksum, recorded_base_checksum))
3655 return svn_error_createf(SVN_ERR_WC_CORRUPT_TEXT_BASE, NULL,
3656 _("Checksum mismatch for '%s':\n"
3659 svn_dirent_local_style(fb->local_abspath, pool),
3660 svn_checksum_to_cstring_display(expected_base_checksum,
3662 svn_checksum_to_cstring_display(recorded_base_checksum,
3665 /* Open the text base for reading, unless this is an added file. */
3668 kff todo: what we really need to do here is:
3670 1. See if there's a file or dir by this name already here.
3671 2. See if it's under revision control.
3672 3. If both are true, open text-base.
3673 4. If only 1 is true, bail, because we can't go destroying user's
3674 files (or as an alternative to bailing, move it to some tmp
3675 name and somehow tell the user, but communicating with the
3676 user without erroring is a whole callback system we haven't
3677 finished inventing yet.)
3680 if (! fb->adding_file)
3682 SVN_ERR_ASSERT(!fb->original_checksum
3683 || fb->original_checksum->kind == svn_checksum_sha1);
3685 source = svn_stream_lazyopen_create(lazy_open_source, fb, FALSE,
3690 source = svn_stream_empty(handler_pool);
3693 /* If we don't have a recorded checksum, use the ra provided checksum */
3694 if (!recorded_base_checksum)
3695 recorded_base_checksum = expected_base_checksum;
3697 /* Checksum the text base while applying deltas */
3698 if (recorded_base_checksum)
3700 hb->expected_source_checksum = svn_checksum_dup(recorded_base_checksum,
3703 /* Wrap stream and store reference to allow calculating the
3705 source = svn_stream_checksummed2(source,
3706 &hb->actual_source_checksum,
3707 NULL, recorded_base_checksum->kind,
3708 TRUE, handler_pool);
3709 hb->source_checksum_stream = source;
3712 tb = apr_palloc(handler_pool, sizeof(struct lazy_target_baton));
3716 target = svn_stream_lazyopen_create(lazy_open_target, tb, TRUE, handler_pool);
3718 /* Prepare to apply the delta. */
3719 svn_txdelta_apply(source, target,
3720 hb->new_text_base_md5_digest,
3721 hb->new_text_base_tmp_abspath /* error_info */,
3723 &hb->apply_handler, &hb->apply_baton);
3725 hb->pool = handler_pool;
3728 /* We're all set. */
3729 *handler_baton = hb;
3730 *handler = window_handler;
3732 return SVN_NO_ERROR;
3736 /* An svn_delta_editor_t function. */
3737 static svn_error_t *
3738 change_file_prop(void *file_baton,
3740 const svn_string_t *value,
3741 apr_pool_t *scratch_pool)
3743 struct file_baton *fb = file_baton;
3744 svn_prop_t *propchange;
3747 return SVN_NO_ERROR;
3749 /* Push a new propchange to the file baton's array of propchanges */
3750 propchange = apr_array_push(fb->propchanges);
3751 propchange->name = apr_pstrdup(fb->pool, name);
3752 propchange->value = value ? svn_string_dup(value, fb->pool) : NULL;
3754 if (!fb->edited && svn_property_kind2(name) == svn_prop_regular_kind)
3755 SVN_ERR(mark_file_edited(fb, scratch_pool));
3758 && strcmp(name, SVN_PROP_SPECIAL) == 0)
3760 struct edit_baton *eb = fb->edit_baton;
3761 svn_boolean_t modified = FALSE;
3762 svn_boolean_t becomes_symlink;
3763 svn_boolean_t was_symlink;
3765 /* Let's see if we have a change as in some scenarios servers report
3766 non-changes of properties. */
3767 becomes_symlink = (value != NULL);
3769 if (fb->adding_file)
3770 was_symlink = becomes_symlink; /* No change */
3775 /* We read the server-props, not the ACTUAL props here as we just
3776 want to see if this is really an incoming prop change. */
3777 SVN_ERR(svn_wc__db_base_get_props(&props, eb->db,
3779 scratch_pool, scratch_pool));
3781 was_symlink = ((props
3782 && svn_hash_gets(props, SVN_PROP_SPECIAL) != NULL)
3784 : svn_tristate_false);
3787 if (was_symlink != becomes_symlink)
3789 /* If the local node was not modified, we continue as usual, if
3790 modified we want a tree conflict just like how we would handle
3791 it when receiving a delete + add (aka "replace") */
3792 if (fb->local_prop_mods)
3795 SVN_ERR(svn_wc__internal_file_modified_p(&modified, eb->db,
3797 FALSE, scratch_pool));
3802 if (!fb->edit_conflict)
3803 fb->edit_conflict = svn_wc__conflict_skel_create(fb->pool);
3805 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
3807 eb->db, fb->local_abspath,
3808 svn_wc_conflict_reason_edited,
3809 svn_wc_conflict_action_replace,
3811 fb->pool, scratch_pool));
3813 SVN_ERR(complete_conflict(fb->edit_conflict, fb->edit_baton,
3814 fb->local_abspath, fb->old_repos_relpath,
3815 fb->old_revision, fb->new_relpath,
3816 svn_node_file, svn_node_file,
3817 fb->pool, scratch_pool));
3819 /* Create a copy of the existing (pre update) BASE node in WORKING,
3820 mark a tree conflict and handle the rest of the update as
3822 SVN_ERR(svn_wc__db_op_make_copy(eb->db, fb->local_abspath,
3823 fb->edit_conflict, NULL,
3826 do_notification(eb, fb->local_abspath, svn_node_file,
3827 svn_wc_notify_tree_conflict, scratch_pool);
3829 /* Ok, we introduced a replacement, so we can now handle the rest
3830 as a normal shadowed update */
3831 fb->shadowed = TRUE;
3832 fb->add_existed = FALSE;
3833 fb->already_notified = TRUE;
3837 return SVN_NO_ERROR;
3840 /* Perform the actual merge of file changes between an original file,
3841 identified by ORIGINAL_CHECKSUM (an empty file if NULL) to a new file
3842 identified by NEW_CHECKSUM.
3844 Merge the result into LOCAL_ABSPATH, which is part of the working copy
3845 identified by WRI_ABSPATH. Use OLD_REVISION and TARGET_REVISION for naming
3846 the intermediate files.
3848 The rest of the arguments are passed to svn_wc__internal_merge().
3851 svn_wc__perform_file_merge(svn_skel_t **work_items,
3852 svn_skel_t **conflict_skel,
3853 svn_boolean_t *found_conflict,
3855 const char *local_abspath,
3856 const char *wri_abspath,
3857 const svn_checksum_t *new_checksum,
3858 const svn_checksum_t *original_checksum,
3859 apr_hash_t *old_actual_props,
3860 const apr_array_header_t *ext_patterns,
3861 svn_revnum_t old_revision,
3862 svn_revnum_t target_revision,
3863 const apr_array_header_t *propchanges,
3864 const char *diff3_cmd,
3865 svn_cancel_func_t cancel_func,
3867 apr_pool_t *result_pool,
3868 apr_pool_t *scratch_pool)
3870 /* Actual file exists and has local mods:
3871 Now we need to let loose svn_wc__internal_merge() to merge
3872 the textual changes into the working file. */
3873 const char *oldrev_str, *newrev_str, *mine_str;
3874 const char *merge_left;
3875 svn_boolean_t delete_left = FALSE;
3876 const char *path_ext = "";
3877 const char *new_text_base_tmp_abspath;
3878 enum svn_wc_merge_outcome_t merge_outcome = svn_wc_merge_unchanged;
3879 svn_skel_t *work_item;
3883 SVN_ERR(svn_wc__db_pristine_get_path(&new_text_base_tmp_abspath,
3884 db, wri_abspath, new_checksum,
3885 scratch_pool, scratch_pool));
3887 /* If we have any file extensions we're supposed to
3888 preserve in generated conflict file names, then find
3889 this path's extension. But then, if it isn't one of
3890 the ones we want to keep in conflict filenames,
3891 pretend it doesn't have an extension at all. */
3892 if (ext_patterns && ext_patterns->nelts)
3894 svn_path_splitext(NULL, &path_ext, local_abspath, scratch_pool);
3895 if (! (*path_ext && svn_cstring_match_glob_list(path_ext, ext_patterns)))
3899 /* old_revision can be invalid when the conflict is against a
3901 if (!SVN_IS_VALID_REVNUM(old_revision))
3904 oldrev_str = apr_psprintf(scratch_pool, ".r%ld%s%s",
3906 *path_ext ? "." : "",
3907 *path_ext ? path_ext : "");
3909 newrev_str = apr_psprintf(scratch_pool, ".r%ld%s%s",
3911 *path_ext ? "." : "",
3912 *path_ext ? path_ext : "");
3913 mine_str = apr_psprintf(scratch_pool, ".mine%s%s",
3914 *path_ext ? "." : "",
3915 *path_ext ? path_ext : "");
3917 if (! original_checksum)
3919 SVN_ERR(get_empty_tmp_file(&merge_left, db, wri_abspath,
3920 result_pool, scratch_pool));
3924 SVN_ERR(svn_wc__db_pristine_get_path(&merge_left, db, wri_abspath,
3926 result_pool, scratch_pool));
3928 /* Merge the changes from the old textbase to the new
3929 textbase into the file we're updating.
3930 Remember that this function wants full paths! */
3931 SVN_ERR(svn_wc__internal_merge(&work_item,
3936 new_text_base_tmp_abspath,
3939 oldrev_str, newrev_str, mine_str,
3941 FALSE /* dry_run */,
3942 diff3_cmd, NULL, propchanges,
3943 cancel_func, cancel_baton,
3944 result_pool, scratch_pool));
3946 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
3947 *found_conflict = (merge_outcome == svn_wc_merge_conflict);
3949 /* If we created a temporary left merge file, get rid of it. */
3952 SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, wri_abspath,
3954 result_pool, scratch_pool));
3955 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
3958 return SVN_NO_ERROR;
3961 /* This is the small planet. It has the complex responsibility of
3962 * "integrating" a new revision of a file into a working copy.
3964 * Given a file_baton FB for a file either already under version control, or
3965 * prepared (see below) to join version control, fully install a
3966 * new revision of the file.
3968 * ### transitional: installation of the working file will be handled
3969 * ### by the *INSTALL_PRISTINE flag.
3971 * By "install", we mean: create a new text-base and prop-base, merge
3972 * any textual and property changes into the working file, and finally
3973 * update all metadata so that the working copy believes it has a new
3974 * working revision of the file. All of this work includes being
3975 * sensitive to eol translation, keyword substitution, and performing
3976 * all actions accumulated the parent directory's work queue.
3978 * Set *CONTENT_STATE to the state of the contents after the
3981 * Return values are allocated in RESULT_POOL and temporary allocations
3982 * are performed in SCRATCH_POOL.
3984 static svn_error_t *
3985 merge_file(svn_skel_t **work_items,
3986 svn_skel_t **conflict_skel,
3987 svn_boolean_t *install_pristine,
3988 const char **install_from,
3989 svn_wc_notify_state_t *content_state,
3990 struct file_baton *fb,
3991 apr_hash_t *actual_props,
3992 apr_time_t last_changed_date,
3993 apr_pool_t *result_pool,
3994 apr_pool_t *scratch_pool)
3996 struct edit_baton *eb = fb->edit_baton;
3997 struct dir_baton *pb = fb->dir_baton;
3998 svn_boolean_t is_locally_modified;
3999 svn_boolean_t found_text_conflict = FALSE;
4001 SVN_ERR_ASSERT(! fb->shadowed
4002 && ! fb->obstruction_found
4003 && ! fb->edit_obstructed);
4006 When this function is called on file F, we assume the following
4009 - The new pristine text of F is present in the pristine store
4010 iff FB->NEW_TEXT_BASE_SHA1_CHECKSUM is not NULL.
4012 - The WC metadata still reflects the old version of F.
4013 (We can still access the old pristine base text of F.)
4015 The goal is to update the local working copy of F to reflect
4016 the changes received from the repository, preserving any local
4021 *install_pristine = FALSE;
4022 *install_from = NULL;
4024 /* Start by splitting the file path, getting an access baton for the parent,
4025 and an entry for the file if any. */
4027 /* Has the user made local mods to the working file?
4028 Note that this compares to the current pristine file, which is
4029 different from fb->old_text_base_path if we have a replaced-with-history
4030 file. However, in the case we had an obstruction, we check against the
4033 if (fb->adding_file && !fb->add_existed)
4035 is_locally_modified = FALSE; /* There is no file: Don't check */
4039 /* The working file is not an obstruction.
4040 So: is the file modified, relative to its ORIGINAL pristine?
4042 This function sets is_locally_modified to FALSE for
4043 files that do not exist and for directories. */
4045 SVN_ERR(svn_wc__internal_file_modified_p(&is_locally_modified,
4046 eb->db, fb->local_abspath,
4047 FALSE /* exact_comparison */,
4051 /* For 'textual' merging, we use the following system:
4053 When a file is modified and we have a new BASE:
4055 * svn_wc_merge uses diff3
4056 * possibly makes backups and marks files as conflicted.
4059 * svn_wc_merge makes backups and marks files as conflicted.
4061 If a file is not modified and we have a new BASE:
4062 * Install from pristine.
4064 If we have property changes related to magic properties or if the
4065 svn:keywords property is set:
4066 * Retranslate from the working file.
4068 if (! is_locally_modified
4069 && fb->new_text_base_sha1_checksum)
4071 /* If there are no local mods, who cares whether it's a text
4072 or binary file! Just write a log command to overwrite
4073 any working file with the new text-base. If newline
4074 conversion or keyword substitution is activated, this
4075 will happen as well during the copy.
4076 For replaced files, though, we want to merge in the changes
4077 even if the file is not modified compared to the (non-revert)
4080 *install_pristine = TRUE;
4082 else if (fb->new_text_base_sha1_checksum)
4084 /* Actual file exists and has local mods:
4085 Now we need to let loose svn_wc__merge_internal() to merge
4086 the textual changes into the working file. */
4087 SVN_ERR(svn_wc__perform_file_merge(work_items,
4089 &found_text_conflict,
4093 fb->new_text_base_sha1_checksum,
4096 : fb->original_checksum,
4100 *eb->target_revision,
4103 eb->cancel_func, eb->cancel_baton,
4104 result_pool, scratch_pool));
4105 } /* end: working file exists and has mods */
4108 /* There is no new text base, but let's see if the working file needs
4109 to be updated for any other reason. */
4111 apr_hash_t *keywords;
4113 /* Determine if any of the propchanges are the "magic" ones that
4114 might require changing the working file. */
4115 svn_boolean_t magic_props_changed;
4117 magic_props_changed = svn_wc__has_magic_property(fb->propchanges);
4119 SVN_ERR(svn_wc__get_translate_info(NULL, NULL,
4122 eb->db, fb->local_abspath,
4124 scratch_pool, scratch_pool));
4125 if (magic_props_changed || keywords)
4127 /* Special edge-case: it's possible that this file installation
4128 only involves propchanges, but that some of those props still
4129 require a retranslation of the working file.
4131 OR that the file doesn't involve propchanges which by themselves
4132 require retranslation, but receiving a change bumps the revision
4133 number which requires re-expansion of keywords... */
4135 if (is_locally_modified)
4137 const char *tmptext;
4139 /* Copy and DEtranslate the working file to a temp text-base.
4140 Note that detranslation is done according to the old props. */
4141 SVN_ERR(svn_wc__internal_translated_file(
4142 &tmptext, fb->local_abspath, eb->db, fb->local_abspath,
4143 SVN_WC_TRANSLATE_TO_NF
4144 | SVN_WC_TRANSLATE_NO_OUTPUT_CLEANUP,
4145 eb->cancel_func, eb->cancel_baton,
4146 result_pool, scratch_pool));
4148 /* We always want to reinstall the working file if the magic
4149 properties have changed, or there are any keywords present.
4150 Note that TMPTEXT might actually refer to the working file
4151 itself (the above function skips a detranslate when not
4152 required). This is acceptable, as we will (re)translate
4153 according to the new properties into a temporary file (from
4154 the working file), and then rename the temp into place. Magic!
4156 *install_pristine = TRUE;
4157 *install_from = tmptext;
4161 /* Use our existing 'copy' from the pristine store instead
4162 of making a new copy. This way we can use the standard code
4163 to update the recorded size and modification time.
4165 *install_pristine = TRUE;
4170 /* Set the returned content state. */
4172 if (found_text_conflict)
4173 *content_state = svn_wc_notify_state_conflicted;
4174 else if (fb->new_text_base_sha1_checksum)
4176 if (is_locally_modified)
4177 *content_state = svn_wc_notify_state_merged;
4179 *content_state = svn_wc_notify_state_changed;
4182 *content_state = svn_wc_notify_state_unchanged;
4184 return SVN_NO_ERROR;
4188 /* An svn_delta_editor_t function. */
4189 /* Mostly a wrapper around merge_file. */
4190 static svn_error_t *
4191 close_file(void *file_baton,
4192 const char *expected_md5_digest,
4195 struct file_baton *fb = file_baton;
4196 struct dir_baton *pdb = fb->dir_baton;
4197 struct edit_baton *eb = fb->edit_baton;
4198 svn_wc_notify_state_t content_state, prop_state;
4199 svn_wc_notify_lock_state_t lock_state;
4200 svn_checksum_t *expected_md5_checksum = NULL;
4201 apr_hash_t *new_base_props = NULL;
4202 apr_hash_t *new_actual_props = NULL;
4203 apr_array_header_t *entry_prop_changes;
4204 apr_array_header_t *dav_prop_changes;
4205 apr_array_header_t *regular_prop_changes;
4206 apr_hash_t *current_base_props = NULL;
4207 apr_hash_t *current_actual_props = NULL;
4208 apr_hash_t *local_actual_props = NULL;
4209 svn_skel_t *all_work_items = NULL;
4210 svn_skel_t *conflict_skel = NULL;
4211 svn_skel_t *work_item;
4212 apr_pool_t *scratch_pool = fb->pool; /* Destroyed at function exit */
4213 svn_boolean_t keep_recorded_info = FALSE;
4214 const svn_checksum_t *new_checksum;
4215 apr_array_header_t *iprops = NULL;
4219 svn_pool_destroy(fb->pool);
4220 SVN_ERR(maybe_release_dir_info(pdb));
4221 return SVN_NO_ERROR;
4225 conflict_skel = fb->edit_conflict;
4227 if (expected_md5_digest)
4228 SVN_ERR(svn_checksum_parse_hex(&expected_md5_checksum, svn_checksum_md5,
4229 expected_md5_digest, scratch_pool));
4231 if (fb->new_text_base_md5_checksum && expected_md5_checksum
4232 && !svn_checksum_match(expected_md5_checksum,
4233 fb->new_text_base_md5_checksum))
4234 return svn_error_trace(
4235 svn_checksum_mismatch_err(expected_md5_checksum,
4236 fb->new_text_base_md5_checksum,
4238 _("Checksum mismatch for '%s'"),
4239 svn_dirent_local_style(
4240 fb->local_abspath, pool)));
4242 /* Gather the changes for each kind of property. */
4243 SVN_ERR(svn_categorize_props(fb->propchanges, &entry_prop_changes,
4244 &dav_prop_changes, ®ular_prop_changes,
4247 /* Extract the changed_* and lock state information. */
4249 svn_revnum_t new_changed_rev;
4250 apr_time_t new_changed_date;
4251 const char *new_changed_author;
4253 SVN_ERR(accumulate_last_change(&new_changed_rev,
4255 &new_changed_author,
4257 scratch_pool, scratch_pool));
4259 if (SVN_IS_VALID_REVNUM(new_changed_rev))
4260 fb->changed_rev = new_changed_rev;
4261 if (new_changed_date != 0)
4262 fb->changed_date = new_changed_date;
4263 if (new_changed_author != NULL)
4264 fb->changed_author = new_changed_author;
4267 /* Determine whether the file has become unlocked. */
4271 lock_state = svn_wc_notify_lock_state_unchanged;
4273 for (i = 0; i < entry_prop_changes->nelts; ++i)
4275 const svn_prop_t *prop
4276 = &APR_ARRAY_IDX(entry_prop_changes, i, svn_prop_t);
4278 /* If we see a change to the LOCK_TOKEN entry prop, then the only
4279 possible change is its REMOVAL. Thus, the lock has been removed,
4280 and we should likewise remove our cached copy of it. */
4281 if (! strcmp(prop->name, SVN_PROP_ENTRY_LOCK_TOKEN))
4283 /* If we lose the lock, but not because we are switching to
4284 another url, remove the state lock from the wc */
4285 if (! eb->switch_relpath
4286 || strcmp(fb->new_relpath, fb->old_repos_relpath) == 0)
4288 SVN_ERR_ASSERT(prop->value == NULL);
4289 SVN_ERR(svn_wc__db_lock_remove(eb->db, fb->local_abspath,
4292 lock_state = svn_wc_notify_lock_state_unlocked;
4299 /* Install all kinds of properties. It is important to do this before
4300 any file content merging, since that process might expand keywords, in
4301 which case we want the new entryprops to be in place. */
4303 /* Write log commands to merge REGULAR_PROPS into the existing
4304 properties of FB->LOCAL_ABSPATH. Update *PROP_STATE to reflect
4305 the result of the regular prop merge.
4307 BASE_PROPS and WORKING_PROPS are hashes of the base and
4308 working props of the file; if NULL they are read from the wc. */
4310 /* ### some of this feels like voodoo... */
4312 if ((!fb->adding_file || fb->add_existed)
4314 SVN_ERR(svn_wc__get_actual_props(&local_actual_props,
4315 eb->db, fb->local_abspath,
4316 scratch_pool, scratch_pool));
4317 if (local_actual_props == NULL)
4318 local_actual_props = apr_hash_make(scratch_pool);
4320 if (fb->add_existed)
4322 /* This node already exists. Grab the current pristine properties. */
4323 SVN_ERR(svn_wc__db_read_pristine_props(¤t_base_props,
4324 eb->db, fb->local_abspath,
4325 scratch_pool, scratch_pool));
4326 current_actual_props = local_actual_props;
4328 else if (!fb->adding_file)
4330 /* Get the BASE properties for proper merging. */
4331 SVN_ERR(svn_wc__db_base_get_props(¤t_base_props,
4332 eb->db, fb->local_abspath,
4333 scratch_pool, scratch_pool));
4334 current_actual_props = local_actual_props;
4337 /* Note: even if the node existed before, it may not have
4338 pristine props (e.g a local-add) */
4339 if (current_base_props == NULL)
4340 current_base_props = apr_hash_make(scratch_pool);
4342 /* And new nodes need an empty set of ACTUAL props. */
4343 if (current_actual_props == NULL)
4344 current_actual_props = apr_hash_make(scratch_pool);
4346 prop_state = svn_wc_notify_state_unknown;
4350 svn_boolean_t install_pristine;
4351 const char *install_from = NULL;
4353 /* Merge the 'regular' props into the existing working proplist. */
4354 /* This will merge the old and new props into a new prop db, and
4355 write <cp> commands to the logfile to install the merged
4357 new_base_props = svn_prop__patch(current_base_props, regular_prop_changes,
4359 SVN_ERR(svn_wc__merge_props(&conflict_skel,
4364 NULL /* server_baseprops (update, not merge) */,
4366 current_actual_props,
4367 regular_prop_changes, /* propchanges */
4370 /* We will ALWAYS have properties to save (after a not-dry-run merge). */
4371 SVN_ERR_ASSERT(new_base_props != NULL && new_actual_props != NULL);
4373 /* Merge the text. This will queue some additional work. */
4374 if (!fb->obstruction_found && !fb->edit_obstructed)
4377 err = merge_file(&work_item, &conflict_skel,
4378 &install_pristine, &install_from,
4379 &content_state, fb, current_actual_props,
4380 fb->changed_date, scratch_pool, scratch_pool);
4382 if (err && err->apr_err == SVN_ERR_WC_PATH_ACCESS_DENIED)
4384 if (eb->notify_func)
4386 svn_wc_notify_t *notify =svn_wc_create_notify(
4388 svn_wc_notify_update_skip_access_denied,
4391 notify->kind = svn_node_file;
4394 eb->notify_func(eb->notify_baton, notify, scratch_pool);
4396 svn_error_clear(err);
4398 SVN_ERR(remember_skipped_tree(eb, fb->local_abspath,
4400 fb->skip_this = TRUE;
4402 svn_pool_destroy(fb->pool);
4403 SVN_ERR(maybe_release_dir_info(pdb));
4404 return SVN_NO_ERROR;
4409 all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4414 install_pristine = FALSE;
4415 if (fb->new_text_base_sha1_checksum)
4416 content_state = svn_wc_notify_state_changed;
4418 content_state = svn_wc_notify_state_unchanged;
4421 if (install_pristine)
4423 svn_boolean_t record_fileinfo;
4425 /* If we are installing from the pristine contents, then go ahead and
4426 record the fileinfo. That will be the "proper" values. Installing
4427 from some random file means the fileinfo does NOT correspond to
4428 the pristine (in which case, the fileinfo will be cleared for
4430 record_fileinfo = (install_from == NULL);
4432 SVN_ERR(svn_wc__wq_build_file_install(&work_item,
4436 eb->use_commit_times,
4438 scratch_pool, scratch_pool));
4439 all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4442 else if (lock_state == svn_wc_notify_lock_state_unlocked
4443 && !fb->obstruction_found)
4445 /* If a lock was removed and we didn't update the text contents, we
4446 might need to set the file read-only.
4448 Note: this will also update the executable flag, but ... meh. */
4449 SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, eb->db,
4451 scratch_pool, scratch_pool));
4452 all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4456 if (! install_pristine
4457 && (content_state == svn_wc_notify_state_unchanged))
4459 /* It is safe to keep the current recorded timestamp and size */
4460 keep_recorded_info = TRUE;
4463 /* Clean up any temporary files. */
4465 /* Remove the INSTALL_FROM file, as long as it doesn't refer to the
4467 if (install_from != NULL
4468 && strcmp(install_from, fb->local_abspath) != 0)
4470 SVN_ERR(svn_wc__wq_build_file_remove(&work_item, eb->db,
4471 fb->local_abspath, install_from,
4472 scratch_pool, scratch_pool));
4473 all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4479 /* Adding or updating a BASE node under a locally added node. */
4480 apr_hash_t *fake_actual_props;
4482 if (fb->adding_file)
4483 fake_actual_props = apr_hash_make(scratch_pool);
4485 fake_actual_props = current_base_props;
4487 /* Store the incoming props (sent as propchanges) in new_base_props
4488 and create a set of new actual props to use for notifications */
4489 new_base_props = svn_prop__patch(current_base_props, regular_prop_changes,
4491 SVN_ERR(svn_wc__merge_props(&conflict_skel,
4496 NULL /* server_baseprops (not merging) */,
4497 current_base_props /* pristine_props */,
4498 fake_actual_props /* actual_props */,
4499 regular_prop_changes, /* propchanges */
4503 if (fb->new_text_base_sha1_checksum)
4504 content_state = svn_wc_notify_state_changed;
4506 content_state = svn_wc_notify_state_unchanged;
4509 /* Insert/replace the BASE node with all of the new metadata. */
4511 /* Set the 'checksum' column of the file's BASE_NODE row to
4512 * NEW_TEXT_BASE_SHA1_CHECKSUM. The pristine text identified by that
4513 * checksum is already in the pristine store. */
4514 new_checksum = fb->new_text_base_sha1_checksum;
4516 /* If we don't have a NEW checksum, then the base must not have changed.
4517 Just carry over the old checksum. */
4518 if (new_checksum == NULL)
4519 new_checksum = fb->original_checksum;
4523 SVN_ERR(complete_conflict(conflict_skel,
4526 fb->old_repos_relpath,
4529 svn_node_file, svn_node_file,
4530 fb->pool, scratch_pool));
4532 SVN_ERR(svn_wc__conflict_create_markers(&work_item,
4533 eb->db, fb->local_abspath,
4535 scratch_pool, scratch_pool));
4537 all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4541 /* Any inherited props to be set set for this base node? */
4542 if (eb->wcroot_iprops)
4544 iprops = svn_hash_gets(eb->wcroot_iprops, fb->local_abspath);
4546 /* close_edit may also update iprops for switched nodes, catching
4547 those for which close_directory is never called (e.g. a switch
4548 with no changes). So as a minor optimization we remove any
4549 iprops from the hash so as not to set them again in
4552 svn_hash_sets(eb->wcroot_iprops, fb->local_abspath, NULL);
4555 SVN_ERR(svn_wc__db_base_add_file(eb->db, fb->local_abspath,
4558 eb->repos_root, eb->repos_uuid,
4559 *eb->target_revision,
4565 (dav_prop_changes->nelts > 0)
4566 ? svn_prop_array_to_hash(
4570 (fb->add_existed && fb->adding_file),
4571 (! fb->shadowed) && new_base_props,
4575 (fb->shadowed && fb->obstruction_found),
4580 if (conflict_skel && eb->conflict_func)
4581 SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, fb->local_abspath,
4583 NULL /* merge_options */,
4590 /* Deal with the WORKING tree, based on updates to the BASE tree. */
4592 svn_hash_sets(fb->dir_baton->not_present_files, fb->name, NULL);
4594 /* Send a notification to the callback function. (Skip notifications
4595 about files which were already notified for another reason.) */
4596 if (eb->notify_func && !fb->already_notified
4597 && (fb->edited || lock_state == svn_wc_notify_lock_state_unlocked))
4599 svn_wc_notify_t *notify;
4600 svn_wc_notify_action_t action = svn_wc_notify_update_update;
4604 if (fb->shadowed || fb->edit_obstructed)
4605 action = fb->adding_file
4606 ? svn_wc_notify_update_shadowed_add
4607 : svn_wc_notify_update_shadowed_update;
4608 else if (fb->obstruction_found || fb->add_existed)
4610 if (content_state != svn_wc_notify_state_conflicted)
4611 action = svn_wc_notify_exists;
4613 else if (fb->adding_file)
4615 action = svn_wc_notify_update_add;
4620 SVN_ERR_ASSERT(lock_state == svn_wc_notify_lock_state_unlocked);
4621 action = svn_wc_notify_update_broken_lock;
4624 /* If the file was moved-away, notify for the moved-away node.
4625 * The original location only had its BASE info changed and
4626 * we don't usually notify about such changes. */
4627 notify = svn_wc_create_notify(fb->local_abspath, action, scratch_pool);
4628 notify->kind = svn_node_file;
4629 notify->content_state = content_state;
4630 notify->prop_state = prop_state;
4631 notify->lock_state = lock_state;
4632 notify->revision = *eb->target_revision;
4633 notify->old_revision = fb->old_revision;
4635 /* Fetch the mimetype from the actual properties */
4636 notify->mime_type = svn_prop_get_value(new_actual_props,
4637 SVN_PROP_MIME_TYPE);
4639 eb->notify_func(eb->notify_baton, notify, scratch_pool);
4642 svn_pool_destroy(fb->pool); /* Destroy scratch_pool */
4644 /* We have one less referrer to the directory */
4645 SVN_ERR(maybe_release_dir_info(pdb));
4647 return SVN_NO_ERROR;
4651 /* An svn_delta_editor_t function. */
4652 static svn_error_t *
4653 close_edit(void *edit_baton,
4656 struct edit_baton *eb = edit_baton;
4657 apr_pool_t *scratch_pool = eb->pool;
4659 /* The editor didn't even open the root; we have to take care of
4660 some cleanup stuffs. */
4661 if (! eb->root_opened
4662 && *eb->target_basename == '\0')
4664 /* We need to "un-incomplete" the root directory. */
4665 SVN_ERR(svn_wc__db_temp_op_end_directory_update(eb->db,
4670 /* By definition, anybody "driving" this editor for update or switch
4671 purposes at a *minimum* must have called set_target_revision() at
4672 the outset, and close_edit() at the end -- even if it turned out
4673 that no changes ever had to be made, and open_root() was never
4674 called. That's fine. But regardless, when the edit is over,
4675 this editor needs to make sure that *all* paths have had their
4676 revisions bumped to the new target revision. */
4678 /* Make sure our update target now has the new working revision.
4679 Also, if this was an 'svn switch', then rewrite the target's
4680 url. All of this tweaking might happen recursively! Note
4681 that if eb->target is NULL, that's okay (albeit "sneaky",
4684 /* Extra check: if the update did nothing but make its target
4685 'deleted', then do *not* run cleanup on the target, as it
4686 will only remove the deleted entry! */
4687 if (! eb->target_deleted)
4689 SVN_ERR(svn_wc__db_op_bump_revisions_post_update(eb->db,
4691 eb->requested_depth,
4695 *(eb->target_revision),
4702 if (*eb->target_basename != '\0')
4704 svn_wc__db_status_t status;
4707 /* Note: we are fetching information about the *target*, not anchor.
4708 There is no guarantee that the target has a BASE node.
4711 The node was not present in BASE, but locally-added, and the
4712 update did not create a new BASE node "under" the local-add.
4714 If there is no BASE node for the target, then we certainly don't
4715 have to worry about removing it. */
4716 err = svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL, NULL,
4717 NULL, NULL, NULL, NULL, NULL, NULL,
4718 NULL, NULL, NULL, NULL,
4719 eb->db, eb->target_abspath,
4720 scratch_pool, scratch_pool);
4723 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
4724 return svn_error_trace(err);
4726 svn_error_clear(err);
4728 else if (status == svn_wc__db_status_excluded)
4730 /* There is a small chance that the explicit target of an update/
4731 switch is gone in the repository, in that specific case the
4732 node hasn't been re-added to the BASE tree by this update.
4734 If so, we should get rid of this excluded node now. */
4736 SVN_ERR(svn_wc__db_base_remove(eb->db, eb->target_abspath,
4737 FALSE /* keep_as_working */,
4738 FALSE /* queue_deletes */,
4739 FALSE /* remove_locks */,
4741 NULL, NULL, scratch_pool));
4746 /* The edit is over: run the wq with proper cancel support,
4747 but first kill the handler that would run it on the pool
4748 cleanup at the end of this function. */
4749 apr_pool_cleanup_kill(eb->pool, eb, cleanup_edit_baton);
4751 SVN_ERR(svn_wc__wq_run(eb->db, eb->wcroot_abspath,
4752 eb->cancel_func, eb->cancel_baton,
4755 /* The edit is over, free its pool.
4756 ### No, this is wrong. Who says this editor/baton won't be used
4757 again? But the change is not merely to remove this call. We
4758 should also make eb->pool not be a subpool (see make_editor),
4759 and change callers of svn_client_{checkout,update,switch} to do
4760 better pool management. ### */
4762 svn_pool_destroy(eb->pool);
4764 return SVN_NO_ERROR;
4768 /*** Returning editors. ***/
4770 /* Helper for the three public editor-supplying functions. */
4771 static svn_error_t *
4772 make_editor(svn_revnum_t *target_revision,
4774 const char *anchor_abspath,
4775 const char *target_basename,
4776 apr_hash_t *wcroot_iprops,
4777 svn_boolean_t use_commit_times,
4778 const char *switch_url,
4780 svn_boolean_t depth_is_sticky,
4781 svn_boolean_t allow_unver_obstructions,
4782 svn_boolean_t adds_as_modification,
4783 svn_boolean_t server_performs_filtering,
4784 svn_boolean_t clean_checkout,
4785 svn_wc_notify_func2_t notify_func,
4787 svn_cancel_func_t cancel_func,
4789 svn_wc_dirents_func_t fetch_dirents_func,
4790 void *fetch_dirents_baton,
4791 svn_wc_conflict_resolver_func2_t conflict_func,
4792 void *conflict_baton,
4793 svn_wc_external_update_t external_func,
4794 void *external_baton,
4795 const char *diff3_cmd,
4796 const apr_array_header_t *preserved_exts,
4797 const svn_delta_editor_t **editor,
4799 apr_pool_t *result_pool,
4800 apr_pool_t *scratch_pool)
4802 struct edit_baton *eb;
4804 apr_pool_t *edit_pool = svn_pool_create(result_pool);
4805 svn_delta_editor_t *tree_editor = svn_delta_default_editor(edit_pool);
4806 const svn_delta_editor_t *inner_editor;
4807 const char *repos_root, *repos_uuid;
4808 struct svn_wc__shim_fetch_baton_t *sfb;
4809 svn_delta_shim_callbacks_t *shim_callbacks =
4810 svn_delta_shim_callbacks_default(edit_pool);
4812 /* An unknown depth can't be sticky. */
4813 if (depth == svn_depth_unknown)
4814 depth_is_sticky = FALSE;
4816 /* Get the anchor's repository root and uuid. The anchor must already exist
4818 SVN_ERR(svn_wc__db_scan_base_repos(NULL, &repos_root, &repos_uuid,
4820 result_pool, scratch_pool));
4822 /* With WC-NG we need a valid repository root */
4823 SVN_ERR_ASSERT(repos_root != NULL && repos_uuid != NULL);
4825 /* Disallow a switch operation to change the repository root of the target,
4826 if that is known. */
4827 if (switch_url && !svn_uri__is_ancestor(repos_root, switch_url))
4828 return svn_error_createf(SVN_ERR_WC_INVALID_SWITCH, NULL,
4829 _("'%s'\nis not the same repository as\n'%s'"),
4830 switch_url, repos_root);
4832 /* Construct an edit baton. */
4833 eb = apr_pcalloc(edit_pool, sizeof(*eb));
4834 eb->pool = edit_pool;
4835 eb->use_commit_times = use_commit_times;
4836 eb->target_revision = target_revision;
4837 eb->repos_root = repos_root;
4838 eb->repos_uuid = repos_uuid;
4840 eb->target_basename = target_basename;
4841 eb->anchor_abspath = anchor_abspath;
4842 eb->wcroot_iprops = wcroot_iprops;
4844 SVN_ERR(svn_wc__db_get_wcroot(&eb->wcroot_abspath, db, anchor_abspath,
4845 edit_pool, scratch_pool));
4848 eb->switch_relpath =
4849 svn_uri_skip_ancestor(repos_root, switch_url, scratch_pool);
4851 eb->switch_relpath = NULL;
4853 if (svn_path_is_empty(target_basename))
4854 eb->target_abspath = eb->anchor_abspath;
4856 eb->target_abspath = svn_dirent_join(eb->anchor_abspath, target_basename,
4859 eb->requested_depth = depth;
4860 eb->depth_is_sticky = depth_is_sticky;
4861 eb->notify_func = notify_func;
4862 eb->notify_baton = notify_baton;
4863 eb->external_func = external_func;
4864 eb->external_baton = external_baton;
4865 eb->diff3_cmd = diff3_cmd;
4866 eb->cancel_func = cancel_func;
4867 eb->cancel_baton = cancel_baton;
4868 eb->conflict_func = conflict_func;
4869 eb->conflict_baton = conflict_baton;
4870 eb->allow_unver_obstructions = allow_unver_obstructions;
4871 eb->adds_as_modification = adds_as_modification;
4872 eb->clean_checkout = clean_checkout;
4873 eb->skipped_trees = apr_hash_make(edit_pool);
4874 eb->dir_dirents = apr_hash_make(edit_pool);
4875 eb->ext_patterns = preserved_exts;
4877 apr_pool_cleanup_register(edit_pool, eb, cleanup_edit_baton,
4878 apr_pool_cleanup_null);
4880 /* Construct an editor. */
4881 tree_editor->set_target_revision = set_target_revision;
4882 tree_editor->open_root = open_root;
4883 tree_editor->delete_entry = delete_entry;
4884 tree_editor->add_directory = add_directory;
4885 tree_editor->open_directory = open_directory;
4886 tree_editor->change_dir_prop = change_dir_prop;
4887 tree_editor->close_directory = close_directory;
4888 tree_editor->absent_directory = absent_directory;
4889 tree_editor->add_file = add_file;
4890 tree_editor->open_file = open_file;
4891 tree_editor->apply_textdelta = apply_textdelta;
4892 tree_editor->change_file_prop = change_file_prop;
4893 tree_editor->close_file = close_file;
4894 tree_editor->absent_file = absent_file;
4895 tree_editor->close_edit = close_edit;
4897 /* Fiddle with the type system. */
4898 inner_editor = tree_editor;
4901 if (!depth_is_sticky
4902 && depth != svn_depth_unknown
4903 && svn_depth_empty <= depth && depth < svn_depth_infinity
4904 && fetch_dirents_func)
4906 /* We are asked to perform an update at a depth less than the ambient
4907 depth. In this case the update won't describe additions that would
4908 have been reported if we updated at the ambient depth. */
4910 svn_node_kind_t dir_kind;
4911 svn_wc__db_status_t dir_status;
4912 const char *dir_repos_relpath;
4913 svn_depth_t dir_depth;
4915 /* we have to do this on the target of the update, not the anchor */
4916 err = svn_wc__db_base_get_info(&dir_status, &dir_kind, NULL,
4917 &dir_repos_relpath, NULL, NULL, NULL,
4918 NULL, NULL, &dir_depth, NULL, NULL, NULL,
4920 db, eb->target_abspath,
4921 scratch_pool, scratch_pool);
4924 && dir_kind == svn_node_dir
4925 && dir_status == svn_wc__db_status_normal)
4927 if (dir_depth > depth)
4929 apr_hash_t *dirents;
4931 /* If we switch, we should look at the new relpath */
4932 if (eb->switch_relpath)
4933 dir_repos_relpath = eb->switch_relpath;
4935 SVN_ERR(fetch_dirents_func(fetch_dirents_baton, &dirents,
4936 repos_root, dir_repos_relpath,
4937 edit_pool, scratch_pool));
4939 if (dirents != NULL && apr_hash_count(dirents))
4940 svn_hash_sets(eb->dir_dirents,
4941 apr_pstrdup(edit_pool, dir_repos_relpath),
4945 if (depth == svn_depth_immediates)
4947 /* Worst case scenario of issue #3569 fix: We have to do the
4948 same for all existing subdirs, but then we check for
4950 const apr_array_header_t *children;
4951 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
4953 SVN_ERR(svn_wc__db_base_get_children(&children, db,
4958 for (i = 0; i < children->nelts; i++)
4960 const char *child_abspath;
4961 const char *child_name;
4963 svn_pool_clear(iterpool);
4965 child_name = APR_ARRAY_IDX(children, i, const char *);
4967 child_abspath = svn_dirent_join(eb->target_abspath,
4968 child_name, iterpool);
4970 SVN_ERR(svn_wc__db_base_get_info(&dir_status, &dir_kind,
4971 NULL, &dir_repos_relpath,
4972 NULL, NULL, NULL, NULL,
4973 NULL, &dir_depth, NULL,
4974 NULL, NULL, NULL, NULL,
4977 iterpool, iterpool));
4979 if (dir_kind == svn_node_dir
4980 && dir_status == svn_wc__db_status_normal
4981 && dir_depth > svn_depth_empty)
4983 apr_hash_t *dirents;
4985 /* If we switch, we should look at the new relpath */
4986 if (eb->switch_relpath)
4987 dir_repos_relpath = svn_relpath_join(
4989 child_name, iterpool);
4991 SVN_ERR(fetch_dirents_func(fetch_dirents_baton, &dirents,
4992 repos_root, dir_repos_relpath,
4993 edit_pool, iterpool));
4995 if (dirents != NULL && apr_hash_count(dirents))
4996 svn_hash_sets(eb->dir_dirents,
4997 apr_pstrdup(edit_pool,
5004 else if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
5005 svn_error_clear(err);
5010 /* We need to limit the scope of our operation to the ambient depths
5011 present in the working copy already, but only if the requested
5012 depth is not sticky. If a depth was explicitly requested,
5013 libsvn_delta/depth_filter_editor.c will ensure that we never see
5014 editor calls that extend beyond the scope of the requested depth.
5015 But even what we do so might extend beyond the scope of our
5016 ambient depth. So we use another filtering editor to avoid
5017 modifying the ambient working copy depth when not asked to do so.
5018 (This can also be skipped if the server understands depth.) */
5019 if (!server_performs_filtering
5020 && !depth_is_sticky)
5021 SVN_ERR(svn_wc__ambient_depth_filter_editor(&inner_editor,
5030 SVN_ERR(svn_delta_get_cancellation_editor(cancel_func,
5038 sfb = apr_palloc(result_pool, sizeof(*sfb));
5040 sfb->base_abspath = eb->anchor_abspath;
5041 sfb->fetch_base = TRUE;
5043 shim_callbacks->fetch_kind_func = svn_wc__fetch_kind_func;
5044 shim_callbacks->fetch_props_func = svn_wc__fetch_props_func;
5045 shim_callbacks->fetch_base_func = svn_wc__fetch_base_func;
5046 shim_callbacks->fetch_baton = sfb;
5048 SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton,
5049 NULL, NULL, shim_callbacks,
5050 result_pool, scratch_pool));
5052 return SVN_NO_ERROR;
5057 svn_wc__get_update_editor(const svn_delta_editor_t **editor,
5059 svn_revnum_t *target_revision,
5060 svn_wc_context_t *wc_ctx,
5061 const char *anchor_abspath,
5062 const char *target_basename,
5063 apr_hash_t *wcroot_iprops,
5064 svn_boolean_t use_commit_times,
5066 svn_boolean_t depth_is_sticky,
5067 svn_boolean_t allow_unver_obstructions,
5068 svn_boolean_t adds_as_modification,
5069 svn_boolean_t server_performs_filtering,
5070 svn_boolean_t clean_checkout,
5071 const char *diff3_cmd,
5072 const apr_array_header_t *preserved_exts,
5073 svn_wc_dirents_func_t fetch_dirents_func,
5074 void *fetch_dirents_baton,
5075 svn_wc_conflict_resolver_func2_t conflict_func,
5076 void *conflict_baton,
5077 svn_wc_external_update_t external_func,
5078 void *external_baton,
5079 svn_cancel_func_t cancel_func,
5081 svn_wc_notify_func2_t notify_func,
5083 apr_pool_t *result_pool,
5084 apr_pool_t *scratch_pool)
5086 return make_editor(target_revision, wc_ctx->db, anchor_abspath,
5087 target_basename, wcroot_iprops, use_commit_times,
5088 NULL, depth, depth_is_sticky, allow_unver_obstructions,
5089 adds_as_modification, server_performs_filtering,
5091 notify_func, notify_baton,
5092 cancel_func, cancel_baton,
5093 fetch_dirents_func, fetch_dirents_baton,
5094 conflict_func, conflict_baton,
5095 external_func, external_baton,
5096 diff3_cmd, preserved_exts, editor, edit_baton,
5097 result_pool, scratch_pool);
5101 svn_wc__get_switch_editor(const svn_delta_editor_t **editor,
5103 svn_revnum_t *target_revision,
5104 svn_wc_context_t *wc_ctx,
5105 const char *anchor_abspath,
5106 const char *target_basename,
5107 const char *switch_url,
5108 apr_hash_t *wcroot_iprops,
5109 svn_boolean_t use_commit_times,
5111 svn_boolean_t depth_is_sticky,
5112 svn_boolean_t allow_unver_obstructions,
5113 svn_boolean_t server_performs_filtering,
5114 const char *diff3_cmd,
5115 const apr_array_header_t *preserved_exts,
5116 svn_wc_dirents_func_t fetch_dirents_func,
5117 void *fetch_dirents_baton,
5118 svn_wc_conflict_resolver_func2_t conflict_func,
5119 void *conflict_baton,
5120 svn_wc_external_update_t external_func,
5121 void *external_baton,
5122 svn_cancel_func_t cancel_func,
5124 svn_wc_notify_func2_t notify_func,
5126 apr_pool_t *result_pool,
5127 apr_pool_t *scratch_pool)
5129 SVN_ERR_ASSERT(switch_url && svn_uri_is_canonical(switch_url, scratch_pool));
5131 return make_editor(target_revision, wc_ctx->db, anchor_abspath,
5132 target_basename, wcroot_iprops, use_commit_times,
5134 depth, depth_is_sticky, allow_unver_obstructions,
5135 FALSE /* adds_as_modification */,
5136 server_performs_filtering,
5137 FALSE /* clean_checkout */,
5138 notify_func, notify_baton,
5139 cancel_func, cancel_baton,
5140 fetch_dirents_func, fetch_dirents_baton,
5141 conflict_func, conflict_baton,
5142 external_func, external_baton,
5143 diff3_cmd, preserved_exts,
5145 result_pool, scratch_pool);
5150 /* ### Note that this function is completely different from the rest of the
5151 update editor in what it updates. The update editor changes only BASE
5152 and ACTUAL and this function just changes WORKING and ACTUAL.
5154 In the entries world this function shared a lot of code with the
5155 update editor but in the wonderful new WC-NG world it will probably
5156 do more and more by itself and would be more logically grouped with
5157 the add/copy functionality in adm_ops.c and copy.c. */
5159 svn_wc_add_repos_file4(svn_wc_context_t *wc_ctx,
5160 const char *local_abspath,
5161 svn_stream_t *new_base_contents,
5162 svn_stream_t *new_contents,
5163 apr_hash_t *new_base_props,
5164 apr_hash_t *new_props,
5165 const char *copyfrom_url,
5166 svn_revnum_t copyfrom_rev,
5167 svn_cancel_func_t cancel_func,
5169 apr_pool_t *scratch_pool)
5171 svn_wc__db_t *db = wc_ctx->db;
5172 const char *dir_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
5173 svn_wc__db_status_t status;
5174 svn_node_kind_t kind;
5175 const char *tmp_text_base_abspath;
5176 svn_checksum_t *new_text_base_md5_checksum;
5177 svn_checksum_t *new_text_base_sha1_checksum;
5178 const char *source_abspath = NULL;
5179 svn_skel_t *all_work_items = NULL;
5180 svn_skel_t *work_item;
5181 const char *repos_root_url;
5182 const char *repos_uuid;
5183 const char *original_repos_relpath;
5184 svn_revnum_t changed_rev;
5185 apr_time_t changed_date;
5186 const char *changed_author;
5188 apr_pool_t *pool = scratch_pool;
5190 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5191 SVN_ERR_ASSERT(new_base_contents != NULL);
5192 SVN_ERR_ASSERT(new_base_props != NULL);
5194 /* We should have a write lock on this file's parent directory. */
5195 SVN_ERR(svn_wc__write_check(db, dir_abspath, pool));
5197 err = svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5198 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5199 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5201 db, local_abspath, scratch_pool, scratch_pool);
5203 if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
5204 return svn_error_trace(err);
5206 svn_error_clear(err);
5210 case svn_wc__db_status_not_present:
5211 case svn_wc__db_status_deleted:
5214 return svn_error_createf(SVN_ERR_ENTRY_EXISTS, NULL,
5215 _("Node '%s' exists."),
5216 svn_dirent_local_style(local_abspath,
5220 SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, &repos_root_url,
5221 &repos_uuid, NULL, NULL, NULL, NULL, NULL, NULL,
5222 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5223 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5224 db, dir_abspath, scratch_pool, scratch_pool));
5228 case svn_wc__db_status_normal:
5229 case svn_wc__db_status_added:
5231 case svn_wc__db_status_deleted:
5233 svn_error_createf(SVN_ERR_WC_SCHEDULE_CONFLICT, NULL,
5234 _("Can't add '%s' to a parent directory"
5235 " scheduled for deletion"),
5236 svn_dirent_local_style(local_abspath,
5239 return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, err,
5240 _("Can't find parent directory's node while"
5241 " trying to add '%s'"),
5242 svn_dirent_local_style(local_abspath,
5245 if (kind != svn_node_dir)
5246 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
5247 _("Can't schedule an addition of '%s'"
5248 " below a not-directory node"),
5249 svn_dirent_local_style(local_abspath,
5252 /* Fabricate the anticipated new URL of the target and check the
5253 copyfrom URL to be in the same repository. */
5254 if (copyfrom_url != NULL)
5256 /* Find the repository_root via the parent directory, which
5257 is always versioned before this function is called */
5259 if (!repos_root_url)
5261 /* The parent is an addition, scan upwards to find the right info */
5262 SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, NULL,
5263 &repos_root_url, &repos_uuid,
5264 NULL, NULL, NULL, NULL,
5265 wc_ctx->db, dir_abspath,
5266 scratch_pool, scratch_pool));
5268 SVN_ERR_ASSERT(repos_root_url);
5270 original_repos_relpath =
5271 svn_uri_skip_ancestor(repos_root_url, copyfrom_url, scratch_pool);
5273 if (!original_repos_relpath)
5274 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
5275 _("Copyfrom-url '%s' has different repository"
5277 copyfrom_url, repos_root_url);
5281 original_repos_relpath = NULL;
5282 copyfrom_rev = SVN_INVALID_REVNUM; /* Just to be sure. */
5285 /* Set CHANGED_* to reflect the entry props in NEW_BASE_PROPS, and
5286 filter NEW_BASE_PROPS so it contains only regular props. */
5288 apr_array_header_t *regular_props;
5289 apr_array_header_t *entry_props;
5291 SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(new_base_props, pool),
5292 &entry_props, NULL, ®ular_props,
5295 /* Put regular props back into a hash table. */
5296 new_base_props = svn_prop_array_to_hash(regular_props, pool);
5298 /* Get the change_* info from the entry props. */
5299 SVN_ERR(accumulate_last_change(&changed_rev,
5302 entry_props, pool, pool));
5305 /* Copy NEW_BASE_CONTENTS into a temporary file so our log can refer to
5306 it, and set TMP_TEXT_BASE_ABSPATH to its path. Compute its
5307 NEW_TEXT_BASE_MD5_CHECKSUM and NEW_TEXT_BASE_SHA1_CHECKSUM as we copy. */
5309 svn_stream_t *tmp_base_contents;
5311 SVN_ERR(svn_wc__open_writable_base(&tmp_base_contents,
5312 &tmp_text_base_abspath,
5313 &new_text_base_md5_checksum,
5314 &new_text_base_sha1_checksum,
5315 wc_ctx->db, local_abspath,
5317 SVN_ERR(svn_stream_copy3(new_base_contents, tmp_base_contents,
5318 cancel_func, cancel_baton, pool));
5321 /* If the caller gave us a new working file, copy it to a safe (temporary)
5322 location and set SOURCE_ABSPATH to that path. We'll then translate/copy
5323 that into place after the node's state has been created. */
5326 const char *temp_dir_abspath;
5327 svn_stream_t *tmp_contents;
5329 SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, db,
5330 local_abspath, pool, pool));
5331 SVN_ERR(svn_stream_open_unique(&tmp_contents, &source_abspath,
5332 temp_dir_abspath, svn_io_file_del_none,
5334 SVN_ERR(svn_stream_copy3(new_contents, tmp_contents,
5335 cancel_func, cancel_baton, pool));
5338 /* Install new text base for copied files. Added files do NOT have a
5340 if (copyfrom_url != NULL)
5342 SVN_ERR(svn_wc__db_pristine_install(db, tmp_text_base_abspath,
5343 new_text_base_sha1_checksum,
5344 new_text_base_md5_checksum, pool));
5348 /* ### There's something wrong around here. Sometimes (merge from a
5349 foreign repository, at least) we are called with copyfrom_url =
5350 NULL and an empty new_base_contents (and an empty set of
5351 new_base_props). Why an empty "new base"?
5353 That happens in merge_tests.py 54,87,88,89,143.
5355 In that case, having been given this supposed "new base" file, we
5356 copy it and calculate its checksum but do not install it. Why?
5359 To crudely work around one issue with this, that we shouldn't
5360 record a checksum in the database if we haven't installed the
5361 corresponding pristine text, for now we'll just set the checksum
5364 The proper solution is probably more like: the caller should pass
5365 NULL for the missing information, and this function should learn to
5368 new_text_base_sha1_checksum = NULL;
5369 new_text_base_md5_checksum = NULL;
5372 /* For added files without NEW_CONTENTS, then generate the working file
5373 from the provided "pristine" contents. */
5374 if (new_contents == NULL && copyfrom_url == NULL)
5375 source_abspath = tmp_text_base_abspath;
5378 svn_boolean_t record_fileinfo;
5380 /* If new contents were provided, then we do NOT want to record the
5381 file information. We assume the new contents do not match the
5382 "proper" values for RECORDED_SIZE and RECORDED_TIME. */
5383 record_fileinfo = (new_contents == NULL);
5385 /* Install the working copy file (with appropriate translation) from
5386 the appropriate source. SOURCE_ABSPATH will be NULL, indicating an
5387 installation from the pristine (available for copied/moved files),
5388 or it will specify a temporary file where we placed a "pristine"
5389 (for an added file) or a detranslated local-mods file. */
5390 SVN_ERR(svn_wc__wq_build_file_install(&work_item,
5393 FALSE /* use_commit_times */,
5396 all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool);
5398 /* If we installed from somewhere besides the official pristine, then
5399 it is a temporary file, which needs to be removed. */
5400 if (source_abspath != NULL)
5402 SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, local_abspath,
5405 all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool);
5409 /* ### ideally, we would have a single DB operation, and queue the work
5410 ### items on that. for now, we'll queue them with the second call. */
5412 SVN_ERR(svn_wc__db_op_copy_file(db, local_abspath,
5417 original_repos_relpath,
5418 original_repos_relpath ? repos_root_url
5420 original_repos_relpath ? repos_uuid : NULL,
5422 new_text_base_sha1_checksum,
5425 FALSE /* is_move */,
5426 NULL /* conflict */,
5430 return svn_error_trace(svn_wc__wq_run(db, dir_abspath,
5431 cancel_func, cancel_baton,
5436 svn_wc__complete_directory_add(svn_wc_context_t *wc_ctx,
5437 const char *local_abspath,
5438 apr_hash_t *new_original_props,
5439 const char *copyfrom_url,
5440 svn_revnum_t copyfrom_rev,
5441 apr_pool_t *scratch_pool)
5443 svn_wc__db_status_t status;
5444 svn_node_kind_t kind;
5445 const char *original_repos_relpath;
5446 const char *original_root_url;
5447 const char *original_uuid;
5448 svn_boolean_t had_props;
5449 svn_boolean_t props_mod;
5451 svn_revnum_t original_revision;
5452 svn_revnum_t changed_rev;
5453 apr_time_t changed_date;
5454 const char *changed_author;
5456 SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
5457 NULL, NULL, NULL, NULL, NULL,
5458 &original_repos_relpath, &original_root_url,
5459 &original_uuid, &original_revision, NULL, NULL,
5460 NULL, NULL, NULL, NULL, &had_props, &props_mod,
5462 wc_ctx->db, local_abspath,
5463 scratch_pool, scratch_pool));
5465 if (status != svn_wc__db_status_added
5466 || kind != svn_node_dir
5469 || !original_repos_relpath)
5471 return svn_error_createf(
5472 SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
5473 _("'%s' is not an unmodified copied directory"),
5474 svn_dirent_local_style(local_abspath, scratch_pool));
5476 if (original_revision != copyfrom_rev
5477 || strcmp(copyfrom_url,
5478 svn_path_url_add_component2(original_root_url,
5479 original_repos_relpath,
5482 return svn_error_createf(
5483 SVN_ERR_WC_COPYFROM_PATH_NOT_FOUND, NULL,
5484 _("Copyfrom '%s' doesn't match original location of '%s'"),
5486 svn_dirent_local_style(local_abspath, scratch_pool));
5490 apr_array_header_t *regular_props;
5491 apr_array_header_t *entry_props;
5493 SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(new_original_props,
5495 &entry_props, NULL, ®ular_props,
5498 /* Put regular props back into a hash table. */
5499 new_original_props = svn_prop_array_to_hash(regular_props, scratch_pool);
5501 /* Get the change_* info from the entry props. */
5502 SVN_ERR(accumulate_last_change(&changed_rev,
5505 entry_props, scratch_pool, scratch_pool));
5508 return svn_error_trace(
5509 svn_wc__db_op_copy_dir(wc_ctx->db, local_abspath,
5511 changed_rev, changed_date, changed_author,
5512 original_repos_relpath, original_root_url,
5513 original_uuid, original_revision,
5514 NULL /* children */,
5515 FALSE /* is_move */,
5517 NULL /* conflict */,
5518 NULL /* work_items */,