# # # patch "automate.cc" # from [0b608759447d6c0c9f4aed5289f60a89decac403] # to [5baa22129500e0c1421f8c9592266617892b857f] # # patch "commands.cc" # from [72f5fa6d4e6a72199a3a1612baa7977f3ab72ea3] # to [d8547de4a2d8a7d1b0f987bb0e9456f89b78b987] # # patch "roster.cc" # from [67380c22a3dfef07d49edbc75646a4b0668de5cb] # to [57139e61b43c48736241df873dc73d2b0dcb00dc] # # patch "roster.hh" # from [6811b78a4a979e2146ba44e6c3a3023dec0b4c53] # to [9b79ea5b9c4aafdfd5aa53eba86594f4d18c2b88] # # patch "work.cc" # from [f0b8375e1b0bfbaf32770b41ece3015e2a34cb66] # to [a2bea917938a4fdcd466c84766bfd74d22bce753] # # patch "work.hh" # from [8c9dc3dba09d6c20d9afb4c9f7c1e11e28e489bb] # to [cffcfb84ad4377b4e7bde7e4d3fca83ae063bf6b] # ============================================================ --- automate.cc 0b608759447d6c0c9f4aed5289f60a89decac403 +++ automate.cc 5baa22129500e0c1421f8c9592266617892b857f @@ -227,7 +227,8 @@ roster_t base, current; temp_node_id_source nis; - get_base_and_current_roster_shape(base, current, nis, app); + E(false, F("FIXME_WORKMERGE")); +//get_base_and_current_roster_shape(base, current, nis, app); if (args.size() == 1) { @@ -684,8 +685,8 @@ inventory_map inventory; cset cs; path_set unchanged, changed, missing, known, unknown, ignored; - - get_base_and_current_roster_shape(base, curr, nis, app); + E(false, F("FIXME_WORKMERGE")); +// get_base_and_current_roster_shape(base, curr, nis, app); make_cset(base, curr, cs); I(cs.deltas_applied.empty()); ============================================================ --- commands.cc 72f5fa6d4e6a72199a3a1612baa7977f3ab72ea3 +++ commands.cc d8547de4a2d8a7d1b0f987bb0e9456f89b78b987 @@ -1376,7 +1376,13 @@ revision_id rid; if (app.revision_selectors.size() == 0) - get_revision_id(rid); + { + revision_set rs; + get_work_revision_set(rs); + N(rs.edges.size() == 1, + F("This workspace has multiple base revisions, you must specify one explicitly.")); + rid = rs.edges.begin()->first; + } else complete(app, idx(app.revision_selectors, 0)(), rid); N(app.db.revision_exists(rid), F("no such revision '%s'") % rid); @@ -1477,13 +1483,12 @@ } app.create_working_copy(dir); - + file_data data; roster_t ros; marking_map mm; - - put_revision_id(ident); - + + L(FL("checking out revision %s to directory %s\n") % ident % dir); app.db.get_roster(ident, ros, mm); @@ -1516,7 +1521,11 @@ write_localized_data(path, dat.inner(), app.lua); } } - remove_work_cset(); + { + revision_set rs; + rs.edges.insert(make_pair(ident, new cset)); + put_work_revision_set(rs); + } update_any_attrs(app); maybe_update_inodeprints(app); } @@ -2156,6 +2165,19 @@ throw usage(name); } +CMD(base_revision, N_("working copy"), "", + N_("get the workspace base revision(s)"), + OPT_NONE) +{ + if (args.size()) + throw usage(name); + + revision_set rs; + get_work_revision_set(rs); + for (edge_map::const_iterator i = rs.edges.begin(); i != rs.edges.end(); ++i) + P(F("%s") % i->first); +} + CMD(attr, N_("working copy"), N_("set PATH ATTR VALUE\nget PATH [ATTR]\ndrop PATH [ATTR]"), N_("set, get or drop file attributes"), OPT_NONE) @@ -2997,6 +3019,39 @@ } +CMD(workspace_merge, N_("tree"), N_("REV REV"), N_("merge two revisions in a workspace"), + OPT_BRANCH_NAME % OPT_DATE % OPT_AUTHOR) +{ + if (args.size() != 2) + throw usage(name); + revision_id l_id, r_id; + complete(app, idx(args,0)(), l_id); + complete(app, idx(args,1)(), r_id); + N(!is_ancestor(l_id, r_id, app), + F("%s in an ancestor of %s; no merge is needed.") % l_id % r_id); + N(!is_ancestor(r_id, l_id, app), + F("%s in an ancestor of %s; no merge is needed.") % r_id % l_id); + roster_t l_roster, r_roster; + marking_map l_marking, r_marking; + app.db.get_roster(l_id, l_roster, l_marking); + app.db.get_roster(r_id, r_roster, r_marking); + std::set l_uncommon_ancestors, r_uncommon_ancestors; + app.db.get_uncommon_ancestors(l_id, r_id, + l_uncommon_ancestors, + r_uncommon_ancestors); + roster_merge_result result; + roster_merge(l_roster, l_marking, l_uncommon_ancestors, + r_roster, r_marking, r_uncommon_ancestors, + result); + + P(F("There are %s node_name_conflicts.") % result.node_name_conflicts.size()); + P(F("There are %s file_content_conflicts.") % result.file_content_conflicts.size()); + P(F("There are %s node_attr_conflicts.") % result.node_attr_conflicts.size()); + P(F("There are %s orphaned_node_conflicts.") % result.orphaned_node_conflicts.size()); + P(F("There are %s rename_target_conflicts.") % result.rename_target_conflicts.size()); + P(F("There are %s directory_loop_conflicts.") % result.directory_loop_conflicts.size()); +} + // should merge support --message, --message-file? It seems somewhat weird, // since a single 'merge' command may perform arbitrarily many actual merges. CMD(merge, N_("tree"), "", N_("merge unmerged heads of branch"), ============================================================ --- roster.cc 67380c22a3dfef07d49edbc75646a4b0668de5cb +++ roster.cc 57139e61b43c48736241df873dc73d2b0dcb00dc @@ -1860,6 +1860,21 @@ } } +void +make_revision_set(parentage const & parents, + roster_t const & to, + revision_set & rs) +{ + rs.edges.clear(); + for (parentage::const_iterator i = parents.begin(); + i != parents.end(); ++i) + { + boost::shared_ptr cs(new cset); + make_cset(i->second, to, *cs); + rs.edges.insert(make_pair(i->first, cs)); + } + calculate_ident(to, rs.new_manifest); +} // we assume our input is sane bool ============================================================ --- roster.hh 6811b78a4a979e2146ba44e6c3a3023dec0b4c53 +++ roster.hh 9b79ea5b9c4aafdfd5aa53eba86594f4d18c2b88 @@ -16,6 +16,8 @@ #include "paths.hh" #include "sanity.hh" #include "vocab.hh" +//#include "revision.hh" +struct revision_set; struct node_id_source { @@ -288,6 +290,13 @@ roster_t const & to, cset & cs); +typedef std::map parentage; + +void +make_revision_set(parentage const & parents, + roster_t const & to, + revision_set & rs); + bool equal_up_to_renumbering(roster_t const & a, marking_map const & a_markings, roster_t const & b, marking_map const & b_markings); ============================================================ --- work.cc f0b8375e1b0bfbaf32770b41ece3015e2a34cb66 +++ work.cc a2bea917938a4fdcd466c84766bfd74d22bce753 @@ -357,7 +357,7 @@ L(FL("work path is %s\n") % w_path); } -void get_work_cset(cset & w) +void get_work_revision_set(revision_set & w) { bookkeeping_path w_path; get_work_path(w_path); @@ -366,7 +366,7 @@ L(FL("checking for un-committed work file %s\n") % w_path); data w_data; read_data(w_path, w_data); - read_cset(w_data, w); + read_revision_set(w_data, w); L(FL("read cset from %s\n") % w_path); } else @@ -375,30 +375,14 @@ } } -void remove_work_cset() +void put_work_revision_set(revision_set & w) { bookkeeping_path w_path; get_work_path(w_path); - if (file_exists(w_path)) - delete_file(w_path); -} -void put_work_cset(cset & w) -{ - bookkeeping_path w_path; - get_work_path(w_path); - - if (w.empty()) - { - if (file_exists(w_path)) - delete_file(w_path); - } - else - { - data w_data; - write_cset(w, w_data); - write_data(w_path, w_data); - } + data w_data; + write_revision_set(w, w_data); + write_data(w_path, w_data); } // revision file name @@ -410,7 +394,7 @@ m_path = bookkeeping_root / revision_file_name; L(FL("revision path is %s\n") % m_path); } - +/* void get_revision_id(revision_id & c) { c = revision_id(); @@ -442,37 +426,25 @@ data c_data(rev.inner()() + "\n"); write_data(c_path, c_data); } +*/ + void -get_base_revision(app_state & app, - revision_id & rid, - roster_t & ros, - marking_map & mm) +get_workspace_parentage_and_revision(app_state & app, + parentage & parents, + revision_set & rs) { - get_revision_id(rid); - - if (!null_id(rid)) + get_work_revision_set(rs); + parents.clear(); + for (edge_map::const_iterator i = rs.edges.begin(); i != rs.edges.end(); ++i) { - - N(app.db.revision_exists(rid), - F("base revision %s does not exist in database\n") % rid); - - app.db.get_roster(rid, ros, mm); + roster_t rost; + app.db.get_roster(i->first, rost); + parents.insert(make_pair(i->first, rost)); } - - L(FL("base roster has %d entries\n") % ros.all_nodes().size()); } -void -get_base_revision(app_state & app, - revision_id & rid, - roster_t & ros) -{ - marking_map mm; - get_base_revision(app, rid, ros, mm); -} - -void +static void get_base_roster(app_state & app, roster_t & ros) { @@ -484,11 +456,16 @@ void get_current_roster_shape(roster_t & ros, node_id_source & nis, app_state & app) { - get_base_roster(app, ros); - cset cs; - get_work_cset(cs); + parentage parents; + revision_set rs; + get_workspace_parentage_and_revision(app, parents, rs); + ros = parents.begin()->second; editable_roster_base er(ros, nis); - cs.apply_to(er); + for (edge_map::const_iterator i = rs.edges.begin(); i != rs.edges.end(); ++i) + { + if (edge_old_revision(i) == parents.begin()->first) + edge_changes(i).apply_to(er); + } } void ============================================================ --- work.hh 8c9dc3dba09d6c20d9afb4c9f7c1e11e28e489bb +++ work.hh cffcfb84ad4377b4e7bde7e4d3fca83ae063bf6b @@ -73,23 +73,15 @@ // the "work" file contains the current cset representing uncommitted // add/drop/rename operations (not deltas) -void get_work_cset(cset & w); -void remove_work_cset(); -void put_work_cset(cset & w); +void get_work_revision_set(revision_set & w); +void put_work_revision_set(revision_set & w); // the "revision" file contains the base revision id that the current working // copy was checked out from -void get_revision_id(revision_id & c); -void put_revision_id(revision_id const & rev); -void get_base_revision(app_state & app, - revision_id & rid, - roster_t & ros, - marking_map & mm); -void get_base_revision(app_state & app, - revision_id & rid, - roster_t & ros); -void get_base_roster(app_state & app, roster_t & ros); +void get_workspace_parentage_and_revision(app_state & app, + parentage & parents, + revision_set & rs); // This returns the current roster, except it does not bother updating the // hashes in that roster -- the "shape" is correct, all files and dirs exist