summaryrefslogtreecommitdiffstats
path: root/subversion/libsvn_wc/questions.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_wc/questions.c')
-rw-r--r--subversion/libsvn_wc/questions.c621
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));
+}
OpenPOWER on IntegriCloud