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
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
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 reset
moviste el puntero master hacia atrás dos commits. Pero como no moviste newBranch, todavía apunta a la confirmación que hizo originalmente.
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.
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.
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 queHEAD
utiliza para referirse a 2 operaciones hace, es decir, antes de we 1. salidanewbranch
y 2. se usagit 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.
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)
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!
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
- Deshace las últimas tres confirmaciones (y sus mensajes) a
master
, pero deja intactos todos los archivos de trabajo - Guarda todos los cambios del archivo de trabajo, haciendo que el árbol de trabajo
master
sea igual al estado HEAD~3 - cambia a una rama existente
newbranch
- 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.
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
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
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
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