Robots y modelos

Notas sobre pruebas, modelado y aventuras en Java y Android

Posts Tagged ‘git

Smart HTTP de Git y Redmine

with 5 comments

Actualmente estoy preparando la actualización de un servidor Redmine que gestiono. Es una actualización bastante grande: no es sólo cuestión de actualizar la instancia Redmine, sino todo el sistema y herramientas de apoyo. Lleva demasiado tiempo sin tocarse y tiene una distribución antigüilla.

Una de las cosas que quería añadir en el servidor actualizado (junto con Redmine 0.9.2, la última versión estable a fecha de hoy) era soporte integrado con Git. Sin embargo, hay un problema, conocido por todos los usuarios de la red de la Universidad de Cádiz: el cortafuegos cierra el paso a prácticamente todo salvo correo y HTTP (y ocasionalmente HTTPS). Por eso, muchos de los protocolos que admite Git están fuera de la cuestión: el propio de Git (que de todos modos no serviría para los push), SSH (como lo proponga le da un patatús al CITI) y rsync (en desuso).

Git ha permitido el uso de HTTP y HTTPS desde hace tiempo sobre WebDAV, al estilo de Subversion, pero nunca me ha gustado este método. Además de sus problemas de rendimiento, de no poder usar “hooks” (manejadores de eventos) y de las molestias que origina (hay que meter un trabajo cron que ejecute “git update-server-info” periódicamente en cada repositorio), tiene una pega gravísima: la consistencia del repositorio está a completa merced de todos y cada uno de los clientes que acceden al repositorio.

Cuando di mi primer curso de Git en 2008, algunos de los asistentes me dijeron tajantemente que ni hablar de activar SSH en sus máquinas, así que probamos el acceso por HTTP. Curiosamente, el primer día que lo probamos y estuvimos mandando commits en rápida sucesión, se corrompió el repositorio varias veces. Al siguiente día, no se corrompió en absoluto, y le estábamos mandando cosas incluso más rápido que antes. Pero entonces llegó uno de los asistentes que faltaba, y dicho y hecho: a los pocos minutos estaba corrompido. El origen del problema: la versión de Curl del asistente que había venido (único usuario de MacBook en la sala, jeje) era antigua. Cuando Git trabaja sobre WebDAV, el servidor HTTP sirve de poco más que de un área de subida de ficheros. Si la versión de la biblioteca curl instalada en el cliente tiene fallos, éstos repercutirán en el repositorio (como con Curl 7.16.0). Ah, y por supuesto no hay nada de atomicidad: si el usuario pulsa Ctrl+C en mitad de la actualización o se le va la conexión, puede que el repositorio también se quede en un estado inconsistente.

Por estas razones, descarté incluso HTTP/HTTPS, y no metí Git en la instalación actual de Neptuno. Todo se quedó en una instalación de Gitosis en mi servidor doméstico. Sin embargo, Git 1.6.6 incluye “smart HTTP“, que arregla los problemas de rendimiento, llama a los “hooks”, evita la necesidad del trabajo cron con “git update-server-info” y proporciona las mismas garantías de atomicidad que el acceso por SSH. El “truco” es que ya el servidor HTTP no es una tonta área de almacenamiento WebDAV, sino un CGI que implementa los servicios que usualmente se invocan mediante SSH a través de HTTP. Lo bueno de todo esto es que además, al ser HTTP, podría integrarlo directamente con el esquema de autenticación de Redmine, con lo que un proyecto público admitiría fetch anónimo pero limitaría los push a los miembros con derecho a commit, y un proyecto privado exigiría los derechos apropiados para tanto fetch como push. La única “pega” es que hay que tener Git 1.6.6 como mínimo tanto en el cliente como en el servidor, pero bueno, los verdaderos usuarios de Git se lo compilan desde las fuentes, que no es tan difícil ;-).

Sin embargo, esta configuración de Apache que combina las instrucciones del CGI con una variación de la propuesta en la web de Redmine no sirve directamente:

SetEnv GIT_PROJECT_ROOT /var/www/git
SetEnv GIT_HTTP_EXPORT_ALL
ScriptAlias /git/ /usr/libexec/git-core/git-http-backend/
PerlLoadModule Apache::Authn::Redmine
<Location /git>
  Order deny,allow
  Allow from all

  PerlAccessHandler Apache::Authn::Redmine::access_handler
  PerlAuthenHandler Apache::Authn::Redmine::authen_handler
  AuthType Basic
  AuthName "Redmine Git Repository"
  Require valid-user

  RedmineDSN "DBI:mysql:database=databasename;host=my.db.server"
  RedmineDbUser "usuario"
  RedmineDbPassword "contraseña"
</Location>

