Oct 19 2014

MAGIT: Git from Emacs

Tag: advancedoverdrive @ 3:22 pm

Hace un tiempo un amigo me pregunto como emplear diff desde emacs. Lo cierto es que en un principio pense en responderle con la utilidad M-x ediff que emacs maneja bastante bien. Sin embargo, y debido a que practicamente en todos nuestros proyectos usamos git, pense que la mejor respuesta no seria esa, sino emplear el sistema de diffs que magit provee. Asi que la solucion no era tan solo el saber como ver los diffs entre el HEAD y un fichero que se estuviese modificando, sino que pasaba por todo un conjunto de features que magit proveia y que hacia mucho mas facil no solo ver diffs, sino emplear muchas de las funcionalidades que git tenia, accesibles de manera ideal desde emacs.

Para instalar Magit, lo podemos hacer de distintas formas, ya sea mediante ELPA, Mermelade o empleando directamente el codigo del repositorio. En este caso explicare la forma recomendada por Magit, que es mediante ELPA (Emacs Lisp Package Archive), que desde Emacs 24 viene incluido por defecto en un intento de realizar instalaciones sencillas asi como actualizaciones para paquetes de Emacs Lisp.

Tan solo debemos anadir el nuevo repositorio anadiendo estas lineas a nuestro .emacs:

; Magit installation using ELPA
(require ‘package)
(add-to-list ‘package-archives
             ‘(«melpa» . «http://melpa.milkbox.net/packages/») t)

Una vez hecho esto, tan solo debemos abrir nuestro emacs, leer de nuevo el fichero de configuracion de emacs mediante M-x load-file de nuestro .emacs o bien M-x package-refresh-contents para actualizar la cache de ELPA.

Una vez hecho esto, tan solo debemos indicar a ELPA que queremos instalar magit de la siguiente forma: M-x package-install e indicarle que queremos instalar magit.

magit_from_elpa

Instalacion de magit desde ELPA

Veamos distintos ejemplos de magit y su potencia:

magit_branch_list_and_actions_over_it

Magit accediendo a distintas ramas y posibles diffs desde emacs

magit_branch_status_and_actions

Estado actual del codigo desde magit, mediante M-x magit-status, posibles acciones que se pueden realizar

magit_diff_status

Ejemplo de diffs en Magit

magit_history_log_details

Historial desde magit a nivel detallado para cada commit

magit_summary_commits_log

 Historial desde magit para commit a nivel resumen

Pero no solo eso, la potencia aqui se ve cuando se pueden combinar potencias de emacs con las de git y magit. Por ejemplo, para ver los diffs de un fichero especifico podemos emplear M-x vc-diff o bien C-x v =. Ademas podemos expandir segun nos interese los diffs mediante TAB o marcar regiones y pulsar «s» para ver ya no diffs de un fichero especifico, sino de una region concreta.

Es una pena que este video ya tenga muchos años de antiguedad, pero aqui se pueden ver algunas features de magit: http://vimeo.com/2871241

Aqui un cheat reference de magit con las funcionalidades mas importantes y sus keybindings: http://daemianmack.com/magit-cheatsheet.html#sec-7

Para mas informacion se puede visitar la web oficial de Magit: http://magit.github.io/documentation

Existen dependencias opcionales para magit, tal como stgit (similar a Quilt para patches desde/a un stack sobre git) , svn (soporte para svn-git), topgit (patch queue manager), wip (para work in progress sobre branches) o ert (automatizacion de tests).

 


Mar 29 2014

TRAMP: Transparent Remote Access, Multiple Protocols + ad-hoc multi-hop

Tag: advancedoverdrive @ 9:31 pm

TRAMP (Transparent Remote Access, Multiple Protocols) se emplea para editar ficheros de manera remota. TRAMP emplea una conexion remota mediante shell (rlogin, telnet, ssh). Puede transferir ficheros usando rcp o algun programa similar o puede codificar el contenido de los ficheros (usando uuencode o base64) y transferirlos mediante la conexion shell.

En GNU/Emacs, como no, tenemos el tramp-mode, que permite usar dicha funcionalidad desde el propio entorno.

Pudiendo asi acceder a ficheros de manera remota de una forma transparente. Ademas se puede emplear sshfs, montando asi un sistema remoto al vuelo mediante ssh o local mount, para una mayor velocidad. Desde emacs se puede emplear en este caso:

sshfs host: localmountpoint

o si el usuario es distinto mediante:

sshfs useratremotehost@host: localmountpoint -o idmapfile,uidfile=uid,gidfile=gid

 En nuestro emacs debemos anadir las siguientes lineas:

(require ‘tramp)
(setq tramp-default-method «ssh»)

Podemos usar los siguientes keybindings para hacer uso de TRAMP:

  • Especificando el host y fichero remoto: C-x C-f /remotehost:filename  RET
  • Especificando el metodo, usuario, host remoto y fichero: C-x C-f /method:usuario@remotehost:filename)
  • Especificando el usuario, host remoto y fichero: C-x C-f /usuario@remotehost:filename
  • Haciendo su para editar ficheros como root de manera local: C-x C-f /su::/etc/hosts
  • Haciendo sudo para editar ficheros como root de manera local: C-x C-f /sudo::/etc/hosts

tramp1

Ejemplo de una conexion editando un fichero remoto con TRAMP

tramp2

Region seleccionada (en rojo) de una vez editado el fichero y modificado, es guardado de la forma tipica con C-x-s.

Si poseemos versiones Emacs 24.3+, podemos emplear ad-hoc multi-hop, que reemplaza a multi de versiones anteriores. qui podemos ver la pagina info de TRAMP en emacs:

C-h i g (tramp) Ad-hoc multi-hops RET

tramp3

Emacs es autodocumentado y desde aqui podemos ver la documentacion de TRAMP

Con la nueva sintaxis, cada hop se separa mediante el simbolo del pipe ‘|‘:

  • Especificando multi-hops: C-x C-f /ssh:you@remotehost|sudo:remotehost:/path/to/file RET

O especificando el usuario en el host remoto (ya que sino por defecto se empleara root):

  • Especificando el usuario remoto en sudo con multihops: C-x C-f /ssh:you@remotehost|sudo:them@remotehost:/path/to/file RET

Tambien, debemos tener en cuenta que debemos usar su: o sudo: si estamos haciendo hoping a un servidor remoto, ademas de indicar el hostname para cada hop, incluso si es el mismo que en el hop previo. El uso de su:: y sudo:: se puede emplear cuando no usemos multiples hops.

Si ademas, queremos conectar a un puerto diferente al tipico del ssh, podemos realizarlo de la siguiente manera:

  • Especificando un puerto distinto al 22 por defecto: C-x C-f /ssh:test@host#2222:/tmp

Si queremos activar variables para debuggear problemas empleando tramp, podemos activarlas anadiendo estas lineas a nuestro .emacs:

(setq tramp-debug-buffer t)

(setq tramp-verbose 10)

Para mas informacion sobre TRAMP, se pueden consultar las siguientes webs:


Mar 05 2014

Ejecucion de Swank (servidor Lisp) y Emacs (como cliente)

Tag: advancedoverdrive @ 10:42 pm

SLIME esta implementado de tal forma que puede ejecutarse el cliente Emacs desde un lado (slime.el) mientras el servidor Lisp en otra maquina distinta. Asi que en un host ejecutaremos emacs y le diremos a SLIME que conecte a una maquina remota.

Conectando a un Lisp remoto

Si queremos cargar swank sin ejecutar emacs de manera habitual, debemos ejecutar lo siguiente:
(load "/path/to/swank-loader.lisp")
(swank-loader:init)
Dentro de la imagen lisp que se esta ejecutando, tan solo debemos iniciar el servidor swank.
(swank:create-server)

Como vamos a tener tunneling en nuestra conexion mediante ssh, podemos indicar a sawnk que no use una conexion extra para la salida, de la siguiente forma:

(setf swank:*use-dedicated-output-stream* nil)

Si debemos realizar ciertas tareas tales como reconectar a swank, debemos mirar en otros argumentos de swank:create-server. Estos argumentos son:

:PORT : Puerto del servidor de escucha (por defecto es 4005).

:STYLE : Estilo de comunicacion

:DONT-CLOSE : Valor booleano que indica si el servidor continua aceptando conexiones despues de recibir la primera (por defecto es NIL). Util si tenemos procesos lisp ejecutandose por largo periodo de tiempo, donde conectariamos en diferentes momentos, en dicho caso deberiamos especificar :dont-close t.

:CODING-SYSTEM : Sistema de codificacion usado para la comunicacion entre Emacs y Lisp.

Un ejemplo completo con estos argumentos:

(swank:create-server :port 4005  :dont-close t :coding-system "utf-8-unix")

Mientras que en el lado de emacs deberiamos usar esto para dicha configuracion:

(setq slime-net-coding-system 'utf-8-unix)
(slime-connect "127.0.0.1" 4005)

Configurando el cliente Emacs

Desde aqui necesitamos crear un tunel ssh en el puerto 4005 desde nuestra maquina local hasta el puerto 4005 en la maquina remota.

$ ssh -L4005:127.0.0.1:4005 username@remote.example.com

Despues de esto podemos ejecutar SLIME mediante M-x slime-connect.

Configurando las translaciones pathname

Cuando ejecutamos swank remotamente, Emacs asume que los ficheros que se vayan a encontrar son ficheros normales. Sin embargo, si queremos slime-compile-and-load-file (C-c C-k) y slime-edit-definition (M-.) para trabajar correctamente, necesitamos encontrar algun modo de decirle a nuestro Emacs local que debe referirse a ficheros remotos.

Existen principalmente dos formas de hacer esto. La primera es usar mount, NFS o similar, el disco duro de la maquina remota sera visible en nuestro sistema de ficheros local como cualquier otro fichero. Sin embargo NFS es realmente lento, con bugs etc. Es por eso que la mejor forma es tener una conexion ssh y emacs con tramp-mode para hacer el resto. Lo que hacemos es decirle a Emacs como debe coger el fichero en una maquina remota y traducirlo en algo que tramp puede entender y acceder. cl:machine-instance devuelve el «hostname» para una maquina remota como hostname.domain.com.  Por lo que podemos tener algo asi:

(add-to-list 'slime-filename-translations
             (slime-create-filename-translator
              :machine-instance "hostname"
              :remote-host "hostname.domain.com"
              :username "user"))

Redireccion global de todo IO al REPL

Por defecto SLIME no cambia *standard-output*  y variantes fuera del REPL. Por lo que si tenemos cualquier otro thread el cual llama format, write-string, etc. La salida solo sera vista en *inferior-lisp* buffer o en la terminal. Por lo que podemos hacer es:

(run-in-new-thread
  (lambda ()
    (write-line "In some random thread.~%" *standard-output*)))

Para enviar la salida al buffer REPL de SLIME, de manera contraria a *inferior-lisp*, configuramos swank:*globally-redirect-io* a T.

El valor de esta variable solo es checkeado una vez cuando swank acepta la conexion. Por lo que debemos configurarlo mediante ~/.swank.lisp, ya que sino necesitaremos llamar a swank::globally-redirect-io-to-connection manualmente.

Conectando a SLIME de manera automatica

Para hacer que SLIME conecte a nuestro Lisp cualquiera que sea nuestro fichero lisp abierto, debemos anadir esto en nuestro .emacs:

(add-hook 'slime-mode-hook
          (lambda ()
            (unless (slime-connected-p)
              (save-excursion (slime)))))

Para entender un poco mejor la arquitectura de SLIME, Swank y Emacs podemos visualizar el siguiente grafico:

slime_swank


« Página anteriorPágina siguiente »