John Szakmeister wrote:
> Okay, I think I've got all of this worked. I use the working copy timestamps
> only if the file is locally modified. For a locally modified file, I will
> also substitute in "(local)" as the author, append an 'M' to the revision
> number, and use the working copy time for LastChangedDate.
>
> So, here's another stab with everything. If no one disapproves of it, I'll
> commit it tomorrow, and hopefully move on to more serious problems. :-)
>
> -John
>
> Log:
> ----------------------------------------
> Fixes a minor issue with the 'export' command failing to copy the BASE
> revision of files when '-rBASE' is specified. Also fixes translation of
> keywords for locally modified files, as well as adding appropriate
> timestamping. Adds a couple of tests for this new functionality.
>
> * subversion/libsvn_client/export.c
> (copy_versioned_files): Added revision parameter. Changed the copy
> algorithm to translate keywords and eol style when appropriate.
>
> (svn_client_export): Added the ability to better distinguish when the
> repository should be contacted versus using the local base or working copy.
>
> * subversion/clients/cmdline/main.c
> (svn_cl__cmd_table): Updated to reflect export's new functionality.
>
> * subversion/tests/clients/cmdline/export_tests.py
> (export_working_copy_with_keyword_translation,
> export_working_copy_with_property_mods,
> export_working_copy_at_base_revision): New export tests.
>
> * subversion/tests/clients/cmdline/svntest/actions.py
> (run_and_verify_export): Added new *args parameter to support new export
> tests.
> ----------------------------------------
>
>
> Index: subversion/libsvn_client/export.c
> ===================================================================
> --- subversion/libsvn_client/export.c (revision 7778)
> +++ subversion/libsvn_client/export.c (working copy)
> @@ -44,6 +44,7 @@
> static svn_error_t *
> copy_versioned_files (const char *from,
> const char *to,
> + svn_opt_revision_t *revision,
> svn_boolean_t force,
> svn_client_ctx_t *ctx,
> apr_pool_t *pool)
> @@ -121,8 +122,8 @@
> const char *new_from = svn_path_join (from, key, subpool);
> const char *new_to = svn_path_join (to, key, subpool);
>
> - SVN_ERR (copy_versioned_files (new_from, new_to, force,
> - ctx, subpool));
> + SVN_ERR (copy_versioned_files (new_from, new_to, revision,
> + force, ctx, subpool));
> }
> }
> else if (*type == svn_node_file)
> @@ -143,8 +144,95 @@
> /* don't copy it if it isn't versioned. */
> if (entry)
> {
> - SVN_ERR (svn_io_copy_file (copy_from, copy_to, TRUE,
> - subpool));
> + svn_subst_keywords_t kw = { 0 };
> + svn_subst_eol_style_t style;
> + apr_hash_t *props;
> + const char *base;
> + svn_string_t *eol_style;
> + svn_string_t *keywords;
> + svn_string_t *executable;
> + svn_string_t *date;
> + const char *eol = NULL;
> + svn_boolean_t local_mod = FALSE;
> + apr_time_t time;
> +
> + if (revision->kind == svn_opt_revision_base)
> + {
> + SVN_ERR (svn_wc_get_pristine_copy_path
> + (copy_from, &base, subpool));
> + SVN_ERR (svn_wc_get_prop_diffs
> + (NULL, &props, copy_from, adm_access, subpool));
> + }
> + else
> + {
> + svn_wc_status_t *status;
> +
> + base = copy_from;
> + SVN_ERR (svn_wc_prop_list (&props, copy_from,
> + adm_access, subpool));
> + SVN_ERR (svn_wc_status (&status, copy_from, adm_access, subpool));
> + 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);
> +
> + if (eol_style)
> + svn_subst_eol_style_from_value (&style, &eol,
> + eol_style->data);
> +
> + if (local_mod)
> + /* Use the modified time from the working copy if the file */
> + SVN_ERR (svn_io_file_affected_time (&time, copy_from, subpool));
> + else
> + time = 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 = "%" SVN_REVNUM_T_FMT "M";
> + author = "(local)";
> + }
> + else
> + {
> + fmt = "%" SVN_REVNUM_T_FMT;
> + author = entry->cmt_author;
> + }
> +
> + SVN_ERR (svn_subst_build_keywords
> + (&kw, keywords->data,
> + apr_psprintf (pool,
> + fmt,
> + entry->cmt_rev),
> + entry->url,
> + time,
> + author,
> + subpool));
> + }
> +
> + SVN_ERR (svn_subst_copy_and_translate (base, copy_to,
> + eol, FALSE,
> + &kw, TRUE,
> + subpool));
> + if (executable)
> + SVN_ERR (svn_io_set_file_executable (copy_to, TRUE, FALSE, subpool));
> +
> + SVN_ERR (svn_io_set_file_affected_time (time, copy_to, subpool));
> }
> }
>
> @@ -208,8 +296,22 @@
> svn_client_ctx_t *ctx,
> apr_pool_t *pool)
> {
> - if (svn_path_is_url (from))
> + svn_boolean_t use_ra = FALSE;
> +
> + if (! svn_path_is_url (from) &&
> + (revision->kind != svn_opt_revision_base) &&
> + (revision->kind != svn_opt_revision_working))
You could include svn_opt_revision_committed as well, because -rBASE and -rCOMMITTED necessarily refer to the same data (even though they may correspond to different revision numbers because of commits elsewhere in the repository).
But you don't have to do so.
This pattern occurs in several places in the client code; I have been looking for good ways to factor it out, but have not found much of a good way yet.
> {
> + if (revision->kind == svn_opt_revision_unspecified)
> + /* Default to WORKING in the case that we have
> + been given a working copy path */
> + revision->kind = svn_opt_revision_working;
> + else
> + use_ra = TRUE;
> + }
> +
> + if (svn_path_is_url (from) || use_ra)
> + {
> const char *URL;
> svn_revnum_t revnum;
> void *ra_baton, *session;
> @@ -219,8 +321,16 @@
> const svn_ra_reporter_t *reporter;
> void *report_baton;
>
> - URL = svn_path_canonicalize (from, pool);
> -
> + if (use_ra)
> + {
> + SVN_ERR (svn_client_url_from_path (&URL, from, pool));
> + if (! URL)
> + return svn_error_createf (SVN_ERR_ENTRY_MISSING_URL, NULL,
> + "'%s' has no URL", from);
> + }
> + else
> + URL = svn_path_canonicalize (from, pool);
> +
> SVN_ERR (svn_client__get_export_editor (&export_editor, &edit_baton,
> to, URL, force, ctx, pool));
>
> @@ -236,7 +346,7 @@
> if (revision->kind == svn_opt_revision_unspecified)
> revision->kind = svn_opt_revision_head;
> SVN_ERR (svn_client__get_revision_number
> - (&revnum, ra_lib, session, revision, to, pool));
> + (&revnum, ra_lib, session, revision, from, pool));
>
> /* Manufacture a basic 'report' to the update reporter. */
> SVN_ERR (ra_lib->do_update (session,
> @@ -273,7 +383,7 @@
> else
> {
> /* just copy the contents of the working copy into the target path. */
> - SVN_ERR (copy_versioned_files (from, to, force, ctx, pool));
> + SVN_ERR (copy_versioned_files (from, to, revision, force, ctx, pool));
> }
>
> return SVN_NO_ERROR;
> Index: subversion/clients/cmdline/main.c
> ===================================================================
> --- subversion/clients/cmdline/main.c (revision 7778)
> +++ subversion/clients/cmdline/main.c (working copy)
> @@ -253,7 +253,7 @@
> { "export", svn_cl__export, {0},
> "Create an unversioned copy of a tree.\n"
> "usage: 1. export [-r REV] URL [PATH]\n"
> - " 2. export PATH1 PATH2\n"
> + " 2. export [-r REV] PATH1 [PATH2]\n"
> "\n"
> " 1. Exports a clean directory tree from the repository specified by\n"
> " URL, at revision REV if it is given, otherwise at HEAD, into\n"
> @@ -261,8 +261,11 @@
> " for the local directory name.\n"
> "\n"
> " 2. Exports a clean directory tree from the working copy specified by\n"
> - " PATH1 into PATH2. All local changes will be preserved, but files\n"
> - " not under version control will not be copied.\n",
> + " PATH1, at revision REV if it is given, otherwise at WORKING, into\n"
> + " PATH2. If PATH2 is omitted, the last component of the PATH1 is used\n"
> + " for the local directory name. If REV is not specified, All local\n"
"All" -> "all".
> + " changes will be preserved, but files not under version control will\n"
> + " not be copied.\n",
> {'r', 'q', svn_cl__force_opt, SVN_CL__AUTH_OPTIONS,
> svn_cl__config_dir_opt} },
>
> Index: subversion/tests/clients/cmdline/export_tests.py
> ===================================================================
> --- subversion/tests/clients/cmdline/export_tests.py (revision 7778)
> +++ subversion/tests/clients/cmdline/export_tests.py (working copy)
> @@ -190,6 +190,81 @@
> expected_output,
> expected_disk)
>
> +def export_working_copy_with_keyword_translation(sbox):
> + "export working copy with keyword translation"
> + sbox.build()
> +
> + wc_dir = sbox.wc_dir
> +
> + # Add a keyword to A/mu and set the svn:keywords property
> + # appropriately to make sure it's translated during
> + # the export operation
> + mu_path = os.path.join(wc_dir, 'A', 'mu')
> + svntest.main.file_append(mu_path, '$LastChangedRevision$')
> + svntest.main.run_svn(None, 'ps', 'svn:keywords',
> + 'LastChangedRevision', mu_path)
> +
> + expected_disk = svntest.main.greek_state.copy()
> + expected_disk.tweak('A/mu',
> + contents=expected_disk.desc['A/mu'].contents +
> + '$LastChangedRevision: 1M $')
> +
> + export_target = sbox.add_wc_path('export')
> +
> + svntest.actions.run_and_verify_export(wc_dir,
> + export_target,
> + svntest.wc.State(sbox.wc_dir, {}),
> + expected_disk)
> +
> +def export_working_copy_with_property_mods(sbox):
> + "export working copy with property mods"
> + sbox.build()
> +
> + wc_dir = sbox.wc_dir
> +
> + # Make a local property mod to A/my
"A/my" -> "A/mu".
> + mu_path = os.path.join(wc_dir, 'A', 'mu')
> + svntest.main.file_append(mu_path, '\n')
> + svntest.main.run_svn(None, 'ps', 'svn:eol-style',
> + 'CR', mu_path)
> +
> + expected_disk = svntest.main.greek_state.copy()
> + expected_disk.tweak('A/mu',
> + contents=expected_disk.desc['A/mu'].contents +
> + '\r')
> +
> + export_target = sbox.add_wc_path('export')
> +
> + svntest.actions.run_and_verify_export(wc_dir,
> + export_target,
> + svntest.wc.State(sbox.wc_dir, {}),
> + expected_disk)
> +
> +def export_working_copy_at_base_revision(sbox):
> + "export working copy at base revision"
> + sbox.build()
> +
> + wc_dir = sbox.wc_dir
> +
> + # Add a keyword to A/mu and set the svn:keywords property
> + # appropriately to make sure it's translated during
> + # the export operation
> + mu_path = os.path.join(wc_dir, 'A', 'mu')
> + svntest.main.file_append(mu_path, 'Appended text')
> +
> + # Note that we don't tweak the expected disk tree at all,
> + # since the appended text should not be present.
> + expected_disk = svntest.main.greek_state.copy()
> +
> + export_target = sbox.add_wc_path('export')
> +
> + svntest.actions.run_and_verify_export(wc_dir,
> + export_target,
> + svntest.wc.State(sbox.wc_dir, {}),
> + expected_disk,
> + None, None, None, None,
> + '-rBASE')
> +
> ########################################################################
> # Run the tests
>
[...]
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org
Received on Wed Nov 19 11:04:19 2003