<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>il libero &#187; Software Libre</title>
	<atom:link href="http://www.miltonpividori.com.ar/category/software/software-libre/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.miltonpividori.com.ar</link>
	<description>Blog de Milton Pividori</description>
	<lastBuildDate>Mon, 02 Jan 2012 04:12:27 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Ubuntu, impresoras idénticas, udev</title>
		<link>http://www.miltonpividori.com.ar/2011/02/22/ubuntu-impresoras-identicas-udev/</link>
		<comments>http://www.miltonpividori.com.ar/2011/02/22/ubuntu-impresoras-identicas-udev/#comments</comments>
		<pubDate>Tue, 22 Feb 2011 13:47:49 +0000</pubDate>
		<dc:creator>miltondp</dc:creator>
				<category><![CDATA[GNU/Linux]]></category>
		<category><![CDATA[Software Libre]]></category>

		<guid isPermaLink="false">http://www.miltonpividori.com.ar/?p=1004</guid>
		<description><![CDATA[Hace un tiempo, en la empresa de mi viejo, compramos dos impresoras Samsung ML-1665. Son impresoras láser y se están utilizando para imprimir facturas. La máquina de la caja, a la cual están conectadas, tiene Ubuntu 10.04. Después de seguir un excelente howto para instalarlas, la primera fue detectada correctamente y comenzó a funcionar a [...]]]></description>
			<content:encoded><![CDATA[<p>Hace un tiempo, en la empresa de mi viejo, compramos dos impresoras Samsung ML-1665. Son impresoras láser y se están utilizando para imprimir facturas. La máquina de la caja, a la cual están conectadas, tiene Ubuntu 10.04. Después de seguir un excelente <a href="http://ubuntuforums.org/showthread.php?t=341621">howto</a> para instalarlas, la primera fue detectada correctamente y comenzó a funcionar a la perfección.</p>
<p>Al conectar la segunda impresora, exactamente igual que la primera, la detección fue también satifactoria. Aunque surgió un pequeño gran problema: al imprimir sobre ésta última, la impresión se realizaba sobre la primera. Observando la configuración de ambas, me percaté de que tenían exactamente el mismo Device URI.</p>
<p>La solución fue bastante rápida: editar el archivo /etc/cups/printers.conf, y cambiar el device URI, que era algo así como &#8220;usb:/Samsung&#8230;&#8221; por &#8220;file:/dev/usblp0&#8243;, y la otra impresora por &#8220;file:/dev/usblp1&#8243;. Con esto el problema fue resuelto y todo comenzó a funcionar como uno esperaba. Aunque no tardó en surgir otro inconveniente.</p>
<p><span id="more-1004"></span><br />
Después de un tiempo de estar funcionando todo de mil maravillas, la cajera me comenta un día que, para sorpresa de ella, las facturas A se imprimían en la impresora para las facturas B, y viceversa. Me había comentado que no fue mucho problema, ya que simplemente cambió las hojas y tema resuelto. Pero desde ese mismo instante no me quedé muy tranquilo.</p>
<p>Después de un tiempo recibo una nueva llamada: las impresoras estaban enloquecidas. Todos los días, al encender la PC, los puertos se cambiaban. Lo que en el principio era una pequeña molestia, ahora se estaba transformando en una situación bastante irritante. No estaba seguro cómo solucionarlo, así que por lo tanto intenté una actualización del sistema, lo que en apariencias lo había solucionado. Duró uno o dos meses más hasta que el problema reapareció.</p>
<p>Entonces esa vez me puse a buscar un poco más, a preguntar, y me encontré con <a href="http://en.wikipedia.org/wiki/Udev">udev</a>. Este componente del kernel Linux, encargado de gestionar dispositivos, permite, entre muchas cosas, crear reglas para: nombrarlos de una forma distinta (cambiar &#8220;sda1&#8243; por &#8220;mi_disco&#8221;), agregar links simbólicos cuando se cumplan ciertas condiciones, ejecutar comandos ante ciertos eventos, y mil cosas más.</p>
<p>Lo que tuve que hacer, entonces, es buscar algún tipo de información en los atributos de cada impresora, que sea único para la misma. Para esto utilicé el comando udevadm:</p>
<pre class="brush: bash; title: ; notranslate">
$ udevadm info -a -p  $(udevadm info -q path -n /dev/usblp0)

[...]

  looking at parent device '/devices/pci0000:00/0000:00:1d.3/usb5/5-2':
    KERNELS==&quot;5-2&quot;
    SUBSYSTEMS==&quot;usb&quot;
    DRIVERS==&quot;usb&quot;
    ATTRS{configuration}==&quot;&quot;
    ATTRS{bNumInterfaces}==&quot; 1&quot;
    ATTRS{bConfigurationValue}==&quot;1&quot;
    ATTRS{bmAttributes}==&quot;c0&quot;
    ATTRS{bMaxPower}==&quot;  0mA&quot;
    ATTRS{urbnum}==&quot;1368&quot;
    ATTRS{idVendor}==&quot;04e8&quot;
    ATTRS{idProduct}==&quot;3301&quot;
    ATTRS{bcdDevice}==&quot;0100&quot;
    ATTRS{bDeviceClass}==&quot;00&quot;
    ATTRS{bDeviceSubClass}==&quot;00&quot;
    ATTRS{bDeviceProtocol}==&quot;00&quot;
    ATTRS{bNumConfigurations}==&quot;1&quot;
    ATTRS{bMaxPacketSize0}==&quot;16&quot;
    ATTRS{speed}==&quot;12&quot;
    ATTRS{busnum}==&quot;5&quot;
    ATTRS{devnum}==&quot;2&quot;
    ATTRS{version}==&quot; 1.10&quot;
    ATTRS{maxchild}==&quot;0&quot;
    ATTRS{quirks}==&quot;0x0&quot;
    ATTRS{authorized}==&quot;1&quot;
    ATTRS{manufacturer}==&quot;Samsung Electronics Co., Ltd.&quot;
    ATTRS{product}==&quot;ML-1660 Series&quot;
    ATTRS{serial}==&quot;Z526BKDZ800022B.&quot;
</pre>
<p>El atributo <em>serial</em> es el que difiere en cada impresora, por lo tanto utilicé éste para crear un archivo <em>/etc/udev/rules.d/impresoras.rules</em> con las siguientes reglas:</p>
<pre class="brush: bash; title: ; notranslate">
SUBSYSTEMS==&quot;usb&quot;, ATTRS{product}==&quot;ML-1660 Series&quot;, ATTRS{serial}==&quot;Z526BKAZ301606A.&quot;, SYMLINK+=&quot;impr_fa&quot;
SUBSYSTEMS==&quot;usb&quot;, ATTRS{product}==&quot;ML-1660 Series&quot;, ATTRS{serial}==&quot;Z526BKDZ800022B.&quot;, SYMLINK+=&quot;impr_fb&quot;
</pre>
<p>Una vez guardado el archivo, hacen un &#8220;service udev reload&#8221;, desenchufan y vuelven a enchufar las impresoras, y verán que aparecen los dispositivos &#8220;impr_fa&#8221; e &#8220;impr_fb&#8221;. Ahora estos serán siempre fijos y aputarán siempre al mismo dispositivo físico (en teoría, les cuento en unos meses). Tal cual lo que estaba buscando. Si hacemos un &#8220;ls&#8221; vemos:</p>
<pre class="brush: bash; title: ; notranslate">
$ ls -l /dev/impr*
lrwxrwxrwx 1 root root 7 2011-02-22 07:34 /dev/impr_fa -&gt; usb/lp1
lrwxrwxrwx 1 root root 7 2011-02-22 10:25 /dev/impr_fb -&gt; usb/lp0
</pre>
<p>Pueden encontrar más información sobre cómo escribir reglas udev en <a href="http://reactivated.net/writing_udev_rules.html">este link</a>. A mí me vino muy bien para las impresoras, pero se podrían hacer cosas interesantes con pendrives y otros dispositivos.</p>
<p>Cuando les mostré esto a <a href="http://www.cesarsandrigo.com.ar/">César</a> y a <a href="http://nacho.larrateguy.com.ar/">Nacho</a>, nos preguntábamos si en Windows será posible hacer algo similar. Una de las respuestas fue: <em>&#8220;estas cosas [impresoras enloquecidas] no pasan en Windows&#8221;</em> <img src='http://www.miltonpividori.com.ar/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />  y <em>&#8220;seguramente debe tener alguna forma de configurarlo&#8221;</em>. Solo por curiosidad: ¿alguien tiene alguna información al respecto?</p>
]]></content:encoded>
			<wfw:commentRss>http://www.miltonpividori.com.ar/2011/02/22/ubuntu-impresoras-identicas-udev/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Gtk3 y HTML5</title>
		<link>http://www.miltonpividori.com.ar/2010/11/24/gtk3-y-html5/</link>
		<comments>http://www.miltonpividori.com.ar/2010/11/24/gtk3-y-html5/#comments</comments>
		<pubDate>Wed, 24 Nov 2010 12:50:34 +0000</pubDate>
		<dc:creator>miltondp</dc:creator>
				<category><![CDATA[GNOME]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[Software Libre]]></category>

		<guid isPermaLink="false">http://www.miltonpividori.com.ar/?p=941</guid>
		<description><![CDATA[Hoy me encontré con un impresionante video en el Planet GNOME: Ojalá los bindings para Mono no se retrasen mucho.]]></description>
			<content:encoded><![CDATA[<p>Hoy me encontré con un impresionante video en el <a href="http://planet.gnome.org/">Planet GNOME</a>:</p>
<p><object type="application/x-shockwave-flash" data="http://www.vimeo.com/moogaloop.swf" width="480" height="322" id="vvq-143-vimeo-1" style="visibility: visible; "><param name="wmode" value="opaque"><param name="allowfullscreen" value="true"><param name="allowscriptacess" value="always"><param name="flashvars" value="wmode=opaque&amp;allowfullscreen=true&amp;allowscriptacess=always&amp;server=www.vimeo.com&amp;clip_id=17132064&amp;show_portrait=0&amp;show_title=1&amp;show_byline=1&amp;fullscreen=1"></object></p>
<p>Ojalá los bindings para Mono no se retrasen mucho.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.miltonpividori.com.ar/2010/11/24/gtk3-y-html5/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Si usás Buildbot con Mercurial&#8230;</title>
		<link>http://www.miltonpividori.com.ar/2009/11/28/si-usas-buildbot-con-mercurial/</link>
		<comments>http://www.miltonpividori.com.ar/2009/11/28/si-usas-buildbot-con-mercurial/#comments</comments>
		<pubDate>Sat, 28 Nov 2009 19:14:42 +0000</pubDate>
		<dc:creator>miltondp</dc:creator>
				<category><![CDATA[GNU/Linux]]></category>
		<category><![CDATA[Mercurial]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[Software Libre]]></category>

		<guid isPermaLink="false">http://www.miltonpividori.com.ar/?p=749</guid>
		<description><![CDATA[&#8230; y la versión de Buildbot es la 0.7.11p3 (la última al momento de escribir este post) y Mercurial 1.3.1, que son las versiones que están en Karmic, quizá tengas un problema cuando el Build Slave intenta bajar el código del repositorio para comenzar el ciclo de compilación/testing. Buildbot permite configurar de varias formas el [...]]]></description>
			<content:encoded><![CDATA[<p>&#8230; y la versión de <a href="http://buildbot.net/">Buildbot</a> es la 0.7.11p3 (la última al momento de escribir este post) y <a href="http://mercurial.selenic.com/">Mercurial</a> 1.3.1, que son las versiones que están en Karmic, quizá tengas un problema cuando el Build Slave intenta bajar el código del repositorio para comenzar el ciclo de compilación/testing.</p>
<div id="attachment_757" class="wp-caption aligncenter" style="width: 310px"><a href="http://www.miltonpividori.com.ar/wp-content/uploads/2009/11/overview.png"><img src="http://www.miltonpividori.com.ar/wp-content/uploads/2009/11/overview-300x180.png" alt="Arquitectura de Buildbot" title="overview" width="300" height="180" class="size-medium wp-image-757" /></a><p class="wp-caption-text">Arquitectura de Buildbot</p></div>
<p>Buildbot permite <a href="http://djmitche.github.com/buildbot/docs/0.7.11/#Source-Checkout">configurar de varias formas el modo en que va a obtener el código fuente del proyecto y cómo realizará las actualizaciones subsiguientes</a>. Por ejemplo, el modo &#8220;update&#8221; hace que las operaciones de checkout/update se ejecuten en el directorio de trabajo, y no en uno independiente, como en el modo &#8220;copy&#8221; o &#8220;clobber&#8221;, que mantienen un directorio separado y limpio del repositorio (en el caso de Mercurial, &#8220;copia de trabajo&#8221; en el caso de Subversion) y hacen una copia del mismo para realizar la compilación (esto asegura que siempre se compile todo y que no influyan archivos generados en procesos anteriores, además de otros problemas menos comunes pero que existen).</p>
<p>El problema que estuve teniendo es cuando utilizo Mercurial. Al intentar traer el código fuente, Buildbot entra en un bucle infinito donde realiza el checkout (clone), borra el directorio, otra vez clone y asi&#8230; me pareció rarísimo el comportamiento. Versiones anteriores de Buildbot con versiones anteriores de Mercurial funcionan bien.</p>
<p>En uno de los pasos para realizar el checkout/update, Buildbot verifica si ha cambiado la URL del repositorio. Si esto es así, entonces hace clobbering, o sea, vuelve a bajar todos los cambios (checkout) y obviamente no realiza un update, aunque el modo no sea &#8220;clobbering&#8221;. Para saber si dicha URL ha cambiado, ejecuta un &#8220;hg paths default&#8221; en el directorio de trabajo y lo compara con la URL asignada en el archivo de configuración central del Build Master.</p>
<p>El bug está en que al ejecutar &#8220;hg paths default&#8221; Mercurial 1.3.1 devuelve el password oculto con asteriscos:</p>
<pre class="brush: bash; title: ; notranslate">
$ hg paths default 

http://miltondp:***@url.mi_proyecto.com/path/al/repo
</pre>
<p>&#8230; y, obviamente, la URL asignada en el archivo de configuración está completa (sin el password oculto). Al compararse ambas, son distintas, y por lo tanto siempre se hace clobbering.</p>
<p><span id="more-749"></span><br />
Rápidamente me fijé si Mercurial poseía alguna opción para mostrar el password en la salida de &#8220;hg paths&#8221;. No posee dicha opción por lo visto, pero me encontré con <a href="http://www.selenic.com/pipermail/mercurial-devel/2009-February/010529.html">un parche</a> que alguien envió alguna vez, el cual parece que nunca fue incluido. El mismo agrega la opción &#8220;&#8211;show-passwords&#8221; para mostrar la URL completa al ejecutar &#8220;hg paths&#8221;. Lo pueden bajar <a href="http://www.miltonpividori.com.ar/files/mercurial_commands.patch">aquí</a>. El archivo a parchear es <em>&#8220;/usr/lib/pymodules/python2.6/mercurial/commands.py&#8221;</em>.</p>
<p>También hay que parchear Buildbot para que utilice esta nueva opción al comparar los repositorios. Este otro parche lo pueden bajar desde <a href="http://www.miltonpividori.com.ar/files/buildbot_commands.patch">este lugar</a>. Tener en cuenta que el archivo a parchear tiene el mismo nombre que el de Mercurial. El path del mismo es <em>&#8220;/usr/lib/python2.6/dist-packages/buildbot/slave/commands.py&#8221;</em></p>
<p>No se olviden de reiniciar el servicio de Buildbot, y de borrar los archivos .pyc de Python (por lo visto a veces funciona aunque no lo borremos). Ahora el ciclo funciona como uno espera:</p>
<p><a href="http://www.miltonpividori.com.ar/wp-content/uploads/2009/11/buildbot.png"><img src="http://www.miltonpividori.com.ar/wp-content/uploads/2009/11/buildbot-257x300.png" alt="buildbot" title="buildbot" width="257" height="300" class="aligncenter size-medium wp-image-763" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.miltonpividori.com.ar/2009/11/28/si-usas-buildbot-con-mercurial/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Gtk+: trabajando con TreeViews</title>
		<link>http://www.miltonpividori.com.ar/2009/06/22/gtk-trabajando-con-treeviews/</link>
		<comments>http://www.miltonpividori.com.ar/2009/06/22/gtk-trabajando-con-treeviews/#comments</comments>
		<pubDate>Mon, 22 Jun 2009 13:32:47 +0000</pubDate>
		<dc:creator>miltondp</dc:creator>
				<category><![CDATA[Facultad]]></category>
		<category><![CDATA[GNOME]]></category>
		<category><![CDATA[Mono/.NET]]></category>
		<category><![CDATA[Proyectos]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[Software Libre]]></category>

		<guid isPermaLink="false">http://www.miltonpividori.com.ar/?p=668</guid>
		<description><![CDATA[Este post puede ser útil para los que utilicen el widget TreeView de Gtk+ (no importa el lenguaje mientras haya bindings), y necesiten activar por código celdas en modo edición. Hay algunas cuestiones a tener en cuenta. Para el Proyecto Final de Carrera estamos desarrollando un sistema de nivel operativo, con funciones de facturación y [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://www.miltonpividori.com.ar/wp-content/uploads/2009/06/edicion-treeview1.png" alt="edicion-treeview1" title="edicion-treeview1" width="186" height="69" class="alignright size-full wp-image-685" /></p>
<p>Este post puede ser útil para los que utilicen el widget <a href="http://library.gnome.org/devel/gtk/unstable/GtkTreeView.html">TreeView</a> de <a href="http://www.gtk.org/">Gtk+</a> (no importa el lenguaje mientras haya bindings), y necesiten activar por código celdas en modo edición. Hay algunas cuestiones a tener en cuenta.</p>
<p>Para el Proyecto Final de Carrera estamos desarrollando un sistema de nivel operativo, con funciones de facturación y demás. Necesitamos manejar un TreeView con campos editables, y al finalizar la edición de uno de ellos es necesario dar el foco a otro campo en modo edición, listo para que el usuario comience a cargar datos sin tocar el mouse.</p>
<p><span id="more-668"></span><br />
Lo primero que hicimos fue conectar un método al evento <a href="http://library.gnome.org/devel/gtk/unstable/GtkCellRendererText.html#GtkCellRendererText-edited">Edited</a> del <a href="http://library.gnome.org/devel/gtk/unstable/GtkCellRendererText.html">CellRendererText</a> de la columna correspondiente. De esta forma, cuando terminamos la edición en el primer campo, podemos colocar allí el código correspondiente para actualizar el modelo del TreeView y otras operaciones más, entre ellas (la que nos interesa) dar el foco al segundo campo en modo edición. La parte final de este método (el evento se activa al finalizar la edición) había quedado así:</p>
<pre class="brush: csharp; title: ; notranslate">
/* ... */

this.tvItems.SetCursor(path,
					   this.tvcCodigoProducto,
					   true);
</pre>
<p>Hay que tener cuidado al utilizar la función <a href="http://library.gnome.org/devel/gtk/unstable/GtkTreeView.html#gtk-tree-view-set-cursor">SetCursor</a>. En algunos casos, si no la usamos cuidadosamente, el TreeView no se comportará en forma correcta. Esos casos son por ejemplo cuando el usuario, al estar editando el campo, hace click en otra fila. Esto hace que se dispare el evento <em>Edited</em>, y por lo tanto se activa el modo edición en el segundo campo (SetCursor), pero el foco lo tiene otra fila, y esto hace que el funcionamiento sea incorrecto.</p>
<p>La solución es, antes de ejecutar SetCursor, verificar qué tipo de evento ha ocurrido. En nuestro caso, ejecutamos dicha función sólo si se ha oprimido la tecla <em>Return</em>. El código queda así:</p>
<pre class="brush: csharp; title: ; notranslate">
/* Una vez editada la cantidad, automáticamente
 * pasamos el foco al campo de 'código' en modo
 * edición, pero en una nueva fila. A esto lo hacemos
 * únicamente si el usuario presionó la tecla Enter */

Gdk.Event ev = Gtk.Application.CurrentEvent;

if (ev.Type == Gdk.EventType.KeyPress) {
	Gdk.EventKey evKey = (Gdk.EventKey)ev;

	if (evKey.Key == Gdk.Key.Return)
		this.tvItems.SetCursor(path,
							   this.tvcCodigoProducto,
							   true);
}
</pre>
<p>Lo cual es más lógico también: si se hace click en otra fila, seguramente es porque se quiere editar otro ítem y por lo tanto no es correcto forzar el foco en otro lugar. Además, como se dijo, esto hacía que el TreeView quede en un estado inconsistente.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.miltonpividori.com.ar/2009/06/22/gtk-trabajando-con-treeviews/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>sl: Steam Locomotive</title>
		<link>http://www.miltonpividori.com.ar/2009/05/06/sl-steam-locomotive/</link>
		<comments>http://www.miltonpividori.com.ar/2009/05/06/sl-steam-locomotive/#comments</comments>
		<pubDate>Wed, 06 May 2009 15:08:25 +0000</pubDate>
		<dc:creator>miltondp</dc:creator>
				<category><![CDATA[GNU/Linux]]></category>
		<category><![CDATA[Humor]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[Software Libre]]></category>

		<guid isPermaLink="false">http://www.miltonpividori.com.ar/?p=650</guid>
		<description><![CDATA[¿Nunca escribieron LS o sl cuando en realidad quisieron ejecutar &#8220;ls&#8221; en la terminal? Bueno, si les pasó, habrán visto que en Ubuntu salta el siguiente mensaje: La primera vez que lo vi me pareció extraño, así que instalé el paquete &#8220;sl&#8221; como me sugería. La descripción del paquete es esta: Me imagino que tendrán [...]]]></description>
			<content:encoded><![CDATA[<p>¿Nunca escribieron <em>LS</em> o <em>sl</em> cuando en realidad quisieron ejecutar &#8220;ls&#8221; en la terminal? Bueno, si les pasó, habrán visto que en Ubuntu salta el siguiente mensaje:</p>
<pre class="brush: plain; title: ; notranslate">
$ sl
El programa «sl» no está instalado actualmente.  Puede instalarlo escribiendo:
sudo apt-get install sl
bash: sl: orden no encontrada
</pre>
<p>La primera vez que lo vi me pareció extraño, así que instalé el paquete &#8220;sl&#8221; como me sugería. La descripción del paquete es esta:</p>
<pre class="brush: plain; title: ; notranslate">
Correct you if you type `sl' by mistake
Sl is a program that can display animations aimed to correct you
if you type 'sl' by mistake.
SL stands for Steam Locomotive.
</pre>
<p>Me imagino que tendrán ahora la intriga de qué es lo que pasa si ejecutan &#8220;sl&#8221;. Simplemente tengan cuidado <img src='http://www.miltonpividori.com.ar/wp-includes/images/smilies/icon_razz.gif' alt=':P' class='wp-smiley' /> </p>
]]></content:encoded>
			<wfw:commentRss>http://www.miltonpividori.com.ar/2009/05/06/sl-steam-locomotive/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Netboot install con dnsmasq</title>
		<link>http://www.miltonpividori.com.ar/2009/04/09/netboot-install-con-dnsmasq/</link>
		<comments>http://www.miltonpividori.com.ar/2009/04/09/netboot-install-con-dnsmasq/#comments</comments>
		<pubDate>Thu, 09 Apr 2009 21:15:01 +0000</pubDate>
		<dc:creator>miltondp</dc:creator>
				<category><![CDATA[GNU/Linux]]></category>
		<category><![CDATA[Software Libre]]></category>

		<guid isPermaLink="false">http://www.miltonpividori.com.ar/?p=625</guid>
		<description><![CDATA[Tengo en mi casa un Pentium III de 800 MHz con 128 MBs de RAM. Es de César y la vamos a utilizar para el proyecto. No tiene lectora de CD y no tiene soporte para bootear por la red. Les muestro en este post lo que tuve que hacer para instalar Ubuntu Server en [...]]]></description>
			<content:encoded><![CDATA[<p>Tengo en mi casa un Pentium III de 800 MHz con 128 MBs de RAM. Es de César y la vamos a utilizar para el proyecto. No tiene lectora de CD y no tiene soporte para bootear por la red. Les muestro en este post lo que tuve que hacer para instalar Ubuntu Server en una máquina así, de esta forma si lo tienen que hacer se ahorran trabajo.</p>
<p><span id="more-625"></span></p>
<h3>Escenario</h3>
<p>La máquina que tendrá el servidor DHCP, TFTP y compartirá Internet es mi notebook, que se llama <em>wasabi</em> (a mí si me gusta el nombre <img src='http://www.miltonpividori.com.ar/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> ). La PC target se llamará <em>tellus</em>. Desde mi notebook me conecto a un router via wifi (el cual me brinda Internet), y a tellus por cable. La interfaz de la primer conexión es wlan0, y la de la segunda eth0.</p>
<h3>Procedimiento</h3>
<h4>wasabi</h4>
<ul>
<li>En wasabi solamente es necesario tener dnsmasq instalado (hará de servidor DHCP y además tiene un server TFTP incluido).</li>
<li>La IP de wasabi será 192.168.0.1</li>
<li>Editamos las opciones en /etc/dnsmasq.conf. Agregamos las siguientes lineas (asegúrense de que el archivo no tenga otras opciones conflictivas activadas):</li>
</ul>
<pre class="brush: plain; title: ; notranslate">
listen-address=192.168.0.1
interface=eth0

dhcp-no-override
dhcp-range=192.168.0.30,192.168.0.60,12h

dhcp-boot=pxelinux.0
enable-tftp
tftp-root=/var/ftpd
</pre>
<ul>
<li>Coloquen el CD de Ubuntu Server (o la versión que sea) en su lectora, y copien los archivos necesarios para realizar la instalación por red:</li>
</ul>
<pre class="brush: plain; title: ; notranslate">
$ sudo cp -ar /media/cdrom0/install/netboot/* /var/ftpd
</pre>
<ul>
<li>Otra opción al paso anterior es bajarse los archivos desde Internet, para <a href="http://archive.ubuntu.com/ubuntu/dists/intrepid/main/installer-i386/current/images/netboot/netboot.tar.gz">Intrepid</a> o para <a href="http://archive.ubuntu.com/ubuntu/dists/jaunty/main/installer-i386/current/images/netboot/netboot.tar.gz">Jaunty</a> por ejemplo.</li>
<li># sudo service dnsmasq restart</li>
<li>En este punto ya tenemos andando dnsmasq, como servidor DHCP, DNS y TFTP.</li>
<li>Activamos IP Masquerading:</li>
</ul>
<pre class="brush: plain; title: ; notranslate">
# echo 1 &gt; /proc/sys/net/ipv4/ip_forward
# iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE
</pre>
<ul>
<li>Como les dije, tellus no puede bootear directamente por red, así que es necesario crear un disquette con <a href="http://www.rom-o-matic.net/">gPXE</a>. Tienen que elegir el modelo de placa de red exacto de la máquina target (tellus en este caso) para poder bootear, traer la imagen desde wasabi y arrancar la instalación. Una vez bajada, pueden grabarla en un disquette (que difícil conseguir un disquette estos días&#8230; y uno que ande) con <a href="http://www.manpagez.com/man/1/dd/">dd</a> en GNU/Linux o <a href="http://www.chrysocome.net/rawwrite">rawwritewin</a> en Windows.</li>
</ul>
<h4>tellus</h4>
<ul>
<li>Colocan el disquette creado y bootean desde él. Deberían ver cómo la máquina target pide una IP y arranca el instalador de Ubuntu.</li>
</ul>
<p>Listo, eso es todo. Un inconveniente que tuve fue resuelto con la opción &#8220;dhcp-no-override&#8221; que les mostré arriba, para dnsmasq. Resulta que en modo normal no arma paquetes DHCP del todo estándar, y que hacían tellus no pueda encontrar la opción de &#8220;archivo de booteo&#8221; al recibir el paquete DHCP.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.miltonpividori.com.ar/2009/04/09/netboot-install-con-dnsmasq/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Mono C# Shell</title>
		<link>http://www.miltonpividori.com.ar/2009/02/12/mono-c-shell/</link>
		<comments>http://www.miltonpividori.com.ar/2009/02/12/mono-c-shell/#comments</comments>
		<pubDate>Thu, 12 Feb 2009 18:41:42 +0000</pubDate>
		<dc:creator>miltondp</dc:creator>
				<category><![CDATA[Mono/.NET]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[Software Libre]]></category>

		<guid isPermaLink="false">http://www.miltonpividori.com.ar/?p=593</guid>
		<description><![CDATA[El otro día necesitaba saber bien cómo funcionaba el método Remove de la clase StringBuilder en Mono. Entonces hice lo siguiente: Si, funcionaba como me lo imaginaba Esta shell para C# está disponible en la versión 2.2 de Mono.]]></description>
			<content:encoded><![CDATA[<p>El otro día necesitaba saber bien cómo funcionaba el método <em>Remove</em> de la clase <em>StringBuilder</em> en Mono. Entonces hice lo siguiente:</p>
<pre class="brush: plain; title: ; notranslate">
miltondp@wasabi:~$ csharp
Mono C# Shell, type &quot;help;&quot; for help

Enter statements below.
csharp&gt; using System.Text;
csharp&gt; var a = new StringBuilder(&quot;Milton&quot;);
csharp&gt; a.Remove(a.Length-1,1);
Milto
csharp&gt; a.Remove(a.Length-1,1);
Milt
csharp&gt; a.Remove(a.Length-1,1);
Mil
csharp&gt; a.Remove(a.Length-1,1);
Mi
csharp&gt; a.Remove(a.Length-1,1);
M
csharp&gt; a.Remove(a.Length-1,1); 

csharp&gt;
</pre>
<p>Si, funcionaba como me lo imaginaba <img src='http://www.miltonpividori.com.ar/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />  Esta shell para C# está disponible en la versión 2.2 de Mono.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.miltonpividori.com.ar/2009/02/12/mono-c-shell/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Mercurial Queues: una aplicación simple</title>
		<link>http://www.miltonpividori.com.ar/2009/01/15/mercurial-queues-una-aplicacion-simple/</link>
		<comments>http://www.miltonpividori.com.ar/2009/01/15/mercurial-queues-una-aplicacion-simple/#comments</comments>
		<pubDate>Thu, 15 Jan 2009 15:47:01 +0000</pubDate>
		<dc:creator>miltondp</dc:creator>
				<category><![CDATA[Mercurial]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[Software Libre]]></category>

		<guid isPermaLink="false">http://www.miltonpividori.com.ar/?p=385</guid>
		<description><![CDATA[Hace un tiempo comencé a leer un poco sobre esta extensión de Mercurial: Mercurial Queues (Mq). ¿Para qué sirve? Bueno, imagínense que tienen una instalación de WordPress con ciertas modificaciones que le han hecho. Supongan que la versión del CMS que están usando es la 2.6.5. Quieren actualizar a la 2.7 pero sin perder los [...]]]></description>
			<content:encoded><![CDATA[<p>Hace un tiempo comencé a leer un poco sobre esta extensión de Mercurial: <a href="http://www.selenic.com/mercurial/wiki/index.cgi/MqExtension">Mercurial Queues</a> (Mq). ¿Para qué sirve? Bueno, imagínense que tienen una instalación de WordPress con ciertas modificaciones que le han hecho. Supongan que la versión del CMS que están usando es la 2.6.5. Quieren actualizar a la 2.7 pero sin perder los cambios, y que la migración sea sencilla. Bueno, Mq les permite mantener estos cambios personales como parches, que pueden aplicar, modificar, quitar, reordenar&#8230; De esta forma, con una serie de pasos, pueden actualizar fácilmente el código &#8220;de fondo&#8221; (o sea, de WordPress 2.6.5 a la 2.7) y reaplicar esos parches sobre esta nueva base. En <a href="http://atlee.ca/blog/2008/10/25/upgrading-wordpress-with-mercurial/">este post</a> pueden ver el ejemplo desarrollado.</p>
<p><span id="more-385"></span><br />
De todas formas, parece que ese no es el uso principal. Piensen que están colaborando con algún proyecto, donde utilizan Mercurial (o cualquier DCVS). Están trabajando en una nueva funcionalidad. Sabemos que los commits son inmutables. No sería conveniente que hagan commits en su repositorio local y luego intenten enviar todos esos cambios (que tendrán varios merges de por medio quizá) para la revisión, porque la dificulta. Lo conveniente sería (según explican al introducirnos en Mq) enviar parches con el trabajo finalizado. De esta forma podríamos ir manteniendo distintos parches de acuerdo a nuestro trabajo e ir actualizándolos a medida que avancemos. Como Mercurial Queues integra los parches a todo el sistema, los mismos son tratados como changesets, por lo tanto podemos pensar en los mismos como commits mutables: terminamos con nuestro trabajo y actualizamos el commit. Al día siguiente nos damos cuenta de que había un error y lo volvemos a actualizar con el nuevo trabajo. Incluso, como dije, podemos descartarlos, reordenarlos, etc. Y a medida que el trabajo se hace más complejo, podemos tener distintos parches, cada uno con cambios relacionados.</p>
<p>Yo tengo hecho mi CV en LaTeX bajo Mercurial. Después de un tiempo manteniéndolo y actualizándolo, me encontré colocando notas en forma de TODO o FIXME para recordar actualizar ciertas cosas, o trabajos sobre el mismo a medio hacer. El problema es que cuando tengo que enviar una versión limpia de mi CV, debo compilar una versión &#8220;estable&#8221; quitando todas estas modificaciones no terminadas. Por lo tanto es ideal usar Mq en esta situación: mantengo estos &#8220;features&#8221; o recordatorios como parches o commits mutables. Cuando quiero agregar algún recordatorio o estoy trabajando en una ampliación o reestruración, trabajo sobre los parches y los voy actualizando. Cuando veo que ya están listos, los transformo en changeset normales (o sea, inmutables). Cuando quiero compilar una versión estable, quito todos los parches, quedando así una versión limpia lista para enviar.</p>
<p>Algunos comandos como para que se den una idea de cómo funciona:</p>
<pre class="brush: plain; title: ; notranslate">
$ hg qinit
$ hg qnew nueva.funcionalidad
# Ahora estoy trabajando en el parche &quot;nueva.funcionalidad&quot;. Realizo cambios, agrego archivos con &quot;hd add&quot;, etc... trabajo comúnmente como siempre.
$ hg qrefresh
# con este comando actualizo el parche en el que estoy trabajando. Todos los cambios realizados son guardados ahora en &quot;nueva.funcionalidad&quot;.
</pre>
<p>Si quiero quitar todos los parches aplicados ejecuto:</p>
<pre class="brush: plain; title: ; notranslate">
$ hg qpop -a
</pre>
<p>Si quiero aplicar todos los parches:</p>
<pre class="brush: plain; title: ; notranslate">
$ hg qpush -a
</pre>
<p>Si quiero moverme a un parche específico:</p>
<pre class="brush: plain; title: ; notranslate">
$ hg qgoto nombre.parche
</pre>
<p>Uno de los inconvenientes que surgió después de haberme encontrado con esta fantástica herramienta, fue que quería subir estos parches al repositorio. De esta forma, podría acceder a ellos y modificarlos desde cualquier máquina. Sin embargo esto no es trivial. La solución pasa por armar otro repositorio en el servidor. Por lo tanto el directorio local <em>.hg/patches</em> que posee todos los parches, se transforma en otro repositorio Mercurial y lo tratamos como tal: de vez en cuando subimos al repo de parches nuestros cambios a los mismos. Esto puede parecer tedioso, pero Mercurial ofrece unas utilidades que facilitan las cosas.</p>
<p>Me parece que es una extensión muy útil. En lo personal no me fue fácil agarrarle la mano rápidamente. Aunque como todo, para dominarlo hay que ponerlo en práctica.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.miltonpividori.com.ar/2009/01/15/mercurial-queues-una-aplicacion-simple/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Mercurial con Meld</title>
		<link>http://www.miltonpividori.com.ar/2008/12/08/mercurial-con-meld/</link>
		<comments>http://www.miltonpividori.com.ar/2008/12/08/mercurial-con-meld/#comments</comments>
		<pubDate>Mon, 08 Dec 2008 23:54:39 +0000</pubDate>
		<dc:creator>miltondp</dc:creator>
				<category><![CDATA[Mercurial]]></category>
		<category><![CDATA[Software Libre]]></category>

		<guid isPermaLink="false">http://www.miltonpividori.com.ar/?p=462</guid>
		<description><![CDATA[Uso meld para realizar comparaciones entre archivos o directorios. Mercurial, al detectar un conflicto, busca en el sistema algún programa gráfico para resolverlo, mostrando las diferencias y así poder realizar el merge. La idea es que en los tres paneles de meld, a la izquierda tengamos la versión de base, en la que se basan [...]]]></description>
			<content:encoded><![CDATA[<p>Uso <a href="http://meld.sourceforge.net/">meld</a> para realizar comparaciones entre archivos o directorios. <a href="http://www.selenic.com/mercurial/">Mercurial</a>, al detectar un conflicto, busca en el sistema algún programa gráfico para resolverlo, mostrando las diferencias y así poder realizar el merge.</p>
<p>La idea es que en los tres paneles de meld, a la izquierda tengamos la versión de base, en la que se basan (valga la redundancia) nuestras modificaciones y las de la otra rama, en el medio nuestro archivo, y a la derecha el del otro. Trabajamos con la vista del medio, realizamos la fusión y guardamos los cambios al finalizar. Así resolvemos el conflicto.</p>
<p>Sin embargo no es ese el orden en el que aparecen los archivos en meld cuando trabajamos con Mercurial. Buscando en la ayuda de configuración, he encontrado estas opciones que podemos utilizar en nuestro ~/.hgrc, y así resolver este inconveniente:</p>
<pre class="brush: plain; title: ; notranslate">
[merge-tools]
meld.executable = meld
meld.args = $base $local $other
meld.priority = 1
</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.miltonpividori.com.ar/2008/12/08/mercurial-con-meld/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Mercurial vs Subversion: branching and merging</title>
		<link>http://www.miltonpividori.com.ar/2008/10/19/mercurial-vs-subversion-branching-and-merging/</link>
		<comments>http://www.miltonpividori.com.ar/2008/10/19/mercurial-vs-subversion-branching-and-merging/#comments</comments>
		<pubDate>Sun, 19 Oct 2008 19:20:01 +0000</pubDate>
		<dc:creator>miltondp</dc:creator>
				<category><![CDATA[Facultad]]></category>
		<category><![CDATA[Mercurial]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[Software Libre]]></category>

		<guid isPermaLink="false">http://www.miltonpividori.com.ar/?p=363</guid>
		<description><![CDATA[Hace un tiempo vengo estudiando este sistema de control de versiones distribuído, hecho principalmente en Python, y que lo utilizan algunos grandes proyectos como Mozilla, OpenJDK, NetBeans, etc. En este post, quería compararlo con Subversion, con respecto a mantener distintas ramas de desarrollo. Aclaro que si bien no tengo una experiencia real con ambos sistemas [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.miltonpividori.com.ar/wp-content/uploads/2008/10/branching.png"><img src="http://www.miltonpividori.com.ar/wp-content/uploads/2008/10/branching.png" alt="" title="branching" width="321" height="135" class="aligncenter size-full wp-image-359" /></a></p>
<p>Hace un tiempo vengo estudiando este sistema de control de versiones distribuído, hecho principalmente en Python, y que lo utilizan algunos grandes proyectos como Mozilla, OpenJDK, NetBeans, etc. En este post, quería compararlo con Subversion, con respecto a mantener distintas ramas de desarrollo.</p>
<p>Aclaro que si bien no tengo una experiencia real con ambos sistemas en el tema, como la podría tener algún desarrollador de un proyecto grande de Software Libre, leyendo los manuales, viendo los ejemplos y utilizándolo en pequeños proyectos con pocos desarrolladores uno puede darse una idea de cómo son las cosas a gran escala.</p>
<p>En muchos lugares se habla de las bondades de los sistemas de control de versiones distribuidos. Una de las ventajas que tienen (entre muchas otras), según se dice, es la facilidad para manejar branches, donde se mencionan las deficiencias de Subversion para manejar varias ramas de desarrollo.</p>
<p>Este post puede servir para alguien que esta pensando en alguna herramienta de versionado, donde se esperan utilizar varios branches en el proyecto, como para poder ver de forma sencilla cómo manejan esto ambos sistemas. Esa &#8220;forma sencilla&#8221; de verlo podría significar también una divergencia importante con la práctica. Así que si piensan que algo es incorrecto, los invito a dejar un comentario.</p>
<p><span id="more-363"></span><br />
En fin, ¿cómo se realizan estas operaciones en ambos sistemas?</p>
<p>Aquí con <em>branch</em> me refiero a una línea de desarrollo para agregar un nuevo feature o corregir un bug, etc. La aclaración es importante, porque en Mercurial casi siempre trabajamos con branches. Si hago un pull y traigo changesets (es decir commits) puede que surjan nuevas ramas. Este no es el significado al que me refiero aquí.</p>
<h3>Subversion</h3>
<p>En Subversion, no existe el concepto de branch. Un branch es simplemente una copia de un directorio a otro lugar. Uno mismo es el que le da ese significado.</p>
<p>Puedo hacer la copia directamente en el repositorio (donde automáticamente hago un commit):</p>
<pre class="brush: plain; title: ; notranslate">
$ svn copy http://svn.example.com/repos/calc/trunk \
           http://svn.example.com/repos/calc/branches/my-calc-branch \
      -m &quot;Creating a private branch of /calc/trunk.&quot;

Committed revision 341.
</pre>
<p>Si tengo la copia de trabajo correspondiente a todo el repositorio en mi máquina (incluyendo las típicas carpetas <em>trunk</em>, <em>branches</em> y <em>tags</em>):</p>
<pre class="brush: plain; title: ; notranslate">
$ svn copy trunk branches/my-calc-branch
$ svn status
A  +   branches/my-calc-branch
$
$ svn commit -m &quot;Creating a private branch of /calc/trunk.&quot;
Adding         branches/my-calc-branch
Committed revision 341.
</pre>
<p>Luego simplemente comienzo a trabajar en esa carpeta, en mi nuevo branch. Sin embargo, hay una serie de inconvenientes. No podemos dejar que la línea principal de desarrollo se aleje demasiado de nuestro branch (supongamos que estamos agregando un nuevo feature), ya que al intentar fusionar todo luego de un largo tiempo, podemos terminar por frustrarnos. Esto, obviamente, no se aplica sólo a Subversion.</p>
<p>Entonces, periódicamente, traemos los cambios hechos en trunk a nuestra copia de trabajo, que corresponde a la rama en la que estamos trabajando:</p>
<pre class="brush: plain; title: ; notranslate">
$ svn merge -r 100:200 http://svn.example.com/repos/trunk my-working-copy
...
$ svn commit -m &quot;Sync with trunk&quot;
</pre>
<p>El comando &#8220;svn merge&#8221; es muy similar a &#8220;svn diff&#8221;, pero aplica los cambios a mi copia de trabajo.</p>
<p>Como dice en el libro de Subversion, fusionar los cambios suena fácil, pero puede resultar en un dolor de cabeza. Yo, el usuario, soy el que tengo que controlar los merges que hago. Subversion (las versiones menores a las 1.5) no lo hacen. Esto significa que puedo fusionar los cambios dos veces, por ejemplo, o puedo comparar dos cosas sin ningún tipo de relación e intentar aplicar los cambios resultantes a mi copia de trabajo. Cuando ejecuto un comando parecido al de arriba, Subversion no sabe qué significan esos cambios. Simplemente se aplican a la copia de trabajo.</p>
<p>Supongamos que el día al fin ha llegado, y quiero traer a trunk mi nuevo feature, desarrollado en mi branch. Como dice en el libro, si están pensando en comparar la última revisión de trunk con la última del branch, se equivocan: ¿qué pasaría con los cambios hechos en trunk que nunca sucedieron en el branch? Se borrarían, y eso no es lo que queremos.</p>
<p>Lo que hay que hacer es comparar el estado inicial del branch con su estado final. De esta forma <em>adiciono</em> a trunk los cambios que he realizado en mi branch. Para esto tengo que saber en qué revisión creé mi branch. Una forma fácil de averiguar esto con Subversion:</p>
<pre class="brush: plain; title: ; notranslate">
$ svn log --verbose --stop-on-copy \

http://svn.example.com/repos/calc/branches/my-calc-branch

…
------------------------------------------------------------------------
r341 | user | 2002-11-03 15:27:56 -0600 (Thu, 07 Nov 2002) | 2 lines
Changed paths:
   A /calc/branches/my-calc-branch (from /calc/trunk:340)

$
</pre>
<p>El procedimiento completo, sacado del libro, es este:</p>
<pre class="brush: plain; title: ; notranslate">
$ cd calc/trunk
$ svn update
At revision 405.

$ svn merge -r 341:405 http://svn.example.com/repos/calc/branches/my-calc-branch
U   integer.c
U   button.c
U   Makefile

$ svn status
M   integer.c
M   button.c
M   Makefile

# ...examine the diffs, compile, test, etc...

$ svn commit -m &quot;Merged my-calc-branch changes r341:405 into the trunk.&quot;
Sending        integer.c
Sending        button.c
Sending        Makefile
Transmitting file data ...
Committed revision 406.
</pre>
<p>Noten lo descriptivos que son los mensajes de commit. Esto es necesario hacerlo así, para saber luego, por ejemplo, que ya se han aplicado los cambios del branch, y qué cambios involucran. Es información crítica: supongan que siguen trabajando en el branch y quieren traer los nuevos cambios hechos. Necesitan saber qué cambios ya han sido aplicados para no volver a traerlos a trunk. Para hacer eso, básicamente se ejecutan los comandos anteriores, alterando el rango de las revisiones según corresponda.</p>
<p>Cuando estaba aprendiendo Subversion, leí muy por arriba estas cosas, ya que no eran las que iba a utilizar habitualmente, y además me pareció un poco complicado, con varias advertencias de posibles problemas al fusionar cambios, etc. Por eso estoy copiando los ejemplos del libro, y ahora, en realidad, estoy viendo bien cómo funciona.</p>
<p>Sin embargo una de las cosas que sí aprendí a hacer ya que me parecieron muy útiles, que tiene relación con esto, es <strong>&#8220;deshacer&#8221; un commit</strong>, o sea, anular sus cambios. La forma de hacerlo es la siguiente:</p>
<pre class="brush: plain; title: ; notranslate">
$ svn merge -r 303:302 http://svn.example.com/repos/calc/trunk
U  integer.c

$ svn status
M  integer.c

$ svn diff
…
# verify that the change is removed
…

$ svn commit -m &quot;Undoing change committed in r303.&quot;
Sending        integer.c
Transmitting file data .
Committed revision 350.
</pre>
<p>Simplemente se invierten, como habrán notado, el orden de las revisiones a comparar, resultando en un patch contrario al que aplica, en este caso, el commit 303, provocando la anulación de sus modificaciones. Sin embargo, si quiero traer un objeto eliminado de vuelta, no utilizo el comando anterior, sino &#8220;svn copy&#8221;.</p>
<p>Todas estas cosas que pueden parecer complicadas (y de hecho lo son), parecen estar comenzando a cambiar a partir de Subversion 1.5. Por ejemplo, algunos de los nuevos features, que podemos leer en las <a href="http://subversion.tigris.org/svn_1.5_releasenotes.html">release notes</a>, son <a href="http://subversion.tigris.org/svn_1.5_releasenotes.html#merge-tracking">merge tracking</a> y la <a href="http://subversion.tigris.org/svn_1.5_releasenotes.html#interactive-conflict-resolution">resolución interactiva de conflictos</a>. Sin embargo el primero, como se menciona, posee una funcionalidad básica.</p>
<h3>Mercurial</h3>
<p>Veamos ahora cómo es esto en Mercurial. Aquí un desarrollador no posee únicamente la copia de trabajo, como en Subversion, sino el repositorio completo. Una forma fácil de crear un branch en Mercurial es clonar mi repositorio actual. Supongamos que tengo uno en la carpeta <em>myproject</em>:</p>
<pre class="brush: plain; title: ; notranslate">
$ hg clone myproject myproject-1.0
updating working directory
2 files updated, 0 files merged, 0 files removed, 0 files unresolved
</pre>
<p>De esta forma puedo comenzar a trabajar en el nuevo branch creado:</p>
<pre class="brush: plain; title: ; notranslate">
$ cd myproject-1.0
$ echo 'Alguna nueva funcionalidad' &gt; algunArchivo
$ hg commit -A -m 'Nuevo feature!'
adding algunArchivo
</pre>
<p>Con esta manera de crear ramas, hay una relación uno-a-uno entre los branches en los que estoy trabajando y las carpetas existentes. Si necesito centralizar el branch porque voy a trabajar con otros desarrolladores y queremos tener un lugar común donde subir los cambios, entonces puedo subir el repositorio por FTP por ejemplo, a la carpeta donde el script <em>hgwebdir.cgi</em> verifique la existencia de repositorios. Con copiar la carpeta allí y realizar unas simples configuraciones en el archivo <em>.hg/hgrc</em> ya estamos. Otra es, si tenemos acceso shell a la máquina servidor, clonar el repositorio allí mismo.</p>
<p><a href="http://hg.mozilla.org/">Estos</a> son los repositorios Mercurial de Mozilla. Allí se pueden ver varios. En uno de ellos se hace un merge con otro, como se puede ver en <a href="http://hg.mozilla.org/actionmonkey-tamarin/rev/604b9b57495b">este changeset</a>.</p>
<p>Si corrijo algún error en mi branch <em>myproject-1.0</em>, seguramente también queramos llevar esas correcciones en la linea principal de desarrollo (digamos, trunk), en el cual, supongamos, también se han hecho modificaciones. Para hacer esto, nos ubicamos en el directorio en donde queremos llevar los cambios (la linea principal en este caso), y ejecutamos un pull indicando la carpeta del branch:</p>
<pre class="brush: plain; title: ; notranslate">
$ cd myproject/
$ hg pull ../myproject-1.0
pulling from ../myproject-1.0
searching for changes
adding changesets
adding manifests
adding file changes
added 2 changesets with 2 changes to 2 files (+1 heads)
(run 'hg heads' to see heads, 'hg merge' to merge)
$ hg merge
2 files updated, 0 files merged, 0 files removed, 0 files unresolved
(branch merge, don't forget to commit)
</pre>
<p>Con el comando pull lo que hago es traer los cambios (changesets) hechos en el repositorio que le paso como argumento. Cada changeset sabe cuál es su padre, por lo tanto al pullear, se armarán las ramas que sean necesarias. Después del pull, yo sigo en la revisión en la que estaba. Nada ha cambiado en mi copia de trabajo, simplemente se sincronizó mi repositorio actual con el otro.</p>
<p>Notar que Mercurial me avisa que han quedado varios heads: un head es un changeset sin hijos. Con el comando &#8220;hg view&#8221; puedo ver el panorama actual del repositorio:</p>
<p><a href="http://www.miltonpividori.com.ar/wp-content/uploads/2008/10/despues_de_un_pull.png"><img src="http://www.miltonpividori.com.ar/wp-content/uploads/2008/10/despues_de_un_pull.png" alt="" title="despues_de_un_pull" width="443" height="491" class="aligncenter size-full wp-image-377" /></a></p>
<p>El comando &#8220;hg merge&#8221; fusiona, justamente, mi copia de trabajo con otra revisión. Lo que hace es aplicar a mi copia de trabajo todos los cambios de la otra rama, según corresponda.</p>
<p>Notar que si bien estamos diciendo que utilizamos un branch, <em>myproject-1.0</em>, estos dos heads aquí, también representan branches, pero del tipo &#8220;little picture&#8221; (como se explica en el <a href="http://hgbook.red-bean.com/">libro de Mercurial</a>). Este tipo de branches aparecen todos los días, cuando pulleamos cambios de otro repositorio: son parte del trabajo cotidiano. En cambio el branch <em>myproject-1.0</em> es del tipo &#8220;big picture&#8221;, ya que vamos a trabajar en la versión 1.0, corrigiendo errores, etc.</p>
<p>Si &#8220;hg merge&#8221; detecta conflictos, se ejecuta algún visor de diferencias, como <a href="http://meld.sourceforge.net/">meld</a> o <a href="http://kdiff3.sourceforge.net/">kdiff3</a>. Mercurial se encarga de buscarlo en el sistema, o podemos setearlo nosotros en el archivo ~/.hgrc. Estos visores tienen tres vistas: a la izquierda están mis cambios, en el medio el resultado, y a la derecha los cambios del otro. Entonces es muy sencillo resolver el conflicto.</p>
<p>Como Mercurial nos recuerda después de ejecutar el merge, no debemos olvidarnos de hacer un commit aquí. Este commit tendrá como padres a los dos heads actuales:</p>
<p><a href="http://www.miltonpividori.com.ar/wp-content/uploads/2008/10/merge-hecho.png"><img src="http://www.miltonpividori.com.ar/wp-content/uploads/2008/10/merge-hecho.png" alt="" title="merge-hecho" width="443" height="491" class="aligncenter size-full wp-image-381" /></a></p>
<p>Si bien estamos hablando de un sistema distribuído, obviamente que si queremos compartir cambios, la manera sencilla de trabajar es poner un repositorio central visto por todos los desarrolladores, al cual todos puedan subir sus cambios, y del cual puedan bajar los cambios de otros. Si realizo commits en mi repositorio local y quiero subirlos al remoto (con un push), Mercurial me avisará si es que estoy por generar heads remotamente. Si esto sucede, tengo que hacer un pull, mergear los dos heads, y luego subir el resultado.</p>
<p>Esta situación es similar en Subversion a cuando intentamos hacer un commit y nos dice que tenemos que ejecutar un update antes: entonces aplica los cambios en nuestra copia de trabajo, resolvemos los conflictos si es necesario, y realizamos un commit.</p>
<p><strong>Otra forma de crear un branch</strong> en Mercurial, es hacerlo en el mismo repositorio, con <em>Named Branches</em>. Así nos ahorramos el tener que subir algo al servidor. Por defecto, si no hemos cambiado nada, trabajamos en el branch llamado <em>default</em>.</p>
<p>Con el comando &#8220;branches&#8221; puedo ver cuáles hay en mi repositorio:</p>
<pre class="brush: plain; title: ; notranslate">
$ hg branches
default                        0:34b3e0e21c40
</pre>
<p>Y con el comando &#8220;branch&#8221; veo en qué branch estoy trabajando ahora:</p>
<pre class="brush: plain; title: ; notranslate">
$ hg branch
default
</pre>
<p>Supongamos que hemos hecho unos commits en la rama <em>default</em>:</p>
<pre class="brush: plain; title: ; notranslate">
$ hg log
changeset:   1:7c0e8fbd1fb1
user:        Milton Pividori &lt;mimail@proveedor.com&gt;
date:        Sun Oct 12 13:52:18 2008 -0300
summary:     Agrego chau.cs

changeset:   0:936587b0804a
user:        Milton Pividori &lt;mimail@proveedor.com&gt;
date:        Sun Oct 12 13:52:05 2008 -0300
summary:     Agrego hola.cs
</pre>
<p>Entonces, para comenzar a trabajar en un nuevo branch, digamos myproject-1.0, hacemos:</p>
<pre class="brush: plain; title: ; notranslate">
$ hg branch myproject-1.0
marked working directory as branch myproject-1.0
$ hg branch
myproject-1.0
</pre>
<p>Como nos indica el comando &#8220;branch&#8221;, ahora estamos trabajando en nuestro nuevo branch recién creado. El comando por sí sólo no hace nada. Simplemente le indica a Mercurial que <em>a partir de ahora</em>, vamos a comenzar a trabajar en este branch. Si hacemos un commit, Mercurial marcará el changeset como perteneciente a ese branch, y así con todos los changesets siguientes. Supongamos que hemos realizado tres commits más en este nuevo branch:</p>
<pre class="brush: plain; title: ; notranslate">
$ hg log -b myproject-1.0
changeset:   4:188979745f99
branch:      myproject-1.0
tag:         tip
user:        Milton Pividori &lt;mimail@proveedor.com&gt;
date:        Sun Oct 12 14:05:53 2008 -0300
summary:     Agrego comoestas.cs

changeset:   3:06231db27675
branch:      myproject-1.0
user:        Milton Pividori &lt;mimail@proveedor.com&gt;
date:        Sun Oct 12 14:01:02 2008 -0300
summary:     Nuevo feature en chau.cs

changeset:   2:d2d1fd5f5b39
branch:      myproject-1.0
user:        Milton Pividori &lt;mimail@proveedor.com&gt;
date:        Sun Oct 12 14:00:48 2008 -0300
summary:     Nuevo feature en hola.cs
</pre>
<p>Y supongamos que el trabajo en la rama <em>default</em> continúa con más commits. Entonces el panorama en el repositorio luce como esto:</p>
<p><a href="http://www.miltonpividori.com.ar/wp-content/uploads/2008/10/branches.png"><img src="http://www.miltonpividori.com.ar/wp-content/uploads/2008/10/branches.png" alt="" title="branches" width="193" height="110" class="aligncenter size-full wp-image-374" /></a></p>
<p>La rama de la izquierda corresponde a <em>default</em>, y la de la derecha a <em>myproject-1.0</em>. Para moverme entre los distintos branches de un repositorio, utilizo el comando &#8220;hg update nombreBranch&#8221;. Si después de eso realizo más commits, obviamente, pertenecerán al branch al que me he movido.</p>
<p>Supongamos que mi trabajo en el branch <em>myproject-1.0</em> ha terminado, y quiero fusionar los cambios con default. Para eso me muevo al branch <em>default</em> (notar que llamo branch a la linea principal de trabajo), o sea a donde quiero llevar mis cambios, y utilizo, nuevamente, el comando &#8220;hg merge&#8221; seguido del nombre del branch del que quiero traer los cambios:</p>
<pre class="brush: plain; title: ; notranslate">
$ hg update default
2 files updated, 0 files merged, 1 files removed, 0 files unresolved
$ hg merge myproject-1.0
merging chau.cs
merging hola.cs
1 files updated, 2 files merged, 0 files removed, 0 files unresolved
(branch merge, don't forget to commit)
$ hg ci -m &quot;Merge de myproject-1.0 a default&quot;
</pre>
<p>El nuevo commit pertenecerá al branch <em>default</em>. Este comportamiento es el esperado: fusiono los cambios de un branch dentro del que yo estoy trabajando.</p>
<p><a href="http://www.miltonpividori.com.ar/wp-content/uploads/2008/10/meld.png">Aquí pueden ver una captura de meld</a> cuando se presenta un conflicto, y a continuación el estado final del repositorio, luego de fusionar los cambios de mi branch a <em>default</em>:</p>
<p><a href="http://www.miltonpividori.com.ar/wp-content/uploads/2008/10/merge-final-named-branches.png"><img src="http://www.miltonpividori.com.ar/wp-content/uploads/2008/10/merge-final-named-branches.png" alt="" title="merge-final-named-branches" width="259" height="118" class="aligncenter size-full wp-image-408" /></a></p>
<p>Veamos cómo <strong>deshacer un commit con Mercurial</strong>. Supongamos que éste es el estado actual del repositorio:</p>
<p><a href="http://www.miltonpividori.com.ar/wp-content/uploads/2008/10/eliminar-changeset1.png"><img src="http://www.miltonpividori.com.ar/wp-content/uploads/2008/10/eliminar-changeset1.png" alt="" title="eliminar-changeset1" width="155" height="113" class="aligncenter size-full wp-image-395" /></a></p>
<p>Si me doy cuenta que el changeset 4 (&#8220;Bug fix en bar&#8221;) es incorrecto, entonces lo deshago con el comando &#8220;hg backout&#8221;:</p>
<pre class="brush: plain; title: ; notranslate">
$ hg backout --merge 4
reverting bar
created new head
changeset 7:cad3793b208a backs out changeset 4:e56015c82591
merging with changeset 7:cad3793b208a
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
(branch merge, don't forget to commit)
</pre>
<p>El comando &#8220;backout&#8221; crea una nueva revisión teniendo como padre aquella que quiere deshacer. Por lo tanto se generará un nuevo head. La opción &#8220;&#8211;merge&#8221; automáticamente fusiona los cambios con la otra cabeza.</p>
<p>Después del commit, el repositorio queda así:</p>
<p><a href="http://www.miltonpividori.com.ar/wp-content/uploads/2008/10/eliminar-changeset2.png"><img src="http://www.miltonpividori.com.ar/wp-content/uploads/2008/10/eliminar-changeset2.png" alt="" title="eliminar-changeset2" width="251" height="135" class="aligncenter size-full wp-image-397" /></a></p>
<p>Al mensaje de log lo genera Mercurial.</p>
<h3>Conclusiones finales</h3>
<p>Creo que realizar branches y luego fusionarlos es bastante más fácil en Mercurial. No tengo que ver en qué revisión comencé el branch y calcular el rango como en Subversion. No tengo que preocuparme de no aplicar dos veces los mismos cambios. Si sigo trabajando en mi branch, traer los nuevos cambios es muy sencillo, ya que Mercurial es el que controla esto. No hace falta ser descriptivo con los logs de los commits, porque con &#8220;hg view&#8221; puedo ver gráficamente el panorama y saber qué commits fueron realizados para fusionar cambios.</p>
<p>Revertir el cambio de una revisión parece sencillo en ambos, salvo que en Mercurial el comando para resucitar un objeto eliminado, que en definitiva es anular un commit, sigue siendo &#8220;backout&#8221;. En Subversion había que utilizar &#8220;copy&#8221;. De todas formas habría que probarlos en situaciones reales en contextos más complicados.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.miltonpividori.com.ar/2008/10/19/mercurial-vs-subversion-branching-and-merging/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

