¿Git Rebase afecta a la sucursal remota o twig local?

Estoy trabajando en mi twig llamada "rol". Remote Master se actualiza con frecuencia. Entonces, después de confirmar mis cambios a la sucursal remota, necesito volver a establecer la base de la sucursal en el último maestro. Así que aquí están mis pasos:

# from local git add . git commit 'Add feature' git push origin role # rebase git pull --rebase origin master 

¿La rebase afecta solo a la twig local o a la twig remota, o ambas? Si solo se modifica la twig local, ¿debería volver a comprometerme con el origen después de la rebase?

Resumen: sí, pero su pregunta es incorrecta. 🙂

Hay un montón de cosas para seguir recto aquí. La terminología de Git no es muy buena para empezar: "remoto", "seguimiento", "sucursal" y "sucursal de seguimiento remoto" significan cosas muy diferentes. Y está usando otro set de términos, lo que hace cosas aún más difíciles. Entonces, antes de responder lo que creo que quieres preguntar, definamos estos términos. (Ver también gitglossary , aunque no todos estos están allí).

  • Un control remoto es un nombre corto como origin . Principalmente sirve como marcador de position para una URL, por lo que no tiene que seguir escribiendo una URL larga, y también forma parte del nombre de una twig de seguimiento remoto (que definiremos en un momento).

  • Una twig es un término ambiguo: ver ¿Qué queremos decir con "twig"? En este caso particular, comenzaremos usándolo para referirnos a un nombre de twig como master . Un nombre de sucursal, en Git, es solo un nombre para una confirmación en particular, que llamamos la punta de la sucursal, pero un nombre de sucursal local (común) como el master tiene una propiedad especial, que veremos pronto.

  • Una twig de seguimiento remoto es un nombre que asigna su Git, y actualizaciones para usted, en su repository, en function de lo que vio su Git cuando su Git llamó a otro Git. Estos son los nombres como origin/master . Estos nombres de twig de seguimiento remoto no tienen la propiedad especial de nombres de twig ordinarios (locales).

  • El seguimiento es un verbo terrible que Git usa como forma abreviada de una forma más moderna y verbosa de decir lo mismo. Se puede establecer una sucursal local para "seguir" una sucursal de seguimiento remoto. La forma más moderna de decir esto es que la sucursal local tiene la sucursal de seguimiento remoto establecida como ascendente . Establecer un flujo ascendente no hace nada que no puedas hacer "a mano", pero hace que Git lo haga automáticamente, lo cual es útil.

    Por lo general, establecemos el origin/master twig de seguimiento remoto como ascendente para el master sucursal local. Del mismo modo, estableceríamos origin/role como upstream para el role sucursal local. Es importante recordar, en este punto, que tanto la sucursal local como la sucursal de seguimiento remoto están en su propio Git. ¡Ambos son en realidad locales! Las twigs de seguimiento remoto se llaman simplemente "seguimiento remoto" porque se actualizan automáticamente .

Luego, recuerde que cuando usa git fetch y git push (y git pull , pero ve al lado), su Git llama a otro Git. El otro Git tiene su propio repository separado. La forma en que su Git obtiene el otro Git es a través de una URL, y la URL se almacena bajo el nombre "remoto", por ejemplo, origin : su Git llama al Git de origen por el teléfono de Internet y habla con el otro Git. Tu Git obtiene cosas de ellos ( git fetch ) o les da cosas ( git push ), y tu Git y su Git funcionan de forma puramente local, con tu Git trabajando en tu repository y su Git trabajando en su repository.

Todo es local

Por eso, en Git, al less en una primera aproximación, todo es local. En realidad, no hay nada remoto, solo entidades locales, además de estas llamadas telefónicas por Internet especiales en las que varios Gits intercambian datos entre sí, mientras trabajan localmente. Afortunadamente, dado que fetch y push son los dos puntos de contacto principales, es fácil mantener esto en línea: todo es local hasta que lo recoges o lo empujas. Esos son los puntos en los que su Git y otro Git transfieren datos y hacen cambios (locales).

Entonces, ahora podemos responder sus preguntas:

¿La rebase afecta solo a la twig local o a la twig remota, o ambas?

Este es fácil: si no estás presionando, solo puedes afectar las cosas locales.

Si solo se modifica la twig local, ¿debería volver a comprometerme con el origen después de la rebase?

