Index: subversion_test/subversion/libsvn_client/cmdline.c =================================================================== --- subversion_test/subversion/libsvn_client/cmdline.c 2008-04-30 21:22:52.000000000 -0500 +++ subversion_bug/subversion/libsvn_client/cmdline.c 2008-04-29 16:00:56.000000000 -0500 @@ -196,39 +196,8 @@ for (i = 0; i < input_targets->nelts; i++) { const char *utf8_target = APR_ARRAY_IDX(input_targets, i, const char *); - const char *peg_start = NULL; /* pointer to the peg revision, if any */ const char *target; - int j; - /* Remove a peg revision, if any, in the target so that it can - be properly canonicalized, otherwise the canonicalization - does not treat a ".@BASE" as a "." with a BASE peg revision, - and it is not canonicalized to "@BASE". If any peg revision - exists, it is appended to the final canonicalized path or - URL. Do not use svn_opt_parse_path() because the resulting - peg revision is a structure that would have to be converted - back into a string. Converting from a string date to the - apr_time_t field in the svn_opt_revision_value_t and back to - a string would not necessarily preserve the exact bytes of - the input date, so its easier just to keep it in string - form. */ - for (j = (strlen(utf8_target) - 1); j >= 0; --j) - { - /* If we hit a path separator, stop looking. This is OK - only because our revision specifiers can't contain - '/'. */ - if (utf8_target[j] == '/') - break; - if (utf8_target[j] == '@') - { - peg_start = utf8_target + j; - break; - } - } - if (peg_start) - utf8_target = apr_pstrmemdup(pool, - utf8_target, - peg_start - utf8_target); /* Relative urls will be canonicalized when they are resolved later in * the function @@ -247,7 +216,8 @@ } else /* not a url, so treat as a path */ { - const char *base_name; + char *base_name; + char *peg_rev; SVN_ERR(svn_opt__arg_canonicalize_path(&target, utf8_target, pool)); @@ -256,6 +226,12 @@ working copy administrative dir, skip it. */ base_name = svn_path_basename(target, pool); + /* "Delete" the peg revision because svn_wc_is_adm_dir() cannot + * handle it. + */ + if (peg_rev = strrchr(base_name, '@')) + *peg_rev = '\0'; + if (svn_wc_is_adm_dir(base_name, pool)) { err = svn_error_createf(SVN_ERR_RESERVED_FILENAME_SPECIFIED, @@ -266,11 +242,6 @@ } } - /* Append the peg revision back to the canonicalized target if - there was a peg revision. */ - if (peg_start) - target = apr_pstrcat(pool, target, peg_start, NULL); - if (rel_url_found) { SVN_ERR(check_root_url_of_target(&root_url, target, Index: subversion_test/subversion/tests/cmdline/basic_tests.py =================================================================== --- subversion_test/subversion/tests/cmdline/basic_tests.py 2008-04-30 21:22:52.000000000 -0500 +++ subversion_bug/subversion/tests/cmdline/basic_tests.py 2008-04-29 15:31:58.000000000 -0500 @@ -2326,6 +2326,46 @@ expected_output, [], 'blame', '^//iota/', mu_url) +def basic_relative_url_with_peg_revisions(sbox): + "basic relative url targets with peg revisions" + + sbox.build() + + # First, make a new revision of iota. + iota = os.path.join(sbox.wc_dir, 'iota') + svntest.main.file_append(iota, "New contents for iota\n") + svntest.main.run_svn(None, 'ci', + '-m', '', iota) + + iota_url = sbox.repo_url + '/iota'; + + # Now, make a new revision of A/mu . + mu = os.path.join(sbox.wc_dir, 'A', 'mu') + mu_url = sbox.repo_url + '/A/mu' + + svntest.main.file_append(mu, "New contents for mu\n") + svntest.main.run_svn(None, 'ci', '-m', '', mu) + + # Delete the file from the current revision + svntest.main.run_svn(None, 'rm', '-m', '', iota_url) + + expected_output = [ + " 1 jrandom This is the file 'iota'.\n", + " 2 jrandom New contents for iota\n", + " 1 jrandom This is the file 'mu'.\n", + " 3 jrandom New contents for mu\n", + ] + + # Canonical version with peg revision + exit_code, output, error = svntest.actions.run_and_verify_svn(None, + expected_output, [], 'blame', '-r3', + '^/iota@3', mu_url) + + # Non-canonical version with peg revision + exit_code, output, error = svntest.actions.run_and_verify_svn(None, + expected_output, [], 'blame', '-r3', + '^//iota/@3', mu_url) + #---------------------------------------------------------------------- ######################################################################## @@ -2377,6 +2417,7 @@ basic_relative_url_using_other_targets, basic_relative_url_multi_repo, basic_relative_url_non_canonical, + basic_relative_url_with_peg_revisions, ] if __name__ == '__main__': Index: subversion_test/subversion/libsvn_subr/opt.c =================================================================== --- subversion_test/subversion/libsvn_subr/opt.c 2008-04-30 21:23:02.000000000 -0500 +++ subversion_bug/subversion/libsvn_subr/opt.c 2008-04-29 15:54:41.000000000 -0500 @@ -946,39 +946,7 @@ for (i = 0; i < input_targets->nelts; i++) { const char *utf8_target = APR_ARRAY_IDX(input_targets, i, const char *); - const char *peg_start = NULL; /* pointer to the peg revision, if any */ const char *target; /* after all processing is finished */ - int j; - - /* Remove a peg revision, if any, in the target so that it can - be properly canonicalized, otherwise the canonicalization - does not treat a ".@BASE" as a "." with a BASE peg revision, - and it is not canonicalized to "@BASE". If any peg revision - exists, it is appended to the final canonicalized path or - URL. Do not use svn_opt_parse_path() because the resulting - peg revision is a structure that would have to be converted - back into a string. Converting from a string date to the - apr_time_t field in the svn_opt_revision_value_t and back to - a string would not necessarily preserve the exact bytes of - the input date, so its easier just to keep it in string - form. */ - for (j = (strlen(utf8_target) - 1); j >= 0; --j) - { - /* If we hit a path separator, stop looking. This is OK - only because our revision specifiers can't contain - '/'. */ - if (utf8_target[j] == '/') - break; - if (utf8_target[j] == '@') - { - peg_start = utf8_target + j; - break; - } - } - if (peg_start) - utf8_target = apr_pstrmemdup(pool, - utf8_target, - peg_start - utf8_target); /* URLs and wc-paths get treated differently. */ if (svn_path_is_url(utf8_target)) @@ -987,13 +955,19 @@ } else /* not a url, so treat as a path */ { - const char *base_name; + char *base_name; + char *peg_rev; SVN_ERR(svn_opt__arg_canonicalize_path(&target, utf8_target, pool)); /* If the target has the same name as a Subversion working copy administrative dir, skip it. */ base_name = svn_path_basename(target, pool); + + /* "Delete" the peg revision */ + if (peg_rev = strrchr(base_name, '@')) + *peg_rev = '\0'; + /* FIXME: The canonical list of administrative directory names is maintained in libsvn_wc/adm_files.c:svn_wc_set_adm_dir(). @@ -1011,11 +985,6 @@ } } - /* Append the peg revision back to the canonicalized target if - there was a peg revision. */ - if (peg_start) - target = apr_pstrcat(pool, target, peg_start, NULL); - APR_ARRAY_PUSH(output_targets, const char *) = target; }