export.c
来自「linux subdivision ying gai ke yi le ba」· C语言 代码 · 共 869 行 · 第 1/2 页
C
869 行
/*
* export.c: export a tree.
*
* ====================================================================
* 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 <apr_file_io.h>
#include <apr_md5.h>
#include "svn_types.h"
#include "svn_wc.h"
#include "svn_client.h"
#include "svn_string.h"
#include "svn_error.h"
#include "svn_path.h"
#include "svn_pools.h"
#include "svn_subst.h"
#include "svn_time.h"
#include "svn_md5.h"
#include "client.h"
#include "svn_private_config.h"
/*** Code. ***/
/* Add EXTERNALS_PROP_VAL for the export destination path PATH to
TRAVERSAL_INFO. */
static void
add_externals (apr_hash_t *externals,
const char *path,
const svn_string_t *externals_prop_val)
{
apr_pool_t *pool = apr_hash_pool_get (externals);
if (! externals_prop_val)
return;
apr_hash_set (externals,
apr_pstrdup (pool, path),
APR_HASH_KEY_STRING,
apr_pstrmemdup (pool, externals_prop_val->data,
externals_prop_val->len));
}
/* Helper function that gets the eol style and optionally overrides the
EOL marker for files marked as native with the EOL marker matching
the string specified in requested_value which is of the same format
as the svn:eol-style property values. */
static svn_error_t *
get_eol_style (svn_subst_eol_style_t *style,
const char **eol,
const char *value,
const char *requested_value)
{
svn_subst_eol_style_from_value (style, eol, value);
if (requested_value && *style == svn_subst_eol_style_native)
{
svn_subst_eol_style_t requested_style;
const char *requested_eol;
svn_subst_eol_style_from_value (&requested_style, &requested_eol,
requested_value);
if (requested_style == svn_subst_eol_style_fixed)
*eol = requested_eol;
else
return svn_error_createf (SVN_ERR_IO_UNKNOWN_EOL, NULL,
_("'%s' is not a valid EOL value"),
requested_value);
}
return SVN_NO_ERROR;
}
static svn_error_t *
copy_versioned_files (const char *from,
const char *to,
svn_opt_revision_t *revision,
svn_boolean_t force,
const char *native_eol,
svn_client_ctx_t *ctx,
apr_pool_t *pool)
{
svn_wc_adm_access_t *adm_access;
const svn_wc_entry_t *entry;
svn_error_t *err;
apr_pool_t *iterpool;
apr_hash_t *dirents;
apr_hash_index_t *hi;
apr_finfo_t finfo;
SVN_ERR (svn_wc_adm_probe_open2 (&adm_access, NULL, from, FALSE,
0, pool));
err = svn_wc_entry (&entry, from, adm_access, FALSE, pool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_NOT_DIRECTORY)
return err;
else
svn_error_clear (err);
}
/* We don't want to copy some random non-versioned directory. */
if (! entry)
{
SVN_ERR (svn_wc_adm_close (adm_access));
return SVN_NO_ERROR;
}
/* Only export directories with the 'added' status if revision
is WORKING. Otherwise, skip it, as it doesn't exist in any
revision other than WORKING. */
if (revision->kind != svn_opt_revision_working &&
entry->schedule == svn_wc_schedule_add)
return SVN_NO_ERROR;
/* Try to make the new directory. If this fails because the
directory already exists, check our FORCE flag to see if we
care. */
SVN_ERR (svn_io_stat (&finfo, from, APR_FINFO_PROT, pool));
err = svn_io_dir_make (to, finfo.protection, pool);
if (err)
{
if (! APR_STATUS_IS_EEXIST (err->apr_err))
return err;
if (! force)
SVN_ERR_W (err, _("Destination directory exists, and will not be "
"overwritten unless forced"));
else
svn_error_clear (err);
}
SVN_ERR (svn_io_get_dirents (&dirents, from, pool));
iterpool = svn_pool_create (pool);
for (hi = apr_hash_first (pool, dirents); hi; hi = apr_hash_next (hi))
{
const svn_node_kind_t *type;
const char *item;
const void *key;
void *val;
svn_pool_clear (iterpool);
apr_hash_this (hi, &key, NULL, &val);
item = key;
type = val;
if (ctx->cancel_func)
SVN_ERR (ctx->cancel_func (ctx->cancel_baton));
/* ### We could also invoke ctx->notify_func somewhere in
### here... Is it called for, though? Not sure. */
if (*type == svn_node_dir)
{
if (strcmp (item, SVN_WC_ADM_DIR_NAME) == 0)
{
; /* skip this, it's an administrative directory. */
}
else
{
const char *new_from = svn_path_join (from, key, iterpool);
const char *new_to = svn_path_join (to, key, iterpool);
SVN_ERR (copy_versioned_files (new_from, new_to, revision,
force, native_eol, ctx,
iterpool));
}
}
else if (*type == svn_node_file)
{
const char *copy_from = svn_path_join (from, item, iterpool);
const char *copy_to = svn_path_join (to, item, iterpool);
svn_subst_keywords_t kw = { 0 };
svn_subst_eol_style_t style;
apr_hash_t *props;
const char *base;
svn_string_t *eol_style, *keywords, *executable, *externals, *special;
const char *eol = NULL;
svn_boolean_t local_mod = FALSE;
apr_time_t tm;
err = svn_wc_entry (&entry, copy_from, adm_access, FALSE, iterpool);
if (err)
{
if (err->apr_err != SVN_ERR_WC_NOT_FILE)
return err;
svn_error_clear (err);
}
/* Only export 'added' files when the revision is WORKING.
Otherwise, skip the 'added' files, since they didn't exist
in the BASE revision and don't have an associated text-base. */
if (! entry || (revision->kind != svn_opt_revision_working &&
entry->schedule == svn_wc_schedule_add))
continue;
if (revision->kind != svn_opt_revision_working)
{
SVN_ERR (svn_wc_get_pristine_copy_path (copy_from, &base,
iterpool));
SVN_ERR (svn_wc_get_prop_diffs (NULL, &props, copy_from,
adm_access, iterpool));
}
else
{
svn_wc_status_t *status;
base = copy_from;
SVN_ERR (svn_wc_prop_list (&props, copy_from,
adm_access, iterpool));
SVN_ERR (svn_wc_status (&status, copy_from,
adm_access, iterpool));
if (status->text_status != svn_wc_status_normal)
local_mod = TRUE;
}
eol_style = apr_hash_get (props, SVN_PROP_EOL_STYLE,
APR_HASH_KEY_STRING);
keywords = apr_hash_get (props, SVN_PROP_KEYWORDS,
APR_HASH_KEY_STRING);
executable = apr_hash_get (props, SVN_PROP_EXECUTABLE,
APR_HASH_KEY_STRING);
externals = apr_hash_get (props, SVN_PROP_EXTERNALS,
APR_HASH_KEY_STRING);
special = apr_hash_get (props, SVN_PROP_SPECIAL,
APR_HASH_KEY_STRING);
if (eol_style)
SVN_ERR (get_eol_style (&style, &eol, eol_style->data, native_eol));
if (local_mod && (! special))
{
/* Use the modified time from the working copy if
the file */
SVN_ERR (svn_io_file_affected_time (&tm, copy_from, iterpool));
}
else
{
tm = entry->cmt_date;
}
if (keywords)
{
const char *fmt;
const char *author;
if (local_mod)
{
/* For locally modified files, we'll append an 'M'
to the revision number, and set the author to
"(local)" since we can't always determine the
current user's username */
fmt = "%ldM";
author = _("(local)");
}
else
{
fmt = "%ld";
author = entry->cmt_author;
}
SVN_ERR (svn_subst_build_keywords
(&kw, keywords->data,
apr_psprintf (iterpool, fmt, entry->cmt_rev),
entry->url, tm, author, iterpool));
}
SVN_ERR (svn_subst_copy_and_translate2 (base, copy_to, eol, FALSE,
&kw, TRUE,
special ? TRUE : FALSE,
iterpool));
if (executable)
SVN_ERR (svn_io_set_file_executable (copy_to, TRUE,
FALSE, iterpool));
if (! special)
SVN_ERR (svn_io_set_file_affected_time (tm, copy_to, iterpool));
}
}
svn_pool_destroy (iterpool);
SVN_ERR (svn_wc_adm_close (adm_access));
return SVN_NO_ERROR;
}
/* Abstraction of open_root.
*
* Create PATH if it does not exist and is not obstructed, and invoke
* NOTIFY_FUNC with NOTIFY_BATON on PATH.
*
* If PATH exists but is a file, then error with SVN_ERR_WC_NOT_DIRECTORY.
*
* If PATH is a already a directory, then error with
* SVN_ERR_WC_OBSTRUCTED_UPDATE, unless FORCE, in which case just
* export into PATH with no error.
*/
static svn_error_t *
open_root_internal (const char *path,
svn_boolean_t force,
svn_wc_notify_func_t notify_func,
void *notify_baton,
apr_pool_t *pool)
{
svn_node_kind_t kind;
SVN_ERR (svn_io_check_path (path, &kind, pool));
if (kind == svn_node_none)
SVN_ERR (svn_io_dir_make (path, APR_OS_DEFAULT, pool));
else if (kind == svn_node_file)
return svn_error_createf (SVN_ERR_WC_NOT_DIRECTORY, NULL,
_("'%s' exists and is not a directory"), path);
else if ((kind != svn_node_dir) || (! force))
return svn_error_createf (SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
_("'%s' already exists"), path);
if (notify_func)
(*notify_func) (notify_baton,
path,
svn_wc_notify_update_add,
svn_node_dir,
NULL,
svn_wc_notify_state_unknown,
svn_wc_notify_state_unknown,
SVN_INVALID_REVNUM);
return SVN_NO_ERROR;
}
/* ---------------------------------------------------------------------- */
/*** A dedicated 'export' editor, which does no .svn/ accounting. ***/
struct edit_baton
{
const char *root_path;
const char *root_url;
svn_boolean_t force;
svn_revnum_t *target_revision;
apr_hash_t *externals;
const char *native_eol;
svn_wc_notify_func_t notify_func;
void *notify_baton;
};
struct dir_baton
{
struct edit_baton *edit_baton;
const char *path;
};
struct file_baton
{
struct edit_baton *edit_baton;
const char *path;
const char *tmppath;
/* We need to keep this around so we can explicitly close it in close_file,
thus flushing its output to disk so we can copy and translate it. */
apr_file_t *tmp_file;
/* The MD5 digest of the file's fulltext. This is all zeros until
the last textdelta window handler call returns. */
unsigned char text_digest[APR_MD5_DIGESTSIZE];
/* The three svn: properties we might actually care about. */
const svn_string_t *eol_style_val;
const svn_string_t *keywords_val;
const svn_string_t *executable_val;
svn_boolean_t special;
/* Any keyword vals to be substituted */
const char *revision;
const char *url;
const char *author;
apr_time_t date;
/* Pool associated with this baton. */
apr_pool_t *pool;
};
struct handler_baton
{
svn_txdelta_window_handler_t apply_handler;
void *apply_baton;
apr_pool_t *pool;
const char *tmppath;
};
static svn_error_t *
set_target_revision (void *edit_baton,
svn_revnum_t target_revision,
apr_pool_t *pool)
{
struct edit_baton *eb = edit_baton;
/* Stashing a target_revision in the baton */
*(eb->target_revision) = target_revision;
return SVN_NO_ERROR;
}
/* Just ensure that the main export directory exists. */
static svn_error_t *
open_root (void *edit_baton,
svn_revnum_t base_revision,
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?