Comment utiliser vimdiff pour résoudre un conflit de fusion git?

159

Je viens de fusionner une branche dans mon master dans git et j'ai Automatic merge failed; fix conflicts and then commit the result.maintenant j'ai couru git mergetoolet vimdiff s'est ouvert avec l'image ci-dessous. Je ne sais pas comment utiliser vimdiff. Que signifie chaque panneau ici et comment dois-je procéder pour résoudre le conflit de fusion?

entrez la description de l'image ici

Cool Guy Yo
la source
3
Consultez cette page . Si c'est ce que vous entendez par «correct», l'état actuel de votre code est en haut à gauche.
romainl
@romainl Je suis toujours confus après avoir lu cela, quels sont les raccourcis et comment choisir le fichier à utiliser comme branche principale?
Cool Guy Yo
2
@Danny http://www.rosipov.com/blog/use-vimdiff-as-git-mergetool/ en est une bonne.
ᴠɪɴᴄᴇɴᴛ
Voir aussi: this
skelliam

Réponses:

142

Les quatre tampons fournissent une vue différente du même fichier. Le tampon en haut à gauche (LOCAL) correspond à l'apparence du fichier dans votre branche cible (dans laquelle vous fusionnez). Le tampon en haut à droite (REMOTE) correspond à l'apparence du fichier dans votre branche source (d'où vous fusionnez). Le tampon du milieu (BASE) est l'ancêtre commun des deux (vous pouvez donc comparer comment les versions gauche et droite ont divergé l'une de l'autre).

Je me trompe peut-être sur le point suivant. Je pense que la source du conflit de fusion est que les deux fichiers ont changé la même partie du fichier depuis BASE; LOCAL a changé les guillemets de doubles à simples, et REMOTE a effectué le même changement mais a également changé la valeur d'arrière-plan d'une couleur à une URL. (Je pense que la fusion n'est pas assez intelligente pour remarquer que toutes les modifications apportées à LOCAL sont également présentes dans REMOTE; il sait juste que LOCAL a apporté des modifications depuis BASE aux mêmes endroits que REMOTE).

Dans tous les cas, le tampon du bas contient le fichier que vous pouvez réellement éditer - celui qui se trouve dans votre répertoire de travail. Vous pouvez apporter les modifications que vous souhaitez; vimvous montre en quoi il diffère de chacune des vues de dessus, qui sont les zones que la fusion automatique ne pouvait pas gérer. Tirez les modifications de LOCAL si vous ne voulez pas les modifications REMOTE. Tirez les modifications de REMOTE si vous préférez celles-ci aux modifications LOCALES. Tirez de BASE si vous pensez que REMOTE et LOCAL sont faux. Faites quelque chose de complètement différent si vous avez une meilleure idée! En fin de compte, les modifications que vous apportez ici sont celles qui seront réellement validées.

chepner
la source
4
Question rapide: comment enregistrer dans vim?
Cool Guy Yo
6
:xou :w( :xsort aussi) plus «retour».
Jonathan Leffler
4
Anders: il existe d'autres outils de fusion que vous pouvez utiliser si vous ne savez pas comment les utiliser vim.
chepner
3
@AndersKitson, puisque vous êtes sur Mac OS X, FileMerge est parfait, gratuit et livré avec XCode.
romainl
8
Pourquoi le vote négatif? S'il y a quelque chose d'incorrect dans les faits, veuillez le corriger ou au moins le signaler.
chepner
91

La réponse de @ chepner est excellente, je voudrais ajouter quelques détails sur la partie "comment dois-je procéder pour résoudre le conflit de fusion" de la question. Si vous regardez comment utiliser réellement vimdiff dans ce cas, cela va ci-dessous.


Tout d'abord, pour aborder l'option «tout abandonner» - si vous ne voulez pas utiliser «vimdiff» et que vous voulez abandonner la fusion: appuyez sur Esc, puis tapez :qa!et appuyez sur Enter. (voir aussi Comment quitter l'éditeur Vim? ). Git vous demandera si la fusion est terminée, répondez avec n.


Si vous souhaitez utiliser vimdiff, voici quelques raccourcis utiles. Cela suppose que vous connaissez les bases de Vim (navigation et insertion / mode normal):

  • accédez au tampon du bas (résultat de la fusion): Ctrl-W j
  • naviguer vers le prochain diff avec j/ k; ou, mieux, utilisez ] cet [ cpour naviguer respectivement vers le diff suivant et précédent
  • utilisez z olorsque vous êtes sur un pli pour l'ouvrir, si vous voulez voir plus de contexte
  • pour chaque différence, selon la réponse de @ chepner, vous pouvez soit obtenir le code d'une version locale, distante ou de base, soit le modifier et le refaire comme bon vous semble
    • pour l'obtenir à partir de la version locale, utilisez :diffget LO
    • depuis la télécommande: :diffget RE
    • de la base: :diffget BA
    • ou, si vous souhaitez modifier le code vous-même, obtenez d'abord une version de local / remote / base, puis passez en mode insertion et modifiez le reste
  • une fois terminé, enregistrez le résultat de la fusion et fermez toutes les fenêtres :wqa
  • normalement, git détecte que la fusion a été faite et crée le commit de fusion

Il ne semble pas possible d'ajouter des segments de conflit locaux et distants sans copier-coller ni raccourcis personnalisés: /vi/10534/is-there-a-way-to-take-both- lors de l'utilisation de vim-as-merge-tool, ce qui est dommage car add add est un type de conflit si courant.

Pour éviter que vimdiff ne vous demande d'appuyer sur Entrée à chaque démarrage, ajoutez à votre .vimrc:

set shortmess=Ot

comme mentionné sur: /vi/771/how-can-i-suppress-the-press-enter-prompt-when-opening-files-in-diff-mode

Vous pouvez rechercher sur Internet d'autres raccourcis vimdiff. J'ai trouvé celui-ci utile: https://gist.github.com/hyamamoto/7783966

Timur
la source
10
Cela devrait être voté x1000 fois et accepté comme une meilleure réponse.
Andrey Portnoy
pour passer rapidement au conflit suivant, recherchez simplement ===. faites "/ ===" et entrez
Apit John Ismail
Voir ce post ( stackoverflow.com/questions/51520705/… ) si plus d'une correspondance trouvée en utilisant :diffget.
Jason le
7

L'outil de fusion ultime pour remplacer vimdiff

C'est une sorte de ironie, mais c'est ce vers quoi j'ai fini par converger en tant que vimmer après avoir essayé vimdiff.

Pour résoudre un conflit de fusion, j'ai presque toujours besoin de voir:

  • ÉLOIGNÉ
  • LOCAL
  • deux diffs:
    • diff BASE REMOTE
    • diff BASE LOCAL

pour ensuite essayer de les mettre ensemble.

Alors que vimdiff affiche BASE, LOCAL et REMOTE à l'écran:

    +--------------------------------+
    | LOCAL  |     BASE     | REMOTE |
    +--------------------------------+
    |             MERGED             |
    +--------------------------------+

Je ne sais pas comment faire pour montrer clairement ces deux différences dont j'ai besoin en plus de regarder droite gauche droite gauche plusieurs fois.

De plus, LOCAL et REMOTE sont déjà visibles dans les marqueurs de conflit git merge, donc je ne gagne pas grand-chose à un outil qui les montre à nouveau.

Par conséquent, j'ai plutôt créé mon propre petit "difftool" qui montre en fait les diffs qui me manquaient:

~ / bin / cirosantilli-mergetool

#!/usr/bin/env bash
BASE="$1"
LOCAL="$2"
REMOTE="$3"
diff --color -u "$BASE" "$LOCAL"
diff --color -u "$BASE" "$REMOTE"
exit 1

GitHub en amont .

Et installez-le avec:

git config --global mergetool.cirosantilli-mergetool.cmd 'cirosantilli-mergetool $BASE $LOCAL $REMOTE'
git config --global mergetool.cirosantilli-mergetool.trustExitCode true
# If you want this to become your default mergetool.
#git config --global merge.tool 'cirosantilli-mergetool'

Maintenant, quand vous faites:

git mergetool -t cirosantilli-mergetool

il montre les deux diffs que je veux sur le terminal, par exemple quelque chose le long:

--- ./src/dev/arm/RealView_BASE_15560.py        2019-12-27 13:46:41.967021591 +0000
+++ ./src/dev/arm/RealView_LOCAL_15560.py       2019-12-27 13:46:41.979021479 +0000
@@ -994,7 +994,7 @@                                                              

     def setupBootLoader(self, cur_sys, loc):
         if not cur_sys.boot_loader:                           
-            cur_sys.boot_loader = [ loc('boot_emm.arm64'), loc('boot_emm.arm') ]
+            cur_sys.boot_loader = [ loc('boot.arm64'), loc('boot.arm') ]
         cur_sys.atags_addr = 0x8000000                  
         cur_sys.load_offset = 0x80000000                    

@@ -1054,7 +1054,7 @@                                           
             ]                                                     

     def setupBootLoader(self, cur_sys, loc):
-        cur_sys.boot_loader = [ loc('boot_emm_v2.arm64') ]
+        cur_sys.boot_loader = [ loc('boot_v2.arm64') ]
         super(VExpress_GEM5_V2_Base,self).setupBootLoader(
                 cur_sys, loc)                             

--- ./src/dev/arm/RealView_BASE_15560.py        2019-12-27 13:46:41.967021591 +0000
+++ ./src/dev/arm/RealView_REMOTE_15560.py      2019-12-27 13:46:41.991021366 +0000
@@ -610,10 +610,10 @@           
     def attachIO(self, *args, **kwargs):              
         self._attach_io(self._off_chip_devices(), *args, **kwargs)

-    def setupBootLoader(self, cur_sys, loc):
-        cur_sys.boot_loader = loc('boot.arm') 
-        cur_sys.atags_addr = 0x100                           
-        cur_sys.load_offset = 0       
+    def setupBootLoader(self, cur_sys, boot_loader, atags_addr, load_offset):
+        cur_sys.boot_loader = boot_loader      
+        cur_sys.atags_addr = atags_addr     
+        cur_sys.load_offset = load_offset

Vous pouvez donc voir ici les deux diffs vidés dans le terminal:

  • RealView_BASE_15560.py contre RealView_LOCAL_15560.py
  • RealView_BASE_15560.py contre RealView_REMOTE_15560.py

Si les diffs sont gros, je vais juste chercher avec mes superpuissances tmux .

Oui, vous perdez certains raccourcis fournis par vimdiff, mais en général, la résolution des conflits nécessite un copier-coller soigneux des deux versions, ce que je peux faire très bien dans une session vim normale avec les marqueurs de conflit git.

Observation et comparaison de fichiers pendant l' vimdiffexécution

Avant de cirosantilli-mergetoolm'asseoir et d'automatiser ma configuration parfaite avec , c'est ce que je faisais pour obtenir les deux différences dont j'avais besoin.

Pendant l' git mergetoolexécution vimdiff, s'il y a un conflit sur un fichier nommé, disons main.py, git génère des fichiers pour chacune des versions, nommés comme:

main_BASE_1367.py
main_LOCAL_1367.py
main_REMOTE_1367.py

dans le même répertoire que main.pyoù se 1367trouve le PID de git mergetool, et donc un entier "aléatoire", comme mentionné à: Dans un conflit de fusion git, quels sont les fichiers BACKUP, BASE, LOCAL et REMOTE générés?

Donc, pour voir les diffs que je veux, je trouve d'abord les fichiers générés avec git status, puis j'ouvre de nouveaux terminaux et je fais un vimdiff entre les paires de fichiers qui me tiennent à cœur:

vim -d main_BASE_1367.py main_LOCAL_1367.py
vim -d main_BASE_1367.py main_REMOTE_1367.py

Ensemble git mergetool, ces informations aident BEAUCOUP à comprendre ce qui se passe rapidement!

De plus, même lorsque mergetool est en cours d'exécution, vous pouvez simplement ouvrir le fichier:

vim main.py

directement et éditez-le ici si vous pensez que ce sera plus facile avec une fenêtre d'éditeur plus grande.

Aller directement à fusionner les conflits

Alors que les ]csauts au prochain point de diff dans vimdiff, il n'y a pas toujours de conflit de fusion.

Pour vous aider, j'ai dans mon ~/.vimrc:

# Git Merge conflict
nnoremap <leader>gm /\v^\<\<\<\<\<\<\< \|\=\=\=\=\=\=\=$\|\>\>\>\>\>\>\> /<cr>

qui trouve directement les conflits.

git imerge

Peut-être que la meilleure option est simplement de renoncer à utiliser vimdiff et de s'appuyer sur vim + git imerge régulier qui a été mentionné à: Comment puis-je savoir quels commits Git provoquent des conflits? puisque la courbe d'apprentissage de vimdiff est ennuyeuse et qu'elle ne fait pas les fonctions dont nous avons le plus besoin.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
la source
1
Vote positif. Je pense avoir mentionné cela il y a 9 ans dans stackoverflow.com/a/3052118/6309 . (voir la dernière partie de la réponse)
VonC
@VonC oui, je pense que vous avez gagné celui-ci! XD
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功