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