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