-
1. Вступ
- 1.1 Про систему контролю версій
- 1.2 Коротка історія Git
- 1.3 Основи Git
- 1.4 Git, зазвичай, тільки додає дані
- 1.5 Три стани
- 1.6 Командний рядок
- 1.7 Інсталяція Git
- 1.8 Початкове налаштування Git
- 1.9 Отримання допомоги
- 1.10 Підсумок
-
2. Основи Git
- 2.1 Створення Git-сховища
- 2.2 Запис змін до репозиторія
- 2.3 Перегляд історії комітів
- 2.4 Скасування речей
- 2.5 Взаємодія з віддаленими сховищами
- 2.6 Теґування
- 2.7 Псевдоніми Git
- 2.8 Підсумок
-
3. Галуження в git
- 3.1 Гілки у кількох словах
- 3.2 Основи галуження та зливання
- 3.3 Управління гілками
- 3.4 Процеси роботи з гілками
- 3.5 Віддалені гілки
- 3.6 Перебазовування
- 3.7 Підсумок
-
4. Git на сервері
- 4.1 Протоколи
- 4.2 Отримання Git на сервері
- 4.3 Генерація вашого публічного ключа SSH
- 4.4 Налаштування Серверу
- 4.5 Демон Git
- 4.6 Розумний HTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 Варіанти стороннього хостингу
- 4.10 Підсумок
-
5. Розподілений Git
-
6. GitHub
-
7. Інструменти Git
- 7.1 Вибір ревізій
- 7.2 Інтерактивне індексування
- 7.3 Ховання та чищення
- 7.4 Підписання праці
- 7.5 Пошук
- 7.6 Переписування історії
- 7.7 Усвідомлення скидання (reset)
- 7.8 Складне злиття
- 7.9 Rerere
- 7.10 Зневадження з Git
- 7.11 Підмодулі
- 7.12 Пакування
- 7.13 Заміна
- 7.14 Збереження посвідчення (credential)
- 7.15 Підсумок
-
8. Налаштування Git
-
9. Git and Other Systems
- 9.1 Git як клієнт
- 9.2 Міграція на Git
- 9.3 Підсумок
-
10. Git зсередини
- 10.1 Кухонні та парадні команди
- 10.2 Об’єкти Git
- 10.3 Посилання Git
- 10.4 Файли пакунки
- 10.5 Специфікація посилань (refspec)
- 10.6 Протоколи передачі
- 10.7 Супроводження та відновлення даних
- 10.8 Змінні середовища
- 10.9 Підсумок
-
A1. Додаток A: Git в інших середовищах
- A1.1 Графічні інтерфейси
- A1.2 Git у Visual Studio
- A1.3 Git в Eclipse
- A1.4 Git у Bash
- A1.5 Git у Zsh
- A1.6 Git у Powershell
- A1.7 Підсумок
-
A2. Додаток B: Вбудовування Git у ваші застосунки
- A2.1 Git з командного рядка
- A2.2 Libgit2
- A2.3 JGit
- A2.4 go-git
-
A3. Додаток C: Команди Git
- A3.1 Налаштування та конфігурація
- A3.2 Отримання та створення проектів
- A3.3 Базове збереження відбитків
- A3.4 Галуження та зливання
- A3.5 Поширення й оновлення проектів
- A3.6 Огляд та порівняння
- A3.7 Зневаджування
- A3.8 Латання (patching)
- A3.9 Електронна пошта
- A3.10 Зовнішні системи
- A3.11 Адміністрування
- A3.12 Кухонні команди
A2.2 Додаток B: Вбудовування Git у ваші застосунки - Libgit2
Libgit2
Інша опція для ваших послуг — використовувати Libgit2. Libgit2 це вільна від залежностей реалізація Git, яка фокусується на гарному API для використання іншими програмами. Ви можете знайти його за адресою http://libgit2.github.com.
Спершу, спробуймо подивитись на те, як виглядає C API. Ось тур чвалом:
// Відкрити репозиторій
git_repository *repo;
int error = git_repository_open(&repo, "/path/to/repository");
// Отримати коміт, на який вказує HEAD
git_object *head_commit;
error = git_revparse_single(&head_commit, repo, "HEAD^{commit}");
git_commit *commit = (git_commit*)head_commit;
// Вивести деякі властивості коміту
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);
// Прибирання
git_commit_free(commit);
git_repository_free(repo);
Перші декілька рядків відкривають репозиторій Git.
Тип git_repository
є дескриптором репозиторія з кешем у памʼяті.
Це також найпростіший метод, вам треба лише знати точний шлях до робочої директорії або директорії .git
репозиторія.
Є також git_repository_open_ext
, яка включає опції для пошуку, git_clone
та подібні для створення локального клону віддаленого сховища, та git_repository_init
для створення цілковито нового сховища.
Другий шматок коду використовує синтаксис rev-parse (докладніше в Гілкові посилання), щоб отримати коміт, на який врешті-решт вказує HEAD.
Результуючий тип — вказівник на git_object
, який відповідає чомусь, що існує в базі даних обʼєків Git в репозиторії.
git_object
насправді є батьківським'' типом для декількох різних типів обʼєктів; розташування памʼяті для кожного з типів
нащадків'' такий саме, як для git_object
, щоб ви могли безпечно проводити до правильного.
У даному випадку, git_object_type(commit)
має повернути GIT_OBJ_COMMIT
, отже його безпечно приводити до вказівника на git_commit
.
Наступна частина демонструє нам доступ до властивостей коміту.
Останній рядок використовує тип git_oid
; це представлення SHA-1 хешу в Libgit2.
З цього прикладу, видніються декілька загальних правил:
-
Якщо оголосити вказівник та передати посилання на нього до виклику Libgit2, то цей виклик імовірно поверне цілочисельний код помилки. Значення
0
означає успіх; будь-що менше — помилку. -
Якщо Libgit2 заповнює вказівник для вас, то ви відповідальні за його звільнення.
-
Якщо виклик Libgit2 повертає
константний
вказівник, то ви не маєте його звільняти, проте він стане нечинним, коли звільнено обʼєкт, якому він належить. -
Писати на C трохи боляче.
Це останнє означає, що дуже малоймовірно, що ви будете писати на C для використання Libgit2. На щастя, доступно чимало привʼязувань до окремих мов, що робить роботу зі сховищами Git з вашої окремої мови та середовища доволі легкою. Погляньмо на вищенаведений приклад, який написано за допомогою привʼязки Libgit2 для Ruby, яка називається 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
, щоб повідомити про помилкові ситуації.
По-друге, немає ніякого явного звільнення ресурсів, оскільки Ruby має збирання сміття.
Погляньмо на трохи складніший приклад: створення коміту з нуля
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)
-
Створити новий блоб, який містить вміст нового файлу.
-
Наповнити індекс верхівкою дерева коміту, та додати новий файл під шляхом
newfle.txt
. -
Це створює нове дерево в ODB, та використовує його для нового коміту.
-
Ми використовуємо однаковий підпис як для автора, так і для автора коміту.
-
Повідомлення коміту.
-
Під час створення коміту, ви маєте задати батьків нового коміту. Це використовує верхівку HEAD для єдиного батька.
-
Rugged (та Libgit2) може додатково оновити посилання під час створення коміту.
-
Повертається значення SHA-1 хешу нового обʼєкту коміту, яке ви можете потім використати для отримання обʼєкту
Commit
.
Код Ruby гарний та чистий, проте оскільки Libgit2 робить усе можливе для оптимізації, цей код буде також працювати швидко. Якщо ви не прихильник Ruby, ми ознайомимось з іншими привʼязками в Інші привʼязки.
Заглиблений функціонал
Libgit2 має декілька можливостей, які є поза межами ядра Git. Одним прикладом є можливість використання додатків: Libgit2 дозволяє вам надавати власні обробники (backend) для декількох типів операцій, щоб ви могли зберігати речі в інший спосіб, ніж типовий Git. 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)
(Зауважте, що помилки зберігаються, проте не обробляються. Сподіваємось, що ваш код краще за наш.)
-
Ініціалізуйте порожню базу даних обʼєктів (ODB - object database) клієнтської частини, яка буде діяти як контейнер для обробників, які у свою чергу виконуватимуть справжню роботу.
-
Ініціалізуйте власний обробник ODB.
-
Додайте обробник до клієнтської частини.
-
Відкрийте сховище, та надайте йому наше ODB для пошуку обʼєктів.
Проте що таке цей git_odb_backend_mine
?
Ну, це конструктор для вашої власної імплементації ODB, і ви можете зробити тут усе, що забажаєте, доки ви заповните структуру git_odb_backend
правильно.
Ось як вона може виглядати:
typedef struct {
git_odb_backend parent;
// Деякі інші речі
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 llog -1
).
LibGit2Sharp
Якщо ви пишете .NET чи Mono застосунок, то LibGit2Sharp (https://github.com/libgit2/libgit2sharp) — це те, що ви шукаєте. Привʼязка написана на C#, та багато уваги було приділено тому, щоб обгорнути сирі виклики Libgit2 API, яке виглядає природнім для CLR. Ось як виглядає наш приклад:
new Repository(@"C:\path\to\repo").Head.Tip.Message;
Для застосунків для настільного Windows, існує навіть пакет NuGet, який допоможе вам швидко розпочати роботу.
objective-git
Якщо ваш застосунок призначено для платформи Apple, то ви, напевно, використовуєте мову 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 до Python має назву Pygit2, та може бути знайдена за адресою http://www.pygit2.org/. Наш приклад програми:
pygit2.Repository("/path/to/repo") # відкрити сховище
.head # отримати поточну гілку
.peel(pygit2.Commit) # перейти до коміту
.message # зчитати повідомлення
Додаткова література
Авжеж, повноцінний опис можливостей Libgit2 не входить в межі цієї книжки. Якщо вам потрібно більше інформації про сам Libgit2, то є документація API за адресою https://libgit2.github.com/libgit2, та набір посібників за адресою https://libgit2.github.com/docs. Щодо інших привʼязок, перегляньте включені README та тести; там часто є маленькі покрокові посібники (tutorials) та посилання на подальшу інформацію.