2
* merge.c: handle the MERGE response processing
4
* ====================================================================
5
* Copyright (c) 2000-2004 CollabNet. All rights reserved.
7
* This software is licensed as described in the file COPYING, which
8
* you should have received as part of this distribution. The terms
9
* are also available at http://subversion.tigris.org/license-1.html.
10
* If newer versions of this license are posted there, you may use a
11
* newer version instead, at your option.
13
* This software consists of voluntary contributions made by many
14
* individuals. For exact contribution history, see the revision
15
* history and logs, available at http://subversion.tigris.org/.
16
* ====================================================================
19
#include <apr_pools.h>
20
#include <apr_buckets.h>
25
#include <util_filter.h>
27
#include "svn_pools.h"
29
#include "svn_props.h"
34
/* #################################################################
36
These functions are currently *VERY* SVN specific.
38
* we don't check prop_elem for what the client requested
39
* we presume a baseline was checked out into the activity, and is
41
* we presume that all "changed" files/dirs were checked out into
42
the activity and are part of the MERGE
43
(not sure if this is SVN specific; I can't see how a file/dir
44
would be part of the new revision if a working resource had
45
not been created for it)
46
* we return some props for some resources, and a different set for
47
other resources (to keep the wire smaller for now)
49
At some point in the future, we'll want to make this "real". Especially
50
for proper interoperability.
52
#################################################################
57
/* -------------------------------------------------------------------------
58
PRIVATE HELPER FUNCTIONS
61
/* send a response to the client for this baton */
62
static svn_error_t *send_response(const dav_svn_repos *repos,
67
apr_bucket_brigade *bb,
73
svn_revnum_t rev_to_use;
75
href = dav_svn_build_uri(repos, DAV_SVN_BUILD_URI_PUBLIC,
76
SVN_IGNORED_REVNUM, path, 0 /* add_href */, pool);
77
rev_to_use = dav_svn_get_safe_cr(root, path, pool);
78
vsn_url = dav_svn_build_uri(repos, DAV_SVN_BUILD_URI_VERSION,
79
rev_to_use, path, 0 /* add_href */, pool);
80
status = ap_fputstrs(output, bb,
81
"<D:response>" DEBUG_CR
83
apr_xml_quote_string(pool, href, 1),
85
"<D:propstat><D:prop>" DEBUG_CR,
87
? "<D:resourcetype><D:collection/></D:resourcetype>"
88
: "<D:resourcetype/>",
90
"<D:checked-in><D:href>",
91
apr_xml_quote_string(pool, vsn_url, 1),
92
"</D:href></D:checked-in>" DEBUG_CR
94
"<D:status>HTTP/1.1 200 OK</D:status>" DEBUG_CR
95
"</D:propstat>" DEBUG_CR
96
"</D:response>" DEBUG_CR,
98
if (status != APR_SUCCESS)
99
return svn_error_wrap_apr(status, "Can't write response to output");
105
static svn_error_t *do_resources(const dav_svn_repos *repos,
107
svn_revnum_t revision,
109
apr_bucket_brigade *bb,
113
apr_hash_t *sent = apr_hash_make(pool);
114
apr_hash_index_t *hi;
115
apr_pool_t *subpool = svn_pool_create(pool);
117
/* Fetch the paths changed in this revision. This will contain
118
everything except otherwise-unchanged parent directories of added
119
and deleted things. Also, note that deleted things don't merit
120
responses of their own -- they are considered modifications to
122
SVN_ERR( svn_fs_paths_changed(&changes, root, pool) );
124
for (hi = apr_hash_first(pool, changes); hi; hi = apr_hash_next(hi))
129
svn_fs_path_change_t *change;
130
svn_boolean_t send_self;
131
svn_boolean_t send_parent;
133
svn_pool_clear(subpool);
134
apr_hash_this(hi, &key, NULL, &val);
138
/* Figure out who needs to get sent. */
139
switch (change->change_kind)
141
case svn_fs_path_change_delete:
146
case svn_fs_path_change_add:
147
case svn_fs_path_change_replace:
152
case svn_fs_path_change_modify:
161
/* If we haven't already sent this path, send it (and then
162
remember that we sent it). */
163
if (! apr_hash_get(sent, path, APR_HASH_KEY_STRING))
165
svn_node_kind_t kind;
166
SVN_ERR( svn_fs_check_path(&kind, root, path, subpool) );
167
SVN_ERR( send_response(repos, root, path,
168
kind == svn_node_dir ? TRUE : FALSE,
169
output, bb, subpool) );
170
apr_hash_set(sent, path, APR_HASH_KEY_STRING, (void *)1);
175
/* If it hasn't already been sent, send the parent directory
176
(and then remember that you sent it). Allocate parent in
177
pool, not subpool, because it stays in the sent hash
179
const char *parent = svn_path_dirname(path, pool);
180
if (! apr_hash_get(sent, parent, APR_HASH_KEY_STRING))
182
SVN_ERR( send_response(repos, root, parent,
183
TRUE, output, bb, subpool) );
184
apr_hash_set(sent, parent, APR_HASH_KEY_STRING, (void *)1);
189
svn_pool_destroy(subpool);
196
/* -------------------------------------------------------------------------
200
dav_error * dav_svn__merge_response(ap_filter_t *output,
201
const dav_svn_repos *repos,
202
svn_revnum_t new_rev,
203
apr_xml_elem *prop_elem,
204
svn_boolean_t disable_merge_response,
207
apr_bucket_brigade *bb;
212
svn_string_t *creationdate, *creator_displayname;
214
serr = svn_fs_revision_root(&root, repos->fs, new_rev, pool);
217
return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
218
"Could not open the FS root for the "
219
"revision just committed.",
223
bb = apr_brigade_create(pool, output->c->bucket_alloc);
225
/* prep some strings */
227
/* the HREF for the baseline is actually the VCC */
228
vcc = dav_svn_build_uri(repos, DAV_SVN_BUILD_URI_VCC, SVN_IGNORED_REVNUM,
229
NULL, 0 /* add_href */, pool);
231
/* the version-name of the baseline is the revision number */
232
rev = apr_psprintf(pool, "%ld", new_rev);
234
/* get the creationdate and creator-displayname of the new revision, too. */
235
serr = svn_fs_revision_prop(&creationdate, repos->fs, new_rev,
236
SVN_PROP_REVISION_DATE, pool);
239
return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
240
"Could not get date of newest revision",
243
serr = svn_fs_revision_prop(&creator_displayname, repos->fs, new_rev,
244
SVN_PROP_REVISION_AUTHOR, pool);
247
return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
248
"Could not get author of newest revision",
253
(void) ap_fputstrs(output, bb,
254
DAV_XML_HEADER DEBUG_CR
255
"<D:merge-response xmlns:D=\"DAV:\">" DEBUG_CR
256
"<D:updated-set>" DEBUG_CR
258
/* generate a response for the new baseline */
259
"<D:response>" DEBUG_CR
261
apr_xml_quote_string(pool, vcc, 1),
263
"<D:propstat><D:prop>" DEBUG_CR
264
/* ### this is wrong. it's a VCC, not a baseline. but
265
### we need to tell the client to look at *this*
266
### resource for the version-name. */
267
"<D:resourcetype><D:baseline/></D:resourcetype>" DEBUG_CR
268
"<D:version-name>", rev, "</D:version-name>" DEBUG_CR,
272
(void) ap_fputstrs(output, bb,
274
apr_xml_quote_string(pool, creationdate->data, 1),
275
"</D:creationdate>" DEBUG_CR,
278
if (creator_displayname)
280
(void) ap_fputstrs(output, bb,
281
"<D:creator-displayname>",
282
apr_xml_quote_string(pool,
283
creator_displayname->data, 1),
284
"</D:creator-displayname>" DEBUG_CR,
287
(void) ap_fputstrs(output, bb,
289
"<D:status>HTTP/1.1 200 OK</D:status>" DEBUG_CR
290
"</D:propstat>" DEBUG_CR
291
"</D:response>" DEBUG_CR,
295
/* ONLY have dir_delta drive the editor if the caller asked us to
296
generate a full MERGE response. svn clients can ask us to
297
suppress this walk by sending specific request headers. */
298
if (! disable_merge_response)
300
/* Now we need to generate responses for all the resources which
301
changed. This is done through a delta of the two roots.
303
Note that a directory is not marked when open_dir is seen
304
(since it typically is used just for changing members in that
305
directory); instead, we want for a property change (the only
306
reason the client would need to fetch a new directory).
308
### we probably should say something about the dirs, so that
309
### we can pass back the new version URL */
311
/* and go make me proud, boy! */
312
serr = do_resources(repos, root, new_rev, output, bb, pool);
315
return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
316
"Error constructing resource list.",
321
/* wrap up the merge response */
322
(void) ap_fputs(output, bb,
323
"</D:updated-set>" DEBUG_CR
324
"</D:merge-response>" DEBUG_CR);
326
/* send whatever is left in the brigade */
327
(void) ap_pass_brigade(output, bb);