Git
Chapters ▾ 2nd Edition

A2.2 Appendix B: Vključevanje Git-a v vašo aplikacijo - Libgit2

Libgit2

© Druga opcija na vašem dosegu je uporabiti Libgit2. Libgit2 je neodvisna implementacija Git-a s fokusom na imetju lepega API-ja za uporabo znotraj ostalih programov. Lahko ga najdete na http://libgit2.github.com.

Najprej poglejmo, kako C API izgleda.

Tu je viharen 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 ravnanje repozitorija s predpomnilnikom v spominu. To je najenostavnejša metoda kot veste točno pot delovnega direktorija repozitorija ali direktorij .git. Obstaja tudi git_repository_open_ext, ki vključuje opcije za iskanje, git_clone in prijatelje za izdelavo lokalnega klona oddaljenega repozitorija in git_repository_init za izdelavo celotnega novega repozitorija.

Drug kos kode uporablja rev-parse sinatakso (glejte Branch References za več o tem), da dobi pošiljanje na katerega HEAD eventuelno kaže. Vrnjeni tip je kazalec git_object, ki predstavlja nekaj, kar obstaja v objektni podatkovni bazi Git-a za repozitorij. git_object je dejansko “parent” tip za nekaj različnih vrst objekta; postavitev spomina za vsakega od tipov “child” 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 oddati git_commit kazalec.

Naslednji kos prikazuje dostop do lastnosti pošiljanja. Zadnja vrstica tu uporablja tip git_oid; to je predstavitev Libgit2 za razpršitev SHA-1.

Iz tega primera se je prič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 indicira uspeh; karkoli manjšega je napaka.

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

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

  • Pisanje C-ja je nekoliko mučno.

Zadnja 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 precej enostavno za delo z repozitoriji Git iz vašega določenega jezika in okolja. Poglejmo zgornji primer napisan z vezavami Ruby za Libgit2, ki so poimenovane Rugged in lahko najdene 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 v neredu. Najprej Rugged uporablja izjeme; lahko vrne stvari kot so ConfigError ali ObjectError za signalizacijo pogojev napak. Drugič ni nobenega eksplicitnega izpiščanja izvorov, odkar Ruby zbira odpadke. Poglejmo nekoliko bolj kompliciran primer: obdelovanje pošiljanja 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 nov blog, ki vsebuje vsebno nove datoteke.

  2. Zapolnite index z drevesom pošiljanja glave in dodajte novo datoteko v pot newfile.txt.

  3. To ustvari novo drevo v ODB in ga uporablja za novo pošiljanje.

  4. Uporabljamo enak podpis za tako avtorja kot tudi za polja pošiljanja.

  5. Sporočilo pošiljanja.

  6. Ko se ustvarja pošiljanje, morate določiti nove starše pošiljanja. To uporablja nasvet od HEAD-a za enega starša.

  7. Rugged (in Libgit2) lahko opcijsko posodobi referenco, ko dela pošiljanje.

  8. Vrnjena vrednost je zgostitev SHA-1 novega objekta pošiljanja, kar lahko potem uporabite, da dobite objekt Commit.

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

Napredna funckionalnost

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, ki jih goli Git počne. Libgit2 omogoča prilagojena ozadja za nastavitve, ref shranjevanje in podatkovno bazo objektov med drugimi stvarmi.

Poglejmo, kako to deluje. Koda spodaj je sposojena iz skupka primerov ozadja ponujenih s strani ekipe Libgit2 (kar je moč najti na https://github.com/libgit2/libgit2-backends). Tu je, kako je nastavljeno 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(odb); (4)

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

  1. Inicializacija prazne objektne podatkovne baze (ODB) “ospredje,” ki se bo obnašalo kot kontejner za “ozadja”, ki so tista, ki delajo pravo delo.

  2. Inicializacija prilagojenega ODB ozadja.

  3. Dodajanje ozadnja 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, konstruktor za vašo lastno ODB implementacijo in lahko delate karkoli želite tam, dokler ustrezno zapolnjujete strukturo v git_odb_backend. Tu je, kako bi lahko izgledal:

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

Subtilna omejitev tu je, da mora biti prvi član my_backend_struct-a struktura git_odb_backend; to zagotavlja, da je postavitev spomina to, kar Libgit2 pričakuje, da je. Preostanek je arbitraren; 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 izvoru Libgit2 za celoten skupek podpisov klica; vaš določen 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žnjice obstojajo za mnoge ostale jezike, vključno C++, Go, Node.js, Erlang in JVM vse v različnih faza zrelosti. Uradno zbirko vezav se lahko najde z brskanjem po repozitorijih na https://github.com/libgit2. Koda, ki jo boste pisali, bo vrnila sporočilo pošiljanja iz pošiljanja, ki eventuelno kaže na HEAD (neke vrste git log -1).

LibGit2Sharp

Če pišete .NET ali Mono aplikacijo, 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 z nativnim občutkom CLR API-jev. Tu je, kako izgleda naš primer programa:

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

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

objective-git

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

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

Objektivni git je polno interoperabilen s Swift-om, torej se ne bojte, če ste izpustili objektni C zadaj.

pygit2

Vezave za Libgit2 v Python-u so imenovane Pygit2 in se jih lahko najde na http://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 polno obravnavanje zmožnosti Libgit2 je izven obsega te knjige. Če želite več informacij o samem Libgit2, je na voljo dokumentacija API na https://libgit2.github.com/libgit2 in skupek vodičev na https://libgit2.github.com/docs. Za ostale povezave preverite zapakiran README in teste; tam so pogostokrat na voljo majhni vodiči in kazalci k nadaljnem branju.