Git
Chapters ▾ 2nd Edition

A2.2 Appendix B: Proqramlara Git Daxil Etmək - Libgit2

Libgit2

Sərəncamınızdakı başqa bir seçim də Libgit2 istifadə etməkdir. Libgit2, digər proqramların daxilində istifadə üçün gözəl bir API-yə sahib olmağa yönəlmiş Git’in asılılıqsız bir tətbiqidir. https://libgit2.orgsaytında tapa bilərsiniz.

Əvvəlcə C API-nin nə olduğuna nəzər salaq. Və budur, qasırğa turu:

// Open a repository
git_repository *repo;
int error = git_repository_open(&repo, "/path/to/repository");

// Dereference HEAD to a commit
git_object *head_commit;
error = git_revparse_single(&head_commit, repo, "HEAD^{commit}");
git_commit *commit = (git_commit*)head_commit;

// Print some of the commit's properties
printf("%s", git_commit_message(commit));
const git_signature *author = git_commit_author(commit);
printf("%s <%s>\n", author->name, author->email);
const git_oid *tree_id = git_commit_tree_id(commit);

// Cleanup
git_commit_free(commit);
git_repository_free(repo);

İlk iki sətir Git deposunu açır. git_repository növü, yaddaşdakı cache-a sahib bir deponun idarəedicisini təmsil edir. Deponun iş qovluğuna və ya .git qovluğuna gedən yolu dəqiq bildiyiniz zaman bu ən sadə metoddur. Axtarış üçün seçimləri ehtiva edən git_repository_open_ext-də var, git_clone və uzaq bir deponun yerli klonunu hazırlayan dostlar, və tamamilə yeni bir depo yaratmaq üçün git_repository_init istifadə olunur.

Kodun ikinci hissəsi, HEAD-in sonunda göstərdiyi commit-i əldə etmək üçün rev-parse sintaksisindən istifadə edir (bu barədə daha çox məlumat üçün Branch Referansları bax). Geri qaytarılmış tip, bir depo üçün Git obyekt bazasında mövcud olanı təmsil edən git_object göstəricisidir. git_object əslində bir neçə müxtəlif növ obyekt üçün “parent” növüdür; “child” növlərinin hər biri üçün yaddaş düzəni, git_object ilə eynidir, buna görə də doğru birinə ata bilərsiniz. Bu vəziyyətdə, git_object_type(commit) GIT_OBJ_COMMIT qaytarır,buna görə git_commit göstəricisinə ötürülməsi təhlükəsizdir.

Növbəti hissə, commit-in xüsusiyyətlərinə necə çatacağını göstərir. Buradakı son sətirdə git_oid növü istifadə olunur; bu Libgit2’nin bir SHA-1 hash üçün təqdimatıdır.

