#
# add_file "roster4.cc"
#
# add_file "roster4.hh"
#
# patch "Makefile.am"
# from [4fd07091c0eb1958cfbafbbd3ad1b8bdc22a776d]
# to [21195f0067eb2ce1cd508365e9c7221a3bd17236]
#
# patch "cset.hh"
# from [9e59f30c4d529d3c406e32cc2ebbf52499fb3d93]
# to [27a33d01fa88feb466ef9a7e911e43cf7478fa3f]
#
# patch "roster4.cc"
# from []
# to [980cb70419bb563c22f62385ecc34363b1e0484e]
#
# patch "roster4.hh"
# from []
# to [d4c48d7ec8ea1c8092b520ee9d8b230c50a0786f]
#
========================================================================
--- Makefile.am 4fd07091c0eb1958cfbafbbd3ad1b8bdc22a776d
+++ Makefile.am 21195f0067eb2ce1cd508365e9c7221a3bd17236
@@ -32,6 +32,7 @@
revision.cc revision.hh \
change_set.cc change_set.hh \
cset.cc cset.hh \
+ roster4.cc roster4.hh \
mt_version.cc mt_version.hh \
automate.cc automate.hh \
database_check.cc database_check.hh \
========================================================================
--- cset.hh 9e59f30c4d529d3c406e32cc2ebbf52499fb3d93
+++ cset.hh 27a33d01fa88feb466ef9a7e911e43cf7478fa3f
@@ -26,7 +26,7 @@
// destructively; this may be the filesystem or an in-memory
// representation (a roster / mfest).
-typedef u64 node_id;
+typedef u32 node_id;
struct editable_tree
{
========================================================================
--- roster4.cc
+++ roster4.cc 980cb70419bb563c22f62385ecc34363b1e0484e
@@ -0,0 +1,1040 @@
+// copyright (C) 2005 nathaniel smith
+// copyright (C) 2005 graydon hoare
+// all rights reserved.
+// licensed to the public under the terms of the GNU GPL (>= 2)
+// see the file COPYING for details
+
+
+#include "roster4.hh"
+#include "vocab.hh"
+#include
+
+using std::inserter;
+using std::make_pair;
+using std::map;
+using std::pair;
+using std::reverse;
+using std::set;
+using std::stack;
+
+
+///////////////////////////////////////////////////////////////////
+
+template
+void
+safe_erase(T & container, typename T::key_type const & key)
+{
+ I(container.erase(key));
+}
+
+template
+typename T::iterator
+safe_insert(T & container, typename T::value_type const & val)
+{
+ std::pair r = container.insert(val);
+ I(r.second);
+ return r.first;
+}
+
+template
+typename T::mapped_type const &
+safe_get(T & container, typename T::key_type const & key)
+{
+ typename T::const_iterator i = container.find(key);
+ I(i != container.end());
+ return i->second;
+}
+
+///////////////////////////////////////////////////////////////////
+
+namespace
+{
+ //
+ // We have a few concepts of "nullness" here:
+ //
+ // - the_null_node is a node_id. It does not correspond to a real node;
+ // it's an id you use for the parent of the root, or of any node which
+ // is detached.
+ //
+ // - the_null_component is a path_component. It is the *name* of the root
+ // node. Its string representation is "", the empty string.
+ //
+ // - The split_path corresponding to the_null_node is [], the empty vector.
+ //
+ // - The split_path corresponding to the root node is [""], the 1-element
+ // vector containing the_null_component.
+ //
+ // - The split_path corresponding to foo/bar is ["", "foo", "bar"].
+ //
+ // - The only legal one-element split_path is [""], referring to the
+ // root node.
+ //
+ // We do this in order to support the notion of moving the root
+ // directory around, or applying attributes to the root directory. A
+ // roster is not considered "sane" for writing to disk unless it has a
+ // root node, but it can assume a transient state during editing in
+ // which there is no root node (if you detach it, to move it somewhere
+ // else in the tree).
+ //
+ // Note that this means that "adds" and "rename second-halfs" should
+ // be intermixed as top-down attaches, so that a "new" root node is
+ // added before a renamed root node is moved elsewhere; just as
+ // "deletes" and "rename first-halfs" should be intermixed as
+ // bottom-up detaches.
+ //
+
+
+ const node_id the_null_node = 0;
+ const node_id first_node = 1;
+ inline bool null_node(node_id n)
+ {
+ return n == the_null_node;
+ }
+
+ const node_id first_temp_node = widen(1) << (sizeof(node_id) * 8 - 1);
+ inline bool temp_node(node_id n)
+ {
+ return n & first_temp_node;
+ }
+}
+
+node::node()
+ : self(the_null_node),
+ parent(the_null_node),
+ name(the_null_component)
+{
+}
+
+node_t
+dir_node::get_child(path_component const & pc) const
+{
+ return safe_get(children, pc);
+}
+
+
+void
+dir_node::attach_child(path_component const & pc, node_t child)
+{
+ I(!null_node(child->parent));
+ I(!null_name(child->name));
+ safe_insert(children, make_pair(pc, child));
+ child->parent = this->self;
+ child->name = pc;
+}
+
+
+node_t
+dir_node::detach_child(path_component const & pc)
+{
+ node_t n = get_child(pc);
+ n->parent = the_null_node;
+ n->name = the_null_component;
+ safe_erase(children, pc);
+ return n;
+}
+
+node_t
+dir_node::clone()
+{
+ dir_t d = dir_t(new dir_node());
+ d->birth_revision = birth_revision;
+ d->self = self;
+ d->parent = parent;
+ d->name = name;
+ d->attrs = attrs;
+ d->children = children;
+ return d;
+}
+
+node_t
+file_node::clone()
+{
+ file_t f = file_t(new file_node());
+ f->birth_revision = birth_revision;
+ f->self = self;
+ f->parent = parent;
+ f->name = name;
+ f->attrs = attrs;
+ f->content = content;
+ return f;
+}
+
+
+static inline void
+dirname_basename(split_path const & sp,
+ split_path & dirname, path_component & basename)
+{
+ I(!sp.empty());
+ split_path::const_iterator penultimate = sp.end();
+ --penultimate;
+ dirname = split_path(sp.begin(), penultimate);
+ if (dirname.empty())
+ I(null_name(basename));
+ basename = *penultimate;
+}
+
+
+bool
+roster_t::has_root() const
+{
+ return static_cast(root_dir);
+}
+
+
+node_t
+roster_t::get_node(split_path const & sp) const
+{
+ split_path dirname;
+ path_component basename;
+ dirname_basename(sp, dirname, basename);
+
+ if (dirname.empty())
+ {
+ I(null_name(basename));
+ return root_dir;
+ }
+
+ I(has_root());
+ dir_t d = root_dir;
+ for (split_path::const_iterator i = dirname.begin(); i != dirname.end(); ++i)
+ d = downcast_to_dir_t(d->get_child(*i));
+ return d->get_child(basename);
+}
+
+
+node_t
+roster_t::get_node(node_id nid) const
+{
+ return safe_get(nodes, nid);
+}
+
+
+void
+roster_t::get_name(node_id nid, split_path & sp) const
+{
+ sp.clear();
+ while (!null_node(nid))
+ {
+ node_t n = get_node(nid);
+ sp.push_back(n->name);
+ nid = n->parent;
+ }
+ reverse(sp.begin(), sp.end());
+}
+
+
+void
+roster_t::replace_node_id(node_id from, node_id to)
+{
+ node_t n = get_node(from);
+ safe_erase(nodes, from);
+ safe_insert(nodes, make_pair(to, n));
+ n->self = to;
+
+ if (is_dir_t(n))
+ {
+ dir_t d = downcast_to_dir_t(n);
+ for (dir_map::iterator i = d->children.begin(); i != d->children.end(); ++i)
+ {
+ I(i->second->parent == from);
+ i->second->parent = to;
+ }
+ }
+}
+
+
+node_id
+roster_t::detach_node(split_path const & pth)
+{
+ split_path dirname;
+ path_component basename;
+ dirname_basename(pth, dirname, basename);
+
+ if (dirname.empty())
+ {
+ // detaching the root dir
+ I(null_name(basename));
+ node_id root_id = root_dir->self;
+ // cleare set the root_dir shared_pointer
+ root_dir.reset();
+ return root_id;
+ }
+
+ dir_t parent = downcast_to_dir_t(get_node(dirname));
+ return parent->detach_child(basename)->self;
+}
+
+
+void
+roster_t::drop_detached_node(node_id nid)
+{
+ // ensure the node is already detached (as best one can)
+ node_t n = get_node(nid);
+ I(null_node(n->parent));
+ I(null_name(n->name));
+ safe_erase(nodes, nid);
+}
+
+
+node_id
+roster_t::create_dir_node(node_id_source & nis)
+{
+ node_id nid = nis.next();
+ safe_insert(nodes, make_pair(nid, dir_t(new dir_node())));
+ return nid;
+}
+
+
+node_id
+roster_t::create_file_node(file_id const & content, node_id_source & nis)
+{
+ node_id nid = nis.next();
+ file_t f = file_t(new file_node());
+ f->content = content;
+ safe_insert(nodes, make_pair(nid, f));
+ return nid;
+}
+
+
+void
+roster_t::attach_node(node_id nid, split_path const & dst)
+{
+ split_path dirname;
+ path_component basename;
+ dirname_basename(dst, dirname, basename);
+
+ node_t n = get_node(nid);
+
+ // ensure the node is already detached (as best one can)
+ I(null_node(n->parent));
+ I(null_name(n->name));
+
+ if (dirname.empty())
+ {
+ // attaching the root dir
+ I(null_name(basename));
+ I(!has_root());
+ root_dir = downcast_to_dir_t(n);
+ }
+ else
+ {
+ dir_t parent = downcast_to_dir_t(get_node(dirname));
+ parent->attach_child(basename, n);
+ }
+}
+
+
+void
+roster_t::apply_delta(split_path const & pth,
+ file_id const & old_id,
+ file_id const & new_id)
+{
+ file_t f = downcast_to_file_t(get_node(pth));
+ I(f->content == old_id);
+ I(!(f->content == new_id));
+ f->content = new_id;
+}
+
+
+void
+roster_t::clear_attr(split_path const & pth,
+ attr_name const & name)
+{
+ set_attr(pth, name, make_pair(false, attr_val()));
+}
+
+
+void
+roster_t::set_attr(split_path const & pth,
+ attr_name const & name,
+ attr_val const & val)
+{
+ set_attr(pth, name, make_pair(true, val));
+}
+
+
+void
+roster_t::set_attr(split_path const & pth,
+ attr_name const & name,
+ pair const & val)
+{
+ I(val.first || val.second.empty());
+ node_t n = get_node(pth);
+ full_attr_map::iterator i = n->attrs.find(name);
+ if (i == n->attrs.end())
+ i = safe_insert(n->attrs, make_pair(name,
+ make_pair(false, attr_val())));
+ I(i->second != val);
+ i->second = val;
+}
+
+marking_t::marking_t(revision_id const & birth_rid, node_t n)
+{
+ set singleton;
+ singleton.insert(birth_rid);
+ parent_name = singleton;
+ file_content = singleton;
+ for (full_attr_map::const_iterator i = n->attrs.begin();
+ i != n->attrs.end(); ++i)
+ attrs.insert(make_pair(i->first, singleton));
+}
+
+
+struct
+dfs_iter
+{
+
+ dir_t root;
+ bool return_root;
+ stack< pair > stk;
+ split_path dirname;
+
+ dfs_iter(dir_t r)
+ : root(r), return_root(true)
+ {
+ if (!root->children.empty())
+ stk.push(make_pair(root, root->children.begin()));
+ }
+
+ void path(split_path & pv)
+ {
+ I(!finished());
+ if (return_root)
+ {
+ pv.clear();
+ pv.push_back(the_null_component);
+ }
+ else
+ {
+ I(!stk.empty());
+ pv = dirname;
+ pv.push_back(stk.top().second->first);
+ }
+ }
+
+ bool finished()
+ {
+ return (!return_root) && stk.empty();
+ }
+
+ node_t operator*()
+ {
+ if (return_root)
+ {
+ return_root = false;
+ return root;
+ }
+ else
+ {
+ I(!stk.empty());
+ return stk.top().second->second;
+ }
+ }
+
+ void operator++()
+ {
+ if (finished())
+ return;
+
+ return_root = false;
+
+ node_t ntmp = stk.top().second->second;
+ if (is_dir_t(ntmp))
+ {
+ dirname.push_back(stk.top().second->first);
+ dir_t dtmp = downcast_to_dir_t(ntmp);
+ stk.push(make_pair(dtmp, dtmp->children.begin()));
+ }
+ else
+ ++(stk.top().second);
+
+ while (!stk.empty()
+ && stk.top().second == stk.top().first->children.end())
+ {
+ stk.pop();
+ if (!dirname.empty())
+ dirname.pop_back();
+ if (!stk.empty())
+ ++stk.top().second;
+ }
+ }
+};
+
+
+void
+roster_t::check_finite_depth() const
+{
+ I(has_root());
+ size_t maxdepth = nodes.size();
+ for (dfs_iter i(root_dir); !i.finished(); ++i)
+ I(maxdepth-- > 0);
+}
+
+namespace
+{
+ // FIXME: remove these someday, when we've found a proper home for
+ // them
+ inline bool
+ null_id(file_id const & i)
+ {
+ return i.inner()().empty();
+ }
+
+ inline bool
+ null_id(revision_id const & i)
+ {
+ return i.inner()().empty();
+ }
+}
+
+void
+roster_t::check_sane(marking_map const & marking) const
+{
+ node_map::const_iterator ri;
+ marking_map::const_iterator mi;
+
+ I(has_root());
+
+ for (ri = nodes.begin(), mi = marking.begin();
+ ri != nodes.end() && mi != marking.end();
+ ++ri, ++mi)
+ {
+ I(ri->first == mi->first);
+ node_id nid = ri->first;
+ I(!null_node(nid) && !temp_node(nid));
+ node_t n = ri->second;
+ if (is_dir_t(n))
+ {
+ if (null_name(n->name) || null_node(n->parent))
+ I(null_name(n->name) && null_node(n->parent));
+ else
+ I(!null_name(n->name) && !null_node(n->parent));
+ }
+ else
+ {
+ I(!null_name(n->name) && !null_node(n->parent));
+ !null_id(downcast_to_file_t(n)->content);
+ }
+ I(!null_id(n->birth_revision));
+ for (full_attr_map::const_iterator i = n->attrs.begin(); i != n->attrs.end(); ++i)
+ I(i->second.first || !i->second.second.empty());
+ if (n != root_dir)
+ I(downcast_to_dir_t(get_node(n->parent))->get_child(n->name) == n);
+ }
+
+ I(ri == nodes.end() && mi == marking.end());
+ check_finite_depth();
+}
+
+
+/// Remainder is not yet compiling, so commented out
+
+
+/*
+
+
+namespace
+{
+ struct temp_node_id_source : public node_id_source
+ {
+ temp_node_id_source() : curr(first_temp_node) {}
+ virtual node_id next()
+ {
+ node_id n = curr++;
+ I(temp_node(n));
+ return n;
+ }
+ node_id curr;
+ };
+
+ struct true_node_id_source : public node_id_source
+ {
+ true_node_id_source(app_state & app) : app(app) {}
+ virtual node_id next()
+ {
+ node_id n = app.db.next_node_id();
+ I(!temp_node(n));
+ return n;
+ }
+ app_state & app;
+ };
+
+ // adaptor class to enable cset application on rosters.
+ class editable_roster_base : public editable_tree
+ {
+ public:
+ editable_roster(roster_t & r, node_id_source & nis)
+ : r(r), nis(nis)
+ {}
+
+ virtual node_id detach_node(split_path const & src)
+ {
+ return r.detch_node(src);
+ }
+ virtual void drop_detached_node(node_id nid)
+ {
+ r.drop_detached_node(nid);
+ }
+ virtual node_id create_dir_node()
+ {
+ return r.create_dir_node(nis);
+ }
+ virtual node_id create_file_node(file_id const & content)
+ {
+ return r.create_file_node(content, nis);
+ }
+ virtual void attach_node(node_id nid, split_path const & dst)
+ {
+ r.attach_node(nid, dst);
+ }
+ virtual void apply_delta(split_path const & pth,
+ file_id const & old_id,
+ file_id const & new_id)
+ {
+ r.apply_delta(pth, old_id, new_id);
+ }
+ virtual void clear_attr(split_path const & pth,
+ attr_name const & name)
+ {
+ r.clear_attr(pth, name);
+ }
+ virtual void set_attr(split_path const & pth,
+ attr_name const & name,
+ attr_val const & val)
+ {
+ r.set_attr(pth, name, val);
+ }
+ protected:
+ roster_t & r;
+ node_id_source & nis;
+ };
+
+ class editable_roster_for_merge : editable_roster_base
+ {
+ public:
+ set new_nodes;
+ virtual node_id create_dir_node()
+ {
+ node_id nid = this->editable_roster_base::create_dir_node();
+ new_nodes.insert(nid);
+ return nid;
+ }
+ virtual node_id create_file_node()
+ {
+ node_id nid = this->editable_roster_base::create_file_node();
+ new_nodes.insert(nid);
+ return nid;
+ }
+ };
+
+ // this handles all the stuff in a_new
+ void unify_roster_oneway(roster_t & a, set & a_new,
+ roster_t & b, set & b_new,
+ set & new_ids,
+ node_id_source & nis)
+ {
+ for (set::const_iterator i = a_new.begin(); i != a_new.end(); ++i)
+ {
+ node_id const aid = *i;
+ split_path sp;
+ // SPEEDUP?: climb out only so far as is necessary to find a shared
+ // id? possibly faster (since usually will get a hit immediately),
+ // but may not be worth the effort (since it doesn't take that long to
+ // get out in any case)
+ a.get_name(aid, sp);
+ node_id const bid = b.lookup(aid);
+ if (temp_node(bid))
+ {
+ node_id new_nid = nis.next();
+ a.replace_node_id(ls, new_nid);
+ b.replace_node_id(rs, new_nid);
+ new_ids.insert(new_nid);
+ b_new.erase(bid);
+ }
+ else
+ {
+ a.replace_node_id(aid, bid);
+ a.node(bid).birth_revision = b.node(bid).birth_revision;
+ }
+ }
+ }
+
+ // after this, left should == right, and there should be no temporary ids
+ // destroys sets, because that's handy (it has to scan over both, but it can
+ // skip some double-scanning)
+ void
+ unify_rosters(roster_t & left, set & left_new,
+ roster_t & right, set & right_new,
+ // these new_souls all come from the given soul source
+ set & new_ids,
+ node_id_source & nis)
+ {
+ unify_roster_oneway(left, left_new, right, right_new, new_souls, ss);
+ unify_roster_oneway(right, right_new, left, left_new, new_souls, ss);
+ }
+
+ // this function implements the case
+ // a b1
+ // \ /
+ // b2
+ void
+ mark_won_merge(set const & a_marks,
+ set const & a_uncommon_ancestors,
+ set const & b1_marks,
+ revision_id const & new_rid,
+ set & new_marks)
+ {
+ for (set::const_iterator i = a_marks.begin();
+ i != a_marks.end(); ++i)
+ {
+ if (a_uncommon_ancestors.find(*j) != a_uncommon_ancestors.end())
+ {
+ // at least one element of *(a) is not an ancestor of b1
+ new_marks.clear();
+ new_marks.insert(new_rid);
+ return;
+ }
+ }
+ // all elements of *(a) are ancestors of b1; this was a clean merge to b,
+ // so copy forward the marks.
+ new_marks = b1_marks;
+ }
+
+ void
+ mark_attrs(full_attr_map const & lattrs, full_attr_map const & rattrs,
+ marking_t const & lmarks, marking_t const & rmarks,
+ set const & left_uncommon_ancestors,
+ set const & right_uncommon_ancestors,
+ full_attr_map const & attrs, marking_t & marks)
+ {
+ for (full_attr_map::const_iterator j = attrs.begin(); j != attrs.end();
+ ++j)
+ {
+ full_attr_map::const_iterator lai = lattrs.find(j->first);
+ full_attr_map::const_iterator rai = rattrs.find(j->first);
+ if (lai == lattrs.end() && rai == rattrs.end())
+ marks.attrs[j->first].insert(new_rid);
+ else if (lai == lattrs.end() && rai != rattrs.end())
+ marks.attrs.insert(*rmarks.attrs.find(j->first));
+ else if (lai != lattrs.end() && rai == rattrs.end())
+ marks.attrs.insert(*lmarks.attrs.find(j->first));
+ else
+ {
+ bool diff_from_left = (j->second != lai->second);
+ bool diff_from_right = (j->second != rai->second);
+ if (diff_from_left && diff_from_right)
+ new_marks.attrs.insert(make_pair(j->first, new_rid));
+ else if (diff_from_left && !diff_from_right)
+ mark_won_merge(*lmarks.attrs.find(j->first),
+ left_uncommon_ancestors,
+ *rmarks.attrs.find(j->first),
+ new_rid, marks.attrs[j->first]);
+ else if (!diff_from_left && diff_from_right)
+ mark_won_merge(*rmarks.attrs.find(j->first),
+ right_uncommon_ancestors,
+ *lmarks.attrs.find(j->first),
+ new_rid, marks.attrs[j->first]);
+ else
+ set_union(lmarks.attrs.find(j->first)->begin(),
+ lmarks.attrs.find(j->first)->end(),
+ rmarks.attrs.find(j->first)->begin(),
+ rmarks.attrs.find(j->first)->end(),
+ inserter(marks.attrs[j->first]));
+ }
+ }
+ }
+
+ // take care of marking a single node both of whose parents exist
+ void
+ mark_nontrivial_node(node_t const & ln, node_t const & rn,
+ marking_t const & lmarks, marking_t const & rmarks,
+ set left_uncommon_ancestors,
+ set right_uncommon_ancestors,
+ node_t const & n, marking_t & marks)
+ {
+ // name
+ {
+ bool diff_from_left = (n.parent != ln.parent || n.name != ln.name);
+ bool diff_from_right = (n.parent != rn.parent || n.name != rn.name);
+ if (diff_from_left && diff_from_right)
+ new_marks.parent_name.insert(new_rid);
+ else if (diff_from_left && !diff_from_right)
+ mark_won_merge(lmarks.parent_name, left_uncommon_ancestors,
+ rmarks.parent_name, new_rid,
+ marks.parent_name);
+ else if (!diff_from_left && diff_from_right)
+ mark_won_merge(rmarks.parent_name, right_uncommon_ancestors,
+ lmarks.parent_name, new_rid,
+ marks.parent_name);
+ else
+ {
+ // this is the case
+ // a a
+ // \ /
+ // a
+ // so we simply union the mark sets. This is technically not
+ // quite the canonical multi-*-merge thing to do; in the case
+ // a1*
+ // / \
+ // b a2
+ // | |
+ // a3* |
+ // \ /
+ // a4
+ // we will set *(a4) = {a1, a3}, even though the minimal
+ // common ancestor set is {a3}. we could fix this by running
+ // erase_ancestors. However, there isn't really any point;
+ // the only operation performed on *(a4) is to test *(a4) > R
+ // for some revision R. The truth-value of this test cannot
+ // be affected by added new revisions to *(a4) that are
+ // ancestors of revisions that are already in *(a4).
+ set_union(lmarks.parent_name.begin(), lmarks.parent_name.end(),
+ rmarks.parent_name.begin(), rmarks.parent_name.end(),
+ inserter(marks.parent_name));
+ }
+ }
+ // content
+ if (n.type == ntype_file)
+ {
+ bool diff_from_left = (n.content != ln.content);
+ bool diff_from_right = (n.content != rn.content);
+ if (diff_from_left && diff_from_right)
+ new_marks.file_content.insert(new_rid);
+ else if (diff_from_left && !diff_from_right)
+ mark_won_merge(lmarks.file_content, left_uncommon_ancestors,
+ rmarks.file_content, new_rid,
+ marks.file_content);
+ else if (!diff_from_left && diff_from_right)
+ mark_won_merge(rmarks.file_content, right_uncommon_ancestors,
+ lmarks.file_content, new_rid,
+ marks.file_content);
+ else
+ set_union(lmarks.file_content.begin(), lmarks.file_content.end(),
+ rmarks.file_content.begin(), rmarks.file_content.end(),
+ inserter(marks.file_content));
+ }
+ // attrs are pain, and thus get their own function
+ mark_attrs(ln.attrs, rn.attrs, lmarks, rmarks,
+ left_uncommon_ancestors, right_uncommon_ancestors,
+ attrs, marks);
+ }
+
+ // this function is also responsible for verifying ancestry invariants --
+ // those invariants on a roster that involve the structure of the roster's
+ // parents, rather than just the structure of the roster itself.
+ void
+ mark_merge_roster(roster_t const & left_r, roster_t const & right_r,
+ marking_map const & left_marking,
+ marking_map const & right_marking,
+ set const & left_uncommon_ancestors,
+ set const & right_uncommon_ancestors,
+ roster_t const & merge, marking_map & marking)
+ {
+ for (map::const_iterator i = merge.all_nodes().begin();
+ i != merge.all_nodes().end(); ++i)
+ {
+ node_t const & n = i->second;
+ // SPEEDUP?: instead of using find repeatedly, iterate everything in
+ // parallel
+ map::const_iterator lni = left_r.all_nodes().find(i->first);
+ map::const_iterator rni = right_r.all_nodes().find(i->first);
+ marking_map::const_iterator lmi = left_marking.find(i->first);
+ marking_map::const_iterator rmi = right_marking.find(i->first);
+ bool exists_in_left = (lni != left_r.all_nodes.end());
+ bool exists_in_right = (rni != right_r.all_nodes.end());
+ if (!exists_in_left && !exists_in_right)
+ {
+ marking.insert(make_pair(i->first, marking_t(new_rid)));
+ I(n.birth_revision == new_rid);
+ }
+ else if (!exists_in_left && exists_in_right)
+ {
+ marking.insert(*rni);
+ node_t const & rn = rni->second;
+ I(n.type == rn.type && n.birth_revision == rn.birth_revision);
+ I(right_uncommon_ancestors.find(n.birth_revision)
+ != right_uncommon_ancestors.end());
+ }
+ else if (exists_in_left && !exists_in_right)
+ {
+ marking.insert(*lni);
+ node_t const & ln = lni->second;
+ I(n.type == ln.type && n.birth_revision == ln.birth_revision);
+ I(left_uncommon_ancestors.find(n.birth_revision)
+ != left_uncommon_ancestors.end());
+ }
+ else
+ {
+ node_t const & ln = lni->second;
+ node_t const & rn = rni->second;
+ I(n.type == rn.type && n.birth_revision == rn.birth_revision);
+ I(n.type == ln.type && n.birth_revision == ln.birth_revision);
+ marking_t marks;
+ marking_t const & lmarks = lmi->second;
+ marking_t const & rmarks = rmi->second;
+ mark_nontrivial_node(ln, rn, lmi->second, rmi->second,
+ left_uncommon_ancestors, right_uncommon_ancestors,
+ n, marks);
+ // attributes can never be deleted.
+ // this is kinda inefficent, but very rarely will any node have
+ // more than 1 attribute.
+ for (full_attr_map::const_iterator j = ln.attrs.begin();
+ j != ln.attrs.end(); ++j)
+ I(n.attrs.find(j->first) != n.attrs.end());
+ for (full_attr_map::const_iterator j = rn.attrs.begin();
+ j != rn.attrs.end(); ++j)
+ I(n.attrs.find(j->first) != n.attrs.end());
+ }
+ }
+ }
+}
+
+void
+make_roster_for_merge(cset const & left_cs, revision_id const & left_rid,
+ cset const & right_cs, revision_id const & right_rid,
+ revision_id const & new_rid,
+ roster_t & result, marking_map & marking, app_state & app)
+{
+ I(!null_id(left_rid) && !null_id(right_rid));
+ roster_t left_r, right_r;
+ marking_map left_marking, right_marking;
+ app.db.get_roster(left_rid, left_r, left_marking);
+ app.db.get_roster(right_rid, right_r, right_marking);
+ {
+ temp_node_id_source nis;
+ // SPEEDUP?: the copies on the next two lines are probably the main
+ // bottleneck in this code
+ result = left_r;
+ roster_t from_right_r(right_r);
+ editable_roster from_left_er(result, nis), from_right_er(from_right_r, nis);
+ left_cs.apply_to(from_left_er);
+ right_cs.apply_to(from_right_er);
+ set new_ids;
+ unify_rosters(result, from_left_er.new_nodes,
+ from_right_r, from_right_er.new_nodes,
+ new_ids, true_node_id_source(app));
+ I(result == new_from_right);
+ }
+ // SPEEDUP?: instead of constructing new marking from scratch, track which
+ // nodes were modified, and scan only them
+ // load one of the parent markings directly into the new marking map
+ marking.clear();
+ set left_uncommon_ancestors, right_uncommon_ancestors;
+ app.db.get_uncommon_ancestors(left_rid, right_rid,
+ left_uncommon_ancestors, right_uncommon_ancestors);
+ mark_merge_roster(left_r, right_r, left_marking, right_marking,
+ left_uncommon_ancestors, right_uncommon_ancestors,
+ result, marking);
+}
+
+namespace
+{
+ class editable_roster_for_nonmerge : editable_roster_base
+ {
+ public:
+ editable_roster_for_nonmerge(roster_t & r, node_id_source & nis,
+ revision_id const & rid,
+ marking_map & marking)
+ : editable_roster_base(r, nis),
+ rid(rid), marking(marking)
+ {}
+ virtual node_id detach_node(split_path const & src)
+ {
+ node_id nid = this->editable_roster_base::detach_node(nid);
+ marking_t & marks = safe_get(marking, nid);
+ marks.parent_name.clear();
+ marks.parent_name.insert(rid);
+ return nid;
+ }
+ virtual void drop_detached_node(node_id nid)
+ {
+ this->editable_roster_base::drop_detached_node(nid);
+ safe_erase(marking, nid);
+ }
+ node_id handle_new(node_id nid)
+ {
+ node_t & n = r.node(nid);
+ n.birth_revision = rid;
+ marking.insert(make_pair(nid, marking_t(rid, n)));
+ return nid;
+ }
+ virtual node_id create_dir_node()
+ {
+ return handle_new(this->editable_roster_base::create_dir_node());
+ }
+ virtual node_id create_file_node()
+ {
+ return handle_new(this->editable_roster_base::create_file_node());
+ }
+ virtual void apply_delta(split_path const & pth,
+ file_id const & old_id, file_id const & new_id)
+ {
+ this->editable_roster_base::apply_delta(pth, old_id, new_id);
+ marking_t & marks = safe_get(marking, r.lookup(pth));
+ marks.file_content.clear();
+ marks.file_content.insert(rid);
+ }
+ void handle_attr(split_path const & pth, attr_name const & name)
+ {
+ marking_t & marks = safe_get(marking, r.lookup(pth));
+ if (marks.attrs.find(name) == marks.attrs.end())
+ marks.attrs.insert(make_pair(name, set()));
+ set & markset = safe_get(marks.attrs, name);
+ markset.clear();
+ markset.insert(rid);
+ }
+ virtual void clear_attr(split_path const & pth, attr_name const & name)
+ {
+ this->editable_roster_base::clear_attr(pth, name);
+ handle_attr(pth, name);
+ }
+ virtual void set_attr(split_path const & pth, attr_name const & name,
+ attr_val const & val);
+ {
+ this->editable_roster_base::set_attr(pth, name, val);
+ handle_attr(pth, name);
+ }
+ private:
+ revision_id const & rid;
+ // marking starts out as the parent's marking
+ marking_map & marking;
+ };
+}
+
+void
+make_roster_for_nonmerge(cset const & cs, revision_id const & parent_rid,
+ revision_id const & new_rid,
+ roster_t & result, marking_map & marking, app_state & app)
+{
+ roster_t parent_r;
+ app.db.get_roster(parent_rid, result, marking);
+ true_node_id_source nis(app.db);
+ editable_roster_for_nonmerge er(result, nis, new_rid, marking);
+ cs.apply_to(er);
+}
+
+void
+make_roster_for_revision(revision_set const & rev, revision_id const & rid,
+ roster_t & result, marking_map & marking, app_state & app)
+{
+ if (rev.edges.size() == 1)
+ make_roster_for_nonmerge(rev.edges.begin()->second,
+ rev.edges.begin()->first,
+ rid, result, marking, app);
+ else if (rev.edges.size() == 2)
+ {
+ edge_map::iterator i = rev.edges.begin();
+ revision_id const & left_rid = i->first;
+ cset const & left_cs = i->second;
+ ++i;
+ revision_id const & right_rid = i->first;
+ cset const & left_cs = i->second;
+ make_roster_for_merge(left_cs, left_rid, right_cs, right_rid,
+ rid, result, marking, app);
+ }
+ else
+ I(false);
+ result.check_sane(marking);
+}
+*/
+
========================================================================
--- roster4.hh
+++ roster4.hh d4c48d7ec8ea1c8092b520ee9d8b230c50a0786f
@@ -0,0 +1,179 @@
+#ifndef __ROSTER_HH__
+#define __ROSTER_HH__
+
+// copyright (C) 2005 nathaniel smith
+// copyright (C) 2005 graydon hoare
+// all rights reserved.
+// licensed to the public under the terms of the GNU GPL (>= 2)
+// see the file COPYING for details
+
+#include