mover cambios comprometidos (pero no empujados) a una nueva rama después de tirar

460

He trabajado bastante ("Su rama está por delante de 'origin / master' en 37 commits"), que realmente debería haber entrado en su propia rama en lugar de en master. Estas confirmaciones solo existen en mi máquina local y no han sido presionadas origin, pero la situación es un tanto complicada porque otros desarrolladores han estado presionando origin/mastery he eliminado esos cambios.

¿Cómo muevo retroactivamente mis 37 commits locales a una nueva sucursal? Sobre la base de los documentos, parece ser que git rebase --onto my-new-branch mastero ...origin/masterdebería hacerlo, pero ambos me acaba de dar el error "fatal: Se necesita una única revisión". man git-rebaseno dice nada acerca de proporcionar una revisión rebasey sus ejemplos no lo hacen, así que no tengo idea de cómo resolver este error.

(Tenga en cuenta que esto no es un duplicado de Mover el trabajo no comprometido existente a una nueva sucursal en Git o ¿Cómo fusionar mis cambios locales no comprometidos en otra rama de Git? Ya que esas preguntas tratan con cambios no comprometidos en el árbol de trabajo local, no cambios que tienen sido comprometido localmente)

Dave Sherohman
fuente
Mira esta solución . Parece ser fácil y limpio.
Tony
Mira esta solución . Parece ser fácil y limpio.
Tony

Respuestas:

518

Esto debería estar bien, ya que aún no ha empujado sus confirmaciones en ningún otro lugar, y puede volver a escribir el historial de su rama después origin/master. Primero ejecutaría un git fetch originpara asegurarme de que origin/masteresté actualizado. Asumiendo que estás actualmente master, deberías poder hacer:

git rebase origin/master

... que reproducir todos tus commits que no están en origin/mastera origin/master. La acción predeterminada de rebase es ignorar las confirmaciones de fusión (por ejemplo, aquellas que git pullprobablemente haya introducido) y solo intentará aplicar el parche introducido por cada una de sus confirmaciones origin/master. (Puede que tenga que resolver algunos conflictos en el camino). Luego, puede crear su nueva sucursal en función del resultado:

git branch new-work

... y luego restablece tu masterespalda a origin/master:

# Use with care - make sure "git status" is clean and you're still on master:
git reset --hard origin/master

Al hacer este tipo de manipulación de ramas con git branch, git resetetc., me resulta útil mirar con frecuencia el gráfico de compromiso con gitk --allo una herramienta similar, solo para comprobar que entiendo a dónde apuntan las diferentes referencias.

Alternativamente, podría haber creado una rama de tema en función de dónde se encuentra su maestro en primer lugar ( git branch new-work-including-merges) y luego restablecer mastercomo se indicó anteriormente. Sin embargo, dado que la rama de su tema incluirá fusiones origin/mastery aún no ha presionado sus cambios, le sugiero que haga una nueva modificación para que el historial sea más ordenado. (Además, cuando finalmente fusiones la rama de tu tema de nuevo a master, los cambios serán más obvios).

Mark Longair
fuente
8
@Olie: No, la respuesta es correcta bajo los supuestos de la pregunta y los que expuse en la parte superior de la respuesta. Los commits que deberían estar en una nueva rama separada ya están en master; el rebase vuelve a escribir la masterrama para que los nuevos commits estén linealmente encima origin/master, luego git branch new-workcrea una new-workrama apuntando a la punta de master(la rama actual) sin cambiar la rama actual a new-work. Así que ahora new-workcontiene todos los nuevos commits. Luego, el restablecimiento mueve la rama actual (aún master) de nuevo a origin/master.
Mark Longair
1
@Quintesse: Lamento mucho si ha perdido el trabajo, pero estoy seguro de que esta respuesta es correcta para la situación descrita por el interrogador original. (Acabo de responder a los comentarios de Olie, espero aclarar). De todos modos, en caso de que ayude a recuperar su trabajo, debería decir que si su trabajo se comprometió en los últimos días (una de las suposiciones en esta pregunta y respuesta ) deberías poder recuperarlo fácilmente a través del git reflog.
Mark Longair
55
@Olie: quizás una mejor manera de explicar: las ramas en git son como etiquetas que apuntan a un commit en particular; se mueven automáticamente a nuevas confirmaciones si las crea mientras está en esa rama o se puede mover con git resetotras formas. El git branch new-workes sólo decir "crear un señalador rama en este cometen mientras me quedo en mi rama actual (que es el amo en este caso)". Por lo tanto, no es necesario tener un comando que mueva las confirmaciones del maestro a la nueva rama: simplemente crea una nueva rama allí y cuando reinicia el maestro, la nueva rama queda donde estaba el maestro
Mark Longair
1
un poco tarde para la fiesta, pero @Olie, solo porque el estado de git cuando está en la nueva sucursal no muestra los compromisos antes del maestro no significa que no estén realmente allí (suponiendo que por eso estaba preocupado). Intenta llevar la nueva rama al origen: verás que los commits están allí
Félix Gagnon-Grenier
2
@ FélixGagnon-Grenier, no te preocupes por el "retraso": siempre hay personas que buscan preguntas antiguas y cada aclaración ayuda. ¡Gracias! :)
Olie
148

