Reescribiendo la historia con Git Rebase

En el flujo de trabajo fundamental de Git, usted desarrolla una nueva característica en una rama de tema dedicada, y luego la vuelve a combinar en una rama de producción una vez que finaliza. Esto hace git merge Una herramienta integral para combinar ramas. Sin embargo, no es el único que ofrece Git..

Combinando ramas al unirlas

Como alternativa al escenario anterior, puede combinar las sucursales con el git rebase mando. En lugar de vincular las ramas con un compromiso de fusión, la redistribución mueve toda la rama de la característica a la punta de dominar Como se muestra abajo.

Combinando ramas con git rebase.

Esto sirve el mismo propósito que git merge, integrando compromisos de diferentes ramas. Pero hay dos razones por las que podríamos optar por una rebase sobre una fusión:

  • Resulta en una historia de proyecto lineal..
  • Nos da la oportunidad de limpiar los compromisos locales..

En este tutorial, exploraremos estos dos casos de uso comunes de git rebase. Desafortunadamente, los beneficios de git rebase venir en una compensación Cuando se usa incorrectamente, puede ser una de las operaciones más peligrosas que puede realizar en un repositorio Git. Por lo tanto, también vamos a echar un vistazo cuidadoso a los peligros de rebasar.

Prerrequisitos

Este tutorial asume que está familiarizado con los comandos básicos de Git y los flujos de trabajo de colaboración. Debe sentirse cómodo preparando y realizando instantáneas, desarrollando características en ramas aisladas, combinando ramas y empujando / tirando de las ramas a / desde repositorios remotos.

1. Rebasar para una historia lineal

El primer caso de uso que exploraremos implica un historial de proyectos divergente. Considere un repositorio donde su rama de producción haya avanzado mientras estaba desarrollando una característica:

Para rebautizar el característica ramificarse en el dominar rama, ejecutarías los siguientes comandos:

git checkout característica git rebase master

Esto trasplanta el característica rama desde su ubicación actual hasta la punta de la dominar rama:

Hay dos escenarios donde querrías hacer esto. En primer lugar, si la característica se basaba en las nuevas confirmaciones en dominar, ahora tendría acceso a ellos. En segundo lugar, si la función estuviera completa, ahora se configuraría para una combinación de avance rápido en dominar. En ambos casos, la rebasación da como resultado un historial lineal, mientras que git merge daría lugar a cometer combinaciones innecesarias.

Por ejemplo, considere lo que sucedería si integrara las confirmaciones en sentido ascendente con una combinación en lugar de una rebase:

función git checkout git merge master 

Esto nos habría dado un compromiso de fusión adicional en el característica rama. Además, esto sucedería cada vez que quisiera incorporar compromisos ascendentes en su función. Eventualmente, su historial de proyecto estará lleno de compromisos de fusión sin sentido.

Integración de compromisos upstream con una fusión

Este mismo beneficio se puede ver cuando se fusiona en la otra dirección. Sin rebase, integrando el acabado. característica ramificarse en dominar requiere una confirmación de fusión. Si bien esto es en realidad un compromiso de fusión significativo (en el sentido de que representa una característica completa), el historial resultante está lleno de bifurcaciones:

Integración de una característica completa con una combinación

Cuando se reajusta antes de fusionar, Git puede avanzar rápidamente dominar a la punta de característica. Encontrarás una historia lineal de cómo tu proyecto ha progresado en el registro de git salida-los compromisos en característica se agrupan cuidadosamente en la parte superior de los compromisos en dominar. Este no es necesariamente el caso cuando las ramas están unidas con un compromiso de fusión.

Rebasando antes de fusionar

Resolviendo conflictos

Cuando corres git rebase, Git toma cada compromiso en la rama y los mueve, uno por uno, a la nueva base. Si cualquiera de estas confirmaciones modifica la (s) misma (s) línea (s) de código que las confirmaciones ascendentes, se generará un conflicto..

los git merge El comando le permite resolver todos los conflictos de la rama al final de la fusión, que es uno de los propósitos principales de una confirmación de fusión. Sin embargo, funciona de manera un poco diferente cuando se está rebasando. Los conflictos se resuelven por compromiso. Así que si git rebase encuentra un conflicto, detendrá el procedimiento de rebase y mostrará un mensaje de advertencia:

Fusionar automáticamente readme.txt CONFLICT (contenido): Fusionar conflicto en readme.txt Error al fusionar los cambios ... Cuando haya resuelto este problema, ejecute "git rebase --continue". Si prefiere omitir este parche, ejecute "git rebase --skip" en su lugar. Para revisar la rama original y detener la reorganización, ejecute "git rebase --abort". 

Visualmente, así es como se ve tu historial de proyectos cuando git rebase encuentra un conflicto:

Los conflictos se pueden inspeccionar ejecutando. estado de git. La salida se ve muy similar a un conflicto de fusión:

Rutas sin combinar: (use "git reset HEAD ... "para desestabilizar) (use" git add ... "para marcar la resolución) ambos modificados: readme.txt no se agregaron cambios a commit (use" git add "y / o" git commit -a ") 

Para resolver el conflicto, abra el archivo en conflicto (readme.txt en el ejemplo anterior), encuentre las líneas afectadas y edítelas manualmente al resultado deseado. Luego, dile a Git que el conflicto se resuelve almacenando el archivo:

git add readme.txt 

