¿Cómo combinar selectivamente o elegir cambios de otra rama en Git?

1450

Estoy usando git en un nuevo proyecto que tiene dos ramas de desarrollo paralelas, pero actualmente experimentales:

  • master: importación de la base de código existente más algunas modificaciones de las que generalmente estoy seguro
  • exp1: rama experimental # 1
  • exp2: rama experimental # 2

exp1y exp2representan dos enfoques arquitectónicos muy diferentes. Hasta que avance, no tengo forma de saber cuál (si alguno) funcionará. A medida que avanzo en una rama, a veces tengo ediciones que serían útiles en la otra rama y me gustaría fusionar solo esas.

¿Cuál es la mejor manera de fusionar cambios selectivos de una rama de desarrollo a otra dejando atrás todo lo demás?

Enfoques que he considerado:

  1. git merge --no-commit seguido de la eliminación manual de una gran cantidad de ediciones que no quiero hacer comunes entre las ramas.

  2. Copia manual de archivos comunes en un directorio temporal seguido de git checkoutpara pasar a la otra rama y luego copia más manual del directorio temporal en el árbol de trabajo.

  3. Una variación de lo anterior. Abandone las expsucursales por ahora y use dos repositorios locales adicionales para la experimentación. Esto hace que la copia manual de archivos sea mucho más sencilla.

Los tres enfoques parecen tediosos y propensos a errores. Espero que haya un mejor enfoque; algo parecido a un parámetro de ruta de filtro que lo haría git-mergemás selectivo.

David Joyner
fuente
55
Si los cambios en sus ramas experimentales están bien organizados en confirmaciones separadas, es mejor pensar en términos de fusionar confirmaciones selectivas en lugar de archivos selectivos. La mayoría de las respuestas a continuación asumen que este es el caso.
akaihola
2
¿Una combinación de git merge -s ours --no-commitseguido por algunos no git read-treesería una buena solución para esto? Ver stackoverflow.com/questions/1214906/…
VonC
34
Una pregunta más reciente tiene una respuesta bien escrita de una línea: stackoverflow.com/questions/10784523/…
brahn
Consulte este blog para fusionar solo archivos específicos jasonrudolph.com/blog/2009/02/25/…
Ashutosh Chamoli

Respuestas:

475

Utiliza el comando cherry-pick para obtener confirmaciones individuales de una rama.

Si los cambios que desea no están en confirmaciones individuales, utilice el método que se muestra aquí para dividir la confirmación en confirmaciones individuales . Hablando en términos generales, se usa git rebase -ipara que el compromiso original se edite, luego git reset HEAD^para revertir selectivamente los cambios, luego git commitpara confirmar ese bit como un nuevo compromiso en el historial.

Hay otro buen método aquí en Red Hat Magazine, donde lo usan git add --patcho posiblemente lo git add --interactiveque le permite agregar solo partes de un trozo, si desea dividir diferentes cambios en un archivo individual (busque en esa página "dividir").

Después de dividir los cambios, ahora puede elegir los que desee.

1800 INFORMACIÓN
fuente
14
Según tengo entendido, esto es innecesariamente más complicado que la respuesta más votada.
Alexander Bird
54
Esta es técnicamente la respuesta correcta, la respuesta correcta parece "enrevesada". --- La respuesta más votada es solo una respuesta rápida y sucia "hace el truco", que para la mayoría de las personas es de lo que se trata (:
Jacob
3
@akaihola: HEAD ^ es correcto. Ver man git-rev-parse: un sufijo ^ a un parámetro de revisión significa el primer padre de ese objeto de confirmación. La notación prefijo ^ se utiliza para excluir confirmaciones accesibles desde una confirmación.
Tyler Rick
13
Solo quería compartir otro enfoque que parece el más limpio y menos complicado de todos: jasonrudolph.com/blog/2009/02/25/… simplicidad total e increíble
superuseroi
14
¿Confundido por el debate sobre qué enfoque es "correcto"? Considere la diferencia entre archivos y confirmaciones (vea Comentarios en la parte inferior) . OP quiere fusionar ARCHIVOS y no menciona los COMPROMISOS. La respuesta más votada es específica de los archivos; la respuesta aceptada usa cherry-pick, que es específica de commits. Cherry-pick puede ser clave para fusionar commits selectivamente, pero puede ser muy doloroso para mover archivos de una rama a otra. Aunque los commits son el corazón de la fuerza de git, ¡no olvides que los archivos todavía tienen un papel!
Kay V
970

Tuve exactamente el mismo problema mencionado anteriormente. Pero encontré esto más claro al explicar la respuesta.

Resumen:

  • Verifique las rutas desde la rama que desea fusionar,

    $ git checkout source_branch -- <paths>...
    

    Sugerencia: también funciona sin lo --visto en la publicación vinculada.

  • o para fusionar selectivamente trozos

    $ git checkout -p source_branch -- <paths>...
    

    Alternativamente, use reset y luego agregue con la opción -p,

    $ git reset <paths>...
    $ git add -p <paths>...
    
  • Finalmente cometer

    $ git commit -m "'Merge' these changes"
    
Chris Steinbach
fuente
99
El artículo vinculado de Bart J es el mejor enfoque. Claro, simple, un comando. Es el que estoy a punto de usar. :)
Pistos
256
Esto no es una fusión real. Está seleccionando los cambios por archivo en lugar de por confirmación, y perderá cualquier información de confirmación existente (autor, mensaje). De acuerdo, esto es bueno si desea fusionar todos los cambios en algunos archivos y está bien que deba volver a hacer todas las confirmaciones. Pero si los archivos contienen cambios para fusionar y otros para descartar, uno de los métodos proporcionados en otras respuestas le servirá mejor.
akaihola
10
@mykhal y otros: esto pone en escena los archivos en el índice automáticamente, por lo que si desprotegido foo.c, git reset HEAD foo.cpuede desestabilizar ese archivo y luego puede diferenciarlo. Descubrí esto después de intentarlo y volver aquí para buscar una respuesta a esto
Michiakig
12
para ver los cambios que también podrías usar:git diff --cached
OderWat
99
Según esta respuesta git checkout -p <revision> -- <path> , será lo mismo que emitir los primeros tres comandos que describió :)
7hi4g0
338

Para fusionar selectivamente archivos de una rama a otra rama, ejecute

git merge --no-ff --no-commit branchX

¿De dónde branchXes la rama que desea fusionar en la rama actual?

La --no-commitopción organizará los archivos que Git ha fusionado sin comprometerlos realmente. Esto le dará la oportunidad de modificar los archivos combinados como desee y luego confirmarlos usted mismo.

Dependiendo de cómo desee fusionar archivos, hay cuatro casos:

1) Quieres una verdadera fusión.

En este caso, acepta los archivos combinados de la forma en que Git los fusionó automáticamente y luego los confirma.

2) Hay algunos archivos que no desea fusionar.

Por ejemplo, desea conservar la versión en la rama actual e ignorar la versión en la rama de la que se está fusionando.

Para seleccionar la versión en la rama actual, ejecute:

git checkout HEAD file1

Esto recuperará la versión de file1en la rama actual y sobrescribirá la file1automatizada por Git.

3) Si desea la versión en branchX (y no una verdadera fusión).

Correr:

git checkout branchX file1

Esto recuperará la versión de file1in branchXy sobrescribirá file1automáticamente fusionada por Git.

4) El último caso es si desea seleccionar solo fusiones específicas file1.

En este caso, puede editar el modificado file1directamente, actualizarlo a lo que quiera que sea la versión file1y luego confirmar.

Si Git no puede fusionar un archivo de forma automática, se reportará el archivo como " sin combinar " y producir una copia en la que deberá resolver los conflictos de forma manual.



Para explicar más a fondo con un ejemplo, supongamos que desea fusionarse branchXen la rama actual:

git merge --no-ff --no-commit branchX

Luego ejecuta el git statuscomando para ver el estado de los archivos modificados.

Por ejemplo:

git status

# On branch master
# Changes to be committed:
#
#       modified:   file1
#       modified:   file2
#       modified:   file3
# Unmerged paths:
#   (use "git add/rm <file>..." as appropriate to mark resolution)
#
#       both modified:      file4
#

Donde file1, file2y file3son los archivos que git se han fusionado automáticamente con éxito.

Lo que esto significa es que los cambios en mastery branchXpara todos esos tres archivos se han combinado sin ningún conflicto.

Puede inspeccionar cómo se realizó la fusión ejecutando git diff --cached;

git diff --cached file1
git diff --cached file2
git diff --cached file3

Si encuentra alguna fusión indeseable, entonces puede

  1. edite el archivo directamente
  2. salvar
  3. git commit

Si no desea fusionar file1y desea conservar la versión en la rama actual

correr

git checkout HEAD file1

Si no desea fusionar file2y solo desea la versión enbranchX

correr

git checkout branchX file2

Si desea file3fusionarse automáticamente, no haga nada.

Git ya lo ha fusionado en este punto.


file4arriba es una fusión fallida por Git. Esto significa que hay cambios en ambas ramas que ocurren en la misma línea. Aquí es donde deberá resolver los conflictos manualmente. Puede descartar la fusión realizada editando el archivo directamente o ejecutando el comando de pago para la versión en la rama en la que desea file4convertirse.


Finalmente, no te olvides git commit.

alvinabad
fuente
10
Sin embargo, git merge --no-commit branchXtenga cuidado: si el es solo un avance rápido, el puntero se actualizará y, por lo tanto, el --no-commit se ignora silenciosamente
cfi
16
@cfi ¿Qué hay de agregar --no-ffpara prevenir ese comportamiento?
Eduardo Costa
55
Definitivamente sugiero actualizar esta respuesta con la opción "--no-ff" de Eduardo. Leí todo el asunto (que por lo demás fue genial) solo para que mi fusión se reenviara rápidamente.
Funktr0n
77
Esta solución ofrece los mejores resultados y flexibilidad.
Thiago Macedo
20
A diferencia de la respuesta con la mayoría de los votos, esta solución conservó mi historial de fusión, lo cual es importante para mí ya que entretejo compromisos parciales de ida y vuelta a través de las ramas. No probé todas las otras soluciones sugeridas, por lo que quizás algunas de ellas también lo hagan.
ws_e_c421
107

No me gustan los enfoques anteriores. Usar cherry-pick es excelente para elegir un solo cambio, pero es una molestia si desea incorporar todos los cambios, excepto algunos malos. Aquí está mi enfoque.

No hay --interactiveargumento que pueda pasar para git merge.

Aquí está la alternativa:

Tiene algunos cambios en la 'función' de la rama y desea llevar algunos, pero no todos, a 'dominar' de una manera no descuidada (es decir, no quiere elegir y comprometer cada uno)

git checkout feature
git checkout -b temp
git rebase -i master

# Above will drop you in an editor and pick the changes you want ala:
pick 7266df7 First change
pick 1b3f7df Another change
pick 5bbf56f Last change

# Rebase b44c147..5bbf56f onto b44c147
#
# Commands:
# pick = use commit
# edit = use commit, but stop for amending
# squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

git checkout master
git pull . temp
git branch -d temp

Así que simplemente envuelva eso en un script de shell, cambie master a $ to y cambie la función a $ from y estará listo:

#!/bin/bash
# git-interactive-merge
from=$1
to=$2
git checkout $from
git checkout -b ${from}_tmp
git rebase -i $to
# Above will drop you in an editor and pick the changes you want
git checkout $to
git pull . ${from}_tmp
git branch -d ${from}_tmp
nosatalian
fuente
Arreglé el formato: este es un método bastante bueno, si desea hacer una selección de confirmaciones
1800 INFORMACIÓN
Estoy usando esta técnica ahora y parece haber funcionado realmente bien.
dylanfm
44
Es posible que desee cambiar git rebase -i $toa git rebase -i $to || $SHELL, para que el usuario pueda llamar git --skip, etc., según sea necesario si falla la nueva versión. También vale la pena encadenar las líneas en &&lugar de las nuevas líneas.
sircolinton
2
Desafortunadamente, parece que el enlace en la respuesta está muerto.
ThomasW
El enlace no solo está inactivo, sino que tiene una advertencia de mala reputación de WOT. Por eso lo quité.
Jean-François Corbett
93

Hay otra forma de hacerlo:

git checkout -p

