Skip to content

Latest commit

 

History

History
371 lines (308 loc) · 12.7 KB

File metadata and controls

371 lines (308 loc) · 12.7 KB

Uvažanje po meri

Če vaš sistem ni eden od zgoraj omenjenih, poiščite uvoznika na spletu — kakovostni uvozniki so na voljo za mnoge druge sisteme, vključno s CVS, Clear Case, Visual Source Safe in celo imenikom arhivov. Če nobeno od teh orodij ne deluje za vas, ali imate bolj zapleteno orodje, ali pa potrebujete drugačen postopek uvoza, uporabite git fast-import. Ta ukaz preprosto prebere navodila iz standardnega vhoda za pisanje določenih podatkov Git. Na ta način je veliko lažje ustvariti objekte Git kot z uporabo surovih ukazov Git ali poskusom pisanja surovih objektov (za več informacij glejte poglavje ch10-git-internals.asc). Na ta način lahko napišete uvozni skript, ki iz sistema, iz katerega uvažate, bere potrebne informacije in izpiše preprosta navodila na standardni izhod. Nato lahko to program poženete in njegov izhod preusmerite skozi git fast-import.

Za hitro demonstracijo boste napisali preprost program za uvažanje. Predpostavimo, da delate v mapi current, svoj projekt pa varnostno kopirate tako, da občasno kopirate mapo v varnostno kopijo back_YYYY_MM_DD z oznako datuma, in to želite uvoziti v Git. Vaša mapa je videti takole:

$ ls /opt/import_from
back_2014_01_02
back_2014_01_04
back_2014_01_14
back_2014_02_03
current

Če želite uvoziti Gitov direktorij, morate pregledati, kako Git shranjuje svoje podatke. Kot se morda spomnite, je Git v osnovi povezan seznam objektov potrditev, ki kažejo na posnetek vsebine. Vse, kar morate storiti, je povedati fast-import, kaj so posnetki vsebine, na kaj kažejo podatki potrditve in v kakšnem vrstnem redu gredo. Vaša strategija bo, da boste posamezne posnetke poiskali po vrstnem redu in za vsako mapo ustvarili potrditev z vsebino te mape, pri tem pa vsako potrditev povezali s prejšnjo.

Kot smo storili v ch08-customizing-git.asc, bomo to napisali v Rubyju, ker gre za jezik, s katerim se običajno ukvarjamo, in ker je običajno enostaven za branje. Ta primer lahko precej enostavno napišete v katerem koli drugem jeziku, ki ga poznate — le ustrezne podatke morate izpisati v stdout. Če delate v sistemu Windows, bodite pozorni, da ne vnesete vsebnosti pomika na konec vrstice — git fast-import zelo natančno zahteva samo vrstice (LF), ne pa vsebnosti pomika s karetom (CRLF), ki jih uporablja Windows.

Najprej se boste premaknili v ciljni imenik in identificirali vsak podimenik, ki predstavlja posnetek, ki ga želite uvoziti kot potrditev. Za vsak podimenik se boste premaknili vanj in natisnili ukaze, ki so potrebni za izvoz. Osnovna zanka je videti tako:

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

V vaši metodi print_export v vsaki mapi zaženete funkcijo, ki vzame manifest in označbo (angl. mark) prejšnje različice posnetka in vrne manifest in označbo za trenutno različico; tako jih lahko pravilno povežete. »Označba« je izraz v fast-import, ki se nanaša na identifikator, ki ga daste potrditvi; vsakič, ko ustvarite potrditev, ji dodelite označbo, ki jo lahko uporabite za povezavo z drugimi potrditvami. Torej, prva stvar, ki jo morate storiti v vaši metodi print_export, je generirati označbo iz imena mape:

mark = convert_dir_to_mark(dir)

To boste naredili z ustvarjanjem polja direktorijev in uporabili vrednost indeksa kot označbo, saj mora biti označba celo število. Vaša metoda je videti takole:

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

Zdaj, ko imate celoštevilsko predstavitev svoje potrditve, potrebujete datum za metapodatke potrditve. Ker je datum izražen v imenu mape, ga boste razčlenili. Naslednja vrstica v vaši datoteki print_export je:

date = convert_dir_to_date(dir)

kjer je convert_dir_to_date definiran kot:

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

To vrne celoštevilsko vrednost za podatke v vsakem direktoriju. Zadnji del metapodatkov, ki ga potrebujete za vsako potrditev, so podatki potrjevalca, ki ga vgradite v kodo preko globalne spremenljivke:

$author = 'John Doe <john@example.com>'

Zdaj ste pripravljeni za začetek izpisa podatkov o potrditvi za svoj program uvažanja. Začetni podatki navajajo, da opredeljujete objekt potrditve in na kateri veji je, nato sledi oznaka, ki ste jo generirali, podatki o potrditelju in sporočilu potrditve, nato pa še prejšnja potrditev, če obstaja. Koda je videti tako:

# 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

Časovni pas zacementirate v kodo (-0700), ker je to enostavno. Če uvažate iz drugega sistema, morate časovni pas določiti kot odmik. Sporočilo potrditve mora biti izraženo v posebnem formatu:

data (size)\n(contents)

Format sestoji iz podatka besede, velikosti podatkov, ki jih je treba prebrati, nove vrstice in nazadnje samih podatkov. Ker morate kasneje uporabiti isti format za določanje vsebine datoteke, ustvarite pomožno metodo export_data:

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

Vse, kar je še ostalo, je določiti vsebino datoteke za vsak posnetek. To je enostavno, saj imate vsak posnetek v mapi — lahko natisnete ukaz deleteall, nato pa vsebino vsake datoteke v mapi. Git bo nato ustrezno zabeležil vsak posnetek:

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

Opomba: Ker se mnogi sistemi nanašajo na svoje revizije kot spremembe med eno potrditvijo in drugo, lahko hitri uvoz vzame tudi ukaze z vsako potrditvijo, ki določajo, katere datoteke so bile dodane, odstranjene ali spremenjene ter kakšne so nove vsebine. Lahko bi izračunali razlike med posnetki in zagotovili samo te podatke, vendar je to bolj zapleteno — raje dajte Gitu vse podatke in ga pustite, da sam ugotovi. Če je to bolj primerno za vaše podatke, preverite stran priročnika fast-import za podrobnosti o tem, kako zagotoviti svoje podatke na ta način.

Format za naštevanje novih vsebin datotek ali določanje spremenjene datoteke z novimi vsebinami je naslednji:

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

Tukaj je način 644 (če imate izvršljive datoteke, morate zaznati in določiti namesto tega 755), in znotraj vrstice pove, da boste takoj po tej vrstici našteli vsebine. Vaša metoda inline_data je videti tako:

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

Ponovno uporabljate metodo export_data, ki ste jo prej definirali, ker je enaka načinu, kako ste določili podatke za sporočilo o potrditvi.

Zadnja stvar, ki jo morate storiti, je vrniti trenutno označbo, da jo lahko prenesete v naslednjo ponovitev:

return mark
Note

Če delujete v sistemu Windows, morate poskrbeti za dodaten korak. Kot je bilo že omenjeno, sistem Windows uporablja znake CRLF za nove vrstice, medtem ko git fast-import pričakuje samo znak LF. Da se izognete temu problemu in zadovoljite git fast-import, morate Rubyju povedati, naj uporablja LF namesto CRLF:

$stdout.binmode

To je vse. Tu je skript v svoji celoti:

#!/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

Če poženete ta skript, boste dobili vsebino, ki je videti nekako takole:

$ 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
(...)

Za zagon uvoznika morate izhodno datoteko preusmeriti v git fast-import, medtem ko ste v mapi Git, kamor želite uvoziti podatke. Lahko ustvarite novo mapo in nato v njej začnete z git init, nato pa zaženete svoj skript:

$ 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
---------------------------------------------------------------------

Kot vidite, vam po uspešnem zaključku poda cel kup statističnih podatkov o doseženem napredku. V tem primeru ste skupaj uvozili 13 objektov za 4 potrditve v 1 vejo. Sedaj lahko za ogled nove zgodovine zaženete 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

In to je to — lep in čist repozitorij Git. Pomembno je opozoriti, da nič ni izvlečeno — na začetku nimate nobene datoteke v svoji delovni mapi. Da bi jih dobili, morate ponastaviti svojo vejo na trenutno stanje veje master:

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

Z orodjem fast-import lahko storite veliko več — obvladovanje različnih načinov, binarnih podatkov, več vej in združevanje, oznak, kazalnikov napredka in še več. V mapi contrib/fast-import izvorne kode Git je na voljo več primerov bolj zapletenih scenarijev.