📄 commit.c
字号:
ne_xml_destroy(parent->cc->cb->error_parser); } /* Add this path to the valid targets hash. */ add_valid_target(parent->cc, path, svn_nonrecursive); svn_pool_destroy(workpool); /* return the file_baton */ *file_baton = file; return SVN_NO_ERROR;}static svn_error_t * commit_open_file(const char *path, void *parent_baton, svn_revnum_t base_revision, apr_pool_t *file_pool, void **file_baton){ resource_baton_t *parent = parent_baton; resource_baton_t *file; const char *name = svn_path_basename(path, file_pool); apr_pool_t *workpool = svn_pool_create(file_pool); version_rsrc_t *rsrc = NULL; file = apr_pcalloc(file_pool, sizeof(*file)); file->pool = file_pool; file->cc = parent->cc; file->created = FALSE; SVN_ERR(add_child(&rsrc, parent->cc, parent->rsrc, name, 0, base_revision, workpool)); file->rsrc = dup_resource(rsrc, file_pool); if (parent->cc->tokens) file->token = apr_hash_get(parent->cc->tokens, path, APR_HASH_KEY_STRING); /* do the CHECKOUT now. we'll PUT the new file contents later on. */ SVN_ERR(checkout_resource(parent->cc, file->rsrc, TRUE, file->token, workpool)); /* ### wait for apply_txdelta before doing a PUT. it might arrive a ### "long time" from now. certainly after many other operations, so ### we don't want to start a PUT just yet. ### so... anything else to do here? what about the COPY case? */ svn_pool_destroy(workpool); *file_baton = file; return SVN_NO_ERROR;}static svn_error_t * commit_stream_write(void *baton, const char *data, apr_size_t *len){ put_baton_t *pb = baton; apr_status_t status; /* drop the data into our temp file */ status = apr_file_write_full(pb->tmpfile, data, *len, NULL); if (status) return svn_error_wrap_apr(status, _("Could not write svndiff to temp file")); return SVN_NO_ERROR;}static svn_error_t * commit_apply_txdelta(void *file_baton, const char *base_checksum, apr_pool_t *pool, svn_txdelta_window_handler_t *handler, void **handler_baton){ resource_baton_t *file = file_baton; put_baton_t *baton; svn_stream_t *stream; baton = apr_pcalloc(file->pool, sizeof(*baton)); file->put_baton = baton; if (base_checksum) baton->base_checksum = apr_pstrdup(file->pool, base_checksum); else baton->base_checksum = NULL; /* ### oh, hell. Neon's request body support is either text (a C string), ### or a FILE*. since we are getting binary data, we must use a FILE* ### for now. isn't that special? */ /* Use the client callback to create a tmpfile. */ SVN_ERR(file->cc->ras->callbacks->open_tmp_file (&baton->tmpfile, file->cc->ras->callback_baton, file->pool)); /* ### register a cleanup on file_pool which closes the file; this ### will ensure that the file always gets tossed, even if we exit ### with an error. */ stream = svn_stream_create(baton, pool); svn_stream_set_write(stream, commit_stream_write); svn_txdelta_to_svndiff(stream, pool, handler, handler_baton); /* Add this path to the valid targets hash. */ add_valid_target(file->cc, file->rsrc->local_path, svn_nonrecursive); return SVN_NO_ERROR;}static svn_error_t * commit_change_file_prop(void *file_baton, const char *name, const svn_string_t *value, apr_pool_t *pool){ resource_baton_t *file = file_baton; /* record the change. it will be applied at close_file time. */ /* ### we should put this into the file_baton's pool */ record_prop_change(file->pool, file, name, value); /* do the CHECKOUT sooner rather than later */ SVN_ERR(checkout_resource(file->cc, file->rsrc, TRUE, file->token, pool)); /* Add this path to the valid targets hash. */ add_valid_target(file->cc, file->rsrc->local_path, svn_nonrecursive); return SVN_NO_ERROR;}static svn_error_t * commit_close_file(void *file_baton, const char *text_checksum, apr_pool_t *pool){ resource_baton_t *file = file_baton; commit_ctx_t *cc = file->cc; if (file->put_baton) { ne_session *sess = cc->ras->sess; put_baton_t *pb = file->put_baton; const char *url = file->rsrc->wr_url; ne_request *req; int code; svn_error_t *err; /* create/prep the request */ req = ne_request_create(sess, "PUT", url); if (req == NULL) { return svn_error_createf(SVN_ERR_RA_DAV_CREATING_REQUEST, NULL, _("Could not create a PUT request (%s)"), url); } ne_add_request_header(req, "Content-Type", SVN_SVNDIFF_MIME_TYPE); if (file->token) { const char *token_header_val; const char *token_uri; token_uri = svn_path_url_add_component(cc->ras->url->data, file->rsrc->url, pool); token_header_val = apr_psprintf(pool, "<%s> (<%s>)", token_uri, file->token); ne_add_request_header(req, "If", token_header_val); } if (pb->base_checksum) ne_add_request_header (req, SVN_DAV_BASE_FULLTEXT_MD5_HEADER, pb->base_checksum); if (text_checksum) ne_add_request_header (req, SVN_DAV_RESULT_FULLTEXT_MD5_HEADER, text_checksum); /* Give the file to neon. The provider will rewind the file. */ err = svn_ra_dav__set_neon_body_provider(req, pb->tmpfile); if (err) { apr_file_close(pb->tmpfile); ne_request_destroy(req); return err; } /* run the request and get the resulting status code (and svn_error_t) */ err = svn_ra_dav__request_dispatch(&code, req, sess, "PUT", url, 201 /* Created */, 204 /* No Content */,#ifdef SVN_NEON_0_25 NULL, NULL,#endif /* SVN_NEON_0_25 */ pool); /* we're done with the file. this should delete it. */ (void) apr_file_close(pb->tmpfile); if (err) return err; } /* Perform all of the property changes on the file. Note that we checked out the file when the first prop change was noted. */ SVN_ERR(do_proppatch(cc->ras, file->rsrc, file, pool)); return SVN_NO_ERROR;}static svn_error_t * commit_close_edit(void *edit_baton, apr_pool_t *pool){ commit_ctx_t *cc = edit_baton; svn_commit_info_t *commit_info = svn_create_commit_info(pool); 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, cc->valid_targets, cc->tokens, cc->keep_locks, cc->disable_merge_response, pool)); SVN_ERR(delete_activity(edit_baton, pool)); SVN_ERR(svn_ra_dav__maybe_store_auth_info(cc->ras, pool)); if (commit_info->revision != SVN_INVALID_REVNUM) SVN_ERR(cc->callback(commit_info, cc->callback_baton, pool)); return SVN_NO_ERROR;}static svn_error_t * commit_abort_edit(void *edit_baton, apr_pool_t *pool){ return delete_activity(edit_baton, pool);}static svn_error_t * apply_log_message(commit_ctx_t *cc, const char *log_msg, apr_pool_t *pool){ const svn_string_t *vcc; const svn_string_t *baseline_url; version_rsrc_t baseline_rsrc = { SVN_INVALID_REVNUM }; ne_proppatch_operation po[2] = { { 0 } }; int rv; svn_stringbuf_t *xml_data; svn_error_t *err = NULL; int retry_count = 5; /* ### this whole sequence can/should be replaced with an expand-property ### REPORT when that is available on the server. */ /* fetch the DAV:version-controlled-configuration from the session's URL */ SVN_ERR(svn_ra_dav__get_one_prop(&vcc, cc->ras->sess, cc->ras->root.path, NULL, &svn_ra_dav__vcc_prop, pool)); /* ### we should use DAV:apply-to-version on the CHECKOUT so we can skip ### retrieval of the baseline */ do { svn_error_clear(err); /* Get the latest baseline from VCC's DAV:checked-in property. This should give us the HEAD revision of the moment. */ SVN_ERR(svn_ra_dav__get_one_prop(&baseline_url, cc->ras->sess, vcc->data, NULL, &svn_ra_dav__checked_in_prop, pool)); baseline_rsrc.pool = pool; baseline_rsrc.vsn_url = baseline_url->data; /* To set the log message, we must checkout the latest baseline and get back a mutable "working" baseline. */ err = checkout_resource(cc, &baseline_rsrc, FALSE, NULL, pool); /* There's a small chance of a race condition here, if apache is experiencing heavy commit concurrency or if the network has long latency. It's possible that the value of HEAD changed between the time we fetched the latest baseline and the time we checkout that baseline. If that happens, apache will throw us a BAD_BASELINE error (deltaV says you can only checkout the latest baseline). We just ignore that specific error and retry a few times, asking for the latest baseline again. */ if (err && err->apr_err != SVN_ERR_APMOD_BAD_BASELINE) return err; } while (err && (--retry_count > 0)); /* Yikes, if we couldn't hold onto HEAD after a few retries, throw a real error.*/ if (err) return err; /* XML-Escape the log message. */ xml_data = NULL; /* Required by svn_xml_escape_*. */ svn_xml_escape_cdata_cstring(&xml_data, log_msg, pool); po[0].name = &log_message_prop; po[0].type = ne_propset; po[0].value = xml_data->data; rv = ne_proppatch(cc->ras->sess, baseline_rsrc.wr_url, po); if (rv != NE_OK) { const char *msg = apr_psprintf(pool, _("applying log message to %s"), baseline_rsrc.wr_url); return svn_ra_dav__convert_error(cc->ras->sess, msg, rv, pool); } return SVN_NO_ERROR;}svn_error_t * svn_ra_dav__get_commit_editor(svn_ra_session_t *session, const svn_delta_editor_t **editor, void **edit_baton, const char *log_msg, svn_commit_callback2_t callback, void *callback_baton, apr_hash_t *lock_tokens, svn_boolean_t keep_locks, apr_pool_t *pool){ svn_ra_dav__session_t *ras = session->priv; svn_delta_editor_t *commit_editor; commit_ctx_t *cc; /* Only initialize the baton the first time through. */ if (! ras->cb) { /* Build a copy_baton for COPY requests. */ ras->cb = apr_pcalloc(ras->pool, sizeof(*ras->cb)); /* Register request hooks in the neon session. They specifically allow any COPY requests (ne_copy()) to parse <D:error> responses. They're no-ops for other requests. */ ne_hook_create_request(ras->sess, create_request_hook, ras->cb); ne_hook_pre_send(ras->sess, pre_send_hook, ras->cb); } /* Make sure the baton uses our current pool, so we don't leak. */ ras->cb->pool = pool; /* Build the main commit editor's baton. */ cc = apr_pcalloc(pool, sizeof(*cc)); cc->ras = ras; cc->valid_targets = apr_hash_make(pool); cc->get_func = ras->callbacks->get_wc_prop; cc->push_func = ras->callbacks->push_wc_prop; cc->cb_baton = ras->callback_baton; cc->log_msg = log_msg; cc->callback = callback; cc->callback_baton = callback_baton; cc->tokens = lock_tokens; cc->keep_locks = keep_locks; cc->cb = ras->cb; /* If the caller didn't give us any way of storing wcprops, then there's no point in getting back a MERGE response full of VR's. */ if (ras->callbacks->push_wc_prop == NULL) cc->disable_merge_response = TRUE; /* ### should we perform an OPTIONS to validate the server we're about ### to talk to? */ /* ** Create an Activity. This corresponds directly to an FS transaction. ** We will check out all further resources within the context of this ** activity. */ SVN_ERR(create_activity(cc, pool)); /* ** Find the latest baseline resource, check it out, and then apply the ** log message onto the thing. */ SVN_ERR(apply_log_message(cc, log_msg, pool)); /* ** Set up the editor. ** ** This structure is used during the commit process. An external caller ** uses these callbacks to describe all the changes in the working copy ** that must be committed to the server. */ commit_editor = svn_delta_default_editor(pool); commit_editor->open_root = commit_open_root; commit_editor->delete_entry = commit_delete_entry; commit_editor->add_directory = commit_add_dir; commit_editor->open_directory = commit_open_dir; commit_editor->change_dir_prop = commit_change_dir_prop; commit_editor->close_directory = commit_close_dir; commit_editor->add_file = commit_add_file; commit_editor->open_file = commit_open_file; commit_editor->apply_textdelta = commit_apply_txdelta; commit_editor->change_file_prop = commit_change_file_prop; commit_editor->close_file = commit_close_file; commit_editor->close_edit = commit_close_edit; commit_editor->abort_edit = commit_abort_edit; *editor = commit_editor; *edit_baton = cc; return SVN_NO_ERROR;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -