2 * questions.c: routines for asking questions about working copies
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 * ====================================================================
28 #include <apr_pools.h>
29 #include <apr_file_io.h>
30 #include <apr_file_info.h>
33 #include "svn_pools.h"
34 #include "svn_types.h"
35 #include "svn_string.h"
36 #include "svn_error.h"
37 #include "svn_dirent_uri.h"
40 #include "svn_props.h"
43 #include "conflicts.h"
44 #include "translate.h"
47 #include "svn_private_config.h"
48 #include "private/svn_wc_private.h"
52 /*** svn_wc_text_modified_p ***/
54 /* svn_wc_text_modified_p answers the question:
56 "Are the contents of F different than the contents of
57 .svn/text-base/F.svn-base or .svn/tmp/text-base/F.svn-base?"
59 In the first case, we're looking to see if a user has made local
60 modifications to a file since the last update or commit. In the
61 second, the file may not be versioned yet (it doesn't exist in
62 entries). Support for the latter case came about to facilitate
63 forced checkouts, updates, and switches, where an unversioned file
64 may obstruct a file about to be added.
66 Note: Assuming that F lives in a directory D at revision V, please
67 notice that we are *NOT* answering the question, "are the contents
68 of F different than revision V of F?" While F may be at a different
69 revision number than its parent directory, but we're only looking
70 for local edits on F, not for consistent directory revisions.
72 TODO: the logic of the routines on this page might change in the
73 future, as they bear some relation to the user interface. For
74 example, if a file is removed -- without telling subversion about
75 it -- how should subversion react? Should it copy the file back
76 out of text-base? Should it ask whether one meant to officially
81 /* Set *MODIFIED_P to TRUE if (after translation) VERSIONED_FILE_ABSPATH
82 * (of VERSIONED_FILE_SIZE bytes) differs from PRISTINE_STREAM (of
83 * PRISTINE_SIZE bytes), else to FALSE if not.
85 * If EXACT_COMPARISON is FALSE, translate VERSIONED_FILE_ABSPATH's EOL
86 * style and keywords to repository-normal form according to its properties,
87 * and compare the result with PRISTINE_STREAM. If EXACT_COMPARISON is
88 * TRUE, translate PRISTINE_STREAM's EOL style and keywords to working-copy
89 * form according to VERSIONED_FILE_ABSPATH's properties, and compare the
90 * result with VERSIONED_FILE_ABSPATH.
92 * HAS_PROPS should be TRUE if the file had properties when it was not
93 * modified, otherwise FALSE.
95 * PROPS_MOD should be TRUE if the file's properties have been changed,
98 * PRISTINE_STREAM will be closed before a successful return.
100 * DB is a wc_db; use SCRATCH_POOL for temporary allocation.
103 compare_and_verify(svn_boolean_t *modified_p,
105 const char *versioned_file_abspath,
106 svn_filesize_t versioned_file_size,
107 svn_stream_t *pristine_stream,
108 svn_filesize_t pristine_size,
109 svn_boolean_t has_props,
110 svn_boolean_t props_mod,
111 svn_boolean_t exact_comparison,
112 apr_pool_t *scratch_pool)
115 svn_subst_eol_style_t eol_style;
117 apr_hash_t *keywords;
118 svn_boolean_t special = FALSE;
119 svn_boolean_t need_translation;
121 SVN_ERR_ASSERT(svn_dirent_is_absolute(versioned_file_abspath));
124 has_props = TRUE; /* Maybe it didn't have properties; but it has now */
128 SVN_ERR(svn_wc__get_translate_info(&eol_style, &eol_str,
131 db, versioned_file_abspath, NULL,
133 scratch_pool, scratch_pool));
135 need_translation = svn_subst_translation_required(eol_style, eol_str,
140 need_translation = FALSE;
142 if (! need_translation
143 && (versioned_file_size != pristine_size))
147 /* ### Why did we open the pristine? */
148 return svn_error_trace(svn_stream_close(pristine_stream));
151 /* ### Other checks possible? */
153 if (need_translation)
155 /* Reading files is necessary. */
156 svn_stream_t *v_stream; /* versioned_file */
160 SVN_ERR(svn_subst_read_specialfile(&v_stream, versioned_file_abspath,
161 scratch_pool, scratch_pool));
165 SVN_ERR(svn_stream_open_readonly(&v_stream, versioned_file_abspath,
166 scratch_pool, scratch_pool));
168 if (!exact_comparison && need_translation)
170 if (eol_style == svn_subst_eol_style_native)
171 eol_str = SVN_SUBST_NATIVE_EOL_STR;
172 else if (eol_style != svn_subst_eol_style_fixed
173 && eol_style != svn_subst_eol_style_none)
174 return svn_error_create(SVN_ERR_IO_UNKNOWN_EOL,
175 svn_stream_close(v_stream), NULL);
177 /* Wrap file stream to detranslate into normal form,
178 * "repairing" the EOL style if it is inconsistent. */
179 v_stream = svn_subst_stream_translated(v_stream,
186 else if (need_translation)
188 /* Wrap base stream to translate into working copy form, and
189 * arrange to throw an error if its EOL style is inconsistent. */
190 pristine_stream = svn_subst_stream_translated(pristine_stream,
197 SVN_ERR(svn_stream_contents_same2(&same, pristine_stream, v_stream,
202 /* Translation would be a no-op, so compare the original file. */
203 svn_stream_t *v_stream; /* versioned_file */
205 SVN_ERR(svn_stream_open_readonly(&v_stream, versioned_file_abspath,
206 scratch_pool, scratch_pool));
208 SVN_ERR(svn_stream_contents_same2(&same, pristine_stream, v_stream,
212 *modified_p = (! same);
218 svn_wc__internal_file_modified_p(svn_boolean_t *modified_p,
220 const char *local_abspath,
221 svn_boolean_t exact_comparison,
222 apr_pool_t *scratch_pool)
224 svn_stream_t *pristine_stream;
225 svn_filesize_t pristine_size;
226 svn_wc__db_status_t status;
227 svn_node_kind_t kind;
228 const svn_checksum_t *checksum;
229 svn_filesize_t recorded_size;
230 apr_time_t recorded_mod_time;
231 svn_boolean_t has_props;
232 svn_boolean_t props_mod;
233 const svn_io_dirent2_t *dirent;
235 /* Read the relevant info */
236 SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
237 NULL, NULL, NULL, &checksum, NULL, NULL, NULL,
239 &recorded_size, &recorded_mod_time,
240 NULL, NULL, NULL, &has_props, &props_mod,
243 scratch_pool, scratch_pool));
245 /* If we don't have a pristine or the node has a status that allows a
246 pristine, just say that the node is modified */
248 || (kind != svn_node_file)
249 || ((status != svn_wc__db_status_normal)
250 && (status != svn_wc__db_status_added)))
256 SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath, FALSE, TRUE,
257 scratch_pool, scratch_pool));
259 if (dirent->kind != svn_node_file)
261 /* There is no file on disk, so the text is missing, not modified. */
266 if (! exact_comparison)
268 /* We're allowed to use a heuristic to determine whether files may
269 have changed. The heuristic has these steps:
271 1. Compare the working file's size
272 with the size cached in the entries file
273 2. If they differ, do a full file compare
274 3. Compare the working file's timestamp
275 with the timestamp cached in the entries file
276 4. If they differ, do a full file compare
277 5. Otherwise, return indicating an unchanged file.
279 There are 2 problematic situations which may occur:
281 1. The cached working size is missing
282 --> In this case, we forget we ever tried to compare
283 and skip to the timestamp comparison. This is
284 because old working copies do not contain cached sizes
286 2. The cached timestamp is missing
287 --> In this case, we forget we ever tried to compare
288 and skip to full file comparison. This is because
289 the timestamp will be removed when the library
290 updates a locally changed file. (ie, this only happens
291 when the file was locally modified.)
295 /* Compare the sizes, if applicable */
296 if (recorded_size != SVN_INVALID_FILESIZE
297 && dirent->filesize != recorded_size)
300 /* Compare the timestamps
302 Note: recorded_mod_time == 0 means not available,
303 which also means the timestamps won't be equal,
304 so there's no need to explicitly check the 'absent' value. */
305 if (recorded_mod_time != dirent->mtime)
313 SVN_ERR(svn_wc__db_pristine_read(&pristine_stream, &pristine_size,
314 db, local_abspath, checksum,
315 scratch_pool, scratch_pool));
317 /* Check all bytes, and verify checksum if requested. */
320 err = compare_and_verify(modified_p, db,
321 local_abspath, dirent->filesize,
322 pristine_stream, pristine_size,
323 has_props, props_mod,
327 /* At this point we already opened the pristine file, so we know that
328 the access denied applies to the working copy path */
329 if (err && APR_STATUS_IS_EACCES(err->apr_err))
330 return svn_error_create(SVN_ERR_WC_PATH_ACCESS_DENIED, err, NULL);
337 svn_boolean_t own_lock;
339 /* The timestamp is missing or "broken" so "repair" it if we can. */
340 SVN_ERR(svn_wc__db_wclock_owns_lock(&own_lock, db, local_abspath, FALSE,
343 SVN_ERR(svn_wc__db_global_record_fileinfo(db, local_abspath,
354 svn_wc_text_modified_p2(svn_boolean_t *modified_p,
355 svn_wc_context_t *wc_ctx,
356 const char *local_abspath,
357 svn_boolean_t unused,
358 apr_pool_t *scratch_pool)
360 return svn_wc__internal_file_modified_p(modified_p, wc_ctx->db,
361 local_abspath, FALSE, scratch_pool);
367 internal_conflicted_p(svn_boolean_t *text_conflicted_p,
368 svn_boolean_t *prop_conflicted_p,
369 svn_boolean_t *tree_conflicted_p,
370 svn_boolean_t *ignore_move_edit_p,
372 const char *local_abspath,
373 apr_pool_t *scratch_pool)
375 svn_node_kind_t kind;
376 svn_skel_t *conflicts;
377 svn_boolean_t resolved_text = FALSE;
378 svn_boolean_t resolved_props = FALSE;
380 SVN_ERR(svn_wc__db_read_conflict(&conflicts, db, local_abspath,
381 scratch_pool, scratch_pool));
385 if (text_conflicted_p)
386 *text_conflicted_p = FALSE;
387 if (prop_conflicted_p)
388 *prop_conflicted_p = FALSE;
389 if (tree_conflicted_p)
390 *tree_conflicted_p = FALSE;
391 if (ignore_move_edit_p)
392 *ignore_move_edit_p = FALSE;
397 SVN_ERR(svn_wc__conflict_read_info(NULL, NULL, text_conflicted_p,
398 prop_conflicted_p, tree_conflicted_p,
399 db, local_abspath, conflicts,
400 scratch_pool, scratch_pool));
402 if (text_conflicted_p && *text_conflicted_p)
404 const char *mine_abspath;
405 const char *their_old_abspath;
406 const char *their_abspath;
407 svn_boolean_t done = FALSE;
409 /* Look for any text conflict, exercising only as much effort as
410 necessary to obtain a definitive answer. This only applies to
411 files, but we don't have to explicitly check that entry is a
412 file, since these attributes would never be set on a directory
413 anyway. A conflict file entry notation only counts if the
414 conflict file still exists on disk. */
416 SVN_ERR(svn_wc__conflict_read_text_conflict(&mine_abspath,
419 db, local_abspath, conflicts,
420 scratch_pool, scratch_pool));
424 SVN_ERR(svn_io_check_path(mine_abspath, &kind, scratch_pool));
426 *text_conflicted_p = (kind == svn_node_file);
428 if (*text_conflicted_p)
432 if (!done && their_abspath)
434 SVN_ERR(svn_io_check_path(their_abspath, &kind, scratch_pool));
436 *text_conflicted_p = (kind == svn_node_file);
438 if (*text_conflicted_p)
442 if (!done && their_old_abspath)
444 SVN_ERR(svn_io_check_path(their_old_abspath, &kind, scratch_pool));
446 *text_conflicted_p = (kind == svn_node_file);
448 if (*text_conflicted_p)
452 if (!done && (mine_abspath || their_abspath || their_old_abspath))
453 resolved_text = TRUE; /* Remove in-db conflict marker */
456 if (prop_conflicted_p && *prop_conflicted_p)
458 const char *prej_abspath;
460 SVN_ERR(svn_wc__conflict_read_prop_conflict(&prej_abspath,
461 NULL, NULL, NULL, NULL,
462 db, local_abspath, conflicts,
463 scratch_pool, scratch_pool));
467 SVN_ERR(svn_io_check_path(prej_abspath, &kind, scratch_pool));
469 *prop_conflicted_p = (kind == svn_node_file);
471 if (! *prop_conflicted_p)
472 resolved_props = TRUE; /* Remove in-db conflict marker */
476 if (ignore_move_edit_p)
478 *ignore_move_edit_p = FALSE;
479 if (tree_conflicted_p && *tree_conflicted_p)
481 svn_wc_conflict_reason_t reason;
482 svn_wc_conflict_action_t action;
484 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action, NULL,
490 if (reason == svn_wc_conflict_reason_moved_away
491 && action == svn_wc_conflict_action_edit)
493 *tree_conflicted_p = FALSE;
494 *ignore_move_edit_p = TRUE;
499 if (resolved_text || resolved_props)
501 svn_boolean_t own_lock;
503 /* The marker files are missing, so "repair" wc.db if we can */
504 SVN_ERR(svn_wc__db_wclock_owns_lock(&own_lock, db, local_abspath, FALSE,
507 SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath,
510 FALSE /* resolved_tree */,
511 NULL /* work_items */,
519 svn_wc__internal_conflicted_p(svn_boolean_t *text_conflicted_p,
520 svn_boolean_t *prop_conflicted_p,
521 svn_boolean_t *tree_conflicted_p,
523 const char *local_abspath,
524 apr_pool_t *scratch_pool)
526 SVN_ERR(internal_conflicted_p(text_conflicted_p, prop_conflicted_p,
527 tree_conflicted_p, NULL,
528 db, local_abspath, scratch_pool));
533 svn_wc__conflicted_for_update_p(svn_boolean_t *conflicted_p,
534 svn_boolean_t *conflict_ignored_p,
536 const char *local_abspath,
537 svn_boolean_t tree_only,
538 apr_pool_t *scratch_pool)
540 svn_boolean_t text_conflicted, prop_conflicted, tree_conflicted;
541 svn_boolean_t conflict_ignored;
543 if (!conflict_ignored_p)
544 conflict_ignored_p = &conflict_ignored;
546 SVN_ERR(internal_conflicted_p(tree_only ? NULL: &text_conflicted,
547 tree_only ? NULL: &prop_conflicted,
548 &tree_conflicted, conflict_ignored_p,
549 db, local_abspath, scratch_pool));
551 *conflicted_p = tree_conflicted;
553 *conflicted_p = text_conflicted || prop_conflicted || tree_conflicted;
560 svn_wc_conflicted_p3(svn_boolean_t *text_conflicted_p,
561 svn_boolean_t *prop_conflicted_p,
562 svn_boolean_t *tree_conflicted_p,
563 svn_wc_context_t *wc_ctx,
564 const char *local_abspath,
565 apr_pool_t *scratch_pool)
567 return svn_error_trace(svn_wc__internal_conflicted_p(text_conflicted_p,
576 svn_wc__min_max_revisions(svn_revnum_t *min_revision,
577 svn_revnum_t *max_revision,
578 svn_wc_context_t *wc_ctx,
579 const char *local_abspath,
580 svn_boolean_t committed,
581 apr_pool_t *scratch_pool)
583 return svn_error_trace(svn_wc__db_min_max_revisions(min_revision,
593 svn_wc__has_switched_subtrees(svn_boolean_t *is_switched,
594 svn_wc_context_t *wc_ctx,
595 const char *local_abspath,
596 const char *trail_url,
597 apr_pool_t *scratch_pool)
599 return svn_error_trace(svn_wc__db_has_switched_subtrees(is_switched,
608 svn_wc__has_local_mods(svn_boolean_t *is_modified,
609 svn_wc_context_t *wc_ctx,
610 const char *local_abspath,
611 svn_cancel_func_t cancel_func,
613 apr_pool_t *scratch_pool)
615 return svn_error_trace(svn_wc__db_has_local_mods(is_modified,