-
1. Kom igång
- 1.1 Om versionshantering
- 1.2 En kort historik om Git
- 1.3 Vad är Git?
- 1.4 Kommandoraden
- 1.5 Installera Git
- 1.6 Första gången med Git
- 1.7 Få hjälp
- 1.8 Sammanfattning
-
2. Grunderna i Git
- 2.1 Att få tag i ett Git‑kodförråd
- 2.2 Spara ändringar i kodförrådet
- 2.3 Visa incheckningshistoriken
- 2.4 Ångra saker
- 2.5 Arbeta med fjärrkodförråd
- 2.6 Att tagga
- 2.7 Git-alias
- 2.8 Sammanfattning
-
3. Git-grenar
- 3.1 Grenar i korthet
- 3.2 Grundläggande gren- och sammanfogningsarbete
- 3.3 Grenhantering
- 3.4 Arbetsflöden med grenar
- 3.5 Fjärrgrenar
- 3.6 Ombasering
- 3.7 Sammanfattning
-
4. Git på servern
- 4.1 Protokollen
- 4.2 Konfigurera Git på en server
- 4.3 Generera din publika SSH-nyckel
- 4.4 Konfigurera servern
- 4.5 Git-demon
- 4.6 Smart HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 Tredjepartsalternativ
- 4.10 Sammanfattning
-
5. Distribuerat Git
-
6. GitHub
-
7. Git-verktyg
- 7.1 Revisionsurval
- 7.2 Interaktiv köläggning
- 7.3 Lägga undan och städa
- 7.4 Signera ditt arbete
- 7.5 Sökning
- 7.6 Skriva om historik
- 7.7 Nollställning förklarad
- 7.8 Avancerad sammanslagning
- 7.9 Rerere
- 7.10 Felsöka med Git
- 7.11 Undermoduler
- 7.12 Bunta
- 7.13 Ersätt
- 7.14 Lagring av inloggningsuppgifter
- 7.15 Sammanfattning
-
8. Anpassa Git
- 8.1 Git‑konfiguration
- 8.2 Git‑attribut
- 8.3 Git‑krokar
- 8.4 Ett exempel på Git‑upprätthållen policy
- 8.5 Sammanfattning
-
9. Git och andra system
- 9.1 Git som klient
- 9.2 Migrera till Git
- 9.3 Sammanfattning
-
10. Git bakom kulisserna
- 10.1 Lågnivådel och användardel
- 10.2 Git-objekt
- 10.3 Git-referenser
- 10.4 Packfiler
- 10.5 Refspecen
- 10.6 Överföringsprotokoll
- 10.7 Underhåll och dataåterställning
- 10.8 Miljövariabler
- 10.9 Sammanfattning
-
A1. Bilaga A: Git i andra miljöer
- A1.1 Grafiska gränssnitt
- A1.2 Git i Visual Studio
- A1.3 Git i Visual Studio Code
- A1.4 Git i IntelliJ / PyCharm / WebStorm / PhpStorm / RubyMine
- A1.5 Git i Sublime Text
- A1.6 Git i Bash
- A1.7 Git i Zsh
- A1.8 Git i PowerShell
- A1.9 Sammanfattning
-
A2. Bilaga B: Bädda in Git i dina applikationer
- A2.1 Git på kommandoraden
- A2.2 Libgit2
- A2.3 JGit
- A2.4 go-git
- A2.5 Dulwich
-
A3. Bilaga C: Git-kommandon
- A3.1 Uppstart och konfiguration
- A3.2 Skaffa och skapa projekt
- A3.3 Grundläggande ögonblicksbilder
- A3.4 Grening och sammanslagning
- A3.5 Dela och uppdatera projekt
- A3.6 Inspektion och jämförelse
- A3.7 Felsökning
- A3.8 Patchning
- A3.9 E‑post
- A3.10 Externa system
- A3.11 Administration
- A3.12 Lågnivåkommandon
9.2 Git och andra system - Migrera till Git
Migrera till Git
Om du har en befintlig kodbas i ett annat VCS men har bestämt dig för att börja använda Git måste du migrera projektet på ett eller annat sätt. Det här avsnittet går igenom importfunktioner för vanliga system och visar sedan hur du utvecklar en egen anpassad importfunktion. Du lär dig hur du importerar data från flera av de större professionellt använda SCM‑systemen, eftersom de utgör majoriteten av dem som byter och eftersom det är lätt att få tag i högkvalitativa verktyg för dem.
Subversion
Om du läste föregående avsnitt om att använda git svn kan du enkelt använda dessa instruktioner för att klona ett kodförråd med git svn clone, sedan sluta använda Subversion‑servern, skicka till en ny Git‑server och börja använda den.
Om du vill ha historiken kan du göra det så snabbt som du kan dra ut datan ur Subversion‑servern (vilket kan ta tid).
Importen är dock inte perfekt, och eftersom den ändå tar lång tid kan du lika gärna göra det rätt.
Det första problemet är författarinformationen.
I Subversion har varje person som checkar in en användare i systemet som registreras i incheckningsinformationen.
Exemplen i föregående avsnitt visar schacon på vissa ställen, till exempel i blame‑utdata och git svn log.
Om du vill mappa detta till bättre Git‑författardata behöver du en mappning från Subversion‑användare till Git‑författare.
Skapa en fil som heter users.txt med denna mappning i ett format som ser ut så här:
schacon = Scott Chacon <schacon@geemail.com>
selse = Someo Nelse <selse@geemail.com>
För att få en lista över de författarnamn som SVN använder kan du köra detta:
$ svn log --xml --quiet | grep author | sort -u | \
perl -pe 's/.*>(.*?)<.*/$1 = /'
Detta genererar loggutdata i XML‑format, behåller bara raderna med författarinformation, tar bort dubbletter och rensar bort XML‑taggarna.
Detta fungerar förstås bara på en maskin med grep, sort och perl installerade.
Omdirigera sedan utdata till din users.txt‑fil så att du kan lägga till motsvarande Git‑användardata bredvid varje post.
|
Notera
|
Om du provar detta på en Windows‑maskin är det här punkten där du kommer att stöta på problem. Microsoft har lagt upp några bra råd och exempel på https://learn.microsoft.com/en-us/azure/devops/repos/git/perform-migration-from-svn-to-git. |
Du kan ge denna fil till git svn för att hjälpa den mappa författardata mer exakt.
Du kan också säga åt git svn att inte inkludera metadata som Subversion normalt importerar genom att skicka --no-metadata till kommandot clone eller init.
Metadata innehåller en git-svn-id i varje incheckningsmeddelande som Git skapar under importen.
Det kan göra din Git‑logg onödigt stor och svårare att läsa.
|
Notera
|
Du behöver behålla metadata om du vill spegla incheckningar som görs i Git‑kodförrådet tillbaka till det ursprungliga SVN‑kodförrådet.
Om du inte vill ha synkroniseringsspår i din incheckningslogg kan du hoppa över parametern |
Det gör att ditt import‑kommando ser ut så här:
$ git svn clone http://my-project.googlecode.com/svn/ \
--authors-file=users.txt --no-metadata --prefix "" -s my_project
$ cd my_project
Nu bör du ha en snyggare Subversion‑import i din my_project‑katalog.
I stället för incheckningar som ser ut så här:
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
ser de ut så här:
commit 03a8785f44c8ea5cdb0e8834b7c8e6c469be2ff2
Author: Scott Chacon <schacon@geemail.com>
Date: Sun May 3 00:12:22 2009 +0000
fixed install - go to trunk
Inte bara ser Author‑fältet mycket bättre ut, utan git-svn-id finns inte längre heller.
Du bör också göra lite efterimport‑städning.
För det första bör du städa upp de märkliga referenser som git svn skapade.
Först flyttar du taggarna så att de blir riktiga taggar i stället för udda fjärrgrenar, och därefter flyttar du resten av grenarna så att de blir lokala.
För att flytta taggarna till riktiga Git‑taggar, kör:
$ 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
Detta tar referenserna som var fjärrgrenar och som började med refs/remotes/tags/ och gör dem till riktiga (lättviktiga) taggar.
Flytta sedan resten av referenserna under refs/remotes till lokala grenar:
$ 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
Det kan hända att du ser några extra grenar som slutar med @xxx (där xxx är ett nummer), trots att du i Subversion bara ser en gren.
Det här är en Subversion‑funktion som kallas “peg‑revisioner”, vilket är något som Git helt saknar en syntaktisk motsvarighet till.
Därför lägger git svn helt enkelt till SVN‑versionsnumret i grennamnet på samma sätt som du hade skrivit det i SVN för att adressera peg‑revisionen för den grenen.
Om du inte längre bryr dig om peg‑revisionerna kan du bara ta bort dem:
$ for p in $(git for-each-ref --format='%(refname:short)' | grep @); do git branch -D $p; done
Nu är alla de gamla grenarna riktiga Git‑grenar och alla de gamla taggarna riktiga Git‑taggar.
Det finns en sista sak att städa upp.
Tyvärr skapar git svn en extra gren som heter trunk, som mappar till Subversions standardgren, men referensen trunk pekar på samma ställe som master.
Eftersom master är mer idiomatiskt Git gör du så här för att ta bort den extra grenen:
$ git branch -d trunk
Sista steget är att lägga till din nya Git‑server som ett fjärrkodförråd och skicka dit. Så här lägger du till din server som fjärrkodförråd:
$ git remote add origin git@my-git-server:myrepository.git
Eftersom du vill att alla dina grenar och taggar ska skickas upp kan du nu köra detta:
$ git push origin --all
$ git push origin --tags
Alla dina grenar och taggar ska nu finnas på din nya Git‑server i en fin, ren import.
Mercurial
Eftersom Mercurial och Git har ganska liknande modeller för att representera versioner, och eftersom Git är lite mer flexibelt, är det ganska okomplicerat att konvertera ett kodförråd från Mercurial till Git med hjälp av verktyget "hg-fast-export", som du behöver ha en kopia av:
$ git clone https://github.com/frej/fast-export.git
Det första steget i konverteringen är att göra en fullständig klon av Mercurial‑kodförrådet du vill konvertera:
$ hg clone <remote repo URL> /tmp/hg-repo
Nästa steg är att skapa en fil för författarmappning.
Mercurial är lite mer förlåtande än Git när det gäller vad som hamnar i author‑fältet för ändringsuppsättningar, så detta är ett bra tillfälle att städa upp.
Detta genereras med ett kort enradskommando i ett bash‑skal:
$ cd /tmp/hg-repo
$ hg log | grep user: | sort | uniq | sed 's/user: *//' > ../authors
Detta tar några sekunder, beroende på hur lång historiken i ditt projekt är, och därefter ser filen /tmp/authors ut ungefär så här:
bob
bob@localhost
bob <bob@company.com>
bob jones <bob <AT> company <DOT> com>
Bob Jones <bob@company.com>
Joe Smith <joe@company.com>
I det här exemplet har samma person (Bob) skapat ändringsuppsättningar under fyra olika namn, varav ett ser korrekt ut och ett skulle vara helt ogiltigt som Git‑incheckning.
Hg-fast-export låter oss fixa detta genom att omvandla varje rad till en regel: "<input>"="<output>", som mappar ett <input> till ett <output>.
I strängarna <input> och <output> stöds alla escape‑sekvenser som Pythons string_escape‑kodning förstår.
Om författarmappningsfilen inte innehåller ett matchande <input> skickas författaren vidare till Git oförändrad.
Om alla användarnamn ser bra ut behöver vi inte filen alls.
I det här exemplet vill vi att filen ska se ut så här:
"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>"
Samma typ av mappningsfil kan användas för att byta namn på grenar och taggar när Mercurial‑namnet inte tillåts av Git.
Nästa steg är att skapa vårt nya Git‑kodförråd och köra exportskriptet:
$ git init /tmp/converted
$ cd /tmp/converted
$ /tmp/fast-export/hg-fast-export.sh -r /tmp/hg-repo -A /tmp/authors
Flaggan -r talar om för hg-fast-export var Mercurial‑kodförrådet vi vill konvertera finns, och flaggan -A talar om var författarmappningsfilen finns (mappningsfiler för grenar och taggar anges med flaggorna -B respektive -T).
Skriptet tolkar Mercurial‑ändringsuppsättningar och konverterar dem till ett skript för Gits funktion "fast-import" (som vi går igenom i detalj lite senare).
Det här tar en stund (även om det är mycket snabbare än över nätverket), och utskriften är ganska omfattande.
$ /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
Det är i stort sett allt. Alla Mercurial‑taggar har konverterats till Git‑taggar, och Mercurial‑grenar och bokmärken har konverterats till Git‑grenar. Nu är du redo att skicka kodförrådet till sitt nya hem på servern:
$ git remote add origin git@my-git-server:myrepository.git
$ git push origin --all
Perforce
Nästa system att importera från är Perforce. Som vi diskuterade ovan finns det två sätt att låta Git och Perforce prata med varandra: git-p4 och Perforce Git Fusion.
Perforce Git Fusion
Git Fusion gör den här processen ganska smärtfri. Konfigurera bara dina projektinställningar, användarmappningar och grenar med en konfigurationsfil (som diskuterats i Git Fusion), och klona kodförrådet. Git Fusion lämnar dig med vad som ser ut som ett inbyggt Git‑kodförråd, som sedan är redo att skickas upp till en vanlig Git‑värd om du vill. Du kan till och med använda Perforce som Git‑värd om du vill det.
Git-p4
Git-p4 kan också fungera som importverktyg.
Som exempel importerar vi Jam‑projektet från Perforce Public Depot.
För att sätta upp din klient måste du exportera miljövariabeln P4PORT så att den pekar på Perforce‑depån:
$ export P4PORT=public.perforce.com:1666
|
Notera
|
För att hänga med behöver du en Perforce‑depå att ansluta till. Vi använder den publika depån på public.perforce.com i våra exempel, men du kan använda vilken depå du har åtkomst till. |
Kör kommandot git p4 clone för att importera Jam‑projektet från Perforce‑servern och ange depån, projektsökvägen och sökvägen dit du vill importera projektet:
$ 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%)
Detta projekt har bara en gren, men om du har grenar som är konfigurerade med gren‑views (eller bara en uppsättning kataloger) kan du använda flaggan --detect-branches till git p4 clone för att importera alla projektets grenar också.
Se Grenar för lite mer detaljer om detta.
Nu är du nästan klar.
Om du går till katalogen p4import och kör git log kan du se ditt importerade arbete:
$ 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]
Du kan se att git-p4 har lämnat en identifierare i varje incheckningsmeddelande.
Det är okej att behålla den identifieraren där om du behöver hänvisa till Perforce‑ändringsnumret senare.
Men om du vill ta bort identifieraren är det nu du ska göra det – innan du börjar arbeta i det nya kodförrådet.
Du kan använda git filter-branch för att ta bort identifierarsträngarna i ett enda svep:
$ git filter-branch --msg-filter 'sed -e "/^\[git-p4:/d"'
Rewrite e5da1c909e5db3036475419f6379f2c73710c4e6 (125/125)
Ref 'refs/heads/master' was rewritten
Om du kör git log ser du att alla SHA‑1‑kontrollsummor för incheckningarna har ändrats, men git-p4‑strängarna syns inte längre i incheckningsmeddelandena:
$ 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).
Din import är redo att skickas upp till din nya Git‑server.
Ett anpassat importskript
Om ditt system inte är ett av ovanstående bör du leta efter ett importverktyg på nätet – det finns importverktyg av god kvalitet för många andra system, inklusive CVS, ClearCase, Visual SourceSafe och till och med kataloger med arkiv.
Om inget av dessa verktyg fungerar för dig, om du har ett mer obskyrt verktyg eller om du behöver en mer anpassad importprocess, bör du använda git fast-import.
Detta kommando läser enkla instruktioner från stdin för att skriva specifik Git‑data.
Det är mycket enklare att skapa Git‑objekt på det här sättet än att köra de råa Git‑kommandona eller försöka skriva de råa objekten (se Git bakom kulisserna för mer information).
På så vis kan du skriva ett importskript som läser den nödvändiga informationen ur systemet du importerar från och skriver tydliga instruktioner till standardutmatningen.
Sedan kan du köra programmet och skicka dess utdata genom git fast-import.
För att snabbt demonstrera detta skriver du ett enkelt importskript.
Anta att du arbetar i current, att du säkerhetskopierar projektet genom att med jämna mellanrum kopiera katalogen till en tidsstämplad säkerhetskopia back_YYYY_MM_DD, och att du vill importera detta till Git.
Din katalogstruktur ser ut så här:
$ ls /opt/import_from
back_2014_01_02
back_2014_01_04
back_2014_01_14
back_2014_02_03
current
För att importera en Git‑katalog behöver du se över hur Git lagrar sin data.
Som du kanske minns är Git i grunden en länkad lista av incheckningsobjekt som pekar på en ögonblicksbild av innehåll.
Allt du behöver göra är att tala om för fast-import vilka ögonblicksbilder av innehåll som finns, vilken incheckningsdata som pekar på dem och i vilken ordning de kommer.
Din strategi blir att gå igenom ögonblicksbilderna en i taget och skapa incheckningar med innehållet i varje katalog, där varje incheckning länkar tillbaka till den föregående.
Som vi gjorde i Ett exempel på Git‑upprätthållen policy skriver vi detta i Ruby, eftersom det är det vi vanligtvis arbetar med och det brukar vara lätt att läsa.
Du kan skriva exemplet ganska enkelt i vilket språk du är bekväm med – det behöver bara skriva rätt information till standardutmatningen.
Och om du kör på Windows betyder det att du måste vara extra noga med att inte introducera vagnreturer i slutet av dina rader – git fast-import vill absolut bara ha radmatningar (LF) och inte de vagnretur‑radmatningar (CRLF) som Windows använder.
Till att börja med byter du till målkatalogen och identifierar varje underkatalog, som var och en är en ögonblicksbild du vill importera som en incheckning. Du byter till varje underkatalog och skriver ut kommandona som behövs för att exportera den. Din grundläggande huvudloop ser ut så här:
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
Du kör print_export inuti varje katalog, som tar manifestet och markeringen för den föregående ögonblicksbilden och returnerar manifestet och markeringen för den här; på så sätt kan du länka dem korrekt.
“Mark” är fast-import‑termen för en identifierare du ger en incheckning; när du skapar incheckningar ger du var och en en markering som du kan använda för att länka till den från andra incheckningar.
Så det första du gör i din print_export‑metod är att generera en markering från katalognamnet:
mark = convert_dir_to_mark(dir)
Du gör detta genom att skapa en array av kataloger och använda indexvärdet som markering, eftersom en markering måste vara ett heltal. Din metod ser ut så här:
$marks = []
def convert_dir_to_mark(dir)
if !$marks.include?(dir)
$marks << dir
end
($marks.index(dir) + 1).to_s
end
Nu när du har en heltalsrepresentation av din incheckning behöver du ett datum för incheckningsmetadata.
Eftersom datumet uttrycks i katalognamnet plockar du ut det därifrån.
Nästa rad i din print_export‑fil är:
date = convert_dir_to_date(dir)
där convert_dir_to_date är definierad så här:
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
Detta returnerar ett heltal för datumet för varje katalog. Den sista metainformationen du behöver för varje incheckning är incheckardata, som du hårdkodar i en global variabel:
$author = 'John Doe <john@example.com>'
Nu är du redo att börja skriva ut incheckningsdata för ditt importskript. Den inledande informationen anger att du definierar ett incheckningsobjekt och vilken gren det ligger på, följt av markeringen du har genererat, incheckarinformationen och incheckningsmeddelandet, och sedan den föregående incheckningen om det finns någon. Koden ser ut så här:
# 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
Du hårdkodar tidszonen (-0700) eftersom det är enkelt att göra. Om du importerar från ett annat system måste du ange tidszonen som ett offset. Incheckningsmeddelandet måste uttryckas i ett särskilt format:
data (size)\n(contents)
Formatet består av ordet data, storleken på datan som ska läsas, en radbrytning och till sist datan.
Eftersom du behöver använda samma format för att ange filinnehållet senare skapar du en hjälpfunktion, export_data:
def export_data(string)
print "data #{string.size}\n#{string}"
end
Allt som återstår är att ange filinnehållet för varje ögonblicksbild.
Det är enkelt eftersom du har varje ögonblicksbild i en katalog – du kan skriva ut kommandot deleteall följt av innehållet i varje fil i katalogen.
Git registrerar sedan varje ögonblicksbild på rätt sätt:
puts 'deleteall'
Dir.glob("**/*").each do |file|
next if !File.file?(file)
inline_data(file)
end
Obs: Eftersom många system ser sina revisioner som förändringar från en incheckning till en annan kan fast-import också ta emot kommandon med varje incheckning som anger vilka filer som har lagts till, tagits bort eller ändrats och vad det nya innehållet är.
Du skulle kunna beräkna skillnaderna mellan ögonblicksbilderna och bara tillhandahålla denna data, men det är mer komplext – du kan lika gärna ge Git all data och låta den lista ut det.
Om detta passar dina data bättre, se manualsidan för fast-import för detaljer om hur du tillhandahåller data på detta sätt.
Formatet för att lista det nya filinnehållet eller ange en modifierad fil med det nya innehållet är följande:
M 644 inline path/to/file
data (size)
(file contents)
Här är 644 läget (om du har körbara filer behöver du upptäcka och ange 755 i stället), och inline säger att du listar innehållet direkt efter denna rad.
Din inline_data‑metod ser ut så här:
def inline_data(file, code = 'M', mode = '644')
content = File.read(file)
puts "#{code} #{mode} inline #{file}"
export_data(content)
end
Du återanvänder metoden export_data som du definierade tidigare, eftersom den är densamma som när du angav data för incheckningsmeddelandet.
Det sista du behöver göra är att returnera den aktuella markeringen så att den kan skickas vidare till nästa varv:
return mark
|
Notera
|
Om du kör på Windows behöver du se till att lägga till ett extra steg.
Som nämnts tidigare använder Windows CRLF för radslut medan
|
Det var allt. Här är skriptet i sin helhet:
#!/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
Om du kör detta skript får du innehåll som ser ut ungefär så här:
$ 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
(...)
För att köra importskriptet skickar du denna utdata genom git fast-import medan du står i Git‑katalogen du vill importera till.
Du kan skapa en ny katalog och sedan köra git init i den som startpunkt, och sedan köra ditt 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
---------------------------------------------------------------------
Som du ser, när den slutförs korrekt ger den dig en massa statistik över vad den åstadkom.
I det här fallet importerade du totalt 13 objekt för 4 incheckningar till en gren.
Nu kan du köra git log för att se din nya historik:
$ 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
Där har du det – ett snyggt, rent Git‑kodförråd.
Det är viktigt att notera att inget är utcheckat – du har inga filer i arbetskatalogen till att börja med.
För att få dem måste du återställa din gren till där master är nu:
$ ls
$ git reset --hard master
HEAD is now at 3caa046 imported from current
$ ls
README.md main.rb
Du kan göra mycket mer med verktyget fast-import – hantera olika lägen, binärdata, flera grenar och sammanslagningar, taggar, förloppsindikatorer och mer.
Flera exempel på mer komplexa scenarier finns i katalogen contrib/fast-import i Gits källkod.