177 votes

git merge : appliquer les changements au code qui a été déplacé dans un autre fichier

Je suis en train de tenter une manœuvre de fusion git assez lourde en ce moment. Un problème que je rencontre est que j'ai fait des changements à un certain code dans ma branche, mais mon collègue a déplacé ce code dans un nouveau fichier dans sa branche. Donc quand j'ai fait git merge my_branch his_branch git n'a pas remarqué que le code du nouveau fichier était le même que celui de l'ancien, et donc aucun de mes changements ne s'y trouve.

Quel est le moyen le plus simple pour appliquer à nouveau mes modifications au code dans les nouveaux fichiers. Je n'aurai pas trop de problèmes pour savoir quels commits doivent être réappliqués (je peux simplement utiliser git log --stat ). Mais pour autant que je sache, il n'y a aucun moyen de faire en sorte que git réapplique les changements dans les nouveaux fichiers. La chose la plus simple que je vois pour l'instant est de réappliquer manuellement les changements, ce qui ne semble pas être une bonne idée.

Je sais que git reconnaît les blobs, pas les fichiers, donc il doit bien y avoir un moyen de lui dire, "appliquer ce changement de code exact de ce commit, sauf qu'il n'est pas là où il était, mais là où il est maintenant dans ce nouveau fichier".

169voto

coredump Points 660

J'ai eu un problème similaire et je l'ai résolu en rebasant mon travail pour qu'il corresponde à l'organisation du fichier cible. Cela fonctionne parce que git garde une trace des fichiers contenu Ainsi, en procédant à un rebasage à la suite d'un renommage, les modifications sont appliquées comme il se doit.

Plus précisément, disons que vous avez modifié original.txt sur votre branche (le local ), mais sur la branche master, original.txt a été copié dans un autre, disons copy.txt . Cette copie a été effectuée dans un commit que nous appelons commit CP .

Vous souhaitez appliquer toutes vos modifications locales, les commits A y B ci-dessous, qui ont été faites le original.txt vers le nouveau fichier copy.txt .

 ---- X -----CP------ (master)
       \ 
        `--A---B--- (local)

Créer une branche jetable move au point de départ de vos modifications avec git branch move X . En d'autres termes, il s'agit de mettre le move branche au moment de la validation X , celui qui précède les commits que vous souhaitez fusionner ; il s'agit très probablement du commit à partir duquel vous avez effectué une dérivation pour mettre en œuvre vos changements. Comme l'utilisateur @digory doo a écrit ci-dessous, vous pouvez faire git merge-base master local pour trouver X .

 ---- X (move)-----CP----- (master)
       \ 
        `--A---B--- (local)

Sur cette branche, lancez la commande de renommage suivante :

git mv original.txt copy.txt

