📄 commit.c
字号:
/* * commit.c : routines for committing changes to the server * * ==================================================================== * Copyright (c) 2000-2004 CollabNet. All rights reserved. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://subversion.tigris.org/license-1.html. * If newer versions of this license are posted there, you may use a * newer version instead, at your option. * * This software consists of voluntary contributions made by many * individuals. For exact contribution history, see the revision * history and logs, available at http://subversion.tigris.org/. * ==================================================================== */#include <apr_pools.h>#include <apr_hash.h>#include <apr_uuid.h>#define APR_WANT_STDIO#define APR_WANT_STRFUNC#include <apr_want.h>#include <stdlib.h> /* for free() */#include <assert.h>#include <ne_socket.h>#include <ne_request.h>#include <ne_props.h>#include <ne_basic.h>#include "svn_pools.h"#include "svn_error.h"#include "svn_delta.h"#include "svn_io.h"#include "svn_ra.h"#include "../libsvn_ra/ra_loader.h"#include "svn_path.h"#include "svn_xml.h"#include "svn_dav.h"#include "svn_props.h"#include "svn_private_config.h"#include "ra_dav.h"/*** version_rsrc_t: identify the relevant pieces of a resource on the server**** REVISION is the resource's revision, or SVN_INVALID_REVNUM if it is** new or is the HEAD.**** URL refers to the public/viewable/original resource.** VSN_URL refers to the version resource that we stored locally** WR_URL refers to a working resource for this resource**** Note that VSN_URL is NULL if this resource has just been added, and** WR_URL can be NULL if the resource has not (yet) been checked out.**** LOCAL_PATH is relative to the root of the commit. It will be used** for the get_func, push_func, and close_func callbacks.*/typedef struct{ svn_revnum_t revision; const char *url; const char *vsn_url; const char *wr_url; const char *local_path; apr_pool_t *pool; /* pool in which this resource is allocated. */} version_rsrc_t;/* Context for parsing <D:error> bodies, used when we call ne_copy(). */struct copy_baton{ /* An indication of whether we're currently processing a COPY request * or not. */ svn_boolean_t making_a_copy; /* A parser for handling <D:error> responses from mod_dav_svn. This * will be NULL if we haven't processed a COPY request yet. */ ne_xml_parser *error_parser; /* If <D:error> is returned, here's where the parsed result goes, or * NULL otherwise. */ svn_error_t *err; /* A place for allocating fields in this structure. */ apr_pool_t *pool;};typedef struct{ svn_ra_dav__session_t *ras; const char *activity_url; apr_hash_t *valid_targets; svn_ra_get_wc_prop_func_t get_func; svn_ra_push_wc_prop_func_t push_func; void *cb_baton; svn_boolean_t disable_merge_response; /* The (potential) author of this commit. */ const char *user; /* Log message for the commit. */ const char *log_msg; /* The commit callback and baton */ svn_commit_callback2_t callback; void *callback_baton; /* The hash of lock-tokens owned by the working copy. */ apr_hash_t *tokens; /* Whether or not to keep the locks after commit is done. */ svn_boolean_t keep_locks; /* A context for neon COPY request callbacks. */ struct copy_baton *cb;} commit_ctx_t;typedef struct{ apr_file_t *tmpfile; svn_stringbuf_t *fname; const char *base_checksum; /* hex md5 of base text; may be null */} put_baton_t;typedef struct{ commit_ctx_t *cc; version_rsrc_t *rsrc; apr_hash_t *prop_changes; /* name/values pairs of new/changed properties. */ apr_array_header_t *prop_deletes; /* names of properties to delete. */ svn_boolean_t created; /* set if this is an add rather than an update */ apr_pool_t *pool; /* the pool from open_foo() / add_foo() */ put_baton_t *put_baton; /* baton for this file's PUT request */ const char *token; /* file's lock token, if available */} resource_baton_t;/* this property will be fetched from the server when we don't find it cached in the WC property store. */static const ne_propname fetch_props[] ={ { "DAV:", "checked-in" }, { NULL }};static const ne_propname log_message_prop = { SVN_DAV_PROP_NS_SVN, "log" };/* perform a deep copy of BASE into POOL, and return the result. */static version_rsrc_t * dup_resource(version_rsrc_t *base, apr_pool_t *pool){ version_rsrc_t *rsrc = apr_pcalloc(pool, sizeof(*rsrc)); rsrc->pool = pool; rsrc->revision = base->revision; rsrc->url = base->url ? apr_pstrdup(pool, base->url) : NULL; rsrc->vsn_url = base->vsn_url ? apr_pstrdup(pool, base->vsn_url) : NULL; rsrc->wr_url = base->wr_url ? apr_pstrdup(pool, base->wr_url) : NULL; rsrc->local_path = base->local_path ? apr_pstrdup(pool, base->local_path) : NULL; return rsrc;}static svn_error_t * simple_request(svn_ra_dav__session_t *ras, const char *method, const char *url, int *code, apr_hash_t *extra_headers, int okay_1, int okay_2, apr_pool_t *pool){ ne_request *req; /* create/prep the request */ req = ne_request_create(ras->sess, method, url); if (req == NULL) { return svn_error_createf(SVN_ERR_RA_DAV_CREATING_REQUEST, NULL, _("Could not create a request (%s '%s')"), method, url); } /* add any extra headers passed in by caller. */ if (extra_headers != NULL) { apr_hash_index_t *hi; for (hi = apr_hash_first(pool, extra_headers); hi; hi = apr_hash_next(hi)) { const void *key; void *val; apr_hash_this(hi, &key, NULL, &val); ne_add_request_header(req, (const char *) key, (const char *) val); } } /* run the request and get the resulting status code (and svn_error_t) */ return svn_ra_dav__request_dispatch(code, req, ras->sess, method, url, okay_1, okay_2,#ifdef SVN_NEON_0_25 NULL, NULL,#endif /* SVN_NEON_0_25 */ pool);}static svn_error_t * delete_activity(void *edit_baton, apr_pool_t *pool){ commit_ctx_t *cc = edit_baton; return simple_request(cc->ras, "DELETE", cc->activity_url, NULL, NULL, 204 /* No Content */, 404 /* Not Found */, pool);}/* Get the version resource URL for RSRC, storing it in RSRC->vsn_url. Use POOL for all temporary allocations. */static svn_error_t * get_version_url(commit_ctx_t *cc, version_rsrc_t *rsrc, svn_boolean_t force, apr_pool_t *pool){ svn_ra_dav_resource_t *propres; const char *url; const svn_string_t *url_str; if (!force && cc->get_func != NULL) { const svn_string_t *vsn_url_value; SVN_ERR((*cc->get_func)(cc->cb_baton, rsrc->local_path, SVN_RA_DAV__LP_VSN_URL, &vsn_url_value, pool)); if (vsn_url_value != NULL) { rsrc->vsn_url = apr_pstrdup(rsrc->pool, vsn_url_value->data); return SVN_NO_ERROR; } /* whoops. it wasn't there. go grab it from the server. */ } if (rsrc->revision == SVN_INVALID_REVNUM) { /* We aren't trying to get a specific version -- use the HEAD. We fetch the version URL from the public URL. */ url = rsrc->url; } else { svn_string_t bc_url; svn_string_t bc_relative; /* The version URL comes from a resource in the Baseline Collection. */ SVN_ERR(svn_ra_dav__get_baseline_info(NULL, &bc_url, &bc_relative, NULL, cc->ras->sess, rsrc->url, rsrc->revision, pool)); url = svn_path_url_add_component(bc_url.data, bc_relative.data, pool); } /* Get the DAV:checked-in property, which contains the URL of the Version Resource */ SVN_ERR(svn_ra_dav__get_props_resource(&propres, cc->ras->sess, url, NULL, fetch_props, pool)); url_str = apr_hash_get(propres->propset, SVN_RA_DAV__PROP_CHECKED_IN, APR_HASH_KEY_STRING); if (url_str == NULL) { /* ### need a proper SVN_ERR here */ return svn_error_create(APR_EGENERAL, NULL, _("Could not fetch the Version Resource URL " "(needed during an import or when it is " "missing from the local, cached props)")); } /* ensure we get the proper lifetime for this URL since it is going into a resource object. */ rsrc->vsn_url = apr_pstrdup(rsrc->pool, url_str->data); if (cc->push_func != NULL) { /* Now we can store the new version-url. */ SVN_ERR((*cc->push_func)(cc->cb_baton, rsrc->local_path, SVN_RA_DAV__LP_VSN_URL, url_str, pool)); } return SVN_NO_ERROR;}/* When FORCE is true, then we force a query to the server, ignoring any cached property. */static svn_error_t * get_activity_collection(commit_ctx_t *cc, const svn_string_t **collection, svn_boolean_t force, apr_pool_t *pool){ if (!force && cc->get_func != NULL) { /* with a get_func, we can just ask for the activity URL from the property store. */ /* get the URL where we should create activities */ SVN_ERR((*cc->get_func)(cc->cb_baton, "", SVN_RA_DAV__LP_ACTIVITY_COLL, collection, pool)); if (*collection != NULL) { /* the property was there. return it. */ return SVN_NO_ERROR; } /* property not found for some reason. get it from the server. */ } /* use our utility function to fetch the activity URL */ SVN_ERR(svn_ra_dav__get_activity_collection(collection, cc->ras, cc->ras->root.path, pool)); if (cc->push_func != NULL) { /* save the (new) activity collection URL into the directory */ SVN_ERR((*cc->push_func)(cc->cb_baton, "", SVN_RA_DAV__LP_ACTIVITY_COLL, *collection, pool)); } return SVN_NO_ERROR;}static svn_error_t * create_activity(commit_ctx_t *cc, apr_pool_t *pool){ const svn_string_t * activity_collection; const char *uuid_buf = svn_uuid_generate(pool); int code; const char *url; /* get the URL where we'll create activities, construct the URL for the activity, and create the activity. The URL for our activity will be ACTIVITY_COLL/UUID */ SVN_ERR(get_activity_collection(cc, &activity_collection, FALSE, pool)); url = svn_path_url_add_component(activity_collection->data, uuid_buf, pool); SVN_ERR(simple_request(cc->ras, "MKACTIVITY", url, &code, NULL, 201 /* Created */, 404 /* Not Found */, pool)); /* if we get a 404, then it generally means that the cached activity collection no longer exists. Retry the sequence, but force a query to the server for the activity collection. */ if (code == 404) { SVN_ERR(get_activity_collection(cc, &activity_collection, TRUE, pool)); url = svn_path_url_add_component(activity_collection->data, uuid_buf, pool); SVN_ERR(simple_request(cc->ras, "MKACTIVITY", url, &code, NULL, 201, 0, pool)); } cc->activity_url = url; return SVN_NO_ERROR;}/* Add a child resource. POOL should be as "temporary" as possible, but probably not as far as requiring a new temp pool. */static svn_error_t * add_child(version_rsrc_t **child, commit_ctx_t *cc, const version_rsrc_t *parent, const char *name, int created, svn_revnum_t revision, apr_pool_t *pool){ version_rsrc_t *rsrc; /* ### todo: This from Yoshiki Hayashi <yoshiki@xemacs.org>: Probably created flag in add_child can be removed because
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -