La request de extracción de una twig contiene confirmaciones de otras twigs

Digamos que tengo dos twigs, master y dev .

Creé una twig desde el master , realicé cambios y los fusioné con el master .

Ahora creé otra twig desde el master , pero modifiqué mis cambios y trato de fusionarlos, pero cuando realizo la request de extracción, la primera twig fusionada con el master aparece en mi request. ¿Cómo puedo evitar esto?

(El problema es que debería haber fusionado la primera twig con dev no master pero ahora ya no puedo hacer eso, porque los mismos cambios se fusionaron con dev con otra tercera twig).

Nota: si esto es TL; DR, salte a la última sección, Dibujando su request de extracción .

Dibujemos lo que ya hiciste. Recuerde, en Git, cada commit "apunta hacia atrás" hacia su commit padre , o hacia un commit de fusión , a ambos de sus (dos) padres; y un nombre de twig como master o dev o newbranch simplemente apunta a un commit, con los commits haciendo el señalamiento hacia atrás para representar una serie completa de commits.

Tengo que hacer algunas suposiciones aquí. Bitbucket (que no he usado desde que convirtieron de GitHub a una tienda exclusiva de Mercurial) y GitHub (que sí uso) son un poco diferentes, pero como ambos usan Git debajo, tienen muchas similitudes. Voy a suponer que está utilizando lo que el website de Bitbucket llama un "flujo de trabajo de bifurcación".

Voy a tratar de evitar hacer muchas otras suposiciones. Eso hace que esta respuesta sea muy larga y complicada. (Podría haber sido útil que haya proporcionado más información en su pregunta, de modo que la respuesta sea más breve).

Dibujando lo que tienes, parte 1

Entonces, comenzaste con:

 ... <-B <-C <-D <-- master 

donde commit D señala de nuevo C , C apunta a B , y así sucesivamente, y los puntos master a D Tenga en count que cada una de estas letras mayúsculas individuales representa una identificación de hash de commit real, una de esas incomprensibles 1badc0ffee4deadcab... o lo que sea. Usamos las letras aquí porque son simples, fáciles y comprensibles; Git usa los hash porque las letras se agotan después de solo 26 commits. 🙂 (Y por otras razones mejores)

Sabemos que los commits siempre apuntan hacia atrás. Tienen que hacerlo: ¡no se puede apuntar hacia adelante a un compromiso que aún no se haya realizado! Así que no nos molestemos con las flechas interiores. Pero los nombres de las twigs se mueven con el time, así que sigamos dibujando sus flechas:

 ...--B--C--D <-- master 

Ahora creaste una nueva twig, que también apunta a D :

 ...--B--C--D <-- master, newbranch (HEAD) 

(Aquí hay un ejercicio rápido para ti: ¿cuáles de estos tres commits están en master , y cuáles están en newbranch ? Esta es una pequeña pregunta, porque la respuesta es que los tres commits están en ambas twigs).

Hemos agregado esta notación HEAD para que podamos decir en qué twig está " on branch newbranch ", como en el git status , on branch newbranch . Ahora haces un nuevo commit E Git hace que la twig actual , la marcada como punto de HEAD al nuevo compromiso que acabamos de hacer, y tiene el nuevo punto de confirmación de nuevo en la twig anterior:

 ...--B--C--D <-- master \ E <-- newbranch (HEAD) 

Hagamos dos commits más solo por el gusto de hacerlo:

 ...--B--C--D <-- master \ E--F--G <-- newbranch (HEAD) 

Ahora el nombre newbranch apunta a G , que apunta a F , y así sucesivamente.

Dibujando lo que tienes, parte 2: fusionando newbranch

La siguiente parte es engañosa. Lo hiciste:

 git checkout master; git merge newbranch 

o tal vez:

 git checkout master; git merge --no-ff newbranch 

pero no dices cuál de estos hiciste. El git checkout hará que la label HEAD al master . Pero, dado el dibujo que hemos dibujado aquí, puedes ver que Git puede simplemente deslizar el nombre master hacia adelante, en la dirección de izquierda a derecha que es el opuesto de todas las flechas internas en las twigs, de modo que el nombre master apunta a G :

 ...--B--C--D \ E--F--G <-- master (HEAD), newbranch 

Git llama a esta operación de deslizamiento de tags un avance rápido . Una cosa crítica acerca de esto es que una operación de avance rápido no hace que una fusión se comprometa . Esto hace que sea más difícil ver dónde estaba el master antes de ejecutar git merge , porque podemos enderezar el pliegue en el dibujo y get:

 ...--B--C--D--E--F--G <-- master (HEAD), newbranch 

Dado que D es realmente 4384e3cde2ce8ecd194202e171ae16333d241326 o una ID de hash fea grande, probablemente no recuerdes lo que realmente era. ¿Cómo sabrá que fusionó exactamente tres commits, y ese master solía ser 4384e3cde2ce8ecd194202e171ae16333d241326 ?

