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 if it
1016 already created by lazy_open_target(). */
1017 if (hb->new_text_base_tmp_abspath)
1019 svn_error_clear(svn_io_remove_file2(hb->new_text_base_tmp_abspath,
1025 /* Tell the file baton about the new text base's checksums. */
1026 fb->new_text_base_md5_checksum =
1027 svn_checksum__from_digest_md5(hb->new_text_base_md5_digest, fb->pool);
1028 fb->new_text_base_sha1_checksum =
1029 svn_checksum_dup(hb->new_text_base_sha1_checksum, fb->pool);
1031 /* Store the new pristine text in the pristine store now. Later, in a
1032 single transaction we will update the BASE_NODE to include a
1033 reference to this pristine text's checksum. */
1034 SVN_ERR(svn_wc__db_pristine_install(db, hb->new_text_base_tmp_abspath,
1035 fb->new_text_base_sha1_checksum,
1036 fb->new_text_base_md5_checksum,
1040 svn_pool_destroy(hb->pool);
1046 /* Find the last-change info within ENTRY_PROPS, and return then in the
1047 CHANGED_* parameters. Each parameter will be initialized to its "none"
1048 value, and will contain the relavent info if found.
1050 CHANGED_AUTHOR will be allocated in RESULT_POOL. SCRATCH_POOL will be
1051 used for some temporary allocations.
1053 static svn_error_t *
1054 accumulate_last_change(svn_revnum_t *changed_rev,
1055 apr_time_t *changed_date,
1056 const char **changed_author,
1057 const apr_array_header_t *entry_props,
1058 apr_pool_t *result_pool,
1059 apr_pool_t *scratch_pool)
1063 *changed_rev = SVN_INVALID_REVNUM;
1065 *changed_author = NULL;
1067 for (i = 0; i < entry_props->nelts; ++i)
1069 const svn_prop_t *prop = &APR_ARRAY_IDX(entry_props, i, svn_prop_t);
1071 /* A prop value of NULL means the information was not
1072 available. We don't remove this field from the entries
1073 file; we have convention just leave it empty. So let's
1074 just skip those entry props that have no values. */
1078 if (! strcmp(prop->name, SVN_PROP_ENTRY_LAST_AUTHOR))
1079 *changed_author = apr_pstrdup(result_pool, prop->value->data);
1080 else if (! strcmp(prop->name, SVN_PROP_ENTRY_COMMITTED_REV))
1083 SVN_ERR(svn_cstring_atoi64(&rev, prop->value->data));
1084 *changed_rev = (svn_revnum_t)rev;
1086 else if (! strcmp(prop->name, SVN_PROP_ENTRY_COMMITTED_DATE))
1087 SVN_ERR(svn_time_from_cstring(changed_date, prop->value->data,
1090 /* Starting with Subversion 1.7 we ignore the SVN_PROP_ENTRY_UUID
1094 return SVN_NO_ERROR;
1098 /* Join ADD_PATH to BASE_PATH. If ADD_PATH is absolute, or if any ".."
1099 * component of it resolves to a path above BASE_PATH, then return
1100 * SVN_ERR_WC_OBSTRUCTED_UPDATE.
1102 * This is to prevent the situation where the repository contains,
1103 * say, "..\nastyfile". Although that's perfectly legal on some
1104 * systems, when checked out onto Win32 it would cause "nastyfile" to
1105 * be created in the parent of the current edit directory.
1107 * (http://cve.mitre.org/cgi-bin/cvename.cgi?name=2007-3846)
1109 static svn_error_t *
1110 path_join_under_root(const char **result_path,
1111 const char *base_path,
1112 const char *add_path,
1115 svn_boolean_t under_root;
1117 SVN_ERR(svn_dirent_is_under_root(&under_root,
1118 result_path, base_path, add_path, pool));
1122 return svn_error_createf(
1123 SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
1124 _("Path '%s' is not in the working copy"),
1125 svn_dirent_local_style(svn_dirent_join(base_path, add_path, pool),
1129 /* This catches issue #3288 */
1130 if (strcmp(add_path, svn_dirent_basename(*result_path, NULL)) != 0)
1132 return svn_error_createf(
1133 SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
1134 _("'%s' is not valid as filename in directory '%s'"),
1135 svn_dirent_local_style(add_path, pool),
1136 svn_dirent_local_style(base_path, pool));
1139 return SVN_NO_ERROR;
1143 /*** The callbacks we'll plug into an svn_delta_editor_t structure. ***/
1145 /* An svn_delta_editor_t function. */
1146 static svn_error_t *
1147 set_target_revision(void *edit_baton,
1148 svn_revnum_t target_revision,
1151 struct edit_baton *eb = edit_baton;
1153 *(eb->target_revision) = target_revision;
1154 return SVN_NO_ERROR;
1157 static svn_error_t *
1158 check_tree_conflict(svn_skel_t **pconflict,
1159 struct edit_baton *eb,
1160 const char *local_abspath,
1161 svn_wc__db_status_t working_status,
1162 svn_boolean_t exists_in_repos,
1163 svn_node_kind_t expected_kind,
1164 svn_wc_conflict_action_t action,
1165 apr_pool_t *result_pool,
1166 apr_pool_t *scratch_pool);
1168 /* An svn_delta_editor_t function. */
1169 static svn_error_t *
1170 open_root(void *edit_baton,
1171 svn_revnum_t base_revision, /* This is ignored in co */
1175 struct edit_baton *eb = edit_baton;
1176 struct dir_baton *db;
1177 svn_boolean_t already_conflicted, conflict_ignored;
1179 svn_wc__db_status_t status;
1180 svn_wc__db_status_t base_status;
1181 svn_node_kind_t kind;
1182 svn_boolean_t have_work;
1184 /* Note that something interesting is actually happening in this
1186 eb->root_opened = TRUE;
1188 SVN_ERR(make_dir_baton(&db, NULL, eb, NULL, FALSE, pool));
1191 err = already_in_a_tree_conflict(&already_conflicted, &conflict_ignored,
1192 eb->db, db->local_abspath, pool);
1196 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
1197 return svn_error_trace(err);
1199 svn_error_clear(err);
1200 already_conflicted = conflict_ignored = FALSE;
1202 else if (already_conflicted)
1204 /* Record a skip of both the anchor and target in the skipped tree
1205 as the anchor itself might not be updated */
1206 SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
1207 SVN_ERR(remember_skipped_tree(eb, eb->target_abspath, pool));
1209 db->skip_this = TRUE;
1210 db->already_notified = TRUE;
1212 /* Notify that we skipped the target, while we actually skipped
1214 do_notification(eb, eb->target_abspath, svn_node_unknown,
1215 svn_wc_notify_skip_conflicted, pool);
1217 return SVN_NO_ERROR;
1221 SVN_ERR(svn_wc__db_read_info(&status, &kind, &db->old_revision,
1222 &db->old_repos_relpath, NULL, NULL,
1223 &db->changed_rev, &db->changed_date,
1224 &db->changed_author, &db->ambient_depth,
1225 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1226 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1227 NULL, NULL, &have_work,
1228 eb->db, db->local_abspath,
1231 if (conflict_ignored)
1233 db->shadowed = TRUE;
1237 const char *move_src_root_abspath;
1239 SVN_ERR(svn_wc__db_base_moved_to(NULL, NULL, &move_src_root_abspath,
1240 NULL, eb->db, db->local_abspath,
1242 if (move_src_root_abspath || *eb->target_basename == '\0')
1243 SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL,
1245 &db->old_repos_relpath, NULL, NULL,
1246 &db->changed_rev, &db->changed_date,
1247 &db->changed_author,
1249 NULL, NULL, NULL, NULL, NULL, NULL,
1250 eb->db, db->local_abspath,
1253 if (move_src_root_abspath)
1255 /* This is an update anchored inside a move. We need to
1256 raise a move-edit tree-conflict on the move root to
1257 update the move destination. */
1258 svn_skel_t *tree_conflict = svn_wc__conflict_skel_create(pool);
1260 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
1261 tree_conflict, eb->db, move_src_root_abspath,
1262 svn_wc_conflict_reason_moved_away,
1263 svn_wc_conflict_action_edit,
1264 move_src_root_abspath, pool, pool));
1266 if (strcmp(db->local_abspath, move_src_root_abspath))
1268 /* We are raising the tree-conflict on some parent of
1269 the edit root, we won't be handling that path again
1270 so raise the conflict now. */
1271 SVN_ERR(complete_conflict(tree_conflict, eb,
1272 move_src_root_abspath,
1273 db->old_repos_relpath,
1274 db->old_revision, db->new_relpath,
1275 svn_node_dir, svn_node_dir,
1277 SVN_ERR(svn_wc__db_op_mark_conflict(eb->db,
1278 move_src_root_abspath,
1281 do_notification(eb, move_src_root_abspath, svn_node_dir,
1282 svn_wc_notify_tree_conflict, pool);
1285 db->edit_conflict = tree_conflict;
1288 db->shadowed = TRUE; /* Needed for the close_directory() on the root, to
1289 make sure it doesn't use the ACTUAL tree */
1292 base_status = status;
1294 if (*eb->target_basename == '\0')
1296 /* For an update with a NULL target, this is equivalent to open_dir(): */
1298 db->was_incomplete = (base_status == svn_wc__db_status_incomplete);
1300 /* ### TODO: Add some tree conflict and obstruction detection, etc. like
1301 open_directory() does.
1302 (or find a way to reuse that code here)
1304 ### BH 2013: I don't think we need all of the detection here, as the
1305 user explicitly asked to update this node. So we don't
1306 have to tell that it is a local replacement/delete.
1309 SVN_ERR(svn_wc__db_temp_op_start_directory_update(eb->db,
1312 *eb->target_revision,
1316 return SVN_NO_ERROR;
1320 /* ===================================================================== */
1321 /* Checking for local modifications. */
1323 /* A baton for use with modcheck_found_entry(). */
1324 typedef struct modcheck_baton_t {
1325 svn_wc__db_t *db; /* wc_db to access nodes */
1326 svn_boolean_t found_mod; /* whether a modification has been found */
1327 svn_boolean_t found_not_delete; /* Found a not-delete modification */
1330 /* An implementation of svn_wc_status_func4_t. */
1331 static svn_error_t *
1332 modcheck_callback(void *baton,
1333 const char *local_abspath,
1334 const svn_wc_status3_t *status,
1335 apr_pool_t *scratch_pool)
1337 modcheck_baton_t *mb = baton;
1339 switch (status->node_status)
1341 case svn_wc_status_normal:
1342 case svn_wc_status_incomplete:
1343 case svn_wc_status_ignored:
1344 case svn_wc_status_none:
1345 case svn_wc_status_unversioned:
1346 case svn_wc_status_external:
1349 case svn_wc_status_deleted:
1350 mb->found_mod = TRUE;
1353 case svn_wc_status_missing:
1354 case svn_wc_status_obstructed:
1355 mb->found_mod = TRUE;
1356 mb->found_not_delete = TRUE;
1357 /* Exit from the status walker: We know what we want to know */
1358 return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
1361 case svn_wc_status_added:
1362 case svn_wc_status_replaced:
1363 case svn_wc_status_modified:
1364 mb->found_mod = TRUE;
1365 mb->found_not_delete = TRUE;
1366 /* Exit from the status walker: We know what we want to know */
1367 return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
1370 return SVN_NO_ERROR;
1374 /* Set *MODIFIED to true iff there are any local modifications within the
1375 * tree rooted at LOCAL_ABSPATH, using DB. If *MODIFIED
1376 * is set to true and all the local modifications were deletes then set
1377 * *ALL_EDITS_ARE_DELETES to true, set it to false otherwise. LOCAL_ABSPATH
1378 * may be a file or a directory. */
1380 svn_wc__node_has_local_mods(svn_boolean_t *modified,
1381 svn_boolean_t *all_edits_are_deletes,
1383 const char *local_abspath,
1384 svn_cancel_func_t cancel_func,
1386 apr_pool_t *scratch_pool)
1388 modcheck_baton_t modcheck_baton = { NULL, FALSE, FALSE };
1391 modcheck_baton.db = db;
1393 /* Walk the WC tree for status with depth infinity, looking for any local
1394 * modifications. If it's a "sparse" directory, that's OK: there can be
1395 * no local mods in the pieces that aren't present in the WC. */
1397 err = svn_wc__internal_walk_status(db, local_abspath,
1399 FALSE, FALSE, FALSE, NULL,
1400 modcheck_callback, &modcheck_baton,
1401 cancel_func, cancel_baton,
1404 if (err && err->apr_err == SVN_ERR_CEASE_INVOCATION)
1405 svn_error_clear(err);
1409 *modified = modcheck_baton.found_mod;
1410 *all_edits_are_deletes = (modcheck_baton.found_mod
1411 && !modcheck_baton.found_not_delete);
1413 return SVN_NO_ERROR;
1416 /* Indicates an unset svn_wc_conflict_reason_t. */
1417 #define SVN_WC_CONFLICT_REASON_NONE (svn_wc_conflict_reason_t)(-1)
1419 /* Check whether the incoming change ACTION on FULL_PATH would conflict with
1420 * LOCAL_ABSPATH's scheduled change. If so, then raise a tree conflict with
1421 * LOCAL_ABSPATH as the victim.
1423 * The edit baton EB gives information including whether the operation is
1424 * an update or a switch.
1426 * WORKING_STATUS is the current node status of LOCAL_ABSPATH
1427 * and EXISTS_IN_REPOS specifies whether a BASE_NODE representation for exists
1428 * for this node. In that case the on disk type is compared to EXPECTED_KIND.
1430 * If a tree conflict reason was found for the incoming action, the resulting
1431 * tree conflict info is returned in *PCONFLICT. PCONFLICT must be non-NULL,
1432 * while *PCONFLICT is always overwritten.
1434 * The tree conflict is allocated in RESULT_POOL. Temporary allocations use
1437 static svn_error_t *
1438 check_tree_conflict(svn_skel_t **pconflict,
1439 struct edit_baton *eb,
1440 const char *local_abspath,
1441 svn_wc__db_status_t working_status,
1442 svn_boolean_t exists_in_repos,
1443 svn_node_kind_t expected_kind,
1444 svn_wc_conflict_action_t action,
1445 apr_pool_t *result_pool,
1446 apr_pool_t *scratch_pool)
1448 svn_wc_conflict_reason_t reason = SVN_WC_CONFLICT_REASON_NONE;
1449 svn_boolean_t modified = FALSE;
1450 svn_boolean_t all_mods_are_deletes = FALSE;
1451 const char *move_src_op_root_abspath = NULL;
1455 /* Find out if there are any local changes to this node that may
1456 * be the "reason" of a tree-conflict with the incoming "action". */
1457 switch (working_status)
1459 case svn_wc__db_status_added:
1460 case svn_wc__db_status_moved_here:
1461 case svn_wc__db_status_copied:
1462 if (!exists_in_repos)
1464 /* The node is locally added, and it did not exist before. This
1465 * is an 'update', so the local add can only conflict with an
1466 * incoming 'add'. In fact, if we receive anything else than an
1467 * svn_wc_conflict_action_add (which includes 'added',
1468 * 'copied-here' and 'moved-here') during update on a node that
1469 * did not exist before, then something is very wrong.
1470 * Note that if there was no action on the node, this code
1471 * would not have been called in the first place. */
1472 SVN_ERR_ASSERT(action == svn_wc_conflict_action_add);
1474 /* Scan the addition in case our caller didn't. */
1475 if (working_status == svn_wc__db_status_added)
1476 SVN_ERR(svn_wc__db_scan_addition(&working_status, NULL, NULL,
1477 NULL, NULL, NULL, NULL,
1479 eb->db, local_abspath,
1480 scratch_pool, scratch_pool));
1482 if (working_status == svn_wc__db_status_moved_here)
1483 reason = svn_wc_conflict_reason_moved_here;
1485 reason = svn_wc_conflict_reason_added;
1489 /* The node is locally replaced but could also be moved-away. */
1490 SVN_ERR(svn_wc__db_base_moved_to(NULL, NULL, NULL,
1491 &move_src_op_root_abspath,
1492 eb->db, local_abspath,
1493 scratch_pool, scratch_pool));
1494 if (move_src_op_root_abspath)
1495 reason = svn_wc_conflict_reason_moved_away;
1497 reason = svn_wc_conflict_reason_replaced;
1502 case svn_wc__db_status_deleted:
1504 SVN_ERR(svn_wc__db_base_moved_to(NULL, NULL, NULL,
1505 &move_src_op_root_abspath,
1506 eb->db, local_abspath,
1507 scratch_pool, scratch_pool));
1508 if (move_src_op_root_abspath)
1509 reason = svn_wc_conflict_reason_moved_away;
1511 reason = svn_wc_conflict_reason_deleted;
1515 case svn_wc__db_status_incomplete:
1516 /* We used svn_wc__db_read_info(), so 'incomplete' means
1517 * - there is no node in the WORKING tree
1518 * - a BASE node is known to exist
1519 * So the node exists and is essentially 'normal'. We still need to
1520 * check prop and text mods, and those checks will retrieve the
1521 * missing information (hopefully). */
1522 case svn_wc__db_status_normal:
1523 if (action == svn_wc_conflict_action_edit)
1525 /* An edit onto a local edit or onto *no* local changes is no
1526 * tree-conflict. (It's possibly a text- or prop-conflict,
1527 * but we don't handle those here.)
1529 * Except when there is a local obstruction
1531 if (exists_in_repos)
1533 svn_node_kind_t disk_kind;
1535 SVN_ERR(svn_io_check_path(local_abspath, &disk_kind,
1538 if (disk_kind != expected_kind && disk_kind != svn_node_none)
1540 reason = svn_wc_conflict_reason_obstructed;
1545 return SVN_NO_ERROR;
1548 /* Replace is handled as delete and then specifically in
1549 add_directory() and add_file(), so we only expect deletes here */
1550 SVN_ERR_ASSERT(action == svn_wc_conflict_action_delete);
1552 /* Check if the update wants to delete or replace a locally
1556 /* Do a deep tree detection of local changes. The update editor will
1557 * not visit the subdirectories of a directory that it wants to delete.
1558 * Therefore, we need to start a separate crawl here. */
1560 SVN_ERR(svn_wc__node_has_local_mods(&modified, &all_mods_are_deletes,
1561 eb->db, local_abspath,
1562 eb->cancel_func, eb->cancel_baton,
1567 if (all_mods_are_deletes)
1568 reason = svn_wc_conflict_reason_deleted;
1570 reason = svn_wc_conflict_reason_edited;
1574 case svn_wc__db_status_server_excluded:
1575 /* Not allowed to view the node. Not allowed to report tree
1577 case svn_wc__db_status_excluded:
1578 /* Locally marked as excluded. No conflicts wanted. */
1579 case svn_wc__db_status_not_present:
1580 /* A committed delete (but parent not updated). The delete is
1581 committed, so no conflict possible during update. */
1582 return SVN_NO_ERROR;
1584 case svn_wc__db_status_base_deleted:
1585 /* An internal status. Should never show up here. */
1586 SVN_ERR_MALFUNCTION();
1591 if (reason == SVN_WC_CONFLICT_REASON_NONE)
1592 /* No conflict with the current action. */
1593 return SVN_NO_ERROR;
1596 /* Sanity checks. Note that if there was no action on the node, this function
1597 * would not have been called in the first place.*/
1598 if (reason == svn_wc_conflict_reason_edited
1599 || reason == svn_wc_conflict_reason_obstructed
1600 || reason == svn_wc_conflict_reason_deleted
1601 || reason == svn_wc_conflict_reason_moved_away
1602 || reason == svn_wc_conflict_reason_replaced)
1604 /* When the node existed before (it was locally deleted, replaced or
1605 * edited), then 'update' cannot add it "again". So it can only send
1606 * _action_edit, _delete or _replace. */
1607 if (action != svn_wc_conflict_action_edit
1608 && action != svn_wc_conflict_action_delete
1609 && action != svn_wc_conflict_action_replace)
1610 return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
1611 _("Unexpected attempt to add a node at path '%s'"),
1612 svn_dirent_local_style(local_abspath, scratch_pool));
1614 else if (reason == svn_wc_conflict_reason_added ||
1615 reason == svn_wc_conflict_reason_moved_here)
1617 /* When the node did not exist before (it was locally added),
1618 * then 'update' cannot want to modify it in any way.
1619 * It can only send _action_add. */
1620 if (action != svn_wc_conflict_action_add)
1621 return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
1622 _("Unexpected attempt to edit, delete, or replace "
1623 "a node at path '%s'"),
1624 svn_dirent_local_style(local_abspath, scratch_pool));
1629 /* A conflict was detected. Create a conflict skel to record it. */
1630 *pconflict = svn_wc__conflict_skel_create(result_pool);
1632 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(*pconflict,
1633 eb->db, local_abspath,
1636 move_src_op_root_abspath,
1637 result_pool, scratch_pool));
1639 return SVN_NO_ERROR;
1643 /* If LOCAL_ABSPATH is inside a conflicted tree and the conflict is
1644 * not a moved-away-edit conflict, set *CONFLICTED to TRUE. Otherwise
1645 * set *CONFLICTED to FALSE.
1647 static svn_error_t *
1648 already_in_a_tree_conflict(svn_boolean_t *conflicted,
1649 svn_boolean_t *ignored,
1651 const char *local_abspath,
1652 apr_pool_t *scratch_pool)
1654 const char *ancestor_abspath = local_abspath;
1655 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1657 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1659 *conflicted = *ignored = FALSE;
1663 svn_boolean_t is_wc_root;
1665 svn_pool_clear(iterpool);
1667 SVN_ERR(svn_wc__conflicted_for_update_p(conflicted, ignored, db,
1668 ancestor_abspath, TRUE,
1670 if (*conflicted || *ignored)
1673 SVN_ERR(svn_wc__db_is_wcroot(&is_wc_root, db, ancestor_abspath,
1678 ancestor_abspath = svn_dirent_dirname(ancestor_abspath, scratch_pool);
1681 svn_pool_destroy(iterpool);
1683 return SVN_NO_ERROR;
1686 /* Temporary helper until the new conflict handling is in place */
1687 static svn_error_t *
1688 node_already_conflicted(svn_boolean_t *conflicted,
1689 svn_boolean_t *conflict_ignored,
1691 const char *local_abspath,
1692 apr_pool_t *scratch_pool)
1694 SVN_ERR(svn_wc__conflicted_for_update_p(conflicted, conflict_ignored, db,
1695 local_abspath, FALSE,
1698 return SVN_NO_ERROR;
1702 /* An svn_delta_editor_t function. */
1703 static svn_error_t *
1704 delete_entry(const char *path,
1705 svn_revnum_t revision,
1709 struct dir_baton *pb = parent_baton;
1710 struct edit_baton *eb = pb->edit_baton;
1711 const char *base = svn_relpath_basename(path, NULL);
1712 const char *local_abspath;
1713 const char *repos_relpath;
1714 svn_node_kind_t kind, base_kind;
1715 svn_revnum_t old_revision;
1716 svn_boolean_t conflicted;
1717 svn_boolean_t have_work;
1718 svn_skel_t *tree_conflict = NULL;
1719 svn_wc__db_status_t status;
1720 svn_wc__db_status_t base_status;
1721 apr_pool_t *scratch_pool;
1722 svn_boolean_t deleting_target;
1723 svn_boolean_t deleting_switched;
1724 svn_boolean_t keep_as_working = FALSE;
1725 svn_boolean_t queue_deletes = TRUE;
1728 return SVN_NO_ERROR;
1730 scratch_pool = svn_pool_create(pb->pool);
1732 SVN_ERR(mark_directory_edited(pb, scratch_pool));
1734 SVN_ERR(path_join_under_root(&local_abspath, pb->local_abspath, base,
1737 deleting_target = (strcmp(local_abspath, eb->target_abspath) == 0);
1739 /* Detect obstructing working copies */
1741 svn_boolean_t is_root;
1743 SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, local_abspath,
1748 /* Just skip this node; a future update will handle it */
1749 SVN_ERR(remember_skipped_tree(eb, local_abspath, pool));
1750 do_notification(eb, local_abspath, svn_node_unknown,
1751 svn_wc_notify_update_skip_obstruction, scratch_pool);
1753 svn_pool_destroy(scratch_pool);
1755 return SVN_NO_ERROR;
1759 SVN_ERR(svn_wc__db_read_info(&status, &kind, &old_revision, &repos_relpath,
1760 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1761 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1762 &conflicted, NULL, NULL, NULL,
1763 NULL, NULL, &have_work,
1764 eb->db, local_abspath,
1765 scratch_pool, scratch_pool));
1769 base_status = status;
1773 SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, &old_revision,
1775 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1776 NULL, NULL, NULL, NULL, NULL,
1777 eb->db, local_abspath,
1778 scratch_pool, scratch_pool));
1780 if (pb->old_repos_relpath && repos_relpath)
1782 const char *expected_name;
1784 expected_name = svn_relpath_skip_ancestor(pb->old_repos_relpath,
1787 deleting_switched = (!expected_name || strcmp(expected_name, base) != 0);
1790 deleting_switched = FALSE;
1792 /* Is this path a conflict victim? */
1794 conflicted = FALSE; /* Conflict applies to WORKING */
1795 else if (conflicted)
1796 SVN_ERR(node_already_conflicted(&conflicted, NULL,
1797 eb->db, local_abspath, scratch_pool));
1800 SVN_ERR(remember_skipped_tree(eb, local_abspath, scratch_pool));
1802 do_notification(eb, local_abspath, svn_node_unknown,
1803 svn_wc_notify_skip_conflicted,
1806 svn_pool_destroy(scratch_pool);
1808 return SVN_NO_ERROR;
1812 /* Receive the remote removal of excluded/server-excluded/not present node.
1813 Do not notify, but perform the change even when the node is shadowed */
1814 if (base_status == svn_wc__db_status_not_present
1815 || base_status == svn_wc__db_status_excluded
1816 || base_status == svn_wc__db_status_server_excluded)
1818 SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath,
1819 FALSE /* keep_as_working */,
1820 FALSE /* queue_deletes */,
1821 FALSE /* remove_locks */,
1822 SVN_INVALID_REVNUM /* not_present_rev */,
1826 if (deleting_target)
1827 eb->target_deleted = TRUE;
1829 svn_pool_destroy(scratch_pool);
1831 return SVN_NO_ERROR;
1834 /* Is this path the victim of a newly-discovered tree conflict? If so,
1835 * remember it and notify the client. Then (if it was existing and
1836 * modified), re-schedule the node to be added back again, as a (modified)
1837 * copy of the previous base version. */
1839 /* Check for conflicts only when we haven't already recorded
1840 * a tree-conflict on a parent node. */
1841 if (!pb->shadowed && !pb->edit_obstructed)
1843 SVN_ERR(check_tree_conflict(&tree_conflict, eb, local_abspath,
1845 (kind == svn_node_dir)
1848 svn_wc_conflict_action_delete,
1849 pb->pool, scratch_pool));
1852 queue_deletes = FALSE; /* There is no in-wc representation */
1854 if (tree_conflict != NULL)
1856 svn_wc_conflict_reason_t reason;
1857 /* When we raise a tree conflict on a node, we don't want to mark the
1858 * node as skipped, to allow a replacement to continue doing at least
1859 * a bit of its work (possibly adding a not present node, for the
1861 if (!pb->deletion_conflicts)
1862 pb->deletion_conflicts = apr_hash_make(pb->pool);
1864 svn_hash_sets(pb->deletion_conflicts, apr_pstrdup(pb->pool, base),
1867 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
1868 eb->db, local_abspath,
1870 scratch_pool, scratch_pool));
1872 if (reason == svn_wc_conflict_reason_edited
1873 || reason == svn_wc_conflict_reason_obstructed)
1875 /* The item exists locally and has some sort of local mod.
1876 * It no longer exists in the repository at its target URL@REV.
1878 * To prepare the "accept mine" resolution for the tree conflict,
1879 * we must schedule the existing content for re-addition as a copy
1880 * of what it was, but with its local modifications preserved. */
1881 keep_as_working = TRUE;
1883 /* Fall through to remove the BASE_NODEs properly, with potentially
1884 keeping a not-present marker */
1886 else if (reason == svn_wc_conflict_reason_deleted
1887 || reason == svn_wc_conflict_reason_moved_away
1888 || reason == svn_wc_conflict_reason_replaced)
1890 /* The item does not exist locally because it was already shadowed.
1891 * We must complete the deletion, leaving the tree conflict info
1892 * as the only difference from a normal deletion. */
1894 /* Fall through to the normal "delete" code path. */
1897 SVN_ERR_MALFUNCTION(); /* other reasons are not expected here */
1900 SVN_ERR(complete_conflict(tree_conflict, eb, local_abspath, repos_relpath,
1902 (kind == svn_node_dir)
1906 pb->pool, scratch_pool));
1908 /* Issue a wq operation to delete the BASE_NODE data and to delete actual
1909 nodes based on that from disk, but leave any WORKING_NODEs on disk.
1911 Local modifications are already turned into copies at this point.
1913 If the thing being deleted is the *target* of this update, then
1914 we need to recreate a 'deleted' entry, so that the parent can give
1915 accurate reports about itself in the future. */
1916 if (! deleting_target && ! deleting_switched)
1918 /* Delete, and do not leave a not-present node. */
1919 SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath,
1920 keep_as_working, queue_deletes, FALSE,
1921 SVN_INVALID_REVNUM /* not_present_rev */,
1922 tree_conflict, NULL,
1927 /* Delete, leaving a not-present node. */
1928 SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath,
1929 keep_as_working, queue_deletes, FALSE,
1930 *eb->target_revision,
1931 tree_conflict, NULL,
1933 if (deleting_target)
1934 eb->target_deleted = TRUE;
1937 /* Don't remove the not-present marker at the final bump */
1938 SVN_ERR(remember_skipped_tree(eb, local_abspath, pool));
1942 SVN_ERR(svn_wc__wq_run(eb->db, pb->local_abspath,
1943 eb->cancel_func, eb->cancel_baton,
1949 if (eb->conflict_func)
1950 SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, local_abspath,
1952 NULL /* merge_options */,
1958 do_notification(eb, local_abspath, svn_node_unknown,
1959 svn_wc_notify_tree_conflict, scratch_pool);
1963 svn_wc_notify_action_t action = svn_wc_notify_update_delete;
1964 svn_node_kind_t node_kind;
1966 if (pb->shadowed || pb->edit_obstructed)
1967 action = svn_wc_notify_update_shadowed_delete;
1969 if (kind == svn_node_dir)
1970 node_kind = svn_node_dir;
1972 node_kind = svn_node_file;
1974 do_notification(eb, local_abspath, node_kind, action, scratch_pool);
1977 svn_pool_destroy(scratch_pool);
1979 return SVN_NO_ERROR;
1982 /* An svn_delta_editor_t function. */
1983 static svn_error_t *
1984 add_directory(const char *path,
1986 const char *copyfrom_path,
1987 svn_revnum_t copyfrom_rev,
1991 struct dir_baton *pb = parent_baton;
1992 struct edit_baton *eb = pb->edit_baton;
1993 struct dir_baton *db;
1994 svn_node_kind_t kind;
1995 svn_wc__db_status_t status;
1996 svn_node_kind_t wc_kind;
1997 svn_boolean_t conflicted;
1998 svn_boolean_t conflict_ignored = FALSE;
1999 svn_boolean_t versioned_locally_and_present;
2000 svn_skel_t *tree_conflict = NULL;
2003 SVN_ERR_ASSERT(! (copyfrom_path || SVN_IS_VALID_REVNUM(copyfrom_rev)));
2005 SVN_ERR(make_dir_baton(&db, path, eb, pb, TRUE, pool));
2009 return SVN_NO_ERROR;
2011 SVN_ERR(mark_directory_edited(db, pool));
2013 if (strcmp(eb->target_abspath, db->local_abspath) == 0)
2015 /* The target of the edit is being added, give it the requested
2016 depth of the edit (but convert svn_depth_unknown to
2017 svn_depth_infinity). */
2018 db->ambient_depth = (eb->requested_depth == svn_depth_unknown)
2019 ? svn_depth_infinity : eb->requested_depth;
2021 else if (eb->requested_depth == svn_depth_immediates
2022 || (eb->requested_depth == svn_depth_unknown
2023 && pb->ambient_depth == svn_depth_immediates))
2025 db->ambient_depth = svn_depth_empty;
2029 db->ambient_depth = svn_depth_infinity;
2032 /* It may not be named the same as the administrative directory. */
2033 if (svn_wc_is_adm_dir(db->name, pool))
2034 return svn_error_createf(
2035 SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
2036 _("Failed to add directory '%s': object of the same name as the "
2037 "administrative directory"),
2038 svn_dirent_local_style(db->local_abspath, pool));
2040 SVN_ERR(svn_io_check_path(db->local_abspath, &kind, db->pool));
2042 err = svn_wc__db_read_info(&status, &wc_kind, NULL, NULL, NULL, NULL, NULL,
2043 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
2044 NULL, NULL, NULL, NULL, NULL,
2045 &conflicted, NULL, NULL, NULL, NULL, NULL, NULL,
2046 eb->db, db->local_abspath, db->pool, db->pool);
2049 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
2050 return svn_error_trace(err);
2052 svn_error_clear(err);
2053 wc_kind = svn_node_unknown;
2054 status = svn_wc__db_status_normal;
2057 versioned_locally_and_present = FALSE;
2059 else if (wc_kind == svn_node_dir
2060 && status == svn_wc__db_status_normal)
2062 /* !! We found the root of a separate working copy obstructing the wc !!
2064 If the directory would be part of our own working copy then
2065 we wouldn't have been called as an add_directory().
2067 The only thing we can do is add a not-present node, to allow
2068 a future update to bring in the new files when the problem is
2069 resolved. Note that svn_wc__db_base_add_not_present_node()
2070 explicitly adds the node into the parent's node database. */
2072 SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db, db->local_abspath,
2076 *eb->target_revision,
2081 SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
2082 db->skip_this = TRUE;
2083 db->already_notified = TRUE;
2085 do_notification(eb, db->local_abspath, svn_node_dir,
2086 svn_wc_notify_update_skip_obstruction, pool);
2088 return SVN_NO_ERROR;
2090 else if (status == svn_wc__db_status_normal
2091 && (wc_kind == svn_node_file
2092 || wc_kind == svn_node_symlink))
2094 /* We found a file external occupating the place we need in BASE.
2096 We can't add a not-present node in this case as that would overwrite
2097 the file external. Luckily the file external itself stops us from
2098 forgetting a child of this parent directory like an obstructing
2101 The reason we get here is that the adm crawler doesn't report
2105 SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
2106 db->skip_this = TRUE;
2107 db->already_notified = TRUE;
2109 do_notification(eb, db->local_abspath, svn_node_file,
2110 svn_wc_notify_update_skip_obstruction, pool);
2112 return SVN_NO_ERROR;
2114 else if (wc_kind == svn_node_unknown)
2115 versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */
2117 versioned_locally_and_present = IS_NODE_PRESENT(status);
2119 /* Is this path a conflict victim? */
2122 if (pb->deletion_conflicts)
2123 tree_conflict = svn_hash_gets(pb->deletion_conflicts, db->name);
2127 svn_wc_conflict_reason_t reason;
2128 const char *move_src_op_root_abspath;
2129 /* So this deletion wasn't just a deletion, it is actually a
2130 replacement. Let's install a better tree conflict. */
2132 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL,
2133 &move_src_op_root_abspath,
2137 db->pool, db->pool));
2139 tree_conflict = svn_wc__conflict_skel_create(db->pool);
2141 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
2143 eb->db, db->local_abspath,
2144 reason, svn_wc_conflict_action_replace,
2145 move_src_op_root_abspath,
2146 db->pool, db->pool));
2148 /* And now stop checking for conflicts here and just perform
2149 a shadowed update */
2150 db->edit_conflict = tree_conflict; /* Cache for close_directory */
2151 tree_conflict = NULL; /* No direct notification */
2152 db->shadowed = TRUE; /* Just continue */
2153 conflicted = FALSE; /* No skip */
2156 SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
2157 eb->db, db->local_abspath, pool));
2160 /* Now the "usual" behaviour if already conflicted. Skip it. */
2163 /* Record this conflict so that its descendants are skipped silently. */
2164 SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
2166 db->skip_this = TRUE;
2167 db->already_notified = TRUE;
2169 /* We skip this node, but once the update completes the parent node will
2170 be updated to the new revision. So a future recursive update of the
2171 parent will not bring in this new node as the revision of the parent
2172 describes to the repository that all children are available.
2174 To resolve this problem, we add a not-present node to allow bringing
2175 the node in once this conflict is resolved.
2177 Note that we can safely assume that no present base node exists,
2178 because then we would not have received an add_directory.
2180 SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db, db->local_abspath,
2184 *eb->target_revision,
2189 /* ### TODO: Also print victim_path in the skip msg. */
2190 do_notification(eb, db->local_abspath, svn_node_dir,
2191 svn_wc_notify_skip_conflicted, pool);
2192 return SVN_NO_ERROR;
2194 else if (conflict_ignored)
2196 db->shadowed = TRUE;
2201 /* Nothing to check; does not and will not exist in working copy */
2203 else if (versioned_locally_and_present)
2205 /* What to do with a versioned or schedule-add dir:
2207 A dir already added without history is OK. Set add_existed
2208 so that user notification is delayed until after any prop
2209 conflicts have been found.
2211 An existing versioned dir is an error. In the future we may
2212 relax this restriction and simply update such dirs.
2214 A dir added with history is a tree conflict. */
2216 svn_boolean_t local_is_non_dir;
2217 svn_wc__db_status_t add_status = svn_wc__db_status_normal;
2219 /* Is the local add a copy? */
2220 if (status == svn_wc__db_status_added)
2221 SVN_ERR(svn_wc__db_scan_addition(&add_status, NULL, NULL, NULL, NULL,
2222 NULL, NULL, NULL, NULL,
2223 eb->db, db->local_abspath,
2227 /* Is there *something* that is not a dir? */
2228 local_is_non_dir = (wc_kind != svn_node_dir
2229 && status != svn_wc__db_status_deleted);
2231 /* Do tree conflict checking if
2232 * - if there is a local copy.
2233 * - if this is a switch operation
2234 * - the node kinds mismatch
2236 * During switch, local adds at the same path as incoming adds get
2237 * "lost" in that switching back to the original will no longer have the
2238 * local add. So switch always alerts the user with a tree conflict. */
2239 if (!eb->adds_as_modification
2241 || add_status != svn_wc__db_status_added)
2243 SVN_ERR(check_tree_conflict(&tree_conflict, eb,
2245 status, FALSE, svn_node_none,
2246 svn_wc_conflict_action_add,
2250 if (tree_conflict == NULL)
2251 db->add_existed = TRUE; /* Take over WORKING */
2253 db->shadowed = TRUE; /* Only update BASE */
2255 else if (kind != svn_node_none)
2257 /* There's an unversioned node at this path. */
2258 db->obstruction_found = TRUE;
2260 /* Unversioned, obstructing dirs are handled by prop merge/conflict,
2261 * if unversioned obstructions are allowed. */
2262 if (! (kind == svn_node_dir && eb->allow_unver_obstructions))
2264 /* Bring in the node as deleted */ /* ### Obstructed Conflict */
2265 db->shadowed = TRUE;
2267 /* Mark a conflict */
2268 tree_conflict = svn_wc__conflict_skel_create(db->pool);
2270 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
2272 eb->db, db->local_abspath,
2273 svn_wc_conflict_reason_unversioned,
2274 svn_wc_conflict_action_add, NULL,
2276 db->edit_conflict = tree_conflict;
2281 SVN_ERR(complete_conflict(tree_conflict, eb, db->local_abspath,
2282 db->old_repos_relpath, db->old_revision,
2288 SVN_ERR(svn_wc__db_base_add_incomplete_directory(
2289 eb->db, db->local_abspath,
2293 *eb->target_revision,
2295 (db->shadowed && db->obstruction_found),
2297 && status == svn_wc__db_status_added),
2298 tree_conflict, NULL,
2301 /* Make sure there is a real directory at LOCAL_ABSPATH, unless we are just
2304 SVN_ERR(svn_wc__ensure_directory(db->local_abspath, pool));
2306 if (tree_conflict != NULL)
2308 if (eb->conflict_func)
2309 SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, db->local_abspath,
2311 NULL /* merge_options */,
2318 db->already_notified = TRUE;
2319 do_notification(eb, db->local_abspath, svn_node_dir,
2320 svn_wc_notify_tree_conflict, pool);
2324 /* If this add was obstructed by dir scheduled for addition without
2325 history let close_directory() handle the notification because there
2326 might be properties to deal with. If PATH was added inside a locally
2327 deleted tree, then suppress notification, a tree conflict was already
2329 if (eb->notify_func && !db->already_notified && !db->add_existed)
2331 svn_wc_notify_action_t action;
2334 action = svn_wc_notify_update_shadowed_add;
2335 else if (db->obstruction_found || db->add_existed)
2336 action = svn_wc_notify_exists;
2338 action = svn_wc_notify_update_add;
2340 db->already_notified = TRUE;
2342 do_notification(eb, db->local_abspath, svn_node_dir, action, pool);
2345 return SVN_NO_ERROR;
2348 /* An svn_delta_editor_t function. */
2349 static svn_error_t *
2350 open_directory(const char *path,
2352 svn_revnum_t base_revision,
2356 struct dir_baton *db, *pb = parent_baton;
2357 struct edit_baton *eb = pb->edit_baton;
2358 svn_boolean_t have_work;
2359 svn_boolean_t conflicted;
2360 svn_boolean_t conflict_ignored = FALSE;
2361 svn_skel_t *tree_conflict = NULL;
2362 svn_wc__db_status_t status, base_status;
2363 svn_node_kind_t wc_kind;
2365 SVN_ERR(make_dir_baton(&db, path, eb, pb, FALSE, pool));
2369 return SVN_NO_ERROR;
2371 /* Detect obstructing working copies */
2373 svn_boolean_t is_root;
2375 SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, db->local_abspath,
2380 /* Just skip this node; a future update will handle it */
2381 SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
2382 db->skip_this = TRUE;
2383 db->already_notified = TRUE;
2385 do_notification(eb, db->local_abspath, svn_node_dir,
2386 svn_wc_notify_update_skip_obstruction, pool);
2388 return SVN_NO_ERROR;
2392 /* We should have a write lock on every directory touched. */
2393 SVN_ERR(svn_wc__write_check(eb->db, db->local_abspath, pool));
2395 SVN_ERR(svn_wc__db_read_info(&status, &wc_kind, &db->old_revision,
2396 &db->old_repos_relpath, NULL, NULL,
2397 &db->changed_rev, &db->changed_date,
2398 &db->changed_author, &db->ambient_depth,
2399 NULL, NULL, NULL, NULL,
2400 NULL, NULL, NULL, NULL, NULL, NULL,
2401 &conflicted, NULL, NULL, NULL,
2402 NULL, NULL, &have_work,
2403 eb->db, db->local_abspath,
2407 base_status = status;
2409 SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &db->old_revision,
2410 &db->old_repos_relpath, NULL, NULL,
2411 &db->changed_rev, &db->changed_date,
2412 &db->changed_author, &db->ambient_depth,
2413 NULL, NULL, NULL, NULL, NULL, NULL,
2414 eb->db, db->local_abspath,
2417 db->was_incomplete = (base_status == svn_wc__db_status_incomplete);
2419 /* Is this path a conflict victim? */
2421 conflicted = FALSE; /* Conflict applies to WORKING */
2422 else if (conflicted)
2423 SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
2424 eb->db, db->local_abspath, pool));
2427 SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
2429 db->skip_this = TRUE;
2430 db->already_notified = TRUE;
2432 do_notification(eb, db->local_abspath, svn_node_unknown,
2433 svn_wc_notify_skip_conflicted, pool);
2435 return SVN_NO_ERROR;
2437 else if (conflict_ignored)
2439 db->shadowed = TRUE;
2442 /* Is this path a fresh tree conflict victim? If so, skip the tree
2443 with one notification. */
2445 /* Check for conflicts only when we haven't already recorded
2446 * a tree-conflict on a parent node. */
2448 SVN_ERR(check_tree_conflict(&tree_conflict, eb, db->local_abspath,
2449 status, TRUE, svn_node_dir,
2450 svn_wc_conflict_action_edit,
2453 /* Remember the roots of any locally deleted trees. */
2454 if (tree_conflict != NULL)
2456 svn_wc_conflict_reason_t reason;
2457 db->edit_conflict = tree_conflict;
2458 /* Other modifications wouldn't be a tree conflict */
2460 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
2461 eb->db, db->local_abspath,
2463 db->pool, db->pool));
2464 SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_deleted
2465 || reason == svn_wc_conflict_reason_moved_away
2466 || reason == svn_wc_conflict_reason_replaced
2467 || reason == svn_wc_conflict_reason_obstructed);
2469 /* Continue updating BASE */
2470 if (reason == svn_wc_conflict_reason_obstructed)
2471 db->edit_obstructed = TRUE;
2473 db->shadowed = TRUE;
2476 /* Mark directory as being at target_revision and URL, but incomplete. */
2477 SVN_ERR(svn_wc__db_temp_op_start_directory_update(eb->db, db->local_abspath,
2479 *eb->target_revision,
2482 return SVN_NO_ERROR;
2486 /* An svn_delta_editor_t function. */
2487 static svn_error_t *
2488 change_dir_prop(void *dir_baton,
2490 const svn_string_t *value,
2493 svn_prop_t *propchange;
2494 struct dir_baton *db = dir_baton;
2497 return SVN_NO_ERROR;
2499 propchange = apr_array_push(db->propchanges);
2500 propchange->name = apr_pstrdup(db->pool, name);
2501 propchange->value = value ? svn_string_dup(value, db->pool) : NULL;
2503 if (!db->edited && svn_property_kind2(name) == svn_prop_regular_kind)
2504 SVN_ERR(mark_directory_edited(db, pool));
2506 return SVN_NO_ERROR;
2509 /* If any of the svn_prop_t objects in PROPCHANGES represents a change
2510 to the SVN_PROP_EXTERNALS property, return that change, else return
2511 null. If PROPCHANGES contains more than one such change, return
2513 static const svn_prop_t *
2514 externals_prop_changed(const apr_array_header_t *propchanges)
2518 for (i = 0; i < propchanges->nelts; i++)
2520 const svn_prop_t *p = &(APR_ARRAY_IDX(propchanges, i, svn_prop_t));
2521 if (strcmp(p->name, SVN_PROP_EXTERNALS) == 0)
2530 /* An svn_delta_editor_t function. */
2531 static svn_error_t *
2532 close_directory(void *dir_baton,
2535 struct dir_baton *db = dir_baton;
2536 struct edit_baton *eb = db->edit_baton;
2537 svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
2538 apr_array_header_t *entry_prop_changes;
2539 apr_array_header_t *dav_prop_changes;
2540 apr_array_header_t *regular_prop_changes;
2541 apr_hash_t *base_props;
2542 apr_hash_t *actual_props;
2543 apr_hash_t *new_base_props = NULL;
2544 apr_hash_t *new_actual_props = NULL;
2545 svn_revnum_t new_changed_rev = SVN_INVALID_REVNUM;
2546 apr_time_t new_changed_date = 0;
2547 const char *new_changed_author = NULL;
2548 apr_pool_t *scratch_pool = db->pool;
2549 svn_skel_t *all_work_items = NULL;
2550 svn_skel_t *conflict_skel = NULL;
2552 /* Skip if we're in a conflicted tree. */
2555 /* Allow the parent to complete its update. */
2556 SVN_ERR(maybe_release_dir_info(db));
2558 return SVN_NO_ERROR;
2562 conflict_skel = db->edit_conflict;
2564 SVN_ERR(svn_categorize_props(db->propchanges, &entry_prop_changes,
2565 &dav_prop_changes, ®ular_prop_changes, pool));
2567 /* Fetch the existing properties. */
2568 if ((!db->adding_dir || db->add_existed)
2571 SVN_ERR(svn_wc__get_actual_props(&actual_props,
2572 eb->db, db->local_abspath,
2573 scratch_pool, scratch_pool));
2576 actual_props = apr_hash_make(pool);
2578 if (db->add_existed)
2580 /* This node already exists. Grab the current pristine properties. */
2581 SVN_ERR(svn_wc__db_read_pristine_props(&base_props,
2582 eb->db, db->local_abspath,
2583 scratch_pool, scratch_pool));
2585 else if (!db->adding_dir)
2587 /* Get the BASE properties for proper merging. */
2588 SVN_ERR(svn_wc__db_base_get_props(&base_props,
2589 eb->db, db->local_abspath,
2590 scratch_pool, scratch_pool));
2593 base_props = apr_hash_make(pool);
2595 /* An incomplete directory might have props which were supposed to be
2596 deleted but weren't. Because the server sent us all the props we're
2597 supposed to have, any previous base props not in this list must be
2598 deleted (issue #1672). */
2599 if (db->was_incomplete)
2602 apr_hash_t *props_to_delete;
2603 apr_hash_index_t *hi;
2605 /* In a copy of the BASE props, remove every property that we see an
2606 incoming change for. The remaining unmentioned properties are those
2607 which need to be deleted. */
2608 props_to_delete = apr_hash_copy(pool, base_props);
2609 for (i = 0; i < regular_prop_changes->nelts; i++)
2611 const svn_prop_t *prop;
2612 prop = &APR_ARRAY_IDX(regular_prop_changes, i, svn_prop_t);
2613 svn_hash_sets(props_to_delete, prop->name, NULL);
2616 /* Add these props to the incoming propchanges (in
2617 * regular_prop_changes). */
2618 for (hi = apr_hash_first(pool, props_to_delete);
2620 hi = apr_hash_next(hi))
2622 const char *propname = svn__apr_hash_index_key(hi);
2623 svn_prop_t *prop = apr_array_push(regular_prop_changes);
2625 /* Record a deletion for PROPNAME. */
2626 prop->name = propname;
2631 /* If this directory has property changes stored up, now is the time
2632 to deal with them. */
2633 if (regular_prop_changes->nelts)
2635 /* If recording traversal info, then see if the
2636 SVN_PROP_EXTERNALS property on this directory changed,
2637 and record before and after for the change. */
2638 if (eb->external_func)
2640 const svn_prop_t *change
2641 = externals_prop_changed(regular_prop_changes);
2645 const svn_string_t *new_val_s = change->value;
2646 const svn_string_t *old_val_s;
2648 old_val_s = svn_hash_gets(base_props, SVN_PROP_EXTERNALS);
2650 if ((new_val_s == NULL) && (old_val_s == NULL))
2651 ; /* No value before, no value after... so do nothing. */
2652 else if (new_val_s && old_val_s
2653 && (svn_string_compare(old_val_s, new_val_s)))
2654 ; /* Value did not change... so do nothing. */
2655 else if (old_val_s || new_val_s)
2656 /* something changed, record the change */
2658 SVN_ERR((eb->external_func)(
2671 /* We don't have a relevant actual row, but we need actual properties
2672 to allow property merging without conflicts. */
2674 actual_props = apr_hash_make(scratch_pool);
2676 actual_props = base_props;
2679 /* Merge pending properties. */
2680 new_base_props = svn_prop__patch(base_props, regular_prop_changes,
2682 SVN_ERR_W(svn_wc__merge_props(&conflict_skel,
2687 NULL /* use baseprops */,
2690 regular_prop_changes,
2693 _("Couldn't do property merge"));
2694 /* After a (not-dry-run) merge, we ALWAYS have props to save. */
2695 SVN_ERR_ASSERT(new_base_props != NULL && new_actual_props != NULL);
2698 SVN_ERR(accumulate_last_change(&new_changed_rev, &new_changed_date,
2699 &new_changed_author, entry_prop_changes,
2700 scratch_pool, scratch_pool));
2702 /* Check if we should add some not-present markers before marking the
2703 directory complete (Issue #3569) */
2705 apr_hash_t *new_children = svn_hash_gets(eb->dir_dirents, db->new_relpath);
2707 if (new_children != NULL)
2709 apr_hash_index_t *hi;
2710 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2712 for (hi = apr_hash_first(scratch_pool, new_children);
2714 hi = apr_hash_next(hi))
2716 const char *child_name;
2717 const char *child_abspath;
2718 const char *child_relpath;
2719 const svn_dirent_t *dirent;
2720 svn_wc__db_status_t status;
2721 svn_node_kind_t child_kind;
2724 svn_pool_clear(iterpool);
2726 child_name = svn__apr_hash_index_key(hi);
2727 child_abspath = svn_dirent_join(db->local_abspath, child_name,
2730 dirent = svn__apr_hash_index_val(hi);
2731 child_kind = (dirent->kind == svn_node_dir)
2735 if (db->ambient_depth < svn_depth_immediates
2736 && child_kind == svn_node_dir)
2737 continue; /* We don't need the subdirs */
2739 /* ### We just check if there is some node in BASE at this path */
2740 err = svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL,
2741 NULL, NULL, NULL, NULL, NULL, NULL,
2742 NULL, NULL, NULL, NULL, NULL,
2743 eb->db, child_abspath,
2744 iterpool, iterpool);
2748 svn_boolean_t is_wcroot;
2749 SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, eb->db, child_abspath,
2753 continue; /* Everything ok... Nothing to do here */
2754 /* Fall through to allow recovering later */
2756 else if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
2757 return svn_error_trace(err);
2759 svn_error_clear(err);
2761 child_relpath = svn_relpath_join(db->new_relpath, child_name,
2764 SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db,
2769 *eb->target_revision,
2775 svn_pool_destroy(iterpool);
2779 if (apr_hash_count(db->not_present_files))
2781 apr_hash_index_t *hi;
2782 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2784 /* This should call some new function (which could also be used
2785 for new_children above) to add all the names in single
2786 transaction, but I can't even trigger it. I've tried
2787 ra_local, ra_svn, ra_neon, ra_serf and they all call
2788 close_file before close_dir. */
2789 for (hi = apr_hash_first(scratch_pool, db->not_present_files);
2791 hi = apr_hash_next(hi))
2793 const char *child = svn__apr_hash_index_key(hi);
2794 const char *child_abspath, *child_relpath;
2796 svn_pool_clear(iterpool);
2798 child_abspath = svn_dirent_join(db->local_abspath, child, iterpool);
2799 child_relpath = svn_dirent_join(db->new_relpath, child, iterpool);
2801 SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db,
2806 *eb->target_revision,
2811 svn_pool_destroy(iterpool);
2814 /* If this directory is merely an anchor for a targeted child, then we
2815 should not be updating the node at all. */
2816 if (db->parent_baton == NULL
2817 && *eb->target_basename != '\0')
2819 /* And we should not have received any changes! */
2820 SVN_ERR_ASSERT(db->propchanges->nelts == 0);
2821 /* ... which also implies NEW_CHANGED_* are not set,
2822 and NEW_BASE_PROPS == NULL. */
2827 apr_array_header_t *iprops = NULL;
2829 /* ### we know a base node already exists. it was created in
2830 ### open_directory or add_directory. let's just preserve the
2831 ### existing DEPTH value, and possibly CHANGED_*. */
2832 /* If we received any changed_* values, then use them. */
2833 if (SVN_IS_VALID_REVNUM(new_changed_rev))
2834 db->changed_rev = new_changed_rev;
2835 if (new_changed_date != 0)
2836 db->changed_date = new_changed_date;
2837 if (new_changed_author != NULL)
2838 db->changed_author = new_changed_author;
2840 /* If no depth is set yet, set to infinity. */
2841 if (db->ambient_depth == svn_depth_unknown)
2842 db->ambient_depth = svn_depth_infinity;
2844 if (eb->depth_is_sticky
2845 && db->ambient_depth != eb->requested_depth)
2847 /* After a depth upgrade the entry must reflect the new depth.
2848 Upgrading to infinity changes the depth of *all* directories,
2849 upgrading to something else only changes the target. */
2851 if (eb->requested_depth == svn_depth_infinity
2852 || (strcmp(db->local_abspath, eb->target_abspath) == 0
2853 && eb->requested_depth > db->ambient_depth))
2855 db->ambient_depth = eb->requested_depth;
2859 /* Do we have new properties to install? Or shall we simply retain
2860 the prior set of properties? If we're installing new properties,
2861 then we also want to write them to an old-style props file. */
2862 props = new_base_props;
2868 svn_skel_t *work_item;
2870 SVN_ERR(complete_conflict(conflict_skel,
2873 db->old_repos_relpath,
2876 svn_node_dir, svn_node_dir,
2877 db->pool, scratch_pool));
2879 SVN_ERR(svn_wc__conflict_create_markers(&work_item,
2880 eb->db, db->local_abspath,
2882 scratch_pool, scratch_pool));
2884 all_work_items = svn_wc__wq_merge(all_work_items, work_item,
2888 /* Any inherited props to be set set for this base node? */
2889 if (eb->wcroot_iprops)
2891 iprops = svn_hash_gets(eb->wcroot_iprops, db->local_abspath);
2893 /* close_edit may also update iprops for switched nodes, catching
2894 those for which close_directory is never called (e.g. a switch
2895 with no changes). So as a minor optimization we remove any
2896 iprops from the hash so as not to set them again in
2899 svn_hash_sets(eb->wcroot_iprops, db->local_abspath, NULL);
2902 /* Update the BASE data for the directory and mark the directory
2904 SVN_ERR(svn_wc__db_base_add_directory(
2905 eb->db, db->local_abspath,
2908 eb->repos_root, eb->repos_uuid,
2909 *eb->target_revision,
2911 db->changed_rev, db->changed_date, db->changed_author,
2912 NULL /* children */,
2914 (dav_prop_changes->nelts > 0)
2915 ? svn_prop_array_to_hash(dav_prop_changes, pool)
2918 (! db->shadowed) && new_base_props != NULL,
2920 iprops, all_work_items,
2924 /* Process all of the queued work items for this directory. */
2925 SVN_ERR(svn_wc__wq_run(eb->db, db->local_abspath,
2926 eb->cancel_func, eb->cancel_baton,
2929 if (conflict_skel && eb->conflict_func)
2930 SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, db->local_abspath,
2932 NULL /* merge_options */,
2939 /* Notify of any prop changes on this directory -- but do nothing if
2940 it's an added or skipped directory, because notification has already
2941 happened in that case - unless the add was obstructed by a dir
2942 scheduled for addition without history, in which case we handle
2943 notification here). */
2944 if (!db->already_notified && eb->notify_func && db->edited)
2946 svn_wc_notify_t *notify;
2947 svn_wc_notify_action_t action;
2949 if (db->shadowed || db->edit_obstructed)
2950 action = svn_wc_notify_update_shadowed_update;
2951 else if (db->obstruction_found || db->add_existed)
2952 action = svn_wc_notify_exists;
2954 action = svn_wc_notify_update_update;
2956 notify = svn_wc_create_notify(db->local_abspath, action, pool);
2957 notify->kind = svn_node_dir;
2958 notify->prop_state = prop_state;
2959 notify->revision = *eb->target_revision;
2960 notify->old_revision = db->old_revision;
2962 eb->notify_func(eb->notify_baton, notify, scratch_pool);
2965 /* We're done with this directory, so remove one reference from the
2966 bump information. */
2967 SVN_ERR(maybe_release_dir_info(db));
2969 return SVN_NO_ERROR;
2973 /* Common code for 'absent_file' and 'absent_directory'. */
2974 static svn_error_t *
2975 absent_node(const char *path,
2976 svn_node_kind_t absent_kind,
2980 struct dir_baton *pb = parent_baton;
2981 struct edit_baton *eb = pb->edit_baton;
2982 apr_pool_t *scratch_pool = svn_pool_create(pool);
2983 const char *name = svn_dirent_basename(path, NULL);
2984 const char *local_abspath;
2986 svn_wc__db_status_t status;
2987 svn_node_kind_t kind;
2990 return SVN_NO_ERROR;
2992 SVN_ERR(mark_directory_edited(pb, scratch_pool));
2994 local_abspath = svn_dirent_join(pb->local_abspath, name, scratch_pool);
2996 /* If an item by this name is scheduled for addition that's a
2997 genuine tree-conflict. */
2998 err = svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
2999 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3000 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3001 NULL, NULL, NULL, NULL,
3002 eb->db, local_abspath,
3003 scratch_pool, scratch_pool);
3007 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
3008 return svn_error_trace(err);
3010 svn_error_clear(err);
3011 status = svn_wc__db_status_not_present;
3012 kind = svn_node_unknown;
3015 if (status == svn_wc__db_status_normal)
3017 svn_boolean_t wcroot;
3018 /* We found an obstructing working copy or a file external! */
3020 SVN_ERR(svn_wc__db_is_wcroot(&wcroot, eb->db, local_abspath,
3026 We have an obstructing working copy; possibly a directory external
3028 We can do two things now:
3029 1) notify the user, record a skip, etc.
3030 2) Just record the absent node in BASE in the parent
3033 As option 2 happens to be exactly what we do anyway, fall through.
3038 /* The server asks us to replace a file external
3039 (Existing BASE node; not reported by the working copy crawler or
3040 there would have been a delete_entry() call.
3042 There is no way we can store this state in the working copy as
3043 the BASE layer is already filled.
3045 We could error out, but that is not helping anybody; the user is not
3046 even seeing with what the file external would be replaced, so let's
3047 report a skip and continue the update.
3050 if (eb->notify_func)
3052 svn_wc_notify_t *notify;
3053 notify = svn_wc_create_notify(
3055 svn_wc_notify_update_skip_obstruction,
3058 eb->notify_func(eb->notify_baton, notify, scratch_pool);
3061 svn_pool_destroy(scratch_pool);
3062 return SVN_NO_ERROR;
3065 else if (status == svn_wc__db_status_not_present
3066 || status == svn_wc__db_status_server_excluded
3067 || status == svn_wc__db_status_excluded)
3069 /* The BASE node is not actually there, so we can safely turn it into
3074 /* We have a local addition. If this would be a BASE node it would have
3075 been deleted before we get here. (Which might have turned it into
3078 ### This should be recorded as a tree conflict and the update
3079 ### can just continue, as we can just record the absent status
3082 SVN_ERR_ASSERT(status != svn_wc__db_status_normal);
3084 return svn_error_createf(
3085 SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
3086 _("Failed to mark '%s' absent: item of the same name is already "
3087 "scheduled for addition"),
3088 svn_dirent_local_style(local_abspath, pool));
3092 const char *repos_relpath;
3093 repos_relpath = svn_relpath_join(pb->new_relpath, name, scratch_pool);
3095 /* Insert an excluded node below the parent node to note that this child
3096 is absent. (This puts it in the parent db if the child is obstructed) */
3097 SVN_ERR(svn_wc__db_base_add_excluded_node(eb->db, local_abspath,
3098 repos_relpath, eb->repos_root,
3100 *(eb->target_revision),
3102 svn_wc__db_status_server_excluded,
3107 svn_pool_destroy(scratch_pool);
3109 return SVN_NO_ERROR;
3113 /* An svn_delta_editor_t function. */
3114 static svn_error_t *
3115 absent_file(const char *path,
3119 return absent_node(path, svn_node_file, parent_baton, pool);
3123 /* An svn_delta_editor_t function. */
3124 static svn_error_t *
3125 absent_directory(const char *path,
3129 return absent_node(path, svn_node_dir, parent_baton, pool);
3133 /* An svn_delta_editor_t function. */
3134 static svn_error_t *
3135 add_file(const char *path,
3137 const char *copyfrom_path,
3138 svn_revnum_t copyfrom_rev,
3142 struct dir_baton *pb = parent_baton;
3143 struct edit_baton *eb = pb->edit_baton;
3144 struct file_baton *fb;
3145 svn_node_kind_t kind = svn_node_none;
3146 svn_node_kind_t wc_kind = svn_node_unknown;
3147 svn_wc__db_status_t status = svn_wc__db_status_normal;
3148 apr_pool_t *scratch_pool;
3149 svn_boolean_t conflicted = FALSE;
3150 svn_boolean_t conflict_ignored = FALSE;
3151 svn_boolean_t versioned_locally_and_present = FALSE;
3152 svn_skel_t *tree_conflict = NULL;
3153 svn_error_t *err = SVN_NO_ERROR;
3155 SVN_ERR_ASSERT(! (copyfrom_path || SVN_IS_VALID_REVNUM(copyfrom_rev)));
3157 SVN_ERR(make_file_baton(&fb, pb, path, TRUE, pool));
3161 return SVN_NO_ERROR;
3163 SVN_ERR(mark_file_edited(fb, pool));
3165 /* The file_pool can stick around for a *long* time, so we want to
3166 use a subpool for any temporary allocations. */
3167 scratch_pool = svn_pool_create(pool);
3170 /* It may not be named the same as the administrative directory. */
3171 if (svn_wc_is_adm_dir(fb->name, pool))
3172 return svn_error_createf(
3173 SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
3174 _("Failed to add file '%s': object of the same name as the "
3175 "administrative directory"),
3176 svn_dirent_local_style(fb->local_abspath, pool));
3178 if (!eb->clean_checkout)
3180 SVN_ERR(svn_io_check_path(fb->local_abspath, &kind, scratch_pool));
3182 err = svn_wc__db_read_info(&status, &wc_kind, NULL, NULL, NULL, NULL, NULL,
3183 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3184 NULL, NULL, NULL, NULL, NULL,
3185 &conflicted, NULL, NULL, NULL, NULL, NULL, NULL,
3186 eb->db, fb->local_abspath,
3187 scratch_pool, scratch_pool);
3192 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
3193 return svn_error_trace(err);
3195 svn_error_clear(err);
3196 wc_kind = svn_node_unknown;
3199 versioned_locally_and_present = FALSE;
3201 else if (wc_kind == svn_node_dir
3202 && status == svn_wc__db_status_normal)
3204 /* !! We found the root of a separate working copy obstructing the wc !!
3206 If the directory would be part of our own working copy then
3207 we wouldn't have been called as an add_file().
3209 The only thing we can do is add a not-present node, to allow
3210 a future update to bring in the new files when the problem is
3212 svn_hash_sets(pb->not_present_files, apr_pstrdup(pb->pool, fb->name),
3215 SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3216 fb->skip_this = TRUE;
3217 fb->already_notified = TRUE;
3219 do_notification(eb, fb->local_abspath, svn_node_file,
3220 svn_wc_notify_update_skip_obstruction, scratch_pool);
3222 svn_pool_destroy(scratch_pool);
3224 return SVN_NO_ERROR;
3226 else if (status == svn_wc__db_status_normal
3227 && (wc_kind == svn_node_file
3228 || wc_kind == svn_node_symlink))
3230 /* We found a file external occupating the place we need in BASE.
3232 We can't add a not-present node in this case as that would overwrite
3233 the file external. Luckily the file external itself stops us from
3234 forgetting a child of this parent directory like an obstructing
3237 The reason we get here is that the adm crawler doesn't report
3240 SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3241 fb->skip_this = TRUE;
3242 fb->already_notified = TRUE;
3244 do_notification(eb, fb->local_abspath, svn_node_file,
3245 svn_wc_notify_update_skip_obstruction, scratch_pool);
3247 svn_pool_destroy(scratch_pool);
3249 return SVN_NO_ERROR;
3251 else if (wc_kind == svn_node_unknown)
3252 versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */
3254 versioned_locally_and_present = IS_NODE_PRESENT(status);
3257 /* Is this path a conflict victim? */
3259 conflicted = FALSE; /* Conflict applies to WORKING */
3260 else if (conflicted)
3262 if (pb->deletion_conflicts)
3263 tree_conflict = svn_hash_gets(pb->deletion_conflicts, fb->name);
3267 svn_wc_conflict_reason_t reason;
3268 const char *move_src_op_root_abspath;
3269 /* So this deletion wasn't just a deletion, it is actually a
3270 replacement. Let's install a better tree conflict. */
3272 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL,
3273 &move_src_op_root_abspath,
3277 fb->pool, fb->pool));
3279 tree_conflict = svn_wc__conflict_skel_create(fb->pool);
3281 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
3283 eb->db, fb->local_abspath,
3284 reason, svn_wc_conflict_action_replace,
3285 move_src_op_root_abspath,
3286 fb->pool, fb->pool));
3288 /* And now stop checking for conflicts here and just perform
3289 a shadowed update */
3290 fb->edit_conflict = tree_conflict; /* Cache for close_file */
3291 tree_conflict = NULL; /* No direct notification */
3292 fb->shadowed = TRUE; /* Just continue */
3293 conflicted = FALSE; /* No skip */
3296 SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
3297 eb->db, fb->local_abspath, pool));
3300 /* Now the usual conflict handling: skip. */
3303 SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3305 fb->skip_this = TRUE;
3306 fb->already_notified = TRUE;
3308 /* We skip this node, but once the update completes the parent node will
3309 be updated to the new revision. So a future recursive update of the
3310 parent will not bring in this new node as the revision of the parent
3311 describes to the repository that all children are available.
3313 To resolve this problem, we add a not-present node to allow bringing
3314 the node in once this conflict is resolved.
3316 Note that we can safely assume that no present base node exists,
3317 because then we would not have received an add_file.
3319 svn_hash_sets(pb->not_present_files, apr_pstrdup(pb->pool, fb->name),
3322 do_notification(eb, fb->local_abspath, svn_node_unknown,
3323 svn_wc_notify_skip_conflicted, scratch_pool);
3325 svn_pool_destroy(scratch_pool);
3327 return SVN_NO_ERROR;
3329 else if (conflict_ignored)
3331 fb->shadowed = TRUE;
3336 /* Nothing to check; does not and will not exist in working copy */
3338 else if (versioned_locally_and_present)
3340 /* What to do with a versioned or schedule-add file:
3342 If the UUID doesn't match the parent's, or the URL isn't a child of
3343 the parent dir's URL, it's an error.
3345 Set add_existed so that user notification is delayed until after any
3346 text or prop conflicts have been found.
3348 Whether the incoming add is a symlink or a file will only be known in
3349 close_file(), when the props are known. So with a locally added file
3350 or symlink, let close_file() check for a tree conflict.
3352 We will never see missing files here, because these would be
3353 re-added during the crawler phase. */
3354 svn_boolean_t local_is_file;
3356 /* Is the local node a copy or move */
3357 if (status == svn_wc__db_status_added)
3358 SVN_ERR(svn_wc__db_scan_addition(&status, NULL, NULL, NULL, NULL, NULL,
3360 eb->db, fb->local_abspath,
3361 scratch_pool, scratch_pool));
3363 /* Is there something that is a file? */
3364 local_is_file = (wc_kind == svn_node_file
3365 || wc_kind == svn_node_symlink);
3367 /* Do tree conflict checking if
3368 * - if there is a local copy.
3369 * - if this is a switch operation
3370 * - the node kinds mismatch
3372 * During switch, local adds at the same path as incoming adds get
3373 * "lost" in that switching back to the original will no longer have the
3374 * local add. So switch always alerts the user with a tree conflict. */
3375 if (!eb->adds_as_modification
3377 || status != svn_wc__db_status_added)
3379 SVN_ERR(check_tree_conflict(&tree_conflict, eb,
3381 status, FALSE, svn_node_none,
3382 svn_wc_conflict_action_add,
3383 scratch_pool, scratch_pool));
3386 if (tree_conflict == NULL)
3387 fb->add_existed = TRUE; /* Take over WORKING */
3389 fb->shadowed = TRUE; /* Only update BASE */
3392 else if (kind != svn_node_none)
3394 /* There's an unversioned node at this path. */
3395 fb->obstruction_found = TRUE;
3397 /* Unversioned, obstructing files are handled by text merge/conflict,
3398 * if unversioned obstructions are allowed. */
3399 if (! (kind == svn_node_file && eb->allow_unver_obstructions))
3401 /* Bring in the node as deleted */ /* ### Obstructed Conflict */
3402 fb->shadowed = TRUE;
3404 /* Mark a conflict */
3405 tree_conflict = svn_wc__conflict_skel_create(fb->pool);
3407 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
3409 eb->db, fb->local_abspath,
3410 svn_wc_conflict_reason_unversioned,
3411 svn_wc_conflict_action_add,
3413 fb->pool, scratch_pool));
3417 /* When this is not the update target add a not-present BASE node now,
3418 to allow marking the parent directory complete in its close_edit() call.
3419 This resolves issues when that occurs before the close_file(). */
3420 if (pb->parent_baton
3421 || *eb->target_basename == '\0'
3422 || (strcmp(fb->local_abspath, eb->target_abspath) != 0))
3424 svn_hash_sets(pb->not_present_files, apr_pstrdup(pb->pool, fb->name),
3428 if (tree_conflict != NULL)
3430 SVN_ERR(complete_conflict(tree_conflict,
3433 fb->old_repos_relpath,
3438 fb->pool, scratch_pool));
3440 SVN_ERR(svn_wc__db_op_mark_conflict(eb->db,
3442 tree_conflict, NULL,
3445 if (eb->conflict_func)
3446 SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, fb->local_abspath,
3448 NULL /* merge_options */,
3455 fb->already_notified = TRUE;
3456 do_notification(eb, fb->local_abspath, svn_node_file,
3457 svn_wc_notify_tree_conflict, scratch_pool);
3460 svn_pool_destroy(scratch_pool);
3462 return SVN_NO_ERROR;
3466 /* An svn_delta_editor_t function. */
3467 static svn_error_t *
3468 open_file(const char *path,
3470 svn_revnum_t base_revision,
3474 struct dir_baton *pb = parent_baton;
3475 struct edit_baton *eb = pb->edit_baton;
3476 struct file_baton *fb;
3477 svn_boolean_t conflicted;
3478 svn_boolean_t conflict_ignored = FALSE;
3479 svn_boolean_t have_work;
3480 svn_wc__db_status_t status;
3481 svn_node_kind_t wc_kind;
3482 svn_skel_t *tree_conflict = NULL;
3484 /* the file_pool can stick around for a *long* time, so we want to use
3485 a subpool for any temporary allocations. */
3486 apr_pool_t *scratch_pool = svn_pool_create(pool);
3488 SVN_ERR(make_file_baton(&fb, pb, path, FALSE, pool));
3492 return SVN_NO_ERROR;
3494 /* Detect obstructing working copies */
3496 svn_boolean_t is_root;
3498 SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, fb->local_abspath,
3503 /* Just skip this node; a future update will handle it */
3504 SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3505 fb->skip_this = TRUE;
3506 fb->already_notified = TRUE;
3508 do_notification(eb, fb->local_abspath, svn_node_file,
3509 svn_wc_notify_update_skip_obstruction, pool);
3511 return SVN_NO_ERROR;
3517 /* If replacing, make sure the .svn entry already exists. */
3518 SVN_ERR(svn_wc__db_read_info(&status, &wc_kind, &fb->old_revision,
3519 &fb->old_repos_relpath, NULL, NULL,
3520 &fb->changed_rev, &fb->changed_date,
3521 &fb->changed_author, NULL,
3522 &fb->original_checksum, NULL, NULL, NULL,
3523 NULL, NULL, NULL, NULL, NULL, NULL,
3524 &conflicted, NULL, NULL, &fb->local_prop_mods,
3525 NULL, NULL, &have_work,
3526 eb->db, fb->local_abspath,
3527 fb->pool, scratch_pool));
3530 SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &fb->old_revision,
3531 &fb->old_repos_relpath, NULL, NULL,
3532 &fb->changed_rev, &fb->changed_date,
3533 &fb->changed_author, NULL,
3534 &fb->original_checksum, NULL, NULL,
3536 eb->db, fb->local_abspath,
3537 fb->pool, scratch_pool));
3539 /* Is this path a conflict victim? */
3541 conflicted = FALSE; /* Conflict applies to WORKING */
3542 else if (conflicted)
3543 SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
3544 eb->db, fb->local_abspath, pool));
3547 SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3549 fb->skip_this = TRUE;
3550 fb->already_notified = TRUE;
3552 do_notification(eb, fb->local_abspath, svn_node_unknown,
3553 svn_wc_notify_skip_conflicted, scratch_pool);
3555 svn_pool_destroy(scratch_pool);
3557 return SVN_NO_ERROR;
3559 else if (conflict_ignored)
3561 fb->shadowed = TRUE;
3564 /* Check for conflicts only when we haven't already recorded
3565 * a tree-conflict on a parent node. */
3567 SVN_ERR(check_tree_conflict(&tree_conflict, eb, fb->local_abspath,
3568 status, TRUE, svn_node_file,
3569 svn_wc_conflict_action_edit,
3570 fb->pool, scratch_pool));
3572 /* Is this path the victim of a newly-discovered tree conflict? */
3573 if (tree_conflict != NULL)
3575 svn_wc_conflict_reason_t reason;
3576 fb->edit_conflict = tree_conflict;
3577 /* Other modifications wouldn't be a tree conflict */
3579 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
3580 eb->db, fb->local_abspath,
3582 scratch_pool, scratch_pool));
3583 SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_deleted
3584 || reason == svn_wc_conflict_reason_moved_away
3585 || reason == svn_wc_conflict_reason_replaced
3586 || reason == svn_wc_conflict_reason_obstructed);
3588 /* Continue updating BASE */
3589 if (reason == svn_wc_conflict_reason_obstructed)
3590 fb->edit_obstructed = TRUE;
3592 fb->shadowed = TRUE;
3595 svn_pool_destroy(scratch_pool);
3597 return SVN_NO_ERROR;
3600 /* Implements svn_stream_lazyopen_func_t. */
3601 static svn_error_t *
3602 lazy_open_source(svn_stream_t **stream,
3604 apr_pool_t *result_pool,
3605 apr_pool_t *scratch_pool)
3607 struct file_baton *fb = baton;
3609 SVN_ERR(svn_wc__db_pristine_read(stream, NULL, fb->edit_baton->db,
3611 fb->original_checksum,
3612 result_pool, scratch_pool));
3615 return SVN_NO_ERROR;
3618 struct lazy_target_baton {
3619 struct file_baton *fb;
3620 struct handler_baton *hb;
3621 struct edit_baton *eb;
3624 /* Implements svn_stream_lazyopen_func_t. */
3625 static svn_error_t *
3626 lazy_open_target(svn_stream_t **stream,
3628 apr_pool_t *result_pool,
3629 apr_pool_t *scratch_pool)
3631 struct lazy_target_baton *tb = baton;
3633 SVN_ERR(svn_wc__open_writable_base(stream, &tb->hb->new_text_base_tmp_abspath,
3634 NULL, &tb->hb->new_text_base_sha1_checksum,
3635 tb->fb->edit_baton->db,
3636 tb->eb->wcroot_abspath,
3637 result_pool, scratch_pool));
3639 return SVN_NO_ERROR;
3642 /* An svn_delta_editor_t function. */
3643 static svn_error_t *
3644 apply_textdelta(void *file_baton,
3645 const char *expected_checksum,
3647 svn_txdelta_window_handler_t *handler,
3648 void **handler_baton)
3650 struct file_baton *fb = file_baton;
3651 apr_pool_t *handler_pool = svn_pool_create(fb->pool);
3652 struct handler_baton *hb = apr_pcalloc(handler_pool, sizeof(*hb));
3653 struct edit_baton *eb = fb->edit_baton;
3654 const svn_checksum_t *recorded_base_checksum;
3655 svn_checksum_t *expected_base_checksum;
3656 svn_stream_t *source;
3657 struct lazy_target_baton *tb;
3658 svn_stream_t *target;
3662 *handler = svn_delta_noop_window_handler;
3663 *handler_baton = NULL;
3664 return SVN_NO_ERROR;
3667 SVN_ERR(mark_file_edited(fb, pool));
3669 /* Parse checksum or sets expected_base_checksum to NULL */
3670 SVN_ERR(svn_checksum_parse_hex(&expected_base_checksum, svn_checksum_md5,
3671 expected_checksum, pool));
3673 /* Before applying incoming svndiff data to text base, make sure
3674 text base hasn't been corrupted, and that its checksum
3675 matches the expected base checksum. */
3677 /* The incoming delta is targeted against EXPECTED_BASE_CHECKSUM. Find and
3678 check our RECORDED_BASE_CHECKSUM. (In WC-1, we could not do this test
3679 for replaced nodes because we didn't store the checksum of the "revert
3680 base". In WC-NG, we do and we can.) */
3681 recorded_base_checksum = fb->original_checksum;
3683 /* If we have a checksum that we want to compare to a MD5 checksum,
3684 ensure that it is a MD5 checksum */
3685 if (recorded_base_checksum
3686 && expected_base_checksum
3687 && recorded_base_checksum->kind != svn_checksum_md5)
3688 SVN_ERR(svn_wc__db_pristine_get_md5(&recorded_base_checksum,
3689 eb->db, eb->wcroot_abspath,
3690 recorded_base_checksum, pool, pool));
3693 if (!svn_checksum_match(expected_base_checksum, recorded_base_checksum))
3694 return svn_error_createf(SVN_ERR_WC_CORRUPT_TEXT_BASE, NULL,
3695 _("Checksum mismatch for '%s':\n"
3698 svn_dirent_local_style(fb->local_abspath, pool),
3699 svn_checksum_to_cstring_display(expected_base_checksum,
3701 svn_checksum_to_cstring_display(recorded_base_checksum,
3704 /* Open the text base for reading, unless this is an added file. */
3707 kff todo: what we really need to do here is:
3709 1. See if there's a file or dir by this name already here.
3710 2. See if it's under revision control.
3711 3. If both are true, open text-base.
3712 4. If only 1 is true, bail, because we can't go destroying user's
3713 files (or as an alternative to bailing, move it to some tmp
3714 name and somehow tell the user, but communicating with the
3715 user without erroring is a whole callback system we haven't
3716 finished inventing yet.)
3719 if (! fb->adding_file)
3721 SVN_ERR_ASSERT(!fb->original_checksum
3722 || fb->original_checksum->kind == svn_checksum_sha1);
3724 source = svn_stream_lazyopen_create(lazy_open_source, fb, FALSE,
3729 source = svn_stream_empty(handler_pool);
3732 /* If we don't have a recorded checksum, use the ra provided checksum */
3733 if (!recorded_base_checksum)
3734 recorded_base_checksum = expected_base_checksum;
3736 /* Checksum the text base while applying deltas */
3737 if (recorded_base_checksum)
3739 hb->expected_source_checksum = svn_checksum_dup(recorded_base_checksum,
3742 /* Wrap stream and store reference to allow calculating the
3744 source = svn_stream_checksummed2(source,
3745 &hb->actual_source_checksum,
3746 NULL, recorded_base_checksum->kind,
3747 TRUE, handler_pool);
3748 hb->source_checksum_stream = source;
3751 tb = apr_palloc(handler_pool, sizeof(struct lazy_target_baton));
3755 target = svn_stream_lazyopen_create(lazy_open_target, tb, TRUE, handler_pool);
3757 /* Prepare to apply the delta. */
3758 svn_txdelta_apply(source, target,
3759 hb->new_text_base_md5_digest,
3760 hb->new_text_base_tmp_abspath /* error_info */,
3762 &hb->apply_handler, &hb->apply_baton);
3764 hb->pool = handler_pool;
3767 /* We're all set. */
3768 *handler_baton = hb;
3769 *handler = window_handler;
3771 return SVN_NO_ERROR;
3775 /* An svn_delta_editor_t function. */
3776 static svn_error_t *
3777 change_file_prop(void *file_baton,
3779 const svn_string_t *value,
3780 apr_pool_t *scratch_pool)
3782 struct file_baton *fb = file_baton;
3783 svn_prop_t *propchange;
3786 return SVN_NO_ERROR;
3788 /* Push a new propchange to the file baton's array of propchanges */
3789 propchange = apr_array_push(fb->propchanges);
3790 propchange->name = apr_pstrdup(fb->pool, name);
3791 propchange->value = value ? svn_string_dup(value, fb->pool) : NULL;
3793 if (!fb->edited && svn_property_kind2(name) == svn_prop_regular_kind)
3794 SVN_ERR(mark_file_edited(fb, scratch_pool));
3797 && strcmp(name, SVN_PROP_SPECIAL) == 0)
3799 struct edit_baton *eb = fb->edit_baton;
3800 svn_boolean_t modified = FALSE;
3801 svn_boolean_t becomes_symlink;
3802 svn_boolean_t was_symlink;
3804 /* Let's see if we have a change as in some scenarios servers report
3805 non-changes of properties. */
3806 becomes_symlink = (value != NULL);
3808 if (fb->adding_file)
3809 was_symlink = becomes_symlink; /* No change */
3814 /* We read the server-props, not the ACTUAL props here as we just
3815 want to see if this is really an incoming prop change. */
3816 SVN_ERR(svn_wc__db_base_get_props(&props, eb->db,
3818 scratch_pool, scratch_pool));
3820 was_symlink = ((props
3821 && svn_hash_gets(props, SVN_PROP_SPECIAL) != NULL)
3823 : svn_tristate_false);
3826 if (was_symlink != becomes_symlink)
3828 /* If the local node was not modified, we continue as usual, if
3829 modified we want a tree conflict just like how we would handle
3830 it when receiving a delete + add (aka "replace") */
3831 if (fb->local_prop_mods)
3834 SVN_ERR(svn_wc__internal_file_modified_p(&modified, eb->db,
3836 FALSE, scratch_pool));
3841 if (!fb->edit_conflict)
3842 fb->edit_conflict = svn_wc__conflict_skel_create(fb->pool);
3844 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
3846 eb->db, fb->local_abspath,
3847 svn_wc_conflict_reason_edited,
3848 svn_wc_conflict_action_replace,
3850 fb->pool, scratch_pool));
3852 SVN_ERR(complete_conflict(fb->edit_conflict, fb->edit_baton,
3853 fb->local_abspath, fb->old_repos_relpath,
3854 fb->old_revision, fb->new_relpath,
3855 svn_node_file, svn_node_file,
3856 fb->pool, scratch_pool));
3858 /* Create a copy of the existing (pre update) BASE node in WORKING,
3859 mark a tree conflict and handle the rest of the update as
3861 SVN_ERR(svn_wc__db_op_make_copy(eb->db, fb->local_abspath,
3862 fb->edit_conflict, NULL,
3865 do_notification(eb, fb->local_abspath, svn_node_file,
3866 svn_wc_notify_tree_conflict, scratch_pool);
3868 /* Ok, we introduced a replacement, so we can now handle the rest
3869 as a normal shadowed update */
3870 fb->shadowed = TRUE;
3871 fb->add_existed = FALSE;
3872 fb->already_notified = TRUE;
3876 return SVN_NO_ERROR;
3879 /* Perform the actual merge of file changes between an original file,
3880 identified by ORIGINAL_CHECKSUM (an empty file if NULL) to a new file
3881 identified by NEW_CHECKSUM.
3883 Merge the result into LOCAL_ABSPATH, which is part of the working copy
3884 identified by WRI_ABSPATH. Use OLD_REVISION and TARGET_REVISION for naming
3885 the intermediate files.
3887 The rest of the arguments are passed to svn_wc__internal_merge().
3890 svn_wc__perform_file_merge(svn_skel_t **work_items,
3891 svn_skel_t **conflict_skel,
3892 svn_boolean_t *found_conflict,
3894 const char *local_abspath,
3895 const char *wri_abspath,
3896 const svn_checksum_t *new_checksum,
3897 const svn_checksum_t *original_checksum,
3898 apr_hash_t *old_actual_props,
3899 const apr_array_header_t *ext_patterns,
3900 svn_revnum_t old_revision,
3901 svn_revnum_t target_revision,
3902 const apr_array_header_t *propchanges,
3903 const char *diff3_cmd,
3904 svn_cancel_func_t cancel_func,
3906 apr_pool_t *result_pool,
3907 apr_pool_t *scratch_pool)
3909 /* Actual file exists and has local mods:
3910 Now we need to let loose svn_wc__internal_merge() to merge
3911 the textual changes into the working file. */
3912 const char *oldrev_str, *newrev_str, *mine_str;
3913 const char *merge_left;
3914 svn_boolean_t delete_left = FALSE;
3915 const char *path_ext = "";
3916 const char *new_text_base_tmp_abspath;
3917 enum svn_wc_merge_outcome_t merge_outcome = svn_wc_merge_unchanged;
3918 svn_skel_t *work_item;
3922 SVN_ERR(svn_wc__db_pristine_get_path(&new_text_base_tmp_abspath,
3923 db, wri_abspath, new_checksum,
3924 scratch_pool, scratch_pool));
3926 /* If we have any file extensions we're supposed to
3927 preserve in generated conflict file names, then find
3928 this path's extension. But then, if it isn't one of
3929 the ones we want to keep in conflict filenames,
3930 pretend it doesn't have an extension at all. */
3931 if (ext_patterns && ext_patterns->nelts)
3933 svn_path_splitext(NULL, &path_ext, local_abspath, scratch_pool);
3934 if (! (*path_ext && svn_cstring_match_glob_list(path_ext, ext_patterns)))
3938 /* old_revision can be invalid when the conflict is against a
3940 if (!SVN_IS_VALID_REVNUM(old_revision))
3943 oldrev_str = apr_psprintf(scratch_pool, ".r%ld%s%s",
3945 *path_ext ? "." : "",
3946 *path_ext ? path_ext : "");
3948 newrev_str = apr_psprintf(scratch_pool, ".r%ld%s%s",
3950 *path_ext ? "." : "",
3951 *path_ext ? path_ext : "");
3952 mine_str = apr_psprintf(scratch_pool, ".mine%s%s",
3953 *path_ext ? "." : "",
3954 *path_ext ? path_ext : "");
3956 if (! original_checksum)
3958 SVN_ERR(get_empty_tmp_file(&merge_left, db, wri_abspath,
3959 result_pool, scratch_pool));
3963 SVN_ERR(svn_wc__db_pristine_get_path(&merge_left, db, wri_abspath,
3965 result_pool, scratch_pool));
3967 /* Merge the changes from the old textbase to the new
3968 textbase into the file we're updating.
3969 Remember that this function wants full paths! */
3970 SVN_ERR(svn_wc__internal_merge(&work_item,
3975 new_text_base_tmp_abspath,
3978 oldrev_str, newrev_str, mine_str,
3980 FALSE /* dry_run */,
3981 diff3_cmd, NULL, propchanges,
3982 cancel_func, cancel_baton,
3983 result_pool, scratch_pool));
3985 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
3986 *found_conflict = (merge_outcome == svn_wc_merge_conflict);
3988 /* If we created a temporary left merge file, get rid of it. */
3991 SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, wri_abspath,
3993 result_pool, scratch_pool));
3994 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
3997 return SVN_NO_ERROR;
4000 /* This is the small planet. It has the complex responsibility of
4001 * "integrating" a new revision of a file into a working copy.
4003 * Given a file_baton FB for a file either already under version control, or
4004 * prepared (see below) to join version control, fully install a
4005 * new revision of the file.
4007 * ### transitional: installation of the working file will be handled
4008 * ### by the *INSTALL_PRISTINE flag.
4010 * By "install", we mean: create a new text-base and prop-base, merge
4011 * any textual and property changes into the working file, and finally
4012 * update all metadata so that the working copy believes it has a new
4013 * working revision of the file. All of this work includes being
4014 * sensitive to eol translation, keyword substitution, and performing
4015 * all actions accumulated the parent directory's work queue.
4017 * Set *CONTENT_STATE to the state of the contents after the
4020 * Return values are allocated in RESULT_POOL and temporary allocations
4021 * are performed in SCRATCH_POOL.
4023 static svn_error_t *
4024 merge_file(svn_skel_t **work_items,
4025 svn_skel_t **conflict_skel,
4026 svn_boolean_t *install_pristine,
4027 const char **install_from,
4028 svn_wc_notify_state_t *content_state,
4029 struct file_baton *fb,
4030 apr_hash_t *actual_props,
4031 apr_time_t last_changed_date,
4032 apr_pool_t *result_pool,
4033 apr_pool_t *scratch_pool)
4035 struct edit_baton *eb = fb->edit_baton;
4036 struct dir_baton *pb = fb->dir_baton;
4037 svn_boolean_t is_locally_modified;
4038 svn_boolean_t found_text_conflict = FALSE;
4040 SVN_ERR_ASSERT(! fb->shadowed
4041 && ! fb->obstruction_found
4042 && ! fb->edit_obstructed);
4045 When this function is called on file F, we assume the following
4048 - The new pristine text of F is present in the pristine store
4049 iff FB->NEW_TEXT_BASE_SHA1_CHECKSUM is not NULL.
4051 - The WC metadata still reflects the old version of F.
4052 (We can still access the old pristine base text of F.)
4054 The goal is to update the local working copy of F to reflect
4055 the changes received from the repository, preserving any local
4060 *install_pristine = FALSE;
4061 *install_from = NULL;
4063 /* Start by splitting the file path, getting an access baton for the parent,
4064 and an entry for the file if any. */
4066 /* Has the user made local mods to the working file?
4067 Note that this compares to the current pristine file, which is
4068 different from fb->old_text_base_path if we have a replaced-with-history
4069 file. However, in the case we had an obstruction, we check against the
4072 if (fb->adding_file && !fb->add_existed)
4074 is_locally_modified = FALSE; /* There is no file: Don't check */
4078 /* The working file is not an obstruction.
4079 So: is the file modified, relative to its ORIGINAL pristine?
4081 This function sets is_locally_modified to FALSE for
4082 files that do not exist and for directories. */
4084 SVN_ERR(svn_wc__internal_file_modified_p(&is_locally_modified,
4085 eb->db, fb->local_abspath,
4086 FALSE /* exact_comparison */,
4090 /* For 'textual' merging, we use the following system:
4092 When a file is modified and we have a new BASE:
4094 * svn_wc_merge uses diff3
4095 * possibly makes backups and marks files as conflicted.
4098 * svn_wc_merge makes backups and marks files as conflicted.
4100 If a file is not modified and we have a new BASE:
4101 * Install from pristine.
4103 If we have property changes related to magic properties or if the
4104 svn:keywords property is set:
4105 * Retranslate from the working file.
4107 if (! is_locally_modified
4108 && fb->new_text_base_sha1_checksum)
4110 /* If there are no local mods, who cares whether it's a text
4111 or binary file! Just write a log command to overwrite
4112 any working file with the new text-base. If newline
4113 conversion or keyword substitution is activated, this
4114 will happen as well during the copy.
4115 For replaced files, though, we want to merge in the changes
4116 even if the file is not modified compared to the (non-revert)
4119 *install_pristine = TRUE;
4121 else if (fb->new_text_base_sha1_checksum)
4123 /* Actual file exists and has local mods:
4124 Now we need to let loose svn_wc__merge_internal() to merge
4125 the textual changes into the working file. */
4126 SVN_ERR(svn_wc__perform_file_merge(work_items,
4128 &found_text_conflict,
4132 fb->new_text_base_sha1_checksum,
4135 : fb->original_checksum,
4139 *eb->target_revision,
4142 eb->cancel_func, eb->cancel_baton,
4143 result_pool, scratch_pool));
4144 } /* end: working file exists and has mods */
4147 /* There is no new text base, but let's see if the working file needs
4148 to be updated for any other reason. */
4150 apr_hash_t *keywords;
4152 /* Determine if any of the propchanges are the "magic" ones that
4153 might require changing the working file. */
4154 svn_boolean_t magic_props_changed;
4156 magic_props_changed = svn_wc__has_magic_property(fb->propchanges);
4158 SVN_ERR(svn_wc__get_translate_info(NULL, NULL,
4161 eb->db, fb->local_abspath,
4163 scratch_pool, scratch_pool));
4164 if (magic_props_changed || keywords)
4166 /* Special edge-case: it's possible that this file installation
4167 only involves propchanges, but that some of those props still
4168 require a retranslation of the working file.
4170 OR that the file doesn't involve propchanges which by themselves
4171 require retranslation, but receiving a change bumps the revision
4172 number which requires re-expansion of keywords... */
4174 if (is_locally_modified)
4176 const char *tmptext;
4178 /* Copy and DEtranslate the working file to a temp text-base.
4179 Note that detranslation is done according to the old props. */
4180 SVN_ERR(svn_wc__internal_translated_file(
4181 &tmptext, fb->local_abspath, eb->db, fb->local_abspath,
4182 SVN_WC_TRANSLATE_TO_NF
4183 | SVN_WC_TRANSLATE_NO_OUTPUT_CLEANUP,
4184 eb->cancel_func, eb->cancel_baton,
4185 result_pool, scratch_pool));
4187 /* We always want to reinstall the working file if the magic
4188 properties have changed, or there are any keywords present.
4189 Note that TMPTEXT might actually refer to the working file
4190 itself (the above function skips a detranslate when not
4191 required). This is acceptable, as we will (re)translate
4192 according to the new properties into a temporary file (from
4193 the working file), and then rename the temp into place. Magic!
4195 *install_pristine = TRUE;
4196 *install_from = tmptext;
4200 /* Use our existing 'copy' from the pristine store instead
4201 of making a new copy. This way we can use the standard code
4202 to update the recorded size and modification time.
4204 *install_pristine = TRUE;
4209 /* Set the returned content state. */
4211 if (found_text_conflict)
4212 *content_state = svn_wc_notify_state_conflicted;
4213 else if (fb->new_text_base_sha1_checksum)
4215 if (is_locally_modified)
4216 *content_state = svn_wc_notify_state_merged;
4218 *content_state = svn_wc_notify_state_changed;
4221 *content_state = svn_wc_notify_state_unchanged;
4223 return SVN_NO_ERROR;
4227 /* An svn_delta_editor_t function. */
4228 /* Mostly a wrapper around merge_file. */
4229 static svn_error_t *
4230 close_file(void *file_baton,
4231 const char *expected_md5_digest,
4234 struct file_baton *fb = file_baton;
4235 struct dir_baton *pdb = fb->dir_baton;
4236 struct edit_baton *eb = fb->edit_baton;
4237 svn_wc_notify_state_t content_state, prop_state;
4238 svn_wc_notify_lock_state_t lock_state;
4239 svn_checksum_t *expected_md5_checksum = NULL;
4240 apr_hash_t *new_base_props = NULL;
4241 apr_hash_t *new_actual_props = NULL;
4242 apr_array_header_t *entry_prop_changes;
4243 apr_array_header_t *dav_prop_changes;
4244 apr_array_header_t *regular_prop_changes;
4245 apr_hash_t *current_base_props = NULL;
4246 apr_hash_t *current_actual_props = NULL;
4247 apr_hash_t *local_actual_props = NULL;
4248 svn_skel_t *all_work_items = NULL;
4249 svn_skel_t *conflict_skel = NULL;
4250 svn_skel_t *work_item;
4251 apr_pool_t *scratch_pool = fb->pool; /* Destroyed at function exit */
4252 svn_boolean_t keep_recorded_info = FALSE;
4253 const svn_checksum_t *new_checksum;
4254 apr_array_header_t *iprops = NULL;
4258 svn_pool_destroy(fb->pool);
4259 SVN_ERR(maybe_release_dir_info(pdb));
4260 return SVN_NO_ERROR;
4264 conflict_skel = fb->edit_conflict;
4266 if (expected_md5_digest)
4267 SVN_ERR(svn_checksum_parse_hex(&expected_md5_checksum, svn_checksum_md5,
4268 expected_md5_digest, scratch_pool));
4270 if (fb->new_text_base_md5_checksum && expected_md5_checksum
4271 && !svn_checksum_match(expected_md5_checksum,
4272 fb->new_text_base_md5_checksum))
4273 return svn_error_trace(
4274 svn_checksum_mismatch_err(expected_md5_checksum,
4275 fb->new_text_base_md5_checksum,
4277 _("Checksum mismatch for '%s'"),
4278 svn_dirent_local_style(
4279 fb->local_abspath, pool)));
4281 /* Gather the changes for each kind of property. */
4282 SVN_ERR(svn_categorize_props(fb->propchanges, &entry_prop_changes,
4283 &dav_prop_changes, ®ular_prop_changes,
4286 /* Extract the changed_* and lock state information. */
4288 svn_revnum_t new_changed_rev;
4289 apr_time_t new_changed_date;
4290 const char *new_changed_author;
4292 SVN_ERR(accumulate_last_change(&new_changed_rev,
4294 &new_changed_author,
4296 scratch_pool, scratch_pool));
4298 if (SVN_IS_VALID_REVNUM(new_changed_rev))
4299 fb->changed_rev = new_changed_rev;
4300 if (new_changed_date != 0)
4301 fb->changed_date = new_changed_date;
4302 if (new_changed_author != NULL)
4303 fb->changed_author = new_changed_author;
4306 /* Determine whether the file has become unlocked. */
4310 lock_state = svn_wc_notify_lock_state_unchanged;
4312 for (i = 0; i < entry_prop_changes->nelts; ++i)
4314 const svn_prop_t *prop
4315 = &APR_ARRAY_IDX(entry_prop_changes, i, svn_prop_t);
4317 /* If we see a change to the LOCK_TOKEN entry prop, then the only
4318 possible change is its REMOVAL. Thus, the lock has been removed,
4319 and we should likewise remove our cached copy of it. */
4320 if (! strcmp(prop->name, SVN_PROP_ENTRY_LOCK_TOKEN))
4322 /* If we lose the lock, but not because we are switching to
4323 another url, remove the state lock from the wc */
4324 if (! eb->switch_relpath
4325 || strcmp(fb->new_relpath, fb->old_repos_relpath) == 0)
4327 SVN_ERR_ASSERT(prop->value == NULL);
4328 SVN_ERR(svn_wc__db_lock_remove(eb->db, fb->local_abspath,
4331 lock_state = svn_wc_notify_lock_state_unlocked;
4338 /* Install all kinds of properties. It is important to do this before
4339 any file content merging, since that process might expand keywords, in
4340 which case we want the new entryprops to be in place. */
4342 /* Write log commands to merge REGULAR_PROPS into the existing
4343 properties of FB->LOCAL_ABSPATH. Update *PROP_STATE to reflect
4344 the result of the regular prop merge.
4346 BASE_PROPS and WORKING_PROPS are hashes of the base and
4347 working props of the file; if NULL they are read from the wc. */
4349 /* ### some of this feels like voodoo... */
4351 if ((!fb->adding_file || fb->add_existed)
4353 SVN_ERR(svn_wc__get_actual_props(&local_actual_props,
4354 eb->db, fb->local_abspath,
4355 scratch_pool, scratch_pool));
4356 if (local_actual_props == NULL)
4357 local_actual_props = apr_hash_make(scratch_pool);
4359 if (fb->add_existed)
4361 /* This node already exists. Grab the current pristine properties. */
4362 SVN_ERR(svn_wc__db_read_pristine_props(¤t_base_props,
4363 eb->db, fb->local_abspath,
4364 scratch_pool, scratch_pool));
4365 current_actual_props = local_actual_props;
4367 else if (!fb->adding_file)
4369 /* Get the BASE properties for proper merging. */
4370 SVN_ERR(svn_wc__db_base_get_props(¤t_base_props,
4371 eb->db, fb->local_abspath,
4372 scratch_pool, scratch_pool));
4373 current_actual_props = local_actual_props;
4376 /* Note: even if the node existed before, it may not have
4377 pristine props (e.g a local-add) */
4378 if (current_base_props == NULL)
4379 current_base_props = apr_hash_make(scratch_pool);
4381 /* And new nodes need an empty set of ACTUAL props. */
4382 if (current_actual_props == NULL)
4383 current_actual_props = apr_hash_make(scratch_pool);
4385 prop_state = svn_wc_notify_state_unknown;
4389 svn_boolean_t install_pristine;
4390 const char *install_from = NULL;
4392 /* Merge the 'regular' props into the existing working proplist. */
4393 /* This will merge the old and new props into a new prop db, and
4394 write <cp> commands to the logfile to install the merged
4396 new_base_props = svn_prop__patch(current_base_props, regular_prop_changes,
4398 SVN_ERR(svn_wc__merge_props(&conflict_skel,
4403 NULL /* server_baseprops (update, not merge) */,
4405 current_actual_props,
4406 regular_prop_changes, /* propchanges */
4409 /* We will ALWAYS have properties to save (after a not-dry-run merge). */
4410 SVN_ERR_ASSERT(new_base_props != NULL && new_actual_props != NULL);
4412 /* Merge the text. This will queue some additional work. */
4413 if (!fb->obstruction_found && !fb->edit_obstructed)
4416 err = merge_file(&work_item, &conflict_skel,
4417 &install_pristine, &install_from,
4418 &content_state, fb, current_actual_props,
4419 fb->changed_date, scratch_pool, scratch_pool);
4421 if (err && err->apr_err == SVN_ERR_WC_PATH_ACCESS_DENIED)
4423 if (eb->notify_func)
4425 svn_wc_notify_t *notify =svn_wc_create_notify(
4427 svn_wc_notify_update_skip_access_denied,
4430 notify->kind = svn_node_file;
4433 eb->notify_func(eb->notify_baton, notify, scratch_pool);
4435 svn_error_clear(err);
4437 SVN_ERR(remember_skipped_tree(eb, fb->local_abspath,
4439 fb->skip_this = TRUE;
4441 svn_pool_destroy(fb->pool);
4442 SVN_ERR(maybe_release_dir_info(pdb));
4443 return SVN_NO_ERROR;
4448 all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4453 install_pristine = FALSE;
4454 if (fb->new_text_base_sha1_checksum)
4455 content_state = svn_wc_notify_state_changed;
4457 content_state = svn_wc_notify_state_unchanged;
4460 if (install_pristine)
4462 svn_boolean_t record_fileinfo;
4464 /* If we are installing from the pristine contents, then go ahead and
4465 record the fileinfo. That will be the "proper" values. Installing
4466 from some random file means the fileinfo does NOT correspond to
4467 the pristine (in which case, the fileinfo will be cleared for
4469 record_fileinfo = (install_from == NULL);
4471 SVN_ERR(svn_wc__wq_build_file_install(&work_item,
4475 eb->use_commit_times,
4477 scratch_pool, scratch_pool));
4478 all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4481 else if (lock_state == svn_wc_notify_lock_state_unlocked
4482 && !fb->obstruction_found)
4484 /* If a lock was removed and we didn't update the text contents, we
4485 might need to set the file read-only.
4487 Note: this will also update the executable flag, but ... meh. */
4488 SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, eb->db,
4490 scratch_pool, scratch_pool));
4491 all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4495 if (! install_pristine
4496 && (content_state == svn_wc_notify_state_unchanged))
4498 /* It is safe to keep the current recorded timestamp and size */
4499 keep_recorded_info = TRUE;
4502 /* Clean up any temporary files. */
4504 /* Remove the INSTALL_FROM file, as long as it doesn't refer to the
4506 if (install_from != NULL
4507 && strcmp(install_from, fb->local_abspath) != 0)
4509 SVN_ERR(svn_wc__wq_build_file_remove(&work_item, eb->db,
4510 fb->local_abspath, install_from,
4511 scratch_pool, scratch_pool));
4512 all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4518 /* Adding or updating a BASE node under a locally added node. */
4519 apr_hash_t *fake_actual_props;
4521 if (fb->adding_file)
4522 fake_actual_props = apr_hash_make(scratch_pool);
4524 fake_actual_props = current_base_props;
4526 /* Store the incoming props (sent as propchanges) in new_base_props
4527 and create a set of new actual props to use for notifications */
4528 new_base_props = svn_prop__patch(current_base_props, regular_prop_changes,
4530 SVN_ERR(svn_wc__merge_props(&conflict_skel,
4535 NULL /* server_baseprops (not merging) */,
4536 current_base_props /* pristine_props */,
4537 fake_actual_props /* actual_props */,
4538 regular_prop_changes, /* propchanges */
4542 if (fb->new_text_base_sha1_checksum)
4543 content_state = svn_wc_notify_state_changed;
4545 content_state = svn_wc_notify_state_unchanged;
4548 /* Insert/replace the BASE node with all of the new metadata. */
4550 /* Set the 'checksum' column of the file's BASE_NODE row to
4551 * NEW_TEXT_BASE_SHA1_CHECKSUM. The pristine text identified by that
4552 * checksum is already in the pristine store. */
4553 new_checksum = fb->new_text_base_sha1_checksum;
4555 /* If we don't have a NEW checksum, then the base must not have changed.
4556 Just carry over the old checksum. */
4557 if (new_checksum == NULL)
4558 new_checksum = fb->original_checksum;
4562 SVN_ERR(complete_conflict(conflict_skel,
4565 fb->old_repos_relpath,
4568 svn_node_file, svn_node_file,
4569 fb->pool, scratch_pool));
4571 SVN_ERR(svn_wc__conflict_create_markers(&work_item,
4572 eb->db, fb->local_abspath,
4574 scratch_pool, scratch_pool));
4576 all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4580 /* Any inherited props to be set set for this base node? */
4581 if (eb->wcroot_iprops)
4583 iprops = svn_hash_gets(eb->wcroot_iprops, fb->local_abspath);
4585 /* close_edit may also update iprops for switched nodes, catching
4586 those for which close_directory is never called (e.g. a switch
4587 with no changes). So as a minor optimization we remove any
4588 iprops from the hash so as not to set them again in
4591 svn_hash_sets(eb->wcroot_iprops, fb->local_abspath, NULL);
4594 SVN_ERR(svn_wc__db_base_add_file(eb->db, fb->local_abspath,
4597 eb->repos_root, eb->repos_uuid,
4598 *eb->target_revision,
4604 (dav_prop_changes->nelts > 0)
4605 ? svn_prop_array_to_hash(
4609 (fb->add_existed && fb->adding_file),
4610 (! fb->shadowed) && new_base_props,
4614 (fb->shadowed && fb->obstruction_found),
4619 if (conflict_skel && eb->conflict_func)
4620 SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, fb->local_abspath,
4622 NULL /* merge_options */,
4629 /* Deal with the WORKING tree, based on updates to the BASE tree. */
4631 svn_hash_sets(fb->dir_baton->not_present_files, fb->name, NULL);
4633 /* Send a notification to the callback function. (Skip notifications
4634 about files which were already notified for another reason.) */
4635 if (eb->notify_func && !fb->already_notified
4636 && (fb->edited || lock_state == svn_wc_notify_lock_state_unlocked))
4638 svn_wc_notify_t *notify;
4639 svn_wc_notify_action_t action = svn_wc_notify_update_update;
4643 if (fb->shadowed || fb->edit_obstructed)
4644 action = fb->adding_file
4645 ? svn_wc_notify_update_shadowed_add
4646 : svn_wc_notify_update_shadowed_update;
4647 else if (fb->obstruction_found || fb->add_existed)
4649 if (content_state != svn_wc_notify_state_conflicted)
4650 action = svn_wc_notify_exists;
4652 else if (fb->adding_file)
4654 action = svn_wc_notify_update_add;
4659 SVN_ERR_ASSERT(lock_state == svn_wc_notify_lock_state_unlocked);
4660 action = svn_wc_notify_update_broken_lock;
4663 /* If the file was moved-away, notify for the moved-away node.
4664 * The original location only had its BASE info changed and
4665 * we don't usually notify about such changes. */
4666 notify = svn_wc_create_notify(fb->local_abspath, action, scratch_pool);
4667 notify->kind = svn_node_file;
4668 notify->content_state = content_state;
4669 notify->prop_state = prop_state;
4670 notify->lock_state = lock_state;
4671 notify->revision = *eb->target_revision;
4672 notify->old_revision = fb->old_revision;
4674 /* Fetch the mimetype from the actual properties */
4675 notify->mime_type = svn_prop_get_value(new_actual_props,
4676 SVN_PROP_MIME_TYPE);
4678 eb->notify_func(eb->notify_baton, notify, scratch_pool);
4681 svn_pool_destroy(fb->pool); /* Destroy scratch_pool */
4683 /* We have one less referrer to the directory */
4684 SVN_ERR(maybe_release_dir_info(pdb));
4686 return SVN_NO_ERROR;
4690 /* An svn_delta_editor_t function. */
4691 static svn_error_t *
4692 close_edit(void *edit_baton,
4695 struct edit_baton *eb = edit_baton;
4696 apr_pool_t *scratch_pool = eb->pool;
4698 /* The editor didn't even open the root; we have to take care of
4699 some cleanup stuffs. */
4700 if (! eb->root_opened
4701 && *eb->target_basename == '\0')
4703 /* We need to "un-incomplete" the root directory. */
4704 SVN_ERR(svn_wc__db_temp_op_end_directory_update(eb->db,
4709 /* By definition, anybody "driving" this editor for update or switch
4710 purposes at a *minimum* must have called set_target_revision() at
4711 the outset, and close_edit() at the end -- even if it turned out
4712 that no changes ever had to be made, and open_root() was never
4713 called. That's fine. But regardless, when the edit is over,
4714 this editor needs to make sure that *all* paths have had their
4715 revisions bumped to the new target revision. */
4717 /* Make sure our update target now has the new working revision.
4718 Also, if this was an 'svn switch', then rewrite the target's
4719 url. All of this tweaking might happen recursively! Note
4720 that if eb->target is NULL, that's okay (albeit "sneaky",
4723 /* Extra check: if the update did nothing but make its target
4724 'deleted', then do *not* run cleanup on the target, as it
4725 will only remove the deleted entry! */
4726 if (! eb->target_deleted)
4728 SVN_ERR(svn_wc__db_op_bump_revisions_post_update(eb->db,
4730 eb->requested_depth,
4734 *(eb->target_revision),
4741 if (*eb->target_basename != '\0')
4743 svn_wc__db_status_t status;
4746 /* Note: we are fetching information about the *target*, not anchor.
4747 There is no guarantee that the target has a BASE node.
4750 The node was not present in BASE, but locally-added, and the
4751 update did not create a new BASE node "under" the local-add.
4753 If there is no BASE node for the target, then we certainly don't
4754 have to worry about removing it. */
4755 err = svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL, NULL,
4756 NULL, NULL, NULL, NULL, NULL, NULL,
4757 NULL, NULL, NULL, NULL,
4758 eb->db, eb->target_abspath,
4759 scratch_pool, scratch_pool);
4762 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
4763 return svn_error_trace(err);
4765 svn_error_clear(err);
4767 else if (status == svn_wc__db_status_excluded)
4769 /* There is a small chance that the explicit target of an update/
4770 switch is gone in the repository, in that specific case the
4771 node hasn't been re-added to the BASE tree by this update.
4773 If so, we should get rid of this excluded node now. */
4775 SVN_ERR(svn_wc__db_base_remove(eb->db, eb->target_abspath,
4776 FALSE /* keep_as_working */,
4777 FALSE /* queue_deletes */,
4778 FALSE /* remove_locks */,
4780 NULL, NULL, scratch_pool));
4785 /* The edit is over: run the wq with proper cancel support,
4786 but first kill the handler that would run it on the pool
4787 cleanup at the end of this function. */
4788 apr_pool_cleanup_kill(eb->pool, eb, cleanup_edit_baton);
4790 SVN_ERR(svn_wc__wq_run(eb->db, eb->wcroot_abspath,
4791 eb->cancel_func, eb->cancel_baton,
4794 /* The edit is over, free its pool.
4795 ### No, this is wrong. Who says this editor/baton won't be used
4796 again? But the change is not merely to remove this call. We
4797 should also make eb->pool not be a subpool (see make_editor),
4798 and change callers of svn_client_{checkout,update,switch} to do
4799 better pool management. ### */
4801 svn_pool_destroy(eb->pool);
4803 return SVN_NO_ERROR;
4807 /*** Returning editors. ***/
4809 /* Helper for the three public editor-supplying functions. */
4810 static svn_error_t *
4811 make_editor(svn_revnum_t *target_revision,
4813 const char *anchor_abspath,
4814 const char *target_basename,
4815 apr_hash_t *wcroot_iprops,
4816 svn_boolean_t use_commit_times,
4817 const char *switch_url,
4819 svn_boolean_t depth_is_sticky,
4820 svn_boolean_t allow_unver_obstructions,
4821 svn_boolean_t adds_as_modification,
4822 svn_boolean_t server_performs_filtering,
4823 svn_boolean_t clean_checkout,
4824 svn_wc_notify_func2_t notify_func,
4826 svn_cancel_func_t cancel_func,
4828 svn_wc_dirents_func_t fetch_dirents_func,
4829 void *fetch_dirents_baton,
4830 svn_wc_conflict_resolver_func2_t conflict_func,
4831 void *conflict_baton,
4832 svn_wc_external_update_t external_func,
4833 void *external_baton,
4834 const char *diff3_cmd,
4835 const apr_array_header_t *preserved_exts,
4836 const svn_delta_editor_t **editor,
4838 apr_pool_t *result_pool,
4839 apr_pool_t *scratch_pool)
4841 struct edit_baton *eb;
4843 apr_pool_t *edit_pool = svn_pool_create(result_pool);
4844 svn_delta_editor_t *tree_editor = svn_delta_default_editor(edit_pool);
4845 const svn_delta_editor_t *inner_editor;
4846 const char *repos_root, *repos_uuid;
4847 struct svn_wc__shim_fetch_baton_t *sfb;
4848 svn_delta_shim_callbacks_t *shim_callbacks =
4849 svn_delta_shim_callbacks_default(edit_pool);
4851 /* An unknown depth can't be sticky. */
4852 if (depth == svn_depth_unknown)
4853 depth_is_sticky = FALSE;
4855 /* Get the anchor's repository root and uuid. The anchor must already exist
4857 SVN_ERR(svn_wc__db_scan_base_repos(NULL, &repos_root, &repos_uuid,
4859 result_pool, scratch_pool));
4861 /* With WC-NG we need a valid repository root */
4862 SVN_ERR_ASSERT(repos_root != NULL && repos_uuid != NULL);
4864 /* Disallow a switch operation to change the repository root of the target,
4865 if that is known. */
4866 if (switch_url && !svn_uri__is_ancestor(repos_root, switch_url))
4867 return svn_error_createf(SVN_ERR_WC_INVALID_SWITCH, NULL,
4868 _("'%s'\nis not the same repository as\n'%s'"),
4869 switch_url, repos_root);
4871 /* Construct an edit baton. */
4872 eb = apr_pcalloc(edit_pool, sizeof(*eb));
4873 eb->pool = edit_pool;
4874 eb->use_commit_times = use_commit_times;
4875 eb->target_revision = target_revision;
4876 eb->repos_root = repos_root;
4877 eb->repos_uuid = repos_uuid;
4879 eb->target_basename = target_basename;
4880 eb->anchor_abspath = anchor_abspath;
4881 eb->wcroot_iprops = wcroot_iprops;
4883 SVN_ERR(svn_wc__db_get_wcroot(&eb->wcroot_abspath, db, anchor_abspath,
4884 edit_pool, scratch_pool));
4887 eb->switch_relpath =
4888 svn_uri_skip_ancestor(repos_root, switch_url, scratch_pool);
4890 eb->switch_relpath = NULL;
4892 if (svn_path_is_empty(target_basename))
4893 eb->target_abspath = eb->anchor_abspath;
4895 eb->target_abspath = svn_dirent_join(eb->anchor_abspath, target_basename,
4898 eb->requested_depth = depth;
4899 eb->depth_is_sticky = depth_is_sticky;
4900 eb->notify_func = notify_func;
4901 eb->notify_baton = notify_baton;
4902 eb->external_func = external_func;
4903 eb->external_baton = external_baton;
4904 eb->diff3_cmd = diff3_cmd;
4905 eb->cancel_func = cancel_func;
4906 eb->cancel_baton = cancel_baton;
4907 eb->conflict_func = conflict_func;
4908 eb->conflict_baton = conflict_baton;
4909 eb->allow_unver_obstructions = allow_unver_obstructions;
4910 eb->adds_as_modification = adds_as_modification;
4911 eb->clean_checkout = clean_checkout;
4912 eb->skipped_trees = apr_hash_make(edit_pool);
4913 eb->dir_dirents = apr_hash_make(edit_pool);
4914 eb->ext_patterns = preserved_exts;
4916 apr_pool_cleanup_register(edit_pool, eb, cleanup_edit_baton,
4917 apr_pool_cleanup_null);
4919 /* Construct an editor. */
4920 tree_editor->set_target_revision = set_target_revision;
4921 tree_editor->open_root = open_root;
4922 tree_editor->delete_entry = delete_entry;
4923 tree_editor->add_directory = add_directory;
4924 tree_editor->open_directory = open_directory;
4925 tree_editor->change_dir_prop = change_dir_prop;
4926 tree_editor->close_directory = close_directory;
4927 tree_editor->absent_directory = absent_directory;
4928 tree_editor->add_file = add_file;
4929 tree_editor->open_file = open_file;
4930 tree_editor->apply_textdelta = apply_textdelta;
4931 tree_editor->change_file_prop = change_file_prop;
4932 tree_editor->close_file = close_file;
4933 tree_editor->absent_file = absent_file;
4934 tree_editor->close_edit = close_edit;
4936 /* Fiddle with the type system. */
4937 inner_editor = tree_editor;
4940 if (!depth_is_sticky
4941 && depth != svn_depth_unknown
4942 && svn_depth_empty <= depth && depth < svn_depth_infinity
4943 && fetch_dirents_func)
4945 /* We are asked to perform an update at a depth less than the ambient
4946 depth. In this case the update won't describe additions that would
4947 have been reported if we updated at the ambient depth. */
4949 svn_node_kind_t dir_kind;
4950 svn_wc__db_status_t dir_status;
4951 const char *dir_repos_relpath;
4952 svn_depth_t dir_depth;
4954 /* we have to do this on the target of the update, not the anchor */
4955 err = svn_wc__db_base_get_info(&dir_status, &dir_kind, NULL,
4956 &dir_repos_relpath, NULL, NULL, NULL,
4957 NULL, NULL, &dir_depth, NULL, NULL, NULL,
4959 db, eb->target_abspath,
4960 scratch_pool, scratch_pool);
4963 && dir_kind == svn_node_dir
4964 && dir_status == svn_wc__db_status_normal)
4966 if (dir_depth > depth)
4968 apr_hash_t *dirents;
4970 /* If we switch, we should look at the new relpath */
4971 if (eb->switch_relpath)
4972 dir_repos_relpath = eb->switch_relpath;
4974 SVN_ERR(fetch_dirents_func(fetch_dirents_baton, &dirents,
4975 repos_root, dir_repos_relpath,
4976 edit_pool, scratch_pool));
4978 if (dirents != NULL && apr_hash_count(dirents))
4979 svn_hash_sets(eb->dir_dirents,
4980 apr_pstrdup(edit_pool, dir_repos_relpath),
4984 if (depth == svn_depth_immediates)
4986 /* Worst case scenario of issue #3569 fix: We have to do the
4987 same for all existing subdirs, but then we check for
4989 const apr_array_header_t *children;
4990 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
4992 SVN_ERR(svn_wc__db_base_get_children(&children, db,
4997 for (i = 0; i < children->nelts; i++)
4999 const char *child_abspath;
5000 const char *child_name;
5002 svn_pool_clear(iterpool);
5004 child_name = APR_ARRAY_IDX(children, i, const char *);
5006 child_abspath = svn_dirent_join(eb->target_abspath,
5007 child_name, iterpool);
5009 SVN_ERR(svn_wc__db_base_get_info(&dir_status, &dir_kind,
5010 NULL, &dir_repos_relpath,
5011 NULL, NULL, NULL, NULL,
5012 NULL, &dir_depth, NULL,
5013 NULL, NULL, NULL, NULL,
5016 iterpool, iterpool));
5018 if (dir_kind == svn_node_dir
5019 && dir_status == svn_wc__db_status_normal
5020 && dir_depth > svn_depth_empty)
5022 apr_hash_t *dirents;
5024 /* If we switch, we should look at the new relpath */
5025 if (eb->switch_relpath)
5026 dir_repos_relpath = svn_relpath_join(
5028 child_name, iterpool);
5030 SVN_ERR(fetch_dirents_func(fetch_dirents_baton, &dirents,
5031 repos_root, dir_repos_relpath,
5032 edit_pool, iterpool));
5034 if (dirents != NULL && apr_hash_count(dirents))
5035 svn_hash_sets(eb->dir_dirents,
5036 apr_pstrdup(edit_pool,
5043 else if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
5044 svn_error_clear(err);
5049 /* We need to limit the scope of our operation to the ambient depths
5050 present in the working copy already, but only if the requested
5051 depth is not sticky. If a depth was explicitly requested,
5052 libsvn_delta/depth_filter_editor.c will ensure that we never see
5053 editor calls that extend beyond the scope of the requested depth.
5054 But even what we do so might extend beyond the scope of our
5055 ambient depth. So we use another filtering editor to avoid
5056 modifying the ambient working copy depth when not asked to do so.
5057 (This can also be skipped if the server understands depth.) */
5058 if (!server_performs_filtering
5059 && !depth_is_sticky)
5060 SVN_ERR(svn_wc__ambient_depth_filter_editor(&inner_editor,
5069 SVN_ERR(svn_delta_get_cancellation_editor(cancel_func,
5077 sfb = apr_palloc(result_pool, sizeof(*sfb));
5079 sfb->base_abspath = eb->anchor_abspath;
5080 sfb->fetch_base = TRUE;
5082 shim_callbacks->fetch_kind_func = svn_wc__fetch_kind_func;
5083 shim_callbacks->fetch_props_func = svn_wc__fetch_props_func;
5084 shim_callbacks->fetch_base_func = svn_wc__fetch_base_func;
5085 shim_callbacks->fetch_baton = sfb;
5087 SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton,
5088 NULL, NULL, shim_callbacks,
5089 result_pool, scratch_pool));
5091 return SVN_NO_ERROR;
5096 svn_wc__get_update_editor(const svn_delta_editor_t **editor,
5098 svn_revnum_t *target_revision,
5099 svn_wc_context_t *wc_ctx,
5100 const char *anchor_abspath,
5101 const char *target_basename,
5102 apr_hash_t *wcroot_iprops,
5103 svn_boolean_t use_commit_times,
5105 svn_boolean_t depth_is_sticky,
5106 svn_boolean_t allow_unver_obstructions,
5107 svn_boolean_t adds_as_modification,
5108 svn_boolean_t server_performs_filtering,
5109 svn_boolean_t clean_checkout,
5110 const char *diff3_cmd,
5111 const apr_array_header_t *preserved_exts,
5112 svn_wc_dirents_func_t fetch_dirents_func,
5113 void *fetch_dirents_baton,
5114 svn_wc_conflict_resolver_func2_t conflict_func,
5115 void *conflict_baton,
5116 svn_wc_external_update_t external_func,
5117 void *external_baton,
5118 svn_cancel_func_t cancel_func,
5120 svn_wc_notify_func2_t notify_func,
5122 apr_pool_t *result_pool,
5123 apr_pool_t *scratch_pool)
5125 return make_editor(target_revision, wc_ctx->db, anchor_abspath,
5126 target_basename, wcroot_iprops, use_commit_times,
5127 NULL, depth, depth_is_sticky, allow_unver_obstructions,
5128 adds_as_modification, server_performs_filtering,
5130 notify_func, notify_baton,
5131 cancel_func, cancel_baton,
5132 fetch_dirents_func, fetch_dirents_baton,
5133 conflict_func, conflict_baton,
5134 external_func, external_baton,
5135 diff3_cmd, preserved_exts, editor, edit_baton,
5136 result_pool, scratch_pool);
5140 svn_wc__get_switch_editor(const svn_delta_editor_t **editor,
5142 svn_revnum_t *target_revision,
5143 svn_wc_context_t *wc_ctx,
5144 const char *anchor_abspath,
5145 const char *target_basename,
5146 const char *switch_url,
5147 apr_hash_t *wcroot_iprops,
5148 svn_boolean_t use_commit_times,
5150 svn_boolean_t depth_is_sticky,
5151 svn_boolean_t allow_unver_obstructions,
5152 svn_boolean_t server_performs_filtering,
5153 const char *diff3_cmd,
5154 const apr_array_header_t *preserved_exts,
5155 svn_wc_dirents_func_t fetch_dirents_func,
5156 void *fetch_dirents_baton,
5157 svn_wc_conflict_resolver_func2_t conflict_func,
5158 void *conflict_baton,
5159 svn_wc_external_update_t external_func,
5160 void *external_baton,
5161 svn_cancel_func_t cancel_func,
5163 svn_wc_notify_func2_t notify_func,
5165 apr_pool_t *result_pool,
5166 apr_pool_t *scratch_pool)
5168 SVN_ERR_ASSERT(switch_url && svn_uri_is_canonical(switch_url, scratch_pool));
5170 return make_editor(target_revision, wc_ctx->db, anchor_abspath,
5171 target_basename, wcroot_iprops, use_commit_times,
5173 depth, depth_is_sticky, allow_unver_obstructions,
5174 FALSE /* adds_as_modification */,
5175 server_performs_filtering,
5176 FALSE /* clean_checkout */,
5177 notify_func, notify_baton,
5178 cancel_func, cancel_baton,
5179 fetch_dirents_func, fetch_dirents_baton,
5180 conflict_func, conflict_baton,
5181 external_func, external_baton,
5182 diff3_cmd, preserved_exts,
5184 result_pool, scratch_pool);
5189 /* ### Note that this function is completely different from the rest of the
5190 update editor in what it updates. The update editor changes only BASE
5191 and ACTUAL and this function just changes WORKING and ACTUAL.
5193 In the entries world this function shared a lot of code with the
5194 update editor but in the wonderful new WC-NG world it will probably
5195 do more and more by itself and would be more logically grouped with
5196 the add/copy functionality in adm_ops.c and copy.c. */
5198 svn_wc_add_repos_file4(svn_wc_context_t *wc_ctx,
5199 const char *local_abspath,
5200 svn_stream_t *new_base_contents,
5201 svn_stream_t *new_contents,
5202 apr_hash_t *new_base_props,
5203 apr_hash_t *new_props,
5204 const char *copyfrom_url,
5205 svn_revnum_t copyfrom_rev,
5206 svn_cancel_func_t cancel_func,
5208 apr_pool_t *scratch_pool)
5210 svn_wc__db_t *db = wc_ctx->db;
5211 const char *dir_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
5212 svn_wc__db_status_t status;
5213 svn_node_kind_t kind;
5214 const char *tmp_text_base_abspath;
5215 svn_checksum_t *new_text_base_md5_checksum;
5216 svn_checksum_t *new_text_base_sha1_checksum;
5217 const char *source_abspath = NULL;
5218 svn_skel_t *all_work_items = NULL;
5219 svn_skel_t *work_item;
5220 const char *repos_root_url;
5221 const char *repos_uuid;
5222 const char *original_repos_relpath;
5223 svn_revnum_t changed_rev;
5224 apr_time_t changed_date;
5225 const char *changed_author;
5227 apr_pool_t *pool = scratch_pool;
5229 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5230 SVN_ERR_ASSERT(new_base_contents != NULL);
5231 SVN_ERR_ASSERT(new_base_props != NULL);
5233 /* We should have a write lock on this file's parent directory. */
5234 SVN_ERR(svn_wc__write_check(db, dir_abspath, pool));
5236 err = svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5237 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5238 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5240 db, local_abspath, scratch_pool, scratch_pool);
5242 if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
5243 return svn_error_trace(err);
5245 svn_error_clear(err);
5249 case svn_wc__db_status_not_present:
5250 case svn_wc__db_status_deleted:
5253 return svn_error_createf(SVN_ERR_ENTRY_EXISTS, NULL,
5254 _("Node '%s' exists."),
5255 svn_dirent_local_style(local_abspath,
5259 SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, &repos_root_url,
5260 &repos_uuid, NULL, NULL, NULL, NULL, NULL, NULL,
5261 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5262 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5263 db, dir_abspath, scratch_pool, scratch_pool));
5267 case svn_wc__db_status_normal:
5268 case svn_wc__db_status_added:
5270 case svn_wc__db_status_deleted:
5272 svn_error_createf(SVN_ERR_WC_SCHEDULE_CONFLICT, NULL,
5273 _("Can't add '%s' to a parent directory"
5274 " scheduled for deletion"),
5275 svn_dirent_local_style(local_abspath,
5278 return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, err,
5279 _("Can't find parent directory's node while"
5280 " trying to add '%s'"),
5281 svn_dirent_local_style(local_abspath,
5284 if (kind != svn_node_dir)
5285 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
5286 _("Can't schedule an addition of '%s'"
5287 " below a not-directory node"),
5288 svn_dirent_local_style(local_abspath,
5291 /* Fabricate the anticipated new URL of the target and check the
5292 copyfrom URL to be in the same repository. */
5293 if (copyfrom_url != NULL)
5295 /* Find the repository_root via the parent directory, which
5296 is always versioned before this function is called */
5298 if (!repos_root_url)
5300 /* The parent is an addition, scan upwards to find the right info */
5301 SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, NULL,
5302 &repos_root_url, &repos_uuid,
5303 NULL, NULL, NULL, NULL,
5304 wc_ctx->db, dir_abspath,
5305 scratch_pool, scratch_pool));
5307 SVN_ERR_ASSERT(repos_root_url);
5309 original_repos_relpath =
5310 svn_uri_skip_ancestor(repos_root_url, copyfrom_url, scratch_pool);
5312 if (!original_repos_relpath)
5313 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
5314 _("Copyfrom-url '%s' has different repository"
5316 copyfrom_url, repos_root_url);
5320 original_repos_relpath = NULL;
5321 copyfrom_rev = SVN_INVALID_REVNUM; /* Just to be sure. */
5324 /* Set CHANGED_* to reflect the entry props in NEW_BASE_PROPS, and
5325 filter NEW_BASE_PROPS so it contains only regular props. */
5327 apr_array_header_t *regular_props;
5328 apr_array_header_t *entry_props;
5330 SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(new_base_props, pool),
5331 &entry_props, NULL, ®ular_props,
5334 /* Put regular props back into a hash table. */
5335 new_base_props = svn_prop_array_to_hash(regular_props, pool);
5337 /* Get the change_* info from the entry props. */
5338 SVN_ERR(accumulate_last_change(&changed_rev,
5341 entry_props, pool, pool));
5344 /* Copy NEW_BASE_CONTENTS into a temporary file so our log can refer to
5345 it, and set TMP_TEXT_BASE_ABSPATH to its path. Compute its
5346 NEW_TEXT_BASE_MD5_CHECKSUM and NEW_TEXT_BASE_SHA1_CHECKSUM as we copy. */
5348 svn_stream_t *tmp_base_contents;
5350 SVN_ERR(svn_wc__open_writable_base(&tmp_base_contents,
5351 &tmp_text_base_abspath,
5352 &new_text_base_md5_checksum,
5353 &new_text_base_sha1_checksum,
5354 wc_ctx->db, local_abspath,
5356 SVN_ERR(svn_stream_copy3(new_base_contents, tmp_base_contents,
5357 cancel_func, cancel_baton, pool));
5360 /* If the caller gave us a new working file, copy it to a safe (temporary)
5361 location and set SOURCE_ABSPATH to that path. We'll then translate/copy
5362 that into place after the node's state has been created. */
5365 const char *temp_dir_abspath;
5366 svn_stream_t *tmp_contents;
5368 SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, db,
5369 local_abspath, pool, pool));
5370 SVN_ERR(svn_stream_open_unique(&tmp_contents, &source_abspath,
5371 temp_dir_abspath, svn_io_file_del_none,
5373 SVN_ERR(svn_stream_copy3(new_contents, tmp_contents,
5374 cancel_func, cancel_baton, pool));
5377 /* Install new text base for copied files. Added files do NOT have a
5379 if (copyfrom_url != NULL)
5381 SVN_ERR(svn_wc__db_pristine_install(db, tmp_text_base_abspath,
5382 new_text_base_sha1_checksum,
5383 new_text_base_md5_checksum, pool));
5387 /* ### There's something wrong around here. Sometimes (merge from a
5388 foreign repository, at least) we are called with copyfrom_url =
5389 NULL and an empty new_base_contents (and an empty set of
5390 new_base_props). Why an empty "new base"?
5392 That happens in merge_tests.py 54,87,88,89,143.
5394 In that case, having been given this supposed "new base" file, we
5395 copy it and calculate its checksum but do not install it. Why?
5398 To crudely work around one issue with this, that we shouldn't
5399 record a checksum in the database if we haven't installed the
5400 corresponding pristine text, for now we'll just set the checksum
5403 The proper solution is probably more like: the caller should pass
5404 NULL for the missing information, and this function should learn to
5407 new_text_base_sha1_checksum = NULL;
5408 new_text_base_md5_checksum = NULL;
5411 /* For added files without NEW_CONTENTS, then generate the working file
5412 from the provided "pristine" contents. */
5413 if (new_contents == NULL && copyfrom_url == NULL)
5414 source_abspath = tmp_text_base_abspath;
5417 svn_boolean_t record_fileinfo;
5419 /* If new contents were provided, then we do NOT want to record the
5420 file information. We assume the new contents do not match the
5421 "proper" values for RECORDED_SIZE and RECORDED_TIME. */
5422 record_fileinfo = (new_contents == NULL);
5424 /* Install the working copy file (with appropriate translation) from
5425 the appropriate source. SOURCE_ABSPATH will be NULL, indicating an
5426 installation from the pristine (available for copied/moved files),
5427 or it will specify a temporary file where we placed a "pristine"
5428 (for an added file) or a detranslated local-mods file. */
5429 SVN_ERR(svn_wc__wq_build_file_install(&work_item,
5432 FALSE /* use_commit_times */,
5435 all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool);
5437 /* If we installed from somewhere besides the official pristine, then
5438 it is a temporary file, which needs to be removed. */
5439 if (source_abspath != NULL)
5441 SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, local_abspath,
5444 all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool);
5448 /* ### ideally, we would have a single DB operation, and queue the work
5449 ### items on that. for now, we'll queue them with the second call. */
5451 SVN_ERR(svn_wc__db_op_copy_file(db, local_abspath,
5456 original_repos_relpath,
5457 original_repos_relpath ? repos_root_url
5459 original_repos_relpath ? repos_uuid : NULL,
5461 new_text_base_sha1_checksum,
5464 FALSE /* is_move */,
5465 NULL /* conflict */,
5469 return svn_error_trace(svn_wc__wq_run(db, dir_abspath,
5470 cancel_func, cancel_baton,
5475 svn_wc__complete_directory_add(svn_wc_context_t *wc_ctx,
5476 const char *local_abspath,
5477 apr_hash_t *new_original_props,
5478 const char *copyfrom_url,
5479 svn_revnum_t copyfrom_rev,
5480 apr_pool_t *scratch_pool)
5482 svn_wc__db_status_t status;
5483 svn_node_kind_t kind;
5484 const char *original_repos_relpath;
5485 const char *original_root_url;
5486 const char *original_uuid;
5487 svn_boolean_t had_props;
5488 svn_boolean_t props_mod;
5490 svn_revnum_t original_revision;
5491 svn_revnum_t changed_rev;
5492 apr_time_t changed_date;
5493 const char *changed_author;
5495 SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
5496 NULL, NULL, NULL, NULL, NULL,
5497 &original_repos_relpath, &original_root_url,
5498 &original_uuid, &original_revision, NULL, NULL,
5499 NULL, NULL, NULL, NULL, &had_props, &props_mod,
5501 wc_ctx->db, local_abspath,
5502 scratch_pool, scratch_pool));
5504 if (status != svn_wc__db_status_added
5505 || kind != svn_node_dir
5508 || !original_repos_relpath)
5510 return svn_error_createf(
5511 SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
5512 _("'%s' is not an unmodified copied directory"),
5513 svn_dirent_local_style(local_abspath, scratch_pool));
5515 if (original_revision != copyfrom_rev
5516 || strcmp(copyfrom_url,
5517 svn_path_url_add_component2(original_root_url,
5518 original_repos_relpath,
5521 return svn_error_createf(
5522 SVN_ERR_WC_COPYFROM_PATH_NOT_FOUND, NULL,
5523 _("Copyfrom '%s' doesn't match original location of '%s'"),
5525 svn_dirent_local_style(local_abspath, scratch_pool));
5529 apr_array_header_t *regular_props;
5530 apr_array_header_t *entry_props;
5532 SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(new_original_props,
5534 &entry_props, NULL, ®ular_props,
5537 /* Put regular props back into a hash table. */
5538 new_original_props = svn_prop_array_to_hash(regular_props, scratch_pool);
5540 /* Get the change_* info from the entry props. */
5541 SVN_ERR(accumulate_last_change(&changed_rev,
5544 entry_props, scratch_pool, scratch_pool));
5547 return svn_error_trace(
5548 svn_wc__db_op_copy_dir(wc_ctx->db, local_abspath,
5550 changed_rev, changed_date, changed_author,
5551 original_repos_relpath, original_root_url,
5552 original_uuid, original_revision,
5553 NULL /* children */,
5555 FALSE /* is_move */,
5556 NULL /* conflict */,
5557 NULL /* work_items */,