Añadir a git los ficheros migrate de django en pycharm

Pycharm doesn't love django + git

Si usas el IDE pycharm y django ≥ 1.7 (o < 1.7 con south) y como gestor de versiones usas git, probablemente te ha pasado que haces un commit y se te olvida añadir (git add) los ficheros de las migraciones. Pycharm hace «git add» automáticamente a todos los ficheros creados, salvo a los creados tras hacer un makemigrations. Para saberlo, pycharm nos los muestra en rojo en lugar de en verde.
¿Por qué este comportamiento? Realmente lo desconozco, pero imagino que porque no ha sido una acción hecha directamente por el usuario, sino como consecuencia de un migrate.

A mí me pasa a menudo que hago un commit y luego otro como un comentario «los migrates del anterior commit». Cuando esto no sólo te pasa a ti, sino que es algo común en los demás compañeros empiezas a preguntarte cómo solucionarlo, y ya de paso convertirte en el héroe que evita el 1×2 en los commits. Depende de la manera de trabajar que tengas, el tipo de integración de las ramas que hagas y demás, esto puede serte útil o no, pero nunca está de más probar algo desconocido.

Para ello podemos usar los hooks de git, que son pequeños scripts que se ejecutan cuando ocurre algún evento, como commit, push, receive. Podemos crear un hook de tipo pre-commit, que se ejecutará antes de hacer un commit, para que no nos deje hacer commit si tenemos algún fichero sin añadir.

Los hooks se instalan en la carpeta .git/hooks del repositorio de git que queramos. Cuando inicializamos un repositorio ya nos crea el directorio con algunos ejemplos.

De acuerdo con la respuesta correcta de este hilo de stackoverflow, podemos crear meter el siguiente código en el fichero pre-commit hook:

ud=`git status -u | grep '^Untracked files:$'`
 
if [ -n "$ud" ];
then 
    cat < <EOF 
¡Cuidado! Hay ficheros que no se han añadido al repositorio
EOF
    exit 1
fi

Este script lo único que hace es ver si al ejecutar git status -u existe la frase ‘Untracked files’. Si es así nos avisa con un bonito texto y no efectúa el commit. De esta manera nos dará un aviso antes de efectuar el commit y podremos subsanarlo sin tener que añadir posteriormente un commit separado.

Si aún así queremos dejar ficheros sin subir, podemos usar git commit --no-verify para que no ejecute el hook.

Más sobre git hooks:

http://git-scm.com/book/es/v2/Customizing-Git-Git-Hooks
http://githooks.com/
https://www.atlassian.com/git/tutorials/git-hooks/

Exportar repositorio con git archive

Acabamos de terminar blazaid y yo la mudanza del dominio, así que me ha picado el gusanillo de volver por estos lares para escribir una nueva entrada. Esta vez para un howto rapidito sobre git.

A veces necesitamos exportar un proyecto de git para subir a un servidor, pero no queremos clonar el repositorio con «git clone», ya que tendremos el árbol de directorios, incluido .git y cosas que no querremos subir. Una opción es copiar únicamente los directorios y ficheros que queramos a una nueva ubicación, y listo. Pero si lo hacemos más de tres veces empezaremos a murmurar cosas como «tu madre era un troll» a la pantalla. Y no es plan… Así que para automatizar un poco, obviando sistemas de Integración Continua, mucho más-mejores y más mega-profesionales, podemos usar el comando git archive.

Todo vino con una búsqueda en google similar a «svn export in git«. En SVN era tan sencillo como el comando anterior, así que siendo git mucho más evolucionado, con exoesqueleto de adamantio y todo, tenía que poderse hacer. No me equivocaba. Un par de visitas a stackoverflow y ya estaba creándome todos los scripts en bash para ello.

Desde el path donde tengamos el repositorio que queramos exportar ejecutamos:


# Formato tar.gz
git archive HEAD | gzip > /path/to/export/project.tar.gz

# O en zip
git archive --format zip --output /path/to/export/project.zip HEAD

Es importante haber hecho commit de todos los ficheros que queramos, puesto que en este caso nos subirá lo que esté en HEAD (se puede poner master / devel o la rama que uséis vosotros). Con esto tendremos un fichero comprimido que podremos copiar donde queramos.

Esto nos comprimirá todo el contenido del proyecto, obviando el directorio .git. Tampoco es mucho avance.

Para elegir qué más cosas quitar de la exportación, podemos hacer uso del fichero .gitattributes, que crearemos en el raíz del proyecto. El mío tiene un aspecto parecido al siguiente:

# Used to remove files from deployment using `git archive`

# Git Files
.gitignore export-ignore
.gitattributes export-ignore

Thumbs.db export-ignore
project.tar.gz export-ignore

# Directories
_docs export-ignore
_scripts export-ignore

# General text files
*README.txt export-ignore
*LICENSE.txt export-ignore
COPYRIGHT.txt export-ignore
CHANGELOG.txt export-ignore

Esto hará que se excluyan las carpetas /_docs y todos los archivos que no queramos, como el propio .gitattributes o los famosos .gitignore, dejándonos un proyecto limpio y listo para poder subirse a producción sin mucho problema.

¡Feliz semana!

Migración de repositorios GIT

En mi trabajo usamos un esquema centralizado, es decir, un repositorio donde se centraliza todo el código. En éste se encuentran dos ramas, la master, cuyo HEAD apunta siempre a la última versión que se corresponde a la desplegada en producción y la devel, cuyo HEAD apunta a la última versión en desarrollo.

El caso es que tenemos para cada proyecto un repositorio de este tipo en una máquina que, aunque simpática, se nos está quedando corta por lo que estoy migrando los repositorios a otra máquina. Se me ha dado un caso en particular que ha sido la necesidad de migrar el contenido de un repositorio clonado del antiguo a un repositorio ya existente en la máquina destino.

Dado que estamos en un repositorio clonado, éste tendrá una referencia al repositorio del que fue clonado denominada origin. Supongamos que ésta apunta a http://server_orig.com/git/repo/ y que el nuevo repositorio está ubicado en http://server_dest.com/git/repo/ y está vacío. Pues bien, la receta (supongo que una de muchas) que he usado es la siguiente. Desde el directorio donde está ubicado nuestro repositorio clonado:

# Hacemos que nuestro repositorio apunte al nuevo con otro nombre
$ git remote add dest http://server_dest.com/git/repo/
# Enviamos nuestras ramas "master" y "devel" al nuevo repositorio
$ git push -u dest --all
# Eliminamos las referencias a los repositorios remotos existentes
$ git remote rm origin
$ git remote rm dest
# Hacemos que "origin" (remote por defecto) apunte al nuevo repositorio
$ git remote add http://server_dest.com/git/repo/

Se puede hacer de más formas (borro el origin y lo creo de nuevo apuntando al nuevo, etcétera) pero al menos el concepto queda claro. Hay que tener en cuenta que git push -u dest --all envía todas las ramas de nuestro repositorio al nuevo. Eso quiere decir que si tenemos más ramas en el repositorio original, si no las tenemos en local, no se enviarán.

Espero que os sirva. Que paséis un estupendo fin de semana!