Git
Chapters ▾ 2nd Edition

10.2 Git’in Daxili İşləri - Git Obyektləri

Git Obyektləri

Git, məzmuna yönəldilə bilən bir sistemdir. Əla. Bəs bunun mənası nədir? Bu, Git-in mərkəzində sadə bir əsas dəyər məlumat deposu olduğu deməkdir. Bunun mənası budur ki, Git deposuna hər cür məzmun daxil edə bilərsiniz, bunun üçün Git bu məzmunu almaq üçün daha sonra istifadə edə biləcəyiniz unikal bir key-i geri qaytaracaqdır.

Nümayiş olaraq, bəzi məlumatları götürən, .git/objects qovluğunda (object database) saxlayan və indi buna istinad edən unikal key-i verən git hash-object plumbing əmrinə baxaq.

Əvvəlcə yeni bir Git deposunu işə salırsınız və objects qovluğunda (əvvəlcədən) heç bir şey olmadığını yoxlayırsınız:

$ git init test
Initialized empty Git repository in /tmp/test/.git/
$ cd test
$ find .git/objects
.git/objects
.git/objects/info
.git/objects/pack
$ find .git/objects -type f

Git, objects qovluğunu işə saldı və içərisində packinfo alt qovluqlarını yaratdı, lakin orada normal fayllar yoxdur. İndi yeni bir məlumat obyekti yaratmaq və onu yeni Git verilənlər bazasında manual şəkildə saxlamaq üçün git hash-object-dən istifadə edək:

$ echo 'test content' | git hash-object -w --stdin
d670460b4b4aece5915caf5c68d12f560a9fe3e4

Ən sadə şəkildə, git hash-object verdiyiniz məzmunu alacaq və would-nin Git verilənlər bazasında saxlamaq üçün istifadə etdiyi unikal key-i qaytaracaqdı. Daha sonra -w seçimi əmrə key-i sadəcə qaytarmağı deyil, həmin obyekti verilənlər bazasına yazmağı əmr edir. Nəhayət, --stdin seçimi, git hash-object-ə məzmunun stdin-dən işlənməsinin alınmasını deyir; Əks təqdirdə, əmr, məzmunun daxil olduğu əmrin sonunda bir fayl adı arqumentinin istifadə olunacağını gözləyir.

Yuxarıda göstərilən əmrdən çıxan nəticə 40 simvollu bir cəmi hash-dir. Bu SHA-1 hash-ı - saxladığınız məzmunun cəmi və üstəlik, bir azdan öyrənəcəyiniz bir başlıqdır. İndi Git-in məlumatlarınızı necə saxladığını görə bilərsiniz:

$ find .git/objects -type f
.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4

Əgər siz yenidən objects qovluğunu araşdırırsınızsa, indi o yeni məzmun üçün özündə bir fayl daxil etdiyini görə bilərsiniz. Git əvvəlcə məzmunu onun SHA-1 nəzarət cəmi və onun başlığı ilə adlandırılan bir məzmuna görə tək bir fayl kimi saxlayır. Alt qovluq SHA-1-in ilk 2 simvolu ilə adlanır və fayl adı qalan 38 simvoldur.

Verilənlər bazanızda bir məzmun olduqdan sonra, bu məzmunu git cat-file əmri ilə yoxlaya bilərsiniz. Bu əmr, Git obyektlərini yoxlamaq üçün bir növ İsveçrə ordusu bıçağıdır. -P, cat-file keçid əmrinə əvvəlcə məzmunun növünü müəyyənləşdirməyi, sonra isə uyğun şəkildə göstərməyi əmr edir:

$ git cat-file -p d670460b4b4aece5915caf5c68d12f560a9fe3e4
test content

İndi Git-ə məzmun əlavə edib yenidən geri pull edə bilərsiniz. Bunu fayllardakı məzmunu ilə də edə bilərsiniz. Məsələn, bir faylda bəzi sadə versiya nəzarəti edə bilərsiniz. Əvvəlcə yeni bir fayl yaradın və məzmununu verilənlər bazanıza yazın:

$ echo 'version 1' > test.txt
$ git hash-object -w test.txt
83baae61804e65cc73a7201a7252750c76066a30

Sonra bir neçə yeni məzmunu fayla yazın və yenidən qeyd edin:

$ echo 'version 2' > test.txt
$ git hash-object -w test.txt
1f7a7a472abf3dd9643fd615f6da379c4acb3e3a

İndi obyekt verilənlər bazanızda bu yeni faylın hər iki versiyası da var (orada saxladığınız ilk məzmun kimi):

$ find .git/objects -type f
.git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a
.git/objects/83/baae61804e65cc73a7201a7252750c76066a30
.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4

Bu nöqtədə, həmin test.txt faylının yerli nüsxəsini silə bilərsiniz, sonra ilk verilənlər bazasını saxladığınız obyekt verilənlər bazasından almaq üçün Git-dən istifadə edin:

$ git cat-file -p 83baae61804e65cc73a7201a7252750c76066a30 > test.txt
$ cat test.txt
version 1

Və ya ikinci versiya:

$ git cat-file -p 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a > test.txt
$ cat test.txt
version 2

Ancaq faylınızın hər bir versiyası üçün SHA-1 key-ni xatırlamaq praktik deyil; üstəlik, sisteminizdə fayl adını deyil, yalnız məzmunu saxlayırsınız. Bu obyekt növünə blob deyilir.

Git-dən SHA-1 key-ni nəzərə alaraq Git-dəki`git cat-file –t` ilə hər hansı bir obyektin növünü sizə izah etməsini istəyə bilərsiniz:

$ git cat-file -t 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a
blob

Ağac Obyektləri

Növbəti araşdıracağımız Git obyekt növü, fayl adının saxlanılması problemini həll edən və eyni zamanda bir qrup faylları birlikdə saxlamağınıza imkan verən tree-dir. Git, məzmunu UNIX fayl sisteminə bənzər, lakin biraz sadələşdirilmiş şəkildə saxlayır.

Bütün məzmun ağac və blob obyektləri, UNIX qovluq girişlərinə uyğun olan ağaclar və inodes və ya fayl məzmununa az və ya çox uyğun gələn blob-larla saxlanılır. Tək bir ağac obyekti, hər biri əlaqəli rejimi, növü və fayl adı ilə bir blob və ya alt ağacın SHA-1 hash olan bir və ya daha çox giriş daxil edir. Məsələn, bir layihədəki ən son ağac belə görünə bilər:

$ git cat-file -p master^{tree}
100644 blob a906cb2a4a904a152e80877d4088654daad0c859      README
100644 blob 8f94139338f9404f26296befa88755fc2598c289      Rakefile
040000 tree 99f1a6d12cb4b6f19c8655fca46c3ecf317074e0      lib

Master^{tree} sintaksisiniz, master branch-nızdakı sonuncu işarə ilə göstərilən ağac obyektini müəyyənləşdirir. Diqqət yetirin ki, lib alt kataloqu blok deyil, o başqa bir ağaca işarə edir:

$ git cat-file -p 99f1a6d12cb4b6f19c8655fca46c3ecf317074e0
100644 blob 47c6340d6459e05787f644c2447d2595f5d3a54b      simplegit.rb
Note

Hansı shell-dən istifadə etdiyinizə görə, master^{tree} sintaksisini istifadə edərkən səhvlərlə qarşılaşa bilərsiniz.

Windows-dakı CMD-də ^ işarəsi qaçmaq üçün istifadə olunur, buna görə bunun qarşısını almaq üçün onu ikiqat artırmalısınız: git cat-file -p master^^{tree}". PowerShell istifadə edərkən, parametrin səhv təhlil edilməməsi üçün {} simvoldan istifadə olunan parametrlərə istinad edilməlidir: `git cat-file -p 'master^{tree}'. ZSH istifadə edirsinizsə, ^ işarəsi globbing üçün istifadə olunur, buna görə bütün ifadəni dırnaq işarələrinə əlavə etməlisiniz: `git cat-file -p 'master^{tree}".

