Tabla de Contenidos

6. Control de Versiones Avanzado

En este tema vamos a ver sobre todo a tratar con ramas, merges y conflictos.

Mas información:

Necesidad de Ramas

El siguiente esquema muestra el sistema de ramas que se va a usar en clase. Mas información en A successful Git branching model

Ejemplo de Ramas

Ramas a crear:

En la rama de funcionalidad se pueden hacer todos los commits que queramos ya que luego se van a unificar en uno solo. Es lo que llamamos "microcommits". Es así ya que son commits parciales que se hacen para acabar la tarea pero finalmente en la rama develop queremos que sean un solo commit.

Pasos a seguir para hacer una modificación.

Usando Ramas

Veamos ahora como se trabaja con ramas

  git branch nuevaRama
  git switch nuevaRama
  git push --set-upstream origin nuevaRama
  git push -u             origin nuevaRama



  git fetch --prune   
  git switch nuevaRama

  • fetch:Se pone --prune , para que borre las referencias locales de ramas remotas que ya no están.
  • switch: Por defecto al cambiar a la nueva rama ya se hace un --track a origin/nuevaRama



  git branch -d miRama
  git branch --delete miRama



  git branch -d -f      miRama
  git branch --delete --force miRama
  git branch -D         miRama



  git push origin --delete miRama



Merge

El merge es unir los commit de una rama en otra. Hay muchas formas de hacer un merge , veamos algunas de ellas.

Comando Características Cuando usarlo
git rebase rama Pone los commits en la nueva rama siempre después de los que ya hay, es decir que los reordena Poner los commits de origin/develop en nuestra develop porque hay commits distintos en cada rama pero del mismo commit origen
git merge --squash rama Junta todos los "microcommits" de la rama en uno solo en la rama destino. Es necesario acto seguido el commit De una rama feature a develop
git merge --ff-only rama Nunca crea nuevos commits en la rama destino pero falla si es necesario crear un nuevo commit,
por lo que --ff-only ayuda a detectar si hay algún problema
De develop a Release
De release a master o para actualizar cualquier rama desde GitHub cuando no hay commits nuevos en local
git merge rama Es el merge normal que puede crear nuevos commits De una rama develop a feature.
Para resolver los conflictos desde nuestra rama feature ya que nos da igual si hay nuevos commits


git rebase rama

Pone los commits en la nueva rama siempre después de los que ya hay, es decir que los reordena.

Se usa para poner los commits de origin/develop en nuestra develop de antes que lo de develop porque hay commits distintos en cada rama pero del mismo commit origen

git commit -m "E"

%%{init: { 'logLevel': 'debug', 'theme': 'default','themeVariables': { 'git0': '#6D97CA','git1': '#00ffff' }, 'gitGraph': {'showBranches': true, 'showCommitLabel':true,'mainBranchName': 'origin/develop','rotateCommitLabel': false}} }%% gitGraph commit id: "A" commit id: "B" branch develop commit id: "E"


git fetch --prune

%%{init: { 'logLevel': 'debug', 'theme': 'default','themeVariables': { 'git0': '#6D97CA','git1': '#00ffff' }, 'gitGraph': {'showBranches': true, 'showCommitLabel':true,'mainBranchName': 'origin/develop','rotateCommitLabel': false}} }%% gitGraph commit id: "A" commit id: "B" branch develop commit id: "E" checkout origin/develop commit id: "C" commit id: "D"


git switch develop
git rebase origin/develop

%%{init: { 'logLevel': 'debug', 'theme': 'default','themeVariables': { 'git0': '#6D97CA','git1': '#00ffff' }, 'gitGraph': {'showBranches': true, 'showCommitLabel':true,'mainBranchName': 'origin/develop','rotateCommitLabel': false}} }%% gitGraph commit id: "A" commit id: "B" commit id: "C" commit id: "D" branch develop commit id: "E" checkout origin/develop

git merge --squash rama

Junta todos los "microcommits" de la rama en uno solo en la rama destino. Es necesario acto seguido el commit

Se usa para pasar los microcommits de una rama feature a develop


git merge --ff-only rama

Solo adelanta el puntero de donde está la rama. Nunca crea nuevos commits en la rama destino pero falla si es necesario crear un nuevo commit,
por lo que --ff-only ayuda a detectar si hay algún problema.

Se usa De develop a Release.De release a master o para actualizar cualquier rama desde GitHub cuando no hay commits nuevos en local


