Git
Chapters ▾ 2nd Edition

9.2 Git və Digər Sistemlər - Git’ə Miqrasiya

Git’ə Miqrasiya

Başqa bir VNS’də mövcud bir kod bazanız varsa, ancaq Git istifadə etməyə qərar verdiyiniz təqdirdə layihənizi bu və ya digər şəkildə köçürməlisiniz. Bu bölmə ümumi sistemlər üçün bəzi idxalçıları nəzərdən keçirir və sonra öz xüsusi idxalatçısının necə inkişaf etdiriləcəyini göstərir. Keçid edən istifadəçilərin əksəriyyətini təşkil etdiyindən və onlar üçün yüksək keyfiyyətli alətlərin gəlməsi asan olduğundan, peşəkarlıqla istifadə olunan bir neçə daha böyük SCM sistemindən məlumatları necə idxal edəcəyinizi öyrənəcəksiniz.

Subversion

git svn istifadəsi ilə bağlı əvvəlki bölümü oxumusunuzsa, bu təlimatları asanlıqla git svn clone deposuna getmək üçün istifadə edə bilərsiniz; sonra, Subversion serverindən istifadəni dayandırın, yeni bir Git serverinə keçin və istifadə etməyə başlayın. Tarixçəni istəsəniz, məlumatı Subversion serverindən çıxara bildiyiniz müddətdə (bir müddət ala bilər) bacara bilərsiniz.

Bununla birlikdə, idxal mükəmməl deyil; və çox uzun çəkəcəyi üçün bunu da edə bilərsiz. İlk problem müəllif məlumatlarıdır. Subversion-da, hər bir iş görən şəxsin sistemdə commit məlumatında qeyd olunan bir istifadəçisi var. Əvvəlki hissədəki nümunələr, blame çıxışı və git svn log kimi bəzi yerlərdə schacon göstərir. Bunu daha yaxşı Git müəllif məlumatlarına uyğunlaşdırmaq istəyirsinizsə, Subversion istifadəçilərindən Git müəlliflərinə qədər bir xəritəyə ehtiyacınız var. Bu istifadəçi şəklində belə bir formatda olan users.txt adlı bir fayl yaradın:

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

SVN-nin istifadə etdiyi müəllif adlarının siyahısını əldə etmək üçün bunu edə bilərsiniz:

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

Gündəlik çıxışı XML formatında yaradır, sonra yalnız müəllif məlumatları olan sətirləri saxlayır, dublikatları atır, XML etiketlərini silir. Aydındır ki, bu yalnız grep, sortperl quraşdırılmış bir maşın üzərində işləyir. Sonra, hər bir girişin yanına ekvivalent Git istifadəçi məlumatlarını əlavə edə bilmək üçün bu çıxışı users.txt faylınıza yönləndirin.

Note

Bunu bir Windows maşınında sınayırsınızsa, problemlə qarşılaşacağınız nöqtə budur. Microsoft,https://docs.microsoft.com/en-us/azure/devops/repos/git/perform-migration-from-svn-to-git[] saytında bəzi yaxşı məsləhətlər və nümunələr verir.

Müəllif məlumatlarını daha dəqiq bir şəkildə göstərməsinə kömək etmək üçün bu faylı git svn-ə təqdim edə bilərsiniz. Ayrıca,git svn-ə Subversion-un normal olaraq idxal etdiyi meta məlumatları daxil etməməsini, --no-metadata-nı clone ya da init əmrinə keçirməklə də deyə bilərsiniz. Metadata, Gitin idxal zamanı meydana gətirəcəyi hər bir mesajın içərisində bir git-svn-id var. Bu, Git log-unuzu şişirə bilər və bir az anlaşılmaz edə bilər.

Note

Git deposundakı commit-ləri orijinal SVN deposuna qaytarmaq istədiyiniz zaman metadatanı saxlamalısınız. Sinxronizasiyanı commit log-da istəmirsinizsə, --no-metadata parametrini əlavə etməyə çəkinməyin.

Bu, import əmrinizi belə göstərir:

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

İndi my_project qovluğunuzda daha yaxşı bir Subversion idxal etməlisiniz. Buna bənzər commit-lər yerinə:

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

they look like this:

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

    fixed install - go to trunk

Müəllif sahəsi həm daha yaxşı görünür, həm də git-svn-id artıq yoxdur. Ayrıca bir az post-import sonrası təmizləmə etməlisiniz. Birincisi, git svn qurduğu qəribə referansları təmizləməlisiniz. Əvvəlcə yazıları qəribə uzaq branch-lardan daha çox həqiqi etiket olduqları üçün daşıyacaqsınız, sonra qalan branch-ları local olduqları üçün köçürəcəksiniz.

Etiketləri uyğun Git etiketi olmaq üçün hərəkət etdirin:

$ 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

Bu, refs/remotes/tags/ ilə başlayan uzaq branch-lar olan referansları alır və onları həqiqi (yüngül) etiketlər edir. Ardından, referansların qalan hissəsini local branch-lar halına gətirmək üçün refs/remotes altına köçürün:

$ 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

Elə ola bilər ki, @xxx (harada ki xxx ədəddir) şəkilçisi olan bəzi əlavə branch-lar görərsiniz, Subversionda isə yalnız bir branch görürsünüz. Bu, əslində Git üçün heç bir sintaktik həmkarı olmayan bir şey olan “peg-revisions” adlı Subversion xüsusiyyətidir. Beləliklə, git svn branch-ın düzəldilməsini həll etmək üçün svn-də yazdığınız şəkildə svn versiya nömrəsini branch-ın adına əlavə edir. Artıq peg-revisions düzəlişləri ilə maraqlanmırsınızsa, sadəcə onları silin:

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

İndi bütün köhnə branch-lar əsl Git branch-larıdır və bütün köhnə etiketlər əsl Git etiketləridir. Təmizləmək üçün son bir şey var. Təəssüf ki, git svn, Subversionun standart branch-ına uyğunlaşan trunk adlı əlavə bir branch yaradır, lakin trunk master ilə eyni yerə işarə edir. master daha idiomatik olaraq Git olduğundan əlavə branch-ı necə silmək olar:

$ git branch -d trunk

Ediləcək son şey yeni Git serverinizi bir remote olaraq əlavə edib ona push etməkdir. Serverinizi məsafədən əlavə etmək üçün bir nümunə:

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

Bütün branch-larınızın və etiketlərinizin yuxarı qalxmasını istədiyiniz üçün indi bunu edə bilərsiniz:

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

Bütün branch-larınız və etiketləriniz təmiz bir idxalda yeni Git serverinizdə olmalıdır.

Mercurial

Mercurial və Git versiyalarını təmsil etmək üçün kifayət qədər oxşar modellərə sahib olduğundan və Git bir az daha çevik olduğundan, bir deponu Mercurial-dan Git-ə çevirmək kifayət qədər sadədir, bunun üçün "hg-fast-export" adlı bir vasitə istifadə etmək lazımdır:

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

Dönüşümün ilk addımı çevirmək istədiyiniz Mercurial deposunun tam bir klonunu əldə etməkdir:

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

Növbəti addım bir müəllif mapping faylı yaratmaqdır. Mercurial, dəyişikliklər üçün müəllif sahəsinə qoyacaqlarına görə Gitdən biraz daha bağışlayır, buna görə də ev təmizləmək üçün yaxşı vaxtdır. Bunu yaratmaq, bash shell-ində bir sətir əmridir:

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

Bu, layihənizin tarixinin nə qədər olacağına görə bir neçə saniyə çəkəcək və bundan sonra /tmp/authors faylı belə görünəcək:

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

Bu nümunədə, eyni şəxs (Bob) biri fərqli görünən və biri Git commiti üçün tamamilə etibarsız olan dörd fərqli ad altında dəyişikliklər yaratdı. Hg-fast-export hər sətri bir qaydaya çevirərək bunu düzəltməyimizə imkan verir: "<input>"="<output>", <input> ilə <output> arasında mapping. <input><output> string-lərinin içərisində, python string_escape kodlaması ilə başa düşülən bütün qaçış ardıcıllığı dəstəklənir. Müəllif mapping faylında uyğun bir <input> yoxdursa, o müəllif dəyişdirilmədən Git-ə göndəriləcək. Bütün istifadəçi adları yaxşı görünürsə, bu fayla heç ehtiyacımız olmayacaq. Bu nümunədə faylımızın belə görünməsini istəyirik:

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

Eyni növ mapping faylı, Mercurial adının Git tərəfindən icazə verilmədiyi zaman branch və etiketlərin adını dəyişdirmək üçün istifadə edilə bilər.

Növbəti addım yeni Git depomuzu yaratmaq və ixrac skriptini çalışdırmaqdır:

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

-r flag-ı hg-fast-export-ə çevirmək istədiyimiz Mercurial deposunu harada tapacağımızı və -A flag-ı isə author-mapping faylını harada tapacağımızı bildirir (branch və etiket mapping faylları müvafiq olaraq -B-T flag-ları ilə təyin olunur). Skript Mercurial dəyişikliklərini təhlil edir və Git’in "fast-import" xüsusiyyəti üçün bir skriptə çevirir (bir az sonra ətraflı şəkildə müzakirə edəcəyik).

Bu bir az çəkir (baxmayaraq ki, şəbəkə üzərində olandan daha çox sürətli olur) və nəticə olduqca açıqdır:

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

Bunun üçün demək olar ki, hamısı var. Mercurial etiketlərin hamısı Git etiketlərinə, Mercurial branch-ları və bookmark-ları Git branch-larına çevrilmişdir. İndi deponu yeni server tərəfindəki evinə aparmağa hazırsınız:

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

Bazaar

Bazaar, Git kimi bir DVCS vasitədir və nəticədə bir Bazaar deposunu Git-ə çevirmək olduqca sadədir. Bunu həyata keçirmək üçün bzr-fastimport pluginini idxal etməlisiniz.

bzr-fastimport pluginn alınması

Fastimport plugininin quraşdırılması proseduru UNIX kimi əməliyyat sistemlərində və Windows-da fərqlidir. Birinci halda, ən sadə, lazım olan bütün asılılıqları quracaq olan bzr-fastimport paketinin quraşdırılmasıdır.

Məsələn, Debian və törəməsi ilə aşağıdakıları edərdiniz:

$ sudo apt-get install bzr-fastimport

RHEL ilə aşağıdakıları edərdiniz:

$ sudo yum install bzr-fastimport

Fedora ilə, 22 buraxılışından bəri yeni paket meneceri dnf:

$ sudo dnf install bzr-fastimport

Paket mövcud deyilsə, onu bir plugin kimi qura bilərsiniz:

$ mkdir --parents ~/.bazaar/plugins     # creates the necessary folders for the plugins
$ cd ~/.bazaar/plugins
$ bzr branch lp:bzr-fastimport fastimport   # imports the fastimport plugin
$ cd fastimport
$ sudo python setup.py install --record=files.txt   # installs the plugin

Bu pluginin işləməsi üçün fastimport Python moduluna ehtiyacınız olacaq. Mövcud olub olmadığını yoxlayıb aşağıdakı əmrlərlə quraşdıra bilərsiniz:

$ python -c "import fastimport"
Traceback (most recent call last):
  File "<string>", line 1, in <module>
ImportError: No module named fastimport
$ pip install fastimport

Mövcud deyilsə, https://pypi.python.org/pypi/fastimport/ ünvanından yükləyə bilərsiniz.

İkinci halda (Windows-da) bzr-fastimport avtomatik olaraq bağımsız versiya və standart quraşdırma ilə quraşdırılır (bütün checkbox-ların işarələnməsinə icazə verin). Yəni bu vəziyyətdə sizin heç bir əlaqəniz yoxdur.

Bu nöqtədə, Bazaar deposun idxal etmək yolu tək bir branch-ınız olduğuna və ya bir neçə branch-ı olan bir depo ilə işlədiyinizə görə fərqlənir.

