tipos tag remove qué practices existen etiquetas crear commits best git merge reset revert

tag - qué tipos de etiquetas existen en git



Deshacer una fusión que ha sido empujada (2)

Aquí las respuestas de Linus Torvals Aquí las respuestas de Linus Torvals http://opensource.apple.com/source/Git/Git-26/src/git-htmldocs/howto/revert-a-faulty-merge.txt

OK, he hecho un desastre. Aparentemente, en mi máquina en casa, la rama de desarrollo no se actualizó. Hice un compromiso y empujé. El resultado fue que la rama de origen / desarrollo real se fusionó con mi rama de desarrollo local, que por alguna razón se consideraron como ramas diferentes.

Por un lado, realmente no entiendo cómo sucedió esto y, en segundo lugar, ¿puedo deshacer esto?

Para ilustrarlo, la red se ve así ahora:

local-develop ---------------------------------- C*--- M - origin/develop --- C --- C --- C --- C --- C --------- /

Lo que realmente quería era que C * se comprometiera con el origen / desarrollo en lugar de fusionar las ramas.

Como dije, esto ya ha sido empujado. ¿Hay alguna forma de eliminar los cambios y enviarlos de la forma que yo quisiera?

Por ejemplo, yo hago:

git reset --hard HEAD~1

No estoy seguro de si esto deshace la fusión y tengo dos desarrollos diferentes, y luego la fusión se elimina, etc. ...?


Las fusiones no ocurren en push, ocurren en git merge (bueno, y pull pero eso es realmente solo fetch + merge, por lo tanto, merge ocurre en merge).

Parece más probable que hayas hecho algo como esto:

<get copy from origin/develop> <wait around for remote''s origin/develop to acquire new commits> git checkout develop # get onto (local) develop branch <edit> git commit -m message # create some new commit(s) git push origin develop # attempt to push -- but this fails! git pull

Es el último paso que crea el commit de fusión ( M arriba), porque pull significa fetch (obtiene todos los commits nuevos que están ahora en origin/develop ), luego merge (toma tu develop local y fusiona tu commit con los nuevos que acabas de obtener )

Si no has git push este nuevo resultado, entonces el repositorio remoto no tiene ninguna de tus confirmaciones locales, las que has etiquetado C* y M En este caso, ¡estás en buena forma! (Puedes comprobar ejecutando git fetch nuevamente para asegurarte de que el origin/develop tu repositorio local coincida con el del control remoto, y luego hacer git log origin/develop para ver qué hay en él).

Puede ser útil recordar que aquí hay dos repositorios git completamente separados: el tuyo, con tus cosas; y el que estás llamando origin aquí. (Llamemos a esa máquina X ). Si tuviera que iniciar sesión en X , tiene su propio historial de confirmación y nombres de sucursal, y así sucesivamente. Allá, cambiarías el directorio al repositorio, ejecutarías git log -1 develop , y verías qué hay en la punta de esa rama.

Ahora, si cierra sesión en X y vuelve a su máquina, puede ejecutar git log -1 origin/develop . Si eso es lo mismo que lo que viste en X , entonces get fetch no tiene nada que actualizar, porque lo que hace el git fetch es, en efecto (pero más eficiente), iniciar sesión en X y ver qué hay allí. Cualquier cosa que tenga X que no tengas en origin/develop , fetch trae y agrega origin/develop . Ahora estás sincronizado con X X no tiene tus cosas, pero tienes las suyas.

Si luego das el paso extra de hacer una merge (incluida la implícita mediante pull ), git hará, si es necesario, un commit de fusión ... pero todo esto está en tu repositorio, en la punta de la rama ( aún develop en este caso). A menos que y hasta que presione esta fusión, comprométase con X (o alguien en X retira sus compromisos de usted, pero ignorémoslo por ahora :-)), X no lo tendrá.

De todos modos, siempre y cuando el control remoto ( X aquí) no tenga su compromiso de fusión, está dorado. Como no lo tienen, nadie más lo hace. Puedes hacer una rebase de tu rama de develop para poner tu confirmación ( C* ) sobre el origin/develop . Eso eliminará el compromiso de fusión ( M ), y luego puede impulsar un avance rápido hacia el origin/develop .

Si X tiene su compromiso de fusión, es decir, si presionó después de pull y obtuvo esa fusión, entonces está estancado (hasta cierto punto) porque, presumiblemente, otras personas tienen acceso a X y ahora están utilizando su compromiso de fusión. Es posible hacer retroceder el repositorio en X , similar a la forma en que puede hacer esto en su repositorio con git reset y git rebase y demás, pero generalmente es una mala idea.

