Index: subversion/libsvn_subr/constructors.c =================================================================== --- subversion/libsvn_subr/constructors.c (revision 16776) +++ subversion/libsvn_subr/constructors.c (working copy) @@ -47,6 +47,8 @@ dst_commit_info->author = src_commit_info->author ? apr_pstrdup (pool, src_commit_info->author) : NULL; dst_commit_info->revision = src_commit_info->revision; + dst_commit_info->post_commit_err = src_commit_info->post_commit_err + ? apr_pstrdup (pool, src_commit_info->post_commit_err) : NULL; return dst_commit_info; } Index: subversion/mod_dav_svn/merge.c =================================================================== --- subversion/mod_dav_svn/merge.c (revision 16776) +++ subversion/mod_dav_svn/merge.c (working copy) @@ -29,6 +29,7 @@ #include "svn_props.h" #include "dav_svn.h" +#include "svn_xml.h" /* ################################################################# @@ -200,6 +201,7 @@ dav_error * dav_svn__merge_response(ap_filter_t *output, const dav_svn_repos *repos, svn_revnum_t new_rev, + char *post_commit_err, apr_xml_elem *prop_elem, svn_boolean_t disable_merge_response, apr_pool_t *pool) @@ -210,6 +212,8 @@ const char *vcc; const char *rev; svn_string_t *creationdate, *creator_displayname; + const char *post_commit_err_elem = NULL, + *post_commit_header_info = NULL; serr = svn_fs_revision_root(&root, repos->fs, new_rev, pool); if (serr != NULL) @@ -231,6 +235,24 @@ /* the version-name of the baseline is the revision number */ rev = apr_psprintf(pool, "%ld", new_rev); + /* get the post-commit hook stderr, if any */ + if (post_commit_err) + { + post_commit_header_info = apr_psprintf (pool, + " xmlns:S=\"%s\"", + SVN_XML_NAMESPACE); + post_commit_err_elem = apr_psprintf (pool, + "%s" + "", + post_commit_err); + } + else + { + post_commit_header_info = "" ; + post_commit_err_elem = "" ; + } + + /* get the creationdate and creator-displayname of the new revision, too. */ serr = svn_fs_revision_prop(&creationdate, repos->fs, new_rev, SVN_PROP_REVISION_DATE, pool); @@ -252,7 +274,9 @@ (void) ap_fputstrs(output, bb, DAV_XML_HEADER DEBUG_CR - "" DEBUG_CR + "" DEBUG_CR "" DEBUG_CR /* generate a response for the new baseline */ @@ -264,7 +288,8 @@ /* ### this is wrong. it's a VCC, not a baseline. but ### we need to tell the client to look at *this* ### resource for the version-name. */ - "" DEBUG_CR + "" DEBUG_CR, + post_commit_err_elem, DEBUG_CR "", rev, "" DEBUG_CR, NULL); if (creationdate) Index: subversion/mod_dav_svn/dav_svn.h =================================================================== --- subversion/mod_dav_svn/dav_svn.h (revision 16776) +++ subversion/mod_dav_svn/dav_svn.h (working copy) @@ -507,6 +507,7 @@ dav_error * dav_svn__merge_response(ap_filter_t *output, const dav_svn_repos *repos, svn_revnum_t new_rev, + char *post_commit_err, apr_xml_elem *prop_elem, svn_boolean_t disable_merge_response, apr_pool_t *pool); Index: subversion/mod_dav_svn/version.c =================================================================== --- subversion/mod_dav_svn/version.c (revision 16776) +++ subversion/mod_dav_svn/version.c (working copy) @@ -1614,6 +1614,7 @@ svn_fs_txn_t *txn; const char *conflict; svn_error_t *serr; + char *post_commit_err = NULL; svn_revnum_t new_rev; apr_hash_t *locks; svn_boolean_t disable_merge_response = FALSE; @@ -1694,7 +1695,11 @@ return dav_svn_convert_err(serr, HTTP_CONFLICT, msg, pool); } else if (serr) - svn_error_clear(serr); + { + if (serr->child->message) + post_commit_err = apr_pstrdup (pool, serr->child->message); + svn_error_clear(serr); + } /* Commit was successful, so schedule deltification. */ register_deltification_cleanup(source->info->repos->repos, new_rev, @@ -1740,7 +1745,8 @@ /* process the response for the new revision. */ return dav_svn__merge_response(output, source->info->repos, new_rev, - prop_elem, disable_merge_response, pool); + post_commit_err, prop_elem, + disable_merge_response, pool); } const dav_hooks_vsn dav_svn_hooks_vsn = { Index: subversion/clients/cmdline/util.c =================================================================== --- subversion/clients/cmdline/util.c (revision 16776) +++ subversion/clients/cmdline/util.c (working copy) @@ -53,10 +53,20 @@ svn_cl__print_commit_info (svn_commit_info_t *commit_info, apr_pool_t *pool) { - if ((commit_info) - && (SVN_IS_VALID_REVNUM (commit_info->revision))) - SVN_ERR (svn_cmdline_printf (pool, _("\nCommitted revision %ld.\n"), + if (commit_info) + { + if (SVN_IS_VALID_REVNUM (commit_info->revision)) + SVN_ERR (svn_cmdline_printf (pool, _("\nCommitted revision %ld.\n"), commit_info->revision)); + + /* Writing to stdout, as there maybe systems that consider the + * presence of stderr as an indication of commit failure. + * OTOH, this is only of informational nature to the user as + * the commit has succeeded. */ + if (commit_info->post_commit_err) + SVN_ERR (svn_cmdline_printf (pool, _("\nWarning:%s\n"), + commit_info->post_commit_err)); + } return SVN_NO_ERROR; } Index: subversion/tests/clients/cmdline/commit_tests.py =================================================================== --- subversion/tests/clients/cmdline/commit_tests.py (revision 16776) +++ subversion/tests/clients/cmdline/commit_tests.py (working copy) @@ -1955,6 +1955,47 @@ os.path.join(wc_dir, 'A', 'yu')) +#---------------------------------------------------------------------- +# Test if the post-commit error message is returned back to the svn +# client and is displayed as a warning. +# +def post_commit_hook_test(sbox): + "post commit hook failure case testing" + + sbox.build() + + # Get paths to the working copy and repository + wc_dir = sbox.wc_dir + repo_dir = sbox.repo_dir + + # Setup the hook configs to echo data back + post_commit_hook = svntest.main.get_post_commit_hook_path (repo_dir) + svntest.main.file_append (post_commit_hook, + """#!/bin/sh + echo "Post-commit Hook says nothing doing on stderr" > /dev/stderr + exit -1 + """) + os.chmod (post_commit_hook, 0755) + + # Modify iota just so there is something to commit. + iota_path = os.path.join (wc_dir, "iota") + svntest.main.file_append (iota_path, "lakalakalakalaka") + + # Now, commit and examine the output (we happen to know that the + # filesystem will report an absolute path because that's the way the + # filesystem is created by this test suite. + expected_output = [ "Sending "+ iota_path + "\n", + "Transmitting file data .\n", + "Committed revision 2.\n", + "\n", + "Warning:'post-commit' hook failed with error output:\n", + "Post-commit Hook says nothing doing on stderr\n", + "\n"] + + svntest.actions.run_and_verify_svn (None, expected_output, [], + 'ci', '-m', 'log msg', iota_path) + + ######################################################################## # Run the tests @@ -1994,6 +2035,7 @@ mods_in_schedule_delete, Skip(tab_test, (os.name != 'posix' or sys.platform == 'cygwin')), local_mods_are_not_commits, + post_commit_hook_test ] if __name__ == '__main__': Index: subversion/libsvn_repos/hooks.c =================================================================== --- subversion/libsvn_repos/hooks.c (revision 16776) +++ subversion/libsvn_repos/hooks.c (working copy) @@ -297,7 +297,7 @@ args[2] = apr_psprintf (pool, "%ld", rev); args[3] = NULL; - SVN_ERR (run_hook_cmd ("post-commit", hook, args, FALSE, NULL, pool)); + SVN_ERR (run_hook_cmd ("post-commit", hook, args, TRUE, NULL, pool)); } return SVN_NO_ERROR; Index: subversion/libsvn_repos/commit.c =================================================================== --- subversion/libsvn_repos/commit.c (revision 16776) +++ subversion/libsvn_repos/commit.c (working copy) @@ -664,6 +664,7 @@ svn_revnum_t new_revision = SVN_INVALID_REVNUM; svn_error_t *err; const char *conflict; + char *post_commit_err = NULL; /* If no transaction has been created (ie. if open_root wasn't called before close_edit), abort the operation here with an @@ -702,12 +703,13 @@ } else if (err) { - /* ### TODO: ra_local is the only RA layer that currently - understands SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED. And as of - at least r12960, svn_repos_fs_commit_txn() would never return - that anyway. If someone who knows ra_svn better can add - handling for this special case, this whole "else if" block - can go away (again). */ + /* Post-commit hook's failure output can be passed back to the + client. However, this cannot be a commit failure. Hence + passing back the post-commit error message as a string to + be displayed as a warning. */ + if (err->child->message) + post_commit_err = apr_pstrdup (pool, err->child->message) ; + svn_error_clear(err); err = SVN_NO_ERROR; } @@ -738,6 +740,7 @@ commit_info->revision = new_revision; commit_info->date = date ? date->data : NULL; commit_info->author = author ? author->data : NULL; + commit_info->post_commit_err = post_commit_err; err2 = (*eb->commit_callback) (commit_info, eb->commit_callback_baton, pool); Index: subversion/libsvn_ra_svn/client.c =================================================================== --- subversion/libsvn_ra_svn/client.c (revision 16776) +++ subversion/libsvn_ra_svn/client.c (working copy) @@ -870,10 +870,12 @@ svn_commit_info_t *commit_info = svn_create_commit_info (ccb->pool); SVN_ERR(handle_auth_request(ccb->sess_baton, ccb->pool)); - SVN_ERR(svn_ra_svn_read_tuple(ccb->sess_baton->conn, ccb->pool, "r(?c)(?c)", - &(commit_info->revision), - &(commit_info->date), - &(commit_info->author))); + SVN_ERR(svn_ra_svn_read_tuple(ccb->sess_baton->conn, ccb->pool, + "r(?c)(?c)(?c)", + &(commit_info->revision), + &(commit_info->date), + &(commit_info->author), + &(commit_info->post_commit_err))); return ccb->callback(commit_info, ccb->callback_baton, ccb->pool); Index: subversion/libsvn_ra_dav/merge.c =================================================================== --- subversion/libsvn_ra_dav/merge.c (revision 16776) +++ subversion/libsvn_ra_dav/merge.c (working copy) @@ -58,6 +58,8 @@ { "DAV:", "collection", ELEM_collection, 0 }, { "DAV:", "baseline", ELEM_baseline, 0 }, { "DAV:", "version-name", ELEM_version_name, SVN_RA_DAV__XML_CDATA }, + { SVN_XML_NAMESPACE, "post-commit-err", + ELEM_post_commit_err, SVN_RA_DAV__XML_CDATA }, { "DAV:", "creationdate", ELEM_creationdate, SVN_RA_DAV__XML_CDATA }, { "DAV:", "creator-displayname", ELEM_creator_displayname, SVN_RA_DAV__XML_CDATA }, @@ -104,6 +106,8 @@ svn_stringbuf_t *committed_date; /* DAV:creationdate for this resource */ svn_stringbuf_t *last_author; /* DAV:creator-displayname for this resource */ + svn_stringbuf_t *post_commit_err;/* SVN_XML_NAMESPACE:post-commit hook's + stderr */ /* We only invoke set_prop() on targets listed in valid_targets. Some entities (such as directories that have had changes @@ -136,7 +140,6 @@ /* Easy check: if path itself is in the hash, then it's legit. */ if (apr_hash_get (valid_targets, path, APR_HASH_KEY_STRING)) return TRUE; - /* Otherwise, this path is bumpable IFF one of its parents is in the hash and marked with a 'recursion' flag. */ parent_path = svn_stringbuf_create (path, pool); @@ -341,6 +344,7 @@ || child == ELEM_version_name || child == ELEM_creationdate || child == ELEM_creator_displayname + || child == ELEM_post_commit_err /* other props */) return SVN_RA_DAV__XML_VALID; else @@ -524,6 +528,10 @@ svn_stringbuf_set(mc->vsn_name, cdata); break; + case ELEM_post_commit_err: + svn_stringbuf_set(mc->post_commit_err, cdata); + break; + case ELEM_creationdate: svn_stringbuf_set(mc->committed_date, cdata); break; @@ -656,6 +664,7 @@ svn_revnum_t *new_rev, const char **committed_date, const char **committed_author, + const char **post_commit_err, svn_ra_dav__session_t *ras, const char *repos_url, const char *activity_url, @@ -686,6 +695,8 @@ mc.vsn_url = MAKE_BUFFER(pool); mc.committed_date = MAKE_BUFFER(pool); mc.last_author = MAKE_BUFFER(pool); + if (post_commit_err) + mc.post_commit_err = MAKE_BUFFER(pool); if (disable_merge_response || (! keep_locks)) @@ -749,6 +760,9 @@ if (committed_author) *committed_author = mc.last_author->len ? apr_pstrdup(pool, mc.last_author->data) : NULL; + if (post_commit_err) + *post_commit_err = mc.post_commit_err->len + ? apr_pstrdup(pool, mc.post_commit_err->data) : NULL; svn_pool_destroy(mc.scratchpool); Index: subversion/libsvn_ra_dav/ra_dav.h =================================================================== --- subversion/libsvn_ra_dav/ra_dav.h (revision 16776) +++ subversion/libsvn_ra_dav/ra_dav.h (working copy) @@ -665,6 +665,7 @@ ELEM_updated_set, ELEM_vcc, ELEM_version_name, + ELEM_post_commit_err, ELEM_error, /* SVN elements */ @@ -719,6 +720,7 @@ svn_revnum_t *new_rev, const char **committed_date, const char **committed_author, + const char **post_commit_err, svn_ra_dav__session_t *ras, const char *repos_url, const char *activity_url, Index: subversion/libsvn_ra_dav/commit.c =================================================================== --- subversion/libsvn_ra_dav/commit.c (revision 16776) +++ subversion/libsvn_ra_dav/commit.c (working copy) @@ -1465,6 +1465,7 @@ SVN_ERR( svn_ra_dav__merge_activity(&(commit_info->revision), &(commit_info->date), &(commit_info->author), + &(commit_info->post_commit_err), cc->ras, cc->ras->root.path, cc->activity_url, Index: subversion/svnserve/serve.c =================================================================== --- subversion/svnserve/serve.c (revision 16776) +++ subversion/svnserve/serve.c (working copy) @@ -68,6 +68,7 @@ svn_revnum_t *new_rev; const char **date; const char **author; + const char **post_commit_err; } commit_callback_baton_t; typedef struct { @@ -796,6 +797,8 @@ ? apr_pstrdup(ccb->pool, commit_info->date): NULL; *ccb->author = commit_info->author ? apr_pstrdup(ccb->pool, commit_info->author) : NULL; + *ccb->post_commit_err = commit_info->post_commit_err + ? apr_pstrdup(ccb->pool, commit_info->post_commit_err) : NULL; return SVN_NO_ERROR; } @@ -899,7 +902,10 @@ apr_array_header_t *params, void *baton) { server_baton_t *b = baton; - const char *log_msg, *date, *author; + const char *log_msg = NULL, + *date = NULL, + *author = NULL, + *post_commit_err = NULL; apr_array_header_t *lock_tokens; svn_boolean_t keep_locks; const svn_delta_editor_t *editor; @@ -936,6 +942,7 @@ ccb.new_rev = &new_rev; ccb.date = &date; ccb.author = &author; + ccb.post_commit_err = &post_commit_err; /* ### Note that svn_repos_get_commit_editor actually wants a decoded URL. */ SVN_CMD_ERR(svn_repos_get_commit_editor4 (&editor, &edit_baton, b->repos, NULL, @@ -961,8 +968,8 @@ if (! keep_locks && lock_tokens) SVN_ERR(unlock_paths(lock_tokens, b, pool)); - SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "r(?c)(?c)", - new_rev, date, author)); + SVN_ERR(svn_ra_svn_write_tuple(conn, pool, "r(?c)(?c)(?c)", + new_rev, date, author, post_commit_err)); if (! b->tunnel) SVN_ERR(svn_fs_deltify_revision(b->fs, new_rev, pool));