# # add_file "merge.cc" # # add_file "merge.hh" # # patch "Makefile.am" # from [4795ba7153051df7877a5ead9c548c82e286600c] # to [d57b2f802841e105a2b45bb847aa17dc696b07aa] # # patch "app_state.cc" # from [bccc125596979ed4ff5d91d75f81d08cb136e20f] # to [851f929f60678a14b02c741e3dee9157d13a7f32] # # patch "app_state.hh" # from [397ffb8acbff1ede6b7ed235481deb090ba177d4] # to [5240bc3b5725c114cd4de8d3a4cba8a4c8144bed] # # patch "commands.cc" # from [775992428125d132d64e7e06b52b1363b5fb7194] # to [b352c9e9819dcf6665b0308c4d74eeeda46b1dac] # # patch "merge.cc" # from [] # to [024fecd028c97a4883ac7a6acc934b09c88a19f5] # # patch "merge.hh" # from [] # to [cce807c1a1f9d187eec49d576481e1a7a4647b15] # # patch "roster_merge.hh" # from [8d5fd76784742cae5f1d88354a43f30559343cff] # to [27a2989edd99424357d037fc0b50dec372bfdd06] # ======================================================================== --- Makefile.am 4795ba7153051df7877a5ead9c548c82e286600c +++ Makefile.am d57b2f802841e105a2b45bb847aa17dc696b07aa @@ -46,6 +46,7 @@ string_queue.cc string_queue.hh \ paths.cc paths.hh \ roster_merge.cc roster_merge.hh \ + merge.cc merge.hh \ \ cleanup.hh unit_tests.hh interner.hh \ cycle_detector.hh randomfile.hh adler32.hh quick_alloc.hh \ ======================================================================== --- app_state.cc bccc125596979ed4ff5d91d75f81d08cb136e20f +++ app_state.cc 851f929f60678a14b02c741e3dee9157d13a7f32 @@ -36,7 +36,8 @@ app_state::app_state() : branch_name(""), db(system_path()), keys(this), stdhooks(true), rcfiles(true), diffs(false), - no_merges(false), set_default(false), verbose(false), search_root("/"), + no_merges(false), set_default(false), verbose(false), date_set(false), + search_root("/"), depth(-1), last(-1), diff_format(unified_diff), diff_args_provided(false), use_lca(false), execute(false), bind_address(""), bind_port(""), missing(false), unknown(false), @@ -352,7 +353,23 @@ void app_state::set_date(utf8 const & d) { - date = d; + try + { + // boost::posix_time is lame: it can parse "basic" ISO times, of the + // form 20000101T120000, but not "extended" ISO times, of the form + // 2000-01-01T12:00:00. So do something stupid to convert one to the + // other. + std::string tmp = d(); + std::string::size_type pos = 0; + while ((pos = tmp.find_first_of("-:")) != string::npos) + tmp.erase(pos, 1); + date = boost::posix_time::from_iso_string(tmp); + date_set = true; + } + catch (std::exception &e) + { + N(false, F("failed to parse date string '%s': %s") % d % e.what()); + } } void ======================================================================== --- app_state.hh 397ffb8acbff1ede6b7ed235481deb090ba177d4 +++ app_state.hh 5240bc3b5725c114cd4de8d3a4cba8a4c8144bed @@ -12,6 +12,7 @@ #include #include #include +#include #include @@ -45,7 +46,8 @@ options_map options; utf8 message; utf8 message_file; - utf8 date; + bool date_set; + boost::posix_time::ptime date; utf8 author; system_path search_root; std::vector revision_selectors; ======================================================================== --- commands.cc 775992428125d132d64e7e06b52b1363b5fb7194 +++ commands.cc b352c9e9819dcf6665b0308c4d74eeeda46b1dac @@ -16,7 +16,6 @@ #include #include #include -#include #include "commands.hh" #include "constants.hh" @@ -48,6 +47,7 @@ #include "options.hh" #include "globish.hh" #include "paths.hh" +#include "merge.hh" // // this file defines the task-oriented "top level" commands which can be @@ -2261,28 +2261,6 @@ } */ -static boost::posix_time::ptime -string_to_datetime(std::string const & s) -{ - try - { - // boost::posix_time is lame: it can parse "basic" ISO times, of the - // form 20000101T120000, but not "extended" ISO times, of the form - // 2000-01-01T12:00:00. So do something stupid to convert one to the - // other. - std::string tmp = s; - std::string::size_type pos = 0; - while ((pos = tmp.find_first_of("-:")) != string::npos) - tmp.erase(pos, 1); - return boost::posix_time::from_iso_string(tmp); - } - catch (std::exception &e) - { - N(false, F("failed to parse date string '%s': %s") % s % e.what()); - } - I(false); -} - CMD(commit, N_("working copy"), N_("[PATH]..."), N_("commit working copy to database"), OPT_BRANCH_NAME % OPT_MESSAGE % OPT_MSGFILE % OPT_DATE % @@ -2445,8 +2423,8 @@ dbw.consume_revision_data(rid, rdat); cert_revision_in_branch(rid, branchname, app, dbw); - if (app.date().length() > 0) - cert_revision_date_time(rid, string_to_datetime(app.date()), app, dbw); + if (app.date_set) + cert_revision_date_time(rid, app.date, app, dbw); else cert_revision_date_now(rid, app, dbw); if (app.author().length() > 0) @@ -3002,126 +2980,8 @@ maybe_update_inodeprints(app); } +*/ - -// this helper tries to produce merge <- mergeN(left,right); it searches -// for a common ancestor and if none is found synthesizes a common one with -// no contents. it then computes composite changesets via the common -// ancestor and does a 3-way merge. - -static void -try_one_merge(revision_id const & left_id, - revision_id const & right_id, - revision_id const & ancestor_id, // empty ==> use common ancestor - revision_id & merged_id, - app_state & app) -{ - revision_id anc_id; - revision_set left_rev, right_rev, anc_rev, merged_rev; - - app.db.get_revision(left_id, left_rev); - app.db.get_revision(right_id, right_rev); - - packet_db_writer dbw(app); - - manifest_map anc_man, left_man, right_man, merged_man; - - boost::shared_ptr - anc_to_left(new change_set()), - anc_to_right(new change_set()), - left_to_merged(new change_set()), - right_to_merged(new change_set()); - - app.db.get_manifest(right_rev.new_manifest, right_man); - app.db.get_manifest(left_rev.new_manifest, left_man); - - // Make sure that we can't create malformed graphs where the left parent is - // a descendent or ancestor of the right, or where both parents are equal, - // etc. - { - set ids; - ids.insert(left_id); - ids.insert(right_id); - erase_ancestors(ids, app); - I(ids.size() == 2); - } - - if (!null_id(ancestor_id)) - { - I(is_ancestor(ancestor_id, left_id, app)); - I(is_ancestor(ancestor_id, right_id, app)); - - anc_id = ancestor_id; - - app.db.get_revision(anc_id, anc_rev); - app.db.get_manifest(anc_rev.new_manifest, anc_man); - - calculate_composite_change_set(anc_id, left_id, app, *anc_to_left); - calculate_composite_change_set(anc_id, right_id, app, *anc_to_right); - } - else if (find_common_ancestor_for_merge(left_id, right_id, anc_id, app)) - { - P(F("common ancestor %s found\n" - "trying 3-way merge\n") % describe_revision(app, anc_id)); - - app.db.get_revision(anc_id, anc_rev); - app.db.get_manifest(anc_rev.new_manifest, anc_man); - - calculate_composite_change_set(anc_id, left_id, app, *anc_to_left); - calculate_composite_change_set(anc_id, right_id, app, *anc_to_right); - } - else - { - P(F("no common ancestor found, synthesizing edges\n")); - build_pure_addition_change_set(left_man, *anc_to_left); - build_pure_addition_change_set(right_man, *anc_to_right); - } - - merge_provider merger(app, anc_man, left_man, right_man); - - merge_change_sets(*anc_to_left, *anc_to_right, - *left_to_merged, *right_to_merged, - merger, app); - - { - // we have to record *some* route to this manifest. we pick the - // smaller of the two. - manifest_map tmp; - apply_change_set(anc_man, *anc_to_left, tmp); - apply_change_set(tmp, *left_to_merged, merged_man); - calculate_ident(merged_man, merged_rev.new_manifest); - delta left_mdelta, right_mdelta; - diff(left_man, merged_man, left_mdelta); - diff(right_man, merged_man, right_mdelta); - if (left_mdelta().size() < right_mdelta().size()) - dbw.consume_manifest_delta(left_rev.new_manifest, - merged_rev.new_manifest, left_mdelta); - else - dbw.consume_manifest_delta(right_rev.new_manifest, - merged_rev.new_manifest, right_mdelta); - } - - merged_rev.edges.insert(std::make_pair(left_id, - std::make_pair(left_rev.new_manifest, - left_to_merged))); - merged_rev.edges.insert(std::make_pair(right_id, - std::make_pair(right_rev.new_manifest, - right_to_merged))); - revision_data merged_data; - write_revision_set(merged_rev, merged_data); - calculate_ident(merged_data, merged_id); - dbw.consume_revision_data(merged_id, merged_data); - if (app.date().length() > 0) - cert_revision_date_time(merged_id, string_to_datetime(app.date()), app, dbw); - else - cert_revision_date_now(merged_id, app, dbw); - if (app.author().length() > 0) - cert_revision_author(merged_id, app.author(), app, dbw); - else - cert_revision_author_default(merged_id, app, dbw); -} - - CMD(merge, N_("tree"), "", N_("merge unmerged heads of branch"), OPT_BRANCH_NAME % OPT_DATE % OPT_AUTHOR % OPT_LCA) { @@ -3152,7 +3012,7 @@ revision_id merged; transaction_guard guard(app.db); - try_one_merge(left, right, revision_id(), merged, app); + interactive_merge_and_store(left, right, merged, app); // merged 1 edge; now we commit this, update merge source and // try next one @@ -3238,7 +3098,7 @@ { revision_id merged; transaction_guard guard(app.db); - try_one_merge(*src_i, *dst_i, revision_id(), merged, app); + interactive_merge_and_store(*src_i, *dst_i, merged, app); packet_db_writer dbw(app); @@ -3255,7 +3115,6 @@ P(F("[merged] %s\n") % merged); } } -*/ CMD(refresh_inodeprints, N_("tree"), "", N_("refresh the inodeprint cache"), OPT_NONE) ======================================================================== --- merge.cc +++ merge.cc 024fecd028c97a4883ac7a6acc934b09c88a19f5 @@ -0,0 +1,76 @@ +// copyright (C) 2005 nathaniel smith +// all rights reserved. +// licensed to the public under the terms of the GNU GPL (>= 2) +// see the file COPYING for details + +#include "merge.hh" +#include "roster_merge.hh" + +void +interactive_merge_and_store(revision_id const & left_rid, + revision_id const & right_rid, + revision_id & merged_rid, + app_state & app) +{ + roster_t left_roster, right_roster; + marking_map left_marking_map, right_marking_map; + std::set left_uncommon_ancestors, right_uncommon_ancestors; + + app.db.get_roster(left_rid, left_roster, left_marking_map); + app.db.get_roster(right_rid, right_roster, right_marking_map); + app.db.get_uncommon_ancestors(left_rid, right_rid, + left_uncommon_ancestors, right_uncommon_ancestors); + + roster_merge_result result; + + roster_merge(left_roster, left_marking_map, left_uncommon_ancestors, + right_roster, right_marking_map, right_uncommon_ancestors, + result); + + roster_t & merged_roster = result.roster; + + // FIXME_ROSTERS: add code here to resolve conflicts + // write new files into the db + + I(result.is_clean()); + I(check_sane(merged_roster)); + + revision_set merged_rev; + + calculate_ident(merged_roster, merged_rev.new_manifest); + + manifest_id left_mid; + calculate_ident(left_roster, left_mid); + cset left_to_merged; + make_cset(left_roster, merged_roster, left_to_merged); + safe_insert(merged_rev.edges, std::make_pair(left_rid, + std::make_pair(left_mid, + left_to_merged))); + + manifest_id right_mid; + calculate_ident(right_roster, right_mid); + cset right_to_merged; + make_cset(right_roster, merged_roster, right_to_merged); + safe_insert(merged_rev.edges, std::make_pair(right_rid, + std::make_pair(right_mid, + right_to_merged))); + + revision_data merged_data; + write_revision_set(merged_rev, merged_data); + calculate_ident(merged_data, merged_rid); + { + transaction_guard guard(app.db); + + app.db.put_revision(merged_rid, merged_rev); + if (app.date_set) + cert_revision_date_time(merged_id, app.date, app, dbw); + else + cert_revision_date_now(merged_id, app, dbw); + if (app.author().length() > 0) + cert_revision_author(merged_id, app.author(), app, dbw); + else + cert_revision_author_default(merged_id, app, dbw); + + guard.commit(); + } +} ======================================================================== --- merge.hh +++ merge.hh cce807c1a1f9d187eec49d576481e1a7a4647b15 @@ -0,0 +1,30 @@ +#ifndef __MERGE_HH__ +#define __MERGE_HH__ + +// copyright (C) 2005 nathaniel smith +// all rights reserved. +// licensed to the public under the terms of the GNU GPL (>= 2) +// see the file COPYING for details + +#include + +#include "app_state.hh" +#include "vocab.hh" +#include "roster.hh" + +// traditional resolve-all-conflicts-as-you-go style merging with 3-way merge +// for file texts +// aborts if merge fails +// writes out resulting revision to the db, along with author and date certs +// (but _not_ branch or changelog certs) +// this version can only be used to merge revisions that are in the db, and +// that are written straight back out to the db; some refactoring would +// probably be good +// 'update' requires some slightly different interface, to deal with the gunk +// around the revision and its files not being in the db, and the resulting +// revision and its merged files not being written back to the db +void +interactive_merge_and_store(revision_id const & left, revision_id const & right, + revision_id & merged, app_state & app); + +#endif ======================================================================== --- roster_merge.hh 8d5fd76784742cae5f1d88354a43f30559343cff +++ roster_merge.hh 27a2989edd99424357d037fc0b50dec372bfdd06 @@ -94,7 +94,7 @@ std::vector node_name_conflicts; std::vector file_content_conflicts; std::vector node_attr_conflicts; - // this roster is sane iff is_clean() returns true + // this roster is sane if is_clean() returns true roster_t roster; bool is_clean(); void clear();