Mover la(s) confirmación (es) más reciente (s) a una nueva rama con Git


Me gustaría pasar los últimos comete he comprometido principal a una nueva rama y tomar maestro antes de los commits realizados. Desafortunadamente, mi Git-fu aún no es lo suficientemente fuerte, ¿alguna ayuda?

Es decir, ¿Cómo puedo salir de esto

master A - B - C - D - E

A esto?

newbranch     C - D - E
             /
master A - B 
Author: BIBIN K ONANKUNJU, 2009-10-27

11 answers

Pasando a una nueva rama

ADVERTENCIA: Este método funciona porque está creando una nueva rama con el primer comando: git branch newbranch. Si desea mover confirmaciones a una rama existente necesita fusionar sus cambios en la rama existente antes de ejecutar git reset --hard HEAD~3 (consulte Mudarse a una sucursal existente infra). Si no fusiona sus cambios primero, se perderán.

A menos que haya otras circunstancias involucradas, esto puede se puede hacer fácilmente ramificando y rodando hacia atrás.

# Note: Any changes not committed will be lost.
git branch newbranch      # Create a new branch, saving the desired commits
git reset --hard HEAD~3   # Move master back by 3 commits (GONE from master)
git checkout newbranch    # Go to the new branch that still has the desired commits

Pero asegúrate de cuántos commits volver. Alternativamente, puede en lugar de HEAD~3, simplemente proporcionar el hash de la confirmación (o la referencia como origin / master ) a la que desea "volver" en la rama master (/current), por ejemplo:

git reset --hard a1b2c3d4

* 1 Solo estará "perdiendo" confirmaciones de la rama master, pero no se preocupe, tendrá esas confirmaciones en ¡newbranch!

ADVERTENCIA: Con Git versión 2.0 y posterior, si más tarde git rebase la nueva rama sobre la rama original (master), es posible que necesite una opción explícita --no-fork-point durante el rebase para evitar perder las confirmaciones transferidas. Tener branch.autosetuprebase always establecido hace que esto sea más probable. Véase La respuesta de John Mellor para más detalles.

Pasando a una rama existente

Si quieres mover tus commits a una rama existente , se verá así:

git checkout existingbranch
git merge master
git checkout master
git reset --hard HEAD~3 # Go back 3 commits. You *will* lose uncommitted work.
git checkout existingbranch
 4914
Author: sykora,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-23 08:07:38

Para aquellos que se preguntan por qué funciona (como lo estaba al principio):

Desea volver a C, y mover D y E a la nueva rama. Esto es lo que parece al principio:

A-B-C-D-E (HEAD)
        ↑
      master

Después de git branch newBranch:

    newBranch
        ↓
A-B-C-D-E (HEAD)
        ↑
      master

Después de git reset --hard HEAD~2:

    newBranch
        ↓
A-B-C-D-E (HEAD)
    ↑
  master

Dado que una rama es solo un puntero, master apunta a la última confirmación. Cuando hiciste newBranch , simplemente hiciste un nuevo puntero a la última confirmación. Luego usando git resetmoviste el puntero master hacia atrás dos commits. Pero como no moviste newBranch, todavía apunta a la confirmación que hizo originalmente.

 861
Author: Ryan Lundy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-31 00:29:20

En General...

El método expuesto por sykora es la mejor opción en este caso. Pero a veces no es el más fácil y no es un método general. Para un método general use git cherry-pick :

Para lograr lo que OP quiere, es un proceso de 2 pasos:

Paso 1-Tenga en cuenta que commits de maestro que desea en un newbranch

Ejecutar

git checkout master
git log

Tenga en cuenta los hashes de (digamos 3) confirmaciones que desea en newbranch. Aquí voy a utilizar:
C commit: 9aa1233
D commit: 453ac3d
E commit: 612ecb3

Nota: Puede usar los primeros siete caracteres o todo el hash de confirmación

Paso 2-Ponlos en el newbranch

git checkout newbranch
git cherry-pick 612ecb3
git cherry-pick 453ac3d
git cherry-pick 9aa1233

O (en Git 1.7.2+, use rangos)

git checkout newbranch
git cherry-pick 612ecb3~1..9aa1233

git cherry-pick aplica esos tres commits a newbranch.

 349
Author: Ivan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-02-28 18:13:52

Otra forma de hacer esto, usando solo 2 comandos. También mantiene intacto su árbol de trabajo actual.

git checkout -b newbranch # switch to a new branch
git branch -f master HEAD~3 # make master point to some older commit

Versión antigua - antes de que me enterara de git branch -f

git checkout -b newbranch # switch to a new branch
git push . +HEAD~3:master # make master point to some older commit 

Ser capaz de push a . es un buen truco para saber.

 256
Author: aragaer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-21 14:17:32

¡La mayoría de las respuestas anteriores están peligrosamente equivocadas!

