[svn.haxx.se] · SVN Dev · SVN Users · SVN Org · TSVN Dev · TSVN Users · Subclipse Dev · Subclipse Users · this month's index

Re: svn commit: r10114 - in trunk: . subversion/include subversion/libsvn_client subversion/libsvn_subr subversion/libsvn_wc subversion/tests/clients/cmdline

From: <kfogel_at_collab.net>
Date: 2004-07-02 19:51:58 CEST

jpieper@tigris.org writes:
> Log:
> Issue #677: Allow special files such as symlinks to be added to
> working copies on systems that support them. The special files are
> stored in the repository and text-base as normal files with the
> svn:special property set. The information necessary to recreate the
> special files is stored in the contents of the normal file.

Bravo!

A documentation comment, regarding the entire change:

There needs to be some way for someone new to the code base to
understand how these 'special' files work. Right now, one must trace
down to a fairly deep level of code in order to make sense of what's
going on, namely that:

   - Certain files are 'special'.

   - There are different types of specialness.

   - The first chunk of content (whitespace delimited) in a special
     file indicates which type of specialness is in effect.

   - The remainder of the content supplies whatever other information
     that type requires (e.g., for a symlink, it's the target path).

Nowhere is this system documented as a whole. For examlpe, even if
one makes it to the doc string for SVN_SUBST__SPECIAL_LINK_STR, it's
not clear that whitespace is the delimiter -- you have to read a
comment in create_special_file() to know that :-).

I guess 'svn_node_special' in svn_types.h is the right central
location for such documentation. Some other places might want to
refer to it explicitly, when it's not obvious from context that
there's a relevant node kind to look at.

-Karl

> --- trunk/subversion/include/svn_io.h (original)
> +++ trunk/subversion/include/svn_io.h Thu Jul 1 06:52:54 2004
> @@ -59,6 +59,16 @@
> svn_node_kind_t *kind,
> apr_pool_t *pool);
>
> +/**
> + * @since New in 1.1.
> + *
> + * Like svn_io_check_path(), but return the actual node type for any
> + * special files.
> + */
> +svn_error_t *svn_io_check_special_path (const char *path,
> + svn_node_kind_t *kind,
> + apr_pool_t *pool);
> +
> /** Like svn_io_check_path(), but resolve symlinks. This returns the
> same varieties of @a kind as svn_io_check_path(). */
> svn_error_t *svn_io_check_resolved_path (const char *path,
> @@ -118,6 +128,29 @@
> const char *suffix,
> svn_boolean_t delete_on_close,
> apr_pool_t *pool);
> +
> +/**
> + * @since New in 1.1.
> + *
> + * Like svn_io_open_unique_file, except that instead of creating a
> + * file, a symlink is generated that references the path @a dest.
> + */
> +svn_error_t *svn_io_create_unique_link (const char **unique_name_p,
> + const char *path,
> + const char *dest,
> + const char *suffix,
> + apr_pool_t *pool);
> +
> +
> +/**
> + * @since New in 1.1.
> + *
> + * Set @a dest to the path that the symlink at @a path references.
> + * Allocate the string from @a pool.
> + */
> +svn_error_t *svn_io_read_link (svn_string_t **dest,
> + const char *path,
> + apr_pool_t *pool);
>
>
> /** Set @a dir to a directory path (allocated in @a pool) deemed
>
> Modified: trunk/subversion/include/svn_props.h
> Url: http://svn.collab.net/viewcvs/svn/trunk/subversion/include/svn_props.h?view=diff&rev=10114&p1=trunk/subversion/include/svn_props.h&r1=10113&p2=trunk/subversion/include/svn_props.h&r2=10114
> ==============================================================================
> --- trunk/subversion/include/svn_props.h (original)
> +++ trunk/subversion/include/svn_props.h Thu Jul 1 06:52:54 2004
> @@ -182,6 +182,12 @@
> /** The value to force the executable property to when set */
> #define SVN_PROP_EXECUTABLE_VALUE "*"
>
> +/** Set if the file should be treated as a special file. */
> +#define SVN_PROP_SPECIAL SVN_PROP_PREFIX "special"
> +
> +/** The value to force the special property to when set. */
> +#define SVN_PROP_SPECIAL_VALUE "*"
> +
> /** Describes external items to check out into this directory.
> *
> * The format is a series of lines, such as:
>
> Modified: trunk/subversion/include/svn_subst.h
> Url: http://svn.collab.net/viewcvs/svn/trunk/subversion/include/svn_subst.h?view=diff&rev=10114&p1=trunk/subversion/include/svn_subst.h&r1=10113&p2=trunk/subversion/include/svn_subst.h&r2=10114
> ==============================================================================
> --- trunk/subversion/include/svn_subst.h (original)
> +++ trunk/subversion/include/svn_subst.h Thu Jul 1 06:52:54 2004
> @@ -163,8 +163,28 @@
> svn_boolean_t expand);
>
>
> -/** Convenience routine: a variant of @c svn_subst_translate_stream which
> - * operates on files. (See previous docstring for details.)
> +/**
> + * @deprecated Provided for backward compatibility with the 1.0.0 API.
> + *
> + * Similar to svn_subst_copy_and_translate2 except that @a special is
> + * always set to @c FALSE.
> + */
> +svn_error_t *
> +svn_subst_copy_and_translate (const char *src,
> + const char *dst,
> + const char *eol_str,
> + svn_boolean_t repair,
> + const svn_subst_keywords_t *keywords,
> + svn_boolean_t expand,
> + apr_pool_t *pool);
> +
> +/**
> + * @since New in 1.1.
> + *
> + * Convenience routine: a variant of @c svn_subst_translate_stream
> + * which operates on files. (See previous docstring for details.) In
> + * addition, it will create/detranslate a special file if @a special
> + * is @c TRUE.
> *
> * Copy the contents of file-path @a src to file-path @a dst atomically,
> * either creating @a dst (or overwriting @a dst if it exists), possibly
> @@ -177,13 +197,14 @@
> * copy.
> */
> svn_error_t *
> -svn_subst_copy_and_translate (const char *src,
> - const char *dst,
> - const char *eol_str,
> - svn_boolean_t repair,
> - const svn_subst_keywords_t *keywords,
> - svn_boolean_t expand,
> - apr_pool_t *pool);
> +svn_subst_copy_and_translate2 (const char *src,
> + const char *dst,
> + const char *eol_str,
> + svn_boolean_t repair,
> + const svn_subst_keywords_t *keywords,
> + svn_boolean_t expand,
> + svn_boolean_t special,
> + apr_pool_t *pool);
>
> /** Convenience routine: a variant of @c svn_subst_translate_stream which
> * operates on cstrings. (See previous docstring for details.)
>
> Modified: trunk/subversion/include/svn_types.h
> Url: http://svn.collab.net/viewcvs/svn/trunk/subversion/include/svn_types.h?view=diff&rev=10114&p1=trunk/subversion/include/svn_types.h&r1=10113&p2=trunk/subversion/include/svn_types.h&r2=10114
> ==============================================================================
> --- trunk/subversion/include/svn_types.h (original)
> +++ trunk/subversion/include/svn_types.h Thu Jul 1 06:52:54 2004
> @@ -86,7 +86,10 @@
> svn_node_dir,
>
> /* something's here, but we don't know what */
> - svn_node_unknown
> + svn_node_unknown,
> +
> + /* a special file */
> + svn_node_special
>
> } svn_node_kind_t;
>
>
> Modified: trunk/subversion/libsvn_client/add.c
> Url: http://svn.collab.net/viewcvs/svn/trunk/subversion/libsvn_client/add.c?view=diff&rev=10114&p1=trunk/subversion/libsvn_client/add.c&r1=10113&p2=trunk/subversion/libsvn_client/add.c&r2=10114
> ==============================================================================
> --- trunk/subversion/libsvn_client/add.c (original)
> +++ trunk/subversion/libsvn_client/add.c Thu Jul 1 06:52:54 2004
> @@ -205,27 +205,44 @@
> apr_hash_t* properties;
> apr_hash_index_t *hi;
> const char *mimetype;
> + svn_node_kind_t kind;
>
> /* add the file */
> SVN_ERR (svn_wc_add (path, adm_access, NULL, SVN_INVALID_REVNUM,
> ctx->cancel_func, ctx->cancel_baton,
> NULL, NULL, pool));
> - /* get automatic properties */
> - SVN_ERR (svn_client__get_auto_props (&properties, &mimetype, path, ctx,
> - pool));
> - if (properties)
> +
> + /* Check to see if this is a special file. */
> + SVN_ERR (svn_io_check_special_path (path, &kind, pool));
> +
> + if (kind == svn_node_special)
> + {
> + /* This must be a special file. */
> + SVN_ERR (svn_wc_prop_set (SVN_PROP_SPECIAL,
> + svn_string_create (SVN_PROP_SPECIAL_VALUE, pool),
> + path, adm_access, pool));
> + mimetype = NULL;
> + }
> + else
> {
> - /* loop through the hashtable and add the properties */
> - for (hi = apr_hash_first (pool, properties);
> - hi != NULL; hi = apr_hash_next (hi))
> + /* get automatic properties */
> + SVN_ERR (svn_client__get_auto_props (&properties, &mimetype, path, ctx,
> + pool));
> + if (properties)
> {
> - const void *pname;
> - void *pval;
> -
> - apr_hash_this (hi, &pname, NULL, &pval);
> - SVN_ERR (svn_wc_prop_set (pname, pval, path, adm_access, pool));
> + /* loop through the hashtable and add the properties */
> + for (hi = apr_hash_first (pool, properties);
> + hi != NULL; hi = apr_hash_next (hi))
> + {
> + const void *pname;
> + void *pval;
> +
> + apr_hash_this (hi, &pname, NULL, &pval);
> + SVN_ERR (svn_wc_prop_set (pname, pval, path, adm_access, pool));
> + }
> }
> }
> +
> /* Report the addition to the caller. */
> if (ctx->notify_func != NULL)
> (*ctx->notify_func) (ctx->notify_baton, path, svn_wc_notify_add,
> @@ -304,7 +321,7 @@
> SVN_ERR (add_dir_recursive (fullpath, dir_access, force,
> ctx, subpool));
> }
> - else if (this_entry.filetype == APR_REG)
> + else if (this_entry.filetype != APR_UNKFILE)
> {
> err = add_file (fullpath, ctx, dir_access, subpool);
> if (err && err->apr_err == SVN_ERR_ENTRY_EXISTS && force)
>
> Modified: trunk/subversion/libsvn_client/commit_util.c
> Url: http://svn.collab.net/viewcvs/svn/trunk/subversion/libsvn_client/commit_util.c?view=diff&rev=10114&p1=trunk/subversion/libsvn_client/commit_util.c&r1=10113&p2=trunk/subversion/libsvn_client/commit_util.c&r2=10114
> ==============================================================================
> --- trunk/subversion/libsvn_client/commit_util.c (original)
> +++ trunk/subversion/libsvn_client/commit_util.c Thu Jul 1 06:52:54 2004
> @@ -189,6 +189,7 @@
> svn_boolean_t tc, pc;
> const char *cf_url = NULL;
> svn_revnum_t cf_rev = entry->copyfrom_rev;
> + const svn_string_t *propval;
>
> /* Early out if the item is already marked as committable. */
> if (look_up_committable (committables, path, pool))
> @@ -210,15 +211,30 @@
> return svn_error_createf
> (SVN_ERR_NODE_UNKNOWN_KIND, NULL, _("Unknown entry kind for '%s'"), path);
>
> - SVN_ERR (svn_io_check_path (path, &kind, pool));
> + SVN_ERR (svn_io_check_special_path (path, &kind, pool));
>
> if ((kind != svn_node_file)
> && (kind != svn_node_dir)
> - && (kind != svn_node_none))
> + && (kind != svn_node_none)
> + && (kind != svn_node_special))
> {
> return svn_error_createf
> (SVN_ERR_NODE_UNKNOWN_KIND, NULL,
> _("Unknown entry kind for '%s'"), path);
> + }
> +
> + /* Verify that the node's type has not changed before attempting to
> + commit. */
> + SVN_ERR (svn_wc_prop_get (&propval, SVN_PROP_SPECIAL, path, adm_access,
> + pool));
> +
> + if ((entry->kind == svn_node_file)
> + && (! propval)
> + && (kind == svn_node_special))
> + {
> + return svn_error_createf
> + (SVN_ERR_NODE_UNEXPECTED_KIND, NULL,
> + _("Entry '%s' has been replaced by a special file"), path);
> }
>
> /* Get a fully populated entry for PATH if we can, and check for
>
> Modified: trunk/subversion/libsvn_subr/io.c
> Url: http://svn.collab.net/viewcvs/svn/trunk/subversion/libsvn_subr/io.c?view=diff&rev=10114&p1=trunk/subversion/libsvn_subr/io.c&r1=10113&p2=trunk/subversion/libsvn_subr/io.c&r2=10114
> ==============================================================================
> --- trunk/subversion/libsvn_subr/io.c (original)
> +++ trunk/subversion/libsvn_subr/io.c Thu Jul 1 06:52:54 2004
> @@ -57,6 +57,7 @@
> #include "svn_pools.h"
> #include "svn_utf.h"
> #include "svn_config.h"
> +#include "svn_private_config.h"
>
>
> /*
> @@ -98,6 +99,7 @@
> static svn_error_t *
> io_check_path (const char *path,
> svn_boolean_t resolve_symlinks,
> + svn_boolean_t expand_special,
> svn_node_kind_t *kind,
> apr_pool_t *pool)
> {
> @@ -128,10 +130,8 @@
> *kind = svn_node_file;
> else if (finfo.filetype == APR_DIR)
> *kind = svn_node_dir;
> -#if 0
> - else if (finfo.filetype == APR_LINK)
> - *kind = svn_node_symlink; /* we support symlinks someday, but not yet */
> -#endif /* 0 */
> + else if (finfo.filetype == APR_LNK)
> + *kind = expand_special ? svn_node_special : svn_node_file;
> else
> *kind = svn_node_unknown;
>
> @@ -144,7 +144,7 @@
> svn_node_kind_t *kind,
> apr_pool_t *pool)
> {
> - return io_check_path (path, TRUE, kind, pool);
> + return io_check_path (path, TRUE, FALSE, kind, pool);
> }
>
> svn_error_t *
> @@ -152,9 +152,16 @@
> svn_node_kind_t *kind,
> apr_pool_t *pool)
> {
> - return io_check_path (path, FALSE, kind, pool);
> + return io_check_path (path, FALSE, FALSE, kind, pool);
> }
>
> +svn_error_t *
> +svn_io_check_special_path (const char *path,
> + svn_node_kind_t *kind,
> + apr_pool_t *pool)
> +{
> + return io_check_path (path, FALSE, TRUE, kind, pool);
> +}
>
> svn_error_t *
> svn_io_open_unique_file (apr_file_t **f,
> @@ -242,6 +249,121 @@
> "Unable to make name for '%s'", path);
> }
>
> +svn_error_t *
> +svn_io_create_unique_link (const char **unique_name_p,
> + const char *path,
> + const char *dest,
> + const char *suffix,
> + apr_pool_t *pool)
> +{
> +#ifdef HAVE_SYMLINK
> + unsigned int i;
> + const char *unique_name;
> + const char *unique_name_apr;
> + int rv;
> +
> + for (i = 1; i <= 99999; i++)
> + {
> + apr_status_t apr_err;
> +
> + /* Special case the first attempt -- if we can avoid having a
> + generated numeric portion at all, that's best. So first we
> + try with just the suffix; then future tries add a number
> + before the suffix. (A do-while loop could avoid the repeated
> + conditional, but it's not worth the clarity loss.)
> +
> + If the first attempt fails, the first number will be "2".
> + This is good, since "1" would misleadingly imply that
> + the second attempt was actually the first... and if someone's
> + got conflicts on their conflicts, we probably don't want to
> + add to their confusion :-). */
> + if (i == 1)
> + unique_name = apr_psprintf (pool, "%s%s", path, suffix);
> + else
> + unique_name = apr_psprintf (pool, "%s.%u%s", path, i, suffix);
> +
> + /* Hmmm. Ideally, we would append to a native-encoding buf
> + before starting iteration, then convert back to UTF-8 for
> + return. But I suppose that would make the appending code
> + sensitive to i18n in a way it shouldn't be... Oh well. */
> + SVN_ERR (svn_path_cstring_from_utf8 (&unique_name_apr, unique_name,
> + pool));
> +
> + do {
> + rv = symlink (dest, unique_name_apr);
> + } while (rv == -1 && APR_STATUS_IS_EINTR (apr_get_os_error ()));
> +
> + apr_err = apr_get_os_error();
> +
> + if (APR_STATUS_IS_EEXIST (apr_err))
> + continue;
> + else if (rv == -1 && apr_err)
> + {
> + /* On Win32, CreateFile failswith an "Access Denied" error
> + code, rather than "File Already Exists", if the colliding
> + name belongs to a directory. */
> + if (APR_STATUS_IS_EACCES (apr_err))
> + {
> + apr_finfo_t finfo;
> + apr_status_t apr_err_2 = apr_stat (&finfo, unique_name_apr,
> + APR_FINFO_TYPE, pool);
> +
> + if (APR_STATUS_IS_SUCCESS (apr_err_2)
> + && (finfo.filetype == APR_DIR))
> + continue;
> +
> + /* Else ignore apr_err_2; better to fall through and
> + return the original error. */
> + }
> +
> + *unique_name_p = NULL;
> + return svn_error_wrap_apr (apr_err, "Can't open '%s'", unique_name);
> + }
> + else
> + {
> + *unique_name_p = unique_name;
> + return SVN_NO_ERROR;
> + }
> + }
> +
> + *unique_name_p = NULL;
> + return svn_error_createf (SVN_ERR_IO_UNIQUE_NAMES_EXHAUSTED,
> + NULL,
> + "Unable to make name for '%s'", path);
> +#else
> + return svn_error_create (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
> + "Symbolic links are not supported on this "
> + "platform");
> +#endif
> +}
> +
> +svn_error_t *
> +svn_io_read_link (svn_string_t **dest,
> + const char *path,
> + apr_pool_t *pool)
> +{
> +#ifdef HAVE_READLINK
> + char buf[1024];
> + int rv;
> +
> + do {
> + rv = readlink (path, buf, sizeof(buf));
> + } while (rv == -1 && APR_STATUS_IS_EINTR (apr_get_os_error ()));
> +
> + if (rv == -1)
> + return svn_error_wrap_apr
> + (apr_get_os_error (), "Can't read contents of link");
> +
> + *dest = svn_string_ncreate (buf, rv, pool);
> +
> + return SVN_NO_ERROR;
> +#else
> + return svn_error_create (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
> + "Symbolic links are not supported on this "
> + "platform");
> +#endif
> +}
> +
> #if 1 /* TODO: Remove this code when APR 0.9.6 is released. */
> #include "apr_env.h"
>
> @@ -635,7 +757,7 @@
> {
> apr_finfo_t finfo;
>
> - SVN_ERR (svn_io_stat (&finfo, path, APR_FINFO_MIN, pool));
> + SVN_ERR (svn_io_stat (&finfo, path, APR_FINFO_MIN | APR_FINFO_LINK, pool));
>
> *apr_time = finfo.mtime;
>
> @@ -1317,7 +1439,6 @@
> /* These exist so we can use their addresses as hash values! */
> static const svn_node_kind_t static_svn_node_file = svn_node_file;
> static const svn_node_kind_t static_svn_node_dir = svn_node_dir;
> - static const svn_node_kind_t static_svn_node_unknown = svn_node_unknown;
>
> *dirents = apr_hash_make (pool);
>
> @@ -1350,7 +1471,7 @@
> /* ### symlinks, etc. will fall into this category for now.
> someday subversion will recognize them. :) */
> apr_hash_set (*dirents, name, APR_HASH_KEY_STRING,
> - &static_svn_node_unknown);
> + &static_svn_node_file);
> }
> }
>
>
> Modified: trunk/subversion/libsvn_subr/subst.c
> Url: http://svn.collab.net/viewcvs/svn/trunk/subversion/libsvn_subr/subst.c?view=diff&rev=10114&p1=trunk/subversion/libsvn_subr/subst.c&r1=10113&p2=trunk/subversion/libsvn_subr/subst.c&r2=10114
> ==============================================================================
> --- trunk/subversion/libsvn_subr/subst.c (original)
> +++ trunk/subversion/libsvn_subr/subst.c Thu Jul 1 06:52:54 2004
> @@ -43,6 +43,13 @@
> #include "svn_subst.h"
> #include "svn_pools.h"
>
> +/**
> + * The textual elements of a detranslated special file. One of these
> + * strings must appear as the first element of any special file as it
> + * exists in the repository or the text base.
> + */
> +#define SVN_SUBST__SPECIAL_LINK_STR "link"
> +
> void
> svn_subst_eol_style_from_value (svn_subst_eol_style_t *style,
> const char **eol,
> @@ -706,11 +713,162 @@
> svn_boolean_t expand,
> apr_pool_t *pool)
> {
> + return svn_subst_copy_and_translate2 (src, dst, eol_str, repair, keywords,
> + expand, FALSE, pool);
> +}
> +
> +/* Given a file containing a repository representation of a special
> + file in SRC, create the appropriate special file at location DST.
> + Perform all allocations in POOL. */
> +static svn_error_t *
> +create_special_file (const char *src,
> + const char *dst,
> + apr_pool_t *pool)
> +{
> + svn_stringbuf_t *contents;
> + char *identifier, *remainder;
> + const char *dst_tmp;
> + svn_error_t *err;
> +
> + /* Read in the detranslated file. */
> + SVN_ERR (svn_stringbuf_from_file (&contents, src, pool));
> +
> + /* Separate off the identifier. The first space character delimits
> + the identifier, after which any remaining characters are specific
> + to the actual special device being created. */
> + identifier = contents->data;
> + for (remainder = identifier; *remainder; remainder++)
> + {
> + if (*remainder == ' ')
> + {
> + *remainder = '\0';
> + remainder++;
> + break;
> + }
> + }
> +
> + if (! strcmp (identifier, SVN_SUBST__SPECIAL_LINK_STR))
> + {
> + /* For symlinks, the type specific data is just a filesystem
> + path that the symlink should reference. */
> + err = svn_io_create_unique_link (&dst_tmp, dst, remainder,
> + ".tmp", pool);
> + }
> + else
> + {
> + /* We should return a valid error here. */
> + return svn_error_createf (SVN_ERR_UNSUPPORTED_FEATURE, NULL,
> + "Unsupported special file type '%s'",
> + identifier);
> + }
> +
> + /* If we had an error, check to see if it was because this type of
> + special device is not supported. */
> + if (err)
> + {
> + if (err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE)
> + {
> + apr_file_t *fp;
> +
> + svn_error_clear (err);
> + /* Fall back to just copying the text-base. */
> + SVN_ERR (svn_io_open_unique_file (&fp, &dst_tmp, dst, ".tmp", FALSE,
> + pool));
> + SVN_ERR (svn_io_file_close (fp, pool));
> + SVN_ERR (svn_io_copy_file (src, dst_tmp, TRUE, pool));
> + }
> + else
> + return err;
> + }
> +
> + /* Do the atomic rename from our temporary location. */
> + SVN_ERR (svn_io_file_rename (dst_tmp, dst, pool));
> +
> + return SVN_NO_ERROR;
> +}
> +
> +
> +/* Given a special file at SRC, generate a textual representation of
> + it in a normal file at DST. Perform all allocations in POOL. */
> +static svn_error_t *
> +detranslate_special_file (const char *src,
> + const char *dst,
> + apr_pool_t *pool)
> +{
> + const char *dst_tmp;
> + svn_string_t *buf;
> + apr_file_t *s, *d;
> + svn_stream_t *src_stream, *dst_stream;
> + apr_finfo_t finfo;
> +
> + /* First determine what type of special file we are
> + detranslating. */
> + SVN_ERR (svn_io_stat (&finfo, src, APR_FINFO_MIN | APR_FINFO_LINK, pool));
> +
> + /* Open a temporary destination that we will eventually atomically
> + rename into place. */
> + SVN_ERR (svn_io_open_unique_file (&d, &dst_tmp, dst,
> + ".tmp", FALSE, pool));
> +
> + dst_stream = svn_stream_from_aprfile (d, pool);
> +
> + switch (finfo.filetype) {
> + case APR_REG:
> + /* Nothing special to do here, just copy the original file's
> + contents. */
> + SVN_ERR (svn_io_file_open (&s, src, APR_READ | APR_BUFFERED,
> + APR_OS_DEFAULT, pool));
> + src_stream = svn_stream_from_aprfile (d, pool);
> +
> + SVN_ERR (svn_stream_copy (src_stream, dst_stream, pool));
> + break;
> + case APR_LNK:
> + /* Determine the destination of the link. */
> + SVN_ERR (svn_io_read_link (&buf, src, pool));
> +
> + SVN_ERR (svn_stream_printf (dst_stream, pool, "link %s",
> + buf->data));
> + break;
> + default:
> + abort ();
> + }
> +
> + SVN_ERR (svn_io_file_close (d, pool));
> +
> + /* Do the atomic rename from our temporary location. */
> + SVN_ERR (svn_io_file_rename (dst_tmp, dst, pool));
> +
> + return SVN_NO_ERROR;
> +}
> +
> +
> +svn_error_t *
> +svn_subst_copy_and_translate2 (const char *src,
> + const char *dst,
> + const char *eol_str,
> + svn_boolean_t repair,
> + const svn_subst_keywords_t *keywords,
> + svn_boolean_t expand,
> + svn_boolean_t special,
> + apr_pool_t *pool)
> +{
> const char *dst_tmp = NULL;
> svn_stream_t *src_stream, *dst_stream;
> apr_file_t *s = NULL, *d = NULL; /* init to null important for APR */
> svn_error_t *err;
> apr_pool_t *subpool;
> +
> + if (special)
> + {
> + /* If this is a 'special' file, we may need to create it or
> + detranslate it. */
> + if (expand)
> + SVN_ERR (create_special_file (src, dst, pool));
> + else
> + SVN_ERR (detranslate_special_file (src, dst, pool));
> +
> + return SVN_NO_ERROR;
> + }
>
> /* The easy way out: no translation needed, just copy. */
> if (! (eol_str || keywords))
>
> Modified: trunk/subversion/libsvn_wc/adm_crawler.c
> Url: http://svn.collab.net/viewcvs/svn/trunk/subversion/libsvn_wc/adm_crawler.c?view=diff&rev=10114&p1=trunk/subversion/libsvn_wc/adm_crawler.c&r1=10113&p2=trunk/subversion/libsvn_wc/adm_crawler.c&r2=10114
> ==============================================================================
> --- trunk/subversion/libsvn_wc/adm_crawler.c (original)
> +++ trunk/subversion/libsvn_wc/adm_crawler.c Thu Jul 1 06:52:54 2004
> @@ -69,6 +69,7 @@
> apr_time_t tstamp;
> const char *bname;
> apr_uint32_t modify_flags = 0;
> + svn_boolean_t special;
>
> text_base_path = svn_wc__text_base_path (file_path, FALSE, pool);
> tmp_text_base_path = svn_wc__text_base_path (file_path, TRUE, pool);
> @@ -80,17 +81,20 @@
> SVN_ERR (svn_wc__get_eol_style (NULL, &eol, file_path, adm_access, pool));
> SVN_ERR (svn_wc__get_keywords (&keywords,
> file_path, adm_access, NULL, pool));
> + SVN_ERR (svn_wc__get_special (&special, file_path, adm_access, pool));
> +
>
> /* When copying the tmp-text-base out to the working copy, make
> sure to do any eol translations or keyword substitutions,
> as dictated by the property values. If these properties
> are turned off, then this is just a normal copy. */
> - SVN_ERR (svn_subst_copy_and_translate (tmp_text_base_path,
> - file_path,
> - eol, FALSE, /* don't repair */
> - keywords,
> - TRUE, /* expand keywords */
> - pool));
> + SVN_ERR (svn_subst_copy_and_translate2 (tmp_text_base_path,
> + file_path,
> + eol, FALSE, /* don't repair */
> + keywords,
> + TRUE, /* expand keywords */
> + special,
> + pool));
>
> SVN_ERR (svn_io_remove_file (tmp_text_base_path, pool));
>
>
> Modified: trunk/subversion/libsvn_wc/adm_ops.c
> Url: http://svn.collab.net/viewcvs/svn/trunk/subversion/libsvn_wc/adm_ops.c?view=diff&rev=10114&p1=trunk/subversion/libsvn_wc/adm_ops.c&r1=10113&p2=trunk/subversion/libsvn_wc/adm_ops.c&r2=10114
> ==============================================================================
> --- trunk/subversion/libsvn_wc/adm_ops.c (original)
> +++ trunk/subversion/libsvn_wc/adm_ops.c Thu Jul 1 06:52:54 2004
> @@ -1245,23 +1245,26 @@
> file. */
> svn_subst_keywords_t *keywords;
> const char *eol;
> + svn_boolean_t special;
>
> SVN_ERR (svn_wc__get_eol_style (NULL, &eol, fullpath, adm_access,
> pool));
> SVN_ERR (svn_wc__get_keywords (&keywords, fullpath, adm_access, NULL,
> pool));
> + SVN_ERR (svn_wc__get_special (&special, fullpath, adm_access, pool));
>
> /* When copying the text-base out to the working copy, make
> sure to do any eol translations or keyword substitutions,
> as dictated by the property values. If these properties
> are turned off, then this is just a normal copy. */
> - if ((err = svn_subst_copy_and_translate (base_thing,
> - fullpath,
> - eol,
> - FALSE, /* don't repair */
> - keywords,
> - TRUE, /* expand keywords */
> - pool)))
> + if ((err = svn_subst_copy_and_translate2 (base_thing,
> + fullpath,
> + eol,
> + FALSE, /* don't repair */
> + keywords,
> + TRUE, /* expand keywords */
> + special,
> + pool)))
> return svn_error_quick_wrap
> (err, apr_psprintf (pool, _("Error restoring text for '%s'"),
> fullpath));
>
> Modified: trunk/subversion/libsvn_wc/log.c
> Url: http://svn.collab.net/viewcvs/svn/trunk/subversion/libsvn_wc/log.c?view=diff&rev=10114&p1=trunk/subversion/libsvn_wc/log.c&r1=10113&p2=trunk/subversion/libsvn_wc/log.c&r2=10114
> ==============================================================================
> --- trunk/subversion/libsvn_wc/log.c (original)
> +++ trunk/subversion/libsvn_wc/log.c Thu Jul 1 06:52:54 2004
> @@ -115,20 +115,24 @@
> {
> svn_subst_keywords_t *keywords;
> const char *eol_str;
> + svn_boolean_t special;
>
> /* Note that this action takes properties from dest, not source. */
> SVN_ERR (svn_wc__get_keywords (&keywords, full_dest_path, adm_access,
> NULL, pool));
> SVN_ERR (svn_wc__get_eol_style (NULL, &eol_str, full_dest_path,
> adm_access, pool));
> + SVN_ERR (svn_wc__get_special (&special, full_dest_path, adm_access,
> + pool));
>
> - SVN_ERR (svn_subst_copy_and_translate (full_from_path,
> - full_dest_path,
> - eol_str,
> - TRUE,
> - keywords,
> - TRUE,
> - pool));
> + SVN_ERR (svn_subst_copy_and_translate2 (full_from_path,
> + full_dest_path,
> + eol_str,
> + TRUE,
> + keywords,
> + TRUE,
> + special,
> + pool));
>
> /* After copying, set the file executable if props dictate. */
> return svn_wc__maybe_set_executable (NULL, full_dest_path, adm_access,
> @@ -139,23 +143,27 @@
> {
> svn_subst_keywords_t *keywords;
> const char *eol_str;
> + svn_boolean_t special;
>
> /* Note that this action takes properties from source, not dest. */
> SVN_ERR (svn_wc__get_keywords (&keywords, full_from_path, adm_access,
> NULL, pool));
> SVN_ERR (svn_wc__get_eol_style (NULL, &eol_str, full_from_path,
> adm_access, pool));
> + SVN_ERR (svn_wc__get_special (&special, full_from_path, adm_access,
> + pool));
>
> /* If any specific eol style was indicated, then detranslate
> back to repository normal form ("\n"), repairingly. But if
> no style indicated, don't touch line endings at all. */
> - return svn_subst_copy_and_translate (full_from_path,
> - full_dest_path,
> - (eol_str ? "\n" : NULL),
> - (eol_str ? TRUE : FALSE),
> - keywords,
> - FALSE, /* contract keywords */
> - pool);
> + return svn_subst_copy_and_translate2 (full_from_path,
> + full_dest_path,
> + (eol_str ? "\n" : NULL),
> + (eol_str ? TRUE : FALSE),
> + keywords,
> + FALSE, /* contract keywords */
> + special,
> + pool);
> }
>
> case svn_wc__xfer_mv:
> @@ -212,6 +220,7 @@
> svn_boolean_t same, did_set;
> const char *tmp_wfile, *pdir, *bname;
> const char *eol_str;
> + svn_boolean_t special;
>
> /* start off assuming that the working file isn't touched. */
> *overwrote_working = FALSE;
> @@ -238,6 +247,7 @@
> /* start off getting the latest translation prop values. */
> SVN_ERR (svn_wc__get_eol_style (NULL, &eol_str, filepath, adm_access, pool));
> SVN_ERR (svn_wc__get_keywords (&keywords, filepath, adm_access, NULL, pool));
> + SVN_ERR (svn_wc__get_special (&special, filepath, adm_access, pool));
>
> svn_path_split (filepath, &pdir, &bname, pool);
> tmp_wfile = svn_wc__adm_path (pdir, TRUE, pool, bname, NULL);
> @@ -252,23 +262,32 @@
> SVN_ERR (svn_io_check_path (tmp_text_base, &kind, pool));
>
> if (kind == svn_node_file)
> - SVN_ERR (svn_subst_copy_and_translate (tmp_text_base,
> - tmp_wfile,
> - eol_str,
> - FALSE, /* don't repair eol */
> - keywords,
> - TRUE, /* expand keywords */
> - pool));
> + SVN_ERR (svn_subst_copy_and_translate2 (tmp_text_base,
> + tmp_wfile,
> + eol_str,
> + FALSE, /* don't repair eol */
> + keywords,
> + TRUE, /* expand keywords */
> + special,
> + pool));
> else
> - SVN_ERR (svn_subst_copy_and_translate (filepath,
> - tmp_wfile,
> - eol_str,
> - FALSE, /* don't repair eol */
> - keywords,
> - TRUE, /* expand keywords */
> - pool));
> -
> - SVN_ERR (svn_io_files_contents_same_p (&same, tmp_wfile, filepath, pool));
> + SVN_ERR (svn_subst_copy_and_translate2 (filepath,
> + tmp_wfile,
> + eol_str,
> + FALSE, /* don't repair eol */
> + keywords,
> + TRUE, /* expand keywords */
> + special,
> + pool));
> +
> + if (! special)
> + {
> + SVN_ERR (svn_io_files_contents_same_p (&same, tmp_wfile, filepath, pool));
> + }
> + else
> + {
> + same = TRUE;
> + }
>
> if (! same)
> {
>
> Modified: trunk/subversion/libsvn_wc/merge.c
> Url: http://svn.collab.net/viewcvs/svn/trunk/subversion/libsvn_wc/merge.c?view=diff&rev=10114&p1=trunk/subversion/libsvn_wc/merge.c&r1=10113&p2=trunk/subversion/libsvn_wc/merge.c&r2=10114
> ==============================================================================
> --- trunk/subversion/libsvn_wc/merge.c (original)
> +++ trunk/subversion/libsvn_wc/merge.c Thu Jul 1 06:52:54 2004
> @@ -52,7 +52,7 @@
> svn_subst_keywords_t *keywords;
> const char *eol;
> const svn_wc_entry_t *entry;
> - svn_boolean_t contains_conflicts;
> + svn_boolean_t contains_conflicts, special;
>
> svn_path_split (merge_target, &mt_pt, &mt_bn, pool);
>
> @@ -234,14 +234,18 @@
> NULL, pool));
> SVN_ERR (svn_wc__get_eol_style (NULL, &eol, merge_target, adm_access,
> pool));
> - SVN_ERR (svn_subst_copy_and_translate (left,
> - left_copy,
> - eol, eol ? TRUE : FALSE,
> - keywords, TRUE, pool));
> - SVN_ERR (svn_subst_copy_and_translate (right,
> - right_copy,
> - eol, eol ? TRUE : FALSE,
> - keywords, TRUE, pool));
> + SVN_ERR (svn_wc__get_special (&special, merge_target, adm_access,
> + pool));
> + SVN_ERR (svn_subst_copy_and_translate2 (left,
> + left_copy,
> + eol, eol ? TRUE : FALSE,
> + keywords, TRUE, special,
> + pool));
> + SVN_ERR (svn_subst_copy_and_translate2 (right,
> + right_copy,
> + eol, eol ? TRUE : FALSE,
> + keywords, TRUE, special,
> + pool));
>
> /* Back up MERGE_TARGET verbatim (it's already in expanded form.) */
> SVN_ERR (svn_io_copy_file (merge_target,
> @@ -289,9 +293,12 @@
> NULL, pool));
> SVN_ERR (svn_wc__get_eol_style (NULL, &eol, merge_target, adm_access,
> pool));
> - SVN_ERR (svn_subst_copy_and_translate (result_target, merge_target,
> - eol, eol ? TRUE : FALSE,
> - keywords, TRUE, pool));
> + SVN_ERR (svn_wc__get_special (&special, merge_target, adm_access,
> + pool));
> + SVN_ERR (svn_subst_copy_and_translate2 (result_target, merge_target,
> + eol, eol ? TRUE : FALSE,
> + keywords, TRUE, special,
> + pool));
> }
>
> /* Don't forget to clean up tmp_target, result_target, tmp_left,
>
> Modified: trunk/subversion/libsvn_wc/status.c
> Url: http://svn.collab.net/viewcvs/svn/trunk/subversion/libsvn_wc/status.c?view=diff&rev=10114&p1=trunk/subversion/libsvn_wc/status.c&r1=10113&p2=trunk/subversion/libsvn_wc/status.c&r2=10114
> ==============================================================================
> --- trunk/subversion/libsvn_wc/status.c (original)
> +++ trunk/subversion/libsvn_wc/status.c Thu Jul 1 06:52:54 2004
> @@ -39,6 +39,7 @@
>
> #include "wc.h"
> #include "props.h"
> +#include "translate.h"
>
>
>
> @@ -211,6 +212,8 @@
> svn_boolean_t prop_modified_p = FALSE;
> svn_boolean_t locked_p = FALSE;
> svn_boolean_t switched_p = FALSE;
> + svn_boolean_t special;
> + svn_node_kind_t special_kind;
>
> /* Defaults for two main variables. */
> enum svn_wc_status_kind final_text_status = svn_wc_status_normal;
> @@ -219,6 +222,7 @@
> /* Check the path kind for PATH. */
> if (path_kind == svn_node_unknown)
> SVN_ERR (svn_io_check_path (path, &path_kind, pool));
> + SVN_ERR (svn_io_check_special_path (path, &special_kind, pool));
>
> if (! entry)
> {
> @@ -300,8 +304,11 @@
> SVN_ERR (svn_wc_props_modified_p (&prop_modified_p, path, adm_access,
> pool));
>
> + SVN_ERR (svn_wc__get_special (&special, path, adm_access, pool));
> +
> /* If the entry is a file, check for textual modifications */
> - if (entry->kind == svn_node_file)
> + if ((entry->kind == svn_node_file) &&
> + ((special ? svn_node_special : svn_node_file) == special_kind))
> SVN_ERR (svn_wc_text_modified_p (&text_modified_p, path, FALSE,
> adm_access, pool));
>
> @@ -380,6 +387,9 @@
> final_text_status = svn_wc_status_missing;
> }
> else if (path_kind != entry->kind)
> + final_text_status = svn_wc_status_obstructed;
> + else if ((special && (special_kind != svn_node_special))
> + || ((! special) && (special_kind == svn_node_special)))
> final_text_status = svn_wc_status_obstructed;
>
> if (path_kind == svn_node_dir && entry->kind == svn_node_dir)
>
> Modified: trunk/subversion/libsvn_wc/translate.c
> Url: http://svn.collab.net/viewcvs/svn/trunk/subversion/libsvn_wc/translate.c?view=diff&rev=10114&p1=trunk/subversion/libsvn_wc/translate.c&r1=10113&p2=trunk/subversion/libsvn_wc/translate.c&r2=10114
> ==============================================================================
> --- trunk/subversion/libsvn_wc/translate.c (original)
> +++ trunk/subversion/libsvn_wc/translate.c Thu Jul 1 06:52:54 2004
> @@ -57,11 +57,13 @@
> svn_subst_eol_style_t style;
> const char *eol;
> svn_subst_keywords_t *keywords;
> + svn_boolean_t special;
>
> SVN_ERR (svn_wc__get_eol_style (&style, &eol, vfile, adm_access, pool));
> SVN_ERR (svn_wc__get_keywords (&keywords, vfile, adm_access, NULL, pool));
> + SVN_ERR (svn_wc__get_special (&special, vfile, adm_access, pool));
>
> - if ((style == svn_subst_eol_style_none) && (! keywords))
> + if ((style == svn_subst_eol_style_none) && (! keywords) && (! special))
> {
> /* Translation would be a no-op, so return the original file. */
> *xlated_p = vfile;
> @@ -91,33 +93,36 @@
>
> if (style == svn_subst_eol_style_fixed)
> {
> - SVN_ERR (svn_subst_copy_and_translate (vfile,
> - tmp_vfile,
> - eol,
> - TRUE,
> - keywords,
> - FALSE,
> - pool));
> + SVN_ERR (svn_subst_copy_and_translate2 (vfile,
> + tmp_vfile,
> + eol,
> + TRUE,
> + keywords,
> + FALSE,
> + special,
> + pool));
> }
> else if (style == svn_subst_eol_style_native)
> {
> - SVN_ERR (svn_subst_copy_and_translate (vfile,
> - tmp_vfile,
> - SVN_WC__DEFAULT_EOL_MARKER,
> - force_repair,
> - keywords,
> - FALSE,
> - pool));
> + SVN_ERR (svn_subst_copy_and_translate2 (vfile,
> + tmp_vfile,
> + SVN_WC__DEFAULT_EOL_MARKER,
> + force_repair,
> + keywords,
> + FALSE,
> + special,
> + pool));
> }
> else if (style == svn_subst_eol_style_none)
> {
> - SVN_ERR (svn_subst_copy_and_translate (vfile,
> - tmp_vfile,
> - NULL,
> - force_repair,
> - keywords,
> - FALSE,
> - pool));
> + SVN_ERR (svn_subst_copy_and_translate2 (vfile,
> + tmp_vfile,
> + NULL,
> + force_repair,
> + keywords,
> + FALSE,
> + special,
> + pool));
> }
> else
> {
> @@ -215,6 +220,24 @@
>
> *keywords = apr_pmemdup (pool, &tmp_keywords, sizeof (tmp_keywords));
>
> + return SVN_NO_ERROR;
> +}
> +
> +
> +svn_error_t *
> +svn_wc__get_special (svn_boolean_t *special,
> + const char *path,
> + svn_wc_adm_access_t *adm_access,
> + apr_pool_t *pool)
> +{
> + const svn_string_t *propval;
> +
> + /* Get the property value. */
> + SVN_ERR (svn_wc_prop_get (&propval, SVN_PROP_SPECIAL, path, adm_access,
> + pool));
> +
> + *special = propval ? TRUE : FALSE;
> +
> return SVN_NO_ERROR;
> }
>
>
> Modified: trunk/subversion/libsvn_wc/translate.h
> Url: http://svn.collab.net/viewcvs/svn/trunk/subversion/libsvn_wc/translate.h?view=diff&rev=10114&p1=trunk/subversion/libsvn_wc/translate.h&r1=10113&p2=trunk/subversion/libsvn_wc/translate.h&r2=10114
> ==============================================================================
> --- trunk/subversion/libsvn_wc/translate.h (original)
> +++ trunk/subversion/libsvn_wc/translate.h Thu Jul 1 06:52:54 2004
> @@ -88,6 +88,15 @@
> apr_pool_t *pool);
>
>
> +/* Determine if the svn:special flag is set on PATH. If so, set
> + SPECIAL to TRUE, if not, set it to FALSE. ADM_ACCESS must be an
> + access baton for PATH. Perform any temporary allocations in
> + POOL. */
> +svn_error_t *svn_wc__get_special (svn_boolean_t *special,
> + const char *path,
> + svn_wc_adm_access_t *adm_access,
> + apr_pool_t *pool);
> +
> /* If the SVN_PROP_EXECUTABLE property is present at all, then set
> PATH executable. If DID_SET is non-null, then set *DID_SET to
> TRUE if did set PATH executable, or to FALSE if not. ADM_ACCESS
>
> Modified: trunk/subversion/tests/clients/cmdline/commit_tests.py
> Url: http://svn.collab.net/viewcvs/svn/trunk/subversion/tests/clients/cmdline/commit_tests.py?view=diff&rev=10114&p1=trunk/subversion/tests/clients/cmdline/commit_tests.py&r1=10113&p2=trunk/subversion/tests/clients/cmdline/commit_tests.py&r2=10114
> ==============================================================================
> --- trunk/subversion/tests/clients/cmdline/commit_tests.py (original)
> +++ trunk/subversion/tests/clients/cmdline/commit_tests.py Thu Jul 1 06:52:54 2004
> @@ -1507,24 +1507,6 @@
> svntest.actions.run_and_verify_status(wc2_dir, expected_status2)
>
>
> -def commit_symlink(sbox):
> - "committing a symlink should fail"
> -
> - sbox.build()
> - wc_dir = sbox.wc_dir
> -
> - newfile_path = os.path.join(wc_dir, 'newfile')
> - linktarget_path = os.path.join(wc_dir, 'linktarget')
> - svntest.main.file_append(newfile_path, 'this is a new file')
> - svntest.main.file_append(linktarget_path, 'this is just a link target')
> - svntest.main.run_svn(None, 'add', newfile_path)
> - os.remove(newfile_path)
> - os.symlink('linktarget', newfile_path)
> -
> - svntest.actions.run_and_verify_svn("Output on stderr expected",
> - None, SVNAnyOutput,
> - 'ci', '-m', 'log msg', wc_dir)
> -
> def commit_nonrecursive(sbox):
> "commit named targets with -N (issues #1195, #1239)"
>
> @@ -1892,7 +1874,6 @@
> commit_multiple_wc,
> commit_nonrecursive,
> failed_commit,
> - Skip(commit_symlink, (os.name != 'posix')),
> commit_out_of_date_deletions,
> commit_with_bad_log_message,
> from_wc_top_with_bad_editor,
>
> Added: trunk/subversion/tests/clients/cmdline/special_tests.py
> Url: http://svn.collab.net/viewcvs/svn/trunk/subversion/tests/clients/cmdline/special_tests.py?view=auto&rev=10114
> ==============================================================================
> --- (empty file)
> +++ trunk/subversion/tests/clients/cmdline/special_tests.py Thu Jul 1 06:52:54 2004
> @@ -0,0 +1,160 @@
> +#!/usr/bin/env python
> +#
> +# special_tests.py: testing special file handling
> +#
> +# Subversion is a tool for revision control.
> +# See http://subversion.tigris.org for more information.
> +#
> +# ====================================================================
> +# 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.
> +#
> +######################################################################
> +
> +# General modules
> +import os, re
> +
> +# Our testing module
> +import svntest
> +
> +
> +# (abbreviation)
> +Skip = svntest.testcase.Skip
> +XFail = svntest.testcase.XFail
> +Item = svntest.wc.StateItem
> +
> +
> +######################################################################
> +# Tests
> +#
> +# Each test must return on success or raise on failure.
> +
> +
> +#----------------------------------------------------------------------
> +
> +def general_symlink(sbox):
> + "general symlink handling"
> +
> + sbox.build()
> + wc_dir = sbox.wc_dir
> +
> + # First try to just commit a symlink
> + newfile_path = os.path.join(wc_dir, 'newfile')
> + linktarget_path = os.path.join(wc_dir, 'linktarget')
> + svntest.main.file_append(linktarget_path, 'this is just a link target')
> + os.symlink('linktarget', newfile_path)
> + svntest.main.run_svn(None, 'add', newfile_path, linktarget_path)
> +
> + expected_output = svntest.wc.State(wc_dir, {
> + 'newfile' : Item(verb='Adding'),
> + 'linktarget' : Item(verb='Adding'),
> + })
> +
> + expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
> + expected_status.tweak(wc_rev=1)
> + expected_status.add({
> + 'newfile' : Item(status=' ', wc_rev=2, repos_rev=2),
> + 'linktarget' : Item(status=' ', wc_rev=2, repos_rev=2),
> + })
> +
> + svntest.actions.run_and_verify_commit(wc_dir, expected_output,
> + expected_status, None,
> + None, None, None, None, wc_dir)
> +
> + ## Now we should update to the previous version, verify that no
> + ## symlink is present, then update back to HEAD and see if the symlink
> + ## is regenerated properly.
> + svntest.actions.run_and_verify_svn(None, None, [],
> + 'up', '-r', '1', wc_dir)
> +
> + # Is the symlink gone?
> + if os.path.isfile(newfile_path) or os.path.islink(newfile_path):
> + raise svntest.Failure
> +
> + svntest.actions.run_and_verify_svn(None, None, [],
> + 'up', '-r', '2', wc_dir)
> +
> + # Is the symlink back?
> + new_target = os.readlink(newfile_path)
> + if new_target != 'linktarget':
> + raise svntest.Failure
> +
> + ## Now change the target of the symlink, verify that it is shown as
> + ## modified and that a commit succeeds.
> + os.remove(newfile_path)
> + os.symlink('A', newfile_path)
> +
> + was_cwd = os.getcwd()
> + os.chdir(wc_dir)
> + svntest.actions.run_and_verify_svn(None, [ "M newfile\n" ], [], 'st')
> +
> + os.chdir(was_cwd)
> +
> + expected_output = svntest.wc.State(wc_dir, {
> + 'newfile' : Item(verb='Sending'),
> + })
> +
> + expected_status = svntest.actions.get_virginal_state(wc_dir, 3)
> + expected_status.tweak(wc_rev=2)
> + expected_status.add({
> + 'newfile' : Item(status=' ', wc_rev=3, repos_rev=3),
> + 'linktarget' : Item(status=' ', wc_rev=2, repos_rev=3),
> + })
> +
> + svntest.actions.run_and_verify_commit(wc_dir, expected_output,
> + expected_status, None,
> + None, None, None, None, wc_dir)
> +
> +
> +def replace_file_with_symlink(sbox):
> + "replace a normal file with a special file"
> +
> + sbox.build()
> + wc_dir = sbox.wc_dir
> +
> + # First replace a normal file with a symlink and make sure we get an
> + # error
> + iota_path = os.path.join(wc_dir, 'iota')
> + os.remove(iota_path)
> + os.symlink('A', iota_path)
> +
> + # Does status show the obstruction?
> + was_cwd = os.getcwd()
> + os.chdir(wc_dir)
> + svntest.actions.run_and_verify_svn(None, [ "~ iota\n" ], [], 'st')
> +
> + # And does a commit fail?
> + os.chdir(was_cwd)
> + stdout_lines, stderr_lines = svntest.main.run_svn(1, 'ci', '-m',
> + 'log msg', wc_dir)
> +
> + regex = 'svn: Commit failed'
> + for line in stderr_lines:
> + if re.match(regex, line):
> + break
> + else:
> + raise svntest.Failure
> +
> +
> +
> +########################################################################
> +# Run the tests
> +
> +
> +# list all tests here, starting with None:
> +test_list = [ None,
> + Skip(general_symlink, (os.name != 'posix')),
> + Skip(replace_file_with_symlink, (os.name != 'posix')),
> + ]
> +
> +if __name__ == '__main__':
> + svntest.main.run_tests(test_list)
> + # NOTREACHED
> +
> +
> +### End of file.
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: svn-unsubscribe@subversion.tigris.org
> For additional commands, e-mail: svn-help@subversion.tigris.org

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org
Received on Fri Jul 2 21:25:44 2004

This is an archived mail posted to the Subversion Dev mailing list.

This site is subject to the Apache Privacy Policy and the Apache Public Forum Archive Policy.