1289177Speter/*
2289177Speter * null-blame-cmd.c -- Subversion export command
3289177Speter *
4289177Speter * ====================================================================
5289177Speter *    Licensed to the Apache Software Foundation (ASF) under one
6289177Speter *    or more contributor license agreements.  See the NOTICE file
7289177Speter *    distributed with this work for additional information
8289177Speter *    regarding copyright ownership.  The ASF licenses this file
9289177Speter *    to you under the Apache License, Version 2.0 (the
10289177Speter *    "License"); you may not use this file except in compliance
11289177Speter *    with the License.  You may obtain a copy of the License at
12289177Speter *
13289177Speter *      http://www.apache.org/licenses/LICENSE-2.0
14289177Speter *
15289177Speter *    Unless required by applicable law or agreed to in writing,
16289177Speter *    software distributed under the License is distributed on an
17289177Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18289177Speter *    KIND, either express or implied.  See the License for the
19289177Speter *    specific language governing permissions and limitations
20289177Speter *    under the License.
21289177Speter * ====================================================================
22289177Speter */
23289177Speter
24289177Speter/* ==================================================================== */
25289177Speter
26289177Speter
27289177Speter
28289177Speter/*** Includes. ***/
29289177Speter
30289177Speter#include "svn_client.h"
31289177Speter#include "svn_cmdline.h"
32289177Speter#include "svn_error.h"
33289177Speter#include "svn_dirent_uri.h"
34289177Speter#include "svn_path.h"
35289177Speter#include "svn_pools.h"
36289177Speter#include "svn_sorts.h"
37289177Speter#include "cl.h"
38289177Speter
39289177Speter#include "svn_private_config.h"
40289177Speter#include "private/svn_string_private.h"
41289177Speter#include "private/svn_client_private.h"
42289177Speter
43289177Speterstruct file_rev_baton {
44289177Speter  apr_int64_t byte_count;
45289177Speter  apr_int64_t delta_count;
46289177Speter  apr_int64_t rev_count;
47289177Speter};
48289177Speter
49289177Speter/* Implements svn_txdelta_window_handler_t */
50289177Speterstatic svn_error_t *
51289177Speterdelta_handler(svn_txdelta_window_t *window, void *baton)
52289177Speter{
53289177Speter  struct file_rev_baton *frb = baton;
54289177Speter
55289177Speter  if (window != NULL)
56289177Speter    frb->byte_count += window->tview_len;
57289177Speter
58289177Speter  return SVN_NO_ERROR;
59289177Speter}
60289177Speter
61289177Speter/* Implementes svn_file_rev_handler_t */
62289177Speterstatic svn_error_t *
63289177Speterfile_rev_handler(void *baton, const char *path, svn_revnum_t revnum,
64289177Speter                 apr_hash_t *rev_props,
65289177Speter                 svn_boolean_t merged_revision,
66289177Speter                 svn_txdelta_window_handler_t *content_delta_handler,
67289177Speter                 void **content_delta_baton,
68289177Speter                 apr_array_header_t *prop_diffs,
69289177Speter                 apr_pool_t *pool)
70289177Speter{
71289177Speter  struct file_rev_baton *frb = baton;
72289177Speter
73289177Speter  frb->rev_count++;
74289177Speter
75289177Speter  if (content_delta_handler)
76289177Speter    {
77289177Speter      *content_delta_handler = delta_handler;
78289177Speter      *content_delta_baton = baton;
79289177Speter      frb->delta_count++;
80289177Speter    }
81289177Speter
82289177Speter  return SVN_NO_ERROR;
83289177Speter}
84289177Speter
85289177Speterstatic svn_error_t *
86289177Speterbench_null_blame(const char *target,
87289177Speter                 const svn_opt_revision_t *peg_revision,
88289177Speter                 const svn_opt_revision_t *start,
89289177Speter                 const svn_opt_revision_t *end,
90289177Speter                 svn_boolean_t include_merged_revisions,
91289177Speter                 svn_boolean_t quiet,
92289177Speter                 svn_client_ctx_t *ctx,
93289177Speter                 apr_pool_t *pool)
94289177Speter{
95289177Speter  struct file_rev_baton frb = { 0, 0, 0};
96289177Speter  svn_ra_session_t *ra_session;
97289177Speter  svn_revnum_t start_revnum, end_revnum;
98289177Speter  svn_boolean_t backwards;
99289177Speter  const char *target_abspath_or_url;
100289177Speter
101289177Speter  if (start->kind == svn_opt_revision_unspecified
102289177Speter      || end->kind == svn_opt_revision_unspecified)
103289177Speter    return svn_error_create
104289177Speter      (SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL);
105289177Speter
106289177Speter  if (svn_path_is_url(target))
107289177Speter    target_abspath_or_url = target;
108289177Speter  else
109289177Speter    SVN_ERR(svn_dirent_get_absolute(&target_abspath_or_url, target, pool));
110289177Speter
111289177Speter
112289177Speter  /* Get an RA plugin for this filesystem object. */
113289177Speter  SVN_ERR(svn_client__ra_session_from_path2(&ra_session, NULL,
114289177Speter                                            target, NULL, peg_revision,
115289177Speter                                            peg_revision,
116289177Speter                                            ctx, pool));
117289177Speter
118289177Speter  SVN_ERR(svn_client__get_revision_number(&start_revnum, NULL, ctx->wc_ctx,
119289177Speter                                          target_abspath_or_url, ra_session,
120289177Speter                                          start, pool));
121289177Speter
122289177Speter  SVN_ERR(svn_client__get_revision_number(&end_revnum, NULL, ctx->wc_ctx,
123289177Speter                                          target_abspath_or_url, ra_session,
124289177Speter                                          end, pool));
125289177Speter
126289177Speter  {
127289177Speter    svn_client__pathrev_t *loc;
128289177Speter    svn_opt_revision_t younger_end;
129289177Speter    younger_end.kind = svn_opt_revision_number;
130289177Speter    younger_end.value.number = MAX(start_revnum, end_revnum);
131289177Speter
132289177Speter    SVN_ERR(svn_client__resolve_rev_and_url(&loc, ra_session,
133289177Speter                                            target, peg_revision,
134289177Speter                                            &younger_end,
135289177Speter                                            ctx, pool));
136289177Speter
137289177Speter    /* Make the session point to the real URL. */
138289177Speter    SVN_ERR(svn_ra_reparent(ra_session, loc->url, pool));
139289177Speter  }
140289177Speter
141289177Speter  backwards = (start_revnum > end_revnum);
142289177Speter
143289177Speter  /* Collect all blame information.
144289177Speter     We need to ensure that we get one revision before the start_rev,
145289177Speter     if available so that we can know what was actually changed in the start
146289177Speter     revision. */
147289177Speter  SVN_ERR(svn_ra_get_file_revs2(ra_session, "",
148289177Speter                                backwards ? start_revnum
149289177Speter                                          : MAX(0, start_revnum-1),
150289177Speter                                end_revnum,
151289177Speter                                include_merged_revisions,
152289177Speter                                file_rev_handler, &frb, pool));
153289177Speter
154289177Speter  if (!quiet)
155289177Speter    SVN_ERR(svn_cmdline_printf(pool,
156289177Speter                               _("%15s revisions\n"
157289177Speter                                 "%15s deltas\n"
158289177Speter                                 "%15s bytes in deltas\n"),
159289177Speter                               svn__ui64toa_sep(frb.rev_count, ',', pool),
160289177Speter                               svn__ui64toa_sep(frb.delta_count, ',', pool),
161289177Speter                               svn__ui64toa_sep(frb.byte_count, ',', pool)));
162289177Speter
163289177Speter  return SVN_NO_ERROR;
164289177Speter}
165289177Speter
166289177Speter
167289177Speter/* This implements the `svn_opt_subcommand_t' interface. */
168289177Spetersvn_error_t *
169289177Spetersvn_cl__null_blame(apr_getopt_t *os,
170289177Speter                   void *baton,
171289177Speter                   apr_pool_t *pool)
172289177Speter{
173289177Speter  svn_cl__opt_state_t *opt_state = ((svn_cl__cmd_baton_t *) baton)->opt_state;
174289177Speter  svn_client_ctx_t *ctx = ((svn_cl__cmd_baton_t *) baton)->ctx;
175289177Speter  apr_pool_t *iterpool;
176289177Speter  apr_array_header_t *targets;
177289177Speter  int i;
178289177Speter  svn_boolean_t end_revision_unspecified = FALSE;
179289177Speter  svn_boolean_t seen_nonexistent_target = FALSE;
180289177Speter
181289177Speter  SVN_ERR(svn_cl__args_to_target_array_print_reserved(&targets, os,
182289177Speter                                                      opt_state->targets,
183289177Speter                                                      ctx, FALSE, pool));
184289177Speter
185289177Speter  /* Blame needs a file on which to operate. */
186289177Speter  if (! targets->nelts)
187289177Speter    return svn_error_create(SVN_ERR_CL_INSUFFICIENT_ARGS, 0, NULL);
188289177Speter
189289177Speter  if (opt_state->end_revision.kind == svn_opt_revision_unspecified)
190289177Speter    {
191289177Speter      if (opt_state->start_revision.kind != svn_opt_revision_unspecified)
192289177Speter        {
193289177Speter          /* In the case that -rX was specified, we actually want to set the
194289177Speter             range to be -r1:X. */
195289177Speter
196289177Speter          opt_state->end_revision = opt_state->start_revision;
197289177Speter          opt_state->start_revision.kind = svn_opt_revision_number;
198289177Speter          opt_state->start_revision.value.number = 1;
199289177Speter        }
200289177Speter      else
201289177Speter        end_revision_unspecified = TRUE;
202289177Speter    }
203289177Speter
204289177Speter  if (opt_state->start_revision.kind == svn_opt_revision_unspecified)
205289177Speter    {
206289177Speter      opt_state->start_revision.kind = svn_opt_revision_number;
207289177Speter      opt_state->start_revision.value.number = 1;
208289177Speter    }
209289177Speter
210289177Speter  /* The final conclusion from issue #2431 is that blame info
211289177Speter     is client output (unlike 'svn cat' which plainly cats the file),
212289177Speter     so the EOL style should be the platform local one.
213289177Speter  */
214289177Speter  iterpool = svn_pool_create(pool);
215289177Speter
216289177Speter  for (i = 0; i < targets->nelts; i++)
217289177Speter    {
218289177Speter      svn_error_t *err;
219289177Speter      const char *target = APR_ARRAY_IDX(targets, i, const char *);
220289177Speter      const char *parsed_path;
221289177Speter      svn_opt_revision_t peg_revision;
222289177Speter
223289177Speter      svn_pool_clear(iterpool);
224289177Speter      SVN_ERR(svn_cl__check_cancel(ctx->cancel_baton));
225289177Speter
226289177Speter      /* Check for a peg revision. */
227289177Speter      SVN_ERR(svn_opt_parse_path(&peg_revision, &parsed_path, target,
228289177Speter                                 iterpool));
229289177Speter
230289177Speter      if (end_revision_unspecified)
231289177Speter        {
232289177Speter          if (peg_revision.kind != svn_opt_revision_unspecified)
233289177Speter            opt_state->end_revision = peg_revision;
234289177Speter          else if (svn_path_is_url(target))
235289177Speter            opt_state->end_revision.kind = svn_opt_revision_head;
236289177Speter          else
237289177Speter            opt_state->end_revision.kind = svn_opt_revision_working;
238289177Speter        }
239289177Speter
240289177Speter      err = bench_null_blame(parsed_path,
241289177Speter                             &peg_revision,
242289177Speter                             &opt_state->start_revision,
243289177Speter                             &opt_state->end_revision,
244289177Speter                             opt_state->use_merge_history,
245289177Speter                             opt_state->quiet,
246289177Speter                             ctx,
247289177Speter                             iterpool);
248289177Speter
249289177Speter      if (err)
250289177Speter        {
251289177Speter          if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND ||
252289177Speter                   err->apr_err == SVN_ERR_ENTRY_NOT_FOUND ||
253289177Speter                   err->apr_err == SVN_ERR_FS_NOT_FILE ||
254289177Speter                   err->apr_err == SVN_ERR_FS_NOT_FOUND)
255289177Speter            {
256289177Speter              svn_handle_warning2(stderr, err, "svn: ");
257289177Speter              svn_error_clear(err);
258289177Speter              err = NULL;
259289177Speter              seen_nonexistent_target = TRUE;
260289177Speter            }
261289177Speter          else
262289177Speter            {
263289177Speter              return svn_error_trace(err);
264289177Speter            }
265289177Speter        }
266289177Speter    }
267289177Speter  svn_pool_destroy(iterpool);
268289177Speter
269289177Speter  if (seen_nonexistent_target)
270289177Speter    return svn_error_create(
271289177Speter      SVN_ERR_ILLEGAL_TARGET, NULL,
272289177Speter      _("Could not perform blame on all targets because some "
273289177Speter        "targets don't exist"));
274289177Speter  else
275289177Speter    return SVN_NO_ERROR;
276289177Speter}
277