Tək bir branch ilə layihə

İndi Bazaar deponuz olan qovluqda cd və Git deposunu işə salın:

$ cd /path/to/the/bzr/repository
$ git init

İndi Bazaar deponuzu sadəcə ixrac edə və aşağıdakı əmrdən istifadə edərək Git deposuna çevirə bilərsiniz:

$ bzr fast-export --plain . | git fast-import

Layihənin ölçüsündən asılı olaraq, Git deponuz bir neçə saniyədən bir neçə dəqiqəyə qədər sürətlə qurulur.

Əsas branch və işləyən bir branch olan bir layihə işi

Branch-ları olan bir Bazaar deposunu da idxal edə bilərsiniz. İki branch-nızın olduğunu düşünək: biri əsas branch-ı təmsil edir (myProject.trunk), digəri işləyən branch-dır (myProject.work).

$ ls
myProject.trunk myProject.work

Git deposunu və cd-ni yaradın:

$ git init git-repo
$ cd git-repo

master branch-ını git-ə pull edin:

$ bzr fast-export --export-marks=../marks.bzr ../myProject.trunk | \
git fast-import --export-marks=../marks.git

İşləyən branch-ı Git-ə pull edin:

$ bzr fast-export --marks=../marks.bzr --git-branch=work ../myProject.work | \
git fast-import --import-marks=../marks.git --export-marks=../marks.git

İndi git branch sizə work branch-ını olduğu kimi master branch-ını da göstərir. Tam olduğundan əmin olmaq üçün qeydləri yoxlayın və marks.bzrmarks.git fayllarından qurtulun.

Səhnələşdirmə sahəsinin sinxronlaşdırılması

Sahib olduğunuz branch sayınız və istifadə etdiyiniz idxal metodundan asılı olmayaraq səhnələşdirmə sahəniz HEAD ilə sinxronizasiya edilmir və bir neçə branch-ın idxalı ilə iş qovluğunuz da sinxronizasiya edilmir. Bu vəziyyət asanlıqla aşağıdakı əmrlə həll olunur:

$ git reset --hard HEAD

.bzrignore ilə ignoring edilmiş faylları ignore etmək

İndi ignore etmək üçün fayllara nəzər salaq. Ediləcək ilk şey, .bzrignore adını .gitignore olaraq dəyişdirməkdir. .bzrignore faylı "!!" ilə və ya "RE:" başlayan bir və ya bir neçə sətir varsa, onu dəyişdirməlisiniz və Bazaarın ignore etdiyi faylları ignore etmək üçün bəlkə də bir neçə .gitignore faylı yaratmalısınız.

Nəhayət, migrasiya üçün bu dəyişikliyi ehtiva edən bir commit yaratmalısınız:

$ git mv .bzrignore .gitignore
$ # modify .gitignore if needed
$ git commit -am 'Migration from Bazaar to Git'

Deponuzu serverə göndərmək

Budur! İndi deponu yeni ev serverinə push edə bilərsiniz:

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

Git deponuz istifadəyə hazırdır.

Perforce

İdxal etməyə baxacağınız növbəti sistem Perforce’dir. Yuxarıda müzakirə etdiyimiz kimi, Git və Perforce-un bir-birlərinə danışmalarına icazə verməyin iki yolu var: git-p4 və Perforce Git Fusion.

Perforce Git Fusion

Git Fusion bu prosesi kifayət qədər ağrısız edir. Bir konfiqurasiya sənədindən istifadə edərək layihə parametrlərinizi, istifadəçi xəritələrinizi və branch-larınızı konfiqurasiya etməyiniz kifayətdir (Git Fusion-da müzakirə olunduğu kimi) və deponu klonlayın. Git Fusion, yerli bir Git deposuna bənzər bir şey buraxır, daha sonra istəsəniz local bir Git hostuna push etməyə hazırdır. İstəsəniz, Perforce’ı Git ev sahibi kimi istifadə edə bilərsiniz.

Git-p4

Git-p4 idxal vasitəsi kimi də çıxış edə bilər. Nümunə olaraq Jam layihəsini Perforce Public Depot-dan idxal edəcəyik. Müştərinizi qurmaq üçün, Perforce deposuna işarə etmək üçün P4PORT mühit dəyişkənini ixrac etməlisiniz:

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

Ardından izləmək üçün əlaqə qurmaq üçün bir Perforce deposuna ehtiyacınız var. Nümunələrimiz üçün public.perforce.com saytındakı ictimai depodan istifadə edəcəyik, ancaq daxil olduğunuz hər hansı bir depodan istifadə edə bilərsiniz.

Depo və layihə yolunu və layihəni idxal etmək istədiyiniz yolu təmin edərək Perforce serverindən Jam layihəsini idxal etmək üçün git p4 clone əmrini işə salın:

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

Bu xüsusi layihənin yalnız bir branch-ı var, ancaq branch görünüşləri ilə (və ya yalnız bir qovluq yığını) konfiqurasiya edilmiş branch-larınız varsa, bütün layihə branch-larını da idxal etmək üçün --detect-branches flagını git p4 clone üçün istifadə edə bilərsiniz. Bu barədə bir az daha ətraflı məlumat üçün Branching bölməsinə baxın.

Bu nöqtədə demək olar ki, bitirdiniz. p4import qovluğuna gedib git log-ı işlətsəniz, idxal etdiyiniz işi görə bilərsiniz:

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

Hər git mesajında git-p4 bir identifikator buraxdığını görə bilərsiniz. Perforce dəyişiklik nömrəsinə daha sonra müraciət etməyiniz lazım olduğu təqdirdə bu identifikatoru orada saxlamaq yaxşıdır. Bununla birlikdə, identifikatoru silmək istəsəniz, indi yeni depoda işə başlamazdan əvvəl bunu etməliyik. Tanımlayıcı sətirləri kütləvi şəkildə silmək üçün git filter-branch istifadə edə bilərsiniz:

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

git log-ı işə salırsınızsa, commit-lər üçün bütün SHA-1 hesablama cəmlərinin dəyişdiyini görə bilərsiniz, lakin git-p4 string-ləri artıq commit mesajlarında yoxdur:

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

İdxalınız yeni Git serverinizə keçməyə hazırdır.

TFS

Komandanız mənbə nəzarətini TFVC-dən Git-ə çevirirsə, əldə edə biləcəyiniz ən yüksək əslinə uyğun dönüşümünü istəyərsiniz. Bu o deməkdir ki, interop bölməsi üçün həm git-tfs, həm də git-tf-ni əhatə etdiyimiz halda, yalnız bu hissə üçün git-tfs-i əhatə edəcəyik, çünki git-tfs branch-ları dəstəkləyir və bu git-tf istifadə etmək olduqca çətindir.

Note

Bu, birtərəfli dönüşümdür. Nəticədə Git deposu orijinal TFVC layihəsi ilə əlaqə qura bilməyəcək.

Ediləcək ilk şey istifadəçi adlarının xəritəsidir. TFVC, dəyişikliklər üçün müəllif sahəsinə daxil olanlarla kifayət qədər sərbəstdir, lakin Git insan tərəfindən oxunaqlı bir ad və e-poçt ünvanı istəyir. Bu məlumatı tf əmr sətrini müştərisindən ala bilərsiniz:

PS> tf history $/myproject -recursive > AUTHORS_TMP

Bu, layihənin tarixindəki bütün dəyişiklikləri tutur və İstifadəçi sütununun (ikincisi) məlumatlarını çıxarmaq üçün işləyəcəyimiz AUTHORS_TMP sənədinə qoyuruq. Faylı açın və sütunun hansı işarədə başlayıb bitdiyini və yerini dəyişdirin, aşağıdakı əmr sətrində cut əmrini 11-20 parametrlərini tapılanlarla əvəz edin:

PS> cat AUTHORS_TMP | cut -b 11-20 | tail -n+3 | sort | uniq > AUTHORS

