1251881Speter/*
2251881Speter * update_editor.c :  main editor for checkouts and updates
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter
25251881Speter
26251881Speter#include <stdlib.h>
27251881Speter#include <string.h>
28251881Speter
29251881Speter#include <apr_pools.h>
30251881Speter#include <apr_hash.h>
31251881Speter#include <apr_md5.h>
32251881Speter#include <apr_tables.h>
33251881Speter#include <apr_strings.h>
34251881Speter
35251881Speter#include "svn_types.h"
36251881Speter#include "svn_pools.h"
37251881Speter#include "svn_hash.h"
38251881Speter#include "svn_string.h"
39251881Speter#include "svn_dirent_uri.h"
40251881Speter#include "svn_path.h"
41251881Speter#include "svn_error.h"
42251881Speter#include "svn_io.h"
43251881Speter#include "svn_private_config.h"
44251881Speter#include "svn_time.h"
45251881Speter
46251881Speter#include "wc.h"
47251881Speter#include "adm_files.h"
48251881Speter#include "conflicts.h"
49251881Speter#include "translate.h"
50251881Speter#include "workqueue.h"
51251881Speter
52251881Speter#include "private/svn_subr_private.h"
53251881Speter#include "private/svn_wc_private.h"
54251881Speter#include "private/svn_editor.h"
55251881Speter
56251881Speter/* Checks whether a svn_wc__db_status_t indicates whether a node is
57251881Speter   present in a working copy. Used by the editor implementation */
58251881Speter#define IS_NODE_PRESENT(status)                             \
59251881Speter           ((status) != svn_wc__db_status_server_excluded &&\
60251881Speter            (status) != svn_wc__db_status_excluded &&       \
61251881Speter            (status) != svn_wc__db_status_not_present)
62251881Speter
63251881Speterstatic svn_error_t *
64251881Speterpath_join_under_root(const char **result_path,
65251881Speter                     const char *base_path,
66251881Speter                     const char *add_path,
67251881Speter                     apr_pool_t *result_pool);
68251881Speter
69251881Speter
70251881Speter/*
71251881Speter * This code handles "checkout" and "update" and "switch".
72251881Speter * A checkout is similar to an update that is only adding new items.
73251881Speter *
74251881Speter * The intended behaviour of "update" and "switch", focusing on the checks
75251881Speter * to be made before applying a change, is:
76251881Speter *
77251881Speter *   For each incoming change:
78251881Speter *     if target is already in conflict or obstructed:
79251881Speter *       skip this change
80251881Speter *     else
81251881Speter *     if this action will cause a tree conflict:
82251881Speter *       record the tree conflict
83251881Speter *       skip this change
84251881Speter *     else:
85251881Speter *       make this change
86251881Speter *
87251881Speter * In more detail:
88251881Speter *
89251881Speter *   For each incoming change:
90251881Speter *
91251881Speter *   1.   if  # Incoming change is inside an item already in conflict:
92251881Speter *    a.    tree/text/prop change to node beneath tree-conflicted dir
93251881Speter *        then  # Skip all changes in this conflicted subtree [*1]:
94251881Speter *          do not update the Base nor the Working
95251881Speter *          notify "skipped because already in conflict" just once
96251881Speter *            for the whole conflicted subtree
97251881Speter *
98251881Speter *        if  # Incoming change affects an item already in conflict:
99251881Speter *    b.    tree/text/prop change to tree-conflicted dir/file, or
100251881Speter *    c.    tree change to a text/prop-conflicted file/dir, or
101251881Speter *    d.    text/prop change to a text/prop-conflicted file/dir [*2], or
102251881Speter *    e.    tree change to a dir tree containing any conflicts,
103251881Speter *        then  # Skip this change [*1]:
104251881Speter *          do not update the Base nor the Working
105251881Speter *          notify "skipped because already in conflict"
106251881Speter *
107251881Speter *   2.   if  # Incoming change affects an item that's "obstructed":
108251881Speter *    a.    on-disk node kind doesn't match recorded Working node kind
109251881Speter *            (including an absence/presence mis-match),
110251881Speter *        then  # Skip this change [*1]:
111251881Speter *          do not update the Base nor the Working
112251881Speter *          notify "skipped because obstructed"
113251881Speter *
114251881Speter *   3.   if  # Incoming change raises a tree conflict:
115251881Speter *    a.    tree/text/prop change to node beneath sched-delete dir, or
116251881Speter *    b.    tree/text/prop change to sched-delete dir/file, or
117251881Speter *    c.    text/prop change to tree-scheduled dir/file,
118251881Speter *        then  # Skip this change:
119251881Speter *          do not update the Base nor the Working [*3]
120251881Speter *          notify "tree conflict"
121251881Speter *
122251881Speter *   4.   Apply the change:
123251881Speter *          update the Base
124251881Speter *          update the Working, possibly raising text/prop conflicts
125251881Speter *          notify
126251881Speter *
127251881Speter * Notes:
128251881Speter *
129251881Speter *      "Tree change" here refers to an add or delete of the target node,
130251881Speter *      including the add or delete part of a copy or move or rename.
131251881Speter *
132251881Speter * [*1] We should skip changes to an entire node, as the base revision number
133251881Speter *      applies to the entire node. Not sure how this affects attempts to
134251881Speter *      handle text and prop changes separately.
135251881Speter *
136251881Speter * [*2] Details of which combinations of property and text changes conflict
137251881Speter *      are not specified here.
138251881Speter *
139251881Speter * [*3] For now, we skip the update, and require the user to:
140251881Speter *        - Modify the WC to be compatible with the incoming change;
141251881Speter *        - Mark the conflict as resolved;
142251881Speter *        - Repeat the update.
143251881Speter *      Ideally, it would be possible to resolve any conflict without
144251881Speter *      repeating the update. To achieve this, we would have to store the
145251881Speter *      necessary data at conflict detection time, and delay the update of
146251881Speter *      the Base until the time of resolving.
147251881Speter */
148251881Speter
149251881Speter
150251881Speter/*** batons ***/
151251881Speter
152251881Speterstruct edit_baton
153251881Speter{
154251881Speter  /* For updates, the "destination" of the edit is ANCHOR_ABSPATH, the
155251881Speter     directory containing TARGET_ABSPATH. If ANCHOR_ABSPATH itself is the
156251881Speter     target, the values are identical.
157251881Speter
158251881Speter     TARGET_BASENAME is the name of TARGET_ABSPATH in ANCHOR_ABSPATH, or "" if
159251881Speter     ANCHOR_ABSPATH is the target */
160251881Speter  const char *target_basename;
161251881Speter
162251881Speter  /* Absolute variants of ANCHOR and TARGET */
163251881Speter  const char *anchor_abspath;
164251881Speter  const char *target_abspath;
165251881Speter
166251881Speter  /* The DB handle for managing the working copy state.  */
167251881Speter  svn_wc__db_t *db;
168251881Speter
169251881Speter  /* Array of file extension patterns to preserve as extensions in
170251881Speter     generated conflict files. */
171251881Speter  const apr_array_header_t *ext_patterns;
172251881Speter
173251881Speter  /* Hash mapping const char * absolute working copy paths to depth-first
174251881Speter     ordered arrays of svn_prop_inherited_item_t * structures representing
175251881Speter     the properties inherited by the base node at that working copy path.
176251881Speter     May be NULL. */
177251881Speter  apr_hash_t *wcroot_iprops;
178251881Speter
179251881Speter  /* The revision we're targeting...or something like that.  This
180251881Speter     starts off as a pointer to the revision to which we are updating,
181251881Speter     or SVN_INVALID_REVNUM, but by the end of the edit, should be
182251881Speter     pointing to the final revision. */
183251881Speter  svn_revnum_t *target_revision;
184251881Speter
185251881Speter  /* The requested depth of this edit. */
186251881Speter  svn_depth_t requested_depth;
187251881Speter
188251881Speter  /* Is the requested depth merely an operational limitation, or is
189251881Speter     also the new sticky ambient depth of the update target? */
190251881Speter  svn_boolean_t depth_is_sticky;
191251881Speter
192251881Speter  /* Need to know if the user wants us to overwrite the 'now' times on
193251881Speter     edited/added files with the last-commit-time. */
194251881Speter  svn_boolean_t use_commit_times;
195251881Speter
196251881Speter  /* Was the root actually opened (was this a non-empty edit)? */
197251881Speter  svn_boolean_t root_opened;
198251881Speter
199251881Speter  /* Was the update-target deleted?  This is a special situation. */
200251881Speter  svn_boolean_t target_deleted;
201251881Speter
202251881Speter  /* Allow unversioned obstructions when adding a path. */
203251881Speter  svn_boolean_t allow_unver_obstructions;
204251881Speter
205251881Speter  /* Handle local additions as modifications of new nodes */
206251881Speter  svn_boolean_t adds_as_modification;
207251881Speter
208251881Speter  /* If set, we check out into an empty directory. This allows for a number
209251881Speter     of conflict checks to be omitted. */
210251881Speter  svn_boolean_t clean_checkout;
211251881Speter
212251881Speter  /* If this is a 'switch' operation, the new relpath of target_abspath,
213251881Speter     else NULL. */
214289180Speter  const char *switch_repos_relpath;
215251881Speter
216251881Speter  /* The URL to the root of the repository. */
217251881Speter  const char *repos_root;
218251881Speter
219251881Speter  /* The UUID of the repos, or NULL. */
220251881Speter  const char *repos_uuid;
221251881Speter
222251881Speter  /* External diff3 to use for merges (can be null, in which case
223251881Speter     internal merge code is used). */
224251881Speter  const char *diff3_cmd;
225251881Speter
226251881Speter  /* Externals handler */
227251881Speter  svn_wc_external_update_t external_func;
228251881Speter  void *external_baton;
229251881Speter
230251881Speter  /* This editor sends back notifications as it edits. */
231251881Speter  svn_wc_notify_func2_t notify_func;
232251881Speter  void *notify_baton;
233251881Speter
234251881Speter  /* This editor is normally wrapped in a cancellation editor anyway,
235251881Speter     so it doesn't bother to check for cancellation itself.  However,
236251881Speter     it needs a cancel_func and cancel_baton available to pass to
237251881Speter     long-running functions. */
238251881Speter  svn_cancel_func_t cancel_func;
239251881Speter  void *cancel_baton;
240251881Speter
241251881Speter  /* This editor will invoke a interactive conflict-resolution
242251881Speter     callback, if available. */
243251881Speter  svn_wc_conflict_resolver_func2_t conflict_func;
244251881Speter  void *conflict_baton;
245251881Speter
246251881Speter  /* Subtrees that were skipped during the edit, and therefore shouldn't
247251881Speter     have their revision/url info updated at the end.  If a path is a
248251881Speter     directory, its descendants will also be skipped.  The keys are paths
249251881Speter     relative to the working copy root and the values unspecified. */
250251881Speter  apr_hash_t *skipped_trees;
251251881Speter
252251881Speter  /* A mapping from const char * repos_relpaths to the apr_hash_t * instances
253251881Speter     returned from fetch_dirents_func for that repos_relpath. These
254251881Speter     are used to avoid issue #3569 in specific update scenarios where a
255251881Speter     restricted depth is used. */
256251881Speter  apr_hash_t *dir_dirents;
257251881Speter
258251881Speter  /* Absolute path of the working copy root or NULL if not initialized yet */
259251881Speter  const char *wcroot_abspath;
260251881Speter
261289180Speter  /* After closing the root directory a copy of its edited value */
262289180Speter  svn_boolean_t edited;
263289180Speter
264251881Speter  apr_pool_t *pool;
265251881Speter};
266251881Speter
267251881Speter
268251881Speter/* Record in the edit baton EB that LOCAL_ABSPATH's base version is not being
269251881Speter * updated.
270251881Speter *
271251881Speter * Add to EB->skipped_trees a copy (allocated in EB->pool) of the string
272251881Speter * LOCAL_ABSPATH.
273251881Speter */
274251881Speterstatic svn_error_t *
275251881Speterremember_skipped_tree(struct edit_baton *eb,
276251881Speter                      const char *local_abspath,
277251881Speter                      apr_pool_t *scratch_pool)
278251881Speter{
279251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
280251881Speter
281251881Speter  svn_hash_sets(eb->skipped_trees,
282251881Speter                apr_pstrdup(eb->pool,
283251881Speter                            svn_dirent_skip_ancestor(eb->wcroot_abspath,
284251881Speter                                                     local_abspath)),
285251881Speter                (void *)1);
286251881Speter
287251881Speter  return SVN_NO_ERROR;
288251881Speter}
289251881Speter
290251881Speter/* Per directory baton. Lives in its own subpool of the parent directory
291251881Speter   or of the edit baton if there is no parent directory */
292251881Speterstruct dir_baton
293251881Speter{
294251881Speter  /* Basename of this directory. */
295251881Speter  const char *name;
296251881Speter
297251881Speter  /* Absolute path of this directory */
298251881Speter  const char *local_abspath;
299251881Speter
300251881Speter  /* The repository relative path this directory will correspond to. */
301289180Speter  const char *new_repos_relpath;
302251881Speter
303251881Speter  /* The revision of the directory before updating */
304251881Speter  svn_revnum_t old_revision;
305251881Speter
306251881Speter  /* The repos_relpath before updating/switching */
307251881Speter  const char *old_repos_relpath;
308251881Speter
309251881Speter  /* The global edit baton. */
310251881Speter  struct edit_baton *edit_baton;
311251881Speter
312251881Speter  /* Baton for this directory's parent, or NULL if this is the root
313251881Speter     directory. */
314251881Speter  struct dir_baton *parent_baton;
315251881Speter
316251881Speter  /* Set if updates to this directory are skipped */
317251881Speter  svn_boolean_t skip_this;
318251881Speter
319251881Speter  /* Set if there was a previous notification for this directory */
320251881Speter  svn_boolean_t already_notified;
321251881Speter
322251881Speter  /* Set if this directory is being added during this editor drive. */
323251881Speter  svn_boolean_t adding_dir;
324251881Speter
325251881Speter  /* Set on a node and its descendants are not present in the working copy
326251881Speter     but should still be updated (not skipped). These nodes should all be
327251881Speter     marked as deleted. */
328251881Speter  svn_boolean_t shadowed;
329251881Speter
330251881Speter  /* Set on a node when the existing node is obstructed, and the edit operation
331251881Speter     continues as semi-shadowed update */
332251881Speter  svn_boolean_t edit_obstructed;
333251881Speter
334251881Speter  /* The (new) changed_* information, cached to avoid retrieving it later */
335251881Speter  svn_revnum_t changed_rev;
336251881Speter  apr_time_t changed_date;
337251881Speter  const char *changed_author;
338251881Speter
339251881Speter  /* If not NULL, contains a mapping of const char* basenames of children that
340251881Speter     have been deleted to their svn_skel_t* tree conflicts.
341251881Speter     We store this hash to allow replacements to continue under a just
342251881Speter     installed tree conflict.
343251881Speter
344251881Speter     The add after the delete will then update the tree conflicts information
345251881Speter     and reinstall it. */
346251881Speter  apr_hash_t *deletion_conflicts;
347251881Speter
348289180Speter  /* A hash of file names (only the hash key matters) seen by add_file and
349289180Speter     add_directory and not yet added to the database, mapping to a const
350289180Speter     char * node kind (via svn_node_kind_to_word(). */
351289180Speter  apr_hash_t *not_present_nodes;
352251881Speter
353251881Speter  /* Set if an unversioned dir of the same name already existed in
354251881Speter     this directory. */
355251881Speter  svn_boolean_t obstruction_found;
356251881Speter
357251881Speter  /* Set if a dir of the same name already exists and is
358251881Speter     scheduled for addition without history. */
359251881Speter  svn_boolean_t add_existed;
360251881Speter
361251881Speter  /* An array of svn_prop_t structures, representing all the property
362251881Speter     changes to be applied to this directory. */
363251881Speter  apr_array_header_t *propchanges;
364251881Speter
365251881Speter  /* A boolean indicating whether this node or one of its children has
366251881Speter     received any 'real' changes. Used to avoid tree conflicts for simple
367251881Speter     entryprop changes, like lock management */
368251881Speter  svn_boolean_t edited;
369251881Speter
370251881Speter  /* The tree conflict to install once the node is really edited */
371251881Speter  svn_skel_t *edit_conflict;
372251881Speter
373251881Speter  /* The bump information for this directory. */
374251881Speter  struct bump_dir_info *bump_info;
375251881Speter
376251881Speter  /* The depth of the directory in the wc (or inferred if added).  Not
377251881Speter     used for filtering; we have a separate wrapping editor for that. */
378251881Speter  svn_depth_t ambient_depth;
379251881Speter
380251881Speter  /* Was the directory marked as incomplete before the update?
381251881Speter     (In other words, are we resuming an interrupted update?)
382251881Speter
383251881Speter     If WAS_INCOMPLETE is set to TRUE we expect to receive all child nodes
384251881Speter     and properties for/of the directory. If WAS_INCOMPLETE is FALSE then
385251881Speter     we only receive the changes in/for children and properties.*/
386251881Speter  svn_boolean_t was_incomplete;
387251881Speter
388251881Speter  /* The pool in which this baton itself is allocated. */
389251881Speter  apr_pool_t *pool;
390251881Speter
391251881Speter  /* how many nodes are referring to baton? */
392251881Speter  int ref_count;
393251881Speter
394251881Speter};
395251881Speter
396251881Speter
397251881Speterstruct handler_baton
398251881Speter{
399251881Speter  svn_txdelta_window_handler_t apply_handler;
400251881Speter  void *apply_baton;
401251881Speter  apr_pool_t *pool;
402251881Speter  struct file_baton *fb;
403251881Speter
404251881Speter  /* Where we are assembling the new file. */
405289180Speter  svn_wc__db_install_data_t *install_data;
406251881Speter
407251881Speter    /* The expected source checksum of the text source or NULL if no base
408251881Speter     checksum is available (MD5 if the server provides a checksum, SHA1 if
409251881Speter     the server doesn't) */
410251881Speter  svn_checksum_t *expected_source_checksum;
411251881Speter
412251881Speter  /* Why two checksums?
413251881Speter     The editor currently provides an md5 which we use to detect corruption
414251881Speter     during transmission.  We use the sha1 inside libsvn_wc both for pristine
415251881Speter     handling and corruption detection.  In the future, the editor will also
416251881Speter     provide a sha1, so we may not have to calculate both, but for the time
417251881Speter     being, that's the way it is. */
418251881Speter
419289180Speter  /* The calculated checksum of the text source or NULL if the actual
420251881Speter     checksum is not being calculated. The checksum kind is identical to the
421251881Speter     kind of expected_source_checksum. */
422251881Speter  svn_checksum_t *actual_source_checksum;
423251881Speter
424251881Speter  /* The stream used to calculate the source checksums */
425251881Speter  svn_stream_t *source_checksum_stream;
426251881Speter
427251881Speter  /* A calculated MD5 digest of NEW_TEXT_BASE_TMP_ABSPATH.
428251881Speter     This is initialized to all zeroes when the baton is created, then
429251881Speter     populated with the MD5 digest of the resultant fulltext after the
430251881Speter     last window is handled by the handler returned from
431251881Speter     apply_textdelta(). */
432251881Speter  unsigned char new_text_base_md5_digest[APR_MD5_DIGESTSIZE];
433251881Speter
434251881Speter  /* A calculated SHA-1 of NEW_TEXT_BASE_TMP_ABSPATH, which we'll use for
435251881Speter     eventually writing the pristine. */
436251881Speter  svn_checksum_t * new_text_base_sha1_checksum;
437251881Speter};
438251881Speter
439251881Speter
440251881Speter/* Get an empty file in the temporary area for WRI_ABSPATH.  The file will
441251881Speter   not be set for automatic deletion, and the name will be returned in
442251881Speter   TMP_FILENAME.
443251881Speter
444251881Speter   This implementation creates a new empty file with a unique name.
445251881Speter
446251881Speter   ### This is inefficient for callers that just want an empty file to read
447251881Speter   ### from.  There could be (and there used to be) a permanent, shared
448251881Speter   ### empty file for this purpose.
449251881Speter
450251881Speter   ### This is inefficient for callers that just want to reserve a unique
451251881Speter   ### file name to create later.  A better way may not be readily available.
452251881Speter */
453251881Speterstatic svn_error_t *
454251881Speterget_empty_tmp_file(const char **tmp_filename,
455251881Speter                   svn_wc__db_t *db,
456251881Speter                   const char *wri_abspath,
457251881Speter                   apr_pool_t *result_pool,
458251881Speter                   apr_pool_t *scratch_pool)
459251881Speter{
460251881Speter  const char *temp_dir_abspath;
461251881Speter
462251881Speter  SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, db, wri_abspath,
463251881Speter                                         scratch_pool, scratch_pool));
464251881Speter  SVN_ERR(svn_io_open_unique_file3(NULL, tmp_filename, temp_dir_abspath,
465251881Speter                                   svn_io_file_del_none,
466251881Speter                                   scratch_pool, scratch_pool));
467251881Speter
468251881Speter  return SVN_NO_ERROR;
469251881Speter}
470251881Speter
471251881Speter/* An APR pool cleanup handler.  This runs the working queue for an
472251881Speter   editor baton. */
473251881Speterstatic apr_status_t
474251881Spetercleanup_edit_baton(void *edit_baton)
475251881Speter{
476251881Speter  struct edit_baton *eb = edit_baton;
477251881Speter  svn_error_t *err;
478251881Speter  apr_pool_t *pool = apr_pool_parent_get(eb->pool);
479251881Speter
480251881Speter  err = svn_wc__wq_run(eb->db, eb->wcroot_abspath,
481251881Speter                       NULL /* cancel_func */, NULL /* cancel_baton */,
482251881Speter                       pool);
483251881Speter
484251881Speter  if (err)
485251881Speter    {
486251881Speter      apr_status_t apr_err = err->apr_err;
487251881Speter      svn_error_clear(err);
488251881Speter      return apr_err;
489251881Speter    }
490251881Speter  return APR_SUCCESS;
491251881Speter}
492251881Speter
493289180Speter/* Calculate the new repos_relpath for a directory or file */
494251881Speterstatic svn_error_t *
495289180Spetercalculate_repos_relpath(const char **new_repos_relpath,
496289180Speter                        const char *local_abspath,
497289180Speter                        const char *old_repos_relpath,
498289180Speter                        struct edit_baton *eb,
499289180Speter                        struct dir_baton *pb,
500289180Speter                        apr_pool_t *result_pool,
501289180Speter                        apr_pool_t *scratch_pool)
502251881Speter{
503289180Speter  const char *name = svn_dirent_basename(local_abspath, NULL);
504251881Speter
505289180Speter  /* Figure out the new_repos_relpath for this directory. */
506289180Speter  if (eb->switch_repos_relpath)
507251881Speter    {
508251881Speter      /* Handle switches... */
509251881Speter
510251881Speter      if (pb == NULL)
511251881Speter        {
512251881Speter          if (*eb->target_basename == '\0')
513251881Speter            {
514251881Speter              /* No parent baton and target_basename=="" means that we are
515289180Speter                 the target of the switch. Thus, our new_repos_relpath will be
516289180Speter                 the switch_repos_relpath.  */
517289180Speter              *new_repos_relpath = eb->switch_repos_relpath;
518251881Speter            }
519251881Speter          else
520251881Speter            {
521251881Speter              /* This node is NOT the target of the switch (one of our
522251881Speter                 children is the target); therefore, it must already exist.
523251881Speter                 Get its old REPOS_RELPATH, as it won't be changing.  */
524289180Speter              *new_repos_relpath = apr_pstrdup(result_pool, old_repos_relpath);
525251881Speter            }
526251881Speter        }
527251881Speter      else
528251881Speter        {
529251881Speter          /* This directory is *not* the root (has a parent). If there is
530251881Speter             no grandparent, then we may have anchored at the parent,
531251881Speter             and self is the target. If we match the target, then set
532289180Speter             new_repos_relpath to the switch_repos_relpath.
533251881Speter
534289180Speter             Otherwise, we simply extend new_repos_relpath from the parent.  */
535289180Speter
536251881Speter          if (pb->parent_baton == NULL
537289180Speter              && strcmp(eb->target_basename, name) == 0)
538289180Speter            *new_repos_relpath = eb->switch_repos_relpath;
539251881Speter          else
540289180Speter            *new_repos_relpath = svn_relpath_join(pb->new_repos_relpath, name,
541289180Speter                                                  result_pool);
542251881Speter        }
543251881Speter    }
544251881Speter  else  /* must be an update */
545251881Speter    {
546251881Speter      /* If we are adding the node, then simply extend the parent's
547251881Speter         relpath for our own.  */
548289180Speter      if (old_repos_relpath == NULL)
549251881Speter        {
550251881Speter          SVN_ERR_ASSERT(pb != NULL);
551289180Speter          *new_repos_relpath = svn_relpath_join(pb->new_repos_relpath, name,
552289180Speter                                                result_pool);
553251881Speter        }
554251881Speter      else
555251881Speter        {
556289180Speter          *new_repos_relpath = apr_pstrdup(result_pool, old_repos_relpath);
557251881Speter        }
558251881Speter    }
559251881Speter
560289180Speter  return SVN_NO_ERROR;
561289180Speter}
562289180Speter
563289180Speter/* Make a new dir baton in a subpool of PB->pool. PB is the parent baton.
564289180Speter   If PATH and PB are NULL, this is the root directory of the edit; in this
565289180Speter   case, make the new dir baton in a subpool of EB->pool.
566289180Speter   ADDING should be TRUE if we are adding this directory.  */
567289180Speterstatic svn_error_t *
568289180Spetermake_dir_baton(struct dir_baton **d_p,
569289180Speter               const char *path,
570289180Speter               struct edit_baton *eb,
571289180Speter               struct dir_baton *pb,
572289180Speter               svn_boolean_t adding,
573289180Speter               apr_pool_t *scratch_pool)
574289180Speter{
575289180Speter  apr_pool_t *dir_pool;
576289180Speter  struct dir_baton *d;
577289180Speter
578289180Speter  if (pb != NULL)
579289180Speter    dir_pool = svn_pool_create(pb->pool);
580289180Speter  else
581289180Speter    dir_pool = svn_pool_create(eb->pool);
582289180Speter
583289180Speter  SVN_ERR_ASSERT(path || (! pb));
584289180Speter
585289180Speter  /* Okay, no easy out, so allocate and initialize a dir baton. */
586289180Speter  d = apr_pcalloc(dir_pool, sizeof(*d));
587289180Speter
588289180Speter  /* Construct the PATH and baseNAME of this directory. */
589289180Speter  if (path)
590289180Speter    {
591289180Speter      d->name = svn_dirent_basename(path, dir_pool);
592289180Speter      SVN_ERR(path_join_under_root(&d->local_abspath,
593289180Speter                                   pb->local_abspath, d->name, dir_pool));
594289180Speter    }
595289180Speter  else
596289180Speter    {
597289180Speter      /* This is the root baton. */
598289180Speter      d->name = NULL;
599289180Speter      d->local_abspath = eb->anchor_abspath;
600289180Speter    }
601289180Speter
602251881Speter  d->edit_baton   = eb;
603251881Speter  d->parent_baton = pb;
604251881Speter  d->pool         = dir_pool;
605251881Speter  d->propchanges  = apr_array_make(dir_pool, 1, sizeof(svn_prop_t));
606251881Speter  d->obstruction_found = FALSE;
607251881Speter  d->add_existed  = FALSE;
608251881Speter  d->ref_count = 1;
609251881Speter  d->old_revision = SVN_INVALID_REVNUM;
610251881Speter  d->adding_dir   = adding;
611251881Speter  d->changed_rev  = SVN_INVALID_REVNUM;
612289180Speter  d->not_present_nodes = apr_hash_make(dir_pool);
613251881Speter
614251881Speter  /* Copy some flags from the parent baton */
615251881Speter  if (pb)
616251881Speter    {
617251881Speter      d->skip_this = pb->skip_this;
618251881Speter      d->shadowed = pb->shadowed || pb->edit_obstructed;
619251881Speter
620251881Speter      /* the parent's bump info has one more referer */
621251881Speter      pb->ref_count++;
622251881Speter    }
623251881Speter
624251881Speter  /* The caller of this function needs to fill these in. */
625251881Speter  d->ambient_depth = svn_depth_unknown;
626251881Speter  d->was_incomplete = FALSE;
627251881Speter
628251881Speter  *d_p = d;
629251881Speter  return SVN_NO_ERROR;
630251881Speter}
631251881Speter
632251881Speter/* Forward declarations. */
633251881Speterstatic svn_error_t *
634251881Speteralready_in_a_tree_conflict(svn_boolean_t *conflicted,
635251881Speter                           svn_boolean_t *ignored,
636251881Speter                           svn_wc__db_t *db,
637251881Speter                           const char *local_abspath,
638251881Speter                           apr_pool_t *scratch_pool);
639251881Speter
640251881Speter
641251881Speterstatic void
642251881Speterdo_notification(const struct edit_baton *eb,
643251881Speter                const char *local_abspath,
644251881Speter                svn_node_kind_t kind,
645251881Speter                svn_wc_notify_action_t action,
646251881Speter                apr_pool_t *scratch_pool)
647251881Speter{
648251881Speter  svn_wc_notify_t *notify;
649251881Speter
650251881Speter  if (eb->notify_func == NULL)
651251881Speter    return;
652251881Speter
653251881Speter  notify = svn_wc_create_notify(local_abspath, action, scratch_pool);
654251881Speter  notify->kind = kind;
655251881Speter
656251881Speter  (*eb->notify_func)(eb->notify_baton, notify, scratch_pool);
657251881Speter}
658251881Speter
659251881Speter/* Decrement the directory's reference count. If it hits zero,
660251881Speter   then this directory is "done". This means it is safe to clear its pool.
661251881Speter
662251881Speter   In addition, when the directory is "done", we recurse to possible cleanup
663251881Speter   the parent directory.
664251881Speter*/
665251881Speterstatic svn_error_t *
666251881Spetermaybe_release_dir_info(struct dir_baton *db)
667251881Speter{
668251881Speter  db->ref_count--;
669251881Speter
670251881Speter  if (!db->ref_count)
671251881Speter    {
672251881Speter      struct dir_baton *pb = db->parent_baton;
673251881Speter
674251881Speter      svn_pool_destroy(db->pool);
675251881Speter
676251881Speter      if (pb)
677251881Speter        SVN_ERR(maybe_release_dir_info(pb));
678251881Speter    }
679251881Speter
680251881Speter  return SVN_NO_ERROR;
681251881Speter}
682251881Speter
683251881Speter/* Per file baton. Lives in its own subpool below the pool of the parent
684251881Speter   directory */
685251881Speterstruct file_baton
686251881Speter{
687251881Speter  /* Pool specific to this file_baton. */
688251881Speter  apr_pool_t *pool;
689251881Speter
690251881Speter  /* Name of this file (its entry in the directory). */
691251881Speter  const char *name;
692251881Speter
693251881Speter  /* Absolute path to this file */
694251881Speter  const char *local_abspath;
695251881Speter
696251881Speter  /* The repository relative path this file will correspond to. */
697289180Speter  const char *new_repos_relpath;
698251881Speter
699251881Speter  /* The revision of the file before updating */
700251881Speter  svn_revnum_t old_revision;
701251881Speter
702251881Speter  /* The repos_relpath before updating/switching */
703251881Speter  const char *old_repos_relpath;
704251881Speter
705251881Speter  /* The global edit baton. */
706251881Speter  struct edit_baton *edit_baton;
707251881Speter
708251881Speter  /* The parent directory of this file. */
709251881Speter  struct dir_baton *dir_baton;
710251881Speter
711251881Speter  /* Set if updates to this directory are skipped */
712251881Speter  svn_boolean_t skip_this;
713251881Speter
714251881Speter  /* Set if there was a previous notification  */
715251881Speter  svn_boolean_t already_notified;
716251881Speter
717251881Speter  /* Set if this file is new. */
718251881Speter  svn_boolean_t adding_file;
719251881Speter
720251881Speter  /* Set if an unversioned file of the same name already existed in
721251881Speter     this directory. */
722251881Speter  svn_boolean_t obstruction_found;
723251881Speter
724251881Speter  /* Set if a file of the same name already exists and is
725251881Speter     scheduled for addition without history. */
726251881Speter  svn_boolean_t add_existed;
727251881Speter
728251881Speter  /* Set if this file is being added in the BASE layer, but is not-present
729251881Speter     in the working copy (replaced, deleted, etc.). */
730251881Speter  svn_boolean_t shadowed;
731251881Speter
732251881Speter  /* Set on a node when the existing node is obstructed, and the edit operation
733251881Speter     continues as semi-shadowed update */
734251881Speter  svn_boolean_t edit_obstructed;
735251881Speter
736251881Speter  /* The (new) changed_* information, cached to avoid retrieving it later */
737251881Speter  svn_revnum_t changed_rev;
738251881Speter  apr_time_t changed_date;
739251881Speter  const char *changed_author;
740251881Speter
741251881Speter  /* If there are file content changes, these are the checksums of the
742251881Speter     resulting new text base, which is in the pristine store, else NULL. */
743251881Speter  const svn_checksum_t *new_text_base_md5_checksum;
744251881Speter  const svn_checksum_t *new_text_base_sha1_checksum;
745251881Speter
746251881Speter  /* The checksum of the file before the update */
747251881Speter  const svn_checksum_t *original_checksum;
748251881Speter
749251881Speter  /* An array of svn_prop_t structures, representing all the property
750251881Speter     changes to be applied to this file.  Once a file baton is
751251881Speter     initialized, this is never NULL, but it may have zero elements.  */
752251881Speter  apr_array_header_t *propchanges;
753251881Speter
754251881Speter  /* For existing files, whether there are local modifications. FALSE for added
755251881Speter     files */
756251881Speter  svn_boolean_t local_prop_mods;
757251881Speter
758251881Speter  /* Bump information for the directory this file lives in */
759251881Speter  struct bump_dir_info *bump_info;
760251881Speter
761251881Speter  /* A boolean indicating whether this node or one of its children has
762251881Speter     received any 'real' changes. Used to avoid tree conflicts for simple
763251881Speter     entryprop changes, like lock management */
764251881Speter  svn_boolean_t edited;
765251881Speter
766251881Speter  /* The tree conflict to install once the node is really edited */
767251881Speter  svn_skel_t *edit_conflict;
768251881Speter};
769251881Speter
770251881Speter
771251881Speter/* Make a new file baton in a subpool of PB->pool. PB is the parent baton.
772251881Speter * PATH is relative to the root of the edit. ADDING tells whether this file
773251881Speter * is being added. */
774251881Speterstatic svn_error_t *
775251881Spetermake_file_baton(struct file_baton **f_p,
776251881Speter                struct dir_baton *pb,
777251881Speter                const char *path,
778251881Speter                svn_boolean_t adding,
779251881Speter                apr_pool_t *scratch_pool)
780251881Speter{
781251881Speter  apr_pool_t *file_pool = svn_pool_create(pb->pool);
782251881Speter  struct file_baton *f = apr_pcalloc(file_pool, sizeof(*f));
783251881Speter
784251881Speter  SVN_ERR_ASSERT(path);
785251881Speter
786251881Speter  /* Make the file's on-disk name. */
787251881Speter  f->name = svn_dirent_basename(path, file_pool);
788251881Speter  f->old_revision = SVN_INVALID_REVNUM;
789251881Speter  SVN_ERR(path_join_under_root(&f->local_abspath,
790251881Speter                               pb->local_abspath, f->name, file_pool));
791251881Speter
792251881Speter  f->pool              = file_pool;
793251881Speter  f->edit_baton        = pb->edit_baton;
794251881Speter  f->propchanges       = apr_array_make(file_pool, 1, sizeof(svn_prop_t));
795251881Speter  f->bump_info         = pb->bump_info;
796251881Speter  f->adding_file       = adding;
797251881Speter  f->obstruction_found = FALSE;
798251881Speter  f->add_existed       = FALSE;
799251881Speter  f->skip_this         = pb->skip_this;
800251881Speter  f->shadowed          = pb->shadowed || pb->edit_obstructed;
801251881Speter  f->dir_baton         = pb;
802251881Speter  f->changed_rev       = SVN_INVALID_REVNUM;
803251881Speter
804251881Speter  /* the directory has one more referer now */
805251881Speter  pb->ref_count++;
806251881Speter
807251881Speter  *f_p = f;
808251881Speter  return SVN_NO_ERROR;
809251881Speter}
810251881Speter
811251881Speter/* Complete a conflict skel by describing the update.
812251881Speter *
813251881Speter * LOCAL_KIND is the node kind of the tree conflict victim in the
814251881Speter * working copy.
815251881Speter *
816251881Speter * All temporary allocations are be made in SCRATCH_POOL, while allocations
817251881Speter * needed for the returned conflict struct are made in RESULT_POOL.
818251881Speter */
819251881Speterstatic svn_error_t *
820251881Spetercomplete_conflict(svn_skel_t *conflict,
821251881Speter                  const struct edit_baton *eb,
822251881Speter                  const char *local_abspath,
823251881Speter                  const char *old_repos_relpath,
824251881Speter                  svn_revnum_t old_revision,
825251881Speter                  const char *new_repos_relpath,
826251881Speter                  svn_node_kind_t local_kind,
827251881Speter                  svn_node_kind_t target_kind,
828289180Speter                  const svn_skel_t *delete_conflict,
829251881Speter                  apr_pool_t *result_pool,
830251881Speter                  apr_pool_t *scratch_pool)
831251881Speter{
832289180Speter  const svn_wc_conflict_version_t *original_version = NULL;
833251881Speter  svn_wc_conflict_version_t *target_version;
834251881Speter  svn_boolean_t is_complete;
835251881Speter
836289180Speter  SVN_ERR_ASSERT(new_repos_relpath);
837289180Speter
838251881Speter  if (!conflict)
839251881Speter    return SVN_NO_ERROR; /* Not conflicted */
840251881Speter
841251881Speter  SVN_ERR(svn_wc__conflict_skel_is_complete(&is_complete, conflict));
842251881Speter
843251881Speter  if (is_complete)
844251881Speter    return SVN_NO_ERROR; /* Already completed */
845251881Speter
846251881Speter  if (old_repos_relpath)
847251881Speter    original_version = svn_wc_conflict_version_create2(eb->repos_root,
848251881Speter                                                       eb->repos_uuid,
849251881Speter                                                       old_repos_relpath,
850251881Speter                                                       old_revision,
851251881Speter                                                       local_kind,
852251881Speter                                                       result_pool);
853289180Speter  else if (delete_conflict)
854289180Speter    {
855289180Speter      const apr_array_header_t *locations;
856251881Speter
857289180Speter      SVN_ERR(svn_wc__conflict_read_info(NULL, &locations, NULL, NULL, NULL,
858289180Speter                                         eb->db, local_abspath,
859289180Speter                                         delete_conflict,
860289180Speter                                         scratch_pool, scratch_pool));
861251881Speter
862289180Speter      if (locations)
863289180Speter        {
864289180Speter          original_version = APR_ARRAY_IDX(locations, 0,
865289180Speter                                           const svn_wc_conflict_version_t *);
866289180Speter        }
867289180Speter    }
868289180Speter
869289180Speter  target_version = svn_wc_conflict_version_create2(eb->repos_root,
870289180Speter                                                   eb->repos_uuid,
871289180Speter                                                   new_repos_relpath,
872289180Speter                                                   *eb->target_revision,
873289180Speter                                                   target_kind,
874289180Speter                                                   result_pool);
875289180Speter
876289180Speter  if (eb->switch_repos_relpath)
877251881Speter    SVN_ERR(svn_wc__conflict_skel_set_op_switch(conflict,
878251881Speter                                                original_version,
879251881Speter                                                target_version,
880251881Speter                                                result_pool, scratch_pool));
881251881Speter  else
882251881Speter    SVN_ERR(svn_wc__conflict_skel_set_op_update(conflict,
883251881Speter                                                original_version,
884251881Speter                                                target_version,
885251881Speter                                                result_pool, scratch_pool));
886251881Speter
887251881Speter  return SVN_NO_ERROR;
888251881Speter}
889251881Speter
890251881Speter
891251881Speter/* Called when a directory is really edited, to avoid marking a
892251881Speter   tree conflict on a node for a no-change edit */
893251881Speterstatic svn_error_t *
894251881Spetermark_directory_edited(struct dir_baton *db, apr_pool_t *scratch_pool)
895251881Speter{
896251881Speter  if (db->edited)
897251881Speter    return SVN_NO_ERROR;
898251881Speter
899251881Speter  if (db->parent_baton)
900251881Speter    SVN_ERR(mark_directory_edited(db->parent_baton, scratch_pool));
901251881Speter
902251881Speter  db->edited = TRUE;
903251881Speter
904251881Speter  if (db->edit_conflict)
905251881Speter    {
906251881Speter      /* We have a (delayed) tree conflict to install */
907251881Speter
908251881Speter      SVN_ERR(complete_conflict(db->edit_conflict, db->edit_baton,
909251881Speter                                db->local_abspath,
910251881Speter                                db->old_repos_relpath, db->old_revision,
911289180Speter                                db->new_repos_relpath,
912251881Speter                                svn_node_dir, svn_node_dir,
913289180Speter                                NULL,
914251881Speter                                db->pool, scratch_pool));
915251881Speter      SVN_ERR(svn_wc__db_op_mark_conflict(db->edit_baton->db,
916251881Speter                                          db->local_abspath,
917251881Speter                                          db->edit_conflict, NULL,
918251881Speter                                          scratch_pool));
919251881Speter
920251881Speter      do_notification(db->edit_baton, db->local_abspath, svn_node_dir,
921251881Speter                      svn_wc_notify_tree_conflict, scratch_pool);
922251881Speter      db->already_notified = TRUE;
923251881Speter    }
924251881Speter
925251881Speter  return SVN_NO_ERROR;
926251881Speter}
927251881Speter
928251881Speter/* Called when a file is really edited, to avoid marking a
929251881Speter   tree conflict on a node for a no-change edit */
930251881Speterstatic svn_error_t *
931251881Spetermark_file_edited(struct file_baton *fb, apr_pool_t *scratch_pool)
932251881Speter{
933251881Speter  if (fb->edited)
934251881Speter    return SVN_NO_ERROR;
935251881Speter
936251881Speter  SVN_ERR(mark_directory_edited(fb->dir_baton, scratch_pool));
937251881Speter
938251881Speter  fb->edited = TRUE;
939251881Speter
940251881Speter  if (fb->edit_conflict)
941251881Speter    {
942251881Speter      /* We have a (delayed) tree conflict to install */
943251881Speter
944251881Speter      SVN_ERR(complete_conflict(fb->edit_conflict, fb->edit_baton,
945251881Speter                                fb->local_abspath, fb->old_repos_relpath,
946289180Speter                                fb->old_revision, fb->new_repos_relpath,
947251881Speter                                svn_node_file, svn_node_file,
948289180Speter                                NULL,
949251881Speter                                fb->pool, scratch_pool));
950251881Speter
951251881Speter      SVN_ERR(svn_wc__db_op_mark_conflict(fb->edit_baton->db,
952251881Speter                                          fb->local_abspath,
953251881Speter                                          fb->edit_conflict, NULL,
954251881Speter                                          scratch_pool));
955251881Speter
956251881Speter      do_notification(fb->edit_baton, fb->local_abspath, svn_node_file,
957251881Speter                      svn_wc_notify_tree_conflict, scratch_pool);
958251881Speter      fb->already_notified = TRUE;
959251881Speter    }
960251881Speter
961251881Speter  return SVN_NO_ERROR;
962251881Speter}
963251881Speter
964251881Speter
965251881Speter/* Handle the next delta window of the file described by BATON.  If it is
966251881Speter * the end (WINDOW == NULL), then check the checksum, store the text in the
967251881Speter * pristine store and write its details into BATON->fb->new_text_base_*. */
968251881Speterstatic svn_error_t *
969251881Speterwindow_handler(svn_txdelta_window_t *window, void *baton)
970251881Speter{
971251881Speter  struct handler_baton *hb = baton;
972251881Speter  struct file_baton *fb = hb->fb;
973251881Speter  svn_error_t *err;
974251881Speter
975251881Speter  /* Apply this window.  We may be done at that point.  */
976251881Speter  err = hb->apply_handler(window, hb->apply_baton);
977251881Speter  if (window != NULL && !err)
978251881Speter    return SVN_NO_ERROR;
979251881Speter
980251881Speter  if (hb->expected_source_checksum)
981251881Speter    {
982251881Speter      /* Close the stream to calculate HB->actual_source_md5_checksum. */
983251881Speter      svn_error_t *err2 = svn_stream_close(hb->source_checksum_stream);
984251881Speter
985251881Speter      if (!err2)
986251881Speter        {
987251881Speter          SVN_ERR_ASSERT(hb->expected_source_checksum->kind ==
988251881Speter                        hb->actual_source_checksum->kind);
989251881Speter
990251881Speter          if (!svn_checksum_match(hb->expected_source_checksum,
991251881Speter                                  hb->actual_source_checksum))
992251881Speter            {
993251881Speter              err = svn_error_createf(SVN_ERR_WC_CORRUPT_TEXT_BASE, err,
994251881Speter                        _("Checksum mismatch while updating '%s':\n"
995251881Speter                          "   expected:  %s\n"
996251881Speter                          "     actual:  %s\n"),
997251881Speter                        svn_dirent_local_style(fb->local_abspath, hb->pool),
998251881Speter                        svn_checksum_to_cstring(hb->expected_source_checksum,
999251881Speter                                                hb->pool),
1000251881Speter                        svn_checksum_to_cstring(hb->actual_source_checksum,
1001251881Speter                                                hb->pool));
1002251881Speter            }
1003251881Speter        }
1004251881Speter
1005251881Speter      err = svn_error_compose_create(err, err2);
1006251881Speter    }
1007251881Speter
1008251881Speter  if (err)
1009251881Speter    {
1010257936Speter      /* We failed to apply the delta; clean up the temporary file if it
1011257936Speter         already created by lazy_open_target(). */
1012289180Speter      if (hb->install_data)
1013257936Speter        {
1014289180Speter          svn_error_clear(svn_wc__db_pristine_install_abort(hb->install_data,
1015289180Speter                                                            hb->pool));
1016257936Speter        }
1017251881Speter    }
1018251881Speter  else
1019251881Speter    {
1020251881Speter      /* Tell the file baton about the new text base's checksums. */
1021251881Speter      fb->new_text_base_md5_checksum =
1022251881Speter        svn_checksum__from_digest_md5(hb->new_text_base_md5_digest, fb->pool);
1023251881Speter      fb->new_text_base_sha1_checksum =
1024251881Speter        svn_checksum_dup(hb->new_text_base_sha1_checksum, fb->pool);
1025251881Speter
1026251881Speter      /* Store the new pristine text in the pristine store now.  Later, in a
1027251881Speter         single transaction we will update the BASE_NODE to include a
1028251881Speter         reference to this pristine text's checksum. */
1029289180Speter      SVN_ERR(svn_wc__db_pristine_install(hb->install_data,
1030251881Speter                                          fb->new_text_base_sha1_checksum,
1031251881Speter                                          fb->new_text_base_md5_checksum,
1032251881Speter                                          hb->pool));
1033251881Speter    }
1034251881Speter
1035251881Speter  svn_pool_destroy(hb->pool);
1036251881Speter
1037251881Speter  return err;
1038251881Speter}
1039251881Speter
1040251881Speter
1041251881Speter/* Find the last-change info within ENTRY_PROPS, and return then in the
1042251881Speter   CHANGED_* parameters. Each parameter will be initialized to its "none"
1043251881Speter   value, and will contain the relavent info if found.
1044251881Speter
1045251881Speter   CHANGED_AUTHOR will be allocated in RESULT_POOL. SCRATCH_POOL will be
1046251881Speter   used for some temporary allocations.
1047251881Speter*/
1048251881Speterstatic svn_error_t *
1049251881Speteraccumulate_last_change(svn_revnum_t *changed_rev,
1050251881Speter                       apr_time_t *changed_date,
1051251881Speter                       const char **changed_author,
1052251881Speter                       const apr_array_header_t *entry_props,
1053251881Speter                       apr_pool_t *result_pool,
1054251881Speter                       apr_pool_t *scratch_pool)
1055251881Speter{
1056251881Speter  int i;
1057251881Speter
1058251881Speter  *changed_rev = SVN_INVALID_REVNUM;
1059251881Speter  *changed_date = 0;
1060251881Speter  *changed_author = NULL;
1061251881Speter
1062251881Speter  for (i = 0; i < entry_props->nelts; ++i)
1063251881Speter    {
1064251881Speter      const svn_prop_t *prop = &APR_ARRAY_IDX(entry_props, i, svn_prop_t);
1065251881Speter
1066251881Speter      /* A prop value of NULL means the information was not
1067251881Speter         available.  We don't remove this field from the entries
1068251881Speter         file; we have convention just leave it empty.  So let's
1069251881Speter         just skip those entry props that have no values. */
1070251881Speter      if (! prop->value)
1071251881Speter        continue;
1072251881Speter
1073251881Speter      if (! strcmp(prop->name, SVN_PROP_ENTRY_LAST_AUTHOR))
1074251881Speter        *changed_author = apr_pstrdup(result_pool, prop->value->data);
1075251881Speter      else if (! strcmp(prop->name, SVN_PROP_ENTRY_COMMITTED_REV))
1076251881Speter        {
1077251881Speter          apr_int64_t rev;
1078251881Speter          SVN_ERR(svn_cstring_atoi64(&rev, prop->value->data));
1079251881Speter          *changed_rev = (svn_revnum_t)rev;
1080251881Speter        }
1081251881Speter      else if (! strcmp(prop->name, SVN_PROP_ENTRY_COMMITTED_DATE))
1082251881Speter        SVN_ERR(svn_time_from_cstring(changed_date, prop->value->data,
1083251881Speter                                      scratch_pool));
1084251881Speter
1085251881Speter      /* Starting with Subversion 1.7 we ignore the SVN_PROP_ENTRY_UUID
1086251881Speter         property here. */
1087251881Speter    }
1088251881Speter
1089251881Speter  return SVN_NO_ERROR;
1090251881Speter}
1091251881Speter
1092251881Speter
1093251881Speter/* Join ADD_PATH to BASE_PATH.  If ADD_PATH is absolute, or if any ".."
1094251881Speter * component of it resolves to a path above BASE_PATH, then return
1095251881Speter * SVN_ERR_WC_OBSTRUCTED_UPDATE.
1096251881Speter *
1097251881Speter * This is to prevent the situation where the repository contains,
1098251881Speter * say, "..\nastyfile".  Although that's perfectly legal on some
1099251881Speter * systems, when checked out onto Win32 it would cause "nastyfile" to
1100251881Speter * be created in the parent of the current edit directory.
1101251881Speter *
1102251881Speter * (http://cve.mitre.org/cgi-bin/cvename.cgi?name=2007-3846)
1103251881Speter */
1104251881Speterstatic svn_error_t *
1105251881Speterpath_join_under_root(const char **result_path,
1106251881Speter                     const char *base_path,
1107251881Speter                     const char *add_path,
1108251881Speter                     apr_pool_t *pool)
1109251881Speter{
1110251881Speter  svn_boolean_t under_root;
1111251881Speter
1112251881Speter  SVN_ERR(svn_dirent_is_under_root(&under_root,
1113251881Speter                                   result_path, base_path, add_path, pool));
1114251881Speter
1115251881Speter  if (! under_root)
1116251881Speter    {
1117251881Speter      return svn_error_createf(
1118251881Speter          SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
1119251881Speter          _("Path '%s' is not in the working copy"),
1120251881Speter          svn_dirent_local_style(svn_dirent_join(base_path, add_path, pool),
1121251881Speter                                 pool));
1122251881Speter    }
1123251881Speter
1124251881Speter  /* This catches issue #3288 */
1125251881Speter  if (strcmp(add_path, svn_dirent_basename(*result_path, NULL)) != 0)
1126251881Speter    {
1127251881Speter      return svn_error_createf(
1128251881Speter          SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
1129251881Speter          _("'%s' is not valid as filename in directory '%s'"),
1130251881Speter          svn_dirent_local_style(add_path, pool),
1131251881Speter          svn_dirent_local_style(base_path, pool));
1132251881Speter    }
1133251881Speter
1134251881Speter  return SVN_NO_ERROR;
1135251881Speter}
1136251881Speter
1137251881Speter
1138251881Speter/*** The callbacks we'll plug into an svn_delta_editor_t structure. ***/
1139251881Speter
1140251881Speter/* An svn_delta_editor_t function. */
1141251881Speterstatic svn_error_t *
1142251881Speterset_target_revision(void *edit_baton,
1143251881Speter                    svn_revnum_t target_revision,
1144251881Speter                    apr_pool_t *pool)
1145251881Speter{
1146251881Speter  struct edit_baton *eb = edit_baton;
1147251881Speter
1148251881Speter  *(eb->target_revision) = target_revision;
1149251881Speter  return SVN_NO_ERROR;
1150251881Speter}
1151251881Speter
1152251881Speter/* An svn_delta_editor_t function. */
1153251881Speterstatic svn_error_t *
1154251881Speteropen_root(void *edit_baton,
1155251881Speter          svn_revnum_t base_revision, /* This is ignored in co */
1156251881Speter          apr_pool_t *pool,
1157251881Speter          void **dir_baton)
1158251881Speter{
1159251881Speter  struct edit_baton *eb = edit_baton;
1160251881Speter  struct dir_baton *db;
1161251881Speter  svn_boolean_t already_conflicted, conflict_ignored;
1162251881Speter  svn_error_t *err;
1163251881Speter  svn_wc__db_status_t status;
1164251881Speter  svn_wc__db_status_t base_status;
1165251881Speter  svn_node_kind_t kind;
1166251881Speter  svn_boolean_t have_work;
1167251881Speter
1168251881Speter  /* Note that something interesting is actually happening in this
1169251881Speter     edit run. */
1170251881Speter  eb->root_opened = TRUE;
1171251881Speter
1172251881Speter  SVN_ERR(make_dir_baton(&db, NULL, eb, NULL, FALSE, pool));
1173251881Speter  *dir_baton = db;
1174251881Speter
1175251881Speter  err = already_in_a_tree_conflict(&already_conflicted, &conflict_ignored,
1176251881Speter                                   eb->db, db->local_abspath, pool);
1177251881Speter
1178251881Speter  if (err)
1179251881Speter    {
1180251881Speter      if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
1181251881Speter        return svn_error_trace(err);
1182251881Speter
1183251881Speter      svn_error_clear(err);
1184251881Speter      already_conflicted = conflict_ignored = FALSE;
1185251881Speter    }
1186251881Speter  else if (already_conflicted)
1187251881Speter    {
1188251881Speter      /* Record a skip of both the anchor and target in the skipped tree
1189251881Speter         as the anchor itself might not be updated */
1190251881Speter      SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
1191251881Speter      SVN_ERR(remember_skipped_tree(eb, eb->target_abspath, pool));
1192251881Speter
1193251881Speter      db->skip_this = TRUE;
1194251881Speter      db->already_notified = TRUE;
1195251881Speter
1196251881Speter      /* Notify that we skipped the target, while we actually skipped
1197251881Speter         the anchor */
1198251881Speter      do_notification(eb, eb->target_abspath, svn_node_unknown,
1199251881Speter                      svn_wc_notify_skip_conflicted, pool);
1200251881Speter
1201251881Speter      return SVN_NO_ERROR;
1202251881Speter    }
1203251881Speter
1204251881Speter
1205251881Speter  SVN_ERR(svn_wc__db_read_info(&status, &kind, &db->old_revision,
1206251881Speter                               &db->old_repos_relpath, NULL, NULL,
1207251881Speter                               &db->changed_rev, &db->changed_date,
1208251881Speter                               &db->changed_author, &db->ambient_depth,
1209251881Speter                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1210251881Speter                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1211251881Speter                               NULL, NULL, &have_work,
1212251881Speter                               eb->db, db->local_abspath,
1213251881Speter                               db->pool, pool));
1214251881Speter
1215289180Speter  if (have_work)
1216251881Speter    {
1217289180Speter      SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL,
1218289180Speter                                       &db->old_revision,
1219289180Speter                                       &db->old_repos_relpath, NULL, NULL,
1220289180Speter                                       &db->changed_rev, &db->changed_date,
1221289180Speter                                       &db->changed_author,
1222289180Speter                                       &db->ambient_depth,
1223289180Speter                                       NULL, NULL, NULL, NULL, NULL, NULL,
1224289180Speter                                       eb->db, db->local_abspath,
1225289180Speter                                       db->pool, pool));
1226251881Speter    }
1227289180Speter  else
1228289180Speter    base_status = status;
1229289180Speter
1230289180Speter  SVN_ERR(calculate_repos_relpath(&db->new_repos_relpath, db->local_abspath,
1231289180Speter                                  db->old_repos_relpath, eb, NULL,
1232289180Speter                                  db->pool, pool));
1233289180Speter
1234289180Speter  if (conflict_ignored)
1235289180Speter    db->shadowed = TRUE;
1236251881Speter  else if (have_work)
1237251881Speter    {
1238362181Sdim      const char *move_dst_op_root_abspath;
1239251881Speter      const char *move_src_root_abspath;
1240251881Speter
1241362181Sdim      SVN_ERR(svn_wc__db_base_moved_to(NULL, &move_dst_op_root_abspath,
1242362181Sdim                                       &move_src_root_abspath,
1243251881Speter                                       NULL, eb->db, db->local_abspath,
1244251881Speter                                       pool, pool));
1245251881Speter
1246251881Speter      if (move_src_root_abspath)
1247251881Speter        {
1248251881Speter          /* This is an update anchored inside a move. We need to
1249251881Speter             raise a move-edit tree-conflict on the move root to
1250251881Speter             update the move destination. */
1251251881Speter          svn_skel_t *tree_conflict = svn_wc__conflict_skel_create(pool);
1252251881Speter
1253251881Speter          SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
1254251881Speter                    tree_conflict, eb->db, move_src_root_abspath,
1255251881Speter                    svn_wc_conflict_reason_moved_away,
1256251881Speter                    svn_wc_conflict_action_edit,
1257362181Sdim                    move_src_root_abspath,
1258362181Sdim                    move_dst_op_root_abspath, pool, pool));
1259251881Speter
1260251881Speter          if (strcmp(db->local_abspath, move_src_root_abspath))
1261251881Speter            {
1262251881Speter              /* We are raising the tree-conflict on some parent of
1263251881Speter                 the edit root, we won't be handling that path again
1264251881Speter                 so raise the conflict now. */
1265251881Speter              SVN_ERR(complete_conflict(tree_conflict, eb,
1266251881Speter                                        move_src_root_abspath,
1267251881Speter                                        db->old_repos_relpath,
1268289180Speter                                        db->old_revision,
1269289180Speter                                        db->new_repos_relpath,
1270251881Speter                                        svn_node_dir, svn_node_dir,
1271289180Speter                                        NULL, pool, pool));
1272251881Speter              SVN_ERR(svn_wc__db_op_mark_conflict(eb->db,
1273251881Speter                                                  move_src_root_abspath,
1274251881Speter                                                  tree_conflict,
1275251881Speter                                                  NULL, pool));
1276251881Speter              do_notification(eb, move_src_root_abspath, svn_node_dir,
1277251881Speter                              svn_wc_notify_tree_conflict, pool);
1278251881Speter            }
1279251881Speter          else
1280251881Speter            db->edit_conflict = tree_conflict;
1281251881Speter        }
1282251881Speter
1283251881Speter      db->shadowed = TRUE; /* Needed for the close_directory() on the root, to
1284251881Speter                              make sure it doesn't use the ACTUAL tree */
1285251881Speter    }
1286251881Speter
1287251881Speter  if (*eb->target_basename == '\0')
1288251881Speter    {
1289251881Speter      /* For an update with a NULL target, this is equivalent to open_dir(): */
1290251881Speter
1291251881Speter      db->was_incomplete = (base_status == svn_wc__db_status_incomplete);
1292251881Speter
1293251881Speter      /* ### TODO: Add some tree conflict and obstruction detection, etc. like
1294251881Speter                   open_directory() does.
1295251881Speter                   (or find a way to reuse that code here)
1296251881Speter
1297251881Speter         ### BH 2013: I don't think we need all of the detection here, as the
1298251881Speter                      user explicitly asked to update this node. So we don't
1299251881Speter                      have to tell that it is a local replacement/delete.
1300251881Speter       */
1301251881Speter
1302251881Speter      SVN_ERR(svn_wc__db_temp_op_start_directory_update(eb->db,
1303251881Speter                                                        db->local_abspath,
1304289180Speter                                                        db->new_repos_relpath,
1305251881Speter                                                        *eb->target_revision,
1306251881Speter                                                        pool));
1307251881Speter    }
1308251881Speter
1309251881Speter  return SVN_NO_ERROR;
1310251881Speter}
1311251881Speter
1312251881Speter
1313251881Speter/* ===================================================================== */
1314251881Speter/* Checking for local modifications. */
1315251881Speter
1316251881Speter/* Indicates an unset svn_wc_conflict_reason_t. */
1317251881Speter#define SVN_WC_CONFLICT_REASON_NONE (svn_wc_conflict_reason_t)(-1)
1318251881Speter
1319251881Speter/* Check whether the incoming change ACTION on FULL_PATH would conflict with
1320251881Speter * LOCAL_ABSPATH's scheduled change. If so, then raise a tree conflict with
1321251881Speter * LOCAL_ABSPATH as the victim.
1322251881Speter *
1323251881Speter * The edit baton EB gives information including whether the operation is
1324251881Speter * an update or a switch.
1325251881Speter *
1326251881Speter * WORKING_STATUS is the current node status of LOCAL_ABSPATH
1327251881Speter * and EXISTS_IN_REPOS specifies whether a BASE_NODE representation for exists
1328251881Speter * for this node. In that case the on disk type is compared to EXPECTED_KIND.
1329251881Speter *
1330251881Speter * If a tree conflict reason was found for the incoming action, the resulting
1331251881Speter * tree conflict info is returned in *PCONFLICT. PCONFLICT must be non-NULL,
1332251881Speter * while *PCONFLICT is always overwritten.
1333251881Speter *
1334251881Speter * The tree conflict is allocated in RESULT_POOL. Temporary allocations use
1335251881Speter * SCRATCH_POOL.
1336251881Speter */
1337251881Speterstatic svn_error_t *
1338251881Spetercheck_tree_conflict(svn_skel_t **pconflict,
1339251881Speter                    struct edit_baton *eb,
1340251881Speter                    const char *local_abspath,
1341251881Speter                    svn_wc__db_status_t working_status,
1342251881Speter                    svn_boolean_t exists_in_repos,
1343251881Speter                    svn_node_kind_t expected_kind,
1344251881Speter                    svn_wc_conflict_action_t action,
1345251881Speter                    apr_pool_t *result_pool,
1346251881Speter                    apr_pool_t *scratch_pool)
1347251881Speter{
1348251881Speter  svn_wc_conflict_reason_t reason = SVN_WC_CONFLICT_REASON_NONE;
1349251881Speter  svn_boolean_t modified = FALSE;
1350251881Speter  const char *move_src_op_root_abspath = NULL;
1351362181Sdim  const char *move_dst_op_root_abspath = NULL;
1352251881Speter
1353251881Speter  *pconflict = NULL;
1354251881Speter
1355251881Speter  /* Find out if there are any local changes to this node that may
1356251881Speter   * be the "reason" of a tree-conflict with the incoming "action". */
1357251881Speter  switch (working_status)
1358251881Speter    {
1359251881Speter      case svn_wc__db_status_added:
1360251881Speter      case svn_wc__db_status_moved_here:
1361251881Speter      case svn_wc__db_status_copied:
1362251881Speter        if (!exists_in_repos)
1363251881Speter          {
1364251881Speter            /* The node is locally added, and it did not exist before.  This
1365251881Speter             * is an 'update', so the local add can only conflict with an
1366251881Speter             * incoming 'add'.  In fact, if we receive anything else than an
1367251881Speter             * svn_wc_conflict_action_add (which includes 'added',
1368251881Speter             * 'copied-here' and 'moved-here') during update on a node that
1369251881Speter             * did not exist before, then something is very wrong.
1370251881Speter             * Note that if there was no action on the node, this code
1371251881Speter             * would not have been called in the first place. */
1372251881Speter            SVN_ERR_ASSERT(action == svn_wc_conflict_action_add);
1373251881Speter
1374251881Speter            /* Scan the addition in case our caller didn't. */
1375251881Speter            if (working_status == svn_wc__db_status_added)
1376251881Speter              SVN_ERR(svn_wc__db_scan_addition(&working_status, NULL, NULL,
1377251881Speter                                               NULL, NULL, NULL, NULL,
1378251881Speter                                               NULL, NULL,
1379251881Speter                                               eb->db, local_abspath,
1380251881Speter                                               scratch_pool, scratch_pool));
1381251881Speter
1382251881Speter            if (working_status == svn_wc__db_status_moved_here)
1383251881Speter              reason = svn_wc_conflict_reason_moved_here;
1384251881Speter            else
1385251881Speter              reason = svn_wc_conflict_reason_added;
1386251881Speter          }
1387251881Speter        else
1388251881Speter          {
1389289180Speter            /* The node is locally replaced but could also be moved-away,
1390289180Speter               but we can't report that it is moved away and replaced.
1391289180Speter
1392289180Speter               And we wouldn't be able to store that each of a dozen
1393289180Speter               descendants was moved to other locations...
1394289180Speter
1395289180Speter               Replaced is what actually happened... */
1396289180Speter
1397289180Speter            reason = svn_wc_conflict_reason_replaced;
1398251881Speter          }
1399251881Speter        break;
1400251881Speter
1401251881Speter
1402251881Speter      case svn_wc__db_status_deleted:
1403251881Speter        {
1404362181Sdim          SVN_ERR(svn_wc__db_base_moved_to(NULL, &move_dst_op_root_abspath,
1405362181Sdim                                           NULL, &move_src_op_root_abspath,
1406251881Speter                                           eb->db, local_abspath,
1407251881Speter                                           scratch_pool, scratch_pool));
1408251881Speter          if (move_src_op_root_abspath)
1409251881Speter            reason = svn_wc_conflict_reason_moved_away;
1410251881Speter          else
1411251881Speter            reason = svn_wc_conflict_reason_deleted;
1412251881Speter        }
1413251881Speter        break;
1414251881Speter
1415251881Speter      case svn_wc__db_status_incomplete:
1416251881Speter        /* We used svn_wc__db_read_info(), so 'incomplete' means
1417251881Speter         * - there is no node in the WORKING tree
1418251881Speter         * - a BASE node is known to exist
1419251881Speter         * So the node exists and is essentially 'normal'. We still need to
1420251881Speter         * check prop and text mods, and those checks will retrieve the
1421251881Speter         * missing information (hopefully). */
1422251881Speter      case svn_wc__db_status_normal:
1423251881Speter        if (action == svn_wc_conflict_action_edit)
1424251881Speter          {
1425251881Speter            /* An edit onto a local edit or onto *no* local changes is no
1426251881Speter             * tree-conflict. (It's possibly a text- or prop-conflict,
1427251881Speter             * but we don't handle those here.)
1428251881Speter             *
1429251881Speter             * Except when there is a local obstruction
1430251881Speter             */
1431251881Speter            if (exists_in_repos)
1432251881Speter              {
1433251881Speter                svn_node_kind_t disk_kind;
1434251881Speter
1435251881Speter                SVN_ERR(svn_io_check_path(local_abspath, &disk_kind,
1436251881Speter                                          scratch_pool));
1437251881Speter
1438251881Speter                if (disk_kind != expected_kind && disk_kind != svn_node_none)
1439251881Speter                  {
1440251881Speter                    reason = svn_wc_conflict_reason_obstructed;
1441251881Speter                    break;
1442251881Speter                  }
1443251881Speter
1444251881Speter              }
1445251881Speter            return SVN_NO_ERROR;
1446251881Speter          }
1447251881Speter
1448251881Speter        /* Replace is handled as delete and then specifically in
1449251881Speter           add_directory() and add_file(), so we only expect deletes here */
1450251881Speter        SVN_ERR_ASSERT(action == svn_wc_conflict_action_delete);
1451251881Speter
1452251881Speter        /* Check if the update wants to delete or replace a locally
1453251881Speter         * modified node. */
1454251881Speter
1455251881Speter
1456251881Speter        /* Do a deep tree detection of local changes. The update editor will
1457251881Speter         * not visit the subdirectories of a directory that it wants to delete.
1458251881Speter         * Therefore, we need to start a separate crawl here. */
1459251881Speter
1460289180Speter        SVN_ERR(svn_wc__node_has_local_mods(&modified, NULL,
1461362181Sdim                                            eb->db, local_abspath, TRUE,
1462251881Speter                                            eb->cancel_func, eb->cancel_baton,
1463251881Speter                                            scratch_pool));
1464251881Speter
1465251881Speter        if (modified)
1466251881Speter          {
1467289180Speter            if (working_status == svn_wc__db_status_deleted)
1468251881Speter              reason = svn_wc_conflict_reason_deleted;
1469251881Speter            else
1470251881Speter              reason = svn_wc_conflict_reason_edited;
1471251881Speter          }
1472251881Speter        break;
1473251881Speter
1474251881Speter      case svn_wc__db_status_server_excluded:
1475251881Speter        /* Not allowed to view the node. Not allowed to report tree
1476251881Speter         * conflicts. */
1477251881Speter      case svn_wc__db_status_excluded:
1478251881Speter        /* Locally marked as excluded. No conflicts wanted. */
1479251881Speter      case svn_wc__db_status_not_present:
1480251881Speter        /* A committed delete (but parent not updated). The delete is
1481251881Speter           committed, so no conflict possible during update. */
1482251881Speter        return SVN_NO_ERROR;
1483251881Speter
1484251881Speter      case svn_wc__db_status_base_deleted:
1485251881Speter        /* An internal status. Should never show up here. */
1486251881Speter        SVN_ERR_MALFUNCTION();
1487251881Speter        break;
1488251881Speter
1489251881Speter    }
1490251881Speter
1491251881Speter  if (reason == SVN_WC_CONFLICT_REASON_NONE)
1492251881Speter    /* No conflict with the current action. */
1493251881Speter    return SVN_NO_ERROR;
1494251881Speter
1495251881Speter
1496251881Speter  /* Sanity checks. Note that if there was no action on the node, this function
1497251881Speter   * would not have been called in the first place.*/
1498251881Speter  if (reason == svn_wc_conflict_reason_edited
1499251881Speter      || reason == svn_wc_conflict_reason_obstructed
1500251881Speter      || reason == svn_wc_conflict_reason_deleted
1501251881Speter      || reason == svn_wc_conflict_reason_moved_away
1502251881Speter      || reason == svn_wc_conflict_reason_replaced)
1503251881Speter    {
1504251881Speter      /* When the node existed before (it was locally deleted, replaced or
1505251881Speter       * edited), then 'update' cannot add it "again". So it can only send
1506251881Speter       * _action_edit, _delete or _replace. */
1507251881Speter    if (action != svn_wc_conflict_action_edit
1508251881Speter        && action != svn_wc_conflict_action_delete
1509251881Speter        && action != svn_wc_conflict_action_replace)
1510251881Speter      return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
1511251881Speter               _("Unexpected attempt to add a node at path '%s'"),
1512251881Speter               svn_dirent_local_style(local_abspath, scratch_pool));
1513251881Speter    }
1514251881Speter  else if (reason == svn_wc_conflict_reason_added ||
1515251881Speter           reason == svn_wc_conflict_reason_moved_here)
1516251881Speter    {
1517251881Speter      /* When the node did not exist before (it was locally added),
1518251881Speter       * then 'update' cannot want to modify it in any way.
1519251881Speter       * It can only send _action_add. */
1520251881Speter      if (action != svn_wc_conflict_action_add)
1521251881Speter        return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
1522251881Speter                 _("Unexpected attempt to edit, delete, or replace "
1523251881Speter                   "a node at path '%s'"),
1524251881Speter                 svn_dirent_local_style(local_abspath, scratch_pool));
1525251881Speter
1526251881Speter    }
1527251881Speter
1528251881Speter
1529251881Speter  /* A conflict was detected. Create a conflict skel to record it. */
1530251881Speter  *pconflict = svn_wc__conflict_skel_create(result_pool);
1531251881Speter
1532251881Speter  SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(*pconflict,
1533251881Speter                                                  eb->db, local_abspath,
1534251881Speter                                                  reason,
1535251881Speter                                                  action,
1536251881Speter                                                  move_src_op_root_abspath,
1537362181Sdim                                                  move_dst_op_root_abspath,
1538251881Speter                                                  result_pool, scratch_pool));
1539251881Speter
1540251881Speter  return SVN_NO_ERROR;
1541251881Speter}
1542251881Speter
1543251881Speter
1544251881Speter/* If LOCAL_ABSPATH is inside a conflicted tree and the conflict is
1545251881Speter * not a moved-away-edit conflict, set *CONFLICTED to TRUE.  Otherwise
1546251881Speter * set *CONFLICTED to FALSE.
1547251881Speter */
1548251881Speterstatic svn_error_t *
1549251881Speteralready_in_a_tree_conflict(svn_boolean_t *conflicted,
1550251881Speter                           svn_boolean_t *ignored,
1551251881Speter                           svn_wc__db_t *db,
1552251881Speter                           const char *local_abspath,
1553251881Speter                           apr_pool_t *scratch_pool)
1554251881Speter{
1555251881Speter  const char *ancestor_abspath = local_abspath;
1556251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1557251881Speter
1558251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1559251881Speter
1560251881Speter  *conflicted = *ignored = FALSE;
1561251881Speter
1562251881Speter  while (TRUE)
1563251881Speter    {
1564251881Speter      svn_boolean_t is_wc_root;
1565251881Speter
1566251881Speter      svn_pool_clear(iterpool);
1567251881Speter
1568251881Speter      SVN_ERR(svn_wc__conflicted_for_update_p(conflicted, ignored, db,
1569251881Speter                                              ancestor_abspath, TRUE,
1570251881Speter                                              scratch_pool));
1571251881Speter      if (*conflicted || *ignored)
1572251881Speter        break;
1573251881Speter
1574251881Speter      SVN_ERR(svn_wc__db_is_wcroot(&is_wc_root, db, ancestor_abspath,
1575251881Speter                                   iterpool));
1576251881Speter      if (is_wc_root)
1577251881Speter        break;
1578251881Speter
1579251881Speter      ancestor_abspath = svn_dirent_dirname(ancestor_abspath, scratch_pool);
1580251881Speter    }
1581251881Speter
1582251881Speter  svn_pool_destroy(iterpool);
1583251881Speter
1584251881Speter  return SVN_NO_ERROR;
1585251881Speter}
1586251881Speter
1587251881Speter/* Temporary helper until the new conflict handling is in place */
1588251881Speterstatic svn_error_t *
1589251881Speternode_already_conflicted(svn_boolean_t *conflicted,
1590251881Speter                        svn_boolean_t *conflict_ignored,
1591251881Speter                        svn_wc__db_t *db,
1592251881Speter                        const char *local_abspath,
1593251881Speter                        apr_pool_t *scratch_pool)
1594251881Speter{
1595251881Speter  SVN_ERR(svn_wc__conflicted_for_update_p(conflicted, conflict_ignored, db,
1596251881Speter                                          local_abspath, FALSE,
1597251881Speter                                          scratch_pool));
1598251881Speter
1599251881Speter  return SVN_NO_ERROR;
1600251881Speter}
1601251881Speter
1602251881Speter
1603251881Speter/* An svn_delta_editor_t function. */
1604251881Speterstatic svn_error_t *
1605251881Speterdelete_entry(const char *path,
1606251881Speter             svn_revnum_t revision,
1607251881Speter             void *parent_baton,
1608251881Speter             apr_pool_t *pool)
1609251881Speter{
1610251881Speter  struct dir_baton *pb = parent_baton;
1611251881Speter  struct edit_baton *eb = pb->edit_baton;
1612251881Speter  const char *base = svn_relpath_basename(path, NULL);
1613251881Speter  const char *local_abspath;
1614251881Speter  const char *repos_relpath;
1615289180Speter  const char *deleted_repos_relpath;
1616289180Speter  svn_node_kind_t kind;
1617251881Speter  svn_revnum_t old_revision;
1618251881Speter  svn_boolean_t conflicted;
1619251881Speter  svn_boolean_t have_work;
1620251881Speter  svn_skel_t *tree_conflict = NULL;
1621251881Speter  svn_wc__db_status_t status;
1622251881Speter  svn_wc__db_status_t base_status;
1623251881Speter  apr_pool_t *scratch_pool;
1624251881Speter  svn_boolean_t deleting_target;
1625251881Speter  svn_boolean_t deleting_switched;
1626251881Speter
1627251881Speter  if (pb->skip_this)
1628251881Speter    return SVN_NO_ERROR;
1629251881Speter
1630251881Speter  scratch_pool = svn_pool_create(pb->pool);
1631251881Speter
1632251881Speter  SVN_ERR(mark_directory_edited(pb, scratch_pool));
1633251881Speter
1634251881Speter  SVN_ERR(path_join_under_root(&local_abspath, pb->local_abspath, base,
1635251881Speter                               scratch_pool));
1636251881Speter
1637251881Speter  deleting_target =  (strcmp(local_abspath, eb->target_abspath) == 0);
1638251881Speter
1639251881Speter  /* Detect obstructing working copies */
1640251881Speter  {
1641251881Speter    svn_boolean_t is_root;
1642251881Speter
1643289180Speter
1644251881Speter    SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, local_abspath,
1645251881Speter                                 scratch_pool));
1646251881Speter
1647251881Speter    if (is_root)
1648251881Speter      {
1649251881Speter        /* Just skip this node; a future update will handle it */
1650251881Speter        SVN_ERR(remember_skipped_tree(eb, local_abspath, pool));
1651251881Speter        do_notification(eb, local_abspath, svn_node_unknown,
1652251881Speter                        svn_wc_notify_update_skip_obstruction, scratch_pool);
1653251881Speter
1654251881Speter        svn_pool_destroy(scratch_pool);
1655251881Speter
1656251881Speter        return SVN_NO_ERROR;
1657251881Speter      }
1658251881Speter  }
1659251881Speter
1660251881Speter  SVN_ERR(svn_wc__db_read_info(&status, &kind, &old_revision, &repos_relpath,
1661251881Speter                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1662251881Speter                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1663251881Speter                               &conflicted, NULL, NULL, NULL,
1664251881Speter                               NULL, NULL, &have_work,
1665251881Speter                               eb->db, local_abspath,
1666251881Speter                               scratch_pool, scratch_pool));
1667251881Speter
1668251881Speter  if (!have_work)
1669251881Speter    {
1670251881Speter      base_status = status;
1671251881Speter    }
1672251881Speter  else
1673289180Speter    SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &old_revision,
1674251881Speter                                     &repos_relpath,
1675251881Speter                                     NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1676251881Speter                                     NULL, NULL, NULL, NULL, NULL,
1677251881Speter                                     eb->db, local_abspath,
1678251881Speter                                     scratch_pool, scratch_pool));
1679251881Speter
1680251881Speter  if (pb->old_repos_relpath && repos_relpath)
1681251881Speter    {
1682251881Speter      const char *expected_name;
1683251881Speter
1684251881Speter      expected_name = svn_relpath_skip_ancestor(pb->old_repos_relpath,
1685251881Speter                                                repos_relpath);
1686251881Speter
1687251881Speter      deleting_switched = (!expected_name || strcmp(expected_name, base) != 0);
1688251881Speter    }
1689251881Speter  else
1690251881Speter    deleting_switched = FALSE;
1691251881Speter
1692251881Speter  /* Is this path a conflict victim? */
1693251881Speter  if (pb->shadowed)
1694251881Speter    conflicted = FALSE; /* Conflict applies to WORKING */
1695251881Speter  else if (conflicted)
1696251881Speter    SVN_ERR(node_already_conflicted(&conflicted, NULL,
1697251881Speter                                    eb->db, local_abspath, scratch_pool));
1698251881Speter  if (conflicted)
1699251881Speter    {
1700251881Speter      SVN_ERR(remember_skipped_tree(eb, local_abspath, scratch_pool));
1701251881Speter
1702251881Speter      do_notification(eb, local_abspath, svn_node_unknown,
1703251881Speter                      svn_wc_notify_skip_conflicted,
1704251881Speter                      scratch_pool);
1705251881Speter
1706251881Speter      svn_pool_destroy(scratch_pool);
1707251881Speter
1708251881Speter      return SVN_NO_ERROR;
1709251881Speter    }
1710251881Speter
1711251881Speter
1712251881Speter  /* Receive the remote removal of excluded/server-excluded/not present node.
1713251881Speter     Do not notify, but perform the change even when the node is shadowed */
1714251881Speter  if (base_status == svn_wc__db_status_not_present
1715251881Speter      || base_status == svn_wc__db_status_excluded
1716251881Speter      || base_status == svn_wc__db_status_server_excluded)
1717251881Speter    {
1718289180Speter      SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath, TRUE,
1719289180Speter                                     deleting_target, FALSE,
1720289180Speter                                     *eb->target_revision,
1721251881Speter                                     NULL, NULL,
1722251881Speter                                     scratch_pool));
1723251881Speter
1724251881Speter      if (deleting_target)
1725251881Speter        eb->target_deleted = TRUE;
1726251881Speter
1727251881Speter      svn_pool_destroy(scratch_pool);
1728251881Speter
1729251881Speter      return SVN_NO_ERROR;
1730251881Speter    }
1731251881Speter
1732251881Speter  /* Is this path the victim of a newly-discovered tree conflict?  If so,
1733251881Speter   * remember it and notify the client. Then (if it was existing and
1734251881Speter   * modified), re-schedule the node to be added back again, as a (modified)
1735251881Speter   * copy of the previous base version.  */
1736251881Speter
1737251881Speter  /* Check for conflicts only when we haven't already recorded
1738251881Speter   * a tree-conflict on a parent node. */
1739251881Speter  if (!pb->shadowed && !pb->edit_obstructed)
1740251881Speter    {
1741251881Speter      SVN_ERR(check_tree_conflict(&tree_conflict, eb, local_abspath,
1742251881Speter                                  status, TRUE,
1743289180Speter                                  kind,
1744251881Speter                                  svn_wc_conflict_action_delete,
1745251881Speter                                  pb->pool, scratch_pool));
1746251881Speter    }
1747251881Speter
1748251881Speter  if (tree_conflict != NULL)
1749251881Speter    {
1750251881Speter      /* When we raise a tree conflict on a node, we don't want to mark the
1751251881Speter       * node as skipped, to allow a replacement to continue doing at least
1752251881Speter       * a bit of its work (possibly adding a not present node, for the
1753251881Speter       * next update) */
1754251881Speter      if (!pb->deletion_conflicts)
1755251881Speter        pb->deletion_conflicts = apr_hash_make(pb->pool);
1756251881Speter
1757251881Speter      svn_hash_sets(pb->deletion_conflicts, apr_pstrdup(pb->pool, base),
1758251881Speter                    tree_conflict);
1759251881Speter
1760289180Speter      /* Whatever the kind of conflict, we can just clear BASE
1761289180Speter         by turning whatever is there into a copy */
1762251881Speter    }
1763251881Speter
1764289180Speter  /* Calculate the repository-relative path of the entry which was
1765289180Speter   * deleted. For updates it's the same as REPOS_RELPATH but for
1766289180Speter   * switches it is within the switch target. */
1767289180Speter  SVN_ERR(calculate_repos_relpath(&deleted_repos_relpath, local_abspath,
1768289180Speter                                  repos_relpath, eb, pb, scratch_pool,
1769289180Speter                                  scratch_pool));
1770251881Speter  SVN_ERR(complete_conflict(tree_conflict, eb, local_abspath, repos_relpath,
1771289180Speter                            old_revision, deleted_repos_relpath,
1772289180Speter                            kind, svn_node_none, NULL,
1773251881Speter                            pb->pool, scratch_pool));
1774251881Speter
1775251881Speter  /* Issue a wq operation to delete the BASE_NODE data and to delete actual
1776251881Speter     nodes based on that from disk, but leave any WORKING_NODEs on disk.
1777251881Speter
1778251881Speter     Local modifications are already turned into copies at this point.
1779251881Speter
1780251881Speter     If the thing being deleted is the *target* of this update, then
1781251881Speter     we need to recreate a 'deleted' entry, so that the parent can give
1782251881Speter     accurate reports about itself in the future. */
1783251881Speter  if (! deleting_target && ! deleting_switched)
1784251881Speter    {
1785251881Speter      /* Delete, and do not leave a not-present node.  */
1786251881Speter      SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath,
1787289180Speter                                     (tree_conflict != NULL),
1788289180Speter                                     FALSE, FALSE,
1789251881Speter                                     SVN_INVALID_REVNUM /* not_present_rev */,
1790251881Speter                                     tree_conflict, NULL,
1791251881Speter                                     scratch_pool));
1792251881Speter    }
1793251881Speter  else
1794251881Speter    {
1795251881Speter      /* Delete, leaving a not-present node.  */
1796251881Speter      SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath,
1797289180Speter                                     (tree_conflict != NULL),
1798289180Speter                                     TRUE, FALSE,
1799251881Speter                                     *eb->target_revision,
1800251881Speter                                     tree_conflict, NULL,
1801251881Speter                                     scratch_pool));
1802251881Speter      if (deleting_target)
1803251881Speter        eb->target_deleted = TRUE;
1804251881Speter      else
1805251881Speter        {
1806251881Speter          /* Don't remove the not-present marker at the final bump */
1807251881Speter          SVN_ERR(remember_skipped_tree(eb, local_abspath, pool));
1808251881Speter        }
1809251881Speter    }
1810251881Speter
1811251881Speter  SVN_ERR(svn_wc__wq_run(eb->db, pb->local_abspath,
1812251881Speter                         eb->cancel_func, eb->cancel_baton,
1813251881Speter                         scratch_pool));
1814251881Speter
1815251881Speter  /* Notify. */
1816251881Speter  if (tree_conflict)
1817253734Speter    {
1818253734Speter      if (eb->conflict_func)
1819253734Speter        SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, local_abspath,
1820289180Speter                                                 kind,
1821253734Speter                                                 tree_conflict,
1822253734Speter                                                 NULL /* merge_options */,
1823253734Speter                                                 eb->conflict_func,
1824253734Speter                                                 eb->conflict_baton,
1825253734Speter                                                 eb->cancel_func,
1826253734Speter                                                 eb->cancel_baton,
1827253734Speter                                                 scratch_pool));
1828289180Speter      do_notification(eb, local_abspath, kind,
1829253734Speter                      svn_wc_notify_tree_conflict, scratch_pool);
1830253734Speter    }
1831251881Speter  else
1832251881Speter    {
1833251881Speter      svn_wc_notify_action_t action = svn_wc_notify_update_delete;
1834251881Speter
1835251881Speter      if (pb->shadowed || pb->edit_obstructed)
1836251881Speter        action = svn_wc_notify_update_shadowed_delete;
1837251881Speter
1838289180Speter      do_notification(eb, local_abspath, kind, action, scratch_pool);
1839251881Speter    }
1840251881Speter
1841251881Speter  svn_pool_destroy(scratch_pool);
1842251881Speter
1843251881Speter  return SVN_NO_ERROR;
1844251881Speter}
1845251881Speter
1846251881Speter/* An svn_delta_editor_t function. */
1847251881Speterstatic svn_error_t *
1848251881Speteradd_directory(const char *path,
1849251881Speter              void *parent_baton,
1850251881Speter              const char *copyfrom_path,
1851251881Speter              svn_revnum_t copyfrom_rev,
1852251881Speter              apr_pool_t *pool,
1853251881Speter              void **child_baton)
1854251881Speter{
1855251881Speter  struct dir_baton *pb = parent_baton;
1856251881Speter  struct edit_baton *eb = pb->edit_baton;
1857251881Speter  struct dir_baton *db;
1858289180Speter  apr_pool_t *scratch_pool = svn_pool_create(pool);
1859251881Speter  svn_node_kind_t kind;
1860251881Speter  svn_wc__db_status_t status;
1861251881Speter  svn_node_kind_t wc_kind;
1862251881Speter  svn_boolean_t conflicted;
1863251881Speter  svn_boolean_t conflict_ignored = FALSE;
1864251881Speter  svn_boolean_t versioned_locally_and_present;
1865251881Speter  svn_skel_t *tree_conflict = NULL;
1866251881Speter  svn_error_t *err;
1867251881Speter
1868251881Speter  SVN_ERR_ASSERT(! (copyfrom_path || SVN_IS_VALID_REVNUM(copyfrom_rev)));
1869251881Speter
1870251881Speter  SVN_ERR(make_dir_baton(&db, path, eb, pb, TRUE, pool));
1871251881Speter  *child_baton = db;
1872251881Speter
1873251881Speter  if (db->skip_this)
1874251881Speter    return SVN_NO_ERROR;
1875251881Speter
1876289180Speter  SVN_ERR(calculate_repos_relpath(&db->new_repos_relpath, db->local_abspath,
1877289180Speter                                  NULL, eb, pb, db->pool, scratch_pool));
1878289180Speter
1879251881Speter  SVN_ERR(mark_directory_edited(db, pool));
1880251881Speter
1881251881Speter  if (strcmp(eb->target_abspath, db->local_abspath) == 0)
1882251881Speter    {
1883251881Speter      /* The target of the edit is being added, give it the requested
1884251881Speter         depth of the edit (but convert svn_depth_unknown to
1885251881Speter         svn_depth_infinity). */
1886251881Speter      db->ambient_depth = (eb->requested_depth == svn_depth_unknown)
1887251881Speter        ? svn_depth_infinity : eb->requested_depth;
1888251881Speter    }
1889251881Speter  else if (eb->requested_depth == svn_depth_immediates
1890251881Speter           || (eb->requested_depth == svn_depth_unknown
1891251881Speter               && pb->ambient_depth == svn_depth_immediates))
1892251881Speter    {
1893251881Speter      db->ambient_depth = svn_depth_empty;
1894251881Speter    }
1895251881Speter  else
1896251881Speter    {
1897251881Speter      db->ambient_depth = svn_depth_infinity;
1898251881Speter    }
1899251881Speter
1900251881Speter  /* It may not be named the same as the administrative directory. */
1901251881Speter  if (svn_wc_is_adm_dir(db->name, pool))
1902251881Speter    return svn_error_createf(
1903251881Speter       SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
1904251881Speter       _("Failed to add directory '%s': object of the same name as the "
1905251881Speter         "administrative directory"),
1906251881Speter       svn_dirent_local_style(db->local_abspath, pool));
1907251881Speter
1908289180Speter  if (!eb->clean_checkout)
1909289180Speter    {
1910289180Speter      SVN_ERR(svn_io_check_path(db->local_abspath, &kind, db->pool));
1911251881Speter
1912289180Speter      err = svn_wc__db_read_info(&status, &wc_kind, NULL, NULL, NULL, NULL, NULL,
1913289180Speter                                NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1914289180Speter                                NULL, NULL, NULL, NULL, NULL,
1915289180Speter                                &conflicted, NULL, NULL, NULL, NULL, NULL, NULL,
1916289180Speter                                eb->db, db->local_abspath,
1917289180Speter                                scratch_pool, scratch_pool);
1918289180Speter    }
1919289180Speter  else
1920289180Speter    {
1921289180Speter      kind = svn_node_none;
1922289180Speter      status = svn_wc__db_status_not_present;
1923289180Speter      wc_kind = svn_node_unknown;
1924289180Speter      conflicted = FALSE;
1925289180Speter      err = NULL;
1926289180Speter    }
1927289180Speter
1928251881Speter  if (err)
1929251881Speter    {
1930251881Speter      if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
1931251881Speter        return svn_error_trace(err);
1932251881Speter
1933251881Speter      svn_error_clear(err);
1934251881Speter      wc_kind = svn_node_unknown;
1935251881Speter      status = svn_wc__db_status_normal;
1936251881Speter      conflicted = FALSE;
1937251881Speter
1938251881Speter      versioned_locally_and_present = FALSE;
1939251881Speter    }
1940289180Speter  else if (status == svn_wc__db_status_normal && wc_kind == svn_node_unknown)
1941251881Speter    {
1942289180Speter      SVN_ERR_ASSERT(conflicted);
1943289180Speter      versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */
1944289180Speter    }
1945289180Speter  else if (status == svn_wc__db_status_normal
1946289180Speter           || status == svn_wc__db_status_incomplete)
1947289180Speter    {
1948289180Speter      svn_boolean_t root;
1949251881Speter
1950289180Speter      SVN_ERR(svn_wc__db_is_wcroot(&root, eb->db, db->local_abspath,
1951289180Speter                                   scratch_pool));
1952251881Speter
1953289180Speter      if (root)
1954289180Speter        {
1955289180Speter          /* !! We found the root of a working copy obstructing the wc !!
1956251881Speter
1957289180Speter             If the directory would be part of our own working copy then
1958289180Speter             we wouldn't have been called as an add_directory().
1959251881Speter
1960289180Speter             The only thing we can do is add a not-present node, to allow
1961289180Speter             a future update to bring in the new files when the problem is
1962289180Speter             resolved.  Note that svn_wc__db_base_add_not_present_node()
1963289180Speter             explicitly adds the node into the parent's node database. */
1964251881Speter
1965289180Speter          svn_hash_sets(pb->not_present_nodes,
1966289180Speter                        apr_pstrdup(pb->pool, db->name),
1967289180Speter                        svn_node_kind_to_word(svn_node_dir));
1968289180Speter        }
1969289180Speter      else if (wc_kind == svn_node_dir)
1970289180Speter        {
1971289180Speter          /* We have an editor violation. Github sometimes does this
1972289180Speter             in its subversion compatibility code, when changing the
1973289180Speter             depth of a working copy, or on updates from incomplete */
1974289180Speter        }
1975289180Speter      else
1976289180Speter        {
1977289180Speter          /* We found a file external occupating the place we need in BASE.
1978251881Speter
1979289180Speter            We can't add a not-present node in this case as that would overwrite
1980289180Speter            the file external. Luckily the file external itself stops us from
1981289180Speter            forgetting a child of this parent directory like an obstructing
1982289180Speter            working copy would.
1983251881Speter
1984289180Speter            The reason we get here is that the adm crawler doesn't report
1985289180Speter            file externals.
1986289180Speter          */
1987289180Speter          SVN_ERR_ASSERT(wc_kind == svn_node_file
1988289180Speter                         || wc_kind == svn_node_symlink);
1989289180Speter        }
1990251881Speter
1991289180Speter      SVN_ERR(remember_skipped_tree(eb, db->local_abspath, scratch_pool));
1992251881Speter      db->skip_this = TRUE;
1993251881Speter      db->already_notified = TRUE;
1994251881Speter
1995289180Speter      do_notification(eb, db->local_abspath, wc_kind,
1996289180Speter                      svn_wc_notify_update_skip_obstruction, scratch_pool);
1997251881Speter
1998289180Speter      svn_pool_destroy(scratch_pool);
1999289180Speter
2000251881Speter      return SVN_NO_ERROR;
2001251881Speter    }
2002251881Speter  else
2003251881Speter    versioned_locally_and_present = IS_NODE_PRESENT(status);
2004251881Speter
2005251881Speter  /* Is this path a conflict victim? */
2006251881Speter  if (conflicted)
2007251881Speter    {
2008251881Speter      if (pb->deletion_conflicts)
2009251881Speter        tree_conflict = svn_hash_gets(pb->deletion_conflicts, db->name);
2010251881Speter
2011251881Speter      if (tree_conflict)
2012251881Speter        {
2013251881Speter          svn_wc_conflict_reason_t reason;
2014286506Speter          const char *move_src_op_root_abspath;
2015362181Sdim          const char *move_dst_op_root_abspath;
2016251881Speter          /* So this deletion wasn't just a deletion, it is actually a
2017251881Speter             replacement. Let's install a better tree conflict. */
2018251881Speter
2019286506Speter          SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL,
2020286506Speter                                                      &move_src_op_root_abspath,
2021362181Sdim                                                      &move_dst_op_root_abspath,
2022251881Speter                                                      eb->db,
2023251881Speter                                                      db->local_abspath,
2024251881Speter                                                      tree_conflict,
2025289180Speter                                                      db->pool, scratch_pool));
2026251881Speter
2027251881Speter          tree_conflict = svn_wc__conflict_skel_create(db->pool);
2028251881Speter
2029251881Speter          SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
2030251881Speter                                        tree_conflict,
2031251881Speter                                        eb->db, db->local_abspath,
2032251881Speter                                        reason, svn_wc_conflict_action_replace,
2033286506Speter                                        move_src_op_root_abspath,
2034362181Sdim                                        move_dst_op_root_abspath,
2035289180Speter                                        db->pool, scratch_pool));
2036251881Speter
2037251881Speter          /* And now stop checking for conflicts here and just perform
2038251881Speter             a shadowed update */
2039251881Speter          db->edit_conflict = tree_conflict; /* Cache for close_directory */
2040251881Speter          tree_conflict = NULL; /* No direct notification */
2041251881Speter          db->shadowed = TRUE; /* Just continue */
2042251881Speter          conflicted = FALSE; /* No skip */
2043251881Speter        }
2044251881Speter      else
2045251881Speter        SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
2046289180Speter                                        eb->db, db->local_abspath,
2047289180Speter                                        scratch_pool));
2048251881Speter    }
2049251881Speter
2050251881Speter  /* Now the "usual" behaviour if already conflicted. Skip it. */
2051251881Speter  if (conflicted)
2052251881Speter    {
2053251881Speter      /* Record this conflict so that its descendants are skipped silently. */
2054251881Speter      SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
2055251881Speter
2056251881Speter      db->skip_this = TRUE;
2057251881Speter      db->already_notified = TRUE;
2058251881Speter
2059251881Speter      /* We skip this node, but once the update completes the parent node will
2060251881Speter         be updated to the new revision. So a future recursive update of the
2061251881Speter         parent will not bring in this new node as the revision of the parent
2062251881Speter         describes to the repository that all children are available.
2063251881Speter
2064251881Speter         To resolve this problem, we add a not-present node to allow bringing
2065251881Speter         the node in once this conflict is resolved.
2066251881Speter
2067251881Speter         Note that we can safely assume that no present base node exists,
2068251881Speter         because then we would not have received an add_directory.
2069251881Speter       */
2070289180Speter      svn_hash_sets(pb->not_present_nodes, apr_pstrdup(pb->pool, db->name),
2071289180Speter                    svn_node_kind_to_word(svn_node_dir));
2072251881Speter
2073251881Speter      do_notification(eb, db->local_abspath, svn_node_dir,
2074289180Speter                      svn_wc_notify_skip_conflicted, scratch_pool);
2075289180Speter
2076289180Speter      svn_pool_destroy(scratch_pool);
2077251881Speter      return SVN_NO_ERROR;
2078251881Speter    }
2079251881Speter  else if (conflict_ignored)
2080251881Speter    {
2081251881Speter      db->shadowed = TRUE;
2082251881Speter    }
2083251881Speter
2084251881Speter  if (db->shadowed)
2085251881Speter    {
2086251881Speter      /* Nothing to check; does not and will not exist in working copy */
2087251881Speter    }
2088251881Speter  else if (versioned_locally_and_present)
2089251881Speter    {
2090251881Speter      /* What to do with a versioned or schedule-add dir:
2091251881Speter
2092251881Speter         A dir already added without history is OK.  Set add_existed
2093251881Speter         so that user notification is delayed until after any prop
2094251881Speter         conflicts have been found.
2095251881Speter
2096251881Speter         An existing versioned dir is an error.  In the future we may
2097251881Speter         relax this restriction and simply update such dirs.
2098251881Speter
2099251881Speter         A dir added with history is a tree conflict. */
2100251881Speter
2101251881Speter      svn_boolean_t local_is_non_dir;
2102251881Speter      svn_wc__db_status_t add_status = svn_wc__db_status_normal;
2103251881Speter
2104251881Speter      /* Is the local add a copy? */
2105251881Speter      if (status == svn_wc__db_status_added)
2106251881Speter        SVN_ERR(svn_wc__db_scan_addition(&add_status, NULL, NULL, NULL, NULL,
2107251881Speter                                         NULL, NULL, NULL, NULL,
2108251881Speter                                         eb->db, db->local_abspath,
2109289180Speter                                         scratch_pool, scratch_pool));
2110251881Speter
2111251881Speter
2112251881Speter      /* Is there *something* that is not a dir? */
2113251881Speter      local_is_non_dir = (wc_kind != svn_node_dir
2114251881Speter                          && status != svn_wc__db_status_deleted);
2115251881Speter
2116251881Speter      /* Do tree conflict checking if
2117251881Speter       *  - if there is a local copy.
2118251881Speter       *  - if this is a switch operation
2119251881Speter       *  - the node kinds mismatch
2120251881Speter       *
2121251881Speter       * During switch, local adds at the same path as incoming adds get
2122251881Speter       * "lost" in that switching back to the original will no longer have the
2123251881Speter       * local add. So switch always alerts the user with a tree conflict. */
2124251881Speter      if (!eb->adds_as_modification
2125251881Speter          || local_is_non_dir
2126251881Speter          || add_status != svn_wc__db_status_added)
2127251881Speter        {
2128251881Speter          SVN_ERR(check_tree_conflict(&tree_conflict, eb,
2129251881Speter                                      db->local_abspath,
2130251881Speter                                      status, FALSE, svn_node_none,
2131251881Speter                                      svn_wc_conflict_action_add,
2132289180Speter                                      db->pool, scratch_pool));
2133251881Speter        }
2134251881Speter
2135251881Speter      if (tree_conflict == NULL)
2136251881Speter        db->add_existed = TRUE; /* Take over WORKING */
2137251881Speter      else
2138251881Speter        db->shadowed = TRUE; /* Only update BASE */
2139251881Speter    }
2140251881Speter  else if (kind != svn_node_none)
2141251881Speter    {
2142251881Speter      /* There's an unversioned node at this path. */
2143251881Speter      db->obstruction_found = TRUE;
2144251881Speter
2145251881Speter      /* Unversioned, obstructing dirs are handled by prop merge/conflict,
2146251881Speter       * if unversioned obstructions are allowed. */
2147251881Speter      if (! (kind == svn_node_dir && eb->allow_unver_obstructions))
2148251881Speter        {
2149251881Speter          /* Bring in the node as deleted */ /* ### Obstructed Conflict */
2150251881Speter          db->shadowed = TRUE;
2151251881Speter
2152251881Speter          /* Mark a conflict */
2153251881Speter          tree_conflict = svn_wc__conflict_skel_create(db->pool);
2154251881Speter
2155251881Speter          SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
2156251881Speter                                        tree_conflict,
2157251881Speter                                        eb->db, db->local_abspath,
2158251881Speter                                        svn_wc_conflict_reason_unversioned,
2159362181Sdim                                        svn_wc_conflict_action_add,
2160362181Sdim                                        NULL, NULL, db->pool, scratch_pool));
2161251881Speter          db->edit_conflict = tree_conflict;
2162251881Speter        }
2163251881Speter    }
2164251881Speter
2165251881Speter  if (tree_conflict)
2166251881Speter    SVN_ERR(complete_conflict(tree_conflict, eb, db->local_abspath,
2167251881Speter                              db->old_repos_relpath, db->old_revision,
2168289180Speter                              db->new_repos_relpath,
2169289180Speter                              wc_kind, svn_node_dir,
2170289180Speter                              pb->deletion_conflicts
2171289180Speter                                ? svn_hash_gets(pb->deletion_conflicts,
2172289180Speter                                                db->name)
2173289180Speter                                : NULL,
2174289180Speter                              db->pool, scratch_pool));
2175251881Speter
2176251881Speter  SVN_ERR(svn_wc__db_base_add_incomplete_directory(
2177251881Speter                                     eb->db, db->local_abspath,
2178289180Speter                                     db->new_repos_relpath,
2179251881Speter                                     eb->repos_root,
2180251881Speter                                     eb->repos_uuid,
2181251881Speter                                     *eb->target_revision,
2182251881Speter                                     db->ambient_depth,
2183251881Speter                                     (db->shadowed && db->obstruction_found),
2184251881Speter                                     (! db->shadowed
2185251881Speter                                      && status == svn_wc__db_status_added),
2186251881Speter                                     tree_conflict, NULL,
2187289180Speter                                     scratch_pool));
2188251881Speter
2189251881Speter  /* Make sure there is a real directory at LOCAL_ABSPATH, unless we are just
2190251881Speter     updating the DB */
2191251881Speter  if (!db->shadowed)
2192289180Speter    SVN_ERR(svn_wc__ensure_directory(db->local_abspath, scratch_pool));
2193251881Speter
2194251881Speter  if (tree_conflict != NULL)
2195251881Speter    {
2196289180Speter      db->edit_conflict = tree_conflict;
2197253734Speter
2198251881Speter      db->already_notified = TRUE;
2199251881Speter      do_notification(eb, db->local_abspath, svn_node_dir,
2200289180Speter                      svn_wc_notify_tree_conflict, scratch_pool);
2201251881Speter    }
2202251881Speter
2203251881Speter
2204251881Speter  /* If this add was obstructed by dir scheduled for addition without
2205251881Speter     history let close_directory() handle the notification because there
2206251881Speter     might be properties to deal with.  If PATH was added inside a locally
2207251881Speter     deleted tree, then suppress notification, a tree conflict was already
2208251881Speter     issued. */
2209251881Speter  if (eb->notify_func && !db->already_notified && !db->add_existed)
2210251881Speter    {
2211251881Speter      svn_wc_notify_action_t action;
2212251881Speter
2213251881Speter      if (db->shadowed)
2214251881Speter        action = svn_wc_notify_update_shadowed_add;
2215251881Speter      else if (db->obstruction_found || db->add_existed)
2216251881Speter        action = svn_wc_notify_exists;
2217251881Speter      else
2218251881Speter        action = svn_wc_notify_update_add;
2219251881Speter
2220251881Speter      db->already_notified = TRUE;
2221251881Speter
2222289180Speter      do_notification(eb, db->local_abspath, svn_node_dir, action,
2223289180Speter                      scratch_pool);
2224251881Speter    }
2225251881Speter
2226289180Speter  svn_pool_destroy(scratch_pool);
2227289180Speter
2228251881Speter  return SVN_NO_ERROR;
2229251881Speter}
2230251881Speter
2231251881Speter/* An svn_delta_editor_t function. */
2232251881Speterstatic svn_error_t *
2233251881Speteropen_directory(const char *path,
2234251881Speter               void *parent_baton,
2235251881Speter               svn_revnum_t base_revision,
2236251881Speter               apr_pool_t *pool,
2237251881Speter               void **child_baton)
2238251881Speter{
2239251881Speter  struct dir_baton *db, *pb = parent_baton;
2240251881Speter  struct edit_baton *eb = pb->edit_baton;
2241251881Speter  svn_boolean_t have_work;
2242251881Speter  svn_boolean_t conflicted;
2243251881Speter  svn_boolean_t conflict_ignored = FALSE;
2244251881Speter  svn_skel_t *tree_conflict = NULL;
2245251881Speter  svn_wc__db_status_t status, base_status;
2246251881Speter  svn_node_kind_t wc_kind;
2247251881Speter
2248251881Speter  SVN_ERR(make_dir_baton(&db, path, eb, pb, FALSE, pool));
2249251881Speter  *child_baton = db;
2250251881Speter
2251251881Speter  if (db->skip_this)
2252251881Speter    return SVN_NO_ERROR;
2253251881Speter
2254251881Speter  /* Detect obstructing working copies */
2255251881Speter  {
2256251881Speter    svn_boolean_t is_root;
2257251881Speter
2258251881Speter    SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, db->local_abspath,
2259251881Speter                                 pool));
2260251881Speter
2261251881Speter    if (is_root)
2262251881Speter      {
2263251881Speter        /* Just skip this node; a future update will handle it */
2264251881Speter        SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
2265251881Speter        db->skip_this = TRUE;
2266251881Speter        db->already_notified = TRUE;
2267251881Speter
2268251881Speter        do_notification(eb, db->local_abspath, svn_node_dir,
2269251881Speter                        svn_wc_notify_update_skip_obstruction, pool);
2270251881Speter
2271251881Speter        return SVN_NO_ERROR;
2272251881Speter      }
2273251881Speter  }
2274251881Speter
2275251881Speter  /* We should have a write lock on every directory touched.  */
2276251881Speter  SVN_ERR(svn_wc__write_check(eb->db, db->local_abspath, pool));
2277251881Speter
2278251881Speter  SVN_ERR(svn_wc__db_read_info(&status, &wc_kind, &db->old_revision,
2279251881Speter                               &db->old_repos_relpath, NULL, NULL,
2280251881Speter                               &db->changed_rev, &db->changed_date,
2281251881Speter                               &db->changed_author, &db->ambient_depth,
2282251881Speter                               NULL, NULL, NULL, NULL,
2283251881Speter                               NULL, NULL, NULL, NULL, NULL, NULL,
2284251881Speter                               &conflicted, NULL, NULL, NULL,
2285251881Speter                               NULL, NULL, &have_work,
2286251881Speter                               eb->db, db->local_abspath,
2287251881Speter                               db->pool, pool));
2288251881Speter
2289251881Speter  if (!have_work)
2290251881Speter    base_status = status;
2291251881Speter  else
2292251881Speter    SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &db->old_revision,
2293251881Speter                                     &db->old_repos_relpath, NULL, NULL,
2294251881Speter                                     &db->changed_rev, &db->changed_date,
2295251881Speter                                     &db->changed_author, &db->ambient_depth,
2296251881Speter                                     NULL, NULL, NULL, NULL, NULL, NULL,
2297251881Speter                                     eb->db, db->local_abspath,
2298251881Speter                                     db->pool, pool));
2299251881Speter
2300251881Speter  db->was_incomplete = (base_status == svn_wc__db_status_incomplete);
2301251881Speter
2302289180Speter  SVN_ERR(calculate_repos_relpath(&db->new_repos_relpath, db->local_abspath,
2303289180Speter                                  db->old_repos_relpath, eb, pb,
2304289180Speter                                  db->pool, pool));
2305289180Speter
2306251881Speter  /* Is this path a conflict victim? */
2307251881Speter  if (db->shadowed)
2308251881Speter    conflicted = FALSE; /* Conflict applies to WORKING */
2309251881Speter  else if (conflicted)
2310251881Speter    SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
2311251881Speter                                    eb->db, db->local_abspath, pool));
2312251881Speter  if (conflicted)
2313251881Speter    {
2314251881Speter      SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
2315251881Speter
2316251881Speter      db->skip_this = TRUE;
2317251881Speter      db->already_notified = TRUE;
2318251881Speter
2319251881Speter      do_notification(eb, db->local_abspath, svn_node_unknown,
2320251881Speter                      svn_wc_notify_skip_conflicted, pool);
2321251881Speter
2322251881Speter      return SVN_NO_ERROR;
2323251881Speter    }
2324251881Speter  else if (conflict_ignored)
2325251881Speter    {
2326251881Speter      db->shadowed = TRUE;
2327251881Speter    }
2328251881Speter
2329251881Speter  /* Is this path a fresh tree conflict victim?  If so, skip the tree
2330251881Speter     with one notification. */
2331251881Speter
2332251881Speter  /* Check for conflicts only when we haven't already recorded
2333251881Speter   * a tree-conflict on a parent node. */
2334251881Speter  if (!db->shadowed)
2335251881Speter    SVN_ERR(check_tree_conflict(&tree_conflict, eb, db->local_abspath,
2336251881Speter                                status, TRUE, svn_node_dir,
2337251881Speter                                svn_wc_conflict_action_edit,
2338251881Speter                                db->pool, pool));
2339251881Speter
2340251881Speter  /* Remember the roots of any locally deleted trees. */
2341251881Speter  if (tree_conflict != NULL)
2342251881Speter    {
2343251881Speter      svn_wc_conflict_reason_t reason;
2344251881Speter      db->edit_conflict = tree_conflict;
2345251881Speter      /* Other modifications wouldn't be a tree conflict */
2346251881Speter
2347362181Sdim      SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL, NULL,
2348251881Speter                                                  eb->db, db->local_abspath,
2349251881Speter                                                  tree_conflict,
2350251881Speter                                                  db->pool, db->pool));
2351251881Speter      SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_deleted
2352251881Speter                     || reason == svn_wc_conflict_reason_moved_away
2353251881Speter                     || reason == svn_wc_conflict_reason_replaced
2354251881Speter                     || reason == svn_wc_conflict_reason_obstructed);
2355251881Speter
2356251881Speter      /* Continue updating BASE */
2357251881Speter      if (reason == svn_wc_conflict_reason_obstructed)
2358251881Speter        db->edit_obstructed = TRUE;
2359251881Speter      else
2360251881Speter        db->shadowed = TRUE;
2361251881Speter    }
2362251881Speter
2363251881Speter  /* Mark directory as being at target_revision and URL, but incomplete. */
2364251881Speter  SVN_ERR(svn_wc__db_temp_op_start_directory_update(eb->db, db->local_abspath,
2365289180Speter                                                    db->new_repos_relpath,
2366251881Speter                                                    *eb->target_revision,
2367251881Speter                                                    pool));
2368251881Speter
2369251881Speter  return SVN_NO_ERROR;
2370251881Speter}
2371251881Speter
2372251881Speter
2373251881Speter/* An svn_delta_editor_t function. */
2374251881Speterstatic svn_error_t *
2375251881Speterchange_dir_prop(void *dir_baton,
2376251881Speter                const char *name,
2377251881Speter                const svn_string_t *value,
2378251881Speter                apr_pool_t *pool)
2379251881Speter{
2380251881Speter  svn_prop_t *propchange;
2381251881Speter  struct dir_baton *db = dir_baton;
2382251881Speter
2383251881Speter  if (db->skip_this)
2384251881Speter    return SVN_NO_ERROR;
2385251881Speter
2386251881Speter  propchange = apr_array_push(db->propchanges);
2387251881Speter  propchange->name = apr_pstrdup(db->pool, name);
2388289180Speter  propchange->value = svn_string_dup(value, db->pool);
2389251881Speter
2390251881Speter  if (!db->edited && svn_property_kind2(name) == svn_prop_regular_kind)
2391251881Speter    SVN_ERR(mark_directory_edited(db, pool));
2392251881Speter
2393251881Speter  return SVN_NO_ERROR;
2394251881Speter}
2395251881Speter
2396251881Speter/* If any of the svn_prop_t objects in PROPCHANGES represents a change
2397251881Speter   to the SVN_PROP_EXTERNALS property, return that change, else return
2398251881Speter   null.  If PROPCHANGES contains more than one such change, return
2399251881Speter   the first. */
2400251881Speterstatic const svn_prop_t *
2401251881Speterexternals_prop_changed(const apr_array_header_t *propchanges)
2402251881Speter{
2403251881Speter  int i;
2404251881Speter
2405251881Speter  for (i = 0; i < propchanges->nelts; i++)
2406251881Speter    {
2407251881Speter      const svn_prop_t *p = &(APR_ARRAY_IDX(propchanges, i, svn_prop_t));
2408251881Speter      if (strcmp(p->name, SVN_PROP_EXTERNALS) == 0)
2409251881Speter        return p;
2410251881Speter    }
2411251881Speter
2412251881Speter  return NULL;
2413251881Speter}
2414251881Speter
2415251881Speter
2416251881Speter
2417251881Speter/* An svn_delta_editor_t function. */
2418251881Speterstatic svn_error_t *
2419251881Speterclose_directory(void *dir_baton,
2420251881Speter                apr_pool_t *pool)
2421251881Speter{
2422251881Speter  struct dir_baton *db = dir_baton;
2423251881Speter  struct edit_baton *eb = db->edit_baton;
2424251881Speter  svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
2425251881Speter  apr_array_header_t *entry_prop_changes;
2426251881Speter  apr_array_header_t *dav_prop_changes;
2427251881Speter  apr_array_header_t *regular_prop_changes;
2428251881Speter  apr_hash_t *base_props;
2429251881Speter  apr_hash_t *actual_props;
2430251881Speter  apr_hash_t *new_base_props = NULL;
2431251881Speter  apr_hash_t *new_actual_props = NULL;
2432251881Speter  svn_revnum_t new_changed_rev = SVN_INVALID_REVNUM;
2433251881Speter  apr_time_t new_changed_date = 0;
2434251881Speter  const char *new_changed_author = NULL;
2435251881Speter  apr_pool_t *scratch_pool = db->pool;
2436251881Speter  svn_skel_t *all_work_items = NULL;
2437251881Speter  svn_skel_t *conflict_skel = NULL;
2438251881Speter
2439251881Speter  /* Skip if we're in a conflicted tree. */
2440251881Speter  if (db->skip_this)
2441251881Speter    {
2442251881Speter      /* Allow the parent to complete its update. */
2443251881Speter      SVN_ERR(maybe_release_dir_info(db));
2444251881Speter
2445251881Speter      return SVN_NO_ERROR;
2446251881Speter    }
2447251881Speter
2448251881Speter  if (db->edited)
2449251881Speter    conflict_skel = db->edit_conflict;
2450251881Speter
2451251881Speter  SVN_ERR(svn_categorize_props(db->propchanges, &entry_prop_changes,
2452251881Speter                               &dav_prop_changes, &regular_prop_changes, pool));
2453251881Speter
2454251881Speter  /* Fetch the existing properties.  */
2455251881Speter  if ((!db->adding_dir || db->add_existed)
2456251881Speter      && !db->shadowed)
2457251881Speter    {
2458251881Speter      SVN_ERR(svn_wc__get_actual_props(&actual_props,
2459251881Speter                                       eb->db, db->local_abspath,
2460251881Speter                                       scratch_pool, scratch_pool));
2461251881Speter    }
2462251881Speter  else
2463251881Speter    actual_props = apr_hash_make(pool);
2464251881Speter
2465251881Speter  if (db->add_existed)
2466251881Speter    {
2467251881Speter      /* This node already exists. Grab the current pristine properties. */
2468251881Speter      SVN_ERR(svn_wc__db_read_pristine_props(&base_props,
2469251881Speter                                             eb->db, db->local_abspath,
2470251881Speter                                             scratch_pool, scratch_pool));
2471251881Speter    }
2472251881Speter  else if (!db->adding_dir)
2473251881Speter    {
2474251881Speter      /* Get the BASE properties for proper merging. */
2475251881Speter      SVN_ERR(svn_wc__db_base_get_props(&base_props,
2476251881Speter                                        eb->db, db->local_abspath,
2477251881Speter                                        scratch_pool, scratch_pool));
2478251881Speter    }
2479251881Speter  else
2480251881Speter    base_props = apr_hash_make(pool);
2481251881Speter
2482251881Speter  /* An incomplete directory might have props which were supposed to be
2483251881Speter     deleted but weren't.  Because the server sent us all the props we're
2484251881Speter     supposed to have, any previous base props not in this list must be
2485251881Speter     deleted (issue #1672). */
2486251881Speter  if (db->was_incomplete)
2487251881Speter    {
2488251881Speter      int i;
2489251881Speter      apr_hash_t *props_to_delete;
2490251881Speter      apr_hash_index_t *hi;
2491251881Speter
2492251881Speter      /* In a copy of the BASE props, remove every property that we see an
2493251881Speter         incoming change for. The remaining unmentioned properties are those
2494251881Speter         which need to be deleted.  */
2495251881Speter      props_to_delete = apr_hash_copy(pool, base_props);
2496251881Speter      for (i = 0; i < regular_prop_changes->nelts; i++)
2497251881Speter        {
2498251881Speter          const svn_prop_t *prop;
2499251881Speter          prop = &APR_ARRAY_IDX(regular_prop_changes, i, svn_prop_t);
2500251881Speter          svn_hash_sets(props_to_delete, prop->name, NULL);
2501251881Speter        }
2502251881Speter
2503251881Speter      /* Add these props to the incoming propchanges (in
2504251881Speter       * regular_prop_changes).  */
2505251881Speter      for (hi = apr_hash_first(pool, props_to_delete);
2506251881Speter           hi != NULL;
2507251881Speter           hi = apr_hash_next(hi))
2508251881Speter        {
2509289180Speter          const char *propname = apr_hash_this_key(hi);
2510251881Speter          svn_prop_t *prop = apr_array_push(regular_prop_changes);
2511251881Speter
2512251881Speter          /* Record a deletion for PROPNAME.  */
2513251881Speter          prop->name = propname;
2514251881Speter          prop->value = NULL;
2515251881Speter        }
2516251881Speter    }
2517251881Speter
2518251881Speter  /* If this directory has property changes stored up, now is the time
2519251881Speter     to deal with them. */
2520251881Speter  if (regular_prop_changes->nelts)
2521251881Speter    {
2522251881Speter      /* If recording traversal info, then see if the
2523251881Speter         SVN_PROP_EXTERNALS property on this directory changed,
2524251881Speter         and record before and after for the change. */
2525251881Speter      if (eb->external_func)
2526251881Speter        {
2527251881Speter          const svn_prop_t *change
2528251881Speter            = externals_prop_changed(regular_prop_changes);
2529251881Speter
2530251881Speter          if (change)
2531251881Speter            {
2532251881Speter              const svn_string_t *new_val_s = change->value;
2533251881Speter              const svn_string_t *old_val_s;
2534251881Speter
2535251881Speter              old_val_s = svn_hash_gets(base_props, SVN_PROP_EXTERNALS);
2536251881Speter
2537251881Speter              if ((new_val_s == NULL) && (old_val_s == NULL))
2538251881Speter                ; /* No value before, no value after... so do nothing. */
2539251881Speter              else if (new_val_s && old_val_s
2540251881Speter                       && (svn_string_compare(old_val_s, new_val_s)))
2541251881Speter                ; /* Value did not change... so do nothing. */
2542251881Speter              else if (old_val_s || new_val_s)
2543251881Speter                /* something changed, record the change */
2544251881Speter                {
2545251881Speter                  SVN_ERR((eb->external_func)(
2546251881Speter                                       eb->external_baton,
2547251881Speter                                       db->local_abspath,
2548251881Speter                                       old_val_s,
2549251881Speter                                       new_val_s,
2550251881Speter                                       db->ambient_depth,
2551251881Speter                                       db->pool));
2552251881Speter                }
2553251881Speter            }
2554251881Speter        }
2555251881Speter
2556251881Speter      if (db->shadowed)
2557251881Speter        {
2558251881Speter          /* We don't have a relevant actual row, but we need actual properties
2559251881Speter             to allow property merging without conflicts. */
2560251881Speter          if (db->adding_dir)
2561251881Speter            actual_props = apr_hash_make(scratch_pool);
2562251881Speter          else
2563251881Speter            actual_props = base_props;
2564251881Speter        }
2565251881Speter
2566251881Speter      /* Merge pending properties. */
2567251881Speter      new_base_props = svn_prop__patch(base_props, regular_prop_changes,
2568251881Speter                                       db->pool);
2569251881Speter      SVN_ERR_W(svn_wc__merge_props(&conflict_skel,
2570251881Speter                                    &prop_state,
2571251881Speter                                    &new_actual_props,
2572251881Speter                                    eb->db,
2573251881Speter                                    db->local_abspath,
2574251881Speter                                    NULL /* use baseprops */,
2575251881Speter                                    base_props,
2576251881Speter                                    actual_props,
2577251881Speter                                    regular_prop_changes,
2578251881Speter                                    db->pool,
2579251881Speter                                    scratch_pool),
2580251881Speter                _("Couldn't do property merge"));
2581251881Speter      /* After a (not-dry-run) merge, we ALWAYS have props to save.  */
2582251881Speter      SVN_ERR_ASSERT(new_base_props != NULL && new_actual_props != NULL);
2583251881Speter    }
2584251881Speter
2585251881Speter  SVN_ERR(accumulate_last_change(&new_changed_rev, &new_changed_date,
2586251881Speter                                 &new_changed_author, entry_prop_changes,
2587251881Speter                                 scratch_pool, scratch_pool));
2588251881Speter
2589251881Speter  /* Check if we should add some not-present markers before marking the
2590251881Speter     directory complete (Issue #3569) */
2591251881Speter  {
2592289180Speter    apr_hash_t *new_children = svn_hash_gets(eb->dir_dirents,
2593289180Speter                                             db->new_repos_relpath);
2594251881Speter
2595251881Speter    if (new_children != NULL)
2596251881Speter      {
2597251881Speter        apr_hash_index_t *hi;
2598251881Speter        apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2599251881Speter
2600251881Speter        for (hi = apr_hash_first(scratch_pool, new_children);
2601251881Speter             hi;
2602251881Speter             hi = apr_hash_next(hi))
2603251881Speter          {
2604251881Speter            const char *child_name;
2605251881Speter            const char *child_abspath;
2606251881Speter            const char *child_relpath;
2607251881Speter            const svn_dirent_t *dirent;
2608251881Speter            svn_wc__db_status_t status;
2609251881Speter            svn_node_kind_t child_kind;
2610251881Speter            svn_error_t *err;
2611251881Speter
2612251881Speter            svn_pool_clear(iterpool);
2613251881Speter
2614289180Speter            child_name = apr_hash_this_key(hi);
2615251881Speter            child_abspath = svn_dirent_join(db->local_abspath, child_name,
2616251881Speter                                            iterpool);
2617251881Speter
2618289180Speter            dirent = apr_hash_this_val(hi);
2619251881Speter            child_kind = (dirent->kind == svn_node_dir)
2620251881Speter                                        ? svn_node_dir
2621251881Speter                                        : svn_node_file;
2622251881Speter
2623251881Speter            if (db->ambient_depth < svn_depth_immediates
2624251881Speter                && child_kind == svn_node_dir)
2625251881Speter              continue; /* We don't need the subdirs */
2626251881Speter
2627251881Speter            /* ### We just check if there is some node in BASE at this path */
2628251881Speter            err = svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL,
2629251881Speter                                           NULL, NULL, NULL, NULL, NULL, NULL,
2630251881Speter                                           NULL, NULL, NULL, NULL, NULL,
2631251881Speter                                           eb->db, child_abspath,
2632251881Speter                                           iterpool, iterpool);
2633251881Speter
2634251881Speter            if (!err)
2635251881Speter              {
2636251881Speter                svn_boolean_t is_wcroot;
2637251881Speter                SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, eb->db, child_abspath,
2638251881Speter                                             iterpool));
2639251881Speter
2640251881Speter                if (!is_wcroot)
2641251881Speter                  continue; /* Everything ok... Nothing to do here */
2642251881Speter                /* Fall through to allow recovering later */
2643251881Speter              }
2644251881Speter            else if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
2645251881Speter              return svn_error_trace(err);
2646251881Speter
2647251881Speter            svn_error_clear(err);
2648251881Speter
2649289180Speter            child_relpath = svn_relpath_join(db->new_repos_relpath, child_name,
2650251881Speter                                             iterpool);
2651251881Speter
2652251881Speter            SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db,
2653251881Speter                                                         child_abspath,
2654251881Speter                                                         child_relpath,
2655251881Speter                                                         eb->repos_root,
2656251881Speter                                                         eb->repos_uuid,
2657251881Speter                                                         *eb->target_revision,
2658251881Speter                                                         child_kind,
2659251881Speter                                                         NULL, NULL,
2660251881Speter                                                         iterpool));
2661251881Speter          }
2662251881Speter
2663251881Speter        svn_pool_destroy(iterpool);
2664251881Speter      }
2665251881Speter  }
2666251881Speter
2667289180Speter  if (apr_hash_count(db->not_present_nodes))
2668251881Speter    {
2669251881Speter      apr_hash_index_t *hi;
2670251881Speter      apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2671251881Speter
2672251881Speter      /* This should call some new function (which could also be used
2673251881Speter         for new_children above) to add all the names in single
2674251881Speter         transaction, but I can't even trigger it.  I've tried
2675251881Speter         ra_local, ra_svn, ra_neon, ra_serf and they all call
2676251881Speter         close_file before close_dir. */
2677289180Speter      for (hi = apr_hash_first(scratch_pool, db->not_present_nodes);
2678251881Speter           hi;
2679251881Speter           hi = apr_hash_next(hi))
2680251881Speter        {
2681289180Speter          const char *child = apr_hash_this_key(hi);
2682251881Speter          const char *child_abspath, *child_relpath;
2683289180Speter          svn_node_kind_t kind = svn_node_kind_from_word(apr_hash_this_val(hi));
2684251881Speter
2685251881Speter          svn_pool_clear(iterpool);
2686251881Speter
2687251881Speter          child_abspath = svn_dirent_join(db->local_abspath, child, iterpool);
2688289180Speter          child_relpath = svn_dirent_join(db->new_repos_relpath, child, iterpool);
2689251881Speter
2690251881Speter          SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db,
2691251881Speter                                                       child_abspath,
2692251881Speter                                                       child_relpath,
2693251881Speter                                                       eb->repos_root,
2694251881Speter                                                       eb->repos_uuid,
2695251881Speter                                                       *eb->target_revision,
2696289180Speter                                                       kind,
2697251881Speter                                                       NULL, NULL,
2698251881Speter                                                       iterpool));
2699251881Speter        }
2700251881Speter      svn_pool_destroy(iterpool);
2701251881Speter    }
2702251881Speter
2703251881Speter  /* If this directory is merely an anchor for a targeted child, then we
2704251881Speter     should not be updating the node at all.  */
2705251881Speter  if (db->parent_baton == NULL
2706251881Speter      && *eb->target_basename != '\0')
2707251881Speter    {
2708251881Speter      /* And we should not have received any changes!  */
2709251881Speter      SVN_ERR_ASSERT(db->propchanges->nelts == 0);
2710251881Speter      /* ... which also implies NEW_CHANGED_* are not set,
2711251881Speter         and NEW_BASE_PROPS == NULL.  */
2712251881Speter    }
2713251881Speter  else
2714251881Speter    {
2715251881Speter      apr_hash_t *props;
2716251881Speter      apr_array_header_t *iprops = NULL;
2717251881Speter
2718251881Speter      /* ### we know a base node already exists. it was created in
2719251881Speter         ### open_directory or add_directory.  let's just preserve the
2720251881Speter         ### existing DEPTH value, and possibly CHANGED_*.  */
2721251881Speter      /* If we received any changed_* values, then use them.  */
2722251881Speter      if (SVN_IS_VALID_REVNUM(new_changed_rev))
2723251881Speter        db->changed_rev = new_changed_rev;
2724251881Speter      if (new_changed_date != 0)
2725251881Speter        db->changed_date = new_changed_date;
2726251881Speter      if (new_changed_author != NULL)
2727251881Speter        db->changed_author = new_changed_author;
2728251881Speter
2729251881Speter      /* If no depth is set yet, set to infinity. */
2730251881Speter      if (db->ambient_depth == svn_depth_unknown)
2731251881Speter        db->ambient_depth = svn_depth_infinity;
2732251881Speter
2733251881Speter      if (eb->depth_is_sticky
2734251881Speter          && db->ambient_depth != eb->requested_depth)
2735251881Speter        {
2736251881Speter          /* After a depth upgrade the entry must reflect the new depth.
2737251881Speter             Upgrading to infinity changes the depth of *all* directories,
2738251881Speter             upgrading to something else only changes the target. */
2739251881Speter
2740251881Speter          if (eb->requested_depth == svn_depth_infinity
2741251881Speter              || (strcmp(db->local_abspath, eb->target_abspath) == 0
2742251881Speter                  && eb->requested_depth > db->ambient_depth))
2743251881Speter            {
2744251881Speter              db->ambient_depth = eb->requested_depth;
2745251881Speter            }
2746251881Speter        }
2747251881Speter
2748251881Speter      /* Do we have new properties to install? Or shall we simply retain
2749251881Speter         the prior set of properties? If we're installing new properties,
2750251881Speter         then we also want to write them to an old-style props file.  */
2751251881Speter      props = new_base_props;
2752251881Speter      if (props == NULL)
2753251881Speter        props = base_props;
2754251881Speter
2755251881Speter      if (conflict_skel)
2756251881Speter        {
2757251881Speter          svn_skel_t *work_item;
2758251881Speter
2759251881Speter          SVN_ERR(complete_conflict(conflict_skel,
2760251881Speter                                    db->edit_baton,
2761251881Speter                                    db->local_abspath,
2762251881Speter                                    db->old_repos_relpath,
2763251881Speter                                    db->old_revision,
2764289180Speter                                    db->new_repos_relpath,
2765251881Speter                                    svn_node_dir, svn_node_dir,
2766289180Speter                                    (db->parent_baton
2767289180Speter                                     && db->parent_baton->deletion_conflicts)
2768289180Speter                                      ? svn_hash_gets(
2769289180Speter                                            db->parent_baton->deletion_conflicts,
2770289180Speter                                            db->name)
2771289180Speter                                      : NULL,
2772251881Speter                                    db->pool, scratch_pool));
2773251881Speter
2774251881Speter          SVN_ERR(svn_wc__conflict_create_markers(&work_item,
2775251881Speter                                                  eb->db, db->local_abspath,
2776251881Speter                                                  conflict_skel,
2777251881Speter                                                  scratch_pool, scratch_pool));
2778251881Speter
2779251881Speter          all_work_items = svn_wc__wq_merge(all_work_items, work_item,
2780251881Speter                                            scratch_pool);
2781251881Speter        }
2782251881Speter
2783251881Speter      /* Any inherited props to be set set for this base node? */
2784251881Speter      if (eb->wcroot_iprops)
2785251881Speter        {
2786251881Speter          iprops = svn_hash_gets(eb->wcroot_iprops, db->local_abspath);
2787251881Speter
2788251881Speter          /* close_edit may also update iprops for switched nodes, catching
2789251881Speter             those for which close_directory is never called (e.g. a switch
2790251881Speter             with no changes).  So as a minor optimization we remove any
2791251881Speter             iprops from the hash so as not to set them again in
2792251881Speter             close_edit. */
2793251881Speter          if (iprops)
2794251881Speter            svn_hash_sets(eb->wcroot_iprops, db->local_abspath, NULL);
2795251881Speter        }
2796251881Speter
2797251881Speter      /* Update the BASE data for the directory and mark the directory
2798251881Speter         complete */
2799251881Speter      SVN_ERR(svn_wc__db_base_add_directory(
2800251881Speter                eb->db, db->local_abspath,
2801251881Speter                eb->wcroot_abspath,
2802289180Speter                db->new_repos_relpath,
2803251881Speter                eb->repos_root, eb->repos_uuid,
2804251881Speter                *eb->target_revision,
2805251881Speter                props,
2806251881Speter                db->changed_rev, db->changed_date, db->changed_author,
2807251881Speter                NULL /* children */,
2808251881Speter                db->ambient_depth,
2809251881Speter                (dav_prop_changes->nelts > 0)
2810251881Speter                    ? svn_prop_array_to_hash(dav_prop_changes, pool)
2811251881Speter                    : NULL,
2812251881Speter                (! db->shadowed) && new_base_props != NULL,
2813289180Speter                new_actual_props, iprops,
2814289180Speter                conflict_skel, all_work_items,
2815251881Speter                scratch_pool));
2816251881Speter    }
2817251881Speter
2818251881Speter  /* Process all of the queued work items for this directory.  */
2819251881Speter  SVN_ERR(svn_wc__wq_run(eb->db, db->local_abspath,
2820251881Speter                         eb->cancel_func, eb->cancel_baton,
2821251881Speter                         scratch_pool));
2822251881Speter
2823289180Speter  if (db->parent_baton)
2824289180Speter    svn_hash_sets(db->parent_baton->not_present_nodes, db->name, NULL);
2825289180Speter
2826251881Speter  if (conflict_skel && eb->conflict_func)
2827251881Speter    SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, db->local_abspath,
2828289180Speter                                             svn_node_dir,
2829251881Speter                                             conflict_skel,
2830251881Speter                                             NULL /* merge_options */,
2831251881Speter                                             eb->conflict_func,
2832251881Speter                                             eb->conflict_baton,
2833251881Speter                                             eb->cancel_func,
2834253734Speter                                             eb->cancel_baton,
2835251881Speter                                             scratch_pool));
2836251881Speter
2837251881Speter  /* Notify of any prop changes on this directory -- but do nothing if
2838251881Speter     it's an added or skipped directory, because notification has already
2839251881Speter     happened in that case - unless the add was obstructed by a dir
2840251881Speter     scheduled for addition without history, in which case we handle
2841251881Speter     notification here). */
2842251881Speter  if (!db->already_notified && eb->notify_func && db->edited)
2843251881Speter    {
2844251881Speter      svn_wc_notify_t *notify;
2845251881Speter      svn_wc_notify_action_t action;
2846251881Speter
2847251881Speter      if (db->shadowed || db->edit_obstructed)
2848251881Speter        action = svn_wc_notify_update_shadowed_update;
2849251881Speter      else if (db->obstruction_found || db->add_existed)
2850251881Speter        action = svn_wc_notify_exists;
2851251881Speter      else
2852251881Speter        action = svn_wc_notify_update_update;
2853251881Speter
2854251881Speter      notify = svn_wc_create_notify(db->local_abspath, action, pool);
2855251881Speter      notify->kind = svn_node_dir;
2856251881Speter      notify->prop_state = prop_state;
2857251881Speter      notify->revision = *eb->target_revision;
2858251881Speter      notify->old_revision = db->old_revision;
2859251881Speter
2860251881Speter      eb->notify_func(eb->notify_baton, notify, scratch_pool);
2861251881Speter    }
2862251881Speter
2863289180Speter  if (db->edited)
2864289180Speter    eb->edited = db->edited;
2865289180Speter
2866251881Speter  /* We're done with this directory, so remove one reference from the
2867251881Speter     bump information. */
2868251881Speter  SVN_ERR(maybe_release_dir_info(db));
2869251881Speter
2870251881Speter  return SVN_NO_ERROR;
2871251881Speter}
2872251881Speter
2873251881Speter
2874251881Speter/* Common code for 'absent_file' and 'absent_directory'. */
2875251881Speterstatic svn_error_t *
2876251881Speterabsent_node(const char *path,
2877251881Speter            svn_node_kind_t absent_kind,
2878251881Speter            void *parent_baton,
2879251881Speter            apr_pool_t *pool)
2880251881Speter{
2881251881Speter  struct dir_baton *pb = parent_baton;
2882251881Speter  struct edit_baton *eb = pb->edit_baton;
2883251881Speter  apr_pool_t *scratch_pool = svn_pool_create(pool);
2884251881Speter  const char *name = svn_dirent_basename(path, NULL);
2885251881Speter  const char *local_abspath;
2886251881Speter  svn_error_t *err;
2887251881Speter  svn_wc__db_status_t status;
2888251881Speter  svn_node_kind_t kind;
2889289180Speter  svn_skel_t *tree_conflict = NULL;
2890251881Speter
2891251881Speter  if (pb->skip_this)
2892251881Speter    return SVN_NO_ERROR;
2893251881Speter
2894251881Speter  local_abspath = svn_dirent_join(pb->local_abspath, name, scratch_pool);
2895251881Speter  /* If an item by this name is scheduled for addition that's a
2896251881Speter     genuine tree-conflict.  */
2897251881Speter  err = svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
2898251881Speter                             NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
2899251881Speter                             NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
2900251881Speter                             NULL, NULL, NULL, NULL,
2901251881Speter                             eb->db, local_abspath,
2902251881Speter                             scratch_pool, scratch_pool);
2903251881Speter
2904251881Speter  if (err)
2905251881Speter    {
2906251881Speter      if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
2907251881Speter        return svn_error_trace(err);
2908251881Speter
2909251881Speter      svn_error_clear(err);
2910251881Speter      status = svn_wc__db_status_not_present;
2911251881Speter      kind = svn_node_unknown;
2912251881Speter    }
2913251881Speter
2914309511Speter  if (status != svn_wc__db_status_server_excluded)
2915309511Speter    SVN_ERR(mark_directory_edited(pb, scratch_pool));
2916309511Speter  /* Else fall through as we should update the revision anyway */
2917309511Speter
2918257936Speter  if (status == svn_wc__db_status_normal)
2919251881Speter    {
2920257936Speter      svn_boolean_t wcroot;
2921257936Speter      /* We found an obstructing working copy or a file external! */
2922251881Speter
2923257936Speter      SVN_ERR(svn_wc__db_is_wcroot(&wcroot, eb->db, local_abspath,
2924257936Speter                                   scratch_pool));
2925251881Speter
2926257936Speter      if (wcroot)
2927257936Speter        {
2928257936Speter          /*
2929257936Speter             We have an obstructing working copy; possibly a directory external
2930257936Speter
2931257936Speter             We can do two things now:
2932257936Speter             1) notify the user, record a skip, etc.
2933257936Speter             2) Just record the absent node in BASE in the parent
2934257936Speter                working copy.
2935257936Speter
2936257936Speter             As option 2 happens to be exactly what we do anyway, fall through.
2937257936Speter           */
2938257936Speter        }
2939257936Speter      else
2940257936Speter        {
2941309511Speter          svn_boolean_t file_external;
2942309511Speter          svn_revnum_t revnum;
2943257936Speter
2944309511Speter          SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &revnum, NULL, NULL,
2945309511Speter                                           NULL, NULL, NULL, NULL, NULL, NULL,
2946309511Speter                                           NULL, NULL, NULL, NULL,
2947309511Speter                                           &file_external,
2948309511Speter                                           eb->db, local_abspath,
2949309511Speter                                           scratch_pool, scratch_pool));
2950257936Speter
2951309511Speter          if (file_external)
2952309511Speter            {
2953309511Speter              /* The server asks us to replace a file external
2954309511Speter                 (Existing BASE node; not reported by the working copy crawler
2955309511Speter                  or there would have been a delete_entry() call.
2956257936Speter
2957309511Speter                 There is no way we can store this state in the working copy as
2958309511Speter                 the BASE layer is already filled.
2959309511Speter                 We could error out, but that is not helping anybody; the user is not
2960309511Speter                 even seeing with what the file external would be replaced, so let's
2961309511Speter                 report a skip and continue the update.
2962309511Speter               */
2963309511Speter
2964309511Speter              if (eb->notify_func)
2965309511Speter                {
2966309511Speter                  svn_wc_notify_t *notify;
2967309511Speter                  notify = svn_wc_create_notify(
2968257936Speter                                    local_abspath,
2969257936Speter                                    svn_wc_notify_update_skip_obstruction,
2970257936Speter                                    scratch_pool);
2971257936Speter
2972309511Speter                  eb->notify_func(eb->notify_baton, notify, scratch_pool);
2973309511Speter                }
2974309511Speter
2975309511Speter              svn_pool_destroy(scratch_pool);
2976309511Speter              return SVN_NO_ERROR;
2977257936Speter            }
2978309511Speter          else
2979309511Speter            {
2980309511Speter              /* We have a normal local node that will now be hidden for the
2981309511Speter                 user. Let's try to delete what is there. This may introduce
2982309511Speter                 tree conflicts if there are local changes */
2983309511Speter              SVN_ERR(delete_entry(path, revnum, pb, scratch_pool));
2984257936Speter
2985309511Speter              /* delete_entry() promises that BASE is empty after the operation,
2986309511Speter                 so we can just fall through now */
2987309511Speter            }
2988257936Speter        }
2989251881Speter    }
2990251881Speter  else if (status == svn_wc__db_status_not_present
2991251881Speter           || status == svn_wc__db_status_server_excluded
2992251881Speter           || status == svn_wc__db_status_excluded)
2993251881Speter    {
2994251881Speter      /* The BASE node is not actually there, so we can safely turn it into
2995251881Speter         an absent node */
2996251881Speter    }
2997251881Speter  else
2998251881Speter    {
2999251881Speter      /* We have a local addition. If this would be a BASE node it would have
3000251881Speter         been deleted before we get here. (Which might have turned it into
3001289180Speter         a copy). */
3002251881Speter      SVN_ERR_ASSERT(status != svn_wc__db_status_normal);
3003251881Speter
3004289180Speter      if (!pb->shadowed && !pb->edit_obstructed)
3005289180Speter        SVN_ERR(check_tree_conflict(&tree_conflict, eb, local_abspath,
3006289180Speter                                    status, FALSE, svn_node_unknown,
3007289180Speter                                    svn_wc_conflict_action_add,
3008289180Speter                                    scratch_pool, scratch_pool));
3009289180Speter
3010251881Speter    }
3011251881Speter
3012251881Speter  {
3013251881Speter    const char *repos_relpath;
3014289180Speter    repos_relpath = svn_relpath_join(pb->new_repos_relpath, name, scratch_pool);
3015251881Speter
3016289180Speter    if (tree_conflict)
3017289180Speter      SVN_ERR(complete_conflict(tree_conflict, eb, local_abspath,
3018289180Speter                                NULL, SVN_INVALID_REVNUM, repos_relpath,
3019289180Speter                                kind, svn_node_unknown, NULL,
3020289180Speter                                scratch_pool, scratch_pool));
3021289180Speter
3022251881Speter    /* Insert an excluded node below the parent node to note that this child
3023251881Speter       is absent. (This puts it in the parent db if the child is obstructed) */
3024251881Speter    SVN_ERR(svn_wc__db_base_add_excluded_node(eb->db, local_abspath,
3025251881Speter                                              repos_relpath, eb->repos_root,
3026251881Speter                                              eb->repos_uuid,
3027251881Speter                                              *(eb->target_revision),
3028251881Speter                                              absent_kind,
3029251881Speter                                              svn_wc__db_status_server_excluded,
3030289180Speter                                              tree_conflict, NULL,
3031251881Speter                                              scratch_pool));
3032289180Speter
3033289180Speter    if (tree_conflict)
3034289180Speter      {
3035289180Speter        if (eb->conflict_func)
3036289180Speter          SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, local_abspath,
3037289180Speter                                                   kind,
3038289180Speter                                                   tree_conflict,
3039289180Speter                                                   NULL /* merge_options */,
3040289180Speter                                                   eb->conflict_func,
3041289180Speter                                                   eb->conflict_baton,
3042289180Speter                                                   eb->cancel_func,
3043289180Speter                                                   eb->cancel_baton,
3044289180Speter                                                   scratch_pool));
3045289180Speter        do_notification(eb, local_abspath, kind, svn_wc_notify_tree_conflict,
3046289180Speter                        scratch_pool);
3047289180Speter      }
3048251881Speter  }
3049251881Speter
3050251881Speter  svn_pool_destroy(scratch_pool);
3051251881Speter
3052251881Speter  return SVN_NO_ERROR;
3053251881Speter}
3054251881Speter
3055251881Speter
3056251881Speter/* An svn_delta_editor_t function. */
3057251881Speterstatic svn_error_t *
3058251881Speterabsent_file(const char *path,
3059251881Speter            void *parent_baton,
3060251881Speter            apr_pool_t *pool)
3061251881Speter{
3062251881Speter  return absent_node(path, svn_node_file, parent_baton, pool);
3063251881Speter}
3064251881Speter
3065251881Speter
3066251881Speter/* An svn_delta_editor_t function. */
3067251881Speterstatic svn_error_t *
3068251881Speterabsent_directory(const char *path,
3069251881Speter                 void *parent_baton,
3070251881Speter                 apr_pool_t *pool)
3071251881Speter{
3072251881Speter  return absent_node(path, svn_node_dir, parent_baton, pool);
3073251881Speter}
3074251881Speter
3075251881Speter
3076251881Speter/* An svn_delta_editor_t function. */
3077251881Speterstatic svn_error_t *
3078251881Speteradd_file(const char *path,
3079251881Speter         void *parent_baton,
3080251881Speter         const char *copyfrom_path,
3081251881Speter         svn_revnum_t copyfrom_rev,
3082251881Speter         apr_pool_t *pool,
3083251881Speter         void **file_baton)
3084251881Speter{
3085251881Speter  struct dir_baton *pb = parent_baton;
3086251881Speter  struct edit_baton *eb = pb->edit_baton;
3087251881Speter  struct file_baton *fb;
3088289180Speter  svn_node_kind_t kind;
3089289180Speter  svn_node_kind_t wc_kind;
3090289180Speter  svn_wc__db_status_t status;
3091251881Speter  apr_pool_t *scratch_pool;
3092289180Speter  svn_boolean_t conflicted;
3093251881Speter  svn_boolean_t conflict_ignored = FALSE;
3094289180Speter  svn_boolean_t versioned_locally_and_present;
3095251881Speter  svn_skel_t *tree_conflict = NULL;
3096251881Speter  svn_error_t *err = SVN_NO_ERROR;
3097251881Speter
3098251881Speter  SVN_ERR_ASSERT(! (copyfrom_path || SVN_IS_VALID_REVNUM(copyfrom_rev)));
3099251881Speter
3100251881Speter  SVN_ERR(make_file_baton(&fb, pb, path, TRUE, pool));
3101251881Speter  *file_baton = fb;
3102251881Speter
3103251881Speter  if (fb->skip_this)
3104251881Speter    return SVN_NO_ERROR;
3105251881Speter
3106289180Speter  SVN_ERR(calculate_repos_relpath(&fb->new_repos_relpath, fb->local_abspath,
3107289180Speter                                  NULL, eb, pb, fb->pool, pool));
3108251881Speter  SVN_ERR(mark_file_edited(fb, pool));
3109251881Speter
3110251881Speter  /* The file_pool can stick around for a *long* time, so we want to
3111251881Speter     use a subpool for any temporary allocations. */
3112251881Speter  scratch_pool = svn_pool_create(pool);
3113251881Speter
3114251881Speter
3115251881Speter  /* It may not be named the same as the administrative directory. */
3116251881Speter  if (svn_wc_is_adm_dir(fb->name, pool))
3117251881Speter    return svn_error_createf(
3118251881Speter       SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
3119251881Speter       _("Failed to add file '%s': object of the same name as the "
3120251881Speter         "administrative directory"),
3121251881Speter       svn_dirent_local_style(fb->local_abspath, pool));
3122251881Speter
3123251881Speter  if (!eb->clean_checkout)
3124251881Speter    {
3125251881Speter      SVN_ERR(svn_io_check_path(fb->local_abspath, &kind, scratch_pool));
3126251881Speter
3127251881Speter      err = svn_wc__db_read_info(&status, &wc_kind, NULL, NULL, NULL, NULL, NULL,
3128251881Speter                                NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3129251881Speter                                NULL, NULL, NULL, NULL, NULL,
3130251881Speter                                &conflicted, NULL, NULL, NULL, NULL, NULL, NULL,
3131251881Speter                                eb->db, fb->local_abspath,
3132251881Speter                                scratch_pool, scratch_pool);
3133251881Speter    }
3134289180Speter  else
3135289180Speter    {
3136289180Speter      kind =  svn_node_none;
3137289180Speter      status = svn_wc__db_status_not_present;
3138289180Speter      wc_kind = svn_node_unknown;
3139289180Speter      conflicted = FALSE;
3140289180Speter    }
3141251881Speter
3142251881Speter  if (err)
3143251881Speter    {
3144251881Speter      if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
3145251881Speter        return svn_error_trace(err);
3146251881Speter
3147251881Speter      svn_error_clear(err);
3148251881Speter      wc_kind = svn_node_unknown;
3149251881Speter      conflicted = FALSE;
3150251881Speter
3151251881Speter      versioned_locally_and_present = FALSE;
3152251881Speter    }
3153289180Speter  else if (status == svn_wc__db_status_normal && wc_kind == svn_node_unknown)
3154251881Speter    {
3155289180Speter      SVN_ERR_ASSERT(conflicted);
3156289180Speter      versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */
3157289180Speter    }
3158289180Speter  else if (status == svn_wc__db_status_normal
3159289180Speter           || status == svn_wc__db_status_incomplete)
3160289180Speter    {
3161289180Speter      svn_boolean_t root;
3162251881Speter
3163289180Speter      SVN_ERR(svn_wc__db_is_wcroot(&root, eb->db, fb->local_abspath,
3164289180Speter                                   scratch_pool));
3165251881Speter
3166289180Speter      if (root)
3167289180Speter        {
3168289180Speter          /* !! We found the root of a working copy obstructing the wc !!
3169251881Speter
3170289180Speter             If the directory would be part of our own working copy then
3171289180Speter             we wouldn't have been called as an add_directory().
3172251881Speter
3173289180Speter             The only thing we can do is add a not-present node, to allow
3174289180Speter             a future update to bring in the new files when the problem is
3175289180Speter             resolved.  Note that svn_wc__db_base_add_not_present_node()
3176289180Speter             explicitly adds the node into the parent's node database. */
3177251881Speter
3178289180Speter          svn_hash_sets(pb->not_present_nodes,
3179289180Speter                        apr_pstrdup(pb->pool, fb->name),
3180289180Speter                        svn_node_kind_to_word(svn_node_dir));
3181289180Speter        }
3182289180Speter      else if (wc_kind == svn_node_dir)
3183289180Speter        {
3184289180Speter          /* We have an editor violation. Github sometimes does this
3185289180Speter             in its subversion compatibility code, when changing the
3186289180Speter             depth of a working copy, or on updates from incomplete */
3187289180Speter        }
3188289180Speter      else
3189289180Speter        {
3190289180Speter          /* We found a file external occupating the place we need in BASE.
3191251881Speter
3192289180Speter             We can't add a not-present node in this case as that would overwrite
3193289180Speter             the file external. Luckily the file external itself stops us from
3194289180Speter             forgetting a child of this parent directory like an obstructing
3195289180Speter             working copy would.
3196251881Speter
3197289180Speter             The reason we get here is that the adm crawler doesn't report
3198289180Speter             file externals.
3199289180Speter           */
3200289180Speter          SVN_ERR_ASSERT(wc_kind == svn_node_file
3201289180Speter                         || wc_kind == svn_node_symlink);
3202289180Speter        }
3203251881Speter
3204251881Speter      SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3205251881Speter      fb->skip_this = TRUE;
3206251881Speter      fb->already_notified = TRUE;
3207251881Speter
3208289180Speter      do_notification(eb, fb->local_abspath, wc_kind,
3209251881Speter                      svn_wc_notify_update_skip_obstruction, scratch_pool);
3210251881Speter
3211251881Speter      svn_pool_destroy(scratch_pool);
3212251881Speter
3213251881Speter      return SVN_NO_ERROR;
3214251881Speter    }
3215251881Speter  else
3216251881Speter    versioned_locally_and_present = IS_NODE_PRESENT(status);
3217251881Speter
3218251881Speter
3219251881Speter  /* Is this path a conflict victim? */
3220251881Speter  if (fb->shadowed)
3221251881Speter    conflicted = FALSE; /* Conflict applies to WORKING */
3222251881Speter  else if (conflicted)
3223251881Speter    {
3224251881Speter      if (pb->deletion_conflicts)
3225251881Speter        tree_conflict = svn_hash_gets(pb->deletion_conflicts, fb->name);
3226251881Speter
3227251881Speter      if (tree_conflict)
3228251881Speter        {
3229251881Speter          svn_wc_conflict_reason_t reason;
3230286506Speter          const char *move_src_op_root_abspath;
3231362181Sdim          const char *move_dst_op_root_abspath;
3232251881Speter          /* So this deletion wasn't just a deletion, it is actually a
3233251881Speter             replacement. Let's install a better tree conflict. */
3234251881Speter
3235286506Speter          SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL,
3236286506Speter                                                      &move_src_op_root_abspath,
3237362181Sdim                                                      &move_dst_op_root_abspath,
3238251881Speter                                                      eb->db,
3239251881Speter                                                      fb->local_abspath,
3240251881Speter                                                      tree_conflict,
3241289180Speter                                                      fb->pool, scratch_pool));
3242251881Speter
3243251881Speter          tree_conflict = svn_wc__conflict_skel_create(fb->pool);
3244251881Speter
3245251881Speter          SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
3246251881Speter                                        tree_conflict,
3247251881Speter                                        eb->db, fb->local_abspath,
3248251881Speter                                        reason, svn_wc_conflict_action_replace,
3249286506Speter                                        move_src_op_root_abspath,
3250362181Sdim                                        move_dst_op_root_abspath,
3251289180Speter                                        fb->pool, scratch_pool));
3252251881Speter
3253251881Speter          /* And now stop checking for conflicts here and just perform
3254251881Speter             a shadowed update */
3255251881Speter          fb->edit_conflict = tree_conflict; /* Cache for close_file */
3256251881Speter          tree_conflict = NULL; /* No direct notification */
3257251881Speter          fb->shadowed = TRUE; /* Just continue */
3258251881Speter          conflicted = FALSE; /* No skip */
3259251881Speter        }
3260251881Speter      else
3261251881Speter        SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
3262251881Speter                                        eb->db, fb->local_abspath, pool));
3263251881Speter    }
3264251881Speter
3265251881Speter  /* Now the usual conflict handling: skip. */
3266251881Speter  if (conflicted)
3267251881Speter    {
3268251881Speter      SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3269251881Speter
3270251881Speter      fb->skip_this = TRUE;
3271251881Speter      fb->already_notified = TRUE;
3272251881Speter
3273251881Speter      /* We skip this node, but once the update completes the parent node will
3274251881Speter         be updated to the new revision. So a future recursive update of the
3275251881Speter         parent will not bring in this new node as the revision of the parent
3276251881Speter         describes to the repository that all children are available.
3277251881Speter
3278251881Speter         To resolve this problem, we add a not-present node to allow bringing
3279251881Speter         the node in once this conflict is resolved.
3280251881Speter
3281251881Speter         Note that we can safely assume that no present base node exists,
3282251881Speter         because then we would not have received an add_file.
3283251881Speter       */
3284289180Speter      svn_hash_sets(pb->not_present_nodes, apr_pstrdup(pb->pool, fb->name),
3285289180Speter                    svn_node_kind_to_word(svn_node_file));
3286251881Speter
3287289180Speter      do_notification(eb, fb->local_abspath, svn_node_file,
3288251881Speter                      svn_wc_notify_skip_conflicted, scratch_pool);
3289251881Speter
3290251881Speter      svn_pool_destroy(scratch_pool);
3291251881Speter
3292251881Speter      return SVN_NO_ERROR;
3293251881Speter    }
3294251881Speter  else if (conflict_ignored)
3295251881Speter    {
3296251881Speter      fb->shadowed = TRUE;
3297251881Speter    }
3298251881Speter
3299251881Speter  if (fb->shadowed)
3300251881Speter    {
3301251881Speter      /* Nothing to check; does not and will not exist in working copy */
3302251881Speter    }
3303251881Speter  else if (versioned_locally_and_present)
3304251881Speter    {
3305251881Speter      /* What to do with a versioned or schedule-add file:
3306251881Speter
3307251881Speter         If the UUID doesn't match the parent's, or the URL isn't a child of
3308251881Speter         the parent dir's URL, it's an error.
3309251881Speter
3310251881Speter         Set add_existed so that user notification is delayed until after any
3311251881Speter         text or prop conflicts have been found.
3312251881Speter
3313251881Speter         Whether the incoming add is a symlink or a file will only be known in
3314251881Speter         close_file(), when the props are known. So with a locally added file
3315251881Speter         or symlink, let close_file() check for a tree conflict.
3316251881Speter
3317251881Speter         We will never see missing files here, because these would be
3318251881Speter         re-added during the crawler phase. */
3319251881Speter      svn_boolean_t local_is_file;
3320251881Speter
3321251881Speter      /* Is the local node a copy or move */
3322251881Speter      if (status == svn_wc__db_status_added)
3323251881Speter        SVN_ERR(svn_wc__db_scan_addition(&status, NULL, NULL, NULL, NULL, NULL,
3324251881Speter                                         NULL, NULL, NULL,
3325251881Speter                                         eb->db, fb->local_abspath,
3326251881Speter                                         scratch_pool, scratch_pool));
3327251881Speter
3328251881Speter      /* Is there something that is a file? */
3329251881Speter      local_is_file = (wc_kind == svn_node_file
3330251881Speter                       || wc_kind == svn_node_symlink);
3331251881Speter
3332251881Speter      /* Do tree conflict checking if
3333251881Speter       *  - if there is a local copy.
3334251881Speter       *  - if this is a switch operation
3335251881Speter       *  - the node kinds mismatch
3336251881Speter       *
3337251881Speter       * During switch, local adds at the same path as incoming adds get
3338251881Speter       * "lost" in that switching back to the original will no longer have the
3339251881Speter       * local add. So switch always alerts the user with a tree conflict. */
3340251881Speter      if (!eb->adds_as_modification
3341251881Speter          || !local_is_file
3342251881Speter          || status != svn_wc__db_status_added)
3343251881Speter        {
3344251881Speter          SVN_ERR(check_tree_conflict(&tree_conflict, eb,
3345251881Speter                                      fb->local_abspath,
3346251881Speter                                      status, FALSE, svn_node_none,
3347251881Speter                                      svn_wc_conflict_action_add,
3348289180Speter                                      fb->pool, scratch_pool));
3349251881Speter        }
3350251881Speter
3351251881Speter      if (tree_conflict == NULL)
3352251881Speter        fb->add_existed = TRUE; /* Take over WORKING */
3353251881Speter      else
3354251881Speter        fb->shadowed = TRUE; /* Only update BASE */
3355251881Speter
3356251881Speter    }
3357251881Speter  else if (kind != svn_node_none)
3358251881Speter    {
3359251881Speter      /* There's an unversioned node at this path. */
3360251881Speter      fb->obstruction_found = TRUE;
3361251881Speter
3362251881Speter      /* Unversioned, obstructing files are handled by text merge/conflict,
3363251881Speter       * if unversioned obstructions are allowed. */
3364251881Speter      if (! (kind == svn_node_file && eb->allow_unver_obstructions))
3365251881Speter        {
3366251881Speter          /* Bring in the node as deleted */ /* ### Obstructed Conflict */
3367251881Speter          fb->shadowed = TRUE;
3368251881Speter
3369251881Speter          /* Mark a conflict */
3370251881Speter          tree_conflict = svn_wc__conflict_skel_create(fb->pool);
3371251881Speter
3372251881Speter          SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
3373251881Speter                                        tree_conflict,
3374251881Speter                                        eb->db, fb->local_abspath,
3375251881Speter                                        svn_wc_conflict_reason_unversioned,
3376251881Speter                                        svn_wc_conflict_action_add,
3377362181Sdim                                        NULL, NULL,
3378251881Speter                                        fb->pool, scratch_pool));
3379251881Speter        }
3380251881Speter    }
3381251881Speter
3382251881Speter  /* When this is not the update target add a not-present BASE node now,
3383251881Speter     to allow marking the parent directory complete in its close_edit() call.
3384251881Speter     This resolves issues when that occurs before the close_file(). */
3385251881Speter  if (pb->parent_baton
3386251881Speter      || *eb->target_basename == '\0'
3387251881Speter      || (strcmp(fb->local_abspath, eb->target_abspath) != 0))
3388251881Speter    {
3389289180Speter      svn_hash_sets(pb->not_present_nodes, apr_pstrdup(pb->pool, fb->name),
3390289180Speter                    svn_node_kind_to_word(svn_node_file));
3391251881Speter    }
3392251881Speter
3393251881Speter  if (tree_conflict != NULL)
3394251881Speter    {
3395251881Speter      SVN_ERR(complete_conflict(tree_conflict,
3396251881Speter                                fb->edit_baton,
3397251881Speter                                fb->local_abspath,
3398251881Speter                                fb->old_repos_relpath,
3399251881Speter                                fb->old_revision,
3400289180Speter                                fb->new_repos_relpath,
3401289180Speter                                wc_kind, svn_node_file,
3402289180Speter                                pb->deletion_conflicts
3403289180Speter                                  ? svn_hash_gets(pb->deletion_conflicts,
3404289180Speter                                                  fb->name)
3405289180Speter                                  : NULL,
3406251881Speter                                fb->pool, scratch_pool));
3407251881Speter
3408251881Speter      SVN_ERR(svn_wc__db_op_mark_conflict(eb->db,
3409251881Speter                                          fb->local_abspath,
3410251881Speter                                          tree_conflict, NULL,
3411251881Speter                                          scratch_pool));
3412251881Speter
3413289180Speter      fb->edit_conflict = tree_conflict;
3414253734Speter
3415251881Speter      fb->already_notified = TRUE;
3416251881Speter      do_notification(eb, fb->local_abspath, svn_node_file,
3417251881Speter                      svn_wc_notify_tree_conflict, scratch_pool);
3418251881Speter    }
3419251881Speter
3420251881Speter  svn_pool_destroy(scratch_pool);
3421251881Speter
3422251881Speter  return SVN_NO_ERROR;
3423251881Speter}
3424251881Speter
3425251881Speter
3426251881Speter/* An svn_delta_editor_t function. */
3427251881Speterstatic svn_error_t *
3428251881Speteropen_file(const char *path,
3429251881Speter          void *parent_baton,
3430251881Speter          svn_revnum_t base_revision,
3431251881Speter          apr_pool_t *pool,
3432251881Speter          void **file_baton)
3433251881Speter{
3434251881Speter  struct dir_baton *pb = parent_baton;
3435251881Speter  struct edit_baton *eb = pb->edit_baton;
3436251881Speter  struct file_baton *fb;
3437251881Speter  svn_boolean_t conflicted;
3438251881Speter  svn_boolean_t conflict_ignored = FALSE;
3439251881Speter  svn_boolean_t have_work;
3440251881Speter  svn_wc__db_status_t status;
3441251881Speter  svn_node_kind_t wc_kind;
3442251881Speter  svn_skel_t *tree_conflict = NULL;
3443251881Speter
3444251881Speter  /* the file_pool can stick around for a *long* time, so we want to use
3445251881Speter     a subpool for any temporary allocations. */
3446251881Speter  apr_pool_t *scratch_pool = svn_pool_create(pool);
3447251881Speter
3448251881Speter  SVN_ERR(make_file_baton(&fb, pb, path, FALSE, pool));
3449251881Speter  *file_baton = fb;
3450251881Speter
3451251881Speter  if (fb->skip_this)
3452251881Speter    return SVN_NO_ERROR;
3453251881Speter
3454251881Speter  /* Detect obstructing working copies */
3455251881Speter  {
3456251881Speter    svn_boolean_t is_root;
3457251881Speter
3458251881Speter    SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, fb->local_abspath,
3459251881Speter                                 pool));
3460251881Speter
3461251881Speter    if (is_root)
3462251881Speter      {
3463251881Speter        /* Just skip this node; a future update will handle it */
3464251881Speter        SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3465251881Speter        fb->skip_this = TRUE;
3466251881Speter        fb->already_notified = TRUE;
3467251881Speter
3468251881Speter        do_notification(eb, fb->local_abspath, svn_node_file,
3469251881Speter                        svn_wc_notify_update_skip_obstruction, pool);
3470251881Speter
3471251881Speter        return SVN_NO_ERROR;
3472251881Speter      }
3473251881Speter  }
3474251881Speter
3475251881Speter  /* Sanity check. */
3476251881Speter
3477251881Speter  SVN_ERR(svn_wc__db_read_info(&status, &wc_kind, &fb->old_revision,
3478251881Speter                               &fb->old_repos_relpath, NULL, NULL,
3479251881Speter                               &fb->changed_rev, &fb->changed_date,
3480251881Speter                               &fb->changed_author, NULL,
3481251881Speter                               &fb->original_checksum, NULL, NULL, NULL,
3482251881Speter                               NULL, NULL, NULL, NULL, NULL, NULL,
3483251881Speter                               &conflicted, NULL, NULL, &fb->local_prop_mods,
3484251881Speter                               NULL, NULL, &have_work,
3485251881Speter                               eb->db, fb->local_abspath,
3486251881Speter                               fb->pool, scratch_pool));
3487251881Speter
3488251881Speter  if (have_work)
3489251881Speter    SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &fb->old_revision,
3490251881Speter                                     &fb->old_repos_relpath, NULL, NULL,
3491251881Speter                                     &fb->changed_rev, &fb->changed_date,
3492251881Speter                                     &fb->changed_author, NULL,
3493251881Speter                                     &fb->original_checksum, NULL, NULL,
3494251881Speter                                     NULL, NULL, NULL,
3495251881Speter                                     eb->db, fb->local_abspath,
3496251881Speter                                     fb->pool, scratch_pool));
3497251881Speter
3498289180Speter  SVN_ERR(calculate_repos_relpath(&fb->new_repos_relpath, fb->local_abspath,
3499289180Speter                                  fb->old_repos_relpath, eb, pb,
3500289180Speter                                  fb->pool, scratch_pool));
3501289180Speter
3502251881Speter  /* Is this path a conflict victim? */
3503251881Speter  if (fb->shadowed)
3504251881Speter    conflicted = FALSE; /* Conflict applies to WORKING */
3505251881Speter  else if (conflicted)
3506251881Speter    SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
3507251881Speter                                    eb->db, fb->local_abspath, pool));
3508251881Speter  if (conflicted)
3509251881Speter    {
3510251881Speter      SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3511251881Speter
3512251881Speter      fb->skip_this = TRUE;
3513251881Speter      fb->already_notified = TRUE;
3514251881Speter
3515251881Speter      do_notification(eb, fb->local_abspath, svn_node_unknown,
3516251881Speter                      svn_wc_notify_skip_conflicted, scratch_pool);
3517251881Speter
3518251881Speter      svn_pool_destroy(scratch_pool);
3519251881Speter
3520251881Speter      return SVN_NO_ERROR;
3521251881Speter    }
3522251881Speter  else if (conflict_ignored)
3523251881Speter    {
3524251881Speter      fb->shadowed = TRUE;
3525251881Speter    }
3526251881Speter
3527251881Speter  /* Check for conflicts only when we haven't already recorded
3528251881Speter   * a tree-conflict on a parent node. */
3529251881Speter  if (!fb->shadowed)
3530251881Speter    SVN_ERR(check_tree_conflict(&tree_conflict, eb, fb->local_abspath,
3531251881Speter                                status, TRUE, svn_node_file,
3532251881Speter                                svn_wc_conflict_action_edit,
3533251881Speter                                fb->pool, scratch_pool));
3534251881Speter
3535251881Speter  /* Is this path the victim of a newly-discovered tree conflict? */
3536251881Speter  if (tree_conflict != NULL)
3537251881Speter    {
3538251881Speter      svn_wc_conflict_reason_t reason;
3539251881Speter      fb->edit_conflict = tree_conflict;
3540251881Speter      /* Other modifications wouldn't be a tree conflict */
3541251881Speter
3542362181Sdim      SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL, NULL,
3543251881Speter                                                  eb->db, fb->local_abspath,
3544251881Speter                                                  tree_conflict,
3545251881Speter                                                  scratch_pool, scratch_pool));
3546251881Speter      SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_deleted
3547251881Speter                     || reason == svn_wc_conflict_reason_moved_away
3548251881Speter                     || reason == svn_wc_conflict_reason_replaced
3549251881Speter                     || reason == svn_wc_conflict_reason_obstructed);
3550251881Speter
3551251881Speter      /* Continue updating BASE */
3552251881Speter      if (reason == svn_wc_conflict_reason_obstructed)
3553251881Speter        fb->edit_obstructed = TRUE;
3554251881Speter      else
3555251881Speter        fb->shadowed = TRUE;
3556251881Speter    }
3557251881Speter
3558251881Speter  svn_pool_destroy(scratch_pool);
3559251881Speter
3560251881Speter  return SVN_NO_ERROR;
3561251881Speter}
3562251881Speter
3563251881Speter/* Implements svn_stream_lazyopen_func_t. */
3564251881Speterstatic svn_error_t *
3565251881Speterlazy_open_source(svn_stream_t **stream,
3566251881Speter                 void *baton,
3567251881Speter                 apr_pool_t *result_pool,
3568251881Speter                 apr_pool_t *scratch_pool)
3569251881Speter{
3570251881Speter  struct file_baton *fb = baton;
3571251881Speter
3572251881Speter  SVN_ERR(svn_wc__db_pristine_read(stream, NULL, fb->edit_baton->db,
3573251881Speter                                   fb->local_abspath,
3574251881Speter                                   fb->original_checksum,
3575251881Speter                                   result_pool, scratch_pool));
3576251881Speter
3577251881Speter
3578251881Speter  return SVN_NO_ERROR;
3579251881Speter}
3580251881Speter
3581251881Speter/* Implements svn_stream_lazyopen_func_t. */
3582251881Speterstatic svn_error_t *
3583251881Speterlazy_open_target(svn_stream_t **stream,
3584251881Speter                 void *baton,
3585251881Speter                 apr_pool_t *result_pool,
3586251881Speter                 apr_pool_t *scratch_pool)
3587251881Speter{
3588289180Speter  struct handler_baton *hb = baton;
3589289180Speter  svn_wc__db_install_data_t *install_data;
3590251881Speter
3591289180Speter  /* By convention return value is undefined on error, but we rely
3592289180Speter     on HB->INSTALL_DATA value in window_handler() and abort
3593289180Speter     INSTALL_STREAM if is not NULL on error.
3594289180Speter     So we store INSTALL_DATA to local variable first, to leave
3595289180Speter     HB->INSTALL_DATA unchanged on error. */
3596289180Speter  SVN_ERR(svn_wc__db_pristine_prepare_install(stream,
3597289180Speter                                              &install_data,
3598289180Speter                                              &hb->new_text_base_sha1_checksum,
3599289180Speter                                              NULL,
3600289180Speter                                              hb->fb->edit_baton->db,
3601289180Speter                                              hb->fb->dir_baton->local_abspath,
3602289180Speter                                              result_pool, scratch_pool));
3603251881Speter
3604289180Speter  hb->install_data = install_data;
3605289180Speter
3606251881Speter  return SVN_NO_ERROR;
3607251881Speter}
3608251881Speter
3609251881Speter/* An svn_delta_editor_t function. */
3610251881Speterstatic svn_error_t *
3611251881Speterapply_textdelta(void *file_baton,
3612251881Speter                const char *expected_checksum,
3613251881Speter                apr_pool_t *pool,
3614251881Speter                svn_txdelta_window_handler_t *handler,
3615251881Speter                void **handler_baton)
3616251881Speter{
3617251881Speter  struct file_baton *fb = file_baton;
3618251881Speter  apr_pool_t *handler_pool = svn_pool_create(fb->pool);
3619251881Speter  struct handler_baton *hb = apr_pcalloc(handler_pool, sizeof(*hb));
3620251881Speter  struct edit_baton *eb = fb->edit_baton;
3621251881Speter  const svn_checksum_t *recorded_base_checksum;
3622251881Speter  svn_checksum_t *expected_base_checksum;
3623251881Speter  svn_stream_t *source;
3624251881Speter  svn_stream_t *target;
3625251881Speter
3626251881Speter  if (fb->skip_this)
3627251881Speter    {
3628251881Speter      *handler = svn_delta_noop_window_handler;
3629251881Speter      *handler_baton = NULL;
3630251881Speter      return SVN_NO_ERROR;
3631251881Speter    }
3632251881Speter
3633251881Speter  SVN_ERR(mark_file_edited(fb, pool));
3634251881Speter
3635251881Speter  /* Parse checksum or sets expected_base_checksum to NULL */
3636251881Speter  SVN_ERR(svn_checksum_parse_hex(&expected_base_checksum, svn_checksum_md5,
3637251881Speter                                 expected_checksum, pool));
3638251881Speter
3639251881Speter  /* Before applying incoming svndiff data to text base, make sure
3640251881Speter     text base hasn't been corrupted, and that its checksum
3641251881Speter     matches the expected base checksum. */
3642251881Speter
3643251881Speter  /* The incoming delta is targeted against EXPECTED_BASE_CHECKSUM. Find and
3644251881Speter     check our RECORDED_BASE_CHECKSUM.  (In WC-1, we could not do this test
3645251881Speter     for replaced nodes because we didn't store the checksum of the "revert
3646251881Speter     base".  In WC-NG, we do and we can.) */
3647251881Speter  recorded_base_checksum = fb->original_checksum;
3648251881Speter
3649251881Speter  /* If we have a checksum that we want to compare to a MD5 checksum,
3650251881Speter     ensure that it is a MD5 checksum */
3651251881Speter  if (recorded_base_checksum
3652251881Speter      && expected_base_checksum
3653251881Speter      && recorded_base_checksum->kind != svn_checksum_md5)
3654251881Speter    SVN_ERR(svn_wc__db_pristine_get_md5(&recorded_base_checksum,
3655251881Speter                                        eb->db, eb->wcroot_abspath,
3656251881Speter                                        recorded_base_checksum, pool, pool));
3657251881Speter
3658251881Speter
3659251881Speter  if (!svn_checksum_match(expected_base_checksum, recorded_base_checksum))
3660251881Speter      return svn_error_createf(SVN_ERR_WC_CORRUPT_TEXT_BASE, NULL,
3661251881Speter                     _("Checksum mismatch for '%s':\n"
3662251881Speter                       "   expected:  %s\n"
3663251881Speter                       "   recorded:  %s\n"),
3664251881Speter                     svn_dirent_local_style(fb->local_abspath, pool),
3665251881Speter                     svn_checksum_to_cstring_display(expected_base_checksum,
3666251881Speter                                                     pool),
3667251881Speter                     svn_checksum_to_cstring_display(recorded_base_checksum,
3668251881Speter                                                     pool));
3669251881Speter
3670251881Speter  /* Open the text base for reading, unless this is an added file. */
3671251881Speter
3672251881Speter  /*
3673251881Speter     kff todo: what we really need to do here is:
3674251881Speter
3675251881Speter     1. See if there's a file or dir by this name already here.
3676251881Speter     2. See if it's under revision control.
3677251881Speter     3. If both are true, open text-base.
3678251881Speter     4. If only 1 is true, bail, because we can't go destroying user's
3679251881Speter        files (or as an alternative to bailing, move it to some tmp
3680251881Speter        name and somehow tell the user, but communicating with the
3681251881Speter        user without erroring is a whole callback system we haven't
3682251881Speter        finished inventing yet.)
3683251881Speter  */
3684251881Speter
3685251881Speter  if (! fb->adding_file)
3686251881Speter    {
3687251881Speter      SVN_ERR_ASSERT(!fb->original_checksum
3688251881Speter                     || fb->original_checksum->kind == svn_checksum_sha1);
3689251881Speter
3690251881Speter      source = svn_stream_lazyopen_create(lazy_open_source, fb, FALSE,
3691251881Speter                                          handler_pool);
3692251881Speter    }
3693251881Speter  else
3694251881Speter    {
3695251881Speter      source = svn_stream_empty(handler_pool);
3696251881Speter    }
3697251881Speter
3698251881Speter  /* If we don't have a recorded checksum, use the ra provided checksum */
3699251881Speter  if (!recorded_base_checksum)
3700251881Speter    recorded_base_checksum = expected_base_checksum;
3701251881Speter
3702251881Speter  /* Checksum the text base while applying deltas */
3703251881Speter  if (recorded_base_checksum)
3704251881Speter    {
3705251881Speter      hb->expected_source_checksum = svn_checksum_dup(recorded_base_checksum,
3706251881Speter                                                      handler_pool);
3707251881Speter
3708251881Speter      /* Wrap stream and store reference to allow calculating the
3709251881Speter         checksum. */
3710251881Speter      source = svn_stream_checksummed2(source,
3711251881Speter                                       &hb->actual_source_checksum,
3712251881Speter                                       NULL, recorded_base_checksum->kind,
3713251881Speter                                       TRUE, handler_pool);
3714251881Speter      hb->source_checksum_stream = source;
3715251881Speter    }
3716251881Speter
3717289180Speter  target = svn_stream_lazyopen_create(lazy_open_target, hb, TRUE, handler_pool);
3718251881Speter
3719251881Speter  /* Prepare to apply the delta.  */
3720251881Speter  svn_txdelta_apply(source, target,
3721251881Speter                    hb->new_text_base_md5_digest,
3722289180Speter                    fb->local_abspath /* error_info */,
3723251881Speter                    handler_pool,
3724251881Speter                    &hb->apply_handler, &hb->apply_baton);
3725251881Speter
3726251881Speter  hb->pool = handler_pool;
3727251881Speter  hb->fb = fb;
3728251881Speter
3729251881Speter  /* We're all set.  */
3730251881Speter  *handler_baton = hb;
3731251881Speter  *handler = window_handler;
3732251881Speter
3733251881Speter  return SVN_NO_ERROR;
3734251881Speter}
3735251881Speter
3736251881Speter
3737251881Speter/* An svn_delta_editor_t function. */
3738251881Speterstatic svn_error_t *
3739251881Speterchange_file_prop(void *file_baton,
3740251881Speter                 const char *name,
3741251881Speter                 const svn_string_t *value,
3742251881Speter                 apr_pool_t *scratch_pool)
3743251881Speter{
3744251881Speter  struct file_baton *fb = file_baton;
3745251881Speter  svn_prop_t *propchange;
3746251881Speter
3747251881Speter  if (fb->skip_this)
3748251881Speter    return SVN_NO_ERROR;
3749251881Speter
3750251881Speter  /* Push a new propchange to the file baton's array of propchanges */
3751251881Speter  propchange = apr_array_push(fb->propchanges);
3752251881Speter  propchange->name = apr_pstrdup(fb->pool, name);
3753289180Speter  propchange->value = svn_string_dup(value, fb->pool);
3754251881Speter
3755251881Speter  if (!fb->edited && svn_property_kind2(name) == svn_prop_regular_kind)
3756251881Speter    SVN_ERR(mark_file_edited(fb, scratch_pool));
3757251881Speter
3758251881Speter  if (! fb->shadowed
3759251881Speter      && strcmp(name, SVN_PROP_SPECIAL) == 0)
3760251881Speter    {
3761251881Speter      struct edit_baton *eb = fb->edit_baton;
3762251881Speter      svn_boolean_t modified = FALSE;
3763251881Speter      svn_boolean_t becomes_symlink;
3764251881Speter      svn_boolean_t was_symlink;
3765251881Speter
3766251881Speter      /* Let's see if we have a change as in some scenarios servers report
3767251881Speter         non-changes of properties. */
3768251881Speter      becomes_symlink = (value != NULL);
3769251881Speter
3770251881Speter      if (fb->adding_file)
3771251881Speter        was_symlink = becomes_symlink; /* No change */
3772251881Speter      else
3773251881Speter        {
3774251881Speter          apr_hash_t *props;
3775251881Speter
3776251881Speter          /* We read the server-props, not the ACTUAL props here as we just
3777251881Speter             want to see if this is really an incoming prop change. */
3778251881Speter          SVN_ERR(svn_wc__db_base_get_props(&props, eb->db,
3779251881Speter                                            fb->local_abspath,
3780251881Speter                                            scratch_pool, scratch_pool));
3781251881Speter
3782251881Speter          was_symlink = ((props
3783251881Speter                              && svn_hash_gets(props, SVN_PROP_SPECIAL) != NULL)
3784251881Speter                              ? svn_tristate_true
3785251881Speter                              : svn_tristate_false);
3786251881Speter        }
3787251881Speter
3788251881Speter      if (was_symlink != becomes_symlink)
3789251881Speter        {
3790251881Speter          /* If the local node was not modified, we continue as usual, if
3791251881Speter             modified we want a tree conflict just like how we would handle
3792251881Speter             it when receiving a delete + add (aka "replace") */
3793251881Speter          if (fb->local_prop_mods)
3794251881Speter            modified = TRUE;
3795251881Speter          else
3796251881Speter            SVN_ERR(svn_wc__internal_file_modified_p(&modified, eb->db,
3797251881Speter                                                     fb->local_abspath,
3798251881Speter                                                     FALSE, scratch_pool));
3799251881Speter        }
3800251881Speter
3801251881Speter      if (modified)
3802251881Speter        {
3803251881Speter          if (!fb->edit_conflict)
3804251881Speter            fb->edit_conflict = svn_wc__conflict_skel_create(fb->pool);
3805251881Speter
3806251881Speter          SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
3807251881Speter                                     fb->edit_conflict,
3808251881Speter                                     eb->db, fb->local_abspath,
3809251881Speter                                     svn_wc_conflict_reason_edited,
3810251881Speter                                     svn_wc_conflict_action_replace,
3811362181Sdim                                     NULL, NULL,
3812251881Speter                                     fb->pool, scratch_pool));
3813251881Speter
3814251881Speter          SVN_ERR(complete_conflict(fb->edit_conflict, fb->edit_baton,
3815251881Speter                                    fb->local_abspath, fb->old_repos_relpath,
3816289180Speter                                    fb->old_revision, fb->new_repos_relpath,
3817251881Speter                                    svn_node_file, svn_node_file,
3818289180Speter                                    NULL, fb->pool, scratch_pool));
3819251881Speter
3820251881Speter          /* Create a copy of the existing (pre update) BASE node in WORKING,
3821251881Speter             mark a tree conflict and handle the rest of the update as
3822251881Speter             shadowed */
3823251881Speter          SVN_ERR(svn_wc__db_op_make_copy(eb->db, fb->local_abspath,
3824251881Speter                                          fb->edit_conflict, NULL,
3825251881Speter                                          scratch_pool));
3826251881Speter
3827251881Speter          do_notification(eb, fb->local_abspath, svn_node_file,
3828251881Speter                          svn_wc_notify_tree_conflict, scratch_pool);
3829251881Speter
3830251881Speter          /* Ok, we introduced a replacement, so we can now handle the rest
3831251881Speter             as a normal shadowed update */
3832251881Speter          fb->shadowed = TRUE;
3833251881Speter          fb->add_existed = FALSE;
3834251881Speter          fb->already_notified = TRUE;
3835251881Speter      }
3836251881Speter    }
3837251881Speter
3838251881Speter  return SVN_NO_ERROR;
3839251881Speter}
3840251881Speter
3841251881Speter/* Perform the actual merge of file changes between an original file,
3842251881Speter   identified by ORIGINAL_CHECKSUM (an empty file if NULL) to a new file
3843251881Speter   identified by NEW_CHECKSUM.
3844251881Speter
3845251881Speter   Merge the result into LOCAL_ABSPATH, which is part of the working copy
3846251881Speter   identified by WRI_ABSPATH. Use OLD_REVISION and TARGET_REVISION for naming
3847251881Speter   the intermediate files.
3848251881Speter
3849251881Speter   The rest of the arguments are passed to svn_wc__internal_merge().
3850251881Speter */
3851251881Spetersvn_error_t *
3852251881Spetersvn_wc__perform_file_merge(svn_skel_t **work_items,
3853251881Speter                           svn_skel_t **conflict_skel,
3854251881Speter                           svn_boolean_t *found_conflict,
3855251881Speter                           svn_wc__db_t *db,
3856251881Speter                           const char *local_abspath,
3857251881Speter                           const char *wri_abspath,
3858251881Speter                           const svn_checksum_t *new_checksum,
3859251881Speter                           const svn_checksum_t *original_checksum,
3860251881Speter                           apr_hash_t *old_actual_props,
3861251881Speter                           const apr_array_header_t *ext_patterns,
3862251881Speter                           svn_revnum_t old_revision,
3863251881Speter                           svn_revnum_t target_revision,
3864251881Speter                           const apr_array_header_t *propchanges,
3865251881Speter                           const char *diff3_cmd,
3866251881Speter                           svn_cancel_func_t cancel_func,
3867251881Speter                           void *cancel_baton,
3868251881Speter                           apr_pool_t *result_pool,
3869251881Speter                           apr_pool_t *scratch_pool)
3870251881Speter{
3871251881Speter  /* Actual file exists and has local mods:
3872251881Speter     Now we need to let loose svn_wc__internal_merge() to merge
3873251881Speter     the textual changes into the working file. */
3874251881Speter  const char *oldrev_str, *newrev_str, *mine_str;
3875251881Speter  const char *merge_left;
3876251881Speter  svn_boolean_t delete_left = FALSE;
3877251881Speter  const char *path_ext = "";
3878289180Speter  const char *new_pristine_abspath;
3879251881Speter  enum svn_wc_merge_outcome_t merge_outcome = svn_wc_merge_unchanged;
3880251881Speter  svn_skel_t *work_item;
3881251881Speter
3882251881Speter  *work_items = NULL;
3883251881Speter
3884289180Speter  SVN_ERR(svn_wc__db_pristine_get_path(&new_pristine_abspath,
3885251881Speter                                       db, wri_abspath, new_checksum,
3886251881Speter                                       scratch_pool, scratch_pool));
3887251881Speter
3888251881Speter  /* If we have any file extensions we're supposed to
3889251881Speter     preserve in generated conflict file names, then find
3890251881Speter     this path's extension.  But then, if it isn't one of
3891251881Speter     the ones we want to keep in conflict filenames,
3892251881Speter     pretend it doesn't have an extension at all. */
3893251881Speter  if (ext_patterns && ext_patterns->nelts)
3894251881Speter    {
3895251881Speter      svn_path_splitext(NULL, &path_ext, local_abspath, scratch_pool);
3896251881Speter      if (! (*path_ext && svn_cstring_match_glob_list(path_ext, ext_patterns)))
3897251881Speter        path_ext = "";
3898251881Speter    }
3899251881Speter
3900251881Speter  /* old_revision can be invalid when the conflict is against a
3901251881Speter     local addition */
3902251881Speter  if (!SVN_IS_VALID_REVNUM(old_revision))
3903251881Speter    old_revision = 0;
3904251881Speter
3905251881Speter  oldrev_str = apr_psprintf(scratch_pool, ".r%ld%s%s",
3906251881Speter                            old_revision,
3907251881Speter                            *path_ext ? "." : "",
3908251881Speter                            *path_ext ? path_ext : "");
3909251881Speter
3910251881Speter  newrev_str = apr_psprintf(scratch_pool, ".r%ld%s%s",
3911251881Speter                            target_revision,
3912251881Speter                            *path_ext ? "." : "",
3913251881Speter                            *path_ext ? path_ext : "");
3914251881Speter  mine_str = apr_psprintf(scratch_pool, ".mine%s%s",
3915251881Speter                          *path_ext ? "." : "",
3916251881Speter                          *path_ext ? path_ext : "");
3917251881Speter
3918251881Speter  if (! original_checksum)
3919251881Speter    {
3920251881Speter      SVN_ERR(get_empty_tmp_file(&merge_left, db, wri_abspath,
3921251881Speter                                 result_pool, scratch_pool));
3922251881Speter      delete_left = TRUE;
3923251881Speter    }
3924251881Speter  else
3925251881Speter    SVN_ERR(svn_wc__db_pristine_get_path(&merge_left, db, wri_abspath,
3926251881Speter                                         original_checksum,
3927251881Speter                                         result_pool, scratch_pool));
3928251881Speter
3929251881Speter  /* Merge the changes from the old textbase to the new
3930251881Speter     textbase into the file we're updating.
3931251881Speter     Remember that this function wants full paths! */
3932251881Speter  SVN_ERR(svn_wc__internal_merge(&work_item,
3933251881Speter                                 conflict_skel,
3934251881Speter                                 &merge_outcome,
3935251881Speter                                 db,
3936251881Speter                                 merge_left,
3937289180Speter                                 new_pristine_abspath,
3938251881Speter                                 local_abspath,
3939251881Speter                                 wri_abspath,
3940251881Speter                                 oldrev_str, newrev_str, mine_str,
3941251881Speter                                 old_actual_props,
3942251881Speter                                 FALSE /* dry_run */,
3943251881Speter                                 diff3_cmd, NULL, propchanges,
3944251881Speter                                 cancel_func, cancel_baton,
3945251881Speter                                 result_pool, scratch_pool));
3946251881Speter
3947251881Speter  *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
3948251881Speter  *found_conflict = (merge_outcome == svn_wc_merge_conflict);
3949251881Speter
3950251881Speter  /* If we created a temporary left merge file, get rid of it. */
3951251881Speter  if (delete_left)
3952251881Speter    {
3953251881Speter      SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, wri_abspath,
3954251881Speter                                           merge_left,
3955251881Speter                                           result_pool, scratch_pool));
3956251881Speter      *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
3957251881Speter    }
3958251881Speter
3959251881Speter  return SVN_NO_ERROR;
3960251881Speter}
3961251881Speter
3962251881Speter/* This is the small planet.  It has the complex responsibility of
3963251881Speter * "integrating" a new revision of a file into a working copy.
3964251881Speter *
3965251881Speter * Given a file_baton FB for a file either already under version control, or
3966251881Speter * prepared (see below) to join version control, fully install a
3967251881Speter * new revision of the file.
3968251881Speter *
3969251881Speter * ### transitional: installation of the working file will be handled
3970251881Speter * ### by the *INSTALL_PRISTINE flag.
3971251881Speter *
3972251881Speter * By "install", we mean: create a new text-base and prop-base, merge
3973251881Speter * any textual and property changes into the working file, and finally
3974251881Speter * update all metadata so that the working copy believes it has a new
3975251881Speter * working revision of the file.  All of this work includes being
3976251881Speter * sensitive to eol translation, keyword substitution, and performing
3977251881Speter * all actions accumulated the parent directory's work queue.
3978251881Speter *
3979251881Speter * Set *CONTENT_STATE to the state of the contents after the
3980251881Speter * installation.
3981251881Speter *
3982251881Speter * Return values are allocated in RESULT_POOL and temporary allocations
3983251881Speter * are performed in SCRATCH_POOL.
3984251881Speter */
3985251881Speterstatic svn_error_t *
3986251881Spetermerge_file(svn_skel_t **work_items,
3987251881Speter           svn_skel_t **conflict_skel,
3988251881Speter           svn_boolean_t *install_pristine,
3989251881Speter           const char **install_from,
3990251881Speter           svn_wc_notify_state_t *content_state,
3991251881Speter           struct file_baton *fb,
3992251881Speter           apr_hash_t *actual_props,
3993251881Speter           apr_time_t last_changed_date,
3994251881Speter           apr_pool_t *result_pool,
3995251881Speter           apr_pool_t *scratch_pool)
3996251881Speter{
3997251881Speter  struct edit_baton *eb = fb->edit_baton;
3998251881Speter  struct dir_baton *pb = fb->dir_baton;
3999251881Speter  svn_boolean_t is_locally_modified;
4000251881Speter  svn_boolean_t found_text_conflict = FALSE;
4001251881Speter
4002251881Speter  SVN_ERR_ASSERT(! fb->shadowed
4003251881Speter                 && ! fb->obstruction_found
4004251881Speter                 && ! fb->edit_obstructed);
4005251881Speter
4006251881Speter  /*
4007251881Speter     When this function is called on file F, we assume the following
4008251881Speter     things are true:
4009251881Speter
4010251881Speter         - The new pristine text of F is present in the pristine store
4011251881Speter           iff FB->NEW_TEXT_BASE_SHA1_CHECKSUM is not NULL.
4012251881Speter
4013251881Speter         - The WC metadata still reflects the old version of F.
4014251881Speter           (We can still access the old pristine base text of F.)
4015251881Speter
4016251881Speter     The goal is to update the local working copy of F to reflect
4017251881Speter     the changes received from the repository, preserving any local
4018251881Speter     modifications.
4019251881Speter  */
4020251881Speter
4021251881Speter  *work_items = NULL;
4022251881Speter  *install_pristine = FALSE;
4023251881Speter  *install_from = NULL;
4024251881Speter
4025251881Speter  /* Start by splitting the file path, getting an access baton for the parent,
4026251881Speter     and an entry for the file if any. */
4027251881Speter
4028251881Speter  /* Has the user made local mods to the working file?
4029251881Speter     Note that this compares to the current pristine file, which is
4030251881Speter     different from fb->old_text_base_path if we have a replaced-with-history
4031251881Speter     file.  However, in the case we had an obstruction, we check against the
4032251881Speter     new text base.
4033251881Speter   */
4034251881Speter  if (fb->adding_file && !fb->add_existed)
4035251881Speter    {
4036251881Speter      is_locally_modified = FALSE; /* There is no file: Don't check */
4037251881Speter    }
4038251881Speter  else
4039251881Speter    {
4040251881Speter      /* The working file is not an obstruction.
4041251881Speter         So: is the file modified, relative to its ORIGINAL pristine?
4042251881Speter
4043251881Speter         This function sets is_locally_modified to FALSE for
4044251881Speter         files that do not exist and for directories. */
4045251881Speter
4046251881Speter      SVN_ERR(svn_wc__internal_file_modified_p(&is_locally_modified,
4047251881Speter                                               eb->db, fb->local_abspath,
4048251881Speter                                               FALSE /* exact_comparison */,
4049251881Speter                                               scratch_pool));
4050251881Speter    }
4051251881Speter
4052251881Speter  /* For 'textual' merging, we use the following system:
4053251881Speter
4054251881Speter     When a file is modified and we have a new BASE:
4055251881Speter      - For text files
4056251881Speter          * svn_wc_merge uses diff3
4057251881Speter          * possibly makes backups and marks files as conflicted.
4058251881Speter
4059251881Speter      - For binary files
4060251881Speter          * svn_wc_merge makes backups and marks files as conflicted.
4061251881Speter
4062251881Speter     If a file is not modified and we have a new BASE:
4063251881Speter       * Install from pristine.
4064251881Speter
4065251881Speter     If we have property changes related to magic properties or if the
4066251881Speter     svn:keywords property is set:
4067251881Speter       * Retranslate from the working file.
4068251881Speter   */
4069251881Speter  if (! is_locally_modified
4070251881Speter      && fb->new_text_base_sha1_checksum)
4071251881Speter    {
4072251881Speter          /* If there are no local mods, who cares whether it's a text
4073251881Speter             or binary file!  Just write a log command to overwrite
4074251881Speter             any working file with the new text-base.  If newline
4075251881Speter             conversion or keyword substitution is activated, this
4076251881Speter             will happen as well during the copy.
4077251881Speter             For replaced files, though, we want to merge in the changes
4078251881Speter             even if the file is not modified compared to the (non-revert)
4079251881Speter             text-base. */
4080251881Speter
4081251881Speter      *install_pristine = TRUE;
4082251881Speter    }
4083251881Speter  else if (fb->new_text_base_sha1_checksum)
4084251881Speter    {
4085251881Speter      /* Actual file exists and has local mods:
4086251881Speter         Now we need to let loose svn_wc__merge_internal() to merge
4087251881Speter         the textual changes into the working file. */
4088251881Speter      SVN_ERR(svn_wc__perform_file_merge(work_items,
4089251881Speter                                         conflict_skel,
4090251881Speter                                         &found_text_conflict,
4091251881Speter                                         eb->db,
4092251881Speter                                         fb->local_abspath,
4093251881Speter                                         pb->local_abspath,
4094251881Speter                                         fb->new_text_base_sha1_checksum,
4095251881Speter                                         fb->add_existed
4096251881Speter                                                  ? NULL
4097251881Speter                                                  : fb->original_checksum,
4098251881Speter                                         actual_props,
4099251881Speter                                         eb->ext_patterns,
4100251881Speter                                         fb->old_revision,
4101251881Speter                                         *eb->target_revision,
4102251881Speter                                         fb->propchanges,
4103251881Speter                                         eb->diff3_cmd,
4104251881Speter                                         eb->cancel_func, eb->cancel_baton,
4105251881Speter                                         result_pool, scratch_pool));
4106251881Speter    } /* end: working file exists and has mods */
4107251881Speter  else
4108251881Speter    {
4109251881Speter      /* There is no new text base, but let's see if the working file needs
4110251881Speter         to be updated for any other reason. */
4111251881Speter
4112251881Speter      apr_hash_t *keywords;
4113251881Speter
4114251881Speter      /* Determine if any of the propchanges are the "magic" ones that
4115251881Speter         might require changing the working file. */
4116251881Speter      svn_boolean_t magic_props_changed;
4117251881Speter
4118251881Speter      magic_props_changed = svn_wc__has_magic_property(fb->propchanges);
4119251881Speter
4120251881Speter      SVN_ERR(svn_wc__get_translate_info(NULL, NULL,
4121251881Speter                                         &keywords,
4122251881Speter                                         NULL,
4123251881Speter                                         eb->db, fb->local_abspath,
4124251881Speter                                         actual_props, TRUE,
4125251881Speter                                         scratch_pool, scratch_pool));
4126251881Speter      if (magic_props_changed || keywords)
4127251881Speter        {
4128251881Speter          /* Special edge-case: it's possible that this file installation
4129251881Speter             only involves propchanges, but that some of those props still
4130251881Speter             require a retranslation of the working file.
4131251881Speter
4132251881Speter             OR that the file doesn't involve propchanges which by themselves
4133251881Speter             require retranslation, but receiving a change bumps the revision
4134251881Speter             number which requires re-expansion of keywords... */
4135251881Speter
4136251881Speter          if (is_locally_modified)
4137251881Speter            {
4138251881Speter              const char *tmptext;
4139251881Speter
4140251881Speter              /* Copy and DEtranslate the working file to a temp text-base.
4141251881Speter                 Note that detranslation is done according to the old props. */
4142251881Speter              SVN_ERR(svn_wc__internal_translated_file(
4143251881Speter                        &tmptext, fb->local_abspath, eb->db, fb->local_abspath,
4144251881Speter                        SVN_WC_TRANSLATE_TO_NF
4145251881Speter                          | SVN_WC_TRANSLATE_NO_OUTPUT_CLEANUP,
4146251881Speter                        eb->cancel_func, eb->cancel_baton,
4147251881Speter                        result_pool, scratch_pool));
4148251881Speter
4149251881Speter              /* We always want to reinstall the working file if the magic
4150251881Speter                 properties have changed, or there are any keywords present.
4151251881Speter                 Note that TMPTEXT might actually refer to the working file
4152251881Speter                 itself (the above function skips a detranslate when not
4153251881Speter                 required). This is acceptable, as we will (re)translate
4154251881Speter                 according to the new properties into a temporary file (from
4155251881Speter                 the working file), and then rename the temp into place. Magic!
4156251881Speter               */
4157251881Speter              *install_pristine = TRUE;
4158251881Speter              *install_from = tmptext;
4159251881Speter            }
4160251881Speter          else
4161251881Speter            {
4162251881Speter              /* Use our existing 'copy' from the pristine store instead
4163251881Speter                 of making a new copy. This way we can use the standard code
4164251881Speter                 to update the recorded size and modification time.
4165251881Speter                 (Issue #3842) */
4166251881Speter              *install_pristine = TRUE;
4167251881Speter            }
4168251881Speter        }
4169251881Speter    }
4170251881Speter
4171251881Speter  /* Set the returned content state. */
4172251881Speter
4173251881Speter  if (found_text_conflict)
4174251881Speter    *content_state = svn_wc_notify_state_conflicted;
4175251881Speter  else if (fb->new_text_base_sha1_checksum)
4176251881Speter    {
4177251881Speter      if (is_locally_modified)
4178251881Speter        *content_state = svn_wc_notify_state_merged;
4179251881Speter      else
4180251881Speter        *content_state = svn_wc_notify_state_changed;
4181251881Speter    }
4182251881Speter  else
4183251881Speter    *content_state = svn_wc_notify_state_unchanged;
4184251881Speter
4185251881Speter  return SVN_NO_ERROR;
4186251881Speter}
4187251881Speter
4188251881Speter
4189251881Speter/* An svn_delta_editor_t function. */
4190251881Speter/* Mostly a wrapper around merge_file. */
4191251881Speterstatic svn_error_t *
4192251881Speterclose_file(void *file_baton,
4193251881Speter           const char *expected_md5_digest,
4194251881Speter           apr_pool_t *pool)
4195251881Speter{
4196251881Speter  struct file_baton *fb = file_baton;
4197251881Speter  struct dir_baton *pdb = fb->dir_baton;
4198251881Speter  struct edit_baton *eb = fb->edit_baton;
4199251881Speter  svn_wc_notify_state_t content_state, prop_state;
4200251881Speter  svn_wc_notify_lock_state_t lock_state;
4201251881Speter  svn_checksum_t *expected_md5_checksum = NULL;
4202251881Speter  apr_hash_t *new_base_props = NULL;
4203251881Speter  apr_hash_t *new_actual_props = NULL;
4204251881Speter  apr_array_header_t *entry_prop_changes;
4205251881Speter  apr_array_header_t *dav_prop_changes;
4206251881Speter  apr_array_header_t *regular_prop_changes;
4207251881Speter  apr_hash_t *current_base_props = NULL;
4208251881Speter  apr_hash_t *current_actual_props = NULL;
4209251881Speter  apr_hash_t *local_actual_props = NULL;
4210251881Speter  svn_skel_t *all_work_items = NULL;
4211251881Speter  svn_skel_t *conflict_skel = NULL;
4212251881Speter  svn_skel_t *work_item;
4213251881Speter  apr_pool_t *scratch_pool = fb->pool; /* Destroyed at function exit */
4214251881Speter  svn_boolean_t keep_recorded_info = FALSE;
4215251881Speter  const svn_checksum_t *new_checksum;
4216251881Speter  apr_array_header_t *iprops = NULL;
4217251881Speter
4218251881Speter  if (fb->skip_this)
4219251881Speter    {
4220251881Speter      svn_pool_destroy(fb->pool);
4221251881Speter      SVN_ERR(maybe_release_dir_info(pdb));
4222251881Speter      return SVN_NO_ERROR;
4223251881Speter    }
4224251881Speter
4225251881Speter  if (fb->edited)
4226251881Speter    conflict_skel = fb->edit_conflict;
4227251881Speter
4228251881Speter  if (expected_md5_digest)
4229251881Speter    SVN_ERR(svn_checksum_parse_hex(&expected_md5_checksum, svn_checksum_md5,
4230251881Speter                                   expected_md5_digest, scratch_pool));
4231251881Speter
4232251881Speter  if (fb->new_text_base_md5_checksum && expected_md5_checksum
4233251881Speter      && !svn_checksum_match(expected_md5_checksum,
4234251881Speter                             fb->new_text_base_md5_checksum))
4235251881Speter    return svn_error_trace(
4236251881Speter                svn_checksum_mismatch_err(expected_md5_checksum,
4237251881Speter                                          fb->new_text_base_md5_checksum,
4238251881Speter                                          scratch_pool,
4239251881Speter                                          _("Checksum mismatch for '%s'"),
4240251881Speter                                          svn_dirent_local_style(
4241251881Speter                                                fb->local_abspath, pool)));
4242251881Speter
4243251881Speter  /* Gather the changes for each kind of property.  */
4244251881Speter  SVN_ERR(svn_categorize_props(fb->propchanges, &entry_prop_changes,
4245251881Speter                               &dav_prop_changes, &regular_prop_changes,
4246251881Speter                               scratch_pool));
4247251881Speter
4248251881Speter  /* Extract the changed_* and lock state information.  */
4249251881Speter  {
4250251881Speter    svn_revnum_t new_changed_rev;
4251251881Speter    apr_time_t new_changed_date;
4252251881Speter    const char *new_changed_author;
4253251881Speter
4254251881Speter    SVN_ERR(accumulate_last_change(&new_changed_rev,
4255251881Speter                                   &new_changed_date,
4256251881Speter                                   &new_changed_author,
4257251881Speter                                   entry_prop_changes,
4258251881Speter                                   scratch_pool, scratch_pool));
4259251881Speter
4260251881Speter    if (SVN_IS_VALID_REVNUM(new_changed_rev))
4261251881Speter      fb->changed_rev = new_changed_rev;
4262251881Speter    if (new_changed_date != 0)
4263251881Speter      fb->changed_date = new_changed_date;
4264251881Speter    if (new_changed_author != NULL)
4265251881Speter      fb->changed_author = new_changed_author;
4266251881Speter  }
4267251881Speter
4268251881Speter  /* Determine whether the file has become unlocked.  */
4269251881Speter  {
4270251881Speter    int i;
4271251881Speter
4272251881Speter    lock_state = svn_wc_notify_lock_state_unchanged;
4273251881Speter
4274251881Speter    for (i = 0; i < entry_prop_changes->nelts; ++i)
4275251881Speter      {
4276251881Speter        const svn_prop_t *prop
4277251881Speter          = &APR_ARRAY_IDX(entry_prop_changes, i, svn_prop_t);
4278251881Speter
4279251881Speter        /* If we see a change to the LOCK_TOKEN entry prop, then the only
4280251881Speter           possible change is its REMOVAL. Thus, the lock has been removed,
4281251881Speter           and we should likewise remove our cached copy of it.  */
4282251881Speter        if (! strcmp(prop->name, SVN_PROP_ENTRY_LOCK_TOKEN))
4283251881Speter          {
4284251881Speter            /* If we lose the lock, but not because we are switching to
4285251881Speter               another url, remove the state lock from the wc */
4286289180Speter            if (! eb->switch_repos_relpath
4287289180Speter                || strcmp(fb->new_repos_relpath, fb->old_repos_relpath) == 0)
4288251881Speter              {
4289251881Speter                SVN_ERR_ASSERT(prop->value == NULL);
4290289180Speter                SVN_ERR(svn_wc__db_lock_remove(eb->db, fb->local_abspath, NULL,
4291251881Speter                                               scratch_pool));
4292251881Speter
4293251881Speter                lock_state = svn_wc_notify_lock_state_unlocked;
4294251881Speter              }
4295251881Speter            break;
4296251881Speter          }
4297251881Speter      }
4298251881Speter  }
4299251881Speter
4300251881Speter  /* Install all kinds of properties.  It is important to do this before
4301251881Speter     any file content merging, since that process might expand keywords, in
4302251881Speter     which case we want the new entryprops to be in place. */
4303251881Speter
4304251881Speter  /* Write log commands to merge REGULAR_PROPS into the existing
4305251881Speter     properties of FB->LOCAL_ABSPATH.  Update *PROP_STATE to reflect
4306251881Speter     the result of the regular prop merge.
4307251881Speter
4308251881Speter     BASE_PROPS and WORKING_PROPS are hashes of the base and
4309251881Speter     working props of the file; if NULL they are read from the wc.  */
4310251881Speter
4311251881Speter  /* ### some of this feels like voodoo... */
4312251881Speter
4313251881Speter  if ((!fb->adding_file || fb->add_existed)
4314251881Speter      && !fb->shadowed)
4315251881Speter    SVN_ERR(svn_wc__get_actual_props(&local_actual_props,
4316251881Speter                                     eb->db, fb->local_abspath,
4317251881Speter                                     scratch_pool, scratch_pool));
4318251881Speter  if (local_actual_props == NULL)
4319251881Speter    local_actual_props = apr_hash_make(scratch_pool);
4320251881Speter
4321251881Speter  if (fb->add_existed)
4322251881Speter    {
4323251881Speter      /* This node already exists. Grab the current pristine properties. */
4324251881Speter      SVN_ERR(svn_wc__db_read_pristine_props(&current_base_props,
4325251881Speter                                             eb->db, fb->local_abspath,
4326251881Speter                                             scratch_pool, scratch_pool));
4327251881Speter      current_actual_props = local_actual_props;
4328251881Speter    }
4329251881Speter  else if (!fb->adding_file)
4330251881Speter    {
4331251881Speter      /* Get the BASE properties for proper merging. */
4332251881Speter      SVN_ERR(svn_wc__db_base_get_props(&current_base_props,
4333251881Speter                                        eb->db, fb->local_abspath,
4334251881Speter                                        scratch_pool, scratch_pool));
4335251881Speter      current_actual_props = local_actual_props;
4336251881Speter    }
4337251881Speter
4338251881Speter  /* Note: even if the node existed before, it may not have
4339251881Speter     pristine props (e.g a local-add)  */
4340251881Speter  if (current_base_props == NULL)
4341251881Speter    current_base_props = apr_hash_make(scratch_pool);
4342251881Speter
4343251881Speter  /* And new nodes need an empty set of ACTUAL props.  */
4344251881Speter  if (current_actual_props == NULL)
4345251881Speter    current_actual_props = apr_hash_make(scratch_pool);
4346251881Speter
4347251881Speter  prop_state = svn_wc_notify_state_unknown;
4348251881Speter
4349251881Speter  if (! fb->shadowed)
4350251881Speter    {
4351251881Speter      svn_boolean_t install_pristine;
4352251881Speter      const char *install_from = NULL;
4353251881Speter
4354251881Speter      /* Merge the 'regular' props into the existing working proplist. */
4355251881Speter      /* This will merge the old and new props into a new prop db, and
4356251881Speter         write <cp> commands to the logfile to install the merged
4357251881Speter         props.  */
4358251881Speter      new_base_props = svn_prop__patch(current_base_props, regular_prop_changes,
4359251881Speter                                       scratch_pool);
4360251881Speter      SVN_ERR(svn_wc__merge_props(&conflict_skel,
4361251881Speter                                  &prop_state,
4362251881Speter                                  &new_actual_props,
4363251881Speter                                  eb->db,
4364251881Speter                                  fb->local_abspath,
4365251881Speter                                  NULL /* server_baseprops (update, not merge)  */,
4366251881Speter                                  current_base_props,
4367251881Speter                                  current_actual_props,
4368251881Speter                                  regular_prop_changes, /* propchanges */
4369251881Speter                                  scratch_pool,
4370251881Speter                                  scratch_pool));
4371251881Speter      /* We will ALWAYS have properties to save (after a not-dry-run merge). */
4372251881Speter      SVN_ERR_ASSERT(new_base_props != NULL && new_actual_props != NULL);
4373251881Speter
4374251881Speter      /* Merge the text. This will queue some additional work.  */
4375251881Speter      if (!fb->obstruction_found && !fb->edit_obstructed)
4376251881Speter        {
4377251881Speter          svn_error_t *err;
4378251881Speter          err = merge_file(&work_item, &conflict_skel,
4379251881Speter                           &install_pristine, &install_from,
4380251881Speter                           &content_state, fb, current_actual_props,
4381251881Speter                           fb->changed_date, scratch_pool, scratch_pool);
4382251881Speter
4383251881Speter          if (err && err->apr_err == SVN_ERR_WC_PATH_ACCESS_DENIED)
4384251881Speter            {
4385251881Speter              if (eb->notify_func)
4386251881Speter                {
4387251881Speter                  svn_wc_notify_t *notify =svn_wc_create_notify(
4388251881Speter                                fb->local_abspath,
4389251881Speter                                svn_wc_notify_update_skip_access_denied,
4390251881Speter                                scratch_pool);
4391251881Speter
4392251881Speter                  notify->kind = svn_node_file;
4393251881Speter                  notify->err = err;
4394251881Speter
4395251881Speter                  eb->notify_func(eb->notify_baton, notify, scratch_pool);
4396251881Speter                }
4397251881Speter              svn_error_clear(err);
4398251881Speter
4399251881Speter              SVN_ERR(remember_skipped_tree(eb, fb->local_abspath,
4400251881Speter                                            scratch_pool));
4401251881Speter              fb->skip_this = TRUE;
4402251881Speter
4403251881Speter              svn_pool_destroy(fb->pool);
4404251881Speter              SVN_ERR(maybe_release_dir_info(pdb));
4405251881Speter              return SVN_NO_ERROR;
4406251881Speter            }
4407251881Speter          else
4408251881Speter            SVN_ERR(err);
4409251881Speter
4410251881Speter          all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4411251881Speter                                            scratch_pool);
4412251881Speter        }
4413251881Speter      else
4414251881Speter        {
4415251881Speter          install_pristine = FALSE;
4416251881Speter          if (fb->new_text_base_sha1_checksum)
4417251881Speter            content_state = svn_wc_notify_state_changed;
4418251881Speter          else
4419251881Speter            content_state = svn_wc_notify_state_unchanged;
4420251881Speter        }
4421251881Speter
4422251881Speter      if (install_pristine)
4423251881Speter        {
4424251881Speter          svn_boolean_t record_fileinfo;
4425251881Speter
4426251881Speter          /* If we are installing from the pristine contents, then go ahead and
4427251881Speter             record the fileinfo. That will be the "proper" values. Installing
4428251881Speter             from some random file means the fileinfo does NOT correspond to
4429251881Speter             the pristine (in which case, the fileinfo will be cleared for
4430251881Speter             safety's sake).  */
4431251881Speter          record_fileinfo = (install_from == NULL);
4432251881Speter
4433251881Speter          SVN_ERR(svn_wc__wq_build_file_install(&work_item,
4434251881Speter                                                eb->db,
4435251881Speter                                                fb->local_abspath,
4436251881Speter                                                install_from,
4437251881Speter                                                eb->use_commit_times,
4438251881Speter                                                record_fileinfo,
4439251881Speter                                                scratch_pool, scratch_pool));
4440251881Speter          all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4441251881Speter                                            scratch_pool);
4442251881Speter        }
4443251881Speter      else if (lock_state == svn_wc_notify_lock_state_unlocked
4444251881Speter               && !fb->obstruction_found)
4445251881Speter        {
4446251881Speter          /* If a lock was removed and we didn't update the text contents, we
4447251881Speter             might need to set the file read-only.
4448251881Speter
4449251881Speter             Note: this will also update the executable flag, but ... meh.  */
4450251881Speter          SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, eb->db,
4451251881Speter                                                   fb->local_abspath,
4452251881Speter                                                   scratch_pool, scratch_pool));
4453251881Speter          all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4454251881Speter                                            scratch_pool);
4455251881Speter        }
4456251881Speter
4457251881Speter      if (! install_pristine
4458251881Speter          && (content_state == svn_wc_notify_state_unchanged))
4459251881Speter        {
4460251881Speter          /* It is safe to keep the current recorded timestamp and size */
4461251881Speter          keep_recorded_info = TRUE;
4462251881Speter        }
4463251881Speter
4464251881Speter      /* Clean up any temporary files.  */
4465251881Speter
4466251881Speter      /* Remove the INSTALL_FROM file, as long as it doesn't refer to the
4467251881Speter         working file.  */
4468251881Speter      if (install_from != NULL
4469251881Speter          && strcmp(install_from, fb->local_abspath) != 0)
4470251881Speter        {
4471251881Speter          SVN_ERR(svn_wc__wq_build_file_remove(&work_item, eb->db,
4472251881Speter                                               fb->local_abspath, install_from,
4473251881Speter                                               scratch_pool, scratch_pool));
4474251881Speter          all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4475251881Speter                                            scratch_pool);
4476251881Speter        }
4477251881Speter    }
4478251881Speter  else
4479251881Speter    {
4480251881Speter      /* Adding or updating a BASE node under a locally added node. */
4481251881Speter      apr_hash_t *fake_actual_props;
4482251881Speter
4483251881Speter      if (fb->adding_file)
4484251881Speter        fake_actual_props = apr_hash_make(scratch_pool);
4485251881Speter      else
4486251881Speter        fake_actual_props = current_base_props;
4487251881Speter
4488251881Speter      /* Store the incoming props (sent as propchanges) in new_base_props
4489251881Speter         and create a set of new actual props to use for notifications */
4490251881Speter      new_base_props = svn_prop__patch(current_base_props, regular_prop_changes,
4491251881Speter                                       scratch_pool);
4492251881Speter      SVN_ERR(svn_wc__merge_props(&conflict_skel,
4493251881Speter                                  &prop_state,
4494251881Speter                                  &new_actual_props,
4495251881Speter                                  eb->db,
4496251881Speter                                  fb->local_abspath,
4497251881Speter                                  NULL /* server_baseprops (not merging) */,
4498251881Speter                                  current_base_props /* pristine_props */,
4499251881Speter                                  fake_actual_props /* actual_props */,
4500251881Speter                                  regular_prop_changes, /* propchanges */
4501251881Speter                                  scratch_pool,
4502251881Speter                                  scratch_pool));
4503251881Speter
4504251881Speter      if (fb->new_text_base_sha1_checksum)
4505251881Speter        content_state = svn_wc_notify_state_changed;
4506251881Speter      else
4507251881Speter        content_state = svn_wc_notify_state_unchanged;
4508251881Speter    }
4509251881Speter
4510251881Speter  /* Insert/replace the BASE node with all of the new metadata.  */
4511251881Speter
4512251881Speter  /* Set the 'checksum' column of the file's BASE_NODE row to
4513251881Speter   * NEW_TEXT_BASE_SHA1_CHECKSUM.  The pristine text identified by that
4514251881Speter   * checksum is already in the pristine store. */
4515251881Speter  new_checksum = fb->new_text_base_sha1_checksum;
4516251881Speter
4517251881Speter  /* If we don't have a NEW checksum, then the base must not have changed.
4518251881Speter     Just carry over the old checksum.  */
4519251881Speter  if (new_checksum == NULL)
4520251881Speter    new_checksum = fb->original_checksum;
4521251881Speter
4522251881Speter  if (conflict_skel)
4523251881Speter    {
4524251881Speter      SVN_ERR(complete_conflict(conflict_skel,
4525251881Speter                                fb->edit_baton,
4526251881Speter                                fb->local_abspath,
4527251881Speter                                fb->old_repos_relpath,
4528251881Speter                                fb->old_revision,
4529289180Speter                                fb->new_repos_relpath,
4530251881Speter                                svn_node_file, svn_node_file,
4531289180Speter                                fb->dir_baton->deletion_conflicts
4532289180Speter                                  ? svn_hash_gets(
4533289180Speter                                        fb->dir_baton->deletion_conflicts,
4534289180Speter                                        fb->name)
4535289180Speter                                  : NULL,
4536251881Speter                                fb->pool, scratch_pool));
4537251881Speter
4538251881Speter      SVN_ERR(svn_wc__conflict_create_markers(&work_item,
4539251881Speter                                              eb->db, fb->local_abspath,
4540251881Speter                                              conflict_skel,
4541251881Speter                                              scratch_pool, scratch_pool));
4542251881Speter
4543251881Speter      all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4544251881Speter                                        scratch_pool);
4545251881Speter    }
4546251881Speter
4547251881Speter  /* Any inherited props to be set set for this base node? */
4548251881Speter  if (eb->wcroot_iprops)
4549251881Speter    {
4550251881Speter      iprops = svn_hash_gets(eb->wcroot_iprops, fb->local_abspath);
4551251881Speter
4552251881Speter      /* close_edit may also update iprops for switched nodes, catching
4553251881Speter         those for which close_directory is never called (e.g. a switch
4554251881Speter         with no changes).  So as a minor optimization we remove any
4555251881Speter         iprops from the hash so as not to set them again in
4556251881Speter         close_edit. */
4557251881Speter      if (iprops)
4558251881Speter        svn_hash_sets(eb->wcroot_iprops, fb->local_abspath, NULL);
4559251881Speter    }
4560251881Speter
4561251881Speter  SVN_ERR(svn_wc__db_base_add_file(eb->db, fb->local_abspath,
4562251881Speter                                   eb->wcroot_abspath,
4563289180Speter                                   fb->new_repos_relpath,
4564251881Speter                                   eb->repos_root, eb->repos_uuid,
4565251881Speter                                   *eb->target_revision,
4566251881Speter                                   new_base_props,
4567251881Speter                                   fb->changed_rev,
4568251881Speter                                   fb->changed_date,
4569251881Speter                                   fb->changed_author,
4570251881Speter                                   new_checksum,
4571251881Speter                                   (dav_prop_changes->nelts > 0)
4572251881Speter                                     ? svn_prop_array_to_hash(
4573251881Speter                                                      dav_prop_changes,
4574251881Speter                                                      scratch_pool)
4575251881Speter                                     : NULL,
4576251881Speter                                   (fb->add_existed && fb->adding_file),
4577251881Speter                                   (! fb->shadowed) && new_base_props,
4578251881Speter                                   new_actual_props,
4579251881Speter                                   iprops,
4580251881Speter                                   keep_recorded_info,
4581251881Speter                                   (fb->shadowed && fb->obstruction_found),
4582251881Speter                                   conflict_skel,
4583251881Speter                                   all_work_items,
4584251881Speter                                   scratch_pool));
4585251881Speter
4586251881Speter  if (conflict_skel && eb->conflict_func)
4587251881Speter    SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, fb->local_abspath,
4588289180Speter                                             svn_node_file,
4589251881Speter                                             conflict_skel,
4590251881Speter                                             NULL /* merge_options */,
4591251881Speter                                             eb->conflict_func,
4592251881Speter                                             eb->conflict_baton,
4593251881Speter                                             eb->cancel_func,
4594251881Speter                                             eb->cancel_baton,
4595251881Speter                                             scratch_pool));
4596251881Speter
4597251881Speter  /* Deal with the WORKING tree, based on updates to the BASE tree.  */
4598251881Speter
4599289180Speter  svn_hash_sets(fb->dir_baton->not_present_nodes, fb->name, NULL);
4600251881Speter
4601251881Speter  /* Send a notification to the callback function.  (Skip notifications
4602251881Speter     about files which were already notified for another reason.) */
4603251881Speter  if (eb->notify_func && !fb->already_notified
4604251881Speter      && (fb->edited || lock_state == svn_wc_notify_lock_state_unlocked))
4605251881Speter    {
4606251881Speter      svn_wc_notify_t *notify;
4607251881Speter      svn_wc_notify_action_t action = svn_wc_notify_update_update;
4608251881Speter
4609251881Speter      if (fb->edited)
4610251881Speter        {
4611251881Speter          if (fb->shadowed || fb->edit_obstructed)
4612251881Speter            action = fb->adding_file
4613251881Speter                            ? svn_wc_notify_update_shadowed_add
4614251881Speter                            : svn_wc_notify_update_shadowed_update;
4615251881Speter          else if (fb->obstruction_found || fb->add_existed)
4616251881Speter            {
4617251881Speter              if (content_state != svn_wc_notify_state_conflicted)
4618251881Speter                action = svn_wc_notify_exists;
4619251881Speter            }
4620251881Speter          else if (fb->adding_file)
4621251881Speter            {
4622251881Speter              action = svn_wc_notify_update_add;
4623251881Speter            }
4624251881Speter        }
4625251881Speter      else
4626251881Speter        {
4627251881Speter          SVN_ERR_ASSERT(lock_state == svn_wc_notify_lock_state_unlocked);
4628251881Speter          action = svn_wc_notify_update_broken_lock;
4629251881Speter        }
4630251881Speter
4631251881Speter      /* If the file was moved-away, notify for the moved-away node.
4632251881Speter       * The original location only had its BASE info changed and
4633251881Speter       * we don't usually notify about such changes. */
4634251881Speter      notify = svn_wc_create_notify(fb->local_abspath, action, scratch_pool);
4635251881Speter      notify->kind = svn_node_file;
4636251881Speter      notify->content_state = content_state;
4637251881Speter      notify->prop_state = prop_state;
4638251881Speter      notify->lock_state = lock_state;
4639251881Speter      notify->revision = *eb->target_revision;
4640251881Speter      notify->old_revision = fb->old_revision;
4641251881Speter
4642251881Speter      /* Fetch the mimetype from the actual properties */
4643251881Speter      notify->mime_type = svn_prop_get_value(new_actual_props,
4644251881Speter                                             SVN_PROP_MIME_TYPE);
4645251881Speter
4646251881Speter      eb->notify_func(eb->notify_baton, notify, scratch_pool);
4647251881Speter    }
4648251881Speter
4649251881Speter  svn_pool_destroy(fb->pool); /* Destroy scratch_pool */
4650251881Speter
4651251881Speter  /* We have one less referrer to the directory */
4652251881Speter  SVN_ERR(maybe_release_dir_info(pdb));
4653251881Speter
4654251881Speter  return SVN_NO_ERROR;
4655251881Speter}
4656251881Speter
4657251881Speter
4658289180Speter/* Implements svn_wc__proplist_receiver_t.
4659289180Speter * Check for the presence of an svn:keywords property and queues an install_file
4660289180Speter * work queue item if present. Thus, when the work queue is run to complete the
4661289180Speter * switch operation, all files with keywords will go through the translation
4662289180Speter * process so URLs etc are updated. */
4663289180Speterstatic svn_error_t *
4664289180Speterupdate_keywords_after_switch_cb(void *baton,
4665289180Speter                                const char *local_abspath,
4666289180Speter                                apr_hash_t *props,
4667289180Speter                                apr_pool_t *scratch_pool)
4668289180Speter{
4669289180Speter  struct edit_baton *eb = baton;
4670289180Speter  svn_string_t *propval;
4671289180Speter  svn_boolean_t modified;
4672289180Speter  svn_boolean_t record_fileinfo;
4673289180Speter  svn_skel_t *work_items;
4674289180Speter  const char *install_from;
4675289180Speter
4676289180Speter  propval = svn_hash_gets(props, SVN_PROP_KEYWORDS);
4677289180Speter  if (!propval)
4678289180Speter    return SVN_NO_ERROR;
4679289180Speter
4680289180Speter  SVN_ERR(svn_wc__internal_file_modified_p(&modified, eb->db,
4681289180Speter                                           local_abspath, FALSE,
4682289180Speter                                           scratch_pool));
4683289180Speter  if (modified)
4684289180Speter    {
4685289180Speter      const char *temp_dir_abspath;
4686289180Speter      svn_stream_t *working_stream;
4687289180Speter      svn_stream_t *install_from_stream;
4688289180Speter
4689289180Speter      SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, eb->db,
4690289180Speter                                             local_abspath, scratch_pool,
4691289180Speter                                             scratch_pool));
4692289180Speter      SVN_ERR(svn_stream_open_readonly(&working_stream, local_abspath,
4693289180Speter                                       scratch_pool, scratch_pool));
4694289180Speter      SVN_ERR(svn_stream_open_unique(&install_from_stream, &install_from,
4695289180Speter                                     temp_dir_abspath, svn_io_file_del_none,
4696289180Speter                                     scratch_pool, scratch_pool));
4697289180Speter      SVN_ERR(svn_stream_copy3(working_stream, install_from_stream,
4698289180Speter                               eb->cancel_func, eb->cancel_baton,
4699289180Speter                               scratch_pool));
4700289180Speter      record_fileinfo = FALSE;
4701289180Speter    }
4702289180Speter  else
4703289180Speter    {
4704289180Speter      install_from = NULL;
4705289180Speter      record_fileinfo = TRUE;
4706289180Speter    }
4707289180Speter
4708289180Speter  SVN_ERR(svn_wc__wq_build_file_install(&work_items, eb->db, local_abspath,
4709289180Speter                                        install_from,
4710289180Speter                                        eb->use_commit_times,
4711289180Speter                                        record_fileinfo,
4712289180Speter                                        scratch_pool, scratch_pool));
4713289180Speter  if (install_from)
4714289180Speter    {
4715289180Speter      svn_skel_t *work_item;
4716289180Speter
4717289180Speter      SVN_ERR(svn_wc__wq_build_file_remove(&work_item, eb->db,
4718289180Speter                                           local_abspath, install_from,
4719289180Speter                                           scratch_pool, scratch_pool));
4720289180Speter      work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
4721289180Speter    }
4722289180Speter
4723289180Speter  SVN_ERR(svn_wc__db_wq_add(eb->db, local_abspath, work_items,
4724289180Speter                            scratch_pool));
4725289180Speter
4726289180Speter  return SVN_NO_ERROR;
4727289180Speter}
4728289180Speter
4729289180Speter
4730251881Speter/* An svn_delta_editor_t function. */
4731251881Speterstatic svn_error_t *
4732251881Speterclose_edit(void *edit_baton,
4733251881Speter           apr_pool_t *pool)
4734251881Speter{
4735251881Speter  struct edit_baton *eb = edit_baton;
4736251881Speter  apr_pool_t *scratch_pool = eb->pool;
4737251881Speter
4738251881Speter  /* The editor didn't even open the root; we have to take care of
4739251881Speter     some cleanup stuffs. */
4740251881Speter  if (! eb->root_opened
4741251881Speter      && *eb->target_basename == '\0')
4742251881Speter    {
4743251881Speter      /* We need to "un-incomplete" the root directory. */
4744251881Speter      SVN_ERR(svn_wc__db_temp_op_end_directory_update(eb->db,
4745251881Speter                                                      eb->anchor_abspath,
4746251881Speter                                                      scratch_pool));
4747251881Speter    }
4748251881Speter
4749251881Speter  /* By definition, anybody "driving" this editor for update or switch
4750251881Speter     purposes at a *minimum* must have called set_target_revision() at
4751251881Speter     the outset, and close_edit() at the end -- even if it turned out
4752251881Speter     that no changes ever had to be made, and open_root() was never
4753251881Speter     called.  That's fine.  But regardless, when the edit is over,
4754251881Speter     this editor needs to make sure that *all* paths have had their
4755251881Speter     revisions bumped to the new target revision. */
4756251881Speter
4757251881Speter  /* Make sure our update target now has the new working revision.
4758251881Speter     Also, if this was an 'svn switch', then rewrite the target's
4759251881Speter     url.  All of this tweaking might happen recursively!  Note
4760251881Speter     that if eb->target is NULL, that's okay (albeit "sneaky",
4761251881Speter     some might say).  */
4762251881Speter
4763251881Speter  /* Extra check: if the update did nothing but make its target
4764251881Speter     'deleted', then do *not* run cleanup on the target, as it
4765251881Speter     will only remove the deleted entry!  */
4766251881Speter  if (! eb->target_deleted)
4767251881Speter    {
4768251881Speter      SVN_ERR(svn_wc__db_op_bump_revisions_post_update(eb->db,
4769251881Speter                                                       eb->target_abspath,
4770251881Speter                                                       eb->requested_depth,
4771289180Speter                                                       eb->switch_repos_relpath,
4772251881Speter                                                       eb->repos_root,
4773251881Speter                                                       eb->repos_uuid,
4774251881Speter                                                       *(eb->target_revision),
4775251881Speter                                                       eb->skipped_trees,
4776251881Speter                                                       eb->wcroot_iprops,
4777289180Speter                                                       ! eb->edited,
4778251881Speter                                                       eb->notify_func,
4779251881Speter                                                       eb->notify_baton,
4780251881Speter                                                       eb->pool));
4781251881Speter
4782251881Speter      if (*eb->target_basename != '\0')
4783251881Speter        {
4784251881Speter          svn_wc__db_status_t status;
4785251881Speter          svn_error_t *err;
4786251881Speter
4787251881Speter          /* Note: we are fetching information about the *target*, not anchor.
4788251881Speter             There is no guarantee that the target has a BASE node.
4789251881Speter             For example:
4790251881Speter
4791251881Speter               The node was not present in BASE, but locally-added, and the
4792251881Speter               update did not create a new BASE node "under" the local-add.
4793251881Speter
4794251881Speter             If there is no BASE node for the target, then we certainly don't
4795251881Speter             have to worry about removing it. */
4796251881Speter          err = svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL, NULL,
4797251881Speter                                         NULL, NULL, NULL, NULL, NULL, NULL,
4798251881Speter                                         NULL, NULL, NULL, NULL,
4799251881Speter                                         eb->db, eb->target_abspath,
4800251881Speter                                         scratch_pool, scratch_pool);
4801251881Speter          if (err)
4802251881Speter            {
4803251881Speter              if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
4804251881Speter                return svn_error_trace(err);
4805251881Speter
4806251881Speter              svn_error_clear(err);
4807251881Speter            }
4808251881Speter          else if (status == svn_wc__db_status_excluded)
4809251881Speter            {
4810251881Speter              /* There is a small chance that the explicit target of an update/
4811251881Speter                 switch is gone in the repository, in that specific case the
4812251881Speter                 node hasn't been re-added to the BASE tree by this update.
4813251881Speter
4814251881Speter                 If so, we should get rid of this excluded node now. */
4815251881Speter
4816251881Speter              SVN_ERR(svn_wc__db_base_remove(eb->db, eb->target_abspath,
4817289180Speter                                             TRUE, FALSE, FALSE,
4818251881Speter                                             SVN_INVALID_REVNUM,
4819251881Speter                                             NULL, NULL, scratch_pool));
4820251881Speter            }
4821251881Speter        }
4822251881Speter    }
4823251881Speter
4824289180Speter  /* Update keywords in switched files.
4825289180Speter     GOTO #1975 (the year of the Altair 8800). */
4826289180Speter  if (eb->switch_repos_relpath)
4827289180Speter    {
4828289180Speter      svn_depth_t depth;
4829289180Speter
4830289180Speter      if (eb->requested_depth > svn_depth_empty)
4831289180Speter        depth = eb->requested_depth;
4832289180Speter      else
4833289180Speter        depth = svn_depth_infinity;
4834289180Speter
4835289180Speter      SVN_ERR(svn_wc__db_read_props_streamily(eb->db,
4836289180Speter                                              eb->target_abspath,
4837289180Speter                                              depth,
4838289180Speter                                              FALSE, /* pristine */
4839289180Speter                                              NULL, /* changelists */
4840289180Speter                                              update_keywords_after_switch_cb,
4841289180Speter                                              eb,
4842289180Speter                                              eb->cancel_func,
4843289180Speter                                              eb->cancel_baton,
4844289180Speter                                              scratch_pool));
4845289180Speter    }
4846289180Speter
4847251881Speter  /* The edit is over: run the wq with proper cancel support,
4848251881Speter     but first kill the handler that would run it on the pool
4849251881Speter     cleanup at the end of this function. */
4850251881Speter  apr_pool_cleanup_kill(eb->pool, eb, cleanup_edit_baton);
4851251881Speter
4852251881Speter  SVN_ERR(svn_wc__wq_run(eb->db, eb->wcroot_abspath,
4853251881Speter                         eb->cancel_func, eb->cancel_baton,
4854251881Speter                         eb->pool));
4855251881Speter
4856251881Speter  /* The edit is over, free its pool.
4857251881Speter     ### No, this is wrong.  Who says this editor/baton won't be used
4858251881Speter     again?  But the change is not merely to remove this call.  We
4859251881Speter     should also make eb->pool not be a subpool (see make_editor),
4860251881Speter     and change callers of svn_client_{checkout,update,switch} to do
4861251881Speter     better pool management. ### */
4862251881Speter
4863251881Speter  svn_pool_destroy(eb->pool);
4864251881Speter
4865251881Speter  return SVN_NO_ERROR;
4866251881Speter}
4867251881Speter
4868251881Speter
4869251881Speter/*** Returning editors. ***/
4870251881Speter
4871251881Speter/* Helper for the three public editor-supplying functions. */
4872251881Speterstatic svn_error_t *
4873251881Spetermake_editor(svn_revnum_t *target_revision,
4874251881Speter            svn_wc__db_t *db,
4875251881Speter            const char *anchor_abspath,
4876251881Speter            const char *target_basename,
4877251881Speter            apr_hash_t *wcroot_iprops,
4878251881Speter            svn_boolean_t use_commit_times,
4879251881Speter            const char *switch_url,
4880251881Speter            svn_depth_t depth,
4881251881Speter            svn_boolean_t depth_is_sticky,
4882251881Speter            svn_boolean_t allow_unver_obstructions,
4883251881Speter            svn_boolean_t adds_as_modification,
4884251881Speter            svn_boolean_t server_performs_filtering,
4885251881Speter            svn_boolean_t clean_checkout,
4886251881Speter            svn_wc_notify_func2_t notify_func,
4887251881Speter            void *notify_baton,
4888251881Speter            svn_cancel_func_t cancel_func,
4889251881Speter            void *cancel_baton,
4890251881Speter            svn_wc_dirents_func_t fetch_dirents_func,
4891251881Speter            void *fetch_dirents_baton,
4892251881Speter            svn_wc_conflict_resolver_func2_t conflict_func,
4893251881Speter            void *conflict_baton,
4894251881Speter            svn_wc_external_update_t external_func,
4895251881Speter            void *external_baton,
4896251881Speter            const char *diff3_cmd,
4897251881Speter            const apr_array_header_t *preserved_exts,
4898251881Speter            const svn_delta_editor_t **editor,
4899251881Speter            void **edit_baton,
4900251881Speter            apr_pool_t *result_pool,
4901251881Speter            apr_pool_t *scratch_pool)
4902251881Speter{
4903251881Speter  struct edit_baton *eb;
4904251881Speter  void *inner_baton;
4905251881Speter  apr_pool_t *edit_pool = svn_pool_create(result_pool);
4906251881Speter  svn_delta_editor_t *tree_editor = svn_delta_default_editor(edit_pool);
4907251881Speter  const svn_delta_editor_t *inner_editor;
4908251881Speter  const char *repos_root, *repos_uuid;
4909251881Speter  struct svn_wc__shim_fetch_baton_t *sfb;
4910251881Speter  svn_delta_shim_callbacks_t *shim_callbacks =
4911251881Speter                                svn_delta_shim_callbacks_default(edit_pool);
4912251881Speter
4913251881Speter  /* An unknown depth can't be sticky. */
4914251881Speter  if (depth == svn_depth_unknown)
4915251881Speter    depth_is_sticky = FALSE;
4916251881Speter
4917251881Speter  /* Get the anchor's repository root and uuid. The anchor must already exist
4918251881Speter     in BASE. */
4919289180Speter  SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, &repos_root,
4920289180Speter                                   &repos_uuid, NULL, NULL, NULL, NULL,
4921289180Speter                                   NULL, NULL, NULL, NULL, NULL, NULL,
4922289180Speter                                   db, anchor_abspath,
4923289180Speter                                   result_pool, scratch_pool));
4924251881Speter
4925251881Speter  /* With WC-NG we need a valid repository root */
4926251881Speter  SVN_ERR_ASSERT(repos_root != NULL && repos_uuid != NULL);
4927251881Speter
4928251881Speter  /* Disallow a switch operation to change the repository root of the target,
4929251881Speter     if that is known. */
4930251881Speter  if (switch_url && !svn_uri__is_ancestor(repos_root, switch_url))
4931251881Speter    return svn_error_createf(SVN_ERR_WC_INVALID_SWITCH, NULL,
4932251881Speter                             _("'%s'\nis not the same repository as\n'%s'"),
4933251881Speter                             switch_url, repos_root);
4934251881Speter
4935251881Speter  /* Construct an edit baton. */
4936251881Speter  eb = apr_pcalloc(edit_pool, sizeof(*eb));
4937251881Speter  eb->pool                     = edit_pool;
4938251881Speter  eb->use_commit_times         = use_commit_times;
4939251881Speter  eb->target_revision          = target_revision;
4940251881Speter  eb->repos_root               = repos_root;
4941251881Speter  eb->repos_uuid               = repos_uuid;
4942251881Speter  eb->db                       = db;
4943251881Speter  eb->target_basename          = target_basename;
4944251881Speter  eb->anchor_abspath           = anchor_abspath;
4945251881Speter  eb->wcroot_iprops            = wcroot_iprops;
4946251881Speter
4947251881Speter  SVN_ERR(svn_wc__db_get_wcroot(&eb->wcroot_abspath, db, anchor_abspath,
4948251881Speter                                edit_pool, scratch_pool));
4949251881Speter
4950251881Speter  if (switch_url)
4951289180Speter    eb->switch_repos_relpath =
4952251881Speter      svn_uri_skip_ancestor(repos_root, switch_url, scratch_pool);
4953251881Speter  else
4954289180Speter    eb->switch_repos_relpath = NULL;
4955251881Speter
4956251881Speter  if (svn_path_is_empty(target_basename))
4957251881Speter    eb->target_abspath = eb->anchor_abspath;
4958251881Speter  else
4959251881Speter    eb->target_abspath = svn_dirent_join(eb->anchor_abspath, target_basename,
4960251881Speter                                         edit_pool);
4961251881Speter
4962251881Speter  eb->requested_depth          = depth;
4963251881Speter  eb->depth_is_sticky          = depth_is_sticky;
4964251881Speter  eb->notify_func              = notify_func;
4965251881Speter  eb->notify_baton             = notify_baton;
4966251881Speter  eb->external_func            = external_func;
4967251881Speter  eb->external_baton           = external_baton;
4968251881Speter  eb->diff3_cmd                = diff3_cmd;
4969251881Speter  eb->cancel_func              = cancel_func;
4970251881Speter  eb->cancel_baton             = cancel_baton;
4971251881Speter  eb->conflict_func            = conflict_func;
4972251881Speter  eb->conflict_baton           = conflict_baton;
4973251881Speter  eb->allow_unver_obstructions = allow_unver_obstructions;
4974251881Speter  eb->adds_as_modification     = adds_as_modification;
4975251881Speter  eb->clean_checkout           = clean_checkout;
4976251881Speter  eb->skipped_trees            = apr_hash_make(edit_pool);
4977251881Speter  eb->dir_dirents              = apr_hash_make(edit_pool);
4978251881Speter  eb->ext_patterns             = preserved_exts;
4979251881Speter
4980251881Speter  apr_pool_cleanup_register(edit_pool, eb, cleanup_edit_baton,
4981251881Speter                            apr_pool_cleanup_null);
4982251881Speter
4983251881Speter  /* Construct an editor. */
4984251881Speter  tree_editor->set_target_revision = set_target_revision;
4985251881Speter  tree_editor->open_root = open_root;
4986251881Speter  tree_editor->delete_entry = delete_entry;
4987251881Speter  tree_editor->add_directory = add_directory;
4988251881Speter  tree_editor->open_directory = open_directory;
4989251881Speter  tree_editor->change_dir_prop = change_dir_prop;
4990251881Speter  tree_editor->close_directory = close_directory;
4991251881Speter  tree_editor->absent_directory = absent_directory;
4992251881Speter  tree_editor->add_file = add_file;
4993251881Speter  tree_editor->open_file = open_file;
4994251881Speter  tree_editor->apply_textdelta = apply_textdelta;
4995251881Speter  tree_editor->change_file_prop = change_file_prop;
4996251881Speter  tree_editor->close_file = close_file;
4997251881Speter  tree_editor->absent_file = absent_file;
4998251881Speter  tree_editor->close_edit = close_edit;
4999251881Speter
5000251881Speter  /* Fiddle with the type system. */
5001251881Speter  inner_editor = tree_editor;
5002251881Speter  inner_baton = eb;
5003251881Speter
5004251881Speter  if (!depth_is_sticky
5005251881Speter      && depth != svn_depth_unknown
5006251881Speter      && svn_depth_empty <= depth && depth < svn_depth_infinity
5007251881Speter      && fetch_dirents_func)
5008251881Speter    {
5009251881Speter      /* We are asked to perform an update at a depth less than the ambient
5010251881Speter         depth. In this case the update won't describe additions that would
5011251881Speter         have been reported if we updated at the ambient depth. */
5012251881Speter      svn_error_t *err;
5013251881Speter      svn_node_kind_t dir_kind;
5014251881Speter      svn_wc__db_status_t dir_status;
5015251881Speter      const char *dir_repos_relpath;
5016251881Speter      svn_depth_t dir_depth;
5017251881Speter
5018251881Speter      /* we have to do this on the target of the update, not the anchor */
5019251881Speter      err = svn_wc__db_base_get_info(&dir_status, &dir_kind, NULL,
5020251881Speter                                     &dir_repos_relpath, NULL, NULL, NULL,
5021251881Speter                                     NULL, NULL, &dir_depth, NULL, NULL, NULL,
5022251881Speter                                     NULL, NULL, NULL,
5023251881Speter                                     db, eb->target_abspath,
5024251881Speter                                     scratch_pool, scratch_pool);
5025251881Speter
5026251881Speter      if (!err
5027251881Speter          && dir_kind == svn_node_dir
5028251881Speter          && dir_status == svn_wc__db_status_normal)
5029251881Speter        {
5030251881Speter          if (dir_depth > depth)
5031251881Speter            {
5032251881Speter              apr_hash_t *dirents;
5033251881Speter
5034251881Speter              /* If we switch, we should look at the new relpath */
5035289180Speter              if (eb->switch_repos_relpath)
5036289180Speter                dir_repos_relpath = eb->switch_repos_relpath;
5037251881Speter
5038251881Speter              SVN_ERR(fetch_dirents_func(fetch_dirents_baton, &dirents,
5039251881Speter                                         repos_root, dir_repos_relpath,
5040251881Speter                                         edit_pool, scratch_pool));
5041251881Speter
5042251881Speter              if (dirents != NULL && apr_hash_count(dirents))
5043251881Speter                svn_hash_sets(eb->dir_dirents,
5044251881Speter                              apr_pstrdup(edit_pool, dir_repos_relpath),
5045251881Speter                              dirents);
5046251881Speter            }
5047251881Speter
5048251881Speter          if (depth == svn_depth_immediates)
5049251881Speter            {
5050251881Speter              /* Worst case scenario of issue #3569 fix: We have to do the
5051251881Speter                 same for all existing subdirs, but then we check for
5052251881Speter                 svn_depth_empty. */
5053251881Speter              const apr_array_header_t *children;
5054251881Speter              apr_pool_t *iterpool = svn_pool_create(scratch_pool);
5055251881Speter              int i;
5056251881Speter              SVN_ERR(svn_wc__db_base_get_children(&children, db,
5057251881Speter                                                   eb->target_abspath,
5058251881Speter                                                   scratch_pool,
5059251881Speter                                                   iterpool));
5060251881Speter
5061251881Speter              for (i = 0; i < children->nelts; i++)
5062251881Speter                {
5063251881Speter                  const char *child_abspath;
5064251881Speter                  const char *child_name;
5065251881Speter
5066251881Speter                  svn_pool_clear(iterpool);
5067251881Speter
5068251881Speter                  child_name = APR_ARRAY_IDX(children, i, const char *);
5069251881Speter
5070251881Speter                  child_abspath = svn_dirent_join(eb->target_abspath,
5071251881Speter                                                  child_name, iterpool);
5072251881Speter
5073251881Speter                  SVN_ERR(svn_wc__db_base_get_info(&dir_status, &dir_kind,
5074251881Speter                                                   NULL, &dir_repos_relpath,
5075251881Speter                                                   NULL, NULL, NULL, NULL,
5076251881Speter                                                   NULL, &dir_depth, NULL,
5077251881Speter                                                   NULL, NULL, NULL, NULL,
5078251881Speter                                                   NULL,
5079251881Speter                                                   db, child_abspath,
5080251881Speter                                                   iterpool, iterpool));
5081251881Speter
5082251881Speter                  if (dir_kind == svn_node_dir
5083251881Speter                      && dir_status == svn_wc__db_status_normal
5084251881Speter                      && dir_depth > svn_depth_empty)
5085251881Speter                    {
5086251881Speter                      apr_hash_t *dirents;
5087251881Speter
5088251881Speter                      /* If we switch, we should look at the new relpath */
5089289180Speter                      if (eb->switch_repos_relpath)
5090251881Speter                        dir_repos_relpath = svn_relpath_join(
5091289180Speter                                                eb->switch_repos_relpath,
5092251881Speter                                                child_name, iterpool);
5093251881Speter
5094251881Speter                      SVN_ERR(fetch_dirents_func(fetch_dirents_baton, &dirents,
5095251881Speter                                                 repos_root, dir_repos_relpath,
5096251881Speter                                                 edit_pool, iterpool));
5097251881Speter
5098251881Speter                      if (dirents != NULL && apr_hash_count(dirents))
5099251881Speter                        svn_hash_sets(eb->dir_dirents,
5100251881Speter                                      apr_pstrdup(edit_pool,
5101251881Speter                                                  dir_repos_relpath),
5102251881Speter                                      dirents);
5103251881Speter                    }
5104251881Speter                }
5105251881Speter            }
5106251881Speter        }
5107251881Speter      else if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
5108251881Speter        svn_error_clear(err);
5109251881Speter      else
5110251881Speter        SVN_ERR(err);
5111251881Speter    }
5112251881Speter
5113251881Speter  /* We need to limit the scope of our operation to the ambient depths
5114251881Speter     present in the working copy already, but only if the requested
5115251881Speter     depth is not sticky. If a depth was explicitly requested,
5116251881Speter     libsvn_delta/depth_filter_editor.c will ensure that we never see
5117251881Speter     editor calls that extend beyond the scope of the requested depth.
5118251881Speter     But even what we do so might extend beyond the scope of our
5119251881Speter     ambient depth.  So we use another filtering editor to avoid
5120251881Speter     modifying the ambient working copy depth when not asked to do so.
5121251881Speter     (This can also be skipped if the server understands depth.) */
5122251881Speter  if (!server_performs_filtering
5123251881Speter      && !depth_is_sticky)
5124251881Speter    SVN_ERR(svn_wc__ambient_depth_filter_editor(&inner_editor,
5125251881Speter                                                &inner_baton,
5126251881Speter                                                db,
5127251881Speter                                                anchor_abspath,
5128251881Speter                                                target_basename,
5129251881Speter                                                inner_editor,
5130251881Speter                                                inner_baton,
5131251881Speter                                                result_pool));
5132251881Speter
5133251881Speter  SVN_ERR(svn_delta_get_cancellation_editor(cancel_func,
5134251881Speter                                            cancel_baton,
5135251881Speter                                            inner_editor,
5136251881Speter                                            inner_baton,
5137251881Speter                                            editor,
5138251881Speter                                            edit_baton,
5139251881Speter                                            result_pool));
5140251881Speter
5141251881Speter  sfb = apr_palloc(result_pool, sizeof(*sfb));
5142251881Speter  sfb->db = db;
5143251881Speter  sfb->base_abspath = eb->anchor_abspath;
5144251881Speter  sfb->fetch_base = TRUE;
5145251881Speter
5146251881Speter  shim_callbacks->fetch_kind_func = svn_wc__fetch_kind_func;
5147251881Speter  shim_callbacks->fetch_props_func = svn_wc__fetch_props_func;
5148251881Speter  shim_callbacks->fetch_base_func = svn_wc__fetch_base_func;
5149251881Speter  shim_callbacks->fetch_baton = sfb;
5150251881Speter
5151251881Speter  SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton,
5152251881Speter                                   NULL, NULL, shim_callbacks,
5153251881Speter                                   result_pool, scratch_pool));
5154251881Speter
5155251881Speter  return SVN_NO_ERROR;
5156251881Speter}
5157251881Speter
5158251881Speter
5159251881Spetersvn_error_t *
5160251881Spetersvn_wc__get_update_editor(const svn_delta_editor_t **editor,
5161251881Speter                          void **edit_baton,
5162251881Speter                          svn_revnum_t *target_revision,
5163251881Speter                          svn_wc_context_t *wc_ctx,
5164251881Speter                          const char *anchor_abspath,
5165251881Speter                          const char *target_basename,
5166251881Speter                          apr_hash_t *wcroot_iprops,
5167251881Speter                          svn_boolean_t use_commit_times,
5168251881Speter                          svn_depth_t depth,
5169251881Speter                          svn_boolean_t depth_is_sticky,
5170251881Speter                          svn_boolean_t allow_unver_obstructions,
5171251881Speter                          svn_boolean_t adds_as_modification,
5172251881Speter                          svn_boolean_t server_performs_filtering,
5173251881Speter                          svn_boolean_t clean_checkout,
5174251881Speter                          const char *diff3_cmd,
5175251881Speter                          const apr_array_header_t *preserved_exts,
5176251881Speter                          svn_wc_dirents_func_t fetch_dirents_func,
5177251881Speter                          void *fetch_dirents_baton,
5178251881Speter                          svn_wc_conflict_resolver_func2_t conflict_func,
5179251881Speter                          void *conflict_baton,
5180251881Speter                          svn_wc_external_update_t external_func,
5181251881Speter                          void *external_baton,
5182251881Speter                          svn_cancel_func_t cancel_func,
5183251881Speter                          void *cancel_baton,
5184251881Speter                          svn_wc_notify_func2_t notify_func,
5185251881Speter                          void *notify_baton,
5186251881Speter                          apr_pool_t *result_pool,
5187251881Speter                          apr_pool_t *scratch_pool)
5188251881Speter{
5189251881Speter  return make_editor(target_revision, wc_ctx->db, anchor_abspath,
5190251881Speter                     target_basename, wcroot_iprops, use_commit_times,
5191251881Speter                     NULL, depth, depth_is_sticky, allow_unver_obstructions,
5192251881Speter                     adds_as_modification, server_performs_filtering,
5193251881Speter                     clean_checkout,
5194251881Speter                     notify_func, notify_baton,
5195251881Speter                     cancel_func, cancel_baton,
5196251881Speter                     fetch_dirents_func, fetch_dirents_baton,
5197251881Speter                     conflict_func, conflict_baton,
5198251881Speter                     external_func, external_baton,
5199251881Speter                     diff3_cmd, preserved_exts, editor, edit_baton,
5200251881Speter                     result_pool, scratch_pool);
5201251881Speter}
5202251881Speter
5203251881Spetersvn_error_t *
5204251881Spetersvn_wc__get_switch_editor(const svn_delta_editor_t **editor,
5205251881Speter                          void **edit_baton,
5206251881Speter                          svn_revnum_t *target_revision,
5207251881Speter                          svn_wc_context_t *wc_ctx,
5208251881Speter                          const char *anchor_abspath,
5209251881Speter                          const char *target_basename,
5210251881Speter                          const char *switch_url,
5211251881Speter                          apr_hash_t *wcroot_iprops,
5212251881Speter                          svn_boolean_t use_commit_times,
5213251881Speter                          svn_depth_t depth,
5214251881Speter                          svn_boolean_t depth_is_sticky,
5215251881Speter                          svn_boolean_t allow_unver_obstructions,
5216251881Speter                          svn_boolean_t server_performs_filtering,
5217251881Speter                          const char *diff3_cmd,
5218251881Speter                          const apr_array_header_t *preserved_exts,
5219251881Speter                          svn_wc_dirents_func_t fetch_dirents_func,
5220251881Speter                          void *fetch_dirents_baton,
5221251881Speter                          svn_wc_conflict_resolver_func2_t conflict_func,
5222251881Speter                          void *conflict_baton,
5223251881Speter                          svn_wc_external_update_t external_func,
5224251881Speter                          void *external_baton,
5225251881Speter                          svn_cancel_func_t cancel_func,
5226251881Speter                          void *cancel_baton,
5227251881Speter                          svn_wc_notify_func2_t notify_func,
5228251881Speter                          void *notify_baton,
5229251881Speter                          apr_pool_t *result_pool,
5230251881Speter                          apr_pool_t *scratch_pool)
5231251881Speter{
5232251881Speter  SVN_ERR_ASSERT(switch_url && svn_uri_is_canonical(switch_url, scratch_pool));
5233251881Speter
5234251881Speter  return make_editor(target_revision, wc_ctx->db, anchor_abspath,
5235251881Speter                     target_basename, wcroot_iprops, use_commit_times,
5236251881Speter                     switch_url,
5237251881Speter                     depth, depth_is_sticky, allow_unver_obstructions,
5238251881Speter                     FALSE /* adds_as_modification */,
5239251881Speter                     server_performs_filtering,
5240251881Speter                     FALSE /* clean_checkout */,
5241251881Speter                     notify_func, notify_baton,
5242251881Speter                     cancel_func, cancel_baton,
5243251881Speter                     fetch_dirents_func, fetch_dirents_baton,
5244251881Speter                     conflict_func, conflict_baton,
5245251881Speter                     external_func, external_baton,
5246251881Speter                     diff3_cmd, preserved_exts,
5247251881Speter                     editor, edit_baton,
5248251881Speter                     result_pool, scratch_pool);
5249251881Speter}
5250251881Speter
5251251881Speter
5252251881Speter
5253251881Speter/* ### Note that this function is completely different from the rest of the
5254251881Speter       update editor in what it updates. The update editor changes only BASE
5255251881Speter       and ACTUAL and this function just changes WORKING and ACTUAL.
5256251881Speter
5257251881Speter       In the entries world this function shared a lot of code with the
5258251881Speter       update editor but in the wonderful new WC-NG world it will probably
5259251881Speter       do more and more by itself and would be more logically grouped with
5260251881Speter       the add/copy functionality in adm_ops.c and copy.c. */
5261251881Spetersvn_error_t *
5262251881Spetersvn_wc_add_repos_file4(svn_wc_context_t *wc_ctx,
5263251881Speter                       const char *local_abspath,
5264251881Speter                       svn_stream_t *new_base_contents,
5265251881Speter                       svn_stream_t *new_contents,
5266251881Speter                       apr_hash_t *new_base_props,
5267251881Speter                       apr_hash_t *new_props,
5268251881Speter                       const char *copyfrom_url,
5269251881Speter                       svn_revnum_t copyfrom_rev,
5270251881Speter                       svn_cancel_func_t cancel_func,
5271251881Speter                       void *cancel_baton,
5272251881Speter                       apr_pool_t *scratch_pool)
5273251881Speter{
5274251881Speter  svn_wc__db_t *db = wc_ctx->db;
5275251881Speter  const char *dir_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
5276251881Speter  svn_wc__db_status_t status;
5277251881Speter  svn_node_kind_t kind;
5278251881Speter  const char *tmp_text_base_abspath;
5279251881Speter  svn_checksum_t *new_text_base_md5_checksum;
5280251881Speter  svn_checksum_t *new_text_base_sha1_checksum;
5281251881Speter  const char *source_abspath = NULL;
5282251881Speter  svn_skel_t *all_work_items = NULL;
5283251881Speter  svn_skel_t *work_item;
5284251881Speter  const char *repos_root_url;
5285251881Speter  const char *repos_uuid;
5286251881Speter  const char *original_repos_relpath;
5287251881Speter  svn_revnum_t changed_rev;
5288251881Speter  apr_time_t changed_date;
5289251881Speter  const char *changed_author;
5290289180Speter  svn_stream_t *tmp_base_contents;
5291289180Speter  svn_wc__db_install_data_t *install_data;
5292251881Speter  svn_error_t *err;
5293251881Speter  apr_pool_t *pool = scratch_pool;
5294251881Speter
5295251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5296251881Speter  SVN_ERR_ASSERT(new_base_contents != NULL);
5297251881Speter  SVN_ERR_ASSERT(new_base_props != NULL);
5298251881Speter
5299251881Speter  /* We should have a write lock on this file's parent directory.  */
5300251881Speter  SVN_ERR(svn_wc__write_check(db, dir_abspath, pool));
5301251881Speter
5302251881Speter  err = svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5303251881Speter                             NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5304251881Speter                             NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5305251881Speter                             NULL, NULL, NULL,
5306251881Speter                             db, local_abspath, scratch_pool, scratch_pool);
5307251881Speter
5308251881Speter  if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
5309251881Speter    return svn_error_trace(err);
5310251881Speter  else if(err)
5311251881Speter    svn_error_clear(err);
5312251881Speter  else
5313251881Speter    switch (status)
5314251881Speter      {
5315251881Speter        case svn_wc__db_status_not_present:
5316251881Speter        case svn_wc__db_status_deleted:
5317251881Speter          break;
5318251881Speter        default:
5319251881Speter          return svn_error_createf(SVN_ERR_ENTRY_EXISTS, NULL,
5320251881Speter                                   _("Node '%s' exists."),
5321251881Speter                                   svn_dirent_local_style(local_abspath,
5322251881Speter                                                          scratch_pool));
5323251881Speter      }
5324251881Speter
5325251881Speter  SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, &repos_root_url,
5326251881Speter                               &repos_uuid, NULL, NULL, NULL, NULL, NULL, NULL,
5327251881Speter                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5328251881Speter                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5329251881Speter                               db, dir_abspath, scratch_pool, scratch_pool));
5330251881Speter
5331251881Speter  switch (status)
5332251881Speter    {
5333251881Speter      case svn_wc__db_status_normal:
5334251881Speter      case svn_wc__db_status_added:
5335251881Speter        break;
5336251881Speter      case svn_wc__db_status_deleted:
5337251881Speter        return
5338251881Speter          svn_error_createf(SVN_ERR_WC_SCHEDULE_CONFLICT, NULL,
5339251881Speter                            _("Can't add '%s' to a parent directory"
5340251881Speter                              " scheduled for deletion"),
5341251881Speter                            svn_dirent_local_style(local_abspath,
5342251881Speter                                                   scratch_pool));
5343251881Speter      default:
5344251881Speter        return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, err,
5345251881Speter                                 _("Can't find parent directory's node while"
5346251881Speter                                   " trying to add '%s'"),
5347251881Speter                                 svn_dirent_local_style(local_abspath,
5348251881Speter                                                        scratch_pool));
5349251881Speter    }
5350251881Speter  if (kind != svn_node_dir)
5351251881Speter    return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
5352251881Speter                             _("Can't schedule an addition of '%s'"
5353251881Speter                               " below a not-directory node"),
5354251881Speter                             svn_dirent_local_style(local_abspath,
5355251881Speter                                                    scratch_pool));
5356251881Speter
5357251881Speter  /* Fabricate the anticipated new URL of the target and check the
5358251881Speter     copyfrom URL to be in the same repository. */
5359251881Speter  if (copyfrom_url != NULL)
5360251881Speter    {
5361251881Speter      /* Find the repository_root via the parent directory, which
5362251881Speter         is always versioned before this function is called */
5363251881Speter
5364251881Speter      if (!repos_root_url)
5365251881Speter        {
5366251881Speter          /* The parent is an addition, scan upwards to find the right info */
5367251881Speter          SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, NULL,
5368251881Speter                                           &repos_root_url, &repos_uuid,
5369251881Speter                                           NULL, NULL, NULL, NULL,
5370251881Speter                                           wc_ctx->db, dir_abspath,
5371251881Speter                                           scratch_pool, scratch_pool));
5372251881Speter        }
5373251881Speter      SVN_ERR_ASSERT(repos_root_url);
5374251881Speter
5375251881Speter      original_repos_relpath =
5376251881Speter          svn_uri_skip_ancestor(repos_root_url, copyfrom_url, scratch_pool);
5377251881Speter
5378251881Speter      if (!original_repos_relpath)
5379251881Speter        return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
5380251881Speter                                 _("Copyfrom-url '%s' has different repository"
5381251881Speter                                   " root than '%s'"),
5382251881Speter                                 copyfrom_url, repos_root_url);
5383251881Speter    }
5384251881Speter  else
5385251881Speter    {
5386251881Speter      original_repos_relpath = NULL;
5387251881Speter      copyfrom_rev = SVN_INVALID_REVNUM;  /* Just to be sure.  */
5388251881Speter    }
5389251881Speter
5390251881Speter  /* Set CHANGED_* to reflect the entry props in NEW_BASE_PROPS, and
5391251881Speter     filter NEW_BASE_PROPS so it contains only regular props. */
5392251881Speter  {
5393251881Speter    apr_array_header_t *regular_props;
5394251881Speter    apr_array_header_t *entry_props;
5395251881Speter
5396251881Speter    SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(new_base_props, pool),
5397251881Speter                                 &entry_props, NULL, &regular_props,
5398251881Speter                                 pool));
5399251881Speter
5400251881Speter    /* Put regular props back into a hash table. */
5401251881Speter    new_base_props = svn_prop_array_to_hash(regular_props, pool);
5402251881Speter
5403251881Speter    /* Get the change_* info from the entry props.  */
5404251881Speter    SVN_ERR(accumulate_last_change(&changed_rev,
5405251881Speter                                   &changed_date,
5406251881Speter                                   &changed_author,
5407251881Speter                                   entry_props, pool, pool));
5408251881Speter  }
5409251881Speter
5410251881Speter  /* Copy NEW_BASE_CONTENTS into a temporary file so our log can refer to
5411251881Speter     it, and set TMP_TEXT_BASE_ABSPATH to its path.  Compute its
5412251881Speter     NEW_TEXT_BASE_MD5_CHECKSUM and NEW_TEXT_BASE_SHA1_CHECKSUM as we copy. */
5413289180Speter  if (copyfrom_url)
5414289180Speter    {
5415289180Speter      SVN_ERR(svn_wc__db_pristine_prepare_install(&tmp_base_contents,
5416289180Speter                                                  &install_data,
5417289180Speter                                                  &new_text_base_sha1_checksum,
5418289180Speter                                                  &new_text_base_md5_checksum,
5419289180Speter                                                  wc_ctx->db, local_abspath,
5420289180Speter                                                  scratch_pool, scratch_pool));
5421289180Speter    }
5422289180Speter  else
5423289180Speter    {
5424289180Speter      const char *tmp_dir_abspath;
5425251881Speter
5426289180Speter      /* We are not installing a PRISTINE file, but we use the same code to
5427289180Speter         create whatever we want to install */
5428251881Speter
5429289180Speter      SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&tmp_dir_abspath,
5430289180Speter                                             db, dir_abspath,
5431289180Speter                                             scratch_pool, scratch_pool));
5432289180Speter
5433289180Speter      SVN_ERR(svn_stream_open_unique(&tmp_base_contents, &tmp_text_base_abspath,
5434289180Speter                                     tmp_dir_abspath, svn_io_file_del_none,
5435289180Speter                                     scratch_pool, scratch_pool));
5436289180Speter
5437289180Speter      new_text_base_sha1_checksum = NULL;
5438289180Speter      new_text_base_md5_checksum = NULL;
5439289180Speter    }
5440289180Speter  SVN_ERR(svn_stream_copy3(new_base_contents, tmp_base_contents,
5441289180Speter                           cancel_func, cancel_baton, pool));
5442289180Speter
5443251881Speter  /* If the caller gave us a new working file, copy it to a safe (temporary)
5444251881Speter     location and set SOURCE_ABSPATH to that path. We'll then translate/copy
5445251881Speter     that into place after the node's state has been created.  */
5446251881Speter  if (new_contents)
5447251881Speter    {
5448251881Speter      const char *temp_dir_abspath;
5449251881Speter      svn_stream_t *tmp_contents;
5450251881Speter
5451251881Speter      SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, db,
5452251881Speter                                             local_abspath, pool, pool));
5453251881Speter      SVN_ERR(svn_stream_open_unique(&tmp_contents, &source_abspath,
5454251881Speter                                     temp_dir_abspath, svn_io_file_del_none,
5455251881Speter                                     pool, pool));
5456251881Speter      SVN_ERR(svn_stream_copy3(new_contents, tmp_contents,
5457251881Speter                               cancel_func, cancel_baton, pool));
5458251881Speter    }
5459251881Speter
5460251881Speter  /* Install new text base for copied files. Added files do NOT have a
5461251881Speter     text base.  */
5462251881Speter  if (copyfrom_url != NULL)
5463251881Speter    {
5464289180Speter      SVN_ERR(svn_wc__db_pristine_install(install_data,
5465251881Speter                                          new_text_base_sha1_checksum,
5466251881Speter                                          new_text_base_md5_checksum, pool));
5467251881Speter    }
5468251881Speter  else
5469251881Speter    {
5470251881Speter      /* ### There's something wrong around here.  Sometimes (merge from a
5471251881Speter         foreign repository, at least) we are called with copyfrom_url =
5472251881Speter         NULL and an empty new_base_contents (and an empty set of
5473251881Speter         new_base_props).  Why an empty "new base"?
5474251881Speter
5475251881Speter         That happens in merge_tests.py 54,87,88,89,143.
5476251881Speter
5477251881Speter         In that case, having been given this supposed "new base" file, we
5478251881Speter         copy it and calculate its checksum but do not install it.  Why?
5479251881Speter         That must be wrong.
5480251881Speter
5481251881Speter         To crudely work around one issue with this, that we shouldn't
5482251881Speter         record a checksum in the database if we haven't installed the
5483251881Speter         corresponding pristine text, for now we'll just set the checksum
5484251881Speter         to NULL.
5485251881Speter
5486251881Speter         The proper solution is probably more like: the caller should pass
5487251881Speter         NULL for the missing information, and this function should learn to
5488251881Speter         handle that. */
5489251881Speter
5490251881Speter      new_text_base_sha1_checksum = NULL;
5491251881Speter      new_text_base_md5_checksum = NULL;
5492251881Speter    }
5493251881Speter
5494251881Speter  /* For added files without NEW_CONTENTS, then generate the working file
5495251881Speter     from the provided "pristine" contents.  */
5496251881Speter  if (new_contents == NULL && copyfrom_url == NULL)
5497251881Speter    source_abspath = tmp_text_base_abspath;
5498251881Speter
5499251881Speter  {
5500251881Speter    svn_boolean_t record_fileinfo;
5501251881Speter
5502251881Speter    /* If new contents were provided, then we do NOT want to record the
5503251881Speter       file information. We assume the new contents do not match the
5504251881Speter       "proper" values for RECORDED_SIZE and RECORDED_TIME.  */
5505251881Speter    record_fileinfo = (new_contents == NULL);
5506251881Speter
5507251881Speter    /* Install the working copy file (with appropriate translation) from
5508251881Speter       the appropriate source. SOURCE_ABSPATH will be NULL, indicating an
5509251881Speter       installation from the pristine (available for copied/moved files),
5510251881Speter       or it will specify a temporary file where we placed a "pristine"
5511251881Speter       (for an added file) or a detranslated local-mods file.  */
5512251881Speter    SVN_ERR(svn_wc__wq_build_file_install(&work_item,
5513251881Speter                                          db, local_abspath,
5514251881Speter                                          source_abspath,
5515251881Speter                                          FALSE /* use_commit_times */,
5516251881Speter                                          record_fileinfo,
5517251881Speter                                          pool, pool));
5518251881Speter    all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool);
5519251881Speter
5520251881Speter    /* If we installed from somewhere besides the official pristine, then
5521251881Speter       it is a temporary file, which needs to be removed.  */
5522251881Speter    if (source_abspath != NULL)
5523251881Speter      {
5524251881Speter        SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, local_abspath,
5525251881Speter                                             source_abspath,
5526251881Speter                                             pool, pool));
5527251881Speter        all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool);
5528251881Speter      }
5529251881Speter  }
5530251881Speter
5531251881Speter  SVN_ERR(svn_wc__db_op_copy_file(db, local_abspath,
5532251881Speter                                  new_base_props,
5533251881Speter                                  changed_rev,
5534251881Speter                                  changed_date,
5535251881Speter                                  changed_author,
5536251881Speter                                  original_repos_relpath,
5537251881Speter                                  original_repos_relpath ? repos_root_url
5538251881Speter                                                         : NULL,
5539251881Speter                                  original_repos_relpath ? repos_uuid : NULL,
5540251881Speter                                  copyfrom_rev,
5541251881Speter                                  new_text_base_sha1_checksum,
5542251881Speter                                  TRUE,
5543251881Speter                                  new_props,
5544251881Speter                                  FALSE /* is_move */,
5545251881Speter                                  NULL /* conflict */,
5546251881Speter                                  all_work_items,
5547251881Speter                                  pool));
5548251881Speter
5549251881Speter  return svn_error_trace(svn_wc__wq_run(db, dir_abspath,
5550251881Speter                                        cancel_func, cancel_baton,
5551251881Speter                                        pool));
5552251881Speter}
5553251881Speter
5554251881Spetersvn_error_t *
5555251881Spetersvn_wc__complete_directory_add(svn_wc_context_t *wc_ctx,
5556251881Speter                               const char *local_abspath,
5557251881Speter                               apr_hash_t *new_original_props,
5558251881Speter                               const char *copyfrom_url,
5559251881Speter                               svn_revnum_t copyfrom_rev,
5560251881Speter                               apr_pool_t *scratch_pool)
5561251881Speter{
5562251881Speter  svn_wc__db_status_t status;
5563251881Speter  svn_node_kind_t kind;
5564251881Speter  const char *original_repos_relpath;
5565251881Speter  const char *original_root_url;
5566251881Speter  const char *original_uuid;
5567251881Speter  svn_boolean_t had_props;
5568251881Speter  svn_boolean_t props_mod;
5569251881Speter
5570251881Speter  svn_revnum_t original_revision;
5571251881Speter  svn_revnum_t changed_rev;
5572251881Speter  apr_time_t changed_date;
5573251881Speter  const char *changed_author;
5574251881Speter
5575251881Speter  SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
5576251881Speter                               NULL, NULL, NULL, NULL, NULL,
5577251881Speter                               &original_repos_relpath, &original_root_url,
5578251881Speter                               &original_uuid, &original_revision, NULL, NULL,
5579251881Speter                               NULL, NULL, NULL, NULL, &had_props, &props_mod,
5580251881Speter                               NULL, NULL, NULL,
5581251881Speter                               wc_ctx->db, local_abspath,
5582251881Speter                               scratch_pool, scratch_pool));
5583251881Speter
5584251881Speter  if (status != svn_wc__db_status_added
5585251881Speter      || kind != svn_node_dir
5586251881Speter      || had_props
5587251881Speter      || props_mod
5588251881Speter      || !original_repos_relpath)
5589251881Speter    {
5590251881Speter      return svn_error_createf(
5591251881Speter                    SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
5592251881Speter                    _("'%s' is not an unmodified copied directory"),
5593251881Speter                    svn_dirent_local_style(local_abspath, scratch_pool));
5594251881Speter    }
5595251881Speter  if (original_revision != copyfrom_rev
5596251881Speter      || strcmp(copyfrom_url,
5597251881Speter                 svn_path_url_add_component2(original_root_url,
5598251881Speter                                             original_repos_relpath,
5599251881Speter                                             scratch_pool)))
5600251881Speter    {
5601251881Speter      return svn_error_createf(
5602251881Speter                    SVN_ERR_WC_COPYFROM_PATH_NOT_FOUND, NULL,
5603251881Speter                    _("Copyfrom '%s' doesn't match original location of '%s'"),
5604251881Speter                    copyfrom_url,
5605251881Speter                    svn_dirent_local_style(local_abspath, scratch_pool));
5606251881Speter    }
5607251881Speter
5608251881Speter  {
5609251881Speter    apr_array_header_t *regular_props;
5610251881Speter    apr_array_header_t *entry_props;
5611251881Speter
5612251881Speter    SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(new_original_props,
5613251881Speter                                                        scratch_pool),
5614251881Speter                                 &entry_props, NULL, &regular_props,
5615251881Speter                                 scratch_pool));
5616251881Speter
5617251881Speter    /* Put regular props back into a hash table. */
5618251881Speter    new_original_props = svn_prop_array_to_hash(regular_props, scratch_pool);
5619251881Speter
5620251881Speter    /* Get the change_* info from the entry props.  */
5621251881Speter    SVN_ERR(accumulate_last_change(&changed_rev,
5622251881Speter                                   &changed_date,
5623251881Speter                                   &changed_author,
5624251881Speter                                   entry_props, scratch_pool, scratch_pool));
5625251881Speter  }
5626251881Speter
5627251881Speter  return svn_error_trace(
5628251881Speter            svn_wc__db_op_copy_dir(wc_ctx->db, local_abspath,
5629251881Speter                                   new_original_props,
5630251881Speter                                   changed_rev, changed_date, changed_author,
5631251881Speter                                   original_repos_relpath, original_root_url,
5632251881Speter                                   original_uuid, original_revision,
5633251881Speter                                   NULL /* children */,
5634286506Speter                                   svn_depth_infinity,
5635251881Speter                                   FALSE /* is_move */,
5636251881Speter                                   NULL /* conflict */,
5637251881Speter                                   NULL /* work_items */,
5638251881Speter                                   scratch_pool));
5639251881Speter}
5640