Comment fusionner ou sélectionner sélectivement les modifications d'une autre branche dans Git?

1450

J'utilise git sur un nouveau projet qui a deux branches de développement parallèles - mais actuellement expérimentales -:

  • master: import de base de code existante plus quelques mods dont je suis généralement sûr
  • exp1: branche expérimentale # 1
  • exp2: branche expérimentale # 2

exp1et exp2représentent deux approches architecturales très différentes. Jusqu'à ce que je progresse, je n'ai aucun moyen de savoir lequel (le cas échéant) fonctionnera. Au fur et à mesure que je progresse dans une branche, j'ai parfois des modifications qui seraient utiles dans l'autre branche et que je voudrais fusionner uniquement.

Quelle est la meilleure façon de fusionner des changements sélectifs d'une branche de développement à une autre tout en laissant de côté tout le reste?

Approches que j'ai envisagées:

  1. git merge --no-commit suivi d'une désorganisation manuelle d'un grand nombre de modifications que je ne veux pas rendre communes entre les branches.

  2. Copie manuelle des fichiers communs dans un répertoire temporaire, puis git checkoutpour passer à l'autre branche, puis plus de copie manuelle du répertoire temporaire dans l'arborescence de travail.

  3. Une variation de ce qui précède. Abandonnez les expbranches pour l'instant et utilisez deux référentiels locaux supplémentaires pour l'expérimentation. Cela rend la copie manuelle des fichiers beaucoup plus simple.

Ces trois approches semblent fastidieuses et sujettes aux erreurs. J'espère qu'il y a une meilleure approche; quelque chose qui ressemble à un paramètre de chemin de filtre qui le rendrait git-mergeplus sélectif.

David Joyner
la source
5
Si les changements dans vos branches expérimentales sont bien organisés en commits séparés, il vaut mieux penser en termes de fusion de commits sélectifs au lieu de fichiers sélectifs. La plupart des réponses ci-dessous supposent que c'est le cas.
akaihola
2
Une combinaison de git merge -s ours --no-commitsuivis par certains ne git read-treeserait-elle pas une bonne solution pour cela? Voir stackoverflow.com/questions/1214906/…
VonC
34
Une question plus récente a une réponse bien écrite d'une seule ligne: stackoverflow.com/questions/10784523/…
brahn
Consultez ce blog pour fusionner des fichiers spécifiques uniquement jasonrudolph.com/blog/2009/02/25/…
Ashutosh Chamoli

Réponses:

475

Vous utilisez la commande cherry-pick pour obtenir des validations individuelles d'une branche.

Si les modifications souhaitées ne se trouvent pas dans des validations individuelles , utilisez la méthode indiquée ici pour diviser la validation en validations individuelles . En gros, vous utilisez git rebase -ipour obtenir le commit d'origine à modifier, puis git reset HEAD^pour annuler sélectivement les modifications, puis git commitpour valider ce bit en tant que nouveau commit dans l'historique.

Il existe une autre méthode intéressante ici dans Red Hat Magazine, où ils utilisent git add --patchou éventuellement git add --interactivequi vous permet d'ajouter seulement des parties d'un morceau, si vous souhaitez fractionner différentes modifications dans un fichier individuel (recherchez dans cette page «fractionner»).

Après avoir divisé les modifications, vous pouvez désormais choisir celles que vous souhaitez.

1800 INFORMATION
la source
14
D'après ce que je comprends, c'est inutilement plus compliqué que la réponse la plus votée.
Alexander Bird
54
C'est techniquement la bonne réponse, la bonne réponse apparaît "alambiquée". --- La réponse la plus votée n'est qu'une réponse rapide et sale "fait l'affaire", qui pour la plupart des gens est tout ce dont ils parlent (:
Jacob
3
@akaihola: HEAD ^ est correct. Voir man git-rev-parse: Un suffixe ^ à un paramètre de révision signifie le premier parent de cet objet commit. La notation préfixe ^ est utilisée pour exclure les validations accessibles depuis une validation.
Tyler Rick
13
Je voulais juste partager une autre approche qui semble la plus propre et la moins compliquée de toutes: jasonrudolph.com/blog/2009/02/25/… simplicité totale et génial
superuseroi
14
Confus par le débat sur quelle approche est «correcte»? Tenez compte de la différence entre les fichiers et les validations (voir les remarques en bas) . OP souhaite fusionner les FICHIERS et ne mentionne pas les COMMITS. La réponse la plus votée est spécifique aux fichiers; la réponse acceptée utilise le choix de la cerise, qui est spécifique aux validations. Cherry-pick peut être la clé pour fusionner les commits de manière sélective, mais cela peut être très pénible pour déplacer des fichiers d'une branche à une autre. Bien que les commits soient au cœur de la force de git, n'oubliez pas que les fichiers ont toujours un rôle!
Kay V
970

J'ai eu exactement le même problème que celui mentionné ci-dessus. Mais j'ai trouvé cela plus clair en expliquant la réponse.

Sommaire:

  • Extraire le (s) chemin (s) de la branche que vous souhaitez fusionner,

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

    Astuce: Cela fonctionne également sans --comme vu dans le post lié.

  • ou pour fusionner sélectivement des mecs

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

    Vous pouvez également utiliser la réinitialisation, puis ajouter avec l'option -p,

    $ git reset <paths>...
    $ git add -p <paths>...
    
  • Finalement, engagez

    $ git commit -m "'Merge' these changes"
    
Chris Steinbach
la source
9
L'article lié de Bart J est la meilleure approche. Clair, simple, une commande. C'est celui que je vais utiliser. :)
Pistos
256
Ce n'est pas une vraie fusion. Vous choisissez les modifications par fichier plutôt que par validation, et vous perdrez toutes les informations de validation existantes (auteur, message). Certes, c'est bien si vous souhaitez fusionner toutes les modifications dans certains fichiers et que vous devez refaire toutes les validations. Mais si les fichiers contiennent à la fois des modifications à fusionner et d'autres à supprimer, l'une des méthodes fournies dans d'autres réponses vous servira mieux.
akaihola
10
@mykhal et d' autres: ce met en scène les fichiers dans l'index automatiquement, donc si vous vérifié foo.cfaire git reset HEAD foo.cpour désindexer ce fichier et vous pouvez alors diff il. J'ai découvert cela après l'avoir essayé et je suis revenu ici pour chercher une réponse à cela
michiakig
12
pour voir les changements que vous pourriez également utiliser:git diff --cached
OderWat
9
Selon cette réponse, ce git checkout -p <revision> -- <path>sera la même chose que d'émettre les trois premières commandes que vous avez décrites :)
7hi4g0
338

Pour fusionner de manière sélective des fichiers d'une branche dans une autre branche, exécutez

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

branchXest la branche à partir de laquelle vous souhaitez fusionner dans la branche actuelle.

L' --no-commitoption mettra en scène les fichiers qui ont été fusionnés par Git sans réellement les valider. Cela vous donnera la possibilité de modifier les fichiers fusionnés comme vous le souhaitez, puis de les valider vous-même.

Selon la façon dont vous souhaitez fusionner les fichiers, il existe quatre cas:

1) Vous voulez une vraie fusion.

Dans ce cas, vous acceptez les fichiers fusionnés comme Git les a fusionnés automatiquement, puis vous les validez.

2) Il y a certains fichiers que vous ne souhaitez pas fusionner.

Par exemple, vous souhaitez conserver la version dans la branche actuelle et ignorer la version dans la branche à partir de laquelle vous fusionnez.

Pour sélectionner la version dans la branche actuelle, exécutez:

git checkout HEAD file1

Cela va récupérer la version de file1dans la branche actuelle et écraser l' file1automerged par Git.

3) Si vous voulez la version dans branchX (et non une vraie fusion).

Courir:

git checkout branchX file1

Cela récupérera la version de file1in branchXet écrasera file1la fusion automatique par Git.

4) Le dernier cas est si vous souhaitez sélectionner uniquement des fusions spécifiques file1.

Dans ce cas, vous pouvez éditer file1directement la modification , la mettre à jour avec ce que vous souhaitez que la version file1devienne, puis valider.

Si Git ne peut pas fusionner un fichier automatiquement, il signalera le fichier comme "non fusionné " et produira une copie où vous devrez résoudre les conflits manuellement.



Pour expliquer plus en détail avec un exemple, disons que vous souhaitez fusionner branchXdans la branche actuelle:

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

Vous exécutez ensuite la git statuscommande pour afficher l'état des fichiers modifiés.

Par exemple:

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
#

file1, file2et file3sont les fichiers que git a réussi à fusionner automatiquement.

Cela signifie que les modifications apportées à masteret branchXpour ces trois fichiers ont été combinées sans aucun conflit.

Vous pouvez vérifier comment la fusion a été effectuée en exécutant le git diff --cached;

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

Si vous trouvez une fusion indésirable, vous pouvez

  1. éditez le fichier directement
  2. enregistrer
  3. git commit

Si vous ne souhaitez pas fusionner file1et souhaitez conserver la version dans la branche actuelle

Courir

git checkout HEAD file1

Si vous ne souhaitez pas fusionner file2et souhaitez uniquement la versionbranchX

Courir

git checkout branchX file2

Si vous souhaitez file3être fusionné automatiquement, ne faites rien.

Git l'a déjà fusionné à ce stade.


file4ci-dessus est une fusion ratée de Git. Cela signifie qu'il y a des changements dans les deux branches qui se produisent sur la même ligne. C'est là que vous devrez résoudre les conflits manuellement. Vous pouvez ignorer la fusion effectuée en modifiant directement le fichier ou en exécutant la commande d'extraction pour la version dans la branche que vous souhaitez file4devenir.


Enfin, n'oubliez pas git commit.

alvinabad
la source
10
Attention cependant: si le git merge --no-commit branchXn'est qu'une avance rapide, le pointeur sera mis à jour, et le --no-commit est donc ignoré en silence
cfi
16
@cfi Que diriez-vous d'ajouter --no-ffpour empêcher ce comportement?
Eduardo Costa
5
Je suggère définitivement de mettre à jour cette réponse avec l'option "--no-ff" d'Eduardo. J'ai lu le tout (ce qui était par ailleurs génial) uniquement pour que ma fusion soit accélérée.
Funktr0n
7
Cette solution donne les meilleurs résultats et flexibilité.
Thiago Macedo
20
Contrairement à la réponse avec le plus de votes, cette solution a préservé mon historique de fusion, ce qui est important pour moi car je fais des allers-retours partiels entre les branches. Je n'ai pas essayé toutes les autres solutions suggérées, alors peut-être que certaines d'entre elles le font également.
ws_e_c421
107

Je n'aime pas les approches ci-dessus. L'utilisation de la cerise sur le gâteau est idéale pour choisir un seul changement, mais c'est pénible si vous souhaitez apporter tous les changements, à l'exception de certains mauvais. Voici mon approche.

Il n'y a aucun --interactiveargument que vous pouvez passer pour git merge.

Voici l'alternative:

Vous avez des changements dans la «fonctionnalité» de la branche et vous voulez en apporter une partie, mais pas tous, au «maître» d'une manière non bâclée (c'est-à-dire que vous ne voulez pas choisir et valider chacun d'eux)

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

Enveloppez simplement cela dans un script shell, changez master en $ to et changez la fonctionnalité en $ from et vous êtes prêt à partir:

#!/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
la source
J'ai corrigé le formatage - c'est une méthode assez sympa, si vous voulez faire une sélection de commits
1800 INFORMATION
J'utilise cette technique maintenant et elle semble avoir très bien fonctionné.
dylanfm
4
Vous voudrez peut-être passer git rebase -i $toà git rebase -i $to || $SHELL, afin que l'utilisateur puisse appeler git --skipetc, si nécessaire si le rebase échoue. Il convient également de chaîner les lignes avec &&au lieu de nouvelles lignes.
sircolinton le
2
Malheureusement, il semble que le lien dans la réponse soit mort.
ThomasW
Non seulement le lien est mort, mais il a un avertissement de mauvaise réputation WOT. Je l'ai donc retiré.
Jean-François Corbett
93

Il y a une autre voie à suivre:

git checkout -p

C'est un mélange entre git checkoutet git add -pet pourrait être exactement ce que vous recherchez:

   -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.
Chronial
la source
10
C'est de loin la méthode la plus simple et la plus simple, tant que vous n'avez qu'un nombre gérable de modifications à intégrer. J'espère que plus de gens remarqueront cette réponse et la voteront positivement. Exemple: git checkout --patch exp1 file_to_merge
Tyler Rick
1
Réponse similaire publiée sur cette question: stackoverflow.com/a/11593308/47185
Tyler Rick
Oh, je ne savais pas que la caisse avait un patch! J'ai à la place checkout / reset / add -p.
Daniel C. Sobral
2
Vraiment la méthode la plus simple. git checkout -p featurebranch nom de fichier. Et la meilleure chose est que lorsque la commande est exécutée, elle vous donne ay / n / e /? / ... etc. option pour décider comment fusionner le fichier. J'ai essayé avec e et j'ai même pu éditer le patch avant de l'appliquer ... Comme c'est cool. Une véritable doublure pour fusionner des fichiers sélectifs d'autres branches.
Infoclogged
55

Bien que certaines de ces réponses soient plutôt bonnes, j'ai l'impression qu'aucune n'a réellement répondu à la contrainte d'origine d'OP: sélectionner des fichiers particuliers dans des branches particulières. Cette solution fait cela, mais peut être fastidieuse s'il y a beaucoup de fichiers.

Disons que vous avez les master, exp1et les exp2branches. Vous souhaitez fusionner un fichier de chacune des branches expérimentales dans master. Je ferais quelque chose comme ça:

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

Cela vous donnera des différences dans le fichier pour chacun des fichiers que vous souhaitez. Rien de plus. Rien de moins. Il est utile que vous ayez des changements de fichier radicalement différents entre les versions - dans mon cas, le changement d'une application de Rails 2 à Rails 3.

EDIT : cela fusionnera les fichiers, mais fera une fusion intelligente. Je n'ai pas été en mesure de comprendre comment utiliser cette méthode pour obtenir des informations de diff dans le fichier (peut-être que ce sera toujours le cas pour des différences extrêmes. De petites choses ennuyeuses comme les espaces blancs sont fusionnées à moins que vous n'utilisiez l' -s recursive -X ignore-all-spaceoption)

Eric Hu
la source
4
Notez également: vous pouvez git checkout exp1 path/to/file_a path/to/file_x
créer
2
C'est beau. Je l'ai fait git checkout feature <path>/*pour obtenir des groupes de fichiers.
isherwood
Cela fonctionne bien, mais a ajouté deux objets de validation supplémentaires. Pas une grosse affaire mais un peu en désordre
MightyPork
@MightyPork vous avez raison. Malheureusement, depuis que j'ai écrit cela il y a si longtemps, je ne sais plus pourquoi les étapes "git stash" et "git merge stash" sont là au lieu d'un "git commit".
Eric Hu
2
Oh c'est clair, je pense. De cette façon, il fusionne le seul fichier, sans écraser nécessairement les modifications précédentes sur la branche cible.
MightyPork
48

La réponse de 1800 INFORMATION est tout à fait correcte. En tant que git noob, cependant, "utiliser git cherry-pick" ne m'a pas suffi pour comprendre cela sans creuser un peu plus sur Internet, donc j'ai pensé publier un guide plus détaillé au cas où quelqu'un d'autre serait dans un bateau similaire.

Mon cas d'utilisation voulait tirer sélectivement les modifications de la branche github de quelqu'un d'autre dans la mienne. Si vous avez déjà une branche locale avec les modifications, il vous suffit de faire les étapes 2 et 5-7.

  1. Créez (sinon créé) une branche locale avec les modifications que vous souhaitez apporter.

    $ git branch mybranch <base branch>

  2. Passez-y.

    $ git checkout mybranch

  3. Déroulez les modifications que vous souhaitez du compte de l'autre personne. Si vous ne l'avez pas déjà fait, vous voudrez les ajouter en tant que télécommande.

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

  4. Retirez tout de leur branche.

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

  5. Affichez les journaux de validation pour voir les modifications que vous souhaitez:

    $ git log

  6. Revenez à la branche dans laquelle vous souhaitez insérer les modifications.

    $ git checkout originalbranch

  7. Cherry choisit vos commits, un par un, avec les hachages.

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

Astuce chapeau: http://www.sourcemage.org/Git_Guide

Cory
la source
3
Astuce: utilisez d'abord la git cherrycommande (voir d'abord le manuel) pour identifier les commits que vous n'avez pas encore fusionnés.
akaihola
Cela fonctionne .. 1. créé une nouvelle branche 2.créé des fichiers / apporté quelques modifications 3. commit 4. checkout la branche master 5. Exécutez git cherry-pick -x hash-of-commit et résolvez les conflits de fusion si vous êtes bon aller.
RamPrasadBismil
Votre lien ne fonctionne plus. Pouvez-vous le mettre à jour s'il vous plaît?
creep3007
42

Voici comment remplacer un Myclass.javafichier en masterbranche par Myclass.javaen feature1branche. Cela fonctionnera même s'il Myclass.javan'existe pas master.

git checkout master
git checkout feature1 Myclass.java

Notez que cela écrasera - et non fusionnera - et ignorera plutôt les modifications locales dans la branche principale.

maestr0
la source
6
Cela ne fusionnera pas. Il va juste écraser les changements sur master avec les changements de la branche feature1.
Skunkwaffle
3
Parfaitement, je cherchais ce genre de fusion où theirsécraser ours=> +1 Cheers;)
olibre
1
Parfois, tout ce que vous voulez faire est de remplacer tout le fichier, c'est donc ce que je voulais, mais vous devez vous assurer de vouloir perdre toutes les modifications que vous avez apportées à ce fichier.
MagicLAMP
1
Solution la plus propre, étant donné que l'OP voulait spécifiquement remplacer le fichier entier par l'équivalent sur une autre branche: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 manière simple, pour fusionner réellement des fichiers spécifiques de deux branches, n'est pas simplement de remplacer des fichiers spécifiques par ceux d'une autre branche.

Première étape: diff les branches

git diff branch_b > my_patch_file.patch

Crée un fichier patch de la différence entre la branche actuelle et branch_b

Deuxième étape: appliquer le correctif sur les fichiers correspondant à un modèle

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

notes utiles sur les options

Vous pouvez utiliser *comme caractère générique dans le modèle d'inclusion.

Les barres obliques n'ont pas besoin d'être échappées.

En outre, vous pouvez utiliser --exclude à la place et l'appliquer à tout sauf aux fichiers correspondant au modèle, ou inverser le correctif avec -R

L'option -p1 est un maintien de la commande de correctif * unix et le fait que le contenu du fichier de correctif précède chaque nom de fichier avec a/ou b/(ou plus selon la façon dont le fichier de correctif a été généré) que vous devez supprimer afin qu'il puisse comprendre le vrai fichier vers le chemin d'accès au fichier auquel le patch doit être appliqué.

Consultez la page de manuel pour git-apply pour plus d'options.

Troisième étape: il n'y a pas de troisième étape

Évidemment, vous voudriez valider vos modifications, mais qui dira que vous n'avez pas d'autres ajustements liés à faire avant de faire votre validation.

masukomi
la source
1
Cela était très utile lorsque current_branch avait beaucoup de changements "supplémentaires" qui devaient être préservés. Vous avez le diff des seuls changements apportés par branch_b comme: git diff HEAD ... branch_b (oui - trois périodes font le tour de magie).
Saad Malik
@masukomi, À l'étape 2, ne devriez-vous pas ajouter le fichier de correctif créé à l'étape 1 comme argument?
Spiralis
Pour moi, toutes les modifications sont rejetées. Une idée pourquoi?
LinusGeffarth
La pensée initiale de @LinusGeffarth est que vous avez peut-être rétrogradé les branches lors de la création du patch? fera un suivi en dehors de SO pour voir si nous pouvons le comprendre.
masukomi
24

Voici comment vous pouvez faire en sorte que l'historique suive seulement quelques fichiers d'une autre branche avec un minimum d'agitation, même si une fusion plus "simple" aurait apporté bien plus de changements que vous ne voulez pas.

Tout d'abord, vous prendrez la décision inhabituelle de déclarer à l'avance que ce que vous êtes sur le point de valider est une fusion, sans git faire quoi que ce soit aux fichiers de votre répertoire de travail:

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

. . . où "nom_branche" correspond à tout ce que vous prétendez fusionner. Si vous deviez vous engager immédiatement, cela ne ferait aucun changement mais cela montrerait toujours l'ascendance de l'autre branche. Vous pouvez ajouter plus de branches / tags / etc. à la ligne de commande si vous en avez besoin également. À ce stade cependant, il n'y a pas de changement à valider, alors récupérez les fichiers des autres révisions, ensuite.

git checkout branchname1 -- file1 file2 etc

Si vous fusionnez à partir de plusieurs autres branches, répétez au besoin.

git checkout branchname2 -- file3 file4 etc

Maintenant, les fichiers de l'autre branche sont dans l'index, prêts à être validés, avec l'historique.

git commit

et vous aurez beaucoup d'explications à faire dans ce message de validation.

Veuillez noter cependant, au cas où ce ne serait pas clair, que c'est une mauvaise chose à faire. Ce n'est pas dans l'esprit de ce qu'est une "branche", et le choix des cerises est une façon plus honnête de faire ce que vous feriez ici. Si vous vouliez faire une autre "fusion" pour d'autres fichiers sur la même branche que vous n'avez pas apportée la dernière fois, cela vous arrêtera avec un message "déjà à jour". C'est un symptôme de ne pas créer de branche quand nous devrions avoir, dans la branche "de" devrait être plus d'une branche différente.

jejese
la source
3
Votre première commande ( git merge --no-ff --no-commit -s outs branchname1) est exactement ce que je cherchais! Merci!
RobM
1
Avec plusieurs branches, l'historique requis, la nécessité de fusionner un ou plusieurs fichiers et de changer le contenu du fichier avant de pousser, cela semble être une alternative décente. Par exemple, dev => master, mais vous souhaitez modifier la définition d'hôte ou similaire avant de pousser sur master.
timss
15

Je sais que je suis un peu en retard, mais c'est mon flux de travail pour la fusion de fichiers sélectifs.

#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
la source
J'ai utilisé une légère variation à ce sujet. Au lieu de fusionner, j'ai choisi les cerises. Ça fait le boulot. Le seul inconvénient de cette approche est que vous perdez la référence au hachage de validation d'origine.
Matt Florence
15

Le moyen le plus simple consiste à définir votre référentiel sur la branche avec laquelle vous souhaitez fusionner, puis à exécuter,

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

Si vous courez

git status

vous verrez le fichier déjà mis en scène ...

Ensuite, exécutez

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

Facile.

dsieczko
la source
3
C'est presque la meilleure réponse que j'ai trouvée. s'il vous plaît voir jasonrudolph.com/blog/2009/02/25/… Si clair, concis et ça marche!
superuseroi
1
qui remplacera complètement le contenu des fichiers de la branche source plutôt que de fusionner
Amare
J'étais sur le point de répondre comme ça, je pensais inventer de nouvelles choses auxquelles on n'a pas encore répondu! Mais c'est la façon la plus simple de le faire. Cela devrait être au top!
Irfandy Jip
15

J'ai trouvé que ce message contenait la réponse la plus simple. Faites simplement:

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

Exemple:

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

Voir le post pour plus d'informations.

Stunner
la source
3
Cela ne fusionne pas vraiment, il écrase le fichier sur la branche courante.
Igor Ralic
1
@igrali C'est un commentaire utile, mais par rapport à la difficulté des moyens "appropriés" de le faire, c'est une bonne solution de contournement. Il faut juste être très prudent.
owensmartin
12

Il est étrange que git ne dispose toujours pas d'un outil aussi pratique "prêt à l'emploi". Je l'utilise beaucoup lors de la mise à jour d'une ancienne branche de version (qui a encore beaucoup d'utilisateurs de logiciels) par seulement quelques corrections de bugs de la branche de version actuelle. Dans ce cas, il est souvent nécessaire d'obtenir rapidement quelques lignes de code du fichier dans le tronc, en ignorant beaucoup d'autres modifications (qui ne sont pas censées entrer dans l'ancienne version) ... Et bien sûr , la fusion interactive à trois voies est nécessaire dans ce cas, git checkout --patch <branch> <file path>n'est pas utilisable à cette fin de fusion sélective.

Vous pouvez le faire facilement:

Ajoutez simplement cette ligne à la [alias]section de votre fichier global .gitconfigou 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;' -"

Cela implique que vous utilisez Beyond Compare. Changez simplement pour le logiciel de votre choix si nécessaire. Ou vous pouvez le changer en fusion automatique à trois voies si vous n'avez pas besoin de la fusion sélective interactive:

[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;' -"

Ensuite, utilisez comme ceci:

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

Cela vous donnera la véritable opportunité de fusion arborescente sélective de n'importe quel fichier dans une autre branche.

JimStar
la source
10

Ce n'est pas exactement ce que vous cherchiez, mais cela m'a été utile:

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

C'est un mélange de quelques réponses.

Felipe
la source
2
C'est en effet utile et pourrait être ajouté à ce qui est pour moi la meilleure réponse, la réponse de @ alvinabad. En faisant: git checkout HEAD file1pour conserver la version actuelle et ne pas fusionner le fichier file1, on peut utiliser l' -poption pour sélectionner une partie du fichier à fusionner. merci pour l'astuce!
Simon C.
Ceci est ma réponse préférée. Simple, au point et fonctionne
Jesse Reza Khorasanee
8

Je ferais un

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

De cette façon, vous pouvez limiter la plage de validations pour un modèle de fichier à partir d'une branche.

Volé à: http://www.gelato.unsw.edu.au/archives/git/0701/37964.html

lumpidu
la source
Dans certaines situations, cela peut être très utile. Cependant, si les changements se trouvent dans une branche différente, vous pouvez simplement passer à la caisse de cette branche, comme dans la réponse de Bart J ci-dessus.
cdunn2001
Qui est Bart Js?
Noir
8

J'ai eu exactement le même problème que celui mentionné ci-dessus. Mais j'ai trouvé ce blog git plus clair en expliquant la réponse.

Commande à partir du lien ci-dessus:

#You are in the branch you want to merge to
git checkout <branch_you_want_to_merge_from> <file_paths...>
Susheel Javadi
la source
avez-vous testé cela? Je suis sûr que les fichiers seront remplacés de <branch_you_want_to_merge_from> plutôt que d'être fusionnés
Amare
7

J'aime la réponse «git-interactive-merge» ci-dessus, mais il y en a une plus facile. Laissez git faire cela pour vous en utilisant une combinaison de rebase d'interactif et sur:

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

Donc, le cas est que vous voulez C1 et C2 de la branche 'fonctionnalité' (point de branche 'A'), mais rien du reste pour l'instant.

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

Ce qui, comme ci-dessus, vous dépose dans l'éditeur interactif où vous sélectionnez les lignes de «sélection» pour C1 et C2 (comme ci-dessus). Enregistrez et quittez, puis il poursuivra le rebase et vous donnera la branche 'temp' et également HEAD à master + C1 + C2:

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

Ensuite, vous pouvez simplement mettre à jour master vers HEAD et supprimer la branche temporaire et vous êtes prêt à partir:

# git branch -f master HEAD
# git branch -d temp
Patauger
la source
7

Et alors git reset --soft branch? Je suis surpris que personne ne l'ait encore mentionné.

Pour moi, c'est le moyen le plus simple de sélectionner de manière sélective les modifications d'une autre branche, car, cette commande place dans mon arbre de travail, toutes les modifications de différence et je peux facilement choisir ou annuler celle dont j'ai besoin. De cette façon, j'ai un contrôle total sur les fichiers validés.

GarryOne
la source
6

Je sais que cette question est ancienne et qu'il existe de nombreuses autres réponses, mais j'ai écrit mon propre script appelé «pmerge» pour fusionner partiellement les répertoires. C'est un travail en cours et j'apprends toujours les scripts git et bash.

Cette commande utilise git merge --no-commit puis désapplique les modifications qui ne correspondent pas au chemin fourni.

Utilisation: git pmerge branch path
Exemple:git merge develop src/

Je ne l'ai pas testé intensivement. Le répertoire de travail doit être exempt de toute modification non validée et de fichiers non suivis.

#!/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
la source
4

Vous pouvez utiliser read-treepour lire ou fusionner l'arborescence distante donnée dans l'index actuel, par exemple:

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

Pour effectuer la fusion, utilisez -mplutôt.

Voir aussi: Comment fusionner un sous-répertoire dans git?

kenorb
la source
3

Une approche simple pour la fusion / validation sélective par fichier:

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
la source
3

Si vous n'avez pas trop de fichiers modifiés, cela ne vous laissera aucun commit supplémentaire.

1. Branche en double temporairement
$ git checkout -b temp_branch

2. Réinitialiser au dernier commit souhaité
$ git reset --hard HEAD~n , où nest le nombre de commits dont vous avez besoin pour revenir en arrière

3. Extraire chaque fichier de la branche d'origine
$ git checkout origin/original_branch filename.ext

Vous pouvez maintenant valider et forcer push (pour écraser à distance), si nécessaire.

JBaczuk
la source
3

Si vous avez seulement besoin de fusionner un répertoire particulier et de laisser tout le reste intact tout en préservant l'historique, vous pouvez éventuellement essayer ceci ... créer un nouveau target-branchhors dumaster avant d'expérimenter.

Les étapes ci-dessous supposent que vous avez deux branches target-branchet source-branch, et le répertoire dir-to-mergeque vous souhaitez fusionner se trouve dans le source-branch. Supposez également que vous avez d'autres répertoires comme dir-to-retaindans la cible que vous ne souhaitez pas modifier et conserver l'historique. Suppose également qu'il existe des conflits de fusion dans le fichier 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
la source
2

Lorsque seuls quelques fichiers ont changé entre les validations actuelles des deux branches, je fusionne manuellement les modifications en parcourant les différents fichiers.

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

raratiru
la source