Este tiene un concepto equivocado embedded. Cuando haces commits, todavía solo estás afectando las cosas locales. Para enviar tus cosas locales a otra parte, necesitas presionarlas (o, si tu máquina puede actuar como server, deja que tu máquina responda la llamada telefónica de otra persona), pero no vayamos allí, ¡al less no todavía!

Aquí hay una gran complicación, porque git fetch y git push no son simétricos.

Push y fetch no son simétricos

Cuando ejecutas git fetch , tu Git llama a su Git y obtiene cosas nuevas de ellos. Tu Git guarda las cosas nuevas-commits y cualquier otra cosa que esos commits necesiten, realmente-lejos en tu repository, luego actualiza los nombres de las sucursales de seguimiento remoto. Estos son bastante independientes de los nombres de las sucursales locales: su origin/master está separado de su master , y su origin/role es independiente de su role . Lo único que hacen los nombres de las twigs de origin/* es recordar qué vio tu Git en su Git, por lo que es bastante seguro cambiarlos cada vez que tu Git vea cosas nuevas en su Git.

Sin embargo, cuando ejecutas git push , tu Git llama a su Git, les envía tus cosas y luego les pide que cambien su master o su role . No tienen un ddd/master o ddd/role : le está pidiendo a su Git que cambie el suyo propio, y solo, master o role . Si tienen algo nuevo en su master o role , que ya no incorporaste a lo que estás presionando, les pedirás que renuncien a esas cosas nuevas, y en cambio las tuyas.

Ahora es el momento de mostrar la propiedad especial de los nombres de las sucursales locales, y observar cómo funcionan los commit y los nombres de las sucursales, y ver cómo git rebase realmente la git rebase . Este también es un buen momento para revisar esa otra pregunta, ¿Qué queremos decir con "twig"?

Cómo las twigs crecen normalmente, y la violencia rebase hace a esto

Git se trata principalmente de commits. Los compromisos son la razón de ser de Git: son instantáneas eternas e inmutables de todo lo que has cometido. No se puede modificar ninguna confirmación, aunque las confirmaciones existentes se pueden copyr a nuevas confirmaciones (diferentes), extrayendo la instantánea anterior, realizando el cambio, sea lo que sea, y convirtiendo la nueva instantánea en una nueva location. Pero, ¿qué quiero decir con "location" aquí? Aquí es donde el gráfico de compromiso ingresa a la image.

Cada commit tiene una ID única. Esta es la gran commit face0ff... feo commit face0ff... tipo de combinación de numbers y letras que Git muestra en varias ocasiones. (De hecho, cada object Git tiene uno de estos ID, pero ahora solo nos importan los objects commit).

Cada confirmación guarda, junto con su instantánea de lo que haya git add o guardado de la confirmación anterior, su nombre, su dirección de correo electrónico, su post de logging y -este es el elemento key aquí- la ID de esa confirmación previa.

Esto significa que compromete cadenas de forma (hacia atrás):

 A <- B <- C <-- master 

Los nombres de las sucursales, como el master , apuntar a punta se compromete. El compromiso de punta es el más reciente de la cadena. Los puntos más recientes vuelven a su confirmación principal (anterior), que apunta a otro padre, y así sucesivamente. Esta cadena termina solo cuando llegamos a la primera confirmación, que no tiene padre.

Debido a que las nuevas confirmaciones apuntan a las confirmaciones más antiguas, necesitamos que Git "avance" el nombre de la sucursal, siempre que hagamos una nueva confirmación, para señalar la nueva confirmación que acabamos de hacer. Es decir, si tenemos A <- B <- C como arriba y hacemos una nueva D , necesitamos que Git mueva al master para que apunte a D ahora:

 A <- B <- C <- D <-- master 

Esta es la propiedad especial de los nombres de las sucursales locales. Avanzan automáticamente , al nuevo compromiso que acabamos de hacer, cuando hacemos una confirmación mientras estamos en esa twig.

Esto lleva a una regla simple sobre los nombres de las sucursales (pero esta regla pronto se viola): avanzan de una manera que mantiene todos los compromisos anteriores . Agregamos un nuevo compromiso a una sucursal, y el nuevo compromiso vuelve al compromiso anterior, y solo lo hemos agregado a la sucursal. (Tenga en count que ahora estamos hablando de una twig como una colección de confirmaciones , en lugar de un nombre que apunta exactamente a una confirmación).

Rebase viola esta regla simple, a propósito. Miremos lo que sucede cuando hacemos twigs. Dejemos de dibujar todas las flechas internas, y solo recordemos que todas apuntan hacia atrás: siempre movemos hacia la izquierda cuando estamos retrocediendo desde las nuevas confirmaciones a las más antiguas.

 A--B--C--D <-- master \ E--F--G <-- role 

Aquí tenemos siete commits en total, tres de ellos solo en el role sucursal, one-commit D -being solo en el branch master y tres-the ABC chain-en ambas twigs. (Esta es otra cosa peculiar de Git: los commits suelen estar en muchas twigs).

Cuando usamos git rebase , queremos mover commits, generalmente para ir justo después de la sugerencia de otra twig (nueva). Por ejemplo, en este caso particular, queremos mover la cadena EFG para que venga después de D , en lugar de ir tras C Pero los commits no se pueden cambiar, solo se copyn.

Entonces, eso es lo que hace git rebase : copy commits. E'-F'-G' EFG a nuevos commits, E'-F'-G' , que se parecen mucho a EFG , pero están en una location diferente en el gráfico: vienen después de D :

 A--B--C--D <-- master \ \ \ E'-F'-G' <-- role \ E--F--G [previous role, now abandoned] 

El command rebase primero copy los commits, luego quita el role nombre de la twig del antiguo commit tip G y lo pega en la nueva copy G' .

Esto pierde la antigua cadena EFG , abandonándola a favor de la nueva E'-F'-G' shiny E'-F'-G' . Los nuevos commits, siendo ligeramente diferentes -si nada más, E' es diferente de E en que E' tiene D como su padre, vs C -tienen diferentes ID.

Por lo tanto, cuando empujamos debemos empujar la fuerza

Si hemos llevado el EFG original al origin , les hemos dado copys duplicadas exactas del EFG original. Estos vienen después de C , no después de D Ahora volveremos a git push origin role , por lo que haremos que nuestro Git llame al otro Git y entregue E'-F'-G' , y luego les pediremos que configuren su role para que apunten a G' .

¡Dirán "no"!

Si establecen su role para apuntar a G' , perderán commits EFG . En este momento, la única forma de que Git encuentre G es mirar el role su (nombre) nombre, y si cambian su role para apuntar a G' , perderán este enlace a G

Por supuesto, ¡eso es lo que queremos que hagan! Pero el valor pnetworkingeterminado es que digan "no, si lo hago, perderé algunos compromisos". (La razón de este incumplimiento es simple: no saben o les importa que les hayamos dado G , todo lo que saben es que, en este momento, perderán G ). Así que tenemos que cambiar la request educada de nuestro Git, "por favor, muévanse". role ", a un command más contundente:" mover role ! "

Idealmente, podemos decir incluso: "Creemos que su role llama G , y si es así, debería pasar a nombrar a G' lugar, pero háganos saber si estamos equivocados acerca de su role , ¿de acuerdo?" Git llama a esto una --force-with-lease . No es compatible con todas las versiones de Git, y no es necesario si estás seguro de que eres el único que cambia su role ; pero se requiere algún tipo de forzamiento, porque necesitas hacer que su Git abandone las confirmaciones antiguas (copydas), al igual que tu Git.

Aparte: git pull

El command git pull es una conveniencia. Cada vez que ejecutas git fetch , puedes recoger un montón de nuevos commits desde el control remoto, como de costumbre, pero esos nuevos commits terminan bajo sus sucursales de rastreo remoto. Una vez que tienes esos commits, generalmente quieres hacer algo con ellos. Dos cosas principales que puedes hacer con ellos es git merge o git rebase . Así que git pull combina git fetch con una git merge inmediata o git rebase después.

El problema principal con esto es que hasta que haya buscado e inspeccionado las nuevas confirmaciones, no necesariamente sabe con certeza qué va a querer hacer con ellas. Usar git pull te hace decidir de antemano qué hacer con ellos. Imagínese diciendo: "Voy a search en este armario oscuro y sacar una botella, y sea lo que sea, voy a beber". Si sacas una cerveza, está bien; pero ¿y si saca una botella de lejía?

En cualquier caso, todo lo anterior aún se aplica a git pull , porque es solo git fetch seguido de un segundo command. La fetch llama a otro Git, a través de la URL almacenada bajo el nombre remoto. El segundo paso, ya sea git merge o git rebase , funciona localmente. Lo particularmente confuso sobre git pull es que, debido a que es anterior a la invención de los nombres de las sucursales de rastreo remoto, tiene una syntax especial propia. Esta es la razón por la cual, aunque está obteniendo de origen, luego rebase en origin/master , escribe "extraer (como rebase) origen maestro" en lugar de "extraer (y luego rebase en) origen / maestro".