1/*
2 * repos_diff_summarize.c -- The diff callbacks for summarizing
3 * the differences of two repository versions
4 *
5 * ====================================================================
6 *    Licensed to the Apache Software Foundation (ASF) under one
7 *    or more contributor license agreements.  See the NOTICE file
8 *    distributed with this work for additional information
9 *    regarding copyright ownership.  The ASF licenses this file
10 *    to you under the Apache License, Version 2.0 (the
11 *    "License"); you may not use this file except in compliance
12 *    with the License.  You may obtain a copy of the License at
13 *
14 *      http://www.apache.org/licenses/LICENSE-2.0
15 *
16 *    Unless required by applicable law or agreed to in writing,
17 *    software distributed under the License is distributed on an
18 *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
19 *    KIND, either express or implied.  See the License for the
20 *    specific language governing permissions and limitations
21 *    under the License.
22 * ====================================================================
23 */
24
25
26#include "svn_dirent_uri.h"
27#include "svn_hash.h"
28#include "svn_props.h"
29#include "svn_pools.h"
30
31#include "client.h"
32
33
34/* Diff callbacks baton.  */
35struct summarize_baton_t {
36  /* The target path of the diff, relative to the anchor; "" if target == anchor. */
37  const char *target;
38
39  /* The summarize callback passed down from the API */
40  svn_client_diff_summarize_func_t summarize_func;
41
42  /* Is the diff handling reversed? (add<->delete) */
43  svn_boolean_t reversed;
44
45  /* The summarize callback baton */
46  void *summarize_func_baton;
47
48  /* Which paths have a prop change. Key is a (const char *) path; the value
49   * is any non-null pointer to indicate that this path has a prop change. */
50  apr_hash_t *prop_changes;
51};
52
53
54/* Call B->summarize_func with B->summarize_func_baton, passing it a
55 * summary object composed from PATH (but made to be relative to the target
56 * of the diff), SUMMARIZE_KIND, PROP_CHANGED (or FALSE if the action is an
57 * add or delete) and NODE_KIND. */
58static svn_error_t *
59send_summary(struct summarize_baton_t *b,
60             const char *path,
61             svn_client_diff_summarize_kind_t summarize_kind,
62             svn_boolean_t prop_changed,
63             svn_node_kind_t node_kind,
64             apr_pool_t *scratch_pool)
65{
66  svn_client_diff_summarize_t *sum = apr_pcalloc(scratch_pool, sizeof(*sum));
67
68  SVN_ERR_ASSERT(summarize_kind != svn_client_diff_summarize_kind_normal
69                 || prop_changed);
70
71  if (b->reversed)
72    {
73      switch(summarize_kind)
74        {
75          case svn_client_diff_summarize_kind_added:
76            summarize_kind = svn_client_diff_summarize_kind_deleted;
77            break;
78          case svn_client_diff_summarize_kind_deleted:
79            summarize_kind = svn_client_diff_summarize_kind_added;
80            break;
81          default:
82            break;
83        }
84    }
85
86  /* PATH is relative to the anchor of the diff, but SUM->path needs to be
87     relative to the target of the diff. */
88  sum->path = svn_relpath_skip_ancestor(b->target, path);
89  sum->summarize_kind = summarize_kind;
90  if (summarize_kind == svn_client_diff_summarize_kind_modified
91      || summarize_kind == svn_client_diff_summarize_kind_normal)
92    sum->prop_changed = prop_changed;
93  sum->node_kind = node_kind;
94
95  SVN_ERR(b->summarize_func(sum, b->summarize_func_baton, scratch_pool));
96  return SVN_NO_ERROR;
97}
98
99/* Are there any changes to relevant (normal) props in PROPCHANGES? */
100static svn_boolean_t
101props_changed(const apr_array_header_t *propchanges,
102              apr_pool_t *scratch_pool)
103{
104  apr_array_header_t *props;
105
106  svn_error_clear(svn_categorize_props(propchanges, NULL, NULL, &props,
107                                       scratch_pool));
108  return (props->nelts != 0);
109}
110
111
112static svn_error_t *
113cb_dir_deleted(svn_wc_notify_state_t *state,
114               svn_boolean_t *tree_conflicted,
115               const char *path,
116               void *diff_baton,
117               apr_pool_t *scratch_pool)
118{
119  struct summarize_baton_t *b = diff_baton;
120
121  SVN_ERR(send_summary(b, path, svn_client_diff_summarize_kind_deleted,
122                       FALSE, svn_node_dir, scratch_pool));
123
124  return SVN_NO_ERROR;
125}
126
127static svn_error_t *
128cb_file_deleted(svn_wc_notify_state_t *state,
129                svn_boolean_t *tree_conflicted,
130                const char *path,
131                const char *tmpfile1,
132                const char *tmpfile2,
133                const char *mimetype1,
134                const char *mimetype2,
135                apr_hash_t *originalprops,
136                void *diff_baton,
137                apr_pool_t *scratch_pool)
138{
139  struct summarize_baton_t *b = diff_baton;
140
141  SVN_ERR(send_summary(b, path, svn_client_diff_summarize_kind_deleted,
142                       FALSE, svn_node_file, scratch_pool));
143
144  return SVN_NO_ERROR;
145}
146
147static svn_error_t *
148cb_dir_added(svn_wc_notify_state_t *state,
149             svn_boolean_t *tree_conflicted,
150             svn_boolean_t *skip,
151             svn_boolean_t *skip_children,
152             const char *path,
153             svn_revnum_t rev,
154             const char *copyfrom_path,
155             svn_revnum_t copyfrom_revision,
156             void *diff_baton,
157             apr_pool_t *scratch_pool)
158{
159  return SVN_NO_ERROR;
160}
161
162static svn_error_t *
163cb_dir_opened(svn_boolean_t *tree_conflicted,
164              svn_boolean_t *skip,
165              svn_boolean_t *skip_children,
166              const char *path,
167              svn_revnum_t rev,
168              void *diff_baton,
169              apr_pool_t *scratch_pool)
170{
171  return SVN_NO_ERROR;
172}
173
174static svn_error_t *
175cb_dir_closed(svn_wc_notify_state_t *contentstate,
176              svn_wc_notify_state_t *propstate,
177              svn_boolean_t *tree_conflicted,
178              const char *path,
179              svn_boolean_t dir_was_added,
180              void *diff_baton,
181              apr_pool_t *scratch_pool)
182{
183  struct summarize_baton_t *b = diff_baton;
184  svn_boolean_t prop_change;
185
186  if (! svn_relpath_skip_ancestor(b->target, path))
187    return SVN_NO_ERROR;
188
189  prop_change = svn_hash_gets(b->prop_changes, path) != NULL;
190  if (dir_was_added || prop_change)
191    SVN_ERR(send_summary(b, path,
192                         dir_was_added ? svn_client_diff_summarize_kind_added
193                                       : svn_client_diff_summarize_kind_normal,
194                         prop_change, svn_node_dir, scratch_pool));
195
196  return SVN_NO_ERROR;
197}
198
199static svn_error_t *
200cb_file_added(svn_wc_notify_state_t *contentstate,
201              svn_wc_notify_state_t *propstate,
202              svn_boolean_t *tree_conflicted,
203              const char *path,
204              const char *tmpfile1,
205              const char *tmpfile2,
206              svn_revnum_t rev1,
207              svn_revnum_t rev2,
208              const char *mimetype1,
209              const char *mimetype2,
210              const char *copyfrom_path,
211              svn_revnum_t copyfrom_revision,
212              const apr_array_header_t *propchanges,
213              apr_hash_t *originalprops,
214              void *diff_baton,
215              apr_pool_t *scratch_pool)
216{
217  struct summarize_baton_t *b = diff_baton;
218
219  SVN_ERR(send_summary(b, path, svn_client_diff_summarize_kind_added,
220                       props_changed(propchanges, scratch_pool),
221                       svn_node_file, scratch_pool));
222
223  return SVN_NO_ERROR;
224}
225
226static svn_error_t *
227cb_file_opened(svn_boolean_t *tree_conflicted,
228               svn_boolean_t *skip,
229               const char *path,
230               svn_revnum_t rev,
231               void *diff_baton,
232               apr_pool_t *scratch_pool)
233{
234  return SVN_NO_ERROR;
235}
236
237static svn_error_t *
238cb_file_changed(svn_wc_notify_state_t *contentstate,
239                svn_wc_notify_state_t *propstate,
240                svn_boolean_t *tree_conflicted,
241                const char *path,
242                const char *tmpfile1,
243                const char *tmpfile2,
244                svn_revnum_t rev1,
245                svn_revnum_t rev2,
246                const char *mimetype1,
247                const char *mimetype2,
248                const apr_array_header_t *propchanges,
249                apr_hash_t *originalprops,
250                void *diff_baton,
251                apr_pool_t *scratch_pool)
252{
253  struct summarize_baton_t *b = diff_baton;
254  svn_boolean_t text_change = (tmpfile2 != NULL);
255  svn_boolean_t prop_change = props_changed(propchanges, scratch_pool);
256
257  if (text_change || prop_change)
258    SVN_ERR(send_summary(b, path,
259                         text_change ? svn_client_diff_summarize_kind_modified
260                                     : svn_client_diff_summarize_kind_normal,
261                         prop_change, svn_node_file, scratch_pool));
262
263  return SVN_NO_ERROR;
264}
265
266static svn_error_t *
267cb_dir_props_changed(svn_wc_notify_state_t *propstate,
268                     svn_boolean_t *tree_conflicted,
269                     const char *path,
270                     svn_boolean_t dir_was_added,
271                     const apr_array_header_t *propchanges,
272                     apr_hash_t *original_props,
273                     void *diff_baton,
274                     apr_pool_t *scratch_pool)
275{
276  struct summarize_baton_t *b = diff_baton;
277
278  if (props_changed(propchanges, scratch_pool))
279    svn_hash_sets(b->prop_changes, path, path);
280
281  return SVN_NO_ERROR;
282}
283
284svn_error_t *
285svn_client__get_diff_summarize_callbacks(
286                        svn_wc_diff_callbacks4_t **callbacks,
287                        void **callback_baton,
288                        const char *target,
289                        svn_boolean_t reversed,
290                        svn_client_diff_summarize_func_t summarize_func,
291                        void *summarize_baton,
292                        apr_pool_t *pool)
293{
294  svn_wc_diff_callbacks4_t *cb = apr_palloc(pool, sizeof(*cb));
295  struct summarize_baton_t *b = apr_palloc(pool, sizeof(*b));
296
297  b->target = target;
298  b->summarize_func = summarize_func;
299  b->summarize_func_baton = summarize_baton;
300  b->prop_changes = apr_hash_make(pool);
301  b->reversed = reversed;
302
303  cb->file_opened = cb_file_opened;
304  cb->file_changed = cb_file_changed;
305  cb->file_added = cb_file_added;
306  cb->file_deleted = cb_file_deleted;
307  cb->dir_deleted = cb_dir_deleted;
308  cb->dir_opened = cb_dir_opened;
309  cb->dir_added = cb_dir_added;
310  cb->dir_props_changed = cb_dir_props_changed;
311  cb->dir_closed = cb_dir_closed;
312
313  *callbacks = cb;
314  *callback_baton = b;
315
316  return SVN_NO_ERROR;
317}
318