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_rename2(tmp_wfile, file_abspath, FALSE,
262 *overwrote_working = TRUE;
265 /* ### should be using OP_SYNC_FILE_FLAGS, or an internal version of
266 ### that here. do we need to set *OVERWROTE_WORKING? */
268 /* ### Re: OVERWROTE_WORKING, the following function is rather liberal
269 ### with setting that flag, so we should probably decide if we really
270 ### care about it when syncing flags. */
271 SVN_ERR(svn_wc__sync_flags_with_props(overwrote_working, db, file_abspath,
278 process_commit_file_install(svn_wc__db_t *db,
279 const char *local_abspath,
280 svn_cancel_func_t cancel_func,
282 apr_pool_t *scratch_pool)
284 svn_boolean_t overwrote_working;
286 /* Install the new file, which may involve expanding keywords.
287 A copy of this file should have been dropped into our `tmp/text-base'
288 directory during the commit process. Part of this process
289 involves recording the textual timestamp for this entry. We'd like
290 to just use the timestamp of the working file, but it is possible
291 that at some point during the commit, the real working file might
295 SVN_ERR(install_committed_file(&overwrote_working, db,
297 cancel_func, cancel_baton,
300 /* We will compute and modify the size and timestamp */
301 if (overwrote_working)
305 SVN_ERR(svn_io_stat(&finfo, local_abspath,
306 APR_FINFO_MIN | APR_FINFO_LINK, scratch_pool));
307 SVN_ERR(svn_wc__db_global_record_fileinfo(db, local_abspath,
308 finfo.size, finfo.mtime,
313 svn_boolean_t modified;
315 /* The working copy file hasn't been overwritten. We just
316 removed the recorded size and modification time from the nodes
317 record by calling svn_wc__db_global_commit().
319 Now we have some file in our working copy that might be what
320 we just committed, but we are not certain at this point.
322 We still have a write lock here, so we check if the file is
323 what we expect it to be and if it is the right file we update
324 the recorded information. (If it isn't we keep the null data).
326 Instead of reimplementing all this here, we just call a function
327 that already does implement this when it notices that we have the
328 right kind of lock (and we ignore the result)
330 SVN_ERR(svn_wc__internal_file_modified_p(&modified,
331 db, local_abspath, FALSE,
339 run_file_commit(work_item_baton_t *wqb,
341 const svn_skel_t *work_item,
342 const char *wri_abspath,
343 svn_cancel_func_t cancel_func,
345 apr_pool_t *scratch_pool)
347 const svn_skel_t *arg1 = work_item->children->next;
348 const char *local_relpath;
349 const char *local_abspath;
351 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
352 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
353 local_relpath, scratch_pool, scratch_pool));
355 /* We don't both parsing the other two values in the skel. */
357 return svn_error_trace(
358 process_commit_file_install(db, local_abspath,
359 cancel_func, cancel_baton,
364 svn_wc__wq_build_file_commit(svn_skel_t **work_item,
366 const char *local_abspath,
367 svn_boolean_t props_mod,
368 apr_pool_t *result_pool,
369 apr_pool_t *scratch_pool)
371 const char *local_relpath;
372 *work_item = svn_skel__make_empty_list(result_pool);
374 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath,
375 local_abspath, result_pool, scratch_pool));
377 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
379 svn_skel__prepend_str(OP_FILE_COMMIT, *work_item, result_pool);
384 /* ------------------------------------------------------------------------ */
388 run_postupgrade(work_item_baton_t *wqb,
390 const svn_skel_t *work_item,
391 const char *wri_abspath,
392 svn_cancel_func_t cancel_func,
394 apr_pool_t *scratch_pool)
396 const char *entries_path;
397 const char *format_path;
398 const char *wcroot_abspath;
401 err = svn_wc__wipe_postupgrade(wri_abspath, FALSE,
402 cancel_func, cancel_baton, scratch_pool);
403 if (err && err->apr_err == SVN_ERR_ENTRY_NOT_FOUND)
404 /* No entry, this can happen when the wq item is rerun. */
405 svn_error_clear(err);
409 SVN_ERR(svn_wc__db_get_wcroot(&wcroot_abspath, db, wri_abspath,
410 scratch_pool, scratch_pool));
412 entries_path = svn_wc__adm_child(wcroot_abspath, SVN_WC__ADM_ENTRIES,
414 format_path = svn_wc__adm_child(wcroot_abspath, SVN_WC__ADM_FORMAT,
417 /* Write the 'format' and 'entries' files.
419 ### The order may matter for some sufficiently old clients.. but
420 ### this code only runs during upgrade after the files had been
421 ### removed earlier during the upgrade. */
422 SVN_ERR(svn_io_write_atomic2(format_path, SVN_WC__NON_ENTRIES_STRING,
423 sizeof(SVN_WC__NON_ENTRIES_STRING) - 1,
424 NULL, TRUE, scratch_pool));
426 SVN_ERR(svn_io_write_atomic2(entries_path, SVN_WC__NON_ENTRIES_STRING,
427 sizeof(SVN_WC__NON_ENTRIES_STRING) - 1,
428 NULL, TRUE, scratch_pool));
434 svn_wc__wq_build_postupgrade(svn_skel_t **work_item,
435 apr_pool_t *result_pool)
437 *work_item = svn_skel__make_empty_list(result_pool);
439 svn_skel__prepend_str(OP_POSTUPGRADE, *work_item, result_pool);
444 /* ------------------------------------------------------------------------ */
446 /* OP_FILE_INSTALL */
448 /* Process the OP_FILE_INSTALL work item WORK_ITEM.
449 * See svn_wc__wq_build_file_install() which generates this work item.
450 * Implements (struct work_item_dispatch).func. */
452 run_file_install(work_item_baton_t *wqb,
454 const svn_skel_t *work_item,
455 const char *wri_abspath,
456 svn_cancel_func_t cancel_func,
458 apr_pool_t *scratch_pool)
460 const svn_skel_t *arg1 = work_item->children->next;
461 const svn_skel_t *arg4 = arg1->next->next->next;
462 const char *local_relpath;
463 const char *local_abspath;
464 svn_boolean_t use_commit_times;
465 svn_boolean_t record_fileinfo;
466 svn_boolean_t special;
467 svn_stream_t *src_stream;
468 svn_subst_eol_style_t style;
470 apr_hash_t *keywords;
471 const char *temp_dir_abspath;
472 svn_stream_t *dst_stream;
474 const char *wcroot_abspath;
475 const char *source_abspath;
476 const svn_checksum_t *checksum;
478 apr_time_t changed_date;
480 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
481 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
482 local_relpath, scratch_pool, scratch_pool));
484 SVN_ERR(svn_skel__parse_int(&val, arg1->next, scratch_pool));
485 use_commit_times = (val != 0);
486 SVN_ERR(svn_skel__parse_int(&val, arg1->next->next, scratch_pool));
487 record_fileinfo = (val != 0);
489 SVN_ERR(svn_wc__db_read_node_install_info(&wcroot_abspath,
492 db, local_abspath, wri_abspath,
493 scratch_pool, scratch_pool));
497 /* Use the provided path for the source. */
498 local_relpath = apr_pstrmemdup(scratch_pool, arg4->data, arg4->len);
499 SVN_ERR(svn_wc__db_from_relpath(&source_abspath, db, wri_abspath,
501 scratch_pool, scratch_pool));
505 /* This error replaces a previous assertion. Reporting an error from here
506 leaves the workingqueue operation in place, so the working copy is
509 But when we report this error the user at least knows what node has
510 this specific problem, so maybe we can find out why users see this
512 return svn_error_createf(SVN_ERR_WC_CORRUPT_TEXT_BASE, NULL,
513 _("Can't install '%s' from pristine store, "
514 "because no checksum is recorded for this "
516 svn_dirent_local_style(local_abspath,
521 SVN_ERR(svn_wc__db_pristine_get_future_path(&source_abspath,
524 scratch_pool, scratch_pool));
527 SVN_ERR(svn_stream_open_readonly(&src_stream, source_abspath,
528 scratch_pool, scratch_pool));
530 /* Fetch all the translation bits. */
531 SVN_ERR(svn_wc__get_translate_info(&style, &eol,
533 &special, db, local_abspath,
535 scratch_pool, scratch_pool));
538 /* When this stream is closed, the resulting special file will
539 atomically be created/moved into place at LOCAL_ABSPATH. */
540 SVN_ERR(svn_subst_create_specialfile(&dst_stream, local_abspath,
541 scratch_pool, scratch_pool));
543 /* Copy the "repository normal" form of the special file into the
545 SVN_ERR(svn_stream_copy3(src_stream, dst_stream,
546 cancel_func, cancel_baton,
549 /* No need to set exec or read-only flags on special files. */
551 /* ### Shouldn't this record a timestamp and size, etc.? */
555 if (svn_subst_translation_required(style, eol, keywords,
557 TRUE /* force_eol_check */))
559 /* Wrap it in a translating (expanding) stream. */
560 src_stream = svn_subst_stream_translated(src_stream, eol,
567 /* Where is the Right Place to put a temp file in this working copy? */
568 SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath,
570 scratch_pool, scratch_pool));
572 /* Translate to a temporary file. We don't want the user seeing a partial
573 file, nor let them muck with it while we translate. We may also need to
574 get its TRANSLATED_SIZE before the user can monkey it. */
575 SVN_ERR(svn_stream__create_for_install(&dst_stream, temp_dir_abspath,
576 scratch_pool, scratch_pool));
578 /* Copy from the source to the dest, translating as we go. This will also
579 close both streams. */
580 SVN_ERR(svn_stream_copy3(src_stream, dst_stream,
581 cancel_func, cancel_baton,
584 /* All done. Move the file into place. */
585 /* With a single db we might want to install files in a missing directory.
586 Simply trying this scenario on error won't do any harm and at least
587 one user reported this problem on IRC. */
588 SVN_ERR(svn_stream__install_stream(dst_stream, local_abspath,
589 TRUE /* make_parents*/, scratch_pool));
591 /* Tweak the on-disk file according to its properties. */
593 if (props && svn_hash_gets(props, SVN_PROP_EXECUTABLE))
594 SVN_ERR(svn_io_set_file_executable(local_abspath, TRUE, FALSE,
598 /* Note that this explicitly checks the pristine properties, to make sure
599 that when the lock is locally set (=modification) it is not read only */
600 if (props && svn_hash_gets(props, SVN_PROP_NEEDS_LOCK))
602 svn_wc__db_status_t status;
603 svn_wc__db_lock_t *lock;
604 SVN_ERR(svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL,
605 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
606 NULL, NULL, &lock, NULL, NULL, NULL, NULL,
607 NULL, NULL, NULL, NULL, NULL, NULL,
609 scratch_pool, scratch_pool));
611 if (!lock && status != svn_wc__db_status_added)
612 SVN_ERR(svn_io_set_file_read_only(local_abspath, FALSE, scratch_pool));
615 if (use_commit_times)
618 SVN_ERR(svn_io_set_file_affected_time(changed_date,
623 /* ### this should happen before we rename the file into place. */
626 SVN_ERR(get_and_record_fileinfo(wqb, local_abspath,
627 FALSE /* ignore_enoent */,
636 svn_wc__wq_build_file_install(svn_skel_t **work_item,
638 const char *local_abspath,
639 const char *source_abspath,
640 svn_boolean_t use_commit_times,
641 svn_boolean_t record_fileinfo,
642 apr_pool_t *result_pool,
643 apr_pool_t *scratch_pool)
645 const char *local_relpath;
646 const char *wri_abspath;
647 *work_item = svn_skel__make_empty_list(result_pool);
649 /* Use the directory of the file to install as wri_abspath to avoid
650 filestats on just obtaining the wc-root */
651 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
653 /* If a SOURCE_ABSPATH was provided, then put it into the skel. If this
654 value is not provided, then the file's pristine contents will be used. */
655 if (source_abspath != NULL)
657 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath,
659 result_pool, scratch_pool));
661 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
664 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath,
665 local_abspath, result_pool, scratch_pool));
667 svn_skel__prepend_int(record_fileinfo, *work_item, result_pool);
668 svn_skel__prepend_int(use_commit_times, *work_item, result_pool);
669 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
670 svn_skel__prepend_str(OP_FILE_INSTALL, *work_item, result_pool);
676 /* ------------------------------------------------------------------------ */
680 /* Process the OP_FILE_REMOVE work item WORK_ITEM.
681 * See svn_wc__wq_build_file_remove() which generates this work item.
682 * Implements (struct work_item_dispatch).func. */
684 run_file_remove(work_item_baton_t *wqb,
686 const svn_skel_t *work_item,
687 const char *wri_abspath,
688 svn_cancel_func_t cancel_func,
690 apr_pool_t *scratch_pool)
692 const svn_skel_t *arg1 = work_item->children->next;
693 const char *local_relpath;
694 const char *local_abspath;
696 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
697 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
698 local_relpath, scratch_pool, scratch_pool));
700 /* Remove the path, no worrying if it isn't there. */
701 return svn_error_trace(svn_io_remove_file2(local_abspath, TRUE,
707 svn_wc__wq_build_file_remove(svn_skel_t **work_item,
709 const char *wri_abspath,
710 const char *local_abspath,
711 apr_pool_t *result_pool,
712 apr_pool_t *scratch_pool)
714 const char *local_relpath;
715 *work_item = svn_skel__make_empty_list(result_pool);
717 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath,
718 local_abspath, result_pool, scratch_pool));
720 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
721 svn_skel__prepend_str(OP_FILE_REMOVE, *work_item, result_pool);
726 /* ------------------------------------------------------------------------ */
728 /* OP_DIRECTORY_REMOVE */
730 /* Process the OP_FILE_REMOVE work item WORK_ITEM.
731 * See svn_wc__wq_build_file_remove() which generates this work item.
732 * Implements (struct work_item_dispatch).func. */
734 run_dir_remove(work_item_baton_t *wqb,
736 const svn_skel_t *work_item,
737 const char *wri_abspath,
738 svn_cancel_func_t cancel_func,
740 apr_pool_t *scratch_pool)
742 const svn_skel_t *arg1 = work_item->children->next;
743 const char *local_relpath;
744 const char *local_abspath;
745 svn_boolean_t recursive;
747 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
748 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
749 local_relpath, scratch_pool, scratch_pool));
755 SVN_ERR(svn_skel__parse_int(&val, arg1->next, scratch_pool));
757 recursive = (val != 0);
760 /* Remove the path, no worrying if it isn't there. */
762 return svn_error_trace(
763 svn_io_remove_dir2(local_abspath, TRUE,
764 cancel_func, cancel_baton,
770 err = svn_io_dir_remove_nonrecursive(local_abspath, scratch_pool);
772 if (err && (APR_STATUS_IS_ENOENT(err->apr_err)
773 || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)
774 || APR_STATUS_IS_ENOTEMPTY(err->apr_err)))
776 svn_error_clear(err);
780 return svn_error_trace(err);
785 svn_wc__wq_build_dir_remove(svn_skel_t **work_item,
787 const char *wri_abspath,
788 const char *local_abspath,
789 svn_boolean_t recursive,
790 apr_pool_t *result_pool,
791 apr_pool_t *scratch_pool)
793 const char *local_relpath;
794 *work_item = svn_skel__make_empty_list(result_pool);
796 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath,
797 local_abspath, result_pool, scratch_pool));
800 svn_skel__prepend_int(TRUE, *work_item, result_pool);
802 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
803 svn_skel__prepend_str(OP_DIRECTORY_REMOVE, *work_item, result_pool);
808 /* ------------------------------------------------------------------------ */
812 /* Process the OP_FILE_MOVE work item WORK_ITEM.
813 * See svn_wc__wq_build_file_move() which generates this work item.
814 * Implements (struct work_item_dispatch).func. */
816 run_file_move(work_item_baton_t *wqb,
818 const svn_skel_t *work_item,
819 const char *wri_abspath,
820 svn_cancel_func_t cancel_func,
822 apr_pool_t *scratch_pool)
824 const svn_skel_t *arg1 = work_item->children->next;
825 const char *src_abspath, *dst_abspath;
826 const char *local_relpath;
829 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
830 SVN_ERR(svn_wc__db_from_relpath(&src_abspath, db, wri_abspath, local_relpath,
831 scratch_pool, scratch_pool));
832 local_relpath = apr_pstrmemdup(scratch_pool, arg1->next->data,
834 SVN_ERR(svn_wc__db_from_relpath(&dst_abspath, db, wri_abspath, local_relpath,
835 scratch_pool, scratch_pool));
837 /* Use svn_io_file_move() instead of svn_io_file_rename() to allow cross
838 device copies. We should not fail in the workqueue. */
840 err = svn_io_file_move(src_abspath, dst_abspath, scratch_pool);
842 /* If the source is not found, we assume the wq op is already handled */
843 if (err && APR_STATUS_IS_ENOENT(err->apr_err))
844 svn_error_clear(err);
853 svn_wc__wq_build_file_move(svn_skel_t **work_item,
855 const char *wri_abspath,
856 const char *src_abspath,
857 const char *dst_abspath,
858 apr_pool_t *result_pool,
859 apr_pool_t *scratch_pool)
861 svn_node_kind_t kind;
862 const char *local_relpath;
863 *work_item = svn_skel__make_empty_list(result_pool);
865 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
866 SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
867 SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
869 /* File must exist */
870 SVN_ERR(svn_io_check_path(src_abspath, &kind, result_pool));
872 if (kind == svn_node_none)
873 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
875 svn_dirent_local_style(src_abspath,
878 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath, dst_abspath,
879 result_pool, scratch_pool));
880 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
882 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath, src_abspath,
883 result_pool, scratch_pool));
884 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
886 svn_skel__prepend_str(OP_FILE_MOVE, *work_item, result_pool);
891 /* ------------------------------------------------------------------------ */
893 /* OP_FILE_COPY_TRANSLATED */
895 /* Process the OP_FILE_COPY_TRANSLATED work item WORK_ITEM.
896 * See run_file_copy_translated() which generates this work item.
897 * Implements (struct work_item_dispatch).func. */
899 run_file_copy_translated(work_item_baton_t *wqb,
901 const svn_skel_t *work_item,
902 const char *wri_abspath,
903 svn_cancel_func_t cancel_func,
905 apr_pool_t *scratch_pool)
907 const svn_skel_t *arg1 = work_item->children->next;
908 const char *local_abspath, *src_abspath, *dst_abspath;
909 const char *local_relpath;
910 svn_subst_eol_style_t style;
912 apr_hash_t *keywords;
913 svn_boolean_t special;
915 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
916 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
917 local_relpath, scratch_pool, scratch_pool));
919 local_relpath = apr_pstrmemdup(scratch_pool, arg1->next->data,
921 SVN_ERR(svn_wc__db_from_relpath(&src_abspath, db, wri_abspath,
922 local_relpath, scratch_pool, scratch_pool));
924 local_relpath = apr_pstrmemdup(scratch_pool, arg1->next->next->data,
925 arg1->next->next->len);
926 SVN_ERR(svn_wc__db_from_relpath(&dst_abspath, db, wri_abspath,
927 local_relpath, scratch_pool, scratch_pool));
929 SVN_ERR(svn_wc__get_translate_info(&style, &eol,
932 db, local_abspath, NULL, FALSE,
933 scratch_pool, scratch_pool));
935 SVN_ERR(svn_subst_copy_and_translate4(src_abspath, dst_abspath,
936 eol, TRUE /* repair */,
937 keywords, TRUE /* expand */,
939 cancel_func, cancel_baton,
946 svn_wc__wq_build_file_copy_translated(svn_skel_t **work_item,
948 const char *local_abspath,
949 const char *src_abspath,
950 const char *dst_abspath,
951 apr_pool_t *result_pool,
952 apr_pool_t *scratch_pool)
954 svn_node_kind_t kind;
955 const char *local_relpath;
957 *work_item = svn_skel__make_empty_list(result_pool);
959 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
960 SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
961 SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
963 /* File must exist */
964 SVN_ERR(svn_io_check_path(src_abspath, &kind, result_pool));
966 if (kind == svn_node_none)
967 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
969 svn_dirent_local_style(src_abspath,
972 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath, dst_abspath,
973 result_pool, scratch_pool));
974 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
976 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath, src_abspath,
977 result_pool, scratch_pool));
978 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
980 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath,
981 local_abspath, result_pool, scratch_pool));
982 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
984 svn_skel__prepend_str(OP_FILE_COPY_TRANSLATED, *work_item, result_pool);
989 /* ------------------------------------------------------------------------ */
991 /* OP_DIRECTORY_INSTALL */
994 run_dir_install(work_item_baton_t *wqb,
996 const svn_skel_t *work_item,
997 const char *wri_abspath,
998 svn_cancel_func_t cancel_func,
1000 apr_pool_t *scratch_pool)
1002 const svn_skel_t *arg1 = work_item->children->next;
1003 const char *local_relpath;
1004 const char *local_abspath;
1006 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
1007 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1008 local_relpath, scratch_pool, scratch_pool));
1010 SVN_ERR(svn_wc__ensure_directory(local_abspath, scratch_pool));
1012 return SVN_NO_ERROR;
1016 svn_wc__wq_build_dir_install(svn_skel_t **work_item,
1018 const char *local_abspath,
1019 apr_pool_t *result_pool,
1020 apr_pool_t *scratch_pool)
1022 const char *local_relpath;
1024 *work_item = svn_skel__make_empty_list(result_pool);
1026 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath,
1027 local_abspath, result_pool, scratch_pool));
1028 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
1030 svn_skel__prepend_str(OP_DIRECTORY_INSTALL, *work_item, result_pool);
1032 return SVN_NO_ERROR;
1036 /* ------------------------------------------------------------------------ */
1038 /* OP_SYNC_FILE_FLAGS */
1040 /* Process the OP_SYNC_FILE_FLAGS work item WORK_ITEM.
1041 * See svn_wc__wq_build_sync_file_flags() which generates this work item.
1042 * Implements (struct work_item_dispatch).func. */
1043 static svn_error_t *
1044 run_sync_file_flags(work_item_baton_t *wqb,
1046 const svn_skel_t *work_item,
1047 const char *wri_abspath,
1048 svn_cancel_func_t cancel_func,
1050 apr_pool_t *scratch_pool)
1052 const svn_skel_t *arg1 = work_item->children->next;
1053 const char *local_relpath;
1054 const char *local_abspath;
1056 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
1057 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1058 local_relpath, scratch_pool, scratch_pool));
1060 return svn_error_trace(svn_wc__sync_flags_with_props(NULL, db,
1061 local_abspath, scratch_pool));
1066 svn_wc__wq_build_sync_file_flags(svn_skel_t **work_item,
1068 const char *local_abspath,
1069 apr_pool_t *result_pool,
1070 apr_pool_t *scratch_pool)
1072 const char *local_relpath;
1073 *work_item = svn_skel__make_empty_list(result_pool);
1075 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath,
1076 local_abspath, result_pool, scratch_pool));
1078 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
1079 svn_skel__prepend_str(OP_SYNC_FILE_FLAGS, *work_item, result_pool);
1081 return SVN_NO_ERROR;
1085 /* ------------------------------------------------------------------------ */
1087 /* OP_PREJ_INSTALL */
1089 static svn_error_t *
1090 run_prej_install(work_item_baton_t *wqb,
1092 const svn_skel_t *work_item,
1093 const char *wri_abspath,
1094 svn_cancel_func_t cancel_func,
1096 apr_pool_t *scratch_pool)
1098 const svn_skel_t *arg1 = work_item->children->next;
1099 const char *local_relpath;
1100 const char *local_abspath;
1101 svn_skel_t *conflicts;
1102 const svn_skel_t *prop_conflict_skel;
1103 const char *tmp_prejfile_abspath;
1104 const char *prejfile_abspath;
1106 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
1107 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1108 local_relpath, scratch_pool, scratch_pool));
1110 SVN_ERR(svn_wc__db_read_conflict(&conflicts, NULL, NULL, db, local_abspath,
1111 scratch_pool, scratch_pool));
1113 SVN_ERR(svn_wc__conflict_read_prop_conflict(&prejfile_abspath,
1114 NULL, NULL, NULL, NULL,
1115 db, local_abspath, conflicts,
1116 scratch_pool, scratch_pool));
1118 if (arg1->next != NULL)
1119 prop_conflict_skel = arg1->next; /* Before Subversion 1.9 */
1121 prop_conflict_skel = NULL; /* Read from DB */
1123 /* Construct a property reject file in the temporary area. */
1124 SVN_ERR(svn_wc__create_prejfile(&tmp_prejfile_abspath,
1127 cancel_func, cancel_baton,
1128 scratch_pool, scratch_pool));
1130 /* ... and atomically move it into place. */
1131 SVN_ERR(svn_io_file_rename2(tmp_prejfile_abspath,
1132 prejfile_abspath, FALSE,
1135 return SVN_NO_ERROR;
1140 svn_wc__wq_build_prej_install(svn_skel_t **work_item,
1142 const char *local_abspath,
1143 /*svn_skel_t *conflict_skel,*/
1144 apr_pool_t *result_pool,
1145 apr_pool_t *scratch_pool)
1147 const char *local_relpath;
1148 *work_item = svn_skel__make_empty_list(result_pool);
1150 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath,
1151 local_abspath, result_pool, scratch_pool));
1153 /* ### In Subversion 1.7 and 1.8 we created a legacy property conflict skel
1155 if (conflict_skel != NULL)
1156 svn_skel__prepend(conflict_skel, *work_item);
1158 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
1159 svn_skel__prepend_str(OP_PREJ_INSTALL, *work_item, result_pool);
1161 return SVN_NO_ERROR;
1165 /* ------------------------------------------------------------------------ */
1167 /* OP_RECORD_FILEINFO */
1170 static svn_error_t *
1171 run_record_fileinfo(work_item_baton_t *wqb,
1173 const svn_skel_t *work_item,
1174 const char *wri_abspath,
1175 svn_cancel_func_t cancel_func,
1177 apr_pool_t *scratch_pool)
1179 const svn_skel_t *arg1 = work_item->children->next;
1180 const char *local_relpath;
1181 const char *local_abspath;
1182 apr_time_t set_time = 0;
1184 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
1186 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1187 local_relpath, scratch_pool, scratch_pool));
1193 SVN_ERR(svn_skel__parse_int(&val, arg1->next, scratch_pool));
1194 set_time = (apr_time_t)val;
1199 svn_node_kind_t kind;
1200 svn_boolean_t is_special;
1202 /* Do not set the timestamp on special files. */
1203 SVN_ERR(svn_io_check_special_path(local_abspath, &kind, &is_special,
1206 /* Don't set affected time when local_abspath does not exist or is
1208 if (kind == svn_node_file && !is_special)
1209 SVN_ERR(svn_io_set_file_affected_time(set_time, local_abspath,
1212 /* Note that we can't use the value we get here for recording as the
1213 filesystem might have a different timestamp granularity */
1217 return svn_error_trace(get_and_record_fileinfo(wqb, local_abspath,
1218 TRUE /* ignore_enoent */,
1222 /* ------------------------------------------------------------------------ */
1224 /* OP_TMP_SET_TEXT_CONFLICT_MARKERS */
1227 static svn_error_t *
1228 run_set_text_conflict_markers(work_item_baton_t *wqb,
1230 const svn_skel_t *work_item,
1231 const char *wri_abspath,
1232 svn_cancel_func_t cancel_func,
1234 apr_pool_t *scratch_pool)
1236 const svn_skel_t *arg = work_item->children->next;
1237 const char *local_relpath;
1238 const char *local_abspath;
1239 const char *old_abspath = NULL;
1240 const char *new_abspath = NULL;
1241 const char *wrk_abspath = NULL;
1243 local_relpath = apr_pstrmemdup(scratch_pool, arg->data, arg->len);
1244 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1245 local_relpath, scratch_pool, scratch_pool));
1248 local_relpath = arg->len ? apr_pstrmemdup(scratch_pool, arg->data, arg->len)
1253 SVN_ERR(svn_wc__db_from_relpath(&old_abspath, db, wri_abspath,
1255 scratch_pool, scratch_pool));
1259 local_relpath = arg->len ? apr_pstrmemdup(scratch_pool, arg->data, arg->len)
1263 SVN_ERR(svn_wc__db_from_relpath(&new_abspath, db, wri_abspath,
1265 scratch_pool, scratch_pool));
1269 local_relpath = arg->len ? apr_pstrmemdup(scratch_pool, arg->data, arg->len)
1274 SVN_ERR(svn_wc__db_from_relpath(&wrk_abspath, db, wri_abspath,
1276 scratch_pool, scratch_pool));
1279 /* Upgrade scenario: We have a workqueue item that describes how to install a
1280 non skel conflict. Fetch all the information we can to create a new style
1282 /* ### Before format 30 this is/was a common code path as we didn't install
1283 ### the conflict directly in the db. It just calls the wc_db code
1284 ### to set the right fields. */
1287 /* Check if we should combine with a property conflict... */
1288 svn_skel_t *conflicts;
1290 SVN_ERR(svn_wc__db_read_conflict(&conflicts, NULL, NULL, db, local_abspath,
1291 scratch_pool, scratch_pool));
1295 /* No conflict exists, create a basic skel */
1296 conflicts = svn_wc__conflict_skel_create(scratch_pool);
1298 SVN_ERR(svn_wc__conflict_skel_set_op_update(conflicts, NULL, NULL,
1303 /* Add the text conflict to the existing onflict */
1304 SVN_ERR(svn_wc__conflict_skel_add_text_conflict(conflicts, db,
1312 SVN_ERR(svn_wc__db_op_mark_conflict(db, local_abspath, conflicts,
1313 NULL, scratch_pool));
1315 return SVN_NO_ERROR;
1318 /* ------------------------------------------------------------------------ */
1320 /* OP_TMP_SET_PROPERTY_CONFLICT_MARKER */
1322 static svn_error_t *
1323 run_set_property_conflict_marker(work_item_baton_t *wqb,
1325 const svn_skel_t *work_item,
1326 const char *wri_abspath,
1327 svn_cancel_func_t cancel_func,
1329 apr_pool_t *scratch_pool)
1331 const svn_skel_t *arg = work_item->children->next;
1332 const char *local_relpath;
1333 const char *local_abspath;
1334 const char *prej_abspath = NULL;
1336 local_relpath = apr_pstrmemdup(scratch_pool, arg->data, arg->len);
1338 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1339 local_relpath, scratch_pool, scratch_pool));
1343 local_relpath = arg->len ? apr_pstrmemdup(scratch_pool, arg->data, arg->len)
1347 SVN_ERR(svn_wc__db_from_relpath(&prej_abspath, db, wri_abspath,
1349 scratch_pool, scratch_pool));
1352 /* Check if we should combine with a text conflict... */
1353 svn_skel_t *conflicts;
1354 apr_hash_t *prop_names;
1356 SVN_ERR(svn_wc__db_read_conflict(&conflicts, NULL, NULL,
1358 scratch_pool, scratch_pool));
1362 /* No conflict exists, create a basic skel */
1363 conflicts = svn_wc__conflict_skel_create(scratch_pool);
1365 SVN_ERR(svn_wc__conflict_skel_set_op_update(conflicts, NULL, NULL,
1370 prop_names = apr_hash_make(scratch_pool);
1371 SVN_ERR(svn_wc__conflict_skel_add_prop_conflict(conflicts, db,
1379 SVN_ERR(svn_wc__db_op_mark_conflict(db, local_abspath, conflicts,
1380 NULL, scratch_pool));
1382 return SVN_NO_ERROR;
1385 /* ------------------------------------------------------------------------ */
1387 static const struct work_item_dispatch dispatch_table[] = {
1388 { OP_FILE_COMMIT, run_file_commit },
1389 { OP_FILE_INSTALL, run_file_install },
1390 { OP_FILE_REMOVE, run_file_remove },
1391 { OP_FILE_MOVE, run_file_move },
1392 { OP_FILE_COPY_TRANSLATED, run_file_copy_translated },
1393 { OP_SYNC_FILE_FLAGS, run_sync_file_flags },
1394 { OP_PREJ_INSTALL, run_prej_install },
1395 { OP_DIRECTORY_REMOVE, run_dir_remove },
1396 { OP_DIRECTORY_INSTALL, run_dir_install },
1399 { OP_POSTUPGRADE, run_postupgrade },
1401 /* Legacy workqueue items. No longer created */
1402 { OP_BASE_REMOVE, run_base_remove },
1403 { OP_RECORD_FILEINFO, run_record_fileinfo },
1404 { OP_TMP_SET_TEXT_CONFLICT_MARKERS, run_set_text_conflict_markers },
1405 { OP_TMP_SET_PROPERTY_CONFLICT_MARKER, run_set_property_conflict_marker },
1411 struct work_item_baton_t
1413 apr_pool_t *result_pool; /* Pool to allocate result in */
1415 svn_boolean_t used; /* needs reset */
1417 apr_hash_t *record_map; /* const char * -> svn_io_dirent2_t map */
1421 static svn_error_t *
1422 dispatch_work_item(work_item_baton_t *wqb,
1424 const char *wri_abspath,
1425 const svn_skel_t *work_item,
1426 svn_cancel_func_t cancel_func,
1428 apr_pool_t *scratch_pool)
1430 const struct work_item_dispatch *scan;
1432 /* Scan the dispatch table for a function to handle this work item. */
1433 for (scan = &dispatch_table[0]; scan->name != NULL; ++scan)
1435 if (svn_skel__matches_atom(work_item->children, scan->name))
1438 #ifdef SVN_DEBUG_WORK_QUEUE
1439 SVN_DBG(("dispatch: operation='%s'\n", scan->name));
1441 SVN_ERR((*scan->func)(wqb, db, work_item, wri_abspath,
1442 cancel_func, cancel_baton,
1445 #ifdef SVN_RUN_WORK_QUEUE_TWICE
1446 #ifdef SVN_DEBUG_WORK_QUEUE
1447 SVN_DBG(("dispatch: operation='%s'\n", scan->name));
1449 /* Being able to run every workqueue item twice is one
1450 requirement for workqueues to be restartable. */
1451 SVN_ERR((*scan->func)(db, work_item, wri_abspath,
1452 cancel_func, cancel_baton,
1460 if (scan->name == NULL)
1462 /* We should know about ALL possible work items here. If we do not,
1463 then something is wrong. Most likely, some kind of format/code
1464 skew. There is nothing more we can do. Erasing or ignoring this
1465 work item could leave the WC in an even more broken state.
1467 Contrary to issue #1581, we cannot simply remove work items and
1468 continue, so bail out with an error. */
1469 return svn_error_createf(SVN_ERR_WC_BAD_ADM_LOG, NULL,
1470 _("Unrecognized work item in the queue"));
1473 return SVN_NO_ERROR;
1478 svn_wc__wq_run(svn_wc__db_t *db,
1479 const char *wri_abspath,
1480 svn_cancel_func_t cancel_func,
1482 apr_pool_t *scratch_pool)
1484 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1485 apr_uint64_t last_id = 0;
1486 work_item_baton_t wib = { 0 };
1487 wib.result_pool = svn_pool_create(scratch_pool);
1489 #ifdef SVN_DEBUG_WORK_QUEUE
1490 SVN_DBG(("wq_run: wri='%s'\n", wri_abspath));
1492 static int count = 0;
1493 const char *count_env_var = getenv("SVN_DEBUG_WORK_QUEUE");
1496 SVN_ERR(svn_cstring_atoi(&count_env_val, count_env_var));
1498 if (count_env_var && ++count == count_env_val)
1499 return svn_error_create(SVN_ERR_CANCELLED, NULL, "fake cancel");
1506 svn_skel_t *work_item;
1509 svn_pool_clear(iterpool);
1513 /* Make sure to do this *early* in the loop iteration. There may
1514 be a LAST_ID that needs to be marked as completed, *before* we
1515 start worrying about anything else. */
1516 SVN_ERR(svn_wc__db_wq_fetch_next(&id, &work_item, db, wri_abspath,
1517 last_id, iterpool, iterpool));
1521 /* Make sure to do this *early* in the loop iteration. There may
1522 be a LAST_ID that needs to be marked as completed, *before* we
1523 start worrying about anything else. */
1524 SVN_ERR(svn_wc__db_wq_record_and_fetch_next(&id, &work_item,
1526 last_id, wib.record_map,
1530 svn_pool_clear(wib.result_pool);
1531 wib.record_map = NULL;
1535 /* Stop work queue processing, if requested. A future 'svn cleanup'
1536 should be able to continue the processing. Note that we may
1537 have WORK_ITEM, but we'll just skip its processing for now. */
1539 SVN_ERR(cancel_func(cancel_baton));
1541 /* If we have a WORK_ITEM, then process the sucker. Otherwise,
1543 if (work_item == NULL)
1546 err = dispatch_work_item(&wib, db, wri_abspath, work_item,
1547 cancel_func, cancel_baton, iterpool);
1550 const char *skel = svn_skel__unparse(work_item, scratch_pool)->data;
1552 return svn_error_createf(SVN_ERR_WC_BAD_ADM_LOG, err,
1553 _("Failed to run the WC DB work queue "
1554 "associated with '%s', work item %d %s"),
1555 svn_dirent_local_style(wri_abspath,
1560 /* The work item finished without error. Mark it completed
1561 in the next loop. */
1565 svn_pool_destroy(iterpool);
1566 return SVN_NO_ERROR;
1571 svn_wc__wq_merge(svn_skel_t *work_item1,
1572 svn_skel_t *work_item2,
1573 apr_pool_t *result_pool)
1575 /* If either argument is NULL, then just return the other. */
1576 if (work_item1 == NULL)
1578 if (work_item2 == NULL)
1581 /* We have two items. Figure out how to join them. */
1582 if (SVN_WC__SINGLE_WORK_ITEM(work_item1))
1584 if (SVN_WC__SINGLE_WORK_ITEM(work_item2))
1586 /* Both are singular work items. Construct a list, then put
1587 both work items into it (in the proper order). */
1589 svn_skel_t *result = svn_skel__make_empty_list(result_pool);
1591 svn_skel__prepend(work_item2, result);
1592 svn_skel__prepend(work_item1, result);
1596 /* WORK_ITEM2 is a list of work items. We can simply shove WORK_ITEM1
1597 in the front to keep the ordering. */
1598 svn_skel__prepend(work_item1, work_item2);
1601 /* WORK_ITEM1 is a list of work items. */
1603 if (SVN_WC__SINGLE_WORK_ITEM(work_item2))
1605 /* Put WORK_ITEM2 onto the end of the WORK_ITEM1 list. */
1606 svn_skel__append(work_item1, work_item2);
1610 /* We have two lists of work items. We need to chain all of the work
1611 items into one big list. We will leave behind the WORK_ITEM2 skel,
1612 as we only want its children. */
1613 svn_skel__append(work_item1, work_item2->children);
1618 static svn_error_t *
1619 get_and_record_fileinfo(work_item_baton_t *wqb,
1620 const char *local_abspath,
1621 svn_boolean_t ignore_enoent,
1622 apr_pool_t *scratch_pool)
1624 const svn_io_dirent2_t *dirent;
1626 SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath, FALSE, ignore_enoent,
1627 wqb->result_pool, scratch_pool));
1629 if (dirent->kind != svn_node_file)
1630 return SVN_NO_ERROR;
1634 if (! wqb->record_map)
1635 wqb->record_map = apr_hash_make(wqb->result_pool);
1637 svn_hash_sets(wqb->record_map, apr_pstrdup(wqb->result_pool, local_abspath),
1640 return SVN_NO_ERROR;