Cuando lo probé, no podía crear clones de forma anónima de un proyecto público: tenía siempre que introducir contraseña. Además, tampoco podía hacer push en un proyecto público, por mucho que pusiera la contraseña. La explicación es algo larga, pero es interesante (y me llevó unos buenos tirones de pelos y media mañana :-/). Está dividida en dos partes:

  • El hecho de que fallaran los clones anónimos es que el manejador de autenticación de Redmine, pensado para WebDAV como está, distingue las acciones de "sólo lectura" (las que pueden hacerse de forma anónima en proyectos públicos y que no requieren derechos de escritura para los miembros de proyectos privados) del resto a través de sus métodos HTTP. Por omisión, estos métodos son GET, PROPFIND, OPTIONS y REPORT: por definición, no deberían producir cambios en los recursos a los que acceden. Sin embargo, al hacer "fetch" con smart HTTP de Git se hace una petición POST al servicio git-upload-pack, y hace que pida contraseña: nos quedamos sin fetch anónimo.
  • El segundo problema se debe a que git-http-backend, el CGI necesario en el servidor para "smart HTTP", obliga por omisión a que todos los push (peticiones al servicio git-receive-pack)  tengan algún tipo de autenticación. De lo contrario, los rechaza con un código de error 403 (Forbidden). Y aquí volvemos con el problema de distinguir las acciones por los métodos HTTP: el servicio git-receive-pack se utiliza en dos fases. La primera fase es un GET a /git/repositorio/info/refs?service=git-receive-pack: git-http-backend exige autenticación, pero el manejador de Redmine no se la pide al usuario, ya que es un método de sólo lectura. Resultado: un bonito Forbidden (403) para cualquiera que quiera hacer push. Los tirones de pelos en este lado vienen de que Git, al ver que el push por smart HTTP no ha ido, asume que el servidor es de tipo WebDAV, y hace más peticiones que hacen más difícil encontrar el problema. Actualización 14/03/2010: puede que en dentro de poco se deje de pedir contraseña para el GET.

¿Cómo corregir esto? Pues haciendo que el manejador de Redmine distinga las peticiones "de sólo lectura" del resto no por método HTTP, sino por dirección, tal y como viene en la página man del CGI (segundo ejemplo de configuración de Apache). Podría haber desarrollado un nuevo manejador de autenticación, pero duplicaría todo el código salvo en un par de sitios. He preferido añadir una nueva directiva que activa este comportamiento, que por omisión está deshabilitado: RedmineGitSmartHttp. Para usarla, pondríamos esta línea dentro del anterior <Location /git>:

RedmineGitSmartHttp yes

y ya todo debería ir sin problemas. Esto no afectará a los bloques <Location /svn> que tengamos en otra parte: se usan instancias distintas del manejador de autenticación, y esas instancias no tendrán esta opción activada, por lo que mantendrán el antiguo comportamiento. Bueno, por lo menos así me ha funcionado en mi servidor de pruebas y así lo indican sus registros :-D. He probado con proyectos públicos y no públicos en SVN y Git en el mismo Redmine y ha ido bien.

Para aquellos interesados en el código, he enviado un parche al proyecto Redmine, para que lo fusilen revisen más y mejores ojos que los míos. A ver qué tal lo reciben. Para aplicarlo, hay que descomprimir las fuentes de Redmine, poner el parche en su directorio principal, y ejecutar esto:

patch -p1 < 0001-Redmine.pm-add-Git-smart-HTTP-support.patch

Un último detalle: Git necesitará nuestro usuario y contraseña para autenticarse. Una opción es clonar con http://usuario@host/git/repositorio, poniendo el usuario en la URL y metiendo la contraseña cada vez que haga falta, pero es muy incómodo: por desgracia, no guarda las contraseñas automáticamente como hace SVN. Otra opción es crear un fichero ~/.netrc (¡sólo legible por nuestro usuario!) con nuestro usuario y contraseña:

machine mihost
login miusuario
password mipassword

No me termina de gustar eso de tener mi contraseña guardada en la máquina, pero bueno, ya pasaba con SVN, así que supongo que es el precio a pagar por no tirar de SSH :-/.

Written by bluezio

24 de febrero de 2010 at 9:50

Publicado en Desarrollo

Tagged with , , , ,

“git add –interactive” en msysGit (Git bajo MinGW en Windows)

leave a comment »

Recientemente tuve que trabajar con Git en Windows. Parece que la versión “oficial” por así decirlo es la compilada por Cygwin, pero no vi ningún binario por ahí para descargar. Instalarme Cygwin en la máquina virtual era demasiado, así que opté por msysGit, una versión de Git ejecutándose bajo MSYS.

MSYS es una implementación de un entorno POSIX minimalista sobre MinGW, un marco base que permite desarrollar programas para Windows sobre herramientas GNU. Es mucho más ligero que Cygwin, y los ejecutables generados no requieren del DLL usual de éste, que ocupa bastante.

