Ramas¶
Administración de ramas¶
Crear una nueva rama¶
Cuando vamos a trabajar en una nueva funcionalidad, es conveniente
hacerlo en una nueva rama, para no modificar la rama principal y dejarla
inestable. Aunque la orden para manejar ramas es git branch
podemos
usar también git checkout
.
Vamos a crear una nueva rama:
git branch borrar-constante
Nota
Si usamos git branch sin ningún argumento, nos devolverá la lista de ramas disponibles.
La orden anterior no devuelve ningún resultado y tampoco nos cambia de rama, para eso debemos usar checkout:
$ git checkout borrar-constante
Switched to branch 'borrar-contante'
Nota
Hay una forma más rapida de hacer ambas acciones en un solo
paso. Con el parámetro -b
de git checkout
podemos cambiarnos a
una rama que, si no existe, se crea instantáneamente.
$ git checkout -b borrar-constante
Switched to a new branch 'borrar-constante'
Modificaciones en la rama secundaria¶
Para realizar la prueba de modificaremos el fichero
curso-de-git.dylan
. El saludo por defecto seguirá siendo Hola
pero podremos modificarlo pasando el parámetro opcional saludo.
Module: curso-de-git-impl
// Internal
define constant $greeting = "Hola";
// Exported
define function greeting (mensaje, #key saludo = $greeting) => (s :: <string>)
concatenate("¡", $greeting, " ", mensaje, "!")
end function;
Añadiremos una nueva prueba.
Module: curso-de-git-test-suite
define test test-$greeting ()
assert-equal("Hola", $greeting);
end test;
define test test-greeting ()
assert-equal("¡Hola mundo!", greeting("mundo"));
end test;
define test test-greeting-saludos ()
assert-equal("¡Saludos mundo!", greeting("mundo", saludo: "Saludos"));
end test;
// Use `_build/bin/curso-de-git-test-suite --help` to see options.
run-test-application()
Podríamos confirmar los cambios todos de golpe, pero lo haremos de uno en uno, con su comentario.
$ git add source/lib/curso-de-git.dylan
$ git commit -m "Saludo como parámetro"
$ git commit -m "Saludo como parámetro"
[borrar-constante d209b81] Saludo como parámetro
1 file changed, 2 insertions(+), 2 deletions(-)
$ git add tests/curso-de-git-test-suite.dylan
$ git commit -m "Añadir prueba para saludo como parámetro"
$ git commit -m "Añadir prueba para saludo como parámetro"
[borrar-constante 9bd66c3] Añadir prueba para saludo como parámetro
1 file changed, 4 insertions(+)
Y ahora con la orden git checkout
podemos movernos entre ramas:
$ git checkout main
$ git checkout borrar-constante
Modificaciones en la rama main¶
Podemos volver y añadiremos el archivo CHANGELOG
en la rama
principal para anotar los cambios en las versiones:
$ git checkout main
Switched to branch 'main'
Your branch is up to date with 'origin/main'.
# Changelog
Todos los cambios notables del proyecto serán documentados en este
fichero.
## [Unreleased]
### Añadido
- v0.1.0 Cambiar saludo por parámetro opcional
Y lo añadimos a nuestro repositorio en la rama (main) en la que estamos:
$ git add CHANGELOG
$ git commit -m "Añadido CHANGELOG"
$ git commit -m "Añadido CHANGELOG"
[main 658f895] Añadido CHANGELOG
1 file changed, 10 insertions(+)
create mode 100644 CHANGELOG
$ git hist --all
* [2024-07-19] [658f895] | Añadido CHANGELOG {{Fernando Raya}} (HEAD -> main)
| * [2024-07-19] [9bd66c3] | Añadir prueba para saludo como parámetro {{Fernando Raya}} (borrar-constante)
| * [2024-07-19] [d209b81] | Saludo como parámetro {{Fernando Raya}}
|/
* [2024-07-12] [b9d1749] | doc: Ampliar descripción {{Fernando Raya}} (origin/main, origin/HEAD)
* [2024-07-12] [b15078e] | doc: Update to v.0.1.2 {{Fernando Raya}}
Mezclar ramas¶
Podemos incorporar los cambios de una rama a otra con la orden git
merge
.
$ git checkout borrar-constante
Switched to branch 'borrar-constante'
$ git merge main
$ git merge main
Merge made by the 'ort' strategy.
CHANGELOG | 10 ++++++++++
1 file changed, 10 insertions(+)
create mode 100644 CHANGELOG
$ git hist --all
* [2024-07-20] [b42d86a] | Merge branch 'main' into borrar-constante {{Fernando Raya}} (HEAD -> borrar-constante)
|\
| * [2024-07-19] [658f895] | Añadido CHANGELOG {{Fernando Raya}} (main)
* | [2024-07-19] [9bd66c3] | Añadir prueba para saludo como parámetro {{Fernando Raya}}
* | [2024-07-19] [d209b81] | Saludo como parámetro {{Fernando Raya}}
|/
* [2024-07-12] [b9d1749] | doc: Ampliar descripción {{Fernando Raya}} (origin/main, origin/HEAD)
$ git status
On branch borrar-constante
nothing to commit, working tree clean
En mi ordenador, la rama main
tiene un cambio no subido al
repositorio remoto:
$ git status
On branch main
Your branch is ahead of 'origin/main' by 1 commit.
(use "git push" to publish your local commits)
$ git push
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 4 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 423 bytes | 423.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
To github.com:fraya/curso-de-git.git
b9d1749..658f895 main -> main
De esa forma se puede trabajar en una rama secundaria incorporando los cambios de la rama principal o de otra rama.
Resolver conflictos¶
Un conflicto es cuando se produce una fusión que Git no es capaz de resolver. Vamos a modificar la rama main para crear un problema con la rama borrar-constante.
$ git checkout main
Modificamos nuestro archivo curso-de-git-app.dylan
de
nuevo. Utilizaremos la función first
, que devuelve el primer
elemento de una lista. Si no hay ningún elemento devolveremos el valor
del parámetro default
, es decir mundo.
Module: curso-de-git-app
Author: Fernando Raya
define function main
(name :: <string>, arguments :: <vector>)
let mensaje = first(arguments, default: "mundo");
format-out("%s\n", greeting(mensaje));
exit-application(0);
end function;
// Calling our main function (which could have any name) should be the last
// thing we do.
main(application-name(), application-arguments());
Y guardamos los cambios:
$ git add app/curso-de-git-app.dylan
$ git commit -m "Simplificar captura argumento"
$ git hist --all
* [2024-07-21] [b29d17a] | Simplificar captura argumento {{Fernando Raya}} (HEAD -> main)
| * [2024-07-20] [b42d86a] | Merge branch 'main' into borrar-constante {{Fernando Raya}} (borrar-constante)
| |\
| |/
|/|
* | [2024-07-19] [658f895] | Añadido CHANGELOG {{Fernando Raya}} (origin/main, origin/HEAD)
| * [2024-07-19] [9bd66c3] | Añadir prueba para saludo como parámetro {{Fernando Raya}}
| * [2024-07-19] [d209b81] | Saludo como parámetro {{Fernando Raya}}
|/
* [2024-07-12] [b9d1749] | doc: Ampliar descripción {{Fernando Raya}}
Crearemos otra rama llamada saludo-moderno y modificamos el mismo fichero:
$ git checkout -b saludo-moderno
Modificaremos la palabra del saludo cambiando del valor por defecto Hola a la palabra Hi.
Module: curso-de-git-app
Author: Fernando Raya
define function main
(name :: <string>, arguments :: <vector>)
let mensaje = first(arguments, default: "mundo");
format-out("%s\n", greeting(mensaje, saludo: "Hi"));
exit-application(0);
end function;
// Calling our main function (which could have any name) should be the last
// thing we do.
main(application-name(), application-arguments());
$ git commit -a -m "Usar 'Hi' en lugar de 'Hola'"
$ git commit -a -m "Usar 'Hi' en lugar de 'Hola'"
[saludo-moderno 9647e4a] Usar 'Hi' en lugar de 'Hola'
1 file changed, 1 insertion(+), 1 deletion(-)
$ git checkout main
Module: curso-de-git-app
Author: Fernando Raya
define function main
(name :: <string>, arguments :: <vector>)
let mensaje = first(arguments, default: "mundo");
format-out("%s\n", greeting(mensaje, saludo: "Saluton"));
exit-application(0);
end function;
// Calling our main function (which could have any name) should be the last
// thing we do.
main(application-name(), application-arguments());
$ git commit -a -m "Saludo en Esperanto"
$ git checkout saludo-moderno
$ git merge main
Auto-merging source/app/curso-de-git-app.dylan
CONFLICT (content): Merge conflict in source/app/curso-de-git-app.dylan
Automatic merge failed; fix conflicts and then commit the result.
Si editamos nuestro archivo app/curso-de-git-app.dylan.php
obtendremos algo similar a
esto:
Module: curso-de-git-app
Author: Fernando Raya
define function main
(name :: <string>, arguments :: <vector>)
let mensaje = first(arguments, default: "mundo");
<<<<<<< HEAD
format-out("%s\n", greeting(mensaje, saludo: "Hi"));
=======
format-out("%s\n", greeting(mensaje, saludo: "Saluton"));
>>>>>>> main
exit-application(0);
end function;
// Calling our main function (which could have any name) should be the last
// thing we do.
main(application-name(), application-arguments());
La primera parte marca el código que estaba en la rama donde trabajábamos (HEAD) y la parte final el código de donde fusionábamos. Resolvemos el conflicto, dejando el archivo como sigue:
Module: curso-de-git-app
Author: Fernando Raya
define function main
(name :: <string>, arguments :: <vector>)
let mensaje = first(arguments, default: "mondo");
format-out("%s\n", greeting(mensaje, saludo: "Saluton"));
exit-application(0);
end function;
// Calling our main function (which could have any name) should be the last
// thing we do.
main(application-name(), application-arguments());
Y resolvemos el conflicto confirmando los cambios:
$ git add curso-de-git-app.dylan
$ git commit -m "Solucionado el conflicto al fusionar con la rama main"
$ git checkout main
$ git merge saludo-moderno
Rebasing vs Merging¶
Rebasing es otra técnica para fusionar distinta a merge y usa la orden
git rebase
. Vamos a dejar nuestro proyecto como estaba antes del
fusionado. Para ello necesitamos anotar el hash anterior al de la acción
de merge. El que tiene la anotación “hola usa la clase HolaMundo”.
Para ello podemos usar la orden git reset
que nos permite mover HEAD
donde queramos.
$ git checkout hola
Switched to branch 'hola'
$ git hist
* a36af04 2013-06-16 | Solucionado el conflicto al fusionar con la rama main (HEAD, hola) [Sergio Gómez]
|\
| * 9c85275 2013-06-16 | Programa interactivo (main) [Sergio Gómez]
* | 9c6ac06 2013-06-16 | Merge commit 'c3e65d0' into hola [Sergio Gómez]
|\ \
| |/
| * c3e65d0 2013-06-16 | Añadido README.md [Sergio Gómez]
* | 9862f33 2013-06-16 | hola usa la clase HolaMundo [Sergio Gómez]
* | 6932156 2013-06-16 | Añadida la clase HolaMundo [Sergio Gómez]
|/
* 81c6e93 2013-06-16 | Movido hola.php a lib [Sergio Gómez]
* 96a39df 2013-06-16 | Añadido el autor del programa y su email [Sergio Gómez]
* fd4da94 2013-06-16 | Se añade un comentario al cambio del valor por defecto (tag: v1) [Sergio Gómez]
* 3283e0d 2013-06-16 | Se añade un parámetro por defecto (tag: v1-beta) [Sergio Gómez]
* efc252e 2013-06-16 | Parametrización del programa [Sergio Gómez]
* e19f2c1 2013-06-16 | Creación del proyecto [Sergio Gómez]
$ git reset --hard 9862f33
HEAD is now at 9862f33 hola usa la clase HolaMundo
Y nuestro estado será:
$ git hist --all
* 9862f33 2013-06-16 | hola usa la clase HolaMundo (HEAD, hola) [Sergio Gómez]
* 6932156 2013-06-16 | Añadida la clase HolaMundo [Sergio Gómez]
| * 9c85275 2013-06-16 | Programa interactivo (main) [Sergio Gómez]
| * c3e65d0 2013-06-16 | Añadido README.md [Sergio Gómez]
|/
* 81c6e93 2013-06-16 | Movido hola.php a lib [Sergio Gómez]
* 96a39df 2013-06-16 | Añadido el autor del programa y su email [Sergio Gómez]
* fd4da94 2013-06-16 | Se añade un comentario al cambio del valor por defecto (tag: v1) [Sergio Gómez]
* 3283e0d 2013-06-16 | Se añade un parámetro por defecto (tag: v1-beta) [Sergio Gómez]
* efc252e 2013-06-16 | Parametrización del programa [Sergio Gómez]
* e19f2c1 2013-06-16 | Creación del proyecto [Sergio Gómez]
Hemos desecho todos los merge y nuestro árbol está “limpio”. Vamos a
probar ahora a hacer un rebase. Continuamos en la rama hola
y
ejecutamos lo siguiente:
$ git rebase main
First, rewinding head to replay your work on top of it...
Applying: Añadida la clase HolaMundo
Applying: hola usa la clase HolaMundo
Using index info to reconstruct a base tree...
M lib/hola.php
Falling back to patching base and 3-way merge...
Auto-merging lib/hola.php
CONFLICT (content): Merge conflict in lib/hola.php
error: Failed to merge in the changes.
Patch failed at 0002 hola usa la clase HolaMundo
The copy of the patch that failed is found in: .git/rebase-apply/patch
When you have resolved this problem, run "git rebase --continue".
If you prefer to skip this patch, run "git rebase --skip" instead.
To check out the original branch and stop rebasing, run "git rebase --abort".
El conflicto, por supuesto, se sigue dando. Resolvemos guardando el
archivo hola.php
como en los casos anteriores:
<?php
// Autor: Sergio Gómez <sergio@uco.es>
require('HolaMundo.php');
print "Introduce tu nombre:";
$nombre = trim(fgets(STDIN));
print new HolaMundo($nombre);
Añadimos los cambios en staging y en esta ocasión, y tal como nos
indicaba en el mensaje anterior, no tenemos que hacer git commit
sino continuar con el rebase:
$ git add lib/hola.php
$ git status
rebase in progress; onto 269eaca
You are currently rebasing branch 'hola' on '269eaca'.
(all conflicts fixed: run "git rebase --continue")
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: lib/hola.php
$ git rebase --continue
Applying: hola usa la clase HolaMundo
Y ahora vemos que nuestro árbol tiene un aspecto distinto, mucho más limpio:
$ git hist --all
* 9862f33 2013-06-16 | hola usa la clase HolaMundo (HEAD -> hola) [Sergio Gómez]
* 6932156 2013-06-16 | Añadida la clase HolaMundo [Sergio Gómez]
* 9c85275 2013-06-16 | Programa interactivo (main) [Sergio Gómez]
* c3e65d0 2013-06-16 | Añadido README.md [Sergio Gómez]
* 81c6e93 2013-06-16 | Movido hola.php a lib [Sergio Gómez]
* 96a39df 2013-06-16 | Añadido el autor del programa y su email [Sergio Gómez]
* fd4da94 2013-06-16 | Se añade un comentario al cambio del valor por defecto (tag: v1) [Sergio Gómez]
* 3283e0d 2013-06-16 | Se añade un parámetro por defecto (tag: v1-beta) [Sergio Gómez]
* efc252e 2013-06-16 | Parametrización del programa [Sergio Gómez]
* e19f2c1 2013-06-16 | Creación del proyecto [Sergio Gómez]
gitGraph:
commit id: "e19f2c1"
commit id: "efc252e"
commit id: "3283e0d" tag: "v1-beta"
commit id: "fd4da94" tag: "v1"
commit id: "96a39df"
commit id: "8c2a509"
commit id: "c3e65d0"
commit id: "9c85275"
branch hola
commit id: "6932156"
commit id: "9862f33"
Lo que hace rebase es volver a aplicar todos los cambios a la rama máster, desde su nodo más reciente. Eso significa que se modifica el orden o la historia de creación de los cambios. Por eso rebase no debe usarse si el orden es importante o si la rama es compartida.
## Mezclando con la rama main
Ya hemos terminado de implementar los cambios en nuestra rama secundaria
y es hora de llevar los cambios a la rama principal. Usamos
git merge
para hacer una fusión normal:
$ git checkout main
Switched to branch 'main'
$ git merge hola
Updating c3e65d0..491f1d2
Fast-forward
lib/HolaMundo.php | 16 ++++++++++++++++
lib/hola.php | 4 +++-
2 files changed, 19 insertions(+), 1 deletion(-)
create mode 100644 lib/HolaMundo.php
$ git hist --all
* 9862f33 2013-06-16 | hola usa la clase HolaMundo (HEAD -> main, hola) [Sergio Gómez]
* 6932156 2013-06-16 | Añadida la clase HolaMundo [Sergio Gómez]
* 9c85275 2013-06-16 | Programa interactivo [Sergio Gómez]
* c3e65d0 2013-06-16 | Añadido README.md [Sergio Gómez]
* 81c6e93 2013-06-16 | Movido hola.php a lib [Sergio Gómez]
* 96a39df 2013-06-16 | Añadido el autor del programa y su email [Sergio Gómez]
* fd4da94 2013-06-16 | Se añade un comentario al cambio del valor por defecto (tag: v1) [Sergio Gómez]
* 3283e0d 2013-06-16 | Se añade un parámetro por defecto (tag: v1-beta) [Sergio Gómez]
* efc252e 2013-06-16 | Parametrización del programa [Sergio Gómez]
* e19f2c1 2013-06-16 | Creación del proyecto [Sergio Gómez]
Vemos que indica que el tipo de fusión es fast-forward. Este tipo de
fusión tiene el problema que no deja rastro de la fusión, por eso suele
ser recomendable usar el parámetro --no-ff
para que quede constancia
siempre de que se ha fusionado una rama con otra.
Vamos a volver a probar ahora sin hacer fast-forward. Reseteamos main al estado “Programa interactivo”.
$ git reset --hard 9c85275
$ git hist --all
* 9862f33 2013-06-16 | hola usa la clase HolaMundo (HEAD -> hola) [Sergio Gómez]
* 6932156 2013-06-16 | Añadida la clase HolaMundo [Sergio Gómez]
* 9c85275 2013-06-16 | Programa interactivo (main) [Sergio Gómez]
* c3e65d0 2013-06-16 | Añadido README.md [Sergio Gómez]
* 81c6e93 2013-06-16 | Movido hola.php a lib [Sergio Gómez]
* 96a39df 2013-06-16 | Añadido el autor del programa y su email [Sergio Gómez]
* fd4da94 2013-06-16 | Se añade un comentario al cambio del valor por defecto (tag: v1) [Sergio Gómez]
* 3283e0d 2013-06-16 | Se añade un parámetro por defecto (tag: v1-beta) [Sergio Gómez]
* efc252e 2013-06-16 | Parametrización del programa [Sergio Gómez]
* e19f2c1 2013-06-16 | Creación del proyecto [Sergio Gómez]
Vemos que estamos como en el final de la sección anterior, así que ahora mezclamos:
$ git merge -m "Aplicando los cambios de la rama hola" --no-ff hola
Merge made by the 'recursive' strategy.
lib/HolaMundo.php | 16 ++++++++++++++++
lib/hola.php | 4 +++-
2 files changed, 19 insertions(+), 1 deletion(-)
create mode 100644 lib/HolaMundo.php
$ git hist --all
* 2eab8ca 2013-06-16 | Aplicando los cambios de la rama hola (HEAD -> main) [Sergio Gomez]
*\
| * 9862f33 2013-06-16 | hola usa la clase HolaMundo (hola) [Sergio Gómez]
| * 6932156 2013-06-16 | Añadida la clase HolaMundo [Sergio Gómez]
|/
* 9c85275 2013-06-16 | Programa interactivo (main) [Sergio Gómez]
* c3e65d0 2013-06-16 | Añadido README.md [Sergio Gómez]
* 81c6e93 2013-06-16 | Movido hola.php a lib [Sergio Gómez]
* 96a39df 2013-06-16 | Añadido el autor del programa y su email [Sergio Gómez]
* fd4da94 2013-06-16 | Se añade un comentario al cambio del valor por defecto (tag: v1) [Sergio Gómez]
* 3283e0d 2013-06-16 | Se añade un parámetro por defecto (tag: v1-beta) [Sergio Gómez]
* efc252e 2013-06-16 | Parametrización del programa [Sergio Gómez]
* e19f2c1 2013-06-16 | Creación del proyecto [Sergio Gómez]
gitGraph:
commit id: "e19f2c1"
commit id: "efc252e"
commit id: "3283e0d" tag: "v1-beta"
commit id: "fd4da94" tag: "v1"
commit id: "96a39df"
commit id: "8c2a509"
commit id: "c3e65d0"
commit id: "9c85275"
branch hola
commit id: "6932156"
commit id: "9862f33"
checkout main
merge hola id: "2eab8ca"
En la siguiente imagen se puede ver la diferencia: