El escenario es bastante habitual cuando trabajamos con GitHub y Pull Requests. Hemos terminado nuestro trabajo en una rama, hemos abierto una Pull Request y estamos esperando a que otro compañero la revise. Mientras tanto, otro desarrollador abre su propia Pull Request, la mergea contra master y modifica alguna de las mismas líneas que nosotros habíamos cambiado. Esto genera un conflicto en GitHub.¿Cómo resolverlo? Vamos a ver como resolver conflictos en GitHub usando rebase.
Cuando volvemos a nuestra Pull Request nos encontramos con algo como esto:

Vamos a ver como hacerlo en nuestra terminal
Situarse en la rama correcta
git checkout feature/WPA-23654
# Cambiado a rama 'feature/WPA-23654'
# Tu rama está actualizada con 'origin/feature/WPA-23654'.
Iniciamos el git pull en modo rebase
Podemos ejecutar directamente:
git pull --rebase origin masterEste comando obtiene los últimos cambios de master y vuelve a aplicar nuestros commits encima de ellos.
Conceptualmente tendremos algo parecido a esto antes del rebase:
A---B---C---D master
\
E---F feature/WPA-23654Después del rebase, Git intenta reconstruir nuestra rama así:
A---B---C---D master
\
E'---F' feature/WPA-23654Nuestros commits se vuelven a aplicar sobre el último estado de master.
Por este motivo los commits E y F pasan a ser nuevos commits E' y F', con hashes diferentes.
¿Qué es el modo rebase en git pull? ¿Cuándo usar git pull –rebase?
I think you should use
https://stackoverflow.com/questions/2472254/when-should-i-use-git-pull-rebasegit pull --rebasewhen collaborating with others on the same branch. You are in your work → commit → work → commit cycle, and when you decide to push your work your push is rejected, because there’s been parallel work on the same branch. At this point I always do apull --rebase. I do not use squash (to flatten commits), but I rebase to avoid the extra merge commits.
Git detecta el conflicto
(venv) ➜ security-management git:(feature/WPA-23654) ✗ git pull --rebase origin master
remote: Enumerating objects: 10, done.
remote: Counting objects: 100% (10/10), done.
remote: Compressing objects: 100% (4/4), done.
remote: Total 4 (delta 2), reused 0 (delta 0), pack-reused 0
Desempaquetando objetos: 100% (4/4), 1.38 KiB | 472.00 KiB/s, listo.
Desde github.com:Wallapop/security-management
* branch master -> FETCH_HEAD
99ca504..9b22c5e master -> origin/master
Auto-fusionando infrastructure/security-alerts/template.yaml
CONFLICTO (contenido): Conflicto de fusión en infrastructure/security-alerts/template.yaml
error: no se pudo aplicar 378c547... add permissions to the lambda role to perform puts in cloudwatch
ayuda: Resolve all conflicts manually, mark them as resolved with
ayuda: "git add/rm <conflicted_files>", then run "git rebase --continue".
ayuda: You can instead skip this commit: run "git rebase --skip".
ayuda: To abort and get back to the state before "git rebase", run "git rebase --abort".
No se pudo aplicar 378c547... add permissions to the lambda role to perform puts in cloudwatch
Git no puede decidir automáticamente cuál de los dos cambios debe conservar.
El proceso de rebase queda entonces detenido.
Entender el conflicto
<<<<<<< HEAD
Resource: '*'
=======
Resource: "*"
- Effect: "Allow"
Action:
- cloudwatch:Enable*
- cloudwatch:Put*
- cloudwatch:Set*
- cloudwatch:Describe*
- cloudwatch:List*
Resource: "*"
>>>>>>> 378c547 (add permissions to the lambda role to perform puts in cloudwatch)
Roles:
- !Ref SecurityAlertsRoleLos marcadores:
<<<<<<<
=======
>>>>>>>separan las dos versiones que Git no ha podido combinar automáticamente.
Durante un rebase hay que tener cierto cuidado al interpretar estas partes: Git está tomando nuestros commits y reproduciéndolos sobre la rama base.
Por tanto, lo importante no es simplemente escoger una parte u otra, sino decidir cómo debe quedar finalmente el fichero.
5. Resolver manualmente el conflicto
En nuestro caso queremos conservar los cambios existentes y añadir además los nuevos permisos de CloudWatch.
Eliminamos completamente los marcadores:
<<<<<<<
=======
>>>>>>>
y guardamos el fichero.
- Effect: "Allow"
Action:
- config:GetResourceConfigHistory
Resource: "*"
- Effect: "Allow"
Action:
- cloudwatch:Enable*
- cloudwatch:Put*
- cloudwatch:Set*
- cloudwatch:Describe*
- cloudwatch:List*
Resource: "*"
Roles:
- !Ref SecurityAlertsRoleMarcar el conflicto como resuelto
Si ejecutamos:
git statustodavía veremos algo parecido a:
Rutas no fusionadas:
(usa "git add <archivo>..." para marcar una resolución)
modificados por ambos:
infrastructure/security-alerts/template.yamlAñadimos el archivo corregido al staging:
git add infrastructure/security-alerts/template.yamlCon esto le estamos diciendo a Git:
He resuelto este conflicto y esta es la versión que quiero conservar.
Continuar el rebase
Ahora podemos continuar:
git rebase --continueDependiendo de nuestra configuración de Git, puede abrirse el editor mostrando el mensaje del commit que Git está volviendo a aplicar.
Normalmente podemos mantener el mensaje existente, guardar y cerrar el editor.
Git continuará procesando nuestros commits.
En este caso obtenemos:
[HEAD desacoplado e89e874]
add permissions to the lambda role to perform puts in cloudwatch
1 file changed, 11 insertions(+), 1 deletion(-)
Rebase aplicado satisfactoriamente y actualizado
refs/heads/feature/WPA-23654.Si hubiera más commits con conflictos, Git volvería a detenerse y repetiríamos el mismo proceso:
resolver conflicto
↓
git add <archivo>
↓
git rebase --continue
↓
siguiente commitHasta completar el rebase.
¿Y si queremos cancelar el rebase?
Si algo sale mal o queremos volver al estado inicial podemos ejecutar:
git rebase --abortGit devolverá nuestra rama al estado que tenía antes de iniciar el rebase.
Existe también:
git rebase --skippero hay que usarlo con mucha precaución porque hace que Git ignore el commit que está intentando aplicar.
Normalmente, ante un conflicto, lo que queremos es resolverlo y ejecutar:
git rebase --continueSubir nuevamente nuestra rama a GitHub
Aquí hay una diferencia importante respecto a un flujo normal.
Un rebase reescribe el historial de nuestra rama.
Los commits originales han sido sustituidos por nuevos commits con hashes diferentes.
Por ello un simple:
git pushnormalmente será rechazado.
Podríamos hacer un force push, pero en lugar de:
git push --forcees preferible utilizar:
git push --force-with-lease origin feature/WPA-23654--force-with-lease añade una protección importante.
Antes de sobrescribir la rama remota comprueba que esta sigue estando en el estado que esperamos. Si otro compañero ha subido nuevos commits a nuestra rama desde la última vez que la consultamos, Git rechazará el push en lugar de sobrescribir esos cambios accidentalmente.
Después del push, GitHub actualizará automáticamente nuestra Pull Request con el nuevo historial de la rama.
El conflicto debería desaparecer y la Pull Request volverá a estar preparada para ser revisada o mergeada.
Resumen
El proceso completo sería:
git checkout feature/WPA-23654
git fetch origin
git rebase origin/masterSi aparece un conflicto:
git status
# Editamos manualmente los archivos afectados
git add infrastructure/security-alerts/template.yaml
git rebase --continueRepetimos el proceso si aparecen nuevos conflictos.
Finalmente:
git push --force-with-lease origin feature/WPA-23654Y si queremos cancelar todo el proceso:
git rebase --abortRebase vs merge
También podríamos actualizar nuestra feature branch haciendo un merge de master.
Por ejemplo:
git fetch origin
git merge origin/masterEsto también resolvería el problema, pero introduciría un merge commit adicional en nuestra rama.
Con rebase, en cambio, reconstruimos nuestros commits sobre la versión más reciente de master, obteniendo un historial lineal:
master
|
A--B--C--D
\
E'--F'Este es precisamente uno de los motivos por los que se utiliza rebase habitualmente antes de actualizar una Pull Request.
No significa que rebase sea siempre mejor que merge: depende de la estrategia Git que utilice el equipo.
Lo importante es recordar que rebase reescribe el historial, por lo que debemos tener especial cuidado cuando trabajamos sobre ramas compartidas.
Y listo, conflictos resueltos
(venv) ➜ security-management git:(feature/WPA-23654) ✗ ggpush -f
Enumerando objetos: 81, listo.
Contando objetos: 100% (81/81), listo.
Compresión delta usando hasta 16 hilos
Comprimiendo objetos: 100% (67/67), listo.
Escribiendo objetos: 100% (67/67), 7.90 KiB | 2.63 MiB/s, listo.
Total 67 (delta 45), reusados 0 (delta 0), pack-reusados 0
remote: Resolving deltas: 100% (45/45), completed with 12 local objects.
To github.com:Wallapop/security-management.git
+ f60f7b6...19b0000 feature/WPA-23654 -> feature/WPA-23654 (forced update)
Links
- https://garrytrinder.github.io/2020/03/squashing-commits-using-interactive-rebase
- https://www.rubenortiz.es/tag/github/
¿Te ha ayudado este artículo?
☕ Invítame a un café