%%{init: { 'logLevel': 'debug', 'theme': 'default','themeVariables': { 'git0': '#6D97CA','git1': '#00ffff' }, 'gitGraph': {'showBranches': true, 'showCommitLabel':true,'mainBranchName': 'develop','rotateCommitLabel': false}} }%% gitGraph commit id: "A" commit id: "B" commit id: "C" commit id: "D"


git fetch --prune

%%{init: { 'logLevel': 'debug', 'theme': 'default','themeVariables': { 'git0': '#6D97CA','git1': '#00ffff' }, 'gitGraph': {'showBranches': true, 'showCommitLabel':true,'mainBranchName': 'develop','rotateCommitLabel': false}} }%% gitGraph commit id: "A" commit id: "B" commit id: "C" commit id: "D" branch origin/develop commit id: "E" commit id: "F"


git merge --ff-only origin/develop

%%{init: { 'logLevel': 'debug', 'theme': 'default','themeVariables': { 'git0': '#6D97CA','git1': '#00ffff' }, 'gitGraph': {'showBranches': true, 'showCommitLabel':true,'mainBranchName': 'develop y origin/develop','rotateCommitLabel': false}} }%% gitGraph commit id: "A" commit id: "B" commit id: "C" commit id: "D" commit id: "E" commit id: "F"

git merge rama

Es el merge normal que puede crear nuevos commits | De una rama develop a feature.

Se usa para resolver los conflictos desde nuestra rama feature ya que nos da igual si hay nuevos commits

Usos de Merge

  git fetch --prune
  git switch develop
  git merge --ff-only origin/develop

  git fetch --prune
  git switch release
  git merge --ff-only origin/release

  git fetch --prune
  git switch master
  git merge --ff-only origin/master



  git switch release
  git merge --ff-only develop

  git switch master
  git merge --ff-only release



  git fetch --prune
  git switch develop
  git rebase origin/develop

Lo normal es hacer el rebase cuando al hacer un git push se produce el siguiente error:

 ! [rejected]        develop -> develop (fetch first)
error: falló el push de algunas referencias a 'https://github.com/usuario/repositorio.git'
ayuda: Actualizaciones fueron rechazadas porque el remoto contiene trabajo que
ayuda: no existe localmente. Esto es causado usualmente por otro repositorio 
ayuda: realizando push a la misma ref. Quizás quiera integrar primero los cambios
ayuda: remotos (ej. 'git pull ...') antes de volver a hacer push.
ayuda: Vea 'Notes about fast-forwards0 en 'git push --help' para detalles.



  git switch develop
  git merge  --squash lorenzo_42
  git commit -am "feat(#42):pantalla de Login"

Recuerda hacer siempre después del git merge --squash el commit



  git switch lorenzo_42
  git merge develop



A partir de la versión 2.27 , al hacer un git pull aparece el siguiente mensaje
logongas@beren:~/Documentos/ensenyament/2daw-daw/prueba_daw$ git pull
ayuda: Hacer un pull sin especificar cómo reconciliar las ramas es poco
ayuda: recomendable. Puedes eliminar este mensaje usando uno de los
ayuda: siguientes comandos antes de tu siguiente pull:
ayuda: 
ayuda:   git config pull.rebase false  # hacer merge (estrategia por defecto)
ayuda:   git config pull.rebase true   # aplicar rebase
ayuda:   git config pull.ff only       # aplicar solo fast-forward
ayuda: 
ayuda: Puedes reemplazar "git config" con "git config --global" para aplicar
ayuda: la preferencia en todos los repositorios. Puedes también pasar --rebase,
ayuda: --no-rebase, o --ff-only en el comando para sobreescribir la configuración
ayuda: por defecto en cada invocación.
ayuda: 

Es decir que avisa que se configure para saber que hacer en caso de problemas, si con Fast-Forward, con un rebase o con merge. Así que como siempre hemos recomendado , es mejor no usar git pull ya que así decidimos nosotros que hacer o en el peor de los casos indicar para que haga siempre un --ff-only mediante al orden:

  git config --global pull.ff only

