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