commit.c

来自「linux subdivision ying gai ke yi le ba」· C语言 代码 · 共 1,448 行 · 第 1/4 页

C
1,448
字号
/*
 * commit.c:  wrappers around wc commit functionality.
 *
 * ====================================================================
 * Copyright (c) 2000-2004 CollabNet.  All rights reserved.
 *
 * This software is licensed as described in the file COPYING, which
 * you should have received as part of this distribution.  The terms
 * are also available at http://subversion.tigris.org/license-1.html.
 * If newer versions of this license are posted there, you may use a
 * newer version instead, at your option.
 *
 * This software consists of voluntary contributions made by many
 * individuals.  For exact contribution history, see the revision
 * history and logs, available at http://subversion.tigris.org/.
 * ====================================================================
 */

/* ==================================================================== */



/*** Includes. ***/

#include <string.h>
#include <assert.h>
#include <apr_strings.h>
#include <apr_hash.h>
#include <apr_md5.h>
#include "svn_wc.h"
#include "svn_ra.h"
#include "svn_delta.h"
#include "svn_subst.h"
#include "svn_client.h"
#include "svn_string.h"
#include "svn_pools.h"
#include "svn_error.h"
#include "svn_path.h"
#include "svn_test.h"
#include "svn_io.h"
#include "svn_md5.h"
#include "svn_time.h"
#include "svn_sorts.h"

#include "client.h"

#include "svn_private_config.h"

/* Apply PATH's contents (as a delta against the empty string) to
   FILE_BATON in EDITOR.  Use POOL for any temporary allocation.
   PROPERTIES is the set of node properties set on this file.

   Fill DIGEST with the md5 checksum of the sent file; DIGEST must be
   at least APR_MD5_DIGESTSIZE bytes long. */
static svn_error_t *
send_file_contents (const char *path,
                    void *file_baton,
                    const svn_delta_editor_t *editor,
                    apr_hash_t *properties,
                    unsigned char *digest,
                    apr_pool_t *pool)
{
  const char *tmpfile_path = NULL;
  svn_stream_t *contents;
  svn_txdelta_window_handler_t handler;
  void *handler_baton;
  apr_file_t *f = NULL;
  svn_error_t *err = SVN_NO_ERROR;
  const svn_string_t *eol_style_val = NULL, *keywords_val = NULL;
  svn_boolean_t special = FALSE;

  /* If there are properties, look for EOL-style and keywords ones. */
  if (properties)
    {
      eol_style_val = apr_hash_get (properties, SVN_PROP_EOL_STYLE,
                                    sizeof (SVN_PROP_EOL_STYLE) - 1);
      keywords_val = apr_hash_get (properties, SVN_PROP_KEYWORDS,
                                   sizeof (SVN_PROP_KEYWORDS) - 1);
      if (apr_hash_get (properties, SVN_PROP_SPECIAL, APR_HASH_KEY_STRING))
        special = TRUE;
    }

  /* Get an editor func that wants to consume the delta stream. */
  SVN_ERR (editor->apply_textdelta (file_baton, NULL, pool,
                                    &handler, &handler_baton));

  /* If we have EOL styles or keywords to de-translate, do it.  Any
     EOL style gets translated to "\n", the repository-normal format.
     Keywords get unexpanded.  */
  if (eol_style_val || keywords_val || special)
    {
      svn_subst_keywords_t keywords = {0};
      const char *temp_dir;
      apr_file_t *tmp_f;

      /* Now create a new tempfile, and open a stream to it. */
      SVN_ERR (svn_io_temp_dir (&temp_dir, pool));
      SVN_ERR (svn_io_open_unique_file 
               (&tmp_f, &tmpfile_path,
                svn_path_join (temp_dir, "svn-import", pool),
                ".tmp", FALSE, pool));
      SVN_ERR (svn_io_file_close (tmp_f, pool));

      /* Generate a keyword structure. */
      if (keywords_val)
        SVN_ERR (svn_subst_build_keywords (&keywords, keywords_val->data, 
                                           APR_STRINGIFY(SVN_INVALID_REVNUM),
                                           "", 0, "", pool));
      
      if ((err = svn_subst_copy_and_translate2 (path, tmpfile_path,
                                                eol_style_val ? "\n" : NULL,
                                                FALSE,
                                                keywords_val ? &keywords : NULL,
                                                FALSE,
                                                special,
                                                pool)))
        goto cleanup;
    }

  /* Open our contents file, either the original path or the temporary
     copy we might have made above. */
  if ((err = svn_io_file_open (&f, tmpfile_path ? tmpfile_path : path, 
                               APR_READ, APR_OS_DEFAULT, pool)))
    goto cleanup;
  contents = svn_stream_from_aprfile (f, pool);

  /* Send the file's contents to the delta-window handler. */
  if ((err = svn_txdelta_send_stream (contents, handler, handler_baton,
                                      digest, pool)))
    goto cleanup;

  /* Close our contents file. */
  if ((err = svn_io_file_close (f, pool)))
    goto cleanup;

 cleanup:
  if (tmpfile_path)
    {
      /* If we used a tempfile, we need to close and remove it, too. */
      svn_error_t *err2 = svn_io_remove_file (tmpfile_path, pool);
      if (err && err2)
        svn_error_compose (err, err2);
      else if (err2)
        err = err2;
    }

  return err;
}


