]> CyberLeo.Net >> Repos - FreeBSD/releng/10.3.git/blob - contrib/subversion/subversion/libsvn_wc/diff_local.c
- Copy stable/10@296371 to releng/10.3 in preparation for 10.3-RC1
[FreeBSD/releng/10.3.git] / contrib / subversion / subversion / libsvn_wc / diff_local.c
1 /*
2  * diff_pristine.c -- A simple diff walker which compares local files against
3  *                    their pristine 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  * This is the simple working copy diff algorithm which is used when you
25  * just use 'svn diff PATH'. It shows what is modified in your working copy
26  * since a node was checked out or copied but doesn't show most kinds of
27  * restructuring operations.
28  *
29  * You can look at this as another form of the status walker.
30  */
31
32 #include <apr_hash.h>
33
34 #include "svn_error.h"
35 #include "svn_pools.h"
36 #include "svn_dirent_uri.h"
37 #include "svn_path.h"
38 #include "svn_hash.h"
39
40 #include "private/svn_wc_private.h"
41 #include "private/svn_diff_tree.h"
42
43 #include "wc.h"
44 #include "props.h"
45 #include "translate.h"
46 #include "diff.h"
47
48 #include "svn_private_config.h"
49
50 /*-------------------------------------------------------------------------*/
51
52 /* Baton containing the state of a directory
53    reported open via a diff processor */
54 struct node_state_t
55 {
56   struct node_state_t *parent;
57
58   apr_pool_t *pool;
59
60   const char *local_abspath;
61   const char *relpath;
62   void *baton;
63
64   svn_diff_source_t *left_src;
65   svn_diff_source_t *right_src;
66   svn_diff_source_t *copy_src;
67
68   svn_boolean_t skip;
69   svn_boolean_t skip_children;
70
71   apr_hash_t *left_props;
72   apr_hash_t *right_props;
73   const apr_array_header_t *propchanges;
74 };
75 \f
76 /* The diff baton */
77 struct diff_baton
78 {
79   /* A wc db. */
80   svn_wc__db_t *db;
81
82   /* Report editor paths relative from this directory */
83   const char *anchor_abspath;
84
85   struct node_state_t *cur;
86
87   const svn_diff_tree_processor_t *processor;
88
89   /* Should this diff ignore node ancestry? */
90   svn_boolean_t ignore_ancestry;
91
92   /* Should this diff not compare copied files with their source? */
93   svn_boolean_t show_copies_as_adds;
94
95   /* Cancel function/baton */
96   svn_cancel_func_t cancel_func;
97   void *cancel_baton;
98
99   apr_pool_t *pool;
100 };
101
102 /* Recursively opens directories on the stack in EB, until LOCAL_ABSPATH
103    is reached. If RECURSIVE_SKIP is TRUE, don't open LOCAL_ABSPATH itself,
104    but create it marked with skip+skip_children.
105  */
106 static svn_error_t *
107 ensure_state(struct diff_baton *eb,
108              const char *local_abspath,
109              svn_boolean_t recursive_skip,
110              apr_pool_t *scratch_pool)
111 {
112   struct node_state_t *ns;
113   apr_pool_t *ns_pool;
114   if (!eb->cur)
115     {
116       const char *relpath;
117
118       relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, local_abspath);
119       if (! relpath)
120         return SVN_NO_ERROR;
121
122       /* Don't recurse on the anchor, as that might loop infinately because
123             svn_dirent_dirname("/",...)   -> "/"
124             svn_dirent_dirname("C:/",...) -> "C:/" (Windows) */
125       if (*relpath)
126         SVN_ERR(ensure_state(eb,
127                              svn_dirent_dirname(local_abspath,scratch_pool),
128                              FALSE,
129                              scratch_pool));
130     }
131   else if (svn_dirent_is_child(eb->cur->local_abspath, local_abspath, NULL))
132     SVN_ERR(ensure_state(eb, svn_dirent_dirname(local_abspath,scratch_pool),
133                          FALSE,
134                          scratch_pool));
135   else
136     return SVN_NO_ERROR;
137
138   if (eb->cur && eb->cur->skip_children)
139     return SVN_NO_ERROR;
140
141   ns_pool = svn_pool_create(eb->cur ? eb->cur->pool : eb->pool);
142   ns = apr_pcalloc(ns_pool, sizeof(*ns));
143
144   ns->pool = ns_pool;
145   ns->local_abspath = apr_pstrdup(ns_pool, local_abspath);
146   ns->relpath = svn_dirent_skip_ancestor(eb->anchor_abspath, ns->local_abspath);
147   ns->parent = eb->cur;
148   eb->cur = ns;
149
150   if (recursive_skip)
151     {
152       ns->skip = TRUE;
153       ns->skip_children = TRUE;
154       return SVN_NO_ERROR;
155     }
156
157   {
158     svn_revnum_t revision;
159     svn_error_t *err;
160
161     err = svn_wc__db_base_get_info(NULL, NULL, &revision, NULL, NULL, NULL,
162                                    NULL, NULL, NULL, NULL, NULL, NULL, NULL,
163                                    NULL, NULL, NULL,
164                                    eb->db, local_abspath,
165                                    scratch_pool, scratch_pool);
166
167     if (err)
168       {
169         if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
170           return svn_error_trace(err);
171         svn_error_clear(err);
172
173         revision = 0; /* Use original revision? */
174       }
175     ns->left_src = svn_diff__source_create(revision, ns->pool);
176     ns->right_src = svn_diff__source_create(SVN_INVALID_REVNUM, ns->pool);
177
178     SVN_ERR(eb->processor->dir_opened(&ns->baton, &ns->skip,
179                                       &ns->skip_children,
180                                       ns->relpath,
181                                       ns->left_src,
182                                       ns->right_src,
183                                       NULL /* copyfrom_source */,
184                                       ns->parent ? ns->parent->baton : NULL,
185                                       eb->processor,
186                                       ns->pool, scratch_pool));
187   }
188
189   return SVN_NO_ERROR;
190 }
191
192 /* Implements svn_wc_status_func3_t */
193 static svn_error_t *
194 diff_status_callback(void *baton,
195                      const char *local_abspath,
196                      const svn_wc_status3_t *status,
197                      apr_pool_t *scratch_pool)
198 {
199   struct diff_baton *eb = baton;
200   svn_wc__db_t *db = eb->db;
201
202   if (! status->versioned)
203     return SVN_NO_ERROR; /* unversioned (includes dir externals) */
204
205   if (status->node_status == svn_wc_status_conflicted
206       && status->text_status == svn_wc_status_none
207       && status->prop_status == svn_wc_status_none)
208     {
209       /* Node is an actual only node describing a tree conflict */
210       return SVN_NO_ERROR;
211     }
212
213   /* Not text/prop modified, not copied. Easy out */
214   if (status->node_status == svn_wc_status_normal && !status->copied)
215     return SVN_NO_ERROR;
216
217   /* Mark all directories where we are no longer inside as closed */
218   while (eb->cur
219          && !svn_dirent_is_ancestor(eb->cur->local_abspath, local_abspath))
220     {
221       struct node_state_t *ns = eb->cur;
222
223       if (!ns->skip)
224         {
225           if (ns->propchanges)
226             SVN_ERR(eb->processor->dir_changed(ns->relpath,
227                                                ns->left_src,
228                                                ns->right_src,
229                                                ns->left_props,
230                                                ns->right_props,
231                                                ns->propchanges,
232                                                ns->baton,
233                                                eb->processor,
234                                                ns->pool));
235           else
236             SVN_ERR(eb->processor->dir_closed(ns->relpath,
237                                               ns->left_src,
238                                               ns->right_src,
239                                               ns->baton,
240                                               eb->processor,
241                                               ns->pool));
242         }
243       eb->cur = ns->parent;
244       svn_pool_clear(ns->pool);
245     }
246   SVN_ERR(ensure_state(eb, svn_dirent_dirname(local_abspath, scratch_pool),
247                        FALSE, scratch_pool));
248
249   if (eb->cur && eb->cur->skip_children)
250     return SVN_NO_ERROR;
251
252   /* This code does about the same thing as the inner body of
253      walk_local_nodes_diff() in diff_editor.c, except that
254      it is already filtered by the status walker, doesn't have to
255      account for remote changes (and many tiny other details) */
256
257   {
258     svn_boolean_t repos_only;
259     svn_boolean_t local_only;
260     svn_wc__db_status_t db_status;
261     svn_boolean_t have_base;
262     svn_node_kind_t base_kind;
263     svn_node_kind_t db_kind = status->kind;
264     svn_depth_t depth_below_here = svn_depth_unknown;
265
266     const char *child_abspath = local_abspath;
267     const char *child_relpath = svn_dirent_skip_ancestor(eb->anchor_abspath,
268                                                          local_abspath);
269
270
271     repos_only = FALSE;
272     local_only = FALSE;
273
274     /* ### optimize away this call using status info. Should
275            be possible in almost every case (except conflict, missing, obst.)*/
276     SVN_ERR(svn_wc__db_read_info(&db_status, NULL, NULL, NULL, NULL, NULL,
277                                  NULL, NULL, NULL, NULL, NULL, NULL, NULL,
278                                  NULL, NULL, NULL, NULL, NULL, NULL, NULL,
279                                  NULL, NULL, NULL, NULL,
280                                  &have_base, NULL, NULL,
281                                  eb->db, local_abspath,
282                                  scratch_pool, scratch_pool));
283     if (!have_base)
284       {
285         local_only = TRUE; /* Only report additions */
286       }
287     else if (db_status == svn_wc__db_status_normal)
288       {
289         /* Simple diff */
290         base_kind = db_kind;
291       }
292     else if (db_status == svn_wc__db_status_deleted)
293       {
294         svn_wc__db_status_t base_status;
295         repos_only = TRUE;
296         SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL,
297                                          NULL, NULL, NULL, NULL, NULL,
298                                          NULL, NULL, NULL, NULL, NULL,
299                                          NULL, NULL, NULL,
300                                          eb->db, local_abspath,
301                                          scratch_pool, scratch_pool));
302
303         if (base_status != svn_wc__db_status_normal)
304           return SVN_NO_ERROR;
305       }
306     else
307       {
308         /* working status is either added or deleted */
309         svn_wc__db_status_t base_status;
310
311         SVN_ERR(svn_wc__db_base_get_info(&base_status, &base_kind, NULL,
312                                          NULL, NULL, NULL, NULL, NULL,
313                                          NULL, NULL, NULL, NULL, NULL,
314                                          NULL, NULL, NULL,
315                                          eb->db, local_abspath,
316                                          scratch_pool, scratch_pool));
317
318         if (base_status != svn_wc__db_status_normal)
319           local_only = TRUE;
320         else if (base_kind != db_kind || !eb->ignore_ancestry)
321           {
322             repos_only = TRUE;
323             local_only = TRUE;
324           }
325       }
326
327     if (repos_only)
328       {
329         /* Report repository form deleted */
330         if (base_kind == svn_node_file)
331           SVN_ERR(svn_wc__diff_base_only_file(db, child_abspath,
332                                               child_relpath,
333                                               SVN_INVALID_REVNUM,
334                                               eb->processor,
335                                               eb->cur ? eb->cur->baton : NULL,
336                                               scratch_pool));
337         else if (base_kind == svn_node_dir)
338           SVN_ERR(svn_wc__diff_base_only_dir(db, child_abspath,
339                                              child_relpath,
340                                              SVN_INVALID_REVNUM,
341                                              depth_below_here,
342                                              eb->processor,
343                                              eb->cur ? eb->cur->baton : NULL,
344                                              eb->cancel_func,
345                                              eb->cancel_baton,
346                                              scratch_pool));
347       }
348     else if (!local_only)
349       {
350         /* Diff base against actual */
351         if (db_kind == svn_node_file)
352           {
353             SVN_ERR(svn_wc__diff_base_working_diff(db, child_abspath,
354                                                    child_relpath,
355                                                    SVN_INVALID_REVNUM,
356                                                    eb->processor,
357                                                    eb->cur
358                                                         ? eb->cur->baton
359                                                         : NULL,
360                                                    FALSE,
361                                                    eb->cancel_func,
362                                                    eb->cancel_baton,
363                                                    scratch_pool));
364           }
365         else if (db_kind == svn_node_dir)
366           {
367             SVN_ERR(ensure_state(eb, local_abspath, FALSE, scratch_pool));
368
369             if (status->prop_status != svn_wc_status_none
370                 && status->prop_status != svn_wc_status_normal)
371               {
372                 apr_array_header_t *propchanges;
373                 SVN_ERR(svn_wc__db_base_get_props(&eb->cur->left_props,
374                                                   eb->db, local_abspath,
375                                                   eb->cur->pool,
376                                                   scratch_pool));
377                 SVN_ERR(svn_wc__db_read_props(&eb->cur->right_props,
378                                               eb->db, local_abspath,
379                                               eb->cur->pool,
380                                               scratch_pool));
381
382                 SVN_ERR(svn_prop_diffs(&propchanges,
383                                        eb->cur->right_props,
384                                        eb->cur->left_props,
385                                        eb->cur->pool));
386
387                 eb->cur->propchanges = propchanges;
388               }
389           }
390       }
391
392     if (local_only && (db_status != svn_wc__db_status_deleted))
393       {
394         if (db_kind == svn_node_file)
395           SVN_ERR(svn_wc__diff_local_only_file(db, child_abspath,
396                                                child_relpath,
397                                                eb->processor,
398                                                eb->cur ? eb->cur->baton : NULL,
399                                                FALSE,
400                                                eb->cancel_func,
401                                                eb->cancel_baton,
402                                                scratch_pool));
403         else if (db_kind == svn_node_dir)
404           SVN_ERR(svn_wc__diff_local_only_dir(db, child_abspath,
405                                               child_relpath, depth_below_here,
406                                               eb->processor,
407                                               eb->cur ? eb->cur->baton : NULL,
408                                               FALSE,
409                                               eb->cancel_func,
410                                               eb->cancel_baton,
411                                               scratch_pool));
412       }
413
414     if (db_kind == svn_node_dir && (local_only || repos_only))
415       SVN_ERR(ensure_state(eb, local_abspath, TRUE /* skip */, scratch_pool));
416   }
417
418   return SVN_NO_ERROR;
419 }
420
421
422 /* Public Interface */
423 svn_error_t *
424 svn_wc_diff6(svn_wc_context_t *wc_ctx,
425              const char *local_abspath,
426              const svn_wc_diff_callbacks4_t *callbacks,
427              void *callback_baton,
428              svn_depth_t depth,
429              svn_boolean_t ignore_ancestry,
430              svn_boolean_t show_copies_as_adds,
431              svn_boolean_t use_git_diff_format,
432              const apr_array_header_t *changelist_filter,
433              svn_cancel_func_t cancel_func,
434              void *cancel_baton,
435              apr_pool_t *scratch_pool)
436 {
437   struct diff_baton eb = { 0 };
438   svn_node_kind_t kind;
439   svn_boolean_t get_all;
440   const svn_diff_tree_processor_t *processor;
441
442   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
443   SVN_ERR(svn_wc__db_read_kind(&kind, wc_ctx->db, local_abspath,
444                                FALSE /* allow_missing */,
445                                TRUE /* show_deleted */,
446                                FALSE /* show_hidden */,
447                                scratch_pool));
448
449   if (kind == svn_node_dir)
450     eb.anchor_abspath = local_abspath;
451   else
452     eb.anchor_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
453
454   SVN_ERR(svn_wc__wrap_diff_callbacks(&processor,
455                                       callbacks, callback_baton, TRUE,
456                                       scratch_pool, scratch_pool));
457
458   if (use_git_diff_format)
459     show_copies_as_adds = TRUE;
460   if (show_copies_as_adds)
461     ignore_ancestry = FALSE;
462
463
464
465   /*
466   if (reverse_order)
467     processor = svn_diff__tree_processor_reverse_create(processor, NULL, pool);
468    */
469
470   if (! show_copies_as_adds && !use_git_diff_format)
471     processor = svn_diff__tree_processor_copy_as_changed_create(processor,
472                                                                 scratch_pool);
473
474   /* Apply changelist filtering to the output */
475   if (changelist_filter && changelist_filter->nelts)
476     {
477       apr_hash_t *changelist_hash;
478
479       SVN_ERR(svn_hash_from_cstring_keys(&changelist_hash, changelist_filter,
480                                          scratch_pool));
481       processor = svn_wc__changelist_filter_tree_processor_create(
482                     processor, wc_ctx, local_abspath,
483                     changelist_hash, scratch_pool);
484     }
485
486   eb.db = wc_ctx->db;
487   eb.processor = processor;
488   eb.ignore_ancestry = ignore_ancestry;
489   eb.show_copies_as_adds = show_copies_as_adds;
490   eb.pool = scratch_pool;
491
492   if (show_copies_as_adds || use_git_diff_format || !ignore_ancestry)
493     get_all = TRUE; /* We need unmodified descendants of copies */
494   else
495     get_all = FALSE;
496
497   /* Walk status handles files and directories */
498   SVN_ERR(svn_wc__internal_walk_status(wc_ctx->db, local_abspath, depth,
499                                        get_all,
500                                        TRUE /* no_ignore */,
501                                        FALSE /* ignore_text_mods */,
502                                        NULL /* ignore_patterns */,
503                                        diff_status_callback, &eb,
504                                        cancel_func, cancel_baton,
505                                        scratch_pool));
506
507   /* Close the remaining open directories */
508   while (eb.cur)
509     {
510       struct node_state_t *ns = eb.cur;
511
512       if (!ns->skip)
513         {
514           if (ns->propchanges)
515             SVN_ERR(processor->dir_changed(ns->relpath,
516                                            ns->left_src,
517                                            ns->right_src,
518                                            ns->left_props,
519                                            ns->right_props,
520                                            ns->propchanges,
521                                            ns->baton,
522                                            processor,
523                                            ns->pool));
524           else
525             SVN_ERR(processor->dir_closed(ns->relpath,
526                                           ns->left_src,
527                                           ns->right_src,
528                                           ns->baton,
529                                           processor,
530                                           ns->pool));
531         }
532       eb.cur = ns->parent;
533       svn_pool_clear(ns->pool);
534     }
535
536   return SVN_NO_ERROR;
537 }