LINUX.ORG.RU

diff для ленивых разработчиков


0

0

В былые времена, на венде, для сравнения oracle-схем (читай баз) я удовлетворялся встроенным в Quest Software TOAD сравнивателем. Он был неплох, и со своей задачей справлялся. Но пересев в линух, меня ждало разочарование. Ни один из [и так небольшого количества] инструментов не предоставляет даже половины былого комфорта. А именно, элементарное сравнение и мёржинг двух пэкеджей (читай библиотек) по несколько тысяч строк в каждом, где каждый отформатирован по-своему а реальных изменений всего-ничего, становится натуральной пыткой. Поняв тщетность попыток найти необходимый функционал в одном инструменте, я решил поискать костыли в виде сторонних инструментов. А именно - самостоятельные сравниловки и мёржиловки ([visual] compare and merging tools). Перебрав практически все популярные инструменты, выяснилось, что очень немногие (читай почти никто) умели пропускать в сравнении двух текстовых файлов символы конца строки.

Поясню на примере:

[bugman@localhost 1]$ cat ora1.sql
declare
nfunctionresult NUMBER(9);
begin

nFunctionResult :=
orauser.pack_util.FGetNeededValue (
in_nSomeParam => somevalue
);

dbms_output.put_line('Function result: '||
nFunctionResult);
exception
when SOME_EXCEPTION THEN
orauser.pack_util.PReportError('SOME_EXCEPTION');
when OTHERS THEN
orauser.pack_util.PReportError('OTHER exception');
end;


[bugman@localhost 1]$ cat ora2.sql
declare
nFunctionResult NUMBER(9);
begin
nFunctionResult := orauser.pack_util.FGetNeededValue (in_nSomeParam => somevalue);
dbms_output.put_line('Function result: '||nFunctionResult);
exception
when SOME_EXCEPTION THEN
orauser.pack_util.PReportError('SOME_EXCEPTION');
when OTHERS THEN
orauser.pack_util.PReportError('OTHER');
end;

[bugman@localhost 1]$ diff ora1.sql ora2.sql
2c2
< nfunctionresult NUMBER(9);
---
> nFunctionResult NUMBER(9);
4,11c4,5
<
< nFunctionResult :=
< orauser.pack_util.FGetNeededValue (
< in_nSomeParam => somevalue
< );
<
< dbms_output.put_line('Function result: '||
< nFunctionResult);
---
> nFunctionResult := orauser.pack_util.FGetNeededValue (in_nSomeParam => somevalue);
> dbms_output.put_line('Function result: '||nFunctionResult);
16c10
< orauser.pack_util.PReportError('OTHER exception');
---
> orauser.pack_util.PReportError('OTHER');

[bugman@localhost 1]$ diff -wi ora1.sql ora2.sql
4,11c4,5
<
< nFunctionResult :=
< orauser.pack_util.FGetNeededValue (
< in_nSomeParam => somevalue
< );
<
< dbms_output.put_line('Function result: '||
< nFunctionResult);
---
> nFunctionResult := orauser.pack_util.FGetNeededValue (in_nSomeParam => somevalue);
> dbms_output.put_line('Function result: '||nFunctionResult);
16c10
< orauser.pack_util.PReportError('OTHER exception');
---
> orauser.pack_util.PReportError('OTHER');



Как видно пытливым человеческим глазом, суть различий файлов ora1.sql и ora2.sql в одном лишь слове и разном форматировании, но их diff абсолютно нечитаем. Не улучшает ситуацию и ключ "положить на все пробелы" -w. Не смотря на то, что в оригинальной документации сказано, что пробелами являются и <a href="http://www.gnu.org/software/diffutils/manual/html_node/White-Space.html#White...">символы конца строки</a>, заведённый мною баг был закрыт в redhat'ной багзилле с пометкой "НЕ БАГ", а сами ГНУшники отписались что мол да, это БАГ, но БАГ документации :)

Из всех переробованных утилит, наиболее удобная оказалась xxdiff (а конкретнее её опция <a href="http://furius.ca/xxdiff/doc/xxdiff-secrets.html#per-hunk-ignore-whitespace">ignore per-hunk whitespaces</a>), но к сожалению, сама утилита не умела работать с юникод файлами, не имела встроенного редактора. чтобы "на лету" править фалы, не предоставляла CLI для возможной интеграции с другими средствами да и к тому же имела не самый приятный интерфейс.

Но ближе всех по смыслу подошла утилитка коммандной строки dwdiff. Эта небольшая обёрточка вокруг diff выдаёт ровно то, что мне нужно - различия на уровне СЛОВ. Но и без ложки дёгтя не обошлось - вывод этой утилиты своеобразен и стандартных опций для приведения разницы в diff формат не имеет:



[bugman@localhost 1]$ dwdiff -i ora1.sql ora2.sql
declare
nFunctionResult NUMBER(9);
begin
nFunctionResult := orauser.pack_util.FGetNeededValue [-(
in_nSomeParam-] {+(in_nSomeParam+} => [-somevalue
);-] {+somevalue);+}
dbms_output.put_line('Function result: [-'||
nFunctionResult);-] {+'||nFunctionResult);+}
exception
when SOME_EXCEPTION THEN
orauser.pack_util.PReportError('SOME_EXCEPTION');
when OTHERS THEN
[-orauser.pack_util.PReportError('OTHER exception');-]
{+orauser.pack_util.PReportError('OTHER');+}
end;



С другой стороны, это и не мудрено, ибо стандарнтный patch имеет уровень строки, а в каком виде выводить разницу двух файлов, если они различаются по-дифовски в каждой строке? Этим вопросом я озадачился и списался с автором сей утилиты, коммрадом G.P. Halkes и вот к чему мы пришли:

[bugman@localhost 1]$ ~/Загрузки/diffwrap.sh ./ora1.sql ./ora2.sql diff -i
10c10
< orauser.pack_util.PReportError('OTHER exception');
---
> orauser.pack_util.PReportError('OTHER');


Сей замечательный враппер позволяет использовать эту тулзу в том числе и с визуальными дифферами, типа kdiff3. Сам скриптик скоро войдёт в состав dwdiff и будет распространятся вместе с ней на радость ленивым кодерам.


Странные вы. Правильный способ (IMHO) — привести перед сравнением оба файла в "стандартный" формат (в простейшем случае, убрать все lfы, и поставить новые в канонических местах, но наверняка где-то есть indent и для "этого" языка). После чего (vim)?diff уже достаточно хорош.

DonkeyHot ★★★★★
()
Ответ на: комментарий от DonkeyHot

видимо, файлы были сгенерены разными софтинами или версиями и в среднестатистическом случае достигают over 9000 строк.
приводить ЭТО в нормальный вид руками не особо-то и приятно)
хотя indent, по идее, должен подхватить.
по-любому не хватает в diff-е умения сопостовлять нескольких строк в одном файле одной строке в другом (естественно, при необходимости).

xydo ★★
()
Ответ на: комментарий от init_6

Meld пользуется в качестве backend'a обычным diff'ом, отсюда невозможность игнорировать разрывы строк. Да и тормознутый он на больших файлах, хотя безусловно по-своему удобный.

ДонКихот, пальцев на конечностях не хватит, если начать перечислять ситуации, когда сравнение нужно, а преформатирование - нет

bugman
() автор топика
Ответ на: комментарий от xydo

>приводить ЭТО в нормальный вид руками

Руками? Вид не должен быть "нормальным", он должен быть одинаковым. Потому достаточно чего-то типа:

:1,$ s/\(begin\|end\|select\|\[(;)\]\|и т.д.\)/\1^v^m/gi

DonkeyHot ★★★★★
()
Ответ на: комментарий от bugman

>когда сравнение нужно, а преформатирование - нет

Сохрани оригиналы. А "в общем" да, лучше если инструменты понимают синтаксис. Только в этом случае парсеры на каждый случай задолбёшся писать, т.ч. (IMO (путь в (или (никуда) (s-exprы))))

DonkeyHot ★★★★★
()
Ответ на: комментарий от DonkeyHot

Так, стоп товарищи :) никто ж и не спорит, что у описываемой мною проблемы существуют другие решения. Более того, я сам подумывал, не натравить ли на сравниваемые исходники какой-нить автоформатер. Но отказался от этой мысли по следующим соображениям: - в коллективе выработался некий coding style guide, привести в соответствие с которым любой автоформатер дело не одного даже наверное дня, тратить на это время, чтобы потом разочароваться в выборе уже нет возможности - если откинуть предыдущий пункт, и просто привести копии упоминаемых файлов в какой-нибудь единый формат только для сравнивания, появляется другая задача - как спроецировать найденные различия на оригиналы?

Предложенное решение я никому не навязываю, а лишь делюсь опытом в том, как можно сэкономить свое время в подобных ситуациях. Использовать его или нет - дело сугубо индивидуальное, но про себя отмечу что это works perfectly for me и более простых и удобных решений я пока не увидел

bugman
() автор топика
Ответ на: комментарий от bugman

>в коллективе выработался некий coding style guide
стрёмный такой кодинг стайл, который разрешает наличие двух таких разных версий файлов.

как вариант:
натравить автоформатирование на оба файла - результат во временные файлы, которые пото уже и сравнивать diff-ом.
дальше попробовать этот diff наложить как патч. только, боюсь в 50% случаев это будет фейл из-за несоответствия контекста. погугли, как можно их устранять автоматически (чтобы patch клал на них известно-что).

xydo ★★
()

Если подойти чуть-чуть филосовски, то:

так как в файлах пробелы и переводы строк несущественны,

то diff и patch *должны* справляться со своей работой в таких условиях.

_______________________________

С другой стороны, довольно-таки странно, что сложившийся coding style не поддерживается каким-либо форматтером, и это явно надо исправлять.

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

> то diff и patch *должны* справляться со своей работой в таких условиях.

то поиск (или написание) таких diff и patch явно имеет практический смысл, и странно, если до сих пор их нет. // FIXED

www_linux_org_ru ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.