Si tiene un número bajo de commits y no le importa si se combinan en un mega-commit, esto funciona bien y no es tan aterrador como hacerlo git rebase:

desestabilizar los archivos (reemplazar 1 con # de confirmaciones)

git reset --soft HEAD~1

crear una nueva sucursal

git checkout -b NewBranchName

agregar los cambios

git add -A

hacer un compromiso

git commit -m "Whatever"
Stachu
fuente
55
Para mostrar un gráfico fácil de entender, utilice git log --all --decorate --oneline --graph.
EliuX
Hola @EliuX: me falta la relevancia aquí. Se puede ampliar?
Stachu
Esto es algo útil para verificar si obtuviste el resultado deseado en lo que hiciste
EliuX
44
¡¡Gracias!! ¡esta es una solución muy simple y funcionó perfectamente!
Chris Sim
91

Me quedé con el mismo problema. He encontrado la solución más fácil que me gusta compartir.

1) Crea una nueva rama con tus cambios.

git checkout -b mybranch

2) (Opcional) Inserte un nuevo código de sucursal en el servidor remoto.

git push origin mybranch

3) Pago de vuelta a la sucursal maestra.

git checkout master

4) Restablezca el código de sucursal maestro con el servidor remoto y elimine la confirmación local.

git reset --hard origin/master
NiRmaL
fuente
10
Eso es realmente la manera más fácil
dhilt
44
Puede omitir el paso 2. Supongo que en el tiempo transcurrido desde la respuesta principal, git ha cambiado y este procedimiento no estaba permitido antes.
Sebastian
Esta es la mejor respuesta en mi opinión.
Macindows
1
Esta respuesta necesita pasar a la cima. Gracias.
amit el
27

Una forma más de asumir que branch1 - es branch con cambios comprometidos branch2 - es branch deseado

git fetch && git checkout branch1
git log

seleccione los ID de confirmación que necesita mover

git fetch && git checkout branch2
git cherry-pick commit_id_first..commit_id_last
git push

Ahora revierta las confirmaciones no apresuradas de la rama inicial

git fetch && git checkout branch1
git reset --soft HEAD~1
Andriy
fuente
55
Cherry-pick es realmente el mejor comando "copiar / mover un solo commit", especialmente. cuando la historia es equipaje para sus propósitos.
John Neuhaus
Esta es hasta ahora la respuesta más conveniente a la pregunta. Gracias por el comando!
Farah
¿Puedes actualizar tu último comentario donde 1 o n es el número de confirmaciones no apresuradas? Esta sigue siendo una muy buena solución para esta pregunta.
chAlexey
9

Alternativamente, justo después de comprometerse con la rama incorrecta, realice estos pasos:

  1. git log
  2. git diff {previous to last commit} {latest commit} > your_changes.patch
  3. git reset --hard origin/{your current branch}
  4. git checkout -b {new branch}
  5. git apply your_changes.patch

Me imagino que hay un enfoque más simple para los pasos uno y dos.

Andreas B
fuente
6

Qué pasa:

  1. Se ramifica desde la CABEZA actual.
  2. Asegúrese de estar en master , no en su nueva sucursal.
  3. git reset volver a la última confirmación antes de comenzar a hacer cambios.
  4. git pull para volver a tirar solo los cambios remotos que arrojó con el reinicio.

¿O eso explotará cuando intentes volver a fusionar la rama?

Tim Keating
fuente
2
Ah, esta es básicamente la opción B descrita por @ Mark-Longair arriba
Tim Keating
2

Aquí hay una manera mucho más simple:

  1. Crea una nueva sucursal

  2. En su nueva sucursal, haga una git merge master: esto fusionará sus cambios comprometidos (no empujados) a su nueva sucursal

  3. Elimine su rama maestra local git branch -D masterUse en -Dlugar de -dporque desea forzar la eliminación de la rama.

  4. Simplemente haga una git fetchen su rama maestra y haga una git pullen su rama maestra para asegurarse de tener el último código de su equipo.

Un kok
fuente
1

Un enfoque más simple, que he estado usando (suponiendo que quiera mover 4 commits):

git format-patch HEAD~4

(Busque en el directorio desde el que ejecutó el último comando para los 4 .patcharchivos)

git reset HEAD~4 --hard

git checkout -b tmp/my-new-branch

Entonces:

git apply /path/to/patch.patch

En el orden que quisieras.

usuario1429980
fuente
0
  1. Pagar copia fresca de sus fuentes

    git clone ........

  2. Hacer rama desde la posición deseada

    git checkout {position} git checkout -b {branch-name}

  3. Agregar repositorio remoto

    git remote add shared ../{original sources location}.git

  4. Obtenga fuentes remotas

    git fetch shared

  5. Checkout rama deseada

    git checkout {branch-name}

  6. Combinar fuentes

    git merge shared/{original branch from shared repository}

Sergey Kabashnyuk
fuente
0

Para mí esta fue la mejor manera:

  1. Verificar cambios y fusionar conflictos git fetch
  2. Cree una nueva sucursal git branch my-changesy empuje a remoto
  3. Cambiar aguas arriba a una nueva sucursal creada git master -u upstream-branch remotes/origin/my-changes
  4. Empuje sus compromisos a la nueva rama ascendente.
  5. Vuelva al flujo anterior. git branch master --set-upstream-to remotes/origin/master
Sebastian
fuente