2 * util.c : utility functions for the libsvn_client library
4 * ====================================================================
5 * Copyright (c) 2005-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 * ====================================================================
20 #include <apr_pools.h>
21 #include <apr_strings.h>
23 #include "svn_pools.h"
24 #include "svn_string.h"
25 #include "svn_error.h"
26 #include "svn_types.h"
28 #include "svn_props.h"
31 #include "svn_client.h"
33 #include "private/svn_wc_private.h"
37 #include "svn_private_config.h"
39 /* Duplicate a HASH containing (char * -> svn_string_t *) key/value
42 string_hash_dup(apr_hash_t
*hash
, apr_pool_t
*pool
)
48 apr_hash_t
*new_hash
= apr_hash_make(pool
);
49 for (hi
= apr_hash_first(pool
, hash
); hi
; hi
= apr_hash_next(hi
))
51 apr_hash_this(hi
, &key
, &klen
, &val
);
52 key
= apr_pstrdup(pool
, key
);
53 val
= svn_string_dup(val
, pool
);
54 apr_hash_set(new_hash
, key
, klen
, val
);
60 svn_client_commit_item_create(const svn_client_commit_item3_t
**item
,
63 *item
= apr_pcalloc(pool
, sizeof(svn_client_commit_item3_t
));
67 svn_client_commit_item3_t
*
68 svn_client_commit_item3_dup(const svn_client_commit_item3_t
*item
,
71 svn_client_commit_item3_t
*new_item
= apr_palloc(pool
, sizeof(*new_item
));
76 new_item
->path
= apr_pstrdup(pool
, new_item
->path
);
79 new_item
->url
= apr_pstrdup(pool
, new_item
->url
);
81 if (new_item
->copyfrom_url
)
82 new_item
->copyfrom_url
= apr_pstrdup(pool
, new_item
->copyfrom_url
);
84 if (new_item
->incoming_prop_changes
)
85 new_item
->incoming_prop_changes
=
86 svn_prop_array_dup(new_item
->incoming_prop_changes
, pool
);
88 if (new_item
->outgoing_prop_changes
)
89 new_item
->outgoing_prop_changes
=
90 svn_prop_array_dup(new_item
->outgoing_prop_changes
, pool
);
95 svn_client_commit_item2_t
*
96 svn_client_commit_item2_dup(const svn_client_commit_item2_t
*item
,
99 svn_client_commit_item2_t
*new_item
= apr_palloc(pool
, sizeof(*new_item
));
104 new_item
->path
= apr_pstrdup(pool
, new_item
->path
);
107 new_item
->url
= apr_pstrdup(pool
, new_item
->url
);
109 if (new_item
->copyfrom_url
)
110 new_item
->copyfrom_url
= apr_pstrdup(pool
, new_item
->copyfrom_url
);
112 if (new_item
->wcprop_changes
)
113 new_item
->wcprop_changes
= svn_prop_array_dup(new_item
->wcprop_changes
,
119 svn_client_proplist_item_t
*
120 svn_client_proplist_item_dup(const svn_client_proplist_item_t
*item
,
123 svn_client_proplist_item_t
*new_item
= apr_pcalloc(pool
, sizeof(*new_item
));
126 new_item
->node_name
= svn_stringbuf_dup(item
->node_name
, pool
);
129 new_item
->prop_hash
= string_hash_dup(item
->prop_hash
, pool
);
134 /* Return WC_PATH's URL and repository root in *URL and REPOS_ROOT,
135 respectively. Set *NEED_WC_CLEANUP if *ADM_ACCESS needed to be
138 wc_path_to_repos_urls(const char **url
, const char **repos_root
,
139 svn_boolean_t
*need_wc_cleanup
,
140 svn_wc_adm_access_t
**adm_access
, const char *wc_path
,
143 const svn_wc_entry_t
*entry
;
147 SVN_ERR(svn_wc_adm_probe_open3(adm_access
, NULL
, wc_path
,
148 FALSE
, 0, NULL
, NULL
, pool
));
149 *need_wc_cleanup
= TRUE
;
151 SVN_ERR(svn_wc__entry_versioned(&entry
, wc_path
, *adm_access
, FALSE
, pool
));
153 SVN_ERR(svn_client__entry_location(url
, NULL
, wc_path
,
154 svn_opt_revision_unspecified
, entry
,
157 /* If we weren't provided a REPOS_ROOT, we'll try to read one from
158 the entry. The entry might not hold a URL -- in that case, we'll
159 need a fallback plan. */
160 if (*repos_root
== NULL
)
161 *repos_root
= entry
->repos
;
168 svn_client__path_relative_to_root(const char **rel_path
,
169 const char *path_or_url
,
170 const char *repos_root
,
171 svn_boolean_t include_leading_slash
,
172 svn_ra_session_t
*ra_session
,
173 svn_wc_adm_access_t
*adm_access
,
176 svn_error_t
*err
= SVN_NO_ERROR
;
177 svn_boolean_t need_wc_cleanup
= FALSE
;
179 assert(repos_root
!= NULL
|| ra_session
!= NULL
);
181 /* If we have a WC path... */
182 if (! svn_path_is_url(path_or_url
))
184 /* ...fetch its entry, and attempt to get both its full URL and
185 repository root URL. If we can't get REPOS_ROOT from the WC
186 entry, we'll get it from the RA layer.*/
187 err
= wc_path_to_repos_urls(&path_or_url
, &repos_root
, &need_wc_cleanup
,
188 &adm_access
, path_or_url
, pool
);
193 /* If we weren't provided a REPOS_ROOT, or couldn't find one in the
194 WC entry, we'll ask the RA layer. */
195 if (repos_root
== NULL
)
197 if ((err
= svn_ra_get_repos_root2(ra_session
, &repos_root
, pool
)))
201 /* Check if PATH_OR_URL *is* the repository root URL. */
202 if (strcmp(repos_root
, path_or_url
) == 0)
204 *rel_path
= include_leading_slash
? "/" : "";
208 /* See if PATH_OR_URL is a child of REPOS_ROOT. If we get NULL
209 back from this, the two URLs have no commonality (which
210 should only happen if our caller provided us a REPOS_ROOT and
211 a PATH_OR_URL of something not in that repository). */
212 const char *rel_url
= svn_path_is_child(repos_root
, path_or_url
, pool
);
215 err
= svn_error_createf(SVN_ERR_CLIENT_UNRELATED_RESOURCES
, NULL
,
216 _("URL '%s' is not a child of repository "
218 path_or_url
, repos_root
);
221 rel_url
= svn_path_uri_decode(rel_url
, pool
);
222 *rel_path
= include_leading_slash
223 ? apr_pstrcat(pool
, "/", rel_url
, NULL
) : rel_url
;
229 svn_error_t
*err2
= svn_wc_adm_close(adm_access
);
233 svn_error_clear(err2
);
239 svn_client__get_repos_root(const char **repos_root
,
240 const char *path_or_url
,
241 const svn_opt_revision_t
*peg_revision
,
242 svn_wc_adm_access_t
*adm_access
,
243 svn_client_ctx_t
*ctx
,
247 const char *target_url
;
248 svn_boolean_t need_wc_cleanup
= FALSE
;
249 svn_error_t
*err
= SVN_NO_ERROR
;
250 apr_pool_t
*sesspool
= NULL
;
252 /* If PATH_OR_URL is a local path and PEG_REVISION keeps us looking
253 locally, we'll first check PATH_OR_URL's entry for a repository
255 if (!svn_path_is_url(path_or_url
)
256 && (peg_revision
->kind
== svn_opt_revision_working
257 || peg_revision
->kind
== svn_opt_revision_base
))
260 err
= wc_path_to_repos_urls(&path_or_url
, repos_root
, &need_wc_cleanup
,
261 &adm_access
, path_or_url
, pool
);
270 /* If PATH_OR_URL was a URL, or PEG_REVISION wasn't a client-side
271 revision, or we weren't otherwise able to find the repository
272 root URL in PATH_OR_URL's WC entry, we use the RA layer to look
274 if (*repos_root
== NULL
)
276 svn_ra_session_t
*ra_session
;
277 sesspool
= svn_pool_create(pool
);
278 if ((err
= svn_client__ra_session_from_path(&ra_session
,
289 if ((err
= svn_ra_get_repos_root2(ra_session
, repos_root
, pool
)))
295 svn_pool_destroy(sesspool
);
299 svn_error_t
*err2
= svn_wc_adm_close(adm_access
);
303 svn_error_clear(err2
);
310 svn_client__default_walker_error_handler(const char *path
,