tutorial force git github version-control

force - git rebase commit



¿Sigue `git rebase master` seguido de` git pull--rebase` y luego `git push--force`? (3)

Aquí está la regla básica: siempre tire antes de rebase.

Antes de volver a establecer una base de trabajo en la parte superior de la rama de integración (ya sea master o de develop ), debe asegurarse de tener los últimos commits que la gente haya puesto en la rama de integración. Entonces, esto es lo que debes hacer:

  1. Guarda tu trabajo. Haga todo lo que desee guardar en su rama de características ( my-branch ) y asegúrese de que el git status no reporte cambios no confirmados.

  2. Consulte la rama de integración ( git checkout master , por ejemplo) y git pull para obtener la última. En este punto, su master sucursal local es exactamente el mismo que el origin/master su sucursal de seguimiento remoto.

  3. Echa un vistazo a la rama de tu característica (atajo: git checkout - para ''saltar de regreso a la rama anterior'').

  4. Rebase: git rebase master para poner su rama de funciones sobre la última confirmación master .

  5. Publicar: git push -u --force-with-lease origin my-branch:my-branch (la primera vez, para configurar el seguimiento de manera apropiada para su rama de características) y git push --force-with-lease (en cualquier otro momento) .

Esta serie de pasos asegurará que sus ramas de características estén siempre limpias en la parte superior de la master y sean fáciles de combinar cuando llegue el momento.

Volví a basar mi-rama sobre maestro:

my-branch>> git rebase origin/master

Luego veo el estado:

➜ supporTool git:(my-branch) git status On branch my-branch Your branch and ''origin/my-branch'' have diverged, and have 12 and 11 different commits each, respectively. (use "git pull" to merge the remote branch into yours) nothing to commit, working tree clean

Entiendo que la orden de confirmaciones ha cambiado localmente frente a remota (micursal)

Entonces, ¿esto significa que siempre tengo que hacer git pull --rebase y luego git push --force para hacer el control remoto con el mismo orden de confirmación?

1) ¿por qué necesito tirar si soy el único desarrollador en esta rama y estaba por delante del control remoto, antes de volver a basarme en el maestro?

2) ¿Qué sucede si alguien más empujó algo al control remoto después de que hice la rebase localmente pero antes de presionar con git push --force ?


No es la orden de los compromisos lo que ha cambiado. Son los compromisos los que han cambiado.

Para comprender realmente todo esto, necesitas saber tres cosas:

  • Los nombres de las sucursales solo apuntan a commits. Específicamente, cualquier nombre de rama, como my-branch , apunta a una única confirmación, que Git denomina "la punta de la rama" o la sugerencia de sugerencia .
  • Las ramas -en las que los humanos pensamos que son ramas, es decir- son cadenas de compromisos. Este encadenamiento se logra internamente mediante flechas hacia atrás: cada compromiso apunta hacia atrás a una confirmación previa. Recoge una cadena de confirmaciones, con estas flechas internas hacia atrás, y obtienes una sugerencia de rama, apuntada por un nombre de rama, que contiene una serie de confirmaciones:

    ...<- o <- o <- o <- o <- o <-- branch1 / o <- o <- o <-o <-- branch2

    (El hecho de que las flechas internas estén todas al revés generalmente no es terriblemente importante, así que tiendo a dibujar esto como:

    o--o--o <-- master

    que es mucho más legible)

  • La forma en que git rebase funciona es que copia commits. Una vez hecho esto, copia los commits, ¡cambia uno (y solo uno!) Nombre de rama para apuntar a la nueva confirmación de punta.

Por ejemplo, digamos que la condición de inicio se ve así:

...--o--A--B <-- master, origin/master / C--D--E--F <-- my-branch, origin/my-branch

Aquí tenemos, en su repositorio, master (un nombre de bifurcación local ordinario regular) que apunta a cometer B : les he dado aquí todos los nombres de una letra, que son más hábiles que los hashes de 40 caracteres, y el origin/master (un llamado nombre de rama de seguimiento remoto ). El origin/master nombre de rama de seguimiento remoto también apunta a confirmar B

Los compromisos C a F están en su rama my-branch , pero no en branch master . (Esto le da 4 confirmaciones de este tipo, frente a las 11 que se muestran en su git status , por lo que este dibujo se simplifica). Su origin/my-branch seguimiento de seguimiento remoto también apunta a cometer F

Pero luego ejecutó git rebase master . Esto copia cada confirmación que necesita copiar, que en este caso es todo C , D , E y F Git obtiene esta lista enumerando todas las confirmaciones que están en su rama my-branch actual, que no están en la rama master . Tenga en cuenta que ambas ramas contienen commit A , y cualquier commit anterior, por lo que estos son los commits alcanzables desde my-branch que git rebase arroja fuera de la lista. Esta idea de accesibilidad depende de las flechas que apuntan hacia atrás; aquí es donde realmente importa el hecho de que las flechas apuntan hacia atrás.

Entonces, veamos qué pasa si y cuando git rebase copia exitosamente esos commits. Obtenemos:

...--o--A--B <-- master, origin/master / / / C''-D''-E''-F'' <-- my-branch / C--D--E--F <-- origin/my-branch

