2 * workqueue.c : manipulating work queue items
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
21 * ====================================================================
24 #include <apr_pools.h>
26 #include "svn_private_config.h"
27 #include "svn_types.h"
28 #include "svn_pools.h"
29 #include "svn_dirent_uri.h"
30 #include "svn_subst.h"
36 #include "workqueue.h"
37 #include "adm_files.h"
38 #include "conflicts.h"
39 #include "translate.h"
41 #include "private/svn_io_private.h"
42 #include "private/svn_skel.h"
45 /* Workqueue operation names. */
46 #define OP_FILE_COMMIT "file-commit"
47 #define OP_FILE_INSTALL "file-install"
48 #define OP_FILE_REMOVE "file-remove"
49 #define OP_FILE_MOVE "file-move"
50 #define OP_FILE_COPY_TRANSLATED "file-translate"
51 #define OP_SYNC_FILE_FLAGS "sync-file-flags"
52 #define OP_PREJ_INSTALL "prej-install"
53 #define OP_DIRECTORY_REMOVE "dir-remove"
54 #define OP_DIRECTORY_INSTALL "dir-install"
56 #define OP_POSTUPGRADE "postupgrade"
59 #define OP_BASE_REMOVE "base-remove"
60 #define OP_RECORD_FILEINFO "record-fileinfo"
61 #define OP_TMP_SET_TEXT_CONFLICT_MARKERS "tmp-set-text-conflict-markers"
62 #define OP_TMP_SET_PROPERTY_CONFLICT_MARKER "tmp-set-property-conflict-marker"
64 /* For work queue debugging. Generates output about its operation. */
65 /* #define SVN_DEBUG_WORK_QUEUE */
67 typedef struct work_item_baton_t work_item_baton_t;
69 struct work_item_dispatch {
71 svn_error_t *(*func)(work_item_baton_t *wqb,
73 const svn_skel_t *work_item,
74 const char *wri_abspath,
75 svn_cancel_func_t cancel_func,
77 apr_pool_t *scratch_pool);
80 /* Forward definition */
82 get_and_record_fileinfo(work_item_baton_t *wqb,
83 const char *local_abspath,
84 svn_boolean_t ignore_enoent,
85 apr_pool_t *scratch_pool);
87 /* ------------------------------------------------------------------------ */
90 /* Removes a BASE_NODE and all it's data, leaving any adds and copies as is.
91 Do this as a depth first traversal to make sure than any parent still exists
95 /* Process the OP_REMOVE_BASE work item WORK_ITEM.
96 * See svn_wc__wq_build_remove_base() which generates this work item.
97 * Implements (struct work_item_dispatch).func. */
99 run_base_remove(work_item_baton_t *wqb,
101 const svn_skel_t *work_item,
102 const char *wri_abspath,
103 svn_cancel_func_t cancel_func,
105 apr_pool_t *scratch_pool)
107 const svn_skel_t *arg1 = work_item->children->next;
108 const char *local_relpath;
109 const char *local_abspath;
110 svn_revnum_t not_present_rev = SVN_INVALID_REVNUM;
113 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
114 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
115 local_relpath, scratch_pool, scratch_pool));
116 SVN_ERR(svn_skel__parse_int(&val, arg1->next, scratch_pool));
118 if (arg1->next->next)
120 not_present_rev = (svn_revnum_t)val;
122 SVN_ERR(svn_skel__parse_int(&val, arg1->next->next, scratch_pool));
126 svn_boolean_t keep_not_present;
128 SVN_ERR_ASSERT(SVN_WC__VERSION <= 28); /* Case unused in later versions*/
130 keep_not_present = (val != 0);
132 if (keep_not_present)
134 SVN_ERR(svn_wc__db_base_get_info(NULL, NULL,
135 ¬_present_rev, NULL,
137 NULL, NULL, NULL, NULL, NULL, NULL,
140 scratch_pool, scratch_pool));
144 SVN_ERR(svn_wc__db_base_remove(db, local_abspath,
145 FALSE /* keep_as_working */,
146 SVN_IS_VALID_REVNUM(not_present_rev), FALSE,
148 NULL, NULL, scratch_pool));
153 /* ------------------------------------------------------------------------ */
155 /* ------------------------------------------------------------------------ */
160 /* FILE_ABSPATH is the new text base of the newly-committed versioned file,
161 * in repository-normal form (aka "detranslated" form). Adjust the working
164 * If eol and/or keyword translation would cause the working file to
165 * change, then overwrite the working file with a translated copy of
166 * the new text base (but only if the translated copy differs from the
167 * current working file -- if they are the same, do nothing, to avoid
168 * clobbering timestamps unnecessarily).
170 * Set the working file's executability according to its svn:executable
173 * Set the working file's read-only attribute according to its properties
174 * and lock status (see svn_wc__maybe_set_read_only()).
176 * If the working file was re-translated or had its executability or
177 * read-only state changed,
178 * then set OVERWROTE_WORKING to TRUE. If the working file isn't
179 * touched at all, then set to FALSE.
181 * Use SCRATCH_POOL for any temporary allocation.
184 install_committed_file(svn_boolean_t *overwrote_working,
186 const char *file_abspath,
187 svn_cancel_func_t cancel_func,
189 apr_pool_t *scratch_pool)
192 const char *tmp_wfile;
193 svn_boolean_t special;
195 /* start off assuming that the working file isn't touched. */
196 *overwrote_working = FALSE;
198 /* In the commit, newlines and keywords may have been
199 * canonicalized and/or contracted... Or they may not have
200 * been. It's kind of hard to know. Here's how we find out:
202 * 1. Make a translated tmp copy of the committed text base,
203 * translated according to the versioned file's properties.
204 * Or, if no committed text base exists (the commit must have
205 * been a propchange only), make a translated tmp copy of the
207 * 2. Compare the translated tmpfile to the working file.
208 * 3. If different, copy the tmpfile over working file.
210 * This means we only rewrite the working file if we absolutely
211 * have to, which is good because it avoids changing the file's
212 * timestamp unless necessary, so editors aren't tempted to
213 * reread the file if they don't really need to.
216 /* Copy and translate the new base-to-be file (if found, else the working
217 * file) from repository-normal form to working form, writing a new
218 * temporary file if any translation was actually done. Set TMP_WFILE to
219 * the translated file's path, which may be the source file's path if no
220 * translation was done. Set SAME to indicate whether the new working
221 * text is the same as the old working text (or TRUE if it's a special
224 const char *tmp = file_abspath;
226 /* Copy and translate, if necessary. The output file will be deleted at
227 * scratch_pool cleanup.
228 * ### That's not quite safe: we might rename the file and then maybe
229 * its path will get re-used for another temp file before pool clean-up.
230 * Instead, we should take responsibility for deleting it. */
231 SVN_ERR(svn_wc__internal_translated_file(&tmp_wfile, tmp, db,
233 SVN_WC_TRANSLATE_FROM_NF,
234 cancel_func, cancel_baton,
235 scratch_pool, scratch_pool));
237 /* If the translation is a no-op, the text base and the working copy
238 * file contain the same content, because we use the same props here
239 * as were used to detranslate from working file to text base.
241 * In that case: don't replace the working file, but make sure
242 * it has the right executable and read_write attributes set.
245 SVN_ERR(svn_wc__get_translate_info(NULL, NULL,
248 db, file_abspath, NULL, FALSE,
249 scratch_pool, scratch_pool));
250 /* Translated file returns the exact pointer if not translated. */
251 if (! special && tmp != tmp_wfile)
252 SVN_ERR(svn_io_files_contents_same_p(&same, tmp_wfile,
253 file_abspath, scratch_pool));
260 SVN_ERR(svn_io_file_rename(tmp_wfile, file_abspath, scratch_pool));
261 *overwrote_working = TRUE;
264 /* ### should be using OP_SYNC_FILE_FLAGS, or an internal version of
265 ### that here. do we need to set *OVERWROTE_WORKING? */
267 /* ### Re: OVERWROTE_WORKING, the following function is rather liberal
268 ### with setting that flag, so we should probably decide if we really
269 ### care about it when syncing flags. */
270 SVN_ERR(svn_wc__sync_flags_with_props(overwrote_working, db, file_abspath,
277 process_commit_file_install(svn_wc__db_t *db,
278 const char *local_abspath,
279 svn_cancel_func_t cancel_func,
281 apr_pool_t *scratch_pool)
283 svn_boolean_t overwrote_working;
285 /* Install the new file, which may involve expanding keywords.
286 A copy of this file should have been dropped into our `tmp/text-base'
287 directory during the commit process. Part of this process
288 involves recording the textual timestamp for this entry. We'd like
289 to just use the timestamp of the working file, but it is possible
290 that at some point during the commit, the real working file might
294 SVN_ERR(install_committed_file(&overwrote_working, db,
296 cancel_func, cancel_baton,
299 /* We will compute and modify the size and timestamp */
300 if (overwrote_working)
304 SVN_ERR(svn_io_stat(&finfo, local_abspath,
305 APR_FINFO_MIN | APR_FINFO_LINK, scratch_pool));
306 SVN_ERR(svn_wc__db_global_record_fileinfo(db, local_abspath,
307 finfo.size, finfo.mtime,
312 svn_boolean_t modified;
314 /* The working copy file hasn't been overwritten. We just
315 removed the recorded size and modification time from the nodes
316 record by calling svn_wc__db_global_commit().
318 Now we have some file in our working copy that might be what
319 we just committed, but we are not certain at this point.
321 We still have a write lock here, so we check if the file is
322 what we expect it to be and if it is the right file we update
323 the recorded information. (If it isn't we keep the null data).
325 Instead of reimplementing all this here, we just call a function
326 that already does implement this when it notices that we have the
327 right kind of lock (and we ignore the result)
329 SVN_ERR(svn_wc__internal_file_modified_p(&modified,
330 db, local_abspath, FALSE,
338 run_file_commit(work_item_baton_t *wqb,
340 const svn_skel_t *work_item,
341 const char *wri_abspath,
342 svn_cancel_func_t cancel_func,
344 apr_pool_t *scratch_pool)
346 const svn_skel_t *arg1 = work_item->children->next;
347 const char *local_relpath;
348 const char *local_abspath;
350 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
351 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
352 local_relpath, scratch_pool, scratch_pool));
354 /* We don't both parsing the other two values in the skel. */
356 return svn_error_trace(
357 process_commit_file_install(db, local_abspath,
358 cancel_func, cancel_baton,
363 svn_wc__wq_build_file_commit(svn_skel_t **work_item,
365 const char *local_abspath,
366 svn_boolean_t props_mod,
367 apr_pool_t *result_pool,
368 apr_pool_t *scratch_pool)
370 const char *local_relpath;
371 *work_item = svn_skel__make_empty_list(result_pool);
373 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath,
374 local_abspath, result_pool, scratch_pool));
376 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
378 svn_skel__prepend_str(OP_FILE_COMMIT, *work_item, result_pool);
383 /* ------------------------------------------------------------------------ */
387 run_postupgrade(work_item_baton_t *wqb,
389 const svn_skel_t *work_item,
390 const char *wri_abspath,
391 svn_cancel_func_t cancel_func,
393 apr_pool_t *scratch_pool)
395 const char *entries_path;
396 const char *format_path;
397 const char *wcroot_abspath;
400 err = svn_wc__wipe_postupgrade(wri_abspath, FALSE,
401 cancel_func, cancel_baton, scratch_pool);
402 if (err && err->apr_err == SVN_ERR_ENTRY_NOT_FOUND)
403 /* No entry, this can happen when the wq item is rerun. */
404 svn_error_clear(err);
408 SVN_ERR(svn_wc__db_get_wcroot(&wcroot_abspath, db, wri_abspath,
409 scratch_pool, scratch_pool));
411 entries_path = svn_wc__adm_child(wcroot_abspath, SVN_WC__ADM_ENTRIES,
413 format_path = svn_wc__adm_child(wcroot_abspath, SVN_WC__ADM_FORMAT,
416 /* Write the 'format' and 'entries' files.
418 ### The order may matter for some sufficiently old clients.. but
419 ### this code only runs during upgrade after the files had been
420 ### removed earlier during the upgrade. */
421 SVN_ERR(svn_io_write_atomic(format_path, SVN_WC__NON_ENTRIES_STRING,
422 sizeof(SVN_WC__NON_ENTRIES_STRING) - 1,
423 NULL, scratch_pool));
425 SVN_ERR(svn_io_write_atomic(entries_path, SVN_WC__NON_ENTRIES_STRING,
426 sizeof(SVN_WC__NON_ENTRIES_STRING) - 1,
427 NULL, scratch_pool));
433 svn_wc__wq_build_postupgrade(svn_skel_t **work_item,
434 apr_pool_t *result_pool)
436 *work_item = svn_skel__make_empty_list(result_pool);
438 svn_skel__prepend_str(OP_POSTUPGRADE, *work_item, result_pool);
443 /* ------------------------------------------------------------------------ */
445 /* OP_FILE_INSTALL */
447 /* Process the OP_FILE_INSTALL work item WORK_ITEM.
448 * See svn_wc__wq_build_file_install() which generates this work item.
449 * Implements (struct work_item_dispatch).func. */
451 run_file_install(work_item_baton_t *wqb,
453 const svn_skel_t *work_item,
454 const char *wri_abspath,
455 svn_cancel_func_t cancel_func,
457 apr_pool_t *scratch_pool)
459 const svn_skel_t *arg1 = work_item->children->next;
460 const svn_skel_t *arg4 = arg1->next->next->next;
461 const char *local_relpath;
462 const char *local_abspath;
463 svn_boolean_t use_commit_times;
464 svn_boolean_t record_fileinfo;
465 svn_boolean_t special;
466 svn_stream_t *src_stream;
467 svn_subst_eol_style_t style;
469 apr_hash_t *keywords;
470 const char *temp_dir_abspath;
471 svn_stream_t *dst_stream;
473 const char *wcroot_abspath;
474 const char *source_abspath;
475 const svn_checksum_t *checksum;
477 apr_time_t changed_date;
479 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
480 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
481 local_relpath, scratch_pool, scratch_pool));
483 SVN_ERR(svn_skel__parse_int(&val, arg1->next, scratch_pool));
484 use_commit_times = (val != 0);
485 SVN_ERR(svn_skel__parse_int(&val, arg1->next->next, scratch_pool));
486 record_fileinfo = (val != 0);
488 SVN_ERR(svn_wc__db_read_node_install_info(&wcroot_abspath,
491 db, local_abspath, wri_abspath,
492 scratch_pool, scratch_pool));
496 /* Use the provided path for the source. */
497 local_relpath = apr_pstrmemdup(scratch_pool, arg4->data, arg4->len);
498 SVN_ERR(svn_wc__db_from_relpath(&source_abspath, db, wri_abspath,
500 scratch_pool, scratch_pool));
504 /* This error replaces a previous assertion. Reporting an error from here
505 leaves the workingqueue operation in place, so the working copy is
508 But when we report this error the user at least knows what node has
509 this specific problem, so maybe we can find out why users see this
511 return svn_error_createf(SVN_ERR_WC_CORRUPT_TEXT_BASE, NULL,
512 _("Can't install '%s' from pristine store, "
513 "because no checksum is recorded for this "
515 svn_dirent_local_style(local_abspath,
520 SVN_ERR(svn_wc__db_pristine_get_future_path(&source_abspath,
523 scratch_pool, scratch_pool));
526 SVN_ERR(svn_stream_open_readonly(&src_stream, source_abspath,
527 scratch_pool, scratch_pool));
529 /* Fetch all the translation bits. */
530 SVN_ERR(svn_wc__get_translate_info(&style, &eol,
532 &special, db, local_abspath,
534 scratch_pool, scratch_pool));
537 /* When this stream is closed, the resulting special file will
538 atomically be created/moved into place at LOCAL_ABSPATH. */
539 SVN_ERR(svn_subst_create_specialfile(&dst_stream, local_abspath,
540 scratch_pool, scratch_pool));
542 /* Copy the "repository normal" form of the special file into the
544 SVN_ERR(svn_stream_copy3(src_stream, dst_stream,
545 cancel_func, cancel_baton,
548 /* No need to set exec or read-only flags on special files. */
550 /* ### Shouldn't this record a timestamp and size, etc.? */
554 if (svn_subst_translation_required(style, eol, keywords,
556 TRUE /* force_eol_check */))
558 /* Wrap it in a translating (expanding) stream. */
559 src_stream = svn_subst_stream_translated(src_stream, eol,
566 /* Where is the Right Place to put a temp file in this working copy? */
567 SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath,
569 scratch_pool, scratch_pool));
571 /* Translate to a temporary file. We don't want the user seeing a partial
572 file, nor let them muck with it while we translate. We may also need to
573 get its TRANSLATED_SIZE before the user can monkey it. */
574 SVN_ERR(svn_stream__create_for_install(&dst_stream, temp_dir_abspath,
575 scratch_pool, scratch_pool));
577 /* Copy from the source to the dest, translating as we go. This will also
578 close both streams. */
579 SVN_ERR(svn_stream_copy3(src_stream, dst_stream,
580 cancel_func, cancel_baton,
583 /* All done. Move the file into place. */
584 /* With a single db we might want to install files in a missing directory.
585 Simply trying this scenario on error won't do any harm and at least
586 one user reported this problem on IRC. */
587 SVN_ERR(svn_stream__install_stream(dst_stream, local_abspath,
588 TRUE /* make_parents*/, scratch_pool));
590 /* Tweak the on-disk file according to its properties. */
592 if (props && svn_hash_gets(props, SVN_PROP_EXECUTABLE))
593 SVN_ERR(svn_io_set_file_executable(local_abspath, TRUE, FALSE,
597 /* Note that this explicitly checks the pristine properties, to make sure
598 that when the lock is locally set (=modification) it is not read only */
599 if (props && svn_hash_gets(props, SVN_PROP_NEEDS_LOCK))
601 svn_wc__db_status_t status;
602 svn_wc__db_lock_t *lock;
603 SVN_ERR(svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL,
604 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
605 NULL, NULL, &lock, NULL, NULL, NULL, NULL,
606 NULL, NULL, NULL, NULL, NULL, NULL,
608 scratch_pool, scratch_pool));
610 if (!lock && status != svn_wc__db_status_added)
611 SVN_ERR(svn_io_set_file_read_only(local_abspath, FALSE, scratch_pool));
614 if (use_commit_times)
617 SVN_ERR(svn_io_set_file_affected_time(changed_date,
622 /* ### this should happen before we rename the file into place. */
625 SVN_ERR(get_and_record_fileinfo(wqb, local_abspath,
626 FALSE /* ignore_enoent */,
635 svn_wc__wq_build_file_install(svn_skel_t **work_item,
637 const char *local_abspath,
638 const char *source_abspath,
639 svn_boolean_t use_commit_times,
640 svn_boolean_t record_fileinfo,
641 apr_pool_t *result_pool,
642 apr_pool_t *scratch_pool)
644 const char *local_relpath;
645 const char *wri_abspath;
646 *work_item = svn_skel__make_empty_list(result_pool);
648 /* Use the directory of the file to install as wri_abspath to avoid
649 filestats on just obtaining the wc-root */
650 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
652 /* If a SOURCE_ABSPATH was provided, then put it into the skel. If this
653 value is not provided, then the file's pristine contents will be used. */
654 if (source_abspath != NULL)
656 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath,
658 result_pool, scratch_pool));
660 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
663 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath,
664 local_abspath, result_pool, scratch_pool));
666 svn_skel__prepend_int(record_fileinfo, *work_item, result_pool);
667 svn_skel__prepend_int(use_commit_times, *work_item, result_pool);
668 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
669 svn_skel__prepend_str(OP_FILE_INSTALL, *work_item, result_pool);
675 /* ------------------------------------------------------------------------ */
679 /* Process the OP_FILE_REMOVE work item WORK_ITEM.
680 * See svn_wc__wq_build_file_remove() which generates this work item.
681 * Implements (struct work_item_dispatch).func. */
683 run_file_remove(work_item_baton_t *wqb,
685 const svn_skel_t *work_item,
686 const char *wri_abspath,
687 svn_cancel_func_t cancel_func,
689 apr_pool_t *scratch_pool)
691 const svn_skel_t *arg1 = work_item->children->next;
692 const char *local_relpath;
693 const char *local_abspath;
695 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
696 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
697 local_relpath, scratch_pool, scratch_pool));
699 /* Remove the path, no worrying if it isn't there. */
700 return svn_error_trace(svn_io_remove_file2(local_abspath, TRUE,
706 svn_wc__wq_build_file_remove(svn_skel_t **work_item,
708 const char *wri_abspath,
709 const char *local_abspath,
710 apr_pool_t *result_pool,
711 apr_pool_t *scratch_pool)
713 const char *local_relpath;
714 *work_item = svn_skel__make_empty_list(result_pool);
716 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath,
717 local_abspath, result_pool, scratch_pool));
719 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
720 svn_skel__prepend_str(OP_FILE_REMOVE, *work_item, result_pool);
725 /* ------------------------------------------------------------------------ */
727 /* OP_DIRECTORY_REMOVE */
729 /* Process the OP_FILE_REMOVE work item WORK_ITEM.
730 * See svn_wc__wq_build_file_remove() which generates this work item.
731 * Implements (struct work_item_dispatch).func. */
733 run_dir_remove(work_item_baton_t *wqb,
735 const svn_skel_t *work_item,
736 const char *wri_abspath,
737 svn_cancel_func_t cancel_func,
739 apr_pool_t *scratch_pool)
741 const svn_skel_t *arg1 = work_item->children->next;
742 const char *local_relpath;
743 const char *local_abspath;
744 svn_boolean_t recursive;
746 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
747 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
748 local_relpath, scratch_pool, scratch_pool));
754 SVN_ERR(svn_skel__parse_int(&val, arg1->next, scratch_pool));
756 recursive = (val != 0);
759 /* Remove the path, no worrying if it isn't there. */
761 return svn_error_trace(
762 svn_io_remove_dir2(local_abspath, TRUE,
763 cancel_func, cancel_baton,
769 err = svn_io_dir_remove_nonrecursive(local_abspath, scratch_pool);
771 if (err && (APR_STATUS_IS_ENOENT(err->apr_err)
772 || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)
773 || APR_STATUS_IS_ENOTEMPTY(err->apr_err)))
775 svn_error_clear(err);
779 return svn_error_trace(err);
784 svn_wc__wq_build_dir_remove(svn_skel_t **work_item,
786 const char *wri_abspath,
787 const char *local_abspath,
788 svn_boolean_t recursive,
789 apr_pool_t *result_pool,
790 apr_pool_t *scratch_pool)
792 const char *local_relpath;
793 *work_item = svn_skel__make_empty_list(result_pool);
795 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath,
796 local_abspath, result_pool, scratch_pool));
799 svn_skel__prepend_int(TRUE, *work_item, result_pool);
801 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
802 svn_skel__prepend_str(OP_DIRECTORY_REMOVE, *work_item, result_pool);
807 /* ------------------------------------------------------------------------ */
811 /* Process the OP_FILE_MOVE work item WORK_ITEM.
812 * See svn_wc__wq_build_file_move() which generates this work item.
813 * Implements (struct work_item_dispatch).func. */
815 run_file_move(work_item_baton_t *wqb,
817 const svn_skel_t *work_item,
818 const char *wri_abspath,
819 svn_cancel_func_t cancel_func,
821 apr_pool_t *scratch_pool)
823 const svn_skel_t *arg1 = work_item->children->next;
824 const char *src_abspath, *dst_abspath;
825 const char *local_relpath;
828 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
829 SVN_ERR(svn_wc__db_from_relpath(&src_abspath, db, wri_abspath, local_relpath,
830 scratch_pool, scratch_pool));
831 local_relpath = apr_pstrmemdup(scratch_pool, arg1->next->data,
833 SVN_ERR(svn_wc__db_from_relpath(&dst_abspath, db, wri_abspath, local_relpath,
834 scratch_pool, scratch_pool));
836 /* Use svn_io_file_move() instead of svn_io_file_rename() to allow cross
837 device copies. We should not fail in the workqueue. */
839 err = svn_io_file_move(src_abspath, dst_abspath, scratch_pool);
841 /* If the source is not found, we assume the wq op is already handled */
842 if (err && APR_STATUS_IS_ENOENT(err->apr_err))
843 svn_error_clear(err);
852 svn_wc__wq_build_file_move(svn_skel_t **work_item,
854 const char *wri_abspath,
855 const char *src_abspath,
856 const char *dst_abspath,
857 apr_pool_t *result_pool,
858 apr_pool_t *scratch_pool)
860 svn_node_kind_t kind;
861 const char *local_relpath;
862 *work_item = svn_skel__make_empty_list(result_pool);
864 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
865 SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
866 SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
868 /* File must exist */
869 SVN_ERR(svn_io_check_path(src_abspath, &kind, result_pool));
871 if (kind == svn_node_none)
872 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
874 svn_dirent_local_style(src_abspath,
877 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath, dst_abspath,
878 result_pool, scratch_pool));
879 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
881 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath, src_abspath,
882 result_pool, scratch_pool));
883 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
885 svn_skel__prepend_str(OP_FILE_MOVE, *work_item, result_pool);
890 /* ------------------------------------------------------------------------ */
892 /* OP_FILE_COPY_TRANSLATED */
894 /* Process the OP_FILE_COPY_TRANSLATED work item WORK_ITEM.
895 * See run_file_copy_translated() which generates this work item.
896 * Implements (struct work_item_dispatch).func. */
898 run_file_copy_translated(work_item_baton_t *wqb,
900 const svn_skel_t *work_item,
901 const char *wri_abspath,
902 svn_cancel_func_t cancel_func,
904 apr_pool_t *scratch_pool)
906 const svn_skel_t *arg1 = work_item->children->next;
907 const char *local_abspath, *src_abspath, *dst_abspath;
908 const char *local_relpath;
909 svn_subst_eol_style_t style;
911 apr_hash_t *keywords;
912 svn_boolean_t special;
914 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
915 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
916 local_relpath, scratch_pool, scratch_pool));
918 local_relpath = apr_pstrmemdup(scratch_pool, arg1->next->data,
920 SVN_ERR(svn_wc__db_from_relpath(&src_abspath, db, wri_abspath,
921 local_relpath, scratch_pool, scratch_pool));
923 local_relpath = apr_pstrmemdup(scratch_pool, arg1->next->next->data,
924 arg1->next->next->len);
925 SVN_ERR(svn_wc__db_from_relpath(&dst_abspath, db, wri_abspath,
926 local_relpath, scratch_pool, scratch_pool));
928 SVN_ERR(svn_wc__get_translate_info(&style, &eol,
931 db, local_abspath, NULL, FALSE,
932 scratch_pool, scratch_pool));
934 SVN_ERR(svn_subst_copy_and_translate4(src_abspath, dst_abspath,
935 eol, TRUE /* repair */,
936 keywords, TRUE /* expand */,
938 cancel_func, cancel_baton,
945 svn_wc__wq_build_file_copy_translated(svn_skel_t **work_item,
947 const char *local_abspath,
948 const char *src_abspath,
949 const char *dst_abspath,
950 apr_pool_t *result_pool,
951 apr_pool_t *scratch_pool)
953 svn_node_kind_t kind;
954 const char *local_relpath;
956 *work_item = svn_skel__make_empty_list(result_pool);
958 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
959 SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
960 SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
962 /* File must exist */
963 SVN_ERR(svn_io_check_path(src_abspath, &kind, result_pool));
965 if (kind == svn_node_none)
966 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
968 svn_dirent_local_style(src_abspath,
971 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath, dst_abspath,
972 result_pool, scratch_pool));
973 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
975 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath, src_abspath,
976 result_pool, scratch_pool));
977 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
979 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath,
980 local_abspath, result_pool, scratch_pool));
981 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
983 svn_skel__prepend_str(OP_FILE_COPY_TRANSLATED, *work_item, result_pool);
988 /* ------------------------------------------------------------------------ */
990 /* OP_DIRECTORY_INSTALL */
993 run_dir_install(work_item_baton_t *wqb,
995 const svn_skel_t *work_item,
996 const char *wri_abspath,
997 svn_cancel_func_t cancel_func,
999 apr_pool_t *scratch_pool)
1001 const svn_skel_t *arg1 = work_item->children->next;
1002 const char *local_relpath;
1003 const char *local_abspath;
1005 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
1006 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1007 local_relpath, scratch_pool, scratch_pool));
1009 SVN_ERR(svn_wc__ensure_directory(local_abspath, scratch_pool));
1011 return SVN_NO_ERROR;
1015 svn_wc__wq_build_dir_install(svn_skel_t **work_item,
1017 const char *local_abspath,
1018 apr_pool_t *result_pool,
1019 apr_pool_t *scratch_pool)
1021 const char *local_relpath;
1023 *work_item = svn_skel__make_empty_list(result_pool);
1025 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath,
1026 local_abspath, result_pool, scratch_pool));
1027 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
1029 svn_skel__prepend_str(OP_DIRECTORY_INSTALL, *work_item, result_pool);
1031 return SVN_NO_ERROR;
1035 /* ------------------------------------------------------------------------ */
1037 /* OP_SYNC_FILE_FLAGS */
1039 /* Process the OP_SYNC_FILE_FLAGS work item WORK_ITEM.
1040 * See svn_wc__wq_build_sync_file_flags() which generates this work item.
1041 * Implements (struct work_item_dispatch).func. */
1042 static svn_error_t *
1043 run_sync_file_flags(work_item_baton_t *wqb,
1045 const svn_skel_t *work_item,
1046 const char *wri_abspath,
1047 svn_cancel_func_t cancel_func,
1049 apr_pool_t *scratch_pool)
1051 const svn_skel_t *arg1 = work_item->children->next;
1052 const char *local_relpath;
1053 const char *local_abspath;
1055 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
1056 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1057 local_relpath, scratch_pool, scratch_pool));
1059 return svn_error_trace(svn_wc__sync_flags_with_props(NULL, db,
1060 local_abspath, scratch_pool));
1065 svn_wc__wq_build_sync_file_flags(svn_skel_t **work_item,
1067 const char *local_abspath,
1068 apr_pool_t *result_pool,
1069 apr_pool_t *scratch_pool)
1071 const char *local_relpath;
1072 *work_item = svn_skel__make_empty_list(result_pool);
1074 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath,
1075 local_abspath, result_pool, scratch_pool));
1077 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
1078 svn_skel__prepend_str(OP_SYNC_FILE_FLAGS, *work_item, result_pool);
1080 return SVN_NO_ERROR;
1084 /* ------------------------------------------------------------------------ */
1086 /* OP_PREJ_INSTALL */
1088 static svn_error_t *
1089 run_prej_install(work_item_baton_t *wqb,
1091 const svn_skel_t *work_item,
1092 const char *wri_abspath,
1093 svn_cancel_func_t cancel_func,
1095 apr_pool_t *scratch_pool)
1097 const svn_skel_t *arg1 = work_item->children->next;
1098 const char *local_relpath;
1099 const char *local_abspath;
1100 svn_skel_t *conflicts;
1101 const svn_skel_t *prop_conflict_skel;
1102 const char *tmp_prejfile_abspath;
1103 const char *prejfile_abspath;
1105 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
1106 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1107 local_relpath, scratch_pool, scratch_pool));
1109 SVN_ERR(svn_wc__db_read_conflict(&conflicts, NULL, NULL, db, local_abspath,
1110 scratch_pool, scratch_pool));
1112 SVN_ERR(svn_wc__conflict_read_prop_conflict(&prejfile_abspath,
1113 NULL, NULL, NULL, NULL,
1114 db, local_abspath, conflicts,
1115 scratch_pool, scratch_pool));
1117 if (arg1->next != NULL)
1118 prop_conflict_skel = arg1->next; /* Before Subversion 1.9 */
1120 prop_conflict_skel = NULL; /* Read from DB */
1122 /* Construct a property reject file in the temporary area. */
1123 SVN_ERR(svn_wc__create_prejfile(&tmp_prejfile_abspath,
1126 cancel_func, cancel_baton,
1127 scratch_pool, scratch_pool));
1129 /* ... and atomically move it into place. */
1130 SVN_ERR(svn_io_file_rename(tmp_prejfile_abspath,
1134 return SVN_NO_ERROR;
1139 svn_wc__wq_build_prej_install(svn_skel_t **work_item,
1141 const char *local_abspath,
1142 /*svn_skel_t *conflict_skel,*/
1143 apr_pool_t *result_pool,
1144 apr_pool_t *scratch_pool)
1146 const char *local_relpath;
1147 *work_item = svn_skel__make_empty_list(result_pool);
1149 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath,
1150 local_abspath, result_pool, scratch_pool));
1152 /* ### In Subversion 1.7 and 1.8 we created a legacy property conflict skel
1154 if (conflict_skel != NULL)
1155 svn_skel__prepend(conflict_skel, *work_item);
1157 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
1158 svn_skel__prepend_str(OP_PREJ_INSTALL, *work_item, result_pool);
1160 return SVN_NO_ERROR;
1164 /* ------------------------------------------------------------------------ */
1166 /* OP_RECORD_FILEINFO */
1169 static svn_error_t *
1170 run_record_fileinfo(work_item_baton_t *wqb,
1172 const svn_skel_t *work_item,
1173 const char *wri_abspath,
1174 svn_cancel_func_t cancel_func,
1176 apr_pool_t *scratch_pool)
1178 const svn_skel_t *arg1 = work_item->children->next;
1179 const char *local_relpath;
1180 const char *local_abspath;
1181 apr_time_t set_time = 0;
1183 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
1185 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1186 local_relpath, scratch_pool, scratch_pool));
1192 SVN_ERR(svn_skel__parse_int(&val, arg1->next, scratch_pool));
1193 set_time = (apr_time_t)val;
1198 svn_node_kind_t kind;
1199 svn_boolean_t is_special;
1201 /* Do not set the timestamp on special files. */
1202 SVN_ERR(svn_io_check_special_path(local_abspath, &kind, &is_special,
1205 /* Don't set affected time when local_abspath does not exist or is
1207 if (kind == svn_node_file && !is_special)
1208 SVN_ERR(svn_io_set_file_affected_time(set_time, local_abspath,
1211 /* Note that we can't use the value we get here for recording as the
1212 filesystem might have a different timestamp granularity */
1216 return svn_error_trace(get_and_record_fileinfo(wqb, local_abspath,
1217 TRUE /* ignore_enoent */,
1221 /* ------------------------------------------------------------------------ */
1223 /* OP_TMP_SET_TEXT_CONFLICT_MARKERS */
1226 static svn_error_t *
1227 run_set_text_conflict_markers(work_item_baton_t *wqb,
1229 const svn_skel_t *work_item,
1230 const char *wri_abspath,
1231 svn_cancel_func_t cancel_func,
1233 apr_pool_t *scratch_pool)
1235 const svn_skel_t *arg = work_item->children->next;
1236 const char *local_relpath;
1237 const char *local_abspath;
1238 const char *old_abspath = NULL;
1239 const char *new_abspath = NULL;
1240 const char *wrk_abspath = NULL;
1242 local_relpath = apr_pstrmemdup(scratch_pool, arg->data, arg->len);
1243 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1244 local_relpath, scratch_pool, scratch_pool));
1247 local_relpath = arg->len ? apr_pstrmemdup(scratch_pool, arg->data, arg->len)
1252 SVN_ERR(svn_wc__db_from_relpath(&old_abspath, db, wri_abspath,
1254 scratch_pool, scratch_pool));
1258 local_relpath = arg->len ? apr_pstrmemdup(scratch_pool, arg->data, arg->len)
1262 SVN_ERR(svn_wc__db_from_relpath(&new_abspath, db, wri_abspath,
1264 scratch_pool, scratch_pool));
1268 local_relpath = arg->len ? apr_pstrmemdup(scratch_pool, arg->data, arg->len)
1273 SVN_ERR(svn_wc__db_from_relpath(&wrk_abspath, db, wri_abspath,
1275 scratch_pool, scratch_pool));
1278 /* Upgrade scenario: We have a workqueue item that describes how to install a
1279 non skel conflict. Fetch all the information we can to create a new style
1281 /* ### Before format 30 this is/was a common code path as we didn't install
1282 ### the conflict directly in the db. It just calls the wc_db code
1283 ### to set the right fields. */
1286 /* Check if we should combine with a property conflict... */
1287 svn_skel_t *conflicts;
1289 SVN_ERR(svn_wc__db_read_conflict(&conflicts, NULL, NULL, db, local_abspath,
1290 scratch_pool, scratch_pool));
1294 /* No conflict exists, create a basic skel */
1295 conflicts = svn_wc__conflict_skel_create(scratch_pool);
1297 SVN_ERR(svn_wc__conflict_skel_set_op_update(conflicts, NULL, NULL,
1302 /* Add the text conflict to the existing onflict */
1303 SVN_ERR(svn_wc__conflict_skel_add_text_conflict(conflicts, db,
1311 SVN_ERR(svn_wc__db_op_mark_conflict(db, local_abspath, conflicts,
1312 NULL, scratch_pool));
1314 return SVN_NO_ERROR;
1317 /* ------------------------------------------------------------------------ */
1319 /* OP_TMP_SET_PROPERTY_CONFLICT_MARKER */
1321 static svn_error_t *
1322 run_set_property_conflict_marker(work_item_baton_t *wqb,
1324 const svn_skel_t *work_item,
1325 const char *wri_abspath,
1326 svn_cancel_func_t cancel_func,
1328 apr_pool_t *scratch_pool)
1330 const svn_skel_t *arg = work_item->children->next;
1331 const char *local_relpath;
1332 const char *local_abspath;
1333 const char *prej_abspath = NULL;
1335 local_relpath = apr_pstrmemdup(scratch_pool, arg->data, arg->len);
1337 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1338 local_relpath, scratch_pool, scratch_pool));
1342 local_relpath = arg->len ? apr_pstrmemdup(scratch_pool, arg->data, arg->len)
1346 SVN_ERR(svn_wc__db_from_relpath(&prej_abspath, db, wri_abspath,
1348 scratch_pool, scratch_pool));
1351 /* Check if we should combine with a text conflict... */
1352 svn_skel_t *conflicts;
1353 apr_hash_t *prop_names;
1355 SVN_ERR(svn_wc__db_read_conflict(&conflicts, NULL, NULL,
1357 scratch_pool, scratch_pool));
1361 /* No conflict exists, create a basic skel */
1362 conflicts = svn_wc__conflict_skel_create(scratch_pool);
1364 SVN_ERR(svn_wc__conflict_skel_set_op_update(conflicts, NULL, NULL,
1369 prop_names = apr_hash_make(scratch_pool);
1370 SVN_ERR(svn_wc__conflict_skel_add_prop_conflict(conflicts, db,
1378 SVN_ERR(svn_wc__db_op_mark_conflict(db, local_abspath, conflicts,
1379 NULL, scratch_pool));
1381 return SVN_NO_ERROR;
1384 /* ------------------------------------------------------------------------ */
1386 static const struct work_item_dispatch dispatch_table[] = {
1387 { OP_FILE_COMMIT, run_file_commit },
1388 { OP_FILE_INSTALL, run_file_install },
1389 { OP_FILE_REMOVE, run_file_remove },
1390 { OP_FILE_MOVE, run_file_move },
1391 { OP_FILE_COPY_TRANSLATED, run_file_copy_translated },
1392 { OP_SYNC_FILE_FLAGS, run_sync_file_flags },
1393 { OP_PREJ_INSTALL, run_prej_install },
1394 { OP_DIRECTORY_REMOVE, run_dir_remove },
1395 { OP_DIRECTORY_INSTALL, run_dir_install },
1398 { OP_POSTUPGRADE, run_postupgrade },
1400 /* Legacy workqueue items. No longer created */
1401 { OP_BASE_REMOVE, run_base_remove },
1402 { OP_RECORD_FILEINFO, run_record_fileinfo },
1403 { OP_TMP_SET_TEXT_CONFLICT_MARKERS, run_set_text_conflict_markers },
1404 { OP_TMP_SET_PROPERTY_CONFLICT_MARKER, run_set_property_conflict_marker },
1410 struct work_item_baton_t
1412 apr_pool_t *result_pool; /* Pool to allocate result in */
1414 svn_boolean_t used; /* needs reset */
1416 apr_hash_t *record_map; /* const char * -> svn_io_dirent2_t map */
1420 static svn_error_t *
1421 dispatch_work_item(work_item_baton_t *wqb,
1423 const char *wri_abspath,
1424 const svn_skel_t *work_item,
1425 svn_cancel_func_t cancel_func,
1427 apr_pool_t *scratch_pool)
1429 const struct work_item_dispatch *scan;
1431 /* Scan the dispatch table for a function to handle this work item. */
1432 for (scan = &dispatch_table[0]; scan->name != NULL; ++scan)
1434 if (svn_skel__matches_atom(work_item->children, scan->name))
1437 #ifdef SVN_DEBUG_WORK_QUEUE
1438 SVN_DBG(("dispatch: operation='%s'\n", scan->name));
1440 SVN_ERR((*scan->func)(wqb, db, work_item, wri_abspath,
1441 cancel_func, cancel_baton,
1444 #ifdef SVN_RUN_WORK_QUEUE_TWICE
1445 #ifdef SVN_DEBUG_WORK_QUEUE
1446 SVN_DBG(("dispatch: operation='%s'\n", scan->name));
1448 /* Being able to run every workqueue item twice is one
1449 requirement for workqueues to be restartable. */
1450 SVN_ERR((*scan->func)(db, work_item, wri_abspath,
1451 cancel_func, cancel_baton,
1459 if (scan->name == NULL)
1461 /* We should know about ALL possible work items here. If we do not,
1462 then something is wrong. Most likely, some kind of format/code
1463 skew. There is nothing more we can do. Erasing or ignoring this
1464 work item could leave the WC in an even more broken state.
1466 Contrary to issue #1581, we cannot simply remove work items and
1467 continue, so bail out with an error. */
1468 return svn_error_createf(SVN_ERR_WC_BAD_ADM_LOG, NULL,
1469 _("Unrecognized work item in the queue"));
1472 return SVN_NO_ERROR;
1477 svn_wc__wq_run(svn_wc__db_t *db,
1478 const char *wri_abspath,
1479 svn_cancel_func_t cancel_func,
1481 apr_pool_t *scratch_pool)
1483 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1484 apr_uint64_t last_id = 0;
1485 work_item_baton_t wib = { 0 };
1486 wib.result_pool = svn_pool_create(scratch_pool);
1488 #ifdef SVN_DEBUG_WORK_QUEUE
1489 SVN_DBG(("wq_run: wri='%s'\n", wri_abspath));
1491 static int count = 0;
1492 const char *count_env_var = getenv("SVN_DEBUG_WORK_QUEUE");
1495 SVN_ERR(svn_cstring_atoi(&count_env_val, count_env_var));
1497 if (count_env_var && ++count == count_env_val)
1498 return svn_error_create(SVN_ERR_CANCELLED, NULL, "fake cancel");
1505 svn_skel_t *work_item;
1508 svn_pool_clear(iterpool);
1512 /* Make sure to do this *early* in the loop iteration. There may
1513 be a LAST_ID that needs to be marked as completed, *before* we
1514 start worrying about anything else. */
1515 SVN_ERR(svn_wc__db_wq_fetch_next(&id, &work_item, db, wri_abspath,
1516 last_id, iterpool, iterpool));
1520 /* Make sure to do this *early* in the loop iteration. There may
1521 be a LAST_ID that needs to be marked as completed, *before* we
1522 start worrying about anything else. */
1523 SVN_ERR(svn_wc__db_wq_record_and_fetch_next(&id, &work_item,
1525 last_id, wib.record_map,
1529 svn_pool_clear(wib.result_pool);
1530 wib.record_map = NULL;
1534 /* Stop work queue processing, if requested. A future 'svn cleanup'
1535 should be able to continue the processing. Note that we may
1536 have WORK_ITEM, but we'll just skip its processing for now. */
1538 SVN_ERR(cancel_func(cancel_baton));
1540 /* If we have a WORK_ITEM, then process the sucker. Otherwise,
1542 if (work_item == NULL)
1545 err = dispatch_work_item(&wib, db, wri_abspath, work_item,
1546 cancel_func, cancel_baton, iterpool);
1549 const char *skel = svn_skel__unparse(work_item, scratch_pool)->data;
1551 return svn_error_createf(SVN_ERR_WC_BAD_ADM_LOG, err,
1552 _("Failed to run the WC DB work queue "
1553 "associated with '%s', work item %d %s"),
1554 svn_dirent_local_style(wri_abspath,
1559 /* The work item finished without error. Mark it completed
1560 in the next loop. */
1564 svn_pool_destroy(iterpool);
1565 return SVN_NO_ERROR;
1570 svn_wc__wq_merge(svn_skel_t *work_item1,
1571 svn_skel_t *work_item2,
1572 apr_pool_t *result_pool)
1574 /* If either argument is NULL, then just return the other. */
1575 if (work_item1 == NULL)
1577 if (work_item2 == NULL)
1580 /* We have two items. Figure out how to join them. */
1581 if (SVN_WC__SINGLE_WORK_ITEM(work_item1))
1583 if (SVN_WC__SINGLE_WORK_ITEM(work_item2))
1585 /* Both are singular work items. Construct a list, then put
1586 both work items into it (in the proper order). */
1588 svn_skel_t *result = svn_skel__make_empty_list(result_pool);
1590 svn_skel__prepend(work_item2, result);
1591 svn_skel__prepend(work_item1, result);
1595 /* WORK_ITEM2 is a list of work items. We can simply shove WORK_ITEM1
1596 in the front to keep the ordering. */
1597 svn_skel__prepend(work_item1, work_item2);
1600 /* WORK_ITEM1 is a list of work items. */
1602 if (SVN_WC__SINGLE_WORK_ITEM(work_item2))
1604 /* Put WORK_ITEM2 onto the end of the WORK_ITEM1 list. */
1605 svn_skel__append(work_item1, work_item2);
1609 /* We have two lists of work items. We need to chain all of the work
1610 items into one big list. We will leave behind the WORK_ITEM2 skel,
1611 as we only want its children. */
1612 svn_skel__append(work_item1, work_item2->children);
1617 static svn_error_t *
1618 get_and_record_fileinfo(work_item_baton_t *wqb,
1619 const char *local_abspath,
1620 svn_boolean_t ignore_enoent,
1621 apr_pool_t *scratch_pool)
1623 const svn_io_dirent2_t *dirent;
1625 SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath, FALSE, ignore_enoent,
1626 wqb->result_pool, scratch_pool));
1628 if (dirent->kind != svn_node_file)
1629 return SVN_NO_ERROR;
1633 if (! wqb->record_map)
1634 wqb->record_map = apr_hash_make(wqb->result_pool);
1636 svn_hash_sets(wqb->record_map, apr_pstrdup(wqb->result_pool, local_abspath),
1639 return SVN_NO_ERROR;