tutorial remota ramas rama partir otra fusionar español crear comandos git git-merge git-cherry-pick git-patch

remota - ¿Cómo fusionar o seleccionar selectivamente los cambios de otra rama en Git?



git pull (24)

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 algunos mods de los que estoy generalmente seguro
  • exp1 : rama experimental # 1
  • exp2 : rama experimental # 2

exp1 y exp2 representan dos enfoques arquitectónicos muy diferentes. Hasta que no avance, no tengo forma de saber cuál funcionará (si es que funciona). A medida que avanzo en una rama, a veces tengo ediciones que serían útiles en la otra rama y me gustaría fusionarlas.

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

Enfoques que he considerado:

  1. git merge --no-commit seguido de una puesta a punto manual de una gran cantidad de ediciones que no quiero hacer en común entre las sucursales.

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

  3. Una variación de lo anterior. Abandone las ramas exp 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 haría que git-merge más selectivo.


Aquí es cómo puede reemplazar el archivo Myclass.java en la rama master con Myclass.java en la rama Myclass.java . Funcionará incluso si Myclass.java no existe en el 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.


Aquí le explicamos cómo puede hacer que el historial siga solo un par de archivos de otra rama con un mínimo de esfuerzo, incluso si una combinación más "simple" hubiera generado muchos más cambios que no desea.

Primero, tomará el paso inusual de declarar por adelantado que lo que está a punto de confirmar es una combinación, sin que Git haga nada en absoluto a los archivos en su directorio de trabajo:

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

. . . donde "nombre de sucursal" es lo que dice que se está fusionando. Si se comprometiera de inmediato, no se realizarían cambios, pero seguiría mostrando la ascendencia de la otra rama. Puede agregar más ramas / etiquetas / etc. a la línea de comandos 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 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 mucho que explicar en ese mensaje de confirmación.

Sin embargo, tenga en cuenta, en caso de que no quede claro, que esto no está bien hecho. No está en el espíritu de lo que es una "rama", y la selección de cerezas es una manera más honesta de hacer lo que estarías haciendo aquí. Si desea hacer otra "combinació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 ramificar cuando deberíamos, en la rama "desde" debería haber más de una rama diferente.


Cuando solo unos pocos archivos han cambiado entre las confirmaciones actuales de las dos ramas, fusiono manualmente los cambios yendo a través de los diferentes archivos.

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


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

Puedes hacerlo fácilmente:

Simplemente agregue esta línea a la sección [alias] en su .gitconfig global .gitconfig o 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 uses 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;'' -"

Entonces use así:

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

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


Hay otra manera de ir:

git checkout -p

Es una mezcla entre git checkout y git add -p y podría ser exactamente lo que está 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.


He encontrado jasonrudolph.com/blog/2009/02/25/… 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.


La forma más sencilla es establecer su repositorio en la rama 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ás el archivo ya montado ...

Entonces corre

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

Sencillo.


La forma sencilla, de combinar archivos específicos de dos ramas, no solo reemplazar archivos específicos con archivos de otra rama.

Paso uno: Difundir las ramas.

git diff branch_b > my_patch_file.patch

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

Paso dos: aplique 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.

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

Las barras diagonales no necesitan ser escapadas.

Además, podría usar --exclude 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 remanente del comando de parche * unix y el hecho de que el contenido del archivo de parche preceda a 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 puede averiguar el archivo real de la ruta al archivo al que se debe aplicar el parche.

Echa un vistazo a la página de manual de git-apply para ver más opciones.

Paso tres: no hay paso tres

Obviamente, querría confirmar sus cambios, pero ¿quién puede decir que no tiene otros ajustes relacionados que desea hacer antes de comprometerse?


La respuesta de 1800 INFORMATION es completamente correcta. Sin embargo, como un git noob, "usar git cherry-pick" no fue suficiente para que resolviera esto sin un poco más de excavación en Internet, así que pensé en publicar una guía más detallada en caso de que alguien más esté en una barco similar.

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

  1. Cree (si no se crea) una rama local con los cambios que desea introducir.

    $ 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. Tirar 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. Vuelve a la rama en la que quieres tirar los cambios.

    $ git checkout originalbranch

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

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

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


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 ramificación ''A''), pero nada del resto por ahora.

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

Lo que, como se indicó anteriormente, lo envía al editor interactivo donde selecciona las líneas de ''selección'' para C1 y C2 (como se muestra arriba). Guarde y salga, y luego procederá con la rebase y le dará la rama ''temp'' y también HEAD at master + C1 + C2:

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

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

# git branch -f master HEAD # git branch -d temp


No es exactamente lo que buscabas, pero fue útil para mí:

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

Es una mezcla de algunas respuestas.


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

No hay --interactive argumento --interactive que puedas pasar a git merge.

Aquí está la alternativa:

Tiene algunos cambios en la ''característica'' de la rama y desea llevar algunos, pero no todos, al ''maestro'' de una manera no descuidada (es decir, no quiere elegir y cometer cada uno de ellos)

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 envuélvalo en un script de shell, cambie el maestro 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


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

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

donde branchX es la rama que desea combinar en la rama actual.

La --no-commit mostrará los archivos que Git ha fusionado sin haberlos --no-commit . Esto le dará la oportunidad de modificar los archivos combinados como desee y luego cometerlos 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 combinar.

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 archivo1 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 del branchX file1 en branchX y sobrescribirá el branchX file1 fusionado automáticamente por Git.

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

En este caso, puede editar el file1 modificado1 directamente, actualizarlo a lo que quiera que se convierta en la versión del file1 y luego confirmar.

Si Git no puede fusionar un archivo automáticamente, lo reportará como "no fusionado " y producirá una copia donde deberá resolver los conflictos manualmente.


Para explicar con más branchX un ejemplo, supongamos que desea fusionar branchX en la rama actual:

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

A continuación, ejecuta el comando git status 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 , file2 y file3 son los archivos que git se ha fusionado correctamente.

Lo que esto significa es que los cambios en el master y branchX para 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. edita el archivo directamente
  2. salvar
  3. git commit

Si no desea combinar file1 y desea conservar la versión en la rama actual

correr

git checkout HEAD file1

Si no desea combinar file2 y solo quiere la versión en branchX

correr

git checkout branchX file2

Si desea que file3 se fusione automáticamente, no haga nada.

Git ya lo ha fusionado en este punto.


file4 anterior 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 combinación realizada editando el archivo directamente o ejecutando el comando de pago para la versión en la rama en la que desea que se convierta el file4 .


Por último, no te olvides de git commit .


Puede usar read-tree de lectura para leer o combinar 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, utilice -m lugar.

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


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

Este comando usa git merge --no-commit y luego no aplica los cambios que no coinciden con la ruta proporcionada.

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

No lo he probado extensivamente. 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


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


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

Digamos que tienes las ramas master , exp1 y exp2 . Desea combinar 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 desea. Nada mas. Nada menos. Es útil tener cambios de archivo radicalmente diferentes entre las 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 todavía lo sea para diferencias extremas. Las pequeñas cosas molestas como el espacio en blanco se vuelven a combinar a menos que use el -s recursive -X ignore-all-space opción de -s recursive -X ignore-all-space )


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

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

2. Restablecer al último compromiso deseado
$ git reset --hard HEAD~n , donde n es el número de confirmaciones que necesita para volver

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

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


Si solo necesita fusionar un directorio en particular y dejar todo lo demás intacto y aún así conservar el historial, puede intentar esto ... cree una nueva target-branch fuera del master antes de experimentar.

Los pasos a continuación suponen que tiene dos sucursales target-branch y source-branch , y el directorio dir-to-merge que desea fusionar se encuentra en el source-branch . También asuma que tiene otros directorios como dir-to-retain en el destino que no desea cambiar y retener el historial. Además, asume que hay conflictos de combinació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.


Tuve exactamente el mismo problema que mencionaste anteriormente. Pero encontré jasonrudolph.com/blog/2009/02/25/… 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...>


Tuve exactamente el mismo problema que mencionaste anteriormente. Pero encontré jasonrudolph.com/blog/2009/02/25/… más claro al explicar la respuesta.

Resumen:

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

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

  • o fusionar selectivamente los tíos

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

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

    $ git reset <paths>... $ git add -p <paths>...

  • Finalmente cometer

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


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


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 . En términos generales, usa git rebase -i para obtener la confirmación original para editar, luego git reset HEAD^ para revertir los cambios de forma selectiva, y luego git commit para confirmar ese bit como una nueva confirmación en el historial.

Hay otro método agradable 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 (búsqueda en esa página para "dividir").

Una vez divididos los cambios, ahora puede elegir los que desee.