13 Pregunta: Mueva los compromisos más recientes a una nueva sucursal con Git

pregunta creada en Thu, Dec 21, 2017 12:00 AM

Me gustaría mover los últimos compromisos que me he comprometido a dominar a una nueva rama y recuperar el maestro antes de que se realizaran esos compromisos. Desafortunadamente, mi Git-fu no es lo suficientemente fuerte todavía, ¿alguna ayuda?

I.e. ¿Cómo puedo ir de esto?

master A - B - C - D - E

a esto?

newbranch     C - D - E
             /
master A - B 
    
4339
  1. Nota: Hice la pregunta opuesta aquí
    2010-12-16 08: 56: 06Z
  2. 2017-04-25 06: 35: 42Z
  3. ¿Se eliminaron los comentarios aquí? Lo pregunto porque durante mi visita bimestral a esta pregunta, siempre me desplazo por ese comentario.
    2018-03-19 14: 02: 40Z
13 Respuestas                              13                         

Mover 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 , debe fusionar sus cambios en la rama existente antes de ejecutar git reset --hard HEAD~3 (consulte Mover a una rama existente a continuación). Si no fusiona sus cambios primero, se perderán.

A menos que haya otras circunstancias involucradas, esto puede hacerse fácilmente bifurcándose y retrocediendo.

# 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 (Make sure you know how many commits you need to go back)
git checkout newbranch    # Go to the new branch that still has the desired commits

Pero asegúrese de cuántos se compromete a volver. Alternativamente, puede en lugar de HEAD~3, simplemente proporcionar el hash del commit (o la referencia como origin /master ) que desea "volver a" en el master ( /actual) rama, por ejemplo:

git reset --hard a1b2c3d4

* 1 Sólo estará "perdiendo" confirmaciones de la rama maestra, pero no se preocupe, ¡tendrá esas confirmaciones en newbranch!

ADVERTENCIA: con la versión Git 2.0 y posterior, si más tarde git rebase la nueva rama sobre la original (master), es posible que necesite una opción explícita 060035099110010101035062 durante la rebase para evitar perder el contenido. sobre cometer. Tener el conjunto --no-fork-point hace que esto sea más probable. Consulte la la respuesta de John Mellor para obtener más información.

Mover a una rama existente

Si desea mover sus compromisos a una rama existente , se verá así:

branch.autosetuprebase always     
5518
2019-06-26 12: 09: 49Z
  1. Y en particular, no intente retroceder más allá del punto en el que presionó por última vez los envíos a otro repositorio desde el que otra persona podría han tirado.
    2009-10-27 03: 23: 50Z
  2. Se pregunta si puede explicar POR QUÉ funciona esto. Para mí, está creando una nueva rama, eliminando 3 confirmaciones de la rama anterior en la que aún se encuentra y luego verifique la rama que realizó. Entonces, ¿cómo se muestran mágicamente los compromisos que eliminó en la nueva rama?
    2010-08-03 18: 28: 13Z
  3. @ Jonathan Dumaine: Porque creé la nueva rama antes de eliminar las confirmaciones de la rama anterior. Todavía están allí en la nueva sucursal.
    2010-08-04 08: 28: 04Z
  4. las ramas en git son solo marcadores que apuntan a confirmaciones en el historial, no se está clonando, creando o eliminando nada (excepto los marcadores)
    2010-08-16 11: 32: 12Z
  5. También tenga en cuenta: ¡No haga esto con cambios no confirmados en su copia de trabajo! ¡Esto solo me mordió! :(
    2011-10-25 03: 59: 38Z

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

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

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

Después de

A-B-C-D-E (HEAD)
        ↑
      master
: git branch newBranch

Después de

    newBranch
        ↓
A-B-C-D-E (HEAD)
        ↑
      master
: git reset --hard HEAD~2

Dado que una rama es solo un puntero, master apunta a la última confirmación. Cuando hizo newBranch , simplemente hizo un nuevo puntero al último compromiso. Luego, utilizando

    newBranch
        ↓
A-B-C-D-E (HEAD)
    ↑
  master
movió el puntero master hacia atrás dos confirmaciones. Pero como no movió newBranch , aún apunta a la confirmación que hizo originalmente.     
935
2015-12-31 00: 29: 20Z
  1. También necesitaba hacer un git reset para que el cambio se muestre en el repositorio principal.
    2014-11-26 19: 20: 10Z
  2. Esta respuesta hace que las confirmaciones se pierdan: la próxima vez que git push origin master --force, las 3 confirmaciones se descartarán silenciosamente desde 06003509911111010505050. Ver mi respuesta para obtener detalles y alternativas más seguras.
    2016-04-06 22: 47: 36Z
  3. @ John, eso es una tontería. Rebasar sin saber lo que estás haciendo causa que se pierda. Si perdiste los compromisos, lo siento por ti, pero esta respuesta no perdió tus compromisos. Tenga en cuenta que git rebase no aparece en el diagrama anterior. Si presionó a newbranch y luego hizo los cambios anteriores, claro, las cosas se pondrían divertidas. Pero eso es un "doctor, me duele cuando hago este" tipo de problema. Y está fuera del alcance de lo que la pregunta original hizo. Le sugiero que escriba su propia pregunta para explorar su escenario en lugar de secuestrar esta.
    2016-04-07 03: 20: 31Z
  4. @ John, en su respuesta, dijo "¡No haga esto! origin/master". Regresa y lee las respuestas otra vez. Nadie sugirió hacer eso.
    2016-04-07 15: 02: 55Z
  5. @ Kyralessa, claro, pero si miras el diagrama de la pregunta, está claro que quieren que origin/master se base en su sucursal local existente de 06003509911001010505050. Después de realizar la respuesta aceptada, cuando el usuario comienza a ejecutar git branch -t newbranch en newbranch, git les recordará que se olvidaron de configurar la rama ascendente, por lo que ejecutarán 0600350991111101010505035062 y luego 0600350991111101010106262 y tendrán el mismo problema. También pueden utilizar master en primer lugar.
    2016-04-07 15: 32: 55Z
En general ...

El método expuesto por sykora es la mejor opción en este caso. Pero a veces no es lo 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: anote la confirmación del maestro que desea en un git rebase

Ejecutar

newbranch

Tenga en cuenta los hashes de (por ejemplo, 3) las confirmaciones que desea en git branch --set-upstream-to=master. Aquí usaré:
C commit: git rebase
D commit: git branch -t newbranch
E commit: newbranch

  

Nota: puede usar los primeros siete caracteres o   todo el hash de cometer

Paso 2: póngalos en el
git checkout master
git log
newbranch O (en Git 1.7.2+, usa rangos) 9aa1233

git cherry-pick aplica esos tres compromisos a newbranch.

    
392
2018-02-28 18: 13: 52Z
  1. ¿El OP no estaba intentando mover de master a newbranch? Si elige en forma selectiva mientras está en maestro, estaría agregando a la rama maestra, agregando confirmaciones que ya tenía, de hecho. Y no retrocede ninguna de las ramas hacia B. ¿O hay algo sutil y genial que no entiendo?
    2012-07-06 02: 48: 27Z
  2. Esto funciona muy bien si confirma accidentalmente la rama no maestra, cuando debería haber creado una nueva rama de función.
    2014-02-27 16: 47: 42Z
  3. + 1 para un enfoque útil en algunas situaciones. Esto es bueno si solo quieres juntar tus propios compromisos (que están intercalados con otros) en una nueva rama.
    2014-10-01 17: 11: 06Z
  4. Es la mejor respuesta. De esta manera puede mover los compromisos a cualquier rama.
    2014-11-05 08: 32: 09Z
  5. ¿Es importante el orden de selección de cerezas?
    2015-04-23 22: 04: 40Z

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

453ac3d

Versión antigua - antes de que me enterara de 612ecb3

newbranch

Poder saber de

git checkout newbranch
git cherry-pick 612ecb3
git cherry-pick 453ac3d
git cherry-pick 9aa1233
a
git checkout newbranch
git cherry-pick 612ecb3~1..9aa1233
es un buen truco para saber.     
288
2018-06-21 14: 17: 32Z
  1. Directorio actual. Supongo que esto funcionaría solo si estás en un directorio superior.
    2014-03-28 05: 35: 03Z
  2. El impulso local induce a sonreír, pero al reflexionar, ¿cómo es diferente a
    git checkout -b newbranch # switch to a new branch
    git branch -f master HEAD~3 # make master point to some older commit
    
    aquí?
    2014-08-05 00: 15: 40Z
  3. @ GerardSexton git branch -f es el director actual. git puede empujar a REMOTES o GIT URLs.
    git checkout -b newbranch # switch to a new branch
    git push . +HEAD~3:master # make master point to some older commit 
    
    es compatible con la sintaxis de las URL de Git. Consulte la sección de URL de GIT en push.
    2014-11-25 10: 24: 20Z
  4. No sé por qué esto no tiene una calificación más alta. Muerto, simple, y sin el pequeño pero potencial peligro de reinicio de git: duro.
    2015-02-06 14: 56: 24Z
  5. @ Godsmith Creo que la gente prefiere tres comandos simples a dos comandos un poco más oscuros. Además, las respuestas más votadas obtienen más votos por la naturaleza de ser mostradas primero.
    2015-10-22 15: 43: 45Z

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

NO hagas esto:

.

¡La próxima vez que ejecute git branch -f (o .) esos 3 confirmaciones se descartarán silenciosamente desde path to local directory! (ver explicación abajo)

En lugar de hacer esto:

git help clone
  • Primero descarta los 3 compromisos más recientes (
    git branch -t newbranch
    git reset --hard HEAD~3
    git checkout newbranch
    
    es como git rebase, pero más seguro, ya que falla en lugar de desechar los cambios no confirmados).
  • Luego se desactiva git pull --rebase.
  • Luego selecciona esas 3 confirmaciones de nuevo en newbranch. Ya que una sucursal ya no hace referencia a ellas, lo hace usando reflog :
    git reset --keep HEAD~3
    git checkout -t -b newbranch
    git cherry-pick ..HEAD@{2}
    
    es el compromiso de que --keep se usó para hacer referencia a 2 operaciones anteriores, es decir, antes de que hiciéramos un vistazo a 06003509911110010105050 y se usó el último paquete de 06002pañales. 3 confirmaciones.

Advertencia: el reflog está habilitado de forma predeterminada, pero si lo ha deshabilitado manualmente (por ejemplo, utilizando un repositorio de git "pelado"), no podrá recuperar los 3 confirmaciones después de ejecutar --hard .

Una alternativa que no se basa en el reflog es:

newbranch

(si lo prefiere, puede escribir newbranch - la rama previamente retirada - en lugar de HEAD@{2}).


Explicación técnica

¿Por qué HEAD descartaría las 3 confirmaciones después del primer ejemplo? Esto se debe a que newbranch sin argumentos habilita la opción git reset de forma predeterminada, que utiliza el reflog local para tratar de ser robusto en contra de que la rama ascendente sea forzada.

Supongamos que bifurcaste el origen /maestro cuando contenía las confirmaciones M1, M2, M3, luego hiciste tres confirmaciones:

git reset --keep HEAD~3

pero luego alguien vuelve a escribir el historial presionando el origen /maestro para eliminar M2:

# 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

Usando su reflog local, @{-1} puede ver que se bifurcó de una encarnación anterior de la rama de origen /maestro, y por lo tanto, las confirmaciones M2 y M3 no son realmente parte de su rama de tema. Por lo tanto, se supone razonablemente que, dado que M2 se eliminó de la rama en sentido ascendente, ya no lo querrá en su rama de tema una vez que la rama de tema haya sido rebasada:

oldbranch

Este comportamiento tiene sentido, y generalmente es lo correcto cuando se rebasa.

Entonces, la razón por la que fallan los siguientes comandos:

git rebase

es porque dejan el reflog en el estado incorrecto. Git considera que git rebase se ha desviado de la rama ascendente en una revisión que incluye las 3 confirmaciones, luego la --fork-point reescribe la historia de la ascendente para eliminar las confirmaciones, por lo que la próxima vez que ejecute la 0600350991111101010501035062, las eliminará como cualquier otra comisión que haya sido eliminada. el upstream.

Pero en este caso particular, queremos que esos 3 compromisos se consideren como parte de la rama del tema. Para lograrlo, debemos desmontar el flujo ascendente en la revisión anterior que no incluye los 3 compromisos. Eso es lo que hacen mis soluciones sugeridas, por lo que ambos dejan el reflog en el estado correcto.

Para obtener más detalles, consulte la definición de

M1--M2--M3  <-- origin/master
         \
          T1--T2--T3  <-- topic
en git rebase y git merge-base docs.     
265
2016-09-16 01: 34: 57Z
  1. Esta respuesta dice "¡NO hagas esto!" por encima de algo que nadie sugirió hacer.
    2016-04-16 21: 41: 32Z
  2. La mayoría de las personas no reescriben el historial publicado, especialmente el
    M1--M3'  <-- origin/master
     \
      M2--M3--T1--T2--T3  <-- topic
    
    . Así que no, no están peligrosamente equivocados.
    2016-09-14 06: 59: 48Z
  3. @ Kyralessa, el git rebase al que se refiere en
    M1--M3'  <-- origin/master
         \
          T1'--T2'--T3'  <-- topic (rebased)
    
    sucede implícitamente si tiene 0600350991100101035062 establecido. Incluso si no lo hace, yo ya te expliqué que el mismo problema ocurre si configuras el seguimiento después de ejecutar estos comandos, ya que es probable que el OP intente hacerlo dada su pregunta.
    2016-09-16 02: 36: 03Z
  4. @ RockLee, sí, el general es la forma decorregir tales situaciones es crear una rama nueva (newbranch2) desde un punto de partida seguro y luego seleccionar todos los compromisos que desee mantener (desde badnewbranch a newbranch2). Cherry-picking le otorgará nuevos hashes, por lo que podrá volver a introducir con seguridad newbranch2 (y ahora puede eliminar badnewbranch).
    2016-09-16 02: 36: 53Z
  5. @ Walf, entendiste mal: git rebase está diseñado para ser robusto en contra de upstreams que haya reescrito su historia. Desafortunadamente, los efectos secundarios de esa robustez afectan a todos, incluso si ni ellos ni su cadena original reescriben la historia.
    2016-09-16 02: 37: 33Z
Una solución mucho más simple utilizando git stash

Entonces lo siguiente es mucho más simple (comenzando en la rama

git branch -t newbranch
git reset --hard HEAD~3
git checkout newbranch
que tiene tres confirmaciones erróneas): newbranch

¿Cuándo usar esto?

  • Si su propósito principal es revertir reset --hard
  • Desea mantener los cambios en el archivo
  • No te importan los mensajes en las confirmaciones erróneas
  • Aún no has empujado
  • Desea que esto sea fácil de memorizar
  • No desea complicaciones, como sucursales temporales /nuevas, encontrar y copiar hashes de confirmación y otros dolores de cabeza

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

  1. Deshace las últimas tres confirmaciones (y sus mensajes) a git rebase, pero deja intactos todos los archivos de trabajo
  2. Guarda todos los cambios de archivos de trabajo, haciendo que el árbol de trabajo --fork-point sea exactamente igual al estado HEAD ~ 3
  3. Cambia a una rama existente master
  4. Aplica los cambios ocultos a su directorio de trabajo y borra el alijo

Ahora puede utilizar -t y git branch como lo haría normalmente. Todas las confirmaciones nuevas se agregarán a git config --global branch.autosetuprebase always.

Lo que esto no hace

  • No deja ramas temporales aleatorias que abarroten tu árbol
  • No conserva los mensajes de confirmación y confirmación erróneos, 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 a Master antes de que se realizaran esas confirmaciones" sin perder los cambios y esta solución hace eso.

Hago esto al menos una vez a la semana cuando accidentalmente hago nuevas confirmaciones para master en lugar de 060035099111110101035062. Por lo general, solo tengo una confirmación para revertir, en cuyo caso, usar 0600350991110101010505050505050505035062 en la línea 1 es una forma más sencilla de enrollar solo una.

No hagas esto si presionaste los cambios del maestro en sentido ascendente

Alguien más puede haber retirado esos cambios. Si solo está reescribiendo su maestro local, no hay ningún impacto cuando se empuja en sentido ascendente, pero empujar un historial reescrito a los colaboradores puede causar dolores de cabeza.

    
65
2019-06-02 01: 19: 24Z
  1. Gracias, estoy tan contento de haber leído mucho para llegar hasta aquí, porque también es un caso de uso bastante común. ¿Somos tan atípicos?
    2018-09-10 17: 58: 03Z
  2. Creo que somos totalmente típicos y "oops, me he comprometido a dominar por error" es el caso de uso más común para la necesidad de revertir un puñado o menos de confirmaciones. Por suerte, esta solución es tan simple que la tengo memorizada ahora.
    2018-09-11 19: 58: 01Z
  3. Esta debe ser la respuesta aceptada. Es sencillo, fácil de entender y fácil de recordar
    2018-11-28 00: 53: 58Z
  4. Puede ser una solución pero no hace exactamente lo que se le pidió porque pierde los últimos 3 confirmaciones.
    2019-06-24 16: 17: 55Z

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

git reset HEAD~3
git stash
git checkout newbranch
git stash pop
    
29
2014-08-17 10: 23: 48Z
  1. ¿No puedes usar master para la misma cosa?
    2015-06-29 17: 37: 49Z
  2. Sí, como alternativa, podría usar master en la rama separada en el escenario anterior.
    2015-06-30 01: 32: 12Z

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

master

¡Ambas ramas pueden ser empujadas sin fuerza!

    
23
2016-01-21 16: 10: 36Z
  1. Pero luego tienes que lidiar con el escenario de reversión, que, dependiendo de tu circunstancia, puede ser mucho más complicado. Si revertir una confirmación en la rama, Git seguirá viendo esas confirmaciones como se han realizado, así que para deshacer eso, tienes que revertir la reversión. Esto quema a unas cuantas personas, especialmente cuando revierten una fusión e intentan fusionar la rama nuevamente, solo para encontrar que Git cree que ya está fusionada esa rama (lo cual es totalmente cierto).
    2016-04-07 16: 15: 37Z
  2. Es por eso que selecciono las confirmaciones al final, en una nueva rama. De esa manera, git los ve como nuevos compromisos, lo que resuelve tu problema.
    2016-04-07 17: 18: 57Z
  3. Esto es más peligroso de lo que parece al principio, ya que estás cambiando el estado de la historia del repositorio sin entender realmente las implicaciones de este estado.
    2016-04-07 19: 25: 22Z
  4. No sigo su argumento. El punto de esta respuesta es que no está cambiando el historial, simplemente agregando nuevos confirmaciones (que deshace los cambios). . Estas nuevas confirmaciones se pueden empujar y fusionar normalmente.
    2016-04-08 10: 33: 01Z

Tenía justo esta situación:

newbranch

Realicé:

git add

Esperaba que el compromiso fuera la CABEZA, pero confirma L ahora ...

Para estar seguro de llegar al lugar correcto en el historial, es más fácil trabajar con el hash del commit

git commit     
13
2013-09-20 10: 17: 35Z

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

newbranch

2) Luego vuelve a la rama antigua.

