]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/subversion/subversion/libsvn_client/blame.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / contrib / subversion / subversion / libsvn_client / blame.c
1 /*
2  * blame.c:  return blame messages
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 #include <apr_pools.h>
25
26 #include "client.h"
27
28 #include "svn_client.h"
29 #include "svn_subst.h"
30 #include "svn_string.h"
31 #include "svn_error.h"
32 #include "svn_diff.h"
33 #include "svn_pools.h"
34 #include "svn_dirent_uri.h"
35 #include "svn_path.h"
36 #include "svn_props.h"
37 #include "svn_sorts.h"
38
39 #include "private/svn_wc_private.h"
40
41 #include "svn_private_config.h"
42
43 #include <assert.h>
44
45 /* The metadata associated with a particular revision. */
46 struct rev
47 {
48   svn_revnum_t revision; /* the revision number */
49   apr_hash_t *rev_props; /* the revision properties */
50   /* Used for merge reporting. */
51   const char *path;      /* the absolute repository path */
52 };
53
54 /* One chunk of blame */
55 struct blame
56 {
57   const struct rev *rev;    /* the responsible revision */
58   apr_off_t start;          /* the starting diff-token (line) */
59   struct blame *next;       /* the next chunk */
60 };
61
62 /* A chain of blame chunks */
63 struct blame_chain
64 {
65   struct blame *blame;      /* linked list of blame chunks */
66   struct blame *avail;      /* linked list of free blame chunks */
67   struct apr_pool_t *pool;  /* Allocate members from this pool. */
68 };
69
70 /* The baton use for the diff output routine. */
71 struct diff_baton {
72   struct blame_chain *chain;
73   const struct rev *rev;
74 };
75
76 /* The baton used for a file revision. */
77 struct file_rev_baton {
78   svn_revnum_t start_rev, end_rev;
79   const char *target;
80   svn_client_ctx_t *ctx;
81   const svn_diff_file_options_t *diff_options;
82   /* name of file containing the previous revision of the file */
83   const char *last_filename;
84   struct rev *rev;     /* the rev for which blame is being assigned
85                           during a diff */
86   struct blame_chain *chain;      /* the original blame chain. */
87   const char *repos_root_url;    /* To construct a url */
88   apr_pool_t *mainpool;  /* lives during the whole sequence of calls */
89   apr_pool_t *lastpool;  /* pool used during previous call */
90   apr_pool_t *currpool;  /* pool used during this call */
91
92   /* These are used for tracking merged revisions. */
93   svn_boolean_t include_merged_revisions;
94   svn_boolean_t merged_revision;
95   struct blame_chain *merged_chain;  /* the merged blame chain. */
96   /* name of file containing the previous merged revision of the file */
97   const char *last_original_filename;
98   /* pools for files which may need to persist for more than one rev. */
99   apr_pool_t *filepool;
100   apr_pool_t *prevfilepool;
101 };
102
103 /* The baton used by the txdelta window handler. */
104 struct delta_baton {
105   /* Our underlying handler/baton that we wrap */
106   svn_txdelta_window_handler_t wrapped_handler;
107   void *wrapped_baton;
108   struct file_rev_baton *file_rev_baton;
109   const char *filename;
110 };
111
112
113
114
115 /* Return a blame chunk associated with REV for a change starting
116    at token START, and allocated in CHAIN->mainpool. */
117 static struct blame *
118 blame_create(struct blame_chain *chain,
119              const struct rev *rev,
120              apr_off_t start)
121 {
122   struct blame *blame;
123   if (chain->avail)
124     {
125       blame = chain->avail;
126       chain->avail = blame->next;
127     }
128   else
129     blame = apr_palloc(chain->pool, sizeof(*blame));
130   blame->rev = rev;
131   blame->start = start;
132   blame->next = NULL;
133   return blame;
134 }
135
136 /* Destroy a blame chunk. */
137 static void
138 blame_destroy(struct blame_chain *chain,
139               struct blame *blame)
140 {
141   blame->next = chain->avail;
142   chain->avail = blame;
143 }
144
145 /* Return the blame chunk that contains token OFF, starting the search at
146    BLAME. */
147 static struct blame *
148 blame_find(struct blame *blame, apr_off_t off)
149 {
150   struct blame *prev = NULL;
151   while (blame)
152     {
153       if (blame->start > off) break;
154       prev = blame;
155       blame = blame->next;
156     }
157   return prev;
158 }
159
160 /* Shift the start-point of BLAME and all subsequence blame-chunks
161    by ADJUST tokens */
162 static void
163 blame_adjust(struct blame *blame, apr_off_t adjust)
164 {
165   while (blame)
166     {
167       blame->start += adjust;
168       blame = blame->next;
169     }
170 }
171
172 /* Delete the blame associated with the region from token START to
173    START + LENGTH */
174 static svn_error_t *
175 blame_delete_range(struct blame_chain *chain,
176                    apr_off_t start,
177                    apr_off_t length)
178 {
179   struct blame *first = blame_find(chain->blame, start);
180   struct blame *last = blame_find(chain->blame, start + length);
181   struct blame *tail = last->next;
182
183   if (first != last)
184     {
185       struct blame *walk = first->next;
186       while (walk != last)
187         {
188           struct blame *next = walk->next;
189           blame_destroy(chain, walk);
190           walk = next;
191         }
192       first->next = last;
193       last->start = start;
194       if (first->start == start)
195         {
196           *first = *last;
197           blame_destroy(chain, last);
198           last = first;
199         }
200     }
201
202   if (tail && tail->start == last->start + length)
203     {
204       *last = *tail;
205       blame_destroy(chain, tail);
206       tail = last->next;
207     }
208
209   blame_adjust(tail, -length);
210
211   return SVN_NO_ERROR;
212 }
213
214 /* Insert a chunk of blame associated with REV starting
215    at token START and continuing for LENGTH tokens */
216 static svn_error_t *
217 blame_insert_range(struct blame_chain *chain,
218                    const struct rev *rev,
219                    apr_off_t start,
220                    apr_off_t length)
221 {
222   struct blame *head = chain->blame;
223   struct blame *point = blame_find(head, start);
224   struct blame *insert;
225
226   if (point->start == start)
227     {
228       insert = blame_create(chain, point->rev, point->start + length);
229       point->rev = rev;
230       insert->next = point->next;
231       point->next = insert;
232     }
233   else
234     {
235       struct blame *middle;
236       middle = blame_create(chain, rev, start);
237       insert = blame_create(chain, point->rev, start + length);
238       middle->next = insert;
239       insert->next = point->next;
240       point->next = middle;
241     }
242   blame_adjust(insert->next, length);
243
244   return SVN_NO_ERROR;
245 }
246
247 /* Callback for diff between subsequent revisions */
248 static svn_error_t *
249 output_diff_modified(void *baton,
250                      apr_off_t original_start,
251                      apr_off_t original_length,
252                      apr_off_t modified_start,
253                      apr_off_t modified_length,
254                      apr_off_t latest_start,
255                      apr_off_t latest_length)
256 {
257   struct diff_baton *db = baton;
258
259   if (original_length)
260     SVN_ERR(blame_delete_range(db->chain, modified_start, original_length));
261
262   if (modified_length)
263     SVN_ERR(blame_insert_range(db->chain, db->rev, modified_start,
264                                modified_length));
265
266   return SVN_NO_ERROR;
267 }
268
269 static const svn_diff_output_fns_t output_fns = {
270         NULL,
271         output_diff_modified
272 };
273
274 /* Add the blame for the diffs between LAST_FILE and CUR_FILE to CHAIN,
275    for revision REV.  LAST_FILE may be NULL in which
276    case blame is added for every line of CUR_FILE. */
277 static svn_error_t *
278 add_file_blame(const char *last_file,
279                const char *cur_file,
280                struct blame_chain *chain,
281                struct rev *rev,
282                const svn_diff_file_options_t *diff_options,
283                apr_pool_t *pool)
284 {
285   if (!last_file)
286     {
287       SVN_ERR_ASSERT(chain->blame == NULL);
288       chain->blame = blame_create(chain, rev, 0);
289     }
290   else
291     {
292       svn_diff_t *diff;
293       struct diff_baton diff_baton;
294
295       diff_baton.chain = chain;
296       diff_baton.rev = rev;
297
298       /* We have a previous file.  Get the diff and adjust blame info. */
299       SVN_ERR(svn_diff_file_diff_2(&diff, last_file, cur_file,
300                                    diff_options, pool));
301       SVN_ERR(svn_diff_output(diff, &diff_baton, &output_fns));
302     }
303
304   return SVN_NO_ERROR;
305 }
306
307 /* The delta window handler for the text delta between the previously seen
308  * revision and the revision currently being handled.
309  *
310  * Record the blame information for this revision in BATON->file_rev_baton.
311  *
312  * Implements svn_txdelta_window_handler_t.
313  */
314 static svn_error_t *
315 window_handler(svn_txdelta_window_t *window, void *baton)
316 {
317   struct delta_baton *dbaton = baton;
318   struct file_rev_baton *frb = dbaton->file_rev_baton;
319   struct blame_chain *chain;
320
321   /* Call the wrapped handler first. */
322   SVN_ERR(dbaton->wrapped_handler(window, dbaton->wrapped_baton));
323
324   /* We patiently wait for the NULL window marking the end. */
325   if (window)
326     return SVN_NO_ERROR;
327
328   /* If we are including merged revisions, we need to add each rev to the
329      merged chain. */
330   if (frb->include_merged_revisions)
331     chain = frb->merged_chain;
332   else
333     chain = frb->chain;
334
335   /* Process this file. */
336   SVN_ERR(add_file_blame(frb->last_filename,
337                          dbaton->filename, chain, frb->rev,
338                          frb->diff_options, frb->currpool));
339
340   /* If we are including merged revisions, and the current revision is not a
341      merged one, we need to add its blame info to the chain for the original
342      line of history. */
343   if (frb->include_merged_revisions && ! frb->merged_revision)
344     {
345       apr_pool_t *tmppool;
346
347       SVN_ERR(add_file_blame(frb->last_original_filename,
348                              dbaton->filename, frb->chain, frb->rev,
349                              frb->diff_options, frb->currpool));
350
351       /* This filename could be around for a while, potentially, so
352          use the longer lifetime pool, and switch it with the previous one*/
353       svn_pool_clear(frb->prevfilepool);
354       tmppool = frb->filepool;
355       frb->filepool = frb->prevfilepool;
356       frb->prevfilepool = tmppool;
357
358       frb->last_original_filename = apr_pstrdup(frb->filepool,
359                                                 dbaton->filename);
360     }
361
362   /* Prepare for next revision. */
363
364   /* Remember the file name so we can diff it with the next revision. */
365   frb->last_filename = dbaton->filename;
366
367   /* Switch pools. */
368   {
369     apr_pool_t *tmp_pool = frb->lastpool;
370     frb->lastpool = frb->currpool;
371     frb->currpool = tmp_pool;
372   }
373
374   return SVN_NO_ERROR;
375 }
376
377
378 /* Calculate and record blame information for one revision of the file,
379  * by comparing the file content against the previously seen revision.
380  *
381  * This handler is called once for each interesting revision of the file.
382  *
383  * Record the blame information for this revision in (file_rev_baton) BATON.
384  *
385  * Implements svn_file_rev_handler_t.
386  */
387 static svn_error_t *
388 file_rev_handler(void *baton, const char *path, svn_revnum_t revnum,
389                  apr_hash_t *rev_props,
390                  svn_boolean_t merged_revision,
391                  svn_txdelta_window_handler_t *content_delta_handler,
392                  void **content_delta_baton,
393                  apr_array_header_t *prop_diffs,
394                  apr_pool_t *pool)
395 {
396   struct file_rev_baton *frb = baton;
397   svn_stream_t *last_stream;
398   svn_stream_t *cur_stream;
399   struct delta_baton *delta_baton;
400   apr_pool_t *filepool;
401
402   /* Clear the current pool. */
403   svn_pool_clear(frb->currpool);
404
405   if (frb->ctx->notify_func2)
406     {
407       svn_wc_notify_t *notify
408             = svn_wc_create_notify_url(
409                             svn_path_url_add_component2(frb->repos_root_url,
410                                                         path+1, pool),
411                             svn_wc_notify_blame_revision, pool);
412       notify->path = path;
413       notify->kind = svn_node_none;
414       notify->content_state = notify->prop_state
415         = svn_wc_notify_state_inapplicable;
416       notify->lock_state = svn_wc_notify_lock_state_inapplicable;
417       notify->revision = revnum;
418       notify->rev_props = rev_props;
419       frb->ctx->notify_func2(frb->ctx->notify_baton2, notify, pool);
420     }
421
422   if (frb->ctx->cancel_func)
423     SVN_ERR(frb->ctx->cancel_func(frb->ctx->cancel_baton));
424
425   /* If there were no content changes, we couldn't care less about this
426      revision now.  Note that we checked the mime type above, so things
427      work if the user just changes the mime type in a commit.
428      Also note that we don't switch the pools in this case.  This is important,
429      since the tempfile will be removed by the pool and we need the tempfile
430      from the last revision with content changes. */
431   if (!content_delta_handler)
432     return SVN_NO_ERROR;
433
434   frb->merged_revision = merged_revision;
435
436   /* Create delta baton. */
437   delta_baton = apr_palloc(frb->currpool, sizeof(*delta_baton));
438
439   /* Prepare the text delta window handler. */
440   if (frb->last_filename)
441     SVN_ERR(svn_stream_open_readonly(&last_stream, frb->last_filename,
442                                      frb->currpool, pool));
443   else
444     last_stream = svn_stream_empty(frb->currpool);
445
446   if (frb->include_merged_revisions && !frb->merged_revision)
447     filepool = frb->filepool;
448   else
449     filepool = frb->currpool;
450
451   SVN_ERR(svn_stream_open_unique(&cur_stream, &delta_baton->filename, NULL,
452                                  svn_io_file_del_on_pool_cleanup,
453                                  filepool, pool));
454
455   /* Get window handler for applying delta. */
456   svn_txdelta_apply(last_stream, cur_stream, NULL, NULL,
457                     frb->currpool,
458                     &delta_baton->wrapped_handler,
459                     &delta_baton->wrapped_baton);
460
461   /* Wrap the window handler with our own. */
462   delta_baton->file_rev_baton = frb;
463   *content_delta_handler = window_handler;
464   *content_delta_baton = delta_baton;
465
466   /* Create the rev structure. */
467   frb->rev = apr_pcalloc(frb->mainpool, sizeof(struct rev));
468
469   if (revnum < frb->start_rev)
470     {
471       /* We shouldn't get more than one revision before the starting
472          revision (unless of including merged revisions). */
473       SVN_ERR_ASSERT((frb->last_filename == NULL)
474                      || frb->include_merged_revisions);
475
476       /* The file existed before start_rev; generate no blame info for
477          lines from this revision (or before). */
478       frb->rev->revision = SVN_INVALID_REVNUM;
479     }
480   else
481     {
482       SVN_ERR_ASSERT(revnum <= frb->end_rev);
483
484       /* Set values from revision props. */
485       frb->rev->revision = revnum;
486       frb->rev->rev_props = svn_prop_hash_dup(rev_props, frb->mainpool);
487     }
488
489   if (frb->include_merged_revisions)
490     frb->rev->path = apr_pstrdup(frb->mainpool, path);
491
492   return SVN_NO_ERROR;
493 }
494
495 /* Ensure that CHAIN_ORIG and CHAIN_MERGED have the same number of chunks,
496    and that for every chunk C, CHAIN_ORIG[C] and CHAIN_MERGED[C] have the
497    same starting value.  Both CHAIN_ORIG and CHAIN_MERGED should not be
498    NULL.  */
499 static void
500 normalize_blames(struct blame_chain *chain,
501                  struct blame_chain *chain_merged,
502                  apr_pool_t *pool)
503 {
504   struct blame *walk, *walk_merged;
505
506   /* Walk over the CHAIN's blame chunks and CHAIN_MERGED's blame chunks,
507      creating new chunks as needed. */
508   for (walk = chain->blame, walk_merged = chain_merged->blame;
509        walk->next && walk_merged->next;
510        walk = walk->next, walk_merged = walk_merged->next)
511     {
512       /* The current chunks should always be starting at the same offset. */
513       assert(walk->start == walk_merged->start);
514
515       if (walk->next->start < walk_merged->next->start)
516         {
517           /* insert a new chunk in CHAIN_MERGED. */
518           struct blame *tmp = blame_create(chain_merged, walk_merged->rev,
519                                            walk->next->start);
520           tmp->next = walk_merged->next;
521           walk_merged->next = tmp;
522         }
523
524       if (walk->next->start > walk_merged->next->start)
525         {
526           /* insert a new chunk in CHAIN. */
527           struct blame *tmp = blame_create(chain, walk->rev,
528                                            walk_merged->next->start);
529           tmp->next = walk->next;
530           walk->next = tmp;
531         }
532     }
533
534   /* If both NEXT pointers are null, the lists are equally long, otherwise
535      we need to extend one of them.  If CHAIN is longer, append new chunks
536      to CHAIN_MERGED until its length matches that of CHAIN. */
537   while (walk->next != NULL)
538     {
539       struct blame *tmp = blame_create(chain_merged, walk_merged->rev,
540                                        walk->next->start);
541       walk_merged->next = tmp;
542
543       walk_merged = walk_merged->next;
544       walk = walk->next;
545     }
546
547   /* Same as above, only extend CHAIN to match CHAIN_MERGED. */
548   while (walk_merged->next != NULL)
549     {
550       struct blame *tmp = blame_create(chain, walk->rev,
551                                        walk_merged->next->start);
552       walk->next = tmp;
553
554       walk = walk->next;
555       walk_merged = walk_merged->next;
556     }
557 }
558
559 svn_error_t *
560 svn_client_blame5(const char *target,
561                   const svn_opt_revision_t *peg_revision,
562                   const svn_opt_revision_t *start,
563                   const svn_opt_revision_t *end,
564                   const svn_diff_file_options_t *diff_options,
565                   svn_boolean_t ignore_mime_type,
566                   svn_boolean_t include_merged_revisions,
567                   svn_client_blame_receiver3_t receiver,
568                   void *receiver_baton,
569                   svn_client_ctx_t *ctx,
570                   apr_pool_t *pool)
571 {
572   struct file_rev_baton frb;
573   svn_ra_session_t *ra_session;
574   svn_revnum_t start_revnum, end_revnum;
575   svn_client__pathrev_t *end_loc;
576   struct blame *walk, *walk_merged = NULL;
577   apr_pool_t *iterpool;
578   svn_stream_t *last_stream;
579   svn_stream_t *stream;
580   const char *target_abspath_or_url;
581
582   if (start->kind == svn_opt_revision_unspecified
583       || end->kind == svn_opt_revision_unspecified)
584     return svn_error_create
585       (SVN_ERR_CLIENT_BAD_REVISION, NULL, NULL);
586
587   if (svn_path_is_url(target))
588     target_abspath_or_url = target;
589   else
590     SVN_ERR(svn_dirent_get_absolute(&target_abspath_or_url, target, pool));
591
592   /* Get an RA plugin for this filesystem object. */
593   SVN_ERR(svn_client__ra_session_from_path2(&ra_session, &end_loc,
594                                             target, NULL, peg_revision, end,
595                                             ctx, pool));
596   end_revnum = end_loc->rev;
597
598   SVN_ERR(svn_client__get_revision_number(&start_revnum, NULL, ctx->wc_ctx,
599                                           target_abspath_or_url, ra_session,
600                                           start, pool));
601
602   if (end_revnum < start_revnum)
603     return svn_error_create
604       (SVN_ERR_CLIENT_BAD_REVISION, NULL,
605        _("Start revision must precede end revision"));
606
607   /* We check the mime-type of the yougest revision before getting all
608      the older revisions. */
609   if (!ignore_mime_type)
610     {
611       apr_hash_t *props;
612       apr_hash_index_t *hi;
613
614       SVN_ERR(svn_client_propget5(&props, NULL, SVN_PROP_MIME_TYPE,
615                                   target_abspath_or_url,  peg_revision,
616                                   end, NULL, svn_depth_empty, NULL, ctx,
617                                   pool, pool));
618
619       /* props could be keyed on URLs or paths depending on the
620          peg_revision and end values so avoid using the key. */
621       hi = apr_hash_first(pool, props);
622       if (hi)
623         {
624           svn_string_t *value;
625
626           /* Should only be one value */
627           SVN_ERR_ASSERT(apr_hash_count(props) == 1);
628
629           value = svn__apr_hash_index_val(hi);
630           if (value && svn_mime_type_is_binary(value->data))
631             return svn_error_createf
632               (SVN_ERR_CLIENT_IS_BINARY_FILE, 0,
633                _("Cannot calculate blame information for binary file '%s'"),
634                (svn_path_is_url(target)
635                 ? target : svn_dirent_local_style(target, pool)));
636         }
637     }
638
639   frb.start_rev = start_revnum;
640   frb.end_rev = end_revnum;
641   frb.target = target;
642   frb.ctx = ctx;
643   frb.diff_options = diff_options;
644   frb.include_merged_revisions = include_merged_revisions;
645   frb.last_filename = NULL;
646   frb.last_original_filename = NULL;
647   frb.chain = apr_palloc(pool, sizeof(*frb.chain));
648   frb.chain->blame = NULL;
649   frb.chain->avail = NULL;
650   frb.chain->pool = pool;
651   if (include_merged_revisions)
652     {
653       frb.merged_chain = apr_palloc(pool, sizeof(*frb.merged_chain));
654       frb.merged_chain->blame = NULL;
655       frb.merged_chain->avail = NULL;
656       frb.merged_chain->pool = pool;
657     }
658
659   SVN_ERR(svn_ra_get_repos_root2(ra_session, &frb.repos_root_url, pool));
660
661   frb.mainpool = pool;
662   /* The callback will flip the following two pools, because it needs
663      information from the previous call.  Obviously, it can't rely on
664      the lifetime of the pool provided by get_file_revs. */
665   frb.lastpool = svn_pool_create(pool);
666   frb.currpool = svn_pool_create(pool);
667   if (include_merged_revisions)
668     {
669       frb.filepool = svn_pool_create(pool);
670       frb.prevfilepool = svn_pool_create(pool);
671     }
672
673   /* Collect all blame information.
674      We need to ensure that we get one revision before the start_rev,
675      if available so that we can know what was actually changed in the start
676      revision. */
677   SVN_ERR(svn_ra_get_file_revs2(ra_session, "",
678                                 start_revnum - (start_revnum > 0 ? 1 : 0),
679                                 end_revnum, include_merged_revisions,
680                                 file_rev_handler, &frb, pool));
681
682   if (end->kind == svn_opt_revision_working)
683     {
684       /* If the local file is modified we have to call the handler on the
685          working copy file with keywords unexpanded */
686       svn_wc_status3_t *status;
687
688       SVN_ERR(svn_wc_status3(&status, ctx->wc_ctx, target_abspath_or_url, pool,
689                              pool));
690
691       if (status->text_status != svn_wc_status_normal
692           || (status->prop_status != svn_wc_status_normal
693               && status->prop_status != svn_wc_status_none))
694         {
695           svn_stream_t *wcfile;
696           svn_stream_t *tempfile;
697           svn_opt_revision_t rev;
698           svn_boolean_t normalize_eols = FALSE;
699           const char *temppath;
700
701           if (status->prop_status != svn_wc_status_none)
702             {
703               const svn_string_t *eol_style;
704               SVN_ERR(svn_wc_prop_get2(&eol_style, ctx->wc_ctx,
705                                        target_abspath_or_url,
706                                        SVN_PROP_EOL_STYLE,
707                                        pool, pool));
708
709               if (eol_style)
710                 {
711                   svn_subst_eol_style_t style;
712                   const char *eol;
713                   svn_subst_eol_style_from_value(&style, &eol, eol_style->data);
714
715                   normalize_eols = (style == svn_subst_eol_style_native);
716                 }
717             }
718
719           rev.kind = svn_opt_revision_working;
720           SVN_ERR(svn_client__get_normalized_stream(&wcfile, ctx->wc_ctx,
721                                                     target_abspath_or_url, &rev,
722                                                     FALSE, normalize_eols,
723                                                     ctx->cancel_func,
724                                                     ctx->cancel_baton,
725                                                     pool, pool));
726
727           SVN_ERR(svn_stream_open_unique(&tempfile, &temppath, NULL,
728                                          svn_io_file_del_on_pool_cleanup,
729                                          pool, pool));
730
731           SVN_ERR(svn_stream_copy3(wcfile, tempfile, ctx->cancel_func,
732                                    ctx->cancel_baton, pool));
733
734           SVN_ERR(add_file_blame(frb.last_filename, temppath, frb.chain, NULL,
735                                  frb.diff_options, pool));
736
737           frb.last_filename = temppath;
738         }
739     }
740
741   /* Report the blame to the caller. */
742
743   /* The callback has to have been called at least once. */
744   SVN_ERR_ASSERT(frb.last_filename != NULL);
745
746   /* Create a pool for the iteration below. */
747   iterpool = svn_pool_create(pool);
748
749   /* Open the last file and get a stream. */
750   SVN_ERR(svn_stream_open_readonly(&last_stream, frb.last_filename,
751                                    pool, pool));
752   stream = svn_subst_stream_translated(last_stream,
753                                        "\n", TRUE, NULL, FALSE, pool);
754
755   /* Perform optional merged chain normalization. */
756   if (include_merged_revisions)
757     {
758       /* If we never created any blame for the original chain, create it now,
759          with the most recent changed revision.  This could occur if a file
760          was created on a branch and them merged to another branch.  This is
761          semanticly a copy, and we want to use the revision on the branch as
762          the most recently changed revision.  ### Is this really what we want
763          to do here?  Do the sematics of copy change? */
764       if (!frb.chain->blame)
765         frb.chain->blame = blame_create(frb.chain, frb.rev, 0);
766
767       normalize_blames(frb.chain, frb.merged_chain, pool);
768       walk_merged = frb.merged_chain->blame;
769     }
770
771   /* Process each blame item. */
772   for (walk = frb.chain->blame; walk; walk = walk->next)
773     {
774       apr_off_t line_no;
775       svn_revnum_t merged_rev;
776       const char *merged_path;
777       apr_hash_t *merged_rev_props;
778
779       if (walk_merged)
780         {
781           merged_rev = walk_merged->rev->revision;
782           merged_rev_props = walk_merged->rev->rev_props;
783           merged_path = walk_merged->rev->path;
784         }
785       else
786         {
787           merged_rev = SVN_INVALID_REVNUM;
788           merged_rev_props = NULL;
789           merged_path = NULL;
790         }
791
792       for (line_no = walk->start;
793            !walk->next || line_no < walk->next->start;
794            ++line_no)
795         {
796           svn_boolean_t eof;
797           svn_stringbuf_t *sb;
798
799           svn_pool_clear(iterpool);
800           SVN_ERR(svn_stream_readline(stream, &sb, "\n", &eof, iterpool));
801           if (ctx->cancel_func)
802             SVN_ERR(ctx->cancel_func(ctx->cancel_baton));
803           if (!eof || sb->len)
804             {
805               if (walk->rev)
806                 SVN_ERR(receiver(receiver_baton, start_revnum, end_revnum,
807                                  line_no, walk->rev->revision,
808                                  walk->rev->rev_props, merged_rev,
809                                  merged_rev_props, merged_path,
810                                  sb->data, FALSE, iterpool));
811               else
812                 SVN_ERR(receiver(receiver_baton, start_revnum, end_revnum,
813                                  line_no, SVN_INVALID_REVNUM,
814                                  NULL, SVN_INVALID_REVNUM,
815                                  NULL, NULL,
816                                  sb->data, TRUE, iterpool));
817             }
818           if (eof) break;
819         }
820
821       if (walk_merged)
822         walk_merged = walk_merged->next;
823     }
824
825   SVN_ERR(svn_stream_close(stream));
826
827   svn_pool_destroy(frb.lastpool);
828   svn_pool_destroy(frb.currpool);
829   if (include_merged_revisions)
830     {
831       svn_pool_destroy(frb.filepool);
832       svn_pool_destroy(frb.prevfilepool);
833     }
834   svn_pool_destroy(iterpool);
835
836   return SVN_NO_ERROR;
837 }