Cómo fusionar un commit específico en Git

1035

Bifurqué una rama de un repositorio en GitHub y cometí algo específico para mí. Ahora descubrí que el repositorio original tenía una buena característica que estaba en HEAD.

Quiero fusionarlo solo sin compromisos previos. ¿Qué tengo que hacer? Sé cómo fusionar todos los commits:

git branch -b a-good-feature
git pull repository master
git checkout master
git merge a-good-feature
git commit -a
git push
netawater
fuente
Si está tratando de hacer esto en relación con github, este artículo lo guiará a través de él. markosullivan.ca/how-to-handle-a-pull-request-from-github
johndpope

Respuestas:

1177

' git cherry-pick' debería ser tu respuesta aquí.

Aplicar el cambio introducido por una confirmación existente.

No olvide leer la respuesta de bdonlan sobre la consecuencia de la selección de cerezas en esta publicación:
"Extraiga todas las confirmaciones de una rama, empuje las confirmaciones especificadas a otra" , donde:

A-----B------C
 \
  \
   D

se convierte en:

A-----B------C
 \
  \
   D-----C'

El problema con este commit es que git considera los commits para incluir todo el historial antes que ellos

Donde C 'tiene una SHA-1identificación diferente .
Del mismo modo, elegir un compromiso de una rama a otra básicamente implica generar un parche, luego aplicarlo, perdiendo así el historial de esa manera.

Este cambio de ID de confirmación rompe la funcionalidad de fusión de git entre otras cosas (aunque si se usa con moderación, hay heurísticas que lo ocultarán).
Sin embargo, lo más importante es que ignora las dependencias funcionales: si C realmente usa una función definida en B, nunca lo sabrá .

VonC
fuente
1
@ openid000: "ramas de grano más fino": que es exactamente lo que bdonlan sugirió en su respuesta.
VonC
8
Nota: "git rebase" también cambia SHA-1. Consulte también "git rebase vs. git merge ( stackoverflow.com/questions/804115/git-rebase-vs-git-merge ) y" git workflow "( stackoverflow.com/questions/457927/… ) para casos en los que" git rebase "es legítimo.
VonC
1
Entre "ramas de grano fino", "selección de cereza" y "rebase", tendrá todas las posibilidades para administrar código en ramas con git.
VonC
@VonC "ramas de grano fino" sí. Tenía dos ramas e hice actualizaciones a un módulo de visualización particular en una rama. Este módulo era independiente de todos los demás códigos, por lo que cherry-pick fue muy conveniente para aplicar estos cambios también a otras sucursales
Cheeku
1
Pero tenga en cuenta que cuando se fusiona, tanto los commits C 'como C prevalecerán en el historial de commit.
Rahul Shah
739

Puede usar git cherry-pick para aplicar una sola confirmación por sí mismo a su rama actual.

Ejemplo: git cherry-pick d42c389f

bdonlan
fuente
68
+1 para su publicación anterior sobre selección de cerezas ( stackoverflow.com/questions/880957/… ). Me tomé la libertad de copiar un extracto en mi propia respuesta anterior.
VonC
44
Probablemente git cherry-pick d42co git cherry-pick d42c3 funcionará. Git es inteligente. ;)
guneysus
2
fatal: mala revisión
Ievgen Naida
3
Acabo de ejecutar este comando y parece haber funcionado, pero cuando hago el estado de git, dice "no hay nada que comprometerse, trabajo de árbol limpio". Cuando actualizo la página Commits en mi página web de bitbucket, no aparece. Pero aparece cuando ejecuto git log. Y veo el código modificado. ¿Alguien puede explicar si tengo que hacer algún paso adicional?
Ray
1
Lamentablemente, esto no responde a la pregunta: en realidad no crea una fusión. No hay ascendencia apuntando a d42c389f. Tal vez el OP no se preocupó por crear una fusión per se, pero la diferencia sí importa a veces.
LarsH
29

Intentemos tomar un ejemplo y entender:

Tengo una rama, digamos master , apuntando a X <commit-id>, y tengo una nueva rama apuntando a Y <sha1>.

Donde Y <commit-id> = <master> confirmaciones de rama - pocas confirmaciones

Ahora, para la rama Y, tengo que cerrar los commits entre la rama maestra y la nueva rama. A continuación se muestra el procedimiento que podemos seguir:

Paso 1:

git checkout -b local origin/new

donde local es el nombre de la sucursal. Cualquier nombre puede ser dado.

