2 * merge.c : routines for performing a MERGE server requests
4 * ====================================================================
5 * Copyright (c) 2000-2007 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 * ====================================================================
21 #include <apr_pools.h>
23 #define APR_WANT_STRFUNC
26 #include "svn_string.h"
27 #include "svn_error.h"
30 #include "svn_pools.h"
31 #include "svn_props.h"
34 #include "private/svn_dav_protocol.h"
35 #include "svn_private_config.h"
40 static const svn_ra_neon__xml_elm_t merge_elements
[] =
42 { "DAV:", "updated-set", ELEM_updated_set
, 0 },
43 { "DAV:", "merged-set", ELEM_merged_set
, 0 },
44 { "DAV:", "ignored-set", ELEM_ignored_set
, 0 },
45 { "DAV:", "href", ELEM_href
, SVN_RA_NEON__XML_CDATA
},
46 { "DAV:", "merge-response", ELEM_merge_response
, 0 },
47 { "DAV:", "checked-in", ELEM_checked_in
, 0 },
48 { "DAV:", "response", ELEM_response
, 0 },
49 { "DAV:", "propstat", ELEM_propstat
, 0 },
50 { "DAV:", "status", ELEM_status
, SVN_RA_NEON__XML_CDATA
},
51 { "DAV:", "responsedescription", ELEM_responsedescription
,
52 SVN_RA_NEON__XML_CDATA
},
53 { "DAV:", "prop", ELEM_prop
, 0 },
54 { "DAV:", "resourcetype", ELEM_resourcetype
, 0 },
55 { "DAV:", "collection", ELEM_collection
, 0 },
56 { "DAV:", "baseline", ELEM_baseline
, 0 },
57 { "DAV:", SVN_DAV__VERSION_NAME
, ELEM_version_name
, SVN_RA_NEON__XML_CDATA
},
58 { SVN_XML_NAMESPACE
, "post-commit-err",
59 ELEM_post_commit_err
, SVN_RA_NEON__XML_CDATA
},
60 { "DAV:", SVN_DAV__CREATIONDATE
, ELEM_creationdate
, SVN_RA_NEON__XML_CDATA
},
61 { "DAV:", "creator-displayname", ELEM_creator_displayname
,
62 SVN_RA_NEON__XML_CDATA
},
68 RTYPE_UNKNOWN
, /* unknown (haven't seen it in the response yet) */
69 RTYPE_REGULAR
, /* a regular (member) resource */
70 RTYPE_COLLECTION
, /* a collection resource */
71 RTYPE_BASELINE
/* a baseline resource */
75 /*WARNING: WANT_CDATA should stay the first element in the baton:
76 svn_ra_neon__xml_collect_cdata() assumes the baton starts with a stringbuf.
78 svn_stringbuf_t
*want_cdata
;
79 svn_stringbuf_t
*cdata
;
82 /* a clearable subpool of pool, for loops. Do not use for anything
83 that must persist beyond the scope of your function! */
84 apr_pool_t
*scratchpool
;
86 /* the BASE_HREF contains the merge target. as resources are specified in
87 the merge response, we make their URLs relative to this URL, thus giving
88 us a path for use in the commit callbacks. */
89 const char *base_href
;
91 svn_revnum_t rev
; /* the new/target revision number for this commit */
93 svn_boolean_t response_has_error
;
94 int response_parent
; /* what element did DAV:response appear within? */
96 int href_parent
; /* what element is the DAV:href appearing within? */
97 svn_stringbuf_t
*href
; /* current response */
99 int status
; /* HTTP status for this DAV:propstat */
100 enum merge_rtype rtype
; /* DAV:resourcetype of this resource */
102 svn_stringbuf_t
*vsn_name
; /* DAV:version-name for this resource */
103 svn_stringbuf_t
*vsn_url
; /* DAV:checked-in for this resource */
104 svn_stringbuf_t
*committed_date
; /* DAV:creationdate for this resource */
105 svn_stringbuf_t
*last_author
; /* DAV:creator-displayname for this
107 svn_stringbuf_t
*post_commit_err
;/* SVN_XML_NAMESPACE:post-commit hook's
110 /* We only invoke set_prop() on targets listed in valid_targets.
111 Some entities (such as directories that have had changes
112 committed underneath but are not themselves targets) will be
113 mentioned in the merge response but not appear in
115 apr_hash_t
*valid_targets
;
117 /* Client callbacks */
118 svn_ra_push_wc_prop_func_t push_prop
;
119 void *cb_baton
; /* baton for above */
124 static void add_ignored(merge_ctx_t
*mc
, const char *cdata
)
126 /* ### the server didn't check in the file(!) */
127 /* ### remember the file and issue a report/warning later */
131 static svn_boolean_t
okay_to_bump_path(const char *path
,
132 apr_hash_t
*valid_targets
,
135 svn_stringbuf_t
*parent_path
;
136 enum svn_recurse_kind r
;
138 /* Easy check: if path itself is in the hash, then it's legit. */
139 if (apr_hash_get(valid_targets
, path
, APR_HASH_KEY_STRING
))
141 /* Otherwise, this path is bumpable IFF one of its parents is in the
142 hash and marked with a 'recursion' flag. */
143 parent_path
= svn_stringbuf_create(path
, pool
);
146 apr_size_t len
= parent_path
->len
;
147 svn_path_remove_component(parent_path
);
148 if (len
== parent_path
->len
)
150 r
= (enum svn_recurse_kind
) apr_hash_get(valid_targets
,
152 APR_HASH_KEY_STRING
);
153 if (r
== svn_recursive
)
156 } while (! svn_path_is_empty(parent_path
->data
));
158 /* Default answer: if we get here, don't allow the bumping. */
163 /* If committed PATH appears in MC->valid_targets, and an MC->push_prop
164 * function exists, then store VSN_URL as the SVN_RA_NEON__LP_VSN_URL
165 * property on PATH. Use POOL for all allocations.
167 * Otherwise, just return SVN_NO_ERROR.
169 static svn_error_t
*bump_resource(merge_ctx_t
*mc
,
174 /* no sense in doing any more work if there's no property setting
175 function at our disposal. */
176 if (mc
->push_prop
== NULL
)
179 /* Only invoke a client callback on PATH if PATH counts as a
180 committed target. The commit-tracking editor built this list for
181 us, and took care not to include directories unless they were
182 directly committed (i.e., received a property change). */
183 if (! okay_to_bump_path(path
, mc
->valid_targets
, pool
))
186 /* Okay, NOW set the new version url. */
188 svn_string_t vsn_url_str
; /* prop setter wants an svn_string_t */
190 vsn_url_str
.data
= vsn_url
;
191 vsn_url_str
.len
= strlen(vsn_url
);
193 SVN_ERR((*mc
->push_prop
)(mc
->cb_baton
, path
,
194 SVN_RA_NEON__LP_VSN_URL
, &vsn_url_str
,
201 static svn_error_t
* handle_resource(merge_ctx_t
*mc
,
204 const char *relative
;
206 if (mc
->response_has_error
)
208 /* ### what to do? */
209 /* ### return "no error", presuming whatever set response_has_error
210 ### has already handled the problem. */
213 if (mc
->response_parent
== ELEM_merged_set
)
215 /* ### shouldn't have happened. we told the server "don't merge" */
216 /* ### need something better than APR_EGENERAL */
217 return svn_error_createf(APR_EGENERAL
, NULL
,
218 _("Protocol error: we told the server not to "
219 "auto-merge any resources, but it said that "
220 "'%s' was merged"), mc
->href
->data
);
222 if (mc
->response_parent
!= ELEM_updated_set
)
224 /* ### unknown parent for this response(!) */
225 /* ### need something better than APR_EGENERAL */
226 return svn_error_createf(APR_EGENERAL
, NULL
,
227 _("Internal error: there is an unknown parent "
228 "(%d) for the 'DAV:response' element within"
229 " the MERGE response"), mc
->response_parent
);
232 /* ### right now, the server isn't sending everything for all resources.
233 ### just skip this requirement. */
234 if (mc
->href
->len
== 0
235 || mc
->vsn_name
->len
== 0
236 || mc
->vsn_url
->len
== 0
237 || mc
->rtype
== RTYPE_UNKNOWN
)
239 /* one or more properties were missing in the DAV:response for the
241 return svn_error_createf(APR_EGENERAL
, NULL
,
242 _("Protocol error: the MERGE response for the "
243 "'%s' resource did not return all of the "
244 "properties that we asked for (and need to "
245 "complete the commit)"), mc
->href
->data
);
249 if (mc
->rtype
== RTYPE_BASELINE
)
251 /* cool. the DAV:version-name tells us the new revision */
252 mc
->rev
= SVN_STR_TO_REV(mc
->vsn_name
->data
);
256 /* a collection or regular resource */
257 if (! svn_path_is_ancestor(mc
->base_href
, mc
->href
->data
))
259 /* ### need something better than APR_EGENERAL */
260 return svn_error_createf(APR_EGENERAL
, NULL
,
261 _("A MERGE response for '%s' is not a child "
262 "of the destination ('%s')"),
263 mc
->href
->data
, mc
->base_href
);
266 /* given HREF of the form: BASE "/" RELATIVE, extract the relative portion */
267 relative
= svn_path_is_child(mc
->base_href
, mc
->href
->data
, NULL
);
268 if (! relative
) /* the paths are equal */
271 /* bump the resource */
272 relative
= svn_path_uri_decode(relative
, pool
);
273 return bump_resource(mc
, relative
, mc
->vsn_url
->data
, pool
);
276 /* Determine whether we're receiving the expected XML response.
277 Return CHILD when interested in receiving the child's contents
278 or one of SVN_RA_NEON__XML_INVALID and SVN_RA_NEON__XML_DECLINE
279 when respectively this is the incorrect response or
280 the element (and its children) are uninteresting */
281 static int validate_element(svn_ra_neon__xml_elmid parent
,
282 svn_ra_neon__xml_elmid child
)
284 if ((child
== ELEM_collection
|| child
== ELEM_baseline
)
285 && parent
!= ELEM_resourcetype
) {
286 /* ### technically, they could occur elsewhere, but screw it */
287 return SVN_RA_NEON__XML_INVALID
;
293 if (child
== ELEM_merge_response
)
296 return SVN_RA_NEON__XML_INVALID
;
298 case ELEM_merge_response
:
299 if (child
== ELEM_updated_set
300 || child
== ELEM_merged_set
301 || child
== ELEM_ignored_set
)
304 return SVN_RA_NEON__XML_DECLINE
; /* any child is allowed */
306 case ELEM_updated_set
:
307 case ELEM_merged_set
:
308 if (child
== ELEM_response
)
311 return SVN_RA_NEON__XML_DECLINE
; /* ignore if something else
314 case ELEM_ignored_set
:
315 if (child
== ELEM_href
)
318 return SVN_RA_NEON__XML_DECLINE
; /* ignore if something else
322 if (child
== ELEM_href
323 || child
== ELEM_status
324 || child
== ELEM_propstat
)
326 else if (child
== ELEM_responsedescription
)
327 /* ### I think we want this... to save a message for the user */
328 return SVN_RA_NEON__XML_DECLINE
; /* valid, but we don't need to see it */
330 return SVN_RA_NEON__XML_DECLINE
; /* ignore if something else
334 if (child
== ELEM_prop
|| child
== ELEM_status
)
336 else if (child
== ELEM_responsedescription
)
337 /* ### I think we want this... to save a message for the user */
338 return SVN_RA_NEON__XML_DECLINE
; /* valid, but we don't need to see it */
340 return SVN_RA_NEON__XML_DECLINE
; /* ignore if something else
344 if (child
== ELEM_checked_in
345 || child
== ELEM_resourcetype
346 || child
== ELEM_version_name
347 || child
== ELEM_creationdate
348 || child
== ELEM_creator_displayname
349 || child
== ELEM_post_commit_err
353 return SVN_RA_NEON__XML_DECLINE
; /* ignore other props */
355 case ELEM_checked_in
:
356 if (child
== ELEM_href
)
359 return SVN_RA_NEON__XML_DECLINE
; /* ignore if something else
362 case ELEM_resourcetype
:
363 if (child
== ELEM_collection
|| child
== ELEM_baseline
)
366 return SVN_RA_NEON__XML_DECLINE
; /* ignore if something else
370 return SVN_RA_NEON__XML_DECLINE
;
377 start_element(int *elem
, void *baton
, int parent
,
378 const char *nspace
, const char *name
, const char **atts
)
380 const svn_ra_neon__xml_elm_t
*elm
381 = svn_ra_neon__lookup_xml_elem(merge_elements
, nspace
, name
);
382 merge_ctx_t
*mc
= baton
;
384 *elem
= elm
? validate_element(parent
, elm
->id
) : SVN_RA_NEON__XML_DECLINE
;
385 if (*elem
< 1) /* not a valid element */
391 mc
->response_has_error
= FALSE
;
393 /* for each response (which corresponds to one resource), note that we
394 haven't seen its resource type yet */
395 mc
->rtype
= RTYPE_UNKNOWN
;
397 /* and we haven't seen these elements yet */
399 mc
->vsn_name
->len
= 0;
400 mc
->vsn_url
->len
= 0;
404 case ELEM_ignored_set
:
405 case ELEM_checked_in
:
406 /* if we see an href "soon", then its parent is ELM */
407 mc
->href_parent
= elm
->id
;
410 case ELEM_updated_set
:
411 case ELEM_merged_set
:
412 mc
->response_parent
= elm
->id
;
416 /* initialize the status so we can figure out if we ever saw a
417 status element in the propstat */
421 case ELEM_resourcetype
:
422 /* we've seen a DAV:resourcetype, so it will be "regular" unless we
423 see something within this element */
424 mc
->rtype
= RTYPE_REGULAR
;
427 case ELEM_collection
:
428 mc
->rtype
= RTYPE_COLLECTION
;
432 mc
->rtype
= RTYPE_BASELINE
;
436 /* one of: ELEM_href, ELEM_status, ELEM_prop,
445 case ELEM_version_name
:
446 case ELEM_post_commit_err
:
447 case ELEM_creationdate
:
448 case ELEM_creator_displayname
:
449 mc
->want_cdata
= mc
->cdata
;
450 svn_stringbuf_setempty(mc
->cdata
);
454 mc
->want_cdata
= NULL
;
463 end_element(void *baton
, int state
,
464 const char *nspace
, const char *name
)
466 merge_ctx_t
*mc
= baton
;
471 switch (mc
->href_parent
)
473 case ELEM_ignored_set
:
474 add_ignored(mc
, mc
->cdata
->data
);
478 /* we're now working on this href... */
479 SVN_ERR(svn_ra_neon__copy_href(mc
->href
, mc
->cdata
->data
,
483 case ELEM_checked_in
:
484 SVN_ERR(svn_ra_neon__copy_href(mc
->vsn_url
, mc
->cdata
->data
,
490 case ELEM_responsedescription
:
491 /* ### I don't think we'll see this right now, due to validate_element */
492 /* ### remember this for error messages? */
499 if (ne_parse_statusline(mc
->cdata
->data
, &hs
) != 0)
500 mc
->response_has_error
= TRUE
;
503 mc
->status
= hs
.code
;
506 /* ### create an error structure? */
507 mc
->response_has_error
= TRUE
;
509 free(hs
.reason_phrase
);
511 if (mc
->response_has_error
)
513 /* ### fix this error value */
514 return svn_error_create(APR_EGENERAL
, NULL
,
515 _("The MERGE property response had an "
522 /* ### does Neon have a symbol for 200? */
523 if (mc
->status
== 200 /* OK */)
525 /* ### what to do? reset all the data? */
527 /* ### else issue an error? status==0 means we never saw one */
532 /* the end of a DAV:response means that we've seen all the information
533 related to this resource. process it. */
534 SVN_ERR(handle_resource(mc
, mc
->scratchpool
));
535 svn_pool_clear(mc
->scratchpool
);
539 case ELEM_checked_in
:
540 /* When we leave a DAV:checked-in element, the parents are DAV:prop,
541 DAV:propstat, then DAV:response. If we see a DAV:href "on the way
542 out", then it is going to belong to the DAV:response. */
543 mc
->href_parent
= ELEM_response
;
546 case ELEM_version_name
:
547 svn_stringbuf_set(mc
->vsn_name
, mc
->cdata
->data
);
550 case ELEM_post_commit_err
:
551 svn_stringbuf_set(mc
->post_commit_err
, mc
->cdata
->data
);
554 case ELEM_creationdate
:
555 svn_stringbuf_set(mc
->committed_date
, mc
->cdata
->data
);
558 case ELEM_creator_displayname
:
559 svn_stringbuf_set(mc
->last_author
, mc
->cdata
->data
);
563 /* one of: ELEM_updated_set, ELEM_merged_set, ELEM_ignored_set,
564 ELEM_prop, ELEM_resourcetype, ELEM_collection, ELEM_baseline */
572 svn_error_t
* svn_ra_neon__assemble_locktoken_body(svn_stringbuf_t
**body
,
573 apr_hash_t
*lock_tokens
,
576 apr_hash_index_t
*hi
;
578 const char *closing_tag
= "</S:lock-token-list>";
579 apr_size_t closing_tag_size
= strlen(closing_tag
);
580 apr_pool_t
*tmppool
= svn_pool_create(pool
);
581 apr_hash_t
*xml_locks
= apr_hash_make(tmppool
);
582 svn_stringbuf_t
*lockbuf
= svn_stringbuf_create
583 ("<S:lock-token-list xmlns:S=\"" SVN_XML_NAMESPACE
"\">" DEBUG_CR
, pool
);
585 buf_size
= lockbuf
->len
;
587 #define SVN_LOCK "<S:lock>" DEBUG_CR
588 #define SVN_LOCK_LEN sizeof(SVN_LOCK)-1
589 #define SVN_LOCK_CLOSE "</S:lock>" DEBUG_CR
590 #define SVN_LOCK_CLOSE_LEN sizeof(SVN_LOCK_CLOSE)-1
591 #define SVN_LOCK_PATH "<S:lock-path>"
592 #define SVN_LOCK_PATH_LEN sizeof(SVN_LOCK_PATH)-1
593 #define SVN_LOCK_PATH_CLOSE "</S:lock-path>" DEBUG_CR
594 #define SVN_LOCK_PATH_CLOSE_LEN sizeof(SVN_LOCK_CLOSE)-1
595 #define SVN_LOCK_TOKEN "<S:lock-token>"
596 #define SVN_LOCK_TOKEN_LEN sizeof(SVN_LOCK_TOKEN)-1
597 #define SVN_LOCK_TOKEN_CLOSE "</S:lock-token>" DEBUG_CR
598 #define SVN_LOCK_TOKEN_CLOSE_LEN sizeof(SVN_LOCK_TOKEN_CLOSE)-1
600 /* First, figure out how much string data we're talking about,
601 and allocate a stringbuf big enough to hold it all... we *never*
602 want have the stringbuf do an auto-reallocation. While here,
603 we'll be copying our hash of paths -> tokens into a hash of
604 xml-escaped-paths -> tokens. */
605 for (hi
= apr_hash_first(tmppool
, lock_tokens
); hi
; hi
= apr_hash_next(hi
))
610 svn_string_t lock_path
;
611 svn_stringbuf_t
*lock_path_xml
= NULL
;
613 apr_hash_this(hi
, &key
, &klen
, &val
);
615 /* XML-escape our key and store it in our temporary hash. */
616 lock_path
.data
= key
;
617 lock_path
.len
= klen
;
618 svn_xml_escape_cdata_string(&lock_path_xml
, &lock_path
, tmppool
);
619 apr_hash_set(xml_locks
, lock_path_xml
->data
, lock_path_xml
->len
, val
);
621 /* Now, on with the stringbuf calculations. */
622 buf_size
+= SVN_LOCK_LEN
;
623 buf_size
+= SVN_LOCK_PATH_LEN
;
624 buf_size
+= lock_path_xml
->len
;
625 buf_size
+= SVN_LOCK_PATH_CLOSE_LEN
;
626 buf_size
+= SVN_LOCK_TOKEN_LEN
;
627 buf_size
+= strlen(val
);
628 buf_size
+= SVN_LOCK_TOKEN_CLOSE_LEN
;
629 buf_size
+= SVN_LOCK_CLOSE_LEN
;
632 buf_size
+= closing_tag_size
;
634 svn_stringbuf_ensure(lockbuf
, buf_size
+ 1);
636 /* Now append all the temporary hash's keys and values into the
637 stringbuf. This is better than doing apr_pstrcat() in a loop,
638 because (1) there's no need to constantly re-alloc, and (2) the
639 stringbuf already knows the end of the buffer, so there's no
640 seek-time to the end of the string when appending. */
641 for (hi
= apr_hash_first(tmppool
, xml_locks
); hi
; hi
= apr_hash_next(hi
))
647 apr_hash_this(hi
, &key
, &klen
, &val
);
649 svn_stringbuf_appendcstr(lockbuf
, SVN_LOCK
);
650 svn_stringbuf_appendcstr(lockbuf
, SVN_LOCK_PATH
);
651 svn_stringbuf_appendbytes(lockbuf
, key
, klen
);
652 svn_stringbuf_appendcstr(lockbuf
, SVN_LOCK_PATH_CLOSE
);
653 svn_stringbuf_appendcstr(lockbuf
, SVN_LOCK_TOKEN
);
654 svn_stringbuf_appendcstr(lockbuf
, val
);
655 svn_stringbuf_appendcstr(lockbuf
, SVN_LOCK_TOKEN_CLOSE
);
656 svn_stringbuf_appendcstr(lockbuf
, SVN_LOCK_CLOSE
);
659 svn_stringbuf_appendcstr(lockbuf
, closing_tag
);
663 #undef SVN_LOCK_CLOSE
664 #undef SVN_LOCK_CLOSE_LEN
666 #undef SVN_LOCK_PATH_LEN
667 #undef SVN_LOCK_PATH_CLOSE
668 #undef SVN_LOCK_PATH_CLOSE_LEN
669 #undef SVN_LOCK_TOKEN
670 #undef SVN_LOCK_TOKEN_LEN
671 #undef SVN_LOCK_TOKEN_CLOSE
672 #undef SVN_LOCK_TOKEN_CLOSE_LEN
676 svn_pool_destroy(tmppool
);
682 svn_error_t
* svn_ra_neon__merge_activity(svn_revnum_t
*new_rev
,
683 const char **committed_date
,
684 const char **committed_author
,
685 const char **post_commit_err
,
686 svn_ra_neon__session_t
*ras
,
687 const char *repos_url
,
688 const char *activity_url
,
689 apr_hash_t
*valid_targets
,
690 apr_hash_t
*lock_tokens
,
691 svn_boolean_t keep_locks
,
692 svn_boolean_t disable_merge_response
,
695 merge_ctx_t mc
= { 0 };
697 apr_hash_t
*extra_headers
= NULL
;
698 svn_stringbuf_t
*lockbuf
= svn_stringbuf_create("", pool
);
700 mc
.cdata
= svn_stringbuf_create("", pool
);
702 mc
.scratchpool
= svn_pool_create(pool
);
703 mc
.base_href
= repos_url
;
704 mc
.rev
= SVN_INVALID_REVNUM
;
706 mc
.valid_targets
= valid_targets
;
707 mc
.push_prop
= ras
->callbacks
->push_wc_prop
;
708 mc
.cb_baton
= ras
->callback_baton
;
710 mc
.href
= MAKE_BUFFER(pool
);
711 mc
.vsn_name
= MAKE_BUFFER(pool
);
712 mc
.vsn_url
= MAKE_BUFFER(pool
);
713 mc
.committed_date
= MAKE_BUFFER(pool
);
714 mc
.last_author
= MAKE_BUFFER(pool
);
716 mc
.post_commit_err
= MAKE_BUFFER(pool
);
718 if (disable_merge_response
723 value
= apr_psprintf(pool
, "%s %s",
724 disable_merge_response
?
725 SVN_DAV_OPTION_NO_MERGE_RESPONSE
: "",
727 "" : SVN_DAV_OPTION_RELEASE_LOCKS
);
730 extra_headers
= apr_hash_make(pool
);
731 apr_hash_set(extra_headers
, SVN_DAV_OPTIONS_HEADER
, APR_HASH_KEY_STRING
,
735 /* Need to marshal the whole [path->token] hash to the server as
736 a string within the body of the MERGE request. */
737 if ((lock_tokens
!= NULL
)
738 && (apr_hash_count(lock_tokens
) > 0))
739 SVN_ERR(svn_ra_neon__assemble_locktoken_body(&lockbuf
, lock_tokens
, pool
));
741 body
= apr_psprintf(pool
,
742 "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
743 "<D:merge xmlns:D=\"DAV:\">"
744 "<D:source><D:href>%s</D:href></D:source>"
745 "<D:no-auto-merge/><D:no-checkout/>"
746 "<D:prop><D:checked-in/>"
747 "<D:" SVN_DAV__VERSION_NAME
"/><D:resourcetype/>"
748 "<D:" SVN_DAV__CREATIONDATE
"/><D:creator-displayname/>"
752 activity_url
, lockbuf
->data
);
754 SVN_ERR(svn_ra_neon__parsed_request(ras
, "MERGE", repos_url
,
757 svn_ra_neon__xml_collect_cdata
,
758 end_element
, &mc
, extra_headers
,
761 /* return some commit properties to the caller. */
765 *committed_date
= mc
.committed_date
->len
766 ? apr_pstrdup(pool
, mc
.committed_date
->data
) : NULL
;
767 if (committed_author
)
768 *committed_author
= mc
.last_author
->len
769 ? apr_pstrdup(pool
, mc
.last_author
->data
) : NULL
;
771 *post_commit_err
= mc
.post_commit_err
->len
772 ? apr_pstrdup(pool
, mc
.post_commit_err
->data
) : NULL
;
774 svn_pool_destroy(mc
.scratchpool
);