# # # patch "commands.cc" # from [ff34e552a53c8e03100a7d6cdba09de085421d5b] # to [f61366a34e5f0fbaf4e25253c67181b4740bb67b] # # patch "restrictions.cc" # from [5e937d86146454597d17d378728b942120c5097c] # to [16bada07dd71f3f2e1479b5365d638acda1da9b0] # # patch "restrictions.hh" # from [fe481d882a2c8a1b17f6a817a639076dc7cf7a88] # to [85100c84ee4c672e33910afbba2d0c4558759cb7] # # patch "roster.cc" # from [44561cd225f7392efefab1db4bc3a60302e6950e] # to [65d9f9f930511f393677d810ae21f16ab0fd5ed8] # # patch "roster.hh" # from [a8c66ef58534ed14f2d900010f75be41478183af] # to [636dd3bbc57a020653b485400165accd2fa23d76] # # patch "work.cc" # from [f8ee64c721cb624b0e58ad9139b93e0abd930129] # to [ea83b9e5b2cc4d2990686d63454c37c414fa118e] # ============================================================ --- commands.cc ff34e552a53c8e03100a7d6cdba09de085421d5b +++ commands.cc f61366a34e5f0fbaf4e25253c67181b4740bb67b @@ -1648,6 +1648,8 @@ get_working_revision_and_rosters(app, args, rs, old_roster, new_roster); new_roster.extract_path_set(paths); + // FIXME_RESTRICTIONS: this looks ok for roster restriction + for (path_set::const_iterator p = paths.begin(); p != paths.end(); ++p) { if (app.restriction_includes(*p)) @@ -1702,6 +1704,8 @@ old_paths, new_paths, included_work, excluded_work); + // FIXME_RESTRICTIONS: this looks ok for roster restriction + for (path_set::const_iterator i = new_paths.begin(); i != new_paths.end(); ++i) { if (i->size() == 1) @@ -3284,6 +3288,8 @@ restrict_cset(work, included_work, excluded_work, app); + // FIXME_RESTRICTIONS: this looks ok for roster restriction + node_map const & nodes = old_roster.all_nodes(); for (node_map::const_iterator i = nodes.begin(); i != nodes.end(); ++i) { ============================================================ --- restrictions.cc 5e937d86146454597d17d378728b942120c5097c +++ restrictions.cc 16bada07dd71f3f2e1479b5365d638acda1da9b0 @@ -14,6 +14,107 @@ #include "transforms.hh" void +restriction::add_nodes(roster_t const & roster, path_set const & paths) +{ + L(F("adding nodes\n")); + + for (path_set::const_iterator i = paths.begin(); i != paths.end(); ++i) + { + // TODO: (future) handle some sort of peg revision path syntax here. + // note that the idea of a --peg option doesn't work because each + // path may be relative to a different revision. + + if (roster.has_node(*i)) + { + node_t node = roster.get_node(*i); + bool recursive = is_dir_t(node); + node_id nid = node->self; + + // TODO: proper wildcard paths like foo/... + // for now we always add directories recursively and files exactly + + // TODO: possibly fail with nice error if path is already explicitly + // in the map? + + L(F("adding nid %d '%s'\n") % nid % file_path(*i)); + insert(nid, recursive); + + // currently we need to insert the parents of included nodes so that + // the included nodes are not orphaned in a restricted roster. this + // happens in cases like add "a" + add "a/b" when only "a/b" is + // included. i.e. "a" must be included for "a/b" to be valid. this + // isn't entirely sensible and should probably be revisited. + + node_id parent = node->parent; + while (!null_node(parent)) + { + split_path sp; + roster.get_name(parent, sp); + L(F("adding parent %d '%s'\n") % parent % file_path(sp)); + insert(parent, false); + node = roster.get_node(parent); + parent = node->parent; + } + + // TODO: consider keeping a list of valid paths here that we can use + // for doing a set-difference against with the full list of paths to + // find those that are not legal in any roster of this restriction + } + } + +} + +bool +restriction::includes(roster_t const & roster, node_id nid) const +{ + // empty restriction includes everything + if (restricted_node_map.empty()) + return true; + + node_id current = nid; + + while (!null_node(current)) + { + split_path sp; + roster.get_name(current, sp); + L(F("checking nid %d '%s'\n") % current % file_path(sp)); + + restriction_map::const_iterator r = restricted_node_map.find(current); + + if (r != restricted_node_map.end()) + { + // found exact node or a recursive parent + if (r->second || current == nid) + { + L(F("included nid %d '%s'\n") % current % file_path(sp)); + return true; + } + } + + node_t node = roster.get_node(current); + current = node->parent; + } + + split_path sp; + roster.get_name(nid, sp); + L(F("excluded nid %d '%s'\n") % nid % file_path(sp)); + + return false; +} + +void +restriction::insert(node_id nid, bool recursive) +{ + // we (mistakenly) allow multiple settings of the recursive include flag on a + // nid. this needs to be fixed but we need to track more than a simple boolean + // to do it. nids can be added non-recursively as parents of included nids, or + // explicitly. we should probably prevent explicit inclusion of foo/bar and + // foo/bar/... though. + + restricted_node_map[nid] |= recursive; +} + +void extract_rearranged_paths(cset const & cs, path_set & paths) { paths.insert(cs.nodes_deleted.begin(), cs.nodes_deleted.end()); ============================================================ --- restrictions.hh fe481d882a2c8a1b17f6a817a639076dc7cf7a88 +++ restrictions.hh 85100c84ee4c672e33910afbba2d0c4558759cb7 @@ -11,11 +11,38 @@ #include "roster.hh" #include "vocab.hh" -struct restriction +using std::map; + +// between any two related revisions, A and B, there is a set of changes (a +// cset) that describes the operations required to get from A to B. for example: +// +// revision A ... changes ... revision B +// +// a restriction is a means of masking off some of these changes to produce a +// third revision, X that lies somewhere between A and B. changes included by +// the restriction when applied to revision A would produce revision X. changes +// excluded by the restriction when applied to revision X would produce revision +// B. +// +// conceptually, a restriction allows for something like a sliding control for +// selecting the changes between revisions A and B. when the control is "all the +// way to the right" all changes are included and X == B. when then control is +// "all the way to the left" no changes are included and X == A. when the +// control is somewhere between these extremes X is a new revision. +// +// revision A ... included ... revision X ... excluded ... revision B + +class restriction { - roster_t base_roster; - roster_t current_roster; - roster_t restricted_roster; + public: + void add_nodes(roster_t const & roster, path_set const & paths); + bool includes(roster_t const & roster, node_id nid) const; + + private: + typedef map restriction_map; + restriction_map restricted_node_map; + + void insert(node_id nid, bool recursive); }; void ============================================================ --- roster.cc 44561cd225f7392efefab1db4bc3a60302e6950e +++ roster.cc 65d9f9f930511f393677d810ae21f16ab0fd5ed8 @@ -20,6 +20,7 @@ #include "vocab.hh" #include "transforms.hh" #include "parallel_iter.hh" +#include "restrictions.hh" #include "safe_map.hh" #include @@ -1311,14 +1312,14 @@ roster_t const & merge, marking_map & marking) { - for (map::const_iterator i = merge.all_nodes().begin(); + for (node_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); + node_map::const_iterator lni = left_r.all_nodes().find(i->first); + node_map::const_iterator rni = right_r.all_nodes().find(i->first); bool exists_in_left = (lni != left_r.all_nodes().end()); bool exists_in_right = (rni != right_r.all_nodes().end()); @@ -1667,7 +1668,7 @@ make_cset(roster_t const & from, roster_t const & to, cset & cs) { cs.clear(); - parallel::iter > i(from.all_nodes(), to.all_nodes()); + parallel::iter i(from.all_nodes(), to.all_nodes()); while (i.next()) { MM(i); @@ -1692,6 +1693,50 @@ } +void make_restricted_csets(roster_t const & from, roster_t const & to, + cset & included, cset & excluded, + restriction const & mask) +{ + included.clear(); + excluded.clear(); + L(F("building restricted csets\n")); + parallel::iter i(from.all_nodes(), to.all_nodes()); + while (i.next()) + { + MM(i); + switch (i.state()) + { + case parallel::invalid: + I(false); + + case parallel::in_left: + L(F("in left %d\n") % i.left_key()); + if (mask.includes(from, i.left_key())) + delta_only_in_from(from, i.left_key(), i.left_data(), included); + else + delta_only_in_from(from, i.left_key(), i.left_data(), excluded); + break; + + case parallel::in_right: + L(F("in right %d\n") % i.right_key()); + if (mask.includes(to, i.right_key())) + delta_only_in_to(to, i.right_key(), i.right_data(), included); + else + delta_only_in_to(to, i.right_key(), i.right_data(), excluded); + break; + + case parallel::in_both: + L(F("in both %d %d\n") % i.left_key() % i.right_key()); + if (mask.includes(from, i.left_key()) || mask.includes(to, i.right_key())) + delta_in_both(i.left_key(), from, i.left_data(), to, i.right_data(), included); + else + delta_in_both(i.left_key(), from, i.left_data(), to, i.right_data(), excluded); + break; + } + } +} + + void select_nodes_modified_by_cset(cset const & cs, roster_t const & old_roster, @@ -1823,6 +1868,8 @@ ros.get_name(nid, sp); file_path fp(sp); + // FIXME_RESTRICTIONS: this looks ok for roster restriction + // Only analyze restriction-included files. if (app.restriction_includes(sp)) { @@ -1878,6 +1925,8 @@ if (!ros.has_root()) return; + // FIXME_RESTRICTIONS: this looks ok for roster restriction + node_map const & nodes = ros.all_nodes(); for (node_map::const_iterator i = nodes.begin(); i != nodes.end(); ++i) { ============================================================ --- roster.hh a8c66ef58534ed14f2d900010f75be41478183af +++ roster.hh 636dd3bbc57a020653b485400165accd2fa23d76 @@ -289,6 +289,15 @@ roster_t const & to, cset & cs); + +// various (circular?) dependencies prevent inclusion of restrictions.hh +class restriction; + +void +make_restricted_csets(roster_t const & from, roster_t const & to, + cset & included, cset & excluded, + restriction const & mask); + void select_nodes_modified_by_cset(cset const & cs, roster_t const & old_roster, ============================================================ --- work.cc f8ee64c721cb624b0e58ad9139b93e0abd930129 +++ work.cc ea83b9e5b2cc4d2990686d63454c37c414fa118e @@ -39,6 +39,12 @@ { split_path sp; path.split(sp); + + // FIXME_RESTRICTIONS: this doesn't look so good for roster restrictions + + // unknown and ignored will never have nodes in the rosters so some other form + // of restriction is going to be needed here + if (app.restriction_includes(sp) && known.find(sp) == known.end()) { if (app.lua.hook_ignore_file(path))