]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/svnbench/null-blame-cmd.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.git] / contrib / subversion / subversion / svnbench / null-blame-cmd.c
1 /*
2  * null-blame-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 \f
28 /*** Includes. ***/
29
30 #include "svn_client.h"
31 #include "svn_cmdline.h"
32 #include "svn_error.h"
33 #include "svn_dirent_uri.h"
34 #include "svn_path.h"
35 #include "svn_pools.h"
36 #include "svn_sorts.h"
37 #include "cl.h"
38
39 #include "svn_private_config.h"
40 #include "private/svn_string_private.h"
41 #include "private/svn_client_private.h"
42
43 struct file_rev_baton {
44   apr_int64_t byte_count;
45   apr_int64_t delta_count;
46   apr_int64_t rev_count;
47 };
48
49 /* Implements svn_txdelta_window_handler_t */
50 static svn_error_t *
51 delta_handler(svn_txdelta_window_t *window, void *baton)
52 {
53   struct file_rev_baton *frb = baton;
54
55   if (window != NULL)
56     frb->byte_count += window->tview_len;
57
58   return SVN_NO_ERROR;
59 }
60
61 /* Implementes svn_file_rev_handler_t */
62 static svn_error_t *
63 file_rev_handler(void *baton, const char *path, svn_revnum_t revnum,
64                  apr_hash_t *rev_props,
65                  svn_boolean_t merged_revision,
66                  svn_txdelta_window_handler_t *content_delta_handler,
67                  void **content_delta_baton,
68                  apr_array_header_t *prop_diffs,
69                  apr_pool_t *pool)
70 {
71   struct file_rev_baton *frb = baton;
72
73   frb->rev_count++;
74
75   if (content_delta_handler)
76     {
77       *content_delta_handler = delta_handler;
78       *content_delta_baton = baton;
79       frb->delta_count++;
80     }
81
82   return SVN_NO_ERROR;
83 }
84
85 static svn_error_t *
86 bench_null_blame(const char *target,
87                  const svn_opt_revision_t *peg_revision,
88                  const svn_opt_revision_t *start,
89                  const svn_opt_revision_t *end,
90                  svn_boolean_t include_merged_revisions,
91                  svn_boolean_t quiet,
92                  svn_client_ctx_t *ctx,
93                  apr_pool_t *pool)
94 {
95   struct file_rev_baton frb = { 0, 0, 0};
96   svn_ra_session_t *ra_session;
97   svn_revnum_t start_revnum, end_revnum;
98   svn_boolean_t backwards;
99   const char *target_abspath_or_url;
100
101   if (start->kind == svn_opt_revision_unspecified
102       || end->kind == svn_opt_revision_unspecified)
103     return svn_error_create
104       (SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL);
105
106   if (svn_path_is_url(target))
107     target_abspath_or_url = target;
108   else
109     SVN_ERR(svn_dirent_get_absolute(&target_abspath_or_url, target, pool));
110
111
112   /* Get an RA plugin for this filesystem object. */
113   SVN_ERR(svn_client__ra_session_from_path2(&ra_session, NULL,
114                                             target, NULL, peg_revision,
115                                             peg_revision,
116                                             ctx, pool));
117
118   SVN_ERR(svn_client__get_revision_number(&start_revnum, NULL, ctx->wc_ctx,
119                                           target_abspath_or_url, ra_session,
120                                           start, pool));
121
122   SVN_ERR(svn_client__get_revision_number(&end_revnum, NULL, ctx->wc_ctx,
123                                           target_abspath_or_url, ra_session,
124                                           end, pool));
125
126   {
127     svn_client__pathrev_t *loc;
128     svn_opt_revision_t younger_end;
129     younger_end.kind = svn_opt_revision_number;
130     younger_end.value.number = MAX(start_revnum, end_revnum);
131
132     SVN_ERR(svn_client__resolve_rev_and_url(&loc, ra_session,
133                                             target, peg_revision,
134                                             &younger_end,
135                                             ctx, pool));
136
137     /* Make the session point to the real URL. */
138     SVN_ERR(svn_ra_reparent(ra_session, loc->url, pool));
139   }
140
141   backwards = (start_revnum > end_revnum);
142
143   /* Collect all blame information.
144      We need to ensure that we get one revision before the start_rev,
145      if available so that we can know what was actually changed in the start
146      revision. */
147   SVN_ERR(svn_ra_get_file_revs2(ra_session, "",
148                                 backwards ? start_revnum
149                                           : MAX(0, start_revnum-1),
150                                 end_revnum,
151                                 include_merged_revisions,
152                                 file_rev_handler, &frb, pool));
153
154   if (!quiet)
155     SVN_ERR(svn_cmdline_printf(pool,
156                                _("%15s revisions\n"
157                                  "%15s deltas\n"
158                                  "%15s bytes in deltas\n"),
159                                svn__ui64toa_sep(frb.rev_count, ',', pool),
160                                svn__ui64toa_sep(frb.delta_count, ',', pool),
161                                svn__ui64toa_sep(frb.byte_count, ',', pool)));
162
163   return SVN_NO_ERROR;
164 }
165
166
167 /* This implements the `svn_opt_subcommand_t' interface. */
168 svn_error_t *
169 svn_cl__null_blame(apr_getopt_t *os,
170                    void *baton,
171                    apr_pool_t *pool)
172 {
173   svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
174   svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
175   apr_pool_t *iterpool;
176   apr_array_header_t *targets;
177   int i;
178   svn_boolean_t end_revision_unspecified = FALSE;
179   svn_boolean_t seen_nonexistent_target = FALSE;
180
181   SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
182                                                       opt_state->targets,
183                                                       ctx, FALSE, pool));
184
185   /* Blame needs a file on which to operate. */
186   if (! targets->nelts)
187     return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);
188
189   if (opt_state->end_revision.kind == svn_opt_revision_unspecified)
190     {
191       if (opt_state->start_revision.kind != svn_opt_revision_unspecified)
192         {
193           /* In the case that -rX was specified, we actually want to set the
194              range to be -r1:X. */
195
196           opt_state->end_revision = opt_state->start_revision;
197           opt_state->start_revision.kind = svn_opt_revision_number;
198           opt_state->start_revision.value.number = 1;
199         }
200       else
201         end_revision_unspecified = TRUE;
202     }
203
204   if (opt_state->start_revision.kind == svn_opt_revision_unspecified)
205     {
206       opt_state->start_revision.kind = svn_opt_revision_number;
207       opt_state->start_revision.value.number = 1;
208     }
209
210   /* The final conclusion from issue #2431 is that blame info
211      is client output (unlike 'svn cat' which plainly cats the file),
212      so the EOL style should be the platform local one.
213   */
214   iterpool = svn_pool_create(pool);
215
216   for (i = 0; i < targets->nelts; i++)
217     {
218       svn_error_t *err;
219       const char *target = APR_ARRAY_IDX(targets, i, const char *);
220       const char *parsed_path;
221       svn_opt_revision_t peg_revision;
222
223       svn_pool_clear(iterpool);
224       SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton));
225
226       /* Check for a peg revision. */
227       SVN_ERR(svn_opt_parse_path(&peg_revision, &parsed_path, target,
228                                  iterpool));
229
230       if (end_revision_unspecified)
231         {
232           if (peg_revision.kind != svn_opt_revision_unspecified)
233             opt_state->end_revision = peg_revision;
234           else if (svn_path_is_url(target))
235             opt_state->end_revision.kind = svn_opt_revision_head;
236           else
237             opt_state->end_revision.kind = svn_opt_revision_working;
238         }
239
240       err = bench_null_blame(parsed_path,
241                              &peg_revision,
242                              &opt_state->start_revision,
243                              &opt_state->end_revision,
244                              opt_state->use_merge_history,
245                              opt_state->quiet,
246                              ctx,
247                              iterpool);
248
249       if (err)
250         {
251           if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND ||
252                    err->apr_err == SVN_ERR_ENTRY_NOT_FOUND ||
253                    err->apr_err == SVN_ERR_FS_NOT_FILE ||
254                    err->apr_err == SVN_ERR_FS_NOT_FOUND)
255             {
256               svn_handle_warning2(stderr, err, "svn: ");
257               svn_error_clear(err);
258               err = NULL;
259               seen_nonexistent_target = TRUE;
260             }
261           else
262             {
263               return svn_error_trace(err);
264             }
265         }
266     }
267   svn_pool_destroy(iterpool);
268
269   if (seen_nonexistent_target)
270     return svn_error_create(
271       SVN_ERR_ILLEGAL_TARGET, NULL,
272       _("Could not perform blame on all targets because some "
273         "targets don't exist"));
274   else
275     return SVN_NO_ERROR;
276 }