null-export-cmd.c revision 362181
1/*
2 * export-cmd.c -- Subversion export command
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
27
28/*** Includes. ***/
29
30#include "svn_client.h"
31#include "svn_error.h"
32#include "svn_dirent_uri.h"
33#include "svn_path.h"
34#include "svn_cmdline.h"
35#include "cl.h"
36
37#include "svn_private_config.h"
38#include "private/svn_string_private.h"
39#include "private/svn_client_private.h"
40
41/*** The export editor code. ***/
42
43/* ---------------------------------------------------------------------- */
44
45/*** A dedicated 'export' editor, which does no .svn/ accounting.  ***/
46
47typedef struct edit_baton_t
48{
49  apr_int64_t file_count;
50  apr_int64_t dir_count;
51  apr_int64_t byte_count;
52  apr_int64_t prop_count;
53  apr_int64_t prop_byte_count;
54} edit_baton_t;
55
56static svn_error_t *
57set_target_revision(void *edit_baton,
58                    svn_revnum_t target_revision,
59                    apr_pool_t *pool)
60{
61  return SVN_NO_ERROR;
62}
63
64
65/* Just ensure that the main export directory exists. */
66static svn_error_t *
67open_root(void *edit_baton,
68          svn_revnum_t base_revision,
69          apr_pool_t *pool,
70          void **root_baton)
71{
72  *root_baton = edit_baton;
73  return SVN_NO_ERROR;
74}
75
76
77/* Ensure the directory exists, and send feedback. */
78static svn_error_t *
79add_directory(const char *path,
80              void *parent_baton,
81              const char *copyfrom_path,
82              svn_revnum_t copyfrom_revision,
83              apr_pool_t *pool,
84              void **baton)
85{
86  edit_baton_t *eb = parent_baton;
87  eb->dir_count++;
88
89  *baton = parent_baton;
90  return SVN_NO_ERROR;
91}
92
93
94/* Build a file baton. */
95static svn_error_t *
96add_file(const char *path,
97          void *parent_baton,
98          const char *copyfrom_path,
99          svn_revnum_t copyfrom_revision,
100          apr_pool_t *pool,
101          void **baton)
102{
103  edit_baton_t *eb = parent_baton;
104  eb->file_count++;
105
106  *baton = parent_baton;
107  return SVN_NO_ERROR;
108}
109
110static svn_error_t *
111window_handler(svn_txdelta_window_t *window, void *baton)
112{
113  edit_baton_t *eb = baton;
114  if (window != NULL)
115    eb->byte_count += window->tview_len;
116
117  return SVN_NO_ERROR;
118}
119
120/* Write incoming data into the tmpfile stream */
121
122static svn_error_t *
123apply_textdelta(void *file_baton,
124                const char *base_checksum,
125                apr_pool_t *pool,
126                svn_txdelta_window_handler_t *handler,
127                void **handler_baton)
128{
129  *handler_baton = file_baton;
130  *handler = window_handler;
131
132  return SVN_NO_ERROR;
133}
134
135static svn_error_t *
136change_file_prop(void *file_baton,
137                 const char *name,
138                 const svn_string_t *value,
139                 apr_pool_t *pool)
140{
141  edit_baton_t *eb = file_baton;
142  eb->prop_count++;
143  eb->prop_byte_count += value->len;
144
145  return SVN_NO_ERROR;
146}
147
148static svn_error_t *
149change_dir_prop(void *dir_baton,
150                const char *name,
151                const svn_string_t *value,
152                apr_pool_t *pool)
153{
154  edit_baton_t *eb = dir_baton;
155  eb->prop_count++;
156
157  return SVN_NO_ERROR;
158}
159
160static svn_error_t *
161close_file(void *file_baton,
162           const char *text_checksum,
163           apr_pool_t *pool)
164{
165  return SVN_NO_ERROR;
166}
167
168/* Implement svn_write_fn_t, simply counting the incoming data. */
169static svn_error_t *
170file_write_handler(void *baton, const char *data, apr_size_t *len)
171{
172  edit_baton_t *eb = baton;
173  eb->byte_count += *len;
174
175  return SVN_NO_ERROR;
176}
177
178/*** Public Interfaces ***/
179
180static svn_error_t *
181bench_null_export(svn_revnum_t *result_rev,
182                  const char *from_path_or_url,
183                  svn_opt_revision_t *peg_revision,
184                  svn_opt_revision_t *revision,
185                  svn_depth_t depth,
186                  void *baton,
187                  svn_client_ctx_t *ctx,
188                  svn_boolean_t quiet,
189                  apr_pool_t *pool)
190{
191  svn_revnum_t edit_revision = SVN_INVALID_REVNUM;
192  svn_boolean_t from_is_url = svn_path_is_url(from_path_or_url);
193
194  SVN_ERR_ASSERT(peg_revision != NULL);
195  SVN_ERR_ASSERT(revision != NULL);
196
197  if (peg_revision->kind == svn_opt_revision_unspecified)
198    peg_revision->kind = svn_path_is_url(from_path_or_url)
199                       ? svn_opt_revision_head
200                       : svn_opt_revision_working;
201
202  if (revision->kind == svn_opt_revision_unspecified)
203    revision = peg_revision;
204
205  if (from_is_url || ! SVN_CLIENT__REVKIND_IS_LOCAL_TO_WC(revision->kind))
206    {
207      svn_client__pathrev_t *loc;
208      svn_ra_session_t *ra_session;
209      svn_node_kind_t kind;
210      edit_baton_t *eb = baton;
211
212      /* Get the RA connection. */
213      SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &loc,
214                                                from_path_or_url, NULL,
215                                                peg_revision,
216                                                revision, ctx, pool));
217
218      SVN_ERR(svn_ra_check_path(ra_session, "", loc->rev, &kind, pool));
219
220      if (kind == svn_node_file)
221        {
222          apr_hash_t *props;
223
224          /* Since we don't use the editor, we must count "manually". */
225          svn_stream_t *stream = svn_stream_create(eb, pool);
226          svn_stream_set_write(stream, file_write_handler);
227          eb->file_count++;
228
229          /* Since you cannot actually root an editor at a file, we
230           * manually drive a few functions of our editor. */
231
232          /* Step outside the editor-likeness for a moment, to actually talk
233           * to the repository. */
234          /* ### note: the stream will not be closed */
235          SVN_ERR(svn_ra_get_file(ra_session, "", loc->rev,
236                                  stream, NULL, &props, pool));
237        }
238      else if (kind == svn_node_dir)
239        {
240          void *edit_baton = NULL;
241          const svn_delta_editor_t *export_editor = NULL;
242          const svn_ra_reporter3_t *reporter;
243          void *report_baton;
244
245          svn_delta_editor_t *editor = svn_delta_default_editor(pool);
246
247          editor->set_target_revision = set_target_revision;
248          editor->open_root = open_root;
249          editor->add_directory = add_directory;
250          editor->add_file = add_file;
251          editor->apply_textdelta = apply_textdelta;
252          editor->close_file = close_file;
253          editor->change_file_prop = change_file_prop;
254          editor->change_dir_prop = change_dir_prop;
255
256          /* for ra_svn, we don't need an editior in quiet mode */
257          if (!quiet || strncmp(loc->repos_root_url, "svn:", 4))
258            SVN_ERR(svn_delta_get_cancellation_editor(ctx->cancel_func,
259                                                      ctx->cancel_baton,
260                                                      editor,
261                                                      baton,
262                                                      &export_editor,
263                                                      &edit_baton,
264                                                      pool));
265
266          /* Manufacture a basic 'report' to the update reporter. */
267          SVN_ERR(svn_ra_do_update3(ra_session,
268                                    &reporter, &report_baton,
269                                    loc->rev,
270                                    "", /* no sub-target */
271                                    depth,
272                                    FALSE, /* don't want copyfrom-args */
273                                    FALSE, /* don't want ignore_ancestry */
274                                    export_editor, edit_baton,
275                                    pool, pool));
276
277          SVN_ERR(reporter->set_path(report_baton, "", loc->rev,
278                                     /* Depth is irrelevant, as we're
279                                        passing start_empty=TRUE anyway. */
280                                     svn_depth_infinity,
281                                     TRUE, /* "help, my dir is empty!" */
282                                     NULL, pool));
283
284          SVN_ERR(reporter->finish_report(report_baton, pool));
285
286          /* We don't receive the "add directory" callback for the starting
287           * node. */
288          eb->dir_count++;
289        }
290      else if (kind == svn_node_none)
291        {
292          return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
293                                   _("URL '%s' doesn't exist"),
294                                   from_path_or_url);
295        }
296      /* kind == svn_node_unknown not handled */
297    }
298
299
300  if (result_rev)
301    *result_rev = edit_revision;
302
303  return SVN_NO_ERROR;
304}
305
306
307/*** Code. ***/
308
309/* This implements the `svn_opt_subcommand_t' interface. */
310svn_error_t *
311svn_cl__null_export(apr_getopt_t *os,
312                    void *baton,
313                    apr_pool_t *pool)
314{
315  svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
316  svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
317  const char *from;
318  apr_array_header_t *targets;
319  svn_error_t *err;
320  svn_opt_revision_t peg_revision;
321  const char *truefrom;
322  edit_baton_t eb = { 0 };
323
324  SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
325                                                      opt_state->targets,
326                                                      ctx, FALSE, pool));
327
328  /* We want exactly 1 or 2 targets for this subcommand. */
329  if (targets->nelts < 1)
330    return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);
331  if (targets->nelts > 2)
332    return svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, 0, NULL);
333
334  /* The first target is the `from' path. */
335  from = APR_ARRAY_IDX(targets, 0, const char *);
336
337  /* Get the peg revision if present. */
338  SVN_ERR(svn_opt_parse_path(&peg_revision, &truefrom, from, pool));
339
340  if (opt_state->depth == svn_depth_unknown)
341    opt_state->depth = svn_depth_infinity;
342
343  /* Do the export. */
344  err = bench_null_export(NULL, truefrom, &peg_revision,
345                          &(opt_state->start_revision),
346                          opt_state->depth,
347                          &eb,
348                          ctx, opt_state->quiet, pool);
349
350  if (!opt_state->quiet)
351    SVN_ERR(svn_cmdline_printf(pool,
352                               _("%15s directories\n"
353                                 "%15s files\n"
354                                 "%15s bytes in files\n"
355                                 "%15s properties\n"
356                                 "%15s bytes in properties\n"),
357                               svn__ui64toa_sep(eb.dir_count, ',', pool),
358                               svn__ui64toa_sep(eb.file_count, ',', pool),
359                               svn__ui64toa_sep(eb.byte_count, ',', pool),
360                               svn__ui64toa_sep(eb.prop_count, ',', pool),
361                               svn__ui64toa_sep(eb.prop_byte_count, ',', pool)));
362
363  return svn_error_trace(err);
364}
365