Cette opération permet de renommer le fichier. Notez que copy.txt n'existait pas encore dans votre arbre à ce moment-là.
Validez votre modification (nous appelons cette validation "commit") MV ).

        ,--MV (move)
       /
 ---- X -----CP----- (master)
       \ 
        `--A---B--- (local)

Vous pouvez maintenant baser votre travail sur la base de move :

git rebase move local

Cela devrait fonctionner sans problème, et vos modifications sont appliquées à copy.txt dans votre agence locale.

        ,--MV (move)---A'---B'--- (local)
       /
 ---- X -----CP----- (master)

Il n'est pas nécessaire d'avoir un engagement. MV dans l'historique de votre branche principale, car l'opération de déplacement peut entraîner un conflit avec l'opération de copie lors du commit CP dans la branche principale.

Il vous suffit de rebaser à nouveau votre travail, en supprimant l'opération de déplacement, comme suit :

git rebase move local --onto CP

... où CP est l'engagement où copy.txt a été introduite dans l'autre branche. Ceci rebase tous les changements sur copy.txt au sommet de la CP commettre. Maintenant, votre local est exactement comme si vous aviez toujours modifié copy.txt et non original.txt et vous pouvez continuer à fusionner avec d'autres.

                ,--A''---B''-- (local)
               /
 -----X-------CP----- (master)

Il est important que les changements soient appliqués sur CP ou autre copy.txt n'existerait pas et les changements seraient appliqués en retour sur le site de la original.txt .

J'espère que c'est clair. Cette réponse arrive tardivement, mais elle peut être utile à quelqu'un d'autre.

37voto

Jefromi Points 127932

Vous pouvez toujours utiliser git diff (ou git format-patch ) pour générer le patch, puis éditer manuellement les noms de fichiers dans le patch, et l'appliquer avec git apply (ou git am ).

En dehors de cela, la seule façon pour que cela fonctionne automatiquement est que la détection de renommage de git puisse comprendre que l'ancien et le nouveau fichier sont la même chose - ce qui semble ne pas être le cas dans votre cas, juste une partie d'entre eux. Il est vrai que git utilise des blobs, pas des fichiers, mais un blob est juste le contenu d'un fichier entier, sans le nom de fichier et les métadonnées attachées. Donc si vous avez un morceau de code déplacé entre deux fichiers, il ne s'agit pas vraiment du même blob - le reste du contenu du blob est différent, il n'y a que le morceau en commun.

33voto

Vincent Scheib Points 2702

Voici un fusionner solution consistant à rencontrer un conflit de fusion avec rename and edit et à le résoudre avec mergetool en reconnaissant les 3 fichiers sources de fusion corrects.

  • Après l'échec d'une fusion en raison d'un "fichier supprimé" dont vous vous rendez compte qu'il a été renommé et modifié :
  1. Vous annulez la fusion.
  2. Commencer les fichiers renommés sur votre branche.
  3. Et fusionner à nouveau.

Visite guidée :

Créer un fichier .txt :

$ git init
Initialized empty Git repository in /tmp/git-rename-and-modify-test/.git/

$ echo "A file." > file.txt
$ git add file.txt
$ git commit -am "file.txt added."
 [master (root-commit) 401b10d] file.txt added.
 1 file changed, 1 insertion(+)
 create mode 100644 file.txt

Créez une branche que vous éditerez plus tard :

$ git branch branch-with-edits
 Branch branch-with-edits set up to track local branch master. 

Créer le renommage et l'édition sur le master :

$ git mv file.txt renamed-and-edited.txt
$ echo "edits on master" >> renamed-and-edited.txt 
$ git commit -am "file.txt + edits -> renamed-and-edited.txt."
 [master def790f] file.txt + edits -> renamed-and-edited.txt.
 2 files changed, 2 insertions(+), 1 deletion(-)
 delete mode 100644 file.txt
 create mode 100644 renamed-and-edited.txt

Passez à la branche et modifiez-la également :

$ git checkout branch-with-edits 
 Switched to branch 'branch-with-edits'
 Your branch is behind 'master' by 1 commit, and can be fast-forwarded.
 (use "git pull" to update your local branch)
$ 
$ echo "edits on branch" >> file.txt 
$ git commit -am "file.txt edited on branch."
 [branch-with-edits 2c4760e] file.txt edited on branch.
 1 file changed, 1 insertion(+) 

Tentative de fusion de master :

$ git merge master
 CONFLICT (modify/delete): file.txt deleted in master and modified in HEAD. Version HEAD of file.txt left in tree.
 Automatic merge failed; fix conflicts and then commit the result. 

Remarquez que le conflit est difficile à résoudre et que les fichiers ont été renommés. Abandonnez, imitez le renommage :

$ git merge --abort
$ git mv file.txt renamed-and-edited.txt
$ git commit -am "Preparing for merge; Human noticed renames files were edited."
 [branch-with-edits ca506da] Preparing for merge; Human noticed renames files were edited.
 1 file changed, 0 insertions(+), 0 deletions(-)
rename file.txt => renamed-and-edited.txt (100%) 

Réessayez de fusionner :

$ git merge master
 Auto-merging renamed-and-edited.txt
 CONFLICT (add/add): Merge conflict in renamed-and-edited.txt
 Recorded preimage for 'renamed-and-edited.txt'
 Automatic merge failed; fix conflicts and then commit the result. 

Excellent ! La fusion entraîne un conflit "normal" qui peut être résolu avec mergetool :

$ git mergetool
 Merging:
 renamed-and-edited.txt

 Normal merge conflict for 'renamed-and-edited.txt':
 {local}: created file
 {remote}: created file
$ git commit 
 Recorded resolution for 'renamed-and-edited.txt'.
 [branch-with-edits 2264483] Merge branch 'master' into branch-with-edits

3voto

Ernesto Baschny Points 331

Ma solution rapide à ce problème (dans mon cas, il ne s'agissait pas d'un seul fichier, mais de toute une structure de répertoires) a été la suivante :

  • déplacer le(s) fichier(s) de "my_branch" à l'endroit où il(s) se trouve(nt) dans "his_branch" (git rm / git add)
  • git commit -m "moved to original location for merging"
  • git merge his_branch (pas de conflits cette fois-ci !)
  • déplacer le(s) fichier(s) à l'endroit désiré (git rm / git add)
  • git commit -m "moved back to final location after merge"

Vous aurez deux commits supplémentaires dans l'historique.

Mais comme git suit le déplacement des fichiers, git blame , git log etc fonctionnera toujours sur ces fichiers, puisque les commits qui ont déplacé les fichiers ne les ont pas modifiés. Je ne vois donc aucun inconvénient à cette méthode et elle est très simple à comprendre.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X