Git hooks
Aprende Git hooks — scripts que se ejecutan automáticamente en el ciclo de vida de Git para hacer lint, pruebas y validaciones. Incluye un ejemplo pre-commit.
¿Qué son los Git hooks?
Los Git hooks son scripts que Git ejecuta automáticamente cuando ocurren ciertos eventos: hacer un commit, fusionar ramas, hacer push y más. Permiten agregar acciones personalizadas al ciclo de vida de Git: ejecutar un linter antes de cada commit, validar el formato de un mensaje de commit o bloquear un push si las pruebas fallan. Los hooks son la forma en que los equipos aplican controles de calidad localmente, antes de que el código defectuoso salga de la máquina.
Esta página cubre dónde viven los hooks, la diferencia entre hooks del lado del cliente y del servidor, los hooks más útiles con ejemplos funcionales, cómo un hook aborta una operación, cómo omitir un hook y cómo compartir hooks en un equipo.
Dónde viven los hooks
Cada repositorio tiene un directorio .git/hooks que contiene scripts de ejemplo con el sufijo .sample. Para activar un hook, añade un script ejecutable con el nombre exacto del hook y sin extensión:
ls .git/hooks
# pre-commit.sample commit-msg.sample pre-push.sample ...Elimina el sufijo .sample (o crea el archivo desde cero) y hazlo ejecutable:
chmod +x .git/hooks/pre-commitUn hook puede escribirse en cualquier lenguaje, siempre que el archivo sea ejecutable y comience con una línea shebang adecuada (#!/bin/sh, #!/usr/bin/env python3, #!/usr/bin/env node, etc.). Git solo requiere que el archivo tenga exactamente el nombre de un hook conocido, sea ejecutable y devuelva un código de salida.
Hooks del lado del cliente vs. del servidor
- Los hooks del lado del cliente se ejecutan en tu máquina alrededor de operaciones locales como hacer commits y push. Son ideales para hacer lint y pruebas.
- Los hooks del lado del servidor (como
pre-receiveypost-receive) se ejecutan en el repositorio remoto cuando recibe un push — útiles para aplicar políticas de forma centralizada.
Los hooks más utilizados son del lado del cliente:
| Hook | Se dispara | Uso típico |
|---|---|---|
pre-commit | Antes de crear un commit | Hacer lint y probar archivos en staging; abortar ante fallos. |
prepare-commit-msg | Antes de que se abra el editor de mensajes | Insertar una plantilla o número de ticket. |
commit-msg | Después de escribir el mensaje | Aplicar una convención de mensajes. |
post-commit | Después de completar un commit | Enviar una notificación; no afecta el commit. |
pre-push | Antes de enviar un push | Ejecutar la suite de pruebas completa como última verificación. |
Cómo un hook aborta una operación
Todo el mecanismo de control es el código de salida. Para un hook "pre-" (pre-commit, pre-push, commit-msg, …):
- Salida
0→ el hook aprobó la operación y Git continúa. - Salida distinta de cero → Git cancela la operación. El commit no se crea o el push no se envía.
Los hooks "post-" (post-commit, post-merge, …) se ejecutan después de que la acción ya se completó, por lo que su código de salida es ignorado — no pueden deshacer nada. Úsalos para notificaciones, no para validación.
Un ejemplo de pre-commit
Este hook pre-commit ejecuta el linter del proyecto y bloquea el commit si reporta problemas. Como sh aborta ante el comando fallido gracias a set -e, no se necesita ninguna comprobación extra de $?:
#!/bin/sh
set -e
echo "Running lint..."
npm run lintSi npm run lint sale con código distinto de cero, set -e propaga ese código y el commit se aborta. Si prefieres un mensaje personalizado, comprueba el resultado explícitamente:
#!/bin/sh
if ! npm run lint; then
echo "Lint failed — commit aborted. Fix the issues and try again."
exit 1
fiGuarda esto como .git/hooks/pre-commit y ejecuta chmod +x .git/hooks/pre-commit.
Un ejemplo de commit-msg
El hook commit-msg recibe un argumento: la ruta a un archivo temporal que contiene el mensaje propuesto. Lee ese archivo, valídalo y sal con código distinto de cero para rechazarlo. Este ejemplo aplica un prefijo de estilo Conventional Commits:
#!/bin/sh
# $1 is the path to the file containing the commit message
message=$(head -n1 "$1")
pattern='^(feat|fix|docs|style|refactor|test|chore): .+'
if ! echo "$message" | grep -Eq "$pattern"; then
echo "Commit message must start with feat:, fix:, docs:, etc."
exit 1
fiOmitir un hook
Un hook es una red de seguridad, no una barrera. Cuando genuinamente necesites saltarte los hooks pre-commit y commit-msg para una operación, usa --no-verify:
git commit --no-verify -m "WIP: skip checks"
git push --no-verifyÚsalo con moderación — saltarse el linter es cómo el código defectuoso se cuela en el historial.
Compartir hooks con un equipo
Como .git/hooks no se incluye en los commits, los hooks no viajan con un clon. Los equipos resuelven esto almacenando los hooks en un directorio rastreado y apuntando Git hacia él:
git config core.hooksPath .githooksAhora Git busca en el directorio rastreado .githooks/ en lugar de .git/hooks. Haz commit de tus scripts allí, márcalos como ejecutables, y cada miembro del equipo los obtendrá después de ejecutar el mismo comando git config (o después de que un script de configuración lo haga por ellos). Consulta Git config para más información sobre cómo almacenar configuraciones por repositorio.
Herramientas como Husky automatizan exactamente esto para proyectos JavaScript, configurando hooks compartidos durante la instalación. Para políticas que no puedes permitir que nadie omita con --no-verify, aplícalas con hooks del lado del servidor o con la protección de ramas de tu plataforma de alojamiento, ya que los hooks del lado del cliente siempre residen en la máquina del desarrollador.
Temas relacionados
- git commit — el comando alrededor del cual se envuelven los hooks pre-commit y commit-msg.
- Firmar commits — verificar la autoría, a menudo combinado con hooks.
- Git config — donde se almacenan
core.hooksPathy otras configuraciones. - Git alias — atajos para los comandos que ejecutan tus hooks.