51 votes

Fusion de Git à l'intérieur d'une ligne

Préambule

J'utilise git comme système de contrôle de version pour un article que mon laboratoire rédige, en LaTeX. Il y a plusieurs personnes qui collaborent.

Je me heurte au fait que git est têtu sur la façon dont il fusionne. Disons que deux personnes ont fait des modifications d'un seul mot sur une ligne, et qu'elles tentent ensuite de les fusionner. Bien que git diff --word-diff semble capable de MONTRER la différence entre les branches mot par mot, git merge semble incapable d'effectuer la fusion mot par mot, et demande à la place une fusion manuelle.

Avec un document LaTeX, c'est particulièrement ennuyeux, car l'habitude courante lors de l'écriture de LaTeX est d'écrire un paragraphe complet par ligne et de laisser votre éditeur de texte gérer l'habillage des mots lors de l'affichage. Nous contournons ce problème pour l'instant en ajoutant une nouvelle ligne pour chaque phrase, de sorte que git puisse au moins fusionner les modifications apportées à différentes phrases d'un même paragraphe. Mais il sera toujours confus en cas de changements multiples dans une phrase, et cela fait que le texte ne s'enroule plus correctement bien sûr.

La question

Existe-t-il un moyen de fusionner deux fichiers dans git "mot par mot" plutôt que "ligne par ligne" ?

14voto

achoo5000 Points 106

Voici une solution dans la même veine que celle de Sehe, avec quelques modifications qui, je l'espère, répondront à vos commentaires :

  • Cette solution envisage de fusionner en phrase plutôt que par mot, comme vous l'aviez fait à la main, seulement maintenant, l'utilisateur verra une seule ligne par paragraphe, mais git verra les paragraphes découpés en phrases. Cela semble plus logique car ajouter/supprimer une phrase d'un paragraphe peut être compatible avec d'autres modifications dans le paragraphe, mais il est probablement plus souhaitable d'avoir une fusion manuelle lorsque la même phrase est modifiée par deux commits. Cela présente également l'avantage que les instantanés "propres" restent quelque peu lisibles par l'homme (et compilables en latex !).
  • Les filtres sont des commandes d'une seule ligne, ce qui devrait faciliter leur portage auprès des collaborateurs.

Comme dans la solution de Saha, faites un (ou ajoutez à) .gittatributes .

    *.tex filter=sentencebreak

Maintenant, il faut mettre en œuvre les filtres de nettoyage et de bavure :

    git config filter.sentencebreak.clean "perl -pe \"s/[.]*?(\\?|\\!|\\.|'') /$&%NL%\\n/g unless m/%/||m/^[\\ *\\\\\\]/\""
    git config filter.sentencebreak.smudge "perl -pe \"s/%NL%\n//gm\""

J'ai créé un fichier de test avec le contenu suivant, remarquez le paragraphe d'une ligne.

    \chapter{Tumbling Tumbleweeds. Intro}
    A way out west there was a fella, fella I want to tell you about, fella by the name of Jeff Lebowski.  At least, that was the handle his lovin' parents gave him, but he never had much use for it himself. This Lebowski, he called himself the Dude. Now, Dude, that's a name no one would self-apply where I come from.  But then, there was a lot about the Dude that didn't make a whole lot of sense to me.  And a lot about where he lived, like- wise.  But then again, maybe that's why I found the place s'durned innarestin'.

    This line has two sentences. But it also ends with a comment. % here

Après l'avoir livré au dépôt local, nous pouvons voir le contenu brut.

    $ git show HEAD:test.tex

    \chapter{Tumbling Tumbleweeds. Intro}
    A way out west there was a fella, fella I want to tell you about, fella by the name of Jeff Lebowski. %NL%
     At least, that was the handle his lovin' parents gave him, but he never had much use for it himself. %NL%
    This Lebowski, he called himself the Dude. %NL%
    Now, Dude, that's a name no one would self-apply where I come from. %NL%
     But then, there was a lot about the Dude that didn't make a whole lot of sense to me. %NL%
     And a lot about where he lived, like- wise. %NL%
     But then again, maybe that's why I found the place s'durned innarestin'.

    This line has two sentences. But it also ends with a comment. % here

Donc les règles du filtre propre sont les suivantes : chaque fois qu'il trouve une chaîne de texte qui se termine par . o ? o ! o '' (c'est la façon dont latex fait les guillemets doubles) puis un espace, il ajoutera %NL% et un caractère de nouvelle ligne. Mais il ignore les lignes qui commencent par des \ (commandes latex) ou qui contiennent un commentaire où que ce soit (afin que les commentaires ne puissent pas faire partie du texte principal).

Le filtre de correction supprime %NL% et le saut de ligne.

