diff options
Diffstat (limited to 'subversion/libsvn_wc/diff_local.c')
-rw-r--r-- | subversion/libsvn_wc/diff_local.c | 541 |
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; +} |