Git
Chapters ▾ 2nd Edition

A2.2 Додатак B: Уграђивање програма Гит у ваше апликације - Libgit2

Libgit2

Још једна доступна опција је да употребите Libgit2. Libgit2 је Гит имплементација која нема зависности, са тежиштем на лепом API која је предвиђена за употребу у другим програмима. Можете да је пронађете на адреси http://libgit2.github.com.

Најпре, хајде да погледамо како изгледа C API. Ево вртоглаве турнеје:

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

Првих неколико линија отвара Гит репозиторијум. Тип git_repository представља ручку репозиторијума са кешом у меморији. Ово је најједноставнија метода, када тачно знате путању до радног директоријума репозиторијума, или до .git директоријума. Постоји и git_repository_open_ext који има опције претраге, git_clone и пријатељи за креирање локалног клона удаљеног репозиторијума и git_repository_init за креирање потпуно новог репозиторијума.

Друго парче кода користи rev-parse синтаксу (за више информација о овоме, погледајте Референце грана) да дође до комита на који показује HEAD. Повратни тип је git_object показивач, који представља нешто што постоји у Гит бази података објеката за репозиторијум. git_object је уствари „родитељски” тип за неколико различитих врста објеката; распоред меморије за сваку врсту „дете” типова је исти као за git_object, тако да безбедно можете да кастујете у одговарајући. У овом случају, git_object_type(commit) би вратило GIT_OBJ_COMMIT, тако да је безбедно да се кастује у git_commit показивач.

Следеће парче показује како да се приступи особинама комита. Последња линија овде користи тип git_oid; ово је Libgit2 репрезентација SHA-1 хеша.

Из овог примера, почело је да се појављује неколико шаблона:

  • Ако декларишете показивач и проследите референцу на њега у Libgit2 позив, тај позив ће вероватно да врати целобројни кôд грешке. Вредност 0 означава успех; све мање од тога је грешка.

  • Ако Libgit2 за вас попуни показивач, ваш је задатак да га ослободите.

  • Ако Libgit2 из позива врати const показивач, не морате да га ослободите, али ће престати да важи када се ослободи објекат којем припада.

  • Писање C кода је помало болно.

Последње значи да је мало вероватно да ћете писати на језику C када користите Libgit2. На срећу, постоји већи број везивања за одређене језике која знатно олакшавају рад са Гит репозиторијумима из вашег одређеног језика и окружења. Хајде да погледамо претходни пример написан Руби везивањима за Libgit2, која се зову Rugged, и можете да их пронађете на адреси 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

Као што видите, кôд је много мање запетљан. Прво, Rugged користи изузетке; може да баци ствари као што су ConfigError или ObjectError којима сигнализира стања грешке. Друго, нема експлицитног ослобађања ресурса, јер језик Руби скупља ђубре. Хајде да погледамо мало компликованији пример: креирање комита од самог почетка:

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. Креира нови блоб у којем се налази садржај новог фајла.

  2. Попуњава индекс са стаблом head комита, па додаје нови фајл на путању newfile.txt.

  3. Ово креира ново стабло у ODB (бази података објеката), па га користи за нови комит.

  4. Користимо исти потпис и за поље аутора и за поље комитера.

  5. Комит порука.

  6. Када се креира комит, морате да наведете родитеље новог комита. Ово користи врх HEAD референце као једног родитеља.

  7. RuggedLibgit2) могу необавезно да ажурирају референцу када праве комит.

  8. Враћена вредност је SHA-1 хеш новог комит објекта коју онда можете употребити да добијете Commit објекат.

Руби кôд је фин и чист, али пошто Libgit2 диже највећи део тежине, овај кôд ће се такође и извршавати прилично брзо. Ако нисте рубиста, дотичемо се и осталих везивања у Остала везивања.

Напредна функционалност

Libgit2 поседује неколико могућности вам опсега Гит језгра. Један од примера је проширивост: Libgit2 вам дозвољава да доставите прилагођене „позадинске механизме” за неколико врста операција, тако да ствари можете складиштити на начин који се разликује од онога који користи програм Гит. Libgit2 између осталог дозвољава прилагођене позадинске механизме за конфигурацију, складиштење референци и базу објеката.

Хајде да видимо како ово функционише. Кôд који следи је позајмљен из скупа примера позадинских механизама које је представио Libgit2 тим (и могу да се пронађу на адреси https://github.com/libgit2/libgit2-backends). Ево како се поставља прилагођени позадински механизам за базу података објеката:

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)

Приметите да се грешке хватају, али се не обрађују. Надамо се да је ваш кôд бољи од нашег.

  1. Иницијализује „чеони део” празне базе података објеката (ODB), која ће служити као контејнер за позадинске механизме који уствари одрађују прави посао.

  2. Иницијализује прилагођени ODB позадински механизам.

  3. Додаје позадински механизам у чеони део.

  4. Отвара репозиторијум и подешава га да користи нашу ODB за претрагу објеката.

Али шта је та git_odb_backend_mine ствар? Па, то је конструктор ваше сопствене ODB имплементације и ту можете да урадите штагод желите, док год исправно попуните git_odb_backend структуру. Ево како то могло да изгледа:

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

Најсуптилније ограничење овде је да први члан my_backend_struct мора бити git_odb_backend структура; то обезбеђује да распоред меморије буде онакав какав очекује Libgit2 кôд. Остатак је произвољан; ова структура можете бити велика или мала колико год је то потребно.

Функција иницијализације алоцира нешто меморије за структуру, поставља прилагођени контекст, па затим попуњава чланове parent структуре коју подржава. Погледајте фајл include/git2/sys/odb_backend.h у Libgit2 изворном коду да видите комплетан скуп потписа позива; ваш дати случај коришћења ће вам помоћи да одредите које од њих ћете пожелети да подржите.

Остала везивања

Libgit2 има везивања за много језика. Ево малог примера који користи неколико комплетних пакета везивања у време писања овог текста; постоје библиотеке за многе друге језике, укључујући C++, Go, Node.js, Erlang и JVM, сви у различитим фазама зрелости. Званична колекција везивања може да се пронађе прегледом репозиторијума на адреси https://github.com/libgit2. Кôд који ћемо ми написати ће да враћа комит поруку из комита на који коначно указује HEAD (нешто као git log -1).

LibGit2Sharp

Ако пишете .NET или Mono апликацију, LibGit2Sharp (https://github.com/libgit2/libgit2sharp) је оно што тражите. Везивања су написана ма C# и посвећено је доста пажње да се сирови Libgit2 позиви умотају у CLR API који изгледају природно. Ево како изгледа наш програм:

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

За десктоп Виндоуз апликације, постоји чак и NuGet пакет који ће вам помоћи да брзо почнете.

objective-git

Ако се ваша апликација извршава на Епл платформи, највероватније користите Objective-C као свој језик имплементације. Objective-Git (https://github.com/libgit2/objective-git) је име Libgit2 везивања за то окружење. Пример програма изгледа овако:

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

Objective-git у потпуности саражује са Swift, тако да се не морате плашити ако сте оставили Objective-C.

pygit2

Libgit2 везивања за Пајтон се називају Pygit2 и можете да их пронађете на адреси http://www.pygit2.org/. Наш пример програма:

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

Наставак читања

Наравно, потпуни третман Libgit2 могућности је ван опсега ове књиге. Ако желите више информација о самом Libgit2, на адреси https://libgit2.github.com/libgit2 постоји API документација, а скуп водича на адреси https://libgit2.github.com/docs. За остала везивања, проверите испоручени README и тестове; тамо често постоје мали туторијали и смернице за наставак читања.