Git слияние внутри строки - программирование
Подтвердить что ты не робот

Git слияние внутри строки

Преамбула

Я использую git как систему управления версиями для бумаги, которую моя лаборатория пишет, в LaTeX. Сотрудников несколько человек.

Я запутался в git, будучи упрямым в том, как он сливается. Скажем, два человека сделали однословные изменения в строке, а затем попытаются объединить их. Хотя git diff -word-diff кажется способным ПОКАЗАТЬ разницу между ветвями по слову, git merge кажется неспособным выполнить слияние по слову и вместо этого требует ручного слияния.

В документе LaTeX это особенно раздражает, так как общая привычка при написании LaTeX состоит в том, чтобы написать полный параграф на строку и просто позволить вашему текстовому редактору обрабатывать перенос слов при отображении для вас. Мы сейчас работаем, добавляя новую строку для каждого предложения, так что git может по крайней мере объединить изменения в разных предложениях в абзаце. Но он все равно запутается в нескольких изменениях внутри предложения, и это сделает текст, конечно, более удобным.

Вопрос

Есть ли способ git объединить два файла "слово за словом", а не "строка за строкой"?

4b9b3361

Ответ 1

Здесь решение в том же духе, что и sehe's, с несколькими изменениями, которые, надеюсь, будут касаться ваших комментариев:

  • Это решение рассматривает слияние по предложению, а не по слову, как вы делали это вручную, только теперь пользователь увидит одну строку в абзаце, но git увидит параграфы, разбитые на предложения. Это кажется более логичным, поскольку добавление/удаление предложения из абзаца может быть совместимо с другими изменениями в абзаце, но, вероятно, более желательно иметь ручное слияние, когда одно и то же предложение редактируется двумя коммитами. Это также имеет преимущество "чистых" снимков, которые все еще могут быть читаемыми человеком (и латекс скомпилирован!).
  • Фильтры представляют собой однострочные команды, которые должны облегчить перенос этого кода в соавторы.

Как и в решении saha, сделайте (или добавьте к) .gittatributes.

    *.tex filter=sentencebreak

Теперь, чтобы реализовать фильтры очистки и смазывания:

    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\""

Я создал тестовый файл со следующим содержимым, обратите внимание на однострочный абзац.

    \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 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 why I found the place s'durned innarestin'.

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

После того, как мы передадим его локальному репо, мы увидим исходное содержимое.

    $ 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 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 why I found the place s'durned innarestin'.

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

Итак, правила чистого фильтра - это когда он находит строку текста, которая заканчивается на . или ? или ! или '' (что латексный способ делать двойные кавычки), затем пробел, он добавит% NL% и символ новой строки. Но он игнорирует строки, которые начинаются с\(латексные команды) или содержат комментарий где угодно (так что комментарии не могут стать частью основного текста).

Фильтр smudge удаляет% NL% и новую строку.

Дифференциация и слияние выполняются в "чистых" файлах, поэтому изменения в абзацах объединяются предложением по предложению. Это желаемое поведение.

Приятно, что файл латекса должен компилироваться в чистом или размытом состоянии, поэтому есть надежда на то, что соавторам не нужно ничего делать. Наконец, вы можете поместить команды git config в оболочку script, которая является частью репо, поэтому соавтору просто нужно будет запустить его в корне репо для настройки.

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

Этот последний бит - это взлом, потому что, когда этот script запускается сначала, ветка уже выгружена (в чистой форме), и она не получает автоматически смазанного.

Вы можете добавить этот файл script и .gitattributes в репо, затем новым пользователям просто нужно клонировать, а затем запустить script в корне репо.

Я думаю, что этот script даже работает на windows git, если выполняется в git bash.

Недостатки:

  • Это не относится к строкам с комментариями, а просто игнорирует их.
  • % NL% - это уродливый
  • Фильтры могут испортить некоторые уравнения (не уверен в этом).

Ответ 2

Вы можете попробовать следующее:

вместо замены механизма слияния (hard) вы можете сделать какую-то "нормализацию" (канонизацию, если хотите). Я не говорю LateX, но позвольте мне проиллюстрировать следующее:

Скажите, что у вас есть вход вроде test.raw

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

Вы хотите, чтобы он разложил/объединил слово за словом. Добавьте следующий .gitattributes файл

*.raw     filter=wordbyword

Тогда

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

Минимальная реализация фильтров будет

/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;
}

После того, как вы завершили файл, проверьте исходное содержимое обработанного blob с помощью git show

HEAD:test.raw`:

curve 
ball 
well 
received 
{misfit} 
whatever

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

#@#DELIM#@#

После изменения содержимого test.raw на

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

Вывод git diff --patch-with-stat будет, вероятно, тем, что вы хотели:

 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#@#

Вы можете увидеть, как это будет работать волшебным образом для слияний, в результате чего слово за словом будет различаться и сливаться. Q.E.D.

(Надеюсь, вам понравится мое творческое использование .gitattributes. Если нет, мне понравилось делать это небольшое упражнение)

Ответ 3

Я считаю, что алгоритм git merge довольно прост (даже если вы можете заставить его работать сложнее с стратегией слияния "терпение" ).
Его рабочий элемент останется линией.

Но общая идея состоит в том, чтобы делегировать какой-либо мелкозернистый механизм обнаружения_решения стороннему инструменту который вы можете настроить с помощью git config mergetool.
Если некоторые слова в длинной строке отличаются, этот внешний инструмент (KDiff3, DiffMerge,...) сможет поднять это изменение и представить его вам.