De esa forma podremos hacer un git pull de forma segura (siempre y cuando no estés en otro ordenador donde no lo hayan configurado así :-(). Aunque mejor aun es dehabilitar git pull: Can I disable git pull?

Mas información:

Mas información:

Conflictos

En caso de hacer un git merge o un git rebase, etc. se puede producir un conflicto. Que consiste en que git no sabe como fusionar los ficheros. En ese caso lo primero es resolver cada conflicto en cada fichero.

Imaginemos el mismo fichero index.html en 2 ramas distintas:

Nuestra Rama Rama a Fusionar
<html>
	<body>
		<h1>Hola mundo</h1>
	</body>
</html>
<html>
	<body>
		<h1>Adios planeta</h1>
	</body>
</html>

Cuando se produce el conflicto, el fichero resultante queda de la siguiente forma:

  <html>
  	<body>
  <<<<<<< HEAD
  		<h1>Hola mundo</h1>
  =======
  		<h1>Adios planeta</h1>
  >>>>>>> Rama a Fusionar
  	</body>
  </html>

Ahora tenemos que decidir como resolver el conflicto que el resultado definitivo ya depende del código que estemos fusionando. Un posible resultado podría ser:

<html>
	<body>
		<h1>Hola y Adios mundo</h1>
	</body>
</html>

git rebase rama

Imaginemos que queremos mergear la rama origin/develop sobre develop.

Si hay un conflicto se producirá el siguiente mensaje:

En primer lugar, rebobinando HEAD para después reproducir tus cambios encima de ésta...
Aplicando: feat(#50):Cabecera tercera
Usando la información del índice para reconstruir un árbol base...
M	index.html
Retrocediendo para parchar base y fusión de 3-vías...
Auto-fusionando index.html
CONFLICTO (contenido): Conflicto de fusión en index.html
error: Falló al fusionar en los cambios.
El parche falló en 0001 feat(#50):Cabecera tercera
Use 'git am --show-current-patch' para ver el parche fallido

Resuelva todos los conflictos manualmente ya sea con 
"git add/rm <archivo_conflictivo>", luego ejecute "git rebase --continue".
Si prefieres saltar este parche, ejecuta "git rebase --skip" .
Para abortar y regresar al estado previo al "git rebase", ejecuta "git rebase --abort".

Ahora debemos como antes arreglar el conflicto editando el fichero index.html pero ahora deberemos hacer dos cosas mas:

  git add index.html 
  git rebase --continue

Es decir añadir el fichero que ha dado el conflicto con un git add y luego hacer un git rebase --continue para que se acabe de fusionar.

El ejemplo completo lo podemos ver aquí:

git switch develop
git rebase origin/develop
vi index.html  #Arreglar el conflicto
git add index.html
git rebase --continue

git merge --squash rama

Imaginemos que queremos mergear una rama de una funcionalidad llamada "Lorenzo_3" sobre develop.

Si hay un conflicto se producirá el siguiente mensaje:

Auto-fusionando index.html
CONFLICTO (contenido): Conflicto de fusión en index.html
Commit de squash -- no actualizando HEAD
Fusión automática falló; arregle los conflictos y luego realice un commit con el resultado.

Ahora debemos como antes arreglar el conflicto editando el fichero index.html y hacer el commit que de todas formas ya íbamos a hacer

  git commit -am "feat(#45):Nueva cabecera"

El ejemplo completo lo podemos ver aqui:

git switch develop
git merge --squash Lorenzo_3
vi index.html  #Arreglar el conflicto
git commit -am "feat(#45):Nueva cabecera"

git merge --ff-only rama

Con un fast-forward nunca se puede producir un conflicto.

git merge rama

Imaginemos que queremos mergear los cambios de develop en la rama Lorenzo_3

Si hay un conflicto se producirá el siguiente mensaje:

Auto-fusionando index.html
CONFLICTO (contenido): Conflicto de fusión en index.html
Fusión automática falló; arregle los conflictos y luego realice un commit con el resultado.

Ahora debemos como antes arreglar el conflicto editando el fichero index.html y el --continue:

  git add index.html 
  git merge --continue

Es decir añadir el fichero que ha dado el conflicto con un git add y luego hacer un git merge --continue para que se acabe de mergear.

El ejemplo completo lo podemos ver aqui:

git switch Lorenzo_3
git merge develop
vi index.html  #Arreglar el conflicto
git add index.html 
git merge --continue

Recetas

git fetch --prune && git switch develop && git merge --ff-only origin/develop && git switch release && git merge --ff-only origin/release && git switch master && git merge --ff-only origin/master && git switch develop

git switch release && git merge --ff-only develop && git switch master && git merge --ff-only release && git switch develop

git switch master && git push && git switch release && git push && git switch develop && git push

Ejercicios

Ejercicio 1

Ejercicio 2

Vamos a seguir con el ejercicio anterior pero ahora con 2 usuarios distintos (se puede hacer en 2 carpetas distintas)

Ejercicio 3

Vamos a seguir con el ejercicio anterior pero ahora con 2 usuarios distintos (se puede hacer en 2 carpetas distintas).

Ejercicio 4

Vamos a seguir con el ejercicio anterior pero ahora con 2 usuarios distintos (se puede hacer en 2 carpetas distintas).

Ejercicio 5

Crea un nuevo proyecto en Git.