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