1 /*
2 * update_editor.c : main editor for checkouts and updates
3 *
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
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
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
20 * under the License.
21 * ====================================================================
22 */
23
24
25
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include <apr_pools.h>
30 #include <apr_hash.h>
31 #include <apr_md5.h>
32 #include <apr_tables.h>
33 #include <apr_strings.h>
34
35 #include "svn_types.h"
36 #include "svn_pools.h"
37 #include "svn_hash.h"
38 #include "svn_string.h"
39 #include "svn_dirent_uri.h"
40 #include "svn_path.h"
41 #include "svn_error.h"
42 #include "svn_io.h"
43 #include "svn_private_config.h"
44 #include "svn_time.h"
45
46 #include "wc.h"
47 #include "adm_files.h"
48 #include "conflicts.h"
49 #include "translate.h"
50 #include "workqueue.h"
51
52 #include "private/svn_subr_private.h"
53 #include "private/svn_wc_private.h"
54 #include "private/svn_editor.h"
55
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)
62
63 static svn_error_t *
64 path_join_under_root(const char **result_path,
65 const char *base_path,
66 const char *add_path,
67 apr_pool_t *result_pool);
68
69
70 /*
71 * This code handles "checkout" and "update" and "switch".
72 * A checkout is similar to an update that is only adding new items.
73 *
74 * The intended behaviour of "update" and "switch", focusing on the checks
75 * to be made before applying a change, is:
76 *
77 * For each incoming change:
78 * if target is already in conflict or obstructed:
79 * skip this change
80 * else
81 * if this action will cause a tree conflict:
82 * record the tree conflict
83 * skip this change
84 * else:
85 * make this change
86 *
87 * In more detail:
88 *
89 * For each incoming change:
90 *
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
97 *
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"
106 *
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"
113 *
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"
121 *
122 * 4. Apply the change:
123 * update the Base
124 * update the Working, possibly raising text/prop conflicts
125 * notify
126 *
127 * Notes:
128 *
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.
131 *
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.
135 *
136 * [*2] Details of which combinations of property and text changes conflict
137 * are not specified here.
138 *
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.
147 */
148
149
150 /*** batons ***/
151
152 struct edit_baton
153 {
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.
157
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;
161
162 /* Absolute variants of ANCHOR and TARGET */
163 const char *anchor_abspath;
164 const char *target_abspath;
165
166 /* The DB handle for managing the working copy state. */
167 svn_wc__db_t *db;
168
169 /* Array of file extension patterns to preserve as extensions in
170 generated conflict files. */
171 const apr_array_header_t *ext_patterns;
172
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.
176 May be NULL. */
177 apr_hash_t *wcroot_iprops;
178
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;
184
185 /* The requested depth of this edit. */
186 svn_depth_t requested_depth;
187
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;
191
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;
195
196 /* Was the root actually opened (was this a non-empty edit)? */
197 svn_boolean_t root_opened;
198
199 /* Was the update-target deleted? This is a special situation. */
200 svn_boolean_t target_deleted;
201
202 /* Allow unversioned obstructions when adding a path. */
203 svn_boolean_t allow_unver_obstructions;
204
205 /* Handle local additions as modifications of new nodes */
206 svn_boolean_t adds_as_modification;
207
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;
211
212 /* If this is a 'switch' operation, the new relpath of target_abspath,
213 else NULL. */
214 const char *switch_repos_relpath;
215
216 /* The URL to the root of the repository. */
217 const char *repos_root;
218
219 /* The UUID of the repos, or NULL. */
220 const char *repos_uuid;
221
222 /* External diff3 to use for merges (can be null, in which case
223 internal merge code is used). */
224 const char *diff3_cmd;
225
226 /* Externals handler */
227 svn_wc_external_update_t external_func;
228 void *external_baton;
229
230 /* This editor sends back notifications as it edits. */
231 svn_wc_notify_func2_t notify_func;
232 void *notify_baton;
233
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;
239 void *cancel_baton;
240
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;
245
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;
251
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;
257
258 /* Absolute path of the working copy root or NULL if not initialized yet */
259 const char *wcroot_abspath;
260
261 /* After closing the root directory a copy of its edited value */
262 svn_boolean_t edited;
263
264 apr_pool_t *pool;
265 };
266
267
268 /* Record in the edit baton EB that LOCAL_ABSPATH's base version is not being
269 * updated.
270 *
271 * Add to EB->skipped_trees a copy (allocated in EB->pool) of the string
272 * LOCAL_ABSPATH.
273 */
274 static svn_error_t *
remember_skipped_tree(struct edit_baton * eb,const char * local_abspath,apr_pool_t * scratch_pool)275 remember_skipped_tree(struct edit_baton *eb,
276 const char *local_abspath,
277 apr_pool_t *scratch_pool)
278 {
279 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
280
281 svn_hash_sets(eb->skipped_trees,
282 apr_pstrdup(eb->pool,
283 svn_dirent_skip_ancestor(eb->wcroot_abspath,
284 local_abspath)),
285 (void *)1);
286
287 return SVN_NO_ERROR;
288 }
289
290 /* Per directory baton. Lives in its own subpool of the parent directory
291 or of the edit baton if there is no parent directory */
292 struct dir_baton
293 {
294 /* Basename of this directory. */
295 const char *name;
296
297 /* Absolute path of this directory */
298 const char *local_abspath;
299
300 /* The repository relative path this directory will correspond to. */
301 const char *new_repos_relpath;
302
303 /* The revision of the directory before updating */
304 svn_revnum_t old_revision;
305
306 /* The repos_relpath before updating/switching */
307 const char *old_repos_relpath;
308
309 /* The global edit baton. */
310 struct edit_baton *edit_baton;
311
312 /* Baton for this directory's parent, or NULL if this is the root
313 directory. */
314 struct dir_baton *parent_baton;
315
316 /* Set if updates to this directory are skipped */
317 svn_boolean_t skip_this;
318
319 /* Set if there was a previous notification for this directory */
320 svn_boolean_t already_notified;
321
322 /* Set if this directory is being added during this editor drive. */
323 svn_boolean_t adding_dir;
324
325 /* Set on a node and its descendants are not present in the working copy
326 but should still be updated (not skipped). These nodes should all be
327 marked as deleted. */
328 svn_boolean_t shadowed;
329
330 /* Set on a node when the existing node is obstructed, and the edit operation
331 continues as semi-shadowed update */
332 svn_boolean_t edit_obstructed;
333
334 /* The (new) changed_* information, cached to avoid retrieving it later */
335 svn_revnum_t changed_rev;
336 apr_time_t changed_date;
337 const char *changed_author;
338
339 /* If not NULL, contains a mapping of const char* basenames of children that
340 have been deleted to their svn_skel_t* tree conflicts.
341 We store this hash to allow replacements to continue under a just
342 installed tree conflict.
343
344 The add after the delete will then update the tree conflicts information
345 and reinstall it. */
346 apr_hash_t *deletion_conflicts;
347
348 /* A hash of file names (only the hash key matters) seen by add_file and
349 add_directory and not yet added to the database, mapping to a const
350 char * node kind (via svn_node_kind_to_word(). */
351 apr_hash_t *not_present_nodes;
352
353 /* Set if an unversioned dir of the same name already existed in
354 this directory. */
355 svn_boolean_t obstruction_found;
356
357 /* Set if a dir of the same name already exists and is
358 scheduled for addition without history. */
359 svn_boolean_t add_existed;
360
361 /* An array of svn_prop_t structures, representing all the property
362 changes to be applied to this directory. */
363 apr_array_header_t *propchanges;
364
365 /* A boolean indicating whether this node or one of its children has
366 received any 'real' changes. Used to avoid tree conflicts for simple
367 entryprop changes, like lock management */
368 svn_boolean_t edited;
369
370 /* The tree conflict to install once the node is really edited */
371 svn_skel_t *edit_conflict;
372
373 /* The bump information for this directory. */
374 struct bump_dir_info *bump_info;
375
376 /* The depth of the directory in the wc (or inferred if added). Not
377 used for filtering; we have a separate wrapping editor for that. */
378 svn_depth_t ambient_depth;
379
380 /* Was the directory marked as incomplete before the update?
381 (In other words, are we resuming an interrupted update?)
382
383 If WAS_INCOMPLETE is set to TRUE we expect to receive all child nodes
384 and properties for/of the directory. If WAS_INCOMPLETE is FALSE then
385 we only receive the changes in/for children and properties.*/
386 svn_boolean_t was_incomplete;
387
388 /* The pool in which this baton itself is allocated. */
389 apr_pool_t *pool;
390
391 /* how many nodes are referring to baton? */
392 int ref_count;
393
394 };
395
396
397 struct handler_baton
398 {
399 svn_txdelta_window_handler_t apply_handler;
400 void *apply_baton;
401 apr_pool_t *pool;
402 struct file_baton *fb;
403
404 /* Where we are assembling the new file. */
405 svn_wc__db_install_data_t *install_data;
406
407 /* The expected source checksum of the text source or NULL if no base
408 checksum is available (MD5 if the server provides a checksum, SHA1 if
409 the server doesn't) */
410 svn_checksum_t *expected_source_checksum;
411
412 /* Why two checksums?
413 The editor currently provides an md5 which we use to detect corruption
414 during transmission. We use the sha1 inside libsvn_wc both for pristine
415 handling and corruption detection. In the future, the editor will also
416 provide a sha1, so we may not have to calculate both, but for the time
417 being, that's the way it is. */
418
419 /* The calculated checksum of the text source or NULL if the actual
420 checksum is not being calculated. The checksum kind is identical to the
421 kind of expected_source_checksum. */
422 svn_checksum_t *actual_source_checksum;
423
424 /* The stream used to calculate the source checksums */
425 svn_stream_t *source_checksum_stream;
426
427 /* A calculated MD5 digest of NEW_TEXT_BASE_TMP_ABSPATH.
428 This is initialized to all zeroes when the baton is created, then
429 populated with the MD5 digest of the resultant fulltext after the
430 last window is handled by the handler returned from
431 apply_textdelta(). */
432 unsigned char new_text_base_md5_digest[APR_MD5_DIGESTSIZE];
433
434 /* A calculated SHA-1 of NEW_TEXT_BASE_TMP_ABSPATH, which we'll use for
435 eventually writing the pristine. */
436 svn_checksum_t * new_text_base_sha1_checksum;
437 };
438
439
440 /* Get an empty file in the temporary area for WRI_ABSPATH. The file will
441 not be set for automatic deletion, and the name will be returned in
442 TMP_FILENAME.
443
444 This implementation creates a new empty file with a unique name.
445
446 ### This is inefficient for callers that just want an empty file to read
447 ### from. There could be (and there used to be) a permanent, shared
448 ### empty file for this purpose.
449
450 ### This is inefficient for callers that just want to reserve a unique
451 ### file name to create later. A better way may not be readily available.
452 */
453 static svn_error_t *
get_empty_tmp_file(const char ** tmp_filename,svn_wc__db_t * db,const char * wri_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)454 get_empty_tmp_file(const char **tmp_filename,
455 svn_wc__db_t *db,
456 const char *wri_abspath,
457 apr_pool_t *result_pool,
458 apr_pool_t *scratch_pool)
459 {
460 const char *temp_dir_abspath;
461
462 SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, db, wri_abspath,
463 scratch_pool, scratch_pool));
464 SVN_ERR(svn_io_open_unique_file3(NULL, tmp_filename, temp_dir_abspath,
465 svn_io_file_del_none,
466 scratch_pool, scratch_pool));
467
468 return SVN_NO_ERROR;
469 }
470
471 /* An APR pool cleanup handler. This runs the working queue for an
472 editor baton. */
473 static apr_status_t
cleanup_edit_baton(void * edit_baton)474 cleanup_edit_baton(void *edit_baton)
475 {
476 struct edit_baton *eb = edit_baton;
477 svn_error_t *err;
478 apr_pool_t *pool = apr_pool_parent_get(eb->pool);
479
480 err = svn_wc__wq_run(eb->db, eb->wcroot_abspath,
481 NULL /* cancel_func */, NULL /* cancel_baton */,
482 pool);
483
484 if (err)
485 {
486 apr_status_t apr_err = err->apr_err;
487 svn_error_clear(err);
488 return apr_err;
489 }
490 return APR_SUCCESS;
491 }
492
493 /* Calculate the new repos_relpath for a directory or file */
494 static svn_error_t *
calculate_repos_relpath(const char ** new_repos_relpath,const char * local_abspath,const char * old_repos_relpath,struct edit_baton * eb,struct dir_baton * pb,apr_pool_t * result_pool,apr_pool_t * scratch_pool)495 calculate_repos_relpath(const char **new_repos_relpath,
496 const char *local_abspath,
497 const char *old_repos_relpath,
498 struct edit_baton *eb,
499 struct dir_baton *pb,
500 apr_pool_t *result_pool,
501 apr_pool_t *scratch_pool)
502 {
503 const char *name = svn_dirent_basename(local_abspath, NULL);
504
505 /* Figure out the new_repos_relpath for this directory. */
506 if (eb->switch_repos_relpath)
507 {
508 /* Handle switches... */
509
510 if (pb == NULL)
511 {
512 if (*eb->target_basename == '\0')
513 {
514 /* No parent baton and target_basename=="" means that we are
515 the target of the switch. Thus, our new_repos_relpath will be
516 the switch_repos_relpath. */
517 *new_repos_relpath = eb->switch_repos_relpath;
518 }
519 else
520 {
521 /* This node is NOT the target of the switch (one of our
522 children is the target); therefore, it must already exist.
523 Get its old REPOS_RELPATH, as it won't be changing. */
524 *new_repos_relpath = apr_pstrdup(result_pool, old_repos_relpath);
525 }
526 }
527 else
528 {
529 /* This directory is *not* the root (has a parent). If there is
530 no grandparent, then we may have anchored at the parent,
531 and self is the target. If we match the target, then set
532 new_repos_relpath to the switch_repos_relpath.
533
534 Otherwise, we simply extend new_repos_relpath from the parent. */
535
536 if (pb->parent_baton == NULL
537 && strcmp(eb->target_basename, name) == 0)
538 *new_repos_relpath = eb->switch_repos_relpath;
539 else
540 *new_repos_relpath = svn_relpath_join(pb->new_repos_relpath, name,
541 result_pool);
542 }
543 }
544 else /* must be an update */
545 {
546 /* If we are adding the node, then simply extend the parent's
547 relpath for our own. */
548 if (old_repos_relpath == NULL)
549 {
550 SVN_ERR_ASSERT(pb != NULL);
551 *new_repos_relpath = svn_relpath_join(pb->new_repos_relpath, name,
552 result_pool);
553 }
554 else
555 {
556 *new_repos_relpath = apr_pstrdup(result_pool, old_repos_relpath);
557 }
558 }
559
560 return SVN_NO_ERROR;
561 }
562
563 /* Make a new dir baton in a subpool of PB->pool. PB is the parent baton.
564 If PATH and PB are NULL, this is the root directory of the edit; in this
565 case, make the new dir baton in a subpool of EB->pool.
566 ADDING should be TRUE if we are adding this directory. */
567 static svn_error_t *
make_dir_baton(struct dir_baton ** d_p,const char * path,struct edit_baton * eb,struct dir_baton * pb,svn_boolean_t adding,apr_pool_t * scratch_pool)568 make_dir_baton(struct dir_baton **d_p,
569 const char *path,
570 struct edit_baton *eb,
571 struct dir_baton *pb,
572 svn_boolean_t adding,
573 apr_pool_t *scratch_pool)
574 {
575 apr_pool_t *dir_pool;
576 struct dir_baton *d;
577
578 if (pb != NULL)
579 dir_pool = svn_pool_create(pb->pool);
580 else
581 dir_pool = svn_pool_create(eb->pool);
582
583 SVN_ERR_ASSERT(path || (! pb));
584
585 /* Okay, no easy out, so allocate and initialize a dir baton. */
586 d = apr_pcalloc(dir_pool, sizeof(*d));
587
588 /* Construct the PATH and baseNAME of this directory. */
589 if (path)
590 {
591 d->name = svn_dirent_basename(path, dir_pool);
592 SVN_ERR(path_join_under_root(&d->local_abspath,
593 pb->local_abspath, d->name, dir_pool));
594 }
595 else
596 {
597 /* This is the root baton. */
598 d->name = NULL;
599 d->local_abspath = eb->anchor_abspath;
600 }
601
602 d->edit_baton = eb;
603 d->parent_baton = pb;
604 d->pool = dir_pool;
605 d->propchanges = apr_array_make(dir_pool, 1, sizeof(svn_prop_t));
606 d->obstruction_found = FALSE;
607 d->add_existed = FALSE;
608 d->ref_count = 1;
609 d->old_revision = SVN_INVALID_REVNUM;
610 d->adding_dir = adding;
611 d->changed_rev = SVN_INVALID_REVNUM;
612 d->not_present_nodes = apr_hash_make(dir_pool);
613
614 /* Copy some flags from the parent baton */
615 if (pb)
616 {
617 d->skip_this = pb->skip_this;
618 d->shadowed = pb->shadowed || pb->edit_obstructed;
619
620 /* the parent's bump info has one more referer */
621 pb->ref_count++;
622 }
623
624 /* The caller of this function needs to fill these in. */
625 d->ambient_depth = svn_depth_unknown;
626 d->was_incomplete = FALSE;
627
628 *d_p = d;
629 return SVN_NO_ERROR;
630 }
631
632 /* Forward declarations. */
633 static svn_error_t *
634 already_in_a_tree_conflict(svn_boolean_t *conflicted,
635 svn_boolean_t *ignored,
636 svn_wc__db_t *db,
637 const char *local_abspath,
638 apr_pool_t *scratch_pool);
639
640
641 static void
do_notification(const struct edit_baton * eb,const char * local_abspath,svn_node_kind_t kind,svn_wc_notify_action_t action,apr_pool_t * scratch_pool)642 do_notification(const struct edit_baton *eb,
643 const char *local_abspath,
644 svn_node_kind_t kind,
645 svn_wc_notify_action_t action,
646 apr_pool_t *scratch_pool)
647 {
648 svn_wc_notify_t *notify;
649
650 if (eb->notify_func == NULL)
651 return;
652
653 notify = svn_wc_create_notify(local_abspath, action, scratch_pool);
654 notify->kind = kind;
655
656 (*eb->notify_func)(eb->notify_baton, notify, scratch_pool);
657 }
658
659 /* Decrement the directory's reference count. If it hits zero,
660 then this directory is "done". This means it is safe to clear its pool.
661
662 In addition, when the directory is "done", we recurse to possible cleanup
663 the parent directory.
664 */
665 static svn_error_t *
maybe_release_dir_info(struct dir_baton * db)666 maybe_release_dir_info(struct dir_baton *db)
667 {
668 db->ref_count--;
669
670 if (!db->ref_count)
671 {
672 struct dir_baton *pb = db->parent_baton;
673
674 svn_pool_destroy(db->pool);
675
676 if (pb)
677 SVN_ERR(maybe_release_dir_info(pb));
678 }
679
680 return SVN_NO_ERROR;
681 }
682
683 /* Per file baton. Lives in its own subpool below the pool of the parent
684 directory */
685 struct file_baton
686 {
687 /* Pool specific to this file_baton. */
688 apr_pool_t *pool;
689
690 /* Name of this file (its entry in the directory). */
691 const char *name;
692
693 /* Absolute path to this file */
694 const char *local_abspath;
695
696 /* The repository relative path this file will correspond to. */
697 const char *new_repos_relpath;
698
699 /* The revision of the file before updating */
700 svn_revnum_t old_revision;
701
702 /* The repos_relpath before updating/switching */
703 const char *old_repos_relpath;
704
705 /* The global edit baton. */
706 struct edit_baton *edit_baton;
707
708 /* The parent directory of this file. */
709 struct dir_baton *dir_baton;
710
711 /* Set if updates to this directory are skipped */
712 svn_boolean_t skip_this;
713
714 /* Set if there was a previous notification */
715 svn_boolean_t already_notified;
716
717 /* Set if this file is new. */
718 svn_boolean_t adding_file;
719
720 /* Set if an unversioned file of the same name already existed in
721 this directory. */
722 svn_boolean_t obstruction_found;
723
724 /* Set if a file of the same name already exists and is
725 scheduled for addition without history. */
726 svn_boolean_t add_existed;
727
728 /* Set if this file is being added in the BASE layer, but is not-present
729 in the working copy (replaced, deleted, etc.). */
730 svn_boolean_t shadowed;
731
732 /* Set on a node when the existing node is obstructed, and the edit operation
733 continues as semi-shadowed update */
734 svn_boolean_t edit_obstructed;
735
736 /* The (new) changed_* information, cached to avoid retrieving it later */
737 svn_revnum_t changed_rev;
738 apr_time_t changed_date;
739 const char *changed_author;
740
741 /* If there are file content changes, these are the checksums of the
742 resulting new text base, which is in the pristine store, else NULL. */
743 const svn_checksum_t *new_text_base_md5_checksum;
744 const svn_checksum_t *new_text_base_sha1_checksum;
745
746 /* The checksum of the file before the update */
747 const svn_checksum_t *original_checksum;
748
749 /* An array of svn_prop_t structures, representing all the property
750 changes to be applied to this file. Once a file baton is
751 initialized, this is never NULL, but it may have zero elements. */
752 apr_array_header_t *propchanges;
753
754 /* For existing files, whether there are local modifications. FALSE for added
755 files */
756 svn_boolean_t local_prop_mods;
757
758 /* Bump information for the directory this file lives in */
759 struct bump_dir_info *bump_info;
760
761 /* A boolean indicating whether this node or one of its children has
762 received any 'real' changes. Used to avoid tree conflicts for simple
763 entryprop changes, like lock management */
764 svn_boolean_t edited;
765
766 /* The tree conflict to install once the node is really edited */
767 svn_skel_t *edit_conflict;
768 };
769
770
771 /* Make a new file baton in a subpool of PB->pool. PB is the parent baton.
772 * PATH is relative to the root of the edit. ADDING tells whether this file
773 * is being added. */
774 static svn_error_t *
make_file_baton(struct file_baton ** f_p,struct dir_baton * pb,const char * path,svn_boolean_t adding,apr_pool_t * scratch_pool)775 make_file_baton(struct file_baton **f_p,
776 struct dir_baton *pb,
777 const char *path,
778 svn_boolean_t adding,
779 apr_pool_t *scratch_pool)
780 {
781 apr_pool_t *file_pool = svn_pool_create(pb->pool);
782 struct file_baton *f = apr_pcalloc(file_pool, sizeof(*f));
783
784 SVN_ERR_ASSERT(path);
785
786 /* Make the file's on-disk name. */
787 f->name = svn_dirent_basename(path, file_pool);
788 f->old_revision = SVN_INVALID_REVNUM;
789 SVN_ERR(path_join_under_root(&f->local_abspath,
790 pb->local_abspath, f->name, file_pool));
791
792 f->pool = file_pool;
793 f->edit_baton = pb->edit_baton;
794 f->propchanges = apr_array_make(file_pool, 1, sizeof(svn_prop_t));
795 f->bump_info = pb->bump_info;
796 f->adding_file = adding;
797 f->obstruction_found = FALSE;
798 f->add_existed = FALSE;
799 f->skip_this = pb->skip_this;
800 f->shadowed = pb->shadowed || pb->edit_obstructed;
801 f->dir_baton = pb;
802 f->changed_rev = SVN_INVALID_REVNUM;
803
804 /* the directory has one more referer now */
805 pb->ref_count++;
806
807 *f_p = f;
808 return SVN_NO_ERROR;
809 }
810
811 /* Complete a conflict skel by describing the update.
812 *
813 * LOCAL_KIND is the node kind of the tree conflict victim in the
814 * working copy.
815 *
816 * All temporary allocations are be made in SCRATCH_POOL, while allocations
817 * needed for the returned conflict struct are made in RESULT_POOL.
818 */
819 static svn_error_t *
complete_conflict(svn_skel_t * conflict,const struct edit_baton * eb,const char * local_abspath,const char * old_repos_relpath,svn_revnum_t old_revision,const char * new_repos_relpath,svn_node_kind_t local_kind,svn_node_kind_t target_kind,const svn_skel_t * delete_conflict,apr_pool_t * result_pool,apr_pool_t * scratch_pool)820 complete_conflict(svn_skel_t *conflict,
821 const struct edit_baton *eb,
822 const char *local_abspath,
823 const char *old_repos_relpath,
824 svn_revnum_t old_revision,
825 const char *new_repos_relpath,
826 svn_node_kind_t local_kind,
827 svn_node_kind_t target_kind,
828 const svn_skel_t *delete_conflict,
829 apr_pool_t *result_pool,
830 apr_pool_t *scratch_pool)
831 {
832 const svn_wc_conflict_version_t *original_version = NULL;
833 svn_wc_conflict_version_t *target_version;
834 svn_boolean_t is_complete;
835
836 SVN_ERR_ASSERT(new_repos_relpath);
837
838 if (!conflict)
839 return SVN_NO_ERROR; /* Not conflicted */
840
841 SVN_ERR(svn_wc__conflict_skel_is_complete(&is_complete, conflict));
842
843 if (is_complete)
844 return SVN_NO_ERROR; /* Already completed */
845
846 if (old_repos_relpath)
847 original_version = svn_wc_conflict_version_create2(eb->repos_root,
848 eb->repos_uuid,
849 old_repos_relpath,
850 old_revision,
851 local_kind,
852 result_pool);
853 else if (delete_conflict)
854 {
855 const apr_array_header_t *locations;
856
857 SVN_ERR(svn_wc__conflict_read_info(NULL, &locations, NULL, NULL, NULL,
858 eb->db, local_abspath,
859 delete_conflict,
860 scratch_pool, scratch_pool));
861
862 if (locations)
863 {
864 original_version = APR_ARRAY_IDX(locations, 0,
865 const svn_wc_conflict_version_t *);
866 }
867 }
868
869 target_version = svn_wc_conflict_version_create2(eb->repos_root,
870 eb->repos_uuid,
871 new_repos_relpath,
872 *eb->target_revision,
873 target_kind,
874 result_pool);
875
876 if (eb->switch_repos_relpath)
877 SVN_ERR(svn_wc__conflict_skel_set_op_switch(conflict,
878 original_version,
879 target_version,
880 result_pool, scratch_pool));
881 else
882 SVN_ERR(svn_wc__conflict_skel_set_op_update(conflict,
883 original_version,
884 target_version,
885 result_pool, scratch_pool));
886
887 return SVN_NO_ERROR;
888 }
889
890
891 /* Called when a directory is really edited, to avoid marking a
892 tree conflict on a node for a no-change edit */
893 static svn_error_t *
mark_directory_edited(struct dir_baton * db,apr_pool_t * scratch_pool)894 mark_directory_edited(struct dir_baton *db, apr_pool_t *scratch_pool)
895 {
896 if (db->edited)
897 return SVN_NO_ERROR;
898
899 if (db->parent_baton)
900 SVN_ERR(mark_directory_edited(db->parent_baton, scratch_pool));
901
902 db->edited = TRUE;
903
904 if (db->edit_conflict)
905 {
906 /* We have a (delayed) tree conflict to install */
907
908 SVN_ERR(complete_conflict(db->edit_conflict, db->edit_baton,
909 db->local_abspath,
910 db->old_repos_relpath, db->old_revision,
911 db->new_repos_relpath,
912 svn_node_dir, svn_node_dir,
913 NULL,
914 db->pool, scratch_pool));
915 SVN_ERR(svn_wc__db_op_mark_conflict(db->edit_baton->db,
916 db->local_abspath,
917 db->edit_conflict, NULL,
918 scratch_pool));
919
920 do_notification(db->edit_baton, db->local_abspath, svn_node_dir,
921 svn_wc_notify_tree_conflict, scratch_pool);
922 db->already_notified = TRUE;
923 }
924
925 return SVN_NO_ERROR;
926 }
927
928 /* Called when a file is really edited, to avoid marking a
929 tree conflict on a node for a no-change edit */
930 static svn_error_t *
mark_file_edited(struct file_baton * fb,apr_pool_t * scratch_pool)931 mark_file_edited(struct file_baton *fb, apr_pool_t *scratch_pool)
932 {
933 if (fb->edited)
934 return SVN_NO_ERROR;
935
936 SVN_ERR(mark_directory_edited(fb->dir_baton, scratch_pool));
937
938 fb->edited = TRUE;
939
940 if (fb->edit_conflict)
941 {
942 /* We have a (delayed) tree conflict to install */
943
944 SVN_ERR(complete_conflict(fb->edit_conflict, fb->edit_baton,
945 fb->local_abspath, fb->old_repos_relpath,
946 fb->old_revision, fb->new_repos_relpath,
947 svn_node_file, svn_node_file,
948 NULL,
949 fb->pool, scratch_pool));
950
951 SVN_ERR(svn_wc__db_op_mark_conflict(fb->edit_baton->db,
952 fb->local_abspath,
953 fb->edit_conflict, NULL,
954 scratch_pool));
955
956 do_notification(fb->edit_baton, fb->local_abspath, svn_node_file,
957 svn_wc_notify_tree_conflict, scratch_pool);
958 fb->already_notified = TRUE;
959 }
960
961 return SVN_NO_ERROR;
962 }
963
964
965 /* Handle the next delta window of the file described by BATON. If it is
966 * the end (WINDOW == NULL), then check the checksum, store the text in the
967 * pristine store and write its details into BATON->fb->new_text_base_*. */
968 static svn_error_t *
window_handler(svn_txdelta_window_t * window,void * baton)969 window_handler(svn_txdelta_window_t *window, void *baton)
970 {
971 struct handler_baton *hb = baton;
972 struct file_baton *fb = hb->fb;
973 svn_error_t *err;
974
975 /* Apply this window. We may be done at that point. */
976 err = hb->apply_handler(window, hb->apply_baton);
977 if (window != NULL && !err)
978 return SVN_NO_ERROR;
979
980 if (hb->expected_source_checksum)
981 {
982 /* Close the stream to calculate HB->actual_source_md5_checksum. */
983 svn_error_t *err2 = svn_stream_close(hb->source_checksum_stream);
984
985 if (!err2)
986 {
987 SVN_ERR_ASSERT(hb->expected_source_checksum->kind ==
988 hb->actual_source_checksum->kind);
989
990 if (!svn_checksum_match(hb->expected_source_checksum,
991 hb->actual_source_checksum))
992 {
993 err = svn_error_createf(SVN_ERR_WC_CORRUPT_TEXT_BASE, err,
994 _("Checksum mismatch while updating '%s':\n"
995 " expected: %s\n"
996 " actual: %s\n"),
997 svn_dirent_local_style(fb->local_abspath, hb->pool),
998 svn_checksum_to_cstring(hb->expected_source_checksum,
999 hb->pool),
1000 svn_checksum_to_cstring(hb->actual_source_checksum,
1001 hb->pool));
1002 }
1003 }
1004
1005 err = svn_error_compose_create(err, err2);
1006 }
1007
1008 if (err)
1009 {
1010 /* We failed to apply the delta; clean up the temporary file if it
1011 already created by lazy_open_target(). */
1012 if (hb->install_data)
1013 {
1014 svn_error_clear(svn_wc__db_pristine_install_abort(hb->install_data,
1015 hb->pool));
1016 }
1017 }
1018 else
1019 {
1020 /* Tell the file baton about the new text base's checksums. */
1021 fb->new_text_base_md5_checksum =
1022 svn_checksum__from_digest_md5(hb->new_text_base_md5_digest, fb->pool);
1023 fb->new_text_base_sha1_checksum =
1024 svn_checksum_dup(hb->new_text_base_sha1_checksum, fb->pool);
1025
1026 /* Store the new pristine text in the pristine store now. Later, in a
1027 single transaction we will update the BASE_NODE to include a
1028 reference to this pristine text's checksum. */
1029 SVN_ERR(svn_wc__db_pristine_install(hb->install_data,
1030 fb->new_text_base_sha1_checksum,
1031 fb->new_text_base_md5_checksum,
1032 hb->pool));
1033 }
1034
1035 svn_pool_destroy(hb->pool);
1036
1037 return err;
1038 }
1039
1040
1041 /* Find the last-change info within ENTRY_PROPS, and return then in the
1042 CHANGED_* parameters. Each parameter will be initialized to its "none"
1043 value, and will contain the relavent info if found.
1044
1045 CHANGED_AUTHOR will be allocated in RESULT_POOL. SCRATCH_POOL will be
1046 used for some temporary allocations.
1047 */
1048 static svn_error_t *
accumulate_last_change(svn_revnum_t * changed_rev,apr_time_t * changed_date,const char ** changed_author,const apr_array_header_t * entry_props,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1049 accumulate_last_change(svn_revnum_t *changed_rev,
1050 apr_time_t *changed_date,
1051 const char **changed_author,
1052 const apr_array_header_t *entry_props,
1053 apr_pool_t *result_pool,
1054 apr_pool_t *scratch_pool)
1055 {
1056 int i;
1057
1058 *changed_rev = SVN_INVALID_REVNUM;
1059 *changed_date = 0;
1060 *changed_author = NULL;
1061
1062 for (i = 0; i < entry_props->nelts; ++i)
1063 {
1064 const svn_prop_t *prop = &APR_ARRAY_IDX(entry_props, i, svn_prop_t);
1065
1066 /* A prop value of NULL means the information was not
1067 available. We don't remove this field from the entries
1068 file; we have convention just leave it empty. So let's
1069 just skip those entry props that have no values. */
1070 if (! prop->value)
1071 continue;
1072
1073 if (! strcmp(prop->name, SVN_PROP_ENTRY_LAST_AUTHOR))
1074 *changed_author = apr_pstrdup(result_pool, prop->value->data);
1075 else if (! strcmp(prop->name, SVN_PROP_ENTRY_COMMITTED_REV))
1076 {
1077 apr_int64_t rev;
1078 SVN_ERR(svn_cstring_atoi64(&rev, prop->value->data));
1079 *changed_rev = (svn_revnum_t)rev;
1080 }
1081 else if (! strcmp(prop->name, SVN_PROP_ENTRY_COMMITTED_DATE))
1082 SVN_ERR(svn_time_from_cstring(changed_date, prop->value->data,
1083 scratch_pool));
1084
1085 /* Starting with Subversion 1.7 we ignore the SVN_PROP_ENTRY_UUID
1086 property here. */
1087 }
1088
1089 return SVN_NO_ERROR;
1090 }
1091
1092
1093 /* Join ADD_PATH to BASE_PATH. If ADD_PATH is absolute, or if any ".."
1094 * component of it resolves to a path above BASE_PATH, then return
1095 * SVN_ERR_WC_OBSTRUCTED_UPDATE.
1096 *
1097 * This is to prevent the situation where the repository contains,
1098 * say, "..\nastyfile". Although that's perfectly legal on some
1099 * systems, when checked out onto Win32 it would cause "nastyfile" to
1100 * be created in the parent of the current edit directory.
1101 *
1102 * (http://cve.mitre.org/cgi-bin/cvename.cgi?name=2007-3846)
1103 */
1104 static svn_error_t *
path_join_under_root(const char ** result_path,const char * base_path,const char * add_path,apr_pool_t * pool)1105 path_join_under_root(const char **result_path,
1106 const char *base_path,
1107 const char *add_path,
1108 apr_pool_t *pool)
1109 {
1110 svn_boolean_t under_root;
1111
1112 SVN_ERR(svn_dirent_is_under_root(&under_root,
1113 result_path, base_path, add_path, pool));
1114
1115 if (! under_root)
1116 {
1117 return svn_error_createf(
1118 SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
1119 _("Path '%s' is not in the working copy"),
1120 svn_dirent_local_style(svn_dirent_join(base_path, add_path, pool),
1121 pool));
1122 }
1123
1124 /* This catches issue #3288 */
1125 if (strcmp(add_path, svn_dirent_basename(*result_path, NULL)) != 0)
1126 {
1127 return svn_error_createf(
1128 SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
1129 _("'%s' is not valid as filename in directory '%s'"),
1130 svn_dirent_local_style(add_path, pool),
1131 svn_dirent_local_style(base_path, pool));
1132 }
1133
1134 return SVN_NO_ERROR;
1135 }
1136
1137
1138 /*** The callbacks we'll plug into an svn_delta_editor_t structure. ***/
1139
1140 /* An svn_delta_editor_t function. */
1141 static svn_error_t *
set_target_revision(void * edit_baton,svn_revnum_t target_revision,apr_pool_t * pool)1142 set_target_revision(void *edit_baton,
1143 svn_revnum_t target_revision,
1144 apr_pool_t *pool)
1145 {
1146 struct edit_baton *eb = edit_baton;
1147
1148 *(eb->target_revision) = target_revision;
1149 return SVN_NO_ERROR;
1150 }
1151
1152 /* An svn_delta_editor_t function. */
1153 static svn_error_t *
open_root(void * edit_baton,svn_revnum_t base_revision,apr_pool_t * pool,void ** dir_baton)1154 open_root(void *edit_baton,
1155 svn_revnum_t base_revision, /* This is ignored in co */
1156 apr_pool_t *pool,
1157 void **dir_baton)
1158 {
1159 struct edit_baton *eb = edit_baton;
1160 struct dir_baton *db;
1161 svn_boolean_t already_conflicted, conflict_ignored;
1162 svn_error_t *err;
1163 svn_wc__db_status_t status;
1164 svn_wc__db_status_t base_status;
1165 svn_node_kind_t kind;
1166 svn_boolean_t have_work;
1167
1168 /* Note that something interesting is actually happening in this
1169 edit run. */
1170 eb->root_opened = TRUE;
1171
1172 SVN_ERR(make_dir_baton(&db, NULL, eb, NULL, FALSE, pool));
1173 *dir_baton = db;
1174
1175 err = already_in_a_tree_conflict(&already_conflicted, &conflict_ignored,
1176 eb->db, db->local_abspath, pool);
1177
1178 if (err)
1179 {
1180 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
1181 return svn_error_trace(err);
1182
1183 svn_error_clear(err);
1184 already_conflicted = conflict_ignored = FALSE;
1185 }
1186 else if (already_conflicted)
1187 {
1188 /* Record a skip of both the anchor and target in the skipped tree
1189 as the anchor itself might not be updated */
1190 SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
1191 SVN_ERR(remember_skipped_tree(eb, eb->target_abspath, pool));
1192
1193 db->skip_this = TRUE;
1194 db->already_notified = TRUE;
1195
1196 /* Notify that we skipped the target, while we actually skipped
1197 the anchor */
1198 do_notification(eb, eb->target_abspath, svn_node_unknown,
1199 svn_wc_notify_skip_conflicted, pool);
1200
1201 return SVN_NO_ERROR;
1202 }
1203
1204
1205 SVN_ERR(svn_wc__db_read_info(&status, &kind, &db->old_revision,
1206 &db->old_repos_relpath, NULL, NULL,
1207 &db->changed_rev, &db->changed_date,
1208 &db->changed_author, &db->ambient_depth,
1209 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1210 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1211 NULL, NULL, &have_work,
1212 eb->db, db->local_abspath,
1213 db->pool, pool));
1214
1215 if (have_work)
1216 {
1217 SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL,
1218 &db->old_revision,
1219 &db->old_repos_relpath, NULL, NULL,
1220 &db->changed_rev, &db->changed_date,
1221 &db->changed_author,
1222 &db->ambient_depth,
1223 NULL, NULL, NULL, NULL, NULL, NULL,
1224 eb->db, db->local_abspath,
1225 db->pool, pool));
1226 }
1227 else
1228 base_status = status;
1229
1230 SVN_ERR(calculate_repos_relpath(&db->new_repos_relpath, db->local_abspath,
1231 db->old_repos_relpath, eb, NULL,
1232 db->pool, pool));
1233
1234 if (conflict_ignored)
1235 db->shadowed = TRUE;
1236 else if (have_work)
1237 {
1238 const char *move_dst_op_root_abspath;
1239 const char *move_src_root_abspath;
1240
1241 SVN_ERR(svn_wc__db_base_moved_to(NULL, &move_dst_op_root_abspath,
1242 &move_src_root_abspath,
1243 NULL, eb->db, db->local_abspath,
1244 pool, pool));
1245
1246 if (move_src_root_abspath)
1247 {
1248 /* This is an update anchored inside a move. We need to
1249 raise a move-edit tree-conflict on the move root to
1250 update the move destination. */
1251 svn_skel_t *tree_conflict = svn_wc__conflict_skel_create(pool);
1252
1253 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
1254 tree_conflict, eb->db, move_src_root_abspath,
1255 svn_wc_conflict_reason_moved_away,
1256 svn_wc_conflict_action_edit,
1257 move_src_root_abspath,
1258 move_dst_op_root_abspath, pool, pool));
1259
1260 if (strcmp(db->local_abspath, move_src_root_abspath))
1261 {
1262 /* We are raising the tree-conflict on some parent of
1263 the edit root, we won't be handling that path again
1264 so raise the conflict now. */
1265 SVN_ERR(complete_conflict(tree_conflict, eb,
1266 move_src_root_abspath,
1267 db->old_repos_relpath,
1268 db->old_revision,
1269 db->new_repos_relpath,
1270 svn_node_dir, svn_node_dir,
1271 NULL, pool, pool));
1272 SVN_ERR(svn_wc__db_op_mark_conflict(eb->db,
1273 move_src_root_abspath,
1274 tree_conflict,
1275 NULL, pool));
1276 do_notification(eb, move_src_root_abspath, svn_node_dir,
1277 svn_wc_notify_tree_conflict, pool);
1278 }
1279 else
1280 db->edit_conflict = tree_conflict;
1281 }
1282
1283 db->shadowed = TRUE; /* Needed for the close_directory() on the root, to
1284 make sure it doesn't use the ACTUAL tree */
1285 }
1286
1287 if (*eb->target_basename == '\0')
1288 {
1289 /* For an update with a NULL target, this is equivalent to open_dir(): */
1290
1291 db->was_incomplete = (base_status == svn_wc__db_status_incomplete);
1292
1293 /* ### TODO: Add some tree conflict and obstruction detection, etc. like
1294 open_directory() does.
1295 (or find a way to reuse that code here)
1296
1297 ### BH 2013: I don't think we need all of the detection here, as the
1298 user explicitly asked to update this node. So we don't
1299 have to tell that it is a local replacement/delete.
1300 */
1301
1302 SVN_ERR(svn_wc__db_temp_op_start_directory_update(eb->db,
1303 db->local_abspath,
1304 db->new_repos_relpath,
1305 *eb->target_revision,
1306 pool));
1307 }
1308
1309 return SVN_NO_ERROR;
1310 }
1311
1312
1313 /* ===================================================================== */
1314 /* Checking for local modifications. */
1315
1316 /* Indicates an unset svn_wc_conflict_reason_t. */
1317 #define SVN_WC_CONFLICT_REASON_NONE (svn_wc_conflict_reason_t)(-1)
1318
1319 /* Check whether the incoming change ACTION on FULL_PATH would conflict with
1320 * LOCAL_ABSPATH's scheduled change. If so, then raise a tree conflict with
1321 * LOCAL_ABSPATH as the victim.
1322 *
1323 * The edit baton EB gives information including whether the operation is
1324 * an update or a switch.
1325 *
1326 * WORKING_STATUS is the current node status of LOCAL_ABSPATH
1327 * and EXISTS_IN_REPOS specifies whether a BASE_NODE representation for exists
1328 * for this node. In that case the on disk type is compared to EXPECTED_KIND.
1329 *
1330 * If a tree conflict reason was found for the incoming action, the resulting
1331 * tree conflict info is returned in *PCONFLICT. PCONFLICT must be non-NULL,
1332 * while *PCONFLICT is always overwritten.
1333 *
1334 * The tree conflict is allocated in RESULT_POOL. Temporary allocations use
1335 * SCRATCH_POOL.
1336 */
1337 static svn_error_t *
check_tree_conflict(svn_skel_t ** pconflict,struct edit_baton * eb,const char * local_abspath,svn_wc__db_status_t working_status,svn_boolean_t exists_in_repos,svn_node_kind_t expected_kind,svn_wc_conflict_action_t action,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1338 check_tree_conflict(svn_skel_t **pconflict,
1339 struct edit_baton *eb,
1340 const char *local_abspath,
1341 svn_wc__db_status_t working_status,
1342 svn_boolean_t exists_in_repos,
1343 svn_node_kind_t expected_kind,
1344 svn_wc_conflict_action_t action,
1345 apr_pool_t *result_pool,
1346 apr_pool_t *scratch_pool)
1347 {
1348 svn_wc_conflict_reason_t reason = SVN_WC_CONFLICT_REASON_NONE;
1349 svn_boolean_t modified = FALSE;
1350 const char *move_src_op_root_abspath = NULL;
1351 const char *move_dst_op_root_abspath = NULL;
1352
1353 *pconflict = NULL;
1354
1355 /* Find out if there are any local changes to this node that may
1356 * be the "reason" of a tree-conflict with the incoming "action". */
1357 switch (working_status)
1358 {
1359 case svn_wc__db_status_added:
1360 case svn_wc__db_status_moved_here:
1361 case svn_wc__db_status_copied:
1362 if (!exists_in_repos)
1363 {
1364 /* The node is locally added, and it did not exist before. This
1365 * is an 'update', so the local add can only conflict with an
1366 * incoming 'add'. In fact, if we receive anything else than an
1367 * svn_wc_conflict_action_add (which includes 'added',
1368 * 'copied-here' and 'moved-here') during update on a node that
1369 * did not exist before, then something is very wrong.
1370 * Note that if there was no action on the node, this code
1371 * would not have been called in the first place. */
1372 SVN_ERR_ASSERT(action == svn_wc_conflict_action_add);
1373
1374 /* Scan the addition in case our caller didn't. */
1375 if (working_status == svn_wc__db_status_added)
1376 SVN_ERR(svn_wc__db_scan_addition(&working_status, NULL, NULL,
1377 NULL, NULL, NULL, NULL,
1378 NULL, NULL,
1379 eb->db, local_abspath,
1380 scratch_pool, scratch_pool));
1381
1382 if (working_status == svn_wc__db_status_moved_here)
1383 reason = svn_wc_conflict_reason_moved_here;
1384 else
1385 reason = svn_wc_conflict_reason_added;
1386 }
1387 else
1388 {
1389 /* The node is locally replaced but could also be moved-away,
1390 but we can't report that it is moved away and replaced.
1391
1392 And we wouldn't be able to store that each of a dozen
1393 descendants was moved to other locations...
1394
1395 Replaced is what actually happened... */
1396
1397 reason = svn_wc_conflict_reason_replaced;
1398 }
1399 break;
1400
1401
1402 case svn_wc__db_status_deleted:
1403 {
1404 SVN_ERR(svn_wc__db_base_moved_to(NULL, &move_dst_op_root_abspath,
1405 NULL, &move_src_op_root_abspath,
1406 eb->db, local_abspath,
1407 scratch_pool, scratch_pool));
1408 if (move_src_op_root_abspath)
1409 reason = svn_wc_conflict_reason_moved_away;
1410 else
1411 reason = svn_wc_conflict_reason_deleted;
1412 }
1413 break;
1414
1415 case svn_wc__db_status_incomplete:
1416 /* We used svn_wc__db_read_info(), so 'incomplete' means
1417 * - there is no node in the WORKING tree
1418 * - a BASE node is known to exist
1419 * So the node exists and is essentially 'normal'. We still need to
1420 * check prop and text mods, and those checks will retrieve the
1421 * missing information (hopefully). */
1422 case svn_wc__db_status_normal:
1423 if (action == svn_wc_conflict_action_edit)
1424 {
1425 /* An edit onto a local edit or onto *no* local changes is no
1426 * tree-conflict. (It's possibly a text- or prop-conflict,
1427 * but we don't handle those here.)
1428 *
1429 * Except when there is a local obstruction
1430 */
1431 if (exists_in_repos)
1432 {
1433 svn_node_kind_t disk_kind;
1434
1435 SVN_ERR(svn_io_check_path(local_abspath, &disk_kind,
1436 scratch_pool));
1437
1438 if (disk_kind != expected_kind && disk_kind != svn_node_none)
1439 {
1440 reason = svn_wc_conflict_reason_obstructed;
1441 break;
1442 }
1443
1444 }
1445 return SVN_NO_ERROR;
1446 }
1447
1448 /* Replace is handled as delete and then specifically in
1449 add_directory() and add_file(), so we only expect deletes here */
1450 SVN_ERR_ASSERT(action == svn_wc_conflict_action_delete);
1451
1452 /* Check if the update wants to delete or replace a locally
1453 * modified node. */
1454
1455
1456 /* Do a deep tree detection of local changes. The update editor will
1457 * not visit the subdirectories of a directory that it wants to delete.
1458 * Therefore, we need to start a separate crawl here. */
1459
1460 SVN_ERR(svn_wc__node_has_local_mods(&modified, NULL,
1461 eb->db, local_abspath, TRUE,
1462 eb->cancel_func, eb->cancel_baton,
1463 scratch_pool));
1464
1465 if (modified)
1466 {
1467 if (working_status == svn_wc__db_status_deleted)
1468 reason = svn_wc_conflict_reason_deleted;
1469 else
1470 reason = svn_wc_conflict_reason_edited;
1471 }
1472 break;
1473
1474 case svn_wc__db_status_server_excluded:
1475 /* Not allowed to view the node. Not allowed to report tree
1476 * conflicts. */
1477 case svn_wc__db_status_excluded:
1478 /* Locally marked as excluded. No conflicts wanted. */
1479 case svn_wc__db_status_not_present:
1480 /* A committed delete (but parent not updated). The delete is
1481 committed, so no conflict possible during update. */
1482 return SVN_NO_ERROR;
1483
1484 case svn_wc__db_status_base_deleted:
1485 /* An internal status. Should never show up here. */
1486 SVN_ERR_MALFUNCTION();
1487 break;
1488
1489 }
1490
1491 if (reason == SVN_WC_CONFLICT_REASON_NONE)
1492 /* No conflict with the current action. */
1493 return SVN_NO_ERROR;
1494
1495
1496 /* Sanity checks. Note that if there was no action on the node, this function
1497 * would not have been called in the first place.*/
1498 if (reason == svn_wc_conflict_reason_edited
1499 || reason == svn_wc_conflict_reason_obstructed
1500 || reason == svn_wc_conflict_reason_deleted
1501 || reason == svn_wc_conflict_reason_moved_away
1502 || reason == svn_wc_conflict_reason_replaced)
1503 {
1504 /* When the node existed before (it was locally deleted, replaced or
1505 * edited), then 'update' cannot add it "again". So it can only send
1506 * _action_edit, _delete or _replace. */
1507 if (action != svn_wc_conflict_action_edit
1508 && action != svn_wc_conflict_action_delete
1509 && action != svn_wc_conflict_action_replace)
1510 return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
1511 _("Unexpected attempt to add a node at path '%s'"),
1512 svn_dirent_local_style(local_abspath, scratch_pool));
1513 }
1514 else if (reason == svn_wc_conflict_reason_added ||
1515 reason == svn_wc_conflict_reason_moved_here)
1516 {
1517 /* When the node did not exist before (it was locally added),
1518 * then 'update' cannot want to modify it in any way.
1519 * It can only send _action_add. */
1520 if (action != svn_wc_conflict_action_add)
1521 return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
1522 _("Unexpected attempt to edit, delete, or replace "
1523 "a node at path '%s'"),
1524 svn_dirent_local_style(local_abspath, scratch_pool));
1525
1526 }
1527
1528
1529 /* A conflict was detected. Create a conflict skel to record it. */
1530 *pconflict = svn_wc__conflict_skel_create(result_pool);
1531
1532 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(*pconflict,
1533 eb->db, local_abspath,
1534 reason,
1535 action,
1536 move_src_op_root_abspath,
1537 move_dst_op_root_abspath,
1538 result_pool, scratch_pool));
1539
1540 return SVN_NO_ERROR;
1541 }
1542
1543
1544 /* If LOCAL_ABSPATH is inside a conflicted tree and the conflict is
1545 * not a moved-away-edit conflict, set *CONFLICTED to TRUE. Otherwise
1546 * set *CONFLICTED to FALSE.
1547 */
1548 static svn_error_t *
already_in_a_tree_conflict(svn_boolean_t * conflicted,svn_boolean_t * ignored,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * scratch_pool)1549 already_in_a_tree_conflict(svn_boolean_t *conflicted,
1550 svn_boolean_t *ignored,
1551 svn_wc__db_t *db,
1552 const char *local_abspath,
1553 apr_pool_t *scratch_pool)
1554 {
1555 const char *ancestor_abspath = local_abspath;
1556 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1557
1558 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1559
1560 *conflicted = *ignored = FALSE;
1561
1562 while (TRUE)
1563 {
1564 svn_boolean_t is_wc_root;
1565
1566 svn_pool_clear(iterpool);
1567
1568 SVN_ERR(svn_wc__conflicted_for_update_p(conflicted, ignored, db,
1569 ancestor_abspath, TRUE,
1570 scratch_pool));
1571 if (*conflicted || *ignored)
1572 break;
1573
1574 SVN_ERR(svn_wc__db_is_wcroot(&is_wc_root, db, ancestor_abspath,
1575 iterpool));
1576 if (is_wc_root)
1577 break;
1578
1579 ancestor_abspath = svn_dirent_dirname(ancestor_abspath, scratch_pool);
1580 }
1581
1582 svn_pool_destroy(iterpool);
1583
1584 return SVN_NO_ERROR;
1585 }
1586
1587 /* Temporary helper until the new conflict handling is in place */
1588 static svn_error_t *
node_already_conflicted(svn_boolean_t * conflicted,svn_boolean_t * conflict_ignored,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * scratch_pool)1589 node_already_conflicted(svn_boolean_t *conflicted,
1590 svn_boolean_t *conflict_ignored,
1591 svn_wc__db_t *db,
1592 const char *local_abspath,
1593 apr_pool_t *scratch_pool)
1594 {
1595 SVN_ERR(svn_wc__conflicted_for_update_p(conflicted, conflict_ignored, db,
1596 local_abspath, FALSE,
1597 scratch_pool));
1598
1599 return SVN_NO_ERROR;
1600 }
1601
1602
1603 /* An svn_delta_editor_t function. */
1604 static svn_error_t *
delete_entry(const char * path,svn_revnum_t revision,void * parent_baton,apr_pool_t * pool)1605 delete_entry(const char *path,
1606 svn_revnum_t revision,
1607 void *parent_baton,
1608 apr_pool_t *pool)
1609 {
1610 struct dir_baton *pb = parent_baton;
1611 struct edit_baton *eb = pb->edit_baton;
1612 const char *base = svn_relpath_basename(path, NULL);
1613 const char *local_abspath;
1614 const char *repos_relpath;
1615 const char *deleted_repos_relpath;
1616 svn_node_kind_t kind;
1617 svn_revnum_t old_revision;
1618 svn_boolean_t conflicted;
1619 svn_boolean_t have_work;
1620 svn_skel_t *tree_conflict = NULL;
1621 svn_wc__db_status_t status;
1622 svn_wc__db_status_t base_status;
1623 apr_pool_t *scratch_pool;
1624 svn_boolean_t deleting_target;
1625 svn_boolean_t deleting_switched;
1626
1627 if (pb->skip_this)
1628 return SVN_NO_ERROR;
1629
1630 scratch_pool = svn_pool_create(pb->pool);
1631
1632 SVN_ERR(mark_directory_edited(pb, scratch_pool));
1633
1634 SVN_ERR(path_join_under_root(&local_abspath, pb->local_abspath, base,
1635 scratch_pool));
1636
1637 deleting_target = (strcmp(local_abspath, eb->target_abspath) == 0);
1638
1639 /* Detect obstructing working copies */
1640 {
1641 svn_boolean_t is_root;
1642
1643
1644 SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, local_abspath,
1645 scratch_pool));
1646
1647 if (is_root)
1648 {
1649 /* Just skip this node; a future update will handle it */
1650 SVN_ERR(remember_skipped_tree(eb, local_abspath, pool));
1651 do_notification(eb, local_abspath, svn_node_unknown,
1652 svn_wc_notify_update_skip_obstruction, scratch_pool);
1653
1654 svn_pool_destroy(scratch_pool);
1655
1656 return SVN_NO_ERROR;
1657 }
1658 }
1659
1660 SVN_ERR(svn_wc__db_read_info(&status, &kind, &old_revision, &repos_relpath,
1661 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1662 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1663 &conflicted, NULL, NULL, NULL,
1664 NULL, NULL, &have_work,
1665 eb->db, local_abspath,
1666 scratch_pool, scratch_pool));
1667
1668 if (!have_work)
1669 {
1670 base_status = status;
1671 }
1672 else
1673 SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &old_revision,
1674 &repos_relpath,
1675 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1676 NULL, NULL, NULL, NULL, NULL,
1677 eb->db, local_abspath,
1678 scratch_pool, scratch_pool));
1679
1680 if (pb->old_repos_relpath && repos_relpath)
1681 {
1682 const char *expected_name;
1683
1684 expected_name = svn_relpath_skip_ancestor(pb->old_repos_relpath,
1685 repos_relpath);
1686
1687 deleting_switched = (!expected_name || strcmp(expected_name, base) != 0);
1688 }
1689 else
1690 deleting_switched = FALSE;
1691
1692 /* Is this path a conflict victim? */
1693 if (pb->shadowed)
1694 conflicted = FALSE; /* Conflict applies to WORKING */
1695 else if (conflicted)
1696 SVN_ERR(node_already_conflicted(&conflicted, NULL,
1697 eb->db, local_abspath, scratch_pool));
1698 if (conflicted)
1699 {
1700 SVN_ERR(remember_skipped_tree(eb, local_abspath, scratch_pool));
1701
1702 do_notification(eb, local_abspath, svn_node_unknown,
1703 svn_wc_notify_skip_conflicted,
1704 scratch_pool);
1705
1706 svn_pool_destroy(scratch_pool);
1707
1708 return SVN_NO_ERROR;
1709 }
1710
1711
1712 /* Receive the remote removal of excluded/server-excluded/not present node.
1713 Do not notify, but perform the change even when the node is shadowed */
1714 if (base_status == svn_wc__db_status_not_present
1715 || base_status == svn_wc__db_status_excluded
1716 || base_status == svn_wc__db_status_server_excluded)
1717 {
1718 SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath, TRUE,
1719 deleting_target, FALSE,
1720 *eb->target_revision,
1721 NULL, NULL,
1722 scratch_pool));
1723
1724 if (deleting_target)
1725 eb->target_deleted = TRUE;
1726
1727 svn_pool_destroy(scratch_pool);
1728
1729 return SVN_NO_ERROR;
1730 }
1731
1732 /* Is this path the victim of a newly-discovered tree conflict? If so,
1733 * remember it and notify the client. Then (if it was existing and
1734 * modified), re-schedule the node to be added back again, as a (modified)
1735 * copy of the previous base version. */
1736
1737 /* Check for conflicts only when we haven't already recorded
1738 * a tree-conflict on a parent node. */
1739 if (!pb->shadowed && !pb->edit_obstructed)
1740 {
1741 SVN_ERR(check_tree_conflict(&tree_conflict, eb, local_abspath,
1742 status, TRUE,
1743 kind,
1744 svn_wc_conflict_action_delete,
1745 pb->pool, scratch_pool));
1746 }
1747
1748 if (tree_conflict != NULL)
1749 {
1750 /* When we raise a tree conflict on a node, we don't want to mark the
1751 * node as skipped, to allow a replacement to continue doing at least
1752 * a bit of its work (possibly adding a not present node, for the
1753 * next update) */
1754 if (!pb->deletion_conflicts)
1755 pb->deletion_conflicts = apr_hash_make(pb->pool);
1756
1757 svn_hash_sets(pb->deletion_conflicts, apr_pstrdup(pb->pool, base),
1758 tree_conflict);
1759
1760 /* Whatever the kind of conflict, we can just clear BASE
1761 by turning whatever is there into a copy */
1762 }
1763
1764 /* Calculate the repository-relative path of the entry which was
1765 * deleted. For updates it's the same as REPOS_RELPATH but for
1766 * switches it is within the switch target. */
1767 SVN_ERR(calculate_repos_relpath(&deleted_repos_relpath, local_abspath,
1768 repos_relpath, eb, pb, scratch_pool,
1769 scratch_pool));
1770 SVN_ERR(complete_conflict(tree_conflict, eb, local_abspath, repos_relpath,
1771 old_revision, deleted_repos_relpath,
1772 kind, svn_node_none, NULL,
1773 pb->pool, scratch_pool));
1774
1775 /* Issue a wq operation to delete the BASE_NODE data and to delete actual
1776 nodes based on that from disk, but leave any WORKING_NODEs on disk.
1777
1778 Local modifications are already turned into copies at this point.
1779
1780 If the thing being deleted is the *target* of this update, then
1781 we need to recreate a 'deleted' entry, so that the parent can give
1782 accurate reports about itself in the future. */
1783 if (! deleting_target && ! deleting_switched)
1784 {
1785 /* Delete, and do not leave a not-present node. */
1786 SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath,
1787 (tree_conflict != NULL),
1788 FALSE, FALSE,
1789 SVN_INVALID_REVNUM /* not_present_rev */,
1790 tree_conflict, NULL,
1791 scratch_pool));
1792 }
1793 else
1794 {
1795 /* Delete, leaving a not-present node. */
1796 SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath,
1797 (tree_conflict != NULL),
1798 TRUE, FALSE,
1799 *eb->target_revision,
1800 tree_conflict, NULL,
1801 scratch_pool));
1802 if (deleting_target)
1803 eb->target_deleted = TRUE;
1804 else
1805 {
1806 /* Don't remove the not-present marker at the final bump */
1807 SVN_ERR(remember_skipped_tree(eb, local_abspath, pool));
1808 }
1809 }
1810
1811 SVN_ERR(svn_wc__wq_run(eb->db, pb->local_abspath,
1812 eb->cancel_func, eb->cancel_baton,
1813 scratch_pool));
1814
1815 /* Notify. */
1816 if (tree_conflict)
1817 {
1818 if (eb->conflict_func)
1819 SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, local_abspath,
1820 kind,
1821 tree_conflict,
1822 NULL /* merge_options */,
1823 eb->conflict_func,
1824 eb->conflict_baton,
1825 eb->cancel_func,
1826 eb->cancel_baton,
1827 scratch_pool));
1828 do_notification(eb, local_abspath, kind,
1829 svn_wc_notify_tree_conflict, scratch_pool);
1830 }
1831 else
1832 {
1833 svn_wc_notify_action_t action = svn_wc_notify_update_delete;
1834
1835 if (pb->shadowed || pb->edit_obstructed)
1836 action = svn_wc_notify_update_shadowed_delete;
1837
1838 do_notification(eb, local_abspath, kind, action, scratch_pool);
1839 }
1840
1841 svn_pool_destroy(scratch_pool);
1842
1843 return SVN_NO_ERROR;
1844 }
1845
1846 /* An svn_delta_editor_t function. */
1847 static svn_error_t *
add_directory(const char * path,void * parent_baton,const char * copyfrom_path,svn_revnum_t copyfrom_rev,apr_pool_t * pool,void ** child_baton)1848 add_directory(const char *path,
1849 void *parent_baton,
1850 const char *copyfrom_path,
1851 svn_revnum_t copyfrom_rev,
1852 apr_pool_t *pool,
1853 void **child_baton)
1854 {
1855 struct dir_baton *pb = parent_baton;
1856 struct edit_baton *eb = pb->edit_baton;
1857 struct dir_baton *db;
1858 apr_pool_t *scratch_pool = svn_pool_create(pool);
1859 svn_node_kind_t kind;
1860 svn_wc__db_status_t status;
1861 svn_node_kind_t wc_kind;
1862 svn_boolean_t conflicted;
1863 svn_boolean_t conflict_ignored = FALSE;
1864 svn_boolean_t versioned_locally_and_present;
1865 svn_skel_t *tree_conflict = NULL;
1866 svn_error_t *err;
1867
1868 SVN_ERR_ASSERT(! (copyfrom_path || SVN_IS_VALID_REVNUM(copyfrom_rev)));
1869
1870 SVN_ERR(make_dir_baton(&db, path, eb, pb, TRUE, pool));
1871 *child_baton = db;
1872
1873 if (db->skip_this)
1874 return SVN_NO_ERROR;
1875
1876 SVN_ERR(calculate_repos_relpath(&db->new_repos_relpath, db->local_abspath,
1877 NULL, eb, pb, db->pool, scratch_pool));
1878
1879 SVN_ERR(mark_directory_edited(db, pool));
1880
1881 if (strcmp(eb->target_abspath, db->local_abspath) == 0)
1882 {
1883 /* The target of the edit is being added, give it the requested
1884 depth of the edit (but convert svn_depth_unknown to
1885 svn_depth_infinity). */
1886 db->ambient_depth = (eb->requested_depth == svn_depth_unknown)
1887 ? svn_depth_infinity : eb->requested_depth;
1888 }
1889 else if (eb->requested_depth == svn_depth_immediates
1890 || (eb->requested_depth == svn_depth_unknown
1891 && pb->ambient_depth == svn_depth_immediates))
1892 {
1893 db->ambient_depth = svn_depth_empty;
1894 }
1895 else
1896 {
1897 db->ambient_depth = svn_depth_infinity;
1898 }
1899
1900 /* It may not be named the same as the administrative directory. */
1901 if (svn_wc_is_adm_dir(db->name, pool))
1902 return svn_error_createf(
1903 SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
1904 _("Failed to add directory '%s': object of the same name as the "
1905 "administrative directory"),
1906 svn_dirent_local_style(db->local_abspath, pool));
1907
1908 if (!eb->clean_checkout)
1909 {
1910 SVN_ERR(svn_io_check_path(db->local_abspath, &kind, db->pool));
1911
1912 err = svn_wc__db_read_info(&status, &wc_kind, NULL, NULL, NULL, NULL, NULL,
1913 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1914 NULL, NULL, NULL, NULL, NULL,
1915 &conflicted, NULL, NULL, NULL, NULL, NULL, NULL,
1916 eb->db, db->local_abspath,
1917 scratch_pool, scratch_pool);
1918 }
1919 else
1920 {
1921 kind = svn_node_none;
1922 status = svn_wc__db_status_not_present;
1923 wc_kind = svn_node_unknown;
1924 conflicted = FALSE;
1925 err = NULL;
1926 }
1927
1928 if (err)
1929 {
1930 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
1931 return svn_error_trace(err);
1932
1933 svn_error_clear(err);
1934 wc_kind = svn_node_unknown;
1935 status = svn_wc__db_status_normal;
1936 conflicted = FALSE;
1937
1938 versioned_locally_and_present = FALSE;
1939 }
1940 else if (status == svn_wc__db_status_normal && wc_kind == svn_node_unknown)
1941 {
1942 SVN_ERR_ASSERT(conflicted);
1943 versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */
1944 }
1945 else if (status == svn_wc__db_status_normal
1946 || status == svn_wc__db_status_incomplete)
1947 {
1948 svn_boolean_t root;
1949
1950 SVN_ERR(svn_wc__db_is_wcroot(&root, eb->db, db->local_abspath,
1951 scratch_pool));
1952
1953 if (root)
1954 {
1955 /* !! We found the root of a working copy obstructing the wc !!
1956
1957 If the directory would be part of our own working copy then
1958 we wouldn't have been called as an add_directory().
1959
1960 The only thing we can do is add a not-present node, to allow
1961 a future update to bring in the new files when the problem is
1962 resolved. Note that svn_wc__db_base_add_not_present_node()
1963 explicitly adds the node into the parent's node database. */
1964
1965 svn_hash_sets(pb->not_present_nodes,
1966 apr_pstrdup(pb->pool, db->name),
1967 svn_node_kind_to_word(svn_node_dir));
1968 }
1969 else if (wc_kind == svn_node_dir)
1970 {
1971 /* We have an editor violation. Github sometimes does this
1972 in its subversion compatibility code, when changing the
1973 depth of a working copy, or on updates from incomplete */
1974 }
1975 else
1976 {
1977 /* We found a file external occupating the place we need in BASE.
1978
1979 We can't add a not-present node in this case as that would overwrite
1980 the file external. Luckily the file external itself stops us from
1981 forgetting a child of this parent directory like an obstructing
1982 working copy would.
1983
1984 The reason we get here is that the adm crawler doesn't report
1985 file externals.
1986 */
1987 SVN_ERR_ASSERT(wc_kind == svn_node_file
1988 || wc_kind == svn_node_symlink);
1989 }
1990
1991 SVN_ERR(remember_skipped_tree(eb, db->local_abspath, scratch_pool));
1992 db->skip_this = TRUE;
1993 db->already_notified = TRUE;
1994
1995 do_notification(eb, db->local_abspath, wc_kind,
1996 svn_wc_notify_update_skip_obstruction, scratch_pool);
1997
1998 svn_pool_destroy(scratch_pool);
1999
2000 return SVN_NO_ERROR;
2001 }
2002 else
2003 versioned_locally_and_present = IS_NODE_PRESENT(status);
2004
2005 /* Is this path a conflict victim? */
2006 if (conflicted)
2007 {
2008 if (pb->deletion_conflicts)
2009 tree_conflict = svn_hash_gets(pb->deletion_conflicts, db->name);
2010
2011 if (tree_conflict)
2012 {
2013 svn_wc_conflict_reason_t reason;
2014 const char *move_src_op_root_abspath;
2015 const char *move_dst_op_root_abspath;
2016 /* So this deletion wasn't just a deletion, it is actually a
2017 replacement. Let's install a better tree conflict. */
2018
2019 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL,
2020 &move_src_op_root_abspath,
2021 &move_dst_op_root_abspath,
2022 eb->db,
2023 db->local_abspath,
2024 tree_conflict,
2025 db->pool, scratch_pool));
2026
2027 tree_conflict = svn_wc__conflict_skel_create(db->pool);
2028
2029 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
2030 tree_conflict,
2031 eb->db, db->local_abspath,
2032 reason, svn_wc_conflict_action_replace,
2033 move_src_op_root_abspath,
2034 move_dst_op_root_abspath,
2035 db->pool, scratch_pool));
2036
2037 /* And now stop checking for conflicts here and just perform
2038 a shadowed update */
2039 db->edit_conflict = tree_conflict; /* Cache for close_directory */
2040 tree_conflict = NULL; /* No direct notification */
2041 db->shadowed = TRUE; /* Just continue */
2042 conflicted = FALSE; /* No skip */
2043 }
2044 else
2045 SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
2046 eb->db, db->local_abspath,
2047 scratch_pool));
2048 }
2049
2050 /* Now the "usual" behaviour if already conflicted. Skip it. */
2051 if (conflicted)
2052 {
2053 /* Record this conflict so that its descendants are skipped silently. */
2054 SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
2055
2056 db->skip_this = TRUE;
2057 db->already_notified = TRUE;
2058
2059 /* We skip this node, but once the update completes the parent node will
2060 be updated to the new revision. So a future recursive update of the
2061 parent will not bring in this new node as the revision of the parent
2062 describes to the repository that all children are available.
2063
2064 To resolve this problem, we add a not-present node to allow bringing
2065 the node in once this conflict is resolved.
2066
2067 Note that we can safely assume that no present base node exists,
2068 because then we would not have received an add_directory.
2069 */
2070 svn_hash_sets(pb->not_present_nodes, apr_pstrdup(pb->pool, db->name),
2071 svn_node_kind_to_word(svn_node_dir));
2072
2073 do_notification(eb, db->local_abspath, svn_node_dir,
2074 svn_wc_notify_skip_conflicted, scratch_pool);
2075
2076 svn_pool_destroy(scratch_pool);
2077 return SVN_NO_ERROR;
2078 }
2079 else if (conflict_ignored)
2080 {
2081 db->shadowed = TRUE;
2082 }
2083
2084 if (db->shadowed)
2085 {
2086 /* Nothing to check; does not and will not exist in working copy */
2087 }
2088 else if (versioned_locally_and_present)
2089 {
2090 /* What to do with a versioned or schedule-add dir:
2091
2092 A dir already added without history is OK. Set add_existed
2093 so that user notification is delayed until after any prop
2094 conflicts have been found.
2095
2096 An existing versioned dir is an error. In the future we may
2097 relax this restriction and simply update such dirs.
2098
2099 A dir added with history is a tree conflict. */
2100
2101 svn_boolean_t local_is_non_dir;
2102 svn_wc__db_status_t add_status = svn_wc__db_status_normal;
2103
2104 /* Is the local add a copy? */
2105 if (status == svn_wc__db_status_added)
2106 SVN_ERR(svn_wc__db_scan_addition(&add_status, NULL, NULL, NULL, NULL,
2107 NULL, NULL, NULL, NULL,
2108 eb->db, db->local_abspath,
2109 scratch_pool, scratch_pool));
2110
2111
2112 /* Is there *something* that is not a dir? */
2113 local_is_non_dir = (wc_kind != svn_node_dir
2114 && status != svn_wc__db_status_deleted);
2115
2116 /* Do tree conflict checking if
2117 * - if there is a local copy.
2118 * - if this is a switch operation
2119 * - the node kinds mismatch
2120 *
2121 * During switch, local adds at the same path as incoming adds get
2122 * "lost" in that switching back to the original will no longer have the
2123 * local add. So switch always alerts the user with a tree conflict. */
2124 if (!eb->adds_as_modification
2125 || local_is_non_dir
2126 || add_status != svn_wc__db_status_added)
2127 {
2128 SVN_ERR(check_tree_conflict(&tree_conflict, eb,
2129 db->local_abspath,
2130 status, FALSE, svn_node_none,
2131 svn_wc_conflict_action_add,
2132 db->pool, scratch_pool));
2133 }
2134
2135 if (tree_conflict == NULL)
2136 db->add_existed = TRUE; /* Take over WORKING */
2137 else
2138 db->shadowed = TRUE; /* Only update BASE */
2139 }
2140 else if (kind != svn_node_none)
2141 {
2142 /* There's an unversioned node at this path. */
2143 db->obstruction_found = TRUE;
2144
2145 /* Unversioned, obstructing dirs are handled by prop merge/conflict,
2146 * if unversioned obstructions are allowed. */
2147 if (! (kind == svn_node_dir && eb->allow_unver_obstructions))
2148 {
2149 /* Bring in the node as deleted */ /* ### Obstructed Conflict */
2150 db->shadowed = TRUE;
2151
2152 /* Mark a conflict */
2153 tree_conflict = svn_wc__conflict_skel_create(db->pool);
2154
2155 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
2156 tree_conflict,
2157 eb->db, db->local_abspath,
2158 svn_wc_conflict_reason_unversioned,
2159 svn_wc_conflict_action_add,
2160 NULL, NULL, db->pool, scratch_pool));
2161 db->edit_conflict = tree_conflict;
2162 }
2163 }
2164
2165 if (tree_conflict)
2166 SVN_ERR(complete_conflict(tree_conflict, eb, db->local_abspath,
2167 db->old_repos_relpath, db->old_revision,
2168 db->new_repos_relpath,
2169 wc_kind, svn_node_dir,
2170 pb->deletion_conflicts
2171 ? svn_hash_gets(pb->deletion_conflicts,
2172 db->name)
2173 : NULL,
2174 db->pool, scratch_pool));
2175
2176 SVN_ERR(svn_wc__db_base_add_incomplete_directory(
2177 eb->db, db->local_abspath,
2178 db->new_repos_relpath,
2179 eb->repos_root,
2180 eb->repos_uuid,
2181 *eb->target_revision,
2182 db->ambient_depth,
2183 (db->shadowed && db->obstruction_found),
2184 (! db->shadowed
2185 && status == svn_wc__db_status_added),
2186 tree_conflict, NULL,
2187 scratch_pool));
2188
2189 /* Make sure there is a real directory at LOCAL_ABSPATH, unless we are just
2190 updating the DB */
2191 if (!db->shadowed)
2192 SVN_ERR(svn_wc__ensure_directory(db->local_abspath, scratch_pool));
2193
2194 if (tree_conflict != NULL)
2195 {
2196 db->edit_conflict = tree_conflict;
2197
2198 db->already_notified = TRUE;
2199 do_notification(eb, db->local_abspath, svn_node_dir,
2200 svn_wc_notify_tree_conflict, scratch_pool);
2201 }
2202
2203
2204 /* If this add was obstructed by dir scheduled for addition without
2205 history let close_directory() handle the notification because there
2206 might be properties to deal with. If PATH was added inside a locally
2207 deleted tree, then suppress notification, a tree conflict was already
2208 issued. */
2209 if (eb->notify_func && !db->already_notified && !db->add_existed)
2210 {
2211 svn_wc_notify_action_t action;
2212
2213 if (db->shadowed)
2214 action = svn_wc_notify_update_shadowed_add;
2215 else if (db->obstruction_found || db->add_existed)
2216 action = svn_wc_notify_exists;
2217 else
2218 action = svn_wc_notify_update_add;
2219
2220 db->already_notified = TRUE;
2221
2222 do_notification(eb, db->local_abspath, svn_node_dir, action,
2223 scratch_pool);
2224 }
2225
2226 svn_pool_destroy(scratch_pool);
2227
2228 return SVN_NO_ERROR;
2229 }
2230
2231 /* An svn_delta_editor_t function. */
2232 static svn_error_t *
open_directory(const char * path,void * parent_baton,svn_revnum_t base_revision,apr_pool_t * pool,void ** child_baton)2233 open_directory(const char *path,
2234 void *parent_baton,
2235 svn_revnum_t base_revision,
2236 apr_pool_t *pool,
2237 void **child_baton)
2238 {
2239 struct dir_baton *db, *pb = parent_baton;
2240 struct edit_baton *eb = pb->edit_baton;
2241 svn_boolean_t have_work;
2242 svn_boolean_t conflicted;
2243 svn_boolean_t conflict_ignored = FALSE;
2244 svn_skel_t *tree_conflict = NULL;
2245 svn_wc__db_status_t status, base_status;
2246 svn_node_kind_t wc_kind;
2247
2248 SVN_ERR(make_dir_baton(&db, path, eb, pb, FALSE, pool));
2249 *child_baton = db;
2250
2251 if (db->skip_this)
2252 return SVN_NO_ERROR;
2253
2254 /* Detect obstructing working copies */
2255 {
2256 svn_boolean_t is_root;
2257
2258 SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, db->local_abspath,
2259 pool));
2260
2261 if (is_root)
2262 {
2263 /* Just skip this node; a future update will handle it */
2264 SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
2265 db->skip_this = TRUE;
2266 db->already_notified = TRUE;
2267
2268 do_notification(eb, db->local_abspath, svn_node_dir,
2269 svn_wc_notify_update_skip_obstruction, pool);
2270
2271 return SVN_NO_ERROR;
2272 }
2273 }
2274
2275 /* We should have a write lock on every directory touched. */
2276 SVN_ERR(svn_wc__write_check(eb->db, db->local_abspath, pool));
2277
2278 SVN_ERR(svn_wc__db_read_info(&status, &wc_kind, &db->old_revision,
2279 &db->old_repos_relpath, NULL, NULL,
2280 &db->changed_rev, &db->changed_date,
2281 &db->changed_author, &db->ambient_depth,
2282 NULL, NULL, NULL, NULL,
2283 NULL, NULL, NULL, NULL, NULL, NULL,
2284 &conflicted, NULL, NULL, NULL,
2285 NULL, NULL, &have_work,
2286 eb->db, db->local_abspath,
2287 db->pool, pool));
2288
2289 if (!have_work)
2290 base_status = status;
2291 else
2292 SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &db->old_revision,
2293 &db->old_repos_relpath, NULL, NULL,
2294 &db->changed_rev, &db->changed_date,
2295 &db->changed_author, &db->ambient_depth,
2296 NULL, NULL, NULL, NULL, NULL, NULL,
2297 eb->db, db->local_abspath,
2298 db->pool, pool));
2299
2300 db->was_incomplete = (base_status == svn_wc__db_status_incomplete);
2301
2302 SVN_ERR(calculate_repos_relpath(&db->new_repos_relpath, db->local_abspath,
2303 db->old_repos_relpath, eb, pb,
2304 db->pool, pool));
2305
2306 /* Is this path a conflict victim? */
2307 if (db->shadowed)
2308 conflicted = FALSE; /* Conflict applies to WORKING */
2309 else if (conflicted)
2310 SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
2311 eb->db, db->local_abspath, pool));
2312 if (conflicted)
2313 {
2314 SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
2315
2316 db->skip_this = TRUE;
2317 db->already_notified = TRUE;
2318
2319 do_notification(eb, db->local_abspath, svn_node_unknown,
2320 svn_wc_notify_skip_conflicted, pool);
2321
2322 return SVN_NO_ERROR;
2323 }
2324 else if (conflict_ignored)
2325 {
2326 db->shadowed = TRUE;
2327 }
2328
2329 /* Is this path a fresh tree conflict victim? If so, skip the tree
2330 with one notification. */
2331
2332 /* Check for conflicts only when we haven't already recorded
2333 * a tree-conflict on a parent node. */
2334 if (!db->shadowed)
2335 SVN_ERR(check_tree_conflict(&tree_conflict, eb, db->local_abspath,
2336 status, TRUE, svn_node_dir,
2337 svn_wc_conflict_action_edit,
2338 db->pool, pool));
2339
2340 /* Remember the roots of any locally deleted trees. */
2341 if (tree_conflict != NULL)
2342 {
2343 svn_wc_conflict_reason_t reason;
2344 db->edit_conflict = tree_conflict;
2345 /* Other modifications wouldn't be a tree conflict */
2346
2347 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL, NULL,
2348 eb->db, db->local_abspath,
2349 tree_conflict,
2350 db->pool, db->pool));
2351 SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_deleted
2352 || reason == svn_wc_conflict_reason_moved_away
2353 || reason == svn_wc_conflict_reason_replaced
2354 || reason == svn_wc_conflict_reason_obstructed);
2355
2356 /* Continue updating BASE */
2357 if (reason == svn_wc_conflict_reason_obstructed)
2358 db->edit_obstructed = TRUE;
2359 else
2360 db->shadowed = TRUE;
2361 }
2362
2363 /* Mark directory as being at target_revision and URL, but incomplete. */
2364 SVN_ERR(svn_wc__db_temp_op_start_directory_update(eb->db, db->local_abspath,
2365 db->new_repos_relpath,
2366 *eb->target_revision,
2367 pool));
2368
2369 return SVN_NO_ERROR;
2370 }
2371
2372
2373 /* An svn_delta_editor_t function. */
2374 static svn_error_t *
change_dir_prop(void * dir_baton,const char * name,const svn_string_t * value,apr_pool_t * pool)2375 change_dir_prop(void *dir_baton,
2376 const char *name,
2377 const svn_string_t *value,
2378 apr_pool_t *pool)
2379 {
2380 svn_prop_t *propchange;
2381 struct dir_baton *db = dir_baton;
2382
2383 if (db->skip_this)
2384 return SVN_NO_ERROR;
2385
2386 propchange = apr_array_push(db->propchanges);
2387 propchange->name = apr_pstrdup(db->pool, name);
2388 propchange->value = svn_string_dup(value, db->pool);
2389
2390 if (!db->edited && svn_property_kind2(name) == svn_prop_regular_kind)
2391 SVN_ERR(mark_directory_edited(db, pool));
2392
2393 return SVN_NO_ERROR;
2394 }
2395
2396 /* If any of the svn_prop_t objects in PROPCHANGES represents a change
2397 to the SVN_PROP_EXTERNALS property, return that change, else return
2398 null. If PROPCHANGES contains more than one such change, return
2399 the first. */
2400 static const svn_prop_t *
externals_prop_changed(const apr_array_header_t * propchanges)2401 externals_prop_changed(const apr_array_header_t *propchanges)
2402 {
2403 int i;
2404
2405 for (i = 0; i < propchanges->nelts; i++)
2406 {
2407 const svn_prop_t *p = &(APR_ARRAY_IDX(propchanges, i, svn_prop_t));
2408 if (strcmp(p->name, SVN_PROP_EXTERNALS) == 0)
2409 return p;
2410 }
2411
2412 return NULL;
2413 }
2414
2415
2416
2417 /* An svn_delta_editor_t function. */
2418 static svn_error_t *
close_directory(void * dir_baton,apr_pool_t * pool)2419 close_directory(void *dir_baton,
2420 apr_pool_t *pool)
2421 {
2422 struct dir_baton *db = dir_baton;
2423 struct edit_baton *eb = db->edit_baton;
2424 svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
2425 apr_array_header_t *entry_prop_changes;
2426 apr_array_header_t *dav_prop_changes;
2427 apr_array_header_t *regular_prop_changes;
2428 apr_hash_t *base_props;
2429 apr_hash_t *actual_props;
2430 apr_hash_t *new_base_props = NULL;
2431 apr_hash_t *new_actual_props = NULL;
2432 svn_revnum_t new_changed_rev = SVN_INVALID_REVNUM;
2433 apr_time_t new_changed_date = 0;
2434 const char *new_changed_author = NULL;
2435 apr_pool_t *scratch_pool = db->pool;
2436 svn_skel_t *all_work_items = NULL;
2437 svn_skel_t *conflict_skel = NULL;
2438
2439 /* Skip if we're in a conflicted tree. */
2440 if (db->skip_this)
2441 {
2442 /* Allow the parent to complete its update. */
2443 SVN_ERR(maybe_release_dir_info(db));
2444
2445 return SVN_NO_ERROR;
2446 }
2447
2448 if (db->edited)
2449 conflict_skel = db->edit_conflict;
2450
2451 SVN_ERR(svn_categorize_props(db->propchanges, &entry_prop_changes,
2452 &dav_prop_changes, ®ular_prop_changes, pool));
2453
2454 /* Fetch the existing properties. */
2455 if ((!db->adding_dir || db->add_existed)
2456 && !db->shadowed)
2457 {
2458 SVN_ERR(svn_wc__get_actual_props(&actual_props,
2459 eb->db, db->local_abspath,
2460 scratch_pool, scratch_pool));
2461 }
2462 else
2463 actual_props = apr_hash_make(pool);
2464
2465 if (db->add_existed)
2466 {
2467 /* This node already exists. Grab the current pristine properties. */
2468 SVN_ERR(svn_wc__db_read_pristine_props(&base_props,
2469 eb->db, db->local_abspath,
2470 scratch_pool, scratch_pool));
2471 }
2472 else if (!db->adding_dir)
2473 {
2474 /* Get the BASE properties for proper merging. */
2475 SVN_ERR(svn_wc__db_base_get_props(&base_props,
2476 eb->db, db->local_abspath,
2477 scratch_pool, scratch_pool));
2478 }
2479 else
2480 base_props = apr_hash_make(pool);
2481
2482 /* An incomplete directory might have props which were supposed to be
2483 deleted but weren't. Because the server sent us all the props we're
2484 supposed to have, any previous base props not in this list must be
2485 deleted (issue #1672). */
2486 if (db->was_incomplete)
2487 {
2488 int i;
2489 apr_hash_t *props_to_delete;
2490 apr_hash_index_t *hi;
2491
2492 /* In a copy of the BASE props, remove every property that we see an
2493 incoming change for. The remaining unmentioned properties are those
2494 which need to be deleted. */
2495 props_to_delete = apr_hash_copy(pool, base_props);
2496 for (i = 0; i < regular_prop_changes->nelts; i++)
2497 {
2498 const svn_prop_t *prop;
2499 prop = &APR_ARRAY_IDX(regular_prop_changes, i, svn_prop_t);
2500 svn_hash_sets(props_to_delete, prop->name, NULL);
2501 }
2502
2503 /* Add these props to the incoming propchanges (in
2504 * regular_prop_changes). */
2505 for (hi = apr_hash_first(pool, props_to_delete);
2506 hi != NULL;
2507 hi = apr_hash_next(hi))
2508 {
2509 const char *propname = apr_hash_this_key(hi);
2510 svn_prop_t *prop = apr_array_push(regular_prop_changes);
2511
2512 /* Record a deletion for PROPNAME. */
2513 prop->name = propname;
2514 prop->value = NULL;
2515 }
2516 }
2517
2518 /* If this directory has property changes stored up, now is the time
2519 to deal with them. */
2520 if (regular_prop_changes->nelts)
2521 {
2522 /* If recording traversal info, then see if the
2523 SVN_PROP_EXTERNALS property on this directory changed,
2524 and record before and after for the change. */
2525 if (eb->external_func)
2526 {
2527 const svn_prop_t *change
2528 = externals_prop_changed(regular_prop_changes);
2529
2530 if (change)
2531 {
2532 const svn_string_t *new_val_s = change->value;
2533 const svn_string_t *old_val_s;
2534
2535 old_val_s = svn_hash_gets(base_props, SVN_PROP_EXTERNALS);
2536
2537 if ((new_val_s == NULL) && (old_val_s == NULL))
2538 ; /* No value before, no value after... so do nothing. */
2539 else if (new_val_s && old_val_s
2540 && (svn_string_compare(old_val_s, new_val_s)))
2541 ; /* Value did not change... so do nothing. */
2542 else if (old_val_s || new_val_s)
2543 /* something changed, record the change */
2544 {
2545 SVN_ERR((eb->external_func)(
2546 eb->external_baton,
2547 db->local_abspath,
2548 old_val_s,
2549 new_val_s,
2550 db->ambient_depth,
2551 db->pool));
2552 }
2553 }
2554 }
2555
2556 if (db->shadowed)
2557 {
2558 /* We don't have a relevant actual row, but we need actual properties
2559 to allow property merging without conflicts. */
2560 if (db->adding_dir)
2561 actual_props = apr_hash_make(scratch_pool);
2562 else
2563 actual_props = base_props;
2564 }
2565
2566 /* Merge pending properties. */
2567 new_base_props = svn_prop__patch(base_props, regular_prop_changes,
2568 db->pool);
2569 SVN_ERR_W(svn_wc__merge_props(&conflict_skel,
2570 &prop_state,
2571 &new_actual_props,
2572 eb->db,
2573 db->local_abspath,
2574 NULL /* use baseprops */,
2575 base_props,
2576 actual_props,
2577 regular_prop_changes,
2578 db->pool,
2579 scratch_pool),
2580 _("Couldn't do property merge"));
2581 /* After a (not-dry-run) merge, we ALWAYS have props to save. */
2582 SVN_ERR_ASSERT(new_base_props != NULL && new_actual_props != NULL);
2583 }
2584
2585 SVN_ERR(accumulate_last_change(&new_changed_rev, &new_changed_date,
2586 &new_changed_author, entry_prop_changes,
2587 scratch_pool, scratch_pool));
2588
2589 /* Check if we should add some not-present markers before marking the
2590 directory complete (Issue #3569) */
2591 {
2592 apr_hash_t *new_children = svn_hash_gets(eb->dir_dirents,
2593 db->new_repos_relpath);
2594
2595 if (new_children != NULL)
2596 {
2597 apr_hash_index_t *hi;
2598 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2599
2600 for (hi = apr_hash_first(scratch_pool, new_children);
2601 hi;
2602 hi = apr_hash_next(hi))
2603 {
2604 const char *child_name;
2605 const char *child_abspath;
2606 const char *child_relpath;
2607 const svn_dirent_t *dirent;
2608 svn_wc__db_status_t status;
2609 svn_node_kind_t child_kind;
2610 svn_error_t *err;
2611
2612 svn_pool_clear(iterpool);
2613
2614 child_name = apr_hash_this_key(hi);
2615 child_abspath = svn_dirent_join(db->local_abspath, child_name,
2616 iterpool);
2617
2618 dirent = apr_hash_this_val(hi);
2619 child_kind = (dirent->kind == svn_node_dir)
2620 ? svn_node_dir
2621 : svn_node_file;
2622
2623 if (db->ambient_depth < svn_depth_immediates
2624 && child_kind == svn_node_dir)
2625 continue; /* We don't need the subdirs */
2626
2627 /* ### We just check if there is some node in BASE at this path */
2628 err = svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL,
2629 NULL, NULL, NULL, NULL, NULL, NULL,
2630 NULL, NULL, NULL, NULL, NULL,
2631 eb->db, child_abspath,
2632 iterpool, iterpool);
2633
2634 if (!err)
2635 {
2636 svn_boolean_t is_wcroot;
2637 SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, eb->db, child_abspath,
2638 iterpool));
2639
2640 if (!is_wcroot)
2641 continue; /* Everything ok... Nothing to do here */
2642 /* Fall through to allow recovering later */
2643 }
2644 else if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
2645 return svn_error_trace(err);
2646
2647 svn_error_clear(err);
2648
2649 child_relpath = svn_relpath_join(db->new_repos_relpath, child_name,
2650 iterpool);
2651
2652 SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db,
2653 child_abspath,
2654 child_relpath,
2655 eb->repos_root,
2656 eb->repos_uuid,
2657 *eb->target_revision,
2658 child_kind,
2659 NULL, NULL,
2660 iterpool));
2661 }
2662
2663 svn_pool_destroy(iterpool);
2664 }
2665 }
2666
2667 if (apr_hash_count(db->not_present_nodes))
2668 {
2669 apr_hash_index_t *hi;
2670 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2671
2672 /* This should call some new function (which could also be used
2673 for new_children above) to add all the names in single
2674 transaction, but I can't even trigger it. I've tried
2675 ra_local, ra_svn, ra_neon, ra_serf and they all call
2676 close_file before close_dir. */
2677 for (hi = apr_hash_first(scratch_pool, db->not_present_nodes);
2678 hi;
2679 hi = apr_hash_next(hi))
2680 {
2681 const char *child = apr_hash_this_key(hi);
2682 const char *child_abspath, *child_relpath;
2683 svn_node_kind_t kind = svn_node_kind_from_word(apr_hash_this_val(hi));
2684
2685 svn_pool_clear(iterpool);
2686
2687 child_abspath = svn_dirent_join(db->local_abspath, child, iterpool);
2688 child_relpath = svn_dirent_join(db->new_repos_relpath, child, iterpool);
2689
2690 SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db,
2691 child_abspath,
2692 child_relpath,
2693 eb->repos_root,
2694 eb->repos_uuid,
2695 *eb->target_revision,
2696 kind,
2697 NULL, NULL,
2698 iterpool));
2699 }
2700 svn_pool_destroy(iterpool);
2701 }
2702
2703 /* If this directory is merely an anchor for a targeted child, then we
2704 should not be updating the node at all. */
2705 if (db->parent_baton == NULL
2706 && *eb->target_basename != '\0')
2707 {
2708 /* And we should not have received any changes! */
2709 SVN_ERR_ASSERT(db->propchanges->nelts == 0);
2710 /* ... which also implies NEW_CHANGED_* are not set,
2711 and NEW_BASE_PROPS == NULL. */
2712 }
2713 else
2714 {
2715 apr_hash_t *props;
2716 apr_array_header_t *iprops = NULL;
2717
2718 /* ### we know a base node already exists. it was created in
2719 ### open_directory or add_directory. let's just preserve the
2720 ### existing DEPTH value, and possibly CHANGED_*. */
2721 /* If we received any changed_* values, then use them. */
2722 if (SVN_IS_VALID_REVNUM(new_changed_rev))
2723 db->changed_rev = new_changed_rev;
2724 if (new_changed_date != 0)
2725 db->changed_date = new_changed_date;
2726 if (new_changed_author != NULL)
2727 db->changed_author = new_changed_author;
2728
2729 /* If no depth is set yet, set to infinity. */
2730 if (db->ambient_depth == svn_depth_unknown)
2731 db->ambient_depth = svn_depth_infinity;
2732
2733 if (eb->depth_is_sticky
2734 && db->ambient_depth != eb->requested_depth)
2735 {
2736 /* After a depth upgrade the entry must reflect the new depth.
2737 Upgrading to infinity changes the depth of *all* directories,
2738 upgrading to something else only changes the target. */
2739
2740 if (eb->requested_depth == svn_depth_infinity
2741 || (strcmp(db->local_abspath, eb->target_abspath) == 0
2742 && eb->requested_depth > db->ambient_depth))
2743 {
2744 db->ambient_depth = eb->requested_depth;
2745 }
2746 }
2747
2748 /* Do we have new properties to install? Or shall we simply retain
2749 the prior set of properties? If we're installing new properties,
2750 then we also want to write them to an old-style props file. */
2751 props = new_base_props;
2752 if (props == NULL)
2753 props = base_props;
2754
2755 if (conflict_skel)
2756 {
2757 svn_skel_t *work_item;
2758
2759 SVN_ERR(complete_conflict(conflict_skel,
2760 db->edit_baton,
2761 db->local_abspath,
2762 db->old_repos_relpath,
2763 db->old_revision,
2764 db->new_repos_relpath,
2765 svn_node_dir, svn_node_dir,
2766 (db->parent_baton
2767 && db->parent_baton->deletion_conflicts)
2768 ? svn_hash_gets(
2769 db->parent_baton->deletion_conflicts,
2770 db->name)
2771 : NULL,
2772 db->pool, scratch_pool));
2773
2774 SVN_ERR(svn_wc__conflict_create_markers(&work_item,
2775 eb->db, db->local_abspath,
2776 conflict_skel,
2777 scratch_pool, scratch_pool));
2778
2779 all_work_items = svn_wc__wq_merge(all_work_items, work_item,
2780 scratch_pool);
2781 }
2782
2783 /* Any inherited props to be set set for this base node? */
2784 if (eb->wcroot_iprops)
2785 {
2786 iprops = svn_hash_gets(eb->wcroot_iprops, db->local_abspath);
2787
2788 /* close_edit may also update iprops for switched nodes, catching
2789 those for which close_directory is never called (e.g. a switch
2790 with no changes). So as a minor optimization we remove any
2791 iprops from the hash so as not to set them again in
2792 close_edit. */
2793 if (iprops)
2794 svn_hash_sets(eb->wcroot_iprops, db->local_abspath, NULL);
2795 }
2796
2797 /* Update the BASE data for the directory and mark the directory
2798 complete */
2799 SVN_ERR(svn_wc__db_base_add_directory(
2800 eb->db, db->local_abspath,
2801 eb->wcroot_abspath,
2802 db->new_repos_relpath,
2803 eb->repos_root, eb->repos_uuid,
2804 *eb->target_revision,
2805 props,
2806 db->changed_rev, db->changed_date, db->changed_author,
2807 NULL /* children */,
2808 db->ambient_depth,
2809 (dav_prop_changes->nelts > 0)
2810 ? svn_prop_array_to_hash(dav_prop_changes, pool)
2811 : NULL,
2812 (! db->shadowed) && new_base_props != NULL,
2813 new_actual_props, iprops,
2814 conflict_skel, all_work_items,
2815 scratch_pool));
2816 }
2817
2818 /* Process all of the queued work items for this directory. */
2819 SVN_ERR(svn_wc__wq_run(eb->db, db->local_abspath,
2820 eb->cancel_func, eb->cancel_baton,
2821 scratch_pool));
2822
2823 if (db->parent_baton)
2824 svn_hash_sets(db->parent_baton->not_present_nodes, db->name, NULL);
2825
2826 if (conflict_skel && eb->conflict_func)
2827 SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, db->local_abspath,
2828 svn_node_dir,
2829 conflict_skel,
2830 NULL /* merge_options */,
2831 eb->conflict_func,
2832 eb->conflict_baton,
2833 eb->cancel_func,
2834 eb->cancel_baton,
2835 scratch_pool));
2836
2837 /* Notify of any prop changes on this directory -- but do nothing if
2838 it's an added or skipped directory, because notification has already
2839 happened in that case - unless the add was obstructed by a dir
2840 scheduled for addition without history, in which case we handle
2841 notification here). */
2842 if (!db->already_notified && eb->notify_func && db->edited)
2843 {
2844 svn_wc_notify_t *notify;
2845 svn_wc_notify_action_t action;
2846
2847 if (db->shadowed || db->edit_obstructed)
2848 action = svn_wc_notify_update_shadowed_update;
2849 else if (db->obstruction_found || db->add_existed)
2850 action = svn_wc_notify_exists;
2851 else
2852 action = svn_wc_notify_update_update;
2853
2854 notify = svn_wc_create_notify(db->local_abspath, action, pool);
2855 notify->kind = svn_node_dir;
2856 notify->prop_state = prop_state;
2857 notify->revision = *eb->target_revision;
2858 notify->old_revision = db->old_revision;
2859
2860 eb->notify_func(eb->notify_baton, notify, scratch_pool);
2861 }
2862
2863 if (db->edited)
2864 eb->edited = db->edited;
2865
2866 /* We're done with this directory, so remove one reference from the
2867 bump information. */
2868 SVN_ERR(maybe_release_dir_info(db));
2869
2870 return SVN_NO_ERROR;
2871 }
2872
2873
2874 /* Common code for 'absent_file' and 'absent_directory'. */
2875 static svn_error_t *
absent_node(const char * path,svn_node_kind_t absent_kind,void * parent_baton,apr_pool_t * pool)2876 absent_node(const char *path,
2877 svn_node_kind_t absent_kind,
2878 void *parent_baton,
2879 apr_pool_t *pool)
2880 {
2881 struct dir_baton *pb = parent_baton;
2882 struct edit_baton *eb = pb->edit_baton;
2883 apr_pool_t *scratch_pool = svn_pool_create(pool);
2884 const char *name = svn_dirent_basename(path, NULL);
2885 const char *local_abspath;
2886 svn_error_t *err;
2887 svn_wc__db_status_t status;
2888 svn_node_kind_t kind;
2889 svn_skel_t *tree_conflict = NULL;
2890
2891 if (pb->skip_this)
2892 return SVN_NO_ERROR;
2893
2894 local_abspath = svn_dirent_join(pb->local_abspath, name, scratch_pool);
2895 /* If an item by this name is scheduled for addition that's a
2896 genuine tree-conflict. */
2897 err = svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
2898 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
2899 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
2900 NULL, NULL, NULL, NULL,
2901 eb->db, local_abspath,
2902 scratch_pool, scratch_pool);
2903
2904 if (err)
2905 {
2906 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
2907 return svn_error_trace(err);
2908
2909 svn_error_clear(err);
2910 status = svn_wc__db_status_not_present;
2911 kind = svn_node_unknown;
2912 }
2913
2914 if (status != svn_wc__db_status_server_excluded)
2915 SVN_ERR(mark_directory_edited(pb, scratch_pool));
2916 /* Else fall through as we should update the revision anyway */
2917
2918 if (status == svn_wc__db_status_normal)
2919 {
2920 svn_boolean_t wcroot;
2921 /* We found an obstructing working copy or a file external! */
2922
2923 SVN_ERR(svn_wc__db_is_wcroot(&wcroot, eb->db, local_abspath,
2924 scratch_pool));
2925
2926 if (wcroot)
2927 {
2928 /*
2929 We have an obstructing working copy; possibly a directory external
2930
2931 We can do two things now:
2932 1) notify the user, record a skip, etc.
2933 2) Just record the absent node in BASE in the parent
2934 working copy.
2935
2936 As option 2 happens to be exactly what we do anyway, fall through.
2937 */
2938 }
2939 else
2940 {
2941 svn_boolean_t file_external;
2942 svn_revnum_t revnum;
2943
2944 SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &revnum, NULL, NULL,
2945 NULL, NULL, NULL, NULL, NULL, NULL,
2946 NULL, NULL, NULL, NULL,
2947 &file_external,
2948 eb->db, local_abspath,
2949 scratch_pool, scratch_pool));
2950
2951 if (file_external)
2952 {
2953 /* The server asks us to replace a file external
2954 (Existing BASE node; not reported by the working copy crawler
2955 or there would have been a delete_entry() call.
2956
2957 There is no way we can store this state in the working copy as
2958 the BASE layer is already filled.
2959 We could error out, but that is not helping anybody; the user is not
2960 even seeing with what the file external would be replaced, so let's
2961 report a skip and continue the update.
2962 */
2963
2964 if (eb->notify_func)
2965 {
2966 svn_wc_notify_t *notify;
2967 notify = svn_wc_create_notify(
2968 local_abspath,
2969 svn_wc_notify_update_skip_obstruction,
2970 scratch_pool);
2971
2972 eb->notify_func(eb->notify_baton, notify, scratch_pool);
2973 }
2974
2975 svn_pool_destroy(scratch_pool);
2976 return SVN_NO_ERROR;
2977 }
2978 else
2979 {
2980 /* We have a normal local node that will now be hidden for the
2981 user. Let's try to delete what is there. This may introduce
2982 tree conflicts if there are local changes */
2983 SVN_ERR(delete_entry(path, revnum, pb, scratch_pool));
2984
2985 /* delete_entry() promises that BASE is empty after the operation,
2986 so we can just fall through now */
2987 }
2988 }
2989 }
2990 else if (status == svn_wc__db_status_not_present
2991 || status == svn_wc__db_status_server_excluded
2992 || status == svn_wc__db_status_excluded)
2993 {
2994 /* The BASE node is not actually there, so we can safely turn it into
2995 an absent node */
2996 }
2997 else
2998 {
2999 /* We have a local addition. If this would be a BASE node it would have
3000 been deleted before we get here. (Which might have turned it into
3001 a copy). */
3002 SVN_ERR_ASSERT(status != svn_wc__db_status_normal);
3003
3004 if (!pb->shadowed && !pb->edit_obstructed)
3005 SVN_ERR(check_tree_conflict(&tree_conflict, eb, local_abspath,
3006 status, FALSE, svn_node_unknown,
3007 svn_wc_conflict_action_add,
3008 scratch_pool, scratch_pool));
3009
3010 }
3011
3012 {
3013 const char *repos_relpath;
3014 repos_relpath = svn_relpath_join(pb->new_repos_relpath, name, scratch_pool);
3015
3016 if (tree_conflict)
3017 SVN_ERR(complete_conflict(tree_conflict, eb, local_abspath,
3018 NULL, SVN_INVALID_REVNUM, repos_relpath,
3019 kind, svn_node_unknown, NULL,
3020 scratch_pool, scratch_pool));
3021
3022 /* Insert an excluded node below the parent node to note that this child
3023 is absent. (This puts it in the parent db if the child is obstructed) */
3024 SVN_ERR(svn_wc__db_base_add_excluded_node(eb->db, local_abspath,
3025 repos_relpath, eb->repos_root,
3026 eb->repos_uuid,
3027 *(eb->target_revision),
3028 absent_kind,
3029 svn_wc__db_status_server_excluded,
3030 tree_conflict, NULL,
3031 scratch_pool));
3032
3033 if (tree_conflict)
3034 {
3035 if (eb->conflict_func)
3036 SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, local_abspath,
3037 kind,
3038 tree_conflict,
3039 NULL /* merge_options */,
3040 eb->conflict_func,
3041 eb->conflict_baton,
3042 eb->cancel_func,
3043 eb->cancel_baton,
3044 scratch_pool));
3045 do_notification(eb, local_abspath, kind, svn_wc_notify_tree_conflict,
3046 scratch_pool);
3047 }
3048 }
3049
3050 svn_pool_destroy(scratch_pool);
3051
3052 return SVN_NO_ERROR;
3053 }
3054
3055
3056 /* An svn_delta_editor_t function. */
3057 static svn_error_t *
absent_file(const char * path,void * parent_baton,apr_pool_t * pool)3058 absent_file(const char *path,
3059 void *parent_baton,
3060 apr_pool_t *pool)
3061 {
3062 return absent_node(path, svn_node_file, parent_baton, pool);
3063 }
3064
3065
3066 /* An svn_delta_editor_t function. */
3067 static svn_error_t *
absent_directory(const char * path,void * parent_baton,apr_pool_t * pool)3068 absent_directory(const char *path,
3069 void *parent_baton,
3070 apr_pool_t *pool)
3071 {
3072 return absent_node(path, svn_node_dir, parent_baton, pool);
3073 }
3074
3075
3076 /* An svn_delta_editor_t function. */
3077 static svn_error_t *
add_file(const char * path,void * parent_baton,const char * copyfrom_path,svn_revnum_t copyfrom_rev,apr_pool_t * pool,void ** file_baton)3078 add_file(const char *path,
3079 void *parent_baton,
3080 const char *copyfrom_path,
3081 svn_revnum_t copyfrom_rev,
3082 apr_pool_t *pool,
3083 void **file_baton)
3084 {
3085 struct dir_baton *pb = parent_baton;
3086 struct edit_baton *eb = pb->edit_baton;
3087 struct file_baton *fb;
3088 svn_node_kind_t kind;
3089 svn_node_kind_t wc_kind;
3090 svn_wc__db_status_t status;
3091 apr_pool_t *scratch_pool;
3092 svn_boolean_t conflicted;
3093 svn_boolean_t conflict_ignored = FALSE;
3094 svn_boolean_t versioned_locally_and_present;
3095 svn_skel_t *tree_conflict = NULL;
3096 svn_error_t *err = SVN_NO_ERROR;
3097
3098 SVN_ERR_ASSERT(! (copyfrom_path || SVN_IS_VALID_REVNUM(copyfrom_rev)));
3099
3100 SVN_ERR(make_file_baton(&fb, pb, path, TRUE, pool));
3101 *file_baton = fb;
3102
3103 if (fb->skip_this)
3104 return SVN_NO_ERROR;
3105
3106 SVN_ERR(calculate_repos_relpath(&fb->new_repos_relpath, fb->local_abspath,
3107 NULL, eb, pb, fb->pool, pool));
3108 SVN_ERR(mark_file_edited(fb, pool));
3109
3110 /* The file_pool can stick around for a *long* time, so we want to
3111 use a subpool for any temporary allocations. */
3112 scratch_pool = svn_pool_create(pool);
3113
3114
3115 /* It may not be named the same as the administrative directory. */
3116 if (svn_wc_is_adm_dir(fb->name, pool))
3117 return svn_error_createf(
3118 SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
3119 _("Failed to add file '%s': object of the same name as the "
3120 "administrative directory"),
3121 svn_dirent_local_style(fb->local_abspath, pool));
3122
3123 if (!eb->clean_checkout)
3124 {
3125 SVN_ERR(svn_io_check_path(fb->local_abspath, &kind, scratch_pool));
3126
3127 err = svn_wc__db_read_info(&status, &wc_kind, NULL, NULL, NULL, NULL, NULL,
3128 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3129 NULL, NULL, NULL, NULL, NULL,
3130 &conflicted, NULL, NULL, NULL, NULL, NULL, NULL,
3131 eb->db, fb->local_abspath,
3132 scratch_pool, scratch_pool);
3133 }
3134 else
3135 {
3136 kind = svn_node_none;
3137 status = svn_wc__db_status_not_present;
3138 wc_kind = svn_node_unknown;
3139 conflicted = FALSE;
3140 }
3141
3142 if (err)
3143 {
3144 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
3145 return svn_error_trace(err);
3146
3147 svn_error_clear(err);
3148 wc_kind = svn_node_unknown;
3149 conflicted = FALSE;
3150
3151 versioned_locally_and_present = FALSE;
3152 }
3153 else if (status == svn_wc__db_status_normal && wc_kind == svn_node_unknown)
3154 {
3155 SVN_ERR_ASSERT(conflicted);
3156 versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */
3157 }
3158 else if (status == svn_wc__db_status_normal
3159 || status == svn_wc__db_status_incomplete)
3160 {
3161 svn_boolean_t root;
3162
3163 SVN_ERR(svn_wc__db_is_wcroot(&root, eb->db, fb->local_abspath,
3164 scratch_pool));
3165
3166 if (root)
3167 {
3168 /* !! We found the root of a working copy obstructing the wc !!
3169
3170 If the directory would be part of our own working copy then
3171 we wouldn't have been called as an add_directory().
3172
3173 The only thing we can do is add a not-present node, to allow
3174 a future update to bring in the new files when the problem is
3175 resolved. Note that svn_wc__db_base_add_not_present_node()
3176 explicitly adds the node into the parent's node database. */
3177
3178 svn_hash_sets(pb->not_present_nodes,
3179 apr_pstrdup(pb->pool, fb->name),
3180 svn_node_kind_to_word(svn_node_dir));
3181 }
3182 else if (wc_kind == svn_node_dir)
3183 {
3184 /* We have an editor violation. Github sometimes does this
3185 in its subversion compatibility code, when changing the
3186 depth of a working copy, or on updates from incomplete */
3187 }
3188 else
3189 {
3190 /* We found a file external occupating the place we need in BASE.
3191
3192 We can't add a not-present node in this case as that would overwrite
3193 the file external. Luckily the file external itself stops us from
3194 forgetting a child of this parent directory like an obstructing
3195 working copy would.
3196
3197 The reason we get here is that the adm crawler doesn't report
3198 file externals.
3199 */
3200 SVN_ERR_ASSERT(wc_kind == svn_node_file
3201 || wc_kind == svn_node_symlink);
3202 }
3203
3204 SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3205 fb->skip_this = TRUE;
3206 fb->already_notified = TRUE;
3207
3208 do_notification(eb, fb->local_abspath, wc_kind,
3209 svn_wc_notify_update_skip_obstruction, scratch_pool);
3210
3211 svn_pool_destroy(scratch_pool);
3212
3213 return SVN_NO_ERROR;
3214 }
3215 else
3216 versioned_locally_and_present = IS_NODE_PRESENT(status);
3217
3218
3219 /* Is this path a conflict victim? */
3220 if (fb->shadowed)
3221 conflicted = FALSE; /* Conflict applies to WORKING */
3222 else if (conflicted)
3223 {
3224 if (pb->deletion_conflicts)
3225 tree_conflict = svn_hash_gets(pb->deletion_conflicts, fb->name);
3226
3227 if (tree_conflict)
3228 {
3229 svn_wc_conflict_reason_t reason;
3230 const char *move_src_op_root_abspath;
3231 const char *move_dst_op_root_abspath;
3232 /* So this deletion wasn't just a deletion, it is actually a
3233 replacement. Let's install a better tree conflict. */
3234
3235 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL,
3236 &move_src_op_root_abspath,
3237 &move_dst_op_root_abspath,
3238 eb->db,
3239 fb->local_abspath,
3240 tree_conflict,
3241 fb->pool, scratch_pool));
3242
3243 tree_conflict = svn_wc__conflict_skel_create(fb->pool);
3244
3245 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
3246 tree_conflict,
3247 eb->db, fb->local_abspath,
3248 reason, svn_wc_conflict_action_replace,
3249 move_src_op_root_abspath,
3250 move_dst_op_root_abspath,
3251 fb->pool, scratch_pool));
3252
3253 /* And now stop checking for conflicts here and just perform
3254 a shadowed update */
3255 fb->edit_conflict = tree_conflict; /* Cache for close_file */
3256 tree_conflict = NULL; /* No direct notification */
3257 fb->shadowed = TRUE; /* Just continue */
3258 conflicted = FALSE; /* No skip */
3259 }
3260 else
3261 SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
3262 eb->db, fb->local_abspath, pool));
3263 }
3264
3265 /* Now the usual conflict handling: skip. */
3266 if (conflicted)
3267 {
3268 SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3269
3270 fb->skip_this = TRUE;
3271 fb->already_notified = TRUE;
3272
3273 /* We skip this node, but once the update completes the parent node will
3274 be updated to the new revision. So a future recursive update of the
3275 parent will not bring in this new node as the revision of the parent
3276 describes to the repository that all children are available.
3277
3278 To resolve this problem, we add a not-present node to allow bringing
3279 the node in once this conflict is resolved.
3280
3281 Note that we can safely assume that no present base node exists,
3282 because then we would not have received an add_file.
3283 */
3284 svn_hash_sets(pb->not_present_nodes, apr_pstrdup(pb->pool, fb->name),
3285 svn_node_kind_to_word(svn_node_file));
3286
3287 do_notification(eb, fb->local_abspath, svn_node_file,
3288 svn_wc_notify_skip_conflicted, scratch_pool);
3289
3290 svn_pool_destroy(scratch_pool);
3291
3292 return SVN_NO_ERROR;
3293 }
3294 else if (conflict_ignored)
3295 {
3296 fb->shadowed = TRUE;
3297 }
3298
3299 if (fb->shadowed)
3300 {
3301 /* Nothing to check; does not and will not exist in working copy */
3302 }
3303 else if (versioned_locally_and_present)
3304 {
3305 /* What to do with a versioned or schedule-add file:
3306
3307 If the UUID doesn't match the parent's, or the URL isn't a child of
3308 the parent dir's URL, it's an error.
3309
3310 Set add_existed so that user notification is delayed until after any
3311 text or prop conflicts have been found.
3312
3313 Whether the incoming add is a symlink or a file will only be known in
3314 close_file(), when the props are known. So with a locally added file
3315 or symlink, let close_file() check for a tree conflict.
3316
3317 We will never see missing files here, because these would be
3318 re-added during the crawler phase. */
3319 svn_boolean_t local_is_file;
3320
3321 /* Is the local node a copy or move */
3322 if (status == svn_wc__db_status_added)
3323 SVN_ERR(svn_wc__db_scan_addition(&status, NULL, NULL, NULL, NULL, NULL,
3324 NULL, NULL, NULL,
3325 eb->db, fb->local_abspath,
3326 scratch_pool, scratch_pool));
3327
3328 /* Is there something that is a file? */
3329 local_is_file = (wc_kind == svn_node_file
3330 || wc_kind == svn_node_symlink);
3331
3332 /* Do tree conflict checking if
3333 * - if there is a local copy.
3334 * - if this is a switch operation
3335 * - the node kinds mismatch
3336 *
3337 * During switch, local adds at the same path as incoming adds get
3338 * "lost" in that switching back to the original will no longer have the
3339 * local add. So switch always alerts the user with a tree conflict. */
3340 if (!eb->adds_as_modification
3341 || !local_is_file
3342 || status != svn_wc__db_status_added)
3343 {
3344 SVN_ERR(check_tree_conflict(&tree_conflict, eb,
3345 fb->local_abspath,
3346 status, FALSE, svn_node_none,
3347 svn_wc_conflict_action_add,
3348 fb->pool, scratch_pool));
3349 }
3350
3351 if (tree_conflict == NULL)
3352 fb->add_existed = TRUE; /* Take over WORKING */
3353 else
3354 fb->shadowed = TRUE; /* Only update BASE */
3355
3356 }
3357 else if (kind != svn_node_none)
3358 {
3359 /* There's an unversioned node at this path. */
3360 fb->obstruction_found = TRUE;
3361
3362 /* Unversioned, obstructing files are handled by text merge/conflict,
3363 * if unversioned obstructions are allowed. */
3364 if (! (kind == svn_node_file && eb->allow_unver_obstructions))
3365 {
3366 /* Bring in the node as deleted */ /* ### Obstructed Conflict */
3367 fb->shadowed = TRUE;
3368
3369 /* Mark a conflict */
3370 tree_conflict = svn_wc__conflict_skel_create(fb->pool);
3371
3372 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
3373 tree_conflict,
3374 eb->db, fb->local_abspath,
3375 svn_wc_conflict_reason_unversioned,
3376 svn_wc_conflict_action_add,
3377 NULL, NULL,
3378 fb->pool, scratch_pool));
3379 }
3380 }
3381
3382 /* When this is not the update target add a not-present BASE node now,
3383 to allow marking the parent directory complete in its close_edit() call.
3384 This resolves issues when that occurs before the close_file(). */
3385 if (pb->parent_baton
3386 || *eb->target_basename == '\0'
3387 || (strcmp(fb->local_abspath, eb->target_abspath) != 0))
3388 {
3389 svn_hash_sets(pb->not_present_nodes, apr_pstrdup(pb->pool, fb->name),
3390 svn_node_kind_to_word(svn_node_file));
3391 }
3392
3393 if (tree_conflict != NULL)
3394 {
3395 SVN_ERR(complete_conflict(tree_conflict,
3396 fb->edit_baton,
3397 fb->local_abspath,
3398 fb->old_repos_relpath,
3399 fb->old_revision,
3400 fb->new_repos_relpath,
3401 wc_kind, svn_node_file,
3402 pb->deletion_conflicts
3403 ? svn_hash_gets(pb->deletion_conflicts,
3404 fb->name)
3405 : NULL,
3406 fb->pool, scratch_pool));
3407
3408 SVN_ERR(svn_wc__db_op_mark_conflict(eb->db,
3409 fb->local_abspath,
3410 tree_conflict, NULL,
3411 scratch_pool));
3412
3413 fb->edit_conflict = tree_conflict;
3414
3415 fb->already_notified = TRUE;
3416 do_notification(eb, fb->local_abspath, svn_node_file,
3417 svn_wc_notify_tree_conflict, scratch_pool);
3418 }
3419
3420 svn_pool_destroy(scratch_pool);
3421
3422 return SVN_NO_ERROR;
3423 }
3424
3425
3426 /* An svn_delta_editor_t function. */
3427 static svn_error_t *
open_file(const char * path,void * parent_baton,svn_revnum_t base_revision,apr_pool_t * pool,void ** file_baton)3428 open_file(const char *path,
3429 void *parent_baton,
3430 svn_revnum_t base_revision,
3431 apr_pool_t *pool,
3432 void **file_baton)
3433 {
3434 struct dir_baton *pb = parent_baton;
3435 struct edit_baton *eb = pb->edit_baton;
3436 struct file_baton *fb;
3437 svn_boolean_t conflicted;
3438 svn_boolean_t conflict_ignored = FALSE;
3439 svn_boolean_t have_work;
3440 svn_wc__db_status_t status;
3441 svn_node_kind_t wc_kind;
3442 svn_skel_t *tree_conflict = NULL;
3443
3444 /* the file_pool can stick around for a *long* time, so we want to use
3445 a subpool for any temporary allocations. */
3446 apr_pool_t *scratch_pool = svn_pool_create(pool);
3447
3448 SVN_ERR(make_file_baton(&fb, pb, path, FALSE, pool));
3449 *file_baton = fb;
3450
3451 if (fb->skip_this)
3452 return SVN_NO_ERROR;
3453
3454 /* Detect obstructing working copies */
3455 {
3456 svn_boolean_t is_root;
3457
3458 SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, fb->local_abspath,
3459 pool));
3460
3461 if (is_root)
3462 {
3463 /* Just skip this node; a future update will handle it */
3464 SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3465 fb->skip_this = TRUE;
3466 fb->already_notified = TRUE;
3467
3468 do_notification(eb, fb->local_abspath, svn_node_file,
3469 svn_wc_notify_update_skip_obstruction, pool);
3470
3471 return SVN_NO_ERROR;
3472 }
3473 }
3474
3475 /* Sanity check. */
3476
3477 SVN_ERR(svn_wc__db_read_info(&status, &wc_kind, &fb->old_revision,
3478 &fb->old_repos_relpath, NULL, NULL,
3479 &fb->changed_rev, &fb->changed_date,
3480 &fb->changed_author, NULL,
3481 &fb->original_checksum, NULL, NULL, NULL,
3482 NULL, NULL, NULL, NULL, NULL, NULL,
3483 &conflicted, NULL, NULL, &fb->local_prop_mods,
3484 NULL, NULL, &have_work,
3485 eb->db, fb->local_abspath,
3486 fb->pool, scratch_pool));
3487
3488 if (have_work)
3489 SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &fb->old_revision,
3490 &fb->old_repos_relpath, NULL, NULL,
3491 &fb->changed_rev, &fb->changed_date,
3492 &fb->changed_author, NULL,
3493 &fb->original_checksum, NULL, NULL,
3494 NULL, NULL, NULL,
3495 eb->db, fb->local_abspath,
3496 fb->pool, scratch_pool));
3497
3498 SVN_ERR(calculate_repos_relpath(&fb->new_repos_relpath, fb->local_abspath,
3499 fb->old_repos_relpath, eb, pb,
3500 fb->pool, scratch_pool));
3501
3502 /* Is this path a conflict victim? */
3503 if (fb->shadowed)
3504 conflicted = FALSE; /* Conflict applies to WORKING */
3505 else if (conflicted)
3506 SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
3507 eb->db, fb->local_abspath, pool));
3508 if (conflicted)
3509 {
3510 SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3511
3512 fb->skip_this = TRUE;
3513 fb->already_notified = TRUE;
3514
3515 do_notification(eb, fb->local_abspath, svn_node_unknown,
3516 svn_wc_notify_skip_conflicted, scratch_pool);
3517
3518 svn_pool_destroy(scratch_pool);
3519
3520 return SVN_NO_ERROR;
3521 }
3522 else if (conflict_ignored)
3523 {
3524 fb->shadowed = TRUE;
3525 }
3526
3527 /* Check for conflicts only when we haven't already recorded
3528 * a tree-conflict on a parent node. */
3529 if (!fb->shadowed)
3530 SVN_ERR(check_tree_conflict(&tree_conflict, eb, fb->local_abspath,
3531 status, TRUE, svn_node_file,
3532 svn_wc_conflict_action_edit,
3533 fb->pool, scratch_pool));
3534
3535 /* Is this path the victim of a newly-discovered tree conflict? */
3536 if (tree_conflict != NULL)
3537 {
3538 svn_wc_conflict_reason_t reason;
3539 fb->edit_conflict = tree_conflict;
3540 /* Other modifications wouldn't be a tree conflict */
3541
3542 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL, NULL,
3543 eb->db, fb->local_abspath,
3544 tree_conflict,
3545 scratch_pool, scratch_pool));
3546 SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_deleted
3547 || reason == svn_wc_conflict_reason_moved_away
3548 || reason == svn_wc_conflict_reason_replaced
3549 || reason == svn_wc_conflict_reason_obstructed);
3550
3551 /* Continue updating BASE */
3552 if (reason == svn_wc_conflict_reason_obstructed)
3553 fb->edit_obstructed = TRUE;
3554 else
3555 fb->shadowed = TRUE;
3556 }
3557
3558 svn_pool_destroy(scratch_pool);
3559
3560 return SVN_NO_ERROR;
3561 }
3562
3563 /* Implements svn_stream_lazyopen_func_t. */
3564 static svn_error_t *
lazy_open_source(svn_stream_t ** stream,void * baton,apr_pool_t * result_pool,apr_pool_t * scratch_pool)3565 lazy_open_source(svn_stream_t **stream,
3566 void *baton,
3567 apr_pool_t *result_pool,
3568 apr_pool_t *scratch_pool)
3569 {
3570 struct file_baton *fb = baton;
3571
3572 SVN_ERR(svn_wc__db_pristine_read(stream, NULL, fb->edit_baton->db,
3573 fb->local_abspath,
3574 fb->original_checksum,
3575 result_pool, scratch_pool));
3576
3577
3578 return SVN_NO_ERROR;
3579 }
3580
3581 /* Implements svn_stream_lazyopen_func_t. */
3582 static svn_error_t *
lazy_open_target(svn_stream_t ** stream,void * baton,apr_pool_t * result_pool,apr_pool_t * scratch_pool)3583 lazy_open_target(svn_stream_t **stream,
3584 void *baton,
3585 apr_pool_t *result_pool,
3586 apr_pool_t *scratch_pool)
3587 {
3588 struct handler_baton *hb = baton;
3589 svn_wc__db_install_data_t *install_data;
3590
3591 /* By convention return value is undefined on error, but we rely
3592 on HB->INSTALL_DATA value in window_handler() and abort
3593 INSTALL_STREAM if is not NULL on error.
3594 So we store INSTALL_DATA to local variable first, to leave
3595 HB->INSTALL_DATA unchanged on error. */
3596 SVN_ERR(svn_wc__db_pristine_prepare_install(stream,
3597 &install_data,
3598 &hb->new_text_base_sha1_checksum,
3599 NULL,
3600 hb->fb->edit_baton->db,
3601 hb->fb->dir_baton->local_abspath,
3602 result_pool, scratch_pool));
3603
3604 hb->install_data = install_data;
3605
3606 return SVN_NO_ERROR;
3607 }
3608
3609 /* An svn_delta_editor_t function. */
3610 static svn_error_t *
apply_textdelta(void * file_baton,const char * expected_checksum,apr_pool_t * pool,svn_txdelta_window_handler_t * handler,void ** handler_baton)3611 apply_textdelta(void *file_baton,
3612 const char *expected_checksum,
3613 apr_pool_t *pool,
3614 svn_txdelta_window_handler_t *handler,
3615 void **handler_baton)
3616 {
3617 struct file_baton *fb = file_baton;
3618 apr_pool_t *handler_pool = svn_pool_create(fb->pool);
3619 struct handler_baton *hb = apr_pcalloc(handler_pool, sizeof(*hb));
3620 struct edit_baton *eb = fb->edit_baton;
3621 const svn_checksum_t *recorded_base_checksum;
3622 svn_checksum_t *expected_base_checksum;
3623 svn_stream_t *source;
3624 svn_stream_t *target;
3625
3626 if (fb->skip_this)
3627 {
3628 *handler = svn_delta_noop_window_handler;
3629 *handler_baton = NULL;
3630 return SVN_NO_ERROR;
3631 }
3632
3633 SVN_ERR(mark_file_edited(fb, pool));
3634
3635 /* Parse checksum or sets expected_base_checksum to NULL */
3636 SVN_ERR(svn_checksum_parse_hex(&expected_base_checksum, svn_checksum_md5,
3637 expected_checksum, pool));
3638
3639 /* Before applying incoming svndiff data to text base, make sure
3640 text base hasn't been corrupted, and that its checksum
3641 matches the expected base checksum. */
3642
3643 /* The incoming delta is targeted against EXPECTED_BASE_CHECKSUM. Find and
3644 check our RECORDED_BASE_CHECKSUM. (In WC-1, we could not do this test
3645 for replaced nodes because we didn't store the checksum of the "revert
3646 base". In WC-NG, we do and we can.) */
3647 recorded_base_checksum = fb->original_checksum;
3648
3649 /* If we have a checksum that we want to compare to a MD5 checksum,
3650 ensure that it is a MD5 checksum */
3651 if (recorded_base_checksum
3652 && expected_base_checksum
3653 && recorded_base_checksum->kind != svn_checksum_md5)
3654 SVN_ERR(svn_wc__db_pristine_get_md5(&recorded_base_checksum,
3655 eb->db, eb->wcroot_abspath,
3656 recorded_base_checksum, pool, pool));
3657
3658
3659 if (!svn_checksum_match(expected_base_checksum, recorded_base_checksum))
3660 return svn_error_createf(SVN_ERR_WC_CORRUPT_TEXT_BASE, NULL,
3661 _("Checksum mismatch for '%s':\n"
3662 " expected: %s\n"
3663 " recorded: %s\n"),
3664 svn_dirent_local_style(fb->local_abspath, pool),
3665 svn_checksum_to_cstring_display(expected_base_checksum,
3666 pool),
3667 svn_checksum_to_cstring_display(recorded_base_checksum,
3668 pool));
3669
3670 /* Open the text base for reading, unless this is an added file. */
3671
3672 /*
3673 kff todo: what we really need to do here is:
3674
3675 1. See if there's a file or dir by this name already here.
3676 2. See if it's under revision control.
3677 3. If both are true, open text-base.
3678 4. If only 1 is true, bail, because we can't go destroying user's
3679 files (or as an alternative to bailing, move it to some tmp
3680 name and somehow tell the user, but communicating with the
3681 user without erroring is a whole callback system we haven't
3682 finished inventing yet.)
3683 */
3684
3685 if (! fb->adding_file)
3686 {
3687 SVN_ERR_ASSERT(!fb->original_checksum
3688 || fb->original_checksum->kind == svn_checksum_sha1);
3689
3690 source = svn_stream_lazyopen_create(lazy_open_source, fb, FALSE,
3691 handler_pool);
3692 }
3693 else
3694 {
3695 source = svn_stream_empty(handler_pool);
3696 }
3697
3698 /* If we don't have a recorded checksum, use the ra provided checksum */
3699 if (!recorded_base_checksum)
3700 recorded_base_checksum = expected_base_checksum;
3701
3702 /* Checksum the text base while applying deltas */
3703 if (recorded_base_checksum)
3704 {
3705 hb->expected_source_checksum = svn_checksum_dup(recorded_base_checksum,
3706 handler_pool);
3707
3708 /* Wrap stream and store reference to allow calculating the
3709 checksum. */
3710 source = svn_stream_checksummed2(source,
3711 &hb->actual_source_checksum,
3712 NULL, recorded_base_checksum->kind,
3713 TRUE, handler_pool);
3714 hb->source_checksum_stream = source;
3715 }
3716
3717 target = svn_stream_lazyopen_create(lazy_open_target, hb, TRUE, handler_pool);
3718
3719 /* Prepare to apply the delta. */
3720 svn_txdelta_apply(source, target,
3721 hb->new_text_base_md5_digest,
3722 fb->local_abspath /* error_info */,
3723 handler_pool,
3724 &hb->apply_handler, &hb->apply_baton);
3725
3726 hb->pool = handler_pool;
3727 hb->fb = fb;
3728
3729 /* We're all set. */
3730 *handler_baton = hb;
3731 *handler = window_handler;
3732
3733 return SVN_NO_ERROR;
3734 }
3735
3736
3737 /* An svn_delta_editor_t function. */
3738 static svn_error_t *
change_file_prop(void * file_baton,const char * name,const svn_string_t * value,apr_pool_t * scratch_pool)3739 change_file_prop(void *file_baton,
3740 const char *name,
3741 const svn_string_t *value,
3742 apr_pool_t *scratch_pool)
3743 {
3744 struct file_baton *fb = file_baton;
3745 svn_prop_t *propchange;
3746
3747 if (fb->skip_this)
3748 return SVN_NO_ERROR;
3749
3750 /* Push a new propchange to the file baton's array of propchanges */
3751 propchange = apr_array_push(fb->propchanges);
3752 propchange->name = apr_pstrdup(fb->pool, name);
3753 propchange->value = svn_string_dup(value, fb->pool);
3754
3755 if (!fb->edited && svn_property_kind2(name) == svn_prop_regular_kind)
3756 SVN_ERR(mark_file_edited(fb, scratch_pool));
3757
3758 if (! fb->shadowed
3759 && strcmp(name, SVN_PROP_SPECIAL) == 0)
3760 {
3761 struct edit_baton *eb = fb->edit_baton;
3762 svn_boolean_t modified = FALSE;
3763 svn_boolean_t becomes_symlink;
3764 svn_boolean_t was_symlink;
3765
3766 /* Let's see if we have a change as in some scenarios servers report
3767 non-changes of properties. */
3768 becomes_symlink = (value != NULL);
3769
3770 if (fb->adding_file)
3771 was_symlink = becomes_symlink; /* No change */
3772 else
3773 {
3774 apr_hash_t *props;
3775
3776 /* We read the server-props, not the ACTUAL props here as we just
3777 want to see if this is really an incoming prop change. */
3778 SVN_ERR(svn_wc__db_base_get_props(&props, eb->db,
3779 fb->local_abspath,
3780 scratch_pool, scratch_pool));
3781
3782 was_symlink = ((props
3783 && svn_hash_gets(props, SVN_PROP_SPECIAL) != NULL)
3784 ? svn_tristate_true
3785 : svn_tristate_false);
3786 }
3787
3788 if (was_symlink != becomes_symlink)
3789 {
3790 /* If the local node was not modified, we continue as usual, if
3791 modified we want a tree conflict just like how we would handle
3792 it when receiving a delete + add (aka "replace") */
3793 if (fb->local_prop_mods)
3794 modified = TRUE;
3795 else
3796 SVN_ERR(svn_wc__internal_file_modified_p(&modified, eb->db,
3797 fb->local_abspath,
3798 FALSE, scratch_pool));
3799 }
3800
3801 if (modified)
3802 {
3803 if (!fb->edit_conflict)
3804 fb->edit_conflict = svn_wc__conflict_skel_create(fb->pool);
3805
3806 SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
3807 fb->edit_conflict,
3808 eb->db, fb->local_abspath,
3809 svn_wc_conflict_reason_edited,
3810 svn_wc_conflict_action_replace,
3811 NULL, NULL,
3812 fb->pool, scratch_pool));
3813
3814 SVN_ERR(complete_conflict(fb->edit_conflict, fb->edit_baton,
3815 fb->local_abspath, fb->old_repos_relpath,
3816 fb->old_revision, fb->new_repos_relpath,
3817 svn_node_file, svn_node_file,
3818 NULL, fb->pool, scratch_pool));
3819
3820 /* Create a copy of the existing (pre update) BASE node in WORKING,
3821 mark a tree conflict and handle the rest of the update as
3822 shadowed */
3823 SVN_ERR(svn_wc__db_op_make_copy(eb->db, fb->local_abspath,
3824 fb->edit_conflict, NULL,
3825 scratch_pool));
3826
3827 do_notification(eb, fb->local_abspath, svn_node_file,
3828 svn_wc_notify_tree_conflict, scratch_pool);
3829
3830 /* Ok, we introduced a replacement, so we can now handle the rest
3831 as a normal shadowed update */
3832 fb->shadowed = TRUE;
3833 fb->add_existed = FALSE;
3834 fb->already_notified = TRUE;
3835 }
3836 }
3837
3838 return SVN_NO_ERROR;
3839 }
3840
3841 /* Perform the actual merge of file changes between an original file,
3842 identified by ORIGINAL_CHECKSUM (an empty file if NULL) to a new file
3843 identified by NEW_CHECKSUM.
3844
3845 Merge the result into LOCAL_ABSPATH, which is part of the working copy
3846 identified by WRI_ABSPATH. Use OLD_REVISION and TARGET_REVISION for naming
3847 the intermediate files.
3848
3849 The rest of the arguments are passed to svn_wc__internal_merge().
3850 */
3851 svn_error_t *
svn_wc__perform_file_merge(svn_skel_t ** work_items,svn_skel_t ** conflict_skel,svn_boolean_t * found_conflict,svn_wc__db_t * db,const char * local_abspath,const char * wri_abspath,const svn_checksum_t * new_checksum,const svn_checksum_t * original_checksum,apr_hash_t * old_actual_props,const apr_array_header_t * ext_patterns,svn_revnum_t old_revision,svn_revnum_t target_revision,const apr_array_header_t * propchanges,const char * diff3_cmd,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * result_pool,apr_pool_t * scratch_pool)3852 svn_wc__perform_file_merge(svn_skel_t **work_items,
3853 svn_skel_t **conflict_skel,
3854 svn_boolean_t *found_conflict,
3855 svn_wc__db_t *db,
3856 const char *local_abspath,
3857 const char *wri_abspath,
3858 const svn_checksum_t *new_checksum,
3859 const svn_checksum_t *original_checksum,
3860 apr_hash_t *old_actual_props,
3861 const apr_array_header_t *ext_patterns,
3862 svn_revnum_t old_revision,
3863 svn_revnum_t target_revision,
3864 const apr_array_header_t *propchanges,
3865 const char *diff3_cmd,
3866 svn_cancel_func_t cancel_func,
3867 void *cancel_baton,
3868 apr_pool_t *result_pool,
3869 apr_pool_t *scratch_pool)
3870 {
3871 /* Actual file exists and has local mods:
3872 Now we need to let loose svn_wc__internal_merge() to merge
3873 the textual changes into the working file. */
3874 const char *oldrev_str, *newrev_str, *mine_str;
3875 const char *merge_left;
3876 svn_boolean_t delete_left = FALSE;
3877 const char *path_ext = "";
3878 const char *new_pristine_abspath;
3879 enum svn_wc_merge_outcome_t merge_outcome = svn_wc_merge_unchanged;
3880 svn_skel_t *work_item;
3881
3882 *work_items = NULL;
3883
3884 SVN_ERR(svn_wc__db_pristine_get_path(&new_pristine_abspath,
3885 db, wri_abspath, new_checksum,
3886 scratch_pool, scratch_pool));
3887
3888 /* If we have any file extensions we're supposed to
3889 preserve in generated conflict file names, then find
3890 this path's extension. But then, if it isn't one of
3891 the ones we want to keep in conflict filenames,
3892 pretend it doesn't have an extension at all. */
3893 if (ext_patterns && ext_patterns->nelts)
3894 {
3895 svn_path_splitext(NULL, &path_ext, local_abspath, scratch_pool);
3896 if (! (*path_ext && svn_cstring_match_glob_list(path_ext, ext_patterns)))
3897 path_ext = "";
3898 }
3899
3900 /* old_revision can be invalid when the conflict is against a
3901 local addition */
3902 if (!SVN_IS_VALID_REVNUM(old_revision))
3903 old_revision = 0;
3904
3905 oldrev_str = apr_psprintf(scratch_pool, ".r%ld%s%s",
3906 old_revision,
3907 *path_ext ? "." : "",
3908 *path_ext ? path_ext : "");
3909
3910 newrev_str = apr_psprintf(scratch_pool, ".r%ld%s%s",
3911 target_revision,
3912 *path_ext ? "." : "",
3913 *path_ext ? path_ext : "");
3914 mine_str = apr_psprintf(scratch_pool, ".mine%s%s",
3915 *path_ext ? "." : "",
3916 *path_ext ? path_ext : "");
3917
3918 if (! original_checksum)
3919 {
3920 SVN_ERR(get_empty_tmp_file(&merge_left, db, wri_abspath,
3921 result_pool, scratch_pool));
3922 delete_left = TRUE;
3923 }
3924 else
3925 SVN_ERR(svn_wc__db_pristine_get_path(&merge_left, db, wri_abspath,
3926 original_checksum,
3927 result_pool, scratch_pool));
3928
3929 /* Merge the changes from the old textbase to the new
3930 textbase into the file we're updating.
3931 Remember that this function wants full paths! */
3932 SVN_ERR(svn_wc__internal_merge(&work_item,
3933 conflict_skel,
3934 &merge_outcome,
3935 db,
3936 merge_left,
3937 new_pristine_abspath,
3938 local_abspath,
3939 wri_abspath,
3940 oldrev_str, newrev_str, mine_str,
3941 old_actual_props,
3942 FALSE /* dry_run */,
3943 diff3_cmd, NULL, propchanges,
3944 cancel_func, cancel_baton,
3945 result_pool, scratch_pool));
3946
3947 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
3948 *found_conflict = (merge_outcome == svn_wc_merge_conflict);
3949
3950 /* If we created a temporary left merge file, get rid of it. */
3951 if (delete_left)
3952 {
3953 SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, wri_abspath,
3954 merge_left,
3955 result_pool, scratch_pool));
3956 *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
3957 }
3958
3959 return SVN_NO_ERROR;
3960 }
3961
3962 /* This is the small planet. It has the complex responsibility of
3963 * "integrating" a new revision of a file into a working copy.
3964 *
3965 * Given a file_baton FB for a file either already under version control, or
3966 * prepared (see below) to join version control, fully install a
3967 * new revision of the file.
3968 *
3969 * ### transitional: installation of the working file will be handled
3970 * ### by the *INSTALL_PRISTINE flag.
3971 *
3972 * By "install", we mean: create a new text-base and prop-base, merge
3973 * any textual and property changes into the working file, and finally
3974 * update all metadata so that the working copy believes it has a new
3975 * working revision of the file. All of this work includes being
3976 * sensitive to eol translation, keyword substitution, and performing
3977 * all actions accumulated the parent directory's work queue.
3978 *
3979 * Set *CONTENT_STATE to the state of the contents after the
3980 * installation.
3981 *
3982 * Return values are allocated in RESULT_POOL and temporary allocations
3983 * are performed in SCRATCH_POOL.
3984 */
3985 static svn_error_t *
merge_file(svn_skel_t ** work_items,svn_skel_t ** conflict_skel,svn_boolean_t * install_pristine,const char ** install_from,svn_wc_notify_state_t * content_state,struct file_baton * fb,apr_hash_t * actual_props,apr_time_t last_changed_date,apr_pool_t * result_pool,apr_pool_t * scratch_pool)3986 merge_file(svn_skel_t **work_items,
3987 svn_skel_t **conflict_skel,
3988 svn_boolean_t *install_pristine,
3989 const char **install_from,
3990 svn_wc_notify_state_t *content_state,
3991 struct file_baton *fb,
3992 apr_hash_t *actual_props,
3993 apr_time_t last_changed_date,
3994 apr_pool_t *result_pool,
3995 apr_pool_t *scratch_pool)
3996 {
3997 struct edit_baton *eb = fb->edit_baton;
3998 struct dir_baton *pb = fb->dir_baton;
3999 svn_boolean_t is_locally_modified;
4000 svn_boolean_t found_text_conflict = FALSE;
4001
4002 SVN_ERR_ASSERT(! fb->shadowed
4003 && ! fb->obstruction_found
4004 && ! fb->edit_obstructed);
4005
4006 /*
4007 When this function is called on file F, we assume the following
4008 things are true:
4009
4010 - The new pristine text of F is present in the pristine store
4011 iff FB->NEW_TEXT_BASE_SHA1_CHECKSUM is not NULL.
4012
4013 - The WC metadata still reflects the old version of F.
4014 (We can still access the old pristine base text of F.)
4015
4016 The goal is to update the local working copy of F to reflect
4017 the changes received from the repository, preserving any local
4018 modifications.
4019 */
4020
4021 *work_items = NULL;
4022 *install_pristine = FALSE;
4023 *install_from = NULL;
4024
4025 /* Start by splitting the file path, getting an access baton for the parent,
4026 and an entry for the file if any. */
4027
4028 /* Has the user made local mods to the working file?
4029 Note that this compares to the current pristine file, which is
4030 different from fb->old_text_base_path if we have a replaced-with-history
4031 file. However, in the case we had an obstruction, we check against the
4032 new text base.
4033 */
4034 if (fb->adding_file && !fb->add_existed)
4035 {
4036 is_locally_modified = FALSE; /* There is no file: Don't check */
4037 }
4038 else
4039 {
4040 /* The working file is not an obstruction.
4041 So: is the file modified, relative to its ORIGINAL pristine?
4042
4043 This function sets is_locally_modified to FALSE for
4044 files that do not exist and for directories. */
4045
4046 SVN_ERR(svn_wc__internal_file_modified_p(&is_locally_modified,
4047 eb->db, fb->local_abspath,
4048 FALSE /* exact_comparison */,
4049 scratch_pool));
4050 }
4051
4052 /* For 'textual' merging, we use the following system:
4053
4054 When a file is modified and we have a new BASE:
4055 - For text files
4056 * svn_wc_merge uses diff3
4057 * possibly makes backups and marks files as conflicted.
4058
4059 - For binary files
4060 * svn_wc_merge makes backups and marks files as conflicted.
4061
4062 If a file is not modified and we have a new BASE:
4063 * Install from pristine.
4064
4065 If we have property changes related to magic properties or if the
4066 svn:keywords property is set:
4067 * Retranslate from the working file.
4068 */
4069 if (! is_locally_modified
4070 && fb->new_text_base_sha1_checksum)
4071 {
4072 /* If there are no local mods, who cares whether it's a text
4073 or binary file! Just write a log command to overwrite
4074 any working file with the new text-base. If newline
4075 conversion or keyword substitution is activated, this
4076 will happen as well during the copy.
4077 For replaced files, though, we want to merge in the changes
4078 even if the file is not modified compared to the (non-revert)
4079 text-base. */
4080
4081 *install_pristine = TRUE;
4082 }
4083 else if (fb->new_text_base_sha1_checksum)
4084 {
4085 /* Actual file exists and has local mods:
4086 Now we need to let loose svn_wc__merge_internal() to merge
4087 the textual changes into the working file. */
4088 SVN_ERR(svn_wc__perform_file_merge(work_items,
4089 conflict_skel,
4090 &found_text_conflict,
4091 eb->db,
4092 fb->local_abspath,
4093 pb->local_abspath,
4094 fb->new_text_base_sha1_checksum,
4095 fb->add_existed
4096 ? NULL
4097 : fb->original_checksum,
4098 actual_props,
4099 eb->ext_patterns,
4100 fb->old_revision,
4101 *eb->target_revision,
4102 fb->propchanges,
4103 eb->diff3_cmd,
4104 eb->cancel_func, eb->cancel_baton,
4105 result_pool, scratch_pool));
4106 } /* end: working file exists and has mods */
4107 else
4108 {
4109 /* There is no new text base, but let's see if the working file needs
4110 to be updated for any other reason. */
4111
4112 apr_hash_t *keywords;
4113
4114 /* Determine if any of the propchanges are the "magic" ones that
4115 might require changing the working file. */
4116 svn_boolean_t magic_props_changed;
4117
4118 magic_props_changed = svn_wc__has_magic_property(fb->propchanges);
4119
4120 SVN_ERR(svn_wc__get_translate_info(NULL, NULL,
4121 &keywords,
4122 NULL,
4123 eb->db, fb->local_abspath,
4124 actual_props, TRUE,
4125 scratch_pool, scratch_pool));
4126 if (magic_props_changed || keywords)
4127 {
4128 /* Special edge-case: it's possible that this file installation
4129 only involves propchanges, but that some of those props still
4130 require a retranslation of the working file.
4131
4132 OR that the file doesn't involve propchanges which by themselves
4133 require retranslation, but receiving a change bumps the revision
4134 number which requires re-expansion of keywords... */
4135
4136 if (is_locally_modified)
4137 {
4138 const char *tmptext;
4139
4140 /* Copy and DEtranslate the working file to a temp text-base.
4141 Note that detranslation is done according to the old props. */
4142 SVN_ERR(svn_wc__internal_translated_file(
4143 &tmptext, fb->local_abspath, eb->db, fb->local_abspath,
4144 SVN_WC_TRANSLATE_TO_NF
4145 | SVN_WC_TRANSLATE_NO_OUTPUT_CLEANUP,
4146 eb->cancel_func, eb->cancel_baton,
4147 result_pool, scratch_pool));
4148
4149 /* We always want to reinstall the working file if the magic
4150 properties have changed, or there are any keywords present.
4151 Note that TMPTEXT might actually refer to the working file
4152 itself (the above function skips a detranslate when not
4153 required). This is acceptable, as we will (re)translate
4154 according to the new properties into a temporary file (from
4155 the working file), and then rename the temp into place. Magic!
4156 */
4157 *install_pristine = TRUE;
4158 *install_from = tmptext;
4159 }
4160 else
4161 {
4162 /* Use our existing 'copy' from the pristine store instead
4163 of making a new copy. This way we can use the standard code
4164 to update the recorded size and modification time.
4165 (Issue #3842) */
4166 *install_pristine = TRUE;
4167 }
4168 }
4169 }
4170
4171 /* Set the returned content state. */
4172
4173 if (found_text_conflict)
4174 *content_state = svn_wc_notify_state_conflicted;
4175 else if (fb->new_text_base_sha1_checksum)
4176 {
4177 if (is_locally_modified)
4178 *content_state = svn_wc_notify_state_merged;
4179 else
4180 *content_state = svn_wc_notify_state_changed;
4181 }
4182 else
4183 *content_state = svn_wc_notify_state_unchanged;
4184
4185 return SVN_NO_ERROR;
4186 }
4187
4188
4189 /* An svn_delta_editor_t function. */
4190 /* Mostly a wrapper around merge_file. */
4191 static svn_error_t *
close_file(void * file_baton,const char * expected_md5_digest,apr_pool_t * pool)4192 close_file(void *file_baton,
4193 const char *expected_md5_digest,
4194 apr_pool_t *pool)
4195 {
4196 struct file_baton *fb = file_baton;
4197 struct dir_baton *pdb = fb->dir_baton;
4198 struct edit_baton *eb = fb->edit_baton;
4199 svn_wc_notify_state_t content_state, prop_state;
4200 svn_wc_notify_lock_state_t lock_state;
4201 svn_checksum_t *expected_md5_checksum = NULL;
4202 apr_hash_t *new_base_props = NULL;
4203 apr_hash_t *new_actual_props = NULL;
4204 apr_array_header_t *entry_prop_changes;
4205 apr_array_header_t *dav_prop_changes;
4206 apr_array_header_t *regular_prop_changes;
4207 apr_hash_t *current_base_props = NULL;
4208 apr_hash_t *current_actual_props = NULL;
4209 apr_hash_t *local_actual_props = NULL;
4210 svn_skel_t *all_work_items = NULL;
4211 svn_skel_t *conflict_skel = NULL;
4212 svn_skel_t *work_item;
4213 apr_pool_t *scratch_pool = fb->pool; /* Destroyed at function exit */
4214 svn_boolean_t keep_recorded_info = FALSE;
4215 const svn_checksum_t *new_checksum;
4216 apr_array_header_t *iprops = NULL;
4217
4218 if (fb->skip_this)
4219 {
4220 svn_pool_destroy(fb->pool);
4221 SVN_ERR(maybe_release_dir_info(pdb));
4222 return SVN_NO_ERROR;
4223 }
4224
4225 if (fb->edited)
4226 conflict_skel = fb->edit_conflict;
4227
4228 if (expected_md5_digest)
4229 SVN_ERR(svn_checksum_parse_hex(&expected_md5_checksum, svn_checksum_md5,
4230 expected_md5_digest, scratch_pool));
4231
4232 if (fb->new_text_base_md5_checksum && expected_md5_checksum
4233 && !svn_checksum_match(expected_md5_checksum,
4234 fb->new_text_base_md5_checksum))
4235 return svn_error_trace(
4236 svn_checksum_mismatch_err(expected_md5_checksum,
4237 fb->new_text_base_md5_checksum,
4238 scratch_pool,
4239 _("Checksum mismatch for '%s'"),
4240 svn_dirent_local_style(
4241 fb->local_abspath, pool)));
4242
4243 /* Gather the changes for each kind of property. */
4244 SVN_ERR(svn_categorize_props(fb->propchanges, &entry_prop_changes,
4245 &dav_prop_changes, ®ular_prop_changes,
4246 scratch_pool));
4247
4248 /* Extract the changed_* and lock state information. */
4249 {
4250 svn_revnum_t new_changed_rev;
4251 apr_time_t new_changed_date;
4252 const char *new_changed_author;
4253
4254 SVN_ERR(accumulate_last_change(&new_changed_rev,
4255 &new_changed_date,
4256 &new_changed_author,
4257 entry_prop_changes,
4258 scratch_pool, scratch_pool));
4259
4260 if (SVN_IS_VALID_REVNUM(new_changed_rev))
4261 fb->changed_rev = new_changed_rev;
4262 if (new_changed_date != 0)
4263 fb->changed_date = new_changed_date;
4264 if (new_changed_author != NULL)
4265 fb->changed_author = new_changed_author;
4266 }
4267
4268 /* Determine whether the file has become unlocked. */
4269 {
4270 int i;
4271
4272 lock_state = svn_wc_notify_lock_state_unchanged;
4273
4274 for (i = 0; i < entry_prop_changes->nelts; ++i)
4275 {
4276 const svn_prop_t *prop
4277 = &APR_ARRAY_IDX(entry_prop_changes, i, svn_prop_t);
4278
4279 /* If we see a change to the LOCK_TOKEN entry prop, then the only
4280 possible change is its REMOVAL. Thus, the lock has been removed,
4281 and we should likewise remove our cached copy of it. */
4282 if (! strcmp(prop->name, SVN_PROP_ENTRY_LOCK_TOKEN))
4283 {
4284 /* If we lose the lock, but not because we are switching to
4285 another url, remove the state lock from the wc */
4286 if (! eb->switch_repos_relpath
4287 || strcmp(fb->new_repos_relpath, fb->old_repos_relpath) == 0)
4288 {
4289 SVN_ERR_ASSERT(prop->value == NULL);
4290 SVN_ERR(svn_wc__db_lock_remove(eb->db, fb->local_abspath, NULL,
4291 scratch_pool));
4292
4293 lock_state = svn_wc_notify_lock_state_unlocked;
4294 }
4295 break;
4296 }
4297 }
4298 }
4299
4300 /* Install all kinds of properties. It is important to do this before
4301 any file content merging, since that process might expand keywords, in
4302 which case we want the new entryprops to be in place. */
4303
4304 /* Write log commands to merge REGULAR_PROPS into the existing
4305 properties of FB->LOCAL_ABSPATH. Update *PROP_STATE to reflect
4306 the result of the regular prop merge.
4307
4308 BASE_PROPS and WORKING_PROPS are hashes of the base and
4309 working props of the file; if NULL they are read from the wc. */
4310
4311 /* ### some of this feels like voodoo... */
4312
4313 if ((!fb->adding_file || fb->add_existed)
4314 && !fb->shadowed)
4315 SVN_ERR(svn_wc__get_actual_props(&local_actual_props,
4316 eb->db, fb->local_abspath,
4317 scratch_pool, scratch_pool));
4318 if (local_actual_props == NULL)
4319 local_actual_props = apr_hash_make(scratch_pool);
4320
4321 if (fb->add_existed)
4322 {
4323 /* This node already exists. Grab the current pristine properties. */
4324 SVN_ERR(svn_wc__db_read_pristine_props(¤t_base_props,
4325 eb->db, fb->local_abspath,
4326 scratch_pool, scratch_pool));
4327 current_actual_props = local_actual_props;
4328 }
4329 else if (!fb->adding_file)
4330 {
4331 /* Get the BASE properties for proper merging. */
4332 SVN_ERR(svn_wc__db_base_get_props(¤t_base_props,
4333 eb->db, fb->local_abspath,
4334 scratch_pool, scratch_pool));
4335 current_actual_props = local_actual_props;
4336 }
4337
4338 /* Note: even if the node existed before, it may not have
4339 pristine props (e.g a local-add) */
4340 if (current_base_props == NULL)
4341 current_base_props = apr_hash_make(scratch_pool);
4342
4343 /* And new nodes need an empty set of ACTUAL props. */
4344 if (current_actual_props == NULL)
4345 current_actual_props = apr_hash_make(scratch_pool);
4346
4347 prop_state = svn_wc_notify_state_unknown;
4348
4349 if (! fb->shadowed)
4350 {
4351 svn_boolean_t install_pristine;
4352 const char *install_from = NULL;
4353
4354 /* Merge the 'regular' props into the existing working proplist. */
4355 /* This will merge the old and new props into a new prop db, and
4356 write <cp> commands to the logfile to install the merged
4357 props. */
4358 new_base_props = svn_prop__patch(current_base_props, regular_prop_changes,
4359 scratch_pool);
4360 SVN_ERR(svn_wc__merge_props(&conflict_skel,
4361 &prop_state,
4362 &new_actual_props,
4363 eb->db,
4364 fb->local_abspath,
4365 NULL /* server_baseprops (update, not merge) */,
4366 current_base_props,
4367 current_actual_props,
4368 regular_prop_changes, /* propchanges */
4369 scratch_pool,
4370 scratch_pool));
4371 /* We will ALWAYS have properties to save (after a not-dry-run merge). */
4372 SVN_ERR_ASSERT(new_base_props != NULL && new_actual_props != NULL);
4373
4374 /* Merge the text. This will queue some additional work. */
4375 if (!fb->obstruction_found && !fb->edit_obstructed)
4376 {
4377 svn_error_t *err;
4378 err = merge_file(&work_item, &conflict_skel,
4379 &install_pristine, &install_from,
4380 &content_state, fb, current_actual_props,
4381 fb->changed_date, scratch_pool, scratch_pool);
4382
4383 if (err && err->apr_err == SVN_ERR_WC_PATH_ACCESS_DENIED)
4384 {
4385 if (eb->notify_func)
4386 {
4387 svn_wc_notify_t *notify =svn_wc_create_notify(
4388 fb->local_abspath,
4389 svn_wc_notify_update_skip_access_denied,
4390 scratch_pool);
4391
4392 notify->kind = svn_node_file;
4393 notify->err = err;
4394
4395 eb->notify_func(eb->notify_baton, notify, scratch_pool);
4396 }
4397 svn_error_clear(err);
4398
4399 SVN_ERR(remember_skipped_tree(eb, fb->local_abspath,
4400 scratch_pool));
4401 fb->skip_this = TRUE;
4402
4403 svn_pool_destroy(fb->pool);
4404 SVN_ERR(maybe_release_dir_info(pdb));
4405 return SVN_NO_ERROR;
4406 }
4407 else
4408 SVN_ERR(err);
4409
4410 all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4411 scratch_pool);
4412 }
4413 else
4414 {
4415 install_pristine = FALSE;
4416 if (fb->new_text_base_sha1_checksum)
4417 content_state = svn_wc_notify_state_changed;
4418 else
4419 content_state = svn_wc_notify_state_unchanged;
4420 }
4421
4422 if (install_pristine)
4423 {
4424 svn_boolean_t record_fileinfo;
4425
4426 /* If we are installing from the pristine contents, then go ahead and
4427 record the fileinfo. That will be the "proper" values. Installing
4428 from some random file means the fileinfo does NOT correspond to
4429 the pristine (in which case, the fileinfo will be cleared for
4430 safety's sake). */
4431 record_fileinfo = (install_from == NULL);
4432
4433 SVN_ERR(svn_wc__wq_build_file_install(&work_item,
4434 eb->db,
4435 fb->local_abspath,
4436 install_from,
4437 eb->use_commit_times,
4438 record_fileinfo,
4439 scratch_pool, scratch_pool));
4440 all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4441 scratch_pool);
4442 }
4443 else if (lock_state == svn_wc_notify_lock_state_unlocked
4444 && !fb->obstruction_found)
4445 {
4446 /* If a lock was removed and we didn't update the text contents, we
4447 might need to set the file read-only.
4448
4449 Note: this will also update the executable flag, but ... meh. */
4450 SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, eb->db,
4451 fb->local_abspath,
4452 scratch_pool, scratch_pool));
4453 all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4454 scratch_pool);
4455 }
4456
4457 if (! install_pristine
4458 && (content_state == svn_wc_notify_state_unchanged))
4459 {
4460 /* It is safe to keep the current recorded timestamp and size */
4461 keep_recorded_info = TRUE;
4462 }
4463
4464 /* Clean up any temporary files. */
4465
4466 /* Remove the INSTALL_FROM file, as long as it doesn't refer to the
4467 working file. */
4468 if (install_from != NULL
4469 && strcmp(install_from, fb->local_abspath) != 0)
4470 {
4471 SVN_ERR(svn_wc__wq_build_file_remove(&work_item, eb->db,
4472 fb->local_abspath, install_from,
4473 scratch_pool, scratch_pool));
4474 all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4475 scratch_pool);
4476 }
4477 }
4478 else
4479 {
4480 /* Adding or updating a BASE node under a locally added node. */
4481 apr_hash_t *fake_actual_props;
4482
4483 if (fb->adding_file)
4484 fake_actual_props = apr_hash_make(scratch_pool);
4485 else
4486 fake_actual_props = current_base_props;
4487
4488 /* Store the incoming props (sent as propchanges) in new_base_props
4489 and create a set of new actual props to use for notifications */
4490 new_base_props = svn_prop__patch(current_base_props, regular_prop_changes,
4491 scratch_pool);
4492 SVN_ERR(svn_wc__merge_props(&conflict_skel,
4493 &prop_state,
4494 &new_actual_props,
4495 eb->db,
4496 fb->local_abspath,
4497 NULL /* server_baseprops (not merging) */,
4498 current_base_props /* pristine_props */,
4499 fake_actual_props /* actual_props */,
4500 regular_prop_changes, /* propchanges */
4501 scratch_pool,
4502 scratch_pool));
4503
4504 if (fb->new_text_base_sha1_checksum)
4505 content_state = svn_wc_notify_state_changed;
4506 else
4507 content_state = svn_wc_notify_state_unchanged;
4508 }
4509
4510 /* Insert/replace the BASE node with all of the new metadata. */
4511
4512 /* Set the 'checksum' column of the file's BASE_NODE row to
4513 * NEW_TEXT_BASE_SHA1_CHECKSUM. The pristine text identified by that
4514 * checksum is already in the pristine store. */
4515 new_checksum = fb->new_text_base_sha1_checksum;
4516
4517 /* If we don't have a NEW checksum, then the base must not have changed.
4518 Just carry over the old checksum. */
4519 if (new_checksum == NULL)
4520 new_checksum = fb->original_checksum;
4521
4522 if (conflict_skel)
4523 {
4524 SVN_ERR(complete_conflict(conflict_skel,
4525 fb->edit_baton,
4526 fb->local_abspath,
4527 fb->old_repos_relpath,
4528 fb->old_revision,
4529 fb->new_repos_relpath,
4530 svn_node_file, svn_node_file,
4531 fb->dir_baton->deletion_conflicts
4532 ? svn_hash_gets(
4533 fb->dir_baton->deletion_conflicts,
4534 fb->name)
4535 : NULL,
4536 fb->pool, scratch_pool));
4537
4538 SVN_ERR(svn_wc__conflict_create_markers(&work_item,
4539 eb->db, fb->local_abspath,
4540 conflict_skel,
4541 scratch_pool, scratch_pool));
4542
4543 all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4544 scratch_pool);
4545 }
4546
4547 /* Any inherited props to be set set for this base node? */
4548 if (eb->wcroot_iprops)
4549 {
4550 iprops = svn_hash_gets(eb->wcroot_iprops, fb->local_abspath);
4551
4552 /* close_edit may also update iprops for switched nodes, catching
4553 those for which close_directory is never called (e.g. a switch
4554 with no changes). So as a minor optimization we remove any
4555 iprops from the hash so as not to set them again in
4556 close_edit. */
4557 if (iprops)
4558 svn_hash_sets(eb->wcroot_iprops, fb->local_abspath, NULL);
4559 }
4560
4561 SVN_ERR(svn_wc__db_base_add_file(eb->db, fb->local_abspath,
4562 eb->wcroot_abspath,
4563 fb->new_repos_relpath,
4564 eb->repos_root, eb->repos_uuid,
4565 *eb->target_revision,
4566 new_base_props,
4567 fb->changed_rev,
4568 fb->changed_date,
4569 fb->changed_author,
4570 new_checksum,
4571 (dav_prop_changes->nelts > 0)
4572 ? svn_prop_array_to_hash(
4573 dav_prop_changes,
4574 scratch_pool)
4575 : NULL,
4576 (fb->add_existed && fb->adding_file),
4577 (! fb->shadowed) && new_base_props,
4578 new_actual_props,
4579 iprops,
4580 keep_recorded_info,
4581 (fb->shadowed && fb->obstruction_found),
4582 conflict_skel,
4583 all_work_items,
4584 scratch_pool));
4585
4586 if (conflict_skel && eb->conflict_func)
4587 SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, fb->local_abspath,
4588 svn_node_file,
4589 conflict_skel,
4590 NULL /* merge_options */,
4591 eb->conflict_func,
4592 eb->conflict_baton,
4593 eb->cancel_func,
4594 eb->cancel_baton,
4595 scratch_pool));
4596
4597 /* Deal with the WORKING tree, based on updates to the BASE tree. */
4598
4599 svn_hash_sets(fb->dir_baton->not_present_nodes, fb->name, NULL);
4600
4601 /* Send a notification to the callback function. (Skip notifications
4602 about files which were already notified for another reason.) */
4603 if (eb->notify_func && !fb->already_notified
4604 && (fb->edited || lock_state == svn_wc_notify_lock_state_unlocked))
4605 {
4606 svn_wc_notify_t *notify;
4607 svn_wc_notify_action_t action = svn_wc_notify_update_update;
4608
4609 if (fb->edited)
4610 {
4611 if (fb->shadowed || fb->edit_obstructed)
4612 action = fb->adding_file
4613 ? svn_wc_notify_update_shadowed_add
4614 : svn_wc_notify_update_shadowed_update;
4615 else if (fb->obstruction_found || fb->add_existed)
4616 {
4617 if (content_state != svn_wc_notify_state_conflicted)
4618 action = svn_wc_notify_exists;
4619 }
4620 else if (fb->adding_file)
4621 {
4622 action = svn_wc_notify_update_add;
4623 }
4624 }
4625 else
4626 {
4627 SVN_ERR_ASSERT(lock_state == svn_wc_notify_lock_state_unlocked);
4628 action = svn_wc_notify_update_broken_lock;
4629 }
4630
4631 /* If the file was moved-away, notify for the moved-away node.
4632 * The original location only had its BASE info changed and
4633 * we don't usually notify about such changes. */
4634 notify = svn_wc_create_notify(fb->local_abspath, action, scratch_pool);
4635 notify->kind = svn_node_file;
4636 notify->content_state = content_state;
4637 notify->prop_state = prop_state;
4638 notify->lock_state = lock_state;
4639 notify->revision = *eb->target_revision;
4640 notify->old_revision = fb->old_revision;
4641
4642 /* Fetch the mimetype from the actual properties */
4643 notify->mime_type = svn_prop_get_value(new_actual_props,
4644 SVN_PROP_MIME_TYPE);
4645
4646 eb->notify_func(eb->notify_baton, notify, scratch_pool);
4647 }
4648
4649 svn_pool_destroy(fb->pool); /* Destroy scratch_pool */
4650
4651 /* We have one less referrer to the directory */
4652 SVN_ERR(maybe_release_dir_info(pdb));
4653
4654 return SVN_NO_ERROR;
4655 }
4656
4657
4658 /* Implements svn_wc__proplist_receiver_t.
4659 * Check for the presence of an svn:keywords property and queues an install_file
4660 * work queue item if present. Thus, when the work queue is run to complete the
4661 * switch operation, all files with keywords will go through the translation
4662 * process so URLs etc are updated. */
4663 static svn_error_t *
update_keywords_after_switch_cb(void * baton,const char * local_abspath,apr_hash_t * props,apr_pool_t * scratch_pool)4664 update_keywords_after_switch_cb(void *baton,
4665 const char *local_abspath,
4666 apr_hash_t *props,
4667 apr_pool_t *scratch_pool)
4668 {
4669 struct edit_baton *eb = baton;
4670 svn_string_t *propval;
4671 svn_boolean_t modified;
4672 svn_boolean_t record_fileinfo;
4673 svn_skel_t *work_items;
4674 const char *install_from;
4675
4676 propval = svn_hash_gets(props, SVN_PROP_KEYWORDS);
4677 if (!propval)
4678 return SVN_NO_ERROR;
4679
4680 SVN_ERR(svn_wc__internal_file_modified_p(&modified, eb->db,
4681 local_abspath, FALSE,
4682 scratch_pool));
4683 if (modified)
4684 {
4685 const char *temp_dir_abspath;
4686 svn_stream_t *working_stream;
4687 svn_stream_t *install_from_stream;
4688
4689 SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, eb->db,
4690 local_abspath, scratch_pool,
4691 scratch_pool));
4692 SVN_ERR(svn_stream_open_readonly(&working_stream, local_abspath,
4693 scratch_pool, scratch_pool));
4694 SVN_ERR(svn_stream_open_unique(&install_from_stream, &install_from,
4695 temp_dir_abspath, svn_io_file_del_none,
4696 scratch_pool, scratch_pool));
4697 SVN_ERR(svn_stream_copy3(working_stream, install_from_stream,
4698 eb->cancel_func, eb->cancel_baton,
4699 scratch_pool));
4700 record_fileinfo = FALSE;
4701 }
4702 else
4703 {
4704 install_from = NULL;
4705 record_fileinfo = TRUE;
4706 }
4707
4708 SVN_ERR(svn_wc__wq_build_file_install(&work_items, eb->db, local_abspath,
4709 install_from,
4710 eb->use_commit_times,
4711 record_fileinfo,
4712 scratch_pool, scratch_pool));
4713 if (install_from)
4714 {
4715 svn_skel_t *work_item;
4716
4717 SVN_ERR(svn_wc__wq_build_file_remove(&work_item, eb->db,
4718 local_abspath, install_from,
4719 scratch_pool, scratch_pool));
4720 work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
4721 }
4722
4723 SVN_ERR(svn_wc__db_wq_add(eb->db, local_abspath, work_items,
4724 scratch_pool));
4725
4726 return SVN_NO_ERROR;
4727 }
4728
4729
4730 /* An svn_delta_editor_t function. */
4731 static svn_error_t *
close_edit(void * edit_baton,apr_pool_t * pool)4732 close_edit(void *edit_baton,
4733 apr_pool_t *pool)
4734 {
4735 struct edit_baton *eb = edit_baton;
4736 apr_pool_t *scratch_pool = eb->pool;
4737
4738 /* The editor didn't even open the root; we have to take care of
4739 some cleanup stuffs. */
4740 if (! eb->root_opened
4741 && *eb->target_basename == '\0')
4742 {
4743 /* We need to "un-incomplete" the root directory. */
4744 SVN_ERR(svn_wc__db_temp_op_end_directory_update(eb->db,
4745 eb->anchor_abspath,
4746 scratch_pool));
4747 }
4748
4749 /* By definition, anybody "driving" this editor for update or switch
4750 purposes at a *minimum* must have called set_target_revision() at
4751 the outset, and close_edit() at the end -- even if it turned out
4752 that no changes ever had to be made, and open_root() was never
4753 called. That's fine. But regardless, when the edit is over,
4754 this editor needs to make sure that *all* paths have had their
4755 revisions bumped to the new target revision. */
4756
4757 /* Make sure our update target now has the new working revision.
4758 Also, if this was an 'svn switch', then rewrite the target's
4759 url. All of this tweaking might happen recursively! Note
4760 that if eb->target is NULL, that's okay (albeit "sneaky",
4761 some might say). */
4762
4763 /* Extra check: if the update did nothing but make its target
4764 'deleted', then do *not* run cleanup on the target, as it
4765 will only remove the deleted entry! */
4766 if (! eb->target_deleted)
4767 {
4768 SVN_ERR(svn_wc__db_op_bump_revisions_post_update(eb->db,
4769 eb->target_abspath,
4770 eb->requested_depth,
4771 eb->switch_repos_relpath,
4772 eb->repos_root,
4773 eb->repos_uuid,
4774 *(eb->target_revision),
4775 eb->skipped_trees,
4776 eb->wcroot_iprops,
4777 ! eb->edited,
4778 eb->notify_func,
4779 eb->notify_baton,
4780 eb->pool));
4781
4782 if (*eb->target_basename != '\0')
4783 {
4784 svn_wc__db_status_t status;
4785 svn_error_t *err;
4786
4787 /* Note: we are fetching information about the *target*, not anchor.
4788 There is no guarantee that the target has a BASE node.
4789 For example:
4790
4791 The node was not present in BASE, but locally-added, and the
4792 update did not create a new BASE node "under" the local-add.
4793
4794 If there is no BASE node for the target, then we certainly don't
4795 have to worry about removing it. */
4796 err = svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL, NULL,
4797 NULL, NULL, NULL, NULL, NULL, NULL,
4798 NULL, NULL, NULL, NULL,
4799 eb->db, eb->target_abspath,
4800 scratch_pool, scratch_pool);
4801 if (err)
4802 {
4803 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
4804 return svn_error_trace(err);
4805
4806 svn_error_clear(err);
4807 }
4808 else if (status == svn_wc__db_status_excluded)
4809 {
4810 /* There is a small chance that the explicit target of an update/
4811 switch is gone in the repository, in that specific case the
4812 node hasn't been re-added to the BASE tree by this update.
4813
4814 If so, we should get rid of this excluded node now. */
4815
4816 SVN_ERR(svn_wc__db_base_remove(eb->db, eb->target_abspath,
4817 TRUE, FALSE, FALSE,
4818 SVN_INVALID_REVNUM,
4819 NULL, NULL, scratch_pool));
4820 }
4821 }
4822 }
4823
4824 /* Update keywords in switched files.
4825 GOTO #1975 (the year of the Altair 8800). */
4826 if (eb->switch_repos_relpath)
4827 {
4828 svn_depth_t depth;
4829
4830 if (eb->requested_depth > svn_depth_empty)
4831 depth = eb->requested_depth;
4832 else
4833 depth = svn_depth_infinity;
4834
4835 SVN_ERR(svn_wc__db_read_props_streamily(eb->db,
4836 eb->target_abspath,
4837 depth,
4838 FALSE, /* pristine */
4839 NULL, /* changelists */
4840 update_keywords_after_switch_cb,
4841 eb,
4842 eb->cancel_func,
4843 eb->cancel_baton,
4844 scratch_pool));
4845 }
4846
4847 /* The edit is over: run the wq with proper cancel support,
4848 but first kill the handler that would run it on the pool
4849 cleanup at the end of this function. */
4850 apr_pool_cleanup_kill(eb->pool, eb, cleanup_edit_baton);
4851
4852 SVN_ERR(svn_wc__wq_run(eb->db, eb->wcroot_abspath,
4853 eb->cancel_func, eb->cancel_baton,
4854 eb->pool));
4855
4856 /* The edit is over, free its pool.
4857 ### No, this is wrong. Who says this editor/baton won't be used
4858 again? But the change is not merely to remove this call. We
4859 should also make eb->pool not be a subpool (see make_editor),
4860 and change callers of svn_client_{checkout,update,switch} to do
4861 better pool management. ### */
4862
4863 svn_pool_destroy(eb->pool);
4864
4865 return SVN_NO_ERROR;
4866 }
4867
4868
4869 /*** Returning editors. ***/
4870
4871 /* Helper for the three public editor-supplying functions. */
4872 static svn_error_t *
make_editor(svn_revnum_t * target_revision,svn_wc__db_t * db,const char * anchor_abspath,const char * target_basename,apr_hash_t * wcroot_iprops,svn_boolean_t use_commit_times,const char * switch_url,svn_depth_t depth,svn_boolean_t depth_is_sticky,svn_boolean_t allow_unver_obstructions,svn_boolean_t adds_as_modification,svn_boolean_t server_performs_filtering,svn_boolean_t clean_checkout,svn_wc_notify_func2_t notify_func,void * notify_baton,svn_cancel_func_t cancel_func,void * cancel_baton,svn_wc_dirents_func_t fetch_dirents_func,void * fetch_dirents_baton,svn_wc_conflict_resolver_func2_t conflict_func,void * conflict_baton,svn_wc_external_update_t external_func,void * external_baton,const char * diff3_cmd,const apr_array_header_t * preserved_exts,const svn_delta_editor_t ** editor,void ** edit_baton,apr_pool_t * result_pool,apr_pool_t * scratch_pool)4873 make_editor(svn_revnum_t *target_revision,
4874 svn_wc__db_t *db,
4875 const char *anchor_abspath,
4876 const char *target_basename,
4877 apr_hash_t *wcroot_iprops,
4878 svn_boolean_t use_commit_times,
4879 const char *switch_url,
4880 svn_depth_t depth,
4881 svn_boolean_t depth_is_sticky,
4882 svn_boolean_t allow_unver_obstructions,
4883 svn_boolean_t adds_as_modification,
4884 svn_boolean_t server_performs_filtering,
4885 svn_boolean_t clean_checkout,
4886 svn_wc_notify_func2_t notify_func,
4887 void *notify_baton,
4888 svn_cancel_func_t cancel_func,
4889 void *cancel_baton,
4890 svn_wc_dirents_func_t fetch_dirents_func,
4891 void *fetch_dirents_baton,
4892 svn_wc_conflict_resolver_func2_t conflict_func,
4893 void *conflict_baton,
4894 svn_wc_external_update_t external_func,
4895 void *external_baton,
4896 const char *diff3_cmd,
4897 const apr_array_header_t *preserved_exts,
4898 const svn_delta_editor_t **editor,
4899 void **edit_baton,
4900 apr_pool_t *result_pool,
4901 apr_pool_t *scratch_pool)
4902 {
4903 struct edit_baton *eb;
4904 void *inner_baton;
4905 apr_pool_t *edit_pool = svn_pool_create(result_pool);
4906 svn_delta_editor_t *tree_editor = svn_delta_default_editor(edit_pool);
4907 const svn_delta_editor_t *inner_editor;
4908 const char *repos_root, *repos_uuid;
4909 struct svn_wc__shim_fetch_baton_t *sfb;
4910 svn_delta_shim_callbacks_t *shim_callbacks =
4911 svn_delta_shim_callbacks_default(edit_pool);
4912
4913 /* An unknown depth can't be sticky. */
4914 if (depth == svn_depth_unknown)
4915 depth_is_sticky = FALSE;
4916
4917 /* Get the anchor's repository root and uuid. The anchor must already exist
4918 in BASE. */
4919 SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, &repos_root,
4920 &repos_uuid, NULL, NULL, NULL, NULL,
4921 NULL, NULL, NULL, NULL, NULL, NULL,
4922 db, anchor_abspath,
4923 result_pool, scratch_pool));
4924
4925 /* With WC-NG we need a valid repository root */
4926 SVN_ERR_ASSERT(repos_root != NULL && repos_uuid != NULL);
4927
4928 /* Disallow a switch operation to change the repository root of the target,
4929 if that is known. */
4930 if (switch_url && !svn_uri__is_ancestor(repos_root, switch_url))
4931 return svn_error_createf(SVN_ERR_WC_INVALID_SWITCH, NULL,
4932 _("'%s'\nis not the same repository as\n'%s'"),
4933 switch_url, repos_root);
4934
4935 /* Construct an edit baton. */
4936 eb = apr_pcalloc(edit_pool, sizeof(*eb));
4937 eb->pool = edit_pool;
4938 eb->use_commit_times = use_commit_times;
4939 eb->target_revision = target_revision;
4940 eb->repos_root = repos_root;
4941 eb->repos_uuid = repos_uuid;
4942 eb->db = db;
4943 eb->target_basename = target_basename;
4944 eb->anchor_abspath = anchor_abspath;
4945 eb->wcroot_iprops = wcroot_iprops;
4946
4947 SVN_ERR(svn_wc__db_get_wcroot(&eb->wcroot_abspath, db, anchor_abspath,
4948 edit_pool, scratch_pool));
4949
4950 if (switch_url)
4951 eb->switch_repos_relpath =
4952 svn_uri_skip_ancestor(repos_root, switch_url, scratch_pool);
4953 else
4954 eb->switch_repos_relpath = NULL;
4955
4956 if (svn_path_is_empty(target_basename))
4957 eb->target_abspath = eb->anchor_abspath;
4958 else
4959 eb->target_abspath = svn_dirent_join(eb->anchor_abspath, target_basename,
4960 edit_pool);
4961
4962 eb->requested_depth = depth;
4963 eb->depth_is_sticky = depth_is_sticky;
4964 eb->notify_func = notify_func;
4965 eb->notify_baton = notify_baton;
4966 eb->external_func = external_func;
4967 eb->external_baton = external_baton;
4968 eb->diff3_cmd = diff3_cmd;
4969 eb->cancel_func = cancel_func;
4970 eb->cancel_baton = cancel_baton;
4971 eb->conflict_func = conflict_func;
4972 eb->conflict_baton = conflict_baton;
4973 eb->allow_unver_obstructions = allow_unver_obstructions;
4974 eb->adds_as_modification = adds_as_modification;
4975 eb->clean_checkout = clean_checkout;
4976 eb->skipped_trees = apr_hash_make(edit_pool);
4977 eb->dir_dirents = apr_hash_make(edit_pool);
4978 eb->ext_patterns = preserved_exts;
4979
4980 apr_pool_cleanup_register(edit_pool, eb, cleanup_edit_baton,
4981 apr_pool_cleanup_null);
4982
4983 /* Construct an editor. */
4984 tree_editor->set_target_revision = set_target_revision;
4985 tree_editor->open_root = open_root;
4986 tree_editor->delete_entry = delete_entry;
4987 tree_editor->add_directory = add_directory;
4988 tree_editor->open_directory = open_directory;
4989 tree_editor->change_dir_prop = change_dir_prop;
4990 tree_editor->close_directory = close_directory;
4991 tree_editor->absent_directory = absent_directory;
4992 tree_editor->add_file = add_file;
4993 tree_editor->open_file = open_file;
4994 tree_editor->apply_textdelta = apply_textdelta;
4995 tree_editor->change_file_prop = change_file_prop;
4996 tree_editor->close_file = close_file;
4997 tree_editor->absent_file = absent_file;
4998 tree_editor->close_edit = close_edit;
4999
5000 /* Fiddle with the type system. */
5001 inner_editor = tree_editor;
5002 inner_baton = eb;
5003
5004 if (!depth_is_sticky
5005 && depth != svn_depth_unknown
5006 && svn_depth_empty <= depth && depth < svn_depth_infinity
5007 && fetch_dirents_func)
5008 {
5009 /* We are asked to perform an update at a depth less than the ambient
5010 depth. In this case the update won't describe additions that would
5011 have been reported if we updated at the ambient depth. */
5012 svn_error_t *err;
5013 svn_node_kind_t dir_kind;
5014 svn_wc__db_status_t dir_status;
5015 const char *dir_repos_relpath;
5016 svn_depth_t dir_depth;
5017
5018 /* we have to do this on the target of the update, not the anchor */
5019 err = svn_wc__db_base_get_info(&dir_status, &dir_kind, NULL,
5020 &dir_repos_relpath, NULL, NULL, NULL,
5021 NULL, NULL, &dir_depth, NULL, NULL, NULL,
5022 NULL, NULL, NULL,
5023 db, eb->target_abspath,
5024 scratch_pool, scratch_pool);
5025
5026 if (!err
5027 && dir_kind == svn_node_dir
5028 && dir_status == svn_wc__db_status_normal)
5029 {
5030 if (dir_depth > depth)
5031 {
5032 apr_hash_t *dirents;
5033
5034 /* If we switch, we should look at the new relpath */
5035 if (eb->switch_repos_relpath)
5036 dir_repos_relpath = eb->switch_repos_relpath;
5037
5038 SVN_ERR(fetch_dirents_func(fetch_dirents_baton, &dirents,
5039 repos_root, dir_repos_relpath,
5040 edit_pool, scratch_pool));
5041
5042 if (dirents != NULL && apr_hash_count(dirents))
5043 svn_hash_sets(eb->dir_dirents,
5044 apr_pstrdup(edit_pool, dir_repos_relpath),
5045 dirents);
5046 }
5047
5048 if (depth == svn_depth_immediates)
5049 {
5050 /* Worst case scenario of issue #3569 fix: We have to do the
5051 same for all existing subdirs, but then we check for
5052 svn_depth_empty. */
5053 const apr_array_header_t *children;
5054 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
5055 int i;
5056 SVN_ERR(svn_wc__db_base_get_children(&children, db,
5057 eb->target_abspath,
5058 scratch_pool,
5059 iterpool));
5060
5061 for (i = 0; i < children->nelts; i++)
5062 {
5063 const char *child_abspath;
5064 const char *child_name;
5065
5066 svn_pool_clear(iterpool);
5067
5068 child_name = APR_ARRAY_IDX(children, i, const char *);
5069
5070 child_abspath = svn_dirent_join(eb->target_abspath,
5071 child_name, iterpool);
5072
5073 SVN_ERR(svn_wc__db_base_get_info(&dir_status, &dir_kind,
5074 NULL, &dir_repos_relpath,
5075 NULL, NULL, NULL, NULL,
5076 NULL, &dir_depth, NULL,
5077 NULL, NULL, NULL, NULL,
5078 NULL,
5079 db, child_abspath,
5080 iterpool, iterpool));
5081
5082 if (dir_kind == svn_node_dir
5083 && dir_status == svn_wc__db_status_normal
5084 && dir_depth > svn_depth_empty)
5085 {
5086 apr_hash_t *dirents;
5087
5088 /* If we switch, we should look at the new relpath */
5089 if (eb->switch_repos_relpath)
5090 dir_repos_relpath = svn_relpath_join(
5091 eb->switch_repos_relpath,
5092 child_name, iterpool);
5093
5094 SVN_ERR(fetch_dirents_func(fetch_dirents_baton, &dirents,
5095 repos_root, dir_repos_relpath,
5096 edit_pool, iterpool));
5097
5098 if (dirents != NULL && apr_hash_count(dirents))
5099 svn_hash_sets(eb->dir_dirents,
5100 apr_pstrdup(edit_pool,
5101 dir_repos_relpath),
5102 dirents);
5103 }
5104 }
5105 }
5106 }
5107 else if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
5108 svn_error_clear(err);
5109 else
5110 SVN_ERR(err);
5111 }
5112
5113 /* We need to limit the scope of our operation to the ambient depths
5114 present in the working copy already, but only if the requested
5115 depth is not sticky. If a depth was explicitly requested,
5116 libsvn_delta/depth_filter_editor.c will ensure that we never see
5117 editor calls that extend beyond the scope of the requested depth.
5118 But even what we do so might extend beyond the scope of our
5119 ambient depth. So we use another filtering editor to avoid
5120 modifying the ambient working copy depth when not asked to do so.
5121 (This can also be skipped if the server understands depth.) */
5122 if (!server_performs_filtering
5123 && !depth_is_sticky)
5124 SVN_ERR(svn_wc__ambient_depth_filter_editor(&inner_editor,
5125 &inner_baton,
5126 db,
5127 anchor_abspath,
5128 target_basename,
5129 inner_editor,
5130 inner_baton,
5131 result_pool));
5132
5133 SVN_ERR(svn_delta_get_cancellation_editor(cancel_func,
5134 cancel_baton,
5135 inner_editor,
5136 inner_baton,
5137 editor,
5138 edit_baton,
5139 result_pool));
5140
5141 sfb = apr_palloc(result_pool, sizeof(*sfb));
5142 sfb->db = db;
5143 sfb->base_abspath = eb->anchor_abspath;
5144 sfb->fetch_base = TRUE;
5145
5146 shim_callbacks->fetch_kind_func = svn_wc__fetch_kind_func;
5147 shim_callbacks->fetch_props_func = svn_wc__fetch_props_func;
5148 shim_callbacks->fetch_base_func = svn_wc__fetch_base_func;
5149 shim_callbacks->fetch_baton = sfb;
5150
5151 SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton,
5152 NULL, NULL, shim_callbacks,
5153 result_pool, scratch_pool));
5154
5155 return SVN_NO_ERROR;
5156 }
5157
5158
5159 svn_error_t *
svn_wc__get_update_editor(const svn_delta_editor_t ** editor,void ** edit_baton,svn_revnum_t * target_revision,svn_wc_context_t * wc_ctx,const char * anchor_abspath,const char * target_basename,apr_hash_t * wcroot_iprops,svn_boolean_t use_commit_times,svn_depth_t depth,svn_boolean_t depth_is_sticky,svn_boolean_t allow_unver_obstructions,svn_boolean_t adds_as_modification,svn_boolean_t server_performs_filtering,svn_boolean_t clean_checkout,const char * diff3_cmd,const apr_array_header_t * preserved_exts,svn_wc_dirents_func_t fetch_dirents_func,void * fetch_dirents_baton,svn_wc_conflict_resolver_func2_t conflict_func,void * conflict_baton,svn_wc_external_update_t external_func,void * external_baton,svn_cancel_func_t cancel_func,void * cancel_baton,svn_wc_notify_func2_t notify_func,void * notify_baton,apr_pool_t * result_pool,apr_pool_t * scratch_pool)5160 svn_wc__get_update_editor(const svn_delta_editor_t **editor,
5161 void **edit_baton,
5162 svn_revnum_t *target_revision,
5163 svn_wc_context_t *wc_ctx,
5164 const char *anchor_abspath,
5165 const char *target_basename,
5166 apr_hash_t *wcroot_iprops,
5167 svn_boolean_t use_commit_times,
5168 svn_depth_t depth,
5169 svn_boolean_t depth_is_sticky,
5170 svn_boolean_t allow_unver_obstructions,
5171 svn_boolean_t adds_as_modification,
5172 svn_boolean_t server_performs_filtering,
5173 svn_boolean_t clean_checkout,
5174 const char *diff3_cmd,
5175 const apr_array_header_t *preserved_exts,
5176 svn_wc_dirents_func_t fetch_dirents_func,
5177 void *fetch_dirents_baton,
5178 svn_wc_conflict_resolver_func2_t conflict_func,
5179 void *conflict_baton,
5180 svn_wc_external_update_t external_func,
5181 void *external_baton,
5182 svn_cancel_func_t cancel_func,
5183 void *cancel_baton,
5184 svn_wc_notify_func2_t notify_func,
5185 void *notify_baton,
5186 apr_pool_t *result_pool,
5187 apr_pool_t *scratch_pool)
5188 {
5189 return make_editor(target_revision, wc_ctx->db, anchor_abspath,
5190 target_basename, wcroot_iprops, use_commit_times,
5191 NULL, depth, depth_is_sticky, allow_unver_obstructions,
5192 adds_as_modification, server_performs_filtering,
5193 clean_checkout,
5194 notify_func, notify_baton,
5195 cancel_func, cancel_baton,
5196 fetch_dirents_func, fetch_dirents_baton,
5197 conflict_func, conflict_baton,
5198 external_func, external_baton,
5199 diff3_cmd, preserved_exts, editor, edit_baton,
5200 result_pool, scratch_pool);
5201 }
5202
5203 svn_error_t *
svn_wc__get_switch_editor(const svn_delta_editor_t ** editor,void ** edit_baton,svn_revnum_t * target_revision,svn_wc_context_t * wc_ctx,const char * anchor_abspath,const char * target_basename,const char * switch_url,apr_hash_t * wcroot_iprops,svn_boolean_t use_commit_times,svn_depth_t depth,svn_boolean_t depth_is_sticky,svn_boolean_t allow_unver_obstructions,svn_boolean_t server_performs_filtering,const char * diff3_cmd,const apr_array_header_t * preserved_exts,svn_wc_dirents_func_t fetch_dirents_func,void * fetch_dirents_baton,svn_wc_conflict_resolver_func2_t conflict_func,void * conflict_baton,svn_wc_external_update_t external_func,void * external_baton,svn_cancel_func_t cancel_func,void * cancel_baton,svn_wc_notify_func2_t notify_func,void * notify_baton,apr_pool_t * result_pool,apr_pool_t * scratch_pool)5204 svn_wc__get_switch_editor(const svn_delta_editor_t **editor,
5205 void **edit_baton,
5206 svn_revnum_t *target_revision,
5207 svn_wc_context_t *wc_ctx,
5208 const char *anchor_abspath,
5209 const char *target_basename,
5210 const char *switch_url,
5211 apr_hash_t *wcroot_iprops,
5212 svn_boolean_t use_commit_times,
5213 svn_depth_t depth,
5214 svn_boolean_t depth_is_sticky,
5215 svn_boolean_t allow_unver_obstructions,
5216 svn_boolean_t server_performs_filtering,
5217 const char *diff3_cmd,
5218 const apr_array_header_t *preserved_exts,
5219 svn_wc_dirents_func_t fetch_dirents_func,
5220 void *fetch_dirents_baton,
5221 svn_wc_conflict_resolver_func2_t conflict_func,
5222 void *conflict_baton,
5223 svn_wc_external_update_t external_func,
5224 void *external_baton,
5225 svn_cancel_func_t cancel_func,
5226 void *cancel_baton,
5227 svn_wc_notify_func2_t notify_func,
5228 void *notify_baton,
5229 apr_pool_t *result_pool,
5230 apr_pool_t *scratch_pool)
5231 {
5232 SVN_ERR_ASSERT(switch_url && svn_uri_is_canonical(switch_url, scratch_pool));
5233
5234 return make_editor(target_revision, wc_ctx->db, anchor_abspath,
5235 target_basename, wcroot_iprops, use_commit_times,
5236 switch_url,
5237 depth, depth_is_sticky, allow_unver_obstructions,
5238 FALSE /* adds_as_modification */,
5239 server_performs_filtering,
5240 FALSE /* clean_checkout */,
5241 notify_func, notify_baton,
5242 cancel_func, cancel_baton,
5243 fetch_dirents_func, fetch_dirents_baton,
5244 conflict_func, conflict_baton,
5245 external_func, external_baton,
5246 diff3_cmd, preserved_exts,
5247 editor, edit_baton,
5248 result_pool, scratch_pool);
5249 }
5250
5251
5252
5253 /* ### Note that this function is completely different from the rest of the
5254 update editor in what it updates. The update editor changes only BASE
5255 and ACTUAL and this function just changes WORKING and ACTUAL.
5256
5257 In the entries world this function shared a lot of code with the
5258 update editor but in the wonderful new WC-NG world it will probably
5259 do more and more by itself and would be more logically grouped with
5260 the add/copy functionality in adm_ops.c and copy.c. */
5261 svn_error_t *
svn_wc_add_repos_file4(svn_wc_context_t * wc_ctx,const char * local_abspath,svn_stream_t * new_base_contents,svn_stream_t * new_contents,apr_hash_t * new_base_props,apr_hash_t * new_props,const char * copyfrom_url,svn_revnum_t copyfrom_rev,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)5262 svn_wc_add_repos_file4(svn_wc_context_t *wc_ctx,
5263 const char *local_abspath,
5264 svn_stream_t *new_base_contents,
5265 svn_stream_t *new_contents,
5266 apr_hash_t *new_base_props,
5267 apr_hash_t *new_props,
5268 const char *copyfrom_url,
5269 svn_revnum_t copyfrom_rev,
5270 svn_cancel_func_t cancel_func,
5271 void *cancel_baton,
5272 apr_pool_t *scratch_pool)
5273 {
5274 svn_wc__db_t *db = wc_ctx->db;
5275 const char *dir_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
5276 svn_wc__db_status_t status;
5277 svn_node_kind_t kind;
5278 const char *tmp_text_base_abspath;
5279 svn_checksum_t *new_text_base_md5_checksum;
5280 svn_checksum_t *new_text_base_sha1_checksum;
5281 const char *source_abspath = NULL;
5282 svn_skel_t *all_work_items = NULL;
5283 svn_skel_t *work_item;
5284 const char *repos_root_url;
5285 const char *repos_uuid;
5286 const char *original_repos_relpath;
5287 svn_revnum_t changed_rev;
5288 apr_time_t changed_date;
5289 const char *changed_author;
5290 svn_stream_t *tmp_base_contents;
5291 svn_wc__db_install_data_t *install_data;
5292 svn_error_t *err;
5293 apr_pool_t *pool = scratch_pool;
5294
5295 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5296 SVN_ERR_ASSERT(new_base_contents != NULL);
5297 SVN_ERR_ASSERT(new_base_props != NULL);
5298
5299 /* We should have a write lock on this file's parent directory. */
5300 SVN_ERR(svn_wc__write_check(db, dir_abspath, pool));
5301
5302 err = svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5303 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5304 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5305 NULL, NULL, NULL,
5306 db, local_abspath, scratch_pool, scratch_pool);
5307
5308 if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
5309 return svn_error_trace(err);
5310 else if(err)
5311 svn_error_clear(err);
5312 else
5313 switch (status)
5314 {
5315 case svn_wc__db_status_not_present:
5316 case svn_wc__db_status_deleted:
5317 break;
5318 default:
5319 return svn_error_createf(SVN_ERR_ENTRY_EXISTS, NULL,
5320 _("Node '%s' exists."),
5321 svn_dirent_local_style(local_abspath,
5322 scratch_pool));
5323 }
5324
5325 SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, &repos_root_url,
5326 &repos_uuid, NULL, NULL, NULL, NULL, NULL, NULL,
5327 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5328 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5329 db, dir_abspath, scratch_pool, scratch_pool));
5330
5331 switch (status)
5332 {
5333 case svn_wc__db_status_normal:
5334 case svn_wc__db_status_added:
5335 break;
5336 case svn_wc__db_status_deleted:
5337 return
5338 svn_error_createf(SVN_ERR_WC_SCHEDULE_CONFLICT, NULL,
5339 _("Can't add '%s' to a parent directory"
5340 " scheduled for deletion"),
5341 svn_dirent_local_style(local_abspath,
5342 scratch_pool));
5343 default:
5344 return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, err,
5345 _("Can't find parent directory's node while"
5346 " trying to add '%s'"),
5347 svn_dirent_local_style(local_abspath,
5348 scratch_pool));
5349 }
5350 if (kind != svn_node_dir)
5351 return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
5352 _("Can't schedule an addition of '%s'"
5353 " below a not-directory node"),
5354 svn_dirent_local_style(local_abspath,
5355 scratch_pool));
5356
5357 /* Fabricate the anticipated new URL of the target and check the
5358 copyfrom URL to be in the same repository. */
5359 if (copyfrom_url != NULL)
5360 {
5361 /* Find the repository_root via the parent directory, which
5362 is always versioned before this function is called */
5363
5364 if (!repos_root_url)
5365 {
5366 /* The parent is an addition, scan upwards to find the right info */
5367 SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, NULL,
5368 &repos_root_url, &repos_uuid,
5369 NULL, NULL, NULL, NULL,
5370 wc_ctx->db, dir_abspath,
5371 scratch_pool, scratch_pool));
5372 }
5373 SVN_ERR_ASSERT(repos_root_url);
5374
5375 original_repos_relpath =
5376 svn_uri_skip_ancestor(repos_root_url, copyfrom_url, scratch_pool);
5377
5378 if (!original_repos_relpath)
5379 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
5380 _("Copyfrom-url '%s' has different repository"
5381 " root than '%s'"),
5382 copyfrom_url, repos_root_url);
5383 }
5384 else
5385 {
5386 original_repos_relpath = NULL;
5387 copyfrom_rev = SVN_INVALID_REVNUM; /* Just to be sure. */
5388 }
5389
5390 /* Set CHANGED_* to reflect the entry props in NEW_BASE_PROPS, and
5391 filter NEW_BASE_PROPS so it contains only regular props. */
5392 {
5393 apr_array_header_t *regular_props;
5394 apr_array_header_t *entry_props;
5395
5396 SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(new_base_props, pool),
5397 &entry_props, NULL, ®ular_props,
5398 pool));
5399
5400 /* Put regular props back into a hash table. */
5401 new_base_props = svn_prop_array_to_hash(regular_props, pool);
5402
5403 /* Get the change_* info from the entry props. */
5404 SVN_ERR(accumulate_last_change(&changed_rev,
5405 &changed_date,
5406 &changed_author,
5407 entry_props, pool, pool));
5408 }
5409
5410 /* Copy NEW_BASE_CONTENTS into a temporary file so our log can refer to
5411 it, and set TMP_TEXT_BASE_ABSPATH to its path. Compute its
5412 NEW_TEXT_BASE_MD5_CHECKSUM and NEW_TEXT_BASE_SHA1_CHECKSUM as we copy. */
5413 if (copyfrom_url)
5414 {
5415 SVN_ERR(svn_wc__db_pristine_prepare_install(&tmp_base_contents,
5416 &install_data,
5417 &new_text_base_sha1_checksum,
5418 &new_text_base_md5_checksum,
5419 wc_ctx->db, local_abspath,
5420 scratch_pool, scratch_pool));
5421 }
5422 else
5423 {
5424 const char *tmp_dir_abspath;
5425
5426 /* We are not installing a PRISTINE file, but we use the same code to
5427 create whatever we want to install */
5428
5429 SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&tmp_dir_abspath,
5430 db, dir_abspath,
5431 scratch_pool, scratch_pool));
5432
5433 SVN_ERR(svn_stream_open_unique(&tmp_base_contents, &tmp_text_base_abspath,
5434 tmp_dir_abspath, svn_io_file_del_none,
5435 scratch_pool, scratch_pool));
5436
5437 new_text_base_sha1_checksum = NULL;
5438 new_text_base_md5_checksum = NULL;
5439 }
5440 SVN_ERR(svn_stream_copy3(new_base_contents, tmp_base_contents,
5441 cancel_func, cancel_baton, pool));
5442
5443 /* If the caller gave us a new working file, copy it to a safe (temporary)
5444 location and set SOURCE_ABSPATH to that path. We'll then translate/copy
5445 that into place after the node's state has been created. */
5446 if (new_contents)
5447 {
5448 const char *temp_dir_abspath;
5449 svn_stream_t *tmp_contents;
5450
5451 SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, db,
5452 local_abspath, pool, pool));
5453 SVN_ERR(svn_stream_open_unique(&tmp_contents, &source_abspath,
5454 temp_dir_abspath, svn_io_file_del_none,
5455 pool, pool));
5456 SVN_ERR(svn_stream_copy3(new_contents, tmp_contents,
5457 cancel_func, cancel_baton, pool));
5458 }
5459
5460 /* Install new text base for copied files. Added files do NOT have a
5461 text base. */
5462 if (copyfrom_url != NULL)
5463 {
5464 SVN_ERR(svn_wc__db_pristine_install(install_data,
5465 new_text_base_sha1_checksum,
5466 new_text_base_md5_checksum, pool));
5467 }
5468 else
5469 {
5470 /* ### There's something wrong around here. Sometimes (merge from a
5471 foreign repository, at least) we are called with copyfrom_url =
5472 NULL and an empty new_base_contents (and an empty set of
5473 new_base_props). Why an empty "new base"?
5474
5475 That happens in merge_tests.py 54,87,88,89,143.
5476
5477 In that case, having been given this supposed "new base" file, we
5478 copy it and calculate its checksum but do not install it. Why?
5479 That must be wrong.
5480
5481 To crudely work around one issue with this, that we shouldn't
5482 record a checksum in the database if we haven't installed the
5483 corresponding pristine text, for now we'll just set the checksum
5484 to NULL.
5485
5486 The proper solution is probably more like: the caller should pass
5487 NULL for the missing information, and this function should learn to
5488 handle that. */
5489
5490 new_text_base_sha1_checksum = NULL;
5491 new_text_base_md5_checksum = NULL;
5492 }
5493
5494 /* For added files without NEW_CONTENTS, then generate the working file
5495 from the provided "pristine" contents. */
5496 if (new_contents == NULL && copyfrom_url == NULL)
5497 source_abspath = tmp_text_base_abspath;
5498
5499 {
5500 svn_boolean_t record_fileinfo;
5501
5502 /* If new contents were provided, then we do NOT want to record the
5503 file information. We assume the new contents do not match the
5504 "proper" values for RECORDED_SIZE and RECORDED_TIME. */
5505 record_fileinfo = (new_contents == NULL);
5506
5507 /* Install the working copy file (with appropriate translation) from
5508 the appropriate source. SOURCE_ABSPATH will be NULL, indicating an
5509 installation from the pristine (available for copied/moved files),
5510 or it will specify a temporary file where we placed a "pristine"
5511 (for an added file) or a detranslated local-mods file. */
5512 SVN_ERR(svn_wc__wq_build_file_install(&work_item,
5513 db, local_abspath,
5514 source_abspath,
5515 FALSE /* use_commit_times */,
5516 record_fileinfo,
5517 pool, pool));
5518 all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool);
5519
5520 /* If we installed from somewhere besides the official pristine, then
5521 it is a temporary file, which needs to be removed. */
5522 if (source_abspath != NULL)
5523 {
5524 SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, local_abspath,
5525 source_abspath,
5526 pool, pool));
5527 all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool);
5528 }
5529 }
5530
5531 SVN_ERR(svn_wc__db_op_copy_file(db, local_abspath,
5532 new_base_props,
5533 changed_rev,
5534 changed_date,
5535 changed_author,
5536 original_repos_relpath,
5537 original_repos_relpath ? repos_root_url
5538 : NULL,
5539 original_repos_relpath ? repos_uuid : NULL,
5540 copyfrom_rev,
5541 new_text_base_sha1_checksum,
5542 TRUE,
5543 new_props,
5544 FALSE /* is_move */,
5545 NULL /* conflict */,
5546 all_work_items,
5547 pool));
5548
5549 return svn_error_trace(svn_wc__wq_run(db, dir_abspath,
5550 cancel_func, cancel_baton,
5551 pool));
5552 }
5553
5554 svn_error_t *
svn_wc__complete_directory_add(svn_wc_context_t * wc_ctx,const char * local_abspath,apr_hash_t * new_original_props,const char * copyfrom_url,svn_revnum_t copyfrom_rev,apr_pool_t * scratch_pool)5555 svn_wc__complete_directory_add(svn_wc_context_t *wc_ctx,
5556 const char *local_abspath,
5557 apr_hash_t *new_original_props,
5558 const char *copyfrom_url,
5559 svn_revnum_t copyfrom_rev,
5560 apr_pool_t *scratch_pool)
5561 {
5562 svn_wc__db_status_t status;
5563 svn_node_kind_t kind;
5564 const char *original_repos_relpath;
5565 const char *original_root_url;
5566 const char *original_uuid;
5567 svn_boolean_t had_props;
5568 svn_boolean_t props_mod;
5569
5570 svn_revnum_t original_revision;
5571 svn_revnum_t changed_rev;
5572 apr_time_t changed_date;
5573 const char *changed_author;
5574
5575 SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
5576 NULL, NULL, NULL, NULL, NULL,
5577 &original_repos_relpath, &original_root_url,
5578 &original_uuid, &original_revision, NULL, NULL,
5579 NULL, NULL, NULL, NULL, &had_props, &props_mod,
5580 NULL, NULL, NULL,
5581 wc_ctx->db, local_abspath,
5582 scratch_pool, scratch_pool));
5583
5584 if (status != svn_wc__db_status_added
5585 || kind != svn_node_dir
5586 || had_props
5587 || props_mod
5588 || !original_repos_relpath)
5589 {
5590 return svn_error_createf(
5591 SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
5592 _("'%s' is not an unmodified copied directory"),
5593 svn_dirent_local_style(local_abspath, scratch_pool));
5594 }
5595 if (original_revision != copyfrom_rev
5596 || strcmp(copyfrom_url,
5597 svn_path_url_add_component2(original_root_url,
5598 original_repos_relpath,
5599 scratch_pool)))
5600 {
5601 return svn_error_createf(
5602 SVN_ERR_WC_COPYFROM_PATH_NOT_FOUND, NULL,
5603 _("Copyfrom '%s' doesn't match original location of '%s'"),
5604 copyfrom_url,
5605 svn_dirent_local_style(local_abspath, scratch_pool));
5606 }
5607
5608 {
5609 apr_array_header_t *regular_props;
5610 apr_array_header_t *entry_props;
5611
5612 SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(new_original_props,
5613 scratch_pool),
5614 &entry_props, NULL, ®ular_props,
5615 scratch_pool));
5616
5617 /* Put regular props back into a hash table. */
5618 new_original_props = svn_prop_array_to_hash(regular_props, scratch_pool);
5619
5620 /* Get the change_* info from the entry props. */
5621 SVN_ERR(accumulate_last_change(&changed_rev,
5622 &changed_date,
5623 &changed_author,
5624 entry_props, scratch_pool, scratch_pool));
5625 }
5626
5627 return svn_error_trace(
5628 svn_wc__db_op_copy_dir(wc_ctx->db, local_abspath,
5629 new_original_props,
5630 changed_rev, changed_date, changed_author,
5631 original_repos_relpath, original_root_url,
5632 original_uuid, original_revision,
5633 NULL /* children */,
5634 svn_depth_infinity,
5635 FALSE /* is_move */,
5636 NULL /* conflict */,
5637 NULL /* work_items */,
5638 scratch_pool));
5639 }
5640