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


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 n. ° 1
  • exp2: rama experimental n. ° 2

exp1 y exp2 representan dos enfoques arquitectónicos muy diferentes. Hasta que llegue más lejos, no tengo forma de saber cuál funcionará (si es que lo hace). 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 los cambios selectivos de una rama de desarrollo a otra mientras se deja atrás todo lo demás?

Enfoques que he considerado:

  1. git merge --no-commit seguido de un desempate 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 por git checkout para pasar a la otra rama y luego más copia manual fuera del directorio temporal en el árbol de trabajo.

  3. Una variación de lo anterior. Abandona el exp ramas por ahora y utilizar 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 similar a un parámetro de ruta de filtro que haría git-merge más selectivo


1158
2018-01-16 04:55


origen


Respuestas:


Usas el cherry-pick comando para obtener compromisos 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 el compromiso en compromisos individuales. En general, usas git rebase -i para obtener el compromiso original de editar, luego git reset HEAD^ para revertir cambios selectivamente, luego git commit para comprometer ese bit como un nuevo compromiso en la historia.

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

Al haber dividido los cambios, ahora puede seleccionar exactamente los que desee.


390
2018-01-16 06:01



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

Resumen:

  • Verifique la (s) ruta (s) de la rama que desea fusionar,

    $ git checkout source_branch -- <paths>...
    
  • o para fusionar selectivamente hunks

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

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

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

    $ git commit -m "'Merge' these changes"
    

816
2017-09-03 08:48



Para combinar selectivamente archivos de una rama en otra rama, ejecuta

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

dónde branchX es la rama desde la que desea fusionar a la rama actual.

los --no-commit La opción pondrá en escena los archivos que Git ha fusionado sin comprometerlos. Esto le dará la oportunidad de modificar los archivos combinados de la forma que desee y luego confirmarlos usted mismo.

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

1) Quieres una fusión verdadera.

En este caso, acepta los archivos fusionados 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 file1 en la rama actual y sobrescribir el file1 automatizado por Git.

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

Correr:

git checkout branchX file1

Esto recuperará la versión de file1 en branchX y sobrescribir file1 auto fusionado por Git.

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

En este caso, puede editar la modificación file1 directamente, actualízalo a lo que quieras con la versión de file1 convertirse en, y luego comprometerse.

Si Git no puede fusionar un archivo automáticamente, informará el archivo como "sin fusionar"y produzca una copia donde tendrá que resolver los conflictos manualmente.



Para explicar con más detalle con un ejemplo, digamos que desea fusionarse branchX en la rama actual:

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

Luego ejecuta el git status comando 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
#

Dónde file1, file2y file3 son los archivos git se han fusionado automáticamente.

Lo que esto significa es que los cambios en el master y branchX para todos esos tres archivos se han combinado juntos sin ningún conflicto.

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

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

Si encuentra alguna combinación indeseable, entonces puede

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

Si no quieres fusionarte file1 y quiere retener la versión en la rama actual

correr

git checkout HEAD file1

Si no quieres fusionarte file2 y solo quiere la versión en branchX

correr

git checkout branchX file2

Si tu quieres file3 para fusionarse automáticamente, no hagas nada.

Git ya se ha fusionado en este punto.


file4 arriba 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 tendrá que resolver los conflictos manualmente. Puede descartar la fusión realizada editando el archivo directamente o ejecutando el comando de finalización de la versión en la rama que desea file4 convertirse.


Por último, no te olvides de git commit.


239
2018-05-21 03:00



No me gustan los enfoques anteriores. Usar cherry-pick es ideal para elegir un solo cambio, pero es doloroso si quieres incluir todos los cambios, excepto algunos malos. Aquí está mi enfoque.

No hay --interactive argumento que puedes pasar a Git Merge.

Aquí está la alternativa:

Usted tiene algunos cambios en la 'función' de la sucursal y desea traer algunos, pero no todos, a 'master' de una manera no descuidada (es decir, no desea seleccionar y confirmar 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 el master en $ to y cambie la función en $ 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

85
2017-08-28 18:47



Hay otra forma de hacerlo:

git checkout -p

Es una mezcla entre git checkout y git add -p y puede 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.

73
2017-08-25 01:31



Si bien algunas de estas respuestas son bastante buenas, creo que ninguna 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 tienes el master, exp1y exp2 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á diffs dentro del archivo para cada uno de los archivos que desee. Nada mas. Nada menos. Es útil que tenga cambios de archivos radicalmente diferentes entre versiones, en mi caso, cambiar una aplicación de Rails 2 a Rails 3.

EDITAR: esto fusionará archivos, pero realiza una fusión inteligente. No pude encontrar la manera de utilizar este método para obtener información de diff en el archivo (tal vez lo haga en caso de diferencias extremas. Las cosas pequeñas y molestas como el espacio en blanco se vuelven a fusionar a menos que use el -s recursive -X ignore-all-space opción)


48
2018-05-07 11:38



1800 La respuesta de INFORMATION es completamente correcta. Como un git noob, sin embargo, "use git cherry-pick" no fue suficiente para que yo pudiera resolver esto sin tener que cavar 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 una bote similar.

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

  1. Crea (si no creaste) una sucursal local con los cambios que deseas traer.

    $ git branch mybranch <base branch>

  2. Cambiar a eso.

    $ git checkout mybranch

  3. Tire los cambios que desea 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. Tira todo de su rama.

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

  5. Vea los registros de commit para ver qué cambios quiere:

    $ git log 

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

    $ git checkout originalbranch

  7. Cherry elige tus compromisos, uno por uno, con los hashes.

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

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


42
2017-07-29 08:37



Aquí es cómo puedes reemplazar Myclass.java presentar en master rama con Myclass.java en feature1 rama. Funcionará incluso si Myclass.java no existe en master.

git checkout master
git checkout feature1 Myclass.java

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


37
2018-02-27 22:42



La manera simple, de hecho unir archivos específicos de dos ramas, no solo reemplazar archivos específicos por archivos de otra rama.

Paso uno: difiere 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: aplicar el parche en los 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

Puedes usar * como un comodín en el patrón de inclusión.

Las barras no necesitan ser escapadas.

Además, podría usar --excluir en su lugar y aplicarlo a todo excepto a los archivos que coincidan con el patrón, o invertir el parche con -R

La opción -p1 es un holdover del comando de parche * unix y el hecho de que los contenidos del archivo de parche preceden a cada nombre de archivo con a/o b/ (o más dependiendo de cómo se generó el archivo de parche) que necesita quitar para que pueda encontrar el archivo real en la ruta al archivo al que se debe aplicar el parche.

Consulte la página man para git-apply para más opciones.

Paso tres: no hay paso tres

Obviamente, querrás confirmar tus cambios, pero ¿quién puede decir que no tienes otros ajustes relacionados que quieras hacer antes de realizar tu compromiso?


22
2018-02-18 04:28



Así es como puedes obtener la historia 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 deseas.

Primero, tomará el paso inusual de declarar de antemano que lo que está a punto de comprometer es una fusión, sin que nada haga nada con los archivos en su directorio de trabajo:

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

. . . donde "nombre de sucursal" es lo que usted dice que se está fusionando. Si tuviera que comprometerse de inmediato, no haría ningún cambio, pero seguiría mostrando ascendencia de la otra rama. Puede agregar más ramas / etiquetas / etc. a la línea de comando si es necesario, también. En este punto, sin embargo, 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 de más de una otra sucursal, 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 comprometerse, con historial.

git commit

y tendrás muchas explicaciones que hacer en ese mensaje de compromiso.

Sin embargo, tenga en cuenta que, en caso de que no esté claro, esto es una tarea desordenada. No está en el espíritu de lo que una "rama" es para, y la selección de cereza es una manera más honesta de hacer lo que estarías 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 ramificación cuando deberíamos tener, en la rama "de" debe haber más de una rama diferente.


20
2017-11-04 10:51



Sé que estoy 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

14
2017-10-29 20:54