]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/subversion/subversion/libsvn_wc/questions.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / subversion / subversion / libsvn_wc / questions.c
1 /*
2  * questions.c:  routines for asking questions about working copies
3  *
4  * ====================================================================
5  *    Licensed to the Apache Software Foundation (ASF) under one
6  *    or more contributor license agreements.  See the NOTICE file
7  *    distributed with this work for additional information
8  *    regarding copyright ownership.  The ASF licenses this file
9  *    to you under the Apache License, Version 2.0 (the
10  *    "License"); you may not use this file except in compliance
11  *    with the License.  You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  *    Unless required by applicable law or agreed to in writing,
16  *    software distributed under the License is distributed on an
17  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18  *    KIND, either express or implied.  See the License for the
19  *    specific language governing permissions and limitations
20  *    under the License.
21  * ====================================================================
22  */
23
24
25 \f
26 #include <string.h>
27
28 #include <apr_pools.h>
29 #include <apr_file_io.h>
30 #include <apr_file_info.h>
31 #include <apr_time.h>
32
33 #include "svn_pools.h"
34 #include "svn_types.h"
35 #include "svn_string.h"
36 #include "svn_error.h"
37 #include "svn_dirent_uri.h"
38 #include "svn_time.h"
39 #include "svn_io.h"
40 #include "svn_props.h"
41
42 #include "wc.h"
43 #include "conflicts.h"
44 #include "translate.h"
45 #include "wc_db.h"
46
47 #include "svn_private_config.h"
48 #include "private/svn_wc_private.h"
49
50 \f
51
52 /*** svn_wc_text_modified_p ***/
53
54 /* svn_wc_text_modified_p answers the question:
55
56    "Are the contents of F different than the contents of
57    .svn/text-base/F.svn-base or .svn/tmp/text-base/F.svn-base?"
58
59    In the first case, we're looking to see if a user has made local
60    modifications to a file since the last update or commit.  In the
61    second, the file may not be versioned yet (it doesn't exist in
62    entries).  Support for the latter case came about to facilitate
63    forced checkouts, updates, and switches, where an unversioned file
64    may obstruct a file about to be added.
65
66    Note: Assuming that F lives in a directory D at revision V, please
67    notice that we are *NOT* answering the question, "are the contents
68    of F different than revision V of F?"  While F may be at a different
69    revision number than its parent directory, but we're only looking
70    for local edits on F, not for consistent directory revisions.
71
72    TODO:  the logic of the routines on this page might change in the
73    future, as they bear some relation to the user interface.  For
74    example, if a file is removed -- without telling subversion about
75    it -- how should subversion react?  Should it copy the file back
76    out of text-base?  Should it ask whether one meant to officially
77    mark it for removal?
78 */
79
80
81 /* Set *MODIFIED_P to TRUE if (after translation) VERSIONED_FILE_ABSPATH
82  * (of VERSIONED_FILE_SIZE bytes) differs from PRISTINE_STREAM (of
83  * PRISTINE_SIZE bytes), else to FALSE if not.
84  *
85  * If EXACT_COMPARISON is FALSE, translate VERSIONED_FILE_ABSPATH's EOL
86  * style and keywords to repository-normal form according to its properties,
87  * and compare the result with PRISTINE_STREAM.  If EXACT_COMPARISON is
88  * TRUE, translate PRISTINE_STREAM's EOL style and keywords to working-copy
89  * form according to VERSIONED_FILE_ABSPATH's properties, and compare the
90  * result with VERSIONED_FILE_ABSPATH.
91  *
92  * HAS_PROPS should be TRUE if the file had properties when it was not
93  * modified, otherwise FALSE.
94  *
95  * PROPS_MOD should be TRUE if the file's properties have been changed,
96  * otherwise FALSE.
97  *
98  * PRISTINE_STREAM will be closed before a successful return.
99  *
100  * DB is a wc_db; use SCRATCH_POOL for temporary allocation.
101  */
102 static svn_error_t *
103 compare_and_verify(svn_boolean_t *modified_p,
104                    svn_wc__db_t *db,
105                    const char *versioned_file_abspath,
106                    svn_filesize_t versioned_file_size,
107                    svn_stream_t *pristine_stream,
108                    svn_filesize_t pristine_size,
109                    svn_boolean_t has_props,
110                    svn_boolean_t props_mod,
111                    svn_boolean_t exact_comparison,
112                    apr_pool_t *scratch_pool)
113 {
114   svn_boolean_t same;
115   svn_subst_eol_style_t eol_style;
116   const char *eol_str;
117   apr_hash_t *keywords;
118   svn_boolean_t special = FALSE;
119   svn_boolean_t need_translation;
120
121   SVN_ERR_ASSERT(svn_dirent_is_absolute(versioned_file_abspath));
122
123   if (props_mod)
124     has_props = TRUE; /* Maybe it didn't have properties; but it has now */
125
126   if (has_props)
127     {
128       SVN_ERR(svn_wc__get_translate_info(&eol_style, &eol_str,
129                                          &keywords,
130                                          &special,
131                                          db, versioned_file_abspath, NULL,
132                                          !exact_comparison,
133                                          scratch_pool, scratch_pool));
134
135       need_translation = svn_subst_translation_required(eol_style, eol_str,
136                                                         keywords, special,
137                                                         TRUE);
138     }
139   else
140     need_translation = FALSE;
141
142   if (! need_translation
143       && (versioned_file_size != pristine_size))
144     {
145       *modified_p = TRUE;
146
147       /* ### Why did we open the pristine? */
148       return svn_error_trace(svn_stream_close(pristine_stream));
149     }
150
151   /* ### Other checks possible? */
152
153   if (need_translation)
154     {
155       /* Reading files is necessary. */
156       svn_stream_t *v_stream;  /* versioned_file */
157
158       if (special)
159         {
160           SVN_ERR(svn_subst_read_specialfile(&v_stream, versioned_file_abspath,
161                                              scratch_pool, scratch_pool));
162         }
163       else
164         {
165           SVN_ERR(svn_stream_open_readonly(&v_stream, versioned_file_abspath,
166                                            scratch_pool, scratch_pool));
167
168           if (!exact_comparison && need_translation)
169             {
170               if (eol_style == svn_subst_eol_style_native)
171                 eol_str = SVN_SUBST_NATIVE_EOL_STR;
172               else if (eol_style != svn_subst_eol_style_fixed
173                        && eol_style != svn_subst_eol_style_none)
174                 return svn_error_create(SVN_ERR_IO_UNKNOWN_EOL,
175                                         svn_stream_close(v_stream), NULL);
176
177               /* Wrap file stream to detranslate into normal form,
178                * "repairing" the EOL style if it is inconsistent. */
179               v_stream = svn_subst_stream_translated(v_stream,
180                                                      eol_str,
181                                                      TRUE /* repair */,
182                                                      keywords,
183                                                      FALSE /* expand */,
184                                                      scratch_pool);
185             }
186           else if (need_translation)
187             {
188               /* Wrap base stream to translate into working copy form, and
189                * arrange to throw an error if its EOL style is inconsistent. */
190               pristine_stream = svn_subst_stream_translated(pristine_stream,
191                                                             eol_str, FALSE,
192                                                             keywords, TRUE,
193                                                             scratch_pool);
194             }
195         }
196
197       SVN_ERR(svn_stream_contents_same2(&same, pristine_stream, v_stream,
198                                         scratch_pool));
199     }
200   else
201     {
202       /* Translation would be a no-op, so compare the original file. */
203       svn_stream_t *v_stream;  /* versioned_file */
204
205       SVN_ERR(svn_stream_open_readonly(&v_stream, versioned_file_abspath,
206                                        scratch_pool, scratch_pool));
207
208       SVN_ERR(svn_stream_contents_same2(&same, pristine_stream, v_stream,
209                                         scratch_pool));
210     }
211
212   *modified_p = (! same);
213
214   return SVN_NO_ERROR;
215 }
216
217 svn_error_t *
218 svn_wc__internal_file_modified_p(svn_boolean_t *modified_p,
219                                  svn_wc__db_t *db,
220                                  const char *local_abspath,
221                                  svn_boolean_t exact_comparison,
222                                  apr_pool_t *scratch_pool)
223 {
224   svn_stream_t *pristine_stream;
225   svn_filesize_t pristine_size;
226   svn_wc__db_status_t status;
227   svn_node_kind_t kind;
228   const svn_checksum_t *checksum;
229   svn_filesize_t recorded_size;
230   apr_time_t recorded_mod_time;
231   svn_boolean_t has_props;
232   svn_boolean_t props_mod;
233   const svn_io_dirent2_t *dirent;
234
235   /* Read the relevant info */
236   SVN_ERR(svn_wc__db_read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL,
237                                NULL, NULL, NULL, &checksum, NULL, NULL, NULL,
238                                NULL, NULL, NULL,
239                                &recorded_size, &recorded_mod_time,
240                                NULL, NULL, NULL, &has_props, &props_mod,
241                                NULL, NULL, NULL,
242                                db, local_abspath,
243                                scratch_pool, scratch_pool));
244
245   /* If we don't have a pristine or the node has a status that allows a
246      pristine, just say that the node is modified */
247   if (!checksum
248       || (kind != svn_node_file)
249       || ((status != svn_wc__db_status_normal)
250           && (status != svn_wc__db_status_added)))
251     {
252       *modified_p = TRUE;
253       return SVN_NO_ERROR;
254     }
255
256   SVN_ERR(svn_io_stat_dirent2(&dirent, local_abspath, FALSE, TRUE,
257                               scratch_pool, scratch_pool));
258
259   if (dirent->kind != svn_node_file)
260     {
261       /* There is no file on disk, so the text is missing, not modified. */
262       *modified_p = FALSE;
263       return SVN_NO_ERROR;
264     }
265
266   if (! exact_comparison)
267     {
268       /* We're allowed to use a heuristic to determine whether files may
269          have changed.  The heuristic has these steps:
270
271          1. Compare the working file's size
272             with the size cached in the entries file
273          2. If they differ, do a full file compare
274          3. Compare the working file's timestamp
275             with the timestamp cached in the entries file
276          4. If they differ, do a full file compare
277          5. Otherwise, return indicating an unchanged file.
278
279          There are 2 problematic situations which may occur:
280
281          1. The cached working size is missing
282          --> In this case, we forget we ever tried to compare
283              and skip to the timestamp comparison.  This is
284              because old working copies do not contain cached sizes
285
286          2. The cached timestamp is missing
287          --> In this case, we forget we ever tried to compare
288              and skip to full file comparison.  This is because
289              the timestamp will be removed when the library
290              updates a locally changed file.  (ie, this only happens
291              when the file was locally modified.)
292
293       */
294
295       /* Compare the sizes, if applicable */
296       if (recorded_size != SVN_INVALID_FILESIZE
297           && dirent->filesize != recorded_size)
298         goto compare_them;
299
300       /* Compare the timestamps
301
302          Note: recorded_mod_time == 0 means not available,
303                which also means the timestamps won't be equal,
304                so there's no need to explicitly check the 'absent' value. */
305       if (recorded_mod_time != dirent->mtime)
306         goto compare_them;
307
308       *modified_p = FALSE;
309       return SVN_NO_ERROR;
310     }
311
312  compare_them:
313   SVN_ERR(svn_wc__db_pristine_read(&pristine_stream, &pristine_size,
314                                    db, local_abspath, checksum,
315                                    scratch_pool, scratch_pool));
316
317   /* Check all bytes, and verify checksum if requested. */
318   {
319     svn_error_t *err;
320     err = compare_and_verify(modified_p, db,
321                              local_abspath, dirent->filesize,
322                              pristine_stream, pristine_size,
323                              has_props, props_mod,
324                              exact_comparison,
325                              scratch_pool);
326
327     /* At this point we already opened the pristine file, so we know that
328        the access denied applies to the working copy path */
329     if (err && APR_STATUS_IS_EACCES(err->apr_err))
330       return svn_error_create(SVN_ERR_WC_PATH_ACCESS_DENIED, err, NULL);
331     else
332       SVN_ERR(err);
333   }
334
335   if (!*modified_p)
336     {
337       svn_boolean_t own_lock;
338
339       /* The timestamp is missing or "broken" so "repair" it if we can. */
340       SVN_ERR(svn_wc__db_wclock_owns_lock(&own_lock, db, local_abspath, FALSE,
341                                           scratch_pool));
342       if (own_lock)
343         SVN_ERR(svn_wc__db_global_record_fileinfo(db, local_abspath,
344                                                   dirent->filesize,
345                                                   dirent->mtime,
346                                                   scratch_pool));
347     }
348
349   return SVN_NO_ERROR;
350 }
351
352
353 svn_error_t *
354 svn_wc_text_modified_p2(svn_boolean_t *modified_p,
355                         svn_wc_context_t *wc_ctx,
356                         const char *local_abspath,
357                         svn_boolean_t unused,
358                         apr_pool_t *scratch_pool)
359 {
360   return svn_wc__internal_file_modified_p(modified_p, wc_ctx->db,
361                                           local_abspath, FALSE, scratch_pool);
362 }
363
364
365 \f
366 static svn_error_t *
367 internal_conflicted_p(svn_boolean_t *text_conflicted_p,
368                       svn_boolean_t *prop_conflicted_p,
369                       svn_boolean_t *tree_conflicted_p,
370                       svn_boolean_t *ignore_move_edit_p,
371                       svn_wc__db_t *db,
372                       const char *local_abspath,
373                       apr_pool_t *scratch_pool)
374 {
375   svn_node_kind_t kind;
376   svn_skel_t *conflicts;
377   svn_boolean_t resolved_text = FALSE;
378   svn_boolean_t resolved_props = FALSE;
379
380   SVN_ERR(svn_wc__db_read_conflict(&conflicts, db, local_abspath,
381                                    scratch_pool, scratch_pool));
382
383   if (!conflicts)
384     {
385       if (text_conflicted_p)
386         *text_conflicted_p = FALSE;
387       if (prop_conflicted_p)
388         *prop_conflicted_p = FALSE;
389       if (tree_conflicted_p)
390         *tree_conflicted_p = FALSE;
391       if (ignore_move_edit_p)
392         *ignore_move_edit_p = FALSE;
393
394       return SVN_NO_ERROR;
395     }
396
397   SVN_ERR(svn_wc__conflict_read_info(NULL, NULL, text_conflicted_p,
398                                      prop_conflicted_p, tree_conflicted_p,
399                                      db, local_abspath, conflicts,
400                                      scratch_pool, scratch_pool));
401
402   if (text_conflicted_p && *text_conflicted_p)
403     {
404       const char *mine_abspath;
405       const char *their_old_abspath;
406       const char *their_abspath;
407       svn_boolean_t done = FALSE;
408
409       /* Look for any text conflict, exercising only as much effort as
410          necessary to obtain a definitive answer.  This only applies to
411          files, but we don't have to explicitly check that entry is a
412          file, since these attributes would never be set on a directory
413          anyway.  A conflict file entry notation only counts if the
414          conflict file still exists on disk.  */
415
416       SVN_ERR(svn_wc__conflict_read_text_conflict(&mine_abspath,
417                                                   &their_old_abspath,
418                                                   &their_abspath,
419                                                   db, local_abspath, conflicts,
420                                                   scratch_pool, scratch_pool));
421
422       if (mine_abspath)
423         {
424           SVN_ERR(svn_io_check_path(mine_abspath, &kind, scratch_pool));
425
426           *text_conflicted_p = (kind == svn_node_file);
427
428           if (*text_conflicted_p)
429             done = TRUE;
430         }
431
432       if (!done && their_abspath)
433         {
434           SVN_ERR(svn_io_check_path(their_abspath, &kind, scratch_pool));
435
436           *text_conflicted_p = (kind == svn_node_file);
437
438           if (*text_conflicted_p)
439             done = TRUE;
440         }
441
442         if (!done && their_old_abspath)
443         {
444           SVN_ERR(svn_io_check_path(their_old_abspath, &kind, scratch_pool));
445
446           *text_conflicted_p = (kind == svn_node_file);
447
448           if (*text_conflicted_p)
449             done = TRUE;
450         }
451
452         if (!done && (mine_abspath || their_abspath || their_old_abspath))
453           resolved_text = TRUE; /* Remove in-db conflict marker */
454     }
455
456   if (prop_conflicted_p && *prop_conflicted_p)
457     {
458       const char *prej_abspath;
459
460       SVN_ERR(svn_wc__conflict_read_prop_conflict(&prej_abspath,
461                                                   NULL, NULL, NULL, NULL,
462                                                   db, local_abspath, conflicts,
463                                                   scratch_pool, scratch_pool));
464
465       if (prej_abspath)
466         {
467           SVN_ERR(svn_io_check_path(prej_abspath, &kind, scratch_pool));
468
469           *prop_conflicted_p = (kind == svn_node_file);
470
471           if (! *prop_conflicted_p)
472             resolved_props = TRUE; /* Remove in-db conflict marker */
473         }
474     }
475
476   if (ignore_move_edit_p)
477     {
478       *ignore_move_edit_p = FALSE;
479       if (tree_conflicted_p && *tree_conflicted_p)
480         {
481           svn_wc_conflict_reason_t reason;
482           svn_wc_conflict_action_t action;
483
484           SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action, NULL,
485                                                       db, local_abspath,
486                                                       conflicts,
487                                                       scratch_pool,
488                                                       scratch_pool));
489
490           if (reason == svn_wc_conflict_reason_moved_away
491               && action == svn_wc_conflict_action_edit)
492             {
493               *tree_conflicted_p = FALSE;
494               *ignore_move_edit_p = TRUE;
495             }
496         }
497     }
498
499   if (resolved_text || resolved_props)
500     {
501       svn_boolean_t own_lock;
502
503       /* The marker files are missing, so "repair" wc.db if we can */
504       SVN_ERR(svn_wc__db_wclock_owns_lock(&own_lock, db, local_abspath, FALSE,
505                                           scratch_pool));
506       if (own_lock)
507         SVN_ERR(svn_wc__db_op_mark_resolved(db, local_abspath,
508                                             resolved_text,
509                                             resolved_props,
510                                             FALSE /* resolved_tree */,
511                                             NULL /* work_items */,
512                                             scratch_pool));
513     }
514
515   return SVN_NO_ERROR;
516 }
517
518 svn_error_t *
519 svn_wc__internal_conflicted_p(svn_boolean_t *text_conflicted_p,
520                               svn_boolean_t *prop_conflicted_p,
521                               svn_boolean_t *tree_conflicted_p,
522                               svn_wc__db_t *db,
523                               const char *local_abspath,
524                               apr_pool_t *scratch_pool)
525 {
526   SVN_ERR(internal_conflicted_p(text_conflicted_p, prop_conflicted_p,
527                                 tree_conflicted_p, NULL,
528                                 db, local_abspath, scratch_pool));
529   return SVN_NO_ERROR;
530 }
531
532 svn_error_t *
533 svn_wc__conflicted_for_update_p(svn_boolean_t *conflicted_p,
534                                 svn_boolean_t *conflict_ignored_p,
535                                 svn_wc__db_t *db,
536                                 const char *local_abspath,
537                                 svn_boolean_t tree_only,
538                                 apr_pool_t *scratch_pool)
539 {
540   svn_boolean_t text_conflicted, prop_conflicted, tree_conflicted;
541   svn_boolean_t conflict_ignored;
542
543   if (!conflict_ignored_p)
544     conflict_ignored_p = &conflict_ignored;
545
546   SVN_ERR(internal_conflicted_p(tree_only ? NULL: &text_conflicted,
547                                 tree_only ? NULL: &prop_conflicted,
548                                 &tree_conflicted, conflict_ignored_p,
549                                 db, local_abspath, scratch_pool));
550   if (tree_only)
551     *conflicted_p = tree_conflicted;
552   else
553     *conflicted_p = text_conflicted || prop_conflicted || tree_conflicted;
554
555   return SVN_NO_ERROR;
556 }
557
558
559 svn_error_t *
560 svn_wc_conflicted_p3(svn_boolean_t *text_conflicted_p,
561                      svn_boolean_t *prop_conflicted_p,
562                      svn_boolean_t *tree_conflicted_p,
563                      svn_wc_context_t *wc_ctx,
564                      const char *local_abspath,
565                      apr_pool_t *scratch_pool)
566 {
567   return svn_error_trace(svn_wc__internal_conflicted_p(text_conflicted_p,
568                                                        prop_conflicted_p,
569                                                        tree_conflicted_p,
570                                                        wc_ctx->db,
571                                                        local_abspath,
572                                                        scratch_pool));
573 }
574
575 svn_error_t *
576 svn_wc__min_max_revisions(svn_revnum_t *min_revision,
577                           svn_revnum_t *max_revision,
578                           svn_wc_context_t *wc_ctx,
579                           const char *local_abspath,
580                           svn_boolean_t committed,
581                           apr_pool_t *scratch_pool)
582 {
583   return svn_error_trace(svn_wc__db_min_max_revisions(min_revision,
584                                                       max_revision,
585                                                       wc_ctx->db,
586                                                       local_abspath,
587                                                       committed,
588                                                       scratch_pool));
589 }
590
591
592 svn_error_t *
593 svn_wc__has_switched_subtrees(svn_boolean_t *is_switched,
594                               svn_wc_context_t *wc_ctx,
595                               const char *local_abspath,
596                               const char *trail_url,
597                               apr_pool_t *scratch_pool)
598 {
599   return svn_error_trace(svn_wc__db_has_switched_subtrees(is_switched,
600                                                           wc_ctx->db,
601                                                           local_abspath,
602                                                           trail_url,
603                                                           scratch_pool));
604 }
605
606
607 svn_error_t *
608 svn_wc__has_local_mods(svn_boolean_t *is_modified,
609                        svn_wc_context_t *wc_ctx,
610                        const char *local_abspath,
611                        svn_cancel_func_t cancel_func,
612                        void *cancel_baton,
613                        apr_pool_t *scratch_pool)
614 {
615   return svn_error_trace(svn_wc__db_has_local_mods(is_modified,
616                                                    wc_ctx->db,
617                                                    local_abspath,
618                                                    cancel_func,
619                                                    cancel_baton,
620                                                    scratch_pool));
621 }