Возможно, я использовал бы 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]))