ver una trabajando seleccionar ramas rama que moverse las funcionan crear con como añadir git git-branch branching-and-merging

trabajando - Mueva los compromisos más recientes a una nueva rama con Git



seleccionar una rama git (11)

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: newbranch confirmación del maestro que desea en un newbranch

Ejecutar

git checkout master git log

Tenga en cuenta los hashes de (digamos 3) las 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 - newbranch en el newbranch

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

O (en Git 1.7.2+, usa rangos)

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

git cherry-pick aplica esos tres compromisos a newbranch.

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?

Es decir, ¿cómo puedo ir de esto?

master A - B - C - D - E

¿a esto?

newbranch C - D - E / master A - B


Solución mucho más simple usando git stash

Si:

  • Su objetivo principal es revertir master , y
  • Desea mantener los cambios pero no se preocupa especialmente por los compromisos individuales, y
  • Aún no has empujado, y
  • 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 el master rama que tiene tres confirmaciones erróneas):

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

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

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

Ahora puedes usar git add y git commit como lo harías normalmente. Todas las nuevas confirmaciones se agregarán a newbranch .

Lo que esto no hace

  • No deja ramas temporales aleatorias que abarrotan 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.

Metas

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 lo hace.

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

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

Alguien más puede haber sacado 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.


¡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 ejecute git rebase (o git pull --rebase ), esas 3 confirmaciones se descartarán silenciosamente de newbranch ! (ver explicación abajo)

En su lugar, haga esto:

git reset --keep HEAD~3 git checkout -t -b newbranch git cherry-pick ..HEAD@{2}

  • Primero, descarta los 3 compromisos más recientes ( --keep es como --hard , pero más seguro, ya que falla en lugar de tirar los cambios no confirmados).
  • Luego se bifurca de newbranch .
  • Luego selecciona esos 3 compromisos de nuevo en newbranch . Dado que ya no están referenciados por una rama, lo hace al usar el reflog de git: HEAD@{2} es el compromiso que HEAD usaba para referirse a 2 operaciones anteriores, es decir, antes de que 1. comprobáramos newbranch y 2. usamos git reset para descartar las 3 confirmaciones.

Advertencia: la reinicialización está habilitada de forma predeterminada, pero si la ha desactivado manualmente (por ejemplo, utilizando un repositorio de git "simple"), no podrá recuperar los 3 confirmaciones después de ejecutar git reset --keep HEAD~3 .

Una alternativa que no se basa en el 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 retirada - en lugar de oldbranch ).

Explicacion tecnica

¿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 de forma predeterminada, que utiliza el reflog local para intentar ser robusto contra la derivación ascendente forzada.

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

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

pero luego alguien vuelve a escribir la historia mediante el origen / maestro de empuje forzado 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 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 ascendente, ya no lo querrá en su rama temática una vez que la rama temática se vuelva a basar:

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

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

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 considera que newbranch ha desviado de la rama ascendente en una revisión que incluye las 3 confirmaciones, luego el reset --hard reescribe el historial de la reset --hard ascendente para eliminar las confirmaciones, por lo que la próxima vez que ejecute git rebase , las descartará como cualquier otra confirmación que ha sido eliminado de la corriente arriba.

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 las 3 confirmaciones. Eso es lo que hacen mis soluciones sugeridas, por lo tanto, ambos dejan el reflog en el estado correcto.

Para obtener más detalles, consulte la definición de --fork-point en git rebase y git merge-base docs.


Mudarse a una nueva sucursal

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 combinar sus cambios en la rama existente antes de ejecutar git reset --hard HEAD~3 (consulte Cómo moverse a una rama existente más abajo). Si no fusionas tus 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 (GONE from master) 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 de la confirmación (o la referencia como origen / maestro ) que desea "volver a" en la rama maestra (/ actual), por ejemplo:

git reset --hard a1b2c3d4

* 1 Solo estarás "perdiendo" las confirmaciones de la rama maestra, pero no te preocupes, ¡tendrás esas confirmaciones en la nueva rama!

ADVERTENCIA: con la versión 2.0 de Git y posterior, si luego git rebase la nueva rama en la rama original ( master ), es posible que necesite una --no-fork-point explícita --no-fork-point durante la rebase para evitar perder las confirmaciones acumuladas. Tener branch.autosetuprebase always establecido hace que esto sea más probable. Vea la respuesta de John Mellor para más detalles.

Mudarse a una sucursal existente

Si desea mover sus compromisos 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


1) Cree una nueva rama, que mueva todos sus cambios a new_branch.

git checkout -b new_branch

2) Luego regresa a la rama vieja.

git checkout master

3) Hacer git rebase

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

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

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

5) Cambiar pick para drop en todas esas 3 confirmaciones. Luego guarda y cierra el editor.

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

6) Ahora se eliminan las últimas 3 confirmaciones de la rama actual ( master ). Ahora empuje la rama con fuerza, con el signo + antes del nombre de la rama.

git push origin +master


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)


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 aprendiera sobre 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.


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:

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, el maestro apuntó a la última confirmación. Cuando hizo newBranch , simplemente hizo un nuevo puntero a la última confirmación. Luego, utilizando git reset , movió el puntero maestro hacia atrás dos confirmaciones. Pero como no moviste NewBranch , todavía apunta a la confirmación que hizo originalmente.


Para hacer esto sin volver a escribir el historial (es decir, si ya has presionado los 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!


Tenía justo esta situación:

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

Yo actué:

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

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

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

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


Usted puede hacer esto es solo 3 pasos simples que he usado.

1) Crea una nueva rama en la que quieras confirmar tu actualización reciente.

git branch <branch name>

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

git log

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 compromiso. También encuentras esto vía mensaje.

git cherry-pick d34bcef232f6c...

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

git cherry-pick d34bcef...86d2aec

Ahora tu trabajo 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

git push