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 /* So this deletion wasn't just a deletion, it is actually a
2129 replacement. Let's install a better tree conflict. */
2131 /* ### Should store the conflict in DB to allow reinstalling
2132 ### with theoretically more data in close_directory() */
2134 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
2138 db->pool, db->pool));
2140 tree_conflict = svn_wc__conflict_skel_create(db->pool);
2142 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
2144 eb->db, db->local_abspath,
2145 reason, svn_wc_conflict_action_replace,
2147 db->pool, db->pool));
2149 /* And now stop checking for conflicts here and just perform
2150 a shadowed update */
2151 db->edit_conflict = tree_conflict; /* Cache for close_directory */
2152 tree_conflict = NULL; /* No direct notification */
2153 db->shadowed = TRUE; /* Just continue */
2154 conflicted = FALSE; /* No skip */
2157 SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
2158 eb->db, db->local_abspath, pool));
2161 /* Now the "usual" behaviour if already conflicted. Skip it. */
2164 /* Record this conflict so that its descendants are skipped silently. */
2165 SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
2167 db->skip_this = TRUE;
2168 db->already_notified = TRUE;
2170 /* We skip this node, but once the update completes the parent node will
2171 be updated to the new revision. So a future recursive update of the
2172 parent will not bring in this new node as the revision of the parent
2173 describes to the repository that all children are available.
2175 To resolve this problem, we add a not-present node to allow bringing
2176 the node in once this conflict is resolved.
2178 Note that we can safely assume that no present base node exists,
2179 because then we would not have received an add_directory.
2181 SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db, db->local_abspath,
2185 *eb->target_revision,
2190 /* ### TODO: Also print victim_path in the skip msg. */
2191 do_notification(eb, db->local_abspath, svn_node_dir,
2192 svn_wc_notify_skip_conflicted, pool);
2193 return SVN_NO_ERROR;
2195 else if (conflict_ignored)
2197 db->shadowed = TRUE;
2202 /* Nothing to check; does not and will not exist in working copy */
2204 else if (versioned_locally_and_present)
2206 /* What to do with a versioned or schedule-add dir:
2208 A dir already added without history is OK. Set add_existed
2209 so that user notification is delayed until after any prop
2210 conflicts have been found.
2212 An existing versioned dir is an error. In the future we may
2213 relax this restriction and simply update such dirs.
2215 A dir added with history is a tree conflict. */
2217 svn_boolean_t local_is_non_dir;
2218 svn_wc__db_status_t add_status = svn_wc__db_status_normal;
2220 /* Is the local add a copy? */
2221 if (status == svn_wc__db_status_added)
2222 SVN_ERR(svn_wc__db_scan_addition(&add_status, NULL, NULL, NULL, NULL,
2223 NULL, NULL, NULL, NULL,
2224 eb->db, db->local_abspath,
2228 /* Is there *something* that is not a dir? */
2229 local_is_non_dir = (wc_kind != svn_node_dir
2230 && status != svn_wc__db_status_deleted);
2232 /* Do tree conflict checking if
2233 * - if there is a local copy.
2234 * - if this is a switch operation
2235 * - the node kinds mismatch
2237 * During switch, local adds at the same path as incoming adds get
2238 * "lost" in that switching back to the original will no longer have the
2239 * local add. So switch always alerts the user with a tree conflict. */
2240 if (!eb->adds_as_modification
2242 || add_status != svn_wc__db_status_added)
2244 SVN_ERR(check_tree_conflict(&tree_conflict, eb,
2246 status, FALSE, svn_node_none,
2247 svn_wc_conflict_action_add,
2251 if (tree_conflict == NULL)
2252 db->add_existed = TRUE; /* Take over WORKING */
2254 db->shadowed = TRUE; /* Only update BASE */
2256 else if (kind != svn_node_none)
2258 /* There's an unversioned node at this path. */
2259 db->obstruction_found = TRUE;
2261 /* Unversioned, obstructing dirs are handled by prop merge/conflict,
2262 * if unversioned obstructions are allowed. */
2263 if (! (kind == svn_node_dir && eb->allow_unver_obstructions))
2265 /* Bring in the node as deleted */ /* ### Obstructed Conflict */
2266 db->shadowed = TRUE;
2268 /* Mark a conflict */
2269 tree_conflict = svn_wc__conflict_skel_create(db->pool);
2271 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
2273 eb->db, db->local_abspath,
2274 svn_wc_conflict_reason_unversioned,
2275 svn_wc_conflict_action_add, NULL,
2277 db->edit_conflict = tree_conflict;
2282 SVN_ERR(complete_conflict(tree_conflict, eb, db->local_abspath,
2283 db->old_repos_relpath, db->old_revision,
2289 SVN_ERR(svn_wc__db_base_add_incomplete_directory(
2290 eb->db, db->local_abspath,
2294 *eb->target_revision,
2296 (db->shadowed && db->obstruction_found),
2298 && status == svn_wc__db_status_added),
2299 tree_conflict, NULL,
2302 /* Make sure there is a real directory at LOCAL_ABSPATH, unless we are just
2305 SVN_ERR(svn_wc__ensure_directory(db->local_abspath, pool));
2307 if (tree_conflict != NULL)
2309 if (eb->conflict_func)
2310 SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, db->local_abspath,
2312 NULL /* merge_options */,
2319 db->already_notified = TRUE;
2320 do_notification(eb, db->local_abspath, svn_node_dir,
2321 svn_wc_notify_tree_conflict, pool);
2325 /* If this add was obstructed by dir scheduled for addition without
2326 history let close_directory() handle the notification because there
2327 might be properties to deal with. If PATH was added inside a locally
2328 deleted tree, then suppress notification, a tree conflict was already
2330 if (eb->notify_func && !db->already_notified && !db->add_existed)
2332 svn_wc_notify_action_t action;
2335 action = svn_wc_notify_update_shadowed_add;
2336 else if (db->obstruction_found || db->add_existed)
2337 action = svn_wc_notify_exists;
2339 action = svn_wc_notify_update_add;
2341 db->already_notified = TRUE;
2343 do_notification(eb, db->local_abspath, svn_node_dir, action, pool);
2346 return SVN_NO_ERROR;
2349 /* An svn_delta_editor_t function. */
2350 static svn_error_t *
2351 open_directory(const char *path,
2353 svn_revnum_t base_revision,
2357 struct dir_baton *db, *pb = parent_baton;
2358 struct edit_baton *eb = pb->edit_baton;
2359 svn_boolean_t have_work;
2360 svn_boolean_t conflicted;
2361 svn_boolean_t conflict_ignored = FALSE;
2362 svn_skel_t *tree_conflict = NULL;
2363 svn_wc__db_status_t status, base_status;
2364 svn_node_kind_t wc_kind;
2366 SVN_ERR(make_dir_baton(&db, path, eb, pb, FALSE, pool));
2370 return SVN_NO_ERROR;
2372 /* Detect obstructing working copies */
2374 svn_boolean_t is_root;
2376 SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, db->local_abspath,
2381 /* Just skip this node; a future update will handle it */
2382 SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
2383 db->skip_this = TRUE;
2384 db->already_notified = TRUE;
2386 do_notification(eb, db->local_abspath, svn_node_dir,
2387 svn_wc_notify_update_skip_obstruction, pool);
2389 return SVN_NO_ERROR;
2393 /* We should have a write lock on every directory touched. */
2394 SVN_ERR(svn_wc__write_check(eb->db, db->local_abspath, pool));
2396 SVN_ERR(svn_wc__db_read_info(&status, &wc_kind, &db->old_revision,
2397 &db->old_repos_relpath, NULL, NULL,
2398 &db->changed_rev, &db->changed_date,
2399 &db->changed_author, &db->ambient_depth,
2400 NULL, NULL, NULL, NULL,
2401 NULL, NULL, NULL, NULL, NULL, NULL,
2402 &conflicted, NULL, NULL, NULL,
2403 NULL, NULL, &have_work,
2404 eb->db, db->local_abspath,
2408 base_status = status;
2410 SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &db->old_revision,
2411 &db->old_repos_relpath, NULL, NULL,
2412 &db->changed_rev, &db->changed_date,
2413 &db->changed_author, &db->ambient_depth,
2414 NULL, NULL, NULL, NULL, NULL, NULL,
2415 eb->db, db->local_abspath,
2418 db->was_incomplete = (base_status == svn_wc__db_status_incomplete);
2420 /* Is this path a conflict victim? */
2422 conflicted = FALSE; /* Conflict applies to WORKING */
2423 else if (conflicted)
2424 SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
2425 eb->db, db->local_abspath, pool));
2428 SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
2430 db->skip_this = TRUE;
2431 db->already_notified = TRUE;
2433 do_notification(eb, db->local_abspath, svn_node_unknown,
2434 svn_wc_notify_skip_conflicted, pool);
2436 return SVN_NO_ERROR;
2438 else if (conflict_ignored)
2440 db->shadowed = TRUE;
2443 /* Is this path a fresh tree conflict victim? If so, skip the tree
2444 with one notification. */
2446 /* Check for conflicts only when we haven't already recorded
2447 * a tree-conflict on a parent node. */
2449 SVN_ERR(check_tree_conflict(&tree_conflict, eb, db->local_abspath,
2450 status, TRUE, svn_node_dir,
2451 svn_wc_conflict_action_edit,
2454 /* Remember the roots of any locally deleted trees. */
2455 if (tree_conflict != NULL)
2457 svn_wc_conflict_reason_t reason;
2458 db->edit_conflict = tree_conflict;
2459 /* Other modifications wouldn't be a tree conflict */
2461 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
2462 eb->db, db->local_abspath,
2464 db->pool, db->pool));
2465 SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_deleted
2466 || reason == svn_wc_conflict_reason_moved_away
2467 || reason == svn_wc_conflict_reason_replaced
2468 || reason == svn_wc_conflict_reason_obstructed);
2470 /* Continue updating BASE */
2471 if (reason == svn_wc_conflict_reason_obstructed)
2472 db->edit_obstructed = TRUE;
2474 db->shadowed = TRUE;
2477 /* Mark directory as being at target_revision and URL, but incomplete. */
2478 SVN_ERR(svn_wc__db_temp_op_start_directory_update(eb->db, db->local_abspath,
2480 *eb->target_revision,
2483 return SVN_NO_ERROR;
2487 /* An svn_delta_editor_t function. */
2488 static svn_error_t *
2489 change_dir_prop(void *dir_baton,
2491 const svn_string_t *value,
2494 svn_prop_t *propchange;
2495 struct dir_baton *db = dir_baton;
2498 return SVN_NO_ERROR;
2500 propchange = apr_array_push(db->propchanges);
2501 propchange->name = apr_pstrdup(db->pool, name);
2502 propchange->value = value ? svn_string_dup(value, db->pool) : NULL;
2504 if (!db->edited && svn_property_kind2(name) == svn_prop_regular_kind)
2505 SVN_ERR(mark_directory_edited(db, pool));
2507 return SVN_NO_ERROR;
2510 /* If any of the svn_prop_t objects in PROPCHANGES represents a change
2511 to the SVN_PROP_EXTERNALS property, return that change, else return
2512 null. If PROPCHANGES contains more than one such change, return
2514 static const svn_prop_t *
2515 externals_prop_changed(const apr_array_header_t *propchanges)
2519 for (i = 0; i < propchanges->nelts; i++)
2521 const svn_prop_t *p = &(APR_ARRAY_IDX(propchanges, i, svn_prop_t));
2522 if (strcmp(p->name, SVN_PROP_EXTERNALS) == 0)
2531 /* An svn_delta_editor_t function. */
2532 static svn_error_t *
2533 close_directory(void *dir_baton,
2536 struct dir_baton *db = dir_baton;
2537 struct edit_baton *eb = db->edit_baton;
2538 svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
2539 apr_array_header_t *entry_prop_changes;
2540 apr_array_header_t *dav_prop_changes;
2541 apr_array_header_t *regular_prop_changes;
2542 apr_hash_t *base_props;
2543 apr_hash_t *actual_props;
2544 apr_hash_t *new_base_props = NULL;
2545 apr_hash_t *new_actual_props = NULL;
2546 svn_revnum_t new_changed_rev = SVN_INVALID_REVNUM;
2547 apr_time_t new_changed_date = 0;
2548 const char *new_changed_author = NULL;
2549 apr_pool_t *scratch_pool = db->pool;
2550 svn_skel_t *all_work_items = NULL;
2551 svn_skel_t *conflict_skel = NULL;
2553 /* Skip if we're in a conflicted tree. */
2556 /* Allow the parent to complete its update. */
2557 SVN_ERR(maybe_release_dir_info(db));
2559 return SVN_NO_ERROR;
2563 conflict_skel = db->edit_conflict;
2565 SVN_ERR(svn_categorize_props(db->propchanges, &entry_prop_changes,
2566 &dav_prop_changes, ®ular_prop_changes, pool));
2568 /* Fetch the existing properties. */
2569 if ((!db->adding_dir || db->add_existed)
2572 SVN_ERR(svn_wc__get_actual_props(&actual_props,
2573 eb->db, db->local_abspath,
2574 scratch_pool, scratch_pool));
2577 actual_props = apr_hash_make(pool);
2579 if (db->add_existed)
2581 /* This node already exists. Grab the current pristine properties. */
2582 SVN_ERR(svn_wc__db_read_pristine_props(&base_props,
2583 eb->db, db->local_abspath,
2584 scratch_pool, scratch_pool));
2586 else if (!db->adding_dir)
2588 /* Get the BASE properties for proper merging. */
2589 SVN_ERR(svn_wc__db_base_get_props(&base_props,
2590 eb->db, db->local_abspath,
2591 scratch_pool, scratch_pool));
2594 base_props = apr_hash_make(pool);
2596 /* An incomplete directory might have props which were supposed to be
2597 deleted but weren't. Because the server sent us all the props we're
2598 supposed to have, any previous base props not in this list must be
2599 deleted (issue #1672). */
2600 if (db->was_incomplete)
2603 apr_hash_t *props_to_delete;
2604 apr_hash_index_t *hi;
2606 /* In a copy of the BASE props, remove every property that we see an
2607 incoming change for. The remaining unmentioned properties are those
2608 which need to be deleted. */
2609 props_to_delete = apr_hash_copy(pool, base_props);
2610 for (i = 0; i < regular_prop_changes->nelts; i++)
2612 const svn_prop_t *prop;
2613 prop = &APR_ARRAY_IDX(regular_prop_changes, i, svn_prop_t);
2614 svn_hash_sets(props_to_delete, prop->name, NULL);
2617 /* Add these props to the incoming propchanges (in
2618 * regular_prop_changes). */
2619 for (hi = apr_hash_first(pool, props_to_delete);
2621 hi = apr_hash_next(hi))
2623 const char *propname = svn__apr_hash_index_key(hi);
2624 svn_prop_t *prop = apr_array_push(regular_prop_changes);
2626 /* Record a deletion for PROPNAME. */
2627 prop->name = propname;
2632 /* If this directory has property changes stored up, now is the time
2633 to deal with them. */
2634 if (regular_prop_changes->nelts)
2636 /* If recording traversal info, then see if the
2637 SVN_PROP_EXTERNALS property on this directory changed,
2638 and record before and after for the change. */
2639 if (eb->external_func)
2641 const svn_prop_t *change
2642 = externals_prop_changed(regular_prop_changes);
2646 const svn_string_t *new_val_s = change->value;
2647 const svn_string_t *old_val_s;
2649 old_val_s = svn_hash_gets(base_props, SVN_PROP_EXTERNALS);
2651 if ((new_val_s == NULL) && (old_val_s == NULL))
2652 ; /* No value before, no value after... so do nothing. */
2653 else if (new_val_s && old_val_s
2654 && (svn_string_compare(old_val_s, new_val_s)))
2655 ; /* Value did not change... so do nothing. */
2656 else if (old_val_s || new_val_s)
2657 /* something changed, record the change */
2659 SVN_ERR((eb->external_func)(
2672 /* We don't have a relevant actual row, but we need actual properties
2673 to allow property merging without conflicts. */
2675 actual_props = apr_hash_make(scratch_pool);
2677 actual_props = base_props;
2680 /* Merge pending properties. */
2681 new_base_props = svn_prop__patch(base_props, regular_prop_changes,
2683 SVN_ERR_W(svn_wc__merge_props(&conflict_skel,
2688 NULL /* use baseprops */,
2691 regular_prop_changes,
2694 _("Couldn't do property merge"));
2695 /* After a (not-dry-run) merge, we ALWAYS have props to save. */
2696 SVN_ERR_ASSERT(new_base_props != NULL && new_actual_props != NULL);
2699 SVN_ERR(accumulate_last_change(&new_changed_rev, &new_changed_date,
2700 &new_changed_author, entry_prop_changes,
2701 scratch_pool, scratch_pool));
2703 /* Check if we should add some not-present markers before marking the
2704 directory complete (Issue #3569) */
2706 apr_hash_t *new_children = svn_hash_gets(eb->dir_dirents, db->new_relpath);
2708 if (new_children != NULL)
2710 apr_hash_index_t *hi;
2711 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2713 for (hi = apr_hash_first(scratch_pool, new_children);
2715 hi = apr_hash_next(hi))
2717 const char *child_name;
2718 const char *child_abspath;
2719 const char *child_relpath;
2720 const svn_dirent_t *dirent;
2721 svn_wc__db_status_t status;
2722 svn_node_kind_t child_kind;
2725 svn_pool_clear(iterpool);
2727 child_name = svn__apr_hash_index_key(hi);
2728 child_abspath = svn_dirent_join(db->local_abspath, child_name,
2731 dirent = svn__apr_hash_index_val(hi);
2732 child_kind = (dirent->kind == svn_node_dir)
2736 if (db->ambient_depth < svn_depth_immediates
2737 && child_kind == svn_node_dir)
2738 continue; /* We don't need the subdirs */
2740 /* ### We just check if there is some node in BASE at this path */
2741 err = svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL,
2742 NULL, NULL, NULL, NULL, NULL, NULL,
2743 NULL, NULL, NULL, NULL, NULL,
2744 eb->db, child_abspath,
2745 iterpool, iterpool);
2749 svn_boolean_t is_wcroot;
2750 SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, eb->db, child_abspath,
2754 continue; /* Everything ok... Nothing to do here */
2755 /* Fall through to allow recovering later */
2757 else if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
2758 return svn_error_trace(err);
2760 svn_error_clear(err);
2762 child_relpath = svn_relpath_join(db->new_relpath, child_name,
2765 SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db,
2770 *eb->target_revision,
2776 svn_pool_destroy(iterpool);
2780 if (apr_hash_count(db->not_present_files))
2782 apr_hash_index_t *hi;
2783 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2785 /* This should call some new function (which could also be used
2786 for new_children above) to add all the names in single
2787 transaction, but I can't even trigger it. I've tried
2788 ra_local, ra_svn, ra_neon, ra_serf and they all call
2789 close_file before close_dir. */
2790 for (hi = apr_hash_first(scratch_pool, db->not_present_files);
2792 hi = apr_hash_next(hi))
2794 const char *child = svn__apr_hash_index_key(hi);
2795 const char *child_abspath, *child_relpath;
2797 svn_pool_clear(iterpool);
2799 child_abspath = svn_dirent_join(db->local_abspath, child, iterpool);
2800 child_relpath = svn_dirent_join(db->new_relpath, child, iterpool);
2802 SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db,
2807 *eb->target_revision,
2812 svn_pool_destroy(iterpool);
2815 /* If this directory is merely an anchor for a targeted child, then we
2816 should not be updating the node at all. */
2817 if (db->parent_baton == NULL
2818 && *eb->target_basename != '\0')
2820 /* And we should not have received any changes! */
2821 SVN_ERR_ASSERT(db->propchanges->nelts == 0);
2822 /* ... which also implies NEW_CHANGED_* are not set,
2823 and NEW_BASE_PROPS == NULL. */
2828 apr_array_header_t *iprops = NULL;
2830 /* ### we know a base node already exists. it was created in
2831 ### open_directory or add_directory. let's just preserve the
2832 ### existing DEPTH value, and possibly CHANGED_*. */
2833 /* If we received any changed_* values, then use them. */
2834 if (SVN_IS_VALID_REVNUM(new_changed_rev))
2835 db->changed_rev = new_changed_rev;
2836 if (new_changed_date != 0)
2837 db->changed_date = new_changed_date;
2838 if (new_changed_author != NULL)
2839 db->changed_author = new_changed_author;
2841 /* If no depth is set yet, set to infinity. */
2842 if (db->ambient_depth == svn_depth_unknown)
2843 db->ambient_depth = svn_depth_infinity;
2845 if (eb->depth_is_sticky
2846 && db->ambient_depth != eb->requested_depth)
2848 /* After a depth upgrade the entry must reflect the new depth.
2849 Upgrading to infinity changes the depth of *all* directories,
2850 upgrading to something else only changes the target. */
2852 if (eb->requested_depth == svn_depth_infinity
2853 || (strcmp(db->local_abspath, eb->target_abspath) == 0
2854 && eb->requested_depth > db->ambient_depth))
2856 db->ambient_depth = eb->requested_depth;
2860 /* Do we have new properties to install? Or shall we simply retain
2861 the prior set of properties? If we're installing new properties,
2862 then we also want to write them to an old-style props file. */
2863 props = new_base_props;
2869 svn_skel_t *work_item;
2871 SVN_ERR(complete_conflict(conflict_skel,
2874 db->old_repos_relpath,
2877 svn_node_dir, svn_node_dir,
2878 db->pool, scratch_pool));
2880 SVN_ERR(svn_wc__conflict_create_markers(&work_item,
2881 eb->db, db->local_abspath,
2883 scratch_pool, scratch_pool));
2885 all_work_items = svn_wc__wq_merge(all_work_items, work_item,
2889 /* Any inherited props to be set set for this base node? */
2890 if (eb->wcroot_iprops)
2892 iprops = svn_hash_gets(eb->wcroot_iprops, db->local_abspath);
2894 /* close_edit may also update iprops for switched nodes, catching
2895 those for which close_directory is never called (e.g. a switch
2896 with no changes). So as a minor optimization we remove any
2897 iprops from the hash so as not to set them again in
2900 svn_hash_sets(eb->wcroot_iprops, db->local_abspath, NULL);
2903 /* Update the BASE data for the directory and mark the directory
2905 SVN_ERR(svn_wc__db_base_add_directory(
2906 eb->db, db->local_abspath,
2909 eb->repos_root, eb->repos_uuid,
2910 *eb->target_revision,
2912 db->changed_rev, db->changed_date, db->changed_author,
2913 NULL /* children */,
2915 (dav_prop_changes->nelts > 0)
2916 ? svn_prop_array_to_hash(dav_prop_changes, pool)
2919 (! db->shadowed) && new_base_props != NULL,
2921 iprops, all_work_items,
2925 /* Process all of the queued work items for this directory. */
2926 SVN_ERR(svn_wc__wq_run(eb->db, db->local_abspath,
2927 eb->cancel_func, eb->cancel_baton,
2930 if (conflict_skel && eb->conflict_func)
2931 SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, db->local_abspath,
2933 NULL /* merge_options */,
2940 /* Notify of any prop changes on this directory -- but do nothing if
2941 it's an added or skipped directory, because notification has already
2942 happened in that case - unless the add was obstructed by a dir
2943 scheduled for addition without history, in which case we handle
2944 notification here). */
2945 if (!db->already_notified && eb->notify_func && db->edited)
2947 svn_wc_notify_t *notify;
2948 svn_wc_notify_action_t action;
2950 if (db->shadowed || db->edit_obstructed)
2951 action = svn_wc_notify_update_shadowed_update;
2952 else if (db->obstruction_found || db->add_existed)
2953 action = svn_wc_notify_exists;
2955 action = svn_wc_notify_update_update;
2957 notify = svn_wc_create_notify(db->local_abspath, action, pool);
2958 notify->kind = svn_node_dir;
2959 notify->prop_state = prop_state;
2960 notify->revision = *eb->target_revision;
2961 notify->old_revision = db->old_revision;
2963 eb->notify_func(eb->notify_baton, notify, scratch_pool);
2966 /* We're done with this directory, so remove one reference from the
2967 bump information. */
2968 SVN_ERR(maybe_release_dir_info(db));
2970 return SVN_NO_ERROR;
2974 /* Common code for 'absent_file' and 'absent_directory'. */
2975 static svn_error_t *
2976 absent_node(const char *path,
2977 svn_node_kind_t absent_kind,
2981 struct dir_baton *pb = parent_baton;
2982 struct edit_baton *eb = pb->edit_baton;
2983 apr_pool_t *scratch_pool = svn_pool_create(pool);
2984 const char *name = svn_dirent_basename(path, NULL);
2985 const char *local_abspath;
2987 svn_wc__db_status_t status;
2988 svn_node_kind_t kind;
2991 return SVN_NO_ERROR;
2993 SVN_ERR(mark_directory_edited(pb, scratch_pool));
2995 local_abspath = svn_dirent_join(pb->local_abspath, name, scratch_pool);
2997 /* If an item by this name is scheduled for addition that's a
2998 genuine tree-conflict. */
2999 err = svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
3000 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3001 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3002 NULL, NULL, NULL, NULL,
3003 eb->db, local_abspath,
3004 scratch_pool, scratch_pool);
3008 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
3009 return svn_error_trace(err);
3011 svn_error_clear(err);
3012 status = svn_wc__db_status_not_present;
3013 kind = svn_node_unknown;
3016 if (status == svn_wc__db_status_normal)
3018 svn_boolean_t wcroot;
3019 /* We found an obstructing working copy or a file external! */
3021 SVN_ERR(svn_wc__db_is_wcroot(&wcroot, eb->db, local_abspath,
3027 We have an obstructing working copy; possibly a directory external
3029 We can do two things now:
3030 1) notify the user, record a skip, etc.
3031 2) Just record the absent node in BASE in the parent
3034 As option 2 happens to be exactly what we do anyway, fall through.
3039 /* The server asks us to replace a file external
3040 (Existing BASE node; not reported by the working copy crawler or
3041 there would have been a delete_entry() call.
3043 There is no way we can store this state in the working copy as
3044 the BASE layer is already filled.
3046 We could error out, but that is not helping anybody; the user is not
3047 even seeing with what the file external would be replaced, so let's
3048 report a skip and continue the update.
3051 if (eb->notify_func)
3053 svn_wc_notify_t *notify;
3054 notify = svn_wc_create_notify(
3056 svn_wc_notify_update_skip_obstruction,
3059 eb->notify_func(eb->notify_baton, notify, scratch_pool);
3062 svn_pool_destroy(scratch_pool);
3063 return SVN_NO_ERROR;
3066 else if (status == svn_wc__db_status_not_present
3067 || status == svn_wc__db_status_server_excluded
3068 || status == svn_wc__db_status_excluded)
3070 /* The BASE node is not actually there, so we can safely turn it into
3075 /* We have a local addition. If this would be a BASE node it would have
3076 been deleted before we get here. (Which might have turned it into
3079 ### This should be recorded as a tree conflict and the update
3080 ### can just continue, as we can just record the absent status
3083 SVN_ERR_ASSERT(status != svn_wc__db_status_normal);
3085 return svn_error_createf(
3086 SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
3087 _("Failed to mark '%s' absent: item of the same name is already "
3088 "scheduled for addition"),
3089 svn_dirent_local_style(local_abspath, pool));
3093 const char *repos_relpath;
3094 repos_relpath = svn_relpath_join(pb->new_relpath, name, scratch_pool);
3096 /* Insert an excluded node below the parent node to note that this child
3097 is absent. (This puts it in the parent db if the child is obstructed) */
3098 SVN_ERR(svn_wc__db_base_add_excluded_node(eb->db, local_abspath,
3099 repos_relpath, eb->repos_root,
3101 *(eb->target_revision),
3103 svn_wc__db_status_server_excluded,
3108 svn_pool_destroy(scratch_pool);
3110 return SVN_NO_ERROR;
3114 /* An svn_delta_editor_t function. */
3115 static svn_error_t *
3116 absent_file(const char *path,
3120 return absent_node(path, svn_node_file, parent_baton, pool);
3124 /* An svn_delta_editor_t function. */
3125 static svn_error_t *
3126 absent_directory(const char *path,
3130 return absent_node(path, svn_node_dir, parent_baton, pool);
3134 /* An svn_delta_editor_t function. */
3135 static svn_error_t *
3136 add_file(const char *path,
3138 const char *copyfrom_path,
3139 svn_revnum_t copyfrom_rev,
3143 struct dir_baton *pb = parent_baton;
3144 struct edit_baton *eb = pb->edit_baton;
3145 struct file_baton *fb;
3146 svn_node_kind_t kind = svn_node_none;
3147 svn_node_kind_t wc_kind = svn_node_unknown;
3148 svn_wc__db_status_t status = svn_wc__db_status_normal;
3149 apr_pool_t *scratch_pool;
3150 svn_boolean_t conflicted = FALSE;
3151 svn_boolean_t conflict_ignored = FALSE;
3152 svn_boolean_t versioned_locally_and_present = FALSE;
3153 svn_skel_t *tree_conflict = NULL;
3154 svn_error_t *err = SVN_NO_ERROR;
3156 SVN_ERR_ASSERT(! (copyfrom_path || SVN_IS_VALID_REVNUM(copyfrom_rev)));
3158 SVN_ERR(make_file_baton(&fb, pb, path, TRUE, pool));
3162 return SVN_NO_ERROR;
3164 SVN_ERR(mark_file_edited(fb, pool));
3166 /* The file_pool can stick around for a *long* time, so we want to
3167 use a subpool for any temporary allocations. */
3168 scratch_pool = svn_pool_create(pool);
3171 /* It may not be named the same as the administrative directory. */
3172 if (svn_wc_is_adm_dir(fb->name, pool))
3173 return svn_error_createf(
3174 SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
3175 _("Failed to add file '%s': object of the same name as the "
3176 "administrative directory"),
3177 svn_dirent_local_style(fb->local_abspath, pool));
3179 if (!eb->clean_checkout)
3181 SVN_ERR(svn_io_check_path(fb->local_abspath, &kind, scratch_pool));
3183 err = svn_wc__db_read_info(&status, &wc_kind, NULL, NULL, NULL, NULL, NULL,
3184 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3185 NULL, NULL, NULL, NULL, NULL,
3186 &conflicted, NULL, NULL, NULL, NULL, NULL, NULL,
3187 eb->db, fb->local_abspath,
3188 scratch_pool, scratch_pool);
3193 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
3194 return svn_error_trace(err);
3196 svn_error_clear(err);
3197 wc_kind = svn_node_unknown;
3200 versioned_locally_and_present = FALSE;
3202 else if (wc_kind == svn_node_dir
3203 && status == svn_wc__db_status_normal)
3205 /* !! We found the root of a separate working copy obstructing the wc !!
3207 If the directory would be part of our own working copy then
3208 we wouldn't have been called as an add_file().
3210 The only thing we can do is add a not-present node, to allow
3211 a future update to bring in the new files when the problem is
3213 svn_hash_sets(pb->not_present_files, apr_pstrdup(pb->pool, fb->name),
3216 SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3217 fb->skip_this = TRUE;
3218 fb->already_notified = TRUE;
3220 do_notification(eb, fb->local_abspath, svn_node_file,
3221 svn_wc_notify_update_skip_obstruction, scratch_pool);
3223 svn_pool_destroy(scratch_pool);
3225 return SVN_NO_ERROR;
3227 else if (status == svn_wc__db_status_normal
3228 && (wc_kind == svn_node_file
3229 || wc_kind == svn_node_symlink))
3231 /* We found a file external occupating the place we need in BASE.
3233 We can't add a not-present node in this case as that would overwrite
3234 the file external. Luckily the file external itself stops us from
3235 forgetting a child of this parent directory like an obstructing
3238 The reason we get here is that the adm crawler doesn't report
3241 SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3242 fb->skip_this = TRUE;
3243 fb->already_notified = TRUE;
3245 do_notification(eb, fb->local_abspath, svn_node_file,
3246 svn_wc_notify_update_skip_obstruction, scratch_pool);
3248 svn_pool_destroy(scratch_pool);
3250 return SVN_NO_ERROR;
3252 else if (wc_kind == svn_node_unknown)
3253 versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */
3255 versioned_locally_and_present = IS_NODE_PRESENT(status);
3258 /* Is this path a conflict victim? */
3260 conflicted = FALSE; /* Conflict applies to WORKING */
3261 else if (conflicted)
3263 if (pb->deletion_conflicts)
3264 tree_conflict = svn_hash_gets(pb->deletion_conflicts, fb->name);
3268 svn_wc_conflict_reason_t reason;
3269 /* So this deletion wasn't just a deletion, it is actually a
3270 replacement. Let's install a better tree conflict. */
3272 /* ### Should store the conflict in DB to allow reinstalling
3273 ### with theoretically more data in close_directory() */
3275 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
3279 fb->pool, fb->pool));
3281 tree_conflict = svn_wc__conflict_skel_create(fb->pool);
3283 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
3285 eb->db, fb->local_abspath,
3286 reason, svn_wc_conflict_action_replace,
3288 fb->pool, fb->pool));
3290 /* And now stop checking for conflicts here and just perform
3291 a shadowed update */
3292 fb->edit_conflict = tree_conflict; /* Cache for close_file */
3293 tree_conflict = NULL; /* No direct notification */
3294 fb->shadowed = TRUE; /* Just continue */
3295 conflicted = FALSE; /* No skip */
3298 SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
3299 eb->db, fb->local_abspath, pool));
3302 /* Now the usual conflict handling: skip. */
3305 SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3307 fb->skip_this = TRUE;
3308 fb->already_notified = TRUE;
3310 /* We skip this node, but once the update completes the parent node will
3311 be updated to the new revision. So a future recursive update of the
3312 parent will not bring in this new node as the revision of the parent
3313 describes to the repository that all children are available.
3315 To resolve this problem, we add a not-present node to allow bringing
3316 the node in once this conflict is resolved.
3318 Note that we can safely assume that no present base node exists,
3319 because then we would not have received an add_file.
3321 svn_hash_sets(pb->not_present_files, apr_pstrdup(pb->pool, fb->name),
3324 do_notification(eb, fb->local_abspath, svn_node_unknown,
3325 svn_wc_notify_skip_conflicted, scratch_pool);
3327 svn_pool_destroy(scratch_pool);
3329 return SVN_NO_ERROR;
3331 else if (conflict_ignored)
3333 fb->shadowed = TRUE;
3338 /* Nothing to check; does not and will not exist in working copy */
3340 else if (versioned_locally_and_present)
3342 /* What to do with a versioned or schedule-add file:
3344 If the UUID doesn't match the parent's, or the URL isn't a child of
3345 the parent dir's URL, it's an error.
3347 Set add_existed so that user notification is delayed until after any
3348 text or prop conflicts have been found.
3350 Whether the incoming add is a symlink or a file will only be known in
3351 close_file(), when the props are known. So with a locally added file
3352 or symlink, let close_file() check for a tree conflict.
3354 We will never see missing files here, because these would be
3355 re-added during the crawler phase. */
3356 svn_boolean_t local_is_file;
3358 /* Is the local node a copy or move */
3359 if (status == svn_wc__db_status_added)
3360 SVN_ERR(svn_wc__db_scan_addition(&status, NULL, NULL, NULL, NULL, NULL,
3362 eb->db, fb->local_abspath,
3363 scratch_pool, scratch_pool));
3365 /* Is there something that is a file? */
3366 local_is_file = (wc_kind == svn_node_file
3367 || wc_kind == svn_node_symlink);
3369 /* Do tree conflict checking if
3370 * - if there is a local copy.
3371 * - if this is a switch operation
3372 * - the node kinds mismatch
3374 * During switch, local adds at the same path as incoming adds get
3375 * "lost" in that switching back to the original will no longer have the
3376 * local add. So switch always alerts the user with a tree conflict. */
3377 if (!eb->adds_as_modification
3379 || status != svn_wc__db_status_added)
3381 SVN_ERR(check_tree_conflict(&tree_conflict, eb,
3383 status, FALSE, svn_node_none,
3384 svn_wc_conflict_action_add,
3385 scratch_pool, scratch_pool));
3388 if (tree_conflict == NULL)
3389 fb->add_existed = TRUE; /* Take over WORKING */
3391 fb->shadowed = TRUE; /* Only update BASE */
3394 else if (kind != svn_node_none)
3396 /* There's an unversioned node at this path. */
3397 fb->obstruction_found = TRUE;
3399 /* Unversioned, obstructing files are handled by text merge/conflict,
3400 * if unversioned obstructions are allowed. */
3401 if (! (kind == svn_node_file && eb->allow_unver_obstructions))
3403 /* Bring in the node as deleted */ /* ### Obstructed Conflict */
3404 fb->shadowed = TRUE;
3406 /* Mark a conflict */
3407 tree_conflict = svn_wc__conflict_skel_create(fb->pool);
3409 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
3411 eb->db, fb->local_abspath,
3412 svn_wc_conflict_reason_unversioned,
3413 svn_wc_conflict_action_add,
3415 fb->pool, scratch_pool));
3419 /* When this is not the update target add a not-present BASE node now,
3420 to allow marking the parent directory complete in its close_edit() call.
3421 This resolves issues when that occurs before the close_file(). */
3422 if (pb->parent_baton
3423 || *eb->target_basename == '\0'
3424 || (strcmp(fb->local_abspath, eb->target_abspath) != 0))
3426 svn_hash_sets(pb->not_present_files, apr_pstrdup(pb->pool, fb->name),
3430 if (tree_conflict != NULL)
3432 SVN_ERR(complete_conflict(tree_conflict,
3435 fb->old_repos_relpath,
3440 fb->pool, scratch_pool));
3442 SVN_ERR(svn_wc__db_op_mark_conflict(eb->db,
3444 tree_conflict, NULL,
3447 if (eb->conflict_func)
3448 SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, fb->local_abspath,
3450 NULL /* merge_options */,
3457 fb->already_notified = TRUE;
3458 do_notification(eb, fb->local_abspath, svn_node_file,
3459 svn_wc_notify_tree_conflict, scratch_pool);
3462 svn_pool_destroy(scratch_pool);
3464 return SVN_NO_ERROR;
3468 /* An svn_delta_editor_t function. */
3469 static svn_error_t *
3470 open_file(const char *path,
3472 svn_revnum_t base_revision,
3476 struct dir_baton *pb = parent_baton;
3477 struct edit_baton *eb = pb->edit_baton;
3478 struct file_baton *fb;
3479 svn_boolean_t conflicted;
3480 svn_boolean_t conflict_ignored = FALSE;
3481 svn_boolean_t have_work;
3482 svn_wc__db_status_t status;
3483 svn_node_kind_t wc_kind;
3484 svn_skel_t *tree_conflict = NULL;
3486 /* the file_pool can stick around for a *long* time, so we want to use
3487 a subpool for any temporary allocations. */
3488 apr_pool_t *scratch_pool = svn_pool_create(pool);
3490 SVN_ERR(make_file_baton(&fb, pb, path, FALSE, pool));
3494 return SVN_NO_ERROR;
3496 /* Detect obstructing working copies */
3498 svn_boolean_t is_root;
3500 SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, fb->local_abspath,
3505 /* Just skip this node; a future update will handle it */
3506 SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3507 fb->skip_this = TRUE;
3508 fb->already_notified = TRUE;
3510 do_notification(eb, fb->local_abspath, svn_node_file,
3511 svn_wc_notify_update_skip_obstruction, pool);
3513 return SVN_NO_ERROR;
3519 /* If replacing, make sure the .svn entry already exists. */
3520 SVN_ERR(svn_wc__db_read_info(&status, &wc_kind, &fb->old_revision,
3521 &fb->old_repos_relpath, NULL, NULL,
3522 &fb->changed_rev, &fb->changed_date,
3523 &fb->changed_author, NULL,
3524 &fb->original_checksum, NULL, NULL, NULL,
3525 NULL, NULL, NULL, NULL, NULL, NULL,
3526 &conflicted, NULL, NULL, &fb->local_prop_mods,
3527 NULL, NULL, &have_work,
3528 eb->db, fb->local_abspath,
3529 fb->pool, scratch_pool));
3532 SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &fb->old_revision,
3533 &fb->old_repos_relpath, NULL, NULL,
3534 &fb->changed_rev, &fb->changed_date,
3535 &fb->changed_author, NULL,
3536 &fb->original_checksum, NULL, NULL,
3538 eb->db, fb->local_abspath,
3539 fb->pool, scratch_pool));
3541 /* Is this path a conflict victim? */
3543 conflicted = FALSE; /* Conflict applies to WORKING */
3544 else if (conflicted)
3545 SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
3546 eb->db, fb->local_abspath, pool));
3549 SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3551 fb->skip_this = TRUE;
3552 fb->already_notified = TRUE;
3554 do_notification(eb, fb->local_abspath, svn_node_unknown,
3555 svn_wc_notify_skip_conflicted, scratch_pool);
3557 svn_pool_destroy(scratch_pool);
3559 return SVN_NO_ERROR;
3561 else if (conflict_ignored)
3563 fb->shadowed = TRUE;
3566 /* Check for conflicts only when we haven't already recorded
3567 * a tree-conflict on a parent node. */
3569 SVN_ERR(check_tree_conflict(&tree_conflict, eb, fb->local_abspath,
3570 status, TRUE, svn_node_file,
3571 svn_wc_conflict_action_edit,
3572 fb->pool, scratch_pool));
3574 /* Is this path the victim of a newly-discovered tree conflict? */
3575 if (tree_conflict != NULL)
3577 svn_wc_conflict_reason_t reason;
3578 fb->edit_conflict = tree_conflict;
3579 /* Other modifications wouldn't be a tree conflict */
3581 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
3582 eb->db, fb->local_abspath,
3584 scratch_pool, scratch_pool));
3585 SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_deleted
3586 || reason == svn_wc_conflict_reason_moved_away
3587 || reason == svn_wc_conflict_reason_replaced
3588 || reason == svn_wc_conflict_reason_obstructed);
3590 /* Continue updating BASE */
3591 if (reason == svn_wc_conflict_reason_obstructed)
3592 fb->edit_obstructed = TRUE;
3594 fb->shadowed = TRUE;
3597 svn_pool_destroy(scratch_pool);
3599 return SVN_NO_ERROR;
3602 /* Implements svn_stream_lazyopen_func_t. */
3603 static svn_error_t *
3604 lazy_open_source(svn_stream_t **stream,
3606 apr_pool_t *result_pool,
3607 apr_pool_t *scratch_pool)
3609 struct file_baton *fb = baton;
3611 SVN_ERR(svn_wc__db_pristine_read(stream, NULL, fb->edit_baton->db,
3613 fb->original_checksum,
3614 result_pool, scratch_pool));
3617 return SVN_NO_ERROR;
3620 struct lazy_target_baton {
3621 struct file_baton *fb;
3622 struct handler_baton *hb;
3623 struct edit_baton *eb;
3626 /* Implements svn_stream_lazyopen_func_t. */
3627 static svn_error_t *
3628 lazy_open_target(svn_stream_t **stream,
3630 apr_pool_t *result_pool,
3631 apr_pool_t *scratch_pool)
3633 struct lazy_target_baton *tb = baton;
3635 SVN_ERR(svn_wc__open_writable_base(stream, &tb->hb->new_text_base_tmp_abspath,
3636 NULL, &tb->hb->new_text_base_sha1_checksum,
3637 tb->fb->edit_baton->db,
3638 tb->eb->wcroot_abspath,
3639 result_pool, scratch_pool));
3641 return SVN_NO_ERROR;
3644 /* An svn_delta_editor_t function. */
3645 static svn_error_t *
3646 apply_textdelta(void *file_baton,
3647 const char *expected_checksum,
3649 svn_txdelta_window_handler_t *handler,
3650 void **handler_baton)
3652 struct file_baton *fb = file_baton;
3653 apr_pool_t *handler_pool = svn_pool_create(fb->pool);
3654 struct handler_baton *hb = apr_pcalloc(handler_pool, sizeof(*hb));
3655 struct edit_baton *eb = fb->edit_baton;
3656 const svn_checksum_t *recorded_base_checksum;
3657 svn_checksum_t *expected_base_checksum;
3658 svn_stream_t *source;
3659 struct lazy_target_baton *tb;
3660 svn_stream_t *target;
3664 *handler = svn_delta_noop_window_handler;
3665 *handler_baton = NULL;
3666 return SVN_NO_ERROR;
3669 SVN_ERR(mark_file_edited(fb, pool));
3671 /* Parse checksum or sets expected_base_checksum to NULL */
3672 SVN_ERR(svn_checksum_parse_hex(&expected_base_checksum, svn_checksum_md5,
3673 expected_checksum, pool));
3675 /* Before applying incoming svndiff data to text base, make sure
3676 text base hasn't been corrupted, and that its checksum
3677 matches the expected base checksum. */
3679 /* The incoming delta is targeted against EXPECTED_BASE_CHECKSUM. Find and
3680 check our RECORDED_BASE_CHECKSUM. (In WC-1, we could not do this test
3681 for replaced nodes because we didn't store the checksum of the "revert
3682 base". In WC-NG, we do and we can.) */
3683 recorded_base_checksum = fb->original_checksum;
3685 /* If we have a checksum that we want to compare to a MD5 checksum,
3686 ensure that it is a MD5 checksum */
3687 if (recorded_base_checksum
3688 && expected_base_checksum
3689 && recorded_base_checksum->kind != svn_checksum_md5)
3690 SVN_ERR(svn_wc__db_pristine_get_md5(&recorded_base_checksum,
3691 eb->db, eb->wcroot_abspath,
3692 recorded_base_checksum, pool, pool));
3695 if (!svn_checksum_match(expected_base_checksum, recorded_base_checksum))
3696 return svn_error_createf(SVN_ERR_WC_CORRUPT_TEXT_BASE, NULL,
3697 _("Checksum mismatch for '%s':\n"
3700 svn_dirent_local_style(fb->local_abspath, pool),
3701 svn_checksum_to_cstring_display(expected_base_checksum,
3703 svn_checksum_to_cstring_display(recorded_base_checksum,
3706 /* Open the text base for reading, unless this is an added file. */
3709 kff todo: what we really need to do here is:
3711 1. See if there's a file or dir by this name already here.
3712 2. See if it's under revision control.
3713 3. If both are true, open text-base.
3714 4. If only 1 is true, bail, because we can't go destroying user's
3715 files (or as an alternative to bailing, move it to some tmp
3716 name and somehow tell the user, but communicating with the
3717 user without erroring is a whole callback system we haven't
3718 finished inventing yet.)
3721 if (! fb->adding_file)
3723 SVN_ERR_ASSERT(!fb->original_checksum
3724 || fb->original_checksum->kind == svn_checksum_sha1);
3726 source = svn_stream_lazyopen_create(lazy_open_source, fb, FALSE,
3731 source = svn_stream_empty(handler_pool);
3734 /* If we don't have a recorded checksum, use the ra provided checksum */
3735 if (!recorded_base_checksum)
3736 recorded_base_checksum = expected_base_checksum;
3738 /* Checksum the text base while applying deltas */
3739 if (recorded_base_checksum)
3741 hb->expected_source_checksum = svn_checksum_dup(recorded_base_checksum,
3744 /* Wrap stream and store reference to allow calculating the
3746 source = svn_stream_checksummed2(source,
3747 &hb->actual_source_checksum,
3748 NULL, recorded_base_checksum->kind,
3749 TRUE, handler_pool);
3750 hb->source_checksum_stream = source;
3753 tb = apr_palloc(handler_pool, sizeof(struct lazy_target_baton));
3757 target = svn_stream_lazyopen_create(lazy_open_target, tb, TRUE, handler_pool);
3759 /* Prepare to apply the delta. */
3760 svn_txdelta_apply(source, target,
3761 hb->new_text_base_md5_digest,
3762 hb->new_text_base_tmp_abspath /* error_info */,
3764 &hb->apply_handler, &hb->apply_baton);
3766 hb->pool = handler_pool;
3769 /* We're all set. */
3770 *handler_baton = hb;
3771 *handler = window_handler;
3773 return SVN_NO_ERROR;
3777 /* An svn_delta_editor_t function. */
3778 static svn_error_t *
3779 change_file_prop(void *file_baton,
3781 const svn_string_t *value,
3782 apr_pool_t *scratch_pool)
3784 struct file_baton *fb = file_baton;
3785 svn_prop_t *propchange;
3788 return SVN_NO_ERROR;
3790 /* Push a new propchange to the file baton's array of propchanges */
3791 propchange = apr_array_push(fb->propchanges);
3792 propchange->name = apr_pstrdup(fb->pool, name);
3793 propchange->value = value ? svn_string_dup(value, fb->pool) : NULL;
3795 if (!fb->edited && svn_property_kind2(name) == svn_prop_regular_kind)
3796 SVN_ERR(mark_file_edited(fb, scratch_pool));
3799 && strcmp(name, SVN_PROP_SPECIAL) == 0)
3801 struct edit_baton *eb = fb->edit_baton;
3802 svn_boolean_t modified = FALSE;
3803 svn_boolean_t becomes_symlink;
3804 svn_boolean_t was_symlink;
3806 /* Let's see if we have a change as in some scenarios servers report
3807 non-changes of properties. */
3808 becomes_symlink = (value != NULL);
3810 if (fb->adding_file)
3811 was_symlink = becomes_symlink; /* No change */
3816 /* We read the server-props, not the ACTUAL props here as we just
3817 want to see if this is really an incoming prop change. */
3818 SVN_ERR(svn_wc__db_base_get_props(&props, eb->db,
3820 scratch_pool, scratch_pool));
3822 was_symlink = ((props
3823 && svn_hash_gets(props, SVN_PROP_SPECIAL) != NULL)
3825 : svn_tristate_false);
3828 if (was_symlink != becomes_symlink)
3830 /* If the local node was not modified, we continue as usual, if
3831 modified we want a tree conflict just like how we would handle
3832 it when receiving a delete + add (aka "replace") */
3833 if (fb->local_prop_mods)
3836 SVN_ERR(svn_wc__internal_file_modified_p(&modified, eb->db,
3838 FALSE, scratch_pool));
3843 if (!fb->edit_conflict)
3844 fb->edit_conflict = svn_wc__conflict_skel_create(fb->pool);
3846 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
3848 eb->db, fb->local_abspath,
3849 svn_wc_conflict_reason_edited,
3850 svn_wc_conflict_action_replace,
3852 fb->pool, scratch_pool));
3854 SVN_ERR(complete_conflict(fb->edit_conflict, fb->edit_baton,
3855 fb->local_abspath, fb->old_repos_relpath,
3856 fb->old_revision, fb->new_relpath,
3857 svn_node_file, svn_node_file,
3858 fb->pool, scratch_pool));
3860 /* Create a copy of the existing (pre update) BASE node in WORKING,
3861 mark a tree conflict and handle the rest of the update as
3863 SVN_ERR(svn_wc__db_op_make_copy(eb->db, fb->local_abspath,
3864 fb->edit_conflict, NULL,
3867 do_notification(eb, fb->local_abspath, svn_node_file,
3868 svn_wc_notify_tree_conflict, scratch_pool);
3870 /* Ok, we introduced a replacement, so we can now handle the rest
3871 as a normal shadowed update */
3872 fb->shadowed = TRUE;
3873 fb->add_existed = FALSE;
3874 fb->already_notified = TRUE;
3878 return SVN_NO_ERROR;
3881 /* Perform the actual merge of file changes between an original file,
3882 identified by ORIGINAL_CHECKSUM (an empty file if NULL) to a new file
3883 identified by NEW_CHECKSUM.
3885 Merge the result into LOCAL_ABSPATH, which is part of the working copy
3886 identified by WRI_ABSPATH. Use OLD_REVISION and TARGET_REVISION for naming
3887 the intermediate files.
3889 The rest of the arguments are passed to svn_wc__internal_merge().
3892 svn_wc__perform_file_merge(svn_skel_t **work_items,
3893 svn_skel_t **conflict_skel,
3894 svn_boolean_t *found_conflict,
3896 const char *local_abspath,
3897 const char *wri_abspath,
3898 const svn_checksum_t *new_checksum,
3899 const svn_checksum_t *original_checksum,
3900 apr_hash_t *old_actual_props,
3901 const apr_array_header_t *ext_patterns,
3902 svn_revnum_t old_revision,
3903 svn_revnum_t target_revision,
3904 const apr_array_header_t *propchanges,
3905 const char *diff3_cmd,
3906 svn_cancel_func_t cancel_func,
3908 apr_pool_t *result_pool,
3909 apr_pool_t *scratch_pool)
3911 /* Actual file exists and has local mods:
3912 Now we need to let loose svn_wc__internal_merge() to merge
3913 the textual changes into the working file. */
3914 const char *oldrev_str, *newrev_str, *mine_str;
3915 const char *merge_left;
3916 svn_boolean_t delete_left = FALSE;
3917 const char *path_ext = "";
3918 const char *new_text_base_tmp_abspath;
3919 enum svn_wc_merge_outcome_t merge_outcome = svn_wc_merge_unchanged;
3920 svn_skel_t *work_item;
3924 SVN_ERR(svn_wc__db_pristine_get_path(&new_text_base_tmp_abspath,
3925 db, wri_abspath, new_checksum,
3926 scratch_pool, scratch_pool));
3928 /* If we have any file extensions we're supposed to
3929 preserve in generated conflict file names, then find
3930 this path's extension. But then, if it isn't one of
3931 the ones we want to keep in conflict filenames,
3932 pretend it doesn't have an extension at all. */
3933 if (ext_patterns && ext_patterns->nelts)
3935 svn_path_splitext(NULL, &path_ext, local_abspath, scratch_pool);
3936 if (! (*path_ext && svn_cstring_match_glob_list(path_ext, ext_patterns)))
3940 /* old_revision can be invalid when the conflict is against a
3942 if (!SVN_IS_VALID_REVNUM(old_revision))
3945 oldrev_str = apr_psprintf(scratch_pool, ".r%ld%s%s",
3947 *path_ext ? "." : "",
3948 *path_ext ? path_ext : "");
3950 newrev_str = apr_psprintf(scratch_pool, ".r%ld%s%s",
3952 *path_ext ? "." : "",
3953 *path_ext ? path_ext : "");
3954 mine_str = apr_psprintf(scratch_pool, ".mine%s%s",
3955 *path_ext ? "." : "",
3956 *path_ext ? path_ext : "");
3958 if (! original_checksum)
3960 SVN_ERR(get_empty_tmp_file(&merge_left, db, wri_abspath,
3961 result_pool, scratch_pool));
3965 SVN_ERR(svn_wc__db_pristine_get_path(&merge_left, db, wri_abspath,
3967 result_pool, scratch_pool));
3969 /* Merge the changes from the old textbase to the new
3970 textbase into the file we're updating.
3971 Remember that this function wants full paths! */
3972 SVN_ERR(svn_wc__internal_merge(&work_item,
3977 new_text_base_tmp_abspath,
3980 oldrev_str, newrev_str, mine_str,
3982 FALSE /* dry_run */,
3983 diff3_cmd, NULL, propchanges,
3984 cancel_func, cancel_baton,
3985 result_pool, scratch_pool));
3987 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
3988 *found_conflict = (merge_outcome == svn_wc_merge_conflict);
3990 /* If we created a temporary left merge file, get rid of it. */
3993 SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, wri_abspath,
3995 result_pool, scratch_pool));
3996 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
3999 return SVN_NO_ERROR;
4002 /* This is the small planet. It has the complex responsibility of
4003 * "integrating" a new revision of a file into a working copy.
4005 * Given a file_baton FB for a file either already under version control, or
4006 * prepared (see below) to join version control, fully install a
4007 * new revision of the file.
4009 * ### transitional: installation of the working file will be handled
4010 * ### by the *INSTALL_PRISTINE flag.
4012 * By "install", we mean: create a new text-base and prop-base, merge
4013 * any textual and property changes into the working file, and finally
4014 * update all metadata so that the working copy believes it has a new
4015 * working revision of the file. All of this work includes being
4016 * sensitive to eol translation, keyword substitution, and performing
4017 * all actions accumulated the parent directory's work queue.
4019 * Set *CONTENT_STATE to the state of the contents after the
4022 * Return values are allocated in RESULT_POOL and temporary allocations
4023 * are performed in SCRATCH_POOL.
4025 static svn_error_t *
4026 merge_file(svn_skel_t **work_items,
4027 svn_skel_t **conflict_skel,
4028 svn_boolean_t *install_pristine,
4029 const char **install_from,
4030 svn_wc_notify_state_t *content_state,
4031 struct file_baton *fb,
4032 apr_hash_t *actual_props,
4033 apr_time_t last_changed_date,
4034 apr_pool_t *result_pool,
4035 apr_pool_t *scratch_pool)
4037 struct edit_baton *eb = fb->edit_baton;
4038 struct dir_baton *pb = fb->dir_baton;
4039 svn_boolean_t is_locally_modified;
4040 svn_boolean_t found_text_conflict = FALSE;
4042 SVN_ERR_ASSERT(! fb->shadowed
4043 && ! fb->obstruction_found
4044 && ! fb->edit_obstructed);
4047 When this function is called on file F, we assume the following
4050 - The new pristine text of F is present in the pristine store
4051 iff FB->NEW_TEXT_BASE_SHA1_CHECKSUM is not NULL.
4053 - The WC metadata still reflects the old version of F.
4054 (We can still access the old pristine base text of F.)
4056 The goal is to update the local working copy of F to reflect
4057 the changes received from the repository, preserving any local
4062 *install_pristine = FALSE;
4063 *install_from = NULL;
4065 /* Start by splitting the file path, getting an access baton for the parent,
4066 and an entry for the file if any. */
4068 /* Has the user made local mods to the working file?
4069 Note that this compares to the current pristine file, which is
4070 different from fb->old_text_base_path if we have a replaced-with-history
4071 file. However, in the case we had an obstruction, we check against the
4074 if (fb->adding_file && !fb->add_existed)
4076 is_locally_modified = FALSE; /* There is no file: Don't check */
4080 /* The working file is not an obstruction.
4081 So: is the file modified, relative to its ORIGINAL pristine?
4083 This function sets is_locally_modified to FALSE for
4084 files that do not exist and for directories. */
4086 SVN_ERR(svn_wc__internal_file_modified_p(&is_locally_modified,
4087 eb->db, fb->local_abspath,
4088 FALSE /* exact_comparison */,
4092 /* For 'textual' merging, we use the following system:
4094 When a file is modified and we have a new BASE:
4096 * svn_wc_merge uses diff3
4097 * possibly makes backups and marks files as conflicted.
4100 * svn_wc_merge makes backups and marks files as conflicted.
4102 If a file is not modified and we have a new BASE:
4103 * Install from pristine.
4105 If we have property changes related to magic properties or if the
4106 svn:keywords property is set:
4107 * Retranslate from the working file.
4109 if (! is_locally_modified
4110 && fb->new_text_base_sha1_checksum)
4112 /* If there are no local mods, who cares whether it's a text
4113 or binary file! Just write a log command to overwrite
4114 any working file with the new text-base. If newline
4115 conversion or keyword substitution is activated, this
4116 will happen as well during the copy.
4117 For replaced files, though, we want to merge in the changes
4118 even if the file is not modified compared to the (non-revert)
4121 *install_pristine = TRUE;
4123 else if (fb->new_text_base_sha1_checksum)
4125 /* Actual file exists and has local mods:
4126 Now we need to let loose svn_wc__merge_internal() to merge
4127 the textual changes into the working file. */
4128 SVN_ERR(svn_wc__perform_file_merge(work_items,
4130 &found_text_conflict,
4134 fb->new_text_base_sha1_checksum,
4137 : fb->original_checksum,
4141 *eb->target_revision,
4144 eb->cancel_func, eb->cancel_baton,
4145 result_pool, scratch_pool));
4146 } /* end: working file exists and has mods */
4149 /* There is no new text base, but let's see if the working file needs
4150 to be updated for any other reason. */
4152 apr_hash_t *keywords;
4154 /* Determine if any of the propchanges are the "magic" ones that
4155 might require changing the working file. */
4156 svn_boolean_t magic_props_changed;
4158 magic_props_changed = svn_wc__has_magic_property(fb->propchanges);
4160 SVN_ERR(svn_wc__get_translate_info(NULL, NULL,
4163 eb->db, fb->local_abspath,
4165 scratch_pool, scratch_pool));
4166 if (magic_props_changed || keywords)
4168 /* Special edge-case: it's possible that this file installation
4169 only involves propchanges, but that some of those props still
4170 require a retranslation of the working file.
4172 OR that the file doesn't involve propchanges which by themselves
4173 require retranslation, but receiving a change bumps the revision
4174 number which requires re-expansion of keywords... */
4176 if (is_locally_modified)
4178 const char *tmptext;
4180 /* Copy and DEtranslate the working file to a temp text-base.
4181 Note that detranslation is done according to the old props. */
4182 SVN_ERR(svn_wc__internal_translated_file(
4183 &tmptext, fb->local_abspath, eb->db, fb->local_abspath,
4184 SVN_WC_TRANSLATE_TO_NF
4185 | SVN_WC_TRANSLATE_NO_OUTPUT_CLEANUP,
4186 eb->cancel_func, eb->cancel_baton,
4187 result_pool, scratch_pool));
4189 /* We always want to reinstall the working file if the magic
4190 properties have changed, or there are any keywords present.
4191 Note that TMPTEXT might actually refer to the working file
4192 itself (the above function skips a detranslate when not
4193 required). This is acceptable, as we will (re)translate
4194 according to the new properties into a temporary file (from
4195 the working file), and then rename the temp into place. Magic!
4197 *install_pristine = TRUE;
4198 *install_from = tmptext;
4202 /* Use our existing 'copy' from the pristine store instead
4203 of making a new copy. This way we can use the standard code
4204 to update the recorded size and modification time.
4206 *install_pristine = TRUE;
4211 /* Set the returned content state. */
4213 if (found_text_conflict)
4214 *content_state = svn_wc_notify_state_conflicted;
4215 else if (fb->new_text_base_sha1_checksum)
4217 if (is_locally_modified)
4218 *content_state = svn_wc_notify_state_merged;
4220 *content_state = svn_wc_notify_state_changed;
4223 *content_state = svn_wc_notify_state_unchanged;
4225 return SVN_NO_ERROR;
4229 /* An svn_delta_editor_t function. */
4230 /* Mostly a wrapper around merge_file. */
4231 static svn_error_t *
4232 close_file(void *file_baton,
4233 const char *expected_md5_digest,
4236 struct file_baton *fb = file_baton;
4237 struct dir_baton *pdb = fb->dir_baton;
4238 struct edit_baton *eb = fb->edit_baton;
4239 svn_wc_notify_state_t content_state, prop_state;
4240 svn_wc_notify_lock_state_t lock_state;
4241 svn_checksum_t *expected_md5_checksum = NULL;
4242 apr_hash_t *new_base_props = NULL;
4243 apr_hash_t *new_actual_props = NULL;
4244 apr_array_header_t *entry_prop_changes;
4245 apr_array_header_t *dav_prop_changes;
4246 apr_array_header_t *regular_prop_changes;
4247 apr_hash_t *current_base_props = NULL;
4248 apr_hash_t *current_actual_props = NULL;
4249 apr_hash_t *local_actual_props = NULL;
4250 svn_skel_t *all_work_items = NULL;
4251 svn_skel_t *conflict_skel = NULL;
4252 svn_skel_t *work_item;
4253 apr_pool_t *scratch_pool = fb->pool; /* Destroyed at function exit */
4254 svn_boolean_t keep_recorded_info = FALSE;
4255 const svn_checksum_t *new_checksum;
4256 apr_array_header_t *iprops = NULL;
4260 svn_pool_destroy(fb->pool);
4261 SVN_ERR(maybe_release_dir_info(pdb));
4262 return SVN_NO_ERROR;
4266 conflict_skel = fb->edit_conflict;
4268 if (expected_md5_digest)
4269 SVN_ERR(svn_checksum_parse_hex(&expected_md5_checksum, svn_checksum_md5,
4270 expected_md5_digest, scratch_pool));
4272 if (fb->new_text_base_md5_checksum && expected_md5_checksum
4273 && !svn_checksum_match(expected_md5_checksum,
4274 fb->new_text_base_md5_checksum))
4275 return svn_error_trace(
4276 svn_checksum_mismatch_err(expected_md5_checksum,
4277 fb->new_text_base_md5_checksum,
4279 _("Checksum mismatch for '%s'"),
4280 svn_dirent_local_style(
4281 fb->local_abspath, pool)));
4283 /* Gather the changes for each kind of property. */
4284 SVN_ERR(svn_categorize_props(fb->propchanges, &entry_prop_changes,
4285 &dav_prop_changes, ®ular_prop_changes,
4288 /* Extract the changed_* and lock state information. */
4290 svn_revnum_t new_changed_rev;
4291 apr_time_t new_changed_date;
4292 const char *new_changed_author;
4294 SVN_ERR(accumulate_last_change(&new_changed_rev,
4296 &new_changed_author,
4298 scratch_pool, scratch_pool));
4300 if (SVN_IS_VALID_REVNUM(new_changed_rev))
4301 fb->changed_rev = new_changed_rev;
4302 if (new_changed_date != 0)
4303 fb->changed_date = new_changed_date;
4304 if (new_changed_author != NULL)
4305 fb->changed_author = new_changed_author;
4308 /* Determine whether the file has become unlocked. */
4312 lock_state = svn_wc_notify_lock_state_unchanged;
4314 for (i = 0; i < entry_prop_changes->nelts; ++i)
4316 const svn_prop_t *prop
4317 = &APR_ARRAY_IDX(entry_prop_changes, i, svn_prop_t);
4319 /* If we see a change to the LOCK_TOKEN entry prop, then the only
4320 possible change is its REMOVAL. Thus, the lock has been removed,
4321 and we should likewise remove our cached copy of it. */
4322 if (! strcmp(prop->name, SVN_PROP_ENTRY_LOCK_TOKEN))
4324 /* If we lose the lock, but not because we are switching to
4325 another url, remove the state lock from the wc */
4326 if (! eb->switch_relpath
4327 || strcmp(fb->new_relpath, fb->old_repos_relpath) == 0)
4329 SVN_ERR_ASSERT(prop->value == NULL);
4330 SVN_ERR(svn_wc__db_lock_remove(eb->db, fb->local_abspath,
4333 lock_state = svn_wc_notify_lock_state_unlocked;
4340 /* Install all kinds of properties. It is important to do this before
4341 any file content merging, since that process might expand keywords, in
4342 which case we want the new entryprops to be in place. */
4344 /* Write log commands to merge REGULAR_PROPS into the existing
4345 properties of FB->LOCAL_ABSPATH. Update *PROP_STATE to reflect
4346 the result of the regular prop merge.
4348 BASE_PROPS and WORKING_PROPS are hashes of the base and
4349 working props of the file; if NULL they are read from the wc. */
4351 /* ### some of this feels like voodoo... */
4353 if ((!fb->adding_file || fb->add_existed)
4355 SVN_ERR(svn_wc__get_actual_props(&local_actual_props,
4356 eb->db, fb->local_abspath,
4357 scratch_pool, scratch_pool));
4358 if (local_actual_props == NULL)
4359 local_actual_props = apr_hash_make(scratch_pool);
4361 if (fb->add_existed)
4363 /* This node already exists. Grab the current pristine properties. */
4364 SVN_ERR(svn_wc__db_read_pristine_props(¤t_base_props,
4365 eb->db, fb->local_abspath,
4366 scratch_pool, scratch_pool));
4367 current_actual_props = local_actual_props;
4369 else if (!fb->adding_file)
4371 /* Get the BASE properties for proper merging. */
4372 SVN_ERR(svn_wc__db_base_get_props(¤t_base_props,
4373 eb->db, fb->local_abspath,
4374 scratch_pool, scratch_pool));
4375 current_actual_props = local_actual_props;
4378 /* Note: even if the node existed before, it may not have
4379 pristine props (e.g a local-add) */
4380 if (current_base_props == NULL)
4381 current_base_props = apr_hash_make(scratch_pool);
4383 /* And new nodes need an empty set of ACTUAL props. */
4384 if (current_actual_props == NULL)
4385 current_actual_props = apr_hash_make(scratch_pool);
4387 prop_state = svn_wc_notify_state_unknown;
4391 svn_boolean_t install_pristine;
4392 const char *install_from = NULL;
4394 /* Merge the 'regular' props into the existing working proplist. */
4395 /* This will merge the old and new props into a new prop db, and
4396 write <cp> commands to the logfile to install the merged
4398 new_base_props = svn_prop__patch(current_base_props, regular_prop_changes,
4400 SVN_ERR(svn_wc__merge_props(&conflict_skel,
4405 NULL /* server_baseprops (update, not merge) */,
4407 current_actual_props,
4408 regular_prop_changes, /* propchanges */
4411 /* We will ALWAYS have properties to save (after a not-dry-run merge). */
4412 SVN_ERR_ASSERT(new_base_props != NULL && new_actual_props != NULL);
4414 /* Merge the text. This will queue some additional work. */
4415 if (!fb->obstruction_found && !fb->edit_obstructed)
4418 err = merge_file(&work_item, &conflict_skel,
4419 &install_pristine, &install_from,
4420 &content_state, fb, current_actual_props,
4421 fb->changed_date, scratch_pool, scratch_pool);
4423 if (err && err->apr_err == SVN_ERR_WC_PATH_ACCESS_DENIED)
4425 if (eb->notify_func)
4427 svn_wc_notify_t *notify =svn_wc_create_notify(
4429 svn_wc_notify_update_skip_access_denied,
4432 notify->kind = svn_node_file;
4435 eb->notify_func(eb->notify_baton, notify, scratch_pool);
4437 svn_error_clear(err);
4439 SVN_ERR(remember_skipped_tree(eb, fb->local_abspath,
4441 fb->skip_this = TRUE;
4443 svn_pool_destroy(fb->pool);
4444 SVN_ERR(maybe_release_dir_info(pdb));
4445 return SVN_NO_ERROR;
4450 all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4455 install_pristine = FALSE;
4456 if (fb->new_text_base_sha1_checksum)
4457 content_state = svn_wc_notify_state_changed;
4459 content_state = svn_wc_notify_state_unchanged;
4462 if (install_pristine)
4464 svn_boolean_t record_fileinfo;
4466 /* If we are installing from the pristine contents, then go ahead and
4467 record the fileinfo. That will be the "proper" values. Installing
4468 from some random file means the fileinfo does NOT correspond to
4469 the pristine (in which case, the fileinfo will be cleared for
4471 record_fileinfo = (install_from == NULL);
4473 SVN_ERR(svn_wc__wq_build_file_install(&work_item,
4477 eb->use_commit_times,
4479 scratch_pool, scratch_pool));
4480 all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4483 else if (lock_state == svn_wc_notify_lock_state_unlocked
4484 && !fb->obstruction_found)
4486 /* If a lock was removed and we didn't update the text contents, we
4487 might need to set the file read-only.
4489 Note: this will also update the executable flag, but ... meh. */
4490 SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, eb->db,
4492 scratch_pool, scratch_pool));
4493 all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4497 if (! install_pristine
4498 && (content_state == svn_wc_notify_state_unchanged))
4500 /* It is safe to keep the current recorded timestamp and size */
4501 keep_recorded_info = TRUE;
4504 /* Clean up any temporary files. */
4506 /* Remove the INSTALL_FROM file, as long as it doesn't refer to the
4508 if (install_from != NULL
4509 && strcmp(install_from, fb->local_abspath) != 0)
4511 SVN_ERR(svn_wc__wq_build_file_remove(&work_item, eb->db,
4512 fb->local_abspath, install_from,
4513 scratch_pool, scratch_pool));
4514 all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4520 /* Adding or updating a BASE node under a locally added node. */
4521 apr_hash_t *fake_actual_props;
4523 if (fb->adding_file)
4524 fake_actual_props = apr_hash_make(scratch_pool);
4526 fake_actual_props = current_base_props;
4528 /* Store the incoming props (sent as propchanges) in new_base_props
4529 and create a set of new actual props to use for notifications */
4530 new_base_props = svn_prop__patch(current_base_props, regular_prop_changes,
4532 SVN_ERR(svn_wc__merge_props(&conflict_skel,
4537 NULL /* server_baseprops (not merging) */,
4538 current_base_props /* pristine_props */,
4539 fake_actual_props /* actual_props */,
4540 regular_prop_changes, /* propchanges */
4544 if (fb->new_text_base_sha1_checksum)
4545 content_state = svn_wc_notify_state_changed;
4547 content_state = svn_wc_notify_state_unchanged;
4550 /* Insert/replace the BASE node with all of the new metadata. */
4552 /* Set the 'checksum' column of the file's BASE_NODE row to
4553 * NEW_TEXT_BASE_SHA1_CHECKSUM. The pristine text identified by that
4554 * checksum is already in the pristine store. */
4555 new_checksum = fb->new_text_base_sha1_checksum;
4557 /* If we don't have a NEW checksum, then the base must not have changed.
4558 Just carry over the old checksum. */
4559 if (new_checksum == NULL)
4560 new_checksum = fb->original_checksum;
4564 SVN_ERR(complete_conflict(conflict_skel,
4567 fb->old_repos_relpath,
4570 svn_node_file, svn_node_file,
4571 fb->pool, scratch_pool));
4573 SVN_ERR(svn_wc__conflict_create_markers(&work_item,
4574 eb->db, fb->local_abspath,
4576 scratch_pool, scratch_pool));
4578 all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4582 /* Any inherited props to be set set for this base node? */
4583 if (eb->wcroot_iprops)
4585 iprops = svn_hash_gets(eb->wcroot_iprops, fb->local_abspath);
4587 /* close_edit may also update iprops for switched nodes, catching
4588 those for which close_directory is never called (e.g. a switch
4589 with no changes). So as a minor optimization we remove any
4590 iprops from the hash so as not to set them again in
4593 svn_hash_sets(eb->wcroot_iprops, fb->local_abspath, NULL);
4596 SVN_ERR(svn_wc__db_base_add_file(eb->db, fb->local_abspath,
4599 eb->repos_root, eb->repos_uuid,
4600 *eb->target_revision,
4606 (dav_prop_changes->nelts > 0)
4607 ? svn_prop_array_to_hash(
4611 (fb->add_existed && fb->adding_file),
4612 (! fb->shadowed) && new_base_props,
4616 (fb->shadowed && fb->obstruction_found),
4621 if (conflict_skel && eb->conflict_func)
4622 SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, fb->local_abspath,
4624 NULL /* merge_options */,
4631 /* Deal with the WORKING tree, based on updates to the BASE tree. */
4633 svn_hash_sets(fb->dir_baton->not_present_files, fb->name, NULL);
4635 /* Send a notification to the callback function. (Skip notifications
4636 about files which were already notified for another reason.) */
4637 if (eb->notify_func && !fb->already_notified
4638 && (fb->edited || lock_state == svn_wc_notify_lock_state_unlocked))
4640 svn_wc_notify_t *notify;
4641 svn_wc_notify_action_t action = svn_wc_notify_update_update;
4645 if (fb->shadowed || fb->edit_obstructed)
4646 action = fb->adding_file
4647 ? svn_wc_notify_update_shadowed_add
4648 : svn_wc_notify_update_shadowed_update;
4649 else if (fb->obstruction_found || fb->add_existed)
4651 if (content_state != svn_wc_notify_state_conflicted)
4652 action = svn_wc_notify_exists;
4654 else if (fb->adding_file)
4656 action = svn_wc_notify_update_add;
4661 SVN_ERR_ASSERT(lock_state == svn_wc_notify_lock_state_unlocked);
4662 action = svn_wc_notify_update_broken_lock;
4665 /* If the file was moved-away, notify for the moved-away node.
4666 * The original location only had its BASE info changed and
4667 * we don't usually notify about such changes. */
4668 notify = svn_wc_create_notify(fb->local_abspath, action, scratch_pool);
4669 notify->kind = svn_node_file;
4670 notify->content_state = content_state;
4671 notify->prop_state = prop_state;
4672 notify->lock_state = lock_state;
4673 notify->revision = *eb->target_revision;
4674 notify->old_revision = fb->old_revision;
4676 /* Fetch the mimetype from the actual properties */
4677 notify->mime_type = svn_prop_get_value(new_actual_props,
4678 SVN_PROP_MIME_TYPE);
4680 eb->notify_func(eb->notify_baton, notify, scratch_pool);
4683 svn_pool_destroy(fb->pool); /* Destroy scratch_pool */
4685 /* We have one less referrer to the directory */
4686 SVN_ERR(maybe_release_dir_info(pdb));
4688 return SVN_NO_ERROR;
4692 /* An svn_delta_editor_t function. */
4693 static svn_error_t *
4694 close_edit(void *edit_baton,
4697 struct edit_baton *eb = edit_baton;
4698 apr_pool_t *scratch_pool = eb->pool;
4700 /* The editor didn't even open the root; we have to take care of
4701 some cleanup stuffs. */
4702 if (! eb->root_opened
4703 && *eb->target_basename == '\0')
4705 /* We need to "un-incomplete" the root directory. */
4706 SVN_ERR(svn_wc__db_temp_op_end_directory_update(eb->db,
4711 /* By definition, anybody "driving" this editor for update or switch
4712 purposes at a *minimum* must have called set_target_revision() at
4713 the outset, and close_edit() at the end -- even if it turned out
4714 that no changes ever had to be made, and open_root() was never
4715 called. That's fine. But regardless, when the edit is over,
4716 this editor needs to make sure that *all* paths have had their
4717 revisions bumped to the new target revision. */
4719 /* Make sure our update target now has the new working revision.
4720 Also, if this was an 'svn switch', then rewrite the target's
4721 url. All of this tweaking might happen recursively! Note
4722 that if eb->target is NULL, that's okay (albeit "sneaky",
4725 /* Extra check: if the update did nothing but make its target
4726 'deleted', then do *not* run cleanup on the target, as it
4727 will only remove the deleted entry! */
4728 if (! eb->target_deleted)
4730 SVN_ERR(svn_wc__db_op_bump_revisions_post_update(eb->db,
4732 eb->requested_depth,
4736 *(eb->target_revision),
4743 if (*eb->target_basename != '\0')
4745 svn_wc__db_status_t status;
4748 /* Note: we are fetching information about the *target*, not anchor.
4749 There is no guarantee that the target has a BASE node.
4752 The node was not present in BASE, but locally-added, and the
4753 update did not create a new BASE node "under" the local-add.
4755 If there is no BASE node for the target, then we certainly don't
4756 have to worry about removing it. */
4757 err = svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL, NULL,
4758 NULL, NULL, NULL, NULL, NULL, NULL,
4759 NULL, NULL, NULL, NULL,
4760 eb->db, eb->target_abspath,
4761 scratch_pool, scratch_pool);
4764 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
4765 return svn_error_trace(err);
4767 svn_error_clear(err);
4769 else if (status == svn_wc__db_status_excluded)
4771 /* There is a small chance that the explicit target of an update/
4772 switch is gone in the repository, in that specific case the
4773 node hasn't been re-added to the BASE tree by this update.
4775 If so, we should get rid of this excluded node now. */
4777 SVN_ERR(svn_wc__db_base_remove(eb->db, eb->target_abspath,
4778 FALSE /* keep_as_working */,
4779 FALSE /* queue_deletes */,
4780 FALSE /* remove_locks */,
4782 NULL, NULL, scratch_pool));
4787 /* The edit is over: run the wq with proper cancel support,
4788 but first kill the handler that would run it on the pool
4789 cleanup at the end of this function. */
4790 apr_pool_cleanup_kill(eb->pool, eb, cleanup_edit_baton);
4792 SVN_ERR(svn_wc__wq_run(eb->db, eb->wcroot_abspath,
4793 eb->cancel_func, eb->cancel_baton,
4796 /* The edit is over, free its pool.
4797 ### No, this is wrong. Who says this editor/baton won't be used
4798 again? But the change is not merely to remove this call. We
4799 should also make eb->pool not be a subpool (see make_editor),
4800 and change callers of svn_client_{checkout,update,switch} to do
4801 better pool management. ### */
4803 svn_pool_destroy(eb->pool);
4805 return SVN_NO_ERROR;
4809 /*** Returning editors. ***/
4811 /* Helper for the three public editor-supplying functions. */
4812 static svn_error_t *
4813 make_editor(svn_revnum_t *target_revision,
4815 const char *anchor_abspath,
4816 const char *target_basename,
4817 apr_hash_t *wcroot_iprops,
4818 svn_boolean_t use_commit_times,
4819 const char *switch_url,
4821 svn_boolean_t depth_is_sticky,
4822 svn_boolean_t allow_unver_obstructions,
4823 svn_boolean_t adds_as_modification,
4824 svn_boolean_t server_performs_filtering,
4825 svn_boolean_t clean_checkout,
4826 svn_wc_notify_func2_t notify_func,
4828 svn_cancel_func_t cancel_func,
4830 svn_wc_dirents_func_t fetch_dirents_func,
4831 void *fetch_dirents_baton,
4832 svn_wc_conflict_resolver_func2_t conflict_func,
4833 void *conflict_baton,
4834 svn_wc_external_update_t external_func,
4835 void *external_baton,
4836 const char *diff3_cmd,
4837 const apr_array_header_t *preserved_exts,
4838 const svn_delta_editor_t **editor,
4840 apr_pool_t *result_pool,
4841 apr_pool_t *scratch_pool)
4843 struct edit_baton *eb;
4845 apr_pool_t *edit_pool = svn_pool_create(result_pool);
4846 svn_delta_editor_t *tree_editor = svn_delta_default_editor(edit_pool);
4847 const svn_delta_editor_t *inner_editor;
4848 const char *repos_root, *repos_uuid;
4849 struct svn_wc__shim_fetch_baton_t *sfb;
4850 svn_delta_shim_callbacks_t *shim_callbacks =
4851 svn_delta_shim_callbacks_default(edit_pool);
4853 /* An unknown depth can't be sticky. */
4854 if (depth == svn_depth_unknown)
4855 depth_is_sticky = FALSE;
4857 /* Get the anchor's repository root and uuid. The anchor must already exist
4859 SVN_ERR(svn_wc__db_scan_base_repos(NULL, &repos_root, &repos_uuid,
4861 result_pool, scratch_pool));
4863 /* With WC-NG we need a valid repository root */
4864 SVN_ERR_ASSERT(repos_root != NULL && repos_uuid != NULL);
4866 /* Disallow a switch operation to change the repository root of the target,
4867 if that is known. */
4868 if (switch_url && !svn_uri__is_ancestor(repos_root, switch_url))
4869 return svn_error_createf(SVN_ERR_WC_INVALID_SWITCH, NULL,
4870 _("'%s'\nis not the same repository as\n'%s'"),
4871 switch_url, repos_root);
4873 /* Construct an edit baton. */
4874 eb = apr_pcalloc(edit_pool, sizeof(*eb));
4875 eb->pool = edit_pool;
4876 eb->use_commit_times = use_commit_times;
4877 eb->target_revision = target_revision;
4878 eb->repos_root = repos_root;
4879 eb->repos_uuid = repos_uuid;
4881 eb->target_basename = target_basename;
4882 eb->anchor_abspath = anchor_abspath;
4883 eb->wcroot_iprops = wcroot_iprops;
4885 SVN_ERR(svn_wc__db_get_wcroot(&eb->wcroot_abspath, db, anchor_abspath,
4886 edit_pool, scratch_pool));
4889 eb->switch_relpath =
4890 svn_uri_skip_ancestor(repos_root, switch_url, scratch_pool);
4892 eb->switch_relpath = NULL;
4894 if (svn_path_is_empty(target_basename))
4895 eb->target_abspath = eb->anchor_abspath;
4897 eb->target_abspath = svn_dirent_join(eb->anchor_abspath, target_basename,
4900 eb->requested_depth = depth;
4901 eb->depth_is_sticky = depth_is_sticky;
4902 eb->notify_func = notify_func;
4903 eb->notify_baton = notify_baton;
4904 eb->external_func = external_func;
4905 eb->external_baton = external_baton;
4906 eb->diff3_cmd = diff3_cmd;
4907 eb->cancel_func = cancel_func;
4908 eb->cancel_baton = cancel_baton;
4909 eb->conflict_func = conflict_func;
4910 eb->conflict_baton = conflict_baton;
4911 eb->allow_unver_obstructions = allow_unver_obstructions;
4912 eb->adds_as_modification = adds_as_modification;
4913 eb->clean_checkout = clean_checkout;
4914 eb->skipped_trees = apr_hash_make(edit_pool);
4915 eb->dir_dirents = apr_hash_make(edit_pool);
4916 eb->ext_patterns = preserved_exts;
4918 apr_pool_cleanup_register(edit_pool, eb, cleanup_edit_baton,
4919 apr_pool_cleanup_null);
4921 /* Construct an editor. */
4922 tree_editor->set_target_revision = set_target_revision;
4923 tree_editor->open_root = open_root;
4924 tree_editor->delete_entry = delete_entry;
4925 tree_editor->add_directory = add_directory;
4926 tree_editor->open_directory = open_directory;
4927 tree_editor->change_dir_prop = change_dir_prop;
4928 tree_editor->close_directory = close_directory;
4929 tree_editor->absent_directory = absent_directory;
4930 tree_editor->add_file = add_file;
4931 tree_editor->open_file = open_file;
4932 tree_editor->apply_textdelta = apply_textdelta;
4933 tree_editor->change_file_prop = change_file_prop;
4934 tree_editor->close_file = close_file;
4935 tree_editor->absent_file = absent_file;
4936 tree_editor->close_edit = close_edit;
4938 /* Fiddle with the type system. */
4939 inner_editor = tree_editor;
4942 if (!depth_is_sticky
4943 && depth != svn_depth_unknown
4944 && svn_depth_empty <= depth && depth < svn_depth_infinity
4945 && fetch_dirents_func)
4947 /* We are asked to perform an update at a depth less than the ambient
4948 depth. In this case the update won't describe additions that would
4949 have been reported if we updated at the ambient depth. */
4951 svn_node_kind_t dir_kind;
4952 svn_wc__db_status_t dir_status;
4953 const char *dir_repos_relpath;
4954 svn_depth_t dir_depth;
4956 /* we have to do this on the target of the update, not the anchor */
4957 err = svn_wc__db_base_get_info(&dir_status, &dir_kind, NULL,
4958 &dir_repos_relpath, NULL, NULL, NULL,
4959 NULL, NULL, &dir_depth, NULL, NULL, NULL,
4961 db, eb->target_abspath,
4962 scratch_pool, scratch_pool);
4965 && dir_kind == svn_node_dir
4966 && dir_status == svn_wc__db_status_normal)
4968 if (dir_depth > depth)
4970 apr_hash_t *dirents;
4972 /* If we switch, we should look at the new relpath */
4973 if (eb->switch_relpath)
4974 dir_repos_relpath = eb->switch_relpath;
4976 SVN_ERR(fetch_dirents_func(fetch_dirents_baton, &dirents,
4977 repos_root, dir_repos_relpath,
4978 edit_pool, scratch_pool));
4980 if (dirents != NULL && apr_hash_count(dirents))
4981 svn_hash_sets(eb->dir_dirents,
4982 apr_pstrdup(edit_pool, dir_repos_relpath),
4986 if (depth == svn_depth_immediates)
4988 /* Worst case scenario of issue #3569 fix: We have to do the
4989 same for all existing subdirs, but then we check for
4991 const apr_array_header_t *children;
4992 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
4994 SVN_ERR(svn_wc__db_base_get_children(&children, db,
4999 for (i = 0; i < children->nelts; i++)
5001 const char *child_abspath;
5002 const char *child_name;
5004 svn_pool_clear(iterpool);
5006 child_name = APR_ARRAY_IDX(children, i, const char *);
5008 child_abspath = svn_dirent_join(eb->target_abspath,
5009 child_name, iterpool);
5011 SVN_ERR(svn_wc__db_base_get_info(&dir_status, &dir_kind,
5012 NULL, &dir_repos_relpath,
5013 NULL, NULL, NULL, NULL,
5014 NULL, &dir_depth, NULL,
5015 NULL, NULL, NULL, NULL,
5018 iterpool, iterpool));
5020 if (dir_kind == svn_node_dir
5021 && dir_status == svn_wc__db_status_normal
5022 && dir_depth > svn_depth_empty)
5024 apr_hash_t *dirents;
5026 /* If we switch, we should look at the new relpath */
5027 if (eb->switch_relpath)
5028 dir_repos_relpath = svn_relpath_join(
5030 child_name, iterpool);
5032 SVN_ERR(fetch_dirents_func(fetch_dirents_baton, &dirents,
5033 repos_root, dir_repos_relpath,
5034 edit_pool, iterpool));
5036 if (dirents != NULL && apr_hash_count(dirents))
5037 svn_hash_sets(eb->dir_dirents,
5038 apr_pstrdup(edit_pool,
5045 else if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
5046 svn_error_clear(err);
5051 /* We need to limit the scope of our operation to the ambient depths
5052 present in the working copy already, but only if the requested
5053 depth is not sticky. If a depth was explicitly requested,
5054 libsvn_delta/depth_filter_editor.c will ensure that we never see
5055 editor calls that extend beyond the scope of the requested depth.
5056 But even what we do so might extend beyond the scope of our
5057 ambient depth. So we use another filtering editor to avoid
5058 modifying the ambient working copy depth when not asked to do so.
5059 (This can also be skipped if the server understands depth.) */
5060 if (!server_performs_filtering
5061 && !depth_is_sticky)
5062 SVN_ERR(svn_wc__ambient_depth_filter_editor(&inner_editor,
5071 SVN_ERR(svn_delta_get_cancellation_editor(cancel_func,
5079 sfb = apr_palloc(result_pool, sizeof(*sfb));
5081 sfb->base_abspath = eb->anchor_abspath;
5082 sfb->fetch_base = TRUE;
5084 shim_callbacks->fetch_kind_func = svn_wc__fetch_kind_func;
5085 shim_callbacks->fetch_props_func = svn_wc__fetch_props_func;
5086 shim_callbacks->fetch_base_func = svn_wc__fetch_base_func;
5087 shim_callbacks->fetch_baton = sfb;
5089 SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton,
5090 NULL, NULL, shim_callbacks,
5091 result_pool, scratch_pool));
5093 return SVN_NO_ERROR;
5098 svn_wc__get_update_editor(const svn_delta_editor_t **editor,
5100 svn_revnum_t *target_revision,
5101 svn_wc_context_t *wc_ctx,
5102 const char *anchor_abspath,
5103 const char *target_basename,
5104 apr_hash_t *wcroot_iprops,
5105 svn_boolean_t use_commit_times,
5107 svn_boolean_t depth_is_sticky,
5108 svn_boolean_t allow_unver_obstructions,
5109 svn_boolean_t adds_as_modification,
5110 svn_boolean_t server_performs_filtering,
5111 svn_boolean_t clean_checkout,
5112 const char *diff3_cmd,
5113 const apr_array_header_t *preserved_exts,
5114 svn_wc_dirents_func_t fetch_dirents_func,
5115 void *fetch_dirents_baton,
5116 svn_wc_conflict_resolver_func2_t conflict_func,
5117 void *conflict_baton,
5118 svn_wc_external_update_t external_func,
5119 void *external_baton,
5120 svn_cancel_func_t cancel_func,
5122 svn_wc_notify_func2_t notify_func,
5124 apr_pool_t *result_pool,
5125 apr_pool_t *scratch_pool)
5127 return make_editor(target_revision, wc_ctx->db, anchor_abspath,
5128 target_basename, wcroot_iprops, use_commit_times,
5129 NULL, depth, depth_is_sticky, allow_unver_obstructions,
5130 adds_as_modification, server_performs_filtering,
5132 notify_func, notify_baton,
5133 cancel_func, cancel_baton,
5134 fetch_dirents_func, fetch_dirents_baton,
5135 conflict_func, conflict_baton,
5136 external_func, external_baton,
5137 diff3_cmd, preserved_exts, editor, edit_baton,
5138 result_pool, scratch_pool);
5142 svn_wc__get_switch_editor(const svn_delta_editor_t **editor,
5144 svn_revnum_t *target_revision,
5145 svn_wc_context_t *wc_ctx,
5146 const char *anchor_abspath,
5147 const char *target_basename,
5148 const char *switch_url,
5149 apr_hash_t *wcroot_iprops,
5150 svn_boolean_t use_commit_times,
5152 svn_boolean_t depth_is_sticky,
5153 svn_boolean_t allow_unver_obstructions,
5154 svn_boolean_t server_performs_filtering,
5155 const char *diff3_cmd,
5156 const apr_array_header_t *preserved_exts,
5157 svn_wc_dirents_func_t fetch_dirents_func,
5158 void *fetch_dirents_baton,
5159 svn_wc_conflict_resolver_func2_t conflict_func,
5160 void *conflict_baton,
5161 svn_wc_external_update_t external_func,
5162 void *external_baton,
5163 svn_cancel_func_t cancel_func,
5165 svn_wc_notify_func2_t notify_func,
5167 apr_pool_t *result_pool,
5168 apr_pool_t *scratch_pool)
5170 SVN_ERR_ASSERT(switch_url && svn_uri_is_canonical(switch_url, scratch_pool));
5172 return make_editor(target_revision, wc_ctx->db, anchor_abspath,
5173 target_basename, wcroot_iprops, use_commit_times,
5175 depth, depth_is_sticky, allow_unver_obstructions,
5176 FALSE /* adds_as_modification */,
5177 server_performs_filtering,
5178 FALSE /* clean_checkout */,
5179 notify_func, notify_baton,
5180 cancel_func, cancel_baton,
5181 fetch_dirents_func, fetch_dirents_baton,
5182 conflict_func, conflict_baton,
5183 external_func, external_baton,
5184 diff3_cmd, preserved_exts,
5186 result_pool, scratch_pool);
5191 /* ### Note that this function is completely different from the rest of the
5192 update editor in what it updates. The update editor changes only BASE
5193 and ACTUAL and this function just changes WORKING and ACTUAL.
5195 In the entries world this function shared a lot of code with the
5196 update editor but in the wonderful new WC-NG world it will probably
5197 do more and more by itself and would be more logically grouped with
5198 the add/copy functionality in adm_ops.c and copy.c. */
5200 svn_wc_add_repos_file4(svn_wc_context_t *wc_ctx,
5201 const char *local_abspath,
5202 svn_stream_t *new_base_contents,
5203 svn_stream_t *new_contents,
5204 apr_hash_t *new_base_props,
5205 apr_hash_t *new_props,
5206 const char *copyfrom_url,
5207 svn_revnum_t copyfrom_rev,
5208 svn_cancel_func_t cancel_func,
5210 apr_pool_t *scratch_pool)
5212 svn_wc__db_t *db = wc_ctx->db;
5213 const char *dir_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
5214 svn_wc__db_status_t status;
5215 svn_node_kind_t kind;
5216 const char *tmp_text_base_abspath;
5217 svn_checksum_t *new_text_base_md5_checksum;
5218 svn_checksum_t *new_text_base_sha1_checksum;
5219 const char *source_abspath = NULL;
5220 svn_skel_t *all_work_items = NULL;
5221 svn_skel_t *work_item;
5222 const char *repos_root_url;
5223 const char *repos_uuid;
5224 const char *original_repos_relpath;
5225 svn_revnum_t changed_rev;
5226 apr_time_t changed_date;
5227 const char *changed_author;
5229 apr_pool_t *pool = scratch_pool;
5231 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5232 SVN_ERR_ASSERT(new_base_contents != NULL);
5233 SVN_ERR_ASSERT(new_base_props != NULL);
5235 /* We should have a write lock on this file's parent directory. */
5236 SVN_ERR(svn_wc__write_check(db, dir_abspath, pool));
5238 err = svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5239 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5240 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5242 db, local_abspath, scratch_pool, scratch_pool);
5244 if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
5245 return svn_error_trace(err);
5247 svn_error_clear(err);
5251 case svn_wc__db_status_not_present:
5252 case svn_wc__db_status_deleted:
5255 return svn_error_createf(SVN_ERR_ENTRY_EXISTS, NULL,
5256 _("Node '%s' exists."),
5257 svn_dirent_local_style(local_abspath,
5261 SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, &repos_root_url,
5262 &repos_uuid, NULL, NULL, NULL, NULL, NULL, NULL,
5263 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5264 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5265 db, dir_abspath, scratch_pool, scratch_pool));
5269 case svn_wc__db_status_normal:
5270 case svn_wc__db_status_added:
5272 case svn_wc__db_status_deleted:
5274 svn_error_createf(SVN_ERR_WC_SCHEDULE_CONFLICT, NULL,
5275 _("Can't add '%s' to a parent directory"
5276 " scheduled for deletion"),
5277 svn_dirent_local_style(local_abspath,
5280 return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, err,
5281 _("Can't find parent directory's node while"
5282 " trying to add '%s'"),
5283 svn_dirent_local_style(local_abspath,
5286 if (kind != svn_node_dir)
5287 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
5288 _("Can't schedule an addition of '%s'"
5289 " below a not-directory node"),
5290 svn_dirent_local_style(local_abspath,
5293 /* Fabricate the anticipated new URL of the target and check the
5294 copyfrom URL to be in the same repository. */
5295 if (copyfrom_url != NULL)
5297 /* Find the repository_root via the parent directory, which
5298 is always versioned before this function is called */
5300 if (!repos_root_url)
5302 /* The parent is an addition, scan upwards to find the right info */
5303 SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, NULL,
5304 &repos_root_url, &repos_uuid,
5305 NULL, NULL, NULL, NULL,
5306 wc_ctx->db, dir_abspath,
5307 scratch_pool, scratch_pool));
5309 SVN_ERR_ASSERT(repos_root_url);
5311 original_repos_relpath =
5312 svn_uri_skip_ancestor(repos_root_url, copyfrom_url, scratch_pool);
5314 if (!original_repos_relpath)
5315 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
5316 _("Copyfrom-url '%s' has different repository"
5318 copyfrom_url, repos_root_url);
5322 original_repos_relpath = NULL;
5323 copyfrom_rev = SVN_INVALID_REVNUM; /* Just to be sure. */
5326 /* Set CHANGED_* to reflect the entry props in NEW_BASE_PROPS, and
5327 filter NEW_BASE_PROPS so it contains only regular props. */
5329 apr_array_header_t *regular_props;
5330 apr_array_header_t *entry_props;
5332 SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(new_base_props, pool),
5333 &entry_props, NULL, ®ular_props,
5336 /* Put regular props back into a hash table. */
5337 new_base_props = svn_prop_array_to_hash(regular_props, pool);
5339 /* Get the change_* info from the entry props. */
5340 SVN_ERR(accumulate_last_change(&changed_rev,
5343 entry_props, pool, pool));
5346 /* Copy NEW_BASE_CONTENTS into a temporary file so our log can refer to
5347 it, and set TMP_TEXT_BASE_ABSPATH to its path. Compute its
5348 NEW_TEXT_BASE_MD5_CHECKSUM and NEW_TEXT_BASE_SHA1_CHECKSUM as we copy. */
5350 svn_stream_t *tmp_base_contents;
5352 SVN_ERR(svn_wc__open_writable_base(&tmp_base_contents,
5353 &tmp_text_base_abspath,
5354 &new_text_base_md5_checksum,
5355 &new_text_base_sha1_checksum,
5356 wc_ctx->db, local_abspath,
5358 SVN_ERR(svn_stream_copy3(new_base_contents, tmp_base_contents,
5359 cancel_func, cancel_baton, pool));
5362 /* If the caller gave us a new working file, copy it to a safe (temporary)
5363 location and set SOURCE_ABSPATH to that path. We'll then translate/copy
5364 that into place after the node's state has been created. */
5367 const char *temp_dir_abspath;
5368 svn_stream_t *tmp_contents;
5370 SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, db,
5371 local_abspath, pool, pool));
5372 SVN_ERR(svn_stream_open_unique(&tmp_contents, &source_abspath,
5373 temp_dir_abspath, svn_io_file_del_none,
5375 SVN_ERR(svn_stream_copy3(new_contents, tmp_contents,
5376 cancel_func, cancel_baton, pool));
5379 /* Install new text base for copied files. Added files do NOT have a
5381 if (copyfrom_url != NULL)
5383 SVN_ERR(svn_wc__db_pristine_install(db, tmp_text_base_abspath,
5384 new_text_base_sha1_checksum,
5385 new_text_base_md5_checksum, pool));
5389 /* ### There's something wrong around here. Sometimes (merge from a
5390 foreign repository, at least) we are called with copyfrom_url =
5391 NULL and an empty new_base_contents (and an empty set of
5392 new_base_props). Why an empty "new base"?
5394 That happens in merge_tests.py 54,87,88,89,143.
5396 In that case, having been given this supposed "new base" file, we
5397 copy it and calculate its checksum but do not install it. Why?
5400 To crudely work around one issue with this, that we shouldn't
5401 record a checksum in the database if we haven't installed the
5402 corresponding pristine text, for now we'll just set the checksum
5405 The proper solution is probably more like: the caller should pass
5406 NULL for the missing information, and this function should learn to
5409 new_text_base_sha1_checksum = NULL;
5410 new_text_base_md5_checksum = NULL;
5413 /* For added files without NEW_CONTENTS, then generate the working file
5414 from the provided "pristine" contents. */
5415 if (new_contents == NULL && copyfrom_url == NULL)
5416 source_abspath = tmp_text_base_abspath;
5419 svn_boolean_t record_fileinfo;
5421 /* If new contents were provided, then we do NOT want to record the
5422 file information. We assume the new contents do not match the
5423 "proper" values for RECORDED_SIZE and RECORDED_TIME. */
5424 record_fileinfo = (new_contents == NULL);
5426 /* Install the working copy file (with appropriate translation) from
5427 the appropriate source. SOURCE_ABSPATH will be NULL, indicating an
5428 installation from the pristine (available for copied/moved files),
5429 or it will specify a temporary file where we placed a "pristine"
5430 (for an added file) or a detranslated local-mods file. */
5431 SVN_ERR(svn_wc__wq_build_file_install(&work_item,
5434 FALSE /* use_commit_times */,
5437 all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool);
5439 /* If we installed from somewhere besides the official pristine, then
5440 it is a temporary file, which needs to be removed. */
5441 if (source_abspath != NULL)
5443 SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, local_abspath,
5446 all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool);
5450 /* ### ideally, we would have a single DB operation, and queue the work
5451 ### items on that. for now, we'll queue them with the second call. */
5453 SVN_ERR(svn_wc__db_op_copy_file(db, local_abspath,
5458 original_repos_relpath,
5459 original_repos_relpath ? repos_root_url
5461 original_repos_relpath ? repos_uuid : NULL,
5463 new_text_base_sha1_checksum,
5466 FALSE /* is_move */,
5467 NULL /* conflict */,
5471 return svn_error_trace(svn_wc__wq_run(db, dir_abspath,
5472 cancel_func, cancel_baton,
5477 svn_wc__complete_directory_add(svn_wc_context_t *wc_ctx,
5478 const char *local_abspath,
5479 apr_hash_t *new_original_props,
5480 const char *copyfrom_url,
5481 svn_revnum_t copyfrom_rev,
5482 apr_pool_t *scratch_pool)
5484 svn_wc__db_status_t status;
5485 svn_node_kind_t kind;
5486 const char *original_repos_relpath;
5487 const char *original_root_url;
5488 const char *original_uuid;
5489 svn_boolean_t had_props;
5490 svn_boolean_t props_mod;
5492 svn_revnum_t original_revision;
5493 svn_revnum_t changed_rev;
5494 apr_time_t changed_date;
5495 const char *changed_author;
5497 SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
5498 NULL, NULL, NULL, NULL, NULL,
5499 &original_repos_relpath, &original_root_url,
5500 &original_uuid, &original_revision, NULL, NULL,
5501 NULL, NULL, NULL, NULL, &had_props, &props_mod,
5503 wc_ctx->db, local_abspath,
5504 scratch_pool, scratch_pool));
5506 if (status != svn_wc__db_status_added
5507 || kind != svn_node_dir
5510 || !original_repos_relpath)
5512 return svn_error_createf(
5513 SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
5514 _("'%s' is not an unmodified copied directory"),
5515 svn_dirent_local_style(local_abspath, scratch_pool));
5517 if (original_revision != copyfrom_rev
5518 || strcmp(copyfrom_url,
5519 svn_path_url_add_component2(original_root_url,
5520 original_repos_relpath,
5523 return svn_error_createf(
5524 SVN_ERR_WC_COPYFROM_PATH_NOT_FOUND, NULL,
5525 _("Copyfrom '%s' doesn't match original location of '%s'"),
5527 svn_dirent_local_style(local_abspath, scratch_pool));
5531 apr_array_header_t *regular_props;
5532 apr_array_header_t *entry_props;
5534 SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(new_original_props,
5536 &entry_props, NULL, ®ular_props,
5539 /* Put regular props back into a hash table. */
5540 new_original_props = svn_prop_array_to_hash(regular_props, scratch_pool);
5542 /* Get the change_* info from the entry props. */
5543 SVN_ERR(accumulate_last_change(&changed_rev,
5546 entry_props, scratch_pool, scratch_pool));
5549 return svn_error_trace(
5550 svn_wc__db_op_copy_dir(wc_ctx->db, local_abspath,
5552 changed_rev, changed_date, changed_author,
5553 original_repos_relpath, original_root_url,
5554 original_uuid, original_revision,
5555 NULL /* children */,
5556 FALSE /* is_move */,
5558 NULL /* conflict */,
5559 NULL /* work_items */,