NO hagas esto:

git branch -t newbranch
git reset --hard HEAD~3
git checkout newbranch

Como la próxima vez que corras git rebase (o git pull --rebase) esas 3 confirmaciones serían silenciosamente descartadas de newbranch! (véase la explicación más adelante)

En su lugar, haga esto:

git reset --keep HEAD~3
git checkout -t -b newbranch
git cherry-pick ..HEAD@{2}
  • Primero descarta las 3 confirmaciones más recientes (--keep es como --hard, pero más seguro, ya que falla en lugar de tirar los cambios no comprometidos).
  • Luego se bifurca newbranch.
  • Entonces elige esas 3 confirmaciones vuelven a newbranch. Dado que ya no son referenciados por una rama, lo hace usando el reflog de git: HEAD@{2} es el commit que HEAD utiliza para referirse a 2 operaciones hace, es decir, antes de we 1. salida newbranch y 2. se usa git reset para descartar las 3 confirmaciones.

Advertencia: el reflog está habilitado de forma predeterminada, pero si lo has deshabilitado manualmente (por ejemplo, usando un repositorio git "desnudo"), no podrás recuperar las 3 confirmaciones después de ejecutarlo git reset --keep HEAD~3.

Una alternativa que no depende del reflog es:

# newbranch will omit the 3 most recent commits.
git checkout -b newbranch HEAD~3
git branch --set-upstream-to=oldbranch
# Cherry-picks the extra commits from oldbranch.
git cherry-pick ..oldbranch
# Discards the 3 most recent commits from oldbranch.
git branch --force oldbranch oldbranch~3

(si lo prefiere, puede escribir @{-1} - la rama previamente eliminada - en lugar de oldbranch).


Explicación técnica

¿Por qué git rebase descartaría las 3 confirmaciones después del primer ejemplo? Esto se debe a que git rebase sin argumentos habilita la opción --fork-point por defecto, que usa el reflog local para intentar ser robusto frente a la rama upstream empujado a la fuerza.

Supongamos que se ramificó de origin / master cuando contenía commits M1, M2, M3, y luego hizo tres commits usted mismo:

M1--M2--M3  <-- origin/master
         \
          T1--T2--T3  <-- topic

Pero luego alguien reescribe la historia empujando a la fuerza origin / master para eliminar M2:

M1--M3'  <-- origin/master
 \
  M2--M3--T1--T2--T3  <-- topic

Usando su reflog local, git rebase puede ver que se bifurcó de una encarnación anterior de la rama origin/master, y por lo tanto que las confirmaciones M2 y M3 no son realmente parte de su rama topic. Por lo tanto, asume razonablemente que desde M2 era eliminado de la rama upstream, ya no lo desea en su rama topic una vez que la rama topic se rebase:

M1--M3'  <-- origin/master
     \
      T1'--T2'--T3'  <-- topic (rebased)

Este comportamiento tiene sentido, y generalmente es lo correcto al cambiar la base.

Así que la razón por la que los siguientes comandos fallan:

git branch -t newbranch
git reset --hard HEAD~3
git checkout newbranch

Es porque dejan el reflog en el estado equivocado. Git ve que newbranch se ha bifurcado de la rama upstream en una revisión que incluye las 3 confirmaciones, luego el reset --hard reescribe la rama upstream historial para eliminar las confirmaciones, y así la próxima vez que corras git rebase las descarta como cualquier otra confirmación que haya sido eliminada del upstream.

Pero en este caso particular queremos que esas 3 confirmaciones sean consideradas como parte de la rama topic. Para lograr eso, necesitamos bifurcar el upstream en la revisión anterior que no incluye las 3 confirmaciones. Eso es lo que hacen mis soluciones sugeridas, por lo tanto ambos dejan el reflog en el estado correcto.

Para más detalles, ver el definición de --fork-point en los documentos git rebase y git merge-base.

 225
Author: John Mellor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-16 01:34:57

Esto no los "mueve" en el sentido técnico, pero tiene el mismo efecto:

A--B--C  (branch-foo)
 \    ^-- I wanted them here!
  \
   D--E--F--G  (branch-bar)
      ^--^--^-- Opps wrong branch!

While on branch-bar:
$ git reset --hard D # remember the SHAs for E, F, G (or E and G for a range)

A--B--C  (branch-foo)
 \
  \
   D-(E--F--G) detached
   ^-- (branch-bar)

Switch to branch-foo
$ git cherry-pick E..G

A--B--C--E'--F'--G' (branch-foo)
 \   E--F--G detached (This can be ignored)
  \ /
   D--H--I (branch-bar)

Now you won't need to worry about the detached branch because it is basically
like they are in the trash can waiting for the day it gets garbage collected.
Eventually some time in the far future it will look like:

A--B--C--E'--F'--G'--L--M--N--... (branch-foo)
 \
  \
   D--H--I--J--K--.... (branch-bar)
 27
