diff options
Diffstat (limited to 'subversion/libsvn_wc/copy.c')
| -rw-r--r-- | subversion/libsvn_wc/copy.c | 182 | 
1 files changed, 142 insertions, 40 deletions
| diff --git a/subversion/libsvn_wc/copy.c b/subversion/libsvn_wc/copy.c index 1e7d7cf08b761..30a0db58cc6a3 100644 --- a/subversion/libsvn_wc/copy.c +++ b/subversion/libsvn_wc/copy.c @@ -42,6 +42,7 @@  #include "svn_private_config.h"  #include "private/svn_wc_private.h" +/* #define RECORD_MIXED_MOVE */  /*** Code. ***/ @@ -50,7 +51,14 @@     TMPDIR_ABSPATH and return the absolute path of the copy in     *DST_ABSPATH.  Return the node kind of SRC_ABSPATH in *KIND.  If     SRC_ABSPATH doesn't exist then set *DST_ABSPATH to NULL to indicate -   that no copy was made. */ +   that no copy was made. + +   If DIRENT is not NULL, it contains the on-disk information of SRC_ABSPATH. +   RECORDED_SIZE (if not SVN_INVALID_FILESIZE) contains the recorded size of +   SRC_ABSPATH, and RECORDED_TIME the recorded size or 0. + +   These values will be used to avoid unneeded work. + */  static svn_error_t *  copy_to_tmpdir(svn_skel_t **work_item,                 svn_node_kind_t *kind, @@ -60,6 +68,9 @@ copy_to_tmpdir(svn_skel_t **work_item,                 const char *tmpdir_abspath,                 svn_boolean_t file_copy,                 svn_boolean_t unversioned, +               const svn_io_dirent2_t *dirent, +               svn_filesize_t recorded_size, +               apr_time_t recorded_time,                 svn_cancel_func_t cancel_func,                 void *cancel_baton,                 apr_pool_t *result_pool, @@ -74,8 +85,14 @@ copy_to_tmpdir(svn_skel_t **work_item,    *work_item = NULL; -  SVN_ERR(svn_io_check_special_path(src_abspath, kind, &is_special, -                                    scratch_pool)); +  if (dirent) +    { +      *kind = dirent->kind; +      is_special = dirent->special; +    } +  else +    SVN_ERR(svn_io_check_special_path(src_abspath, kind, &is_special, +                                      scratch_pool));    if (*kind == svn_node_none)      {        return SVN_NO_ERROR; @@ -104,9 +121,21 @@ copy_to_tmpdir(svn_skel_t **work_item,           the timestamp might match, than to examine the           destination later as the destination timestamp will           never match. */ -      SVN_ERR(svn_wc__internal_file_modified_p(&modified, -                                               db, src_abspath, -                                               FALSE, scratch_pool)); + +      if (dirent +          && dirent->kind == svn_node_file +          && recorded_size != SVN_INVALID_FILESIZE +          && recorded_size == dirent->filesize +          && recorded_time == dirent->mtime) +        { +          modified = FALSE; /* Recorded matches on-disk. Easy out */ +        } +      else +        { +          SVN_ERR(svn_wc__internal_file_modified_p(&modified, db, src_abspath, +                                                   FALSE, scratch_pool)); +        } +        if (!modified)          {            /* Why create a temp copy if we can just reinstall from pristine? */ @@ -117,6 +146,15 @@ copy_to_tmpdir(svn_skel_t **work_item,            return SVN_NO_ERROR;          }      } +  else if (*kind == svn_node_dir && !file_copy) +    { +      /* Just build a new direcory from the workqueue */ +      SVN_ERR(svn_wc__wq_build_dir_install(work_item, +                                           db, dst_abspath, +                                           result_pool, scratch_pool)); + +      return SVN_NO_ERROR; +    }    /* Set DST_TMP_ABSPATH to a temporary unique path.  If *KIND is file, leave       a file there and then overwrite it; otherwise leave no node on disk at @@ -172,7 +210,14 @@ copy_to_tmpdir(svn_skel_t **work_item,     versioned file itself.     This also works for versioned symlinks that are stored in the db as -   svn_node_file with svn:special set. */ +   svn_node_file with svn:special set. + +   If DIRENT is not NULL, it contains the on-disk information of SRC_ABSPATH. +   RECORDED_SIZE (if not SVN_INVALID_FILESIZE) contains the recorded size of +   SRC_ABSPATH, and RECORDED_TIME the recorded size or 0. + +   These values will be used to avoid unneeded work. +*/  static svn_error_t *  copy_versioned_file(svn_wc__db_t *db,                      const char *src_abspath, @@ -182,6 +227,9 @@ copy_versioned_file(svn_wc__db_t *db,                      svn_boolean_t metadata_only,                      svn_boolean_t conflicted,                      svn_boolean_t is_move, +                    const svn_io_dirent2_t *dirent, +                    svn_filesize_t recorded_size, +                    apr_time_t recorded_time,                      svn_cancel_func_t cancel_func,                      void *cancel_baton,                      svn_wc_notify_func2_t notify_func, @@ -210,8 +258,9 @@ copy_versioned_file(svn_wc__db_t *db,            svn_error_t *err;            /* Is there a text conflict at the source path? */ -          SVN_ERR(svn_wc__db_read_conflict(&conflict, db, src_abspath, -                                         scratch_pool, scratch_pool)); +          SVN_ERR(svn_wc__db_read_conflict(&conflict, NULL, NULL, +                                           db, src_abspath, +                                           scratch_pool, scratch_pool));            err = svn_wc__conflict_read_text_conflict(&conflict_working, NULL, NULL,                                                      db, src_abspath, conflict, @@ -248,6 +297,7 @@ copy_versioned_file(svn_wc__db_t *db,                               dst_abspath, tmpdir_abspath,                               TRUE /* file_copy */,                               handle_as_unversioned /* unversioned */, +                             dirent, recorded_size, recorded_time,                               cancel_func, cancel_baton,                               scratch_pool, scratch_pool));      } @@ -265,10 +315,6 @@ copy_versioned_file(svn_wc__db_t *db,                                 scratch_pool);        notify->kind = svn_node_file; -      /* When we notify that we performed a copy, make sure we already did */ -      if (work_items != NULL) -        SVN_ERR(svn_wc__wq_run(db, dst_abspath, -                               cancel_func, cancel_baton, scratch_pool));        (*notify_func)(notify_baton, notify, scratch_pool);      }    return SVN_NO_ERROR; @@ -282,6 +328,8 @@ copy_versioned_file(svn_wc__db_t *db,     data in addition to copying the directory.     WITHIN_ONE_WC is TRUE if the copy/move is within a single working copy (root) + +   If DIRENT is not NULL, it contains the on-disk information of SRC_ABSPATH.   */  static svn_error_t *  copy_versioned_dir(svn_wc__db_t *db, @@ -291,6 +339,7 @@ copy_versioned_dir(svn_wc__db_t *db,                     const char *tmpdir_abspath,                     svn_boolean_t metadata_only,                     svn_boolean_t is_move, +                   const svn_io_dirent2_t *dirent,                     svn_cancel_func_t cancel_func,                     void *cancel_baton,                     svn_wc_notify_func2_t notify_func, @@ -314,6 +363,7 @@ copy_versioned_dir(svn_wc__db_t *db,                               tmpdir_abspath,                               FALSE /* file_copy */,                               FALSE /* unversioned */, +                             dirent, SVN_INVALID_FILESIZE, 0,                               cancel_func, cancel_baton,                               scratch_pool, scratch_pool));      } @@ -353,6 +403,7 @@ copy_versioned_dir(svn_wc__db_t *db,    SVN_ERR(svn_wc__db_read_children_info(&versioned_children,                                          &conflicted_children,                                          db, src_abspath, +                                        FALSE /* base_tree_only */,                                          scratch_pool, iterpool));    for (hi = apr_hash_first(scratch_pool, versioned_children);         hi; @@ -366,8 +417,8 @@ copy_versioned_dir(svn_wc__db_t *db,        if (cancel_func)          SVN_ERR(cancel_func(cancel_baton)); -      child_name = svn__apr_hash_index_key(hi); -      info = svn__apr_hash_index_val(hi); +      child_name = apr_hash_this_key(hi); +      info = apr_hash_this_val(hi);        child_src_abspath = svn_dirent_join(src_abspath, child_name, iterpool);        child_dst_abspath = svn_dirent_join(dst_abspath, child_name, iterpool); @@ -394,6 +445,12 @@ copy_versioned_dir(svn_wc__db_t *db,                                              tmpdir_abspath,                                              metadata_only, info->conflicted,                                              is_move, +                                            disk_children +                                              ? svn_hash_gets(disk_children, +                                                              child_name) +                                              : NULL, +                                            info->recorded_size, +                                            info->recorded_time,                                              cancel_func, cancel_baton,                                              NULL, NULL,                                              iterpool)); @@ -403,6 +460,10 @@ copy_versioned_dir(svn_wc__db_t *db,                                         child_src_abspath, child_dst_abspath,                                         dst_op_root_abspath, tmpdir_abspath,                                         metadata_only, is_move, +                                       disk_children +                                              ? svn_hash_gets(disk_children, +                                                              child_name) +                                              : NULL,                                         cancel_func, cancel_baton, NULL, NULL,                                         iterpool));            else @@ -421,7 +482,7 @@ copy_versioned_dir(svn_wc__db_t *db,                                       child_dst_abspath, dst_op_root_abspath,                                       is_move, NULL, iterpool)); -          /* Don't recurse on children while all we do is creating not-present +          /* Don't recurse on children when all we do is creating not-present               children */          }        else if (info->status == svn_wc__db_status_incomplete) @@ -467,7 +528,7 @@ copy_versioned_dir(svn_wc__db_t *db,        for (hi = apr_hash_first(scratch_pool, disk_children); hi;             hi = apr_hash_next(hi))          { -          const char *name = svn__apr_hash_index_key(hi); +          const char *name = apr_hash_this_key(hi);            const char *unver_src_abspath, *unver_dst_abspath;            svn_skel_t *work_item; @@ -488,6 +549,7 @@ copy_versioned_dir(svn_wc__db_t *db,            SVN_ERR(copy_to_tmpdir(&work_item, NULL, db, unver_src_abspath,                                   unver_dst_abspath, tmpdir_abspath,                                   TRUE /* recursive */, TRUE /* unversioned */, +                                 NULL, SVN_INVALID_FILESIZE, 0,                                   cancel_func, cancel_baton,                                   scratch_pool, iterpool)); @@ -507,10 +569,10 @@ copy_versioned_dir(svn_wc__db_t *db,   * The additional parameter IS_MOVE indicates whether this is a copy or   * a move operation.   * - * If MOVE_DEGRADED_TO_COPY is not NULL and a move had to be degraded - * to a copy, then set *MOVE_DEGRADED_TO_COPY. */ + * If RECORD_MOVE_ON_DELETE is not NULL and a move had to be degraded + * to a copy, then set *RECORD_MOVE_ON_DELETE to FALSE. */  static svn_error_t * -copy_or_move(svn_boolean_t *move_degraded_to_copy, +copy_or_move(svn_boolean_t *record_move_on_delete,               svn_wc_context_t *wc_ctx,               const char *src_abspath,               const char *dst_abspath, @@ -533,6 +595,8 @@ copy_or_move(svn_boolean_t *move_degraded_to_copy,    svn_boolean_t within_one_wc;    svn_wc__db_status_t src_status;    svn_error_t *err; +  svn_filesize_t recorded_size; +  apr_time_t recorded_time;    SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));    SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath)); @@ -550,7 +614,8 @@ copy_or_move(svn_boolean_t *move_degraded_to_copy,      err = svn_wc__db_read_info(&src_status, &src_db_kind, NULL,                                 &src_repos_relpath, &src_repos_root_url,                                 &src_repos_uuid, NULL, NULL, NULL, NULL, NULL, -                               NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +                               NULL, NULL, NULL, NULL, NULL, NULL, +                               &recorded_size, &recorded_time,                                 NULL, &conflicted, NULL, NULL, NULL, NULL,                                 NULL, NULL,                                 db, src_abspath, scratch_pool, scratch_pool); @@ -643,10 +708,13 @@ copy_or_move(svn_boolean_t *move_degraded_to_copy,                                             scratch_pool, scratch_pool));          else            /* If not added, the node must have a base or we can't copy */ -          SVN_ERR(svn_wc__db_scan_base_repos(NULL, &src_repos_root_url, -                                             &src_repos_uuid, -                                             db, src_abspath, -                                             scratch_pool, scratch_pool)); +          SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, +                                           &src_repos_root_url, +                                           &src_repos_uuid, NULL, NULL, NULL, +                                           NULL, NULL, NULL, NULL, NULL, NULL, +                                           NULL, +                                           db, src_abspath, +                                           scratch_pool, scratch_pool));        }      if (!dst_repos_root_url) @@ -660,10 +728,13 @@ copy_or_move(svn_boolean_t *move_degraded_to_copy,                                             scratch_pool, scratch_pool));          else            /* If not added, the node must have a base or we can't copy */ -          SVN_ERR(svn_wc__db_scan_base_repos(NULL, &dst_repos_root_url, -                                             &dst_repos_uuid, -                                             db, dstdir_abspath, -                                             scratch_pool, scratch_pool)); +          SVN_ERR(svn_wc__db_base_get_info(NULL, NULL, NULL, NULL, +                                           &dst_repos_root_url, +                                           &dst_repos_uuid, NULL, NULL, NULL, +                                           NULL, NULL, NULL, NULL, NULL, NULL, +                                           NULL, +                                           db, dstdir_abspath, +                                           scratch_pool, scratch_pool));        }      if (strcmp(src_repos_root_url, dst_repos_root_url) != 0 @@ -751,8 +822,8 @@ copy_or_move(svn_boolean_t *move_degraded_to_copy,    if (is_move        && !within_one_wc)      { -      if (move_degraded_to_copy) -        *move_degraded_to_copy = TRUE; +      if (record_move_on_delete) +        *record_move_on_delete = FALSE;        is_move = FALSE;      } @@ -768,6 +839,7 @@ copy_or_move(svn_boolean_t *move_degraded_to_copy,        err = copy_versioned_file(db, src_abspath, dst_abspath, dst_abspath,                                  tmpdir_abspath,                                  metadata_only, conflicted, is_move, +                                NULL, recorded_size, recorded_time,                                  cancel_func, cancel_baton,                                  notify_func, notify_baton,                                  scratch_pool); @@ -795,14 +867,17 @@ copy_or_move(svn_boolean_t *move_degraded_to_copy,                                                                  scratch_pool),                                           min_rev, max_rev); +#ifndef RECORD_MIXED_MOVE                is_move = FALSE; -              if (move_degraded_to_copy) -                *move_degraded_to_copy = TRUE; +              if (record_move_on_delete) +                *record_move_on_delete = FALSE; +#endif              }          }        err = copy_versioned_dir(db, src_abspath, dst_abspath, dst_abspath,                                 tmpdir_abspath, metadata_only, is_move, +                               NULL /* dirent */,                                 cancel_func, cancel_baton,                                 notify_func, notify_baton,                                 scratch_pool); @@ -871,7 +946,8 @@ remove_node_conflict_markers(svn_wc__db_t *db,  {    svn_skel_t *conflict; -  SVN_ERR(svn_wc__db_read_conflict(&conflict, db, src_abspath, +  SVN_ERR(svn_wc__db_read_conflict(&conflict, NULL, NULL, +                                   db, src_abspath,                                     scratch_pool, scratch_pool));    /* Do we have conflict markers that should be removed? */ @@ -923,6 +999,8 @@ static svn_error_t *  remove_all_conflict_markers(svn_wc__db_t *db,                              const char *src_dir_abspath,                              const char *dst_dir_abspath, +                            svn_cancel_func_t cancel_func, +                            void *cancel_baton,                              apr_pool_t *scratch_pool)  {    apr_pool_t *iterpool = svn_pool_create(scratch_pool); @@ -936,14 +1014,18 @@ remove_all_conflict_markers(svn_wc__db_t *db,            artillery. */    SVN_ERR(svn_wc__db_read_children_info(&nodes, &conflicts, db,                                          src_dir_abspath, +                                        FALSE /* base_tree_only */,                                          scratch_pool, iterpool));    for (hi = apr_hash_first(scratch_pool, nodes);         hi;         hi = apr_hash_next(hi))      { -      const char *name = svn__apr_hash_index_key(hi); -      struct svn_wc__db_info_t *info = svn__apr_hash_index_val(hi); +      const char *name = apr_hash_this_key(hi); +      struct svn_wc__db_info_t *info = apr_hash_this_val(hi); + +      if (cancel_func) +        SVN_ERR(cancel_func(cancel_baton));        if (info->conflicted)          { @@ -961,6 +1043,7 @@ remove_all_conflict_markers(svn_wc__db_t *db,                              db,                              svn_dirent_join(src_dir_abspath, name, iterpool),                              svn_dirent_join(dst_dir_abspath, name, iterpool), +                            cancel_func, cancel_baton,                              iterpool));          }      } @@ -982,7 +1065,7 @@ svn_wc__move2(svn_wc_context_t *wc_ctx,                apr_pool_t *scratch_pool)  {    svn_wc__db_t *db = wc_ctx->db; -  svn_boolean_t move_degraded_to_copy = FALSE; +  svn_boolean_t record_on_delete = TRUE;    svn_node_kind_t kind;    svn_boolean_t conflicted; @@ -994,7 +1077,7 @@ svn_wc__move2(svn_wc_context_t *wc_ctx,                                svn_dirent_dirname(dst_abspath, scratch_pool),                                scratch_pool)); -  SVN_ERR(copy_or_move(&move_degraded_to_copy, +  SVN_ERR(copy_or_move(&record_on_delete,                         wc_ctx, src_abspath, dst_abspath,                         TRUE /* metadata_only */,                         TRUE /* is_move */, @@ -1018,7 +1101,25 @@ svn_wc__move2(svn_wc_context_t *wc_ctx,       is still in a valid state. So be careful when switching this over       to the workqueue. */    if (!metadata_only) -    SVN_ERR(svn_io_file_rename(src_abspath, dst_abspath, scratch_pool)); +    { +      svn_error_t *err; + +      err = svn_error_trace(svn_io_file_rename(src_abspath, dst_abspath, +                                               scratch_pool)); + +      /* Let's try if we can keep wc.db consistent even when the move +         fails. Deleting the target is a wc.db only operation, while +         going forward (delaying the error) would try to change +         conflict markers, which might also fail. */ +      if (err) +        return svn_error_trace( +          svn_error_compose_create( +              err, +              svn_wc__db_op_delete(wc_ctx->db, dst_abspath, NULL, TRUE, +                                   NULL, NULL, cancel_func, cancel_baton, +                                   NULL, NULL, +                                   scratch_pool))); +    }    SVN_ERR(svn_wc__db_read_info(NULL, &kind, NULL, NULL, NULL, NULL, NULL,                                 NULL, NULL, NULL, NULL, NULL, NULL, NULL, @@ -1030,6 +1131,7 @@ svn_wc__move2(svn_wc_context_t *wc_ctx,    if (kind == svn_node_dir)      SVN_ERR(remove_all_conflict_markers(db, src_abspath, dst_abspath, +                                        cancel_func, cancel_baton,                                          scratch_pool));    if (conflicted) @@ -1045,7 +1147,7 @@ svn_wc__move2(svn_wc_context_t *wc_ctx,      }    SVN_ERR(svn_wc__db_op_delete(db, src_abspath, -                               move_degraded_to_copy ? NULL : dst_abspath, +                               record_on_delete ? dst_abspath : NULL,                                 TRUE /* delete_dir_externals */,                                 NULL /* conflict */, NULL /* work_items */,                                 cancel_func, cancel_baton, | 
