/* * repos_diff_summarize.c -- The diff callbacks for summarizing * the differences of two repository 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. * ==================================================================== */ #include "svn_dirent_uri.h" #include "svn_hash.h" #include "svn_props.h" #include "svn_pools.h" #include "client.h" /* Diff callbacks baton. */ struct summarize_baton_t { /* The target path of the diff, relative to the anchor; "" if target == anchor. */ const char *target; /* The summarize callback passed down from the API */ svn_client_diff_summarize_func_t summarize_func; /* Is the diff handling reversed? (add<->delete) */ svn_boolean_t reversed; /* The summarize callback baton */ void *summarize_func_baton; /* Which paths have a prop change. Key is a (const char *) path; the value * is any non-null pointer to indicate that this path has a prop change. */ apr_hash_t *prop_changes; }; /* Call B->summarize_func with B->summarize_func_baton, passing it a * summary object composed from PATH (but made to be relative to the target * of the diff), SUMMARIZE_KIND, PROP_CHANGED (or FALSE if the action is an * add or delete) and NODE_KIND. */ static svn_error_t * send_summary(struct summarize_baton_t *b, const char *path, svn_client_diff_summarize_kind_t summarize_kind, svn_boolean_t prop_changed, svn_node_kind_t node_kind, apr_pool_t *scratch_pool) { svn_client_diff_summarize_t *sum = apr_pcalloc(scratch_pool, sizeof(*sum)); SVN_ERR_ASSERT(summarize_kind != svn_client_diff_summarize_kind_normal || prop_changed); if (b->reversed) { switch(summarize_kind) { case svn_client_diff_summarize_kind_added: summarize_kind = svn_client_diff_summarize_kind_deleted; break; case svn_client_diff_summarize_kind_deleted: summarize_kind = svn_client_diff_summarize_kind_added; break; default: break; } } /* PATH is relative to the anchor of the diff, but SUM->path needs to be relative to the target of the diff. */ sum->path = svn_relpath_skip_ancestor(b->target, path); sum->summarize_kind = summarize_kind; if (summarize_kind == svn_client_diff_summarize_kind_modified || summarize_kind == svn_client_diff_summarize_kind_normal) sum->prop_changed = prop_changed; sum->node_kind = node_kind; SVN_ERR(b->summarize_func(sum, b->summarize_func_baton, scratch_pool)); return SVN_NO_ERROR; } /* Are there any changes to relevant (normal) props in PROPCHANGES? */ static svn_boolean_t props_changed(const apr_array_header_t *propchanges, apr_pool_t *scratch_pool) { apr_array_header_t *props; svn_error_clear(svn_categorize_props(propchanges, NULL, NULL, &props, scratch_pool)); return (props->nelts != 0); } static svn_error_t * cb_dir_deleted(svn_wc_notify_state_t *state, svn_boolean_t *tree_conflicted, const char *path, void *diff_baton, apr_pool_t *scratch_pool) { struct summarize_baton_t *b = diff_baton; SVN_ERR(send_summary(b, path, svn_client_diff_summarize_kind_deleted, FALSE, svn_node_dir, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * cb_file_deleted(svn_wc_notify_state_t *state, svn_boolean_t *tree_conflicted, const char *path, const char *tmpfile1, const char *tmpfile2, const char *mimetype1, const char *mimetype2, apr_hash_t *originalprops, void *diff_baton, apr_pool_t *scratch_pool) { struct summarize_baton_t *b = diff_baton; SVN_ERR(send_summary(b, path, svn_client_diff_summarize_kind_deleted, FALSE, svn_node_file, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * cb_dir_added(svn_wc_notify_state_t *state, svn_boolean_t *tree_conflicted, svn_boolean_t *skip, svn_boolean_t *skip_children, const char *path, svn_revnum_t rev, const char *copyfrom_path, svn_revnum_t copyfrom_revision, void *diff_baton, apr_pool_t *scratch_pool) { return SVN_NO_ERROR; } static svn_error_t * cb_dir_opened(svn_boolean_t *tree_conflicted, svn_boolean_t *skip, svn_boolean_t *skip_children, const char *path, svn_revnum_t rev, void *diff_baton, apr_pool_t *scratch_pool) { return SVN_NO_ERROR; } static svn_error_t * cb_dir_closed(svn_wc_notify_state_t *contentstate, svn_wc_notify_state_t *propstate, svn_boolean_t *tree_conflicted, const char *path, svn_boolean_t dir_was_added, void *diff_baton, apr_pool_t *scratch_pool) { struct summarize_baton_t *b = diff_baton; svn_boolean_t prop_change; if (! svn_relpath_skip_ancestor(b->target, path)) return SVN_NO_ERROR; prop_change = svn_hash_gets(b->prop_changes, path) != NULL; if (dir_was_added || prop_change) SVN_ERR(send_summary(b, path, dir_was_added ? svn_client_diff_summarize_kind_added : svn_client_diff_summarize_kind_normal, prop_change, svn_node_dir, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * cb_file_added(svn_wc_notify_state_t *contentstate, svn_wc_notify_state_t *propstate, svn_boolean_t *tree_conflicted, const char *path, const char *tmpfile1, const char *tmpfile2, svn_revnum_t rev1, svn_revnum_t rev2, const char *mimetype1, const char *mimetype2, const char *copyfrom_path, svn_revnum_t copyfrom_revision, const apr_array_header_t *propchanges, apr_hash_t *originalprops, void *diff_baton, apr_pool_t *scratch_pool) { struct summarize_baton_t *b = diff_baton; SVN_ERR(send_summary(b, path, svn_client_diff_summarize_kind_added, props_changed(propchanges, scratch_pool), svn_node_file, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * cb_file_opened(svn_boolean_t *tree_conflicted, svn_boolean_t *skip, const char *path, svn_revnum_t rev, void *diff_baton, apr_pool_t *scratch_pool) { return SVN_NO_ERROR; } static svn_error_t * cb_file_changed(svn_wc_notify_state_t *contentstate, svn_wc_notify_state_t *propstate, svn_boolean_t *tree_conflicted, const char *path, const char *tmpfile1, const char *tmpfile2, svn_revnum_t rev1, svn_revnum_t rev2, const char *mimetype1, const char *mimetype2, const apr_array_header_t *propchanges, apr_hash_t *originalprops, void *diff_baton, apr_pool_t *scratch_pool) { struct summarize_baton_t *b = diff_baton; svn_boolean_t text_change = (tmpfile2 != NULL); svn_boolean_t prop_change = props_changed(propchanges, scratch_pool); if (text_change || prop_change) SVN_ERR(send_summary(b, path, text_change ? svn_client_diff_summarize_kind_modified : svn_client_diff_summarize_kind_normal, prop_change, svn_node_file, scratch_pool)); return SVN_NO_ERROR; } static svn_error_t * cb_dir_props_changed(svn_wc_notify_state_t *propstate, svn_boolean_t *tree_conflicted, const char *path, svn_boolean_t dir_was_added, const apr_array_header_t *propchanges, apr_hash_t *original_props, void *diff_baton, apr_pool_t *scratch_pool) { struct summarize_baton_t *b = diff_baton; if (props_changed(propchanges, scratch_pool)) svn_hash_sets(b->prop_changes, path, path); return SVN_NO_ERROR; } svn_error_t * svn_client__get_diff_summarize_callbacks( svn_wc_diff_callbacks4_t **callbacks, void **callback_baton, const char *target, svn_boolean_t reversed, svn_client_diff_summarize_func_t summarize_func, void *summarize_baton, apr_pool_t *pool) { svn_wc_diff_callbacks4_t *cb = apr_palloc(pool, sizeof(*cb)); struct summarize_baton_t *b = apr_palloc(pool, sizeof(*b)); b->target = target; b->summarize_func = summarize_func; b->summarize_func_baton = summarize_baton; b->prop_changes = apr_hash_make(pool); b->reversed = reversed; cb->file_opened = cb_file_opened; cb->file_changed = cb_file_changed; cb->file_added = cb_file_added; cb->file_deleted = cb_file_deleted; cb->dir_deleted = cb_dir_deleted; cb->dir_opened = cb_dir_opened; cb->dir_added = cb_dir_added; cb->dir_props_changed = cb_dir_props_changed; cb->dir_closed = cb_dir_closed; *callbacks = cb; *callback_baton = b; return SVN_NO_ERROR; }