# # # rename "diff_patch.cc" # to "diff_output.cc" # # rename "diff_patch.hh" # to "diff_output.hh" # # rename "merge.cc" # to "merge_content.cc" # # rename "merge.hh" # to "merge_content.hh" # # rename "roster_merge.cc" # to "merge_roster.cc" # # rename "roster_merge.hh" # to "merge_roster.hh" # # patch "Makefile.am" # from [160e83e50254c7e2c88f08d3b4fe6301ee614a35] # to [789e3045b844e413fabcc6180e5898ed812ac3c5] # # patch "cmd_conflicts.cc" # from [b13aa30d0d716b4e52ef8ad0d39337f3f0d41a17] # to [29cf4482ddd3d446052d3767588c9caf0a656cdf] # # patch "cmd_diff_log.cc" # from [598d52bb8f16c8d44e431d4cbfcdc02cf0f3c5ae] # to [022198a63c53dc5a47f0d1824fb5eeed6c155f80] # # patch "cmd_files.cc" # from [d2e6d85c9e3f7504a8eff0e54b2b694d2a13bb78] # to [a052e282223aa32471545d4dbbd84f267f546271] # # patch "cmd_merging.cc" # from [883fada25173b9e2800de82dc275c6d70cd33619] # to [71f2dae2ca58490e8db2a81c22a777decfc75ded] # # patch "cmd_netsync.cc" # from [fdc1d80f91a50232e52e08f3b1a9d4c814470251] # to [ccffd713e9f1139b29b9726fb5de936b2683a265] # # patch "cmd_ws_commit.cc" # from [542d6ade8937dff9fa9516b800aef85650225e65] # to [6eafae7a1ae356aae0765ec5ea2e230c14c34681] # # patch "diff_output.cc" # from [be1a66379348f926f25b574f808cb0f158139f05] # to [c41e41988dd3283841bf171f494280ba9b5066e2] # # patch "diff_output.hh" # from [d377b6a5fab0b570b44533383244b7f03cac4c13] # to [399f633945756b8ca315ee658d632243e357226f] # # patch "interner.hh" # from [cbf57f9a888faf417eb9bb0cba373a18589b7c5b] # to [149f63e6eb3f1b29d26d017d5e94d8482327afd6] # # patch "merge_content.cc" # from [f913e742c88967bed03438ba82c443015fefb335] # to [678fec71c1a640e4c91ac0c601fccda6d90a358f] # # patch "merge_content.hh" # from [837e0f0510935be055622c626378e289e6f5a59e] # to [a4003604503ede3dc6a72b7daeaa326f5931f6f4] # # patch "merge_roster.cc" # from [ca479751ee42bfec579dc26bfe5d330b22607e61] # to [acfb17d85ecf0a9691ebb8833817b43750b5725d] # # patch "merge_roster.hh" # from [f530fb6fe1086d629f82d12b73d5019754615c48] # to [5bb429497a38b162b753014738c602b582c2fbbe] # # patch "po/POTFILES.in" # from [965ad78c697c24a5ed21a77a7c63226905090316] # to [639dab3c215bfa742d5c757537da5069a3a83744] # # patch "work.cc" # from [56a3a0f37a20d60df660b7d0cf00092ca3b6470b] # to [f72a10818deea7e7140ff2a4be92289238577854] # ============================================================ --- Makefile.am 160e83e50254c7e2c88f08d3b4fe6301ee614a35 +++ Makefile.am 789e3045b844e413fabcc6180e5898ed812ac3c5 @@ -29,7 +29,7 @@ MOST_SOURCES = \ app_state.cc app_state.hh \ botan_pipe_cache.hh \ commands.cc commands.hh $(CMD_SOURCES) \ - diff_patch.cc diff_patch.hh \ + diff_output.cc diff_output.hh \ lua_hooks.cc lua_hooks.hh \ transforms.cc transforms.hh \ update.cc update.hh \ @@ -67,8 +67,8 @@ MOST_SOURCES = \ restrictions.cc restrictions.hh \ hmac.cc hmac.hh \ string_queue.cc string_queue.hh \ - roster_merge.cc roster_merge.hh \ - merge.cc merge.hh \ + merge_roster.cc merge_roster.hh \ + merge_content.cc merge_3way.cc merge_content.hh \ legacy.cc legacy.hh uri.cc uri.hh \ graph.cc graph.hh \ roster_delta.cc roster_delta.hh \ @@ -298,10 +298,10 @@ UNIT_TEST_SOURCES = \ # these files contain unit tests UNIT_TEST_SOURCES = \ basic_io.cc charset.cc commands.cc crypto_tests.cc cset.cc \ - dates.cc diff_patch.cc globish.cc graph.cc netcmd.cc \ + dates.cc merge_3way.cc globish.cc graph.cc netcmd.cc \ netxx_pipe.cc numeric_vocab.cc option.cc outdated_indicator.cc \ packet.cc paths.cc refiner.cc restrictions.cc rev_height.cc \ - revision.cc roster.cc roster_merge.cc simplestring_xform.cc \ + revision.cc roster.cc merge_roster.cc simplestring_xform.cc \ string_queue.cc transforms.cc uri.cc vocab.cc xdelta.cc # these files do not contain unit tests, but are required for unit testing @@ -318,10 +318,11 @@ UNIT_TEST_OBJ_SUPPORT = \ mtn-epoch.$(OBJEXT) mtn-file_io.$(OBJEXT) mtn-gzip.$(OBJEXT) \ mtn-hmac.$(OBJEXT) mtn-inodeprint.$(OBJEXT) \ mtn-key_store.$(OBJEXT) mtn-keys.$(OBJEXT) mtn-lcs.$(OBJEXT) \ - mtn-lua.$(OBJEXT) mtn-lua_hooks.$(OBJEXT) mtn-merge.$(OBJEXT) \ - mtn-merkle_tree.$(OBJEXT) mtn-pcrewrap.$(OBJEXT) \ - mtn-project.$(OBJEXT) mtn-sanity.$(OBJEXT) \ - mtn-schema.$(OBJEXT) mtn-migrate_schema.$(OBJEXT) \ + mtn-lua.$(OBJEXT) mtn-lua_hooks.$(OBJEXT) \ + mtn-merge_content.$(OBJEXT) mtn-merkle_tree.$(OBJEXT) \ + mtn-pcrewrap.$(OBJEXT) mtn-project.$(OBJEXT) \ + mtn-sanity.$(OBJEXT) mtn-schema.$(OBJEXT) \ + mtn-migrate_schema.$(OBJEXT) \ mtn-specialized_lexical_cast.$(OBJEXT) mtn-ssh_agent.$(OBJEXT) \ mtn-std_hooks.$(OBJEXT) mtn-ui.$(OBJEXT) mtn-work.$(OBJEXT) \ mtn-migrate_work.$(OBJEXT) ============================================================ --- cmd_conflicts.cc b13aa30d0d716b4e52ef8ad0d39337f3f0d41a17 +++ cmd_conflicts.cc 29cf4482ddd3d446052d3767588c9caf0a656cdf @@ -13,7 +13,7 @@ #include "app_state.hh" #include "cmd.hh" #include "database.hh" -#include "roster_merge.hh" +#include "merge_roster.hh" CMD_GROUP(conflicts, "conflicts", "", CMD_REF(tree), N_("Commands for conflict resolutions"), ============================================================ --- cmd_diff_log.cc 598d52bb8f16c8d44e431d4cbfcdc02cf0f3c5ae +++ cmd_diff_log.cc 022198a63c53dc5a47f0d1824fb5eeed6c155f80 @@ -17,7 +17,7 @@ #include "asciik.hh" #include "charset.hh" #include "cmd.hh" -#include "diff_patch.hh" +#include "diff_output.hh" #include "file_io.hh" #include "restrictions.hh" #include "revision.hh" ============================================================ --- cmd_files.cc d2e6d85c9e3f7504a8eff0e54b2b694d2a13bb78 +++ cmd_files.cc a052e282223aa32471545d4dbbd84f267f546271 @@ -14,7 +14,8 @@ #include "annotate.hh" #include "revision.hh" #include "cmd.hh" -#include "diff_patch.hh" +#include "diff_output.hh" +#include "merge_content.hh" #include "file_io.hh" #include "simplestring_xform.hh" #include "transforms.hh" ============================================================ --- cmd_merging.cc 883fada25173b9e2800de82dc275c6d70cd33619 +++ cmd_merging.cc 71f2dae2ca58490e8db2a81c22a777decfc75ded @@ -15,11 +15,11 @@ #include "basic_io.hh" #include "cmd.hh" -#include "diff_patch.hh" -#include "merge.hh" +#include "diff_output.hh" +#include "merge_content.hh" #include "restrictions.hh" #include "revision.hh" -#include "roster_merge.hh" +#include "merge_roster.hh" #include "transforms.hh" #include "update.hh" #include "work.hh" ============================================================ --- cmd_netsync.cc fdc1d80f91a50232e52e08f3b1a9d4c814470251 +++ cmd_netsync.cc ccffd713e9f1139b29b9726fb5de936b2683a265 @@ -1,7 +1,7 @@ #include "base.hh" #include "cmd.hh" -#include "merge.hh" +#include "merge_content.hh" #include "netcmd.hh" #include "globish.hh" #include "keys.hh" ============================================================ --- cmd_ws_commit.cc 542d6ade8937dff9fa9516b800aef85650225e65 +++ cmd_ws_commit.cc 6eafae7a1ae356aae0765ec5ea2e230c14c34681 @@ -12,7 +12,7 @@ #include #include "cmd.hh" -#include "merge.hh" +#include "merge_content.hh" #include "file_io.hh" #include "restrictions.hh" #include "revision.hh" ============================================================ --- diff_patch.cc be1a66379348f926f25b574f808cb0f158139f05 +++ diff_output.cc c41e41988dd3283841bf171f494280ba9b5066e2 @@ -9,8 +9,7 @@ // PURPOSE. #include "base.hh" -#include "diff_patch.hh" - +#include "diff_output.hh" #include "file_io.hh" #include "interner.hh" #include "lcs.hh" @@ -21,460 +20,18 @@ #include #include -using std::min; using std::max; +using std::min; using std::ostream; using std::ostream_iterator; using std::string; -using std::swap; using std::vector; using boost::scoped_ptr; -struct conflict {}; +// This file handles printing out various diff formats for the case where +// someone wants to *read* a diff rather than apply it. The actual diff +// computation is done in lcs.cc. -// -// a 3-way merge works like this: -// -// /----> right -// ancestor -// \----> left -// -// first you compute the edit list "EDITS(ancestor,left)". -// -// then you make an offset table "leftpos" which describes positions in -// "ancestor" as they map to "left"; that is, for 0 < apos < -// ancestor.size(), we have -// -// left[leftpos[apos]] == ancestor[apos] -// -// you do this by walking through the edit list and either jumping the -// current index ahead an extra position, on an insert, or remaining still, -// on a delete. on an insert *or* a delete, you push the current index back -// onto the leftpos array. -// -// next you compute the edit list "EDITS(ancestor,right)". -// -// you then go through this edit list applying the edits to left, rather -// than ancestor, and using the table leftpos to map the position of each -// edit to an appropriate spot in left. this means you walk a "curr_left" -// index through the edits, and for each edit e: -// -// - if e is a delete (and e.pos is a position in ancestor) -// - increment curr_left without copying anything to "merged" -// -// - if e is an insert (and e.pos is a position in right) -// - copy right[e.pos] to "merged" -// - leave curr_left alone -// -// - when advancing to apos (and apos is a position in ancestor) -// - copy left[curr_left] to merged while curr_left < leftpos[apos] -// -// -// the practical upshot is that you apply the delta from ancestor->right -// to the adjusted contexts in left, producing something vaguely like -// the concatenation of delta(ancestor,left) :: delta(ancestor,right). -// -// NB: this is, as far as I can tell, what diff3 does. I don't think I'm -// infringing on anyone's fancy patents here. -// - -typedef enum { preserved = 0, deleted = 1, changed = 2 } edit_t; -static const char etab[3][10] = - { - "preserved", - "deleted", - "changed" - }; - -struct extent -{ - extent(size_t p, size_t l, edit_t t) - : pos(p), len(l), type(t) - {} - size_t pos; - size_t len; - edit_t type; -}; - -void calculate_extents(vector const & a_b_edits, - vector const & b, - vector & prefix, - vector & extents, - vector & suffix, - size_t const a_len, - interner & intern) -{ - extents.reserve(a_len * 2); - - size_t a_pos = 0, b_pos = 0; - - for (vector::const_iterator i = a_b_edits.begin(); - i != a_b_edits.end(); ++i) - { - // L(FL("edit: %d") % *i); - if (*i < 0) - { - // negative elements code the negation of the one-based index into A - // of the element to be deleted - size_t a_deleted = (-1 - *i); - - // fill positions out to the deletion point - while (a_pos < a_deleted) - { - a_pos++; - extents.push_back(extent(b_pos++, 1, preserved)); - } - - // L(FL(" -- delete at A-pos %d (B-pos = %d)") % a_deleted % b_pos); - - // skip the deleted line - a_pos++; - extents.push_back(extent(b_pos, 0, deleted)); - } - else - { - // positive elements code the one-based index into B of the element to - // be inserted - size_t b_inserted = (*i - 1); - - // fill positions out to the insertion point - while (b_pos < b_inserted) - { - a_pos++; - extents.push_back(extent(b_pos++, 1, preserved)); - } - - // L(FL(" -- insert at B-pos %d (A-pos = %d) : '%s'") - // % b_inserted % a_pos % intern.lookup(b.at(b_inserted))); - - // record that there was an insertion, but a_pos did not move. - if ((b_pos == 0 && extents.empty()) - || (b_pos == prefix.size())) - { - prefix.push_back(b.at(b_pos)); - } - else if (a_len == a_pos) - { - suffix.push_back(b.at(b_pos)); - } - else - { - // make the insertion - extents.back().type = changed; - extents.back().len++; - } - b_pos++; - } - } - - while (extents.size() < a_len) - extents.push_back(extent(b_pos++, 1, preserved)); -} - -void normalize_extents(vector & a_b_map, - vector const & a, - vector const & b) -{ - for (size_t i = 0; i < a_b_map.size(); ++i) - { - if (i > 0) - { - size_t j = i; - while (j > 0 - && (a_b_map.at(j-1).type == preserved) - && (a_b_map.at(j).type == changed) - && (a.at(j) == b.at(a_b_map.at(j).pos + a_b_map.at(j).len - 1))) - { - // This is implied by (a_b_map.at(j-1).type == preserved) - I(a.at(j-1) == b.at(a_b_map.at(j-1).pos)); - - // Coming into loop we have: - // i - // z --pres--> z 0 - // o --pres--> o 1 - // a --chng--> a 2 The important thing here is that 'a' in - // t the LHS matches with ... - // u - // v - // a ... the a on the RHS here. Hence we can - // q --pres--> q 3 'shift' the entire 'changed' block - // e --chng--> d 4 upwards, leaving a 'preserved' line - // g --pres--> g 5 'a'->'a' - // - // Want to end up with: - // i - // z --pres--> z 0 - // o --chng--> o 1 - // a - // t - // u - // v - // a --pres--> a 2 - // q --pres--> q 3 - // e --chng--> d 4 - // g --pres--> g 5 - // - // Now all the 'changed' extents are normalised to the - // earliest possible position. - - L(FL("exchanging preserved extent [%d+%d] with changed extent [%d+%d]") - % a_b_map.at(j-1).pos - % a_b_map.at(j-1).len - % a_b_map.at(j).pos - % a_b_map.at(j).len); - - swap(a_b_map.at(j-1).len, a_b_map.at(j).len); - swap(a_b_map.at(j-1).type, a_b_map.at(j).type); - - // Adjust position of the later, preserved extent. It should - // better point to the second 'a' in the above example. - a_b_map.at(j).pos = a_b_map.at(j-1).pos + a_b_map.at(j-1).len; - - --j; - } - } - } - - for (size_t i = 0; i < a_b_map.size(); ++i) - { - if (i > 0) - { - size_t j = i; - while (j > 0 - && a_b_map.at(j).type == changed - && a_b_map.at(j-1).type == changed - && a_b_map.at(j).len > 1 - && a_b_map.at(j-1).pos + a_b_map.at(j-1).len == a_b_map.at(j).pos) - { - // step 1: move a chunk from this insert extent to its - // predecessor - size_t piece = a_b_map.at(j).len - 1; - // L(FL("moving change piece of len %d from pos %d to pos %d") - // % piece - // % a_b_map.at(j).pos - // % a_b_map.at(j-1).pos); - a_b_map.at(j).len = 1; - a_b_map.at(j).pos += piece; - a_b_map.at(j-1).len += piece; - - // step 2: if this extent (now of length 1) has become a "changed" - // extent identical to its previous state, switch it to a "preserved" - // extent. - if (b.at(a_b_map.at(j).pos) == a.at(j)) - { - // L(FL("changing normalized 'changed' extent at %d to 'preserved'") - // % a_b_map.at(j).pos); - a_b_map.at(j).type = preserved; - } - --j; - } - } - } -} - - -void merge_extents(vector const & a_b_map, - vector const & a_c_map, - vector const & b, - vector const & c, - interner const & in, - vector & merged) -{ - I(a_b_map.size() == a_c_map.size()); - - vector::const_iterator i = a_b_map.begin(); - vector::const_iterator j = a_c_map.begin(); - merged.reserve(a_b_map.size() * 2); - - // for (; i != a_b_map.end(); ++i, ++j) - // { - - // L(FL("trying to merge: [%s %d %d] vs. [%s %d %d]") - // % etab[i->type] % i->pos % i->len - // % etab[j->type] % j->pos % j->len); - // } - - // i = a_b_map.begin(); - // j = a_c_map.begin(); - - for (; i != a_b_map.end(); ++i, ++j) - { - - // L(FL("trying to merge: [%s %d %d] vs. [%s %d %d]") - // % etab[i->type] % i->pos % i->len - // % etab[j->type] % j->pos % j->len); - - // mutual, identical preserves / inserts / changes - if (((i->type == changed && j->type == changed) - || (i->type == preserved && j->type == preserved)) - && i->len == j->len) - { - for (size_t k = 0; k < i->len; ++k) - { - if (b.at(i->pos + k) != c.at(j->pos + k)) - { - L(FL("conflicting edits: %s %d[%d] '%s' vs. %s %d[%d] '%s'") - % etab[i->type] % i->pos % k % in.lookup(b.at(i->pos + k)) - % etab[j->type] % j->pos % k % in.lookup(c.at(j->pos + k))); - throw conflict(); - } - merged.push_back(b.at(i->pos + k)); - } - } - - // mutual or single-edge deletes - else if ((i->type == deleted && j->type == deleted) - || (i->type == deleted && j->type == preserved) - || (i->type == preserved && j->type == deleted)) - { - // do nothing - } - - // single-edge insert / changes - else if (i->type == changed && j->type == preserved) - for (size_t k = 0; k < i->len; ++k) - merged.push_back(b.at(i->pos + k)); - - else if (i->type == preserved && j->type == changed) - for (size_t k = 0; k < j->len; ++k) - merged.push_back(c.at(j->pos + k)); - - else - { - L(FL("conflicting edits: [%s %d %d] vs. [%s %d %d]") - % etab[i->type] % i->pos % i->len - % etab[j->type] % j->pos % j->len); - throw conflict(); - } - - // if (merged.empty()) - // L(FL(" --> EMPTY")); - // else - // L(FL(" --> [%d]: %s") % (merged.size() - 1) % in.lookup(merged.back())); - } -} - - -void merge_via_edit_scripts(vector const & ancestor, - vector const & left, - vector const & right, - vector & merged) -{ - vector anc_interned; - vector left_interned, right_interned; - vector left_edits, right_edits; - vector left_prefix, right_prefix; - vector left_suffix, right_suffix; - vector left_extents, right_extents; - vector merged_interned; - interner in; - - // for (int i = 0; i < min(min(left.size(), right.size()), ancestor.size()); ++i) - // { - // cerr << '[' << i << "] " << left[i] << ' ' << ancestor[i] << ' ' << right[i] << '\n'; - // } - - anc_interned.reserve(ancestor.size()); - for (vector::const_iterator i = ancestor.begin(); - i != ancestor.end(); ++i) - anc_interned.push_back(in.intern(*i)); - - left_interned.reserve(left.size()); - for (vector::const_iterator i = left.begin(); - i != left.end(); ++i) - left_interned.push_back(in.intern(*i)); - - right_interned.reserve(right.size()); - for (vector::const_iterator i = right.begin(); - i != right.end(); ++i) - right_interned.push_back(in.intern(*i)); - - L(FL("calculating left edit script on %d -> %d lines") - % anc_interned.size() % left_interned.size()); - - edit_script(anc_interned.begin(), anc_interned.end(), - left_interned.begin(), left_interned.end(), - min(ancestor.size(), left.size()), - left_edits); - - L(FL("calculating right edit script on %d -> %d lines") - % anc_interned.size() % right_interned.size()); - - edit_script(anc_interned.begin(), anc_interned.end(), - right_interned.begin(), right_interned.end(), - min(ancestor.size(), right.size()), - right_edits); - - L(FL("calculating left extents on %d edits") % left_edits.size()); - calculate_extents(left_edits, left_interned, - left_prefix, left_extents, left_suffix, - anc_interned.size(), in); - - L(FL("calculating right extents on %d edits") % right_edits.size()); - calculate_extents(right_edits, right_interned, - right_prefix, right_extents, right_suffix, - anc_interned.size(), in); - - L(FL("normalizing %d right extents") % right_extents.size()); - normalize_extents(right_extents, anc_interned, right_interned); - - L(FL("normalizing %d left extents") % left_extents.size()); - normalize_extents(left_extents, anc_interned, left_interned); - - - if ((!right_prefix.empty()) && (!left_prefix.empty())) - { - L(FL("conflicting prefixes")); - throw conflict(); - } - - if ((!right_suffix.empty()) && (!left_suffix.empty())) - { - L(FL("conflicting suffixes")); - throw conflict(); - } - - L(FL("merging %d left, %d right extents") - % left_extents.size() % right_extents.size()); - - copy(left_prefix.begin(), left_prefix.end(), back_inserter(merged_interned)); - copy(right_prefix.begin(), right_prefix.end(), back_inserter(merged_interned)); - - merge_extents(left_extents, right_extents, - left_interned, right_interned, - in, merged_interned); - - copy(left_suffix.begin(), left_suffix.end(), back_inserter(merged_interned)); - copy(right_suffix.begin(), right_suffix.end(), back_inserter(merged_interned)); - - merged.reserve(merged_interned.size()); - for (vector::const_iterator i = merged_interned.begin(); - i != merged_interned.end(); ++i) - merged.push_back(in.lookup(*i)); -} - - -bool merge3(vector const & ancestor, - vector const & left, - vector const & right, - vector & merged) -{ - try - { - merge_via_edit_scripts(ancestor, left, right, merged); - } - catch(conflict &) - { - L(FL("conflict detected. no merge.")); - return false; - } - return true; -} - -// the remaining part of this file just handles printing out various -// diff formats for the case where someone wants to *read* a diff -// rather than apply it. - struct hunk_consumer { vector const & a; @@ -1038,180 +595,7 @@ make_diff(string const & filename1, } } -#ifdef BUILD_UNIT_TESTS -#include "unit_tests.hh" -#include "lexical_cast.hh" -#include "randomfile.hh" -using std::cerr; -using std::cout; -using std::stringstream; - -using boost::lexical_cast; - -static void dump_incorrect_merge(vector const & expected, - vector const & got, - string const & prefix) -{ - size_t mx = expected.size(); - if (mx < got.size()) - mx = got.size(); - for (size_t i = 0; i < mx; ++i) - { - cerr << "bad merge: " << i << " [" << prefix << "]\t"; - - if (i < expected.size()) - cerr << '[' << expected[i] << "]\t"; - else - cerr << "[--nil--]\t"; - - if (i < got.size()) - cerr << '[' << got[i] << "]\t"; - else - cerr << "[--nil--]\t"; - - cerr << '\n'; - } -} - -// high tech randomizing test -UNIT_TEST(diff_patch, randomizing_merge) -{ - randomizer rng; - for (int i = 0; i < 30; ++i) - { - vector anc, d1, d2, m1, m2, gm; - - file_randomizer::build_random_fork(anc, d1, d2, gm, (10 + 2 * i), rng); - - UNIT_TEST_CHECK(merge3(anc, d1, d2, m1)); - if (gm != m1) - dump_incorrect_merge (gm, m1, "random_merge 1"); - UNIT_TEST_CHECK(gm == m1); - - UNIT_TEST_CHECK(merge3(anc, d2, d1, m2)); - if (gm != m2) - dump_incorrect_merge (gm, m2, "random_merge 2"); - UNIT_TEST_CHECK(gm == m2); - } -} - - -// old boring tests -UNIT_TEST(diff_patch, merge_prepend) -{ - UNIT_TEST_CHECKPOINT("prepend test"); - vector anc, d1, d2, m1, m2, gm; - for (int i = 10; i < 20; ++i) - { - d2.push_back(lexical_cast(i)); - gm.push_back(lexical_cast(i)); - } - - for (int i = 0; i < 10; ++i) - { - anc.push_back(lexical_cast(i)); - d1.push_back(lexical_cast(i)); - d2.push_back(lexical_cast(i)); - gm.push_back(lexical_cast(i)); - } - - UNIT_TEST_CHECK(merge3(anc, d1, d2, m1)); - if (gm != m1) - dump_incorrect_merge (gm, m1, "merge_prepend 1"); - UNIT_TEST_CHECK(gm == m1); - - - UNIT_TEST_CHECK(merge3(anc, d2, d1, m2)); - if (gm != m2) - dump_incorrect_merge (gm, m2, "merge_prepend 2"); - UNIT_TEST_CHECK(gm == m2); -} - -UNIT_TEST(diff_patch, merge_append) -{ - UNIT_TEST_CHECKPOINT("append test"); - vector anc, d1, d2, m1, m2, gm; - for (int i = 0; i < 10; ++i) - anc.push_back(lexical_cast(i)); - - d1 = anc; - d2 = anc; - gm = anc; - - for (int i = 10; i < 20; ++i) - { - d2.push_back(lexical_cast(i)); - gm.push_back(lexical_cast(i)); - } - - UNIT_TEST_CHECK(merge3(anc, d1, d2, m1)); - if (gm != m1) - dump_incorrect_merge (gm, m1, "merge_append 1"); - UNIT_TEST_CHECK(gm == m1); - - UNIT_TEST_CHECK(merge3(anc, d2, d1, m2)); - if (gm != m2) - dump_incorrect_merge (gm, m2, "merge_append 2"); - UNIT_TEST_CHECK(gm == m2); - - -} - -UNIT_TEST(diff_patch, merge_additions) -{ - UNIT_TEST_CHECKPOINT("additions test"); - string ancestor("I like oatmeal\nI like orange juice\nI like toast"); - string desc1("I like oatmeal\nI don't like spam\nI like orange juice\nI like toast"); - string confl("I like oatmeal\nI don't like tuna\nI like orange juice\nI like toast"); - string desc2("I like oatmeal\nI like orange juice\nI don't like tuna\nI like toast"); - string good_merge("I like oatmeal\nI don't like spam\nI like orange juice\nI don't like tuna\nI like toast"); - vector anc, d1, cf, d2, m1, m2, gm; - - split_into_lines(ancestor, anc); - split_into_lines(desc1, d1); - split_into_lines(confl, cf); - split_into_lines(desc2, d2); - split_into_lines(good_merge, gm); - - UNIT_TEST_CHECK(merge3(anc, d1, d2, m1)); - if (gm != m1) - dump_incorrect_merge (gm, m1, "merge_addition 1"); - UNIT_TEST_CHECK(gm == m1); - - UNIT_TEST_CHECK(merge3(anc, d2, d1, m2)); - if (gm != m2) - dump_incorrect_merge (gm, m2, "merge_addition 2"); - UNIT_TEST_CHECK(gm == m2); - - UNIT_TEST_CHECK(!merge3(anc, d1, cf, m1)); -} - -UNIT_TEST(diff_patch, merge_deletions) -{ - string ancestor("I like oatmeal\nI like orange juice\nI like toast"); - string desc2("I like oatmeal\nI like toast"); - - vector anc, d1, d2, m1, m2, gm; - - split_into_lines(ancestor, anc); - split_into_lines(desc2, d2); - d1 = anc; - gm = d2; - - UNIT_TEST_CHECK(merge3(anc, d1, d2, m1)); - if (gm != m1) - dump_incorrect_merge (gm, m1, "merge_deletion 1"); - UNIT_TEST_CHECK(gm == m1); - - UNIT_TEST_CHECK(merge3(anc, d2, d1, m2)); - if (gm != m2) - dump_incorrect_merge (gm, m2, "merge_deletion 2"); - UNIT_TEST_CHECK(gm == m2); -} - -#endif // BUILD_UNIT_TESTS - // Local Variables: // mode: C++ // fill-column: 76 ============================================================ --- diff_patch.hh d377b6a5fab0b570b44533383244b7f03cac4c13 +++ diff_output.hh 399f633945756b8ca315ee658d632243e357226f @@ -14,10 +14,8 @@ // this file is to contain some stripped down, in-process implementations // of GNU-diffutils-like things (diff, diff3, maybe patch..) -#include "vector.hh" #include "vocab.hh" - void make_diff(std::string const & filename1, std::string const & filename2, file_id const & id1, @@ -28,11 +26,6 @@ void make_diff(std::string const & filen diff_type type, std::string const & pattern); -bool merge3(std::vector const & ancestor, - std::vector const & left, - std::vector const & right, - std::vector & merged); - // Local Variables: // mode: C++ // fill-column: 76 ============================================================ --- interner.hh cbf57f9a888faf417eb9bb0cba373a18589b7c5b +++ interner.hh 149f63e6eb3f1b29d26d017d5e94d8482327afd6 @@ -10,7 +10,7 @@ // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. - +#include "vector.hh" #include "hash_map.hh" #include "sanity.hh" ============================================================ --- merge.cc f913e742c88967bed03438ba82c443015fefb335 +++ merge_content.cc 678fec71c1a640e4c91ac0c601fccda6d90a358f @@ -9,15 +9,15 @@ // PURPOSE. #include "base.hh" -#include "merge.hh" +#include "merge_content.hh" #include "constants.hh" #include "database.hh" -#include "diff_patch.hh" +#include "diff_output.hh" #include "file_io.hh" #include "lua_hooks.hh" #include "revision.hh" -#include "roster_merge.hh" +#include "merge_roster.hh" #include "simplestring_xform.hh" #include "transforms.hh" #include "xdelta.hh" ============================================================ --- merge.hh 837e0f0510935be055622c626378e289e6f5a59e +++ merge_content.hh a4003604503ede3dc6a72b7daeaa326f5931f6f4 @@ -263,6 +263,15 @@ store_roster_merge_result(database & db, revision_id const & right_rid, revision_id & merged_rid); +// Do a three-way merge on file content, expressed as vectors of +// strings (one per line). + +bool merge3(std::vector const & ancestor, + std::vector const & left, + std::vector const & right, + std::vector & merged); + + // Local Variables: // mode: C++ // fill-column: 76 ============================================================ --- roster_merge.cc ca479751ee42bfec579dc26bfe5d330b22607e61 +++ merge_roster.cc acfb17d85ecf0a9691ebb8833817b43750b5725d @@ -16,7 +16,7 @@ #include "lua_hooks.hh" #include "options.hh" #include "parallel_iter.hh" -#include "roster_merge.hh" +#include "merge_roster.hh" #include "safe_map.hh" #include "transforms.hh" #include "vocab.hh" ============================================================ --- roster_merge.hh f530fb6fe1086d629f82d12b73d5019754615c48 +++ merge_roster.hh 5bb429497a38b162b753014738c602b582c2fbbe @@ -15,7 +15,7 @@ #include "rev_types.hh" #include "database.hh" -#include "merge.hh" +#include "merge_content.hh" #include "roster.hh" // needs full definition of roster_t available // interactions between conflict types: ============================================================ --- po/POTFILES.in 965ad78c697c24a5ed21a77a7c63226905090316 +++ po/POTFILES.in 639dab3c215bfa742d5c757537da5069a3a83744 @@ -1,21 +1,29 @@ adler32.hh adler32.hh +ancestry.cc +annotate.cc +annotate.hh app_state.cc app_state.hh asciik.cc asciik.hh automate.cc +base.hh basic_io.cc basic_io.hh +botan_pipe_cache.hh cert.cc cert.hh +char_classifiers.hh charset.cc +charset.hh cleanup.hh -cmd.hh cmd_automate.cc -cmd_db.cc +cmd.cc cmd_conflicts.cc +cmd_db.cc cmd_diff_log.cc cmd_files.cc +cmd.hh cmd_key_cert.cc cmd_list.cc cmd_merging.cc @@ -27,32 +35,71 @@ constants.hh commands.hh constants.cc constants.hh +cset.cc +cset.hh +current_exception.hh cycle_detector.hh database.cc +database_check.cc database.hh -database_check.cc dates.cc -diff_patch.cc -diff_patch.hh +dates.hh +diff_output.cc +diff_output.hh +enumerator.cc +enumerator.hh +epoch.cc +epoch.hh file_io.cc file_io.hh globish.cc +globish.hh +graph.cc +graph.hh +gzip.cc +gzip.hh +hash_map.hh +hmac.cc +hmac.hh +hybrid_map.hh +inodeprint.cc +inodeprint.hh interner.hh -key_store.cc keys.cc keys.hh +key_store.cc +key_store.hh lcs.cc lcs.hh +legacy.cc +legacy.hh +lexical_cast.hh +lru_writeback_cache.hh lua.cc +luaext_globish.cc +luaext_guess_binary.cc +luaext_mkstemp.cc +luaext_parse_basic_io.cc +luaext_platform.cc lua.hh lua_hooks.cc -merge.cc -merge.hh +lua_hooks.hh +merge_3way.cc +merge_content.cc +merge_content.hh +merge_roster.cc +merge_roster.hh merkle_tree.cc merkle_tree.hh +migrate_ancestry.cc +migrate_schema.cc +migrate_work.cc +migration.hh mkstemp.cc mkstemp.hh monotone.cc +mtn-sanity.cc +mtn-sanity.hh mt_version.cc mt_version.hh netcmd.cc @@ -60,36 +107,67 @@ netxx_pipe.cc netio.hh netsync.cc netxx_pipe.cc +netxx_pipe.hh +numeric_vocab.cc numeric_vocab.hh option.cc +option.hh +options.cc +options.hh options_list.hh +origin_type.hh +outdated_indicator.cc +outdated_indicator.hh packet.cc packet.hh +parallel_iter.hh paths.cc paths.hh pcrewrap.cc +pcrewrap.hh platform.hh +platform-wrapped.hh +project.cc +project.hh quick_alloc.hh randomfile.hh +randomizer.cc +randomizer.hh rcs_file.cc rcs_file.hh rcs_import.cc rcs_import.hh refiner.cc +refiner.hh restrictions.cc +restrictions.hh +rev_height.cc +rev_height.hh revision.cc revision.hh +rev_types.hh roster.cc -roster_merge.cc +roster_delta.cc +roster_delta.hh +roster.hh safe_map.hh sanity.cc sanity.hh -schema_migration.cc -schema_migration.hh selectors.cc +selectors.hh sha1.cc +sha1_engine.hh +sha1.hh +simplestring_xform.cc +simplestring_xform.hh +specialized_lexical_cast.cc ssh_agent.cc +ssh_agent.hh std_hooks.lua +string_queue.cc +string_queue.hh +tester.cc +tester-plaf.hh transforms.cc transforms.hh txt2c.cc @@ -105,8 +183,12 @@ uri.hh update.hh uri.cc uri.hh +vector.hh +vocab_cast.hh vocab.cc +vocab_hash.hh vocab.hh +vocab_macros.hh vocab_terms.hh win32/fs.cc win32/get_system_flavour.cc @@ -115,6 +197,5 @@ work.hh win32/ssh_agent_platform.cc work.cc work.hh -work_migration.cc xdelta.cc xdelta.hh ============================================================ --- work.cc 56a3a0f37a20d60df660b7d0cf00092ca3b6470b +++ work.cc f72a10818deea7e7140ff2a4be92289238577854 @@ -27,7 +27,7 @@ #include "simplestring_xform.hh" #include "revision.hh" #include "inodeprint.hh" -#include "merge.hh" +#include "merge_content.hh" #include "charset.hh" #include "app_state.hh" #include "database.hh"