[MERGE] new merge-type=lca
Aaron Bentley
aaron at aaronbentley.com
Thu Jan 3 05:53:50 GMT 2008
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
Andrew Bennetts wrote:
> Aaron Bentley wrote:
> This is a very good description. It'd be great to preserve it for interested
> developers in doc/developers somewhere.
Okay.
> I wonder if this merge is good enough that we don't need to warn users that
> we've detected a criss-cross when merging?
In the case of criss-cross, they will sometimes see their merge
resolutions shown as conflicts with alternative resolutions of the same
conflict (this is what the 'conflicted-a' / 'conflicted-b' lines do).
So I *think* that it still makes sense to warn them that the merge may
be a little weirder than normal. What do you think?
>> + def _merged_lines(self, file_id):
...
> It's a shame you need to duplicate so many lines here to change one function
> call. I guess it's not worth adding complexity to do something about it.
Yeah, it seems like a pretty even trade.
>> + @staticmethod
>> + def _subtract_plans(old_plan, new_plan):
>> + matcher = patiencediff.PatienceSequenceMatcher(None, old_plan,
>> + new_plan)
>> + last_j = 0
>> + for i, j, n in matcher.get_matching_blocks():
>> + for jj in range(last_j, j):
>> + yield new_plan[jj]
>> + for jj in range(j, j+n):
>> + plan_line = new_plan[jj]
>> + if plan_line[0] == 'new-b':
>> + pass
>> + elif plan_line[0] == 'killed-b':
>> + yield 'unchanged', plan_line[1]
>> + else:
>> + yield plan_line
>> + last_j = j + n
>
> I see this function is just moved, rather than added, but I find it very hard to
> understand. It's not obvious to me why subtracting one plan from another should
> produce this particular transformation.
>
> It's tangential to your change, so don't feel that you need to do this to get my
> approval for lca-merge, but some explanatory comments for this functional would
> be extremely helpful.
Here's the explanation I'm planning to put:
"""Remove changes from new_plan that came from old_plan.
It is assumed that the difference between the old_plan and new_plan
is their choice of 'b' text.
All lines from new_plan that differ from old_plan are emitted
verbatim. All lines from new_plan that match old_plan but are
not about the 'b' revision are emitted verbatim.
Lines that match and are about the 'b' revision are the lines we
don't want, so we convert 'killed-b' -> 'unchanged', and 'new-b'
is skipped entirely.
"""
I don't feel like that explanation's crystal clear, but it's the best
I've been able to come up with. What do you think?
>> + def _determine_status(self, revision_id, unique_lines):
>> + """Determines the status unique lines versus all lcas.
>> +
>> + Basically, determines why the line is unique to this revision.
>> +
>> + A line may be determined new or killed, but not both.
>> +
>> + :return a tuple of (new_this, killed_other):
>> + """
>> + new = self._find_new(revision_id)
>> + killed = set(unique_lines).difference(new)
>> + return new, killed
>
> It'd help to say something like “:param unique_lines: a collection of line
> numbers”. Or perhaps just rename it to “unique_line_numbers”. When I see a
> variable called “foo_line”, I tend to expect it to be the contents of the line,
> not a line number.
Makes sense.
>> +class _PlanLCAMerge(_PlanMergeBase):
>> + """
>> + This implementation differs from _PlanMerge.plan_merge in that:
> It's odd to describe a class' implementation as differing from a method.
> Did you mean to say “This implementation differs from _PlanMergeBase in that:
> ...”?
Docstring represents an earlier sate, where PlanMerge had 'plan_merge'
and 'plan_lca_merge' members instead of there being two classes. Will fix.
>> + def test_lca_merge_criss_cross(self):
>> + tree_a = self.make_branch_and_tree('a')
>> + self.build_tree_contents([('a/file', 'base-contents\n')])
>> + tree_a.add('file')
>> + tree_a.commit('', rev_id='rev1')
>> + tree_b = tree_a.bzrdir.sprout('b').open_workingtree()
>> + self.build_tree_contents([('a/file',
>> + 'base-contents\nthis-contents\n')])
>> + tree_a.commit('', rev_id='rev2a')
>> + self.build_tree_contents([('b/file',
>> + 'base-contents\nother-contents\n')])
>> + tree_b.commit('', rev_id='rev2b')
>> + self.build_tree_contents([('a/file',
>> + 'base-contents\nthis-contents\n')])
>
> Isn't this call to build_tree_contents redundant?
Yes. Good catch.
>> + def plan_lca_merge(self, ver_a, ver_b, base=None):
>> + from merge import _PlanLCAMerge
>
> Relative imports are fragile. Always use “from bzrlib.foo ...”
The spirit is willing, but the flesh is weak.
Thanks for your review.
Aaron
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.6 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org
iD8DBQFHfHhu0F+nu1YWqI0RAmX8AJ440BoHy7KrEMrB/QY/ziYlFjK9YQCfbjKb
rUgOAMOC9i8XohS+kLhH8tI=
=1gjt
-----END PGP SIGNATURE-----
More information about the bazaar
mailing list