Question

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

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

git merge --no-commit et git checkout représentent deux approches architecturales très différentes. Jusqu'à ce que je continue, 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 pourraient être utiles dans l'autre branche et j'aimerais fusionner celles-ci.

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

Approches que j'ai envisagées:

  1. exp suivi de la décomposition manuelle d’un grand nombre de modifications que je ne souhaite pas faire en commun entre les branches.

  2. Copie manuelle des fichiers communs dans un répertoire temporaire suivie de git-merge pour passer à l'autre branche, puis copie manuelle supplémentaire du répertoire temporaire dans l'arborescence de travail.

  3. Une variation de ce qui précède. Abandonnez les <=> branches pour le moment et utilisez deux référentiels locaux supplémentaires à des fins d’expérimentation. Cela rend la copie manuelle de 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 rendrait <=> plus sélectif.

Était-ce utile?

La solution

Vous utilisez la commande cherry-pick pour obtenir des commits individuels. une branche.

Si les modifications souhaitées ne sont pas validées individuellement, utilisez la méthode indiquée ici pour diviser le commettre dans des commits individuels . En gros, vous utilisez git rebase -i pour obtenir le commit d'origine à éditer, puis git reset HEAD^ pour annuler de manière sélective les modifications, puis git commit pour commettre ce bit en tant que nouveau commit dans l'historique.

Il existe une autre méthode intéressante dans Red Hat Magazine, dans laquelle ils utilisent git add --patch ou éventuellement git add --interactive, ce qui vous permet d'ajouter uniquement des parties d'un morceau si vous souhaitez fractionner différents éléments. modifications apportées à un fichier individuel (recherchez dans cette page " fractionné ").

Après avoir divisé les modifications, vous pouvez maintenant sélectionner uniquement celles que vous souhaitez.

Autres conseils

J'ai eu exactement le même problème que mentionné par vous ci-dessus. Mais j'ai trouvé ceci devient plus clair pour expliquer la réponse.

Résumé:

  • Vérifiez le ou les chemins de la branche que vous souhaitez fusionner,

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

    Astuce: Cela fonctionne également sans -- comme dans la publication liée.

  • ou pour fusionner de manière sélective des morceaux

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

    Sinon, utilisez reset, puis ajoutez avec l'option -p,

    $ git reset <paths>...
    $ git add -p <paths>...
    
  • Enfin, validez

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

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

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

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

L'option --no-commit organisera les fichiers qui ont été fusionnés par Git sans les valider réellement. Cela vous donnera la possibilité de modifier les fichiers fusionnés comme vous le souhaitez, puis de les valider vous-même.

Selon la manière 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 automatiquement fusionnés, puis vous les validez.

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 de la branche à partir de laquelle vous effectuez la fusion.

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

git checkout HEAD file1

Ceci récupérera la version de file1 dans la branche actuelle et écrasera le git status automatisé par Git.

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

Exécuter:

git checkout branchX file1

Ceci récupérera la version de file2 dans file3 et écrasera master la fusion automatique de Git.

4) Le dernier cas est si vous souhaitez sélectionner uniquement des fusions spécifiques dans git diff --cached.

Dans ce cas, vous pouvez modifier directement git commit la version modifiée, la mettre à jour avec ce que vous souhaitez que la version de file4 devienne, puis valider.

Si Git ne peut pas fusionner un fichier automatiquement, il le signalera comme & "; sans fusion &"; et produisez une copie où vous devrez résoudre les conflits manuellement.


Pour expliquer davantage avec un exemple, supposons que vous souhaitiez fusionner <=> dans la branche actuelle:

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
#

Vous exécutez ensuite la commande <=> pour afficher le statut des fichiers modifiés.

Par exemple:

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

Où <=>, <=> et <=> sont les fichiers git ont été fusionnés avec succès.

Cela signifie que les modifications apportées dans <=> et <=> pour ces trois fichiers ont été combinées sans aucun conflit.

Vous pouvez vérifier comment la fusion a été effectuée en exécutant la <=>;

git checkout branchX file2

Si vous trouvez une fusion indésirable, vous pouvez alors

  1. éditez le fichier directement
  2. enregistrer
  3. <=>

Si vous ne souhaitez pas fusionner <=> et souhaitez conserver la version dans la branche actuelle

Exécuter

<*>

Si vous ne souhaitez pas fusionner <=> et souhaitez uniquement conserver la version <>>

Exécuter

<*>

Si vous souhaitez que <=> soit fusionné automatiquement, ne faites rien.

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