La diferencia entre la confirmación C , la original y la confirmación C'' , la copia, es que C'' está construido encima de B Tiene lo que sea diferente entre A y B , más los cambios de A a C 1 De forma similar, D'' está construido encima de C'' , y así sucesivamente.

Cuando terminó la rebase, movió el nombre de la rama actual para apuntar a la nueva confirmación de punta, F'' . Pero no mueve ningún otro nombre. En particular, no mueve origin/my-branch , lo cual tiene mucho sentido, ya que origin/my-branch está destinado a recordar lo que tu Git vio en el Git de origen para my-branch , cuando tu Git habló por última vez con su Git a través de git fetch o git push .

Recuerde, toda esta acción ha tenido lugar en su repositorio. Es de suponer que el repositorio sobre el origin ha cambiado. Esto "presumiblemente" puede meternos en problemas y ahí es donde entra el tema "con arrendamiento", pero antes de profundizar en eso, eche un vistazo al gráfico anterior. Cuenta cuántos commits están en my-branch y aún no están en origin/my-branch . Luego, cuente cuántos commits están en origin/my-branch pero no en my-branch .

Recuerde, para este conteo, usamos accesibilidad : seguimos las flechas internas hacia atrás. Commit A es el primero en ambas ramas. La actualización my-branch incluye A--B--C''-D''-E''-F'' : tiene A El antiguo origin/my-branch incluye ACDEF : tiene A también. Así que dejamos de contar al tocar A , y eso significa que my-branch ahora tiene cinco (¡no cuatro!) Commits que origin/my-branch carece, mientras que origin/my-branch tiene cuatro commits que my-branch carece. (Compare esto con su git status : usted comenzó con 11 commits en my-branch que no estaban también en master , y ganó 1 más de la misma manera, por lo que tiene 12-vs-11).

Todos los commits restantes en origin/my-branch son, por supuesto, los que hemos copiado al rebasar. El extra en my-branch es solo el que recogimos que también está en master : commit B

1 Si los cambios de A a C terminan reemplazando , en lugar de aumentar, los cambios de A a B , entonces el árbol asociado con C'' coincide con el árbol de C Sin embargo, el ID padre sigue siendo diferente: el padre de C es A , mientras que el padre de C'' es B Esto solo basta para hacer que los dos compromisos sean diferentes: tendrán un hash SHA-1 de 40 caracteres diferente.

Empujar, con o sin fuerza y ​​tal vez arrendar

Es en este punto, mientras tiene este nuevo gráfico, que ha decidido git push para obtener algunas o todas sus nuevas confirmaciones copiadas en el repositorio en origin .

El truco más fundamental de git push , la parte que siempre hace, consiste en contactar a otro Git, a través de una URL, y tomar algunos de tus commit (s) y entregárselos a ese otro Git. La URL normalmente proviene del nombre remoto, origin . Un control remoto es, en una primera aproximación, solo un nombre corto para la URL completa. Entonces la parte interesante restante, de hecho la parte más interesante, es este proceso de entrega.

Para entregar algunos commit (s), le dices a tu Git que busque commit tips específicos, usualmente al darle a tu Git un nombre de rama como my-branch . Para decirle a tu Git qué decirle al otro Git, también le das a tu Git otro nombre de sucursal. Separe estos dos nombres de rama con dos puntos, como en:

git push origin my-branch:my-branch

El nombre de la izquierda es cómo le dices a tu Git qué consejo comprometer a presionar. El nombre de la derecha es cómo le dices a tu Git qué rama te gustaría que su Git cambie en su extremo.

Por lo general, por supuesto, estas son las mismas, por lo que su Git le permite dejar de lado la repetición repetitiva y repetitiva de la repetición :my-branch . (Esta frase la presenta el Departamento del Departamento de Redundancia.) Entonces, simplemente ejecuta:

git push origin my-branch

o, con una versión moderna (2.0 o posterior) de Git, 2 la aún más simple:

git push

(esto resuelve las cosas correctas de la configuración ascendente de su sucursal actual). Pero es importante tener en cuenta que esto "significa" git push origin my-branch:my-branch , es decir, use my-branch para encontrar el compromiso de propina, haga que su Git entregue su Git que se compromete, y solicite a su Git su Git para establecer su my-branch . (Así es cómo y por qué es posible usar diferentes nombres de sucursales en su repositorio frente a su repositorio; no es algo que necesite con frecuencia, pero cuando lo desea, resulta extremadamente útil).

Cuando su Git se contacta con su Git, su Git comienza entregando estos ID de confirmación de propinas. (Puede insertar más de una rama a la vez, por lo tanto, "ID de propinas" está en plural.) Su Git luego verifica si ya tienen las confirmaciones -por sus ID de hash exclusivas de 40 caracteres- y, de no ser así, tiene su Git envía el contenido real de la confirmación, incluidos los ID principales de los commits. Su Git pide más contenidos de objetos según sea necesario. Esta conversación continúa hasta que tu Git le haya dado a su Git algo que ellos reconocen: una identificación hash que ya tienen, como por ejemplo B en nuestro dibujo.

