monotone

monotone Mtn Source Tree

Root/annotate.cc

1// Copyright (C) 2005 Emile Snyder <emile@alumni.reed.edu>
2//
3// This program is made available under the GNU GPL version 2.0 or
4// greater. See the accompanying file COPYING for details.
5//
6// This program is distributed WITHOUT ANY WARRANTY; without even the
7// implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
8// PURPOSE.
9
10#include "base.hh"
11#include <set>
12#include <iostream>
13
14#include <boost/shared_ptr.hpp>
15#include <boost/multi_index_container.hpp>
16#include <boost/multi_index/ordered_index.hpp>
17#include <boost/multi_index/key_extractors.hpp>
18
19#include "annotate.hh"
20#include "cert.hh"
21#include "constants.hh"
22#include "cset.hh"
23#include "database.hh"
24#include "interner.hh"
25#include "lcs.hh"
26#include "platform.hh"
27#include "project.hh"
28#include "revision.hh"
29#include "sanity.hh"
30#include "simplestring_xform.hh"
31#include "transforms.hh"
32#include "ui.hh"
33#include "vocab.hh"
34#include "rev_height.hh"
35#include "roster.hh"
36
37using std::back_insert_iterator;
38using std::back_inserter;
39using std::cout;
40using std::map;
41using std::min;
42using std::set;
43using std::string;
44using std::vector;
45using std::pair;
46
47using boost::shared_ptr;
48
49using boost::multi_index::multi_index_container;
50using boost::multi_index::indexed_by;
51using boost::multi_index::ordered_unique;
52using boost::multi_index::tag;
53using boost::multi_index::member;
54
55
56class annotate_lineage_mapping;
57
58
59class annotate_context
60{
61public:
62 annotate_context(project_t & project, file_id fid);
63
64 shared_ptr<annotate_lineage_mapping> initial_lineage() const;
65
66 /// credit any uncopied lines (as recorded in copied_lines) to
67 /// rev, and reset copied_lines.
68 void evaluate(revision_id rev);
69
70 void set_copied(int index);
71 void set_touched(int index);
72
73 void set_equivalent(int index, int index2);
74 void annotate_equivalent_lines();
75
76 /// return true if we have no more unassigned lines
77 bool is_complete() const;
78
79 void dump(bool just_revs) const;
80
81 string get_line(int line_index) const
82 {
83 return file_lines[line_index];
84 }
85
86private:
87 void build_revisions_to_annotations(map<revision_id, string> & r2a) const;
88
89 project_t & project;
90
91 /// keep a count so we can tell quickly whether we can terminate
92 size_t annotated_lines_completed;
93
94 vector<string> file_lines;
95 vector<revision_id> annotations;
96
97 /// equivalent_lines[n] = m means that line n should be blamed to the same
98 /// revision as line m
99 map<int, int> equivalent_lines;
100
101 // elements of the set are indexes into the array of lines in the
102 // UDOI lineages add entries here when they notice that they copied
103 // a line from the UDOI
104 set<size_t> copied_lines;
105
106 // similarly, lineages add entries here for all the lines from the
107 // UDOI they know about that they didn't copy
108 set<size_t> touched_lines;
109};
110
111
112/*
113 An annotate_lineage_mapping tells you, for each line of a file,
114 where in the ultimate descendent of interest (UDOI) the line came
115 from (a line not present in the UDOI is represented as -1).
116*/
117class annotate_lineage_mapping
118{
119public:
120 annotate_lineage_mapping(file_data const & data);
121 annotate_lineage_mapping(vector<string> const & lines);
122
123 // debugging
124 //bool equal_interned (const annotate_lineage_mapping &rhs) const;
125
126 /// need a better name. does the work of setting copied bits in the
127 /// context object.
128 shared_ptr<annotate_lineage_mapping>
129 build_parent_lineage(shared_ptr<annotate_context> acp,
130 revision_id parent_rev,
131 file_data const & parent_data) const;
132
133 void merge(annotate_lineage_mapping const & other,
134 shared_ptr<annotate_context> const & acp);
135
136 void credit_mapped_lines(shared_ptr<annotate_context> acp) const;
137 void set_copied_all_mapped(shared_ptr<annotate_context> acp) const;
138
139private:
140 void init_with_lines(vector<string> const & lines);
141
142 static interner<long> in; // FIX, testing hack
143
144 vector<long, QA(long)> file_interned;
145
146 // maps an index into the vector of lines for our current version of
147 // the file into an index into the vector of lines of the UDOI:
148 // eg. if the line file_interned[i] will turn into line 4 in the
149 // UDOI, mapping[i] = 4
150 vector<int> mapping;
151};
152
153interner<long> annotate_lineage_mapping::in;
154
155
156/*
157 annotate_node_work encapsulates the input data needed to process
158 the annotations for a given childrev, considering all the
159 childrev -> parentrevN edges.
160*/
161struct annotate_node_work
162{
163 annotate_node_work(shared_ptr<annotate_context> annotations_,
164 shared_ptr<annotate_lineage_mapping> lineage_,
165 revision_id revision_, node_id fid_,
166 rev_height height_,
167 set<revision_id> interesting_ancestors_,
168 file_id content_,
169 bool marked_)
170 : annotations(annotations_),
171 lineage(lineage_),
172 revision(revision_),
173 fid(fid_),
174 height(height_),
175 interesting_ancestors(interesting_ancestors_),
176 content(content_),
177 marked(marked_)
178 {}
179
180 shared_ptr<annotate_context> annotations;
181 shared_ptr<annotate_lineage_mapping> lineage;
182 revision_id revision;
183 node_id fid;
184 rev_height height;
185 set<revision_id> interesting_ancestors;
186 file_id content;
187 bool marked;
188};
189
190struct by_rev {};
191
192// instead of using a priority queue and a set to keep track of the already
193// seen revisions, we use a multi index container. it stores work units
194// indexed by both, their revision and their revision's height, with the
195// latter being used by default. usage of that data structure frees us from
196// the burden of keeping two data structures in sync.
197typedef multi_index_container<
198 annotate_node_work,
199 indexed_by<
200 ordered_unique<
201 member<annotate_node_work,rev_height,&annotate_node_work::height>,
202 std::greater<rev_height> >,
203 ordered_unique<
204 tag<by_rev>,
205 member<annotate_node_work,revision_id,&annotate_node_work::revision> >
206 >
207 > work_units;
208
209
210annotate_context::annotate_context(project_t & project, file_id fid)
211 : project(project), annotated_lines_completed(0)
212{
213 // initialize file_lines
214 file_data fpacked;
215 project.db.get_file_version(fid, fpacked);
216 string encoding = constants::default_encoding; // FIXME
217 split_into_lines(fpacked.inner()(), encoding, file_lines);
218 L(FL("annotate_context::annotate_context initialized "
219 "with %d file lines\n") % file_lines.size());
220
221 // initialize annotations
222 revision_id nullid;
223 annotations.clear();
224 annotations.reserve(file_lines.size());
225 annotations.insert(annotations.begin(), file_lines.size(), nullid);
226 L(FL("annotate_context::annotate_context initialized "
227 "with %d entries in annotations\n") % annotations.size());
228
229 // initialize copied_lines and touched_lines
230 copied_lines.clear();
231 touched_lines.clear();
232}
233
234
235shared_ptr<annotate_lineage_mapping>
236annotate_context::initial_lineage() const
237{
238 shared_ptr<annotate_lineage_mapping>
239 res(new annotate_lineage_mapping(file_lines));
240 return res;
241}
242
243
244void
245annotate_context::evaluate(revision_id rev)
246{
247 revision_id nullid;
248 I(copied_lines.size() <= annotations.size());
249 I(touched_lines.size() <= annotations.size());
250
251 // Find the lines that we touched but that no other parent copied.
252 set<size_t> credit_lines;
253 set_difference(touched_lines.begin(), touched_lines.end(),
254 copied_lines.begin(), copied_lines.end(),
255 inserter(credit_lines, credit_lines.begin()));
256
257 set<size_t>::const_iterator i;
258 for (i = credit_lines.begin(); i != credit_lines.end(); i++)
259 {
260 I(*i < annotations.size());
261 if (annotations[*i] == nullid)
262 {
263 // L(FL("evaluate setting annotations[%d] -> %s, since "
264 // "touched_lines contained %d, copied_lines didn't and "
265 // "annotations[%d] was nullid\n") % *i % rev % *i % *i);
266
267 annotations[*i] = rev;
268 annotated_lines_completed++;
269 }
270 else
271 {
272 //L(FL("evaluate LEAVING annotations[%d] -> %s")
273 // % *i % annotations[*i]);
274 }
275 }
276
277 copied_lines.clear();
278 touched_lines.clear();
279}
280
281void
282annotate_context::set_copied(int index)
283{
284 //L(FL("annotate_context::set_copied %d") % index);
285
286 if (index == -1)
287 return;
288
289 I(index >= 0 && index < (int)file_lines.size());
290 copied_lines.insert(index);
291}
292
293void
294annotate_context::set_touched(int index)
295{
296 //L(FL("annotate_context::set_touched %d") % index);
297
298 if (index == -1)
299 return;
300
301 I(index >= 0 && index <= (int)file_lines.size());
302 touched_lines.insert(index);
303}
304
305void
306annotate_context::set_equivalent(int index, int index2)
307{
308 L(FL("annotate_context::set_equivalent "
309 "index %d index2 %d\n") % index % index2);
310 equivalent_lines[index] = index2;
311}
312
313void
314annotate_context::annotate_equivalent_lines()
315{
316 revision_id null_id;
317
318 for (size_t i=0; i<annotations.size(); i++)
319 {
320 if (annotations[i] == null_id)
321 {
322 map<int, int>::const_iterator j = equivalent_lines.find(i);
323 if (j == equivalent_lines.end())
324 {
325 L(FL("annotate_equivalent_lines unable to find "
326 "equivalent for line %d\n") % i);
327 }
328 I(j != equivalent_lines.end());
329 annotations[i] = annotations[j->second];
330 annotated_lines_completed++;
331 }
332 }
333}
334
335bool
336annotate_context::is_complete() const
337{
338 if (annotated_lines_completed == annotations.size())
339 return true;
340
341 I(annotated_lines_completed < annotations.size());
342 return false;
343}
344
345static string
346cert_string_value(vector< revision<cert> > const & certs,
347 cert_name const & name,
348 bool from_start, bool from_end,
349 string const & sep)
350{
351 for (vector< revision<cert> >::const_iterator i = certs.begin();
352 i != certs.end(); ++i)
353 {
354 if (i->inner().name == name)
355 {
356 cert_value tv(i->inner().value);
357 string::size_type f = 0;
358 string::size_type l = string::npos;
359 if (from_start)
360 l = tv ().find_first_of(sep);
361 if (from_end)
362 {
363 f = tv ().find_last_of(sep);
364 if (f == string::npos)
365 f = 0;
366 }
367 return tv().substr(f, l);
368 }
369 }
370
371 return "";
372}
373
374void
375annotate_context::build_revisions_to_annotations
376(map<revision_id, string> & revs_to_notations) const
377{
378 I(annotations.size() == file_lines.size());
379
380 // build set of unique revisions present in annotations
381 set<revision_id> seen;
382 for (vector<revision_id>::const_iterator i = annotations.begin();
383 i != annotations.end(); i++)
384 {
385 seen.insert(*i);
386 }
387
388 size_t max_note_length = 0;
389
390 // build revision -> annotation string mapping
391 for (set<revision_id>::const_iterator i = seen.begin();
392 i != seen.end(); i++)
393 {
394 vector< revision<cert> > certs;
395 project.get_revision_certs(*i, certs);
396 erase_bogus_certs(project.db, certs);
397
398 string author(cert_string_value(certs, author_cert_name,
399 true, false, "@< "));
400
401 string date(cert_string_value(certs, date_cert_name,
402 true, false, "T"));
403
404 string result;
405 string hex_rev_str(encode_hexenc(i->inner()()));
406 result.append(hex_rev_str.substr(0, 8));
407 result.append(".. by ");
408 result.append(author);
409 result.append(" ");
410 result.append(date);
411 result.append(": ");
412
413 max_note_length = ((result.size() > max_note_length)
414 ? result.size()
415 : max_note_length);
416 revs_to_notations[*i] = result;
417 }
418
419 // justify annotation strings
420 for (map<revision_id, string>::iterator i = revs_to_notations.begin();
421 i != revs_to_notations.end(); i++)
422 {
423 size_t l = i->second.size();
424 i->second.insert(string::size_type(0), max_note_length - l, ' ');
425 }
426}
427
428void
429annotate_context::dump(bool just_revs) const
430{
431 revision_id nullid;
432 I(annotations.size() == file_lines.size());
433
434 map<revision_id, string> revs_to_notations;
435 string empty_note;
436 if (!just_revs)
437 {
438 build_revisions_to_annotations(revs_to_notations);
439 size_t max_note_length = revs_to_notations.begin()->second.size();
440 empty_note.insert(string::size_type(0), max_note_length - 2, ' ');
441 }
442
443 revision_id lastid = nullid;
444 for (size_t i = 0; i < file_lines.size(); i++)
445 {
446 //I(! (annotations[i] == nullid) );
447 if (!just_revs)
448 {
449 if (lastid == annotations[i])
450 cout << empty_note << ": "
451 << file_lines[i] << '\n';
452 else
453 cout << revs_to_notations[annotations[i]]
454 << file_lines[i] << '\n';
455 lastid = annotations[i];
456 }
457 else
458 cout << annotations[i] << ": "
459 << file_lines[i] << '\n';
460 }
461}
462
463annotate_lineage_mapping::annotate_lineage_mapping(file_data const & data)
464{
465 // split into lines
466 vector<string> lines;
467 string encoding = constants::default_encoding; // FIXME
468 split_into_lines (data.inner()().data(), encoding, lines);
469
470 init_with_lines(lines);
471}
472
473annotate_lineage_mapping::annotate_lineage_mapping
474(vector<string> const & lines)
475{
476 init_with_lines(lines);
477}
478
479/*
480bool
481annotate_lineage_mapping::equal_interned
482(annotate_lineage_mapping const & rhs) const
483{
484 bool result = true;
485
486 if (file_interned.size() != rhs.file_interned.size()) {
487 L(FL("annotate_lineage_mapping::equal_interned "
488 "lhs size %d != rhs size %d\n")
489 % file_interned.size() % rhs.file_interned.size());
490 result = false;
491 }
492
493 size_t limit = min(file_interned.size(), rhs.file_interned.size());
494 for (size_t i=0; i<limit; i++)
495 {
496 if (file_interned[i] != rhs.file_interned[i])
497 {
498 L(FL("annotate_lineage_mapping::equal_interned "
499 "lhs[%d]:%ld != rhs[%d]:%ld\n")
500 % i % file_interned[i] % i % rhs.file_interned[i]);
501 result = false;
502 }
503 }
504
505 return result;
506}
507*/
508
509void
510annotate_lineage_mapping::init_with_lines(vector<string> const & lines)
511{
512 file_interned.clear();
513 file_interned.reserve(lines.size());
514 mapping.clear();
515 mapping.reserve(lines.size());
516
517 int count;
518 vector<string>::const_iterator i;
519 for (count=0, i = lines.begin(); i != lines.end(); i++, count++)
520 {
521 file_interned.push_back(in.intern(*i));
522 mapping.push_back(count);
523 }
524 L(FL("annotate_lineage_mapping::init_with_lines "
525 "ending with %d entries in mapping\n") % mapping.size());
526}
527
528shared_ptr<annotate_lineage_mapping>
529annotate_lineage_mapping::build_parent_lineage
530(shared_ptr<annotate_context> acp,
531 revision_id parent_rev,
532 file_data const & parent_data) const
533{
534 bool verbose = false;
535 shared_ptr<annotate_lineage_mapping>
536 parent_lineage(new annotate_lineage_mapping(parent_data));
537
538 vector<long, QA(long)> lcs;
539 back_insert_iterator< vector<long, QA(long)> > bii(lcs);
540 longest_common_subsequence(file_interned.begin(),
541 file_interned.end(),
542 parent_lineage->file_interned.begin(),
543 parent_lineage->file_interned.end(),
544 min(file_interned.size(),
545 parent_lineage->file_interned.size()),
546 back_inserter(lcs));
547
548 if (verbose)
549 L(FL("build_parent_lineage: "
550 "file_lines.size() == %d, "
551 "parent.file_lines.size() == %d, "
552 "lcs.size() == %d\n")
553 % file_interned.size()
554 % parent_lineage->file_interned.size()
555 % lcs.size());
556
557 // do the copied lines thing for our annotate_context
558 vector<long> lcs_src_lines;
559 lcs_src_lines.resize(lcs.size());
560 size_t i, j;
561 i = j = 0;
562 while (i < file_interned.size() && j < lcs.size())
563 {
564 //if (verbose)
565 if (file_interned[i] == 14)
566 L(FL("%s file_interned[%d]: %ld\tlcs[%d]: %ld\tmapping[%d]: %ld")
567 % parent_rev % i % file_interned[i] % j % lcs[j] % i % mapping[i]);
568
569 if (file_interned[i] == lcs[j])
570 {
571 acp->set_copied(mapping[i]);
572 lcs_src_lines[j] = mapping[i];
573 j++;
574 }
575 else
576 {
577 acp->set_touched(mapping[i]);
578 }
579
580 i++;
581 }
582 if (verbose)
583 L(FL("loop ended with i: %d, j: %d, lcs.size(): %d")
584 % i % j % lcs.size());
585 I(j == lcs.size());
586
587 // set touched for the rest of the lines in the file
588 while (i < file_interned.size())
589 {
590 acp->set_touched(mapping[i]);
591 i++;
592 }
593
594 // determine the mapping for parent lineage
595 if (verbose)
596 L(FL("build_parent_lineage: building mapping now "
597 "for parent_rev %s\n") % parent_rev);
598
599 i = j = 0;
600
601 while (i < parent_lineage->file_interned.size() && j < lcs.size())
602 {
603 if (parent_lineage->file_interned[i] == lcs[j])
604 {
605 parent_lineage->mapping[i] = lcs_src_lines[j];
606 j++;
607 }
608 else
609 {
610 parent_lineage->mapping[i] = -1;
611 }
612 if (verbose)
613 L(FL("mapping[%d] -> %d") % i % parent_lineage->mapping[i]);
614
615 i++;
616 }
617 I(j == lcs.size());
618 // set mapping for the rest of the lines in the file
619 while (i < parent_lineage->file_interned.size())
620 {
621 parent_lineage->mapping[i] = -1;
622 if (verbose)
623 L(FL("mapping[%d] -> %d") % i % parent_lineage->mapping[i]);
624 i++;
625 }
626
627 return parent_lineage;
628}
629
630void
631annotate_lineage_mapping::merge(annotate_lineage_mapping const & other,
632 shared_ptr<annotate_context> const & acp)
633{
634 I(file_interned.size() == other.file_interned.size());
635 I(mapping.size() == other.mapping.size());
636 //I(equal_interned(other)); // expensive check
637
638 for (size_t i=0; i<mapping.size(); i++)
639 {
640 if (mapping[i] == -1 && other.mapping[i] >= 0)
641 mapping[i] = other.mapping[i];
642
643 if (mapping[i] >= 0 && other.mapping[i] >= 0)
644 {
645 //I(mapping[i] == other.mapping[i]);
646 if (mapping[i] != other.mapping[i])
647 {
648 // a given line in the current merged mapping will split
649 // and become multiple lines in the UDOI. so we have to
650 // remember that whenever we ultimately assign blame for
651 // mapping[i] we blame the same revision on
652 // other.mapping[i].
653 acp->set_equivalent(other.mapping[i], mapping[i]);
654 }
655 }
656 }
657}
658
659void
660annotate_lineage_mapping::credit_mapped_lines
661(shared_ptr<annotate_context> acp) const
662{
663 vector<int>::const_iterator i;
664 for (i=mapping.begin(); i != mapping.end(); i++)
665 {
666 acp->set_touched(*i);
667 }
668}
669
670void
671annotate_lineage_mapping::set_copied_all_mapped
672(shared_ptr<annotate_context> acp) const
673{
674 vector<int>::const_iterator i;
675 for (i=mapping.begin(); i != mapping.end(); i++)
676 {
677 acp->set_copied(*i);
678 }
679}
680
681// fetches the list of file_content markings for the given revision_id and
682// node_id
683static void get_file_content_marks(database & db,
684 revision_id const & rev,
685 node_id const & fid,
686 set<revision_id> & content_marks)
687{
688 marking_t markings;
689 db.get_markings(rev, fid, markings);
690
691 I(!markings.file_content.empty());
692
693 content_marks.clear();
694 content_marks.insert(markings.file_content.begin(),
695 markings.file_content.end());
696}
697
698static void
699do_annotate_node(database & db,
700 annotate_node_work const & work_unit,
701 work_units & work_units)
702{
703 L(FL("do_annotate_node for node %s") % work_unit.revision);
704
705 size_t added_in_parent_count = 0;
706
707 for (set<revision_id>::const_iterator i = work_unit.interesting_ancestors.begin();
708 i != work_unit.interesting_ancestors.end(); i++)
709 {
710 // here, 'parent' means either a real parent or one of the marked
711 // ancestors, depending on whether work_unit.marked is true.
712 revision_id parent_revision = *i;
713
714 L(FL("do_annotate_node processing edge from parent %s to child %s")
715 % parent_revision % work_unit.revision);
716
717 I(!(work_unit.revision == parent_revision));
718
719 file_id file_in_parent;
720
721 work_units::index<by_rev>::type::iterator lmn =
722 work_units.get<by_rev>().find(parent_revision);
723
724 // find out the content hash of the file in parent.
725 if (lmn != work_units.get<by_rev>().end())
726 // we already got the content hash.
727 file_in_parent = lmn->content;
728 else
729 {
730 if (work_unit.marked)
731 db.get_file_content(parent_revision, work_unit.fid, file_in_parent);
732 else
733 // we are not marked, so parent is marked.
734 file_in_parent = work_unit.content;
735 }
736
737 // stop if file is not present in the parent.
738 if (null_id(file_in_parent))
739 {
740 L(FL("file added in %s, continuing") % work_unit.revision);
741 added_in_parent_count++;
742 continue;
743 }
744
745 // the node was live in the parent, so this represents a delta.
746 shared_ptr<annotate_lineage_mapping> parent_lineage;
747
748 if (file_in_parent == work_unit.content)
749 {
750 L(FL("parent file identical, "
751 "set copied all mapped and copy lineage\n"));
752 parent_lineage = work_unit.lineage;
753 parent_lineage->set_copied_all_mapped(work_unit.annotations);
754 }
755 else
756 {
757 file_data data;
758 db.get_file_version(file_in_parent, data);
759 L(FL("building parent lineage for parent file %s")
760 % file_in_parent);
761 parent_lineage
762 = work_unit.lineage->build_parent_lineage(work_unit.annotations,
763 parent_revision,
764 data);
765 }
766
767 // If this parent has not yet been queued for processing, create the
768 // work unit for it.
769 if (lmn == work_units.get<by_rev>().end())
770 {
771 set<revision_id> parents_interesting_ancestors;
772 bool parent_marked;
773
774 if (work_unit.marked)
775 {
776 // we are marked, thus we don't know a priori whether parent
777 // is marked or not.
778 get_file_content_marks(db, parent_revision, work_unit.fid, parents_interesting_ancestors);
779 parent_marked = (parents_interesting_ancestors.size() == 1
780 && *(parents_interesting_ancestors.begin()) == parent_revision);
781 }
782 else
783 parent_marked = true;
784
785 // if it's marked, we need to look at its parents instead.
786 if (parent_marked)
787 db.get_revision_parents(parent_revision, parents_interesting_ancestors);
788
789 rev_height parent_height;
790 db.get_rev_height(parent_revision, parent_height);
791 annotate_node_work newunit(work_unit.annotations,
792 parent_lineage,
793 parent_revision,
794 work_unit.fid,
795 parent_height,
796 parents_interesting_ancestors,
797 file_in_parent,
798 parent_marked);
799 work_units.insert(newunit);
800 }
801 else
802 {
803 // already a pending node, so we just have to merge the lineage.
804 L(FL("merging lineage from node %s to parent %s")
805 % work_unit.revision % parent_revision);
806
807 lmn->lineage->merge(*parent_lineage, work_unit.annotations);
808 }
809 }
810
811 if (added_in_parent_count == work_unit.interesting_ancestors.size())
812 {
813 work_unit.lineage->credit_mapped_lines(work_unit.annotations);
814 }
815
816 work_unit.annotations->evaluate(work_unit.revision);
817}
818
819void
820do_annotate (project_t & project, file_t file_node,
821 revision_id rid, bool just_revs)
822{
823 L(FL("annotating file %s with content %s in revision %s")
824 % file_node->self % file_node->content % rid);
825
826 shared_ptr<annotate_context>
827 acp(new annotate_context(project, file_node->content));
828
829 shared_ptr<annotate_lineage_mapping> lineage
830 = acp->initial_lineage();
831
832 work_units work_units;
833 {
834 // prepare the first work_unit
835 rev_height height;
836 project.db.get_rev_height(rid, height);
837 set<revision_id> rids_interesting_ancestors;
838 get_file_content_marks(project.db, rid, file_node->self,
839 rids_interesting_ancestors);
840 bool rid_marked = (rids_interesting_ancestors.size() == 1
841 && *(rids_interesting_ancestors.begin()) == rid);
842 if (rid_marked)
843 project.db.get_revision_parents(rid, rids_interesting_ancestors);
844
845 annotate_node_work workunit(acp, lineage, rid, file_node->self, height,
846 rids_interesting_ancestors, file_node->content,
847 rid_marked);
848 work_units.insert(workunit);
849 }
850
851 while (!(work_units.empty() || acp->is_complete()))
852 {
853 // get the work unit for the revision with the greatest height
854 work_units::iterator w = work_units.begin();
855 I(w != work_units.end());
856
857 // do_annotate_node() might insert new work units into work_units, and
858 // thus might invalidate the iterator
859 annotate_node_work work = *w;
860 work_units.erase(w);
861
862 do_annotate_node(project.db, work, work_units);
863 }
864
865 acp->annotate_equivalent_lines();
866 I(acp->is_complete());
867
868 acp->dump(just_revs);
869}
870
871// Local Variables:
872// mode: C++
873// fill-column: 76
874// c-file-style: "gnu"
875// indent-tabs-mode: nil
876// End:
877// vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s:

Archive Download this file

Branches

Tags

Quick Links:     www.monotone.ca    -     Downloads    -     Documentation    -     Wiki    -     Code Forge    -     Build Status