Un flujo de trabajo de Git es una receta o recomendación sobre cómo utilizar Git para realizar el trabajo de una manera consistente y productiva. Los flujos de trabajo de Git animan a los usuarios a aprovechar Git de forma efectiva y consistente. Git ofrece mucha flexibilidad en la forma en que los usuarios gestionan los cambios. Dado que Git se centra en la flexibilidad, no existe un proceso estandarizado sobre cómo interactuar con Git. Cuando se trabaja con un equipo en un proyecto gestionado con Git, es importante asegurarse de que todo el equipo está de acuerdo en cómo se aplicará el flujo de cambios. Para asegurar que el equipo está en la misma página, se debe desarrollar o seleccionar un flujo de trabajo Git acordado. Hay varios flujos de trabajo Git publicados que pueden ser adecuados para tu equipo. Aquí discutiremos algunas de estas opciones de flujo de trabajo.
La variedad de posibles flujos de trabajo puede hacer difícil saber por dónde empezar cuando se implementa Git en el lugar de trabajo. Esta página proporciona un punto de partida mediante el estudio de los flujos de trabajo de Git más comunes para los equipos de software.
Mientras lees, recuerda que estos flujos de trabajo están diseñados para ser directrices más que reglas concretas. Queremos mostrarte lo que es posible, para que puedas mezclar y combinar aspectos de diferentes flujos de trabajo para adaptarlos a tus necesidades individuales.
¿Qué es un flujo de trabajo Git exitoso?
Al evaluar un flujo de trabajo para tu equipo, lo más importante es que tengas en cuenta la cultura de tu equipo. Quieres que el flujo de trabajo mejore la eficacia de tu equipo y no sea una carga que limite la productividad. Algunos aspectos a tener en cuenta al evaluar un flujo de trabajo Git son:
- ¿Este flujo de trabajo se adapta al tamaño del equipo?
- ¿Es fácil deshacer los errores y equivocaciones con este flujo de trabajo?
- ¿Este flujo de trabajo impone alguna nueva sobrecarga cognitiva innecesaria al equipo?
Flujo de trabajo centralizado
El flujo de trabajo centralizado es un gran flujo de trabajo de Git para los equipos en transición desde SVN. Al igual que Subversion, el flujo de trabajo centralizado utiliza un repositorio central para servir como único punto de entrada para todos los cambios en el proyecto. En lugar de trunk
, la rama de desarrollo por defecto se llama master
y todos los cambios se envían a esta rama. Este flujo de trabajo no requiere ninguna otra rama además de master
.
La transición a un sistema de control de versiones distribuido puede parecer una tarea desalentadora, pero no tienes que cambiar tu flujo de trabajo actual para aprovechar Git. Su equipo puede desarrollar proyectos exactamente de la misma manera que lo hacen con Subversion.
Sin embargo, el uso de Git para impulsar su flujo de trabajo de desarrollo presenta algunas ventajas sobre SVN. En primer lugar, proporciona a cada desarrollador su propia copia local de todo el proyecto. Este entorno aislado permite a cada desarrollador trabajar de forma independiente de todos los demás cambios en un proyecto: pueden añadir confirmaciones a su repositorio local y olvidarse por completo de los desarrollos ascendentes hasta que les resulte conveniente.
Segundo, te da acceso al robusto modelo de ramificación y fusión de Git. A diferencia de SVN, las ramas de Git están diseñadas para ser un mecanismo a prueba de fallos para integrar código y compartir cambios entre repositorios. El flujo de trabajo centralizado es similar a otros flujos de trabajo en cuanto a la utilización de un repositorio remoto alojado en el lado del servidor del que los desarrolladores empujan y tiran. En comparación con otros flujos de trabajo, el flujo de trabajo centralizado no tiene patrones definidos de solicitud de extracción o bifurcación. Un flujo de trabajo centralizado es generalmente más adecuado para los equipos que migran de SVN a Git y para los equipos de menor tamaño.
Cómo funciona
Los desarrolladores comienzan clonando el repositorio central. En sus propias copias locales del proyecto, editan los archivos y confirman los cambios como lo harían con SVN; sin embargo, estos nuevos commits se almacenan localmente – están completamente aislados del repositorio central. Esto permite a los desarrolladores aplazar la sincronización con el flujo ascendente hasta que estén en un punto de ruptura conveniente.
Para publicar los cambios en el proyecto oficial, los desarrolladores «empujan» su rama local master
al repositorio central. Esto es el equivalente a svn commit
, excepto que añade todos los commits locales que no están ya en la rama central master
.
Inicializar el repositorio central
Primero, alguien tiene que crear el repositorio central en un servidor. Si es un proyecto nuevo, puedes inicializar un repositorio vacío. Si no, tendrás que importar un repositorio Git o SVN existente.
Los repositorios centrales deben ser siempre repositorios desnudos (no deben tener un directorio de trabajo), que se pueden crear de la siguiente manera:
ssh user@host git init --bare /path/to/repo.git
Asegúrese de utilizar un nombre de usuario SSH válido para user
, el dominio o la dirección IP de tu servidor para host
, y la ubicación donde te gustaría almacenar tu repo para /path/to/repo.git
. Ten en cuenta que la extensión .git
se añade convencionalmente al nombre del repositorio para indicar que se trata de un repositorio desnudo.
Repositorios centrales alojados
Los repositorios centrales suelen crearse a través de servicios de alojamiento de Git de terceros como Bitbucket Cloud o Bitbucket Server. El proceso de inicialización de un repositorio desnudo discutido anteriormente es manejado por usted por el servicio de alojamiento. El servicio de alojamiento proporcionará entonces una dirección para el repositorio central para acceder desde tu repositorio local.
Clonar el repositorio central
A continuación, cada desarrollador crea una copia local de todo el proyecto. Esto se consigue a través del comando git clone
:
git clone ssh://user@host/path/to/repo.git
Cuando clonas un repositorio, Git añade automáticamente un acceso directo llamado origin
que apunta al repositorio «padre», bajo el supuesto de que querrás interactuar con él más adelante.
Hacer cambios y confirmar
Una vez que el repositorio se clona localmente, un desarrollador puede hacer cambios utilizando el proceso estándar de confirmación de Git: editar, poner en escena y confirmar. Si no estás familiarizado con el área de staging, es una forma de preparar un commit sin tener que incluir todos los cambios en el directorio de trabajo. Esto te permite crear commits muy focalizados, incluso si has hecho muchos cambios locales.
git status # View the state of the repo git add # Stage a file git commit # Commit a file
Recuerda que como estos comandos crean commits locales, John puede repetir este proceso tantas veces como quiera sin preocuparse de lo que ocurre en el repositorio central. Esto puede ser muy útil para grandes características que necesitan ser divididas en trozos más simples y atómicos.
Empujar nuevos commits al repositorio central
Una vez que el repositorio local tiene nuevos cambios comprometidos. Estos cambios necesitarán ser empujados para compartirlos con otros desarrolladores del proyecto.
git push origin master
Este comando empujará los nuevos cambios confirmados al repositorio central. Al empujar los cambios al repositorio central, es posible que se hayan empujado previamente actualizaciones de otro desarrollador que contengan código que entre en conflicto con las actualizaciones que se pretenden empujar. Git emitirá un mensaje indicando este conflicto. En esta situación, git pull
deberá ejecutarse primero. Este escenario de conflicto se ampliará en la siguiente sección.
Gestión de conflictos
El repositorio central representa el proyecto oficial, por lo que su historial de commits debe ser tratado como sagrado e inmutable. Si los commits locales de un desarrollador difieren del repositorio central, Git se negará a empujar sus cambios porque esto sobrescribiría los commits oficiales.
Antes de que el desarrollador pueda publicar su característica, necesita obtener los commits centrales actualizados y volver a basar sus cambios sobre ellos. Esto es como decir: «Quiero añadir mis cambios a lo que todos los demás ya han hecho». El resultado es un historial perfectamente lineal, al igual que en los flujos de trabajo tradicionales de SVN.
Si los cambios locales entran en conflicto directo con los commits del upstream, Git detendrá el proceso de rebase y te dará la oportunidad de resolver manualmente los conflictos. Lo bueno de Git es que utiliza los mismos comandos git status
y git add
tanto para generar commits como para resolver conflictos de fusión. Esto facilita a los nuevos desarrolladores la gestión de sus propias fusiones. Además, si se meten en problemas, Git hace que sea muy fácil abortar todo el rebase y volver a intentarlo (o ir a buscar ayuda).
Ejemplo
Tomemos un ejemplo general de cómo colaboraría un equipo pequeño típico utilizando este flujo de trabajo. Veremos cómo dos desarrolladores, Juan y María, pueden trabajar en características separadas y compartir sus contribuciones a través de un repositorio centralizado.
Juan trabaja en su característica
En su repositorio local, John puede desarrollar características utilizando el proceso de commit estándar de Git: edit, stage, y commit.
Recuerda que como estos comandos crean commits locales, John puede repetir este proceso tantas veces como quiera sin preocuparse de lo que ocurre en el repositorio central.
Mary trabaja en su característica
Mientras tanto, María está trabajando en su propia característica en su propio repositorio local utilizando el mismo proceso de edición/etapa/commit. Al igual que Juan, a ella no le importa lo que está pasando en el repositorio central, y realmente no le importa lo que Juan está haciendo en su repositorio local, ya que todos los repositorios locales son privados.
Juan publica su característica
Una vez que Juan termina su característica, debe publicar sus commits locales en el repositorio central para que otros miembros del equipo puedan acceder a ella. Puede hacerlo con el comando git push
, de la siguiente manera:
git push origin master
Recuerda que origin
es la conexión remota al repositorio central que Git creó cuando Juan lo clonó. El argumento master
le dice a Git que intente que la origin
de la rama master
se parezca a su rama local master
. Como el repositorio central no se ha actualizado desde que Juan lo clonó, esto no dará lugar a ningún conflicto y el push funcionará como se esperaba.
María intenta publicar su función
Veamos qué ocurre si María trata de empujar su característica después de que Juan haya publicado con éxito sus cambios en el repositorio central. Puede utilizar exactamente el mismo comando push:
git push origin master
Pero, como su historia local se ha desviado del repositorio central, Git rechazará la petición con un mensaje de error bastante verboso:
error: failed to push some refs to '/path/to/repo.git' hint: Updates were rejected because the tip of your current branch is behind hint: its remote counterpart. Merge the remote changes (e.g. 'git pull') hint: before pushing again. hint: See the 'Note about fast-forwards' in 'git push --help' for details.
Esto evita que María sobrescriba los commits oficiales. Tiene que tirar de las actualizaciones de Juan a su repositorio, integrarlas con sus cambios locales, y luego volver a intentarlo.
María hace un rebase sobre el/los commit(s) de Juan
Mary puede utilizar git pull
para incorporar los cambios del upstream a su repositorio. Este comando es algo así como svn update
-trae todo el historial de commits de aguas arriba al repositorio local de María y trata de integrarlo con sus commits locales:
git pull --rebase origin master
La opción --rebase
le dice a Git que mueva todos los commits de María a la punta de la rama master
después de sincronizarla con los cambios del repositorio central, como se muestra a continuación:
El pull seguiría funcionando si se olvidara esta opción, pero se acabaría con un superfluo «merge commit» cada vez que alguien necesitara sincronizar con el repositorio central. Para este flujo de trabajo, siempre es mejor rebase en lugar de generar un commit de fusión.
Mary resuelve un conflicto de fusión
El rebasamiento funciona transfiriendo cada commit local a la rama master
actualizada de uno en uno. Esto significa que se detectan los conflictos de fusión en una base de compromiso por compromiso en lugar de resolver todos ellos en un compromiso de fusión masiva. Esto mantiene tus confirmaciones tan enfocadas como sea posible y hace un historial de proyecto limpio. A su vez, esto hace que sea mucho más fácil averiguar dónde se introdujeron los errores y, si es necesario, revertir los cambios con un impacto mínimo en el proyecto.
Si María y Juan están trabajando en características no relacionadas, es poco probable que el proceso de rebasado genere conflictos. Pero si lo hace, Git detendrá el rebase en el commit actual y emitirá el siguiente mensaje, junto con algunas instrucciones relevantes:
CONFLICT (content): Merge conflict in
Lo bueno de Git es que cualquiera puede resolver sus propios conflictos de fusión. En nuestro ejemplo, María simplemente ejecutaría un git status
para ver dónde está el problema. Los archivos en conflicto aparecerán en la sección de rutas no fusionadas:
# Unmerged paths: # (use "git reset HEAD ..." to unstage) # (use "git add/rm ..." as appropriate to mark resolution) # # both modified:
Entonces, ella editará el/los archivos a su gusto. Una vez que esté contenta con el resultado, puede preparar el archivo(s) de la manera habitual y dejar que git rebase
haga el resto:
git add git rebase --continue
Y eso es todo. Git pasará al siguiente commit y repetirá el proceso para cualquier otro commit que genere conflictos.
Si llegas a este punto y te das cuenta y no tienes ni idea de lo que está pasando, no te asustes. Simplemente ejecuta el siguiente comando y volverás al punto de partida:
git rebase --abort
Mary publica con éxito su feature
Después de que haya terminado de sincronizar con el repositorio central, María podrá publicar sus cambios con éxito:
git push origin master
A dónde ir desde aquí
Como puedes ver, es posible replicar un entorno de desarrollo tradicional de Subversion usando sólo un puñado de comandos Git. Esto es genial para la transición de los equipos fuera de SVN, pero no aprovecha la naturaleza distribuida de Git.
El flujo de trabajo centralizado es genial para equipos pequeños. El proceso de resolución de conflictos detallado anteriormente puede formar un cuello de botella a medida que su equipo escala en tamaño. Si tu equipo se siente cómodo con el flujo de trabajo centralizado pero quiere agilizar sus esfuerzos de colaboración, definitivamente vale la pena explorar los beneficios del flujo de trabajo de rama de características. Al dedicar una rama aislada a cada característica, es posible iniciar discusiones en profundidad en torno a las nuevas adiciones antes de integrarlas en el proyecto oficial.
Otros flujos de trabajo comunes
El flujo de trabajo centralizado es esencialmente un bloque de construcción para otros flujos de trabajo Git. La mayoría de los flujos de trabajo Git populares tendrán algún tipo de repo centralizado del que los desarrolladores individuales empujarán y sacarán. A continuación discutiremos brevemente otros flujos de trabajo Git populares. Estos flujos de trabajo extendidos ofrecen patrones más especializados con respecto a la gestión de ramas para el desarrollo de características, correcciones en caliente, y la eventual liberación.
Bifurcación de características
La bifurcación de características es una extensión lógica del flujo de trabajo centralizado. La idea central detrás del flujo de trabajo de ramificación de características es que todo el desarrollo de características debe tener lugar en una rama dedicada en lugar de la rama master
. Esta encapsulación facilita que varios desarrolladores trabajen en una característica particular sin perturbar el código base principal. También significa que la rama master
nunca debería contener código roto, lo cual es una gran ventaja para los entornos de integración continua.
Flujo de trabajo de Gitflow
El flujo de trabajo de Gitflow se publicó por primera vez en una entrada del blog de Vincent Driessen en nvie, de gran prestigio, en 2010. El flujo de trabajo Gitflow define un modelo de ramificación estricto diseñado en torno a la liberación del proyecto. Este flujo de trabajo no añade ningún concepto o comando nuevo más allá de lo que se requiere para el flujo de trabajo de ramificación de características. En cambio, asigna roles muy específicos a las diferentes ramas y define cómo y cuándo deben interactuar.
Flujo de trabajo de bifurcación
El flujo de trabajo de bifurcación es fundamentalmente diferente a los otros flujos de trabajo discutidos en este tutorial. En lugar de utilizar un único repositorio del lado del servidor para actuar como la base de código «central», da a cada desarrollador un repositorio del lado del servidor. Esto significa que cada colaborador tiene no uno, sino dos repositorios Git: uno local privado y otro público del lado del servidor.
Directrices
No hay un flujo de trabajo Git que sirva para todo. Como se ha dicho anteriormente, es importante desarrollar un flujo de trabajo de Git que sea una mejora de la productividad para tu equipo. Además de la cultura del equipo, un flujo de trabajo también debe complementar la cultura empresarial. Las funciones de Git, como las ramas y las etiquetas, deben complementar el calendario de publicación de tu empresa. Si tu equipo utiliza un software de gestión de proyectos de seguimiento de tareas, es posible que quieras utilizar ramas que se correspondan con las tareas en curso. Además, algunas pautas a tener en cuenta a la hora de decidir un flujo de trabajo son:
Ramificaciones de corta duración
Cuanto más tiempo viva una rama separada de la rama de producción, mayor será el riesgo de conflictos de fusión y desafíos de despliegue. Las ramas de corta duración promueven fusiones y despliegues más limpios.
Minimizar y simplificar las reversiones
Es importante tener un flujo de trabajo que ayude a prevenir proactivamente las fusiones que tendrán que ser revertidas. Un flujo de trabajo que pruebe una rama antes de permitir que se fusione en la rama master
es un ejemplo. Sin embargo, los accidentes ocurren. Dicho esto, es beneficioso tener un flujo de trabajo que permita revertir fácilmente y que no interrumpa el flujo para otros miembros del equipo.
Combina con un calendario de lanzamientos
Un flujo de trabajo debe complementar el ciclo de lanzamientos de desarrollo de software de tu negocio. Si planea lanzar varias veces al día, querrá mantener su master
rama estable. Si tu calendario de lanzamientos es menos frecuente, puedes considerar el uso de etiquetas Git para etiquetar una rama a una versión.
Resumen
En este documento hemos hablado de los flujos de trabajo Git. Hemos profundizado en un Flujo de Trabajo Centralizado con ejemplos prácticos. Ampliando el Flujo de Trabajo Centralizado discutimos flujos de trabajo especializados adicionales. Algunos puntos clave de este documento son:
- No hay un flujo de trabajo de Git que sirva para todos
- Un flujo de trabajo debe ser simple y mejorar la productividad de tu equipo
- Tus requisitos de negocio deben ayudar a dar forma a tu flujo de trabajo de Git
Para leer sobre el siguiente flujo de trabajo de Git echa un vistazo a nuestro completo desglose del flujo de trabajo de la rama de características.