1 |
/* |
2 |
* shelf.c: implementation of shelving |
3 |
* |
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 |
12 |
* |
13 |
* http://www.apache.org/licenses/LICENSE-2.0 |
14 |
* |
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 |
20 |
* under the License. |
21 |
* ==================================================================== |
22 |
*/ |
23 |
|
24 |
/* ==================================================================== */ |
25 |
|
26 |
/* We define this here to remove any further warnings about the usage of |
27 |
experimental functions in this file. */ |
28 |
#define SVN_EXPERIMENTAL |
29 |
|
30 |
#include "svn_client.h" |
31 |
#include "svn_wc.h" |
32 |
#include "svn_pools.h" |
33 |
#include "svn_dirent_uri.h" |
34 |
#include "svn_path.h" |
35 |
#include "svn_hash.h" |
36 |
#include "svn_utf.h" |
37 |
#include "svn_ctype.h" |
38 |
#include "svn_props.h" |
39 |
|
40 |
#include "client.h" |
41 |
#include "private/svn_client_shelf.h" |
42 |
#include "private/svn_client_private.h" |
43 |
#include "private/svn_wc_private.h" |
44 |
#include "private/svn_sorts_private.h" |
45 |
#include "svn_private_config.h" |
46 |
|
47 |
|
48 |
static svn_error_t * |
49 |
shelf_name_encode(char **encoded_name_p, |
50 |
const char *name, |
51 |
apr_pool_t *result_pool) |
52 |
{ |
53 |
char *encoded_name |
54 |
= apr_palloc(result_pool, strlen(name) * 2 + 1); |
55 |
char *out_pos = encoded_name; |
56 |
|
57 |
if (name[0] == '\0') |
58 |
return svn_error_create(SVN_ERR_BAD_CHANGELIST_NAME, NULL, |
59 |
_("Shelf name cannot be the empty string")); |
60 |
|
61 |
while (*name) |
62 |
{ |
63 |
apr_snprintf(out_pos, 3, "%02x", (unsigned char)(*name++)); |
64 |
out_pos += 2; |
65 |
} |
66 |
*encoded_name_p = encoded_name; |
67 |
return SVN_NO_ERROR; |
68 |
} |
69 |
|
70 |
static svn_error_t * |
71 |
shelf_name_decode(char **decoded_name_p, |
72 |
const char *codename, |
73 |
apr_pool_t *result_pool) |
74 |
{ |
75 |
svn_stringbuf_t *sb |
76 |
= svn_stringbuf_create_ensure(strlen(codename) / 2, result_pool); |
77 |
const char *input = codename; |
78 |
|
79 |
while (*input) |
80 |
{ |
81 |
int c; |
82 |
int nchars; |
83 |
int nitems = sscanf(input, "%02x%n", &c, &nchars); |
84 |
|
85 |
if (nitems != 1 || nchars != 2) |
86 |
return svn_error_createf(SVN_ERR_BAD_CHANGELIST_NAME, NULL, |
87 |
_("Shelve: Bad encoded name '%s'"), codename); |
88 |
svn_stringbuf_appendbyte(sb, c); |
89 |
input += 2; |
90 |
} |
91 |
*decoded_name_p = sb->data; |
92 |
return SVN_NO_ERROR; |
93 |
} |
94 |
|
95 |
/* Set *NAME to the shelf name from FILENAME, if FILENAME names a '.current' |
96 |
* file, else to NULL. */ |
97 |
static svn_error_t * |
98 |
shelf_name_from_filename(char **name, |
99 |
const char *filename, |
100 |
apr_pool_t *result_pool) |
101 |
{ |
102 |
size_t len = strlen(filename); |
103 |
static const char suffix[] = ".current"; |
104 |
size_t suffix_len = sizeof(suffix) - 1; |
105 |
|
106 |
if (len > suffix_len && strcmp(filename + len - suffix_len, suffix) == 0) |
107 |
{ |
108 |
char *codename = apr_pstrndup(result_pool, filename, len - suffix_len); |
109 |
SVN_ERR(shelf_name_decode(name, codename, result_pool)); |
110 |
} |
111 |
else |
112 |
{ |
113 |
*name = NULL; |
114 |
} |
115 |
return SVN_NO_ERROR; |
116 |
} |
117 |
|
118 |
/* Set *DIR to the shelf storage directory inside the WC's administrative |
119 |
* area. Ensure the directory exists. */ |
120 |
static svn_error_t * |
121 |
get_shelves_dir(char **dir, |
122 |
svn_wc_context_t *wc_ctx, |
123 |
const char *local_abspath, |
124 |
apr_pool_t *result_pool, |
125 |
apr_pool_t *scratch_pool) |
126 |
{ |
127 |
char *experimental_abspath; |
128 |
|
129 |
SVN_ERR(svn_wc__get_experimental_dir(&experimental_abspath, |
130 |
wc_ctx, local_abspath, |
131 |
scratch_pool, scratch_pool)); |
132 |
*dir = svn_dirent_join(experimental_abspath, "shelves/v3", result_pool); |
133 |
|
134 |
/* Ensure the directory exists. (Other versions of svn don't create it.) */ |
135 |
SVN_ERR(svn_io_make_dir_recursively(*dir, scratch_pool)); |
136 |
|
137 |
return SVN_NO_ERROR; |
138 |
} |
139 |
|
140 |
/* Set *ABSPATH to the abspath of the file storage dir for SHELF |
141 |
* version VERSION, no matter whether it exists. |
142 |
*/ |
143 |
static svn_error_t * |
144 |
shelf_version_files_dir_abspath(const char **abspath, |
145 |
svn_client__shelf_t *shelf, |
146 |
int version, |
147 |
apr_pool_t *result_pool, |
148 |
apr_pool_t *scratch_pool) |
149 |
{ |
150 |
char *codename; |
151 |
char *filename; |
152 |
|
153 |
SVN_ERR(shelf_name_encode(&codename, shelf->name, result_pool)); |
154 |
filename = apr_psprintf(scratch_pool, "%s-%03d.wc", codename, version); |
155 |
*abspath = svn_dirent_join(shelf->shelves_dir, filename, result_pool); |
156 |
return SVN_NO_ERROR; |
157 |
} |
158 |
|
159 |
/* Create a shelf-version object for a version that may or may not already |
160 |
* exist on disk. |
161 |
*/ |
162 |
static svn_error_t * |
163 |
shelf_version_create(svn_client__shelf_version_t **new_version_p, |
164 |
svn_client__shelf_t *shelf, |
165 |
int version_number, |
166 |
apr_pool_t *result_pool) |
167 |
{ |
168 |
svn_client__shelf_version_t *shelf_version |
169 |
= apr_pcalloc(result_pool, sizeof(*shelf_version)); |
170 |
|
171 |
shelf_version->shelf = shelf; |
172 |
shelf_version->version_number = version_number; |
173 |
SVN_ERR(shelf_version_files_dir_abspath(&shelf_version->files_dir_abspath, |
174 |
shelf, version_number, |
175 |
result_pool, result_pool)); |
176 |
*new_version_p = shelf_version; |
177 |
return SVN_NO_ERROR; |
178 |
} |
179 |
|
180 |
/* Delete the storage for SHELF:VERSION. */ |
181 |
static svn_error_t * |
182 |
shelf_version_delete(svn_client__shelf_t *shelf, |
183 |
int version, |
184 |
apr_pool_t *scratch_pool) |
185 |
{ |
186 |
const char *files_dir_abspath; |
187 |
|
188 |
SVN_ERR(shelf_version_files_dir_abspath(&files_dir_abspath, |
189 |
shelf, version, |
190 |
scratch_pool, scratch_pool)); |
191 |
SVN_ERR(svn_io_remove_dir2(files_dir_abspath, TRUE /*ignore_enoent*/, |
192 |
NULL, NULL, /*cancel*/ |
193 |
scratch_pool)); |
194 |
return SVN_NO_ERROR; |
195 |
} |
196 |
|
197 |
/* */ |
198 |
static svn_error_t * |
199 |
get_log_abspath(char **log_abspath, |
200 |
svn_client__shelf_t *shelf, |
201 |
apr_pool_t *result_pool, |
202 |
apr_pool_t *scratch_pool) |
203 |
{ |
204 |
char *codename; |
205 |
const char *filename; |
206 |
|
207 |
SVN_ERR(shelf_name_encode(&codename, shelf->name, result_pool)); |
208 |
filename = apr_pstrcat(scratch_pool, codename, ".log", SVN_VA_NULL); |
209 |
*log_abspath = svn_dirent_join(shelf->shelves_dir, filename, result_pool); |
210 |
return SVN_NO_ERROR; |
211 |
} |
212 |
|
213 |
/* Set SHELF->revprops by reading from its storage (the '.log' file). |
214 |
* Set SHELF->revprops to empty if the storage file does not exist; this |
215 |
* is not an error. |
216 |
*/ |
217 |
static svn_error_t * |
218 |
shelf_read_revprops(svn_client__shelf_t *shelf, |
219 |
apr_pool_t *scratch_pool) |
220 |
{ |
221 |
char *log_abspath; |
222 |
svn_error_t *err; |
223 |
svn_stream_t *stream; |
224 |
|
225 |
SVN_ERR(get_log_abspath(&log_abspath, shelf, scratch_pool, scratch_pool)); |
226 |
|
227 |
shelf->revprops = apr_hash_make(shelf->pool); |
228 |
err = svn_stream_open_readonly(&stream, log_abspath, |
229 |
scratch_pool, scratch_pool); |
230 |
if (err && APR_STATUS_IS_ENOENT(err->apr_err)) |
231 |
{ |
232 |
svn_error_clear(err); |
233 |
return SVN_NO_ERROR; |
234 |
} |
235 |
else |
236 |
SVN_ERR(err); |
237 |
SVN_ERR(svn_hash_read2(shelf->revprops, stream, "PROPS-END", shelf->pool)); |
238 |
SVN_ERR(svn_stream_close(stream)); |
239 |
return SVN_NO_ERROR; |
240 |
} |
241 |
|
242 |
/* Write SHELF's revprops to its file storage. |
243 |
*/ |
244 |
static svn_error_t * |
245 |
shelf_write_revprops(svn_client__shelf_t *shelf, |
246 |
apr_pool_t *scratch_pool) |
247 |
{ |
248 |
char *log_abspath; |
249 |
apr_file_t *file; |
250 |
svn_stream_t *stream; |
251 |
|
252 |
SVN_ERR(get_log_abspath(&log_abspath, shelf, scratch_pool, scratch_pool)); |
253 |
|
254 |
SVN_ERR(svn_io_file_open(&file, log_abspath, |
255 |
APR_FOPEN_WRITE | APR_FOPEN_CREATE | APR_FOPEN_TRUNCATE, |
256 |
APR_FPROT_OS_DEFAULT, scratch_pool)); |
257 |
stream = svn_stream_from_aprfile2(file, FALSE /*disown*/, scratch_pool); |
258 |
|
259 |
SVN_ERR(svn_hash_write2(shelf->revprops, stream, "PROPS-END", scratch_pool)); |
260 |
SVN_ERR(svn_stream_close(stream)); |
261 |
return SVN_NO_ERROR; |
262 |
} |
263 |
|
264 |
svn_error_t * |
265 |
svn_client__shelf_revprop_set(svn_client__shelf_t *shelf, |
266 |
const char *prop_name, |
267 |
const svn_string_t *prop_val, |
268 |
apr_pool_t *scratch_pool) |
269 |
{ |
270 |
svn_hash_sets(shelf->revprops, apr_pstrdup(shelf->pool, prop_name), |
271 |
svn_string_dup(prop_val, shelf->pool)); |
272 |
SVN_ERR(shelf_write_revprops(shelf, scratch_pool)); |
273 |
return SVN_NO_ERROR; |
274 |
} |
275 |
|
276 |
svn_error_t * |
277 |
svn_client__shelf_revprop_set_all(svn_client__shelf_t *shelf, |
278 |
apr_hash_t *revprop_table, |
279 |
apr_pool_t *scratch_pool) |
280 |
{ |
281 |
if (revprop_table) |
282 |
shelf->revprops = svn_prop_hash_dup(revprop_table, shelf->pool); |
283 |
else |
284 |
shelf->revprops = apr_hash_make(shelf->pool); |
285 |
|
286 |
SVN_ERR(shelf_write_revprops(shelf, scratch_pool)); |
287 |
return SVN_NO_ERROR; |
288 |
} |
289 |
|
290 |
svn_error_t * |
291 |
svn_client__shelf_revprop_get(svn_string_t **prop_val, |
292 |
svn_client__shelf_t *shelf, |
293 |
const char *prop_name, |
294 |
apr_pool_t *result_pool) |
295 |
{ |
296 |
*prop_val = svn_hash_gets(shelf->revprops, prop_name); |
297 |
return SVN_NO_ERROR; |
298 |
} |
299 |
|
300 |
svn_error_t * |
301 |
svn_client__shelf_revprop_list(apr_hash_t **props, |
302 |
svn_client__shelf_t *shelf, |
303 |
apr_pool_t *result_pool) |
304 |
{ |
305 |
*props = shelf->revprops; |
306 |
return SVN_NO_ERROR; |
307 |
} |
308 |
|
309 |
/* */ |
310 |
static svn_error_t * |
311 |
get_current_abspath(char **current_abspath, |
312 |
svn_client__shelf_t *shelf, |
313 |
apr_pool_t *result_pool) |
314 |
{ |
315 |
char *codename; |
316 |
char *filename; |
317 |
|
318 |
SVN_ERR(shelf_name_encode(&codename, shelf->name, result_pool)); |
319 |
filename = apr_psprintf(result_pool, "%s.current", codename); |
320 |
*current_abspath = svn_dirent_join(shelf->shelves_dir, filename, result_pool); |
321 |
return SVN_NO_ERROR; |
322 |
} |
323 |
|
324 |
/* Read SHELF->max_version from its storage (the '.current' file). |
325 |
* Set SHELF->max_version to -1 if that file does not exist. |
326 |
*/ |
327 |
static svn_error_t * |
328 |
shelf_read_current(svn_client__shelf_t *shelf, |
329 |
apr_pool_t *scratch_pool) |
330 |
{ |
331 |
char *current_abspath; |
332 |
svn_error_t *err; |
333 |
|
334 |
SVN_ERR(get_current_abspath(¤t_abspath, shelf, scratch_pool)); |
335 |
err = svn_io_read_version_file(&shelf->max_version, |
336 |
current_abspath, scratch_pool); |
337 |
if (err) |
338 |
{ |
339 |
shelf->max_version = -1; |
340 |
svn_error_clear(err); |
341 |
return SVN_NO_ERROR; |
342 |
} |
343 |
return SVN_NO_ERROR; |
344 |
} |
345 |
|
346 |
/* */ |
347 |
static svn_error_t * |
348 |
shelf_write_current(svn_client__shelf_t *shelf, |
349 |
apr_pool_t *scratch_pool) |
350 |
{ |
351 |
char *current_abspath; |
352 |
|
353 |
SVN_ERR(get_current_abspath(¤t_abspath, shelf, scratch_pool)); |
354 |
SVN_ERR(svn_io_write_version_file(current_abspath, shelf->max_version, |
355 |
scratch_pool)); |
356 |
return SVN_NO_ERROR; |
357 |
} |
358 |
|
359 |
/*-------------------------------------------------------------------------*/ |
360 |
/* Status Reporting */ |
361 |
|
362 |
/* Adjust a status STATUS_IN obtained from the shelf storage WC, to add |
363 |
* shelf-related metadata: |
364 |
* - changelist: 'svn:shelf:SHELFNAME' |
365 |
*/ |
366 |
static svn_error_t * |
367 |
status_augment(svn_wc_status3_t **status_p, |
368 |
const svn_wc_status3_t *status_in, |
369 |
svn_client__shelf_version_t *shelf_version, |
370 |
apr_pool_t *result_pool) |
371 |
{ |
372 |
*status_p = svn_wc_dup_status3(status_in, result_pool); |
373 |
(*status_p)->changelist = apr_psprintf(result_pool, "svn:shelf:%s", |
374 |
shelf_version->shelf->name); |
375 |
return SVN_NO_ERROR; |
376 |
} |
377 |
|
378 |
/* Read status from shelf storage. |
379 |
*/ |
380 |
static svn_error_t * |
381 |
status_read(svn_wc_status3_t **status, |
382 |
svn_client__shelf_version_t *shelf_version, |
383 |
const char *relpath, |
384 |
apr_pool_t *result_pool, |
385 |
apr_pool_t *scratch_pool) |
386 |
{ |
387 |
svn_client_ctx_t *ctx = shelf_version->shelf->ctx; |
388 |
char *abspath |
389 |
= svn_dirent_join(shelf_version->files_dir_abspath, relpath, |
390 |
scratch_pool); |
391 |
|
392 |
SVN_ERR(svn_wc_status3(status, ctx->wc_ctx, abspath, |
393 |
result_pool, scratch_pool)); |
394 |
SVN_ERR(status_augment(status, *status, shelf_version, result_pool)); |
395 |
return SVN_NO_ERROR; |
396 |
} |
397 |
|
398 |
/* A visitor function type for use with shelf_status_walk(). |
399 |
* The same as svn_wc_status_func4_t except relpath instead of abspath. |
400 |
*/ |
401 |
typedef svn_error_t *(*shelf_status_visitor_t)(void *baton, |
402 |
const char *relpath, |
403 |
const svn_wc_status3_t *status, |
404 |
apr_pool_t *scratch_pool); |
405 |
|
406 |
/* Baton for shelved_files_walk_visitor(). */ |
407 |
struct shelf_status_baton_t |
408 |
{ |
409 |
svn_client__shelf_version_t *shelf_version; |
410 |
shelf_status_visitor_t walk_func; |
411 |
void *walk_baton; |
412 |
}; |
413 |
|
414 |
/* Convert a svn_wc_status_func4_t callback invocation to call a |
415 |
* shelf_status_visitor_t callback. |
416 |
* |
417 |
* Call BATON->walk_func(BATON->walk_baton, relpath, ...) for the shelved |
418 |
* storage path ABSPATH, converting ABSPATH to a WC-relative path, and |
419 |
* augmenting the STATUS. |
420 |
* |
421 |
* The opposite of wc_status_visitor(). |
422 |
* |
423 |
* Implements svn_wc_status_func4_t. */ |
424 |
static svn_error_t * |
425 |
shelf_status_visitor(void *baton, |
426 |
const char *abspath, |
427 |
const svn_wc_status3_t *status, |
428 |
apr_pool_t *scratch_pool) |
429 |
{ |
430 |
struct shelf_status_baton_t *b = baton; |
431 |
const char *relpath; |
432 |
svn_wc_status3_t *new_status; |
433 |
|
434 |
relpath = svn_dirent_skip_ancestor(b->shelf_version->files_dir_abspath, |
435 |
abspath); |
436 |
SVN_ERR(status_augment(&new_status, status, b->shelf_version, scratch_pool)); |
437 |
SVN_ERR(b->walk_func(b->walk_baton, relpath, new_status, scratch_pool)); |
438 |
return SVN_NO_ERROR; |
439 |
} |
440 |
|
441 |
/* Report the shelved status of the path SHELF_VERSION:WC_RELPATH |
442 |
* via WALK_FUNC(WALK_BATON, ...). |
443 |
*/ |
444 |
static svn_error_t * |
445 |
shelf_status_visit_path(svn_client__shelf_version_t *shelf_version, |
446 |
const char *wc_relpath, |
447 |
shelf_status_visitor_t walk_func, |
448 |
void *walk_baton, |
449 |
apr_pool_t *scratch_pool) |
450 |
{ |
451 |
svn_wc_status3_t *status; |
452 |
|
453 |
SVN_ERR(status_read(&status, shelf_version, wc_relpath, |
454 |
scratch_pool, scratch_pool)); |
455 |
SVN_ERR(walk_func(walk_baton, wc_relpath, status, scratch_pool)); |
456 |
return SVN_NO_ERROR; |
457 |
} |
458 |
|
459 |
/* Report the shelved status of all the shelved paths in SHELF_VERSION |
460 |
* at and under WC_RELPATH, via WALK_FUNC(WALK_BATON, ...). |
461 |
*/ |
462 |
static svn_error_t * |
463 |
shelf_status_walk(svn_client__shelf_version_t *shelf_version, |
464 |
const char *wc_relpath, |
465 |
shelf_status_visitor_t walk_func, |
466 |
void *walk_baton, |
467 |
apr_pool_t *scratch_pool) |
468 |
{ |
469 |
svn_client_ctx_t *ctx = shelf_version->shelf->ctx; |
470 |
char *walk_root_abspath |
471 |
= svn_dirent_join(shelf_version->files_dir_abspath, wc_relpath, |
472 |
scratch_pool); |
473 |
struct shelf_status_baton_t baton; |
474 |
svn_error_t *err; |
475 |
|
476 |
baton.shelf_version = shelf_version; |
477 |
baton.walk_func = walk_func; |
478 |
baton.walk_baton = walk_baton; |
479 |
err = svn_wc_walk_status(ctx->wc_ctx, walk_root_abspath, |
480 |
svn_depth_infinity, |
481 |
FALSE /*get_all*/, |
482 |
TRUE /*no_ignore*/, |
483 |
FALSE /*ignore_text_mods*/, |
484 |
NULL /*ignore_patterns: use the defaults*/, |
485 |
shelf_status_visitor, &baton, |
486 |
NULL, NULL, /*cancellation*/ |
487 |
scratch_pool); |
488 |
if (err && APR_STATUS_IS_ENOENT(err->apr_err)) |
489 |
svn_error_clear(err); |
490 |
else |
491 |
SVN_ERR(err); |
492 |
|
493 |
return SVN_NO_ERROR; |
494 |
} |
495 |
|
496 |
/* Baton for wc_status_visitor(). */ |
497 |
typedef struct wc_status_baton_t |
498 |
{ |
499 |
svn_client__shelf_version_t *shelf_version; |
500 |
svn_wc_status_func4_t walk_func; |
501 |
void *walk_baton; |
502 |
} wc_status_baton_t; |
503 |
|
504 |
/* Convert a shelf_status_visitor_t callback invocation to call a |
505 |
* svn_wc_status_func4_t callback. |
506 |
* |
507 |
* Call BATON->walk_func(BATON->walk_baton, abspath, ...) for the WC- |
508 |
* relative path RELPATH, converting RELPATH to an abspath in the user's WC. |
509 |
* |
510 |
* The opposite of shelf_status_visitor(). |
511 |
* |
512 |
* Implements shelf_status_visitor_t. */ |
513 |
static svn_error_t * |
514 |
wc_status_visitor(void *baton, |
515 |
const char *relpath, |
516 |
const svn_wc_status3_t *status, |
517 |
apr_pool_t *scratch_pool) |
518 |
{ |
519 |
struct wc_status_baton_t *b = baton; |
520 |
svn_client__shelf_t *shelf = b->shelf_version->shelf; |
521 |
const char *abspath = svn_dirent_join(shelf->wc_root_abspath, relpath, |
522 |
scratch_pool); |
523 |
SVN_ERR(b->walk_func(b->walk_baton, abspath, status, scratch_pool)); |
524 |
return SVN_NO_ERROR; |
525 |
} |
526 |
|
527 |
svn_error_t * |
528 |
svn_client__shelf_version_status_walk(svn_client__shelf_version_t *shelf_version, |
529 |
const char *wc_relpath, |
530 |
svn_wc_status_func4_t walk_func, |
531 |
void *walk_baton, |
532 |
apr_pool_t *scratch_pool) |
533 |
{ |
534 |
wc_status_baton_t baton; |
535 |
|
536 |
baton.shelf_version = shelf_version; |
537 |
baton.walk_func = walk_func; |
538 |
baton.walk_baton = walk_baton; |
539 |
SVN_ERR(shelf_status_walk(shelf_version, wc_relpath, |
540 |
wc_status_visitor, &baton, |
541 |
scratch_pool)); |
542 |
return SVN_NO_ERROR; |
543 |
} |
544 |
|
545 |
/*-------------------------------------------------------------------------*/ |
546 |
/* Shelf Storage */ |
547 |
|
548 |
/* Construct a shelf object representing an empty shelf: no versions, |
549 |
* no revprops, no looking to see if such a shelf exists on disk. |
550 |
*/ |
551 |
static svn_error_t * |
552 |
shelf_construct(svn_client__shelf_t **shelf_p, |
553 |
const char *name, |
554 |
const char *local_abspath, |
555 |
svn_client_ctx_t *ctx, |
556 |
apr_pool_t *result_pool) |
557 |
{ |
558 |
svn_client__shelf_t *shelf = apr_palloc(result_pool, sizeof(*shelf)); |
559 |
char *shelves_dir; |
560 |
|
561 |
SVN_ERR(svn_client_get_wc_root(&shelf->wc_root_abspath, |
562 |
local_abspath, ctx, |
563 |
result_pool, result_pool)); |
564 |
SVN_ERR(get_shelves_dir(&shelves_dir, ctx->wc_ctx, local_abspath, |
565 |
result_pool, result_pool)); |
566 |
shelf->shelves_dir = shelves_dir; |
567 |
shelf->ctx = ctx; |
568 |
shelf->pool = result_pool; |
569 |
|
570 |
shelf->name = apr_pstrdup(result_pool, name); |
571 |
shelf->revprops = apr_hash_make(result_pool); |
572 |
shelf->max_version = 0; |
573 |
|
574 |
*shelf_p = shelf; |
575 |
return SVN_NO_ERROR; |
576 |
} |
577 |
|
578 |
svn_error_t * |
579 |
svn_client__shelf_open_existing(svn_client__shelf_t **shelf_p, |
580 |
const char *name, |
581 |
const char *local_abspath, |
582 |
svn_client_ctx_t *ctx, |
583 |
apr_pool_t *result_pool) |
584 |
{ |
585 |
SVN_ERR(shelf_construct(shelf_p, name, |
586 |
local_abspath, ctx, result_pool)); |
587 |
SVN_ERR(shelf_read_revprops(*shelf_p, result_pool)); |
588 |
SVN_ERR(shelf_read_current(*shelf_p, result_pool)); |
589 |
if ((*shelf_p)->max_version < 0) |
590 |
{ |
591 |
return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, |
592 |
_("Shelf '%s' not found"), |
593 |
name); |
594 |
} |
595 |
return SVN_NO_ERROR; |
596 |
} |
597 |
|
598 |
svn_error_t * |
599 |
svn_client__shelf_open_or_create(svn_client__shelf_t **shelf_p, |
600 |
const char *name, |
601 |
const char *local_abspath, |
602 |
svn_client_ctx_t *ctx, |
603 |
apr_pool_t *result_pool) |
604 |
{ |
605 |
svn_client__shelf_t *shelf; |
606 |
|
607 |
SVN_ERR(shelf_construct(&shelf, name, |
608 |
local_abspath, ctx, result_pool)); |
609 |
SVN_ERR(shelf_read_revprops(shelf, result_pool)); |
610 |
SVN_ERR(shelf_read_current(shelf, result_pool)); |
611 |
if (shelf->max_version < 0) |
612 |
{ |
613 |
shelf->max_version = 0; |
614 |
SVN_ERR(shelf_write_current(shelf, result_pool)); |
615 |
} |
616 |
*shelf_p = shelf; |
617 |
return SVN_NO_ERROR; |
618 |
} |
619 |
|
620 |
svn_error_t * |
621 |
svn_client__shelf_close(svn_client__shelf_t *shelf, |
622 |
apr_pool_t *scratch_pool) |
623 |
{ |
624 |
return SVN_NO_ERROR; |
625 |
} |
626 |
|
627 |
svn_error_t * |
628 |
svn_client__shelf_delete(const char *name, |
629 |
const char *local_abspath, |
630 |
svn_boolean_t dry_run, |
631 |
svn_client_ctx_t *ctx, |
632 |
apr_pool_t *scratch_pool) |
633 |
{ |
634 |
svn_client__shelf_t *shelf; |
635 |
int i; |
636 |
char *abspath; |
637 |
|
638 |
SVN_ERR(svn_client__shelf_open_existing(&shelf, name, |
639 |
local_abspath, ctx, scratch_pool)); |
640 |
|
641 |
/* Remove the versions. */ |
642 |
for (i = shelf->max_version; i > 0; i--) |
643 |
{ |
644 |
SVN_ERR(shelf_version_delete(shelf, i, scratch_pool)); |
645 |
} |
646 |
|
647 |
/* Remove the other files */ |
648 |
SVN_ERR(get_log_abspath(&abspath, shelf, scratch_pool, scratch_pool)); |
649 |
SVN_ERR(svn_io_remove_file2(abspath, TRUE /*ignore_enoent*/, scratch_pool)); |
650 |
SVN_ERR(get_current_abspath(&abspath, shelf, scratch_pool)); |
651 |
SVN_ERR(svn_io_remove_file2(abspath, TRUE /*ignore_enoent*/, scratch_pool)); |
652 |
|
653 |
SVN_ERR(svn_client__shelf_close(shelf, scratch_pool)); |
654 |
return SVN_NO_ERROR; |
655 |
} |
656 |
|
657 |
/* Baton for paths_changed_visitor(). */ |
658 |
struct paths_changed_walk_baton_t |
659 |
{ |
660 |
apr_hash_t *paths_hash; |
661 |
const char *wc_root_abspath; |
662 |
apr_pool_t *pool; |
663 |
}; |
664 |
|
665 |
/* Add to the list(s) in BATON, the RELPATH of a shelved 'binary' file. |
666 |
* Implements shelved_files_walk_func_t. */ |
667 |
static svn_error_t * |
668 |
paths_changed_visitor(void *baton, |
669 |
const char *relpath, |
670 |
const svn_wc_status3_t *s, |
671 |
apr_pool_t *scratch_pool) |
672 |
{ |
673 |
struct paths_changed_walk_baton_t *b = baton; |
674 |
|
675 |
relpath = apr_pstrdup(b->pool, relpath); |
676 |
svn_hash_sets(b->paths_hash, relpath, relpath); |
677 |
return SVN_NO_ERROR; |
678 |
} |
679 |
|
680 |
/* Get the paths changed, relative to WC root or as abspaths, as a hash |
681 |
* and/or an array (in no particular order). |
682 |
*/ |
683 |
static svn_error_t * |
684 |
shelf_paths_changed(apr_hash_t **paths_hash_p, |
685 |
apr_array_header_t **paths_array_p, |
686 |
svn_client__shelf_version_t *shelf_version, |
687 |
apr_pool_t *result_pool, |
688 |
apr_pool_t *scratch_pool) |
689 |
{ |
690 |
svn_client__shelf_t *shelf = shelf_version->shelf; |
691 |
apr_hash_t *paths_hash = apr_hash_make(result_pool); |
692 |
struct paths_changed_walk_baton_t baton; |
693 |
|
694 |
baton.paths_hash = paths_hash; |
695 |
baton.wc_root_abspath = shelf->wc_root_abspath; |
696 |
baton.pool = result_pool; |
697 |
SVN_ERR(shelf_status_walk(shelf_version, "", |
698 |
paths_changed_visitor, &baton, |
699 |
scratch_pool)); |
700 |
|
701 |
if (paths_hash_p) |
702 |
*paths_hash_p = paths_hash; |
703 |
if (paths_array_p) |
704 |
SVN_ERR(svn_hash_keys(paths_array_p, paths_hash, result_pool)); |
705 |
|
706 |
return SVN_NO_ERROR; |
707 |
} |
708 |
|
709 |
svn_error_t * |
710 |
svn_client__shelf_paths_changed(apr_hash_t **affected_paths, |
711 |
svn_client__shelf_version_t *shelf_version, |
712 |
apr_pool_t *result_pool, |
713 |
apr_pool_t *scratch_pool) |
714 |
{ |
715 |
SVN_ERR(shelf_paths_changed(affected_paths, NULL, shelf_version, |
716 |
result_pool, scratch_pool)); |
717 |
return SVN_NO_ERROR; |
718 |
} |
719 |
|
720 |
svn_error_t * |
721 |
svn_client__shelf_replay(svn_client__shelf_version_t *shelf_version, |
722 |
const char *top_relpath, |
723 |
const svn_delta_editor_t *editor, |
724 |
void *edit_baton, |
725 |
svn_wc_notify_func2_t notify_func, |
726 |
void *notify_baton, |
727 |
apr_pool_t *scratch_pool) |
728 |
{ |
729 |
svn_client_ctx_t *ctx = shelf_version->shelf->ctx; |
730 |
apr_array_header_t *src_targets = apr_array_make(scratch_pool, 1, |
731 |
sizeof(char *)); |
732 |
const char *src_wc_abspath |
733 |
= svn_dirent_join(shelf_version->files_dir_abspath, top_relpath, scratch_pool); |
734 |
|
735 |
APR_ARRAY_PUSH(src_targets, const char *) = src_wc_abspath; |
736 |
SVN_ERR(svn_client__wc_replay(src_wc_abspath, |
737 |
src_targets, svn_depth_infinity, NULL, |
738 |
editor, edit_baton, |
739 |
notify_func, notify_baton, |
740 |
ctx, scratch_pool)); |
741 |
return SVN_NO_ERROR; |
742 |
} |
743 |
|
744 |
/* Baton for test_apply_file_visitor(). */ |
745 |
struct test_apply_files_baton_t |
746 |
{ |
747 |
svn_client__shelf_version_t *shelf_version; |
748 |
svn_boolean_t conflict; /* would it conflict? */ |
749 |
svn_client_ctx_t *ctx; |
750 |
}; |
751 |
|
752 |
/* Ideally, set BATON->conflict if we can't apply a change to WC |
753 |
* at RELPATH without conflict. But in fact, just check |
754 |
* if WC at RELPATH is locally modified. |
755 |
* |
756 |
* Implements shelved_files_walk_func_t. */ |
757 |
static svn_error_t * |
758 |
test_apply_file_visitor(void *baton, |
759 |
const char *relpath, |
760 |
const svn_wc_status3_t *s, |
761 |
apr_pool_t *scratch_pool) |
762 |
{ |
763 |
struct test_apply_files_baton_t *b = baton; |
764 |
const char *wc_root_abspath = b->shelf_version->shelf->wc_root_abspath; |
765 |
const char *to_wc_abspath = svn_dirent_join(wc_root_abspath, relpath, |
766 |
scratch_pool); |
767 |
svn_wc_status3_t *status; |
768 |
|
769 |
SVN_ERR(svn_wc_status3(&status, b->ctx->wc_ctx, to_wc_abspath, |
770 |
scratch_pool, scratch_pool)); |
771 |
switch (status->node_status) |
772 |
{ |
773 |
case svn_wc_status_normal: |
774 |
case svn_wc_status_none: |
775 |
break; |
776 |
default: |
777 |
b->conflict = TRUE; |
778 |
} |
779 |
|
780 |
return SVN_NO_ERROR; |
781 |
} |
782 |
|
783 |
svn_error_t * |
784 |
svn_client__shelf_test_apply_file(svn_boolean_t *conflict_p, |
785 |
svn_client__shelf_version_t *shelf_version, |
786 |
const char *file_relpath, |
787 |
apr_pool_t *scratch_pool) |
788 |
{ |
789 |
struct test_apply_files_baton_t baton = {0}; |
790 |
|
791 |
baton.shelf_version = shelf_version; |
792 |
baton.conflict = FALSE; |
793 |
baton.ctx = shelf_version->shelf->ctx; |
794 |
SVN_ERR(shelf_status_visit_path(shelf_version, file_relpath, |
795 |
test_apply_file_visitor, &baton, |
796 |
scratch_pool)); |
797 |
*conflict_p = baton.conflict; |
798 |
|
799 |
return SVN_NO_ERROR; |
800 |
} |
801 |
|
802 |
static svn_error_t * |
803 |
wc_mods_editor(const svn_delta_editor_t **editor_p, |
804 |
void **edit_baton_p, |
805 |
const char *dst_wc_abspath, |
806 |
svn_wc_notify_func2_t notify_func, |
807 |
void *notify_baton, |
808 |
svn_client_ctx_t *ctx, |
809 |
apr_pool_t *result_pool, |
810 |
apr_pool_t *scratch_pool) |
811 |
{ |
812 |
svn_client__pathrev_t *base; |
813 |
const char *dst_wc_url; |
814 |
svn_ra_session_t *ra_session; |
815 |
|
816 |
/* We'll need an RA session to obtain the base of any copies */ |
817 |
SVN_ERR(svn_client__wc_node_get_base(&base, |
818 |
dst_wc_abspath, ctx->wc_ctx, |
819 |
scratch_pool, scratch_pool)); |
820 |
dst_wc_url = base->url; |
821 |
SVN_ERR(svn_client_open_ra_session2(&ra_session, |
822 |
dst_wc_url, dst_wc_abspath, |
823 |
ctx, result_pool, scratch_pool)); |
824 |
SVN_ERR(svn_client__wc_editor(editor_p, edit_baton_p, |
825 |
dst_wc_abspath, |
826 |
notify_func, notify_baton, |
827 |
ra_session, ctx, result_pool)); |
828 |
return SVN_NO_ERROR; |
829 |
} |
830 |
|
831 |
svn_error_t * |
832 |
svn_client__shelf_mods_editor(const svn_delta_editor_t **editor_p, |
833 |
void **edit_baton_p, |
834 |
svn_client__shelf_version_t *shelf_version, |
835 |
svn_wc_notify_func2_t notify_func, |
836 |
void *notify_baton, |
837 |
svn_client_ctx_t *ctx, |
838 |
apr_pool_t *result_pool) |
839 |
{ |
840 |
SVN_ERR(wc_mods_editor(editor_p, edit_baton_p, |
841 |
shelf_version->files_dir_abspath, |
842 |
notify_func, notify_baton, |
843 |
ctx, result_pool, result_pool)); |
844 |
return SVN_NO_ERROR; |
845 |
} |
846 |
|
847 |
svn_error_t * |
848 |
svn_client__shelf_apply(svn_client__shelf_version_t *shelf_version, |
849 |
svn_boolean_t dry_run, |
850 |
apr_pool_t *scratch_pool) |
851 |
{ |
852 |
svn_client__shelf_t *shelf = shelf_version->shelf; |
853 |
const svn_delta_editor_t *editor; |
854 |
void *edit_baton; |
855 |
|
856 |
SVN_ERR(wc_mods_editor(&editor, &edit_baton, |
857 |
shelf->wc_root_abspath, |
858 |
NULL, NULL, /*notification*/ |
859 |
shelf->ctx, scratch_pool, scratch_pool)); |
860 |
|
861 |
SVN_ERR(svn_client__shelf_replay(shelf_version, "", |
862 |
editor, edit_baton, |
863 |
shelf->ctx->notify_func2, shelf->ctx->notify_baton2, |
864 |
scratch_pool)); |
865 |
|
866 |
svn_io_sleep_for_timestamps(shelf->wc_root_abspath, |
867 |
scratch_pool); |
868 |
return SVN_NO_ERROR; |
869 |
} |
870 |
|
871 |
/* Baton for paths_changed_visitor(). */ |
872 |
struct unapply_walk_baton_t |
873 |
{ |
874 |
const char *wc_root_abspath; |
875 |
svn_boolean_t dry_run; |
876 |
svn_boolean_t use_commit_times; |
877 |
svn_client_ctx_t *ctx; |
878 |
apr_pool_t *pool; |
879 |
}; |
880 |
|
881 |
/* Revert the change at RELPATH in the user's WC. |
882 |
* Implements shelved_files_walk_func_t. */ |
883 |
static svn_error_t * |
884 |
unapply_visitor(void *baton, |
885 |
const char *relpath, |
886 |
const svn_wc_status3_t *s, |
887 |
apr_pool_t *scratch_pool) |
888 |
{ |
889 |
struct unapply_walk_baton_t *b = baton; |
890 |
const char *abspath = svn_dirent_join(b->wc_root_abspath, relpath, |
891 |
scratch_pool); |
892 |
|
893 |
if (!b->dry_run) |
894 |
{ |
895 |
apr_array_header_t *targets |
896 |
= apr_array_make(scratch_pool, 1, sizeof(char *)); |
897 |
svn_depth_t depth; |
898 |
|
899 |
APR_ARRAY_PUSH(targets, const char *) = abspath; |
900 |
|
901 |
/* If the local modification is a "delete" then revert it all |
902 |
(recursively). Otherwise we'd have to walk paths in |
903 |
top-down order to revert a delete, whereas we need bottom-up |
904 |
order to revert children of an added directory. */ |
905 |
if (s->node_status == svn_wc_status_deleted |
906 |
|| s->node_status == svn_wc_status_replaced |
907 |
|| s->node_status == svn_wc_status_added) |
908 |
depth = svn_depth_infinity; |
909 |
else |
910 |
depth = svn_depth_empty; |
911 |
SVN_ERR(svn_wc_revert6(b->ctx->wc_ctx, |
912 |
abspath, |
913 |
depth, |
914 |
b->use_commit_times, |
915 |
NULL /*changelists*/, |
916 |
FALSE /*clear_changelists*/, |
917 |
FALSE /*metadata_only*/, |
918 |
FALSE /*added_keep_local*/, |
919 |
b->ctx->cancel_func, b->ctx->cancel_baton, |
920 |
NULL, NULL, /*notification*/ |
921 |
scratch_pool)); |
922 |
} |
923 |
return SVN_NO_ERROR; |
924 |
} |
925 |
|
926 |
svn_error_t * |
927 |
svn_client__shelf_unapply(svn_client__shelf_version_t *shelf_version, |
928 |
svn_boolean_t dry_run, |
929 |
apr_pool_t *scratch_pool) |
930 |
{ |
931 |
svn_client_ctx_t *ctx = shelf_version->shelf->ctx; |
932 |
svn_client__shelf_t *shelf = shelf_version->shelf; |
933 |
struct unapply_walk_baton_t baton; |
934 |
svn_config_t *cfg; |
935 |
|
936 |
baton.wc_root_abspath = shelf->wc_root_abspath; |
937 |
baton.dry_run = dry_run; |
938 |
baton.ctx = ctx; |
939 |
baton.pool = scratch_pool; |
940 |
|
941 |
cfg = ctx->config ? svn_hash_gets(ctx->config, SVN_CONFIG_CATEGORY_CONFIG) |
942 |
: NULL; |
943 |
SVN_ERR(svn_config_get_bool(cfg, &baton.use_commit_times, |
944 |
SVN_CONFIG_SECTION_MISCELLANY, |
945 |
SVN_CONFIG_OPTION_USE_COMMIT_TIMES, FALSE)); |
946 |
|
947 |
SVN_WC__CALL_WITH_WRITE_LOCK( |
948 |
shelf_status_walk(shelf_version, "", |
949 |
unapply_visitor, &baton, |
950 |
scratch_pool), |
951 |
ctx->wc_ctx, shelf_version->shelf->wc_root_abspath, |
952 |
FALSE /*lock_anchor*/, scratch_pool); |
953 |
return SVN_NO_ERROR; |
954 |
} |
955 |
|
956 |
svn_error_t * |
957 |
svn_client__shelf_delete_newer_versions(svn_client__shelf_t *shelf, |
958 |
svn_client__shelf_version_t *shelf_version, |
959 |
apr_pool_t *scratch_pool) |
960 |
{ |
961 |
int previous_version = shelf_version ? shelf_version->version_number : 0; |
962 |
int i; |
963 |
|
964 |
/* Delete any newer checkpoints */ |
965 |
for (i = shelf->max_version; i > previous_version; i--) |
966 |
{ |
967 |
SVN_ERR(shelf_version_delete(shelf, i, scratch_pool)); |
968 |
} |
969 |
|
970 |
shelf->max_version = previous_version; |
971 |
SVN_ERR(shelf_write_current(shelf, scratch_pool)); |
972 |
return SVN_NO_ERROR; |
973 |
} |
974 |
|
975 |
svn_error_t * |
976 |
svn_client__shelf_diff(svn_client__shelf_version_t *shelf_version, |
977 |
const char *shelf_relpath, |
978 |
svn_depth_t depth, |
979 |
svn_boolean_t ignore_ancestry, |
980 |
const svn_diff_tree_processor_t *diff_processor, |
981 |
apr_pool_t *scratch_pool) |
982 |
{ |
983 |
svn_client_ctx_t *ctx = shelf_version->shelf->ctx; |
984 |
char *local_abspath |
985 |
= svn_dirent_join(shelf_version->files_dir_abspath, shelf_relpath, |
986 |
scratch_pool); |
987 |
|
988 |
if (shelf_version->version_number == 0) |
989 |
return SVN_NO_ERROR; |
990 |
|
991 |
SVN_ERR(svn_wc__diff7(FALSE /*anchor_at_given_paths*/, |
992 |
ctx->wc_ctx, local_abspath, |
993 |
depth, |
994 |
ignore_ancestry, |
995 |
NULL /*changelists*/, |
996 |
diff_processor, |
997 |
NULL, NULL, /*cancellation*/ |
998 |
scratch_pool, scratch_pool)); |
999 |
return SVN_NO_ERROR; |
1000 |
} |
1001 |
|
1002 |
/* Populate the storage a new shelf-version object NEW_SHELF_VERSION, |
1003 |
* by creating a shelf storage WC with its base state copied from the |
1004 |
* 'real' WC. |
1005 |
*/ |
1006 |
static svn_error_t * |
1007 |
shelf_copy_base(svn_client__shelf_version_t *new_shelf_version, |
1008 |
apr_pool_t *scratch_pool) |
1009 |
{ |
1010 |
svn_client_ctx_t *ctx = new_shelf_version->shelf->ctx; |
1011 |
const char *users_wc_abspath = new_shelf_version->shelf->wc_root_abspath; |
1012 |
svn_client__pathrev_t *users_wc_root_base; |
1013 |
svn_opt_revision_t users_wc_root_rev; |
1014 |
svn_ra_session_t *ra_session = NULL; |
1015 |
svn_boolean_t sleep_here = FALSE; |
1016 |
|
1017 |
SVN_ERR(svn_client__wc_node_get_base(&users_wc_root_base, |
1018 |
users_wc_abspath, ctx->wc_ctx, |
1019 |
scratch_pool, scratch_pool)); |
1020 |
|
1021 |
/* ### We need to read and recreate the mixed-rev, switched-URL, |
1022 |
mixed-depth WC state; but for a rough start we'll just use |
1023 |
HEAD, unswitched, depth-infinity. */ |
1024 |
users_wc_root_rev.kind = svn_opt_revision_head; |
1025 |
|
1026 |
/* ### TODO: Create an RA session that reads from the user's WC. |
1027 |
For a rough start, we'll just let 'checkout' read from the repo. */ |
1028 |
|
1029 |
SVN_ERR(svn_client__checkout_internal(NULL /*result_rev*/, &sleep_here, |
1030 |
users_wc_root_base->url, |
1031 |
new_shelf_version->files_dir_abspath, |
1032 |
&users_wc_root_rev, &users_wc_root_rev, |
1033 |
svn_depth_infinity, |
1034 |
TRUE /*ignore_externals*/, |
1035 |
FALSE /*allow_unver_obstructions*/, |
1036 |
ra_session, |
1037 |
ctx, scratch_pool)); |
1038 |
/* ### hopefully we won't eventually need to sleep_here... */ |
1039 |
if (sleep_here) |
1040 |
svn_io_sleep_for_timestamps(new_shelf_version->files_dir_abspath, |
1041 |
scratch_pool); |
1042 |
return SVN_NO_ERROR; |
1043 |
} |
1044 |
|
1045 |
/* */ |
1046 |
struct shelf_save_notifer_baton_t |
1047 |
{ |
1048 |
svn_client__shelf_version_t *shelf_version; |
1049 |
svn_wc_notify_func2_t notify_func; |
1050 |
void *notify_baton; |
1051 |
svn_client_status_func_t shelved_func; |
1052 |
void *shelved_baton; |
1053 |
svn_boolean_t any_shelved; |
1054 |
}; |
1055 |
|
1056 |
/* */ |
1057 |
static void |
1058 |
shelf_save_notifier(void *baton, |
1059 |
const svn_wc_notify_t *notify, |
1060 |
apr_pool_t *pool) |
1061 |
{ |
1062 |
struct shelf_save_notifer_baton_t *nb = baton; |
1063 |
const char *wc_relpath |
1064 |
= svn_dirent_skip_ancestor(nb->shelf_version->shelf->wc_root_abspath, |
1065 |
notify->path); |
1066 |
svn_client_status_t *cst = NULL; |
1067 |
#if 0 |
1068 |
svn_wc_status3_t *wc_status; |
1069 |
|
1070 |
svn_error_clear(status_read(&wc_status, nb->shelf_version, wc_relpath, |
1071 |
pool, pool)); |
1072 |
svn_error_clear(svn_client__create_status( |
1073 |
&cst, nb->shelf_version->shelf->ctx->wc_ctx, |
1074 |
notify->path, wc_status, pool, pool)); |
1075 |
#endif |
1076 |
svn_error_clear(nb->shelved_func(nb->shelved_baton, wc_relpath, cst, pool)); |
1077 |
nb->any_shelved = TRUE; |
1078 |
|
1079 |
nb->notify_func(nb->notify_baton, notify, pool); |
1080 |
} |
1081 |
|
1082 |
svn_error_t * |
1083 |
svn_client__shelf_save_new_version3(svn_client__shelf_version_t **new_version_p, |
1084 |
svn_client__shelf_t *shelf, |
1085 |
const apr_array_header_t *paths, |
1086 |
svn_depth_t depth, |
1087 |
const apr_array_header_t *changelists, |
1088 |
svn_client_status_func_t shelved_func, |
1089 |
void *shelved_baton, |
1090 |
svn_client_status_func_t not_shelved_func, |
1091 |
void *not_shelved_baton, |
1092 |
apr_pool_t *scratch_pool) |
1093 |
{ |
1094 |
svn_client_ctx_t *ctx = shelf->ctx; |
1095 |
int next_version = shelf->max_version + 1; |
1096 |
svn_client__shelf_version_t *new_shelf_version; |
1097 |
struct shelf_save_notifer_baton_t nb; |
1098 |
const svn_delta_editor_t *editor; |
1099 |
void *edit_baton; |
1100 |
|
1101 |
SVN_ERR(shelf_version_create(&new_shelf_version, |
1102 |
shelf, next_version, scratch_pool)); |
1103 |
SVN_ERR(shelf_copy_base(new_shelf_version, scratch_pool)); |
1104 |
|
1105 |
nb.shelf_version = new_shelf_version; |
1106 |
nb.notify_func = ctx->notify_func2; |
1107 |
nb.notify_baton = ctx->notify_baton2; |
1108 |
nb.shelved_func = shelved_func; |
1109 |
nb.shelved_baton = shelved_baton; |
1110 |
nb.any_shelved = FALSE; |
1111 |
SVN_ERR(svn_client__shelf_mods_editor(&editor, &edit_baton, |
1112 |
new_shelf_version, |
1113 |
NULL, NULL, /*notification*/ |
1114 |
ctx, scratch_pool)); |
1115 |
SVN_ERR(svn_client__wc_replay(shelf->wc_root_abspath, |
1116 |
paths, depth, changelists, |
1117 |
editor, edit_baton, |
1118 |
shelf_save_notifier, &nb, |
1119 |
ctx, scratch_pool)); |
1120 |
|
1121 |
if (nb.any_shelved) |
1122 |
{ |
1123 |
shelf->max_version = next_version; |
1124 |
SVN_ERR(shelf_write_current(shelf, scratch_pool)); |
1125 |
|
1126 |
if (new_version_p) |
1127 |
SVN_ERR(svn_client__shelf_version_open(new_version_p, shelf, next_version, |
1128 |
scratch_pool, scratch_pool)); |
1129 |
} |
1130 |
else |
1131 |
{ |
1132 |
if (new_version_p) |
1133 |
*new_version_p = NULL; |
1134 |
} |
1135 |
return SVN_NO_ERROR; |
1136 |
} |
1137 |
|
1138 |
svn_error_t * |
1139 |
svn_client__shelf_get_log_message(char **log_message, |
1140 |
svn_client__shelf_t *shelf, |
1141 |
apr_pool_t *result_pool) |
1142 |
{ |
1143 |
svn_string_t *propval = svn_hash_gets(shelf->revprops, SVN_PROP_REVISION_LOG); |
1144 |
|
1145 |
if (propval) |
1146 |
*log_message = apr_pstrdup(result_pool, propval->data); |
1147 |
else |
1148 |
*log_message = NULL; |
1149 |
return SVN_NO_ERROR; |
1150 |
} |
1151 |
|
1152 |
svn_error_t * |
1153 |
svn_client__shelf_set_log_message(svn_client__shelf_t *shelf, |
1154 |
const char *message, |
1155 |
apr_pool_t *scratch_pool) |
1156 |
{ |
1157 |
svn_string_t *propval |
1158 |
= message ? svn_string_create(message, shelf->pool) : NULL; |
1159 |
|
1160 |
SVN_ERR(svn_client__shelf_revprop_set(shelf, SVN_PROP_REVISION_LOG, propval, |
1161 |
scratch_pool)); |
1162 |
return SVN_NO_ERROR; |
1163 |
} |
1164 |
|
1165 |
svn_error_t * |
1166 |
svn_client__shelf_list(apr_hash_t **shelf_infos, |
1167 |
const char *local_abspath, |
1168 |
svn_client_ctx_t *ctx, |
1169 |
apr_pool_t *result_pool, |
1170 |
apr_pool_t *scratch_pool) |
1171 |
{ |
1172 |
const char *wc_root_abspath; |
1173 |
char *shelves_dir; |
1174 |
apr_hash_t *dirents; |
1175 |
apr_hash_index_t *hi; |
1176 |
|
1177 |
SVN_ERR(svn_wc__get_wcroot(&wc_root_abspath, ctx->wc_ctx, local_abspath, |
1178 |
scratch_pool, scratch_pool)); |
1179 |
SVN_ERR(get_shelves_dir(&shelves_dir, ctx->wc_ctx, local_abspath, |
1180 |
scratch_pool, scratch_pool)); |
1181 |
SVN_ERR(svn_io_get_dirents3(&dirents, shelves_dir, FALSE /*only_check_type*/, |
1182 |
result_pool, scratch_pool)); |
1183 |
|
1184 |
*shelf_infos = apr_hash_make(result_pool); |
1185 |
|
1186 |
/* Remove non-shelves */ |
1187 |
for (hi = apr_hash_first(scratch_pool, dirents); hi; hi = apr_hash_next(hi)) |
1188 |
{ |
1189 |
const char *filename = apr_hash_this_key(hi); |
1190 |
svn_io_dirent2_t *dirent = apr_hash_this_val(hi); |
1191 |
char *name = NULL; |
1192 |
|
1193 |
svn_error_clear(shelf_name_from_filename(&name, filename, result_pool)); |
1194 |
if (name && dirent->kind == svn_node_file) |
1195 |
{ |
1196 |
svn_client__shelf_info_t *info |
1197 |
= apr_palloc(result_pool, sizeof(*info)); |
1198 |
|
1199 |
info->mtime = dirent->mtime; |
1200 |
svn_hash_sets(*shelf_infos, name, info); |
1201 |
} |
1202 |
} |
1203 |
|
1204 |
return SVN_NO_ERROR; |
1205 |
} |
1206 |
|
1207 |
svn_error_t * |
1208 |
svn_client__shelf_version_open(svn_client__shelf_version_t **shelf_version_p, |
1209 |
svn_client__shelf_t *shelf, |
1210 |
int version_number, |
1211 |
apr_pool_t *result_pool, |
1212 |
apr_pool_t *scratch_pool) |
1213 |
{ |
1214 |
svn_client__shelf_version_t *shelf_version; |
1215 |
const svn_io_dirent2_t *dirent; |
1216 |
|
1217 |
SVN_ERR(shelf_version_create(&shelf_version, |
1218 |
shelf, version_number, result_pool)); |
1219 |
SVN_ERR(svn_io_stat_dirent2(&dirent, |
1220 |
shelf_version->files_dir_abspath, |
1221 |
FALSE /*verify_truename*/, |
1222 |
TRUE /*ignore_enoent*/, |
1223 |
result_pool, scratch_pool)); |
1224 |
if (dirent->kind == svn_node_none) |
1225 |
{ |
1226 |
return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL, |
1227 |
_("Shelf '%s' version %d not found"), |
1228 |
shelf->name, version_number); |
1229 |
} |
1230 |
shelf_version->mtime = dirent->mtime; |
1231 |
*shelf_version_p = shelf_version; |
1232 |
return SVN_NO_ERROR; |
1233 |
} |
1234 |
|
1235 |
svn_error_t * |
1236 |
svn_client__shelf_get_newest_version(svn_client__shelf_version_t **shelf_version_p, |
1237 |
svn_client__shelf_t *shelf, |
1238 |
apr_pool_t *result_pool, |
1239 |
apr_pool_t *scratch_pool) |
1240 |
{ |
1241 |
if (shelf->max_version == 0) |
1242 |
{ |
1243 |
*shelf_version_p = NULL; |
1244 |
return SVN_NO_ERROR; |
1245 |
} |
1246 |
|
1247 |
SVN_ERR(svn_client__shelf_version_open(shelf_version_p, |
1248 |
shelf, shelf->max_version, |
1249 |
result_pool, scratch_pool)); |
1250 |
return SVN_NO_ERROR; |
1251 |
} |
1252 |
|
1253 |
svn_error_t * |
1254 |
svn_client__shelf_get_all_versions(apr_array_header_t **versions_p, |
1255 |
svn_client__shelf_t *shelf, |
1256 |
apr_pool_t *result_pool, |
1257 |
apr_pool_t *scratch_pool) |
1258 |
{ |
1259 |
int i; |
1260 |
|
1261 |
*versions_p = apr_array_make(result_pool, shelf->max_version - 1, |
1262 |
sizeof(svn_client__shelf_version_t *)); |
1263 |
|
1264 |
for (i = 1; i <= shelf->max_version; i++) |
1265 |
{ |
1266 |
svn_client__shelf_version_t *shelf_version; |
1267 |
|
1268 |
SVN_ERR(svn_client__shelf_version_open(&shelf_version, |
1269 |
shelf, i, |
1270 |
result_pool, scratch_pool)); |
1271 |
APR_ARRAY_PUSH(*versions_p, svn_client__shelf_version_t *) = shelf_version; |
1272 |
} |
1273 |
return SVN_NO_ERROR; |
1274 |
} |