2 * commit.c: wrappers around wc commit functionality.
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 * ====================================================================
19 /* ==================================================================== */
27 #include <apr_strings.h>
32 #include "svn_delta.h"
33 #include "svn_subst.h"
34 #include "svn_client.h"
35 #include "svn_string.h"
36 #include "svn_pools.h"
37 #include "svn_error.h"
38 #include "svn_error_codes.h"
43 #include "svn_sorts.h"
44 #include "svn_props.h"
49 #include "svn_private_config.h"
51 /* Import context baton.
53 ### TODO: Add the following items to this baton:
54 /` import editor/baton. `/
55 const svn_delta_editor_t *editor;
58 /` Client context baton `/
59 svn_client_ctx_t `ctx;
61 /` Paths (keys) excluded from the import (values ignored) `/
64 typedef struct import_ctx_t
66 /* Whether any changes were made to the repository */
67 svn_boolean_t repos_changed
;
72 /* Apply PATH's contents (as a delta against the empty string) to
73 FILE_BATON in EDITOR. Use POOL for any temporary allocation.
74 PROPERTIES is the set of node properties set on this file.
76 Fill DIGEST with the md5 checksum of the sent file; DIGEST must be
77 at least APR_MD5_DIGESTSIZE bytes long. */
79 send_file_contents(const char *path
,
81 const svn_delta_editor_t
*editor
,
82 apr_hash_t
*properties
,
83 unsigned char *digest
,
86 const char *tmpfile_path
= NULL
;
87 svn_stream_t
*contents
;
88 svn_txdelta_window_handler_t handler
;
91 const svn_string_t
*eol_style_val
= NULL
, *keywords_val
= NULL
;
92 svn_boolean_t special
= FALSE
;
93 svn_subst_eol_style_t eol_style
;
97 /* If there are properties, look for EOL-style and keywords ones. */
100 eol_style_val
= apr_hash_get(properties
, SVN_PROP_EOL_STYLE
,
101 sizeof(SVN_PROP_EOL_STYLE
) - 1);
102 keywords_val
= apr_hash_get(properties
, SVN_PROP_KEYWORDS
,
103 sizeof(SVN_PROP_KEYWORDS
) - 1);
104 if (apr_hash_get(properties
, SVN_PROP_SPECIAL
, APR_HASH_KEY_STRING
))
108 /* Get an editor func that wants to consume the delta stream. */
109 SVN_ERR(editor
->apply_textdelta(file_baton
, NULL
, pool
,
110 &handler
, &handler_baton
));
113 svn_subst_eol_style_from_value(&eol_style
, &eol
, eol_style_val
->data
);
117 eol_style
= svn_subst_eol_style_none
;
121 SVN_ERR(svn_subst_build_keywords2(&keywords
, keywords_val
->data
,
122 APR_STRINGIFY(SVN_INVALID_REVNUM
),
127 /* If we have EOL styles or keywords to de-translate, do it. */
128 if (svn_subst_translation_required(eol_style
, eol
, keywords
, special
, TRUE
))
130 const char *temp_dir
;
132 /* Now create a new tempfile, and open a stream to it. */
133 SVN_ERR(svn_io_temp_dir(&temp_dir
, pool
));
134 SVN_ERR(svn_io_open_unique_file2
135 (NULL
, &tmpfile_path
,
136 svn_path_join(temp_dir
, "svn-import", pool
),
137 ".tmp", svn_io_file_del_on_pool_cleanup
, pool
));
139 SVN_ERR(svn_subst_translate_to_normal_form
140 (path
, tmpfile_path
, eol_style
, eol
, FALSE
,
141 keywords
, special
, pool
));
144 /* Open our contents file, either the original path or the temporary
145 copy we might have made above. */
146 SVN_ERR(svn_io_file_open(&f
, tmpfile_path
? tmpfile_path
: path
,
147 APR_READ
, APR_OS_DEFAULT
, pool
));
148 contents
= svn_stream_from_aprfile(f
, pool
);
150 /* Send the file's contents to the delta-window handler. */
151 SVN_ERR(svn_txdelta_send_stream(contents
, handler
, handler_baton
,
154 /* Close our contents file. */
155 SVN_ERR(svn_io_file_close(f
, pool
));
157 /* The temp file is removed by the pool cleanup run by the caller */
163 /* Import file PATH as EDIT_PATH in the repository directory indicated
164 * by DIR_BATON in EDITOR.
166 * Accumulate file paths and their batons in FILES, which must be
167 * non-null. (These are used to send postfix textdeltas later).
169 * If CTX->NOTIFY_FUNC is non-null, invoke it with CTX->NOTIFY_BATON
172 * Use POOL for any temporary allocation.
175 import_file(const svn_delta_editor_t
*editor
,
178 const char *edit_path
,
179 import_ctx_t
*import_ctx
,
180 svn_client_ctx_t
*ctx
,
184 const char *mimetype
= NULL
;
185 unsigned char digest
[APR_MD5_DIGESTSIZE
];
186 const char *text_checksum
;
187 apr_hash_t
* properties
;
188 apr_hash_index_t
*hi
;
189 svn_node_kind_t kind
;
190 svn_boolean_t is_special
;
192 SVN_ERR(svn_path_check_valid(path
, pool
));
194 SVN_ERR(svn_io_check_special_path(path
, &kind
, &is_special
, pool
));
196 /* Add the file, using the pool from the FILES hash. */
197 SVN_ERR(editor
->add_file(edit_path
, dir_baton
, NULL
, SVN_INVALID_REVNUM
,
200 /* Remember that the repository was modified */
201 import_ctx
->repos_changed
= TRUE
;
205 /* add automatic properties */
206 SVN_ERR(svn_client__get_auto_props(&properties
, &mimetype
, path
, ctx
,
210 properties
= apr_hash_make(pool
);
214 for (hi
= apr_hash_first(pool
, properties
); hi
; hi
= apr_hash_next(hi
))
219 apr_hash_this(hi
, &pname
, NULL
, &pval
);
220 SVN_ERR(editor
->change_file_prop(file_baton
, pname
, pval
, pool
));
224 if (ctx
->notify_func2
)
226 svn_wc_notify_t
*notify
227 = svn_wc_create_notify(path
, svn_wc_notify_commit_added
, pool
);
228 notify
->kind
= svn_node_file
;
229 notify
->mime_type
= mimetype
;
230 notify
->content_state
= notify
->prop_state
231 = svn_wc_notify_state_inapplicable
;
232 notify
->lock_state
= svn_wc_notify_lock_state_inapplicable
;
233 (*ctx
->notify_func2
)(ctx
->notify_baton2
, notify
, pool
);
236 /* If this is a special file, we need to set the svn:special
237 property and create a temporary detranslated version in order to
238 send to the server. */
241 apr_hash_set(properties
, SVN_PROP_SPECIAL
, APR_HASH_KEY_STRING
,
242 svn_string_create(SVN_PROP_BOOLEAN_TRUE
, pool
));
243 SVN_ERR(editor
->change_file_prop(file_baton
, SVN_PROP_SPECIAL
,
244 apr_hash_get(properties
,
246 APR_HASH_KEY_STRING
),
250 /* Now, transmit the file contents. */
251 SVN_ERR(send_file_contents(path
, file_baton
, editor
,
252 properties
, digest
, pool
));
254 /* Finally, close the file. */
255 text_checksum
= svn_md5_digest_to_cstring(digest
, pool
);
256 SVN_ERR(editor
->close_file(file_baton
, text_checksum
, pool
));
262 /* Import directory PATH into the repository directory indicated by
263 * DIR_BATON in EDITOR. EDIT_PATH is the path imported as the root
264 * directory, so all edits are relative to that.
266 * DEPTH is the depth at this point in the descent (it may be changed
267 * for recursive calls).
269 * Accumulate file paths and their batons in FILES, which must be
270 * non-null. (These are used to send postfix textdeltas later).
272 * EXCLUDES is a hash whose keys are absolute paths to exclude from
273 * the import (values are unused).
275 * If NO_IGNORE is FALSE, don't import files or directories that match
278 * If CTX->NOTIFY_FUNC is non-null, invoke it with CTX->NOTIFY_BATON for each
281 * Use POOL for any temporary allocation. */
283 import_dir(const svn_delta_editor_t
*editor
,
286 const char *edit_path
,
288 apr_hash_t
*excludes
,
289 svn_boolean_t no_ignore
,
290 svn_boolean_t ignore_unknown_node_types
,
291 import_ctx_t
*import_ctx
,
292 svn_client_ctx_t
*ctx
,
295 apr_pool_t
*subpool
= svn_pool_create(pool
); /* iteration pool */
297 apr_hash_index_t
*hi
;
298 apr_array_header_t
*ignores
;
300 SVN_ERR(svn_path_check_valid(path
, pool
));
303 SVN_ERR(svn_wc_get_default_ignores(&ignores
, ctx
->config
, pool
));
305 SVN_ERR(svn_io_get_dirents2(&dirents
, path
, pool
));
307 for (hi
= apr_hash_first(pool
, dirents
); hi
; hi
= apr_hash_next(hi
))
309 const char *this_path
, *this_edit_path
, *abs_path
;
310 const svn_io_dirent_t
*dirent
;
311 const char *filename
;
315 svn_pool_clear(subpool
);
317 apr_hash_this(hi
, &key
, NULL
, &val
);
322 if (ctx
->cancel_func
)
323 SVN_ERR(ctx
->cancel_func(ctx
->cancel_baton
));
325 if (svn_wc_is_adm_dir(filename
, subpool
))
327 /* If someone's trying to import a directory named the same
328 as our administrative directories, that's probably not
329 what they wanted to do. If they are importing a file
330 with that name, something is bound to blow up when they
331 checkout what they've imported. So, just skip items with
333 if (ctx
->notify_func2
)
335 svn_wc_notify_t
*notify
336 = svn_wc_create_notify(svn_path_join(path
, filename
,
338 svn_wc_notify_skip
, subpool
);
339 notify
->kind
= svn_node_dir
;
340 notify
->content_state
= notify
->prop_state
341 = svn_wc_notify_state_inapplicable
;
342 notify
->lock_state
= svn_wc_notify_lock_state_inapplicable
;
343 (*ctx
->notify_func2
)(ctx
->notify_baton2
, notify
, subpool
);
348 /* Typically, we started importing from ".", in which case
349 edit_path is "". So below, this_path might become "./blah",
350 and this_edit_path might become "blah", for example. */
351 this_path
= svn_path_join(path
, filename
, subpool
);
352 this_edit_path
= svn_path_join(edit_path
, filename
, subpool
);
354 /* If this is an excluded path, exclude it. */
355 SVN_ERR(svn_path_get_absolute(&abs_path
, this_path
, subpool
));
356 if (apr_hash_get(excludes
, abs_path
, APR_HASH_KEY_STRING
))
359 if ((!no_ignore
) && svn_wc_match_ignore_list(filename
, ignores
,
363 if (dirent
->kind
== svn_node_dir
&& depth
>= svn_depth_immediates
)
365 void *this_dir_baton
;
367 /* Add the new subdirectory, getting a descent baton from
369 SVN_ERR(editor
->add_directory(this_edit_path
, dir_baton
,
370 NULL
, SVN_INVALID_REVNUM
, subpool
,
373 /* Remember that the repository was modified */
374 import_ctx
->repos_changed
= TRUE
;
376 /* By notifying before the recursive call below, we display
377 a directory add before displaying adds underneath the
378 directory. To do it the other way around, just move this
379 after the recursive call. */
380 if (ctx
->notify_func2
)
382 svn_wc_notify_t
*notify
383 = svn_wc_create_notify(this_path
, svn_wc_notify_commit_added
,
385 notify
->kind
= svn_node_dir
;
386 notify
->content_state
= notify
->prop_state
387 = svn_wc_notify_state_inapplicable
;
388 notify
->lock_state
= svn_wc_notify_lock_state_inapplicable
;
389 (*ctx
->notify_func2
)(ctx
->notify_baton2
, notify
, subpool
);
394 svn_depth_t depth_below_here
= depth
;
395 if (depth
== svn_depth_immediates
)
396 depth_below_here
= svn_depth_empty
;
398 SVN_ERR(import_dir(editor
, this_dir_baton
, this_path
,
399 this_edit_path
, depth_below_here
, excludes
,
400 no_ignore
, ignore_unknown_node_types
,
405 /* Finally, close the sub-directory. */
406 SVN_ERR(editor
->close_directory(this_dir_baton
, subpool
));
408 else if (dirent
->kind
== svn_node_file
&& depth
>= svn_depth_files
)
410 SVN_ERR(import_file(editor
, dir_baton
, this_path
,
411 this_edit_path
, import_ctx
, ctx
, subpool
));
413 else if (dirent
->kind
!= svn_node_dir
&& dirent
->kind
!= svn_node_file
)
415 if (ignore_unknown_node_types
)
418 if (ctx
->notify_func2
)
420 svn_wc_notify_t
*notify
421 = svn_wc_create_notify(this_path
,
422 svn_wc_notify_skip
, subpool
);
423 notify
->kind
= svn_node_dir
;
424 notify
->content_state
= notify
->prop_state
425 = svn_wc_notify_state_inapplicable
;
426 notify
->lock_state
= svn_wc_notify_lock_state_inapplicable
;
427 (*ctx
->notify_func2
)(ctx
->notify_baton2
, notify
, subpool
);
431 return svn_error_createf
432 (SVN_ERR_NODE_UNKNOWN_KIND
, NULL
,
433 _("Unknown or unversionable type for '%s'"),
434 svn_path_local_style(this_path
, subpool
));
438 svn_pool_destroy(subpool
);
443 /* Recursively import PATH to a repository using EDITOR and
444 * EDIT_BATON. PATH can be a file or directory.
446 * DEPTH is the depth at which to import PATH; it behaves as for
447 * svn_client_import3().
449 * NEW_ENTRIES is an ordered array of path components that must be
450 * created in the repository (where the ordering direction is
451 * parent-to-child). If PATH is a directory, NEW_ENTRIES may be empty
452 * -- the result is an import which creates as many new entries in the
453 * top repository target directory as there are importable entries in
454 * the top of PATH; but if NEW_ENTRIES is not empty, its last item is
455 * the name of a new subdirectory in the repository to hold the
456 * import. If PATH is a file, NEW_ENTRIES may not be empty, and its
457 * last item is the name used for the file in the repository. If
458 * NEW_ENTRIES contains more than one item, all but the last item are
459 * the names of intermediate directories that are created before the
460 * real import begins. NEW_ENTRIES may NOT be NULL.
462 * EXCLUDES is a hash whose keys are absolute paths to exclude from
463 * the import (values are unused).
465 * If NO_IGNORE is FALSE, don't import files or directories that match
468 * If CTX->NOTIFY_FUNC is non-null, invoke it with CTX->NOTIFY_BATON for
469 * each imported path, passing actions svn_wc_notify_commit_added.
471 * Use POOL for any temporary allocation.
473 * Note: the repository directory receiving the import was specified
474 * when the editor was fetched. (I.e, when EDITOR->open_root() is
475 * called, it returns a directory baton for that directory, which is
476 * not necessarily the root.)
479 import(const char *path
,
480 apr_array_header_t
*new_entries
,
481 const svn_delta_editor_t
*editor
,
484 apr_hash_t
*excludes
,
485 svn_boolean_t no_ignore
,
486 svn_boolean_t ignore_unknown_node_types
,
487 svn_client_ctx_t
*ctx
,
491 svn_node_kind_t kind
;
492 apr_array_header_t
*ignores
;
493 apr_array_header_t
*batons
= NULL
;
494 const char *edit_path
= "";
495 import_ctx_t
*import_ctx
= apr_pcalloc(pool
, sizeof(*import_ctx
));
497 /* Get a root dir baton. We pass an invalid revnum to open_root
498 to mean "base this on the youngest revision". Should we have an
499 SVN_YOUNGEST_REVNUM defined for these purposes? */
500 SVN_ERR(editor
->open_root(edit_baton
, SVN_INVALID_REVNUM
,
503 /* Import a file or a directory tree. */
504 SVN_ERR(svn_io_check_path(path
, &kind
, pool
));
506 /* Make the intermediate directory components necessary for properly
507 rooting our import source tree. */
508 if (new_entries
->nelts
)
512 batons
= apr_array_make(pool
, new_entries
->nelts
, sizeof(void *));
513 for (i
= 0; i
< new_entries
->nelts
; i
++)
515 const char *component
= APR_ARRAY_IDX(new_entries
, i
, const char *);
516 edit_path
= svn_path_join(edit_path
, component
, pool
);
518 /* If this is the last path component, and we're importing a
519 file, then this component is the name of the file, not an
520 intermediate directory. */
521 if ((i
== new_entries
->nelts
- 1) && (kind
== svn_node_file
))
524 APR_ARRAY_PUSH(batons
, void *) = root_baton
;
525 SVN_ERR(editor
->add_directory(edit_path
,
527 NULL
, SVN_INVALID_REVNUM
,
530 /* Remember that the repository was modified */
531 import_ctx
->repos_changed
= TRUE
;
534 else if (kind
== svn_node_file
)
536 return svn_error_create
537 (SVN_ERR_NODE_UNKNOWN_KIND
, NULL
,
538 _("New entry name required when importing a file"));
541 /* Note that there is no need to check whether PATH's basename is
542 the same name that we reserve for our administrative
543 subdirectories. It would be strange -- though not illegal -- to
544 import the contents of a directory of that name, because the
545 directory's own name is not part of those contents. Of course,
546 if something underneath it also has our reserved name, then we'll
549 if (kind
== svn_node_file
)
551 svn_boolean_t ignores_match
= FALSE
;
555 SVN_ERR(svn_wc_get_default_ignores(&ignores
, ctx
->config
, pool
));
556 ignores_match
= svn_wc_match_ignore_list(path
, ignores
, pool
);
559 SVN_ERR(import_file(editor
, root_baton
, path
, edit_path
,
560 import_ctx
, ctx
, pool
));
562 else if (kind
== svn_node_dir
)
564 SVN_ERR(import_dir(editor
, root_baton
, path
, edit_path
,
565 depth
, excludes
, no_ignore
,
566 ignore_unknown_node_types
, import_ctx
, ctx
, pool
));
569 else if (kind
== svn_node_none
570 || kind
== svn_node_unknown
)
572 return svn_error_createf(SVN_ERR_NODE_UNKNOWN_KIND
, NULL
,
573 _("'%s' does not exist"),
574 svn_path_local_style(path
, pool
));
577 /* Close up shop; it's time to go home. */
578 SVN_ERR(editor
->close_directory(root_baton
, pool
));
579 if (batons
&& batons
->nelts
)
582 while ((baton
= (void **) apr_array_pop(batons
)))
584 SVN_ERR(editor
->close_directory(*baton
, pool
));
588 if (import_ctx
->repos_changed
)
589 SVN_ERR(editor
->close_edit(edit_baton
, pool
));
591 SVN_ERR(editor
->abort_edit(edit_baton
, pool
));
598 get_ra_editor(svn_ra_session_t
**ra_session
,
599 svn_revnum_t
*latest_rev
,
600 const svn_delta_editor_t
**editor
,
602 svn_client_ctx_t
*ctx
,
603 const char *base_url
,
604 const char *base_dir
,
605 svn_wc_adm_access_t
*base_access
,
607 apr_array_header_t
*commit_items
,
608 svn_commit_info_t
**commit_info_p
,
609 svn_boolean_t is_commit
,
610 apr_hash_t
*lock_tokens
,
611 svn_boolean_t keep_locks
,
615 apr_hash_t
*revprop_table
;
617 /* Open an RA session to URL. */
618 SVN_ERR(svn_client__open_ra_session_internal(ra_session
,
620 base_access
, commit_items
,
621 is_commit
, !is_commit
,
624 /* If this is an import (aka, not a commit), we need to verify that
625 our repository URL exists. */
628 svn_node_kind_t kind
;
630 SVN_ERR(svn_ra_check_path(*ra_session
, "", SVN_INVALID_REVNUM
,
632 if (kind
== svn_node_none
)
633 return svn_error_createf(SVN_ERR_FS_NO_SUCH_ENTRY
, NULL
,
634 _("Path '%s' does not exist"),
638 /* Fetch the latest revision if requested. */
640 SVN_ERR(svn_ra_get_latest_revnum(*ra_session
, latest_rev
, pool
));
642 SVN_ERR(svn_client__get_revprop_table(&revprop_table
, log_msg
, ctx
, pool
));
644 /* Fetch RA commit editor. */
645 SVN_ERR(svn_client__commit_get_baton(&commit_baton
, commit_info_p
, pool
));
646 return svn_ra_get_commit_editor3(*ra_session
, editor
, edit_baton
,
648 svn_client__commit_callback
,
649 commit_baton
, lock_tokens
, keep_locks
,
654 /*** Public Interfaces. ***/
657 svn_client_import3(svn_commit_info_t
**commit_info_p
,
661 svn_boolean_t no_ignore
,
662 svn_boolean_t ignore_unknown_node_types
,
663 svn_client_ctx_t
*ctx
,
666 svn_error_t
*err
= SVN_NO_ERROR
;
667 const char *log_msg
= "";
668 const svn_delta_editor_t
*editor
;
670 svn_ra_session_t
*ra_session
;
671 apr_hash_t
*excludes
= apr_hash_make(pool
);
672 svn_node_kind_t kind
;
673 const char *base_dir
= path
;
674 apr_array_header_t
*new_entries
= apr_array_make(pool
, 4,
675 sizeof(const char *));
680 /* Create a new commit item and add it to the array. */
681 if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx
))
683 /* If there's a log message gatherer, create a temporary commit
684 item array solely to help generate the log message. The
685 array is not used for the import itself. */
686 svn_client_commit_item3_t
*item
;
687 const char *tmp_file
;
688 apr_array_header_t
*commit_items
689 = apr_array_make(pool
, 1, sizeof(item
));
691 SVN_ERR(svn_client_commit_item_create
692 ((const svn_client_commit_item3_t
**) &item
, pool
));
693 item
->path
= apr_pstrdup(pool
, path
);
694 item
->state_flags
= SVN_CLIENT_COMMIT_ITEM_ADD
;
695 APR_ARRAY_PUSH(commit_items
, svn_client_commit_item3_t
*) = item
;
697 SVN_ERR(svn_client__get_log_msg(&log_msg
, &tmp_file
, commit_items
,
703 const char *abs_path
;
704 SVN_ERR(svn_path_get_absolute(&abs_path
, tmp_file
, pool
));
705 apr_hash_set(excludes
, abs_path
, APR_HASH_KEY_STRING
, (void *)1);
709 SVN_ERR(svn_io_check_path(path
, &kind
, pool
));
710 if (kind
== svn_node_file
)
711 svn_path_split(path
, &base_dir
, NULL
, pool
);
713 /* Figure out all the path components we need to create just to have
714 a place to stick our imported tree. */
715 subpool
= svn_pool_create(pool
);
718 svn_pool_clear(subpool
);
720 /* See if the user is interested in cancelling this operation. */
721 if (ctx
->cancel_func
)
722 SVN_ERR(ctx
->cancel_func(ctx
->cancel_baton
));
726 /* If get_ra_editor below failed we either tried to open
727 an invalid url, or else some other kind of error. In case
728 the url was bad we back up a directory and try again. */
730 if (err
->apr_err
!= SVN_ERR_FS_NO_SUCH_ENTRY
)
733 svn_error_clear(err
);
735 svn_path_split(url
, &temp
, &dir
, pool
);
736 APR_ARRAY_PUSH(new_entries
, const char *) =
737 svn_path_uri_decode(dir
, pool
);
741 while ((err
= get_ra_editor(&ra_session
, NULL
,
742 &editor
, &edit_baton
, ctx
, url
, base_dir
,
743 NULL
, log_msg
, NULL
, commit_info_p
,
744 FALSE
, NULL
, TRUE
, subpool
)));
746 /* Reverse the order of the components we added to our NEW_ENTRIES array. */
747 if (new_entries
->nelts
)
750 const char *component
;
751 for (i
= 0; i
< (new_entries
->nelts
/ 2); i
++)
753 j
= new_entries
->nelts
- i
- 1;
755 APR_ARRAY_IDX(new_entries
, i
, const char *);
756 APR_ARRAY_IDX(new_entries
, i
, const char *) =
757 APR_ARRAY_IDX(new_entries
, j
, const char *);
758 APR_ARRAY_IDX(new_entries
, j
, const char *) =
763 /* An empty NEW_ENTRIES list the first call to get_ra_editor() above
764 succeeded. That means that URL corresponds to an already
765 existing filesystem entity. */
766 if (kind
== svn_node_file
&& (! new_entries
->nelts
))
767 return svn_error_createf
768 (SVN_ERR_ENTRY_EXISTS
, NULL
,
769 _("Path '%s' already exists"), url
);
771 /* The repository doesn't know about the reserved administrative
773 if (new_entries
->nelts
774 /* What's this, what's this? This assignment is here because we
775 use the value to construct the error message just below. It
776 may not be aesthetically pleasing, but it's less ugly than
777 calling APR_ARRAY_IDX twice. */
778 && svn_wc_is_adm_dir(temp
= APR_ARRAY_IDX(new_entries
,
779 new_entries
->nelts
- 1,
782 return svn_error_createf
783 (SVN_ERR_CL_ADM_DIR_RESERVED
, NULL
,
784 _("'%s' is a reserved name and cannot be imported"),
785 /* ### Is svn_path_local_style() really necessary for this? */
786 svn_path_local_style(temp
, pool
));
789 /* If an error occurred during the commit, abort the edit and return
790 the error. We don't even care if the abort itself fails. */
791 if ((err
= import(path
, new_entries
, editor
, edit_baton
,
792 depth
, excludes
, no_ignore
,
793 ignore_unknown_node_types
, ctx
, subpool
)))
795 svn_error_clear(editor
->abort_edit(edit_baton
, subpool
));
799 /* Transfer *COMMIT_INFO from the subpool to the callers pool */
802 svn_commit_info_t
*tmp_commit_info
;
804 tmp_commit_info
= svn_create_commit_info(pool
);
805 *tmp_commit_info
= **commit_info_p
;
806 if (tmp_commit_info
->date
)
807 tmp_commit_info
->date
= apr_pstrdup(pool
, tmp_commit_info
->date
);
808 if (tmp_commit_info
->author
)
809 tmp_commit_info
->author
= apr_pstrdup(pool
, tmp_commit_info
->author
);
810 if (tmp_commit_info
->post_commit_err
)
811 tmp_commit_info
->post_commit_err
812 = apr_pstrdup(pool
, tmp_commit_info
->post_commit_err
);
813 *commit_info_p
= tmp_commit_info
;
816 svn_pool_destroy(subpool
);
823 svn_client_import2(svn_commit_info_t
**commit_info_p
,
826 svn_boolean_t nonrecursive
,
827 svn_boolean_t no_ignore
,
828 svn_client_ctx_t
*ctx
,
831 return svn_client_import3(commit_info_p
,
833 SVN_DEPTH_INFINITY_OR_FILES(! nonrecursive
),
834 no_ignore
, FALSE
, ctx
, pool
);
838 svn_client_import(svn_client_commit_info_t
**commit_info_p
,
841 svn_boolean_t nonrecursive
,
842 svn_client_ctx_t
*ctx
,
845 svn_commit_info_t
*commit_info
= NULL
;
848 err
= svn_client_import2(&commit_info
,
849 path
, url
, nonrecursive
,
851 /* These structs have the same layout for the common fields. */
852 *commit_info_p
= (svn_client_commit_info_t
*) commit_info
;
857 remove_tmpfiles(apr_hash_t
*tempfiles
,
860 apr_hash_index_t
*hi
;
863 /* Split if there's nothing to be done. */
867 /* Make a subpool. */
868 subpool
= svn_pool_create(pool
);
870 /* Clean up any tempfiles. */
871 for (hi
= apr_hash_first(pool
, tempfiles
); hi
; hi
= apr_hash_next(hi
))
877 svn_pool_clear(subpool
);
878 apr_hash_this(hi
, &key
, NULL
, &val
);
880 err
= svn_io_remove_file((const char *)key
, subpool
);
884 if (! APR_STATUS_IS_ENOENT(err
->apr_err
))
887 svn_error_clear(err
);
891 /* Remove the subpool. */
892 svn_pool_destroy(subpool
);
900 reconcile_errors(svn_error_t
*commit_err
,
901 svn_error_t
*unlock_err
,
902 svn_error_t
*bump_err
,
903 svn_error_t
*cleanup_err
,
908 /* Early release (for good behavior). */
909 if (! (commit_err
|| unlock_err
|| bump_err
|| cleanup_err
))
912 /* If there was a commit error, start off our error chain with
916 commit_err
= svn_error_quick_wrap
917 (commit_err
, _("Commit failed (details follow):"));
921 /* Else, create a new "general" error that will lead off the errors
924 err
= svn_error_create(SVN_ERR_BASE
, NULL
,
925 _("Commit succeeded, but other errors follow:"));
927 /* If there was an unlock error... */
930 /* Wrap the error with some headers. */
931 unlock_err
= svn_error_quick_wrap
932 (unlock_err
, _("Error unlocking locked dirs (details follow):"));
934 /* Append this error to the chain. */
935 svn_error_compose(err
, unlock_err
);
938 /* If there was a bumping error... */
941 /* Wrap the error with some headers. */
942 bump_err
= svn_error_quick_wrap
943 (bump_err
, _("Error bumping revisions post-commit (details follow):"));
945 /* Append this error to the chain. */
946 svn_error_compose(err
, bump_err
);
949 /* If there was a cleanup error... */
952 /* Wrap the error with some headers. */
953 cleanup_err
= svn_error_quick_wrap
954 (cleanup_err
, _("Error in post-commit clean-up (details follow):"));
956 /* Append this error to the chain. */
957 svn_error_compose(err
, cleanup_err
);
963 /* Remove redundancies by removing duplicates from NONRECURSIVE_TARGETS,
964 * and removing any target that either is, or is a descendant of, a path in
965 * RECURSIVE_TARGETS. Return the result in *PUNIQUE_TARGETS.
968 remove_redundancies(apr_array_header_t
**punique_targets
,
969 const apr_array_header_t
*nonrecursive_targets
,
970 const apr_array_header_t
*recursive_targets
,
973 apr_pool_t
*temp_pool
;
974 apr_array_header_t
*abs_recursive_targets
= NULL
;
975 apr_hash_t
*abs_targets
;
976 apr_array_header_t
*rel_targets
;
979 if ((nonrecursive_targets
->nelts
<= 0) || (! punique_targets
))
981 /* No targets or no place to store our work means this function
982 really has nothing to do. */
984 *punique_targets
= NULL
;
988 /* Initialize our temporary pool. */
989 temp_pool
= svn_pool_create(pool
);
991 /* Create our list of absolute paths for our "keepers" */
992 abs_targets
= apr_hash_make(temp_pool
);
994 /* Create our list of absolute paths for our recursive targets */
995 if (recursive_targets
)
997 abs_recursive_targets
= apr_array_make(temp_pool
,
998 recursive_targets
->nelts
,
999 sizeof(const char *));
1001 for (i
= 0; i
< recursive_targets
->nelts
; i
++)
1003 const char *rel_path
=
1004 APR_ARRAY_IDX(recursive_targets
, i
, const char *);
1005 const char *abs_path
;
1007 /* Get the absolute path for this target. */
1008 SVN_ERR(svn_path_get_absolute(&abs_path
, rel_path
, temp_pool
));
1010 APR_ARRAY_PUSH(abs_recursive_targets
, const char *) = abs_path
;
1014 /* Create our list of untainted paths for our "keepers" */
1015 rel_targets
= apr_array_make(pool
, nonrecursive_targets
->nelts
,
1016 sizeof(const char *));
1018 /* For each target in our list we do the following:
1020 1. Calculate its absolute path (ABS_PATH).
1021 2. See if any of the keepers in RECURSIVE_TARGETS is a parent of, or
1022 is the same path as, ABS_PATH. If so, we ignore this
1023 target. If not, however, add this target's original path to
1025 for (i
= 0; i
< nonrecursive_targets
->nelts
; i
++)
1027 const char *rel_path
= APR_ARRAY_IDX(nonrecursive_targets
, i
,
1029 const char *abs_path
;
1031 svn_boolean_t keep_me
;
1033 /* Get the absolute path for this target. */
1034 SVN_ERR(svn_path_get_absolute(&abs_path
, rel_path
, temp_pool
));
1036 /* For each keeper in ABS_TARGETS, see if this target is the
1037 same as or a child of that keeper. */
1040 if (abs_recursive_targets
)
1042 for (j
= 0; j
< abs_recursive_targets
->nelts
; j
++)
1044 const char *keeper
= APR_ARRAY_IDX(abs_recursive_targets
, j
,
1047 /* Quit here if we find this path already in the keepers. */
1048 if (strcmp(keeper
, abs_path
) == 0)
1054 /* Quit here if this path is a child of one of the keepers. */
1055 if (svn_path_is_child(keeper
, abs_path
, temp_pool
))
1063 /* If this is a new keeper, add its absolute path to ABS_TARGETS
1064 and its original path to REL_TARGETS. */
1066 && apr_hash_get(abs_targets
, abs_path
, APR_HASH_KEY_STRING
) == NULL
)
1068 APR_ARRAY_PUSH(rel_targets
, const char *) = rel_path
;
1069 apr_hash_set(abs_targets
, abs_path
, APR_HASH_KEY_STRING
, abs_path
);
1073 /* Destroy our temporary pool. */
1074 svn_pool_destroy(temp_pool
);
1076 /* Make sure we return the list of untainted keeper paths. */
1077 *punique_targets
= rel_targets
;
1079 return SVN_NO_ERROR
;
1082 /* Adjust relative targets. If there is an empty string in REL_TARGETS
1083 * get the actual target anchor point. It is likely that this is one dir up
1084 * from BASE_DIR, therefor we need to prepend the name part of the actual
1085 * target to all paths in REL_TARGETS. Return the new anchor in *PBASE_DIR,
1086 * and the adjusted relative paths in *PREL_TARGETS.
1088 static svn_error_t
*
1089 adjust_rel_targets(const char **pbase_dir
,
1090 apr_array_header_t
**prel_targets
,
1091 const char *base_dir
,
1092 apr_array_header_t
*rel_targets
,
1097 svn_boolean_t anchor_one_up
= FALSE
;
1098 apr_array_header_t
*new_rel_targets
;
1100 for (i
= 0; i
< rel_targets
->nelts
; i
++)
1102 target
= APR_ARRAY_IDX(rel_targets
, i
, const char *);
1104 if (target
[0] == '\0')
1106 anchor_one_up
= TRUE
;
1111 /* Default to not doing anything */
1112 new_rel_targets
= rel_targets
;
1116 const char *parent_dir
, *name
;
1118 SVN_ERR(svn_wc_get_actual_target(base_dir
, &parent_dir
, &name
, pool
));
1122 /* Our new "grandfather directory" is the parent directory
1123 of the former one. */
1124 base_dir
= apr_pstrdup(pool
, parent_dir
);
1126 new_rel_targets
= apr_array_make(pool
, rel_targets
->nelts
,
1128 for (i
= 0; i
< rel_targets
->nelts
; i
++)
1130 target
= APR_ARRAY_IDX(rel_targets
, i
, const char *);
1131 target
= svn_path_join(name
, target
, pool
);
1132 APR_ARRAY_PUSH(new_rel_targets
, const char *) = target
;
1137 *pbase_dir
= base_dir
;
1138 *prel_targets
= new_rel_targets
;
1140 return SVN_NO_ERROR
;
1144 /* For all lock tokens in ALL_TOKENS for URLs under BASE_URL, add them
1145 to a new hashtable allocated in POOL. *RESULT is set to point to this
1146 new hash table. *RESULT will be keyed on const char * URI-decoded paths
1147 relative to BASE_URL. The lock tokens will not be duplicated. */
1148 static svn_error_t
*
1149 collect_lock_tokens(apr_hash_t
**result
,
1150 apr_hash_t
*all_tokens
,
1151 const char *base_url
,
1154 apr_hash_index_t
*hi
;
1155 size_t base_len
= strlen(base_url
);
1157 *result
= apr_hash_make(pool
);
1159 for (hi
= apr_hash_first(pool
, all_tokens
); hi
; hi
= apr_hash_next(hi
))
1166 apr_hash_this(hi
, &key
, NULL
, &val
);
1170 if (strncmp(base_url
, url
, base_len
) == 0
1171 && (url
[base_len
] == '\0' || url
[base_len
] == '/'))
1173 if (url
[base_len
] == '\0')
1176 url
= svn_path_uri_decode(url
+ base_len
+ 1, pool
);
1177 apr_hash_set(*result
, url
, APR_HASH_KEY_STRING
, token
);
1181 return SVN_NO_ERROR
;
1184 struct post_commit_baton
1186 svn_wc_committed_queue_t
*queue
;
1188 svn_wc_adm_access_t
*base_dir_access
;
1189 svn_boolean_t keep_changelist
;
1190 svn_boolean_t keep_locks
;
1191 apr_hash_t
*digests
;
1194 static svn_error_t
*
1195 post_process_commit_item(void *baton
, void *this_item
, apr_pool_t
*pool
)
1197 struct post_commit_baton
*btn
= baton
;
1198 apr_pool_t
*subpool
= btn
->qpool
;
1200 svn_client_commit_item3_t
*item
=
1201 *(svn_client_commit_item3_t
**)this_item
;
1202 svn_boolean_t loop_recurse
= FALSE
;
1203 const char *adm_access_path
;
1204 svn_wc_adm_access_t
*adm_access
;
1205 svn_boolean_t remove_lock
;
1206 svn_error_t
*bump_err
;
1208 if (item
->kind
== svn_node_dir
)
1209 adm_access_path
= item
->path
;
1211 svn_path_split(item
->path
, &adm_access_path
, NULL
, pool
);
1213 bump_err
= svn_wc_adm_retrieve(&adm_access
, btn
->base_dir_access
,
1214 adm_access_path
, pool
);
1216 && bump_err
->apr_err
== SVN_ERR_WC_NOT_LOCKED
)
1218 /* Is it a directory that was deleted in the commit?
1219 Then we probably committed a missing directory. */
1220 if (item
->kind
== svn_node_dir
1221 && item
->state_flags
& SVN_CLIENT_COMMIT_ITEM_DELETE
)
1223 /* Mark it as deleted in the parent. */
1224 svn_error_clear(bump_err
);
1225 return svn_wc_mark_missing_deleted(item
->path
,
1226 btn
->base_dir_access
, pool
);
1233 if ((item
->state_flags
& SVN_CLIENT_COMMIT_ITEM_ADD
)
1234 && (item
->kind
== svn_node_dir
)
1235 && (item
->copyfrom_url
))
1236 loop_recurse
= TRUE
;
1238 remove_lock
= (! btn
->keep_locks
&& (item
->state_flags
1239 & SVN_CLIENT_COMMIT_ITEM_LOCK_TOKEN
));
1241 /* Allocate the queue in a longer-lived pool than (iter)pool:
1242 we want it to survive the next iteration. */
1243 SVN_ERR(svn_wc_queue_committed
1245 item
->path
, adm_access
, loop_recurse
,
1246 item
->incoming_prop_changes
,
1247 remove_lock
, (! btn
->keep_changelist
),
1248 apr_hash_get(btn
->digests
, item
->path
, APR_HASH_KEY_STRING
),
1251 return SVN_NO_ERROR
;
1255 static svn_error_t
*
1256 commit_item_is_changed(void *baton
, void *this_item
, apr_pool_t
*pool
)
1258 svn_client_commit_item3_t
**item
= this_item
;
1260 if ((*item
)->state_flags
!= SVN_CLIENT_COMMIT_ITEM_LOCK_TOKEN
)
1261 svn_iter_break(pool
);
1263 return SVN_NO_ERROR
;
1266 struct lock_dirs_baton
1268 svn_client_ctx_t
*ctx
;
1269 svn_wc_adm_access_t
*base_dir_access
;
1273 static svn_error_t
*
1274 lock_dirs_for_commit(void *baton
, void *this_item
, apr_pool_t
*pool
)
1276 struct lock_dirs_baton
*btn
= baton
;
1277 svn_wc_adm_access_t
*adm_access
;
1279 return svn_wc_adm_open3(&adm_access
, btn
->base_dir_access
,
1280 *(const char **)this_item
,
1281 TRUE
, /* Write lock */
1282 btn
->levels_to_lock
,
1283 btn
->ctx
->cancel_func
,
1284 btn
->ctx
->cancel_baton
,
1288 struct check_dir_delete_baton
1290 svn_wc_adm_access_t
*base_dir_access
;
1294 static svn_error_t
*
1295 check_nonrecursive_dir_delete(void *baton
, void *this_item
, apr_pool_t
*pool
)
1297 struct check_dir_delete_baton
*btn
= baton
;
1298 svn_wc_adm_access_t
*adm_access
;
1301 SVN_ERR(svn_path_get_absolute(&target
, *(const char **)this_item
, pool
));
1302 SVN_ERR_W(svn_wc_adm_probe_retrieve(&adm_access
, btn
->base_dir_access
,
1304 _("Are all the targets part of the same working copy?"));
1306 /* ### TODO(sd): This check is slightly too strict. It should be
1309 ### * delete an empty directory when depth==svn_depth_empty;
1311 ### * delete a directory containing only files when
1312 ### depth==svn_depth_files;
1314 ### * delete a directory containing only files and empty
1315 ### subdirs when depth==svn_depth_immediates.
1317 ### But for now, we insist on svn_depth_infinity if you're
1318 ### going to delete a directory, because we're lazy and
1319 ### trying to get depthy commits working in the first place.
1321 ### This would be fairly easy to fix, though: just, well,
1322 ### check the above conditions!
1324 if (btn
->depth
!= svn_depth_infinity
)
1326 svn_wc_status2_t
*status
;
1327 svn_node_kind_t kind
;
1329 SVN_ERR(svn_io_check_path(target
, &kind
, pool
));
1331 if (kind
== svn_node_dir
)
1333 SVN_ERR(svn_wc_status2(&status
, target
, adm_access
, pool
));
1334 if (status
->text_status
== svn_wc_status_deleted
||
1335 status
->text_status
== svn_wc_status_replaced
)
1336 return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE
, NULL
,
1337 _("Cannot non-recursively commit a "
1338 "directory deletion"));
1341 return SVN_NO_ERROR
;
1345 svn_client_commit4(svn_commit_info_t
**commit_info_p
,
1346 const apr_array_header_t
*targets
,
1348 svn_boolean_t keep_locks
,
1349 svn_boolean_t keep_changelist
,
1350 const char *changelist_name
,
1351 svn_client_ctx_t
*ctx
,
1354 const svn_delta_editor_t
*editor
;
1356 svn_ra_session_t
*ra_session
;
1357 const char *log_msg
;
1358 const char *base_dir
;
1359 const char *base_url
;
1361 apr_array_header_t
*rel_targets
;
1362 apr_array_header_t
*dirs_to_lock
;
1363 apr_array_header_t
*dirs_to_lock_recursive
;
1364 svn_boolean_t lock_base_dir_recursive
= FALSE
;
1365 apr_hash_t
*committables
, *lock_tokens
, *tempfiles
= NULL
, *digests
;
1366 svn_wc_adm_access_t
*base_dir_access
;
1367 apr_array_header_t
*commit_items
;
1368 svn_error_t
*cmt_err
= SVN_NO_ERROR
, *unlock_err
= SVN_NO_ERROR
;
1369 svn_error_t
*bump_err
= SVN_NO_ERROR
, *cleanup_err
= SVN_NO_ERROR
;
1370 svn_boolean_t commit_in_progress
= FALSE
;
1371 const char *display_dir
= "";
1374 /* Committing URLs doesn't make sense, so error if it's tried. */
1375 for (i
= 0; i
< targets
->nelts
; i
++)
1377 target
= APR_ARRAY_IDX(targets
, i
, const char *);
1378 if (svn_path_is_url(target
))
1379 return svn_error_createf
1380 (SVN_ERR_ILLEGAL_TARGET
, NULL
,
1381 _("'%s' is a URL, but URLs cannot be commit targets"), target
);
1384 /* Condense the target list. */
1385 SVN_ERR(svn_path_condense_targets(&base_dir
, &rel_targets
, targets
,
1386 depth
== svn_depth_infinity
, pool
));
1388 /* When svn_path_condense_targets() was written, we didn't have real
1389 * depths, we just had recursive / nonrecursive.
1391 * Nowadays things are more complex. If depth == svn_depth_files,
1392 * for example, and two targets are "foo" and "foo/bar", then
1393 * ideally we should condense out "foo/bar" if it's a file and not
1394 * if it's a directory. And, of course, later when we get adm
1395 * access batons for the commit, we'd ideally lock directories to
1396 * precisely the depth required and no deeper.
1398 * But for now we don't do that. Instead, we lock recursively from
1399 * base_dir, if depth indicates that we might need anything below
1400 * there (but note that above, we don't condense away targets that
1401 * need to be named explicitly when depth != svn_depth_infinity).
1403 * Here's a case where this all matters:
1408 * $ svn ci -m "log msg" --depth=immediates . A/D/G
1410 * If we don't lock base_dir recursively, then it will get an error...
1412 * subversion/libsvn_wc/lock.c:570: (apr_err=155004)
1413 * svn: Working copy '/blah/blah/blah/wc' locked
1414 * svn: run 'svn cleanup' to remove locks \
1415 * (type 'svn help cleanup' for details)
1417 * ...because later (see dirs_to_lock_recursively and dirs_to_lock)
1418 * we'd call svn_wc_adm_open3() to get access objects for "" and
1419 * "A/D/G", but the request for "" would fail because base_dir_access
1420 * would already be open for that directory. (In that circumstance,
1421 * you're supposed to use svn_wc_adm_retrieve() instead; but it
1422 * would be clumsy to have a conditional path just to decide between
1423 * open3() and retrieve().)
1425 * (Note that the results would be the same if even the working copy
1426 * were an explicit argument, e.g.:
1427 * 'svn ci -m "log msg" --depth=immediates wc wc/A/D/G'.)
1429 * So we set lock_base_dir_recursive=TRUE now, and end up locking
1430 * more than we need to, but this keeps the code simple and correct.
1432 * In an inspired bit of foresight, the adm locking code anticipated
1433 * the eventual addition of svn_depth_immediates, and allows us to
1434 * set the exact number of lock levels. So optimizing the code here
1435 * at least shouldn't require any changes to the adm locking system.
1437 if (depth
== svn_depth_files
|| depth
== svn_depth_immediates
)
1439 const char *rel_target
;
1440 for (i
= 0; i
< rel_targets
->nelts
; ++i
)
1442 rel_target
= APR_ARRAY_IDX(rel_targets
, i
, const char *);
1443 if (rel_target
[0] == '\0')
1444 lock_base_dir_recursive
= TRUE
;
1448 /* No targets means nothing to commit, so just return. */
1452 /* Prepare an array to accumulate dirs to lock */
1453 dirs_to_lock
= apr_array_make(pool
, 1, sizeof(target
));
1454 dirs_to_lock_recursive
= apr_array_make(pool
, 1, sizeof(target
));
1456 /* If we calculated only a base_dir and no relative targets, this
1457 must mean that we are being asked to commit (effectively) a
1459 if ((! rel_targets
) || (! rel_targets
->nelts
))
1461 const char *parent_dir
, *name
;
1463 SVN_ERR(svn_wc_get_actual_target(base_dir
, &parent_dir
, &name
, pool
));
1466 svn_node_kind_t kind
;
1468 /* Our new "grandfather directory" is the parent directory
1469 of the former one. */
1470 base_dir
= apr_pstrdup(pool
, parent_dir
);
1472 /* Make the array if it wasn't already created. */
1474 rel_targets
= apr_array_make(pool
, targets
->nelts
, sizeof(name
));
1476 /* Now, push this name as a relative path to our new
1478 APR_ARRAY_PUSH(rel_targets
, const char *) = name
;
1480 target
= svn_path_join(base_dir
, name
, pool
);
1481 SVN_ERR(svn_io_check_path(target
, &kind
, pool
));
1483 /* If the final target is a dir, we want to recursively lock it */
1484 if (kind
== svn_node_dir
)
1486 if (depth
== svn_depth_infinity
|| depth
== svn_depth_immediates
)
1487 APR_ARRAY_PUSH(dirs_to_lock_recursive
, const char *) = target
;
1489 APR_ARRAY_PUSH(dirs_to_lock
, const char *) = target
;
1494 /* Unconditionally lock recursively down from base_dir. */
1495 lock_base_dir_recursive
= TRUE
;
1498 else if (! lock_base_dir_recursive
)
1500 apr_pool_t
*subpool
= svn_pool_create(pool
);
1502 SVN_ERR(adjust_rel_targets(&base_dir
, &rel_targets
,
1503 base_dir
, rel_targets
,
1506 for (i
= 0; i
< rel_targets
->nelts
; i
++)
1508 const char *parent_dir
, *name
;
1509 svn_node_kind_t kind
;
1511 svn_pool_clear(subpool
);
1513 target
= svn_path_join(base_dir
,
1514 APR_ARRAY_IDX(rel_targets
, i
, const char *),
1517 SVN_ERR(svn_io_check_path(target
, &kind
, subpool
));
1519 /* If the final target is a dir, we want to lock it */
1520 if (kind
== svn_node_dir
)
1522 /* Notice how here we test infinity||immediates, but up
1523 in the call to svn_path_condense_targets(), we only
1524 tested depth==infinity. That's because condensation
1525 and adm lock acquisition serve different purposes. */
1526 if (depth
== svn_depth_infinity
|| depth
== svn_depth_immediates
)
1527 APR_ARRAY_PUSH(dirs_to_lock_recursive
,
1528 const char *) = apr_pstrdup(pool
, target
);
1530 /* Don't lock if target is the base_dir, base_dir will be
1531 locked anyway and we can't lock it twice */
1532 if (strcmp(target
, base_dir
) != 0)
1533 APR_ARRAY_PUSH(dirs_to_lock
,
1534 const char *) = apr_pstrdup(pool
, target
);
1537 /* Now we need to iterate over the parent paths of this path
1538 adding them to the set of directories we want to lock.
1539 Do nothing if target is already the base_dir. */
1540 if (strcmp(target
, base_dir
) != 0)
1542 svn_path_split(target
, &parent_dir
, &name
, subpool
);
1544 target
= parent_dir
;
1546 while (strcmp(target
, base_dir
) != 0)
1548 if ((target
[0] == '\0') ||
1549 svn_dirent_is_root(target
, strlen(target
))
1553 APR_ARRAY_PUSH(dirs_to_lock
,
1554 const char *) = apr_pstrdup(pool
, target
);
1555 target
= svn_path_dirname(target
, subpool
);
1560 svn_pool_destroy(subpool
);
1563 SVN_ERR(svn_wc_adm_open3(&base_dir_access
, NULL
, base_dir
,
1564 TRUE
, /* Write lock */
1565 lock_base_dir_recursive
? -1 : 0, /* lock levels */
1566 ctx
->cancel_func
, ctx
->cancel_baton
,
1569 if (!lock_base_dir_recursive
)
1571 apr_array_header_t
*unique_dirs_to_lock
;
1572 struct lock_dirs_baton btn
;
1574 /* Sort the paths in a depth-last directory-ish order. */
1575 qsort(dirs_to_lock
->elts
, dirs_to_lock
->nelts
,
1576 dirs_to_lock
->elt_size
, svn_sort_compare_paths
);
1577 qsort(dirs_to_lock_recursive
->elts
, dirs_to_lock_recursive
->nelts
,
1578 dirs_to_lock_recursive
->elt_size
, svn_sort_compare_paths
);
1580 /* Remove any duplicates */
1581 SVN_ERR(svn_path_remove_redundancies(&unique_dirs_to_lock
,
1582 dirs_to_lock_recursive
,
1584 dirs_to_lock_recursive
= unique_dirs_to_lock
;
1586 /* Remove dirs and descendants from dirs_to_lock if there is
1587 any ancestor in dirs_to_lock_recursive */
1588 SVN_ERR(remove_redundancies(&unique_dirs_to_lock
,
1590 dirs_to_lock_recursive
,
1592 dirs_to_lock
= unique_dirs_to_lock
;
1594 btn
.base_dir_access
= base_dir_access
;
1596 btn
.levels_to_lock
= 0;
1597 /* First lock all the dirs to be locked non-recursively */
1599 SVN_ERR(svn_iter_apr_array(NULL
, dirs_to_lock
,
1600 lock_dirs_for_commit
, &btn
, pool
));
1602 /* Lock the rest of the targets (recursively) */
1603 btn
.levels_to_lock
= -1;
1604 if (dirs_to_lock_recursive
)
1605 SVN_ERR(svn_iter_apr_array(NULL
, dirs_to_lock_recursive
,
1606 lock_dirs_for_commit
, &btn
, pool
));
1609 /* One day we might support committing from multiple working copies, but
1610 we don't yet. This check ensures that we don't silently commit a
1611 subset of the targets.
1613 At the same time, if a non-recursive commit is desired, do not
1614 allow a deleted directory as one of the targets. */
1616 struct check_dir_delete_baton btn
;
1618 btn
.base_dir_access
= base_dir_access
;
1620 SVN_ERR(svn_iter_apr_array(NULL
, targets
,
1621 check_nonrecursive_dir_delete
, &btn
,
1625 /* Crawl the working copy for commit items. */
1626 if ((cmt_err
= svn_client__harvest_committables(&committables
,
1637 /* ### todo: Currently there should be only one hash entry, which
1638 has a hacked name until we have the entries files storing
1639 canonical repository URLs. Then, the hacked name can go away
1640 and be replaced with a canonical repos URL, and from there we
1641 are poised to started handling nested working copies. See
1642 http://subversion.tigris.org/issues/show_bug.cgi?id=960. */
1643 if (! ((commit_items
= apr_hash_get(committables
,
1644 SVN_CLIENT__SINGLE_REPOS_NAME
,
1645 APR_HASH_KEY_STRING
))))
1648 /* If our array of targets contains only locks (and no actual file
1649 or prop modifications), then we return here to avoid committing a
1650 revision with no changes. */
1652 svn_boolean_t not_found_changed_path
= TRUE
;
1655 cmt_err
= svn_iter_apr_array(¬_found_changed_path
,
1657 commit_item_is_changed
, NULL
, pool
);
1658 if (not_found_changed_path
|| cmt_err
)
1662 /* Go get a log message. If an error occurs, or no log message is
1663 specified, abort the operation. */
1664 if (SVN_CLIENT__HAS_LOG_MSG_FUNC(ctx
))
1666 const char *tmp_file
;
1667 cmt_err
= svn_client__get_log_msg(&log_msg
, &tmp_file
, commit_items
,
1670 if (cmt_err
|| (! log_msg
))
1676 /* Sort and condense our COMMIT_ITEMS. */
1677 if ((cmt_err
= svn_client__condense_commit_items(&base_url
,
1682 /* Collect our lock tokens with paths relative to base_url. */
1683 if ((cmt_err
= collect_lock_tokens(&lock_tokens
, lock_tokens
, base_url
,
1687 if ((cmt_err
= get_ra_editor(&ra_session
, NULL
,
1688 &editor
, &edit_baton
, ctx
,
1689 base_url
, base_dir
, base_dir_access
,
1690 log_msg
, commit_items
, commit_info_p
,
1691 TRUE
, lock_tokens
, keep_locks
, pool
)))
1694 /* Make a note that we have a commit-in-progress. */
1695 commit_in_progress
= TRUE
;
1697 /* Determine prefix to strip from the commit notify messages */
1698 if ((cmt_err
= svn_path_get_absolute(&display_dir
,
1699 display_dir
, pool
)))
1701 display_dir
= svn_path_get_longest_ancestor(display_dir
, base_dir
, pool
);
1703 /* Perform the commit. */
1704 cmt_err
= svn_client__do_commit(base_url
, commit_items
, base_dir_access
,
1707 &tempfiles
, &digests
, ctx
, pool
);
1709 /* Handle a successful commit. */
1711 || (cmt_err
->apr_err
== SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED
))
1713 svn_wc_committed_queue_t
*queue
= svn_wc_committed_queue_create(pool
);
1714 struct post_commit_baton btn
;
1718 btn
.base_dir_access
= base_dir_access
;
1719 btn
.keep_changelist
= keep_changelist
;
1720 btn
.keep_locks
= keep_locks
;
1721 btn
.digests
= digests
;
1723 /* Make a note that our commit is finished. */
1724 commit_in_progress
= FALSE
;
1726 bump_err
= svn_iter_apr_array(NULL
, commit_items
,
1727 post_process_commit_item
, &btn
,
1732 assert(*commit_info_p
);
1734 = svn_wc_process_committed_queue(queue
, base_dir_access
,
1735 (*commit_info_p
)->revision
,
1736 (*commit_info_p
)->date
,
1737 (*commit_info_p
)->author
,
1741 /* Sleep to ensure timestamp integrity. */
1742 svn_sleep_for_timestamps();
1745 /* Abort the commit if it is still in progress. */
1746 if (commit_in_progress
)
1747 svn_error_clear(editor
->abort_edit(edit_baton
, pool
));
1749 /* A bump error is likely to occur while running a working copy log file,
1750 explicitly unlocking and removing temporary files would be wrong in
1751 that case. A commit error (cmt_err) should only occur before any
1752 attempt to modify the working copy, so it doesn't prevent explicit
1756 unlock_err
= svn_wc_adm_close(base_dir_access
);
1759 cleanup_err
= remove_tmpfiles(tempfiles
, pool
);
1762 /* As per our promise, if *commit_info_p isn't set, provide a default where
1763 rev = SVN_INVALID_REVNUM. */
1764 if (! *commit_info_p
)
1765 *commit_info_p
= svn_create_commit_info(pool
);
1767 return reconcile_errors(cmt_err
, unlock_err
, bump_err
, cleanup_err
, pool
);
1771 svn_client_commit3(svn_commit_info_t
**commit_info_p
,
1772 const apr_array_header_t
*targets
,
1773 svn_boolean_t recurse
,
1774 svn_boolean_t keep_locks
,
1775 svn_client_ctx_t
*ctx
,
1778 svn_depth_t depth
= SVN_DEPTH_INFINITY_OR_FILES(recurse
);
1780 return svn_client_commit4(commit_info_p
, targets
, depth
, keep_locks
,
1781 FALSE
, NULL
, ctx
, pool
);
1785 svn_client_commit2(svn_client_commit_info_t
**commit_info_p
,
1786 const apr_array_header_t
*targets
,
1787 svn_boolean_t recurse
,
1788 svn_boolean_t keep_locks
,
1789 svn_client_ctx_t
*ctx
,
1792 svn_commit_info_t
*commit_info
= NULL
;
1795 err
= svn_client_commit3(&commit_info
, targets
, recurse
, keep_locks
,
1797 /* These structs have the same layout for the common fields. */
1798 *commit_info_p
= (svn_client_commit_info_t
*) commit_info
;
1803 svn_client_commit(svn_client_commit_info_t
**commit_info_p
,
1804 const apr_array_header_t
*targets
,
1805 svn_boolean_t nonrecursive
,
1806 svn_client_ctx_t
*ctx
,
1809 return svn_client_commit2(commit_info_p
, targets
,
1810 nonrecursive
? FALSE
: TRUE
,