Skip to content

Latest commit

 

History

History
373 lines (307 loc) · 18.5 KB

File metadata and controls

373 lines (307 loc) · 18.5 KB

Прилагодљиви увозник

Ако ваш систем није ниједан од горе наведених, требало би да на мрежи потражите алат за увоз – доступни су квалитетни алати за увоз из многих других система, укључујући CVS, Clear Case, Visual Source Safe, чак и директоријум архива. Ако ниједан од њих не ради у вашем случају, имате прилично непознат алат, или вам је потребан још више прилагођени процес увоза, требало би да искористите git fast-import. Ова команда чита једноставне инструкције са stdin за уписивање одређених Гит података. На овај начин је много једноставније креирати Гит објекте него да извршавате сирове Гит команде, или да покушате уписивање сирових објеката (за више информација, погледајте ch10-git-internals.asc). На овај начин можете написати скрипту за увоз која чита неопходне информације из система који увозите и пише јасне инструкције на stdout. Затим можете да покренете овај програм и спроведете његов излаз кроз git fast-import.

Да бисмо брзо показали, написаћете једноставан алат за увоз. Претпоставимо да радите у current, повремено правите резервну копију свог пројекта копирњањем директоријума у временски обележен back_YYYY_MM_DD директоријум који чува резерве и желите да то увезете у програм Гит. Структура ваших директоријума изгледа овако:

$ ls /opt/import_from
back_2014_01_02
back_2014_01_04
back_2014_01_14
back_2014_02_03
current

Да бисте увезли Гит директоријум, морате знати како програм Гит чува своје податке. Вероватно се сећате, Гит је у основи увезана листа комит објеката који показују на снимак садржаја. Све што треба да урадите је да команди fast-import кажете шта су снимци садржаја, који комит подаци указују на њих, и у ком редоследу се јављају. Ваша стратегија ће бити да пролазите кроз снимке један по један и да креирате комитове са садржајем сваког директоријум, повезујући сваки комит са претходним.

Као што смо урадили у ch08-customizing-git.asc, написаћемо то у Рубију, јер у општем случају радимо на том језику и углавном може лако да се чита. Овај пример прилично лако можете да напишете на било ком језику који вам је близак – он само мора да испише одговарајуће информације на stdout. А ако извршавате на Виндоуз систему, то значи да посебно морате пазити да не уведете carriage returns карактере на крај редове – git fast-import стриктно жели само line feed (LF) карактере, а не carriage return line feed (CRLF) комбинацију коју користи Виндоуз.

За почетак, прећи ћете у циљни директоријум и идентификоваћете свако поддиректоријум, од којих је сваки снимак који желите да увезете као комит. Ући ћете у свако поддиректоријум и исписаћете команде које су неопходне да се извезе. Ваша основна главна петља изгледа овако:

last_mark = nil

# прођи кроз све директоријуме
Dir.chdir(ARGV[0]) do
  Dir.glob("*").each do |dir|
    next if File.file?(dir)

    # уђи у циљни директоријум
    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 = 'John Doe <john@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 командом након које следи садржај сваког фајла у директоријуму. Програм Гит онда на одговарајући начин бележи сваки снимак:

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

Напомена: пошто многи системи на своје ревизије гледају као на промене од једног на други комит, fast-import такође може да прими и команде уз сваки комит које наводе који фајлови су додати, уклоњени или измењени, као шта је нови садржај. Израчунали бисте разлике између снимака и доставили само те податке, али то је много компликованије – исто тако можете програму Гит да доставите све податке, па да њему препустите да одреди шта и како. Ако је тако погодније за ваше податке, проверите fast-import ман страницу у вези детаља о томе како да податке доставите на овај начин.

Формат за испис садржаја новог фајла или за навођење измењеног фајла са новим садржајем је као што следи:

M 644 inline путања/до/фајла
data (величина)
(садржај фајла)

Овде 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 методу коју сте дефинисали раније, јер је начин исти као онај на који сте навели податке комит поруке.

Последње што треба да урадите је да вратите текући маркер, тако да се може проследити у наредну итерацију:

return mark
Note

Ако користите Виндоуз биће вам потребан још један корак. Као што смо поменули раније, Виндоуз користи CRLF карактере као прелом реда, док команда git fast-import очекује само LF карактер. Да бисте решили овај проблем и усрећили команду git fast-import, потребно је да Рубију наложите да уместо CRLF користи LF:

$stdout.binmode

То је то. Ево како изгледа комплетна скрипта:

#!/usr/bin/env ruby

$stdout.binmode
$author = "John Doe <john@example.com>"

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


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

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

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

def print_export(dir, last_mark)
    date = convert_dir_to_date(dir)
    mark = convert_dir_to_mark(dir)

    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

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


# Loop through the directories
last_mark = nil
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

Ако извршите ову скрипту, добићете садржај који отприлике изгледа овако:

$ ruby import.rb /opt/import_from
commit refs/heads/master
mark :1
committer John Doe <john@example.com> 1388649600 -0700
data 29
imported from back_2014_01_02deleteall
M 644 inline README.md
data 28
# Hello

This is my readme.
commit refs/heads/master
mark :2
committer John Doe <john@example.com> 1388822400 -0700
data 29
imported from back_2014_01_04from :1
deleteall
M 644 inline main.rb
data 34
#!/bin/env ruby

puts "Hey there"
M 644 inline README.md
(...)

Да бисте покренули алат за увоз, проследите овај излаз команди git fast-import док се налазите у Гит директоријуму у који желите да увезете податке. Можете да креирате нови директоријум, па да у њему као први корак извршите git init, а затим покренете своју скрипту:

$ git init
Initialized empty Git repository in /opt/import_to/.git/
$ ruby import.rb /opt/import_from | git fast-import
git-fast-import statistics:
---------------------------------------------------------------------
Alloc'd objects:       5000
Total objects:           13 (         6 duplicates                  )
      blobs  :            5 (         4 duplicates          3 deltas of          5 attempts)
      trees  :            4 (         1 duplicates          0 deltas of          4 attempts)
      commits:            4 (         1 duplicates          0 deltas of          0 attempts)
      tags   :            0 (         0 duplicates          0 deltas of          0 attempts)
Total branches:           1 (         1 loads     )
      marks:           1024 (         5 unique    )
      atoms:              2
Memory total:          2344 KiB
       pools:          2110 KiB
     objects:           234 KiB
---------------------------------------------------------------------
pack_report: getpagesize()            =       4096
pack_report: core.packedGitWindowSize = 1073741824
pack_report: core.packedGitLimit      = 8589934592
pack_report: pack_used_ctr            =         10
pack_report: pack_mmap_calls          =          5
pack_report: pack_open_windows        =          2 /          2
pack_report: pack_mapped              =       1457 /       1457
---------------------------------------------------------------------

Као што можете видети, када се процес успешно заврши, враћа вам гомилу статистичких података о ономе што је урађено. У овом случају, увезли сте укупно 13 објеката за 4 комита у 1 грану. Сада можете да извршите git log и погледате своју нову историју:

$ git log -2
commit 3caa046d4aac682a55867132ccdfbe0d3fdee498
Author: John Doe <john@example.com>
Date:   Tue Jul 29 19:39:04 2014 -0700

    imported from current

commit 4afc2b945d0d3c8cd00556fbe2e8224569dc9def
Author: John Doe <john@example.com>
Date:   Mon Feb 3 01:00:00 2014 -0700

    imported from back_2014_02_03

Ево финог чистог Гит репозиторијума. Важно је приметити да се ништа не одјављује – у почетку немате ниједан фајл у свом радном директоријуму. Да бисте их добили, своју грану најпре морате да ресетујете на место на којем се сада налази master:

$ ls
$ git reset --hard master
HEAD is now at 3caa046 imported from current
$ ls
README.md main.rb

Алатом fast-import можете да урадите још много тога – обрађујете различите режиме, бинарне податке, вишеструке гране и спајања, ознаке, индикаторе напретка процеса и још тога.Већи број примера компликованијих сценарија можете да погледате у contrib/fast-import директоријуму изворног кода програма Гит.