Puedes usar el tipo --no-ff merge para obligar a Git a realizar una combinación real, incluso si no es necesario. Esto ayuda a descubrir lo que sucedió. En lugar de simplemente deslizar el nombre master hacia adelante, Git hará esto:

 ...--B--C--D---------H <-- master (HEAD) \ / E--F--G <-- newbranch 

En otras palabras, aquí Git realiza una nueva combinación de compromiso H La nueva confirmación apunta tanto a D como a G De hecho, esto es lo que lo convierte en un commit de fusión: apunta a dos padres.

Por qué estamos haciendo todo este gráfico-dibujo

La forma precisa del gráfico de command git merge post- git merge no afecta su problema actual. Obtendrás lo mismo con cualquiera de los dos charts posteriores a la fusión. Aún así, es importante tener en count cuándo entran las asignaciones de fusión y lo que hacen: registran este tipo de historial real para futuras exploraciones. Y en cualquier caso, es importante poder dibujar el gráfico, pnetworkingecir lo que sucederá con una request de extracción. Ambos charts posteriores a la merge generan un problema.

A veces, hacer que esta "burbuja de fusión" haga que H apunte hacia atrás a G y D * es simplemente un desorder inútil. Si no hay necesidad de recordar en el futuro que este grupo de confirmaciones llegó de una sola vez a través de la fusión, puede y probablemente deba usar la combinación de avance rápido. Si desea forzar una combinación real, ahora sabe que existe esta --no-ff para git merge para hacer eso.

Si usa GitHub para realizar fusiones (tal vez mediante requestes de extracción), puede usar los controles de GitHub para decidir si forzar fusiones reales. Estos controles son diferentes de los de Git (naturalmente, ya que son botones clicky de la GUI en su browser). GitHub generalmente intenta ocultar estos charts, lo cual creo que es un error: el gráfico es crucial para determinar qué es realmente posible y qué sucederá cuando intentes varias cosas. Por ejemplo, muestra, visualmente, por qué su request de extracción tuvo el problema.

(No he usado la interfaz web de Bitbucket pero supongo que funciona de manera muy similar a la de GitHub).

Dibujando su segundo set de cambios

Ahora dices:

Ahora creé otra twig desde el master , pero apliqué mis cambios a dev y estoy tratando de fusionarlos, pero cuando hago la request de extracción, esa primera twig fusionada a master aparece en mi request.

Supongamos que hiciste una fusión de avance rápido y, por lo tanto, tienes esto en tus repositorys de Git, para empezar. newbranch que ha eliminado newbranch o que no nos importa, y que su nueva nueva twig, que "empujó para zaphod ", se llama zaphod :

 ...--D--E--F--G <-- master, zaphod (HEAD) 

Ahora haces algunos commits nuevos (pongamos dos):

 ...--D--E--F--G <-- master \ H--I <-- zaphod (HEAD) 

Luego haces algo, no está claro para mí exactamente qué serie de commands de Git y / o cuadros de dialog de la Web y / o la GUI, para hacer que tu repository Bitbucket haga que el nombre dev también se refiera al compromiso I , o quizás a una nueva fusión J que apunta hacia I

Tenga en count que hay dos repositorys propios de Git involucrados en este punto: uno en su computadora local y otro en Bitbucket. Lo interesante de las ID hash de Git, que hemos estado dibujando aquí como letras mayúsculas simples, es que son las mismas en todas las copys de cada repository. (¡Esto no es verdad para los nombres de las sucursales!) Así que en Bitbucket, en lugar de zaphod , tenemos el nombre dev apuntando a cometer J En realidad, tampoco necesitamos la notación HEAD ya que no compromete directamente en Bitbucket:

 ...--D--E--F--G <-- master \ H--I <-- dev 

Mientras tanto, hay un tercer repository que tenemos que traer. Este tercer repository es de lo que se bifurcó, en su "flujo de trabajo de bifurcación" de Bitbucket.

Asumiré por el momento que este tercer repository comenzó con un aspecto como este:

 ...--B--C--D <-- master, dev 

(Si este no es el caso, es su trabajo dibujar las partes apropiadas del gráfico que tienen en este tercer repository).

Dibujando su request de extracción

Con todo lo anterior fuera del path, veamos qué es realmente una request de extracción.

Todo lo que una request de extracción es , es una request (de ahí el nombre) de que otra persona agregue, a su repository, algún set de confirmaciones de uno de sus repositorys, usando un nombre asociado con su repository particular -como su desarrollador- y un nombre asociado con su repository, a menudo el mismo nombre nuevamente.

En este caso, el repository desde el que realiza la request de extracción es el que ha almacenado en Bitbucket que es su fork de su repository. Entonces, es por eso que necesitamos el dibujo del gráfico que está en su repository de Bitbucket:

 ...--B--C--D--E--F--G <-- master \ H--I <-- dev 

También necesitamos saber qué tienen en su repository. Lo he asumido:

 ...--B--C--D <-- master, dev 

Tenga en count que su D y su D son literalmente la misma confirmación.

Lo que les pides que hagan es configurar su desarrollador para apuntar a cometer I

Para que lo hagan, deben llevar sus confirmaciones E--F--G--H--I a su repository, lo que da como resultado:

 ...--B--C--D <-- master \ E--F--G--H--I <-- dev 

Pero eso no es lo que realmente quieres que hagan en este momento. Desea que ellos tomen algunos commit (s) que se parecen un poco a H e I , y hagan esto:

 ...--B--C--D <-- master \ H2--I2 <-- dev 

Para que eso suceda, primero debes hacer H2 e I2 .

Para hacer H2 e I2 , tendrá que copyr sus confirmaciones originales H e I a nuevos commits. Necesitará estos nuevos compromisos para venir justo después de cometer D Entonces ahora necesitas ubicar su commit de tip-of- dev .

He estado dibujando esto como commit D , pero eso podría no ser correcto. No tengo tus repositorys y he tenido que hacer varias conjeturas (incluido el flujo de trabajo de Bitbucket elegido).

Si tiene su repository Bitbucket configurado como upstream , puede ejecutar git fetch remote-name para get su propio Git, en su computadora local, para get su último commit de su dev y registrarlo como remote-name /dev . (Normalmente su bifurcación Bitbucket se llamaría origin , de modo que origin/dev registra el desarrollo de su fork en su repository en Bitbucket. Puede nombrar su repository Bitbucket en upstream para que upstream/dev nombre la confirmación en la punta de su twig dev ).

Digamos, para ser más concretos, que su repository de Bitbucket, al que puede hacer reference por el nombre en upstream desde su repository de Git local, realmente tiene esto en él:

 ...--B--C--D <-- master \ N--O <-- dev 

Aquí ejecutaría git fetch upstream , y ahora su repository -el local, no su copy en Bitbucket todavía- tendría:

  H--I <-- zaphod (HEAD), origin/dev / ...--B--C--D--E--F--G <-- master \ N--O <-- upstream/dev 

Luego necesitas crear tu propia twig que apunte a cometer O Para hacerlo localmente, puedes usar git checkout con -b y --track :

 $ git checkout -b for-upstream-dev --track upstream/dev H--I <-- zaphod, origin/dev / ...--B--C--D--E--F--G <-- master \ N--O <-- upstream/dev, for-upstream-dev (HEAD) 

Ahora puede copyr commits H y I para confirmar H2 e I2 , usando git cherry-pick :

  $ git cherry-pick zaphod~2..zaphod H--I <-- zaphod, origin/dev / ...--B--C--D--E--F--G <-- master \ N--O <-- upstream/dev \ H2--I2 <-- for-upstream-dev (HEAD) 

Una vez que haya configurado esto en su repository local , necesita que su repository Bitbucket establezca su dev en el punto para confirmar I2 . Para hacer eso debes forzar-empujar. Normalmente, forzar el empuje es malo, ya que le dice a otro repository Git que pierda algunos commits, pero aquí quiere que su repository Bitbucket pierda sus confirmaciones H--I Asi que:

 $ git push --force origin for-upstream-dev:dev 

haría que tu Git (local) llamara al Git de origin , ese es tu Bitbit Git, y dile: te doy mi I2 , y por supuesto mi H2 y O y N si los necesitas; ¡Ahora configura tu desarrollador en mi I2 !

Asumiendo que obedezcan este command, Bitbucket ahora cambiará su copy de tu tenedor para que se vea así:

 ...--B--C--D--E--F--G <-- master \ N--O--H2--I2 <-- dev 

(Y, cuando el origin Git acepta este empuje, su propio Git mueve su origin/dev al punto a I2 , para recordar que el origin ahora tiene su dev apunta a I2 ).

Ahora compare este gráfico con el del repository de Bitbucket en sentido ascendente, el que su Git llama en upstream :

 ...--B--C--D <-- master \ N--O <-- dev 

La diferencia entre estos dos charts es que les pedirá, en upstream tomen el H2 y el I2 su origin y los agreguen a su desarrollador … que es lo que pidieron en su pregunta.

Tenga en count que su propio repository (local) todavía tiene el complicado y enmarañado lío de twigs llamado zaphod y demás. Una vez que su upstream ha aceptado los cambios en su dev -tomar H2 y I2 es probable que sea hora de limpiar su repository local, haciendo que se vea al less un poco, si no mucho, como su repository de origin . Para hacer eso, puedes eliminar tu propia twig zaphod completo:

  H--I [abandoned] / ...--B--C--D--E--F--G <-- master \ N--O <-- upstream/dev (maybe) \ H2--I2 <-- for-origin-dev, origin/dev, upstream/dev (maybe) 

Tenga en count que una vez que suelta una label de bifurcación, la única forma de encontrar las confirmaciones que solía encontrar en la label de bifurcación es usar algún otro nombre (como la ID de hash sin procesar). Si todos los nombres de algunos commit (s) se han ido, de modo que no hay forma de encontrarlos, con el time serán recolectados como basura , es decir, descartados por completo. Esto es lo que sucederá con el H--I original, ahora que están abandonados.

Los (maybe) s anteriores se deben a que tu propio Git no sabrá que upstream ha aceptado tu petición de extracción hasta que git fetch upstream para que tu Git (local) llame al Git upstream y encuentre su último desarrollador.