Git
Chapters ▾ 2nd Edition

A2.3 Appendix B: Встраивание Git’а в ваши приложения - JGit

JGit

Если вы хотите использовать Git из Java-программ, существует библиотека для работы с Git, называемая JGit. Она достаточно полно реализует функциональность Git и написана полностью на Java. Эта библиотека получила широкое распространение в Java-мире. Проект JGit находится под опекой Eclipse и расположен по адресу http://www.eclipse.org/jgit.

Приступая к работе

Существует несколько способов добавить JGit в проект и начать писать код с использованием предоставляемого API. Возможно, самый простой путь — использование Maven. Подключение библиотеки происходит путём добавления следующих строк в секцию <dependencies> в вашем pom.xml:

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

С момента выхода книги скорее всего появились новые версии JGit, проверьте обновления на 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

Служебный API

У JGit есть два уровня API: служебный ("plumbing" API, "трубопровод") и пользовательский ("porcelain" API, "фарфор"). Эта терминология заимствована из самого Git и JGit разделён на две части: "фарфоровый" API предоставляет удобные методы для распространённых задач прикладного уровня (тех, для решения которых вы бы использовали обычные Git-команды) и "сантехнический" API для прямого взаимодействия с низкоуровневыми объектами репозитория.

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

// Создание нового репозитория; директория должна существовать
Repository newlyCreatedRepo = FileRepositoryBuilder.create(
    new File("/tmp/new_repo/.git"));

// Открыть существующий репозиторий
Repository existingRepo = new FileRepositoryBuilder()
    .setGitDir(new File("my_repo/.git"))
    .build();

Вызовы методов билдера можно объединять в цепочку чтобы указать всю информацию для поиска репозитория независимо от того, знает ли ваша программа его точное месторасположение или нет. Можно читать системные переменные (.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()). Объекты типа Ref также служат для представления ссылок на теги и самих тегов; вы можете узнать, является ли тег "конечным" ("peeled"), т.е. ссылается ли он на целевой объект потенциально длинной цепи тегов.

Вторая строка получает объект на который указывает ссылка master в виде ObjectId. ObjectId представляют SHA-1 хэш объекта, который, возможно, сохранён внутри базы данных объектов Git. Следующая строка похожа на предыдущую, но используется rev-parse синтаксис (см. детали в Ссылки на ветки); вы можете использовать любой, подходящий формат и JGit вернёт либо валидный ObjectId для указанного объекта, либо null.

Следующие две строки показывают, как можно получить содержимое объекта. В этом примере мы используем ObjectLoader.copyTo() чтобы передать содержимое файла прямиком в stdout, но у ObjectLoader есть методы для чтения типа и размера объекта, а также для считывания объекта в виде массива байтов. Для больших объектов (у которых .isLarge() возвращает true) можно использовать метод .openStream() для открытия потока последовательного чтения объекта без полной загрузки в память.

Следующие строки показывают, как создать новую ветку. Мы создаём объект типа RefUpdate, устанавливаем некоторые параметры и вызываем метод .update() чтобы инициировать изменение. После этого мы удаляем эту же ветку. Обратите внимание на необходимость вызова .setForceUpdate(true) для корректной работы; иначе вызов .delete() вернёт REJECTED и ничего не произойдёт.

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

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

Пользовательский API

Служебные API достаточно всеобъемлющи, но сложны в использовании для простых задач вроде добавления файла в индекс или создания нового коммита. У JGit есть API более высокого уровня, входная точка в который — это класс Git:

Repository repo;
// создание репозитория...
Git git = new Git(repo);

В классе Git можно найти отличный набор высокоуровневых "текучих" методов (builder-style / fluent interface). Давайте взглянем на пример — результат выполнения этого кода смахивает на 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. Обратите внимание на использование класса CredentialsProvider для аутентификации.

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

Дальнейшее чтение

Это лишь небольшой пример всех возможностей JGit. Если вы заинтересованы в более детальной работе с JGit, вот список источников информации для старта:

  • Официальная документация по JGit API доступна в Интернете на http://download.eclipse.org/jgit/docs/latest/apidocs. Это обыкновенный Javadoc, так что ваша любимая IDE может скачать её и использовать оффлайн.

  • "Поваренная книга" JGit, расположенная по адресу https://github.com/centic9/jgit-cookbook включает в себя много готовых "рецептов" использования JGit для решения тех или иных задач.

  • Вопрос на StackOverflow http://stackoverflow.com/questions/6861881 содержит несколько полезных ссылок.