Git
Chapters ▾ 2nd Edition

A2.2 Dodatek B: Vdelava Gita v vašo aplikacijo - Libgit2

Libgit2

Druga možnost na vašem dosegu je uporabiti Libgit2. Libgit2 je neodvisna implementacija Gita s poudarkom imeti dober API za uporabo znotraj ostalih programov. Lahko ga najdete na https://libgit2.org.

Najprej poglejmo, kako je videti C API. Tu je vihravi ogled:

// 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);

Prvih nekaj vrstic odpre repozitorij Git. Tip git_repository predstavlja oprimek na repozitorij s predpomnilnikom v spominu. To je najenostavnejša metoda, ko poznate točno pot do delovnega direktorija repozitorija ali direktorij .git. Obstaja tudi git_repository_open_ext, ki vključuje možnosti za iskanje, git_clone s prijatelji za izdelavo lokalnega klona oddaljenega repozitorija in git_repository_init za izdelavo celotnega novega repozitorija.

Drug kos kode uporablja sintakso rev-parse (glejte razdelek Reference vej za več o tem), da dobi potrditev, na katero HEAD eventualno kaže. Vrnjeni tip je kazalec git_object, ki predstavlja nekaj, kar obstaja v objektni podatkovni bazi Gita za repozitorij. git_object je dejansko »nadrejeni« tip za nekaj različnih vrst objektov; postavitev spomina za vsakega od »podrejenih« tipov je enak za git_object, tako da lahko varno oddate pravega. V tem primeru bi git_object_type(commit) vrnil GIT_OBJ_COMMIT, torej je varno potrditi kazalec git_commit.

Naslednji kos prikazuje dostop do lastnosti potrditve. Zadnja vrstica tu uporablja tip git_oid; to je predstavitev Libgit2 za zgoščeno vrednost SHA-1.

Iz tega primera se je začelo pojavljati nekaj vzorcev:

  • Če določite kazalec in podate referenco nanj v klicu Libgit2, bo ta klic verjetno vrnil kodo napake celega števila. Vrednost 0 kaže uspeh; karkoli manjšega je napaka.

  • Če Libgit2 zapolni kazalec za vas, ste odgovorni za njegovo izpustitev.

  • Če Libgit2 iz klica vrne kazalec const, vam ga ni treba izpustiti, vendar bo postal neveljaven, ko je objekt, ki mu pripada, izpuščen.

  • Pisanje C-ja je nekoliko mučno.

Zadnje pomeni, da ni zelo verjetno, da boste pisali C, ko uporabljate Libgit2. Na srečo je na voljo število jezikom specifičnih vezav, ki naredijo delo z repozitoriji Git iz vašega določenega jezika in okolja precej enostavno. Poglejmo zgornji primer napisan z vezavami Ruby za Libgit2, ki so poimenovane Rugged, in lahko jih najdete na https://github.com/libgit2/rugged.

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

Kot lahko vidite, je koda veliko manj natrpana. Najprej, Rugged uporablja izjeme; lahko vrne stvari, kot so ConfigError ali ObjectError za signalizacijo pogojev napak. Drugič, ne obstaja nobena eksplicitna sprostitev virov, ker Ruby sprošča pomnilnik (angl. garbage collection). Poglejmo nekoliko bolj zapleten primer: obdelovanje potrditve od začetka:

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. Ustvarite novo zbirko binarnih podatkov (blob), ki vsebuje vsebino nove datoteke.

  2. Zapolnite indeks z drevesom potrditve glave in dodajte novo datoteko v pot newfile.txt.

  3. To ustvari novo drevo v ODB in ga uporablja za novo potrditev.

  4. Uporabljamo enak podpis tako za avtorja kot tudi za polja potrditve.

  5. Sporočilo potrditve.

  6. Ko se ustvarja potrditev, morate določiti nove nadrejene potrditve. To uporablja vrh HEAD-a za eno nadrejeno.

  7. Rugged (in Libgit2) lahko opcijsko posodobi referenco, ko dela potrditev.

  8. Vrnjena vrednost je zgoščena vrednost SHA-1 novega objekta potrditve, kar lahko potem uporabite, da dobite objekt Commit.

Koda Ruby je lepa in čista, vendar ker Libgit2 izvaja težka opravila, se bo ta koda tudi poganjala hitro. Če niste rubist, se bomo dotaknili nekaterih ostalih povezav v Ostale vezave.

