📄 commit.c
字号:
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;}#ifdef SVN_NEON_0_25/* This implements the svn_ra_dav__request_interrogator() interface. USERDATA is 'char **'. */static svn_error_t *interrogate_for_location(ne_request *request, int dispatch_return_val, void *userdata){ char **location = userdata; if (location) { const char *val = ne_get_response_header(request, "location"); if (val) *location = strdup(val); } return SVN_NO_ERROR;}#endif /* SVN_NEON_0_25 */static svn_error_t * do_checkout(commit_ctx_t *cc, const char *vsn_url, svn_boolean_t allow_404, const char *token, int *code, char **locn, apr_pool_t *pool){ ne_request *req; const char *body; svn_error_t *err; /* 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));#ifndef SVN_NEON_0_25 /* * 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);#endif /* ! SVN_NEON_0_25 */ if (token) { const char *token_header_val; token_header_val = apr_psprintf(pool, "(<%s>)", token); ne_add_request_header(req, "If", token_header_val); } /* run the request and get the resulting status code (and svn_error_t) */ err = svn_ra_dav__request_dispatch(code, req, cc->ras->sess, "CHECKOUT", vsn_url, 201 /* Created */, allow_404 ? 404 /* Not Found */ : 0,#ifdef SVN_NEON_0_25 interrogate_for_location, locn,#endif /* SVN_NEON_0_25 */ pool); return err;}static svn_error_t * checkout_resource(commit_ctx_t *cc, version_rsrc_t *rsrc, svn_boolean_t allow_404, const char *token, 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, token, &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, token, &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"), svn_path_local_style(rsrc->local_path, pool)); 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 whenproppatching, writ by Ben: Once upon a time, I thought it would be necessary to attach theX-SVN-Version-Name header to every PROPPATCH request we send. Thiswould 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* tryto CHECKOUT an out-of-date VR. And furthermore, a directory is nevera 'committable' (according to svn_client_commit) unless it has apropchange. 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 directorywith a propchange, and an add/rm of its child. This commit shouldfail, due to the out-of-date propchange. However, it's *possible*that it will fail for a different reason: we might attempt the add/rmfirst, which means checking out the parent VR, which *would* beavailable from the cache, and thus we get an early error. Instead ofseeing an error about 'cannot proppatch out-of-date dir', the userwill see an error about 'cannot checkout out-of-date parent'. Notreally a big deal I guess.*/static svn_error_t * do_proppatch(svn_ra_dav__session_t *ras, const version_rsrc_t *rsrc, resource_baton_t *rb, apr_pool_t *pool){ const char *url = rsrc->wr_url; apr_hash_t *extra_headers = NULL; if (rb->token) { const char *token_header_val; token_header_val = apr_psprintf(pool, "(<%s>)", rb->token); extra_headers = apr_hash_make(pool); apr_hash_set(extra_headers, "If", APR_HASH_KEY_STRING, token_header_val); } return svn_ra_dav__do_proppatch(ras, url, rb->prop_changes, rb->prop_deletes, extra_headers, pool);}static voidadd_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, (void*)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; version_rsrc_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;} /* Helper func for commit_delete_entry. Find all keys in LOCK_TOKENS which are children of DIR. Returns the keys (and their vals) in CHILD_TOKENS. No keys or values are reallocated or dup'd. If no keys are children, then return an empty hash. Use POOL to allocate new hash. */static apr_hash_t *get_child_tokens(apr_hash_t *lock_tokens, const char *dir, apr_pool_t *pool){ apr_hash_index_t *hi; apr_hash_t *tokens = apr_hash_make(pool); apr_pool_t *subpool = svn_pool_create(pool); for (hi = apr_hash_first(pool, lock_tokens); hi; hi = apr_hash_next(hi)) { const void *key; apr_ssize_t klen; void *val; svn_pool_clear(subpool); apr_hash_this(hi, &key, &klen, &val); if (svn_path_is_child(dir, key, subpool)) apr_hash_set(tokens, key, klen, val); } svn_pool_destroy(subpool); return tokens;}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; svn_error_t *serr; if (SVN_IS_VALID_REVNUM(revision)) { const char *revstr = apr_psprintf(pool, "%ld", revision); if (! extra_headers) extra_headers = apr_hash_make(pool); apr_hash_set(extra_headers, SVN_DAV_VERSION_NAME_HEADER, APR_HASH_KEY_STRING, revstr); } /* get the URL to the working collection */ SVN_ERR(checkout_resource(parent->cc, parent->rsrc, TRUE, NULL, pool)); /* create the URL for the child resource */ child = svn_path_url_add_component(parent->rsrc->wr_url, name, pool); /* Start out assuming that we're deleting a file; try to lookup the path itself in the token-hash, and if found, attach it to the If: header. */ if (parent->cc->tokens) { const char *token = apr_hash_get(parent->cc->tokens, path, APR_HASH_KEY_STRING); if (token) { const char *token_header_val; const char *token_uri; token_uri = svn_path_url_add_component(parent->cc->ras->url->data, path, pool); token_header_val = apr_psprintf(pool, "<%s> (<%s>)", token_uri, token); extra_headers = apr_hash_make(pool); apr_hash_set(extra_headers, "If", APR_HASH_KEY_STRING, token_header_val); } } /* dav_method_delete() always calls dav_unlock(), but if the svn client passed --no-unlock to 'svn commit', then we need to send a header which prevents mod_dav_svn from actually doing the unlock. */ if (parent->cc->keep_locks) { if (! extra_headers) extra_headers = apr_hash_make(pool); apr_hash_set(extra_headers, SVN_DAV_OPTIONS_HEADER, APR_HASH_KEY_STRING, SVN_DAV_OPTION_KEEP_LOCKS); } /* 404 is ignored, because mod_dav_svn is effectively merging against the HEAD revision on-the-fly. In such a universe, a failed deletion (because it's already missing) is OK; deletion is an idempotent merge operation. */ serr = simple_request(parent->cc->ras, "DELETE", child, &code,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -