··
Si llevas años haciendo QA y Git te sigue pareciendo un campo de minas, esta guía recoge todo lo que hay que saber para automatizar, leer diffs, revisar PRs y no romper el repo del equipo. Flujos reales, comandos con ejemplos y errores que he visto (y cometido).

Llevo años viendo QAs que manejan Postman como ninjas, conocen cada opción avanzada de Playwright y detectan un bug por el reflejo del navegador, pero se quedan bloqueados cuando un desarrollador les dice “hazme un pull y revisa mi rama”. Git sigue siendo un punto ciego del perfil QA en España, sobre todo en gente que viene del manual y ha empezado a automatizar.
Esta guía es lo que me hubiera gustado que alguien me pusiera delante cuando me tocó empezar a tocar repos de verdad. Nada de teoría de grafos ni historia del kernel de Linux. Solo los comandos y conceptos que vas a usar cada día cuando escribas scripts de prueba, automatices E2E o tengas que revisar un PR de backend desde QA.
Lo primero que cambió todo para mí fue dejar de ver Git como una herramienta de “guardar versiones”. Git guarda cambios en forma de commits, y cada commit es una foto del proyecto entero en un momento concreto. Lo importante es que tu trabajo vive en cuatro zonas distintas, y los comandos de Git solo mueven cosas entre esas zonas.
Working directory. Los ficheros que ves en el IDE, lo que editas a mano.
Staging area. Los cambios que ya has seleccionado para formar parte del próximo commit.
Repositorio local. El historial de commits en tu máquina, dentro de la carpeta .git.
Repositorio remoto. GitHub, GitLab, Bitbucket o lo que uséis en el equipo.
Cuando haces git add mueves algo del working directory al staging. Con git commit lo metes en el historial local. Con git push lo subes al remoto. Entender esto resuelve el 80 % de los “no sé por qué no me aparece”.
La analogía que mejor me ha funcionado al explicarlo a gente de QA, el working directory es como un test que estás escribiendo, el staging es como haberlo marcado ya para la suite, el commit es haberlo ejecutado y guardado el resultado, y el push es haberlo publicado en el dashboard del equipo.
Esto se hace una sola vez en tu máquina. Identifica quién eres tú en los commits:
git config --global user.name "Tu Nombre"
git config --global user.email "[email protected]"
git config --global init.defaultBranch main
git config --global pull.rebase falseLa última línea es opinable, pero si empiezas en Git te recomiendo pull.rebase false, que hace merge por defecto al hacer pull. Evitas sorpresas hasta que entiendas rebase de verdad. Más abajo lo explico.
Si vas a trabajar con GitHub y varios proyectos, configura una clave SSH y olvídate de meter contraseñas cada vez que hagas push:
ssh-keygen -t ed25519 -C "[email protected]"
cat ~/.ssh/id_ed25519.pub # pegas el contenido en GitHub → Settings → SSH keysImagina que te acaban de dar acceso a un repo de automatización. Este es el ciclo que vas a repetir cien veces por semana.
# 1. Descargar el repo por primera vez
git clone [email protected]:empresa/tests-e2e.git
cd tests-e2e
# 2. Ver en qué rama estás y qué ha tocado alguien
git status
git log --oneline -10
# 3. Actualizar tu copia local antes de empezar
git pull
# 4. Crear una rama nueva para tu tarea
git checkout -b fix/login-flaky
# 5. Editar código, correr tests, lo de siempre
# ...
# 6. Ver qué has cambiado
git status
git diff
# 7. Seleccionar lo que va al commit
git add tests/auth/login.spec.ts
# o todo a la vez:
git add .
# 8. Hacer el commit con un mensaje decente
git commit -m "fix(auth): esperar a que el token llegue antes de navegar"
# 9. Subir la rama al remoto
git push -u origin fix/login-flakyLa primera vez que empujas una rama nueva necesitas -u origin nombre-rama. Las siguientes basta con git push.
Si me tuviera que quedar con una sola costumbre, sería esta, nunca trabajes directamente en main. Cada cambio, por pequeño que sea, va en su propia rama. Un typo en el README va en una rama. Una suite de tests nueva va en una rama. Un arreglo de un selector CSS va en una rama.
Tiene tres ventajas que vas a notar la primera semana.
Puedes cambiar de tarea sin perder nada. git checkout main y estás limpio.
Los pull requests salen pequeños y se revisan rápido.
Si algo sale mal, borras la rama y punto. Nadie más ha visto tu caos.
Convención que uso para nombrar ramas en proyectos de testing:
test/ para añadir casos nuevos, ej. test/checkout-tarjeta-3ds
fix/ para arreglar tests rotos o flaky, ej. fix/login-timeout-ci
refactor/ para reorganizar sin cambiar comportamiento
chore/ para dependencias, configuración y CI
Un QA que lee bien un diff ve cosas que nadie más en el equipo ve. Porque el diff es donde viven los bugs antes de llegar a producción. Si te toca revisar un PR, mirar el diff te dice qué cambió, qué testear y qué casos de regresión plantear.
En la línea de comandos:
git diff # cambios en working directory
git diff --staged # cambios ya añadidos al staging
git diff main...fix/login # diferencia entre dos ramas
git log -p archivo.ts # historial con el diff de cada commitEn la interfaz de GitHub o GitLab, el diff sale con colores. Verde es lo añadido, rojo lo borrado. Lo que yo miro como QA cuando reviso un PR:
¿Hay tests nuevos para el cambio? Si no, bandera roja.
¿El cambio toca condiciones if, bucles o early returns? Son los sitios donde mejor se esconden bugs.
¿Han tocado código de manejo de errores? Suele estar mal testado.
¿Hay cambios en migraciones, schemas o contratos de API? Ahí hay que probar hacia atrás y hacia delante.
¿Ha crecido la complejidad sin que haya tests nuevos? También bandera roja.
Son las dos formas de traer cambios de otra rama a la tuya. La diferencia en una frase, merge preserva la historia real, rebase la reescribe para que quede lineal.
# Merge, crea un commit extra que une las dos ramas
git checkout fix/login
git merge main
# Rebase, apila tus commits encima de main como si los acabaras de hacer
git checkout fix/login
git rebase mainReglas prácticas que aplico yo y que no me han fallado:
Rebase solo en ramas que aún no has empujado, o que solo tú usas. Si reescribes la historia de una rama compartida, rompes a tu equipo.
Para integrar tu rama en main, en proyectos pequeños da igual uno u otro. En proyectos grandes suele haber política del equipo, así que pregunta antes de inventar.
Si acabas de empezar con Git, quédate con merge hasta que entiendas rebase bien. No hay prisa.
Vas a tener conflictos. No es un fallo tuyo, es lo normal en equipos de más de una persona. Un conflicto ocurre cuando dos personas han tocado las mismas líneas del mismo fichero en ramas distintas, y Git no sabe cuál ganar.
Cuando haces git pull o git merge y aparece un conflicto, verás algo así dentro del fichero:
<<<<<<< HEAD
await page.getByRole('button', { name: 'Entrar' }).click();
=======
await page.getByTestId('login-submit').click();
>>>>>>> mainLo de arriba es tu versión, lo de abajo es lo que llega de main. Tu trabajo es decidir qué se queda. Editas el fichero, borras los marcadores (<<<, ===, >>>), dejas el código final, y cierras el conflicto:
git add fichero-conflictivo.ts
git commit # Git ya rellena el mensaje por tiTres errores que he visto mil veces en gente nueva:
Dejar los marcadores (<<<) dentro del código y hacer commit. El test ni compila.
Quedarse siempre con su versión sin entender qué hacía la otra. Pierdes cambios del compañero sin saberlo.
Hacer git merge --abort en pánico por miedo a romper algo. Muchas veces aguantar y resolver es más rápido que tirar atrás.
Aquí es donde un QA bien formado marca la diferencia. El .gitignore decide qué Git ignora al hacer git add. Un repo de testing sin .gitignore bien configurado acaba lleno de basura y, lo peor, con secretos publicados sin querer.
Plantilla mínima para proyectos con Playwright, pytest, Cypress o similares:
# Dependencias
node_modules/
.venv/
__pycache__/
# Artefactos de ejecución
test-results/
playwright-report/
coverage/
videos/
screenshots/
*.log
# Entornos locales y secretos
.env
.env.local
.env.*.local
secrets.json
*.pem
*.key
# IDE
.idea/
.vscode/
*.swp
.DS_StoreSobre los secretos, dos normas que no negocio. La primera, nunca commitear un .env con valores reales. La segunda, si por error commiteas uno, considera la credencial comprometida y rotátala. Borrarlo en el siguiente commit no sirve, porque el historial sigue ahí para siempre. Esto lo he tratado con detalle en la entrada sobre variables de entorno en scripts E2E.
Esta es la sección que más me habría pedido mi yo del pasado. Git casi nunca borra nada de verdad. Casi todo se puede recuperar si sabes dónde mirar.
git restore archivo.ts
# o antes de Git 2.23:
git checkout -- archivo.tsgit add y quiero quitarlo del staginggit restore --staged archivo.ts
# equivalente antiguo: git reset HEAD archivo.tsgit commit --amend -m "mensaje corregido"git add el-fichero.ts
git commit --amend --no-editgit reset HEAD~1 # deshace el commit, deja los cambios sin stageargit revert <hash-del-commit>revert crea un commit nuevo que anula los cambios del anterior. Nunca reescribe historia, así que es seguro en ramas compartidas. Es la única forma limpia de tirar atrás algo que ya vio tu equipo.
git stash # guarda los cambios en un "cajón"
git checkout main
# ...haces lo que tengas que hacer...
git checkout fix/login
git stash pop # recupera los cambiosgit reflogEl reflog es tu red de seguridad. Guarda cada movimiento de HEAD durante unos 90 días por defecto. Si has hecho un reset muy agresivo o has borrado una rama, casi seguro que puedes recuperar el trabajo desde aquí. Lo primero que hago cuando alguien me dice “he perdido horas de código” es abrir el reflog.
Los tags marcan un commit concreto con un nombre estable, típicamente una versión (v1.2.0, release-2026-04-01). Son la herramienta para coordinar el equipo de QA con el de release:
git tag -a v1.2.0 -m "Release de abril"
git push origin v1.2.0
git tag # listar tags locales
git checkout v1.2.0 # ir a ese punto del historial (modo detached)En QA los uso para dos cosas. Primero, para fijar la versión exacta que se desplegó en un entorno y poder reproducir bugs con el código correcto. Segundo, como disparador de pipelines. Si tu suite se ejecuta al publicar un tag, tienes un tren de release bien definido.
Aviso de seguridad que no se menciona suficiente, los tags son mutables por defecto. Alguien con permisos puede moverlos. En GitHub Actions y en Dockerfiles lo correcto es fijar por SHA completo del commit, no por tag. Esto lo desarrollé en el post sobre ataques de cadena de suministro en Dockerfiles.
Los hooks son scripts que Git ejecuta en momentos concretos del ciclo. El más útil para QA es pre-commit, que corre antes de registrar el commit y puede bloquearlo si algo falla. Con herramientas como pre-commit o husky se configura en minutos.
Lo que meto yo en un pre-commit decente para un repo de tests:
Linter (ESLint, ruff, black) sobre los ficheros tocados.
Formatter automático (prettier, black).
Tipado si aplica (tsc, mypy).
Búsqueda de secretos típicos (detect-secrets, gitleaks).
Tests unitarios rápidos, los que tardan menos de cinco segundos.
Regla importante, los hooks no sustituyen al CI. Un hook se puede saltar con --no-verify, así que en CI vuelves a validar todo. El hook es una primera barrera para detectar fallos antes de gastar minutos en un pipeline remoto.
Lista no exhaustiva, ordenada por dolor de cabeza causado.
Push --force a una rama compartida. Borras el trabajo de otros sin avisar. Si tienes que hacerlo, usa --force-with-lease, que solo sobrescribe si no hay cambios nuevos que no hayas visto.
Commitear el .env. Ver sección de .gitignore. Rota el secreto, no lo disimules.
Commits gigantes tipo “cambios varios”. Imposibles de revisar, imposibles de revertir con precisión. Divide.
No tirar de git pull antes de empezar. Acabas resolviendo conflictos que no tocaban.
Pelearse con la línea de comandos sin entender lo que hace. Si no sabes qué va a hacer un comando, haz primero git status y git log. En muchos comandos existe la bandera --dry-run.
Confiar solo en la UI de GitHub o del IDE. Cuando algo se tuerce, la CLI es la única que te dice la verdad.
Los comandos que, si me robaran el cerebro, querría que estuvieran tatuados en el brazo:
# Día a día
git status
git diff
git add .
git commit -m "mensaje"
git push
# Ramas
git checkout -b fix/algo
git checkout main
git branch -d fix/algo
# Sincronizar con el remoto
git pull
git fetch
git push -u origin nombre-rama
# Historia
git log --oneline --graph --all -20
git blame archivo.ts
# Rescate
git stash
git stash pop
git reflog
git revert <hash>
# Limpiar cosas (con cuidado)
git clean -fd # borra ficheros no trackeados
git reset --hard # tira TODOS los cambios localesGit es un tema sin fondo. Una vez te manejes con lo anterior, estos son los siguientes pasos que recomiendo para un perfil de QA que automatiza.
Aprender git bisect para encontrar qué commit introdujo un bug. Es una búsqueda binaria sobre el historial. Cuando le pillas el truco ya no vuelves atrás.
Entender bien el rebase interactivo (git rebase -i) para limpiar commits antes de abrir un PR.
Mirar cómo se usan los tags y las releases en el CI del equipo. Si automatizas en Playwright o en pipelines de self-healing, tu suite suele engancharse a eventos de Git.
Leer el historial de un proyecto grande con git log. Aprendes más de la cultura del equipo en una hora que con meses de reuniones.
Si te ha servido, pásaselo al siguiente QA que veas intentando sobrevivir con “el botón de la izquierda” de SourceTree sin saber qué hace por debajo. Git no es mágico, es solo mecánico, y una vez entiendes la mecánica dejas de tenerle miedo.

Jose, autor del blog
QA Engineer. Escribo en voz alta sobre automatización, IA y arquitectura de software. Si algo te ha servido, escríbeme y cuéntamelo.
¿Qué te ha parecido? ¿Qué añadirías? Cada comentario afina la siguiente entrada.
Si esto te ha gustado
Los scripts E2E necesitan datos sensibles —tokens de API, credenciales, URLs privadas— sin que aparezcan en el código. En JMO Labs hemos añadido variables de script con modo privado: se inyectan automáticamente, se enmascaran en los logs y se acceden con una sintaxis limpia.

Los tests E2E se rompen con cada cambio de interfaz. En JMO Labs construimos un pipeline de 5 fases con IA que planifica, ejecuta, repara selectores, diagnostica fallos y verifica resultados de forma autónoma. La caché de selectores hace que cada ejecución sea más rápida que la anterior.

Playwright no es solo para tests E2E. En JMO Labs lo usamos como motor completo: 9 fases de comprobación, localizador de 9 estrategias con self-healing, grabación de vídeo, testing responsive con viewports reales y accesibilidad con axe-core.