La différenciation et la fusion sont effectuées sur les fichiers "propres", de sorte que les modifications apportées aux paragraphes sont fusionnées phrase par phrase. C'est le comportement souhaité.

Ce qui est bien, c'est que le fichier latex devrait être compilé dans l'état propre ou dans l'état maculé, ce qui permet d'espérer que les collaborateurs n'auront rien à faire. Enfin, vous pourriez mettre le git config dans un shell script qui fait partie du repo afin qu'un collaborateur n'ait qu'à l'exécuter dans le Root du repo pour être configuré.

    #!/bin/bash

    git config filter.sentencebreak.clean "perl -pe \"s/[.]*?(\\?|\\!|\\.|'') /$&%NL%\\n/g unless m/%/||m/^[\\ *\\\\\\]/\""
    git config filter.sentencebreak.smudge "perl -pe \"s/%NL%\n//gm\""

    fileArray=($(find . -iname "*.tex"))

    for (( i=0; i<${#fileArray[@]}; i++ ));
    do
        perl -pe "s/%NL%\n//gm" < ${fileArray[$i]} > temp
        mv temp ${fileArray[$i]}
    done

Cette dernière petite partie est un hack parce que lorsque ce script est exécuté pour la première fois, la branche est déjà extraite (dans la forme propre) et elle n'est pas maculée automatiquement.

Vous pouvez ajouter ce script et le fichier .gitattributes au repo, puis les nouveaux utilisateurs doivent simplement cloner, puis exécuter le script dans la racine du repo.

Je pense que ce script fonctionne même sur git Windows s'il est fait en git bash.

Inconvénients :

  • Cela ne gère pas les lignes avec des commentaires de manière intelligente, il les ignore tout simplement.
  • %NL% est plutôt moche
  • Les filtres peuvent fausser certaines équations (je n'en suis pas sûr).

8voto

sehe Points 123151

Vous pourrait Essayez ceci :

au lieu de remplacer un moteur de fusion ( dur ), vous pouvez effectuer une sorte de "normalisation" (canonisation, si vous voulez). Je ne parle pas LateX, mais permettez-moi d'illustrer mon propos comme suit :

Disons que vous avez une entrée comme test.raw

curve ball well received {misfit} whatever
proprietary format extinction {benefit}.

Vous voulez qu'il diffère/fusionne mot par mot. Ajoutez ce qui suit .gitattributes fichier

*.raw     filter=wordbyword

Entonces

git config --global filter.wordbyword.clean /home/username/bin/wordbyword.clean
git config --global filter.wordbyword.smudge /home/username/bin/wordbyword.smudge

Une implémentation minimaliste des filtres serait

/home/username/bin/wordbyword.clean

#!/usr/bin/perl
use strict;
use warnings;

while (<>)
{
    print "$_\n" foreach (m/(.*?\s+)/go);
    print '#@#DELIM#@#' . "\n";
}

/home/username/bin/wordbyword.smudge

#!/usr/bin/perl
use strict;
use warnings;

while (<>)
{
    chomp; '#@#DELIM#@#' eq $_ and print "\n" or print;
}

Après avoir validé le fichier, inspectez le contenu brut du blob validé avec `git show'.

HEAD:test.raw`:

curve 
ball 
well 
received 
{misfit} 
whatever

#@#DELIM#@#
proprietary 
format 
extinction 
{benefit}.

#@#DELIM#@#

Après avoir changé le contenu de test.raw en

curve ball welled repreived {misfit} whatever
proprietary extinction format {benefit}.

La sortie de git diff --patch-with-stat sera probablement ce que vous vouliez :

 test.raw |    6 +++---
 1 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/test.raw b/test.raw
index b0b0b88..ed8c393 100644
--- a/test.raw
+++ b/test.raw
@@ -1,14 +1,14 @@
 curve 
 ball 
-well 
-received 
+welled 
+repreived 
 {misfit} 
 whatever

 #@#DELIM#@#
 proprietary 
-format 
 extinction 
+format 
 {benefit}.

 #@#DELIM#@#

Vous pouvez voir comment cela fonctionnerait comme par magie pour les fusions résultant de la diffraction et de la fusion mot à mot. Q.E.D.

( J'espère que vous avez aimé mon utilisation créative de .gitattributes. Sinon, j'ai apprécié de faire ce petit exercice )

3voto

VonC Points 414372

Je crois que le git merge L'algorithme est assez simple (même si vous pouvez le faire travailler davantage avec la stratégie de fusion "patience").
Son poste de travail restera la ligne.

Mais l'idée générale est de déléguer tout mécanisme de détection et de résolution à grain fin à un outil tiers. que vous pouvez configurer avec git config mergetool .
Si certains mots d'une longue ligne diffèrent, cet outil externe ( KDiff3 , DiffMerge ...) sera en mesure de récupérer cette modification et de vous la présenter.

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