Выбрать главу

last_mark = nil # loop through the directories Dir.chdir(ARGV[0]) do Dir.glob("*").each do |dir| next if File.file?(dir) # move into the target directory Dir.chdir(dir) do last_mark = print_export(dir, last_mark) end end end

Вы запускаете функцию print_export внутри каждого каталога, она берёт запись и отметку предыдущего снимка состояния и возвращает запись и отметку текущего; таким образом они соединяются нужным образом между собой. «Отметка» — это термин утилиты fast-import, обозначающий идентификатор, который вы даёте коммиту; когда вы создаёте коммиты, вы назначаете каждому из них отметку, которую можно использовать для связывания с другими коммитами. Таким образом, первая операция, которую надо включить в метод print_export, это генерация отметки из имени каталога:

mark = convert_dir_to_mark(dir)

Мы сделаем это путём создания массива каталогов и используя значение порядкового номера каталога в массиве, как его отметку, поскольку отметка должна быть целым числом:

$marks = [] def convert_dir_to_mark(dir) if !$marks.include?(dir) $marks << dir end ($marks.index(dir) + 1).to_s end

Теперь, когда мы имеем целочисленное представление нашего коммита, нам нужны даты, чтобы указывать их в метаданных коммитов. Поскольку дата записана в имени каталога, мы выделяем её оттуда. Следующей строкой в сценарии print_export будет:

date = convert_dir_to_date(dir)

где метод convert_dir_to_date определён как:

def convert_dir_to_date(dir) if dir == 'current' return Time.now().to_i else dir = dir.gsub('back_', '') (year, month, day) = dir.split('_') return Time.local(year, month, day).to_i end end

Этот метод возвращает целочисленное значение даты для каждого каталога. Последняя часть метаданных, которая нам нужна для всех коммитов это данные о коммитере, которые мы жёстко задаём в глобальной переменной:

$author = 'Scott Chacon <schacon@example.com>'

Теперь мы готовы приступить к выводу данных коммита в своём сценарии импорта. Дадим начальную информацию говорящую, что мы задаём объект коммита, ветку, на которой он находится, затем отметку, которую мы ранее сгенерировали, информацию о коммитере и сообщение коммита, а затем предыдущий коммит, если он есть. Код выглядит следующим образом:

# print the import information puts 'commit refs/heads/master' puts 'mark :' + mark puts "committer #{$author} #{date} -0700" export_data('imported from ' + dir) puts 'from :' + last_mark if last_mark

Мы жёстко задаём часовой пояс (-0700), поскольку так проще. Если вы импортируете данные из другой системы, вы должны указать часовой пояс в виде смещения. Сообщение коммита должно быть представлено в особом формате:

data (size)\n(contents)

Формат состоит из слова data, размера данных, которые требуется прочесть, символа переноса строки и, наконец, самих данных. Поскольку нам потребуется использовать такой же формат позже, для описания содержимого файла, создадим вспомогательный метод export_data:

def export_data(string) print "data #{string.size}\n#{string}" end

Всё что нам осталось, это описать содержимое файла для каждого снимка состояния. Это просто, поскольку каждый из них содержится в каталоге: мы можем вывести команду deleteall, за которой следует содержимое каждого файла в каталоге. После этого Git соответствующим образом позаботится о регистрации каждого снимка:

puts 'deleteall' Dir.glob("**/*").each do |file| next if !File.file?(file) inline_data(file) end

Примечание: поскольку многие системы рассматривают свои ревизии как изменения от одного коммита до другого, fast-import также может принимать команды задающие для каждого коммита, какие файлы были добавлены, удалены или модифицированы, а также что является новым содержимым файлов. В нашем примере вы могли бы вычислить разность между снимками состояния и предоставить только эти данные, но это сложнее. С таким же успехом можно предоставить Git все данные для того, чтобы он сам вычислил разницу. Если с вашими данными проще предоставлять разницу между снимками состояния, обратитесь к странице руководства fast-import для получения подробностей о том как предоставлять данные таким способом.

Формат для задания содержимого нового файла, либо указания нового содержимого изменённого файла следующий:

M 644 inline path/to/file data (size) (file contents)

Здесь, 644 — это права доступа (если в проекте есть исполняемые файлы, вам надо выявить их и назначить им права доступа 755), а параметр inline говорит о том, что содержимое будет выводиться непосредственно после этой строки. Метод inline_data выглядит следующим образом:

def inline_data(file, code = 'M', mode = '644') content = File.read(file) puts "#{code} #{mode} inline #{file}" export_data(content) end

Мы повторно используем метод export_data, определённый ранее, поскольку он работает тут так же, как и при задании сообщений коммитов.

Последнее, что вам осталось сделать, это вернуть текущую отметку, чтобы её можно было передать для использования в следующую итерацию: