On Jan 8, 2008 6:49 PM, Matt Mackall &lt;<a href="mailto:mpm@selenic.com">mpm@selenic.com</a>&gt; wrote:<br><div class="gmail_quote"><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
<div><div></div><div class="Wj3C7c"><br>On Mon, 2008-01-07 at 15:20 -0600, Steve Borho wrote:<br>&gt; # HG changeset patch<br>&gt; # User Steve Borho &lt;<a href="mailto:steve@borho.org">steve@borho.org</a>&gt;<br>&gt; # Date 1199739435 21600
<br>&gt; # Node ID 1dabe5c4867c4f1d855494ecd0f76d6436882c6e<br>&gt; # Parent &nbsp;3ef279074c77c3cf3f6b35f0f73dee2fdba5aa41<br>&gt; hgmerge: add new hgmerge package under mercurial<br>&gt;<br>&gt; diff --git a/mercurial/hgmerge/README.txt b/mercurial/hgmerge/README.txt
<br>&gt; new file mode 100644<br>&gt; --- /dev/null<br>&gt; +++ b/mercurial/hgmerge/README.txt<br>&gt; @@ -0,0 +1,96 @@<br>&gt; +Cross-platform merging for Mercurial.<br>&gt; +<br>&gt; +The purpose of hgmerge is to provide a file revision merging interface, with
<br>&gt; +good default behavior and user configurability. &nbsp;It is the default merge<br>&gt; +behavior for Mercurial when HGMERGE is not set in your environmant and ui.merge<br>&gt; +is not set in your hgrc file(s).<br>&gt; +
<br>&gt; +When unconfigured, hgmerge will attempt to perform a 3-way merge using an<br>&gt; +included simplemerge script that runs without user interaction. &nbsp;If no conflicts<br>&gt; +are found, the merge is successful. &nbsp;When conflicts are found, hgmerge will
<br>&gt; +search for interactive 3-way merge tools on your computer and use the first one<br>&gt; +it finds. &nbsp;If no tools are detected (or if requested tool is not found), the<br>&gt; +partially merged file with conflict markers is left in the working directory to
<br>&gt; +be resolved manually. &nbsp;Note that Mercurial does not track &#39;conflict&#39; file<br>&gt; +status, so users have to be diligent to not check in partially merged files.<br>&gt; +<br>&gt; +The &#39;hg debuginstall&#39; command will report the list of plug-ins that are detected
<br>&gt; +on your machine.<br>&gt; +<br>&gt; +By adding entries in their hgrc files, users can:<br>&gt; +* completely override the search by specifying a default plug-in<br>&gt; +* specify plugins to use for specific file extensions
<br>&gt; +* modify the built-in plug-ins (override characteristics)<br>&gt; +* define search precedence for plug-ins<br>&gt; +* define entirely new plug-ins.<br>&gt; +<br>&gt; +Hgmerge defines two new hgrc sections. &nbsp;[hgmerge] is used to define default and
<br>&gt; +file extension based plug-ins. &nbsp;[hgmerge-plugins] is used to configure built-in<br>&gt; +plugins and to define entirely new plugins.<br>&gt; +<br>&gt; +Example hgmerge section:<br>&gt; +[hgmerge]<br>&gt; +default = kdiff3
<br>&gt; +ext.png = mypngmerge<br>&gt; +ext.lib = takemine<br>&gt; +<br>&gt; +When a file extension plug-in is specified (e.g., .png), hgmerge will bypass the<br>&gt; +initial simplemerge step and directly call the specified plug-in. &nbsp;This is
<br>&gt; +useful for e.g. binary formats that cannot be merged as text. &nbsp;There are two<br>&gt; +special plug-ins intended for file extension use: &#39;takemine&#39; and &#39;takeother&#39;<br>&gt; +(with predictable behaviors). &nbsp;These two will not show up in the list of
<br>&gt; +installed plug-ins but are always available.<br>&gt; +<br>&gt; +Note that unless the plug-in was selected by a file extension match, hgmerge<br>&gt; +will specially handle file types which are unmergeable by most merge tools
<br>&gt; +(symlinks, binary files, etc). &nbsp;Unmergeable files bypass the entire simplemerge<br>&gt; +and plugin architecture and instead the user will be asked to chose between the<br>&gt; +&#39;local&#39; and &#39;other&#39; versions of the file.
<br>&gt; +<br>&gt; +Plug-In Configuration<br>&gt; +=====================<br>&gt; +Merge plug-ins can be configured through Mercurial&#39;s configuration system<br>&gt; +(hgrc). One may modify existing plug-ins or define new ones. Plug-in
<br>&gt; +configurations exist in the section [hgmerge-plugins].<br>&gt; +<br>&gt; +Plug-ins can be of one of two types: tool and custom. The default type is tool.<br>&gt; +<br>&gt; +Tool Plug-Ins<br>&gt; +-------------<br>
&gt; +A tool definition represents an external program and parameters for running<br>&gt; +that, such as its arguments. The argument line can contain variables, the<br>&gt; +following are supported: $base, $local, $other, $output.
<br>&gt; +<br>&gt; +The following options can be set for a tool:<br>&gt; + &nbsp; &nbsp; executable: Either just the name of the executable or its pathname. Per<br>&gt; + &nbsp; &nbsp; default the same as the plug-in&#39;s name.<br>&gt; + &nbsp; &nbsp; arguments: The arguments to pass to the tool (default: $base $local $other
<br>&gt; + &nbsp; &nbsp; $output)<br>&gt; + &nbsp; &nbsp; priority: The priority in which to evaluate this plug-in.<br>&gt; + &nbsp; &nbsp; stdout: Should the tool&#39;s standard output be used as the merge result?<br>&gt; + &nbsp; &nbsp; check_conflicts: Check whether there are conflicts even though the tool
<br>&gt; + &nbsp; &nbsp; reported none?<br>&gt; + &nbsp; &nbsp; win.regpath_installdir: Specify a pathname in the Windows registry defining<br>&gt; + &nbsp; &nbsp; the tool&#39;s installation directory. The format of this option is like this:<br>&gt; + &nbsp; &nbsp; &lt;key name&gt;\&lt;value name&gt;. If the key itself actually holds the value, end
<br>&gt; + &nbsp; &nbsp; the pathname with a backslash, so it&#39;s clear there is no value name<br>&gt; + &nbsp; &nbsp; component.<br>&gt; + &nbsp; &nbsp; win.regpath_installpath: Like the former, except that the registry value<br>&gt; + &nbsp; &nbsp; is taken to specify the installation path of the tool&#39;s executable.
<br>&gt; +<br>&gt; +Example tool configuration:<br>&gt; +[hgmerge-plugins]<br>&gt; +gvimdiff.type = tool<br>&gt; +gvimdiff.arguments = --nofork -d -g -O $output $other $base<br>&gt; +gvimdiff.priority = 1<br>&gt; +gvimdiff.win.regpath_installpath = Software\Vim\GVim\path
<br>&gt; +kdiff3.executable = ~/bin/kdiff3 &nbsp; # override built in plug-in value<br>&gt; +<br>&gt; +Note that the plugin type defaults to tool and can be left unspecified, and the<br>&gt; +priority defaults to 0 (higher priority tools are detected first).
<br>&gt; +<br>&gt; +Custom Plug-Ins<br>&gt; +---------------<br>&gt; +A &quot;custom&quot; plug-in is defined by a Python class. TODO ...<br>&gt; +<br>&gt; +# vim: textwidth=80<br>&gt; diff --git a/mercurial/hgmerge/TODO.txt b/mercurial/hgmerge/TODO.txt
<br>&gt; new file mode 100644<br>&gt; --- /dev/null<br>&gt; +++ b/mercurial/hgmerge/TODO.txt<br>&gt; @@ -0,0 +1,5 @@<br>&gt; +* Allow user to turn off automatic invocation of merge tool<br>&gt; +* Port tests to standard Mercurial system
<br>&gt; +<br>&gt; +MacOS &nbsp; &nbsp; &nbsp; &nbsp; - needs wrappers for more OSX tools<br>&gt; + &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;- needs testing<br><br></div></div>Let&#39;s not have a TODO file. I&#39;d prefer not to have a separate directory<br>too, but let&#39;s see..
</blockquote><div class="Wj3C7c"><br>True, it&#39;s for initial development.&nbsp;<br><br>&gt; diff --git a/mercurial/hgmerge/__init__.py b/mercurial/hgmerge/__init__.py<br>&gt; new file mode 100644<br>&gt; --- /dev/null<br>&gt; +++ b/mercurial/hgmerge/__init__.py
<br>&gt; @@ -0,0 +1,544 @@<br>&gt; +# -*- coding: utf-8 -*-<br>&gt; +&quot;&quot;&quot; Logic for merging changes in two versions of a file.<br>&gt; +@var stockplugins: Sequence of stock plug-in representations.<br>&gt; +&quot;&quot;&quot;
<br>&gt; +import shutil<br>&gt; +import StringIO<br>&gt; +import filecmp<br>&gt; +import random<br>&gt; +import traceback<br>&gt; +<br>&gt; +from mercurial.hgmerge._common import *<br>&gt; +import mercurial.util as hgutil
<br>&gt; +import _simplemerge<br>&gt; +import _plugins as hgmergeplugs<br>&gt; +import _pluginapi as hgpluginapi<br>&gt; +<br>&gt; +stockplugins = hgmergeplugs.plugins<br>&gt; +<br>&gt; +# Initially not defined<br>&gt; +plugins = None
<br>&gt; +<br>&gt; +class filedesc(object):<br>&gt; + &nbsp; &nbsp;&#39;&#39;&#39; Describe properties of a file.<br>&gt; + &nbsp; &nbsp;@ivar name: The file&#39;s name.<br>&gt; + &nbsp; &nbsp;@ivar islink: Is the file a symlink?<br>&gt; + &nbsp; &nbsp;&#39;&#39;&#39;
<br><br></div><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">I detect design overkill.</blockquote><div>&nbsp;</div><div>How? It&#39;s a direct subset of Mercurial file contexts. If necessary it may grow to share more attributes with the latter. Don&#39;t recall off the top of my head why we didn&#39;t pass in file contexts directly.
<br></div><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;"><div class="Ih2E3d"><br>&gt; +class _pluginopts(object):<br>&gt; + &nbsp; &nbsp;&#39;&#39;&#39; Plug-in options handler baseclass.
<br>&gt; + &nbsp; &nbsp;&#39;&#39;&#39;<br>&gt; + &nbsp; &nbsp;def __init__(self, ui, sectname, name):<br>&gt; + &nbsp; &nbsp; &nbsp; &nbsp;self.attrs = {}<br>&gt; + &nbsp; &nbsp; &nbsp; &nbsp;self.__ui, self.__sect, self.__name = ui, sectname, name<br>&gt; + &nbsp; &nbsp; &nbsp; &nbsp;self._set_int(&#39;priority&#39;, default=0)
<br>&gt; +<br>&gt; + &nbsp; &nbsp;def _set_string(self, prop, **kwds):<br>&gt; + &nbsp; &nbsp; &nbsp; &nbsp;self._set_opt(prop, self.__ui.config, **kwds)<br>&gt; +<br>&gt; + &nbsp; &nbsp;def _set_bool(self, prop, **kwds):<br>&gt; + &nbsp; &nbsp; &nbsp; &nbsp;self._set_opt(prop, self.__ui.configbool, **kwds)
<br><br></div>Yes, looks like someone&#39;s a Java fan.<br></blockquote><div><br>I don&#39;t write Java. I don&#39;t have anything against it for that matter either .. This is more typical generic programming, which I&nbsp; _am_ a fan of. My design typically turns out the way it does based on (unit) testability.
<br></div><div>&nbsp;</div><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">This code is about 5 times as long as it needs to be. Needs a few rounds
<br>of simplifying.<br><div class="Ih2E3d"><br>&gt; +def _is_mergeable(base, local, other):<br>&gt; + &nbsp; &nbsp;&#39;&#39;&#39; Are the files mergeable?<br>&gt; + &nbsp; &nbsp;@return: Success?<br>&gt; + &nbsp; &nbsp;&#39;&#39;&#39;<br>&gt; + &nbsp; &nbsp;mergetypes = (&#39;dos&#39;, &#39;unix&#39;, &#39;mac&#39;)
<br>&gt; +<br>&gt; + &nbsp; &nbsp;for eoltp in (local.eoltype, other.eoltype, base.eoltype):<br>&gt; + &nbsp; &nbsp; &nbsp; &nbsp;if eoltp not in mergetypes:<br>&gt; + &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;return False<br><br></div>Hmmm. Don&#39;t really like this. Does it apply to user-defined tools?
</blockquote><div>&nbsp;</div><div class="Ih2E3d">Not sure what you mean by that. It is not consulted if a tool is specified for this kind of file at least.<br><br>&gt; +def _readfile(fname):<br>&gt; + &nbsp; &nbsp;f = file(fname, &#39;rb&#39;)
<br>&gt; + &nbsp; &nbsp;try: data = f.read()<br>&gt; + &nbsp; &nbsp;finally: f.close()<br>&gt; + &nbsp; &nbsp;return data<br><br></div><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
This function should be taken out and shot.<br><div class="Ih2E3d"></div></blockquote><div><br>I don&#39;t see why, but what do I know.&nbsp;<br><br></div><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
<div class="Ih2E3d">&gt; \ No newline at end of file<br><br></div>Oops.<br><div class="Ih2E3d"></div></blockquote><div><br>Oops.<br>&nbsp;<br></div><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
<div class="Ih2E3d"><br>&gt; diff --git a/contrib/simplemerge b/mercurial/hgmerge/_simplemerge.py<br>&gt; old mode 100755<br>&gt; new mode 100644<br>&gt; copy from contrib/simplemerge<br>&gt; copy to mercurial/hgmerge/_simplemerge.py
<br><br></div>I think we have tests that want to use this.<br><div class="Ih2E3d"><br>&gt; --- /dev/null<br>&gt; +++ b/mercurial/hgmerge/design.txt<br>&gt; @@ -0,0 +1,26 @@<br>&gt; +Hgmerge&#39;s design explained<br><br></div>
Put this in a file docstring.<br></blockquote><div><br>Sure, it was written merely to hash out initial ideas anyway.<br><br>Arve<br></div></div><br>