cut əmri hər sətirdən yalnız 11 ilə 20 arasındakı simvolları saxlayır. tail əmri sahə başlıqları və ASCII-art vurğulayan ilk iki sətiri atlayır. Bütün bunların nəticəsi, təkrarlamaları aradan qaldırmaq üçün sortuniq şəkillərinə göndərilir və AUTHORS adlı bir faylda saxlanılır. Növbəti addım manualdır; git-tfs-in bu fayldan səmərəli istifadə etməsi üçün hər sətir bu formatda olmalıdır:

DOMAIN\username = User Name <email@address.com>

Sol tərəfdəki hissə TFVC-dən “User” sahəsidir və bərabər işarəsinin sağ tərəfindəki hissə Git commit-ləri üçün istifadə ediləcək istifadəçi adıdır.

Bu faylı əldə etdikdən sonra bir sonrakı iş, maraqlandığınız TFVC layihəsinin tam bir klonunu yaratmaqdır:

PS> git tfs clone --with-branches --authors=AUTHORS https://username.visualstudio.com/DefaultCollection $/project/Trunk project_git

Bundan sonra, commit mesajların altından git-tfs-id bölmələrini təmizləmək lazımdır. Aşağıdakı əmr bunu edəcəkdir:

PS> git filter-branch -f --msg-filter 'sed "s/^git-tfs-id:.*$//g"' '--' --all

Bu, “git-tfs-id:” ilə başlayan hər hansı bir sətri boşluqla əvəz etmək üçün Git-bash mühitindən sed əmrini istifadə edir və Git bundan sonra ignore qalacaq.

Hamısı bitdikdən sonra yeni bir remote əlavə etməyə, bütün branch-larınızı yuxarıya qaldırmağa və komandanızın Git-dən işə başlamasına hazırsınız.

Xüsusi İdxalçı

Sisteminiz yuxarıda göstərilənlərdən biri deyilsə, onlayn bir idxalçı axtarmalısınız - keyfiyyətli idxalçılar CVS, Clear Case, Visual Source Safe hətta arxivlər siyahısı daxil olmaqla bir çox digər sistemlər üçün mövcuddur.

Bu vasitələrdən heç biri sizin üçün işləmirsə, daha qaranlıq bir alətiniz varsa və ya başqa bir xüsusi idxal prosesinə ehtiyacınız varsa, git fast-import istifadə etməlisiniz. Bu əmr, müəyyən Git məlumatlarını yazmaq üçün stdin-dən sadə təlimatları oxuyur. Git obyektlərini bu şəkildə yaratmaq, xam Git əmrlərini işə salmaqdan və ya xam obyektləri yazmağa çalışmaqdan daha asandır (daha çox məlumat üçün Git’in Daxili İşləri bax). Bu şəkildə, idxal etdiyiniz sistemdən lazımi məlumatları oxuyan və stdout üçün birbaşa təlimatları yazan bir idxal skriptini yaza bilərsiniz. Daha sonra bu proqramı işə sala və nəticəsini git fast-import vasitəsilə ötürə bilərsiniz.

Sürətlə nümayiş etdirmək üçün sadə bir idxalçı yazacaqsınız. Tutaq ki, current-də işləyirsiniz, zaman zaman qovluğu bir time-stamped back_YYYY_MM_DD ehtiyat qovluğuna kopyalayaraq proyektinizi backup edirsiniz və bunu Git-ə idxal etmək istəyirsiniz. Qovluq quruluşunuz belə görünür:

$ ls /opt/import_from
back_2014_01_02
back_2014_01_04
back_2014_01_14
back_2014_02_03
current

Bir Git qovluğunu idxal etmək üçün Gitin məlumatlarını necə saxladığını nəzərdən keçirməlisiniz. Xatırladığınız kimi, Git əsas etibarilə məzmunun bir snapshot-una işarə edən obyektlərin əlaqəli siyahısıdır. Etməli olduğunuz şey məzmunun snpshot-larının nə olduğunu, məlumatları onlara yönəldən şeyləri və daxil olma qaydalarını fast-import etməyinizdir. Strategiyanız snapshot-lardan bir-bir keçmək və hər bir qovluğu əvvəlki ilə əlaqələndirərək hər bir kataloqun məzmunu ilə commit-lər yaratmaq olacaq.

Git-Enforced Siyasət Nümunəsi-də etdiyimiz kimi, bunu Ruby-də yazacağıq, çünki ümumiyyətlə işləmək və oxunması asan olur. Bu nümunəni tanış olduğunuz hər hansı bir şeydə olduqca asanlıqla yaza bilərsiniz - yalnız müvafiq məlumatları stdout-a yazdırmaq lazımdır. Və Windows üzərində işləyirsinizsə, bu, sətirlərinizin sonunda vaqon qayıtmalarını daxil etməməyə xüsusi diqqət yetirməli olduğunuz mənasını verir - git fast-import yalnız Windows istifadə etdiyi daşıyıcı xətləri (CRLF) deyil, yalnız xətt lentləri (LF) istəməyə gəldikdə çox dəqiqdir.

Başlamaq üçün hədəf qovluğunu dəyişəcəksiniz və hər biri commit olaraq idxal etmək istədiyiniz bir snapshot-u olan hər subdirectory-ni müəyyənləşdirəcəksiniz. Hər subdirectory-ni keçib ixrac etmək üçün lazım olan əmrləri çap edəcəksiniz. Əsas dövrəniz belə görünü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

Hər bir qovluğun içərisində əvvəlki snapshot-un manifestini və işarəsini götürən və bunun manifestini və işarəsini qaytaran print_export işlədirsiniz; bu şəkildə onları düzgün bir şəkildə bağlaya bilərsiniz. “Mark” commit-ə verdiyiniz bir identifikator üçün fast-import terminidir; commit-lər yaradarkən, hər birinə digər commit-lərdən əlaqələndirmək üçün istifadə edə biləcəyiniz bir işarə verirsiniz. Beləliklə, print_export metodunda ediləcək ilk şey qovluq adından bir işarənin yaradılmasıdır:

mark = convert_dir_to_mark(dir)

Bunu bir dizin qovluğu yaratmaqla və indeks dəyərini işarə olaraq istifadə etməklə edəcəksiniz, çünki bir işarə tam olmalıdır. Metodunuz belə görünür:

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

Commit-inizin tam bir təmsilçiliyinə sahib olduğunuz üçün, commit meta məlumatları üçün bir tarixə ehtiyacınız var. Tarix qovluq adına ifadə olunduğundan, onu təhlil edəcəksiniz. Print_export falınızdakı növbəti sətir:

date = convert_dir_to_date(dir)

where convert_dir_to_date is defined as:

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

Bu, hər bir qovluğun tarixi üçün bir tam dəyər qaytarır. Hər bir commit üçün lazım olan son meta məlumat parçası, qlobal dəyişəndə sabit kodladığınız ötürmə məlumatlarıdır:

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

İndi idxalçı üçün commit məlumatlarını çap etməyə başlamağa hazırsınız. İlkin məlumatda commit obyektini və hansı branch-da olduğunuzu, sonra yaratdığınız işarəni, məlumat verən məlumatı və commit mesajını, sonra varsa əvvəlki commit-i təyin etdiyiniz bildirilir. Kod belə görünü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

Saat dilimini (-0700) kodlaşdırın, çünki bunu etmək asandır. Başqa bir sistemdən idxal edirsinizsə, zaman zonasını ofset kimi göstərməlisiniz. Commit mesajı xüsusi bir formatda ifadə olunmalıdır:

data (size)\n(contents)

Format məlumat sözündən, oxunacaq məlumatın ölçüsündən, yeni bir sətirdən və nəhayət məlumatdan ibarətdir. Faylın məzmununu daha sonra müəyyənləşdirmək üçün eyni formatı istifadə etməlisiniz, çünki köməkçi bir metod, export_data yaradırsınız:

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

Qalan yalnız hər bir snapshot üçün fayl məzmununun müəyyənləşdirilməsidir. Bu çox asandır, çünki hər biriniz bir kataloqdadır - qovluqdakı hər bir sənədin məzmunu və ardından deleteall əmrini çap edə bilərsiniz. Daha sonra Git hər bir snapshot-u uyğun şəkildə qeyd edəcək:

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

Qeyd: Bir çox sistem öz reviziyalarını bir commit-dən digərinə dəyişiklik kimi qəbul etdiyini düşündüyündən, sürətli idxal da hər bir faylda hansı faylların əlavə olunduğunu, silindiyini və ya dəyişdirildiyini və yeni məzmununun nə olduğunu müəyyənləşdirmək üçün əmrlər götürə bilər. Snapshot-lar arasındakı fərqləri hesablaya bilər və yalnız bu məlumatları verə bilərsiniz, ancaq bunu etmək daha mürəkkəbdir - Git-ə bütün məlumatları verə və onu başa düşməsinə icazə verə bilərsiniz. Bu, məlumatlarınıza daha uyğun gəlirsə, məlumatlarınızı bu şəkildə necə təqdim edəcəyiniz barədə ətraflı məlumat üçün fast-import man page-ini yoxlayın.

Yeni fayl məzmununun siyahısı və ya dəyişdirilmiş bir sənədin yeni məzmunu ilə göstərilməsi formatı belədir:

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

Budur, 644 rejimi (executable fyllarınız varsa, bunun əvəzinə 755-i aşkarlamalı və təyin etməlisiniz) və sətir içərisindəki məzmunu bu sətirdən dərhal sonra sadalayacağınızı söyləyir. inline_data metodunuz belə görünür:

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

Daha əvvəl müəyyənləşdirdiyiniz export_data metodundan təkrar istifadə edirsiniz, çünki bu, commit mesajı məlumatlarınızı təyin etməyinizlə eynidir.

Etməli olduğunuz son şey cari işarəni növbəti təkrarlamaya ötürmək üçün qaytarmaqdır:

return mark
Note

Windows-da işləyirsinizsə, əlavə bir addım əlavə etdiyinizə əmin olmalısınız. Daha əvvəl də qeyd edildiyi kimi, Windows yeni sətir simvolları üçün CRLF istifadə edir, git fast-import isə yalnız LF gözləyir. Bu problemi həll etmək və git fast-import-ı xoşbəxt etmək üçün ruby-ə CRLF əvəzinə LF istifadə etməsini söyləməlisiniz:

$stdout.binmode

Bu qədər. Budur skript bütövlükdə:

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

Bu skripti işlədirsinizsə, buna bənzər bir məzmun əldə edəcəksiniz:

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

İmporter-i çalıştırmak üçün bu çıxışı idxal etmək istədiyiniz Git qovluğunda olarkən git fast-import vasitəsilə ilə ötürün. Yeni bir qovluq yaradıb sonra bir başlanğıc nöqtəsi üçün içərisindəki git init -i işə sala və sonra skriptinizi işə sala bilərsiniz:

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

Gördüyünüz kimi uğurla başa çatdıqda, həyata keçirdikləri barədə bir çox statistika verir. Bu vəziyyətdə, 1 branch-a 4 commit üçün cəmi 13 obyekt idxal etdiniz. İndi yeni tarixçənizi görmək üçün git log-u işə sala bilərsiniz:

$ 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

Budur - gözəl, təmiz bir Git deposu. Heç bir şeyin yoxlanılmadığını qeyd etmək vacibdir - əvvəlcə iş qovluğunda heç bir faylınız yoxdur. Bunları əldə etmək üçün branchnızı master-i n olduğu yerə sıfırlamalısınız:

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

fast-import aləti ilə daha çox şey edə bilərsiniz - fərqli rejimləri, ikili məlumatları, birdən çox branch-ı və birləşmə, etiketlər, irəliləyiş göstəriciləri və s. Daha mürəkkəb ssenarilərin bir sıra nümunələri Git qaynaq kodunun contrib/fast-import qovluğunda mövcuddur.