La ventaja principal de msysGit es que en teoría consiste simplemente en seguir los pasos de un instalador. Sin embargo, no resulta tan fácil: no todo funciona directamente tras instalarlo. En particular, la terminal estilo bash que proporciona da bastantes problemas: acabé descartándola por la línea de órdenes usual de Windows XP (cmd). Sí, es horrible, pero por lo menos funciona.

Lo que realmente era vital para mí era “git add –interactive” (suelo ponerlo bajo el alias “git ai“): lo uso todo el tiempo, y me resulta realmente útil. git-gui será gráfico, pero es incomodísimo en comparación con él. Además, permite enviar al índice sólo parte de los cambios, dividir los cambios en partes más pequeñas, o incluso editar el cambio antes de pasarlo al índice. También uso mucho “git rebase –interactive” para limpiar mis commits, retirando los enfoques que he ido descartando y disimulando los errores que he cometido :-D.

Para que ambos funcionaran debidamente, tuve que instalar la última versión de less del proyecto GnuWin32, y la última versión oficial de Vim para Windows. Después cambié los nombres de less.exe y vim.exe del subdirectorio bin dentro del de Git a otros que no interfirieran, y puse los directorios en que estaban los binarios del nuevo less y Vim en la variable de entorno PATH.

Con esto quedó todo bien. El único pero es que sólo funciona si uso cmd, pero espero que los de msysGit afinen un poco más el asunto en futuras versiones.

Written by bluezio

24 de enero de 2009 at 19:56

Publicado en Uncategorized

Tagged with , ,

Recuperando un repositorio SVN desde un clon de git-svn

with one comment

Hoy me vi en la necesidad de recuperar un repositorio SVN perdido a partir de un clon Git creado con git-svn. Miré y encontré cosas sobre cambiar la URL del repositorio, pero no me sirvieron de mucho: el problema es que sencillamente, el antiguo repositorio está kaput y punto.

Rebuscando y mezclando cosas de aquí y aquí, he conseguido una pequeña receta que puede que sirva a alguien. No recupera fechas y los autores sólo a medias, pero bueno, por lo menos recuperamos los datos de las revisiones en sí.

Supongamos que trabajo sólo sobre /tmp y que el clon creado con git-svn del repositorio SVN desparecido está en /tmp/original-git. Lo primero que tenemos que hacer es crear un nuevo repositorio SVN, con una única revisión con un fichero cualquiera, que tenga algún contenido (con un espacio sirve), para que git-svn lo pueda clonar y manejar bien. Para ello, ejecutaremos:

svnadmin create svn-recreado
svn co file:///tmp/svn-recreado recreado
cd recreado
echo " " > borra.me
svn add borra.me
svn ci -m "primera revisión"

Con esto ya tenemos la primera revisión con algún fichero no vacío. Esto es importante, ya que de lo contrario no podremos crear un clon con git-svn. Vamos a crear ese clon. Tal y como sugiere este enlace, necesitaremos la opción de configuración svn.addAuthorFrom si queremos conservar (aunque será en las descripciones y no en el nombre de usuario en sí) la autoría de cada revisión.,o hacer siempre "git svn dcommit --add-author-from". Puestos a elegir, la primera opción evita despistes. Ejecutamos estas órdenes:

git config --global svn.addAuthorFrom true
git svn clone file:///tmp/svn-recreado recreado-git

Ya tenemos el repositorio SVN y su clon Git con la revisión inicial que necesitamos. Vamos a enviar las revisiones del antiguo repositorio SVN desde /tmp/original-git a /tmp/recreado-git:

cd /tmp/original-git
git push file:///tmp/recreado-git/.git master:refs/heads/old-svn

Con esto hemos empujado las revisiones de la rama principal del antiguo clon Git a una nueva rama llamada old-svn en recreado-git. Esta rama no guarda parentesco alguno con la rama master de recreado-git. Tendríamos algo así:

old-svn: A --> B --> C
master: D

Para que cuando hagamos "git svn dcommit" desde old-svn se envíen las revisiones A, B y C al repositorio SVN, se tienen que cumplir una serie de condiciones. La primera es retirar los git-svn-id de los mensajes de los commits (ya que hacen referencia a la antigua URL y números de revisión). Para eso tenemos git-filter-branch:

git filter-branch --msg-filter 'sed -e /^git-svn-id:/d' old-svn

Esto transforma todos los commits de la rama old-svn, cambiando el mensaje con un guión de sed (más información aquí). Lo que hace este filtro es, en las líneas que cumplen la expresión regular /^git-svn-id:/ (es decir, que empiezan por git-svn-id:) ejecutar la orden "d", que efectivamente borra dicha línea.

Lo siguiente es crear el enlace D --> A entre las revisiones. Lo desconocía hasta hoy, pero Git permite hacerlo manualmente, utilizando el fichero .git/info/grafts, que consiste en una serie de líneas "<hijo> <padre1> <padre2>..." con los SHA1 correspondientes. Tendremos primero que averiguar los SHA-1 de la última revisión de la rama old-svn y master. Para eso, podemos utilizar git-rev-list:

echo `git rev-list old-svn | tail -1` `git rev-list master | tail -1` >> .git/info/grafts

Ahora, si ejecutamos:

git checkout old-svn
git log

Deberíamos de ver la primera revisión del nuevo repositorio SVN justo después de la primera de nuestro repositorio. Con esto hemos terminado las preparaciones. Cambiamos a la rama master, reunimos con old-svn y enviamos al repositorio SVN:

git checkout master
git merge old-svn
git svn dcommit

Una nota: es posible que se quede calado en mitad de una revisión, como un coche de segunda mano :-D. Con arrancar el proceso de nuevo y continuar el dcommit sirve:

git checkout -f old-svn
git rebase master
git checkout master
git merge old-svn
git svn dcommit

Written by bluezio

13 de octubre de 2008 at 21:26

Publicado en Uncategorized

Tagged with , ,

Encuesta y libro de Git

leave a comment »

Sigo con mi aprendizaje y enseñanza simultánea de Git :-). Hace poco la gente de la lista de correo ha publicado la Git User’s Survey 2008, que está arrojando algunos resultados muy interesantes. Traduciendo del mensaje de la lista de correo con el resumen parcial de los resultados tras las primeras 1000 respuestas, parece ser que:

  • El 59% de la gente que usa Git sabe C, y el 52-53% saben Ruby y/o shell scripting. Le sigue la gente de Python y luego los de Perl. Java tiene un 42% (buenas noticias para los de JGit y EGit). Tcl/Tk está muy bajo, por lo que parece que conseguir contribuidores para gitk va a estar complicado. 12 respuestas no eran ni programadores.
  • Algunos usos interesantes de Git incluyeron:
    • Emplear Git para seguir otros proyectos que usan SVN (lo que hago yo ahora, y es muy cómodo).
    • Sistemas de ficheros versionados.
    • Distribución de materiales / colaboración al redactar documentos.
    • Creación de zonas de pruebas en las que ver exactamente qué hacen los instaladores binarios.
    • Almacenamiento de resultados de pruebas y compilaciones.
  • La mayoría de la gente usa paquetes binarios para conseguir Git (59%), y luego vienen los medio frikis con los paquetes fuente y tarballs (28%) y los muy muy frikis que lo sacan del repositorio directamente (22%).
  • La mayoría de los usuarios de Git trabajan sobre Linux, luego viene MacOS X, y luego Windows. Hay 2 usándolo en iPhone, supuestamente… :-/
  • En cuanto a porcelanas, la mayoría de la gente usa core-git, y si no, o usa sus propios scripts o Easy Git. Si se trata de sistemas de gestión de parches a la quilt, lo que se emplea más es StGIT.
  • En herramientas gráficas vence por goleada gitk y git-gui, pero QGit tiene un respetable 11%. GitNub está ahí, pero parece más un visor para MacOS X de las contribuciones a un repositorio alojado a GitHub que un visor de Git en general. Dicen que JGit y EGit están avanzando, así que a lo mejor vemos integración en condiciones con Git para la edición del año que viene. También se está implementando algo del estilo en KDevelop 4.
  • En interfaces Web, domina gitweb con un 80% (git instaweb y tachán, se entiende), y le sigue Gitorious (14%), que está muy bien si quieres algo estilo forja: es una web, y al mismo tiempo un proyecto de software libre (te puedes descargar su código e instalar tu propia versión, no como GitHub, cuyo código es cerrado). Está cgit por ahí, un CGI hecho entero en C que tiene pinta de ser muy limpio y ligero (6%). Mira, han mencionado Redmine, el que enseñé a la gente del curso de Git.
  • A la gente no le interesaría pagar por soporte comercial, con un rotundo “no” del 69% (y un 22% “no aplicable”).

De todas formas, esto irá cambiando un poco conforme se vayan recogiendo algunas respuestas. Ya hay una parte 2 y una parte 3 de ese resumen, pero no tengo tiempo para traducirlas :-D.

Por otro lado, Scott Chacon está trabajando en un libro de Git que recoge muchos gráficos de gran calidad y sus screencasts, y está pidiendo colaboración de la comunidad. Sólo he tenido tiempo de echarle un vistazo, pero tiene muy buena pinta.

Written by bluezio

6 de septiembre de 2008 at 9:02

Publicado en Uncategorized

Tagged with ,

Seguir

Recibe cada nueva publicación en tu buzón de correo electrónico.

Únete a otros 260 seguidores