summaryrefslogtreecommitdiffstats
path: root/subversion/libsvn_wc/diff_local.c
diff options
context:
space:
mode:
authorpeter <peter@FreeBSD.org>2013-06-18 02:07:41 +0000
committerpeter <peter@FreeBSD.org>2013-06-18 02:07:41 +0000
commitd25dac7fcc6acc838b71bbda8916fd9665c709ab (patch)
tree135691142dc0e75a5e5d97b5074d03436435b8e0 /subversion/libsvn_wc/diff_local.c
downloadFreeBSD-src-d25dac7fcc6acc838b71bbda8916fd9665c709ab.zip
FreeBSD-src-d25dac7fcc6acc838b71bbda8916fd9665c709ab.tar.gz
Import trimmed svn-1.8.0-rc3
Diffstat (limited to 'subversion/libsvn_wc/diff_local.c')
-rw-r--r--subversion/libsvn_wc/diff_local.c541
1 files changed, 541 insertions, 0 deletions
diff --git a/subversion/libsvn_wc/diff_local.c b/subversion/libsvn_wc/diff_local.c
new file mode 100644
index 0000000..ad87c76
--- /dev/null
+++ b/subversion/libsvn_wc/diff_local.c
@@ -0,0 +1,541 @@
+/*
+ * diff_pristine.c -- A simple diff walker which compares local files against
+ * their pristine versions.
+ *
+ * ====================================================================
+ * 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.
+ * ====================================================================
+ *
+ * This is the simple working copy diff algorithm which is used when you
+ * just use 'svn diff PATH'. It shows what is modified in your working copy
+ * since a node was checked out or copied but doesn't show most kinds of
+ * restructuring operations.
+ *
+ * You can look at this as another form of the status walker.
+ */
+
+#include <apr_hash.h>
+
+#include "svn_error.h"
+#include "svn_pools.h"
+#include "svn_dirent_uri.h"
+#include "svn_path.h"
+#include "svn_hash.h"
+
+#include "private/svn_wc_private.h"
+#include "private/svn_diff_tree.h"
+
+#include "wc.h"
+#include "props.h"
+#include "translate.h"
+#include "diff.h"
+
+#include "svn_private_config.h"
+
+/*-------------------------------------------------------------------------*/
+
+/* Baton containing the state of a directory
+ reported open via a diff processor */
+struct node_state_t
+{
+ struct node_state_t *parent;
+
+ apr_pool_t *pool;
+
+ const char *local_abspath;
+ const char *relpath;
+ void *baton;
+
+ svn_diff_source_t *left_src;
+ svn_diff_source_t *right_src;
+ svn_diff_source_t *copy_src;
+
+ svn_boolean_t skip;
+ svn_boolean_t skip_children;
+
+ apr_hash_t *left_props;
+ apr_hash_t *right_props;
+ const apr_array_header_t *propchanges;
+};
+
+/* The diff baton */
+struct diff_baton
+{
+ /* A wc db. */
+ svn_wc__db_t *db;
+
+ /* Report editor paths relative from this directory */
+ const char *anchor_abspath;
+
+ struct node_state_t *cur;
+
+ const svn_diff_tree_processor_t *processor;
+
+ /* Should this diff ignore node ancestry? */
+ svn_boolean_t ignore_ancestry;
+
+ /* Should this diff not compare copied files with their source? */
+ svn_boolean_t show_copies_as_adds;
+
+ /* Hash whose keys are const char * changelist names. */
+ apr_hash_t *changelist_hash;
+
+ /* Cancel function/baton */
+ svn_cancel_func_t cancel_func;
+ void *cancel_baton;
+
+ apr_pool_t *pool;
+};
+
+/* Recursively opens directories on the stack in EB, until LOCAL_ABSPATH
+ is reached. If RECURSIVE_SKIP is TRUE, don't open LOCAL_ABSPATH itself,
+ but create it marked with skip+skip_children.
+ */
+static svn_error_t *
+ensure_state(struct diff_baton *eb,
+ const char *local_abspath,
+ svn_boolean_t recursive_skip,
+ apr_pool_t *scratch_pool)
+{
+ struct node_state_t *ns;
+ apr_pool_t *ns_pool;
+ if (!eb->cur)
+ {
+ if (!svn_dirent_is_ancestor(eb->anchor_abspath, local_abspath))
+ return SVN_NO_ERROR;
+
+ SVN_ERR(ensure_state(eb,
+ svn_dirent_dirname(local_abspath,scratch_pool),
+ FALSE,
+ scratch_pool));
+ }
+ else if (svn_dirent_is_child(eb->cur->local_abspath, local_abspath, NULL))
+ SVN_ERR(ensure_state(eb, svn_dirent_dirname(local_abspath,scratch_pool),
+ FALSE,
+ scratch_pool));
+ else
+ return SVN_NO_ERROR;
+
+ if (eb->cur && eb->cur->skip_children)
+ return SVN_NO_ERROR;
+
+ ns_pool = svn_pool_create(eb->cur ? eb->cur->pool : eb->pool);
+ ns = apr_pcalloc(ns_pool, sizeof(*ns));
+
+ ns->pool = ns_pool;
+ ns->local_abspath = apr_pstrdup(ns_pool, local_abspath);
+ ns->relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, ns->local_abspath);
+ ns->parent = eb->cur;
+ eb->cur = ns;
+
+ if (recursive_skip)
+ {
+ ns->skip = TRUE;
+ ns->skip_children = TRUE;
+ return SVN_NO_ERROR;
+ }
+
+ {
+ svn_revnum_t revision;
+ svn_error_t *err;
+
+ err = svn_wc__db_base_get_info(NULL, NULL, &revision, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL,
+ eb->db, local_abspath,
+ scratch_pool, scratch_pool);
+
+ if (err)
+ {
+ if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
+ return svn_error_trace(err);
+ svn_error_clear(err);
+
+ revision = 0; /* Use original revision? */
+ }
+ ns->left_src = svn_diff__source_create(revision, ns->pool);
+ ns->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, ns->pool);
+
+ SVN_ERR(eb->processor->dir_opened(&ns->baton, &ns->skip,
+ &ns->skip_children,
+ ns->relpath,
+ ns->left_src,
+ ns->right_src,
+ NULL /* copyfrom_source */,
+ ns->parent ? ns->parent->baton : NULL,
+ eb->processor,
+ ns->pool, scratch_pool));
+ }
+
+ return SVN_NO_ERROR;
+}
+
+/* Implements svn_wc_status_func3_t */
+static svn_error_t *
+diff_status_callback(void *baton,
+ const char *local_abspath,
+ const svn_wc_status3_t *status,
+ apr_pool_t *scratch_pool)
+{
+ struct diff_baton *eb = baton;
+ svn_wc__db_t *db = eb->db;
+
+ switch (status->node_status)
+ {
+ case svn_wc_status_unversioned:
+ case svn_wc_status_ignored:
+ return SVN_NO_ERROR; /* No diff */
+
+ case svn_wc_status_conflicted:
+ if (status->text_status == svn_wc_status_none
+ && status->prop_status == svn_wc_status_none)
+ {
+ /* Node is an actual only node describing a tree conflict */
+ return SVN_NO_ERROR;
+ }
+ break;
+
+ default:
+ break; /* Go check other conditions */
+ }
+
+ /* Not text/prop modified, not copied. Easy out */
+ if (status->node_status == svn_wc_status_normal && !status->copied)
+ return SVN_NO_ERROR;
+
+ /* Mark all directories where we are no longer inside as closed */
+ while (eb->cur
+ && !svn_dirent_is_ancestor(eb->cur->local_abspath, local_abspath))
+ {
+ struct node_state_t *ns = eb->cur;
+
+ if (!ns->skip)
+ {
+ if (ns->propchanges)
+ SVN_ERR(eb->processor->dir_changed(ns->relpath,
+ ns->left_src,
+ ns->right_src,
+ ns->left_props,
+ ns->right_props,
+ ns->propchanges,
+ ns->baton,
+ eb->processor,
+ ns->pool));
+ else
+ SVN_ERR(eb->processor->dir_closed(ns->relpath,
+ ns->left_src,
+ ns->right_src,
+ ns->baton,
+ eb->processor,
+ ns->pool));
+ }
+ eb->cur = ns->parent;
+ svn_pool_clear(ns->pool);
+ }
+ SVN_ERR(ensure_state(eb, svn_dirent_dirname(local_abspath, scratch_pool),
+ FALSE, scratch_pool));
+
+ if (eb->cur && eb->cur->skip_children)
+ return SVN_NO_ERROR;
+
+ if (eb->changelist_hash != NULL
+ && (!status->changelist
+ || ! svn_hash_gets(eb->changelist_hash, status->changelist)))
+ return SVN_NO_ERROR; /* Filtered via changelist */
+
+ /* This code does about the same thing as the inner body of
+ walk_local_nodes_diff() in diff_editor.c, except that
+ it is already filtered by the status walker, doesn't have to
+ account for remote changes (and many tiny other details) */
+
+ {
+ svn_boolean_t repos_only;
+ svn_boolean_t local_only;
+ svn_wc__db_status_t db_status;
+ svn_boolean_t have_base;
+ svn_node_kind_t base_kind;
+ svn_node_kind_t db_kind = status->kind;
+ svn_depth_t depth_below_here = svn_depth_unknown;
+
+ const char *child_abspath = local_abspath;
+ const char *child_relpath = svn_dirent_skip_ancestor(eb->anchor_abspath,
+ local_abspath);
+
+
+ repos_only = FALSE;
+ local_only = FALSE;
+
+ /* ### optimize away this call using status info. Should
+ be possible in almost every case (except conflict, missing, obst.)*/
+ SVN_ERR(svn_wc__db_read_info(&db_status, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ &have_base, NULL, NULL,
+ eb->db, local_abspath,
+ scratch_pool, scratch_pool));
+ if (!have_base)
+ {
+ local_only = TRUE; /* Only report additions */
+ }
+ else if (db_status == svn_wc__db_status_normal)
+ {
+ /* Simple diff */
+ base_kind = db_kind;
+ }
+ else if (db_status == svn_wc__db_status_deleted)
+ {
+ svn_wc__db_status_t base_status;
+ repos_only = TRUE;
+ SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL,
+ eb->db, local_abspath,
+ scratch_pool, scratch_pool));
+
+ if (base_status != svn_wc__db_status_normal)
+ return SVN_NO_ERROR;
+ }
+ else
+ {
+ /* working status is either added or deleted */
+ svn_wc__db_status_t base_status;
+
+ SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL,
+ eb->db, local_abspath,
+ scratch_pool, scratch_pool));
+
+ if (base_status != svn_wc__db_status_normal)
+ local_only = TRUE;
+ else if (base_kind != db_kind || !eb->ignore_ancestry)
+ {
+ repos_only = TRUE;
+ local_only = TRUE;
+ }
+ }
+
+ if (repos_only)
+ {
+ /* Report repository form deleted */
+ if (base_kind == svn_node_file)
+ SVN_ERR(svn_wc__diff_base_only_file(db, child_abspath,
+ child_relpath,
+ SVN_INVALID_REVNUM,
+ eb->processor,
+ eb->cur ? eb->cur->baton : NULL,
+ scratch_pool));
+ else if (base_kind == svn_node_dir)
+ SVN_ERR(svn_wc__diff_base_only_dir(db, child_abspath,
+ child_relpath,
+ SVN_INVALID_REVNUM,
+ depth_below_here,
+ eb->processor,
+ eb->cur ? eb->cur->baton : NULL,
+ eb->cancel_func,
+ eb->cancel_baton,
+ scratch_pool));
+ }
+ else if (!local_only)
+ {
+ /* Diff base against actual */
+ if (db_kind == svn_node_file)
+ {
+ SVN_ERR(svn_wc__diff_base_working_diff(db, child_abspath,
+ child_relpath,
+ SVN_INVALID_REVNUM,
+ eb->changelist_hash,
+ eb->processor,
+ eb->cur
+ ? eb->cur->baton
+ : NULL,
+ FALSE,
+ eb->cancel_func,
+ eb->cancel_baton,
+ scratch_pool));
+ }
+ else if (db_kind == svn_node_dir)
+ {
+ SVN_ERR(ensure_state(eb, local_abspath, FALSE, scratch_pool));
+
+ if (status->prop_status != svn_wc_status_none
+ && status->prop_status != svn_wc_status_normal)
+ {
+ apr_array_header_t *propchanges;
+ SVN_ERR(svn_wc__db_base_get_props(&eb->cur->left_props,
+ eb->db, local_abspath,
+ eb->cur->pool,
+ scratch_pool));
+ SVN_ERR(svn_wc__db_read_props(&eb->cur->right_props,
+ eb->db, local_abspath,
+ eb->cur->pool,
+ scratch_pool));
+
+ SVN_ERR(svn_prop_diffs(&propchanges,
+ eb->cur->right_props,
+ eb->cur->left_props,
+ eb->cur->pool));
+
+ eb->cur->propchanges = propchanges;
+ }
+ }
+ }
+
+ if (local_only)
+ {
+ if (db_kind == svn_node_file)
+ SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath,
+ child_relpath,
+ eb->processor,
+ eb->cur ? eb->cur->baton : NULL,
+ eb->changelist_hash,
+ FALSE,
+ eb->cancel_func,
+ eb->cancel_baton,
+ scratch_pool));
+ else if (db_kind == svn_node_dir)
+ SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath,
+ child_relpath, depth_below_here,
+ eb->processor,
+ eb->cur ? eb->cur->baton : NULL,
+ eb->changelist_hash,
+ FALSE,
+ eb->cancel_func,
+ eb->cancel_baton,
+ scratch_pool));
+ }
+
+ if (db_kind == svn_node_dir && (local_only || repos_only))
+ SVN_ERR(ensure_state(eb, local_abspath, TRUE /* skip */, scratch_pool));
+ }
+
+ return SVN_NO_ERROR;
+}
+
+
+/* Public Interface */
+svn_error_t *
+svn_wc_diff6(svn_wc_context_t *wc_ctx,
+ const char *local_abspath,
+ const svn_wc_diff_callbacks4_t *callbacks,
+ void *callback_baton,
+ svn_depth_t depth,
+ svn_boolean_t ignore_ancestry,
+ svn_boolean_t show_copies_as_adds,
+ svn_boolean_t use_git_diff_format,
+ const apr_array_header_t *changelist_filter,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *scratch_pool)
+{
+ struct diff_baton eb = { 0 };
+ svn_node_kind_t kind;
+ svn_boolean_t get_all;
+ const svn_diff_tree_processor_t *processor;
+
+ SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
+ SVN_ERR(svn_wc__db_read_kind(&kind, wc_ctx->db, local_abspath,
+ FALSE /* allow_missing */,
+ TRUE /* show_deleted */,
+ FALSE /* show_hidden */,
+ scratch_pool));
+
+ if (kind == svn_node_dir)
+ eb.anchor_abspath = local_abspath;
+ else
+ eb.anchor_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
+
+ SVN_ERR(svn_wc__wrap_diff_callbacks(&processor,
+ callbacks, callback_baton, TRUE,
+ scratch_pool, scratch_pool));
+
+ if (use_git_diff_format)
+ show_copies_as_adds = TRUE;
+ if (show_copies_as_adds)
+ ignore_ancestry = FALSE;
+
+
+
+ /*
+ if (reverse_order)
+ processor = svn_diff__tree_processor_reverse_create(processor, NULL, pool);
+ */
+
+ if (! show_copies_as_adds && !use_git_diff_format)
+ processor = svn_diff__tree_processor_copy_as_changed_create(processor,
+ scratch_pool);
+
+ eb.db = wc_ctx->db;
+ eb.processor = processor;
+ eb.ignore_ancestry = ignore_ancestry;
+ eb.show_copies_as_adds = show_copies_as_adds;
+ eb.pool = scratch_pool;
+
+ if (changelist_filter && changelist_filter->nelts)
+ SVN_ERR(svn_hash_from_cstring_keys(&eb.changelist_hash, changelist_filter,
+ scratch_pool));
+
+ if (show_copies_as_adds || use_git_diff_format || !ignore_ancestry)
+ get_all = TRUE; /* We need unmodified descendants of copies */
+ else
+ get_all = FALSE;
+
+ /* Walk status handles files and directories */
+ SVN_ERR(svn_wc__internal_walk_status(wc_ctx->db, local_abspath, depth,
+ get_all,
+ TRUE /* no_ignore */,
+ FALSE /* ignore_text_mods */,
+ NULL /* ignore_patterns */,
+ diff_status_callback, &eb,
+ cancel_func, cancel_baton,
+ scratch_pool));
+
+ /* Close the remaining open directories */
+ while (eb.cur)
+ {
+ struct node_state_t *ns = eb.cur;
+
+ if (!ns->skip)
+ {
+ if (ns->propchanges)
+ SVN_ERR(processor->dir_changed(ns->relpath,
+ ns->left_src,
+ ns->right_src,
+ ns->left_props,
+ ns->right_props,
+ ns->propchanges,
+ ns->baton,
+ processor,
+ ns->pool));
+ else
+ SVN_ERR(processor->dir_closed(ns->relpath,
+ ns->left_src,
+ ns->right_src,
+ ns->baton,
+ processor,
+ ns->pool));
+ }
+ eb.cur = ns->parent;
+ svn_pool_clear(ns->pool);
+ }
+
+ return SVN_NO_ERROR;
+}
OpenPOWER on IntegriCloud