Git en la práctica
Los comandos que se preguntan: crear y clonar un repositorio, el día a día, las ramas, merge frente a rebase, la sincronización con el remoto y las tres formas de deshacer.
Crear el repositorio y configurarlo
Un repositorio local se puede obtener de dos formas: creándolo desde cero sobre un directorio propio, o clonando uno que ya exista. Clonar trae el proyecto entero con toda su historia, no solo la última versión.
# Opción A: crear un repositorio nuevo sobre un directorio propio
cd llegandoalcorte-temario
git init
# Opción B: clonar uno que ya existe (trae toda la historia)
git clone https://github.com/llegandoalcorte/temario-tai.git
# Quién firma los commits de esta máquina
git config --global user.name "Diego"
git config --global user.email "admin@llegandoalcorte.com"git init crea un directorio oculto llamado .git dentro del proyecto. Ahí vive todo: la metainformación, los objetos (commits, árboles y ficheros), las referencias a ramas y etiquetas y el índice o área de preparación. Si se borra ese directorio, se pierde la historia y queda solo la copia de ficheros del disco.
Las ramas de Git no son directorios en el disco: son referencias internas dentro de .git que apuntan a un commit. Por eso crear una rama es instantáneo, mientras que en Subversion equivale a copiar un directorio entero.
El fichero .gitignore enumera lo que Git debe ignorar y no subir nunca: resultados de compilación, dependencias descargables, ficheros temporales del editor y, sobre todo, credenciales. En un proyecto Java se ignoran los .class porque se pueden regenerar a partir de los .java; en uno de Node, el directorio de dependencias.
Para el examen
Qué es una rama: una referencia dentro de .git que apunta a un commit
Lo que NO es: un directorio
Consecuencia: crearlas es instantáneo
Los comandos del día a día
El ciclo básico se repite decenas de veces al día: mirar cómo está el directorio de trabajo, revisar qué se ha cambiado, seleccionar lo que entra en el commit, confirmar y consultar la historia.
git status # en qué rama estoy y qué falta por añadir o confirmar
git diff # qué he cambiado y todavía no he preparado
git add tema-9.ts # preparo un fichero concreto
git add . # preparo todo lo del directorio actual
git commit -m "Añade el test del subtema de pruebas"
git log # la historia completa, del más reciente al primero
git log --graph --oneline --all # la misma historia, resumida y con las ramas dibujadas
git blame tema-9.ts # quién escribió cada línea y en qué commitgit log recorre la historia hacia atrás partiendo de HEAD. HEAD es un puntero al commit en el que estás situado ahora mismo, normalmente el último de tu rama actual; desde él se llega al anterior, y así sucesivamente hasta el primer commit del proyecto.
Cuidado con la errata que circula por muchos apuntes: la opción para ver cada commit en una sola línea es --oneline, no «--online». No existe ninguna opción de Git que se llame así.
git tag crea etiquetas: nombres estables para un commit concreto, que se usan para marcar una versión publicada. A diferencia de una rama, una etiqueta no se mueve: siempre señalará al mismo commit.
Para el examen
Ver cada commit en una línea: --oneline
Errata que circula: «--online», que no existe
Ramas
Una rama es una línea de desarrollo paralela. Se crea para trabajar en algo (un correctivo urgente, una funcionalidad nueva) sin molestar al resto del equipo ni arriesgar la línea principal, y cuando el trabajo está listo se incorpora de vuelta.
git branch # lista las ramas; la actual sale marcada
git branch preguntas-falladas # crea la rama, pero NO se mueve a ella
git checkout preguntas-falladas # se mueve a la rama ya creada
git checkout -b calendario-repasos # crea la rama y se mueve en un solo paso
# Desde Git 2.23 existe un comando específico para cambiar de rama
git switch calendario-repasos
git switch -c calendario-repasos # equivale a checkout -bTodo repositorio arranca con una rama inicial. Históricamente se llamaba master, y sigue siendo el nombre por defecto en muchas instalaciones, pero GitHub y otras plataformas usan hoy main; en Subversion el equivalente se llama trunk. Es una convención, no una obligación: el nombre se puede cambiar con git branch -m nombre_viejo nombre_nuevo.
git branch crea la rama pero deja donde estabas; git checkout cambia de rama. Es la pareja que más se pregunta, y por eso existe el atajo git checkout -b, que hace las dos cosas.
git checkout tiene un segundo uso que conviene no mezclar con el anterior: si se le da el nombre de un fichero en vez del de una rama, descarta los cambios hechos sobre ese fichero y lo devuelve al estado del último commit. Desde Git 2.23 esa función tiene su propio comando, git restore, precisamente para deshacer la ambigüedad.
Para el examen
git branch: crea la rama, pero no cambia de sitio
git checkout: cambia de rama
git checkout -b: hace las dos cosas
Merge frente a rebase
Los dos comandos sirven para lo mismo: incorporar a tu rama el trabajo hecho en otra. La diferencia está en qué le hacen a la historia, y esa diferencia es una pregunta clásica de examen.
git merge conserva la historia tal como ocurrió: crea un commit de fusión con dos padres, de modo que en el registro se ve que hubo dos líneas paralelas y en qué punto se juntaron. git rebase reescribe: coge los commits de tu rama y los vuelve a aplicar uno a uno encima de la punta de la otra rama, como si los hubieras escrito después. El resultado es una historia lineal, más fácil de leer, pero con commits nuevos y por tanto con identificadores distintos.
# Fusionar: primero me sitúo en la rama que va a RECIBIR los cambios
git checkout main
git merge calendario-repasos
# Reescribir: me sitúo en la rama que va a MOVERSE
git checkout calendario-repasos
git rebase main
# Rebase interactivo: agrupar los 3 últimos commits en uno solo
git rebase -i HEAD~3| Aspecto | merge | rebase |
|---|---|---|
| Efecto sobre la historia | La conserva; añade un commit de fusión | La reescribe; los commits se recrean con otro identificador |
| Aspecto del registro | Ramificado, se ve que hubo trabajo en paralelo | Lineal, como si todo se hubiera hecho en fila |
| Dónde te sitúas antes | En la rama que recibe los cambios | En la rama que se va a mover |
| Riesgo | Ninguno; solo suma | No debe usarse sobre commits ya publicados |
La regla de oro: no se reescribe historia que ya han descargado otros. Rebase, amend y reset son seguros mientras el commit siga solo en tu máquina; después obligan a los demás a arreglar su copia.
Dentro del merge hay dos casos. Si la rama que recibe no ha avanzado desde que te separaste, Git resuelve la fusión moviendo simplemente el puntero hacia delante: es el fast forward, y no genera ningún commit de fusión. Con la opción --no-ff se fuerza a crear el commit de fusión igualmente, para que en el registro quede constancia de que existió una rama. Y si las dos ramas han tocado las mismas líneas del mismo fichero, Git no puede decidir por su cuenta: marca un conflicto y espera a que una persona elija qué versión vale.
El rebase interactivo, con la opción -i, se detiene y pide confirmación antes de aplicar cada commit, lo que permite reordenarlos, cambiar sus mensajes o fundir varios en uno. Se usa para limpiar una serie de commits pequeños antes de proponerlos al proyecto principal.
Para el examen
Merge: conserva la historia real
Rebase: la reescribe, dejándola lineal
Regla de oro: no se reescribe historia que ya han descargado otros
Sincronizar con el repositorio remoto
git remote -v # a qué repositorios remotos estoy conectado
git fetch # descargo lo nuevo del remoto, sin tocar mi rama
git fetch --all # lo mismo para todas las ramas del repositorio
git pull # fetch + merge: descargo y fusiono en mi rama actual
git pull origin main # indicando remoto y rama de forma explícita
git push origin main # subo mis commits locales al remotoLa diferencia entre fetch y pull es la pregunta habitual, y suele explicarse mal. Los dos descargan: git fetch trae al repositorio local todos los objetos nuevos que haya en el remoto, commits y ficheros incluidos, y los deja en las ramas de seguimiento (origin/main). Lo que fetch NO hace es tocar tu directorio de trabajo ni fusionar nada en tu rama. git pull hace las dos cosas: primero el fetch y después el merge sobre tu rama actual.
fetch descarga y no cambia tu trabajo; pull descarga y además fusiona. Por eso fetch es seguro en cualquier momento y pull conviene hacerlo con el directorio de trabajo limpio.
El aviso de que «tu rama está adelantada» significa que tienes commits en local que el remoto todavía no tiene, y se resuelve con push. Si Git rechaza el push es al revés: el remoto ha avanzado por su cuenta y hay que traerse antes esos cambios con pull o fetch.
Para el examen
fetch: descarga y no toca tu trabajo
pull: descarga y además fusiona
Cuándo usar cada uno: fetch siempre es seguro; pull, con el directorio limpio
Deshacer: reset, revert y amend
Hay dos filosofías para deshacer. Una borra el commit de la historia y hace como si nunca hubiera existido; la otra deja el commit donde está y añade otro que lo anula. La primera solo es aceptable si nadie más ha visto ese commit.
git reset retrocede el puntero de la rama hasta un commit anterior, indicado con HEAD, con HEAD~2 o directamente con su identificador. Lo que cambia entre sus tres variantes es hasta qué zona arrastra los cambios que quedan huérfanos.
| Variante | Qué deshace | Dónde quedan los cambios |
|---|---|---|
| git reset --soft | Solo el commit | En el área de preparación, listos para volver a confirmarse |
| git reset --mixed (por defecto) | El commit y la preparación (el add) | En el directorio de trabajo, sin preparar |
| git reset --hard | El commit, la preparación y los ficheros | En ninguna parte: se pierden |
--hard es la única variante destructiva y no avisa. Las otras dos solo mueven los cambios hacia atrás por las zonas de trabajo, así que siempre se pueden recuperar.
git reset --soft HEAD~1 # deshago el último commit, conservo el trabajo preparado
git reset --mixed HEAD~1 # deshago el commit y el add; el trabajo sigue en disco
git reset --hard HEAD~1 # deshago el commit y BORRO el trabajo
git commit --amend # rehago el último commit (mensaje o contenido)
git revert 8f3c21a # creo un commit nuevo que anula lo que hizo ese commitgit revert es la alternativa segura para lo ya publicado: no borra nada de la historia, sino que introduce un commit nuevo cuyo contenido es exactamente el inverso del que se quiere anular. Al final la historia es más larga, pero nadie tiene que arreglar su copia.
git commit --amend sirve para corregir el último commit: cambiar su mensaje o añadirle algo que se olvidó, en vez de dejar en la historia un commit adicional que no aporta nada. Cuidado con una imprecisión frecuente: amend no modifica el commit anterior, construye uno nuevo y lo pone en su lugar, así que el identificador cambia y también cuenta como reescribir historia.
Para el examen
reset --hard: la única variante destructiva, y no avisa
revert: crea un commit que deshace
amend: rehace el último commit
Flujos de trabajo y herramientas gráficas
Git no impone cómo organizar las ramas, así que los equipos adoptan convenios. El más citado es Gitflow, que define una rama permanente para lo publicado y otra para el desarrollo en curso, y ramas temporales para cada funcionalidad, cada preparación de versión y cada arreglo urgente.
| Rama de Gitflow | Para qué sirve |
|---|---|
| main o master | Refleja siempre lo que está en producción |
| develop | Integra el trabajo terminado a la espera de la próxima versión |
| feature/… | Una por cada funcionalidad nueva; nace de develop y vuelve a develop |
| release/… | Preparación de una versión: solo correcciones y ajustes finales |
| hotfix/… | Arreglo urgente que sale de main y se incorpora a main y a develop |
Frente a Gitflow, muchos equipos prefieren hoy flujos más simples, con una sola rama principal y ramas cortas de funcionalidad que se integran mediante pull request, precisamente porque encaja mejor con la integración continua: cuanto más viva una rama separada, más doloroso es fusionarla.
Sobre las herramientas gráficas: Sourcetree es un cliente gratuito de Atlassian, la misma empresa de Jira y Bitbucket, y sirve para trabajar con Git sin escribir comandos. No aporta nada que la línea de comandos no tenga, pero ayuda a visualizar el grafo de ramas.
Para el examen
main: lo publicado
develop: lo integrado
Ramas temporales de Gitflow: feature, release y hotfix