Es una mezcla entre git checkouty git add -ppodría ser exactamente lo que estás buscando:

   -p, --patch
       Interactively select hunks in the difference between the <tree-ish>
       (or the index, if unspecified) and the working tree. The chosen
       hunks are then applied in reverse to the working tree (and if a
       <tree-ish> was specified, the index).

       This means that you can use git checkout -p to selectively discard
       edits from your current working tree. See the “Interactive Mode”
       section of git-add(1) to learn how to operate the --patch mode.
Crónica
fuente
10
Este es, con mucho, el método más sencillo y sencillo, siempre y cuando solo tenga una cantidad manejable de cambios para combinar. Espero que más personas noten esta respuesta y la voten. Ejemplo: git checkout --patch exp1 file_to_merge
Tyler Rick
1
Respuesta similar publicada en esta pregunta: stackoverflow.com/a/11593308/47185
Tyler Rick
¡Oh, no sabía que el pago tenía un parche! Hice checkout / reset / add -p en su lugar.
Daniel C. Sobral
2
Realmente el método más simple. git checkout -p featurebranch nombre de archivo. Y lo mejor es que cuando se ejecuta el comando, te da ay / n / e /? / ... etc. opción para decidir cómo fusionar el archivo. Intenté con e e incluso pude editar el parche antes de aplicar ... Qué bueno es. Un verdadero revestimiento para combinar archivos selectivos de otras ramas.
Infoclogged
55

Si bien algunas de estas respuestas son bastante buenas, siento que ninguna realmente respondió la restricción original de OP: seleccionar archivos particulares de ramas particulares. Esta solución hace eso, pero puede ser tedioso si hay muchos archivos.

Digamos que tiene las master, exp1y exp2las ramas. Desea fusionar un archivo de cada una de las ramas experimentales en maestro. Haría algo como esto:

git checkout master
git checkout exp1 path/to/file_a
git checkout exp2 path/to/file_b

# save these files as a stash
git stash
# merge stash with master
git merge stash

Esto le dará diferencias en el archivo para cada uno de los archivos que desee. Nada mas. Nada menos. Es útil que tenga cambios de archivo radicalmente diferentes entre versiones, en mi caso, cambiar una aplicación de Rails 2 a Rails 3.

EDITAR : esto fusionará archivos, pero hace una combinación inteligente. No pude averiguar cómo usar este método para obtener información de diferencias en el archivo (tal vez aún lo hará por diferencias extremas. Pequeñas cosas molestas como los espacios en blanco se fusionan de nuevo a menos que use la -s recursive -X ignore-all-spaceopción)

