-
1. Начало
- 1.1 За Version Control системите
- 1.2 Кратка история на Git
- 1.3 Какво е Git
- 1.4 Конзолата на Git
- 1.5 Инсталиране на Git
- 1.6 Първоначална настройка на Git
- 1.7 Помощна информация в Git
- 1.8 Обобщение
-
2. Основи на Git
-
3. Клонове в Git
-
4. GitHub
-
5. Git инструменти
- 5.1 Избор на къмити
- 5.2 Интерактивно индексиране
- 5.3 Stashing и Cleaning
- 5.4 Подписване на вашата работа
- 5.5 Търсене
- 5.6 Манипулация на историята
- 5.7 Мистерията на командата Reset
- 5.8 Сливане за напреднали
- 5.9 Rerere
- 5.10 Дебъгване с Git
- 5.11 Подмодули
- 5.12 Пакети в Git (Bundling)
- 5.13 Заместване
- 5.14 Credential Storage система
- 5.15 Обобщение
-
6. Настройване на Git
- 6.1 Git конфигурации
- 6.2 Git атрибути
- 6.3 Git Hooks
- 6.4 Примерна Git-Enforced политика
- 6.5 Обобщение
-
7. Git и други системи
- 7.1 Git като клиент
- 7.2 Миграция към Git
- 7.3 Обобщение
-
8. Git на ниско ниво
- 8.1 Plumbing и Porcelain команди
- 8.2 Git обекти
- 8.3 Git референции
- 8.4 Packfiles
- 8.5 Refspec спецификации
- 8.6 Транспортни протоколи
- 8.7 Поддръжка и възстановяване на данни
- 8.8 Environment променливи
- 8.9 Обобщение
-
9. Приложение A: Git в други среди
-
10. Приложение B: Вграждане на Git в приложения
- 10.1 Git от команден ред
- 10.2 Libgit2
- 10.3 JGit
- 10.4 go-git
- 10.5 Dulwich
-
A1. Приложение C: Git команди
- A1.1 Настройки и конфигурация
- A1.2 Издърпване и създаване на проекти
- A1.3 Snapshotting
- A1.4 Клонове и сливане
- A1.5 Споделяне и обновяване на проекти
- A1.6 Инспекция и сравнение
- A1.7 Дебъгване
- A1.8 Patching
- A1.9 Email команди
- A1.10 Външни системи
- A1.11 Административни команди
- A1.12 Plumbing команди
7.2 Git и други системи - Миграция към Git
Миграция към Git
Ако имате наличен код под друга VCS, но искате да използвате Git, ще трябва да мигрирате проекта по някакъв начин. Тази секция разглежда някои популярни импортиращи инструменти и показва как да си направите свой собствен потребителски importer. Ще научим как се импортират данни от няколко от най-големите SCM системи защото те формират болшинството потребители, които мигрират и защото за тях се предлагат висококачествени инструменти.
Subversion
Ако сте прочели секцията за използването на git svn
, може да сте ползвали тези инструкции за да направите git svn clone
към хранилище, след това да сте спрели да използвате Subversion сървъра, да сте публикували в нов Git сървър и да ползвате него занапред.
Ако искате историята, може да направите това толкова бързо колкото може да теглите от Subversion сървъра (което може да отнеме доста време).
Обаче, импортът не е перфектен и понеже може да отнеме време, няма да е лошо да го направите правилно.
Първият проблем е с информацията за автора.
В Subversion, всеки къмитващ автор има акаунт на сървъра, който се записва в къмит информацията.
Примерите в предишната секция показваха schacon
в някои позиции, като наприме blame
изхода и git svn log
.
Ако искате да свържете тази информация с информацията за авторите в Git, ще ви трябва някакъв мапинг между Subversion потребителските акаунти и Git авторите.
Създайте файл users.txt
, който прави мапинга така:
schacon = Scott Chacon <schacon@geemail.com>
selse = Someo Nelse <selse@geemail.com>
За да получите списък с имената на авторите, които SVN използва:
$ svn log --xml --quiet | grep author | sort -u | \
perl -pe 's/.*>(.*?)<.*/$1 = /'
Това генерира лог изхода в XML формат, след това филтрира само редовете с author данни, премахва повторенията и изчиства XML таговете.
Очевидно това ще работи при налични grep
, sort
, и perl
.
След това, пренасочете този изход във файла users.txt
, така че да добавите еквивалентните Git потребителски данни във всеки ред.
Забележка
|
Ако пробвате това на Windows машина, ще срещнете проблем. Microsoft предлага няколко добри съвета и примера на адрес https://docs.microsoft.com/en-us/azure/devops/repos/git/perform-migration-from-svn-to-git. |
Може да подадете този файл на командата git svn
за да я подпомогнете в по-акуратното мапване на данните за авторите.
Можете също да укажете на git svn
да не включва метаданните, които Subversion нормално импортира с флага --no-metadata
към командите clone
или init
.
Метаданните включват git-svn-id
вграден във всяко къмит съобщение, което Git ще генерира по време на импорта.
Това може да задръсти вашия Git лог и да го направи по-неясен.
Забележка
|
Ще имате нужда от метаданните, ако искате да клонирате къмитите направени в Git хранилището обратно в оригиналното SVN такова.
Ако не желаете синхронизацията във вашия commit-лог, може спокойно да пропуснете параметъра |
Така командата import
ще изглежда по следния начин:
$ git svn clone http://my-project.googlecode.com/svn/ \
--authors-file=users.txt --no-metadata --prefix "" -s my_project
$ cd my_project
Сега ще имате по-красив Subversion импорт в директорията my_project
.
Вместо къмити изглеждащи така:
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
те ще се показват като:
commit 03a8785f44c8ea5cdb0e8834b7c8e6c469be2ff2
Author: Scott Chacon <schacon@geemail.com>
Date: Sun May 3 00:12:22 2009 +0000
fixed install - go to trunk
Сега не само полето Author изглежда по-добре, но и git-svn-id
информацията няма да се появява.
Бихте могли да направите и допълнително почистване след импорта.
Добре би било да махнете странните референции, които git svn
настройва.
Първо ще преместите таговете, така че да бъдат реални тагове вместо странни отдалечени референции и след това ще преместите останалите клонове, така че да станат локални.
За да превърнете таговете в реални Git тагове, изпълнете:
$ 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
Това ще вземе референциите представляващи отдалечени клонове и започващи с refs/remotes/tags/
и ще ги конвертира в реални олекотени тагове в Git.
След това, преместваме останалите референции от refs/remotes
като локални клонове:
$ 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
Може да се случи да видите някои допълнителни клонове със суфикс @xxx
(където xxx е число), докато в Subversion виждате само един клон.
Това в действителност е Subversion функция наречена “peg-revisions”, за която Git просто няма синтактичен аналог.
По тази причина git svn
просто добавя svn version номера към името на клона точно по същия начин, по който бихте го написали в svn за да адресирате въпросния peg-revision в този клон.
Ако тези peg-revisions не ви интересуват, може просто да ги премахнете:
$ for p in $(git for-each-ref --format='%(refname:short)' | grep @); do git branch -D $p; done
Сега всички стари клонове са реални Git клонове и всички стари тагове са реални Git такива.
Има още едно последно нещо за коригиране.
За съжаление, git svn
създава допълнителен клон с име trunk
, който съответства на клона по подразбиране на Subversion, но указателят trunk
сочи към същото място, към което и master
.
Понеже master
е нещото, с което сме свикнали с Git, ето как да премахнете допълнителния клон:
$ git branch -d trunk
Последно, добавяме нашия нов Git сървър като remote и публикуваме в него:
$ git remote add origin git@my-git-server:myrepository.git
Понеже искаме всичките ни клонове и тагове да се публикуват, можем да изпълним:
$ git push origin --all
$ git push origin --tags
Сега всичките клонове и тагове ще са в новия ни Git сървър в резултат на един подреден и изчистен импорт.
Mercurial
Mercurial и Git имат подобни модели за представяне на версиите и понеже Git е малко по-гъвкав, конвертирането на хранилище от Mercurial към Git е сравнително лесно чрез инструмента "hg-fast-export", който можете да свалите от:
$ git clone https://github.com/frej/fast-export.git
Първата стъпка е да се сдобием с пълно копие на хранилището на Mercurial, което ще конвертираме:
$ hg clone <remote repo URL> /tmp/hg-repo
Следващата е да създадем author mapping файл.
Mercurial е по-малко рестриктивен от Git по отношение на това какво може да се слага в author полето на changeset-ите, така че това е удобен момент за почистване.
Създаването на списъка отнема една команда в bash
шела:
$ cd /tmp/hg-repo
$ hg log | grep user: | sort | uniq | sed 's/user: *//' > ../authors
Това ще отнеме няколко секунди в зависимост от дължината на историята на проекта, след което файлът /tmp/authors
ще изглежда по подобен начин:
bob
bob@localhost
bob <bob@company.com>
bob jones <bob <AT> company <DOT> com>
Bob Jones <bob@company.com>
Joe Smith <joe@company.com>
В този конкретен пример, един и същи човек (Bob) е създал changeset-и под четири различни имена, едно от които изглежда коректно и друго, което ще е изцяло невалидно за един Git къмит.
Hg-fast-export ни позволява да коригираме това превръщайки всеки ред в правило: "<input>"="<output>"
, мапвайки <input>
към <output>
.
В стринговете <input>
и <output>
, са позволени всички escape последователности, които се поддържат от string_escape
енкодинга на python.
Ако author mapping файлът не съдържа съответен <input>
, този автор ще се изпрати към Git непроменен.
Ако всички потребителски имена изглеждат добре, то въобще няма да се нуждаем от такъв файл.
В този пример искаме файлът ни да изглежда така:
"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>"
Същият вид мапинг файлове може да се използва за преименуване на клонове и тагове, когато дадено Mercurial име не е позволено за Git.
След това е време да създадем новото ни Git хранилище и да пуснем експортиращия скрипт:
$ git init /tmp/converted
$ cd /tmp/converted
$ /tmp/fast-export/hg-fast-export.sh -r /tmp/hg-repo -A /tmp/authors
Флагът -r
инструктира hg-fast-export къде да намери Mercurial хранилището, което ще се конвертира, а -A
указва къде е author-mapping файла (съответно за файловете за клонове и тагове се използват флаговете -B
и -T
).
Скриптът парсва Mercurial changeset-ите и ги конвертира в скрипт за целите на "fast-import" функцията на Git (ще я разгледаме малко по-късно).
Това отнема малко време (но за сметка на това е много по-бързо, в сравнение с времето необходимо, ако трябваше да се прави по мрежата) и изходът е доста подробен:
$ /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
Това е почти всичко. Всички Mercurial тагове са конвертирани в Git тагове и Mercurial клоновете и bookmarks обектите са превърнати в съответните Git клонове. Сега сте готови да публикувате хранилището в сървъра:
$ git remote add origin git@my-git-server:myrepository.git
$ git push origin --all
Bazaar
Bazaar е DVCS инструмент подобен на Git и е сравнително лесно да конвертирате Bazaar хранилище в Git такова.
За целта ви трябва плъгина bzr-fastimport
.
Инсталация на bzr-fastimport плъгина
Процедурата е различна под UNIX операционни системи и Windows.
В първия случай, най-лесният начин е да се инсталира пакета bzr-fastimport
, който ще си изтегли и всички необходими допълнителни изисквания.
Например, под Debian и дериватите му, може да използвате:
$ sudo apt-get install bzr-fastimport
С RHEL варианти командата е:
$ sudo yum install bzr-fastimport
При Fedora от версия 22 има нов пакетен мениджър, dnf:
$ sudo dnf install bzr-fastimport
Ако пакетът не е наличен, може да го инсталирате като плъгин:
$ mkdir --parents ~/.bazaar/plugins # създава необходимите директории за плъгини
$ cd ~/.bazaar/plugins
$ bzr branch lp:bzr-fastimport fastimport # импортира fastimport плъгина
$ cd fastimport
$ sudo python setup.py install --record=files.txt # инсталира плъгина
За да работи този плъгин, ще ви трябва също и fastimport
модула за Python.
Може да проверите дали е наличен и, ако трябва да го инсталирате, така:
$ python -c "import fastimport"
Traceback (most recent call last):
File "<string>", line 1, in <module>
ImportError: No module named fastimport
$ pip install fastimport
Ако не е наличен, можете да го изтеглите от https://pypi.python.org/pypi/fastimport/.
Под Windows, bzr-fastimport
се инсталира автоматично със standalone версията и инсталацията по подразбиране (изберете всички чекбоксове).
На този етап начините за импортиране на Bazaar хранилище се различават според това дали имате само един клон или не.
Проект с единичен клон
Влезте в директорията, съдържаща Bazaar хранилището и инициализирайте Git хранилище:
$ cd /path/to/the/bzr/repository
$ git init
След това можете просто да експортирате Bazaar хранилището и да го конвертирате в Git така:
$ bzr fast-export --plain . | git fast-import
Според размера на проекта, процесът може да отнеме секунди или няколко минути.
Проект с главен клон и работен клон
Можете също да импортирате Bazaar хранилище съдържащо клонове. Да кажем, че имате два клона: един главен (myProject.trunk) и един текущ (myProject.work).
$ ls
myProject.trunk myProject.work
Създайте Git хранилище и влезте в него:
$ git init git-repo
$ cd git-repo
Изтеглете master
клона в Git:
$ bzr fast-export --export-marks=../marks.bzr ../myProject.trunk | \
git fast-import --export-marks=../marks.git
Направете същото и за работния клон:
$ bzr fast-export --marks=../marks.bzr --git-branch=work ../myProject.work | \
git fast-import --import-marks=../marks.git --export-marks=../marks.git
Сега git branch
показва master
и work
клонове.
Проверете логовете за да се уверите, че те са изцяло импортирани и махнете файловете marks.bzr
и marks.git
.
Синхронизиране на индексната област
Колкото и клонове да имате и без значение от метода на импортиране, индексната ви област сега не е синхронизирана с HEAD
. В случая при импортиране на повече от един клон, то това важи и за работната директория.
Ситуацията се разрешава лесно с командата:
$ git reset --hard HEAD
Игнориране на файловете от .bzrignore
Нека сега да видим каква е ситуацията с игнорирането на файлове.
Първото нещо за правене е да преименуваме .bzrignore
в .gitignore
.
Ако файлът .bzrignore
съдържа един или повече редове започващи с "!!" или "RE:", ще трябва да го коригирате и може би дори да създадете няколко .gitignore
файлове с цел да игнорирате точно същото съдържание като Bazaar.
Последно ще създадем къмит, който съдържа тези промени за миграцията:
$ git mv .bzrignore .gitignore
$ # modify .gitignore if needed
$ git commit -am 'Migration from Bazaar to Git'
Изпращане на хранилището към сървъра
Сега можем да публикуваме импортираното хранилище в новия му дом:
$ git remote add origin git@my-git-server:mygitrepository.git
$ git push origin --all
$ git push origin --tags
Новото Git хранилище е готово за ползване.
Perforce
Следващата система, за която ще разгледаме процеса по импортиране на данни, е Perforce. Както видяхме по-рано, Git и Perforce могат да комуникират по два начина: посредством git-p4 и Perforce Git Fusion.
Perforce Git Fusion
Git Fusion прави процеса сравнително безболезнен. Просто задаваме настройките на проекта, мапинга на потребителите и клоновете в конфигурационен файл (виж Git Fusion) и клонираме хранилището. Git Fusion ни изработва резултат подобен на оригинално Git хранилище, което е готово да се публикува на Git сървър. Можем дори да използваме Perforce като Git хост, ако желаем това.
Git-p4
Git-p4 може също да работи като имортиращ инструмент. Като пример, ще импортираме проекта Jam от Perforce публичното депо. За да настроим клиента си, трябва да експортираме P4PORT environment променливата, така че да сочи към Perforce депото:
$ export P4PORT=public.perforce.com:1666
Забележка
|
За да продължите примерните инструкции, се нуждаете от достъп до Perforce депо. Ще използваме публичното такова на адрес public.perforce.com, но може да експериментирате с всяко друго, до което имате достъп. |
Изпълняваме командата git p4 clone
за да импортираме проекта Jam от Perforce сървъра, подавайки ѝ като аргументи депото с пътя до проекта и пътя, в който искаме да го импортираме:
$ 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%)
Този проект има само един клон, но ако имаме клонове конфигурирани с branch изгледи (или само множество от директории), може да използваме флага --detect-branches
за да инструктираме git p4 clone
да импортира всички клонове на проекта.
Вижте Клонове за повече подробности.
На този етап сме почти готови.
Ако влезем в директорията p4import
и изпълним git log
, можем да видим импортираната работа:
$ 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]
Може да забележите как git-p4
е оставила идентификатор във всяко къмит съобщение.
Добре е той да се запази, в случай че по-късно се наложи да се обърнем към Perforce change number-a по някаква причина.
Обаче, ако искаме да махнем идентификатора, сега е времето за това — преди да започнем работа по новото хранилище.
За целта използваме командата git filter-branch
:
$ git filter-branch --msg-filter 'sed -e "/^\[git-p4:/d"'
Rewrite e5da1c909e5db3036475419f6379f2c73710c4e6 (125/125)
Ref 'refs/heads/master' was rewritten
Ако пуснем git log
, ще видим как всички SHA-1 чексуми за къмитите са се променили, но git-p4
стринговете вече отсъстват от къмит съобщенията:
$ 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).
Сега импортираното хранилище е готово да се публикува на Git сървър.
Потребителски импортиращ инструмент
Ако системата ви не е сред дотук разгледаните, може да потърсите импортиращ инструмент в Интернет — качествени такива са налични за CVS, Clear Case, Visual Source Safe, дори за директория от архиви.
Ако никой от тях не работи в конкретния случай или се нуждаете от по-специфичен процес на импортиране, тогава може да използвате git fast-import
.
Тази команда чете прости инструкции от стандартния вход за да записва специфични Git данни.
Много по-лесно е да създавате Git обекти по този начин, вместо да използвате raw Git командите или да се опитвате да записвате raw обекти (вижте Git на ниско ниво за повече информация).
По този начин можете да напишете собствен импортиращ скрипт, който чете необходимата информация от системата, от която импортирате и печата последоветелно инструкции към стандартния изход.
Можете да пуснете програмата и да пренасочите изхода ѝ към git fast-import
.
За демонстрация, ще напишем прост импортиращ инструмент.
Да приемем, че работите в директория current
, редовно архивирате проекта си в отделни директории именувани back_YYYY_MM_DD
според датата, и в един момент решавате да импортирате всичко това в Git хранилище.
Структурата на директориите изглежда така:
$ ls /opt/import_from
back_2014_01_02
back_2014_01_04
back_2014_01_14
back_2014_02_03
current
За да импортираме Git директория, трябва да разгледаме как Git съхранява данните си.
Както може би помните, Git грубо казано е свързан списък от къмит обекти, които сочат към snapshot-и от съдържание.
Всичко, което трябва да укажете на fast-import
е какво са snapshot-ите със съдържание, какви къмит данни сочат към тях и реда, по който идват.
Стратегията е да минаваме по snapshot-ите един след друг и да създаваме къмити със съдържанието на всяка директория свързвайки всеки къмит с предишния.
Както го направихме в Примерна Git-Enforced политика, ще пишем скрипта си на Ruby.
Бихте могли да използвате езика, с който вие се чувствате комфортно — просто трябва да печатате съответната информация на stdout
.
Освен това, ако сте под Windows, трябва да внимавате да не вмъквате carriage returns символи в края на редовете — git fast-import
е чувствителен за това и очаква само line feeds (LF) а не carriage return line feeds (CRLF), което нормално се случва под Windows.
За начало, влизаме в съответната директория и идентифицираме всяка поддиректория, която ще бъде snapshot, който да импортираме. След това, печатаме командите, необходими за експорта. Основният цикъл изглежда по такъв начин:
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
от всяка директория, което взема манифеста и маркировката на предишния snapshot и връща манифеста и маркировката на текущия. По този начин можем да ги свържем коректно.
Маркировката (“Mark”) във 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>'
Сега сме готови да започнем печатането на къмит данните за нашия importer. Началните данни твърдят, че създаваме къмит обект с данни за това в какъв клон е той, следвани от генерираната маркировка, информацията за автора и къмит съобщението и след това — предишния къмит, ако има такъв. Кодът изглежда така:
# 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, размерът на данните за прочитане, нов ред, и накрая, самите данни.
Тук ще използваме helper метод наречен export_data
, защото по-късно се нуждаем от същия формат за указване на съдържанието на файловете.
def export_data(string)
print "data #{string.size}\n#{string}"
end
Остана да укажем файловото съдържание на всеки snapshot.
Това е лесно, защото имаме всеки един в директория — можете да отпечатате командата deleteall
последвана от съдържанието на всеки файл в директорията.
Git след това ще запише съответно всеки snapshot:
puts 'deleteall'
Dir.glob("**/*").each do |file|
next if !File.file?(file)
inline_data(file)
end
Заб.: Понеже много системи представят версиите си като промени между два къмита, fast-import може също така да приема команди с всеки къмит, които да указват кои файлове са били добавени, премахнати или модифицирани и какво е новото съдържание.
Можете да изчислите разликите между snapshot-ите и да подадете само тези данни, но това е по-сложно и може да оставите на 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
дефиниран по-рано, понеже форматът е като за данните на къмит съобщенията.
Последното нещо, което трябва да сторим е да върнем текущата маркировка, така че тя да бъде изпратена към следващата итерация:
return mark
Забележка
|
Под Windows трябва да добавите допълнителна стъпка.
Както вече казахме, Windows използва CRLF за символите за край на ред, докато
|
Това е. Ето целия скрипт:
#!/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
(...)
За да стартираме importer-а, пренасочваме изхода през git fast-import
докато сме в Git директорията, в която искаме да импортираме.
Може да създадем нова директория, да изпълним 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
Получавате чисто ново Git хранилище.
Важно е да отбележим, че нищо не е извлечено на този етап — отначало работната директория е празна.
За да си получим файловете, трябва да върнем клона си там където е master
в момента:
$ ls
$ git reset --hard master
HEAD is now at 3caa046 imported from current
$ ls
README.md main.rb
Можете да правите още много неща с fast-import
инструмента — да обработвате различни режими, двоични данни, множество клонове и сливане, тагове, индикатори за прогрес и т.н.
Има много примери за по-сложни сценарии в директорията contrib/fast-import
в сорс кода на Git.