2 * copy_foreign.c: copy from other repository support.
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
21 * ====================================================================
24 /* ==================================================================== */
30 #include "svn_client.h"
31 #include "svn_delta.h"
32 #include "svn_dirent_uri.h"
33 #include "svn_error.h"
34 #include "svn_error_codes.h"
36 #include "svn_pools.h"
37 #include "svn_props.h"
44 #include "private/svn_subr_private.h"
45 #include "private/svn_wc_private.h"
46 #include "svn_private_config.h"
51 const char *anchor_abspath;
53 svn_wc_context_t *wc_ctx;
54 svn_wc_notify_func2_t notify_func;
62 struct dir_baton_t *pb;
63 struct edit_baton_t *eb;
65 const char *local_abspath;
67 svn_boolean_t created;
68 apr_hash_t *properties;
73 /* svn_delta_editor_t function */
75 edit_open(void *edit_baton,
76 svn_revnum_t base_revision,
77 apr_pool_t *result_pool,
80 struct edit_baton_t *eb = edit_baton;
81 apr_pool_t *dir_pool = svn_pool_create(eb->pool);
82 struct dir_baton_t *db = apr_pcalloc(dir_pool, sizeof(*db));
87 db->local_abspath = eb->anchor_abspath;
89 SVN_ERR(svn_io_make_dir_recursively(eb->anchor_abspath, dir_pool));
96 /* svn_delta_editor_t function */
98 edit_close(void *edit_baton,
99 apr_pool_t *scratch_pool)
105 dir_add(const char *path,
107 const char *copyfrom_path,
108 svn_revnum_t copyfrom_revision,
109 apr_pool_t *result_pool,
112 struct dir_baton_t *pb = parent_baton;
113 struct edit_baton_t *eb = pb->eb;
114 apr_pool_t *dir_pool = svn_pool_create(pb->pool);
115 struct dir_baton_t *db = apr_pcalloc(dir_pool, sizeof(*db));
116 svn_boolean_t under_root;
125 SVN_ERR(svn_dirent_is_under_root(&under_root, &db->local_abspath,
126 eb->anchor_abspath, path, db->pool));
129 return svn_error_createf(
130 SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
131 _("Path '%s' is not in the working copy"),
132 svn_dirent_local_style(path, db->pool));
135 SVN_ERR(svn_io_make_dir_recursively(db->local_abspath, db->pool));
142 dir_change_prop(void *dir_baton,
144 const svn_string_t *value,
145 apr_pool_t *scratch_pool)
147 struct dir_baton_t *db = dir_baton;
148 struct edit_baton_t *eb = db->eb;
149 svn_prop_kind_t prop_kind;
151 prop_kind = svn_property_kind2(name);
153 if (prop_kind != svn_prop_regular_kind
154 || ! strcmp(name, SVN_PROP_MERGEINFO))
156 /* We can't handle DAV, ENTRY and merge specific props here */
162 /* We can still store them in the hash for immediate addition
163 with the svn_wc_add_from_disk2() call */
164 if (! db->properties)
165 db->properties = apr_hash_make(db->pool);
168 svn_hash_sets(db->properties, apr_pstrdup(db->pool, name),
169 svn_string_dup(value, db->pool));
173 /* We have already notified for this directory, so don't do that again */
174 SVN_ERR(svn_wc_prop_set4(eb->wc_ctx, db->local_abspath, name, value,
175 svn_depth_empty, FALSE, NULL,
176 NULL, NULL, /* Cancelation */
177 NULL, NULL, /* Notification */
184 /* Releases the directory baton if there are no more users */
186 maybe_done(struct dir_baton_t *db)
192 struct dir_baton_t *pb = db->pb;
194 svn_pool_clear(db->pool);
197 SVN_ERR(maybe_done(pb));
204 ensure_added(struct dir_baton_t *db,
205 apr_pool_t *scratch_pool)
211 SVN_ERR(ensure_added(db->pb, scratch_pool));
215 /* Add the directory with all the already collected properties */
216 SVN_ERR(svn_wc_add_from_disk2(db->eb->wc_ctx,
220 db->eb->notify_baton,
227 dir_close(void *dir_baton,
228 apr_pool_t *scratch_pool)
230 struct dir_baton_t *db = dir_baton;
231 /*struct edit_baton_t *eb = db->eb;*/
233 SVN_ERR(ensure_added(db, scratch_pool));
235 SVN_ERR(maybe_done(db));
244 struct dir_baton_t *pb;
245 struct edit_baton_t *eb;
247 const char *local_abspath;
248 apr_hash_t *properties;
250 svn_boolean_t writing;
251 unsigned char digest[APR_MD5_DIGESTSIZE];
253 const char *tmp_path;
257 file_add(const char *path,
259 const char *copyfrom_path,
260 svn_revnum_t copyfrom_revision,
261 apr_pool_t *result_pool,
264 struct dir_baton_t *pb = parent_baton;
265 struct edit_baton_t *eb = pb->eb;
266 apr_pool_t *file_pool = svn_pool_create(pb->pool);
267 struct file_baton_t *fb = apr_pcalloc(file_pool, sizeof(*fb));
268 svn_boolean_t under_root;
272 fb->pool = file_pool;
276 SVN_ERR(svn_dirent_is_under_root(&under_root, &fb->local_abspath,
277 eb->anchor_abspath, path, fb->pool));
280 return svn_error_createf(
281 SVN_ERR_WC_OBSTRUCTED_UPDATE, NULL,
282 _("Path '%s' is not in the working copy"),
283 svn_dirent_local_style(path, fb->pool));
291 file_change_prop(void *file_baton,
293 const svn_string_t *value,
294 apr_pool_t *scratch_pool)
296 struct file_baton_t *fb = file_baton;
297 svn_prop_kind_t prop_kind;
299 prop_kind = svn_property_kind2(name);
301 if (prop_kind != svn_prop_regular_kind
302 || ! strcmp(name, SVN_PROP_MERGEINFO))
304 /* We can't handle DAV, ENTRY and merge specific props here */
308 /* We store all properties in the hash for immediate addition
309 with the svn_wc_add_from_disk2() call */
310 if (! fb->properties)
311 fb->properties = apr_hash_make(fb->pool);
314 svn_hash_sets(fb->properties, apr_pstrdup(fb->pool, name),
315 svn_string_dup(value, fb->pool));
321 file_textdelta(void *file_baton,
322 const char *base_checksum,
323 apr_pool_t *result_pool,
324 svn_txdelta_window_handler_t *handler,
325 void **handler_baton)
327 struct file_baton_t *fb = file_baton;
328 svn_stream_t *target;
330 SVN_ERR_ASSERT(! fb->writing);
332 SVN_ERR(svn_stream_open_writable(&target, fb->local_abspath, fb->pool,
336 svn_txdelta_apply(svn_stream_empty(fb->pool) /* source */,
341 /* Provide the handler directly */
342 handler, handler_baton);
348 file_close(void *file_baton,
349 const char *text_checksum,
350 apr_pool_t *scratch_pool)
352 struct file_baton_t *fb = file_baton;
353 struct edit_baton_t *eb = fb->eb;
354 struct dir_baton_t *pb = fb->pb;
356 SVN_ERR(ensure_added(pb, fb->pool));
360 svn_checksum_t *expected_checksum;
361 svn_checksum_t *actual_checksum;
363 SVN_ERR(svn_checksum_parse_hex(&expected_checksum, svn_checksum_md5,
364 text_checksum, fb->pool));
365 actual_checksum = svn_checksum__from_digest_md5(fb->digest, fb->pool);
367 if (! svn_checksum_match(expected_checksum, actual_checksum))
368 return svn_error_trace(
369 svn_checksum_mismatch_err(expected_checksum,
372 _("Checksum mismatch for '%s'"),
373 svn_dirent_local_style(
378 SVN_ERR(svn_wc_add_from_disk2(eb->wc_ctx, fb->local_abspath, fb->properties,
379 eb->notify_func, eb->notify_baton,
382 svn_pool_destroy(fb->pool);
383 SVN_ERR(maybe_done(pb));
389 copy_foreign_dir(svn_ra_session_t *ra_session,
390 svn_client__pathrev_t *location,
391 svn_wc_context_t *wc_ctx,
392 const char *dst_abspath,
394 svn_wc_notify_func2_t notify_func,
396 svn_cancel_func_t cancel_func,
398 apr_pool_t *scratch_pool)
400 struct edit_baton_t eb;
401 svn_delta_editor_t *editor = svn_delta_default_editor(scratch_pool);
402 const svn_delta_editor_t *wrapped_editor;
404 const svn_ra_reporter3_t *reporter;
405 void *reporter_baton;
407 eb.pool = scratch_pool;
408 eb.anchor_abspath = dst_abspath;
411 eb.notify_func = notify_func;
412 eb.notify_baton = notify_baton;
414 editor->open_root = edit_open;
415 editor->close_edit = edit_close;
417 editor->add_directory = dir_add;
418 editor->change_dir_prop = dir_change_prop;
419 editor->close_directory = dir_close;
421 editor->add_file = file_add;
422 editor->change_file_prop = file_change_prop;
423 editor->apply_textdelta = file_textdelta;
424 editor->close_file = file_close;
426 SVN_ERR(svn_delta_get_cancellation_editor(cancel_func, cancel_baton,
428 &wrapped_editor, &wrapped_baton,
431 SVN_ERR(svn_ra_do_update3(ra_session, &reporter, &reporter_baton,
432 location->rev, "", svn_depth_infinity,
433 FALSE, FALSE, wrapped_editor, wrapped_baton,
434 scratch_pool, scratch_pool));
436 SVN_ERR(reporter->set_path(reporter_baton, "", location->rev, depth,
437 TRUE /* incomplete */,
438 NULL, scratch_pool));
440 SVN_ERR(reporter->finish_report(reporter_baton, scratch_pool));
447 svn_client__copy_foreign(const char *url,
448 const char *dst_abspath,
449 svn_opt_revision_t *peg_revision,
450 svn_opt_revision_t *revision,
452 svn_boolean_t make_parents,
453 svn_boolean_t already_locked,
454 svn_client_ctx_t *ctx,
455 apr_pool_t *scratch_pool)
457 svn_ra_session_t *ra_session;
458 svn_client__pathrev_t *loc;
459 svn_node_kind_t kind;
460 svn_node_kind_t wc_kind;
461 const char *dir_abspath;
463 SVN_ERR_ASSERT(svn_path_is_url(url));
464 SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
466 /* Do we need to validate/update revisions? */
468 SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &loc,
474 SVN_ERR(svn_ra_check_path(ra_session, "", loc->rev, &kind, scratch_pool));
476 if (kind != svn_node_file && kind != svn_node_dir)
477 return svn_error_createf(
478 SVN_ERR_ILLEGAL_TARGET, NULL,
479 _("'%s' is not a valid location inside a repository"),
482 SVN_ERR(svn_wc_read_kind2(&wc_kind, ctx->wc_ctx, dst_abspath, FALSE, TRUE,
485 if (wc_kind != svn_node_none)
487 return svn_error_createf(
488 SVN_ERR_ENTRY_EXISTS, NULL,
489 _("'%s' is already under version control"),
490 svn_dirent_local_style(dst_abspath, scratch_pool));
493 dir_abspath = svn_dirent_dirname(dst_abspath, scratch_pool);
494 SVN_ERR(svn_wc_read_kind2(&wc_kind, ctx->wc_ctx, dir_abspath,
495 FALSE, FALSE, scratch_pool));
497 if (wc_kind == svn_node_none)
500 SVN_ERR(svn_client__make_local_parents(dir_abspath, make_parents, ctx,
503 SVN_ERR(svn_wc_read_kind2(&wc_kind, ctx->wc_ctx, dir_abspath,
504 FALSE, FALSE, scratch_pool));
507 if (wc_kind != svn_node_dir)
508 return svn_error_createf(
509 SVN_ERR_ENTRY_NOT_FOUND, NULL,
510 _("Can't add '%s', because no parent directory is found"),
511 svn_dirent_local_style(dst_abspath, scratch_pool));
514 if (kind == svn_node_file)
516 svn_stream_t *target;
518 apr_hash_index_t *hi;
519 SVN_ERR(svn_stream_open_writable(&target, dst_abspath, scratch_pool,
522 SVN_ERR(svn_ra_get_file(ra_session, "", loc->rev, target, NULL, &props,
526 for (hi = apr_hash_first(scratch_pool, props); hi;
527 hi = apr_hash_next(hi))
529 const char *name = svn__apr_hash_index_key(hi);
531 if (svn_property_kind2(name) != svn_prop_regular_kind
532 || ! strcmp(name, SVN_PROP_MERGEINFO))
534 /* We can't handle DAV, ENTRY and merge specific props here */
535 svn_hash_sets(props, name, NULL);
540 SVN_WC__CALL_WITH_WRITE_LOCK(
541 svn_wc_add_from_disk2(ctx->wc_ctx, dst_abspath, props,
542 ctx->notify_func2, ctx->notify_baton2,
544 ctx->wc_ctx, dir_abspath, FALSE, scratch_pool);
546 SVN_ERR(svn_wc_add_from_disk2(ctx->wc_ctx, dst_abspath, props,
547 ctx->notify_func2, ctx->notify_baton2,
553 SVN_WC__CALL_WITH_WRITE_LOCK(
554 copy_foreign_dir(ra_session, loc,
555 ctx->wc_ctx, dst_abspath,
557 ctx->notify_func2, ctx->notify_baton2,
558 ctx->cancel_func, ctx->cancel_baton,
560 ctx->wc_ctx, dir_abspath, FALSE, scratch_pool);
562 SVN_ERR(copy_foreign_dir(ra_session, loc,
563 ctx->wc_ctx, dst_abspath,
565 ctx->notify_func2, ctx->notify_baton2,
566 ctx->cancel_func, ctx->cancel_baton,