update_editor.c revision 309511
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    {
1238251881Speter      const char *move_src_root_abspath;
1239251881Speter
1240251881Speter      SVN_ERR(svn_wc__db_base_moved_to(NULL, NULL, &move_src_root_abspath,
1241251881Speter                                       NULL, eb->db, db->local_abspath,
1242251881Speter                                       pool, pool));
1243251881Speter
1244251881Speter      if (move_src_root_abspath)
1245251881Speter        {
1246251881Speter          /* This is an update anchored inside a move. We need to
1247251881Speter             raise a move-edit tree-conflict on the move root to
1248251881Speter             update the move destination. */
1249251881Speter          svn_skel_t *tree_conflict = svn_wc__conflict_skel_create(pool);
1250251881Speter
1251251881Speter          SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
1252251881Speter                    tree_conflict, eb->db, move_src_root_abspath,
1253251881Speter                    svn_wc_conflict_reason_moved_away,
1254251881Speter                    svn_wc_conflict_action_edit,
1255251881Speter                    move_src_root_abspath, pool, pool));
1256251881Speter
1257251881Speter          if (strcmp(db->local_abspath, move_src_root_abspath))
1258251881Speter            {
1259251881Speter              /* We are raising the tree-conflict on some parent of
1260251881Speter                 the edit root, we won't be handling that path again
1261251881Speter                 so raise the conflict now. */
1262251881Speter              SVN_ERR(complete_conflict(tree_conflict, eb,
1263251881Speter                                        move_src_root_abspath,
1264251881Speter                                        db->old_repos_relpath,
1265289180Speter                                        db->old_revision,
1266289180Speter                                        db->new_repos_relpath,
1267251881Speter                                        svn_node_dir, svn_node_dir,
1268289180Speter                                        NULL, pool, pool));
1269251881Speter              SVN_ERR(svn_wc__db_op_mark_conflict(eb->db,
1270251881Speter                                                  move_src_root_abspath,
1271251881Speter                                                  tree_conflict,
1272251881Speter                                                  NULL, pool));
1273251881Speter              do_notification(eb, move_src_root_abspath, svn_node_dir,
1274251881Speter                              svn_wc_notify_tree_conflict, pool);
1275251881Speter            }
1276251881Speter          else
1277251881Speter            db->edit_conflict = tree_conflict;
1278251881Speter        }
1279251881Speter
1280251881Speter      db->shadowed = TRUE; /* Needed for the close_directory() on the root, to
1281251881Speter                              make sure it doesn't use the ACTUAL tree */
1282251881Speter    }
1283251881Speter
1284251881Speter  if (*eb->target_basename == '\0')
1285251881Speter    {
1286251881Speter      /* For an update with a NULL target, this is equivalent to open_dir(): */
1287251881Speter
1288251881Speter      db->was_incomplete = (base_status == svn_wc__db_status_incomplete);
1289251881Speter
1290251881Speter      /* ### TODO: Add some tree conflict and obstruction detection, etc. like
1291251881Speter                   open_directory() does.
1292251881Speter                   (or find a way to reuse that code here)
1293251881Speter
1294251881Speter         ### BH 2013: I don't think we need all of the detection here, as the
1295251881Speter                      user explicitly asked to update this node. So we don't
1296251881Speter                      have to tell that it is a local replacement/delete.
1297251881Speter       */
1298251881Speter
1299251881Speter      SVN_ERR(svn_wc__db_temp_op_start_directory_update(eb->db,
1300251881Speter                                                        db->local_abspath,
1301289180Speter                                                        db->new_repos_relpath,
1302251881Speter                                                        *eb->target_revision,
1303251881Speter                                                        pool));
1304251881Speter    }
1305251881Speter
1306251881Speter  return SVN_NO_ERROR;
1307251881Speter}
1308251881Speter
1309251881Speter
1310251881Speter/* ===================================================================== */
1311251881Speter/* Checking for local modifications. */
1312251881Speter
1313251881Speter/* Indicates an unset svn_wc_conflict_reason_t. */
1314251881Speter#define SVN_WC_CONFLICT_REASON_NONE (svn_wc_conflict_reason_t)(-1)
1315251881Speter
1316251881Speter/* Check whether the incoming change ACTION on FULL_PATH would conflict with
1317251881Speter * LOCAL_ABSPATH's scheduled change. If so, then raise a tree conflict with
1318251881Speter * LOCAL_ABSPATH as the victim.
1319251881Speter *
1320251881Speter * The edit baton EB gives information including whether the operation is
1321251881Speter * an update or a switch.
1322251881Speter *
1323251881Speter * WORKING_STATUS is the current node status of LOCAL_ABSPATH
1324251881Speter * and EXISTS_IN_REPOS specifies whether a BASE_NODE representation for exists
1325251881Speter * for this node. In that case the on disk type is compared to EXPECTED_KIND.
1326251881Speter *
1327251881Speter * If a tree conflict reason was found for the incoming action, the resulting
1328251881Speter * tree conflict info is returned in *PCONFLICT. PCONFLICT must be non-NULL,
1329251881Speter * while *PCONFLICT is always overwritten.
1330251881Speter *
1331251881Speter * The tree conflict is allocated in RESULT_POOL. Temporary allocations use
1332251881Speter * SCRATCH_POOL.
1333251881Speter */
1334251881Speterstatic svn_error_t *
1335251881Spetercheck_tree_conflict(svn_skel_t **pconflict,
1336251881Speter                    struct edit_baton *eb,
1337251881Speter                    const char *local_abspath,
1338251881Speter                    svn_wc__db_status_t working_status,
1339251881Speter                    svn_boolean_t exists_in_repos,
1340251881Speter                    svn_node_kind_t expected_kind,
1341251881Speter                    svn_wc_conflict_action_t action,
1342251881Speter                    apr_pool_t *result_pool,
1343251881Speter                    apr_pool_t *scratch_pool)
1344251881Speter{
1345251881Speter  svn_wc_conflict_reason_t reason = SVN_WC_CONFLICT_REASON_NONE;
1346251881Speter  svn_boolean_t modified = FALSE;
1347251881Speter  const char *move_src_op_root_abspath = NULL;
1348251881Speter
1349251881Speter  *pconflict = NULL;
1350251881Speter
1351251881Speter  /* Find out if there are any local changes to this node that may
1352251881Speter   * be the "reason" of a tree-conflict with the incoming "action". */
1353251881Speter  switch (working_status)
1354251881Speter    {
1355251881Speter      case svn_wc__db_status_added:
1356251881Speter      case svn_wc__db_status_moved_here:
1357251881Speter      case svn_wc__db_status_copied:
1358251881Speter        if (!exists_in_repos)
1359251881Speter          {
1360251881Speter            /* The node is locally added, and it did not exist before.  This
1361251881Speter             * is an 'update', so the local add can only conflict with an
1362251881Speter             * incoming 'add'.  In fact, if we receive anything else than an
1363251881Speter             * svn_wc_conflict_action_add (which includes 'added',
1364251881Speter             * 'copied-here' and 'moved-here') during update on a node that
1365251881Speter             * did not exist before, then something is very wrong.
1366251881Speter             * Note that if there was no action on the node, this code
1367251881Speter             * would not have been called in the first place. */
1368251881Speter            SVN_ERR_ASSERT(action == svn_wc_conflict_action_add);
1369251881Speter
1370251881Speter            /* Scan the addition in case our caller didn't. */
1371251881Speter            if (working_status == svn_wc__db_status_added)
1372251881Speter              SVN_ERR(svn_wc__db_scan_addition(&working_status, NULL, NULL,
1373251881Speter                                               NULL, NULL, NULL, NULL,
1374251881Speter                                               NULL, NULL,
1375251881Speter                                               eb->db, local_abspath,
1376251881Speter                                               scratch_pool, scratch_pool));
1377251881Speter
1378251881Speter            if (working_status == svn_wc__db_status_moved_here)
1379251881Speter              reason = svn_wc_conflict_reason_moved_here;
1380251881Speter            else
1381251881Speter              reason = svn_wc_conflict_reason_added;
1382251881Speter          }
1383251881Speter        else
1384251881Speter          {
1385289180Speter            /* The node is locally replaced but could also be moved-away,
1386289180Speter               but we can't report that it is moved away and replaced.
1387289180Speter
1388289180Speter               And we wouldn't be able to store that each of a dozen
1389289180Speter               descendants was moved to other locations...
1390289180Speter
1391289180Speter               Replaced is what actually happened... */
1392289180Speter
1393289180Speter            reason = svn_wc_conflict_reason_replaced;
1394251881Speter          }
1395251881Speter        break;
1396251881Speter
1397251881Speter
1398251881Speter      case svn_wc__db_status_deleted:
1399251881Speter        {
1400251881Speter          SVN_ERR(svn_wc__db_base_moved_to(NULL, NULL, NULL,
1401251881Speter                                           &move_src_op_root_abspath,
1402251881Speter                                           eb->db, local_abspath,
1403251881Speter                                           scratch_pool, scratch_pool));
1404251881Speter          if (move_src_op_root_abspath)
1405251881Speter            reason = svn_wc_conflict_reason_moved_away;
1406251881Speter          else
1407251881Speter            reason = svn_wc_conflict_reason_deleted;
1408251881Speter        }
1409251881Speter        break;
1410251881Speter
1411251881Speter      case svn_wc__db_status_incomplete:
1412251881Speter        /* We used svn_wc__db_read_info(), so 'incomplete' means
1413251881Speter         * - there is no node in the WORKING tree
1414251881Speter         * - a BASE node is known to exist
1415251881Speter         * So the node exists and is essentially 'normal'. We still need to
1416251881Speter         * check prop and text mods, and those checks will retrieve the
1417251881Speter         * missing information (hopefully). */
1418251881Speter      case svn_wc__db_status_normal:
1419251881Speter        if (action == svn_wc_conflict_action_edit)
1420251881Speter          {
1421251881Speter            /* An edit onto a local edit or onto *no* local changes is no
1422251881Speter             * tree-conflict. (It's possibly a text- or prop-conflict,
1423251881Speter             * but we don't handle those here.)
1424251881Speter             *
1425251881Speter             * Except when there is a local obstruction
1426251881Speter             */
1427251881Speter            if (exists_in_repos)
1428251881Speter              {
1429251881Speter                svn_node_kind_t disk_kind;
1430251881Speter
1431251881Speter                SVN_ERR(svn_io_check_path(local_abspath, &disk_kind,
1432251881Speter                                          scratch_pool));
1433251881Speter
1434251881Speter                if (disk_kind != expected_kind && disk_kind != svn_node_none)
1435251881Speter                  {
1436251881Speter                    reason = svn_wc_conflict_reason_obstructed;
1437251881Speter                    break;
1438251881Speter                  }
1439251881Speter
1440251881Speter              }
1441251881Speter            return SVN_NO_ERROR;
1442251881Speter          }
1443251881Speter
1444251881Speter        /* Replace is handled as delete and then specifically in
1445251881Speter           add_directory() and add_file(), so we only expect deletes here */
1446251881Speter        SVN_ERR_ASSERT(action == svn_wc_conflict_action_delete);
1447251881Speter
1448251881Speter        /* Check if the update wants to delete or replace a locally
1449251881Speter         * modified node. */
1450251881Speter
1451251881Speter
1452251881Speter        /* Do a deep tree detection of local changes. The update editor will
1453251881Speter         * not visit the subdirectories of a directory that it wants to delete.
1454251881Speter         * Therefore, we need to start a separate crawl here. */
1455251881Speter
1456289180Speter        SVN_ERR(svn_wc__node_has_local_mods(&modified, NULL,
1457289180Speter                                            eb->db, local_abspath, FALSE,
1458251881Speter                                            eb->cancel_func, eb->cancel_baton,
1459251881Speter                                            scratch_pool));
1460251881Speter
1461251881Speter        if (modified)
1462251881Speter          {
1463289180Speter            if (working_status == svn_wc__db_status_deleted)
1464251881Speter              reason = svn_wc_conflict_reason_deleted;
1465251881Speter            else
1466251881Speter              reason = svn_wc_conflict_reason_edited;
1467251881Speter          }
1468251881Speter        break;
1469251881Speter
1470251881Speter      case svn_wc__db_status_server_excluded:
1471251881Speter        /* Not allowed to view the node. Not allowed to report tree
1472251881Speter         * conflicts. */
1473251881Speter      case svn_wc__db_status_excluded:
1474251881Speter        /* Locally marked as excluded. No conflicts wanted. */
1475251881Speter      case svn_wc__db_status_not_present:
1476251881Speter        /* A committed delete (but parent not updated). The delete is
1477251881Speter           committed, so no conflict possible during update. */
1478251881Speter        return SVN_NO_ERROR;
1479251881Speter
1480251881Speter      case svn_wc__db_status_base_deleted:
1481251881Speter        /* An internal status. Should never show up here. */
1482251881Speter        SVN_ERR_MALFUNCTION();
1483251881Speter        break;
1484251881Speter
1485251881Speter    }
1486251881Speter
1487251881Speter  if (reason == SVN_WC_CONFLICT_REASON_NONE)
1488251881Speter    /* No conflict with the current action. */
1489251881Speter    return SVN_NO_ERROR;
1490251881Speter
1491251881Speter
1492251881Speter  /* Sanity checks. Note that if there was no action on the node, this function
1493251881Speter   * would not have been called in the first place.*/
1494251881Speter  if (reason == svn_wc_conflict_reason_edited
1495251881Speter      || reason == svn_wc_conflict_reason_obstructed
1496251881Speter      || reason == svn_wc_conflict_reason_deleted
1497251881Speter      || reason == svn_wc_conflict_reason_moved_away
1498251881Speter      || reason == svn_wc_conflict_reason_replaced)
1499251881Speter    {
1500251881Speter      /* When the node existed before (it was locally deleted, replaced or
1501251881Speter       * edited), then 'update' cannot add it "again". So it can only send
1502251881Speter       * _action_edit, _delete or _replace. */
1503251881Speter    if (action != svn_wc_conflict_action_edit
1504251881Speter        && action != svn_wc_conflict_action_delete
1505251881Speter        && action != svn_wc_conflict_action_replace)
1506251881Speter      return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
1507251881Speter               _("Unexpected attempt to add a node at path '%s'"),
1508251881Speter               svn_dirent_local_style(local_abspath, scratch_pool));
1509251881Speter    }
1510251881Speter  else if (reason == svn_wc_conflict_reason_added ||
1511251881Speter           reason == svn_wc_conflict_reason_moved_here)
1512251881Speter    {
1513251881Speter      /* When the node did not exist before (it was locally added),
1514251881Speter       * then 'update' cannot want to modify it in any way.
1515251881Speter       * It can only send _action_add. */
1516251881Speter      if (action != svn_wc_conflict_action_add)
1517251881Speter        return svn_error_createf(SVN_ERR_WC_FOUND_CONFLICT, NULL,
1518251881Speter                 _("Unexpected attempt to edit, delete, or replace "
1519251881Speter                   "a node at path '%s'"),
1520251881Speter                 svn_dirent_local_style(local_abspath, scratch_pool));
1521251881Speter
1522251881Speter    }
1523251881Speter
1524251881Speter
1525251881Speter  /* A conflict was detected. Create a conflict skel to record it. */
1526251881Speter  *pconflict = svn_wc__conflict_skel_create(result_pool);
1527251881Speter
1528251881Speter  SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(*pconflict,
1529251881Speter                                                  eb->db, local_abspath,
1530251881Speter                                                  reason,
1531251881Speter                                                  action,
1532251881Speter                                                  move_src_op_root_abspath,
1533251881Speter                                                  result_pool, scratch_pool));
1534251881Speter
1535251881Speter  return SVN_NO_ERROR;
1536251881Speter}
1537251881Speter
1538251881Speter
1539251881Speter/* If LOCAL_ABSPATH is inside a conflicted tree and the conflict is
1540251881Speter * not a moved-away-edit conflict, set *CONFLICTED to TRUE.  Otherwise
1541251881Speter * set *CONFLICTED to FALSE.
1542251881Speter */
1543251881Speterstatic svn_error_t *
1544251881Speteralready_in_a_tree_conflict(svn_boolean_t *conflicted,
1545251881Speter                           svn_boolean_t *ignored,
1546251881Speter                           svn_wc__db_t *db,
1547251881Speter                           const char *local_abspath,
1548251881Speter                           apr_pool_t *scratch_pool)
1549251881Speter{
1550251881Speter  const char *ancestor_abspath = local_abspath;
1551251881Speter  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1552251881Speter
1553251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1554251881Speter
1555251881Speter  *conflicted = *ignored = FALSE;
1556251881Speter
1557251881Speter  while (TRUE)
1558251881Speter    {
1559251881Speter      svn_boolean_t is_wc_root;
1560251881Speter
1561251881Speter      svn_pool_clear(iterpool);
1562251881Speter
1563251881Speter      SVN_ERR(svn_wc__conflicted_for_update_p(conflicted, ignored, db,
1564251881Speter                                              ancestor_abspath, TRUE,
1565251881Speter                                              scratch_pool));
1566251881Speter      if (*conflicted || *ignored)
1567251881Speter        break;
1568251881Speter
1569251881Speter      SVN_ERR(svn_wc__db_is_wcroot(&is_wc_root, db, ancestor_abspath,
1570251881Speter                                   iterpool));
1571251881Speter      if (is_wc_root)
1572251881Speter        break;
1573251881Speter
1574251881Speter      ancestor_abspath = svn_dirent_dirname(ancestor_abspath, scratch_pool);
1575251881Speter    }
1576251881Speter
1577251881Speter  svn_pool_destroy(iterpool);
1578251881Speter
1579251881Speter  return SVN_NO_ERROR;
1580251881Speter}
1581251881Speter
1582251881Speter/* Temporary helper until the new conflict handling is in place */
1583251881Speterstatic svn_error_t *
1584251881Speternode_already_conflicted(svn_boolean_t *conflicted,
1585251881Speter                        svn_boolean_t *conflict_ignored,
1586251881Speter                        svn_wc__db_t *db,
1587251881Speter                        const char *local_abspath,
1588251881Speter                        apr_pool_t *scratch_pool)
1589251881Speter{
1590251881Speter  SVN_ERR(svn_wc__conflicted_for_update_p(conflicted, conflict_ignored, db,
1591251881Speter                                          local_abspath, FALSE,
1592251881Speter                                          scratch_pool));
1593251881Speter
1594251881Speter  return SVN_NO_ERROR;
1595251881Speter}
1596251881Speter
1597251881Speter
1598251881Speter/* An svn_delta_editor_t function. */
1599251881Speterstatic svn_error_t *
1600251881Speterdelete_entry(const char *path,
1601251881Speter             svn_revnum_t revision,
1602251881Speter             void *parent_baton,
1603251881Speter             apr_pool_t *pool)
1604251881Speter{
1605251881Speter  struct dir_baton *pb = parent_baton;
1606251881Speter  struct edit_baton *eb = pb->edit_baton;
1607251881Speter  const char *base = svn_relpath_basename(path, NULL);
1608251881Speter  const char *local_abspath;
1609251881Speter  const char *repos_relpath;
1610289180Speter  const char *deleted_repos_relpath;
1611289180Speter  svn_node_kind_t kind;
1612251881Speter  svn_revnum_t old_revision;
1613251881Speter  svn_boolean_t conflicted;
1614251881Speter  svn_boolean_t have_work;
1615251881Speter  svn_skel_t *tree_conflict = NULL;
1616251881Speter  svn_wc__db_status_t status;
1617251881Speter  svn_wc__db_status_t base_status;
1618251881Speter  apr_pool_t *scratch_pool;
1619251881Speter  svn_boolean_t deleting_target;
1620251881Speter  svn_boolean_t deleting_switched;
1621251881Speter
1622251881Speter  if (pb->skip_this)
1623251881Speter    return SVN_NO_ERROR;
1624251881Speter
1625251881Speter  scratch_pool = svn_pool_create(pb->pool);
1626251881Speter
1627251881Speter  SVN_ERR(mark_directory_edited(pb, scratch_pool));
1628251881Speter
1629251881Speter  SVN_ERR(path_join_under_root(&local_abspath, pb->local_abspath, base,
1630251881Speter                               scratch_pool));
1631251881Speter
1632251881Speter  deleting_target =  (strcmp(local_abspath, eb->target_abspath) == 0);
1633251881Speter
1634251881Speter  /* Detect obstructing working copies */
1635251881Speter  {
1636251881Speter    svn_boolean_t is_root;
1637251881Speter
1638289180Speter
1639251881Speter    SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, local_abspath,
1640251881Speter                                 scratch_pool));
1641251881Speter
1642251881Speter    if (is_root)
1643251881Speter      {
1644251881Speter        /* Just skip this node; a future update will handle it */
1645251881Speter        SVN_ERR(remember_skipped_tree(eb, local_abspath, pool));
1646251881Speter        do_notification(eb, local_abspath, svn_node_unknown,
1647251881Speter                        svn_wc_notify_update_skip_obstruction, scratch_pool);
1648251881Speter
1649251881Speter        svn_pool_destroy(scratch_pool);
1650251881Speter
1651251881Speter        return SVN_NO_ERROR;
1652251881Speter      }
1653251881Speter  }
1654251881Speter
1655251881Speter  SVN_ERR(svn_wc__db_read_info(&status, &kind, &old_revision, &repos_relpath,
1656251881Speter                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1657251881Speter                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1658251881Speter                               &conflicted, NULL, NULL, NULL,
1659251881Speter                               NULL, NULL, &have_work,
1660251881Speter                               eb->db, local_abspath,
1661251881Speter                               scratch_pool, scratch_pool));
1662251881Speter
1663251881Speter  if (!have_work)
1664251881Speter    {
1665251881Speter      base_status = status;
1666251881Speter    }
1667251881Speter  else
1668289180Speter    SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &old_revision,
1669251881Speter                                     &repos_relpath,
1670251881Speter                                     NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1671251881Speter                                     NULL, NULL, NULL, NULL, NULL,
1672251881Speter                                     eb->db, local_abspath,
1673251881Speter                                     scratch_pool, scratch_pool));
1674251881Speter
1675251881Speter  if (pb->old_repos_relpath && repos_relpath)
1676251881Speter    {
1677251881Speter      const char *expected_name;
1678251881Speter
1679251881Speter      expected_name = svn_relpath_skip_ancestor(pb->old_repos_relpath,
1680251881Speter                                                repos_relpath);
1681251881Speter
1682251881Speter      deleting_switched = (!expected_name || strcmp(expected_name, base) != 0);
1683251881Speter    }
1684251881Speter  else
1685251881Speter    deleting_switched = FALSE;
1686251881Speter
1687251881Speter  /* Is this path a conflict victim? */
1688251881Speter  if (pb->shadowed)
1689251881Speter    conflicted = FALSE; /* Conflict applies to WORKING */
1690251881Speter  else if (conflicted)
1691251881Speter    SVN_ERR(node_already_conflicted(&conflicted, NULL,
1692251881Speter                                    eb->db, local_abspath, scratch_pool));
1693251881Speter  if (conflicted)
1694251881Speter    {
1695251881Speter      SVN_ERR(remember_skipped_tree(eb, local_abspath, scratch_pool));
1696251881Speter
1697251881Speter      do_notification(eb, local_abspath, svn_node_unknown,
1698251881Speter                      svn_wc_notify_skip_conflicted,
1699251881Speter                      scratch_pool);
1700251881Speter
1701251881Speter      svn_pool_destroy(scratch_pool);
1702251881Speter
1703251881Speter      return SVN_NO_ERROR;
1704251881Speter    }
1705251881Speter
1706251881Speter
1707251881Speter  /* Receive the remote removal of excluded/server-excluded/not present node.
1708251881Speter     Do not notify, but perform the change even when the node is shadowed */
1709251881Speter  if (base_status == svn_wc__db_status_not_present
1710251881Speter      || base_status == svn_wc__db_status_excluded
1711251881Speter      || base_status == svn_wc__db_status_server_excluded)
1712251881Speter    {
1713289180Speter      SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath, TRUE,
1714289180Speter                                     deleting_target, FALSE,
1715289180Speter                                     *eb->target_revision,
1716251881Speter                                     NULL, NULL,
1717251881Speter                                     scratch_pool));
1718251881Speter
1719251881Speter      if (deleting_target)
1720251881Speter        eb->target_deleted = TRUE;
1721251881Speter
1722251881Speter      svn_pool_destroy(scratch_pool);
1723251881Speter
1724251881Speter      return SVN_NO_ERROR;
1725251881Speter    }
1726251881Speter
1727251881Speter  /* Is this path the victim of a newly-discovered tree conflict?  If so,
1728251881Speter   * remember it and notify the client. Then (if it was existing and
1729251881Speter   * modified), re-schedule the node to be added back again, as a (modified)
1730251881Speter   * copy of the previous base version.  */
1731251881Speter
1732251881Speter  /* Check for conflicts only when we haven't already recorded
1733251881Speter   * a tree-conflict on a parent node. */
1734251881Speter  if (!pb->shadowed && !pb->edit_obstructed)
1735251881Speter    {
1736251881Speter      SVN_ERR(check_tree_conflict(&tree_conflict, eb, local_abspath,
1737251881Speter                                  status, TRUE,
1738289180Speter                                  kind,
1739251881Speter                                  svn_wc_conflict_action_delete,
1740251881Speter                                  pb->pool, scratch_pool));
1741251881Speter    }
1742251881Speter
1743251881Speter  if (tree_conflict != NULL)
1744251881Speter    {
1745251881Speter      /* When we raise a tree conflict on a node, we don't want to mark the
1746251881Speter       * node as skipped, to allow a replacement to continue doing at least
1747251881Speter       * a bit of its work (possibly adding a not present node, for the
1748251881Speter       * next update) */
1749251881Speter      if (!pb->deletion_conflicts)
1750251881Speter        pb->deletion_conflicts = apr_hash_make(pb->pool);
1751251881Speter
1752251881Speter      svn_hash_sets(pb->deletion_conflicts, apr_pstrdup(pb->pool, base),
1753251881Speter                    tree_conflict);
1754251881Speter
1755289180Speter      /* Whatever the kind of conflict, we can just clear BASE
1756289180Speter         by turning whatever is there into a copy */
1757251881Speter    }
1758251881Speter
1759289180Speter  /* Calculate the repository-relative path of the entry which was
1760289180Speter   * deleted. For updates it's the same as REPOS_RELPATH but for
1761289180Speter   * switches it is within the switch target. */
1762289180Speter  SVN_ERR(calculate_repos_relpath(&deleted_repos_relpath, local_abspath,
1763289180Speter                                  repos_relpath, eb, pb, scratch_pool,
1764289180Speter                                  scratch_pool));
1765251881Speter  SVN_ERR(complete_conflict(tree_conflict, eb, local_abspath, repos_relpath,
1766289180Speter                            old_revision, deleted_repos_relpath,
1767289180Speter                            kind, svn_node_none, NULL,
1768251881Speter                            pb->pool, scratch_pool));
1769251881Speter
1770251881Speter  /* Issue a wq operation to delete the BASE_NODE data and to delete actual
1771251881Speter     nodes based on that from disk, but leave any WORKING_NODEs on disk.
1772251881Speter
1773251881Speter     Local modifications are already turned into copies at this point.
1774251881Speter
1775251881Speter     If the thing being deleted is the *target* of this update, then
1776251881Speter     we need to recreate a 'deleted' entry, so that the parent can give
1777251881Speter     accurate reports about itself in the future. */
1778251881Speter  if (! deleting_target && ! deleting_switched)
1779251881Speter    {
1780251881Speter      /* Delete, and do not leave a not-present node.  */
1781251881Speter      SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath,
1782289180Speter                                     (tree_conflict != NULL),
1783289180Speter                                     FALSE, FALSE,
1784251881Speter                                     SVN_INVALID_REVNUM /* not_present_rev */,
1785251881Speter                                     tree_conflict, NULL,
1786251881Speter                                     scratch_pool));
1787251881Speter    }
1788251881Speter  else
1789251881Speter    {
1790251881Speter      /* Delete, leaving a not-present node.  */
1791251881Speter      SVN_ERR(svn_wc__db_base_remove(eb->db, local_abspath,
1792289180Speter                                     (tree_conflict != NULL),
1793289180Speter                                     TRUE, FALSE,
1794251881Speter                                     *eb->target_revision,
1795251881Speter                                     tree_conflict, NULL,
1796251881Speter                                     scratch_pool));
1797251881Speter      if (deleting_target)
1798251881Speter        eb->target_deleted = TRUE;
1799251881Speter      else
1800251881Speter        {
1801251881Speter          /* Don't remove the not-present marker at the final bump */
1802251881Speter          SVN_ERR(remember_skipped_tree(eb, local_abspath, pool));
1803251881Speter        }
1804251881Speter    }
1805251881Speter
1806251881Speter  SVN_ERR(svn_wc__wq_run(eb->db, pb->local_abspath,
1807251881Speter                         eb->cancel_func, eb->cancel_baton,
1808251881Speter                         scratch_pool));
1809251881Speter
1810251881Speter  /* Notify. */
1811251881Speter  if (tree_conflict)
1812253734Speter    {
1813253734Speter      if (eb->conflict_func)
1814253734Speter        SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, local_abspath,
1815289180Speter                                                 kind,
1816253734Speter                                                 tree_conflict,
1817253734Speter                                                 NULL /* merge_options */,
1818253734Speter                                                 eb->conflict_func,
1819253734Speter                                                 eb->conflict_baton,
1820253734Speter                                                 eb->cancel_func,
1821253734Speter                                                 eb->cancel_baton,
1822253734Speter                                                 scratch_pool));
1823289180Speter      do_notification(eb, local_abspath, kind,
1824253734Speter                      svn_wc_notify_tree_conflict, scratch_pool);
1825253734Speter    }
1826251881Speter  else
1827251881Speter    {
1828251881Speter      svn_wc_notify_action_t action = svn_wc_notify_update_delete;
1829251881Speter
1830251881Speter      if (pb->shadowed || pb->edit_obstructed)
1831251881Speter        action = svn_wc_notify_update_shadowed_delete;
1832251881Speter
1833289180Speter      do_notification(eb, local_abspath, kind, action, scratch_pool);
1834251881Speter    }
1835251881Speter
1836251881Speter  svn_pool_destroy(scratch_pool);
1837251881Speter
1838251881Speter  return SVN_NO_ERROR;
1839251881Speter}
1840251881Speter
1841251881Speter/* An svn_delta_editor_t function. */
1842251881Speterstatic svn_error_t *
1843251881Speteradd_directory(const char *path,
1844251881Speter              void *parent_baton,
1845251881Speter              const char *copyfrom_path,
1846251881Speter              svn_revnum_t copyfrom_rev,
1847251881Speter              apr_pool_t *pool,
1848251881Speter              void **child_baton)
1849251881Speter{
1850251881Speter  struct dir_baton *pb = parent_baton;
1851251881Speter  struct edit_baton *eb = pb->edit_baton;
1852251881Speter  struct dir_baton *db;
1853289180Speter  apr_pool_t *scratch_pool = svn_pool_create(pool);
1854251881Speter  svn_node_kind_t kind;
1855251881Speter  svn_wc__db_status_t status;
1856251881Speter  svn_node_kind_t wc_kind;
1857251881Speter  svn_boolean_t conflicted;
1858251881Speter  svn_boolean_t conflict_ignored = FALSE;
1859251881Speter  svn_boolean_t versioned_locally_and_present;
1860251881Speter  svn_skel_t *tree_conflict = NULL;
1861251881Speter  svn_error_t *err;
1862251881Speter
1863251881Speter  SVN_ERR_ASSERT(! (copyfrom_path || SVN_IS_VALID_REVNUM(copyfrom_rev)));
1864251881Speter
1865251881Speter  SVN_ERR(make_dir_baton(&db, path, eb, pb, TRUE, pool));
1866251881Speter  *child_baton = db;
1867251881Speter
1868251881Speter  if (db->skip_this)
1869251881Speter    return SVN_NO_ERROR;
1870251881Speter
1871289180Speter  SVN_ERR(calculate_repos_relpath(&db->new_repos_relpath, db->local_abspath,
1872289180Speter                                  NULL, eb, pb, db->pool, scratch_pool));
1873289180Speter
1874251881Speter  SVN_ERR(mark_directory_edited(db, pool));
1875251881Speter
1876251881Speter  if (strcmp(eb->target_abspath, db->local_abspath) == 0)
1877251881Speter    {
1878251881Speter      /* The target of the edit is being added, give it the requested
1879251881Speter         depth of the edit (but convert svn_depth_unknown to
1880251881Speter         svn_depth_infinity). */
1881251881Speter      db->ambient_depth = (eb->requested_depth == svn_depth_unknown)
1882251881Speter        ? svn_depth_infinity : eb->requested_depth;
1883251881Speter    }
1884251881Speter  else if (eb->requested_depth == svn_depth_immediates
1885251881Speter           || (eb->requested_depth == svn_depth_unknown
1886251881Speter               && pb->ambient_depth == svn_depth_immediates))
1887251881Speter    {
1888251881Speter      db->ambient_depth = svn_depth_empty;
1889251881Speter    }
1890251881Speter  else
1891251881Speter    {
1892251881Speter      db->ambient_depth = svn_depth_infinity;
1893251881Speter    }
1894251881Speter
1895251881Speter  /* It may not be named the same as the administrative directory. */
1896251881Speter  if (svn_wc_is_adm_dir(db->name, pool))
1897251881Speter    return svn_error_createf(
1898251881Speter       SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
1899251881Speter       _("Failed to add directory '%s': object of the same name as the "
1900251881Speter         "administrative directory"),
1901251881Speter       svn_dirent_local_style(db->local_abspath, pool));
1902251881Speter
1903289180Speter  if (!eb->clean_checkout)
1904289180Speter    {
1905289180Speter      SVN_ERR(svn_io_check_path(db->local_abspath, &kind, db->pool));
1906251881Speter
1907289180Speter      err = svn_wc__db_read_info(&status, &wc_kind, NULL, NULL, NULL, NULL, NULL,
1908289180Speter                                NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
1909289180Speter                                NULL, NULL, NULL, NULL, NULL,
1910289180Speter                                &conflicted, NULL, NULL, NULL, NULL, NULL, NULL,
1911289180Speter                                eb->db, db->local_abspath,
1912289180Speter                                scratch_pool, scratch_pool);
1913289180Speter    }
1914289180Speter  else
1915289180Speter    {
1916289180Speter      kind = svn_node_none;
1917289180Speter      status = svn_wc__db_status_not_present;
1918289180Speter      wc_kind = svn_node_unknown;
1919289180Speter      conflicted = FALSE;
1920289180Speter      err = NULL;
1921289180Speter    }
1922289180Speter
1923251881Speter  if (err)
1924251881Speter    {
1925251881Speter      if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
1926251881Speter        return svn_error_trace(err);
1927251881Speter
1928251881Speter      svn_error_clear(err);
1929251881Speter      wc_kind = svn_node_unknown;
1930251881Speter      status = svn_wc__db_status_normal;
1931251881Speter      conflicted = FALSE;
1932251881Speter
1933251881Speter      versioned_locally_and_present = FALSE;
1934251881Speter    }
1935289180Speter  else if (status == svn_wc__db_status_normal && wc_kind == svn_node_unknown)
1936251881Speter    {
1937289180Speter      SVN_ERR_ASSERT(conflicted);
1938289180Speter      versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */
1939289180Speter    }
1940289180Speter  else if (status == svn_wc__db_status_normal
1941289180Speter           || status == svn_wc__db_status_incomplete)
1942289180Speter    {
1943289180Speter      svn_boolean_t root;
1944251881Speter
1945289180Speter      SVN_ERR(svn_wc__db_is_wcroot(&root, eb->db, db->local_abspath,
1946289180Speter                                   scratch_pool));
1947251881Speter
1948289180Speter      if (root)
1949289180Speter        {
1950289180Speter          /* !! We found the root of a working copy obstructing the wc !!
1951251881Speter
1952289180Speter             If the directory would be part of our own working copy then
1953289180Speter             we wouldn't have been called as an add_directory().
1954251881Speter
1955289180Speter             The only thing we can do is add a not-present node, to allow
1956289180Speter             a future update to bring in the new files when the problem is
1957289180Speter             resolved.  Note that svn_wc__db_base_add_not_present_node()
1958289180Speter             explicitly adds the node into the parent's node database. */
1959251881Speter
1960289180Speter          svn_hash_sets(pb->not_present_nodes,
1961289180Speter                        apr_pstrdup(pb->pool, db->name),
1962289180Speter                        svn_node_kind_to_word(svn_node_dir));
1963289180Speter        }
1964289180Speter      else if (wc_kind == svn_node_dir)
1965289180Speter        {
1966289180Speter          /* We have an editor violation. Github sometimes does this
1967289180Speter             in its subversion compatibility code, when changing the
1968289180Speter             depth of a working copy, or on updates from incomplete */
1969289180Speter        }
1970289180Speter      else
1971289180Speter        {
1972289180Speter          /* We found a file external occupating the place we need in BASE.
1973251881Speter
1974289180Speter            We can't add a not-present node in this case as that would overwrite
1975289180Speter            the file external. Luckily the file external itself stops us from
1976289180Speter            forgetting a child of this parent directory like an obstructing
1977289180Speter            working copy would.
1978251881Speter
1979289180Speter            The reason we get here is that the adm crawler doesn't report
1980289180Speter            file externals.
1981289180Speter          */
1982289180Speter          SVN_ERR_ASSERT(wc_kind == svn_node_file
1983289180Speter                         || wc_kind == svn_node_symlink);
1984289180Speter        }
1985251881Speter
1986289180Speter      SVN_ERR(remember_skipped_tree(eb, db->local_abspath, scratch_pool));
1987251881Speter      db->skip_this = TRUE;
1988251881Speter      db->already_notified = TRUE;
1989251881Speter
1990289180Speter      do_notification(eb, db->local_abspath, wc_kind,
1991289180Speter                      svn_wc_notify_update_skip_obstruction, scratch_pool);
1992251881Speter
1993289180Speter      svn_pool_destroy(scratch_pool);
1994289180Speter
1995251881Speter      return SVN_NO_ERROR;
1996251881Speter    }
1997251881Speter  else
1998251881Speter    versioned_locally_and_present = IS_NODE_PRESENT(status);
1999251881Speter
2000251881Speter  /* Is this path a conflict victim? */
2001251881Speter  if (conflicted)
2002251881Speter    {
2003251881Speter      if (pb->deletion_conflicts)
2004251881Speter        tree_conflict = svn_hash_gets(pb->deletion_conflicts, db->name);
2005251881Speter
2006251881Speter      if (tree_conflict)
2007251881Speter        {
2008251881Speter          svn_wc_conflict_reason_t reason;
2009286506Speter          const char *move_src_op_root_abspath;
2010251881Speter          /* So this deletion wasn't just a deletion, it is actually a
2011251881Speter             replacement. Let's install a better tree conflict. */
2012251881Speter
2013286506Speter          SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL,
2014286506Speter                                                      &move_src_op_root_abspath,
2015251881Speter                                                      eb->db,
2016251881Speter                                                      db->local_abspath,
2017251881Speter                                                      tree_conflict,
2018289180Speter                                                      db->pool, scratch_pool));
2019251881Speter
2020251881Speter          tree_conflict = svn_wc__conflict_skel_create(db->pool);
2021251881Speter
2022251881Speter          SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
2023251881Speter                                        tree_conflict,
2024251881Speter                                        eb->db, db->local_abspath,
2025251881Speter                                        reason, svn_wc_conflict_action_replace,
2026286506Speter                                        move_src_op_root_abspath,
2027289180Speter                                        db->pool, scratch_pool));
2028251881Speter
2029251881Speter          /* And now stop checking for conflicts here and just perform
2030251881Speter             a shadowed update */
2031251881Speter          db->edit_conflict = tree_conflict; /* Cache for close_directory */
2032251881Speter          tree_conflict = NULL; /* No direct notification */
2033251881Speter          db->shadowed = TRUE; /* Just continue */
2034251881Speter          conflicted = FALSE; /* No skip */
2035251881Speter        }
2036251881Speter      else
2037251881Speter        SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
2038289180Speter                                        eb->db, db->local_abspath,
2039289180Speter                                        scratch_pool));
2040251881Speter    }
2041251881Speter
2042251881Speter  /* Now the "usual" behaviour if already conflicted. Skip it. */
2043251881Speter  if (conflicted)
2044251881Speter    {
2045251881Speter      /* Record this conflict so that its descendants are skipped silently. */
2046251881Speter      SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
2047251881Speter
2048251881Speter      db->skip_this = TRUE;
2049251881Speter      db->already_notified = TRUE;
2050251881Speter
2051251881Speter      /* We skip this node, but once the update completes the parent node will
2052251881Speter         be updated to the new revision. So a future recursive update of the
2053251881Speter         parent will not bring in this new node as the revision of the parent
2054251881Speter         describes to the repository that all children are available.
2055251881Speter
2056251881Speter         To resolve this problem, we add a not-present node to allow bringing
2057251881Speter         the node in once this conflict is resolved.
2058251881Speter
2059251881Speter         Note that we can safely assume that no present base node exists,
2060251881Speter         because then we would not have received an add_directory.
2061251881Speter       */
2062289180Speter      svn_hash_sets(pb->not_present_nodes, apr_pstrdup(pb->pool, db->name),
2063289180Speter                    svn_node_kind_to_word(svn_node_dir));
2064251881Speter
2065251881Speter      do_notification(eb, db->local_abspath, svn_node_dir,
2066289180Speter                      svn_wc_notify_skip_conflicted, scratch_pool);
2067289180Speter
2068289180Speter      svn_pool_destroy(scratch_pool);
2069251881Speter      return SVN_NO_ERROR;
2070251881Speter    }
2071251881Speter  else if (conflict_ignored)
2072251881Speter    {
2073251881Speter      db->shadowed = TRUE;
2074251881Speter    }
2075251881Speter
2076251881Speter  if (db->shadowed)
2077251881Speter    {
2078251881Speter      /* Nothing to check; does not and will not exist in working copy */
2079251881Speter    }
2080251881Speter  else if (versioned_locally_and_present)
2081251881Speter    {
2082251881Speter      /* What to do with a versioned or schedule-add dir:
2083251881Speter
2084251881Speter         A dir already added without history is OK.  Set add_existed
2085251881Speter         so that user notification is delayed until after any prop
2086251881Speter         conflicts have been found.
2087251881Speter
2088251881Speter         An existing versioned dir is an error.  In the future we may
2089251881Speter         relax this restriction and simply update such dirs.
2090251881Speter
2091251881Speter         A dir added with history is a tree conflict. */
2092251881Speter
2093251881Speter      svn_boolean_t local_is_non_dir;
2094251881Speter      svn_wc__db_status_t add_status = svn_wc__db_status_normal;
2095251881Speter
2096251881Speter      /* Is the local add a copy? */
2097251881Speter      if (status == svn_wc__db_status_added)
2098251881Speter        SVN_ERR(svn_wc__db_scan_addition(&add_status, NULL, NULL, NULL, NULL,
2099251881Speter                                         NULL, NULL, NULL, NULL,
2100251881Speter                                         eb->db, db->local_abspath,
2101289180Speter                                         scratch_pool, scratch_pool));
2102251881Speter
2103251881Speter
2104251881Speter      /* Is there *something* that is not a dir? */
2105251881Speter      local_is_non_dir = (wc_kind != svn_node_dir
2106251881Speter                          && status != svn_wc__db_status_deleted);
2107251881Speter
2108251881Speter      /* Do tree conflict checking if
2109251881Speter       *  - if there is a local copy.
2110251881Speter       *  - if this is a switch operation
2111251881Speter       *  - the node kinds mismatch
2112251881Speter       *
2113251881Speter       * During switch, local adds at the same path as incoming adds get
2114251881Speter       * "lost" in that switching back to the original will no longer have the
2115251881Speter       * local add. So switch always alerts the user with a tree conflict. */
2116251881Speter      if (!eb->adds_as_modification
2117251881Speter          || local_is_non_dir
2118251881Speter          || add_status != svn_wc__db_status_added)
2119251881Speter        {
2120251881Speter          SVN_ERR(check_tree_conflict(&tree_conflict, eb,
2121251881Speter                                      db->local_abspath,
2122251881Speter                                      status, FALSE, svn_node_none,
2123251881Speter                                      svn_wc_conflict_action_add,
2124289180Speter                                      db->pool, scratch_pool));
2125251881Speter        }
2126251881Speter
2127251881Speter      if (tree_conflict == NULL)
2128251881Speter        db->add_existed = TRUE; /* Take over WORKING */
2129251881Speter      else
2130251881Speter        db->shadowed = TRUE; /* Only update BASE */
2131251881Speter    }
2132251881Speter  else if (kind != svn_node_none)
2133251881Speter    {
2134251881Speter      /* There's an unversioned node at this path. */
2135251881Speter      db->obstruction_found = TRUE;
2136251881Speter
2137251881Speter      /* Unversioned, obstructing dirs are handled by prop merge/conflict,
2138251881Speter       * if unversioned obstructions are allowed. */
2139251881Speter      if (! (kind == svn_node_dir && eb->allow_unver_obstructions))
2140251881Speter        {
2141251881Speter          /* Bring in the node as deleted */ /* ### Obstructed Conflict */
2142251881Speter          db->shadowed = TRUE;
2143251881Speter
2144251881Speter          /* Mark a conflict */
2145251881Speter          tree_conflict = svn_wc__conflict_skel_create(db->pool);
2146251881Speter
2147251881Speter          SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
2148251881Speter                                        tree_conflict,
2149251881Speter                                        eb->db, db->local_abspath,
2150251881Speter                                        svn_wc_conflict_reason_unversioned,
2151251881Speter                                        svn_wc_conflict_action_add, NULL,
2152289180Speter                                        db->pool, scratch_pool));
2153251881Speter          db->edit_conflict = tree_conflict;
2154251881Speter        }
2155251881Speter    }
2156251881Speter
2157251881Speter  if (tree_conflict)
2158251881Speter    SVN_ERR(complete_conflict(tree_conflict, eb, db->local_abspath,
2159251881Speter                              db->old_repos_relpath, db->old_revision,
2160289180Speter                              db->new_repos_relpath,
2161289180Speter                              wc_kind, svn_node_dir,
2162289180Speter                              pb->deletion_conflicts
2163289180Speter                                ? svn_hash_gets(pb->deletion_conflicts,
2164289180Speter                                                db->name)
2165289180Speter                                : NULL,
2166289180Speter                              db->pool, scratch_pool));
2167251881Speter
2168251881Speter  SVN_ERR(svn_wc__db_base_add_incomplete_directory(
2169251881Speter                                     eb->db, db->local_abspath,
2170289180Speter                                     db->new_repos_relpath,
2171251881Speter                                     eb->repos_root,
2172251881Speter                                     eb->repos_uuid,
2173251881Speter                                     *eb->target_revision,
2174251881Speter                                     db->ambient_depth,
2175251881Speter                                     (db->shadowed && db->obstruction_found),
2176251881Speter                                     (! db->shadowed
2177251881Speter                                      && status == svn_wc__db_status_added),
2178251881Speter                                     tree_conflict, NULL,
2179289180Speter                                     scratch_pool));
2180251881Speter
2181251881Speter  /* Make sure there is a real directory at LOCAL_ABSPATH, unless we are just
2182251881Speter     updating the DB */
2183251881Speter  if (!db->shadowed)
2184289180Speter    SVN_ERR(svn_wc__ensure_directory(db->local_abspath, scratch_pool));
2185251881Speter
2186251881Speter  if (tree_conflict != NULL)
2187251881Speter    {
2188289180Speter      db->edit_conflict = tree_conflict;
2189253734Speter
2190251881Speter      db->already_notified = TRUE;
2191251881Speter      do_notification(eb, db->local_abspath, svn_node_dir,
2192289180Speter                      svn_wc_notify_tree_conflict, scratch_pool);
2193251881Speter    }
2194251881Speter
2195251881Speter
2196251881Speter  /* If this add was obstructed by dir scheduled for addition without
2197251881Speter     history let close_directory() handle the notification because there
2198251881Speter     might be properties to deal with.  If PATH was added inside a locally
2199251881Speter     deleted tree, then suppress notification, a tree conflict was already
2200251881Speter     issued. */
2201251881Speter  if (eb->notify_func && !db->already_notified && !db->add_existed)
2202251881Speter    {
2203251881Speter      svn_wc_notify_action_t action;
2204251881Speter
2205251881Speter      if (db->shadowed)
2206251881Speter        action = svn_wc_notify_update_shadowed_add;
2207251881Speter      else if (db->obstruction_found || db->add_existed)
2208251881Speter        action = svn_wc_notify_exists;
2209251881Speter      else
2210251881Speter        action = svn_wc_notify_update_add;
2211251881Speter
2212251881Speter      db->already_notified = TRUE;
2213251881Speter
2214289180Speter      do_notification(eb, db->local_abspath, svn_node_dir, action,
2215289180Speter                      scratch_pool);
2216251881Speter    }
2217251881Speter
2218289180Speter  svn_pool_destroy(scratch_pool);
2219289180Speter
2220251881Speter  return SVN_NO_ERROR;
2221251881Speter}
2222251881Speter
2223251881Speter/* An svn_delta_editor_t function. */
2224251881Speterstatic svn_error_t *
2225251881Speteropen_directory(const char *path,
2226251881Speter               void *parent_baton,
2227251881Speter               svn_revnum_t base_revision,
2228251881Speter               apr_pool_t *pool,
2229251881Speter               void **child_baton)
2230251881Speter{
2231251881Speter  struct dir_baton *db, *pb = parent_baton;
2232251881Speter  struct edit_baton *eb = pb->edit_baton;
2233251881Speter  svn_boolean_t have_work;
2234251881Speter  svn_boolean_t conflicted;
2235251881Speter  svn_boolean_t conflict_ignored = FALSE;
2236251881Speter  svn_skel_t *tree_conflict = NULL;
2237251881Speter  svn_wc__db_status_t status, base_status;
2238251881Speter  svn_node_kind_t wc_kind;
2239251881Speter
2240251881Speter  SVN_ERR(make_dir_baton(&db, path, eb, pb, FALSE, pool));
2241251881Speter  *child_baton = db;
2242251881Speter
2243251881Speter  if (db->skip_this)
2244251881Speter    return SVN_NO_ERROR;
2245251881Speter
2246251881Speter  /* Detect obstructing working copies */
2247251881Speter  {
2248251881Speter    svn_boolean_t is_root;
2249251881Speter
2250251881Speter    SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, db->local_abspath,
2251251881Speter                                 pool));
2252251881Speter
2253251881Speter    if (is_root)
2254251881Speter      {
2255251881Speter        /* Just skip this node; a future update will handle it */
2256251881Speter        SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
2257251881Speter        db->skip_this = TRUE;
2258251881Speter        db->already_notified = TRUE;
2259251881Speter
2260251881Speter        do_notification(eb, db->local_abspath, svn_node_dir,
2261251881Speter                        svn_wc_notify_update_skip_obstruction, pool);
2262251881Speter
2263251881Speter        return SVN_NO_ERROR;
2264251881Speter      }
2265251881Speter  }
2266251881Speter
2267251881Speter  /* We should have a write lock on every directory touched.  */
2268251881Speter  SVN_ERR(svn_wc__write_check(eb->db, db->local_abspath, pool));
2269251881Speter
2270251881Speter  SVN_ERR(svn_wc__db_read_info(&status, &wc_kind, &db->old_revision,
2271251881Speter                               &db->old_repos_relpath, NULL, NULL,
2272251881Speter                               &db->changed_rev, &db->changed_date,
2273251881Speter                               &db->changed_author, &db->ambient_depth,
2274251881Speter                               NULL, NULL, NULL, NULL,
2275251881Speter                               NULL, NULL, NULL, NULL, NULL, NULL,
2276251881Speter                               &conflicted, NULL, NULL, NULL,
2277251881Speter                               NULL, NULL, &have_work,
2278251881Speter                               eb->db, db->local_abspath,
2279251881Speter                               db->pool, pool));
2280251881Speter
2281251881Speter  if (!have_work)
2282251881Speter    base_status = status;
2283251881Speter  else
2284251881Speter    SVN_ERR(svn_wc__db_base_get_info(&base_status, NULL, &db->old_revision,
2285251881Speter                                     &db->old_repos_relpath, NULL, NULL,
2286251881Speter                                     &db->changed_rev, &db->changed_date,
2287251881Speter                                     &db->changed_author, &db->ambient_depth,
2288251881Speter                                     NULL, NULL, NULL, NULL, NULL, NULL,
2289251881Speter                                     eb->db, db->local_abspath,
2290251881Speter                                     db->pool, pool));
2291251881Speter
2292251881Speter  db->was_incomplete = (base_status == svn_wc__db_status_incomplete);
2293251881Speter
2294289180Speter  SVN_ERR(calculate_repos_relpath(&db->new_repos_relpath, db->local_abspath,
2295289180Speter                                  db->old_repos_relpath, eb, pb,
2296289180Speter                                  db->pool, pool));
2297289180Speter
2298251881Speter  /* Is this path a conflict victim? */
2299251881Speter  if (db->shadowed)
2300251881Speter    conflicted = FALSE; /* Conflict applies to WORKING */
2301251881Speter  else if (conflicted)
2302251881Speter    SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
2303251881Speter                                    eb->db, db->local_abspath, pool));
2304251881Speter  if (conflicted)
2305251881Speter    {
2306251881Speter      SVN_ERR(remember_skipped_tree(eb, db->local_abspath, pool));
2307251881Speter
2308251881Speter      db->skip_this = TRUE;
2309251881Speter      db->already_notified = TRUE;
2310251881Speter
2311251881Speter      do_notification(eb, db->local_abspath, svn_node_unknown,
2312251881Speter                      svn_wc_notify_skip_conflicted, pool);
2313251881Speter
2314251881Speter      return SVN_NO_ERROR;
2315251881Speter    }
2316251881Speter  else if (conflict_ignored)
2317251881Speter    {
2318251881Speter      db->shadowed = TRUE;
2319251881Speter    }
2320251881Speter
2321251881Speter  /* Is this path a fresh tree conflict victim?  If so, skip the tree
2322251881Speter     with one notification. */
2323251881Speter
2324251881Speter  /* Check for conflicts only when we haven't already recorded
2325251881Speter   * a tree-conflict on a parent node. */
2326251881Speter  if (!db->shadowed)
2327251881Speter    SVN_ERR(check_tree_conflict(&tree_conflict, eb, db->local_abspath,
2328251881Speter                                status, TRUE, svn_node_dir,
2329251881Speter                                svn_wc_conflict_action_edit,
2330251881Speter                                db->pool, pool));
2331251881Speter
2332251881Speter  /* Remember the roots of any locally deleted trees. */
2333251881Speter  if (tree_conflict != NULL)
2334251881Speter    {
2335251881Speter      svn_wc_conflict_reason_t reason;
2336251881Speter      db->edit_conflict = tree_conflict;
2337251881Speter      /* Other modifications wouldn't be a tree conflict */
2338251881Speter
2339251881Speter      SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
2340251881Speter                                                  eb->db, db->local_abspath,
2341251881Speter                                                  tree_conflict,
2342251881Speter                                                  db->pool, db->pool));
2343251881Speter      SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_deleted
2344251881Speter                     || reason == svn_wc_conflict_reason_moved_away
2345251881Speter                     || reason == svn_wc_conflict_reason_replaced
2346251881Speter                     || reason == svn_wc_conflict_reason_obstructed);
2347251881Speter
2348251881Speter      /* Continue updating BASE */
2349251881Speter      if (reason == svn_wc_conflict_reason_obstructed)
2350251881Speter        db->edit_obstructed = TRUE;
2351251881Speter      else
2352251881Speter        db->shadowed = TRUE;
2353251881Speter    }
2354251881Speter
2355251881Speter  /* Mark directory as being at target_revision and URL, but incomplete. */
2356251881Speter  SVN_ERR(svn_wc__db_temp_op_start_directory_update(eb->db, db->local_abspath,
2357289180Speter                                                    db->new_repos_relpath,
2358251881Speter                                                    *eb->target_revision,
2359251881Speter                                                    pool));
2360251881Speter
2361251881Speter  return SVN_NO_ERROR;
2362251881Speter}
2363251881Speter
2364251881Speter
2365251881Speter/* An svn_delta_editor_t function. */
2366251881Speterstatic svn_error_t *
2367251881Speterchange_dir_prop(void *dir_baton,
2368251881Speter                const char *name,
2369251881Speter                const svn_string_t *value,
2370251881Speter                apr_pool_t *pool)
2371251881Speter{
2372251881Speter  svn_prop_t *propchange;
2373251881Speter  struct dir_baton *db = dir_baton;
2374251881Speter
2375251881Speter  if (db->skip_this)
2376251881Speter    return SVN_NO_ERROR;
2377251881Speter
2378251881Speter  propchange = apr_array_push(db->propchanges);
2379251881Speter  propchange->name = apr_pstrdup(db->pool, name);
2380289180Speter  propchange->value = svn_string_dup(value, db->pool);
2381251881Speter
2382251881Speter  if (!db->edited && svn_property_kind2(name) == svn_prop_regular_kind)
2383251881Speter    SVN_ERR(mark_directory_edited(db, pool));
2384251881Speter
2385251881Speter  return SVN_NO_ERROR;
2386251881Speter}
2387251881Speter
2388251881Speter/* If any of the svn_prop_t objects in PROPCHANGES represents a change
2389251881Speter   to the SVN_PROP_EXTERNALS property, return that change, else return
2390251881Speter   null.  If PROPCHANGES contains more than one such change, return
2391251881Speter   the first. */
2392251881Speterstatic const svn_prop_t *
2393251881Speterexternals_prop_changed(const apr_array_header_t *propchanges)
2394251881Speter{
2395251881Speter  int i;
2396251881Speter
2397251881Speter  for (i = 0; i < propchanges->nelts; i++)
2398251881Speter    {
2399251881Speter      const svn_prop_t *p = &(APR_ARRAY_IDX(propchanges, i, svn_prop_t));
2400251881Speter      if (strcmp(p->name, SVN_PROP_EXTERNALS) == 0)
2401251881Speter        return p;
2402251881Speter    }
2403251881Speter
2404251881Speter  return NULL;
2405251881Speter}
2406251881Speter
2407251881Speter
2408251881Speter
2409251881Speter/* An svn_delta_editor_t function. */
2410251881Speterstatic svn_error_t *
2411251881Speterclose_directory(void *dir_baton,
2412251881Speter                apr_pool_t *pool)
2413251881Speter{
2414251881Speter  struct dir_baton *db = dir_baton;
2415251881Speter  struct edit_baton *eb = db->edit_baton;
2416251881Speter  svn_wc_notify_state_t prop_state = svn_wc_notify_state_unknown;
2417251881Speter  apr_array_header_t *entry_prop_changes;
2418251881Speter  apr_array_header_t *dav_prop_changes;
2419251881Speter  apr_array_header_t *regular_prop_changes;
2420251881Speter  apr_hash_t *base_props;
2421251881Speter  apr_hash_t *actual_props;
2422251881Speter  apr_hash_t *new_base_props = NULL;
2423251881Speter  apr_hash_t *new_actual_props = NULL;
2424251881Speter  svn_revnum_t new_changed_rev = SVN_INVALID_REVNUM;
2425251881Speter  apr_time_t new_changed_date = 0;
2426251881Speter  const char *new_changed_author = NULL;
2427251881Speter  apr_pool_t *scratch_pool = db->pool;
2428251881Speter  svn_skel_t *all_work_items = NULL;
2429251881Speter  svn_skel_t *conflict_skel = NULL;
2430251881Speter
2431251881Speter  /* Skip if we're in a conflicted tree. */
2432251881Speter  if (db->skip_this)
2433251881Speter    {
2434251881Speter      /* Allow the parent to complete its update. */
2435251881Speter      SVN_ERR(maybe_release_dir_info(db));
2436251881Speter
2437251881Speter      return SVN_NO_ERROR;
2438251881Speter    }
2439251881Speter
2440251881Speter  if (db->edited)
2441251881Speter    conflict_skel = db->edit_conflict;
2442251881Speter
2443251881Speter  SVN_ERR(svn_categorize_props(db->propchanges, &entry_prop_changes,
2444251881Speter                               &dav_prop_changes, &regular_prop_changes, pool));
2445251881Speter
2446251881Speter  /* Fetch the existing properties.  */
2447251881Speter  if ((!db->adding_dir || db->add_existed)
2448251881Speter      && !db->shadowed)
2449251881Speter    {
2450251881Speter      SVN_ERR(svn_wc__get_actual_props(&actual_props,
2451251881Speter                                       eb->db, db->local_abspath,
2452251881Speter                                       scratch_pool, scratch_pool));
2453251881Speter    }
2454251881Speter  else
2455251881Speter    actual_props = apr_hash_make(pool);
2456251881Speter
2457251881Speter  if (db->add_existed)
2458251881Speter    {
2459251881Speter      /* This node already exists. Grab the current pristine properties. */
2460251881Speter      SVN_ERR(svn_wc__db_read_pristine_props(&base_props,
2461251881Speter                                             eb->db, db->local_abspath,
2462251881Speter                                             scratch_pool, scratch_pool));
2463251881Speter    }
2464251881Speter  else if (!db->adding_dir)
2465251881Speter    {
2466251881Speter      /* Get the BASE properties for proper merging. */
2467251881Speter      SVN_ERR(svn_wc__db_base_get_props(&base_props,
2468251881Speter                                        eb->db, db->local_abspath,
2469251881Speter                                        scratch_pool, scratch_pool));
2470251881Speter    }
2471251881Speter  else
2472251881Speter    base_props = apr_hash_make(pool);
2473251881Speter
2474251881Speter  /* An incomplete directory might have props which were supposed to be
2475251881Speter     deleted but weren't.  Because the server sent us all the props we're
2476251881Speter     supposed to have, any previous base props not in this list must be
2477251881Speter     deleted (issue #1672). */
2478251881Speter  if (db->was_incomplete)
2479251881Speter    {
2480251881Speter      int i;
2481251881Speter      apr_hash_t *props_to_delete;
2482251881Speter      apr_hash_index_t *hi;
2483251881Speter
2484251881Speter      /* In a copy of the BASE props, remove every property that we see an
2485251881Speter         incoming change for. The remaining unmentioned properties are those
2486251881Speter         which need to be deleted.  */
2487251881Speter      props_to_delete = apr_hash_copy(pool, base_props);
2488251881Speter      for (i = 0; i < regular_prop_changes->nelts; i++)
2489251881Speter        {
2490251881Speter          const svn_prop_t *prop;
2491251881Speter          prop = &APR_ARRAY_IDX(regular_prop_changes, i, svn_prop_t);
2492251881Speter          svn_hash_sets(props_to_delete, prop->name, NULL);
2493251881Speter        }
2494251881Speter
2495251881Speter      /* Add these props to the incoming propchanges (in
2496251881Speter       * regular_prop_changes).  */
2497251881Speter      for (hi = apr_hash_first(pool, props_to_delete);
2498251881Speter           hi != NULL;
2499251881Speter           hi = apr_hash_next(hi))
2500251881Speter        {
2501289180Speter          const char *propname = apr_hash_this_key(hi);
2502251881Speter          svn_prop_t *prop = apr_array_push(regular_prop_changes);
2503251881Speter
2504251881Speter          /* Record a deletion for PROPNAME.  */
2505251881Speter          prop->name = propname;
2506251881Speter          prop->value = NULL;
2507251881Speter        }
2508251881Speter    }
2509251881Speter
2510251881Speter  /* If this directory has property changes stored up, now is the time
2511251881Speter     to deal with them. */
2512251881Speter  if (regular_prop_changes->nelts)
2513251881Speter    {
2514251881Speter      /* If recording traversal info, then see if the
2515251881Speter         SVN_PROP_EXTERNALS property on this directory changed,
2516251881Speter         and record before and after for the change. */
2517251881Speter      if (eb->external_func)
2518251881Speter        {
2519251881Speter          const svn_prop_t *change
2520251881Speter            = externals_prop_changed(regular_prop_changes);
2521251881Speter
2522251881Speter          if (change)
2523251881Speter            {
2524251881Speter              const svn_string_t *new_val_s = change->value;
2525251881Speter              const svn_string_t *old_val_s;
2526251881Speter
2527251881Speter              old_val_s = svn_hash_gets(base_props, SVN_PROP_EXTERNALS);
2528251881Speter
2529251881Speter              if ((new_val_s == NULL) && (old_val_s == NULL))
2530251881Speter                ; /* No value before, no value after... so do nothing. */
2531251881Speter              else if (new_val_s && old_val_s
2532251881Speter                       && (svn_string_compare(old_val_s, new_val_s)))
2533251881Speter                ; /* Value did not change... so do nothing. */
2534251881Speter              else if (old_val_s || new_val_s)
2535251881Speter                /* something changed, record the change */
2536251881Speter                {
2537251881Speter                  SVN_ERR((eb->external_func)(
2538251881Speter                                       eb->external_baton,
2539251881Speter                                       db->local_abspath,
2540251881Speter                                       old_val_s,
2541251881Speter                                       new_val_s,
2542251881Speter                                       db->ambient_depth,
2543251881Speter                                       db->pool));
2544251881Speter                }
2545251881Speter            }
2546251881Speter        }
2547251881Speter
2548251881Speter      if (db->shadowed)
2549251881Speter        {
2550251881Speter          /* We don't have a relevant actual row, but we need actual properties
2551251881Speter             to allow property merging without conflicts. */
2552251881Speter          if (db->adding_dir)
2553251881Speter            actual_props = apr_hash_make(scratch_pool);
2554251881Speter          else
2555251881Speter            actual_props = base_props;
2556251881Speter        }
2557251881Speter
2558251881Speter      /* Merge pending properties. */
2559251881Speter      new_base_props = svn_prop__patch(base_props, regular_prop_changes,
2560251881Speter                                       db->pool);
2561251881Speter      SVN_ERR_W(svn_wc__merge_props(&conflict_skel,
2562251881Speter                                    &prop_state,
2563251881Speter                                    &new_actual_props,
2564251881Speter                                    eb->db,
2565251881Speter                                    db->local_abspath,
2566251881Speter                                    NULL /* use baseprops */,
2567251881Speter                                    base_props,
2568251881Speter                                    actual_props,
2569251881Speter                                    regular_prop_changes,
2570251881Speter                                    db->pool,
2571251881Speter                                    scratch_pool),
2572251881Speter                _("Couldn't do property merge"));
2573251881Speter      /* After a (not-dry-run) merge, we ALWAYS have props to save.  */
2574251881Speter      SVN_ERR_ASSERT(new_base_props != NULL && new_actual_props != NULL);
2575251881Speter    }
2576251881Speter
2577251881Speter  SVN_ERR(accumulate_last_change(&new_changed_rev, &new_changed_date,
2578251881Speter                                 &new_changed_author, entry_prop_changes,
2579251881Speter                                 scratch_pool, scratch_pool));
2580251881Speter
2581251881Speter  /* Check if we should add some not-present markers before marking the
2582251881Speter     directory complete (Issue #3569) */
2583251881Speter  {
2584289180Speter    apr_hash_t *new_children = svn_hash_gets(eb->dir_dirents,
2585289180Speter                                             db->new_repos_relpath);
2586251881Speter
2587251881Speter    if (new_children != NULL)
2588251881Speter      {
2589251881Speter        apr_hash_index_t *hi;
2590251881Speter        apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2591251881Speter
2592251881Speter        for (hi = apr_hash_first(scratch_pool, new_children);
2593251881Speter             hi;
2594251881Speter             hi = apr_hash_next(hi))
2595251881Speter          {
2596251881Speter            const char *child_name;
2597251881Speter            const char *child_abspath;
2598251881Speter            const char *child_relpath;
2599251881Speter            const svn_dirent_t *dirent;
2600251881Speter            svn_wc__db_status_t status;
2601251881Speter            svn_node_kind_t child_kind;
2602251881Speter            svn_error_t *err;
2603251881Speter
2604251881Speter            svn_pool_clear(iterpool);
2605251881Speter
2606289180Speter            child_name = apr_hash_this_key(hi);
2607251881Speter            child_abspath = svn_dirent_join(db->local_abspath, child_name,
2608251881Speter                                            iterpool);
2609251881Speter
2610289180Speter            dirent = apr_hash_this_val(hi);
2611251881Speter            child_kind = (dirent->kind == svn_node_dir)
2612251881Speter                                        ? svn_node_dir
2613251881Speter                                        : svn_node_file;
2614251881Speter
2615251881Speter            if (db->ambient_depth < svn_depth_immediates
2616251881Speter                && child_kind == svn_node_dir)
2617251881Speter              continue; /* We don't need the subdirs */
2618251881Speter
2619251881Speter            /* ### We just check if there is some node in BASE at this path */
2620251881Speter            err = svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL,
2621251881Speter                                           NULL, NULL, NULL, NULL, NULL, NULL,
2622251881Speter                                           NULL, NULL, NULL, NULL, NULL,
2623251881Speter                                           eb->db, child_abspath,
2624251881Speter                                           iterpool, iterpool);
2625251881Speter
2626251881Speter            if (!err)
2627251881Speter              {
2628251881Speter                svn_boolean_t is_wcroot;
2629251881Speter                SVN_ERR(svn_wc__db_is_wcroot(&is_wcroot, eb->db, child_abspath,
2630251881Speter                                             iterpool));
2631251881Speter
2632251881Speter                if (!is_wcroot)
2633251881Speter                  continue; /* Everything ok... Nothing to do here */
2634251881Speter                /* Fall through to allow recovering later */
2635251881Speter              }
2636251881Speter            else if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
2637251881Speter              return svn_error_trace(err);
2638251881Speter
2639251881Speter            svn_error_clear(err);
2640251881Speter
2641289180Speter            child_relpath = svn_relpath_join(db->new_repos_relpath, child_name,
2642251881Speter                                             iterpool);
2643251881Speter
2644251881Speter            SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db,
2645251881Speter                                                         child_abspath,
2646251881Speter                                                         child_relpath,
2647251881Speter                                                         eb->repos_root,
2648251881Speter                                                         eb->repos_uuid,
2649251881Speter                                                         *eb->target_revision,
2650251881Speter                                                         child_kind,
2651251881Speter                                                         NULL, NULL,
2652251881Speter                                                         iterpool));
2653251881Speter          }
2654251881Speter
2655251881Speter        svn_pool_destroy(iterpool);
2656251881Speter      }
2657251881Speter  }
2658251881Speter
2659289180Speter  if (apr_hash_count(db->not_present_nodes))
2660251881Speter    {
2661251881Speter      apr_hash_index_t *hi;
2662251881Speter      apr_pool_t *iterpool = svn_pool_create(scratch_pool);
2663251881Speter
2664251881Speter      /* This should call some new function (which could also be used
2665251881Speter         for new_children above) to add all the names in single
2666251881Speter         transaction, but I can't even trigger it.  I've tried
2667251881Speter         ra_local, ra_svn, ra_neon, ra_serf and they all call
2668251881Speter         close_file before close_dir. */
2669289180Speter      for (hi = apr_hash_first(scratch_pool, db->not_present_nodes);
2670251881Speter           hi;
2671251881Speter           hi = apr_hash_next(hi))
2672251881Speter        {
2673289180Speter          const char *child = apr_hash_this_key(hi);
2674251881Speter          const char *child_abspath, *child_relpath;
2675289180Speter          svn_node_kind_t kind = svn_node_kind_from_word(apr_hash_this_val(hi));
2676251881Speter
2677251881Speter          svn_pool_clear(iterpool);
2678251881Speter
2679251881Speter          child_abspath = svn_dirent_join(db->local_abspath, child, iterpool);
2680289180Speter          child_relpath = svn_dirent_join(db->new_repos_relpath, child, iterpool);
2681251881Speter
2682251881Speter          SVN_ERR(svn_wc__db_base_add_not_present_node(eb->db,
2683251881Speter                                                       child_abspath,
2684251881Speter                                                       child_relpath,
2685251881Speter                                                       eb->repos_root,
2686251881Speter                                                       eb->repos_uuid,
2687251881Speter                                                       *eb->target_revision,
2688289180Speter                                                       kind,
2689251881Speter                                                       NULL, NULL,
2690251881Speter                                                       iterpool));
2691251881Speter        }
2692251881Speter      svn_pool_destroy(iterpool);
2693251881Speter    }
2694251881Speter
2695251881Speter  /* If this directory is merely an anchor for a targeted child, then we
2696251881Speter     should not be updating the node at all.  */
2697251881Speter  if (db->parent_baton == NULL
2698251881Speter      && *eb->target_basename != '\0')
2699251881Speter    {
2700251881Speter      /* And we should not have received any changes!  */
2701251881Speter      SVN_ERR_ASSERT(db->propchanges->nelts == 0);
2702251881Speter      /* ... which also implies NEW_CHANGED_* are not set,
2703251881Speter         and NEW_BASE_PROPS == NULL.  */
2704251881Speter    }
2705251881Speter  else
2706251881Speter    {
2707251881Speter      apr_hash_t *props;
2708251881Speter      apr_array_header_t *iprops = NULL;
2709251881Speter
2710251881Speter      /* ### we know a base node already exists. it was created in
2711251881Speter         ### open_directory or add_directory.  let's just preserve the
2712251881Speter         ### existing DEPTH value, and possibly CHANGED_*.  */
2713251881Speter      /* If we received any changed_* values, then use them.  */
2714251881Speter      if (SVN_IS_VALID_REVNUM(new_changed_rev))
2715251881Speter        db->changed_rev = new_changed_rev;
2716251881Speter      if (new_changed_date != 0)
2717251881Speter        db->changed_date = new_changed_date;
2718251881Speter      if (new_changed_author != NULL)
2719251881Speter        db->changed_author = new_changed_author;
2720251881Speter
2721251881Speter      /* If no depth is set yet, set to infinity. */
2722251881Speter      if (db->ambient_depth == svn_depth_unknown)
2723251881Speter        db->ambient_depth = svn_depth_infinity;
2724251881Speter
2725251881Speter      if (eb->depth_is_sticky
2726251881Speter          && db->ambient_depth != eb->requested_depth)
2727251881Speter        {
2728251881Speter          /* After a depth upgrade the entry must reflect the new depth.
2729251881Speter             Upgrading to infinity changes the depth of *all* directories,
2730251881Speter             upgrading to something else only changes the target. */
2731251881Speter
2732251881Speter          if (eb->requested_depth == svn_depth_infinity
2733251881Speter              || (strcmp(db->local_abspath, eb->target_abspath) == 0
2734251881Speter                  && eb->requested_depth > db->ambient_depth))
2735251881Speter            {
2736251881Speter              db->ambient_depth = eb->requested_depth;
2737251881Speter            }
2738251881Speter        }
2739251881Speter
2740251881Speter      /* Do we have new properties to install? Or shall we simply retain
2741251881Speter         the prior set of properties? If we're installing new properties,
2742251881Speter         then we also want to write them to an old-style props file.  */
2743251881Speter      props = new_base_props;
2744251881Speter      if (props == NULL)
2745251881Speter        props = base_props;
2746251881Speter
2747251881Speter      if (conflict_skel)
2748251881Speter        {
2749251881Speter          svn_skel_t *work_item;
2750251881Speter
2751251881Speter          SVN_ERR(complete_conflict(conflict_skel,
2752251881Speter                                    db->edit_baton,
2753251881Speter                                    db->local_abspath,
2754251881Speter                                    db->old_repos_relpath,
2755251881Speter                                    db->old_revision,
2756289180Speter                                    db->new_repos_relpath,
2757251881Speter                                    svn_node_dir, svn_node_dir,
2758289180Speter                                    (db->parent_baton
2759289180Speter                                     && db->parent_baton->deletion_conflicts)
2760289180Speter                                      ? svn_hash_gets(
2761289180Speter                                            db->parent_baton->deletion_conflicts,
2762289180Speter                                            db->name)
2763289180Speter                                      : NULL,
2764251881Speter                                    db->pool, scratch_pool));
2765251881Speter
2766251881Speter          SVN_ERR(svn_wc__conflict_create_markers(&work_item,
2767251881Speter                                                  eb->db, db->local_abspath,
2768251881Speter                                                  conflict_skel,
2769251881Speter                                                  scratch_pool, scratch_pool));
2770251881Speter
2771251881Speter          all_work_items = svn_wc__wq_merge(all_work_items, work_item,
2772251881Speter                                            scratch_pool);
2773251881Speter        }
2774251881Speter
2775251881Speter      /* Any inherited props to be set set for this base node? */
2776251881Speter      if (eb->wcroot_iprops)
2777251881Speter        {
2778251881Speter          iprops = svn_hash_gets(eb->wcroot_iprops, db->local_abspath);
2779251881Speter
2780251881Speter          /* close_edit may also update iprops for switched nodes, catching
2781251881Speter             those for which close_directory is never called (e.g. a switch
2782251881Speter             with no changes).  So as a minor optimization we remove any
2783251881Speter             iprops from the hash so as not to set them again in
2784251881Speter             close_edit. */
2785251881Speter          if (iprops)
2786251881Speter            svn_hash_sets(eb->wcroot_iprops, db->local_abspath, NULL);
2787251881Speter        }
2788251881Speter
2789251881Speter      /* Update the BASE data for the directory and mark the directory
2790251881Speter         complete */
2791251881Speter      SVN_ERR(svn_wc__db_base_add_directory(
2792251881Speter                eb->db, db->local_abspath,
2793251881Speter                eb->wcroot_abspath,
2794289180Speter                db->new_repos_relpath,
2795251881Speter                eb->repos_root, eb->repos_uuid,
2796251881Speter                *eb->target_revision,
2797251881Speter                props,
2798251881Speter                db->changed_rev, db->changed_date, db->changed_author,
2799251881Speter                NULL /* children */,
2800251881Speter                db->ambient_depth,
2801251881Speter                (dav_prop_changes->nelts > 0)
2802251881Speter                    ? svn_prop_array_to_hash(dav_prop_changes, pool)
2803251881Speter                    : NULL,
2804251881Speter                (! db->shadowed) && new_base_props != NULL,
2805289180Speter                new_actual_props, iprops,
2806289180Speter                conflict_skel, all_work_items,
2807251881Speter                scratch_pool));
2808251881Speter    }
2809251881Speter
2810251881Speter  /* Process all of the queued work items for this directory.  */
2811251881Speter  SVN_ERR(svn_wc__wq_run(eb->db, db->local_abspath,
2812251881Speter                         eb->cancel_func, eb->cancel_baton,
2813251881Speter                         scratch_pool));
2814251881Speter
2815289180Speter  if (db->parent_baton)
2816289180Speter    svn_hash_sets(db->parent_baton->not_present_nodes, db->name, NULL);
2817289180Speter
2818251881Speter  if (conflict_skel && eb->conflict_func)
2819251881Speter    SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, db->local_abspath,
2820289180Speter                                             svn_node_dir,
2821251881Speter                                             conflict_skel,
2822251881Speter                                             NULL /* merge_options */,
2823251881Speter                                             eb->conflict_func,
2824251881Speter                                             eb->conflict_baton,
2825251881Speter                                             eb->cancel_func,
2826253734Speter                                             eb->cancel_baton,
2827251881Speter                                             scratch_pool));
2828251881Speter
2829251881Speter  /* Notify of any prop changes on this directory -- but do nothing if
2830251881Speter     it's an added or skipped directory, because notification has already
2831251881Speter     happened in that case - unless the add was obstructed by a dir
2832251881Speter     scheduled for addition without history, in which case we handle
2833251881Speter     notification here). */
2834251881Speter  if (!db->already_notified && eb->notify_func && db->edited)
2835251881Speter    {
2836251881Speter      svn_wc_notify_t *notify;
2837251881Speter      svn_wc_notify_action_t action;
2838251881Speter
2839251881Speter      if (db->shadowed || db->edit_obstructed)
2840251881Speter        action = svn_wc_notify_update_shadowed_update;
2841251881Speter      else if (db->obstruction_found || db->add_existed)
2842251881Speter        action = svn_wc_notify_exists;
2843251881Speter      else
2844251881Speter        action = svn_wc_notify_update_update;
2845251881Speter
2846251881Speter      notify = svn_wc_create_notify(db->local_abspath, action, pool);
2847251881Speter      notify->kind = svn_node_dir;
2848251881Speter      notify->prop_state = prop_state;
2849251881Speter      notify->revision = *eb->target_revision;
2850251881Speter      notify->old_revision = db->old_revision;
2851251881Speter
2852251881Speter      eb->notify_func(eb->notify_baton, notify, scratch_pool);
2853251881Speter    }
2854251881Speter
2855289180Speter  if (db->edited)
2856289180Speter    eb->edited = db->edited;
2857289180Speter
2858251881Speter  /* We're done with this directory, so remove one reference from the
2859251881Speter     bump information. */
2860251881Speter  SVN_ERR(maybe_release_dir_info(db));
2861251881Speter
2862251881Speter  return SVN_NO_ERROR;
2863251881Speter}
2864251881Speter
2865251881Speter
2866251881Speter/* Common code for 'absent_file' and 'absent_directory'. */
2867251881Speterstatic svn_error_t *
2868251881Speterabsent_node(const char *path,
2869251881Speter            svn_node_kind_t absent_kind,
2870251881Speter            void *parent_baton,
2871251881Speter            apr_pool_t *pool)
2872251881Speter{
2873251881Speter  struct dir_baton *pb = parent_baton;
2874251881Speter  struct edit_baton *eb = pb->edit_baton;
2875251881Speter  apr_pool_t *scratch_pool = svn_pool_create(pool);
2876251881Speter  const char *name = svn_dirent_basename(path, NULL);
2877251881Speter  const char *local_abspath;
2878251881Speter  svn_error_t *err;
2879251881Speter  svn_wc__db_status_t status;
2880251881Speter  svn_node_kind_t kind;
2881289180Speter  svn_skel_t *tree_conflict = NULL;
2882251881Speter
2883251881Speter  if (pb->skip_this)
2884251881Speter    return SVN_NO_ERROR;
2885251881Speter
2886251881Speter  local_abspath = svn_dirent_join(pb->local_abspath, name, scratch_pool);
2887251881Speter  /* If an item by this name is scheduled for addition that's a
2888251881Speter     genuine tree-conflict.  */
2889251881Speter  err = svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
2890251881Speter                             NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
2891251881Speter                             NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
2892251881Speter                             NULL, NULL, NULL, NULL,
2893251881Speter                             eb->db, local_abspath,
2894251881Speter                             scratch_pool, scratch_pool);
2895251881Speter
2896251881Speter  if (err)
2897251881Speter    {
2898251881Speter      if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
2899251881Speter        return svn_error_trace(err);
2900251881Speter
2901251881Speter      svn_error_clear(err);
2902251881Speter      status = svn_wc__db_status_not_present;
2903251881Speter      kind = svn_node_unknown;
2904251881Speter    }
2905251881Speter
2906309511Speter  if (status != svn_wc__db_status_server_excluded)
2907309511Speter    SVN_ERR(mark_directory_edited(pb, scratch_pool));
2908309511Speter  /* Else fall through as we should update the revision anyway */
2909309511Speter
2910257936Speter  if (status == svn_wc__db_status_normal)
2911251881Speter    {
2912257936Speter      svn_boolean_t wcroot;
2913257936Speter      /* We found an obstructing working copy or a file external! */
2914251881Speter
2915257936Speter      SVN_ERR(svn_wc__db_is_wcroot(&wcroot, eb->db, local_abspath,
2916257936Speter                                   scratch_pool));
2917251881Speter
2918257936Speter      if (wcroot)
2919257936Speter        {
2920257936Speter          /*
2921257936Speter             We have an obstructing working copy; possibly a directory external
2922257936Speter
2923257936Speter             We can do two things now:
2924257936Speter             1) notify the user, record a skip, etc.
2925257936Speter             2) Just record the absent node in BASE in the parent
2926257936Speter                working copy.
2927257936Speter
2928257936Speter             As option 2 happens to be exactly what we do anyway, fall through.
2929257936Speter           */
2930257936Speter        }
2931257936Speter      else
2932257936Speter        {
2933309511Speter          svn_boolean_t file_external;
2934309511Speter          svn_revnum_t revnum;
2935257936Speter
2936309511Speter          SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &revnum, NULL, NULL,
2937309511Speter                                           NULL, NULL, NULL, NULL, NULL, NULL,
2938309511Speter                                           NULL, NULL, NULL, NULL,
2939309511Speter                                           &file_external,
2940309511Speter                                           eb->db, local_abspath,
2941309511Speter                                           scratch_pool, scratch_pool));
2942257936Speter
2943309511Speter          if (file_external)
2944309511Speter            {
2945309511Speter              /* The server asks us to replace a file external
2946309511Speter                 (Existing BASE node; not reported by the working copy crawler
2947309511Speter                  or there would have been a delete_entry() call.
2948257936Speter
2949309511Speter                 There is no way we can store this state in the working copy as
2950309511Speter                 the BASE layer is already filled.
2951309511Speter                 We could error out, but that is not helping anybody; the user is not
2952309511Speter                 even seeing with what the file external would be replaced, so let's
2953309511Speter                 report a skip and continue the update.
2954309511Speter               */
2955309511Speter
2956309511Speter              if (eb->notify_func)
2957309511Speter                {
2958309511Speter                  svn_wc_notify_t *notify;
2959309511Speter                  notify = svn_wc_create_notify(
2960257936Speter                                    local_abspath,
2961257936Speter                                    svn_wc_notify_update_skip_obstruction,
2962257936Speter                                    scratch_pool);
2963257936Speter
2964309511Speter                  eb->notify_func(eb->notify_baton, notify, scratch_pool);
2965309511Speter                }
2966309511Speter
2967309511Speter              svn_pool_destroy(scratch_pool);
2968309511Speter              return SVN_NO_ERROR;
2969257936Speter            }
2970309511Speter          else
2971309511Speter            {
2972309511Speter              /* We have a normal local node that will now be hidden for the
2973309511Speter                 user. Let's try to delete what is there. This may introduce
2974309511Speter                 tree conflicts if there are local changes */
2975309511Speter              SVN_ERR(delete_entry(path, revnum, pb, scratch_pool));
2976257936Speter
2977309511Speter              /* delete_entry() promises that BASE is empty after the operation,
2978309511Speter                 so we can just fall through now */
2979309511Speter            }
2980257936Speter        }
2981251881Speter    }
2982251881Speter  else if (status == svn_wc__db_status_not_present
2983251881Speter           || status == svn_wc__db_status_server_excluded
2984251881Speter           || status == svn_wc__db_status_excluded)
2985251881Speter    {
2986251881Speter      /* The BASE node is not actually there, so we can safely turn it into
2987251881Speter         an absent node */
2988251881Speter    }
2989251881Speter  else
2990251881Speter    {
2991251881Speter      /* We have a local addition. If this would be a BASE node it would have
2992251881Speter         been deleted before we get here. (Which might have turned it into
2993289180Speter         a copy). */
2994251881Speter      SVN_ERR_ASSERT(status != svn_wc__db_status_normal);
2995251881Speter
2996289180Speter      if (!pb->shadowed && !pb->edit_obstructed)
2997289180Speter        SVN_ERR(check_tree_conflict(&tree_conflict, eb, local_abspath,
2998289180Speter                                    status, FALSE, svn_node_unknown,
2999289180Speter                                    svn_wc_conflict_action_add,
3000289180Speter                                    scratch_pool, scratch_pool));
3001289180Speter
3002251881Speter    }
3003251881Speter
3004251881Speter  {
3005251881Speter    const char *repos_relpath;
3006289180Speter    repos_relpath = svn_relpath_join(pb->new_repos_relpath, name, scratch_pool);
3007251881Speter
3008289180Speter    if (tree_conflict)
3009289180Speter      SVN_ERR(complete_conflict(tree_conflict, eb, local_abspath,
3010289180Speter                                NULL, SVN_INVALID_REVNUM, repos_relpath,
3011289180Speter                                kind, svn_node_unknown, NULL,
3012289180Speter                                scratch_pool, scratch_pool));
3013289180Speter
3014251881Speter    /* Insert an excluded node below the parent node to note that this child
3015251881Speter       is absent. (This puts it in the parent db if the child is obstructed) */
3016251881Speter    SVN_ERR(svn_wc__db_base_add_excluded_node(eb->db, local_abspath,
3017251881Speter                                              repos_relpath, eb->repos_root,
3018251881Speter                                              eb->repos_uuid,
3019251881Speter                                              *(eb->target_revision),
3020251881Speter                                              absent_kind,
3021251881Speter                                              svn_wc__db_status_server_excluded,
3022289180Speter                                              tree_conflict, NULL,
3023251881Speter                                              scratch_pool));
3024289180Speter
3025289180Speter    if (tree_conflict)
3026289180Speter      {
3027289180Speter        if (eb->conflict_func)
3028289180Speter          SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, local_abspath,
3029289180Speter                                                   kind,
3030289180Speter                                                   tree_conflict,
3031289180Speter                                                   NULL /* merge_options */,
3032289180Speter                                                   eb->conflict_func,
3033289180Speter                                                   eb->conflict_baton,
3034289180Speter                                                   eb->cancel_func,
3035289180Speter                                                   eb->cancel_baton,
3036289180Speter                                                   scratch_pool));
3037289180Speter        do_notification(eb, local_abspath, kind, svn_wc_notify_tree_conflict,
3038289180Speter                        scratch_pool);
3039289180Speter      }
3040251881Speter  }
3041251881Speter
3042251881Speter  svn_pool_destroy(scratch_pool);
3043251881Speter
3044251881Speter  return SVN_NO_ERROR;
3045251881Speter}
3046251881Speter
3047251881Speter
3048251881Speter/* An svn_delta_editor_t function. */
3049251881Speterstatic svn_error_t *
3050251881Speterabsent_file(const char *path,
3051251881Speter            void *parent_baton,
3052251881Speter            apr_pool_t *pool)
3053251881Speter{
3054251881Speter  return absent_node(path, svn_node_file, parent_baton, pool);
3055251881Speter}
3056251881Speter
3057251881Speter
3058251881Speter/* An svn_delta_editor_t function. */
3059251881Speterstatic svn_error_t *
3060251881Speterabsent_directory(const char *path,
3061251881Speter                 void *parent_baton,
3062251881Speter                 apr_pool_t *pool)
3063251881Speter{
3064251881Speter  return absent_node(path, svn_node_dir, parent_baton, pool);
3065251881Speter}
3066251881Speter
3067251881Speter
3068251881Speter/* An svn_delta_editor_t function. */
3069251881Speterstatic svn_error_t *
3070251881Speteradd_file(const char *path,
3071251881Speter         void *parent_baton,
3072251881Speter         const char *copyfrom_path,
3073251881Speter         svn_revnum_t copyfrom_rev,
3074251881Speter         apr_pool_t *pool,
3075251881Speter         void **file_baton)
3076251881Speter{
3077251881Speter  struct dir_baton *pb = parent_baton;
3078251881Speter  struct edit_baton *eb = pb->edit_baton;
3079251881Speter  struct file_baton *fb;
3080289180Speter  svn_node_kind_t kind;
3081289180Speter  svn_node_kind_t wc_kind;
3082289180Speter  svn_wc__db_status_t status;
3083251881Speter  apr_pool_t *scratch_pool;
3084289180Speter  svn_boolean_t conflicted;
3085251881Speter  svn_boolean_t conflict_ignored = FALSE;
3086289180Speter  svn_boolean_t versioned_locally_and_present;
3087251881Speter  svn_skel_t *tree_conflict = NULL;
3088251881Speter  svn_error_t *err = SVN_NO_ERROR;
3089251881Speter
3090251881Speter  SVN_ERR_ASSERT(! (copyfrom_path || SVN_IS_VALID_REVNUM(copyfrom_rev)));
3091251881Speter
3092251881Speter  SVN_ERR(make_file_baton(&fb, pb, path, TRUE, pool));
3093251881Speter  *file_baton = fb;
3094251881Speter
3095251881Speter  if (fb->skip_this)
3096251881Speter    return SVN_NO_ERROR;
3097251881Speter
3098289180Speter  SVN_ERR(calculate_repos_relpath(&fb->new_repos_relpath, fb->local_abspath,
3099289180Speter                                  NULL, eb, pb, fb->pool, pool));
3100251881Speter  SVN_ERR(mark_file_edited(fb, pool));
3101251881Speter
3102251881Speter  /* The file_pool can stick around for a *long* time, so we want to
3103251881Speter     use a subpool for any temporary allocations. */
3104251881Speter  scratch_pool = svn_pool_create(pool);
3105251881Speter
3106251881Speter
3107251881Speter  /* It may not be named the same as the administrative directory. */
3108251881Speter  if (svn_wc_is_adm_dir(fb->name, pool))
3109251881Speter    return svn_error_createf(
3110251881Speter       SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
3111251881Speter       _("Failed to add file '%s': object of the same name as the "
3112251881Speter         "administrative directory"),
3113251881Speter       svn_dirent_local_style(fb->local_abspath, pool));
3114251881Speter
3115251881Speter  if (!eb->clean_checkout)
3116251881Speter    {
3117251881Speter      SVN_ERR(svn_io_check_path(fb->local_abspath, &kind, scratch_pool));
3118251881Speter
3119251881Speter      err = svn_wc__db_read_info(&status, &wc_kind, NULL, NULL, NULL, NULL, NULL,
3120251881Speter                                NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3121251881Speter                                NULL, NULL, NULL, NULL, NULL,
3122251881Speter                                &conflicted, NULL, NULL, NULL, NULL, NULL, NULL,
3123251881Speter                                eb->db, fb->local_abspath,
3124251881Speter                                scratch_pool, scratch_pool);
3125251881Speter    }
3126289180Speter  else
3127289180Speter    {
3128289180Speter      kind =  svn_node_none;
3129289180Speter      status = svn_wc__db_status_not_present;
3130289180Speter      wc_kind = svn_node_unknown;
3131289180Speter      conflicted = FALSE;
3132289180Speter    }
3133251881Speter
3134251881Speter  if (err)
3135251881Speter    {
3136251881Speter      if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
3137251881Speter        return svn_error_trace(err);
3138251881Speter
3139251881Speter      svn_error_clear(err);
3140251881Speter      wc_kind = svn_node_unknown;
3141251881Speter      conflicted = FALSE;
3142251881Speter
3143251881Speter      versioned_locally_and_present = FALSE;
3144251881Speter    }
3145289180Speter  else if (status == svn_wc__db_status_normal && wc_kind == svn_node_unknown)
3146251881Speter    {
3147289180Speter      SVN_ERR_ASSERT(conflicted);
3148289180Speter      versioned_locally_and_present = FALSE; /* Tree conflict ACTUAL-only node */
3149289180Speter    }
3150289180Speter  else if (status == svn_wc__db_status_normal
3151289180Speter           || status == svn_wc__db_status_incomplete)
3152289180Speter    {
3153289180Speter      svn_boolean_t root;
3154251881Speter
3155289180Speter      SVN_ERR(svn_wc__db_is_wcroot(&root, eb->db, fb->local_abspath,
3156289180Speter                                   scratch_pool));
3157251881Speter
3158289180Speter      if (root)
3159289180Speter        {
3160289180Speter          /* !! We found the root of a working copy obstructing the wc !!
3161251881Speter
3162289180Speter             If the directory would be part of our own working copy then
3163289180Speter             we wouldn't have been called as an add_directory().
3164251881Speter
3165289180Speter             The only thing we can do is add a not-present node, to allow
3166289180Speter             a future update to bring in the new files when the problem is
3167289180Speter             resolved.  Note that svn_wc__db_base_add_not_present_node()
3168289180Speter             explicitly adds the node into the parent's node database. */
3169251881Speter
3170289180Speter          svn_hash_sets(pb->not_present_nodes,
3171289180Speter                        apr_pstrdup(pb->pool, fb->name),
3172289180Speter                        svn_node_kind_to_word(svn_node_dir));
3173289180Speter        }
3174289180Speter      else if (wc_kind == svn_node_dir)
3175289180Speter        {
3176289180Speter          /* We have an editor violation. Github sometimes does this
3177289180Speter             in its subversion compatibility code, when changing the
3178289180Speter             depth of a working copy, or on updates from incomplete */
3179289180Speter        }
3180289180Speter      else
3181289180Speter        {
3182289180Speter          /* We found a file external occupating the place we need in BASE.
3183251881Speter
3184289180Speter             We can't add a not-present node in this case as that would overwrite
3185289180Speter             the file external. Luckily the file external itself stops us from
3186289180Speter             forgetting a child of this parent directory like an obstructing
3187289180Speter             working copy would.
3188251881Speter
3189289180Speter             The reason we get here is that the adm crawler doesn't report
3190289180Speter             file externals.
3191289180Speter           */
3192289180Speter          SVN_ERR_ASSERT(wc_kind == svn_node_file
3193289180Speter                         || wc_kind == svn_node_symlink);
3194289180Speter        }
3195251881Speter
3196251881Speter      SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3197251881Speter      fb->skip_this = TRUE;
3198251881Speter      fb->already_notified = TRUE;
3199251881Speter
3200289180Speter      do_notification(eb, fb->local_abspath, wc_kind,
3201251881Speter                      svn_wc_notify_update_skip_obstruction, scratch_pool);
3202251881Speter
3203251881Speter      svn_pool_destroy(scratch_pool);
3204251881Speter
3205251881Speter      return SVN_NO_ERROR;
3206251881Speter    }
3207251881Speter  else
3208251881Speter    versioned_locally_and_present = IS_NODE_PRESENT(status);
3209251881Speter
3210251881Speter
3211251881Speter  /* Is this path a conflict victim? */
3212251881Speter  if (fb->shadowed)
3213251881Speter    conflicted = FALSE; /* Conflict applies to WORKING */
3214251881Speter  else if (conflicted)
3215251881Speter    {
3216251881Speter      if (pb->deletion_conflicts)
3217251881Speter        tree_conflict = svn_hash_gets(pb->deletion_conflicts, fb->name);
3218251881Speter
3219251881Speter      if (tree_conflict)
3220251881Speter        {
3221251881Speter          svn_wc_conflict_reason_t reason;
3222286506Speter          const char *move_src_op_root_abspath;
3223251881Speter          /* So this deletion wasn't just a deletion, it is actually a
3224251881Speter             replacement. Let's install a better tree conflict. */
3225251881Speter
3226286506Speter          SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL,
3227286506Speter                                                      &move_src_op_root_abspath,
3228251881Speter                                                      eb->db,
3229251881Speter                                                      fb->local_abspath,
3230251881Speter                                                      tree_conflict,
3231289180Speter                                                      fb->pool, scratch_pool));
3232251881Speter
3233251881Speter          tree_conflict = svn_wc__conflict_skel_create(fb->pool);
3234251881Speter
3235251881Speter          SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
3236251881Speter                                        tree_conflict,
3237251881Speter                                        eb->db, fb->local_abspath,
3238251881Speter                                        reason, svn_wc_conflict_action_replace,
3239286506Speter                                        move_src_op_root_abspath,
3240289180Speter                                        fb->pool, scratch_pool));
3241251881Speter
3242251881Speter          /* And now stop checking for conflicts here and just perform
3243251881Speter             a shadowed update */
3244251881Speter          fb->edit_conflict = tree_conflict; /* Cache for close_file */
3245251881Speter          tree_conflict = NULL; /* No direct notification */
3246251881Speter          fb->shadowed = TRUE; /* Just continue */
3247251881Speter          conflicted = FALSE; /* No skip */
3248251881Speter        }
3249251881Speter      else
3250251881Speter        SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
3251251881Speter                                        eb->db, fb->local_abspath, pool));
3252251881Speter    }
3253251881Speter
3254251881Speter  /* Now the usual conflict handling: skip. */
3255251881Speter  if (conflicted)
3256251881Speter    {
3257251881Speter      SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3258251881Speter
3259251881Speter      fb->skip_this = TRUE;
3260251881Speter      fb->already_notified = TRUE;
3261251881Speter
3262251881Speter      /* We skip this node, but once the update completes the parent node will
3263251881Speter         be updated to the new revision. So a future recursive update of the
3264251881Speter         parent will not bring in this new node as the revision of the parent
3265251881Speter         describes to the repository that all children are available.
3266251881Speter
3267251881Speter         To resolve this problem, we add a not-present node to allow bringing
3268251881Speter         the node in once this conflict is resolved.
3269251881Speter
3270251881Speter         Note that we can safely assume that no present base node exists,
3271251881Speter         because then we would not have received an add_file.
3272251881Speter       */
3273289180Speter      svn_hash_sets(pb->not_present_nodes, apr_pstrdup(pb->pool, fb->name),
3274289180Speter                    svn_node_kind_to_word(svn_node_file));
3275251881Speter
3276289180Speter      do_notification(eb, fb->local_abspath, svn_node_file,
3277251881Speter                      svn_wc_notify_skip_conflicted, scratch_pool);
3278251881Speter
3279251881Speter      svn_pool_destroy(scratch_pool);
3280251881Speter
3281251881Speter      return SVN_NO_ERROR;
3282251881Speter    }
3283251881Speter  else if (conflict_ignored)
3284251881Speter    {
3285251881Speter      fb->shadowed = TRUE;
3286251881Speter    }
3287251881Speter
3288251881Speter  if (fb->shadowed)
3289251881Speter    {
3290251881Speter      /* Nothing to check; does not and will not exist in working copy */
3291251881Speter    }
3292251881Speter  else if (versioned_locally_and_present)
3293251881Speter    {
3294251881Speter      /* What to do with a versioned or schedule-add file:
3295251881Speter
3296251881Speter         If the UUID doesn't match the parent's, or the URL isn't a child of
3297251881Speter         the parent dir's URL, it's an error.
3298251881Speter
3299251881Speter         Set add_existed so that user notification is delayed until after any
3300251881Speter         text or prop conflicts have been found.
3301251881Speter
3302251881Speter         Whether the incoming add is a symlink or a file will only be known in
3303251881Speter         close_file(), when the props are known. So with a locally added file
3304251881Speter         or symlink, let close_file() check for a tree conflict.
3305251881Speter
3306251881Speter         We will never see missing files here, because these would be
3307251881Speter         re-added during the crawler phase. */
3308251881Speter      svn_boolean_t local_is_file;
3309251881Speter
3310251881Speter      /* Is the local node a copy or move */
3311251881Speter      if (status == svn_wc__db_status_added)
3312251881Speter        SVN_ERR(svn_wc__db_scan_addition(&status, NULL, NULL, NULL, NULL, NULL,
3313251881Speter                                         NULL, NULL, NULL,
3314251881Speter                                         eb->db, fb->local_abspath,
3315251881Speter                                         scratch_pool, scratch_pool));
3316251881Speter
3317251881Speter      /* Is there something that is a file? */
3318251881Speter      local_is_file = (wc_kind == svn_node_file
3319251881Speter                       || wc_kind == svn_node_symlink);
3320251881Speter
3321251881Speter      /* Do tree conflict checking if
3322251881Speter       *  - if there is a local copy.
3323251881Speter       *  - if this is a switch operation
3324251881Speter       *  - the node kinds mismatch
3325251881Speter       *
3326251881Speter       * During switch, local adds at the same path as incoming adds get
3327251881Speter       * "lost" in that switching back to the original will no longer have the
3328251881Speter       * local add. So switch always alerts the user with a tree conflict. */
3329251881Speter      if (!eb->adds_as_modification
3330251881Speter          || !local_is_file
3331251881Speter          || status != svn_wc__db_status_added)
3332251881Speter        {
3333251881Speter          SVN_ERR(check_tree_conflict(&tree_conflict, eb,
3334251881Speter                                      fb->local_abspath,
3335251881Speter                                      status, FALSE, svn_node_none,
3336251881Speter                                      svn_wc_conflict_action_add,
3337289180Speter                                      fb->pool, scratch_pool));
3338251881Speter        }
3339251881Speter
3340251881Speter      if (tree_conflict == NULL)
3341251881Speter        fb->add_existed = TRUE; /* Take over WORKING */
3342251881Speter      else
3343251881Speter        fb->shadowed = TRUE; /* Only update BASE */
3344251881Speter
3345251881Speter    }
3346251881Speter  else if (kind != svn_node_none)
3347251881Speter    {
3348251881Speter      /* There's an unversioned node at this path. */
3349251881Speter      fb->obstruction_found = TRUE;
3350251881Speter
3351251881Speter      /* Unversioned, obstructing files are handled by text merge/conflict,
3352251881Speter       * if unversioned obstructions are allowed. */
3353251881Speter      if (! (kind == svn_node_file && eb->allow_unver_obstructions))
3354251881Speter        {
3355251881Speter          /* Bring in the node as deleted */ /* ### Obstructed Conflict */
3356251881Speter          fb->shadowed = TRUE;
3357251881Speter
3358251881Speter          /* Mark a conflict */
3359251881Speter          tree_conflict = svn_wc__conflict_skel_create(fb->pool);
3360251881Speter
3361251881Speter          SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
3362251881Speter                                        tree_conflict,
3363251881Speter                                        eb->db, fb->local_abspath,
3364251881Speter                                        svn_wc_conflict_reason_unversioned,
3365251881Speter                                        svn_wc_conflict_action_add,
3366251881Speter                                        NULL,
3367251881Speter                                        fb->pool, scratch_pool));
3368251881Speter        }
3369251881Speter    }
3370251881Speter
3371251881Speter  /* When this is not the update target add a not-present BASE node now,
3372251881Speter     to allow marking the parent directory complete in its close_edit() call.
3373251881Speter     This resolves issues when that occurs before the close_file(). */
3374251881Speter  if (pb->parent_baton
3375251881Speter      || *eb->target_basename == '\0'
3376251881Speter      || (strcmp(fb->local_abspath, eb->target_abspath) != 0))
3377251881Speter    {
3378289180Speter      svn_hash_sets(pb->not_present_nodes, apr_pstrdup(pb->pool, fb->name),
3379289180Speter                    svn_node_kind_to_word(svn_node_file));
3380251881Speter    }
3381251881Speter
3382251881Speter  if (tree_conflict != NULL)
3383251881Speter    {
3384251881Speter      SVN_ERR(complete_conflict(tree_conflict,
3385251881Speter                                fb->edit_baton,
3386251881Speter                                fb->local_abspath,
3387251881Speter                                fb->old_repos_relpath,
3388251881Speter                                fb->old_revision,
3389289180Speter                                fb->new_repos_relpath,
3390289180Speter                                wc_kind, svn_node_file,
3391289180Speter                                pb->deletion_conflicts
3392289180Speter                                  ? svn_hash_gets(pb->deletion_conflicts,
3393289180Speter                                                  fb->name)
3394289180Speter                                  : NULL,
3395251881Speter                                fb->pool, scratch_pool));
3396251881Speter
3397251881Speter      SVN_ERR(svn_wc__db_op_mark_conflict(eb->db,
3398251881Speter                                          fb->local_abspath,
3399251881Speter                                          tree_conflict, NULL,
3400251881Speter                                          scratch_pool));
3401251881Speter
3402289180Speter      fb->edit_conflict = tree_conflict;
3403253734Speter
3404251881Speter      fb->already_notified = TRUE;
3405251881Speter      do_notification(eb, fb->local_abspath, svn_node_file,
3406251881Speter                      svn_wc_notify_tree_conflict, scratch_pool);
3407251881Speter    }
3408251881Speter
3409251881Speter  svn_pool_destroy(scratch_pool);
3410251881Speter
3411251881Speter  return SVN_NO_ERROR;
3412251881Speter}
3413251881Speter
3414251881Speter
3415251881Speter/* An svn_delta_editor_t function. */
3416251881Speterstatic svn_error_t *
3417251881Speteropen_file(const char *path,
3418251881Speter          void *parent_baton,
3419251881Speter          svn_revnum_t base_revision,
3420251881Speter          apr_pool_t *pool,
3421251881Speter          void **file_baton)
3422251881Speter{
3423251881Speter  struct dir_baton *pb = parent_baton;
3424251881Speter  struct edit_baton *eb = pb->edit_baton;
3425251881Speter  struct file_baton *fb;
3426251881Speter  svn_boolean_t conflicted;
3427251881Speter  svn_boolean_t conflict_ignored = FALSE;
3428251881Speter  svn_boolean_t have_work;
3429251881Speter  svn_wc__db_status_t status;
3430251881Speter  svn_node_kind_t wc_kind;
3431251881Speter  svn_skel_t *tree_conflict = NULL;
3432251881Speter
3433251881Speter  /* the file_pool can stick around for a *long* time, so we want to use
3434251881Speter     a subpool for any temporary allocations. */
3435251881Speter  apr_pool_t *scratch_pool = svn_pool_create(pool);
3436251881Speter
3437251881Speter  SVN_ERR(make_file_baton(&fb, pb, path, FALSE, pool));
3438251881Speter  *file_baton = fb;
3439251881Speter
3440251881Speter  if (fb->skip_this)
3441251881Speter    return SVN_NO_ERROR;
3442251881Speter
3443251881Speter  /* Detect obstructing working copies */
3444251881Speter  {
3445251881Speter    svn_boolean_t is_root;
3446251881Speter
3447251881Speter    SVN_ERR(svn_wc__db_is_wcroot(&is_root, eb->db, fb->local_abspath,
3448251881Speter                                 pool));
3449251881Speter
3450251881Speter    if (is_root)
3451251881Speter      {
3452251881Speter        /* Just skip this node; a future update will handle it */
3453251881Speter        SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3454251881Speter        fb->skip_this = TRUE;
3455251881Speter        fb->already_notified = TRUE;
3456251881Speter
3457251881Speter        do_notification(eb, fb->local_abspath, svn_node_file,
3458251881Speter                        svn_wc_notify_update_skip_obstruction, pool);
3459251881Speter
3460251881Speter        return SVN_NO_ERROR;
3461251881Speter      }
3462251881Speter  }
3463251881Speter
3464251881Speter  /* Sanity check. */
3465251881Speter
3466251881Speter  SVN_ERR(svn_wc__db_read_info(&status, &wc_kind, &fb->old_revision,
3467251881Speter                               &fb->old_repos_relpath, NULL, NULL,
3468251881Speter                               &fb->changed_rev, &fb->changed_date,
3469251881Speter                               &fb->changed_author, NULL,
3470251881Speter                               &fb->original_checksum, NULL, NULL, NULL,
3471251881Speter                               NULL, NULL, NULL, NULL, NULL, NULL,
3472251881Speter                               &conflicted, NULL, NULL, &fb->local_prop_mods,
3473251881Speter                               NULL, NULL, &have_work,
3474251881Speter                               eb->db, fb->local_abspath,
3475251881Speter                               fb->pool, scratch_pool));
3476251881Speter
3477251881Speter  if (have_work)
3478251881Speter    SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, &fb->old_revision,
3479251881Speter                                     &fb->old_repos_relpath, NULL, NULL,
3480251881Speter                                     &fb->changed_rev, &fb->changed_date,
3481251881Speter                                     &fb->changed_author, NULL,
3482251881Speter                                     &fb->original_checksum, NULL, NULL,
3483251881Speter                                     NULL, NULL, NULL,
3484251881Speter                                     eb->db, fb->local_abspath,
3485251881Speter                                     fb->pool, scratch_pool));
3486251881Speter
3487289180Speter  SVN_ERR(calculate_repos_relpath(&fb->new_repos_relpath, fb->local_abspath,
3488289180Speter                                  fb->old_repos_relpath, eb, pb,
3489289180Speter                                  fb->pool, scratch_pool));
3490289180Speter
3491251881Speter  /* Is this path a conflict victim? */
3492251881Speter  if (fb->shadowed)
3493251881Speter    conflicted = FALSE; /* Conflict applies to WORKING */
3494251881Speter  else if (conflicted)
3495251881Speter    SVN_ERR(node_already_conflicted(&conflicted, &conflict_ignored,
3496251881Speter                                    eb->db, fb->local_abspath, pool));
3497251881Speter  if (conflicted)
3498251881Speter    {
3499251881Speter      SVN_ERR(remember_skipped_tree(eb, fb->local_abspath, pool));
3500251881Speter
3501251881Speter      fb->skip_this = TRUE;
3502251881Speter      fb->already_notified = TRUE;
3503251881Speter
3504251881Speter      do_notification(eb, fb->local_abspath, svn_node_unknown,
3505251881Speter                      svn_wc_notify_skip_conflicted, scratch_pool);
3506251881Speter
3507251881Speter      svn_pool_destroy(scratch_pool);
3508251881Speter
3509251881Speter      return SVN_NO_ERROR;
3510251881Speter    }
3511251881Speter  else if (conflict_ignored)
3512251881Speter    {
3513251881Speter      fb->shadowed = TRUE;
3514251881Speter    }
3515251881Speter
3516251881Speter  /* Check for conflicts only when we haven't already recorded
3517251881Speter   * a tree-conflict on a parent node. */
3518251881Speter  if (!fb->shadowed)
3519251881Speter    SVN_ERR(check_tree_conflict(&tree_conflict, eb, fb->local_abspath,
3520251881Speter                                status, TRUE, svn_node_file,
3521251881Speter                                svn_wc_conflict_action_edit,
3522251881Speter                                fb->pool, scratch_pool));
3523251881Speter
3524251881Speter  /* Is this path the victim of a newly-discovered tree conflict? */
3525251881Speter  if (tree_conflict != NULL)
3526251881Speter    {
3527251881Speter      svn_wc_conflict_reason_t reason;
3528251881Speter      fb->edit_conflict = tree_conflict;
3529251881Speter      /* Other modifications wouldn't be a tree conflict */
3530251881Speter
3531251881Speter      SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, NULL, NULL,
3532251881Speter                                                  eb->db, fb->local_abspath,
3533251881Speter                                                  tree_conflict,
3534251881Speter                                                  scratch_pool, scratch_pool));
3535251881Speter      SVN_ERR_ASSERT(reason == svn_wc_conflict_reason_deleted
3536251881Speter                     || reason == svn_wc_conflict_reason_moved_away
3537251881Speter                     || reason == svn_wc_conflict_reason_replaced
3538251881Speter                     || reason == svn_wc_conflict_reason_obstructed);
3539251881Speter
3540251881Speter      /* Continue updating BASE */
3541251881Speter      if (reason == svn_wc_conflict_reason_obstructed)
3542251881Speter        fb->edit_obstructed = TRUE;
3543251881Speter      else
3544251881Speter        fb->shadowed = TRUE;
3545251881Speter    }
3546251881Speter
3547251881Speter  svn_pool_destroy(scratch_pool);
3548251881Speter
3549251881Speter  return SVN_NO_ERROR;
3550251881Speter}
3551251881Speter
3552251881Speter/* Implements svn_stream_lazyopen_func_t. */
3553251881Speterstatic svn_error_t *
3554251881Speterlazy_open_source(svn_stream_t **stream,
3555251881Speter                 void *baton,
3556251881Speter                 apr_pool_t *result_pool,
3557251881Speter                 apr_pool_t *scratch_pool)
3558251881Speter{
3559251881Speter  struct file_baton *fb = baton;
3560251881Speter
3561251881Speter  SVN_ERR(svn_wc__db_pristine_read(stream, NULL, fb->edit_baton->db,
3562251881Speter                                   fb->local_abspath,
3563251881Speter                                   fb->original_checksum,
3564251881Speter                                   result_pool, scratch_pool));
3565251881Speter
3566251881Speter
3567251881Speter  return SVN_NO_ERROR;
3568251881Speter}
3569251881Speter
3570251881Speter/* Implements svn_stream_lazyopen_func_t. */
3571251881Speterstatic svn_error_t *
3572251881Speterlazy_open_target(svn_stream_t **stream,
3573251881Speter                 void *baton,
3574251881Speter                 apr_pool_t *result_pool,
3575251881Speter                 apr_pool_t *scratch_pool)
3576251881Speter{
3577289180Speter  struct handler_baton *hb = baton;
3578289180Speter  svn_wc__db_install_data_t *install_data;
3579251881Speter
3580289180Speter  /* By convention return value is undefined on error, but we rely
3581289180Speter     on HB->INSTALL_DATA value in window_handler() and abort
3582289180Speter     INSTALL_STREAM if is not NULL on error.
3583289180Speter     So we store INSTALL_DATA to local variable first, to leave
3584289180Speter     HB->INSTALL_DATA unchanged on error. */
3585289180Speter  SVN_ERR(svn_wc__db_pristine_prepare_install(stream,
3586289180Speter                                              &install_data,
3587289180Speter                                              &hb->new_text_base_sha1_checksum,
3588289180Speter                                              NULL,
3589289180Speter                                              hb->fb->edit_baton->db,
3590289180Speter                                              hb->fb->dir_baton->local_abspath,
3591289180Speter                                              result_pool, scratch_pool));
3592251881Speter
3593289180Speter  hb->install_data = install_data;
3594289180Speter
3595251881Speter  return SVN_NO_ERROR;
3596251881Speter}
3597251881Speter
3598251881Speter/* An svn_delta_editor_t function. */
3599251881Speterstatic svn_error_t *
3600251881Speterapply_textdelta(void *file_baton,
3601251881Speter                const char *expected_checksum,
3602251881Speter                apr_pool_t *pool,
3603251881Speter                svn_txdelta_window_handler_t *handler,
3604251881Speter                void **handler_baton)
3605251881Speter{
3606251881Speter  struct file_baton *fb = file_baton;
3607251881Speter  apr_pool_t *handler_pool = svn_pool_create(fb->pool);
3608251881Speter  struct handler_baton *hb = apr_pcalloc(handler_pool, sizeof(*hb));
3609251881Speter  struct edit_baton *eb = fb->edit_baton;
3610251881Speter  const svn_checksum_t *recorded_base_checksum;
3611251881Speter  svn_checksum_t *expected_base_checksum;
3612251881Speter  svn_stream_t *source;
3613251881Speter  svn_stream_t *target;
3614251881Speter
3615251881Speter  if (fb->skip_this)
3616251881Speter    {
3617251881Speter      *handler = svn_delta_noop_window_handler;
3618251881Speter      *handler_baton = NULL;
3619251881Speter      return SVN_NO_ERROR;
3620251881Speter    }
3621251881Speter
3622251881Speter  SVN_ERR(mark_file_edited(fb, pool));
3623251881Speter
3624251881Speter  /* Parse checksum or sets expected_base_checksum to NULL */
3625251881Speter  SVN_ERR(svn_checksum_parse_hex(&expected_base_checksum, svn_checksum_md5,
3626251881Speter                                 expected_checksum, pool));
3627251881Speter
3628251881Speter  /* Before applying incoming svndiff data to text base, make sure
3629251881Speter     text base hasn't been corrupted, and that its checksum
3630251881Speter     matches the expected base checksum. */
3631251881Speter
3632251881Speter  /* The incoming delta is targeted against EXPECTED_BASE_CHECKSUM. Find and
3633251881Speter     check our RECORDED_BASE_CHECKSUM.  (In WC-1, we could not do this test
3634251881Speter     for replaced nodes because we didn't store the checksum of the "revert
3635251881Speter     base".  In WC-NG, we do and we can.) */
3636251881Speter  recorded_base_checksum = fb->original_checksum;
3637251881Speter
3638251881Speter  /* If we have a checksum that we want to compare to a MD5 checksum,
3639251881Speter     ensure that it is a MD5 checksum */
3640251881Speter  if (recorded_base_checksum
3641251881Speter      && expected_base_checksum
3642251881Speter      && recorded_base_checksum->kind != svn_checksum_md5)
3643251881Speter    SVN_ERR(svn_wc__db_pristine_get_md5(&recorded_base_checksum,
3644251881Speter                                        eb->db, eb->wcroot_abspath,
3645251881Speter                                        recorded_base_checksum, pool, pool));
3646251881Speter
3647251881Speter
3648251881Speter  if (!svn_checksum_match(expected_base_checksum, recorded_base_checksum))
3649251881Speter      return svn_error_createf(SVN_ERR_WC_CORRUPT_TEXT_BASE, NULL,
3650251881Speter                     _("Checksum mismatch for '%s':\n"
3651251881Speter                       "   expected:  %s\n"
3652251881Speter                       "   recorded:  %s\n"),
3653251881Speter                     svn_dirent_local_style(fb->local_abspath, pool),
3654251881Speter                     svn_checksum_to_cstring_display(expected_base_checksum,
3655251881Speter                                                     pool),
3656251881Speter                     svn_checksum_to_cstring_display(recorded_base_checksum,
3657251881Speter                                                     pool));
3658251881Speter
3659251881Speter  /* Open the text base for reading, unless this is an added file. */
3660251881Speter
3661251881Speter  /*
3662251881Speter     kff todo: what we really need to do here is:
3663251881Speter
3664251881Speter     1. See if there's a file or dir by this name already here.
3665251881Speter     2. See if it's under revision control.
3666251881Speter     3. If both are true, open text-base.
3667251881Speter     4. If only 1 is true, bail, because we can't go destroying user's
3668251881Speter        files (or as an alternative to bailing, move it to some tmp
3669251881Speter        name and somehow tell the user, but communicating with the
3670251881Speter        user without erroring is a whole callback system we haven't
3671251881Speter        finished inventing yet.)
3672251881Speter  */
3673251881Speter
3674251881Speter  if (! fb->adding_file)
3675251881Speter    {
3676251881Speter      SVN_ERR_ASSERT(!fb->original_checksum
3677251881Speter                     || fb->original_checksum->kind == svn_checksum_sha1);
3678251881Speter
3679251881Speter      source = svn_stream_lazyopen_create(lazy_open_source, fb, FALSE,
3680251881Speter                                          handler_pool);
3681251881Speter    }
3682251881Speter  else
3683251881Speter    {
3684251881Speter      source = svn_stream_empty(handler_pool);
3685251881Speter    }
3686251881Speter
3687251881Speter  /* If we don't have a recorded checksum, use the ra provided checksum */
3688251881Speter  if (!recorded_base_checksum)
3689251881Speter    recorded_base_checksum = expected_base_checksum;
3690251881Speter
3691251881Speter  /* Checksum the text base while applying deltas */
3692251881Speter  if (recorded_base_checksum)
3693251881Speter    {
3694251881Speter      hb->expected_source_checksum = svn_checksum_dup(recorded_base_checksum,
3695251881Speter                                                      handler_pool);
3696251881Speter
3697251881Speter      /* Wrap stream and store reference to allow calculating the
3698251881Speter         checksum. */
3699251881Speter      source = svn_stream_checksummed2(source,
3700251881Speter                                       &hb->actual_source_checksum,
3701251881Speter                                       NULL, recorded_base_checksum->kind,
3702251881Speter                                       TRUE, handler_pool);
3703251881Speter      hb->source_checksum_stream = source;
3704251881Speter    }
3705251881Speter
3706289180Speter  target = svn_stream_lazyopen_create(lazy_open_target, hb, TRUE, handler_pool);
3707251881Speter
3708251881Speter  /* Prepare to apply the delta.  */
3709251881Speter  svn_txdelta_apply(source, target,
3710251881Speter                    hb->new_text_base_md5_digest,
3711289180Speter                    fb->local_abspath /* error_info */,
3712251881Speter                    handler_pool,
3713251881Speter                    &hb->apply_handler, &hb->apply_baton);
3714251881Speter
3715251881Speter  hb->pool = handler_pool;
3716251881Speter  hb->fb = fb;
3717251881Speter
3718251881Speter  /* We're all set.  */
3719251881Speter  *handler_baton = hb;
3720251881Speter  *handler = window_handler;
3721251881Speter
3722251881Speter  return SVN_NO_ERROR;
3723251881Speter}
3724251881Speter
3725251881Speter
3726251881Speter/* An svn_delta_editor_t function. */
3727251881Speterstatic svn_error_t *
3728251881Speterchange_file_prop(void *file_baton,
3729251881Speter                 const char *name,
3730251881Speter                 const svn_string_t *value,
3731251881Speter                 apr_pool_t *scratch_pool)
3732251881Speter{
3733251881Speter  struct file_baton *fb = file_baton;
3734251881Speter  svn_prop_t *propchange;
3735251881Speter
3736251881Speter  if (fb->skip_this)
3737251881Speter    return SVN_NO_ERROR;
3738251881Speter
3739251881Speter  /* Push a new propchange to the file baton's array of propchanges */
3740251881Speter  propchange = apr_array_push(fb->propchanges);
3741251881Speter  propchange->name = apr_pstrdup(fb->pool, name);
3742289180Speter  propchange->value = svn_string_dup(value, fb->pool);
3743251881Speter
3744251881Speter  if (!fb->edited && svn_property_kind2(name) == svn_prop_regular_kind)
3745251881Speter    SVN_ERR(mark_file_edited(fb, scratch_pool));
3746251881Speter
3747251881Speter  if (! fb->shadowed
3748251881Speter      && strcmp(name, SVN_PROP_SPECIAL) == 0)
3749251881Speter    {
3750251881Speter      struct edit_baton *eb = fb->edit_baton;
3751251881Speter      svn_boolean_t modified = FALSE;
3752251881Speter      svn_boolean_t becomes_symlink;
3753251881Speter      svn_boolean_t was_symlink;
3754251881Speter
3755251881Speter      /* Let's see if we have a change as in some scenarios servers report
3756251881Speter         non-changes of properties. */
3757251881Speter      becomes_symlink = (value != NULL);
3758251881Speter
3759251881Speter      if (fb->adding_file)
3760251881Speter        was_symlink = becomes_symlink; /* No change */
3761251881Speter      else
3762251881Speter        {
3763251881Speter          apr_hash_t *props;
3764251881Speter
3765251881Speter          /* We read the server-props, not the ACTUAL props here as we just
3766251881Speter             want to see if this is really an incoming prop change. */
3767251881Speter          SVN_ERR(svn_wc__db_base_get_props(&props, eb->db,
3768251881Speter                                            fb->local_abspath,
3769251881Speter                                            scratch_pool, scratch_pool));
3770251881Speter
3771251881Speter          was_symlink = ((props
3772251881Speter                              && svn_hash_gets(props, SVN_PROP_SPECIAL) != NULL)
3773251881Speter                              ? svn_tristate_true
3774251881Speter                              : svn_tristate_false);
3775251881Speter        }
3776251881Speter
3777251881Speter      if (was_symlink != becomes_symlink)
3778251881Speter        {
3779251881Speter          /* If the local node was not modified, we continue as usual, if
3780251881Speter             modified we want a tree conflict just like how we would handle
3781251881Speter             it when receiving a delete + add (aka "replace") */
3782251881Speter          if (fb->local_prop_mods)
3783251881Speter            modified = TRUE;
3784251881Speter          else
3785251881Speter            SVN_ERR(svn_wc__internal_file_modified_p(&modified, eb->db,
3786251881Speter                                                     fb->local_abspath,
3787251881Speter                                                     FALSE, scratch_pool));
3788251881Speter        }
3789251881Speter
3790251881Speter      if (modified)
3791251881Speter        {
3792251881Speter          if (!fb->edit_conflict)
3793251881Speter            fb->edit_conflict = svn_wc__conflict_skel_create(fb->pool);
3794251881Speter
3795251881Speter          SVN_ERR(svn_wc__conflict_skel_add_tree_conflict(
3796251881Speter                                     fb->edit_conflict,
3797251881Speter                                     eb->db, fb->local_abspath,
3798251881Speter                                     svn_wc_conflict_reason_edited,
3799251881Speter                                     svn_wc_conflict_action_replace,
3800251881Speter                                     NULL,
3801251881Speter                                     fb->pool, scratch_pool));
3802251881Speter
3803251881Speter          SVN_ERR(complete_conflict(fb->edit_conflict, fb->edit_baton,
3804251881Speter                                    fb->local_abspath, fb->old_repos_relpath,
3805289180Speter                                    fb->old_revision, fb->new_repos_relpath,
3806251881Speter                                    svn_node_file, svn_node_file,
3807289180Speter                                    NULL, fb->pool, scratch_pool));
3808251881Speter
3809251881Speter          /* Create a copy of the existing (pre update) BASE node in WORKING,
3810251881Speter             mark a tree conflict and handle the rest of the update as
3811251881Speter             shadowed */
3812251881Speter          SVN_ERR(svn_wc__db_op_make_copy(eb->db, fb->local_abspath,
3813251881Speter                                          fb->edit_conflict, NULL,
3814251881Speter                                          scratch_pool));
3815251881Speter
3816251881Speter          do_notification(eb, fb->local_abspath, svn_node_file,
3817251881Speter                          svn_wc_notify_tree_conflict, scratch_pool);
3818251881Speter
3819251881Speter          /* Ok, we introduced a replacement, so we can now handle the rest
3820251881Speter             as a normal shadowed update */
3821251881Speter          fb->shadowed = TRUE;
3822251881Speter          fb->add_existed = FALSE;
3823251881Speter          fb->already_notified = TRUE;
3824251881Speter      }
3825251881Speter    }
3826251881Speter
3827251881Speter  return SVN_NO_ERROR;
3828251881Speter}
3829251881Speter
3830251881Speter/* Perform the actual merge of file changes between an original file,
3831251881Speter   identified by ORIGINAL_CHECKSUM (an empty file if NULL) to a new file
3832251881Speter   identified by NEW_CHECKSUM.
3833251881Speter
3834251881Speter   Merge the result into LOCAL_ABSPATH, which is part of the working copy
3835251881Speter   identified by WRI_ABSPATH. Use OLD_REVISION and TARGET_REVISION for naming
3836251881Speter   the intermediate files.
3837251881Speter
3838251881Speter   The rest of the arguments are passed to svn_wc__internal_merge().
3839251881Speter */
3840251881Spetersvn_error_t *
3841251881Spetersvn_wc__perform_file_merge(svn_skel_t **work_items,
3842251881Speter                           svn_skel_t **conflict_skel,
3843251881Speter                           svn_boolean_t *found_conflict,
3844251881Speter                           svn_wc__db_t *db,
3845251881Speter                           const char *local_abspath,
3846251881Speter                           const char *wri_abspath,
3847251881Speter                           const svn_checksum_t *new_checksum,
3848251881Speter                           const svn_checksum_t *original_checksum,
3849251881Speter                           apr_hash_t *old_actual_props,
3850251881Speter                           const apr_array_header_t *ext_patterns,
3851251881Speter                           svn_revnum_t old_revision,
3852251881Speter                           svn_revnum_t target_revision,
3853251881Speter                           const apr_array_header_t *propchanges,
3854251881Speter                           const char *diff3_cmd,
3855251881Speter                           svn_cancel_func_t cancel_func,
3856251881Speter                           void *cancel_baton,
3857251881Speter                           apr_pool_t *result_pool,
3858251881Speter                           apr_pool_t *scratch_pool)
3859251881Speter{
3860251881Speter  /* Actual file exists and has local mods:
3861251881Speter     Now we need to let loose svn_wc__internal_merge() to merge
3862251881Speter     the textual changes into the working file. */
3863251881Speter  const char *oldrev_str, *newrev_str, *mine_str;
3864251881Speter  const char *merge_left;
3865251881Speter  svn_boolean_t delete_left = FALSE;
3866251881Speter  const char *path_ext = "";
3867289180Speter  const char *new_pristine_abspath;
3868251881Speter  enum svn_wc_merge_outcome_t merge_outcome = svn_wc_merge_unchanged;
3869251881Speter  svn_skel_t *work_item;
3870251881Speter
3871251881Speter  *work_items = NULL;
3872251881Speter
3873289180Speter  SVN_ERR(svn_wc__db_pristine_get_path(&new_pristine_abspath,
3874251881Speter                                       db, wri_abspath, new_checksum,
3875251881Speter                                       scratch_pool, scratch_pool));
3876251881Speter
3877251881Speter  /* If we have any file extensions we're supposed to
3878251881Speter     preserve in generated conflict file names, then find
3879251881Speter     this path's extension.  But then, if it isn't one of
3880251881Speter     the ones we want to keep in conflict filenames,
3881251881Speter     pretend it doesn't have an extension at all. */
3882251881Speter  if (ext_patterns && ext_patterns->nelts)
3883251881Speter    {
3884251881Speter      svn_path_splitext(NULL, &path_ext, local_abspath, scratch_pool);
3885251881Speter      if (! (*path_ext && svn_cstring_match_glob_list(path_ext, ext_patterns)))
3886251881Speter        path_ext = "";
3887251881Speter    }
3888251881Speter
3889251881Speter  /* old_revision can be invalid when the conflict is against a
3890251881Speter     local addition */
3891251881Speter  if (!SVN_IS_VALID_REVNUM(old_revision))
3892251881Speter    old_revision = 0;
3893251881Speter
3894251881Speter  oldrev_str = apr_psprintf(scratch_pool, ".r%ld%s%s",
3895251881Speter                            old_revision,
3896251881Speter                            *path_ext ? "." : "",
3897251881Speter                            *path_ext ? path_ext : "");
3898251881Speter
3899251881Speter  newrev_str = apr_psprintf(scratch_pool, ".r%ld%s%s",
3900251881Speter                            target_revision,
3901251881Speter                            *path_ext ? "." : "",
3902251881Speter                            *path_ext ? path_ext : "");
3903251881Speter  mine_str = apr_psprintf(scratch_pool, ".mine%s%s",
3904251881Speter                          *path_ext ? "." : "",
3905251881Speter                          *path_ext ? path_ext : "");
3906251881Speter
3907251881Speter  if (! original_checksum)
3908251881Speter    {
3909251881Speter      SVN_ERR(get_empty_tmp_file(&merge_left, db, wri_abspath,
3910251881Speter                                 result_pool, scratch_pool));
3911251881Speter      delete_left = TRUE;
3912251881Speter    }
3913251881Speter  else
3914251881Speter    SVN_ERR(svn_wc__db_pristine_get_path(&merge_left, db, wri_abspath,
3915251881Speter                                         original_checksum,
3916251881Speter                                         result_pool, scratch_pool));
3917251881Speter
3918251881Speter  /* Merge the changes from the old textbase to the new
3919251881Speter     textbase into the file we're updating.
3920251881Speter     Remember that this function wants full paths! */
3921251881Speter  SVN_ERR(svn_wc__internal_merge(&work_item,
3922251881Speter                                 conflict_skel,
3923251881Speter                                 &merge_outcome,
3924251881Speter                                 db,
3925251881Speter                                 merge_left,
3926289180Speter                                 new_pristine_abspath,
3927251881Speter                                 local_abspath,
3928251881Speter                                 wri_abspath,
3929251881Speter                                 oldrev_str, newrev_str, mine_str,
3930251881Speter                                 old_actual_props,
3931251881Speter                                 FALSE /* dry_run */,
3932251881Speter                                 diff3_cmd, NULL, propchanges,
3933251881Speter                                 cancel_func, cancel_baton,
3934251881Speter                                 result_pool, scratch_pool));
3935251881Speter
3936251881Speter  *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
3937251881Speter  *found_conflict = (merge_outcome == svn_wc_merge_conflict);
3938251881Speter
3939251881Speter  /* If we created a temporary left merge file, get rid of it. */
3940251881Speter  if (delete_left)
3941251881Speter    {
3942251881Speter      SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, wri_abspath,
3943251881Speter                                           merge_left,
3944251881Speter                                           result_pool, scratch_pool));
3945251881Speter      *work_items = svn_wc__wq_merge(*work_items, work_item, result_pool);
3946251881Speter    }
3947251881Speter
3948251881Speter  return SVN_NO_ERROR;
3949251881Speter}
3950251881Speter
3951251881Speter/* This is the small planet.  It has the complex responsibility of
3952251881Speter * "integrating" a new revision of a file into a working copy.
3953251881Speter *
3954251881Speter * Given a file_baton FB for a file either already under version control, or
3955251881Speter * prepared (see below) to join version control, fully install a
3956251881Speter * new revision of the file.
3957251881Speter *
3958251881Speter * ### transitional: installation of the working file will be handled
3959251881Speter * ### by the *INSTALL_PRISTINE flag.
3960251881Speter *
3961251881Speter * By "install", we mean: create a new text-base and prop-base, merge
3962251881Speter * any textual and property changes into the working file, and finally
3963251881Speter * update all metadata so that the working copy believes it has a new
3964251881Speter * working revision of the file.  All of this work includes being
3965251881Speter * sensitive to eol translation, keyword substitution, and performing
3966251881Speter * all actions accumulated the parent directory's work queue.
3967251881Speter *
3968251881Speter * Set *CONTENT_STATE to the state of the contents after the
3969251881Speter * installation.
3970251881Speter *
3971251881Speter * Return values are allocated in RESULT_POOL and temporary allocations
3972251881Speter * are performed in SCRATCH_POOL.
3973251881Speter */
3974251881Speterstatic svn_error_t *
3975251881Spetermerge_file(svn_skel_t **work_items,
3976251881Speter           svn_skel_t **conflict_skel,
3977251881Speter           svn_boolean_t *install_pristine,
3978251881Speter           const char **install_from,
3979251881Speter           svn_wc_notify_state_t *content_state,
3980251881Speter           struct file_baton *fb,
3981251881Speter           apr_hash_t *actual_props,
3982251881Speter           apr_time_t last_changed_date,
3983251881Speter           apr_pool_t *result_pool,
3984251881Speter           apr_pool_t *scratch_pool)
3985251881Speter{
3986251881Speter  struct edit_baton *eb = fb->edit_baton;
3987251881Speter  struct dir_baton *pb = fb->dir_baton;
3988251881Speter  svn_boolean_t is_locally_modified;
3989251881Speter  svn_boolean_t found_text_conflict = FALSE;
3990251881Speter
3991251881Speter  SVN_ERR_ASSERT(! fb->shadowed
3992251881Speter                 && ! fb->obstruction_found
3993251881Speter                 && ! fb->edit_obstructed);
3994251881Speter
3995251881Speter  /*
3996251881Speter     When this function is called on file F, we assume the following
3997251881Speter     things are true:
3998251881Speter
3999251881Speter         - The new pristine text of F is present in the pristine store
4000251881Speter           iff FB->NEW_TEXT_BASE_SHA1_CHECKSUM is not NULL.
4001251881Speter
4002251881Speter         - The WC metadata still reflects the old version of F.
4003251881Speter           (We can still access the old pristine base text of F.)
4004251881Speter
4005251881Speter     The goal is to update the local working copy of F to reflect
4006251881Speter     the changes received from the repository, preserving any local
4007251881Speter     modifications.
4008251881Speter  */
4009251881Speter
4010251881Speter  *work_items = NULL;
4011251881Speter  *install_pristine = FALSE;
4012251881Speter  *install_from = NULL;
4013251881Speter
4014251881Speter  /* Start by splitting the file path, getting an access baton for the parent,
4015251881Speter     and an entry for the file if any. */
4016251881Speter
4017251881Speter  /* Has the user made local mods to the working file?
4018251881Speter     Note that this compares to the current pristine file, which is
4019251881Speter     different from fb->old_text_base_path if we have a replaced-with-history
4020251881Speter     file.  However, in the case we had an obstruction, we check against the
4021251881Speter     new text base.
4022251881Speter   */
4023251881Speter  if (fb->adding_file && !fb->add_existed)
4024251881Speter    {
4025251881Speter      is_locally_modified = FALSE; /* There is no file: Don't check */
4026251881Speter    }
4027251881Speter  else
4028251881Speter    {
4029251881Speter      /* The working file is not an obstruction.
4030251881Speter         So: is the file modified, relative to its ORIGINAL pristine?
4031251881Speter
4032251881Speter         This function sets is_locally_modified to FALSE for
4033251881Speter         files that do not exist and for directories. */
4034251881Speter
4035251881Speter      SVN_ERR(svn_wc__internal_file_modified_p(&is_locally_modified,
4036251881Speter                                               eb->db, fb->local_abspath,
4037251881Speter                                               FALSE /* exact_comparison */,
4038251881Speter                                               scratch_pool));
4039251881Speter    }
4040251881Speter
4041251881Speter  /* For 'textual' merging, we use the following system:
4042251881Speter
4043251881Speter     When a file is modified and we have a new BASE:
4044251881Speter      - For text files
4045251881Speter          * svn_wc_merge uses diff3
4046251881Speter          * possibly makes backups and marks files as conflicted.
4047251881Speter
4048251881Speter      - For binary files
4049251881Speter          * svn_wc_merge makes backups and marks files as conflicted.
4050251881Speter
4051251881Speter     If a file is not modified and we have a new BASE:
4052251881Speter       * Install from pristine.
4053251881Speter
4054251881Speter     If we have property changes related to magic properties or if the
4055251881Speter     svn:keywords property is set:
4056251881Speter       * Retranslate from the working file.
4057251881Speter   */
4058251881Speter  if (! is_locally_modified
4059251881Speter      && fb->new_text_base_sha1_checksum)
4060251881Speter    {
4061251881Speter          /* If there are no local mods, who cares whether it's a text
4062251881Speter             or binary file!  Just write a log command to overwrite
4063251881Speter             any working file with the new text-base.  If newline
4064251881Speter             conversion or keyword substitution is activated, this
4065251881Speter             will happen as well during the copy.
4066251881Speter             For replaced files, though, we want to merge in the changes
4067251881Speter             even if the file is not modified compared to the (non-revert)
4068251881Speter             text-base. */
4069251881Speter
4070251881Speter      *install_pristine = TRUE;
4071251881Speter    }
4072251881Speter  else if (fb->new_text_base_sha1_checksum)
4073251881Speter    {
4074251881Speter      /* Actual file exists and has local mods:
4075251881Speter         Now we need to let loose svn_wc__merge_internal() to merge
4076251881Speter         the textual changes into the working file. */
4077251881Speter      SVN_ERR(svn_wc__perform_file_merge(work_items,
4078251881Speter                                         conflict_skel,
4079251881Speter                                         &found_text_conflict,
4080251881Speter                                         eb->db,
4081251881Speter                                         fb->local_abspath,
4082251881Speter                                         pb->local_abspath,
4083251881Speter                                         fb->new_text_base_sha1_checksum,
4084251881Speter                                         fb->add_existed
4085251881Speter                                                  ? NULL
4086251881Speter                                                  : fb->original_checksum,
4087251881Speter                                         actual_props,
4088251881Speter                                         eb->ext_patterns,
4089251881Speter                                         fb->old_revision,
4090251881Speter                                         *eb->target_revision,
4091251881Speter                                         fb->propchanges,
4092251881Speter                                         eb->diff3_cmd,
4093251881Speter                                         eb->cancel_func, eb->cancel_baton,
4094251881Speter                                         result_pool, scratch_pool));
4095251881Speter    } /* end: working file exists and has mods */
4096251881Speter  else
4097251881Speter    {
4098251881Speter      /* There is no new text base, but let's see if the working file needs
4099251881Speter         to be updated for any other reason. */
4100251881Speter
4101251881Speter      apr_hash_t *keywords;
4102251881Speter
4103251881Speter      /* Determine if any of the propchanges are the "magic" ones that
4104251881Speter         might require changing the working file. */
4105251881Speter      svn_boolean_t magic_props_changed;
4106251881Speter
4107251881Speter      magic_props_changed = svn_wc__has_magic_property(fb->propchanges);
4108251881Speter
4109251881Speter      SVN_ERR(svn_wc__get_translate_info(NULL, NULL,
4110251881Speter                                         &keywords,
4111251881Speter                                         NULL,
4112251881Speter                                         eb->db, fb->local_abspath,
4113251881Speter                                         actual_props, TRUE,
4114251881Speter                                         scratch_pool, scratch_pool));
4115251881Speter      if (magic_props_changed || keywords)
4116251881Speter        {
4117251881Speter          /* Special edge-case: it's possible that this file installation
4118251881Speter             only involves propchanges, but that some of those props still
4119251881Speter             require a retranslation of the working file.
4120251881Speter
4121251881Speter             OR that the file doesn't involve propchanges which by themselves
4122251881Speter             require retranslation, but receiving a change bumps the revision
4123251881Speter             number which requires re-expansion of keywords... */
4124251881Speter
4125251881Speter          if (is_locally_modified)
4126251881Speter            {
4127251881Speter              const char *tmptext;
4128251881Speter
4129251881Speter              /* Copy and DEtranslate the working file to a temp text-base.
4130251881Speter                 Note that detranslation is done according to the old props. */
4131251881Speter              SVN_ERR(svn_wc__internal_translated_file(
4132251881Speter                        &tmptext, fb->local_abspath, eb->db, fb->local_abspath,
4133251881Speter                        SVN_WC_TRANSLATE_TO_NF
4134251881Speter                          | SVN_WC_TRANSLATE_NO_OUTPUT_CLEANUP,
4135251881Speter                        eb->cancel_func, eb->cancel_baton,
4136251881Speter                        result_pool, scratch_pool));
4137251881Speter
4138251881Speter              /* We always want to reinstall the working file if the magic
4139251881Speter                 properties have changed, or there are any keywords present.
4140251881Speter                 Note that TMPTEXT might actually refer to the working file
4141251881Speter                 itself (the above function skips a detranslate when not
4142251881Speter                 required). This is acceptable, as we will (re)translate
4143251881Speter                 according to the new properties into a temporary file (from
4144251881Speter                 the working file), and then rename the temp into place. Magic!
4145251881Speter               */
4146251881Speter              *install_pristine = TRUE;
4147251881Speter              *install_from = tmptext;
4148251881Speter            }
4149251881Speter          else
4150251881Speter            {
4151251881Speter              /* Use our existing 'copy' from the pristine store instead
4152251881Speter                 of making a new copy. This way we can use the standard code
4153251881Speter                 to update the recorded size and modification time.
4154251881Speter                 (Issue #3842) */
4155251881Speter              *install_pristine = TRUE;
4156251881Speter            }
4157251881Speter        }
4158251881Speter    }
4159251881Speter
4160251881Speter  /* Set the returned content state. */
4161251881Speter
4162251881Speter  if (found_text_conflict)
4163251881Speter    *content_state = svn_wc_notify_state_conflicted;
4164251881Speter  else if (fb->new_text_base_sha1_checksum)
4165251881Speter    {
4166251881Speter      if (is_locally_modified)
4167251881Speter        *content_state = svn_wc_notify_state_merged;
4168251881Speter      else
4169251881Speter        *content_state = svn_wc_notify_state_changed;
4170251881Speter    }
4171251881Speter  else
4172251881Speter    *content_state = svn_wc_notify_state_unchanged;
4173251881Speter
4174251881Speter  return SVN_NO_ERROR;
4175251881Speter}
4176251881Speter
4177251881Speter
4178251881Speter/* An svn_delta_editor_t function. */
4179251881Speter/* Mostly a wrapper around merge_file. */
4180251881Speterstatic svn_error_t *
4181251881Speterclose_file(void *file_baton,
4182251881Speter           const char *expected_md5_digest,
4183251881Speter           apr_pool_t *pool)
4184251881Speter{
4185251881Speter  struct file_baton *fb = file_baton;
4186251881Speter  struct dir_baton *pdb = fb->dir_baton;
4187251881Speter  struct edit_baton *eb = fb->edit_baton;
4188251881Speter  svn_wc_notify_state_t content_state, prop_state;
4189251881Speter  svn_wc_notify_lock_state_t lock_state;
4190251881Speter  svn_checksum_t *expected_md5_checksum = NULL;
4191251881Speter  apr_hash_t *new_base_props = NULL;
4192251881Speter  apr_hash_t *new_actual_props = NULL;
4193251881Speter  apr_array_header_t *entry_prop_changes;
4194251881Speter  apr_array_header_t *dav_prop_changes;
4195251881Speter  apr_array_header_t *regular_prop_changes;
4196251881Speter  apr_hash_t *current_base_props = NULL;
4197251881Speter  apr_hash_t *current_actual_props = NULL;
4198251881Speter  apr_hash_t *local_actual_props = NULL;
4199251881Speter  svn_skel_t *all_work_items = NULL;
4200251881Speter  svn_skel_t *conflict_skel = NULL;
4201251881Speter  svn_skel_t *work_item;
4202251881Speter  apr_pool_t *scratch_pool = fb->pool; /* Destroyed at function exit */
4203251881Speter  svn_boolean_t keep_recorded_info = FALSE;
4204251881Speter  const svn_checksum_t *new_checksum;
4205251881Speter  apr_array_header_t *iprops = NULL;
4206251881Speter
4207251881Speter  if (fb->skip_this)
4208251881Speter    {
4209251881Speter      svn_pool_destroy(fb->pool);
4210251881Speter      SVN_ERR(maybe_release_dir_info(pdb));
4211251881Speter      return SVN_NO_ERROR;
4212251881Speter    }
4213251881Speter
4214251881Speter  if (fb->edited)
4215251881Speter    conflict_skel = fb->edit_conflict;
4216251881Speter
4217251881Speter  if (expected_md5_digest)
4218251881Speter    SVN_ERR(svn_checksum_parse_hex(&expected_md5_checksum, svn_checksum_md5,
4219251881Speter                                   expected_md5_digest, scratch_pool));
4220251881Speter
4221251881Speter  if (fb->new_text_base_md5_checksum && expected_md5_checksum
4222251881Speter      && !svn_checksum_match(expected_md5_checksum,
4223251881Speter                             fb->new_text_base_md5_checksum))
4224251881Speter    return svn_error_trace(
4225251881Speter                svn_checksum_mismatch_err(expected_md5_checksum,
4226251881Speter                                          fb->new_text_base_md5_checksum,
4227251881Speter                                          scratch_pool,
4228251881Speter                                          _("Checksum mismatch for '%s'"),
4229251881Speter                                          svn_dirent_local_style(
4230251881Speter                                                fb->local_abspath, pool)));
4231251881Speter
4232251881Speter  /* Gather the changes for each kind of property.  */
4233251881Speter  SVN_ERR(svn_categorize_props(fb->propchanges, &entry_prop_changes,
4234251881Speter                               &dav_prop_changes, &regular_prop_changes,
4235251881Speter                               scratch_pool));
4236251881Speter
4237251881Speter  /* Extract the changed_* and lock state information.  */
4238251881Speter  {
4239251881Speter    svn_revnum_t new_changed_rev;
4240251881Speter    apr_time_t new_changed_date;
4241251881Speter    const char *new_changed_author;
4242251881Speter
4243251881Speter    SVN_ERR(accumulate_last_change(&new_changed_rev,
4244251881Speter                                   &new_changed_date,
4245251881Speter                                   &new_changed_author,
4246251881Speter                                   entry_prop_changes,
4247251881Speter                                   scratch_pool, scratch_pool));
4248251881Speter
4249251881Speter    if (SVN_IS_VALID_REVNUM(new_changed_rev))
4250251881Speter      fb->changed_rev = new_changed_rev;
4251251881Speter    if (new_changed_date != 0)
4252251881Speter      fb->changed_date = new_changed_date;
4253251881Speter    if (new_changed_author != NULL)
4254251881Speter      fb->changed_author = new_changed_author;
4255251881Speter  }
4256251881Speter
4257251881Speter  /* Determine whether the file has become unlocked.  */
4258251881Speter  {
4259251881Speter    int i;
4260251881Speter
4261251881Speter    lock_state = svn_wc_notify_lock_state_unchanged;
4262251881Speter
4263251881Speter    for (i = 0; i < entry_prop_changes->nelts; ++i)
4264251881Speter      {
4265251881Speter        const svn_prop_t *prop
4266251881Speter          = &APR_ARRAY_IDX(entry_prop_changes, i, svn_prop_t);
4267251881Speter
4268251881Speter        /* If we see a change to the LOCK_TOKEN entry prop, then the only
4269251881Speter           possible change is its REMOVAL. Thus, the lock has been removed,
4270251881Speter           and we should likewise remove our cached copy of it.  */
4271251881Speter        if (! strcmp(prop->name, SVN_PROP_ENTRY_LOCK_TOKEN))
4272251881Speter          {
4273251881Speter            /* If we lose the lock, but not because we are switching to
4274251881Speter               another url, remove the state lock from the wc */
4275289180Speter            if (! eb->switch_repos_relpath
4276289180Speter                || strcmp(fb->new_repos_relpath, fb->old_repos_relpath) == 0)
4277251881Speter              {
4278251881Speter                SVN_ERR_ASSERT(prop->value == NULL);
4279289180Speter                SVN_ERR(svn_wc__db_lock_remove(eb->db, fb->local_abspath, NULL,
4280251881Speter                                               scratch_pool));
4281251881Speter
4282251881Speter                lock_state = svn_wc_notify_lock_state_unlocked;
4283251881Speter              }
4284251881Speter            break;
4285251881Speter          }
4286251881Speter      }
4287251881Speter  }
4288251881Speter
4289251881Speter  /* Install all kinds of properties.  It is important to do this before
4290251881Speter     any file content merging, since that process might expand keywords, in
4291251881Speter     which case we want the new entryprops to be in place. */
4292251881Speter
4293251881Speter  /* Write log commands to merge REGULAR_PROPS into the existing
4294251881Speter     properties of FB->LOCAL_ABSPATH.  Update *PROP_STATE to reflect
4295251881Speter     the result of the regular prop merge.
4296251881Speter
4297251881Speter     BASE_PROPS and WORKING_PROPS are hashes of the base and
4298251881Speter     working props of the file; if NULL they are read from the wc.  */
4299251881Speter
4300251881Speter  /* ### some of this feels like voodoo... */
4301251881Speter
4302251881Speter  if ((!fb->adding_file || fb->add_existed)
4303251881Speter      && !fb->shadowed)
4304251881Speter    SVN_ERR(svn_wc__get_actual_props(&local_actual_props,
4305251881Speter                                     eb->db, fb->local_abspath,
4306251881Speter                                     scratch_pool, scratch_pool));
4307251881Speter  if (local_actual_props == NULL)
4308251881Speter    local_actual_props = apr_hash_make(scratch_pool);
4309251881Speter
4310251881Speter  if (fb->add_existed)
4311251881Speter    {
4312251881Speter      /* This node already exists. Grab the current pristine properties. */
4313251881Speter      SVN_ERR(svn_wc__db_read_pristine_props(&current_base_props,
4314251881Speter                                             eb->db, fb->local_abspath,
4315251881Speter                                             scratch_pool, scratch_pool));
4316251881Speter      current_actual_props = local_actual_props;
4317251881Speter    }
4318251881Speter  else if (!fb->adding_file)
4319251881Speter    {
4320251881Speter      /* Get the BASE properties for proper merging. */
4321251881Speter      SVN_ERR(svn_wc__db_base_get_props(&current_base_props,
4322251881Speter                                        eb->db, fb->local_abspath,
4323251881Speter                                        scratch_pool, scratch_pool));
4324251881Speter      current_actual_props = local_actual_props;
4325251881Speter    }
4326251881Speter
4327251881Speter  /* Note: even if the node existed before, it may not have
4328251881Speter     pristine props (e.g a local-add)  */
4329251881Speter  if (current_base_props == NULL)
4330251881Speter    current_base_props = apr_hash_make(scratch_pool);
4331251881Speter
4332251881Speter  /* And new nodes need an empty set of ACTUAL props.  */
4333251881Speter  if (current_actual_props == NULL)
4334251881Speter    current_actual_props = apr_hash_make(scratch_pool);
4335251881Speter
4336251881Speter  prop_state = svn_wc_notify_state_unknown;
4337251881Speter
4338251881Speter  if (! fb->shadowed)
4339251881Speter    {
4340251881Speter      svn_boolean_t install_pristine;
4341251881Speter      const char *install_from = NULL;
4342251881Speter
4343251881Speter      /* Merge the 'regular' props into the existing working proplist. */
4344251881Speter      /* This will merge the old and new props into a new prop db, and
4345251881Speter         write <cp> commands to the logfile to install the merged
4346251881Speter         props.  */
4347251881Speter      new_base_props = svn_prop__patch(current_base_props, regular_prop_changes,
4348251881Speter                                       scratch_pool);
4349251881Speter      SVN_ERR(svn_wc__merge_props(&conflict_skel,
4350251881Speter                                  &prop_state,
4351251881Speter                                  &new_actual_props,
4352251881Speter                                  eb->db,
4353251881Speter                                  fb->local_abspath,
4354251881Speter                                  NULL /* server_baseprops (update, not merge)  */,
4355251881Speter                                  current_base_props,
4356251881Speter                                  current_actual_props,
4357251881Speter                                  regular_prop_changes, /* propchanges */
4358251881Speter                                  scratch_pool,
4359251881Speter                                  scratch_pool));
4360251881Speter      /* We will ALWAYS have properties to save (after a not-dry-run merge). */
4361251881Speter      SVN_ERR_ASSERT(new_base_props != NULL && new_actual_props != NULL);
4362251881Speter
4363251881Speter      /* Merge the text. This will queue some additional work.  */
4364251881Speter      if (!fb->obstruction_found && !fb->edit_obstructed)
4365251881Speter        {
4366251881Speter          svn_error_t *err;
4367251881Speter          err = merge_file(&work_item, &conflict_skel,
4368251881Speter                           &install_pristine, &install_from,
4369251881Speter                           &content_state, fb, current_actual_props,
4370251881Speter                           fb->changed_date, scratch_pool, scratch_pool);
4371251881Speter
4372251881Speter          if (err && err->apr_err == SVN_ERR_WC_PATH_ACCESS_DENIED)
4373251881Speter            {
4374251881Speter              if (eb->notify_func)
4375251881Speter                {
4376251881Speter                  svn_wc_notify_t *notify =svn_wc_create_notify(
4377251881Speter                                fb->local_abspath,
4378251881Speter                                svn_wc_notify_update_skip_access_denied,
4379251881Speter                                scratch_pool);
4380251881Speter
4381251881Speter                  notify->kind = svn_node_file;
4382251881Speter                  notify->err = err;
4383251881Speter
4384251881Speter                  eb->notify_func(eb->notify_baton, notify, scratch_pool);
4385251881Speter                }
4386251881Speter              svn_error_clear(err);
4387251881Speter
4388251881Speter              SVN_ERR(remember_skipped_tree(eb, fb->local_abspath,
4389251881Speter                                            scratch_pool));
4390251881Speter              fb->skip_this = TRUE;
4391251881Speter
4392251881Speter              svn_pool_destroy(fb->pool);
4393251881Speter              SVN_ERR(maybe_release_dir_info(pdb));
4394251881Speter              return SVN_NO_ERROR;
4395251881Speter            }
4396251881Speter          else
4397251881Speter            SVN_ERR(err);
4398251881Speter
4399251881Speter          all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4400251881Speter                                            scratch_pool);
4401251881Speter        }
4402251881Speter      else
4403251881Speter        {
4404251881Speter          install_pristine = FALSE;
4405251881Speter          if (fb->new_text_base_sha1_checksum)
4406251881Speter            content_state = svn_wc_notify_state_changed;
4407251881Speter          else
4408251881Speter            content_state = svn_wc_notify_state_unchanged;
4409251881Speter        }
4410251881Speter
4411251881Speter      if (install_pristine)
4412251881Speter        {
4413251881Speter          svn_boolean_t record_fileinfo;
4414251881Speter
4415251881Speter          /* If we are installing from the pristine contents, then go ahead and
4416251881Speter             record the fileinfo. That will be the "proper" values. Installing
4417251881Speter             from some random file means the fileinfo does NOT correspond to
4418251881Speter             the pristine (in which case, the fileinfo will be cleared for
4419251881Speter             safety's sake).  */
4420251881Speter          record_fileinfo = (install_from == NULL);
4421251881Speter
4422251881Speter          SVN_ERR(svn_wc__wq_build_file_install(&work_item,
4423251881Speter                                                eb->db,
4424251881Speter                                                fb->local_abspath,
4425251881Speter                                                install_from,
4426251881Speter                                                eb->use_commit_times,
4427251881Speter                                                record_fileinfo,
4428251881Speter                                                scratch_pool, scratch_pool));
4429251881Speter          all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4430251881Speter                                            scratch_pool);
4431251881Speter        }
4432251881Speter      else if (lock_state == svn_wc_notify_lock_state_unlocked
4433251881Speter               && !fb->obstruction_found)
4434251881Speter        {
4435251881Speter          /* If a lock was removed and we didn't update the text contents, we
4436251881Speter             might need to set the file read-only.
4437251881Speter
4438251881Speter             Note: this will also update the executable flag, but ... meh.  */
4439251881Speter          SVN_ERR(svn_wc__wq_build_sync_file_flags(&work_item, eb->db,
4440251881Speter                                                   fb->local_abspath,
4441251881Speter                                                   scratch_pool, scratch_pool));
4442251881Speter          all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4443251881Speter                                            scratch_pool);
4444251881Speter        }
4445251881Speter
4446251881Speter      if (! install_pristine
4447251881Speter          && (content_state == svn_wc_notify_state_unchanged))
4448251881Speter        {
4449251881Speter          /* It is safe to keep the current recorded timestamp and size */
4450251881Speter          keep_recorded_info = TRUE;
4451251881Speter        }
4452251881Speter
4453251881Speter      /* Clean up any temporary files.  */
4454251881Speter
4455251881Speter      /* Remove the INSTALL_FROM file, as long as it doesn't refer to the
4456251881Speter         working file.  */
4457251881Speter      if (install_from != NULL
4458251881Speter          && strcmp(install_from, fb->local_abspath) != 0)
4459251881Speter        {
4460251881Speter          SVN_ERR(svn_wc__wq_build_file_remove(&work_item, eb->db,
4461251881Speter                                               fb->local_abspath, install_from,
4462251881Speter                                               scratch_pool, scratch_pool));
4463251881Speter          all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4464251881Speter                                            scratch_pool);
4465251881Speter        }
4466251881Speter    }
4467251881Speter  else
4468251881Speter    {
4469251881Speter      /* Adding or updating a BASE node under a locally added node. */
4470251881Speter      apr_hash_t *fake_actual_props;
4471251881Speter
4472251881Speter      if (fb->adding_file)
4473251881Speter        fake_actual_props = apr_hash_make(scratch_pool);
4474251881Speter      else
4475251881Speter        fake_actual_props = current_base_props;
4476251881Speter
4477251881Speter      /* Store the incoming props (sent as propchanges) in new_base_props
4478251881Speter         and create a set of new actual props to use for notifications */
4479251881Speter      new_base_props = svn_prop__patch(current_base_props, regular_prop_changes,
4480251881Speter                                       scratch_pool);
4481251881Speter      SVN_ERR(svn_wc__merge_props(&conflict_skel,
4482251881Speter                                  &prop_state,
4483251881Speter                                  &new_actual_props,
4484251881Speter                                  eb->db,
4485251881Speter                                  fb->local_abspath,
4486251881Speter                                  NULL /* server_baseprops (not merging) */,
4487251881Speter                                  current_base_props /* pristine_props */,
4488251881Speter                                  fake_actual_props /* actual_props */,
4489251881Speter                                  regular_prop_changes, /* propchanges */
4490251881Speter                                  scratch_pool,
4491251881Speter                                  scratch_pool));
4492251881Speter
4493251881Speter      if (fb->new_text_base_sha1_checksum)
4494251881Speter        content_state = svn_wc_notify_state_changed;
4495251881Speter      else
4496251881Speter        content_state = svn_wc_notify_state_unchanged;
4497251881Speter    }
4498251881Speter
4499251881Speter  /* Insert/replace the BASE node with all of the new metadata.  */
4500251881Speter
4501251881Speter  /* Set the 'checksum' column of the file's BASE_NODE row to
4502251881Speter   * NEW_TEXT_BASE_SHA1_CHECKSUM.  The pristine text identified by that
4503251881Speter   * checksum is already in the pristine store. */
4504251881Speter  new_checksum = fb->new_text_base_sha1_checksum;
4505251881Speter
4506251881Speter  /* If we don't have a NEW checksum, then the base must not have changed.
4507251881Speter     Just carry over the old checksum.  */
4508251881Speter  if (new_checksum == NULL)
4509251881Speter    new_checksum = fb->original_checksum;
4510251881Speter
4511251881Speter  if (conflict_skel)
4512251881Speter    {
4513251881Speter      SVN_ERR(complete_conflict(conflict_skel,
4514251881Speter                                fb->edit_baton,
4515251881Speter                                fb->local_abspath,
4516251881Speter                                fb->old_repos_relpath,
4517251881Speter                                fb->old_revision,
4518289180Speter                                fb->new_repos_relpath,
4519251881Speter                                svn_node_file, svn_node_file,
4520289180Speter                                fb->dir_baton->deletion_conflicts
4521289180Speter                                  ? svn_hash_gets(
4522289180Speter                                        fb->dir_baton->deletion_conflicts,
4523289180Speter                                        fb->name)
4524289180Speter                                  : NULL,
4525251881Speter                                fb->pool, scratch_pool));
4526251881Speter
4527251881Speter      SVN_ERR(svn_wc__conflict_create_markers(&work_item,
4528251881Speter                                              eb->db, fb->local_abspath,
4529251881Speter                                              conflict_skel,
4530251881Speter                                              scratch_pool, scratch_pool));
4531251881Speter
4532251881Speter      all_work_items = svn_wc__wq_merge(all_work_items, work_item,
4533251881Speter                                        scratch_pool);
4534251881Speter    }
4535251881Speter
4536251881Speter  /* Any inherited props to be set set for this base node? */
4537251881Speter  if (eb->wcroot_iprops)
4538251881Speter    {
4539251881Speter      iprops = svn_hash_gets(eb->wcroot_iprops, fb->local_abspath);
4540251881Speter
4541251881Speter      /* close_edit may also update iprops for switched nodes, catching
4542251881Speter         those for which close_directory is never called (e.g. a switch
4543251881Speter         with no changes).  So as a minor optimization we remove any
4544251881Speter         iprops from the hash so as not to set them again in
4545251881Speter         close_edit. */
4546251881Speter      if (iprops)
4547251881Speter        svn_hash_sets(eb->wcroot_iprops, fb->local_abspath, NULL);
4548251881Speter    }
4549251881Speter
4550251881Speter  SVN_ERR(svn_wc__db_base_add_file(eb->db, fb->local_abspath,
4551251881Speter                                   eb->wcroot_abspath,
4552289180Speter                                   fb->new_repos_relpath,
4553251881Speter                                   eb->repos_root, eb->repos_uuid,
4554251881Speter                                   *eb->target_revision,
4555251881Speter                                   new_base_props,
4556251881Speter                                   fb->changed_rev,
4557251881Speter                                   fb->changed_date,
4558251881Speter                                   fb->changed_author,
4559251881Speter                                   new_checksum,
4560251881Speter                                   (dav_prop_changes->nelts > 0)
4561251881Speter                                     ? svn_prop_array_to_hash(
4562251881Speter                                                      dav_prop_changes,
4563251881Speter                                                      scratch_pool)
4564251881Speter                                     : NULL,
4565251881Speter                                   (fb->add_existed && fb->adding_file),
4566251881Speter                                   (! fb->shadowed) && new_base_props,
4567251881Speter                                   new_actual_props,
4568251881Speter                                   iprops,
4569251881Speter                                   keep_recorded_info,
4570251881Speter                                   (fb->shadowed && fb->obstruction_found),
4571251881Speter                                   conflict_skel,
4572251881Speter                                   all_work_items,
4573251881Speter                                   scratch_pool));
4574251881Speter
4575251881Speter  if (conflict_skel && eb->conflict_func)
4576251881Speter    SVN_ERR(svn_wc__conflict_invoke_resolver(eb->db, fb->local_abspath,
4577289180Speter                                             svn_node_file,
4578251881Speter                                             conflict_skel,
4579251881Speter                                             NULL /* merge_options */,
4580251881Speter                                             eb->conflict_func,
4581251881Speter                                             eb->conflict_baton,
4582251881Speter                                             eb->cancel_func,
4583251881Speter                                             eb->cancel_baton,
4584251881Speter                                             scratch_pool));
4585251881Speter
4586251881Speter  /* Deal with the WORKING tree, based on updates to the BASE tree.  */
4587251881Speter
4588289180Speter  svn_hash_sets(fb->dir_baton->not_present_nodes, fb->name, NULL);
4589251881Speter
4590251881Speter  /* Send a notification to the callback function.  (Skip notifications
4591251881Speter     about files which were already notified for another reason.) */
4592251881Speter  if (eb->notify_func && !fb->already_notified
4593251881Speter      && (fb->edited || lock_state == svn_wc_notify_lock_state_unlocked))
4594251881Speter    {
4595251881Speter      svn_wc_notify_t *notify;
4596251881Speter      svn_wc_notify_action_t action = svn_wc_notify_update_update;
4597251881Speter
4598251881Speter      if (fb->edited)
4599251881Speter        {
4600251881Speter          if (fb->shadowed || fb->edit_obstructed)
4601251881Speter            action = fb->adding_file
4602251881Speter                            ? svn_wc_notify_update_shadowed_add
4603251881Speter                            : svn_wc_notify_update_shadowed_update;
4604251881Speter          else if (fb->obstruction_found || fb->add_existed)
4605251881Speter            {
4606251881Speter              if (content_state != svn_wc_notify_state_conflicted)
4607251881Speter                action = svn_wc_notify_exists;
4608251881Speter            }
4609251881Speter          else if (fb->adding_file)
4610251881Speter            {
4611251881Speter              action = svn_wc_notify_update_add;
4612251881Speter            }
4613251881Speter        }
4614251881Speter      else
4615251881Speter        {
4616251881Speter          SVN_ERR_ASSERT(lock_state == svn_wc_notify_lock_state_unlocked);
4617251881Speter          action = svn_wc_notify_update_broken_lock;
4618251881Speter        }
4619251881Speter
4620251881Speter      /* If the file was moved-away, notify for the moved-away node.
4621251881Speter       * The original location only had its BASE info changed and
4622251881Speter       * we don't usually notify about such changes. */
4623251881Speter      notify = svn_wc_create_notify(fb->local_abspath, action, scratch_pool);
4624251881Speter      notify->kind = svn_node_file;
4625251881Speter      notify->content_state = content_state;
4626251881Speter      notify->prop_state = prop_state;
4627251881Speter      notify->lock_state = lock_state;
4628251881Speter      notify->revision = *eb->target_revision;
4629251881Speter      notify->old_revision = fb->old_revision;
4630251881Speter
4631251881Speter      /* Fetch the mimetype from the actual properties */
4632251881Speter      notify->mime_type = svn_prop_get_value(new_actual_props,
4633251881Speter                                             SVN_PROP_MIME_TYPE);
4634251881Speter
4635251881Speter      eb->notify_func(eb->notify_baton, notify, scratch_pool);
4636251881Speter    }
4637251881Speter
4638251881Speter  svn_pool_destroy(fb->pool); /* Destroy scratch_pool */
4639251881Speter
4640251881Speter  /* We have one less referrer to the directory */
4641251881Speter  SVN_ERR(maybe_release_dir_info(pdb));
4642251881Speter
4643251881Speter  return SVN_NO_ERROR;
4644251881Speter}
4645251881Speter
4646251881Speter
4647289180Speter/* Implements svn_wc__proplist_receiver_t.
4648289180Speter * Check for the presence of an svn:keywords property and queues an install_file
4649289180Speter * work queue item if present. Thus, when the work queue is run to complete the
4650289180Speter * switch operation, all files with keywords will go through the translation
4651289180Speter * process so URLs etc are updated. */
4652289180Speterstatic svn_error_t *
4653289180Speterupdate_keywords_after_switch_cb(void *baton,
4654289180Speter                                const char *local_abspath,
4655289180Speter                                apr_hash_t *props,
4656289180Speter                                apr_pool_t *scratch_pool)
4657289180Speter{
4658289180Speter  struct edit_baton *eb = baton;
4659289180Speter  svn_string_t *propval;
4660289180Speter  svn_boolean_t modified;
4661289180Speter  svn_boolean_t record_fileinfo;
4662289180Speter  svn_skel_t *work_items;
4663289180Speter  const char *install_from;
4664289180Speter
4665289180Speter  propval = svn_hash_gets(props, SVN_PROP_KEYWORDS);
4666289180Speter  if (!propval)
4667289180Speter    return SVN_NO_ERROR;
4668289180Speter
4669289180Speter  SVN_ERR(svn_wc__internal_file_modified_p(&modified, eb->db,
4670289180Speter                                           local_abspath, FALSE,
4671289180Speter                                           scratch_pool));
4672289180Speter  if (modified)
4673289180Speter    {
4674289180Speter      const char *temp_dir_abspath;
4675289180Speter      svn_stream_t *working_stream;
4676289180Speter      svn_stream_t *install_from_stream;
4677289180Speter
4678289180Speter      SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, eb->db,
4679289180Speter                                             local_abspath, scratch_pool,
4680289180Speter                                             scratch_pool));
4681289180Speter      SVN_ERR(svn_stream_open_readonly(&working_stream, local_abspath,
4682289180Speter                                       scratch_pool, scratch_pool));
4683289180Speter      SVN_ERR(svn_stream_open_unique(&install_from_stream, &install_from,
4684289180Speter                                     temp_dir_abspath, svn_io_file_del_none,
4685289180Speter                                     scratch_pool, scratch_pool));
4686289180Speter      SVN_ERR(svn_stream_copy3(working_stream, install_from_stream,
4687289180Speter                               eb->cancel_func, eb->cancel_baton,
4688289180Speter                               scratch_pool));
4689289180Speter      record_fileinfo = FALSE;
4690289180Speter    }
4691289180Speter  else
4692289180Speter    {
4693289180Speter      install_from = NULL;
4694289180Speter      record_fileinfo = TRUE;
4695289180Speter    }
4696289180Speter
4697289180Speter  SVN_ERR(svn_wc__wq_build_file_install(&work_items, eb->db, local_abspath,
4698289180Speter                                        install_from,
4699289180Speter                                        eb->use_commit_times,
4700289180Speter                                        record_fileinfo,
4701289180Speter                                        scratch_pool, scratch_pool));
4702289180Speter  if (install_from)
4703289180Speter    {
4704289180Speter      svn_skel_t *work_item;
4705289180Speter
4706289180Speter      SVN_ERR(svn_wc__wq_build_file_remove(&work_item, eb->db,
4707289180Speter                                           local_abspath, install_from,
4708289180Speter                                           scratch_pool, scratch_pool));
4709289180Speter      work_items = svn_wc__wq_merge(work_items, work_item, scratch_pool);
4710289180Speter    }
4711289180Speter
4712289180Speter  SVN_ERR(svn_wc__db_wq_add(eb->db, local_abspath, work_items,
4713289180Speter                            scratch_pool));
4714289180Speter
4715289180Speter  return SVN_NO_ERROR;
4716289180Speter}
4717289180Speter
4718289180Speter
4719251881Speter/* An svn_delta_editor_t function. */
4720251881Speterstatic svn_error_t *
4721251881Speterclose_edit(void *edit_baton,
4722251881Speter           apr_pool_t *pool)
4723251881Speter{
4724251881Speter  struct edit_baton *eb = edit_baton;
4725251881Speter  apr_pool_t *scratch_pool = eb->pool;
4726251881Speter
4727251881Speter  /* The editor didn't even open the root; we have to take care of
4728251881Speter     some cleanup stuffs. */
4729251881Speter  if (! eb->root_opened
4730251881Speter      && *eb->target_basename == '\0')
4731251881Speter    {
4732251881Speter      /* We need to "un-incomplete" the root directory. */
4733251881Speter      SVN_ERR(svn_wc__db_temp_op_end_directory_update(eb->db,
4734251881Speter                                                      eb->anchor_abspath,
4735251881Speter                                                      scratch_pool));
4736251881Speter    }
4737251881Speter
4738251881Speter  /* By definition, anybody "driving" this editor for update or switch
4739251881Speter     purposes at a *minimum* must have called set_target_revision() at
4740251881Speter     the outset, and close_edit() at the end -- even if it turned out
4741251881Speter     that no changes ever had to be made, and open_root() was never
4742251881Speter     called.  That's fine.  But regardless, when the edit is over,
4743251881Speter     this editor needs to make sure that *all* paths have had their
4744251881Speter     revisions bumped to the new target revision. */
4745251881Speter
4746251881Speter  /* Make sure our update target now has the new working revision.
4747251881Speter     Also, if this was an 'svn switch', then rewrite the target's
4748251881Speter     url.  All of this tweaking might happen recursively!  Note
4749251881Speter     that if eb->target is NULL, that's okay (albeit "sneaky",
4750251881Speter     some might say).  */
4751251881Speter
4752251881Speter  /* Extra check: if the update did nothing but make its target
4753251881Speter     'deleted', then do *not* run cleanup on the target, as it
4754251881Speter     will only remove the deleted entry!  */
4755251881Speter  if (! eb->target_deleted)
4756251881Speter    {
4757251881Speter      SVN_ERR(svn_wc__db_op_bump_revisions_post_update(eb->db,
4758251881Speter                                                       eb->target_abspath,
4759251881Speter                                                       eb->requested_depth,
4760289180Speter                                                       eb->switch_repos_relpath,
4761251881Speter                                                       eb->repos_root,
4762251881Speter                                                       eb->repos_uuid,
4763251881Speter                                                       *(eb->target_revision),
4764251881Speter                                                       eb->skipped_trees,
4765251881Speter                                                       eb->wcroot_iprops,
4766289180Speter                                                       ! eb->edited,
4767251881Speter                                                       eb->notify_func,
4768251881Speter                                                       eb->notify_baton,
4769251881Speter                                                       eb->pool));
4770251881Speter
4771251881Speter      if (*eb->target_basename != '\0')
4772251881Speter        {
4773251881Speter          svn_wc__db_status_t status;
4774251881Speter          svn_error_t *err;
4775251881Speter
4776251881Speter          /* Note: we are fetching information about the *target*, not anchor.
4777251881Speter             There is no guarantee that the target has a BASE node.
4778251881Speter             For example:
4779251881Speter
4780251881Speter               The node was not present in BASE, but locally-added, and the
4781251881Speter               update did not create a new BASE node "under" the local-add.
4782251881Speter
4783251881Speter             If there is no BASE node for the target, then we certainly don't
4784251881Speter             have to worry about removing it. */
4785251881Speter          err = svn_wc__db_base_get_info(&status, NULL, NULL, NULL, NULL, NULL,
4786251881Speter                                         NULL, NULL, NULL, NULL, NULL, NULL,
4787251881Speter                                         NULL, NULL, NULL, NULL,
4788251881Speter                                         eb->db, eb->target_abspath,
4789251881Speter                                         scratch_pool, scratch_pool);
4790251881Speter          if (err)
4791251881Speter            {
4792251881Speter              if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
4793251881Speter                return svn_error_trace(err);
4794251881Speter
4795251881Speter              svn_error_clear(err);
4796251881Speter            }
4797251881Speter          else if (status == svn_wc__db_status_excluded)
4798251881Speter            {
4799251881Speter              /* There is a small chance that the explicit target of an update/
4800251881Speter                 switch is gone in the repository, in that specific case the
4801251881Speter                 node hasn't been re-added to the BASE tree by this update.
4802251881Speter
4803251881Speter                 If so, we should get rid of this excluded node now. */
4804251881Speter
4805251881Speter              SVN_ERR(svn_wc__db_base_remove(eb->db, eb->target_abspath,
4806289180Speter                                             TRUE, FALSE, FALSE,
4807251881Speter                                             SVN_INVALID_REVNUM,
4808251881Speter                                             NULL, NULL, scratch_pool));
4809251881Speter            }
4810251881Speter        }
4811251881Speter    }
4812251881Speter
4813289180Speter  /* Update keywords in switched files.
4814289180Speter     GOTO #1975 (the year of the Altair 8800). */
4815289180Speter  if (eb->switch_repos_relpath)
4816289180Speter    {
4817289180Speter      svn_depth_t depth;
4818289180Speter
4819289180Speter      if (eb->requested_depth > svn_depth_empty)
4820289180Speter        depth = eb->requested_depth;
4821289180Speter      else
4822289180Speter        depth = svn_depth_infinity;
4823289180Speter
4824289180Speter      SVN_ERR(svn_wc__db_read_props_streamily(eb->db,
4825289180Speter                                              eb->target_abspath,
4826289180Speter                                              depth,
4827289180Speter                                              FALSE, /* pristine */
4828289180Speter                                              NULL, /* changelists */
4829289180Speter                                              update_keywords_after_switch_cb,
4830289180Speter                                              eb,
4831289180Speter                                              eb->cancel_func,
4832289180Speter                                              eb->cancel_baton,
4833289180Speter                                              scratch_pool));
4834289180Speter    }
4835289180Speter
4836251881Speter  /* The edit is over: run the wq with proper cancel support,
4837251881Speter     but first kill the handler that would run it on the pool
4838251881Speter     cleanup at the end of this function. */
4839251881Speter  apr_pool_cleanup_kill(eb->pool, eb, cleanup_edit_baton);
4840251881Speter
4841251881Speter  SVN_ERR(svn_wc__wq_run(eb->db, eb->wcroot_abspath,
4842251881Speter                         eb->cancel_func, eb->cancel_baton,
4843251881Speter                         eb->pool));
4844251881Speter
4845251881Speter  /* The edit is over, free its pool.
4846251881Speter     ### No, this is wrong.  Who says this editor/baton won't be used
4847251881Speter     again?  But the change is not merely to remove this call.  We
4848251881Speter     should also make eb->pool not be a subpool (see make_editor),
4849251881Speter     and change callers of svn_client_{checkout,update,switch} to do
4850251881Speter     better pool management. ### */
4851251881Speter
4852251881Speter  svn_pool_destroy(eb->pool);
4853251881Speter
4854251881Speter  return SVN_NO_ERROR;
4855251881Speter}
4856251881Speter
4857251881Speter
4858251881Speter/*** Returning editors. ***/
4859251881Speter
4860251881Speter/* Helper for the three public editor-supplying functions. */
4861251881Speterstatic svn_error_t *
4862251881Spetermake_editor(svn_revnum_t *target_revision,
4863251881Speter            svn_wc__db_t *db,
4864251881Speter            const char *anchor_abspath,
4865251881Speter            const char *target_basename,
4866251881Speter            apr_hash_t *wcroot_iprops,
4867251881Speter            svn_boolean_t use_commit_times,
4868251881Speter            const char *switch_url,
4869251881Speter            svn_depth_t depth,
4870251881Speter            svn_boolean_t depth_is_sticky,
4871251881Speter            svn_boolean_t allow_unver_obstructions,
4872251881Speter            svn_boolean_t adds_as_modification,
4873251881Speter            svn_boolean_t server_performs_filtering,
4874251881Speter            svn_boolean_t clean_checkout,
4875251881Speter            svn_wc_notify_func2_t notify_func,
4876251881Speter            void *notify_baton,
4877251881Speter            svn_cancel_func_t cancel_func,
4878251881Speter            void *cancel_baton,
4879251881Speter            svn_wc_dirents_func_t fetch_dirents_func,
4880251881Speter            void *fetch_dirents_baton,
4881251881Speter            svn_wc_conflict_resolver_func2_t conflict_func,
4882251881Speter            void *conflict_baton,
4883251881Speter            svn_wc_external_update_t external_func,
4884251881Speter            void *external_baton,
4885251881Speter            const char *diff3_cmd,
4886251881Speter            const apr_array_header_t *preserved_exts,
4887251881Speter            const svn_delta_editor_t **editor,
4888251881Speter            void **edit_baton,
4889251881Speter            apr_pool_t *result_pool,
4890251881Speter            apr_pool_t *scratch_pool)
4891251881Speter{
4892251881Speter  struct edit_baton *eb;
4893251881Speter  void *inner_baton;
4894251881Speter  apr_pool_t *edit_pool = svn_pool_create(result_pool);
4895251881Speter  svn_delta_editor_t *tree_editor = svn_delta_default_editor(edit_pool);
4896251881Speter  const svn_delta_editor_t *inner_editor;
4897251881Speter  const char *repos_root, *repos_uuid;
4898251881Speter  struct svn_wc__shim_fetch_baton_t *sfb;
4899251881Speter  svn_delta_shim_callbacks_t *shim_callbacks =
4900251881Speter                                svn_delta_shim_callbacks_default(edit_pool);
4901251881Speter
4902251881Speter  /* An unknown depth can't be sticky. */
4903251881Speter  if (depth == svn_depth_unknown)
4904251881Speter    depth_is_sticky = FALSE;
4905251881Speter
4906251881Speter  /* Get the anchor's repository root and uuid. The anchor must already exist
4907251881Speter     in BASE. */
4908289180Speter  SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, &repos_root,
4909289180Speter                                   &repos_uuid, NULL, NULL, NULL, NULL,
4910289180Speter                                   NULL, NULL, NULL, NULL, NULL, NULL,
4911289180Speter                                   db, anchor_abspath,
4912289180Speter                                   result_pool, scratch_pool));
4913251881Speter
4914251881Speter  /* With WC-NG we need a valid repository root */
4915251881Speter  SVN_ERR_ASSERT(repos_root != NULL && repos_uuid != NULL);
4916251881Speter
4917251881Speter  /* Disallow a switch operation to change the repository root of the target,
4918251881Speter     if that is known. */
4919251881Speter  if (switch_url && !svn_uri__is_ancestor(repos_root, switch_url))
4920251881Speter    return svn_error_createf(SVN_ERR_WC_INVALID_SWITCH, NULL,
4921251881Speter                             _("'%s'\nis not the same repository as\n'%s'"),
4922251881Speter                             switch_url, repos_root);
4923251881Speter
4924251881Speter  /* Construct an edit baton. */
4925251881Speter  eb = apr_pcalloc(edit_pool, sizeof(*eb));
4926251881Speter  eb->pool                     = edit_pool;
4927251881Speter  eb->use_commit_times         = use_commit_times;
4928251881Speter  eb->target_revision          = target_revision;
4929251881Speter  eb->repos_root               = repos_root;
4930251881Speter  eb->repos_uuid               = repos_uuid;
4931251881Speter  eb->db                       = db;
4932251881Speter  eb->target_basename          = target_basename;
4933251881Speter  eb->anchor_abspath           = anchor_abspath;
4934251881Speter  eb->wcroot_iprops            = wcroot_iprops;
4935251881Speter
4936251881Speter  SVN_ERR(svn_wc__db_get_wcroot(&eb->wcroot_abspath, db, anchor_abspath,
4937251881Speter                                edit_pool, scratch_pool));
4938251881Speter
4939251881Speter  if (switch_url)
4940289180Speter    eb->switch_repos_relpath =
4941251881Speter      svn_uri_skip_ancestor(repos_root, switch_url, scratch_pool);
4942251881Speter  else
4943289180Speter    eb->switch_repos_relpath = NULL;
4944251881Speter
4945251881Speter  if (svn_path_is_empty(target_basename))
4946251881Speter    eb->target_abspath = eb->anchor_abspath;
4947251881Speter  else
4948251881Speter    eb->target_abspath = svn_dirent_join(eb->anchor_abspath, target_basename,
4949251881Speter                                         edit_pool);
4950251881Speter
4951251881Speter  eb->requested_depth          = depth;
4952251881Speter  eb->depth_is_sticky          = depth_is_sticky;
4953251881Speter  eb->notify_func              = notify_func;
4954251881Speter  eb->notify_baton             = notify_baton;
4955251881Speter  eb->external_func            = external_func;
4956251881Speter  eb->external_baton           = external_baton;
4957251881Speter  eb->diff3_cmd                = diff3_cmd;
4958251881Speter  eb->cancel_func              = cancel_func;
4959251881Speter  eb->cancel_baton             = cancel_baton;
4960251881Speter  eb->conflict_func            = conflict_func;
4961251881Speter  eb->conflict_baton           = conflict_baton;
4962251881Speter  eb->allow_unver_obstructions = allow_unver_obstructions;
4963251881Speter  eb->adds_as_modification     = adds_as_modification;
4964251881Speter  eb->clean_checkout           = clean_checkout;
4965251881Speter  eb->skipped_trees            = apr_hash_make(edit_pool);
4966251881Speter  eb->dir_dirents              = apr_hash_make(edit_pool);
4967251881Speter  eb->ext_patterns             = preserved_exts;
4968251881Speter
4969251881Speter  apr_pool_cleanup_register(edit_pool, eb, cleanup_edit_baton,
4970251881Speter                            apr_pool_cleanup_null);
4971251881Speter
4972251881Speter  /* Construct an editor. */
4973251881Speter  tree_editor->set_target_revision = set_target_revision;
4974251881Speter  tree_editor->open_root = open_root;
4975251881Speter  tree_editor->delete_entry = delete_entry;
4976251881Speter  tree_editor->add_directory = add_directory;
4977251881Speter  tree_editor->open_directory = open_directory;
4978251881Speter  tree_editor->change_dir_prop = change_dir_prop;
4979251881Speter  tree_editor->close_directory = close_directory;
4980251881Speter  tree_editor->absent_directory = absent_directory;
4981251881Speter  tree_editor->add_file = add_file;
4982251881Speter  tree_editor->open_file = open_file;
4983251881Speter  tree_editor->apply_textdelta = apply_textdelta;
4984251881Speter  tree_editor->change_file_prop = change_file_prop;
4985251881Speter  tree_editor->close_file = close_file;
4986251881Speter  tree_editor->absent_file = absent_file;
4987251881Speter  tree_editor->close_edit = close_edit;
4988251881Speter
4989251881Speter  /* Fiddle with the type system. */
4990251881Speter  inner_editor = tree_editor;
4991251881Speter  inner_baton = eb;
4992251881Speter
4993251881Speter  if (!depth_is_sticky
4994251881Speter      && depth != svn_depth_unknown
4995251881Speter      && svn_depth_empty <= depth && depth < svn_depth_infinity
4996251881Speter      && fetch_dirents_func)
4997251881Speter    {
4998251881Speter      /* We are asked to perform an update at a depth less than the ambient
4999251881Speter         depth. In this case the update won't describe additions that would
5000251881Speter         have been reported if we updated at the ambient depth. */
5001251881Speter      svn_error_t *err;
5002251881Speter      svn_node_kind_t dir_kind;
5003251881Speter      svn_wc__db_status_t dir_status;
5004251881Speter      const char *dir_repos_relpath;
5005251881Speter      svn_depth_t dir_depth;
5006251881Speter
5007251881Speter      /* we have to do this on the target of the update, not the anchor */
5008251881Speter      err = svn_wc__db_base_get_info(&dir_status, &dir_kind, NULL,
5009251881Speter                                     &dir_repos_relpath, NULL, NULL, NULL,
5010251881Speter                                     NULL, NULL, &dir_depth, NULL, NULL, NULL,
5011251881Speter                                     NULL, NULL, NULL,
5012251881Speter                                     db, eb->target_abspath,
5013251881Speter                                     scratch_pool, scratch_pool);
5014251881Speter
5015251881Speter      if (!err
5016251881Speter          && dir_kind == svn_node_dir
5017251881Speter          && dir_status == svn_wc__db_status_normal)
5018251881Speter        {
5019251881Speter          if (dir_depth > depth)
5020251881Speter            {
5021251881Speter              apr_hash_t *dirents;
5022251881Speter
5023251881Speter              /* If we switch, we should look at the new relpath */
5024289180Speter              if (eb->switch_repos_relpath)
5025289180Speter                dir_repos_relpath = eb->switch_repos_relpath;
5026251881Speter
5027251881Speter              SVN_ERR(fetch_dirents_func(fetch_dirents_baton, &dirents,
5028251881Speter                                         repos_root, dir_repos_relpath,
5029251881Speter                                         edit_pool, scratch_pool));
5030251881Speter
5031251881Speter              if (dirents != NULL && apr_hash_count(dirents))
5032251881Speter                svn_hash_sets(eb->dir_dirents,
5033251881Speter                              apr_pstrdup(edit_pool, dir_repos_relpath),
5034251881Speter                              dirents);
5035251881Speter            }
5036251881Speter
5037251881Speter          if (depth == svn_depth_immediates)
5038251881Speter            {
5039251881Speter              /* Worst case scenario of issue #3569 fix: We have to do the
5040251881Speter                 same for all existing subdirs, but then we check for
5041251881Speter                 svn_depth_empty. */
5042251881Speter              const apr_array_header_t *children;
5043251881Speter              apr_pool_t *iterpool = svn_pool_create(scratch_pool);
5044251881Speter              int i;
5045251881Speter              SVN_ERR(svn_wc__db_base_get_children(&children, db,
5046251881Speter                                                   eb->target_abspath,
5047251881Speter                                                   scratch_pool,
5048251881Speter                                                   iterpool));
5049251881Speter
5050251881Speter              for (i = 0; i < children->nelts; i++)
5051251881Speter                {
5052251881Speter                  const char *child_abspath;
5053251881Speter                  const char *child_name;
5054251881Speter
5055251881Speter                  svn_pool_clear(iterpool);
5056251881Speter
5057251881Speter                  child_name = APR_ARRAY_IDX(children, i, const char *);
5058251881Speter
5059251881Speter                  child_abspath = svn_dirent_join(eb->target_abspath,
5060251881Speter                                                  child_name, iterpool);
5061251881Speter
5062251881Speter                  SVN_ERR(svn_wc__db_base_get_info(&dir_status, &dir_kind,
5063251881Speter                                                   NULL, &dir_repos_relpath,
5064251881Speter                                                   NULL, NULL, NULL, NULL,
5065251881Speter                                                   NULL, &dir_depth, NULL,
5066251881Speter                                                   NULL, NULL, NULL, NULL,
5067251881Speter                                                   NULL,
5068251881Speter                                                   db, child_abspath,
5069251881Speter                                                   iterpool, iterpool));
5070251881Speter
5071251881Speter                  if (dir_kind == svn_node_dir
5072251881Speter                      && dir_status == svn_wc__db_status_normal
5073251881Speter                      && dir_depth > svn_depth_empty)
5074251881Speter                    {
5075251881Speter                      apr_hash_t *dirents;
5076251881Speter
5077251881Speter                      /* If we switch, we should look at the new relpath */
5078289180Speter                      if (eb->switch_repos_relpath)
5079251881Speter                        dir_repos_relpath = svn_relpath_join(
5080289180Speter                                                eb->switch_repos_relpath,
5081251881Speter                                                child_name, iterpool);
5082251881Speter
5083251881Speter                      SVN_ERR(fetch_dirents_func(fetch_dirents_baton, &dirents,
5084251881Speter                                                 repos_root, dir_repos_relpath,
5085251881Speter                                                 edit_pool, iterpool));
5086251881Speter
5087251881Speter                      if (dirents != NULL && apr_hash_count(dirents))
5088251881Speter                        svn_hash_sets(eb->dir_dirents,
5089251881Speter                                      apr_pstrdup(edit_pool,
5090251881Speter                                                  dir_repos_relpath),
5091251881Speter                                      dirents);
5092251881Speter                    }
5093251881Speter                }
5094251881Speter            }
5095251881Speter        }
5096251881Speter      else if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
5097251881Speter        svn_error_clear(err);
5098251881Speter      else
5099251881Speter        SVN_ERR(err);
5100251881Speter    }
5101251881Speter
5102251881Speter  /* We need to limit the scope of our operation to the ambient depths
5103251881Speter     present in the working copy already, but only if the requested
5104251881Speter     depth is not sticky. If a depth was explicitly requested,
5105251881Speter     libsvn_delta/depth_filter_editor.c will ensure that we never see
5106251881Speter     editor calls that extend beyond the scope of the requested depth.
5107251881Speter     But even what we do so might extend beyond the scope of our
5108251881Speter     ambient depth.  So we use another filtering editor to avoid
5109251881Speter     modifying the ambient working copy depth when not asked to do so.
5110251881Speter     (This can also be skipped if the server understands depth.) */
5111251881Speter  if (!server_performs_filtering
5112251881Speter      && !depth_is_sticky)
5113251881Speter    SVN_ERR(svn_wc__ambient_depth_filter_editor(&inner_editor,
5114251881Speter                                                &inner_baton,
5115251881Speter                                                db,
5116251881Speter                                                anchor_abspath,
5117251881Speter                                                target_basename,
5118251881Speter                                                inner_editor,
5119251881Speter                                                inner_baton,
5120251881Speter                                                result_pool));
5121251881Speter
5122251881Speter  SVN_ERR(svn_delta_get_cancellation_editor(cancel_func,
5123251881Speter                                            cancel_baton,
5124251881Speter                                            inner_editor,
5125251881Speter                                            inner_baton,
5126251881Speter                                            editor,
5127251881Speter                                            edit_baton,
5128251881Speter                                            result_pool));
5129251881Speter
5130251881Speter  sfb = apr_palloc(result_pool, sizeof(*sfb));
5131251881Speter  sfb->db = db;
5132251881Speter  sfb->base_abspath = eb->anchor_abspath;
5133251881Speter  sfb->fetch_base = TRUE;
5134251881Speter
5135251881Speter  shim_callbacks->fetch_kind_func = svn_wc__fetch_kind_func;
5136251881Speter  shim_callbacks->fetch_props_func = svn_wc__fetch_props_func;
5137251881Speter  shim_callbacks->fetch_base_func = svn_wc__fetch_base_func;
5138251881Speter  shim_callbacks->fetch_baton = sfb;
5139251881Speter
5140251881Speter  SVN_ERR(svn_editor__insert_shims(editor, edit_baton, *editor, *edit_baton,
5141251881Speter                                   NULL, NULL, shim_callbacks,
5142251881Speter                                   result_pool, scratch_pool));
5143251881Speter
5144251881Speter  return SVN_NO_ERROR;
5145251881Speter}
5146251881Speter
5147251881Speter
5148251881Spetersvn_error_t *
5149251881Spetersvn_wc__get_update_editor(const svn_delta_editor_t **editor,
5150251881Speter                          void **edit_baton,
5151251881Speter                          svn_revnum_t *target_revision,
5152251881Speter                          svn_wc_context_t *wc_ctx,
5153251881Speter                          const char *anchor_abspath,
5154251881Speter                          const char *target_basename,
5155251881Speter                          apr_hash_t *wcroot_iprops,
5156251881Speter                          svn_boolean_t use_commit_times,
5157251881Speter                          svn_depth_t depth,
5158251881Speter                          svn_boolean_t depth_is_sticky,
5159251881Speter                          svn_boolean_t allow_unver_obstructions,
5160251881Speter                          svn_boolean_t adds_as_modification,
5161251881Speter                          svn_boolean_t server_performs_filtering,
5162251881Speter                          svn_boolean_t clean_checkout,
5163251881Speter                          const char *diff3_cmd,
5164251881Speter                          const apr_array_header_t *preserved_exts,
5165251881Speter                          svn_wc_dirents_func_t fetch_dirents_func,
5166251881Speter                          void *fetch_dirents_baton,
5167251881Speter                          svn_wc_conflict_resolver_func2_t conflict_func,
5168251881Speter                          void *conflict_baton,
5169251881Speter                          svn_wc_external_update_t external_func,
5170251881Speter                          void *external_baton,
5171251881Speter                          svn_cancel_func_t cancel_func,
5172251881Speter                          void *cancel_baton,
5173251881Speter                          svn_wc_notify_func2_t notify_func,
5174251881Speter                          void *notify_baton,
5175251881Speter                          apr_pool_t *result_pool,
5176251881Speter                          apr_pool_t *scratch_pool)
5177251881Speter{
5178251881Speter  return make_editor(target_revision, wc_ctx->db, anchor_abspath,
5179251881Speter                     target_basename, wcroot_iprops, use_commit_times,
5180251881Speter                     NULL, depth, depth_is_sticky, allow_unver_obstructions,
5181251881Speter                     adds_as_modification, server_performs_filtering,
5182251881Speter                     clean_checkout,
5183251881Speter                     notify_func, notify_baton,
5184251881Speter                     cancel_func, cancel_baton,
5185251881Speter                     fetch_dirents_func, fetch_dirents_baton,
5186251881Speter                     conflict_func, conflict_baton,
5187251881Speter                     external_func, external_baton,
5188251881Speter                     diff3_cmd, preserved_exts, editor, edit_baton,
5189251881Speter                     result_pool, scratch_pool);
5190251881Speter}
5191251881Speter
5192251881Spetersvn_error_t *
5193251881Spetersvn_wc__get_switch_editor(const svn_delta_editor_t **editor,
5194251881Speter                          void **edit_baton,
5195251881Speter                          svn_revnum_t *target_revision,
5196251881Speter                          svn_wc_context_t *wc_ctx,
5197251881Speter                          const char *anchor_abspath,
5198251881Speter                          const char *target_basename,
5199251881Speter                          const char *switch_url,
5200251881Speter                          apr_hash_t *wcroot_iprops,
5201251881Speter                          svn_boolean_t use_commit_times,
5202251881Speter                          svn_depth_t depth,
5203251881Speter                          svn_boolean_t depth_is_sticky,
5204251881Speter                          svn_boolean_t allow_unver_obstructions,
5205251881Speter                          svn_boolean_t server_performs_filtering,
5206251881Speter                          const char *diff3_cmd,
5207251881Speter                          const apr_array_header_t *preserved_exts,
5208251881Speter                          svn_wc_dirents_func_t fetch_dirents_func,
5209251881Speter                          void *fetch_dirents_baton,
5210251881Speter                          svn_wc_conflict_resolver_func2_t conflict_func,
5211251881Speter                          void *conflict_baton,
5212251881Speter                          svn_wc_external_update_t external_func,
5213251881Speter                          void *external_baton,
5214251881Speter                          svn_cancel_func_t cancel_func,
5215251881Speter                          void *cancel_baton,
5216251881Speter                          svn_wc_notify_func2_t notify_func,
5217251881Speter                          void *notify_baton,
5218251881Speter                          apr_pool_t *result_pool,
5219251881Speter                          apr_pool_t *scratch_pool)
5220251881Speter{
5221251881Speter  SVN_ERR_ASSERT(switch_url && svn_uri_is_canonical(switch_url, scratch_pool));
5222251881Speter
5223251881Speter  return make_editor(target_revision, wc_ctx->db, anchor_abspath,
5224251881Speter                     target_basename, wcroot_iprops, use_commit_times,
5225251881Speter                     switch_url,
5226251881Speter                     depth, depth_is_sticky, allow_unver_obstructions,
5227251881Speter                     FALSE /* adds_as_modification */,
5228251881Speter                     server_performs_filtering,
5229251881Speter                     FALSE /* clean_checkout */,
5230251881Speter                     notify_func, notify_baton,
5231251881Speter                     cancel_func, cancel_baton,
5232251881Speter                     fetch_dirents_func, fetch_dirents_baton,
5233251881Speter                     conflict_func, conflict_baton,
5234251881Speter                     external_func, external_baton,
5235251881Speter                     diff3_cmd, preserved_exts,
5236251881Speter                     editor, edit_baton,
5237251881Speter                     result_pool, scratch_pool);
5238251881Speter}
5239251881Speter
5240251881Speter
5241251881Speter
5242251881Speter/* ### Note that this function is completely different from the rest of the
5243251881Speter       update editor in what it updates. The update editor changes only BASE
5244251881Speter       and ACTUAL and this function just changes WORKING and ACTUAL.
5245251881Speter
5246251881Speter       In the entries world this function shared a lot of code with the
5247251881Speter       update editor but in the wonderful new WC-NG world it will probably
5248251881Speter       do more and more by itself and would be more logically grouped with
5249251881Speter       the add/copy functionality in adm_ops.c and copy.c. */
5250251881Spetersvn_error_t *
5251251881Spetersvn_wc_add_repos_file4(svn_wc_context_t *wc_ctx,
5252251881Speter                       const char *local_abspath,
5253251881Speter                       svn_stream_t *new_base_contents,
5254251881Speter                       svn_stream_t *new_contents,
5255251881Speter                       apr_hash_t *new_base_props,
5256251881Speter                       apr_hash_t *new_props,
5257251881Speter                       const char *copyfrom_url,
5258251881Speter                       svn_revnum_t copyfrom_rev,
5259251881Speter                       svn_cancel_func_t cancel_func,
5260251881Speter                       void *cancel_baton,
5261251881Speter                       apr_pool_t *scratch_pool)
5262251881Speter{
5263251881Speter  svn_wc__db_t *db = wc_ctx->db;
5264251881Speter  const char *dir_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
5265251881Speter  svn_wc__db_status_t status;
5266251881Speter  svn_node_kind_t kind;
5267251881Speter  const char *tmp_text_base_abspath;
5268251881Speter  svn_checksum_t *new_text_base_md5_checksum;
5269251881Speter  svn_checksum_t *new_text_base_sha1_checksum;
5270251881Speter  const char *source_abspath = NULL;
5271251881Speter  svn_skel_t *all_work_items = NULL;
5272251881Speter  svn_skel_t *work_item;
5273251881Speter  const char *repos_root_url;
5274251881Speter  const char *repos_uuid;
5275251881Speter  const char *original_repos_relpath;
5276251881Speter  svn_revnum_t changed_rev;
5277251881Speter  apr_time_t changed_date;
5278251881Speter  const char *changed_author;
5279289180Speter  svn_stream_t *tmp_base_contents;
5280289180Speter  svn_wc__db_install_data_t *install_data;
5281251881Speter  svn_error_t *err;
5282251881Speter  apr_pool_t *pool = scratch_pool;
5283251881Speter
5284251881Speter  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5285251881Speter  SVN_ERR_ASSERT(new_base_contents != NULL);
5286251881Speter  SVN_ERR_ASSERT(new_base_props != NULL);
5287251881Speter
5288251881Speter  /* We should have a write lock on this file's parent directory.  */
5289251881Speter  SVN_ERR(svn_wc__write_check(db, dir_abspath, pool));
5290251881Speter
5291251881Speter  err = svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5292251881Speter                             NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5293251881Speter                             NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5294251881Speter                             NULL, NULL, NULL,
5295251881Speter                             db, local_abspath, scratch_pool, scratch_pool);
5296251881Speter
5297251881Speter  if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
5298251881Speter    return svn_error_trace(err);
5299251881Speter  else if(err)
5300251881Speter    svn_error_clear(err);
5301251881Speter  else
5302251881Speter    switch (status)
5303251881Speter      {
5304251881Speter        case svn_wc__db_status_not_present:
5305251881Speter        case svn_wc__db_status_deleted:
5306251881Speter          break;
5307251881Speter        default:
5308251881Speter          return svn_error_createf(SVN_ERR_ENTRY_EXISTS, NULL,
5309251881Speter                                   _("Node '%s' exists."),
5310251881Speter                                   svn_dirent_local_style(local_abspath,
5311251881Speter                                                          scratch_pool));
5312251881Speter      }
5313251881Speter
5314251881Speter  SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, &repos_root_url,
5315251881Speter                               &repos_uuid, NULL, NULL, NULL, NULL, NULL, NULL,
5316251881Speter                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5317251881Speter                               NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5318251881Speter                               db, dir_abspath, scratch_pool, scratch_pool));
5319251881Speter
5320251881Speter  switch (status)
5321251881Speter    {
5322251881Speter      case svn_wc__db_status_normal:
5323251881Speter      case svn_wc__db_status_added:
5324251881Speter        break;
5325251881Speter      case svn_wc__db_status_deleted:
5326251881Speter        return
5327251881Speter          svn_error_createf(SVN_ERR_WC_SCHEDULE_CONFLICT, NULL,
5328251881Speter                            _("Can't add '%s' to a parent directory"
5329251881Speter                              " scheduled for deletion"),
5330251881Speter                            svn_dirent_local_style(local_abspath,
5331251881Speter                                                   scratch_pool));
5332251881Speter      default:
5333251881Speter        return svn_error_createf(SVN_ERR_ENTRY_NOT_FOUND, err,
5334251881Speter                                 _("Can't find parent directory's node while"
5335251881Speter                                   " trying to add '%s'"),
5336251881Speter                                 svn_dirent_local_style(local_abspath,
5337251881Speter                                                        scratch_pool));
5338251881Speter    }
5339251881Speter  if (kind != svn_node_dir)
5340251881Speter    return svn_error_createf(SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
5341251881Speter                             _("Can't schedule an addition of '%s'"
5342251881Speter                               " below a not-directory node"),
5343251881Speter                             svn_dirent_local_style(local_abspath,
5344251881Speter                                                    scratch_pool));
5345251881Speter
5346251881Speter  /* Fabricate the anticipated new URL of the target and check the
5347251881Speter     copyfrom URL to be in the same repository. */
5348251881Speter  if (copyfrom_url != NULL)
5349251881Speter    {
5350251881Speter      /* Find the repository_root via the parent directory, which
5351251881Speter         is always versioned before this function is called */
5352251881Speter
5353251881Speter      if (!repos_root_url)
5354251881Speter        {
5355251881Speter          /* The parent is an addition, scan upwards to find the right info */
5356251881Speter          SVN_ERR(svn_wc__db_scan_addition(NULL, NULL, NULL,
5357251881Speter                                           &repos_root_url, &repos_uuid,
5358251881Speter                                           NULL, NULL, NULL, NULL,
5359251881Speter                                           wc_ctx->db, dir_abspath,
5360251881Speter                                           scratch_pool, scratch_pool));
5361251881Speter        }
5362251881Speter      SVN_ERR_ASSERT(repos_root_url);
5363251881Speter
5364251881Speter      original_repos_relpath =
5365251881Speter          svn_uri_skip_ancestor(repos_root_url, copyfrom_url, scratch_pool);
5366251881Speter
5367251881Speter      if (!original_repos_relpath)
5368251881Speter        return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
5369251881Speter                                 _("Copyfrom-url '%s' has different repository"
5370251881Speter                                   " root than '%s'"),
5371251881Speter                                 copyfrom_url, repos_root_url);
5372251881Speter    }
5373251881Speter  else
5374251881Speter    {
5375251881Speter      original_repos_relpath = NULL;
5376251881Speter      copyfrom_rev = SVN_INVALID_REVNUM;  /* Just to be sure.  */
5377251881Speter    }
5378251881Speter
5379251881Speter  /* Set CHANGED_* to reflect the entry props in NEW_BASE_PROPS, and
5380251881Speter     filter NEW_BASE_PROPS so it contains only regular props. */
5381251881Speter  {
5382251881Speter    apr_array_header_t *regular_props;
5383251881Speter    apr_array_header_t *entry_props;
5384251881Speter
5385251881Speter    SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(new_base_props, pool),
5386251881Speter                                 &entry_props, NULL, &regular_props,
5387251881Speter                                 pool));
5388251881Speter
5389251881Speter    /* Put regular props back into a hash table. */
5390251881Speter    new_base_props = svn_prop_array_to_hash(regular_props, pool);
5391251881Speter
5392251881Speter    /* Get the change_* info from the entry props.  */
5393251881Speter    SVN_ERR(accumulate_last_change(&changed_rev,
5394251881Speter                                   &changed_date,
5395251881Speter                                   &changed_author,
5396251881Speter                                   entry_props, pool, pool));
5397251881Speter  }
5398251881Speter
5399251881Speter  /* Copy NEW_BASE_CONTENTS into a temporary file so our log can refer to
5400251881Speter     it, and set TMP_TEXT_BASE_ABSPATH to its path.  Compute its
5401251881Speter     NEW_TEXT_BASE_MD5_CHECKSUM and NEW_TEXT_BASE_SHA1_CHECKSUM as we copy. */
5402289180Speter  if (copyfrom_url)
5403289180Speter    {
5404289180Speter      SVN_ERR(svn_wc__db_pristine_prepare_install(&tmp_base_contents,
5405289180Speter                                                  &install_data,
5406289180Speter                                                  &new_text_base_sha1_checksum,
5407289180Speter                                                  &new_text_base_md5_checksum,
5408289180Speter                                                  wc_ctx->db, local_abspath,
5409289180Speter                                                  scratch_pool, scratch_pool));
5410289180Speter    }
5411289180Speter  else
5412289180Speter    {
5413289180Speter      const char *tmp_dir_abspath;
5414251881Speter
5415289180Speter      /* We are not installing a PRISTINE file, but we use the same code to
5416289180Speter         create whatever we want to install */
5417251881Speter
5418289180Speter      SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&tmp_dir_abspath,
5419289180Speter                                             db, dir_abspath,
5420289180Speter                                             scratch_pool, scratch_pool));
5421289180Speter
5422289180Speter      SVN_ERR(svn_stream_open_unique(&tmp_base_contents, &tmp_text_base_abspath,
5423289180Speter                                     tmp_dir_abspath, svn_io_file_del_none,
5424289180Speter                                     scratch_pool, scratch_pool));
5425289180Speter
5426289180Speter      new_text_base_sha1_checksum = NULL;
5427289180Speter      new_text_base_md5_checksum = NULL;
5428289180Speter    }
5429289180Speter  SVN_ERR(svn_stream_copy3(new_base_contents, tmp_base_contents,
5430289180Speter                           cancel_func, cancel_baton, pool));
5431289180Speter
5432251881Speter  /* If the caller gave us a new working file, copy it to a safe (temporary)
5433251881Speter     location and set SOURCE_ABSPATH to that path. We'll then translate/copy
5434251881Speter     that into place after the node's state has been created.  */
5435251881Speter  if (new_contents)
5436251881Speter    {
5437251881Speter      const char *temp_dir_abspath;
5438251881Speter      svn_stream_t *tmp_contents;
5439251881Speter
5440251881Speter      SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath, db,
5441251881Speter                                             local_abspath, pool, pool));
5442251881Speter      SVN_ERR(svn_stream_open_unique(&tmp_contents, &source_abspath,
5443251881Speter                                     temp_dir_abspath, svn_io_file_del_none,
5444251881Speter                                     pool, pool));
5445251881Speter      SVN_ERR(svn_stream_copy3(new_contents, tmp_contents,
5446251881Speter                               cancel_func, cancel_baton, pool));
5447251881Speter    }
5448251881Speter
5449251881Speter  /* Install new text base for copied files. Added files do NOT have a
5450251881Speter     text base.  */
5451251881Speter  if (copyfrom_url != NULL)
5452251881Speter    {
5453289180Speter      SVN_ERR(svn_wc__db_pristine_install(install_data,
5454251881Speter                                          new_text_base_sha1_checksum,
5455251881Speter                                          new_text_base_md5_checksum, pool));
5456251881Speter    }
5457251881Speter  else
5458251881Speter    {
5459251881Speter      /* ### There's something wrong around here.  Sometimes (merge from a
5460251881Speter         foreign repository, at least) we are called with copyfrom_url =
5461251881Speter         NULL and an empty new_base_contents (and an empty set of
5462251881Speter         new_base_props).  Why an empty "new base"?
5463251881Speter
5464251881Speter         That happens in merge_tests.py 54,87,88,89,143.
5465251881Speter
5466251881Speter         In that case, having been given this supposed "new base" file, we
5467251881Speter         copy it and calculate its checksum but do not install it.  Why?
5468251881Speter         That must be wrong.
5469251881Speter
5470251881Speter         To crudely work around one issue with this, that we shouldn't
5471251881Speter         record a checksum in the database if we haven't installed the
5472251881Speter         corresponding pristine text, for now we'll just set the checksum
5473251881Speter         to NULL.
5474251881Speter
5475251881Speter         The proper solution is probably more like: the caller should pass
5476251881Speter         NULL for the missing information, and this function should learn to
5477251881Speter         handle that. */
5478251881Speter
5479251881Speter      new_text_base_sha1_checksum = NULL;
5480251881Speter      new_text_base_md5_checksum = NULL;
5481251881Speter    }
5482251881Speter
5483251881Speter  /* For added files without NEW_CONTENTS, then generate the working file
5484251881Speter     from the provided "pristine" contents.  */
5485251881Speter  if (new_contents == NULL && copyfrom_url == NULL)
5486251881Speter    source_abspath = tmp_text_base_abspath;
5487251881Speter
5488251881Speter  {
5489251881Speter    svn_boolean_t record_fileinfo;
5490251881Speter
5491251881Speter    /* If new contents were provided, then we do NOT want to record the
5492251881Speter       file information. We assume the new contents do not match the
5493251881Speter       "proper" values for RECORDED_SIZE and RECORDED_TIME.  */
5494251881Speter    record_fileinfo = (new_contents == NULL);
5495251881Speter
5496251881Speter    /* Install the working copy file (with appropriate translation) from
5497251881Speter       the appropriate source. SOURCE_ABSPATH will be NULL, indicating an
5498251881Speter       installation from the pristine (available for copied/moved files),
5499251881Speter       or it will specify a temporary file where we placed a "pristine"
5500251881Speter       (for an added file) or a detranslated local-mods file.  */
5501251881Speter    SVN_ERR(svn_wc__wq_build_file_install(&work_item,
5502251881Speter                                          db, local_abspath,
5503251881Speter                                          source_abspath,
5504251881Speter                                          FALSE /* use_commit_times */,
5505251881Speter                                          record_fileinfo,
5506251881Speter                                          pool, pool));
5507251881Speter    all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool);
5508251881Speter
5509251881Speter    /* If we installed from somewhere besides the official pristine, then
5510251881Speter       it is a temporary file, which needs to be removed.  */
5511251881Speter    if (source_abspath != NULL)
5512251881Speter      {
5513251881Speter        SVN_ERR(svn_wc__wq_build_file_remove(&work_item, db, local_abspath,
5514251881Speter                                             source_abspath,
5515251881Speter                                             pool, pool));
5516251881Speter        all_work_items = svn_wc__wq_merge(all_work_items, work_item, pool);
5517251881Speter      }
5518251881Speter  }
5519251881Speter
5520251881Speter  SVN_ERR(svn_wc__db_op_copy_file(db, local_abspath,
5521251881Speter                                  new_base_props,
5522251881Speter                                  changed_rev,
5523251881Speter                                  changed_date,
5524251881Speter                                  changed_author,
5525251881Speter                                  original_repos_relpath,
5526251881Speter                                  original_repos_relpath ? repos_root_url
5527251881Speter                                                         : NULL,
5528251881Speter                                  original_repos_relpath ? repos_uuid : NULL,
5529251881Speter                                  copyfrom_rev,
5530251881Speter                                  new_text_base_sha1_checksum,
5531251881Speter                                  TRUE,
5532251881Speter                                  new_props,
5533251881Speter                                  FALSE /* is_move */,
5534251881Speter                                  NULL /* conflict */,
5535251881Speter                                  all_work_items,
5536251881Speter                                  pool));
5537251881Speter
5538251881Speter  return svn_error_trace(svn_wc__wq_run(db, dir_abspath,
5539251881Speter                                        cancel_func, cancel_baton,
5540251881Speter                                        pool));
5541251881Speter}
5542251881Speter
5543251881Spetersvn_error_t *
5544251881Spetersvn_wc__complete_directory_add(svn_wc_context_t *wc_ctx,
5545251881Speter                               const char *local_abspath,
5546251881Speter                               apr_hash_t *new_original_props,
5547251881Speter                               const char *copyfrom_url,
5548251881Speter                               svn_revnum_t copyfrom_rev,
5549251881Speter                               apr_pool_t *scratch_pool)
5550251881Speter{
5551251881Speter  svn_wc__db_status_t status;
5552251881Speter  svn_node_kind_t kind;
5553251881Speter  const char *original_repos_relpath;
5554251881Speter  const char *original_root_url;
5555251881Speter  const char *original_uuid;
5556251881Speter  svn_boolean_t had_props;
5557251881Speter  svn_boolean_t props_mod;
5558251881Speter
5559251881Speter  svn_revnum_t original_revision;
5560251881Speter  svn_revnum_t changed_rev;
5561251881Speter  apr_time_t changed_date;
5562251881Speter  const char *changed_author;
5563251881Speter
5564251881Speter  SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
5565251881Speter                               NULL, NULL, NULL, NULL, NULL,
5566251881Speter                               &original_repos_relpath, &original_root_url,
5567251881Speter                               &original_uuid, &original_revision, NULL, NULL,
5568251881Speter                               NULL, NULL, NULL, NULL, &had_props, &props_mod,
5569251881Speter                               NULL, NULL, NULL,
5570251881Speter                               wc_ctx->db, local_abspath,
5571251881Speter                               scratch_pool, scratch_pool));
5572251881Speter
5573251881Speter  if (status != svn_wc__db_status_added
5574251881Speter      || kind != svn_node_dir
5575251881Speter      || had_props
5576251881Speter      || props_mod
5577251881Speter      || !original_repos_relpath)
5578251881Speter    {
5579251881Speter      return svn_error_createf(
5580251881Speter                    SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
5581251881Speter                    _("'%s' is not an unmodified copied directory"),
5582251881Speter                    svn_dirent_local_style(local_abspath, scratch_pool));
5583251881Speter    }
5584251881Speter  if (original_revision != copyfrom_rev
5585251881Speter      || strcmp(copyfrom_url,
5586251881Speter                 svn_path_url_add_component2(original_root_url,
5587251881Speter                                             original_repos_relpath,
5588251881Speter                                             scratch_pool)))
5589251881Speter    {
5590251881Speter      return svn_error_createf(
5591251881Speter                    SVN_ERR_WC_COPYFROM_PATH_NOT_FOUND, NULL,
5592251881Speter                    _("Copyfrom '%s' doesn't match original location of '%s'"),
5593251881Speter                    copyfrom_url,
5594251881Speter                    svn_dirent_local_style(local_abspath, scratch_pool));
5595251881Speter    }
5596251881Speter
5597251881Speter  {
5598251881Speter    apr_array_header_t *regular_props;
5599251881Speter    apr_array_header_t *entry_props;
5600251881Speter
5601251881Speter    SVN_ERR(svn_categorize_props(svn_prop_hash_to_array(new_original_props,
5602251881Speter                                                        scratch_pool),
5603251881Speter                                 &entry_props, NULL, &regular_props,
5604251881Speter                                 scratch_pool));
5605251881Speter
5606251881Speter    /* Put regular props back into a hash table. */
5607251881Speter    new_original_props = svn_prop_array_to_hash(regular_props, scratch_pool);
5608251881Speter
5609251881Speter    /* Get the change_* info from the entry props.  */
5610251881Speter    SVN_ERR(accumulate_last_change(&changed_rev,
5611251881Speter                                   &changed_date,
5612251881Speter                                   &changed_author,
5613251881Speter                                   entry_props, scratch_pool, scratch_pool));
5614251881Speter  }
5615251881Speter
5616251881Speter  return svn_error_trace(
5617251881Speter            svn_wc__db_op_copy_dir(wc_ctx->db, local_abspath,
5618251881Speter                                   new_original_props,
5619251881Speter                                   changed_rev, changed_date, changed_author,
5620251881Speter                                   original_repos_relpath, original_root_url,
5621251881Speter                                   original_uuid, original_revision,
5622251881Speter                                   NULL /* children */,
5623286506Speter                                   svn_depth_infinity,
5624251881Speter                                   FALSE /* is_move */,
5625251881Speter                                   NULL /* conflict */,
5626251881Speter                                   NULL /* work_items */,
5627251881Speter                                   scratch_pool));
5628251881Speter}
5629