informatica:programacion:cursos:control_version_git_avanzado:branching
Diferencias
Muestra las diferencias entre dos versiones de la página.
| Próxima revisión | Revisión previa | ||
| informatica:programacion:cursos:control_version_git_avanzado:branching [2023/05/29 11:13] – creado tempwin | informatica:programacion:cursos:control_version_git_avanzado:branching [2023/06/03 16:41] (actual) – [Fusionando código: merge, rebase y cherry-pick] tempwin | ||
|---|---|---|---|
| Línea 1: | Línea 1: | ||
| ====== Branching ====== | ====== Branching ====== | ||
| + | |||
| + | Sección perteneciente al curso [[informatica: | ||
| ===== Introducción ===== | ===== Introducción ===== | ||
| + | |||
| + | Las ramas en git no son más que una referencia que apunta a un commit. | ||
| + | |||
| + | Para nosotros, por comodidad, es más recomendable pensar en las ramas como itinerarios de commits. | ||
| + | |||
| + | El modelo de trabajo será normalmente: | ||
| + | |||
| + | - Abrir nueva rama | ||
| + | - Trabajar en ella | ||
| + | - La volcamos en la rama principal | ||
| + | - Borramos la rama | ||
| + | - Abrir nueva rama | ||
| + | - Trabajar en ella | ||
| + | - La volcamos en la rama principal | ||
| + | - Borramos la rama | ||
| + | - ... | ||
| + | |||
| + | Veremos la metodología **git flow**. | ||
| + | |||
| ===== El modelo de ramas de Git ===== | ===== El modelo de ramas de Git ===== | ||
| + | |||
| + | Tenemos dos concepciones de ramas, una más " | ||
| + | |||
| + | {{ : | ||
| + | |||
| + | Esto hace que sea muy cómodo trabajar con ramas porque es muy fácil movernos entre ellas al ser un " | ||
| + | |||
| + | <WRAP center round info 60%> | ||
| + | Un commit no tiene información sobre a qué rama pertenece. | ||
| + | </ | ||
| + | |||
| + | Recordemos: una rama es una referencia a un commit. | ||
| + | |||
| + | La **rama master** se crea automáticamente al iniciar un repositorio git. | ||
| + | |||
| + | Cuando HEAD apunta a una rama, al hacer un commit, avanza HEAD y esto hace que la rama también avance con él. | ||
| + | |||
| + | Cuando decimos, por ejemplo, que estamos en **master** estamos queriendo decir que tenemos HEAD apuntando a la referencia **master**. | ||
| + | |||
| + | Cuando creamos una nueva rama, lo que hacemos es crear una nueva referencia que apunta al commit en el que estamos. En ese momento nos podemos cambiar a esa nueva rama ('' | ||
| + | |||
| + | Las ramas las vamos a entender siempre como entornos paralelos que creamos para desarrollar una característica o corregir un fallo y, al terminar, volcaremos a la rama principal. Borraremos la rama alternativa. | ||
| ===== Trabajo con ramas ===== | ===== Trabajo con ramas ===== | ||
| - | ==== Comandos para trabajar con ramas ==== | + | ==== Listar |
| + | |||
| + | < | ||
| + | git branch | ||
| + | </ | ||
| + | |||
| + | Git nos señala la rama en la que estamos añadiendo un asterisco delante. | ||
| + | |||
| + | <WRAP center round info 60%> | ||
| + | Si el repositorio de git está recién iniciado, el comando anterior no mostrará nada porque no hay ningún commit. | ||
| + | </ | ||
| + | |||
| + | Recordamos que "estar en master" | ||
| + | |||
| + | ==== Creación de ramas ==== | ||
| + | |||
| + | < | ||
| + | git branch nombre_rama [< | ||
| + | </ | ||
| + | |||
| + | Cuando no indicamos nada, la rama se crea en el commit en el que estemos. | ||
| + | |||
| + | Al crear la rama, se crea una referencia que apunta a un commit | ||
| + | |||
| + | Ir a una rama: | ||
| + | |||
| + | < | ||
| + | git checkout nombre_rama | ||
| + | </ | ||
| + | |||
| + | El comando anterior saltará al commit al que apunte dicha rama y HEAD apuntará a esa rama. | ||
| + | |||
| + | Para evitar olvidarnos de cambiar de rama al crearla, podemos usar el siguiente comando que no solo creará la rama sino nos moveremos a ella: | ||
| + | |||
| + | < | ||
| + | git checkout -b nombre_rama | ||
| + | </ | ||
| + | |||
| + | ==== Eliminar una rama ==== | ||
| + | |||
| + | < | ||
| + | git branch -d / -D nombre_rama | ||
| + | </ | ||
| + | |||
| + | * '' | ||
| + | * '' | ||
| + | |||
| + | **Importante**: | ||
| + | |||
| + | <WRAP center round info 60%> | ||
| + | Recordamos que no se borran los commits realmente sino la referencia, quedando ese itinerario " | ||
| + | </ | ||
| + | |||
| + | ==== Renombrar una rama ==== | ||
| + | |||
| + | < | ||
| + | git branch -m nombre_antiguo nombre_nuevo | ||
| + | </ | ||
| ==== Ejemplo de trabajo con ramas ==== | ==== Ejemplo de trabajo con ramas ==== | ||
| + | |||
| + | <WRAP center round todo 60%> | ||
| + | Poner ejemplo en consola del gráfico anterior con dos ramas | ||
| + | </ | ||
| + | |||
| + | Si queremos " | ||
| + | |||
| + | < | ||
| + | git branch -f nombre_rama < | ||
| + | </ | ||
| + | |||
| + | Si quisiéramos mover una rama a la rama en la que nos encontramos no hace falta indicar commit: | ||
| + | |||
| + | < | ||
| + | git branch -f nombre_rama | ||
| + | </ | ||
| ===== Fusionando código: merge, rebase y cherry-pick ===== | ===== Fusionando código: merge, rebase y cherry-pick ===== | ||
| + | |||
| + | Generalmente cuando queramos desarrollar una nueva característica de nuestra aplicación/ | ||
| + | |||
| + | En algún momento, al terminar de trabajar en la nueva rama, querremos volcarla a la rama principal. Decimos entonces que **fusionamos** el código. | ||
| + | |||
| + | En esta fusión de código lo que se hace es crear un nuevo commit que fusiona ambas ramas, así que este nuevo commit tendrá como padres uno de la rama principal y otro de la rama nueva. | ||
| + | |||
| + | El propósito de una rama es que cuando terminemos con ella, la volquemos a otra rama principal. En este proceso de volcado, los commits que estaban en la rama paralela formarán parte de la rama principal. | ||
| + | |||
| + | Aunque hay otras metodologías, | ||
| ==== Fusionando código: merge ==== | ==== Fusionando código: merge ==== | ||
| + | |||
| + | '' | ||
| + | |||
| + | Objetivo: que una rama tenga los commits de otra. | ||
| + | |||
| + | {{ : | ||
| + | |||
| + | Tipos de merge: | ||
| + | |||
| + | * **Fast forward**: no crea un nuevo commit, sin conflictos. Es el más rápido. | ||
| + | * **A tres bandas** (//3-way merge//): crea un nuevo commit, existe posibilidad de conflictos | ||
| + | |||
| + | Si surgen conflictos, lo primero que tenemos que hacer es '' | ||
| + | |||
| + | **master** es una rama que solo se actualiza cada vez que se hace una subida a producción del código. La rama sobre la que se trabaja diariamente es **develop** (puede recibir otro nombre, pero por convención, | ||
| + | |||
| + | El **merge** se hace desde la rama destino. | ||
| + | |||
| + | Con el merge **fast forward** el código no cambia, sencillamente git apunta la rama master a donde la rama secundaria, no se crea ningún commit. | ||
| + | |||
| + | {{ : | ||
| + | |||
| + | <WRAP center round tip 60%> | ||
| + | Recordemos que tras fusionar una rama, borraremos la rama secundaria puesto que lo que nos interesa es la rama principal. | ||
| + | </ | ||
| + | |||
| + | Con el merge **a tres bandas**, en los casos en que tanto la rama principal y la secundaria avanzan por caminos distintos. | ||
| + | |||
| + | {{ : | ||
| + | |||
| + | La fusión **a tres bandas** crea un nuevo commit con el commit común a las dos ramas, el código de una rama y el código de la otra, por eso lo de "a 3 bandas" | ||
| + | |||
| + | {{ : | ||
| + | |||
| + | Esta fusión puede ocasionar conflictos. Por ejemplo si estamos en la siguiente situación: | ||
| + | |||
| + | {{ : | ||
| + | |||
| + | Git no podrá decidir por nosotros y nos pedirá solucionar. | ||
| + | |||
| + | <WRAP center round tip 60%> | ||
| + | Con '' | ||
| + | </ | ||
| ==== Ejemplo práctico de merge ==== | ==== Ejemplo práctico de merge ==== | ||
| + | |||
| + | Si tenemos una rama principal llamada **develop** y una secundaria llamada **experimento** y esta segunda está más avanzada que la primera y la que queremos volcar en la primera: | ||
| + | |||
| + | - Nos situamos en la rama destino de la fusión (**develop**): | ||
| + | - Hacemos el merge: '' | ||
| + | |||
| + | Git habrá pasado la etiqueta **develop** al commit al que está apuntado **experimento** | ||
| + | |||
| + | <WRAP center round todo 60%> | ||
| + | Poner ejemplo en la línea de comandos donde se ve que Git indica que ha hecho un **fast forward** | ||
| + | </ | ||
| + | |||
| + | Ahorra borraremos la rama **experimento**: | ||
| + | |||
| + | < | ||
| + | git branch -d experimento | ||
| + | </ | ||
| + | |||
| + | <WRAP center round tip 60%> | ||
| + | Al borrar una rama, Git comprueba si sus commits quedarían huérfanos. En caso positivo, Git no dejaría que borrásemos la rama porque se " | ||
| + | </ | ||
| + | |||
| + | En un merge a 3 bandas con conflictos, hacemos '' | ||
| ==== Fusionando código: rebase ==== | ==== Fusionando código: rebase ==== | ||
| + | |||
| + | En el **rebase** el resultado de la fusión siempre es una línea, no hay bifurcaciones. | ||
| + | |||
| + | * El **rebase** sirve para aplicar una rama al final de otra. | ||
| + | * Volcar el trabajo de una rama en otra | ||
| + | * Objetivo: que una rama tenga los commits de otra | ||
| + | * No crea un nuevo commit | ||
| + | * A diferencia del **merge**, con **rebase** hay confictos por pasos: lanzar siempre '' | ||
| + | * No hacer rebase si ya se ha subido la rama (auxiliar/ | ||
| + | |||
| + | A diferencia del **merge**, el **rebase** se aplica desde la rama origen. | ||
| + | |||
| + | {{ : | ||
| + | |||
| + | Después de un rebase tenemos que adelantar la rama principal ('' | ||
| + | |||
| + | <WRAP center round important 60%> | ||
| + | Si vamos a hacer un rebase, hay que pensar si hemos subido al repositorio remoto la rama secundaria. Si lo hemos subido, no podemos hacer **rebase** sino **merge**. **rebase** es una de las opciones de rescritura de la historia. Si seguimos un modelo correcto de ramas, esto no debería ser un problema porque las ramas auxiliares nunca se suben al escritorio remoto pues las vamos borrando, solo son útiles en nuestro repositorio local mientras vamos desarrollando. | ||
| + | </ | ||
| ==== Ejemplo práctico de rebase ==== | ==== Ejemplo práctico de rebase ==== | ||
| + | |||
| + | <WRAP center round todo 60%> | ||
| + | Poner ejemplo de consola | ||
| + | </ | ||
| + | |||
| + | Regla de oro: en el momento en que aparezca un conflicto, usamos '' | ||
| + | |||
| + | <WRAP center round tip 60%> | ||
| + | Con '' | ||
| + | </ | ||
| ==== Fusionando código: Cherry-pick ==== | ==== Fusionando código: Cherry-pick ==== | ||
| + | |||
| + | * Aplicar un commit aislado a una rama | ||
| + | * No se aplican los commits anteriores | ||
| + | * Modificador '' | ||
| + | * Confictos: lanzar siempre '' | ||
| + | |||
| + | {{ : | ||
| + | |||
| + | Al igual que el **merge**, **cherry-pick** se usa también desde la rama destino. | ||
| + | |||
| + | < | ||
| + | git cherry-pick < | ||
| + | </ | ||
| + | |||
| + | Si surgen conflictos, '' | ||
| + | |||
| + | <WRAP center round tip 60%> | ||
| + | Con '' | ||
| + | </ | ||
| + | |||
| + | Cuando terminamos de solucionar conflictos: | ||
| + | |||
| + | < | ||
| + | git cherry-pick --continue | ||
| + | </ | ||
| + | |||
| + | <WRAP center round tip 60%> | ||
| + | cherry-pick se podrá utilizar siempre y cuando se hayan hecho commits "con cabeza" | ||
| + | </ | ||
| + | |||
| ===== Métodos para mover referencias ===== | ===== Métodos para mover referencias ===== | ||
| + | |||
| + | 3 métodos para mover referencias. | ||
| + | |||
| + | Uno de los más usados: | ||
| + | |||
| + | < | ||
| + | git branch -f nombre_rama [< | ||
| + | </ | ||
| + | |||
| + | Cuando no especificamos el commit, moverá la rama al HEAD. | ||
| + | |||
| + | Para usar el método anterior, tenemos que estar en una rama distinta a la que nos queramos mover. | ||
| + | |||
| + | Otro método es el '' | ||
| + | |||
| + | < | ||
| + | git reset < | ||
| + | </ | ||
| + | |||
| + | Movemos tanto la referencia de rama y HEAD. Como no queremos guardar los cambios de los commits que quedan huérfanos, usamos el modificador '' | ||
| + | |||
| + | Por último, si solo queremos avanzar la referencia de una rama hasta un commit más avanzado (cuando es el mismo itinerario): | ||
| + | |||
| + | < | ||
| + | git merge rama_destino | ||
| + | </ | ||
| ===== Deshacer errores con las ramas ===== | ===== Deshacer errores con las ramas ===== | ||
| + | |||
| + | La facilidad a la hora de mover referencias (ramas) en git hace que no sea tan complicado deshacer errores. Esto no afecta ni al log ni al contenido de los commits. | ||
| ==== Cómo corregir errores con las ramas ==== | ==== Cómo corregir errores con las ramas ==== | ||
| + | |||
| + | Si tenemos HEAD por delante de la rama principal. Dos formas | ||
| + | |||
| + | < | ||
| + | git branch -f develop | ||
| + | </ | ||
| + | |||
| + | Alternativa (desde develop): | ||
| + | |||
| + | < | ||
| + | git merge < | ||
| + | </ | ||
| + | |||
| + | Y finalmente hacemos que HEAD apunte a develop: | ||
| + | |||
| + | < | ||
| + | git checkout develop | ||
| + | </ | ||
| ==== Cómo deshacer un merge ==== | ==== Cómo deshacer un merge ==== | ||
| + | |||
| + | Si queremos deshacer un merge fast-forward, | ||
| + | |||
| + | Si no se ha añadido ningún commmit, es decir, el cambio ha sido de referencias, | ||
| + | |||
| + | En el caso de un merge **a 3 bandas**, pasa lo mismo. Hay que estudiar lo que ha ocurrido al hacer el merge para saber cómo podemos volver al estado anterior. | ||
| ==== Cómo deshacer una rebase y un Cherry-pick ==== | ==== Cómo deshacer una rebase y un Cherry-pick ==== | ||
| + | |||
| + | Para deshacer un **rebase**, lo mismo que antes, tenemos que saber cómo estaban las cosas antes para recuperarlas. | ||
| + | |||
| + | Lo mismo con deshacer **cherry-pick**, | ||
| ===== Git Flow ===== | ===== Git Flow ===== | ||
| + | |||
| + | Hasta ahora hemos ido viendo el **modelo general** de cómo trabajar con rama en git: | ||
| + | |||
| + | - Creamos una rama auxiliar | ||
| + | - Trabajamos en ella | ||
| + | - Fusionamos con **develop** | ||
| + | - Eliminamos la rama auxiliar | ||
| + | |||
| + | Esta metodología es un conjunto de buenas prácticas que nos aseguran no tener conflictos o al menos tener pocos y fáciles de resolver. | ||
| + | |||
| + | Cada empresa o programador puede tener su propio flujo. Hay una serie de métodos que comparte la comunidad. Uno de ellos es **git flow**, un modelo de // | ||
| + | |||
| + | {{ : | ||
| + | |||
| + | Tuvo mucho éxito y se ha convertido en uno de los métodos más utilizados. | ||
| + | |||
| ==== El modelo git flow ==== | ==== El modelo git flow ==== | ||
| + | |||
| + | Este modelo se basa es tener dos ramas principales: | ||
| + | |||
| + | * **master** branch | ||
| + | * **develop** branch | ||
| + | |||
| + | A diferencia del modelo general, ambas ramas estarán vivas siempre, nunca se borrarán. | ||
| + | |||
| + | Cada vez que el código alcance un estado tal que podamos publicar la aplicación, | ||
| + | |||
| + | Ambas ramas son las únicas que compartiremos con el resto de desarrolladores, | ||
| + | |||
| + | Este modelo además tiene una serie tipos de ramas auxiliares: | ||
| + | |||
| + | * **feature** branch: cada vez que programemos algo nuevo, abrimos este tipo de rama (se abre a partir de la última versión de **develop**). Al terminar, se vuelca en develop y se borra la rama auxiliar. | ||
| + | * **bugfix** branch: similares a las // | ||
| + | * **hotfix** branch: para corregir errores que detectamos en **master**. Nace desde master y al terminar, la rama se fusionará con develop y master. | ||
| + | * **release** branch: sirven para hacer una puesta a producción. Cuando develop está lista para pasar a producción, | ||
| + | |||
| + | Toda esta metodología existe en un software llamado **git flow** que se puede instalar en Windows (ya lo incluye), Linux y macOS. | ||
| + | |||
| + | Empezar a usar git flow en un repositorio: | ||
| + | |||
| + | < | ||
| + | git flow init | ||
| + | </ | ||
| + | |||
| + | Nos preguntará cómo queremos llamar a las ramas. Git nos da unas propuestas, pero podemos poner lo que queramos. | ||
| + | |||
| + | El uso sería el siguiente: | ||
| + | |||
| + | Iniciar rama: | ||
| + | |||
| + | < | ||
| + | git flow tipo_rama start nombre_rama | ||
| + | </ | ||
| + | |||
| + | Cuando hayamos terminado con la rama: | ||
| + | |||
| + | < | ||
| + | git flow finish | ||
| + | </ | ||
| + | |||
| + | <WRAP center round important 60%> | ||
| + | Si no estuviésemos en esa rama, el comando tendría que ser '' | ||
| + | </ | ||
| + | |||
| + | |||
| + | Usando el programa **git flow**, no tenemos que recordar de dónde salen las ramas, a dónde se deben volcar, ya se encarga él. | ||
| ==== Poniendo en práctica git flow ==== | ==== Poniendo en práctica git flow ==== | ||
| + | |||
| + | Vemos el uso del programa **git flow** para facilitar el flujo de trabajo git flow. | ||
| + | |||
| + | Empezamos: | ||
| + | |||
| + | < | ||
| + | git flow init | ||
| + | </ | ||
| + | |||
| + | <WRAP center round todo 60%> | ||
| + | Poner ejemplo de salida por consola | ||
| + | </ | ||
| + | |||
| + | Comenzamos a trabajar: | ||
| + | |||
| + | < | ||
| + | git flow feature start readme | ||
| + | </ | ||
| + | |||
| + | Y se habrá creado la rama '' | ||
| + | |||
| + | Cuando terminemos de hacer cambios, cerramos la rama: | ||
| + | |||
| + | < | ||
| + | git flow feature finish readme | ||
| + | </ | ||
| + | |||
| + | <WRAP center round tip 60%> | ||
| + | Recordemos que si vamos a cerrar la rama en la que estamos, podemos abreviar con '' | ||
| + | </ | ||
| + | |||
| + | |||
| + | <WRAP center round info 60%> | ||
| + | El git flow está programado de manera que los **merge** sean a tres bandas, evitando el fast forward | ||
| + | </ | ||
| + | |||
| + | Cuando tengamos listo todo para publicar una versión, creamos un rama // | ||
| + | |||
| + | < | ||
| + | git flow release start v1.0.0 | ||
| + | </ | ||
| + | |||
| + | Al terminar: | ||
| + | |||
| + | < | ||
| + | git flow finish | ||
| + | </ | ||
| + | |||
| + | Si tenemos un error en producción, | ||
| + | |||
| + | < | ||
| + | git flow hotfix start idioma | ||
| + | </ | ||
| + | |||
| + | Git flow ya sabe que tiene que crearla a partir de master. Cuando terminemos de hacer los cambios y queramos cerrar la rama: | ||
| + | |||
| + | < | ||
| + | git flow finish | ||
| + | </ | ||
| + | |||
| + | El " | ||
| ===== Conclusión ===== | ===== Conclusión ===== | ||
| + | |||
| + | Hemos aprendido a trabajar con ramas, que es básicamente trabajar con referencias. | ||
| + | |||
| + | El **rebase** solo se puede hacer si la rama origen no la hemos subido al repositorio remoto. | ||
| + | |||
| + | El cherry-pick es muy útil para cuando no queremos traernos todo el trabajo de una rama. | ||
| + | |||
| + | El modelo git flow permite un flujo de trabajo con bajo número de conflictos. | ||
| ===== Recursos ===== | ===== Recursos ===== | ||
informatica/programacion/cursos/control_version_git_avanzado/branching.1685351625.txt.gz · Última modificación: por tempwin
