⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 copy.c

📁 linux subdivision ying gai ke yi le ba
💻 C
📖 第 1 页 / 共 3 页
字号:
  /* Pass null for the path, to ensure error if trying to get a
     revision based on the working copy.  And additionally, we can't
     pass an 'unspecified' revnum to the update reporter;  assume HEAD
     if not specified. */
  revision.kind = src_revision->kind;
  revision.value = src_revision->value;
  if (revision.kind == svn_opt_revision_unspecified)
    revision.kind = svn_opt_revision_head;

  SVN_ERR (svn_client__get_revision_number
           (&src_revnum, ra_lib, sess, &revision, NULL, pool));

  /* Verify that SRC_URL exists in the repository. */
  SVN_ERR (ra_lib->check_path (sess, "", src_revnum, &src_kind, pool));
  if (src_kind == svn_node_none)
    {
      if (SVN_IS_VALID_REVNUM (src_revnum))
        return svn_error_createf
          (SVN_ERR_FS_NOT_FOUND, NULL,
           _("Path '%s' not found in revision %ld"),
           src_url, src_revnum);
      else
        return svn_error_createf
          (SVN_ERR_FS_NOT_FOUND, NULL,
           _("Path '%s' not found in head revision"), src_url);
    }

  /* There are two interfering sets of cases to watch out for here:
   *
   * First set:
   *
   *   1) If DST_PATH does not exist, then great.  We're going to
   *      create a new entry in its parent.
   *   2) If it does exist, then it must be a directory and we're
   *      copying to a new entry inside that dir (the entry's name is
   *      the basename of SRC_URL).
   *
   * But while that's all going on, we must also remember:
   *
   *   A) If SRC_URL is a directory in the repository, we can check
   *      it out directly, no problem.
   *   B) If SRC_URL is a file, we have to manually get the editor
   *      started, since there won't be a root to open.
   *
   * I'm going to ignore B for the moment, and implement cases 1 and
   * 2 under A.
   */

  /* First, figure out about dst. */
  SVN_ERR (svn_io_check_path (dst_path, &dst_kind, pool));
  if (dst_kind == svn_node_dir)
    {
      const char *base_name;
      svn_path_split (src_url, NULL, &base_name, pool);
      dst_path = svn_path_join (dst_path, 
                                svn_path_uri_decode (base_name, pool),
                                pool);
    }
  else if (dst_kind != svn_node_none)  /* must be a file */
    {
      return svn_error_createf (SVN_ERR_ENTRY_EXISTS, NULL,
                                _("File '%s' already exists"), dst_path);
    }

  /* Now that dst_path has possibly been reset, check that there's
     nothing in the way of the upcoming checkout. */
  SVN_ERR (svn_io_check_path (dst_path, &dst_kind, pool));
  if (dst_kind != svn_node_none)
    return svn_error_createf (SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
                              _("'%s' is in the way"), dst_path);

  SVN_ERR (svn_wc_adm_probe_open2 (&adm_access, NULL, dst_path, TRUE,
                                   0, pool));

  /* We've already checked for physical obstruction by a working file.
     But there could also be logical obstruction by an entry whose
     working file happens to be missing.*/ 
  {
    const svn_wc_entry_t *ent;

    SVN_ERR (svn_wc_entry (&ent, dst_path, adm_access, FALSE, pool));
    if (ent && (ent->kind != svn_node_dir))
      return svn_error_createf
        (SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
         _("Entry for '%s' exists (though the working file is missing)"),
         dst_path);
  }

  /* Decide whether the two repositories are the same or not. */
  { 
    svn_error_t *src_err, *dst_err;
    const char *parent;
   
    /* Get the repository uuid of SRC_URL */
    src_err = ra_lib->get_uuid (sess, &src_uuid, pool);
    if (src_err && src_err->apr_err != SVN_ERR_RA_NO_REPOS_UUID)
      return src_err;

    /* Get repository uuid of dst's parent directory, since dst may
       not exist.  ### TODO:  we should probably walk up the wc here,
       in case the parent dir has an imaginary URL.  */
    svn_path_split (dst_path, &parent, NULL, pool);
    dst_err = svn_client_uuid_from_path (&dst_uuid, parent, adm_access,
                                         ctx, pool);
    if (dst_err && dst_err->apr_err != SVN_ERR_RA_NO_REPOS_UUID)
      return dst_err;
    
    /* If either of the UUIDs are nonexistent, then at least one of
       the repositories must be very old.  Rather than punish the
       user, just assume the repositories are different, so no
       copy-history is attempted. */
    if (src_err || dst_err || (! src_uuid) || (! dst_uuid))
      same_repositories = FALSE;
        
    else
      same_repositories = (strcmp (src_uuid, dst_uuid) == 0) ? TRUE : FALSE; 
  }

  if (src_kind == svn_node_dir)
    {    
      SVN_ERR (svn_client__checkout_internal
               (NULL, src_url, dst_path, &revision, TRUE, NULL, ctx, pool));

      if ((revision.kind == svn_opt_revision_head) && same_repositories)
        {
          /* If we just checked out from the "head" revision, that's fine,
             but we don't want to pass a '-1' as a copyfrom_rev to
             svn_wc_add().  That function will dump it right into the
             entry, and when we try to commit later on, the
             'add-dir-with-history' step will be -very- unhappy; it only
             accepts specific revisions.
             
             On the other hand, we *could* say that -1 is a legitimate
             copyfrom_rev, but I think that's bogus.  Somebody made a copy
             from a particular revision;  if they wait a long time to
             commit, it would be terrible if the copied happened from a
             newer revision!! */
          
          /* We just did a checkout; whatever revision we just got, that
             should be the copyfrom_revision when we commit later. */
          const svn_wc_entry_t *d_entry;
          svn_wc_adm_access_t *dst_access;
          SVN_ERR (svn_wc_adm_open2 (&dst_access, adm_access, dst_path,
                                     TRUE, -1, pool));
          SVN_ERR (svn_wc_entry (&d_entry, dst_path, dst_access, FALSE, pool));
          src_revnum = d_entry->revision;
        }

      /* Rewrite URLs recursively, remove wcprops, and mark everything
         as 'copied' -- assuming that the src and dst are from the
         same repository.  (It's kind of weird that svn_wc_add() is the
         way to do this; see its doc for more about the controversy.) */
      if (same_repositories)
        {
          /* Schedule dst_path for addition in parent, with copy history.
             (This function also recursively puts a 'copied' flag on every
             entry). */
          SVN_ERR (svn_wc_add (dst_path, adm_access, src_url, src_revnum,
                               ctx->cancel_func, ctx->cancel_baton, 
                               ctx->notify_func, ctx->notify_baton, pool));
        }
      else  /* different repositories */
        {
          /* ### Someday, we would just call svn_wc_add(), as above,
             but with no copyfrom args.  I.e. in the
             directory-foreign-UUID case, we still want everything
             scheduled for addition, URLs rewritten, and wcprop cache
             deleted, but WITHOUT any copied flags or copyfrom urls.
             Unfortunately, svn_wc_add() is such a mess that it chokes
             at the moment when we pass a NULL copyfromurl. */
          
          return svn_error_createf
            (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
             _("Source URL '%s' is from foreign repository; "
               "leaving it as a disjoint WC"), src_url);
        }
    } /* end directory case */

  else if (src_kind == svn_node_file)
    {
      apr_file_t *fp;
      svn_stream_t *fstream;
      svn_revnum_t real_rev;
      const char *new_text_path;
      apr_hash_t *new_props;

      SVN_ERR (svn_io_open_unique_file
               (&fp, &new_text_path, dst_path, ".tmp", FALSE, pool));

      fstream = svn_stream_from_aprfile (fp, pool);
      SVN_ERR (ra_lib->get_file
               (sess, "", src_revnum, fstream, &real_rev, &new_props, pool));
      SVN_ERR (svn_stream_close (fstream));
      SVN_ERR (svn_io_file_close (fp, pool));

      /* If SRC_REVNUM is invalid (HEAD), then REAL_REV is now the
         revision that was actually retrieved.  This is the value we
         want to use as 'copyfrom_rev' below. */
      if (! SVN_IS_VALID_REVNUM (src_revnum))
        src_revnum = real_rev;

      SVN_ERR (svn_wc_add_repos_file
               (dst_path, adm_access,
                new_text_path, new_props,
                same_repositories ? src_url : NULL,
                same_repositories ? src_revnum : SVN_INVALID_REVNUM,
                pool));

      /* Ideally, svn_wc_add_repos_file() would take a notify function
         and baton, and we wouldn't have to make this call here.
         However, the situation is... complicated.  See issue #1552
         for the full story. */
      if (ctx->notify_func)
        (*ctx->notify_func) (ctx->notify_baton,
                             dst_path,
                             svn_wc_notify_add,
                             src_kind,
                             NULL,
                             svn_wc_notify_state_unknown,
                             svn_wc_notify_state_unknown,
                             SVN_INVALID_REVNUM);
    }
  
  SVN_ERR (svn_wc_adm_close (adm_access));

  return SVN_NO_ERROR;
}


