Git
Chapters ▾ 2nd Edition

7.7 Git Alətləri - Reset Demystified

Reset Demystified

Daha ixtisaslaşmış alətlərə keçməzdən əvvəl Git resetcheckout əmrləri barədə danışaq. Bu əmrlər, Git-lə ilk qarşılaşdığınız zaman ən çaşdırıcı hissələrindən biridir. Onlar o qədər şeylər edirlər ki, onları həqiqətən başa düşmək və düzgün işlətmək ümidsiz görünür. Bunun üçün sadə bir metafora tövsiyə edirik.

The Three Trees

resetcheckout barədə düşünməyin daha asan yolu üç fərqli ağac məzmun meneceri olmaq üçün Git’in zehni çərçivəsindədir. Buradakı “tree” ilə, həqiqətən, məlumatların quruluşunu deyil, “files toplusunu” nəzərdə tuturuq. Bir neçə hal var ki, indeks tam olaraq bir ağac kimi davranmır, amma məqsədlərimiz üçün bu gün bu şəkildə düşünmək daha asandır.

Sistem olaraq Git normal işləmə rejimində üç ağac idarə edir və manipulyasiya edir:

Tree Rol

HEAD

Son commit-i çəkmək, növbəti valideyn

Index

Təklif olunan növbəti commit snapshot-u

Working Directory

Sandbox

The HEAD

HEAD, öz növbəsində həmin branch-da edilən son commit-ə göstərici olan cari branch arayışına işarədir. Demək ki, HEAD yaradılan növbəti commit-in valideyni olacaq. HEAD bu branch-dakı son commit-nizin snapshot-u kimi düşünmək ümumiyyətlə ən sadədir. Əslində bunun necə göründüyünə baxmaq çox asandır. Budur HEAD anlıq görüntüsündə hər bir fayl üçün faktiki qovluq siyahısının və SHA-1 yoxlama cədvəlinin alınmasına dair bir nümunə:

$ git cat-file -p HEAD
tree cfda3bf379e4f8dba8717dee55aab78aef7f4daf
author Scott Chacon  1301511835 -0700
committer Scott Chacon  1301511835 -0700

initial commit

$ git ls-tree -r HEAD
100644 blob a906cb2a4a904a152...   README
100644 blob 8f94139338f9404f2...   Rakefile
040000 tree 99f1a6d12cb4b6f19...   lib

Git cat-filels-tree əmrləri aşağı səviyyə işlər üçün istifadə olunan və həqiqətən gündəlik işlərdə istifadə edilməyən “plumbing” əmrləridir, lakin bunlar burada nələrin baş verdiyini görməyə kömək edir.

Index

Index təklif olunan növbəti commit-dir. Biz də bu anlayışa Git’in “Staging Area” olaraq müraciət etdik, çünki Git git commit işlətdiyiniz zaman bu hissə Git-in baxdığı yerdir.

Git, bu indeks iş sənədlərinizdə son yoxlanılan və əvvəlcə yoxlanıldıqda göründüyü bütün fayl məzmunlarının siyahısı ilə doldurulur. Daha sonra həmin faylların bəzilərini yeni versiyaları ilə əvəz edirsiniz və git commit onları yeni bir commit üçün ağaca çevirir.

$ git ls-files -s
100644 a906cb2a4a904a152e80877d4088654daad0c859 0	README
100644 8f94139338f9404f26296befa88755fc2598c289 0	Rakefile
100644 47c6340d6459e05787f644c2447d2595f5d3a54b 0	lib/simplegit.rb

Yenə də, burada indeksinizin göründüyünü göstərən pərdə arxasında daha çox olan git ls-files istifadə olunur.

İndeks texniki cəhətdən bir ağac quruluşunda deyil - əslində sadə bir təzahür şəklində tətbiq olunur - lakin məqsədlərimiz üçün kifayət qədər yaxındır.

İş Qovluğu

Nəhayət, bir iş qovluğunuz var (ümumiyyətlə “working tree” adlandırılır). Digər iki ağac, məzmunlarını səmərəli, lakin əlverişsiz bir şəkildə, .git qovluğu içərisində saxlayır. İşçi qovluq onları faktiki fayllara ayırır, bu da onları redaktə etməyi asanlaşdırır. İşçi qovluğu bir sandbox olaraq düşünün, burada dəyişiklikləri sahəyə (indeks) və sonra tarixə verməzdən əvvəl sınaqdan keçirə bilərsiniz.

$ tree
.
├── README
├── Rakefile
└── lib
    └── simplegit.rb

1 directory, 3 files

Workflow

Git-in tipik workflow-u bu üç ağacı manipulyasiya etməklə layihənizin görüntülərini ardıcıl olaraq daha yaxşı vəziyyətlərdə qeyd etməkdir.

reset workflow

Bu prosesi görüntüləyək: sizin bir sənədlə yeni bir qovluğa girdiyinizi fərz edək. Bunu faylın v1-i adlandıracağıq və göy rəngdə göstərəcəyik. İndi biz doğmamış master brancha-a işarə edən bir HEAD arayışı ilə Git depo yaradacaq və git init-i işlədəcəyik.

reset ex1

Bu anda yalnız iş qovluğu ağacı hər hansı bir məzmuna malikdir.

İndi bu faylı yerinə yetirmək istəyirik, buna görə işçi qovluğunda məzmun götürmək və indeksə kopyalamaq üçün git add istifadə edirik.

reset ex2

Sonra indeksin məzmununu götürən və onu daimi şəkli kimi saxlayan, həmin görüntüyə işarə edən bir commit obyekti yaradan və həmin commit-ə işarə etmək üçün master yeniləyən git commit-i işə salırıq.

reset ex3

git status-u işə salsaq, heç bir dəyişiklik görməyəcəyik, çünki hər üç ağac eyni qalacaq.

İndi həmin fayla bir dəyişiklik etmək və onu yerinə yetirmək istəyirik. Eyni prosesdən keçəcəyik; əvvəlcə iş sənədlərindəki faylı dəyişdiririk. Ona faylın v2-si deyək və qırmızı ilə göstərək.

reset ex4

Hal-hazırda git status-u işlədiriksə, faylı qırmızı rəngdə “Changes not staged for commit” şəklində görəcəyik, çünki bu giriş indeks və işçi qovluq arasında fərqlidir. Sonra biz onu indeksimizə daxil etmək üçün git add-ı işə salırıq.

reset ex5

Bu nöqtədə, əgər biz git status-u işlədiriksə, faylı “Changes to be committed” altında yaşıl rəngdə görəcəyik, çünki indeks və HEAD fərqlənir - yəni təklif olunan növbəti commit-imiz artıq son commit-mizdən fərqlənir. Nəhayət, commit-i yekunlaşdırmaq üçün git commit-i işlədirik.

reset ex6

İndi git status bizə heç bir output verməyəcək, çünki hər üç ağac yenə eynidir.

Branch-ları dəyişdirmə və ya klonlama oxşar bir prosesdən keçir. Bir branch-ı çıxartdıqda, yeni branch-ı ref-ə işarələmək üçün HEAD-ı dəyişdirir, commit-in görüntüsü ilə indeksinizi doldurur, sonra indeksin məzmununu iş qovluğunuza kopyalayır.

Reset-in Rolu

reset əmri bu kontekstdə baxıldıqda daha mənalı olur.

Bu nümunələrin məqsədləri üçün deyək ki, file.txt-ı yenidən dəyişdirdik və üçüncü dəfə tətbiq etdik. İndi tariximiz belə görünür:

reset start

İndi başlatdığınız zaman reset-in nə etdiyini araşdıraq. Bu üç ağacı birbaşa sadə və proqnozlaşdırılan şəkildə manipulyasiya edir. Üç əsas əməliyyatı yerinə yetirir.

Addım 1: Move HEAD

Yenidən qurmağın ilk işi, HEAD-in göstərdiyi şeyi hərəkət etdirməkdir. Bu, HEAD-ın özünü dəyişdirməklə eyni deyil (bu da yoxlama qaydasındadır); reset, rəhbərin işarə etdiyi branch-ı hərəkət etdirir. Bu, HEAD master branch-na (yəni hazırda master branch-a) təyin olunarsa, 9e5e6a4 git reset 9e5e6a4 səviyyəsinə master nöqtəsi ilə başlayacaq deməkdir.

reset soft

Commit ilə hər hansı bir reset formasından asılı olmayaraq, bu daima etməyə çalışacağı ilk şeydir. reset --soft ilə isə, sadəcə orada dayanacaq.

İndi bu diaqrama nəzər salmaq və nəyin baş verdiyini anlamaq üçün bir addım ataq: bu, son git commit-i ləğv etdi. git commit-i işə saldığınız zaman, Git yeni bir commit yaradır və rəhbərlik etdiyi branch-ı ona doğru istiqamətləndirir. HEAD~-a reset etdikdə (HEAD-ın valideyni), sənədi və ya iş qovluğunu dəyişdirmədən branch-ı olduğu yerə aparırsan. İndi indeksini yeniləyə bilər və git commit --amend-in nə edəcəyini yerinə yetirmək üçün yenidən git commit işlədə bilərsiniz (Son Commit Dəyişdirilməsi-a bax)

Addım 2: İndeksin yenilənməsi (-- mixed)

Qeyd edək ki, indi git status-u işləsəniz, indekslə yeni HEAD-in fərqini yaşıl rəngdə görəcəksiniz.

Reset-in sonrakı işi, indiyə kimi HEAD-ın qeyd etdiyi hər şeyi məzmunu ilə yeniləməkdir.

reset mixed

--mixed seçimi göstərsəniz, reset bu nöqtədə dayanacaq. Bu da standartdır, buna görə heç bir seçim etməmisinizsə (bu vəziyyətdə git reset HEAD~ edin), əmr dayanacaq.

İndi bu diaqrama baxmaq və nəyin baş verdiyini anlamaq üçün başqa bir saniyə ayırın: bu hələ də son commit-nizi ləğv etdi, lakin, eyni zamanda hər şeyi dayandırdı. Bütün git addgit commit əmrlərinizi yerinə yetirmədən əvvəl geri döndünüz.

Adım 3: İş Qovluğu Yeniləmə (--hard)

reset-in edəcəyi üçüncü şey, işləyən qovluğu indeksə bənzətməkdir. --hard seçimindən istifadə edirsinizsə, o, bu mərhələyə davam edəcəkdir.

reset hard

Beləliklə, nə baş verdiyini düşünək. Sonuncu əmrinizi, git addgit commit əmrlərini iş qovluğunuzda etdiyiniz bütün işləri ləğv etdiniz.

Qeyd etmək vacibdir ki, bu bayraq (--hard) reset əmrini təhlükəli hala gətirməyin yeganə yoludur və Git-in məlumatları məhv edəcəyi çox az hallardan biridir. reset-in hər hansı bir başqa çağırışı asanlıqla geri qaytarıla bilər, lakin --hard seçimi, işləyən qovluqdakı faylları çətinliklə yazacaq. Bu vəziyyətdə, hələ də Git DB-də commit-də v3 versiyamız var və onu reflog-muza baxaraq geri ala bilərdik, amma əgər commit etməsəydik, Git yenə də faylın üstünə yazacaqdı və o bərpaedilməz olardı.

Recap

reset əmri bu üç ağacı müəyyən bir qaydada ləğv edir, bunu söylədikdə dayandırır:

  1. Branch HEAD nöqtələrini hərəkətə gətirin (--soft olduqda dayanın).

  2. İndeksi HEAD kimi göstərin (--hard olduqda dayandırın).

  3. İş qovluğunu indeks kimi göstərin.

Path ilə Reset

Bu, əsas formada reset-in davranışını əhatə edir, eyni zamanda hərəkət etmək üçün path ilə təmin edə bilərsiniz. Path-i təyin etsəniz, reset 1-ci addımı atlayacaq və hərəkətlərinin qalan hissəsini müəyyən bir fayl və ya fayllar dəsti ilə məhdudlaşdıracaqdır. Bu, həqiqətən bir növ məna kəsb edir — HEAD sadəcə bir göstəricidir və bir commit-in bir hissəsini və digərinin hissəsini göstərə bilməzsiniz. Lakin indeks və işləyən qovluq qismən yenilənə bilər, buna görə reset 2 və 3 addımlarla davam edir.

Beləliklə, git reset file.txt-u işlətdiyimizi fərz edək. Bu forma (bir SHA-1 və ya branch-ı göstərmədiyinizə və --soft və ya --hard göstərmədiyinizə görə) git reset --mixed HEAD file.txt üçün stenoqramdır:

  1. Branch-ı HEAD nöqtələrinə köçürün (atıldı).

  2. İndeksi HEAD kimi göstərin (burada dayanın).

Beləliklə, bu, yalnız file.txt-ı HEAD-dan indeksə köçürür.

reset path1

Bu, faylı unstaging etməyə təsir göstərir. Bu əmrin diaqramına baxsaq və git add-ın nə etdiyini düşünsək, onlar tam əkslərdir.

reset path2

Buna görə git status əmrinin nəticəsi bir faylın açılmaması üçün işə başlamağınızı təklif edir (bu barədə daha çox məlumat üçün Mərhələli Bir Faylın Mərhələlərə Ayrılmaması-a baxın).

Git-in bu versiyasını çıxarmaq üçün müəyyən bir commit-i göstərərək “pull the data from HEAD” dediyini asanlıqla edə bilərik. Sadəcə git reset eb43bf file.txt kimi bir şey işlədərdik.

reset path3

Bu, işin içindəki faylın məzmunu v1-ə qaytardığımız, üzərinə git add işlədib yenidən v3-ə qaytardığımız kimi eyni şeyi edir (həqiqətən bütün bu addımlardan keçmədən). İndi git commit-i işə salırıqsa, bu işi yenidən v1-ə qaytaran bir dəyişiklik qeyd edəcək, baxmayaraq ki, bu, heç işlədiyimiz qovluqda heç olmamışdır.

git add kimi, reset əmri, bir hunk-by-hunk əsasında məzmunu açmaq üçün --patch seçimini qəbul edəcəyi də maraqlıdır. Beləliklə, məzmunu seçmə şəkildə dayandıra və ya geri qaytara bilərsiniz.

Squashing

Gəlin bu yeni güc ilə necə maraqlı bir iş görəcəyimizə nəzər salaq.

“oops.”, “WIP” və “forgot this file” kimi bir sıra commit-niz olduğunu düşünək. Həqiqətən ağıllı görünməyinizi təmin edən reset-i tez və asanlıqla bir vahid commit halına gətirə bilərsiniz. Squashing Commits bunun başqa bir yolunu göstərir, lakin bu nümunədə reset tətbiq etmək daha asandır.

Deyək ki, birinci commit-in bir sənəd olduğu bir layihə var, ikincisi yeni bir fayl əlavə etdi və birincisini dəyişdirdi, üçüncü commit isə ilk sənədini yenidən dəyişdirdi. İkinci commit davam edən bir iş idi və siz onu squash istəyirsiniz.

reset squash r1

HEAD branch-nı köhnə bir commit-ə (davam etdirmək istədiyiniz ən yeni commit-ə) köçürmək üçün git reset --soft HEAD~2 tətbiq edə bilərsiniz.

reset squash r2

Və sonra sadəcə yenidən git commit-i işə salın:

reset squash r3

İndi əlçatan tarixçənizi, push edəcəyiniz tarixin indi bir fayl-a.txt v1 ilə iş görməyinizə, sonra da hər ikisinin file-a.txt faylını v3-ə dəyişdirib fayl-b.txt əlavə etməyinə bənzəyirsiniz. Faylın v2 versiyası ilə commit artıq tarixdə yoxdur.

Yoxlama

Nəhayət, checkoutreset arasındakı fərqin nə olduğunu düşünə bilərsiniz. reset kimi, checkout da üç ağacı idarə edir və əmrə bir fayl path verib verməməyinizə görə bir az fərqlidir.

Path-lar Olmadan

git checkout [branch] işlətməsi git reset --hard [branch] işlətməsinə bənzəyir, çünki [branch] kimi görünməyiniz üçün üç ağacı da yeniləyir, ancaq burada iki mühim fərq var.

Birincisi, reset --hard-dan fərqli olaraq, checkout iş üçün təhlükəsizdir; bu dəyişikliklər olan faylları üzə vurmadığından əmin olacaq. Əslində, o bundan bir az daha ağıllıdır - iş qovluğunda əhəmiyyətsiz bir birləşmə etməyə çalışır, belə ki dəyişdirmədiyiniz bütün fayllar yenilənəcəkdir. reset --hard, əksinə, lövhədə hər şeyi yoxlamadan əvəz edəcəkdir.

İkinci önəmli fərq, checkout-un HEAD-i necə yeniləməsidir. HEAD-ın göstərdiyi branch-ı yenidən hərəkət etdirərkən, checkout başqa bir branch-a işarə etmək üçün HEAD-ı özünə aparacaqdır.

Məsələn, deyək ki, fərqli commit-lərə işarə edən masterdevelop branch-larımız var və hazırda develop-dayıq (ona görə də HEAD buna işarə edir). git reset master işləsək, develop özü indi master-in etdiyi eyni commit-ə işarə edəcəkdir. Bunun əvəzinə git checkout master işlətsək, develop hərəkət etmir, HEAD özü edir. HEAD indi master-ə işarə edəcəkdir.

Beləliklə, hər iki vəziyyətdə də HEAD-i A commit-ni göstərməyə yönəldirik, amma bunu necə etdiyimiz çox fərqlidir. reset, HEAD nöqtələrini hərəkətə gətirəcək, checkout HEAD-in özünü hərəkət etdirəcəkdir.

reset checkout

Path-larla Birlikdə

checkout-u işlətmənin başqa yolu, reset kimi HEAD hərəkət etməyən bir file path-dır. Eynilə git reset [branch] file faylına bənzəyir, indeksi həmin sənədlə həmin faylda yeniləyir, eyni zamanda iş qovluğundakı faylın üzərinə yazır. Tam olaraq git reset --hard [branch] file kimi olardı (reset onu işlətməyinizə imkan verərsə) - iş qovluğu təhlükəsiz deyil və HEAD hərəkət etmir.

Ayrıca, git resetgit add kimi, checkout seçilmiş şəkildə bir hunk-by-hunk əsasında fayl məzmununu geri qaytarmağınız üçün bir --patch seçimi qəbul edəcəkdir.

Məzmun

Ümid edirik ki, indi reset əmri başa düşürsünüz və daha rahat hiss edirsiniz, amma bunun yəqin ki, checkout-dan nə dərəcədə fərqləndiyinə dair bir az qarışıq və bəlkə də fərqli çağırışların bütün qaydalarını xatırlaya bilmirsiniz.

Burada əmrlərin hansı ağaclara təsir etdiyini göstərən bir cheat-sheet var. “HEAD” sütununda, əmr, rəhbərin göstərdiyi istinadı (branch-ı) hərəkət etdirərsə, “HEAD” ifadəsini işlədiyi təqdirdə, “REF” və oxunuşunu özü idarə edərsə “HEAD” oxuyur. WD Safe? mövzusuna xüsusi diqqət yetirin. sütun - əgər YOX deyirsə, bu əmri işə salmadan əvvəl düşünün.

HEAD Index Workdir WD Safe?

Commit Level

reset --soft [commit]

REF

NO

NO

YES

reset [commit]

REF

YES

NO

YES

reset --hard [commit]

REF

YES

YES

NO

checkout <commit>

HEAD

YES

YES

YES

File Level

reset [commit] <paths>

NO

YES

NO

YES

checkout [commit] <paths>

NO

YES

YES

NO