Founding Member: $499/mes de por vida
Trucos técnicos

Git sin pisarse entre terminales

Tres piezas para cuando varias terminales o sesiones de tu asistente trabajan en el mismo repo: un "git add" que nunca guarda borrados ni trabajo ajeno, un candado que niega git stash y --autostash si apartarían cambios ajenos, y un aviso cuando tu commit deshace otro.

HookPremiumTrucos técnicosgit e infra

🌱 Empieza aquí · Git sin pisarse entre terminales

Cuando abres dos o más terminales (o sesiones de tu asistente) sobre el mismo proyecto, una puede llevarse sin querer el trabajo de la otra. Esta semilla le enseña a tu asistente tres cuidados con git (la herramienta que guarda el historial de tu proyecto):

  1. Guardar solo lo suyo. Un "agregar al commit" selectivo que nunca guarda un archivo borrado sin tu aprobación y que deja fuera lo que ya estaba a medias cuando arrancó.
  2. No esconder lo ajeno. Un candado que le niega git stash y --autostash (dos comandos que "apartan" los cambios sin guardar) si con eso se llevaría cambios de otra sesión.
  3. Avisar si deshizo algo. Un vigía que, justo después de un commit, avisa si ese commit regresó un archivo a como estaba antes del commit de otra sesión de los últimos 15 minutos.

Para instalarla

Abre esta carpeta con Claude Code y dile a tu asistente:

"Siembra esta semilla 🌱"

Con eso basta de tu parte. Tu asistente hace todo lo técnico y al final te dice cómo comprobar que quedó funcionando. Vas a ver algunas palabras en inglés en la pantalla; ni caso, él te guía.

¿Otras formas de pedírselo? "instala esta semilla", "pon a funcionar el candado de git", "haz el setup de esto". Te entiende igual.


Para el asistente que siembra 🤖

Esta semilla es un hook (gancho de automatización: un script que Claude Code corre solo antes o después de cada herramienta). Trae tres piezas en kernel/:

| Archivo | Qué es | Cómo se activa | |---|---|---| | guardia-autostash.py | Candado PreToolUse (antes de ejecutar) del Bash. Niega con exit 2 un git stash o --autostash que apartaría cambios ajenos. | Se registra en settings.json. | | commit-deshace-ajeno-aviso.py | Vigía PostToolUse (después de ejecutar) del Bash. Tras un git commit, avisa si deshizo un commit de otra sesión. Nunca bloquea. | Se registra en settings.json. | | git-safe-add.sh | Biblioteca de bash con git_safe_add y git_write_baseline. Reemplaza a git add -A en los scripts que guardan solos. | No se registra: se carga con source desde el script que commitea. |

Además: settings.ejemplo.json (el registro de los dos hooks), turnos.ejemplo.json (el formato opcional del registro de turnos) y pruebas/ (tres bancos de prueba con repos de juguete).

Requisitos: bash, git (2.18 o más nuevo) y python3 (3.8 o más nuevo). No instales nada que falte sin preguntarle a la persona.

1. Copia el kernel a la carpeta de hooks

Corre esto desde la carpeta de esta semilla (macOS, Linux, o Windows con WSL / Git Bash):

SEMILLA="$(pwd)"
DESTINO="$HOME/.claude/hooks/git-sin-pisarse"
mkdir -p "$DESTINO"
cp "$SEMILLA/kernel/git-safe-add.sh" \
   "$SEMILLA/kernel/guardia-autostash.py" \
   "$SEMILLA/kernel/commit-deshace-ajeno-aviso.py" "$DESTINO/"
mkdir -p "$DESTINO/pruebas"
cp "$SEMILLA/kernel/pruebas/"*.sh "$DESTINO/pruebas/"
chmod +x "$DESTINO/"*.py "$DESTINO/"*.sh "$DESTINO/pruebas/"*.sh

Windows sin WSL ni Git Bash: no está soportado. git-safe-add.sh necesita bash, y los dos scripts de Python comparan rutas con /. Dile a la persona que use WSL.

2. Registra los dos hooks en settings.json