master

3) Hacer git rebase

develop

4) Luego, el editor abierto contiene los últimos 3 datos de confirmación.

git reset HEAD^

5) Cambie

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)
a rebase en todos esos 3 confirmaciones. Luego guarda y cierra el editor. rebase

6) Ahora se eliminan las últimas 3 confirmaciones de la rama actual (

git checkout master
git revert <commitID(s)>
git checkout -b new-branch
git cherry-pick <commitID(s)>
). Ahora empuje la rama con fuerza, con el signo
Branch one: A B C D E F     J   L M  
                       \ (Merge)
Branch two:             G I   K     N
antes del nombre de la rama.
git branch newbranch 
git reset --hard HEAD~8 
git checkout newbranch
    
2
2018-06-01 05: 40: 11Z

¿Cómo puedo pasar de esto

git branch newbranch 
git reset --hard #########
git checkout newbranch

a esto?

git checkout -b new_branch

Con dos comandos

  • git branch -m master newbranch

dando

git checkout master

y

  • git branch master B

dando

git rebase -i <short-hash-of-B-commit>
    
1
2019-05-16 12: 00: 50Z
  1. Sí, esto funciona y es bastante fácil. Sourcetree GUI está un poco confundido acerca de los cambios realizados en el shell de git, pero después de una búsqueda está bien otra vez.
    2019-05-24 15: 05: 48Z

Puedes hacer esto es solo 3 pasos simples que utilicé.

1) crea una nueva rama en la que quieras confirmar tu última actualización.

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

2) Buscar ID de compromiso reciente para confirmar en una nueva rama.

0600350991100101035062

3) Copie la nota de identificación de confirmación que la lista de confirmación más reciente tiene lugar en la parte superior. para que puedas encontrar tu commit. También encuentras esto vía mensaje.

pick

también puede proporcionar un rango de ID de confirmación.

0600350991100101035062

Ahora tu trabajo está hecho. Si eligió la identificación correcta y la rama correcta, entonces tendrá éxito. Así que antes de hacer esto ten cuidado. de lo contrario puede ocurrir otro problema.

Ahora puedes empujar tu código

0600350991100101035062

    
0
2018-05-25 14: 14: 27Z

Si solo necesitas mover todos tus no presionados a una nueva rama , entonces solo necesitas,

  1. cree una nueva rama a partir de la actual: drop

  2. presione su nueva rama :

    ...
    drop <C's hash> C
    drop <D's hash> D
    drop <E's hash> E
    ...
    
  3. revertir su rama (actual) al último estado estable /presionado: 0600350991100101035062

Algunas personas también tienen otros master en lugar de +, deben usar el número adecuado

git push origin +master
    
0
2019-06-17 07: 38: 10Z
A - B - C - D - E 
                |
                master
fuente colocada aquí