2013-12-04 3 views
1

У меня есть куча Sed/unix fu, что я , начинающий подозревать, не будет лучшим способом выполнить задачу, учитывая отклонение строк, выходящих из 'svn diff' ...Каков наилучший способ превратить Subversion diff в JSON?

svn diff -r 1:9 | 
expand | 
sed -e 's/^Index: \(.*\)/]}, { "index":"\1", /g' | 
sed -e 's/^--- \(.*\)/"from":"\1", /g' | 
sed -e 's/^+++ \(.*\)/"to":"\1", "chunks":[/g' | 
sed -e 's/^@@ \(.*\) @@/]},{"locn":"\1", "lines": [/g' | 
sed -e 's/^-\(.*\)/"-\1",/g' | 
sed -e 's/^+\(.*\)/"+\1",/g' | 
sed -e 's/^ \(.*\)/" \1",/g' | 
sed -e 's/^==============.*//g' | 
tr -d '\n' | 
sed -e 's/"chunks":\[\]},{/"chunks":\[{/g' | 
sed -e 's/^]}, \(.*\)/{"changes":[ \1]}]}]}/g' | 
sed -e 's/,\]}/]}/g' | 
jshon 

Он надежно получается ...

Index: file1.txt 
=================================================================== 
--- file1.txt (revision 8) 
+++ file1.txt (revision 9) 
@@ -1,3 +1,5 @@ 
+zzz 
+ 
aaa 

Efficiently Blah blah 
@@ -7,3 +9,5 @@ 
functional solutions. 

bbb 
+ 
+www 

В ...

{ 
"changes": [ 
    { 
    "index": "file1.txt", 
    "to": "file1.txt (revision 9)", 
    "from": "file1.txt (revision 8)", 
    "chunks": [ 
    { 
    "locn": "-1,3 +1,5", 
    "lines": [ 
     "+zzz", 
     "+", 
     " aaa", 
     " ", 
     " Efficiently blah blah" 
    ] 
    }, 
    { 
    "locn": "-7,3 +9,5", 
    "lines": [ 
     " functional solutions.", 
     " ", 
     " bbb", 
     "+", 
     "+www" 
    ] 
    } 
    ] 
    } 
] 
} 

Но есть намного больше, что может выйти из о f 'svn diff', чем я обрабатываю, и мне интересно, не так ли глупо продолжать в этом направлении.

ответ

2

Возможно, я использовал бы diff parser in libsvn_diff. Я не уверен, что он был обернут привязками, но вполне вероятно, что он работает с привязками Python.

Начать с svn_diff_open_patch_file(), а затем перебирать патчи в файле, вызывая svn_diff_parse_next_patch(), пока не даст вам NULL для svn_patch_t.

Как только у вас есть структура для каждого файла, для создания вашего JSON должно быть тривиально.

Справедливое предупреждение, могут быть ошибки в этом разностном анализаторе. Это было написано для svn-патча, на котором я нахожу багги (хотя я думаю, что большинство ошибок находятся в приложении патча, а не в разборе). С другой стороны, это должно означать, что даже если мы отрегулируем выход формата патча, у вас всегда должен быть хороший парсер. И, конечно же, ваши отчеты об ошибках (если у вас все получится) могут улучшить наш парсер.

Только другие вещи, которые возникают у меня, это то, что API не является потоковым (он работает с файлами), который может быть не таким, каким вы хотите. Также, если вы действительно хотите спуститься по кроличьей норе, вы можете просто управлять слоем WC/RA прямо в качестве получателя диска редактора, который генерирует ваш json-выход вместо унифицированного diff. Но это, вероятно, намного больше, чем то, что вы хотите, потому что есть тонна кода, чтобы обрабатывать все различные варианты типов-типов diff (локальные для локальных, репо для репо, локальные для репо, репо для локальных).

Пример

Поэтому я решил играть с дифф анализатором. В итоге я написал следующий скрипт python, чтобы использовать его, и в качестве примера создаю почти тот же вывод JSON. Обратите внимание, что синтаксический анализатор отбрасывает строку индекса, поэтому у меня нет этого в моем представлении.

Я столкнулся с одним небольшим изменением, которое мне пришлось сделать для привязок SWIG Python, создающих эту работу (поле hunks svn_patch_t не было должным образом преобразовано в список python), который я исправил в r1548379 на соединительной линии Subversion (I предположите, что патч будет применяться чисто к 1.8).

Обратите внимание, что в документации svn_diff_hunk_readline_diff_text() первая строка будет заголовком hunk, но это не похоже на правду. Хотя вы можете восстановить данные заголовка hunk, которые вы хотели, с помощью функций svn_diff_hunk_get_ {original, modified} _ {start, length}.

Я не потрудился возиться с синтаксисом изменения свойств или синтаксического анализа операции (я не думаю, что поддержка для этого действительно завершена, но если вы этого хотите, я оставляю это как упражнение для вас).

Мое приложение, если это не самый Pythonic-код.Часть этого обусловлена ​​тем, что API-интерфейсы C, которые завернуты, не способствуют этому, а часть заключается в том, что я просто не очень удобен с Python. Я сделал это на Python, поскольку эти привязки ближе к завершению в этом отношении.

Вы можете выполнить следующий сценарий с просто: python scriptname.py patchfile

import sys 
from svn import diff, core 
import json 

class UDiff: 
    def convert_svn_patch_t(self, patch, pool): 
    data = {} 
    data['from'] = patch.old_filename 
    data['to'] = patch.new_filename 
    iter_pool = core.Pool(pool); 
    chunks = [] 
    for hunk in patch.hunks: 
     iter_pool.clear() 
     chunk = {} 
     orig_start = diff.svn_diff_hunk_get_original_start(hunk) 
     orig_len = diff.svn_diff_hunk_get_original_length(hunk) 
     mod_start = diff.svn_diff_hunk_get_modified_start(hunk) 
     mod_len = diff.svn_diff_hunk_get_modified_length(hunk) 
     chunk['locn'] = "-%d,%d +%d,%d" % \ 
         (orig_start, orig_len, mod_start, mod_len) 
     lines = [] 
     while True: 
     text, eol, eof = diff.svn_diff_hunk_readline_diff_text(hunk, 
                   iter_pool, 
                   iter_pool) 
     if eof: 
      break; 
     lines.append("%s%s" % (text, eol)) 
     chunk['lines'] = lines 
     chunks.append(chunk) 
    data['chunks'] = chunks 
    self.data = data 

    def as_dict(self): 
    return self.data 

    def __init__(self, patch, pool): 
    self.convert_svn_patch_t(patch, pool) 

class UDiffAsJson: 
    def __init__(self): 
    self.pool = core.Pool() 

    def convert(self, fname): 
    patch_file = diff.svn_diff_open_patch_file(fname, self.pool) 
    iter_pool = core.Pool(self.pool) 
    changes = [] 
    while True: 
     iter_pool.clear() 
     patch = diff.svn_diff_parse_next_patch(patch_file, 
              False, # reverse 
              False, # ignore_whitespace 
              iter_pool, iter_pool) 
     if not patch: 
     break 
     udiff = UDiff(patch, iter_pool) 
     changes.append(udiff.as_dict()) 
    data = {} 
    data['changes'] = changes 
    diff.svn_diff_close_patch_file(patch_file, iter_pool) 
    return json.dumps(data, indent=True) 

if __name__ == "__main__": 
    udiffasjson = UDiffAsJson() 
    sys.stdout.write(udiffasjson.convert(sys.argv[1])) 
Смежные вопросы