Git
Chapters ▾ 2nd Edition

A2.3 Appendix B: Вграждане на Git в приложения - JGit

JGit

Ако искате да ползвате Git от Java програма, налична е пълнофункционалната библиотека JGit. JGit е Git имплементация написана на Java и е много популярна в Java общността. Проектът JGit е под шапката на Eclipse и е на адрес https://www.eclipse.org/jgit/.

Настройка

Има няколко начина да свържете проекта си с JGit. Вероятно най-лесният е да използвате Maven — интеграцията се извършва с добавяне на следното в <dependencies> тага на файла pom.xml:

<dependency>
    <groupId>org.eclipse.jgit</groupId>
    <artifactId>org.eclipse.jgit</artifactId>
    <version>3.5.0.201409260305-r</version>
</dependency>

Елементът version вероятно ще е различен по времето, когато четете това, проверете https://mvnrepository.com/artifact/org.eclipse.jgit/org.eclipse.jgit за актуална информация за хранилището. След като това бъде направено, Maven автоматично ще намери и използва JGit библиотеките, които ви трябват.

Ако вместо това сами управлявате binary зависимостите, компилирани JGit binaries има на адрес https://www.eclipse.org/jgit/download. Може да ги интегрирате в проекта си с команди от рода на:

javac -cp .:org.eclipse.jgit-3.5.0.201409260305-r.jar App.java
java -cp .:org.eclipse.jgit-3.5.0.201409260305-r.jar App

Plumbing

JGit има две основни API нива: plumbing и porcelain. Терминологията им идва от самия Git и JGit е разделена на приблизително същите видове области: porcelain API-тата са friendly front-end за основните user-level действия (нещата, които нормално потребителят би използвал с Git в командния ред), докато plumbing API-тата са за директен контакт с low-level обекти в хранилище.

Отправната точка за повечето JGit сесии е класът Repository и първата ни задача е да го инстанциираме в обект. За хранилище от файловата система (да, JGit позволява и други storage модели), това се прави с помощта на FileRepositoryBuilder:

// Create a new repository
Repository newlyCreatedRepo = FileRepositoryBuilder.create(
    new File("/tmp/new_repo/.git"));
newlyCreatedRepo.create();

// Open an existing repository
Repository existingRepo = new FileRepositoryBuilder()
    .setGitDir(new File("my_repo/.git"))
    .build();

Builder-ът има чудесен API за да осигури всички неща, необходими за намиране на Git хранилище без значение дали програмата ви знае къде точно се намира то. Може да използва environment променливи (.readEnvironment()), да започне от място в работната директория и да търси (.setWorkTree(…).findGitDir()), или просто да отвори известна .git директория.

След като вече имате инстанция на Repository, можете да правите всякакви неща с обекта. Бърз пример:

// Get a reference
Ref master = repo.getRef("master");

// Get the object the reference points to
ObjectId masterTip = master.getObjectId();

// Rev-parse
ObjectId obj = repo.resolve("HEAD^{tree}");

// Load raw object contents
ObjectLoader loader = repo.open(masterTip);
loader.copyTo(System.out);

// Create a branch
RefUpdate createBranch1 = repo.updateRef("refs/heads/branch1");
createBranch1.setNewObjectId(masterTip);
createBranch1.update();

// Delete a branch
RefUpdate deleteBranch1 = repo.updateRef("refs/heads/branch1");
deleteBranch1.setForceUpdate(true);
deleteBranch1.delete();

// Config
Config cfg = repo.getConfig();
String name = cfg.getString("user", null, "name");

Тук има доста неща, нека ги разгледаме подред.

Първият ред взема указател към master референцията. JGit автоматично намира действителната master референция, която се пази в refs/heads/master, и връща обект, който позволява да извличате информация за нея. Може да получите името ѝ (.getName()) както и целевия обект на директна референция (.getObjectId()) или референцията сочена от symbolic ref (.getTarget()). Ref обектите се използват също за представяне на tag refs и objects, така че можете да питате дали тагът е “peeled,” което значи че сочи към финалната цел на (потенциално дълъг) стринг от таг обекти.

Вторият ред взема целта на master референцията, която се връща като ObjectId инстанция. ObjectId представлява SHA-1 хеш на обект, който може да съществува или не в базата данни с обекти на Git. Третият ред е подобен, но показва как JGit обработва rev-parse синтаксиса (за повече информация погледнете в Референции към клонове), можете да подадете произволен object specifier, който Git разбира, и JGit ще върне или валиден ObjectId за този обект или null.

Следващите два реда показват как да заредите raw съдържанието на обект. В този пример ние извикваме ObjectLoader.copyTo() за да пратим съдържанието на обекта директно към stdout, но ObjectLoader има също методи за четене на типа и размера на обектa и може също така да ги върне като byte масив. За големи обекти (където .isLarge() връща true), може да извикате .openStream() за да получите подобен на InputStream обект способен да чете object данни без да ги изтегля в паметта изцяло.

Следващите няколко реда показват какво е необходимо за създаване на клон. Създаваме RefUpdate обект, конфигурираме малко параметри и извикваме .update() за да активираме промяната. Директно след това идва кодът за изтриване на същия клон. Отбележете, че .setForceUpdate(true) е необходимо условие за това, в противен случай .delete() повикването ще върне REJECTED и няма да се случи нищо.

Последният пример показва как да извлечем конфигурационна стойност на Git, user.name. Тази Config инстанция използва отвореното по-рано хранилище за локалната конфигурация, но също така може да установи автоматично глобалните и системни конфигурационни файлове и да чете и от тях.

Това е само малка част от plumbing API-тата, съществуват много други методи и класове. Тук също не показахме как JGit обработва грешки, това става с exceptions. JGit API-тата понякога хвърлят стандартни Java exceptions (като например IOException), но съществуват и JGit-специфични типове изключения (като NoRemoteRepositoryException, CorruptObjectException, и NoMergeBaseException).

Porcelain

Plumbing API-тата са сравнително изчерпателни, но понякога може да е тромаво да се използват за постигане на тривиални задачи като добавяне на файл в индекса или създаването на нов къмит. JGit предлага набор API-та от по-високо ниво посредством класа Git:

Repository repo;
// construct repo...
Git git = new Git(repo);

Git класът притежава чудесна колекция от high-level методи в builder стил, които могат да се използват за реализиране на доволно сложни сценарии. Да видим пример — ще направим нещо еквивалентно на git ls-remote:

CredentialsProvider cp = new UsernamePasswordCredentialsProvider("username", "p4ssw0rd");
Collection<Ref> remoteRefs = git.lsRemote()
    .setCredentialsProvider(cp)
    .setRemote("origin")
    .setTags(true)
    .setHeads(false)
    .call();
for (Ref ref : remoteRefs) {
    System.out.println(ref.getName() + " -> " + ref.getObjectId().name());
}

Това е стандартно използване на Git класа, методите връщат команден обект, който ви позволява да правите chaining на повикванията им за да задавате параметри и да ги стартирате с .call(). В този случай ние питаме origin референцията за тагове, но не и за heads. Също така, обърнете внимание на обекта CredentialsProvider, който се ползва за автентикация.

Много други команди са достъпни през класа Git, включително add, blame, commit, clean, push, rebase, revert, и reset.

Повече информация

Това е само малка демонстрация на възможностите на JGit. Ако се интересувате и искате да научите повече, ето къде да потърсите допълнителна информация и вдъхновение:

  • Официалната JGit API документация на адрес https://www.eclipse.org/jgit/documentation/. Това са стандартни Javadoc, така че любимата ви JVM IDE ще може да ги инсталира и локално.

  • Хранилището JGit Cookbook на адрес https://github.com/centic9/jgit-cookbook съдържа много примери за извършване на специфични дейности с JGit.