Eric Hu
fuente
44
También nota: se puede hacer varios archivos de una rama dado todo en línea, por ejemplogit checkout exp1 path/to/file_a path/to/file_x
EMiller
2
Esto es hermoso. Lo hice git checkout feature <path>/*para obtener grupos de archivos.
isherwood
Esto funciona bien, pero agregó dos objetos de confirmación adicionales. No es un gran problema, pero un poco desordenado
MightyPork
@MightyPork tienes razón. Desafortunadamente, desde que escribí esto hace tanto tiempo, ya no estoy seguro de por qué los pasos "git stash" y "git merge stash" están ahí en lugar de "git commit".
Eric Hu
2
Oh, eso está claro, creo. De esta manera, fusiona un archivo, no necesariamente sobrescribiendo los cambios anteriores en la rama de destino.
MightyPork
48

La respuesta de 1800 INFORMACIÓN es completamente correcta. Sin embargo, como git noob, "usar git cherry-pick" no fue suficiente para que me diera cuenta sin investigar un poco más en Internet, así que pensé en publicar una guía más detallada en caso de que alguien más esté en un barco similar

Mi caso de uso era querer tirar selectivamente los cambios de la rama github de otra persona a la mía. Si ya tiene una sucursal local con los cambios, solo necesita realizar los pasos 2 y 5-7.

  1. Cree (si no se creó) una sucursal local con los cambios que desea incorporar.

    $ git branch mybranch <base branch>

  2. Cambia a eso.

    $ git checkout mybranch

  3. Despliegue los cambios que desee de la cuenta de la otra persona. Si aún no lo ha hecho, querrá agregarlos como control remoto.

    $ git remote add repos-w-changes <git url>

  4. Tire hacia abajo todo de su rama.

    $ git pull repos-w-changes branch-i-want

  5. Vea los registros de confirmación para ver qué cambios desea:

    $ git log

  6. Vuelva a la rama en la que desea extraer los cambios.

    $ git checkout originalbranch

  7. Cherry elige tus commits, uno por uno, con los hash.

    $ git cherry-pick -x hash-of-commit

Punta de sombrero: http://www.sourcemage.org/Git_Guide

Cory
fuente
3
Consejo: primero use el git cherrycomando (consulte el manual primero) para identificar confirmaciones que aún no ha fusionado.
akaihola
Esto funciona ... 1. creó una nueva rama 2. creó algunos archivos / realizó algunos cambios 3. commit 4. verifique la rama maestra 5. Ejecute git cherry-pick -x hash-of-commit y resuelva conflictos de fusión si es bueno ir.
RamPrasadBismil
Tu enlace ya no funciona. ¿Puedes actualizarlo por favor?
creep3007
42

Aquí es cómo puede reemplazar el Myclass.javaarchivo en masterrama con Myclass.javaen feature1rama. Funcionará incluso si Myclass.javano existe master.

git checkout master
git checkout feature1 Myclass.java

Tenga en cuenta que esto sobrescribirá, no fusionará, e ignorará los cambios locales en la rama maestra.

maestr0
fuente
66
Esto no se fusionará. Simplemente sobrescribirá los cambios en el maestro con los cambios de la rama feature1.
Skunkwaffle
3
Perfectamente, estaba buscando este tipo de fusión donde theirssobrescribir ours=> +1 Cheers;)
olibre
1
Algunas veces, todo lo que quiere hacer es reemplazar todo el archivo, así que esto es lo que quería, pero debe asegurarse de que desea perder todos los cambios que realizó en este archivo.
MagicLAMP
1
Solución más limpia, dado que el OP específicamente quería reemplazar todo el archivo con el equivalente en otra rama:2. Manual copying of common files into a temp directory followed by ...copying out of the temp directory into the working tree.
Brent Faust
29

La manera simple, para fusionar archivos específicos de dos ramas, no solo reemplazar archivos específicos con los de otra rama.

Paso uno: difunde las ramas

git diff branch_b > my_patch_file.patch

Crea un archivo de parche de la diferencia entre la rama actual y branch_b

Paso dos: aplique el parche en archivos que coincidan con un patrón

git apply -p1 --include=pattern/matching/the/path/to/file/or/folder my_patch_file.patch

notas útiles sobre las opciones

Puede usarlo *como comodín en el patrón de inclusión.

Las barras no necesitan ser escapadas.

Además, puede usar --excluir en su lugar y aplicarlo a todo excepto a los archivos que coinciden con el patrón, o revertir el parche con -R

La opción -p1 es un remanente del comando de parche * unix y el hecho de que el contenido del archivo de parche antepone cada nombre de archivo con a/o b/(o más, dependiendo de cómo se generó el archivo de parche) que debe eliminar para que pueda descubrir el archivo real a la ruta al archivo al que se debe aplicar el parche.

Consulte la página del manual de git-apply para obtener más opciones.

Paso tres: no hay paso tres

Obviamente, querrá confirmar sus cambios, pero quién puede decir que no tiene otros ajustes relacionados que desea hacer antes de realizar su confirmación.

masukomi
fuente
1
Esto fue muy útil cuando current_branch tenía muchos cambios "adicionales" que debían preservarse. Obtuve la diferencia de solo los cambios introducidos por branch_b como: git diff HEAD ... branch_b (sí, tres períodos hacen el truco de magia).
Saad Malik
@masukomi, en el paso 2, ¿no debería agregar el archivo de parche creado en el paso 1 como argumento?
Spiralis
Para mí, todos los cambios son rechazados. ¿Alguna idea de por qué?
LinusGeffarth
pensamiento inicial @LinusGeffarth es que tal vez tienes las ramas hacia atrás al hacer el parche? realizará un seguimiento fuera de SO para ver si podemos resolverlo.
masukomi
24

Así es como puede obtener el historial para seguir solo un par de archivos de otra rama con un mínimo de alboroto, incluso si una fusión más "simple" hubiera traído muchos más cambios que no desea.

Primero, darás el paso inusual de declarar de antemano que lo que estás a punto de cometer es una fusión, sin que git haga nada en absoluto a los archivos en tu directorio de trabajo:

git merge --no-ff --no-commit -s ours branchname1

. . . donde "nombre de sucursal" es lo que usted dice que está fusionando. Si tuviera que comprometerse de inmediato, no haría cambios, pero aún mostraría ascendencia de la otra rama. Puede agregar más ramas / etiquetas / etc. a la línea de comando si es necesario, también. Sin embargo, en este punto, no hay cambios para confirmar, así que obtenga los archivos de las otras revisiones, a continuación.

git checkout branchname1 -- file1 file2 etc

Si se estaba fusionando desde más de otra rama, repita según sea necesario.

git checkout branchname2 -- file3 file4 etc

Ahora los archivos de la otra rama están en el índice, listos para ser confirmados, con historial.

git commit

y tendrás muchas explicaciones que hacer en ese mensaje de confirmación.

Sin embargo, tenga en cuenta, en caso de que no esté claro, que esto es algo desordenado que hacer. No está en el espíritu de lo que es una "rama", y la selección de cerezas es una forma más honesta de hacer lo que estaría haciendo aquí. Si desea hacer otra "fusión" para otros archivos en la misma rama que no trajo la última vez, se detendrá con un mensaje "ya actualizado". Es un síntoma de no ramificarse cuando deberíamos haberlo hecho, en la rama "desde" debería haber más de una rama diferente.

revs jejese
fuente
3
¡Su primer comando ( git merge --no-ff --no-commit -s outs branchname1) es exactamente lo que estaba buscando! ¡Gracias!
RobM
1
Con múltiples ramas, el historial requerido, la necesidad de fusionar uno o varios archivos y tener que cambiar el contenido del archivo antes de enviarlo, esto parece ser una alternativa decente. Por ejemplo, dev => master, pero desea cambiar la definición de host o similar antes de presionar para master.
Timss
15

Sé que llego un poco tarde, pero este es mi flujo de trabajo para fusionar archivos selectivos.

#make a new branch ( this will be temporary)
git checkout -b newbranch
# grab the changes 
git merge --no-commit  featurebranch
# unstage those changes
git reset HEAD
(you can now see the files from the merge are unstaged)
# now you can chose which files are to be merged.
git add -p
# remember to "git add" any new files you wish to keep
git commit
Felix
fuente
Usé una ligera variación en esto. En lugar de fusionarme, tomé la cereza. Hace el trabajo El único inconveniente de este enfoque es que pierde la referencia al hash de confirmación original.
Matt Florence
15

La forma más fácil es establecer su repositorio en la rama con la que desea fusionar y luego ejecutar,

git checkout [branch with file] [path to file you would like to merge]

Si tu corres

git status

verá el archivo ya organizado ...

Entonces corre

git commit -m "Merge changes on '[branch]' to [file]"

Simple.

dsieczko
fuente
3
Esta es casi la mejor respuesta que encontré. visite jasonrudolph.com/blog/2009/02/25/… ¡ Tan claro, conciso y simplemente funciona!
superuseroi
1
eso reemplazará por completo el contenido de los archivos de la rama de origen en lugar de fusionarse
Amare
Estaba a punto de responder así, ¡pensé que inventaría cosas nuevas que aún no han sido respondidas! Pero esta es la forma más sencilla de hacerlo. Esto debería estar en la cima!
Irfandy Jip
15

Encontré esta publicación para contener la respuesta más simple. Simplemente hacer:

$ #git checkout <branch from which you want files> <file paths>

Ejemplo:

$ #pulling .gitignore file from branchB into current branch
$ git checkout branchB .gitignore

Vea la publicación para más información.

Persona maravillosa
fuente
3
Esto realmente no se fusiona, sobrescribe el archivo en la rama actual.
Igor Ralic
1
@igrali Ese es un comentario útil, pero en comparación con la dificultad de las formas "adecuadas" de hacer esto, esta es una buena solución. Solo hay que tener mucho cuidado.
owensmartin
12

Es extraño que git todavía no tenga una herramienta tan conveniente "lista para usar". Lo uso mucho cuando actualizo alguna rama de la versión anterior (que todavía tiene muchos usuarios de software) con solo algunas correcciones de errores de la rama de la versión actual. En este caso, a menudo es necesario obtener rápidamente algunas líneas de código del archivo en el tronco, ignorando muchos otros cambios (que no se supone que entren en la versión anterior) ... Y, por supuesto , la combinación tripartita interactiva es necesario en este caso, git checkout --patch <branch> <file path>no es utilizable para este propósito de fusión selectiva.

Puedes hacerlo fácilmente:

Simplemente agregue esta línea a la [alias]sección en su archivo global .gitconfigo local .git/config:

[alias]
    mergetool-file = "!sh -c 'git show $1:$2 > $2.theirs; git show $(git merge-base $1 $(git rev-parse HEAD)):$2 > $2.base; /C/BCompare3/BCompare.exe $2.theirs $2 $2.base $2; rm -f $2.theirs; rm -f $2.base;' -"

Implica que usas Beyond Compare. Simplemente cambie al software de su elección si es necesario. O puede cambiarlo a fusión automática de tres vías si no necesita la fusión selectiva interactiva:

[alias]
    mergetool-file = "!sh -c 'git show $1:$2 > $2.theirs; git show $(git merge-base $1 $(git rev-parse HEAD)):$2 > $2.base; git merge-file $2 $2.base $2.theirs; rm -f $2.theirs; rm -f $2.base;' -"

Luego use así:

git mergetool-file <source branch> <file path>

Esto le dará la verdadera oportunidad de fusión selectiva en forma de árbol de cualquier archivo en otra rama.

JimStar
fuente
10

No es exactamente lo que estabas buscando, pero me fue útil:

git checkout -p <branch> -- <paths> ...

Es una mezcla de algunas respuestas.

Felipe
fuente
2
Esto es realmente útil y podría agregarse a lo que para mí es la mejor respuesta, la respuesta de @alvinabad. Al hacerlo: git checkout HEAD file1para mantener la versión actual y separar el archivo file1, se puede usar la -popción para seleccionar parte del archivo a fusionar. gracias por el truco!
Simon C.
Esta es mi respuesta favorita. Simple, al grano y funciona
Jesse Reza Khorasanee
8

Yo haría un

git diff commit1..commit2 filepattern | git-apply --index && git commit

De esta manera, puede limitar el rango de confirmaciones para un patrón de archivos desde una rama.

Robado de: http://www.gelato.unsw.edu.au/archives/git/0701/37964.html

lumpidu
fuente
En algunas situaciones, esto podría ser muy útil. Sin embargo, si los cambios están en una rama diferente, solo puede pagar desde la punta de esa rama, como en la respuesta de Bart J anterior.
cdunn2001
Quien es Bart Js?
Negro
8

Tuve exactamente el mismo problema mencionado anteriormente. Pero encontré este blog git más claro al explicar la respuesta.

Comando desde el enlace de arriba:

#You are in the branch you want to merge to
git checkout <branch_you_want_to_merge_from> <file_paths...>
Susheel Javadi
fuente
probaste esto? Estoy seguro de que los archivos se reemplazarán de <branch_you_want_to_merge_from> en lugar de fusionarse
Amare
7

Me gusta la respuesta 'git-interactive-merge', arriba, pero hay una más fácil. Deje que git haga esto por usted usando una combinación de rebase de interactivo y en:

      A---C1---o---C2---o---o feature
     /
----o---o---o---o master

Entonces, el caso es que desea C1 y C2 de la rama 'característica' (punto de rama 'A'), pero ninguno de los demás por ahora.

# git branch temp feature
# git checkout master
# git rebase -i --onto HEAD A temp

Lo cual, como arriba, te lleva al editor interactivo donde seleccionas las líneas 'pick' para C1 y C2 (como arriba). Guarde y salga, y luego procederá con el rebase y le dará la rama 'temp' y también HEAD en master + C1 + C2:

      A---C1---o---C2---o---o feature
     /
----o---o---o---o-master--C1---C2 [HEAD, temp]

Luego puede actualizar master a HEAD y eliminar la rama temporal y listo:

# git branch -f master HEAD
# git branch -d temp
Vadear
fuente
7

¿Qué hay de git reset --soft branch? Me sorprende que nadie lo haya mencionado todavía.

Para mí, es la forma más fácil de seleccionar selectivamente los cambios de otra rama, ya que este comando pone en mi árbol de trabajo todos los cambios de diferencias y puedo elegir o revertir fácilmente cuál necesito. De esta manera, tengo control total sobre los archivos comprometidos.

GarryOne
fuente
6

Sé que esta pregunta es antigua y hay muchas otras respuestas, pero escribí mi propio script llamado 'pmerge' para fusionar parcialmente los directorios. Es un trabajo en progreso y todavía estoy aprendiendo las secuencias de comandos git y bash.

Este comando usa git merge --no-commity luego no aplica los cambios que no coinciden con la ruta provista.

Uso: git pmerge branch path
Ejemplo:git merge develop src/

No lo he probado exhaustivamente. El directorio de trabajo debe estar libre de cambios no confirmados y archivos sin seguimiento.

#!/bin/bash

E_BADARGS=65

if [ $# -ne 2 ]
then
    echo "Usage: `basename $0` branch path"
    exit $E_BADARGS
fi

git merge $1 --no-commit
IFS=$'\n'
# list of changes due to merge | replace nulls w newlines | strip lines to just filenames | ensure lines are unique
for f in $(git status --porcelain -z -uno | tr '\000' '\n' | sed -e 's/^[[:graph:]][[:space:]]\{1,\}//' | uniq); do
    [[ $f == $2* ]] && continue
    if git reset $f >/dev/null 2>&1; then
        # reset failed... file was previously unversioned
        echo Deleting $f
        rm $f
    else
        echo Reverting $f
        git checkout -- $f >/dev/null 2>&1
    fi
done
unset IFS
Andy
fuente
4

Puede usar read-treepara leer o fusionar un árbol remoto dado en el índice actual, por ejemplo:

git remote add foo [email protected]/foo.git
git fetch foo
git read-tree --prefix=my-folder/ -u foo/master:trunk/their-folder

Para realizar la fusión, use -men su lugar.

Ver también: ¿Cómo fusiono un subdirectorio en git?

kenorb
fuente
3

Un enfoque simple para la fusión / confirmación selectiva por archivo:

git checkout dstBranch git merge srcBranch // make changes, including resolving conflicts to single files git add singleFile1 singleFile2 git commit -m "message specific to a few files" git reset --hard # blow away uncommitted changes

Dave C
fuente
3

Si no tiene demasiados archivos que han cambiado, esto lo dejará sin compromisos adicionales.

1. Duplicar la rama temporalmente
$ git checkout -b temp_branch

2. Restablezca la última confirmación deseada
$ git reset --hard HEAD~n , ¿dónde nestá la cantidad de confirmaciones que necesita para volver?

3. Verifique cada archivo de la rama original
$ git checkout origin/original_branch filename.ext

Ahora puede comprometer y forzar el empuje (para sobrescribir el control remoto), si es necesario.

JBaczuk
fuente
3

Si solo necesita fusionar un directorio en particular y dejar todo lo demás intacto y al mismo tiempo preservar el historial, posiblemente podría intentar esto ... crear un nuevo target-branchfuera del masterantes de experimentar.

Los pasos a continuación suponen que tiene dos ramas target-branchy que source-branchel directorio dir-to-mergeque desea fusionar está en source-branch. También suponga que tiene otros directorios como dir-to-retainen el destino que no desea cambiar y retener el historial. Además, se supone que hay conflictos de fusión en el dir-to-merge.

git checkout target-branch
git merge --no-ff --no-commit -X theirs source-branch
# the option "-X theirs", will pick theirs when there is a conflict. 
# the options "--no--ff --no-commit" prevent a commit after a merge, and give you an opportunity to fix other directories you want to retain, before you commit this merge.

# the above, would have messed up the other directories that you want to retain.
# so you need to reset them for every directory that you want to retain.
git reset HEAD dir-to-retain
# verify everything and commit.
code4kix
fuente
2

Cuando solo unos pocos archivos han cambiado entre las confirmaciones actuales de las dos ramas, combino manualmente los cambios revisando los diferentes archivos.

git difftoll <branch-1>..<branch-2>

raratiru
fuente