Napredna funkcionalnost

Libgit2 ima nekaj zmožnosti, ki so izven obsega jedra Git. En primer je možnost vtičnikov: Libgit2 vam omogoča ponujati prilagojena »ozadja« za nekaj tipov operacij, tako da lahko shranite stvari na različne načine, kot jih počne goli Git. Libgit2 med drugimi stvarmi omogoča prilagojena ozadja za nastavitve, shranjevanje ref in objektno podatkovno bazo.

Poglejmo, kako to deluje. Koda spodaj je izposojena iz skupka primerov ozadja ponujenih s strani ekipe Libgit2 (kar je moč najti na https://github.com/libgit2/libgit2-backends). Tako se nastavi prilagojeno ozadje za objektno podatkovno bazo:

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)

Pomnite, da so napake zajete, vendar niso upravljane. Upamo, da je vaša koda boljša od naše.

  1. Inicializacija »ospredja« prazne objektne podatkovne baze (ODB), ki se bo obnašalo kot kontejner za »ozadja«, ki pa so tista, ki delajo pravo delo.

  2. Inicializacija prilagojenega ozadja ODB.

  3. Dodajanje ozadja k ospredju.

  4. Odpiranje repozitorija in njegova nastavitev, da uporablja našo ODB za iskanje objektov.

Vendar, kaj je ta stvar git_odb_backend_mine? Torej, to je konstruktor za vašo lastno ODB implementacijo in tam lahko naredite karkoli želite, dokler ustrezno zapolnjujete strukturo v git_odb_backend. Takole bi lahko bilo videti:

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;
}

Najsubtilnejša omejitev tu je, da mora biti prvi član my_backend_struct struktura git_odb_backend; to zagotavlja, da je postavitev spomina to, kar Libgit2 pričakuje, da je. Preostanek je poljuben; ta struktura je lahko tako velika ali majhna, kakor jo potrebujete.

Funkcija inicializacije dodeli nekaj spomina za strukturo, nastavi kontekst po meri in nato zapolni člane strukture parent, ki jo podpira. Poglejmo datoteko include/git2/sys/odb_backend.h v izvorni kodi Libgit2 za celoten skupek podpisov klica; določeni primer uporabe vam bo pomagal določiti, katerega od teh boste želeli podpirati.

Ostale vezave

Libgit2 ima vezave za mnogo jezikov. Tu bomo pokazali majhen primer, ki uporablja nekaj od bolj celovitih vezav paketov od tega pisanja; knjižnice obstajajo za mnoge ostale jezike, vključno C++, Go, Node.js, Erlang in JVM, vse v različnih fazah zrelosti. Uradno zbirko vezav se lahko najde z brskanjem po repozitorijih na https://github.com/libgit2. Koda, ki jo boste pisali, bo vrnila potrditveno sporočilo iz potrditve, ki eventualno kaže na HEAD (neke vrste git log -1).

LibGit2Sharp

Če pišete aplikacijo .NET ali Mono, je Libgit2Sharp (https://github.com/libgit2/libgit2sharp) to, kar iščete. Vezave so napisane v C# in veliko skrbnosti je bilo dane za ovitje surovih klicev Libgit2 s CLR API-ji, ki dajejo bolj domač občutek. Tako je videti naš primer programa:

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

Za namizne aplikacije Windows obstaja celo paket NuGet, ki vam bo pomagal hitro začeti.

objective-git

Če se vaša aplikacija poganja na platformi Apple, verjetno uporabljate objektni C kot vaš jezik implementacije. ObjectiveGit (https://github.com/libgit2/objective-git) je ime vezave Libgit2 za to okolje. Primer programa je videti takole:

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

ObjectiveGit je polno interoperabilen s Swiftom, torej se ne bojte, če ste pustili objektni C zadaj.

pygit2

Vezave za Libgit2 v Pythonu so imenovane Pygit2 in lahko se jih najde na https://www.pygit2.org/. Naš primer programa:

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

Nadaljnje branje

Seveda je polno obravnavanje zmožnosti Libgit2 izven obsega te knjige. Če želite več informacij o samem Libgit2, je na voljo dokumentacija API na https://libgit2.github.com/libgit2 in skupek vodnikov na https://libgit2.github.com/docs. Za ostale povezave preverite zapakiran README in teste; tam so pogostokrat na voljo majhni vodniki in kazalci k nadaljnjemu branju.

scroll-to-top