commit.c
来自「linux subdivision ying gai ke yi le ba」· C语言 代码 · 共 1,333 行 · 第 1/4 页
C
1,333 行
apr_pool_t *pool)
{
const svn_string_t * activity_collection;
apr_uuid_t uuid;
char uuid_buf[APR_UUID_FORMATTED_LENGTH + 1];
int code;
const char *url;
/* the URL for our activity will be ACTIVITY_COLL/UUID */
apr_uuid_get(&uuid);
apr_uuid_format(uuid_buf, &uuid);
/* get the URL where we'll create activities, construct the URL
for the activity, and create the activity. */
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(resource_t **child,
commit_ctx_t *cc,
const resource_t *parent,
const char *name,
int created,
svn_revnum_t revision,
apr_pool_t *pool)
{
resource_t *rsrc;
/* ### todo: This from Yoshiki Hayashi <yoshiki@xemacs.org>:
Probably created flag in add_child can be removed because
revision is valid => created is false
revision is invalid => created is true
*/
rsrc = apr_pcalloc(pool, sizeof(*rsrc));
rsrc->pool = pool;
rsrc->revision = revision;
rsrc->url = svn_path_url_add_component(parent->url, name, pool);
rsrc->local_path = svn_path_join(parent->local_path, name, pool);
/* Case 1: the resource is truly "new". Either it was added as a
completely new object, or implicitly created via a COPY. Either
way, it has no VR URL anywhere. However, we *can* derive its WR
URL by the rules of deltaV: "copy structure is preserved below
the WR you COPY to." */
if (created || (parent->vsn_url == NULL))
rsrc->wr_url = svn_path_url_add_component(parent->wr_url, name, pool);
/* Case 2: the resource is already under version-control somewhere.
This means it has a VR URL already, and the WR URL won't exist
until it's "checked out". */
else
SVN_ERR( get_version_url(cc, rsrc, FALSE, pool) );
*child = rsrc;
return SVN_NO_ERROR;
}
static svn_error_t * do_checkout(commit_ctx_t *cc,
const char *vsn_url,
svn_boolean_t allow_404,
int *code,
char **locn,
apr_pool_t *pool)
{
ne_request *req;
const char *body;
/* assert: vsn_url != NULL */
/* ### send a CHECKOUT resource on vsn_url; include cc->activity_url;
### place result into res->wr_url and return it */
/* create/prep the request */
req = ne_request_create(cc->ras->sess, "CHECKOUT", vsn_url);
if (req == NULL)
{
return svn_error_createf(SVN_ERR_RA_DAV_CREATING_REQUEST, NULL,
"Could not create a CHECKOUT request (%s)",
vsn_url);
}
/* ### store this into cc to avoid pool growth */
body = apr_psprintf(pool,
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<D:checkout xmlns:D=\"DAV:\">"
"<D:activity-set>"
"<D:href>%s</D:href>"
"</D:activity-set></D:checkout>", cc->activity_url);
ne_set_request_body_buffer(req, body, strlen(body));
/*
* We have different const qualifiers here. locn is const char **,
* but the prototype is void * (as opposed to const void *).
*/
ne_add_response_header_handler(req, "location",
ne_duplicate_header, (void *)locn);
/* run the request and get the resulting status code (and svn_error_t) */
return svn_ra_dav__request_dispatch(code, req, cc->ras->sess,
"CHECKOUT", vsn_url,
201 /* Created */,
allow_404 ? 404 /* Not Found */ : 0,
pool);
}
static svn_error_t * checkout_resource(commit_ctx_t *cc,
resource_t *rsrc,
svn_boolean_t allow_404,
apr_pool_t *pool)
{
int code;
char *locn = NULL;
ne_uri parse;
svn_error_t *err;
if (rsrc->wr_url != NULL)
{
/* already checked out! */
return NULL;
}
/* check out the Version Resource */
err = do_checkout(cc, rsrc->vsn_url, allow_404, &code, &locn, pool);
/* possibly run the request again, with a re-fetched Version Resource */
if (err == NULL && allow_404 && code == 404)
{
/* free the LOCN if it got assigned. */
if (locn)
free(locn);
/* re-fetch, forcing a query to the server */
SVN_ERR( get_version_url(cc, rsrc, TRUE, pool) );
/* do it again, but don't allow a 404 this time */
err = do_checkout(cc, rsrc->vsn_url, FALSE, &code, &locn, pool);
}
/* special-case when conflicts occur */
if (err)
{
/* free the LOCN if it got assigned. */
if (locn)
free(locn);
if (err->apr_err == SVN_ERR_FS_CONFLICT)
return svn_error_createf
(err->apr_err, err,
"Your file or directory '%s' is probably out-of-date",
rsrc->local_path);
return err;
}
/* we got the header, right? */
if (locn == NULL)
return svn_error_create(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
"The CHECKOUT response did not contain a "
"'Location:' header");
/* The location is an absolute URI. We want just the path portion. */
/* ### what to do with the rest? what if it points somewhere other
### than the current session? */
ne_uri_parse(locn, &parse);
rsrc->wr_url = apr_pstrdup(rsrc->pool, parse.path);
ne_uri_free(&parse);
free(locn);
return SVN_NO_ERROR;
}
static void record_prop_change(apr_pool_t *pool,
resource_baton_t *r,
const char *name,
const svn_string_t *value)
{
/* copy the name into the pool so we get the right lifetime (who knows
what the caller will do with it) */
name = apr_pstrdup(pool, name);
if (value)
{
/* changed/new property */
if (r->prop_changes == NULL)
r->prop_changes = apr_hash_make(pool);
apr_hash_set(r->prop_changes, name, APR_HASH_KEY_STRING,
svn_string_dup(value, pool));
}
else
{
/* deleted property. */
if (r->prop_deletes == NULL)
r->prop_deletes = apr_array_make(pool, 5, sizeof(char *));
*(const char **)apr_array_push(r->prop_deletes) = name;
}
}
/*
A very long note about enforcing directory-up-to-dateness when
proppatching, writ by Ben:
Once upon a time, I thought it would be necessary to attach the
X-SVN-Version-Name header to every PROPPATCH request we send. This
would allow mod_dav_svn to verify that a directory is up-to-date.
But it turns out that mod_dav_svn screams and errors if you *ever* try
to CHECKOUT an out-of-date VR. And furthermore, a directory is never
a 'committable' (according to svn_client_commit) unless it has a
propchange. Therefore:
1. when ra_dav's commit editor attempts to CHECKOUT a parent directory
because some child is being added or deleted, it's *unable* to get
the VR cache, and thus just gets the HEAD one instead. So it ends
up always doing a CHECKOUT of the latest version of the directory.
This is actually fine; Subversion's semantics allow us to
add/delete children on out-of-date directories. If, in dav terms,
this means always checking out the latest directory, so be it. Any
namespace conflicts will be detected with the actual PUT or DELETE
of the child.
2. when ra_dav's commit editor receives a directory propchange, it
*is* able to get the VR cache (because the dir is a "committable"),
and thus it does a CHECKOUT of the older directory. And mod_dav_svn
will scream if the VR is out-of-date, which is exactly what we want in
the directory propchange scenario.
The only potential badness here is the case of committing a directory
with a propchange, and an add/rm of its child. This commit should
fail, due to the out-of-date propchange. However, it's *possible*
that it will fail for a different reason: we might attempt the add/rm
first, which means checking out the parent VR, which *would* be
available from the cache, and thus we get an early error. Instead of
seeing an error about 'cannot proppatch out-of-date dir', the user
will see an error about 'cannot checkout out-of-date parent'. Not
really a big deal I guess.
*/
static svn_error_t * do_proppatch(svn_ra_session_t *ras,
const resource_t *rsrc,
resource_baton_t *rb,
apr_pool_t *pool)
{
const char *url = rsrc->wr_url;
return svn_ra_dav__do_proppatch(ras, url, rb->prop_changes,
rb->prop_deletes, pool);
}
static void
add_valid_target (commit_ctx_t *cc,
const char *path,
enum svn_recurse_kind kind)
{
apr_hash_t *hash = cc->valid_targets;
svn_string_t *path_str = svn_string_create(path, apr_hash_pool_get(hash));
apr_hash_set (hash, path_str->data, path_str->len, &kind);
}
static svn_error_t * commit_open_root(void *edit_baton,
svn_revnum_t base_revision,
apr_pool_t *dir_pool,
void **root_baton)
{
commit_ctx_t *cc = edit_baton;
resource_baton_t *root;
resource_t *rsrc;
/* create the root resource. no wr_url (yet). */
rsrc = apr_pcalloc(dir_pool, sizeof(*rsrc));
rsrc->pool = dir_pool;
/* ### should this be 'base_revision' here? we might not always be
### working against the head! (think "properties"). */
rsrc->revision = SVN_INVALID_REVNUM;
rsrc->url = cc->ras->root.path;
rsrc->local_path = "";
SVN_ERR( get_version_url(cc, rsrc, FALSE, dir_pool) );
root = apr_pcalloc(dir_pool, sizeof(*root));
root->pool = dir_pool;
root->cc = cc;
root->rsrc = rsrc;
root->created = FALSE;
*root_baton = root;
return SVN_NO_ERROR;
}
static svn_error_t * commit_delete_entry(const char *path,
svn_revnum_t revision,
void *parent_baton,
apr_pool_t *pool)
{
resource_baton_t *parent = parent_baton;
const char *name = svn_path_basename(path, pool);
apr_hash_t *extra_headers = NULL;
const char *child;
int code;
if (SVN_IS_VALID_REVNUM(revision))
{
const char *revstr = apr_psprintf(pool, "%ld", revision);
extra_headers = apr_hash_make(pool);
apr_hash_set(extra_headers, SVN_DAV_VERSION_NAME_HEADER,
APR_HASH_KEY_STRING, revstr);
}
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?