Ahora, supongamos que de hecho ha pasado al otro repositorio (en la máquina X ), pero está absolutamente seguro de que nadie más ha visto sus cambios todavía, y definitivamente los va a restablecer, en lugar de revertirlos (revertir cantidades a "confesar" y dejar atrás el registro atormentado, que permite a todos los demás recuperarse fácilmente, pero también les permite ver su error :-)). 1

Este es el truco: primero necesitas obtener el repo de la máquina X para decir que "punta de la rama de devel es cometer C7", donde C7 está en tu propio diagrama de antes, solo re numerado para poder nombrar cada compromiso de manera diferente:

--------------------------- C*--- M --- C4 --- C5 --- C6 --- C7 ---- /

Entonces, ¿cómo puedes hacer eso? Bueno, una forma es iniciar sesión en X , 2 cd en el repositorio (incluso si es --bare ), y usar git update-ref allí. Digamos que SHA1 para C7 es en realidad 50db850 (como se muestra en "git log"). Entonces podrías hacer esto:

localhost$ ssh X X$ cd /path/to/repo.git X$ git update-ref refs/heads/develop 50db850

Pero si no puede iniciar sesión en X , o simplemente no quiere hacerlo, puede hacer lo mismo con git push -f . 3 (Esto tiene otras ventajas: en particular, su git repo sabrá que el origin/develop ha sido rebobinado, una vez que la push -f completa con éxito). Simplemente haga que un branch-tip local apunte a la confirmación correcta: 4

localhost$ git branch resetter 50db850 localhost$ git log resetter # make sure it looks right ... localhost$ git push -f origin resetter:develop Total 0 (delta 0), reused 0 (delta 0) To ssh://[redacted] + 21011c9...50db850 resetter -> develop (forced update) localhost$ git branch -d resetter # we don''t need it anymore

Una vez que haya hecho esto, la máquina X volverá al estado que deseaba y podrá continuar como si nunca hubiera presionado la fusión que no le gustaba.

Tenga en cuenta que cuando hace el push -f , si alguien más ha realizado nuevos commits encima de M , estos también se volverán invisibles (técnicamente aún están ahí, junto con su commit de fusión, pero están "perdidos" en el lost+found sentido lost+found de git fsck --lost-found , y después de unos meses realmente desaparecerán para siempre). 5

De nuevo y muy importante : este tipo de "reversión del repositorio compartido" es un gran problema para otros usuarios de ese repositorio compartido, así que asegúrese de que está bien antes de hacerlo.

1 Este tipo de fusión trivial ni siquiera necesita revertir. No hay nada fundamentalmente malo en dejar la fusión allí. Sin embargo, si se trata de una fusión errónea más seria, hay otra desventaja para revertir una fusión, además del registro de sus "oops": hace que "rehacer" los cambios más tarde sea un poco más difícil, en una fusión posterior ". a propósito "verán la combinación anterior y pensarán: OK, no necesito volver a fusionar esos cambios. Luego debe "revertir la reversión".

Creo que la lección correcta para llevar aquí es: mira ( git log ) antes de presionar para asegurarte de que lo que estás a punto de impulsar es lo que pretendes impulsar.

2 O, más fácil y simple: git ls-remote . Utiliza el código de protocolo de búsqueda para ver qué tiene el control remoto. Pero oculta el tema que estoy buscando aquí, que es: ¡el control remoto es un repositorio como el tuyo!

3 Las próximas versiones de git versión 2 tienen nuevas "características de seguridad" que serán utilizables con git push -f . Pero aún no han salido, así que eso no te ayuda. Voy a volver a editar esto más adelante cuando estén, para tomar nota de ellos. Hasta entonces, tenga mucho cuidado aquí: en este punto está compitiendo contra cualquier otra persona que intente insertar cosas nuevas en el repositorio compartido.

4 Incluso puede hacer esto mediante ID de SHA-1 sin formato. Sin embargo, el nombre de la sucursal es más fácil de escribir correctamente varias veces seguidas.

5 La retención y vencimiento es a través de "reflogs" de git. El servidor compartido puede no estar registrando todas las actualizaciones de ref; si no, solo los repos privados que ya tenían los nuevos commits los retendrán. La caducidad predeterminada más corta es de 30 días, es decir, alrededor de un mes, para confirmaciones que ya no se pueden obtener desde la sugerencia de la sucursal. Pero tenga en cuenta que es una lucha loca y no divertida para hacer que todos busquen en sus repositorios confirmaciones "perdidas" después de empujar forzosamente "perder trabajo" desde un repositorio compartido.