Git básico: commit, branch y merge
1. Descripción general
Git es una herramienta de control de versiones que casi todo proyecto usa, pero muchos principiantes solo recuerdan algunos comandos sin entender su esencia. Este artículo cubre tres conceptos fundamentales: commit (guardar una instantánea), branch (una rama de trabajo independiente) y merge (fusionar ramas). Después de leerlo, serás capaz de crear nuevas ramas con confianza, hacer commits correctamente, resolver conflictos al fusionar y saber cuándo usar merge en lugar de rebase.
2. Commit: guardar una instantánea
2.1. ¿Qué es un commit?
Cada commit es una “fotografía” del estado completo de los archivos en un momento específico, junto con un puntero al commit padre. Git no guarda diffs, sino instantáneas — esto hace que el branching y merging sean rápidos y económicos.
# Inicializar repositorio y hacer el primer commit
git init order-service
cd order-service
echo "# Order Service" > README.md
git add README.md
git commit -m "docs: agregar README para Order Service"
Explicación:
git initcrea un nuevo repositorio, genera el directorio oculto.git.git add README.mdlleva el archivo al staging area (área de buffer esperando commit).git commit -m "..."crea una instantánea con un mensaje que describe el cambio.
Verifica el historial:
git log --oneline
a1b2c3d docs: agregar README para Order Service
2.2. Escribir buenos mensajes de commit
Un commit debe responder a la pregunta “¿qué hace este cambio y por qué?”. Convención común (conventional commits):
feat: agregar API para crear pedidos
fix: corregir cálculo incorrecto del total al aplicar cupón
refactor: extraer OrderValidator de OrderService
Nota: Evita mensajes vagos como
"update"o"fix bug". Algunos meses después, al revisargit log, no sabrás qué arreglaba ese commit.
2.3. Hacer commit parcial con git add -p
Cuando un archivo tiene múltiples cambios no relacionados, no los commits todos juntos.
git add -p src/main/java/com/nonladev/OrderService.java
Git te preguntará qué “hunk” (porción de cambios) quieres poner en stage, permitiéndote dividirlos en commits más pequeños, fáciles de revisar y revertir si es necesario.
3. Branch: trabajar independientemente en múltiples ramas
3.1. Crear y cambiar de branch
Una rama es solo un puntero ligero que apunta a un commit específico. Crear una rama nueva no copia todo el código, solo crea otro puntero.
# Crear una nueva rama y cambiar a ella
git branch feature/add-payment
git checkout feature/add-payment
# O combinar los dos comandos anteriores en uno (Git ≥ 2.23)
git switch -c feature/add-payment
Explicación:
git branch <nombre>crea la rama pero permaneces en la rama anterior.git checkout <nombre>ogit switch <nombre>mueveHEADa esa rama.-cengit switches un atajo para crear-y-cambiar al mismo tiempo.
3.2. Ver la lista de ramas
git branch -v
* feature/add-payment d4e5f6a agregar PaymentService skeleton
main a1b2c3d docs: agregar README para Order Service
El asterisco * indica la rama actual (donde está HEAD).
3.3. Nombrar ramas siguiendo convenciones
Nombrar ramas claramente ayuda a todo el equipo a saber qué hace esa rama:
feature/add-payment # agregar una nueva característica
fix/order-total-wrong # corrección de error
refactor/extract-validator
hotfix/prod-null-pointer
3.4. Eliminar rama después de terminar
# Eliminar rama local que ya fue fusionada
git branch -d feature/add-payment
# Eliminar rama no fusionada (fuerza eliminar, cuidado de perder commits)
git branch -D feature/add-payment
-d (minúscula) es más seguro porque Git te advertirá si la rama no se ha fusionado en ningún lugar.
4. Merge: fusionar ramas
4.1. Merge de avance rápido (Fast-forward)
Si main no tiene nuevos commits desde que creaste la rama, Git solo mueve el puntero main al último commit de la rama feature — se llama fast-forward.
git switch main
git merge feature/add-payment
Updating a1b2c3d..d4e5f6a
Fast-forward
PaymentService.java | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
No se crea un nuevo commit de fusión, el historial sigue siendo una línea recta.
4.2. Commit de fusión (3-way merge)
Si ambas ramas tienen commits propios desde el punto de divergencia, Git crea un merge commit con dos padres que fusiona el contenido de ambas ramas.
git switch main
git commit -am "fix: corregir validación de cantidad de items" # main tiene nuevo commit
git switch feature/add-payment
git switch main
git merge feature/add-payment
Merge made by the 'recursive' strategy.
PaymentService.java | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
Git crea automáticamente un commit de fusión con un mensaje predeterminado Merge branch 'feature/add-payment'.
4.3. Resolver conflictos en merge
El conflicto ocurre cuando dos ramas modifican la misma línea de código. Ejemplo en la clase OrderService:
// En main
public class OrderService {
public BigDecimal calculateTotal(Order order) {
return order.getSubtotal().multiply(BigDecimal.valueOf(1.1)); // agregar impuesto 10%
}
}
// En feature/add-payment
public class OrderService {
public BigDecimal calculateTotal(Order order) {
return order.getSubtotal().subtract(order.getDiscount()); // restar descuento
}
}
Al fusionar, Git no puede decidir automáticamente cuál mantener, el archivo mostrará marcadores:
public class OrderService {
public BigDecimal calculateTotal(Order order) {
<<<<<<< HEAD
return order.getSubtotal().multiply(BigDecimal.valueOf(1.1));
=======
return order.getSubtotal().subtract(order.getDiscount());
>>>>>>> feature/add-payment
}
}
Debes editar manualmente para combinar la lógica correctamente (por ejemplo: restar el descuento primero, luego sumar el impuesto), elimina los marcadores <<<<<<<, =======, >>>>>>>, y luego:
git add OrderService.java
git commit
Nota: El commit después de resolver el conflicto no necesita
-m, Git abrirá un editor con un mensaje predeterminado que indica claramente que es un commit de fusión — mantén ese mensaje a menos que quieras agregar notas adicionales.
4.4. Merge vs Rebase
| Criterio | merge |
rebase |
|---|---|---|
| Historial | Mantiene el historial, con commit de fusión | Reescribe el historial, rama plana |
| Seguridad | No modifica commits anteriores | Modifica commits anteriores (cambia hash) |
| Cuándo usar | Rama compartida públicamente (main, develop) |
Rama personal, quieres historial limpio antes de crear PR |
# Rebasar rama feature sobre el main más reciente
git switch feature/add-payment
git rebase main
Nota: No hagas
rebasede una rama que ya pusheaste y otros están usando — causarás conflictos masivos cuando intenten hacer pull de nuevo.
5. Probar un escenario de merge con JUnit
Aunque Git no es código Java, puedes escribir tests para verificar que la lógica de negocio después de fusionar dos ramas no se rompe — esta es la razón por la que CI siempre ejecuta tests en el commit de fusión. Ejemplo de OrderService después de resolver el conflicto en la sección 4.3:
public class OrderService {
public BigDecimal calculateTotal(Order order) {
BigDecimal afterDiscount = order.getSubtotal().subtract(order.getDiscount());
return afterDiscount.multiply(BigDecimal.valueOf(1.1));
}
}
class OrderServiceTest {
private final OrderService orderService = new OrderService();
@Test
void shouldApplyDiscountThenTax() {
Order order = new Order(
BigDecimal.valueOf(100), // subtotal
BigDecimal.valueOf(10) // descuento
);
BigDecimal total = orderService.calculateTotal(order);
assertThat(total).isEqualByComparingTo(BigDecimal.valueOf(99.0));
}
}
Explicación: subtotal 100 menos descuento 10 quedan 90, multiplicado por impuesto 10% resulta 99. Este test es la “red de seguridad” que asegura que fusionar las dos lógicas (impuesto y descuento) no se anulen mutuamente.
✓ shouldApplyDiscountThenTax()
6. Flujo de trabajo sugerido para equipos pequeños
- Siempre crea una rama nueva desde
mainpara cada tarea:git switch -c feature/xxx. - Commits pequeños, frecuentes, con mensajes claros.
- Antes de crear un Pull Request, actualiza la rama con el
mainmás reciente:git switch feature/add-payment git fetch origin git merge origin/main - Resuelve conflictos (si los hay) localmente y vuelve a ejecutar los tests.
- Haz push y crea un PR, deja que CI ejecute tests en el commit de fusión antes de fusionar en
main.
Nota: No hagas commits directamente en
maincuando trabajes en equipo — siempre usa rama + PR para que haya revisión y CI verifique primero.
7. Errores comunes
- Olvidar
git addantes de hacer commit: Git dicenothing to commit, working tree cleanaunque hayas modificado archivos — porque los archivos no están en stage. - Fusionar la rama equivocada: siempre ejecuta
git statusygit branchpara verificar dónde estás antes de hacermerge. - Marcadores de conflicto olvidados en el código: olvidar eliminar
<<<<<<<hará que el código no compile — siempre compila/prueba inmediatamente después de resolver conflictos. - Hacer force push en rama compartida:
git push --forcedespués de rebasar una rama compartida borrará el historial de tus compañeros. Solo usa--force-with-leasey solo en ramas personales.
8. Conclusión
Commit, branch y merge son las tres operaciones que usas diariamente con Git. Dominar commit ayuda a mantener un historial de código claro, las ramas ayudan a trabajar en paralelo sin interferirse, y entender merge (junto con resolver conflictos) te da confianza al fusionar código de múltiples personas. Practica commits pequeños, usa convenciones de nombres para ramas, y siempre ejecuta tests antes de fusionar en main.