volver revertir restaurar recuperar deshacer descartar cambios archivo anterior git rebase git-rebase git-reset

restaurar - revertir cambios en un archivo git



¿Cómo recupero/vuelvo a sincronizar después de que alguien empuja una rebase o un reinicio a una rama publicada? (3)

Todos hemos escuchado que uno nunca debería rebasar el trabajo publicado, que es peligroso, etc. Sin embargo, no he visto ninguna receta publicada sobre cómo lidiar con la situación en caso de que se publique una rebase.

Ahora, tenga en cuenta que esto solo es realmente factible si el repositorio solo es clonado por un grupo de personas conocido (y preferiblemente pequeño), de modo que quien empuje la rebase o resetee puede notificar a todos los demás que deberán prestar atención la próxima vez ha podido recuperar(!).

Una solución obvia que he visto funcionará si no tienes confirmaciones locales en foo y se vuelve a configurar:

git fetch git checkout foo git reset --hard origin/foo

Esto simplemente descartará el estado local de foo a favor de su historial según el repositorio remoto.

Pero, ¿cómo se puede lidiar con la situación si uno ha cometido cambios locales sustanciales en esa rama?


Comenzando con git 1.9 / 2.0 Q1 2014, no tendrá que marcar su origen de bifurcación anterior antes de en la bifurcación ascendente reescrita, como se describe en la answer :
Consulte la confirmación 07d406b y confirme d96855f :

Después de trabajar en la rama topic creada con git checkout -b topic origin/master , el historial del git checkout -b topic origin/master la raíz del seguimiento remoto puede haberse rebobinado y reconstruido, lo que lleva a un historial de esta forma:

o---B1 / ---o---o---B2--o---o---o---B (origin/master) / B3 / Derived (topic)

donde origin/master solía apuntar a commits B3 , B2 , B1 y ahora apunta a B , y su rama de topic se inició en la parte superior de la misma cuando origin/master estaba en B3 .

Este modo usa el reflog de origin/master para encontrar B3 como el punto de horquilla, de modo que el topic puede volver a establecer en la parte superior del origin/master actualizado de la siguiente manera:

$ fork_point=$(git merge-base --fork-point origin/master topic) $ git rebase --onto origin/master $fork_point topic

Es por eso que el comando git merge-base tiene una nueva opción:

--fork-point::

Encuentre el punto en el que una bifurcación (o cualquier historia que conduzca a <commit> ) bifurcada desde otra bifurcación (o cualquier referencia) <ref> .
Esto no solo busca el ancestro común de las dos confirmaciones, sino que también tiene en cuenta el reflog de <ref> para ver si el historial que lleva a <commit> bifurcó desde una versión anterior de la rama <ref> .

El comando " git pull --rebase " calcula el punto de bifurcación de la bifurcación que se vuelve a calcular utilizando las entradas de reflog de la bifurcación " base " (generalmente una bifurcación de seguimiento remoto) en la que se basó el trabajo de la bifurcación para hacer frente al caso en el cual la rama "base" ha sido rebobinada y reconstruida.

Por ejemplo, si el historial se viera como:

  • la punta actual de la rama " base " está en B , pero la búsqueda anterior observó que su punta solía ser B3 y luego B2 y luego B1 antes de llegar a la confirmación actual, y
  • la rama que se vuelve a establecer en la parte superior de la última "base" se basa en la confirmación B3 ,

intenta encontrar B3 pasando por la salida de " git rev-list --reflog base " (es decir, B , B1 , B2 , B3 ) hasta que encuentra un compromiso que es un antepasado del consejo actual " Derived (topic) " .

Internamente, tenemos get_merge_bases_many() que puede calcular esto con one-go.
Quisiéramos una base de fusión entre Derived y una confirmación de fusión ficticia que resultaría fusionando todos los consejos históricos de " base (origin/master) ".
Cuando existe una confirmación de este tipo, deberíamos obtener un único resultado, que coincida exactamente con una de las entradas de reflog de " base ".

Git 2.1 (Q3 2014) agregará hacer que esta característica sea más robusta a esto: vea commit 1e0dacd por John Keeping ( johnkeeping )

manejar correctamente el escenario donde tenemos la siguiente topología:

C --- D --- E <- dev / B <- master@{1} / o --- B'' --- C* --- D* <- master

dónde:

  • B'' es una versión fija de B que no es idéntica al parche con B ;
  • C* y D* son parche idénticos a C y D respectivamente y entran en conflicto textualmente si se aplican en el orden incorrecto;
  • E depende textualmente de D

El resultado correcto de git rebase master dev es que B se identifica como el punto tenedor de dev y master , de modo que C , D , E son las confirmaciones que deben repetirse en el master ; pero C y D son parche idénticos con C* y D* y, por lo tanto, pueden soltarse, de modo que el resultado final es:

o --- B'' --- C* --- D* --- E <- dev

Si el punto de horquilla no está identificado, entonces al seleccionar B en una rama que contiene B'' produce un conflicto y si las confirmaciones idénticas a las de parche no se identifican correctamente, la elección de C en una rama que contiene D (o D* equivalente) da como resultado conflicto.


Diría que la recuperación de la sección de rebase de la página de manual de git-rebase cubre casi todo esto.

Realmente no es diferente de la recuperación de tu propia rebase: mueves una rama y vuelves a establecer todas las ramas que la tenían en su historial en su nueva posición.


Volver a sincronizar después de una rebase impuesta no es tan complicado en la mayoría de los casos.

git checkout foo git branch old-foo origin/foo # BEFORE fetching!! git fetch git rebase --onto origin/foo old-foo foo git branch -D old-foo

Es decir. primero configura un marcador para el lugar donde estaba originalmente la rama remota, luego lo usa para reproducir sus confirmaciones locales a partir de ese momento en la rama remota rebasada.

Rebasar es como violencia: si no resuelve tu problema, solo necesitas más. ☺

Puede hacer esto sin el marcador, por supuesto, si busca el ID de origin/foo antes de la rebase y lo usa.

Esta es también la forma en que lidias con la situación en la que olvidaste crear un marcador antes de ir a buscar. No se pierde nada; solo tiene que verificar el reflog para la sucursal remota:

git reflog show origin/foo | awk '' PRINT_NEXT==1 { print $1; exit } /fetch: forced-update/ { PRINT_NEXT=1 }''

Esto imprimirá el ID de confirmación al que apunta el origin/foo antes de la recuperación más reciente que cambió su historial.

Entonces puedes simplemente

git rebase --onto origin/foo $commit foo