/monotone-1.0/src/annotate.cc
C++ | 902 lines | 635 code | 135 blank | 132 comment | 108 complexity | f380dec0e122a3c51efc7b8510256f37 MD5 | raw file
Possible License(s): GPL-2.0
- // Copyright (C) 2005 Emile Snyder <emile@alumni.reed.edu>
- //
- // This program is made available under the GNU GPL version 2.0 or
- // greater. See the accompanying file COPYING for details.
- //
- // This program is distributed WITHOUT ANY WARRANTY; without even the
- // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
- // PURPOSE.
- #include "base.hh"
- #include <set>
- #include <iostream>
- #include <boost/shared_ptr.hpp>
- #include <boost/multi_index_container.hpp>
- #include <boost/multi_index/ordered_index.hpp>
- #include <boost/multi_index/key_extractors.hpp>
- #include "annotate.hh"
- #include "cert.hh"
- #include "constants.hh"
- #include "cset.hh"
- #include "database.hh"
- #include "date_format.hh"
- #include "interner.hh"
- #include "lcs.hh"
- #include "platform.hh"
- #include "project.hh"
- #include "revision.hh"
- #include "sanity.hh"
- #include "simplestring_xform.hh"
- #include "transforms.hh"
- #include "ui.hh"
- #include "vocab.hh"
- #include "rev_height.hh"
- #include "roster.hh"
- using std::back_insert_iterator;
- using std::back_inserter;
- using std::cout;
- using std::map;
- using std::min;
- using std::set;
- using std::string;
- using std::vector;
- using std::pair;
- using boost::shared_ptr;
- using boost::multi_index::multi_index_container;
- using boost::multi_index::indexed_by;
- using boost::multi_index::ordered_unique;
- using boost::multi_index::tag;
- using boost::multi_index::member;
- class annotate_lineage_mapping;
- class annotate_context
- {
- public:
- annotate_context(app_state & app, project_t & project, file_id fid);
- shared_ptr<annotate_lineage_mapping> initial_lineage() const;
- /// credit any uncopied lines (as recorded in copied_lines) to
- /// rev, and reset copied_lines.
- void evaluate(revision_id rev);
- void set_copied(int index);
- void set_touched(int index);
- void set_equivalent(int index, int index2);
- void annotate_equivalent_lines();
- /// return true if we have no more unassigned lines
- bool is_complete() const;
- void dump(bool just_revs) const;
- string get_line(int line_index) const
- {
- return file_lines[line_index];
- }
- private:
- void build_revisions_to_annotations(map<revision_id, string> & r2a) const;
- app_state & app;
- project_t & project;
- /// keep a count so we can tell quickly whether we can terminate
- size_t annotated_lines_completed;
- vector<string> file_lines;
- vector<revision_id> annotations;
- /// equivalent_lines[n] = m means that line n should be blamed to the same
- /// revision as line m
- map<int, int> equivalent_lines;
- // elements of the set are indexes into the array of lines in the
- // UDOI lineages add entries here when they notice that they copied
- // a line from the UDOI
- set<size_t> copied_lines;
- // similarly, lineages add entries here for all the lines from the
- // UDOI they know about that they didn't copy
- set<size_t> touched_lines;
- };
- /*
- An annotate_lineage_mapping tells you, for each line of a file,
- where in the ultimate descendent of interest (UDOI) the line came
- from (a line not present in the UDOI is represented as -1).
- */
- class annotate_lineage_mapping
- {
- public:
- annotate_lineage_mapping(file_data const & data);
- annotate_lineage_mapping(vector<string> const & lines);
- // debugging
- //bool equal_interned (const annotate_lineage_mapping &rhs) const;
- /// need a better name. does the work of setting copied bits in the
- /// context object.
- shared_ptr<annotate_lineage_mapping>
- build_parent_lineage(shared_ptr<annotate_context> acp,
- revision_id parent_rev,
- file_data const & parent_data) const;
- void merge(annotate_lineage_mapping const & other,
- shared_ptr<annotate_context> const & acp);
- void credit_mapped_lines(shared_ptr<annotate_context> acp) const;
- void set_copied_all_mapped(shared_ptr<annotate_context> acp) const;
- private:
- void init_with_lines(vector<string> const & lines);
- static interner<long> in; // FIX, testing hack
- vector<long, QA(long)> file_interned;
- // maps an index into the vector of lines for our current version of
- // the file into an index into the vector of lines of the UDOI:
- // eg. if the line file_interned[i] will turn into line 4 in the
- // UDOI, mapping[i] = 4
- vector<int> mapping;
- };
- interner<long> annotate_lineage_mapping::in;
- /*
- annotate_node_work encapsulates the input data needed to process
- the annotations for a given childrev, considering all the
- childrev -> parentrevN edges.
- */
- struct annotate_node_work
- {
- annotate_node_work(shared_ptr<annotate_context> annotations_,
- shared_ptr<annotate_lineage_mapping> lineage_,
- revision_id revision_, node_id fid_,
- rev_height height_,
- set<revision_id> interesting_ancestors_,
- file_id content_,
- bool marked_)
- : annotations(annotations_),
- lineage(lineage_),
- revision(revision_),
- fid(fid_),
- height(height_),
- interesting_ancestors(interesting_ancestors_),
- content(content_),
- marked(marked_)
- {}
- shared_ptr<annotate_context> annotations;
- shared_ptr<annotate_lineage_mapping> lineage;
- revision_id revision;
- node_id fid;
- rev_height height;
- set<revision_id> interesting_ancestors;
- file_id content;
- bool marked;
- };
- struct by_rev {};
- // instead of using a priority queue and a set to keep track of the already
- // seen revisions, we use a multi index container. it stores work units
- // indexed by both, their revision and their revision's height, with the
- // latter being used by default. usage of that data structure frees us from
- // the burden of keeping two data structures in sync.
- typedef multi_index_container<
- annotate_node_work,
- indexed_by<
- ordered_unique<
- member<annotate_node_work,rev_height,&annotate_node_work::height>,
- std::greater<rev_height> >,
- ordered_unique<
- tag<by_rev>,
- member<annotate_node_work,revision_id,&annotate_node_work::revision> >
- >
- > work_units;
- annotate_context::annotate_context(app_state & app, project_t & project, file_id fid)
- : app(app), project(project), annotated_lines_completed(0)
- {
- // initialize file_lines
- file_data fpacked;
- project.db.get_file_version(fid, fpacked);
- string encoding = constants::default_encoding; // FIXME
- split_into_lines(fpacked.inner()(), encoding, file_lines);
- L(FL("annotate_context::annotate_context initialized "
- "with %d file lines\n") % file_lines.size());
- // initialize annotations
- revision_id nullid;
- annotations.clear();
- annotations.reserve(file_lines.size());
- annotations.insert(annotations.begin(), file_lines.size(), nullid);
- L(FL("annotate_context::annotate_context initialized "
- "with %d entries in annotations\n") % annotations.size());
- // initialize copied_lines and touched_lines
- copied_lines.clear();
- touched_lines.clear();
- }
- shared_ptr<annotate_lineage_mapping>
- annotate_context::initial_lineage() const
- {
- shared_ptr<annotate_lineage_mapping>
- res(new annotate_lineage_mapping(file_lines));
- return res;
- }
- void
- annotate_context::evaluate(revision_id rev)
- {
- revision_id nullid;
- I(copied_lines.size() <= annotations.size());
- I(touched_lines.size() <= annotations.size());
- // Find the lines that we touched but that no other parent copied.
- set<size_t> credit_lines;
- set_difference(touched_lines.begin(), touched_lines.end(),
- copied_lines.begin(), copied_lines.end(),
- inserter(credit_lines, credit_lines.begin()));
- set<size_t>::const_iterator i;
- for (i = credit_lines.begin(); i != credit_lines.end(); i++)
- {
- I(*i < annotations.size());
- if (annotations[*i] == nullid)
- {
- // L(FL("evaluate setting annotations[%d] -> %s, since "
- // "touched_lines contained %d, copied_lines didn't and "
- // "annotations[%d] was nullid\n") % *i % rev % *i % *i);
- annotations[*i] = rev;
- annotated_lines_completed++;
- }
- else
- {
- //L(FL("evaluate LEAVING annotations[%d] -> %s")
- // % *i % annotations[*i]);
- }
- }
- copied_lines.clear();
- touched_lines.clear();
- }
- void
- annotate_context::set_copied(int index)
- {
- //L(FL("annotate_context::set_copied %d") % index);
- if (index == -1)
- return;
- I(index >= 0 && index < (int)file_lines.size());
- copied_lines.insert(index);
- }
- void
- annotate_context::set_touched(int index)
- {
- //L(FL("annotate_context::set_touched %d") % index);
- if (index == -1)
- return;
- I(index >= 0 && index <= (int)file_lines.size());
- touched_lines.insert(index);
- }
- void
- annotate_context::set_equivalent(int index, int index2)
- {
- L(FL("annotate_context::set_equivalent "
- "index %d index2 %d\n") % index % index2);
- equivalent_lines[index] = index2;
- }
- void
- annotate_context::annotate_equivalent_lines()
- {
- revision_id null_id;
- for (size_t i=0; i<annotations.size(); i++)
- {
- if (annotations[i] == null_id)
- {
- map<int, int>::const_iterator j = equivalent_lines.find(i);
- if (j == equivalent_lines.end())
- {
- L(FL("annotate_equivalent_lines unable to find "
- "equivalent for line %d\n") % i);
- }
- I(j != equivalent_lines.end());
- annotations[i] = annotations[j->second];
- annotated_lines_completed++;
- }
- }
- }
- bool
- annotate_context::is_complete() const
- {
- if (annotated_lines_completed == annotations.size())
- return true;
- I(annotated_lines_completed < annotations.size());
- return false;
- }
- static string
- cert_string_value(vector<cert> const & certs,
- cert_name const & name,
- bool from_start, bool from_end,
- string const & sep)
- {
- for (vector<cert>::const_iterator i = certs.begin();
- i != certs.end(); ++i)
- {
- if (i->name == name)
- {
- cert_value tv(i->value);
- string::size_type f = 0;
- string::size_type l = string::npos;
- if (from_start)
- l = tv ().find_first_of(sep);
- if (from_end)
- {
- f = tv ().find_last_of(sep);
- if (f == string::npos)
- f = 0;
- }
- return tv().substr(f, l);
- }
- }
- return "";
- }
- static string
- cert_date_value(vector<cert> const & certs,
- cert_name const & name,
- bool from_start, bool from_end,
- string const & fmt)
- {
- string certval = cert_string_value(certs, name, from_start, from_end, "");
- if (fmt.empty() || certval.empty())
- return certval;
- return date_t(certval).as_formatted_localtime(fmt);
- }
- void
- annotate_context::build_revisions_to_annotations
- (map<revision_id, string> & revs_to_notations) const
- {
- I(annotations.size() == file_lines.size());
- // build set of unique revisions present in annotations
- set<revision_id> seen;
- for (vector<revision_id>::const_iterator i = annotations.begin();
- i != annotations.end(); i++)
- {
- seen.insert(*i);
- }
- size_t max_note_length = 0;
- string date_fmt = get_date_format(app.opts, app.lua, date_short);
- // build revision -> annotation string mapping
- for (set<revision_id>::const_iterator i = seen.begin();
- i != seen.end(); i++)
- {
- vector<cert> certs;
- project.get_revision_certs(*i, certs);
- project.db.erase_bogus_certs(project, certs);
- string author(cert_string_value(certs, author_cert_name,
- true, false, "@< "));
- string date(cert_date_value(certs, date_cert_name, true, false, date_fmt));
- string hex_rev_str(encode_hexenc(i->inner()(), i->inner().made_from));
- string result;
- // author and / or date could be empty for two reasons
- // a) the specific certs are untrusted by the user, and as such
- // got erased earlier from the set
- // b) the specific revision does not contain (for whatever reason)
- // the needed certificate
- // for both reasons we change the output slightly so that no unwanted
- // spaces pop up
- if (!author.empty() && !date.empty())
- result = (F("%s.. by '%s' %s: ") % hex_rev_str.substr(0, 8) % author % date).str();
- else if (!author.empty())
- result = (F("%s.. by '%s': ") % hex_rev_str.substr(0, 8) % author).str();
- else if (!date.empty())
- result = (F("%s.. %s: ") % hex_rev_str.substr(0, 8) % date).str();
- else
- result = (F("%s..: ") % hex_rev_str.substr(0, 8)).str();
- max_note_length = ((result.size() > max_note_length)
- ? result.size()
- : max_note_length);
- revs_to_notations[*i] = result;
- }
- // justify annotation strings
- for (map<revision_id, string>::iterator i = revs_to_notations.begin();
- i != revs_to_notations.end(); i++)
- {
- size_t l = i->second.size();
- i->second.insert(string::size_type(0), max_note_length - l, ' ');
- }
- }
- void
- annotate_context::dump(bool just_revs) const
- {
- revision_id nullid;
- I(annotations.size() == file_lines.size());
- if (file_lines.empty())
- return;
- map<revision_id, string> revs_to_notations;
- string empty_note;
- if (!just_revs)
- {
- build_revisions_to_annotations(revs_to_notations);
- size_t max_note_length = revs_to_notations.begin()->second.size();
- empty_note.insert(string::size_type(0), max_note_length - 2, ' ');
- }
- revision_id lastid = nullid;
- for (size_t i = 0; i < file_lines.size(); i++)
- {
- //I(! (annotations[i] == nullid) );
- if (!just_revs)
- {
- if (lastid == annotations[i])
- cout << empty_note << ": "
- << file_lines[i] << '\n';
- else
- cout << revs_to_notations[annotations[i]]
- << file_lines[i] << '\n';
- lastid = annotations[i];
- }
- else
- cout << annotations[i] << ": "
- << file_lines[i] << '\n';
- }
- }
- annotate_lineage_mapping::annotate_lineage_mapping(file_data const & data)
- {
- // split into lines
- vector<string> lines;
- string encoding = constants::default_encoding; // FIXME
- split_into_lines (data.inner()().data(), encoding, lines);
- init_with_lines(lines);
- }
- annotate_lineage_mapping::annotate_lineage_mapping
- (vector<string> const & lines)
- {
- init_with_lines(lines);
- }
- /*
- bool
- annotate_lineage_mapping::equal_interned
- (annotate_lineage_mapping const & rhs) const
- {
- bool result = true;
- if (file_interned.size() != rhs.file_interned.size()) {
- L(FL("annotate_lineage_mapping::equal_interned "
- "lhs size %d != rhs size %d\n")
- % file_interned.size() % rhs.file_interned.size());
- result = false;
- }
- size_t limit = min(file_interned.size(), rhs.file_interned.size());
- for (size_t i=0; i<limit; i++)
- {
- if (file_interned[i] != rhs.file_interned[i])
- {
- L(FL("annotate_lineage_mapping::equal_interned "
- "lhs[%d]:%ld != rhs[%d]:%ld\n")
- % i % file_interned[i] % i % rhs.file_interned[i]);
- result = false;
- }
- }
- return result;
- }
- */
- void
- annotate_lineage_mapping::init_with_lines(vector<string> const & lines)
- {
- file_interned.clear();
- file_interned.reserve(lines.size());
- mapping.clear();
- mapping.reserve(lines.size());
- int count;
- vector<string>::const_iterator i;
- for (count=0, i = lines.begin(); i != lines.end(); i++, count++)
- {
- file_interned.push_back(in.intern(*i));
- mapping.push_back(count);
- }
- L(FL("annotate_lineage_mapping::init_with_lines "
- "ending with %d entries in mapping\n") % mapping.size());
- }
- shared_ptr<annotate_lineage_mapping>
- annotate_lineage_mapping::build_parent_lineage
- (shared_ptr<annotate_context> acp,
- revision_id parent_rev,
- file_data const & parent_data) const
- {
- bool verbose = false;
- shared_ptr<annotate_lineage_mapping>
- parent_lineage(new annotate_lineage_mapping(parent_data));
- vector<long, QA(long)> lcs;
- back_insert_iterator< vector<long, QA(long)> > bii(lcs);
- longest_common_subsequence(file_interned.begin(),
- file_interned.end(),
- parent_lineage->file_interned.begin(),
- parent_lineage->file_interned.end(),
- back_inserter(lcs));
- if (verbose)
- L(FL("build_parent_lineage: "
- "file_lines.size() == %d, "
- "parent.file_lines.size() == %d, "
- "lcs.size() == %d\n")
- % file_interned.size()
- % parent_lineage->file_interned.size()
- % lcs.size());
- // do the copied lines thing for our annotate_context
- vector<long> lcs_src_lines;
- lcs_src_lines.resize(lcs.size());
- size_t i, j;
- i = j = 0;
- while (i < file_interned.size() && j < lcs.size())
- {
- //if (verbose)
- if (file_interned[i] == 14)
- L(FL("%s file_interned[%d]: %ld\tlcs[%d]: %ld\tmapping[%d]: %ld")
- % parent_rev % i % file_interned[i] % j % lcs[j] % i % mapping[i]);
- if (file_interned[i] == lcs[j])
- {
- acp->set_copied(mapping[i]);
- lcs_src_lines[j] = mapping[i];
- j++;
- }
- else
- {
- acp->set_touched(mapping[i]);
- }
- i++;
- }
- if (verbose)
- L(FL("loop ended with i: %d, j: %d, lcs.size(): %d")
- % i % j % lcs.size());
- I(j == lcs.size());
- // set touched for the rest of the lines in the file
- while (i < file_interned.size())
- {
- acp->set_touched(mapping[i]);
- i++;
- }
- // determine the mapping for parent lineage
- if (verbose)
- L(FL("build_parent_lineage: building mapping now "
- "for parent_rev %s\n") % parent_rev);
- i = j = 0;
- while (i < parent_lineage->file_interned.size() && j < lcs.size())
- {
- if (parent_lineage->file_interned[i] == lcs[j])
- {
- parent_lineage->mapping[i] = lcs_src_lines[j];
- j++;
- }
- else
- {
- parent_lineage->mapping[i] = -1;
- }
- if (verbose)
- L(FL("mapping[%d] -> %d") % i % parent_lineage->mapping[i]);
- i++;
- }
- I(j == lcs.size());
- // set mapping for the rest of the lines in the file
- while (i < parent_lineage->file_interned.size())
- {
- parent_lineage->mapping[i] = -1;
- if (verbose)
- L(FL("mapping[%d] -> %d") % i % parent_lineage->mapping[i]);
- i++;
- }
- return parent_lineage;
- }
- void
- annotate_lineage_mapping::merge(annotate_lineage_mapping const & other,
- shared_ptr<annotate_context> const & acp)
- {
- I(file_interned.size() == other.file_interned.size());
- I(mapping.size() == other.mapping.size());
- //I(equal_interned(other)); // expensive check
- for (size_t i=0; i<mapping.size(); i++)
- {
- if (mapping[i] == -1 && other.mapping[i] >= 0)
- mapping[i] = other.mapping[i];
- if (mapping[i] >= 0 && other.mapping[i] >= 0)
- {
- //I(mapping[i] == other.mapping[i]);
- if (mapping[i] != other.mapping[i])
- {
- // a given line in the current merged mapping will split
- // and become multiple lines in the UDOI. so we have to
- // remember that whenever we ultimately assign blame for
- // mapping[i] we blame the same revision on
- // other.mapping[i].
- acp->set_equivalent(other.mapping[i], mapping[i]);
- }
- }
- }
- }
- void
- annotate_lineage_mapping::credit_mapped_lines
- (shared_ptr<annotate_context> acp) const
- {
- vector<int>::const_iterator i;
- for (i=mapping.begin(); i != mapping.end(); i++)
- {
- acp->set_touched(*i);
- }
- }
- void
- annotate_lineage_mapping::set_copied_all_mapped
- (shared_ptr<annotate_context> acp) const
- {
- vector<int>::const_iterator i;
- for (i=mapping.begin(); i != mapping.end(); i++)
- {
- acp->set_copied(*i);
- }
- }
- // fetches the list of file_content markings for the given revision_id and
- // node_id
- static void get_file_content_marks(database & db,
- revision_id const & rev,
- node_id const & fid,
- set<revision_id> & content_marks)
- {
- const_marking_t markings;
- db.get_markings(rev, fid, markings);
- I(!markings->file_content.empty());
- content_marks.clear();
- content_marks.insert(markings->file_content.begin(),
- markings->file_content.end());
- }
- static void
- do_annotate_node(database & db,
- annotate_node_work const & work_unit,
- work_units & work_units)
- {
- L(FL("do_annotate_node for node %s") % work_unit.revision);
- size_t added_in_parent_count = 0;
- for (set<revision_id>::const_iterator i = work_unit.interesting_ancestors.begin();
- i != work_unit.interesting_ancestors.end(); i++)
- {
- // here, 'parent' means either a real parent or one of the marked
- // ancestors, depending on whether work_unit.marked is true.
- revision_id parent_revision = *i;
- L(FL("do_annotate_node processing edge from parent %s to child %s")
- % parent_revision % work_unit.revision);
- I(!(work_unit.revision == parent_revision));
- file_id file_in_parent;
- work_units::index<by_rev>::type::iterator lmn =
- work_units.get<by_rev>().find(parent_revision);
- // find out the content hash of the file in parent.
- if (lmn != work_units.get<by_rev>().end())
- // we already got the content hash.
- file_in_parent = lmn->content;
- else
- {
- if (work_unit.marked)
- db.get_file_content(parent_revision, work_unit.fid, file_in_parent);
- else
- // we are not marked, so parent is marked.
- file_in_parent = work_unit.content;
- }
- // stop if file is not present in the parent.
- if (null_id(file_in_parent))
- {
- L(FL("file added in %s, continuing") % work_unit.revision);
- added_in_parent_count++;
- continue;
- }
- // the node was live in the parent, so this represents a delta.
- shared_ptr<annotate_lineage_mapping> parent_lineage;
- if (file_in_parent == work_unit.content)
- {
- L(FL("parent file identical, "
- "set copied all mapped and copy lineage\n"));
- parent_lineage = work_unit.lineage;
- parent_lineage->set_copied_all_mapped(work_unit.annotations);
- }
- else
- {
- file_data data;
- db.get_file_version(file_in_parent, data);
- L(FL("building parent lineage for parent file %s")
- % file_in_parent);
- parent_lineage
- = work_unit.lineage->build_parent_lineage(work_unit.annotations,
- parent_revision,
- data);
- }
- // If this parent has not yet been queued for processing, create the
- // work unit for it.
- if (lmn == work_units.get<by_rev>().end())
- {
- set<revision_id> parents_interesting_ancestors;
- bool parent_marked;
- if (work_unit.marked)
- {
- // we are marked, thus we don't know a priori whether parent
- // is marked or not.
- get_file_content_marks(db, parent_revision, work_unit.fid, parents_interesting_ancestors);
- parent_marked = (parents_interesting_ancestors.size() == 1
- && *(parents_interesting_ancestors.begin()) == parent_revision);
- }
- else
- parent_marked = true;
- // if it's marked, we need to look at its parents instead.
- if (parent_marked)
- db.get_revision_parents(parent_revision, parents_interesting_ancestors);
- rev_height parent_height;
- db.get_rev_height(parent_revision, parent_height);
- annotate_node_work newunit(work_unit.annotations,
- parent_lineage,
- parent_revision,
- work_unit.fid,
- parent_height,
- parents_interesting_ancestors,
- file_in_parent,
- parent_marked);
- work_units.insert(newunit);
- }
- else
- {
- // already a pending node, so we just have to merge the lineage.
- L(FL("merging lineage from node %s to parent %s")
- % work_unit.revision % parent_revision);
- lmn->lineage->merge(*parent_lineage, work_unit.annotations);
- }
- }
- if (added_in_parent_count == work_unit.interesting_ancestors.size())
- {
- work_unit.lineage->credit_mapped_lines(work_unit.annotations);
- }
- work_unit.annotations->evaluate(work_unit.revision);
- }
- void
- do_annotate (app_state & app, project_t & project, const_file_t file_node,
- revision_id rid, bool just_revs)
- {
- L(FL("annotating file %s with content %s in revision %s")
- % file_node->self % file_node->content % rid);
- shared_ptr<annotate_context>
- acp(new annotate_context(app, project, file_node->content));
- shared_ptr<annotate_lineage_mapping> lineage
- = acp->initial_lineage();
- work_units work_units;
- {
- // prepare the first work_unit
- rev_height height;
- project.db.get_rev_height(rid, height);
- set<revision_id> rids_interesting_ancestors;
- get_file_content_marks(project.db, rid, file_node->self,
- rids_interesting_ancestors);
- bool rid_marked = (rids_interesting_ancestors.size() == 1
- && *(rids_interesting_ancestors.begin()) == rid);
- if (rid_marked)
- project.db.get_revision_parents(rid, rids_interesting_ancestors);
- annotate_node_work workunit(acp, lineage, rid, file_node->self, height,
- rids_interesting_ancestors, file_node->content,
- rid_marked);
- work_units.insert(workunit);
- }
- while (!(work_units.empty() || acp->is_complete()))
- {
- // get the work unit for the revision with the greatest height
- work_units::iterator w = work_units.begin();
- I(w != work_units.end());
- // do_annotate_node() might insert new work units into work_units, and
- // thus might invalidate the iterator
- annotate_node_work work = *w;
- work_units.erase(w);
- do_annotate_node(project.db, work, work_units);
- }
- acp->annotate_equivalent_lines();
- I(acp->is_complete());
- acp->dump(just_revs);
- }
- // Local Variables:
- // mode: C++
- // fill-column: 76
- // c-file-style: "gnu"
- // indent-tabs-mode: nil
- // End:
- // vim: et:sw=2:sts=2:ts=2:cino=>2s,{s,\:s,+s,t0,g0,^-2,e-2,n-2,p2s,(0,=s: