=== subversion/include/svn_ra_svn.h ================================================================== --- subversion/include/svn_ra_svn.h (revision 49926) +++ subversion/include/svn_ra_svn.h (local) @@ -346,8 +346,17 @@ /** Receive edit commands over the network and use them to drive @a editor * with @a edit_baton. On return, @a *aborted will be set if the edit was - * aborted. + * aborted. The drive can be terminated with a finish-replay command only + * if @a for_replay is true. */ +svn_error_t *svn_ra_svn_drive_editor2(svn_ra_svn_conn_t *conn, apr_pool_t *pool, + const svn_delta_editor_t *editor, + void *edit_baton, + svn_boolean_t *aborted, + svn_boolean_t for_replay); + +/** Like svn_ra_svn_drive_editor2, but with @a for_replay always FALSE. + */ svn_error_t *svn_ra_svn_drive_editor(svn_ra_svn_conn_t *conn, apr_pool_t *pool, const svn_delta_editor_t *editor, void *edit_baton, === subversion/libsvn_ra_dav/replay.c ================================================================== --- subversion/libsvn_ra_dav/replay.c (revision 49926) +++ subversion/libsvn_ra_dav/replay.c (local) @@ -389,7 +389,7 @@ if (rb->dirs->nelts) svn_pool_destroy(APR_ARRAY_IDX(rb->dirs, 0, dir_item_t).pool); - rb->err = rb->editor->close_edit(rb->edit_baton, rb->pool); + rb->err = SVN_NO_ERROR; break; case ELEM_apply_textdelta: === subversion/libsvn_ra_local/ra_plugin.c ================================================================== --- subversion/libsvn_ra_local/ra_plugin.c (revision 49926) +++ subversion/libsvn_ra_local/ra_plugin.c (local) @@ -1266,8 +1266,6 @@ send_deltas, editor, edit_baton, NULL, NULL, pool)); - SVN_ERR(editor->close_edit(edit_baton, pool)); - return SVN_NO_ERROR; } === subversion/libsvn_ra_serf/replay.c ================================================================== --- subversion/libsvn_ra_serf/replay.c (revision 49926) +++ subversion/libsvn_ra_serf/replay.c (local) @@ -406,7 +406,6 @@ if (state == REPORT && strcmp(name.name, "editor-report") == 0) { - SVN_ERR(ctx->editor->close_edit(ctx->editor_baton, parser->state->pool)); svn_ra_serf__xml_pop_state(parser); } else if (state == OPEN_DIR && strcmp(name.name, "open-directory") == 0) === subversion/libsvn_ra_svn/client.c ================================================================== --- subversion/libsvn_ra_svn/client.c (revision 49926) +++ subversion/libsvn_ra_svn/client.c (local) @@ -1862,8 +1862,8 @@ _("Server doesn't support the replay " "command"))); - SVN_ERR(svn_ra_svn_drive_editor(sess->conn, pool, editor, edit_baton, - NULL)); + SVN_ERR(svn_ra_svn_drive_editor2(sess->conn, pool, editor, edit_baton, + NULL, TRUE)); SVN_ERR(svn_ra_svn_read_cmd_response(sess->conn, pool, "")); === subversion/libsvn_ra_svn/editor.c ================================================================== --- subversion/libsvn_ra_svn/editor.c (revision 49926) +++ subversion/libsvn_ra_svn/editor.c (local) @@ -67,6 +67,7 @@ apr_hash_t *tokens; svn_boolean_t *aborted; apr_pool_t *pool; + svn_boolean_t for_replay; } ra_svn_driver_state_t; typedef struct { @@ -704,6 +705,18 @@ return svn_ra_svn_write_cmd_response(conn, pool, ""); } +static svn_error_t *ra_svn_handle_finish_replay(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + apr_array_header_t *params, + void *baton) +{ + ra_svn_driver_state_t *ds = baton; + if (!ds->for_replay) + return svn_error_createf(SVN_ERR_RA_SVN_UNKNOWN_CMD, NULL, + _("Command 'finish-replay' invalid outside of replays")); + return SVN_NO_ERROR; +} + static const svn_ra_svn_cmd_entry_t ra_svn_edit_commands[] = { { "target-rev", ra_svn_handle_target_rev }, { "open-root", ra_svn_handle_open_root }, @@ -719,23 +732,39 @@ { "close-file", ra_svn_handle_close_file }, { "close-edit", ra_svn_handle_close_edit, TRUE }, { "abort-edit", ra_svn_handle_abort_edit, TRUE }, + { "finish-replay", ra_svn_handle_finish_replay, TRUE }, { NULL } }; -svn_error_t *svn_ra_svn_drive_editor(svn_ra_svn_conn_t *conn, apr_pool_t *pool, - const svn_delta_editor_t *editor, - void *edit_baton, - svn_boolean_t *aborted) +svn_error_t *svn_ra_svn_drive_editor2(svn_ra_svn_conn_t *conn, apr_pool_t *pool, + const svn_delta_editor_t *editor, + void *edit_baton, + svn_boolean_t *aborted, + svn_boolean_t for_replay) { ra_svn_driver_state_t state; if (svn_ra_svn_has_capability(conn, SVN_RA_SVN_CAP_EDIT_PIPELINE)) - return svn_ra_svn__drive_editorp(conn, pool, editor, edit_baton, aborted); + return svn_ra_svn__drive_editorp(conn, pool, editor, edit_baton, aborted, for_replay); state.editor = editor; state.edit_baton = edit_baton; state.tokens = apr_hash_make(pool); state.aborted = aborted; state.pool = pool; + state.for_replay = for_replay; return svn_ra_svn_handle_commands(conn, pool, ra_svn_edit_commands, &state); } + +svn_error_t *svn_ra_svn_drive_editor(svn_ra_svn_conn_t *conn, apr_pool_t *pool, + const svn_delta_editor_t *editor, + void *edit_baton, + svn_boolean_t *aborted) +{ + return svn_ra_svn_drive_editor2(conn, + pool, + editor, + edit_baton, + aborted, + FALSE); +} === subversion/libsvn_ra_svn/editorp.c ================================================================== --- subversion/libsvn_ra_svn/editorp.c (revision 49926) +++ subversion/libsvn_ra_svn/editorp.c (local) @@ -71,6 +71,7 @@ apr_pool_t *pool; apr_pool_t *file_pool; int file_refs; + svn_boolean_t for_replay; } ra_svn_driver_state_t; /* Works for both directories and files; however, the pool handling is @@ -788,6 +789,20 @@ return svn_ra_svn_write_cmd_response(conn, pool, ""); } +static svn_error_t *ra_svn_handle_finish_replay(svn_ra_svn_conn_t *conn, + apr_pool_t *pool, + apr_array_header_t *params, + ra_svn_driver_state_t *ds) +{ + if (!ds->for_replay) + return svn_error_createf(SVN_ERR_RA_SVN_UNKNOWN_CMD, NULL, + _("Command 'finish-replay' invalid outside of replays")); + ds->done = TRUE; + if (ds->aborted) + *ds->aborted = FALSE; + return SVN_NO_ERROR; +} + static const struct { const char *cmd; svn_error_t *(*handler)(svn_ra_svn_conn_t *conn, apr_pool_t *pool, @@ -812,6 +827,7 @@ { "absent-file", ra_svn_handle_absent_file }, { "close-edit", ra_svn_handle_close_edit }, { "abort-edit", ra_svn_handle_abort_edit }, + { "finish-replay", ra_svn_handle_finish_replay }, { NULL } }; @@ -837,7 +853,8 @@ apr_pool_t *pool, const svn_delta_editor_t *editor, void *edit_baton, - svn_boolean_t *aborted) + svn_boolean_t *aborted, + svn_boolean_t for_replay) { ra_svn_driver_state_t state; apr_pool_t *subpool = svn_pool_create(pool); @@ -854,6 +871,7 @@ state.pool = pool; state.file_pool = svn_pool_create(pool); state.file_refs = 0; + state.for_replay = for_replay; while (!state.done) { === subversion/libsvn_ra_svn/protocol ================================================================== --- subversion/libsvn_ra_svn/protocol (revision 49926) +++ subversion/libsvn_ra_svn/protocol (local) @@ -461,6 +461,10 @@ params: ( ) response: ( ) + finish-replay + params: ( ) + Only delivered from server to client, at the end of a replay. + 3.1.3. Report Command Set To reduce round-trip delays, report commands do not return responses. === subversion/libsvn_ra_svn/ra_svn.h ================================================================== --- subversion/libsvn_ra_svn/ra_svn.h (revision 49926) +++ subversion/libsvn_ra_svn/ra_svn.h (local) @@ -92,7 +92,8 @@ apr_pool_t *pool, const svn_delta_editor_t *editor, void *edit_baton, - svn_boolean_t *aborted); + svn_boolean_t *aborted, + svn_boolean_t for_replay); /* CRAM-MD5 client implementation. */ svn_error_t *svn_ra_svn__cram_client(svn_ra_svn_conn_t *conn, apr_pool_t *pool, === subversion/mod_dav_svn/reports/replay.c ================================================================== --- subversion/mod_dav_svn/reports/replay.c (revision 49926) +++ subversion/mod_dav_svn/reports/replay.c (local) @@ -63,7 +63,16 @@ return SVN_NO_ERROR; } +static svn_error_t * +end_report(edit_baton_t *eb) +{ + SVN_ERR(dav_svn__send_xml(eb->bb, eb->output, + "" DEBUG_CR)); + return SVN_NO_ERROR; +} + + static svn_error_t * maybe_close_textdelta(edit_baton_t *eb) { @@ -379,11 +388,6 @@ static svn_error_t * close_edit(void *edit_baton, apr_pool_t *pool) { - edit_baton_t *eb = edit_baton; - - SVN_ERR(dav_svn__send_xml(eb->bb, eb->output, - "" DEBUG_CR)); - return SVN_NO_ERROR; } @@ -531,7 +535,7 @@ "Problem replaying revision", resource->pool); - if ((err = editor->close_edit(edit_baton, resource->pool))) + if ((err = end_report(edit_baton))) return dav_svn__convert_err(err, HTTP_INTERNAL_SERVER_ERROR, "Problem closing editor drive", resource->pool); === subversion/svnserve/serve.c ================================================================== --- subversion/svnserve/serve.c (revision 49926) +++ subversion/svnserve/serve.c (local) @@ -1984,13 +1984,12 @@ err = svn_repos_replay2(root, b->fs_path->data, low_water_mark, send_deltas, editor, edit_baton, authz_check_access_cb_func(b), b, pool); - if (! err) - SVN_CMD_ERR(editor->close_edit(edit_baton, pool)); if (err) svn_error_clear(editor->abort_edit(edit_baton, pool)); SVN_CMD_ERR(err); + SVN_ERR(svn_ra_svn_write_cmd(conn, pool, "finish-replay", "")); SVN_ERR(svn_ra_svn_write_cmd_response(conn, pool, "")); return SVN_NO_ERROR; === subversion/svnsync/main.c ================================================================== --- subversion/svnsync/main.c (revision 49926) +++ subversion/svnsync/main.c (local) @@ -1075,6 +1075,8 @@ SVN_ERR(svn_ra_replay(from_session, current, 0, TRUE, cancel_editor, cancel_baton, subpool)); + SVN_ERR(cancel_editor->close_edit(cancel_baton, subpool)); + /* Sanity check that we actually committed the revision we meant to. */ if (baton->committed_rev != current) return svn_error_createf