Question Comment fusionner sélectivement ou choisir des changements d'une autre branche dans Git?


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

  • master: importation 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

exp1 et exp2 représentent deux approches architecturales très différentes. Jusqu'à ce que je sois plus loin je n'ai aucun moyen de savoir lequel (si l'un ou l'autre) fonctionnera. Au fur et à mesure que je progresse dans une branche, j'ai parfois des modifications qui seraient utiles dans l'autre branche et j'aimerais fusionner celles-ci.

Quel est le meilleur moyen de fusionner des changements sélectifs d'une branche de développement à une autre tout en laissant derrière tout le reste?

Approches que j'ai considérées:

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

  2. Copie manuelle de fichiers communs dans un répertoire temporaire suivi de git checkout pour passer à l'autre branche et ensuite plus de copie manuelle hors du répertoire temporaire dans l'arbre de travail.

  3. Une variation sur ce qui précède. Abandonner le exp branches pour l'instant et utiliser 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 de semblable à un paramètre de chemin de filtre qui ferait git-merge plus sélectif.


1158
2018-01-16 04:55


origine


Réponses:


Vous utilisez le cerise commande pour obtenir des commits individuels d'une branche.

Si les modifications que vous souhaitez ne sont pas dans des validations individuelles, utilisez la méthode indiquée ici pour diviser le commit en commits individuels. En gros, vous utilisez git rebase -i pour obtenir le commit original à éditer, alors git reset HEAD^ pour inverser sélectivement les changements, puis git commit commettre ce bit comme un nouveau commit dans l'histoire.

Il y a une autre belle méthode ici dans le magazine Red Hat, où ils utilisent git add --patch ou peut-être git add --interactive ce qui vous permet d'ajouter juste des parties d'un morceau, si vous voulez diviser différentes modifications dans un fichier individuel (recherche dans cette page pour "split").

Après avoir divisé les changements, vous pouvez maintenant choisir seulement ceux que vous voulez.


390
2018-01-16 06:01



J'ai eu exactement le même problème que mentionné par vous ci-dessus. Mais j'ai trouvé ce plus clair dans l'explication de la réponse.

Résumé:

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

    $ git checkout source_branch -- <paths>...
    
  • 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>...
    
  • Enfin valider

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

816
2017-09-03 08:48



Pour fusionner sélectivement des fichiers d'une branche dans une autre branche, exécutez

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

branchX est la branche que vous voulez fusionner dans la branche actuelle.

le --no-commit option mettra en scène les fichiers qui ont été fusionnés par Git sans les valider. Cela vous donnera l'opportunité de modifier les fichiers fusionnés comme vous le souhaitez, puis de les valider vous-même.

Selon la façon dont vous voulez fusionner les fichiers, il y a quatre cas:

1) Vous voulez une vraie fusion.

Dans ce cas, vous acceptez les fichiers fusionnés de la même façon que Git les a fusionnés automatiquement puis les a validés.

2) Il y a des fichiers que vous ne voulez 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 file1 dans la branche actuelle et remplacer le file1 automatisé par Git.

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

Courir:

git checkout branchX file1

Cela va récupérer la version de file1 dans branchX et écraser file1 auto-fusionné par Git.

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

Dans ce cas, vous pouvez modifier la modification file1 directement, mettez-le à jour pour ce que vous voulez la version de file1 devenir, puis s'engager.

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



Pour expliquer plus loin avec un exemple, disons que vous voulez fusionner branchX dans la branche actuelle:

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

Vous exécutez ensuite le git status commande 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, file2, et file3 sont les fichiers git ont fusionné avec succès.

Qu'est-ce que cela signifie est que les changements dans la master et branchX pour tous ces trois fichiers ont été combinés ensemble 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 alors vous pouvez

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

Si vous ne voulez pas fusionner file1 et que vous voulez conserver la version dans la branche actuelle

Courir

git checkout HEAD file1

Si vous ne voulez pas fusionner file2 et ne veulent que la version en branchX

Courir

git checkout branchX file2

Si tu veux file3 être fusionné automatiquement, ne faites rien.

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


file4 ci-dessus est une fusion échouée par Git. Cela signifie qu'il y a des changements dans les deux branches qui se produisent sur la même ligne. C'est ici que vous devrez résoudre les conflits manuellement. Vous pouvez ignorer la fusion effectuée en éditant le fichier directement ou en exécutant la commande checkout pour la version dans la branche que vous voulez file4 devenir.


Enfin, n'oubliez pas de git commit.


239
2018-05-21 03:00



Je n'aime pas les approches ci-dessus. L'utilisation de la sélection de cerises est excellente pour choisir un seul changement, mais c'est une douleur si vous voulez apporter tous les changements, sauf pour certains mauvais. Voici mon approche.

Il n'y a pas --interactive argument que vous pouvez passer à git merge.

Voici l'alternative:

Vous avez quelques changements dans la 'fonctionnalité' de la branche et vous voulez en amener certains mais pas tous à 'maîtriser' d'une manière non bâclée (c'est-à-dire que vous ne voulez pas choisir et valider chacun d'entre 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

Alors, enveloppez-le dans un script shell, changez le 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

85
2017-08-28 18:47



Il y a une autre façon de faire:

git checkout -p

C'est un mélange entre git checkout et git add -p et pourrait être exactement ce que vous cherchez:

   -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



Bien que certaines de ces réponses soient plutôt bonnes, je pense qu'aucun d'entre eux n'a réellement répondu à la contrainte d'origine de 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 le master, exp1, et exp2 branches. Vous voulez fusionner un fichier de chacune des branches expérimentales en 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 voulez. Rien de plus. Rien de moins. Il est utile que vous ayez des changements de fichiers radicalement différents entre les versions - dans mon cas, en changeant une application de Rails 2 en Rails 3.

MODIFIER: cela va fusionner les fichiers, mais fait une fusion intelligente. Je n'ai pas été capable de comprendre comment utiliser cette méthode pour obtenir des informations sur les différences dans les fichiers (peut-être que cela sera toujours le cas pour des différences extrêmes.) Les petites choses ennuyeuses comme les espaces blancs seront réintégrées à moins que vous utilisiez le -s recursive -X ignore-all-space option)


48
2018-05-07 11:38



La réponse de 1800 INFORMATION est complètement correcte. En tant que git noob, cependant, "utiliser git cherry-pick" ne me suffisait pas pour comprendre cela sans creuser un peu plus sur internet alors j'ai pensé que je posterais un guide plus détaillé au cas où quelqu'un d'autre serait dans un bateau similaire.

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

  1. Créez (si non créé) une branche locale avec les changements que vous voulez apporter.

    $ git branch mybranch <base branch>

  2. Passer en elle.

    $ git checkout mybranch

  3. Déposez les changements que vous voulez 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. Tirez tout de leur branche.

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

  5. Consultez les journaux de validation pour voir les modifications souhaitées:

    $ git log 

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

    $ git checkout originalbranch

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

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

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


42
2017-07-29 08:37



Voici comment vous pouvez remplacer Myclass.java déposer master branche avec Myclass.java dans feature1 branche. Cela fonctionnera même si Myclass.java n'existe pas sur master.

git checkout master
git checkout feature1 Myclass.java

Notez que cela écrase - ne fusionne pas - et ignore les changements locaux dans la branche master plutôt.


37
2018-02-27 22:42