Paso 2:

  git merge origin/master --no-ff --stat -v --log=300

Combine las confirmaciones de la rama maestra a la nueva rama y también cree una confirmación de fusión del mensaje de registro con descripciones de una línea de, como máximo, <n> confirmaciones reales que se fusionan.

Para obtener más información y parámetros sobre la fusión de Git, consulte:

git merge --help

Además, si necesita fusionar una confirmación específica, puede usar:

git cherry-pick <commit-id>
Spyder
fuente
¿Cambiaste la definición de Yen tu oración 3d? "Tengo una nueva rama apuntando a Y" vs "Ahora digamos para la rama Y", parece que Y solía ser una confirmación y luego se convirtió en una rama
Purefan
cómo puedo hacer eso desde cierto punto de compromiso de una rama que quiero fusionar
Kulbhushan Singh
3

En mi caso de uso, teníamos una necesidad similar de CI CD. Utilizamos git flow con desarrollo y ramas maestras. Los desarrolladores son libres de fusionar los cambios directamente para desarrollar o mediante una solicitud de extracción de una rama de características. Sin embargo, para dominar, fusionamos solo los commits estables de la rama de desarrollo de forma automatizada a través de Jenkins.

En este caso, hacer una selección de cerezas no es una buena opción. Sin embargo, creamos una rama local a partir del commit-id y luego fusionamos esa rama local para dominar y realizar mvn clean verify (utilizamos maven). Si tiene éxito, libere el artefacto de la versión de producción a nexus utilizando el complemento de lanzamiento de Maven con localCheckout = true option y pushChanges = false. Finalmente, cuando todo sea exitoso, empuje los cambios y etiquételos al origen.

Un fragmento de código de muestra:

Suponiendo que está en el maestro si se hace manualmente. Sin embargo, en Jenkins, cuando finalice la compra del repositorio, estará en la rama predeterminada (maestro si está configurado).

git pull  // Just to pull any changes.
git branch local-<commitd-id> <commit-id>  // Create a branch from the given commit-id
git merge local-<commit-id>  // Merge that local branch to master.
mvn clean verify   // Verify if the code is build able
mvn <any args> release:clean release:prepare release:perform // Release artifacts
git push origin/master  // Push the local changes performed above to origin.
git push origin <tag>  // Push the tag to origin

Esto te dará un control total con una fusión sin miedo o un infierno de conflicto.

No dude en asesorar en caso de que haya una mejor opción.

nrkkalyan
fuente
3

Las respuestas principales describen cómo aplicar los cambios de una confirmación específica a la rama actual. Si eso es lo que quieres decir con "cómo fusionar", simplemente usa cherry-pick como sugieren.

Pero si realmente desea una fusión , es decir, desea una nueva confirmación con dos padres (la confirmación existente en la rama actual y la confirmación a la que desea aplicar los cambios), entonces una selección de cereza no logrará eso.

Puede ser deseable tener un verdadero historial de fusión, por ejemplo, si su proceso de compilación aprovecha la ascendencia git para establecer automáticamente cadenas de versión basadas en la última etiqueta (usando git describe).

En lugar de seleccionar Cherry, puede hacer un real git merge --no-commity luego ajustar manualmente el índice para eliminar cualquier cambio que no desee.

Supongamos que está en la rama Ay desea fusionar el commit en la punta de la rama B:

git checkout A
git merge --no-commit B

Ahora está configurado para crear un compromiso con dos padres, el consejo actual se compromete con Ay B. Sin embargo, es posible que se apliquen más cambios de los que desea, incluidos los cambios de confirmaciones anteriores en la rama B. Debe deshacer estos cambios no deseados y luego confirmar.

(Puede haber una manera fácil de configurar el estado del directorio de trabajo y el índice de nuevo a la forma en que estaba antes de la fusión, para que tenga una pizarra limpia sobre la cual elegir el compromiso que quería en primer lugar. Pero No sé cómo lograr esa pizarra limpia, git checkout HEADy git reset HEADambos eliminarán el estado de fusión, lo que anulará el propósito de este método).

Así que deshaga manualmente los cambios no deseados. Por ejemplo, podrías

git revert --no-commit 012ea56

por cada commit no deseado 012ea56.

Cuando termine de ajustar las cosas, cree su compromiso:

git commit -m "Merge in commit 823749a from B which tweaked the timeout code"

Ahora solo tiene el cambio que desea, y el árbol de ascendencia muestra que técnicamente se fusionó con B.

LarsH
fuente