]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/subversion/subversion/svn/shelve-cmd.c
Update svn-1.9.7 to 1.10.0.
[FreeBSD/FreeBSD.git] / contrib / subversion / subversion / svn / shelve-cmd.c
1 /*
2  * shelve-cmd.c -- Shelve commands.
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 /* We define this here to remove any further warnings about the usage of
25    experimental functions in this file. */
26 #define SVN_EXPERIMENTAL
27
28 #include "svn_client.h"
29 #include "svn_error_codes.h"
30 #include "svn_error.h"
31 #include "svn_path.h"
32 #include "svn_utf.h"
33
34 #include "cl.h"
35
36 #include "svn_private_config.h"
37 #include "private/svn_sorts_private.h"
38
39
40 /* First argument should be the name of a shelved change. */
41 static svn_error_t *
42 get_name(const char **name,
43          apr_getopt_t *os,
44          apr_pool_t *result_pool,
45          apr_pool_t *scratch_pool)
46 {
47   apr_array_header_t *args;
48
49   SVN_ERR(svn_opt_parse_num_args(&args, os, 1, scratch_pool));
50   SVN_ERR(svn_utf_cstring_to_utf8(name,
51                                   APR_ARRAY_IDX(args, 0, const char *),
52                                   result_pool));
53   return SVN_NO_ERROR;
54 }
55
56 /* A comparison function for svn_sort__hash(), comparing the mtime of two
57    svn_client_shelved_patch_info_t's. */
58 static int
59 compare_shelved_patch_infos_by_mtime(const svn_sort__item_t *a,
60                                      const svn_sort__item_t *b)
61 {
62   svn_client_shelved_patch_info_t *a_val = a->value;
63   svn_client_shelved_patch_info_t *b_val = b->value;
64
65   return (a_val->dirent->mtime < b_val->dirent->mtime)
66            ? -1 : (a_val->dirent->mtime > b_val->dirent->mtime) ? 1 : 0;
67 }
68
69 /* Return a list of shelved changes sorted by patch file mtime, oldest first.
70  */
71 static svn_error_t *
72 list_sorted_by_date(apr_array_header_t **list,
73                     const char *local_abspath,
74                     svn_client_ctx_t *ctx,
75                     apr_pool_t *scratch_pool)
76 {
77   apr_hash_t *shelved_patch_infos;
78
79   SVN_ERR(svn_client_shelves_list(&shelved_patch_infos, local_abspath,
80                                   ctx, scratch_pool, scratch_pool));
81   *list = svn_sort__hash(shelved_patch_infos,
82                          compare_shelved_patch_infos_by_mtime,
83                          scratch_pool);
84   return SVN_NO_ERROR;
85 }
86
87 #ifndef WIN32
88 /* Run CMD with ARGS.
89  * Send its stdout to the parent's stdout. Disconnect its stdin and stderr.
90  */
91 static svn_error_t *
92 run_cmd(const char *cmd,
93         const char *const *args,
94         apr_pool_t *scratch_pool)
95 {
96   apr_status_t apr_err;
97   apr_file_t *outfile;
98   svn_error_t *err;
99   int exitcode;
100
101   apr_err = apr_file_open_stdout(&outfile, scratch_pool);
102   if (apr_err)
103     return svn_error_wrap_apr(apr_err, "Can't open stdout");
104
105   err = svn_io_run_cmd(NULL /*path*/, cmd, args,
106                        &exitcode, NULL /*exitwhy*/,
107                        TRUE /*inherit*/,
108                        NULL /*infile*/, outfile, NULL /*errfile*/,
109                        scratch_pool);
110   if (err || exitcode)
111     return svn_error_createf(SVN_ERR_EXTERNAL_PROGRAM, err,
112                              _("Could not run external command '%s'"), cmd);
113   return SVN_NO_ERROR;
114 }
115 #endif
116
117 /* Display a list of shelved changes */
118 static svn_error_t *
119 shelves_list(const char *local_abspath,
120              svn_boolean_t diffstat,
121              svn_client_ctx_t *ctx,
122              apr_pool_t *scratch_pool)
123 {
124   apr_array_header_t *list;
125   int i;
126
127   SVN_ERR(list_sorted_by_date(&list,
128                               local_abspath, ctx, scratch_pool));
129
130   for (i = 0; i < list->nelts; i++)
131     {
132       const svn_sort__item_t *item = &APR_ARRAY_IDX(list, i, svn_sort__item_t);
133       const char *name = item->key;
134       svn_client_shelved_patch_info_t *info = item->value;
135       int age = (int)((apr_time_now() - info->mtime) / 1000000 / 60);
136       apr_hash_t *paths;
137
138       SVN_ERR(svn_client_shelf_get_paths(&paths,
139                                          name, local_abspath, ctx,
140                                          scratch_pool, scratch_pool));
141
142       SVN_ERR(svn_cmdline_printf(scratch_pool,
143                                  _("%-30s %6d mins old %10ld bytes %4d paths changed\n"),
144                                  name, age, (long)info->dirent->filesize,
145                                  apr_hash_count(paths)));
146       SVN_ERR(svn_cmdline_printf(scratch_pool,
147                                  _(" %.50s\n"),
148                                  info->message));
149
150       if (diffstat)
151         {
152 #ifndef WIN32
153           const char *args[4];
154           svn_error_t *err;
155
156           args[0] = "diffstat";
157           args[1] = "-p0";
158           args[2] = info->patch_path;
159           args[3] = NULL;
160           err = run_cmd("diffstat", args, scratch_pool);
161           if (err)
162             svn_error_clear(err);
163           else
164             SVN_ERR(svn_cmdline_printf(scratch_pool,
165                                        "\n"));
166 #endif
167         }
168     }
169
170   return SVN_NO_ERROR;
171 }
172
173 /* Find the name of the youngest shelved change.
174  */
175 static svn_error_t *
176 name_of_youngest(const char **name_p,
177                  const char *local_abspath,
178                  svn_client_ctx_t *ctx,
179                  apr_pool_t *result_pool,
180                  apr_pool_t *scratch_pool)
181 {
182   apr_array_header_t *list;
183   const svn_sort__item_t *youngest_item;
184
185   SVN_ERR(list_sorted_by_date(&list,
186                               local_abspath, ctx, scratch_pool));
187   if (list->nelts == 0)
188     return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, NULL,
189                             _("No shelved changes found"));
190
191   youngest_item = &APR_ARRAY_IDX(list, list->nelts - 1, svn_sort__item_t);
192   *name_p = youngest_item->key;
193   return SVN_NO_ERROR;
194 }
195
196 /* This implements the `svn_opt_subcommand_t' interface. */
197 svn_error_t *
198 svn_cl__shelve(apr_getopt_t *os,
199                void *baton,
200                apr_pool_t *pool)
201 {
202   svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
203   svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
204   const char *local_abspath;
205   const char *name;
206   apr_array_header_t *targets;
207   svn_boolean_t has_changes;
208
209   if (opt_state->quiet)
210     ctx->notify_func2 = NULL; /* Easy out: avoid unneeded work */
211
212   SVN_ERR(svn_dirent_get_absolute(&local_abspath, "", pool));
213
214   if (opt_state->list)
215     {
216       if (os->ind < os->argc)
217         return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, NULL);
218
219       SVN_ERR(shelves_list(local_abspath,
220                            ! opt_state->quiet /*diffstat*/,
221                            ctx, pool));
222       return SVN_NO_ERROR;
223     }
224
225   SVN_ERR(get_name(&name, os, pool, pool));
226
227   if (opt_state->remove)
228     {
229       if (os->ind < os->argc)
230         return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, NULL);
231
232       SVN_ERR(svn_client_shelves_delete(name, local_abspath,
233                                         opt_state->dry_run,
234                                         ctx, pool));
235       if (! opt_state->quiet)
236         SVN_ERR(svn_cmdline_printf(pool, "deleted '%s'\n", name));
237       return SVN_NO_ERROR;
238     }
239
240   /* Parse the remaining arguments as paths. */
241   SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
242                                                       opt_state->targets,
243                                                       ctx, FALSE, pool));
244   svn_opt_push_implicit_dot_target(targets, pool);
245
246   {
247       svn_depth_t depth = opt_state->depth;
248       svn_error_t *err;
249
250       /* shelve has no implicit dot-target `.', so don't you put that
251          code here! */
252       if (!targets->nelts)
253         return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);
254
255       SVN_ERR(svn_cl__check_targets_are_local_paths(targets));
256
257       if (depth == svn_depth_unknown)
258         depth = svn_depth_infinity;
259
260       SVN_ERR(svn_cl__eat_peg_revisions(&targets, targets, pool));
261
262       if (ctx->log_msg_func3)
263         SVN_ERR(svn_cl__make_log_msg_baton(&ctx->log_msg_baton3,
264                                            opt_state, NULL, ctx->config,
265                                            pool));
266       err = svn_client_shelve(name,
267                               targets, depth, opt_state->changelists,
268                               opt_state->keep_local, opt_state->dry_run,
269                               ctx, pool);
270       if (ctx->log_msg_func3)
271         SVN_ERR(svn_cl__cleanup_log_msg(ctx->log_msg_baton3,
272                                         err, pool));
273       else
274         SVN_ERR(err);
275   }
276
277   /* If no modifications were shelved, throw an error. */
278   SVN_ERR(svn_client_shelf_has_changes(&has_changes,
279                                        name, local_abspath, ctx, pool));
280   if (! has_changes)
281     {
282       SVN_ERR(svn_client_shelves_delete(name, local_abspath,
283                                         opt_state->dry_run, ctx, pool));
284       return svn_error_createf(SVN_ERR_ILLEGAL_TARGET, NULL,
285                                _("No changes were shelved"));
286     }
287
288   if (! opt_state->quiet)
289     SVN_ERR(svn_cmdline_printf(pool, "shelved '%s'\n", name));
290
291   return SVN_NO_ERROR;
292 }
293
294 /* This implements the `svn_opt_subcommand_t' interface. */
295 svn_error_t *
296 svn_cl__unshelve(apr_getopt_t *os,
297                  void *baton,
298                  apr_pool_t *pool)
299 {
300   svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
301   svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
302   const char *local_abspath;
303   const char *name;
304   apr_array_header_t *targets;
305
306   SVN_ERR(svn_dirent_get_absolute(&local_abspath, "", pool));
307
308   if (opt_state->list)
309     {
310       if (os->ind < os->argc)
311         return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, NULL);
312
313       SVN_ERR(shelves_list(local_abspath,
314                            ! opt_state->quiet /*diffstat*/,
315                            ctx, pool));
316       return SVN_NO_ERROR;
317     }
318
319   if (os->ind < os->argc)
320     {
321       SVN_ERR(get_name(&name, os, pool, pool));
322     }
323   else
324     {
325       SVN_ERR(name_of_youngest(&name, local_abspath, ctx, pool, pool));
326       SVN_ERR(svn_cmdline_printf(pool,
327                                  _("unshelving the youngest change, '%s'\n"),
328                                  name));
329     }
330
331   /* There should be no remaining arguments. */
332   SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
333                                                       opt_state->targets,
334                                                       ctx, FALSE, pool));
335   if (targets->nelts)
336     return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, NULL);
337
338   if (opt_state->quiet)
339     ctx->notify_func2 = NULL; /* Easy out: avoid unneeded work */
340
341   SVN_ERR(svn_client_unshelve(name, local_abspath,
342                               opt_state->keep_local, opt_state->dry_run,
343                               ctx, pool));
344   if (! opt_state->quiet)
345     SVN_ERR(svn_cmdline_printf(pool, "unshelved '%s'\n", name));
346
347   return SVN_NO_ERROR;
348 }
349
350 /* This implements the `svn_opt_subcommand_t' interface. */
351 svn_error_t *
352 svn_cl__shelves(apr_getopt_t *os,
353                 void *baton,
354                 apr_pool_t *pool)
355 {
356   svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
357   svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
358   const char *local_abspath;
359
360   /* There should be no remaining arguments. */
361   if (os->ind < os->argc)
362     return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, NULL);
363
364   SVN_ERR(svn_dirent_get_absolute(&local_abspath, "", pool));
365   SVN_ERR(shelves_list(local_abspath, ! opt_state->quiet /*diffstat*/,
366                        ctx, pool));
367
368   return SVN_NO_ERROR;
369 }