Abre $HOME/.claude/settings.json (créalo si no existe). Suma estas dos entradas a lo que ya haya en hooks.PreToolUse y hooks.PostToolUse; no reemplaces los hooks que la persona ya tenga. El contenido exacto está en kernel/settings.ejemplo.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          { "type": "command",
            "command": "python3 \"$HOME/.claude/hooks/git-sin-pisarse/guardia-autostash.py\"",
            "timeout": 20 }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          { "type": "command",
            "command": "python3 \"$HOME/.claude/hooks/git-sin-pisarse/commit-deshace-ajeno-aviso.py\"",
            "timeout": 20 }
        ]
      }
    ]
  }
}

Muéstrale a la persona el cambio antes de guardarlo. Los hooks se cargan al abrir una sesión nueva de Claude Code.

3. (Opcional) Usa git_safe_add en los scripts que guardan solos

Si la persona tiene un script que commitea por su cuenta (al cerrar sesión, un respaldo automático, una sincronización), cambia su git add -A por esto. Dos reglas del helper: corre bajo bash (no zsh) y no se captura con $(...), porque los resultados salen en variables y una subshell los perdería. Guarda el baseline fuera del repo.

Son dos momentos distintos. Si el baseline se escribiera al guardar, todo lo sucio quedaría como "de antes" y no se guardaría nada.

Al ARRANCAR la sesión (por ejemplo, en un hook SessionStart o al inicio del script): anota lo que ya estaba sucio, que no es de esta sesión.

source "$HOME/.claude/hooks/git-sin-pisarse/git-safe-add.sh"
REPO="<TU-RUTA>/mi-repo"
BASELINE="${TMPDIR:-/tmp}/baseline-$(basename "$REPO")-${CLAUDE_CODE_SESSION_ID:-manual}.txt"
cd "$REPO" || exit 1
git_write_baseline "$BASELINE"

Al GUARDAR (en lugar del git add -A): agrega solo modificados y nuevos que no estaban en el baseline.

source "$HOME/.claude/hooks/git-sin-pisarse/git-safe-add.sh"
REPO="<TU-RUTA>/mi-repo"
BASELINE="${TMPDIR:-/tmp}/baseline-$(basename "$REPO")-${CLAUDE_CODE_SESSION_ID:-manual}.txt"
cd "$REPO" || exit 1
git_safe_add "$BASELINE"
echo "agregados=$GIT_SAFE_ADDED borrados-frenados=$GIT_SAFE_SKIPPED_DELETES movidos=$GIT_SAFE_MOVED fuera-por-baseline=$GIT_SAFE_SKIPPED_BASELINE"
if [ -n "$GIT_SAFE_ADD_ERROR" ]; then echo "el add falló: $GIT_SAFE_ADD_ERROR"; fi
git diff --cached --name-status
if [ "$GIT_SAFE_ADDED" -gt 0 ]; then git commit -m "Guarda el trabajo de esta sesión"; fi

Ojo con lo que ya estaba en stage. git_safe_add decide lo que él agrega, pero no vacía el índice (el "stage", la lista de lo que entra al siguiente commit). Si alguien ya había puesto algo ahí (un git rm, o un git add de otra terminal), el git commit también se lo lleva, borrados incluidos, y GIT_SAFE_ADDED lo cuenta. Por eso el bloque imprime git diff --cached --name-status justo antes del commit: queda en la salida como registro de qué entró (una D es un borrado). Si la persona prefiere revisar antes de guardar, quita la última línea del bloque y que el commit se haga a mano después de leer esa lista.

Si el archivo de baseline no existe, git_safe_add no excluye nada por baseline (los borrados siguen frenados igual). Reemplaza <TU-RUTA>/mi-repo por la carpeta real del proyecto. Los borrados frenados quedan anotados (una línea por ruta y por día) en $HOME/.claude/git-sin-pisarse/deletes-detectados.log, y los add fallidos en git-add-fallidos.log de la misma carpeta. Para mover esos logs, exporta GIT_SAFE_LOG_DIR=<TU-RUTA> antes de llamar a git_safe_add.

4. (Opcional) Registro de turnos: decir qué rutas son de esta sesión

Sin registro, el candado trata todo cambio sin guardar como ajeno y niega el stash si el árbol tiene cualquier cambio. Es el lado seguro. Si la persona quiere que una sesión pueda hacer stash de sus archivos, el candado lee (solo lectura) un registro de turnos en $HOME/.claude/git-sin-pisarse/turnos.json (o en la ruta de la variable GIT_SIN_PISARSE_TURNOS). Formato, como en kernel/turnos.ejemplo.json:

  • session: el id de sesión que Claude Code manda al hook.
  • paths: archivos o carpetas de ese turno.
  • expires_at: hasta cuándo vale, en segundos desde 1970 (epoch). Un turno vencido no protege.

Esta semilla no trae una herramienta para escribir ese registro: se escribe a mano o con otra herramienta de la persona.

5. (Opcional) El renglón Claude-Session en los commits

El vigía distingue sesiones por un renglón Claude-Session: <id> en el mensaje del commit. Si dos commits lo traen con el mismo valor, son de la misma sesión y no avisa. Si alguno no lo trae, cuenta como ajeno: sin ese renglón, también va a avisar cuando una sesión revierte su propio commit de hace menos de 15 minutos.

6. Verificación post-siembra (obligatoria)

a) Los archivos quedaron en su lugar:

DESTINO="$HOME/.claude/hooks/git-sin-pisarse"
ls -la "$DESTINO/git-safe-add.sh" "$DESTINO/guardia-autostash.py" "$DESTINO/commit-deshace-ajeno-aviso.py"; echo "exit=$?"
grep -c 'git-sin-pisarse' "$HOME/.claude/settings.json"

Salida esperada: tres líneas con los archivos, exit=0, y un 2 (las dos entradas registradas). Si sale No such file or directory, repite el paso 1; si sale 0, el paso 2.

b) El candado niega de verdad (repo de juguete, evento simulado, nada real se toca):

GUARDIA="$HOME/.claude/hooks/git-sin-pisarse/guardia-autostash.py"
R="$(mktemp -d)"
git -C "$R" init -q
printf 'uno\n' > "$R/a.txt"
git -C "$R" add a.txt
git -C "$R" -c user.name=prueba -c user.email=prueba@local commit -qm base
printf 'cambio de otra terminal\n' >> "$R/a.txt"
printf '{"tool_name":"Bash","session_id":"prueba","cwd":"%s","tool_input":{"command":"git stash"}}' "$R" \
  | GIT_SIN_PISARSE_TURNOS=/nonexistent python3 "$GUARDIA"; echo "EXIT=$?"
printf '{"tool_name":"Bash","session_id":"prueba","cwd":"%s","tool_input":{"command":"git stash list"}}' "$R" \
  | GIT_SIN_PISARSE_TURNOS=/nonexistent python3 "$GUARDIA"; echo "EXIT=$?"
rm -rf "$R"

Salida esperada: primero un mensaje [guardia-autostash] NEGADO: ... a.txt ... con EXIT=2; después, sin mensaje, EXIT=0 (git stash list no aparta nada).

c) Los tres bancos de prueba en verde (cada uno arma sus repos en una carpeta temporal):

P="$HOME/.claude/hooks/git-sin-pisarse/pruebas"
bash "$P/banco-git-safe-add.sh" | tail -1
bash "$P/banco-autostash.sh" | tail -1
bash "$P/banco-commit-deshace-ajeno.sh" | tail -1

Salida esperada: BANCO VERDE (12/12), TODOS EN VERDE (43 casos, 0 fallos) y bien=12 mal=0.

Cuando las tres partes den lo esperado, avísale a la persona que ya quedó y explícale en una línea qué gana con esto.


✅ ¿Cómo sé que funcionó?

Si tu asistente intenta git stash (o un pull con --autostash) mientras otra terminal tiene cambios sin guardar en el mismo proyecto, el candado lo frena y le dice la salida segura: guardar solo sus archivos con git add -- <archivos> y git commit -- <archivos>, o traer lo del servidor con git fetch y git merge origin/<rama> --no-edit.

Si un commit regresa un archivo a como estaba antes del commit de otra sesión de hace menos de 15 minutos, vas a ver un aviso ⚠️ COMMIT QUE DESHACE OTRO con el archivo y el commit afectado. Solo avisa: tú decides si fue a propósito.

Y si conectaste git_safe_add a tus scripts de guardado, el script ya no mete por su cuenta ningún borrado (salvo un movimiento cuyo contenido exacto ya está en otra ruta) ni lo que estaba a medias al arrancar, y cada borrado frenado queda anotado en el log. Lo que alguien ya había puesto en stage a mano sí entra al commit: revísalo en la lista que el bloque muestra antes de guardar. Eso es todo. 🌱

¿Quieres implementarla con acompañamiento?

Agenda una asesoría directa con JP y aterrízala en tu caso · desde $600 MXN.

Agendar una asesoría