/* This implements svn_editor_cb_complete_t */ static svn_error_t * complete_cb(void *baton, apr_pool_t *scratch_pool) { struct ev2_baton *eb = baton; svn_revnum_t revision; svn_error_t *post_commit_err; const char *conflict_path; svn_error_t *err; const char *post_commit_errstr; apr_hash_t *hooks_env; /* Parse the hooks-env file (if any). */ SVN_ERR(svn_repos__parse_hooks_env(&hooks_env, eb->repos->hooks_env_path, scratch_pool, scratch_pool)); /* The transaction has been fully edited. Let the pre-commit hook have a look at the thing. */ SVN_ERR(svn_repos__hooks_pre_commit(eb->repos, hooks_env, eb->txn_name, scratch_pool)); /* Hook is done. Let's do the actual commit. */ SVN_ERR(svn_fs__editor_commit(&revision, &post_commit_err, &conflict_path, eb->inner, scratch_pool, scratch_pool)); /* Did a conflict occur during the commit process? */ if (conflict_path != NULL) return svn_error_createf(SVN_ERR_FS_CONFLICT, NULL, _("Conflict at '%s'"), conflict_path); /* Since did not receive an error during the commit process, and no conflict was specified... we committed a revision. Run the hooks. Other errors may have occurred within the FS (specified by the POST_COMMIT_ERR localvar), but we need to run the hooks. */ SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision)); err = svn_repos__hooks_post_commit(eb->repos, hooks_env, revision, eb->txn_name, scratch_pool); if (err) err = svn_error_create(SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED, err, _("Commit succeeded, but post-commit hook failed")); /* Combine the FS errors with the hook errors, and stringify. */ err = svn_error_compose_create(post_commit_err, err); if (err) { post_commit_errstr = svn_repos__post_commit_error_str(err, scratch_pool); svn_error_clear(err); } else { post_commit_errstr = NULL; } return svn_error_trace(invoke_commit_cb(eb->commit_cb, eb->commit_baton, eb->repos->fs, revision, post_commit_errstr, scratch_pool)); }
static svn_error_t * close_edit(void *edit_baton, apr_pool_t *pool) { struct edit_baton *eb = edit_baton; svn_revnum_t new_revision = SVN_INVALID_REVNUM; svn_error_t *err; const char *conflict; const 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 error. */ if (! eb->txn) return svn_error_create(SVN_ERR_REPOS_BAD_ARGS, NULL, "No valid transaction supplied to close_edit"); /* Commit. */ err = svn_repos_fs_commit_txn(&conflict, eb->repos, &new_revision, eb->txn, pool); if (SVN_IS_VALID_REVNUM(new_revision)) { if (err) { /* If the error was in post-commit, then the commit itself succeeded. In which case, save the post-commit warning (to be reported back to the client, who will probably display it as a warning) and clear the error. */ post_commit_err = svn_repos__post_commit_error_str(err, pool); svn_error_clear(err); err = SVN_NO_ERROR; } } else { /* ### todo: we should check whether it really was a conflict, and return the conflict info if so? */ /* If the commit failed, it's *probably* due to a conflict -- that is, the txn being out-of-date. The filesystem gives us the ability to continue diddling the transaction and try again; but let's face it: that's not how the cvs or svn works from a user interface standpoint. Thus we don't make use of this fs feature (for now, at least.) So, in a nutshell: svn commits are an all-or-nothing deal. Each commit creates a new fs txn which either succeeds or is aborted completely. No second chances; the user simply needs to update and commit again :) */ eb->txn_aborted = TRUE; return svn_error_trace( svn_error_compose_create(err, svn_fs_abort_txn(eb->txn, pool))); } /* Pass new revision information to the caller's callback. */ { svn_string_t *date, *author; svn_commit_info_t *commit_info; /* Even if there was a post-commit hook failure, it's more serious if one of the calls here fails, so we explicitly check for errors here, while saving the possible post-commit error for later. */ err = svn_fs_revision_prop(&date, svn_repos_fs(eb->repos), new_revision, SVN_PROP_REVISION_DATE, pool); if (! err) { err = svn_fs_revision_prop(&author, svn_repos_fs(eb->repos), new_revision, SVN_PROP_REVISION_AUTHOR, pool); } if ((! err) && eb->commit_callback) { commit_info = svn_create_commit_info(pool); /* fill up the svn_commit_info structure */ 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; err = (*eb->commit_callback)(commit_info, eb->commit_callback_baton, pool); } } return svn_error_trace(err); }
static svn_error_t * close_edit(void *edit_baton, apr_pool_t *pool) { struct edit_baton *eb = edit_baton; svn_revnum_t new_revision = SVN_INVALID_REVNUM; svn_error_t *err; const char *conflict; const 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 error. */ if (! eb->txn) return svn_error_create(SVN_ERR_REPOS_BAD_ARGS, NULL, "No valid transaction supplied to close_edit"); /* Commit. */ err = svn_repos_fs_commit_txn(&conflict, eb->repos, &new_revision, eb->txn, pool); if (SVN_IS_VALID_REVNUM(new_revision)) { /* The actual commit succeeded, i.e. the transaction does no longer exist and we can't use txn_root for conflict resolution etc. Since close_edit is supposed to release resources, do it now. */ if (eb->txn_root) svn_fs_close_root(eb->txn_root); if (err) { /* If the error was in post-commit, then the commit itself succeeded. In which case, save the post-commit warning (to be reported back to the client, who will probably display it as a warning) and clear the error. */ post_commit_err = svn_repos__post_commit_error_str(err, pool); svn_error_clear(err); } /* Make sure a future abort doesn't perform any work. This may occur if the commit callback returns an error! */ eb->txn = NULL; eb->txn_root = NULL; } else { /* ### todo: we should check whether it really was a conflict, and return the conflict info if so? */ /* If the commit failed, it's *probably* due to a conflict -- that is, the txn being out-of-date. The filesystem gives us the ability to continue diddling the transaction and try again; but let's face it: that's not how the cvs or svn works from a user interface standpoint. Thus we don't make use of this fs feature (for now, at least.) So, in a nutshell: svn commits are an all-or-nothing deal. Each commit creates a new fs txn which either succeeds or is aborted completely. No second chances; the user simply needs to update and commit again :) */ eb->txn_aborted = TRUE; return svn_error_trace( svn_error_compose_create(err, svn_fs_abort_txn(eb->txn, pool))); } /* At this point, the post-commit error has been converted to a string. That information will be passed to a callback, if provided. If the callback invocation fails in some way, that failure is returned here. IOW, the post-commit error information is low priority compared to other gunk here. */ /* Pass new revision information to the caller's callback. */ return svn_error_trace(invoke_commit_cb(eb->commit_callback, eb->commit_callback_baton, eb->repos->fs, new_revision, post_commit_err, pool)); }