Author: Sukima,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-08-17 10:23:48

Para hacer esto sin reescribir el historial (es decir, si ya has enviado las confirmaciones):

git checkout master
git revert <commitID(s)>
git checkout -b new-branch
git cherry-pick <commitID(s)>

Ambas ramas pueden ser empujadas sin fuerza!

 19
Author: teh_senaus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-21 16:10:36

Solución mucho más simple usando git stash

Si:

  • Su propósito principal es retroceder master, y
  • Desea mantener los cambios, pero no se preocupa particularmente por las confirmaciones individuales, y{[18]]}
  • Aún no has empujado, y{[18]]}
  • Quieres que esto sea fácil y no complicado con ramas temporales y otros dolores de cabeza

Entonces lo siguiente es mucho más simple (comenzando en la rama master que tiene tres errores commits):

git reset HEAD~3
git stash
git checkout newbranch
git stash pop

Lo que esto hace, por número de línea

  1. Deshace las últimas tres confirmaciones (y sus mensajes) a master, pero deja intactos todos los archivos de trabajo
  2. Guarda todos los cambios del archivo de trabajo, haciendo que el árbol de trabajo master sea igual al estado HEAD~3
  3. cambia a una rama existente newbranch
  4. Aplica los cambios guardados a su directorio de trabajo y borra el guardado

Ahora puede usar git add y git commit como normalmente gustar. Todas las confirmaciones nuevas se añadirán a newbranch.

Lo que esto no hace{[43]]}
  • No deja ramas temporales al azar saturando tu árbol{[18]]}
  • No conserva las confirmaciones erróneas y los mensajes de confirmación, por lo que deberá agregar un nuevo mensaje de confirmación a esta nueva confirmación

Objetivos

El OP declaró que el objetivo era "recuperar al maestro antes de que se hicieran esas confirmaciones" sin perder cambios y esta solución lo hace que.

Hago esto al menos una vez a la semana cuando accidentalmente hago nuevas confirmaciones a master en lugar de develop. Normalmente solo tengo un commit para revertir, en cuyo caso usar git reset HEAD^ en la línea 1 es una forma más sencilla de revertir solo un commit.

No hagas esto si empujaste los cambios de master hacia arriba

Alguien más puede haber tirado de esos cambios. Si solo está reescribiendo su maestro local, no hay impacto cuando se empuja hacia arriba, sino empujando un historial reescrito a los colaboradores puede causarles dolores de cabeza.

 17
Author: Slam,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-09-26 23:36:43

Tenía esta situación:

Branch one: A B C D E F     J   L M  
                       \ (Merge)
Branch two:             G I   K     N

Actué:

git branch newbranch 
git reset --hard HEAD~8 
git checkout newbranch

Esperaba que commit I fuera la CABEZA, pero commit L es ahora...

Para estar seguro de aterrizar en el lugar correcto en la historia es más fácil trabajar con el hash de la confirmación

git branch newbranch 
git reset --hard #########
git checkout newbranch
 12
Author: Darkglow,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-09-20 10:17:35

1) Crea una nueva rama, que mueve todos tus cambios a new_branch.

git checkout -b new_branch

2) Luego vuelve a la rama antigua.

git checkout master

3) Hacer git rebase

git rebase -i <short-hash-of-B-commit>

4) Entonces el editor abierto contiene la información de los últimos 3 cambios.

...
pick <C's hash> C
pick <D's hash> D
pick <E's hash> E
...

5) Cambia pick a drop en todas esas 3 confirmaciones. A continuación, guarde y cierre el editor.

...
drop <C's hash> C
drop <D's hash> D
drop <E's hash> E
...

6) Ahora los últimos 3 commits se eliminan de la rama actual (master). Ahora empuje la rama con fuerza, con el signo + antes de la rama nombre.

git push origin +master
 2
Author: rashok,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-01 05:40:11

Usted puede hacer esto es solo 3 simple paso que he utilizado.

1) crea una nueva rama donde quieras confirmar tu actualización reciente.

git branch <branch name>

2) Encuentra el Id de confirmación reciente para la confirmación en la nueva rama.

git log

3) Copie ese id de confirmación tenga en cuenta que la lista de confirmación más reciente tiene lugar en la parte superior. así que puedes encontrar tu compromiso. también encontrará esto a través de mensaje.

git cherry-pick d34bcef232f6c...

También puedes proporcionar algunos sonidos de commit id.

git cherry-pick d34bcef...86d2aec

Ahora su trabajo hecho. Si elegiste el id correcto y la rama correcta, entonces tendrás éxito. Así que antes de hacer esto tenga cuidado. de lo contrario, puede ocurrir otro problema.

Ahora puedes enviar tu código

git push

 0
Author: Pankaj Kumar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-25 14:14:27