Git Merge: applique les modifications au code déplacé vers un autre fichier

Je suis en train d’essayer une manoeuvre de merde assez puissante. Un problème que je rencontre est que j’ai apporté des modifications à du code dans ma twig, mais mon collègue a déplacé ce code dans un nouveau fichier dans sa twig. Donc, quand j’ai git merge my_branch his_branch , git n’a pas remarqué que le code dans le nouveau fichier était le même que l’ancien, et donc aucun de mes changements n’est là.

Quelle est la meilleure façon de procéder pour appliquer à nouveau les modifications au code dans les nouveaux fichiers. Je n’aurai pas trop de problèmes pour trouver git log --stat à appliquer (je peux juste utiliser git log --stat ). Mais pour autant que je sache, il n’y a aucun moyen d’obtenir git pour réappliquer les modifications dans les nouveaux fichiers. Le plus simple est de réappliquer manuellement les modifications, ce qui ne semble pas être une bonne idée.

Je sais que git reconnaît les blobs, pas les fichiers, donc il doit sûrement y avoir un moyen de le dire, “appliquez ce changement de code exact à partir de cette validation, sauf où il se trouve mais où il se trouve dans ce nouveau fichier”.

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.

Dites que vous avez modifié original.txt sur votre twig (la twig local ), mais sur la twig master, original.txt a été copié sur un autre, disons copy.txt . Cette copie a été effectuée dans un commit que nous nommons commit CP .

Vous souhaitez appliquer toutes vos modifications locales, les commits A et B ci-dessous, qui ont été effectués sur original.txt , dans le nouveau fichier copy.txt .

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

Créez un move twig jetable au sharepoint départ de vos modifications. C’est-à-dire, placez la twig de move à commit X , celle qui précède les commits que vous souhaitez fusionner; Très probablement, il s’agit de la validation à partir de laquelle vous avez déployé vos modifications. En tant qu’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 twig, lancez la commande de renommage suivante:

 git mv original.txt copy.txt 

Cela renomme le fichier. Notez que copy.txt n’existait pas encore dans votre arbre à ce stade.
Validez votre modification (nous nommons ce commit MV ).

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

Vous pouvez maintenant rebaser votre travail en haut du move :

 git rebase move local 

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

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

Maintenant, vous ne voulez pas nécessairement avoir besoin de valider MV dans l’historique de votre twig principale, car l’opération de déplacement peut entraîner un conflit avec l’opération de copie sur le CP validation dans la twig principale.

Vous n’avez qu’à refaire votre travail à nouveau, en supprimant l’opération de déplacement, comme suit:

 git rebase move local --onto CP 

… où CP est le commit où copy.txt été introduit dans l’autre twig. Cela rebase toutes les modifications sur copy.txt au-dessus de la validation du CP . Maintenant, votre twig local est exactement comme si vous copy.txt toujours copy.txt et pas original.txt , et vous pouvez continuer à fusionner avec d’autres.

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

Il est important que les modifications soient appliquées sur le CP ou sinon copy.txt n’existerait pas et les modifications seraient appliquées sur original.txt .

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

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

À part cela, la seule façon de fonctionner automatiquement est de savoir si la détection de renommage de git peut déterminer que les anciens et les nouveaux fichiers sont identiques – ce qui semble ne pas être vraiment votre cas, juste une partie d’entre eux. Il est vrai que git utilise des blobs, pas des fichiers, mais un blob n’est que le contenu d’un fichier entier, sans le nom de fichier et les métadonnées attachés. Donc, si vous avez un morceau de code déplacé entre deux fichiers, ils ne sont pas vraiment les mêmes: le rest du contenu du blob est différent, juste le morceau en commun.

Voici une solution de fusion consistant à rencontrer un conflit de fusion avec renommer et éditer et à le résoudre avec mergetool en reconnaissant les 3 fichiers sources de fusion corrects.

  • Après une fusion échoue à cause de “fichier supprimé” que vous réalisez avoir été renommé et modifié:

    1. Vous abandonnez la fusion.
    2. Validez les fichiers renommés sur votre twig.
    3. Et fusionner à nouveau.

Procédure pas à pas:

Créez 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 twig où vous éditerez plus tard:

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

Créez le renommer et éditer 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 twig 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(+) 

Essayez de fusionner le 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. 

Notez 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%) 

Essayez à nouveau 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. 

Génial! 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