Bu nümunədən bir-iki pattern ortaya çıxmağa başladı:

  • Bir göstərici elan etsəniz və bir referansı Libgit2 çağırışına göndərsəniz, bu çağırış, ehtimal ki, tam bir error kodu qaytaracaqdır. A `0 'dəyəri müvəffəqiyyəti göstərir; daha az bir şey səhvdir.

  • Libgit2 sizin üçün bir göstəricini doldurursa, onu sərbəst buraxmağa cavabdehsiniz.

  • Libgit2 bir çağırışdan const göstəricisini qaytarırsa, onu sərbəst buraxmanız lazım deyil, ancaq aid olduğu obyekt sərbəst buraxıldıqda etibarsız olacaqdır.

  • C yazmaq biraz ağrılıdır.

Bu son, Libgit2 istifadə edərkən C yazacağınızın çox ehtimal olunmadığını göstərir. Xoşbəxtlikdən, xüsusi dilinizdən və mühitinizdən Git depoları ilə işləməyi asanlaşdıran bir sıra dilə bağlı bağlamalar mövcuddur. Güclü adlanan və https://github.com/libgit2/rugged ünvanında olan Libgit2 üçün Ruby bağlayıcılarından istifadə edərək yazılmış yuxarıdakı nümunəyə nəzər salaq.

repo = Rugged::Repository.new('path/to/repository')
commit = repo.head.target
puts commit.message
puts "#{commit.author[:name]} <#{commit.author[:email]}>"
tree = commit.tree

Gördüyünüz kimi, kod daha az qarışıqdır. Birincisi, Rugged istisnalardan istifadə edir; səhv şərtlərinə siqnal vermək üçün ConfigError və ya ObjectError kimi şeyləri qaldıra bilər. İkincisi, resursların açıq şəkildə sərbəst buraxılması yoxdur, çünki Ruby garbage-collected-dir. Bir az daha mürəkkəb bir nümunəyə nəzər salaq: sıfırdan commit hazırlamaq

blob_id = repo.write("Blob contents", :blob) # (1)

index = repo.index
index.read_tree(repo.head.target.tree)
index.add(:path => 'newfile.txt', :oid => blob_id) # (2)

sig = {
    :email => "bob@example.com",
    :name => "Bob User",
    :time => Time.now,
}

commit_id = Rugged::Commit.create(repo,
    :tree => index.write_tree(repo), # (3)
    :author => sig,
    :committer => sig, # (4)
    :message => "Add newfile.txt", # (5)
    :parents => repo.empty? ? [] : [ repo.head.target ].compact, # (6)
    :update_ref => 'HEAD', # (7)
)
commit = repo.lookup(commit_id) # (8)
  1. Yeni bir faylın məzmunu olan yeni bir blob yaradın.

  2. İndeksi başlıqlı commit ağac ilə doldurun və yeni faylı newfile.txt yoluna əlavə edin.

  3. Bu, ODB-də yeni bir ağac yaradır və onu yeni commit üçün istifadə edir.

  4. Həm müəllif, həm də müəllif sahələri üçün eyni imzanı istifadə edirik.

  5. Commit mesajı.

  6. Commit yaradarkən, yeni commit-in valideynlərini göstərməlisiniz. Bu, tək valideyn üçün HEAD tip-indən istifadə edir.

  7. Rugged (və Libgit2) bir öhdəlik götürərkən istəyə görə bir referansı yeniləyə bilər.

  8. Qayıdış dəyəri yeni bir əmr obyekti olan SHA-1 hash-ıdır, bundan sonra Commit obyektini əldə etmək üçün istifadə edə bilərsiniz.

Ruby kodu gözəl və təmizdir, lakin Libgit2 heavy lifting etdiyindən bu kod da çox sürətli işləyəcəkdir. Rubyist deyilsinizsə, Other Bindings bəzi digər bağlantılara toxunuruq.

Ətraflı funksionallıq

Libgit2, Git əsas xaricində olmayan bir neçə xüsusiyyətə malikdir. Bir nümunə taxıla biləndir: Libgit2, bir neçə növ əməliyyat üçün xüsusi “arxa planlar” təmin etməyə imkan verir, beləliklə şeyləri Git-dən fərqli bir şəkildə saxlaya bilərsiniz. Libgit2, digər şeylər arasında, konfiqurasiya, ref storage və obyekt verilənlər bazası üçün xüsusi geri imkanları verir.

Gəlin bunun necə işlədiyini nəzərdən keçirək. Aşağıdakı kod, Libgit2 komandası tərəfindən təmin edilmiş geri nümunələr toplusundan götürüldü ( https://github.com/libgit2/libgit2-backends). Obyekt databazası üçün xüsusi bir geribildirim necə qurulur:

git_odb *odb;
int error = git_odb_new(&odb); // (1)

git_odb_backend *my_backend;
error = git_odb_backend_mine(&my_backend, /*…*/); // (2)

error = git_odb_add_backend(odb, my_backend, 1); // (3)

git_repository *repo;
error = git_repository_open(&repo, "some-path");
error = git_repository_set_odb(repo, odb); // (4)

Səhvlərin tutulduğunu, ancaq handle edilmədiyini unutmayın. Ümid edirik ki, kodunuz bizimkindən daha yaxşıdır.__

  1. Həqiqi işi görənlər “backends” üçün konteyner rolunu oynayacaq boş bir obyekt verilənlər bazasını (ODB) “frontend”-i işə salın.

  2. Xüsusi bir ODB backendi başlatın.

  3. Backend-i frontend-ə əlavə edin.

  4. Bir depo açın və obyektləri axtarmaq üçün ODB-dən istifadə edəcəyik.

Bəs bu git_odb_backend_mine nədir? Yaxşı, bu öz ODB həyata keçirməyiniz üçün konstruktordur və git_odb_backend quruluşunu düzgün doldurduqca orada istədiyinizi edə bilərsiniz. Budur nəyə bənzədiyinə baxaq:

typedef struct {
    git_odb_backend parent;

    // Some other stuff
    void *custom_context;
} my_backend_struct;

int git_odb_backend_mine(git_odb_backend **backend_out, /*…*/)
{
    my_backend_struct *backend;

    backend = calloc(1, sizeof (my_backend_struct));

    backend->custom_context = …;

    backend->parent.read = &my_backend__read;
    backend->parent.read_prefix = &my_backend__read_prefix;
    backend->parent.read_header = &my_backend__read_header;
    // …

    *backend_out = (git_odb_backend *) backend;

    return GIT_SUCCESS;
}

Buradakı ən incə bir məhdudiyyət, budur ki my_backend_struct-ın ilk üzvü git_odb_backend quruluşu olmalıdır; bu yaddaş sxeminin Libgit2 kodunun olmasını gözlədiyini təmin edir. Qalan hissəsi özbaşınadır; bu quruluş lazım olduğunuz qədər böyük və ya kiçik ola bilər.

Başlatma funksiyası quruluş üçün bir az yaddaş ayırır, xüsusi kontekst qurur və sonra dəstəklədiyi parent strukturunun üzvlərini doldurur. Tam çağırış imzaları dəsti üçün Libgit2 mənbəyindəki include/git2/sys/odb_backend.h faylına nəzər yetirin; xüsusi istifadə vəziyyətiniz bunlardan hansını dəstəkləmək istədiyinizi müəyyənləşdirməyə kömək edəcəkdir.

Other Bindings

Libgit2 bir çox dildə bağlanır. Bu yazıdan etibarən daha dolğun bağlama paketlərindən bir neçəsini istifadə edərək kiçik bir nümunə göstərdik; kitabxanalar C++, Go, Node.js, Erlang, və JVM, daxil olmaqla bir çox başqa dillər üçün mövcuddur. Rəsmi bağlamalar kolleksiyasını https://github.com/libgit2 saytındakı depolara baxmaqla tapa bilərsiniz. Yazacağımız kod HEAD tərəfindən göstərilən commit-in öhdəsindən gələn mesajı qaytaracaqdır (git log -1 kimi).

LibGit2Sharp

Bir .NET və ya Mono tətbiqi yazırsınızsa, LibGit2Sharp (https://github.com/libgit2/libgit2sharp) axtardığınız şeydir. Bağlamalar C# -də yazılıb və xam Libgit2 zənglərini doğma hiss olunan CLR API-lərlə bağlamağa çox diqqət yetirilib. Nümunə proqramımız belə görünür:

new Repository(@"C:\path\to\repo").Head.Tip.Message;

Masaüstü Windows tətbiqetmələrində, işə başlamağınıza kömək edəcək bir NuGet paketi də var.

objective-git

Tətbiqiniz bir Apple platformasında işləyirsə, ehtimal ki, tətbiq dili olaraq Objective-C istifadə edirsiniz. Obyektiv-Git (https://github.com/libgit2/objective-git) bu mühit üçün Libgit2 bağlamalarının adıdır. Nümunə proqram belə görünür:

GTRepository *repo =
    [[GTRepository alloc] initWithURL:[NSURL fileURLWithPath: @"/path/to/repo"] error:NULL];
NSString *msg = [[[repo headReferenceWithError:NULL] resolvedTarget] message];

Objective-git Swift ilə tam qarşılıqlı əlaqəlidir, buna görə də Objective-C-ni geridə qoymusunuzsa qorxmayın.

pygit2

Pythondakı Libgit2 üçün bağlamalara Pygit2 deyilir və https://www.pygit2.org-də tapa bilərsiniz. Nümunə proqramımız:

pygit2.Repository("/path/to/repo") # open repository
    .head                          # get the current branch
    .peel(pygit2.Commit)           # walk down to the commit
    .message                       # read the message

Əlavə Oxu

Əlbətdə ki, Libgit2-nin imkanlarına tam bir baxış bu kitabın əhatəsi xaricindədir. Libgit2’nin özü haqqında daha çox məlumat istəyirsinizsə, https://libgit2.github.com/libgit2 ünvanında API sənədləri və https://libgit2.github.com/docs adresində bir sıra təlimatlar var.

Digər bağlamalar üçün birləşdirilmiş README və testləri yoxlayın; tez-tez orada daha da oxumaq üçün kiçik təlimatlar və göstəricilər var.