Git
Chapters ▾ 2nd Edition

9.2 Git in ostali sistemi - Migracija na Git

Migracija na Git

Če imate obstoječo bazo kode v drugem VCS-ju, vendar ste se odločili začeti uporabljati Git, morate migrirati vaš projekt na en ali drug način. Ta razdelek gre skozi nekaj uvoznikov za pogoste sisteme in nato demonstrira, kako razviti vašega lastnega uvoznika. Naučili se boste, kako uvažati podatke iz nekaj največjih profesionalnih uporabljenih sistemov SCM (upravljanje izvorne kode, angl. source code management), kar je glavnina uporabnikov, ki preklapljajo, in ker so visoko kakovostna orodja zanje enostavna za dobiti.

Subversion

Če ste prebrali prejšnji razdelek o uporabi git svn, lahko enostavno uporabite ta navodila za git svn clone repozitorija; nato prenehajte uporabljati strežnik Subversion, potisnite na novi strežnik Git in ga začnite uporabljati. Če želite zgodovino, lahko to dosežete, kakor hitro lahko povlečete podatke iz strežnika Subversion (kar lahko vzame nekaj časa).

Vendar uvažanje ni popolno; in ker bo vzelo nekaj časa, lahko tudi naredite, kakor je prav. Prvi problem so informacije avtorja. V Subversionu ima vsaka oseba, ki potrjuje, uporabnika na sistemu, ki je posnet v informacijah potrditve. Primeri v prejšnjem razdelku prikazujejo na nekaterih mestih schacon, kot je izpis blame in git svn log. Če želite preslikati to na boljše podatke avtorja v Gitu, morate preslikati uporabnike iz Subversiona na avtorje Git. Ustvarite datoteko imenovano users.txt, ki ima to preslikavo v naslednji obliki:

schacon = Scott Chacon <schacon@geemail.com>
selse = Someo Nelse <selse@geemail.com>

Da dobite seznam imen avtorjev, ki jih uporablja SVN, lahko poženete to:

$ svn log --xml --quiet | grep author | sort -u | \
  perl -pe 's/.*>(.*?)<.*/$1 = /'

To generira izpis dnevnika v formatu XML, nato obdrži samo vrstice z informacijami avtorja, opusti duplikate in izpusti značke XML. Očitno to deluje samo na napravi z nameščenimi grep, sort in perl. Nato preusmerite ta izpis v vašo datoteko users.txt, da lahko dodate enakovredne podatke uporabnika Git zraven vsakega vnosa.

Opomba

Če to poskušate na napravi Windows, je to točka, kjer boste naleteli na težave. Microsoft je ponudil nekaj dobrih nasvetov in primerov na https://learn.microsoft.com/en-us/azure/devops/repos/git/perform-migration-from-svn-to-git.

To datoteko lahko ponudite git svn, da bolj točno pomaga preslikati podatke avtorja. Poveste lahko tudi, naj git svn ne vključuje metapodatkov, ki jih Subvestion običajno uvaža s podajanjem --no-metadata k ukazoma clone ali init. Ti metapodatki vključujejo git-svn-id znotraj vsakega sporočila potrditve, ki jih bo Git generiral med vnosom. To lahko napihne vaš dnevnik Git in ga naredi nekoliko nejasnega.

Opomba

Te metapodatke morate obdržati, ko želite prezrcaliti potrditve narejene v repozitoriju Git nazaj na prvotni repozitorij SVN. Če ne želite te sinhronizacije v svojem dnevniku potrditev, lahko tudi spustite parameter --no-metadata.

To naredi, da je vaš ukaz import videti naslednje:

$ git svn clone http://my-project.googlecode.com/svn/ \
      --authors-file=users.txt --no-metadata --prefix "" -s my_project
$ cd my_project

Sedaj morate imeti lepši uvoz Subversiona v vašem direktoriju my_project. Namesto potrditev, ki so videti takole:

commit 37efa680e8473b615de980fa935944215428a35a
Author: schacon <schacon@4c93b258-373f-11de-be05-5f7a86268029>
Date:   Sun May 3 00:12:22 2009 +0000

    fixed install - go to trunk

    git-svn-id: https://my-project.googlecode.com/svn/trunk@94 4c93b258-373f-11de-
    be05-5f7a86268029

so videti takole:

commit 03a8785f44c8ea5cdb0e8834b7c8e6c469be2ff2
Author: Scott Chacon <schacon@geemail.com>
Date:   Sun May 3 00:12:22 2009 +0000

    fixed install - go to trunk

Ne samo, da je polje Author videti veliko boljše, ampak tudi git-svn-id ni več tam.

Sedaj bi morali narediti tudi nekaj čiščenja po uvozu. Za eno stvar, bi morali počistiti čudne reference, ki jih je nastavil git svn. Najprej boste premaknili oznake, da so dejansko oznake namesto čudnih oddaljenih vej in nato boste premaknili preostanek vej, da so lokalne.

Da premaknete oznake, da so ustrezne oznake Git, poženite:

$ for t in $(git for-each-ref --format='%(refname:short)' refs/remotes/tags); do git tag ${t/tags\//} $t && git branch -D -r $t; done

To vzame reference, ki so oddaljene veje in se začnejo z refs/remotes/tags/, ter jih naredi realne (enostavne) oznake.

Naslednje, premaknite preostanek referenc pod refs/remotes, da so lokalne veje:

$ for b in $(git for-each-ref --format='%(refname:short)' refs/remotes); do git branch $b refs/remotes/$b && git branch -D -r $b; done

Lahko se zgodi, da boste videli nekatere dodatne veje, ki nimajo pripone @xxx (kjer je xxx število), medtem ko boste v Subversion videli samo eno vejo. To je dejansko lastnost v Subversionu imenovana »peg-revisions«, kar je nekaj, za kar Git enostavno nima sintaktične protipostavke. Torej, git svn enostavno doda številko različice SVN k imenu veje na isti način, kot bi napisali, da naslovite SVN za peg-revision tiste veje. Če vas peg-revision ne skrbi več, jih enostavno odstranite:

$ for p in $(git for-each-ref --format='%(refname:short)' | grep @); do git branch -D $p; done

Sedaj so vse stare veje prave veje Git in vse stare oznake so prave oznake Git.

Še nekaj je treba počistiti. Na žalost git svn ustvari dodatno vejo imenovano trunk, ki preslika privzete veje Subversiona, vendar pa kaže točka ref trunk na isto mesto kot master. Ker je master idiomatsko bolj Gitov, takole odstranite dodatno vejo:

$ git branch -d trunk

Zadnja stvar, ki jo morate narediti, je dodati vaš novi strežnik Git kot daljavo in potisniti nanj. Tu je primer dodajanja vašega strežnika kot daljavo:

$ git remote add origin git@my-git-server:myrepository.git

Ker želite dodati gor vse vaše veje in oznake, lahko sedaj poženete to:

$ git push origin --all
$ git push origin --tags

Vse vaše veje in oznake bi morale biti na vašem novem strežniku Git z lepim in čistim uvozom.

Mercurial

Ker imate Mercurial in Git precej podobna modela za predstavitev verzij in ker je Git nekoliko bolj prilagodljiv, je pretvorba repozitorija iz Mercurial na Git precej enostavna z uporabo orodja imenovanega »hg-fast-export«, ki ga boste morali kopirati:

$ git clone https://github.com/frej/fast-export.git

Prvi korak pretvorbe je dobiti polni klon repozitorija Mercurial, ki ga želite pretvoriti:

$ hg clone <remote repo URL> /tmp/hg-repo

Naslednji korak je ustvariti datoteko preslikave avtorja. Mercurial je nekoliko bolj odpustljiv kot Git, zaradi česar bo dal polje avtorja za skupke sprememb, torej je to dober čas za čiščenje hiše. Generiranje tega je ukaz ene vrstice v lupini bash:

$ cd /tmp/hg-repo
$ hg log | grep user: | sort | uniq | sed 's/user: *//' > ../authors

To bo vzelo nekaj sekund, odvisno od tega kako dolga je zgodovina vašega projekta in potem bo datoteka /tmp/authors videti nekako takole:

bob
bob@localhost
bob <bob@company.com>
bob jones <bob <AT> company <DOT> com>
Bob Jones <bob@company.com>
Joe Smith <joe@company.com>

V tem primeru je ista oseba (Bob) ustvarila skupek sprememb pod štirimi različnimi imeni, ena izmed njih je dejansko videti v redu in ena od njih bi bila popolnoma neveljavna za potrditev Git. Hg-fast-export nam omogoča to popraviti s spremembo vsake vrstice v pravilo: "<input>"="<output>", kar preslika <input> v <output>. Znotraj nizov <input> in <output> so podprta vsa ubežana zaporedja, ki jih razume Python kodiran string_escape. Če datoteka s preslikavami avtorjev ne vsebuje prilegajočega se <input>, bo ta avtor posredovan naprej Gitu nespremenjen. Če so vsa uporabniška imena videti v redu, te datoteke sploh ne bomo potrebovali. V tem primeru želimo, da je naša datoteka videti takole:

"bob"="Bob Jones <bob@company.com>"
"bob@localhost"="Bob Jones <bob@company.com>"
"bob <bob@company.com>"="Bob Jones <bob@company.com>"
"bob jones <bob <AT> company <DOT> com>"="Bob Jones <bob@company.com>"

Enaka datoteka preslikave se lahko uporabi za preimenovanje vej in oznak, ko ime Mercuriala to ne dovoljuje z Gitom.

Naslednji korak je ustvariti naš novi repozitorij Git in pognati izvozni skript:

$ git init /tmp/converted
$ cd /tmp/converted
$ /tmp/fast-export/hg-fast-export.sh -r /tmp/hg-repo -A /tmp/authors

Zastaviva -r pove hg-fast-export, kje najti repozitorij Mercurial, ki ga želimo pretvoriti, zastavica -A pa mu pove, kje najti datoteko preslikav avtorjev (datoteke preslikav vej in oznak so določene z zastavicama -B in -T flags). Skript prevede skupke sprememb Mercuriala in jih pretvori v skript za Gitovo lastnost »fast-import« (o kateri bomo govorili v podrobnosti nekoliko kasneje). To vzame nekaj časa (čeprav je veliko hitreje, kot bi bilo preko omrežja) in izpis je precej opisen:

$ /tmp/fast-export/hg-fast-export.sh -r /tmp/hg-repo -A /tmp/authors
Loaded 4 authors
master: Exporting full revision 1/22208 with 13/0/0 added/changed/removed files
master: Exporting simple delta revision 2/22208 with 1/1/0 added/changed/removed files
master: Exporting simple delta revision 3/22208 with 0/1/0 added/changed/removed files
[…]
master: Exporting simple delta revision 22206/22208 with 0/4/0 added/changed/removed files
master: Exporting simple delta revision 22207/22208 with 0/2/0 added/changed/removed files
master: Exporting thorough delta revision 22208/22208 with 3/213/0 added/changed/removed files
Exporting tag [0.4c] at [hg r9] [git :10]
Exporting tag [0.4d] at [hg r16] [git :17]
[…]
Exporting tag [3.1-rc] at [hg r21926] [git :21927]
Exporting tag [3.1] at [hg r21973] [git :21974]
Issued 22315 commands
git-fast-import statistics:
---------------------------------------------------------------------
Alloc'd objects:     120000
Total objects:       115032 (    208171 duplicates                  )
      blobs  :        40504 (    205320 duplicates      26117 deltas of      39602 attempts)
      trees  :        52320 (      2851 duplicates      47467 deltas of      47599 attempts)
      commits:        22208 (         0 duplicates          0 deltas of          0 attempts)
      tags   :            0 (         0 duplicates          0 deltas of          0 attempts)
Total branches:         109 (         2 loads     )
      marks:        1048576 (     22208 unique    )
      atoms:           1952
Memory total:          7860 KiB
       pools:          2235 KiB
     objects:          5625 KiB
---------------------------------------------------------------------
pack_report: getpagesize()            =       4096
pack_report: core.packedGitWindowSize = 1073741824
pack_report: core.packedGitLimit      = 8589934592
pack_report: pack_used_ctr            =      90430
pack_report: pack_mmap_calls          =      46771
pack_report: pack_open_windows        =          1 /          1
pack_report: pack_mapped              =  340852700 /  340852700
---------------------------------------------------------------------

$ git shortlog -sn
   369  Bob Jones
   365  Joe Smith

To je večinoma vse, kar je. Vse oznake Mercuriala so bile pretvorjene v oznake Git in veje z zaznamki Mercurial so bili pretvorjeni v veje Git. Sedaj ste pripravljeni potisniti repozitorij na njegov novi strežniški dom:

$ git remote add origin git@my-git-server:myrepository.git
$ git push origin --all

Perforce

Naslednji sistem, ki ga boste pogledali pri uvažanju, je Perforce. Kot smo govorili zgoraj, sta dva načina, da omogočimo Gitu in Perforcu govoriti drug z drugim: git-p4 in Perforce Git Fusion.

Perforce Git Fusion

Git Fusion naredi ta proces precej neboleč. Samo nastavite nastavitve svojega projekta, preslikave uporabnika in veje, ki uporabljajo nastavitveno datoteko (kot je omenjeno v Git Fusion) in klonirajte repozitorij. Git Fusion vas pusti z nečim, kar je videti kot domači repozitorij Git, ki je nato pripravljen za potiskanje na domačega gostitelja Git, če želite. Perforce bi lahko uporabili celo kot vaš gostitelj Git, če želite.

Git-p4

Git-p4 se lahko obnaša tudi kot uvozno orodje. Kot primer, bomo uvozili projekt Jam iz Perforce Public Depot. Da nastavite svojega odjemalca, morate izvoziti okoljsko spremenljivko P4PORT, da kaže na depo Perforce:

$ export P4PORT=public.perforce.com:1666
Opomba

Da sledite zraven, boste za povezavo potrebovali depo Perforce. Za svoj primer bomo uporabljali javni depo na public.perforce.com, vendar lahko uporabite tudi katerikoli drugi depo, do katerega imate dostop.

Poženite ukaz git p4 clone, da uvozite projekt Jam iz strežnika Perforce, kar dobavlja depo in pot projekta ter pot, v katero želite uvoziti projekt:

$ git-p4 clone //guest/perforce_software/jam@all p4import
Importing from //guest/perforce_software/jam@all into p4import
Initialized empty Git repository in /private/tmp/p4import/.git/
Import destination: refs/remotes/p4/master
Importing revision 9957 (100%)

Ta določeni projekt ima samo eno vejo, vendar če imate veje, ki so nastavljive s pogledi vej (ali samo skupkom direktorijev), lahko uporabite zastavico --detect-branches na git p4 clone, da uvozite tudi vse veje projekta. Glejte razdelek Veje za nekoliko več podrobnosti o tem.

Na tej točki ste že skoraj končali. Če greste v direktorij p4import in poženete git log, lahko vidite svoje uvoženo delo:

$ git log -2
commit e5da1c909e5db3036475419f6379f2c73710c4e6
Author: giles <giles@giles@perforce.com>
Date:   Wed Feb 8 03:13:27 2012 -0800

    Correction to line 355; change </UL> to </OL>.

    [git-p4: depot-paths = "//public/jam/src/": change = 8068]

commit aa21359a0a135dda85c50a7f7cf249e4f7b8fd98
Author: kwirth <kwirth@perforce.com>
Date:   Tue Jul 7 01:35:51 2009 -0800

    Fix spelling error on Jam doc page (cummulative -> cumulative).

    [git-p4: depot-paths = "//public/jam/src/": change = 7304]

Vidite lahko, da je git-p4 pustil identifikator v vsakem sporočilu potrditve. V redu je obdržati ta identifikator tam v primeru, če se morate kasneje sklicevati na število spremembe Perforce. Vendar če želite odstraniti identifikator, je to sedaj čas, da naredite — preden začnete delati na novem repozitoriju. Lahko uporabite git filter-branch, da odstranite nize identifikatorja v celoti:

$ git filter-branch --msg-filter 'sed -e "/^\[git-p4:/d"'
Rewrite e5da1c909e5db3036475419f6379f2c73710c4e6 (125/125)
Ref 'refs/heads/master' was rewritten

Če poženete git log, lahko vidite, da so bile vse kontrolne vsote SHA-1 za potrditve spremenjene, vendar nizi git-p4 niso več v sporočilih potrditev:

$ git log -2
commit b17341801ed838d97f7800a54a6f9b95750839b7
Author: giles <giles@giles@perforce.com>
Date:   Wed Feb 8 03:13:27 2012 -0800

    Correction to line 355; change </UL> to </OL>.

commit 3e68c2e26cd89cb983eb52c024ecdfba1d6b3fff
Author: kwirth <kwirth@perforce.com>
Date:   Tue Jul 7 01:35:51 2009 -0800

    Fix spelling error on Jam doc page (cummulative -> cumulative).

Vaš uvoz je sedaj pripravljen za potisk na vaš novi strežnik Git.

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 Notranjost Gita). 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 Primer pravilnika, ki ga uveljavlja Git, 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
Opomba

Č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.

scroll-to-top