<=> ci-dessus est une fusion échouée par Git. Cela signifie que des changements dans les deux branches se produisent sur la même ligne. C'est là que vous devrez résoudre les conflits manuellement. Vous pouvez annuler la fusion effectuée en modifiant directement le fichier ou en exécutant la commande d'extraction correspondant à la version de la branche que vous souhaitez <=> devenir.


Enfin, n'oubliez pas de <=>.

Je n'aime pas les approches ci-dessus. L’utilisation de cherry-pick est idéale pour sélectionner 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 pas --interactive d'argument que vous pouvez passer à git merge.

Voici l'alternative:

Vous avez quelques changements dans les 'fonctionnalités' de la branche et vous voulez en apporter certains mais pas tous à 'maîtriser' de manière non négligeable (c'est-à-dire que vous ne voulez pas sélectionner 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, encapsulez 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

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 correspondre 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.

Bien que certaines de ces réponses soient plutôt bonnes, j’ai le sentiment qu’aucune d’entre elles n’a répondu à la contrainte initiale de OP: sélectionner des fichiers particuliers dans des branches particulières. Cette solution le fait, mais peut s’avérer fastidieuse s’il ya plusieurs fichiers.

Disons que vous avez les branches master, exp1 et exp2. Vous souhaitez fusionner un fichier de chacune des branches expérimentales dans le fichier maître. Je voudrais faire 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 de modifier radicalement les fichiers d'une version à l'autre. Dans mon cas, vous modifiez une application de Rails 2 à Rails 3.

MODIFIER : cette option permet de fusionner des fichiers, mais effectue une fusion intelligente. Je ne pouvais pas comprendre comment utiliser cette méthode pour obtenir des informations de diff dans le fichier (peut-être que ce sera toujours le cas pour les différences extrêmes. Des petites choses gênantes telles que les espaces sont fusionnées à moins d'utiliser l'option -s recursive -X ignore-all-space)

La réponse de 1800 INFORMATION est tout à fait correcte. En tant que git noob, cependant, & "Utilisez git cherry-pick &"; Cela ne suffisait pas pour moi de comprendre cela sans un peu plus de fouilles sur Internet, alors j'ai pensé poster un guide plus détaillé au cas où quelqu'un d'autre serait dans un bateau similaire.

Mon cas d'utilisation cherchait à extraire de manière sélective les modifications apportées par la branche github de quelqu'un d'autre à la mienne. Si vous avez déjà une branche locale avec les modifications, il vous suffit de suivre les étapes 2 et 5 à 7.

  1. Créez (si ce n’est pas créé) une branche locale avec les modifications que vous souhaitez apporter.

    $ git branch mybranch <base branch>

  2. Basculez-vous dessus.

    $ git checkout mybranch

  3. Extrayez les modifications souhaitées 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. Déroulez tout de leur branche.

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

  5. Consultez les journaux de validation pour connaître les modifications souhaitées:

    $ git log

  6. Revenez dans la branche dans laquelle vous souhaitez extraire les modifications.

    $ git checkout originalbranch

  7. Choisissez vos commits, un par un, avec les hachages.

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

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

Voici comment vous pouvez remplacer Myclass.java fichier dans master branche par feature1 dans <=> branche. Cela fonctionnera même si <=> n'existe pas sur <=>.

git checkout master
git checkout feature1 Myclass.java

Notez que cela écrasera - pas fusionnera - et ignorera les modifications locales dans la branche principale.

La méthode la plus simple, pour fusionner des fichiers spécifiques de deux branches, ne pas simplement remplacer des fichiers spécifiques par des fichiers d'une autre branche.

Étape 1: Diffusez les branches

git diff branch_b > my_patch_file.patch

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

Deuxième étape: appliquez le correctif aux fichiers correspondant à un motif

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.

Il n'est pas nécessaire d'échapper aux barres obliques.

Vous pouvez également utiliser --exclude à la place et l'appliquer à tout sauf aux fichiers correspondant au motif, ou inverser le patch avec -R

.

L'option -p1 est un effet de réserve de la commande * unix patch et le fait que le contenu du fichier de correctif ajoute à chaque nom de fichier la valeur a/ ou b/ (ou plus (en fonction de la manière dont le fichier de correctif a été généré)). besoin de se déshabiller pour pouvoir trouver le vrai fichier dans le chemin du fichier auquel le correctif doit être appliqué.

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

Troisième étape: il n'y a pas d'étape trois

Évidemment, vous voudriez valider vos modifications, mais qui peut dire que vous ne souhaitez pas effectuer d’autres ajustements connexes avant de vous engager;

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

Tout d'abord, vous allez déclarer inhabituellement que ce que vous êtes sur le point de valider est une fusion, sans rien faire des fichiers dans votre répertoire de travail:

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

. . . où " branchname " est ce que vous prétendez être en train de fusionner. Si vous deviez vous engager immédiatement, cela ne ferait aucun changement, mais cela montrerait tout de même une ascendance de l'autre branche. Vous pouvez ajouter plus de branches / tags / etc. à la ligne de commande si vous avez besoin, aussi bien. Cependant, à ce stade, il n'y a aucune modification à valider, donc récupérez les fichiers des autres révisions, ensuite.

git checkout branchname1 -- file1 file2 etc

Si vous effectuez une fusion à partir de plusieurs autres branches, recommencez si nécessaire.

git checkout branchname2 -- file3 file4 etc

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

git commit

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

S'il vous plaît noter cependant, au cas où ce n'était pas clair, que c'est une chose foirée à faire. Ce n’est pas dans l’esprit de quelle & "Branche &"; Le choix de cerises est une façon plus honnête de faire ce que vous feriez, ici. Si vous voulez faire une autre & Quot; fusionnez & Quot; pour les autres fichiers de la même branche que ceux que vous n'avez pas apportés la dernière fois, cela vous arrêtera avec un & "déjà mis à jour &"; message. C'est un symptôme de ne pas créer de branche quand nous aurions dû, dans le & "From &"; branche doit avoir plus d’une branche différente.

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

J'ai trouvé ce message pour contenir la réponse la plus simple. Faire 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 l'article pour plus d'informations.

La méthode la plus simple consiste à définir votre rapport 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 exécutez

git status

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

Puis lancez

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

Simple.

Il est étrange que git ne dispose toujours pas d'un outil aussi pratique & "prêt à l'emploi" & "; Je l'utilise beaucoup lorsque je mets à jour une ancienne branche de version (qui compte encore de nombreux utilisateurs de logiciels) avec seulement quelques corrections de bugs de la branche de version actuelle. Dans ce cas, il est souvent nécessaire d’obtenir rapidement seulement quelques lignes de code du fichier dans le coffre, en ignorant de nombreux autres changements (qui ne sont pas censés entrer dans la version précédente) ... Et Bien sûr, une 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 section [alias] de votre fichier global .gitconfig ou 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. Il suffit de changer le logiciel de votre choix si nécessaire. Vous pouvez également choisir une 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 sélective par arborescence de n'importe quel fichier d'une autre branche.

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 réponses.

J'ai eu exactement le même problème que mentionné par vous ci-dessus. Mais j'ai trouvé Ce blog git explique plus clairement 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...>

je ferais un

  

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

De cette manière, vous pouvez limiter le nombre de commits d'un fichier à une branche.

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

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

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

Le cas est donc que vous voulez C1 et C2 de la branche 'fonctionnalité' (point de branche 'A'), mais aucun des autres pour l'instant.

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

Ce qui, comme ci-dessus, vous place dans l’éditeur interactif dans lequel vous sélectionnez les lignes de «sélection» pour C1 et C2 (comme ci-dessus). Sauvegardez et quittez, puis la rebase continuera et vous donnera la branche 'temp' ainsi que HEAD sur le maître + 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 en HEAD et supprimer la branche temporaire. Vous pouvez continuer:

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

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 des 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 n'applique pas 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é de manière approfondie. Le répertoire de travail ne doit contenir aucune modification non validée ni aucun fichier non suivi.

#!/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

Qu'en est-il de git reset --soft branch? Je suis surpris que personne n'en ait encore parlé.

Pour moi, c’est le moyen le plus simple de sélectionner de manière sélective les modifications d’une autre branche. En effet, cette commande place dans mon arbre de travail toutes les différences et je peux facilement choisir ou annuler celle dont j’ai besoin. De cette manière, je contrôle totalement les fichiers validés.

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

git remote add foo git@example.com/foo.git
git fetch foo
git read-tree --prefix=my-folder/ -u foo/master:trunk/their-folder

Pour effectuer la fusion, utilisez -m à la place.

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

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

Si vous n'avez pas trop de fichiers modifiés, cela ne vous laissera pas de commits supplémentaires.

1. Dupliquer une branche temporairement
$ git checkout -b temp_branch

2. Réinitialiser au dernier engagement souhaité
$ git reset --hard HEAD~n, où n est 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 commettre et forcer le push (pour écraser la télécommande), si nécessaire.

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>

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éez un nouveau target-branch sur la master avant d'expérimenter.

Les étapes ci-dessous supposent que vous avez deux branches source-branch et dir-to-merge, et que le répertoire dir-to-retain que vous souhaitez fusionner se trouve dans le <=>. Supposez également que vous avez d'autres répertoires, comme <=> dans la cible, que vous ne souhaitez pas modifier et conserver l'historique. En outre, supposons qu'il y ait des conflits de fusion dans le <=>.

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.
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top