En este punto, su Git ha entregado todos los commits que debe darles (más cualquier otro objeto interno de Git, árboles y blobs, todos los cuales tienen sus propias ID de hash exclusivas), y ahora su Git comienza el proceso de evaluación.

2 El elemento clave aquí es la configuración de push.default , que en Git 2.0-and-later es simple , si no la cambia. En versiones anteriores de Git, se configuró para que matching por defecto.

Forzar o no forzar

Git se basa en la idea de agregar cosas nuevas .

Específicamente, siempre que realice una nueva confirmación, simplemente la agrega a la cadena de compromiso existente. Independientemente de la rama en la que se encuentre ahora, agrega una nueva confirmación y mueve la etiqueta de la rama hacia adelante para acomodarla:

before: ...--C--D--E <-- my-branch after: ...--C--D--E--F <-- my-branch

Por lo tanto, Git espera que las etiquetas de las sucursales se muevan así: para agregar nuevas confirmaciones.

Entonces, para un git push normal, sin --force , Git permitirá el empuje si el resultado solo agrega nuevas confirmaciones . No importa cuán complejo sea el empuje:

before: ...--o--o--o <-- branch after: o-------o / / ...--o--o--o--o--o--o--o---o <-- branch / / o--o--o--o

la pregunta clave es: ¿solo agrega o deja caer algo? Lo anterior está permitido, pero el siguiente no es:

before: ...--o--o--o--o <-- branch after: ...--o--o--x--x / o--o <-- branch

Una etiqueta de rama solo apunta a una confirmación, así que si Git permitiera este empujón, las últimas dos confirmaciones en la fila superior (ahora marcadas x--x ) serían "olvidadas".

Entonces, mira hacia atrás en tus gráficos de rebase. Si empujas la nueva my-branch , todas las confirmaciones originales -las que copiaste para ponerlas después de la punta del maestro- serían "olvidadas".

Por supuesto, esto es precisamente lo que quieres. Estás abandonando los originales a favor de las nuevas copias. Quieres que el origin abandone también.

Aquí es donde --force entra: un git push --force establece la "bandera de fuerza", que tu Git pasa al otro Git. Su Git no necesita obedecer la bandera de la fuerza en absoluto, pero si lo hacen, simplemente les dice: "sí, adelante y olvida algunos compromisos".

La bandera de la fuerza es demasiado poderosa

Ahora, siempre que el repositorio Git de origin no haya cambiado aquí, usar --force o su equivalente funciona bien. CDEF tu cadena de compromiso CDEF y estás presionando las copias, así que si "olvidan" el CDEF original, está bien.

Si my-branch es realmente privada para ti, hemos terminado. No necesitamos preocuparnos por otras posibilidades. Pero, ¿y si no lo es? ¿Qué pasaría si alguien más hubiera dicho "oh, wow, rama my-branch tiene cosas geniales, permítanme agregar commit G y push" y lo hicieron y el Git over origin ahora tiene la cadena CDEFG :

...--o--A--B <-- master / / / C''-D''-E''-F'' [proposed replacement] / C--D--E--F--G <-- my-branch

Tenga en cuenta la falta de origin/ aquí: este es un dibujo del repositorio en origin ; entonces no tiene origin/ en sus propios nombres. Es por eso que F'' es la punta del reemplazo propuesto para my-branch .

Si su Git toma el reemplazo propuesto, su Git perderá el compromiso G No tienes commit G , por lo que no lo copiaste en un nuevo G'' .

Fuerza con arrendamiento

La idea detrás de la opción --force-with-lease realmente se toma de las instrucciones de "multiprocesamiento" de las computadoras "compare and swap" o CAS, e internamente, en realidad se llama la opción "cas". La forma en que funciona es que tu Git le dice a su Git: "Creo que tus puntos my-branch cometen F , y me gustaría que lo cambies por la fuerza a F'' ".

Si, de hecho, la my-branch de my-branch Git apunta hacia F , el empuje de " --force-with-lease tiene éxito, reemplazando su my-branch para que ahora apunte a la F'' que les diste.

Si, por otro lado, se actualizó la my-branch git de Git, la inserción --force-with-lease falla. Ahora puede ejecutar git fetch para traer nuevos commits y averiguar dónde apunta realmente su etiqueta my-branch . Tenga en cuenta que su Git obtiene su noción de dónde "su" rama señala ("Creo que su my-branch es ...") desde su origin/my-branch . Hay variantes de forzar con arrendamiento que le permiten controlar esto más finamente, pero es poco probable que las necesite.

Eso es todo lo que hace: simplemente modifica la "fuerza" fuerte en una "fuerza" un poco más débil, pero solo si ... ". Sin embargo, si intenta cooperar con otra persona, trabajando en una sucursal que se actualiza o reescribe de esta manera y se envía a través de un servidor central, "forzar con arrendamiento" puede ser muy útil.


git rebase no está destinado a ser utilizado en una sucursal pública (principal o rama de desarrollo si se siguen las características o el flujo de trabajo de arreglo)

  1. Porque el cambio de bases cambia el historial
  2. ¿Dónde vas a rebase exactamente?