/* Import file PATH as EDIT_PATH in the repository directory indicated
 * by DIR_BATON in EDITOR.  
 *
 * Accumulate file paths and their batons in FILES, which must be
 * non-null.  (These are used to send postfix textdeltas later).
 *
 * If CTX->NOTIFY_FUNC is non-null, invoke it with CTX->NOTIFY_BATON for each
 * file.
 *
 * Use POOL for any temporary allocation.
 */
static svn_error_t *
import_file (const svn_delta_editor_t *editor,
             void *dir_baton,
             const char *path,
             const char *edit_path,
             svn_client_ctx_t *ctx,
             apr_pool_t *pool)
{
  void *file_baton;
  const char *mimetype = NULL;
  unsigned char digest[APR_MD5_DIGESTSIZE];
  const char *text_checksum;
  apr_hash_t* properties;
  apr_hash_index_t *hi;
  svn_node_kind_t kind;
  svn_boolean_t is_special;

  /* Add the file, using the pool from the FILES hash. */
  SVN_ERR (editor->add_file (edit_path, dir_baton, NULL, SVN_INVALID_REVNUM, 
                             pool, &file_baton));

  SVN_ERR (svn_io_check_special_path (path, &kind, &is_special, pool));

  if (! is_special)
    {
      /* add automatic properties */
      SVN_ERR (svn_client__get_auto_props (&properties, &mimetype, path, ctx,
                                           pool));
    }
  else
    properties = apr_hash_make (pool);
      
  if (properties)
    {
      for (hi = apr_hash_first (pool, properties); hi; hi = apr_hash_next (hi))
        {
          const void *pname;
          void *pval;

          apr_hash_this (hi, &pname, NULL, &pval);
          SVN_ERR (editor->change_file_prop (file_baton, pname, pval, pool));
        }
    }

  if (ctx->notify_func)
    (*ctx->notify_func) (ctx->notify_baton,
                         path,
                         svn_wc_notify_commit_added,
                         svn_node_file,
                         mimetype,
                         svn_wc_notify_state_inapplicable,
                         svn_wc_notify_state_inapplicable,
                         SVN_INVALID_REVNUM);

  /* If this is a special file, we need to set the svn:special
     property and create a temporary detranslated version in order to
     send to the server. */
  if (is_special)
    {
      apr_hash_set (properties, SVN_PROP_SPECIAL, APR_HASH_KEY_STRING,
                    svn_string_create (SVN_PROP_SPECIAL_VALUE, pool));
      SVN_ERR (editor->change_file_prop (file_baton, SVN_PROP_SPECIAL,
                                         apr_hash_get (properties,
                                                       SVN_PROP_SPECIAL,
                                                       APR_HASH_KEY_STRING),
                                         pool));
    }

  /* Now, transmit the file contents. */
  SVN_ERR (send_file_contents (path, file_baton, editor, 
                               properties, digest, pool));

  /* Finally, close the file. */
  text_checksum = svn_md5_digest_to_cstring (digest, pool);
  SVN_ERR (editor->close_file (file_baton, text_checksum, pool));

  return SVN_NO_ERROR;
}
             

/* Import directory PATH into the repository directory indicated by
 * DIR_BATON in EDITOR.  EDIT_PATH is the path imported as the root
 * directory, so all edits are relative to that.
 *
 * Accumulate file paths and their batons in FILES, which must be
 * non-null.  (These are used to send postfix textdeltas later).
 *
 * If CTX->NOTIFY_FUNC is non-null, invoke it with CTX->NOTIFY_BATON for each
 * directory.
 *
 * EXCLUDES is a hash whose keys are absolute paths to exclude from
 * the import (values are unused).
 * 
 * Use POOL for any temporary allocation.  */
static svn_error_t *
import_dir (const svn_delta_editor_t *editor, 
            void *dir_baton,
            const char *path,
            const char *edit_path,
            svn_boolean_t nonrecursive,
            apr_hash_t *excludes,
            svn_client_ctx_t *ctx,
            apr_pool_t *pool)
{
  apr_pool_t *subpool = svn_pool_create (pool);  /* iteration pool */
  apr_hash_t *dirents;
  apr_hash_index_t *hi;
  apr_array_header_t *ignores;

  SVN_ERR (svn_wc_get_default_ignores (&ignores, ctx->config, pool));

  SVN_ERR (svn_io_get_dirents (&dirents, path, pool));

  for (hi = apr_hash_first (pool, dirents); hi; hi = apr_hash_next (hi))
    {
      const char *this_path, *this_edit_path, *abs_path;
      const svn_node_kind_t *filetype;
      const char *filename;
      const void *key;
      void *val;
      
      svn_pool_clear (subpool);

      apr_hash_this (hi, &key, NULL, &val);

      filename = key;
      filetype = val;

      if (ctx->cancel_func)
        SVN_ERR (ctx->cancel_func (ctx->cancel_baton));

      if (strcmp (filename, SVN_WC_ADM_DIR_NAME) == 0)
        {
          /* If someone's trying to import a directory named the same
             as our administrative directories, that's probably not
             what they wanted to do.  If they are importing a file
             with that name, something is bound to blow up when they
             checkout what they've imported.  So, just skip items with
             that name.  */
          if (ctx->notify_func)
            (*ctx->notify_func) (ctx->notify_baton,
                                 svn_path_join (path, filename, subpool),
                                 svn_wc_notify_skip,
                                 svn_node_dir,
                                 NULL,
                                 svn_wc_notify_state_inapplicable,
                                 svn_wc_notify_state_inapplicable,
                                 SVN_INVALID_REVNUM);
          continue;
        }

      /* Typically, we started importing from ".", in which case
         edit_path is "".  So below, this_path might become "./blah",
         and this_edit_path might become "blah", for example. */
      this_path = svn_path_join (path, filename, subpool);
      this_edit_path = svn_path_join (edit_path, filename, subpool);

      /* If this is an excluded path, exclude it. */
      SVN_ERR (svn_path_get_absolute (&abs_path, this_path, subpool));
      if (apr_hash_get (excludes, abs_path, APR_HASH_KEY_STRING))
        continue;

      if (svn_cstring_match_glob_list (filename, ignores))
        continue;

      /* We only import subdirectories when we're doing a regular
         recursive import. */
      if ((*filetype == svn_node_dir) && (! nonrecursive))
        {
          void *this_dir_baton;

          /* Add the new subdirectory, getting a descent baton from
             the editor. */
          SVN_ERR (editor->add_directory (this_edit_path, dir_baton, 
                                          NULL, SVN_INVALID_REVNUM, subpool,
                                          &this_dir_baton));

          /* By notifying before the recursive call below, we display
             a directory add before displaying adds underneath the
             directory.  To do it the other way around, just move this
             after the recursive call. */
          if (ctx->notify_func)
            (*ctx->notify_func) (ctx->notify_baton,
                                 this_path,
                                 svn_wc_notify_commit_added,
                                 svn_node_dir,
                                 NULL,
                                 svn_wc_notify_state_inapplicable,
                                 svn_wc_notify_state_inapplicable,
                                 SVN_INVALID_REVNUM);

          /* Recurse. */
          SVN_ERR (import_dir (editor, this_dir_baton, 
                               this_path, this_edit_path, 
                               FALSE, excludes, ctx, subpool));

          /* Finally, close the sub-directory. */
          SVN_ERR (editor->close_directory (this_dir_baton, subpool));
        }
      else if (*filetype == svn_node_file)
        {

⌨️ 快捷键说明

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