static svn_error_t *
setup_copy (svn_client_commit_info_t **commit_info,
            const char *src_path,
            const svn_opt_revision_t *src_revision,
            const char *dst_path,
            svn_boolean_t is_move,
            svn_boolean_t force,
            svn_client_ctx_t *ctx,
            apr_pool_t *pool)
{
  svn_boolean_t src_is_url, dst_is_url;

  /* Are either of our paths URLs? */
  src_is_url = svn_path_is_url (src_path);
  dst_is_url = svn_path_is_url (dst_path);

  if (!src_is_url && !dst_is_url
      && svn_path_is_child (src_path, dst_path, pool))
    return svn_error_createf
      (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
       _("Cannot copy path '%s' into its own child '%s'"),
       src_path, dst_path);

  if (is_move)
    {
      if (src_is_url == dst_is_url)
        {
          if (strcmp (src_path, dst_path) == 0)
            return svn_error_createf
              (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
               _("Cannot move path '%s' into itself"),
               src_path);
        }
      else
        {
          /* Disallow moves between the working copy and the repository. */
          return svn_error_create 
            (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
             _("No support for repos <--> working copy moves"));
        }

      /* It doesn't make sense to specify revisions in a move. */

      /* ### todo: this check could fail wrongly.  For example,
         someone could pass in an svn_opt_revision_number that just
         happens to be the HEAD.  It's fair enough to punt then, IMHO,
         and just demand that the user not specify a revision at all;
         beats mucking up this function with RA calls and such. */ 
      if (src_revision->kind != svn_opt_revision_unspecified
          && src_revision->kind != svn_opt_revision_head)
        {
          return svn_error_create
            (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
             _("Cannot specify revisions with move operations"));
        }
    }
  else
    {
      if (!src_is_url)
        {
          if (src_revision->kind != svn_opt_revision_unspecified
              && src_revision->kind != svn_opt_revision_working)
            {
              /* We can convert the working copy path to a URL based on the
                 entries file. */
              svn_wc_adm_access_t *adm_access;  /* ### FIXME local */
              const svn_wc_entry_t *entry;
              SVN_ERR (svn_wc_adm_probe_open2 (&adm_access, NULL,
                                               src_path, FALSE, 0,
                                               pool));
              SVN_ERR (svn_wc_entry (&entry, src_path, adm_access, FALSE,
                                     pool));
              SVN_ERR (svn_wc_adm_close (adm_access));

              if (! entry)
                return svn_error_createf
                  (SVN_ERR_UNVERSIONED_RESOURCE, NULL,
                   _("'%s' is not under version control"), src_path);

              if (! entry->url)
                return svn_error_createf
                  (SVN_ERR_ENTRY_MISSING_URL, NULL,
                   _("'%s' does not seem to have a URL associated with it"),
                   src_path);

              src_path = entry->url;
              src_is_url = TRUE;
            }
        }
    }

  /* Now, call the right handler for the operation. */
  if ((! src_is_url) && (! dst_is_url))
    {
      SVN_ERR (wc_to_wc_copy (src_path, dst_path,
                              is_move, force,
                              ctx,
                              pool));
    }
  else if ((! src_is_url) && (dst_is_url))
    {
      SVN_ERR (wc_to_repos_copy (commit_info, src_path, dst_path, 
                                 ctx, pool));
    }
  else if ((src_is_url) && (! dst_is_url))
    {
      SVN_ERR (repos_to_wc_copy (src_path, src_revision, 
                                 dst_path, ctx,
                                 pool));
    }
  else
    {
      SVN_ERR (repos_to_repos_copy (commit_info, src_path, src_revision,
                                    dst_path, ctx, is_move, pool));
    }

  return SVN_NO_ERROR;
}



/* Public Interfaces */

svn_error_t *
svn_client_copy (svn_client_commit_info_t **commit_info,
                 const char *src_path,
                 const svn_opt_revision_t *src_revision,
                 const char *dst_path,
                 svn_client_ctx_t *ctx,
                 apr_pool_t *pool)
{
  return setup_copy (commit_info, 
                     src_path, src_revision, dst_path,
                     FALSE /* is_move */,
                     TRUE /* force, set to avoid deletion check */,
                     ctx,
                     pool);
}


svn_error_t *
svn_client_move (svn_client_commit_info_t **commit_info,
                 const char *src_path,
                 const svn_opt_revision_t *src_revision,
                 const char *dst_path,
                 svn_boolean_t force,
                 svn_client_ctx_t *ctx,
                 apr_pool_t *pool)
{
  return setup_copy (commit_info,
                     src_path, src_revision, dst_path,
                     TRUE /* is_move */,
                     force,
                     ctx,
                     pool);
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -