-
1. Başlanğıc
- 1.1 Versiyaya Nəzarət Haqqında
- 1.2 Git’in Qısa Hekayəsi
- 1.3 Git Nədir?
- 1.4 Əmr Sətiri
- 1.5 Git’i Quraşdırmaq
- 1.6 İlk Dəfə Git Quraşdırması
- 1.7 Kömək Almaq
- 1.8 Qısa Məzmun
-
2. Git’in Əsasları
-
3. Git’də Branch
- 3.1 Nutshell’də Branch’lar
- 3.2 Sadə Branching və Birləşdirmə
- 3.3 Branch İdarəedilməsi
- 3.4 Branching İş Axınları
- 3.5 Uzaq Branch’lar
- 3.6 Rebasing
- 3.7 Qısa Məzmun
-
4. Server’də Git
- 4.1 Protokollar
- 4.2 Serverdə Git Əldə Etmək
- 4.3 Sizin öz SSH Public Key’nizi yaratmaq
- 4.4 Server qurmaq
- 4.5 Git Daemon
- 4.6 Smart HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 Üçüncü Tərəf Seçimləri
- 4.10 Qısa Məzmun
-
5. Paylanmış Git
-
6. GitHub
-
7. Git Alətləri
- 7.1 Reviziya Seçimi
- 7.2 Interaktiv Səhnələşdirmə
- 7.3 Stashing və Təmizləmə
- 7.4 İşinizin İmzalanması
- 7.5 Axtarış
- 7.6 Tarixi Yenidən Yazmaq
- 7.7 Reset Demystified
- 7.8 İnkişaf etmiş Birləşmə
- 7.9 Rerere
- 7.10 Git ilə Debugging
- 7.11 Alt Modullar
- 7.12 Bundling
- 7.13 Dəyişdirmək
- 7.14 Etibarlı Yaddaş
- 7.15 Qısa Məzmun
-
8. Git’i Fərdiləşdirmək
- 8.1 Git Konfiqurasiyası
- 8.2 Git Atributları
- 8.3 Git Hook’ları
- 8.4 Git-Enforced Siyasət Nümunəsi
- 8.5 Qısa Məzmun
-
9. Git və Digər Sistemlər
- 9.1 Git Müştəri kimi
- 9.2 Git’ə Miqrasiya
- 9.3 Qısa Məzmun
-
10. Git’in Daxili İşləri
- 10.1 Plumbing və Porcelain
- 10.2 Git Obyektləri
- 10.3 Git Referansları
- 10.4 Packfile’lar
- 10.5 Refspec
- 10.6 Transfer Protokolları
- 10.7 Maintenance və Məlumatların Bərpası
- 10.8 Mühit Dəyişənləri
- 10.9 Qısa Məzmun
-
A1. Appendix A: Digər Mühitlərdə Git
- A1.1 Qrafik interfeyslər
- A1.2 Visual Studio’da Git
- A1.3 Visual Studio Code’da Git
- A1.4 Eclipse’də Git
- A1.5 Sublime Text’də Git
- A1.6 Bash’da Git
- A1.7 Zsh’də Git
- A1.8 PowerShell’də Git
- A1.9 Qısa Məzmun
-
A2. Appendix B: Proqramlara Git Daxil Etmək
- A2.1 Əmr-sətri Git
- A2.2 Libgit2
- A2.3 JGit
- A2.4 go-git
- A2.5 Dulwich
-
A3. Appendix C: Git Əmrləri
- A3.1 Quraşdırma və Konfiqurasiya
- A3.2 Layihələrin Alınması və Yaradılması
- A3.3 Sadə Snapshotting
- A3.4 Branching və Birləşmə
- A3.5 Layihələrin Paylaşılması və Yenilənməsi
- A3.6 Yoxlama və Müqayisə
- A3.7 Debugging
- A3.8 Patching
- A3.9 E-poçt
- A3.10 Xarici Sistemlər
- A3.11 İdarəetmə
- A3.12 Plumbing Əmrləri
8.4 Git’i Fərdiləşdirmək - Git-Enforced Siyasət Nümunəsi
Git-Enforced Siyasət Nümunəsi
Bu hissədə, xüsusi bir commit mesajı formatını yoxlayan və yalnız müəyyən istifadəçilərin bir layihədəki müəyyən alt qovluğu dəyişdirməsinə imkan verən bir Git workflow qurmaq üçün öyrəndiklərinizi istifadə edəcəksiniz. Developer-in push-nunun rədd ediləcəyini və siyasətləri həqiqətən tətbiq edən server skriptlərini bilməsinə kömək edən müştəri skriptləri quracaqsınız.
Göstərəcəyimiz ssenarilər Ruby ilə yazılmışdır; qismən intellektual hərəkətsizliyimizə görə, həm də Ruby-ni mütləq yaza bilməsəniz də oxumaq asandır. Bununla birlikdə, hər hansı bir dil işləyəcəkdir - Git ilə paylanan bütün nümunə hook skriptləri ya Perl ya da Bash-dadır, buna görə nümunələrə baxaraq bu dillərdə çox sayda hook nümunəsi görə bilərsiniz.
Server Tərəf Hook’u
Bütün server tərəfli işlər hooks
qovluğunuzdakı update
faylına daxil olacaq.
update
hook-u hər bir push üçün bir dəfə işləyir və üç arqument götürür:
-
push olunan istinadın adı
-
Bu branch-ın olduğu köhnə düzəliş
-
push olunan yeni versiya
Push SSH üzərində işləyirsə push edən istifadəçiyə də girişiniz var.
Hər kəsin ümumi key identifikasiyası yolu ilə tək bir istifadəçi ilə (“git” kimi) əlaqə qurmasına icazə verdiyiniz təqdirdə, bu istifadəçiyə açıq key-ə əsasən hansı istifadəçinin birləşdiyini təyin edən shell wrapper verməlisiniz və müvafiq olaraq bu mühit dəyişəndir.
Burada əlaqə istifadəçisinin $USER
mühit dəyişənində olduğunu düşünəcəyik, buna görə də yeniləmə skriptiniz lazım olan bütün məlumatları toplamaqla başlayır:
#!/usr/bin/env ruby
$refname = ARGV[0]
$oldrev = ARGV[1]
$newrev = ARGV[2]
$user = ENV['USER']
puts "Enforcing Policies..."
puts "(#{$refname}) (#{$oldrev[0,6]}) (#{$newrev[0,6]})"
Bəli, bunlar qlobal dəyişənlərdir. Mühakimə etməyin - bu şəkildə nümayiş etdirmək daha asandır.
Xüsusi bir Commit-Mesaj Formatının Tətbiq Edilməsi
İlk probleminiz hər bir commit mesajının müəyyən bir formata riayət etməsini təmin etməkdir. Yalnız bir hədəfə sahib olmaq üçün hər bir mesajın “ref: 1234” kimi görünən bir simli daxil etdiyini fərz edin, çünki hər bir commit-in bilet sisteminizdəki bir iş elementi ilə əlaqələndirilməsini istəyirsiniz. Hər bir push-un yuxarı qaldırıldığına baxmalısınız, bu sətirin commit mesajında olub olmadığını görməlisiniz və əgər sətir heç bir commit-də yoxdursa, non-zero-dan çıxın, beləliklə push rədd edilir.
$newrev
və $oldrev
dəyərlərini götürərək git rev-list
adlı Git plumbing əmrinə ötürərək push edilən bütün commit-lərin SHA-1 dəyərlərinin siyahısını əldə edə bilərsiniz.
Bu, əsasən git log
əmridir, lakin standart olaraq yalnız SHA-1 dəyərlərini yazdırır və başqa heç bir məlumat yoxdur.
Beləliklə, bir SHA-1 əmri ilə digər biri arasında tətbiq olunan bütün SHA-1-lərin siyahısını almaq üçün belə bir şey edə bilərsiniz:
$ git rev-list 538c33..d14fc7
d14fc7c847ab946ec39590d87783c69b031bdfb7
9f585da4401b0a3999e84113824d15245c13f0be
234071a1be950e2a8d078e6141f5cd20c1e61ad3
dfa04c9ef3d5197182f13fb5b9b1fb7717d2222a
17716ec0f1ff5c77eff40b7fe912f9f6cfd0e475
Bu nəticəni götürə bilər, SHA-1-ləri işləyənlərin hər birindən keçə bilər, bunun üçün mesajı götürə və bu mesajı nümunə axtaran normal bir ifadəyə qarşı test edə bilərsiniz.
Bu commit-lərin hər birindən test etmək üçün commit mesajını necə alacağınızı bilməlisiniz.
Raw commit məlumatlarını almaq üçün, git cat-file
adlı başqa bir plumbing əmrindən istifadə edə bilərsiniz.
Plumbing əmrlərinə daha detallı Git’in Daxili İşləri-dan baxacağıq; ancaq hələlik bu əmrin sizə verdiyi budur:
$ git cat-file commit ca82a6
tree cfda3bf379e4f8dba8717dee55aab78aef7f4daf
parent 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
author Scott Chacon <schacon@gmail.com> 1205815931 -0700
committer Scott Chacon <schacon@gmail.com> 1240030591 -0700
Change the version number
SHA-1 dəyərinə sahib olduqda commit mesajını commit-dən əldə etməyin sadə bir yolu ilk boş sətirə keçmək və bundan sonra hər şeyi götürməkdir.
Bunu Unix sistemlərindəki sed
əmri ilə edə bilərsiniz:
$ git cat-file commit ca82a6 | sed '1,/^$/d'
Change the version number
Uyğun olmayan bir şey görsəniz, push etməyə və çıxmağa çalışan hər bir commit-dən commit mesajını almaq üçün bu tilsimi istifadə edə bilərsiniz. Ssenaridən çıxmaq və push-u rədd etmək üçün non-zero’dan çıxın. Bütün metod belə görünür:
$regex = /\[ref: (\d+)\]/
# enforced custom commit message format
def check_message_format
missed_revs = `git rev-list #{$oldrev}..#{$newrev}`.split("\n")
missed_revs.each do |rev|
message = `git cat-file commit #{rev} | sed '1,/^$/d'`
if !$regex.match(message)
puts "[POLICY] Your message is not formatted correctly"
exit 1
end
end
end
check_message_format
Bunu update
skriptinə qoymağınız qaydanıza əməl etməyən mesajları olan commit-ləri olan yeniləmələri rədd edəcəkdir.
User-Based ACL Sisteminin Tətbiqi
Fərz edək ki, hansı istifadəçilərə layihələrinizin hansı hissələrində dəyişikliklər etməyə icazə verildiyini göstərən bir giriş nəzarəti siyahısı (ACL) istifadə edən bir mexanizm əlavə etmək istəyirsiniz.
Bəzi insanlar tam giriş hüququna malikdirlər, bəziləri isə yalnız müəyyən alt qovluqlara və ya müəyyən fayllara dəyişiklik edə bilər.
Bunu tətbiq etmək üçün bu qaydaları serverdəki boş Git deposunda yaşayan acl
adlı bir fayla yazacaqsınız.
Bu qaydalara baxaraq update
hook-na baxacaqsınız, push etdiyiniz bütün commit-lər üçün hansı faylların təqdim olunduğuna baxın və push edən istifadəçinin bütün bu faylları yeniləməyə giriş imkanının olub olmadığını müəyyənləşdirəcəksiniz.
Edəcəyiniz ilk şey ACL yazmaqdır.
Burada CVS ACL mexanizmi kimi bir formatdan çox istifadə edəcəksiniz: birinci sahənin avail
və ya unavail
olduğu bir sıra sətirlərdən istifadə edir, növbəti sahə istifadəçilərin vergüllə ayrılmış siyahısıdır,sonra qayda tətbiq olunur və son sahə qaydanın tətbiq olunduğu path-dir (blank açıq giriş deməkdir).
Bu sahələrin hamısı boru (|
) işarəsi ilə ayrılmışdır.
Bu vəziyyətdə, bir neçə idarəçiniz var, bəzi sənəd yazarlarına doc
qovluğuna giriş imkanı var və yalnız lib
və tests
qovluqlarına çıxışı olan bir developer var və ACL dosyanız belə görünür:
avail|nickh,pjhyett,defunkt,tpw
avail|usinclair,cdickens,ebronte|doc
avail|schacon|lib
avail|schacon|tests
Bu məlumatları istifadə edə biləcəyiniz bir quruluşu oxumaqla başlayırsınız.
Bu vəziyyətdə, nümunəni sadə saxlamaq üçün yalnız avail
direktivlərini yerinə yetirəcəksiniz.
Burada key-in istifadəçi adı olduğu və dəyərin istifadəçinin yazma girişinə sahib olduğu bir sıranın olduğu assosiativ bir sıra verən metod var:
def get_acl_access_data(acl_file)
# read in ACL data
acl_file = File.read(acl_file).split("\n").reject { |line| line == '' }
access = {}
acl_file.each do |line|
avail, users, path = line.split('|')
next unless avail == 'avail'
users.split(',').each do |user|
access[user] ||= []
access[user] << path
end
end
access
end
Daha əvvəl baxdığınız ACL faylında bu get_acl_access_data
metodu aşağıdakı kimi bir məlumat strukturunu qaytarır:
{"defunkt"=>[nil],
"tpw"=>[nil],
"nickh"=>[nil],
"pjhyett"=>[nil],
"schacon"=>["lib", "tests"],
"cdickens"=>["doc"],
"usinclair"=>["doc"],
"ebronte"=>["doc"]}
Artıq icazələrinızı çeşidlədikdən sonra, push etdiyiniz commit-lərin hansı path-ların dəyişdirildiyini təyin etməlisiniz, beləliklə push edən istifadəçinin hər yerə girişinə əmin ola bilərsiniz.
git log
əmrinə --name-only
seçimi ilə təkcə bir işdə hansı faylların dəyişdirildiyini olduqca asanlıqla görə bilərsiniz ( Git’in Əsasları-də qısaca qeyd olunur):
$ git log -1 --name-only --pretty=format:'' 9f585d
README
lib/test.rb
get_acl_access_data
metodundan qaytarılan ACL quruluşundan istifadə edirsinizsə və hər bir siyahıda sadalanan fayllarla müqayisə etsəniz, istifadəçinin bütün commit-lərini push etmək imkanının olub olmadığını müəyyən edə bilərsiniz:
# only allows certain users to modify certain subdirectories in a project
def check_directory_perms
access = get_acl_access_data('acl')
# see if anyone is trying to push something they can't
new_commits = `git rev-list #{$oldrev}..#{$newrev}`.split("\n")
new_commits.each do |rev|
files_modified = `git log -1 --name-only --pretty=format:'' #{rev}`.split("\n")
files_modified.each do |path|
next if path.size == 0
has_file_access = false
access[$user].each do |access_path|
if !access_path # user has access to everything
|| (path.start_with? access_path) # access to this path
has_file_access = true
end
end
if !has_file_access
puts "[POLICY] You do not have access to push to #{path}"
exit 1
end
end
end
end
check_directory_perms
Serverinizə git rev-list
ilə göndərilən yeni commit-lərin siyahısını alırsınız.
Sonra, bu commit-lərin hər biri üçün hansı faylların dəyişdirildiyini tapır və push edən istifadəçinin dəyişdirilən bütün path-lara girişinə əmin olun.
Artıq istifadəçiləriniz pis qurulmuş mesajlarla və ya təyin olunmuş path-ların xaricində dəyişdirilmiş fayllarla heç bir commit götürə bilməzlər.
Testdən Keçirmək
Bütün bu kodu qoymağınız lazım olan fayl olan chmod u+x .git/hooks/update
işlədirsinizsə və uyğun olmayan bir mesajla bir commit götürməyə çalışsanız, buna bənzər bir şey əldə edirsiniz.
$ git push -f origin master
Counting objects: 5, done.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 323 bytes, done.
Total 3 (delta 1), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
Enforcing Policies...
(refs/heads/master) (8338c5) (c5b616)
[POLICY] Your message is not formatted correctly
error: hooks/update exited with error code 1
error: hook declined to update refs/heads/master
To git@gitserver:project.git
! [remote rejected] master -> master (hook declined)
error: failed to push some refs to 'git@gitserver:project.git'
Burada bir-iki maraqlı şey var. Birincisi, bunu hook işləməyə başladığı yerdə görürsən.
Enforcing Policies...
(refs/heads/master) (fb8c72) (c56860)
Yeniləmə skriptinizin əvvəlində onu çap etdiyinizi unutmayın.
Ssenarinizin stdout
ilə əks olunduğu hər hansı bir şey müştəriyə ötürüləcəkdir.
Növbəti şey error mesajıdır.
[POLICY] Your message is not formatted correctly
error: hooks/update exited with error code 1
error: hook declined to update refs/heads/master
Birinci sətir sizin tərəfinizdən çap olundu, digər ikisi Git, yeniləmə skriptinin sıfırdan çıxdığını və bununla push etmənizin azaldığını söylədi. Son olaraq:
To git@gitserver:project.git
! [remote rejected] master -> master (hook declined)
error: failed to push some refs to 'git@gitserver:project.git'
Hook-un rədd etdiyi hər bir müraciət üçün remote rədd edilmiş bir mesaj görəcəksiniz və o, bunun hook çatışmazlığı səbəbindən xüsusi olaraq rədd edildiyini bildirir.
Bundan əlavə, kimsə girişi olmayan bir faylı düzəltməyə və içərisində olan bir commit-i push etməyə çalışsa, oxşar bir şey görəcəkdir.
Məsələn, bir fayl müəllifi lib
qovluğunda bir şey dəyişdirərək bir commit-i push etməyə çalışırsa, belə görürür:
[POLICY] You do not have access to push to lib/test.rb
Bundan sonra, o update
skripti olduğu və icra edilə biləcəyi müddətdə, deponuzda heç vaxt sizin nümunəniz olmayan bir commit mesajı olmayacaq və istifadəçiləriniz sandbox altında qalacaq.
Müştəri Tərəf Hook-lar
Bu yanaşmanın mənfi tərəfi, istifadəçilərinizin push-ları rədd edildikdə qaçılmaz olaraq nəticələnəcək. Diqqətlə hazırlanmış işlərinin son anda rədd edilməsi son dərəcə məyus və qarışıq ola bilər; və bundan əlavə tarixlərini düzəltmək üçün edit etməli olacaqlar ki, bu da həmişə ürək qırıqlığıdır.
Bu çıxılmaz vəziyyətin cavabı, istifadəçilərin serverin rədd edə biləcəyi bir şey etdikləri zaman xəbərdar etmək üçün istifadə edə biləcəyi bəzi müştəri tərəfindəki hook-ları təmin etməkdir.
Beləliklə, hər hansı bir problemi commit etməzdən əvvəl və bu problemləri həll etmək çətinləşməmişdən əvvəl düzəldə bilərlər.
Hook-lar bir layihənin klonu ilə köçürülmədiyi üçün bu skriptləri başqa bir şəkildə paylamalı və sonra istifadəçilərinizdən bunları .git/hooks
qovluğuna kopyalayıb icraya hazır etməlisiniz.
Bu hook-ları proyekt daxilində və ya ayrı bir layihədə paylaya bilərsiniz, lakin Git onları avtomatik olaraq qurmayacaqdır.
Başlamaq üçün, hər bir commit qeydə alınmazdan əvvəl commit mesajınızı yoxlamalısınız, belə ki, serverin pis formatlanmış commit mesajlarına görə dəyişikliklərinizi rədd etməyəcəyini bilirsiniz.
Bunu etmək üçün commit-msg
hook-u əlavə edə bilərsiniz.
Birinci arqument kimi ötürülən fayldan mesajı oxudunuzsa və bunu nümunə ilə müqayisə etsəniz, uyğunlaşma olmadığı təqdirdə Git-i commit-i ləğv etməyə məcbur edə bilərsiniz:
#!/usr/bin/env ruby
message_file = ARGV[0]
message = File.read(message_file)
$regex = /\[ref: (\d+)\]/
if !$regex.match(message)
puts "[POLICY] Your message is not formatted correctly"
exit 1
end
Əgər həmin skript yerindədirsə (.git/hooks/commit-msg
-də) və işlədilə bilərsə və düzgün hazırlanmamış bir mesajla məşğul olursunuzsa, bunu görürsünüz:
$ git commit -am 'Test'
[POLICY] Your message is not formatted correctly
Bu vəziyyətdə heç bir commit tamamlanmamışdır. Bununla birlikdə, mesajınız uyğun bir pattern-i ehtiva edirsə, Git sizə imkan verir:
$ git commit -am 'Test [ref: 132]'
[master e05c914] Test [ref: 132]
1 file changed, 1 insertions(+), 0 deletions(-)
Sonra ACL əhatənizdən kənar faylları dəyişdirmədiyinizə əmin olmaq istəyirsiniz.
Layihənizin .git
qovluğunda əvvəllər istifadə etdiyiniz ACL sənədinin bir nüsxəsi varsa, aşağıdakı pre-commit
ssenarisi sizin üçün bu məhdudiyyətləri tətbiq edəcəkdir:
#!/usr/bin/env ruby
$user = ENV['USER']
# [ insert acl_access_data method from above ]
# only allows certain users to modify certain subdirectories in a project
def check_directory_perms
access = get_acl_access_data('.git/acl')
files_modified = `git diff-index --cached --name-only HEAD`.split("\n")
files_modified.each do |path|
next if path.size == 0
has_file_access = false
access[$user].each do |access_path|
if !access_path || (path.index(access_path) == 0)
has_file_access = true
end
if !has_file_access
puts "[POLICY] You do not have access to push to #{path}"
exit 1
end
end
end
check_directory_perms
Bu, server tərəfindəki hissə ilə təxminən eyni skriptdir, lakin iki mühüm fərq var.
Birincisi, ACL faylı başqa bir yerdədir, çünki bu ssenari .git
qovluğundan deyil, iş qovluğundan işləyir.
ACL faylının yolunu buradan dəyişdirməlisiniz:
access = get_acl_access_data('acl')
to this:
access = get_acl_access_data('.git/acl')
Digər vacib fərq, dəyişdirilmiş faylların siyahısını əldə etmək üsuludur. Server tərəfindəki metod, tapşırıqların jurnalına baxdığından və bu anda commit hələ qeyd olunmadığından, bunun əvəzinə fayl siyahısını səhnələşdirmə sahəsindən almalısınız. Bunun əvəzinə:
files_modified = `git log -1 --name-only --pretty=format:'' #{ref}`
you have to use:
files_modified = `git diff-index --cached --name-only HEAD`
Lakin, sadəcə iki fərq bunlardır - əks halda, ssenari eyni şəkildə işləyir.
Bir xəbərdarlıq budur ki, remote machine-a push etdiyiniz eyni istifadəçi ilə yerli olaraq işləməyinizi gözləyir.
Fərqlidirsə, $user
dəyişənini manual olaraq təyin etməlisiniz.
Burada edə biləcəyimiz başqa bir şey də istifadəçinin sürətli göndərilməyən istinadları push etməsindən əmin olmaqdır. Sürətli olmayan bir istinad almaq üçün ya əvvəldən push etdiyiniz bir commit-i geri qaytarmalısınız, ya da fərqli bir yerli branch-ı eyni remote branch-a push etməyə çalışmalısınız.
Çox güman ki, server bu siyasəti tətbiq etmək üçün artıq receive.denyDeletes
və receive.denyNonFastForwards
ilə konfiqurasiya olunmuşdur, buna görə tutmağa çalışa biləcəyiniz tək təsadüfi şey, artıq push olunmuş commit-lərin geri qaytarılmasıdır.
Budur bunu yoxlayan bir pre-rebase skriptinin nümunəsi. Yenidən yazmaq istədiyiniz bütün commit-lərin siyahısını alır və remote istinadlarınızdan birində olub olmadığını yoxlayır. Remote istinadlarınızdan birinin əlçatan olduğunu görsə, geri qaytarmanı ləğv edir.
#!/usr/bin/env ruby
base_branch = ARGV[0]
if ARGV[1]
topic_branch = ARGV[1]
else
topic_branch = "HEAD"
end
target_shas = `git rev-list #{base_branch}..#{topic_branch}`.split("\n")
remote_refs = `git branch -r`.split("\n").map { |r| r.strip }
target_shas.each do |sha|
remote_refs.each do |remote_ref|
shas_pushed = `git rev-list ^#{sha}^@ refs/remotes/#{remote_ref}`
if shas_pushed.split("\n").include?(sha)
puts "[POLICY] Commit #{sha} has already been pushed to #{remote_ref}"
exit 1
end
end
end
Bu skript Reviziya Seçimi-də əhatə olunmayan bir sintaksisdən istifadə edir. Bunu işə salmaqla əvvəlcədən push edilmiş commit-lərin siyahısını alırsınız:
`git rev-list ^#{sha}^@ refs/remotes/#{remote_ref}`
SHA^@
sintaksisi, bu vəzifəni yerinə yetirən bütün valideynləri həll edir.
Remote-dakı son işdən əldə edilə bilən və push etməyə çalışdığınız SHA-1-lərin hər hansı bir valideynindən əlçatmaz olan hər hansı bir commit axtarırsınız - yəni bu irəliləyişdir.
Bu yanaşmanın əsas çatışmazlığı çox yavaş ola biləcəyi və çox vaxt lazımsız olmasıdır - itələməni -f
ilə məcbur etməyə çalışmasanız, server sizi xəbərdar edəcək və push qəbul etməyəcək.
Bununla birlikdə, bu maraqlı bir məşqdir və nəzəri cəhətdən daha sonra geri qayıtmaq və düzəltmək məcburiyyətində qalmağınızın qarşısını almağa kömək edə bilər.