Git 🌙
Chapters ▾ 2nd Edition

A2.3 Додаток B: Вбудовування Git у ваші застосунки - JGit

JGit

Якщо ви бажаєте використовувати Git з Java програми, існує повнофункціональна бібліотека Git під назвою JGit. JGit — це відносно повнофункціональна імплементація Git, написана на Java, та широко використовується в спільноті Java. Проект JGit знаходиться під опікою Eclipse, та його домашню сторінку можна знайти за адресою http://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 напевно збільшиться, коли ви будете це читати; дивіться http://mvnrepository.com/artifact/org.eclipse.jgit/org.eclipse.jgit для оновленої інформації про репозиторій. Щойно це зроблено, Maven автоматично отримає та використає бібліотеки JGit, які вам потрібні.

Якщо ви бажаєте власноруч контролювати двійкові залежності, то зібрані двійкові файли JGit доступні вам за адресою http://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

Кухонне

JGit має два рівні API: кухонний (plumbing — дослівно водопровід) та парадний (porcelain — дослівно порцеляна). Ця термінологія походить зі самого Git, та JGit розділено на приблизно такі самі частини: парадне API — зручний інтерфейс для поширених дій рівня користувача (такі речі, для яких звичайний користувач Git використовує інструмент командного рядку), в той час як кухонне API призначено для взаємодії з низькорівневими обʼєктами сховища напряму.

Більшість сесій JGit починаються з класу Repository, та перше, що ви бажаєте зробити — створити його примірник (instance). Для сховищ заснованих на файлових системах (так, JGit дозволяє інші моделі збереження), це досягається за допомогою FileRepositoryBuilder:

// Створення нового репозиторія
Repository newlyCreatedRepo = FileRepositoryBuilder.create(
    new File("/tmp/new_repo/.git"));
newlyCreatedRepo.create();

// Відкриття існуючого репозиторія
Repository existingRepo = new FileRepositoryBuilder()
    .setGitDir(new File("my_repo/.git"))
    .build();

Будівник (builder) має легкий API для надання всіх даних, які йому потрібні для знайдення репозиторія — знає ваша програма чи ні, де саме його розташовано. Він може використовувати змінні середовища (.readEnvironment()), почати з поточної директорії та шукати (.setWorkTree(…).findGitDir()), чи просто відкрити відому директорію .git, як у прикладі вище.

Щойно ви отримали примірник Repository, ви можете з ним робити будь-що. Ось швидкі приклади:

// Отримати посилання
Ref master = repo.getRef("master");

// Отримати обʼєкт, на яке воно вказує
ObjectId masterTip = master.getObjectId();

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

// Зчитати сирий вміст обʼєкту
ObjectLoader loader = repo.open(masterTip);
loader.copyTo(System.out);

// Створити гілку
RefUpdate createBranch1 = repo.updateRef("refs/heads/branch1");
createBranch1.setNewObjectId(masterTip);
createBranch1.update();

// Вилучити гілку
RefUpdate deleteBranch1 = repo.updateRef("refs/heads/branch1");
deleteBranch1.setForceUpdate(true);
deleteBranch1.delete();

// Конфігурація
Config cfg = repo.getConfig();
String name = cfg.getString("user", null, "name");

Тут сталося чимало, отже розгляньмо кожну секцію окремо.

Перший рядок отримує вказівник на посилання master. JGit автоматично бере справжнє посилання master, яке знаходиться в refs/heads/master, та повертає обʼєкт, який дозволяє вам отримувати інформацію про посилання. Ви можете отримати назву (.getName()), та чи цільовий обʼєкт прямого посилання (.getObjectId()), чи посилання, на яке вказує символічне посилання (.getTarget()). Обʼєкти посилань також використовуються для представлення посилань та обʼєктів теґів, отже ви можете спитати, чи ``очищено'' теґ, тобто чи вказує він на фінальну ціль (можливо довгого) рядку обʼєкту теґу.

Другий рядок отримує ціль посилання master, яке повернено як примірник ObjectId. ObjectId репрезентує SHA-1 хеш обʼєкту, який може існувати чи не існувати в базі даних обʼєктів Git. Третій рядок схожий, проте показує, як JGit працює з синтаксисом rev-parse (докладніше тут: Гілкові посилання); ви можете передати будь-який специфікатор обʼєкту Git, який розуміє Git, та JGit поверне або чинний ObjectId цього обʼєкту, або null.

Наступні два рядки показують, як зчитати сирий вміст обʼєкту. У цьому прикладі, ми викликаємо ObjectLoader.copyTo(), щоб направити вміст обʼєкту напряму до stdout, проте ObjectLoader також має методи для зчитування типу та розміру обʼєкта, а також повернути його як масив байтів. Для великих обʼєктів (для яких .isLarge() повертає true), ви можете викликати .openStream(), щоб отримати подібний до InputStream обʼєкт, який може читати дані сирого об’єкта без копіювання його в памʼять одразу.

Наступні декілька рядків показують, що треба для створення нової гілки. Ми створюємо примірник RefUpdate, налаштовуємо деякі параметри, та викликаємо .update(), щоб зробити зміни. Безпосередньо далі наведено код для вилучення цієї ж гілки. Зауважте, що .setForceUpdate(true) є необхідним щоб це спрацювало; інакше .delete() поверне REJECTED, і нічого не станеться.

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

Це лише маленька вибірка повного кухонного API; у ньому доступно набагато більше методів та класів. Також тут не показано, як JGit обробляє помилки, для чого використовуються винятки. API JGit іноді кидає стандартні винятки Java (такі як IOException), проте також має дерево специфічних для JGit типів винятків, які надає бібліотека (такі як NoRemoteRepositoryException, CorruptObjectException та NoMergeBaseException).

Парадне

Кухонне API доволі повне, проте доволі громіздко складати виклики його функцій разом для вирішення поширених завдань, таких як додавання файлу до індексу, чи створення нового коміту. JGit надає набір API вищого рівня щоб зарадити з цим, і вхідною точкою до цих API є клас Git:

Repository repo;
// Створити репозиторій...
Git git = new Git(repo);

Клас Git має гарний набір високорівневих методів будівників, які можна використати для створення доволі складної поведінки. Подивімося на приклад, який робить щось на кшталт 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; методи повертають обʼєкти команди, що дозволяє вам створювати ланцюжок викликів для встановлення параметрів, які виконуються, коли ви викликаєте .call(). У даному випадку, ми просимо віддалене сховище origin надати теґи, проте не звичайні посилання (heads). Також зверніть увагу на використання обʼєкту CredentialsProvider для автентифікації.

Багато інших команд доступно в класі Git, включно з (проте не тільки) add, blame, commit, clean, push, rebase, revert та reset.

Додаткова література

Це лише маленька вибірка повних можливостей JGit. Якщо ви зацікавлені та бажаєте дізнатись більше, ось де можна пошукати інформацію та натхнення:

  • Офіційну документацію JGit API можна знайти за адресою http://www.eclipse.org/jgit/documentation/. Це стандартний Javadoc, отже ваше улюблене JVM IDE буде в змозі також встановити цю документацію локально.

  • JGit Cookbook (куховарська рецептів) за адресою https://github.com/centic9/jgit-cookbook містить багато прикладів виконання конкретних завдань за допомогою JGit.

scroll-to-top