Tenga en cuenta que esta es exactamente la misma forma en que marca una git merge El conflicto como resuelto. Pero recuerde que está en medio de una rebase, no quiere olvidarse del resto de las confirmaciones que necesitan ser movidas. El último paso es decirle a Git que termine de rebasar con el --continuar opción:

git rebase --continuar 

Esto moverá el resto de las confirmaciones, una por una, y si surge cualquier otro conflicto, deberá repetir este proceso nuevamente..

Si no desea resolver el conflicto, puede optar por cualquiera --omitir o --abortar banderas Este último es particularmente útil si no tienes idea de lo que está pasando y solo quieres volver a la seguridad.

# Ignorar el compromiso que causó el conflicto git rebase --skip # Abortar el rebase completo y volver al tablero de dibujo git rebase --abort 

2. Rebasando para limpiar compromisos locales

Hasta ahora, solo hemos estado usando git rebase mover ramas, pero es mucho más poderoso que eso. Pasando el -yo bandera, puede comenzar una sesión de rebasing interactiva. La reorganización interactiva le permite definir con precisión cómo se moverá cada confirmación a la nueva base. Esto le da la oportunidad de limpiar el historial de una característica antes de compartirla con otros desarrolladores.

Por ejemplo, digamos que terminaste de trabajar en tu característica Rama y ya está listo para integrarlo en dominar. Para comenzar una sesión interactiva de rebasado, ejecute el siguiente comando:

función git checkout git rebase -i master 

Esto abrirá un editor que contiene todas las confirmaciones en característica que están a punto de ser movidos:

pick 5c43c2b [Descripción de la confirmación más antigua] pick b8f3240 [Descripción de la segunda confirmación más antigua] pick c069f4a [Descripción de la confirmación más reciente] 

Este listado define lo que el característica La rama se verá después de la rebase. Cada línea representa un commit y el recoger El comando antes de cada hash de confirmación define lo que sucederá durante la rebase. Tenga en cuenta que las confirmaciones se enumeran de la más antigua a la más reciente. Al modificar este listado, obtienes un control completo sobre el historial de tu proyecto.

Si desea cambiar el orden de las confirmaciones, simplemente reordene las líneas. Si desea cambiar un mensaje de confirmación, utilice el expresar en otras palabras mando. Si quieres combinar dos confirmaciones, cambia la recoger orden a squash. Esto hará rodar todos los cambios en ese compromiso en el que está arriba. Por ejemplo, si aplastó la segunda confirmación en la lista anterior, el característica La rama se vería como la siguiente después de guardar y cerrar el editor:

Aplastando el segundo commit con un rebase interactivo

los editar El comando es particularmente poderoso. Cuando llega a la confirmación especificada, Git pausará el procedimiento de rebase, al igual que cuando encuentra un conflicto. Esto le da la oportunidad de alterar los contenidos de la confirmación con git commit - enmendar O incluso agregar más compromisos con el estándar. git añadir/git commit comandos Cualquier nuevo compromiso que agregue será parte de la nueva rama..

La reorganización interactiva puede tener un impacto profundo en el flujo de trabajo de su desarrollo. En lugar de preocuparse por dividir sus cambios en confirmaciones encapsuladas, puede concentrarse en escribir su código. Si terminó por comentar lo que debería ser un solo cambio en cuatro instantáneas separadas, entonces eso no es un problema: vuelva a escribir el historial con git rebase -i y aplastarlos a todos en un compromiso significativo.

3. Los peligros de rebasar

Ahora que tienes un entendimiento de git rebase, Podemos hablar de cuándo no usarlo. Internamente, el rebasado no mueve los compromisos a una nueva sucursal. En su lugar, crea nuevos compromisos que contienen los cambios deseados. Con esto en mente, la reorganización se visualiza mejor como lo siguiente:

Después de la rebase, el se compromete en característica Habrá diferentes hashes de confirmación. Esto significa que no solo reposicionamos una sucursal, literalmente, hemos reescrito nuestro historial de proyectos. Este es un efecto secundario muy importante de git rebase.

Cuando trabajas solo en un proyecto, reescribir la historia no es un gran problema. Sin embargo, tan pronto como comience a trabajar en un entorno de colaboración, puede ser muy peligroso. Si reescribe las confirmaciones que otros desarrolladores están usando (por ejemplo, las confirmaciones en la dominar se verá como si esos compromisos se desvanecieran la próxima vez que intenten hacer tu trabajo. Esto resulta en un escenario confuso del cual es difícil recuperarse.

Con esto en mente, nunca debe volver a hacer un borrador de las confirmaciones que se han enviado a un repositorio público a menos que esté seguro de que nadie ha basado su trabajo en ellas..

Conclusión

Este tutorial introdujo los dos casos de uso más comunes de git rebase. Hablamos mucho sobre cómo mover sucursales, pero tenga en cuenta que la rebasación se trata realmente de controlar el historial de su proyecto. El poder de reescribir los compromisos después del hecho lo libera para centrarse en sus tareas de desarrollo en lugar de dividir su trabajo en instantáneas aisladas.

Tenga en cuenta que la reorganización es una adición completamente opcional a su caja de herramientas de Git. Todavía puede hacer todo lo que necesita con el viejo llano. git merge comandos De hecho, esto es más seguro ya que evita la posibilidad de reescribir la historia pública. Sin embargo, si entiendes los riesgos., git rebase Puede ser una forma mucho más limpia de integrar sucursales en comparación con la combinación de compromisos.