diff options
Diffstat (limited to 'subversion/libsvn_wc/questions.c')
-rw-r--r-- | subversion/libsvn_wc/questions.c | 621 |
1 files changed, 621 insertions, 0 deletions
diff --git a/subversion/libsvn_wc/questions.c b/subversion/libsvn_wc/questions.c new file mode 100644 index 0000000..c2a42b6 --- /dev/null +++ b/subversion/libsvn_wc/questions.c @@ -0,0 +1,621 @@ +/* + * questions.c: routines for asking questions about working copies + * + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + */ + + + +#include <string.h> + +#include <apr_pools.h> +#include <apr_file_io.h> +#include <apr_file_info.h> +#include <apr_time.h> + +#include "svn_pools.h" +#include "svn_types.h" +#include "svn_string.h" +#include "svn_error.h" +#include "svn_dirent_uri.h" +#include "svn_time.h" +#include "svn_io.h" +#include "svn_props.h" + +#include "wc.h" +#include "conflicts.h" +#include "translate.h" +#include "wc_db.h" + +#include "svn_private_config.h" +#include "private/svn_wc_private.h" + + + +/*** svn_wc_text_modified_p ***/ + +/* svn_wc_text_modified_p answers the question: + + "Are the contents of F different than the contents of + .svn/text-base/F.svn-base or .svn/tmp/text-base/F.svn-base?" + + In the first case, we're looking to see if a user has made local + modifications to a file since the last update or commit. In the + second, the file may not be versioned yet (it doesn't exist in + entries). Support for the latter case came about to facilitate + forced checkouts, updates, and switches, where an unversioned file + may obstruct a file about to be added. + + Note: Assuming that F lives in a directory D at revision V, please + notice that we are *NOT* answering the question, "are the contents + of F different than revision V of F?" While F may be at a different + revision number than its parent directory, but we're only looking + for local edits on F, not for consistent directory revisions. + + TODO: the logic of the routines on this page might change in the + future, as they bear some relation to the user interface. For + example, if a file is removed -- without telling subversion about + it -- how should subversion react? Should it copy the file back + out of text-base? Should it ask whether one meant to officially + mark it for removal? +*/ + + +/* Set *MODIFIED_P to TRUE if (after translation) VERSIONED_FILE_ABSPATH + * (of VERSIONED_FILE_SIZE bytes) differs from PRISTINE_STREAM (of + * PRISTINE_SIZE bytes), else to FALSE if not. + * + * If EXACT_COMPARISON is FALSE, translate VERSIONED_FILE_ABSPATH's EOL + * style and keywords to repository-normal form according to its properties, + * and compare the result with PRISTINE_STREAM. If EXACT_COMPARISON is + * TRUE, translate PRISTINE_STREAM's EOL style and keywords to working-copy + * form according to VERSIONED_FILE_ABSPATH's properties, and compare the + * result with VERSIONED_FILE_ABSPATH. + * + * HAS_PROPS should be TRUE if the file had properties when it was not + * modified, otherwise FALSE. + * + * PROPS_MOD should be TRUE if the file's properties have been changed, + * otherwise FALSE. + * + * PRISTINE_STREAM will be closed before a successful return. + * + * DB is a wc_db; use SCRATCH_POOL for temporary allocation. + */ +static svn_error_t * +compare_and_verify(svn_boolean_t *modified_p, + svn_wc__db_t *db, + const char *versioned_file_abspath, + svn_filesize_t versioned_file_size, + svn_stream_t *pristine_stream, + svn_filesize_t pristine_size, + svn_boolean_t has_props, + svn_boolean_t props_mod, + svn_boolean_t exact_comparison, + apr_pool_t *scratch_pool) +{ + svn_boolean_t same; + svn_subst_eol_style_t eol_style; + const char *eol_str; + apr_hash_t *keywords; + svn_boolean_t special = FALSE; + svn_boolean_t need_translation; + + SVN_ERR_ASSERT(svn_dirent_is_absolute(versioned_file_abspath)); + + if (props_mod) + has_props = TRUE; /* Maybe it didn't have properties; but it has now */ + + if (has_props) + { + SVN_ERR(svn_wc__get_translate_info(&eol_style, &eol_str, + &keywords, + &special, + db, versioned_file_abspath, NULL, + !exact_comparison, + scratch_pool, scratch_pool)); + + need_translation = svn_subst_translation_required(eol_style, eol_str, + keywords, special, + TRUE); + } + else + need_translation = FALSE; + + if (! need_translation + && (versioned_file_size != pristine_size)) + { + *modified_p = TRUE; + + /* ### Why did we open the pristine? */ + return svn_error_trace(svn_stream_close(pristine_stream)); + } + + /* ### Other checks possible? */ + + if (need_translation) + { + /* Reading files is necessary. */ + svn_stream_t *v_stream; /* versioned_file */ + + if (special) + { + SVN_ERR(svn_subst_read_specialfile(&v_stream, versioned_file_abspath, + scratch_pool, scratch_pool)); + } + else + { + SVN_ERR(svn_stream_open_readonly(&v_stream, versioned_file_abspath, + scratch_pool, scratch_pool)); + + if (!exact_comparison && need_translation) + { + if (eol_style == svn_subst_eol_style_native) + eol_str = SVN_SUBST_NATIVE_EOL_STR; + else if (eol_style != svn_subst_eol_style_fixed + && eol_style != svn_subst_eol_style_none) + return svn_error_create(SVN_ERR_IO_UNKNOWN_EOL, + svn_stream_close(v_stream), NULL); + + /* Wrap file stream to detranslate into normal form, + * "repairing" the EOL style if it is inconsistent. */ + v_stream = svn_subst_stream_translated(v_stream, + eol_str, + TRUE /* repair */, + keywords, + FALSE /* expand */, + scratch_pool); + } + else if (need_translation) + { + /* Wrap base stream to translate into working copy form, and + * arrange to throw an error if its EOL style is inconsistent. */ + pristine_stream = svn_subst_stream_translated(pristine_stream, + eol_str, FALSE, + keywords, TRUE, + scratch_pool); + } + } + + SVN_ERR(svn_stream_contents_same2(&same, pristine_stream, v_stream, + scratch_pool)); + } + else + { + /* Translation would be a no-op, so compare the original file. */ + svn_stream_t *v_stream; /* versioned_file */ + + SVN_ERR(svn_stream_open_readonly(&v_stream, versioned_file_abspath, + scratch_pool, scratch_pool)); + + SVN_ERR(svn_stream_contents_same2(&same, pristine_stream, v_stream, + scratch_pool)); + } + + *modified_p = (! same); + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_wc__internal_file_modified_p(svn_boolean_t *modified_p, + svn_wc__db_t *db, + const char *local_abspath, + svn_boolean_t exact_comparison, + apr_pool_t *scratch_pool) +{ + svn_stream_t *pristine_stream; + svn_filesize_t pristine_size; + svn_wc__db_status_t status; + svn_node_kind_t kind; + const svn_checksum_t *checksum; + svn_filesize_t recorded_size; + apr_time_t recorded_mod_time; + svn_boolean_t has_props; + svn_boolean_t props_mod; + const svn_io_dirent2_t *dirent; + + /* Read the relevant info */ + SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, &checksum, NULL, NULL, NULL, + NULL, NULL, NULL, + &recorded_size, &recorded_mod_time, + NULL, NULL, NULL, &has_props, &props_mod, + NULL, NULL, NULL, + db, local_abspath, + scratch_pool, scratch_pool)); + + /* If we don't have a pristine or the node has a status that allows a + pristine, just say that the node is modified */ + if (!checksum + || (kind != svn_node_file) + || ((status != svn_wc__db_status_normal) + && (status != svn_wc__db_status_added))) + { + *modified_p = TRUE; + return SVN_NO_ERROR; + } + + SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath, FALSE, TRUE, + scratch_pool, scratch_pool)); + + if (dirent->kind != svn_node_file) + { + /* There is no file on disk, so the text is missing, not modified. */ + *modified_p = FALSE; + return SVN_NO_ERROR; + } + + if (! exact_comparison) + { + /* We're allowed to use a heuristic to determine whether files may + have changed. The heuristic has these steps: + + 1. Compare the working file's size + with the size cached in the entries file + 2. If they differ, do a full file compare + 3. Compare the working file's timestamp + with the timestamp cached in the entries file + 4. If they differ, do a full file compare + 5. Otherwise, return indicating an unchanged file. + + There are 2 problematic situations which may occur: + + 1. The cached working size is missing + --> In this case, we forget we ever tried to compare + and skip to the timestamp comparison. This is + because old working copies do not contain cached sizes + + 2. The cached timestamp is missing + --> In this case, we forget we ever tried to compare + and skip to full file comparison. This is because + the timestamp will be removed when the library + updates a locally changed file. (ie, this only happens + when the file was locally modified.) + + */ + + /* Compare the sizes, if applicable */ + if (recorded_size != SVN_INVALID_FILESIZE + && dirent->filesize != recorded_size) + goto compare_them; + + /* Compare the timestamps + + Note: recorded_mod_time == 0 means not available, + which also means the timestamps won't be equal, + so there's no need to explicitly check the 'absent' value. */ + if (recorded_mod_time != dirent->mtime) + goto compare_them; + + *modified_p = FALSE; + return SVN_NO_ERROR; + } + + compare_them: + SVN_ERR(svn_wc__db_pristine_read(&pristine_stream, &pristine_size, + db, local_abspath, checksum, + scratch_pool, scratch_pool)); + + /* Check all bytes, and verify checksum if requested. */ + { + svn_error_t *err; + err = compare_and_verify(modified_p, db, + local_abspath, dirent->filesize, + pristine_stream, pristine_size, + has_props, props_mod, + exact_comparison, + scratch_pool); + + /* At this point we already opened the pristine file, so we know that + the access denied applies to the working copy path */ + if (err && APR_STATUS_IS_EACCES(err->apr_err)) + return svn_error_create(SVN_ERR_WC_PATH_ACCESS_DENIED, err, NULL); + else + SVN_ERR(err); + } + + if (!*modified_p) + { + svn_boolean_t own_lock; + + /* The timestamp is missing or "broken" so "repair" it if we can. */ + SVN_ERR(svn_wc__db_wclock_owns_lock(&own_lock, db, local_abspath, FALSE, + scratch_pool)); + if (own_lock) + SVN_ERR(svn_wc__db_global_record_fileinfo(db, local_abspath, + dirent->filesize, + dirent->mtime, + scratch_pool)); + } + + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_wc_text_modified_p2(svn_boolean_t *modified_p, + svn_wc_context_t *wc_ctx, + const char *local_abspath, + svn_boolean_t unused, + apr_pool_t *scratch_pool) +{ + return svn_wc__internal_file_modified_p(modified_p, wc_ctx->db, + local_abspath, FALSE, scratch_pool); +} + + + +static svn_error_t * +internal_conflicted_p(svn_boolean_t *text_conflicted_p, + svn_boolean_t *prop_conflicted_p, + svn_boolean_t *tree_conflicted_p, + svn_boolean_t *ignore_move_edit_p, + svn_wc__db_t *db, + const char *local_abspath, + apr_pool_t *scratch_pool) +{ + svn_node_kind_t kind; + svn_skel_t *conflicts; + svn_boolean_t resolved_text = FALSE; + svn_boolean_t resolved_props = FALSE; + + SVN_ERR(svn_wc__db_read_conflict(&conflicts, db, local_abspath, + scratch_pool, scratch_pool)); + + if (!conflicts) + { + if (text_conflicted_p) + *text_conflicted_p = FALSE; + if (prop_conflicted_p) + *prop_conflicted_p = FALSE; + if (tree_conflicted_p) + *tree_conflicted_p = FALSE; + if (ignore_move_edit_p) + *ignore_move_edit_p = FALSE; + + return SVN_NO_ERROR; + } + + SVN_ERR(svn_wc__conflict_read_info(NULL, NULL, text_conflicted_p, + prop_conflicted_p, tree_conflicted_p, + db, local_abspath, conflicts, + scratch_pool, scratch_pool)); + + if (text_conflicted_p && *text_conflicted_p) + { + const char *mine_abspath; + const char *their_old_abspath; + const char *their_abspath; + svn_boolean_t done = FALSE; + + /* Look for any text conflict, exercising only as much effort as + necessary to obtain a definitive answer. This only applies to + files, but we don't have to explicitly check that entry is a + file, since these attributes would never be set on a directory + anyway. A conflict file entry notation only counts if the + conflict file still exists on disk. */ + + SVN_ERR(svn_wc__conflict_read_text_conflict(&mine_abspath, + &their_old_abspath, + &their_abspath, + db, local_abspath, conflicts, + scratch_pool, scratch_pool)); + + if (mine_abspath) + { + SVN_ERR(svn_io_check_path(mine_abspath, &kind, scratch_pool)); + + *text_conflicted_p = (kind == svn_node_file); + + if (*text_conflicted_p) + done = TRUE; + } + + if (!done && their_abspath) + { + SVN_ERR(svn_io_check_path(their_abspath, &kind, scratch_pool)); + + *text_conflicted_p = (kind == svn_node_file); + + if (*text_conflicted_p) + done = TRUE; + } + + if (!done && their_old_abspath) + { + SVN_ERR(svn_io_check_path(their_old_abspath, &kind, scratch_pool)); + + *text_conflicted_p = (kind == svn_node_file); + + if (*text_conflicted_p) + done = TRUE; + } + + if (!done && (mine_abspath || their_abspath || their_old_abspath)) + resolved_text = TRUE; /* Remove in-db conflict marker */ + } + + if (prop_conflicted_p && *prop_conflicted_p) + { + const char *prej_abspath; + + SVN_ERR(svn_wc__conflict_read_prop_conflict(&prej_abspath, + NULL, NULL, NULL, NULL, + db, local_abspath, conflicts, + scratch_pool, scratch_pool)); + + if (prej_abspath) + { + SVN_ERR(svn_io_check_path(prej_abspath, &kind, scratch_pool)); + + *prop_conflicted_p = (kind == svn_node_file); + + if (! *prop_conflicted_p) + resolved_props = TRUE; /* Remove in-db conflict marker */ + } + } + + if (ignore_move_edit_p) + { + *ignore_move_edit_p = FALSE; + if (tree_conflicted_p && *tree_conflicted_p) + { + svn_wc_conflict_reason_t reason; + svn_wc_conflict_action_t action; + + SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action, NULL, + db, local_abspath, + conflicts, + scratch_pool, + scratch_pool)); + + if (reason == svn_wc_conflict_reason_moved_away + && action == svn_wc_conflict_action_edit) + { + *tree_conflicted_p = FALSE; + *ignore_move_edit_p = TRUE; + } + } + } + + if (resolved_text || resolved_props) + { + svn_boolean_t own_lock; + + /* The marker files are missing, so "repair" wc.db if we can */ + SVN_ERR(svn_wc__db_wclock_owns_lock(&own_lock, db, local_abspath, FALSE, + scratch_pool)); + if (own_lock) + SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath, + resolved_text, + resolved_props, + FALSE /* resolved_tree */, + NULL /* work_items */, + scratch_pool)); + } + + return SVN_NO_ERROR; +} + +svn_error_t * +svn_wc__internal_conflicted_p(svn_boolean_t *text_conflicted_p, + svn_boolean_t *prop_conflicted_p, + svn_boolean_t *tree_conflicted_p, + svn_wc__db_t *db, + const char *local_abspath, + apr_pool_t *scratch_pool) +{ + SVN_ERR(internal_conflicted_p(text_conflicted_p, prop_conflicted_p, + tree_conflicted_p, NULL, + db, local_abspath, scratch_pool)); + return SVN_NO_ERROR; +} + +svn_error_t * +svn_wc__conflicted_for_update_p(svn_boolean_t *conflicted_p, + svn_boolean_t *conflict_ignored_p, + svn_wc__db_t *db, + const char *local_abspath, + svn_boolean_t tree_only, + apr_pool_t *scratch_pool) +{ + svn_boolean_t text_conflicted, prop_conflicted, tree_conflicted; + svn_boolean_t conflict_ignored; + + if (!conflict_ignored_p) + conflict_ignored_p = &conflict_ignored; + + SVN_ERR(internal_conflicted_p(tree_only ? NULL: &text_conflicted, + tree_only ? NULL: &prop_conflicted, + &tree_conflicted, conflict_ignored_p, + db, local_abspath, scratch_pool)); + if (tree_only) + *conflicted_p = tree_conflicted; + else + *conflicted_p = text_conflicted || prop_conflicted || tree_conflicted; + + return SVN_NO_ERROR; +} + + +svn_error_t * +svn_wc_conflicted_p3(svn_boolean_t *text_conflicted_p, + svn_boolean_t *prop_conflicted_p, + svn_boolean_t *tree_conflicted_p, + svn_wc_context_t *wc_ctx, + const char *local_abspath, + apr_pool_t *scratch_pool) +{ + return svn_error_trace(svn_wc__internal_conflicted_p(text_conflicted_p, + prop_conflicted_p, + tree_conflicted_p, + wc_ctx->db, + local_abspath, + scratch_pool)); +} + +svn_error_t * +svn_wc__min_max_revisions(svn_revnum_t *min_revision, + svn_revnum_t *max_revision, + svn_wc_context_t *wc_ctx, + const char *local_abspath, + svn_boolean_t committed, + apr_pool_t *scratch_pool) +{ + return svn_error_trace(svn_wc__db_min_max_revisions(min_revision, + max_revision, + wc_ctx->db, + local_abspath, + committed, + scratch_pool)); +} + + +svn_error_t * +svn_wc__has_switched_subtrees(svn_boolean_t *is_switched, + svn_wc_context_t *wc_ctx, + const char *local_abspath, + const char *trail_url, + apr_pool_t *scratch_pool) +{ + return svn_error_trace(svn_wc__db_has_switched_subtrees(is_switched, + wc_ctx->db, + local_abspath, + trail_url, + scratch_pool)); +} + + +svn_error_t * +svn_wc__has_local_mods(svn_boolean_t *is_modified, + svn_wc_context_t *wc_ctx, + const char *local_abspath, + svn_cancel_func_t cancel_func, + void *cancel_baton, + apr_pool_t *scratch_pool) +{ + return svn_error_trace(svn_wc__db_has_local_mods(is_modified, + wc_ctx->db, + local_abspath, + cancel_func, + cancel_baton, + scratch_pool)); +} |