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_types.h"
27 #include "svn_pools.h"
28 #include "svn_dirent_uri.h"
29 #include "svn_subst.h"
35 #include "workqueue.h"
36 #include "adm_files.h"
37 #include "conflicts.h"
38 #include "translate.h"
40 #include "svn_private_config.h"
41 #include "private/svn_skel.h"
44 /* Workqueue operation names. */
45 #define OP_FILE_COMMIT "file-commit"
46 #define OP_FILE_INSTALL "file-install"
47 #define OP_FILE_REMOVE "file-remove"
48 #define OP_FILE_MOVE "file-move"
49 #define OP_FILE_COPY_TRANSLATED "file-translate"
50 #define OP_SYNC_FILE_FLAGS "sync-file-flags"
51 #define OP_PREJ_INSTALL "prej-install"
52 #define OP_DIRECTORY_REMOVE "dir-remove"
53 #define OP_DIRECTORY_INSTALL "dir-install"
55 #define OP_POSTUPGRADE "postupgrade"
58 #define OP_BASE_REMOVE "base-remove"
59 #define OP_RECORD_FILEINFO "record-fileinfo"
60 #define OP_TMP_SET_TEXT_CONFLICT_MARKERS "tmp-set-text-conflict-markers"
61 #define OP_TMP_SET_PROPERTY_CONFLICT_MARKER "tmp-set-property-conflict-marker"
63 /* For work queue debugging. Generates output about its operation. */
64 /* #define SVN_DEBUG_WORK_QUEUE */
66 typedef struct work_item_baton_t work_item_baton_t;
68 struct work_item_dispatch {
70 svn_error_t *(*func)(work_item_baton_t *wqb,
72 const svn_skel_t *work_item,
73 const char *wri_abspath,
74 svn_cancel_func_t cancel_func,
76 apr_pool_t *scratch_pool);
79 /* Forward definition */
81 get_and_record_fileinfo(work_item_baton_t *wqb,
82 const char *local_abspath,
83 svn_boolean_t ignore_enoent,
84 apr_pool_t *scratch_pool);
86 /* ------------------------------------------------------------------------ */
89 /* Removes a BASE_NODE and all it's data, leaving any adds and copies as is.
90 Do this as a depth first traversal to make sure than any parent still exists
94 /* Process the OP_REMOVE_BASE work item WORK_ITEM.
95 * See svn_wc__wq_build_remove_base() which generates this work item.
96 * Implements (struct work_item_dispatch).func. */
98 run_base_remove(work_item_baton_t *wqb,
100 const svn_skel_t *work_item,
101 const char *wri_abspath,
102 svn_cancel_func_t cancel_func,
104 apr_pool_t *scratch_pool)
106 const svn_skel_t *arg1 = work_item->children->next;
107 const char *local_relpath;
108 const char *local_abspath;
109 svn_revnum_t not_present_rev = SVN_INVALID_REVNUM;
112 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
113 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
114 local_relpath, scratch_pool, scratch_pool));
115 SVN_ERR(svn_skel__parse_int(&val, arg1->next, scratch_pool));
117 if (arg1->next->next)
119 not_present_rev = (svn_revnum_t)val;
121 SVN_ERR(svn_skel__parse_int(&val, arg1->next->next, scratch_pool));
125 svn_boolean_t keep_not_present;
127 SVN_ERR_ASSERT(SVN_WC__VERSION <= 28); /* Case unused in later versions*/
129 keep_not_present = (val != 0);
131 if (keep_not_present)
133 SVN_ERR(svn_wc__db_base_get_info(NULL, NULL,
134 ¬_present_rev, NULL,
136 NULL, NULL, NULL, NULL, NULL, NULL,
139 scratch_pool, scratch_pool));
143 SVN_ERR(svn_wc__db_base_remove(db, local_abspath,
144 FALSE /* keep_as_working */,
145 TRUE /* queue_deletes */,
146 FALSE /* remove_locks */,
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;
398 const char *adm_path;
399 const char *temp_path;
402 err = svn_wc__wipe_postupgrade(wri_abspath, FALSE,
403 cancel_func, cancel_baton, scratch_pool);
404 if (err && err->apr_err == SVN_ERR_ENTRY_NOT_FOUND)
405 /* No entry, this can happen when the wq item is rerun. */
406 svn_error_clear(err);
410 SVN_ERR(svn_wc__db_get_wcroot(&wcroot_abspath, db, wri_abspath,
411 scratch_pool, scratch_pool));
413 adm_path = svn_wc__adm_child(wcroot_abspath, NULL, scratch_pool);
414 entries_path = svn_wc__adm_child(wcroot_abspath, SVN_WC__ADM_ENTRIES,
416 format_path = svn_wc__adm_child(wcroot_abspath, SVN_WC__ADM_FORMAT,
419 /* Write the 'format' and 'entries' files.
421 ### The order may matter for some sufficiently old clients.. but
422 ### this code only runs during upgrade after the files had been
423 ### removed earlier during the upgrade. */
424 SVN_ERR(svn_io_write_unique(&temp_path, adm_path, SVN_WC__NON_ENTRIES_STRING,
425 sizeof(SVN_WC__NON_ENTRIES_STRING) - 1,
426 svn_io_file_del_none, scratch_pool));
427 SVN_ERR(svn_io_file_rename(temp_path, format_path, scratch_pool));
429 SVN_ERR(svn_io_write_unique(&temp_path, adm_path, SVN_WC__NON_ENTRIES_STRING,
430 sizeof(SVN_WC__NON_ENTRIES_STRING) - 1,
431 svn_io_file_del_none, scratch_pool));
432 SVN_ERR(svn_io_file_rename(temp_path, entries_path, scratch_pool));
438 svn_wc__wq_build_postupgrade(svn_skel_t **work_item,
439 apr_pool_t *result_pool)
441 *work_item = svn_skel__make_empty_list(result_pool);
443 svn_skel__prepend_str(OP_POSTUPGRADE, *work_item, result_pool);
448 /* ------------------------------------------------------------------------ */
450 /* OP_FILE_INSTALL */
452 /* Process the OP_FILE_INSTALL work item WORK_ITEM.
453 * See svn_wc__wq_build_file_install() which generates this work item.
454 * Implements (struct work_item_dispatch).func. */
456 run_file_install(work_item_baton_t *wqb,
458 const svn_skel_t *work_item,
459 const char *wri_abspath,
460 svn_cancel_func_t cancel_func,
462 apr_pool_t *scratch_pool)
464 const svn_skel_t *arg1 = work_item->children->next;
465 const svn_skel_t *arg4 = arg1->next->next->next;
466 const char *local_relpath;
467 const char *local_abspath;
468 svn_boolean_t use_commit_times;
469 svn_boolean_t record_fileinfo;
470 svn_boolean_t special;
471 svn_stream_t *src_stream;
472 svn_subst_eol_style_t style;
474 apr_hash_t *keywords;
475 const char *temp_dir_abspath;
476 svn_stream_t *dst_stream;
477 const char *dst_abspath;
479 const char *wcroot_abspath;
480 const char *source_abspath;
481 const svn_checksum_t *checksum;
483 apr_time_t changed_date;
485 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
486 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
487 local_relpath, scratch_pool, scratch_pool));
489 SVN_ERR(svn_skel__parse_int(&val, arg1->next, scratch_pool));
490 use_commit_times = (val != 0);
491 SVN_ERR(svn_skel__parse_int(&val, arg1->next->next, scratch_pool));
492 record_fileinfo = (val != 0);
494 SVN_ERR(svn_wc__db_read_node_install_info(&wcroot_abspath,
497 db, local_abspath, wri_abspath,
498 scratch_pool, scratch_pool));
502 /* Use the provided path for the source. */
503 local_relpath = apr_pstrmemdup(scratch_pool, arg4->data, arg4->len);
504 SVN_ERR(svn_wc__db_from_relpath(&source_abspath, db, wri_abspath,
506 scratch_pool, scratch_pool));
510 /* This error replaces a previous assertion. Reporting an error from here
511 leaves the workingqueue operation in place, so the working copy is
514 But when we report this error the user at least knows what node has
515 this specific problem, so maybe we can find out why users see this
517 return svn_error_createf(SVN_ERR_WC_CORRUPT_TEXT_BASE, NULL,
518 _("Can't install '%s' from pristine store, "
519 "because no checksum is recorded for this "
521 svn_dirent_local_style(local_abspath,
526 SVN_ERR(svn_wc__db_pristine_get_future_path(&source_abspath,
529 scratch_pool, scratch_pool));
532 SVN_ERR(svn_stream_open_readonly(&src_stream, source_abspath,
533 scratch_pool, scratch_pool));
535 /* Fetch all the translation bits. */
536 SVN_ERR(svn_wc__get_translate_info(&style, &eol,
538 &special, db, local_abspath,
540 scratch_pool, scratch_pool));
543 /* When this stream is closed, the resulting special file will
544 atomically be created/moved into place at LOCAL_ABSPATH. */
545 SVN_ERR(svn_subst_create_specialfile(&dst_stream, local_abspath,
546 scratch_pool, scratch_pool));
548 /* Copy the "repository normal" form of the special file into the
550 SVN_ERR(svn_stream_copy3(src_stream, dst_stream,
551 cancel_func, cancel_baton,
554 /* No need to set exec or read-only flags on special files. */
556 /* ### Shouldn't this record a timestamp and size, etc.? */
560 if (svn_subst_translation_required(style, eol, keywords,
562 TRUE /* force_eol_check */))
564 /* Wrap it in a translating (expanding) stream. */
565 src_stream = svn_subst_stream_translated(src_stream, eol,
572 /* Where is the Right Place to put a temp file in this working copy? */
573 SVN_ERR(svn_wc__db_temp_wcroot_tempdir(&temp_dir_abspath,
575 scratch_pool, scratch_pool));
577 /* Translate to a temporary file. We don't want the user seeing a partial
578 file, nor let them muck with it while we translate. We may also need to
579 get its TRANSLATED_SIZE before the user can monkey it. */
580 SVN_ERR(svn_stream_open_unique(&dst_stream, &dst_abspath,
582 svn_io_file_del_none,
583 scratch_pool, scratch_pool));
585 /* Copy from the source to the dest, translating as we go. This will also
586 close both streams. */
587 SVN_ERR(svn_stream_copy3(src_stream, dst_stream,
588 cancel_func, cancel_baton,
591 /* All done. Move the file into place. */
596 err = svn_io_file_rename(dst_abspath, local_abspath, scratch_pool);
598 /* With a single db we might want to install files in a missing directory.
599 Simply trying this scenario on error won't do any harm and at least
600 one user reported this problem on IRC. */
601 if (err && APR_STATUS_IS_ENOENT(err->apr_err))
605 err2 = svn_io_make_dir_recursively(svn_dirent_dirname(local_abspath,
610 /* Creating directory didn't work: Return all errors */
611 return svn_error_trace(svn_error_compose_create(err, err2));
613 /* We could create a directory: retry install */
614 svn_error_clear(err);
616 SVN_ERR(svn_io_file_rename(dst_abspath, local_abspath, scratch_pool));
622 /* Tweak the on-disk file according to its properties. */
624 if (props && svn_hash_gets(props, SVN_PROP_EXECUTABLE))
625 SVN_ERR(svn_io_set_file_executable(local_abspath, TRUE, FALSE,
629 /* Note that this explicitly checks the pristine properties, to make sure
630 that when the lock is locally set (=modification) it is not read only */
631 if (props && svn_hash_gets(props, SVN_PROP_NEEDS_LOCK))
633 svn_wc__db_status_t status;
634 svn_wc__db_lock_t *lock;
635 SVN_ERR(svn_wc__db_read_info(&status, NULL, NULL, NULL, NULL, NULL, NULL,
636 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
637 NULL, NULL, &lock, NULL, NULL, NULL, NULL,
638 NULL, NULL, NULL, NULL, NULL, NULL,
640 scratch_pool, scratch_pool));
642 if (!lock && status != svn_wc__db_status_added)
643 SVN_ERR(svn_io_set_file_read_only(local_abspath, FALSE, scratch_pool));
646 if (use_commit_times)
649 SVN_ERR(svn_io_set_file_affected_time(changed_date,
654 /* ### this should happen before we rename the file into place. */
657 SVN_ERR(get_and_record_fileinfo(wqb, local_abspath,
658 FALSE /* ignore_enoent */,
667 svn_wc__wq_build_file_install(svn_skel_t **work_item,
669 const char *local_abspath,
670 const char *source_abspath,
671 svn_boolean_t use_commit_times,
672 svn_boolean_t record_fileinfo,
673 apr_pool_t *result_pool,
674 apr_pool_t *scratch_pool)
676 const char *local_relpath;
677 const char *wri_abspath;
678 *work_item = svn_skel__make_empty_list(result_pool);
680 /* Use the directory of the file to install as wri_abspath to avoid
681 filestats on just obtaining the wc-root */
682 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
684 /* If a SOURCE_ABSPATH was provided, then put it into the skel. If this
685 value is not provided, then the file's pristine contents will be used. */
686 if (source_abspath != NULL)
688 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath,
690 result_pool, scratch_pool));
692 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
695 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath,
696 local_abspath, result_pool, scratch_pool));
698 svn_skel__prepend_int(record_fileinfo, *work_item, result_pool);
699 svn_skel__prepend_int(use_commit_times, *work_item, result_pool);
700 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
701 svn_skel__prepend_str(OP_FILE_INSTALL, *work_item, result_pool);
707 /* ------------------------------------------------------------------------ */
711 /* Process the OP_FILE_REMOVE work item WORK_ITEM.
712 * See svn_wc__wq_build_file_remove() which generates this work item.
713 * Implements (struct work_item_dispatch).func. */
715 run_file_remove(work_item_baton_t *wqb,
717 const svn_skel_t *work_item,
718 const char *wri_abspath,
719 svn_cancel_func_t cancel_func,
721 apr_pool_t *scratch_pool)
723 const svn_skel_t *arg1 = work_item->children->next;
724 const char *local_relpath;
725 const char *local_abspath;
727 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
728 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
729 local_relpath, scratch_pool, scratch_pool));
731 /* Remove the path, no worrying if it isn't there. */
732 return svn_error_trace(svn_io_remove_file2(local_abspath, TRUE,
738 svn_wc__wq_build_file_remove(svn_skel_t **work_item,
740 const char *wri_abspath,
741 const char *local_abspath,
742 apr_pool_t *result_pool,
743 apr_pool_t *scratch_pool)
745 const char *local_relpath;
746 *work_item = svn_skel__make_empty_list(result_pool);
748 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath,
749 local_abspath, result_pool, scratch_pool));
751 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
752 svn_skel__prepend_str(OP_FILE_REMOVE, *work_item, result_pool);
757 /* ------------------------------------------------------------------------ */
759 /* OP_DIRECTORY_REMOVE */
761 /* Process the OP_FILE_REMOVE work item WORK_ITEM.
762 * See svn_wc__wq_build_file_remove() which generates this work item.
763 * Implements (struct work_item_dispatch).func. */
765 run_dir_remove(work_item_baton_t *wqb,
767 const svn_skel_t *work_item,
768 const char *wri_abspath,
769 svn_cancel_func_t cancel_func,
771 apr_pool_t *scratch_pool)
773 const svn_skel_t *arg1 = work_item->children->next;
774 const char *local_relpath;
775 const char *local_abspath;
776 svn_boolean_t recursive;
778 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
779 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
780 local_relpath, scratch_pool, scratch_pool));
786 SVN_ERR(svn_skel__parse_int(&val, arg1->next, scratch_pool));
788 recursive = (val != 0);
791 /* Remove the path, no worrying if it isn't there. */
793 return svn_error_trace(
794 svn_io_remove_dir2(local_abspath, TRUE,
795 cancel_func, cancel_baton,
801 err = svn_io_dir_remove_nonrecursive(local_abspath, scratch_pool);
803 if (err && (APR_STATUS_IS_ENOENT(err->apr_err)
804 || SVN__APR_STATUS_IS_ENOTDIR(err->apr_err)
805 || APR_STATUS_IS_ENOTEMPTY(err->apr_err)))
807 svn_error_clear(err);
811 return svn_error_trace(err);
816 svn_wc__wq_build_dir_remove(svn_skel_t **work_item,
818 const char *wri_abspath,
819 const char *local_abspath,
820 svn_boolean_t recursive,
821 apr_pool_t *result_pool,
822 apr_pool_t *scratch_pool)
824 const char *local_relpath;
825 *work_item = svn_skel__make_empty_list(result_pool);
827 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath,
828 local_abspath, result_pool, scratch_pool));
831 svn_skel__prepend_int(TRUE, *work_item, result_pool);
833 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
834 svn_skel__prepend_str(OP_DIRECTORY_REMOVE, *work_item, result_pool);
839 /* ------------------------------------------------------------------------ */
843 /* Process the OP_FILE_MOVE work item WORK_ITEM.
844 * See svn_wc__wq_build_file_move() which generates this work item.
845 * Implements (struct work_item_dispatch).func. */
847 run_file_move(work_item_baton_t *wqb,
849 const svn_skel_t *work_item,
850 const char *wri_abspath,
851 svn_cancel_func_t cancel_func,
853 apr_pool_t *scratch_pool)
855 const svn_skel_t *arg1 = work_item->children->next;
856 const char *src_abspath, *dst_abspath;
857 const char *local_relpath;
860 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
861 SVN_ERR(svn_wc__db_from_relpath(&src_abspath, db, wri_abspath, local_relpath,
862 scratch_pool, scratch_pool));
863 local_relpath = apr_pstrmemdup(scratch_pool, arg1->next->data,
865 SVN_ERR(svn_wc__db_from_relpath(&dst_abspath, db, wri_abspath, local_relpath,
866 scratch_pool, scratch_pool));
868 /* Use svn_io_file_move() instead of svn_io_file_rename() to allow cross
869 device copies. We should not fail in the workqueue. */
871 err = svn_io_file_move(src_abspath, dst_abspath, scratch_pool);
873 /* If the source is not found, we assume the wq op is already handled */
874 if (err && APR_STATUS_IS_ENOENT(err->apr_err))
875 svn_error_clear(err);
884 svn_wc__wq_build_file_move(svn_skel_t **work_item,
886 const char *wri_abspath,
887 const char *src_abspath,
888 const char *dst_abspath,
889 apr_pool_t *result_pool,
890 apr_pool_t *scratch_pool)
892 svn_node_kind_t kind;
893 const char *local_relpath;
894 *work_item = svn_skel__make_empty_list(result_pool);
896 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
897 SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
898 SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
900 /* File must exist */
901 SVN_ERR(svn_io_check_path(src_abspath, &kind, result_pool));
903 if (kind == svn_node_none)
904 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
906 svn_dirent_local_style(src_abspath,
909 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath, dst_abspath,
910 result_pool, scratch_pool));
911 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
913 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, wri_abspath, src_abspath,
914 result_pool, scratch_pool));
915 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
917 svn_skel__prepend_str(OP_FILE_MOVE, *work_item, result_pool);
922 /* ------------------------------------------------------------------------ */
924 /* OP_FILE_COPY_TRANSLATED */
926 /* Process the OP_FILE_COPY_TRANSLATED work item WORK_ITEM.
927 * See run_file_copy_translated() which generates this work item.
928 * Implements (struct work_item_dispatch).func. */
930 run_file_copy_translated(work_item_baton_t *wqb,
932 const svn_skel_t *work_item,
933 const char *wri_abspath,
934 svn_cancel_func_t cancel_func,
936 apr_pool_t *scratch_pool)
938 const svn_skel_t *arg1 = work_item->children->next;
939 const char *local_abspath, *src_abspath, *dst_abspath;
940 const char *local_relpath;
941 svn_subst_eol_style_t style;
943 apr_hash_t *keywords;
944 svn_boolean_t special;
946 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
947 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
948 local_relpath, scratch_pool, scratch_pool));
950 local_relpath = apr_pstrmemdup(scratch_pool, arg1->next->data,
952 SVN_ERR(svn_wc__db_from_relpath(&src_abspath, db, wri_abspath,
953 local_relpath, scratch_pool, scratch_pool));
955 local_relpath = apr_pstrmemdup(scratch_pool, arg1->next->next->data,
956 arg1->next->next->len);
957 SVN_ERR(svn_wc__db_from_relpath(&dst_abspath, db, wri_abspath,
958 local_relpath, scratch_pool, scratch_pool));
960 SVN_ERR(svn_wc__get_translate_info(&style, &eol,
963 db, local_abspath, NULL, FALSE,
964 scratch_pool, scratch_pool));
966 SVN_ERR(svn_subst_copy_and_translate4(src_abspath, dst_abspath,
967 eol, TRUE /* repair */,
968 keywords, TRUE /* expand */,
970 cancel_func, cancel_baton,
977 svn_wc__wq_build_file_copy_translated(svn_skel_t **work_item,
979 const char *local_abspath,
980 const char *src_abspath,
981 const char *dst_abspath,
982 apr_pool_t *result_pool,
983 apr_pool_t *scratch_pool)
985 svn_node_kind_t kind;
986 const char *local_relpath;
988 *work_item = svn_skel__make_empty_list(result_pool);
990 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
991 SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
992 SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
994 /* File must exist */
995 SVN_ERR(svn_io_check_path(src_abspath, &kind, result_pool));
997 if (kind == svn_node_none)
998 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
1000 svn_dirent_local_style(src_abspath,
1003 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath, dst_abspath,
1004 result_pool, scratch_pool));
1005 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
1007 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath, src_abspath,
1008 result_pool, scratch_pool));
1009 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
1011 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath,
1012 local_abspath, result_pool, scratch_pool));
1013 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
1015 svn_skel__prepend_str(OP_FILE_COPY_TRANSLATED, *work_item, result_pool);
1017 return SVN_NO_ERROR;
1020 /* ------------------------------------------------------------------------ */
1022 /* OP_DIRECTORY_INSTALL */
1024 static svn_error_t *
1025 run_dir_install(work_item_baton_t *wqb,
1027 const svn_skel_t *work_item,
1028 const char *wri_abspath,
1029 svn_cancel_func_t cancel_func,
1031 apr_pool_t *scratch_pool)
1033 const svn_skel_t *arg1 = work_item->children->next;
1034 const char *local_relpath;
1035 const char *local_abspath;
1037 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
1038 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1039 local_relpath, scratch_pool, scratch_pool));
1041 SVN_ERR(svn_wc__ensure_directory(local_abspath, scratch_pool));
1043 return SVN_NO_ERROR;
1047 svn_wc__wq_build_dir_install(svn_skel_t **work_item,
1049 const char *local_abspath,
1050 apr_pool_t *scratch_pool,
1051 apr_pool_t *result_pool)
1053 const char *local_relpath;
1055 *work_item = svn_skel__make_empty_list(result_pool);
1057 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath,
1058 local_abspath, result_pool, scratch_pool));
1059 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
1061 svn_skel__prepend_str(OP_DIRECTORY_INSTALL, *work_item, result_pool);
1063 return SVN_NO_ERROR;
1067 /* ------------------------------------------------------------------------ */
1069 /* OP_SYNC_FILE_FLAGS */
1071 /* Process the OP_SYNC_FILE_FLAGS work item WORK_ITEM.
1072 * See svn_wc__wq_build_sync_file_flags() which generates this work item.
1073 * Implements (struct work_item_dispatch).func. */
1074 static svn_error_t *
1075 run_sync_file_flags(work_item_baton_t *wqb,
1077 const svn_skel_t *work_item,
1078 const char *wri_abspath,
1079 svn_cancel_func_t cancel_func,
1081 apr_pool_t *scratch_pool)
1083 const svn_skel_t *arg1 = work_item->children->next;
1084 const char *local_relpath;
1085 const char *local_abspath;
1087 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
1088 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1089 local_relpath, scratch_pool, scratch_pool));
1091 return svn_error_trace(svn_wc__sync_flags_with_props(NULL, db,
1092 local_abspath, scratch_pool));
1097 svn_wc__wq_build_sync_file_flags(svn_skel_t **work_item,
1099 const char *local_abspath,
1100 apr_pool_t *result_pool,
1101 apr_pool_t *scratch_pool)
1103 const char *local_relpath;
1104 *work_item = svn_skel__make_empty_list(result_pool);
1106 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath,
1107 local_abspath, result_pool, scratch_pool));
1109 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
1110 svn_skel__prepend_str(OP_SYNC_FILE_FLAGS, *work_item, result_pool);
1112 return SVN_NO_ERROR;
1116 /* ------------------------------------------------------------------------ */
1118 /* OP_PREJ_INSTALL */
1120 static svn_error_t *
1121 run_prej_install(work_item_baton_t *wqb,
1123 const svn_skel_t *work_item,
1124 const char *wri_abspath,
1125 svn_cancel_func_t cancel_func,
1127 apr_pool_t *scratch_pool)
1129 const svn_skel_t *arg1 = work_item->children->next;
1130 const char *local_relpath;
1131 const char *local_abspath;
1132 svn_skel_t *conflicts;
1133 const svn_skel_t *prop_conflict_skel;
1134 const char *tmp_prejfile_abspath;
1135 const char *prejfile_abspath;
1137 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
1138 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1139 local_relpath, scratch_pool, scratch_pool));
1141 SVN_ERR(svn_wc__db_read_conflict(&conflicts, db, local_abspath,
1142 scratch_pool, scratch_pool));
1144 SVN_ERR(svn_wc__conflict_read_prop_conflict(&prejfile_abspath,
1145 NULL, NULL, NULL, NULL,
1146 db, local_abspath, conflicts,
1147 scratch_pool, scratch_pool));
1149 if (arg1->next != NULL)
1150 prop_conflict_skel = arg1->next;
1152 SVN_ERR_MALFUNCTION(); /* ### wc_db can't provide it ... yet. */
1154 /* Construct a property reject file in the temporary area. */
1155 SVN_ERR(svn_wc__create_prejfile(&tmp_prejfile_abspath,
1158 scratch_pool, scratch_pool));
1160 /* ... and atomically move it into place. */
1161 SVN_ERR(svn_io_file_rename(tmp_prejfile_abspath,
1165 return SVN_NO_ERROR;
1170 svn_wc__wq_build_prej_install(svn_skel_t **work_item,
1172 const char *local_abspath,
1173 svn_skel_t *conflict_skel,
1174 apr_pool_t *result_pool,
1175 apr_pool_t *scratch_pool)
1177 const char *local_relpath;
1178 *work_item = svn_skel__make_empty_list(result_pool);
1180 /* ### gotta have this, today */
1181 SVN_ERR_ASSERT(conflict_skel != NULL);
1183 SVN_ERR(svn_wc__db_to_relpath(&local_relpath, db, local_abspath,
1184 local_abspath, result_pool, scratch_pool));
1186 if (conflict_skel != NULL)
1187 svn_skel__prepend(conflict_skel, *work_item);
1188 svn_skel__prepend_str(local_relpath, *work_item, result_pool);
1189 svn_skel__prepend_str(OP_PREJ_INSTALL, *work_item, result_pool);
1191 return SVN_NO_ERROR;
1195 /* ------------------------------------------------------------------------ */
1197 /* OP_RECORD_FILEINFO */
1200 static svn_error_t *
1201 run_record_fileinfo(work_item_baton_t *wqb,
1203 const svn_skel_t *work_item,
1204 const char *wri_abspath,
1205 svn_cancel_func_t cancel_func,
1207 apr_pool_t *scratch_pool)
1209 const svn_skel_t *arg1 = work_item->children->next;
1210 const char *local_relpath;
1211 const char *local_abspath;
1212 apr_time_t set_time = 0;
1214 local_relpath = apr_pstrmemdup(scratch_pool, arg1->data, arg1->len);
1216 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1217 local_relpath, scratch_pool, scratch_pool));
1223 SVN_ERR(svn_skel__parse_int(&val, arg1->next, scratch_pool));
1224 set_time = (apr_time_t)val;
1229 svn_node_kind_t kind;
1230 svn_boolean_t is_special;
1232 /* Do not set the timestamp on special files. */
1233 SVN_ERR(svn_io_check_special_path(local_abspath, &kind, &is_special,
1236 /* Don't set affected time when local_abspath does not exist or is
1238 if (kind == svn_node_file && !is_special)
1239 SVN_ERR(svn_io_set_file_affected_time(set_time, local_abspath,
1242 /* Note that we can't use the value we get here for recording as the
1243 filesystem might have a different timestamp granularity */
1247 return svn_error_trace(get_and_record_fileinfo(wqb, local_abspath,
1248 TRUE /* ignore_enoent */,
1252 /* ------------------------------------------------------------------------ */
1254 /* OP_TMP_SET_TEXT_CONFLICT_MARKERS */
1257 static svn_error_t *
1258 run_set_text_conflict_markers(work_item_baton_t *wqb,
1260 const svn_skel_t *work_item,
1261 const char *wri_abspath,
1262 svn_cancel_func_t cancel_func,
1264 apr_pool_t *scratch_pool)
1266 const svn_skel_t *arg = work_item->children->next;
1267 const char *local_relpath;
1268 const char *local_abspath;
1269 const char *old_abspath = NULL;
1270 const char *new_abspath = NULL;
1271 const char *wrk_abspath = NULL;
1273 local_relpath = apr_pstrmemdup(scratch_pool, arg->data, arg->len);
1274 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1275 local_relpath, scratch_pool, scratch_pool));
1278 local_relpath = arg->len ? apr_pstrmemdup(scratch_pool, arg->data, arg->len)
1283 SVN_ERR(svn_wc__db_from_relpath(&old_abspath, db, wri_abspath,
1285 scratch_pool, scratch_pool));
1289 local_relpath = arg->len ? apr_pstrmemdup(scratch_pool, arg->data, arg->len)
1293 SVN_ERR(svn_wc__db_from_relpath(&new_abspath, db, wri_abspath,
1295 scratch_pool, scratch_pool));
1299 local_relpath = arg->len ? apr_pstrmemdup(scratch_pool, arg->data, arg->len)
1304 SVN_ERR(svn_wc__db_from_relpath(&wrk_abspath, db, wri_abspath,
1306 scratch_pool, scratch_pool));
1309 /* Upgrade scenario: We have a workqueue item that describes how to install a
1310 non skel conflict. Fetch all the information we can to create a new style
1312 /* ### Before format 30 this is/was a common code path as we didn't install
1313 ### the conflict directly in the db. It just calls the wc_db code
1314 ### to set the right fields. */
1317 /* Check if we should combine with a property conflict... */
1318 svn_skel_t *conflicts;
1320 SVN_ERR(svn_wc__db_read_conflict(&conflicts, db, local_abspath,
1321 scratch_pool, scratch_pool));
1325 /* No conflict exists, create a basic skel */
1326 conflicts = svn_wc__conflict_skel_create(scratch_pool);
1328 SVN_ERR(svn_wc__conflict_skel_set_op_update(conflicts, NULL, NULL,
1333 /* Add the text conflict to the existing onflict */
1334 SVN_ERR(svn_wc__conflict_skel_add_text_conflict(conflicts, db,
1342 SVN_ERR(svn_wc__db_op_mark_conflict(db, local_abspath, conflicts,
1343 NULL, scratch_pool));
1345 return SVN_NO_ERROR;
1348 /* ------------------------------------------------------------------------ */
1350 /* OP_TMP_SET_PROPERTY_CONFLICT_MARKER */
1352 static svn_error_t *
1353 run_set_property_conflict_marker(work_item_baton_t *wqb,
1355 const svn_skel_t *work_item,
1356 const char *wri_abspath,
1357 svn_cancel_func_t cancel_func,
1359 apr_pool_t *scratch_pool)
1361 const svn_skel_t *arg = work_item->children->next;
1362 const char *local_relpath;
1363 const char *local_abspath;
1364 const char *prej_abspath = NULL;
1366 local_relpath = apr_pstrmemdup(scratch_pool, arg->data, arg->len);
1368 SVN_ERR(svn_wc__db_from_relpath(&local_abspath, db, wri_abspath,
1369 local_relpath, scratch_pool, scratch_pool));
1373 local_relpath = arg->len ? apr_pstrmemdup(scratch_pool, arg->data, arg->len)
1377 SVN_ERR(svn_wc__db_from_relpath(&prej_abspath, db, wri_abspath,
1379 scratch_pool, scratch_pool));
1382 /* Check if we should combine with a text conflict... */
1383 svn_skel_t *conflicts;
1384 apr_hash_t *prop_names;
1386 SVN_ERR(svn_wc__db_read_conflict(&conflicts, db, local_abspath,
1387 scratch_pool, scratch_pool));
1391 /* No conflict exists, create a basic skel */
1392 conflicts = svn_wc__conflict_skel_create(scratch_pool);
1394 SVN_ERR(svn_wc__conflict_skel_set_op_update(conflicts, NULL, NULL,
1399 prop_names = apr_hash_make(scratch_pool);
1400 SVN_ERR(svn_wc__conflict_skel_add_prop_conflict(conflicts, db,
1408 SVN_ERR(svn_wc__db_op_mark_conflict(db, local_abspath, conflicts,
1409 NULL, scratch_pool));
1411 return SVN_NO_ERROR;
1414 /* ------------------------------------------------------------------------ */
1416 static const struct work_item_dispatch dispatch_table[] = {
1417 { OP_FILE_COMMIT, run_file_commit },
1418 { OP_FILE_INSTALL, run_file_install },
1419 { OP_FILE_REMOVE, run_file_remove },
1420 { OP_FILE_MOVE, run_file_move },
1421 { OP_FILE_COPY_TRANSLATED, run_file_copy_translated },
1422 { OP_SYNC_FILE_FLAGS, run_sync_file_flags },
1423 { OP_PREJ_INSTALL, run_prej_install },
1424 { OP_DIRECTORY_REMOVE, run_dir_remove },
1425 { OP_DIRECTORY_INSTALL, run_dir_install },
1428 { OP_POSTUPGRADE, run_postupgrade },
1430 /* Legacy workqueue items. No longer created */
1431 { OP_BASE_REMOVE, run_base_remove },
1432 { OP_RECORD_FILEINFO, run_record_fileinfo },
1433 { OP_TMP_SET_TEXT_CONFLICT_MARKERS, run_set_text_conflict_markers },
1434 { OP_TMP_SET_PROPERTY_CONFLICT_MARKER, run_set_property_conflict_marker },
1440 struct work_item_baton_t
1442 apr_pool_t *result_pool; /* Pool to allocate result in */
1444 svn_boolean_t used; /* needs reset */
1446 apr_hash_t *record_map; /* const char * -> svn_io_dirent2_t map */
1450 static svn_error_t *
1451 dispatch_work_item(work_item_baton_t *wqb,
1453 const char *wri_abspath,
1454 const svn_skel_t *work_item,
1455 svn_cancel_func_t cancel_func,
1457 apr_pool_t *scratch_pool)
1459 const struct work_item_dispatch *scan;
1461 /* Scan the dispatch table for a function to handle this work item. */
1462 for (scan = &dispatch_table[0]; scan->name != NULL; ++scan)
1464 if (svn_skel__matches_atom(work_item->children, scan->name))
1467 #ifdef SVN_DEBUG_WORK_QUEUE
1468 SVN_DBG(("dispatch: operation='%s'\n", scan->name));
1470 SVN_ERR((*scan->func)(wqb, db, work_item, wri_abspath,
1471 cancel_func, cancel_baton,
1474 #ifdef SVN_RUN_WORK_QUEUE_TWICE
1475 #ifdef SVN_DEBUG_WORK_QUEUE
1476 SVN_DBG(("dispatch: operation='%s'\n", scan->name));
1478 /* Being able to run every workqueue item twice is one
1479 requirement for workqueues to be restartable. */
1480 SVN_ERR((*scan->func)(db, work_item, wri_abspath,
1481 cancel_func, cancel_baton,
1489 if (scan->name == NULL)
1491 /* We should know about ALL possible work items here. If we do not,
1492 then something is wrong. Most likely, some kind of format/code
1493 skew. There is nothing more we can do. Erasing or ignoring this
1494 work item could leave the WC in an even more broken state.
1496 Contrary to issue #1581, we cannot simply remove work items and
1497 continue, so bail out with an error. */
1498 return svn_error_createf(SVN_ERR_WC_BAD_ADM_LOG, NULL,
1499 _("Unrecognized work item in the queue"));
1502 return SVN_NO_ERROR;
1507 svn_wc__wq_run(svn_wc__db_t *db,
1508 const char *wri_abspath,
1509 svn_cancel_func_t cancel_func,
1511 apr_pool_t *scratch_pool)
1513 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1514 apr_uint64_t last_id = 0;
1515 work_item_baton_t wib = { 0 };
1516 wib.result_pool = svn_pool_create(scratch_pool);
1518 #ifdef SVN_DEBUG_WORK_QUEUE
1519 SVN_DBG(("wq_run: wri='%s'\n", wri_abspath));
1521 static int count = 0;
1522 const char *count_env_var = getenv("SVN_DEBUG_WORK_QUEUE");
1524 if (count_env_var && ++count == atoi(count_env_var))
1525 return svn_error_create(SVN_ERR_CANCELLED, NULL, "fake cancel");
1532 svn_skel_t *work_item;
1535 svn_pool_clear(iterpool);
1539 /* Make sure to do this *early* in the loop iteration. There may
1540 be a LAST_ID that needs to be marked as completed, *before* we
1541 start worrying about anything else. */
1542 SVN_ERR(svn_wc__db_wq_fetch_next(&id, &work_item, db, wri_abspath,
1543 last_id, iterpool, iterpool));
1547 /* Make sure to do this *early* in the loop iteration. There may
1548 be a LAST_ID that needs to be marked as completed, *before* we
1549 start worrying about anything else. */
1550 SVN_ERR(svn_wc__db_wq_record_and_fetch_next(&id, &work_item,
1552 last_id, wib.record_map,
1556 svn_pool_clear(wib.result_pool);
1557 wib.record_map = NULL;
1561 /* Stop work queue processing, if requested. A future 'svn cleanup'
1562 should be able to continue the processing. Note that we may
1563 have WORK_ITEM, but we'll just skip its processing for now. */
1565 SVN_ERR(cancel_func(cancel_baton));
1567 /* If we have a WORK_ITEM, then process the sucker. Otherwise,
1569 if (work_item == NULL)
1572 err = dispatch_work_item(&wib, db, wri_abspath, work_item,
1573 cancel_func, cancel_baton, iterpool);
1576 const char *skel = svn_skel__unparse(work_item, scratch_pool)->data;
1578 return svn_error_createf(SVN_ERR_WC_BAD_ADM_LOG, err,
1579 _("Failed to run the WC DB work queue "
1580 "associated with '%s', work item %d %s"),
1581 svn_dirent_local_style(wri_abspath,
1586 /* The work item finished without error. Mark it completed
1587 in the next loop. */
1591 svn_pool_destroy(iterpool);
1592 return SVN_NO_ERROR;
1597 svn_wc__wq_merge(svn_skel_t *work_item1,
1598 svn_skel_t *work_item2,
1599 apr_pool_t *result_pool)
1601 /* If either argument is NULL, then just return the other. */
1602 if (work_item1 == NULL)
1604 if (work_item2 == NULL)
1607 /* We have two items. Figure out how to join them. */
1608 if (SVN_WC__SINGLE_WORK_ITEM(work_item1))
1610 if (SVN_WC__SINGLE_WORK_ITEM(work_item2))
1612 /* Both are singular work items. Construct a list, then put
1613 both work items into it (in the proper order). */
1615 svn_skel_t *result = svn_skel__make_empty_list(result_pool);
1617 svn_skel__prepend(work_item2, result);
1618 svn_skel__prepend(work_item1, result);
1622 /* WORK_ITEM2 is a list of work items. We can simply shove WORK_ITEM1
1623 in the front to keep the ordering. */
1624 svn_skel__prepend(work_item1, work_item2);
1627 /* WORK_ITEM1 is a list of work items. */
1629 if (SVN_WC__SINGLE_WORK_ITEM(work_item2))
1631 /* Put WORK_ITEM2 onto the end of the WORK_ITEM1 list. */
1632 svn_skel__append(work_item1, work_item2);
1636 /* We have two lists of work items. We need to chain all of the work
1637 items into one big list. We will leave behind the WORK_ITEM2 skel,
1638 as we only want its children. */
1639 svn_skel__append(work_item1, work_item2->children);
1644 static svn_error_t *
1645 get_and_record_fileinfo(work_item_baton_t *wqb,
1646 const char *local_abspath,
1647 svn_boolean_t ignore_enoent,
1648 apr_pool_t *scratch_pool)
1650 const svn_io_dirent2_t *dirent;
1652 SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath, FALSE, ignore_enoent,
1653 wqb->result_pool, scratch_pool));
1655 if (dirent->kind != svn_node_file)
1656 return SVN_NO_ERROR;
1660 if (! wqb->record_map)
1661 wqb->record_map = apr_hash_make(wqb->result_pool);
1663 svn_hash_sets(wqb->record_map, apr_pstrdup(wqb->result_pool, local_abspath),
1666 return SVN_NO_ERROR;