2
* log.c: handle the adm area's log file.
4
* ====================================================================
5
* Copyright (c) 2000-2004 CollabNet. All rights reserved.
7
* This software is licensed as described in the file COPYING, which
8
* you should have received as part of this distribution. The terms
9
* are also available at http://subversion.tigris.org/license-1.html.
10
* If newer versions of this license are posted there, you may use a
11
* newer version instead, at your option.
13
* This software consists of voluntary contributions made by many
14
* individuals. For exact contribution history, see the revision
15
* history and logs, available at http://subversion.tigris.org/.
16
* ====================================================================
23
#include <apr_pools.h>
24
#include <apr_strings.h>
27
#include "svn_error.h"
28
#include "svn_string.h"
30
#include "svn_pools.h"
38
#include "adm_files.h"
40
#include "translate.h"
41
#include "questions.h"
43
#include "svn_private_config.h"
46
/*** Userdata for the callbacks. ***/
50
svn_xml_parser_t *parser;
51
svn_boolean_t entries_modified;
52
svn_wc_adm_access_t *adm_access; /* the dir in which all this happens */
53
const char *diff3_cmd; /* external diff3 cmd, or null if none */
55
/* Which top-level log element we're on for this logfile. Some
56
callers care whether a failure happened on the first element or
57
on some later element (e.g., 'svn cleanup').
59
This is initialized to 0 when the log_runner is created, and
60
incremented every time start_handler() is called. */
66
/*** The XML handlers. ***/
68
/* Used by file_xfer_under_path(). */
69
enum svn_wc__xfer_action {
73
svn_wc__xfer_cp_and_translate,
74
svn_wc__xfer_cp_and_detranslate
78
/* Perform some sort of copy-related ACTION on NAME and DEST:
80
svn_wc__xfer_cp: just do a copy of NAME to DEST.
81
svn_wc__xfer_mv: do a copy, then remove NAME.
82
svn_wc__xfer_append: append contents of NAME to DEST
83
svn_wc__xfer_cp_and_translate: copy NAME to DEST, doing any eol
84
and keyword expansion according to
85
the current property vals of DEST.
86
svn_wc__xfer_cp_and_detranslate: copy NAME to DEST, converting to LF
87
and contracting keywords according to
88
the current property vals of NAME.
91
file_xfer_under_path (svn_wc_adm_access_t *adm_access,
94
enum svn_wc__xfer_action action,
98
const char *full_from_path, *full_dest_path;
100
full_from_path = svn_path_join (svn_wc_adm_access_path (adm_access), name,
102
full_dest_path = svn_path_join (svn_wc_adm_access_path (adm_access), dest,
107
case svn_wc__xfer_append:
108
return svn_io_append_file (full_from_path, full_dest_path, pool);
110
case svn_wc__xfer_cp:
111
return svn_io_copy_file (full_from_path, full_dest_path, FALSE, pool);
113
case svn_wc__xfer_cp_and_translate:
115
svn_subst_keywords_t *keywords;
117
svn_boolean_t special;
119
/* Note that this action takes properties from dest, not source. */
120
SVN_ERR (svn_wc__get_keywords (&keywords, full_dest_path, adm_access,
122
SVN_ERR (svn_wc__get_eol_style (NULL, &eol_str, full_dest_path,
124
SVN_ERR (svn_wc__get_special (&special, full_dest_path, adm_access,
127
SVN_ERR (svn_subst_copy_and_translate2 (full_from_path,
136
SVN_ERR (svn_wc__maybe_set_read_only (NULL, full_dest_path,
139
/* After copying, set the file executable if props dictate. */
140
return svn_wc__maybe_set_executable (NULL, full_dest_path, adm_access,
144
case svn_wc__xfer_cp_and_detranslate:
146
svn_subst_keywords_t *keywords;
148
svn_boolean_t special;
150
/* Note that this action takes properties from source, not dest. */
151
SVN_ERR (svn_wc__get_keywords (&keywords, full_from_path, adm_access,
153
SVN_ERR (svn_wc__get_eol_style (NULL, &eol_str, full_from_path,
155
SVN_ERR (svn_wc__get_special (&special, full_from_path, adm_access,
158
/* If any specific eol style was indicated, then detranslate
159
back to repository normal form ("\n"), repairingly. But if
160
no style indicated, don't touch line endings at all. */
161
return svn_subst_copy_and_translate2 (full_from_path,
163
(eol_str ? "\n" : NULL),
164
(eol_str ? TRUE : FALSE),
166
FALSE, /* contract keywords */
171
case svn_wc__xfer_mv:
172
SVN_ERR (svn_wc__prep_file_for_replacement (full_dest_path, TRUE, pool));
174
err = svn_io_file_rename (full_from_path,
175
full_dest_path, pool);
177
/* If we got an ENOENT, that's ok; the move has probably
178
already completed in an earlier run of this log. */
181
if (! APR_STATUS_IS_ENOENT(err->apr_err))
182
return svn_error_quick_wrap (err, _("Can't move source to dest"));
183
svn_error_clear (err);
191
/* If new text was committed, then replace the text base for
192
* newly-committed file NAME in directory PATH with the new
193
* post-commit text base, which is waiting in the adm tmp area in
196
* If eol and/or keyword translation would cause the working file to
197
* change, then overwrite the working file with a translated copy of
198
* the new text base (but only if the translated copy differs from the
199
* current working file -- if they are the same, do nothing, to avoid
200
* clobbering timestamps unnecessarily).
202
* If the executable property is set, the set working file's
205
* If the working file was re-translated or had executability set,
206
* then set OVERWROTE_WORKING to TRUE. If the working file isn't
207
* touched at all, then set to FALSE.
209
* Use POOL for any temporary allocation.
212
install_committed_file (svn_boolean_t *overwrote_working,
213
svn_wc_adm_access_t *adm_access,
217
const char *filepath;
218
const char *tmp_text_base;
219
svn_node_kind_t kind;
220
svn_subst_keywords_t *keywords;
222
svn_boolean_t same, did_set;
223
const char *tmp_wfile, *pdir, *bname;
225
svn_boolean_t special;
227
/* start off assuming that the working file isn't touched. */
228
*overwrote_working = FALSE;
230
filepath = svn_path_join (svn_wc_adm_access_path (adm_access), name, pool);
232
/* In the commit, newlines and keywords may have been
233
* canonicalized and/or contracted... Or they may not have
234
* been. It's kind of hard to know. Here's how we find out:
236
* 1. Make a translated tmp copy of the committed text base.
237
* Or, if no committed text base exists (the commit must have
238
* been a propchange only), make a translated tmp copy of the
240
* 2. Compare the translated tmpfile to the working file.
241
* 3. If different, copy the tmpfile over working file.
243
* This means we only rewrite the working file if we absolutely
244
* have to, which is good because it avoids changing the file's
245
* timestamp unless necessary, so editors aren't tempted to
246
* reread the file if they don't really need to.
249
/* start off getting the latest translation prop values. */
250
SVN_ERR (svn_wc__get_eol_style (NULL, &eol_str, filepath, adm_access, pool));
251
SVN_ERR (svn_wc__get_keywords (&keywords, filepath, adm_access, NULL, pool));
252
SVN_ERR (svn_wc__get_special (&special, filepath, adm_access, pool));
254
svn_path_split (filepath, &pdir, &bname, pool);
255
tmp_wfile = svn_wc__adm_path (pdir, TRUE, pool, bname, NULL);
257
SVN_ERR (svn_io_open_unique_file (&ignored, &tmp_wfile,
258
tmp_wfile, SVN_WC__TMP_EXT,
260
SVN_ERR (svn_io_file_close (ignored, pool));
262
/* Is there a tmp_text_base that needs to be installed? */
263
tmp_text_base = svn_wc__text_base_path (filepath, 1, pool);
264
SVN_ERR (svn_io_check_path (tmp_text_base, &kind, pool));
266
if (kind == svn_node_file)
267
SVN_ERR (svn_subst_copy_and_translate2 (tmp_text_base,
270
FALSE, /* don't repair eol */
272
TRUE, /* expand keywords */
276
SVN_ERR (svn_subst_copy_and_translate2 (filepath,
279
FALSE, /* don't repair eol */
281
TRUE, /* expand keywords */
287
SVN_ERR (svn_io_files_contents_same_p (&same, tmp_wfile, filepath, pool));
296
SVN_ERR (svn_io_copy_file (tmp_wfile, filepath, FALSE, pool));
297
*overwrote_working = TRUE;
300
SVN_ERR (svn_io_remove_file (tmp_wfile, pool));
302
SVN_ERR (svn_wc__maybe_set_read_only (&did_set, filepath, adm_access, pool));
304
/* the file may have been overwritten or its timestamp changed by
305
setting it read-only */
306
*overwrote_working = TRUE;
308
/* Set the working file's execute bit if props dictate. */
309
SVN_ERR (svn_wc__maybe_set_executable (&did_set, filepath, adm_access, pool));
311
/* okay, so we didn't -overwrite- the working file, but we changed
312
its timestamp, which is the point of returning this flag. :-) */
313
*overwrote_working = TRUE;
315
/* Install the new text base if one is waiting. */
316
if (kind == svn_node_file) /* tmp_text_base exists */
317
SVN_ERR (svn_wc__sync_text_base (filepath, pool));
323
/* Sometimes, documentation would only confuse matters. */
325
pick_error_code (struct log_runner *loggy)
327
if (loggy->count <= 1)
328
return SVN_ERR_WC_BAD_ADM_LOG_START;
330
return SVN_ERR_WC_BAD_ADM_LOG;
334
signal_error (struct log_runner *loggy, svn_error_t *err)
336
svn_xml_signal_bailout
337
(svn_error_createf (pick_error_code (loggy), err,
338
_("In directory '%s'"),
339
svn_path_local_style (svn_wc_adm_access_path
349
/*** Dispatch on the xml opening tag. ***/
352
log_do_merge (struct log_runner *loggy,
356
const char *left, *right;
357
const char *left_label, *right_label, *target_label;
358
enum svn_wc_merge_outcome_t merge_outcome;
360
/* NAME is the basename of our merge_target. Pull out LEFT and RIGHT. */
361
left = svn_xml_get_attr_value (SVN_WC__LOG_ATTR_ARG_1, atts);
363
return svn_error_createf (pick_error_code (loggy), NULL,
364
_("Missing 'left' attribute in '%s'"),
366
(svn_wc_adm_access_path (loggy->adm_access),
368
right = svn_xml_get_attr_value (SVN_WC__LOG_ATTR_ARG_2, atts);
370
return svn_error_createf (pick_error_code (loggy), NULL,
371
_("Missing 'right' attribute in '%s'"),
373
(svn_wc_adm_access_path (loggy->adm_access),
376
/* Grab all three labels too. If non-existent, we'll end up passing
377
NULLs to svn_wc_merge, which is fine -- it will use default
379
left_label = svn_xml_get_attr_value (SVN_WC__LOG_ATTR_ARG_3, atts);
380
right_label = svn_xml_get_attr_value (SVN_WC__LOG_ATTR_ARG_4, atts);
381
target_label = svn_xml_get_attr_value (SVN_WC__LOG_ATTR_ARG_5, atts);
383
/* Convert the 3 basenames into full paths. */
384
left = svn_path_join (svn_wc_adm_access_path (loggy->adm_access), left,
386
right = svn_path_join (svn_wc_adm_access_path (loggy->adm_access), right,
388
name = svn_path_join (svn_wc_adm_access_path (loggy->adm_access), name,
391
/* Now do the merge with our full paths. */
392
SVN_ERR (svn_wc_merge (left, right, name, loggy->adm_access,
393
left_label, right_label, target_label,
394
FALSE, &merge_outcome, loggy->diff3_cmd,
402
log_do_file_xfer (struct log_runner *loggy,
404
enum svn_wc__xfer_action action,
408
const char *dest = NULL;
410
/* We have the name (src), and the destination is absolutely required. */
411
dest = svn_xml_get_attr_value (SVN_WC__LOG_ATTR_DEST, atts);
413
return svn_error_createf (pick_error_code (loggy), NULL,
414
_("Missing 'dest' attribute in '%s'"),
416
(svn_wc_adm_access_path (loggy->adm_access),
419
err = file_xfer_under_path (loggy->adm_access, name, dest, action,
422
signal_error (loggy, err);
427
/* Make file NAME in log's CWD readonly */
429
log_do_file_readonly (struct log_runner *loggy,
432
const char *full_path
433
= svn_path_join (svn_wc_adm_access_path (loggy->adm_access), name,
436
SVN_ERR (svn_io_set_file_read_only (full_path, FALSE, loggy->pool));
441
/* Maybe make file NAME in log's CWD readonly */
443
log_do_file_maybe_readonly (struct log_runner *loggy,
446
const char *full_path
447
= svn_path_join (svn_wc_adm_access_path (loggy->adm_access), name,
450
SVN_ERR (svn_wc__maybe_set_read_only (NULL, full_path, loggy->adm_access,
456
/* Set file NAME in log's CWD to timestamp value in ATTS. */
458
log_do_file_timestamp (struct log_runner *loggy,
462
apr_time_t timestamp;
463
svn_node_kind_t kind;
464
const char *full_path
465
= svn_path_join (svn_wc_adm_access_path (loggy->adm_access), name,
468
const char *timestamp_string
469
= svn_xml_get_attr_value (SVN_WC__LOG_ATTR_TIMESTAMP, atts);
470
svn_boolean_t is_special;
472
if (! timestamp_string)
473
return svn_error_createf (pick_error_code (loggy), NULL,
474
_("Missing 'timestamp' attribute in '%s'"),
476
(svn_wc_adm_access_path (loggy->adm_access),
479
/* Do not set the timestamp on special files. */
480
SVN_ERR (svn_io_check_special_path (full_path, &kind, &is_special,
485
SVN_ERR (svn_time_from_cstring (×tamp, timestamp_string,
488
SVN_ERR (svn_io_set_file_affected_time (timestamp, full_path,
496
/* Remove file NAME in log's CWD. */
498
log_do_rm (struct log_runner *loggy, const char *name)
500
const char *full_path
501
= svn_path_join (svn_wc_adm_access_path (loggy->adm_access),
504
SVN_ERR (svn_io_remove_file (full_path, loggy->pool));
513
log_do_modify_entry (struct log_runner *loggy,
518
apr_hash_t *ah = svn_xml_make_att_hash (atts, loggy->pool);
520
svn_wc_entry_t *entry;
521
apr_uint32_t modify_flags;
522
const char *valuestr;
524
/* Convert the attributes into an entry structure. */
525
SVN_ERR (svn_wc__atts_to_entry (&entry, &modify_flags, ah, loggy->pool));
527
/* Make TFILE the path of the thing being modified. */
528
tfile = svn_path_join (svn_wc_adm_access_path (loggy->adm_access),
529
strcmp (name, SVN_WC_ENTRY_THIS_DIR) ? name : "",
532
/* Did the log command give us any timestamps? There are three
533
possible scenarios here. We must check both text_time
534
and prop_time for each of the three scenarios. */
537
valuestr = apr_hash_get (ah, SVN_WC__ENTRY_ATTR_TEXT_TIME,
538
APR_HASH_KEY_STRING);
540
if ((modify_flags & SVN_WC__ENTRY_MODIFY_TEXT_TIME)
541
&& (! strcmp (valuestr, SVN_WC_TIMESTAMP_WC)))
543
svn_node_kind_t tfile_kind;
544
apr_time_t text_time;
546
err = svn_io_check_path (tfile, &tfile_kind, loggy->pool);
548
return svn_error_createf
549
(pick_error_code (loggy), err,
550
_("Error checking path '%s'"), svn_path_local_style (tfile,
553
err = svn_io_file_affected_time (&text_time, tfile, loggy->pool);
555
return svn_error_createf
556
(pick_error_code (loggy), err,
557
_("Error getting 'affected time' on '%s'"),
558
svn_path_local_style (tfile, loggy->pool));
560
entry->text_time = text_time;
564
valuestr = apr_hash_get (ah, SVN_WC__ENTRY_ATTR_PROP_TIME,
565
APR_HASH_KEY_STRING);
567
if ((modify_flags & SVN_WC__ENTRY_MODIFY_PROP_TIME)
568
&& (! strcmp (valuestr, SVN_WC_TIMESTAMP_WC)))
571
svn_node_kind_t pfile_kind;
572
apr_time_t prop_time;
574
err = svn_wc__prop_path (&pfile, tfile, loggy->adm_access, FALSE,
577
signal_error (loggy, err);
579
err = svn_io_check_path (pfile, &pfile_kind, loggy->pool);
581
return svn_error_createf
582
(pick_error_code (loggy), err,
583
_("Error checking path '%s'"),
584
svn_path_local_style (pfile, loggy->pool));
586
err = svn_io_file_affected_time (&prop_time, pfile, loggy->pool);
588
return svn_error_createf
589
(pick_error_code (loggy), NULL,
590
_("Error getting 'affected time' on '%s'"),
591
svn_path_local_style (pfile, loggy->pool));
593
entry->prop_time = prop_time;
596
/* Now write the new entry out */
597
err = svn_wc__entry_modify (loggy->adm_access, name,
598
entry, modify_flags, FALSE, loggy->pool);
600
return svn_error_createf (pick_error_code (loggy), err,
601
_("Error modifying entry for '%s'"), name);
602
loggy->entries_modified = TRUE;
608
log_do_delete_lock (struct log_runner *loggy,
612
svn_wc_entry_t entry;
614
entry.lock_token = entry.lock_comment = entry.lock_owner = NULL;
615
entry.lock_creation_date = 0;
617
/* Now write the new entry out */
618
err = svn_wc__entry_modify (loggy->adm_access, name,
620
SVN_WC__ENTRY_MODIFY_LOCK_TOKEN
621
| SVN_WC__ENTRY_MODIFY_LOCK_OWNER
622
| SVN_WC__ENTRY_MODIFY_LOCK_COMMENT
623
| SVN_WC__ENTRY_MODIFY_LOCK_CREATION_DATE,
626
return svn_error_createf (pick_error_code (loggy), err,
627
_("Error removing lock from entry for '%s'"),
629
loggy->entries_modified = TRUE;
634
/* Ben sez: this log command is (at the moment) only executed by the
635
update editor. It attempts to forcefully remove working data. */
637
log_do_delete_entry (struct log_runner *loggy, const char *name)
639
svn_wc_adm_access_t *adm_access;
640
const svn_wc_entry_t *entry;
641
svn_error_t *err = SVN_NO_ERROR;
642
const char *full_path
643
= svn_path_join (svn_wc_adm_access_path (loggy->adm_access), name,
646
/* Figure out if 'name' is a dir or a file */
647
SVN_ERR (svn_wc_adm_probe_retrieve (&adm_access, loggy->adm_access, full_path,
649
SVN_ERR (svn_wc_entry (&entry, full_path, adm_access, FALSE, loggy->pool));
652
/* Hmm....this entry is already absent from the revision control
653
system. Chances are good that this item was removed via a
654
commit from this working copy. */
657
/* Remove the object from revision control -- whether it's a
658
single file or recursive directory removal. Attempt
659
attempt to destroy all working files & dirs too.
661
### We pass NULL, NULL for cancel_func and cancel_baton below.
662
### If they were available, it would be nice to use them. */
663
if (entry->kind == svn_node_dir)
665
svn_wc_adm_access_t *ignored;
667
/* If we get the right kind of error, it means the directory is
668
already missing, so all we need to do is delete its entry in
669
the parent directory. */
670
err = svn_wc_adm_retrieve (&ignored, adm_access, full_path, loggy->pool);
673
if (err->apr_err == SVN_ERR_WC_NOT_LOCKED)
677
svn_error_clear (err);
680
if (entry->schedule != svn_wc_schedule_add)
682
SVN_ERR (svn_wc_entries_read (&entries, loggy->adm_access,
684
svn_wc__entry_remove (entries, name);
685
SVN_ERR (svn_wc__entries_write (entries, loggy->adm_access,
696
err = svn_wc_remove_from_revision_control (adm_access,
697
SVN_WC_ENTRY_THIS_DIR,
699
FALSE, /* instant_error */
704
else if (entry->kind == svn_node_file)
706
err = svn_wc_remove_from_revision_control (loggy->adm_access, name,
708
FALSE, /* instant_error */
713
if ((err) && (err->apr_err == SVN_ERR_WC_LEFT_LOCAL_MOD))
715
svn_error_clear (err);
722
/* Note: assuming that svn_wc__log_commit() is what created all of
723
the <committed...> commands, the `name' attribute will either be a
724
file or SVN_WC_ENTRY_THIS_DIR. */
726
log_do_committed (struct log_runner *loggy,
731
apr_pool_t *pool = loggy->pool;
732
int is_this_dir = (strcmp (name, SVN_WC_ENTRY_THIS_DIR) == 0);
733
const char *rev = svn_xml_get_attr_value (SVN_WC__LOG_ATTR_REVISION, atts);
734
svn_boolean_t wc_root, overwrote_working = FALSE, remove_executable = FALSE;
735
svn_boolean_t set_read_write = FALSE;
736
const char *full_path;
737
const char *pdir, *base_name;
739
const svn_wc_entry_t *orig_entry;
740
svn_wc_entry_t *entry;
741
apr_time_t text_time = 0; /* By default, don't override old stamp. */
742
apr_time_t prop_time = 0; /* By default, don't override old stamp. */
743
svn_node_kind_t kind;
744
svn_wc_adm_access_t *adm_access;
746
/* Determine the actual full path of the affected item. */
748
full_path = svn_path_join (svn_wc_adm_access_path (loggy->adm_access),
751
full_path = apr_pstrdup (pool, svn_wc_adm_access_path (loggy->adm_access));
753
/*** Perform sanity checking operations ***/
755
/* If no new post-commit revision was given us, bail with an error. */
757
return svn_error_createf (pick_error_code (loggy), NULL,
758
_("Missing 'revision' attribute for '%s'"),
761
/* Read the entry for the affected item. If we can't find the
762
entry, or if the entry states that our item is not either "this
763
dir" or a file kind, perhaps this isn't really the entry our log
764
creator was expecting. */
765
SVN_ERR (svn_wc_adm_probe_retrieve (&adm_access, loggy->adm_access, full_path,
767
SVN_ERR (svn_wc_entry (&orig_entry, full_path, adm_access, TRUE, pool));
769
|| ((! is_this_dir) && (orig_entry->kind != svn_node_file)))
770
return svn_error_createf
771
(pick_error_code (loggy), NULL,
772
_("Log command for directory '%s' is mislocated"), name);
774
entry = svn_wc_entry_dup (orig_entry, pool);
776
/*** Handle the committed deletion case ***/
778
/* If the committed item was scheduled for deletion, it needs to
779
now be removed from revision control. Once that is accomplished,
780
we are finished handling this item. */
781
if (entry->schedule == svn_wc_schedule_delete)
783
svn_revnum_t new_rev = SVN_STR_TO_REV(rev);
785
/* If we are suppose to delete "this dir", drop a 'killme' file
786
into my own administrative dir as a signal for svn_wc__run_log()
787
to blow away the administrative area after it is finished
788
processing this logfile. */
791
/* Bump the revision number of this_dir anyway, so that it
792
might be higher than its parent's revnum. If it's
793
higher, then the process that sees KILLME and destroys
794
the directory can also place a 'deleted' dir entry in the
796
svn_wc_entry_t tmpentry;
797
tmpentry.revision = new_rev;
798
tmpentry.kind = svn_node_dir;
800
SVN_ERR (svn_wc__entry_modify
801
(loggy->adm_access, NULL, &tmpentry,
802
SVN_WC__ENTRY_MODIFY_REVISION | SVN_WC__ENTRY_MODIFY_KIND,
804
loggy->entries_modified = TRUE;
806
/* Drop the 'killme' file. */
807
return svn_wc__make_adm_thing (loggy->adm_access, SVN_WC__ADM_KILLME,
808
svn_node_file, APR_OS_DEFAULT,
813
/* Else, we're deleting a file, and we can safely remove files
814
from revision control without screwing something else up.
816
### We pass NULL, NULL for cancel_func and cancel_baton below.
817
### If they were available, it would be nice to use them. */
820
const svn_wc_entry_t *parentry;
821
svn_wc_entry_t tmp_entry;
823
SVN_ERR (svn_wc_remove_from_revision_control (loggy->adm_access,
828
/* If the parent entry's working rev 'lags' behind new_rev... */
829
SVN_ERR (svn_wc_entry (&parentry,
830
svn_wc_adm_access_path (loggy->adm_access),
833
if (new_rev > parentry->revision)
835
/* ...then the parent's revision is now officially a
836
lie; therefore, it must remember the file as being
837
'deleted' for a while. Create a new, uninteresting
839
tmp_entry.kind = svn_node_file;
840
tmp_entry.deleted = TRUE;
841
tmp_entry.revision = new_rev;
842
SVN_ERR (svn_wc__entry_modify
843
(loggy->adm_access, name, &tmp_entry,
844
SVN_WC__ENTRY_MODIFY_REVISION
845
| SVN_WC__ENTRY_MODIFY_KIND
846
| SVN_WC__ENTRY_MODIFY_DELETED,
848
loggy->entries_modified = TRUE;
856
/*** Mark the committed item committed-to-date ***/
859
/* If "this dir" has been replaced (delete + add), all its
860
immmediate children *must* be either scheduled for deletion (they
861
were children of "this dir" during the "delete" phase of its
862
replacement), added (they are new children of the replaced dir),
863
or replaced (they are new children of the replace dir that have
864
the same names as children that were present during the "delete"
865
phase of the replacement).
867
Children which are added or replaced will have been reported as
868
individual commit targets, and thus will be re-visited by
869
log_do_committed(). Children which were marked for deletion,
870
however, need to be outright removed from revision control. */
871
if ((entry->schedule == svn_wc_schedule_replace) && is_this_dir)
873
apr_hash_index_t *hi;
875
/* Loop over all children entries, look for items scheduled for
877
SVN_ERR (svn_wc_entries_read (&entries, loggy->adm_access, TRUE, pool));
878
for (hi = apr_hash_first (pool, entries); hi; hi = apr_hash_next (hi))
883
const svn_wc_entry_t *cur_entry;
884
svn_wc_adm_access_t *entry_access;
886
/* Get the next entry */
887
apr_hash_this (hi, &key, &klen, &val);
888
cur_entry = (svn_wc_entry_t *) val;
890
/* Skip each entry that isn't scheduled for deletion. */
891
if (cur_entry->schedule != svn_wc_schedule_delete)
894
/* Determine what arguments to hand to our removal function,
895
and let BASE_NAME double as an "ok" flag to run that function. */
897
if (cur_entry->kind == svn_node_file)
899
pdir = svn_wc_adm_access_path (loggy->adm_access);
900
base_name = apr_pstrdup (pool, key);
901
entry_access = loggy->adm_access;
903
else if (cur_entry->kind == svn_node_dir)
905
pdir = svn_path_join (svn_wc_adm_access_path (loggy->adm_access),
907
base_name = SVN_WC_ENTRY_THIS_DIR;
908
SVN_ERR (svn_wc_adm_retrieve (&entry_access, loggy->adm_access,
912
/* ### We pass NULL, NULL for cancel_func and cancel_baton below.
913
### If they were available, it would be nice to use them. */
915
SVN_ERR (svn_wc_remove_from_revision_control
916
(entry_access, base_name, FALSE, FALSE,
922
/* For file commit items, we need to "install" the user's working
923
file as the new `text-base' in the administrative area. A copy
924
of this file should have been dropped into our `tmp/text-base'
925
directory during the commit process. Part of this process
926
involves setting the textual timestamp for this entry. We'd like
927
to just use the timestamp of the working file, but it is possible that
928
at some point during the commit, the real working file might have
929
changed again. If that has happened, we'll use the timestamp of
930
the copy of this file in `tmp/text-base'. */
933
const char *wf = full_path, *tmpf;
935
/* Make sure our working file copy is present in the temp area. */
936
tmpf = svn_wc__text_base_path (wf, 1, pool);
937
if ((err = svn_io_check_path (tmpf, &kind, pool)))
938
return svn_error_createf (pick_error_code (loggy), err,
939
_("Error checking existence of '%s'"), name);
940
if (kind == svn_node_file)
942
svn_boolean_t modified;
945
/* Verify that the working file is the same as the tmpf file. */
946
if ((err = svn_wc__versioned_file_modcheck (&modified, wf,
949
return svn_error_createf (pick_error_code (loggy), err,
950
_("Error comparing '%s' and '%s'"),
951
svn_path_local_style (wf, pool),
952
svn_path_local_style (tmpf, pool));
954
/* If they are the same, use the working file's timestamp,
955
else use the tmpf file's timestamp. */
956
chosen = modified ? tmpf : wf;
958
/* Get the timestamp from our chosen file. */
959
if ((err = svn_io_file_affected_time (&text_time, chosen, pool)))
960
return svn_error_createf
961
(pick_error_code (loggy), err,
962
_("Error getting 'affected time' for '%s'"),
963
svn_path_local_style (chosen, pool));
967
/* Now check for property commits. If a property commit occurred, a
968
copy of the "working" property file should have been dumped in
969
the admistrative `tmp' area. We'll let that tmpfile's existence
970
be a signal that we need to do post-commit property processing.
971
Also, we have to again decide which timestamp to use (see the
972
text-time case above). */
974
const char *wf, *tmpf, *basef;
976
/* Get property file pathnames (not from the `tmp' area) depending
977
on whether we're examining a file or THIS_DIR */
979
/* ### Logic check: if is_this_dir, then full_path is the same
980
as loggy->adm_access->path, I think. In which case we don't need the
981
inline conditionals below... */
983
SVN_ERR (svn_wc__prop_path
986
? svn_wc_adm_access_path (loggy->adm_access) : full_path,
987
loggy->adm_access, FALSE, pool));
988
SVN_ERR (svn_wc__prop_base_path
991
? svn_wc_adm_access_path (loggy->adm_access) : full_path,
992
loggy->adm_access, FALSE, pool));
994
/* If this file was replaced in the commit, then we definitely
995
need to begin by removing any old residual prop-base file. */
996
if (entry->schedule == svn_wc_schedule_replace)
998
svn_node_kind_t kinder;
999
SVN_ERR (svn_io_check_path (basef, &kinder, pool));
1000
if (kinder == svn_node_file)
1001
SVN_ERR (svn_io_remove_file (basef, pool));
1004
SVN_ERR (svn_wc__prop_path
1007
? svn_wc_adm_access_path (loggy->adm_access) : full_path,
1008
loggy->adm_access, TRUE, pool));
1009
if ((err = svn_io_check_path (tmpf, &kind, pool)))
1010
return svn_error_createf (pick_error_code (loggy), err,
1011
_("Error checking existence of '%s'"),
1012
svn_path_local_style (tmpf, pool));
1013
if (kind == svn_node_file)
1018
/* We need to decide which prop-timestamp to use, just like we
1019
did with text-time above. */
1020
if ((err = svn_io_files_contents_same_p (&same, wf, tmpf, pool)))
1021
return svn_error_createf (pick_error_code (loggy), err,
1022
_("Error comparing '%s' and '%s'"),
1023
svn_path_local_style (wf, pool),
1024
svn_path_local_style (tmpf, pool));
1026
/* If they are the same, use the working file's timestamp,
1027
else use the tmp_base file's timestamp. */
1028
chosen = same ? wf : tmpf;
1030
/* Get the timestamp of our chosen file. */
1031
if ((err = svn_io_file_affected_time (&prop_time, chosen, pool)))
1032
return svn_error_createf (pick_error_code (loggy), err,
1033
_("Error getting 'affected time' of '%s'"),
1034
svn_path_local_style (chosen, pool));
1036
/* Examine propchanges here before installing the new
1037
propbase. If the executable prop was -deleted-, then set a
1038
flag that will remind us to run -x after our call to
1039
install_committed_file(). */
1043
apr_array_header_t *propchanges;
1044
SVN_ERR (svn_wc_get_prop_diffs (&propchanges, NULL,
1045
full_path, loggy->adm_access,
1047
for (i = 0; i < propchanges->nelts; i++)
1049
svn_prop_t *propchange
1050
= &APR_ARRAY_IDX (propchanges, i, svn_prop_t);
1052
if ((! strcmp (propchange->name, SVN_PROP_EXECUTABLE))
1053
&& (propchange->value == NULL))
1055
remove_executable = TRUE;
1060
for (i = 0; i < propchanges->nelts; i++)
1062
svn_prop_t *propchange
1063
= &APR_ARRAY_IDX (propchanges, i, svn_prop_t);
1065
if ((! strcmp (propchange->name, SVN_PROP_NEEDS_LOCK))
1066
&& (propchange->value == NULL))
1068
set_read_write = TRUE;
1074
/* Make the tmp prop file the new pristine one. */
1075
SVN_ERR (svn_wc__prep_file_for_replacement (basef, TRUE, pool));
1076
SVN_ERR (svn_io_file_rename (tmpf, basef, pool));
1077
SVN_ERR (svn_io_set_file_read_only (basef, FALSE, pool));
1081
/* Timestamps have been decided on, and prop-base has been installed
1082
if necessary. Now we install the new text-base (if present), and
1083
possibly re-translate the working file. */
1086
/* Install the new file, which may involve expanding keywords. */
1087
if ((err = install_committed_file
1088
(&overwrote_working, loggy->adm_access, name, pool)))
1089
return svn_error_createf
1090
(pick_error_code (loggy), err,
1091
_("Error replacing text-base of '%s'"), name);
1093
/* The previous call will have run +x if the executable property
1094
was added or already present. But if this property was
1095
-removed-, (detected earlier), then run -x here on the new
1097
if (remove_executable)
1099
SVN_ERR (svn_io_set_file_executable (full_path,
1100
FALSE, /* chmod -x */
1102
overwrote_working = TRUE; /* entry needs wc-file's timestamp */
1107
SVN_ERR (svn_io_set_file_read_write_carefully (full_path, TRUE,
1109
overwrote_working = TRUE; /* entry needs wc-file's timestamp */
1114
/* If the working file was overwritten (due to re-translation)
1115
or touched (due to +x / -x), then use *that* textual
1116
timestamp instead. */
1117
if (overwrote_working)
1118
if ((err = svn_io_file_affected_time (&text_time, full_path, pool)))
1119
return svn_error_createf (pick_error_code (loggy), err,
1120
_("Error getting 'affected time' of '%s'"),
1121
svn_path_local_style (full_path, pool));
1124
/* Files have been moved, and timestamps have been found. It is now
1125
fime for The Big Entry Modification. */
1126
entry->revision = SVN_STR_TO_REV (rev);
1127
entry->kind = is_this_dir ? svn_node_dir : svn_node_file;
1128
entry->schedule = svn_wc_schedule_normal;
1129
entry->copied = FALSE;
1130
entry->deleted = FALSE;
1131
entry->text_time = text_time;
1132
entry->prop_time = prop_time;
1133
entry->conflict_old = NULL;
1134
entry->conflict_new = NULL;
1135
entry->conflict_wrk = NULL;
1136
entry->prejfile = NULL;
1137
entry->copyfrom_url = NULL;
1138
entry->copyfrom_rev = SVN_INVALID_REVNUM;
1139
if ((err = svn_wc__entry_modify (loggy->adm_access, name, entry,
1140
(SVN_WC__ENTRY_MODIFY_REVISION
1141
| SVN_WC__ENTRY_MODIFY_SCHEDULE
1142
| SVN_WC__ENTRY_MODIFY_COPIED
1143
| SVN_WC__ENTRY_MODIFY_DELETED
1144
| SVN_WC__ENTRY_MODIFY_COPYFROM_URL
1145
| SVN_WC__ENTRY_MODIFY_COPYFROM_REV
1146
| SVN_WC__ENTRY_MODIFY_CONFLICT_OLD
1147
| SVN_WC__ENTRY_MODIFY_CONFLICT_NEW
1148
| SVN_WC__ENTRY_MODIFY_CONFLICT_WRK
1149
| SVN_WC__ENTRY_MODIFY_PREJFILE
1151
? SVN_WC__ENTRY_MODIFY_TEXT_TIME
1154
? SVN_WC__ENTRY_MODIFY_PROP_TIME
1156
| SVN_WC__ENTRY_MODIFY_FORCE),
1158
return svn_error_createf
1159
(pick_error_code (loggy), err,
1160
_("Error modifying entry of '%s'"), name);
1161
loggy->entries_modified = TRUE;
1163
/* If we aren't looking at "this dir" (meaning we are looking at a
1164
file), we are finished. From here on out, it's all about a
1165
directory's entry in its parent. */
1167
return SVN_NO_ERROR;
1169
/* For directories, we also have to reset the state in the parent's
1170
entry for this directory, unless the current directory is a `WC
1171
root' (meaning, our parent directory on disk is not our parent in
1172
Version Control Land), in which case we're all finished here. */
1173
SVN_ERR (svn_wc_is_wc_root (&wc_root,
1174
svn_wc_adm_access_path (loggy->adm_access),
1178
return SVN_NO_ERROR;
1180
/* Make sure our entry exists in the parent. */
1182
svn_wc_adm_access_t *paccess;
1183
svn_boolean_t unassociated = FALSE;
1185
svn_path_split (svn_wc_adm_access_path (loggy->adm_access), &pdir,
1188
err = svn_wc_adm_retrieve (&paccess, loggy->adm_access, pdir, pool);
1189
if (err && (err->apr_err == SVN_ERR_WC_NOT_LOCKED))
1191
svn_error_clear (err);
1192
SVN_ERR (svn_wc_adm_open3 (&paccess, NULL, pdir, TRUE, 0,
1194
unassociated = TRUE;
1199
SVN_ERR (svn_wc_entries_read (&entries, paccess, FALSE, pool));
1200
if (apr_hash_get (entries, base_name, APR_HASH_KEY_STRING))
1202
if ((err = svn_wc__entry_modify (paccess, base_name, entry,
1203
(SVN_WC__ENTRY_MODIFY_SCHEDULE
1204
| SVN_WC__ENTRY_MODIFY_COPIED
1205
| SVN_WC__ENTRY_MODIFY_DELETED
1206
| SVN_WC__ENTRY_MODIFY_FORCE),
1208
return svn_error_createf (pick_error_code (loggy), err,
1209
_("Error modifying entry of '%s'"), name);
1213
SVN_ERR (svn_wc_adm_close (paccess));
1216
return SVN_NO_ERROR;
1220
/* See documentation for SVN_WC__LOG_MODIFY_WCPROP. */
1221
static svn_error_t *
1222
log_do_modify_wcprop (struct log_runner *loggy,
1227
const char *propname, *propval, *path;
1229
if (strcmp (name, SVN_WC_ENTRY_THIS_DIR) == 0)
1230
path = svn_wc_adm_access_path (loggy->adm_access);
1232
path = svn_path_join (svn_wc_adm_access_path (loggy->adm_access),
1235
propname = svn_xml_get_attr_value (SVN_WC__LOG_ATTR_PROPNAME, atts);
1236
propval = svn_xml_get_attr_value (SVN_WC__LOG_ATTR_PROPVAL, atts);
1240
value.data = propval;
1241
value.len = strlen (propval);
1244
return svn_wc__wcprop_set (propname, propval ? &value : NULL,
1245
path, loggy->adm_access, loggy->pool);
1250
start_handler (void *userData, const char *eltname, const char **atts)
1252
svn_error_t *err = SVN_NO_ERROR;
1253
struct log_runner *loggy = userData;
1255
/* All elements use the `name' attribute, so grab it now. */
1256
const char *name = svn_xml_get_attr_value (SVN_WC__LOG_ATTR_NAME, atts);
1258
/* Clear the per-log-item pool. */
1259
svn_pool_clear (loggy->pool);
1261
if (strcmp (eltname, "wc-log") == 0) /* ignore expat pacifier */
1266
(loggy, svn_error_createf
1267
(pick_error_code (loggy), NULL,
1268
_("Log entry missing 'name' attribute (entry '%s' "
1269
"for directory '%s')"),
1271
svn_path_local_style (svn_wc_adm_access_path (loggy->adm_access),
1276
/* Increment the top-level element count before processing any commands. */
1280
if (strcmp (eltname, SVN_WC__LOG_MODIFY_ENTRY) == 0) {
1281
err = log_do_modify_entry (loggy, name, atts);
1283
else if (strcmp (eltname, SVN_WC__LOG_DELETE_LOCK) == 0) {
1284
err = log_do_delete_lock (loggy, name);
1286
else if (strcmp (eltname, SVN_WC__LOG_DELETE_ENTRY) == 0) {
1287
err = log_do_delete_entry (loggy, name);
1289
else if (strcmp (eltname, SVN_WC__LOG_COMMITTED) == 0) {
1290
err = log_do_committed (loggy, name, atts);
1292
else if (strcmp (eltname, SVN_WC__LOG_MODIFY_WCPROP) == 0) {
1293
err = log_do_modify_wcprop (loggy, name, atts);
1295
else if (strcmp (eltname, SVN_WC__LOG_RM) == 0) {
1296
err = log_do_rm (loggy, name);
1298
else if (strcmp (eltname, SVN_WC__LOG_MERGE) == 0) {
1299
err = log_do_merge (loggy, name, atts);
1301
else if (strcmp (eltname, SVN_WC__LOG_MV) == 0) {
1302
err = log_do_file_xfer (loggy, name, svn_wc__xfer_mv, atts);
1304
else if (strcmp (eltname, SVN_WC__LOG_CP) == 0) {
1305
err = log_do_file_xfer (loggy, name, svn_wc__xfer_cp, atts);
1307
else if (strcmp (eltname, SVN_WC__LOG_CP_AND_TRANSLATE) == 0) {
1308
err = log_do_file_xfer (loggy, name,svn_wc__xfer_cp_and_translate, atts);
1310
else if (strcmp (eltname, SVN_WC__LOG_CP_AND_DETRANSLATE) == 0) {
1311
err = log_do_file_xfer (loggy, name,svn_wc__xfer_cp_and_detranslate, atts);
1313
else if (strcmp (eltname, SVN_WC__LOG_APPEND) == 0) {
1314
err = log_do_file_xfer (loggy, name, svn_wc__xfer_append, atts);
1316
else if (strcmp (eltname, SVN_WC__LOG_READONLY) == 0) {
1317
err = log_do_file_readonly (loggy, name);
1319
else if (strcmp (eltname, SVN_WC__LOG_MAYBE_READONLY) == 0) {
1320
err = log_do_file_maybe_readonly (loggy, name);
1322
else if (strcmp (eltname, SVN_WC__LOG_SET_TIMESTAMP) == 0) {
1323
err = log_do_file_timestamp (loggy, name, atts);
1328
(loggy, svn_error_createf
1329
(pick_error_code (loggy), NULL,
1330
_("Unrecognized logfile element '%s' in '%s'"),
1332
svn_path_local_style (svn_wc_adm_access_path (loggy->adm_access),
1339
(loggy, svn_error_createf
1340
(pick_error_code (loggy), err,
1341
_("Error processing command '%s' in '%s'"),
1343
svn_path_local_style (svn_wc_adm_access_path (loggy->adm_access),
1349
/* Process the "KILLME" file in ADM_ACCESS
1351
static svn_error_t *
1352
handle_killme (svn_wc_adm_access_t *adm_access,
1353
svn_cancel_func_t cancel_func,
1357
const svn_wc_entry_t *thisdir_entry, *parent_entry;
1358
svn_wc_entry_t tmp_entry;
1360
SVN_ERR (svn_wc_entry (&thisdir_entry,
1361
svn_wc_adm_access_path (adm_access), adm_access,
1364
/* Blow away the entire directory, and all those below it too. */
1365
err = svn_wc_remove_from_revision_control (adm_access,
1366
SVN_WC_ENTRY_THIS_DIR,
1368
FALSE, /* no instant err */
1369
cancel_func, cancel_baton,
1371
if (err && err->apr_err != SVN_ERR_WC_LEFT_LOCAL_MOD)
1373
svn_error_clear (err);
1375
/* If revnum of this dir is greater than parent's revnum, then
1376
recreate 'deleted' entry in parent. */
1378
const char *parent, *bname;
1379
svn_wc_adm_access_t *parent_access;
1381
svn_path_split (svn_wc_adm_access_path (adm_access), &parent, &bname, pool);
1382
SVN_ERR (svn_wc_adm_retrieve (&parent_access, adm_access, parent, pool));
1383
SVN_ERR (svn_wc_entry (&parent_entry, parent, parent_access, FALSE, pool));
1385
if (thisdir_entry->revision > parent_entry->revision)
1387
tmp_entry.kind = svn_node_dir;
1388
tmp_entry.deleted = TRUE;
1389
tmp_entry.revision = thisdir_entry->revision;
1390
SVN_ERR (svn_wc__entry_modify (parent_access, bname, &tmp_entry,
1391
SVN_WC__ENTRY_MODIFY_REVISION
1392
| SVN_WC__ENTRY_MODIFY_KIND
1393
| SVN_WC__ENTRY_MODIFY_DELETED,
1397
return SVN_NO_ERROR;
1401
/*** Using the parser to run the log file. ***/
1403
/* Determine the log file that should be used for a given number. */
1405
svn_wc__logfile_path (int log_number,
1408
return apr_psprintf (pool, SVN_WC__ADM_LOG "%s",
1409
(log_number == 0) ? ""
1410
: apr_psprintf (pool, ".%d", log_number));
1413
/* Run a sequence of log files. */
1415
svn_wc__run_log (svn_wc_adm_access_t *adm_access,
1416
const char *diff3_cmd,
1419
svn_error_t *err, *err2;
1420
svn_xml_parser_t *parser;
1421
struct log_runner *loggy = apr_pcalloc (pool, sizeof (*loggy));
1424
apr_file_t *f = NULL;
1425
const char *logfile_path;
1427
apr_pool_t *iterpool = svn_pool_create (pool);
1429
/* kff todo: use the tag-making functions here, now. */
1430
const char *log_start
1431
= "<wc-log xmlns=\"http://subversion.tigris.org/xmlns\">\n";
1435
parser = svn_xml_make_parser (loggy, start_handler, NULL, NULL, pool);
1436
loggy->adm_access = adm_access;
1437
loggy->pool = svn_pool_create (pool);
1438
loggy->parser = parser;
1439
loggy->entries_modified = FALSE;
1440
loggy->diff3_cmd = diff3_cmd;
1443
/* Expat wants everything wrapped in a top-level form, so start with
1444
a ghost open tag. */
1445
SVN_ERR (svn_xml_parse (parser, log_start, strlen (log_start), 0));
1447
for (log_number = 0; ; log_number++)
1449
svn_pool_clear (iterpool);
1450
logfile_path = svn_wc__logfile_path (log_number, iterpool);
1451
/* Parse the log file's contents. */
1452
err = svn_wc__open_adm_file (&f, svn_wc_adm_access_path (adm_access),
1453
logfile_path, APR_READ, iterpool);
1456
if (APR_STATUS_IS_ENOENT (err->apr_err))
1458
svn_error_clear (err);
1463
SVN_ERR_W (err, _("Couldn't open log"));
1468
buf_len = sizeof (buf);
1470
err = svn_io_file_read (f, buf, &buf_len, iterpool);
1471
if (err && !APR_STATUS_IS_EOF(err->apr_err))
1472
return svn_error_createf
1474
_("Error reading administrative log file in '%s'"),
1475
svn_path_local_style (svn_wc_adm_access_path (adm_access),
1478
err2 = svn_xml_parse (parser, buf, buf_len, 0);
1482
svn_error_clear (err);
1487
svn_error_clear (err);
1488
SVN_ERR (svn_io_file_close (f, iterpool));
1492
/* Pacify Expat with a pointless closing element tag. */
1493
SVN_ERR (svn_xml_parse (parser, log_end, strlen (log_end), 1));
1495
svn_xml_free_parser (parser);
1497
if (loggy->entries_modified == TRUE)
1499
apr_hash_t *entries;
1500
SVN_ERR (svn_wc_entries_read (&entries, loggy->adm_access, TRUE, pool));
1501
SVN_ERR (svn_wc__entries_write (entries, loggy->adm_access, pool));
1504
/* Check for a 'killme' file in the administrative area. */
1505
if (svn_wc__adm_path_exists (svn_wc_adm_access_path (adm_access), 0, pool,
1506
SVN_WC__ADM_KILLME, NULL))
1508
SVN_ERR (handle_killme (adm_access, NULL, NULL, pool));
1512
for (log_number--; log_number >= 0; log_number--)
1514
svn_pool_clear (iterpool);
1515
logfile_path = svn_wc__logfile_path (log_number, iterpool);
1517
/* No 'killme'? Remove the logfile; its commands have been executed. */
1518
SVN_ERR (svn_wc__remove_adm_file (svn_wc_adm_access_path (adm_access),
1519
iterpool, logfile_path, NULL));
1523
svn_pool_destroy (iterpool);
1525
return SVN_NO_ERROR;
1530
/*** Recursively do log things. ***/
1533
svn_wc_cleanup (const char *path,
1534
svn_wc_adm_access_t *optional_adm_access,
1535
const char *diff3_cmd,
1536
svn_cancel_func_t cancel_func,
1540
return svn_wc_cleanup2 (path, diff3_cmd, cancel_func, cancel_baton, pool);
1544
svn_wc_cleanup2 (const char *path,
1545
const char *diff3_cmd,
1546
svn_cancel_func_t cancel_func,
1550
apr_hash_t *entries = NULL;
1551
apr_hash_index_t *hi;
1552
svn_node_kind_t kind;
1553
svn_wc_adm_access_t *adm_access;
1554
svn_boolean_t cleanup;
1555
int wc_format_version;
1556
apr_pool_t *subpool;
1558
/* Check cancellation; note that this catches recursive calls too. */
1560
SVN_ERR (cancel_func (cancel_baton));
1562
SVN_ERR (svn_wc_check_wc (path, &wc_format_version, pool));
1564
/* a "version" of 0 means a non-wc directory */
1565
if (wc_format_version == 0)
1566
return svn_error_createf
1567
(SVN_ERR_WC_NOT_DIRECTORY, NULL,
1568
_("'%s' is not a working copy directory"),
1569
svn_path_local_style (path, pool));
1571
/* Lock this working copy directory, or steal an existing lock */
1572
SVN_ERR (svn_wc__adm_steal_write_lock (&adm_access, NULL, path, pool));
1574
/* Recurse on versioned elements first, oddly enough. */
1575
SVN_ERR (svn_wc_entries_read (&entries, adm_access, FALSE, pool));
1576
subpool = svn_pool_create (pool);
1577
for (hi = apr_hash_first (pool, entries); hi; hi = apr_hash_next (hi))
1581
const svn_wc_entry_t *entry;
1582
const char *entry_path;
1584
svn_pool_clear (subpool);
1585
apr_hash_this (hi, &key, NULL, &val);
1587
entry_path = svn_path_join (path, key, subpool);
1589
if (entry->kind == svn_node_dir
1590
&& strcmp (key, SVN_WC_ENTRY_THIS_DIR) != 0)
1592
/* Sub-directories */
1593
SVN_ERR (svn_io_check_path (entry_path, &kind, subpool));
1594
if (kind == svn_node_dir)
1595
SVN_ERR (svn_wc_cleanup2 (entry_path, diff3_cmd,
1596
cancel_func, cancel_baton, subpool));
1600
/* "." and things that are not directories, check for mods to
1601
trigger the timestamp repair mechanism. Since this rewrites
1602
the entries file for each timestamp fixed it has the potential
1603
to be slow, perhaps we need something more sophisticated? */
1604
svn_boolean_t modified;
1605
SVN_ERR (svn_wc_props_modified_p (&modified, entry_path,
1606
adm_access, subpool));
1607
if (entry->kind == svn_node_file)
1608
SVN_ERR (svn_wc_text_modified_p (&modified, entry_path, FALSE,
1609
adm_access, subpool));
1612
svn_pool_destroy (subpool);
1614
if (svn_wc__adm_path_exists (svn_wc_adm_access_path (adm_access), 0, pool,
1615
SVN_WC__ADM_KILLME, NULL))
1617
/* A KILLME indicates that the log has already been run */
1618
SVN_ERR (handle_killme (adm_access, cancel_func, cancel_baton, pool));
1622
/* In an attempt to maintain consitency between the decisions made in
1623
this function, and those made in the access baton lock-removal code,
1624
we use the same test as the lock-removal code. */
1625
SVN_ERR (svn_wc__adm_is_cleanup_required (&cleanup, adm_access, pool));
1627
SVN_ERR (svn_wc__run_log (adm_access, diff3_cmd, pool));
1630
/* Cleanup the tmp area of the admin subdir, if running the log has not
1631
removed it! The logs have been run, so anything left here has no hope
1633
if (svn_wc__adm_path_exists (path, 0, pool, NULL))
1634
SVN_ERR (svn_wc__adm_cleanup_tmp_area (adm_access, pool));
1636
SVN_ERR (svn_wc_adm_close (adm_access));
1638
return SVN_NO_ERROR;