Konseptual olaraq, Gitin saxladığı məlumatlar belə bir şeyə bənzəyir:

Simple version of the Git data model
Figure 149. Git data modelinin sadə versiyası

Öz ağacınızı olduqca asanlıqla yarada blərsiniz. Git normal olaraq quruluş sahənizin və ya indeksinizin vəziyyətini götürərək ondan bir sıra ağac obyektlərini yazaraq bir ağac yaradır Beləliklə, bir ağac obyekti yaratmaq üçün əvvəlcə bəzi sənədləri quraraq bir indeks qurmalısınız. Single entry – ilə sadə indeks qurmaq üçün test.txt faylınızın ilk versiyası — git update-index plumbing əmrindən istifadə edə bilərsiniz. Bu əmrdən süni şəkildə yeni bir səhnə sahəsinə test.txt faylının əvvəlki versiyasını əlavə etmək üçün istifadə edirsiniz. Fayl hazırlama sahənizdə hələ olmadığı üçün --add seçimini keçməlisiniz (hələ də staging sahəniz qurulmayıbsa) və əlavə etdiyiniz fayl olmadığı üçün --cacheinfo qovluğunuzda ancaq verilənlər bazanızdadır. Sonra rejimi, SHA-1 və fayl adını təyin edirsiniz:

$ git update-index --add --cacheinfo 100644 \
  83baae61804e65cc73a7201a7252750c76066a30 test.txt

Bu vəziyyətdə, 100644 rejimini təyin edirsiniz, yəni normal bir fayldır. Digər seçimlər 100755-dir, yəni icra edilə bilən bir fayldır; və simvolik bir əlaqəni ifadə edən `120000`seçimidir. Rejim normal UNIX rejimlərindən götürülmüşdür, lakin daha az çevikdir - bu üç rejim Git-dəki fayllar (bloblar) üçün etibarlı olanlardır (baxmayaraq ki, digər rejimlər kitabçalar və alt modullar üçün istifadə olunur).

İndi quruluş sahəsini bir ağac obyektinə yazmaq üçün git write-tree istifadə edə bilərsiniz. Heç bir -w seçiminə ehtiyac yoxdur - bu əmri çağırmaq avtomatik olaraq indiki vəziyyətdə bir ağac obyektini indeks vəziyyətindən yaradır:

$ git write-tree
d8329fc1cc938780ffdd9f94e0d364e0ea74f579
$ git cat-file -p d8329fc1cc938780ffdd9f94e0d364e0ea74f579
100644 blob 83baae61804e65cc73a7201a7252750c76066a30      test.txt

Daha əvvəl gördüyünüz eyni git cat-file əmrini istifadə edərək ağac obyekti olduğunu da doğrulaya bilərsiniz:

$ git cat-file -t d8329fc1cc938780ffdd9f94e0d364e0ea74f579
tree

İndi test.txt-in ikinci versiyası və yeni bir fayl ilə yeni bir ağac yaradacaqsınız:

$ echo 'new file' > new.txt
$ git update-index --add --cacheinfo 100644 \
  1f7a7a472abf3dd9643fd615f6da379c4acb3e3a test.txt
$ git update-index --add new.txt

İndi səhnələşdirmə sahənizdə test.txt-in yeni versiyası ilə yanaşı yeni new.txt faylı var. O ağacı yazın (staging sahəsinin və ya indeksin bir ağac obyektinə yazılmasına istinadən) və necə göründüyünə baxın:

$ git write-tree
0155eb4229851634a0f03eb265b69f5a2d56f341
$ git cat-file -p 0155eb4229851634a0f03eb265b69f5a2d56f341
100644 blob fa49b077972391ad58037050f2a75f74e3671e92      new.txt
100644 blob 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a      test.txt

Diqqət yetirin ki, bu ağacın həm fayl girişləri, həm də test.txt SHA-1 əvvəldən qalan “version 2” SHA-1 (1f7a7a) versiyası var. Yalnız əyləncə üçün ilk ağacı alt qovluq olaraq bu ağaca əlavə edəcəksiniz. Quruluş sahənizə ağacları git read-tree-ni işə salaraq oxuya bilərsiniz. Bu vəziyyətdə, mövcud əmri --prefix seçimindən istifadə edərək subtree olaraq quruluş sahənizə oxuya bilərsiniz:

$ git read-tree --prefix=bak d8329fc1cc938780ffdd9f94e0d364e0ea74f579
$ git write-tree
3c4e9cd789d88d8d89c1073707c3585e41b0e614
$ git cat-file -p 3c4e9cd789d88d8d89c1073707c3585e41b0e614
040000 tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579      bak
100644 blob fa49b077972391ad58037050f2a75f74e3671e92      new.txt
100644 blob 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a      test.txt

Yeni yazdığınız ağacdan bir iş qovluğu yaratmışsanız, iş qovluğunun üst səviyyəsindəki iki faylı və test.txt sənədinin ilk versiyasını ehtiva edən bak adlı bir alt qovluğu əldə edəcəksiniz. Git-in bu strukturlar üçün ehtiva etdiyi məlumatları belə hesab edə bilərsiniz:

The content structure of your current Git data
Figure 150. The content structure of your current Git data

Commit Obyektləri

Yuxarıda sadalananların hamısını etmisinizsə, indi izləmək istədiyiniz layihənizin fərqli anlarını əks etdirən üç ağacınız var, lakin əvvəlki problem yerində qalır: anlıq snapshot-ları xatırlamaq üçün hər üç SHA-1 dəyərini xatırlamalısınız. Snapshot-ları kimin saxladığı, nə vaxt və ya nə üçün saxlandığı barədə heç bir məlumatınız isə yoxdur. Commit obyektinin sizin üçün saxladığı əsas məlumatlar budur.

Bir öhdəlik obyekti yaratmaq üçün, commit-tree işə salırsınız və bir ağac SHA-1 təyin edirsiniz və əgər varsa, əvvəlcədən obyektlər yaradırsınız. Yazdığınız ilk ağacdan başlayın:

$ echo 'First commit' | git commit-tree d8329f
fdf4fc3344e67ab068f836878b6c4951e3b15f3d
Note

Fərqli yaradılış müddəti və müəllif məlumatları səbəbindən fərqli bir hash dəyəri əldə edəcəksiniz. Üstəlik, prinsip etibarilə hər hansı bir commit obyekti məlumatların verildiyi dəqiq şəkildə çoxaldıla bilsə də, bu kitabın inşaatının tarixi təfərrüatları, çap edilən commit-lərin verilmiş commit-lərə uyğun olmaya biləcəyini göstərir. Bu bölmədə daha çox əmsal və etiket hash-lərini öz checksum-larınızla əvəz edin.

Artıq yeni commit-inizə git cat-file-dan baxa bilərsiniz:

$ git cat-file -p fdf4fc3
tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579
author Scott Chacon <schacon@gmail.com> 1243040974 -0700
committer Scott Chacon <schacon@gmail.com> 1243040974 -0700

First commit

Commit obyekti üçün format sadədir: həmin nöqtədə layihənin snapshot-u üçün ən yüksək səviyyəli ağacı təyin edir; valideyn varsa, commit-i götürür (yuxarıda göstərilən commit obyektinin valideynləri yoxdur); author/committer məlumatı (istifadəçi adınız və istifadəçi poçtunuzun konfiqurasiya parametrlərindən və zaman damğasından istifadə edən); boş bir sətir və sonra commit mesajı.

Sonra, hər biri özündən əvvəl gələn commit-ə istinad edən digər iki commit obyektini yazacaqsınız:

$ echo 'Second commit' | git commit-tree 0155eb -p fdf4fc3
cac0cab538b970a37ea1e769cbbde608743bc96d
$ echo 'Third commit'  | git commit-tree 3c4e9c -p cac0cab
1a410efbd13591db07496601ebc7a059dd55cfe9

Each of the three commit objects points to one of the three snapshot trees you created. Oddly enough, you have a real Git history now that you can view with the git log command, if you run it on the last commit SHA-1: Hər üç commit obyekti yaratdığınız üç snapshot ağacından birini göstərir. Qəribədir ki, son olaraq SHA-1-də işləsəniz, git log əmri ilə baxa biləcəyiniz gerçək bir Git tarixiniz var:

$ git log --stat 1a410e
commit 1a410efbd13591db07496601ebc7a059dd55cfe9
Author: Scott Chacon <schacon@gmail.com>
Date:   Fri May 22 18:15:24 2009 -0700

  Third commit

 bak/test.txt | 1 +
 1 file changed, 1 insertion(+)

commit cac0cab538b970a37ea1e769cbbde608743bc96d
Author: Scott Chacon <schacon@gmail.com>
Date:   Fri May 22 18:14:29 2009 -0700

  Second commit

 new.txt  | 1 +
 test.txt | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

commit fdf4fc3344e67ab068f836878b6c4951e3b15f3d
Author: Scott Chacon <schacon@gmail.com>
Date:   Fri May 22 18:09:34 2009 -0700

    First commit

 test.txt | 1 +
 1 file changed, 1 insertion(+)

Əla. Front end əmrlərindən heç birini istifadə etmədən Git tarixçəsi yaratmaq üçün aşağı səviyyə əməliyyatları tamamlamısınız.

Siz git addgit commit əmrlərini çalışdırdığınızda Git bunu edir - dəyişən fayllar üçün blob-ları saxlayır, indeksləri yeniləyir, ağacları və üst səviyyə istinad obyektlərini və onlardan dərhal əvvəl gələn vəzifələri yazır. Bu üç əsas Git obyekti — the blob, the tree və the commit — əvvəlcə .git/objects qovluğunda ayrı fayllar kimi saxlanılır. İndi bütün obyektlər, içində saxladıqları şərh edilmiş şəkildə nümunələr qovluğundadır:

$ find .git/objects -type f
.git/objects/01/55eb4229851634a0f03eb265b69f5a2d56f341 # tree 2
.git/objects/1a/410efbd13591db07496601ebc7a059dd55cfe9 # commit 3
.git/objects/1f/7a7a472abf3dd9643fd615f6da379c4acb3e3a # test.txt v2
.git/objects/3c/4e9cd789d88d8d89c1073707c3585e41b0e614 # tree 3
.git/objects/83/baae61804e65cc73a7201a7252750c76066a30 # test.txt v1
.git/objects/ca/c0cab538b970a37ea1e769cbbde608743bc96d # commit 2
.git/objects/d6/70460b4b4aece5915caf5c68d12f560a9fe3e4 # 'test content'
.git/objects/d8/329fc1cc938780ffdd9f94e0d364e0ea74f579 # tree 1
.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92 # new.txt
.git/objects/fd/f4fc3344e67ab068f836878b6c4951e3b15f3d # commit 1

Bütün daxili göstəriciləri təqib etsəniz, belə bir obyekt qrafiki əldə edirsiniz: . Git qovluğunuzdakı bütün əlçatan obyektlər image::images/data-model-3.png[All the reachable objects in your Git directory]

Object Deposu

Daha əvvəl Git obyekt verilənlər bazasına sadiq olduğunuz hər bir obyektlə birlikdə saxlanılan bir başlığın olduğunu qeyd etmişdik. Git-in obyektlərini necə saxladığını görmək üçün bir dəqiqə ayıraq. Blob obyektinin necə saxlandığını - bu halda “what is up, doc?”-- sətrini Ruby skript dilində interaktiv şəkildə görəcəksiniz.

İnteraktiv Ruby rejimini irb əmri ilə işə sala bilərsiniz:

$ irb
>> content = "what is up, doc?"
=> "what is up, doc?"

Git əvvəlcə obyekt tipini təyin etməklə başlayan bir başlıq qurur — bu halda bu bir blob sayılır. Başlığın bu birinci hissəsinə Git, boşluğun ardından məzmunun bayt ölçüsünü və son sıfır baytını əlavə edir:

>> header = "blob #{content.bytesize}\0"
=> "blob 16\u0000"

Git, başlığı və orijinal məzmunu birləşdirir və sonra bu yeni məzmunun SHA-1 checksum cəmini hesablayır. Ruby-də bir string-in SHA-1 dəyərini SHA1 toplama kitabxanasını require əmri ilə daxil edib daha sonra sətirlə Digest::SHA1.hexdigest() işə salaraq hesablaya bilərsiniz:

>> store = header + content
=> "blob 16\u0000what is up, doc?"
>> require 'digest/sha1'
=> true
>> sha1 = Digest::SHA1.hexdigest(store)
=> "bd9dbf5aae1a3862dd1526723246b20206e5fc37"

Gəlin onu Git hash-object output-u ilə müqayisə edək. Burada input-a yeni bir xətt əlavə edilməməsi üçün echo –n istifadə edirik.

$ echo -n "what is up, doc?" | git hash-object --stdin
bd9dbf5aae1a3862dd1526723246b20206e5fc37

Git, zlib kitabxanası ilə Ruby-də edə biləcəyiniz yeni məzmunu zlib ilə sıxışdırır. Əvvəlcə kitabxananı tələb etməlisiniz və sonra məzmunda Zlib::Deflate.deflate() işə salmalısınız:

>> require 'zlib'
=> true
>> zlib_content = Zlib::Deflate.deflate(store)
=> "x\x9CK\xCA\xC9OR04c(\xCFH,Q\xC8,V(-\xD0QH\xC9O\xB6\a\x00_\x1C\a\x9D"

Nəhayət, zlib-deflated məzmununuzu diskdəki bir obyektə yazacaqsınız. Yazmaq istədiyiniz obyektin path-ini təyin edəcəksiniz (SHA-1 dəyərinin ilk iki simvolu alt direktoriya adı və son 38 simvol həmin qovluqdakı fayl adıdır). Ruby-də, mövcud olmadıqda alt qovluğu yaratmaq üçün FileUtils.mkdir_p() funksiyasından istifadə edə bilərsiniz. Daha sonra, faylı File.open() ilə açın və əvvəlki zlib-compressed məzmunu nəticəsi ilə ortaya çıxan fayl sapı üzərinə bir write() çağırışı ilə yazın:

>> path = '.git/objects/' + sha1[0,2] + '/' + sha1[2,38]
=> ".git/objects/bd/9dbf5aae1a3862dd1526723246b20206e5fc37"
>> require 'fileutils'
=> true
>> FileUtils.mkdir_p(File.dirname(path))
=> ".git/objects/bd"
>> File.open(path, 'w') { |f| f.write zlib_content }
=> 32

Gəlin git cat-file istifadə edərək obyektin məzmununu yoxlayaq:

---
$ git cat-file -p bd9dbf5aae1a3862dd1526723246b20206e5fc37
what is up, doc?
---

Və budur – siz tamamlanmış bir Git blob obyekti yaratdınız.

Bütün Git obyektləri eyni şəkildə, yalnız fərqli növlər ilə saxlanılır – o, simvol bloku əvəzinə, commit başlığı və ya ağacla başlayacaq. Blob məzmunu təxminən hər şey ola bilsə də, commit və ağac məzmunu çox xüsusi formatlandırılmışdır.