diff -ruN subversion/include/svn_subst.h subversion/include/svn_subst.h --- subversion/include/svn_subst.h 2011-05-12 21:30:02.000000000 +0400 +++ subversion/include/svn_subst.h 2011-09-07 21:47:19.000000000 +0400 @@ -127,16 +127,31 @@ * Set @a *kw to a new keywords hash filled with the appropriate contents * given a @a keywords_string (the contents of the svn:keywords * property for the file in question), the revision @a rev, the @a url, - * the @a date the file was committed on, and the @a author of the last - * commit. Any of these can be @c NULL to indicate that the information is - * not present, or @c 0 for @a date. + * the url of the root of the @a repos, the @a date the file was committed + * on, and the @a author of the last commit. Any of these can be @c NULL + * to indicate that the information is not present, or @c 0 for @a date. * * Hash keys are of type const char *. * Hash values are of type svn_string_t *. * * All memory is allocated out of @a pool. * - * @since New in 1.3. + * @since New in 1.6 + */ +svn_error_t * +svn_subst_build_keywords3(apr_hash_t **kw, + const char *keywords_string, + const char *rev, + const char *url, + const char *repos, + apr_time_t date, + const char *author, + apr_pool_t *pool); + +/** Similar to svn_subst_build_keywords3() except that it does not + * supply the repository location. + * + * @deprecated Provided for backward compatibility with the 1.3 API. */ svn_error_t * svn_subst_build_keywords2(apr_hash_t **kw, diff -ruN subversion/libsvn_client/cat.c subversion/libsvn_client/cat.c --- subversion/libsvn_client/cat.c 2011-08-18 18:05:35.000000000 +0400 +++ subversion/libsvn_client/cat.c 2011-09-07 21:47:19.000000000 +0400 @@ -137,12 +137,15 @@ const char *rev_str; const char *author; const char *url; + const char *repos; SVN_ERR(svn_wc__node_get_changed_info(&changed_rev, NULL, &author, wc_ctx, local_abspath, scratch_pool, scratch_pool)); SVN_ERR(svn_wc__node_get_url(&url, wc_ctx, local_abspath, scratch_pool, scratch_pool)); + SVN_ERR(svn_wc__node_get_repos_info(&repos, NULL, wc_ctx, local_abspath, + scratch_pool, scratch_pool)); if (local_mod) { @@ -158,8 +161,8 @@ rev_str = apr_psprintf(scratch_pool, "%ld", changed_rev); } - SVN_ERR(svn_subst_build_keywords2(&kw, keywords->data, rev_str, url, tm, - author, scratch_pool)); + SVN_ERR(svn_subst_build_keywords3(&kw, keywords->data, rev_str, url, + repos, tm, author, scratch_pool)); } /* Wrap the output stream if translation is needed. */ @@ -188,6 +191,7 @@ svn_string_t *keywords; apr_hash_t *props; const char *url; + const char *repos_root_url; svn_stream_t *output = out; svn_error_t *err; @@ -231,6 +235,8 @@ &url, path_or_url, NULL, peg_revision, revision, ctx, pool)); + /* Find the repos root URL */ + SVN_ERR(svn_ra_get_repos_root2(ra_session, &repos_root_url, pool)); /* Grab some properties we need to know in order to figure out if anything special needs to be done with this file. */ @@ -281,10 +287,11 @@ if (cmt_date) SVN_ERR(svn_time_from_cstring(&when, cmt_date->data, pool)); - SVN_ERR(svn_subst_build_keywords2 + SVN_ERR(svn_subst_build_keywords3 (&kw, keywords->data, cmt_rev->data, url, + repos_root_url, when, cmt_author ? cmt_author->data : NULL, pool)); diff -ruN subversion/libsvn_client/commit.c subversion/libsvn_client/commit.c --- subversion/libsvn_client/commit.c 2011-08-17 18:01:10.000000000 +0400 +++ subversion/libsvn_client/commit.c 2011-09-07 21:47:19.000000000 +0400 @@ -126,9 +126,9 @@ } if (keywords_val) - SVN_ERR(svn_subst_build_keywords2(&keywords, keywords_val->data, + SVN_ERR(svn_subst_build_keywords3(&keywords, keywords_val->data, APR_STRINGIFY(SVN_INVALID_REVNUM), - "", 0, "", pool)); + "", "", 0, "", pool)); else keywords = NULL; diff -ruN subversion/libsvn_client/export.c subversion/libsvn_client/export.c --- subversion/libsvn_client/export.c 2011-06-24 12:09:09.000000000 +0400 +++ subversion/libsvn_client/export.c 2011-09-07 21:47:19.000000000 +0400 @@ -274,6 +274,7 @@ svn_revnum_t changed_rev; const char *suffix; const char *url; + const char *repos; const char *author; SVN_ERR(svn_wc__node_get_changed_info(&changed_rev, NULL, &author, @@ -296,11 +297,13 @@ SVN_ERR(svn_wc__node_get_url(&url, wc_ctx, from_abspath, scratch_pool, scratch_pool)); + SVN_ERR(svn_wc__node_get_repos_info(&repos, NULL, wc_ctx, from_abspath, + scratch_pool, scratch_pool)); - SVN_ERR(svn_subst_build_keywords2 + SVN_ERR(svn_subst_build_keywords3 (&kw, keywords->data, apr_psprintf(scratch_pool, "%ld%s", changed_rev, suffix), - url, tm, author, scratch_pool)); + url, repos, tm, author, scratch_pool)); } /* For atomicity, we translate to a tmp file and then rename the tmp file @@ -702,6 +705,7 @@ /* Any keyword vals to be substituted */ const char *revision; const char *url; + const char *repos; const char *author; apr_time_t date; @@ -823,6 +827,7 @@ fb->edit_baton = eb; fb->path = full_path; fb->url = full_url; + fb->repos = eb->root_url; fb->pool = pool; *baton = fb; @@ -987,8 +992,8 @@ } if (fb->keywords_val) - SVN_ERR(svn_subst_build_keywords2(&final_kw, fb->keywords_val->data, - fb->revision, fb->url, fb->date, + SVN_ERR(svn_subst_build_keywords3(&final_kw, fb->keywords_val->data, + fb->revision, fb->url, fb->repos, fb->date, fb->author, pool)); SVN_ERR(svn_subst_copy_and_translate4(fb->tmppath, fb->path, diff -ruN subversion/libsvn_subr/subst.c subversion/libsvn_subr/subst.c --- subversion/libsvn_subr/subst.c 2011-07-29 21:28:11.000000000 +0400 +++ subversion/libsvn_subr/subst.c 2011-09-07 21:47:19.000000000 +0400 @@ -135,8 +135,11 @@ * %b basename of the URL of this file * %d short format of date of this revision * %D long format of date of this revision + * %P path relative to root of repos * %r number of this revision + * %R root url of repository * %u URL of this file + * %_ a space * %% a literal % * * All memory is allocated out of @a pool. @@ -145,12 +148,14 @@ keyword_printf(const char *fmt, const char *rev, const char *url, + const char *repos, apr_time_t date, const char *author, apr_pool_t *pool) { svn_stringbuf_t *value = svn_stringbuf_ncreate("", 0, pool); const char *cur; + const char *relative; size_t n; for (;;) @@ -203,6 +208,23 @@ svn_stringbuf_appendcstr(value, svn_time_to_human_cstring(date, pool)); break; + case 'P': /* relative path of this file */ + relative = url; + if (relative && repos) + { + int len = strlen(repos); + + if (strncmp(repos, relative, len) == 0 + && relative[len] == '/') + relative += len + 1; + } + if (relative) + svn_stringbuf_appendcstr(value, relative); + break; + case 'R': /* root of repos */ + if (repos) + svn_stringbuf_appendcstr(value, repos); + break; case 'r': /* number of this revision */ if (rev) svn_stringbuf_appendcstr(value, rev); @@ -211,6 +233,9 @@ if (url) svn_stringbuf_appendcstr(value, url); break; + case '_': /* '%_' => a space */ + svn_stringbuf_appendbytes(value, " ", 1); + break; case '%': /* '%%' => a literal % */ svn_stringbuf_appendbyte(value, *cur); break; @@ -246,8 +271,8 @@ apr_hash_t *kwhash; const svn_string_t *val; - SVN_ERR(svn_subst_build_keywords2(&kwhash, keywords_val, rev, - url, date, author, pool)); + SVN_ERR(svn_subst_build_keywords3(&kwhash, keywords_val, rev, + url, "", date, author, pool)); /* The behaviour of pre-1.3 svn_subst_build_keywords, which we are * replicating here, is to write to a slot in the svn_subst_keywords_t @@ -286,6 +311,21 @@ const char *author, apr_pool_t *pool) { + SVN_ERR(svn_subst_build_keywords3(kw, keywords_val, rev, + url, "", date, author, pool)); + return SVN_NO_ERROR; +} + +svn_error_t * +svn_subst_build_keywords3(apr_hash_t **kw, + const char *keywords_val, + const char *rev, + const char *url, + const char *repos, + apr_time_t date, + const char *author, + apr_pool_t *pool) +{ apr_array_header_t *keyword_tokens; int i; *kw = apr_hash_make(pool); @@ -296,6 +336,24 @@ for (i = 0; i < keyword_tokens->nelts; ++i) { const char *keyword = APR_ARRAY_IDX(keyword_tokens, i, const char *); + apr_array_header_t *keyword_tokens2; + + keyword_tokens2 = svn_cstring_split(keyword, "=", TRUE /* chop */, pool); + if (keyword_tokens2->nelts == 2) + { + svn_string_t *custom_val; + const char *custom_expand; + + keyword = APR_ARRAY_IDX(keyword_tokens2, 0, const char*); + custom_expand = APR_ARRAY_IDX(keyword_tokens2, 1, const char*); + if (! strcmp(custom_expand, "%H")) + custom_expand = "%P %r %d %a"; + else if (! strcmp(custom_expand, "%I")) + custom_expand = "%b %r %d %a"; + custom_val = keyword_printf(custom_expand, rev, url, repos, date, author, pool); + apr_hash_set(*kw, keyword, APR_HASH_KEY_STRING, custom_val); + return SVN_NO_ERROR; + } if ((! strcmp(keyword, SVN_KEYWORD_REVISION_LONG)) || (! strcmp(keyword, SVN_KEYWORD_REVISION_MEDIUM)) @@ -303,7 +361,7 @@ { svn_string_t *revision_val; - revision_val = keyword_printf("%r", rev, url, date, author, pool); + revision_val = keyword_printf("%r", rev, url, repos, date, author, pool); apr_hash_set(*kw, SVN_KEYWORD_REVISION_LONG, APR_HASH_KEY_STRING, revision_val); apr_hash_set(*kw, SVN_KEYWORD_REVISION_MEDIUM, @@ -316,7 +374,7 @@ { svn_string_t *date_val; - date_val = keyword_printf("%D", rev, url, date, author, pool); + date_val = keyword_printf("%D", rev, url, repos, date, author, pool); apr_hash_set(*kw, SVN_KEYWORD_DATE_LONG, APR_HASH_KEY_STRING, date_val); apr_hash_set(*kw, SVN_KEYWORD_DATE_SHORT, @@ -327,7 +385,7 @@ { svn_string_t *author_val; - author_val = keyword_printf("%a", rev, url, date, author, pool); + author_val = keyword_printf("%a", rev, url, repos, date, author, pool); apr_hash_set(*kw, SVN_KEYWORD_AUTHOR_LONG, APR_HASH_KEY_STRING, author_val); apr_hash_set(*kw, SVN_KEYWORD_AUTHOR_SHORT, @@ -338,7 +396,7 @@ { svn_string_t *url_val; - url_val = keyword_printf("%u", rev, url, date, author, pool); + url_val = keyword_printf("%u", rev, url, repos, date, author, pool); apr_hash_set(*kw, SVN_KEYWORD_URL_LONG, APR_HASH_KEY_STRING, url_val); apr_hash_set(*kw, SVN_KEYWORD_URL_SHORT, @@ -348,7 +406,7 @@ { svn_string_t *id_val; - id_val = keyword_printf("%b %r %d %a", rev, url, date, author, + id_val = keyword_printf("%b %r %d %a", rev, url, repos, date, author, pool); apr_hash_set(*kw, SVN_KEYWORD_ID, APR_HASH_KEY_STRING, id_val); @@ -357,7 +415,7 @@ { svn_string_t *header_val; - header_val = keyword_printf("%u %r %d %a", rev, url, date, author, + header_val = keyword_printf("%u %r %d %a", rev, url, repos, date, author, pool); apr_hash_set(*kw, SVN_KEYWORD_HEADER, APR_HASH_KEY_STRING, header_val); diff -ruN subversion/libsvn_wc/translate.c subversion/libsvn_wc/translate.c --- subversion/libsvn_wc/translate.c 2011-07-07 15:29:08.000000000 +0400 +++ subversion/libsvn_wc/translate.c 2011-09-07 21:47:19.000000000 +0400 @@ -313,10 +313,10 @@ apr_time_t changed_date; const char *changed_author; const char *url; + const char *repos_root_url; if (! for_normalization) { - const char *repos_root_url; const char *repos_relpath; SVN_ERR(svn_wc__db_read_info(NULL, NULL, NULL, &repos_relpath, @@ -341,13 +341,23 @@ changed_rev = SVN_INVALID_REVNUM; changed_date = 0; changed_author = ""; + + SVN_ERR(svn_wc__db_read_info(NULL, NULL, NULL, NULL, + &repos_root_url, NULL, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + db, local_abspath, + scratch_pool, scratch_pool)); } - SVN_ERR(svn_subst_build_keywords2(keywords, + SVN_ERR(svn_subst_build_keywords3(keywords, keyword_list, apr_psprintf(scratch_pool, "%ld", changed_rev), url, + repos_root_url, changed_date, changed_author, result_pool));